@vincemakes/kiso-tui-cells 0.46.2 → 0.47.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/approval-panel.d.ts +149 -62
- package/dist/approval-panel.js +346 -227
- package/dist/components.d.ts +151 -21
- package/dist/components.js +1242 -825
- package/dist/diff.d.ts +40 -3
- package/dist/diff.js +142 -5
- package/dist/graphite.d.ts +143 -0
- package/dist/graphite.js +311 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +6 -3
- package/dist/md.d.ts +18 -7
- package/dist/md.js +290 -37
- package/dist/render.d.ts +136 -27
- package/dist/render.js +381 -137
- package/dist/strings.d.ts +47 -46
- package/dist/strings.js +130 -171
- package/package.json +1 -1
package/dist/strings.d.ts
CHANGED
|
@@ -47,7 +47,7 @@ export declare function projectTrustRows(files: readonly TrustArtifact[], indent
|
|
|
47
47
|
* the artifact listing as the always-verbose args, and the question as
|
|
48
48
|
* the rule override. The same rows the scrollback records — the panel
|
|
49
49
|
* is a bounded block, the record is not. */
|
|
50
|
-
export declare function projectTrustView(root: string, files: readonly TrustArtifact[]): PanelView;
|
|
50
|
+
export declare function projectTrustView(root: string, files: readonly TrustArtifact[], shownRoot?: string): PanelView;
|
|
51
51
|
/** E3 — the non-TTY note: artifacts were found and deliberately NOT
|
|
52
52
|
* loaded, with the one path back (run once interactively). Printed to
|
|
53
53
|
* stderr by the caller; never silent. */
|
|
@@ -72,7 +72,7 @@ export declare function projectUntrustedNote(count: number, root: string): strin
|
|
|
72
72
|
* applied"); what follows it is the action the answer performs. The
|
|
73
73
|
* invariant a test now holds for every simple view: the dock-less
|
|
74
74
|
* question names `simpleOptions[0]`, the action `y` performs. */
|
|
75
|
-
export declare function uncertainView(name: string, executionId: string): PanelView;
|
|
75
|
+
export declare function uncertainView(name: string, executionId: string, target?: readonly string[]): PanelView;
|
|
76
76
|
/**
|
|
77
77
|
* KC3.5 — the SAME uncertainty gate, said honestly for an ask_user call.
|
|
78
78
|
*
|
|
@@ -87,7 +87,7 @@ export declare function uncertainView(name: string, executionId: string): PanelV
|
|
|
87
87
|
* of idempotency, so an interrupted ask meets this gate on the way
|
|
88
88
|
* back. Re-asking is safe — that is what the first option says out loud.)
|
|
89
89
|
*/
|
|
90
|
-
export declare function unansweredAskView(executionId: string): PanelView;
|
|
90
|
+
export declare function unansweredAskView(executionId: string, questions?: readonly string[]): PanelView;
|
|
91
91
|
/**
|
|
92
92
|
* 0.40.0 (the owner's session) — the cold resume. A session resumed 27
|
|
93
93
|
* minutes after its last request re-sent a 727k prefix the provider had
|
|
@@ -122,6 +122,15 @@ export interface BannerExtension {
|
|
|
122
122
|
* reads `built-in: mcp, skills, subagent`, from this one composition.
|
|
123
123
|
*/
|
|
124
124
|
export declare function extensionsBannerText(builtIn: readonly BannerExtension[], user: readonly BannerExtension[], project: readonly BannerExtension[]): string;
|
|
125
|
+
/** The last sweep (owner, 2026-10-06) — the same three lists as the
|
|
126
|
+
* opening's EXTENSIONS row: how many, then the names — the built-in ones
|
|
127
|
+
* bare, the user's after `user:`, the project's after `project:`. The
|
|
128
|
+
* pipe's `[N extensions: …]` line stays `extensionsBannerText`'s. Null
|
|
129
|
+
* when nothing loaded. */
|
|
130
|
+
export declare function extensionsFact(builtIn: readonly BannerExtension[], user: readonly BannerExtension[], project: readonly BannerExtension[]): {
|
|
131
|
+
readonly value: string;
|
|
132
|
+
readonly note: string;
|
|
133
|
+
} | null;
|
|
125
134
|
/** One gesture: what you press, and what it does. */
|
|
126
135
|
export interface KeyBinding {
|
|
127
136
|
readonly keys: string;
|
|
@@ -129,59 +138,39 @@ export interface KeyBinding {
|
|
|
129
138
|
}
|
|
130
139
|
/**
|
|
131
140
|
* TUI2-R1 (D) — THE key table. Every reader derives from it: the `?`
|
|
132
|
-
* sheet and
|
|
141
|
+
* sheet and `keysHelpRow`. A sheet that has drifted from the keys is
|
|
133
142
|
* worse than no sheet, and the only way to make drift impossible is to
|
|
134
143
|
* have one table and no second copy of it.
|
|
135
144
|
*
|
|
136
|
-
* The order is the sheet's reading order,
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
* then the
|
|
145
|
+
* The order is the sheet's reading order, two bindings to a row — the
|
|
146
|
+
* sheets round (owner, 2026-10-06): the sheet is two columns, left then
|
|
147
|
+
* right, so the table is read in pairs. Grouped by WHAT A HUMAN IS DOING:
|
|
148
|
+
* putting something in and changing course, then finding things, then
|
|
149
|
+
* seeing more, then editing, then the clipboard, then the shell. A key
|
|
150
|
+
* with two spellings is named by one (alt+⏎ also answers to ctrl+⏎,
|
|
151
|
+
* alt+←→ to ctrl+←→); ctrl+w stays named because it is the word deletion
|
|
152
|
+
* that works on every terminal (§8.6). `ctrl+t`, `ctrl+g` and the `!`
|
|
153
|
+
* gestures joined from /help's table, which named them and the sheet did
|
|
154
|
+
* not; `? this sheet` left, because `?` is how you got here.
|
|
141
155
|
*/
|
|
142
156
|
export declare const KEY_BINDINGS: readonly KeyBinding[];
|
|
143
157
|
/** A tool's name as the SCREEN says it. Display-only: the raw name stays
|
|
144
158
|
* on the cell, and dispatch, the mode gate, the policy keys, the /last
|
|
145
159
|
* RAW block and every model-facing byte keep reading that. */
|
|
146
160
|
export declare function displayVerb(name: string): string;
|
|
147
|
-
/**
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
|
|
151
|
-
* TUI2-R1.5 pin 6 — this row has to be true of BOTH panel flavors.
|
|
152
|
-
*
|
|
153
|
-
* The R1.5 wording ("digits pick · ⏎ confirms") was the sentence that
|
|
154
|
-
* covered an approval where a digit only SELECTED and an ask where a
|
|
155
|
-
* digit ANSWERED. TUI2-R3v2 ① removed the disagreement it was papering
|
|
156
|
-
* over: every panel is a list with a bar on it, ↑↓ move the bar, ⏎ (or a
|
|
157
|
-
* click) takes the row under it, and a digit takes its row outright.
|
|
158
|
-
* One sentence, now true of every flavor for the same reason rather than
|
|
159
|
-
* by careful omission.
|
|
160
|
-
*
|
|
161
|
-
* The two ask-only gestures keep their clauses because the ask is the
|
|
162
|
-
* only flavor with a set to build and an answer to write.
|
|
163
|
-
*/
|
|
164
|
-
/**
|
|
165
|
-
* R6/D2 — the row claims only what is TRUE OF EVERY panel.
|
|
166
|
-
*
|
|
167
|
-
* Its old sentence ("⏎ or click confirms · 1-4 instant · space toggles")
|
|
168
|
-
* was documented as true of every flavor, and had silently stopped
|
|
169
|
-
* being: `1-4 instant` is false on the ask's multi-select and on the
|
|
170
|
-
* pick panel (where a digit only moves the cursor and never commits),
|
|
171
|
-
* `or click` is false on both (their frames deliberately report no
|
|
172
|
-
* clickable span), and "1-4" is wrong whenever a panel has a different
|
|
173
|
-
* option count. Layered honesty: this row states the invariant, and each
|
|
174
|
-
* panel's own affordance row states that panel's whole truth.
|
|
175
|
-
*/
|
|
176
|
-
export declare const PANEL_KEYS_ROW = "panels: \u2191\u2193 move \u00B7 \u23CE confirms \u00B7 digits act on their row \u00B7 t types";
|
|
177
|
-
/**
|
|
178
|
-
* TUI2-R1 (D) — the sheet, one screen, static.
|
|
161
|
+
/** Graphite R3e — how every read-only sheet closes, on its last row: esc
|
|
162
|
+
* closes it, and what is typed goes to the input. The sheets round
|
|
163
|
+
* (owner, 2026-10-06) gave the keys sheet the same row and the same
|
|
164
|
+
* behaviour as `/status`.
|
|
179
165
|
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
|
|
166
|
+
* DECLARED REVERSAL (the sheets round): the keys sheet's last row was
|
|
167
|
+
* `PANEL_KEYS_ROW` (`panels: ↑↓ move · ⏎ confirms · digits act on their
|
|
168
|
+
* row · t types`), with its own grid (`SHEET_GRID`, `SHEET_STOPS`) and
|
|
169
|
+
* a clause-dropping ladder (DC-2). Every panel's own key row now says
|
|
170
|
+
* that panel's keys (§8.2, P3, P4), the row had gone stale (`t types`
|
|
171
|
+
* left `/model` in P3), and the grid was a second and third table that
|
|
172
|
+
* had to agree with KEY_BINDINGS by hand. */
|
|
173
|
+
export declare const SHEET_CLOSE = "esc closes \u00B7 typing goes to the input";
|
|
185
174
|
/** R8b — the band's own opening row: a labelled rule at full width.
|
|
186
175
|
*
|
|
187
176
|
* Moved here from the @ picker, unchanged in every byte, because the
|
|
@@ -189,6 +178,18 @@ export declare const PANEL_KEYS_ROW = "panels: \u2191\u2193 move \u00B7 \u23CE c
|
|
|
189
178
|
* module — the dependency only runs one way. `at-picker.ts` re-exports
|
|
190
179
|
* it, so every existing import site is untouched. */
|
|
191
180
|
export declare function bandHeader(label: string, W: number): string;
|
|
181
|
+
/**
|
|
182
|
+
* TUI2-R1 (D), the sheets round (owner, 2026-10-06) — the keys sheet: the
|
|
183
|
+
* band's named hairline, the bindings two to a row at the content edge,
|
|
184
|
+
* the key in ink and what it does dim, each column measured over the
|
|
185
|
+
* whole table so they line up, and the closing row. Narrower than two
|
|
186
|
+
* columns need, it is one column in the table's order. Rows CUT at the
|
|
187
|
+
* width rather than folding: the sheet's contract is "one screen".
|
|
188
|
+
*
|
|
189
|
+
* DECLARED REVERSAL: the sheet began at column 0 with the keys bold and
|
|
190
|
+
* hand-set column stops (DC-1/DC-3); §1.8's one content edge and the
|
|
191
|
+
* command list's name/description tones replace them.
|
|
192
|
+
*/
|
|
192
193
|
export declare function keysSheetRows(W: number): string[];
|
|
193
194
|
/** TUI2-R1 (D) — the keys as ONE line, for /help. The same table the
|
|
194
195
|
* sheet renders, joined — so the two can disagree only by deleting a
|
package/dist/strings.js
CHANGED
|
@@ -45,7 +45,7 @@ export function projectTrustRows(files, indent = "") {
|
|
|
45
45
|
* the artifact listing as the always-verbose args, and the question as
|
|
46
46
|
* the rule override. The same rows the scrollback records — the panel
|
|
47
47
|
* is a bounded block, the record is not. */
|
|
48
|
-
export function projectTrustView(root, files) {
|
|
48
|
+
export function projectTrustView(root, files, shownRoot = root) {
|
|
49
49
|
return {
|
|
50
50
|
flavor: "simple",
|
|
51
51
|
name: "project trust",
|
|
@@ -54,6 +54,10 @@ export function projectTrustView(root, files) {
|
|
|
54
54
|
statusText: "❯ project trust",
|
|
55
55
|
args: { kind: "text", lines: projectTrustRows(files) },
|
|
56
56
|
ruleOverride: "trust this project's .kiso?",
|
|
57
|
+
// Graphite P1b (owner, 2026-09-30): kiso's own question, and the
|
|
58
|
+
// answers say what they do, like the other gates'
|
|
59
|
+
asked: { question: "trust this project?", facts: [shownRoot], prose: "Its .kiso folder holds these files. kiso loads none of them until you trust the project." },
|
|
60
|
+
simpleOptions: ["trust it", "not now"],
|
|
57
61
|
fallbackQuestion: `trust this project's .kiso? (y/n) `,
|
|
58
62
|
};
|
|
59
63
|
}
|
|
@@ -83,15 +87,20 @@ export function projectUntrustedNote(count, root) {
|
|
|
83
87
|
* applied"); what follows it is the action the answer performs. The
|
|
84
88
|
* invariant a test now holds for every simple view: the dock-less
|
|
85
89
|
* question names `simpleOptions[0]`, the action `y` performs. */
|
|
86
|
-
export function uncertainView(name, executionId) {
|
|
90
|
+
export function uncertainView(name, executionId, target = []) {
|
|
91
|
+
// Graphite P1b: the panel quotes WHAT may have run — the command, or the
|
|
92
|
+
// call's target as its tool card names it (the CLI reads it from the
|
|
93
|
+
// execution record) — where it used to quote the execution id twice
|
|
94
|
+
const what = name === "shell" ? "command" : "call";
|
|
87
95
|
return {
|
|
88
96
|
flavor: "simple",
|
|
89
97
|
name: "uncertain execution",
|
|
90
98
|
title: `${name} (${executionId})`,
|
|
91
99
|
speaker: "kiso",
|
|
92
100
|
statusText: "❯ uncertain execution",
|
|
93
|
-
args: { kind: "text", lines: [executionId] },
|
|
101
|
+
args: { kind: "text", lines: target.length > 0 ? [...target] : [executionId] },
|
|
94
102
|
ruleOverride: "an interrupted execution may have applied — rerun it?",
|
|
103
|
+
asked: { question: "rerun it?", facts: [name], prose: `kiso stopped before this ${what}'s result was saved, so it may already have run. Check the workspace, then choose.` },
|
|
95
104
|
simpleOptions: ["rerun it", "abandon it"],
|
|
96
105
|
fallbackQuestion: `interrupted execution: ${escapeTerminal(name)} (${executionId}) — rerun it? (y)es / (n)o `,
|
|
97
106
|
};
|
|
@@ -110,15 +119,18 @@ export function uncertainView(name, executionId) {
|
|
|
110
119
|
* of idempotency, so an interrupted ask meets this gate on the way
|
|
111
120
|
* back. Re-asking is safe — that is what the first option says out loud.)
|
|
112
121
|
*/
|
|
113
|
-
export function unansweredAskView(executionId) {
|
|
122
|
+
export function unansweredAskView(executionId, questions = []) {
|
|
123
|
+
const many = questions.length > 1;
|
|
114
124
|
return {
|
|
115
125
|
flavor: "simple",
|
|
116
126
|
name: "unanswered question",
|
|
117
127
|
title: `ask_user (${executionId})`,
|
|
118
128
|
speaker: "kiso",
|
|
119
129
|
statusText: "❯ unanswered question",
|
|
120
|
-
|
|
130
|
+
// Graphite P1b: the question itself, quoted — not its execution id
|
|
131
|
+
args: { kind: "text", lines: questions.length > 0 ? [...questions] : [executionId] },
|
|
121
132
|
ruleOverride: "an unanswered question was interrupted — ask it again?",
|
|
133
|
+
asked: { question: many ? "ask them again?" : "ask it again?", facts: ["never answered"], prose: `The session stopped while ${many ? "these questions" : "this question"} waited for you.` },
|
|
122
134
|
simpleOptions: ["ask it again", "drop it"],
|
|
123
135
|
fallbackQuestion: `an unanswered question was interrupted (${executionId}) — ask it again? (y)es / (n)o `,
|
|
124
136
|
};
|
|
@@ -136,13 +148,18 @@ export function coldResumeLine(tokens, minutesAgo) {
|
|
|
136
148
|
}
|
|
137
149
|
export function coldResumeView(tokens, minutesAgo) {
|
|
138
150
|
const line = coldResumeLine(tokens, minutesAgo);
|
|
151
|
+
const k = `${Math.round(tokens / 1000)}k`;
|
|
139
152
|
return {
|
|
140
153
|
flavor: "simple",
|
|
141
154
|
name: "cold cache",
|
|
142
|
-
title: `compact first? (${
|
|
155
|
+
title: `compact first? (${k} tokens, ${minutesAgo} min idle)`,
|
|
143
156
|
speaker: "kiso",
|
|
144
157
|
statusText: "❯ resumed session",
|
|
145
|
-
|
|
158
|
+
// Graphite P1b: the size and the idle time ride the band; one
|
|
159
|
+
// sentence says what happens and what compacting costs — nothing is
|
|
160
|
+
// quoted, so the gutter is empty
|
|
161
|
+
args: { kind: "text", lines: [] },
|
|
162
|
+
asked: { question: "compact first?", facts: [`${k} tokens`, `idle ${minutesAgo} min`], prose: `The cache expired while the session was idle, so the next request sends all ${k} tokens again. Compacting first is one summary call; the turns after it are small.` },
|
|
146
163
|
ruleOverride: `${line} — compact first? (one summary call, then every turn is cheap)`,
|
|
147
164
|
simpleOptions: ["compact first", "keep the full history"],
|
|
148
165
|
fallbackQuestion: `${line} — compact first? (y)es / (n)o `,
|
|
@@ -173,49 +190,76 @@ export function extensionsBannerText(builtIn, user, project) {
|
|
|
173
190
|
parts.push(`project: ${project.map(label).join(", ")}`);
|
|
174
191
|
return ` · [${total} extension${total === 1 ? "" : "s"}: ${parts.join(" · ")}]`;
|
|
175
192
|
}
|
|
193
|
+
/** The last sweep (owner, 2026-10-06) — the same three lists as the
|
|
194
|
+
* opening's EXTENSIONS row: how many, then the names — the built-in ones
|
|
195
|
+
* bare, the user's after `user:`, the project's after `project:`. The
|
|
196
|
+
* pipe's `[N extensions: …]` line stays `extensionsBannerText`'s. Null
|
|
197
|
+
* when nothing loaded. */
|
|
198
|
+
export function extensionsFact(builtIn, user, project) {
|
|
199
|
+
const total = builtIn.length + user.length + project.length;
|
|
200
|
+
if (total === 0)
|
|
201
|
+
return null;
|
|
202
|
+
const label = (e) => e.connecting === true ? `${e.name} (connecting…)` : e.note !== undefined ? `${e.name} (${e.note})` : e.name;
|
|
203
|
+
const parts = [];
|
|
204
|
+
if (builtIn.length > 0)
|
|
205
|
+
parts.push(builtIn.map(label).join(", "));
|
|
206
|
+
if (user.length > 0)
|
|
207
|
+
parts.push(`user: ${user.map(label).join(", ")}`);
|
|
208
|
+
if (project.length > 0)
|
|
209
|
+
parts.push(`project: ${project.map(label).join(", ")}`);
|
|
210
|
+
return { value: String(total), note: parts.join(" \u00b7 ") };
|
|
211
|
+
}
|
|
176
212
|
/**
|
|
177
213
|
* TUI2-R1 (D) — THE key table. Every reader derives from it: the `?`
|
|
178
|
-
* sheet and
|
|
214
|
+
* sheet and `keysHelpRow`. A sheet that has drifted from the keys is
|
|
179
215
|
* worse than no sheet, and the only way to make drift impossible is to
|
|
180
216
|
* have one table and no second copy of it.
|
|
181
217
|
*
|
|
182
|
-
* The order is the sheet's reading order,
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
* then the
|
|
218
|
+
* The order is the sheet's reading order, two bindings to a row — the
|
|
219
|
+
* sheets round (owner, 2026-10-06): the sheet is two columns, left then
|
|
220
|
+
* right, so the table is read in pairs. Grouped by WHAT A HUMAN IS DOING:
|
|
221
|
+
* putting something in and changing course, then finding things, then
|
|
222
|
+
* seeing more, then editing, then the clipboard, then the shell. A key
|
|
223
|
+
* with two spellings is named by one (alt+⏎ also answers to ctrl+⏎,
|
|
224
|
+
* alt+←→ to ctrl+←→); ctrl+w stays named because it is the word deletion
|
|
225
|
+
* that works on every terminal (§8.6). `ctrl+t`, `ctrl+g` and the `!`
|
|
226
|
+
* gestures joined from /help's table, which named them and the sheet did
|
|
227
|
+
* not; `? this sheet` left, because `?` is how you got here.
|
|
187
228
|
*/
|
|
188
229
|
export const KEY_BINDINGS = [
|
|
189
230
|
{ keys: "enter", what: "send" },
|
|
190
|
-
{ keys: "
|
|
231
|
+
{ keys: "esc", what: "stop the run" },
|
|
232
|
+
{ keys: "ctrl+j", what: "newline (shift+\u23ce too)" },
|
|
233
|
+
{ keys: "alt+\u23ce", what: "stop it, send this" },
|
|
191
234
|
{ keys: "@", what: "files" },
|
|
192
|
-
{ keys: "esc", what: "stop" },
|
|
193
|
-
{ keys: "alt+⏎ / ctrl+⏎", what: "redirect" },
|
|
194
235
|
{ keys: "/", what: "commands" },
|
|
195
|
-
{ keys: "
|
|
196
|
-
{ keys: "
|
|
236
|
+
{ keys: "\u2191\u2193", what: "history \u00b7 take back a steer" },
|
|
237
|
+
{ keys: "tab", what: "complete" },
|
|
238
|
+
{ keys: "ctrl+o", what: "expand all" },
|
|
197
239
|
// R5 — the transcript viewer. It has to be HERE or it does not exist:
|
|
198
240
|
// R4a retired the printed key from the fold row on the ground that a
|
|
199
241
|
// row cannot say which fold a key opens, and the sheet is where the
|
|
200
242
|
// discoverability moved. A surface nobody can find is not a feature.
|
|
201
243
|
{ keys: "ctrl+r", what: "transcript" },
|
|
202
|
-
{ keys: "
|
|
203
|
-
{ keys: "
|
|
204
|
-
// E1 §1/§3 — the editor's daily three
|
|
205
|
-
// gestures reach the same code
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
{ keys: "
|
|
210
|
-
{ keys: "
|
|
211
|
-
{ keys: "ctrl+x", what: "copy the last answer" },
|
|
212
|
-
{ keys: "ctrl+z / ctrl+y", what: "undo / redo" },
|
|
244
|
+
{ keys: "ctrl+t", what: "hide thinking" },
|
|
245
|
+
{ keys: "ctrl+g", what: "edit in $EDITOR" },
|
|
246
|
+
// E1 §1/§3 — the editor's daily three (word motion, word deletion,
|
|
247
|
+
// copy); three encodings of the word gestures reach the same code.
|
|
248
|
+
{ keys: "alt+\u2190\u2192", what: "word motion" },
|
|
249
|
+
{ keys: "alt+\u232b", what: "delete a word (ctrl+w too)" },
|
|
250
|
+
{ keys: "ctrl+z", what: "undo" },
|
|
251
|
+
{ keys: "ctrl+y", what: "redo" },
|
|
252
|
+
{ keys: "ctrl+x", what: "copy the answer" },
|
|
213
253
|
// REL-0152-D15/D16 — the image paste. It is on the sheet because a
|
|
214
254
|
// terminal's own Cmd+V only ever pastes TEXT: a human with an image on
|
|
215
255
|
// the clipboard has no way to discover this key by trying the obvious
|
|
216
|
-
// one, which is exactly the case the sheet exists for.
|
|
217
|
-
|
|
218
|
-
{ keys: "
|
|
256
|
+
// one, which is exactly the case the sheet exists for.
|
|
257
|
+
{ keys: "ctrl+v", what: "attach an image" },
|
|
258
|
+
{ keys: "!cmd", what: "run it and send it" },
|
|
259
|
+
{ keys: "!!cmd", what: "run it, show it here only" },
|
|
260
|
+
// the main-sync round (ADR-0058 3e): the background key, beside the
|
|
261
|
+
// shell gestures it serves; the live row teaches it while it applies
|
|
262
|
+
{ keys: "ctrl+b", what: "background a command" },
|
|
219
263
|
];
|
|
220
264
|
/**
|
|
221
265
|
* TUI2-R2pre ④ — THE display-verb table (the integrator's ruling).
|
|
@@ -251,93 +295,19 @@ const DISPLAY_VERB = {
|
|
|
251
295
|
export function displayVerb(name) {
|
|
252
296
|
return DISPLAY_VERB[name] ?? name;
|
|
253
297
|
}
|
|
254
|
-
/**
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
|
|
258
|
-
* TUI2-R1.5 pin 6 — this row has to be true of BOTH panel flavors.
|
|
259
|
-
*
|
|
260
|
-
* The R1.5 wording ("digits pick · ⏎ confirms") was the sentence that
|
|
261
|
-
* covered an approval where a digit only SELECTED and an ask where a
|
|
262
|
-
* digit ANSWERED. TUI2-R3v2 ① removed the disagreement it was papering
|
|
263
|
-
* over: every panel is a list with a bar on it, ↑↓ move the bar, ⏎ (or a
|
|
264
|
-
* click) takes the row under it, and a digit takes its row outright.
|
|
265
|
-
* One sentence, now true of every flavor for the same reason rather than
|
|
266
|
-
* by careful omission.
|
|
267
|
-
*
|
|
268
|
-
* The two ask-only gestures keep their clauses because the ask is the
|
|
269
|
-
* only flavor with a set to build and an answer to write.
|
|
270
|
-
*/
|
|
271
|
-
/**
|
|
272
|
-
* R6/D2 — the row claims only what is TRUE OF EVERY panel.
|
|
298
|
+
/** Graphite R3e — how every read-only sheet closes, on its last row: esc
|
|
299
|
+
* closes it, and what is typed goes to the input. The sheets round
|
|
300
|
+
* (owner, 2026-10-06) gave the keys sheet the same row and the same
|
|
301
|
+
* behaviour as `/status`.
|
|
273
302
|
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
|
|
282
|
-
*/
|
|
283
|
-
export const PANEL_KEYS_ROW = "panels: ↑↓ move \u00b7 ⏎ confirms \u00b7 digits act on their row \u00b7 t types";
|
|
284
|
-
/** The sheet's grid: the first six bindings in two 3-column rows, the
|
|
285
|
-
* last four in two 2-column rows (the wide entries get the room). The
|
|
286
|
-
* COLUMN STOPS are the prototype's absolute positions, floored by the
|
|
287
|
-
* content (a future binding widens its column rather than overrunning
|
|
288
|
-
* it — the grid degrades to one space, never to a collision). */
|
|
289
|
-
const SHEET_GRID = [
|
|
290
|
-
[0, 1, 2],
|
|
291
|
-
[3, 4, 5],
|
|
292
|
-
[6, 7],
|
|
293
|
-
// R5: ctrl+r joins at index 8, so the tail shifts by one. The grid is
|
|
294
|
-
// a SECOND table that must agree with KEY_BINDINGS and nothing makes
|
|
295
|
-
// it — adding a binding without touching this silently drops the LAST
|
|
296
|
-
// one off the sheet, which is exactly what happened on the first
|
|
297
|
-
// attempt and exactly what the "ONE SOURCE" gate exists to catch.
|
|
298
|
-
[8, 9],
|
|
299
|
-
// UD-1's undo row shares this one now — the table has an even count
|
|
300
|
-
// again, so no binding needs a row to itself.
|
|
301
|
-
//
|
|
302
|
-
// E1 §1/§3 — three more, and the warning above earned itself a second
|
|
303
|
-
// time: adding them to KEY_BINDINGS without touching this grid pushed
|
|
304
|
-
// the last three off the sheet, and the "ONE SOURCE" gate caught it
|
|
305
|
-
// exactly as its comment predicted it would.
|
|
306
|
-
[10, 11],
|
|
307
|
-
[12, 13],
|
|
308
|
-
// ctrl+v joins the undo row's neighbour: the count is even again, so
|
|
309
|
-
// the last row pairs instead of standing alone. The warning three
|
|
310
|
-
// comments up applies unchanged — this grid does not update itself.
|
|
311
|
-
[14, 15],
|
|
312
|
-
];
|
|
313
|
-
const SHEET_STOPS = [
|
|
314
|
-
[16, 43],
|
|
315
|
-
[16, 43],
|
|
316
|
-
[36],
|
|
317
|
-
[36],
|
|
318
|
-
[36],
|
|
319
|
-
// E1 §1/§3 — the two-column rows the three new bindings land on.
|
|
320
|
-
// A THIRD table that must agree with the other two and nothing makes
|
|
321
|
-
// it: `SHEET_GRID` says which bindings share a row, this says where
|
|
322
|
-
// their second column starts, and `KEY_BINDINGS` says what they are.
|
|
323
|
-
// The grid's own comment warned about the pair; the trio is worse.
|
|
324
|
-
// The stop is 39 here because `alt+⌫ / alt+d delete word (ctrl+w too)`
|
|
325
|
-
// is the widest first cell on the sheet — a narrower stop packs the
|
|
326
|
-
// two cells together with a single space and the column disappears.
|
|
327
|
-
// The last row PAIRS now that ctrl+v joined (it had one binding and an
|
|
328
|
-
// empty stop list); it takes a stop like its neighbours so the second
|
|
329
|
-
// column lines up rather than following a single space.
|
|
330
|
-
[39],
|
|
331
|
-
[39],
|
|
332
|
-
];
|
|
333
|
-
/**
|
|
334
|
-
* TUI2-R1 (D) — the sheet, one screen, static.
|
|
335
|
-
*
|
|
336
|
-
* Rows CUT at the width rather than folding: the sheet's contract with
|
|
337
|
-
* the reader is "one screen", and a folded grid at 40 columns is two
|
|
338
|
-
* screens pretending to be one. A narrow terminal shows fewer columns
|
|
339
|
-
* of the same truth, which is the honest degradation.
|
|
340
|
-
*/
|
|
303
|
+
* DECLARED REVERSAL (the sheets round): the keys sheet's last row was
|
|
304
|
+
* `PANEL_KEYS_ROW` (`panels: ↑↓ move · ⏎ confirms · digits act on their
|
|
305
|
+
* row · t types`), with its own grid (`SHEET_GRID`, `SHEET_STOPS`) and
|
|
306
|
+
* a clause-dropping ladder (DC-2). Every panel's own key row now says
|
|
307
|
+
* that panel's keys (§8.2, P3, P4), the row had gone stale (`t types`
|
|
308
|
+
* left `/model` in P3), and the grid was a second and third table that
|
|
309
|
+
* had to agree with KEY_BINDINGS by hand. */
|
|
310
|
+
export const SHEET_CLOSE = "esc closes \u00b7 typing goes to the input";
|
|
341
311
|
/** R8b — the band's own opening row: a labelled rule at full width.
|
|
342
312
|
*
|
|
343
313
|
* Moved here from the @ picker, unchanged in every byte, because the
|
|
@@ -357,66 +327,54 @@ export function bandHeader(label, W) {
|
|
|
357
327
|
out += ch;
|
|
358
328
|
w += cw;
|
|
359
329
|
}
|
|
360
|
-
|
|
330
|
+
// Graphite §8.1 (R3a): the hairline in `line`, the band's name bold
|
|
331
|
+
// gold — the band names itself; off a known ground, one dim span
|
|
332
|
+
if (p.line === "" || p.gold === "")
|
|
333
|
+
return `${p.dim}${out}${p.reset}`;
|
|
334
|
+
const at = out.indexOf("\u2500 ") + 2;
|
|
335
|
+
// the NAME is the label up to its first ` · `; what follows (a scope,
|
|
336
|
+
// a count, a key) is the band's facts, dim
|
|
337
|
+
const sep = label.indexOf(" \u00b7 ");
|
|
338
|
+
const nameEnd = Math.min(out.length, at + (sep < 0 ? label.length : sep));
|
|
339
|
+
const labelEnd = Math.min(out.length, at + label.length);
|
|
340
|
+
return `${p.line}${out.slice(0, at)}${p.fgEnd}${p.bold}${p.gold}${out.slice(at, nameEnd)}${p.reset}${p.dim}${out.slice(nameEnd, labelEnd)}${p.reset}${p.line}${out.slice(labelEnd)}${p.fgEnd}`;
|
|
361
341
|
}
|
|
342
|
+
/**
|
|
343
|
+
* TUI2-R1 (D), the sheets round (owner, 2026-10-06) — the keys sheet: the
|
|
344
|
+
* band's named hairline, the bindings two to a row at the content edge,
|
|
345
|
+
* the key in ink and what it does dim, each column measured over the
|
|
346
|
+
* whole table so they line up, and the closing row. Narrower than two
|
|
347
|
+
* columns need, it is one column in the table's order. Rows CUT at the
|
|
348
|
+
* width rather than folding: the sheet's contract is "one screen".
|
|
349
|
+
*
|
|
350
|
+
* DECLARED REVERSAL: the sheet began at column 0 with the keys bold and
|
|
351
|
+
* hand-set column stops (DC-1/DC-3); §1.8's one content edge and the
|
|
352
|
+
* command list's name/description tones replace them.
|
|
353
|
+
*/
|
|
362
354
|
export function keysSheetRows(W) {
|
|
363
355
|
const p = palette();
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
const
|
|
372
|
-
const b = KEY_BINDINGS[i];
|
|
373
|
-
return b === undefined ? "" : `${b.keys} ${b.what}`;
|
|
374
|
-
};
|
|
375
|
-
// R8b — THE SHEET NAMES ITSELF, in the band vocabulary.
|
|
376
|
-
//
|
|
377
|
-
// Every other overlay does: `─── commands ───`, `─── files ───`,
|
|
378
|
-
// `─── sessions ───`, `── transcript · N folds ──`. This one opened
|
|
379
|
-
// with a bare bold word at column 0, which is the exact condition
|
|
380
|
-
// TUI2-R1.5 ⑦(b) named when it made the rule — with scrollback
|
|
381
|
-
// behind an overlay, nothing said where the surface began.
|
|
356
|
+
const width = (col, f) => Math.max(0, ...col.map((b) => displayWidth(f(b))));
|
|
357
|
+
const cell = (b, kw, dw) => `${b.keys}${" ".repeat(kw - displayWidth(b.keys) + 2)}${p.dim}${b.what}${p.reset}${dw === 0 ? "" : " ".repeat(dw - displayWidth(b.what))}`;
|
|
358
|
+
const left = KEY_BINDINGS.filter((_, i) => i % 2 === 0);
|
|
359
|
+
const right = KEY_BINDINGS.filter((_, i) => i % 2 === 1);
|
|
360
|
+
const kl = width(left, (b) => b.keys);
|
|
361
|
+
const dl = width(left, (b) => b.what);
|
|
362
|
+
const kr = width(right, (b) => b.keys);
|
|
363
|
+
const dr = width(right, (b) => b.what);
|
|
382
364
|
const rows = [bandHeader("keys", W)];
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
for (let c = 0; c < indexes.length; c += 1) {
|
|
388
|
-
row += cell(indexes[c]);
|
|
389
|
-
width += displayWidth(plainCell(indexes[c]));
|
|
390
|
-
const stop = SHEET_STOPS[r][c];
|
|
391
|
-
if (stop === undefined)
|
|
392
|
-
continue; // the last column pads nothing
|
|
393
|
-
const pad = Math.max(1, stop - width);
|
|
394
|
-
row += " ".repeat(pad);
|
|
395
|
-
width += pad;
|
|
365
|
+
if (2 + kl + 2 + dl + 3 + kr + 2 + dr <= W) {
|
|
366
|
+
for (let i = 0; i < left.length; i += 1) {
|
|
367
|
+
const r = right[i];
|
|
368
|
+
rows.push(` ${cell(left[i], kl, r === undefined ? 0 : dl)}${r === undefined ? "" : ` ${cell(r, kr, 0)}`}`);
|
|
396
369
|
}
|
|
397
|
-
rows.push(row);
|
|
398
370
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
* DC-2 — the panel row degrades by CLAUSE.
|
|
404
|
-
*
|
|
405
|
-
* The row is 76 columns of independent clauses joined by ` · `. At 72 it
|
|
406
|
-
* used to lose the tail of the last one, so `t types` became `t`: the
|
|
407
|
-
* reader was told a key existed and not told what it did, on a row that
|
|
408
|
-
* still looked complete. Dropping a whole clause says less; it never
|
|
409
|
-
* says something false. `cutLine`'s ellipsis is the floor below this, for
|
|
410
|
-
* a width that cannot hold even the first clause.
|
|
411
|
-
*/
|
|
412
|
-
function panelKeysRow(W) {
|
|
413
|
-
const clauses = PANEL_KEYS_ROW.split(" \u00b7 ");
|
|
414
|
-
for (let n = clauses.length; n > 1; n -= 1) {
|
|
415
|
-
const row = clauses.slice(0, n).join(" \u00b7 ");
|
|
416
|
-
if (displayWidth(row) <= W)
|
|
417
|
-
return row;
|
|
371
|
+
else {
|
|
372
|
+
const k = width(KEY_BINDINGS, (b) => b.keys);
|
|
373
|
+
for (const b of KEY_BINDINGS)
|
|
374
|
+
rows.push(` ${cell(b, k, 0)}`);
|
|
418
375
|
}
|
|
419
|
-
|
|
376
|
+
rows.push(` ${p.dim}${SHEET_CLOSE}${p.reset}`);
|
|
377
|
+
return rows.map((row) => cutLine(row, W));
|
|
420
378
|
}
|
|
421
379
|
/** TUI2-R1 (D) — the keys as ONE line, for /help. The same table the
|
|
422
380
|
* sheet renders, joined — so the two can disagree only by deleting a
|
|
@@ -450,6 +408,7 @@ const HELP_TABLE = [
|
|
|
450
408
|
// ADR-0058 (3e): the session's tasks, and the key that makes one
|
|
451
409
|
["/tasks", "list this session's background tasks; stop one or show its output"],
|
|
452
410
|
["/status", "show session id, event count, and context estimate"],
|
|
411
|
+
["/name", "name this session · /name shows it · /name - clears it"],
|
|
453
412
|
// 0.40.6: what kiso runs with, and where each value came from
|
|
454
413
|
["/settings", "show the settings in force, each value's source, and how to change it"],
|
|
455
414
|
// A command with no row is a command nobody can find. `/context` has
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-tui-cells",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.47.1",
|
|
4
4
|
"description": "kiso tui-cells \u2014 the components cell renderer (components, diff, width, the render slice). Zero runtime dependencies: input is data, output is bytes.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|