mirror of
https://github.com/black-ant/Ant-Browser.git
synced 2026-07-14 18:48:55 +08:00
fix: stabilize automation script import
This commit is contained in:
@@ -17,8 +17,8 @@ func TestAutomationScriptListSeedsDefaultScriptsOnFreshApp(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("AutomationScriptList returned error: %v", err)
|
||||
}
|
||||
if len(items) != 3 {
|
||||
t.Fatalf("expected three default scripts, got %d", len(items))
|
||||
if len(items) != 4 {
|
||||
t.Fatalf("expected four default scripts, got %d", len(items))
|
||||
}
|
||||
|
||||
byID := make(map[string]automation.ScriptRecord, len(items))
|
||||
@@ -29,6 +29,7 @@ func TestAutomationScriptListSeedsDefaultScriptsOnFreshApp(t *testing.T) {
|
||||
expectedNames := map[string]string{
|
||||
"dual-instance-runtime-switch": "双实例启动与 Runtime 切换",
|
||||
"news-query-txt": "查询新闻并写 TXT",
|
||||
"proton-mail-first-message": "Proton 邮件搜索并读取最新邮件",
|
||||
"web-image-generate-download": "网页图片生成并下载",
|
||||
}
|
||||
|
||||
@@ -105,13 +106,14 @@ func TestAutomationScriptListAddsMissingBuiltinWhenLegacyMarkerExists(t *testing
|
||||
if err != nil {
|
||||
t.Fatalf("AutomationScriptList returned error: %v", err)
|
||||
}
|
||||
if len(items) != 4 {
|
||||
t.Fatalf("expected custom script plus three defaults, got %d items", len(items))
|
||||
if len(items) != 5 {
|
||||
t.Fatalf("expected custom script plus four defaults, got %d items", len(items))
|
||||
}
|
||||
|
||||
expectedDefaultIDs := []string{
|
||||
automation.DualInstanceRuntimeScriptID,
|
||||
automation.NewsQueryTXTScriptID,
|
||||
automation.ProtonMailFirstMessageID,
|
||||
automation.WebImageGenerateScriptID,
|
||||
}
|
||||
for _, scriptID := range expectedDefaultIDs {
|
||||
|
||||
@@ -74,10 +74,17 @@ func (a *App) ensureAutomationScriptDefaults(store *automation.ScriptStore) erro
|
||||
return a.markAutomationScriptDefaultsInitialized()
|
||||
}
|
||||
|
||||
importedCount := 0
|
||||
var lastImportErr error
|
||||
for _, bundle := range defaults {
|
||||
if _, err := store.ImportBundle(bundle); err != nil {
|
||||
return err
|
||||
lastImportErr = err
|
||||
continue
|
||||
}
|
||||
importedCount++
|
||||
}
|
||||
if importedCount == 0 && lastImportErr != nil {
|
||||
return lastImportErr
|
||||
}
|
||||
return a.markAutomationScriptDefaultsInitialized()
|
||||
}
|
||||
@@ -92,15 +99,11 @@ func (a *App) ensureAutomationScriptDefaults(store *automation.ScriptStore) erro
|
||||
if existing, exists := existingByID[bundle.Record.ID]; exists {
|
||||
if existing.Source.Type == "builtin" {
|
||||
bundle.Record = mergeBuiltinDefaultScriptForMigration(existing, bundle.Record)
|
||||
if _, err := store.ImportBundle(bundle); err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = store.ImportBundle(bundle)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if _, err := store.ImportBundle(bundle); err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = store.ImportBundle(bundle)
|
||||
}
|
||||
}
|
||||
return a.markAutomationScriptDefaultsInitialized()
|
||||
|
||||
@@ -12,7 +12,6 @@ import type { BrowserProfile } from "../types";
|
||||
export type ImportMode =
|
||||
| "local"
|
||||
| "git";
|
||||
export type LocalImportKind = "file" | "directory";
|
||||
export const DUAL_INSTANCE_SCRIPT_ID = "dual-instance-runtime-switch";
|
||||
export const NEWS_SCRIPT_ID = "news-query-txt";
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ import {
|
||||
resolveDualLaunchCodes,
|
||||
type AutomationCardPresentation,
|
||||
type ImportMode,
|
||||
type LocalImportKind,
|
||||
} from "./AutomationPage.helpers";
|
||||
export function AutomationPage() {
|
||||
const navigate = useNavigate();
|
||||
@@ -55,7 +54,6 @@ export function AutomationPage() {
|
||||
useState<AutomationScriptType>("playwright-cdp");
|
||||
const [createName, setCreateName] = useState("");
|
||||
const [importMode, setImportMode] = useState<ImportMode>("local");
|
||||
const [localImportKind, setLocalImportKind] = useState<LocalImportKind>("file");
|
||||
const [gitURL, setGitURL] = useState("");
|
||||
const [gitRef, setGitRef] = useState("");
|
||||
const [gitScriptPath, setGitScriptPath] = useState("");
|
||||
@@ -200,7 +198,6 @@ export function AutomationPage() {
|
||||
|
||||
const resetImportModal = () => {
|
||||
setImportMode("local");
|
||||
setLocalImportKind("file");
|
||||
setGitURL("");
|
||||
setGitRef("");
|
||||
setGitScriptPath("");
|
||||
@@ -239,44 +236,52 @@ export function AutomationPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const finishImport = (imported: AutomationScriptRecord[], failedCount = 0) => {
|
||||
if (imported.length === 0) {
|
||||
throw new Error("未导入任何脚本");
|
||||
}
|
||||
|
||||
setScripts((current) => mergeImportedScripts(current, imported));
|
||||
setImportOpen(false);
|
||||
resetImportModal();
|
||||
if (imported.length === 1 && failedCount === 0) {
|
||||
toast.success("脚本已导入");
|
||||
openScript(imported[0].id);
|
||||
return;
|
||||
}
|
||||
|
||||
toast.success(`已导入 ${imported.length} 个脚本`);
|
||||
if (failedCount > 0) {
|
||||
toast.warning(`${failedCount} 个脚本包导入失败`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleLocalImport = async (kind: "file" | "directory") => {
|
||||
setBusyAction("import");
|
||||
try {
|
||||
const imported = kind === "directory"
|
||||
? await importAutomationScriptFromLocalDirectory()
|
||||
: await importAutomationScriptFromLocalFile();
|
||||
finishImport([imported]);
|
||||
} catch (error: unknown) {
|
||||
const message = error instanceof Error ? error.message : "脚本导入失败";
|
||||
toast.error(message);
|
||||
} finally {
|
||||
setBusyAction("none");
|
||||
}
|
||||
};
|
||||
|
||||
const handleImport = async () => {
|
||||
setBusyAction("import");
|
||||
try {
|
||||
let imported: AutomationScriptRecord[] = [];
|
||||
let failedCount = 0;
|
||||
|
||||
switch (importMode) {
|
||||
case "local":
|
||||
imported = [
|
||||
localImportKind === "directory"
|
||||
? await importAutomationScriptFromLocalDirectory()
|
||||
: await importAutomationScriptFromLocalFile(),
|
||||
];
|
||||
break;
|
||||
case "git":
|
||||
imported = [
|
||||
finishImport([
|
||||
await importAutomationScriptFromGit(gitURL, gitRef, gitScriptPath),
|
||||
];
|
||||
]);
|
||||
break;
|
||||
default:
|
||||
throw new Error("不支持的导入方式");
|
||||
}
|
||||
|
||||
if (imported.length === 0) {
|
||||
throw new Error("未导入任何脚本");
|
||||
}
|
||||
|
||||
setScripts((current) => mergeImportedScripts(current, imported));
|
||||
setImportOpen(false);
|
||||
resetImportModal();
|
||||
if (imported.length === 1 && failedCount === 0) {
|
||||
toast.success("脚本已导入");
|
||||
openScript(imported[0].id);
|
||||
} else {
|
||||
toast.success(`已导入 ${imported.length} 个脚本`);
|
||||
if (failedCount > 0) {
|
||||
toast.warning(`${failedCount} 个脚本包导入失败`);
|
||||
}
|
||||
throw new Error("请选择要导入的文件或文件夹");
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
const message = error instanceof Error ? error.message : "脚本导入失败";
|
||||
@@ -391,14 +396,14 @@ export function AutomationPage() {
|
||||
open={importOpen}
|
||||
busyAction={modalBusyAction}
|
||||
importMode={importMode}
|
||||
localImportKind={localImportKind}
|
||||
gitURL={gitURL}
|
||||
gitRef={gitRef}
|
||||
gitScriptPath={gitScriptPath}
|
||||
onClose={closeImportModal}
|
||||
onImport={handleImport}
|
||||
onImportLocalFile={() => handleLocalImport("file")}
|
||||
onImportLocalDirectory={() => handleLocalImport("directory")}
|
||||
onImportModeChange={setImportMode}
|
||||
onLocalImportKindChange={setLocalImportKind}
|
||||
onGitURLChange={setGitURL}
|
||||
onGitRefChange={setGitRef}
|
||||
onGitScriptPathChange={setGitScriptPath}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Button, FormItem, Input, Modal, Select } from "../../../shared/components";
|
||||
import { AUTOMATION_SCRIPT_TYPE_OPTIONS, type AutomationScriptType } from "../automationScripts";
|
||||
import type { ImportMode, LocalImportKind } from "./AutomationPage.helpers";
|
||||
import type { ImportMode } from "./AutomationPage.helpers";
|
||||
|
||||
interface CreateAutomationScriptModalProps {
|
||||
open: boolean;
|
||||
@@ -73,14 +73,14 @@ interface ImportAutomationScriptModalProps {
|
||||
open: boolean;
|
||||
busyAction: "none" | "create" | "import";
|
||||
importMode: ImportMode;
|
||||
localImportKind: LocalImportKind;
|
||||
gitURL: string;
|
||||
gitRef: string;
|
||||
gitScriptPath: string;
|
||||
onClose: () => void;
|
||||
onImport: () => Promise<void>;
|
||||
onImportLocalFile: () => Promise<void>;
|
||||
onImportLocalDirectory: () => Promise<void>;
|
||||
onImportModeChange: (value: ImportMode) => void;
|
||||
onLocalImportKindChange: (value: LocalImportKind) => void;
|
||||
onGitURLChange: (value: string) => void;
|
||||
onGitRefChange: (value: string) => void;
|
||||
onGitScriptPathChange: (value: string) => void;
|
||||
@@ -90,14 +90,14 @@ export function ImportAutomationScriptModal({
|
||||
open,
|
||||
busyAction,
|
||||
importMode,
|
||||
localImportKind,
|
||||
gitURL,
|
||||
gitRef,
|
||||
gitScriptPath,
|
||||
onClose,
|
||||
onImport,
|
||||
onImportLocalFile,
|
||||
onImportLocalDirectory,
|
||||
onImportModeChange,
|
||||
onLocalImportKindChange,
|
||||
onGitURLChange,
|
||||
onGitRefChange,
|
||||
onGitScriptPathChange,
|
||||
@@ -117,12 +117,14 @@ export function ImportAutomationScriptModal({
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => void onImport()}
|
||||
loading={busyAction === "import"}
|
||||
>
|
||||
导入
|
||||
</Button>
|
||||
{importMode === "git" ? (
|
||||
<Button
|
||||
onClick={() => void onImport()}
|
||||
loading={busyAction === "import"}
|
||||
>
|
||||
导入
|
||||
</Button>
|
||||
) : null}
|
||||
</>
|
||||
}
|
||||
>
|
||||
@@ -151,28 +153,21 @@ export function ImportAutomationScriptModal({
|
||||
</div>
|
||||
|
||||
{importMode === "local" ? (
|
||||
<div className="space-y-3">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{[
|
||||
{ value: "file", label: "ZIP / 文件" },
|
||||
{ value: "directory", label: "文件夹" },
|
||||
].map((item) => (
|
||||
<Button
|
||||
key={item.value}
|
||||
size="sm"
|
||||
variant={localImportKind === item.value ? "primary" : "secondary"}
|
||||
onClick={() => onLocalImportKindChange(item.value as LocalImportKind)}
|
||||
disabled={busyAction !== "none"}
|
||||
>
|
||||
{item.label}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
<div className="rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-secondary)] px-4 py-4 text-sm text-[var(--color-text-secondary)]">
|
||||
{localImportKind === "directory"
|
||||
? "点击导入后选择脚本文件夹,适合一整套本地脚本目录。"
|
||||
: "点击导入后选择本地文件,支持标准 ZIP 脚本包、JSON 模板和单文件脚本。"}
|
||||
</div>
|
||||
<div className="grid grid-cols-1 gap-3 md:grid-cols-2">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => void onImportLocalFile()}
|
||||
loading={busyAction === "import"}
|
||||
>
|
||||
选择 ZIP / 文件
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => void onImportLocalDirectory()}
|
||||
loading={busyAction === "import"}
|
||||
>
|
||||
选择文件夹
|
||||
</Button>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user