pi-ask-tool-extension 0.2.4 → 0.2.6
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/package.json +1 -1
- package/src/ask-inline-note.ts +14 -4
- package/src/ask-inline-ui.ts +34 -5
- package/src/ask-tabs-ui.ts +80 -6
package/package.json
CHANGED
package/src/ask-inline-note.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { wrapTextWithAnsi } from "@mariozechner/pi-tui";
|
|
1
|
+
import { CURSOR_MARKER, wrapTextWithAnsi } from "@mariozechner/pi-tui";
|
|
2
2
|
|
|
3
3
|
const INLINE_NOTE_SEPARATOR = " — note: ";
|
|
4
4
|
const INLINE_EDIT_CURSOR_INVERT_ON = "\u001b[7m";
|
|
@@ -17,14 +17,19 @@ function clampCursorIndex(index: number, rawTextLength: number): number {
|
|
|
17
17
|
return Math.floor(index);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
function buildEditingInlineNote(
|
|
20
|
+
function buildEditingInlineNote(
|
|
21
|
+
rawNote: string,
|
|
22
|
+
editingCursorIndex?: number,
|
|
23
|
+
includeHardwareCursorMarker = false,
|
|
24
|
+
): string {
|
|
21
25
|
const cursorIndex = clampCursorIndex(editingCursorIndex ?? rawNote.length, rawNote.length);
|
|
22
26
|
const beforeCursor = sanitizeNoteForInlineDisplay(rawNote.slice(0, cursorIndex));
|
|
23
27
|
const rawCharAtCursor = rawNote.slice(cursorIndex, cursorIndex + 1);
|
|
24
28
|
const charAtCursor = sanitizeNoteForInlineDisplay(rawCharAtCursor) || " ";
|
|
25
29
|
const afterCursorStartIndex = rawCharAtCursor.length > 0 ? cursorIndex + 1 : cursorIndex;
|
|
26
30
|
const afterCursor = sanitizeNoteForInlineDisplay(rawNote.slice(afterCursorStartIndex));
|
|
27
|
-
const
|
|
31
|
+
const cursorMarker = includeHardwareCursorMarker ? CURSOR_MARKER : "";
|
|
32
|
+
const cursorCell = `${cursorMarker}${INLINE_EDIT_CURSOR_INVERT_ON}${charAtCursor}${INLINE_EDIT_CURSOR_INVERT_OFF}`;
|
|
28
33
|
return `${beforeCursor}${cursorCell}${afterCursor}`;
|
|
29
34
|
}
|
|
30
35
|
|
|
@@ -48,6 +53,7 @@ export function buildOptionLabelWithInlineNote(
|
|
|
48
53
|
isEditingNote: boolean,
|
|
49
54
|
maxInlineLabelLength?: number,
|
|
50
55
|
editingCursorIndex?: number,
|
|
56
|
+
includeHardwareCursorMarker = false,
|
|
51
57
|
): string {
|
|
52
58
|
const sanitizedNote = sanitizeNoteForInlineDisplay(rawNote);
|
|
53
59
|
if (!isEditingNote && sanitizedNote.trim().length === 0) {
|
|
@@ -55,7 +61,9 @@ export function buildOptionLabelWithInlineNote(
|
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
const labelPrefix = `${baseOptionLabel}${INLINE_NOTE_SEPARATOR}`;
|
|
58
|
-
const inlineNote = isEditingNote
|
|
64
|
+
const inlineNote = isEditingNote
|
|
65
|
+
? buildEditingInlineNote(rawNote, editingCursorIndex, includeHardwareCursorMarker)
|
|
66
|
+
: sanitizedNote.trim();
|
|
59
67
|
const inlineLabel = `${labelPrefix}${inlineNote}`;
|
|
60
68
|
|
|
61
69
|
if (maxInlineLabelLength == null) {
|
|
@@ -74,6 +82,7 @@ export function buildWrappedOptionLabelWithInlineNote(
|
|
|
74
82
|
maxInlineLabelLength: number,
|
|
75
83
|
wrapPadding = INLINE_NOTE_WRAP_PADDING,
|
|
76
84
|
editingCursorIndex?: number,
|
|
85
|
+
includeHardwareCursorMarker = false,
|
|
77
86
|
): string[] {
|
|
78
87
|
const inlineLabel = buildOptionLabelWithInlineNote(
|
|
79
88
|
baseOptionLabel,
|
|
@@ -81,6 +90,7 @@ export function buildWrappedOptionLabelWithInlineNote(
|
|
|
81
90
|
isEditingNote,
|
|
82
91
|
undefined,
|
|
83
92
|
editingCursorIndex,
|
|
93
|
+
includeHardwareCursorMarker,
|
|
84
94
|
);
|
|
85
95
|
const sanitizedWrapPadding = Number.isFinite(wrapPadding) ? Math.max(0, Math.floor(wrapPadding)) : 0;
|
|
86
96
|
const sanitizedMaxInlineLabelLength = Number.isFinite(maxInlineLabelLength)
|
package/src/ask-inline-ui.ts
CHANGED
|
@@ -111,6 +111,12 @@ export async function askSingleQuestionWithInlineNote(
|
|
|
111
111
|
noteEditor.setText(getRawNoteForOption(cursorOptionIndex));
|
|
112
112
|
};
|
|
113
113
|
|
|
114
|
+
const openNoteEditorForCurrentOption = () => {
|
|
115
|
+
if (isNoteEditorOpen) return;
|
|
116
|
+
isNoteEditorOpen = true;
|
|
117
|
+
loadCurrentNoteIntoEditor();
|
|
118
|
+
};
|
|
119
|
+
|
|
114
120
|
const saveCurrentNoteFromEditor = (value: string) => {
|
|
115
121
|
noteByOptionIndex.set(cursorOptionIndex, value);
|
|
116
122
|
};
|
|
@@ -181,6 +187,7 @@ export async function askSingleQuestionWithInlineNote(
|
|
|
181
187
|
Math.max(1, width - prefixWidth),
|
|
182
188
|
INLINE_NOTE_WRAP_PADDING,
|
|
183
189
|
isEditingThisOption ? activeEditingCursorIndex : undefined,
|
|
190
|
+
isEditingThisOption,
|
|
184
191
|
);
|
|
185
192
|
const continuationPrefix = " ".repeat(prefixWidth);
|
|
186
193
|
addLine(`${cursorPrefix}${theme.fg(optionColor, `${markerText}${wrappedInlineLabelLines[0] ?? ""}`)}`);
|
|
@@ -217,25 +224,38 @@ export async function askSingleQuestionWithInlineNote(
|
|
|
217
224
|
requestUiRerender();
|
|
218
225
|
return;
|
|
219
226
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
227
|
+
|
|
228
|
+
if (
|
|
229
|
+
(matchesKey(data, Key.up) || matchesKey(data, Key.down)) &&
|
|
230
|
+
getTrimmedNoteForOption(cursorOptionIndex).length === 0
|
|
231
|
+
) {
|
|
232
|
+
isNoteEditorOpen = false;
|
|
233
|
+
} else {
|
|
234
|
+
noteEditor.handleInput(data);
|
|
235
|
+
requestUiRerender();
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
223
238
|
}
|
|
224
239
|
|
|
225
240
|
if (matchesKey(data, Key.up)) {
|
|
226
241
|
cursorOptionIndex = Math.max(0, cursorOptionIndex - 1);
|
|
242
|
+
if (selectableOptionLabels[cursorOptionIndex] === OTHER_OPTION) {
|
|
243
|
+
openNoteEditorForCurrentOption();
|
|
244
|
+
}
|
|
227
245
|
requestUiRerender();
|
|
228
246
|
return;
|
|
229
247
|
}
|
|
230
248
|
if (matchesKey(data, Key.down)) {
|
|
231
249
|
cursorOptionIndex = Math.min(selectableOptionLabels.length - 1, cursorOptionIndex + 1);
|
|
250
|
+
if (selectableOptionLabels[cursorOptionIndex] === OTHER_OPTION) {
|
|
251
|
+
openNoteEditorForCurrentOption();
|
|
252
|
+
}
|
|
232
253
|
requestUiRerender();
|
|
233
254
|
return;
|
|
234
255
|
}
|
|
235
256
|
|
|
236
257
|
if (matchesKey(data, Key.tab)) {
|
|
237
|
-
|
|
238
|
-
loadCurrentNoteIntoEditor();
|
|
258
|
+
openNoteEditorForCurrentOption();
|
|
239
259
|
requestUiRerender();
|
|
240
260
|
return;
|
|
241
261
|
}
|
|
@@ -257,10 +277,19 @@ export async function askSingleQuestionWithInlineNote(
|
|
|
257
277
|
|
|
258
278
|
if (matchesKey(data, Key.escape)) {
|
|
259
279
|
done({ cancelled: true });
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (selectableOptionLabels[cursorOptionIndex] === OTHER_OPTION) {
|
|
284
|
+
openNoteEditorForCurrentOption();
|
|
285
|
+
noteEditor.handleInput(data);
|
|
286
|
+
requestUiRerender();
|
|
287
|
+
return;
|
|
260
288
|
}
|
|
261
289
|
};
|
|
262
290
|
|
|
263
291
|
return {
|
|
292
|
+
focused: true,
|
|
264
293
|
render,
|
|
265
294
|
invalidate: () => {
|
|
266
295
|
cachedRenderedLines = undefined;
|
package/src/ask-tabs-ui.ts
CHANGED
|
@@ -388,6 +388,7 @@ export async function askQuestionsWithTabs(
|
|
|
388
388
|
Math.max(1, width - prefixWidth),
|
|
389
389
|
INLINE_NOTE_WRAP_PADDING,
|
|
390
390
|
isEditingThisOption ? activeEditingCursorIndex : undefined,
|
|
391
|
+
isEditingThisOption,
|
|
391
392
|
);
|
|
392
393
|
const continuationPrefix = " ".repeat(prefixWidth);
|
|
393
394
|
addLine(`${cursorPrefix}${theme.fg(optionColor, `${markerText}${wrappedInlineLabelLines[0] ?? ""}`)}`);
|
|
@@ -404,12 +405,12 @@ export async function askQuestionsWithTabs(
|
|
|
404
405
|
addLine(
|
|
405
406
|
theme.fg(
|
|
406
407
|
"dim",
|
|
407
|
-
" ↑↓ move •
|
|
408
|
+
" ↑↓ move • Space toggle/select • Enter next • Tab add note • ←/→ switch tabs • Esc cancel",
|
|
408
409
|
),
|
|
409
410
|
);
|
|
410
411
|
} else {
|
|
411
412
|
addLine(
|
|
412
|
-
theme.fg("dim", " ↑↓ move • Enter select • Tab add note • ←/→ switch tabs • Esc cancel"),
|
|
413
|
+
theme.fg("dim", " ↑↓ move • Space/Enter select • Tab add note • ←/→ switch tabs • Esc cancel"),
|
|
413
414
|
);
|
|
414
415
|
}
|
|
415
416
|
}
|
|
@@ -449,19 +450,44 @@ export async function askQuestionsWithTabs(
|
|
|
449
450
|
requestUiRerender();
|
|
450
451
|
return;
|
|
451
452
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
453
|
+
|
|
454
|
+
const questionIndex = getActiveQuestionIndex();
|
|
455
|
+
const cursorOptionIndex = questionIndex == null ? 0 : cursorOptionIndexByQuestion[questionIndex];
|
|
456
|
+
const noteIsEmpty = questionIndex == null || getTrimmedQuestionNote(questionIndex, cursorOptionIndex).length === 0;
|
|
457
|
+
if (
|
|
458
|
+
noteIsEmpty &&
|
|
459
|
+
(matchesKey(data, Key.up) || matchesKey(data, Key.down) || matchesKey(data, Key.left) || matchesKey(data, Key.right))
|
|
460
|
+
) {
|
|
461
|
+
isNoteEditorOpen = false;
|
|
462
|
+
} else {
|
|
463
|
+
noteEditor.handleInput(data);
|
|
464
|
+
requestUiRerender();
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
455
467
|
}
|
|
456
468
|
|
|
457
469
|
if (matchesKey(data, Key.left)) {
|
|
458
470
|
activeTabIndex = (activeTabIndex - 1 + preparedQuestions.length + 1) % (preparedQuestions.length + 1);
|
|
471
|
+
if (getActiveQuestionIndex() != null) {
|
|
472
|
+
const questionIndex = getActiveQuestionIndex() as number;
|
|
473
|
+
if (preparedQuestions[questionIndex].options[cursorOptionIndexByQuestion[questionIndex]] === OTHER_OPTION) {
|
|
474
|
+
openNoteEditorForActiveOption();
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
459
478
|
requestUiRerender();
|
|
460
479
|
return;
|
|
461
480
|
}
|
|
462
481
|
|
|
463
482
|
if (matchesKey(data, Key.right)) {
|
|
464
483
|
activeTabIndex = (activeTabIndex + 1) % (preparedQuestions.length + 1);
|
|
484
|
+
if (getActiveQuestionIndex() != null) {
|
|
485
|
+
const questionIndex = getActiveQuestionIndex() as number;
|
|
486
|
+
if (preparedQuestions[questionIndex].options[cursorOptionIndexByQuestion[questionIndex]] === OTHER_OPTION) {
|
|
487
|
+
openNoteEditorForActiveOption();
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
465
491
|
requestUiRerender();
|
|
466
492
|
return;
|
|
467
493
|
}
|
|
@@ -482,6 +508,10 @@ export async function askQuestionsWithTabs(
|
|
|
482
508
|
|
|
483
509
|
if (matchesKey(data, Key.up)) {
|
|
484
510
|
cursorOptionIndexByQuestion[questionIndex] = Math.max(0, cursorOptionIndexByQuestion[questionIndex] - 1);
|
|
511
|
+
if (preparedQuestion.options[cursorOptionIndexByQuestion[questionIndex]] === OTHER_OPTION) {
|
|
512
|
+
openNoteEditorForActiveOption();
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
485
515
|
requestUiRerender();
|
|
486
516
|
return;
|
|
487
517
|
}
|
|
@@ -491,6 +521,10 @@ export async function askQuestionsWithTabs(
|
|
|
491
521
|
preparedQuestion.options.length - 1,
|
|
492
522
|
cursorOptionIndexByQuestion[questionIndex] + 1,
|
|
493
523
|
);
|
|
524
|
+
if (preparedQuestion.options[cursorOptionIndexByQuestion[questionIndex]] === OTHER_OPTION) {
|
|
525
|
+
openNoteEditorForActiveOption();
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
494
528
|
requestUiRerender();
|
|
495
529
|
return;
|
|
496
530
|
}
|
|
@@ -500,7 +534,7 @@ export async function askQuestionsWithTabs(
|
|
|
500
534
|
return;
|
|
501
535
|
}
|
|
502
536
|
|
|
503
|
-
if (matchesKey(data, Key.
|
|
537
|
+
if (matchesKey(data, Key.space)) {
|
|
504
538
|
const cursorOptionIndex = cursorOptionIndexByQuestion[questionIndex];
|
|
505
539
|
|
|
506
540
|
if (preparedQuestion.multi) {
|
|
@@ -533,6 +567,37 @@ export async function askQuestionsWithTabs(
|
|
|
533
567
|
return;
|
|
534
568
|
}
|
|
535
569
|
|
|
570
|
+
requestUiRerender();
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
if (matchesKey(data, Key.enter)) {
|
|
575
|
+
const cursorOptionIndex = cursorOptionIndexByQuestion[questionIndex];
|
|
576
|
+
|
|
577
|
+
if (preparedQuestion.multi) {
|
|
578
|
+
if (
|
|
579
|
+
cursorOptionIndex === preparedQuestion.otherOptionIndex &&
|
|
580
|
+
selectedOptionIndexesByQuestion[questionIndex].includes(cursorOptionIndex) &&
|
|
581
|
+
getTrimmedQuestionNote(questionIndex, cursorOptionIndex).length === 0
|
|
582
|
+
) {
|
|
583
|
+
openNoteEditorForActiveOption();
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
advanceToNextTabOrSubmit();
|
|
588
|
+
requestUiRerender();
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
selectedOptionIndexesByQuestion[questionIndex] = [cursorOptionIndex];
|
|
593
|
+
if (
|
|
594
|
+
cursorOptionIndex === preparedQuestion.otherOptionIndex &&
|
|
595
|
+
getTrimmedQuestionNote(questionIndex, cursorOptionIndex).length === 0
|
|
596
|
+
) {
|
|
597
|
+
openNoteEditorForActiveOption();
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
|
|
536
601
|
advanceToNextTabOrSubmit();
|
|
537
602
|
requestUiRerender();
|
|
538
603
|
return;
|
|
@@ -540,10 +605,19 @@ export async function askQuestionsWithTabs(
|
|
|
540
605
|
|
|
541
606
|
if (matchesKey(data, Key.escape)) {
|
|
542
607
|
done(createTabsUiStateSnapshot(true, selectedOptionIndexesByQuestion, noteByQuestionByOption));
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
if (preparedQuestion.options[cursorOptionIndexByQuestion[questionIndex]] === OTHER_OPTION) {
|
|
612
|
+
openNoteEditorForActiveOption();
|
|
613
|
+
noteEditor.handleInput(data);
|
|
614
|
+
requestUiRerender();
|
|
615
|
+
return;
|
|
543
616
|
}
|
|
544
617
|
};
|
|
545
618
|
|
|
546
619
|
return {
|
|
620
|
+
focused: true,
|
|
547
621
|
render,
|
|
548
622
|
invalidate: () => {
|
|
549
623
|
cachedRenderedLines = undefined;
|