@vincemakes/kiso-tui 0.20.1 → 0.20.2

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.
@@ -227,6 +227,22 @@ export declare class Body {
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
+ /**
231
+ * DC-35 — ctrl+r does not print the same expansion twice in a row.
232
+ *
233
+ * The ring walks newest-back and restarts its cycle once every entry
234
+ * has been opened (R4/C1, which is what makes the walk immune to the
235
+ * ring growing underneath it). With a ring of ONE the restart is
236
+ * immediate, so holding the key appended the identical block over
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
239
+ * something that does not exist.
240
+ *
241
+ * The bar is the BOTTOM of the transcript, not "ever shown": once
242
+ * other content has arrived the expansion has scrolled up and
243
+ * re-opening it is the point of the key, so the guard clears itself
244
+ * the moment a cell is added.
245
+ */
230
246
  expandNext(): {
231
247
  kind: "toggled";
232
248
  } | {
@@ -234,6 +250,7 @@ export declare class Body {
234
250
  lines: string[];
235
251
  } | {
236
252
  kind: "none";
253
+ why?: "already-last";
237
254
  };
238
255
  /** Docked = the chrome is live (a color TTY with a real size). */
239
256
  get active(): boolean;
@@ -1336,7 +1336,42 @@ export class Body {
1336
1336
  cutLine(`${p.dim} ${viewerHint(this.#viewer, entries)}${p.reset}`, W),
1337
1337
  ];
1338
1338
  }
1339
+ /** DC-35 — the last block this key appended, and the cell count when
1340
+ * it did. An expansion earns its rows by showing something the
1341
+ * transcript does not already END with. */
1342
+ #lastAppend = null;
1343
+ /**
1344
+ * DC-35 — ctrl+r does not print the same expansion twice in a row.
1345
+ *
1346
+ * The ring walks newest-back and restarts its cycle once every entry
1347
+ * has been opened (R4/C1, which is what makes the walk immune to the
1348
+ * ring growing underneath it). With a ring of ONE the restart is
1349
+ * immediate, so holding the key appended the identical block over
1350
+ * and over — the owner got three copies of the same four rows, each
1351
+ * closing with `ctrl+r opens the one before it`, a footer naming
1352
+ * something that does not exist.
1353
+ *
1354
+ * The bar is the BOTTOM of the transcript, not "ever shown": once
1355
+ * other content has arrived the expansion has scrolled up and
1356
+ * re-opening it is the point of the key, so the guard clears itself
1357
+ * the moment a cell is added.
1358
+ */
1339
1359
  expandNext() {
1360
+ const out = this.#expandNextRaw();
1361
+ if (out.kind !== "appended")
1362
+ return out;
1363
+ const lines = out.lines.join("\n");
1364
+ if (this.#lastAppend !== null && this.#lastAppend.lines === lines && this.#lastAppend.atCells === this.#cells.length) {
1365
+ // NOT the same answer as "nothing is folded". The caller says
1366
+ // which, because a reader who pressed the key deserves to know
1367
+ // whether there is nothing to open or whether they are already
1368
+ // looking at it.
1369
+ return { kind: "none", why: "already-last" };
1370
+ }
1371
+ this.#lastAppend = { lines, atCells: this.#cells.length };
1372
+ return out;
1373
+ }
1374
+ #expandNextRaw() {
1340
1375
  for (let i = this.#cells.length - 1; i >= this.#committed; i -= 1) {
1341
1376
  const cell = this.#cells[i];
1342
1377
  if (cell.kind === "tool" && cell.state !== "pending") {
package/dist/editor.js CHANGED
@@ -1004,7 +1004,11 @@ export class Editor {
1004
1004
  i += 1;
1005
1005
  continue;
1006
1006
  }
1007
- if (!typing && (c === "t" || c === "T")) {
1007
+ // DC-36: no `t` row means no custom phase to enter — the
1008
+ // option list IS the world (a closed set), and a key
1009
+ // that leads to a surface the panel does not draw is
1010
+ // worse than an absent key.
1011
+ if (!typing && (c === "t" || c === "T") && this.#panel?.view.pick?.typeHint !== undefined) {
1008
1012
  panel.pick = { cursor: panel.pick.cursor, phase: "custom" };
1009
1013
  this.#chars = [];
1010
1014
  this.#cursor = 0;
package/dist/index.d.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  * editor, the diff renderer, and the palette.
8
8
  */
9
9
  export { Body, Dock, CURSOR_MARKER, type BodyOptions } from "./compositor.js";
10
- export { panelAffordance, panelBlockRows, panelLead, panelLeadPlain, panelLeadWidth, panelStatus, PICK_MAX, modelPickView, pickAffordance, pickBlockRows, pickLeadPlain, type PickOption, type PickResult, type PickRuntime, type PickSpec, type PanelArgs, type PanelFlavor, type PanelPhase, deletionRiskHint, SAFER_BACK, SAFER_DEGRADED, SAFER_DEGRADED_TRUNCATED, saferDegradedNote, type SaferAnswer, type SaferFailure, type SaferOption, type SaferRuntime, panelOptions, type PanelOption, type PanelOptionKind, type PanelState, type PanelVerdict, type PanelView, } from "./approval-panel.js";
10
+ export { panelAffordance, panelBlockRows, panelLead, panelLeadPlain, panelLeadWidth, panelStatus, PICK_MAX, modePickView, modelPickView, pickAffordance, pickBlockRows, pickLeadPlain, type PickOption, type PickResult, type PickRuntime, type PickSpec, type PanelArgs, type PanelFlavor, type PanelPhase, deletionRiskHint, SAFER_BACK, SAFER_DEGRADED, SAFER_DEGRADED_TRUNCATED, saferDegradedNote, type SaferAnswer, type SaferFailure, type SaferOption, type SaferRuntime, panelOptions, type PanelOption, type PanelOptionKind, type PanelState, type PanelVerdict, type PanelView, } from "./approval-panel.js";
11
11
  export { Container, foldLine, foldWords, visibleWidth, SPINNER, type Component, type FrameCtx } from "./components.js";
12
12
  export { Editor, MENU_ITEMS, PROMPT, PROMPT_WIDTH, displayWidth, charWidth, widthOf, type MenuItem, } from "./editor.js";
13
13
  export { bannerLines, COLOR_OFF, COLOR_ON, currentGround, setGround, escapeTerminal, foldResult, foldThinking, kUnit, palette, renderEvent, renderRecap, renderResumeList, renderSessionLine, renderStatusLine, relativeTime, renderTerminalGap, renderToolSummary, TAGLINE, toolTarget, truncateRow, type Palette, type PathResolver, type RecapStats, type ResumeMeta, type RenderInput, type RenderResult, type RunUsage, } from "./render.js";
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ export { Body, Dock, CURSOR_MARKER } from "./compositor.js";
12
12
  // approval is pending (the shape authority is the committed preview).
13
13
  export { panelAffordance, panelBlockRows, panelLead, panelLeadPlain, panelLeadWidth, panelStatus,
14
14
  // TUI2-R2 ④: the pick payload — the panel slot's third occupant.
15
- PICK_MAX, modelPickView, pickAffordance, pickBlockRows, pickLeadPlain, deletionRiskHint, SAFER_BACK, SAFER_DEGRADED, SAFER_DEGRADED_TRUNCATED, saferDegradedNote, panelOptions, } from "./approval-panel.js";
15
+ PICK_MAX, modePickView, modelPickView, pickAffordance, pickBlockRows, pickLeadPlain, deletionRiskHint, SAFER_BACK, SAFER_DEGRADED, SAFER_DEGRADED_TRUNCATED, saferDegradedNote, panelOptions, } from "./approval-panel.js";
16
16
  export { Container, foldLine, foldWords, visibleWidth, SPINNER } from "./components.js";
17
17
  export { Editor, MENU_ITEMS, PROMPT, PROMPT_WIDTH, displayWidth, charWidth, widthOf, } from "./editor.js";
18
18
  export { bannerLines, COLOR_OFF, COLOR_ON, currentGround, setGround, escapeTerminal, foldResult, foldThinking, kUnit, palette, renderEvent, renderRecap, renderResumeList, renderSessionLine, renderStatusLine, relativeTime, renderTerminalGap, renderToolSummary, TAGLINE, toolTarget, truncateRow, } from "./render.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-tui",
3
- "version": "0.20.1",
3
+ "version": "0.20.2",
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.1"
38
+ "@vincemakes/kiso-tui-cells": "0.20.2"
39
39
  }
40
40
  }