@vincemakes/kiso-tui-cells 0.17.0 → 0.19.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.
- package/dist/components.d.ts +79 -0
- package/dist/components.js +119 -4
- package/dist/strings.js +17 -2
- package/package.json +1 -1
package/dist/components.d.ts
CHANGED
|
@@ -355,11 +355,41 @@ 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";
|
|
365
395
|
}, W: number): string[];
|
|
@@ -370,6 +400,55 @@ export declare function turnFold(t: {
|
|
|
370
400
|
edits: number;
|
|
371
401
|
others: [string, number][];
|
|
372
402
|
}, W: number): string[];
|
|
403
|
+
/**
|
|
404
|
+
* R4 — the standing act slot.
|
|
405
|
+
*
|
|
406
|
+
* The stretch's ONE line sits above it; this is the region under it,
|
|
407
|
+
* and it STANDS: allocated when the stretch opens, released at the
|
|
408
|
+
* fold.
|
|
409
|
+
*
|
|
410
|
+
* R3i built the same window INTERMITTENTLY — a running call got its
|
|
411
|
+
* fixed 1+3 block (W8), a finished one got nothing — so the live
|
|
412
|
+
* region's height was a function of how many calls happened to be in
|
|
413
|
+
* flight this frame. Over one real stretch that is 2 rows, then 7,
|
|
414
|
+
* then 2, then 17 for a three-call batch, then 2 again, and every
|
|
415
|
+
* transition scrolls everything above it. The owner's report was that
|
|
416
|
+
* the screen "keeps jumping", and it was an accurate description of
|
|
417
|
+
* the design, not a defect in its execution.
|
|
418
|
+
*
|
|
419
|
+
* The cure is not a smaller window, it is a STANDING one: between two
|
|
420
|
+
* calls the slot keeps the call that just finished rather than
|
|
421
|
+
* collapsing, and before any call it keeps the thinking that is
|
|
422
|
+
* producing them — which is R3i ruling 5 ("thinking belongs on the
|
|
423
|
+
* stretch line, IN THE ACT WINDOW, and in full in expansions") finally
|
|
424
|
+
* wired, since R3i stated it while building no window for the thinking
|
|
425
|
+
* phase to live in.
|
|
426
|
+
*
|
|
427
|
+
* Four rows, deliberately the same 1+3 shape W8 gave a running call, so
|
|
428
|
+
* the commonest frame — exactly one call in flight — renders byte-for-
|
|
429
|
+
* byte what 0.17.0 shipped.
|
|
430
|
+
*/
|
|
431
|
+
export declare const ACT_SLOT_ROWS = 4;
|
|
432
|
+
/**
|
|
433
|
+
* R4 — the slot's body rows: the tail of `text`, newest at the BOTTOM,
|
|
434
|
+
* bottom-padded to exactly `rows`.
|
|
435
|
+
*
|
|
436
|
+
* The same dim │ gutter a running call's window uses (W2's table), and
|
|
437
|
+
* the same two VD-4 rules: leading blank gutters are skipped, and the
|
|
438
|
+
* short-output pad goes at the BOTTOM so output starts under its own
|
|
439
|
+
* header and grows downward. The slot's CONTENTS change; its shape
|
|
440
|
+
* does not.
|
|
441
|
+
*/
|
|
442
|
+
export declare function slotTail(text: string, W: number, rows: number): string[];
|
|
443
|
+
/** R4 — clamp or pad assembled slot rows to EXACTLY `rows`. The padding
|
|
444
|
+
* is what makes the slot stand; the clamp is what keeps the slot from
|
|
445
|
+
* ever being the thing that trips the force-commit cap (a slot that
|
|
446
|
+
* could overflow would commit real cells to relieve blank rows). */
|
|
447
|
+
export declare function slotPad(content: readonly string[], rows: number): string[];
|
|
448
|
+
/** R4 — the slot's overflow row: the calls in flight beyond the head
|
|
449
|
+
* budget. It lives INSIDE the slot (it is one of the four rows), which
|
|
450
|
+
* is what keeps a parallel burst from growing the region. */
|
|
451
|
+
export declare function moreRunningRow(n: number, W: number): string;
|
|
373
452
|
/** The approval mini-diff (W7): capped at 12 folded rows — the head +
|
|
374
453
|
* the named middle (the renderer cut — what was cut, how to expand) +
|
|
375
454
|
* the tail. The rows are folded at the current width BEFORE the cap —
|
package/dist/components.js
CHANGED
|
@@ -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
|
-
|
|
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[
|
|
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[
|
|
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,45 @@ 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
|
-
|
|
1252
|
+
// R4a (owner ruling, 2026-08-30) — the fold row prints NO key.
|
|
1253
|
+
//
|
|
1254
|
+
// R4 printed `· ctrl+r 3` so the row could name its own target. The
|
|
1255
|
+
// owner's objection is the right one: a number you cannot type is not
|
|
1256
|
+
// a selector, it is decoration that costs a column — and the
|
|
1257
|
+
// reference implementation, checked rather than assumed, prints
|
|
1258
|
+
// nothing on the row either. Its expansion lives in a MODE you enter,
|
|
1259
|
+
// where the pointer can reach every fold including the ones that have
|
|
1260
|
+
// scrolled away; the row itself stays clean.
|
|
1261
|
+
//
|
|
1262
|
+
// So the affordance is retired here and owed to that mode. Until it
|
|
1263
|
+
// exists, `ctrl+r` still opens the most recent fold — it is simply no
|
|
1264
|
+
// longer advertised on a row that cannot say which one it means.
|
|
1265
|
+
const key = "";
|
|
1222
1266
|
const lead = t.phase === "thinking" ? [`thinking ${t.thoughtSeconds}s`] : t.phase === "settled" && t.thoughtSeconds > 0 ? [`thought ${t.thoughtSeconds}s`] : [];
|
|
1223
1267
|
const clauseText = troubleClause(t);
|
|
1224
1268
|
let meta = [...lead, ...(t.phase === "thinking" ? [] : stretchTerms(t, live))].join(" · ");
|
|
@@ -1552,6 +1596,77 @@ function shellLiveTail(text, W) {
|
|
|
1552
1596
|
kept.push(`${p.dim}${BODY_ROW}${p.reset}`);
|
|
1553
1597
|
return [...kept, cutLine(`${p.dim}${CUT_ROW}live tail · esc stop · alt+⏎ redirect${p.reset}`, W)];
|
|
1554
1598
|
}
|
|
1599
|
+
/**
|
|
1600
|
+
* R4 — the standing act slot.
|
|
1601
|
+
*
|
|
1602
|
+
* The stretch's ONE line sits above it; this is the region under it,
|
|
1603
|
+
* and it STANDS: allocated when the stretch opens, released at the
|
|
1604
|
+
* fold.
|
|
1605
|
+
*
|
|
1606
|
+
* R3i built the same window INTERMITTENTLY — a running call got its
|
|
1607
|
+
* fixed 1+3 block (W8), a finished one got nothing — so the live
|
|
1608
|
+
* region's height was a function of how many calls happened to be in
|
|
1609
|
+
* flight this frame. Over one real stretch that is 2 rows, then 7,
|
|
1610
|
+
* then 2, then 17 for a three-call batch, then 2 again, and every
|
|
1611
|
+
* transition scrolls everything above it. The owner's report was that
|
|
1612
|
+
* the screen "keeps jumping", and it was an accurate description of
|
|
1613
|
+
* the design, not a defect in its execution.
|
|
1614
|
+
*
|
|
1615
|
+
* The cure is not a smaller window, it is a STANDING one: between two
|
|
1616
|
+
* calls the slot keeps the call that just finished rather than
|
|
1617
|
+
* collapsing, and before any call it keeps the thinking that is
|
|
1618
|
+
* producing them — which is R3i ruling 5 ("thinking belongs on the
|
|
1619
|
+
* stretch line, IN THE ACT WINDOW, and in full in expansions") finally
|
|
1620
|
+
* wired, since R3i stated it while building no window for the thinking
|
|
1621
|
+
* phase to live in.
|
|
1622
|
+
*
|
|
1623
|
+
* Four rows, deliberately the same 1+3 shape W8 gave a running call, so
|
|
1624
|
+
* the commonest frame — exactly one call in flight — renders byte-for-
|
|
1625
|
+
* byte what 0.17.0 shipped.
|
|
1626
|
+
*/
|
|
1627
|
+
export const ACT_SLOT_ROWS = 4;
|
|
1628
|
+
/**
|
|
1629
|
+
* R4 — the slot's body rows: the tail of `text`, newest at the BOTTOM,
|
|
1630
|
+
* bottom-padded to exactly `rows`.
|
|
1631
|
+
*
|
|
1632
|
+
* The same dim │ gutter a running call's window uses (W2's table), and
|
|
1633
|
+
* the same two VD-4 rules: leading blank gutters are skipped, and the
|
|
1634
|
+
* short-output pad goes at the BOTTOM so output starts under its own
|
|
1635
|
+
* header and grows downward. The slot's CONTENTS change; its shape
|
|
1636
|
+
* does not.
|
|
1637
|
+
*/
|
|
1638
|
+
export function slotTail(text, W, rows) {
|
|
1639
|
+
if (rows <= 0)
|
|
1640
|
+
return [];
|
|
1641
|
+
const p = palette();
|
|
1642
|
+
const all = blockRows(text, W);
|
|
1643
|
+
const from = all.findIndex((r) => visibleWidth(r) > visibleWidth(BODY_ROW));
|
|
1644
|
+
const body = from < 0 ? [] : all.slice(from);
|
|
1645
|
+
const kept = body.slice(Math.max(0, body.length - rows));
|
|
1646
|
+
while (kept.length < rows)
|
|
1647
|
+
kept.push(`${p.dim}${BODY_ROW}${p.reset}`);
|
|
1648
|
+
return kept;
|
|
1649
|
+
}
|
|
1650
|
+
/** R4 — clamp or pad assembled slot rows to EXACTLY `rows`. The padding
|
|
1651
|
+
* is what makes the slot stand; the clamp is what keeps the slot from
|
|
1652
|
+
* ever being the thing that trips the force-commit cap (a slot that
|
|
1653
|
+
* could overflow would commit real cells to relieve blank rows). */
|
|
1654
|
+
export function slotPad(content, rows) {
|
|
1655
|
+
if (rows <= 0)
|
|
1656
|
+
return [];
|
|
1657
|
+
const p = palette();
|
|
1658
|
+
const out = content.slice(0, rows);
|
|
1659
|
+
while (out.length < rows)
|
|
1660
|
+
out.push(`${p.dim}${BODY_ROW}${p.reset}`);
|
|
1661
|
+
return out;
|
|
1662
|
+
}
|
|
1663
|
+
/** R4 — the slot's overflow row: the calls in flight beyond the head
|
|
1664
|
+
* budget. It lives INSIDE the slot (it is one of the four rows), which
|
|
1665
|
+
* is what keeps a parallel burst from growing the region. */
|
|
1666
|
+
export function moreRunningRow(n, W) {
|
|
1667
|
+
const p = palette();
|
|
1668
|
+
return cutLine(` ${p.dim}${CUT_ROW}+${n} more running${p.reset}`, W);
|
|
1669
|
+
}
|
|
1555
1670
|
/** W12: the delegate's child sessions collapse to the tool row plus ONE
|
|
1556
1671
|
* line — the height NEVER changes (running → settled replaces the row
|
|
1557
1672
|
* in place). The running row derives from the INPUT: the parent has no
|
package/dist/strings.js
CHANGED
|
@@ -189,6 +189,11 @@ export const KEY_BINDINGS = [
|
|
|
189
189
|
{ keys: "/", what: "commands" },
|
|
190
190
|
{ keys: "↑↓", what: "history / queue pop" },
|
|
191
191
|
{ keys: "ctrl+r", what: "expand cells" },
|
|
192
|
+
// R5 — the transcript viewer. It has to be HERE or it does not exist:
|
|
193
|
+
// R4a retired the printed key from the fold row on the ground that a
|
|
194
|
+
// row cannot say which fold a key opens, and the sheet is where the
|
|
195
|
+
// discoverability moved. A surface nobody can find is not a feature.
|
|
196
|
+
{ keys: "ctrl+o", what: "transcript" },
|
|
192
197
|
{ keys: "tab", what: "complete (menu / @)" },
|
|
193
198
|
{ keys: "?", what: "this sheet" },
|
|
194
199
|
{ keys: "ctrl+z / ctrl+y", what: "undo / redo" },
|
|
@@ -254,15 +259,22 @@ const SHEET_GRID = [
|
|
|
254
259
|
[0, 1, 2],
|
|
255
260
|
[3, 4, 5],
|
|
256
261
|
[6, 7],
|
|
262
|
+
// R5: ctrl+o joins at index 8, so the tail shifts by one. The grid is
|
|
263
|
+
// a SECOND table that must agree with KEY_BINDINGS and nothing makes
|
|
264
|
+
// it — adding a binding without touching this silently drops the LAST
|
|
265
|
+
// one off the sheet, which is exactly what happened on the first
|
|
266
|
+
// attempt and exactly what the "ONE SOURCE" gate exists to catch.
|
|
257
267
|
[8, 9],
|
|
258
|
-
|
|
268
|
+
// UD-1's undo row shares this one now — the table has an even count
|
|
269
|
+
// again, so no binding needs a row to itself.
|
|
270
|
+
[10, 11],
|
|
259
271
|
];
|
|
260
272
|
const SHEET_STOPS = [
|
|
261
273
|
[16, 43],
|
|
262
274
|
[16, 43],
|
|
263
275
|
[36],
|
|
264
276
|
[36],
|
|
265
|
-
[],
|
|
277
|
+
[36],
|
|
266
278
|
];
|
|
267
279
|
/**
|
|
268
280
|
* TUI2-R1 (D) — the sheet, one screen, static.
|
|
@@ -381,6 +393,9 @@ export function helpRows() {
|
|
|
381
393
|
["/help", "print this list of commands"],
|
|
382
394
|
["/think", "show the last full thinking block"],
|
|
383
395
|
["/last", "show the most recent tool call's input and output"],
|
|
396
|
+
// R4 (C4d): a committed row is the terminal's, and cannot be
|
|
397
|
+
// re-wrapped in place (ADR-0046) — this appends it re-folded.
|
|
398
|
+
["/rewrap", "re-print the recent prose at the current width"],
|
|
384
399
|
["/status", "show session id, event count, and context estimate"],
|
|
385
400
|
["/mode", "show the approval tier; /mode <name> switches (manual/default/accept-edits/plan/bypass)"],
|
|
386
401
|
["/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.
|
|
3
|
+
"version": "0.19.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",
|