@vincemakes/kiso-tui-cells 0.47.0 → 0.47.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.
@@ -726,8 +726,17 @@ class ToolExecution {
726
726
  // and answered); a policy's is ambient. The outcome word says
727
727
  // which, once.
728
728
  const word = c.verdict !== null && c.verdict.decision === "denied" && c.verdict.decidedBy === undefined ? "denied by you" : "denied";
729
- const outcome = [`${word} \u00b7 ${escapeTerminal(c.reason)}`, word];
730
- const body = toolBlockParts(c, W, ctx);
729
+ // 0.47.1 (finding 0470-F4, owner 2026-10-07): said once. The
730
+ // runtime's reason when the person gave none is the words
731
+ // "denied by user" — `denied by you · denied by user` said the
732
+ // refusal twice on one row, and the body (`[Permission denied]
733
+ // denied by user`, what the model was handed) a third time. So
734
+ // that refusal is `denied by you` alone, with no body. A refusal
735
+ // WITH a reason keeps both: the head gives its reason way on a
736
+ // narrow row (the pinned order), and the body still says it.
737
+ const quiet = word === "denied by you" && c.reason === DEFAULT_REFUSAL;
738
+ const outcome = quiet ? [word] : [`${word} \u00b7 ${escapeTerminal(c.reason)}`, word];
739
+ const body = quiet && c.resultText.trim() === `[Permission denied] ${c.reason}` ? { rows: [] } : toolBlockParts(c, W, ctx);
731
740
  return card("fail", " ", verb, escapeTerminal(toolTargetOf(c)), outcome, body.rows, null, W, true, fold);
732
741
  }
733
742
  // 4c: a card settled from the durable log carries no clock at
@@ -1324,9 +1333,12 @@ function toolBlockParts(c, W, ctx) {
1324
1333
  // A shell's conclusion is at the bottom, so its note goes ABOVE the
1325
1334
  // tail; everything else answers at the top, so its note closes it.
1326
1335
  // The card round: a settled, collapsed card's key rides the note.
1327
- return dir === "tail"
1328
- ? [...cutNote(all.length - CAP_PREVIEW, "earlier", W, tone, keyed), ...tailWindow(all, CAP_PREVIEW, starts)]
1329
- : [...all.slice(0, CAP_PREVIEW), ...cutNote(all.length - CAP_PREVIEW, "more", W, tone, keyed)];
1336
+ if (dir === "tail") {
1337
+ const w = tailWindow(all, CAP_PREVIEW, starts);
1338
+ return [...cutNote(w.lines, "earlier", W, tone, keyed, w.partial), ...w.rows];
1339
+ }
1340
+ const h = headCut(all, CAP_PREVIEW, starts);
1341
+ return [...h.rows, ...cutNote(h.lines, "more", W, tone, keyed, h.partial)];
1330
1342
  };
1331
1343
  const shellTail = (out, dir) => capped(out.rows, dir, out.starts);
1332
1344
  // a diff inside a card: its own rows, the head of them until the key
@@ -1466,17 +1478,44 @@ function blockLines(text, W, tone = "dim") {
1466
1478
  * REVERSAL of §7.4's foot for a collapsed card — the note and the key
1467
1479
  * were two rows for one fact. The key gives way after the count's word,
1468
1480
  * the count never. */
1469
- function cutNote(cut, word, W, tone, key = false) {
1470
- const n = `${cut} ${word} line${cut === 1 ? "" : "s"}`;
1481
+ function cutNote(cut, word, W, tone, key = false, partial = false) {
1482
+ // 0.47.1 (finding 0470-F1, owner 2026-10-07): `cut` counts output
1483
+ // LINES, the unit the note names (it counted rows: a wrapped line hidden
1484
+ // whole was two or more "lines"). `partial`: the line at the cut is
1485
+ // shown only in part — its start above a tail, its rest below a head —
1486
+ // and the note says so rather than counting it.
1487
+ const part = word === "earlier" ? "start" : "rest";
1488
+ const lines = `${cut} ${word} line${cut === 1 ? "" : "s"}`;
1489
+ const n = !partial ? lines : cut === 0 ? `the ${part} of this line` : `${lines} and the ${part} of this one`;
1490
+ const short = !partial ? `\u2026 ${cut}` : cut === 0 ? "\u2026" : `\u2026 ${cut}+`;
1491
+ // the part's clause gives way before the count's word does
1492
+ const tiers = partial && cut > 0 ? [`\u2026 ${n}`, `\u2026 ${lines}`, short] : [`\u2026 ${n}`, short];
1471
1493
  const room = bodyTextWidth(W);
1472
1494
  if (key) {
1473
- for (const [left, k] of [[`\u2026 ${n}`, EXPAND_ROW], [`\u2026 ${cut}`, EXPAND_ROW], [`\u2026 ${cut}`, "ctrl+o"]]) {
1495
+ for (const [left, k] of [...tiers.map((l) => [l, EXPAND_ROW]), [short, "ctrl+o"]]) {
1474
1496
  const gap = room - visibleWidth(left) - visibleWidth(k);
1475
1497
  if (gap >= 2)
1476
1498
  return noteRow(`${left}${" ".repeat(gap)}${k}`, W, tone);
1477
1499
  }
1478
1500
  }
1479
- return noteRow(pickTier([`\u2026 ${n}`, `\u2026 ${cut}`], room), W, tone);
1501
+ return noteRow(pickTier(tiers, room), W, tone);
1502
+ }
1503
+ /** What a cut window hides, for its note (0.47.1, finding 0470-F1): the
1504
+ * output lines among the hidden rows, and whether the line at the cut is
1505
+ * shown in part. `starts` marks each row that begins a line; where it is
1506
+ * unknown (empty) every row counts as a line, as before. A tail that opens
1507
+ * on a continuation row shows the end of a line whose start is hidden —
1508
+ * that line is not counted, it is the note's `partial`. */
1509
+ function tailCount(shown, hidden, starts, first) {
1510
+ const partial = starts[first] === false;
1511
+ return { rows: shown, lines: hidden.filter((i) => starts[i] !== false).length - (partial ? 1 : 0), partial };
1512
+ }
1513
+ function headCut(rows, cap, starts) {
1514
+ let lines = 0;
1515
+ for (let i = cap; i < rows.length; i += 1)
1516
+ if (starts[i] !== false)
1517
+ lines += 1;
1518
+ return { rows: rows.slice(0, cap), lines, partial: starts[cap] === false };
1480
1519
  }
1481
1520
  /** The card round — a shell's tail never opens on a blank row or in the
1482
1521
  * middle of a wrapped line, and keeps its height (DC-46: a live window
@@ -1489,10 +1528,13 @@ function cutNote(cut, word, W, tone, key = false) {
1489
1528
  * the plain tail. The rows it hides are counted in the note. `starts`
1490
1529
  * marks each row that begins a line of output. */
1491
1530
  const TAIL_REACH = 12;
1531
+ /** The runtime's reason for a refusal the person gave no words for
1532
+ * (session.approve(id, false)) — a card says `denied by you` alone then. */
1533
+ const DEFAULT_REFUSAL = "denied by user";
1492
1534
  function tailWindow(rows, cap, starts) {
1493
1535
  const n = rows.length;
1494
1536
  if (n <= cap)
1495
- return [...rows];
1537
+ return { rows: [...rows], lines: 0, partial: false };
1496
1538
  const blank = (i) => visibleWidth(rows[i]) <= visibleWidth(bodyRow());
1497
1539
  let blanks = 0;
1498
1540
  for (let i = n - cap; i < n; i += 1)
@@ -1507,16 +1549,19 @@ function tailWindow(rows, cap, starts) {
1507
1549
  if (blank(s) || starts[s] === false || blanks < extra)
1508
1550
  continue;
1509
1551
  const out = [];
1552
+ const hidden = Array.from({ length: s }, (_, i) => i);
1510
1553
  let dropped = 0;
1511
1554
  for (let i = s; i < n; i += 1) {
1512
- if (dropped < extra && blank(i))
1555
+ if (dropped < extra && blank(i)) {
1513
1556
  dropped += 1;
1557
+ hidden.push(i);
1558
+ }
1514
1559
  else
1515
1560
  out.push(rows[i]);
1516
1561
  }
1517
- return out;
1562
+ return tailCount(out, hidden, starts, s);
1518
1563
  }
1519
- return rows.slice(n - cap);
1564
+ return tailCount(rows.slice(n - cap), Array.from({ length: n - cap }, (_, i) => i), starts, n - cap);
1520
1565
  }
1521
1566
  /** The error text, uncapped: the answer is at the start, and the whole
1522
1567
  * of it is the body — the head row carries only the outcome word
@@ -1571,9 +1616,12 @@ function liveWindow(c, W, cap, tone) {
1571
1616
  const rows = all.slice(from);
1572
1617
  if (rows.length <= cap)
1573
1618
  return rows;
1574
- return c.name === "shell"
1575
- ? [...cutNote(rows.length - cap, "earlier", W, tone), ...tailWindow(rows, cap, starts.slice(from))]
1576
- : [...rows.slice(0, cap), ...cutNote(rows.length - cap, "more", W, tone)];
1619
+ if (c.name === "shell") {
1620
+ const w = tailWindow(rows, cap, starts.slice(from));
1621
+ return [...cutNote(w.lines, "earlier", W, tone, false, w.partial), ...w.rows];
1622
+ }
1623
+ const h = headCut(rows, cap, starts.slice(from));
1624
+ return [...h.rows, ...cutNote(h.lines, "more", W, tone, false, h.partial)];
1577
1625
  }
1578
1626
  /** DC-46 — the window's HIGH-WATER, per cell: the room a frame leaves
1579
1627
  * caps how far a window may GROW, and never shrinks one that already
@@ -39,25 +39,25 @@ export declare const GRAPHITE: {
39
39
  readonly dark: {
40
40
  readonly ground: "#0b0b0b";
41
41
  readonly ink: "#ededed";
42
- readonly ink2: "#b5b5b5";
43
- readonly dim: "#8e8e8e";
42
+ readonly ink2: "#c2c2c2";
43
+ readonly dim: "#a4a4a4";
44
44
  readonly rail: "#6b6b6b";
45
- readonly line: "#212121";
46
- readonly washRun: "#141b28";
47
- readonly washDone: "#1b1b1b";
48
- readonly washFail: "#2a1716";
49
- readonly washAsk: "#211d13";
50
- readonly human: "#efe6cf";
45
+ readonly line: "#656565";
46
+ readonly washRun: "#2b3854";
47
+ readonly washDone: "#333333";
48
+ readonly washFail: "#4d2b28";
49
+ readonly washAsk: "#483818";
50
+ readonly human: "#e8dfc6";
51
51
  readonly humanInk: "#141620";
52
52
  readonly gold: "#e3b04b";
53
53
  readonly goldMark: "#e3b04b";
54
54
  readonly blue: "#82a8f5";
55
- readonly code: "#1a2438";
55
+ readonly code: "#283658";
56
56
  readonly ok: "#8fd19e";
57
57
  readonly fail: "#f2877a";
58
- readonly add: "#16301f";
59
- readonly del: "#3a1b1a";
60
- readonly track: "#2a2a2a";
58
+ readonly add: "#2a5034";
59
+ readonly del: "#5e302d";
60
+ readonly track: "#474747";
61
61
  };
62
62
  };
63
63
  export type Token = Exclude<keyof (typeof GRAPHITE)["light"], "ground">;
@@ -84,11 +84,21 @@ export declare function contrast(a: Rgb, b: Rgb): number;
84
84
  /** `t` of the way from `a` to `b`, per channel. */
85
85
  export declare function mix(a: Rgb, b: Rgb, t: number): Rgb;
86
86
  /**
87
- * §3.4 — one surface for any ground. Per channel an affine map
88
- * `s = a + k·g`, fitted so that the white reference ground gives the
89
- * table's white value and the black one its black value: the two
90
- * grounds reproduce the table exactly, and a terminal whose black is
91
- * `#1e1e1e` gets a card that sits the same distance off ITS ground.
87
+ * §3.4 — one surface for any ground. A DARK ground: per channel an affine
88
+ * map `s = a + k·g`, fitted so that the white reference ground gives the
89
+ * table's white value and the black one its black value — a terminal
90
+ * whose black is `#1e1e1e` gets a card that sits the same distance off
91
+ * ITS ground, and the distance narrows as a dark ground lightens, which
92
+ * keeps the text above the floor on the lighter dark themes. A LIGHT
93
+ * ground: the light table, moved by how far the ground sits from white.
94
+ * The two reference grounds reproduce the table exactly.
95
+ *
96
+ * 0.47.1 (the dark table's re-adaptation): DECLARED REVERSAL for the
97
+ * light grounds only. The one map through both tables tied them to the
98
+ * dark table — lifting the dark cards pulled a card on `#eeeeee` to within
99
+ * 1.09:1 of its ground. A light ground now answers to the light table
100
+ * alone; on the light grounds measured the cards move by a channel step
101
+ * or two at most.
92
102
  */
93
103
  export declare function deriveSurface(token: Token, ground: Rgb): Rgb;
94
104
  /** A colour as the terminal will show it: itself in the 24-bit tier, the
@@ -118,6 +128,16 @@ export declare function nearest256(c: Rgb): number;
118
128
  * standard statement; anything else gets the 256 tier, which every
119
129
  * terminal kiso docks in renders. */
120
130
  export declare function colourTier(colorterm: string | undefined): Tier;
131
+ /** 0.47.1 (owner, 2026-10-07) — 24-bit where the terminal is KNOWN to
132
+ * render it, though it does not say so in COLORTERM. Windows Terminal is
133
+ * the case that found it: drawn in the 256 tier, the person's cream block
134
+ * became pink (index 224) and its gold edge olive (186). Each terminal is
135
+ * recognised by what it sets; the list is the reference implementation's.
136
+ * Inside tmux or screen only COLORTERM counts — the outer terminal's
137
+ * variables are inherited, its rendering is not. Apple Terminal says it
138
+ * in COLORTERM where it renders 24-bit (§2), and keeps the 256 tier
139
+ * otherwise. PURE: the environment and the platform are passed in. */
140
+ export declare function terminalTier(env: Readonly<Record<string, string | undefined>>, platform: string): Tier;
121
141
  export declare function fg(c: Rgb, tier: Tier): string;
122
142
  export declare function bg(c: Rgb, tier: Tier): string;
123
143
  export {};
package/dist/graphite.js CHANGED
@@ -32,28 +32,37 @@ export const GRAPHITE = {
32
32
  del: "#fadfdc",
33
33
  track: "#e2e2e2",
34
34
  },
35
+ // 0.47.1 — the dark table re-adapted (owner, 2026-10-07: preset D on
36
+ // the dark palette page): the surfaces were a few levels above the
37
+ // ground and read as heavy on a black terminal; they rise a step, and
38
+ // dim and ink2 rise with them to keep the floor. washAsk and code sit
39
+ // just under D's values (#524222, #30406a) so every pair of
40
+ // TEXT_PAIRS still clears 4.5, in the 256 tier too.
35
41
  dark: {
36
42
  ground: "#0b0b0b",
37
43
  ink: "#ededed",
38
- ink2: "#b5b5b5",
39
- dim: "#8e8e8e",
44
+ ink2: "#c2c2c2",
45
+ dim: "#a4a4a4",
40
46
  rail: "#6b6b6b",
41
- line: "#212121",
42
- washRun: "#141b28",
43
- washDone: "#1b1b1b",
44
- washFail: "#2a1716",
45
- washAsk: "#211d13",
46
- human: "#efe6cf",
47
+ // the owner, 2026-10-08: the composer's rules read too grey on Apple
48
+ // Terminal's Pro profile (its transparency lightens the black) — the
49
+ // hairlines rise to #656565
50
+ line: "#656565",
51
+ washRun: "#2b3854",
52
+ washDone: "#333333",
53
+ washFail: "#4d2b28",
54
+ washAsk: "#483818",
55
+ human: "#e8dfc6",
47
56
  humanInk: "#141620",
48
57
  gold: "#e3b04b",
49
58
  goldMark: "#e3b04b",
50
59
  blue: "#82a8f5",
51
- code: "#1a2438",
60
+ code: "#283658",
52
61
  ok: "#8fd19e",
53
62
  fail: "#f2877a",
54
- add: "#16301f",
55
- del: "#3a1b1a",
56
- track: "#2a2a2a",
63
+ add: "#2a5034",
64
+ del: "#5e302d",
65
+ track: "#474747",
57
66
  },
58
67
  };
59
68
  /** The tokens that are GROUNDS near the terminal's own (§3.4): each is
@@ -94,13 +103,28 @@ export function mix(a, b, t) {
94
103
  const REF_LIGHT = hexRgb(GRAPHITE.light.ground);
95
104
  const REF_DARK = hexRgb(GRAPHITE.dark.ground);
96
105
  /**
97
- * §3.4 — one surface for any ground. Per channel an affine map
98
- * `s = a + k·g`, fitted so that the white reference ground gives the
99
- * table's white value and the black one its black value: the two
100
- * grounds reproduce the table exactly, and a terminal whose black is
101
- * `#1e1e1e` gets a card that sits the same distance off ITS ground.
106
+ * §3.4 — one surface for any ground. A DARK ground: per channel an affine
107
+ * map `s = a + k·g`, fitted so that the white reference ground gives the
108
+ * table's white value and the black one its black value — a terminal
109
+ * whose black is `#1e1e1e` gets a card that sits the same distance off
110
+ * ITS ground, and the distance narrows as a dark ground lightens, which
111
+ * keeps the text above the floor on the lighter dark themes. A LIGHT
112
+ * ground: the light table, moved by how far the ground sits from white.
113
+ * The two reference grounds reproduce the table exactly.
114
+ *
115
+ * 0.47.1 (the dark table's re-adaptation): DECLARED REVERSAL for the
116
+ * light grounds only. The one map through both tables tied them to the
117
+ * dark table — lifting the dark cards pulled a card on `#eeeeee` to within
118
+ * 1.09:1 of its ground. A light ground now answers to the light table
119
+ * alone; on the light grounds measured the cards move by a channel step
120
+ * or two at most.
102
121
  */
103
122
  export function deriveSurface(token, ground) {
123
+ if (groundFrom(ground) === "light") {
124
+ const s = hexRgb(GRAPHITE.light[token]);
125
+ const shift = (sc, rc, g) => Math.max(0, Math.min(255, Math.round(sc + (g - rc))));
126
+ return { r: shift(s.r, REF_LIGHT.r, ground.r), g: shift(s.g, REF_LIGHT.g, ground.g), b: shift(s.b, REF_LIGHT.b, ground.b) };
127
+ }
104
128
  const w = hexRgb(GRAPHITE.light[token]);
105
129
  const d = hexRgb(GRAPHITE.dark[token]);
106
130
  const chan = (wc, dc, lw, ld, g) => {
@@ -249,6 +273,36 @@ export function colourTier(colorterm) {
249
273
  const v = colorterm?.trim().toLowerCase();
250
274
  return v === "truecolor" || v === "24bit" ? "24bit" : "256";
251
275
  }
276
+ /** 0.47.1 (owner, 2026-10-07) — 24-bit where the terminal is KNOWN to
277
+ * render it, though it does not say so in COLORTERM. Windows Terminal is
278
+ * the case that found it: drawn in the 256 tier, the person's cream block
279
+ * became pink (index 224) and its gold edge olive (186). Each terminal is
280
+ * recognised by what it sets; the list is the reference implementation's.
281
+ * Inside tmux or screen only COLORTERM counts — the outer terminal's
282
+ * variables are inherited, its rendering is not. Apple Terminal says it
283
+ * in COLORTERM where it renders 24-bit (§2), and keeps the 256 tier
284
+ * otherwise. PURE: the environment and the platform are passed in. */
285
+ export function terminalTier(env, platform) {
286
+ if (colourTier(env.COLORTERM) === "24bit")
287
+ return "24bit";
288
+ const term = (env.TERM ?? "").toLowerCase();
289
+ if (env.TMUX !== undefined || term.startsWith("tmux") || term.startsWith("screen"))
290
+ return "256";
291
+ const program = (env.TERM_PROGRAM ?? "").toLowerCase();
292
+ const known = env.WT_SESSION !== undefined ||
293
+ env.ITERM_SESSION_ID !== undefined ||
294
+ env.WEZTERM_PANE !== undefined ||
295
+ env.KITTY_WINDOW_ID !== undefined ||
296
+ env.GHOSTTY_RESOURCES_DIR !== undefined ||
297
+ ["iterm.app", "wezterm", "ghostty", "kitty", "vscode", "warpterminal", "alacritty"].includes(program) ||
298
+ term.includes("ghostty") ||
299
+ term === "alacritty" ||
300
+ (env.TERMINAL_EMULATOR ?? "").toLowerCase() === "jetbrains-jediterm" ||
301
+ // a Windows console renders 24-bit even where Windows Terminal hosts
302
+ // it without WT_SESSION (cmd.exe from Win+R)
303
+ platform === "win32";
304
+ return known ? "24bit" : "256";
305
+ }
252
306
  export function fg(c, tier) {
253
307
  return tier === "24bit" ? `\x1b[38;2;${c.r};${c.g};${c.b}m` : `\x1b[38;5;${nearest256(c)}m`;
254
308
  }
package/dist/index.d.ts CHANGED
@@ -20,4 +20,4 @@ export { bannerLines, COLOR_OFF, COLOR_DARK, COLOR_LIGHT, COLOR_NEUTRAL, COLOR_O
20
20
  export { groundFrom, parseOscColor, relativeLuminance, resolveGround, type Ground, type GroundInputs, type Rgb } from "./ground.js";
21
21
  /** Graphite (design.md §2, §3.4) — the tokens, the tier and the derived
22
22
  * surfaces, as colours. Pure; `render.ts` writes them as SGR. */
23
- export { GRAPHITE, breathRamp, colourTier, contrast, deriveSurface, graphiteColours, hexRgb, nearest256, rgbHex, weakestPair, type Colours, type Kind, type Tier, type Token } from "./graphite.js";
23
+ export { GRAPHITE, breathRamp, colourTier, terminalTier, contrast, deriveSurface, graphiteColours, hexRgb, nearest256, rgbHex, weakestPair, type Colours, type Kind, type Tier, type Token } from "./graphite.js";
package/dist/index.js CHANGED
@@ -34,4 +34,4 @@ export { bannerLines, COLOR_OFF, COLOR_DARK, COLOR_LIGHT, COLOR_NEUTRAL, COLOR_O
34
34
  export { groundFrom, parseOscColor, relativeLuminance, resolveGround } from "./ground.js";
35
35
  /** Graphite (design.md §2, §3.4) — the tokens, the tier and the derived
36
36
  * surfaces, as colours. Pure; `render.ts` writes them as SGR. */
37
- export { GRAPHITE, breathRamp, colourTier, contrast, deriveSurface, graphiteColours, hexRgb, nearest256, rgbHex, weakestPair } from "./graphite.js";
37
+ export { GRAPHITE, breathRamp, colourTier, terminalTier, contrast, deriveSurface, graphiteColours, hexRgb, nearest256, rgbHex, weakestPair } from "./graphite.js";
package/dist/render.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * dependencies (the tui-cells package has none).
7
7
  */
8
8
  import { charWidth, displayWidth, visibleWidth, widthCut } from "./width.js";
9
- import { bg, breathRamp, colourTier, fg, graphiteColours, mix } from "./graphite.js";
9
+ import { bg, breathRamp, fg, graphiteColours, mix, terminalTier } from "./graphite.js";
10
10
  const BASE = { bold: "\x1b[1m", dim: "\x1b[2m", red: "\x1b[31m", green: "\x1b[32m", warn: "\x1b[33m", italic: "\x1b[3m", italicEnd: "\x1b[23m", underline: "\x1b[4m", underlineEnd: "\x1b[24m", rv: "\x1b[7m", rvEnd: "\x1b[27m", reset: "\x1b[0m" };
11
11
  /**
12
12
  * DC-9 (design §2.3) — the failure colour is theme-resolved.
@@ -217,7 +217,7 @@ export function palette() {
217
217
  return COLOR_OFF;
218
218
  if (ground === "unknown")
219
219
  return COLOR_NEUTRAL;
220
- const tier = colourTier(process.env.COLORTERM);
220
+ const tier = terminalTier(process.env, process.platform);
221
221
  const r = groundRgb === null ? -1 : groundRgb.r;
222
222
  const g = groundRgb === null ? -1 : groundRgb.g;
223
223
  const b = groundRgb === null ? -1 : groundRgb.b;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-tui-cells",
3
- "version": "0.47.0",
3
+ "version": "0.47.1",
4
4
  "description": "kiso tui-cells \u2014 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",