pi-web-ui 0.97.0 → 0.99.0
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/CHANGELOG.md +126 -1
- package/README.md +3 -3
- package/README.zh-CN.md +3 -3
- package/bin/pi-web-ui.mjs +64 -23
- package/dist/server/agent-service.js +645 -147
- package/dist/server/approval-rules.js +5 -10
- package/dist/server/atomic-file.js +40 -0
- package/dist/server/attachments.js +22 -0
- package/dist/server/claim-store.js +5 -11
- package/dist/server/client-state.js +220 -23
- package/dist/server/compact-context-tool.js +3 -1
- package/dist/server/compacted-history.js +7 -5
- package/dist/server/composer-drafts.js +4 -6
- package/dist/server/control-socket.js +0 -40
- package/dist/server/delegate-mode.js +6 -12
- package/dist/server/dsh/dsh-agent-service.js +32 -30
- package/dist/server/dsh/dsh-client.js +45 -9
- package/dist/server/dsh/dsh-serialize.js +12 -10
- package/dist/server/dsh/dsh-sessions.js +20 -6
- package/dist/server/dsh/runtime/goal-rpc.mjs +14 -1
- package/dist/server/dsh/runtime/runtime-root.mjs +1 -1
- package/dist/server/ensure-bash.js +3 -5
- package/dist/server/eval-tool.js +30 -5
- package/dist/server/goal-review-gate.js +7 -20
- package/dist/server/goal-service.js +77 -10
- package/dist/server/index.js +83 -15
- package/dist/server/launch-origin.js +0 -4
- package/dist/server/locales.js +3 -5
- package/dist/server/lsp-tool.js +110 -14
- package/dist/server/markers/builtins/notify.js +4 -2
- package/dist/server/markers/builtins/rename.js +0 -1
- package/dist/server/markers/builtins/todo.js +6 -26
- package/dist/server/markers/marker.js +0 -15
- package/dist/server/markers/registry.js +0 -3
- package/dist/server/markers/store.js +0 -3
- package/dist/server/mcp-hot-reload.js +12 -5
- package/dist/server/model-admin.js +19 -18
- package/dist/server/model-enrich.js +2 -3
- package/dist/server/patch-tool.js +78 -28
- package/dist/server/permission-preset.js +25 -0
- package/dist/server/plan-manager.js +196 -10
- package/dist/server/plan-mode.js +58 -20
- package/dist/server/plugin-api-catalog.js +2 -0
- package/dist/server/plugin-catalog.js +6 -6
- package/dist/server/plugin-dom.js +3 -4
- package/dist/server/plugin-facilities.js +3 -5
- package/dist/server/plugin-grants.js +5 -8
- package/dist/server/plugin-permissions.js +4 -6
- package/dist/server/plugin-schedule.js +4 -7
- package/dist/server/plugin-updater.js +151 -38
- package/dist/server/plugins.js +16 -2
- package/dist/server/prompt-composer.js +12 -3
- package/dist/server/protocol-version.js +1 -1
- package/dist/server/resolve-global-sdk.js +8 -0
- package/dist/server/schedule-agent-tool.js +5 -1
- package/dist/server/scheduler-tasks.js +46 -27
- package/dist/server/sdk-origin.js +27 -0
- package/dist/server/serialize.js +22 -9
- package/dist/server/slash-commands.js +16 -0
- package/dist/server/subagent-templates.js +4 -10
- package/dist/server/subagents.js +22 -11
- package/dist/server/terminals.js +24 -12
- package/dist/server/text-quote.js +59 -0
- package/dist/server/tool-manager.js +12 -3
- package/dist/server/tool-overrides.js +24 -0
- package/dist/server/update-check.js +69 -40
- package/extensions/webui.ts +15 -1
- package/package.json +31 -19
- package/plugin-sdk/client-utils.mjs +92 -0
- package/plugin-sdk/index.d.ts +21 -0
- package/plugin-sdk/index.mjs +2 -0
- package/plugins/catalog.json +7 -0
- package/web/dist/assets/{TerminalPanel-CITBqQrb.js → TerminalPanel-NerrJlTS.js} +1 -1
- package/web/dist/assets/index-BdMaGCcs.css +1 -0
- package/web/dist/assets/index-ByCBN4Kt.js +368 -0
- package/web/dist/index.html +2 -2
- package/web/dist/sw.js +2 -1
- package/web/public/sw.js +2 -1
- package/web/dist/assets/index-DwToli0J.css +0 -1
- package/web/dist/assets/index-oI7UKOqJ.js +0 -368
|
@@ -32,21 +32,6 @@ export function compareVersions(a, b) {
|
|
|
32
32
|
}
|
|
33
33
|
return 0;
|
|
34
34
|
}
|
|
35
|
-
/**
|
|
36
|
-
* Cache a zero-arg function's value for ttlMs. Plain value memoization: the
|
|
37
|
-
* pi probe returns null on failure instead of throwing, so errors thread
|
|
38
|
-
* through as ordinary values and there is nothing to rethrow.
|
|
39
|
-
*/
|
|
40
|
-
export function memoizeWithTtl(fn, ttlMs) {
|
|
41
|
-
let entry = null;
|
|
42
|
-
return () => {
|
|
43
|
-
const now = Date.now();
|
|
44
|
-
if (!entry || now - entry.at >= ttlMs) {
|
|
45
|
-
entry = { at: now, value: fn() };
|
|
46
|
-
}
|
|
47
|
-
return entry.value;
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
35
|
/**
|
|
51
36
|
* Parse `pi --version` stdout into a version string, or null. Two-stage:
|
|
52
37
|
* prefer a line that is exactly the version (optional leading "v", optional
|
|
@@ -375,29 +360,43 @@ function piCliOnPath() {
|
|
|
375
360
|
}
|
|
376
361
|
return null;
|
|
377
362
|
}
|
|
363
|
+
/** Read a pi-core package.json's version, or null when absent, not ours, or
|
|
364
|
+
* unreadable. Shared by both resolution strategies below. */
|
|
365
|
+
function readPiCoreVersionAt(pkgPath) {
|
|
366
|
+
if (!existsSync(pkgPath))
|
|
367
|
+
return null;
|
|
368
|
+
try {
|
|
369
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
370
|
+
if (pkg.name === PI_CORE_PACKAGE && pkg.version)
|
|
371
|
+
return pkg.version;
|
|
372
|
+
}
|
|
373
|
+
catch {
|
|
374
|
+
/* unreadable package.json */
|
|
375
|
+
}
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
378
|
/**
|
|
379
|
-
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
379
|
+
* <binDir>/node_modules/<pkg>/package.json — the concrete layout a global npm
|
|
380
|
+
* install always uses. This is the FIRST candidate because it is the only one
|
|
381
|
+
* that works on Windows: npm's cmd-shim writes a regular `pi` shell script
|
|
382
|
+
* (not a symlink into the package), so the realpath walk below would start
|
|
383
|
+
* from <prefix> itself and never see the package one level down (issue #533).
|
|
382
384
|
*/
|
|
383
|
-
function
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
385
|
+
function versionFromSiblingNodeModules(bin) {
|
|
386
|
+
return readPiCoreVersionAt(join(dirname(bin), "node_modules", ...PI_CORE_PACKAGE.split("/"), "package.json"));
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* POSIX: `pi` is a symlink into <prefix>/node_modules/<pkg>/dist/bundle/cli.js
|
|
390
|
+
* — realpath it and walk up (≤8 levels) until a package.json names our
|
|
391
|
+
* package. Functionally unchanged from the original probe.
|
|
392
|
+
*/
|
|
393
|
+
function versionFromSymlinkWalk(bin) {
|
|
387
394
|
try {
|
|
388
395
|
let dir = dirname(realpathSync(bin));
|
|
389
396
|
for (let i = 0; i < 8; i++) {
|
|
390
|
-
const
|
|
391
|
-
if (
|
|
392
|
-
|
|
393
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
394
|
-
if (pkg.name === PI_CORE_PACKAGE && pkg.version)
|
|
395
|
-
return pkg.version;
|
|
396
|
-
}
|
|
397
|
-
catch {
|
|
398
|
-
/* unreadable package.json — keep walking */
|
|
399
|
-
}
|
|
400
|
-
}
|
|
397
|
+
const version = readPiCoreVersionAt(join(dir, "package.json"));
|
|
398
|
+
if (version)
|
|
399
|
+
return version;
|
|
401
400
|
const parent = dirname(dir);
|
|
402
401
|
if (parent === dir)
|
|
403
402
|
break;
|
|
@@ -409,6 +408,17 @@ function readPiCoreVersionFromDisk() {
|
|
|
409
408
|
}
|
|
410
409
|
return null;
|
|
411
410
|
}
|
|
411
|
+
/**
|
|
412
|
+
* Read the pi core version from disk: resolve the `pi` bin on PATH, then try
|
|
413
|
+
* the concrete global-npm layout first (Windows) and the POSIX symlink walk
|
|
414
|
+
* second. Exported for unit tests — defaultProbePiCore memoizes its result.
|
|
415
|
+
*/
|
|
416
|
+
export function readPiCoreVersionFromDisk() {
|
|
417
|
+
const bin = piCliOnPath();
|
|
418
|
+
if (!bin)
|
|
419
|
+
return null;
|
|
420
|
+
return versionFromSiblingNodeModules(bin) ?? versionFromSymlinkWalk(bin);
|
|
421
|
+
}
|
|
412
422
|
/**
|
|
413
423
|
* Default pi core probe: run the globally installed `pi --version`, memoized
|
|
414
424
|
* machine-wide for PI_PROBE_TTL_MS so repeated collectTargets calls never
|
|
@@ -485,13 +495,16 @@ export function isHostProvidedPackage(name) {
|
|
|
485
495
|
export function collectTargets(agentDir, webuiVersion, probePiCore = defaultProbePiCore, opts) {
|
|
486
496
|
const targets = [{ name: "pi-web-ui", version: webuiVersion, kind: "webui" }];
|
|
487
497
|
const coreVersion = probePiCore() ?? readVendoredPiCore(agentDir);
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
498
|
+
// issue #533: keep the pi-core row even when the version is undetectable
|
|
499
|
+
// (e.g. unreadable global install) instead of silently dropping it — an
|
|
500
|
+
// honest "could not detect" row beats a missing one. checkAll resolves the
|
|
501
|
+
// key into the item's localized `error`.
|
|
502
|
+
targets.push({
|
|
503
|
+
name: PI_CORE_PACKAGE,
|
|
504
|
+
version: coreVersion ?? "unknown",
|
|
505
|
+
kind: "pi-core",
|
|
506
|
+
...(coreVersion ? {} : { error: "pluginupdate.piCore.unknown" }),
|
|
507
|
+
});
|
|
495
508
|
targets.push(...listInstalledPackages(agentDir).filter((pkg) => !isHostProvidedPackage(pkg.name)));
|
|
496
509
|
targets.push(...listGitExtensions(agentDir, opts?.projectCwd));
|
|
497
510
|
return targets;
|
|
@@ -636,6 +649,22 @@ gitCheck = defaultCheckGitExtension) {
|
|
|
636
649
|
while (cursor < targets.length) {
|
|
637
650
|
const i = cursor++;
|
|
638
651
|
const t = targets[i];
|
|
652
|
+
if (t.error) {
|
|
653
|
+
// Pre-flagged target (issue #533, e.g. undetectable pi-core
|
|
654
|
+
// version): `error` carries an i18n key resolved here so
|
|
655
|
+
// collectTargets stays language-free. No registry lookup.
|
|
656
|
+
results[i] = {
|
|
657
|
+
name: t.name,
|
|
658
|
+
kind: t.kind,
|
|
659
|
+
current: t.version,
|
|
660
|
+
latest: null,
|
|
661
|
+
latestPublishedAt: null,
|
|
662
|
+
upToDate: false,
|
|
663
|
+
...(t.source ? { source: t.source } : {}),
|
|
664
|
+
error: pick(l, "无法检测已安装的引擎版本(可能未装全局 pi,或安装方式不标准),可手动检查。", "Could not detect the installed engine version (no global pi, or a non-standard install); check manually.", t.error),
|
|
665
|
+
};
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
639
668
|
if (t.kind === "git-extension") {
|
|
640
669
|
try {
|
|
641
670
|
if (!t.installDir)
|
package/extensions/webui.ts
CHANGED
|
@@ -215,14 +215,28 @@ export default function (pi: ExtensionAPI): void {
|
|
|
215
215
|
const cwd = opts.cwd ?? ctx.cwd;
|
|
216
216
|
const url = `http://localhost:${port}`;
|
|
217
217
|
|
|
218
|
+
// 探测并向子进程显式透传宿主 Pi SDK 路径(issue #482)
|
|
219
|
+
let hostSdkDir: string | undefined;
|
|
220
|
+
try {
|
|
221
|
+
const resolved = import.meta.resolve?.("@earendil-works/pi-coding-agent");
|
|
222
|
+
if (resolved) {
|
|
223
|
+
let p = resolved.startsWith("file:") ? fileURLToPath(resolved) : resolved;
|
|
224
|
+
if (p.endsWith("index.js") || p.endsWith("index.mjs")) p = dirname(p);
|
|
225
|
+
if (existsSync(join(p, "package.json"))) hostSdkDir = p;
|
|
226
|
+
}
|
|
227
|
+
} catch {}
|
|
228
|
+
|
|
218
229
|
const env = {
|
|
219
230
|
...process.env,
|
|
220
231
|
PORT: String(port),
|
|
221
232
|
PI_WEB_PORT: String(port), // server/index.js 读取 PI_WEB_PORT
|
|
222
233
|
PI_WEB_CWD: cwd,
|
|
234
|
+
...(hostSdkDir ? { PI_WEB_SDK_DIR: hostSdkDir } : {}),
|
|
223
235
|
...(process.env.PI_WEB_DATA_DIR ? {} : { PI_WEB_DATA_DIR: join(cwd, ".pi-web") }),
|
|
224
236
|
};
|
|
225
|
-
const
|
|
237
|
+
const hookPath = join(PKG_ROOT, "dist", "server", "resolve-global-sdk.js");
|
|
238
|
+
const nodeArgs = existsSync(hookPath) ? ["--import", hookPath, SERVER_ENTRY] : [SERVER_ENTRY];
|
|
239
|
+
const proc = spawn(NODE, nodeArgs, {
|
|
226
240
|
cwd,
|
|
227
241
|
env,
|
|
228
242
|
stdio: "ignore",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-web-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.99.0",
|
|
4
4
|
"description": "Just open your browser to get all your work done — browser cockpit for AI coding agents (chat, code, files, terminal, Git in one tab)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -79,39 +79,35 @@
|
|
|
79
79
|
"desktop:dist": "npm run build && npm run build:desktop && electron-builder -c desktop/electron-builder.yml",
|
|
80
80
|
"format": "prettier --write server/ web/src/ extensions/ scripts/ bin/ desktop/ tests/*.mjs tests/unit/ *.mjs",
|
|
81
81
|
"format:check": "prettier --check server/ web/src/ extensions/ scripts/ bin/ desktop/ tests/*.mjs tests/unit/ *.mjs",
|
|
82
|
-
"lint": "oxlint server/ web/src/ extensions/ scripts/ desktop/",
|
|
83
|
-
"lint:fix": "oxlint --fix server/ web/src/ extensions/ scripts/ desktop/"
|
|
82
|
+
"lint": "oxlint server/ web/src/ extensions/ scripts/ bin/ desktop/",
|
|
83
|
+
"lint:fix": "oxlint --fix server/ web/src/ extensions/ scripts/ bin/ desktop/"
|
|
84
84
|
},
|
|
85
85
|
"dependencies": {
|
|
86
86
|
"@deepseek-ai/dsh-sdk-jsonrpc-server": "^0.1.5-rc.3",
|
|
87
87
|
"@deepseek-ai/dsh-sdk-protocol": "^0.1.5-rc.3",
|
|
88
88
|
"@earendil-works/pi-coding-agent": ">=0.85.1",
|
|
89
|
-
"@xterm/addon-fit": "^0.11.0",
|
|
90
|
-
"@xterm/xterm": "^6.0.0",
|
|
91
89
|
"compression": "^1.8.2",
|
|
92
90
|
"electron-updater": "^6.8.9",
|
|
93
91
|
"express": "^5.2.1",
|
|
94
|
-
"highlight.js": "^11.12.0",
|
|
95
|
-
"html-to-image": "^1.11.13",
|
|
96
|
-
"katex": "^0.18.7",
|
|
97
92
|
"node-pty": "^1.1.0",
|
|
98
|
-
"react": "^19.3.0",
|
|
99
|
-
"react-dom": "^19.3.0",
|
|
100
|
-
"react-icons": "^5.7.0",
|
|
101
|
-
"react-markdown": "^10.1.0",
|
|
102
|
-
"rehype-highlight": "^7.0.2",
|
|
103
|
-
"rehype-katex": "^7.0.1",
|
|
104
|
-
"rehype-raw": "^7.0.0",
|
|
105
|
-
"rehype-sanitize": "^6.0.0",
|
|
106
|
-
"remark-breaks": "^4.0.0",
|
|
107
|
-
"remark-gfm": "^4.0.1",
|
|
108
|
-
"remark-math": "^6.0.0",
|
|
109
93
|
"tar": "^7.5.22",
|
|
110
94
|
"typebox": "^1.3.34",
|
|
111
95
|
"undici": "^8.9.0",
|
|
112
96
|
"ws": "^8.21.3",
|
|
113
97
|
"yauzl": "^3.4.0"
|
|
114
98
|
},
|
|
99
|
+
"peerDependencies": {
|
|
100
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
101
|
+
"typebox": "*"
|
|
102
|
+
},
|
|
103
|
+
"peerDependenciesMeta": {
|
|
104
|
+
"@earendil-works/pi-coding-agent": {
|
|
105
|
+
"optional": true
|
|
106
|
+
},
|
|
107
|
+
"typebox": {
|
|
108
|
+
"optional": true
|
|
109
|
+
}
|
|
110
|
+
},
|
|
115
111
|
"devDependencies": {
|
|
116
112
|
"@types/compression": "^1.8.1",
|
|
117
113
|
"@types/express": "^5.0.6",
|
|
@@ -121,16 +117,32 @@
|
|
|
121
117
|
"@types/ws": "^8.18.1",
|
|
122
118
|
"@types/yauzl": "^3.4.0",
|
|
123
119
|
"@vitejs/plugin-react": "^6.1.1",
|
|
120
|
+
"@xterm/addon-fit": "^0.11.0",
|
|
121
|
+
"@xterm/xterm": "^6.0.0",
|
|
124
122
|
"concurrently": "^10.0.5",
|
|
125
123
|
"cross-env": "^10.1.0",
|
|
126
124
|
"electron": "^44.4.2",
|
|
127
125
|
"electron-builder": "^26.15.3",
|
|
128
126
|
"esbuild": "^0.28.2",
|
|
127
|
+
"highlight.js": "^11.12.0",
|
|
128
|
+
"html-to-image": "^1.11.13",
|
|
129
129
|
"jsdom": "^30.1.0",
|
|
130
|
+
"katex": "^0.18.7",
|
|
130
131
|
"mermaid": "^12.0.0",
|
|
131
132
|
"oxlint": "^1.83.0",
|
|
132
133
|
"playwright-core": "^1.63.0",
|
|
133
134
|
"prettier": "^3.9.8",
|
|
135
|
+
"react": "^19.3.0",
|
|
136
|
+
"react-dom": "^19.3.0",
|
|
137
|
+
"react-icons": "^5.7.0",
|
|
138
|
+
"react-markdown": "^10.1.0",
|
|
139
|
+
"rehype-highlight": "^7.0.2",
|
|
140
|
+
"rehype-katex": "^7.0.1",
|
|
141
|
+
"rehype-raw": "^7.0.0",
|
|
142
|
+
"rehype-sanitize": "^6.0.0",
|
|
143
|
+
"remark-breaks": "^4.0.0",
|
|
144
|
+
"remark-gfm": "^4.0.1",
|
|
145
|
+
"remark-math": "^6.0.0",
|
|
134
146
|
"tsx": "^4.23.13",
|
|
135
147
|
"typescript": "^7.0.2",
|
|
136
148
|
"vis-timeline": "^8.5.4",
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* client-utils.mjs — pi-web-ui 插件客户端常用工具层。
|
|
3
|
+
*
|
|
4
|
+
* 供插件在 client/entry.mjs 或网页视图中复用,消除 HTML 转义与基础接口漂移面。
|
|
5
|
+
* 纯 ESM,零依赖。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 安全 HTML 实体转义。
|
|
10
|
+
* @param {unknown} s 待转义内容
|
|
11
|
+
* @returns {string} 转义后的 HTML 字符串
|
|
12
|
+
*/
|
|
13
|
+
export function esc(s) {
|
|
14
|
+
return String(s ?? "").replace(
|
|
15
|
+
/[&<>"']/g,
|
|
16
|
+
(c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c],
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 安全取用 window.__piWebUiHost 宿主桥接 API。
|
|
22
|
+
* @returns {any} 宿主 API 对象或 null
|
|
23
|
+
*/
|
|
24
|
+
export function hostApi() {
|
|
25
|
+
return typeof window !== "undefined" ? window.__piWebUiHost ?? null : null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 获取插件服务端路由的统一根路径。
|
|
30
|
+
* @param {string} pluginId 插件 id
|
|
31
|
+
* @returns {string} 路由根路径,如 "/plugins-api/my-plugin"
|
|
32
|
+
*/
|
|
33
|
+
export function apiBase(pluginId) {
|
|
34
|
+
return `/plugins-api/${pluginId}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 格式化字节大小为人类可读字符串(B / KB / MB / GB)。
|
|
39
|
+
* @param {number|unknown} bytes 字节大小
|
|
40
|
+
* @param {number} [decimals=1] 小数位数
|
|
41
|
+
* @returns {string}
|
|
42
|
+
*/
|
|
43
|
+
export function formatBytes(bytes, decimals = 1) {
|
|
44
|
+
const n = Number(bytes);
|
|
45
|
+
if (!Number.isFinite(n) || n < 0) return "";
|
|
46
|
+
if (n < 1024) return `${n} B`;
|
|
47
|
+
if (n < 1024 * 1024) return `${(n / 1024).toFixed(decimals)} KB`;
|
|
48
|
+
if (n < 1024 * 1024 * 1024) return `${(n / (1024 * 1024)).toFixed(decimals)} MB`;
|
|
49
|
+
return `${(n / (1024 * 1024 * 1024)).toFixed(decimals)} GB`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 为异步操作施加超时限制。
|
|
54
|
+
* @template T
|
|
55
|
+
* @param {Promise<T>} promise 目标 Promise
|
|
56
|
+
* @param {number} timeoutMs 超时时间(毫秒)
|
|
57
|
+
* @param {string} [errorMsg="Operation timed out"] 超时提示文案
|
|
58
|
+
* @returns {Promise<T>}
|
|
59
|
+
*/
|
|
60
|
+
export function withTimeout(promise, timeoutMs, errorMsg = "Operation timed out") {
|
|
61
|
+
return Promise.race([
|
|
62
|
+
promise,
|
|
63
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error(errorMsg)), timeoutMs)),
|
|
64
|
+
]);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 生成随机 UUID(浏览器优先使用 crypto.randomUUID,提供纯 JS 降级)。
|
|
69
|
+
* @returns {string}
|
|
70
|
+
*/
|
|
71
|
+
export function uuid() {
|
|
72
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
73
|
+
return crypto.randomUUID();
|
|
74
|
+
}
|
|
75
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
76
|
+
const r = (Math.random() * 16) | 0;
|
|
77
|
+
const v = c === "x" ? r : (r & 0x3) | 0x8;
|
|
78
|
+
return v.toString(16);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 归一化相对路径(反斜杠转斜杠、去除首尾多余斜杠与 "./")。
|
|
84
|
+
* @param {string} p
|
|
85
|
+
* @returns {string}
|
|
86
|
+
*/
|
|
87
|
+
export function normalizeRel(p) {
|
|
88
|
+
return String(p ?? "")
|
|
89
|
+
.replace(/\\/g, "/")
|
|
90
|
+
.replace(/^\.?\/+/, "")
|
|
91
|
+
.replace(/\/+$/, "");
|
|
92
|
+
}
|
package/plugin-sdk/index.d.ts
CHANGED
|
@@ -445,3 +445,24 @@ export interface MockHost extends PluginHost {
|
|
|
445
445
|
}
|
|
446
446
|
|
|
447
447
|
export declare function createMockHost(overrides?: MockHostOverrides): MockHost;
|
|
448
|
+
|
|
449
|
+
/** 安全 HTML 实体转义。 */
|
|
450
|
+
export declare function esc(s: unknown): string;
|
|
451
|
+
|
|
452
|
+
/** 安全取用 window.__piWebUiHost 宿主桥接 API。 */
|
|
453
|
+
export declare function hostApi(): any;
|
|
454
|
+
|
|
455
|
+
/** 获取插件服务端路由的统一根路径。 */
|
|
456
|
+
export declare function apiBase(pluginId: string): string;
|
|
457
|
+
|
|
458
|
+
/** 格式化字节大小为人类可读字符串(B / KB / MB / GB)。 */
|
|
459
|
+
export declare function formatBytes(bytes: number | unknown, decimals?: number): string;
|
|
460
|
+
|
|
461
|
+
/** 为异步操作施加超时限制。 */
|
|
462
|
+
export declare function withTimeout<T>(promise: Promise<T>, timeoutMs: number, errorMsg?: string): Promise<T>;
|
|
463
|
+
|
|
464
|
+
/** 生成随机 UUID。 */
|
|
465
|
+
export declare function uuid(): string;
|
|
466
|
+
|
|
467
|
+
/** 归一化相对路径(反斜杠转斜杠、去除首尾多余斜杠与 "./")。 */
|
|
468
|
+
export declare function normalizeRel(p: string): string;
|
package/plugin-sdk/index.mjs
CHANGED
package/plugins/catalog.json
CHANGED
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
{
|
|
63
63
|
"id": "wechat-ilink",
|
|
64
64
|
"name": "微信通道",
|
|
65
|
+
"nameEn": "WeChat Channel",
|
|
65
66
|
"icon": "💬",
|
|
66
67
|
"iconSvg": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z\"/></svg>",
|
|
67
68
|
"description": "微信扫码登录(与腾讯 openclaw-weixin 同源的 ilink 协议):出站长轮询收消息,微信里直接指挥 agent,无需公网 IP。",
|
|
@@ -72,6 +73,7 @@
|
|
|
72
73
|
{
|
|
73
74
|
"id": "voice-input",
|
|
74
75
|
"name": "语音输入",
|
|
76
|
+
"nameEn": "Voice Input",
|
|
75
77
|
"icon": "🎤",
|
|
76
78
|
"iconSvg": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z\"/><path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/><line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"23\"/><line x1=\"8\" y1=\"23\" x2=\"16\" y2=\"23\"/></svg>",
|
|
77
79
|
"description": "语音输入:输入框旁的麦克风按钮,浏览器语音识别直接听写进输入框;不支持/识别失败时自动降级为服务端转写(远端接口,或一键安装的本地 Whisper,免费不出网)。",
|
|
@@ -102,6 +104,7 @@
|
|
|
102
104
|
{
|
|
103
105
|
"id": "notes",
|
|
104
106
|
"name": "笔记",
|
|
107
|
+
"nameEn": "Notes",
|
|
105
108
|
"icon": "📓",
|
|
106
109
|
"iconSvg": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1024 1024\"><path d=\"M746.666667 64a170.666667 170.666667 0 0 1 170.666666 170.666667v554.666666a170.666667 170.666667 0 0 1-170.666666 170.666667H298.666667a170.666667 170.666667 0 0 1-170.666667-170.666667V234.666667a170.666667 170.666667 0 0 1 170.666667-170.666667h448z m0 74.666667H298.666667a96 96 0 0 0-95.893334 91.477333L202.666667 234.666667v554.666666a96 96 0 0 0 91.477333 95.893334L298.666667 885.333333h448a96 96 0 0 0 95.893333-91.477333L842.666667 789.333333V234.666667a96 96 0 0 0-91.477334-95.893334L746.666667 138.666667z\" fill=\"currentColor\"/><path d=\"M224 325.333333a37.333333 37.333333 0 0 1 3.072 74.538667l-3.072 0.128H106.666667a37.333333 37.333333 0 0 1-3.072-74.538667L106.666667 325.333333h117.333333zM224 624a37.333333 37.333333 0 0 1 3.072 74.538667l-3.072 0.128H106.666667a37.333333 37.333333 0 0 1-3.072-74.538667L106.666667 624h117.333333zM693.333333 346.666667a37.333333 37.333333 0 0 1 37.205334 34.261333l0.128 3.072v256a37.333333 37.333333 0 0 1-74.538667 3.072L656 640V384c0-20.608 16.725333-37.333333 37.333333-37.333333z\" fill=\"currentColor\"/></svg>",
|
|
107
110
|
"description": "随手记:笔记 + 待办 + 日程提醒三合一。顶栏按钮打开可自由拖拽的全局浮窗(位置/尺寸记忆,设置也在浮窗里,无独立视图页);提醒走服务端定时(重启不丢、错过补送),另有 5 个 AI 工具与 /note /todo /remind 快速捕获。",
|
|
@@ -112,6 +115,7 @@
|
|
|
112
115
|
{
|
|
113
116
|
"id": "desktop-use",
|
|
114
117
|
"name": "桌面控制",
|
|
118
|
+
"nameEn": "Desktop Control",
|
|
115
119
|
"icon": "🖱️",
|
|
116
120
|
"iconSvg": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"5\" y=\"2\" width=\"14\" height=\"20\" rx=\"7\"/><line x1=\"12\" y1=\"6\" x2=\"12\" y2=\"10\"/></svg>",
|
|
117
121
|
"description": "让 AI 操作 Windows 桌面:按元素名查控件坐标(UI Automation,无需截图)→ 点击 / 输入(含中文粘贴)/ 按键 / 聚焦窗口。输入库缺失时自动安装,卸载时连带删除。",
|
|
@@ -132,6 +136,7 @@
|
|
|
132
136
|
{
|
|
133
137
|
"id": "reconnect",
|
|
134
138
|
"name": "重连",
|
|
139
|
+
"nameEn": "Reconnect",
|
|
135
140
|
"icon": "🔄",
|
|
136
141
|
"iconSvg": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8\"/><path d=\"M21 3v5h-5\"/><path d=\"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16\"/><path d=\"M3 21v-5h5\"/></svg>",
|
|
137
142
|
"description": "断连恢复:查看网页服务与重启守护状态,一键重新连接或重启网页服务(支持单服务/全服务重启,配套可选独立守护进程)。",
|
|
@@ -142,6 +147,7 @@
|
|
|
142
147
|
{
|
|
143
148
|
"id": "sol-savings",
|
|
144
149
|
"name": "SoL-Pi 状态",
|
|
150
|
+
"nameEn": "SoL-Pi Status",
|
|
145
151
|
"icon": "⚡",
|
|
146
152
|
"iconSvg": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/></svg>",
|
|
147
153
|
"description": "SoL-Pi 会话节省统计与规划底栏:实时追踪大工具输出打包(Observation Pack)节省的上下文 Token 与在线计划(Plan)进度,借鉴 pi-sol-plan-footer 在底栏显示。",
|
|
@@ -152,6 +158,7 @@
|
|
|
152
158
|
{
|
|
153
159
|
"id": "session-migrate",
|
|
154
160
|
"name": "会话迁移",
|
|
161
|
+
"nameEn": "Session Migration",
|
|
155
162
|
"icon": "🔄",
|
|
156
163
|
"iconSvg": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8\"/><path d=\"M21 3v5h-5\"/><path d=\"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16\"/><path d=\"M8 16H3v5\"/></svg>",
|
|
157
164
|
"description": "扫描 ~/.omp/agent/sessions 与 ~/.pi/agent/sessions 的历史会话,只读预览后一键导入 pi 会话库(issue 286 Phase 1)。",
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-hePW80VL.js";import{i as t,t as n}from"./react-CR5VJ85Q.js";import{a as r,c as i,d as a,f as o,g as s,h as c,i as l,l as u,m as d,o as f,p,r as m,s as h,t as g,u as _}from"./index-
|
|
1
|
+
import{r as e}from"./rolldown-runtime-hePW80VL.js";import{i as t,t as n}from"./react-CR5VJ85Q.js";import{a as r,c as i,d as a,f as o,g as s,h as c,i as l,l as u,m as d,o as f,p,r as m,s as h,t as g,u as _}from"./index-ByCBN4Kt.js";import{n as v,t as y}from"./xterm-Cf5xxe4g.js";var b=e(t(),1);function x(e,t){return Math.abs(t)>10&&Math.abs(t)>Math.abs(e)*1.2}function S(e,t){return e-t}var C=n();function w(e){let t=null;return{clean:e.replace(/\r?\n?\[pi-term-exit:(-?\d+)\]\r?\n?/g,(e,n)=>(t=Number(n),`\r
|
|
2
2
|
`)).replace(/\r?\n?\x1b\[90m\[(?:进程已退出,退出码 |Process exited with code )-?\d+\]\x1b\[0m\r?\n?/g,`\r
|
|
3
3
|
`),exitCode:t}}function T({conversationId:e,terminalId:t,command:n,cwd:i,title:a,agentBash:o,active:s,running:c,exitCode:u,register:d}){let p=(0,b.useRef)(null),h=(0,b.useRef)(null),{locale:g}=f(),_=(0,b.useRef)(g);_.current=g;let T=n?JSON.stringify(n):``,E=(0,b.useRef)(c===!1);(0,b.useEffect)(()=>{let c=p.current;if(!c)return;let u=new v({theme:l(),fontFamily:`"SF Mono", "JetBrains Mono", ui-monospace, Menlo, Consolas, monospace`,fontSize:13,cursorBlink:!0,scrollback:8e3}),f=new y;u.loadAddon(f),u.open(c),h.current={term:u,fit:f},s&&u.focus();let g=()=>{u.options.theme=l()};window.addEventListener(m,g),u.attachCustomKeyEventHandler(e=>{if(e.type!==`keydown`)return!0;let t=e.key?.toLowerCase();if((e.ctrlKey||e.metaKey)&&t===`v`)return!1;if(e.ctrlKey&&!e.shiftKey&&!e.altKey&&t===`c`&&u.hasSelection()){let e=u.textarea;return e&&(e.value=u.getSelection(),e.select()),!1}return!0});let b=d(e,t,{write:e=>u.write(w(e).clean),dispose:()=>u.dispose()}),C=()=>{try{f.fit(),r({type:`terminal_resize`,terminalId:t,conversationId:e,cols:u.cols,rows:u.rows})}catch{}},T=requestAnimationFrame(()=>{try{f.fit()}catch{}E.current||r(n?{type:`run_command`,terminalId:t,conversationId:e,command:n,cols:u.cols,rows:u.rows}:{type:`terminal_create`,terminalId:t,title:a,locale:_.current,agentBash:o,conversationId:e,cwd:i,cols:u.cols,rows:u.rows})}),D=u.onData(n=>{r({type:`terminal_input`,terminalId:t,conversationId:e,data:n})}),O=u.element,k=null,A=0,j=0,M=0,N=!1,P=e=>{if(e.touches.length!==1){k=null,N=!1;return}let t=e.touches[0];k=t.identifier,A=t.clientX,j=t.clientY,M=t.clientY,N=!1},F=e=>{if(k===null||e.touches.length!==1)return;let t=e.touches[0];if(t.identifier!==k)return;if(!N){if(!x(t.clientX-A,t.clientY-j))return;N=!0}e.preventDefault();let n=S(M,t.clientY);M=t.clientY,n!==0&&typeof WheelEvent<`u`&&(c.querySelector(`.xterm-viewport`)??O)?.dispatchEvent(new WheelEvent(`wheel`,{bubbles:!0,cancelable:!0,deltaY:n,deltaMode:0,clientX:t.clientX,clientY:t.clientY}))},I=e=>{if(k!==null){if(e.touches.length>0){k=null,N=!1;return}k=null,N=!1}};O?.addEventListener(`touchstart`,P,{passive:!0}),O?.addEventListener(`touchmove`,F,{passive:!1}),O?.addEventListener(`touchend`,I),O?.addEventListener(`touchcancel`,I);let L=null;return typeof ResizeObserver<`u`&&(L=new ResizeObserver(()=>{c.offsetWidth>0&&c.offsetHeight>0&&C()}),L.observe(c)),()=>{cancelAnimationFrame(T),D.dispose(),O?.removeEventListener(`touchstart`,P),O?.removeEventListener(`touchmove`,F),O?.removeEventListener(`touchend`,I),O?.removeEventListener(`touchcancel`,I),window.removeEventListener(m,g),L?.disconnect(),b(),u.dispose(),h.current=null}},[e,t,T,d]),(0,b.useEffect)(()=>{if(!s)return;let n=requestAnimationFrame(()=>{let n=h.current;if(n){try{n.fit.fit(),r({type:`terminal_resize`,terminalId:t,conversationId:e,cols:n.term.cols,rows:n.term.rows})}catch{}n.term.focus()}});return()=>cancelAnimationFrame(n)},[s]);let{t:D}=f(),O=(0,b.useRef)(void 0);return(0,b.useEffect)(()=>{if(c===O.current||(O.current=c,c!==!1))return;let e=h.current;e&&e.term.write(`\r\n\x1b[90m${D(`exitBanner`,{code:u??``})}\x1b[0m\r\n`)},[c,t,u]),(0,C.jsx)(`div`,{ref:p,className:`term-xterm ${s?``:`hidden`}`})}var E={name:``,command:``,cwd:"${pwd}"};function D({chat:e,terminal:t,uiTerminalToolbar:n,onUiAction:l}){let f=h(),[m,v]=(0,b.useState)(null),[y,x]=(0,b.useState)(!1),[S,w]=(0,b.useState)(!1),[D,O]=(0,b.useState)(null),[k,A]=(0,b.useState)(E),[j,M]=(0,b.useState)(null),N=(0,b.useRef)(null),[P,F]=(0,b.useState)(!0),[I,L]=(0,b.useState)(null),[R,z]=(0,b.useState)(``);(0,b.useEffect)(()=>{e.terminals.length===0?v(null):e.terminals.some(e=>e.id===m)||v(e.terminals[e.terminals.length-1].id)},[e.terminals,m]),(0,b.useEffect)(()=>{e.terminalActiveId&&(v(e.terminalActiveId),x(!1))},[e.terminalActiveId]),(0,b.useEffect)(()=>()=>{N.current&&clearTimeout(N.current)},[]);let B=e.terminals.filter(e=>!e.agentBash),V=e.terminals.filter(e=>e.agentBash),H=n=>{if(!e.ready)return;let r=i(),a=e.activeConversationId||e.state?.conversationId||``;t.create({...n,id:r,conversationId:a,cols:n.cols??80,rows:n.rows??24,running:!0,exitCode:null}),v(r),x(!1)},U=()=>H({title:f(`terminalTitle`,{n:B.length+1}),cwd:e.state?.cwd??``}),W=n=>{let i=n.name||n.command,a=e.terminals.find(e=>e.title===i);if(a){t.restart(a.id),v(a.id),r({type:`run_command`,terminalId:a.id,conversationId:a.conversationId,command:n,cols:80,rows:24});return}H({title:i,cwd:e.state?.cwd??``,command:n})},G=n=>{let i=e.terminals.find(e=>e.id===n);if(i&&r({type:`terminal_kill`,terminalId:n,conversationId:i.conversationId}),t.close(n),m===n){let t=e.terminals.filter(e=>e.id!==n);v(t.length>0?t[t.length-1].id:null)}},K=e=>(0,C.jsxs)(`div`,{className:`term-tab ${e.id===m?`active`:``}`,children:[(0,C.jsxs)(`button`,{type:`button`,className:`term-tab-main`,title:`${e.cwd}${e.command?`\n> ${e.command.command}`:``}`,onClick:()=>{I||(v(e.id),x(!1))},children:[(0,C.jsx)(`span`,{className:`term-tab-dot ${e.running?`run`:`exit`}`}),(0,C.jsxs)(`span`,{className:`term-tab-title`,children:[I===e.id?(0,C.jsx)(`input`,{autoFocus:!0,className:`term-tab-rename-input`,value:R,placeholder:e.title,onClick:e=>e.stopPropagation(),onChange:e=>z(e.target.value),onKeyDown:t=>{if(t.stopPropagation(),t.key===`Enter`&&!t.nativeEvent.isComposing){let t=R.trim();t&&r({type:`rename_terminal`,terminalId:e.id,conversationId:e.conversationId,title:t}),L(null)}else t.key===`Escape`&&L(null)},onBlur:()=>L(null)}):e.title,!e.running&&(0,C.jsx)(`span`,{className:`term-tab-exit`,children:f(`exited`,{code:e.exitCode===null?``:` ${e.exitCode}`})})]})]}),(0,C.jsx)(`button`,{type:`button`,className:`term-tab-close term-tab-rename`,title:f(`renameTerminal`),onClick:t=>{t.stopPropagation(),z(e.title),L(e.id)},children:(0,C.jsx)(u,{})}),(0,C.jsx)(`button`,{type:`button`,className:`term-tab-close`,title:f(`closeTerminal`),onClick:()=>G(e.id),children:(0,C.jsx)(s,{})})]},e.id),q=()=>{w(!0),O(null),A(E)},J=t=>{let n=e.commands[t];n&&(w(!1),O(t),A({name:n.name,command:n.command,cwd:n.cwd??``}))},Y=()=>{w(!1),O(null)},X=()=>{let t=k.name.trim(),n=k.command.trim();if(!t||!n)return;let i={name:t,command:n,cwd:k.cwd.trim()||void 0},a=S?[...e.commands,i]:D===null?e.commands:e.commands.map((e,t)=>t===D?i:e);r({type:`save_commands`,commands:a}),Y()},Z=t=>{if(j===t){let n=e.commands.filter((e,n)=>n!==t);r({type:`save_commands`,commands:n}),M(null),N.current&&clearTimeout(N.current)}else M(t),N.current&&clearTimeout(N.current),N.current=setTimeout(()=>M(null),2500)},ee=S||D!==null,te=n===void 0?[`host:term-cmd-refresh`,`host:term-cmd-new`,`host:term-tab-new`].map(e=>({id:e,source:`host`})):n.filter(e=>!e.hidden),Q=(e,t)=>{let n=new Set(e);return te.filter(e=>n.has(e.id)||t&&e.source!==`host`)},$={"host:term-cmd-refresh":(0,C.jsx)(`button`,{type:`button`,className:`panel-refresh`,title:f(`rerun`),onClick:()=>r({type:`list_commands`}),children:(0,C.jsx)(p,{})}),"host:term-cmd-new":(0,C.jsx)(`button`,{type:`button`,className:`panel-new`,title:f(`newCommand`),onClick:q,children:(0,C.jsx)(o,{})}),"host:term-tab-new":(0,C.jsx)(`button`,{type:`button`,className:`panel-new`,title:f(`newTerminal`),onClick:U,children:(0,C.jsx)(o,{})})};return(0,C.jsxs)(`div`,{className:`terminal-view`,children:[(0,C.jsxs)(`aside`,{className:`term-side term-commands ${y?`open`:``}`,children:[(0,C.jsxs)(`div`,{className:`panel-header`,children:[(0,C.jsx)(`span`,{className:`panel-title`,children:f(`commands`)}),(0,C.jsx)(`div`,{className:`panel-header-actions`,children:g(Q([`host:term-cmd-refresh`,`host:term-cmd-new`],!1),$,l)})]}),(0,C.jsx)(`div`,{className:`panel-body`,children:ee?(0,C.jsxs)(`div`,{className:`cmd-form`,children:[(0,C.jsx)(`label`,{htmlFor:`cmd-name`,children:f(`name`)}),(0,C.jsx)(`input`,{id:`cmd-name`,className:`cmd-input`,value:k.name,placeholder:f(`exampleName`),autoFocus:!0,onChange:e=>A({...k,name:e.target.value})}),(0,C.jsx)(`label`,{htmlFor:`cmd-command`,children:f(`command`)}),(0,C.jsx)(`input`,{id:`cmd-command`,className:`cmd-input`,value:k.command,placeholder:f(`exampleCommand`),onChange:e=>A({...k,command:e.target.value}),onKeyDown:e=>{e.key===`Enter`&&!e.nativeEvent.isComposing&&X()}}),(0,C.jsxs)(`label`,{htmlFor:`cmd-cwd`,children:[f(`directory`),` `,(0,C.jsx)(`span`,{className:`cmd-hint`,children:f(`cwdHint`)})]}),(0,C.jsx)(`input`,{id:`cmd-cwd`,className:`cmd-input`,value:k.cwd,placeholder:"${pwd}",onChange:e=>A({...k,cwd:e.target.value}),onKeyDown:e=>{e.key===`Enter`&&!e.nativeEvent.isComposing&&X()}}),(0,C.jsxs)(`div`,{className:`cmd-form-actions`,children:[(0,C.jsx)(`button`,{type:`button`,className:`btn`,onClick:Y,children:f(`cancel`)}),(0,C.jsx)(`button`,{type:`button`,className:`btn primary`,disabled:!k.name.trim()||!k.command.trim(),onClick:X,children:f(`save`)})]})]}):(0,C.jsxs)(C.Fragment,{children:[e.commands.length===0&&(0,C.jsx)(`div`,{className:`panel-empty`,children:f(`noCommands`)}),e.commands.map((e,t)=>(0,C.jsxs)(`div`,{className:`cmd-item`,children:[(0,C.jsx)(`button`,{type:`button`,className:`cmd-run`,title:f(`clickToRun`),onClick:()=>W(e),children:(0,C.jsx)(a,{})}),(0,C.jsxs)(`button`,{type:`button`,className:`cmd-main`,title:f(`clickToRun`),onClick:()=>W(e),children:[(0,C.jsx)(`span`,{className:`cmd-name`,children:e.name}),(0,C.jsx)(`span`,{className:`cmd-command`,children:e.command}),e.cwd&&(0,C.jsx)(`span`,{className:`cmd-cwd`,children:e.cwd})]}),(0,C.jsx)(`button`,{type:`button`,className:`cmd-act`,title:f(`edit`),onClick:()=>J(t),children:(0,C.jsx)(u,{})}),(0,C.jsx)(`button`,{type:`button`,className:`cmd-act del ${j===t?`confirm`:``}`,title:f(`delete`),onClick:()=>Z(t),children:j===t?f(`confirmQ`):(0,C.jsx)(c,{})})]},t))]})}),(0,C.jsxs)(`div`,{className:`term-tabs-block`,children:[(0,C.jsxs)(`div`,{className:`panel-header`,children:[(0,C.jsx)(`span`,{className:`panel-title`,children:f(`terminal`)}),g(Q([`host:term-tab-new`],!0),$,l)]}),(0,C.jsxs)(`div`,{className:`panel-body`,children:[e.terminals.length===0&&(0,C.jsx)(`div`,{className:`panel-empty`,children:f(`noTerminal`)}),B.map(K),V.length>0&&(0,C.jsxs)(`div`,{className:`term-folder`,children:[(0,C.jsxs)(`button`,{type:`button`,className:`term-folder-header ${P?`open`:``}`,title:f(`aiBashGroup`),onClick:()=>F(e=>!e),children:[(0,C.jsx)(`span`,{className:`term-folder-caret`,children:P?`▾`:`▸`}),(0,C.jsx)(`span`,{className:`term-folder-title`,children:f(`aiBashGroup`)}),(0,C.jsx)(`span`,{className:`term-folder-count`,children:V.length})]}),P&&(0,C.jsx)(`div`,{className:`term-folder-body`,children:V.map(K)})]})]})]})]}),(0,C.jsxs)(`div`,{className:`term-main`,children:[y&&(0,C.jsx)(`div`,{className:`drawer-backdrop`,onClick:()=>x(!1)}),(0,C.jsx)(`button`,{type:`button`,className:`term-side-toggle`,title:f(`commands`),onClick:()=>x(e=>!e),children:(0,C.jsx)(_,{})}),e.terminals.length===0?(0,C.jsxs)(`div`,{className:`term-empty`,children:[(0,C.jsx)(d,{className:`term-empty-icon`}),(0,C.jsx)(`div`,{className:`term-empty-title`,children:f(`builtinTerminal`)}),(0,C.jsx)(`div`,{className:`term-empty-sub`,children:f(`termEmptySub`)})]}):e.terminals.map(e=>(0,C.jsx)(T,{conversationId:e.conversationId,terminalId:e.id,command:e.command,cwd:e.cwd,title:e.title,agentBash:e.agentBash,active:e.id===m,running:e.running,exitCode:e.exitCode,register:t.register},`${e.conversationId}:${e.id}`))]})]})}export{D as TerminalPanel};
|