@vincemakes/kiso-tui 0.8.0 → 0.10.0
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/ask-panel.d.ts +8 -7
- package/dist/ask-panel.js +31 -12
- package/dist/at-picker.d.ts +4 -0
- package/dist/at-picker.js +41 -8
- package/dist/compositor.d.ts +28 -10
- package/dist/compositor.js +405 -53
- package/dist/context-ledger.d.ts +66 -0
- package/dist/context-ledger.js +87 -0
- package/dist/editor.d.ts +11 -1
- package/dist/editor.js +301 -17
- package/dist/index.d.ts +7 -5
- package/dist/index.js +16 -5
- package/dist/session-picker.d.ts +113 -0
- package/dist/session-picker.js +249 -0
- package/dist/status.d.ts +32 -2
- package/dist/status.js +24 -3
- package/package.json +2 -2
package/dist/ask-panel.d.ts
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* durability would need a new durable mechanism), no "chat about this"
|
|
28
28
|
* hand-off, no timeout and no countdown.
|
|
29
29
|
*/
|
|
30
|
-
import { type AskAnswer, type AskOption, type AskQuestion, type AskResult, type AskRuntime, type AskSpec, type PanelPhase, type PanelSel, type PanelState, type PanelView } from "./approval-panel.js";
|
|
30
|
+
import { type AskAnswer, type AskOption, type AskQuestion, type AskResult, type AskRuntime, type AskSpec, type PanelPhase, type PanelSel, type PanelState, type PanelView, type PickRuntime } from "./approval-panel.js";
|
|
31
31
|
/** The schema's own bounds — the registry refuses anything outside them
|
|
32
32
|
* (extensions/ask validates; these are the numbers it validates to). */
|
|
33
33
|
export declare const ASK_MAX_QUESTIONS = 4;
|
|
@@ -77,13 +77,14 @@ export declare function askStatus(view: PanelView, state: AskRuntime): string;
|
|
|
77
77
|
/** The input row's lead: the digit lead while picking, the typing lead
|
|
78
78
|
* in the custom phase (the rule-input phase's shape, reused). */
|
|
79
79
|
export declare function askLeadPlain(state: AskRuntime): string;
|
|
80
|
-
export declare function panelBlockRows(view: PanelView, phase: PanelPhase, sel: PanelSel, W: number, maxRows: number, ask?: AskRuntime): string[];
|
|
81
|
-
export declare function panelLead(view: PanelView, phase: PanelPhase, sel: PanelSel, ask?: AskRuntime): string;
|
|
82
|
-
export declare function panelLeadPlain(view: PanelView, phase: PanelPhase, sel: PanelSel, ask?: AskRuntime): string;
|
|
83
|
-
export declare function panelStatus(view: PanelView, phase: PanelPhase, sel: PanelSel, ask?: AskRuntime): string;
|
|
84
|
-
export declare function panelAffordance(view: PanelView, phase: PanelPhase, sel: PanelSel, ask?: AskRuntime): string;
|
|
80
|
+
export declare function panelBlockRows(view: PanelView, phase: PanelPhase, sel: PanelSel, W: number, maxRows: number, ask?: AskRuntime, pick?: PickRuntime): string[];
|
|
81
|
+
export declare function panelLead(view: PanelView, phase: PanelPhase, sel: PanelSel, ask?: AskRuntime, pick?: PickRuntime): string;
|
|
82
|
+
export declare function panelLeadPlain(view: PanelView, phase: PanelPhase, sel: PanelSel, ask?: AskRuntime, pick?: PickRuntime): string;
|
|
83
|
+
export declare function panelStatus(view: PanelView, phase: PanelPhase, sel: PanelSel, ask?: AskRuntime, pick?: PickRuntime): string;
|
|
84
|
+
export declare function panelAffordance(view: PanelView, phase: PanelPhase, sel: PanelSel, ask?: AskRuntime, pick?: PickRuntime): string;
|
|
85
85
|
/** The whole panel state in one call — the compositor's four reads share
|
|
86
|
-
* one source, so an ask can never render half as an approval
|
|
86
|
+
* one source, so an ask can never render half as an approval (TUI2-R2
|
|
87
|
+
* ④: nor a pick as either). */
|
|
87
88
|
export declare const panelRowsOf: (s: PanelState, W: number, maxRows: number) => string[];
|
|
88
89
|
export declare const panelLeadOf: (s: PanelState) => string;
|
|
89
90
|
export declare const panelStatusOf: (s: PanelState) => string;
|
package/dist/ask-panel.js
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* durability would need a new durable mechanism), no "chat about this"
|
|
28
28
|
* hand-off, no timeout and no countdown.
|
|
29
29
|
*/
|
|
30
|
-
import { panelAffordance as basePanelAffordance, panelBlockRows as basePanelBlockRows, panelLead as basePanelLead, panelLeadPlain as basePanelLeadPlain, panelStatus as basePanelStatus, } from "./approval-panel.js";
|
|
30
|
+
import { panelAffordance as basePanelAffordance, panelBlockRows as basePanelBlockRows, panelLead as basePanelLead, panelLeadPlain as basePanelLeadPlain, panelStatus as basePanelStatus, pickAffordance, pickBlockRows, pickLead, pickLeadPlain, pickStatus, } from "./approval-panel.js";
|
|
31
31
|
import { cutLine } from "@vincemakes/kiso-tui-cells/components";
|
|
32
32
|
import { escapeTerminal, palette } from "./render.js";
|
|
33
33
|
/** The schema's own bounds — the registry refuses anything outside them
|
|
@@ -197,7 +197,15 @@ export function askBlockRows(view, state, W, maxRows) {
|
|
|
197
197
|
rows.push(...body);
|
|
198
198
|
}
|
|
199
199
|
rows.push(`${gutter}${p.dim}${askAffordance(state)}${p.reset}`);
|
|
200
|
-
|
|
200
|
+
// TUI2-R1.5 11 (VD-13), shared with the approval panel: a real bottom RULE, in the block's own edge
|
|
201
|
+
// vocabulary — the same box-drawing run its divider already uses —
|
|
202
|
+
// anchored at the gutter column. It used to be `\u2514 `: a two-cell stub
|
|
203
|
+
// floating at column 1, with no rule running from it and no corner
|
|
204
|
+
// above it to answer. Worse, `\u2514 ` is the cut-notice prefix everywhere
|
|
205
|
+
// else in the product, so a CAPPED panel emitted two elbow rows in a
|
|
206
|
+
// row meaning entirely different things. The rule reads as an edge,
|
|
207
|
+
// and the cut notice above it reads as a notice.
|
|
208
|
+
rows.push(`${p.dim}\u2514${"\u2500".repeat(Math.max(0, W - 1))}${p.reset}`);
|
|
201
209
|
return rows;
|
|
202
210
|
}
|
|
203
211
|
/** The status row's right-hand hint — the phase's keys. */
|
|
@@ -217,38 +225,49 @@ export function askLeadPlain(state) {
|
|
|
217
225
|
return state.phase === "custom" ? "your answer: " : "1-4> ";
|
|
218
226
|
}
|
|
219
227
|
// ── the dispatchers: the panel slot, with the ask branch folded in ────
|
|
220
|
-
export function panelBlockRows(view, phase, sel, W, maxRows, ask) {
|
|
228
|
+
export function panelBlockRows(view, phase, sel, W, maxRows, ask, pick) {
|
|
229
|
+
if (view.pick !== undefined && pick !== undefined)
|
|
230
|
+
return pickBlockRows(view, pick, W, maxRows);
|
|
221
231
|
if (view.ask !== undefined && ask !== undefined)
|
|
222
232
|
return askBlockRows(view, ask, W, maxRows);
|
|
223
233
|
return basePanelBlockRows(view, phase, sel, W, maxRows);
|
|
224
234
|
}
|
|
225
|
-
export function panelLead(view, phase, sel, ask) {
|
|
235
|
+
export function panelLead(view, phase, sel, ask, pick) {
|
|
226
236
|
const p = palette();
|
|
237
|
+
if (view.pick !== undefined && pick !== undefined)
|
|
238
|
+
return pickLead(view, pick);
|
|
227
239
|
if (view.ask !== undefined && ask !== undefined)
|
|
228
240
|
return `${p.bold}${askLeadPlain(ask)}${p.reset}`;
|
|
229
241
|
return basePanelLead(view, phase, sel);
|
|
230
242
|
}
|
|
231
|
-
export function panelLeadPlain(view, phase, sel, ask) {
|
|
243
|
+
export function panelLeadPlain(view, phase, sel, ask, pick) {
|
|
244
|
+
if (view.pick !== undefined && pick !== undefined)
|
|
245
|
+
return pickLeadPlain(view, pick);
|
|
232
246
|
if (view.ask !== undefined && ask !== undefined)
|
|
233
247
|
return askLeadPlain(ask);
|
|
234
248
|
return basePanelLeadPlain(view, phase, sel);
|
|
235
249
|
}
|
|
236
|
-
export function panelStatus(view, phase, sel, ask) {
|
|
250
|
+
export function panelStatus(view, phase, sel, ask, pick) {
|
|
251
|
+
if (view.pick !== undefined && pick !== undefined)
|
|
252
|
+
return pickStatus(view);
|
|
237
253
|
if (view.ask !== undefined && ask !== undefined)
|
|
238
254
|
return askStatus(view, ask);
|
|
239
255
|
return basePanelStatus(view, phase, sel);
|
|
240
256
|
}
|
|
241
|
-
export function panelAffordance(view, phase, sel, ask) {
|
|
257
|
+
export function panelAffordance(view, phase, sel, ask, pick) {
|
|
258
|
+
if (view.pick !== undefined && pick !== undefined)
|
|
259
|
+
return pickAffordance(pick);
|
|
242
260
|
if (view.ask !== undefined && ask !== undefined)
|
|
243
261
|
return askAffordance(ask);
|
|
244
262
|
return basePanelAffordance(view, phase, sel);
|
|
245
263
|
}
|
|
246
264
|
/** The whole panel state in one call — the compositor's four reads share
|
|
247
|
-
* one source, so an ask can never render half as an approval
|
|
248
|
-
|
|
249
|
-
export const
|
|
250
|
-
export const
|
|
251
|
-
export const
|
|
265
|
+
* one source, so an ask can never render half as an approval (TUI2-R2
|
|
266
|
+
* ④: nor a pick as either). */
|
|
267
|
+
export const panelRowsOf = (s, W, maxRows) => panelBlockRows(s.view, s.phase, s.sel, W, maxRows, s.ask, s.pick);
|
|
268
|
+
export const panelLeadOf = (s) => panelLead(s.view, s.phase, s.sel, s.ask, s.pick);
|
|
269
|
+
export const panelStatusOf = (s) => panelStatus(s.view, s.phase, s.sel, s.ask, s.pick);
|
|
270
|
+
export const panelAffordanceOf = (s) => panelAffordance(s.view, s.phase, s.sel, s.ask, s.pick);
|
|
252
271
|
// ── the view: what the human reads when the model asks ────────────────
|
|
253
272
|
/** The ask's PanelView. The dock-less fallback question is HONEST: a
|
|
254
273
|
* terminal without a panel cannot walk options, so it says the ask is
|
package/dist/at-picker.d.ts
CHANGED
|
@@ -157,3 +157,7 @@ export declare function atPanelRows(state: {
|
|
|
157
157
|
selected: number;
|
|
158
158
|
capped: boolean;
|
|
159
159
|
}, W: number): string[];
|
|
160
|
+
/** TUI2-R1.5 ⑦(b) — the one-row dim header that turns a band into a
|
|
161
|
+
* surface. Shared by the @ picker and the / menu so the two read the
|
|
162
|
+
* same way. */
|
|
163
|
+
export declare function bandHeader(label: string, W: number): string;
|
package/dist/at-picker.js
CHANGED
|
@@ -182,20 +182,41 @@ function splitPath(path) {
|
|
|
182
182
|
export function atRow(match, selected, W) {
|
|
183
183
|
const p = palette();
|
|
184
184
|
const { dir, name } = splitPath(escapeTerminal(match.path));
|
|
185
|
-
const lead = selected ? `${p.rv}→ ${p.rvEnd}` : " ";
|
|
186
185
|
// the name's own matched positions, mapped out of the full path
|
|
187
186
|
const marks = new Set(match.hit.filter((i) => i >= dir.length).map((i) => i - dir.length));
|
|
188
|
-
//
|
|
189
|
-
|
|
187
|
+
// TUI2-R1.5 ⑧ (VD-9): the directory rides NEXT TO the name it
|
|
188
|
+
// qualifies. It used to be right-aligned to the band's far edge, which
|
|
189
|
+
// on a 100-column terminal put `src/` some eighty columns from the
|
|
190
|
+
// `parser.ts` it belongs to: the eye had to cross the whole row to
|
|
191
|
+
// learn which parser.ts this was, and the column read as a second list.
|
|
192
|
+
// Adjacent and dim, it is what it always meant to be — a qualifier.
|
|
193
|
+
// the name is the flexible column and the qualifier gives way first: a
|
|
194
|
+
// narrow window keeps the thing being aimed at.
|
|
195
|
+
const room = Math.max(1, W - 2);
|
|
196
|
+
const suffix = dir === "" ? "" : widthCut(` — ${dir}`, Math.max(0, room - 4));
|
|
197
|
+
const nameRoom = Math.max(1, room - visibleWidth(suffix));
|
|
190
198
|
const shownName = widthCut(name, nameRoom);
|
|
191
199
|
let painted = "";
|
|
192
200
|
for (let i = 0; i < shownName.length; i += 1) {
|
|
193
201
|
painted += marks.has(i) ? `${p.bold}${shownName[i]}${p.reset}` : shownName[i];
|
|
194
202
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
203
|
+
const text = `${painted}${suffix === "" ? "" : `${p.dim}${suffix}${p.reset}`}`;
|
|
204
|
+
const width = visibleWidth(shownName) + visibleWidth(suffix);
|
|
205
|
+
if (!selected)
|
|
206
|
+
return ` ${text}`;
|
|
207
|
+
// TUI2-R1.5 ⑧ (VD-9): the selection is a FULL-ROW bar — the W16 chip
|
|
208
|
+
// mechanism, which the user chip and the turn fold already use. A
|
|
209
|
+
// two-cell `→ ` marker on the inverse band is one character of
|
|
210
|
+
// highlight in an eighty-column row, and the walkthrough could barely
|
|
211
|
+
// find it. The bar spans the row's whole width so the selection is
|
|
212
|
+
// visible from anywhere on the line. Mono discipline: reverse video,
|
|
213
|
+
// no new colours.
|
|
214
|
+
//
|
|
215
|
+
// The inner spans close with rvEnd (SGR 27), never SGR 0 — a reset
|
|
216
|
+
// inside the bar would punch a hole in it. `painted`'s bold marks and
|
|
217
|
+
// the dim suffix both end in SGR 0, so the bar is re-opened after each.
|
|
218
|
+
const inner = `${painted.replaceAll(p.reset, `${p.reset}${p.rv}`)}${suffix === "" ? "" : `${p.dim}${suffix}${p.reset}${p.rv}`}`;
|
|
219
|
+
return `${p.rv} ${inner}${" ".repeat(Math.max(0, W - width - 2))} ${p.rvEnd}`;
|
|
199
220
|
}
|
|
200
221
|
/**
|
|
201
222
|
* KC3 §4 — the counter row: `(n/total)`, where n is the 1-based
|
|
@@ -220,9 +241,21 @@ export function atCounterRow(selected, total, capped, W) {
|
|
|
220
241
|
*/
|
|
221
242
|
export function atPanelRows(state, W) {
|
|
222
243
|
const { first, count } = atWindow(state.matches.length, state.selected);
|
|
223
|
-
|
|
244
|
+
// TUI2-R1.5 ⑦(b) (VD-8): the band NAMES itself. It renders frameless
|
|
245
|
+
// directly above the composer, so with scrollback behind it there was
|
|
246
|
+
// nothing to say where the surface began — the rows read as more
|
|
247
|
+
// history. One dim row is enough to make it UI. Mono discipline: dim
|
|
248
|
+
// text, no rule, no colour.
|
|
249
|
+
const rows = [bandHeader("files", W)];
|
|
224
250
|
for (let i = first; i < first + count; i += 1)
|
|
225
251
|
rows.push(atRow(state.matches[i], i === state.selected, W));
|
|
226
252
|
rows.push(atCounterRow(state.selected, state.matches.length, state.capped, W));
|
|
227
253
|
return rows;
|
|
228
254
|
}
|
|
255
|
+
/** TUI2-R1.5 ⑦(b) — the one-row dim header that turns a band into a
|
|
256
|
+
* surface. Shared by the @ picker and the / menu so the two read the
|
|
257
|
+
* same way. */
|
|
258
|
+
export function bandHeader(label, W) {
|
|
259
|
+
const p = palette();
|
|
260
|
+
return `${p.dim}${widthCut(label, Math.max(1, W))}${p.reset}`;
|
|
261
|
+
}
|
package/dist/compositor.d.ts
CHANGED
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
import { type MenuItem } from "./editor.js";
|
|
46
46
|
import type { PanelState } from "./approval-panel.js";
|
|
47
47
|
import { type AtMatch } from "./at-picker.js";
|
|
48
|
+
import { type SessionPickState } from "./session-picker.js";
|
|
48
49
|
/** KC3 §4 — the @ picker's bound state (the editor's atState()). */
|
|
49
50
|
export interface AtPanelState {
|
|
50
51
|
readonly matches: readonly AtMatch[];
|
|
@@ -109,6 +110,20 @@ export declare class Body {
|
|
|
109
110
|
* outcome. */
|
|
110
111
|
toolVerdict(callId: string, decision: "approved" | "denied", decidedBy?: string, reason?: string): void;
|
|
111
112
|
toolRunning(callId: string): void;
|
|
113
|
+
/**
|
|
114
|
+
* TUI2-R1 (C) — the RUNNING call's observed output.
|
|
115
|
+
*
|
|
116
|
+
* The CLI tails the shell tool's progress sidecar and hands what it
|
|
117
|
+
* read to the cell. Deliberately narrow: only a cell that is still
|
|
118
|
+
* RUNNING accepts it, so an observation can never overwrite a real
|
|
119
|
+
* result, and an unchanged read costs no frame at all (a poller
|
|
120
|
+
* fires far more often than the output changes).
|
|
121
|
+
*
|
|
122
|
+
* This adds no event and no durable state. The text lands in the
|
|
123
|
+
* cell's live rendering and is replaced wholesale by the tool's own
|
|
124
|
+
* result at settle — which is the only text anything else ever reads.
|
|
125
|
+
*/
|
|
126
|
+
toolProgress(callId: string, text: string): void;
|
|
112
127
|
toolSucceeded(callId: string): void;
|
|
113
128
|
toolFailed(callId: string, error: string): void;
|
|
114
129
|
toolResult(callId: string, result: {
|
|
@@ -152,7 +167,7 @@ export declare class Body {
|
|
|
152
167
|
* frame. The inactive path keeps the historical bytes (no resume —
|
|
153
168
|
* the pipe contract). */
|
|
154
169
|
banner(version: string, extensionsText: string, resume?: ResumeMeta[]): void;
|
|
155
|
-
raw(lines: string[]): void;
|
|
170
|
+
raw(lines: string[], wrap?: "words"): void;
|
|
156
171
|
/** The last COMPLETE thinking block, for /think. */
|
|
157
172
|
lastThinking(): string | null;
|
|
158
173
|
/** The last completed tool call, for /last. */
|
|
@@ -164,15 +179,6 @@ export declare class Body {
|
|
|
164
179
|
isError: boolean;
|
|
165
180
|
};
|
|
166
181
|
} | null;
|
|
167
|
-
/** W15 — the expand key's target (ctrl+r). A cell still in the LIVE
|
|
168
|
-
* region (the newest live tool) TOGGLES in place — the compositor
|
|
169
|
-
* owns those rows and redraws them (the body flips to the full
|
|
170
|
-
* form, no cap). A committed cell can never toggle — history is
|
|
171
|
-
* never rewritten (ADR-0046) — so the key APPENDS a fresh expanded
|
|
172
|
-
* block at the bottom instead, the /last idiom aimed at a chosen
|
|
173
|
-
* cell: the pointer cycles the collapsed history, newest first, and
|
|
174
|
-
* the header names the target ("N turns back" — the user cells
|
|
175
|
-
* after it), so every press tells the user what they got. */
|
|
176
182
|
expandNext(): {
|
|
177
183
|
kind: "toggled";
|
|
178
184
|
} | {
|
|
@@ -208,6 +214,8 @@ export declare class Body {
|
|
|
208
214
|
bindInput(state: () => InputState, prompt: string): void;
|
|
209
215
|
/** Bind the editor's slash-command menu state — the MenuSelect slot
|
|
210
216
|
* occupant (the menu replaces the editor's view while open). */
|
|
217
|
+
/** TUI2-R1 (D): bind the editor's keys-sheet flag. */
|
|
218
|
+
bindSheet(state: () => boolean): void;
|
|
211
219
|
bindMenu(state: () => {
|
|
212
220
|
items: readonly MenuItem[];
|
|
213
221
|
selected: number;
|
|
@@ -216,6 +224,9 @@ export declare class Body {
|
|
|
216
224
|
* band — see #menuRows for why that is a decision and not a
|
|
217
225
|
* shortcut. */
|
|
218
226
|
bindAt(state: () => AtPanelState | null): void;
|
|
227
|
+
/** TUI2-R2 ②: bind the editor's session picker — the band's third
|
|
228
|
+
* occupant (see #menuRows for why they share one). */
|
|
229
|
+
bindPick(state: () => SessionPickState | null): void;
|
|
219
230
|
/** Bind the pending-turn queue — the CLI's live slots (chat.ts):
|
|
220
231
|
* the chips render in the menu-rows family, the live caps shrink
|
|
221
232
|
* by their rows, and the +N queued hint rides the status row. */
|
|
@@ -252,6 +263,9 @@ export declare class Dock {
|
|
|
252
263
|
* occupant (the panel replaces the live region + the input lead
|
|
253
264
|
* while up; the old ApprovalPrompt's question slot retires). */
|
|
254
265
|
bindApproval(state: () => PanelState | null): void;
|
|
266
|
+
/** TUI2-R1 (D): bind the editor's keys-sheet flag — the slot read for
|
|
267
|
+
* the ? overlay (the menu/picker binding pattern). */
|
|
268
|
+
bindSheet(state: () => boolean): void;
|
|
255
269
|
bindInput(state: () => InputState, prompt: string): void;
|
|
256
270
|
bindMenu(state: () => {
|
|
257
271
|
items: readonly MenuItem[];
|
|
@@ -261,6 +275,10 @@ export declare class Dock {
|
|
|
261
275
|
* menu (see Body#menuRows). Unbound, the picker cannot render, and
|
|
262
276
|
* every frame is byte-identical to before the round. */
|
|
263
277
|
bindAt(state: () => AtPanelState | null): void;
|
|
278
|
+
/** TUI2-R2 ②: bind the editor's session picker — the band's third
|
|
279
|
+
* occupant. Unbound (every path but bare `kiso resume`), the picker
|
|
280
|
+
* cannot render and every frame is byte-identical to before. */
|
|
281
|
+
bindPick(state: () => SessionPickState | null): void;
|
|
264
282
|
/** W22: bind the pending-turn queue — the chips + the +N queued
|
|
265
283
|
* hint (the CLI binds it from chat(); the editor's pop keys ride
|
|
266
284
|
* the LineInput's own bindQueue). */
|