@vincemakes/kiso-tui-cells 0.14.0 → 0.15.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.
- package/dist/components.d.ts +0 -4
- package/dist/components.js +1 -18
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/render.d.ts +9 -12
- package/dist/render.js +27 -43
- package/dist/strings.js +2 -0
- package/package.json +1 -1
package/dist/components.d.ts
CHANGED
|
@@ -147,10 +147,6 @@ export type BodyCell = {
|
|
|
147
147
|
decidedBy?: string;
|
|
148
148
|
reason?: string;
|
|
149
149
|
} | null;
|
|
150
|
-
} | {
|
|
151
|
-
kind: "text";
|
|
152
|
-
text: string;
|
|
153
|
-
done: boolean;
|
|
154
150
|
}
|
|
155
151
|
/** TUI2-MD ⑤ — ONE markdown block of assistant body text. The cell is
|
|
156
152
|
* the commit unit the compositor already had, so block-freeze needs
|
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,
|
|
25
|
+
import { bannerLines, 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
|
|
@@ -152,8 +152,6 @@ export function cellComponent(cell) {
|
|
|
152
152
|
return new ThinkingFold(cell);
|
|
153
153
|
case "tool":
|
|
154
154
|
return new ToolExecution(cell);
|
|
155
|
-
case "text":
|
|
156
|
-
return new AssistantMessage(cell);
|
|
157
155
|
case "md":
|
|
158
156
|
return new MarkdownBlock(cell);
|
|
159
157
|
case "notice":
|
|
@@ -1171,21 +1169,6 @@ function toolCutNote(name, resultText) {
|
|
|
1171
1169
|
return `capped by ${escapeTerminal(displayVerb(name))} · /last for the rest`;
|
|
1172
1170
|
return null;
|
|
1173
1171
|
}
|
|
1174
|
-
/** The assistant body text — wrapped at W, the inline-code tint per
|
|
1175
|
-
* row (the #16e rule: a span never matches across rows). */
|
|
1176
|
-
class AssistantMessage {
|
|
1177
|
-
cell;
|
|
1178
|
-
constructor(cell) {
|
|
1179
|
-
this.cell = cell;
|
|
1180
|
-
}
|
|
1181
|
-
render(W, _ctx) {
|
|
1182
|
-
// TUI2-R1.5 9 (VD-10): the model's prose is the clearest case of
|
|
1183
|
-
// text a human reads — it wraps at word boundaries.
|
|
1184
|
-
const text = escapeTerminal(this.cell.text);
|
|
1185
|
-
const wrapped = foldWords(text, W);
|
|
1186
|
-
return wrapped.length > 0 ? wrapped.map((l) => colorInlineCode(l)) : [""];
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
1172
|
/** TUI2-MD ⑤ — one markdown block. Pure in (block, W): the same source
|
|
1190
1173
|
* and the same width give the same bytes, which is the freeze property
|
|
1191
1174
|
* the commit path relies on. The block carries its own leading blank
|
package/dist/index.d.ts
CHANGED
|
@@ -14,4 +14,4 @@ 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_ON,
|
|
17
|
+
export { bannerLines, COLOR_OFF, COLOR_ON, escapeTerminal, foldResult, foldThinking, kUnit, palette, relativeTime, renderResumeList, renderTerminalGap, renderToolSummary, TAGLINE, toolTarget, truncateRow, type Palette, type ResumeMeta, } from "./render.js";
|
package/dist/index.js
CHANGED
|
@@ -28,4 +28,4 @@ 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_ON,
|
|
31
|
+
export { bannerLines, COLOR_OFF, COLOR_ON, escapeTerminal, foldResult, foldThinking, kUnit, palette, relativeTime, renderResumeList, renderTerminalGap, renderToolSummary, TAGLINE, toolTarget, truncateRow, } from "./render.js";
|
package/dist/render.d.ts
CHANGED
|
@@ -67,15 +67,6 @@ export declare function palette(): Palette;
|
|
|
67
67
|
* EVERY externally-sourced string must pass through this before any output.
|
|
68
68
|
*/
|
|
69
69
|
export declare function escapeTerminal(text: string): string;
|
|
70
|
-
/**
|
|
71
|
-
* TUI v5 #16e: the inline-code tint — backtick spans in ONE line of
|
|
72
|
-
* assistant body text get the `code` color. Deliberately NOT a markdown
|
|
73
|
-
* engine: single level only (`[^`]*` cannot nest), a span never matches
|
|
74
|
-
* across lines (the caller passes one line; an opener without a closer
|
|
75
|
-
* on the same line stays plain). NO_COLOR / pipes → the codes are empty
|
|
76
|
-
* strings → the line passes through byte-identical.
|
|
77
|
-
*/
|
|
78
|
-
export declare function colorInlineCode(line: string): string;
|
|
79
70
|
/**
|
|
80
71
|
* v2b — one thinking BLOCK folds to ONE dim line: the first 100 chars, a
|
|
81
72
|
* " (… /think shows full)" marker when the block is longer. The consumer
|
|
@@ -131,9 +122,8 @@ export declare const TAGLINE = "the coding agent that survives kill -9";
|
|
|
131
122
|
export declare function truncateRow(row: string, width: number): string;
|
|
132
123
|
/** v3 §01 (V6-2) + W1: the banner lines for a width W and height H —
|
|
133
124
|
* the tier table (extends the existing "under 40 columns, skip the
|
|
134
|
-
* logo" rule with a HEIGHT input):
|
|
135
|
-
* W ≥ 40 and H ≥
|
|
136
|
-
* W ≥ 40 and 14–19 rows → COMPACT (v6's LOGO_ROWS, byte-identical)
|
|
125
|
+
* logo" rule with a HEIGHT input; VD-14 merged the two art tiers):
|
|
126
|
+
* W ≥ 40 and H ≥ 14 → the 2-row wordmark, 2-column indent
|
|
137
127
|
* anything smaller → text rows only
|
|
138
128
|
* then the blank, then "vX — tagline" — the art IS the wordmark, so the
|
|
139
129
|
* text row does not repeat the name — then extensions — then the W5
|
|
@@ -153,6 +143,13 @@ export interface ResumeMeta {
|
|
|
153
143
|
readonly events: number;
|
|
154
144
|
readonly runs: number;
|
|
155
145
|
readonly updatedAt: number;
|
|
146
|
+
/** TT-1B (W5 unification) — the ONE-CELL badge glyph from the
|
|
147
|
+
* picker's BADGE_GLYPH vocabulary, derived by the caller from the
|
|
148
|
+
* SAME projection the picker uses (session-cards — one source of
|
|
149
|
+
* truth about durability). Plain: the banner's uniform dim wrap
|
|
150
|
+
* styles the row; the picker keeps color on its own surface.
|
|
151
|
+
* Absent on every meta → the pre-TT-1B bytes, verbatim. */
|
|
152
|
+
readonly badge?: string;
|
|
156
153
|
}
|
|
157
154
|
export declare function relativeTime(updatedAt: number, now: number): string;
|
|
158
155
|
export declare function renderResumeList(metas: readonly ResumeMeta[], W: number, now: number): string[];
|
package/dist/render.js
CHANGED
|
@@ -9,7 +9,14 @@ import { charWidth, displayWidth } from "./width.js";
|
|
|
9
9
|
export const COLOR_ON = { bold: "\x1b[1m", dim: "\x1b[2m", red: "\x1b[31m", green: "\x1b[32m", warn: "\x1b[33m", code: "\x1b[38;5;252m", italic: "\x1b[3m", italicEnd: "\x1b[23m", rv: "\x1b[7m", rvEnd: "\x1b[27m", reset: "\x1b[0m" };
|
|
10
10
|
export const COLOR_OFF = { bold: "", dim: "", red: "", green: "", warn: "", code: "", italic: "", italicEnd: "", rv: "", rvEnd: "", reset: "" };
|
|
11
11
|
export function palette() {
|
|
12
|
-
|
|
12
|
+
// PH-1a (finding PH-F5): the no-color.org contract is "present AND
|
|
13
|
+
// non-empty" — the old `=== undefined` check let an EMPTY `NO_COLOR=`
|
|
14
|
+
// (a common shell-profile/CI shape) kill the colors, and through the
|
|
15
|
+
// dock's activation gate (`palette().bold !== ""`) the entire docked
|
|
16
|
+
// UI with them. The v4-round plan recorded this as debugging pitfall ①;
|
|
17
|
+
// it was a bug.
|
|
18
|
+
const noColor = process.env.NO_COLOR;
|
|
19
|
+
return (noColor === undefined || noColor === "") && process.stdout.isTTY ? COLOR_ON : COLOR_OFF;
|
|
13
20
|
}
|
|
14
21
|
/**
|
|
15
22
|
* E group/round 8: strip terminal-injection vectors from MODEL/TOOL text before it
|
|
@@ -25,20 +32,6 @@ export function escapeTerminal(text) {
|
|
|
25
32
|
.replace(/[\u0080-\u009f]/g, "") // C1
|
|
26
33
|
.replace(/[\u202a-\u202e\u2066-\u2069]/g, ""); // bidi
|
|
27
34
|
}
|
|
28
|
-
/**
|
|
29
|
-
* TUI v5 #16e: the inline-code tint — backtick spans in ONE line of
|
|
30
|
-
* assistant body text get the `code` color. Deliberately NOT a markdown
|
|
31
|
-
* engine: single level only (`[^`]*` cannot nest), a span never matches
|
|
32
|
-
* across lines (the caller passes one line; an opener without a closer
|
|
33
|
-
* on the same line stays plain). NO_COLOR / pipes → the codes are empty
|
|
34
|
-
* strings → the line passes through byte-identical.
|
|
35
|
-
*/
|
|
36
|
-
export function colorInlineCode(line) {
|
|
37
|
-
const p = palette();
|
|
38
|
-
if (p.code === "" || p.reset === "")
|
|
39
|
-
return line;
|
|
40
|
-
return line.replace(/`([^`]*)`/g, `${p.code}\`$1\`${p.reset}`);
|
|
41
|
-
}
|
|
42
35
|
/**
|
|
43
36
|
* v2b — one thinking BLOCK folds to ONE dim line: the first 100 chars, a
|
|
44
37
|
* " (… /think shows full)" marker when the block is longer. The consumer
|
|
@@ -169,20 +162,13 @@ export function renderTerminalGap(statusLine) {
|
|
|
169
162
|
* Pure.
|
|
170
163
|
*/
|
|
171
164
|
export const TAGLINE = "the coding agent that survives kill -9";
|
|
172
|
-
/**
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
* the
|
|
178
|
-
const
|
|
179
|
-
"██ ██ ██████ ████████ ████████",
|
|
180
|
-
"██ ██ ██ ██ ██ ██",
|
|
181
|
-
"████ ██ ████████ ██ ██",
|
|
182
|
-
"████ ██ ██ ██ ██",
|
|
183
|
-
"██ ██ ██ ██ ██ ██",
|
|
184
|
-
"██ ██ ██████ ████████ ████████",
|
|
185
|
-
];
|
|
165
|
+
/** TT-1B (VD-14) — the ONE wordmark, 2 rows. The 36x6 pixel art and the
|
|
166
|
+
* 3-row compact logo both retire: a tall pixel banner's mid-scroll cut
|
|
167
|
+
* state renders as glyph garbage (VD-14 — frames 03/04/s2-05 of the
|
|
168
|
+
* 2026-08-17 walkthrough), inherent to the height. This is the compact
|
|
169
|
+
* font with its base row folded into lower half-blocks — same alphabet,
|
|
170
|
+
* 15 columns, and the cut window shrinks to nothing a reader catches. */
|
|
171
|
+
const WORDMARK_ROWS = ["█ █ ▀█▀ █▀▀ █▀█", "█▀▄ ▄█▄ ▄▄█ █▄█"];
|
|
186
172
|
/** v3 §01 (W1): truncate a row at `width`, marking the hidden span
|
|
187
173
|
* " (+N)". W1: the width math is the charWidth authority (the banner's
|
|
188
174
|
* brick glyphs are 1 cell — the art's 38 columns clear 40), and the
|
|
@@ -216,9 +202,8 @@ export function truncateRow(row, width) {
|
|
|
216
202
|
}
|
|
217
203
|
/** v3 §01 (V6-2) + W1: the banner lines for a width W and height H —
|
|
218
204
|
* the tier table (extends the existing "under 40 columns, skip the
|
|
219
|
-
* logo" rule with a HEIGHT input):
|
|
220
|
-
* W ≥ 40 and H ≥
|
|
221
|
-
* W ≥ 40 and 14–19 rows → COMPACT (v6's LOGO_ROWS, byte-identical)
|
|
205
|
+
* logo" rule with a HEIGHT input; VD-14 merged the two art tiers):
|
|
206
|
+
* W ≥ 40 and H ≥ 14 → the 2-row wordmark, 2-column indent
|
|
222
207
|
* anything smaller → text rows only
|
|
223
208
|
* then the blank, then "vX — tagline" — the art IS the wordmark, so the
|
|
224
209
|
* text row does not repeat the name — then extensions — then the W5
|
|
@@ -226,15 +211,9 @@ export function truncateRow(row, width) {
|
|
|
226
211
|
* with a " (+N)" marker. Pure. */
|
|
227
212
|
export function bannerLines(W, H, version, extensionsText, resume = [], now = Date.now()) {
|
|
228
213
|
const rows = [];
|
|
229
|
-
if (W >= 40) {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
rows.push(truncateRow(` ${r}`, W));
|
|
233
|
-
}
|
|
234
|
-
else if (H >= 14) {
|
|
235
|
-
for (const r of LOGO_ROWS)
|
|
236
|
-
rows.push(truncateRow(r, W));
|
|
237
|
-
}
|
|
214
|
+
if (W >= 40 && H >= 14) {
|
|
215
|
+
for (const r of WORDMARK_ROWS)
|
|
216
|
+
rows.push(truncateRow(` ${r}`, W));
|
|
238
217
|
}
|
|
239
218
|
if (rows.length > 0)
|
|
240
219
|
rows.push("");
|
|
@@ -284,12 +263,17 @@ export function renderResumeList(metas, W, now) {
|
|
|
284
263
|
const whens = metas.map((m) => relativeTime(m.updatedAt, now));
|
|
285
264
|
const metaTexts = metas.map((m) => `${m.events} events · ${m.runs} runs`);
|
|
286
265
|
const metaW = Math.max(...metaTexts.map((t) => t.length));
|
|
287
|
-
|
|
266
|
+
// TT-1B (W5): the glyph column exists only when a badge is present —
|
|
267
|
+
// a badge-less list keeps its exact pre-TT-1B bytes; in a mixed list
|
|
268
|
+
// every row reserves the column so the when/title columns never shift.
|
|
269
|
+
const badged = metas.some((m) => m.badge !== undefined);
|
|
270
|
+
const titleW = Math.max(1, W - (badged ? 15 : 13) - metaW);
|
|
288
271
|
for (let i = 0; i < metas.length; i += 1) {
|
|
289
272
|
const title = escapeTerminal(metas[i].title);
|
|
290
273
|
const shown = titleCut(title, titleW);
|
|
291
274
|
const pad = titleW - displayWidth(shown);
|
|
292
|
-
|
|
275
|
+
const glyph = badged ? `${metas[i].badge ?? " "} ` : "";
|
|
276
|
+
rows.push(` ${glyph}${whens[i].padEnd(7)} ${shown}${" ".repeat(pad)} ${metaTexts[i].padStart(metaW)}`);
|
|
293
277
|
}
|
|
294
278
|
return rows;
|
|
295
279
|
}
|
package/dist/strings.js
CHANGED
|
@@ -338,6 +338,8 @@ export function helpRows() {
|
|
|
338
338
|
cmd("/mode", "show the approval tier; /mode <name> switches (manual/default/accept-edits/plan/bypass)"),
|
|
339
339
|
cmd("/model", "list model profiles; /model <name|provider/model> switches"),
|
|
340
340
|
cmd("/compact", "summarize the older conversation to free context"),
|
|
341
|
+
cmd("/clear", "start a fresh conversation (the old session stays resumable)"),
|
|
342
|
+
cmd("/resume", "switch to another session; /resume <id> goes directly"),
|
|
341
343
|
// TUI2-R1 (D): DELIBERATELY UNCHANGED. Deriving this sentence from
|
|
342
344
|
// KEY_BINDINGS would be an improvement and it would also move an
|
|
343
345
|
// assertion outside the round's two declared supersession classes,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-tui-cells",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.1",
|
|
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",
|