@vincemakes/kiso-tui-cells 0.16.4 → 0.16.6
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/approval-panel.js +4 -4
- package/dist/components.d.ts +19 -2
- package/dist/components.js +63 -13
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/md.js +1 -1
- package/dist/render.d.ts +52 -5
- package/dist/render.js +59 -8
- package/package.json +1 -1
package/dist/approval-panel.js
CHANGED
|
@@ -273,7 +273,7 @@ export function panelBlockLayout(view, phase, cursor, W, maxRows, note, safer) {
|
|
|
273
273
|
// block, and none of them the composer's. A rule SEPARATES, a gutter
|
|
274
274
|
// SCOPES — the args keep their gutter because they are a verbatim
|
|
275
275
|
// block; everything that was drawing a boundary is one rule now.
|
|
276
|
-
rows.push(`${p.dim}${"\
|
|
276
|
+
rows.push(`${p.dim}${"\u2500".repeat(Math.max(0, W))}${p.reset}`);
|
|
277
277
|
rows.push(` ${cutLine(panelRuleText(view), Math.max(1, W - 2))}`);
|
|
278
278
|
rows.push(` ${cutLine(`${p.bold}${escapeTerminal(view.title)}${p.reset}`, Math.max(1, W - 2))}`);
|
|
279
279
|
// TUI2-R1.5 ⑤ (VD-11): the divider is a LABEL, not a design note. "the
|
|
@@ -388,7 +388,7 @@ export function panelBlockLayout(view, phase, cursor, W, maxRows, note, safer) {
|
|
|
388
388
|
// else in the product, so a CAPPED panel emitted two elbow rows in a
|
|
389
389
|
// row meaning entirely different things. The rule reads as an edge,
|
|
390
390
|
// and the cut notice above it reads as a notice.
|
|
391
|
-
rows.push(`${p.dim}${"\
|
|
391
|
+
rows.push(`${p.dim}${"\u2500".repeat(Math.max(0, W))}${p.reset}`);
|
|
392
392
|
return { rows, ...layout };
|
|
393
393
|
}
|
|
394
394
|
/**
|
|
@@ -493,7 +493,7 @@ export function panelAffordance(view, phase, cursor, safer) {
|
|
|
493
493
|
export function pickBlockRows(view, state, W, maxRows) {
|
|
494
494
|
const p = palette();
|
|
495
495
|
const spec = view.pick;
|
|
496
|
-
const rows = [`${p.dim}${"\
|
|
496
|
+
const rows = [`${p.dim}${"\u2500".repeat(Math.max(0, W))}${p.reset}`]; // R2: the same rule the composer and the other panels use
|
|
497
497
|
const room = Math.max(1, W - 2);
|
|
498
498
|
rows.push(` ${cutLine(`${p.bold}${escapeTerminal(spec.header.split(" \u2014 ")[0] ?? spec.header)}${p.reset}${p.dim}${escapeTerminal(spec.header.slice((spec.header.split(" \u2014 ")[0] ?? "").length))}${p.reset}`, room)}`);
|
|
499
499
|
if (spec.options.length === 0) {
|
|
@@ -546,7 +546,7 @@ export function pickBlockRows(view, state, W, maxRows) {
|
|
|
546
546
|
const tText = cutLine(`${typing ? p.bold : ""}${typing ? "\u2192" : " "} t ${p.reset}${p.dim}${escapeTerminal(spec.typeHint)}${p.reset}`, room);
|
|
547
547
|
rows.push(typing ? selectionBar(tText, visibleWidth(tText), W) : ` ${tText}`);
|
|
548
548
|
rows.push(` ${p.dim}${cutLine(pickAffordance(state), room)}${p.reset}`);
|
|
549
|
-
rows.push(`${p.dim}${"\
|
|
549
|
+
rows.push(`${p.dim}${"\u2500".repeat(Math.max(0, W))}${p.reset}`);
|
|
550
550
|
return rows;
|
|
551
551
|
}
|
|
552
552
|
/** The digits are the keys, so the list the panel offers is bounded by
|
package/dist/components.d.ts
CHANGED
|
@@ -293,6 +293,18 @@ export declare function exploreRows(parts: readonly {
|
|
|
293
293
|
* metadata terms survive (the W14 metadata rule — they give way LAST),
|
|
294
294
|
* the words width-cut at the end with the honest "…" (never a silent
|
|
295
295
|
* truncate — invariant ① holds on the ONE row by construction). */
|
|
296
|
+
/**
|
|
297
|
+
* R3b — what a run of work DID, in words. One definition, because two
|
|
298
|
+
* surfaces say it: the fold line (`turnFold`, above) and the expand
|
|
299
|
+
* header the compositor writes when that fold is opened. A second copy
|
|
300
|
+
* would be a second answer to the same question, and the first thing to
|
|
301
|
+
* drift would be the plurals — `search_text` is "matches", not
|
|
302
|
+
* "searchs", and only the ROLLUP_NOUN table knows that.
|
|
303
|
+
*
|
|
304
|
+
* Zero terms are dropped (owner ruling, R3b): a term earns its place by
|
|
305
|
+
* having a count.
|
|
306
|
+
*/
|
|
307
|
+
export declare function foldTerms(reads: number, edits: number, others: readonly [string, number][]): string[];
|
|
296
308
|
export declare function turnFold(t: {
|
|
297
309
|
words: string;
|
|
298
310
|
thoughtSeconds: number;
|
|
@@ -378,14 +390,19 @@ export declare function selectionBar(styled: string, visible: number, W: number)
|
|
|
378
390
|
/**
|
|
379
391
|
* R2 — the composer's rails, and the ONE edge vocabulary.
|
|
380
392
|
*
|
|
393
|
+
* R3 (owner, 2026-08-27): the rule is a SOLID hairline (`\u2500`), not
|
|
394
|
+
* the dashed `\u254c` R2 shipped, and it is solid EVERYWHERE — the
|
|
395
|
+
* composer, every panel's open and close, the band headers and the
|
|
396
|
+
* markdown rule. One line, one weight, no exceptions to remember.
|
|
397
|
+
*
|
|
381
398
|
* W6 turned two \u254c dotted rows into a rounded box, reasoning that
|
|
382
399
|
* "the box already says input lives here". That is reversed here, and
|
|
383
400
|
* the reason is not taste: a rule is a DELIMITER and a box is a
|
|
384
401
|
* CONTAINER, and the screen was carrying six edge vocabularies at once
|
|
385
402
|
* (this box, the panel's \u2502 gutter and \u2514\u2500\u2500 tail, the
|
|
386
403
|
* diff gutter, the quote's \u258f, the table's rails, the markdown
|
|
387
|
-
* rule).
|
|
388
|
-
*
|
|
404
|
+
* rule). ONE rule replaces the ones that SEPARATE; the \u2502 gutter
|
|
405
|
+
* survives where it SCOPES.
|
|
389
406
|
*
|
|
390
407
|
* Row-neutral by construction: CHROME_ROWS is still 4, so every gate
|
|
391
408
|
* keyed on H \u2212 4 is untouched, and the input row gains the two
|
package/dist/components.js
CHANGED
|
@@ -22,7 +22,7 @@ import { displayWidth, visibleWidth } from "./width.js";
|
|
|
22
22
|
// KEY_BINDINGS). strings.js imports only render/width here, so this edge
|
|
23
23
|
// adds no cycle.
|
|
24
24
|
import { displayVerb } from "./strings.js";
|
|
25
|
-
import { bannerLines, escapeTerminal, foldThinking, foldResult, renderTerminalGap, renderToolSummary, toolTarget, kUnit, palette, } from "./render.js";
|
|
25
|
+
import { bannerLines, breathFrame, escapeTerminal, foldThinking, foldResult, renderTerminalGap, renderToolSummary, toolTarget, kUnit, palette, } from "./render.js";
|
|
26
26
|
// TUI2-MD: the markdown renderer's surface reaches the tui through this
|
|
27
27
|
// module (the tui's components shim re-exports it) — one import edge,
|
|
28
28
|
// and it points one way: md.ts measures with the width authority, never
|
|
@@ -657,7 +657,14 @@ class ToolExecution {
|
|
|
657
657
|
// leaves; the duration then rides the row, always legible.
|
|
658
658
|
const elapsed = c.startedAt !== null ? Math.max(1, Math.round((ctx.now - c.startedAt) / 1000)) : 1;
|
|
659
659
|
const dur = ` · ${elapsed}s`;
|
|
660
|
-
|
|
660
|
+
// R3 (design §5.2): a running command BREATHES — one glyph, seven
|
|
661
|
+
// greys, bottoming out on the ground's dim token (§2.2 applies
|
|
662
|
+
// mid-animation, not just at rest). The quadrant spinner it
|
|
663
|
+
// replaces ROTATED, which §5.3 forbids for a call whose duration
|
|
664
|
+
// cannot be predicted: a turning mark implies progress the
|
|
665
|
+
// product does not have. With no ground the breath freezes to a
|
|
666
|
+
// static `●` and says the same thing more quietly.
|
|
667
|
+
const out = gutterCut(`${breathFrame(ctx.spinnerI)} `, `${verbCol} ${liveTarget(c)}`, Math.max(4, W - dur.length));
|
|
661
668
|
out[0] = `${out[0]}${p.dim}${dur}${p.reset}`;
|
|
662
669
|
out.push(...toolBlockBody(c, W));
|
|
663
670
|
return out;
|
|
@@ -953,23 +960,56 @@ function countTerm(n, singular, plural) {
|
|
|
953
960
|
* metadata terms survive (the W14 metadata rule — they give way LAST),
|
|
954
961
|
* the words width-cut at the end with the honest "…" (never a silent
|
|
955
962
|
* truncate — invariant ① holds on the ONE row by construction). */
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
963
|
+
/**
|
|
964
|
+
* R3b — what a run of work DID, in words. One definition, because two
|
|
965
|
+
* surfaces say it: the fold line (`turnFold`, above) and the expand
|
|
966
|
+
* header the compositor writes when that fold is opened. A second copy
|
|
967
|
+
* would be a second answer to the same question, and the first thing to
|
|
968
|
+
* drift would be the plurals — `search_text` is "matches", not
|
|
969
|
+
* "searchs", and only the ROLLUP_NOUN table knows that.
|
|
970
|
+
*
|
|
971
|
+
* Zero terms are dropped (owner ruling, R3b): a term earns its place by
|
|
972
|
+
* having a count.
|
|
973
|
+
*/
|
|
974
|
+
export function foldTerms(reads, edits, others) {
|
|
975
|
+
const parts = [];
|
|
976
|
+
if (reads > 0)
|
|
977
|
+
parts.push(countTerm(reads, "read", "reads"));
|
|
978
|
+
if (edits > 0)
|
|
979
|
+
parts.push(countTerm(edits, "edit", "edits"));
|
|
980
|
+
for (const [name, n] of others) {
|
|
981
|
+
if (n === 0)
|
|
982
|
+
continue;
|
|
960
983
|
const noun = ROLLUP_NOUN[name];
|
|
961
|
-
if (noun !== undefined)
|
|
984
|
+
if (noun !== undefined)
|
|
962
985
|
parts.push(countTerm(n, noun.endsWith("es") ? noun.slice(0, -2) : noun.slice(0, -1), noun));
|
|
963
|
-
}
|
|
964
986
|
else {
|
|
965
987
|
const verb = displayVerb(name);
|
|
966
988
|
parts.push(countTerm(n, verb, `${verb}s`));
|
|
967
989
|
}
|
|
968
990
|
}
|
|
969
|
-
|
|
991
|
+
return parts;
|
|
992
|
+
}
|
|
993
|
+
export function turnFold(t, W) {
|
|
994
|
+
const p = palette();
|
|
995
|
+
// R3b (owner, 2026-08-27): ZERO TERMS ARE DROPPED. W14 always wrote
|
|
996
|
+
// `no reads · no edits`, which is a sentence about things that did not
|
|
997
|
+
// happen — on a segment fold, where a run is usually all reads or all
|
|
998
|
+
// edits, half the row was the half that said nothing. A term earns its
|
|
999
|
+
// place by having a count.
|
|
1000
|
+
const meta = [`thought ${t.thoughtSeconds}s`, ...foldTerms(t.reads, t.edits, t.others)].join(" · ");
|
|
970
1001
|
const words = escapeTerminal(t.words);
|
|
971
1002
|
if (words === "") {
|
|
972
1003
|
const row = `${p.bold}✦${p.reset} ${meta}`;
|
|
1004
|
+
// R3b: the fold NAMES ITS OWN KEY. Without it the segment's work is
|
|
1005
|
+
// unreachable — no rollup row, no expand, nothing — and hiding a
|
|
1006
|
+
// tool call behind a line with no way back is the one thing this
|
|
1007
|
+
// product must not do. The suffix gives way first at a narrow
|
|
1008
|
+
// width, on the same principle the settled card's does: an
|
|
1009
|
+
// affordance that does not fit is dropped, never half-drawn.
|
|
1010
|
+
const keyed = `${row}${p.dim} · ctrl+r${p.reset}`;
|
|
1011
|
+
if (visibleWidth(keyed) <= W)
|
|
1012
|
+
return [keyed];
|
|
973
1013
|
return visibleWidth(row) <= W ? [row] : [`${p.bold}✦${p.reset} ${widthCut(meta, Math.max(1, W - 3))}…`]; // a wordless turn folds to the W14 shape
|
|
974
1014
|
}
|
|
975
1015
|
// A9 (ruling R2, mock A): the user chip rides the fold — the human's
|
|
@@ -1547,26 +1587,36 @@ export function selectionBar(styled, visible, W) {
|
|
|
1547
1587
|
/**
|
|
1548
1588
|
* R2 — the composer's rails, and the ONE edge vocabulary.
|
|
1549
1589
|
*
|
|
1590
|
+
* R3 (owner, 2026-08-27): the rule is a SOLID hairline (`\u2500`), not
|
|
1591
|
+
* the dashed `\u254c` R2 shipped, and it is solid EVERYWHERE — the
|
|
1592
|
+
* composer, every panel's open and close, the band headers and the
|
|
1593
|
+
* markdown rule. One line, one weight, no exceptions to remember.
|
|
1594
|
+
*
|
|
1550
1595
|
* W6 turned two \u254c dotted rows into a rounded box, reasoning that
|
|
1551
1596
|
* "the box already says input lives here". That is reversed here, and
|
|
1552
1597
|
* the reason is not taste: a rule is a DELIMITER and a box is a
|
|
1553
1598
|
* CONTAINER, and the screen was carrying six edge vocabularies at once
|
|
1554
1599
|
* (this box, the panel's \u2502 gutter and \u2514\u2500\u2500 tail, the
|
|
1555
1600
|
* diff gutter, the quote's \u258f, the table's rails, the markdown
|
|
1556
|
-
* rule).
|
|
1557
|
-
*
|
|
1601
|
+
* rule). ONE rule replaces the ones that SEPARATE; the \u2502 gutter
|
|
1602
|
+
* survives where it SCOPES.
|
|
1558
1603
|
*
|
|
1559
1604
|
* Row-neutral by construction: CHROME_ROWS is still 4, so every gate
|
|
1560
1605
|
* keyed on H \u2212 4 is untouched, and the input row gains the two
|
|
1561
1606
|
* columns the walls were taking.
|
|
1562
1607
|
*/
|
|
1563
1608
|
export function boxTop(W) {
|
|
1564
|
-
|
|
1609
|
+
// R3: the palette's dim, not a hardcoded SGR 2 — `dim` is an absolute
|
|
1610
|
+
// grey once the ground is known, and a rail that hardcodes the
|
|
1611
|
+
// attribute would be the one chrome row not obeying the table.
|
|
1612
|
+
const p = palette();
|
|
1613
|
+
return `${p.dim}${"\u2500".repeat(Math.max(0, W))}${p.reset}`;
|
|
1565
1614
|
}
|
|
1566
1615
|
/** R2 — the same rule below. Named for its POSITION, not its shape, so
|
|
1567
1616
|
* the compositor's two call sites did not have to move. */
|
|
1568
1617
|
export function boxBottom(W) {
|
|
1569
|
-
|
|
1618
|
+
const p = palette();
|
|
1619
|
+
return `${p.dim}${"\u2500".repeat(Math.max(0, W))}${p.reset}`;
|
|
1570
1620
|
}
|
|
1571
1621
|
/** The terminal label + rhythm gap (the pipe path's v2c bytes — the
|
|
1572
1622
|
* exact render the passthrough needs). */
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* package); the cli never imports it directly. Experimental — no
|
|
7
7
|
* API-stability promise yet.
|
|
8
8
|
*/
|
|
9
|
-
export { SPINNER, foldLine, foldWords, visibleWidth, bodySpacing, Container, cellComponent, focusToken, ROLLUP_NOUN, turnFold, CAP_TASK_LIVE, formatDuration, statusLine, boxTop, boxBottom, terminalPipe, type FrameCtx, type RenderLine, type Component, type BodyCell, } from "./components.js";
|
|
9
|
+
export { SPINNER, foldLine, foldWords, visibleWidth, bodySpacing, Container, cellComponent, focusToken, ROLLUP_NOUN, turnFold, foldTerms, CAP_TASK_LIVE, formatDuration, statusLine, boxTop, boxBottom, terminalPipe, type FrameCtx, type RenderLine, type Component, type BodyCell, } from "./components.js";
|
|
10
10
|
export { editFileDiff, truncateDiff, writeFileDiff, type DiffLine, type DiffResult } from "./diff.js";
|
|
11
11
|
export { pendingQueueRows } from "./components.js";
|
|
12
12
|
export { charWidth, displayWidth, leadWidth, widthOf } from "./width.js";
|
|
@@ -14,7 +14,7 @@ export { panelAffordance, panelBlockRows, panelLead, panelLeadPlain, panelLeadWi
|
|
|
14
14
|
export { interactivePrompt, projectTrustRows, projectTrustView, projectUntrustedNote, uncertainView, type TrustArtifact, } from "./strings.js";
|
|
15
15
|
export { extensionsBannerText, helpRows, unansweredAskView, type BannerExtension } from "./strings.js";
|
|
16
16
|
export { displayVerb } from "./strings.js";
|
|
17
|
-
export { bannerLines, COLOR_OFF, COLOR_DARK, COLOR_LIGHT, COLOR_NEUTRAL, COLOR_ON, currentGround, setGround, escapeTerminal, foldResult, foldThinking, kUnit, palette, relativeTime, renderResumeList, renderTerminalGap, renderToolSummary, TAGLINE, toolTarget, truncateRow, type Palette, type ResumeMeta, } from "./render.js";
|
|
17
|
+
export { bannerLines, COLOR_OFF, COLOR_DARK, COLOR_LIGHT, COLOR_NEUTRAL, COLOR_ON, currentGround, setGround, escapeTerminal, foldResult, foldThinking, kUnit, palette, relativeTime, renderResumeList, MOTION_FRAMES, TWINKLE, breathFrame, twinkleFrame, renderTerminalGap, renderToolSummary, TAGLINE, toolTarget, truncateRow, type Palette, type ResumeMeta, } from "./render.js";
|
|
18
18
|
/** DC-3 — the ground: is the terminal light or dark. Pure; see the
|
|
19
19
|
* module comment for why `unknown` is a result and not a failure. */
|
|
20
20
|
export { groundFrom, parseOscColor, relativeLuminance, resolveGround, type Ground, type GroundInputs, type Rgb } from "./ground.js";
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* package); the cli never imports it directly. Experimental — no
|
|
7
7
|
* API-stability promise yet.
|
|
8
8
|
*/
|
|
9
|
-
export { SPINNER, foldLine, foldWords, visibleWidth, bodySpacing, Container, cellComponent, focusToken, ROLLUP_NOUN, turnFold, CAP_TASK_LIVE, formatDuration, statusLine, boxTop, boxBottom, terminalPipe, } from "./components.js";
|
|
9
|
+
export { SPINNER, foldLine, foldWords, visibleWidth, bodySpacing, Container, cellComponent, focusToken, ROLLUP_NOUN, turnFold, foldTerms, CAP_TASK_LIVE, formatDuration, statusLine, boxTop, boxBottom, terminalPipe, } from "./components.js";
|
|
10
10
|
export { editFileDiff, truncateDiff, writeFileDiff } from "./diff.js";
|
|
11
11
|
// W22 (the v8 input round): the pending-queue chips — the SAME
|
|
12
12
|
// UserMessage chip with the □ gutter, pre-rendered above the input
|
|
@@ -28,7 +28,7 @@ export { extensionsBannerText, helpRows, unansweredAskView } from "./strings.js"
|
|
|
28
28
|
// TUI2-R2pre ④: the ONE display-verb table — the screen names the act,
|
|
29
29
|
// the tool table names the call.
|
|
30
30
|
export { displayVerb } from "./strings.js";
|
|
31
|
-
export { bannerLines, COLOR_OFF, COLOR_DARK, COLOR_LIGHT, COLOR_NEUTRAL, COLOR_ON, currentGround, setGround, escapeTerminal, foldResult, foldThinking, kUnit, palette, relativeTime, renderResumeList, renderTerminalGap, renderToolSummary, TAGLINE, toolTarget, truncateRow, } from "./render.js";
|
|
31
|
+
export { bannerLines, COLOR_OFF, COLOR_DARK, COLOR_LIGHT, COLOR_NEUTRAL, COLOR_ON, currentGround, setGround, escapeTerminal, foldResult, foldThinking, kUnit, palette, relativeTime, renderResumeList, MOTION_FRAMES, TWINKLE, breathFrame, twinkleFrame, renderTerminalGap, renderToolSummary, TAGLINE, toolTarget, truncateRow, } from "./render.js";
|
|
32
32
|
/** DC-3 — the ground: is the terminal light or dark. Pure; see the
|
|
33
33
|
* module comment for why `unknown` is a result and not a failure. */
|
|
34
34
|
export { groundFrom, parseOscColor, relativeLuminance, resolveGround } from "./ground.js";
|
package/dist/md.js
CHANGED
|
@@ -258,7 +258,7 @@ function blockBody(b, W) {
|
|
|
258
258
|
// R2: the dashed rule, at the block's own width. The 28 was a
|
|
259
259
|
// guess that read as a short line rather than a divider, and ─
|
|
260
260
|
// belonged to the box vocabulary this round is collapsing.
|
|
261
|
-
return [`${p.dim}${"\
|
|
261
|
+
return [`${p.dim}${"\u2500".repeat(Math.max(1, W))}${p.reset}`];
|
|
262
262
|
case "fence-open":
|
|
263
263
|
// E2: the RAIL, not a gutter. A block drawn with ``` is still a
|
|
264
264
|
// fenced block when a human selects it and pastes it somewhere
|
package/dist/render.d.ts
CHANGED
|
@@ -82,11 +82,22 @@ export interface Palette {
|
|
|
82
82
|
/**
|
|
83
83
|
* DC-3 — one table per ground.
|
|
84
84
|
*
|
|
85
|
-
* `dim` is
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
85
|
+
* R3 (owner, 2026-08-27) — `dim` is ABSOLUTE once the ground is known,
|
|
86
|
+
* and design.md §2's table always said so: light `243` `#767676` at
|
|
87
|
+
* 4.54:1, dark `246` `#949494` at 5.50:1 (both re-measured here).
|
|
88
|
+
*
|
|
89
|
+
* DC-3 shipped SGR 2 instead, on the argument that an attribute adapts
|
|
90
|
+
* to the ground while an absolute grey asserts one. That argument is
|
|
91
|
+
* right about what SGR 2 IS and wrong about what it MEASURES: a
|
|
92
|
+
* terminal renders it as a fraction of its own foreground, and on Apple
|
|
93
|
+
* Terminal's light profile that lands well under the 4.5:1 floor — the
|
|
94
|
+
* labels, the keys row and the status row were all reported unreadable
|
|
95
|
+
* in real use. An attribute that adapts to an unknown ratio is not a
|
|
96
|
+
* contrast guarantee; the table's measured value is.
|
|
97
|
+
*
|
|
98
|
+
* The UNKNOWN ground keeps SGR 2, because §3.1 forbids an absolute
|
|
99
|
+
* foreground in a palette that has not established a background — the
|
|
100
|
+
* attribute is exactly the "correct on any ground" degradation there.
|
|
90
101
|
*/
|
|
91
102
|
export declare const COLOR_NEUTRAL: Palette;
|
|
92
103
|
export declare const COLOR_LIGHT: Palette;
|
|
@@ -141,6 +152,42 @@ export declare function kUnit(value: number | null): string;
|
|
|
141
152
|
* this verbatim; the render tests pin the sequence.
|
|
142
153
|
*/
|
|
143
154
|
export declare function renderTerminalGap(statusLine: string | null): string;
|
|
155
|
+
/**
|
|
156
|
+
* v3 §01 (V6-2) — the banner, block-split. The logo is THREE BRICK rows
|
|
157
|
+
* (the logo.svg pixel form — K I S O), then a BLANK, then the info rows:
|
|
158
|
+
* "kiso vX — tagline" + extensions. The tagline rides the version line
|
|
159
|
+
* (the old logo MIDDLE row was the tagline — a text row masquerading as
|
|
160
|
+
* the logo's centre). Every row truncates at the terminal width with a
|
|
161
|
+
* " (+N)" marker (N = the hidden display width); a window narrower than
|
|
162
|
+
* 40 columns skips the logo + the blank entirely — only the info rows.
|
|
163
|
+
* Pure.
|
|
164
|
+
*/
|
|
165
|
+
/**
|
|
166
|
+
* design.md §5.2 — THE TWO CYCLES, built. Seven frames each, walked at
|
|
167
|
+
* the existing 200ms spinner cadence, so a waiting screen's byte volume
|
|
168
|
+
* and frame rate are exactly what they were.
|
|
169
|
+
*
|
|
170
|
+
* §5.3 is why neither rotates: "a breath says alive; a turn says
|
|
171
|
+
* counting". A call whose duration cannot be predicted must not wear a
|
|
172
|
+
* mark that implies progress it does not have.
|
|
173
|
+
*/
|
|
174
|
+
/** The THINKING twinkle — glyphs only, no colour at all, so it survives
|
|
175
|
+
* NO_COLOR and any ground intact. §4.1: it settles onto `✦`, which is
|
|
176
|
+
* the same mark the collapsed segment keeps, so nothing new appears at
|
|
177
|
+
* the transition. Every glyph is in Menlo and absent from Apple Color
|
|
178
|
+
* Emoji (§6.1's test, run). */
|
|
179
|
+
export declare const TWINKLE: readonly ["✧", "✦", "✶", "✸", "✺", "✸", "✦"];
|
|
180
|
+
/** The breath's frame: `●` at the step's grey, for the CURRENT ground.
|
|
181
|
+
* With no ground — or under NO_COLOR — it freezes to a static `●`,
|
|
182
|
+
* because a brightness ramp needs a background to be a ramp against and
|
|
183
|
+
* §3.1 forbids guessing one. The glyph never changes, so the freeze
|
|
184
|
+
* degrades the motion and never the meaning. */
|
|
185
|
+
export declare function breathFrame(step: number): string;
|
|
186
|
+
/** The twinkle's frame — pure glyph, no palette involved. */
|
|
187
|
+
export declare function twinkleFrame(step: number): string;
|
|
188
|
+
/** Both cycles are seven frames, so ONE counter walks them and the two
|
|
189
|
+
* marks stay in step on a screen showing both. */
|
|
190
|
+
export declare const MOTION_FRAMES = 7;
|
|
144
191
|
export declare const TAGLINE = "the coding agent that survives kill -9";
|
|
145
192
|
/** R2 — what the opening knows about the session. Optional because the
|
|
146
193
|
* off-TTY caller prints a banner before a model is bound. */
|
package/dist/render.js
CHANGED
|
@@ -28,19 +28,30 @@ const BASE = { bold: "\x1b[1m", dim: "\x1b[2m", red: "\x1b[31m", green: "\x1b[32
|
|
|
28
28
|
* 4's principle exactly: when the ground is unknown, use the thing that
|
|
29
29
|
* is correct on any ground rather than guessing one.
|
|
30
30
|
*/
|
|
31
|
-
const withWash = (wash, washEnd, red = BASE.red) => ({ ...BASE, red, wash, washEnd, code: wash });
|
|
31
|
+
const withWash = (wash, washEnd, red = BASE.red, dim = BASE.dim) => ({ ...BASE, red, dim, wash, washEnd, code: wash });
|
|
32
32
|
/**
|
|
33
33
|
* DC-3 — one table per ground.
|
|
34
34
|
*
|
|
35
|
-
* `dim` is
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
35
|
+
* R3 (owner, 2026-08-27) — `dim` is ABSOLUTE once the ground is known,
|
|
36
|
+
* and design.md §2's table always said so: light `243` `#767676` at
|
|
37
|
+
* 4.54:1, dark `246` `#949494` at 5.50:1 (both re-measured here).
|
|
38
|
+
*
|
|
39
|
+
* DC-3 shipped SGR 2 instead, on the argument that an attribute adapts
|
|
40
|
+
* to the ground while an absolute grey asserts one. That argument is
|
|
41
|
+
* right about what SGR 2 IS and wrong about what it MEASURES: a
|
|
42
|
+
* terminal renders it as a fraction of its own foreground, and on Apple
|
|
43
|
+
* Terminal's light profile that lands well under the 4.5:1 floor — the
|
|
44
|
+
* labels, the keys row and the status row were all reported unreadable
|
|
45
|
+
* in real use. An attribute that adapts to an unknown ratio is not a
|
|
46
|
+
* contrast guarantee; the table's measured value is.
|
|
47
|
+
*
|
|
48
|
+
* The UNKNOWN ground keeps SGR 2, because §3.1 forbids an absolute
|
|
49
|
+
* foreground in a palette that has not established a background — the
|
|
50
|
+
* attribute is exactly the "correct on any ground" degradation there.
|
|
40
51
|
*/
|
|
41
52
|
export const COLOR_NEUTRAL = withWash("\x1b[7m", "\x1b[27m");
|
|
42
|
-
export const COLOR_LIGHT = withWash("\x1b[48;5;255m", "\x1b[49m", "\x1b[38;5;124m");
|
|
43
|
-
export const COLOR_DARK = withWash("\x1b[48;5;236m", "\x1b[49m", "\x1b[38;5;173m");
|
|
53
|
+
export const COLOR_LIGHT = withWash("\x1b[48;5;255m", "\x1b[49m", "\x1b[38;5;124m", "\x1b[38;5;243m");
|
|
54
|
+
export const COLOR_DARK = withWash("\x1b[48;5;236m", "\x1b[49m", "\x1b[38;5;173m", "\x1b[38;5;246m");
|
|
44
55
|
/** The historical name — the palette for a colour TTY whose ground has
|
|
45
56
|
* not been established. Unchanged in every byte except `code`, which
|
|
46
57
|
* was the defect. */
|
|
@@ -211,6 +222,46 @@ export function renderTerminalGap(statusLine) {
|
|
|
211
222
|
* 40 columns skips the logo + the blank entirely — only the info rows.
|
|
212
223
|
* Pure.
|
|
213
224
|
*/
|
|
225
|
+
/**
|
|
226
|
+
* design.md §5.2 — THE TWO CYCLES, built. Seven frames each, walked at
|
|
227
|
+
* the existing 200ms spinner cadence, so a waiting screen's byte volume
|
|
228
|
+
* and frame rate are exactly what they were.
|
|
229
|
+
*
|
|
230
|
+
* §5.3 is why neither rotates: "a breath says alive; a turn says
|
|
231
|
+
* counting". A call whose duration cannot be predicted must not wear a
|
|
232
|
+
* mark that implies progress it does not have.
|
|
233
|
+
*/
|
|
234
|
+
/** The THINKING twinkle — glyphs only, no colour at all, so it survives
|
|
235
|
+
* NO_COLOR and any ground intact. §4.1: it settles onto `✦`, which is
|
|
236
|
+
* the same mark the collapsed segment keeps, so nothing new appears at
|
|
237
|
+
* the transition. Every glyph is in Menlo and absent from Apple Color
|
|
238
|
+
* Emoji (§6.1's test, run). */
|
|
239
|
+
export const TWINKLE = ["\u2727", "\u2726", "\u2736", "\u2738", "\u273a", "\u2738", "\u2726"];
|
|
240
|
+
/** The COMMAND breath — brightness only, one glyph. The ramps bottom out
|
|
241
|
+
* EXACTLY on the ground's dim token (§2.2: "the floor is a floor,
|
|
242
|
+
* including mid-animation"): light ends at 243 (4.54:1 on white), dark
|
|
243
|
+
* at 246 (5.50:1 on #1e1e1e). Measured, not assumed. */
|
|
244
|
+
const BREATH_LIGHT = [232, 236, 240, 243, 240, 236, 232];
|
|
245
|
+
const BREATH_DARK = [255, 251, 248, 246, 248, 251, 255];
|
|
246
|
+
/** The breath's frame: `●` at the step's grey, for the CURRENT ground.
|
|
247
|
+
* With no ground — or under NO_COLOR — it freezes to a static `●`,
|
|
248
|
+
* because a brightness ramp needs a background to be a ramp against and
|
|
249
|
+
* §3.1 forbids guessing one. The glyph never changes, so the freeze
|
|
250
|
+
* degrades the motion and never the meaning. */
|
|
251
|
+
export function breathFrame(step) {
|
|
252
|
+
const p = palette();
|
|
253
|
+
const ramp = currentGround() === "light" ? BREATH_LIGHT : currentGround() === "dark" ? BREATH_DARK : null;
|
|
254
|
+
if (ramp === null || p.bold === "")
|
|
255
|
+
return "\u25cf";
|
|
256
|
+
return `\x1b[38;5;${ramp[step % ramp.length]}m\u25cf${p.reset}`;
|
|
257
|
+
}
|
|
258
|
+
/** The twinkle's frame — pure glyph, no palette involved. */
|
|
259
|
+
export function twinkleFrame(step) {
|
|
260
|
+
return TWINKLE[step % TWINKLE.length];
|
|
261
|
+
}
|
|
262
|
+
/** Both cycles are seven frames, so ONE counter walks them and the two
|
|
263
|
+
* marks stay in step on a screen showing both. */
|
|
264
|
+
export const MOTION_FRAMES = 7;
|
|
214
265
|
/** DC-18: the display-width prefix of PLAIN text. `widthCut` lives in
|
|
215
266
|
* components.ts, which imports this module — the dependency runs one
|
|
216
267
|
* way, so the four lines live here rather than inverting it. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-tui-cells",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.6",
|
|
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",
|