@vincemakes/kiso-tui 0.19.0 → 0.19.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.
@@ -73,8 +73,39 @@ export declare function askDescriptionStop(q: AskQuestion, W: number): number;
73
73
  * type-your-own line last. R2: the shape is the RULE's — rule,
74
74
  * title, header, body, affordance, rule. */
75
75
  export declare function askBlockRows(view: PanelView, state: AskRuntime, W: number, maxRows: number): string[];
76
- /** The status row's right-hand hint — the phase's keys. */
77
- export declare function askAffordance(state: AskRuntime): string;
76
+ /**
77
+ * R6/D2 the affordance names the FINISHER, and it names the real keys.
78
+ *
79
+ * Three defects lived in the one line this replaces:
80
+ * - it never mentioned ENTER, on the one panel shape where enter is the
81
+ * only way to finish. The owner could not find the finisher because
82
+ * the screen did not name it;
83
+ * - it said the same words in both modes, while a digit means two
84
+ * different things across them (take vs mark);
85
+ * - it hardcoded "1-4" whatever the option count was — the same defect
86
+ * REL-0152-D3 fixed in the input LEAD one function up, and missed
87
+ * here.
88
+ *
89
+ * The clauses are returned whole; `askAffordanceFit` below is what a
90
+ * width-bound caller uses.
91
+ */
92
+ export declare function askAffordance(state: AskRuntime, q?: AskQuestion): string;
93
+ /**
94
+ * The affordance at a width, by the DC-2 clause ladder.
95
+ *
96
+ * The row this serves used to be pushed UNCUT into the panel block
97
+ * (`askBlockRows`), unlike the approval's cutLine'd row — and panel rows
98
+ * go through `#checked`, which THROWS on any row wider than W
99
+ * (invariant ①). A multi-question ask on question 2 at W ≤ 41, or any
100
+ * ask at W ≤ 32, killed the renderer. So the ladder is not polish; it is
101
+ * the fix for a crash.
102
+ *
103
+ * The order gives way from the least load-bearing inward, and the LAST
104
+ * standing pair in both modes is the way through and the way out. In
105
+ * multi, `⏎ sends` is the fact whose absence caused this whole finding:
106
+ * it gives way never.
107
+ */
108
+ export declare function askAffordanceFit(state: AskRuntime, q: AskQuestion | undefined, W: number): string;
78
109
  /** The status row's left text — the ask's own line, with the walk. */
79
110
  export declare function askStatus(view: PanelView, state: AskRuntime): string;
80
111
  /** The input row's lead: the digit lead while picking, the typing lead
package/dist/ask-panel.js CHANGED
@@ -149,7 +149,27 @@ export function askKey(spec, state, key) {
149
149
  // row's neighbours answer with.
150
150
  if (state.cursor === customRow(q))
151
151
  return { state: { ...state, phase: "custom" } };
152
- return answered(state, state.qIndex) ? advance(spec, state) : { state };
152
+ if (answered(state, state.qIndex))
153
+ return advance(spec, state);
154
+ // R6/D2 — ENTER TAKES THE BAR'S ROW.
155
+ //
156
+ // It used to return `{ state }` here: the cursor is not a pick, so
157
+ // bar-on-option-1 + enter did NOTHING, in either mode, with no
158
+ // message. The approval panel ruled the opposite in TUI2-R3v2 ①
159
+ // ("the panel used to ignore the key every human presses first"),
160
+ // so the product had two selection models under one identical
161
+ // bar — and the owner's dogfood is that trap verbatim: bar to
162
+ // option 1, enter (silence), "do I really have to type 1?", digit
163
+ // (a mark appears), enter (it commits).
164
+ //
165
+ // Single-select takes the row and advances, which is what the bar
166
+ // has always promised. Multi MARKS the row and stays: the press
167
+ // becomes visible (◯ → ◉), which teaches the mode in one frame,
168
+ // and the second enter sends the set. A press that "did nothing"
169
+ // is the defect; a press that does the smallest true thing is the
170
+ // fix.
171
+ const taken = toggle(state, state.cursor, multi);
172
+ return multi ? { state: taken } : advance(spec, taken);
153
173
  }
154
174
  // SPACE selects at the cursor and NEVER commits — in either mode. It
155
175
  // used to answer-and-advance a single-select question, which made a
@@ -270,7 +290,10 @@ export function askBlockRows(view, state, W, maxRows) {
270
290
  // here), but the multi-select gesture it carried is INFORMATION and
271
291
  // rides the header instead — dropping it would have been a regression
272
292
  // wearing a restyle's clothes.
273
- const gesture = multi ? `${p.dim} · pick any space toggles${p.reset}` : "";
293
+ // R6/D2: the header names the MODE; the keys live on the keys row,
294
+ // once. It used to name `space toggles` there and nowhere say what
295
+ // finishes the set.
296
+ const gesture = multi ? `${p.dim} · pick any${p.reset}` : "";
274
297
  rows.push(` ${cutLine(`${p.dim}${header}${p.reset}${gesture}`, Math.max(1, W - 2))}`);
275
298
  rows.push("");
276
299
  const picks = state.picks[state.qIndex] ?? [];
@@ -311,7 +334,9 @@ export function askBlockRows(view, state, W, maxRows) {
311
334
  else {
312
335
  rows.push(...body);
313
336
  }
314
- rows.push(` ${p.dim}${askAffordance(state)}${p.reset}`);
337
+ // R6/D2: FITTED, and the two-space indent is inside the budget —
338
+ // the uncut push here is what made a narrow ask throw.
339
+ rows.push(` ${p.dim}${askAffordanceFit(state, q, Math.max(1, W - 2))}${p.reset}`);
315
340
  // R2, shared with the approval panel: the block closes with the SAME
316
341
  // dashed rule it opened with, and the same one the composer uses.
317
342
  // TUI2-R1.5 ⑪ had already replaced a two-cell `\u2514 ` stub with a
@@ -320,11 +345,68 @@ export function askBlockRows(view, state, W, maxRows) {
320
345
  rows.push(`${p.dim}${"\u2500".repeat(Math.max(0, W))}${p.reset}`);
321
346
  return rows;
322
347
  }
323
- /** The status row's right-hand hint — the phase's keys. */
324
- export function askAffordance(state) {
348
+ /**
349
+ * R6/D2 — the affordance names the FINISHER, and it names the real keys.
350
+ *
351
+ * Three defects lived in the one line this replaces:
352
+ * - it never mentioned ENTER, on the one panel shape where enter is the
353
+ * only way to finish. The owner could not find the finisher because
354
+ * the screen did not name it;
355
+ * - it said the same words in both modes, while a digit means two
356
+ * different things across them (take vs mark);
357
+ * - it hardcoded "1-4" whatever the option count was — the same defect
358
+ * REL-0152-D3 fixed in the input LEAD one function up, and missed
359
+ * here.
360
+ *
361
+ * The clauses are returned whole; `askAffordanceFit` below is what a
362
+ * width-bound caller uses.
363
+ */
364
+ export function askAffordance(state, q) {
325
365
  if (state.phase === "custom")
326
366
  return "enter answers · esc backs out";
327
- return state.qIndex > 0 ? "1-4 pick · t type · ← back · esc decline" : "1-4 pick · t type · esc decline";
367
+ const n = q?.options.length ?? 4;
368
+ const multi = q?.multiSelect === true;
369
+ const back = state.qIndex > 0 ? ["← back"] : [];
370
+ return (multi
371
+ ? ["↑↓ move", `space or 1-${n} marks`, "⏎ sends the set", "t types", ...back, "esc declines"]
372
+ : ["↑↓ move", "⏎ confirms", `1-${n} instant`, "t types", ...back, "esc declines"]).join(" · ");
373
+ }
374
+ /**
375
+ * The affordance at a width, by the DC-2 clause ladder.
376
+ *
377
+ * The row this serves used to be pushed UNCUT into the panel block
378
+ * (`askBlockRows`), unlike the approval's cutLine'd row — and panel rows
379
+ * go through `#checked`, which THROWS on any row wider than W
380
+ * (invariant ①). A multi-question ask on question 2 at W ≤ 41, or any
381
+ * ask at W ≤ 32, killed the renderer. So the ladder is not polish; it is
382
+ * the fix for a crash.
383
+ *
384
+ * The order gives way from the least load-bearing inward, and the LAST
385
+ * standing pair in both modes is the way through and the way out. In
386
+ * multi, `⏎ sends` is the fact whose absence caused this whole finding:
387
+ * it gives way never.
388
+ */
389
+ export function askAffordanceFit(state, q, W) {
390
+ if (state.phase === "custom")
391
+ return cutLine("enter answers · esc backs out", W);
392
+ const n = q?.options.length ?? 4;
393
+ const multi = q?.multiSelect === true;
394
+ const back = state.qIndex > 0 ? ["← back"] : [];
395
+ const act = multi ? `space or 1-${n} marks` : `1-${n} instant`;
396
+ const go = multi ? "⏎ sends the set" : "⏎ confirms";
397
+ const tiers = [
398
+ ["↑↓ move", act, go, "t types", ...back, "esc declines"],
399
+ ["↑↓ move", act, go, ...back, "esc declines"],
400
+ [act, go, ...back, "esc declines"],
401
+ [multi ? "space marks" : `1-${n} picks`, multi ? "⏎ sends" : "⏎ confirms", "esc declines"],
402
+ [multi ? "⏎ sends" : "⏎ confirms", "esc declines"],
403
+ ];
404
+ for (const tier of tiers) {
405
+ const row = tier.join(" · ");
406
+ if (visibleWidth(row) <= W)
407
+ return row;
408
+ }
409
+ return cutLine(tiers[tiers.length - 1].join(" · "), W);
328
410
  }
329
411
  /** The status row's left text — the ask's own line, with the walk. */
330
412
  export function askStatus(view, state) {
@@ -380,7 +462,7 @@ export function panelAffordance(view, phase, cursor, ask, pick, safer) {
380
462
  if (view.pick !== undefined && pick !== undefined)
381
463
  return pickAffordance(pick);
382
464
  if (view.ask !== undefined && ask !== undefined)
383
- return askAffordance(ask);
465
+ return askAffordance(ask, view.ask.questions[ask.qIndex]);
384
466
  return basePanelAffordance(view, phase, cursor, safer);
385
467
  }
386
468
  /** The whole panel state in one call — the compositor's four reads share
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-tui",
3
- "version": "0.19.0",
3
+ "version": "0.19.1",
4
4
  "description": "kiso tui — the pure terminal layer (cell renderer, dock, raw editor, diff, palette). Zero runtime dependencies: input is data, output is bytes.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -35,6 +35,6 @@
35
35
  },
36
36
  "homepage": "https://github.com/vincemakes/kiso/tree/main/packages/tui#readme",
37
37
  "dependencies": {
38
- "@vincemakes/kiso-tui-cells": "0.19.0"
38
+ "@vincemakes/kiso-tui-cells": "0.19.1"
39
39
  }
40
40
  }