@vincemakes/kiso-tui 0.1.21 → 0.1.24

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 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
@@ -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;
@@ -372,10 +405,13 @@ 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 SGR BACKGROUND block, no
376
- // prefix — every line carries the block's background
377
- // (multi-line whole; resize-safe). Pipes stay plain.
378
- return cell.text.split("\n").map((l) => `${p.bg}${escapeTerminal(l)}${p.reset}`);
408
+ // v3 §02: the user message is a SGR block, no prefix — every
409
+ // line carries the block's highlight (multi-line whole;
410
+ // resize-safe). TUI v4 #16c: the fixed dark background
411
+ // (48;5;237) is GONE — reverse video (7m) swaps foreground/
412
+ // background, so the block follows the terminal theme (light
413
+ // theme → dark text on light). Pipes stay plain.
414
+ return cell.text.split("\n").map((l) => `${p.rev}${escapeTerminal(l)}${p.reset}`);
379
415
  case "thinking": {
380
416
  const block = cell.text;
381
417
  const trimmed = escapeTerminal(block.trim());
@@ -425,7 +461,15 @@ export class Body {
425
461
  case "notice":
426
462
  return [escapeTerminal(cell.text)];
427
463
  case "raw":
428
- return cell.lines.map((l) => escapeTerminal(l));
464
+ // TUI v4 #16b: the raw cell carries the CLI's OWN pre-rendered
465
+ // lines (the banner, the recap, slash-command output) — the
466
+ // SGR is applied at COMPOSITION time (renderRecap/startupBanner),
467
+ // and model/tool content was already escapeTerminal'd there.
468
+ // Re-escaping at render STRIPPED the ESC from the SGR — the
469
+ // literal "[38;5;75m▞[0m" garbage the user saw (the #16 乱码,
470
+ // also the banner's dim). Verbatim: the injection guard lives
471
+ // at composition, not here.
472
+ return cell.lines;
429
473
  case "terminal":
430
474
  // the honest label (done / aborted / error) + the status + the
431
475
  // rhythm gap blank
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 = "\u258Cyou> ";
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,10 +67,14 @@ export function displayWidth(text) {
67
67
  return w;
68
68
  }
69
69
  import { palette } from "./render.js";
70
- export const PROMPT = "▌you> "; // the kiso brick motif: one blue half-block, then you>
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)" },
77
+ { name: "/model", desc: "list model profiles; switch with /model <name|provider/model>" },
74
78
  { name: "/compact", desc: "summarize the older conversation to free context" },
75
79
  { name: "/think", desc: "show the last full thinking block" },
76
80
  { name: "/last", desc: "show the most recent tool call's input and output" },
package/dist/render.d.ts CHANGED
@@ -14,14 +14,16 @@
14
14
  * errors; dim for metadata. NO_COLOR set, or a non-TTY output → every
15
15
  * code is empty, so pipes and CI carry ZERO ANSI (the existing byte-level
16
16
  * e2e assertions guard it). Everything not listed here is plain.
17
- * v3: `bg` — the user-message block background (SGR 48, dark gray 237).
17
+ * v3: `rev` — the user-message block highlight (SGR 7 reverse video,
18
+ * TUI v4 #16c: the fixed dark background is GONE — reverse follows the
19
+ * terminal theme).
18
20
  */
19
21
  export interface Palette {
20
22
  readonly blue: string;
21
23
  readonly dim: string;
22
24
  readonly red: string;
23
25
  readonly green: string;
24
- readonly bg: string;
26
+ readonly rev: string;
25
27
  readonly reset: string;
26
28
  }
27
29
  export declare const COLOR_ON: Palette;
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 = { blue: "\x1b[38;5;75m", dim: "\x1b[2m", red: "\x1b[31m", green: "\x1b[32m", bg: "\x1b[48;5;237m", reset: "\x1b[0m" };
11
- export const COLOR_OFF = { blue: "", dim: "", red: "", green: "", bg: "", reset: "" };
10
+ export const COLOR_ON = { blue: "\x1b[38;5;75m", dim: "\x1b[2m", red: "\x1b[31m", green: "\x1b[32m", rev: "\x1b[7m", reset: "\x1b[0m" };
11
+ export const COLOR_OFF = { blue: "", dim: "", red: "", green: "", rev: "", reset: "" };
12
12
  export function palette() {
13
13
  return process.env.NO_COLOR === undefined && process.stdout.isTTY ? COLOR_ON : COLOR_OFF;
14
14
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-tui",
3
- "version": "0.1.21",
4
- "description": "kiso tui the pure terminal layer (cell renderer, dock, raw editor, diff, palette). Zero runtime dependencies: input is data, output is bytes.",
3
+ "version": "0.1.24",
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",
7
7
  "exports": {