pi-web-ui 0.90.1 → 0.91.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 +48 -1
- package/dist/server/agent-service.js +763 -61
- package/dist/server/client-state.js +70 -5
- package/dist/server/compaction-markers.js +287 -0
- package/dist/server/delegate-task.js +12 -1
- package/dist/server/dsh/dsh-agent-service.js +21 -1
- package/dist/server/file-transfer-routes.js +4 -1
- package/dist/server/icon-svg.js +35 -0
- package/dist/server/index.js +120 -30
- package/dist/server/plugin-catalog.js +5 -0
- package/dist/server/plugin-facilities.js +42 -11
- package/dist/server/plugins.js +47 -8
- package/dist/server/prompt-composer.js +1 -1
- package/dist/server/protocol-version.js +1 -1
- package/dist/server/schedule-agent-tool.js +23 -5
- package/dist/server/scheduler-tasks.js +60 -1
- package/dist/server/scm-commitmsg.js +140 -0
- package/dist/server/scm.js +42 -0
- package/dist/server/settings-service.js +40 -0
- package/dist/server/skill-tool.js +120 -0
- package/dist/server/soft-cap.js +68 -0
- package/dist/server/subagent-templates.js +6 -0
- package/dist/server/subagents.js +157 -16
- package/dist/server/tool-manager.js +7 -1
- package/package.json +2 -1
- package/plugins/catalog.json +10 -0
- package/web/dist/assets/{TerminalPanel-CkxM4Byl.js → TerminalPanel-GtDQ1h85.js} +1 -1
- package/web/dist/assets/es-DIDZppIF.js +2 -0
- package/web/dist/assets/index-CdsKltOS.css +1 -0
- package/web/dist/assets/index-OoULNF70.js +363 -0
- package/web/dist/index.html +2 -2
- package/web/dist/assets/index-DJIV4A-t.js +0 -355
- package/web/dist/assets/index-DLdcRBs_.css +0 -1
package/dist/server/subagents.js
CHANGED
|
@@ -130,7 +130,18 @@ export function makeSubagentTools(host, lang, selfConvId) {
|
|
|
130
130
|
if (p.template && !host.isTemplateUsable(p.template)) {
|
|
131
131
|
return text(pick(getLang(), `子代理模板不可用:${p.template}(不存在或已停用)。用 subagent_templates 查看当前可用模板清单;不传 template 则按默认配置运行。`, `Subagent template unavailable: ${p.template} (missing or disabled). Use subagent_templates to list available templates; omit template to run with defaults.`, "subagents.spawn.template.unavailable", { "p.template": p.template }));
|
|
132
132
|
}
|
|
133
|
-
|
|
133
|
+
// spawn 通道是唯一的失败面(数量上限 / runtime 创建失败 / 坏 cwd 都经由
|
|
134
|
+
// host 抛错):转成返回文本而不是直接抛,让 AI 能读到原因并调整重试。
|
|
135
|
+
let convId;
|
|
136
|
+
try {
|
|
137
|
+
convId = await host.spawnSubagent(p.prompt, p.type ?? "general", p.cwd ?? ctx.cwd, p.template, p.model);
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
141
|
+
return text(pick(getLang(), `子代理启动失败:${msg}`, `Failed to start subagent: ${msg}`, "subagents.spawn.failed", {
|
|
142
|
+
error: msg,
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
134
145
|
const subagentType = p.type ?? "general";
|
|
135
146
|
const subagentTitleText = subagentTitle(p.prompt);
|
|
136
147
|
// Optional segments are pre-rendered per language (translators pick
|
|
@@ -197,6 +208,10 @@ export function makeSubagentTools(host, lang, selfConvId) {
|
|
|
197
208
|
}),
|
|
198
209
|
}),
|
|
199
210
|
execute: async (_id, p) => {
|
|
211
|
+
if (!host.getSubagent(p.runId)) {
|
|
212
|
+
const missingSteerId = shortId(p.runId);
|
|
213
|
+
return text(pick(getLang(), `未找到子代理 ${missingSteerId}(可能已移出),消息未注入。请用 subagent_list 确认仍在运行的子代理。`, `Subagent ${missingSteerId} not found (may have been dismissed); message not injected. Use subagent_list to confirm running subagents.`, "subagents.steer.not.found", { missingSteerId: missingSteerId }));
|
|
214
|
+
}
|
|
200
215
|
await host.steerSubagent(p.runId, p.message);
|
|
201
216
|
const steerId = shortId(p.runId);
|
|
202
217
|
return text(pick(getLang(), `已向子代理 ${steerId} 注入消息。`, `Message injected into subagent ${steerId}.`, "subagents.steer.injected", { steerId: steerId }));
|
|
@@ -229,6 +244,10 @@ export function makeSubagentTools(host, lang, selfConvId) {
|
|
|
229
244
|
}),
|
|
230
245
|
}),
|
|
231
246
|
execute: async (_id, p) => {
|
|
247
|
+
if (!host.getSubagent(p.runId)) {
|
|
248
|
+
const missingStopId = shortId(p.runId);
|
|
249
|
+
return text(pick(getLang(), `未找到子代理 ${missingStopId}(可能已移出或已结束),无需停止。请用 subagent_list 确认仍在运行的子代理。`, `Subagent ${missingStopId} not found (may have been dismissed or already finished); nothing to stop. Use subagent_list to confirm running subagents.`, "subagents.stop.not.found", { missingStopId: missingStopId }));
|
|
250
|
+
}
|
|
232
251
|
await host.stopSubagent(p.runId);
|
|
233
252
|
const stopId = shortId(p.runId);
|
|
234
253
|
return text(pick(getLang(), `已请求停止子代理 ${stopId}。`, `Stop requested for subagent ${stopId}.`, "subagents.stop.requested", { stopId: stopId }));
|
|
@@ -239,12 +258,15 @@ export function makeSubagentTools(host, lang, selfConvId) {
|
|
|
239
258
|
label: "Wait for subagents",
|
|
240
259
|
description: bilingual("Wait for multiple subagents to finish at once (blocks this round until all reach a terminal state or time out), " +
|
|
241
260
|
"then summarize each result/error — no need to poll subagent_get_result. Pass runIds for specific subagents " +
|
|
242
|
-
"(convIds returned by subagent_spawn); omit = wait for
|
|
243
|
-
"
|
|
261
|
+
"(convIds returned by subagent_spawn); omit = wait for own descendant subagents when called from a subagent, " +
|
|
262
|
+
"else all currently running ones. The calling session itself and its ancestors are never waited on " +
|
|
263
|
+
"(a subagent calling this without runIds won't deadlock on itself or its parent). " +
|
|
264
|
+
"Descendants spawned during the wait are picked up automatically. " +
|
|
244
265
|
"On timeout or abort of this round, returns the remaining unfinished list; call again to continue waiting. " +
|
|
245
266
|
"Good for: collecting parallel subagents.", "一次性等待多个子代理全部完成(阻塞本回合直到它们都到达终态或超时),然后汇总返回每个的结果/错误——" +
|
|
246
267
|
"不用反复调 subagent_get_result 轮询。传 runIds 指定要等的子代理(subagent_spawn 返回的 convId);" +
|
|
247
|
-
"不传 =
|
|
268
|
+
"不传 = 子代理调用时只等自己的后代,主对话调用时等当前全部运行中的子代理。调用者自身与祖先永不计入等待" +
|
|
269
|
+
"(子代理不传 runIds 时不会等自己或父级,避免父子互等到超时)。等待期间新派生的后代会自动纳入。" +
|
|
248
270
|
"超时或本轮被中止时返回剩余未完成名单,可再次调用继续等。" +
|
|
249
271
|
"适合:并行派发多个子代理后收口。"),
|
|
250
272
|
promptSnippet: "wait for multiple subagents to finish (no polling) and get all results",
|
|
@@ -259,25 +281,129 @@ export function makeSubagentTools(host, lang, selfConvId) {
|
|
|
259
281
|
})),
|
|
260
282
|
}),
|
|
261
283
|
execute: async (_id, p, signal) => {
|
|
262
|
-
|
|
263
|
-
//
|
|
264
|
-
//
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
284
|
+
// 祖先链:调用者往上经 parentId 能走到的子代理集合。父级正在执行中的
|
|
285
|
+
// wait_all 卡着(streaming=true),等它 = 父子互等、必到超时才返回。
|
|
286
|
+
// parentId 可能指向普通主对话(不在子代理列表里),走到空即停;环按 visited 截断。
|
|
287
|
+
const ancestorIdsOf = (selfId) => {
|
|
288
|
+
const out = new Set();
|
|
289
|
+
const seen = new Set([selfId]);
|
|
290
|
+
let cur = host.getSubagent(selfId)?.parentId;
|
|
291
|
+
while (cur && !seen.has(cur)) {
|
|
292
|
+
seen.add(cur);
|
|
293
|
+
// 只有子代理才可能被圈进等待集;普通主对话记下来也无妨(wanted 里没有它)。
|
|
294
|
+
out.add(cur);
|
|
295
|
+
cur = host.getSubagent(cur)?.parentId;
|
|
296
|
+
}
|
|
297
|
+
return out;
|
|
298
|
+
};
|
|
299
|
+
// 后代展开:roots 里任一 id 经 parentId 链能向上走到的子代理。等待集
|
|
300
|
+
// 传了显式 runIds 时也要顺带等它们的后代(fire-and-forget 的孙子辈否则会漏)。
|
|
301
|
+
const expandDescendants = (roots) => {
|
|
302
|
+
if (roots instanceof Set ? roots.size === 0 : roots.length === 0)
|
|
303
|
+
return [];
|
|
304
|
+
const rootSet = roots instanceof Set ? roots : new Set(roots);
|
|
305
|
+
const all = host.listSubagents();
|
|
306
|
+
const byId = new Map(all.map((s) => [s.convId, s]));
|
|
307
|
+
const out = [];
|
|
308
|
+
for (const s of all) {
|
|
309
|
+
if (rootSet.has(s.convId))
|
|
310
|
+
continue;
|
|
311
|
+
let curParent = s.parentId;
|
|
312
|
+
const seen = new Set();
|
|
313
|
+
while (curParent) {
|
|
314
|
+
if (rootSet.has(curParent)) {
|
|
315
|
+
out.push(s.convId);
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
if (seen.has(curParent))
|
|
319
|
+
break;
|
|
320
|
+
seen.add(curParent);
|
|
321
|
+
curParent = byId.get(curParent)?.parentId;
|
|
322
|
+
// parentId 指向普通主对话(不在 byId 里):链到此为止,
|
|
323
|
+
// 但 root 本身可能就是那个主对话 id(主调 wait_all 的 selfConvId)。
|
|
324
|
+
if (!curParent)
|
|
325
|
+
break;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return out;
|
|
329
|
+
};
|
|
330
|
+
const explicit = p.runIds && p.runIds.length > 0;
|
|
331
|
+
const wanted = new Set();
|
|
332
|
+
const roots = new Set();
|
|
333
|
+
let implicitGlobal = false;
|
|
334
|
+
// 调用者自身 + 祖先永不等待:子代理调本工具时它自己正在 streaming,
|
|
335
|
+
// 父级同样 streaming(卡在它自己的 wait 里),等谁都是互等死锁到超时。
|
|
336
|
+
// 先算好排除集,显式 runIds 里的祖先直接剔除(不再展开它的子树,
|
|
337
|
+
// 否则等父会顺带把整棵子树含兄弟分支都圈进来)。
|
|
338
|
+
const excluded = new Set();
|
|
339
|
+
if (selfConvId) {
|
|
340
|
+
excluded.add(selfConvId);
|
|
341
|
+
for (const a of ancestorIdsOf(selfConvId))
|
|
342
|
+
excluded.add(a);
|
|
343
|
+
}
|
|
344
|
+
if (explicit) {
|
|
345
|
+
for (const id of p.runIds) {
|
|
346
|
+
if (!excluded.has(id))
|
|
347
|
+
roots.add(id);
|
|
348
|
+
}
|
|
349
|
+
for (const id of roots)
|
|
350
|
+
wanted.add(id);
|
|
351
|
+
for (const id of expandDescendants(roots)) {
|
|
352
|
+
if (!excluded.has(id))
|
|
353
|
+
wanted.add(id);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
else if (selfConvId && host.getSubagent(selfConvId)) {
|
|
357
|
+
// 子代理无参:只等自己的后代(不含自己)。全局等会把父级/无关兄弟
|
|
358
|
+
// 也圈进来:父级卡在 wait 里 streaming=true,子等父 = 父子互等到超时。
|
|
359
|
+
roots.add(selfConvId);
|
|
360
|
+
for (const id of expandDescendants(roots)) {
|
|
361
|
+
if (!excluded.has(id))
|
|
362
|
+
wanted.add(id);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
// 主对话无参:等当前全部(保持老语义),但同样动态追后代。
|
|
367
|
+
implicitGlobal = true;
|
|
368
|
+
for (const r of host.listSubagents()) {
|
|
369
|
+
if (!excluded.has(r.convId))
|
|
370
|
+
wanted.add(r.convId);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
268
373
|
if (wanted.size === 0) {
|
|
269
374
|
const emptyLang = getLang();
|
|
270
|
-
return text(pick(emptyLang, "
|
|
375
|
+
return text(pick(emptyLang, "没有需要等待的子代理(调用者自身与祖先不计入等待;子代理无参时只等自己的后代)。", "No subagents to wait for (the calling session itself and its ancestors are never waited on; a subagent without runIds only waits for its own descendants).", "subagents.wait.empty"));
|
|
271
376
|
}
|
|
272
377
|
const timeoutMs = Math.min(Math.max(p.timeoutSeconds ?? 600, 1), Math.floor(WAIT_CAP_MS / 1000)) * 1000;
|
|
273
378
|
const waitStart = Date.now();
|
|
274
379
|
const deadline = waitStart + timeoutMs;
|
|
275
380
|
// 已到终态的、(或已被移出找不到的)直接归位;剩下的阻塞轮询到
|
|
276
381
|
// 全部完成/超时/中止(移出 = 无法再等,立即按收口处理)。
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
382
|
+
// 每轮动态追踪:等待期间新派生的后代(roots 的子孙、或主调全局新增)自动纳入,
|
|
383
|
+
// fire-and-forget 的孙子辈不会漏;自身与祖先永远排除(父子互等死锁)。
|
|
384
|
+
const trackNewArrivals = () => {
|
|
385
|
+
if (implicitGlobal) {
|
|
386
|
+
for (const r of host.listSubagents()) {
|
|
387
|
+
if (excluded.has(r.convId))
|
|
388
|
+
continue;
|
|
389
|
+
wanted.add(r.convId);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
for (const id of expandDescendants(roots)) {
|
|
394
|
+
if (excluded.has(id))
|
|
395
|
+
continue;
|
|
396
|
+
wanted.add(id);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
const pending = () => {
|
|
401
|
+
trackNewArrivals();
|
|
402
|
+
return [...wanted].filter((id) => {
|
|
403
|
+
const r = host.getSubagent(id);
|
|
404
|
+
return r !== undefined && !isSubagentTerminal(r);
|
|
405
|
+
});
|
|
406
|
+
};
|
|
281
407
|
while (pending().length > 0 && Date.now() < deadline && !(signal?.aborted ?? false)) {
|
|
282
408
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
283
409
|
}
|
|
@@ -295,7 +421,7 @@ export function makeSubagentTools(host, lang, selfConvId) {
|
|
|
295
421
|
? `- ${shortId(r.convId)}(${r.type})· ${r.title} · ${subagentVerdict(r, tLang)}`
|
|
296
422
|
: `- ${shortId(r.convId)} (${r.type}) · ${r.title} · ${subagentVerdict(r, tLang)}`) +
|
|
297
423
|
(body ? `\n ${body}` : "") +
|
|
298
|
-
(r.output ? `\n ${r.output.split("\n").
|
|
424
|
+
(r.output ? `\n ${clipSubagentOutput(r.output, tLang).split("\n").join("\n ")}` : ""));
|
|
299
425
|
})
|
|
300
426
|
.join("\n");
|
|
301
427
|
const timeoutSecs = Math.round(timeoutMs / 1000);
|
|
@@ -354,6 +480,21 @@ export function makeSubagentTools(host, lang, selfConvId) {
|
|
|
354
480
|
function shortId(id) {
|
|
355
481
|
return id.slice(0, 8);
|
|
356
482
|
}
|
|
483
|
+
/**
|
|
484
|
+
* wait_all 收口时的长输出截断:留头(任务复述)+ 留尾(最终结论),中间折叠。
|
|
485
|
+
* 旧实现只取前 30 行,长输出的子代理结论(一般在尾部)会被丢掉,模型收回一个
|
|
486
|
+
* 没结论的摘要。短输出原样返回。
|
|
487
|
+
*/
|
|
488
|
+
function clipSubagentOutput(output, lang) {
|
|
489
|
+
const HEAD = 10;
|
|
490
|
+
const TAIL = 30;
|
|
491
|
+
const lines = output.split("\n");
|
|
492
|
+
if (lines.length <= HEAD + TAIL + 5)
|
|
493
|
+
return output;
|
|
494
|
+
const omitted = lines.length - HEAD - TAIL;
|
|
495
|
+
const marker = lang === "zh" ? `… [中间省略 ${omitted} 行,头尾保留] …` : `… [${omitted} lines omitted, head and tail kept] …`;
|
|
496
|
+
return [...lines.slice(0, HEAD), marker, ...lines.slice(-TAIL)].join("\n");
|
|
497
|
+
}
|
|
357
498
|
/**
|
|
358
499
|
* 收集 root 的传递子代理后代 id(parentId 链向上能走到 root 的;含嵌套的嵌套)。
|
|
359
500
|
* root 自身不含;非子代理对话不含(普通对话不参与子代理清理口径)。
|
|
@@ -50,6 +50,9 @@ export const BROWSER_PAGE_TOOL_NAME = "browser_page";
|
|
|
50
50
|
/** 别的对话读取工具(定义见 conversation-read-tool.ts):运行中对话(含子代理)+
|
|
51
51
|
* 历史会话转录,只读。 */
|
|
52
52
|
export const CONVERSATION_READ_TOOL_NAME = "conversation_read";
|
|
53
|
+
/** 技能全文按名加载工具(定义见 skill-tool.ts):名录见 {{skills}} 段,
|
|
54
|
+
* 全文走本工具按需取,不再让模型拼路径调 read。 */
|
|
55
|
+
export const SKILL_TOOL_NAME = "skill";
|
|
53
56
|
/** 定时任务工具(定义见 schedule-agent-tool.ts):创建/查看/取消内置调度任务,
|
|
54
57
|
* 到期自动唤醒发起对话执行 prompt 并汇报。 */
|
|
55
58
|
export const SCHEDULE_TASK_TOOL_NAME = "schedule_task";
|
|
@@ -57,7 +60,7 @@ export const SCHEDULE_LIST_TOOL_NAME = "schedule_list";
|
|
|
57
60
|
export const SCHEDULE_CANCEL_TOOL_NAME = "schedule_cancel";
|
|
58
61
|
/** 旧工具名(持久化迁移用;新代码一律用 MARKERS_LIST_TOOL_NAME)。 */
|
|
59
62
|
export const LEGACY_MARKERS_LIST_TOOL_NAME = "markers_list";
|
|
60
|
-
/** 可开关的 Agent 工具总目录(共
|
|
63
|
+
/** 可开关的 Agent 工具总目录(共 24 个;bash 本体与 SDK 内置 edit/read
|
|
61
64
|
* 不进目录——关了 agent 就残了,不给关)。 */
|
|
62
65
|
export const AGENT_TOOL_CATALOG = [
|
|
63
66
|
...TERMINAL_TOOL_NAMES.map((name) => ({
|
|
@@ -81,6 +84,9 @@ export const AGENT_TOOL_CATALOG = [
|
|
|
81
84
|
{ name: BROWSER_PAGE_TOOL_NAME, group: "other", defaultOn: false, dshVisible: false },
|
|
82
85
|
// 只读别的对话(含子代理实时消息与历史转录),默认开;DSH 引擎没有该 customTool。
|
|
83
86
|
{ name: CONVERSATION_READ_TOOL_NAME, group: "other", defaultOn: true, dshVisible: false },
|
|
87
|
+
// 技能全文按名加载(名录仍在 {{skills}} 段),默认开;DSH 引擎没有该 customTool
|
|
88
|
+
// (走 goal-rpc,无 customTool 注册面)。
|
|
89
|
+
{ name: SKILL_TOOL_NAME, group: "other", defaultOn: true, dshVisible: false },
|
|
84
90
|
// 定时/延时唤醒:默认开(不打开 AI 根本不知道能定时;60s 间隔底线+面板可随时取消),
|
|
85
91
|
// DSH 引擎没有该 customTool(走 goal-rpc,无 customTool 注册面)。
|
|
86
92
|
{ name: SCHEDULE_TASK_TOOL_NAME, group: "other", defaultOn: true, dshVisible: false },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-web-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.91.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": {
|
|
@@ -91,6 +91,7 @@
|
|
|
91
91
|
"electron-updater": "^6.8.9",
|
|
92
92
|
"express": "^5.2.1",
|
|
93
93
|
"highlight.js": "^11.12.0",
|
|
94
|
+
"html-to-image": "^1.11.13",
|
|
94
95
|
"katex": "^0.18.7",
|
|
95
96
|
"node-pty": "^1.1.0",
|
|
96
97
|
"react": "^19.3.0",
|
package/plugins/catalog.json
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
"id": "webmail",
|
|
4
4
|
"name": "webmail",
|
|
5
5
|
"icon": "📬",
|
|
6
|
+
"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=\"M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z\"/><polyline points=\"22,6 12,13 2,6\"/></svg>",
|
|
6
7
|
"description": "网页邮箱:IMAP 收件 + SMTP 发信 + 新邮件通知,可开放给 AI 直接管理邮件。",
|
|
7
8
|
"descriptionEn": "Web mail: IMAP inbox + SMTP send + new-mail notifications, optionally let the AI manage mail.",
|
|
8
9
|
"source": "xing-shuyin/pi-web-ui/plugins/webmail",
|
|
@@ -12,6 +13,7 @@
|
|
|
12
13
|
"id": "db-client",
|
|
13
14
|
"name": "db-client",
|
|
14
15
|
"icon": "🗄️",
|
|
16
|
+
"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\"><ellipse cx=\"12\" cy=\"5\" rx=\"9\" ry=\"3\"/><path d=\"M21 12c0 1.66-4 3-9 3s-9-1.34-9-3\"/><path d=\"M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5\"/></svg>",
|
|
15
17
|
"description": "数据库连接管理:MySQL / PostgreSQL / SQLite / SQL Server / MongoDB / Redis 的库表浏览、SQL 查询与数据编辑。",
|
|
16
18
|
"descriptionEn": "Database client: browse schemas, run SQL and edit data across MySQL / PostgreSQL / SQLite / SQL Server / MongoDB / Redis.",
|
|
17
19
|
"source": "xing-shuyin/pi-web-ui/plugins/db-client",
|
|
@@ -21,6 +23,7 @@
|
|
|
21
23
|
"id": "vscode-editor",
|
|
22
24
|
"name": "vscode-editor",
|
|
23
25
|
"icon": "📝",
|
|
26
|
+
"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=\"m16 18 6-6-6-6\"/><path d=\"m8 6-6 6 6 6\"/></svg>",
|
|
24
27
|
"description": "类 VSCode 工作台:多根文件树 + CodeMirror 编辑器 + 底部终端 + Remote-SSH + SFTP 同步;向 AI 注册 SFTP/SSH/远端文件工具,可自主传代码。",
|
|
25
28
|
"descriptionEn": "VSCode-like workbench: multi-root file tree, CodeMirror editor, integrated terminal, Remote-SSH and SFTP sync; registers SFTP/SSH/remote-file tools so the AI can deploy code itself.",
|
|
26
29
|
"source": "xing-shuyin/pi-web-ui/plugins/vscode-editor",
|
|
@@ -30,6 +33,7 @@
|
|
|
30
33
|
"id": "mermaid",
|
|
31
34
|
"name": "mermaid",
|
|
32
35
|
"icon": "📊",
|
|
36
|
+
"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\"><circle cx=\"18\" cy=\"5\" r=\"3\"/><circle cx=\"6\" cy=\"12\" r=\"3\"/><circle cx=\"18\" cy=\"19\" r=\"3\"/><line x1=\"8.59\" y1=\"13.51\" x2=\"15.42\" y2=\"17.49\"/><line x1=\"15.41\" y1=\"6.51\" x2=\"8.59\" y2=\"10.49\"/></svg>",
|
|
33
37
|
"description": "把对话里的 ```mermaid 围栏渲染成 SVG 图表(fenced-code 渲染插件,本地引擎离线优先)。",
|
|
34
38
|
"descriptionEn": "Renders ```mermaid fences in messages to SVG diagrams (fenced-code renderer plugin, offline-first local engine).",
|
|
35
39
|
"source": "xing-shuyin/pi-web-ui/plugins/mermaid",
|
|
@@ -39,6 +43,7 @@
|
|
|
39
43
|
"id": "run-trace",
|
|
40
44
|
"name": "run-trace",
|
|
41
45
|
"icon": "🧭",
|
|
46
|
+
"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\"><polyline points=\"22 12 18 12 15 21 9 3 6 12 2 12\"/></svg>",
|
|
42
47
|
"description": "运行轨迹:任务 → 思考 → 工具 → 文件改动 → 结果的时间线聚合视图,支持回放与节点详情。",
|
|
43
48
|
"descriptionEn": "Run trajectory: task → thinking → tools → file changes → result timeline with replay and node details.",
|
|
44
49
|
"source": "xing-shuyin/pi-web-ui/plugins/run-trace",
|
|
@@ -48,6 +53,7 @@
|
|
|
48
53
|
"id": "legado-web",
|
|
49
54
|
"name": "legado-web",
|
|
50
55
|
"icon": "📖",
|
|
56
|
+
"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=\"M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z\"/><path d=\"M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z\"/></svg>",
|
|
51
57
|
"description": "Legado 阅读(文本源):搜索 / 发现 / 详情 / 目录 / 正文,书源 JSON 与安卓版兼容,可导入、检测、删废源;内置跨域+GBK 代理与本地存储,并给 AI 配了修源接口(读规则、读书源文件、逐步诊断链路、试规则验证)。",
|
|
52
58
|
"descriptionEn": "Legado reader (text book sources): search / explore / info / TOC / content with Android-compatible book source JSON, plus source import and health checking. Ships its own CORS+GBK proxy, local store, and AI tools to diagnose and repair book sources.",
|
|
53
59
|
"source": "xing-shuyin/pi-web-ui/plugins/legado-web",
|
|
@@ -57,6 +63,7 @@
|
|
|
57
63
|
"id": "wechat-ilink",
|
|
58
64
|
"name": "微信通道",
|
|
59
65
|
"icon": "💬",
|
|
66
|
+
"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>",
|
|
60
67
|
"description": "微信扫码登录(与腾讯 openclaw-weixin 同源的 ilink 协议):出站长轮询收消息,微信里直接指挥 agent,无需公网 IP。",
|
|
61
68
|
"descriptionEn": "WeChat channel (ilink protocol, same origin as Tencent openclaw-weixin): outbound long-polling, drive the agent from WeChat, no public IP needed.",
|
|
62
69
|
"source": "xing-shuyin/pi-web-ui/plugins/wechat-ilink",
|
|
@@ -66,6 +73,7 @@
|
|
|
66
73
|
"id": "voice-input",
|
|
67
74
|
"name": "语音输入",
|
|
68
75
|
"icon": "🎤",
|
|
76
|
+
"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>",
|
|
69
77
|
"description": "语音输入:输入框旁的麦克风按钮,浏览器语音识别直接听写进输入框;不支持/识别失败时自动降级为服务端转写(远端接口,或一键安装的本地 Whisper,免费不出网)。",
|
|
70
78
|
"descriptionEn": "Voice input: a mic button next to the composer. Browser speech recognition dictates into the input box, with server-side transcription as automatic fallback (remote API, or one-click local Whisper that runs fully offline).",
|
|
71
79
|
"source": "xing-shuyin/pi-web-ui/plugins/voice-input",
|
|
@@ -75,6 +83,7 @@
|
|
|
75
83
|
"id": "live-preview",
|
|
76
84
|
"name": "live-preview",
|
|
77
85
|
"icon": "🌐",
|
|
86
|
+
"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\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"2\" y1=\"12\" x2=\"22\" y2=\"12\"/><path d=\"M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z\"/></svg>",
|
|
78
87
|
"description": "Live Server 式预览:/liveserver 看 HTML(含相对资源与自动刷新),/md 看 Markdown 渲染;真服务只绑回环地址,经宿主通用代理对外只露同源前缀。",
|
|
79
88
|
"descriptionEn": "Live-Server-style preview: HTML under /liveserver (relative assets + auto reload), rendered Markdown under /md. Loopback-only inner server exposed via host proxy prefixes.",
|
|
80
89
|
"source": "xing-shuyin/pi-web-ui/plugins/live-preview",
|
|
@@ -84,6 +93,7 @@
|
|
|
84
93
|
"id": "image-toolkit",
|
|
85
94
|
"name": "image-toolkit",
|
|
86
95
|
"icon": "🖼",
|
|
96
|
+
"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=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"/><circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"/><polyline points=\"21 15 16 10 5 21\"/></svg>",
|
|
87
97
|
"description": "图片处理工作台:压缩(可按目标体积二分逼近)、裁剪、缩放、旋转/翻转、格式转换(PNG/JPEG/WebP/AVIF)、批量导出 ZIP、水印、滤镜调色、图片信息与 EXIF,可直接读写工作区图片;另给 AI 配了 4 个工具,让 agent 自己压缩/裁剪/缩放/加水印工作区里的图。",
|
|
88
98
|
"descriptionEn": "Image workbench: compress (with target-size binary search), crop, resize, rotate/flip, format conversion (PNG/JPEG/WebP/AVIF), batch ZIP export, watermark, filters, image info + EXIF, and direct workspace read/write. Ships 4 AI tools so the agent can compress/crop/resize/watermark workspace images itself.",
|
|
89
99
|
"source": "xing-shuyin/pi-web-ui/plugins/image-toolkit",
|
|
@@ -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-OoULNF70.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};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
function e(e,t){if(e.match(/^[a-z]+:\/\//i))return e;if(e.match(/^\/\//))return window.location.protocol+e;if(e.match(/^[a-z]+:/i))return e;let n=document.implementation.createHTMLDocument(),r=n.createElement(`base`),i=n.createElement(`a`);return n.head.appendChild(r),n.body.appendChild(i),t&&(r.href=t),i.href=e,i.href}var t=(()=>{let e=0,t=()=>`0000${(Math.random()*36**4<<0).toString(36)}`.slice(-4);return()=>(e+=1,`u${t()}${e}`)})();function n(e){let t=[];for(let n=0,r=e.length;n<r;n++)t.push(e[n]);return t}var r=null;function i(e={}){return r||(e.includeStyleProperties?(r=e.includeStyleProperties,r):(r=n(window.getComputedStyle(document.documentElement)),r))}function a(e,t){let n=(e.ownerDocument.defaultView||window).getComputedStyle(e).getPropertyValue(t);return n?parseFloat(n.replace(`px`,``)):0}function o(e){let t=a(e,`border-left-width`),n=a(e,`border-right-width`);return e.clientWidth+t+n}function s(e){let t=a(e,`border-top-width`),n=a(e,`border-bottom-width`);return e.clientHeight+t+n}function c(e,t={}){return{width:t.width||o(e),height:t.height||s(e)}}function ee(){let e,t;try{t=process}catch{}let n=t&&t.env?t.env.devicePixelRatio:null;return n&&(e=parseInt(n,10),Number.isNaN(e)&&(e=1)),e||window.devicePixelRatio||1}var l=16384;function te(e){(e.width>l||e.height>l)&&(e.width>l&&e.height>l?e.width>e.height?(e.height*=l/e.width,e.width=l):(e.width*=l/e.height,e.height=l):e.width>l?(e.height*=l/e.width,e.width=l):(e.width*=l/e.height,e.height=l))}function u(e,t={}){return e.toBlob?new Promise(n=>{e.toBlob(n,t.type?t.type:`image/png`,t.quality?t.quality:1)}):new Promise(n=>{let r=window.atob(e.toDataURL(t.type?t.type:void 0,t.quality?t.quality:void 0).split(`,`)[1]),i=r.length,a=new Uint8Array(i);for(let e=0;e<i;e+=1)a[e]=r.charCodeAt(e);n(new Blob([a],{type:t.type?t.type:`image/png`}))})}function d(e){return new Promise((t,n)=>{let r=new Image;r.onload=()=>{r.decode().then(()=>{requestAnimationFrame(()=>t(r))})},r.onerror=n,r.crossOrigin=`anonymous`,r.decoding=`async`,r.src=e})}async function f(e){return Promise.resolve().then(()=>new XMLSerializer().serializeToString(e)).then(encodeURIComponent).then(e=>`data:image/svg+xml;charset=utf-8,${e}`)}async function p(e,t,n){let r=`http://www.w3.org/2000/svg`,i=document.createElementNS(r,`svg`),a=document.createElementNS(r,`foreignObject`);return i.setAttribute(`width`,`${t}`),i.setAttribute(`height`,`${n}`),i.setAttribute(`viewBox`,`0 0 ${t} ${n}`),a.setAttribute(`width`,`100%`),a.setAttribute(`height`,`100%`),a.setAttribute(`x`,`0`),a.setAttribute(`y`,`0`),a.setAttribute(`externalResourcesRequired`,`true`),i.appendChild(a),a.appendChild(e),f(i)}var m=(e,t)=>{if(e instanceof t)return!0;let n=Object.getPrototypeOf(e);return n===null?!1:n.constructor.name===t.name||m(n,t)};function ne(e){let t=e.getPropertyValue(`content`);return`${e.cssText} content: '${t.replace(/'|"/g,``)}';`}function re(e,t){return i(t).map(t=>`${t}: ${e.getPropertyValue(t)}${e.getPropertyPriority(t)?` !important`:``};`).join(` `)}function ie(e,t,n,r){let i=`.${e}:${t}`,a=n.cssText?ne(n):re(n,r);return document.createTextNode(`${i}{${a}}`)}function h(e,n,r,i){let a=window.getComputedStyle(e,r),o=a.getPropertyValue(`content`);if(o===``||o===`none`)return;let s=t();try{n.className=`${n.className} ${s}`}catch{return}let c=document.createElement(`style`);c.appendChild(ie(s,r,a,i)),n.appendChild(c)}function ae(e,t,n){h(e,t,`:before`,n),h(e,t,`:after`,n)}var g=`application/font-woff`,_=`image/jpeg`,v={woff:g,woff2:g,ttf:`application/font-truetype`,eot:`application/vnd.ms-fontobject`,png:`image/png`,jpg:_,jpeg:_,gif:`image/gif`,tiff:`image/tiff`,svg:`image/svg+xml`,webp:`image/webp`};function y(e){let t=/\.([^./]*?)$/g.exec(e);return t?t[1]:``}function b(e){return v[y(e).toLowerCase()]||``}function oe(e){return e.split(/,/)[1]}function x(e){return e.search(/^(data:)/)!==-1}function S(e,t){return`data:${t};base64,${e}`}async function C(e,t,n){let r=await fetch(e,t);if(r.status===404)throw Error(`Resource "${r.url}" not found`);let i=await r.blob();return new Promise((e,t)=>{let a=new FileReader;a.onerror=t,a.onloadend=()=>{try{e(n({res:r,result:a.result}))}catch(e){t(e)}},a.readAsDataURL(i)})}var w={};function se(e,t,n){let r=e.replace(/\?.*/,``);return n&&(r=e),/ttf|otf|eot|woff2?/i.test(r)&&(r=r.replace(/.*\//,``)),t?`[${t}]${r}`:r}async function T(e,t,n){let r=se(e,t,n.includeQueryParams);if(w[r]!=null)return w[r];n.cacheBust&&(e+=(/\?/.test(e)?`&`:`?`)+new Date().getTime());let i;try{i=S(await C(e,n.fetchRequestInit,({res:e,result:n})=>(t||=e.headers.get(`Content-Type`)||``,oe(n))),t)}catch(t){i=n.imagePlaceholder||``;let r=`Failed to fetch resource: ${e}`;t&&(r=typeof t==`string`?t:t.message),r&&console.warn(r)}return w[r]=i,i}async function ce(e){let t=e.toDataURL();return t===`data:,`?e.cloneNode(!1):d(t)}async function le(e,t){if(e.currentSrc){let t=document.createElement(`canvas`),n=t.getContext(`2d`);return t.width=e.clientWidth,t.height=e.clientHeight,n?.drawImage(e,0,0,t.width,t.height),d(t.toDataURL())}let n=e.poster;return d(await T(n,b(n),t))}async function E(e,t){try{if(e?.contentDocument?.body)return await I(e.contentDocument.body,t,!0)}catch{}return e.cloneNode(!1)}async function D(e,t){return m(e,HTMLCanvasElement)?ce(e):m(e,HTMLVideoElement)?le(e,t):m(e,HTMLIFrameElement)?E(e,t):e.cloneNode(k(e))}var O=e=>e.tagName!=null&&e.tagName.toUpperCase()===`SLOT`,k=e=>e.tagName!=null&&e.tagName.toUpperCase()===`SVG`;async function A(e,t,r){if(k(t))return t;let i=[];return i=O(e)&&e.assignedNodes?n(e.assignedNodes()):m(e,HTMLIFrameElement)&&e.contentDocument?.body?n(e.contentDocument.body.childNodes):n((e.shadowRoot??e).childNodes),i.length===0||m(e,HTMLVideoElement)||await i.reduce((e,n)=>e.then(()=>I(n,r)).then(e=>{e&&t.appendChild(e)}),Promise.resolve()),t}function j(e,t,n){let r=t.style;if(!r)return;let a=window.getComputedStyle(e);a.cssText?(r.cssText=a.cssText,r.transformOrigin=a.transformOrigin):i(n).forEach(n=>{let i=a.getPropertyValue(n);n===`font-size`&&i.endsWith(`px`)&&(i=`${Math.floor(parseFloat(i.substring(0,i.length-2)))-.1}px`),m(e,HTMLIFrameElement)&&n===`display`&&i===`inline`&&(i=`block`),n===`d`&&t.getAttribute(`d`)&&(i=`path(${t.getAttribute(`d`)})`),r.setProperty(n,i,a.getPropertyPriority(n))})}function M(e,t){m(e,HTMLTextAreaElement)&&(t.innerHTML=e.value),m(e,HTMLInputElement)&&t.setAttribute(`value`,e.value)}function N(e,t){if(m(e,HTMLSelectElement)){let n=t,r=Array.from(n.children).find(t=>e.value===t.getAttribute(`value`));r&&r.setAttribute(`selected`,``)}}function P(e,t,n){return m(t,Element)&&(j(e,t,n),ae(e,t,n),M(e,t),N(e,t)),t}async function F(e,t){let n=e.querySelectorAll?e.querySelectorAll(`use`):[];if(n.length===0)return e;let r={};for(let i=0;i<n.length;i++){let a=n[i].getAttribute(`xlink:href`);if(a){let n=e.querySelector(a),i=document.querySelector(a);!n&&i&&!r[a]&&(r[a]=await I(i,t,!0))}}let i=Object.values(r);if(i.length){let t=`http://www.w3.org/1999/xhtml`,n=document.createElementNS(t,`svg`);n.setAttribute(`xmlns`,t),n.style.position=`absolute`,n.style.width=`0`,n.style.height=`0`,n.style.overflow=`hidden`,n.style.display=`none`;let r=document.createElementNS(t,`defs`);n.appendChild(r);for(let e=0;e<i.length;e++)r.appendChild(i[e]);e.appendChild(n)}return e}async function I(e,t,n){return!n&&t.filter&&!t.filter(e)?null:Promise.resolve(e).then(e=>D(e,t)).then(n=>A(e,n,t)).then(n=>P(e,n,t)).then(e=>F(e,t))}var L=/url\((['"]?)([^'"]+?)\1\)/g,R=/url\([^)]+\)\s*format\((["']?)([^"']+)\1\)/g,z=/src:\s*(?:url\([^)]+\)\s*format\([^)]+\)[,;]\s*)+/g;function B(e){let t=e.replace(/([.*+?^${}()|\[\]\/\\])/g,`\\$1`);return RegExp(`(url\\(['"]?)(${t})(['"]?\\))`,`g`)}function V(e){let t=[];return e.replace(L,(e,n,r)=>(t.push(r),e)),t.filter(e=>!x(e))}async function H(t,n,r,i,a){try{let o=r?e(n,r):n,s=b(n),c;return c=a?S(await a(o),s):await T(o,s,i),t.replace(B(n),`$1${c}$3`)}catch{}return t}function U(e,{preferredFontFormat:t}){return t?e.replace(z,e=>{for(;;){let[n,,r]=R.exec(e)||[];if(!r)return``;if(r===t)return`src: ${n};`}}):e}function W(e){return e.search(L)!==-1}async function G(e,t,n){if(!W(e))return e;let r=U(e,n);return V(r).reduce((e,r)=>e.then(e=>H(e,r,t,n)),Promise.resolve(r))}async function K(e,t,n){let r=t.style?.getPropertyValue(e);if(r){let i=await G(r,null,n);return t.style.setProperty(e,i,t.style.getPropertyPriority(e)),!0}return!1}async function ue(e,t){await K(`background`,e,t)||await K(`background-image`,e,t),await K(`mask`,e,t)||await K(`-webkit-mask`,e,t)||await K(`mask-image`,e,t)||await K(`-webkit-mask-image`,e,t)}async function de(e,t){let n=m(e,HTMLImageElement);if((!n||x(e.src))&&(!m(e,SVGImageElement)||x(e.href.baseVal)))return;let r=n?e.src:e.href.baseVal,i=await T(r,b(r),t);await new Promise((r,a)=>{e.onload=r,e.onerror=t.onImageErrorHandler?(...e)=>{try{r(t.onImageErrorHandler(...e))}catch(e){a(e)}}:a;let o=e;o.decode&&=r,o.loading===`lazy`&&(o.loading=`eager`),n?(e.srcset=``,e.src=i):e.href.baseVal=i})}async function fe(e,t){let r=n(e.childNodes).map(e=>q(e,t));await Promise.all(r).then(()=>e)}async function q(e,t){m(e,Element)&&(await ue(e,t),await de(e,t),await fe(e,t))}function pe(e,t){let{style:n}=e;t.backgroundColor&&(n.backgroundColor=t.backgroundColor),t.width&&(n.width=`${t.width}px`),t.height&&(n.height=`${t.height}px`);let r=t.style;return r!=null&&Object.keys(r).forEach(e=>{n[e]=r[e]}),e}var J={};async function Y(e){let t=J[e];return t??(t={url:e,cssText:await(await fetch(e)).text()},J[e]=t,t)}async function X(e,t){let n=e.cssText,r=/url\(["']?([^"')]+)["']?\)/g,i=(n.match(/url\([^)]+\)/g)||[]).map(async i=>{let a=i.replace(r,`$1`);return a.startsWith(`https://`)||(a=new URL(a,e.url).href),C(a,t.fetchRequestInit,({result:e})=>(n=n.replace(i,`url(${e})`),[i,e]))});return Promise.all(i).then(()=>n)}function Z(e){if(e==null)return[];let t=[],n=e.replace(/(\/\*[\s\S]*?\*\/)/gi,``),r=RegExp(`((@.*?keyframes [\\s\\S]*?){([\\s\\S]*?}\\s*?)})`,`gi`);for(;;){let e=r.exec(n);if(e===null)break;t.push(e[0])}n=n.replace(r,``);let i=/@import[\s\S]*?url\([^)]*\)[\s\S]*?;/gi,a=RegExp(`((\\s*?(?:\\/\\*[\\s\\S]*?\\*\\/)?\\s*?@media[\\s\\S]*?){([\\s\\S]*?)}\\s*?})|(([\\s\\S]*?){([\\s\\S]*?)})`,`gi`);for(;;){let e=i.exec(n);if(e===null){if(e=a.exec(n),e===null)break;i.lastIndex=a.lastIndex}else a.lastIndex=i.lastIndex;t.push(e[0])}return t}async function me(e,t){let r=[],i=[];return e.forEach(r=>{if(`cssRules`in r)try{n(r.cssRules||[]).forEach((e,n)=>{if(e.type===CSSRule.IMPORT_RULE){let a=n+1,o=e.href,s=Y(o).then(e=>X(e,t)).then(e=>Z(e).forEach(e=>{try{r.insertRule(e,e.startsWith(`@import`)?a+=1:r.cssRules.length)}catch(t){console.error(`Error inserting rule from remote css`,{rule:e,error:t})}})).catch(e=>{console.error(`Error loading remote css`,e.toString())});i.push(s)}})}catch(n){let a=e.find(e=>e.href==null)||document.styleSheets[0];r.href!=null&&i.push(Y(r.href).then(e=>X(e,t)).then(e=>Z(e).forEach(e=>{a.insertRule(e,a.cssRules.length)})).catch(e=>{console.error(`Error loading remote stylesheet`,e)})),console.error(`Error inlining remote css file`,n)}}),Promise.all(i).then(()=>(e.forEach(e=>{if(`cssRules`in e)try{n(e.cssRules||[]).forEach(e=>{r.push(e)})}catch(t){console.error(`Error while reading CSS rules from ${e.href}`,t)}}),r))}function he(e){return e.filter(e=>e.type===CSSRule.FONT_FACE_RULE).filter(e=>W(e.style.getPropertyValue(`src`)))}async function ge(e,t){if(e.ownerDocument==null)throw Error(`Provided element is not within a Document`);return he(await me(n(e.ownerDocument.styleSheets),t))}function Q(e){return e.trim().replace(/["']/g,``)}function $(e){let t=new Set;function n(e){(e.style.fontFamily||getComputedStyle(e).fontFamily).split(`,`).forEach(e=>{t.add(Q(e))}),Array.from(e.children).forEach(e=>{e instanceof HTMLElement&&n(e)})}return n(e),t}async function _e(e,t){let n=await ge(e,t),r=$(e);return(await Promise.all(n.filter(e=>r.has(Q(e.style.fontFamily))).map(e=>{let n=e.parentStyleSheet?e.parentStyleSheet.href:null;return G(e.cssText,n,t)}))).join(`
|
|
2
|
+
`)}async function ve(e,t){let n=t.fontEmbedCSS==null?t.skipFonts?null:await _e(e,t):t.fontEmbedCSS;if(n){let t=document.createElement(`style`),r=document.createTextNode(n);t.appendChild(r),e.firstChild?e.insertBefore(t,e.firstChild):e.appendChild(t)}}async function ye(e,t={}){let{width:n,height:r}=c(e,t),i=await I(e,t,!0);return await ve(i,t),await q(i,t),pe(i,t),await p(i,n,r)}async function be(e,t={}){let{width:n,height:r}=c(e,t),i=await d(await ye(e,t)),a=document.createElement(`canvas`),o=a.getContext(`2d`),s=t.pixelRatio||ee(),l=t.canvasWidth||n,u=t.canvasHeight||r;return a.width=l*s,a.height=u*s,t.skipAutoScale||te(a),a.style.width=`${l}`,a.style.height=`${u}`,t.backgroundColor&&(o.fillStyle=t.backgroundColor,o.fillRect(0,0,a.width,a.height)),o.drawImage(i,0,0,a.width,a.height),a}async function xe(e,t={}){return await u(await be(e,t))}export{xe as toBlob};
|