@vincemakes/kiso-tui 0.16.4 → 0.16.5

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/ask-panel.js CHANGED
@@ -263,7 +263,7 @@ export function askBlockRows(view, state, W, maxRows) {
263
263
  const counter = spec.questions.length > 1 ? `${p.dim} ‹ ${state.qIndex + 1}/${spec.questions.length} ›${p.reset}` : "";
264
264
  const rows = [];
265
265
  // R2: the same dashed rule the composer and the approval panel use.
266
- rows.push(`${p.dim}${"\u254c".repeat(Math.max(0, W))}${p.reset}`);
266
+ rows.push(`${p.dim}${"\u2500".repeat(Math.max(0, W))}${p.reset}`);
267
267
  rows.push(` ${cutLine(`${p.bold}${escapeTerminal(q.question)}${p.reset}${counter}`, Math.max(1, W - 2))}`);
268
268
  const header = q.header === undefined ? "the question" : escapeTerminal(q.header.slice(0, ASK_HEADER_CAP));
269
269
  // R2: the divider row is gone (the opening rule says a block starts
@@ -317,7 +317,7 @@ export function askBlockRows(view, state, W, maxRows) {
317
317
  // TUI2-R1.5 ⑪ had already replaced a two-cell `\u2514 ` stub with a
318
318
  // real rule for the reason that stub read as the cut-notice prefix it
319
319
  // collides with; this keeps that finding and only changes which rule.
320
- rows.push(`${p.dim}${"\u254c".repeat(Math.max(0, W))}${p.reset}`);
320
+ rows.push(`${p.dim}${"\u2500".repeat(Math.max(0, W))}${p.reset}`);
321
321
  return rows;
322
322
  }
323
323
  /** The status row's right-hand hint — the phase's keys. */
package/dist/at-picker.js CHANGED
@@ -265,6 +265,6 @@ export function atPanelRows(state, W) {
265
265
  */
266
266
  export function bandHeader(label, W) {
267
267
  const p = palette();
268
- const head = `\u254c\u254c\u254c ${label} `;
269
- return `${p.dim}${widthCut(`${head}${"\u254c".repeat(Math.max(1, W - head.length))}`, Math.max(1, W))}${p.reset}`;
268
+ const head = `\u2500\u2500\u2500 ${label} `;
269
+ return `${p.dim}${widthCut(`${head}${"\u2500".repeat(Math.max(1, W - head.length))}`, Math.max(1, W))}${p.reset}`;
270
270
  }
@@ -2,3 +2,4 @@
2
2
  * Amendment 4) — this module is the re-export shim: the compositor's
3
3
  * and index.ts's imports (./components.js) stay verbatim. */
4
4
  export * from "@vincemakes/kiso-tui-cells/components";
5
+ export { MOTION_FRAMES, TWINKLE, breathFrame, twinkleFrame } from "@vincemakes/kiso-tui-cells/render";
@@ -2,3 +2,6 @@
2
2
  * Amendment 4) — this module is the re-export shim: the compositor's
3
3
  * and index.ts's imports (./components.js) stay verbatim. */
4
4
  export * from "@vincemakes/kiso-tui-cells/components";
5
+ // R3 (design §5.2): the two motion cycles reach the compositor through
6
+ // the same shim every other cell primitive does.
7
+ export { MOTION_FRAMES, TWINKLE, breathFrame, twinkleFrame } from "@vincemakes/kiso-tui-cells/render";
@@ -52,7 +52,7 @@ import { MOUSE_OFF } from "./editor.js";
52
52
  import { atPanelRows, bandHeader } from "./at-picker.js";
53
53
  // TUI2-R2 ②: the session picker's rows — the band's third occupant.
54
54
  import { sessionPickerRows } from "./session-picker.js";
55
- import { Container, ROLLUP_NOUN, SPINNER, MdStream, bodySpacing, boxBottom, boxTop, cellComponent, exploreCounts, focusToken, exploreRows, foldLine, isExploreTool, pendingQueueRows, statusLine, turnFold, visibleWidth, } from "./components.js";
55
+ import { Container, ROLLUP_NOUN, MOTION_FRAMES, MdStream, bodySpacing, boxBottom, boxTop, cellComponent, exploreCounts, focusToken, exploreRows, foldLine, isExploreTool, pendingQueueRows, statusLine, turnFold, visibleWidth, } from "./components.js";
56
56
  import { bannerLines, escapeTerminal, foldResult, foldThinking, palette, renderTerminalGap, renderToolSummary, toolTarget } from "./render.js";
57
57
  import { displayVerb, keysSheetRows } from "./strings.js";
58
58
  /** The cursor marker — an APC private sequence the focus component
@@ -1232,15 +1232,32 @@ export class Body {
1232
1232
  }, this.#frameMs);
1233
1233
  this.#frameTimer.unref();
1234
1234
  }
1235
- /** The spinner: a ONE-SHOT re-armed ONLY while a running tool exists —
1236
- * no tool no timer zero bytes (the #14/#15 contract, structural). */
1235
+ /**
1236
+ * The spinner: a ONE-SHOT re-armed ONLY while something is MOVING
1237
+ * nothing moving → no timer → zero bytes (the #14/#15 contract,
1238
+ * structural).
1239
+ *
1240
+ * R3: "moving" used to mean a running TOOL and nothing else. But the
1241
+ * same counter drives the status row's `working` twinkle, and the
1242
+ * model spends whole stretches thinking with no tool open at all —
1243
+ * so the one indicator whose entire job is "I am still alive" froze
1244
+ * solid exactly when the user most needed it to move. It was
1245
+ * reported as "I thought it had failed", which is precisely the
1246
+ * message a frozen liveness mark sends.
1247
+ *
1248
+ * An OPEN THINKING cell counts as movement now, and so does a tool
1249
+ * parked at an approval — that one is waiting on a human rather than
1250
+ * working, but the mark is still the proof the process is alive.
1251
+ */
1237
1252
  #armSpinner() {
1238
1253
  if (this.#spinnerTimer !== null)
1239
1254
  return;
1240
1255
  this.#spinnerTimer = setTimeout(() => {
1241
1256
  this.#spinnerTimer = null;
1242
- if (this.#cells.some((c) => c.kind === "tool" && c.state === "running" && !c.done)) {
1243
- this.#spinnerI = (this.#spinnerI + 1) % SPINNER.length;
1257
+ const moving = this.#cells.some((c) => (c.kind === "tool" && (c.state === "running" || c.state === "approval") && !c.done) ||
1258
+ (c.kind === "thinking" && !c.done));
1259
+ if (moving) {
1260
+ this.#spinnerI = (this.#spinnerI + 1) % MOTION_FRAMES;
1244
1261
  this.#dirty = true;
1245
1262
  this.#scheduleFrame();
1246
1263
  this.#armSpinner();
package/dist/editor.js CHANGED
@@ -88,7 +88,13 @@ const N_MAX = 6;
88
88
  const OSC_MAX = 1024;
89
89
  /** The dim "…" — the ONE truncation mark: the horizontal scroll's
90
90
  * prefix (unchanged) and the viewport's hidden-rows markers. */
91
- const ELLIPSIS = "\x1b[2m…\x1b[0m";
91
+ /** R3: built per call from the palette — `dim` is an absolute grey once
92
+ * the ground is known, so a frozen SGR 2 here would be the one span
93
+ * that ignores it. */
94
+ const ellipsis = () => {
95
+ const p = palette();
96
+ return `${p.dim}\u2026${p.reset}`;
97
+ };
92
98
  /**
93
99
  * The editor. Raw mode + bracketed paste (?2004h) on enter, restored on
94
100
  * exit. The input row is rendered by `onRender` (the CLI wires it to the
@@ -459,9 +465,9 @@ export class Editor {
459
465
  // the cursor's own row carries the horizontal scroll (and its
460
466
  // "…"); the other rows render whole and cap at the frame's wall
461
467
  const from = i === cursorLine ? b.start + this.#scroll : b.start;
462
- const scrolled = i === cursorLine && this.#scroll > 0 ? ELLIPSIS : "";
463
- const above = i === first && first > 0 ? ELLIPSIS : "";
464
- const below = i === first + n - 1 && first + n < bounds.length ? ELLIPSIS : "";
468
+ const scrolled = i === cursorLine && this.#scroll > 0 ? ellipsis() : "";
469
+ const above = i === first && first > 0 ? ellipsis() : "";
470
+ const below = i === first + n - 1 && first + n < bounds.length ? ellipsis() : "";
465
471
  lines.push(`${above}${scrolled}${String.fromCodePoint(...this.#chars.slice(from, b.end))}${below}`);
466
472
  }
467
473
  const cursorRow = cursorLine - first;
package/dist/status.d.ts CHANGED
@@ -21,9 +21,21 @@
21
21
  * deliberate exception: the running row's interrupt hint, which KC2 §2
22
22
  * widens to name the new gesture.
23
23
  */
24
- /** v3 §03/§05 — the working glyph family; the CLI's 200ms spinner walks
25
- * it and hands each glyph back to `runningStatus`. */
26
- export declare const STATUS_GLYPHS: readonly ["▖", "▘", "▝", "▗"];
24
+ /**
25
+ * R3 (design §5.2) — the working glyph family is the TWINKLE, and the
26
+ * CLI's 200ms spinner walks it exactly as it walked the four quadrant
27
+ * blocks it replaces.
28
+ *
29
+ * Two reasons the quadrants had to go. §5.3: they ROTATED, and a mark
30
+ * that turns implies progress a call of unpredictable duration does not
31
+ * have. §4.1: the twinkle settles onto `✦`, which is the mark the
32
+ * folded segment keeps — so the glyph a human watches while the model
33
+ * thinks is the glyph left behind when the thought collapses into a
34
+ * record, and nothing new appears at the transition.
35
+ *
36
+ * Glyphs only, no colour: intact under NO_COLOR and on any ground.
37
+ */
38
+ export declare const STATUS_GLYPHS: readonly ["✧", "✦", "✶", "✸", "✺", "✸", "✦"];
27
39
  /**
28
40
  * The RUNNING row: the rotating glyph, the wall seconds since `since`
29
41
  * (never below 1 — a run that just started still reads "1s", so the row
package/dist/status.js CHANGED
@@ -22,9 +22,22 @@
22
22
  * widens to name the new gesture.
23
23
  */
24
24
  import { kUnit } from "./render.js";
25
- /** v3 §03/§05 the working glyph family; the CLI's 200ms spinner walks
26
- * it and hands each glyph back to `runningStatus`. */
27
- export const STATUS_GLYPHS = ["▖", "▘", "▝", "▗"];
25
+ import { TWINKLE } from "@vincemakes/kiso-tui-cells/render";
26
+ /**
27
+ * R3 (design §5.2) the working glyph family is the TWINKLE, and the
28
+ * CLI's 200ms spinner walks it exactly as it walked the four quadrant
29
+ * blocks it replaces.
30
+ *
31
+ * Two reasons the quadrants had to go. §5.3: they ROTATED, and a mark
32
+ * that turns implies progress a call of unpredictable duration does not
33
+ * have. §4.1: the twinkle settles onto `✦`, which is the mark the
34
+ * folded segment keeps — so the glyph a human watches while the model
35
+ * thinks is the glyph left behind when the thought collapses into a
36
+ * record, and nothing new appears at the transition.
37
+ *
38
+ * Glyphs only, no colour: intact under NO_COLOR and on any ground.
39
+ */
40
+ export const STATUS_GLYPHS = TWINKLE;
28
41
  /** The ~ctx estimate as the whole-percent LEFT. A non-finite ratio (no
29
42
  * window, no estimate) yields null and the row prints "~null%" — the
30
43
  * long-standing shape, kept on purpose: an honest null beats an
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-tui",
3
- "version": "0.16.4",
3
+ "version": "0.16.5",
4
4
  "description": "kiso tui — 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",
@@ -35,6 +35,6 @@
35
35
  },
36
36
  "homepage": "https://github.com/vincemakes/kiso/tree/main/packages/tui#readme",
37
37
  "dependencies": {
38
- "@vincemakes/kiso-tui-cells": "0.16.4"
38
+ "@vincemakes/kiso-tui-cells": "0.16.5"
39
39
  }
40
40
  }