@vincemakes/kiso-tui 0.15.0 → 0.15.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/compositor.d.ts +5 -0
- package/dist/compositor.js +18 -3
- package/dist/editor.d.ts +9 -0
- package/dist/editor.js +32 -1
- package/dist/status.d.ts +6 -0
- package/dist/status.js +1 -2
- package/package.json +38 -38
package/dist/compositor.d.ts
CHANGED
|
@@ -74,6 +74,11 @@ export interface InputState {
|
|
|
74
74
|
readonly cursorCol?: number;
|
|
75
75
|
}
|
|
76
76
|
export interface BodyOptions {
|
|
77
|
+
/** REL-0150-D1 test seam: overrides the TERM_PROGRAM detection for
|
|
78
|
+
* the conservative frame mode — process.env is SHARED across
|
|
79
|
+
* concurrently-running test files in one worker, so a test that
|
|
80
|
+
* mutated the env would bleed 40ms frames into its neighbors. */
|
|
81
|
+
readonly termProgram?: string;
|
|
77
82
|
/** Is the cell renderer live? A color TTY with a real size — checked
|
|
78
83
|
* per mutation (the TIOCSWINSZ can land after main constructs us). */
|
|
79
84
|
active: () => boolean;
|
package/dist/compositor.js
CHANGED
|
@@ -153,9 +153,24 @@ export class Body {
|
|
|
153
153
|
// box top), the live caps shrink by their rows, and the status
|
|
154
154
|
// row's right hint shows the count while any turn waits.
|
|
155
155
|
#queueState = () => [];
|
|
156
|
+
/** REL-0150-D1 — the conservative frame mode. Terminal.app does not
|
|
157
|
+
* support DEC 2026 synchronized output (half-frames paint on its own
|
|
158
|
+
* schedule — the reviewer dogfood watched the tearing live) and its
|
|
159
|
+
* renderer is throughput-weak (typed input lagged seconds behind the
|
|
160
|
+
* stream). Where TERM_PROGRAM says Apple_Terminal: the frame wraps
|
|
161
|
+
* in ?25l/?25h (cursor hidden during the repaint — the classic
|
|
162
|
+
* anti-tearing degrade; every frame re-shows it) instead of the dead
|
|
163
|
+
* 2026 bytes, and the coalesce window widens 16→40ms (fewer, bigger
|
|
164
|
+
* frames: fewer tear opportunities, less renderer pressure). Every
|
|
165
|
+
* other terminal keeps today's bytes exactly. Heuristic on purpose —
|
|
166
|
+
* a DECRQM round-trip would race the editor for stdin at boot. */
|
|
167
|
+
#conservative;
|
|
168
|
+
#frameMs;
|
|
156
169
|
constructor(opts) {
|
|
157
170
|
this.#opts = opts;
|
|
158
171
|
this.#write = opts.write ?? ((s) => process.stdout.write(s));
|
|
172
|
+
this.#conservative = (opts.termProgram ?? process.env.TERM_PROGRAM) === "Apple_Terminal";
|
|
173
|
+
this.#frameMs = this.#conservative ? 40 : FRAME_MS;
|
|
159
174
|
this.#active = opts.active();
|
|
160
175
|
// v6: the single writer — the compositor IS the dock; the CLI's
|
|
161
176
|
// onDock callback (which used to re-pin the dock after a scroll)
|
|
@@ -938,7 +953,7 @@ export class Body {
|
|
|
938
953
|
this.#dirty = false;
|
|
939
954
|
this.render();
|
|
940
955
|
}
|
|
941
|
-
},
|
|
956
|
+
}, this.#frameMs);
|
|
942
957
|
this.#frameTimer.unref();
|
|
943
958
|
}
|
|
944
959
|
/** The spinner: a ONE-SHOT re-armed ONLY while a running tool exists —
|
|
@@ -1165,7 +1180,7 @@ export class Body {
|
|
|
1165
1180
|
panelSpan === null ? null : { top: liveTop + panelSpan.offset, count: panelSpan.count, first: panelSpan.first };
|
|
1166
1181
|
// 5. the frame bytes.
|
|
1167
1182
|
const out = [];
|
|
1168
|
-
out.push("\x1b[?2026h"); //
|
|
1183
|
+
out.push(this.#conservative ? "\x1b[?25l" : "\x1b[?2026h"); // D1: sync ON, or cursor-hide where 2026 is dead bytes
|
|
1169
1184
|
// A8: the bottom-anchored window (the model's last H rows) shifts
|
|
1170
1185
|
// DOWN when the live region SHRINKS — the done-fold, the fold-hold
|
|
1171
1186
|
// release at the terminal event. The steady path's scroll syncs
|
|
@@ -1186,7 +1201,7 @@ export class Body {
|
|
|
1186
1201
|
else {
|
|
1187
1202
|
this.#drawSteady(out, W, H, liveTop, liveLines, queueRows, menuRows, editor);
|
|
1188
1203
|
}
|
|
1189
|
-
out.push("\x1b[?2026l");
|
|
1204
|
+
out.push(this.#conservative ? "\x1b[?25h" : "\x1b[?2026l");
|
|
1190
1205
|
this.#write(out.join(""));
|
|
1191
1206
|
this.#lastLiveTop = liveTop;
|
|
1192
1207
|
this.#lastLiveRows = liveRowsTotal;
|
package/dist/editor.d.ts
CHANGED
|
@@ -60,6 +60,9 @@ export declare class Editor {
|
|
|
60
60
|
onSigint(cb: () => void): void;
|
|
61
61
|
onEot(cb: () => void): void;
|
|
62
62
|
onEscape(cb: () => void): void;
|
|
63
|
+
/** R3a — Shift+Tab: the approval-tier cycle. The MEANING lives in the
|
|
64
|
+
* CLI (which tier follows which); the editor only reports the key. */
|
|
65
|
+
onModeCycle(cb: () => void): void;
|
|
63
66
|
onExpand(cb: () => void): void;
|
|
64
67
|
/** KC2 §2: the redirect chain — the gesture hands the buffer's text
|
|
65
68
|
* over while the run is told to stop. Mirrors onEscape (a list, so
|
|
@@ -153,4 +156,10 @@ export declare class Editor {
|
|
|
153
156
|
count: number;
|
|
154
157
|
first?: number;
|
|
155
158
|
} | null) | null): void;
|
|
159
|
+
/** R3a — cross-session input history: seed the recall buffer and
|
|
160
|
+
* register the append sink. The cap and the adjacent-duplicate
|
|
161
|
+
* collapse are unchanged; the seed takes the TAIL of what the CLI
|
|
162
|
+
* loaded. Never persists question answers (#remember's callers
|
|
163
|
+
* already exclude them). */
|
|
164
|
+
bindHistory(seed: readonly string[], persist: (line: string) => void): void;
|
|
156
165
|
}
|
package/dist/editor.js
CHANGED
|
@@ -55,6 +55,9 @@ export const MENU_ITEMS = [
|
|
|
55
55
|
{ name: "/mode", desc: "switch the approval tier (manual/default/accept-edits/plan/bypass)" },
|
|
56
56
|
{ name: "/model", desc: "list model profiles; switch with /model <name|provider/model>" },
|
|
57
57
|
{ name: "/compact", desc: "summarize the older conversation to free context" },
|
|
58
|
+
// the /resume+/clear mini-spec: the session-navigation pair
|
|
59
|
+
{ name: "/clear", desc: "start a fresh conversation (the old session stays resumable)" },
|
|
60
|
+
{ name: "/resume", desc: "switch to another session; /resume <id> goes directly" },
|
|
58
61
|
{ name: "/think", desc: "show the last full thinking block" },
|
|
59
62
|
{ name: "/last", desc: "show the most recent tool call's input and output" },
|
|
60
63
|
{ name: "/status", desc: "show session id, event count, and context estimate" },
|
|
@@ -223,6 +226,12 @@ export class Editor {
|
|
|
223
226
|
onEscape(cb) {
|
|
224
227
|
this.#escapeCbs.push(cb);
|
|
225
228
|
}
|
|
229
|
+
#onModeCycle = null;
|
|
230
|
+
/** R3a — Shift+Tab: the approval-tier cycle. The MEANING lives in the
|
|
231
|
+
* CLI (which tier follows which); the editor only reports the key. */
|
|
232
|
+
onModeCycle(cb) {
|
|
233
|
+
this.#onModeCycle = cb;
|
|
234
|
+
}
|
|
226
235
|
onExpand(cb) {
|
|
227
236
|
this.#expandCbs.push(cb);
|
|
228
237
|
}
|
|
@@ -1144,6 +1153,14 @@ export class Editor {
|
|
|
1144
1153
|
this.#mouseEvent(params, final === "M");
|
|
1145
1154
|
return;
|
|
1146
1155
|
}
|
|
1156
|
+
// R3a — Shift+Tab (CSI Z, the universal back-tab encoding) cycles
|
|
1157
|
+
// the approval tier. Composer-idle ONLY: a panel, picker, menu,
|
|
1158
|
+
// history browse or question owns its keys first (the W21 gate),
|
|
1159
|
+
// and a mid-word back-tab has no meaning the composer would miss.
|
|
1160
|
+
if (final === "Z" && params === "" && this.#composerIdle()) {
|
|
1161
|
+
this.#onModeCycle?.();
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1147
1164
|
// KC1 §4 — Shift+Enter WHERE THE TERMINAL ENCODES IT: kitty's
|
|
1148
1165
|
// CSI-u (ESC [ 13;2 u) and xterm's modifyOtherKeys (ESC [ 27;2;13 ~).
|
|
1149
1166
|
// Never claimed universal — Ctrl+J is the everywhere baseline; a
|
|
@@ -1736,11 +1753,25 @@ export class Editor {
|
|
|
1736
1753
|
* answers, never empties; adjacent duplicates collapse, the tail
|
|
1737
1754
|
* caps at 100. A redirect is a turn, so it is remembered too. */
|
|
1738
1755
|
#remember(line) {
|
|
1739
|
-
if (this.#history[this.#history.length - 1] !== line)
|
|
1756
|
+
if (this.#history[this.#history.length - 1] !== line) {
|
|
1740
1757
|
this.#history.push(line);
|
|
1758
|
+
// R3a: the persistence seam — the CLI owns the file (the tui
|
|
1759
|
+
// stays I/O-free); adjacent-duplicate collapse already applied
|
|
1760
|
+
this.#persistHistory?.(line);
|
|
1761
|
+
}
|
|
1741
1762
|
if (this.#history.length > 100)
|
|
1742
1763
|
this.#history.shift();
|
|
1743
1764
|
}
|
|
1765
|
+
#persistHistory = null;
|
|
1766
|
+
/** R3a — cross-session input history: seed the recall buffer and
|
|
1767
|
+
* register the append sink. The cap and the adjacent-duplicate
|
|
1768
|
+
* collapse are unchanged; the seed takes the TAIL of what the CLI
|
|
1769
|
+
* loaded. Never persists question answers (#remember's callers
|
|
1770
|
+
* already exclude them). */
|
|
1771
|
+
bindHistory(seed, persist) {
|
|
1772
|
+
this.#history = seed.slice(-100).filter((l) => l !== "");
|
|
1773
|
+
this.#persistHistory = persist;
|
|
1774
|
+
}
|
|
1744
1775
|
/** KC2 §2 — the NORMAL composer state: the redirect gesture is live
|
|
1745
1776
|
* ONLY here. The approval panel, the slash menu, the history browse
|
|
1746
1777
|
* and the queue-pop walk each OWN their keys first (the W21 "the
|
package/dist/status.d.ts
CHANGED
|
@@ -52,6 +52,12 @@ export declare function runningStatus(glyph: string, since: number, outTokens: n
|
|
|
52
52
|
*/
|
|
53
53
|
export interface StatusMeter {
|
|
54
54
|
readonly cacheHitPct: number | null;
|
|
55
|
+
/** RETIRED from the row (the owner's 2026-08-23 directive): live
|
|
56
|
+
* prices fluctuate and the canonical table is "an approximation,
|
|
57
|
+
* not a bill" — a four-decimal figure on the status bar claimed a
|
|
58
|
+
* precision the data never had. The canonical cost STAYS recorded
|
|
59
|
+
* (trace ledger, /context); the field is kept so callers need not
|
|
60
|
+
* change shape, and it renders NOTHING. */
|
|
55
61
|
readonly costUsd: number | null;
|
|
56
62
|
}
|
|
57
63
|
/** The IDLE row: the approval tier as the CALLER names it, the /mode
|
package/dist/status.js
CHANGED
|
@@ -55,8 +55,7 @@ export function idleStatus(tier, model, ctxRatio, meter) {
|
|
|
55
55
|
const parts = [`▸ ${tier}`, "/mode to switch", model];
|
|
56
56
|
if (meter?.cacheHitPct != null)
|
|
57
57
|
parts.push(`CH ${Math.round(meter.cacheHitPct)}%`);
|
|
58
|
-
|
|
59
|
-
parts.push(`$${meter.costUsd.toFixed(4)}`);
|
|
58
|
+
// costUsd deliberately NOT rendered — see StatusMeter.costUsd.
|
|
60
59
|
parts.push(`ctx left ~${ctxLeft(ctxRatio)}%`);
|
|
61
60
|
return parts.join(" · ");
|
|
62
61
|
}
|
package/package.json
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
2
|
+
"name": "@vincemakes/kiso-tui",
|
|
3
|
+
"version": "0.15.2",
|
|
4
|
+
"description": "kiso tui \u2014 the pure terminal layer (cell renderer, dock, raw editor, diff, palette). Zero runtime dependencies: input is data, output is bytes.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"README.md",
|
|
16
|
+
"LICENSE"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc -p tsconfig.build.json",
|
|
20
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
21
|
+
"test": "vitest run"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^26.1.2",
|
|
25
|
+
"typescript": "^5.7.2",
|
|
26
|
+
"vitest": "^3.0.0"
|
|
27
|
+
},
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/vincemakes/kiso.git",
|
|
31
|
+
"directory": "packages/tui"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/vincemakes/kiso/issues"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/vincemakes/kiso/tree/main/packages/tui#readme",
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@vincemakes/kiso-tui-cells": "0.15.2"
|
|
39
|
+
}
|
|
40
40
|
}
|