mirror of
https://github.com/black-ant/Ant-Browser.git
synced 2026-07-14 18:48:55 +08:00
fix automation script import and local import UI
This commit is contained in:
@@ -8,10 +8,11 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
automationScriptDefaultsMarkerName = "defaults-seeded-v10"
|
||||
automationScriptDefaultsMarkerName = "defaults-seeded-v11"
|
||||
)
|
||||
|
||||
var automationScriptDefaultsLegacyMarkerNames = []string{
|
||||
"defaults-seeded-v10",
|
||||
"defaults-seeded-v9",
|
||||
"defaults-seeded-v8",
|
||||
"defaults-seeded-v7",
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
const (
|
||||
DualInstanceRuntimeScriptID = "dual-instance-runtime-switch"
|
||||
NewsQueryTXTScriptID = "news-query-txt"
|
||||
ProtonMailFirstMessageID = "proton-mail-first-message"
|
||||
WebImageGenerateScriptID = "web-image-generate-download"
|
||||
builtinScriptLibraryRoot = "demo-library"
|
||||
)
|
||||
@@ -20,12 +21,14 @@ const (
|
||||
var builtinScriptLibraryPackageDirs = map[string]string{
|
||||
DualInstanceRuntimeScriptID: builtinScriptLibraryRoot + "/" + DualInstanceRuntimeScriptID,
|
||||
NewsQueryTXTScriptID: builtinScriptLibraryRoot + "/" + NewsQueryTXTScriptID,
|
||||
ProtonMailFirstMessageID: builtinScriptLibraryRoot + "/" + ProtonMailFirstMessageID,
|
||||
WebImageGenerateScriptID: builtinScriptLibraryRoot + "/" + WebImageGenerateScriptID,
|
||||
}
|
||||
|
||||
var builtinScriptLibraryDefaultOrder = []string{
|
||||
DualInstanceRuntimeScriptID,
|
||||
NewsQueryTXTScriptID,
|
||||
ProtonMailFirstMessageID,
|
||||
WebImageGenerateScriptID,
|
||||
}
|
||||
|
||||
@@ -120,6 +123,7 @@ func importBuiltinScriptLibraryBundle(packageDir string) (ImportedBundle, error)
|
||||
Format: mapStringValueAny(descriptor, "format"),
|
||||
PackageFormat: mapStringValueAny(descriptor, "packageFormat"),
|
||||
ManifestVersion: mapIntValueAny(descriptor, "manifestVersion"),
|
||||
ID: mapStringValueAny(descriptor, "id"),
|
||||
Name: mapStringValueAny(descriptor, "name"),
|
||||
Description: mapStringValueAny(descriptor, "description"),
|
||||
Type: mapStringValueAny(descriptor, "type"),
|
||||
|
||||
@@ -7,8 +7,8 @@ func TestDefaultScriptBundles(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("DefaultScriptBundles returned error: %v", err)
|
||||
}
|
||||
if len(bundles) != 3 {
|
||||
t.Fatalf("expected three default script bundles, got %d", len(bundles))
|
||||
if len(bundles) != 4 {
|
||||
t.Fatalf("expected four default script bundles, got %d", len(bundles))
|
||||
}
|
||||
|
||||
expected := []struct {
|
||||
@@ -29,6 +29,12 @@ func TestDefaultScriptBundles(t *testing.T) {
|
||||
uri: "repo://backend/internal/automation/demo-library/news-query-txt",
|
||||
publicAPI: "",
|
||||
},
|
||||
{
|
||||
id: ProtonMailFirstMessageID,
|
||||
name: "Proton 邮件搜索并读取最新邮件",
|
||||
uri: "repo://backend/internal/automation/demo-library/proton-mail-first-message",
|
||||
publicAPI: "mail/proton-first-message",
|
||||
},
|
||||
{
|
||||
id: WebImageGenerateScriptID,
|
||||
name: "网页图片生成并下载",
|
||||
|
||||
@@ -5,5 +5,7 @@
|
||||
!dual-instance-runtime-switch/**
|
||||
!news-query-txt/
|
||||
!news-query-txt/**
|
||||
!proton-mail-first-message/
|
||||
!proton-mail-first-message/**
|
||||
!web-image-generate-download/
|
||||
!web-image-generate-download/**
|
||||
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"createdAt": "2026-05-17T16:05:38+08:00",
|
||||
"description": "打开 Proton Inbox,用查询语句搜索邮件,再用发件邮箱和收件人约束结果,返回命中邮件的正文内容和验证码。",
|
||||
"entryFile": "index.cjs",
|
||||
"format": "ant-automation-script",
|
||||
"id": "proton-mail-first-message",
|
||||
"manifestVersion": 1,
|
||||
"name": "Proton 邮件搜索并读取最新邮件",
|
||||
"notes": "searchQuery 只写入 Proton 搜索框;senderEmail 作为高级搜索约束,recipient 留空也可直接跑。若同一邮箱里 OpenAI / ChatGPT 邮件较多,再补 recipient 缩小范围。旧字段 recipientQuery 仍兼容。",
|
||||
"packageFormat": "ant-automation-script",
|
||||
"params": {
|
||||
"recipient": "",
|
||||
"searchQuery": "OpenAI, ChatGPT",
|
||||
"senderEmail": ""
|
||||
},
|
||||
"publicAPI": {
|
||||
"enabled": true,
|
||||
"method": "POST",
|
||||
"path": "mail/proton-first-message",
|
||||
"requestMode": "params-only",
|
||||
"responseMode": "envelope",
|
||||
"timeoutMs": 120000,
|
||||
"requestBodyText": "",
|
||||
"responseBodyText": "{\r\n \"ok\": true,\r\n \"scriptId\": \"proton-mail-first-message\",\r\n \"scriptName\": \"Proton 邮件搜索并读取最新邮件\",\r\n \"status\": \"success\",\r\n \"summary\": \"已返回当前打开邮件内容\",\r\n \"error\": \"\",\r\n \"runId\": \"xxx\",\r\n \"durationMs\": 5902,\r\n \"result\": {\r\n \"verificationCode\": \"856734\",\r\n \"subject\": \"Your temporary ChatGPT login code\",\r\n \"senderEmail\": \"noreply@example.com\",\r\n \"recipientEmail\": \"target@example.com\",\r\n \"contentText\": \"OpenAI\\nChatGPT Log-in Code\\nIf that was you, enter this code:\\n856734\\nBest,\\nOpenAI\"\r\n }\r\n}",
|
||||
"variables": [
|
||||
|
||||
]
|
||||
},
|
||||
"selector": {
|
||||
"code": ""
|
||||
},
|
||||
"source": {
|
||||
"importedAt": "",
|
||||
"path": "proton-mail-first-message",
|
||||
"ref": "HEAD",
|
||||
"type": "builtin",
|
||||
"uri": "repo://backend/internal/automation/demo-library/proton-mail-first-message"
|
||||
},
|
||||
"status": "ready",
|
||||
"tags": [
|
||||
"Playwright",
|
||||
"邮箱",
|
||||
"Proton",
|
||||
"邮件处理"
|
||||
],
|
||||
"targetConfig": {
|
||||
"mode": "manual",
|
||||
"selector": {
|
||||
"code": "",
|
||||
"profileId": "",
|
||||
"profileName": "",
|
||||
"groupId": "",
|
||||
"keywords": [
|
||||
|
||||
],
|
||||
"tags": [
|
||||
|
||||
]
|
||||
},
|
||||
"templateSelector": {
|
||||
"code": "",
|
||||
"profileId": "",
|
||||
"profileName": "",
|
||||
"groupId": "",
|
||||
"keywords": [
|
||||
|
||||
],
|
||||
"tags": [
|
||||
|
||||
]
|
||||
},
|
||||
"createNameTemplate": ""
|
||||
},
|
||||
"type": "playwright-cdp",
|
||||
"updatedAt": "2026-05-24T02:24:39.006Z"
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,7 @@ type scriptImportEnvelope struct {
|
||||
PackageFormat string `json:"packageFormat"`
|
||||
ManifestVersion int `json:"manifestVersion"`
|
||||
Manifest map[string]any `json:"manifest"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Type string `json:"type"`
|
||||
|
||||
@@ -118,6 +118,7 @@ func importDirectoryBundle(packageRoot string, entryPath string, descriptor map[
|
||||
Format: mapStringValueAny(descriptor, "format"),
|
||||
PackageFormat: mapStringValueAny(descriptor, "packageFormat"),
|
||||
ManifestVersion: mapIntValueAny(descriptor, "manifestVersion"),
|
||||
ID: mapStringValueAny(descriptor, "id"),
|
||||
Name: mapStringValueAny(descriptor, "name"),
|
||||
Description: mapStringValueAny(descriptor, "description"),
|
||||
Type: mapStringValueAny(descriptor, "type"),
|
||||
@@ -130,6 +131,8 @@ func importDirectoryBundle(packageRoot string, entryPath string, descriptor map[
|
||||
ParamsText: descriptor["paramsText"],
|
||||
ScriptText: string(entryData),
|
||||
Notes: appendImportSourceNote(mapStringValueAny(descriptor, "notes"), sourceLabel),
|
||||
TargetConfig: mapObjectValue(descriptor, "targetConfig"),
|
||||
PublicAPI: mapObjectValue(descriptor, "publicAPI"),
|
||||
Source: mapObjectValue(descriptor, "source"),
|
||||
}, filepath.Base(packageRoot), sourceLabel)
|
||||
if err != nil {
|
||||
|
||||
@@ -49,6 +49,7 @@ func buildImportedRecord(envelope scriptImportEnvelope, defaultName string, sour
|
||||
}
|
||||
mergeDescriptorValue(descriptor, "format", envelope.Format)
|
||||
mergeDescriptorValue(descriptor, "packageFormat", envelope.PackageFormat)
|
||||
mergeDescriptorValue(descriptor, "id", envelope.ID)
|
||||
if envelope.ManifestVersion > 0 {
|
||||
descriptor["manifestVersion"] = envelope.ManifestVersion
|
||||
}
|
||||
@@ -99,18 +100,27 @@ func buildImportedRecord(envelope scriptImportEnvelope, defaultName string, sour
|
||||
}
|
||||
|
||||
now := time.Now().Format(time.RFC3339)
|
||||
packageFormat := normalizeScriptPackageFormat(firstNonEmpty(mapStringValueAny(descriptor, "packageFormat"), mapStringValueAny(descriptor, "format")))
|
||||
recordID := uuid.NewString()
|
||||
recordStatus := "draft"
|
||||
if packageFormat == defaultScriptPackageFormat {
|
||||
if manifestID := strings.TrimSpace(mapStringValueAny(descriptor, "id")); manifestID != "" {
|
||||
recordID = manifestID
|
||||
}
|
||||
recordStatus = mapStringValueAny(descriptor, "status")
|
||||
}
|
||||
source := inferImportSource(sourceLabel)
|
||||
if explicitSource, ok := descriptor["source"].(map[string]any); ok {
|
||||
source = mergeImportedSource(source, explicitSource)
|
||||
}
|
||||
return normalizeScriptRecord(ScriptRecord{
|
||||
PackageFormat: normalizeScriptPackageFormat(firstNonEmpty(mapStringValueAny(descriptor, "packageFormat"), mapStringValueAny(descriptor, "format"))),
|
||||
PackageFormat: packageFormat,
|
||||
ManifestVersion: normalizeScriptManifestVersion(mapIntValueAny(descriptor, "manifestVersion"), 0),
|
||||
ID: uuid.NewString(),
|
||||
ID: recordID,
|
||||
Name: firstNonEmpty(mapStringValueAny(descriptor, "name"), defaultName, "导入脚本"),
|
||||
Description: mapStringValueAny(descriptor, "description"),
|
||||
Type: mapStringValueAny(descriptor, "type"),
|
||||
Status: "draft",
|
||||
Status: recordStatus,
|
||||
EntryFile: normalizeScriptEntryFile(firstNonEmpty(mapStringValueAny(descriptor, "entryFile"), defaultEntryFileForName(defaultName))),
|
||||
Tags: mapStringSliceValue(descriptor, "tags"),
|
||||
SelectorText: stringifyImportJSONValue(firstNonNil(descriptor["selectorText"], descriptor["selector"])),
|
||||
|
||||
@@ -98,6 +98,54 @@ func TestImportBundleFromBytesSupportsZipPackage(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportBundleFromZipPreservesPackageIdentityAndPublicAPI(t *testing.T) {
|
||||
zipPath := filepath.Join(t.TempDir(), "proton-package.zip")
|
||||
if err := os.WriteFile(zipPath, buildScriptPackageZipBytes(t, map[string]string{
|
||||
scriptPackageManifestName: `{
|
||||
"format": "ant-automation-script",
|
||||
"packageFormat": "ant-automation-script",
|
||||
"manifestVersion": 1,
|
||||
"id": "proton-mail-first-message",
|
||||
"name": "Proton 邮件搜索并读取最新邮件",
|
||||
"type": "playwright-cdp",
|
||||
"status": "ready",
|
||||
"entryFile": "index.cjs",
|
||||
"targetConfig": {
|
||||
"mode": "manual"
|
||||
},
|
||||
"publicAPI": {
|
||||
"enabled": true,
|
||||
"method": "POST",
|
||||
"path": "mail/proton-first-message",
|
||||
"requestMode": "params-only",
|
||||
"responseMode": "envelope",
|
||||
"timeoutMs": 120000
|
||||
}
|
||||
}`,
|
||||
"index.cjs": "module.exports.run = async () => ({ ok: true })",
|
||||
}), 0o644); err != nil {
|
||||
t.Fatalf("write zip failed: %v", err)
|
||||
}
|
||||
|
||||
bundle, err := ImportBundleFromZip(zipPath, "本地文件 "+zipPath)
|
||||
if err != nil {
|
||||
t.Fatalf("ImportBundleFromZip returned error: %v", err)
|
||||
}
|
||||
|
||||
if bundle.Record.ID != "proton-mail-first-message" {
|
||||
t.Fatalf("expected package id to round-trip, got %q", bundle.Record.ID)
|
||||
}
|
||||
if bundle.Record.Status != "ready" {
|
||||
t.Fatalf("expected package status to round-trip, got %q", bundle.Record.Status)
|
||||
}
|
||||
if !bundle.Record.PublicAPI.Enabled || bundle.Record.PublicAPI.Path != "mail/proton-first-message" {
|
||||
t.Fatalf("expected public api to round-trip, got %+v", bundle.Record.PublicAPI)
|
||||
}
|
||||
if bundle.Record.TargetConfig.Mode != "manual" {
|
||||
t.Fatalf("expected target config to round-trip, got %+v", bundle.Record.TargetConfig)
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportBundleFromZipSupportsSingleRootDirectory(t *testing.T) {
|
||||
zipPath := filepath.Join(t.TempDir(), "nested.zip")
|
||||
if err := os.WriteFile(zipPath, buildScriptPackageZipBytes(t, map[string]string{
|
||||
|
||||
@@ -10,12 +10,9 @@ import {
|
||||
import type { BrowserProfile } from "../types";
|
||||
|
||||
export type ImportMode =
|
||||
| "text"
|
||||
| "local-file"
|
||||
| "local-dir"
|
||||
| "local-library"
|
||||
| "remote-url"
|
||||
| "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";
|
||||
|
||||
|
||||
@@ -10,9 +10,6 @@ import {
|
||||
importAutomationScriptFromGit,
|
||||
importAutomationScriptFromLocalDirectory,
|
||||
importAutomationScriptFromLocalFile,
|
||||
importAutomationScriptFromLocalLibrary,
|
||||
importAutomationScriptFromRemote,
|
||||
importAutomationScriptFromText,
|
||||
saveAutomationScript,
|
||||
} from "../automationScriptApi";
|
||||
import {
|
||||
@@ -37,6 +34,7 @@ import {
|
||||
resolveDualLaunchCodes,
|
||||
type AutomationCardPresentation,
|
||||
type ImportMode,
|
||||
type LocalImportKind,
|
||||
} from "./AutomationPage.helpers";
|
||||
export function AutomationPage() {
|
||||
const navigate = useNavigate();
|
||||
@@ -56,9 +54,8 @@ export function AutomationPage() {
|
||||
const [createType, setCreateType] =
|
||||
useState<AutomationScriptType>("playwright-cdp");
|
||||
const [createName, setCreateName] = useState("");
|
||||
const [importMode, setImportMode] = useState<ImportMode>("text");
|
||||
const [importText, setImportText] = useState("");
|
||||
const [remoteURL, setRemoteURL] = 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("");
|
||||
@@ -202,9 +199,8 @@ export function AutomationPage() {
|
||||
};
|
||||
|
||||
const resetImportModal = () => {
|
||||
setImportMode("text");
|
||||
setImportText("");
|
||||
setRemoteURL("");
|
||||
setImportMode("local");
|
||||
setLocalImportKind("file");
|
||||
setGitURL("");
|
||||
setGitRef("");
|
||||
setGitScriptPath("");
|
||||
@@ -250,24 +246,12 @@ export function AutomationPage() {
|
||||
let failedCount = 0;
|
||||
|
||||
switch (importMode) {
|
||||
case "text": {
|
||||
imported = [await importAutomationScriptFromText(importText)];
|
||||
break;
|
||||
}
|
||||
case "local-file":
|
||||
imported = [await importAutomationScriptFromLocalFile()];
|
||||
break;
|
||||
case "local-dir":
|
||||
imported = [await importAutomationScriptFromLocalDirectory()];
|
||||
break;
|
||||
case "local-library": {
|
||||
const result = await importAutomationScriptFromLocalLibrary();
|
||||
imported = result.imported;
|
||||
failedCount = result.failed.length;
|
||||
break;
|
||||
}
|
||||
case "remote-url":
|
||||
imported = [await importAutomationScriptFromRemote(remoteURL)];
|
||||
case "local":
|
||||
imported = [
|
||||
localImportKind === "directory"
|
||||
? await importAutomationScriptFromLocalDirectory()
|
||||
: await importAutomationScriptFromLocalFile(),
|
||||
];
|
||||
break;
|
||||
case "git":
|
||||
imported = [
|
||||
@@ -407,16 +391,14 @@ export function AutomationPage() {
|
||||
open={importOpen}
|
||||
busyAction={modalBusyAction}
|
||||
importMode={importMode}
|
||||
importText={importText}
|
||||
remoteURL={remoteURL}
|
||||
localImportKind={localImportKind}
|
||||
gitURL={gitURL}
|
||||
gitRef={gitRef}
|
||||
gitScriptPath={gitScriptPath}
|
||||
onClose={closeImportModal}
|
||||
onImport={handleImport}
|
||||
onImportModeChange={setImportMode}
|
||||
onImportTextChange={setImportText}
|
||||
onRemoteURLChange={setRemoteURL}
|
||||
onLocalImportKindChange={setLocalImportKind}
|
||||
onGitURLChange={setGitURL}
|
||||
onGitRefChange={setGitRef}
|
||||
onGitScriptPathChange={setGitScriptPath}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Button, FormItem, Input, Modal, Select, Textarea } from "../../../shared/components";
|
||||
import { Button, FormItem, Input, Modal, Select } from "../../../shared/components";
|
||||
import { AUTOMATION_SCRIPT_TYPE_OPTIONS, type AutomationScriptType } from "../automationScripts";
|
||||
import type { ImportMode } from "./AutomationPage.helpers";
|
||||
import type { ImportMode, LocalImportKind } from "./AutomationPage.helpers";
|
||||
|
||||
interface CreateAutomationScriptModalProps {
|
||||
open: boolean;
|
||||
@@ -73,16 +73,14 @@ interface ImportAutomationScriptModalProps {
|
||||
open: boolean;
|
||||
busyAction: "none" | "create" | "import";
|
||||
importMode: ImportMode;
|
||||
importText: string;
|
||||
remoteURL: string;
|
||||
localImportKind: LocalImportKind;
|
||||
gitURL: string;
|
||||
gitRef: string;
|
||||
gitScriptPath: string;
|
||||
onClose: () => void;
|
||||
onImport: () => Promise<void>;
|
||||
onImportModeChange: (value: ImportMode) => void;
|
||||
onImportTextChange: (value: string) => void;
|
||||
onRemoteURLChange: (value: string) => void;
|
||||
onLocalImportKindChange: (value: LocalImportKind) => void;
|
||||
onGitURLChange: (value: string) => void;
|
||||
onGitRefChange: (value: string) => void;
|
||||
onGitScriptPathChange: (value: string) => void;
|
||||
@@ -92,16 +90,14 @@ export function ImportAutomationScriptModal({
|
||||
open,
|
||||
busyAction,
|
||||
importMode,
|
||||
importText,
|
||||
remoteURL,
|
||||
localImportKind,
|
||||
gitURL,
|
||||
gitRef,
|
||||
gitScriptPath,
|
||||
onClose,
|
||||
onImport,
|
||||
onImportModeChange,
|
||||
onImportTextChange,
|
||||
onRemoteURLChange,
|
||||
onLocalImportKindChange,
|
||||
onGitURLChange,
|
||||
onGitRefChange,
|
||||
onGitScriptPathChange,
|
||||
@@ -133,73 +129,50 @@ export function ImportAutomationScriptModal({
|
||||
<div className="space-y-4">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{[
|
||||
{ value: "text", label: "文本" },
|
||||
{ value: "local-file", label: "本地文件" },
|
||||
{ value: "local-dir", label: "本地目录" },
|
||||
{ value: "local-library", label: "脚本库" },
|
||||
{ value: "remote-url", label: "远程 URL" },
|
||||
{ value: "git", label: "Git" },
|
||||
{ value: "local", label: "本地", disabled: false },
|
||||
{ value: "git", label: "Git(进行中)", disabled: false },
|
||||
{ value: "script-library", label: "脚本库(计划中)", disabled: true },
|
||||
].map((item) => (
|
||||
<Button
|
||||
key={item.value}
|
||||
size="sm"
|
||||
variant={importMode === item.value ? "primary" : "secondary"}
|
||||
onClick={() => onImportModeChange(item.value as ImportMode)}
|
||||
disabled={busyAction !== "none"}
|
||||
onClick={() => {
|
||||
if (item.value === "script-library") {
|
||||
return;
|
||||
}
|
||||
onImportModeChange(item.value as ImportMode);
|
||||
}}
|
||||
disabled={busyAction !== "none" || item.disabled}
|
||||
>
|
||||
{item.label}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{importMode === "text" ? (
|
||||
<>
|
||||
<div className="text-sm text-[var(--color-text-secondary)]">
|
||||
支持导入导出的脚本 JSON,导入后会按草稿保存。
|
||||
{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>
|
||||
<FormItem label="脚本 JSON">
|
||||
<Textarea
|
||||
rows={18}
|
||||
value={importText}
|
||||
onChange={(event) => onImportTextChange(event.target.value)}
|
||||
className="font-mono"
|
||||
placeholder='{"manifest":{"name":"示例脚本"}}'
|
||||
/>
|
||||
</FormItem>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
{importMode === "local-file" ? (
|
||||
<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)]">
|
||||
导入时会弹出文件选择框。支持单个 `.js/.cjs/.mjs` 脚本文件、导出的
|
||||
`.json` 模板,或标准 `.zip` 脚本包。`.ts/.cts/.mts` 仅在设置页开启 TypeScript 导入构建后支持。
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{importMode === "local-dir" ? (
|
||||
<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)]">
|
||||
导入时会弹出目录选择框。适合导入一整套本地脚本目录,或 Git 拉下来的脚本包目录。目录里的 `.ts/.cts/.mts` 入口也需要先在设置页开启 TypeScript 导入构建。
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{importMode === "local-library" ? (
|
||||
<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)]">
|
||||
导入时会弹出目录选择框。系统会扫描所选目录下的脚本包并批量导入,来源按本地目录记录,后续刷新不走 Git。
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{importMode === "remote-url" ? (
|
||||
<div className="space-y-4">
|
||||
<div className="text-sm text-[var(--color-text-secondary)]">
|
||||
适合导入单个远程脚本文件、导出的脚本 JSON,或标准脚本 ZIP。多文件仓库也可以继续使用 Git 导入;远程 `.ts/.cts/.mts` 同样要求设置页已开启 TypeScript 导入构建。
|
||||
<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>
|
||||
<FormItem label="远程地址">
|
||||
<Input
|
||||
value={remoteURL}
|
||||
onChange={(event) => onRemoteURLChange(event.target.value)}
|
||||
placeholder="https://example.com/script.cjs"
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user