@vincemakes/kiso-tui 0.20.4 → 0.21.1

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
@@ -418,7 +418,7 @@ export function askStatus(view, state) {
418
418
  // that no competitor can copy without building the log first.
419
419
  const total = view.ask.questions.length;
420
420
  const where = total > 1 ? `question ${state.qIndex + 1} of ${total}` : "a question for you";
421
- return `⏸ ${where} · answers are durable facts`;
421
+ return `❯ ${where} · answers are durable facts`;
422
422
  }
423
423
  /** The input row's lead: the digit lead while picking, the typing lead
424
424
  * in the custom phase (the rule-input phase's shape, reused). */
@@ -508,9 +508,9 @@ export function askView(spec) {
508
508
  name: "ask_user",
509
509
  title: first.header ?? first.question,
510
510
  speaker: "kiso",
511
- statusText: " a question for you",
511
+ statusText: " a question for you",
512
512
  args: { kind: "text", lines: askDeclineList(spec) },
513
- fallbackQuestion: `⚠ ${escapeTerminal(first.question)} — this terminal cannot show the option panel; the question is declined `,
513
+ fallbackQuestion: `${escapeTerminal(first.question)} — this terminal cannot show the option panel; the question is declined `,
514
514
  ask: spec,
515
515
  };
516
516
  }
@@ -221,21 +221,21 @@ export declare class Body {
221
221
  };
222
222
  /** Whether the viewer owns the live region right now. */
223
223
  viewerOpen(): boolean;
224
- /** ctrl+o — open on the newest fold, or close. */
224
+ /** ctrl+r — open on the newest fold, or close. */
225
225
  viewerToggleMode(): void;
226
226
  /** The viewer's keys. Everything the surface can do, a key does —
227
227
  * there is no pointer, so there is nothing a pointer could reach
228
228
  * that a keyboard cannot. */
229
229
  viewerKey(cmd: "up" | "down" | "toggle" | "all" | "pageUp" | "pageDown" | "home" | "end"): void;
230
230
  /**
231
- * DC-35 — ctrl+r does not print the same expansion twice in a row.
231
+ * DC-35 — ctrl+o does not print the same expansion twice in a row.
232
232
  *
233
233
  * The ring walks newest-back and restarts its cycle once every entry
234
234
  * has been opened (R4/C1, which is what makes the walk immune to the
235
235
  * ring growing underneath it). With a ring of ONE the restart is
236
236
  * immediate, so holding the key appended the identical block over
237
237
  * and over — the owner got three copies of the same four rows, each
238
- * closing with `ctrl+r opens the one before it`, a footer naming
238
+ * closing with `ctrl+o opens the one before it`, a footer naming
239
239
  * something that does not exist.
240
240
  *
241
241
  * The bar is the BOTTOM of the transcript, not "ever shown": once
@@ -93,7 +93,7 @@ function rolledTitle(cell) {
93
93
  * one.
94
94
  *
95
95
  * Extracted at R3b so the segment fold's expansion opens a run to the
96
- * SAME rows `ctrl+r` on the run itself would have opened. Two copies of
96
+ * SAME rows `ctrl+o` on the run itself would have opened. Two copies of
97
97
  * this would be two answers to "show me that run". */
98
98
  function rolledDetail(cell, W) {
99
99
  const p = palette();
@@ -331,7 +331,7 @@ export class Body {
331
331
  #mdBase = 0;
332
332
  #toolCells = new Map(); // callId → cell index (parallel tools)
333
333
  // W15: the collapsed (cut) tool cells — committed cells whose last
334
- // rendered row carried the "ctrl+r" affordance; the expand key's
334
+ // rendered row carried the "ctrl+o" affordance; the expand key's
335
335
  // cycling pointer walks this list from the newest back.
336
336
  #collapsed = [];
337
337
  /** R4 (C1) — the ring walk is by IDENTITY, not by a modular pointer.
@@ -1040,7 +1040,7 @@ export class Body {
1040
1040
  lastTool() {
1041
1041
  return this.#lastTool;
1042
1042
  }
1043
- /** W15 — the expand key's target (ctrl+r). A cell still in the LIVE
1043
+ /** W15 — the expand key's target (ctrl+o). A cell still in the LIVE
1044
1044
  * region (the newest live tool) TOGGLES in place — the compositor
1045
1045
  * owns those rows and redraws them (the body flips to the full
1046
1046
  * form, no cap). A committed cell can never toggle — history is
@@ -1050,7 +1050,7 @@ export class Body {
1050
1050
  * the header names the target ("N turns back" — the user cells
1051
1051
  * after it), so every press tells the user what they got. */
1052
1052
  /**
1053
- * TUI2-R2 ⑤ — the cell the next ctrl+r will act on, or -1.
1053
+ * TUI2-R2 ⑤ — the cell the next ctrl+o will act on, or -1.
1054
1054
  *
1055
1055
  * The rule is expandNext's own first loop, extracted verbatim: the
1056
1056
  * LAST live cell that can toggle. It is a separate method rather than
@@ -1158,7 +1158,7 @@ export class Body {
1158
1158
  const saved = head.rolled;
1159
1159
  head.rolled = rolledOf(run);
1160
1160
  // the run OPENS. The fold's key already asked to see the
1161
- // work, so what lands is the same rows `ctrl+r` on the
1161
+ // work, so what lands is the same rows `ctrl+o` on the
1162
1162
  // run itself would have opened — its title, then its
1163
1163
  // detail — never its collapsed row, which would make the
1164
1164
  // reader press a second time for what the first press
@@ -1226,7 +1226,7 @@ export class Body {
1226
1226
  viewerOpen() {
1227
1227
  return this.#viewer !== null;
1228
1228
  }
1229
- /** ctrl+o — open on the newest fold, or close. */
1229
+ /** ctrl+r — open on the newest fold, or close. */
1230
1230
  viewerToggleMode() {
1231
1231
  if (this.#viewer !== null) {
1232
1232
  this.#viewer = null;
@@ -1288,7 +1288,7 @@ export class Body {
1288
1288
  /**
1289
1289
  * The expandable things in the transcript, oldest first.
1290
1290
  *
1291
- * The set is `#collapsed` — the SAME ring `ctrl+r` walks — so the two
1291
+ * The set is `#collapsed` — the SAME ring `ctrl+o` walks — so the two
1292
1292
  * mechanisms can never disagree about what is reachable. The ring is
1293
1293
  * newest-first (it is unshifted on commit); reading order is the
1294
1294
  * other way, so it is reversed here.
@@ -1315,7 +1315,7 @@ export class Body {
1315
1315
  }
1316
1316
  if (cell.kind !== "tool")
1317
1317
  continue;
1318
- // the tool card's FULL body — the same rows its own ctrl+r
1318
+ // the tool card's FULL body — the same rows its own ctrl+o
1319
1319
  // opens. The expanded flag is saved and restored inside this
1320
1320
  // synchronous call, the pattern the rollup path has always
1321
1321
  // used for head.rolled; it never outlives the render, so the
@@ -1355,14 +1355,14 @@ export class Body {
1355
1355
  * transcript does not already END with. */
1356
1356
  #lastAppend = null;
1357
1357
  /**
1358
- * DC-35 — ctrl+r does not print the same expansion twice in a row.
1358
+ * DC-35 — ctrl+o does not print the same expansion twice in a row.
1359
1359
  *
1360
1360
  * The ring walks newest-back and restarts its cycle once every entry
1361
1361
  * has been opened (R4/C1, which is what makes the walk immune to the
1362
1362
  * ring growing underneath it). With a ring of ONE the restart is
1363
1363
  * immediate, so holding the key appended the identical block over
1364
1364
  * and over — the owner got three copies of the same four rows, each
1365
- * closing with `ctrl+r opens the one before it`, a footer naming
1365
+ * closing with `ctrl+o opens the one before it`, a footer naming
1366
1366
  * something that does not exist.
1367
1367
  *
1368
1368
  * The bar is the BOTTOM of the transcript, not "ever shown": once
@@ -1395,7 +1395,7 @@ export class Body {
1395
1395
  }
1396
1396
  // W20: the LIVE task block toggles in place too — the capped
1397
1397
  // form flips to the full list (the "done-collapse expands
1398
- // under ctrl+r" claim). The SETTLED block is already full —
1398
+ // under ctrl+o" claim). The SETTLED block is already full —
1399
1399
  // no toggle, and its rows carry no affordance, so it never
1400
1400
  // joins #collapsed (the committed /last append is moot).
1401
1401
  if (cell.kind === "checklist" && !cell.done) {
@@ -1462,12 +1462,12 @@ export class Body {
1462
1462
  // R3i phase 4 — THE FOOTER TELLS THE TRUTH ABOUT THIS PATH.
1463
1463
  //
1464
1464
  // The rows come from the rollup's own projection, whose last
1465
- // row reads `└ ctrl+r collapses` — true where it was written
1465
+ // row reads `└ ctrl+o collapses` — true where it was written
1466
1466
  // (the LIVE toggle, which really does close again) and false
1467
1467
  // here. A committed row is ink: ADR-0046 forbids rewriting
1468
1468
  // history, so nothing about this block can be taken back. The
1469
1469
  // next press opens the NEXT fold, and the row now says so.
1470
- const closing = rows.length > 0 && /ctrl\+r collapses/.test(rows[rows.length - 1] ?? "");
1470
+ const closing = rows.length > 0 && /ctrl\+o collapses/.test(rows[rows.length - 1] ?? "");
1471
1471
  const body = closing ? rows.slice(0, -1) : rows;
1472
1472
  return {
1473
1473
  kind: "appended",
@@ -1480,7 +1480,7 @@ export class Body {
1480
1480
  ...body,
1481
1481
  // R8a: an in-block note takes the block's indent, not a
1482
1482
  // second corner — the corner opens the body above it.
1483
- `${p.dim} end of expansion · ctrl+r opens the one before it${p.reset}`,
1483
+ `${p.dim} end of expansion · ctrl+o opens the one before it${p.reset}`,
1484
1484
  ],
1485
1485
  };
1486
1486
  }
@@ -1832,7 +1832,7 @@ export class Body {
1832
1832
  // to the options it names. And it never disappears: the status row
1833
1833
  // drops its hint when the left text plus the hint will not fit, so
1834
1834
  // the duplicate was width-dependent — one copy on an ask (long left
1835
- // text), two on an approval (`⏸ run paused`, twelve cells) — which
1835
+ // text), two on an approval (`❯ run paused`, twelve cells) — which
1836
1836
  // is why every fixed-width gate was blind to it. Each flavour's
1837
1837
  // block carries its own row (approval-panel.ts pushes it for the
1838
1838
  // approval and the pick, ask-panel.ts for the ask), so nothing is
@@ -2133,7 +2133,7 @@ export class Body {
2133
2133
  * The phases, in the order they are tested:
2134
2134
  * - an EXPANDED live cell outranks the slot (W15 — "the user asked
2135
2135
  * for it"): it renders in full, variable height. This is also
2136
- * DC-28's cure: mid-stretch `ctrl+r` had a target it toggled and
2136
+ * DC-28's cure: mid-stretch `ctrl+o` had a target it toggled and
2137
2137
  * never drew, so the press did nothing visible now and changed a
2138
2138
  * later expansion's shape;
2139
2139
  * - CALLS IN FLIGHT: one head row each within the budget, the tail
@@ -2178,8 +2178,8 @@ export class Body {
2178
2178
  //
2179
2179
  // (The approval half is a regression this round caused and its
2180
2180
  // gate caught: the first draft treated a pending approval as a
2181
- // call in flight, so `⏸ edit x.ts` lost its diff tail and the
2182
- // `ctrl+r to expand` note with it.)
2181
+ // call in flight, so `❯ edit x.ts` lost its diff tail and the
2182
+ // `ctrl+o to expand` note with it.)
2183
2183
  const owned = tools.filter((i) => toolAt(i).expanded || toolAt(i).state === "approval");
2184
2184
  if (owned.length > 0) {
2185
2185
  // In CELL ORDER, so the frame reads the way the work happened:
@@ -2638,7 +2638,7 @@ export class Body {
2638
2638
  panelSpan = frame.options;
2639
2639
  }
2640
2640
  else {
2641
- // TUI2-R2 ⑤ (D, candidate 1): the FOCUS — the cell the next ctrl+r
2641
+ // TUI2-R2 ⑤ (D, candidate 1): the FOCUS — the cell the next ctrl+o
2642
2642
  // will act on brightens its own token. The index is derived from
2643
2643
  // the SAME scan expandNext performs (#focusIndex shares its rule
2644
2644
  // by construction), so the marker can never point at a cell the
@@ -2882,11 +2882,11 @@ export class Body {
2882
2882
  #commitCell(i, W, ctx) {
2883
2883
  const cell = this.#cells[i];
2884
2884
  const lines = this.#foldOrRollup(cell, i, W, ctx);
2885
- // W15: a tool cell whose committed rows carried the "ctrl+r"
2885
+ // W15: a tool cell whose committed rows carried the "ctrl+o"
2886
2886
  // affordance joins the expand history — the detection is the
2887
2887
  // renderer's OWN output, so the read's "/last"-only cut note never
2888
2888
  // lands here. TUI2-R1 (A/B): the affordance is no longer only the
2889
- // renderer cut's "└ … ctrl+r" — the self-naming head suffix and
2889
+ // renderer cut's "└ … ctrl+o" — the self-naming head suffix and
2890
2890
  // the exploration row carry it on the HEAD row, and a promise the
2891
2891
  // key does not answer would be the one thing worse than silence.
2892
2892
  // unshift: the cells commit oldest-first, so the NEWEST cut lands
@@ -2900,13 +2900,13 @@ export class Body {
2900
2900
  // printed bytes.
2901
2901
  //
2902
2902
  // This used to require the rendered rows to contain the literal
2903
- // "ctrl+r", which made the affordance LOAD-BEARING: retiring the
2903
+ // "ctrl+o", which made the affordance LOAD-BEARING: retiring the
2904
2904
  // printed key (the owner's ruling) would have silently emptied the
2905
2905
  // ring and taken the expand key with it — not a missing hint, a
2906
2906
  // missing feature. A fold head is a fold head because the segment
2907
2907
  // says so; a tool cell is expandable when it is hiding rows.
2908
2908
  const isFoldHead = this.#segmentOf(i)?.headCell === i;
2909
- const hidesRows = cell.kind === "tool" && lines.some((l) => l.includes("ctrl+r"));
2909
+ const hidesRows = cell.kind === "tool" && lines.some((l) => l.includes("ctrl+o"));
2910
2910
  if (isFoldHead || hidesRows) {
2911
2911
  this.#collapsed.unshift(i);
2912
2912
  // R4a — a new fold resets the walk, so the FIRST press after any
@@ -3161,7 +3161,7 @@ export class Body {
3161
3161
  if (cell.kind !== "tool" || !isExploreTool(cell.name))
3162
3162
  return false;
3163
3163
  // the run is still growing while NOTHING but explore cells follow —
3164
- // the turn-less noise cells (permission raws, notices) are
3164
+ // the turn-less noise cells (permission raws, notices) are
3165
3165
  // transparent here for the same reason the run scan sees through
3166
3166
  // them: the streaming execution interleaves them between the calls.
3167
3167
  for (let j = i + 1; j < this.#cells.length; j += 1) {
@@ -3270,7 +3270,7 @@ export class Body {
3270
3270
  // split every real burst into fragments. Writes, edits, shells and
3271
3271
  // extension tools still break the run at the first one.
3272
3272
  // the maximal read-only run around i — forward/backward scans over
3273
- // the cells. The turn-less noise cells (the permission raws, the
3273
+ // the cells. The turn-less noise cells (the permission raws, the
3274
3274
  // notices) are TRANSPARENT: the streaming execution (loop.ts launch)
3275
3275
  // interleaves them BETWEEN the calls of one burst, so the run must
3276
3276
  // see through them. It never crosses a user/text/thinking cell —
package/dist/editor.js CHANGED
@@ -83,7 +83,7 @@ export function viewerCommand(text) {
83
83
  return "end";
84
84
  case "\x1b":
85
85
  case "q":
86
- case "\x0f": // ctrl+o — the key that opens it also puts it away
86
+ case "\x12": // ctrl+r — the key that opens it also puts it away
87
87
  return "close";
88
88
  default:
89
89
  return null;
@@ -275,7 +275,7 @@ export class Editor {
275
275
  // the CLI's — here it is only "these two keys, pressed together, hand
276
276
  // the line over by a different door than Enter's".
277
277
  #redirectCbs = [];
278
- // W15: the expand-key list (ctrl+r) — the CLI's dispatch decides the
278
+ // W15: the expand-key list (ctrl+o) — the CLI's dispatch decides the
279
279
  // target (a live cell toggles in place; a committed cell appends the
280
280
  // expanded block). Mirrors the escape list: multiple listeners can
281
281
  // coexist; the editor never interprets the key itself.
@@ -929,7 +929,7 @@ export class Editor {
929
929
  // sheet (which any key dismisses) this surface is INTERACTIVE, so
930
930
  // the chunk is matched against its own bindings and anything
931
931
  // unrecognised is swallowed rather than typed into the composer
932
- // behind it. esc closes; ctrl+o closes too, so the key that opens
932
+ // behind it. esc closes; ctrl+r closes too, so the key that opens
933
933
  // it also puts it away.
934
934
  if (this.#viewerUp?.() === true) {
935
935
  const cmd = viewerCommand(text);
@@ -1407,18 +1407,30 @@ export class Editor {
1407
1407
  }
1408
1408
  i += 1;
1409
1409
  }
1410
- else if (c === "\x12") {
1411
- // W15: the expand key (ctrl+r) — rides the chain like a
1412
- // command, the editor just forwards it.
1410
+ else if (c === "\x0f") {
1411
+ // W15: the expand key — rides the chain like a command, the
1412
+ // editor just forwards it.
1413
+ //
1414
+ // DC-41 (owner ruling 2026-09-02): this is ctrl+o now. R5
1415
+ // gave ctrl+o to the viewer because it was free and because
1416
+ // the reference implementation uses it — betting that the
1417
+ // finger cared about the KEY. It does not: the reference's
1418
+ // ctrl+o expands the tool output in front of you, which is
1419
+ // this action, and the owner reported reaching for it and
1420
+ // getting a reader. The gesture and the job are together
1421
+ // again; §7.1 is why kiso's form of it appends rather than
1422
+ // toggling in place.
1413
1423
  for (const cb of [...this.#expandCbs])
1414
1424
  cb();
1415
1425
  i += 1;
1416
1426
  }
1417
- else if (c === "\x0f" && this.#composerIdle() && this.#chars.length === 0) {
1418
- // R5 — ctrl+o opens the transcript viewer. Free in kiso, and
1419
- // the same key the reference implementation uses, so the
1420
- // muscle memory transfers even though what it opens is not
1421
- // the same surface. Idle composer only, exactly like `?`:
1427
+ else if (c === "\x12" && this.#composerIdle() && this.#chars.length === 0) {
1428
+ // R5 — the transcript viewer, on ctrl+r since DC-41. The
1429
+ // reference binds ctrl+r to renaming a session, which kiso
1430
+ // has no equivalent of, so nothing a reference user means by
1431
+ // it is displaced; every other free control key in this
1432
+ // dispatch collides with something that surface actually
1433
+ // does. Idle composer only, exactly like `?`:
1422
1434
  // mid-text it would be a keystroke stolen from the human.
1423
1435
  this.#viewerSend("open");
1424
1436
  i += 1;
package/dist/render.js CHANGED
@@ -72,7 +72,7 @@ export function renderEvent(ev, prevThinking = false, resolvePath = (p) => p) {
72
72
  case "permission_requested":
73
73
  // round 8: the tool NAME is model text — escaped like everything else.
74
74
  return {
75
- text: `⏸ ${escapeTerminal(ev.name)} needs approval ${p.dim}${approvalDetail(ev.name, ev.input, resolvePath)}${p.reset} `,
75
+ text: `❯ ${escapeTerminal(ev.name)} needs approval ${p.dim}${approvalDetail(ev.name, ev.input, resolvePath)}${p.reset} `,
76
76
  newline: false,
77
77
  prompt: true,
78
78
  };
@@ -100,7 +100,7 @@ export function renderEvent(ev, prevThinking = false, resolvePath = (p) => p) {
100
100
  return { text: `${p.dim} [summarized up to seq ${ev.coversToSeq}]${p.reset}\n`, newline: true, prompt: false };
101
101
  case "uncertain_pending":
102
102
  return {
103
- text: `${p.red}${escapeTerminal(ev.name)} failed (${ev.executionId}): ${escapeTerminal(ev.error.slice(0, 160))}${p.reset}\n`,
103
+ text: `${p.red}${escapeTerminal(ev.name)} failed (${ev.executionId}): ${escapeTerminal(ev.error.slice(0, 160))}${p.reset}\n`,
104
104
  newline: true,
105
105
  prompt: false,
106
106
  };
@@ -38,7 +38,7 @@ import { visibleWidth } from "./components.js";
38
38
  /** The gutter every viewer row carries: the cursor mark and its space. */
39
39
  export const VIEWER_GUTTER = 3;
40
40
  export function viewerInit(entries) {
41
- // the cursor starts on the NEWEST fold — the one ctrl+r would have
41
+ // the cursor starts on the NEWEST fold — the one ctrl+o would have
42
42
  // opened, so the two mechanisms agree on their first answer.
43
43
  return { cursor: Math.max(0, entries.length - 1), top: 0, open: new Set() };
44
44
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-tui",
3
- "version": "0.20.4",
3
+ "version": "0.21.1",
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.20.4"
38
+ "@vincemakes/kiso-tui-cells": "0.21.1"
39
39
  }
40
40
  }