@vincemakes/kiso-tui 0.20.0 → 0.20.2
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/at-picker.d.ts +2 -1
- package/dist/at-picker.js +6 -5
- package/dist/compositor.d.ts +17 -0
- package/dist/compositor.js +76 -10
- package/dist/editor.js +17 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
package/dist/at-picker.d.ts
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* lexical order of the path (never localeCompare, whose result depends
|
|
13
13
|
* on the machine's locale).
|
|
14
14
|
*/
|
|
15
|
+
import { bandHeader } from "@vincemakes/kiso-tui-cells/strings";
|
|
15
16
|
/** The bound source's item — a repo-relative path and nothing else.
|
|
16
17
|
* Structural: the CLI passes whatever it likes as long as it has a
|
|
17
18
|
* path (slice 5 passes exactly this). */
|
|
@@ -168,4 +169,4 @@ export declare function atPanelRows(state: {
|
|
|
168
169
|
* composer and every panel now share, so a band opens the way everything
|
|
169
170
|
* else does and the label tells you WHICH band in the same row.
|
|
170
171
|
*/
|
|
171
|
-
export
|
|
172
|
+
export { bandHeader };
|
package/dist/at-picker.js
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { escapeTerminal, palette } from "./render.js";
|
|
16
16
|
import { selectionBar, visibleWidth, widthCut } from "./components.js";
|
|
17
|
+
import { bandHeader } from "@vincemakes/kiso-tui-cells/strings";
|
|
17
18
|
/**
|
|
18
19
|
* KC3 §5 — the ONE cap. The file list is computed per open with no
|
|
19
20
|
* index and no watcher, so its cost is bounded here rather than
|
|
@@ -263,8 +264,8 @@ export function atPanelRows(state, W) {
|
|
|
263
264
|
* composer and every panel now share, so a band opens the way everything
|
|
264
265
|
* else does and the label tells you WHICH band in the same row.
|
|
265
266
|
*/
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
}
|
|
267
|
+
// R8b: bandHeader MOVED to tui-cells/strings.ts — the keys sheet needs
|
|
268
|
+
// it and lives there, and `components.ts` already imports that module,
|
|
269
|
+
// so the dependency only runs one way. Re-exported here so every
|
|
270
|
+
// existing import site is untouched.
|
|
271
|
+
export { bandHeader };
|
package/dist/compositor.d.ts
CHANGED
|
@@ -227,6 +227,22 @@ export declare class Body {
|
|
|
227
227
|
* there is no pointer, so there is nothing a pointer could reach
|
|
228
228
|
* that a keyboard cannot. */
|
|
229
229
|
viewerKey(cmd: "up" | "down" | "toggle" | "all" | "pageUp" | "pageDown" | "home" | "end"): void;
|
|
230
|
+
/**
|
|
231
|
+
* DC-35 — ctrl+r does not print the same expansion twice in a row.
|
|
232
|
+
*
|
|
233
|
+
* The ring walks newest-back and restarts its cycle once every entry
|
|
234
|
+
* has been opened (R4/C1, which is what makes the walk immune to the
|
|
235
|
+
* ring growing underneath it). With a ring of ONE the restart is
|
|
236
|
+
* immediate, so holding the key appended the identical block over
|
|
237
|
+
* and over — the owner got three copies of the same four rows, each
|
|
238
|
+
* closing with `ctrl+r opens the one before it`, a footer naming
|
|
239
|
+
* something that does not exist.
|
|
240
|
+
*
|
|
241
|
+
* The bar is the BOTTOM of the transcript, not "ever shown": once
|
|
242
|
+
* other content has arrived the expansion has scrolled up and
|
|
243
|
+
* re-opening it is the point of the key, so the guard clears itself
|
|
244
|
+
* the moment a cell is added.
|
|
245
|
+
*/
|
|
230
246
|
expandNext(): {
|
|
231
247
|
kind: "toggled";
|
|
232
248
|
} | {
|
|
@@ -234,6 +250,7 @@ export declare class Body {
|
|
|
234
250
|
lines: string[];
|
|
235
251
|
} | {
|
|
236
252
|
kind: "none";
|
|
253
|
+
why?: "already-last";
|
|
237
254
|
};
|
|
238
255
|
/** Docked = the chrome is live (a color TTY with a real size). */
|
|
239
256
|
get active(): boolean;
|
package/dist/compositor.js
CHANGED
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
* line-mode bytes byte-for-byte (the e2e guards them).
|
|
44
44
|
*/
|
|
45
45
|
import { truncateDiff } from "./diff.js";
|
|
46
|
-
import { displayWidth } from "./editor.js";
|
|
46
|
+
import { MENU_ITEMS, displayWidth } from "./editor.js";
|
|
47
47
|
import { leadWidth } from "./width.js"; // W23: the ONE width authority (the editor, #inputRow, and editCol share it)
|
|
48
48
|
// KC3.5: the panel-slot reads come from the DISPATCHERS — one source
|
|
49
49
|
// for four reads, so an ask can never render half as an approval.
|
|
@@ -52,7 +52,7 @@ import { MOUSE_OFF } from "./editor.js";
|
|
|
52
52
|
import { atPanelRows, bandHeader } from "./at-picker.js";
|
|
53
53
|
// TUI2-R2 ②: the session picker's rows — the band's third occupant.
|
|
54
54
|
import { sessionPickerRows } from "./session-picker.js";
|
|
55
|
-
import { ACT_SLOT_ROWS, Container, ROLLUP_NOUN, MOTION_FRAMES, MdStream, bodySpacing, boxBottom, boxTop, cellComponent, exploreCounts, foldCountsObjects, foldTerms, focusToken, exploreRows, foldLine, cutLine, isExploreTool, moreRunningRow, pendingQueueRows, slotPad, slotTail, statusLine, stretchLine, turnFold, visibleWidth, breathFrame, } from "./components.js";
|
|
55
|
+
import { ACT_SLOT_ROWS, Container, ROLLUP_NOUN, MOTION_FRAMES, MdStream, bodySpacing, boxBottom, boxTop, cellComponent, exploreCounts, foldCountsObjects, foldTerms, focusToken, exploreRows, foldLine, gutterCut, cutLine, isExploreTool, moreRunningRow, pendingQueueRows, slotPad, slotTail, statusLine, stretchLine, turnFold, visibleWidth, breathFrame, } from "./components.js";
|
|
56
56
|
import { bannerLines, escapeTerminal, foldResult, foldThinking, palette, renderTerminalGap, renderToolSummary, toolTarget } from "./render.js";
|
|
57
57
|
import { displayVerb, keysSheetRows } from "./strings.js";
|
|
58
58
|
// R5 — the transcript viewer's PURE projection. The compositor supplies
|
|
@@ -77,6 +77,9 @@ const CHROME_ROWS = 4; // box top + input + box bottom + status — the design
|
|
|
77
77
|
* same unbounded height the projection exists to remove; the rest are
|
|
78
78
|
* COUNTED, never dropped silently. */
|
|
79
79
|
const LIVE_ACT_HEADS = 3;
|
|
80
|
+
/** R8 — the command band's window: five rows plus a counter, the same
|
|
81
|
+
* budget the composer's own ceiling can afford above it. */
|
|
82
|
+
const MENU_WINDOW = 5;
|
|
80
83
|
/** W13 / TUI2-R1 (B) — a rolled run's TITLE: the exploration sentence on
|
|
81
84
|
* a mixed run, W13's verb+count on a single-name one. */
|
|
82
85
|
function rolledTitle(cell) {
|
|
@@ -1333,7 +1336,42 @@ export class Body {
|
|
|
1333
1336
|
cutLine(`${p.dim} ${viewerHint(this.#viewer, entries)}${p.reset}`, W),
|
|
1334
1337
|
];
|
|
1335
1338
|
}
|
|
1339
|
+
/** DC-35 — the last block this key appended, and the cell count when
|
|
1340
|
+
* it did. An expansion earns its rows by showing something the
|
|
1341
|
+
* transcript does not already END with. */
|
|
1342
|
+
#lastAppend = null;
|
|
1343
|
+
/**
|
|
1344
|
+
* DC-35 — ctrl+r does not print the same expansion twice in a row.
|
|
1345
|
+
*
|
|
1346
|
+
* The ring walks newest-back and restarts its cycle once every entry
|
|
1347
|
+
* has been opened (R4/C1, which is what makes the walk immune to the
|
|
1348
|
+
* ring growing underneath it). With a ring of ONE the restart is
|
|
1349
|
+
* immediate, so holding the key appended the identical block over
|
|
1350
|
+
* and over — the owner got three copies of the same four rows, each
|
|
1351
|
+
* closing with `ctrl+r opens the one before it`, a footer naming
|
|
1352
|
+
* something that does not exist.
|
|
1353
|
+
*
|
|
1354
|
+
* The bar is the BOTTOM of the transcript, not "ever shown": once
|
|
1355
|
+
* other content has arrived the expansion has scrolled up and
|
|
1356
|
+
* re-opening it is the point of the key, so the guard clears itself
|
|
1357
|
+
* the moment a cell is added.
|
|
1358
|
+
*/
|
|
1336
1359
|
expandNext() {
|
|
1360
|
+
const out = this.#expandNextRaw();
|
|
1361
|
+
if (out.kind !== "appended")
|
|
1362
|
+
return out;
|
|
1363
|
+
const lines = out.lines.join("\n");
|
|
1364
|
+
if (this.#lastAppend !== null && this.#lastAppend.lines === lines && this.#lastAppend.atCells === this.#cells.length) {
|
|
1365
|
+
// NOT the same answer as "nothing is folded". The caller says
|
|
1366
|
+
// which, because a reader who pressed the key deserves to know
|
|
1367
|
+
// whether there is nothing to open or whether they are already
|
|
1368
|
+
// looking at it.
|
|
1369
|
+
return { kind: "none", why: "already-last" };
|
|
1370
|
+
}
|
|
1371
|
+
this.#lastAppend = { lines, atCells: this.#cells.length };
|
|
1372
|
+
return out;
|
|
1373
|
+
}
|
|
1374
|
+
#expandNextRaw() {
|
|
1337
1375
|
for (let i = this.#cells.length - 1; i >= this.#committed; i -= 1) {
|
|
1338
1376
|
const cell = this.#cells[i];
|
|
1339
1377
|
if (cell.kind === "tool" && cell.state !== "pending") {
|
|
@@ -1426,7 +1464,9 @@ export class Body {
|
|
|
1426
1464
|
// always the part a reader could use.
|
|
1427
1465
|
`${p.bold}✦${p.reset} expanded · ${escapeTerminal(head.length === 0 ? "thinking" : head.join(" · "))} · ${back}`,
|
|
1428
1466
|
...body,
|
|
1429
|
-
|
|
1467
|
+
// R8a: an in-block note takes the block's indent, not a
|
|
1468
|
+
// second corner — the corner opens the body above it.
|
|
1469
|
+
`${p.dim} end of expansion · ctrl+r opens the one before it${p.reset}`,
|
|
1430
1470
|
],
|
|
1431
1471
|
};
|
|
1432
1472
|
}
|
|
@@ -3261,14 +3301,40 @@ export class Body {
|
|
|
3261
3301
|
// picker's does. Both render frameless directly above the composer,
|
|
3262
3302
|
// so with scrollback behind them there was nothing to say where the
|
|
3263
3303
|
// surface began — the rows read as more history.
|
|
3304
|
+
// R8 — THE BAND IS A WINDOW, and the rows are a table.
|
|
3305
|
+
//
|
|
3306
|
+
// It used to draw every match and fold each long description over
|
|
3307
|
+
// as many rows as it took, which is why a bare `/` could not open
|
|
3308
|
+
// it: eleven commands plus wraps is most of a short terminal. A
|
|
3309
|
+
// fixed window is what lets the trigger be the `/` the banner
|
|
3310
|
+
// advertises (see the editor's #menuFiltered).
|
|
3311
|
+
//
|
|
3312
|
+
// Three shape rules, all of them §1.3 or §1.2:
|
|
3313
|
+
// - the leading `/` comes off the rows. It is already on the
|
|
3314
|
+
// input line directly below, so printing it eleven more times
|
|
3315
|
+
// is a mark carrying no fact the screen does not have.
|
|
3316
|
+
// - the name column is padded to the longest command in the
|
|
3317
|
+
// WHOLE list, not the visible slice, so the descriptions do
|
|
3318
|
+
// not shift sideways as the window scrolls.
|
|
3319
|
+
// - a description is CUT, never folded — a folded row would
|
|
3320
|
+
// break the window's height, which is the thing being bought.
|
|
3321
|
+
const items = menu.items;
|
|
3322
|
+
const col = MENU_ITEMS.reduce((n, m) => Math.max(n, m.name.length - 1), 0);
|
|
3323
|
+
const windowed = items.length > MENU_WINDOW;
|
|
3324
|
+
// the window's top is derived from the selection alone (this
|
|
3325
|
+
// method is re-entered per frame and keeps no state): centre it,
|
|
3326
|
+
// clamped to the ends.
|
|
3327
|
+
const top = windowed ? Math.max(0, Math.min(menu.selected - ((MENU_WINDOW - 1) >> 1), items.length - MENU_WINDOW)) : 0;
|
|
3264
3328
|
const rows = [bandHeader("commands", W)];
|
|
3265
|
-
for (let i =
|
|
3266
|
-
const item =
|
|
3267
|
-
const
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3329
|
+
for (let i = top; i < Math.min(items.length, top + MENU_WINDOW); i += 1) {
|
|
3330
|
+
const item = items[i];
|
|
3331
|
+
const label = `${item.name.slice(1).padEnd(col)} ${item.desc}`;
|
|
3332
|
+
rows.push(...(i === menu.selected ? gutterCut(`${p.bold}▸${p.reset} `, `${p.bold}${label}${p.reset}`, W) : gutterCut(" ", `${p.dim}${label}${p.reset}`, W)));
|
|
3333
|
+
}
|
|
3334
|
+
// the counter earns its row only when the list is CUT — over a
|
|
3335
|
+
// list you can see all of, it says nothing the rows do not.
|
|
3336
|
+
if (windowed)
|
|
3337
|
+
rows.push(` ${p.dim}(${menu.selected + 1}/${items.length})${p.reset}`);
|
|
3272
3338
|
return rows;
|
|
3273
3339
|
}
|
|
3274
3340
|
/** ONE input row's bytes — the marker embedded at `embedAt` (the
|
package/dist/editor.js
CHANGED
|
@@ -542,11 +542,20 @@ export class Editor {
|
|
|
542
542
|
return null;
|
|
543
543
|
return { items: this.#menuFiltered(), selected: this.#menuSel };
|
|
544
544
|
}
|
|
545
|
-
/** v3 §04: the filtered command list for the current buffer
|
|
546
|
-
*
|
|
545
|
+
/** v3 §04: the filtered command list for the current buffer.
|
|
546
|
+
*
|
|
547
|
+
* A BARE `/` OPENS IT (owner-ruled 2026-09-01). It used to wait for
|
|
548
|
+
* a second character, which made the key the banner advertises —
|
|
549
|
+
* `/ commands` — a thing you had to already know the answer to: the
|
|
550
|
+
* list that tells you the commands appeared only once you had typed
|
|
551
|
+
* one. The reason for the wait was real and is fixed on the other
|
|
552
|
+
* side: the band drew EVERY match with no window, so a bare `/`
|
|
553
|
+
* would have piled eleven rows plus wraps above the composer. The
|
|
554
|
+
* band windows now (see the compositor's #menuRows), so the trigger
|
|
555
|
+
* no longer has to do the rationing. */
|
|
547
556
|
#menuFiltered() {
|
|
548
557
|
const line = this.line();
|
|
549
|
-
if (!line.startsWith("/")
|
|
558
|
+
if (!line.startsWith("/"))
|
|
550
559
|
return [];
|
|
551
560
|
return MENU_ITEMS.filter((m) => m.name.startsWith(line));
|
|
552
561
|
}
|
|
@@ -995,7 +1004,11 @@ export class Editor {
|
|
|
995
1004
|
i += 1;
|
|
996
1005
|
continue;
|
|
997
1006
|
}
|
|
998
|
-
|
|
1007
|
+
// DC-36: no `t` row means no custom phase to enter — the
|
|
1008
|
+
// option list IS the world (a closed set), and a key
|
|
1009
|
+
// that leads to a surface the panel does not draw is
|
|
1010
|
+
// worse than an absent key.
|
|
1011
|
+
if (!typing && (c === "t" || c === "T") && this.#panel?.view.pick?.typeHint !== undefined) {
|
|
999
1012
|
panel.pick = { cursor: panel.pick.cursor, phase: "custom" };
|
|
1000
1013
|
this.#chars = [];
|
|
1001
1014
|
this.#cursor = 0;
|
package/dist/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* editor, the diff renderer, and the palette.
|
|
8
8
|
*/
|
|
9
9
|
export { Body, Dock, CURSOR_MARKER, type BodyOptions } from "./compositor.js";
|
|
10
|
-
export { panelAffordance, panelBlockRows, panelLead, panelLeadPlain, panelLeadWidth, panelStatus, PICK_MAX, modelPickView, pickAffordance, pickBlockRows, pickLeadPlain, type PickOption, type PickResult, type PickRuntime, type PickSpec, type PanelArgs, type PanelFlavor, type PanelPhase, deletionRiskHint, SAFER_BACK, SAFER_DEGRADED, SAFER_DEGRADED_TRUNCATED, saferDegradedNote, type SaferAnswer, type SaferFailure, type SaferOption, type SaferRuntime, panelOptions, type PanelOption, type PanelOptionKind, type PanelState, type PanelVerdict, type PanelView, } from "./approval-panel.js";
|
|
10
|
+
export { panelAffordance, panelBlockRows, panelLead, panelLeadPlain, panelLeadWidth, panelStatus, PICK_MAX, modePickView, modelPickView, pickAffordance, pickBlockRows, pickLeadPlain, type PickOption, type PickResult, type PickRuntime, type PickSpec, type PanelArgs, type PanelFlavor, type PanelPhase, deletionRiskHint, SAFER_BACK, SAFER_DEGRADED, SAFER_DEGRADED_TRUNCATED, saferDegradedNote, type SaferAnswer, type SaferFailure, type SaferOption, type SaferRuntime, panelOptions, type PanelOption, type PanelOptionKind, type PanelState, type PanelVerdict, type PanelView, } from "./approval-panel.js";
|
|
11
11
|
export { Container, foldLine, foldWords, visibleWidth, SPINNER, type Component, type FrameCtx } from "./components.js";
|
|
12
12
|
export { Editor, MENU_ITEMS, PROMPT, PROMPT_WIDTH, displayWidth, charWidth, widthOf, type MenuItem, } from "./editor.js";
|
|
13
13
|
export { bannerLines, COLOR_OFF, COLOR_ON, currentGround, setGround, escapeTerminal, foldResult, foldThinking, kUnit, palette, renderEvent, renderRecap, renderResumeList, renderSessionLine, renderStatusLine, relativeTime, renderTerminalGap, renderToolSummary, TAGLINE, toolTarget, truncateRow, type Palette, type PathResolver, type RecapStats, type ResumeMeta, type RenderInput, type RenderResult, type RunUsage, } from "./render.js";
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ export { Body, Dock, CURSOR_MARKER } from "./compositor.js";
|
|
|
12
12
|
// approval is pending (the shape authority is the committed preview).
|
|
13
13
|
export { panelAffordance, panelBlockRows, panelLead, panelLeadPlain, panelLeadWidth, panelStatus,
|
|
14
14
|
// TUI2-R2 ④: the pick payload — the panel slot's third occupant.
|
|
15
|
-
PICK_MAX, modelPickView, pickAffordance, pickBlockRows, pickLeadPlain, deletionRiskHint, SAFER_BACK, SAFER_DEGRADED, SAFER_DEGRADED_TRUNCATED, saferDegradedNote, panelOptions, } from "./approval-panel.js";
|
|
15
|
+
PICK_MAX, modePickView, modelPickView, pickAffordance, pickBlockRows, pickLeadPlain, deletionRiskHint, SAFER_BACK, SAFER_DEGRADED, SAFER_DEGRADED_TRUNCATED, saferDegradedNote, panelOptions, } from "./approval-panel.js";
|
|
16
16
|
export { Container, foldLine, foldWords, visibleWidth, SPINNER } from "./components.js";
|
|
17
17
|
export { Editor, MENU_ITEMS, PROMPT, PROMPT_WIDTH, displayWidth, charWidth, widthOf, } from "./editor.js";
|
|
18
18
|
export { bannerLines, COLOR_OFF, COLOR_ON, currentGround, setGround, escapeTerminal, foldResult, foldThinking, kUnit, palette, renderEvent, renderRecap, renderResumeList, renderSessionLine, renderStatusLine, relativeTime, renderTerminalGap, renderToolSummary, TAGLINE, toolTarget, truncateRow, } from "./render.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-tui",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.2",
|
|
4
4
|
"description": "kiso tui — the pure terminal layer (cell renderer, dock, raw editor, diff, palette). Zero runtime dependencies: input is data, output is bytes.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -35,6 +35,6 @@
|
|
|
35
35
|
},
|
|
36
36
|
"homepage": "https://github.com/vincemakes/kiso/tree/main/packages/tui#readme",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@vincemakes/kiso-tui-cells": "0.20.
|
|
38
|
+
"@vincemakes/kiso-tui-cells": "0.20.2"
|
|
39
39
|
}
|
|
40
40
|
}
|