@mrclrchtr/supi-ask-user 1.5.0 → 1.6.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-core",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "SuPi core — shared infrastructure for SuPi extensions (XML context tags, config system)",
5
5
  "license": "MIT",
6
6
  "repository": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-ask-user",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "SuPi ask-user extension — rich questionnaire UI for structured agent-user decisions",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -21,7 +21,7 @@
21
21
  ],
22
22
  "main": "src/api.ts",
23
23
  "dependencies": {
24
- "@mrclrchtr/supi-core": "1.5.0"
24
+ "@mrclrchtr/supi-core": "1.6.0"
25
25
  },
26
26
  "bundledDependencies": [
27
27
  "@mrclrchtr/supi-core"
@@ -24,6 +24,7 @@ import {
24
24
  choiceRowValue,
25
25
  defaultChoiceRowIndex,
26
26
  type FocusTarget,
27
+ noteTargetLabel,
27
28
  type OverlayAction,
28
29
  type OverlayMode,
29
30
  previewOptionIndexForRows,
@@ -32,7 +33,6 @@ import type { OverlayArgs } from "./types.ts";
32
33
 
33
34
  export class AskUserOverlay implements Component, Focusable {
34
35
  focused = false;
35
-
36
36
  private readonly editor: Editor;
37
37
  private focus: FocusTarget = "choices";
38
38
  private mode: OverlayMode = "choice";
@@ -40,7 +40,6 @@ export class AskUserOverlay implements Component, Focusable {
40
40
  private cachedWidth: number | undefined;
41
41
  private cachedLines: string[] | undefined;
42
42
  private readonly onAbort: () => void;
43
-
44
43
  private choiceRows: ChoiceRow[] = [];
45
44
  private choiceRowIndex = 0;
46
45
  private previewOptionIndex = 0;
@@ -48,7 +47,6 @@ export class AskUserOverlay implements Component, Focusable {
48
47
  private textActions: Array<{ action: OverlayAction; label: string }> = [];
49
48
  private actionIndex = 0;
50
49
  private actionList: SelectList | undefined;
51
-
52
50
  constructor(private readonly args: OverlayArgs) {
53
51
  this.editor = new Editor(args.tui, makeEditorTheme(args.theme));
54
52
  this.editor.onSubmit = (value) => this.handleEditorSubmit(value);
@@ -79,6 +77,10 @@ export class AskUserOverlay implements Component, Focusable {
79
77
  this.args.controller.currentQuestion,
80
78
  this.previewOptionIndex,
81
79
  ),
80
+ noteTargetLabel:
81
+ this.mode === "note-input"
82
+ ? noteTargetLabel(this.args.controller, this.choiceRows, this.choiceRowIndex)
83
+ : undefined,
82
84
  });
83
85
  return this.cachedLines;
84
86
  }
@@ -155,7 +157,6 @@ export class AskUserOverlay implements Component, Focusable {
155
157
 
156
158
  this.choiceList.handleInput(data);
157
159
  }
158
-
159
160
  private handleActionKey(data: string): void {
160
161
  const question = this.args.controller.currentQuestion;
161
162
  if (!this.actionList) return;
@@ -390,7 +391,6 @@ export class AskUserOverlay implements Component, Focusable {
390
391
  this.cachedLines = undefined;
391
392
  this.args.tui.requestRender();
392
393
  }
393
-
394
394
  private finish(): void {
395
395
  if (this.closed) return;
396
396
  this.closed = true;
@@ -29,6 +29,7 @@ export interface RenderOverlayFrameArgs {
29
29
  actionList: SelectList | undefined;
30
30
  textActionLabels: string[];
31
31
  previewText?: string;
32
+ noteTargetLabel?: string;
32
33
  }
33
34
 
34
35
  export function renderOverlayFrame(args: RenderOverlayFrameArgs): string[] {
@@ -191,7 +192,9 @@ function renderEditorLines(args: RenderOverlayFrameArgs, width: number): string[
191
192
  : args.mode === "custom-input"
192
193
  ? "Other answer"
193
194
  : args.mode === "note-input"
194
- ? "Option note"
195
+ ? args.noteTargetLabel
196
+ ? `Note for: ${args.noteTargetLabel}`
197
+ : "Option note"
195
198
  : "Your answer";
196
199
 
197
200
  const lines = [args.theme.fg("accent", label), ...args.editor.render(Math.max(20, width - 1))];
@@ -150,19 +150,34 @@ export function choiceRowValue(row: ChoiceRow): string {
150
150
  return row.kind === "option" ? `option:${row.optionIndex}` : `action:${row.action}`;
151
151
  }
152
152
 
153
+ export function noteTargetLabel(
154
+ controller: AskUserController,
155
+ choiceRows: ChoiceRow[],
156
+ choiceRowIndex: number,
157
+ ): string | undefined {
158
+ const question = controller.currentQuestion;
159
+ if (question.type !== "choice") return undefined;
160
+ const row = choiceRows[choiceRowIndex];
161
+ if (row?.kind !== "option") return undefined;
162
+ return question.options[row.optionIndex]?.label;
163
+ }
164
+
153
165
  function renderOptionRow(args: {
154
166
  option: { label: string; description?: string };
155
167
  labelText: string;
168
+ hasNote: boolean;
156
169
  isSelected: boolean;
157
170
  theme: Theme;
158
171
  width: number;
159
172
  }): string[] {
160
- const { theme, isSelected, labelText, width, option } = args;
173
+ const { theme, isSelected, labelText, hasNote, width, option } = args;
161
174
  const prefix = isSelected ? "\u2192 " : " ";
175
+ const baseText = isSelected
176
+ ? theme.fg("accent", `${prefix}${labelText}`)
177
+ : `${prefix}${labelText}`;
178
+ const noteSuffix = hasNote ? ` ${theme.fg("accent", "[note]")}` : "";
162
179
 
163
- const lines: string[] = [
164
- isSelected ? theme.fg("accent", `${prefix}${labelText}`) : `${prefix}${labelText}`,
165
- ];
180
+ const lines: string[] = [`${baseText}${noteSuffix}`];
166
181
 
167
182
  if (option.description) {
168
183
  const descWidth = Math.max(10, width - 2);
@@ -200,9 +215,8 @@ function prepareOptionLabel(
200
215
  option: { label: string },
201
216
  marker: string,
202
217
  recommended: boolean,
203
- hasNote: boolean,
204
218
  ): string {
205
- return `${marker} ${option.label}${recommended ? " (recommended)" : ""}${hasNote ? " [note]" : ""}`;
219
+ return `${marker} ${option.label}${recommended ? " (recommended)" : ""}`;
206
220
  }
207
221
 
208
222
  export function renderChoiceList(args: {
@@ -228,9 +242,9 @@ export function renderChoiceList(args: {
228
242
  const marker = prepareOptionMarker(question, row.optionIndex, selectedIndexes);
229
243
  const recommended = question.recommendedIndexes.includes(row.optionIndex);
230
244
  const hasNote = !!controller.getChoiceOptionNote(question.id, option.value);
231
- const labelText = prepareOptionLabel(option, marker, recommended, hasNote);
245
+ const labelText = prepareOptionLabel(option, marker, recommended);
232
246
 
233
- lines.push(...renderOptionRow({ option, labelText, isSelected, theme, width }));
247
+ lines.push(...renderOptionRow({ option, labelText, hasNote, isSelected, theme, width }));
234
248
  } else {
235
249
  const answer = controller.getAnswer(question.id);
236
250
  const actionLabelText =