pi-web-ui 0.96.0 → 0.97.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 +69 -2
- package/README.md +26 -0
- package/README.zh-CN.md +25 -0
- package/bin/pi-web-ui.mjs +11 -2
- package/dist/server/agent-service.js +1195 -120
- package/dist/server/client-state.js +89 -1
- package/dist/server/compacted-history.js +146 -0
- package/dist/server/delegate-mode.js +149 -0
- package/dist/server/delegate-task.js +10 -10
- package/dist/server/dsh/dsh-agent-service.js +35 -2
- package/dist/server/dsh/dsh-client.js +3 -1
- package/dist/server/eval-tool.js +3 -1
- package/dist/server/goal-review-gate.js +263 -0
- package/dist/server/goal-service.js +1225 -445
- package/dist/server/index.js +79 -20
- package/dist/server/lsp-tool.js +13 -10
- package/dist/server/markers/builtins/todo.js +0 -2
- package/dist/server/mcp-bridge.js +13 -0
- package/dist/server/patch-tool.js +1 -0
- package/dist/server/patch-turn-end-boundary.js +77 -0
- package/dist/server/permission-preset.js +44 -0
- package/dist/server/plan-mode.js +376 -0
- package/dist/server/plugin-facilities.js +36 -3
- package/dist/server/plugin-installer.js +30 -0
- package/dist/server/plugins.js +6 -3
- package/dist/server/protocol-version.js +1 -1
- package/dist/server/serialize.js +11 -0
- package/dist/server/settings-service.js +20 -8
- package/dist/server/subagents.js +360 -408
- package/dist/server/text-sniff.js +15 -9
- package/dist/server/tool-manager.js +48 -4
- package/dist/server/update-check.js +11 -3
- package/dist/server/wait-subscription-scan.js +5 -1
- package/package.json +3 -3
- package/themes/zhupi-dark.css +79 -44
- package/themes/zhupi.css +79 -44
- package/web/dist/assets/{TerminalPanel-DDChcYOh.js → TerminalPanel-CITBqQrb.js} +1 -1
- package/web/dist/assets/index-DwToli0J.css +1 -0
- package/web/dist/assets/index-oI7UKOqJ.js +368 -0
- package/web/dist/index.html +2 -2
- package/web/dist/assets/index-Do9RgJC3.js +0 -366
- package/web/dist/assets/index-DryOsILO.css +0 -1
|
@@ -187,21 +187,27 @@ export function looksLikeText(buf) {
|
|
|
187
187
|
}
|
|
188
188
|
return control / Math.max(text.length, 1) < 0.02;
|
|
189
189
|
}
|
|
190
|
-
/** Decode bytes: strict UTF-8 first
|
|
191
|
-
*
|
|
192
|
-
*
|
|
190
|
+
/** Decode bytes: strict UTF-8 first (retrying with up to 3 truncated tail
|
|
191
|
+
* bytes removed for excerpt/preview buffers cut mid-character), falling back
|
|
192
|
+
* to GBK (Windows legacy Chinese files), then latin1 as a last resort — so
|
|
193
|
+
* previews and inline attachments never show mojibake for GBK/GB2312 files
|
|
194
|
+
* nor for truncated UTF-8 files (issue #392). */
|
|
193
195
|
export function decodeText(buf) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
catch {
|
|
196
|
+
for (let cut = 0; cut < 4; cut++) {
|
|
197
|
+
const slice = cut === 0 ? buf : buf.subarray(0, Math.max(0, buf.length - cut));
|
|
198
198
|
try {
|
|
199
|
-
return new TextDecoder("
|
|
199
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(slice);
|
|
200
200
|
}
|
|
201
201
|
catch {
|
|
202
|
-
|
|
202
|
+
// Tail byte might be cut mid-character; try trimming up to 3 bytes.
|
|
203
203
|
}
|
|
204
204
|
}
|
|
205
|
+
try {
|
|
206
|
+
return new TextDecoder("gbk").decode(buf);
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
return buf.toString("latin1");
|
|
210
|
+
}
|
|
205
211
|
}
|
|
206
212
|
/** Sniff an image MIME type from magic bytes (extension is only a hint).
|
|
207
213
|
* Returns null when the bytes don't look like a known raster format —
|
|
@@ -25,8 +25,11 @@ export const TERMINAL_TOOL_NAMES = [
|
|
|
25
25
|
"terminal_read",
|
|
26
26
|
"terminal_wait",
|
|
27
27
|
];
|
|
28
|
-
/** 第一方子代理工具(定义见 subagents.ts
|
|
29
|
-
export const
|
|
28
|
+
/** 第一方子代理工具(定义见 subagents.ts,统一为单 action 工具)。 */
|
|
29
|
+
export const SUBAGENT_TOOL_NAME = "subagent";
|
|
30
|
+
export const SUBAGENT_TOOL_NAMES = [SUBAGENT_TOOL_NAME];
|
|
31
|
+
/** 旧版 8 个独立子代理工具名(持久化配置迁移用)。 */
|
|
32
|
+
export const LEGACY_SUBAGENT_TOOL_NAMES = [
|
|
30
33
|
"subagent_spawn",
|
|
31
34
|
"subagent_get_result",
|
|
32
35
|
"subagent_steer",
|
|
@@ -251,7 +254,7 @@ export const AGENT_TOOL_CATALOG = [
|
|
|
251
254
|
},
|
|
252
255
|
];
|
|
253
256
|
/** SDK 核心内置工具名(可被门控显式禁用或被预设白名单过滤)。 */
|
|
254
|
-
export const CORE_BUILTIN_TOOL_NAMES = ["bash", "read", "edit", "write"];
|
|
257
|
+
export const CORE_BUILTIN_TOOL_NAMES = ["bash", "read", "edit", "write", "powershell", "ls", "grep", "find"];
|
|
255
258
|
export function isCoreBuiltinTool(name) {
|
|
256
259
|
return CORE_BUILTIN_TOOL_NAMES.includes(name);
|
|
257
260
|
}
|
|
@@ -268,7 +271,11 @@ export function normalizeDisabledAgentTools(v) {
|
|
|
268
271
|
const out = [];
|
|
269
272
|
for (const x of v) {
|
|
270
273
|
// 旧名迁移:markers_list → todo_list(改名前已关闭的用户保持关闭)。
|
|
271
|
-
|
|
274
|
+
let name = x === LEGACY_MARKERS_LIST_TOOL_NAME ? MARKERS_LIST_TOOL_NAME : x;
|
|
275
|
+
// 旧版子代理工具迁移:任意旧 subagent_* 关闭均迁移为关闭 subagent 工具。
|
|
276
|
+
if (typeof name === "string" && LEGACY_SUBAGENT_TOOL_NAMES.includes(name)) {
|
|
277
|
+
name = SUBAGENT_TOOL_NAME;
|
|
278
|
+
}
|
|
272
279
|
if (typeof name === "string" && (KNOWN_NAMES.has(name) || isCoreBuiltinTool(name)) && !out.includes(name)) {
|
|
273
280
|
out.push(name);
|
|
274
281
|
}
|
|
@@ -363,7 +370,9 @@ export const PI_AGENT_PRESETS = [
|
|
|
363
370
|
trust: "system",
|
|
364
371
|
isDefault: true,
|
|
365
372
|
name: "全功能",
|
|
373
|
+
nameEn: "Full access",
|
|
366
374
|
description: "提供全部可用工具与扩展能力(默认)",
|
|
375
|
+
descriptionEn: "All available tools and extension capabilities (default)",
|
|
367
376
|
order: 0,
|
|
368
377
|
},
|
|
369
378
|
{
|
|
@@ -371,7 +380,9 @@ export const PI_AGENT_PRESETS = [
|
|
|
371
380
|
trust: "system",
|
|
372
381
|
isDefault: false,
|
|
373
382
|
name: "极简模式",
|
|
383
|
+
nameEn: "Minimal",
|
|
374
384
|
description: "仅保留 bash 与 read;插件工具、技能名录与终端引导同步隐藏",
|
|
385
|
+
descriptionEn: "Keeps only bash and read; plugin tools, skill catalog and terminal guidance are hidden too",
|
|
375
386
|
order: 1,
|
|
376
387
|
},
|
|
377
388
|
{
|
|
@@ -379,7 +390,9 @@ export const PI_AGENT_PRESETS = [
|
|
|
379
390
|
trust: "system",
|
|
380
391
|
isDefault: false,
|
|
381
392
|
name: "代码开发",
|
|
393
|
+
nameEn: "Code development",
|
|
382
394
|
description: "专注于代码读写与执行(bash, read, edit, write, edit_soft);插件工具与技能名录同步隐藏",
|
|
395
|
+
descriptionEn: "Focused on reading, writing and running code (bash, read, edit, write, edit_soft); plugin tools and skill catalog are hidden too",
|
|
383
396
|
order: 2,
|
|
384
397
|
},
|
|
385
398
|
{
|
|
@@ -387,7 +400,9 @@ export const PI_AGENT_PRESETS = [
|
|
|
387
400
|
trust: "system",
|
|
388
401
|
isDefault: false,
|
|
389
402
|
name: "只读分析",
|
|
403
|
+
nameEn: "Read-only analysis",
|
|
390
404
|
description: "仅保留只读工具,禁止写操作;插件工具同步隐藏(读写未知,保守处理)",
|
|
405
|
+
descriptionEn: "Keeps only read-only tools and forbids writes; plugin tools are hidden too (read/write unknown, handled conservatively)",
|
|
391
406
|
order: 3,
|
|
392
407
|
},
|
|
393
408
|
{
|
|
@@ -395,7 +410,9 @@ export const PI_AGENT_PRESETS = [
|
|
|
395
410
|
trust: "system",
|
|
396
411
|
isDefault: false,
|
|
397
412
|
name: "纯对话",
|
|
413
|
+
nameEn: "Chat only",
|
|
398
414
|
description: "无工具问答模式,模型不调用任何工具;插件工具与技能名录同步隐藏",
|
|
415
|
+
descriptionEn: "No-tools Q&A: the model never calls a tool; plugin tools and skill catalog are hidden too",
|
|
399
416
|
order: 4,
|
|
400
417
|
},
|
|
401
418
|
];
|
|
@@ -404,19 +421,45 @@ export const PI_PERMISSION_OPTIONS = [
|
|
|
404
421
|
{
|
|
405
422
|
value: "read-only",
|
|
406
423
|
name: "只读模式",
|
|
424
|
+
nameEn: "Read Only",
|
|
407
425
|
description: "禁止所有文件修改(write/edit/edit_soft)及任何非只读操作",
|
|
426
|
+
descriptionEn: "Forbids all file modification (write/edit/edit_soft) and any non-read-only operation",
|
|
408
427
|
},
|
|
409
428
|
{
|
|
410
429
|
value: "workspace-write-never",
|
|
411
430
|
name: "工作区内修改",
|
|
431
|
+
nameEn: "Workspace Write",
|
|
412
432
|
description: "仅允许在当前工作区目录下修改文件,工作区外写操作一律拒绝",
|
|
433
|
+
descriptionEn: "Only files under the current workspace may be modified; writes outside it are always denied",
|
|
413
434
|
},
|
|
414
435
|
{
|
|
415
436
|
value: "danger-full-access",
|
|
416
437
|
name: "完全权限",
|
|
438
|
+
nameEn: "Full access",
|
|
417
439
|
description: "允许修改任意目录文件及执行全量操作(需要二次确认)",
|
|
440
|
+
descriptionEn: "Allows modifying files anywhere and running any operation (needs a second confirmation)",
|
|
418
441
|
},
|
|
419
442
|
];
|
|
443
|
+
/**
|
|
444
|
+
* 预设/权限文案随界面语言落定:中文界面用服务端默认文案,其它语言用
|
|
445
|
+
* nameEn ?? name(与审批规则的 labelEn/reasonEn、插件的 label/labelEn 同一约定;
|
|
446
|
+
* 其它语言包同理回落英文)。服务端 notice 的 textEn 也走这里,否则英文界面会看到
|
|
447
|
+
* `Switched to preset "全功能"`。
|
|
448
|
+
*
|
|
449
|
+
* lang 是 ServerLang(resolveServerLang 的产物):只有 "zh" 算中文。
|
|
450
|
+
* 本文件保持零依赖,故直接比字符串而不 import server/i18n.js。
|
|
451
|
+
*/
|
|
452
|
+
export function localizedName(item, lang, fallback = "") {
|
|
453
|
+
if (!item)
|
|
454
|
+
return fallback;
|
|
455
|
+
return lang === "zh" ? (item.name ?? item.nameEn ?? fallback) : (item.nameEn ?? item.name ?? fallback);
|
|
456
|
+
}
|
|
457
|
+
/** localizedName 的描述版本(description 两边都可缺省 → undefined)。 */
|
|
458
|
+
export function localizedDescription(item, lang) {
|
|
459
|
+
if (!item)
|
|
460
|
+
return undefined;
|
|
461
|
+
return lang === "zh" ? (item.description ?? item.descriptionEn) : (item.descriptionEn ?? item.description);
|
|
462
|
+
}
|
|
420
463
|
/** 按预设过滤活跃工具名。 */
|
|
421
464
|
export function filterToolsByPreset(tools, preset) {
|
|
422
465
|
const all = Array.from(tools);
|
|
@@ -437,6 +480,7 @@ export function filterToolsByPreset(tools, preset) {
|
|
|
437
480
|
"edit",
|
|
438
481
|
"edit_soft",
|
|
439
482
|
"bash",
|
|
483
|
+
"powershell",
|
|
440
484
|
"terminal_create",
|
|
441
485
|
"terminal_input",
|
|
442
486
|
"terminal_close",
|
|
@@ -466,13 +466,21 @@ function readVendoredPiCore(agentDir) {
|
|
|
466
466
|
return null;
|
|
467
467
|
}
|
|
468
468
|
}
|
|
469
|
+
/**
|
|
470
|
+
* 宿主 SDK 及其 peer/内部包(@earendil-works/* / @mariozechner/*):
|
|
471
|
+
* 属于宿主引擎随核心一同分发或由别名提供的 SDK 内部构件,非用户安装的独立扩展组件;
|
|
472
|
+
* 既不应在组件列表重复展示,也不能被 `pi update npm:<name>` 更新(会报 No matching package found,issue #393)。
|
|
473
|
+
*/
|
|
474
|
+
export function isHostProvidedPackage(name) {
|
|
475
|
+
return name === PI_CORE_PACKAGE || name.startsWith("@earendil-works/") || name.startsWith("@mariozechner/");
|
|
476
|
+
}
|
|
469
477
|
/**
|
|
470
478
|
* Build the full local target list: webui + the pi core + installed packages
|
|
471
479
|
* + git-source extensions (issue #178) from global and project settings.
|
|
472
480
|
* The pi core version comes from the CLI probe (injectable for tests), falling
|
|
473
481
|
* back to the vendored copy under <agentDir>/npm/node_modules. Packages
|
|
474
|
-
* listing the core directly are filtered out so the
|
|
475
|
-
*
|
|
482
|
+
* listing the core directly or host-provided SDK peers are filtered out so the
|
|
483
|
+
* pi-core row wins and host peers are not listed as updatable (issue #393).
|
|
476
484
|
*/
|
|
477
485
|
export function collectTargets(agentDir, webuiVersion, probePiCore = defaultProbePiCore, opts) {
|
|
478
486
|
const targets = [{ name: "pi-web-ui", version: webuiVersion, kind: "webui" }];
|
|
@@ -484,7 +492,7 @@ export function collectTargets(agentDir, webuiVersion, probePiCore = defaultProb
|
|
|
484
492
|
kind: "pi-core",
|
|
485
493
|
});
|
|
486
494
|
}
|
|
487
|
-
targets.push(...listInstalledPackages(agentDir).filter((pkg) => pkg.name
|
|
495
|
+
targets.push(...listInstalledPackages(agentDir).filter((pkg) => !isHostProvidedPackage(pkg.name)));
|
|
488
496
|
targets.push(...listGitExtensions(agentDir, opts?.projectCwd));
|
|
489
497
|
return targets;
|
|
490
498
|
}
|
|
@@ -239,12 +239,16 @@ export function hasPendingWaitSubscription(options) {
|
|
|
239
239
|
/**
|
|
240
240
|
* 纯函数版置换决策:true = 保留(不得 dispose),false = 调用方可释放。
|
|
241
241
|
* Pure decision core of displaceActive(): true = retain, false = may dispose.
|
|
242
|
-
* 顺序与 displaceActive 保持一致:review/wizard → streaming → terminals →
|
|
242
|
+
* 顺序与 displaceActive 保持一致:pin → review/wizard → streaming → terminals →
|
|
243
243
|
* active subagent run → pending wake → listed+continued(「打开后继续过」的会话也保留)。
|
|
244
244
|
*/
|
|
245
245
|
export function shouldRetainActive(input) {
|
|
246
|
+
if (input.pinned)
|
|
247
|
+
return true;
|
|
246
248
|
if (input.reviewing || input.wizardRunning)
|
|
247
249
|
return true;
|
|
250
|
+
if (input.promptInFlight)
|
|
251
|
+
return true;
|
|
248
252
|
if (input.streaming || input.compacting)
|
|
249
253
|
return true;
|
|
250
254
|
if (input.openTerminals > 0)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-web-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.97.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": {
|
|
@@ -83,8 +83,8 @@
|
|
|
83
83
|
"lint:fix": "oxlint --fix server/ web/src/ extensions/ scripts/ desktop/"
|
|
84
84
|
},
|
|
85
85
|
"dependencies": {
|
|
86
|
-
"@deepseek-ai/dsh-sdk-jsonrpc-server": "^0.1.
|
|
87
|
-
"@deepseek-ai/dsh-sdk-protocol": "^0.1.
|
|
86
|
+
"@deepseek-ai/dsh-sdk-jsonrpc-server": "^0.1.5-rc.3",
|
|
87
|
+
"@deepseek-ai/dsh-sdk-protocol": "^0.1.5-rc.3",
|
|
88
88
|
"@earendil-works/pi-coding-agent": ">=0.85.1",
|
|
89
89
|
"@xterm/addon-fit": "^0.11.0",
|
|
90
90
|
"@xterm/xterm": "^6.0.0",
|
package/themes/zhupi-dark.css
CHANGED
|
@@ -149,11 +149,13 @@ body,
|
|
|
149
149
|
letter-spacing: 0.06em;
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
/* 顶栏控件全部素色化:无底无框,hover 才现 hairline
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
.topbar
|
|
156
|
-
.topbar
|
|
152
|
+
/* 顶栏控件全部素色化:无底无框,hover 才现 hairline。兜底所有 button /
|
|
153
|
+
* [role=button]——不管类名是谁(基线 chip / 插件注入 / capsule 等),顶栏里
|
|
154
|
+
* 一律裸图标,dropdown 触发器不许再带盒子。 */
|
|
155
|
+
.topbar button,
|
|
156
|
+
.topbar [role="button"],
|
|
157
|
+
.topbar a.chip,
|
|
158
|
+
.topbar .chip {
|
|
157
159
|
background: transparent !important;
|
|
158
160
|
border: 1px solid transparent !important;
|
|
159
161
|
border-radius: 3px !important;
|
|
@@ -163,14 +165,20 @@ body,
|
|
|
163
165
|
color 0.15s ease !important;
|
|
164
166
|
}
|
|
165
167
|
|
|
166
|
-
.topbar
|
|
167
|
-
.topbar
|
|
168
|
-
.topbar .
|
|
169
|
-
.topbar .
|
|
168
|
+
.topbar button:hover,
|
|
169
|
+
.topbar [role="button"]:hover,
|
|
170
|
+
.topbar a.chip:hover,
|
|
171
|
+
.topbar .chip:hover {
|
|
170
172
|
border-color: var(--border) !important;
|
|
171
173
|
background: transparent !important;
|
|
172
174
|
}
|
|
173
175
|
|
|
176
|
+
.topbar .dropdown {
|
|
177
|
+
background: transparent !important;
|
|
178
|
+
border: none !important;
|
|
179
|
+
box-shadow: none !important;
|
|
180
|
+
}
|
|
181
|
+
|
|
174
182
|
/* ---- 2. 消息区:左缘墨线活动脊 + 文档化排版 ----
|
|
175
183
|
* 列缘这条线让给脊线与输入框边框:脊线(虚线针脚)与输入框左边框同一条
|
|
176
184
|
* 像素线,历史虚下来、接到输入框变实;消息正文整体右移 23px 挂在轨上。
|
|
@@ -190,35 +198,9 @@ body,
|
|
|
190
198
|
padding: 2px 0 10px;
|
|
191
199
|
}
|
|
192
200
|
|
|
193
|
-
/*
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
/* 兜底路径(用户消息 / 纯文本回复,无 head 可宿主):底部操作行改为悬停
|
|
197
|
-
浮牌,钉在消息右上角的空纸面上 */
|
|
198
|
-
.msg-actions {
|
|
199
|
-
position: absolute;
|
|
200
|
-
top: 0;
|
|
201
|
-
right: 0;
|
|
202
|
-
margin: 0;
|
|
203
|
-
z-index: 2;
|
|
204
|
-
padding: 2px;
|
|
205
|
-
background: var(--menu-bg) !important;
|
|
206
|
-
border: 1px solid var(--border) !important;
|
|
207
|
-
border-radius: 4px !important;
|
|
208
|
-
box-shadow: 0 4px 14px -4px rgba(0, 0, 0, 0.5) !important;
|
|
209
|
-
opacity: 0;
|
|
210
|
-
pointer-events: none;
|
|
211
|
-
transition:
|
|
212
|
-
opacity 0.12s ease,
|
|
213
|
-
pointer-events 0.12s ease;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
.msg:hover .msg-actions,
|
|
217
|
-
.msg-actions.has-open-dropdown,
|
|
218
|
-
.msg-actions:focus-within {
|
|
219
|
-
opacity: 1;
|
|
220
|
-
pointer-events: auto;
|
|
221
|
-
}
|
|
201
|
+
/* 操作按钮:卡头只有自己的复制键(.chead-copy),消息级按钮(复制三件套 /
|
|
202
|
+
分叉 / 回滚 / 朗读)统一在底部 .msg-actions 行,流内排在消息气泡/正文下边,
|
|
203
|
+
桌面端与手机端保持一致。 */
|
|
222
204
|
|
|
223
205
|
/* 角色行:书目式小标 */
|
|
224
206
|
.msg-meta {
|
|
@@ -237,7 +219,8 @@ body,
|
|
|
237
219
|
margin-left: -6px;
|
|
238
220
|
}
|
|
239
221
|
|
|
240
|
-
/*
|
|
222
|
+
/* 正文块上的单一复制键去重:卡头现在只有自己的复制键,消息级复制
|
|
223
|
+
(纯文本 / Markdown / 长图三件套)统一在底部 .msg-actions 行里。 */
|
|
241
224
|
.msg-text-copy {
|
|
242
225
|
display: none !important;
|
|
243
226
|
}
|
|
@@ -469,6 +452,12 @@ div.chead[role="button"]:hover,
|
|
|
469
452
|
border-bottom: 1px dotted var(--border-soft) !important;
|
|
470
453
|
border-radius: 0 !important;
|
|
471
454
|
box-shadow: none !important;
|
|
455
|
+
padding-left: 0 !important;
|
|
456
|
+
padding-right: 0 !important;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.msg-collapsed-action::before {
|
|
460
|
+
margin-left: 0 !important;
|
|
472
461
|
}
|
|
473
462
|
|
|
474
463
|
.msg-error {
|
|
@@ -487,7 +476,7 @@ div.chead[role="button"]:hover,
|
|
|
487
476
|
|
|
488
477
|
.inputbox {
|
|
489
478
|
background: var(--inputbox-bg) !important;
|
|
490
|
-
border:
|
|
479
|
+
border: none !important;
|
|
491
480
|
border-radius: 3px !important;
|
|
492
481
|
box-shadow: inset 3px 0 0 0 rgba(224, 90, 53, 0.4) !important;
|
|
493
482
|
transition:
|
|
@@ -496,14 +485,14 @@ div.chead[role="button"]:hover,
|
|
|
496
485
|
}
|
|
497
486
|
|
|
498
487
|
.inputbox:focus-within {
|
|
499
|
-
border
|
|
488
|
+
border: none !important;
|
|
500
489
|
box-shadow: inset 3px 0 0 0 var(--accent) !important;
|
|
501
490
|
}
|
|
502
491
|
|
|
503
492
|
.btn.send {
|
|
504
493
|
background: var(--send-blue) !important;
|
|
505
494
|
color: #131313 !important;
|
|
506
|
-
border-radius:
|
|
495
|
+
border-radius: 50% !important;
|
|
507
496
|
box-shadow: none !important;
|
|
508
497
|
font-weight: 600;
|
|
509
498
|
}
|
|
@@ -515,7 +504,54 @@ div.chead[role="button"]:hover,
|
|
|
515
504
|
.btn.stop {
|
|
516
505
|
background: var(--stop-red) !important;
|
|
517
506
|
color: #131313 !important;
|
|
507
|
+
border-radius: 50% !important;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/* 输入框底部模型选择、思考强度、预设等按钮:去边框、透明底,hover 现 hairline */
|
|
511
|
+
.composer-tools {
|
|
512
|
+
border-top: none !important;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
.composer-tools .chip,
|
|
516
|
+
.composer-tools .capsule-segment {
|
|
517
|
+
font-size: 13px !important;
|
|
518
|
+
padding: 4px 10px !important;
|
|
519
|
+
gap: 6px !important;
|
|
520
|
+
background: transparent !important;
|
|
521
|
+
border: 1px solid transparent !important;
|
|
522
|
+
box-shadow: none !important;
|
|
518
523
|
border-radius: 3px !important;
|
|
524
|
+
transition:
|
|
525
|
+
border-color 0.15s ease,
|
|
526
|
+
background 0.15s ease !important;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
.composer-tools .model-thinking-capsule {
|
|
530
|
+
background: transparent !important;
|
|
531
|
+
border: none !important;
|
|
532
|
+
box-shadow: none !important;
|
|
533
|
+
padding: 0 !important;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
.composer-tools .model-thinking-capsule .capsule-divider {
|
|
537
|
+
display: none !important;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
.composer-tools .chip:hover,
|
|
541
|
+
.composer-tools .capsule-segment:hover {
|
|
542
|
+
border-color: var(--border) !important;
|
|
543
|
+
background: transparent !important;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
.composer-tools-left {
|
|
547
|
+
gap: 6px !important;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
.composer-tools .chip-model,
|
|
551
|
+
.composer-tools .chip-sub,
|
|
552
|
+
.composer-tools .capsule-model-name,
|
|
553
|
+
.composer-tools .capsule-thinking-text {
|
|
554
|
+
font-size: 12.5px !important;
|
|
519
555
|
}
|
|
520
556
|
|
|
521
557
|
/* ---- 9. 底栏 / 状态栏:hairline 收边 ---- */
|
|
@@ -606,8 +642,7 @@ div.chead[role="button"]:hover,
|
|
|
606
642
|
0 16px 40px -12px rgba(0, 0, 0, 0.55) !important;
|
|
607
643
|
}
|
|
608
644
|
|
|
609
|
-
.dd-menu
|
|
610
|
-
.dropdown {
|
|
645
|
+
.dd-menu {
|
|
611
646
|
background: var(--menu-bg) !important;
|
|
612
647
|
border: 1px solid var(--border) !important;
|
|
613
648
|
border-radius: 4px !important;
|
package/themes/zhupi.css
CHANGED
|
@@ -154,11 +154,13 @@ body,
|
|
|
154
154
|
letter-spacing: 0.06em;
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
/* 顶栏控件全部素色化:无底无框,hover 才现 hairline
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
.topbar
|
|
161
|
-
.topbar
|
|
157
|
+
/* 顶栏控件全部素色化:无底无框,hover 才现 hairline。兜底所有 button /
|
|
158
|
+
* [role=button]——不管类名是谁(基线 chip / 插件注入 / capsule 等),顶栏里
|
|
159
|
+
* 一律裸图标,dropdown 触发器不许再带盒子。 */
|
|
160
|
+
.topbar button,
|
|
161
|
+
.topbar [role="button"],
|
|
162
|
+
.topbar a.chip,
|
|
163
|
+
.topbar .chip {
|
|
162
164
|
background: transparent !important;
|
|
163
165
|
border: 1px solid transparent !important;
|
|
164
166
|
border-radius: 3px !important;
|
|
@@ -168,14 +170,20 @@ body,
|
|
|
168
170
|
color 0.15s ease !important;
|
|
169
171
|
}
|
|
170
172
|
|
|
171
|
-
.topbar
|
|
172
|
-
.topbar
|
|
173
|
-
.topbar .
|
|
174
|
-
.topbar .
|
|
173
|
+
.topbar button:hover,
|
|
174
|
+
.topbar [role="button"]:hover,
|
|
175
|
+
.topbar a.chip:hover,
|
|
176
|
+
.topbar .chip:hover {
|
|
175
177
|
border-color: var(--border) !important;
|
|
176
178
|
background: transparent !important;
|
|
177
179
|
}
|
|
178
180
|
|
|
181
|
+
.topbar .dropdown {
|
|
182
|
+
background: transparent !important;
|
|
183
|
+
border: none !important;
|
|
184
|
+
box-shadow: none !important;
|
|
185
|
+
}
|
|
186
|
+
|
|
179
187
|
/* ---- 2. 消息区:左缘墨线活动脊 + 文档化排版 ----
|
|
180
188
|
* 列缘这条线让给脊线与输入框边框:脊线(虚线针脚)与输入框左边框同一条
|
|
181
189
|
* 像素线,历史虚下来、接到输入框变实,像「线活了起来」;消息正文整体
|
|
@@ -197,35 +205,9 @@ body,
|
|
|
197
205
|
padding: 2px 0 10px;
|
|
198
206
|
}
|
|
199
207
|
|
|
200
|
-
/*
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
/* 兜底路径(用户消息 / 纯文本回复,无 head 可宿主):底部操作行改为悬停
|
|
204
|
-
浮牌,钉在消息右上角的空纸面上 */
|
|
205
|
-
.msg-actions {
|
|
206
|
-
position: absolute;
|
|
207
|
-
top: 0;
|
|
208
|
-
right: 0;
|
|
209
|
-
margin: 0;
|
|
210
|
-
z-index: 2;
|
|
211
|
-
padding: 2px;
|
|
212
|
-
background: var(--menu-bg) !important;
|
|
213
|
-
border: 1px solid var(--border) !important;
|
|
214
|
-
border-radius: 4px !important;
|
|
215
|
-
box-shadow: 0 4px 14px -4px rgba(38, 34, 24, 0.35) !important;
|
|
216
|
-
opacity: 0;
|
|
217
|
-
pointer-events: none;
|
|
218
|
-
transition:
|
|
219
|
-
opacity 0.12s ease,
|
|
220
|
-
pointer-events 0.12s ease;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
.msg:hover .msg-actions,
|
|
224
|
-
.msg-actions.has-open-dropdown,
|
|
225
|
-
.msg-actions:focus-within {
|
|
226
|
-
opacity: 1;
|
|
227
|
-
pointer-events: auto;
|
|
228
|
-
}
|
|
208
|
+
/* 操作按钮:卡头只有自己的复制键(.chead-copy),消息级按钮(复制三件套 /
|
|
209
|
+
分叉 / 回滚 / 朗读)统一在底部 .msg-actions 行,流内排在消息气泡/正文下边,
|
|
210
|
+
桌面端与手机端保持一致。 */
|
|
229
211
|
|
|
230
212
|
/* 角色行:书目式小标 */
|
|
231
213
|
.msg-meta {
|
|
@@ -244,7 +226,8 @@ body,
|
|
|
244
226
|
margin-left: -6px;
|
|
245
227
|
}
|
|
246
228
|
|
|
247
|
-
/*
|
|
229
|
+
/* 正文块上的单一复制键去重:卡头现在只有自己的复制键,消息级复制
|
|
230
|
+
(纯文本 / Markdown / 长图三件套)统一在底部 .msg-actions 行里。 */
|
|
248
231
|
.msg-text-copy {
|
|
249
232
|
display: none !important;
|
|
250
233
|
}
|
|
@@ -481,6 +464,12 @@ div.chead[role="button"]:hover,
|
|
|
481
464
|
border-bottom: 1px dotted var(--border-soft) !important;
|
|
482
465
|
border-radius: 0 !important;
|
|
483
466
|
box-shadow: none !important;
|
|
467
|
+
padding-left: 0 !important;
|
|
468
|
+
padding-right: 0 !important;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.msg-collapsed-action::before {
|
|
472
|
+
margin-left: 0 !important;
|
|
484
473
|
}
|
|
485
474
|
|
|
486
475
|
.msg-error {
|
|
@@ -499,7 +488,7 @@ div.chead[role="button"]:hover,
|
|
|
499
488
|
|
|
500
489
|
.inputbox {
|
|
501
490
|
background: var(--inputbox-bg) !important;
|
|
502
|
-
border:
|
|
491
|
+
border: none !important;
|
|
503
492
|
border-radius: 3px !important;
|
|
504
493
|
box-shadow: inset 3px 0 0 0 rgba(191, 59, 31, 0.35) !important;
|
|
505
494
|
transition:
|
|
@@ -508,14 +497,14 @@ div.chead[role="button"]:hover,
|
|
|
508
497
|
}
|
|
509
498
|
|
|
510
499
|
.inputbox:focus-within {
|
|
511
|
-
border
|
|
500
|
+
border: none !important;
|
|
512
501
|
box-shadow: inset 3px 0 0 0 var(--accent) !important;
|
|
513
502
|
}
|
|
514
503
|
|
|
515
504
|
.btn.send {
|
|
516
505
|
background: var(--send-blue) !important;
|
|
517
506
|
color: #f6f2e9 !important;
|
|
518
|
-
border-radius:
|
|
507
|
+
border-radius: 50% !important;
|
|
519
508
|
box-shadow: none !important;
|
|
520
509
|
font-weight: 600;
|
|
521
510
|
}
|
|
@@ -527,7 +516,54 @@ div.chead[role="button"]:hover,
|
|
|
527
516
|
.btn.stop {
|
|
528
517
|
background: var(--stop-red) !important;
|
|
529
518
|
color: #f6f2e9 !important;
|
|
519
|
+
border-radius: 50% !important;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/* 输入框底部模型选择、思考强度、预设等按钮:去边框、透明底,hover 现 hairline */
|
|
523
|
+
.composer-tools {
|
|
524
|
+
border-top: none !important;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
.composer-tools .chip,
|
|
528
|
+
.composer-tools .capsule-segment {
|
|
529
|
+
font-size: 13px !important;
|
|
530
|
+
padding: 4px 10px !important;
|
|
531
|
+
gap: 6px !important;
|
|
532
|
+
background: transparent !important;
|
|
533
|
+
border: 1px solid transparent !important;
|
|
534
|
+
box-shadow: none !important;
|
|
530
535
|
border-radius: 3px !important;
|
|
536
|
+
transition:
|
|
537
|
+
border-color 0.15s ease,
|
|
538
|
+
background 0.15s ease !important;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
.composer-tools .model-thinking-capsule {
|
|
542
|
+
background: transparent !important;
|
|
543
|
+
border: none !important;
|
|
544
|
+
box-shadow: none !important;
|
|
545
|
+
padding: 0 !important;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
.composer-tools .model-thinking-capsule .capsule-divider {
|
|
549
|
+
display: none !important;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
.composer-tools .chip:hover,
|
|
553
|
+
.composer-tools .capsule-segment:hover {
|
|
554
|
+
border-color: var(--border) !important;
|
|
555
|
+
background: transparent !important;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
.composer-tools-left {
|
|
559
|
+
gap: 6px !important;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
.composer-tools .chip-model,
|
|
563
|
+
.composer-tools .chip-sub,
|
|
564
|
+
.composer-tools .capsule-model-name,
|
|
565
|
+
.composer-tools .capsule-thinking-text {
|
|
566
|
+
font-size: 12.5px !important;
|
|
531
567
|
}
|
|
532
568
|
|
|
533
569
|
/* ---- 9. 底栏 / 状态栏:hairline 收边 ---- */
|
|
@@ -659,8 +695,7 @@ div.chead[role="button"]:hover,
|
|
|
659
695
|
0 16px 40px -12px rgba(38, 34, 24, 0.25) !important;
|
|
660
696
|
}
|
|
661
697
|
|
|
662
|
-
.dd-menu
|
|
663
|
-
.dropdown {
|
|
698
|
+
.dd-menu {
|
|
664
699
|
background: var(--menu-bg) !important;
|
|
665
700
|
border: 1px solid var(--border) !important;
|
|
666
701
|
border-radius: 4px !important;
|
|
@@ -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-oI7UKOqJ.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};
|