@vincemakes/kiso-tui-cells 0.16.4 → 0.16.5
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 +7 -2
- package/dist/components.js +23 -6
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- 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
|
@@ -378,14 +378,19 @@ export declare function selectionBar(styled: string, visible: number, W: number)
|
|
|
378
378
|
/**
|
|
379
379
|
* R2 — the composer's rails, and the ONE edge vocabulary.
|
|
380
380
|
*
|
|
381
|
+
* R3 (owner, 2026-08-27): the rule is a SOLID hairline (`\u2500`), not
|
|
382
|
+
* the dashed `\u254c` R2 shipped, and it is solid EVERYWHERE — the
|
|
383
|
+
* composer, every panel's open and close, the band headers and the
|
|
384
|
+
* markdown rule. One line, one weight, no exceptions to remember.
|
|
385
|
+
*
|
|
381
386
|
* W6 turned two \u254c dotted rows into a rounded box, reasoning that
|
|
382
387
|
* "the box already says input lives here". That is reversed here, and
|
|
383
388
|
* the reason is not taste: a rule is a DELIMITER and a box is a
|
|
384
389
|
* CONTAINER, and the screen was carrying six edge vocabularies at once
|
|
385
390
|
* (this box, the panel's \u2502 gutter and \u2514\u2500\u2500 tail, the
|
|
386
391
|
* diff gutter, the quote's \u258f, the table's rails, the markdown
|
|
387
|
-
* rule).
|
|
388
|
-
*
|
|
392
|
+
* rule). ONE rule replaces the ones that SEPARATE; the \u2502 gutter
|
|
393
|
+
* survives where it SCOPES.
|
|
389
394
|
*
|
|
390
395
|
* Row-neutral by construction: CHROME_ROWS is still 4, so every gate
|
|
391
396
|
* 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;
|
|
@@ -1547,26 +1554,36 @@ export function selectionBar(styled, visible, W) {
|
|
|
1547
1554
|
/**
|
|
1548
1555
|
* R2 — the composer's rails, and the ONE edge vocabulary.
|
|
1549
1556
|
*
|
|
1557
|
+
* R3 (owner, 2026-08-27): the rule is a SOLID hairline (`\u2500`), not
|
|
1558
|
+
* the dashed `\u254c` R2 shipped, and it is solid EVERYWHERE — the
|
|
1559
|
+
* composer, every panel's open and close, the band headers and the
|
|
1560
|
+
* markdown rule. One line, one weight, no exceptions to remember.
|
|
1561
|
+
*
|
|
1550
1562
|
* W6 turned two \u254c dotted rows into a rounded box, reasoning that
|
|
1551
1563
|
* "the box already says input lives here". That is reversed here, and
|
|
1552
1564
|
* the reason is not taste: a rule is a DELIMITER and a box is a
|
|
1553
1565
|
* CONTAINER, and the screen was carrying six edge vocabularies at once
|
|
1554
1566
|
* (this box, the panel's \u2502 gutter and \u2514\u2500\u2500 tail, the
|
|
1555
1567
|
* diff gutter, the quote's \u258f, the table's rails, the markdown
|
|
1556
|
-
* rule).
|
|
1557
|
-
*
|
|
1568
|
+
* rule). ONE rule replaces the ones that SEPARATE; the \u2502 gutter
|
|
1569
|
+
* survives where it SCOPES.
|
|
1558
1570
|
*
|
|
1559
1571
|
* Row-neutral by construction: CHROME_ROWS is still 4, so every gate
|
|
1560
1572
|
* keyed on H \u2212 4 is untouched, and the input row gains the two
|
|
1561
1573
|
* columns the walls were taking.
|
|
1562
1574
|
*/
|
|
1563
1575
|
export function boxTop(W) {
|
|
1564
|
-
|
|
1576
|
+
// R3: the palette's dim, not a hardcoded SGR 2 — `dim` is an absolute
|
|
1577
|
+
// grey once the ground is known, and a rail that hardcodes the
|
|
1578
|
+
// attribute would be the one chrome row not obeying the table.
|
|
1579
|
+
const p = palette();
|
|
1580
|
+
return `${p.dim}${"\u2500".repeat(Math.max(0, W))}${p.reset}`;
|
|
1565
1581
|
}
|
|
1566
1582
|
/** R2 — the same rule below. Named for its POSITION, not its shape, so
|
|
1567
1583
|
* the compositor's two call sites did not have to move. */
|
|
1568
1584
|
export function boxBottom(W) {
|
|
1569
|
-
|
|
1585
|
+
const p = palette();
|
|
1586
|
+
return `${p.dim}${"\u2500".repeat(Math.max(0, W))}${p.reset}`;
|
|
1570
1587
|
}
|
|
1571
1588
|
/** The terminal label + rhythm gap (the pipe path's v2c bytes — the
|
|
1572
1589
|
* exact render the passthrough needs). */
|
package/dist/index.d.ts
CHANGED
|
@@ -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
|
@@ -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.5",
|
|
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",
|