vanspod 0.0.0-stage → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -3
- package/bin/register.cjs +11 -0
- package/bin/vanspod.cjs +2 -0
- package/dist/adapterSettings.js +37 -0
- package/dist/adapters/chat-output.js +69 -0
- package/dist/adapters/chat-stream.js +44 -0
- package/dist/adapters/chat.js +63 -0
- package/dist/adapters/claude-reasoning.js +118 -0
- package/dist/adapters/claude-request.js +190 -0
- package/dist/adapters/claude-response.js +79 -0
- package/dist/adapters/index.js +19 -0
- package/dist/adapters/messages-stream.js +126 -0
- package/dist/adapters/messages.js +21 -0
- package/dist/adapters/responses-stream.js +70 -0
- package/dist/adapters/responses.js +28 -0
- package/dist/adapters/stream.js +99 -0
- package/dist/adapters/transport.js +26 -0
- package/dist/adapters/types.js +35 -0
- package/dist/appearance.js +10 -0
- package/dist/classroomApp.js +1814 -0
- package/dist/classroomUpdate.js +155 -0
- package/dist/claudeWebSearch.js +248 -0
- package/dist/clientDetection.js +35 -0
- package/dist/clientIcons.js +33 -0
- package/dist/clientRouteRuntime.js +149 -0
- package/dist/clientRoutes.js +118 -0
- package/dist/clientSettingsBackup.js +110 -0
- package/dist/clientTraffic.js +26 -0
- package/dist/clipboard.js +32 -0
- package/dist/codexWebSearch.js +80 -0
- package/dist/codexWire/apply-patch-envelope.js +139 -0
- package/dist/codexWire/chat-options.js +51 -0
- package/dist/codexWire/chat-tools.js +80 -0
- package/dist/codexWire/freeform-wrapper-scan.js +326 -0
- package/dist/codexWire/progressive-freeform-input.js +133 -0
- package/dist/codexWire/request.js +73 -0
- package/dist/codexWire/responses-tools.js +88 -0
- package/dist/codexWire/tool-arguments.js +39 -0
- package/dist/codexWire/tool-guidance.js +77 -0
- package/dist/commandRunner.js +100 -0
- package/dist/connectionMascot.js +235 -0
- package/dist/courseCatalog.js +131 -0
- package/dist/desktop.js +25 -0
- package/dist/entry.js +17 -0
- package/dist/environmentIcons.js +11 -0
- package/dist/exitGuard.js +17 -0
- package/dist/folderPicker.js +35 -0
- package/dist/installProgress.js +171 -0
- package/dist/installations.js +72 -0
- package/dist/instance.js +160 -0
- package/dist/launcher.js +155 -0
- package/dist/legacyMigration.js +87 -0
- package/dist/lifecyclePage.js +41 -0
- package/dist/modelProxy.js +105 -0
- package/dist/modelStatus.js +42 -0
- package/dist/page.js +1016 -0
- package/dist/pagePrototype.js +528 -0
- package/dist/platformStorage.js +91 -0
- package/dist/removal.js +41 -0
- package/dist/responsesChat.js +835 -0
- package/dist/routeFiles.js +646 -0
- package/dist/routerSessionModels.js +32 -0
- package/dist/runtimeOptions.js +14 -0
- package/dist/secureStorage.js +63 -0
- package/dist/settingsActions.js +100 -0
- package/dist/start.js +822 -0
- package/dist/storageErrors.js +17 -0
- package/dist/storageLocation.js +58 -0
- package/dist/version.js +4 -0
- package/dist/vsCodeRouteSettings.js +208 -0
- package/dist/webSearch.js +56 -0
- package/dist/windowsSecureStorage.js +163 -0
- package/media/brand-icon.ico +0 -0
- package/media/brand-logo.png +0 -0
- package/media/fadou-superhero-blink.png +0 -0
- package/media/fadou-superhero.png +0 -0
- package/package.json +30 -6
- package/scripts/tray.ps1 +40 -0
- package/scripts/uninstall.ps1 +133 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CODE_MODE_HOST_CONTRACT_SENTENCE = exports.CODE_MODE_RESULT_ECHO_SENTENCE = void 0;
|
|
4
|
+
exports.codexToolGuidance = codexToolGuidance;
|
|
5
|
+
exports.appendToolInstructions = appendToolInstructions;
|
|
6
|
+
exports.codeModeInputDescription = codeModeInputDescription;
|
|
7
|
+
const types_1 = require("../adapters/types");
|
|
8
|
+
// Tool prompt contracts aligned with opencodex 2.81.0 (19bd34a), adapted
|
|
9
|
+
// to VansPod's reversible wire names. No runtime dependency on opencodex.
|
|
10
|
+
exports.CODE_MODE_RESULT_ECHO_SENTENCE = "Nothing in the isolate is echoed automatically: a bare trailing `await tools.<name>(...)` or final expression value is DISCARDED, and the cell reports empty output. Pass anything you need to read to `text(...)` (or `notify(...)`) in the same cell — for example `text(JSON.stringify(await tools.exec_command({cmd: 'ls'})))` — and treat an empty result as your own missing `text(...)` call rather than a failed command or lost context.";
|
|
11
|
+
exports.CODE_MODE_HOST_CONTRACT_SENTENCE = "Host contract for the nested helpers: `tools.apply_patch(patch)` takes exactly one string, never an object such as `{input: ...}`; the patch text opens with the bare marker line `*** Begin Patch` and closes with the bare marker line `*** End Patch`, written without a code fence, prose, or extra asterisks on those lines (blank lines or indentation around the markers are tolerated; a decorated or missing marker is rejected). The isolate has no `import`, `require`, or module loader; use the globals the exec tool description lists (for example `tools`, `text`, `notify`, `store`/`load`, `ALL_TOOLS`). For a command that may outlive `yield_time_ms`, let `tools.exec_command` return a `session_id` and poll it on later calls with `tools.write_stdin({session_id, chars: \"\"})` instead of blocking a shell in a sleep loop.";
|
|
12
|
+
const NEIGHBOR_AGENT_TOOL_NAMES = ["Read", "Grep", "Glob", "Bash", "LS"];
|
|
13
|
+
function quoteNames(names) { return names.map(name => "`" + name + "`").join(", "); }
|
|
14
|
+
function uniqueNames(names) { return [...new Set(names.filter(name => name.trim().length > 0))]; }
|
|
15
|
+
function codeModeExecWireName(advertised, verifiedName) {
|
|
16
|
+
if (!verifiedName)
|
|
17
|
+
return undefined;
|
|
18
|
+
return advertised.has(verifiedName) ? verifiedName : undefined;
|
|
19
|
+
}
|
|
20
|
+
function buildToolCatalogNudge(wireNames, codeModeExecName) {
|
|
21
|
+
const names = uniqueNames(wireNames ?? []);
|
|
22
|
+
if (names.length === 0)
|
|
23
|
+
return undefined;
|
|
24
|
+
const advertised = new Set(names);
|
|
25
|
+
const unavailableNeighborNames = NEIGHBOR_AGENT_TOOL_NAMES.filter(name => !advertised.has(name));
|
|
26
|
+
const verifiedCodeModeExecName = codeModeExecWireName(advertised, codeModeExecName);
|
|
27
|
+
return [
|
|
28
|
+
"Tool contract: use the current tool catalog as ground truth.",
|
|
29
|
+
"Valid tool names for this turn are exactly " + quoteNames(names) + ".",
|
|
30
|
+
"These listed names are the complete top-level tool-call surface for this turn.",
|
|
31
|
+
"Call only listed names with their listed argument keys; do not invent, translate, or rename tools.",
|
|
32
|
+
"Names mentioned only in instructions, tool descriptions, argument descriptions, or nested helper APIs are not additional top-level tools.",
|
|
33
|
+
verifiedCodeModeExecName
|
|
34
|
+
? "`" + verifiedCodeModeExecName + "` is Codex code mode: its body is JavaScript evaluated in a V8 isolate. Nested helpers are called INSIDE that body as `await tools.<name>(...)`, for example `await tools.exec_command({cmd: \"ls\"})` or `await tools.codex_app__list_threads({})`. Absence from the top-level catalog or from `" + verifiedCodeModeExecName + "`'s description is not absence: deferred helpers stay callable on `tools.<name>`. Discover them from the isolate global `ALL_TOOLS`, not `tools.ALL_TOOLS`. Do not skip an available nested helper because it is omitted from the listed top-level names. " + exports.CODE_MODE_RESULT_ECHO_SENTENCE + " Nested `tools.apply_patch(input)` is host-executed: the string must begin exactly with `*** Begin Patch` and end with `*** End Patch`, each marker line being three asterisks, one space, the two words, then end of line with no further asterisks. VansPod does not rewrite JavaScript inside exec, so extra asterisks on a marker line are rejected by Codex before the file is touched. " + exports.CODE_MODE_HOST_CONTRACT_SENTENCE
|
|
35
|
+
: "If a listed tool exposes nested helpers such as a tools.* API, call the listed parent tool and use those helpers only inside that tool's input.",
|
|
36
|
+
unavailableNeighborNames.length > 0
|
|
37
|
+
? "Do not use neighboring-agent tool names " + quoteNames(unavailableNeighborNames) + " unless this turn's catalog lists those exact names."
|
|
38
|
+
: undefined,
|
|
39
|
+
"If you need shell, file search, file read, edit, or discovery behavior, choose the listed tool that provides that capability.",
|
|
40
|
+
"Count a tool call only after its tool result returns; batch independent read-only calls when the runtime supports it.",
|
|
41
|
+
].filter((line) => typeof line === "string").join(" ");
|
|
42
|
+
}
|
|
43
|
+
function identities(tools, namespace) {
|
|
44
|
+
return tools.flatMap(value => {
|
|
45
|
+
const tool = (0, types_1.object)(value, "tool");
|
|
46
|
+
if (tool.type === "namespace")
|
|
47
|
+
return identities((0, types_1.list)(tool.tools, "namespace tools"), String(tool.name));
|
|
48
|
+
const fn = tool.function ? (0, types_1.object)(tool.function, "function") : tool;
|
|
49
|
+
return [{ name: String(fn.name), namespace, custom: tool.type === "custom" }];
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function builtin(tool) { return tool.namespace === undefined || tool.namespace === "functions"; }
|
|
53
|
+
function selected(tool, choice) {
|
|
54
|
+
if (choice === "none")
|
|
55
|
+
return false;
|
|
56
|
+
if (!choice || typeof choice === "string")
|
|
57
|
+
return true;
|
|
58
|
+
const selector = (0, types_1.object)(choice, "tool_choice");
|
|
59
|
+
if (selector.type === "allowed_tools")
|
|
60
|
+
return (0, types_1.list)(selector.tools, "allowed tools").some(value => selected(tool, value));
|
|
61
|
+
const fn = selector.function ? (0, types_1.object)(selector.function, "tool choice function") : selector;
|
|
62
|
+
return fn.name === tool.name && (fn.namespace === undefined ? builtin(tool) : fn.namespace === tool.namespace);
|
|
63
|
+
}
|
|
64
|
+
function codexToolGuidance(body, wireNames) {
|
|
65
|
+
const tools = body.tools === undefined ? [] : identities((0, types_1.list)(body.tools, "tools"));
|
|
66
|
+
const visible = tools.map((tool, index) => ({ tool, name: wireNames?.[index] ?? tool.name })).filter(({ tool }) => selected(tool, body.tool_choice));
|
|
67
|
+
const exec = visible.find(({ tool }) => builtin(tool) && tool.name === "exec" && tool.custom);
|
|
68
|
+
const codeMode = !!exec && !visible.some(({ tool }) => builtin(tool) && ["exec_command", "shell_command"].includes(tool.name));
|
|
69
|
+
return { catalog: buildToolCatalogNudge(visible.map(value => value.name), codeMode ? exec?.name : undefined),
|
|
70
|
+
codeMode, execNames: new Set(codeMode ? [exec.name] : []) };
|
|
71
|
+
}
|
|
72
|
+
function appendToolInstructions(instructions, sentences) {
|
|
73
|
+
return sentences.reduce((acc, sentence) => acc.includes(sentence) ? acc : [acc, sentence].filter(Boolean).join("\n\n"), instructions);
|
|
74
|
+
}
|
|
75
|
+
function codeModeInputDescription(existing) {
|
|
76
|
+
return appendToolInstructions(existing ?? "", ["JavaScript source for unified exec; do not provide a bare shell command.", exports.CODE_MODE_RESULT_ECHO_SENTENCE]);
|
|
77
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.collectOutput = collectOutput;
|
|
4
|
+
exports.shellLaunch = shellLaunch;
|
|
5
|
+
exports.runCommand = runCommand;
|
|
6
|
+
const node_child_process_1 = require("node:child_process");
|
|
7
|
+
const ABORTED_NOTICE = "\n已中止。\n";
|
|
8
|
+
const ABORT_GRACE_MS = 5000;
|
|
9
|
+
function collectOutput(command, args, cwd, onOutput, signal) {
|
|
10
|
+
return new Promise((resolve) => {
|
|
11
|
+
let settled = false;
|
|
12
|
+
let aborted = false;
|
|
13
|
+
const finish = (result) => {
|
|
14
|
+
if (settled)
|
|
15
|
+
return;
|
|
16
|
+
settled = true;
|
|
17
|
+
signal?.removeEventListener("abort", abort);
|
|
18
|
+
resolve({ ...result, aborted });
|
|
19
|
+
};
|
|
20
|
+
const child = (0, node_child_process_1.spawn)(command, args, {
|
|
21
|
+
cwd,
|
|
22
|
+
env: { ...process.env, PYTHONUTF8: "1", PYTHONIOENCODING: "utf-8" },
|
|
23
|
+
windowsHide: process.platform === "win32",
|
|
24
|
+
// A process group lets abort reach grandchildren such as servers started by uv.
|
|
25
|
+
detached: process.platform !== "win32",
|
|
26
|
+
// cmd.exe parses the complete command itself; Node's CRT escaping would
|
|
27
|
+
// turn embedded quotes into literal characters passed to tools like uv.
|
|
28
|
+
windowsVerbatimArguments: process.platform === "win32" && command === "cmd.exe",
|
|
29
|
+
});
|
|
30
|
+
const abort = () => {
|
|
31
|
+
if (settled || aborted)
|
|
32
|
+
return;
|
|
33
|
+
aborted = true;
|
|
34
|
+
// An escaped descendant may hold the output pipe open, so an aborted
|
|
35
|
+
// command ends when its own process exits rather than on "close".
|
|
36
|
+
const abandon = () => finish({
|
|
37
|
+
exitCode: child.exitCode ?? undefined, output: Buffer.concat(chunks).toString("utf8"), spawnError: false,
|
|
38
|
+
});
|
|
39
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
40
|
+
abandon();
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
child.once("exit", abandon);
|
|
44
|
+
setTimeout(() => { child.kill(); abandon(); }, ABORT_GRACE_MS).unref();
|
|
45
|
+
killTree(child);
|
|
46
|
+
};
|
|
47
|
+
const chunks = [];
|
|
48
|
+
const receive = (chunk) => {
|
|
49
|
+
chunks.push(chunk);
|
|
50
|
+
onOutput?.(Buffer.concat(chunks).toString("utf8"));
|
|
51
|
+
};
|
|
52
|
+
child.stdout?.on("data", receive);
|
|
53
|
+
child.stderr?.on("data", receive);
|
|
54
|
+
child.on("error", () => finish({
|
|
55
|
+
exitCode: 1, output: Buffer.concat(chunks).toString("utf8"), spawnError: true,
|
|
56
|
+
}));
|
|
57
|
+
child.on("close", (code) => finish({
|
|
58
|
+
exitCode: code ?? undefined, output: Buffer.concat(chunks).toString("utf8"), spawnError: false,
|
|
59
|
+
}));
|
|
60
|
+
if (signal?.aborted)
|
|
61
|
+
abort();
|
|
62
|
+
else
|
|
63
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function killTree(child) {
|
|
67
|
+
if (child.pid === undefined)
|
|
68
|
+
return;
|
|
69
|
+
if (process.platform === "win32") {
|
|
70
|
+
(0, node_child_process_1.spawn)("taskkill", ["/PID", String(child.pid), "/T", "/F"], { stdio: "ignore", windowsHide: true })
|
|
71
|
+
.on("error", () => child.kill())
|
|
72
|
+
.on("exit", (code) => { if (code)
|
|
73
|
+
child.kill(); });
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
process.kill(-child.pid, "SIGKILL");
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
child.kill("SIGKILL");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function shellLaunch(command) {
|
|
84
|
+
if (process.platform === "win32") {
|
|
85
|
+
return { command: "cmd.exe", args: ["/d", "/s", "/c", `"${command}"`] };
|
|
86
|
+
}
|
|
87
|
+
return { command: "sh", args: ["-c", command] };
|
|
88
|
+
}
|
|
89
|
+
async function runCommand(cwd, command, onOutput, signal) {
|
|
90
|
+
const launch = shellLaunch(command);
|
|
91
|
+
const result = await collectOutput(launch.command, launch.args, cwd, onOutput, signal);
|
|
92
|
+
if (result.aborted)
|
|
93
|
+
return { output: `${result.output}${ABORTED_NOTICE}`, exitCode: result.exitCode, aborted: true };
|
|
94
|
+
if (result.spawnError)
|
|
95
|
+
throw new Error("command failed");
|
|
96
|
+
if (result.exitCode !== 0) {
|
|
97
|
+
return { output: `${result.output}\n指令執行失敗(結束碼:${result.exitCode ?? "未知"})。\n`, exitCode: result.exitCode };
|
|
98
|
+
}
|
|
99
|
+
return { output: result.output, exitCode: result.exitCode };
|
|
100
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.connectionMascotScript = exports.connectionMascotStyles = exports.connectionMascot = void 0;
|
|
4
|
+
/** Keep the original brand artwork; animate its layers in the browser. */
|
|
5
|
+
exports.connectionMascot = `<div class="connection-mascot">
|
|
6
|
+
<div class="mascot-traveler">
|
|
7
|
+
<div class="mascot-arrival">
|
|
8
|
+
<button type="button" class="mascot-play" aria-label="和法鬥超人打招呼" title="和法鬥超人打招呼">
|
|
9
|
+
<span class="mascot-idle">
|
|
10
|
+
<span class="mascot-facing">
|
|
11
|
+
<svg class="mascot-art" viewBox="0 0 1024 1024" aria-hidden="true" focusable="false">
|
|
12
|
+
<defs>
|
|
13
|
+
<clipPath id="mascot-arm-clip"><path d="M85 320H220L235 400L222 462L241 508L254 530L207 589L149 522L104 451L85 410Z"/></clipPath>
|
|
14
|
+
<mask id="mascot-arm-mask"><rect width="1024" height="1024" fill="white"/><path d="M85 320H220L235 400L222 462L241 508L254 530L207 589L149 522L104 451L85 410Z" fill="black"/></mask>
|
|
15
|
+
<clipPath id="mascot-left-foot-clip"><rect x="235" y="915" width="175" height="100"/></clipPath>
|
|
16
|
+
<clipPath id="mascot-right-foot-clip"><rect x="490" y="927" width="185" height="88"/></clipPath>
|
|
17
|
+
<clipPath id="mascot-logo-clip"><rect x="283" y="612" width="315" height="105"/></clipPath>
|
|
18
|
+
<filter id="mascot-logo-alpha" x="0" y="0" width="100%" height="100%" color-interpolation-filters="sRGB">
|
|
19
|
+
<feColorMatrix in="SourceGraphic" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 32 -32 0 0.6" result="lettering"/>
|
|
20
|
+
<feColorMatrix in="SourceGraphic" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 -16 0 0 3.5" result="outline"/>
|
|
21
|
+
<feComposite in="lettering" in2="outline" operator="arithmetic" k2="1" k3="1" result="ink"/>
|
|
22
|
+
<feComposite in="SourceGraphic" in2="ink" operator="in"/>
|
|
23
|
+
</filter>
|
|
24
|
+
<filter id="mascot-logo-erase" x="0" y="0" width="100%" height="100%" color-interpolation-filters="sRGB">
|
|
25
|
+
<feColorMatrix in="SourceGraphic" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 32 -32 0 0.6" result="lettering"/>
|
|
26
|
+
<feColorMatrix in="SourceGraphic" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -16 0 0 3.5" result="outline"/>
|
|
27
|
+
<feComposite in="lettering" in2="outline" operator="arithmetic" k2="1" k3="1"/>
|
|
28
|
+
</filter>
|
|
29
|
+
<linearGradient id="mascot-shirt-fill"><stop stop-color="#3d6878"/><stop offset="0.5" stop-color="#3a677a"/><stop offset="1" stop-color="#356270"/></linearGradient>
|
|
30
|
+
<mask id="mascot-detail-mask"><rect width="1024" height="1024" fill="white"/><rect x="235" y="915" width="175" height="100" fill="black"/><rect x="490" y="927" width="185" height="88" fill="black"/><image href="/fadou-superhero.png" width="1024" height="1024" clip-path="url(#mascot-logo-clip)" filter="url(#mascot-logo-erase)"/></mask>
|
|
31
|
+
<clipPath id="mascot-body-clip"><path d="M0 0H1024V490H700L670 534L739 601L768 638L765 681L740 718L702 749L691 778L668 803L646 813V1024H0Z"/></clipPath>
|
|
32
|
+
<clipPath id="mascot-cape-clip"><path d="M688 490L1024 490V850H640V789L670 760L704 730L744 679L744 620L668 545Z"/></clipPath>
|
|
33
|
+
<clipPath id="mascot-eyes-clip"><ellipse cx="334" cy="359" rx="52" ry="43"/><ellipse cx="535" cy="369" rx="53" ry="43"/></clipPath>
|
|
34
|
+
</defs>
|
|
35
|
+
<g class="mascot-cape"><image href="/fadou-superhero.png" width="1024" height="1024" clip-path="url(#mascot-cape-clip)"/></g>
|
|
36
|
+
<rect x="283" y="612" width="315" height="105" fill="url(#mascot-shirt-fill)"/>
|
|
37
|
+
<g mask="url(#mascot-detail-mask)"><image href="/fadou-superhero.png" width="1024" height="1024" clip-path="url(#mascot-body-clip)" mask="url(#mascot-arm-mask)"/></g>
|
|
38
|
+
<g class="mascot-foot mascot-left-foot"><image href="/fadou-superhero.png" width="1024" height="1024" clip-path="url(#mascot-left-foot-clip)"/></g>
|
|
39
|
+
<g class="mascot-foot mascot-right-foot"><image href="/fadou-superhero.png" width="1024" height="1024" clip-path="url(#mascot-right-foot-clip)"/></g>
|
|
40
|
+
<g class="mascot-logo"><image href="/fadou-superhero.png" width="1024" height="1024" clip-path="url(#mascot-logo-clip)" filter="url(#mascot-logo-alpha)"/></g>
|
|
41
|
+
<g class="mascot-arm"><image href="/fadou-superhero.png" width="1024" height="1024" clip-path="url(#mascot-arm-clip)"/></g>
|
|
42
|
+
<g class="mascot-blink" clip-path="url(#mascot-eyes-clip)"><image href="/fadou-superhero-blink.png" x="11" y="4" width="1024" height="1024"/></g>
|
|
43
|
+
</svg>
|
|
44
|
+
</span>
|
|
45
|
+
</span>
|
|
46
|
+
</button>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
<button type="button" class="mascot-pause" aria-label="暫停法鬥超人動畫" aria-pressed="false" title="暫停動畫"><svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true"><path d="M8 5v14M16 5v14" fill="none" stroke="currentColor" stroke-width="2"/></svg></button>
|
|
50
|
+
</div>`;
|
|
51
|
+
// One lap: centre → left (6s), rest (6s), right (12s), rest (6s), centre (6s).
|
|
52
|
+
// Both feet settle at each edge; no stepping in place during the rests.
|
|
53
|
+
function footstepKeyframes(name, direction) {
|
|
54
|
+
const frames = [];
|
|
55
|
+
for (const [start, end] of [[0, 6], [12, 24], [30, 36]]) {
|
|
56
|
+
for (let time = start; time <= end; time += 0.5) {
|
|
57
|
+
const phase = (time - start) % 1;
|
|
58
|
+
const angle = time === start || time === end ? 0 : (phase === 0.5 ? -5 : 5) * direction;
|
|
59
|
+
frames.push(`${time / 36 * 100}% { transform: translateY(${angle < 0 ? -6 : 0}px) rotate(${angle}deg); }`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return `@keyframes ${name} { ${frames.join("\n")} }`;
|
|
63
|
+
}
|
|
64
|
+
exports.connectionMascotStyles = `
|
|
65
|
+
.settings-card[data-setting="連線"]:has(.connection-mascot) { row-gap: 0; }
|
|
66
|
+
.settings-card[data-setting="連線"]:has(.connection-mascot) .settings-content { padding-top: 16px; }
|
|
67
|
+
.settings-card[data-setting="連線"]:has(.connection-mascot) .connection-feedback { order: -1; }
|
|
68
|
+
.connection-mascot { position: relative; flex: 1 0 144px; width: 100%; min-height: 144px; overflow: hidden; container-type: inline-size; }
|
|
69
|
+
/* Compensate for the PNG's transparent space below the soles. */
|
|
70
|
+
.mascot-traveler { position: absolute; bottom: -4px; left: 8px; width: 136px; max-width: calc(100% - 16px); animation: mascot-walk 36s linear 780ms infinite both; }
|
|
71
|
+
.mascot-facing { display: block; animation: mascot-turn 36s linear 780ms infinite; }
|
|
72
|
+
.mascot-logo { transform-origin: 440.5px 664.5px; animation: mascot-turn 36s linear 780ms infinite; }
|
|
73
|
+
.mascot-left-foot { transform-origin: 322px 915px; animation: mascot-left-step 36s linear 780ms infinite; }
|
|
74
|
+
.mascot-right-foot { transform-origin: 580px 927px; animation: mascot-right-step 36s linear 780ms infinite; }
|
|
75
|
+
.mascot-waiting { flex-basis: 164px; min-height: 164px; padding: 12px 148px 32px 0; }
|
|
76
|
+
.mascot-waiting .mascot-traveler { left: auto; right: 8px; transform: none; }
|
|
77
|
+
.mascot-waiting .mascot-traveler, .mascot-waiting .mascot-facing, .mascot-waiting .mascot-logo, .mascot-waiting .mascot-foot, .mascot-waiting .mascot-arrival { animation: none; }
|
|
78
|
+
.mascot-speech { position: relative; padding: 10px 12px; border: 1px solid var(--line); border-radius: 12px; background: var(--input); font-size: 12px; line-height: 1.6; overflow-wrap: anywhere; }
|
|
79
|
+
.mascot-speech:has(> .connection-feedback:empty) { display: none; }
|
|
80
|
+
.mascot-speech::after { content: ''; position: absolute; right: -7px; top: 28px; width: 12px; height: 12px; background: var(--input); border-right: 1px solid var(--line); border-top: 1px solid var(--line); transform: rotate(45deg); }
|
|
81
|
+
.mascot-quote { color: var(--text); }
|
|
82
|
+
.mascot-speech .connection-error, .mascot-speech .connection-message { padding: 0; margin-top: 6px; border-radius: 0; background: transparent; color: var(--muted); font-size: 11px; }
|
|
83
|
+
.mascot-speech .connection-error svg, .mascot-speech .connection-message svg { display: none; }
|
|
84
|
+
.mascot-speaking .connection-message { margin-top: 0; color: var(--text); font-size: 12px; }
|
|
85
|
+
.mascot-speaking { flex-basis: 144px; min-height: 144px; padding-bottom: 0; }
|
|
86
|
+
main button.mascot-speech-close { margin-top: 6px; padding: 4px 0; min-height: 24px; background: transparent; color: var(--muted); border: 0; border-radius: 4px; font-size: 11px; }
|
|
87
|
+
main button.mascot-speech-close:hover { color: var(--text); }
|
|
88
|
+
main button.mascot-pause { position: absolute; right: 0; bottom: 0; display: grid; place-items: center; min-height: 24px; width: 24px; padding: 0; border: 0; border-radius: 6px; background: transparent; color: var(--muted); opacity: 0.65; }
|
|
89
|
+
main button.mascot-pause:hover, main button.mascot-pause:focus-visible { opacity: 1; background: var(--input); }
|
|
90
|
+
.connection-mascot:hover .mascot-traveler, .connection-mascot:hover .mascot-facing, .connection-mascot:hover .mascot-logo, .connection-mascot:hover .mascot-foot,
|
|
91
|
+
.connection-mascot:focus-within .mascot-traveler, .connection-mascot:focus-within .mascot-facing, .connection-mascot:focus-within .mascot-logo, .connection-mascot:focus-within .mascot-foot,
|
|
92
|
+
.connection-mascot[data-paused] * { animation-play-state: paused; }
|
|
93
|
+
.mascot-arrival { animation: mascot-arrive 780ms cubic-bezier(0.22, 1, 0.36, 1) both; transform-origin: center bottom; }
|
|
94
|
+
main button.mascot-play { display: block; width: 136px; max-width: 100%; min-height: 0; padding: 0; border: 0; border-radius: 16px; background: transparent; color: inherit; touch-action: manipulation; }
|
|
95
|
+
.mascot-play:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
96
|
+
.mascot-play:active { transform: scale(0.97); }
|
|
97
|
+
.mascot-idle { display: block; user-select: none; transform-origin: 50% 97%; animation: mascot-breathe 3200ms ease-in-out 780ms infinite; }
|
|
98
|
+
.mascot-art { display: block; width: 100%; height: auto; overflow: visible; }
|
|
99
|
+
.mascot-cape { transform-origin: 680px 530px; animation: mascot-cape 2400ms ease-in-out infinite; }
|
|
100
|
+
.mascot-arm { transform-origin: 235px 540px; }
|
|
101
|
+
.mascot-blink { opacity: 0; animation: mascot-blink 7400ms steps(1, end) infinite; }
|
|
102
|
+
@keyframes mascot-arrive {
|
|
103
|
+
0% { opacity: 0; transform: translateY(16px) scale(0.9); }
|
|
104
|
+
35% { opacity: 1; transform: translateY(-7px) scale(1.02); }
|
|
105
|
+
60% { transform: translateY(2px) scale(0.99); }
|
|
106
|
+
80% { transform: translateY(-2px) scale(1); }
|
|
107
|
+
100% { opacity: 1; transform: translateY(0) scale(1); }
|
|
108
|
+
}
|
|
109
|
+
@keyframes mascot-breathe {
|
|
110
|
+
0%, 100% { transform: translateY(0) scale(1); }
|
|
111
|
+
50% { transform: scale(1.012, 1.018); }
|
|
112
|
+
}
|
|
113
|
+
@keyframes mascot-cape {
|
|
114
|
+
0%, 100% { transform: rotate(0deg) scale(1); }
|
|
115
|
+
50% { transform: rotate(-1.5deg) scale(1.012, 0.99); }
|
|
116
|
+
}
|
|
117
|
+
@keyframes mascot-blink {
|
|
118
|
+
0%, 42%, 85%, 90%, 100% { opacity: 0; }
|
|
119
|
+
40%, 83%, 88% { opacity: 1; }
|
|
120
|
+
}
|
|
121
|
+
@keyframes mascot-walk {
|
|
122
|
+
0%, 100% { transform: translateX(max(0px, calc((100cqw - 152px) / 2))); }
|
|
123
|
+
16.666667%, 33.333333% { transform: translateX(0); }
|
|
124
|
+
66.666667%, 83.333333% { transform: translateX(max(0px, calc(100cqw - 152px))); }
|
|
125
|
+
}
|
|
126
|
+
@keyframes mascot-turn {
|
|
127
|
+
0%, 16.666667% { transform: scaleX(1); }
|
|
128
|
+
18%, 66.666667% { transform: scaleX(-1); }
|
|
129
|
+
68%, 100% { transform: scaleX(1); }
|
|
130
|
+
}
|
|
131
|
+
${footstepKeyframes("mascot-left-step", 1)}
|
|
132
|
+
${footstepKeyframes("mascot-right-step", -1)}
|
|
133
|
+
@media (prefers-reduced-motion: reduce) {
|
|
134
|
+
.mascot-arrival, .mascot-idle, .mascot-cape, .mascot-blink, .mascot-traveler, .mascot-facing, .mascot-logo, .mascot-foot { animation: none; }
|
|
135
|
+
.mascot-traveler { left: 50%; transform: translateX(-50%); }
|
|
136
|
+
.mascot-waiting .mascot-traveler { left: auto; transform: none; }
|
|
137
|
+
main button.mascot-pause { display: none; }
|
|
138
|
+
.mascot-play:active { transform: none; }
|
|
139
|
+
}
|
|
140
|
+
@media (prefers-contrast: more) { .mascot-play:focus-visible { outline-width: 3px; } }
|
|
141
|
+
`;
|
|
142
|
+
exports.connectionMascotScript = `
|
|
143
|
+
var mascotSpeechDismissed = false;
|
|
144
|
+
function updateCopyFeedback(updated) {
|
|
145
|
+
var card = document.querySelector('[data-setting="連線"]');
|
|
146
|
+
var nextCard = updated.querySelector('[data-setting="連線"]');
|
|
147
|
+
var stage = card?.querySelector('.connection-mascot') || document.querySelector('.classroom-update .connection-mascot');
|
|
148
|
+
var nextStage = nextCard?.querySelector('.connection-mascot');
|
|
149
|
+
if (!stage || !nextStage || !nextCard.querySelector('.connection-copy')) return false;
|
|
150
|
+
var speech = nextStage.querySelector('.mascot-speech');
|
|
151
|
+
var feedback = nextCard.querySelector('.settings-content > .connection-feedback');
|
|
152
|
+
if (!speech && !feedback) return false;
|
|
153
|
+
card.querySelector('.settings-content > .connection-feedback')?.remove();
|
|
154
|
+
stage.querySelector('.mascot-speech')?.remove();
|
|
155
|
+
if (nextStage.classList.contains('mascot-speaking')) {
|
|
156
|
+
if (!speech) return false;
|
|
157
|
+
mascotSpeechDismissed = false;
|
|
158
|
+
stage.classList.add('mascot-waiting', 'mascot-speaking');
|
|
159
|
+
stage.prepend(speech);
|
|
160
|
+
} else {
|
|
161
|
+
if (stage.classList.contains('mascot-speaking')) hideMascotSpeech(stage);
|
|
162
|
+
card.querySelector('.settings-content').append(feedback);
|
|
163
|
+
}
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
function hideMascotSpeech(stage) {
|
|
167
|
+
stage.querySelector('.mascot-speech')?.remove();
|
|
168
|
+
stage.classList.remove('mascot-waiting', 'mascot-speaking');
|
|
169
|
+
stage.getAnimations({ subtree: true }).forEach(function (animation) {
|
|
170
|
+
if (animation.animationName === 'mascot-arrive') animation.currentTime = 780;
|
|
171
|
+
if (['mascot-walk', 'mascot-turn', 'mascot-left-step', 'mascot-right-step'].includes(animation.animationName)) animation.currentTime = 27780;
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
function mascotAnimationState() {
|
|
175
|
+
var mascot = document.querySelector('.connection-mascot');
|
|
176
|
+
return { speechDismissed: mascotSpeechDismissed, paused: mascot?.hasAttribute('data-paused') || false, animations: mascot ? mascot.getAnimations({ subtree: true }).filter(function (animation) {
|
|
177
|
+
return typeof animation.animationName === 'string';
|
|
178
|
+
}).map(function (animation) {
|
|
179
|
+
return { name: animation.animationName, time: animation.currentTime };
|
|
180
|
+
}) : [] };
|
|
181
|
+
}
|
|
182
|
+
function restoreMascotAnimationState(state) {
|
|
183
|
+
var mascot = document.querySelector('.connection-mascot');
|
|
184
|
+
if (!mascot) return;
|
|
185
|
+
mascotSpeechDismissed = Boolean(state.speechDismissed);
|
|
186
|
+
if (mascotSpeechDismissed && mascot.classList.contains('mascot-speaking')) hideMascotSpeech(mascot);
|
|
187
|
+
if (state.paused) {
|
|
188
|
+
mascot.setAttribute('data-paused', '');
|
|
189
|
+
var pause = mascot.querySelector('.mascot-pause');
|
|
190
|
+
pause.setAttribute('aria-pressed', 'true');
|
|
191
|
+
pause.setAttribute('aria-label', '繼續法鬥超人動畫');
|
|
192
|
+
pause.title = '繼續動畫';
|
|
193
|
+
}
|
|
194
|
+
mascot.getAnimations({ subtree: true }).forEach(function (animation) {
|
|
195
|
+
var saved = (state.animations || []).find(function (value) { return value.name === animation.animationName; });
|
|
196
|
+
if (saved && saved.time !== null) animation.currentTime = saved.time;
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
document.addEventListener('click', function (event) {
|
|
200
|
+
var close = event.target.closest?.('.mascot-speech-close');
|
|
201
|
+
if (close && !close.disabled) {
|
|
202
|
+
var speaking = close.closest('.connection-mascot');
|
|
203
|
+
mascotSpeechDismissed = true;
|
|
204
|
+
hideMascotSpeech(speaking);
|
|
205
|
+
document.querySelector('[data-setting="連線"] .connection-copy')?.focus({ preventScroll: true });
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
var pause = event.target.closest?.('.mascot-pause');
|
|
209
|
+
if (pause && !pause.disabled) {
|
|
210
|
+
var stage = pause.closest('.connection-mascot');
|
|
211
|
+
var paused = stage.toggleAttribute('data-paused');
|
|
212
|
+
pause.setAttribute('aria-pressed', String(paused));
|
|
213
|
+
pause.setAttribute('aria-label', paused ? '繼續法鬥超人動畫' : '暫停法鬥超人動畫');
|
|
214
|
+
pause.title = paused ? '繼續動畫' : '暫停動畫';
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
var button = event.target.closest?.('.mascot-play');
|
|
218
|
+
if (!button || button.disabled) return;
|
|
219
|
+
var reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
220
|
+
var arm = button.querySelector('.mascot-arm');
|
|
221
|
+
arm.getAnimations().forEach(function (animation) { animation.cancel(); });
|
|
222
|
+
if (!reduced) arm.animate([
|
|
223
|
+
{ transform: 'rotate(0deg)' }, { transform: 'rotate(-8deg)', offset: 0.25 },
|
|
224
|
+
{ transform: 'rotate(5deg)', offset: 0.5 }, { transform: 'rotate(-6deg)', offset: 0.75 },
|
|
225
|
+
{ transform: 'rotate(0deg)' }
|
|
226
|
+
], { duration: 800, easing: 'ease-in-out' });
|
|
227
|
+
button.getAnimations().forEach(function (animation) { animation.cancel(); });
|
|
228
|
+
button.animate(reduced ? [{ opacity: 0.65 }, { opacity: 1 }] : [
|
|
229
|
+
{ transform: 'translateY(0) rotate(0deg)' },
|
|
230
|
+
{ transform: 'translateY(-8px) rotate(-4deg)', offset: 0.35 },
|
|
231
|
+
{ transform: 'translateY(-3px) rotate(3deg)', offset: 0.65 },
|
|
232
|
+
{ transform: 'translateY(0) rotate(0deg)' }
|
|
233
|
+
], { duration: reduced ? 180 : 600, easing: 'ease-out' });
|
|
234
|
+
});
|
|
235
|
+
`;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ACTION_KIND_LABELS = exports.ACTION_KINDS = void 0;
|
|
4
|
+
exports.actionKindLabel = actionKindLabel;
|
|
5
|
+
exports.parseCourseCatalog = parseCourseCatalog;
|
|
6
|
+
const yaml_1 = require("yaml");
|
|
7
|
+
exports.ACTION_KINDS = ["skill", "package", "mcp"];
|
|
8
|
+
exports.ACTION_KIND_LABELS = {
|
|
9
|
+
skill: "Skill",
|
|
10
|
+
package: "套件",
|
|
11
|
+
mcp: "MCP",
|
|
12
|
+
};
|
|
13
|
+
function isNonEmptyString(value) {
|
|
14
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
15
|
+
}
|
|
16
|
+
function isActionKind(value) {
|
|
17
|
+
return typeof value === "string" && exports.ACTION_KINDS.includes(value);
|
|
18
|
+
}
|
|
19
|
+
function actionKindLabel(kind) {
|
|
20
|
+
return exports.ACTION_KIND_LABELS[kind];
|
|
21
|
+
}
|
|
22
|
+
/** 解析 Course Catalog YAML。失敗時不回半套清單。 */
|
|
23
|
+
function parseCourseCatalog(source) {
|
|
24
|
+
let doc;
|
|
25
|
+
try {
|
|
26
|
+
doc = (0, yaml_1.parse)(source);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
30
|
+
return { ok: false, error: `YAML 無法解析:${message}` };
|
|
31
|
+
}
|
|
32
|
+
if (!doc || typeof doc !== "object" || Array.isArray(doc)) {
|
|
33
|
+
return { ok: false, error: "根層必須是物件,且含 actions 陣列" };
|
|
34
|
+
}
|
|
35
|
+
const actionsRaw = doc.actions;
|
|
36
|
+
if (!Array.isArray(actionsRaw)) {
|
|
37
|
+
return { ok: false, error: "缺少頂層鍵 actions(陣列)" };
|
|
38
|
+
}
|
|
39
|
+
const actions = [];
|
|
40
|
+
for (let i = 0; i < actionsRaw.length; i++) {
|
|
41
|
+
const row = actionsRaw[i];
|
|
42
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) {
|
|
43
|
+
return { ok: false, error: `actions[${i}] 必須是物件` };
|
|
44
|
+
}
|
|
45
|
+
const { id, title, command, description, kind } = row;
|
|
46
|
+
if (!isNonEmptyString(id) || !isNonEmptyString(title) || !isNonEmptyString(command)) {
|
|
47
|
+
return {
|
|
48
|
+
ok: false,
|
|
49
|
+
error: `actions[${i}] 缺少必填欄位 id/title/command`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const kindValue = isNonEmptyString(kind) ? kind.trim() : kind;
|
|
53
|
+
if (!isActionKind(kindValue)) {
|
|
54
|
+
return {
|
|
55
|
+
ok: false,
|
|
56
|
+
error: `actions[${i}].kind 必須是 skill/package/mcp`,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const action = {
|
|
60
|
+
id: id.trim(),
|
|
61
|
+
title: title.trim(),
|
|
62
|
+
kind: kindValue,
|
|
63
|
+
command: command.trim(),
|
|
64
|
+
};
|
|
65
|
+
if (description !== undefined) {
|
|
66
|
+
if (!isNonEmptyString(description)) {
|
|
67
|
+
return {
|
|
68
|
+
ok: false,
|
|
69
|
+
error: `actions[${i}].description 若提供須為非空字串`,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
action.description = description.trim();
|
|
73
|
+
}
|
|
74
|
+
actions.push(action);
|
|
75
|
+
}
|
|
76
|
+
const snippetsResult = parseSnippets(doc.snippets);
|
|
77
|
+
if (!snippetsResult.ok) {
|
|
78
|
+
return snippetsResult;
|
|
79
|
+
}
|
|
80
|
+
return { ok: true, actions, snippets: snippetsResult.snippets };
|
|
81
|
+
}
|
|
82
|
+
function parseSnippets(snippetsRaw) {
|
|
83
|
+
if (snippetsRaw === undefined || snippetsRaw === null) {
|
|
84
|
+
return { ok: true, snippets: [] };
|
|
85
|
+
}
|
|
86
|
+
if (!Array.isArray(snippetsRaw)) {
|
|
87
|
+
return { ok: false, error: "頂層鍵 snippets 若提供須為陣列" };
|
|
88
|
+
}
|
|
89
|
+
const snippets = [];
|
|
90
|
+
const seenIds = new Set();
|
|
91
|
+
for (let i = 0; i < snippetsRaw.length; i++) {
|
|
92
|
+
const row = snippetsRaw[i];
|
|
93
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) {
|
|
94
|
+
return { ok: false, error: `snippets[${i}] 必須是物件` };
|
|
95
|
+
}
|
|
96
|
+
const { id, title, body, paste_hint: pasteHintRaw } = row;
|
|
97
|
+
if (!isNonEmptyString(id) || !isNonEmptyString(title)) {
|
|
98
|
+
return {
|
|
99
|
+
ok: false,
|
|
100
|
+
error: `snippets[${i}] 缺少必填欄位 id/title/body`,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
if (typeof body !== "string" || body.length === 0) {
|
|
104
|
+
return {
|
|
105
|
+
ok: false,
|
|
106
|
+
error: `snippets[${i}] 缺少必填欄位 id/title/body`,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const snippetId = id.trim();
|
|
110
|
+
if (seenIds.has(snippetId)) {
|
|
111
|
+
return { ok: false, error: `snippets 內 id 重複:${snippetId}` };
|
|
112
|
+
}
|
|
113
|
+
seenIds.add(snippetId);
|
|
114
|
+
const snippet = {
|
|
115
|
+
id: snippetId,
|
|
116
|
+
title: title.trim(),
|
|
117
|
+
body,
|
|
118
|
+
};
|
|
119
|
+
if (pasteHintRaw !== undefined) {
|
|
120
|
+
if (!isNonEmptyString(pasteHintRaw)) {
|
|
121
|
+
return {
|
|
122
|
+
ok: false,
|
|
123
|
+
error: `snippets[${i}].paste_hint 若提供須為非空字串`,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
snippet.pasteHint = pasteHintRaw.trim();
|
|
127
|
+
}
|
|
128
|
+
snippets.push(snippet);
|
|
129
|
+
}
|
|
130
|
+
return { ok: true, snippets };
|
|
131
|
+
}
|
package/dist/desktop.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.openBrowser = openBrowser;
|
|
7
|
+
exports.asset = asset;
|
|
8
|
+
const node_child_process_1 = require("node:child_process");
|
|
9
|
+
const node_sea_1 = require("node:sea");
|
|
10
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
11
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
12
|
+
function openBrowser(url) {
|
|
13
|
+
if (process.env.CLASSROOM_APP_HEADLESS === "1")
|
|
14
|
+
return;
|
|
15
|
+
const child = process.platform === "win32"
|
|
16
|
+
? (0, node_child_process_1.spawn)("cmd.exe", ["/d", "/c", "start", "", url], { detached: true, stdio: "ignore", windowsHide: true })
|
|
17
|
+
: (0, node_child_process_1.spawn)("open", [url], { detached: true, stdio: "ignore" });
|
|
18
|
+
child.on("error", () => process.stderr.write("請用瀏覽器開啟顯示的本機網址。\n"));
|
|
19
|
+
child.unref();
|
|
20
|
+
}
|
|
21
|
+
function asset(name) {
|
|
22
|
+
return (0, node_sea_1.isSea)()
|
|
23
|
+
? Promise.resolve(Buffer.from((0, node_sea_1.getAsset)(name)))
|
|
24
|
+
: promises_1.default.readFile(node_path_1.default.join(__dirname, "..", name.startsWith("brand-") || name.startsWith("fadou-superhero") ? "media" : "scripts", name));
|
|
25
|
+
}
|
package/dist/entry.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const launcher_1 = require("./launcher");
|
|
7
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const instance_1 = require("./instance");
|
|
10
|
+
void (0, launcher_1.runCli)().catch(async (error) => {
|
|
11
|
+
const message = error instanceof Error ? error.message : "VansPod 操作失敗。";
|
|
12
|
+
if (process.env.VANSPOD_WORKER_TOKEN) {
|
|
13
|
+
await promises_1.default.writeFile(node_path_1.default.join((0, instance_1.managementDirectory)(), "startup-error.json"), JSON.stringify({ message }), { mode: 0o600 }).catch(() => undefined);
|
|
14
|
+
}
|
|
15
|
+
process.stderr.write(`${message}\n`);
|
|
16
|
+
process.exitCode = 1;
|
|
17
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.environmentIcons = void 0;
|
|
4
|
+
// Brand icons from https://github.com/simple-icons/simple-icons (CC0-1.0).
|
|
5
|
+
// PowerShell from 11.15.0; other icons from develop.
|
|
6
|
+
exports.environmentIcons = {
|
|
7
|
+
"uv": "<svg aria-hidden=\"true\" fill=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m0 .1058.0504 11.9496.0403 9.5597c.0055 1.3199 1.08 2.3854 2.4 2.3798l9.5596-.0403 5.9749-.0252.6075-.0026c1.316-.0056 2.3799-1.0963 2.3799-2.4123h1.0946v2.3894L24 23.9042 23.8992.005 12.9056.0513l.0463 9.5245v5.9637h-1.9583L11.04 9.584 10.9936.0594Z\"/></svg>",
|
|
8
|
+
"git": "<svg aria-hidden=\"true\" fill=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M13.09 23.549a1.54 1.54 0 0 1-2.18 0L.451 13.089a1.54 1.54 0 0 1 0-2.179l7.191-7.19 2.733 2.733a1.85 1.85 0 0 0 .964 2.326v6.66a1.849 1.849 0 1 0 1.54 0V8.957l2.508 2.508a1.85 1.85 0 1 0 1.09-1.09l-2.634-2.634a1.85 1.85 0 0 0-2.378-2.377L8.73 2.63 10.91.451a1.54 1.54 0 0 1 2.179 0l10.459 10.46a1.54 1.54 0 0 1 0 2.179z\"/></svg>",
|
|
9
|
+
"node": "<svg aria-hidden=\"true\" fill=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M11.998,24c-0.321,0-0.641-0.084-0.922-0.247l-2.936-1.737c-0.438-0.245-0.224-0.332-0.08-0.383 c0.585-0.203,0.703-0.25,1.328-0.604c0.065-0.037,0.151-0.023,0.218,0.017l2.256,1.339c0.082,0.045,0.197,0.045,0.272,0l8.795-5.076 c0.082-0.047,0.134-0.141,0.134-0.238V6.921c0-0.099-0.053-0.192-0.137-0.242l-8.791-5.072c-0.081-0.047-0.189-0.047-0.271,0 L3.075,6.68C2.99,6.729,2.936,6.825,2.936,6.921v10.15c0,0.097,0.054,0.189,0.139,0.235l2.409,1.392 c1.307,0.654,2.108-0.116,2.108-0.89V7.787c0-0.142,0.114-0.253,0.256-0.253h1.115c0.139,0,0.255,0.112,0.255,0.253v10.021 c0,1.745-0.95,2.745-2.604,2.745c-0.508,0-0.909,0-2.026-0.551L2.28,18.675c-0.57-0.329-0.922-0.945-0.922-1.604V6.921 c0-0.659,0.353-1.275,0.922-1.603l8.795-5.082c0.557-0.315,1.296-0.315,1.848,0l8.794,5.082c0.57,0.329,0.924,0.944,0.924,1.603 v10.15c0,0.659-0.354,1.273-0.924,1.604l-8.794,5.078C12.643,23.916,12.324,24,11.998,24z M19.099,13.993 c0-1.9-1.284-2.406-3.987-2.763c-2.731-0.361-3.009-0.548-3.009-1.187c0-0.528,0.235-1.233,2.258-1.233 c1.807,0,2.473,0.389,2.747,1.607c0.024,0.115,0.129,0.199,0.247,0.199h1.141c0.071,0,0.138-0.031,0.186-0.081 c0.048-0.054,0.074-0.123,0.067-0.196c-0.177-2.098-1.571-3.076-4.388-3.076c-2.508,0-4.004,1.058-4.004,2.833 c0,1.925,1.488,2.457,3.895,2.695c2.88,0.282,3.103,0.703,3.103,1.269c0,0.983-0.789,1.402-2.642,1.402 c-2.327,0-2.839-0.584-3.011-1.742c-0.02-0.124-0.126-0.215-0.253-0.215h-1.137c-0.141,0-0.254,0.112-0.254,0.253 c0,1.482,0.806,3.248,4.655,3.248C17.501,17.007,19.099,15.91,19.099,13.993z\"/></svg>",
|
|
10
|
+
"pwsh": "<svg aria-hidden=\"true\" fill=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M23.181 2.974c.568 0 .923.463.792 1.035l-3.659 15.982c-.13.572-.697 1.035-1.265 1.035H.819c-.568 0-.923-.463-.792-1.035L3.686 4.009c.13-.572.697-1.035 1.265-1.035zm-8.375 9.346c.251-.394.227-.905-.09-1.243L9.122 5.125c-.38-.404-1.037-.407-1.466-.003-.429.402-.468 1.056-.088 1.46l4.662 4.96v.11l-7.42 5.374c-.45.327-.533.977-.187 1.453.346.476.991.597 1.44.27l8.229-5.91c.28-.196.438-.365.514-.52zm-2.796 4.399a.928.928 0 00-.934.923c0 .51.418.923.934.923h4.433a.928.928 0 00.934-.923.928.928 0 00-.934-.923z\"/></svg>"
|
|
11
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.exitDecision = exitDecision;
|
|
4
|
+
exports.removalInterruptions = removalInterruptions;
|
|
5
|
+
/** 安裝與模型回答合併成一次確認;未確認不中斷任何工作。 */
|
|
6
|
+
function exitDecision(action, work, confirmed) {
|
|
7
|
+
const running = [
|
|
8
|
+
...(work.installing ? ["安裝仍在執行"] : []),
|
|
9
|
+
...(work.activeRequests ? ["仍有模型回答"] : []),
|
|
10
|
+
];
|
|
11
|
+
if (!running.length || confirmed)
|
|
12
|
+
return { proceed: true };
|
|
13
|
+
return { proceed: false, message: `${running.join("、")},確認中止並${action === "stop" ? "結束" : "移除"} VansPod?` };
|
|
14
|
+
}
|
|
15
|
+
function removalInterruptions(work) {
|
|
16
|
+
return `${work.installing ? "\n安裝仍在執行,移除前將中止安裝。" : ""}${work.activeRequests ? "\n仍有模型回答,移除將中斷目前回答。" : ""}`;
|
|
17
|
+
}
|