deepclause-sdk 0.1.2 → 0.1.3
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/dist/cli/repl.d.ts +19 -4
- package/dist/cli/repl.d.ts.map +1 -1
- package/dist/cli/repl.js +267 -44
- package/dist/cli/repl.js.map +1 -1
- package/dist/repl/backend.d.ts.map +1 -1
- package/dist/repl/backend.js +18 -5
- package/dist/repl/backend.js.map +1 -1
- package/dist/repl/render.d.ts +52 -2
- package/dist/repl/render.d.ts.map +1 -1
- package/dist/repl/render.js +33 -12
- package/dist/repl/render.js.map +1 -1
- package/dist/repl/status.d.ts +3 -1
- package/dist/repl/status.d.ts.map +1 -1
- package/dist/repl/status.js +4 -0
- package/dist/repl/status.js.map +1 -1
- package/dist/repl/tui/autocomplete.d.ts +19 -0
- package/dist/repl/tui/autocomplete.d.ts.map +1 -0
- package/dist/repl/tui/autocomplete.js +41 -0
- package/dist/repl/tui/autocomplete.js.map +1 -0
- package/dist/repl/tui/document.d.ts +51 -0
- package/dist/repl/tui/document.d.ts.map +1 -0
- package/dist/repl/tui/document.js +160 -0
- package/dist/repl/tui/document.js.map +1 -0
- package/dist/repl/tui/picker.d.ts +31 -0
- package/dist/repl/tui/picker.d.ts.map +1 -0
- package/dist/repl/tui/picker.js +43 -0
- package/dist/repl/tui/picker.js.map +1 -0
- package/dist/repl/tui/renderer.d.ts +98 -0
- package/dist/repl/tui/renderer.d.ts.map +1 -0
- package/dist/repl/tui/renderer.js +284 -0
- package/dist/repl/tui/renderer.js.map +1 -0
- package/dist/repl/tui/status-bar.d.ts +4 -2
- package/dist/repl/tui/status-bar.d.ts.map +1 -1
- package/dist/repl/tui/status-bar.js +80 -24
- package/dist/repl/tui/status-bar.js.map +1 -1
- package/dist/repl/tui/terminal.d.ts +28 -0
- package/dist/repl/tui/terminal.d.ts.map +1 -0
- package/dist/repl/tui/terminal.js +94 -0
- package/dist/repl/tui/terminal.js.map +1 -0
- package/dist/repl/tui/theme.d.ts +60 -0
- package/dist/repl/tui/theme.d.ts.map +1 -1
- package/dist/repl/tui/theme.js +142 -0
- package/dist/repl/tui/theme.js.map +1 -1
- package/dist/repl/tui/tool-pane.d.ts +68 -0
- package/dist/repl/tui/tool-pane.d.ts.map +1 -0
- package/dist/repl/tui/tool-pane.js +257 -0
- package/dist/repl/tui/tool-pane.js.map +1 -0
- package/dist/repl/tui/working-line.d.ts +23 -0
- package/dist/repl/tui/working-line.d.ts.map +1 -0
- package/dist/repl/tui/working-line.js +58 -0
- package/dist/repl/tui/working-line.js.map +1 -0
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component renderer for the fullscreen TUI.
|
|
3
|
+
*
|
|
4
|
+
* It implements the same surface as `ReplRenderer` so the CLI's dispatch code is
|
|
5
|
+
* unchanged, but it emits *components* where a component earns its keep:
|
|
6
|
+
*
|
|
7
|
+
* - a tool call becomes a `DmlToolPane` (collapsible, with its own status),
|
|
8
|
+
* - streamed assistant text becomes a live `Markdown` block, so prose renders as
|
|
9
|
+
* it arrives instead of one line at a time,
|
|
10
|
+
* - the model-call indicator moves to the dock (`onWorking`), where a `Loader`
|
|
11
|
+
* cannot be flushed away by unrelated output the way a transcript line could.
|
|
12
|
+
*
|
|
13
|
+
* Everything else is delegated to a `ReplRenderer` whose output is funnelled into
|
|
14
|
+
* the document, so the existing formatting — output, logs, errors, usage,
|
|
15
|
+
* compaction, solutions, user/steer blocks — is reused rather than reinvented.
|
|
16
|
+
*/
|
|
17
|
+
import type { DMLBinding, DMLEvent } from '../../types.js';
|
|
18
|
+
import { type ReplRendererOptions } from '../render.js';
|
|
19
|
+
import { TranscriptDocument } from './document.js';
|
|
20
|
+
export interface TuiRendererOptions extends ReplRendererOptions {
|
|
21
|
+
document: TranscriptDocument;
|
|
22
|
+
/**
|
|
23
|
+
* Show or hide the working indicator. The dock owns the `Loader`; the renderer
|
|
24
|
+
* only reports the state, so the indicator does not depend on which event
|
|
25
|
+
* happened to arrive last.
|
|
26
|
+
*/
|
|
27
|
+
onWorking?: (label: string | null) => void;
|
|
28
|
+
}
|
|
29
|
+
export declare class TuiRenderer {
|
|
30
|
+
/** Kept for parity with `ReplRenderer`; the TUI is always rich. */
|
|
31
|
+
readonly mode: "rich";
|
|
32
|
+
private readonly document;
|
|
33
|
+
private readonly text;
|
|
34
|
+
private readonly onWorking?;
|
|
35
|
+
/** Whether activity events (and therefore frame labels) are shown. */
|
|
36
|
+
private readonly showEvents;
|
|
37
|
+
/** Tool panes in order, so the last one can be expanded on demand. */
|
|
38
|
+
private readonly panes;
|
|
39
|
+
private currentPane?;
|
|
40
|
+
private stream?;
|
|
41
|
+
/** The live (unterminated) line written by the delegated renderer. */
|
|
42
|
+
private pending;
|
|
43
|
+
/**
|
|
44
|
+
* What was emitted last: a text block or a component.
|
|
45
|
+
*
|
|
46
|
+
* The line renderer already separates its own blocks, but it cannot see the
|
|
47
|
+
* components (panes, streamed markdown) that never pass through it — so text
|
|
48
|
+
* followed by a component, or the reverse, ran together. Any blank requested here
|
|
49
|
+
* collapses with one the line renderer adds, so this cannot double them.
|
|
50
|
+
*/
|
|
51
|
+
private lastBlock;
|
|
52
|
+
constructor(options: TuiRendererOptions);
|
|
53
|
+
/** Keep unlike output apart: one blank line between a text block and a component. */
|
|
54
|
+
private separate;
|
|
55
|
+
/**
|
|
56
|
+
* Close any open streamed-text block.
|
|
57
|
+
*
|
|
58
|
+
* Called at a turn boundary by the host, which knows when a prompt was submitted
|
|
59
|
+
* even if the run that was streaming never reported `finished` (an interrupted
|
|
60
|
+
* turn, for instance).
|
|
61
|
+
*/
|
|
62
|
+
endTextBlock(): void;
|
|
63
|
+
/** Expand or collapse the most recent tool pane (ctrl+o). */
|
|
64
|
+
toggleLastPane(): boolean;
|
|
65
|
+
render(event: DMLEvent): void;
|
|
66
|
+
private renderToolCall;
|
|
67
|
+
private newStreamBlock;
|
|
68
|
+
/** Finish the live block, so later text starts a new one. */
|
|
69
|
+
private endStream;
|
|
70
|
+
/**
|
|
71
|
+
* Feed the delegated renderer's output into the document, mirroring the line
|
|
72
|
+
* sink the readline path uses: `\r` replaces the live line, `\n` commits it, and
|
|
73
|
+
* `ESC[K`/`ESC[J` are dropped because the pending line already replaces it.
|
|
74
|
+
*/
|
|
75
|
+
private writeDocumentText;
|
|
76
|
+
/**
|
|
77
|
+
* While the TUI is live everything goes through the document. The CLI calls
|
|
78
|
+
* this again on the way out (to "restore stdout" for the exit footer), and that
|
|
79
|
+
* is exactly when output should stop being a component and go to the terminal
|
|
80
|
+
* again — otherwise the footer is written into a viewport that has been torn
|
|
81
|
+
* down and the user never sees it.
|
|
82
|
+
*/
|
|
83
|
+
setSink(sink?: (text: string) => void, errorSink?: (text: string) => void): void;
|
|
84
|
+
setImageSink(sink?: (lines: string[]) => void): void;
|
|
85
|
+
setSuppressInputPrompt(suppress: boolean): void;
|
|
86
|
+
setReasoningMode(mode: 'off' | 'summary' | 'full'): void;
|
|
87
|
+
getReasoningMode(): 'off' | 'summary' | 'full';
|
|
88
|
+
getWidth(): number;
|
|
89
|
+
setWidth(width: number): void;
|
|
90
|
+
writeLine(text?: string): void;
|
|
91
|
+
writeErrorLine(text: string): void;
|
|
92
|
+
listing(text: string): string;
|
|
93
|
+
image(lines: string[]): void;
|
|
94
|
+
solution(bindings: DMLBinding[], terminator?: string, prefix?: string, maxValue?: number): void;
|
|
95
|
+
flush(): void;
|
|
96
|
+
dispose(): void;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../src/repl/tui/renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EAA6B,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnF,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AA2CnD,MAAM,WAAW,kBAAmB,SAAQ,mBAAmB;IAC7D,QAAQ,EAAE,kBAAkB,CAAC;IAC7B;;;;OAIG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;CAC5C;AAED,qBAAa,WAAW;IACtB,mEAAmE;IACnE,QAAQ,CAAC,IAAI,EAAG,MAAM,CAAU;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAe;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAiC;IAC5D,sEAAsE;IACtE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAU;IACrC,sEAAsE;IACtE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;IAC3C,OAAO,CAAC,WAAW,CAAC,CAAc;IAClC,OAAO,CAAC,MAAM,CAAC,CAAiB;IAChC,sEAAsE;IACtE,OAAO,CAAC,OAAO,CAAM;IACrB;;;;;;;OAOG;IACH,OAAO,CAAC,SAAS,CAAyC;gBAE9C,OAAO,EAAE,kBAAkB;IAmBvC,qFAAqF;IACrF,OAAO,CAAC,QAAQ;IAOhB;;;;;;OAMG;IACH,YAAY,IAAI,IAAI;IAIpB,6DAA6D;IAC7D,cAAc,IAAI,OAAO;IAQzB,MAAM,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAoC7B,OAAO,CAAC,cAAc;IA8BtB,OAAO,CAAC,cAAc;IAMtB,6DAA6D;IAC7D,OAAO,CAAC,SAAS;IAIjB;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAgCzB;;;;;;OAMG;IACH,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,EAAE,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAKhF,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,GAAG,IAAI;IASpD,sBAAsB,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI;IAI/C,gBAAgB,CAAC,IAAI,EAAE,KAAK,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI;IAIxD,gBAAgB,IAAI,KAAK,GAAG,SAAS,GAAG,MAAM;IAI9C,QAAQ,IAAI,MAAM;IAIlB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI7B,SAAS,CAAC,IAAI,SAAK,GAAG,IAAI;IAI1B,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIlC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAK7B,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IAK5B,QAAQ,CAAC,QAAQ,EAAE,UAAU,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,SAAK,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IAK3F,KAAK,IAAI,IAAI;IAIb,OAAO,IAAI,IAAI;CAGhB"}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component renderer for the fullscreen TUI.
|
|
3
|
+
*
|
|
4
|
+
* It implements the same surface as `ReplRenderer` so the CLI's dispatch code is
|
|
5
|
+
* unchanged, but it emits *components* where a component earns its keep:
|
|
6
|
+
*
|
|
7
|
+
* - a tool call becomes a `DmlToolPane` (collapsible, with its own status),
|
|
8
|
+
* - streamed assistant text becomes a live `Markdown` block, so prose renders as
|
|
9
|
+
* it arrives instead of one line at a time,
|
|
10
|
+
* - the model-call indicator moves to the dock (`onWorking`), where a `Loader`
|
|
11
|
+
* cannot be flushed away by unrelated output the way a transcript line could.
|
|
12
|
+
*
|
|
13
|
+
* Everything else is delegated to a `ReplRenderer` whose output is funnelled into
|
|
14
|
+
* the document, so the existing formatting — output, logs, errors, usage,
|
|
15
|
+
* compaction, solutions, user/steer blocks — is reused rather than reinvented.
|
|
16
|
+
*/
|
|
17
|
+
import { Markdown } from '@earendil-works/pi-tui';
|
|
18
|
+
import { framePrefix, ReplRenderer } from '../render.js';
|
|
19
|
+
import { markdownTheme } from './theme.js';
|
|
20
|
+
import { DmlToolPane } from './tool-pane.js';
|
|
21
|
+
/** Events that produce no transcript output of their own. */
|
|
22
|
+
const QUIET_EVENTS = new Set(['model_call', 'finished', 'meta_result', 'usage']);
|
|
23
|
+
/** A markdown block whose text grows as the model streams. */
|
|
24
|
+
class StreamingBlock {
|
|
25
|
+
buffer = '';
|
|
26
|
+
markdown;
|
|
27
|
+
/** The frame's indentation, so nested answers stay inside the tree. */
|
|
28
|
+
prefix;
|
|
29
|
+
constructor(prefix) {
|
|
30
|
+
this.prefix = prefix;
|
|
31
|
+
this.markdown = new Markdown('', 0, 0, markdownTheme());
|
|
32
|
+
}
|
|
33
|
+
setPrefix(prefix) {
|
|
34
|
+
this.prefix = prefix;
|
|
35
|
+
}
|
|
36
|
+
append(chunk) {
|
|
37
|
+
this.buffer += chunk;
|
|
38
|
+
this.markdown.setText(this.buffer);
|
|
39
|
+
}
|
|
40
|
+
get isEmpty() {
|
|
41
|
+
return this.buffer.trim() === '';
|
|
42
|
+
}
|
|
43
|
+
render(width) {
|
|
44
|
+
const lines = this.markdown.render(Math.max(1, width - this.prefix.length));
|
|
45
|
+
if (!this.prefix)
|
|
46
|
+
return lines;
|
|
47
|
+
return lines.map((line) => (line === '' ? line : `${this.prefix}${line}`));
|
|
48
|
+
}
|
|
49
|
+
invalidate() {
|
|
50
|
+
this.markdown.invalidate();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export class TuiRenderer {
|
|
54
|
+
/** Kept for parity with `ReplRenderer`; the TUI is always rich. */
|
|
55
|
+
mode = 'rich';
|
|
56
|
+
document;
|
|
57
|
+
text;
|
|
58
|
+
onWorking;
|
|
59
|
+
/** Whether activity events (and therefore frame labels) are shown. */
|
|
60
|
+
showEvents;
|
|
61
|
+
/** Tool panes in order, so the last one can be expanded on demand. */
|
|
62
|
+
panes = [];
|
|
63
|
+
currentPane;
|
|
64
|
+
stream;
|
|
65
|
+
/** The live (unterminated) line written by the delegated renderer. */
|
|
66
|
+
pending = '';
|
|
67
|
+
/**
|
|
68
|
+
* What was emitted last: a text block or a component.
|
|
69
|
+
*
|
|
70
|
+
* The line renderer already separates its own blocks, but it cannot see the
|
|
71
|
+
* components (panes, streamed markdown) that never pass through it — so text
|
|
72
|
+
* followed by a component, or the reverse, ran together. Any blank requested here
|
|
73
|
+
* collapses with one the line renderer adds, so this cannot double them.
|
|
74
|
+
*/
|
|
75
|
+
lastBlock = 'none';
|
|
76
|
+
constructor(options) {
|
|
77
|
+
this.document = options.document;
|
|
78
|
+
this.onWorking = options.onWorking;
|
|
79
|
+
this.showEvents = options.events !== false;
|
|
80
|
+
this.text = new ReplRenderer({
|
|
81
|
+
mode: 'rich',
|
|
82
|
+
color: options.color,
|
|
83
|
+
verbose: options.verbose,
|
|
84
|
+
stream: options.stream,
|
|
85
|
+
events: options.events,
|
|
86
|
+
reasoning: options.reasoning,
|
|
87
|
+
markdown: options.markdown,
|
|
88
|
+
});
|
|
89
|
+
this.text.setSink((text) => this.writeDocumentText(text), (text) => this.writeDocumentText(text));
|
|
90
|
+
}
|
|
91
|
+
/** Keep unlike output apart: one blank line between a text block and a component. */
|
|
92
|
+
separate(kind) {
|
|
93
|
+
if (this.lastBlock !== 'none' && this.lastBlock !== kind) {
|
|
94
|
+
this.document.appendText('');
|
|
95
|
+
}
|
|
96
|
+
this.lastBlock = kind;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Close any open streamed-text block.
|
|
100
|
+
*
|
|
101
|
+
* Called at a turn boundary by the host, which knows when a prompt was submitted
|
|
102
|
+
* even if the run that was streaming never reported `finished` (an interrupted
|
|
103
|
+
* turn, for instance).
|
|
104
|
+
*/
|
|
105
|
+
endTextBlock() {
|
|
106
|
+
this.endStream();
|
|
107
|
+
}
|
|
108
|
+
/** Expand or collapse the most recent tool pane (ctrl+o). */
|
|
109
|
+
toggleLastPane() {
|
|
110
|
+
const pane = this.panes[this.panes.length - 1];
|
|
111
|
+
if (!pane)
|
|
112
|
+
return false;
|
|
113
|
+
pane.toggle();
|
|
114
|
+
this.document.onChange?.();
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
render(event) {
|
|
118
|
+
switch (event.type) {
|
|
119
|
+
case 'tool_call':
|
|
120
|
+
this.renderToolCall(event);
|
|
121
|
+
return;
|
|
122
|
+
case 'model_call':
|
|
123
|
+
this.onWorking?.(event.modelCallState === 'started' ? 'working…' : null);
|
|
124
|
+
return;
|
|
125
|
+
case 'stream':
|
|
126
|
+
if (event.content) {
|
|
127
|
+
if (!this.stream)
|
|
128
|
+
this.separate('component');
|
|
129
|
+
this.stream ??= this.newStreamBlock(framePrefix(event, this.showEvents));
|
|
130
|
+
this.stream.append(event.content);
|
|
131
|
+
this.document.onChange?.();
|
|
132
|
+
}
|
|
133
|
+
return;
|
|
134
|
+
case 'answer':
|
|
135
|
+
case 'error':
|
|
136
|
+
this.endStream();
|
|
137
|
+
this.separate('text');
|
|
138
|
+
this.text.render(event);
|
|
139
|
+
return;
|
|
140
|
+
case 'finished':
|
|
141
|
+
// A run ended. Without closing the block, the next turn's streamed text is
|
|
142
|
+
// appended to this one and renders *above* the prompt the user just typed,
|
|
143
|
+
// because the block sits earlier in the transcript.
|
|
144
|
+
if ((event.depth ?? 0) === 0)
|
|
145
|
+
this.endStream();
|
|
146
|
+
return;
|
|
147
|
+
default:
|
|
148
|
+
// Events that render nothing must not add a separator, or switching models
|
|
149
|
+
// or finishing a turn would open a gap.
|
|
150
|
+
if (!QUIET_EVENTS.has(event.type))
|
|
151
|
+
this.separate('text');
|
|
152
|
+
this.text.render(event);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
renderToolCall(event) {
|
|
156
|
+
const name = event.toolName ?? 'tool';
|
|
157
|
+
const state = event.toolState ?? 'running';
|
|
158
|
+
const prefix = framePrefix(event, this.showEvents);
|
|
159
|
+
if (state === 'starting' || !this.currentPane) {
|
|
160
|
+
this.endStream();
|
|
161
|
+
this.separate('component');
|
|
162
|
+
this.currentPane = new DmlToolPane({ toolName: name, args: event.toolArgs ?? {}, prefix });
|
|
163
|
+
this.panes.push(this.currentPane);
|
|
164
|
+
this.document.appendComponent(this.currentPane);
|
|
165
|
+
this.onWorking?.(`${name}…`);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
// A `completed` event can carry the frame metadata the start event lacked.
|
|
169
|
+
this.currentPane.setPrefix(prefix);
|
|
170
|
+
if (event.toolArgs)
|
|
171
|
+
this.currentPane.setArgs(event.toolArgs);
|
|
172
|
+
}
|
|
173
|
+
if (state === 'completed' || state === 'failed') {
|
|
174
|
+
this.currentPane.setResult(event.toolResult ?? event.toolSummary ?? '', state === 'completed', event.toolDurationMs);
|
|
175
|
+
this.onWorking?.(null);
|
|
176
|
+
this.currentPane = undefined;
|
|
177
|
+
this.document.onChange?.();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
newStreamBlock(prefix) {
|
|
181
|
+
const block = new StreamingBlock(prefix);
|
|
182
|
+
this.document.appendComponent(block);
|
|
183
|
+
return block;
|
|
184
|
+
}
|
|
185
|
+
/** Finish the live block, so later text starts a new one. */
|
|
186
|
+
endStream() {
|
|
187
|
+
this.stream = undefined;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Feed the delegated renderer's output into the document, mirroring the line
|
|
191
|
+
* sink the readline path uses: `\r` replaces the live line, `\n` commits it, and
|
|
192
|
+
* `ESC[K`/`ESC[J` are dropped because the pending line already replaces it.
|
|
193
|
+
*/
|
|
194
|
+
writeDocumentText(chunk) {
|
|
195
|
+
let index = 0;
|
|
196
|
+
while (index < chunk.length) {
|
|
197
|
+
const char = chunk[index];
|
|
198
|
+
if (char === '\r') {
|
|
199
|
+
this.pending = '';
|
|
200
|
+
this.document.setPending('');
|
|
201
|
+
index += 1;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
if (char === '\n') {
|
|
205
|
+
this.document.appendText(this.pending);
|
|
206
|
+
this.pending = '';
|
|
207
|
+
this.document.setPending('');
|
|
208
|
+
index += 1;
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
if (char === '\u001b' && chunk[index + 1] === '[') {
|
|
212
|
+
const match = /^\u001b\[[0-9;]*[JK]/.exec(chunk.slice(index));
|
|
213
|
+
if (match) {
|
|
214
|
+
index += match[0].length;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
this.pending += char;
|
|
219
|
+
this.document.setPending(this.pending);
|
|
220
|
+
index += 1;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// ---- ReplRenderer-compatible surface ----
|
|
224
|
+
/**
|
|
225
|
+
* While the TUI is live everything goes through the document. The CLI calls
|
|
226
|
+
* this again on the way out (to "restore stdout" for the exit footer), and that
|
|
227
|
+
* is exactly when output should stop being a component and go to the terminal
|
|
228
|
+
* again — otherwise the footer is written into a viewport that has been torn
|
|
229
|
+
* down and the user never sees it.
|
|
230
|
+
*/
|
|
231
|
+
setSink(sink, errorSink) {
|
|
232
|
+
if (!sink)
|
|
233
|
+
return;
|
|
234
|
+
this.text.setSink(sink, errorSink);
|
|
235
|
+
}
|
|
236
|
+
setImageSink(sink) {
|
|
237
|
+
this.text.setImageSink(sink
|
|
238
|
+
? (lines) => {
|
|
239
|
+
this.endStream();
|
|
240
|
+
sink(lines);
|
|
241
|
+
}
|
|
242
|
+
: undefined);
|
|
243
|
+
}
|
|
244
|
+
setSuppressInputPrompt(suppress) {
|
|
245
|
+
this.text.setSuppressInputPrompt(suppress);
|
|
246
|
+
}
|
|
247
|
+
setReasoningMode(mode) {
|
|
248
|
+
this.text.setReasoningMode(mode);
|
|
249
|
+
}
|
|
250
|
+
getReasoningMode() {
|
|
251
|
+
return this.text.getReasoningMode();
|
|
252
|
+
}
|
|
253
|
+
getWidth() {
|
|
254
|
+
return this.text.getWidth();
|
|
255
|
+
}
|
|
256
|
+
setWidth(width) {
|
|
257
|
+
this.text.setWidth(width);
|
|
258
|
+
}
|
|
259
|
+
writeLine(text = '') {
|
|
260
|
+
this.text.writeLine(text);
|
|
261
|
+
}
|
|
262
|
+
writeErrorLine(text) {
|
|
263
|
+
this.text.writeErrorLine(text);
|
|
264
|
+
}
|
|
265
|
+
listing(text) {
|
|
266
|
+
this.endStream();
|
|
267
|
+
return this.text.listing(text);
|
|
268
|
+
}
|
|
269
|
+
image(lines) {
|
|
270
|
+
this.endStream();
|
|
271
|
+
this.text.image(lines);
|
|
272
|
+
}
|
|
273
|
+
solution(bindings, terminator, prefix = '', maxValue) {
|
|
274
|
+
this.endStream();
|
|
275
|
+
this.text.solution(bindings, terminator, prefix, maxValue);
|
|
276
|
+
}
|
|
277
|
+
flush() {
|
|
278
|
+
this.text.flush();
|
|
279
|
+
}
|
|
280
|
+
dispose() {
|
|
281
|
+
this.text.dispose();
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
//# sourceMappingURL=renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderer.js","sourceRoot":"","sources":["../../../src/repl/tui/renderer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Fixed status bar for the REPL TUI (pi-style footer).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* the
|
|
4
|
+
* One row: where the harness lives and what it is doing on the left, the model on
|
|
5
|
+
* the right, and the token/context counters in between. Anything that does not fit
|
|
6
|
+
* is dropped whole, in priority order (see `render`), so a narrow terminal loses
|
|
7
|
+
* detail instead of showing truncated fragments.
|
|
6
8
|
*/
|
|
7
9
|
import { type Component } from '@earendil-works/pi-tui';
|
|
8
10
|
import { type ReplStatusSnapshot } from '../status.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status-bar.d.ts","sourceRoot":"","sources":["../../../src/repl/tui/status-bar.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"status-bar.d.ts","sourceRoot":"","sources":["../../../src/repl/tui/status-bar.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAiC,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEvF,OAAO,EAIL,KAAK,kBAAkB,EACxB,MAAM,cAAc,CAAC;AAGtB,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,kBAAkB,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC;IACzB,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC;CAC3B;AA2DD,qBAAa,kBAAmB,YAAW,SAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,gBAAgB;IAEtD,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IA+B/B,UAAU,IAAI,IAAI;CAGnB"}
|
|
@@ -1,27 +1,71 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Fixed status bar for the REPL TUI (pi-style footer).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* the
|
|
4
|
+
* One row: where the harness lives and what it is doing on the left, the model on
|
|
5
|
+
* the right, and the token/context counters in between. Anything that does not fit
|
|
6
|
+
* is dropped whole, in priority order (see `render`), so a narrow terminal loses
|
|
7
|
+
* detail instead of showing truncated fragments.
|
|
6
8
|
*/
|
|
7
9
|
import { truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';
|
|
8
10
|
import { formatContextSegment, formatModelSegment, formatUsageSegment, } from '../status.js';
|
|
9
11
|
import { dim } from './theme.js';
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
/**
|
|
13
|
+
* The workspace, as short as it can be while still identifying it.
|
|
14
|
+
*
|
|
15
|
+
* A single row cannot afford `/home/me/git/org/project` — it takes half the line
|
|
16
|
+
* and pushes the model and counters off it — so this falls back to the parent and
|
|
17
|
+
* name (`…/org/project`) and then to the name alone.
|
|
18
|
+
*/
|
|
19
|
+
function shortenPath(path, maxWidth = 24) {
|
|
20
|
+
const home = process.env.HOME;
|
|
21
|
+
const shown = home && path.startsWith(home) ? `~${path.slice(home.length)}` : path;
|
|
22
|
+
if (visibleWidth(shown) <= maxWidth)
|
|
23
|
+
return shown;
|
|
24
|
+
const parts = shown.split('/').filter(Boolean);
|
|
25
|
+
const parentAndName = `…/${parts.slice(-2).join('/')}`;
|
|
26
|
+
if (visibleWidth(parentAndName) <= maxWidth)
|
|
27
|
+
return parentAndName;
|
|
28
|
+
return `…/${parts[parts.length - 1] ?? shown}`;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Join the segments that fit, in the order given, as whole segments.
|
|
32
|
+
*
|
|
33
|
+
* The old code truncated the last segment to fit, which is how a footer ended up
|
|
34
|
+
* reading `deepseek-flash · ` — a separator with nothing after it, and no way to
|
|
35
|
+
* tell a short value from a cut one. Skipping a long segment (a full path) also
|
|
36
|
+
* lets the short, more useful ones after it through.
|
|
37
|
+
*/
|
|
38
|
+
function joinFitting(segments, width, separator = ' · ') {
|
|
39
|
+
const present = segments.filter((segment) => Boolean(segment));
|
|
40
|
+
const kept = [];
|
|
41
|
+
let used = 0;
|
|
42
|
+
for (const segment of present) {
|
|
43
|
+
const cost = (kept.length > 0 ? visibleWidth(separator) : 0) + visibleWidth(segment);
|
|
44
|
+
if (used + cost > width)
|
|
45
|
+
continue;
|
|
46
|
+
kept.push(segment);
|
|
47
|
+
used += cost;
|
|
17
48
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
49
|
+
// Nothing fit: say so with an empty string. The caller decides what to truncate —
|
|
50
|
+
// this never cuts a segment in half, which is what produced `deepseek-flash · `.
|
|
51
|
+
return kept.join(separator);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Left content padded, with `right` against the right edge.
|
|
55
|
+
*
|
|
56
|
+
* The counters are dropped whole before the model is touched: a run is identified
|
|
57
|
+
* by its model, and a half-printed counter is worse than a missing one.
|
|
58
|
+
*/
|
|
59
|
+
function spread(left, right, width) {
|
|
60
|
+
const gap = 2;
|
|
61
|
+
const rightWidth = visibleWidth(right);
|
|
62
|
+
if (rightWidth >= width)
|
|
63
|
+
return truncateToWidth(right, width, '…');
|
|
64
|
+
const joined = joinFitting(left, Math.max(0, width - rightWidth - gap), ' ');
|
|
65
|
+
if (joined) {
|
|
66
|
+
return joined + ' '.repeat(width - visibleWidth(joined) - rightWidth) + right;
|
|
21
67
|
}
|
|
22
|
-
|
|
23
|
-
const total = leftWidth + visibleWidth(truncated);
|
|
24
|
-
return left + ' '.repeat(Math.max(0, width - total)) + truncated;
|
|
68
|
+
return ' '.repeat(width - rightWidth) + right;
|
|
25
69
|
}
|
|
26
70
|
export class StatusBarComponent {
|
|
27
71
|
options;
|
|
@@ -30,15 +74,27 @@ export class StatusBarComponent {
|
|
|
30
74
|
}
|
|
31
75
|
render(width) {
|
|
32
76
|
const snapshot = this.options.getSnapshot();
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
77
|
+
// The long steering hint only on a wide terminal; `running` alone still says
|
|
78
|
+
// what matters, and the dock's loader shows that work is happening.
|
|
79
|
+
const running = this.options.isRunning?.()
|
|
80
|
+
? (width >= 100 ? 'running (type to steer)' : 'running')
|
|
81
|
+
: undefined;
|
|
82
|
+
const frozen = this.options.isFrozen?.() ? 'FROZEN' : undefined;
|
|
83
|
+
const harness = this.options.harness ? `harness ${this.options.harness}` : undefined;
|
|
84
|
+
// Priority order, most useful first: where we are, what state we are in, what
|
|
85
|
+
// it has cost, what we are pointed at. The session id is printed by the exit
|
|
86
|
+
// footer, and the backend is only interesting when debugging, so they go last.
|
|
87
|
+
const line = spread([
|
|
88
|
+
shortenPath(this.options.workspace),
|
|
89
|
+
running,
|
|
90
|
+
frozen,
|
|
91
|
+
formatUsageSegment(snapshot),
|
|
92
|
+
formatContextSegment(snapshot),
|
|
93
|
+
harness,
|
|
94
|
+
`backend ${this.options.backend}`,
|
|
95
|
+
`session ${this.options.sessionId}`,
|
|
96
|
+
], formatModelSegment(snapshot), width);
|
|
97
|
+
return [dim(line)];
|
|
42
98
|
}
|
|
43
99
|
invalidate() {
|
|
44
100
|
// Stateless rendering: reads the snapshot on each frame.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status-bar.js","sourceRoot":"","sources":["../../../src/repl/tui/status-bar.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"status-bar.js","sourceRoot":"","sources":["../../../src/repl/tui/status-bar.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A terminal that knows how tall it really is.
|
|
3
|
+
*
|
|
4
|
+
* pi-tui asks the stream: `rows` is `process.stdout.rows || LINES || 24`. When the
|
|
5
|
+
* stream does not report a size — an unsized pty, a multiplexer or embedded
|
|
6
|
+
* terminal that never sends SIGWINCH, a wrapper that resizes the pty after the
|
|
7
|
+
* child starts — `rows` is 0 and the fallback is a **hard-coded 24**. The TUI then
|
|
8
|
+
* draws a 24-row frame inside a taller terminal, so the UI occupies the top of the
|
|
9
|
+
* screen and leaves the rest untouched, which reads as "it does not use the full
|
|
10
|
+
* height".
|
|
11
|
+
*
|
|
12
|
+
* `refreshTerminalDimensions()` (which pi-tui calls on start) only re-raises
|
|
13
|
+
* SIGWINCH at itself, so it cannot fix a stream that has no size to report.
|
|
14
|
+
* `stty size` asks the tty directly, which is the same source `tput`/`$LINES`
|
|
15
|
+
* use, and is the fallback here.
|
|
16
|
+
*/
|
|
17
|
+
import { ProcessTerminal } from '@earendil-works/pi-tui';
|
|
18
|
+
export declare class SizedTerminal extends ProcessTerminal {
|
|
19
|
+
private fallback?;
|
|
20
|
+
private queried;
|
|
21
|
+
/** Re-read the size (call on resize, where the stream may still not report one). */
|
|
22
|
+
refreshSize(): void;
|
|
23
|
+
private streamSize;
|
|
24
|
+
private ttySize;
|
|
25
|
+
get rows(): number;
|
|
26
|
+
get columns(): number;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=terminal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../../../src/repl/tui/terminal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAyCzD,qBAAa,aAAc,SAAQ,eAAe;IAChD,OAAO,CAAC,QAAQ,CAAC,CAAO;IACxB,OAAO,CAAC,OAAO,CAAS;IAExB,oFAAoF;IACpF,WAAW,IAAI,IAAI;IAKnB,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,OAAO;IAUf,IAAa,IAAI,IAAI,MAAM,CAM1B;IAED,IAAa,OAAO,IAAI,MAAM,CAM7B;CACF"}
|