@vincemakes/kiso-tui-cells 0.17.0 → 0.18.0

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.
@@ -355,13 +355,44 @@ export interface StretchTerms {
355
355
  readonly targets: readonly string[];
356
356
  /** the trouble the stretch met: [kind, count, what it was]. */
357
357
  readonly trouble: readonly (readonly ["failed" | "denied" | "interrupted", number, string])[];
358
+ /** R4 — the tool names that still have a call IN FLIGHT. The tense is
359
+ * PER TERM, not per line: a stretch whose shell has finished while a
360
+ * read runs says `ran 1 shell command · reading 1 file`. The whole
361
+ * line used to go progressive, which the standing act slot made a
362
+ * visible contradiction — the gap frame put `running npm run check`
363
+ * directly above a row reading `(exit 0, 12.4s)`. Absent ⇒ every
364
+ * term takes the line's own tense, which is what the settled line
365
+ * wants. */
366
+ readonly liveNames?: readonly string[];
358
367
  /** A9 — the human's words, on a QUIET turn's fold only. */
359
368
  readonly words?: string;
360
369
  /** the live mark; the caller passes the spinner's current frame. */
361
370
  readonly mark?: string;
362
371
  }
372
+ /**
373
+ * R4 (C1) — the fold NAMES ITS OWN TARGET.
374
+ *
375
+ * `ctrl+r` used to be printed identically on every fold on the screen,
376
+ * and the key walked a ring whose order nothing on screen expressed —
377
+ * so the owner's report was exact: "there is no way to know which
378
+ * stretch it opens". The tint that marks the next target can only be
379
+ * drawn on a LIVE row, and every fold worth reopening is, by
380
+ * construction, in the scrollback where nothing can be tinted.
381
+ *
382
+ * A pointer cannot fix this either, and the bound is worth stating
383
+ * once: SGR mouse reports address the VIEWPORT, so a fold that has
384
+ * scrolled into the terminal's own scrollback is unreachable by any
385
+ * pointer, permanently, on the primary screen. The ordinal is not a
386
+ * cheaper substitute for clicking — it is the form of the affordance
387
+ * that reaches every fold, and it survives a pipe as characters.
388
+ *
389
+ * The number rides the KEY, inside the width ladder, so it is paid for
390
+ * by the same give-way order as every other span (law: the key never
391
+ * gives way — it just got two characters longer).
392
+ */
363
393
  export declare function stretchLine(t: StretchTerms & {
364
394
  readonly phase: "thinking" | "acting" | "settled";
395
+ readonly foldKey?: number;
365
396
  }, W: number): string[];
366
397
  export declare function turnFold(t: {
367
398
  words: string;
@@ -370,6 +401,55 @@ export declare function turnFold(t: {
370
401
  edits: number;
371
402
  others: [string, number][];
372
403
  }, W: number): string[];
404
+ /**
405
+ * R4 — the standing act slot.
406
+ *
407
+ * The stretch's ONE line sits above it; this is the region under it,
408
+ * and it STANDS: allocated when the stretch opens, released at the
409
+ * fold.
410
+ *
411
+ * R3i built the same window INTERMITTENTLY — a running call got its
412
+ * fixed 1+3 block (W8), a finished one got nothing — so the live
413
+ * region's height was a function of how many calls happened to be in
414
+ * flight this frame. Over one real stretch that is 2 rows, then 7,
415
+ * then 2, then 17 for a three-call batch, then 2 again, and every
416
+ * transition scrolls everything above it. The owner's report was that
417
+ * the screen "keeps jumping", and it was an accurate description of
418
+ * the design, not a defect in its execution.
419
+ *
420
+ * The cure is not a smaller window, it is a STANDING one: between two
421
+ * calls the slot keeps the call that just finished rather than
422
+ * collapsing, and before any call it keeps the thinking that is
423
+ * producing them — which is R3i ruling 5 ("thinking belongs on the
424
+ * stretch line, IN THE ACT WINDOW, and in full in expansions") finally
425
+ * wired, since R3i stated it while building no window for the thinking
426
+ * phase to live in.
427
+ *
428
+ * Four rows, deliberately the same 1+3 shape W8 gave a running call, so
429
+ * the commonest frame — exactly one call in flight — renders byte-for-
430
+ * byte what 0.17.0 shipped.
431
+ */
432
+ export declare const ACT_SLOT_ROWS = 4;
433
+ /**
434
+ * R4 — the slot's body rows: the tail of `text`, newest at the BOTTOM,
435
+ * bottom-padded to exactly `rows`.
436
+ *
437
+ * The same dim │ gutter a running call's window uses (W2's table), and
438
+ * the same two VD-4 rules: leading blank gutters are skipped, and the
439
+ * short-output pad goes at the BOTTOM so output starts under its own
440
+ * header and grows downward. The slot's CONTENTS change; its shape
441
+ * does not.
442
+ */
443
+ export declare function slotTail(text: string, W: number, rows: number): string[];
444
+ /** R4 — clamp or pad assembled slot rows to EXACTLY `rows`. The padding
445
+ * is what makes the slot stand; the clamp is what keeps the slot from
446
+ * ever being the thing that trips the force-commit cap (a slot that
447
+ * could overflow would commit real cells to relieve blank rows). */
448
+ export declare function slotPad(content: readonly string[], rows: number): string[];
449
+ /** R4 — the slot's overflow row: the calls in flight beyond the head
450
+ * budget. It lives INSIDE the slot (it is one of the four rows), which
451
+ * is what keeps a parallel burst from growing the region. */
452
+ export declare function moreRunningRow(n: number, W: number): string;
373
453
  /** The approval mini-diff (W7): capped at 12 folded rows — the head +
374
454
  * the named middle (the renderer cut — what was cut, how to expand) +
375
455
  * the tail. The rows are folded at the current width BEFORE the cap —
@@ -1189,11 +1189,21 @@ const STRETCH_COMPACT = [
1189
1189
  * per-segment folds over; the "absorbs at least two rows" rule alone
1190
1190
  * does not answer it. */
1191
1191
  function stretchTerms(t, live) {
1192
+ // R4 — the tense is per TERM. A name with nothing in flight is in the
1193
+ // past whatever the line's own phase is; with liveNames absent (the
1194
+ // settled line) every term follows the line.
1195
+ const tense = (name) => (live && (t.liveNames === undefined || t.liveNames.includes(name)) ? 1 : 0);
1192
1196
  const total = t.calls.reduce((n, [, c]) => n + c, 0);
1193
- if (total === 1 && t.targets.length === 1) {
1197
+ // R4 the one-call TARGET form is the SETTLED line's. Live, the act
1198
+ // slot directly below already names the target on its head row, so
1199
+ // the line was printing the same words twice, one above the other
1200
+ // (`running npm run check` over `shell npm run check`). Settled there
1201
+ // is no slot, and naming the target is strictly more than counting to
1202
+ // one — which is the R3i rule this keeps, where it applies.
1203
+ if (total === 1 && t.targets.length === 1 && !live) {
1194
1204
  const [name] = t.calls[0];
1195
1205
  const e = TERM[name];
1196
- return [`${e === undefined ? name : e[live ? 1 : 0]} ${t.targets[0]}`];
1206
+ return [`${e === undefined ? name : e[0]} ${t.targets[0]}`];
1197
1207
  }
1198
1208
  return t.calls
1199
1209
  .filter(([, n]) => n > 0)
@@ -1201,7 +1211,7 @@ function stretchTerms(t, live) {
1201
1211
  const e = TERM[name];
1202
1212
  if (e === undefined)
1203
1213
  return `${n} × ${displayVerb(name)}`;
1204
- return `${e[live ? 1 : 0]} ${n} ${n === 1 ? e[2] : e[3]}`;
1214
+ return `${e[tense(name)]} ${n} ${n === 1 ? e[2] : e[3]}`;
1205
1215
  });
1206
1216
  }
1207
1217
  /** R3i — the trouble clause: which call, and what happened, in WORDS.
@@ -1214,11 +1224,32 @@ function troubleClause(t) {
1214
1224
  .map(([kind, n, what]) => (what === "" || kind === "interrupted" ? `${n} ${kind}` : `${n} ${kind}: ${what}`))
1215
1225
  .join(" · ");
1216
1226
  }
1227
+ /**
1228
+ * R4 (C1) — the fold NAMES ITS OWN TARGET.
1229
+ *
1230
+ * `ctrl+r` used to be printed identically on every fold on the screen,
1231
+ * and the key walked a ring whose order nothing on screen expressed —
1232
+ * so the owner's report was exact: "there is no way to know which
1233
+ * stretch it opens". The tint that marks the next target can only be
1234
+ * drawn on a LIVE row, and every fold worth reopening is, by
1235
+ * construction, in the scrollback where nothing can be tinted.
1236
+ *
1237
+ * A pointer cannot fix this either, and the bound is worth stating
1238
+ * once: SGR mouse reports address the VIEWPORT, so a fold that has
1239
+ * scrolled into the terminal's own scrollback is unreachable by any
1240
+ * pointer, permanently, on the primary screen. The ordinal is not a
1241
+ * cheaper substitute for clicking — it is the form of the affordance
1242
+ * that reaches every fold, and it survives a pipe as characters.
1243
+ *
1244
+ * The number rides the KEY, inside the width ladder, so it is paid for
1245
+ * by the same give-way order as every other span (law: the key never
1246
+ * gives way — it just got two characters longer).
1247
+ */
1217
1248
  export function stretchLine(t, W) {
1218
1249
  const p = palette();
1219
1250
  const live = t.phase !== "settled";
1220
1251
  const mark = t.phase === "settled" ? `${p.bold}✦${p.reset}` : `${p.dim}${t.mark ?? "✧"}${p.reset}`;
1221
- const key = t.phase === "settled" ? " · ctrl+r" : "";
1252
+ const key = t.phase === "settled" ? (t.foldKey === undefined ? " · ctrl+r" : ` · ctrl+r ${t.foldKey}`) : "";
1222
1253
  const lead = t.phase === "thinking" ? [`thinking ${t.thoughtSeconds}s`] : t.phase === "settled" && t.thoughtSeconds > 0 ? [`thought ${t.thoughtSeconds}s`] : [];
1223
1254
  const clauseText = troubleClause(t);
1224
1255
  let meta = [...lead, ...(t.phase === "thinking" ? [] : stretchTerms(t, live))].join(" · ");
@@ -1552,6 +1583,77 @@ function shellLiveTail(text, W) {
1552
1583
  kept.push(`${p.dim}${BODY_ROW}${p.reset}`);
1553
1584
  return [...kept, cutLine(`${p.dim}${CUT_ROW}live tail · esc stop · alt+⏎ redirect${p.reset}`, W)];
1554
1585
  }
1586
+ /**
1587
+ * R4 — the standing act slot.
1588
+ *
1589
+ * The stretch's ONE line sits above it; this is the region under it,
1590
+ * and it STANDS: allocated when the stretch opens, released at the
1591
+ * fold.
1592
+ *
1593
+ * R3i built the same window INTERMITTENTLY — a running call got its
1594
+ * fixed 1+3 block (W8), a finished one got nothing — so the live
1595
+ * region's height was a function of how many calls happened to be in
1596
+ * flight this frame. Over one real stretch that is 2 rows, then 7,
1597
+ * then 2, then 17 for a three-call batch, then 2 again, and every
1598
+ * transition scrolls everything above it. The owner's report was that
1599
+ * the screen "keeps jumping", and it was an accurate description of
1600
+ * the design, not a defect in its execution.
1601
+ *
1602
+ * The cure is not a smaller window, it is a STANDING one: between two
1603
+ * calls the slot keeps the call that just finished rather than
1604
+ * collapsing, and before any call it keeps the thinking that is
1605
+ * producing them — which is R3i ruling 5 ("thinking belongs on the
1606
+ * stretch line, IN THE ACT WINDOW, and in full in expansions") finally
1607
+ * wired, since R3i stated it while building no window for the thinking
1608
+ * phase to live in.
1609
+ *
1610
+ * Four rows, deliberately the same 1+3 shape W8 gave a running call, so
1611
+ * the commonest frame — exactly one call in flight — renders byte-for-
1612
+ * byte what 0.17.0 shipped.
1613
+ */
1614
+ export const ACT_SLOT_ROWS = 4;
1615
+ /**
1616
+ * R4 — the slot's body rows: the tail of `text`, newest at the BOTTOM,
1617
+ * bottom-padded to exactly `rows`.
1618
+ *
1619
+ * The same dim │ gutter a running call's window uses (W2's table), and
1620
+ * the same two VD-4 rules: leading blank gutters are skipped, and the
1621
+ * short-output pad goes at the BOTTOM so output starts under its own
1622
+ * header and grows downward. The slot's CONTENTS change; its shape
1623
+ * does not.
1624
+ */
1625
+ export function slotTail(text, W, rows) {
1626
+ if (rows <= 0)
1627
+ return [];
1628
+ const p = palette();
1629
+ const all = blockRows(text, W);
1630
+ const from = all.findIndex((r) => visibleWidth(r) > visibleWidth(BODY_ROW));
1631
+ const body = from < 0 ? [] : all.slice(from);
1632
+ const kept = body.slice(Math.max(0, body.length - rows));
1633
+ while (kept.length < rows)
1634
+ kept.push(`${p.dim}${BODY_ROW}${p.reset}`);
1635
+ return kept;
1636
+ }
1637
+ /** R4 — clamp or pad assembled slot rows to EXACTLY `rows`. The padding
1638
+ * is what makes the slot stand; the clamp is what keeps the slot from
1639
+ * ever being the thing that trips the force-commit cap (a slot that
1640
+ * could overflow would commit real cells to relieve blank rows). */
1641
+ export function slotPad(content, rows) {
1642
+ if (rows <= 0)
1643
+ return [];
1644
+ const p = palette();
1645
+ const out = content.slice(0, rows);
1646
+ while (out.length < rows)
1647
+ out.push(`${p.dim}${BODY_ROW}${p.reset}`);
1648
+ return out;
1649
+ }
1650
+ /** R4 — the slot's overflow row: the calls in flight beyond the head
1651
+ * budget. It lives INSIDE the slot (it is one of the four rows), which
1652
+ * is what keeps a parallel burst from growing the region. */
1653
+ export function moreRunningRow(n, W) {
1654
+ const p = palette();
1655
+ return cutLine(` ${p.dim}${CUT_ROW}+${n} more running${p.reset}`, W);
1656
+ }
1555
1657
  /** W12: the delegate's child sessions collapse to the tool row plus ONE
1556
1658
  * line — the height NEVER changes (running → settled replaces the row
1557
1659
  * in place). The running row derives from the INPUT: the parent has no
package/dist/strings.js CHANGED
@@ -381,6 +381,9 @@ export function helpRows() {
381
381
  ["/help", "print this list of commands"],
382
382
  ["/think", "show the last full thinking block"],
383
383
  ["/last", "show the most recent tool call's input and output"],
384
+ // R4 (C4d): a committed row is the terminal's, and cannot be
385
+ // re-wrapped in place (ADR-0046) — this appends it re-folded.
386
+ ["/rewrap", "re-print the recent prose at the current width"],
384
387
  ["/status", "show session id, event count, and context estimate"],
385
388
  ["/mode", "show the approval tier; /mode <name> switches (manual/default/accept-edits/plan/bypass)"],
386
389
  ["/model", "list model profiles; /model <name|provider/model> switches"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-tui-cells",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "kiso tui-cells — the components cell renderer (components, diff, width, the render slice). Zero runtime dependencies: input is data, output is bytes.",
5
5
  "type": "module",
6
6
  "license": "MIT",