@vincemakes/kiso-tui-cells 0.31.1 → 0.32.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 +1 -0
- package/dist/components.js +12 -1
- package/dist/render.d.ts +31 -0
- package/dist/render.js +47 -2
- package/dist/strings.js +15 -0
- package/package.json +1 -1
package/dist/components.d.ts
CHANGED
package/dist/components.js
CHANGED
|
@@ -30,7 +30,7 @@ import { displayWidth, visibleWidth, widthCut } from "./width.js";
|
|
|
30
30
|
// KEY_BINDINGS). strings.js imports only render/width here, so this edge
|
|
31
31
|
// adds no cycle.
|
|
32
32
|
import { displayVerb } from "./strings.js";
|
|
33
|
-
import { bannerLines, breathFrame, cutLine, escapeTerminal, foldThinking, foldResult, renderTerminalGap, renderToolSummary, toolTarget, kUnit, palette, currentGround, } from "./render.js";
|
|
33
|
+
import { bannerLines, breathFrame, cutLine, escapeTerminal, foldThinking, foldThinkingRow, foldResult, renderTerminalGap, renderToolSummary, toolTarget, kUnit, palette, currentGround, } from "./render.js";
|
|
34
34
|
// TUI2-MD: the markdown renderer's surface reaches the tui through this
|
|
35
35
|
// module (the tui's components shim re-exports it) — one import edge,
|
|
36
36
|
// and it points one way: md.ts measures with the width authority, never
|
|
@@ -370,6 +370,17 @@ class ThinkingBlock {
|
|
|
370
370
|
const text = escapeTerminal(this.cell.text).trim();
|
|
371
371
|
if (text === "")
|
|
372
372
|
return [];
|
|
373
|
+
// §2.3 — ctrl+t folded this block. The row is the pipe's own fold,
|
|
374
|
+
// fitted: `foldThinkingRow` is the single source of the shape and
|
|
375
|
+
// with unlimited room it IS `foldThinking`, so thinking has two
|
|
376
|
+
// renderings in the product and not three. It is width-aware here
|
|
377
|
+
// because a row must measure ≤ W and the pipe's line does not —
|
|
378
|
+
// cutting it from the right would take the `/think` suffix, which
|
|
379
|
+
// is the one part of a folded block that says how to read the rest.
|
|
380
|
+
// It keeps the block's own indent, so a folded block sits in the
|
|
381
|
+
// column an unfolded one does (DC-47).
|
|
382
|
+
if (this.cell.folded)
|
|
383
|
+
return [`${THINK_COL}${foldThinkingRow(this.cell.text, Math.max(1, W - THINK_COL.length))}`];
|
|
373
384
|
// DC-47 — THINKING GOES ONE LEVEL DEEPER THAN PROSE, and the
|
|
374
385
|
// reason is a law rather than a taste.
|
|
375
386
|
//
|
package/dist/render.d.ts
CHANGED
|
@@ -161,6 +161,21 @@ export declare function escapeTerminal(text: string): string;
|
|
|
161
161
|
* strategy is presentation-independent.
|
|
162
162
|
*/
|
|
163
163
|
export declare function foldThinking(block: string): string;
|
|
164
|
+
/** §2.3 — the same fold, as a ROW that fits `room` columns.
|
|
165
|
+
*
|
|
166
|
+
* A row must measure ≤ W (invariant ①), and the pipe's line does not:
|
|
167
|
+
* `…` + 100 characters + " (N chars · /think)" is about 122 columns, so
|
|
168
|
+
* on an 80-column terminal the frame's cut takes the SUFFIX — which is
|
|
169
|
+
* the affordance, the one part of a folded block that says how to read
|
|
170
|
+
* the rest of it. Cutting from the right removes exactly the thing the
|
|
171
|
+
* fold exists to leave behind.
|
|
172
|
+
*
|
|
173
|
+
* So the suffix is reserved FIRST and the head takes what is left. The
|
|
174
|
+
* vocabulary is unchanged — the leading `…`, the character count, the
|
|
175
|
+
* `/think` route — and with unlimited room the result is byte-for-byte
|
|
176
|
+
* the line the pipe has always written, which is what keeps the two
|
|
177
|
+
* renderings one shape rather than two. */
|
|
178
|
+
export declare function foldThinkingRow(block: string, room: number): string;
|
|
164
179
|
/** v2b — the [result] echo truncates at 160 chars + a /last hint. */
|
|
165
180
|
export declare function foldResult(content: string): string;
|
|
166
181
|
/**
|
|
@@ -179,6 +194,22 @@ export declare function renderToolSummary(name: string, input: Record<string, un
|
|
|
179
194
|
* the summary detail uses, WITHOUT the counts (the header names what
|
|
180
195
|
* was expanded, not its size). */
|
|
181
196
|
export declare function toolTarget(name: string, input: Record<string, unknown>): string;
|
|
197
|
+
/**
|
|
198
|
+
* HF-1 (0.32.1) — a head row is ONE row, whatever the model wrote.
|
|
199
|
+
*
|
|
200
|
+
* The owner's 0.32.0 dogfood: the model issued a heredoc shell command
|
|
201
|
+
* (`python3 - <<'EOF' …`), the running card's head carried its newlines
|
|
202
|
+
* into the compositor, invariant ①b threw in the field and the process
|
|
203
|
+
* died. `escapeTerminal` keeps `\n` on purpose (text blocks need it); a
|
|
204
|
+
* HEAD ROW does not — it is one physical row by construction. So every
|
|
205
|
+
* builder that puts a tool's target on a row projects the breaks to a
|
|
206
|
+
* visible mark first: a line break becomes `⏎` (CRLF is one), a tab
|
|
207
|
+
* becomes one cell of space (its width is a property of the column, and
|
|
208
|
+
* a cut row has no column to give it). The full command is still on the
|
|
209
|
+
* approval panel and in the durable log; the row says what ran, in one
|
|
210
|
+
* row.
|
|
211
|
+
*/
|
|
212
|
+
export declare function oneRow(text: string): string;
|
|
182
213
|
/** k-units for the status line: 12345 → 12.3k, 800 → 800, null → ?. */
|
|
183
214
|
export declare function kUnit(value: number | null): string;
|
|
184
215
|
/**
|
package/dist/render.js
CHANGED
|
@@ -113,10 +113,34 @@ export function escapeTerminal(text) {
|
|
|
113
113
|
* strategy is presentation-independent.
|
|
114
114
|
*/
|
|
115
115
|
export function foldThinking(block) {
|
|
116
|
+
// the PIPE's line: no room limit, so the row path below reproduces
|
|
117
|
+
// today's bytes exactly and this stays the one source of the shape.
|
|
118
|
+
return `${foldThinkingRow(block, Number.POSITIVE_INFINITY)}\n`;
|
|
119
|
+
}
|
|
120
|
+
/** §2.3 — the same fold, as a ROW that fits `room` columns.
|
|
121
|
+
*
|
|
122
|
+
* A row must measure ≤ W (invariant ①), and the pipe's line does not:
|
|
123
|
+
* `…` + 100 characters + " (N chars · /think)" is about 122 columns, so
|
|
124
|
+
* on an 80-column terminal the frame's cut takes the SUFFIX — which is
|
|
125
|
+
* the affordance, the one part of a folded block that says how to read
|
|
126
|
+
* the rest of it. Cutting from the right removes exactly the thing the
|
|
127
|
+
* fold exists to leave behind.
|
|
128
|
+
*
|
|
129
|
+
* So the suffix is reserved FIRST and the head takes what is left. The
|
|
130
|
+
* vocabulary is unchanged — the leading `…`, the character count, the
|
|
131
|
+
* `/think` route — and with unlimited room the result is byte-for-byte
|
|
132
|
+
* the line the pipe has always written, which is what keeps the two
|
|
133
|
+
* renderings one shape rather than two. */
|
|
134
|
+
export function foldThinkingRow(block, room) {
|
|
116
135
|
const p = palette();
|
|
117
136
|
const trimmed = escapeTerminal(block.trim());
|
|
118
137
|
const truncated = trimmed.length > 100;
|
|
119
|
-
|
|
138
|
+
const suffix = truncated ? ` (${block.length} chars · /think)` : "";
|
|
139
|
+
const head = Number.isFinite(room)
|
|
140
|
+
? // the leading … costs one column, the suffix costs its own width
|
|
141
|
+
widthCut(trimmed.slice(0, 100), Math.max(1, room - 1 - displayWidth(suffix)))
|
|
142
|
+
: trimmed.slice(0, 100);
|
|
143
|
+
return `${p.dim}…${head}${suffix}${p.reset}`;
|
|
120
144
|
}
|
|
121
145
|
/** v2b — the [result] echo truncates at 160 chars + a /last hint. */
|
|
122
146
|
export function foldResult(content) {
|
|
@@ -143,7 +167,7 @@ export function renderToolSummary(name, input, result, reason = null) {
|
|
|
143
167
|
}
|
|
144
168
|
const mark = result.isError ? `${p.red}✗${p.reset}` : `${p.bold}✓${p.reset}`;
|
|
145
169
|
const shortName = name.replace("_file", "");
|
|
146
|
-
const detail = toolSummaryDetail(name, input, result);
|
|
170
|
+
const detail = oneRow(toolSummaryDetail(name, input, result)); // HF-1: one row, whatever the command
|
|
147
171
|
return `${mark} ${escapeTerminal(`${shortName} ${detail}`)}`;
|
|
148
172
|
}
|
|
149
173
|
function toolSummaryDetail(name, input, result) {
|
|
@@ -189,6 +213,27 @@ function toolSummaryDetail(name, input, result) {
|
|
|
189
213
|
* the summary detail uses, WITHOUT the counts (the header names what
|
|
190
214
|
* was expanded, not its size). */
|
|
191
215
|
export function toolTarget(name, input) {
|
|
216
|
+
return oneRow(toolTargetRaw(name, input));
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* HF-1 (0.32.1) — a head row is ONE row, whatever the model wrote.
|
|
220
|
+
*
|
|
221
|
+
* The owner's 0.32.0 dogfood: the model issued a heredoc shell command
|
|
222
|
+
* (`python3 - <<'EOF' …`), the running card's head carried its newlines
|
|
223
|
+
* into the compositor, invariant ①b threw in the field and the process
|
|
224
|
+
* died. `escapeTerminal` keeps `\n` on purpose (text blocks need it); a
|
|
225
|
+
* HEAD ROW does not — it is one physical row by construction. So every
|
|
226
|
+
* builder that puts a tool's target on a row projects the breaks to a
|
|
227
|
+
* visible mark first: a line break becomes `⏎` (CRLF is one), a tab
|
|
228
|
+
* becomes one cell of space (its width is a property of the column, and
|
|
229
|
+
* a cut row has no column to give it). The full command is still on the
|
|
230
|
+
* approval panel and in the durable log; the row says what ran, in one
|
|
231
|
+
* row.
|
|
232
|
+
*/
|
|
233
|
+
export function oneRow(text) {
|
|
234
|
+
return text.replace(/\r\n|\n|\r/g, "\u23ce").replace(/\t/g, " ");
|
|
235
|
+
}
|
|
236
|
+
function toolTargetRaw(name, input) {
|
|
192
237
|
switch (name) {
|
|
193
238
|
case "read_file":
|
|
194
239
|
case "write_file":
|
package/dist/strings.js
CHANGED
|
@@ -440,6 +440,21 @@ export function helpRows() {
|
|
|
440
440
|
["/compact", "summarize the older conversation to free context"],
|
|
441
441
|
["/clear", "start a fresh conversation (the old session stays resumable)"],
|
|
442
442
|
["/resume", "switch to another session; /resume <id> goes directly"],
|
|
443
|
+
// §2.5: the conversation is untouched — this rereads what kiso was
|
|
444
|
+
// built with, not what it has said.
|
|
445
|
+
["/reload", "reread extensions, skills and config into this session"],
|
|
446
|
+
// §2.2: the two shell gestures and their one escape. They sit
|
|
447
|
+
// beside the slash commands because that is what a reader is
|
|
448
|
+
// looking for when they look here, even though `!` is not one.
|
|
449
|
+
// §2.3: the switch belongs beside ctrl+o's job, and a gesture the
|
|
450
|
+
// sheet does not name is a gesture nobody uses (DC-30, DC-36).
|
|
451
|
+
["ctrl+t", "fold the thinking blocks, and fold them back"],
|
|
452
|
+
// §2.4: the composer, in your own editor. It names the variables
|
|
453
|
+
// because that is what a reader has to set for it to work.
|
|
454
|
+
["ctrl+g", "edit the composer in $VISUAL or $EDITOR — the text comes back unsent"],
|
|
455
|
+
["!<cmd>", "run a shell command and send it with its output as your turn"],
|
|
456
|
+
["!!<cmd>", "run one and show it here only — the model never sees it"],
|
|
457
|
+
["\\!", "send a line that really starts with ! (the only escape)"],
|
|
443
458
|
["exit", "leave the session"],
|
|
444
459
|
// TUI2-R1 (D): the SENTENCE is deliberately unchanged. Deriving it
|
|
445
460
|
// from KEY_BINDINGS would be an improvement and it would also move
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-tui-cells",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.32.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",
|