@aiwayds/dsh-tui-pi 0.7.1 → 0.8.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 +12 -10
- package/lib/canvas-terminal.d.ts +37 -0
- package/lib/canvas-terminal.js +91 -0
- package/lib/canvas-terminal.js.map +1 -0
- package/lib/theme/index.js +6 -12
- package/lib/theme/index.js.map +1 -1
- package/lib/theme/palette.d.ts +2 -2
- package/lib/theme/palette.js +2 -2
- package/lib/tui.js +17 -12
- package/lib/tui.js.map +1 -1
- package/package.json +3 -4
- package/scripts/dev-install.mjs +0 -2
- package/scripts/diagnose-container-tui.sh +612 -0
- package/patches/@earendil-works__pi-tui.patch +0 -276
package/README.md
CHANGED
|
@@ -200,6 +200,10 @@ GitHub light / GitHub dark palettes, hot-switchable at runtime:
|
|
|
200
200
|
- `DSH_TUI_TRANSPARENT=1` — see-through canvas (terminal background shows through).
|
|
201
201
|
- `auto` mode detects the terminal and follows live light/dark switches.
|
|
202
202
|
|
|
203
|
+
The full-screen canvas background ships inside the package — a write-stream
|
|
204
|
+
decorator (`src/canvas-terminal.ts`) paints every erase sequence with the
|
|
205
|
+
theme color via BCE, no patched dependencies.
|
|
206
|
+
|
|
203
207
|
---
|
|
204
208
|
|
|
205
209
|
## Install (local)
|
|
@@ -247,12 +251,10 @@ dsh --profile tui
|
|
|
247
251
|
|
|
248
252
|
**What does NOT happen automatically:**
|
|
249
253
|
|
|
250
|
-
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
without the patch; the unpatched select-list just renders unselected rows
|
|
255
|
-
as plain `prefix + value` instead of a full-row backdrop.
|
|
254
|
+
- Nothing patch-related anymore: since 0.8.0 the repo and the npm package
|
|
255
|
+
run the same pristine `@earendil-works/pi-tui` — the canvas background is
|
|
256
|
+
painted by our own write-stream decorator (BCE), which ships in the
|
|
257
|
+
package and needs no `pnpm-workspace.yaml` entries in consumer profiles.
|
|
256
258
|
|
|
257
259
|
### Troubleshooting
|
|
258
260
|
|
|
@@ -278,7 +280,7 @@ dsh --profile tui # or: dsh-tui-pi (bin shim)
|
|
|
278
280
|
```sh
|
|
279
281
|
pnpm check # tsc --noEmit
|
|
280
282
|
pnpm build # emit lib/
|
|
281
|
-
pnpm test # unit tests, node --test against lib/ (
|
|
283
|
+
pnpm test # unit tests, node --test against lib/ (386 tests, pretest builds)
|
|
282
284
|
```
|
|
283
285
|
|
|
284
286
|
Local type-checking symlinks `node_modules/@deepseek-ai/*` to the installed
|
|
@@ -286,9 +288,9 @@ dsh closure (`/opt/homebrew/lib/node_modules/@deepseek-ai/dsh/node_modules`);
|
|
|
286
288
|
those symlinks stay out of any tarball. `scripts/link-dsh-closure.mjs` (the
|
|
287
289
|
package's `postinstall`) re-creates every link after each `pnpm install`.
|
|
288
290
|
|
|
289
|
-
**pi-tui
|
|
290
|
-
|
|
291
|
-
`
|
|
291
|
+
**pi-tui**: pristine `@earendil-works/pi-tui` 0.84.2 from npm — no patches,
|
|
292
|
+
no fork. The full-screen canvas background is our own write-stream decorator
|
|
293
|
+
(`src/canvas-terminal.ts`, BCE).
|
|
292
294
|
|
|
293
295
|
---
|
|
294
296
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal write-stream decorator painting the canvas via BCE.
|
|
3
|
+
*
|
|
4
|
+
* pi-tui renders rows as `\x1b[{row};1H\x1b[2K{line}` and full redraws as
|
|
5
|
+
* `\x1b[2J` — no background of their own. Terminals with BCE (back color
|
|
6
|
+
* erase) fill erased regions with the current SGR background, so prefixing
|
|
7
|
+
* the canvas background before every erase sequence paints the whole screen
|
|
8
|
+
* without touching pi-tui. Rows the diff renderer skips keep the background
|
|
9
|
+
* of their last paint, which is correct until the canvas color changes —
|
|
10
|
+
* the caller must then force a full redraw (requestRender(true)).
|
|
11
|
+
*/
|
|
12
|
+
import type { Terminal } from '@earendil-works/pi-tui';
|
|
13
|
+
export declare class CanvasTerminal implements Terminal {
|
|
14
|
+
/** '' = transparent passthrough (DSH_TUI_TRANSPARENT). */
|
|
15
|
+
private background;
|
|
16
|
+
/** False once the alt-screen exit sequence is seen — the main-screen document dump that follows stays unpainted. */
|
|
17
|
+
private active;
|
|
18
|
+
private readonly inner;
|
|
19
|
+
constructor(inner: Terminal);
|
|
20
|
+
/** Set the canvas background SGR; undefined reverts to transparent. */
|
|
21
|
+
setCanvasBackground(bg: string | undefined): void;
|
|
22
|
+
write(data: string): void;
|
|
23
|
+
start(onInput: (data: string) => void, onResize: () => void): void;
|
|
24
|
+
stop(): void;
|
|
25
|
+
drainInput(maxMs?: number, idleMs?: number): Promise<void>;
|
|
26
|
+
get columns(): number;
|
|
27
|
+
get rows(): number;
|
|
28
|
+
get kittyProtocolActive(): boolean;
|
|
29
|
+
moveBy(lines: number): void;
|
|
30
|
+
hideCursor(): void;
|
|
31
|
+
showCursor(): void;
|
|
32
|
+
clearLine(): void;
|
|
33
|
+
clearFromCursor(): void;
|
|
34
|
+
clearScreen(): void;
|
|
35
|
+
setTitle(title: string): void;
|
|
36
|
+
setProgress(active: boolean): void;
|
|
37
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal write-stream decorator painting the canvas via BCE.
|
|
3
|
+
*
|
|
4
|
+
* pi-tui renders rows as `\x1b[{row};1H\x1b[2K{line}` and full redraws as
|
|
5
|
+
* `\x1b[2J` — no background of their own. Terminals with BCE (back color
|
|
6
|
+
* erase) fill erased regions with the current SGR background, so prefixing
|
|
7
|
+
* the canvas background before every erase sequence paints the whole screen
|
|
8
|
+
* without touching pi-tui. Rows the diff renderer skips keep the background
|
|
9
|
+
* of their last paint, which is correct until the canvas color changes —
|
|
10
|
+
* the caller must then force a full redraw (requestRender(true)).
|
|
11
|
+
*/
|
|
12
|
+
const ENTER_ALT_SCREEN = '\x1b[?1049h';
|
|
13
|
+
const EXIT_ALT_SCREEN = '\x1b[?1049l';
|
|
14
|
+
export class CanvasTerminal {
|
|
15
|
+
/** '' = transparent passthrough (DSH_TUI_TRANSPARENT). */
|
|
16
|
+
background = '';
|
|
17
|
+
/** False once the alt-screen exit sequence is seen — the main-screen document dump that follows stays unpainted. */
|
|
18
|
+
active = true;
|
|
19
|
+
inner;
|
|
20
|
+
constructor(inner) {
|
|
21
|
+
this.inner = inner;
|
|
22
|
+
}
|
|
23
|
+
/** Set the canvas background SGR; undefined reverts to transparent. */
|
|
24
|
+
setCanvasBackground(bg) {
|
|
25
|
+
this.background = bg ?? '';
|
|
26
|
+
}
|
|
27
|
+
write(data) {
|
|
28
|
+
if (data.includes(EXIT_ALT_SCREEN)) {
|
|
29
|
+
// The exit buffer starts with EXIT_ALT_SCREEN; write it (and anything
|
|
30
|
+
// after, until the next enter) unchanged so quitting leaves no themed
|
|
31
|
+
// background behind on the shell.
|
|
32
|
+
this.active = false;
|
|
33
|
+
this.inner.write(data);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (data.includes(ENTER_ALT_SCREEN))
|
|
37
|
+
this.active = true;
|
|
38
|
+
if (this.background === '' || !this.active) {
|
|
39
|
+
this.inner.write(data);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// Injection happens only at erase-sequence boundaries; kitty image
|
|
43
|
+
// payloads are APC base64/hex and never contain ESC bytes.
|
|
44
|
+
this.inner.write(data
|
|
45
|
+
.replaceAll('\x1b[2K', `${this.background}\x1b[2K`)
|
|
46
|
+
.replaceAll('\x1b[2J', `${this.background}\x1b[2J`));
|
|
47
|
+
}
|
|
48
|
+
start(onInput, onResize) {
|
|
49
|
+
this.inner.start(onInput, onResize);
|
|
50
|
+
}
|
|
51
|
+
stop() {
|
|
52
|
+
this.inner.stop();
|
|
53
|
+
}
|
|
54
|
+
drainInput(maxMs, idleMs) {
|
|
55
|
+
return this.inner.drainInput(maxMs, idleMs);
|
|
56
|
+
}
|
|
57
|
+
get columns() {
|
|
58
|
+
return this.inner.columns;
|
|
59
|
+
}
|
|
60
|
+
get rows() {
|
|
61
|
+
return this.inner.rows;
|
|
62
|
+
}
|
|
63
|
+
get kittyProtocolActive() {
|
|
64
|
+
return this.inner.kittyProtocolActive;
|
|
65
|
+
}
|
|
66
|
+
moveBy(lines) {
|
|
67
|
+
this.inner.moveBy(lines);
|
|
68
|
+
}
|
|
69
|
+
hideCursor() {
|
|
70
|
+
this.inner.hideCursor();
|
|
71
|
+
}
|
|
72
|
+
showCursor() {
|
|
73
|
+
this.inner.showCursor();
|
|
74
|
+
}
|
|
75
|
+
clearLine() {
|
|
76
|
+
this.inner.clearLine();
|
|
77
|
+
}
|
|
78
|
+
clearFromCursor() {
|
|
79
|
+
this.inner.clearFromCursor();
|
|
80
|
+
}
|
|
81
|
+
clearScreen() {
|
|
82
|
+
this.inner.clearScreen();
|
|
83
|
+
}
|
|
84
|
+
setTitle(title) {
|
|
85
|
+
this.inner.setTitle(title);
|
|
86
|
+
}
|
|
87
|
+
setProgress(active) {
|
|
88
|
+
this.inner.setProgress(active);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=canvas-terminal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas-terminal.js","sourceRoot":"","sources":["../src/canvas-terminal.ts"],"names":[],"mappings":"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"}
|
package/lib/theme/index.js
CHANGED
|
@@ -47,27 +47,21 @@ export function buildTheme(palette) {
|
|
|
47
47
|
const fg = (hex) => (text) => ansiFg(hex) + text + RESET;
|
|
48
48
|
const bg = (hex) => (text) => ansiBg(hex) + text + RESET;
|
|
49
49
|
const bold = (text) => BOLD + text + RESET;
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
// (
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
// theme, so its rows get the same full-row treatment.
|
|
50
|
+
// Picker rows. Upstream SelectList themes only the selected row (via
|
|
51
|
+
// selectedText, which receives the "→ value …" row whole), plus the
|
|
52
|
+
// description / scroll info / no-match lines; unselected value rows render
|
|
53
|
+
// plain (upstream has no hook for them). selectedPrefix is required by the
|
|
54
|
+
// SelectListTheme interface even though upstream 0.84.2 never calls it.
|
|
55
|
+
// Editor-inline slash autocomplete shares this selectList theme.
|
|
57
56
|
const selectList = {
|
|
58
57
|
selectedPrefix: text => bg(palette.canvasSubtle)(fg(palette.accent)(bold(`▸ ${text}`))),
|
|
59
58
|
selectedText: text => bg(palette.canvasSubtle)(fg(palette.fgDefault)(bold(text))),
|
|
60
59
|
description: text => bg(palette.canvasSubtle)(fg(palette.fgSubtle)(text)),
|
|
61
60
|
scrollInfo: text => bg(palette.canvasSubtle)(fg(palette.fgSubtle)(text)),
|
|
62
61
|
noMatch: text => bg(palette.canvasSubtle)(fg(palette.danger)(text)),
|
|
63
|
-
unselectedText: text => bg(palette.canvasSubtle)(fg(palette.fgDefault)(text)),
|
|
64
62
|
};
|
|
65
63
|
const editor = {
|
|
66
64
|
borderColor: fg(palette.borderDefault),
|
|
67
|
-
// The editor's input rows are otherwise unstyled (terminal default
|
|
68
|
-
// foreground), which is invisible on the app-painted dark canvas — theme
|
|
69
|
-
// the typed text with the palette's body color (light text on dark).
|
|
70
|
-
textColor: fg(palette.fgDefault),
|
|
71
65
|
selectList,
|
|
72
66
|
};
|
|
73
67
|
const markdown = {
|
package/lib/theme/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/theme/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,WAAW,EAAgB,MAAM,cAAc,CAAA;AAEvF,iDAAiD;AACjD,MAAM,UAAU,MAAM,CAAC,GAAW;IAChC,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IACvC,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IACvC,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IACvC,OAAO,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAA;AACpC,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,MAAM,CAAC,GAAW;IAChC,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IACvC,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IACvC,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IACvC,OAAO,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAA;AACpC,CAAC;AAED,MAAM,CAAC,MAAM,IAAI,GAAG,SAAS,CAAA;AAC7B,MAAM,CAAC,MAAM,KAAK,GAAG,SAAS,CAAA;AA6B9B,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,oEAAoE;IACpE,KAAK,EAAE,SAAS;IAChB,QAAQ,EAAE,SAAS;IACnB,QAAQ,EAAE;QACR,GAAG,EAAE,SAAS;QACd,OAAO,EAAE,SAAS;QAClB,GAAG,EAAE,SAAS;QACd,MAAM,EAAE,SAAS;QACjB,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;QAChB,GAAG,EAAE,SAAS;KACe;IAC/B,SAAS,EAAE,SAAS;IACpB,WAAW,EAAE,SAAS;IACtB,aAAa,EAAE,SAAS;IACxB,aAAa,EAAE,SAAS;IACxB,KAAK,EAAE,SAAS;IAChB,QAAQ,EAAE,SAAS;IACnB,KAAK,EAAE,SAAS;CACR,CAAA;AAWV,8CAA8C;AAC9C,MAAM,UAAU,UAAU,CAAC,OAAgB;IACzC,MAAM,EAAE,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,KAAK,CAAA;IACxE,MAAM,EAAE,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,KAAK,CAAA;IACxE,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,GAAG,IAAI,GAAG,KAAK,CAAA;IAElD,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/theme/index.ts"],"names":[],"mappings":"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"}
|
package/lib/theme/palette.d.ts
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* `canvas` (the terminal can't carry alpha) — same values as `blend()`
|
|
14
14
|
* below.
|
|
15
15
|
*
|
|
16
|
-
* The TUI paints every rendered row with `canvas` (
|
|
17
|
-
*
|
|
16
|
+
* The TUI paints every rendered row with `canvas` (BCE write-stream
|
|
17
|
+
* decorator, see src/canvas-terminal.ts) — the app owns its background, so
|
|
18
18
|
* a theme switch recolors the whole screen and the terminal/multiplexer
|
|
19
19
|
* background never shows through. `canvasSubtle` stays the visible raised
|
|
20
20
|
* surface on top of it. DSH_TUI_TRANSPARENT=1 reverts to the old
|
package/lib/theme/palette.js
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* `canvas` (the terminal can't carry alpha) — same values as `blend()`
|
|
14
14
|
* below.
|
|
15
15
|
*
|
|
16
|
-
* The TUI paints every rendered row with `canvas` (
|
|
17
|
-
*
|
|
16
|
+
* The TUI paints every rendered row with `canvas` (BCE write-stream
|
|
17
|
+
* decorator, see src/canvas-terminal.ts) — the app owns its background, so
|
|
18
18
|
* a theme switch recolors the whole screen and the terminal/multiplexer
|
|
19
19
|
* background never shows through. `canvasSubtle` stays the visible raised
|
|
20
20
|
* surface on top of it. DSH_TUI_TRANSPARENT=1 reverts to the old
|
package/lib/tui.js
CHANGED
|
@@ -19,26 +19,28 @@
|
|
|
19
19
|
* counts) is maintained incrementally by event listeners and read O(1) here.
|
|
20
20
|
*/
|
|
21
21
|
import { Container, matchesKey, ProcessTerminal, ScrollView, Spacer, Text, TuiAltScreen, VStack, } from '@earendil-works/pi-tui';
|
|
22
|
+
import { CanvasTerminal } from "./canvas-terminal.js";
|
|
22
23
|
import { CwdBorderEditor } from "./editor.js";
|
|
23
24
|
import { mergeKeyBindings, resolveKeyAction } from "./keymap.js";
|
|
24
25
|
import { ansiBg, ansiFg, RESET, resolveTheme } from "./theme/index.js";
|
|
25
26
|
export function startTui(options = {}) {
|
|
26
|
-
const terminal = new ProcessTerminal();
|
|
27
|
+
const terminal = new CanvasTerminal(new ProcessTerminal());
|
|
27
28
|
const tui = new TuiAltScreen(terminal, true);
|
|
28
29
|
// Mutable theme ref: `applyTheme` swaps it and every later read (handle
|
|
29
30
|
// getter, baked closures below) observes the new bundle on the next call.
|
|
30
31
|
let themeRef = resolveTheme(process.env, options.themePreference ?? 'auto');
|
|
31
|
-
// App-owned canvas:
|
|
32
|
-
// canvas background
|
|
33
|
-
// switch recolors the WHOLE screen, not
|
|
34
|
-
// own bg. Without it the terminal
|
|
35
|
-
// unpainted rows and "freezes" on
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
// terminal theme to stay
|
|
32
|
+
// App-owned canvas: the write-stream decorator injects the palette's
|
|
33
|
+
// canvas background before every erase sequence (BCE — see
|
|
34
|
+
// canvas-terminal.ts), so a theme switch recolors the WHOLE screen, not
|
|
35
|
+
// only the surfaces that paint their own bg. Without it the terminal
|
|
36
|
+
// default background shows through the unpainted rows and "freezes" on
|
|
37
|
+
// switch — most visible inside cmux/gostty, where the pane background
|
|
38
|
+
// belongs to the multiplexer. DSH_TUI_TRANSPARENT=1 opts back into the
|
|
39
|
+
// old see-through canvas for users who want their terminal theme to stay
|
|
40
|
+
// visible.
|
|
39
41
|
const paintCanvas = process.env.DSH_TUI_TRANSPARENT !== '1';
|
|
40
42
|
if (paintCanvas)
|
|
41
|
-
|
|
43
|
+
terminal.setCanvasBackground(ansiBg(themeRef.palette.canvas));
|
|
42
44
|
// ------------------------------------------------------------- component tree --
|
|
43
45
|
// Live Todos widget, pinned ABOVE the chat input: a plain Container with
|
|
44
46
|
// auto height — it renders zero rows while empty and grows to its
|
|
@@ -171,9 +173,12 @@ export function startTui(options = {}) {
|
|
|
171
173
|
return;
|
|
172
174
|
themeRef = theme;
|
|
173
175
|
if (paintCanvas)
|
|
174
|
-
|
|
176
|
+
terminal.setCanvasBackground(ansiBg(theme.palette.canvas));
|
|
175
177
|
rebuildEditor();
|
|
176
|
-
|
|
178
|
+
// Forced full redraw: the diff renderer skips content-unchanged rows
|
|
179
|
+
// (blank fillers render as '' under both themes), which would leave
|
|
180
|
+
// them painted with the previous canvas color.
|
|
181
|
+
tui.requestRender(true);
|
|
177
182
|
},
|
|
178
183
|
setEditorAutocompleteProvider(provider) {
|
|
179
184
|
autocompleteProvider = provider;
|
package/lib/tui.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tui.js","sourceRoot":"","sources":["../src/tui.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EACL,SAAS,EACT,UAAU,EACV,eAAe,EACf,UAAU,EACV,MAAM,EACN,IAAI,EACJ,YAAY,EACZ,MAAM,GAGP,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAoC,MAAM,aAAa,CAAA;AAClG,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAuC,MAAM,kBAAkB,CAAA;AAqE3G,MAAM,UAAU,QAAQ,CAAC,UAA2B,EAAE;IACpD,MAAM,QAAQ,GAAG,IAAI,eAAe,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"tui.js","sourceRoot":"","sources":["../src/tui.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiwayds/dsh-tui-pi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "pi-style terminal UI for DeepSeek Harness (dsh) — pi-tui look & feel, dsh slash commands, GitHub light/dark themes, powerline footer",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
"lib",
|
|
31
31
|
"bin",
|
|
32
32
|
"cordis.patch.yml",
|
|
33
|
-
"patches",
|
|
34
33
|
"scripts",
|
|
35
34
|
"templates",
|
|
36
35
|
"README.md",
|
|
@@ -45,8 +44,8 @@
|
|
|
45
44
|
"test": "node --test test/*.test.mjs"
|
|
46
45
|
},
|
|
47
46
|
"dependencies": {
|
|
48
|
-
"@aiwayds/dsh-dcp": "
|
|
49
|
-
"@aiwayds/dsh-subagent-registry": "
|
|
47
|
+
"@aiwayds/dsh-dcp": "0.4.0",
|
|
48
|
+
"@aiwayds/dsh-subagent-registry": "0.1.3",
|
|
50
49
|
"@earendil-works/pi-tui": "0.84.2"
|
|
51
50
|
},
|
|
52
51
|
"devDependencies": {
|
package/scripts/dev-install.mjs
CHANGED
|
@@ -19,8 +19,6 @@
|
|
|
19
19
|
* The profile's package.json must declare BOTH keys pointing at the tarball:
|
|
20
20
|
* "dsh-tui-pi": "file:<repo>/<name>-<version>.tgz" (bundle resolution)
|
|
21
21
|
* "@aiwayds/dsh-tui-pi": "file:<repo>/<name>-<version>.tgz" (loader import)
|
|
22
|
-
* and pnpm-workspace.yaml must carry the pi-tui patchedDependencies (see
|
|
23
|
-
* ~/.dsh/profiles/tui/pnpm-workspace.yaml).
|
|
24
22
|
*
|
|
25
23
|
* After installing: restart dsh (or /reload inside the TUI) to load the new
|
|
26
24
|
* copy.
|
|
@@ -0,0 +1,612 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# diagnose-container-tui.sh — pipe this into a bare Ubuntu 22 container to
|
|
3
|
+
# diagnose why dsh-tui-pi shows a blank TUI after plugin install.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# podman run --rm -i ubuntu:22.04 bash < scripts/diagnose-container-tui.sh
|
|
7
|
+
#
|
|
8
|
+
# Or with docker:
|
|
9
|
+
# docker run --rm -i ubuntu:22.04 bash < scripts/diagnose-container-tui.sh
|
|
10
|
+
#
|
|
11
|
+
# The script is self-contained: it installs Node 22, pnpm, dsh 0.1.0-rc.7,
|
|
12
|
+
# and @aiwayds/dsh-tui-pi@0.7.1, then runs a battery of diagnostic checks.
|
|
13
|
+
# No code is modified — this is purely diagnostic.
|
|
14
|
+
|
|
15
|
+
set -uo pipefail
|
|
16
|
+
|
|
17
|
+
# ---------------------------------------------------------------------------
|
|
18
|
+
# helpers
|
|
19
|
+
# ---------------------------------------------------------------------------
|
|
20
|
+
PASS=0
|
|
21
|
+
FAIL=0
|
|
22
|
+
WARN=0
|
|
23
|
+
STEP=0
|
|
24
|
+
|
|
25
|
+
banner() {
|
|
26
|
+
printf '\n%s\n' "================================================================"
|
|
27
|
+
printf ' %s\n' "$1"
|
|
28
|
+
printf '%s\n\n' "================================================================"
|
|
29
|
+
}
|
|
30
|
+
step() {
|
|
31
|
+
STEP=$((STEP+1))
|
|
32
|
+
printf '\n--- [%d] %s ---\n' "$STEP" "$1"
|
|
33
|
+
}
|
|
34
|
+
ok() { printf ' [OK] %s\n' "$1"; PASS=$((PASS+1)); }
|
|
35
|
+
fail() { printf ' [FAIL] %s\n' "$1"; FAIL=$((FAIL+1)); }
|
|
36
|
+
warn() { printf ' [WARN] %s\n' "$1"; WARN=$((WARN+1)); }
|
|
37
|
+
info() { printf ' [INFO] %s\n' "$1"; }
|
|
38
|
+
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
banner "dsh-tui-pi container TUI diagnostics"
|
|
41
|
+
info "Date: $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
|
|
42
|
+
info "Host: $(uname -a)"
|
|
43
|
+
# ---------------------------------------------------------------------------
|
|
44
|
+
|
|
45
|
+
# ===== 0. Environment setup =====
|
|
46
|
+
step "Installing Node 22 (nodesource) + pnpm + utilities"
|
|
47
|
+
|
|
48
|
+
apt-get update -qq >/dev/null 2>&1
|
|
49
|
+
# script(1) is in bsdutils on Ubuntu; some images have it already
|
|
50
|
+
apt-get install -y -qq curl ca-certificates bsdutils >/dev/null 2>&1 || true
|
|
51
|
+
|
|
52
|
+
# nodesource Node 22
|
|
53
|
+
if ! command -v node &>/dev/null; then
|
|
54
|
+
curl -fsSL https://deb.nodesource.com/setup_22.x | bash - >/dev/null 2>&1
|
|
55
|
+
apt-get install -y -qq nodejs >/dev/null 2>&1
|
|
56
|
+
fi
|
|
57
|
+
info "node $(node --version), npm $(npm --version)"
|
|
58
|
+
|
|
59
|
+
if ! command -v pnpm &>/dev/null; then
|
|
60
|
+
npm install -g pnpm >/dev/null 2>&1
|
|
61
|
+
fi
|
|
62
|
+
info "pnpm $(pnpm --version)"
|
|
63
|
+
|
|
64
|
+
# TERM for TUI rendering
|
|
65
|
+
export TERM=xterm-256color
|
|
66
|
+
info "TERM=$TERM"
|
|
67
|
+
|
|
68
|
+
# ===== 1. Install dsh =====
|
|
69
|
+
step "Installing @deepseek-ai/dsh@0.1.0-rc.7"
|
|
70
|
+
|
|
71
|
+
npm install -g @deepseek-ai/dsh@0.1.0-rc.7 2>&1 | tail -5
|
|
72
|
+
if command -v dsh &>/dev/null; then
|
|
73
|
+
ok "dsh on PATH: $(which dsh)"
|
|
74
|
+
info "dsh --version: $(dsh --version 2>&1 || echo '(flag not supported)')"
|
|
75
|
+
else
|
|
76
|
+
fail "dsh not found on PATH after install"
|
|
77
|
+
echo "ABORT: cannot continue without dsh"
|
|
78
|
+
exit 1
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
# ===== 2. Install dsh-tui-pi plugin =====
|
|
82
|
+
step "Installing @aiwayds/dsh-tui-pi@0.7.1 into profile 'tui'"
|
|
83
|
+
|
|
84
|
+
INSTALL_OUTPUT=$(dsh plugin --profile tui add @aiwayds/dsh-tui-pi@0.7.1 2>&1) || true
|
|
85
|
+
INSTALL_EXIT=$?
|
|
86
|
+
info "dsh plugin add exit code: $INSTALL_EXIT"
|
|
87
|
+
echo "$INSTALL_OUTPUT" | head -30 | sed 's/^/ /'
|
|
88
|
+
if [ "$INSTALL_EXIT" -eq 0 ]; then
|
|
89
|
+
ok "Plugin install reported success (exit 0)"
|
|
90
|
+
else
|
|
91
|
+
fail "Plugin install failed (exit $INSTALL_EXIT)"
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
# ===== 3. Profile directory structure =====
|
|
95
|
+
step "Profile directory structure"
|
|
96
|
+
|
|
97
|
+
PROFILE_DIR="$HOME/.dsh/profiles/tui"
|
|
98
|
+
if [ -d "$PROFILE_DIR" ]; then
|
|
99
|
+
ok "Profile dir exists: $PROFILE_DIR"
|
|
100
|
+
info "Top-level contents:"
|
|
101
|
+
ls -la "$PROFILE_DIR/" 2>&1 | sed 's/^/ /'
|
|
102
|
+
else
|
|
103
|
+
fail "Profile dir does not exist: $PROFILE_DIR"
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
# ===== 4. Check profile bundles =====
|
|
107
|
+
step "Profile bundles / cordis.patch.yml registration"
|
|
108
|
+
|
|
109
|
+
FOUND_BUNDLES=0
|
|
110
|
+
for f in "$PROFILE_DIR/dsh.profile" "$PROFILE_DIR/dsh.profile.yml" "$PROFILE_DIR/dsh.profile.yaml"; do
|
|
111
|
+
if [ -f "$f" ]; then
|
|
112
|
+
ok "Found profile config: $f"
|
|
113
|
+
echo " --- contents ---"
|
|
114
|
+
cat "$f" 2>&1 | sed 's/^/ /'
|
|
115
|
+
echo " --- end ---"
|
|
116
|
+
FOUND_BUNDLES=1
|
|
117
|
+
fi
|
|
118
|
+
done
|
|
119
|
+
|
|
120
|
+
# Also check package.json for bundle references
|
|
121
|
+
if [ -f "$PROFILE_DIR/package.json" ]; then
|
|
122
|
+
info "package.json dependencies:"
|
|
123
|
+
node -e "
|
|
124
|
+
const pkg = JSON.parse(require('fs').readFileSync('$PROFILE_DIR/package.json','utf8'));
|
|
125
|
+
const deps = {...(pkg.dependencies||{}), ...(pkg.devDependencies||{})};
|
|
126
|
+
for (const [k,v] of Object.entries(deps)) {
|
|
127
|
+
console.log(' ' + k + ': ' + v);
|
|
128
|
+
}
|
|
129
|
+
" 2>&1 || true
|
|
130
|
+
|
|
131
|
+
# Check if dsh-tui-pi is in dependencies
|
|
132
|
+
if node -e "
|
|
133
|
+
const pkg = JSON.parse(require('fs').readFileSync('$PROFILE_DIR/package.json','utf8'));
|
|
134
|
+
const deps = {...(pkg.dependencies||{}), ...(pkg.devDependencies||{})};
|
|
135
|
+
const has = deps['@aiwayds/dsh-tui-pi'] || deps['dsh-tui-pi'];
|
|
136
|
+
process.exit(has ? 0 : 1);
|
|
137
|
+
" 2>/dev/null; then
|
|
138
|
+
ok "dsh-tui-pi is listed in profile package.json dependencies"
|
|
139
|
+
else
|
|
140
|
+
fail "dsh-tui-pi NOT found in profile package.json dependencies"
|
|
141
|
+
fi
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
if [ "$FOUND_BUNDLES" -eq 0 ]; then
|
|
145
|
+
# Check if cordis.patch.yml was copied into the profile
|
|
146
|
+
if find "$PROFILE_DIR" -name "cordis.patch.yml" 2>/dev/null | head -1 | grep -q .; then
|
|
147
|
+
ok "cordis.patch.yml found in profile tree"
|
|
148
|
+
else
|
|
149
|
+
warn "No dsh.profile file and no cordis.patch.yml found in profile"
|
|
150
|
+
info "dsh-tui-pi registers via cordis.patch.yml (insert id: tui-pi)"
|
|
151
|
+
info "The plugin loader must discover this file in node_modules"
|
|
152
|
+
fi
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
# ===== 5. Check node_modules for plugin package =====
|
|
156
|
+
step "Plugin package in node_modules"
|
|
157
|
+
|
|
158
|
+
TUI_PKG_DIR=""
|
|
159
|
+
for candidate in \
|
|
160
|
+
"$PROFILE_DIR/node_modules/@aiwayds/dsh-tui-pi" \
|
|
161
|
+
"$PROFILE_DIR/node_modules/dsh-tui-pi"; do
|
|
162
|
+
if [ -d "$candidate" ]; then
|
|
163
|
+
ok "Plugin package found: $candidate"
|
|
164
|
+
TUI_PKG_DIR="$candidate"
|
|
165
|
+
info "Version: $(node -e "console.log(JSON.parse(require('fs').readFileSync('$candidate/package.json','utf8')).version)" 2>&1 || echo 'unreadable')"
|
|
166
|
+
info "lib/ contents:"
|
|
167
|
+
ls "$candidate/lib/" 2>&1 | head -15 | sed 's/^/ /'
|
|
168
|
+
break
|
|
169
|
+
fi
|
|
170
|
+
done
|
|
171
|
+
if [ -z "$TUI_PKG_DIR" ]; then
|
|
172
|
+
fail "dsh-tui-pi package NOT found in node_modules"
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
# ===== 6. cordis.patch.yml verification =====
|
|
176
|
+
step "cordis.patch.yml content (bundle registration id)"
|
|
177
|
+
|
|
178
|
+
if [ -n "$TUI_PKG_DIR" ] && [ -f "$TUI_PKG_DIR/cordis.patch.yml" ]; then
|
|
179
|
+
ok "cordis.patch.yml found in plugin package"
|
|
180
|
+
echo " --- contents ---"
|
|
181
|
+
cat "$TUI_PKG_DIR/cordis.patch.yml" | sed 's/^/ /'
|
|
182
|
+
echo " --- end ---"
|
|
183
|
+
|
|
184
|
+
if grep -q "tui-pi" "$TUI_PKG_DIR/cordis.patch.yml"; then
|
|
185
|
+
ok "Bundle id 'tui-pi' present in patch"
|
|
186
|
+
else
|
|
187
|
+
fail "Bundle id 'tui-pi' NOT found in cordis.patch.yml"
|
|
188
|
+
fi
|
|
189
|
+
else
|
|
190
|
+
fail "cordis.patch.yml NOT found in plugin package"
|
|
191
|
+
fi
|
|
192
|
+
|
|
193
|
+
# ===== 7. @deepseek-ai closure symlinks =====
|
|
194
|
+
step "@deepseek-ai closure (critical: single cordis instance contract)"
|
|
195
|
+
|
|
196
|
+
# The consumer runtime closure: ~/.dsh/profiles/node_modules/@deepseek-ai/
|
|
197
|
+
GLOBAL_CLOSURE="$HOME/.dsh/profiles/node_modules/@deepseek-ai"
|
|
198
|
+
if [ -d "$GLOBAL_CLOSURE" ]; then
|
|
199
|
+
ok "Global closure exists: $GLOBAL_CLOSURE"
|
|
200
|
+
info "Contents:"
|
|
201
|
+
ls -la "$GLOBAL_CLOSURE/" 2>&1 | sed 's/^/ /'
|
|
202
|
+
|
|
203
|
+
# Check if cordis is symlink or physical (AGENTS.md iron rule 8)
|
|
204
|
+
CORDIS_PATH="$GLOBAL_CLOSURE/cordis"
|
|
205
|
+
if [ -e "$CORDIS_PATH" ]; then
|
|
206
|
+
if [ -L "$CORDIS_PATH" ]; then
|
|
207
|
+
ok "cordis is a symlink (correct — single closure)"
|
|
208
|
+
info " -> $(readlink "$CORDIS_PATH")"
|
|
209
|
+
else
|
|
210
|
+
fail "cordis is a PHYSICAL directory (WRONG — causes dual cordis instances)"
|
|
211
|
+
info " This triggers 'Cannot read properties of undefined (reading prepare)' crash"
|
|
212
|
+
fi
|
|
213
|
+
else
|
|
214
|
+
fail "cordis NOT found in global closure"
|
|
215
|
+
fi
|
|
216
|
+
else
|
|
217
|
+
warn "Global closure dir not found: $GLOBAL_CLOSURE"
|
|
218
|
+
info "Checking plugin-local @deepseek-ai..."
|
|
219
|
+
PLUGIN_CLOSURE="${TUI_PKG_DIR:+$TUI_PKG_DIR/node_modules/@deepseek-ai}"
|
|
220
|
+
if [ -n "$PLUGIN_CLOSURE" ] && [ -d "$PLUGIN_CLOSURE" ]; then
|
|
221
|
+
info "Plugin-local @deepseek-ai found: $PLUGIN_CLOSURE"
|
|
222
|
+
ls -la "$PLUGIN_CLOSURE/" 2>&1 | sed 's/^/ /'
|
|
223
|
+
else
|
|
224
|
+
fail "No @deepseek-ai found anywhere — plugin cannot resolve cordis types"
|
|
225
|
+
fi
|
|
226
|
+
fi
|
|
227
|
+
|
|
228
|
+
# dsh global install's own closure
|
|
229
|
+
DSH_BIN=$(which dsh 2>/dev/null || echo "")
|
|
230
|
+
if [ -n "$DSH_BIN" ]; then
|
|
231
|
+
DSH_REAL=$(realpath "$DSH_BIN" 2>/dev/null || echo "$DSH_BIN")
|
|
232
|
+
DSH_GLOBAL_CLOSURE="$(dirname "$(dirname "$DSH_REAL")")/node_modules/@deepseek-ai"
|
|
233
|
+
if [ -d "$DSH_GLOBAL_CLOSURE" ]; then
|
|
234
|
+
ok "dsh global closure: $DSH_GLOBAL_CLOSURE"
|
|
235
|
+
info "Contents:"
|
|
236
|
+
ls "$DSH_GLOBAL_CLOSURE/" 2>&1 | sed 's/^/ /'
|
|
237
|
+
else
|
|
238
|
+
warn "dsh global closure not at expected path: $DSH_GLOBAL_CLOSURE"
|
|
239
|
+
fi
|
|
240
|
+
fi
|
|
241
|
+
|
|
242
|
+
# ===== 8. Node module resolution test =====
|
|
243
|
+
step "Module resolution: can the plugin resolve @deepseek-ai/cordis?"
|
|
244
|
+
|
|
245
|
+
if [ -n "$TUI_PKG_DIR" ]; then
|
|
246
|
+
RESOLVE_TEST=$(node -e "
|
|
247
|
+
try {
|
|
248
|
+
const path = require.resolve('@deepseek-ai/cordis', { paths: ['$TUI_PKG_DIR'] });
|
|
249
|
+
console.log('RESOLVED: ' + path);
|
|
250
|
+
} catch (e) {
|
|
251
|
+
console.log('FAILED: ' + e.message);
|
|
252
|
+
}
|
|
253
|
+
" 2>&1)
|
|
254
|
+
info "@deepseek-ai/cordis from plugin dir:"
|
|
255
|
+
echo " $RESOLVE_TEST"
|
|
256
|
+
|
|
257
|
+
if echo "$RESOLVE_TEST" | grep -q "RESOLVED"; then
|
|
258
|
+
ok "@deepseek-ai/cordis resolves from plugin directory"
|
|
259
|
+
else
|
|
260
|
+
fail "@deepseek-ai/cordis does NOT resolve from plugin directory"
|
|
261
|
+
info "This is the most common root cause of blank TUI"
|
|
262
|
+
fi
|
|
263
|
+
|
|
264
|
+
# Also check pi-tui
|
|
265
|
+
PIUI_TEST=$(node -e "
|
|
266
|
+
try {
|
|
267
|
+
const path = require.resolve('@earendil-works/pi-tui', { paths: ['$TUI_PKG_DIR'] });
|
|
268
|
+
console.log('RESOLVED: ' + path);
|
|
269
|
+
} catch (e) {
|
|
270
|
+
console.log('FAILED: ' + e.message);
|
|
271
|
+
}
|
|
272
|
+
" 2>&1)
|
|
273
|
+
info "@earendil-works/pi-tui from plugin dir:"
|
|
274
|
+
echo " $PIUI_TEST"
|
|
275
|
+
|
|
276
|
+
if echo "$PIUI_TEST" | grep -q "RESOLVED"; then
|
|
277
|
+
ok "@earendil-works/pi-tui resolves"
|
|
278
|
+
else
|
|
279
|
+
fail "@earendil-works/pi-tui does NOT resolve"
|
|
280
|
+
fi
|
|
281
|
+
fi
|
|
282
|
+
|
|
283
|
+
# ===== 9. pi-tui native bindings (informational — Linux degrades gracefully) =====
|
|
284
|
+
step "pi-tui native bindings (informational — Linux has no native helpers)"
|
|
285
|
+
|
|
286
|
+
if [ -n "$TUI_PKG_DIR" ]; then
|
|
287
|
+
PIUI_PKG=$(node -e "
|
|
288
|
+
try { console.log(require.resolve('@earendil-works/pi-tui/package.json', { paths: ['$TUI_PKG_DIR'] }).replace('/package.json','')); }
|
|
289
|
+
catch { console.log(''); }
|
|
290
|
+
" 2>/dev/null || echo "")
|
|
291
|
+
|
|
292
|
+
if [ -n "$PIUI_PKG" ] && [ -d "$PIUI_PKG" ]; then
|
|
293
|
+
info "pi-tui package: $PIUI_PKG"
|
|
294
|
+
if [ -d "$PIUI_PKG/native" ]; then
|
|
295
|
+
info "Native platforms shipped:"
|
|
296
|
+
ls "$PIUI_PKG/native/" 2>&1 | sed 's/^/ /'
|
|
297
|
+
|
|
298
|
+
# Check linux availability
|
|
299
|
+
if [ -d "$PIUI_PKG/native/linux" ]; then
|
|
300
|
+
ok "Linux native bindings exist"
|
|
301
|
+
else
|
|
302
|
+
info "No linux native bindings — but this is EXPECTED and harmless"
|
|
303
|
+
info "pi-tui native-modifiers.js returns undefined on process.platform !== 'darwin'/'win32'"
|
|
304
|
+
info "The native helpers are only for darwin modifier keys and win32 console mode"
|
|
305
|
+
info "Linux terminal input works without native bindings (process.stdin raw mode)"
|
|
306
|
+
fi
|
|
307
|
+
fi
|
|
308
|
+
fi
|
|
309
|
+
fi
|
|
310
|
+
|
|
311
|
+
# ===== 10. dsh --help — command registration check =====
|
|
312
|
+
step "dsh --profile tui --help (are TUI commands visible?)"
|
|
313
|
+
|
|
314
|
+
HELP_OUTPUT=$(dsh --profile tui --help 2>&1) || true
|
|
315
|
+
echo "$HELP_OUTPUT" | head -30 | sed 's/^/ /'
|
|
316
|
+
|
|
317
|
+
TUI_CMDS=0
|
|
318
|
+
for cmd in "/login" "/logout" "/settings" "/model" "/theme" "/session" "/resume" "/hotkeys" "/reload"; do
|
|
319
|
+
if echo "$HELP_OUTPUT" | grep -qi "$cmd"; then
|
|
320
|
+
ok "Command in help: $cmd"
|
|
321
|
+
TUI_CMDS=$((TUI_CMDS+1))
|
|
322
|
+
fi
|
|
323
|
+
done
|
|
324
|
+
if [ "$TUI_CMDS" -eq 0 ]; then
|
|
325
|
+
fail "No dsh-tui-pi commands found in --help output"
|
|
326
|
+
info "This suggests the plugin is NOT being loaded by dsh"
|
|
327
|
+
else
|
|
328
|
+
ok "$TUI_CMDS dsh-tui-pi commands found in --help"
|
|
329
|
+
fi
|
|
330
|
+
|
|
331
|
+
# ===== 11. dsh --profile tui --version =====
|
|
332
|
+
step "dsh --profile tui --version (non-interactive sanity check)"
|
|
333
|
+
|
|
334
|
+
VERSION_OUTPUT=$(dsh --profile tui --version 2>&1) || true
|
|
335
|
+
info "Output:"
|
|
336
|
+
echo "$VERSION_OUTPUT" | sed 's/^/ /'
|
|
337
|
+
if echo "$VERSION_OUTPUT" | grep -qi "dsh-tui-pi\|0\.7\.1\|tui-pi"; then
|
|
338
|
+
ok "Version output mentions dsh-tui-pi or version"
|
|
339
|
+
else
|
|
340
|
+
warn "Version output does not mention dsh-tui-pi"
|
|
341
|
+
fi
|
|
342
|
+
|
|
343
|
+
# ===== 12. Check if dsh exits cleanly without TTY =====
|
|
344
|
+
step "dsh --profile tui exit behavior (no TTY, 5s timeout)"
|
|
345
|
+
|
|
346
|
+
EXIT_LOG="/tmp/dsh-exit-test.log"
|
|
347
|
+
START_TS=$(date +%s)
|
|
348
|
+
timeout 5 dsh --profile tui </dev/null >"$EXIT_LOG" 2>&1 || true
|
|
349
|
+
EXIT_CODE=$?
|
|
350
|
+
END_TS=$(date +%s)
|
|
351
|
+
DURATION=$((END_TS - START_TS))
|
|
352
|
+
|
|
353
|
+
info "Exit code: $EXIT_CODE (after ${DURATION}s)"
|
|
354
|
+
info "Output bytes: $(wc -c < "$EXIT_LOG")"
|
|
355
|
+
|
|
356
|
+
if [ "$EXIT_CODE" -eq 124 ]; then
|
|
357
|
+
info "Exit 124 = timeout (TUI started but did not exit on its own)"
|
|
358
|
+
info "This is expected: TUI waits for user input"
|
|
359
|
+
ok "TUI process is alive (timeout = it ran for 5 seconds)"
|
|
360
|
+
elif [ "$EXIT_CODE" -eq 0 ]; then
|
|
361
|
+
warn "Exit 0 = TUI exited immediately without error"
|
|
362
|
+
info "Possible cause: TUI detects no TTY and exits gracefully"
|
|
363
|
+
else
|
|
364
|
+
warn "Exit $EXIT_CODE = TUI exited with error"
|
|
365
|
+
fi
|
|
366
|
+
|
|
367
|
+
if [ -s "$EXIT_LOG" ]; then
|
|
368
|
+
info "Exit test output:"
|
|
369
|
+
cat "$EXIT_LOG" | head -30 | cat -v | sed 's/^/ /'
|
|
370
|
+
else
|
|
371
|
+
info "(no output captured)"
|
|
372
|
+
fi
|
|
373
|
+
|
|
374
|
+
# ===== 13. script command capture =====
|
|
375
|
+
step "Terminal capture with 'script -q' (PTY emulation)"
|
|
376
|
+
|
|
377
|
+
SCRIPT_LOG="/tmp/dsh-tui-script.log"
|
|
378
|
+
# script provides a pseudo-TTY, which may let the TUI render
|
|
379
|
+
timeout 5 script -q "$SCRIPT_LOG" -c "dsh --profile tui" </dev/null 2>&1 || true
|
|
380
|
+
|
|
381
|
+
SCRIPT_SIZE=$(wc -c < "$SCRIPT_LOG" 2>/dev/null || echo 0)
|
|
382
|
+
info "script log size: $SCRIPT_SIZE bytes"
|
|
383
|
+
|
|
384
|
+
if [ "$SCRIPT_SIZE" -gt 0 ]; then
|
|
385
|
+
ok "script captured output ($SCRIPT_SIZE bytes)"
|
|
386
|
+
info "First 50 lines (cat -v shows escape sequences):"
|
|
387
|
+
head -50 "$SCRIPT_LOG" | cat -v | sed 's/^/ /'
|
|
388
|
+
|
|
389
|
+
# Check for alt-screen activation: ESC[?1049h
|
|
390
|
+
if cat -v "$SCRIPT_LOG" | grep -q '\[?1049h\|\[2J\|\[H'; then
|
|
391
|
+
ok "Alt-screen / cursor-home escape sequences detected"
|
|
392
|
+
else
|
|
393
|
+
info "No alt-screen escapes (TUI may not have entered alt-screen mode)"
|
|
394
|
+
fi
|
|
395
|
+
|
|
396
|
+
# Strip escape sequences and check for visible text
|
|
397
|
+
VISIBLE=$(sed 's/\x1b\[[0-9;]*[a-zA-Z]//g; s/\x1b\][^\x07]*\x07//g; s/\x1b[()][AB012]//g' "$SCRIPT_LOG" | tr -d '[:cntrl:]' | tr -s ' ')
|
|
398
|
+
if [ -n "$VISIBLE" ] && [ ${#VISIBLE} -gt 10 ]; then
|
|
399
|
+
ok "Visible text content found after stripping escapes"
|
|
400
|
+
info "Cleaned text (first 300 chars):"
|
|
401
|
+
echo "$VISIBLE" | head -c 300 | sed 's/^/ /'
|
|
402
|
+
else
|
|
403
|
+
fail "No visible text content — TUI output is blank or all-escape"
|
|
404
|
+
info "This confirms the blank TUI symptom"
|
|
405
|
+
fi
|
|
406
|
+
else
|
|
407
|
+
fail "script captured zero bytes — TUI produced no output at all"
|
|
408
|
+
fi
|
|
409
|
+
|
|
410
|
+
# ===== 14. Direct pipe capture =====
|
|
411
|
+
step "Direct pipe capture: dsh --profile tui 2>&1 (no PTY)"
|
|
412
|
+
|
|
413
|
+
PIPE_LOG="/tmp/dsh-tui-pipe.log"
|
|
414
|
+
timeout 5 dsh --profile tui </dev/null >"$PIPE_LOG" 2>&1 || true
|
|
415
|
+
|
|
416
|
+
PIPE_SIZE=$(wc -c < "$PIPE_LOG" 2>/dev/null || echo 0)
|
|
417
|
+
info "Pipe log size: $PIPE_SIZE bytes"
|
|
418
|
+
|
|
419
|
+
if [ "$PIPE_SIZE" -gt 0 ]; then
|
|
420
|
+
ok "Pipe captured output ($PIPE_SIZE bytes)"
|
|
421
|
+
info "Raw output (cat -v):"
|
|
422
|
+
cat -v "$PIPE_LOG" | head -50 | sed 's/^/ /'
|
|
423
|
+
|
|
424
|
+
# Check for TUI content markers
|
|
425
|
+
if cat "$PIPE_LOG" | grep -qi "dsh-tui-pi\|pi-tui\|whale\|DSH TUI\|welcome"; then
|
|
426
|
+
ok "TUI content markers found in pipe output"
|
|
427
|
+
else
|
|
428
|
+
info "No TUI content markers in pipe output"
|
|
429
|
+
fi
|
|
430
|
+
else
|
|
431
|
+
fail "Pipe captured zero bytes — TUI is silent without a PTY"
|
|
432
|
+
fi
|
|
433
|
+
|
|
434
|
+
# ===== 15. stderr-only capture =====
|
|
435
|
+
step "Stderr capture (error diagnostics)"
|
|
436
|
+
|
|
437
|
+
STDERR_LOG="/tmp/dsh-stderr.log"
|
|
438
|
+
timeout 5 dsh --profile tui </dev/null 2>"$STDERR_LOG" || true
|
|
439
|
+
|
|
440
|
+
STDERR_SIZE=$(wc -c < "$STDERR_LOG" 2>/dev/null || echo 0)
|
|
441
|
+
info "Stderr size: $STDERR_SIZE bytes"
|
|
442
|
+
|
|
443
|
+
if [ "$STDERR_SIZE" -gt 0 ]; then
|
|
444
|
+
warn "Stderr has output (may contain errors):"
|
|
445
|
+
cat "$STDERR_LOG" | head -40 | sed 's/^/ /'
|
|
446
|
+
else
|
|
447
|
+
info "Stderr is empty (no error output)"
|
|
448
|
+
fi
|
|
449
|
+
|
|
450
|
+
# ===== 16. DSH home directory inspection =====
|
|
451
|
+
step "DSH home directory (~/.dsh/)"
|
|
452
|
+
|
|
453
|
+
if [ -d "$HOME/.dsh" ]; then
|
|
454
|
+
ok "$HOME/.dsh exists"
|
|
455
|
+
info "Top-level:"
|
|
456
|
+
ls -la "$HOME/.dsh/" 2>&1 | sed 's/^/ /'
|
|
457
|
+
|
|
458
|
+
# Log files
|
|
459
|
+
info "Looking for log files..."
|
|
460
|
+
LOGS=$(find "$HOME/.dsh" -maxdepth 3 \( -name "*.log" -o -name "debug*" -o -name "error*" -o -name "crash*" \) 2>/dev/null)
|
|
461
|
+
if [ -n "$LOGS" ]; then
|
|
462
|
+
ok "Log files found:"
|
|
463
|
+
echo "$LOGS" | sed 's/^/ /'
|
|
464
|
+
# Show last few lines of each log
|
|
465
|
+
for lf in $LOGS; do
|
|
466
|
+
info "--- $lf (last 10 lines) ---"
|
|
467
|
+
tail -10 "$lf" 2>&1 | sed 's/^/ /'
|
|
468
|
+
done
|
|
469
|
+
else
|
|
470
|
+
info "No log files found in ~/.dsh/"
|
|
471
|
+
fi
|
|
472
|
+
|
|
473
|
+
# settings.yaml
|
|
474
|
+
if [ -f "$HOME/.dsh/settings.yaml" ]; then
|
|
475
|
+
ok "settings.yaml exists"
|
|
476
|
+
info "Contents:"
|
|
477
|
+
cat "$HOME/.dsh/settings.yaml" | sed 's/^/ /'
|
|
478
|
+
else
|
|
479
|
+
warn "No settings.yaml (fresh install, no config yet)"
|
|
480
|
+
fi
|
|
481
|
+
|
|
482
|
+
# .credentials.yaml
|
|
483
|
+
if [ -f "$HOME/.dsh/.credentials.yaml" ]; then
|
|
484
|
+
ok ".credentials.yaml exists"
|
|
485
|
+
else
|
|
486
|
+
warn "No .credentials.yaml — /login may be required"
|
|
487
|
+
info "TUI might show blank if it blocks on authentication"
|
|
488
|
+
fi
|
|
489
|
+
else
|
|
490
|
+
fail "$HOME/.dsh does not exist"
|
|
491
|
+
fi
|
|
492
|
+
|
|
493
|
+
# ===== 17. NODE_DEBUG=module trace =====
|
|
494
|
+
step "Module loading trace (NODE_DEBUG=module)"
|
|
495
|
+
|
|
496
|
+
MODULE_LOG="/tmp/dsh-module-debug.log"
|
|
497
|
+
timeout 5 NODE_DEBUG=module dsh --profile tui </dev/null 2>"$MODULE_LOG" || true
|
|
498
|
+
|
|
499
|
+
MODULE_SIZE=$(wc -c < "$MODULE_LOG" 2>/dev/null || echo 0)
|
|
500
|
+
info "Module debug log: $MODULE_SIZE bytes"
|
|
501
|
+
|
|
502
|
+
if [ "$MODULE_SIZE" -gt 0 ]; then
|
|
503
|
+
# Check for plugin loading
|
|
504
|
+
if grep -i "dsh-tui-pi\|tui-pi\|@aiwayds" "$MODULE_LOG" >/dev/null 2>&1; then
|
|
505
|
+
ok "Module trace mentions dsh-tui-pi"
|
|
506
|
+
grep -i "dsh-tui-pi\|tui-pi\|@aiwayds" "$MODULE_LOG" | head -5 | sed 's/^/ /'
|
|
507
|
+
else
|
|
508
|
+
fail "Module trace does NOT mention dsh-tui-pi — plugin never loaded"
|
|
509
|
+
fi
|
|
510
|
+
|
|
511
|
+
# Check for errors
|
|
512
|
+
ERRORS=$(grep -i "error\|ERR_\|MODULE_NOT_FOUND\|cannot find\|not found" "$MODULE_LOG" 2>/dev/null | head -10)
|
|
513
|
+
if [ -n "$ERRORS" ]; then
|
|
514
|
+
warn "Errors in module debug:"
|
|
515
|
+
echo "$ERRORS" | sed 's/^/ /'
|
|
516
|
+
fi
|
|
517
|
+
else
|
|
518
|
+
warn "Module debug log empty"
|
|
519
|
+
fi
|
|
520
|
+
|
|
521
|
+
# ===== 18. dsh profile list =====
|
|
522
|
+
step "dsh profile list"
|
|
523
|
+
|
|
524
|
+
PROFILE_LIST=$(dsh profile list 2>&1) || true
|
|
525
|
+
echo "$PROFILE_LIST" | sed 's/^/ /'
|
|
526
|
+
if echo "$PROFILE_LIST" | grep -qi "tui"; then
|
|
527
|
+
ok "Profile 'tui' in profile list"
|
|
528
|
+
else
|
|
529
|
+
warn "Profile 'tui' not in profile list"
|
|
530
|
+
fi
|
|
531
|
+
|
|
532
|
+
# ===== 19. dsh plugin list =====
|
|
533
|
+
step "dsh plugin --profile tui list"
|
|
534
|
+
|
|
535
|
+
PLUGIN_LIST=$(dsh plugin --profile tui list 2>&1) || true
|
|
536
|
+
echo "$PLUGIN_LIST" | sed 's/^/ /'
|
|
537
|
+
if echo "$PLUGIN_LIST" | grep -qi "dsh-tui-pi\|tui-pi\|@aiwayds"; then
|
|
538
|
+
ok "dsh-tui-pi in plugin list"
|
|
539
|
+
else
|
|
540
|
+
fail "dsh-tui-pi NOT in plugin list"
|
|
541
|
+
fi
|
|
542
|
+
|
|
543
|
+
# ===== 20. Full plugin node_modules listing =====
|
|
544
|
+
step "Full node_modules listing for @deepseek-ai and @earendil-works"
|
|
545
|
+
|
|
546
|
+
if [ -n "$TUI_PKG_DIR" ]; then
|
|
547
|
+
info "@deepseek-ai in plugin node_modules:"
|
|
548
|
+
ls -la "$TUI_PKG_DIR/node_modules/@deepseek-ai/" 2>&1 | sed 's/^/ /' || info " (not found)"
|
|
549
|
+
|
|
550
|
+
info "@earendil-works in plugin node_modules:"
|
|
551
|
+
ls -la "$TUI_PKG_DIR/node_modules/@earendil-works/" 2>&1 | sed 's/^/ /' || info " (not found)"
|
|
552
|
+
fi
|
|
553
|
+
|
|
554
|
+
# Also check the profile-level node_modules
|
|
555
|
+
info "@deepseek-ai in profile node_modules:"
|
|
556
|
+
ls -la "$PROFILE_DIR/node_modules/@deepseek-ai/" 2>&1 | sed 's/^/ /' || info " (not found)"
|
|
557
|
+
|
|
558
|
+
# ===== 21. strace-lite: check if process opens any files =====
|
|
559
|
+
step "Process activity trace (strace-lite via /proc)"
|
|
560
|
+
|
|
561
|
+
# On Ubuntu, we can check /proc for the dsh process
|
|
562
|
+
timeout 3 bash -c '
|
|
563
|
+
dsh --profile tui </dev/null &
|
|
564
|
+
PID=$!
|
|
565
|
+
sleep 1
|
|
566
|
+
if [ -d "/proc/$PID" ]; then
|
|
567
|
+
echo " [INFO] dsh process alive as PID $PID"
|
|
568
|
+
echo " [INFO] Open file descriptors:"
|
|
569
|
+
ls -la /proc/$PID/fd 2>/dev/null | tail -20 | sed "s/^/ /"
|
|
570
|
+
echo " [INFO] Memory maps (node modules loaded):"
|
|
571
|
+
grep "node_modules\|pi-tui\|dsh-tui" /proc/$PID/maps 2>/dev/null | head -10 | sed "s/^/ /"
|
|
572
|
+
else
|
|
573
|
+
echo " [INFO] dsh process already exited"
|
|
574
|
+
fi
|
|
575
|
+
wait $PID 2>/dev/null || true
|
|
576
|
+
' 2>&1 || true
|
|
577
|
+
|
|
578
|
+
# ===== Summary =====
|
|
579
|
+
banner "DIAGNOSTIC SUMMARY"
|
|
580
|
+
echo ""
|
|
581
|
+
printf " PASS: %d\n" "$PASS"
|
|
582
|
+
printf " FAIL: %d\n" "$FAIL"
|
|
583
|
+
printf " WARN: %d\n" "$WARN"
|
|
584
|
+
echo ""
|
|
585
|
+
|
|
586
|
+
if [ "$FAIL" -gt 0 ]; then
|
|
587
|
+
echo " ISSUES DETECTED — review [FAIL] items above."
|
|
588
|
+
echo ""
|
|
589
|
+
echo " Common root causes for blank TUI in containers:"
|
|
590
|
+
echo " 1. @deepseek-ai/cordis not resolvable from plugin (closure broken)"
|
|
591
|
+
echo " 2. Plugin not registered in profile bundles (cordis.patch.yml missing)"
|
|
592
|
+
echo " 3. No TTY — pi-tui TuiAltScreen requires a real terminal"
|
|
593
|
+
echo " (podman run -i without -t gives stdin but no PTY)"
|
|
594
|
+
echo " 4. Missing .credentials.yaml — TUI may block waiting for /login"
|
|
595
|
+
echo " 5. pi-tui process.platform check fails silently on unexpected env"
|
|
596
|
+
echo ""
|
|
597
|
+
echo " NOTE: pi-tui native bindings (darwin-modifiers, win32-console-mode)"
|
|
598
|
+
echo " are NOT needed on Linux — native-modifiers.js returns undefined"
|
|
599
|
+
echo " gracefully for platform !== 'darwin'/'win32'. This is NOT the cause."
|
|
600
|
+
else
|
|
601
|
+
echo " All checks passed."
|
|
602
|
+
echo " Blank TUI is likely a rendering issue (no PTY or terminal detection)."
|
|
603
|
+
fi
|
|
604
|
+
|
|
605
|
+
echo ""
|
|
606
|
+
echo " Diagnostic logs:"
|
|
607
|
+
echo " /tmp/dsh-tui-script.log (script PTY capture)"
|
|
608
|
+
echo " /tmp/dsh-tui-pipe.log (direct pipe capture)"
|
|
609
|
+
echo " /tmp/dsh-stderr.log (stderr)"
|
|
610
|
+
echo " /tmp/dsh-module-debug.log (NODE_DEBUG=module)"
|
|
611
|
+
echo " /tmp/dsh-exit-test.log (exit behavior)"
|
|
612
|
+
echo ""
|
|
@@ -1,276 +0,0 @@
|
|
|
1
|
-
diff --git a/dist/components/editor.d.ts b/dist/components/editor.d.ts
|
|
2
|
-
index a6fedc9e3b36d066e34860d040db6df47d88c432..f4d9afd8bc64f5ce5aafa06bb1aa8db3338c4f7d 100644
|
|
3
|
-
--- a/dist/components/editor.d.ts
|
|
4
|
-
+++ b/dist/components/editor.d.ts
|
|
5
|
-
@@ -24,6 +24,8 @@ export interface TextChunk {
|
|
6
|
-
export declare function wordWrapLine(line: string, maxWidth: number, preSegmented?: Intl.SegmentData[]): TextChunk[];
|
|
7
|
-
export interface EditorTheme {
|
|
8
|
-
borderColor: (str: string) => string;
|
|
9
|
-
+ /** Optional style for the editor's input text rows. Absent → the terminal default foreground is used (invisible on an app-painted dark canvas). */
|
|
10
|
-
+ textColor?: (str: string) => string;
|
|
11
|
-
selectList: SelectListTheme;
|
|
12
|
-
}
|
|
13
|
-
export interface EditorOptions {
|
|
14
|
-
diff --git a/dist/components/editor.js b/dist/components/editor.js
|
|
15
|
-
index 4786b46932b65785cfbd2304610ca77ccead910a..93c5f7e1596a0b567cf9fc59600c16596a60e567 100644
|
|
16
|
-
--- a/dist/components/editor.js
|
|
17
|
-
+++ b/dist/components/editor.js
|
|
18
|
-
@@ -433,13 +433,13 @@ export class Editor {
|
|
19
|
-
const afterGraphemes = [...this.segment(after, "grapheme")];
|
|
20
|
-
const firstGrapheme = afterGraphemes[0]?.segment || "";
|
|
21
|
-
const restAfter = after.slice(firstGrapheme.length);
|
|
22
|
-
- const cursor = `\x1b[7m${firstGrapheme}\x1b[0m`;
|
|
23
|
-
+ const cursor = `\x1b[7m${firstGrapheme}\x1b[27m`;
|
|
24
|
-
displayText = before + marker + cursor + restAfter;
|
|
25
|
-
// lineVisibleWidth stays the same - we're replacing, not adding
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
// Cursor is at the end - add highlighted space
|
|
29
|
-
- const cursor = "\x1b[7m \x1b[0m";
|
|
30
|
-
+ const cursor = "\x1b[7m \x1b[27m";
|
|
31
|
-
displayText = before + marker + cursor;
|
|
32
|
-
lineVisibleWidth = lineVisibleWidth + 1;
|
|
33
|
-
// If cursor overflows content width into the padding, flag it
|
|
34
|
-
@@ -451,8 +451,14 @@ export class Editor {
|
|
35
|
-
// Calculate padding based on actual visible width
|
|
36
|
-
const padding = " ".repeat(Math.max(0, contentWidth - lineVisibleWidth));
|
|
37
|
-
const lineRightPadding = cursorInPadding ? rightPadding.slice(1) : rightPadding;
|
|
38
|
-
- // Render the line (no side borders, just horizontal lines above and below)
|
|
39
|
-
- result.push(`${leftPadding}${displayText}${padding}${lineRightPadding}`);
|
|
40
|
-
+ // Render the line (no side borders, just horizontal lines above and below).
|
|
41
|
-
+ // The theme's optional textColor styles the input rows — without it they
|
|
42
|
-
+ // fall back to the terminal default foreground, which is invisible on an
|
|
43
|
-
+ // app-painted dark canvas. Applied per line; the cursor's reverse-video
|
|
44
|
-
+ // closes with ESC[27m (reverse off, not full reset) so the themed
|
|
45
|
-
+ // foreground survives past a mid-text cursor.
|
|
46
|
-
+ const styledText = this.theme.textColor ? this.theme.textColor(displayText) : displayText;
|
|
47
|
-
+ result.push(`${leftPadding}${styledText}${padding}${lineRightPadding}`);
|
|
48
|
-
}
|
|
49
|
-
// Render bottom border (with scroll indicator if more content below)
|
|
50
|
-
const linesBelow = layoutLines.length - (this.scrollOffset + visibleLines.length);
|
|
51
|
-
@@ -463,14 +469,20 @@ export class Editor {
|
|
52
|
-
else {
|
|
53
|
-
result.push(horizontal.repeat(width));
|
|
54
|
-
}
|
|
55
|
-
- // Add autocomplete list if active
|
|
56
|
-
+ // Add autocomplete list if active — inside a full box (┌─┐ rows)
|
|
57
|
-
if (this.autocompleteState && this.autocompleteList) {
|
|
58
|
-
- const autocompleteResult = this.autocompleteList.render(contentWidth);
|
|
59
|
-
+ const boxInnerWidth = Math.max(1, contentWidth - 2);
|
|
60
|
-
+ const autocompleteResult = this.autocompleteList.render(boxInnerWidth);
|
|
61
|
-
+ result.push(`${leftPadding}${this.borderColor(`┌${"─".repeat(boxInnerWidth)}┐`)}${rightPadding}`);
|
|
62
|
-
for (const line of autocompleteResult) {
|
|
63
|
-
const lineWidth = visibleWidth(line);
|
|
64
|
-
- const linePadding = " ".repeat(Math.max(0, contentWidth - lineWidth));
|
|
65
|
-
- result.push(`${leftPadding}${line}${linePadding}${rightPadding}`);
|
|
66
|
-
+ const linePadding = " ".repeat(Math.max(0, boxInnerWidth - lineWidth));
|
|
67
|
-
+ // Strip trailing RESETs so the row's own backdrop SGR bleeds
|
|
68
|
-
+ // into the padding and the right side border (solid interior);
|
|
69
|
-
+ // the reset before rightPadding keeps the editor margin clear.
|
|
70
|
-
+ result.push(`${leftPadding}│${line.replace(/(?:\x1b\[0m)+$/, "")}${linePadding}│\x1b[0m${rightPadding}`);
|
|
71
|
-
}
|
|
72
|
-
+ result.push(`${leftPadding}${this.borderColor(`└${"─".repeat(boxInnerWidth)}┘`)}${rightPadding}`);
|
|
73
|
-
}
|
|
74
|
-
return result;
|
|
75
|
-
}
|
|
76
|
-
diff --git a/dist/components/select-list.d.ts b/dist/components/select-list.d.ts
|
|
77
|
-
index d507de4ebff8e7fbaacd089677099c63b4dfff6c..4b8a374cd1db0544a11082f8008f73f8eb8d7ede 100644
|
|
78
|
-
--- a/dist/components/select-list.d.ts
|
|
79
|
-
+++ b/dist/components/select-list.d.ts
|
|
80
|
-
@@ -10,6 +10,8 @@ export interface SelectListTheme {
|
|
81
|
-
description: (text: string) => string;
|
|
82
|
-
scrollInfo: (text: string) => string;
|
|
83
|
-
noMatch: (text: string) => string;
|
|
84
|
-
+ /** Style hook for unselected rows (plain rows render without any theme call upstream). */
|
|
85
|
-
+ unselectedText?: (text: string) => string;
|
|
86
|
-
}
|
|
87
|
-
export interface SelectListTruncatePrimaryContext {
|
|
88
|
-
text: string;
|
|
89
|
-
diff --git a/dist/components/select-list.js b/dist/components/select-list.js
|
|
90
|
-
index 2e9231150951c3d3f3e13ac8ae8d623f06d38d5d..d5c8859ecd08fe4eccdc3cfca8ac7a3226f8fcb5 100644
|
|
91
|
-
--- a/dist/components/select-list.js
|
|
92
|
-
+++ b/dist/components/select-list.js
|
|
93
|
-
@@ -101,18 +101,21 @@ export class SelectList {
|
|
94
|
-
if (remainingWidth > MIN_DESCRIPTION_WIDTH) {
|
|
95
|
-
const truncatedDesc = truncateToWidth(descriptionSingleLine, remainingWidth, "");
|
|
96
|
-
if (isSelected) {
|
|
97
|
-
- return this.theme.selectedText(`${prefix}${truncatedValue}${spacing}${truncatedDesc}`);
|
|
98
|
-
+ // selectedPrefix is otherwise dead in renderItem: style the
|
|
99
|
-
+ // arrow accent, then the row text (concatenated, not nested —
|
|
100
|
-
+ // nesting would let the prefix's RESETs clear the row style).
|
|
101
|
-
+ return this.theme.selectedPrefix("") + this.theme.selectedText(`${truncatedValue}${spacing}${truncatedDesc}`);
|
|
102
|
-
}
|
|
103
|
-
const descText = this.theme.description(spacing + truncatedDesc);
|
|
104
|
-
- return prefix + truncatedValue + descText;
|
|
105
|
-
+ return (this.theme.unselectedText ?? ((t) => t))(prefix + truncatedValue) + descText;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
const maxWidth = width - prefixWidth - 2;
|
|
109
|
-
const truncatedValue = this.truncatePrimary(item, isSelected, maxWidth, maxWidth);
|
|
110
|
-
if (isSelected) {
|
|
111
|
-
- return this.theme.selectedText(`${prefix}${truncatedValue}`);
|
|
112
|
-
+ return this.theme.selectedPrefix("") + this.theme.selectedText(`${truncatedValue}`);
|
|
113
|
-
}
|
|
114
|
-
- return prefix + truncatedValue;
|
|
115
|
-
+ return (this.theme.unselectedText ?? ((t) => t))(prefix + truncatedValue);
|
|
116
|
-
}
|
|
117
|
-
getPrimaryColumnWidth() {
|
|
118
|
-
const { min, max } = this.getPrimaryColumnBounds();
|
|
119
|
-
diff --git a/dist/index.d.ts b/dist/index.d.ts
|
|
120
|
-
index 50b5aeb61f12011642978bc2c31d8a2eb36d17d3..28b70df8001dc6b78056c408bdd6016f34f44e46 100644
|
|
121
|
-
--- a/dist/index.d.ts
|
|
122
|
-
+++ b/dist/index.d.ts
|
|
123
|
-
@@ -25,7 +25,7 @@ export { ProcessTerminal, type Terminal } from "./terminal.ts";
|
|
124
|
-
export { parseOsc11BackgroundColor, parseTerminalColorSchemeReport, type RgbColor, type TerminalColorScheme, } from "./terminal-colors.ts";
|
|
125
|
-
export { allocateImageId, type CellDimensions, calculateImageRows, deleteAllKittyImages, deleteKittyImage, detectCapabilities, encodeITerm2, encodeKitty, getCapabilities, getCellDimensions, getGifDimensions, getImageDimensions, getJpegDimensions, getPngDimensions, getWebpDimensions, hyperlink, type ImageDimensions, type ImageProtocol, type ImageRenderOptions, imageFallback, renderImage, resetCapabilitiesCache, setCapabilities, setCellDimensions, type TerminalCapabilities, } from "./terminal-image.ts";
|
|
126
|
-
export { type Component, Container, CURSOR_MARKER, compositeTuiLine, type Focusable, isFocusable, isViewportTUI, type OverlayAnchor, type OverlayHandle, type OverlayMargin, type OverlayOptions, type OverlayUnfocusOptions, type SizeValue, type TUI, type TuiInputListener, type TuiInputListenerResult, type TuiMode, type TuiStopOptions, type ViewportTUI, } from "./tui.ts";
|
|
127
|
-
-export { TuiAltScreen, type TuiAltScreenOptions } from "./tui-alt-screen.ts";
|
|
128
|
-
+export { paintCanvasRow, TuiAltScreen, type TuiAltScreenOptions } from "./tui-alt-screen.ts";
|
|
129
|
-
export { TuiMainScreen, type TuiMainScreenRenderState } from "./tui-main-screen.ts";
|
|
130
|
-
export { getOsc8LinkAtColumn, sliceByColumn, stripTerminalSequences, truncateToWidth, visibleWidth, wrapTextWithAnsi, } from "./utils.ts";
|
|
131
|
-
//# sourceMappingURL=index.d.ts.map
|
|
132
|
-
|
|
133
|
-
diff --git a/dist/index.js b/dist/index.js
|
|
134
|
-
index 97acf774699dded9c5e490f2441a93ec05b73be5..80a1abf6ac515c4df22e3bf78fc0504d50ad6dd4 100644
|
|
135
|
-
--- a/dist/index.js
|
|
136
|
-
+++ b/dist/index.js
|
|
137
|
-
@@ -35,7 +35,7 @@ export { parseOsc11BackgroundColor, parseTerminalColorSchemeReport, } from "./te
|
|
138
|
-
// Terminal image support
|
|
139
|
-
export { allocateImageId, calculateImageRows, deleteAllKittyImages, deleteKittyImage, detectCapabilities, encodeITerm2, encodeKitty, getCapabilities, getCellDimensions, getGifDimensions, getImageDimensions, getJpegDimensions, getPngDimensions, getWebpDimensions, hyperlink, imageFallback, renderImage, resetCapabilitiesCache, setCapabilities, setCellDimensions, } from "./terminal-image.js";
|
|
140
|
-
export { Container, CURSOR_MARKER, compositeTuiLine, isFocusable, isViewportTUI, } from "./tui.js";
|
|
141
|
-
-export { TuiAltScreen } from "./tui-alt-screen.js";
|
|
142
|
-
+export { paintCanvasRow, TuiAltScreen } from "./tui-alt-screen.js";
|
|
143
|
-
export { TuiMainScreen } from "./tui-main-screen.js";
|
|
144
|
-
// Utilities
|
|
145
|
-
export { getOsc8LinkAtColumn, sliceByColumn, stripTerminalSequences, truncateToWidth, visibleWidth, wrapTextWithAnsi, } from "./utils.js";
|
|
146
|
-
diff --git a/dist/tui-alt-screen.d.ts b/dist/tui-alt-screen.d.ts
|
|
147
|
-
index 848119411c1ff2885b8d1efe97f2f297bd4cd470..96422839632a71e6382a1fc6fcd9b43e7b435609 100644
|
|
148
|
-
--- a/dist/tui-alt-screen.d.ts
|
|
149
|
-
+++ b/dist/tui-alt-screen.d.ts
|
|
150
|
-
@@ -1,5 +1,11 @@
|
|
151
|
-
import type { Terminal } from "./terminal.ts";
|
|
152
|
-
import { type Component, TuiBase, type TuiStopOptions, VIEWPORT_TUI, type ViewportTUI } from "./tui.ts";
|
|
153
|
-
+/**
|
|
154
|
-
+ * Paint one screen row with a canvas background SGR: prefix the row with the
|
|
155
|
-
+ * bg code, re-inject it after every SGR that clears the background, and pad
|
|
156
|
-
+ * to the full row width so the erase-line remainder carries the color too.
|
|
157
|
-
+ */
|
|
158
|
-
+export declare function paintCanvasRow(line: string, background: string, width: number): string;
|
|
159
|
-
export interface TuiAltScreenOptions {
|
|
160
|
-
/** Number of logical lines moved for each mouse-wheel event. */
|
|
161
|
-
wheelScrollLines?: number;
|
|
162
|
-
@@ -57,11 +63,20 @@ export declare class TuiAltScreen extends TuiBase implements ViewportTUI {
|
|
163
|
-
private readonly openUrl?;
|
|
164
|
-
private readonly onRightClickPaste?;
|
|
165
|
-
private readonly copySelection?;
|
|
166
|
-
+ private canvasBackground?;
|
|
167
|
-
constructor(terminal: Terminal, showHardwareCursor?: boolean, logDirectory?: string, options?: TuiAltScreenOptions);
|
|
168
|
-
get viewportTop(): number;
|
|
169
|
-
get isFollowingOutput(): boolean;
|
|
170
|
-
setLayoutRoot(component: Component | undefined): void;
|
|
171
|
-
render(width: number): string[];
|
|
172
|
-
+ /**
|
|
173
|
-
+ * Paint every rendered row with a canvas background SGR (e.g.
|
|
174
|
-
+ * "\x1b[48;2;13;17;23m"). `undefined` restores the transparent default
|
|
175
|
-
+ * (the terminal background shows through). The code is re-injected after
|
|
176
|
-
+ * every SGR that clears the background and rows are padded to the full
|
|
177
|
-
+ * width, so the whole screen carries the color — image rows excepted.
|
|
178
|
-
+ */
|
|
179
|
-
+ setCanvasBackground(background: string | undefined): void;
|
|
180
|
-
protected getMountedRoots(): readonly Component[];
|
|
181
|
-
private getPrimaryScrollView;
|
|
182
|
-
protected beforeTerminalStart(): void;
|
|
183
|
-
diff --git a/dist/tui-alt-screen.js b/dist/tui-alt-screen.js
|
|
184
|
-
index 4edcf845ba7866782a8ec7219508a2dce0073a91..338f2b29e7ba926399f3d663b0b79669c8ac6c13 100644
|
|
185
|
-
--- a/dist/tui-alt-screen.js
|
|
186
|
-
+++ b/dist/tui-alt-screen.js
|
|
187
|
-
@@ -21,6 +21,51 @@ const END_SYNCHRONIZED_OUTPUT = "\x1b[?2026l";
|
|
188
|
-
const OSC133_ZONE_PREFIX = /^(?:\x1b\]133;[ABC](?:\x07|\x1b\\))+/;
|
|
189
|
-
const OSC133_PROMPT_START = /^\x1b\]133;A(?:\x07|\x1b\\)/;
|
|
190
|
-
const PAGE_SCROLL_OVERLAP = 4;
|
|
191
|
-
+const CSI_PATTERN = /\x1b\[[0-9;:]*[A-Za-z]/g;
|
|
192
|
-
+/**
|
|
193
|
-
+ * True when an SGR sequence resets the background to the terminal default:
|
|
194
|
-
+ * a full reset (empty params or param 0) or the explicit default-bg param 49.
|
|
195
|
-
+ * Intentional background sets (48/4…, 48;2;…) do not clear, so they win.
|
|
196
|
-
+ */
|
|
197
|
-
+function sgrClearsBackground(sequence) {
|
|
198
|
-
+ const params = sequence.slice(2, -1);
|
|
199
|
-
+ if (params === "") return true;
|
|
200
|
-
+ // A color-set (3x foreground / 4x background — ANSI 30-37/40-47, truecolor
|
|
201
|
-
+ // 38;2/48;2, 256-color 38;5/48;5) never clears the background, even when a
|
|
202
|
-
+ // channel is 0: 48;2;0;121;107 (teal) would otherwise read the 0 as the
|
|
203
|
-
+ // reset param and re-inject the canvas over the segment. (49 alone — bg →
|
|
204
|
-
+ // default — IS a clear and falls through to the loop below.)
|
|
205
|
-
+ if (/^(?:3[0-7]|38;|4[0-8]|48;)/.test(params)) return false;
|
|
206
|
-
+ for (const param of params.split(";")) {
|
|
207
|
-
+ const code = param === "" ? 0 : Number.parseInt(param, 10);
|
|
208
|
-
+ if (Number.isNaN(code)) continue;
|
|
209
|
-
+ if (code === 0 || code === 49) return true;
|
|
210
|
-
+ }
|
|
211
|
-
+ return false;
|
|
212
|
-
+}
|
|
213
|
-
+/**
|
|
214
|
-
+ * Paint one screen row with a canvas background SGR: prefix the row with the
|
|
215
|
-
+ * bg code, re-inject it after every SGR that clears the background, and pad
|
|
216
|
-
+ * to the full row width so the erase-line remainder carries the color too.
|
|
217
|
-
+ * Image rows are the caller's responsibility (they must stay untouched).
|
|
218
|
-
+ */
|
|
219
|
-
+export function paintCanvasRow(line, background, width) {
|
|
220
|
-
+ let painted = background;
|
|
221
|
-
+ let last = 0;
|
|
222
|
-
+ for (const match of line.matchAll(CSI_PATTERN)) {
|
|
223
|
-
+ const sequence = match[0];
|
|
224
|
-
+ painted += line.slice(last, match.index);
|
|
225
|
-
+ painted += sequence;
|
|
226
|
-
+ if (sequence.endsWith("m") && sgrClearsBackground(sequence)) {
|
|
227
|
-
+ painted += background;
|
|
228
|
-
+ }
|
|
229
|
-
+ last = match.index + sequence.length;
|
|
230
|
-
+ }
|
|
231
|
-
+ painted += line.slice(last);
|
|
232
|
-
+ const pad = width - visibleWidth(line);
|
|
233
|
-
+ if (pad > 0) painted += " ".repeat(pad);
|
|
234
|
-
+ return `${painted}\x1b[0m`;
|
|
235
|
-
+}
|
|
236
|
-
const MAX_CACHED_OFFSCREEN_KITTY_IMAGES = 16;
|
|
237
|
-
const MAX_CACHED_OFFSCREEN_KITTY_TRANSMISSION_BYTES = 32 * 1024 * 1024;
|
|
238
|
-
const MAX_CACHED_OFFSCREEN_KITTY_DECODED_BYTES = 64 * 1024 * 1024;
|
|
239
|
-
@@ -64,6 +109,7 @@ export class TuiAltScreen extends TuiBase {
|
|
240
|
-
openUrl;
|
|
241
|
-
onRightClickPaste;
|
|
242
|
-
copySelection;
|
|
243
|
-
+ canvasBackground;
|
|
244
|
-
constructor(terminal, showHardwareCursor, logDirectory, options = {}) {
|
|
245
|
-
super(terminal, showHardwareCursor, logDirectory);
|
|
246
|
-
this.implicitDocument = {
|
|
247
|
-
@@ -100,6 +146,18 @@ export class TuiAltScreen extends TuiBase {
|
|
248
|
-
render(width) {
|
|
249
|
-
return this.layoutRoot?.render(width) ?? super.render(width);
|
|
250
|
-
}
|
|
251
|
-
+ /**
|
|
252
|
-
+ * Paint every rendered row with a canvas background SGR (e.g.
|
|
253
|
-
+ * "\x1b[48;2;13;17;23m"). `undefined` restores the transparent default
|
|
254
|
-
+ * (the terminal background shows through). The code is re-injected after
|
|
255
|
-
+ * every SGR that clears the background and rows are padded to the full
|
|
256
|
-
+ * width, so the whole screen carries the color — image rows excepted.
|
|
257
|
-
+ */
|
|
258
|
-
+ setCanvasBackground(background) {
|
|
259
|
-
+ if (this.canvasBackground === background) return;
|
|
260
|
-
+ this.canvasBackground = background;
|
|
261
|
-
+ this.requestRender();
|
|
262
|
-
+ }
|
|
263
|
-
getMountedRoots() {
|
|
264
|
-
return this.layoutRoot ? [this.layoutRoot] : this.children;
|
|
265
|
-
}
|
|
266
|
-
@@ -1099,6 +1157,10 @@ export class TuiAltScreen extends TuiBase {
|
|
267
|
-
return line;
|
|
268
|
-
return sliceByColumn(line, 0, width, true);
|
|
269
|
-
});
|
|
270
|
-
+ if (this.canvasBackground !== undefined) {
|
|
271
|
-
+ const canvasBackground = this.canvasBackground;
|
|
272
|
-
+ screen = screen.map((line) => (isImageLine(line) ? line : paintCanvasRow(line, canvasBackground, width)));
|
|
273
|
-
+ }
|
|
274
|
-
const fullRedraw = this.previousScreen.length === 0 || this.previousScreenWidth !== width || this.previousScreenHeight !== height;
|
|
275
|
-
const imagesNeedRedraw = screen.some((line, row) => line !== this.previousScreen[row] && (isImageLine(line) || isImageLine(this.previousScreen[row] ?? "")));
|
|
276
|
-
const redrawImages = fullRedraw || imagesNeedRedraw;
|