@vincemakes/kiso-tui-cells 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/approval-panel.d.ts +75 -0
- package/dist/approval-panel.js +125 -12
- package/dist/components.d.ts +55 -6
- package/dist/components.js +466 -23
- package/dist/diff.d.ts +24 -4
- package/dist/diff.js +30 -16
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -1
- package/dist/render.d.ts +8 -0
- package/dist/render.js +2 -2
- package/dist/strings.d.ts +57 -0
- package/dist/strings.js +171 -0
- package/dist/width.d.ts +16 -6
- package/dist/width.js +78 -6
- package/package.json +1 -1
package/dist/approval-panel.d.ts
CHANGED
|
@@ -75,6 +75,38 @@ export interface AskRuntime {
|
|
|
75
75
|
readonly custom: readonly (string | null)[];
|
|
76
76
|
readonly phase: "options" | "custom";
|
|
77
77
|
}
|
|
78
|
+
/** One thing that can be picked: what it is, and what qualifies it. */
|
|
79
|
+
export interface PickOption {
|
|
80
|
+
readonly label: string;
|
|
81
|
+
/** the dim qualifier ("profile: ds \u00b7 current") \u2014 what tells two
|
|
82
|
+
* similar rows apart */
|
|
83
|
+
readonly note?: string;
|
|
84
|
+
}
|
|
85
|
+
/** The whole pick: the header sentence, the options, the free-text
|
|
86
|
+
* escape hatch, and the honest empty state. */
|
|
87
|
+
export interface PickSpec {
|
|
88
|
+
readonly header: string;
|
|
89
|
+
readonly options: readonly PickOption[];
|
|
90
|
+
/** the `t` row \u2014 typing it directly is always available, because a
|
|
91
|
+
* list of profiles is never the list of models that exist */
|
|
92
|
+
readonly typeHint: string;
|
|
93
|
+
/** shown INSTEAD of the options when there are none. The copy is the
|
|
94
|
+
* caller's and is reproduced verbatim. */
|
|
95
|
+
readonly emptyNote?: string;
|
|
96
|
+
}
|
|
97
|
+
/** The pick panel's runtime state \u2014 the editor owns it, the compositor
|
|
98
|
+
* reads it (the AskRuntime precedent, two fields instead of five). */
|
|
99
|
+
export interface PickRuntime {
|
|
100
|
+
readonly cursor: number;
|
|
101
|
+
readonly phase: "options" | "custom";
|
|
102
|
+
}
|
|
103
|
+
/** What was picked: a listed option by INDEX (never a label the caller
|
|
104
|
+
* would have to re-match against its own list), or typed text. */
|
|
105
|
+
export type PickResult = {
|
|
106
|
+
readonly index: number;
|
|
107
|
+
} | {
|
|
108
|
+
readonly custom: string;
|
|
109
|
+
};
|
|
78
110
|
/** The ALWAYS-verbose args (the panel's body): the untruncated diff
|
|
79
111
|
* (edit/write), or the full text (shell = the command line, other =
|
|
80
112
|
* the pretty-printed JSON). The CLI composes them UNTRUNCATED — the
|
|
@@ -116,6 +148,9 @@ export interface PanelView {
|
|
|
116
148
|
* panel renders the ask block and the editor routes the ask keys;
|
|
117
149
|
* absent = the approval/simple panel, unchanged. */
|
|
118
150
|
readonly ask?: AskSpec;
|
|
151
|
+
/** TUI2-R2 \u2463: the options, when this view is a PICK. Same contract
|
|
152
|
+
* as `ask`, one payload over. */
|
|
153
|
+
readonly pick?: PickSpec;
|
|
119
154
|
}
|
|
120
155
|
export type PanelVerdict = {
|
|
121
156
|
readonly action: "allow";
|
|
@@ -135,6 +170,13 @@ export type PanelVerdict = {
|
|
|
135
170
|
| {
|
|
136
171
|
readonly action: "answers";
|
|
137
172
|
readonly result: AskResult;
|
|
173
|
+
}
|
|
174
|
+
/** TUI2-R2 \u2463: the pick's verdict \u2014 the chosen index or the typed
|
|
175
|
+
* text. Only pick views ever produce it, so the approval path's
|
|
176
|
+
* switch is untouched. */
|
|
177
|
+
| {
|
|
178
|
+
readonly action: "picked";
|
|
179
|
+
readonly result: PickResult;
|
|
138
180
|
};
|
|
139
181
|
/** The bound panel state the compositor reads — the editor owns the
|
|
140
182
|
* phase/selection state machine and the key routing; the compositor
|
|
@@ -145,6 +187,8 @@ export interface PanelState {
|
|
|
145
187
|
readonly sel: PanelSel;
|
|
146
188
|
/** KC3.5: the ask's walk — present exactly when `view.ask` is. */
|
|
147
189
|
readonly ask?: AskRuntime;
|
|
190
|
+
/** TUI2-R2 \u2463: the pick's walk — present exactly when `view.pick` is. */
|
|
191
|
+
readonly pick?: PickRuntime;
|
|
148
192
|
}
|
|
149
193
|
/** The block's rows — EXACTLY the preview's frame shape, the gutter at
|
|
150
194
|
* the left edge (the preview's two-space mock indent is its own
|
|
@@ -168,3 +212,34 @@ export declare function panelStatus(view: PanelView, phase: PanelPhase, sel: Pan
|
|
|
168
212
|
* phase's keys. The approval flavor gains the tab-amend path; the
|
|
169
213
|
* simple flavor (the trust/uncertain gates) never does. */
|
|
170
214
|
export declare function panelAffordance(view: PanelView, phase: PanelPhase, sel: PanelSel): string;
|
|
215
|
+
/**
|
|
216
|
+
* The pick block's rows — the prototype's C frame.
|
|
217
|
+
*
|
|
218
|
+
* The header says what is in effect right now, because the first
|
|
219
|
+
* question anyone opening this panel has is "what am I on?". The
|
|
220
|
+
* options are numbered from 1 and the number IS the key. The `t` row is
|
|
221
|
+
* last and always present: a profile list is a convenience, never the
|
|
222
|
+
* set of models that exist, and a picker that can only offer what
|
|
223
|
+
* someone remembered to configure is a smaller product than the one it
|
|
224
|
+
* replaced.
|
|
225
|
+
*
|
|
226
|
+
* Single-row discipline: every row CUTS, never folds — the block's
|
|
227
|
+
* height is its row count (the W20 rule the #checked throw demands).
|
|
228
|
+
*/
|
|
229
|
+
export declare function pickBlockRows(view: PanelView, state: PickRuntime, W: number, maxRows: number): string[];
|
|
230
|
+
/** The digits are the keys, so the list the panel offers is bounded by
|
|
231
|
+
* the digits there are. Beyond it, `/model <name>` still takes any
|
|
232
|
+
* profile — the panel says so rather than paginating. */
|
|
233
|
+
export declare const PICK_MAX = 9;
|
|
234
|
+
/** The input row's lead: the digit range while picking, the named
|
|
235
|
+
* prompt while typing one out. */
|
|
236
|
+
export declare function pickLeadPlain(view: PanelView, state: PickRuntime): string;
|
|
237
|
+
export declare function pickLead(view: PanelView, state: PickRuntime): string;
|
|
238
|
+
/** The status row's left text \u2014 the CALLER's, because only the caller
|
|
239
|
+
* knows whether a run is paused behind this panel. */
|
|
240
|
+
export declare function pickStatus(view: PanelView): string;
|
|
241
|
+
export declare function pickAffordance(state: PickRuntime): string;
|
|
242
|
+
/** Compose a pick view. The flavor/name/title/args fields exist for the
|
|
243
|
+
* approval path and are given inert values here \u2014 the pick block
|
|
244
|
+
* reads none of them. */
|
|
245
|
+
export declare function modelPickView(spec: PickSpec, statusText: string): PanelView;
|
package/dist/approval-panel.js
CHANGED
|
@@ -26,6 +26,9 @@
|
|
|
26
26
|
*/
|
|
27
27
|
import { displayWidth } from "./width.js";
|
|
28
28
|
import { cutLine, diffBody, gutterFold, visibleWidth, widthCut } from "./components.js";
|
|
29
|
+
// TUI2-R2pre ④: strings.js takes only a TYPE from this module, so the
|
|
30
|
+
// import is erased at compile time and no runtime cycle exists.
|
|
31
|
+
import { displayVerb } from "./strings.js";
|
|
29
32
|
import { escapeTerminal, palette } from "./render.js";
|
|
30
33
|
/** The rule line's text — the why-asked line (the R3 chain): the tool
|
|
31
34
|
* name, the first non-abstain speaker, the fix hint (the §3.5 table,
|
|
@@ -37,7 +40,11 @@ function panelRuleText(view) {
|
|
|
37
40
|
if (view.ruleOverride !== undefined)
|
|
38
41
|
return escapeTerminal(view.ruleOverride);
|
|
39
42
|
const hint = view.hint;
|
|
40
|
-
|
|
43
|
+
// TUI2-R2pre ④: the rule line is the panel's header — it says the ACT
|
|
44
|
+
// ("edit needs approval"). view.name keeps the RAW tool name, which is
|
|
45
|
+
// what the option-2 rule prefill and the fallbackQuestion (the
|
|
46
|
+
// dock-less/pipe path — byte-identical by ruling) still read.
|
|
47
|
+
const base = `${p.bold}${escapeTerminal(displayVerb(view.name))}${p.reset} ${p.dim}needs approval — asked by${p.reset} ${p.bold}${escapeTerminal(view.speaker)}${p.reset}`;
|
|
41
48
|
return hint ? `${base}${p.dim} ·${p.reset} ${p.code}${escapeTerminal(hint)}${p.reset}` : base;
|
|
42
49
|
}
|
|
43
50
|
/** The numbered options row — "1 Yes 2 Yes, don't ask again for
|
|
@@ -50,10 +57,14 @@ function panelRuleText(view) {
|
|
|
50
57
|
* single-row discipline — the row never folds). */
|
|
51
58
|
function panelOptionsRow(view, sel, W) {
|
|
52
59
|
const p = palette();
|
|
60
|
+
// TUI2-R1.5 ⑪ (VD-13): ONE separator grammar. The options were
|
|
61
|
+
// two-space separated while every other metadata group in the product
|
|
62
|
+
// uses `·`, and at 80 columns that put `3 No` far enough from its
|
|
63
|
+
// neighbours to read as detached rather than as the third option.
|
|
53
64
|
const o1 = sel === 1 ? `${p.bold} 1 Yes${p.reset}` : ` 1 Yes`;
|
|
54
|
-
const o3 = sel === 3 ? `${p.bold}
|
|
65
|
+
const o3 = sel === 3 ? `${p.bold}3 No${p.reset}` : `3 No`;
|
|
55
66
|
if (view.flavor === "simple")
|
|
56
|
-
return `${o1}
|
|
67
|
+
return `${o1} · ${o3}`;
|
|
57
68
|
// the option-2 span: " 2 Yes, don't ask again for <name>" — the
|
|
58
69
|
// fixed part is 45 (the gutter + the 1/3 options + the separators +
|
|
59
70
|
// the 28-cell prefix); the name cuts to W−46 + "…". The "…" needs
|
|
@@ -62,13 +73,18 @@ function panelOptionsRow(view, sel, W) {
|
|
|
62
73
|
// DROPS: the rule name is the cuttable span, the 1/3 options are
|
|
63
74
|
// the semantics — the approval decision must survive a narrow
|
|
64
75
|
// winch, and invariant ① must never fire on the options row.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const o2 = sel === 2 ? `${p.bold}
|
|
71
|
-
|
|
76
|
+
// TUI2-R1.5 ⑪: option 2 states what it DOES; the tool it would do it
|
|
77
|
+
// for is the panel's title, one row above, and repeating it here was
|
|
78
|
+
// what made this row the widest thing in the block. The fixed part is
|
|
79
|
+
// now 33 cells, so the whole row survives far narrower windows than the
|
|
80
|
+
// 47 the rule name used to demand.
|
|
81
|
+
const o2 = sel === 2 ? `${p.bold}2 Yes, don't ask again${p.reset}` : `2 Yes, don't ask again`;
|
|
82
|
+
const full = `${o1} · ${o2} · ${o3}`;
|
|
83
|
+
if (visibleWidth(full) <= W - 2)
|
|
84
|
+
return full;
|
|
85
|
+
// too narrow for the middle option: the 1/3 decision is the semantics
|
|
86
|
+
// and must survive any winch (invariant ① never fires on this row).
|
|
87
|
+
return cutLine(`${o1} · ${o3}`, Math.max(1, W - 2));
|
|
72
88
|
}
|
|
73
89
|
/** The block's rows — EXACTLY the preview's frame shape, the gutter at
|
|
74
90
|
* the left edge (the preview's two-space mock indent is its own
|
|
@@ -80,7 +96,11 @@ export function panelBlockRows(view, phase, sel, W, maxRows) {
|
|
|
80
96
|
const rows = [];
|
|
81
97
|
rows.push(`${gutter}${cutLine(panelRuleText(view), Math.max(1, W - 2))}`);
|
|
82
98
|
rows.push(`${gutter}${cutLine(`${p.bold}${escapeTerminal(view.title)}${p.reset}`, Math.max(1, W - 2))}`);
|
|
83
|
-
|
|
99
|
+
// TUI2-R1.5 ⑤ (VD-11): the divider is a LABEL, not a design note. "the
|
|
100
|
+
// full args — never truncated" is a sentence about the implementation,
|
|
101
|
+
// addressed to whoever was building the panel; the human reading it
|
|
102
|
+
// during an approval wants to know what the block below is.
|
|
103
|
+
rows.push(`${cutLine(`${p.dim}─ args (full) ─${p.reset}`, Math.max(1, W - 2))}`);
|
|
84
104
|
// the args — the bounded block's body: fold, then cap. The └ cut is
|
|
85
105
|
// ONE row (the W20 discipline): when the args exceed the budget, one
|
|
86
106
|
// notice row carries the count and where the rest is (the event log).
|
|
@@ -100,7 +120,15 @@ export function panelBlockRows(view, phase, sel, W, maxRows) {
|
|
|
100
120
|
rows.push(...shown);
|
|
101
121
|
rows.push(`${gutter}${panelOptionsRow(view, sel, W)}`);
|
|
102
122
|
rows.push(`${gutter}${p.dim}${panelAffordance(view, phase, sel)}${p.reset}`);
|
|
103
|
-
|
|
123
|
+
// TUI2-R1.5 11 (VD-13): a real bottom RULE, in the block's own edge
|
|
124
|
+
// vocabulary — the same box-drawing run its divider already uses —
|
|
125
|
+
// anchored at the gutter column. It used to be `\u2514 `: a two-cell stub
|
|
126
|
+
// floating at column 1, with no rule running from it and no corner
|
|
127
|
+
// above it to answer. Worse, `\u2514 ` is the cut-notice prefix everywhere
|
|
128
|
+
// else in the product, so a CAPPED panel emitted two elbow rows in a
|
|
129
|
+
// row meaning entirely different things. The rule reads as an edge,
|
|
130
|
+
// and the cut notice above it reads as a notice.
|
|
131
|
+
rows.push(`${p.dim}\u2514${"\u2500".repeat(Math.max(0, W - 1))}${p.reset}`);
|
|
104
132
|
return rows;
|
|
105
133
|
}
|
|
106
134
|
/** The input row's lead for the panel's phase (the preview's chrome
|
|
@@ -149,3 +177,88 @@ export function panelAffordance(view, phase, sel) {
|
|
|
149
177
|
return sel === 0 ? "tab amend · esc cancel" : "enter sends · esc backs out";
|
|
150
178
|
return sel === 0 ? "esc cancel" : "enter sends";
|
|
151
179
|
}
|
|
180
|
+
// ── TUI2-R2 ④: the pick block, its lead, its status, its affordance ──
|
|
181
|
+
/**
|
|
182
|
+
* The pick block's rows — the prototype's C frame.
|
|
183
|
+
*
|
|
184
|
+
* The header says what is in effect right now, because the first
|
|
185
|
+
* question anyone opening this panel has is "what am I on?". The
|
|
186
|
+
* options are numbered from 1 and the number IS the key. The `t` row is
|
|
187
|
+
* last and always present: a profile list is a convenience, never the
|
|
188
|
+
* set of models that exist, and a picker that can only offer what
|
|
189
|
+
* someone remembered to configure is a smaller product than the one it
|
|
190
|
+
* replaced.
|
|
191
|
+
*
|
|
192
|
+
* Single-row discipline: every row CUTS, never folds — the block's
|
|
193
|
+
* height is its row count (the W20 rule the #checked throw demands).
|
|
194
|
+
*/
|
|
195
|
+
export function pickBlockRows(view, state, W, maxRows) {
|
|
196
|
+
const p = palette();
|
|
197
|
+
const spec = view.pick;
|
|
198
|
+
const gutter = `${p.dim}\u2502${p.reset} `;
|
|
199
|
+
const rows = [];
|
|
200
|
+
const room = Math.max(1, W - 2);
|
|
201
|
+
rows.push(`${gutter}${cutLine(`${p.bold}${escapeTerminal(spec.header.split(" \u2014 ")[0] ?? spec.header)}${p.reset}${p.dim}${escapeTerminal(spec.header.slice((spec.header.split(" \u2014 ")[0] ?? "").length))}${p.reset}`, room)}`);
|
|
202
|
+
if (spec.options.length === 0) {
|
|
203
|
+
// the honest empty state \u2014 the caller's own copy, verbatim
|
|
204
|
+
rows.push(`${gutter}${cutLine(`${p.dim} ${escapeTerminal(spec.emptyNote ?? "no options")}${p.reset}`, room)}`);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
// the budget: the header, the t row, the affordance and the rule
|
|
208
|
+
const budget = Math.max(1, maxRows - 4);
|
|
209
|
+
const shown = spec.options.slice(0, Math.min(budget, PICK_MAX));
|
|
210
|
+
for (let i = 0; i < shown.length; i += 1) {
|
|
211
|
+
const o = shown[i];
|
|
212
|
+
const mark = i === state.cursor && state.phase === "options";
|
|
213
|
+
const head = `${mark ? p.bold : ""} ${i + 1} ${escapeTerminal(o.label)}${mark ? p.reset : ""}`;
|
|
214
|
+
const note = o.note === undefined ? "" : `${p.dim} ${escapeTerminal(o.note)}${p.reset}`;
|
|
215
|
+
rows.push(`${gutter}${cutLine(`${head}${note}`, room)}`);
|
|
216
|
+
}
|
|
217
|
+
if (spec.options.length > shown.length) {
|
|
218
|
+
rows.push(`${gutter}${cutLine(`${p.dim} \u2514 +${spec.options.length - shown.length} more \u2014 /model <name> takes any of them${p.reset}`, room)}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
rows.push(`${gutter}${cutLine(`${state.phase === "custom" ? p.bold : ""} t ${p.reset}${p.dim}${escapeTerminal(spec.typeHint)}${p.reset}`, room)}`);
|
|
222
|
+
rows.push(`${gutter}${p.dim}${cutLine(pickAffordance(state), room)}${p.reset}`);
|
|
223
|
+
rows.push(`${p.dim}\u2514${"\u2500".repeat(Math.max(0, W - 1))}${p.reset}`);
|
|
224
|
+
return rows;
|
|
225
|
+
}
|
|
226
|
+
/** The digits are the keys, so the list the panel offers is bounded by
|
|
227
|
+
* the digits there are. Beyond it, `/model <name>` still takes any
|
|
228
|
+
* profile — the panel says so rather than paginating. */
|
|
229
|
+
export const PICK_MAX = 9;
|
|
230
|
+
/** The input row's lead: the digit range while picking, the named
|
|
231
|
+
* prompt while typing one out. */
|
|
232
|
+
export function pickLeadPlain(view, state) {
|
|
233
|
+
if (state.phase === "custom")
|
|
234
|
+
return "provider/model: ";
|
|
235
|
+
const n = Math.min(view.pick.options.length, PICK_MAX);
|
|
236
|
+
return n === 0 ? "t> " : `1-${n}> `;
|
|
237
|
+
}
|
|
238
|
+
export function pickLead(view, state) {
|
|
239
|
+
const p = palette();
|
|
240
|
+
return `${p.bold}${pickLeadPlain(view, state)}${p.reset}`;
|
|
241
|
+
}
|
|
242
|
+
/** The status row's left text \u2014 the CALLER's, because only the caller
|
|
243
|
+
* knows whether a run is paused behind this panel. */
|
|
244
|
+
export function pickStatus(view) {
|
|
245
|
+
return view.statusText;
|
|
246
|
+
}
|
|
247
|
+
export function pickAffordance(state) {
|
|
248
|
+
return state.phase === "custom" ? "enter commits \u00b7 esc backs out" : "digits pick \u00b7 \u23ce confirms \u00b7 esc";
|
|
249
|
+
}
|
|
250
|
+
/** Compose a pick view. The flavor/name/title/args fields exist for the
|
|
251
|
+
* approval path and are given inert values here \u2014 the pick block
|
|
252
|
+
* reads none of them. */
|
|
253
|
+
export function modelPickView(spec, statusText) {
|
|
254
|
+
return {
|
|
255
|
+
flavor: "simple",
|
|
256
|
+
name: "model",
|
|
257
|
+
title: "model",
|
|
258
|
+
speaker: "you",
|
|
259
|
+
statusText,
|
|
260
|
+
args: { kind: "text", lines: [] },
|
|
261
|
+
fallbackQuestion: "switch model? (name) ",
|
|
262
|
+
pick: spec,
|
|
263
|
+
};
|
|
264
|
+
}
|
package/dist/components.d.ts
CHANGED
|
@@ -108,12 +108,19 @@ export type BodyCell = {
|
|
|
108
108
|
* the head of an N > 2 same-tool run renders the group (the
|
|
109
109
|
* work order's claimed shape: "✓ read 5 files (2.4k lines,
|
|
110
110
|
* 1.1s)" + the target children). The members carry null — the
|
|
111
|
-
* compositor's rolled-heads bookkeeping renders them [].
|
|
111
|
+
* compositor's rolled-heads bookkeeping renders them [].
|
|
112
|
+
* TUI2-R1 (B): `parts` is set when the run spans MORE THAN ONE
|
|
113
|
+
* read-only tool — the same mechanism, the exploration row.
|
|
114
|
+
* Absent (a single-name run) keeps W13's row byte for byte. */
|
|
112
115
|
rolled: null | {
|
|
113
116
|
count: number;
|
|
114
117
|
lines: number;
|
|
115
118
|
elapsed: string;
|
|
116
119
|
targets: string[];
|
|
120
|
+
parts?: readonly {
|
|
121
|
+
name: string;
|
|
122
|
+
subjects: readonly string[];
|
|
123
|
+
}[];
|
|
117
124
|
};
|
|
118
125
|
/** W19: a DENIED call's reason (the CLI extracted it from the
|
|
119
126
|
* result's "[Permission denied] " prefix, keyed on the "denied"
|
|
@@ -152,6 +159,7 @@ export type BodyCell = {
|
|
|
152
159
|
kind: "raw";
|
|
153
160
|
lines: string[];
|
|
154
161
|
done: true;
|
|
162
|
+
wrap?: "words";
|
|
155
163
|
} | {
|
|
156
164
|
kind: "terminal";
|
|
157
165
|
label: string;
|
|
@@ -196,11 +204,7 @@ export declare function cellComponent(cell: BodyCell): Component;
|
|
|
196
204
|
* (the gutter's 2 cells), so a long line hard-folds INSIDE the chip
|
|
197
205
|
* and invariant ① holds on the band. */
|
|
198
206
|
export declare function pendingQueueRows(lines: readonly string[], W: number): string[];
|
|
199
|
-
|
|
200
|
-
* (W2: a wrapped tool row keeps its state mark — the left edge alone
|
|
201
|
-
* distinguishes the states at --plain; the UserMessage rail precedent,
|
|
202
|
-
* v5 #16f). The gutter carries its own SGR (e.g. the bold ✓). W21:
|
|
203
|
-
* exported for the approval panel's text args (the same │ gutter). */
|
|
207
|
+
export declare function foldWords(line: string, W: number): string[];
|
|
204
208
|
export declare function gutterFold(gutter: string, line: string, W: number): string[];
|
|
205
209
|
/** A6: the tool-header variant — ONE cut row, never a fold. A wide
|
|
206
210
|
* header (a long target path, a wordy denial reason) used to wrap
|
|
@@ -210,12 +214,57 @@ export declare function gutterFold(gutter: string, line: string, W: number): str
|
|
|
210
214
|
* full content. The budget: the gutter's own visible width + the
|
|
211
215
|
* ellipsis ride the row (the invariant ① cap holds). */
|
|
212
216
|
export declare function gutterCut(gutter: string, line: string, W: number): string[];
|
|
217
|
+
export declare function expandSuffix(lines: number | null, room: number): string;
|
|
218
|
+
/**
|
|
219
|
+
* TUI2-R2 ⑤ (D, candidate 1) — the FOCUS tint.
|
|
220
|
+
*
|
|
221
|
+
* The cell the next ctrl+r will act on brightens its own `ctrl+r` token
|
|
222
|
+
* to the code tint; the rest of the suffix — the separator, the count —
|
|
223
|
+
* stays dim, because what is being marked is the KEY's target, not the
|
|
224
|
+
* row. Zero new rows, zero new columns: the affordance the cell already
|
|
225
|
+
* prints is the marker.
|
|
226
|
+
*
|
|
227
|
+
* Applied to a row rather than composed into it on purpose. The token is
|
|
228
|
+
* emitted from several places (the settled suffix, the renderer's own
|
|
229
|
+
* `└ +N … · ctrl+r` cut rows) and threading a flag through all of them
|
|
230
|
+
* would put the invariant "exactly one bright token" in as many hands as
|
|
231
|
+
* there are emitters. Here it has exactly one.
|
|
232
|
+
*
|
|
233
|
+
* NO_COLOR: p.code is empty, so the row's bytes are untouched.
|
|
234
|
+
*/
|
|
235
|
+
export declare function focusToken(row: string, W: number): string;
|
|
213
236
|
/** W13 — the rollup opt-in table: which tools collapse, and the count
|
|
214
237
|
* NOUN (read_file calls → "5 files", list_dir → "5 dirs", search_text
|
|
215
238
|
* → "5 matches"). Only these tools opt in — a shell burst is never
|
|
216
239
|
* rolled up (its rows carry meaning). The folded-turn line (W14) reuses
|
|
217
240
|
* the plurals for its other-tool terms ("2 dirs", "1 match"). */
|
|
218
241
|
export declare const ROLLUP_NOUN: Readonly<Record<string, string>>;
|
|
242
|
+
/** TUI2-R1 (B) — the verb column of the expanded list names the ACT.
|
|
243
|
+
* TUI2-R2pre ④: this used to be a private three-tool table saying the
|
|
244
|
+
* same thing as the card head's `_file` strip, in a different way and
|
|
245
|
+
* for a different set of tools. Both are `displayVerb` now — the whole
|
|
246
|
+
* point of the ruling is that there is ONE answer to "what does the
|
|
247
|
+
* screen call this". The cut note, which used to be the deliberate
|
|
248
|
+
* exception here, moved with it (see toolCutNote). */
|
|
249
|
+
/** Whether a tool joins an exploration run. Exactly the read-only set —
|
|
250
|
+
* writes, edits, shells and extension tools never group (a burst of
|
|
251
|
+
* side effects is a list of things that HAPPENED, and every row of it
|
|
252
|
+
* carries meaning). */
|
|
253
|
+
export declare function isExploreTool(name: string): boolean;
|
|
254
|
+
/** "8 files · 14 searches" — the per-tool counts in first-call order. */
|
|
255
|
+
export declare function exploreCounts(parts: readonly {
|
|
256
|
+
name: string;
|
|
257
|
+
subjects: readonly string[];
|
|
258
|
+
}[]): string;
|
|
259
|
+
/** TUI2-R1 (B) — the expanded list: ONE row per tool, the verb column
|
|
260
|
+
* then the distinct subjects in first-call order, a repeated subject
|
|
261
|
+
* carrying its ×count, the first three shown and the rest counted.
|
|
262
|
+
* A search's subject is its PATTERN (quoted — the thing that was
|
|
263
|
+
* looked for); a read's or a list's is its path. */
|
|
264
|
+
export declare function exploreRows(parts: readonly {
|
|
265
|
+
name: string;
|
|
266
|
+
subjects: readonly string[];
|
|
267
|
+
}[], W: number): string[];
|
|
219
268
|
/** W14 — the folded-turn line: a whole QUIET turn (no text), once it is
|
|
220
269
|
* scrollback, becomes ONE line — the work order's claimed shape
|
|
221
270
|
* (`▞ thought 19s · 5 reads · no edits`), the counts accumulated at
|