@vincemakes/kiso-tui-cells 0.25.0 → 0.26.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.
@@ -40,18 +40,6 @@ export interface FrameCtx {
40
40
  * the tier table reads H, so a resize RE-TIERS instead of
41
41
  * re-folding frozen rows). */
42
42
  readonly height: number;
43
- /** R7a — this cell is drawn UNDER an activity header that carries the
44
- * breathing mark, so its own head row wears a plain gutter.
45
- *
46
- * The mark belongs to the ACTIVITY, not to each call in it. A
47
- * four-file burst drew four breathing marks, which is four marks
48
- * distinguishing nothing (law 1.3, the same ground R2 retired the
49
- * tick and cross on) — and worse, on a read that finishes in
50
- * 200ms the mark is gone before the eye lands, so per-row it is
51
- * motion that never resolves into meaning. On the header it is lit
52
- * for the whole stretch, which is the fact it is there to carry:
53
- * work is in flight. Owner-ruled 2026-08-31. */
54
- readonly grouped?: boolean;
55
43
  /** R13 E2 / DC-43 — how many PREVIEW rows a running card may take this
56
44
  * frame. Undefined is the full window (`CAP_PREVIEW`); the compositor
57
45
  * lowers it when the live region is tight, and 0 degrades the card to
@@ -77,7 +65,8 @@ export declare function foldLine(line: string, W: number): string[];
77
65
  * renderer can measure without importing this module back — the
78
66
  * re-export is verbatim, so every existing importer and the barrel see
79
67
  * exactly what they saw. */
80
- export { visibleWidth } from "./width.js";
68
+ export { visibleWidth, widthCut } from "./width.js";
69
+ export { cutLine } from "./render.js";
81
70
  /** A component: render the display lines for one piece of state. */
82
71
  export interface Component {
83
72
  render(width: number, ctx: FrameCtx): string[];
@@ -90,7 +79,7 @@ export interface Component {
90
79
  * `prev` is the previous sibling's OWN rows (raw — a cell's own blank
91
80
  * must never count toward its height). The blank is a JOIN artifact:
92
81
  * the cell's own render stays blank-free, so per-cell accounting
93
- * (heights, the fold cache) never sees a fake row. */
82
+ * (heights, the line cache) never sees a fake row. */
94
83
  export declare function bodySpacing(prev: readonly string[] | null, rows: readonly string[]): string[];
95
84
  /** The container — vertical concatenation with the W11 formula. No
96
85
  * component decides its own spacing: every blank in the body is the
@@ -133,9 +122,9 @@ export type BodyCell = {
133
122
  * rows and redraws them); a committed cell can never toggle
134
123
  * (history is never rewritten — ADR-0046). */
135
124
  expanded: boolean;
136
- /** W14: the turn boundary — the index of the turn record that
137
- * created this cell (the fold-hold's owner; −1 when no turn
138
- * exists yet the pre-turn cells never hold). */
125
+ /** W14: the index of the turn record that created this cell
126
+ * (−1 before the first turn). The checklist reads it: a task
127
+ * list belongs to its turn. */
139
128
  turn: number;
140
129
  /** W19: a DENIED call's reason (the CLI extracted it from the
141
130
  * result's "[Permission denied] " prefix, keyed on the "denied"
@@ -229,6 +218,24 @@ export declare function cellComponent(cell: BodyCell): Component;
229
218
  * (the gutter's 2 cells), so a long line hard-folds INSIDE the chip
230
219
  * and invariant ① holds on the band. */
231
220
  export declare function pendingQueueRows(lines: readonly string[], W: number): string[];
221
+ /**
222
+ * TUI2-R1.5 ⑨ (VD-10) — the WORD-aware fold, for text a human reads.
223
+ *
224
+ * foldLine is a hard character fold at the width. That is exactly right
225
+ * for verbatim tool output, where a byte is a byte and a break is a
226
+ * display artefact the reader knows to ignore; it is exactly wrong for
227
+ * prose, where the reader's eye has to reassemble "ex" + "pected" into a
228
+ * word it already knew. The walkthrough read three of those off one
229
+ * screen.
230
+ *
231
+ * The implementation is a wrapper, not a second engine: the text is cut
232
+ * at the last space that fits and each resulting segment is handed to
233
+ * foldLine, which keeps the SGR close/reopen discipline, the display-
234
+ * width arithmetic and the newline handling in ONE place. A word longer
235
+ * than the width falls through to foldLine's hard break — an
236
+ * overflowing row would violate invariant ①, and a word that cannot fit
237
+ * has to be broken somewhere.
238
+ */
232
239
  export declare function foldWords(line: string, W: number): string[];
233
240
  export declare function gutterFold(gutter: string, line: string, W: number): string[];
234
241
  /** A6: the tool-header variant — ONE cut row, never a fold. A wide
@@ -239,81 +246,7 @@ export declare function gutterFold(gutter: string, line: string, W: number): str
239
246
  * full content. The budget: the gutter's own visible width + the
240
247
  * ellipsis ride the row (the invariant ① cap holds). */
241
248
  export declare function gutterCut(gutter: string, line: string, W: number): string[];
242
- export declare function expandSuffix(lines: number | null, room: number): string;
243
- /**
244
- * TUI2-R2 ⑤ (D, candidate 1) — the FOCUS tint.
245
- *
246
- * The cell the next ctrl+o will act on brightens its own `ctrl+o` token
247
- * to the code tint; the rest of the suffix — the separator, the count —
248
- * stays dim, because what is being marked is the KEY's target, not the
249
- * row. Zero new rows, zero new columns: the affordance the cell already
250
- * prints is the marker.
251
- *
252
- * Applied to a row rather than composed into it on purpose. The token is
253
- * emitted from several places (the settled suffix, the renderer's own
254
- * `└ +N … · ctrl+o` cut rows) and threading a flag through all of them
255
- * would put the invariant "exactly one bright token" in as many hands as
256
- * there are emitters. Here it has exactly one.
257
- *
258
- * NO_COLOR: p.dim is empty, so the row's bytes are untouched.
259
- */
260
- export declare function focusToken(row: string, W: number): string;
261
- /** W13 — the rollup opt-in table: which tools collapse, and the count
262
- * NOUN (read_file calls → "5 files", list_dir → "5 dirs", search_text
263
- * → "5 matches"). Only these tools opt in — a shell burst is never
264
- * rolled up (its rows carry meaning). The folded-turn line (W14) reuses
265
- * the plurals for its other-tool terms ("2 dirs", "1 match"). */
266
- export declare const ROLLUP_NOUN: Readonly<Record<string, string>>;
267
- /** TUI2-R1 (B) — the verb column of the expanded list names the ACT.
268
- * TUI2-R2pre ④: this used to be a private three-tool table saying the
269
- * same thing as the card head's `_file` strip, in a different way and
270
- * for a different set of tools. Both are `displayVerb` now — the whole
271
- * point of the ruling is that there is ONE answer to "what does the
272
- * screen call this". The cut note, which used to be the deliberate
273
- * exception here, moved with it (see toolCutNote). */
274
- /** Whether a tool joins an exploration run. Exactly the read-only set —
275
- * writes, edits, shells and extension tools never group (a burst of
276
- * side effects is a list of things that HAPPENED, and every row of it
277
- * carries meaning). */
278
- export declare function isExploreTool(name: string): boolean;
279
- /** Does this tool's fold term count distinct targets rather than calls? */
280
- export declare function foldCountsObjects(name: string): boolean;
281
249
  export declare function foldTerms(reads: number, edits: number, others: readonly [string, number][]): string[];
282
- /**
283
- * R3i — THE STRETCH LINE: the turn's one working row, in three phases.
284
- *
285
- * A STRETCH is the run of thinking and tool calls between two blocks of
286
- * the model's prose. While it runs it is this line plus a bounded act
287
- * window; when it closes it commits as this same line, frozen, with its
288
- * key. The contract in one sentence: **the line you watch is the line
289
- * you keep** — the settle changes the mark, the tense and the key, and
290
- * nothing else.
291
- *
292
- * thinking ✧ thinking 4s
293
- * acting ✶ reading 6 files · running 4 shell commands
294
- * settled ✦ thought 9s · read 6 files · ran 4 shell commands · ctrl+o
295
- *
296
- * THE GIVE-WAY LADDER, in order, because at some width everything
297
- * cannot fit:
298
- *
299
- * 1. the human's WORDS (the A9 chip on a quiet turn) — they are on
300
- * screen above, in the chip band;
301
- * 2. the NOUNS compact, cheapest word first, and stop as soon as the
302
- * row fits — buying one cell must not spend every substitution;
303
- * 3. the COUNTS cut, with the honest "…";
304
- * 4. the TROUBLE CLAUSE cuts. The design first said it never gives
305
- * way, and that was unimplementable: a long clause overflows after
306
- * the counts have already cut to a bare "…", and invariant ①
307
- * throws on that row;
308
- * 5. the KEY gives way NEVER. A fold with no key is the turn's work
309
- * behind a line with no way back to it, which is the one thing
310
- * this row must not be.
311
- *
312
- * `…` in this file means CUT HERE and nothing else — which is why the
313
- * live phases carry no trailing ellipsis for in-flight, though the
314
- * reference implementation uses one. The moving mark and the present
315
- * tense already say it twice.
316
- */
317
250
  /**
318
251
  * R3i phase 5 — THE ANSWERED QUESTION'S BLOCK.
319
252
  *
@@ -331,7 +264,7 @@ export declare function foldTerms(reads: number, edits: number, others: readonly
331
264
  * is emphasis, never information). A typed answer says `(typed)`,
332
265
  * because where an answer came from is a fact about it.
333
266
  *
334
- * It is WORDS, not work (law 1.7): it never folds into a stretch line,
267
+ * It is WORDS, not work (law 1.7): no summary ever stands for it,
335
268
  * because the one thing a summary must not do is speak for the human.
336
269
  *
337
270
  * A result that is not the ask's own JSON yields NOTHING. This renderer
@@ -339,97 +272,16 @@ export declare function foldTerms(reads: number, edits: number, others: readonly
339
272
  * would be a row the product cannot stand behind.
340
273
  */
341
274
  export declare function askedBlock(resultText: string, seconds: number, W: number): string[];
342
- export interface StretchTerms {
343
- /** the segment's OWN measured thinking seconds; 0 drops the term */
344
- readonly thoughtSeconds: number;
345
- /** the segment's calls as [tool name, count], in first-call order.
346
- * Object-counting tools are deduped by target upstream (R3h). */
347
- readonly calls: readonly (readonly [string, number])[];
348
- /** the targets acted on — read only when the stretch made exactly
349
- * ONE call, where naming the target says everything the two rows it
350
- * replaces said (see the one-call rule below). */
351
- readonly targets: readonly string[];
352
- /** the trouble the stretch met: [kind, count, what it was]. */
353
- readonly trouble: readonly (readonly ["failed" | "denied" | "interrupted", number, string])[];
354
- /** R4 — the tool names that still have a call IN FLIGHT. The tense is
355
- * PER TERM, not per line: a stretch whose shell has finished while a
356
- * read runs says `ran 1 shell command · reading 1 file`. The whole
357
- * line used to go progressive, which the standing act slot made a
358
- * visible contradiction — the gap frame put `running npm run check`
359
- * directly above a row reading `(exit 0, 12.4s)`. Absent ⇒ every
360
- * term takes the line's own tense, which is what the settled line
361
- * wants. */
362
- readonly liveNames?: readonly string[];
363
- /** A9 — the human's words, on a QUIET turn's fold only. */
364
- readonly words?: string;
365
- /** the live mark; the caller passes the spinner's current frame. */
366
- readonly mark?: string;
367
- }
368
275
  /** The caps — screen rows counted AFTER the fold, at the current width
369
276
  * (the W7 table). The renderer-cut row is inside the cap. */
370
277
  /** R13 — ONE preview cap, every tool. It was the shell's alone while
371
278
  * the shell was the only settled call with rows on screen. */
372
279
  export declare const CAP_PREVIEW = 5;
373
- /** DC-46 — the running window's ceiling is the SETTLED preview's, and a
374
- * running card reaches it by growing rather than by being handed it.
375
- * `LIVE_WINDOW` (CAP_PREVIEW + 1) retires with the allocation it sized. */
376
- /** The rows a card costs besides its window: two pads, the head, two
377
- * blanks and the status row. Below this there is no card (DC-43). */
378
- export declare const CARD_CHROME = 6;
379
280
  /** 0.24.2 ② — the live region's `thinking…` placeholder: dim italic at
380
281
  * column 2, no glyph, the SAME shape a thinking paragraph takes so that
381
282
  * whatever arrives replaces it in place. Never committed — see the
382
283
  * compositor's #project for why that is what makes it allowed. */
383
284
  export declare function thinkingRow(): string;
384
- /**
385
- * R4 — the standing act slot.
386
- *
387
- * The stretch's ONE line sits above it; this is the region under it,
388
- * and it STANDS: allocated when the stretch opens, released at the
389
- * fold.
390
- *
391
- * R3i built the same window INTERMITTENTLY — a running call got its
392
- * fixed 1+3 block (W8), a finished one got nothing — so the live
393
- * region's height was a function of how many calls happened to be in
394
- * flight this frame. Over one real stretch that is 2 rows, then 7,
395
- * then 2, then 17 for a three-call batch, then 2 again, and every
396
- * transition scrolls everything above it. The owner's report was that
397
- * the screen "keeps jumping", and it was an accurate description of
398
- * the design, not a defect in its execution.
399
- *
400
- * The cure is not a smaller window, it is a STANDING one: between two
401
- * calls the slot keeps the call that just finished rather than
402
- * collapsing, and before any call it keeps the thinking that is
403
- * producing them — which is R3i ruling 5 ("thinking belongs on the
404
- * stretch line, IN THE ACT WINDOW, and in full in expansions") finally
405
- * wired, since R3i stated it while building no window for the thinking
406
- * phase to live in.
407
- *
408
- * Four rows, deliberately the same 1+3 shape W8 gave a running call, so
409
- * the commonest frame — exactly one call in flight — renders byte-for-
410
- * byte what 0.17.0 shipped.
411
- */
412
- export declare const ACT_SLOT_ROWS = 4;
413
- /**
414
- * R4 — the slot's body rows: the tail of `text`, newest at the BOTTOM,
415
- * bottom-padded to exactly `rows`.
416
- *
417
- * The same dim │ gutter a running call's window uses (W2's table), and
418
- * the same two VD-4 rules: leading blank gutters are skipped, and the
419
- * short-output pad goes at the BOTTOM so output starts under its own
420
- * header and grows downward. The slot's CONTENTS change; its shape
421
- * does not.
422
- */
423
- export declare function slotTail(text: string, W: number, rows: number): string[];
424
- /** R4 — clamp or pad assembled slot rows to EXACTLY `rows`. The padding
425
- * is what makes the slot stand; the clamp is what keeps the slot from
426
- * ever being the thing that trips the force-commit cap (a slot that
427
- * could overflow would commit real cells to relieve blank rows). */
428
- export declare function slotPad(content: readonly string[], rows: number): string[];
429
- /** R4 — the slot's overflow row: the calls in flight beyond the head
430
- * budget. It lives INSIDE the slot (it is one of the four rows), which
431
- * is what keeps a parallel burst from growing the region. */
432
- export declare function moreRunningRow(n: number, W: number): string;
433
285
  /** The approval mini-diff (W7): capped at 12 folded rows — the head +
434
286
  * the named middle (the renderer cut — what was cut, how to expand) +
435
287
  * the tail. The rows are folded at the current width BEFORE the cap —
@@ -451,16 +303,6 @@ export declare function diffBody(diff: import("./diff.js").DiffLine[] | null, W:
451
303
  * done-collapse. Every live row CUTS at W (never folds) — the block's
452
304
  * height is its row count. */
453
305
  export declare const CAP_TASK_LIVE = 6;
454
- /** W20 — the live block's fixed-window row cut: an SGR-aware ONE-ROW
455
- * truncation (foldLine wraps; a wrapped row would break the height
456
- * cap — every live row is exactly one screen row at every width).
457
- * A line that fits (≤ W) passes through whole; an overflow cuts the
458
- * content at W−1 — the ellipsis's slot — and the ellipsis rides AFTER
459
- * the reset (post-reset — the PTY needles' convention). The cut row
460
- * never exceeds W (invariant ①). W21: exported for the approval
461
- * panel's single-row lines (the rule line, the title, the divider,
462
- * the options/affordance rows). */
463
- export declare function cutLine(line: string, W: number): string;
464
306
  /** W20 — the settled block's duration, the `2h 14m` form (the task
465
307
  * narrative's long-horizon idiom): minutes+seconds under an hour,
466
308
  * hours+minutes past it. */
@@ -487,11 +329,8 @@ export declare function formatDuration(totalSeconds: number): string;
487
329
  * survives longest because it is the door to everything; `ctrl+r`
488
330
  * outranks `↑ history` because pressing up is how a person finds the
489
331
  * history by accident, and nothing finds ctrl+r by accident. */
490
- export declare function idleHint(room: number): string;
491
- export declare function statusLine(status: string, tail: string, W: number, hint?: string): string;
492
- /** The display-width prefix of a plain (SGR-free) text. W21: exported
493
- * for the approval panel's option-2 rule-name cut. */
494
- export declare function widthCut(text: string, max: number): string;
332
+ export declare function idleHint(room: number, expand?: "expand all" | "collapse all" | null): string;
333
+ export declare function statusLine(status: string, tail: string, W: number, hint?: string, expand?: "expand all" | "collapse all" | null): string;
495
334
  /**
496
335
  * TUI2-R3v2 ① — THE selection bar. One engine, every selection surface.
497
336
  *