@aiwayds/dsh-tui-pi 0.8.3 → 0.9.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/README.md +44 -3
- package/README.zh.md +36 -3
- package/assets/fonts/LICENSE-Hack.txt +45 -0
- package/assets/fonts/LICENSE-NerdFonts.txt +126 -0
- package/assets/fonts/LICENSE-NotoSansSymbols.txt +93 -0
- package/assets/fonts/LICENSE-NotoSansSymbols2.txt +93 -0
- package/assets/fonts/dsh-tui-pi-nerd.ttf +0 -0
- package/assets/fonts-gen.mjs +219 -0
- package/assets/whale-gen.mjs +169 -0
- package/assets/whale-source.png +0 -0
- package/lib/agents.js +1 -1
- package/lib/agents.js.map +1 -1
- package/lib/dsh-events.d.ts +32 -3
- package/lib/dsh-events.js +14 -0
- package/lib/dsh-events.js.map +1 -1
- package/lib/font-detect.d.ts +66 -0
- package/lib/font-detect.js +124 -0
- package/lib/font-detect.js.map +1 -0
- package/lib/footer.d.ts +3 -2
- package/lib/footer.js +16 -8
- package/lib/footer.js.map +1 -1
- package/lib/icons.d.ts +53 -0
- package/lib/icons.js +78 -0
- package/lib/icons.js.map +1 -0
- package/lib/index.js +44 -13
- package/lib/index.js.map +1 -1
- package/lib/live-widgets.d.ts +16 -5
- package/lib/live-widgets.js +29 -8
- package/lib/live-widgets.js.map +1 -1
- package/lib/messages.js +2 -1
- package/lib/messages.js.map +1 -1
- package/lib/session.d.ts +99 -16
- package/lib/session.js +322 -64
- package/lib/session.js.map +1 -1
- package/lib/sessions.d.ts +15 -0
- package/lib/sessions.js +18 -3
- package/lib/sessions.js.map +1 -1
- package/lib/settings.js +2 -2
- package/lib/skills.d.ts +19 -3
- package/lib/skills.js +43 -10
- package/lib/skills.js.map +1 -1
- package/lib/subagent-policy.d.ts +11 -11
- package/lib/subagent-policy.js +17 -15
- package/lib/subagent-policy.js.map +1 -1
- package/lib/subagent-viewer.d.ts +20 -5
- package/lib/subagent-viewer.js +29 -15
- package/lib/subagent-viewer.js.map +1 -1
- package/lib/theme-settings.d.ts +25 -10
- package/lib/theme-settings.js +40 -14
- package/lib/theme-settings.js.map +1 -1
- package/lib/tokens.d.ts +38 -0
- package/lib/tokens.js +68 -0
- package/lib/tokens.js.map +1 -0
- package/package.json +2 -1
- package/scripts/install-font.mjs +329 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nerd Font detection for the icon-set self-adaptation — a
|
|
3
|
+
* zero-dependency probe that tells `src/icons.ts` whether the terminal can
|
|
4
|
+
* render the powerline PUA glyph U+E0B0 (and the other weak-terminal glyphs)
|
|
5
|
+
* before the `auto` icon-set mode picks between 'nerdfont' and 'plain'.
|
|
6
|
+
*
|
|
7
|
+
* Detection is per-platform and deliberately approximate:
|
|
8
|
+
*
|
|
9
|
+
* - Linux: `fc-list -q <name>` for each candidate family. fc-list exits 0
|
|
10
|
+
* when the family is installed (quiet mode prints nothing either way).
|
|
11
|
+
* When `fc-list` itself is missing (ENOENT — a fontconfig-less system),
|
|
12
|
+
* every probe fails and the probe reports "no Nerd Font".
|
|
13
|
+
* - macOS: scan `~/Library/Fonts` and `/Library/Fonts` and match file names
|
|
14
|
+
* against a Nerd/Powerline heuristic (the real Core Text registry is not
|
|
15
|
+
* reachable without Objective-C bindings — a file-name scan is the
|
|
16
|
+
* reasonable zero-dependency approximation; it also recognises the TUI's
|
|
17
|
+
* own bundled font `dsh-tui-pi-nerd.ttf`, so `node scripts/install-font.mjs`
|
|
18
|
+
* flipping the terminal to it is enough to flip `auto` → nerdfont).
|
|
19
|
+
* - Windows / anything else: false (no free probe; the conservative answer
|
|
20
|
+
* is the plain glyphs).
|
|
21
|
+
*
|
|
22
|
+
* The result is memoised at module level — the TUI probes once at startup and
|
|
23
|
+
* hot-applied 'auto' settings changes resolve against that same snapshot. All
|
|
24
|
+
* failure paths return `false`; the probe never throws.
|
|
25
|
+
*/
|
|
26
|
+
import { execFile as execFileCb } from 'node:child_process';
|
|
27
|
+
import { readdir as readdirCb } from 'node:fs';
|
|
28
|
+
import { homedir } from 'node:os';
|
|
29
|
+
import { join } from 'node:path';
|
|
30
|
+
import { promisify } from 'node:util';
|
|
31
|
+
const execFile = promisify(execFileCb);
|
|
32
|
+
const readdir = promisify(readdirCb);
|
|
33
|
+
/** Candidate font families probed with `fc-list -q` on Linux. */
|
|
34
|
+
export const NERD_FONT_CANDIDATES = [
|
|
35
|
+
'JetBrainsMono Nerd Font',
|
|
36
|
+
'Hack Nerd Font',
|
|
37
|
+
'MesloLGS NF',
|
|
38
|
+
'Cascadia Mono PL',
|
|
39
|
+
'PowerlineSymbols',
|
|
40
|
+
'0xProto Nerd Font',
|
|
41
|
+
];
|
|
42
|
+
/**
|
|
43
|
+
* macOS font-file-name heuristic for a Nerd/Powerline-capable font, incl. the
|
|
44
|
+
* TUI's own bundled subset (`dsh-tui-pi-nerd.ttf`). `MesloLGS` mirrors the
|
|
45
|
+
* Linux candidate list (MesloLGS NF ships as `MesloLGS NF Regular.ttf`).
|
|
46
|
+
*/
|
|
47
|
+
export const NERD_FONT_FILE_RE = /Nerd|Powerline|Cascadia.*PL|MesloLGS|dsh-tui-pi-nerd/i;
|
|
48
|
+
/** The real environment: this process's platform, home and filesystem. */
|
|
49
|
+
const realEnv = {
|
|
50
|
+
platform: process.platform,
|
|
51
|
+
home: homedir(),
|
|
52
|
+
readdir: (dir) => readdir(dir),
|
|
53
|
+
execFile: (file, args, options) => execFile(file, args, options),
|
|
54
|
+
};
|
|
55
|
+
/** Probe one platform with `fc-list -q`; `false` when fc-list is absent. */
|
|
56
|
+
async function probeLinux(env) {
|
|
57
|
+
for (const candidate of NERD_FONT_CANDIDATES) {
|
|
58
|
+
try {
|
|
59
|
+
// 2s cap: a broken fontconfig / stale NFS font dir must fall through to
|
|
60
|
+
// the plain glyphs instead of hanging the TUI startup.
|
|
61
|
+
await env.execFile('fc-list', ['-q', candidate], { timeout: 2000 });
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// fc-list missing (ENOENT), timed out, or the family not installed —
|
|
66
|
+
// try the next.
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
/** Probe macOS by scanning the two font directories for matching file names. */
|
|
72
|
+
async function probeDarwin(env) {
|
|
73
|
+
for (const dir of [join(env.home, 'Library', 'Fonts'), '/Library/Fonts']) {
|
|
74
|
+
let names;
|
|
75
|
+
try {
|
|
76
|
+
names = await env.readdir(dir);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
continue; // missing/unreadable directory — try the next
|
|
80
|
+
}
|
|
81
|
+
for (const name of names) {
|
|
82
|
+
if (NERD_FONT_FILE_RE.test(name))
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The full probe against an injected environment: Linux uses fc-list, macOS
|
|
90
|
+
* uses the font-directory scan, every other platform reports `false`. Never
|
|
91
|
+
* throws — every platform path is wrapped or falls through to `false`.
|
|
92
|
+
*/
|
|
93
|
+
export async function probeNerdFont(env) {
|
|
94
|
+
switch (env.platform) {
|
|
95
|
+
case 'linux':
|
|
96
|
+
return probeLinux(env);
|
|
97
|
+
case 'darwin':
|
|
98
|
+
return probeDarwin(env);
|
|
99
|
+
default:
|
|
100
|
+
// Windows and the rest: no free probe, conservative plain answer.
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** Module-level memo: the TUI probes once per process, never again. */
|
|
105
|
+
let cachedNerdFont;
|
|
106
|
+
/**
|
|
107
|
+
* Detect whether a Nerd/Powerline font is available, memoised. The first call
|
|
108
|
+
* (TUI startup, src/index.ts) runs the platform probe and caches the result;
|
|
109
|
+
* every later call — including a hot-applied 'auto' iconSet change — resolves
|
|
110
|
+
* against the same startup snapshot. Never throws.
|
|
111
|
+
*/
|
|
112
|
+
export function detectNerdFontAvailable(env = realEnv) {
|
|
113
|
+
if (cachedNerdFont !== undefined)
|
|
114
|
+
return Promise.resolve(cachedNerdFont);
|
|
115
|
+
return probeNerdFont(env).then((value) => {
|
|
116
|
+
cachedNerdFont = value;
|
|
117
|
+
return value;
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
/** Drop the memoised result — a test seam (deterministic probe tests). */
|
|
121
|
+
export function resetNerdFontCache() {
|
|
122
|
+
cachedNerdFont = undefined;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=font-detect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"font-detect.js","sourceRoot":"","sources":["../src/font-detect.ts"],"names":[],"mappings":"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"}
|
package/lib/footer.d.ts
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* Segments (left → right): fixed brand "dsh" · provider · model+thinking ·
|
|
5
5
|
* context usage · cache-hit rate · message count · tool count, then a
|
|
6
6
|
* right-aligned 24h clock. Segment backgrounds are the pi-powerline-footer
|
|
7
|
-
* palette; separators are
|
|
8
|
-
*
|
|
7
|
+
* palette; separators are the powerline arrow from src/icons.ts (U+E0B0 in
|
|
8
|
+
* nerdfont/auto-with-font, ▸ in the plain icon set), tinted by the
|
|
9
|
+
* neighbouring segment colours.
|
|
9
10
|
*
|
|
10
11
|
* Render cost is O(segments): every statistic is read from the bridge's
|
|
11
12
|
* incrementally maintained counters (never a session-log scan). The 1s clock
|
package/lib/footer.js
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* Segments (left → right): fixed brand "dsh" · provider · model+thinking ·
|
|
5
5
|
* context usage · cache-hit rate · message count · tool count, then a
|
|
6
6
|
* right-aligned 24h clock. Segment backgrounds are the pi-powerline-footer
|
|
7
|
-
* palette; separators are
|
|
8
|
-
*
|
|
7
|
+
* palette; separators are the powerline arrow from src/icons.ts (U+E0B0 in
|
|
8
|
+
* nerdfont/auto-with-font, ▸ in the plain icon set), tinted by the
|
|
9
|
+
* neighbouring segment colours.
|
|
9
10
|
*
|
|
10
11
|
* Render cost is O(segments): every statistic is read from the bridge's
|
|
11
12
|
* incrementally maintained counters (never a session-log scan). The 1s clock
|
|
@@ -15,6 +16,7 @@ import { truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';
|
|
|
15
16
|
import { ansiBg, ansiFg, BOLD, POWERLINE, RESET } from "./theme/index.js";
|
|
16
17
|
import { hexIsLight } from "./theme/palette.js";
|
|
17
18
|
import { clipToWidth } from "./text.js";
|
|
19
|
+
import { arrowRight } from "./icons.js";
|
|
18
20
|
/**
|
|
19
21
|
* The footer keybinding hint, assembled from the user's `dsh-tui.footerHints`
|
|
20
22
|
* selection (see buildFooterHint) - the pre-feature full string is what the
|
|
@@ -80,7 +82,6 @@ export class FooterHint {
|
|
|
80
82
|
return [` ${styled}${pad} `];
|
|
81
83
|
}
|
|
82
84
|
}
|
|
83
|
-
const ARROW_RIGHT = '\uE0B0';
|
|
84
85
|
const WHITE = ansiFg('#FFFFFF');
|
|
85
86
|
/** Segment text on bright fills (e.g. amber #FFC107): white is unreadable there. */
|
|
86
87
|
const DARK_TEXT = ansiFg('#1f2328');
|
|
@@ -103,10 +104,10 @@ function buildSegments(segs) {
|
|
|
103
104
|
const s = segs[i];
|
|
104
105
|
out += `${ansiBg(s.bgHex)}${BOLD}${segmentText(s.bgHex)} ${s.label} `;
|
|
105
106
|
if (i + 1 < segs.length) {
|
|
106
|
-
out += `${ansiBg(segs[i + 1].bgHex)}${ansiFg(s.bgHex)}${
|
|
107
|
+
out += `${ansiBg(segs[i + 1].bgHex)}${ansiFg(s.bgHex)}${arrowRight()}`;
|
|
107
108
|
}
|
|
108
109
|
else {
|
|
109
|
-
out += RESET + ansiFg(s.bgHex) +
|
|
110
|
+
out += RESET + ansiFg(s.bgHex) + arrowRight() + RESET;
|
|
110
111
|
}
|
|
111
112
|
}
|
|
112
113
|
return out;
|
|
@@ -157,11 +158,18 @@ export class PowerlineFooter {
|
|
|
157
158
|
bgHex: thinkingBg(effort),
|
|
158
159
|
});
|
|
159
160
|
}
|
|
160
|
-
// Context usage:
|
|
161
|
+
// Context usage: the current occupancy estimate (latest request's billed
|
|
162
|
+
// input + output + a CJK estimate of messages after it) vs. the model's
|
|
163
|
+
// window — NOT the cumulative inputTokens, which only grows. Percent is
|
|
164
|
+
// capped at 100 (the window is a hard ceiling; the estimate can overshoot
|
|
165
|
+
// while it prices pending messages that a compaction will drop).
|
|
161
166
|
const window = this.source.getContextWindow();
|
|
162
|
-
|
|
167
|
+
// Defensive `?? 0`: a stats source without the field (any object missing
|
|
168
|
+
// contextTokens) degrades to an empty context instead of rendering
|
|
169
|
+
// `undefined`/`NaN` in the segment.
|
|
170
|
+
const used = stats.contextTokens ?? 0;
|
|
163
171
|
if (window !== undefined && window > 0) {
|
|
164
|
-
const percent = (used / window) * 100;
|
|
172
|
+
const percent = Math.min(100, (used / window) * 100);
|
|
165
173
|
segs.push({
|
|
166
174
|
label: `🧠 ${fmtNum(used)}/${fmtNum(window)}(${percent.toFixed(1)}%)`,
|
|
167
175
|
bgHex: contextBg(percent),
|
package/lib/footer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"footer.js","sourceRoot":"","sources":["../src/footer.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"footer.js","sourceRoot":"","sources":["../src/footer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,eAAe,EAAE,YAAY,EAAkB,MAAM,wBAAwB,CAAA;AAEtF,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAiB,MAAM,kBAAkB,CAAA;AACxF,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEvC;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,WAAW,GACtB,yGAAyG,CAAA;AAY3G,4DAA4D;AAC5D,MAAM,CAAC,MAAM,oBAAoB,GAAgB,MAAM,CAAC,MAAM,CAAC;IAC7D,IAAI,EAAE,IAAI;IACV,IAAI,EAAE,IAAI;IACV,IAAI,EAAE,IAAI;IACV,SAAS,EAAE,IAAI;IACf,SAAS,EAAE,IAAI;IACf,OAAO,EAAE,IAAI;CACd,CAAC,CAAA;AAEF,sEAAsE;AACtE,MAAM,CAAC,MAAM,iBAAiB,GAA4D;IACxF,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE;IACpC,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE;IACrC,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,EAAE;IACxC,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,sBAAsB,EAAE;IAClD,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,mBAAmB,EAAE;IAC/C,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,aAAa,EAAE;CACxC,CAAA;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,KAAkB;IAChD,MAAM,KAAK,GAAG,iBAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACtF,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IACjC,OAAO,KAAK,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAA;AACjC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,OAAO,UAAU;IACJ,QAAQ,CAAgB;IACxB,QAAQ,CAAmB;IAE5C,YAAY,QAAwB,EAAE,QAA2B;QAC/D,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;QACxB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;IAC1B,CAAC;IAED,UAAU,KAA2E,CAAC;IAEtF,MAAM,CAAC,KAAa;QAClB,MAAM,IAAI,GAAG,eAAe,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;QAC7C,IAAI,IAAI,KAAK,EAAE;YAAE,OAAO,EAAE,CAAA;QAC1B,2EAA2E;QAC3E,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAA;QACpC,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;QACxC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,GAAG,KAAK,CAAA;QACzE,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;QACtE,OAAO,CAAC,IAAI,MAAM,GAAG,GAAG,GAAG,CAAC,CAAA;IAC9B,CAAC;CACF;AAED,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,CAAA;AAC/B,oFAAoF;AACpF,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC,CAAA;AAEnC,+EAA+E;AAC/E,SAAS,WAAW,CAAC,KAAa;IAChC,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAA;AAC9C,CAAC;AAkBD,MAAM,UAAU,MAAM,CAAC,CAAS;IAC9B,IAAI,CAAC,IAAI,SAAS;QAAE,OAAO,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAA;IAC3D,IAAI,CAAC,IAAI,IAAI;QAAE,OAAO,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAA;IACjD,OAAO,GAAG,CAAC,EAAE,CAAA;AACf,CAAC;AAED,SAAS,aAAa,CAAC,IAAwB;IAC7C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IAChC,IAAI,GAAG,GAAG,EAAE,CAAA;IACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAE,CAAA;QAClB,GAAG,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,GAAG,CAAA;QACrE,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YACxB,GAAG,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAE,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,UAAU,EAAE,EAAE,CAAA;QACzE,CAAC;aAAM,CAAC;YACN,GAAG,IAAI,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,UAAU,EAAE,GAAG,KAAK,CAAA;QACvD,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,yEAAyE;AACzE,SAAS,UAAU,CAAC,KAAyB;IAC3C,MAAM,GAAG,GAAG,CAAC,KAAK,IAAI,KAAK,CAAoC,CAAA;IAC/D,OAAO,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAA;AAC1D,CAAC;AAED,SAAS,YAAY,CAAC,KAAyB;IAC7C,QAAQ,KAAK,IAAI,KAAK,EAAE,CAAC;QACvB,KAAK,SAAS,CAAC,CAAC,OAAO,GAAG,CAAA;QAC1B,KAAK,KAAK,CAAC,CAAC,OAAO,GAAG,CAAA;QACtB,KAAK,QAAQ,CAAC,CAAC,OAAO,GAAG,CAAA;QACzB,KAAK,MAAM,CAAC,CAAC,OAAO,GAAG,CAAA;QACvB,KAAK,OAAO,CAAC,CAAC,OAAO,GAAG,CAAA;QACxB,KAAK,KAAK,CAAC,CAAC,OAAO,GAAG,CAAA;QACtB,OAAO,CAAC,CAAC,OAAO,GAAG,CAAA;IACrB,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,OAAe;IAChC,IAAI,OAAO,IAAI,EAAE;QAAE,OAAO,SAAS,CAAC,aAAa,CAAA;IACjD,IAAI,OAAO,IAAI,EAAE;QAAE,OAAO,SAAS,CAAC,aAAa,CAAA;IACjD,IAAI,OAAO,IAAI,EAAE;QAAE,OAAO,SAAS,CAAC,WAAW,CAAA;IAC/C,OAAO,SAAS,CAAC,SAAS,CAAA;AAC5B,CAAC;AAED,MAAM,OAAO,eAAe;IACT,MAAM,CAAkB;IACxB,QAAQ,CAAgB;IAEzC,YAAY,MAAwB,EAAE,QAAwB;QAC5D,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;IAC1B,CAAC;IAED,UAAU,KAA0E,CAAC;IAErF,MAAM,CAAC,KAAa;QAClB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAA;QACpC,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAA;QAE5C,MAAM,IAAI,GAAc,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,CAAC,CAAA;QAClE,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAA;YAC3E,MAAM,KAAK,GAAG,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAG,EAAE,EAAE,CAAC,CAAA;YAChE,MAAM,MAAM,GAAG,SAAS,CAAC,eAAe,IAAI,KAAK,CAAA;YACjD,IAAI,CAAC,IAAI,CAAC;gBACR,KAAK,EAAE,MAAM,KAAK,IAAI,YAAY,CAAC,MAAM,CAAC,IAAI,MAAM,EAAE;gBACtD,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;aAC1B,CAAC,CAAA;QACJ,CAAC;QAED,yEAAyE;QACzE,wEAAwE;QACxE,wEAAwE;QACxE,0EAA0E;QAC1E,iEAAiE;QACjE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,gBAAgB,EAAE,CAAA;QAC7C,yEAAyE;QACzE,mEAAmE;QACnE,oCAAoC;QACpC,MAAM,IAAI,GAAG,KAAK,CAAC,aAAa,IAAI,CAAC,CAAA;QACrC,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;YACvC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,GAAG,MAAM,CAAC,GAAG,GAAG,CAAC,CAAA;YACpD,IAAI,CAAC,IAAI,CAAC;gBACR,KAAK,EAAE,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;gBACrE,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC;aAC1B,CAAC,CAAA;QACJ,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,CAAC,SAAS,EAAE,CAAC,CAAA;QACxE,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,eAAe,GAAG,CAAC,IAAI,KAAK,CAAC,gBAAgB,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;YAClG,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,CAAC,CAAA;QACvF,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,CAAC,QAAQ,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAA;QAC5E,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,CAAC,aAAa,QAAQ,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,CAAC,CAAA;QAE/E,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,CAAA;QAChC,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,CAAC,CAAA;QAEpC,yEAAyE;QACzE,wEAAwE;QACxE,0EAA0E;QAC1E,MAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC,kBAAkB,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAA;QACtE,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,IAAI,GAAG,KAAK,CAAA;QACtE,MAAM,UAAU,GAAG,YAAY,CAAC,KAAK,CAAC,CAAA;QACtC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,SAAS,GAAG,UAAU,CAAC,CAAA;QAEvD,OAAO,CAAC,eAAe,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,CAAC,CAAA;IACjE,CAAC;CACF"}
|
package/lib/icons.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Icon handling for dsh-tui-pi (icon-set self-adaptation).
|
|
3
|
+
*
|
|
4
|
+
* Glyph-risk classification for every symbol this TUI renders:
|
|
5
|
+
*
|
|
6
|
+
* - PUA, must fall back — the ONLY Private Use Area codepoint in the whole
|
|
7
|
+
* project is the powerline separator U+E0B0 (footer's arrow between
|
|
8
|
+
* segments). No default terminal font ships it, so a terminal without a
|
|
9
|
+
* Nerd Font shows a tofu box. `auto`/`plain` swap it for `▸` U+25B8.
|
|
10
|
+
*
|
|
11
|
+
* - Standard Unicode, weak-terminal-risky — U+23F9 `⏹` (stop) and U+2B58
|
|
12
|
+
* `⭘` (heavy circle) are standard codepoints, but weak terminals /
|
|
13
|
+
* emoji-less fonts still render them as boxes. `auto`/`plain` swap them
|
|
14
|
+
* for `■` U+25A0 and `●` U+25CF.
|
|
15
|
+
*
|
|
16
|
+
* - Standard Unicode, rely on system fallback — everything else
|
|
17
|
+
* (⚙ ⚠ ⚡ ✓ ✔ ✗ ✘ ⌘ ⎇ ⓘ ◐ ☑ ☐ ◔ ◑ ◕ ◉ ○ ★ ☁ 🤖 🧠 💬 🔧 ● …). Modern
|
|
18
|
+
* terminals resolve these via font fallback and they are left untouched
|
|
19
|
+
* in every mode.
|
|
20
|
+
*
|
|
21
|
+
* Modes (`dsh-tui.iconSet`, persisted in the settings namespace):
|
|
22
|
+
* - 'nerdfont': use the PUA powerline glyph (assumes a Nerd Font is the
|
|
23
|
+
* terminal's font).
|
|
24
|
+
* - 'plain': use the safe standard-Unicode stand-ins for every risky glyph.
|
|
25
|
+
* - 'auto' (default): nerdfont when a Nerd Font is detected at startup,
|
|
26
|
+
* plain otherwise (see src/font-detect.ts).
|
|
27
|
+
*
|
|
28
|
+
* The resolved mode lives in module state: `applyIconSet` is called once at
|
|
29
|
+
* TUI startup and again on every hot-applied `iconSet` settings change, so
|
|
30
|
+
* footer/notice/panel accessors return the right glyph at render time. The
|
|
31
|
+
* module defaults to 'nerdfont' — the pre-feature output (`\uE0B0`) is
|
|
32
|
+
* preserved unless the resolution lands on 'plain'.
|
|
33
|
+
*/
|
|
34
|
+
/** The persisted/`auto` icon-set mode; 'auto' resolves at startup. */
|
|
35
|
+
export type IconSet = 'auto' | 'nerdfont' | 'plain';
|
|
36
|
+
/** A resolved (non-auto) icon set — the value that drives glyph selection. */
|
|
37
|
+
export type ResolvedIconSet = Exclude<IconSet, 'auto'>;
|
|
38
|
+
/**
|
|
39
|
+
* Resolve an icon-set mode against the startup font detection: `auto` picks
|
|
40
|
+
* the nerdfont glyphs when a Nerd Font is available and the plain fallbacks
|
|
41
|
+
* otherwise; an explicit `nerdfont`/`plain` pin returns itself unchanged.
|
|
42
|
+
*/
|
|
43
|
+
export declare function resolveIconSet(mode: IconSet, nerdfontAvailable: boolean): IconSet;
|
|
44
|
+
/** Set the live resolved icon set (startup + hot-apply, src/index.ts). */
|
|
45
|
+
export declare function applyIconSet(set: IconSet): void;
|
|
46
|
+
/** The live resolved icon set (exposed for tests and the /settings UI). */
|
|
47
|
+
export declare function getResolvedIconSet(): ResolvedIconSet;
|
|
48
|
+
/** The powerline segment separator: `\uE0B0` (nerdfont) / `▸` (plain). */
|
|
49
|
+
export declare function arrowRight(set?: IconSet): string;
|
|
50
|
+
/** The stop glyph: `⏹` (nerdfont) / `■` (plain). */
|
|
51
|
+
export declare function stopIcon(set?: IconSet): string;
|
|
52
|
+
/** The subagent glyph: `⭘` (nerdfont) / `●` (plain). */
|
|
53
|
+
export declare function sunglassesIcon(set?: IconSet): string;
|
package/lib/icons.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Icon handling for dsh-tui-pi (icon-set self-adaptation).
|
|
3
|
+
*
|
|
4
|
+
* Glyph-risk classification for every symbol this TUI renders:
|
|
5
|
+
*
|
|
6
|
+
* - PUA, must fall back — the ONLY Private Use Area codepoint in the whole
|
|
7
|
+
* project is the powerline separator U+E0B0 (footer's arrow between
|
|
8
|
+
* segments). No default terminal font ships it, so a terminal without a
|
|
9
|
+
* Nerd Font shows a tofu box. `auto`/`plain` swap it for `▸` U+25B8.
|
|
10
|
+
*
|
|
11
|
+
* - Standard Unicode, weak-terminal-risky — U+23F9 `⏹` (stop) and U+2B58
|
|
12
|
+
* `⭘` (heavy circle) are standard codepoints, but weak terminals /
|
|
13
|
+
* emoji-less fonts still render them as boxes. `auto`/`plain` swap them
|
|
14
|
+
* for `■` U+25A0 and `●` U+25CF.
|
|
15
|
+
*
|
|
16
|
+
* - Standard Unicode, rely on system fallback — everything else
|
|
17
|
+
* (⚙ ⚠ ⚡ ✓ ✔ ✗ ✘ ⌘ ⎇ ⓘ ◐ ☑ ☐ ◔ ◑ ◕ ◉ ○ ★ ☁ 🤖 🧠 💬 🔧 ● …). Modern
|
|
18
|
+
* terminals resolve these via font fallback and they are left untouched
|
|
19
|
+
* in every mode.
|
|
20
|
+
*
|
|
21
|
+
* Modes (`dsh-tui.iconSet`, persisted in the settings namespace):
|
|
22
|
+
* - 'nerdfont': use the PUA powerline glyph (assumes a Nerd Font is the
|
|
23
|
+
* terminal's font).
|
|
24
|
+
* - 'plain': use the safe standard-Unicode stand-ins for every risky glyph.
|
|
25
|
+
* - 'auto' (default): nerdfont when a Nerd Font is detected at startup,
|
|
26
|
+
* plain otherwise (see src/font-detect.ts).
|
|
27
|
+
*
|
|
28
|
+
* The resolved mode lives in module state: `applyIconSet` is called once at
|
|
29
|
+
* TUI startup and again on every hot-applied `iconSet` settings change, so
|
|
30
|
+
* footer/notice/panel accessors return the right glyph at render time. The
|
|
31
|
+
* module defaults to 'nerdfont' — the pre-feature output (`\uE0B0`) is
|
|
32
|
+
* preserved unless the resolution lands on 'plain'.
|
|
33
|
+
*/
|
|
34
|
+
/** PUA — must fall back (see the classification above). */
|
|
35
|
+
const ARROW_RIGHT_GLYPHS = { nerdfont: '\uE0B0', plain: '\u25B8' };
|
|
36
|
+
/** Standard Unicode, weak-terminal-risky: `⏹` → `■`. */
|
|
37
|
+
const STOP_GLYPHS = { nerdfont: '\u23F9', plain: '\u25A0' };
|
|
38
|
+
/** Standard Unicode, weak-terminal-risky: `⭘` → `●`. */
|
|
39
|
+
const SUNGLASSES_GLYPHS = { nerdfont: '\u2B58', plain: '\u25CF' };
|
|
40
|
+
/**
|
|
41
|
+
* Resolve an icon-set mode against the startup font detection: `auto` picks
|
|
42
|
+
* the nerdfont glyphs when a Nerd Font is available and the plain fallbacks
|
|
43
|
+
* otherwise; an explicit `nerdfont`/`plain` pin returns itself unchanged.
|
|
44
|
+
*/
|
|
45
|
+
export function resolveIconSet(mode, nerdfontAvailable) {
|
|
46
|
+
if (mode === 'auto')
|
|
47
|
+
return nerdfontAvailable ? 'nerdfont' : 'plain';
|
|
48
|
+
return mode;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The live resolved icon set. Defaults to 'nerdfont' — the legacy output —
|
|
52
|
+
* so a TUI that renders before the async font detection lands never flickers
|
|
53
|
+
* from a plain glyph to the powerline arrow; only a resolved 'plain' flips it.
|
|
54
|
+
*/
|
|
55
|
+
let resolvedIconSet = 'nerdfont';
|
|
56
|
+
/** Set the live resolved icon set (startup + hot-apply, src/index.ts). */
|
|
57
|
+
export function applyIconSet(set) {
|
|
58
|
+
// 'auto' is resolved at startup (font detection, index.ts); a hot-applied
|
|
59
|
+
// settings 'auto' keeps the current resolved set — never flickers.
|
|
60
|
+
resolvedIconSet = set === 'auto' ? resolvedIconSet : set;
|
|
61
|
+
}
|
|
62
|
+
/** The live resolved icon set (exposed for tests and the /settings UI). */
|
|
63
|
+
export function getResolvedIconSet() {
|
|
64
|
+
return resolvedIconSet;
|
|
65
|
+
}
|
|
66
|
+
/** The powerline segment separator: `\uE0B0` (nerdfont) / `▸` (plain). */
|
|
67
|
+
export function arrowRight(set = resolvedIconSet) {
|
|
68
|
+
return ARROW_RIGHT_GLYPHS[set === 'auto' ? resolvedIconSet : set];
|
|
69
|
+
}
|
|
70
|
+
/** The stop glyph: `⏹` (nerdfont) / `■` (plain). */
|
|
71
|
+
export function stopIcon(set = resolvedIconSet) {
|
|
72
|
+
return STOP_GLYPHS[set === 'auto' ? resolvedIconSet : set];
|
|
73
|
+
}
|
|
74
|
+
/** The subagent glyph: `⭘` (nerdfont) / `●` (plain). */
|
|
75
|
+
export function sunglassesIcon(set = resolvedIconSet) {
|
|
76
|
+
return SUNGLASSES_GLYPHS[set === 'auto' ? resolvedIconSet : set];
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=icons.js.map
|
package/lib/icons.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"icons.js","sourceRoot":"","sources":["../src/icons.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAWH,2DAA2D;AAC3D,MAAM,kBAAkB,GAAe,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAA;AAC9E,wDAAwD;AACxD,MAAM,WAAW,GAAe,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAA;AACvE,wDAAwD;AACxD,MAAM,iBAAiB,GAAe,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAA;AAE7E;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,IAAa,EAAE,iBAA0B;IACtE,IAAI,IAAI,KAAK,MAAM;QAAE,OAAO,iBAAiB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAA;IACpE,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;GAIG;AACH,IAAI,eAAe,GAAoB,UAAU,CAAA;AAEjD,0EAA0E;AAC1E,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,0EAA0E;IAC1E,mEAAmE;IACnE,eAAe,GAAG,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,GAAG,CAAA;AAC1D,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,kBAAkB;IAChC,OAAO,eAAe,CAAA;AACxB,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,UAAU,CAAC,MAAe,eAAe;IACvD,OAAO,kBAAkB,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;AACnE,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,QAAQ,CAAC,MAAe,eAAe;IACrD,OAAO,WAAW,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;AAC5D,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,cAAc,CAAC,MAAe,eAAe;IAC3D,OAAO,iBAAiB,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;AAClE,CAAC"}
|
package/lib/index.js
CHANGED
|
@@ -22,7 +22,9 @@ import { displayPermissionPreset } from "./permission.js";
|
|
|
22
22
|
import { pickEffort, pickModel, pickPermission, pickSkill, pickTheme } from "./selectors.js";
|
|
23
23
|
import { parseSkillCommand, skillGesture } from "./skills.js";
|
|
24
24
|
import { DshSessionBridge, persistDefaultModel, stashSessionIdForReload, takeStashedSessionId } from "./session.js";
|
|
25
|
-
import { currentThemePreference, readFooterHintsPreference, readPanelHeightPreference, readThemePreference, registerThemeSettings, writeThemePreference, } from "./theme-settings.js";
|
|
25
|
+
import { currentThemePreference, readFooterHintsPreference, readIconSetPreference, readPanelHeightPreference, readSubagentLimits, readThemePreference, registerThemeSettings, writeThemePreference, } from "./theme-settings.js";
|
|
26
|
+
import { applyIconSet, resolveIconSet, stopIcon } from "./icons.js";
|
|
27
|
+
import { detectNerdFontAvailable } from "./font-detect.js";
|
|
26
28
|
import { openAgentManager } from "./agents.js";
|
|
27
29
|
import { openSettingsBrowser } from "./settings.js";
|
|
28
30
|
import { openLoginFlow, openLogoutFlow } from "./login.js";
|
|
@@ -90,6 +92,14 @@ export function apply(ctx) {
|
|
|
90
92
|
* the new segment selection. Armed inside runTui once the TUI exists.
|
|
91
93
|
*/
|
|
92
94
|
let applyFooterHintsRef;
|
|
95
|
+
/**
|
|
96
|
+
* Live icon-set hot-reload sink, wired to the same watch hook: a committed
|
|
97
|
+
* `dsh-tui` iconSet change re-resolves the risky glyphs (an 'auto' change
|
|
98
|
+
* resolves against the startup font-detection snapshot) and repaints the
|
|
99
|
+
* footer/notices/panels that carry them. Armed inside runTui once the TUI
|
|
100
|
+
* exists.
|
|
101
|
+
*/
|
|
102
|
+
let applyIconSetRef;
|
|
93
103
|
ctx.effect(async () => {
|
|
94
104
|
// The theme bundle is built once at TUI startup and held by every
|
|
95
105
|
// component, so the persisted preference must land before startTui — the
|
|
@@ -105,17 +115,26 @@ export function apply(ctx) {
|
|
|
105
115
|
// setTheme replay that follows already renders at that new height.
|
|
106
116
|
// applyTheme carries the theme-bundle identity guard, so a height-only
|
|
107
117
|
// commit never triggers a second rebuild.
|
|
108
|
-
registerThemeSettings(ctx, (pref, height, footerHints) => {
|
|
118
|
+
registerThemeSettings(ctx, (pref, height, footerHints, iconSet) => {
|
|
109
119
|
applyPanelHeightRef?.(height);
|
|
110
120
|
applyThemeRef?.(pref);
|
|
111
121
|
applyFooterHintsRef?.(footerHints);
|
|
122
|
+
applyIconSetRef?.(iconSet);
|
|
112
123
|
});
|
|
113
124
|
const themePreference = await readThemePreference(ctx);
|
|
114
125
|
const panelHeight = await readPanelHeightPreference(ctx);
|
|
115
126
|
const footerHints = await readFooterHintsPreference(ctx);
|
|
127
|
+
// Icon-set self-adaptation: probe the platform once at startup (the
|
|
128
|
+
// memoised snapshot shared with every later 'auto' resolution), resolve
|
|
129
|
+
// the persisted mode against it and push the result into src/icons.ts
|
|
130
|
+
// BEFORE the TUI's first frame — so the footer renders the right
|
|
131
|
+
// separator from frame one, never a late plain→powerline flicker.
|
|
132
|
+
const iconSetPreference = await readIconSetPreference(ctx);
|
|
133
|
+
const nerdfontAvailable = await detectNerdFontAvailable();
|
|
134
|
+
applyIconSet(resolveIconSet(iconSetPreference, nerdfontAvailable));
|
|
116
135
|
let disposer;
|
|
117
136
|
try {
|
|
118
|
-
disposer = runTui(themePreference, panelHeight, footerHints);
|
|
137
|
+
disposer = runTui(themePreference, panelHeight, footerHints, nerdfontAvailable);
|
|
119
138
|
}
|
|
120
139
|
catch (error) {
|
|
121
140
|
// An async-effect failure after startTui would otherwise orphan the
|
|
@@ -136,7 +155,7 @@ export function apply(ctx) {
|
|
|
136
155
|
* reaches cordis. Returns the effect disposer handed back to cordis on
|
|
137
156
|
* teardown.
|
|
138
157
|
*/
|
|
139
|
-
function runTui(themePreference, panelHeight, footerHints) {
|
|
158
|
+
function runTui(themePreference, panelHeight, footerHints, nerdfontAvailable) {
|
|
140
159
|
// User keybindings (`~/.dsh/keybindings.json`): a partial map of the app
|
|
141
160
|
// keys, read once per TUI start — `/reload` re-runs apply() and re-reads
|
|
142
161
|
// it. Broken entries surface as notices instead of breaking startup.
|
|
@@ -185,7 +204,7 @@ export function apply(ctx) {
|
|
|
185
204
|
void stopTask();
|
|
186
205
|
break;
|
|
187
206
|
}
|
|
188
|
-
renderer.renderNotice(
|
|
207
|
+
renderer.renderNotice(`${stopIcon()} stopping — Esc was double-pressed`, 'info');
|
|
189
208
|
stopConfirmTimer = setTimeout(() => {
|
|
190
209
|
stopConfirmTimer = undefined;
|
|
191
210
|
void stopTask();
|
|
@@ -226,7 +245,7 @@ export function apply(ctx) {
|
|
|
226
245
|
void disposeAndExit(0);
|
|
227
246
|
break;
|
|
228
247
|
}
|
|
229
|
-
renderer.renderNotice(
|
|
248
|
+
renderer.renderNotice(`${stopIcon()} Ctrl+C ×2 — quitting…`, 'info');
|
|
230
249
|
quitConfirmTimer = setTimeout(() => {
|
|
231
250
|
quitConfirmTimer = undefined;
|
|
232
251
|
void disposeAndExit(0);
|
|
@@ -291,7 +310,7 @@ export function apply(ctx) {
|
|
|
291
310
|
* ESC/Ctrl+C double-press guards); the notice renders immediately.
|
|
292
311
|
*/
|
|
293
312
|
const stopTask = async () => {
|
|
294
|
-
renderer.renderNotice(
|
|
313
|
+
renderer.renderNotice(`${stopIcon()} canceling current turn…`, 'info');
|
|
295
314
|
const cancelled = await bridge.cancelActiveTurn();
|
|
296
315
|
// State raced idle between the decision and the cancel call — nothing
|
|
297
316
|
// to cancel (e.g. the turn settled inside the confirm window).
|
|
@@ -318,7 +337,10 @@ export function apply(ctx) {
|
|
|
318
337
|
// into the last-request area BELOW the editor. Owned here — fed by
|
|
319
338
|
// session events and the bridge's subagent fold, ticked by the live
|
|
320
339
|
// timer, recolored by applyTheme.
|
|
321
|
-
const liveWidgets = new LiveWidgets(ui.widgets, ui.lastRequest, ui.theme, () => ui.requestRender(), panelHeight
|
|
340
|
+
const liveWidgets = new LiveWidgets(ui.widgets, ui.lastRequest, ui.theme, () => ui.requestRender(), panelHeight,
|
|
341
|
+
// Live maxRounds for the compact running-agent lines' `round N/M` — read
|
|
342
|
+
// at every rebuild (the /agents limits panel hot-applies).
|
|
343
|
+
() => readSubagentLimits(ctx).maxRounds);
|
|
322
344
|
// Arm the panel-height watch sink now that the widgets exist: a committed
|
|
323
345
|
// panelHeight change re-budgets the think/tool panels — they are
|
|
324
346
|
// self-drawing, so the next frame already renders at the new height.
|
|
@@ -392,15 +414,16 @@ export function apply(ctx) {
|
|
|
392
414
|
// Subagent fine-grained control, all in-process (see subagent-policy.ts):
|
|
393
415
|
// a tools.guard denies spawn-tool calls once `maxAgents` children run
|
|
394
416
|
// (workflow fan-out, which bypasses the tool pipeline, is pruned on
|
|
395
|
-
// `subagent/start`), and when a child's
|
|
396
|
-
// the policy queues one wrap-up message into its next turn.
|
|
397
|
-
// read live from the `dsh-tui` settings namespace
|
|
417
|
+
// `subagent/start`), and when a child's assistant-message count reaches
|
|
418
|
+
// `maxRounds` the policy queues one wrap-up message into its next turn.
|
|
419
|
+
// Limits are read live from the `dsh-tui` settings namespace
|
|
420
|
+
// (/agents → l limits).
|
|
398
421
|
const subagentPolicy = applySubagentPolicy(ctx, {
|
|
399
422
|
getLive: () => bridge.getLiveChildren(),
|
|
400
|
-
|
|
423
|
+
getRoundCount: childId => bridge.getRoundCount(childId),
|
|
401
424
|
isSettled: childId => bridge.isChildSettled(childId),
|
|
402
425
|
});
|
|
403
|
-
bridgeCallbacks.
|
|
426
|
+
bridgeCallbacks.onRoundCount = (childId, count) => subagentPolicy.onRoundCount(childId, count);
|
|
404
427
|
/**
|
|
405
428
|
* One O(events) fold of the session log into the effective preset, stored
|
|
406
429
|
* for the badge provider. Runs once per session (ensure/resume, see the
|
|
@@ -975,6 +998,14 @@ export function apply(ctx) {
|
|
|
975
998
|
footerHintsRef = hints;
|
|
976
999
|
ui.requestRender();
|
|
977
1000
|
};
|
|
1001
|
+
// Arm the icon-set watch sink: a committed iconSet change hot-applies the
|
|
1002
|
+
// risky glyphs (an 'auto' change resolves against the startup font-
|
|
1003
|
+
// detection snapshot, matching the startup glyph choice) and repaints the
|
|
1004
|
+
// footer/notices/panels that carry them on the next frame.
|
|
1005
|
+
applyIconSetRef = (set) => {
|
|
1006
|
+
applyIconSet(resolveIconSet(set, nerdfontAvailable));
|
|
1007
|
+
ui.requestRender();
|
|
1008
|
+
};
|
|
978
1009
|
/**
|
|
979
1010
|
* Hot-apply a theme bundle to the running TUI: the transcript rebuilds
|
|
980
1011
|
* from its replay buffer, the dock/editor swap to the new bundle, and the
|