@hyperfrontend/questions 0.1.0 → 0.2.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/CHANGELOG.md +17 -1
- package/README.md +19 -7
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/math/index.cjs.js +7 -0
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/math/index.esm.js +5 -0
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/object/index.cjs.js +5 -0
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/object/index.esm.js +4 -0
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/promise/index.cjs.js +6 -0
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/promise/index.esm.js +5 -0
- package/index.cjs.js +175 -151
- package/index.d.ts +331 -9
- package/index.d.ts.map +1 -1
- package/index.esm.js +143 -120
- package/package.json +13 -3
- package/index.cjs.js.map +0 -1
- package/index.esm.js.map +0 -1
- package/render.d.ts +0 -113
- package/render.d.ts.map +0 -1
- package/terminal.d.ts +0 -109
- package/terminal.d.ts.map +0 -1
- package/types.d.ts +0 -108
- package/types.d.ts.map +0 -1
package/index.cjs.js
CHANGED
|
@@ -1,68 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
*
|
|
8
|
-
* These references are captured at module initialization time to protect against
|
|
9
|
-
* prototype pollution attacks. Import only what you need for tree-shaking.
|
|
10
|
-
*
|
|
11
|
-
* @module @hyperfrontend/immutable-api-utils/built-in-copy/object
|
|
12
|
-
*/
|
|
13
|
-
const _Object = globalThis.Object;
|
|
14
|
-
/**
|
|
15
|
-
* (Safe copy) Prevents modification of existing property attributes and values,
|
|
16
|
-
* and prevents the addition of new properties.
|
|
17
|
-
*/
|
|
18
|
-
const freeze = _Object.freeze;
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Safe Promise factory and bound static methods.
|
|
22
|
-
*
|
|
23
|
-
* @module @hyperfrontend/immutable-api-utils/built-in-copy/promise
|
|
24
|
-
*/
|
|
25
|
-
/* eslint-disable workspace/lib-require-jsdoc-example */
|
|
26
|
-
const _Promise = globalThis.Promise;
|
|
27
|
-
const _Reflect = globalThis.Reflect;
|
|
28
|
-
/**
|
|
29
|
-
* (Safe copy) Creates a new Promise using the captured Promise constructor.
|
|
30
|
-
* Use this instead of `new Promise()`.
|
|
31
|
-
*
|
|
32
|
-
* @param executor - The executor function.
|
|
33
|
-
* @returns A new Promise instance.
|
|
34
|
-
*/
|
|
35
|
-
const createPromise = (executor) => _Reflect.construct(_Promise, [executor]);
|
|
36
|
-
/**
|
|
37
|
-
* (Safe copy) Returns a Promise that resolves with the given value.
|
|
38
|
-
*/
|
|
39
|
-
_Promise.resolve.bind(_Promise);
|
|
40
|
-
/**
|
|
41
|
-
* (Safe copy) Returns a Promise that rejects with the given reason.
|
|
42
|
-
*/
|
|
43
|
-
_Promise.reject.bind(_Promise);
|
|
44
|
-
/**
|
|
45
|
-
* (Safe copy) Returns a Promise that resolves when all promises resolve.
|
|
46
|
-
*/
|
|
47
|
-
_Promise.all.bind(_Promise);
|
|
48
|
-
/**
|
|
49
|
-
* (Safe copy) Returns a Promise that resolves/rejects with the first settled promise.
|
|
50
|
-
*/
|
|
51
|
-
_Promise.race.bind(_Promise);
|
|
52
|
-
/**
|
|
53
|
-
* (Safe copy) Returns a Promise that resolves when all promises settle.
|
|
54
|
-
*/
|
|
55
|
-
_Promise.allSettled.bind(_Promise);
|
|
56
|
-
/**
|
|
57
|
-
* (Safe copy) Returns a Promise that resolves with the first fulfilled promise.
|
|
58
|
-
*/
|
|
59
|
-
_Promise.any.bind(_Promise);
|
|
60
|
-
/**
|
|
61
|
-
* (Safe copy) Creates a Promise along with its resolve and reject functions.
|
|
62
|
-
* Note: Available only in ES2024+ environments.
|
|
63
|
-
*/
|
|
64
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
65
|
-
_Promise.withResolvers?.bind(_Promise);
|
|
3
|
+
const index_cjs_js = require('./_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/object/index.cjs.js');
|
|
4
|
+
const node_readline = require('node:readline');
|
|
5
|
+
const index_cjs_js$1 = require('./_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/promise/index.cjs.js');
|
|
6
|
+
const index_cjs_js$2 = require('./_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/math/index.cjs.js');
|
|
66
7
|
|
|
67
8
|
/**
|
|
68
9
|
* Terminal I/O utilities using Node.js readline.
|
|
@@ -72,7 +13,7 @@ _Promise.withResolvers?.bind(_Promise);
|
|
|
72
13
|
/**
|
|
73
14
|
* Key codes for terminal navigation.
|
|
74
15
|
*/
|
|
75
|
-
const Key = freeze({
|
|
16
|
+
const Key = index_cjs_js.freeze({
|
|
76
17
|
Up: '\x1B[A',
|
|
77
18
|
Down: '\x1B[B',
|
|
78
19
|
Left: '\x1B[D',
|
|
@@ -88,7 +29,7 @@ const Key = freeze({
|
|
|
88
29
|
/**
|
|
89
30
|
* ANSI escape codes for terminal styling.
|
|
90
31
|
*/
|
|
91
|
-
const Ansi = freeze({
|
|
32
|
+
const Ansi = index_cjs_js.freeze({
|
|
92
33
|
/** Clear entire line */
|
|
93
34
|
ClearLine: '\x1B[2K',
|
|
94
35
|
/** Move cursor to start of line */
|
|
@@ -107,6 +48,13 @@ const Ansi = freeze({
|
|
|
107
48
|
* @returns ANSI escape sequence string
|
|
108
49
|
*/
|
|
109
50
|
cursorDown: (n) => `\x1B[${n}B`,
|
|
51
|
+
/**
|
|
52
|
+
* Generates ANSI escape code to move cursor left by specified columns.
|
|
53
|
+
*
|
|
54
|
+
* @param n - Number of columns to move left
|
|
55
|
+
* @returns ANSI escape sequence string
|
|
56
|
+
*/
|
|
57
|
+
cursorLeft: (n) => `\x1B[${n}D`,
|
|
110
58
|
/** Escape code to hide cursor */
|
|
111
59
|
HideCursor: '\x1B[?25l',
|
|
112
60
|
/** Escape code to show cursor */
|
|
@@ -129,6 +77,8 @@ const Ansi = freeze({
|
|
|
129
77
|
Green: '\x1B[32m',
|
|
130
78
|
/** Escape code for yellow foreground */
|
|
131
79
|
Yellow: '\x1B[33m',
|
|
80
|
+
/** Escape code for red foreground */
|
|
81
|
+
Red: '\x1B[31m',
|
|
132
82
|
/** Escape code for gray foreground */
|
|
133
83
|
Gray: '\x1B[90m',
|
|
134
84
|
});
|
|
@@ -160,7 +110,7 @@ function createTerminal(config = {}) {
|
|
|
160
110
|
const write = (text) => {
|
|
161
111
|
output.write(text);
|
|
162
112
|
};
|
|
163
|
-
const readKey = () => createPromise((resolve) => {
|
|
113
|
+
const readKey = () => index_cjs_js$1.createPromise((resolve) => {
|
|
164
114
|
const wasRaw = input.isRaw;
|
|
165
115
|
if (input.setRawMode) {
|
|
166
116
|
input.setRawMode(true);
|
|
@@ -178,7 +128,7 @@ function createTerminal(config = {}) {
|
|
|
178
128
|
};
|
|
179
129
|
input.once('data', onData);
|
|
180
130
|
});
|
|
181
|
-
const readLine = () => createPromise((resolve) => {
|
|
131
|
+
const readLine = () => index_cjs_js$1.createPromise((resolve) => {
|
|
182
132
|
const readline = getReadline();
|
|
183
133
|
readline.once('line', (line) => {
|
|
184
134
|
resolve(line);
|
|
@@ -205,7 +155,7 @@ function createTerminal(config = {}) {
|
|
|
205
155
|
}
|
|
206
156
|
write(Ansi.ShowCursor);
|
|
207
157
|
};
|
|
208
|
-
return freeze({
|
|
158
|
+
return index_cjs_js.freeze({
|
|
209
159
|
write,
|
|
210
160
|
readKey,
|
|
211
161
|
readLine,
|
|
@@ -226,7 +176,7 @@ function createTerminal(config = {}) {
|
|
|
226
176
|
/**
|
|
227
177
|
* Unicode symbols for prompt rendering.
|
|
228
178
|
*/
|
|
229
|
-
const Symbol = freeze({
|
|
179
|
+
const Symbol = index_cjs_js.freeze({
|
|
230
180
|
Pointer: '❯',
|
|
231
181
|
Radio: '◯',
|
|
232
182
|
RadioSelected: '◉',
|
|
@@ -240,7 +190,7 @@ const Symbol = freeze({
|
|
|
240
190
|
/**
|
|
241
191
|
* Style functions for ANSI text formatting.
|
|
242
192
|
*/
|
|
243
|
-
const style = freeze({
|
|
193
|
+
const style = index_cjs_js.freeze({
|
|
244
194
|
/**
|
|
245
195
|
* Applies bold styling to text.
|
|
246
196
|
*
|
|
@@ -276,6 +226,13 @@ const style = freeze({
|
|
|
276
226
|
* @returns Text wrapped in yellow ANSI codes
|
|
277
227
|
*/
|
|
278
228
|
yellow: (text) => `${Ansi.Yellow}${text}${Ansi.Reset}`,
|
|
229
|
+
/**
|
|
230
|
+
* Applies red color to text.
|
|
231
|
+
*
|
|
232
|
+
* @param text - Text to style
|
|
233
|
+
* @returns Text wrapped in red ANSI codes
|
|
234
|
+
*/
|
|
235
|
+
red: (text) => `${Ansi.Red}${text}${Ansi.Reset}`,
|
|
279
236
|
/**
|
|
280
237
|
* Applies gray color to text.
|
|
281
238
|
*
|
|
@@ -337,7 +294,7 @@ function renderCancelled() {
|
|
|
337
294
|
/**
|
|
338
295
|
* Result state of a prompt interaction.
|
|
339
296
|
*/
|
|
340
|
-
const PromptResult = freeze({
|
|
297
|
+
const PromptResult = index_cjs_js.freeze({
|
|
341
298
|
/** User submitted a value */
|
|
342
299
|
Submitted: 'submitted',
|
|
343
300
|
/** User cancelled the prompt (Ctrl+C) */
|
|
@@ -403,22 +360,22 @@ async function confirm(config) {
|
|
|
403
360
|
term.write(Ansi.CursorStart + Ansi.ClearLine);
|
|
404
361
|
term.write(renderMessage(config.message) + renderCancelled() + '\n');
|
|
405
362
|
term.close();
|
|
406
|
-
return freeze({ result: PromptResult.Cancelled, value: undefined });
|
|
363
|
+
return index_cjs_js.freeze({ result: PromptResult.Cancelled, value: undefined });
|
|
407
364
|
}
|
|
408
365
|
if (lowerKey === 'y') {
|
|
409
366
|
drawResult(true);
|
|
410
367
|
term.close();
|
|
411
|
-
return freeze({ result: PromptResult.Submitted, value: true });
|
|
368
|
+
return index_cjs_js.freeze({ result: PromptResult.Submitted, value: true });
|
|
412
369
|
}
|
|
413
370
|
if (lowerKey === 'n') {
|
|
414
371
|
drawResult(false);
|
|
415
372
|
term.close();
|
|
416
|
-
return freeze({ result: PromptResult.Submitted, value: false });
|
|
373
|
+
return index_cjs_js.freeze({ result: PromptResult.Submitted, value: false });
|
|
417
374
|
}
|
|
418
375
|
if (key === Key.Enter && config.initial !== undefined) {
|
|
419
376
|
drawResult(config.initial);
|
|
420
377
|
term.close();
|
|
421
|
-
return freeze({ result: PromptResult.Submitted, value: config.initial });
|
|
378
|
+
return index_cjs_js.freeze({ result: PromptResult.Submitted, value: config.initial });
|
|
422
379
|
}
|
|
423
380
|
}
|
|
424
381
|
}
|
|
@@ -446,7 +403,7 @@ function arrayIncludes(arr, value) {
|
|
|
446
403
|
* @param query - Search query string
|
|
447
404
|
* @returns Array of indices matching the query
|
|
448
405
|
*/
|
|
449
|
-
function filterChoices(choices, query) {
|
|
406
|
+
function filterChoices$1(choices, query) {
|
|
450
407
|
if (!query) {
|
|
451
408
|
return choices.map((_, i) => i);
|
|
452
409
|
}
|
|
@@ -457,7 +414,7 @@ function filterChoices(choices, query) {
|
|
|
457
414
|
indices.push(i);
|
|
458
415
|
}
|
|
459
416
|
});
|
|
460
|
-
return freeze(indices);
|
|
417
|
+
return index_cjs_js.freeze(indices);
|
|
461
418
|
}
|
|
462
419
|
/**
|
|
463
420
|
* Creates initial state for multiselect prompt.
|
|
@@ -467,7 +424,7 @@ function filterChoices(choices, query) {
|
|
|
467
424
|
* @returns Initial multiselect state
|
|
468
425
|
*/
|
|
469
426
|
function createInitialState$2(config) {
|
|
470
|
-
return freeze({
|
|
427
|
+
return index_cjs_js.freeze({
|
|
471
428
|
cursor: 0,
|
|
472
429
|
selected: config.initial ? [...config.initial] : [],
|
|
473
430
|
choices: config.choices,
|
|
@@ -619,7 +576,7 @@ function processKey$2(key, state, config) {
|
|
|
619
576
|
if (newCursor < state.scrollOffset) {
|
|
620
577
|
newScrollOffset = newCursor;
|
|
621
578
|
}
|
|
622
|
-
return freeze({ ...state, cursor: newCursor, scrollOffset: newScrollOffset });
|
|
579
|
+
return index_cjs_js.freeze({ ...state, cursor: newCursor, scrollOffset: newScrollOffset });
|
|
623
580
|
}
|
|
624
581
|
if (key === Key.Down) {
|
|
625
582
|
let newCursor = state.cursor + 1;
|
|
@@ -632,7 +589,7 @@ function processKey$2(key, state, config) {
|
|
|
632
589
|
if (newCursor >= state.scrollOffset + maxVisible) {
|
|
633
590
|
newScrollOffset = newCursor - maxVisible + 1;
|
|
634
591
|
}
|
|
635
|
-
return freeze({ ...state, cursor: newCursor, scrollOffset: newScrollOffset });
|
|
592
|
+
return index_cjs_js.freeze({ ...state, cursor: newCursor, scrollOffset: newScrollOffset });
|
|
636
593
|
}
|
|
637
594
|
if (key === Key.Space) {
|
|
638
595
|
const actualIndex = state.filteredIndices[state.cursor];
|
|
@@ -645,22 +602,22 @@ function processKey$2(key, state, config) {
|
|
|
645
602
|
const isSelected = arrayIncludes(state.selected, actualIndex);
|
|
646
603
|
if (isSelected) {
|
|
647
604
|
const newSelected = state.selected.filter((i) => i !== actualIndex);
|
|
648
|
-
return freeze({ ...state, selected: newSelected });
|
|
605
|
+
return index_cjs_js.freeze({ ...state, selected: newSelected });
|
|
649
606
|
}
|
|
650
607
|
else {
|
|
651
608
|
if (config.max !== undefined && state.selected.length >= config.max) {
|
|
652
609
|
return state;
|
|
653
610
|
}
|
|
654
611
|
const newSelected = [...state.selected, actualIndex];
|
|
655
|
-
return freeze({ ...state, selected: newSelected });
|
|
612
|
+
return index_cjs_js.freeze({ ...state, selected: newSelected });
|
|
656
613
|
}
|
|
657
614
|
}
|
|
658
615
|
if (config.searchable) {
|
|
659
616
|
if (key === Key.Backspace || key === '\b') {
|
|
660
617
|
if (state.searchQuery.length > 0) {
|
|
661
618
|
const newQuery = state.searchQuery.slice(0, -1);
|
|
662
|
-
const newFiltered = filterChoices(state.choices, newQuery);
|
|
663
|
-
return freeze({
|
|
619
|
+
const newFiltered = filterChoices$1(state.choices, newQuery);
|
|
620
|
+
return index_cjs_js.freeze({
|
|
664
621
|
...state,
|
|
665
622
|
searchQuery: newQuery,
|
|
666
623
|
filteredIndices: newFiltered,
|
|
@@ -672,8 +629,8 @@ function processKey$2(key, state, config) {
|
|
|
672
629
|
}
|
|
673
630
|
if (key.length === 1 && key >= ' ' && key !== Key.Space) {
|
|
674
631
|
const newQuery = state.searchQuery + key;
|
|
675
|
-
const newFiltered = filterChoices(state.choices, newQuery);
|
|
676
|
-
return freeze({
|
|
632
|
+
const newFiltered = filterChoices$1(state.choices, newQuery);
|
|
633
|
+
return index_cjs_js.freeze({
|
|
677
634
|
...state,
|
|
678
635
|
searchQuery: newQuery,
|
|
679
636
|
filteredIndices: newFiltered,
|
|
@@ -753,7 +710,7 @@ async function multiselect(config) {
|
|
|
753
710
|
term.write(Ansi.HideCursor);
|
|
754
711
|
const redraw = (submitted = false) => {
|
|
755
712
|
if (lineCount > 0) {
|
|
756
|
-
term.write(Ansi.cursorUp(lineCount
|
|
713
|
+
term.write(Ansi.cursorUp(lineCount) + Ansi.CursorStart);
|
|
757
714
|
}
|
|
758
715
|
term.write(Ansi.ClearToEnd);
|
|
759
716
|
lineCount = render$2(term, config, state, submitted);
|
|
@@ -767,13 +724,13 @@ async function multiselect(config) {
|
|
|
767
724
|
const key = await term.readKey();
|
|
768
725
|
if (term.isCancelled()) {
|
|
769
726
|
if (lineCount > 0) {
|
|
770
|
-
term.write(Ansi.cursorUp(lineCount
|
|
727
|
+
term.write(Ansi.cursorUp(lineCount) + Ansi.CursorStart);
|
|
771
728
|
}
|
|
772
729
|
term.write(Ansi.ClearToEnd);
|
|
773
730
|
term.write(renderMessage(config.message) + renderCancelled() + '\n');
|
|
774
731
|
term.write(Ansi.ShowCursor);
|
|
775
732
|
term.close();
|
|
776
|
-
return freeze({ result: PromptResult.Cancelled, value: undefined });
|
|
733
|
+
return index_cjs_js.freeze({ result: PromptResult.Cancelled, value: undefined });
|
|
777
734
|
}
|
|
778
735
|
if (key === Key.Enter) {
|
|
779
736
|
const validationError = validateSelection(state, config);
|
|
@@ -784,14 +741,14 @@ async function multiselect(config) {
|
|
|
784
741
|
}
|
|
785
742
|
const selectedValues = state.selected.map((i) => state.choices[i]?.value).filter((v) => v !== undefined);
|
|
786
743
|
if (lineCount > 0) {
|
|
787
|
-
term.write(Ansi.cursorUp(lineCount
|
|
744
|
+
term.write(Ansi.cursorUp(lineCount) + Ansi.CursorStart);
|
|
788
745
|
}
|
|
789
746
|
term.write(Ansi.ClearToEnd);
|
|
790
747
|
render$2(term, config, state, true);
|
|
791
748
|
term.write('\n');
|
|
792
749
|
term.write(Ansi.ShowCursor);
|
|
793
750
|
term.close();
|
|
794
|
-
return freeze({ result: PromptResult.Submitted, value: freeze(selectedValues) });
|
|
751
|
+
return index_cjs_js.freeze({ result: PromptResult.Submitted, value: index_cjs_js.freeze(selectedValues) });
|
|
795
752
|
}
|
|
796
753
|
errorMessage = undefined;
|
|
797
754
|
state = processKey$2(key, state, config);
|
|
@@ -799,6 +756,27 @@ async function multiselect(config) {
|
|
|
799
756
|
}
|
|
800
757
|
}
|
|
801
758
|
|
|
759
|
+
/**
|
|
760
|
+
* Filters choices based on search query.
|
|
761
|
+
*
|
|
762
|
+
* @internal
|
|
763
|
+
* @param choices - All available choices
|
|
764
|
+
* @param query - Search query string
|
|
765
|
+
* @returns Array of indices matching the query
|
|
766
|
+
*/
|
|
767
|
+
function filterChoices(choices, query) {
|
|
768
|
+
if (!query) {
|
|
769
|
+
return choices.map((_, i) => i);
|
|
770
|
+
}
|
|
771
|
+
const lowerQuery = query.toLowerCase();
|
|
772
|
+
const indices = [];
|
|
773
|
+
choices.forEach((choice, i) => {
|
|
774
|
+
if (choice.label.toLowerCase().includes(lowerQuery)) {
|
|
775
|
+
indices.push(i);
|
|
776
|
+
}
|
|
777
|
+
});
|
|
778
|
+
return index_cjs_js.freeze(indices);
|
|
779
|
+
}
|
|
802
780
|
/**
|
|
803
781
|
* Creates initial state for select prompt.
|
|
804
782
|
*
|
|
@@ -807,9 +785,11 @@ async function multiselect(config) {
|
|
|
807
785
|
* @returns Initial select state
|
|
808
786
|
*/
|
|
809
787
|
function createInitialState$1(config) {
|
|
810
|
-
return freeze({
|
|
788
|
+
return index_cjs_js.freeze({
|
|
811
789
|
cursor: config.initial ?? 0,
|
|
812
790
|
choices: config.choices,
|
|
791
|
+
filteredIndices: config.choices.map((_, i) => i),
|
|
792
|
+
searchQuery: '',
|
|
813
793
|
scrollOffset: 0,
|
|
814
794
|
});
|
|
815
795
|
}
|
|
@@ -819,12 +799,12 @@ function createInitialState$1(config) {
|
|
|
819
799
|
* @internal
|
|
820
800
|
* @param state - Current prompt state
|
|
821
801
|
* @param maxVisible - Maximum number of visible choices
|
|
822
|
-
* @returns
|
|
802
|
+
* @returns Object containing visible indices and start index
|
|
823
803
|
*/
|
|
824
804
|
function getVisibleChoices(state, maxVisible) {
|
|
825
|
-
const total = state.
|
|
805
|
+
const total = state.filteredIndices.length;
|
|
826
806
|
if (total <= maxVisible) {
|
|
827
|
-
return {
|
|
807
|
+
return { indices: state.filteredIndices, startIndex: 0 };
|
|
828
808
|
}
|
|
829
809
|
let startIndex = state.scrollOffset;
|
|
830
810
|
if (state.cursor < startIndex) {
|
|
@@ -834,7 +814,7 @@ function getVisibleChoices(state, maxVisible) {
|
|
|
834
814
|
startIndex = state.cursor - maxVisible + 1;
|
|
835
815
|
}
|
|
836
816
|
return {
|
|
837
|
-
|
|
817
|
+
indices: state.filteredIndices.slice(startIndex, startIndex + maxVisible),
|
|
838
818
|
startIndex,
|
|
839
819
|
};
|
|
840
820
|
}
|
|
@@ -843,13 +823,11 @@ function getVisibleChoices(state, maxVisible) {
|
|
|
843
823
|
*
|
|
844
824
|
* @internal
|
|
845
825
|
* @param choice - The choice to render
|
|
846
|
-
* @param isSelected - Whether this choice is selected
|
|
847
826
|
* @param isFocused - Whether cursor is on this choice
|
|
848
827
|
* @returns Formatted choice string
|
|
849
828
|
*/
|
|
850
|
-
function renderChoice(choice,
|
|
829
|
+
function renderChoice(choice, isFocused) {
|
|
851
830
|
const pointer = isFocused ? style.cyan(Symbol.Pointer) : ' ';
|
|
852
|
-
const radio = isSelected ? style.green(Symbol.RadioSelected) : style.dim(Symbol.Radio);
|
|
853
831
|
let label = choice.label;
|
|
854
832
|
if (choice.disabled) {
|
|
855
833
|
label = style.dim(label + ' (disabled)');
|
|
@@ -858,7 +836,7 @@ function renderChoice(choice, isSelected, isFocused) {
|
|
|
858
836
|
label = style.cyan(label);
|
|
859
837
|
}
|
|
860
838
|
const hint = choice.hint ? style.dim(` — ${choice.hint}`) : '';
|
|
861
|
-
return `${pointer} ${
|
|
839
|
+
return `${pointer} ${label}${hint}`;
|
|
862
840
|
}
|
|
863
841
|
/**
|
|
864
842
|
* Renders the select prompt to the terminal.
|
|
@@ -872,36 +850,53 @@ function renderChoice(choice, isSelected, isFocused) {
|
|
|
872
850
|
*/
|
|
873
851
|
function render$1(term, config, state, submitted) {
|
|
874
852
|
const maxVisible = config.maxVisible ?? 10;
|
|
875
|
-
const {
|
|
853
|
+
const { indices: visibleIndices, startIndex } = getVisibleChoices(state, maxVisible);
|
|
876
854
|
let output = Ansi.CursorStart + Ansi.ClearLine + renderMessage(config.message);
|
|
877
855
|
if (submitted) {
|
|
878
|
-
const
|
|
856
|
+
const actualIndex = state.filteredIndices[state.cursor];
|
|
857
|
+
const selectedChoice = actualIndex !== undefined ? state.choices[actualIndex] : undefined;
|
|
879
858
|
/* istanbul ignore next -- @preserve defensive: cursor always within bounds */
|
|
880
859
|
output += renderSubmitted(selectedChoice?.label ?? '');
|
|
881
860
|
term.write(output);
|
|
882
861
|
return 1;
|
|
883
862
|
}
|
|
884
|
-
|
|
863
|
+
if (config.searchable && state.searchQuery) {
|
|
864
|
+
output += style.cyan(state.searchQuery) + style.dim(' (type to filter)');
|
|
865
|
+
}
|
|
866
|
+
else if (config.searchable) {
|
|
867
|
+
output += style.dim('(type to filter, enter to select)');
|
|
868
|
+
}
|
|
869
|
+
else {
|
|
870
|
+
output += style.dim('(use arrows, enter to select)');
|
|
871
|
+
}
|
|
885
872
|
term.write(output + '\n');
|
|
886
873
|
let lineCount = 1;
|
|
887
874
|
const showScrollUp = startIndex > 0;
|
|
888
|
-
const showScrollDown = startIndex + maxVisible < state.
|
|
875
|
+
const showScrollDown = startIndex + maxVisible < state.filteredIndices.length;
|
|
889
876
|
if (showScrollUp) {
|
|
890
877
|
term.write(Ansi.ClearLine + style.dim(` ${Symbol.Ellipsis} (${startIndex} more above)`) + '\n');
|
|
891
878
|
lineCount++;
|
|
892
879
|
}
|
|
893
|
-
|
|
894
|
-
const
|
|
895
|
-
|
|
896
|
-
|
|
880
|
+
visibleIndices.forEach((actualIndex, i) => {
|
|
881
|
+
const choice = state.choices[actualIndex];
|
|
882
|
+
/* istanbul ignore if -- @preserve defensive: actualIndex always valid from filteredIndices */
|
|
883
|
+
if (!choice)
|
|
884
|
+
return;
|
|
885
|
+
const viewIndex = startIndex + i;
|
|
886
|
+
const isFocused = viewIndex === state.cursor;
|
|
887
|
+
const line = renderChoice(choice, isFocused);
|
|
897
888
|
term.write(Ansi.ClearLine + line + '\n');
|
|
898
889
|
lineCount++;
|
|
899
890
|
});
|
|
900
891
|
if (showScrollDown) {
|
|
901
|
-
const remaining = state.
|
|
892
|
+
const remaining = state.filteredIndices.length - (startIndex + maxVisible);
|
|
902
893
|
term.write(Ansi.ClearLine + style.dim(` ${Symbol.Ellipsis} (${remaining} more below)`) + '\n');
|
|
903
894
|
lineCount++;
|
|
904
895
|
}
|
|
896
|
+
if (state.filteredIndices.length === 0 && config.searchable) {
|
|
897
|
+
term.write(Ansi.ClearLine + style.dim(' No matches found') + '\n');
|
|
898
|
+
lineCount++;
|
|
899
|
+
}
|
|
905
900
|
return lineCount;
|
|
906
901
|
}
|
|
907
902
|
/**
|
|
@@ -910,16 +905,17 @@ function render$1(term, config, state, submitted) {
|
|
|
910
905
|
* @internal
|
|
911
906
|
* @param key - The key that was pressed
|
|
912
907
|
* @param state - Current prompt state
|
|
913
|
-
* @param
|
|
908
|
+
* @param config - Prompt configuration
|
|
914
909
|
* @returns Updated state after processing the key
|
|
915
910
|
*/
|
|
916
|
-
function processKey$1(key, state,
|
|
917
|
-
const
|
|
918
|
-
|
|
911
|
+
function processKey$1(key, state, config) {
|
|
912
|
+
const maxVisible = config.maxVisible ?? 10;
|
|
913
|
+
const total = state.filteredIndices.length;
|
|
914
|
+
if (total === 0 && key !== Key.Backspace && key !== '\b')
|
|
919
915
|
return state;
|
|
920
916
|
if (key === Key.Up) {
|
|
921
917
|
let newCursor = state.cursor - 1;
|
|
922
|
-
while (newCursor >= 0 && state.choices[newCursor]?.disabled) {
|
|
918
|
+
while (newCursor >= 0 && state.choices[state.filteredIndices[newCursor] ?? -1]?.disabled) {
|
|
923
919
|
newCursor--;
|
|
924
920
|
}
|
|
925
921
|
if (newCursor < 0)
|
|
@@ -928,11 +924,11 @@ function processKey$1(key, state, maxVisible) {
|
|
|
928
924
|
if (newCursor < state.scrollOffset) {
|
|
929
925
|
newScrollOffset = newCursor;
|
|
930
926
|
}
|
|
931
|
-
return freeze({ ...state, cursor: newCursor, scrollOffset: newScrollOffset });
|
|
927
|
+
return index_cjs_js.freeze({ ...state, cursor: newCursor, scrollOffset: newScrollOffset });
|
|
932
928
|
}
|
|
933
929
|
if (key === Key.Down) {
|
|
934
930
|
let newCursor = state.cursor + 1;
|
|
935
|
-
while (newCursor < total && state.choices[newCursor]?.disabled) {
|
|
931
|
+
while (newCursor < total && state.choices[state.filteredIndices[newCursor] ?? -1]?.disabled) {
|
|
936
932
|
newCursor++;
|
|
937
933
|
}
|
|
938
934
|
if (newCursor >= total)
|
|
@@ -941,7 +937,34 @@ function processKey$1(key, state, maxVisible) {
|
|
|
941
937
|
if (newCursor >= state.scrollOffset + maxVisible) {
|
|
942
938
|
newScrollOffset = newCursor - maxVisible + 1;
|
|
943
939
|
}
|
|
944
|
-
return freeze({ ...state, cursor: newCursor, scrollOffset: newScrollOffset });
|
|
940
|
+
return index_cjs_js.freeze({ ...state, cursor: newCursor, scrollOffset: newScrollOffset });
|
|
941
|
+
}
|
|
942
|
+
if (config.searchable) {
|
|
943
|
+
if (key === Key.Backspace || key === '\b') {
|
|
944
|
+
if (state.searchQuery.length > 0) {
|
|
945
|
+
const newQuery = state.searchQuery.slice(0, -1);
|
|
946
|
+
const newFiltered = filterChoices(state.choices, newQuery);
|
|
947
|
+
return index_cjs_js.freeze({
|
|
948
|
+
...state,
|
|
949
|
+
searchQuery: newQuery,
|
|
950
|
+
filteredIndices: newFiltered,
|
|
951
|
+
cursor: 0,
|
|
952
|
+
scrollOffset: 0,
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
return state;
|
|
956
|
+
}
|
|
957
|
+
if (key.length === 1 && key >= ' ') {
|
|
958
|
+
const newQuery = state.searchQuery + key;
|
|
959
|
+
const newFiltered = filterChoices(state.choices, newQuery);
|
|
960
|
+
return index_cjs_js.freeze({
|
|
961
|
+
...state,
|
|
962
|
+
searchQuery: newQuery,
|
|
963
|
+
filteredIndices: newFiltered,
|
|
964
|
+
cursor: 0,
|
|
965
|
+
scrollOffset: 0,
|
|
966
|
+
});
|
|
967
|
+
}
|
|
945
968
|
}
|
|
946
969
|
return state;
|
|
947
970
|
}
|
|
@@ -949,7 +972,7 @@ function processKey$1(key, state, maxVisible) {
|
|
|
949
972
|
* Prompts for single selection from a list of choices.
|
|
950
973
|
*
|
|
951
974
|
* Pure functional prompt with arrow key navigation, scrolling support,
|
|
952
|
-
* and optional
|
|
975
|
+
* optional disabled choices, and optional type-to-filter search.
|
|
953
976
|
*
|
|
954
977
|
* @param config - Select prompt configuration
|
|
955
978
|
* @returns Promise resolving to selected value or cancellation
|
|
@@ -981,17 +1004,26 @@ function processKey$1(key, state, maxVisible) {
|
|
|
981
1004
|
* initial: 1, // Start on Pro
|
|
982
1005
|
* })
|
|
983
1006
|
* ```
|
|
1007
|
+
*
|
|
1008
|
+
* @example With search
|
|
1009
|
+
* ```typescript
|
|
1010
|
+
* const outcome = await select({
|
|
1011
|
+
* message: 'Pick a project:',
|
|
1012
|
+
* choices: projects.map((p) => ({ label: p.name, value: p.id })),
|
|
1013
|
+
* searchable: true,
|
|
1014
|
+
* })
|
|
1015
|
+
* ```
|
|
984
1016
|
*/
|
|
985
1017
|
async function select(config) {
|
|
986
1018
|
const term = createTerminal({ input: config.input, output: config.output });
|
|
987
|
-
const maxVisible = config.maxVisible ?? 10;
|
|
988
1019
|
let state = createInitialState$1(config);
|
|
989
1020
|
let lineCount = 0;
|
|
990
1021
|
term.write(Ansi.HideCursor);
|
|
991
1022
|
const redraw = (submitted = false) => {
|
|
992
1023
|
if (lineCount > 0) {
|
|
993
|
-
term.write(Ansi.cursorUp(lineCount
|
|
1024
|
+
term.write(Ansi.cursorUp(lineCount) + Ansi.CursorStart);
|
|
994
1025
|
}
|
|
1026
|
+
term.write(Ansi.ClearToEnd);
|
|
995
1027
|
lineCount = render$1(term, config, state, submitted);
|
|
996
1028
|
};
|
|
997
1029
|
redraw();
|
|
@@ -999,52 +1031,38 @@ async function select(config) {
|
|
|
999
1031
|
const key = await term.readKey();
|
|
1000
1032
|
if (term.isCancelled()) {
|
|
1001
1033
|
if (lineCount > 0) {
|
|
1002
|
-
term.write(Ansi.cursorUp(lineCount
|
|
1034
|
+
term.write(Ansi.cursorUp(lineCount) + Ansi.CursorStart);
|
|
1003
1035
|
}
|
|
1004
1036
|
term.write(Ansi.ClearToEnd);
|
|
1005
1037
|
term.write(renderMessage(config.message) + renderCancelled() + '\n');
|
|
1006
1038
|
term.write(Ansi.ShowCursor);
|
|
1007
1039
|
term.close();
|
|
1008
|
-
return freeze({ result: PromptResult.Cancelled, value: undefined });
|
|
1040
|
+
return index_cjs_js.freeze({ result: PromptResult.Cancelled, value: undefined });
|
|
1009
1041
|
}
|
|
1010
1042
|
if (key === Key.Enter) {
|
|
1011
|
-
const
|
|
1043
|
+
const actualIndex = state.filteredIndices[state.cursor];
|
|
1044
|
+
if (actualIndex === undefined) {
|
|
1045
|
+
continue;
|
|
1046
|
+
}
|
|
1047
|
+
const selectedChoice = state.choices[actualIndex];
|
|
1012
1048
|
if (!selectedChoice || selectedChoice.disabled) {
|
|
1013
1049
|
continue;
|
|
1014
1050
|
}
|
|
1015
1051
|
if (lineCount > 0) {
|
|
1016
|
-
term.write(Ansi.cursorUp(lineCount
|
|
1052
|
+
term.write(Ansi.cursorUp(lineCount) + Ansi.CursorStart);
|
|
1017
1053
|
}
|
|
1018
1054
|
term.write(Ansi.ClearToEnd);
|
|
1019
1055
|
render$1(term, config, state, true);
|
|
1020
1056
|
term.write('\n');
|
|
1021
1057
|
term.write(Ansi.ShowCursor);
|
|
1022
1058
|
term.close();
|
|
1023
|
-
return freeze({ result: PromptResult.Submitted, value: selectedChoice.value });
|
|
1059
|
+
return index_cjs_js.freeze({ result: PromptResult.Submitted, value: selectedChoice.value });
|
|
1024
1060
|
}
|
|
1025
|
-
state = processKey$1(key, state,
|
|
1061
|
+
state = processKey$1(key, state, config);
|
|
1026
1062
|
redraw();
|
|
1027
1063
|
}
|
|
1028
1064
|
}
|
|
1029
1065
|
|
|
1030
|
-
/**
|
|
1031
|
-
* Safe copies of Math built-in methods.
|
|
1032
|
-
*
|
|
1033
|
-
* These references are captured at module initialization time to protect against
|
|
1034
|
-
* prototype pollution attacks. Import only what you need for tree-shaking.
|
|
1035
|
-
*
|
|
1036
|
-
* @module @hyperfrontend/immutable-api-utils/built-in-copy/math
|
|
1037
|
-
*/
|
|
1038
|
-
const _Math = globalThis.Math;
|
|
1039
|
-
/**
|
|
1040
|
-
* (Safe copy) Returns the larger of zero or more numbers.
|
|
1041
|
-
*/
|
|
1042
|
-
const max = _Math.max;
|
|
1043
|
-
/**
|
|
1044
|
-
* (Safe copy) Returns the smaller of zero or more numbers.
|
|
1045
|
-
*/
|
|
1046
|
-
const min = _Math.min;
|
|
1047
|
-
|
|
1048
1066
|
/**
|
|
1049
1067
|
* Creates initial state for text prompt.
|
|
1050
1068
|
*
|
|
@@ -1071,14 +1089,18 @@ function createInitialState(config) {
|
|
|
1071
1089
|
*/
|
|
1072
1090
|
function render(term, config, state, submitted) {
|
|
1073
1091
|
const displayValue = config.format ? config.format(state.value) : state.value;
|
|
1074
|
-
|
|
1092
|
+
const messageText = config.renderMessage ? config.renderMessage(state.value) : config.message;
|
|
1093
|
+
let output = renderMessage(messageText);
|
|
1094
|
+
let trailingChars = 0;
|
|
1075
1095
|
if (submitted) {
|
|
1076
1096
|
output += renderSubmitted(displayValue || config.initial || '');
|
|
1077
1097
|
}
|
|
1078
1098
|
else {
|
|
1079
1099
|
output += displayValue;
|
|
1100
|
+
trailingChars = displayValue.length - state.cursorPos;
|
|
1080
1101
|
if (config.initial && !state.value) {
|
|
1081
1102
|
output += style.dim(config.initial);
|
|
1103
|
+
trailingChars += config.initial.length;
|
|
1082
1104
|
}
|
|
1083
1105
|
}
|
|
1084
1106
|
term.write(Ansi.CursorStart + Ansi.ClearLine + output);
|
|
@@ -1086,6 +1108,9 @@ function render(term, config, state, submitted) {
|
|
|
1086
1108
|
term.write('\n' + style.yellow(` ${state.error}`));
|
|
1087
1109
|
return 2;
|
|
1088
1110
|
}
|
|
1111
|
+
if (!submitted && trailingChars > 0) {
|
|
1112
|
+
term.write(Ansi.cursorLeft(trailingChars));
|
|
1113
|
+
}
|
|
1089
1114
|
return 1;
|
|
1090
1115
|
}
|
|
1091
1116
|
/**
|
|
@@ -1122,13 +1147,13 @@ function processKey(key, state) {
|
|
|
1122
1147
|
if (key === Key.Left) {
|
|
1123
1148
|
return {
|
|
1124
1149
|
...state,
|
|
1125
|
-
cursorPos: max(0, state.cursorPos - 1),
|
|
1150
|
+
cursorPos: index_cjs_js$2.max(0, state.cursorPos - 1),
|
|
1126
1151
|
};
|
|
1127
1152
|
}
|
|
1128
1153
|
if (key === Key.Right) {
|
|
1129
1154
|
return {
|
|
1130
1155
|
...state,
|
|
1131
|
-
cursorPos: min(state.value.length, state.cursorPos + 1),
|
|
1156
|
+
cursorPos: index_cjs_js$2.min(state.value.length, state.cursorPos + 1),
|
|
1132
1157
|
};
|
|
1133
1158
|
}
|
|
1134
1159
|
return state;
|
|
@@ -1173,7 +1198,6 @@ async function text(config) {
|
|
|
1173
1198
|
const term = createTerminal({ input: config.input, output: config.output });
|
|
1174
1199
|
let state = createInitialState(config);
|
|
1175
1200
|
let lineCount = 0;
|
|
1176
|
-
term.write(Ansi.HideCursor);
|
|
1177
1201
|
const redraw = (submitted = false) => {
|
|
1178
1202
|
if (lineCount > 0) {
|
|
1179
1203
|
term.clearLines(lineCount);
|
|
@@ -1187,7 +1211,7 @@ async function text(config) {
|
|
|
1187
1211
|
redraw();
|
|
1188
1212
|
term.write(renderCancelled() + '\n');
|
|
1189
1213
|
term.close();
|
|
1190
|
-
return freeze({ result: PromptResult.Cancelled, value: undefined });
|
|
1214
|
+
return index_cjs_js.freeze({ result: PromptResult.Cancelled, value: undefined });
|
|
1191
1215
|
}
|
|
1192
1216
|
if (key === Key.Enter) {
|
|
1193
1217
|
const value = state.value || config.initial || '';
|
|
@@ -1202,7 +1226,7 @@ async function text(config) {
|
|
|
1202
1226
|
redraw(true);
|
|
1203
1227
|
term.write('\n');
|
|
1204
1228
|
term.close();
|
|
1205
|
-
return freeze({ result: PromptResult.Submitted, value });
|
|
1229
|
+
return index_cjs_js.freeze({ result: PromptResult.Submitted, value });
|
|
1206
1230
|
}
|
|
1207
1231
|
state = processKey(key, state);
|
|
1208
1232
|
redraw();
|
|
@@ -1213,5 +1237,5 @@ exports.PromptResult = PromptResult;
|
|
|
1213
1237
|
exports.confirm = confirm;
|
|
1214
1238
|
exports.multiselect = multiselect;
|
|
1215
1239
|
exports.select = select;
|
|
1240
|
+
exports.style = style;
|
|
1216
1241
|
exports.text = text;
|
|
1217
|
-
//# sourceMappingURL=index.cjs.js.map
|