@vincemakes/kiso-tui 0.39.2 → 0.40.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/compositor.d.ts +39 -3
- package/dist/compositor.js +133 -8
- package/dist/context-ledger.d.ts +5 -0
- package/dist/context-ledger.js +11 -2
- package/dist/editor.d.ts +22 -7
- package/dist/editor.js +111 -40
- package/dist/index.d.ts +3 -3
- package/dist/index.js +6 -3
- package/dist/lines.d.ts +5 -0
- package/dist/lines.js +19 -4
- package/dist/panel-input.d.ts +8 -0
- package/dist/panel-input.js +17 -4
- package/dist/pick-input.d.ts +5 -1
- package/dist/pick-input.js +33 -5
- package/dist/session-picker.d.ts +54 -26
- package/dist/session-picker.js +109 -54
- package/dist/status.d.ts +62 -2
- package/dist/status.js +94 -42
- package/package.json +2 -2
package/dist/compositor.d.ts
CHANGED
|
@@ -132,10 +132,13 @@ export declare class Body {
|
|
|
132
132
|
toolProgress(callId: string, text: string): void;
|
|
133
133
|
toolSucceeded(callId: string): void;
|
|
134
134
|
toolFailed(callId: string, error: string): void;
|
|
135
|
+
/** `untimed`: 4c's replay settles a card from the durable log, whose
|
|
136
|
+
* events carry no clock — the card then says nothing about time. */
|
|
135
137
|
toolResult(callId: string, result: {
|
|
136
138
|
content: string;
|
|
137
139
|
isError: boolean;
|
|
138
140
|
reason?: string | null;
|
|
141
|
+
untimed?: boolean;
|
|
139
142
|
}): void;
|
|
140
143
|
textAppend(text: string): void;
|
|
141
144
|
textEnd(): void;
|
|
@@ -144,6 +147,18 @@ export declare class Body {
|
|
|
144
147
|
* CLI's wall-clocked thinking window. */
|
|
145
148
|
endTurn(thoughtSeconds: number): void;
|
|
146
149
|
terminal(label: string, statusLineText: string): void;
|
|
150
|
+
/**
|
|
151
|
+
* 4c — fold what `replay` puts on the body into ONE row. The replayed
|
|
152
|
+
* cells are the same cells a live run makes (the caller drives the
|
|
153
|
+
* ordinary mutations), moved out of the transcript into the fold cell:
|
|
154
|
+
* the row commits as one line and never expands on screen — so a resize
|
|
155
|
+
* reprint redraws one row however long the history — and the ctrl+r
|
|
156
|
+
* viewer reads them. Mutations only schedule frames, so nothing the
|
|
157
|
+
* replay adds can have committed before it is moved. Active bodies
|
|
158
|
+
* only: a pipe has no viewer to read a fold with (the caller prints the
|
|
159
|
+
* plain tail there).
|
|
160
|
+
*/
|
|
161
|
+
fold(label: string, replay: () => void, summary?: string | null): void;
|
|
147
162
|
notice(text: string): void;
|
|
148
163
|
/** W20 — the task checklist as STATE, not events: the FIRST call of a
|
|
149
164
|
* turn creates the ONE live block (done:false — the commit loop only
|
|
@@ -220,6 +235,10 @@ export declare class Body {
|
|
|
220
235
|
count: number;
|
|
221
236
|
first: number;
|
|
222
237
|
} | null;
|
|
238
|
+
visiblePickWindow(): {
|
|
239
|
+
first: number;
|
|
240
|
+
size: number;
|
|
241
|
+
} | null;
|
|
223
242
|
/** SIGWINCH: clear the OLD live area (recorded geometry, ED only —
|
|
224
243
|
* zero LF, zero \x1b[3J — the shell history untouched), then the
|
|
225
244
|
* full-redraw path at the NEW geometry (O(height), zero replay).
|
|
@@ -339,8 +358,19 @@ export declare class Body {
|
|
|
339
358
|
* reads it — the marker math never desyncs by construction). */
|
|
340
359
|
editCol(): number;
|
|
341
360
|
/** The old dock's redraw — the editor's onRender target: mark + the
|
|
342
|
-
* scheduler (16ms coalescing — the old sync draw coalesces the same).
|
|
343
|
-
|
|
361
|
+
* scheduler (16ms coalescing — the old sync draw coalesces the same).
|
|
362
|
+
*
|
|
363
|
+
* Item 6: a KEY-originated redraw paints on the next tick and takes
|
|
364
|
+
* the pending trailing frame with it. Keyboard input is the one
|
|
365
|
+
* latency-sensitive source: the trailing window made every key wait
|
|
366
|
+
* 16 ms, and 40 ms on Terminal.app — where an IME commit erases the
|
|
367
|
+
* terminal's marked text at once and the committed characters came
|
|
368
|
+
* back a frame window later, a visible blink. Stream, tool and
|
|
369
|
+
* spinner marks keep the trailing window exactly (the conservative
|
|
370
|
+
* mode's throughput protection is for them). setImmediate, not a
|
|
371
|
+
* synchronous paint: every mutation one chunk of keys makes still
|
|
372
|
+
* lands in one frame. */
|
|
373
|
+
redraw(fromKey?: boolean): void;
|
|
344
374
|
/** Teardown — flush a pending frame, stop the timers. */
|
|
345
375
|
close(): void;
|
|
346
376
|
/** The live region's scalar — the unit tests assert the cap directly
|
|
@@ -369,6 +399,12 @@ export declare class Dock {
|
|
|
369
399
|
count: number;
|
|
370
400
|
first: number;
|
|
371
401
|
} | null;
|
|
402
|
+
/** B (review of this round): the pick panel's window for the frame last
|
|
403
|
+
* drawn — what the digit keys must name. */
|
|
404
|
+
visiblePickWindow(): {
|
|
405
|
+
first: number;
|
|
406
|
+
size: number;
|
|
407
|
+
} | null;
|
|
372
408
|
setStatus(text: string, hint?: string | null): void;
|
|
373
409
|
setTail(tail: string): void;
|
|
374
410
|
/** W21: bind the editor's panel state — the PanelSelect slot
|
|
@@ -403,5 +439,5 @@ export declare class Dock {
|
|
|
403
439
|
* the LineInput's own bindQueue). */
|
|
404
440
|
bindQueue(state: () => readonly string[]): void;
|
|
405
441
|
editCol(): number;
|
|
406
|
-
redraw(): void;
|
|
442
|
+
redraw(fromKey?: boolean): void;
|
|
407
443
|
}
|
package/dist/compositor.js
CHANGED
|
@@ -52,6 +52,7 @@ import { leadWidth } from "./width.js"; // W23: the ONE width authority (the edi
|
|
|
52
52
|
// for four reads, so an ask can never render half as an approval.
|
|
53
53
|
import { panelFrameOf, panelLeadOf, panelStatusOf } from "./ask-panel.js";
|
|
54
54
|
import { MOUSE_OFF } from "./editor.js";
|
|
55
|
+
import { pickWindowOf } from "./approval-panel.js";
|
|
55
56
|
import { atPanelRows, bandHeader } from "./at-picker.js";
|
|
56
57
|
// TUI2-R2 ②: the session picker's rows — the band's third occupant.
|
|
57
58
|
import { sessionPickerRows } from "./session-picker.js";
|
|
@@ -197,6 +198,10 @@ export class Body {
|
|
|
197
198
|
#lastInputRows = 1;
|
|
198
199
|
#lastAnchorRow = 0;
|
|
199
200
|
#frameTimer = null;
|
|
201
|
+
#inputFrame = null;
|
|
202
|
+
/** Body mutations and non-key redraws so far — the input frame's
|
|
203
|
+
* "did anything but typing change the screen?" */
|
|
204
|
+
#runMarks = 0;
|
|
200
205
|
#spinnerTimer = null;
|
|
201
206
|
#spinnerI = 0;
|
|
202
207
|
#lastThinking = null;
|
|
@@ -520,6 +525,8 @@ export class Body {
|
|
|
520
525
|
this.#write(`${p.red} failed: ${escapeTerminal(error.slice(0, 160))}${p.reset}\n`);
|
|
521
526
|
}
|
|
522
527
|
}
|
|
528
|
+
/** `untimed`: 4c's replay settles a card from the durable log, whose
|
|
529
|
+
* events carry no clock — the card then says nothing about time. */
|
|
523
530
|
toolResult(callId, result) {
|
|
524
531
|
const call = this.#pendingCalls.get(callId);
|
|
525
532
|
if (call !== undefined) {
|
|
@@ -543,7 +550,7 @@ export class Body {
|
|
|
543
550
|
cell.isError = result.isError;
|
|
544
551
|
cell.resultText = result.content;
|
|
545
552
|
cell.reason = result.reason ?? null;
|
|
546
|
-
cell.doneAt = Date.now();
|
|
553
|
+
cell.doneAt = result.untimed === true ? null : Date.now();
|
|
547
554
|
cell.done = true;
|
|
548
555
|
}
|
|
549
556
|
this.#mark();
|
|
@@ -692,6 +699,31 @@ export class Body {
|
|
|
692
699
|
this.#cells.push({ kind: "terminal", label: label.trim(), line: statusLineText, done: true });
|
|
693
700
|
this.#mark();
|
|
694
701
|
}
|
|
702
|
+
/**
|
|
703
|
+
* 4c — fold what `replay` puts on the body into ONE row. The replayed
|
|
704
|
+
* cells are the same cells a live run makes (the caller drives the
|
|
705
|
+
* ordinary mutations), moved out of the transcript into the fold cell:
|
|
706
|
+
* the row commits as one line and never expands on screen — so a resize
|
|
707
|
+
* reprint redraws one row however long the history — and the ctrl+r
|
|
708
|
+
* viewer reads them. Mutations only schedule frames, so nothing the
|
|
709
|
+
* replay adds can have committed before it is moved. Active bodies
|
|
710
|
+
* only: a pipe has no viewer to read a fold with (the caller prints the
|
|
711
|
+
* plain tail there).
|
|
712
|
+
*/
|
|
713
|
+
fold(label, replay, summary = null) {
|
|
714
|
+
if (!this.#isActive())
|
|
715
|
+
return;
|
|
716
|
+
const start = this.#cells.length;
|
|
717
|
+
replay();
|
|
718
|
+
this.#closeOpenThinking();
|
|
719
|
+
this.#closeOpenText();
|
|
720
|
+
const children = this.#cells.splice(start);
|
|
721
|
+
for (const cell of children)
|
|
722
|
+
if (!cell.done)
|
|
723
|
+
cell.done = true; // a replay settles; nothing in a fold is live
|
|
724
|
+
this.#cells.push({ kind: "fold", label, children, summary, done: true });
|
|
725
|
+
this.#mark();
|
|
726
|
+
}
|
|
695
727
|
notice(text) {
|
|
696
728
|
if (!this.#isActive()) {
|
|
697
729
|
this.#closeOpenThinking();
|
|
@@ -935,8 +967,15 @@ export class Body {
|
|
|
935
967
|
const cell = this.#cells[idx];
|
|
936
968
|
if (cell === undefined)
|
|
937
969
|
continue;
|
|
938
|
-
//
|
|
939
|
-
//
|
|
970
|
+
// 4c — the resumed history's fold: the head is its one row, the
|
|
971
|
+
// body is the replayed turns rendered at the viewer's width —
|
|
972
|
+
// the checkpoint's summary first when it is one.
|
|
973
|
+
if (cell.kind === "fold") {
|
|
974
|
+
out.push({ head: cellComponent(cell).render(inner, ctx)[0] ?? "", body: this.#foldBody(cell, inner, ctx) });
|
|
975
|
+
continue;
|
|
976
|
+
}
|
|
977
|
+
// R13 — every other entry is a card, and a card's entry is its
|
|
978
|
+
// own full body.
|
|
940
979
|
if (cell.kind !== "tool")
|
|
941
980
|
continue;
|
|
942
981
|
// the tool card's FULL body — the same rows its own ctrl+o
|
|
@@ -959,6 +998,24 @@ export class Body {
|
|
|
959
998
|
}
|
|
960
999
|
return out;
|
|
961
1000
|
}
|
|
1001
|
+
/** 4c — a fold's children as the transcript would have shown them: each
|
|
1002
|
+
* cell's own render, spaced by the body's own formula (no blank between
|
|
1003
|
+
* two blocks of one message). */
|
|
1004
|
+
#foldBody(cell, W, ctx) {
|
|
1005
|
+
const out = [];
|
|
1006
|
+
let prev = null;
|
|
1007
|
+
let prevKind = null;
|
|
1008
|
+
const cells = cell.summary === null ? cell.children : [{ kind: "raw", lines: cell.summary.split("\n"), done: true, wrap: "words" }, ...cell.children];
|
|
1009
|
+
for (const child of cells) {
|
|
1010
|
+
const rows = cellComponent(child).render(W, ctx);
|
|
1011
|
+
if (rows.length === 0)
|
|
1012
|
+
continue;
|
|
1013
|
+
out.push(...(prevKind === "md" && child.kind === "md" ? rows : bodySpacing(prev, rows)));
|
|
1014
|
+
prev = rows;
|
|
1015
|
+
prevKind = child.kind;
|
|
1016
|
+
}
|
|
1017
|
+
return out;
|
|
1018
|
+
}
|
|
962
1019
|
/** The viewer's band: its title, its list, its keys. */
|
|
963
1020
|
#viewerBand(W) {
|
|
964
1021
|
if (this.#viewer === null)
|
|
@@ -1185,6 +1242,13 @@ export class Body {
|
|
|
1185
1242
|
panelOptionRows() {
|
|
1186
1243
|
return this.#panelRowSpan;
|
|
1187
1244
|
}
|
|
1245
|
+
/** B (review of this round): the pick panel's window for the frame last
|
|
1246
|
+
* drawn — the renderer's own value, not a second derivation. `null` when
|
|
1247
|
+
* no pick panel was on screen. */
|
|
1248
|
+
#pickWin = null;
|
|
1249
|
+
visiblePickWindow() {
|
|
1250
|
+
return this.#pickWin;
|
|
1251
|
+
}
|
|
1188
1252
|
/** SIGWINCH: clear the OLD live area (recorded geometry, ED only —
|
|
1189
1253
|
* zero LF, zero \x1b[3J — the shell history untouched), then the
|
|
1190
1254
|
* full-redraw path at the NEW geometry (O(height), zero replay).
|
|
@@ -1513,20 +1577,64 @@ export class Body {
|
|
|
1513
1577
|
return 1 + leadWidth(lead) + st.cursor;
|
|
1514
1578
|
}
|
|
1515
1579
|
/** The old dock's redraw — the editor's onRender target: mark + the
|
|
1516
|
-
* scheduler (16ms coalescing — the old sync draw coalesces the same).
|
|
1517
|
-
|
|
1580
|
+
* scheduler (16ms coalescing — the old sync draw coalesces the same).
|
|
1581
|
+
*
|
|
1582
|
+
* Item 6: a KEY-originated redraw paints on the next tick and takes
|
|
1583
|
+
* the pending trailing frame with it. Keyboard input is the one
|
|
1584
|
+
* latency-sensitive source: the trailing window made every key wait
|
|
1585
|
+
* 16 ms, and 40 ms on Terminal.app — where an IME commit erases the
|
|
1586
|
+
* terminal's marked text at once and the committed characters came
|
|
1587
|
+
* back a frame window later, a visible blink. Stream, tool and
|
|
1588
|
+
* spinner marks keep the trailing window exactly (the conservative
|
|
1589
|
+
* mode's throughput protection is for them). setImmediate, not a
|
|
1590
|
+
* synchronous paint: every mutation one chunk of keys makes still
|
|
1591
|
+
* lands in one frame. */
|
|
1592
|
+
redraw(fromKey = false) {
|
|
1518
1593
|
if (!this.#isActive())
|
|
1519
1594
|
return;
|
|
1520
1595
|
this.#dirty = true;
|
|
1596
|
+
if (fromKey) {
|
|
1597
|
+
this.#scheduleInputFrame();
|
|
1598
|
+
return;
|
|
1599
|
+
}
|
|
1600
|
+
this.#runMarks += 1; // a non-key redraw is not typing: it keeps the window
|
|
1521
1601
|
this.#scheduleFrame();
|
|
1522
1602
|
}
|
|
1523
1603
|
// ---- the scheduler (event-driven; zero heartbeat timers) ----
|
|
1524
1604
|
#mark() {
|
|
1525
1605
|
if (!this.#isActive())
|
|
1526
1606
|
return;
|
|
1607
|
+
this.#runMarks += 1;
|
|
1527
1608
|
this.#dirty = true;
|
|
1528
1609
|
this.#scheduleFrame();
|
|
1529
1610
|
}
|
|
1611
|
+
#scheduleInputFrame() {
|
|
1612
|
+
if (this.#inputFrame !== null)
|
|
1613
|
+
return;
|
|
1614
|
+
const marksAtKey = this.#runMarks;
|
|
1615
|
+
this.#inputFrame = setImmediate(() => {
|
|
1616
|
+
this.#inputFrame = null;
|
|
1617
|
+
// A key whose consequence is run state (an approval verdict, a
|
|
1618
|
+
// submit) mutates the body before this tick. That frame carries
|
|
1619
|
+
// the run, so it keeps the run's trailing window, exactly as
|
|
1620
|
+
// before: painting it now showed an approved write's transient
|
|
1621
|
+
// live card for one frame before its result settled.
|
|
1622
|
+
if (this.#runMarks !== marksAtKey) {
|
|
1623
|
+
if (this.#dirty)
|
|
1624
|
+
this.#scheduleFrame();
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1627
|
+
if (this.#frameTimer !== null) {
|
|
1628
|
+
clearTimeout(this.#frameTimer);
|
|
1629
|
+
this.#frameTimer = null;
|
|
1630
|
+
}
|
|
1631
|
+
if (this.#dirty) {
|
|
1632
|
+
this.#dirty = false;
|
|
1633
|
+
this.render();
|
|
1634
|
+
}
|
|
1635
|
+
});
|
|
1636
|
+
this.#inputFrame.unref();
|
|
1637
|
+
}
|
|
1530
1638
|
#scheduleFrame() {
|
|
1531
1639
|
if (this.#frameTimer !== null)
|
|
1532
1640
|
return;
|
|
@@ -1574,6 +1682,10 @@ export class Body {
|
|
|
1574
1682
|
}
|
|
1575
1683
|
/** Teardown — flush a pending frame, stop the timers. */
|
|
1576
1684
|
close() {
|
|
1685
|
+
if (this.#inputFrame !== null) {
|
|
1686
|
+
clearImmediate(this.#inputFrame);
|
|
1687
|
+
this.#inputFrame = null;
|
|
1688
|
+
}
|
|
1577
1689
|
if (this.#frameTimer !== null) {
|
|
1578
1690
|
clearTimeout(this.#frameTimer);
|
|
1579
1691
|
this.#frameTimer = null;
|
|
@@ -1749,6 +1861,7 @@ export class Body {
|
|
|
1749
1861
|
// at when they clicked, which is this one.
|
|
1750
1862
|
this.#panelRowSpan =
|
|
1751
1863
|
live.panelSpan === null ? null : { top: liveTop + live.panelSpan.offset, count: live.panelSpan.count, first: live.panelSpan.first };
|
|
1864
|
+
this.#pickWin = live.pickWin ?? null;
|
|
1752
1865
|
// 5. the frame bytes.
|
|
1753
1866
|
this.#paint(W, H, liveTop, liveLines, liveRowsTotal, chrome);
|
|
1754
1867
|
}
|
|
@@ -1849,7 +1962,11 @@ export class Body {
|
|
|
1849
1962
|
// fires on it. W22: the queue band sits below the panel — the
|
|
1850
1963
|
// cap shrinks by it.
|
|
1851
1964
|
const frame = panelFrameOf(panel, W, capped);
|
|
1852
|
-
|
|
1965
|
+
// B (review of this round): the pick window THIS frame draws. The size
|
|
1966
|
+
// depends on the budget, so the renderer is the only producer — the
|
|
1967
|
+
// digit keys are handed this value instead of deriving a second one.
|
|
1968
|
+
const pick = panel.pick ?? null;
|
|
1969
|
+
return { lines: frame.rows, panelSpan: frame.options, pickWin: pick === null ? null : pickWindowOf(panel.view, pick.cursor, pick.phase, capped) };
|
|
1853
1970
|
}
|
|
1854
1971
|
return { lines: this.#liveProjection(W, ctx, cap), panelSpan: null };
|
|
1855
1972
|
}
|
|
@@ -2024,6 +2141,9 @@ export class Body {
|
|
|
2024
2141
|
// oldest-first, so the newest cut is at the front.
|
|
2025
2142
|
if (cell.kind === "tool" && lines.some((l) => l.includes("ctrl+o")))
|
|
2026
2143
|
this.#collapsed.unshift(i);
|
|
2144
|
+
// 4c: a fold is read in the viewer and nowhere else.
|
|
2145
|
+
if (cell.kind === "fold")
|
|
2146
|
+
this.#collapsed.unshift(i);
|
|
2027
2147
|
this.#lineCache[i] = lines;
|
|
2028
2148
|
const placed = this.#space(i, i > 0 ? this.#lineCache[i - 1] : null, lines);
|
|
2029
2149
|
this.#committed += 1;
|
|
@@ -2762,6 +2882,11 @@ export class Dock {
|
|
|
2762
2882
|
panelOptionRows() {
|
|
2763
2883
|
return compositorRef?.panelOptionRows() ?? null;
|
|
2764
2884
|
}
|
|
2885
|
+
/** B (review of this round): the pick panel's window for the frame last
|
|
2886
|
+
* drawn — what the digit keys must name. */
|
|
2887
|
+
visiblePickWindow() {
|
|
2888
|
+
return compositorRef?.visiblePickWindow() ?? null;
|
|
2889
|
+
}
|
|
2765
2890
|
setStatus(text, hint) {
|
|
2766
2891
|
compositorRef?.setStatus(text, hint ?? null);
|
|
2767
2892
|
}
|
|
@@ -2848,8 +2973,8 @@ export class Dock {
|
|
|
2848
2973
|
editCol() {
|
|
2849
2974
|
return compositorRef?.editCol() ?? 1;
|
|
2850
2975
|
}
|
|
2851
|
-
redraw() {
|
|
2852
|
-
compositorRef?.redraw();
|
|
2976
|
+
redraw(fromKey = false) {
|
|
2977
|
+
compositorRef?.redraw(fromKey);
|
|
2853
2978
|
}
|
|
2854
2979
|
}
|
|
2855
2980
|
/** The one-compositor registry — the Dock façade routes to it. */
|
package/dist/context-ledger.d.ts
CHANGED
|
@@ -58,6 +58,11 @@ export interface ContextLedger {
|
|
|
58
58
|
* the detail text rides after them, dim, and is cut by the caller's
|
|
59
59
|
* width if it must be.
|
|
60
60
|
*/
|
|
61
|
+
/** 0.40.0 — the ONE fill rule for a ▰▱ meter: `ratio` of `cells`, rounded,
|
|
62
|
+
* clamped to the bar. The `/context` bar and the compacting row's bar both
|
|
63
|
+
* draw through it, so the two cannot fill differently. Plain glyphs; the
|
|
64
|
+
* caller styles them. */
|
|
65
|
+
export declare function meterGlyphs(ratio: number, cells: number): string;
|
|
61
66
|
export declare function contextRows(ledger: ContextLedger): string[];
|
|
62
67
|
/** TUI2-R1 (E) — the honest fallback. The ledger is written PER REQUEST:
|
|
63
68
|
* a session that has not called the model yet has no sidecar, and the
|
package/dist/context-ledger.js
CHANGED
|
@@ -45,15 +45,24 @@ function k(n) {
|
|
|
45
45
|
* the detail text rides after them, dim, and is cut by the caller's
|
|
46
46
|
* width if it must be.
|
|
47
47
|
*/
|
|
48
|
+
/** 0.40.0 — the ONE fill rule for a ▰▱ meter: `ratio` of `cells`, rounded,
|
|
49
|
+
* clamped to the bar. The `/context` bar and the compacting row's bar both
|
|
50
|
+
* draw through it, so the two cannot fill differently. Plain glyphs; the
|
|
51
|
+
* caller styles them. */
|
|
52
|
+
export function meterGlyphs(ratio, cells) {
|
|
53
|
+
const filled = Math.max(0, Math.min(cells, Math.round((Number.isFinite(ratio) ? ratio : 0) * cells)));
|
|
54
|
+
return `${"\u25b0".repeat(filled)}${"\u25b1".repeat(cells - filled)}`;
|
|
55
|
+
}
|
|
48
56
|
export function contextRows(ledger) {
|
|
49
57
|
const p = palette();
|
|
50
58
|
const used = ledger.systemPrompt + ledger.toolTable + ledger.skillsIndex + ledger.envelope + ledger.messages;
|
|
51
59
|
const free = Math.max(0, ledger.window - used);
|
|
52
60
|
const ratio = ledger.window > 0 ? Math.min(1, used / ledger.window) : 1;
|
|
53
|
-
const
|
|
61
|
+
const bar = meterGlyphs(ratio, BAR_CELLS);
|
|
62
|
+
const filled = bar.indexOf("\u25b1") < 0 ? BAR_CELLS : bar.indexOf("\u25b1");
|
|
54
63
|
const rows = [
|
|
55
64
|
`${p.bold}context — ${k(used)} / ${k(ledger.window)} tokens (${Math.round(ratio * 100)}%)${p.reset}`,
|
|
56
|
-
`${p.bold}${
|
|
65
|
+
`${p.bold}${bar.slice(0, filled)}${p.reset}${p.dim}${bar.slice(filled)}${p.reset}`,
|
|
57
66
|
];
|
|
58
67
|
/** One surface row: the label at 14 columns, the count right-aligned
|
|
59
68
|
* at 5, then the dim detail. */
|
package/dist/editor.d.ts
CHANGED
|
@@ -27,13 +27,20 @@ import { type PanelState, type PanelVerdict, type PanelView, type SaferAnswer }
|
|
|
27
27
|
import { type AtItem, type AtMatch } from "./at-picker.js";
|
|
28
28
|
import { type SessionCardView, type SessionPickState } from "./session-picker.js";
|
|
29
29
|
/**
|
|
30
|
-
* TUI2-R3v2
|
|
30
|
+
* TUI2-R3v2 ②, corrected by the owner's ruling of 2026-09-21 (finding
|
|
31
|
+
* DC-56) — the mouse-mode bytes, stated once.
|
|
31
32
|
*
|
|
32
33
|
* ?1000 is the button-event report and ?1006 is the SGR encoding that
|
|
33
34
|
* makes it parseable past column 95 (the legacy X10 encoding packs the
|
|
34
|
-
* coordinate into one byte and simply breaks on a wide terminal).
|
|
35
|
-
*
|
|
36
|
-
*
|
|
35
|
+
* coordinate into one byte and simply breaks on a wide terminal). kiso
|
|
36
|
+
* still RESETS them — on entry (a previous process may have died with
|
|
37
|
+
* reporting left on) and on exit — but it no longer ENABLES them, because
|
|
38
|
+
* a terminal that is reporting stops scrolling its own scrollback: a
|
|
39
|
+
* surface that takes the mouse takes the history with it. kiso's
|
|
40
|
+
* transcript lives in that scrollback by design, and the first version of
|
|
41
|
+
* this feature locked it exactly where a person most wants to read back —
|
|
42
|
+
* under a blocking question. The history wins: no surface claims the
|
|
43
|
+
* mouse, and every surface keeps its keys.
|
|
37
44
|
*/
|
|
38
45
|
/**
|
|
39
46
|
* R5 — the viewer's key table, as a pure function of the input chunk.
|
|
@@ -45,7 +52,6 @@ import { type SessionCardView, type SessionPickState } from "./session-picker.js
|
|
|
45
52
|
* different route.
|
|
46
53
|
*/
|
|
47
54
|
export declare function viewerCommand(text: string): "up" | "down" | "toggle" | "all" | "pageUp" | "pageDown" | "home" | "end" | "close" | null;
|
|
48
|
-
export declare const MOUSE_ON = "\u001B[?1000h\u001B[?1006h";
|
|
49
55
|
export declare const MOUSE_OFF = "\u001B[?1000l\u001B[?1006l";
|
|
50
56
|
export declare const PROMPT = "\u258C ";
|
|
51
57
|
export declare const PROMPT_WIDTH: number;
|
|
@@ -65,7 +71,7 @@ export declare const MENU_ITEMS: readonly MenuItem[];
|
|
|
65
71
|
export declare class Editor {
|
|
66
72
|
#private;
|
|
67
73
|
readonly closed: Promise<void>;
|
|
68
|
-
constructor(onRender: () => void);
|
|
74
|
+
constructor(onRender: (fromKey: boolean) => void);
|
|
69
75
|
/**
|
|
70
76
|
* DC-7 — the terminal answering a question kiso asked it.
|
|
71
77
|
*
|
|
@@ -153,6 +159,9 @@ export declare class Editor {
|
|
|
153
159
|
items: readonly MenuItem[];
|
|
154
160
|
selected: number;
|
|
155
161
|
} | null;
|
|
162
|
+
/** 0.40.1 — bind the menu's extra entries (the CLI binds the installed
|
|
163
|
+
* skills). A function, read per keystroke, never a snapshot. */
|
|
164
|
+
bindMenuExtras(extras: () => readonly MenuItem[]): void;
|
|
156
165
|
/** KC3 §3 — bind the file source. The tui owns no file list and
|
|
157
166
|
* never touches a disk (input is data, output is bytes): the CLI
|
|
158
167
|
* feeds the paths, and until it does, the picker cannot open at
|
|
@@ -168,7 +177,7 @@ export declare class Editor {
|
|
|
168
177
|
capped: boolean;
|
|
169
178
|
} | null;
|
|
170
179
|
/** Open the picker on a bound card source (PickInput, S5). */
|
|
171
|
-
beginPick(cards: () => readonly SessionCardView[], onPick: (id: string | null) => void): void;
|
|
180
|
+
beginPick(cards: () => readonly SessionCardView[], onPick: (id: string | null) => void, here?: string): void;
|
|
172
181
|
pickState(): SessionPickState | null;
|
|
173
182
|
/** One-shot question mode: the NEXT submit answers, not a turn. */
|
|
174
183
|
question(_query: string, cb: (answer: string) => void): void;
|
|
@@ -218,6 +227,12 @@ export declare class Editor {
|
|
|
218
227
|
count: number;
|
|
219
228
|
first?: number;
|
|
220
229
|
} | null) | null): void;
|
|
230
|
+
/** B: the pick window the last frame drew — the Dock's value, not the
|
|
231
|
+
* editor's guess. Same pull-on-demand shape as `bindPanelRows`. */
|
|
232
|
+
bindPickWindow(fn: (() => {
|
|
233
|
+
first: number;
|
|
234
|
+
size: number;
|
|
235
|
+
} | null) | null): void;
|
|
221
236
|
/** R3a — cross-session input history: seed the recall buffer and
|
|
222
237
|
* register the append sink. The cap and the adjacent-duplicate
|
|
223
238
|
* collapse are unchanged; the seed takes the TAIL of what the CLI
|