@vincemakes/kiso-tui 0.1.23 → 0.1.25
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/body.d.ts +9 -0
- package/dist/body.js +61 -13
- package/dist/dock.d.ts +3 -2
- package/dist/dock.js +22 -5
- package/dist/editor.d.ts +1 -1
- package/dist/editor.js +5 -2
- package/dist/render.d.ts +21 -9
- package/dist/render.js +22 -8
- package/package.json +1 -1
package/dist/body.d.ts
CHANGED
|
@@ -84,6 +84,15 @@ export declare class Body {
|
|
|
84
84
|
constructor(opts: BodyOptions);
|
|
85
85
|
/** Teardown — flush a pending frame, stop the timers. */
|
|
86
86
|
close(): void;
|
|
87
|
+
/**
|
|
88
|
+
* TUI v4 #16a: a resize reflows the terminal's scrollback — the frozen
|
|
89
|
+
* rows' positions are no longer what we tracked (and the frozen CONTENT
|
|
90
|
+
* is never re-emitted: the terminal reflowed it, we only redraw the
|
|
91
|
+
* dock + active tail). The counters reset so the next render writes new
|
|
92
|
+
* frozen lines through the REAL-LF scroll path above the tail, never at
|
|
93
|
+
* a stale CUP row (the drag-garbage the #16 user saw).
|
|
94
|
+
*/
|
|
95
|
+
onResize(): void;
|
|
87
96
|
/** The last COMPLETE thinking block, for /think. */
|
|
88
97
|
lastThinking(): string | null;
|
|
89
98
|
/** The last completed tool call, for /last. */
|
package/dist/body.js
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
*/
|
|
26
26
|
import { truncateDiff } from "./diff.js";
|
|
27
27
|
import { displayWidth } from "./editor.js";
|
|
28
|
-
import { escapeTerminal, foldResult, foldThinking, palette, renderTerminalGap, renderToolSummary, } from "./render.js";
|
|
28
|
+
import { colorInlineCode, escapeTerminal, foldResult, foldThinking, palette, renderTerminalGap, renderToolSummary, } from "./render.js";
|
|
29
29
|
/** The spinner glyphs, cycled by the heartbeat (v3 §05 — the working family). */
|
|
30
30
|
const SPINNER = ["▖", "▘", "▝", "▗"];
|
|
31
31
|
const TOOL_SUMMARY_MAX = 60; // the tool line's parameter summary, chars
|
|
@@ -48,11 +48,21 @@ export class Body {
|
|
|
48
48
|
#pipeBuf = ""; // the passthrough's thinking buffer — the cell model needs no buffer
|
|
49
49
|
#toolCells = new Map(); // callId → cell index (parallel tools)
|
|
50
50
|
#write;
|
|
51
|
+
#resizeHandler = null;
|
|
51
52
|
constructor(opts) {
|
|
52
53
|
this.#opts = opts;
|
|
53
54
|
this.#write = opts.write ?? ((s) => process.stdout.write(s));
|
|
54
55
|
this.#active = opts.active();
|
|
55
56
|
if (this.#isActive()) {
|
|
57
|
+
// TUI v4 #16a: the resize event — the terminal reflows its
|
|
58
|
+
// scrollback, so OUR row bookkeeping is stale. The frozen
|
|
59
|
+
// content is never re-emitted (the terminal reflowed it); the
|
|
60
|
+
// counters reset so the next render lands new frozen lines via
|
|
61
|
+
// the REAL-LF scroll path (just above the tail), never at stale
|
|
62
|
+
// CUP rows — the overwrite garbage after a drag (the #16
|
|
63
|
+
// defect). The dock redraws its own chrome on the same event.
|
|
64
|
+
this.#resizeHandler = () => this.onResize();
|
|
65
|
+
process.stdout.on("resize", this.#resizeHandler);
|
|
56
66
|
this.#heartbeat = setInterval(() => {
|
|
57
67
|
// #14/#15: the idle heartbeat PAINTS NOTHING unless an
|
|
58
68
|
// ANIMATION advances — only a RUNNING tool's glyph/elapsed
|
|
@@ -86,9 +96,32 @@ export class Body {
|
|
|
86
96
|
clearInterval(this.#heartbeat);
|
|
87
97
|
this.#heartbeat = null;
|
|
88
98
|
}
|
|
99
|
+
if (this.#resizeHandler !== null) {
|
|
100
|
+
process.stdout.off("resize", this.#resizeHandler);
|
|
101
|
+
this.#resizeHandler = null;
|
|
102
|
+
}
|
|
89
103
|
if (this.#dirty)
|
|
90
104
|
this.render();
|
|
91
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* TUI v4 #16a: a resize reflows the terminal's scrollback — the frozen
|
|
108
|
+
* rows' positions are no longer what we tracked (and the frozen CONTENT
|
|
109
|
+
* is never re-emitted: the terminal reflowed it, we only redraw the
|
|
110
|
+
* dock + active tail). The counters reset so the next render writes new
|
|
111
|
+
* frozen lines through the REAL-LF scroll path above the tail, never at
|
|
112
|
+
* a stale CUP row (the drag-garbage the #16 user saw).
|
|
113
|
+
*/
|
|
114
|
+
onResize() {
|
|
115
|
+
if (!this.#isActive())
|
|
116
|
+
return;
|
|
117
|
+
this.#frozenRows = Number.MAX_SAFE_INTEGER; // the frozen area is "full" — the next line scrolls
|
|
118
|
+
this.#oldTailTop = 0; // the reflowed tail's old rows are gone — clear only the new area
|
|
119
|
+
// NO #dirty: an immediate frame would re-render the ACTIVE TAIL —
|
|
120
|
+
// re-emitting its bytes (the terminal already reflowed the tail's
|
|
121
|
+
// content; a re-print duplicates the text in the byte stream — the
|
|
122
|
+
// #16 storm gate's "response text exactly once"). The geometry
|
|
123
|
+
// reset takes effect at the next NATURAL render (the next event).
|
|
124
|
+
}
|
|
92
125
|
/** The last COMPLETE thinking block, for /think. */
|
|
93
126
|
lastThinking() {
|
|
94
127
|
return this.#lastThinking;
|
|
@@ -103,7 +136,7 @@ export class Body {
|
|
|
103
136
|
this.#closeOpenThinking();
|
|
104
137
|
this.#closeOpenText();
|
|
105
138
|
const p = palette();
|
|
106
|
-
this.#write(`${p.
|
|
139
|
+
this.#write(`${p.bold}you> ${escapeTerminal(text)}${p.reset}\n`);
|
|
107
140
|
return;
|
|
108
141
|
}
|
|
109
142
|
this.#closeOpenThinking();
|
|
@@ -372,10 +405,12 @@ export class Body {
|
|
|
372
405
|
const p = palette();
|
|
373
406
|
switch (cell.kind) {
|
|
374
407
|
case "user":
|
|
375
|
-
// v3 §02: the user message is a
|
|
376
|
-
//
|
|
377
|
-
//
|
|
378
|
-
|
|
408
|
+
// v3 §02, TUI v5 #16f (v4.1 design): the user message is a left
|
|
409
|
+
// rail — a bright-white BOLD ▍ per line, then the text (the
|
|
410
|
+
// reverse-video block is RETIRED: it washed out on light
|
|
411
|
+
// themes). Multi-line whole: every line carries the rail
|
|
412
|
+
// (多行连贯); resize-safe; NO_COLOR → the rail renders plain.
|
|
413
|
+
return cell.text.split("\n").map((l) => `${p.bold}▍${p.reset} ${escapeTerminal(l)}`);
|
|
379
414
|
case "thinking": {
|
|
380
415
|
const block = cell.text;
|
|
381
416
|
const trimmed = escapeTerminal(block.trim());
|
|
@@ -393,11 +428,11 @@ export class Body {
|
|
|
393
428
|
return [`${p.red}✗ ${name} (${err}, ${elapsed}s)${p.reset}`];
|
|
394
429
|
}
|
|
395
430
|
const delta = cell.added + cell.removed > 0 ? `, +${cell.added} -${cell.removed}` : "";
|
|
396
|
-
return [`${p.
|
|
431
|
+
return [`${p.bold}✓ ${name}${p.reset} (${summary}${delta}, ${elapsed}s)`];
|
|
397
432
|
}
|
|
398
433
|
if (cell.state === "approval") {
|
|
399
|
-
const lines = [`→ ${name} ${summary} ${p.
|
|
400
|
-
// v2e: the mini-diff — ▎
|
|
434
|
+
const lines = [`→ ${name} ${summary} ${p.bold}⏸${p.reset}`];
|
|
435
|
+
// v2e: the mini-diff — ▎ bold edge (the brick motif), - red /
|
|
401
436
|
// + green / context dim; NO_COLOR keeps the ± prefixes plain.
|
|
402
437
|
if (cell.diff !== null) {
|
|
403
438
|
for (const d of cell.diff) {
|
|
@@ -406,26 +441,39 @@ export class Body {
|
|
|
406
441
|
: d.kind === "+"
|
|
407
442
|
? `${p.green}+ ${escapeTerminal(d.text)}${p.reset}`
|
|
408
443
|
: `${p.dim} ${escapeTerminal(d.text)}${p.reset}`;
|
|
409
|
-
lines.push(`${p.
|
|
444
|
+
lines.push(`${p.bold}▎${p.reset}${body}`);
|
|
410
445
|
}
|
|
411
446
|
}
|
|
412
447
|
return lines;
|
|
413
448
|
}
|
|
414
449
|
if (cell.state === "running") {
|
|
415
450
|
const elapsed = cell.startedAt !== null ? Math.max(1, Math.round((Date.now() - cell.startedAt) / 1000)) : 1;
|
|
416
|
-
return [`→ ${name} ${summary} ${p.
|
|
451
|
+
return [`→ ${name} ${summary} ${p.bold}${SPINNER[this.#spinnerI % SPINNER.length]}${p.reset} ${elapsed}s`];
|
|
417
452
|
}
|
|
418
453
|
return [`→ ${name} ${summary}`];
|
|
419
454
|
}
|
|
420
455
|
case "text": {
|
|
421
456
|
const text = escapeTerminal(cell.text);
|
|
422
457
|
const wrapped = this.#wrap(text, W);
|
|
423
|
-
|
|
458
|
+
// TUI v5 #16e: the inline-code tint — backtick spans in
|
|
459
|
+
// assistant body text, matched PER LINE after the wrap (a
|
|
460
|
+
// span opened on one line and closed on another does NOT
|
|
461
|
+
// match — 跨行不匹配). NO_COLOR → the codes are empty →
|
|
462
|
+
// byte-identical.
|
|
463
|
+
return wrapped.length > 0 ? wrapped.map((l) => colorInlineCode(l)) : [""];
|
|
424
464
|
}
|
|
425
465
|
case "notice":
|
|
426
466
|
return [escapeTerminal(cell.text)];
|
|
427
467
|
case "raw":
|
|
428
|
-
|
|
468
|
+
// TUI v4 #16b: the raw cell carries the CLI's OWN pre-rendered
|
|
469
|
+
// lines (the banner, the recap, slash-command output) — the
|
|
470
|
+
// SGR is applied at COMPOSITION time (renderRecap/startupBanner),
|
|
471
|
+
// and model/tool content was already escapeTerminal'd there.
|
|
472
|
+
// Re-escaping at render STRIPPED the ESC from the SGR — the
|
|
473
|
+
// literal "[38;5;75m▞[0m" garbage the user saw (the #16 乱码,
|
|
474
|
+
// also the banner's dim). Verbatim: the injection guard lives
|
|
475
|
+
// at composition, not here.
|
|
476
|
+
return cell.lines;
|
|
429
477
|
case "terminal":
|
|
430
478
|
// the honest label (done / aborted / error) + the status + the
|
|
431
479
|
// rhythm gap blank
|
package/dist/dock.d.ts
CHANGED
|
@@ -69,7 +69,8 @@ export declare class Dock {
|
|
|
69
69
|
/** The bottom four rows, wrapped in CSI 2026 (synchronized output —
|
|
70
70
|
* the pi trick against flicker). The cursor ends at the input line's
|
|
71
71
|
* edit position. v3 §03: the upper ╌ row, the input row, the lower
|
|
72
|
-
* ╌ row, the status row — the status is dim (
|
|
73
|
-
* come from the CLI's composition).
|
|
72
|
+
* ╌ row, the status row — the status is dim (bold accents inside
|
|
73
|
+
* come from the CLI's composition). TUI v5 #16g: the idle status
|
|
74
|
+
* row carries the right-aligned "/ commands · ↑ history" hint. */
|
|
74
75
|
redraw(): void;
|
|
75
76
|
}
|
package/dist/dock.js
CHANGED
|
@@ -52,7 +52,7 @@ export class Dock {
|
|
|
52
52
|
* mode — the bottom three rows need room to exist. */
|
|
53
53
|
enter() {
|
|
54
54
|
const rows = process.stdout.rows ?? 0;
|
|
55
|
-
if (process.stdout.isTTY !== true || palette().
|
|
55
|
+
if (process.stdout.isTTY !== true || palette().bold === "" || rows < 4)
|
|
56
56
|
return;
|
|
57
57
|
this.#active = true;
|
|
58
58
|
this.#height = rows;
|
|
@@ -134,8 +134,9 @@ export class Dock {
|
|
|
134
134
|
/** The bottom four rows, wrapped in CSI 2026 (synchronized output —
|
|
135
135
|
* the pi trick against flicker). The cursor ends at the input line's
|
|
136
136
|
* edit position. v3 §03: the upper ╌ row, the input row, the lower
|
|
137
|
-
* ╌ row, the status row — the status is dim (
|
|
138
|
-
* come from the CLI's composition).
|
|
137
|
+
* ╌ row, the status row — the status is dim (bold accents inside
|
|
138
|
+
* come from the CLI's composition). TUI v5 #16g: the idle status
|
|
139
|
+
* row carries the right-aligned "/ commands · ↑ history" hint. */
|
|
139
140
|
redraw() {
|
|
140
141
|
if (!this.#active)
|
|
141
142
|
return;
|
|
@@ -144,7 +145,7 @@ export class Dock {
|
|
|
144
145
|
const W = this.#width;
|
|
145
146
|
const sep = `${p.dim}${"╌".repeat(W)}${p.reset}`;
|
|
146
147
|
const status = `${this.#status}${this.#tail === "" ? "" : ` · ${this.#tail}`}`;
|
|
147
|
-
const statusLine = this.#question ??
|
|
148
|
+
const statusLine = this.#question ?? this.#statusRow(status, p, W);
|
|
148
149
|
const inp = this.#inputState();
|
|
149
150
|
const out = [];
|
|
150
151
|
// P3 (审查): the DEC private-mode SET/RESET needs the "?" prefix —
|
|
@@ -160,7 +161,7 @@ export class Dock {
|
|
|
160
161
|
const item = menu.items[i];
|
|
161
162
|
const row = H - 4 - (menu.items.length - 1 - i);
|
|
162
163
|
const text = i === menu.selected
|
|
163
|
-
? `${p.
|
|
164
|
+
? `${p.bold}▸ ${item.name}${p.reset} ${item.desc}`
|
|
164
165
|
: `${p.dim} ${item.name} ${item.desc}${p.reset}`;
|
|
165
166
|
out.push(`\x1b[${row};1H\x1b[0K${text}`);
|
|
166
167
|
}
|
|
@@ -173,4 +174,20 @@ export class Dock {
|
|
|
173
174
|
out.push("\x1b[?2026l"); // synchronized output OFF
|
|
174
175
|
process.stdout.write(out.join(""));
|
|
175
176
|
}
|
|
177
|
+
/** TUI v5 #16g: the status row — the base status left-aligned, the
|
|
178
|
+
* "/ commands · ↑ history" hint right-aligned in the idle state
|
|
179
|
+
* (tail empty, no takeover question). The hint is CUT FIRST when
|
|
180
|
+
* the width is short — the status itself is never truncated for it;
|
|
181
|
+
* the running state carries its own esc hint in the status text, so
|
|
182
|
+
* the non-empty tail suppresses this one. */
|
|
183
|
+
#statusRow(status, p, W) {
|
|
184
|
+
const hint = this.#tail === "" && this.#question === null ? " / commands · ↑ history" : "";
|
|
185
|
+
if (hint === "")
|
|
186
|
+
return `${p.dim}${status}${p.reset}`;
|
|
187
|
+
const statusW = displayWidth(status.replace(/\x1b\[[0-9;]*m/g, ""));
|
|
188
|
+
const hintW = displayWidth(hint);
|
|
189
|
+
if (statusW + hintW > W)
|
|
190
|
+
return `${p.dim}${status}${p.reset}`;
|
|
191
|
+
return `${p.dim}${status}${" ".repeat(W - statusW - hintW)}${hint}${p.reset}`;
|
|
192
|
+
}
|
|
176
193
|
}
|
package/dist/editor.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export declare function charWidth(cp: number): number;
|
|
|
22
22
|
export declare function widthOf(chars: readonly number[]): number;
|
|
23
23
|
/** Display width of a string. */
|
|
24
24
|
export declare function displayWidth(text: string): number;
|
|
25
|
-
export declare const PROMPT = "\
|
|
25
|
+
export declare const PROMPT = "\u258C ";
|
|
26
26
|
export declare const PROMPT_WIDTH: number;
|
|
27
27
|
/** v3 §04 — the slash-command menu's command table (English one-liners). */
|
|
28
28
|
export interface MenuItem {
|
package/dist/editor.js
CHANGED
|
@@ -67,7 +67,10 @@ export function displayWidth(text) {
|
|
|
67
67
|
return w;
|
|
68
68
|
}
|
|
69
69
|
import { palette } from "./render.js";
|
|
70
|
-
|
|
70
|
+
// TUI v4 #16d: the input row is the blue brick + the edit area — the
|
|
71
|
+
// "you>" text is gone (the brick IS the prompt; the pipe path's readline
|
|
72
|
+
// prompt keeps its own "you> " — v2a line mode, byte-for-byte).
|
|
73
|
+
export const PROMPT = "▌ ";
|
|
71
74
|
export const PROMPT_WIDTH = displayWidth(PROMPT);
|
|
72
75
|
export const MENU_ITEMS = [
|
|
73
76
|
{ name: "/mode", desc: "switch the approval tier (manual/default/accept-edits/plan/bypass)" },
|
|
@@ -209,7 +212,7 @@ export class Editor {
|
|
|
209
212
|
const st = this.dockState();
|
|
210
213
|
const W = (process.stdout.columns ?? 0) || 80; // a degenerate 0 size (no TIOCSWINSZ) falls back
|
|
211
214
|
const cursorCol = Math.min(1 + PROMPT_WIDTH + st.cursor, W);
|
|
212
|
-
process.stdout.write(`\r\x1b[0K${p.
|
|
215
|
+
process.stdout.write(`\r\x1b[0K${p.bold}${PROMPT}${p.reset}${st.line}\x1b[${cursorCol}G`);
|
|
213
216
|
}
|
|
214
217
|
// ---- input ----
|
|
215
218
|
/** Feed raw stdin bytes — the parser. Public for unit tests. */
|
package/dist/render.d.ts
CHANGED
|
@@ -8,20 +8,23 @@
|
|
|
8
8
|
* package has ZERO kiso-core imports: input is data, output is bytes.
|
|
9
9
|
*/
|
|
10
10
|
/**
|
|
11
|
-
* v2a — the palette, centralized (no hard-coded codes elsewhere)
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
11
|
+
* v2a — the palette, centralized (no hard-coded codes elsewhere); v5
|
|
12
|
+
* (TUI v5 #16e, the v4.1 design): the decorative blue (38;5;75) is
|
|
13
|
+
* RETIRED — the identity accents (the you> prompt, the banner tagline,
|
|
14
|
+
* ✓ marks, slash-command names, the ▍ user rail, the input brick) are
|
|
15
|
+
* bright-white BOLD (SGR 1); `code` is the content semantic tint for
|
|
16
|
+
* inline code spans in assistant text (256-color 110 — the cube color
|
|
17
|
+
* nearest the design's #8fb4d8); red for errors, dim for metadata,
|
|
18
|
+
* green for the diff additions. NO_COLOR set, or a non-TTY output →
|
|
19
|
+
* every code is empty, so pipes and CI carry ZERO ANSI (the existing
|
|
20
|
+
* byte-level e2e assertions guard it). Everything not listed is plain.
|
|
18
21
|
*/
|
|
19
22
|
export interface Palette {
|
|
20
|
-
readonly
|
|
23
|
+
readonly bold: string;
|
|
21
24
|
readonly dim: string;
|
|
22
25
|
readonly red: string;
|
|
23
26
|
readonly green: string;
|
|
24
|
-
readonly
|
|
27
|
+
readonly code: string;
|
|
25
28
|
readonly reset: string;
|
|
26
29
|
}
|
|
27
30
|
export declare const COLOR_ON: Palette;
|
|
@@ -34,6 +37,15 @@ export declare function palette(): Palette;
|
|
|
34
37
|
* EVERY externally-sourced string must pass through this before any output.
|
|
35
38
|
*/
|
|
36
39
|
export declare function escapeTerminal(text: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* TUI v5 #16e: the inline-code tint — backtick spans in ONE line of
|
|
42
|
+
* assistant body text get the `code` color. Deliberately NOT a markdown
|
|
43
|
+
* engine: single level only (`[^`]*` cannot nest), a span never matches
|
|
44
|
+
* across lines (the caller passes one line; an opener without a closer
|
|
45
|
+
* on the same line stays plain). NO_COLOR / pipes → the codes are empty
|
|
46
|
+
* strings → the line passes through byte-identical.
|
|
47
|
+
*/
|
|
48
|
+
export declare function colorInlineCode(line: string): string;
|
|
37
49
|
/** The canonical-path resolver for the approval detail — injected by the
|
|
38
50
|
* caller (the CLI passes the tools' own resolution). The tui package is
|
|
39
51
|
* pure terminal: input is data, output is bytes, zero runtime deps. */
|
package/dist/render.js
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* kiso-core's Event — the CLI translates Event → RenderInput. The tui
|
|
8
8
|
* package has ZERO kiso-core imports: input is data, output is bytes.
|
|
9
9
|
*/
|
|
10
|
-
export const COLOR_ON = {
|
|
11
|
-
export const COLOR_OFF = {
|
|
10
|
+
export const COLOR_ON = { bold: "\x1b[1m", dim: "\x1b[2m", red: "\x1b[31m", green: "\x1b[32m", code: "\x1b[38;5;110m", reset: "\x1b[0m" };
|
|
11
|
+
export const COLOR_OFF = { bold: "", dim: "", red: "", green: "", code: "", reset: "" };
|
|
12
12
|
export function palette() {
|
|
13
13
|
return process.env.NO_COLOR === undefined && process.stdout.isTTY ? COLOR_ON : COLOR_OFF;
|
|
14
14
|
}
|
|
@@ -26,6 +26,20 @@ export function escapeTerminal(text) {
|
|
|
26
26
|
.replace(/[\u0080-\u009f]/g, "") // C1
|
|
27
27
|
.replace(/[\u202a-\u202e\u2066-\u2069]/g, ""); // bidi
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* TUI v5 #16e: the inline-code tint — backtick spans in ONE line of
|
|
31
|
+
* assistant body text get the `code` color. Deliberately NOT a markdown
|
|
32
|
+
* engine: single level only (`[^`]*` cannot nest), a span never matches
|
|
33
|
+
* across lines (the caller passes one line; an opener without a closer
|
|
34
|
+
* on the same line stays plain). NO_COLOR / pipes → the codes are empty
|
|
35
|
+
* strings → the line passes through byte-identical.
|
|
36
|
+
*/
|
|
37
|
+
export function colorInlineCode(line) {
|
|
38
|
+
const p = palette();
|
|
39
|
+
if (p.code === "" || p.reset === "")
|
|
40
|
+
return line;
|
|
41
|
+
return line.replace(/`([^`]*)`/g, `${p.code}\`$1\`${p.reset}`);
|
|
42
|
+
}
|
|
29
43
|
/**
|
|
30
44
|
* Render one event. `text` may be a continuation (text_delta appends to the
|
|
31
45
|
* current line); `newline` says whether the line is complete.
|
|
@@ -58,9 +72,9 @@ export function renderEvent(ev, prevThinking = false, resolvePath = (p) => p) {
|
|
|
58
72
|
const p = palette();
|
|
59
73
|
switch (ev.type) {
|
|
60
74
|
case "user_input":
|
|
61
|
-
// v2a:
|
|
62
|
-
// itself; this render is the REPLAY path).
|
|
63
|
-
return { text: `${p.
|
|
75
|
+
// v2a/v5: bold (the identity accent — the interactive prompt
|
|
76
|
+
// echoes itself; this render is the REPLAY path).
|
|
77
|
+
return { text: `${p.bold}you> ${escapeTerminal(typeof ev.content === "string" ? ev.content : "(content)")}${p.reset}\n`, newline: true, prompt: false };
|
|
64
78
|
case "text_delta":
|
|
65
79
|
return { text: escapeTerminal(ev.text), newline: false, prompt: false };
|
|
66
80
|
case "text_end":
|
|
@@ -164,9 +178,9 @@ function approvalDetail(name, input, resolvePath) {
|
|
|
164
178
|
* code; failures (isError) are ✗. Pure and deterministic.
|
|
165
179
|
*/
|
|
166
180
|
export function renderToolSummary(name, input, result) {
|
|
167
|
-
// v2a: ✓ is a
|
|
181
|
+
// v2a/v5: ✓ is a bold identity accent; ✗ stays red.
|
|
168
182
|
const p = palette();
|
|
169
|
-
const mark = result.isError ? `${p.red}✗${p.reset}` : `${p.
|
|
183
|
+
const mark = result.isError ? `${p.red}✗${p.reset}` : `${p.bold}✓${p.reset}`;
|
|
170
184
|
const shortName = name.replace("_file", "");
|
|
171
185
|
const detail = toolSummaryDetail(name, input, result);
|
|
172
186
|
return `${mark} ${escapeTerminal(`${shortName} ${detail}`)}`;
|
|
@@ -315,7 +329,7 @@ export function renderRecap(s) {
|
|
|
315
329
|
}
|
|
316
330
|
if (s.ctxLeftPct !== null)
|
|
317
331
|
parts.push(`ctx left ~${Math.round(s.ctxLeftPct)}%`);
|
|
318
|
-
return `${p.
|
|
332
|
+
return `${p.bold}▞${p.reset} ${parts.join(" · ")}\n`;
|
|
319
333
|
}
|
|
320
334
|
/** One-line summary of a session, for `kiso sessions`. */
|
|
321
335
|
export function renderSessionLine(meta) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-tui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.25",
|
|
4
4
|
"description": "kiso tui \u2014 the pure terminal layer (cell renderer, dock, raw editor, diff, palette). Zero runtime dependencies: input is data, output is bytes.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|