pi-web-ui 0.96.0 → 0.96.1
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 +22 -1
- package/dist/server/index.js +44 -19
- package/dist/server/plugin-installer.js +30 -0
- package/dist/server/settings-service.js +2 -2
- package/package.json +1 -1
- package/themes/zhupi-dark.css +74 -14
- package/themes/zhupi.css +74 -14
- package/web/dist/assets/{TerminalPanel-DDChcYOh.js → TerminalPanel-O3GyFIGG.js} +1 -1
- package/web/dist/assets/index-B7Zp23Vc.css +1 -0
- package/web/dist/assets/{index-Do9RgJC3.js → index-ByfoaDwp.js} +21 -21
- package/web/dist/index.html +2 -2
- package/web/dist/assets/index-DryOsILO.css +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,26 @@
|
|
|
10
10
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
|
+
## [0.96.1] — 2026-09-26
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **插件安装审批弹窗层级提升与进度反馈补齐(#382)** —— 将能力与目录访问审批弹窗提升至高于设置弹窗的顶层浮层(`z-index: 350`),无需关闭设置页;修复「记住并允许」未持久化问题,授权写入 `plugin-permissions.json` 并在再次安装时直接放行;点击安装即刻反馈等待确认/执行中状态,已安装列表补挂进度条,关闭弹窗后主界面提供全局进度指示。
|
|
18
|
+
- **顶栏全部居中对齐时偏右修复** —— 修复当顶栏条目全部设为居中时,因右侧缺失占位弹簧(`tb-spacer`)导致条目被左侧单个弹簧推向最右侧的问题,确保中间条目精准水平居中。
|
|
19
|
+
- **设置保存回执时序修复** —— 确保 `set_settings` 在触发 `needsReload` 重启前先行持久化并推送状态回执。
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- **朱批与朱批·夜主题界面精细化** —— 顶栏控件全面素色化(透明底、无边框,悬停现 hairline);消息折叠条内边距清零,折叠指示三角光学对齐左侧墨线;输入框边框微调,发送/停止按钮微调为圆形;输入框底部模型选择、思考强度、预设等按钮去边框透明底,悬停显示细边框。
|
|
24
|
+
- **输入框与顶栏控件排版微调** —— 顶栏 chip 统一幽灵化;输入框底部模型与思考强度 chip 尺寸微调收紧;思考强度 Chip 文案精简为 `{level}`,保留 tooltip 说明;预设选择器图标优化为 `FiSliders`。
|
|
25
|
+
|
|
26
|
+
<!-- auto-i18n:start -->
|
|
27
|
+
### i18n
|
|
28
|
+
|
|
29
|
+
- 前端中文变更(1):`thinkingChip`
|
|
30
|
+
- 前端英文变更(1):`thinkingChip`
|
|
31
|
+
<!-- auto-i18n:end -->
|
|
32
|
+
|
|
13
33
|
## [0.96.0] — 2026-09-26
|
|
14
34
|
|
|
15
35
|
### Added
|
|
@@ -1293,7 +1313,8 @@ when?, children?}`,也收 `topbar` / `settings` 这类简写别名);宿主
|
|
|
1293
1313
|
- 0.35.1(2026-08-27):编辑重问保留附件(#18)+ 全窗口拖放(#19)。
|
|
1294
1314
|
- 0.29.0(2026-08-23):全局搜索弹窗(Ctrl+K)+ 消息列表惰性窗口化。
|
|
1295
1315
|
|
|
1296
|
-
[Unreleased]: https://github.com/xing-shuyin/pi-web-ui/compare/v0.96.
|
|
1316
|
+
[Unreleased]: https://github.com/xing-shuyin/pi-web-ui/compare/v0.96.1...main
|
|
1317
|
+
[0.96.1]: https://github.com/xing-shuyin/pi-web-ui/releases/tag/v0.96.1
|
|
1297
1318
|
[0.96.0]: https://github.com/xing-shuyin/pi-web-ui/releases/tag/v0.96.0
|
|
1298
1319
|
[0.95.0]: https://github.com/xing-shuyin/pi-web-ui/releases/tag/v0.95.0
|
|
1299
1320
|
[0.94.1]: https://github.com/xing-shuyin/pi-web-ui/releases/tag/v0.94.1
|
package/dist/server/index.js
CHANGED
|
@@ -48,7 +48,7 @@ import { validateClientId } from "./ws-client-id.js";
|
|
|
48
48
|
import { PendingCommandQueue } from "./ws-pending-queue.js";
|
|
49
49
|
import { installPack, isKnownPack, listPacks, loadServerStrings, readPackFile, removePack, unloadServerStrings, } from "./locales.js";
|
|
50
50
|
import { PluginManager, resolvePluginClientFile, } from "./plugins.js";
|
|
51
|
-
import { buildPluginJobArgs, inspectInstallSpec, PluginInstaller } from "./plugin-installer.js";
|
|
51
|
+
import { buildPluginJobArgs, confirmPluginInstall, inspectInstallSpec, PluginInstaller } from "./plugin-installer.js";
|
|
52
52
|
import { syncPluginCatalog } from "./plugin-catalog-sync.js";
|
|
53
53
|
import { McpBridge } from "./mcp-bridge.js";
|
|
54
54
|
import { createMcpHotReload } from "./mcp-hot-reload.js";
|
|
@@ -1129,22 +1129,14 @@ function findDomConsentById(id) {
|
|
|
1129
1129
|
}
|
|
1130
1130
|
// ---------------------------------------------------------------------------
|
|
1131
1131
|
// 插件安装的用户确认门(P0):plugin_catalog_sync 的 install:true 与 plugin_job
|
|
1132
|
-
// 的 install/update
|
|
1133
|
-
// 这两类消息,没有这道门就能借 WS 静默安装任意插件。复用 permissionRequester
|
|
1134
|
-
// 的同一条「plugin_permission_request 弹窗 + 120s 超时视为拒绝」管线 —— 不新增
|
|
1135
|
-
// 协议消息,将安装清单放在 reason 里展示;这里直接调 requester(不写授权表,
|
|
1136
|
-
// remember 标志被忽略)。拒绝 / 超时 / 未接弹窗设施(无头 DSH)一律 fail-closed。
|
|
1132
|
+
// 的 install/update 在真正动安装器之前必须拿到用户确认。
|
|
1137
1133
|
// ---------------------------------------------------------------------------
|
|
1138
|
-
async function
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
const ans = await ask("plugin-installer", {
|
|
1144
|
-
family: "net",
|
|
1145
|
-
reason: `安装确认:将安装/更新以下插件(id ← source):\n${list}\n拒绝或 120 秒未确认则不安装。`,
|
|
1134
|
+
async function confirmPluginInstallHelper(items) {
|
|
1135
|
+
return confirmPluginInstall(items, {
|
|
1136
|
+
permGrants: pluginMgr.permGrants,
|
|
1137
|
+
permissionRequester: pluginMgr.permissionRequester,
|
|
1138
|
+
onGrantsChanged: pushPluginPermissions,
|
|
1146
1139
|
});
|
|
1147
|
-
return ans.ok === true;
|
|
1148
1140
|
}
|
|
1149
1141
|
// 内置定时任务(issue #184):全局 <dataDir>/scheduler-tasks.json,TTL 与
|
|
1150
1142
|
// client-state 同级;Agent 工具建的任务优先唤醒发起对话(issue #193:
|
|
@@ -2174,15 +2166,35 @@ wss.on("connection", (ws) => {
|
|
|
2174
2166
|
// plugin_job;拒绝/超时直接回一条 done,让面板上的作业就地结束(不占
|
|
2175
2167
|
// 安装锁、不弹「失败」之外的噪音)。卸载不在本门范围内(由面板本身发起)。
|
|
2176
2168
|
if (jobAction === "install" || jobAction === "update") {
|
|
2177
|
-
|
|
2169
|
+
const alreadyGranted = pluginMgr.permGrants.has("plugin-installer", "net", { host: "github.com" });
|
|
2170
|
+
if (!alreadyGranted) {
|
|
2171
|
+
send({
|
|
2172
|
+
type: "plugin_job",
|
|
2173
|
+
jobId,
|
|
2174
|
+
action: jobAction,
|
|
2175
|
+
pluginId,
|
|
2176
|
+
phase: "start",
|
|
2177
|
+
});
|
|
2178
|
+
send({
|
|
2179
|
+
type: "plugin_job",
|
|
2180
|
+
jobId,
|
|
2181
|
+
action: jobAction,
|
|
2182
|
+
pluginId,
|
|
2183
|
+
phase: "log",
|
|
2184
|
+
line: jobLang() === "zh" ? "等待确认安装授权…" : "Waiting for install confirmation…",
|
|
2185
|
+
});
|
|
2186
|
+
}
|
|
2187
|
+
void confirmPluginInstallHelper([{ id: pluginId, source: String(msg.source ?? "") }])
|
|
2178
2188
|
.then((confirmed) => {
|
|
2179
2189
|
if (!confirmed) {
|
|
2180
|
-
jobDone(false,
|
|
2190
|
+
jobDone(false, jobLang() === "zh"
|
|
2191
|
+
? "用户未确认安装(拒绝或 120 秒超时)"
|
|
2192
|
+
: "Installation not confirmed (rejected or timed out after 120s)");
|
|
2181
2193
|
return;
|
|
2182
2194
|
}
|
|
2183
2195
|
startPluginJob();
|
|
2184
2196
|
})
|
|
2185
|
-
.catch(() => jobDone(false, "安装确认流程异常"));
|
|
2197
|
+
.catch(() => jobDone(false, jobLang() === "zh" ? "安装确认流程异常" : "Installation confirmation error"));
|
|
2186
2198
|
break;
|
|
2187
2199
|
}
|
|
2188
2200
|
startPluginJob();
|
|
@@ -2195,6 +2207,19 @@ wss.on("connection", (ws) => {
|
|
|
2195
2207
|
done: async (ok, info) => {
|
|
2196
2208
|
if (ok) {
|
|
2197
2209
|
await reloadPluginsAndPush(jobLang);
|
|
2210
|
+
const isZh = jobLang() === "zh";
|
|
2211
|
+
const actionLabel = jobAction === "uninstall"
|
|
2212
|
+
? isZh
|
|
2213
|
+
? "卸载"
|
|
2214
|
+
: "uninstalled"
|
|
2215
|
+
: jobAction === "update"
|
|
2216
|
+
? isZh
|
|
2217
|
+
? "更新"
|
|
2218
|
+
: "updated"
|
|
2219
|
+
: isZh
|
|
2220
|
+
? "安装"
|
|
2221
|
+
: "installed";
|
|
2222
|
+
cs?.emitNotice("info", `插件「${pluginId}」${actionLabel}完成`, `Plugin "${pluginId}" ${actionLabel} successfully`);
|
|
2198
2223
|
}
|
|
2199
2224
|
else if (info.error) {
|
|
2200
2225
|
cs?.emitNotice("error", `插件操作失败:${info.error}`, `Plugin operation failed: ${info.error}`);
|
|
@@ -2354,7 +2379,7 @@ wss.on("connection", (ws) => {
|
|
|
2354
2379
|
// 本地文件来源只允许工作区内(防任意路径文件探测 oracle)。
|
|
2355
2380
|
workspaceRoot: cs?.cwd ?? CWD,
|
|
2356
2381
|
// 安装确认门(P0):拒绝/超时只写目录不安装。
|
|
2357
|
-
confirmInstall: (items) =>
|
|
2382
|
+
confirmInstall: (items) => confirmPluginInstallHelper(items),
|
|
2358
2383
|
}).then((r) => {
|
|
2359
2384
|
if (r.installRefused) {
|
|
2360
2385
|
cs?.emitNotice("warning", "目录已同步,但安装未获用户确认(拒绝或超时),未安装任何插件", "Catalog synced, but installation was not confirmed (denied or timed out) — nothing was installed");
|
|
@@ -315,3 +315,33 @@ export class PluginInstaller {
|
|
|
315
315
|
this.currentJobId = null;
|
|
316
316
|
}
|
|
317
317
|
}
|
|
318
|
+
/**
|
|
319
|
+
* 插件安装的用户确认门(P0):plugin_catalog_sync 的 install:true 与 plugin_job
|
|
320
|
+
* 的 install/update 在真正动安装器之前必须拿到用户确认。
|
|
321
|
+
*
|
|
322
|
+
* 用户若选择「记住并允许」,授权存入 plugin-permissions.json(后续同类操作自动放行,
|
|
323
|
+
* 设置面板「能力授权」页可审计与撤销)。拒绝 / 超时 / 未接弹窗设施(无头 DSH)一律 fail-closed。
|
|
324
|
+
*/
|
|
325
|
+
export async function confirmPluginInstall(items, deps) {
|
|
326
|
+
if (deps.permGrants?.has("plugin-installer", "net", { host: "github.com" })) {
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
const ask = deps.permissionRequester;
|
|
330
|
+
if (!ask)
|
|
331
|
+
return false;
|
|
332
|
+
const list = items.map((x) => `${x.id} ← ${x.source}`).join("\n");
|
|
333
|
+
const ans = await ask("plugin-installer", {
|
|
334
|
+
family: "net",
|
|
335
|
+
hosts: ["github.com"],
|
|
336
|
+
reason: `安装确认:将安装/更新以下插件(id ← source):\n${list}\n拒绝或 120 秒未确认则不安装。`,
|
|
337
|
+
});
|
|
338
|
+
if (ans.ok && ans.remember && deps.permGrants) {
|
|
339
|
+
deps.permGrants.grant("plugin-installer", "net", {
|
|
340
|
+
hosts: ["github.com"],
|
|
341
|
+
reason: "插件安装/更新确认",
|
|
342
|
+
remember: true,
|
|
343
|
+
});
|
|
344
|
+
deps.onGrantsChanged?.();
|
|
345
|
+
}
|
|
346
|
+
return ans.ok === true;
|
|
347
|
+
}
|
|
@@ -573,11 +573,11 @@ export class SettingsService {
|
|
|
573
573
|
// 必须在 this.push() 前生效,保证 promptSnapshot 收集到的是最新的活跃工具集与完整提示词!
|
|
574
574
|
if (toolGatingChanged)
|
|
575
575
|
this.host.applyToolGating();
|
|
576
|
-
if (needsReload)
|
|
577
|
-
await this.applyRuntime();
|
|
578
576
|
this.host.stateStore.saveSettings(this.host.clientId, this.settings);
|
|
579
577
|
this.push();
|
|
580
578
|
this.host.flushSnapshot();
|
|
579
|
+
if (needsReload)
|
|
580
|
+
await this.applyRuntime();
|
|
581
581
|
}
|
|
582
582
|
/** Save the CURRENT settings as a named preset (overwrites if exists). */
|
|
583
583
|
async savePreset(name) {
|
package/package.json
CHANGED
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 挂在轨上。
|
|
@@ -469,6 +477,12 @@ div.chead[role="button"]:hover,
|
|
|
469
477
|
border-bottom: 1px dotted var(--border-soft) !important;
|
|
470
478
|
border-radius: 0 !important;
|
|
471
479
|
box-shadow: none !important;
|
|
480
|
+
padding-left: 0 !important;
|
|
481
|
+
padding-right: 0 !important;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
.msg-collapsed-action::before {
|
|
485
|
+
margin-left: 0 !important;
|
|
472
486
|
}
|
|
473
487
|
|
|
474
488
|
.msg-error {
|
|
@@ -487,7 +501,7 @@ div.chead[role="button"]:hover,
|
|
|
487
501
|
|
|
488
502
|
.inputbox {
|
|
489
503
|
background: var(--inputbox-bg) !important;
|
|
490
|
-
border:
|
|
504
|
+
border: none !important;
|
|
491
505
|
border-radius: 3px !important;
|
|
492
506
|
box-shadow: inset 3px 0 0 0 rgba(224, 90, 53, 0.4) !important;
|
|
493
507
|
transition:
|
|
@@ -496,14 +510,14 @@ div.chead[role="button"]:hover,
|
|
|
496
510
|
}
|
|
497
511
|
|
|
498
512
|
.inputbox:focus-within {
|
|
499
|
-
border
|
|
513
|
+
border: none !important;
|
|
500
514
|
box-shadow: inset 3px 0 0 0 var(--accent) !important;
|
|
501
515
|
}
|
|
502
516
|
|
|
503
517
|
.btn.send {
|
|
504
518
|
background: var(--send-blue) !important;
|
|
505
519
|
color: #131313 !important;
|
|
506
|
-
border-radius:
|
|
520
|
+
border-radius: 50% !important;
|
|
507
521
|
box-shadow: none !important;
|
|
508
522
|
font-weight: 600;
|
|
509
523
|
}
|
|
@@ -515,7 +529,54 @@ div.chead[role="button"]:hover,
|
|
|
515
529
|
.btn.stop {
|
|
516
530
|
background: var(--stop-red) !important;
|
|
517
531
|
color: #131313 !important;
|
|
532
|
+
border-radius: 50% !important;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/* 输入框底部模型选择、思考强度、预设等按钮:去边框、透明底,hover 现 hairline */
|
|
536
|
+
.composer-tools {
|
|
537
|
+
border-top: none !important;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
.composer-tools .chip,
|
|
541
|
+
.composer-tools .capsule-segment {
|
|
542
|
+
font-size: 13px !important;
|
|
543
|
+
padding: 4px 10px !important;
|
|
544
|
+
gap: 6px !important;
|
|
545
|
+
background: transparent !important;
|
|
546
|
+
border: 1px solid transparent !important;
|
|
547
|
+
box-shadow: none !important;
|
|
518
548
|
border-radius: 3px !important;
|
|
549
|
+
transition:
|
|
550
|
+
border-color 0.15s ease,
|
|
551
|
+
background 0.15s ease !important;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
.composer-tools .model-thinking-capsule {
|
|
555
|
+
background: transparent !important;
|
|
556
|
+
border: none !important;
|
|
557
|
+
box-shadow: none !important;
|
|
558
|
+
padding: 0 !important;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
.composer-tools .model-thinking-capsule .capsule-divider {
|
|
562
|
+
display: none !important;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
.composer-tools .chip:hover,
|
|
566
|
+
.composer-tools .capsule-segment:hover {
|
|
567
|
+
border-color: var(--border) !important;
|
|
568
|
+
background: transparent !important;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
.composer-tools-left {
|
|
572
|
+
gap: 6px !important;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
.composer-tools .chip-model,
|
|
576
|
+
.composer-tools .chip-sub,
|
|
577
|
+
.composer-tools .capsule-model-name,
|
|
578
|
+
.composer-tools .capsule-thinking-text {
|
|
579
|
+
font-size: 12.5px !important;
|
|
519
580
|
}
|
|
520
581
|
|
|
521
582
|
/* ---- 9. 底栏 / 状态栏:hairline 收边 ---- */
|
|
@@ -606,8 +667,7 @@ div.chead[role="button"]:hover,
|
|
|
606
667
|
0 16px 40px -12px rgba(0, 0, 0, 0.55) !important;
|
|
607
668
|
}
|
|
608
669
|
|
|
609
|
-
.dd-menu
|
|
610
|
-
.dropdown {
|
|
670
|
+
.dd-menu {
|
|
611
671
|
background: var(--menu-bg) !important;
|
|
612
672
|
border: 1px solid var(--border) !important;
|
|
613
673
|
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
|
* 像素线,历史虚下来、接到输入框变实,像「线活了起来」;消息正文整体
|
|
@@ -481,6 +489,12 @@ div.chead[role="button"]:hover,
|
|
|
481
489
|
border-bottom: 1px dotted var(--border-soft) !important;
|
|
482
490
|
border-radius: 0 !important;
|
|
483
491
|
box-shadow: none !important;
|
|
492
|
+
padding-left: 0 !important;
|
|
493
|
+
padding-right: 0 !important;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.msg-collapsed-action::before {
|
|
497
|
+
margin-left: 0 !important;
|
|
484
498
|
}
|
|
485
499
|
|
|
486
500
|
.msg-error {
|
|
@@ -499,7 +513,7 @@ div.chead[role="button"]:hover,
|
|
|
499
513
|
|
|
500
514
|
.inputbox {
|
|
501
515
|
background: var(--inputbox-bg) !important;
|
|
502
|
-
border:
|
|
516
|
+
border: none !important;
|
|
503
517
|
border-radius: 3px !important;
|
|
504
518
|
box-shadow: inset 3px 0 0 0 rgba(191, 59, 31, 0.35) !important;
|
|
505
519
|
transition:
|
|
@@ -508,14 +522,14 @@ div.chead[role="button"]:hover,
|
|
|
508
522
|
}
|
|
509
523
|
|
|
510
524
|
.inputbox:focus-within {
|
|
511
|
-
border
|
|
525
|
+
border: none !important;
|
|
512
526
|
box-shadow: inset 3px 0 0 0 var(--accent) !important;
|
|
513
527
|
}
|
|
514
528
|
|
|
515
529
|
.btn.send {
|
|
516
530
|
background: var(--send-blue) !important;
|
|
517
531
|
color: #f6f2e9 !important;
|
|
518
|
-
border-radius:
|
|
532
|
+
border-radius: 50% !important;
|
|
519
533
|
box-shadow: none !important;
|
|
520
534
|
font-weight: 600;
|
|
521
535
|
}
|
|
@@ -527,7 +541,54 @@ div.chead[role="button"]:hover,
|
|
|
527
541
|
.btn.stop {
|
|
528
542
|
background: var(--stop-red) !important;
|
|
529
543
|
color: #f6f2e9 !important;
|
|
544
|
+
border-radius: 50% !important;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/* 输入框底部模型选择、思考强度、预设等按钮:去边框、透明底,hover 现 hairline */
|
|
548
|
+
.composer-tools {
|
|
549
|
+
border-top: none !important;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
.composer-tools .chip,
|
|
553
|
+
.composer-tools .capsule-segment {
|
|
554
|
+
font-size: 13px !important;
|
|
555
|
+
padding: 4px 10px !important;
|
|
556
|
+
gap: 6px !important;
|
|
557
|
+
background: transparent !important;
|
|
558
|
+
border: 1px solid transparent !important;
|
|
559
|
+
box-shadow: none !important;
|
|
530
560
|
border-radius: 3px !important;
|
|
561
|
+
transition:
|
|
562
|
+
border-color 0.15s ease,
|
|
563
|
+
background 0.15s ease !important;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
.composer-tools .model-thinking-capsule {
|
|
567
|
+
background: transparent !important;
|
|
568
|
+
border: none !important;
|
|
569
|
+
box-shadow: none !important;
|
|
570
|
+
padding: 0 !important;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
.composer-tools .model-thinking-capsule .capsule-divider {
|
|
574
|
+
display: none !important;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
.composer-tools .chip:hover,
|
|
578
|
+
.composer-tools .capsule-segment:hover {
|
|
579
|
+
border-color: var(--border) !important;
|
|
580
|
+
background: transparent !important;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
.composer-tools-left {
|
|
584
|
+
gap: 6px !important;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
.composer-tools .chip-model,
|
|
588
|
+
.composer-tools .chip-sub,
|
|
589
|
+
.composer-tools .capsule-model-name,
|
|
590
|
+
.composer-tools .capsule-thinking-text {
|
|
591
|
+
font-size: 12.5px !important;
|
|
531
592
|
}
|
|
532
593
|
|
|
533
594
|
/* ---- 9. 底栏 / 状态栏:hairline 收边 ---- */
|
|
@@ -659,8 +720,7 @@ div.chead[role="button"]:hover,
|
|
|
659
720
|
0 16px 40px -12px rgba(38, 34, 24, 0.25) !important;
|
|
660
721
|
}
|
|
661
722
|
|
|
662
|
-
.dd-menu
|
|
663
|
-
.dropdown {
|
|
723
|
+
.dd-menu {
|
|
664
724
|
background: var(--menu-bg) !important;
|
|
665
725
|
border: 1px solid var(--border) !important;
|
|
666
726
|
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-ByfoaDwp.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};
|