@crazyhappyone/dsh-tui 0.1.0-alpha.52 → 0.1.0-alpha.53
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 +199 -26
- 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
|
@@ -39537,38 +39537,91 @@ function padDisplayEnd(text4, width) {
|
|
|
39537
39537
|
return `${text4}${" ".repeat(width - cols)}`;
|
|
39538
39538
|
}
|
|
39539
39539
|
function wrapDisplayLines(text4, maxCols) {
|
|
39540
|
+
return wrapDisplayLinesLimited(text4, maxCols, Number.POSITIVE_INFINITY).lines;
|
|
39541
|
+
}
|
|
39542
|
+
function wrapDisplayLinesLimited(text4, maxCols, limit) {
|
|
39543
|
+
const bounded = Number.isFinite(limit) ? Math.max(0, Math.floor(limit)) : Number.POSITIVE_INFINITY;
|
|
39540
39544
|
const source = text4.split("\n");
|
|
39541
|
-
if (
|
|
39545
|
+
if (bounded === 0) return { lines: [], truncated: text4 !== "" };
|
|
39546
|
+
if (maxCols <= 0) {
|
|
39547
|
+
const lines = source.slice(0, bounded);
|
|
39548
|
+
return { lines, truncated: source.length > lines.length };
|
|
39549
|
+
}
|
|
39542
39550
|
const out = [];
|
|
39543
|
-
|
|
39551
|
+
const room = () => out.length < bounded;
|
|
39552
|
+
const mark2 = (line5) => {
|
|
39553
|
+
const marker = `${wcwidthSafeSlice(line5, Math.max(0, maxCols - 1))}\u2026`;
|
|
39554
|
+
if (out.length < bounded) out.push(marker);
|
|
39555
|
+
else out[out.length - 1] = marker;
|
|
39556
|
+
};
|
|
39557
|
+
for (let lineIndex = 0; lineIndex < source.length; lineIndex += 1) {
|
|
39558
|
+
const line5 = source[lineIndex] ?? "";
|
|
39559
|
+
const laterLine = lineIndex < source.length - 1;
|
|
39544
39560
|
if (line5 === "" || displayWidth(line5) <= maxCols) {
|
|
39561
|
+
if (!room()) {
|
|
39562
|
+
mark2("");
|
|
39563
|
+
return { lines: out, truncated: true };
|
|
39564
|
+
}
|
|
39565
|
+
if (out.length + 1 === bounded && laterLine) {
|
|
39566
|
+
mark2(line5);
|
|
39567
|
+
return { lines: out, truncated: true };
|
|
39568
|
+
}
|
|
39545
39569
|
out.push(line5);
|
|
39546
39570
|
continue;
|
|
39547
39571
|
}
|
|
39548
39572
|
if (/^[\x20-\x7e]*$/u.test(line5)) {
|
|
39549
39573
|
for (let i2 = 0; i2 < line5.length; i2 += maxCols) {
|
|
39550
|
-
|
|
39574
|
+
const piece = line5.slice(i2, i2 + maxCols);
|
|
39575
|
+
const continues = i2 + maxCols < line5.length || laterLine;
|
|
39576
|
+
if (!room()) {
|
|
39577
|
+
mark2(piece);
|
|
39578
|
+
return { lines: out, truncated: true };
|
|
39579
|
+
}
|
|
39580
|
+
if (out.length + 1 === bounded && continues) {
|
|
39581
|
+
mark2(piece);
|
|
39582
|
+
return { lines: out, truncated: true };
|
|
39583
|
+
}
|
|
39584
|
+
out.push(piece);
|
|
39551
39585
|
}
|
|
39552
39586
|
continue;
|
|
39553
39587
|
}
|
|
39554
39588
|
let curLine = "";
|
|
39555
39589
|
let curCols = 0;
|
|
39556
|
-
|
|
39590
|
+
const segments = [...GRAPHEME.segment(line5)];
|
|
39591
|
+
for (let segmentIndex = 0; segmentIndex < segments.length; segmentIndex += 1) {
|
|
39592
|
+
const segment2 = segments[segmentIndex]?.segment ?? "";
|
|
39557
39593
|
const width = displayWidth(segment2);
|
|
39558
|
-
if (curCols + width
|
|
39559
|
-
out.push(curLine);
|
|
39560
|
-
curLine = segment2;
|
|
39561
|
-
curCols = width;
|
|
39562
|
-
} else {
|
|
39594
|
+
if (curCols + width <= maxCols || curLine === "") {
|
|
39563
39595
|
curLine += segment2;
|
|
39564
39596
|
curCols += width;
|
|
39597
|
+
continue;
|
|
39598
|
+
}
|
|
39599
|
+
if (!room()) {
|
|
39600
|
+
mark2(curLine);
|
|
39601
|
+
return { lines: out, truncated: true };
|
|
39602
|
+
}
|
|
39603
|
+
const continues = segmentIndex < segments.length || laterLine;
|
|
39604
|
+
if (out.length + 1 === bounded && continues) {
|
|
39605
|
+
mark2(curLine);
|
|
39606
|
+
return { lines: out, truncated: true };
|
|
39565
39607
|
}
|
|
39608
|
+
out.push(curLine);
|
|
39609
|
+
curLine = segment2;
|
|
39610
|
+
curCols = width;
|
|
39566
39611
|
}
|
|
39567
39612
|
if (curLine !== "") {
|
|
39613
|
+
if (!room()) {
|
|
39614
|
+
mark2(curLine);
|
|
39615
|
+
return { lines: out, truncated: true };
|
|
39616
|
+
}
|
|
39617
|
+
if (out.length + 1 === bounded && laterLine) {
|
|
39618
|
+
mark2(curLine);
|
|
39619
|
+
return { lines: out, truncated: true };
|
|
39620
|
+
}
|
|
39568
39621
|
out.push(curLine);
|
|
39569
39622
|
}
|
|
39570
39623
|
}
|
|
39571
|
-
return out;
|
|
39624
|
+
return { lines: out, truncated: false };
|
|
39572
39625
|
}
|
|
39573
39626
|
|
|
39574
39627
|
// packages/tui/tui-render/src/terminal-capabilities.ts
|
|
@@ -56347,6 +56400,14 @@ function transcriptBlockId(row) {
|
|
|
56347
56400
|
function transcriptBlockVersion(row, revisions) {
|
|
56348
56401
|
return revisions.revision(row.kind === "compaction" ? row.divider : row.message);
|
|
56349
56402
|
}
|
|
56403
|
+
function settledProjectionSignature(row, revisions, options) {
|
|
56404
|
+
return [
|
|
56405
|
+
transcriptBlockVersion(row, revisions),
|
|
56406
|
+
options.hasSubsequent ? "gap" : "tail",
|
|
56407
|
+
options.isLatestAssistant ? "latest" : "",
|
|
56408
|
+
options.expandedCompactionId ?? ""
|
|
56409
|
+
].join("\0");
|
|
56410
|
+
}
|
|
56350
56411
|
function activeTurnVersion(turn, revisions) {
|
|
56351
56412
|
return revisions.revision(turn, [
|
|
56352
56413
|
turn.assistantText,
|
|
@@ -56880,6 +56941,9 @@ function StreamView({
|
|
|
56880
56941
|
const viewportRef = (0, import_react37.useRef)(null);
|
|
56881
56942
|
const layoutCache = (0, import_react37.useRef)(new TranscriptLayoutCache());
|
|
56882
56943
|
const projectorStates = (0, import_react37.useRef)(/* @__PURE__ */ new Map());
|
|
56944
|
+
const settledProjectionCache = (0, import_react37.useRef)(/* @__PURE__ */ new Map());
|
|
56945
|
+
const settledProjectionScope = (0, import_react37.useRef)("");
|
|
56946
|
+
const settledProjectionPresenters = (0, import_react37.useRef)(void 0);
|
|
56883
56947
|
const rebuildBlocks = (0, import_react37.useRef)(/* @__PURE__ */ new Map());
|
|
56884
56948
|
const lineStore = (0, import_react37.useRef)(void 0);
|
|
56885
56949
|
if (lineStore.current === void 0) {
|
|
@@ -57059,6 +57123,24 @@ function StreamView({
|
|
|
57059
57123
|
);
|
|
57060
57124
|
const hasActiveTurn = activeTurn !== void 0;
|
|
57061
57125
|
const settledEntryRows = (0, import_react37.useMemo)(() => {
|
|
57126
|
+
const projectionScope = [
|
|
57127
|
+
settledBlockRowsScope.scopeKey,
|
|
57128
|
+
settledAssistantBlockRowsScope.scopeKey,
|
|
57129
|
+
reasoningExpanded ? "r1" : "r0",
|
|
57130
|
+
toolCardsExpanded ? "t1" : "t0",
|
|
57131
|
+
mode,
|
|
57132
|
+
locale
|
|
57133
|
+
].join("\0");
|
|
57134
|
+
if (settledProjectionScope.current !== projectionScope) {
|
|
57135
|
+
settledProjectionCache.current = /* @__PURE__ */ new Map();
|
|
57136
|
+
settledProjectionScope.current = projectionScope;
|
|
57137
|
+
}
|
|
57138
|
+
if (settledProjectionPresenters.current !== presenters) {
|
|
57139
|
+
settledProjectionCache.current = /* @__PURE__ */ new Map();
|
|
57140
|
+
settledProjectionPresenters.current = presenters;
|
|
57141
|
+
}
|
|
57142
|
+
const previousCache = settledProjectionCache.current;
|
|
57143
|
+
const nextCache = /* @__PURE__ */ new Map();
|
|
57062
57144
|
const definitions = /* @__PURE__ */ new Map();
|
|
57063
57145
|
const batchPresenters = presenters === void 0 ? void 0 : { get(name2) {
|
|
57064
57146
|
if (!definitions.has(name2)) definitions.set(name2, presenters.get(name2));
|
|
@@ -57075,6 +57157,24 @@ function StreamView({
|
|
|
57075
57157
|
const latestAssistant = hasActiveTurn ? void 0 : latestAssistantId(history, void 0);
|
|
57076
57158
|
for (const [index2, row] of transcript.entries()) {
|
|
57077
57159
|
const id = transcriptBlockId(row);
|
|
57160
|
+
const hasSubsequent = index2 < transcript.length - 1 || hasActiveTurn;
|
|
57161
|
+
const cached2 = previousCache.get(id);
|
|
57162
|
+
const signature = settledProjectionSignature(row, revisions, {
|
|
57163
|
+
hasSubsequent,
|
|
57164
|
+
isLatestAssistant: row.kind === "message" && row.message.id === latestAssistant,
|
|
57165
|
+
expandedCompactionId
|
|
57166
|
+
});
|
|
57167
|
+
if (cached2?.signature === signature) {
|
|
57168
|
+
map2.set(id, cached2.lines);
|
|
57169
|
+
if (cached2.textRanges !== void 0) textRanges.set(id, cached2.textRanges);
|
|
57170
|
+
if (cached2.reasoningSticky !== void 0) reasoningSticky.set(id, cached2.reasoningSticky);
|
|
57171
|
+
if (cached2.toolSticky !== void 0) toolSticky.set(id, [...cached2.toolSticky]);
|
|
57172
|
+
if (cached2.userSticky !== void 0) userSticky.set(id, cached2.userSticky);
|
|
57173
|
+
for (const block of cached2.storeBlocks) storeBlocks.set(block.entry.id, block);
|
|
57174
|
+
nextCache.set(id, cached2);
|
|
57175
|
+
continue;
|
|
57176
|
+
}
|
|
57177
|
+
const blocksBefore = storeBlocks.size;
|
|
57078
57178
|
if (row.kind === "compaction") {
|
|
57079
57179
|
const meta3 = {
|
|
57080
57180
|
compactionSummary: row.divider.summary,
|
|
@@ -57089,10 +57189,18 @@ function StreamView({
|
|
|
57089
57189
|
meta: meta3
|
|
57090
57190
|
}, settledBlockRowsScope, void 0, false);
|
|
57091
57191
|
map2.set(id, projection.lines);
|
|
57192
|
+
nextCache.set(id, {
|
|
57193
|
+
signature,
|
|
57194
|
+
lines: projection.lines,
|
|
57195
|
+
textRanges: void 0,
|
|
57196
|
+
storeBlocks: [...storeBlocks.values()].slice(blocksBefore),
|
|
57197
|
+
reasoningSticky: void 0,
|
|
57198
|
+
toolSticky: void 0,
|
|
57199
|
+
userSticky: void 0
|
|
57200
|
+
});
|
|
57092
57201
|
continue;
|
|
57093
57202
|
}
|
|
57094
57203
|
if (row.message.kind === "user") {
|
|
57095
|
-
const hasSubsequent = index2 < transcript.length - 1 || hasActiveTurn;
|
|
57096
57204
|
const projection = project(id, {
|
|
57097
57205
|
id,
|
|
57098
57206
|
kind: "user",
|
|
@@ -57100,7 +57208,17 @@ function StreamView({
|
|
|
57100
57208
|
...hasSubsequent ? { meta: { userMessageGap: true } } : {}
|
|
57101
57209
|
}, settledBlockRowsScope, void 0, false);
|
|
57102
57210
|
map2.set(id, projection.lines);
|
|
57103
|
-
|
|
57211
|
+
const sticky = { start: 0, end: projection.lines.length, text: row.message.text };
|
|
57212
|
+
userSticky.set(id, sticky);
|
|
57213
|
+
nextCache.set(id, {
|
|
57214
|
+
signature,
|
|
57215
|
+
lines: projection.lines,
|
|
57216
|
+
textRanges: void 0,
|
|
57217
|
+
storeBlocks: [...storeBlocks.values()].slice(blocksBefore),
|
|
57218
|
+
reasoningSticky: void 0,
|
|
57219
|
+
toolSticky: void 0,
|
|
57220
|
+
userSticky: sticky
|
|
57221
|
+
});
|
|
57104
57222
|
continue;
|
|
57105
57223
|
}
|
|
57106
57224
|
const parts = displayedParts(
|
|
@@ -57204,9 +57322,20 @@ function StreamView({
|
|
|
57204
57322
|
meta: tailMeta
|
|
57205
57323
|
}, settledBlockRowsScope, void 0, false).lines);
|
|
57206
57324
|
}
|
|
57207
|
-
|
|
57325
|
+
const built = rows2.build();
|
|
57326
|
+
map2.set(id, built);
|
|
57208
57327
|
if (ranges.length > 0) textRanges.set(id, ranges);
|
|
57328
|
+
nextCache.set(id, {
|
|
57329
|
+
signature,
|
|
57330
|
+
lines: built,
|
|
57331
|
+
textRanges: ranges.length > 0 ? ranges : void 0,
|
|
57332
|
+
storeBlocks: [...storeBlocks.values()].slice(blocksBefore),
|
|
57333
|
+
reasoningSticky: reasoningSticky.get(id),
|
|
57334
|
+
toolSticky: toolSticky.get(id),
|
|
57335
|
+
userSticky: void 0
|
|
57336
|
+
});
|
|
57209
57337
|
}
|
|
57338
|
+
settledProjectionCache.current = nextCache;
|
|
57210
57339
|
return { rows: map2, textRanges, storeBlocks, reasoningSticky, toolSticky, userSticky };
|
|
57211
57340
|
}, [
|
|
57212
57341
|
contentWidth,
|
|
@@ -59068,14 +59197,16 @@ function SettingsBrowser(props) {
|
|
|
59068
59197
|
const detailBudget = compact || active === void 0 ? 0 : Math.min(6, Math.max(0, space - Math.min(3, Math.max(1, rows.length))));
|
|
59069
59198
|
if (active !== void 0 && detailBudget > 0) {
|
|
59070
59199
|
const source = filtered ? `${active.namespace} \xB7 ${active.field}` : fieldLabel(active, locale);
|
|
59071
|
-
|
|
59200
|
+
const pushBounded = (text4, limit) => {
|
|
59201
|
+
if (limit <= 0 || detail.length >= detailBudget) return;
|
|
59202
|
+
const room = Math.min(limit, detailBudget - detail.length);
|
|
59203
|
+
const wrapped = wrapDisplayLinesLimited(escapeContent(text4), width, room);
|
|
59204
|
+
detail.push(...wrapped.lines);
|
|
59205
|
+
};
|
|
59206
|
+
pushBounded(`\u5F53\u524D\u9879: ${source}`, filtered ? 2 : 1);
|
|
59072
59207
|
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
|
-
}
|
|
59208
|
+
pushBounded(`${active.navigable === true ? "\u5305\u542B" : "\u5F53\u524D\u503C"}: ${value}`, detailBudget);
|
|
59209
|
+
if (active.description !== void 0) pushBounded(active.description, detailBudget);
|
|
59079
59210
|
}
|
|
59080
59211
|
const size = Math.min(rows.length, Math.max(0, space - detail.length));
|
|
59081
59212
|
const start = Math.max(0, Math.min(rows.length - size, selected - Math.floor(size / 2)));
|
|
@@ -99586,7 +99717,19 @@ function parseSettingsFieldValue(namespace, field, current, draft, knownProvider
|
|
|
99586
99717
|
if (field === "defaultInput" || field.endsWith(".defaultInput")) {
|
|
99587
99718
|
const trimmed = draft.trim().toLowerCase();
|
|
99588
99719
|
if (trimmed === "") return ["text", "image"];
|
|
99589
|
-
const
|
|
99720
|
+
const json2 = trimmed.startsWith("[") ? trimmed : void 0;
|
|
99721
|
+
let parsed;
|
|
99722
|
+
if (json2 !== void 0) {
|
|
99723
|
+
try {
|
|
99724
|
+
parsed = JSON.parse(json2);
|
|
99725
|
+
} catch {
|
|
99726
|
+
throw new TypeError("JSON \u65E0\u6548");
|
|
99727
|
+
}
|
|
99728
|
+
if (!Array.isArray(parsed) || parsed.some((item) => typeof item !== "string")) {
|
|
99729
|
+
throw new TypeError('defaultInput \u9700\u8981\u5B57\u7B26\u4E32\u6570\u7EC4\uFF0C\u4F8B\u5982 ["text", "image"]');
|
|
99730
|
+
}
|
|
99731
|
+
}
|
|
99732
|
+
const tokens = parsed === void 0 ? trimmed.split(/[,\s]+/).filter((t2) => t2 !== "") : parsed;
|
|
99590
99733
|
const result = [];
|
|
99591
99734
|
for (const token of tokens) {
|
|
99592
99735
|
if (token === "text" || token === "\u6587\u672C") {
|
|
@@ -99633,6 +99776,10 @@ function childSchema(schema, key) {
|
|
|
99633
99776
|
const node2 = record2(schema);
|
|
99634
99777
|
return record2(node2.dict)[key] ?? record2(node2.properties)[key] ?? node2.inner ?? node2.items;
|
|
99635
99778
|
}
|
|
99779
|
+
function navigableValue(value) {
|
|
99780
|
+
if (Array.isArray(value)) return value.some((item) => typeof item !== "string");
|
|
99781
|
+
return value !== null && typeof value === "object";
|
|
99782
|
+
}
|
|
99636
99783
|
function summary(value) {
|
|
99637
99784
|
return Array.isArray(value) ? `${String(value.length)} \u9879` : value !== null && typeof value === "object" ? `${String(Object.keys(value).length)} \u4E2A\u5B57\u6BB5` : stringifySettingValue(value);
|
|
99638
99785
|
}
|
|
@@ -99672,6 +99819,7 @@ function settingsTreeFromDescribe(descriptors, credentials) {
|
|
|
99672
99819
|
const identity = Array.isArray(parentValue) && typeof modelId === "string" && parentValue.filter((entry) => record2(entry).id === modelId).length === 1 ? `id:${modelId}` : key;
|
|
99673
99820
|
const id = `${parentId}:${JSON.stringify(identity)}`;
|
|
99674
99821
|
const nested = value !== null && typeof value === "object";
|
|
99822
|
+
const navigable = navigableValue(value);
|
|
99675
99823
|
const meta3 = record2(record2(schema).meta);
|
|
99676
99824
|
const label = Array.isArray(parentValue) ? String(record2(value).id ?? record2(value).name ?? key) : key;
|
|
99677
99825
|
nodes.push({
|
|
@@ -99681,14 +99829,14 @@ function settingsTreeFromDescribe(descriptors, credentials) {
|
|
|
99681
99829
|
field,
|
|
99682
99830
|
sourcePath: path2,
|
|
99683
99831
|
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")}` :
|
|
99832
|
+
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
99833
|
editValue: stringifySettingValue(value),
|
|
99686
|
-
...
|
|
99687
|
-
...
|
|
99834
|
+
...navigable && secrets.some((secret) => path2.every((key2, index2) => secret[index2] === key2)) ? { editable: false } : {},
|
|
99835
|
+
...navigable ? { navigable: true } : {},
|
|
99688
99836
|
...typeof meta3.description === "string" ? { description: meta3.description } : {},
|
|
99689
99837
|
...meta3.required === true ? { required: true } : {}
|
|
99690
99838
|
});
|
|
99691
|
-
if (
|
|
99839
|
+
if (navigable) {
|
|
99692
99840
|
for (const [child, next] of Object.entries(value)) walk(next, [...path2, child], id, childSchema(schema, child));
|
|
99693
99841
|
if (namespace === "llm-pi-ai" && field === "providers") {
|
|
99694
99842
|
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 +100747,9 @@ var RuntimeController = class _RuntimeController {
|
|
|
100599
100747
|
settingsOpen = false;
|
|
100600
100748
|
settingsRows = [];
|
|
100601
100749
|
settingsSelectedIndex = 0;
|
|
100750
|
+
/** Selection changes inside one input burst share one settings notification. */
|
|
100751
|
+
settingsMovePending = false;
|
|
100752
|
+
settingsMoveEmit;
|
|
100602
100753
|
settingsNavigation = new SettingsNavigation([]);
|
|
100603
100754
|
settingsSearching = false;
|
|
100604
100755
|
settingsEditing = false;
|
|
@@ -103523,7 +103674,8 @@ ${line5}`.slice(-64e3);
|
|
|
103523
103674
|
Math.max(0, this.settingsSelectedIndex + action.delta)
|
|
103524
103675
|
);
|
|
103525
103676
|
this.settingsNavigation.selectedIndex = this.settingsSelectedIndex;
|
|
103526
|
-
this.
|
|
103677
|
+
this.settingsPaneSnapshot = void 0;
|
|
103678
|
+
this.scheduleSettingsSelectionEmit();
|
|
103527
103679
|
return;
|
|
103528
103680
|
case "settings-edit":
|
|
103529
103681
|
case "settings-edit-json":
|
|
@@ -106857,6 +107009,8 @@ ${row.hint ?? ""}` });
|
|
|
106857
107009
|
notifyListeners() {
|
|
106858
107010
|
this.emitLastRun = Date.now();
|
|
106859
107011
|
this.emitPending = false;
|
|
107012
|
+
this.settingsMovePending = false;
|
|
107013
|
+
this.settingsMoveEmit = void 0;
|
|
106860
107014
|
for (const listener of this.listeners) {
|
|
106861
107015
|
try {
|
|
106862
107016
|
listener();
|
|
@@ -106865,8 +107019,27 @@ ${row.hint ?? ""}` });
|
|
|
106865
107019
|
}
|
|
106866
107020
|
}
|
|
106867
107021
|
}
|
|
107022
|
+
/**
|
|
107023
|
+
* Publish only the settings selection. A wheel or key burst keeps the latest
|
|
107024
|
+
* index readable immediately and notifies subscribers once per turn.
|
|
107025
|
+
*/
|
|
107026
|
+
scheduleSettingsSelectionEmit() {
|
|
107027
|
+
if (this.closed) return;
|
|
107028
|
+
this.settingsMovePending = true;
|
|
107029
|
+
if (this.settingsMoveEmit !== void 0) return;
|
|
107030
|
+
const token = queueMicrotask(() => {
|
|
107031
|
+
if (this.settingsMoveEmit !== token || this.closed || !this.settingsMovePending) return;
|
|
107032
|
+
this.settingsMoveEmit = void 0;
|
|
107033
|
+
this.settingsMovePending = false;
|
|
107034
|
+
if (this.emitTimer !== void 0) this.emitPending = true;
|
|
107035
|
+
else this.notifyListeners();
|
|
107036
|
+
});
|
|
107037
|
+
this.settingsMoveEmit = token;
|
|
107038
|
+
}
|
|
106868
107039
|
emit() {
|
|
106869
107040
|
if (this.closed) return;
|
|
107041
|
+
this.settingsMovePending = false;
|
|
107042
|
+
this.settingsMoveEmit = void 0;
|
|
106870
107043
|
this.modelSnapshot = void 0;
|
|
106871
107044
|
this.sessionPaneSnapshot = void 0;
|
|
106872
107045
|
this.searchPaneSnapshot = void 0;
|