@crazyhappyone/dsh-tui 0.1.0-alpha.52 → 0.1.0-alpha.54
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 +27 -0
- package/README.md +3 -3
- package/README.zh.md +3 -3
- package/dist/index.js +183 -19
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.0-alpha.53 — 2026-10-22
|
|
4
|
+
|
|
5
|
+
Prerelease for DSH `0.2.1-rc.2`; npm channel: `next`.
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- String arrays such as `defaultInput` and `tags` stay on one editable settings row. Enter opens the value editor instead of one child row per element, and JSON or comma-separated input round-trips through the official settings patch. Arrays containing non-strings, including model lists and numeric arrays, remain browsable.
|
|
10
|
+
- Settings details stop measuring a long value once the visible row budget is full. The last visible row keeps the next source chunk and ends with `…`.
|
|
11
|
+
- Rapid settings selection updates coalesce to one notification per turn, and settled transcript rows reuse their projection while the block version and render scope stay unchanged.
|
|
12
|
+
- CI checks out the DSH tag named by `COMPATIBLE_DSH_VERSION`, activates that checkout's declared pnpm version, and runs the TUI tests from the mounted plugin sources.
|
|
13
|
+
|
|
14
|
+
### Verification and upgrade
|
|
15
|
+
|
|
16
|
+
- Focused verification passed 250 tests: settings navigation, settings interaction, content wrapping, settings usability and hierarchy layout, stream view, markdown rendering, and block rows. The full TUI suite, launcher tests, typecheck, and OS PTY checks were not rerun for this patch.
|
|
17
|
+
- Update with `npm install --global @crazyhappyone/dsh-tui@next`, then relaunch. Source-linked installations can use `/reload` once the new bundle is available. The official profile and configuration services remain authoritative; no migration is required.
|
|
18
|
+
|
|
19
|
+
### 中文变更说明
|
|
20
|
+
|
|
21
|
+
- `defaultInput`、`tags` 等纯字符串数组留在同一行编辑,Enter 打开值编辑器,不再为每个元素生成子行;JSON 和逗号输入都通过官方 settings patch 往返。包含非字符串的数组,包括模型列表和数字数组,仍可逐项浏览。
|
|
22
|
+
- 设置详情在可见行数用完后停止测量超长值,最后一行保留下一段内容并以 `…` 结束。
|
|
23
|
+
- 连续移动设置选中项时,每个事件循环只通知一次;已完成的聊天投影在块版本和渲染范围不变时复用。
|
|
24
|
+
- CI 按 `COMPATIBLE_DSH_VERSION` 检出对应 DSH tag,启用该检出声明的 pnpm 版本,再运行挂载后的 TUI 测试。
|
|
25
|
+
- 验证:上述聚焦测试 250 项通过。本补丁未重跑完整 TUI 套件、launcher 测试、类型检查和 OS PTY。
|
|
26
|
+
- 升级:`npm install --global @crazyhappyone/dsh-tui@next` 后重新启动;源码链接安装可在新 bundle 就绪后 `/reload`。继续使用官方配置服务,无需迁移。
|
|
27
|
+
|
|
28
|
+
[Full comparison](https://github.com/zhangyang-crazy-one/dsh-tui/compare/v0.1.0-alpha.52...v0.1.0-alpha.53)
|
|
29
|
+
|
|
3
30
|
## 0.1.0-alpha.52 — 2026-10-21
|
|
4
31
|
|
|
5
32
|
Prerelease for DSH `0.2.1-rc.2`; npm channel: `next`.
|
package/README.md
CHANGED
|
@@ -15,9 +15,9 @@ English | [中文](README.zh.md)
|
|
|
15
15
|
|
|
16
16
|
`@crazyhappyone/dsh-tui` is a terminal interface for DeepSeek Harness (dsh), shipped as a dsh **plugin bundle** plus a `dsh-tui` launcher. The package declares `dsh.bundle`, so dsh installs it into a profile like any other plugin; the launcher only automates that install and boots the profile. This repository owns the plugin sources (`tui/`, `tui-render/`). DSH continues to own agents, sessions, tools, persistence, providers, permissions, and profile assembly.
|
|
17
17
|
|
|
18
|
-
## Release 0.1.0-alpha.
|
|
18
|
+
## Release 0.1.0-alpha.53
|
|
19
19
|
|
|
20
|
-
This prerelease targets DSH `0.2.1-rc.2`.
|
|
20
|
+
This prerelease targets DSH `0.2.1-rc.2`. String arrays such as `defaultInput` now edit in place, settings details truncate long values at the visible row budget, and settled transcript rows reuse their projection while unchanged. See the [changelog](CHANGELOG.md#010-alpha53--2026-10-22) for the verification boundary.
|
|
21
21
|
|
|
22
22
|
```sh
|
|
23
23
|
npm install --global @crazyhappyone/dsh-tui@next
|
|
@@ -35,7 +35,7 @@ In `/settings`, Tab / Shift+Tab switches categories and restores each category's
|
|
|
35
35
|
|
|
36
36
|
## Table of Contents
|
|
37
37
|
|
|
38
|
-
- [Release and upgrade](#release-010-
|
|
38
|
+
- [Release and upgrade](#release-010-alpha53)
|
|
39
39
|
- [Changelog](CHANGELOG.md)
|
|
40
40
|
- [Terminal capabilities](#terminal-capabilities)
|
|
41
41
|
- [Screenshots](#screenshots)
|
package/README.zh.md
CHANGED
|
@@ -15,9 +15,9 @@ kind: "package-group"
|
|
|
15
15
|
|
|
16
16
|
`@crazyhappyone/dsh-tui` 是 DeepSeek Harness(dsh)的终端界面,以 dsh **插件 bundle** 加 `dsh-tui` launcher 的形式发布。包声明了 `dsh.bundle`,因此 dsh 会像安装其他插件一样把它装进 profile;launcher 只负责自动完成这一安装并启动 profile。本仓库拥有插件源码(`tui/`、`tui-render/`)。Agent、会话、工具、持久化、provider、权限与 profile 组装仍由 DSH 拥有。
|
|
17
17
|
|
|
18
|
-
## 0.1.0-alpha.
|
|
18
|
+
## 0.1.0-alpha.53 版本
|
|
19
19
|
|
|
20
|
-
本预发布版面向 DSH `0.2.1-rc.2
|
|
20
|
+
本预发布版面向 DSH `0.2.1-rc.2`。`defaultInput` 等纯字符串数组改为原位编辑,设置详情按可见行数截断长值,未变化的已完成聊天行复用投影。验证边界见 [changelog](CHANGELOG.md#010-alpha53--2026-10-22)。
|
|
21
21
|
|
|
22
22
|
```sh
|
|
23
23
|
npm install --global @crazyhappyone/dsh-tui@next
|
|
@@ -35,7 +35,7 @@ dsh-tui
|
|
|
35
35
|
|
|
36
36
|
## 目录
|
|
37
37
|
|
|
38
|
-
- [版本与升级](#010-
|
|
38
|
+
- [版本与升级](#010-alpha53-版本)
|
|
39
39
|
- [Changelog](CHANGELOG.md)
|
|
40
40
|
- [终端能力](#终端能力)
|
|
41
41
|
- [截图](#screenshots)
|
package/dist/index.js
CHANGED
|
@@ -39570,6 +39570,44 @@ function wrapDisplayLines(text4, maxCols) {
|
|
|
39570
39570
|
}
|
|
39571
39571
|
return out;
|
|
39572
39572
|
}
|
|
39573
|
+
function wrapDisplayLinesLimited(text4, maxCols, limit) {
|
|
39574
|
+
const bounded = Number.isFinite(limit) ? Math.max(0, Math.floor(limit)) : Number.POSITIVE_INFINITY;
|
|
39575
|
+
if (bounded === 0) return { lines: [], truncated: text4 !== "" };
|
|
39576
|
+
if (bounded === Number.POSITIVE_INFINITY) return { lines: wrapDisplayLines(text4, maxCols), truncated: false };
|
|
39577
|
+
if (maxCols <= 0) {
|
|
39578
|
+
const lines2 = text4.split("\n", bounded + 1);
|
|
39579
|
+
const truncated2 = lines2.length > bounded;
|
|
39580
|
+
if (truncated2) lines2.pop();
|
|
39581
|
+
return { lines: lines2, truncated: truncated2 };
|
|
39582
|
+
}
|
|
39583
|
+
const lines = [];
|
|
39584
|
+
let row = "";
|
|
39585
|
+
let columns = 0;
|
|
39586
|
+
const truncated = () => ({
|
|
39587
|
+
lines: [...lines, `${wcwidthSafeSlice(row, Math.max(0, maxCols - 1))}\u2026`],
|
|
39588
|
+
truncated: true
|
|
39589
|
+
});
|
|
39590
|
+
for (const { segment: segment2 } of GRAPHEME.segment(text4)) {
|
|
39591
|
+
if (segment2 === "\n") {
|
|
39592
|
+
if (lines.length + 1 === bounded) return truncated();
|
|
39593
|
+
lines.push(row);
|
|
39594
|
+
row = "";
|
|
39595
|
+
columns = 0;
|
|
39596
|
+
continue;
|
|
39597
|
+
}
|
|
39598
|
+
const width = displayWidth(segment2);
|
|
39599
|
+
if (columns + width > maxCols && row !== "") {
|
|
39600
|
+
if (lines.length + 1 === bounded) return truncated();
|
|
39601
|
+
lines.push(row);
|
|
39602
|
+
row = "";
|
|
39603
|
+
columns = 0;
|
|
39604
|
+
}
|
|
39605
|
+
row += segment2;
|
|
39606
|
+
columns += width;
|
|
39607
|
+
}
|
|
39608
|
+
lines.push(row);
|
|
39609
|
+
return { lines, truncated: false };
|
|
39610
|
+
}
|
|
39573
39611
|
|
|
39574
39612
|
// packages/tui/tui-render/src/terminal-capabilities.ts
|
|
39575
39613
|
var ESC_TIMEOUT_MS = 40;
|
|
@@ -56347,6 +56385,14 @@ function transcriptBlockId(row) {
|
|
|
56347
56385
|
function transcriptBlockVersion(row, revisions) {
|
|
56348
56386
|
return revisions.revision(row.kind === "compaction" ? row.divider : row.message);
|
|
56349
56387
|
}
|
|
56388
|
+
function settledProjectionSignature(row, revisions, options) {
|
|
56389
|
+
return [
|
|
56390
|
+
transcriptBlockVersion(row, revisions),
|
|
56391
|
+
options.hasSubsequent ? "gap" : "tail",
|
|
56392
|
+
options.isLatestAssistant ? "latest" : "",
|
|
56393
|
+
row.kind === "compaction" && options.expandedCompactionId === row.divider.compactionId ? "expanded" : ""
|
|
56394
|
+
].join("\0");
|
|
56395
|
+
}
|
|
56350
56396
|
function activeTurnVersion(turn, revisions) {
|
|
56351
56397
|
return revisions.revision(turn, [
|
|
56352
56398
|
turn.assistantText,
|
|
@@ -56880,6 +56926,9 @@ function StreamView({
|
|
|
56880
56926
|
const viewportRef = (0, import_react37.useRef)(null);
|
|
56881
56927
|
const layoutCache = (0, import_react37.useRef)(new TranscriptLayoutCache());
|
|
56882
56928
|
const projectorStates = (0, import_react37.useRef)(/* @__PURE__ */ new Map());
|
|
56929
|
+
const settledProjectionCache = (0, import_react37.useRef)(/* @__PURE__ */ new Map());
|
|
56930
|
+
const settledProjectionScope = (0, import_react37.useRef)("");
|
|
56931
|
+
const settledProjectionPresenters = (0, import_react37.useRef)(void 0);
|
|
56883
56932
|
const rebuildBlocks = (0, import_react37.useRef)(/* @__PURE__ */ new Map());
|
|
56884
56933
|
const lineStore = (0, import_react37.useRef)(void 0);
|
|
56885
56934
|
if (lineStore.current === void 0) {
|
|
@@ -57059,6 +57108,24 @@ function StreamView({
|
|
|
57059
57108
|
);
|
|
57060
57109
|
const hasActiveTurn = activeTurn !== void 0;
|
|
57061
57110
|
const settledEntryRows = (0, import_react37.useMemo)(() => {
|
|
57111
|
+
const projectionScope = [
|
|
57112
|
+
settledBlockRowsScope.scopeKey,
|
|
57113
|
+
settledAssistantBlockRowsScope.scopeKey,
|
|
57114
|
+
reasoningExpanded ? "r1" : "r0",
|
|
57115
|
+
toolCardsExpanded ? "t1" : "t0",
|
|
57116
|
+
mode,
|
|
57117
|
+
locale
|
|
57118
|
+
].join("\0");
|
|
57119
|
+
if (settledProjectionScope.current !== projectionScope) {
|
|
57120
|
+
settledProjectionCache.current = /* @__PURE__ */ new Map();
|
|
57121
|
+
settledProjectionScope.current = projectionScope;
|
|
57122
|
+
}
|
|
57123
|
+
if (settledProjectionPresenters.current !== presenters) {
|
|
57124
|
+
settledProjectionCache.current = /* @__PURE__ */ new Map();
|
|
57125
|
+
settledProjectionPresenters.current = presenters;
|
|
57126
|
+
}
|
|
57127
|
+
const previousCache = settledProjectionCache.current;
|
|
57128
|
+
const nextCache = /* @__PURE__ */ new Map();
|
|
57062
57129
|
const definitions = /* @__PURE__ */ new Map();
|
|
57063
57130
|
const batchPresenters = presenters === void 0 ? void 0 : { get(name2) {
|
|
57064
57131
|
if (!definitions.has(name2)) definitions.set(name2, presenters.get(name2));
|
|
@@ -57071,10 +57138,33 @@ function StreamView({
|
|
|
57071
57138
|
const toolSticky = /* @__PURE__ */ new Map();
|
|
57072
57139
|
const userSticky = /* @__PURE__ */ new Map();
|
|
57073
57140
|
const projectorCache = projectorStates.current;
|
|
57074
|
-
const project = makeBlockProjector({ toolRows, plainRows, locale }, storeBlocks);
|
|
57075
57141
|
const latestAssistant = hasActiveTurn ? void 0 : latestAssistantId(history, void 0);
|
|
57076
57142
|
for (const [index2, row] of transcript.entries()) {
|
|
57077
57143
|
const id = transcriptBlockId(row);
|
|
57144
|
+
const hasSubsequent = index2 < transcript.length - 1 || hasActiveTurn;
|
|
57145
|
+
const cached2 = previousCache.get(id);
|
|
57146
|
+
const signature = settledProjectionSignature(row, revisions, {
|
|
57147
|
+
hasSubsequent,
|
|
57148
|
+
isLatestAssistant: row.kind === "message" && row.message.id === latestAssistant,
|
|
57149
|
+
expandedCompactionId
|
|
57150
|
+
});
|
|
57151
|
+
if (cached2?.signature === signature) {
|
|
57152
|
+
map2.set(id, cached2.lines);
|
|
57153
|
+
if (cached2.textRanges !== void 0) textRanges.set(id, cached2.textRanges);
|
|
57154
|
+
if (cached2.reasoningSticky !== void 0) reasoningSticky.set(id, cached2.reasoningSticky);
|
|
57155
|
+
if (cached2.toolSticky !== void 0) toolSticky.set(id, [...cached2.toolSticky]);
|
|
57156
|
+
if (cached2.userSticky !== void 0) userSticky.set(id, cached2.userSticky);
|
|
57157
|
+
for (const block of cached2.storeBlocks) storeBlocks.set(block.entry.id, block);
|
|
57158
|
+
nextCache.set(id, cached2);
|
|
57159
|
+
continue;
|
|
57160
|
+
}
|
|
57161
|
+
const rowBlocks = /* @__PURE__ */ new Map();
|
|
57162
|
+
const project = makeBlockProjector({ toolRows, plainRows, locale }, rowBlocks);
|
|
57163
|
+
const collectBlocks = () => {
|
|
57164
|
+
const blocks = [...rowBlocks.values()];
|
|
57165
|
+
for (const block of blocks) storeBlocks.set(block.entry.id, block);
|
|
57166
|
+
return blocks;
|
|
57167
|
+
};
|
|
57078
57168
|
if (row.kind === "compaction") {
|
|
57079
57169
|
const meta3 = {
|
|
57080
57170
|
compactionSummary: row.divider.summary,
|
|
@@ -57089,10 +57179,18 @@ function StreamView({
|
|
|
57089
57179
|
meta: meta3
|
|
57090
57180
|
}, settledBlockRowsScope, void 0, false);
|
|
57091
57181
|
map2.set(id, projection.lines);
|
|
57182
|
+
nextCache.set(id, {
|
|
57183
|
+
signature,
|
|
57184
|
+
lines: projection.lines,
|
|
57185
|
+
textRanges: void 0,
|
|
57186
|
+
storeBlocks: collectBlocks(),
|
|
57187
|
+
reasoningSticky: void 0,
|
|
57188
|
+
toolSticky: void 0,
|
|
57189
|
+
userSticky: void 0
|
|
57190
|
+
});
|
|
57092
57191
|
continue;
|
|
57093
57192
|
}
|
|
57094
57193
|
if (row.message.kind === "user") {
|
|
57095
|
-
const hasSubsequent = index2 < transcript.length - 1 || hasActiveTurn;
|
|
57096
57194
|
const projection = project(id, {
|
|
57097
57195
|
id,
|
|
57098
57196
|
kind: "user",
|
|
@@ -57100,7 +57198,17 @@ function StreamView({
|
|
|
57100
57198
|
...hasSubsequent ? { meta: { userMessageGap: true } } : {}
|
|
57101
57199
|
}, settledBlockRowsScope, void 0, false);
|
|
57102
57200
|
map2.set(id, projection.lines);
|
|
57103
|
-
|
|
57201
|
+
const sticky = { start: 0, end: projection.lines.length, text: row.message.text };
|
|
57202
|
+
userSticky.set(id, sticky);
|
|
57203
|
+
nextCache.set(id, {
|
|
57204
|
+
signature,
|
|
57205
|
+
lines: projection.lines,
|
|
57206
|
+
textRanges: void 0,
|
|
57207
|
+
storeBlocks: collectBlocks(),
|
|
57208
|
+
reasoningSticky: void 0,
|
|
57209
|
+
toolSticky: void 0,
|
|
57210
|
+
userSticky: sticky
|
|
57211
|
+
});
|
|
57104
57212
|
continue;
|
|
57105
57213
|
}
|
|
57106
57214
|
const parts = displayedParts(
|
|
@@ -57204,9 +57312,20 @@ function StreamView({
|
|
|
57204
57312
|
meta: tailMeta
|
|
57205
57313
|
}, settledBlockRowsScope, void 0, false).lines);
|
|
57206
57314
|
}
|
|
57207
|
-
|
|
57315
|
+
const built = rows2.build();
|
|
57316
|
+
map2.set(id, built);
|
|
57208
57317
|
if (ranges.length > 0) textRanges.set(id, ranges);
|
|
57318
|
+
nextCache.set(id, {
|
|
57319
|
+
signature,
|
|
57320
|
+
lines: built,
|
|
57321
|
+
textRanges: ranges.length > 0 ? ranges : void 0,
|
|
57322
|
+
storeBlocks: collectBlocks(),
|
|
57323
|
+
reasoningSticky: reasoningSticky.get(id),
|
|
57324
|
+
toolSticky: toolSticky.get(id),
|
|
57325
|
+
userSticky: void 0
|
|
57326
|
+
});
|
|
57209
57327
|
}
|
|
57328
|
+
settledProjectionCache.current = nextCache;
|
|
57210
57329
|
return { rows: map2, textRanges, storeBlocks, reasoningSticky, toolSticky, userSticky };
|
|
57211
57330
|
}, [
|
|
57212
57331
|
contentWidth,
|
|
@@ -58994,7 +59113,8 @@ var SETTINGS_GROUP_LABELS = {
|
|
|
58994
59113
|
var import_jsx_runtime15 = __toESM(require_jsx_runtime(), 1);
|
|
58995
59114
|
function fit(text4, width) {
|
|
58996
59115
|
const safe = escapeContent(text4).replace(/\n/gu, " \u21B5 ");
|
|
58997
|
-
|
|
59116
|
+
const prefix = wcwidthSafeSlice(safe, width);
|
|
59117
|
+
return prefix.length === safe.length ? safe : `${wcwidthSafeSlice(prefix, Math.max(0, width - 1))}\u2026`;
|
|
58998
59118
|
}
|
|
58999
59119
|
function canToggleSetting(row) {
|
|
59000
59120
|
return row !== void 0 && row.navigable !== true && row.editable !== false && (row.value === "true" || row.value === "false" || row.namespace === "tui" && ["brandAnimation", "locale", "theme", "colorTier"].includes(row.field));
|
|
@@ -59068,14 +59188,16 @@ function SettingsBrowser(props) {
|
|
|
59068
59188
|
const detailBudget = compact || active === void 0 ? 0 : Math.min(6, Math.max(0, space - Math.min(3, Math.max(1, rows.length))));
|
|
59069
59189
|
if (active !== void 0 && detailBudget > 0) {
|
|
59070
59190
|
const source = filtered ? `${active.namespace} \xB7 ${active.field}` : fieldLabel(active, locale);
|
|
59071
|
-
|
|
59191
|
+
const pushBounded = (text4, limit) => {
|
|
59192
|
+
if (limit <= 0 || detail.length >= detailBudget) return;
|
|
59193
|
+
const room = Math.min(limit, detailBudget - detail.length);
|
|
59194
|
+
const wrapped = wrapDisplayLinesLimited(escapeContent(text4), width, room);
|
|
59195
|
+
detail.push(...wrapped.lines);
|
|
59196
|
+
};
|
|
59197
|
+
pushBounded(`\u5F53\u524D\u9879: ${source}`, filtered ? 2 : 1);
|
|
59072
59198
|
const value = active.value === "" ? "\u2014" : active.value;
|
|
59073
|
-
|
|
59074
|
-
if (active.description !== void 0)
|
|
59075
|
-
if (detail.length > detailBudget) {
|
|
59076
|
-
detail.length = detailBudget;
|
|
59077
|
-
detail[detail.length - 1] = `${wcwidthSafeSlice(detail[detail.length - 1], Math.max(0, width - 1))}\u2026`;
|
|
59078
|
-
}
|
|
59199
|
+
pushBounded(`${active.navigable === true ? "\u5305\u542B" : "\u5F53\u524D\u503C"}: ${value}`, detailBudget);
|
|
59200
|
+
if (active.description !== void 0) pushBounded(active.description, detailBudget);
|
|
59079
59201
|
}
|
|
59080
59202
|
const size = Math.min(rows.length, Math.max(0, space - detail.length));
|
|
59081
59203
|
const start = Math.max(0, Math.min(rows.length - size, selected - Math.floor(size / 2)));
|
|
@@ -99586,7 +99708,19 @@ function parseSettingsFieldValue(namespace, field, current, draft, knownProvider
|
|
|
99586
99708
|
if (field === "defaultInput" || field.endsWith(".defaultInput")) {
|
|
99587
99709
|
const trimmed = draft.trim().toLowerCase();
|
|
99588
99710
|
if (trimmed === "") return ["text", "image"];
|
|
99589
|
-
const
|
|
99711
|
+
const json2 = trimmed.startsWith("[") ? trimmed : void 0;
|
|
99712
|
+
let parsed;
|
|
99713
|
+
if (json2 !== void 0) {
|
|
99714
|
+
try {
|
|
99715
|
+
parsed = JSON.parse(json2);
|
|
99716
|
+
} catch {
|
|
99717
|
+
throw new TypeError("JSON \u65E0\u6548");
|
|
99718
|
+
}
|
|
99719
|
+
if (!Array.isArray(parsed) || parsed.some((item) => typeof item !== "string")) {
|
|
99720
|
+
throw new TypeError('defaultInput \u9700\u8981\u5B57\u7B26\u4E32\u6570\u7EC4\uFF0C\u4F8B\u5982 ["text", "image"]');
|
|
99721
|
+
}
|
|
99722
|
+
}
|
|
99723
|
+
const tokens = parsed === void 0 ? trimmed.split(/[,\s]+/).filter((t2) => t2 !== "") : parsed;
|
|
99590
99724
|
const result = [];
|
|
99591
99725
|
for (const token of tokens) {
|
|
99592
99726
|
if (token === "text" || token === "\u6587\u672C") {
|
|
@@ -99633,6 +99767,10 @@ function childSchema(schema, key) {
|
|
|
99633
99767
|
const node2 = record2(schema);
|
|
99634
99768
|
return record2(node2.dict)[key] ?? record2(node2.properties)[key] ?? node2.inner ?? node2.items;
|
|
99635
99769
|
}
|
|
99770
|
+
function navigableValue(value) {
|
|
99771
|
+
if (Array.isArray(value)) return value.some((item) => typeof item !== "string");
|
|
99772
|
+
return value !== null && typeof value === "object";
|
|
99773
|
+
}
|
|
99636
99774
|
function summary(value) {
|
|
99637
99775
|
return Array.isArray(value) ? `${String(value.length)} \u9879` : value !== null && typeof value === "object" ? `${String(Object.keys(value).length)} \u4E2A\u5B57\u6BB5` : stringifySettingValue(value);
|
|
99638
99776
|
}
|
|
@@ -99671,7 +99809,7 @@ function settingsTreeFromDescribe(descriptors, credentials) {
|
|
|
99671
99809
|
const modelId = record2(value).id;
|
|
99672
99810
|
const identity = Array.isArray(parentValue) && typeof modelId === "string" && parentValue.filter((entry) => record2(entry).id === modelId).length === 1 ? `id:${modelId}` : key;
|
|
99673
99811
|
const id = `${parentId}:${JSON.stringify(identity)}`;
|
|
99674
|
-
const
|
|
99812
|
+
const navigable = navigableValue(value);
|
|
99675
99813
|
const meta3 = record2(record2(schema).meta);
|
|
99676
99814
|
const label = Array.isArray(parentValue) ? String(record2(value).id ?? record2(value).name ?? key) : key;
|
|
99677
99815
|
nodes.push({
|
|
@@ -99681,14 +99819,14 @@ function settingsTreeFromDescribe(descriptors, credentials) {
|
|
|
99681
99819
|
field,
|
|
99682
99820
|
sourcePath: path2,
|
|
99683
99821
|
label,
|
|
99684
|
-
value: path2.length === 2 && path2[0] === "providers" ? `${String(Array.isArray(record2(value).models) ? record2(value).models.length : 0)} \u6A21\u578B \xB7 ${String(record2(value).api ?? "\u9ED8\u8BA4\u534F\u8BAE")}` :
|
|
99822
|
+
value: path2.length === 2 && path2[0] === "providers" ? `${String(Array.isArray(record2(value).models) ? record2(value).models.length : 0)} \u6A21\u578B \xB7 ${String(record2(value).api ?? "\u9ED8\u8BA4\u534F\u8BAE")}` : navigable ? summary(value) : stringifySettingsFieldValue(namespace, field, value),
|
|
99685
99823
|
editValue: stringifySettingValue(value),
|
|
99686
|
-
...
|
|
99687
|
-
...
|
|
99824
|
+
...navigable && secrets.some((secret) => path2.every((key2, index2) => secret[index2] === key2)) ? { editable: false } : {},
|
|
99825
|
+
...navigable ? { navigable: true } : {},
|
|
99688
99826
|
...typeof meta3.description === "string" ? { description: meta3.description } : {},
|
|
99689
99827
|
...meta3.required === true ? { required: true } : {}
|
|
99690
99828
|
});
|
|
99691
|
-
if (
|
|
99829
|
+
if (navigable) {
|
|
99692
99830
|
for (const [child, next] of Object.entries(value)) walk(next, [...path2, child], id, childSchema(schema, child));
|
|
99693
99831
|
if (namespace === "llm-pi-ai" && field === "providers") {
|
|
99694
99832
|
nodes.push({ id: `${id}:add`, parentId: id, namespace, field, sourcePath: path2, label: "+ \u6DFB\u52A0 Provider", value: "\u76EE\u5F55\u540D / \u8868\u5355", editValue: PROVIDER_FORM_TEMPLATE });
|
|
@@ -100599,6 +100737,9 @@ var RuntimeController = class _RuntimeController {
|
|
|
100599
100737
|
settingsOpen = false;
|
|
100600
100738
|
settingsRows = [];
|
|
100601
100739
|
settingsSelectedIndex = 0;
|
|
100740
|
+
/** Selection changes inside one input burst share one settings notification. */
|
|
100741
|
+
settingsMovePending = false;
|
|
100742
|
+
settingsMoveEmit;
|
|
100602
100743
|
settingsNavigation = new SettingsNavigation([]);
|
|
100603
100744
|
settingsSearching = false;
|
|
100604
100745
|
settingsEditing = false;
|
|
@@ -103523,7 +103664,8 @@ ${line5}`.slice(-64e3);
|
|
|
103523
103664
|
Math.max(0, this.settingsSelectedIndex + action.delta)
|
|
103524
103665
|
);
|
|
103525
103666
|
this.settingsNavigation.selectedIndex = this.settingsSelectedIndex;
|
|
103526
|
-
this.
|
|
103667
|
+
this.settingsPaneSnapshot = void 0;
|
|
103668
|
+
this.scheduleSettingsSelectionEmit();
|
|
103527
103669
|
return;
|
|
103528
103670
|
case "settings-edit":
|
|
103529
103671
|
case "settings-edit-json":
|
|
@@ -106857,6 +106999,8 @@ ${row.hint ?? ""}` });
|
|
|
106857
106999
|
notifyListeners() {
|
|
106858
107000
|
this.emitLastRun = Date.now();
|
|
106859
107001
|
this.emitPending = false;
|
|
107002
|
+
this.settingsMovePending = false;
|
|
107003
|
+
this.settingsMoveEmit = void 0;
|
|
106860
107004
|
for (const listener of this.listeners) {
|
|
106861
107005
|
try {
|
|
106862
107006
|
listener();
|
|
@@ -106865,8 +107009,28 @@ ${row.hint ?? ""}` });
|
|
|
106865
107009
|
}
|
|
106866
107010
|
}
|
|
106867
107011
|
}
|
|
107012
|
+
/**
|
|
107013
|
+
* Publish only the settings selection. A wheel or key burst keeps the latest
|
|
107014
|
+
* index readable immediately and notifies subscribers once per turn.
|
|
107015
|
+
*/
|
|
107016
|
+
scheduleSettingsSelectionEmit() {
|
|
107017
|
+
if (this.closed) return;
|
|
107018
|
+
this.settingsMovePending = true;
|
|
107019
|
+
if (this.settingsMoveEmit !== void 0) return;
|
|
107020
|
+
const token = {};
|
|
107021
|
+
this.settingsMoveEmit = token;
|
|
107022
|
+
queueMicrotask(() => {
|
|
107023
|
+
if (this.settingsMoveEmit !== token || this.closed || !this.settingsMovePending) return;
|
|
107024
|
+
this.settingsMoveEmit = void 0;
|
|
107025
|
+
this.settingsMovePending = false;
|
|
107026
|
+
if (this.emitTimer !== void 0) this.emitPending = true;
|
|
107027
|
+
else this.notifyListeners();
|
|
107028
|
+
});
|
|
107029
|
+
}
|
|
106868
107030
|
emit() {
|
|
106869
107031
|
if (this.closed) return;
|
|
107032
|
+
this.settingsMovePending = false;
|
|
107033
|
+
this.settingsMoveEmit = void 0;
|
|
106870
107034
|
this.modelSnapshot = void 0;
|
|
106871
107035
|
this.sessionPaneSnapshot = void 0;
|
|
106872
107036
|
this.searchPaneSnapshot = void 0;
|