@topcli/prompts 1.8.1 → 1.9.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.
package/README.md CHANGED
@@ -86,6 +86,10 @@ select(message: string, options: SelectOptions): Promise<string>
86
86
  Scrollable select depending `maxVisible` (default `8`).
87
87
  Use `ignoreValues` to skip result render & clear lines after a selected one.
88
88
 
89
+ Use `autocomplete` to allow filtered choices. This can be useful for a large list of choices.
90
+
91
+ Use `caseSensitive` to make autocomplete filters case sensitive. Default `false`
92
+
89
93
  ### `multiselect()`
90
94
 
91
95
  ```ts
@@ -97,6 +101,8 @@ Use `preSelectedChoices` to pre-select choices.
97
101
 
98
102
  Use `validators` to handle user input.
99
103
 
104
+ Use `showHint: false` to disable hint (this option is truthy by default).
105
+
100
106
  **Example**
101
107
 
102
108
  ```js
@@ -106,7 +112,7 @@ const os = await multiselect('Choose OS', {
106
112
  });
107
113
  ```
108
114
 
109
- Use `autocomplete` to allow filtered choices. This can be usefull for a large list of choices.
115
+ Use `autocomplete` to allow filtered choices. This can be useful for a large list of choices.
110
116
 
111
117
  Use `caseSensitive` to make autocomplete filters case sensitive. Default `false`
112
118
 
@@ -172,6 +178,8 @@ export interface SelectOptions extends SharedOptions {
172
178
  choices: (Choice | string)[];
173
179
  maxVisible?: number;
174
180
  ignoreValues?: (string | number | boolean)[];
181
+ autocomplete?: boolean;
182
+ caseSensitive?: boolean;
175
183
  }
176
184
 
177
185
  export interface MultiselectOptions extends SharedOptions {
@@ -181,6 +189,7 @@ export interface MultiselectOptions extends SharedOptions {
181
189
  validators?: Validator[];
182
190
  autocomplete?: boolean;
183
191
  caseSensitive?: boolean;
192
+ showHint?: boolean;
184
193
  }
185
194
 
186
195
  export interface ConfirmOptions extends SharedOptions {
@@ -202,6 +211,7 @@ export interface ConfirmOptions extends SharedOptions {
202
211
  <td align="center" valign="top" width="14.28%"><a href="http://sofiand.github.io/portfolio-client/"><img src="https://avatars.githubusercontent.com/u/39944043?v=4?s=100" width="100px;" alt="Yefis"/><br /><sub><b>Yefis</b></sub></a><br /><a href="https://github.com/TopCli/prompts/commits?author=SofianD" title="Code">💻</a> <a href="https://github.com/TopCli/prompts/commits?author=SofianD" title="Documentation">📖</a></td>
203
212
  <td align="center" valign="top" width="14.28%"><a href="http://justie.dev"><img src="https://avatars.githubusercontent.com/u/7118300?v=4?s=100" width="100px;" alt="Ben"/><br /><sub><b>Ben</b></sub></a><br /><a href="https://github.com/TopCli/prompts/commits?author=JUSTIVE" title="Documentation">📖</a> <a href="#maintenance-JUSTIVE" title="Maintenance">🚧</a></td>
204
213
  <td align="center" valign="top" width="14.28%"><a href="https://github.com/ncukondo"><img src="https://avatars.githubusercontent.com/u/17022138?v=4?s=100" width="100px;" alt="Takeshi Kondo"/><br /><sub><b>Takeshi Kondo</b></sub></a><br /><a href="#maintenance-ncukondo" title="Maintenance">🚧</a></td>
214
+ <td align="center" valign="top" width="14.28%"><a href="https://github.com/FredGuiou"><img src="https://avatars.githubusercontent.com/u/99122562?v=4?s=100" width="100px;" alt="FredGuiou"/><br /><sub><b>FredGuiou</b></sub></a><br /><a href="https://github.com/TopCli/prompts/commits?author=FredGuiou" title="Code">💻</a> <a href="https://github.com/TopCli/prompts/commits?author=FredGuiou" title="Tests">⚠️</a> <a href="https://github.com/TopCli/prompts/commits?author=FredGuiou" title="Documentation">📖</a></td>
205
215
  </tr>
206
216
  </tbody>
207
217
  </table>
package/dist/index.cjs CHANGED
@@ -379,10 +379,10 @@ var ConfirmPrompt = class extends AbstractPrompt {
379
379
  return `${query} ${this.#getHint()}`;
380
380
  }
381
381
  #onQuestionAnswer() {
382
- const defaultLines = this.fastAnswer ? 0 : 1;
382
+ this.clearLastLine();
383
383
  this.stdout.moveCursor(
384
384
  -this.stdout.columns,
385
- -(Math.floor((0, import_wcwidth2.default)(stripAnsi(this.#getQuestionQuery())) / this.stdout.columns) || defaultLines)
385
+ -Math.floor((0, import_wcwidth2.default)(stripAnsi(this.#getQuestionQuery())) / this.stdout.columns)
386
386
  );
387
387
  this.stdout.clearScreenDown();
388
388
  this.write(`${this.selectedValue ? SYMBOLS.Tick : SYMBOLS.Cross} ${import_kleur3.default.bold(this.message)}${import_node_os3.EOL}`);
@@ -413,18 +413,47 @@ var ConfirmPrompt = class extends AbstractPrompt {
413
413
  var import_node_os4 = require("os");
414
414
  var import_kleur4 = __toESM(require("kleur"), 1);
415
415
  var import_wcwidth3 = __toESM(require("@topcli/wcwidth"), 1);
416
+ var kRequiredChoiceProperties = ["label", "value"];
416
417
  var SelectPrompt = class extends AbstractPrompt {
417
418
  #boundExitEvent = () => void 0;
418
419
  #boundKeyPressEvent = () => void 0;
419
420
  activeIndex = 0;
420
- selectedIndexes = [];
421
421
  questionMessage;
422
- longestChoicelength;
422
+ autocompleteValue = "";
423
423
  options;
424
424
  lastRender;
425
425
  get choices() {
426
426
  return this.options.choices;
427
427
  }
428
+ get filteredChoices() {
429
+ if (!(this.options.autocomplete && this.autocompleteValue.length > 0)) {
430
+ return this.choices;
431
+ }
432
+ const isCaseSensitive = this.options.caseSensitive;
433
+ const autocompleteValue = isCaseSensitive ? this.autocompleteValue : this.autocompleteValue.toLowerCase();
434
+ return this.choices.filter((choice) => this.#filterChoice(choice, autocompleteValue, isCaseSensitive));
435
+ }
436
+ #filterChoice(choice, autocompleteValue, isCaseSensitive = false) {
437
+ const choiceValue = typeof choice === "string" ? isCaseSensitive ? choice : choice.toLowerCase() : isCaseSensitive ? choice.label : choice.label.toLowerCase();
438
+ if (autocompleteValue.includes(" ")) {
439
+ return this.#filterMultipleWords(choiceValue, autocompleteValue, isCaseSensitive);
440
+ }
441
+ return choiceValue.includes(autocompleteValue);
442
+ }
443
+ #filterMultipleWords(choiceValue, autocompleteValue, isCaseSensitive) {
444
+ return autocompleteValue.split(" ").every((word) => {
445
+ const wordValue = isCaseSensitive ? word : word.toLowerCase();
446
+ return choiceValue.includes(wordValue) || choiceValue.includes(autocompleteValue);
447
+ });
448
+ }
449
+ get longestChoice() {
450
+ return Math.max(...this.filteredChoices.map((choice) => {
451
+ if (typeof choice === "string") {
452
+ return choice.length;
453
+ }
454
+ return choice.label.length;
455
+ }));
456
+ }
428
457
  constructor(message, options) {
429
458
  const {
430
459
  stdin = process.stdin,
@@ -441,22 +470,20 @@ var SelectPrompt = class extends AbstractPrompt {
441
470
  this.destroy();
442
471
  throw new TypeError("Missing required param: choices");
443
472
  }
444
- this.longestChoicelength = Math.max(...choices.map((choice) => {
473
+ for (const choice of choices) {
445
474
  if (typeof choice === "string") {
446
- return choice.length;
475
+ continue;
447
476
  }
448
- const kRequiredChoiceProperties2 = ["label", "value"];
449
- for (const prop of kRequiredChoiceProperties2) {
477
+ for (const prop of kRequiredChoiceProperties) {
450
478
  if (!choice[prop]) {
451
479
  this.destroy();
452
480
  throw new TypeError(`Missing ${prop} for choice ${JSON.stringify(choice)}`);
453
481
  }
454
482
  }
455
- return choice.label.length;
456
- }));
483
+ }
457
484
  }
458
485
  #getFormattedChoice(choiceIndex) {
459
- const choice = this.choices[choiceIndex];
486
+ const choice = this.filteredChoices[choiceIndex];
460
487
  if (typeof choice === "string") {
461
488
  return { value: choice, label: choice };
462
489
  }
@@ -464,21 +491,24 @@ var SelectPrompt = class extends AbstractPrompt {
464
491
  }
465
492
  #getVisibleChoices() {
466
493
  const maxVisible = this.options.maxVisible || 8;
467
- let startIndex = Math.min(this.choices.length - maxVisible, this.activeIndex - Math.floor(maxVisible / 2));
494
+ let startIndex = Math.min(this.filteredChoices.length - maxVisible, this.activeIndex - Math.floor(maxVisible / 2));
468
495
  if (startIndex < 0) {
469
496
  startIndex = 0;
470
497
  }
471
- const endIndex = Math.min(startIndex + maxVisible, this.choices.length);
498
+ const endIndex = Math.min(startIndex + maxVisible, this.filteredChoices.length);
472
499
  return { startIndex, endIndex };
473
500
  }
474
501
  #showChoices() {
475
502
  const { startIndex, endIndex } = this.#getVisibleChoices();
476
503
  this.lastRender = { startIndex, endIndex };
504
+ if (this.options.autocomplete) {
505
+ this.write(`${SYMBOLS.Pointer} ${this.autocompleteValue}${import_node_os4.EOL}`);
506
+ }
477
507
  for (let choiceIndex = startIndex; choiceIndex < endIndex; choiceIndex++) {
478
508
  const choice = this.#getFormattedChoice(choiceIndex);
479
509
  const isChoiceSelected = choiceIndex === this.activeIndex;
480
510
  const showPreviousChoicesArrow = startIndex > 0 && choiceIndex === startIndex;
481
- const showNextChoicesArrow = endIndex < this.choices.length && choiceIndex === endIndex - 1;
511
+ const showNextChoicesArrow = endIndex < this.filteredChoices.length && choiceIndex === endIndex - 1;
482
512
  let prefixArrow = " ";
483
513
  if (showPreviousChoicesArrow) {
484
514
  prefixArrow = SYMBOLS.Previous;
@@ -487,16 +517,17 @@ var SelectPrompt = class extends AbstractPrompt {
487
517
  }
488
518
  const prefix = `${prefixArrow}${isChoiceSelected ? `${SYMBOLS.Pointer} ` : " "}`;
489
519
  const formattedLabel = choice.label.padEnd(
490
- this.longestChoicelength < 10 ? this.longestChoicelength : 0
520
+ this.longestChoice < 10 ? this.longestChoice : 0
491
521
  );
492
522
  const formattedDescription = choice.description ? ` - ${choice.description}` : "";
493
523
  const color = isChoiceSelected ? import_kleur4.default.white().bold : import_kleur4.default.gray;
494
- const str = color(`${prefix}${formattedLabel}${formattedDescription}${import_node_os4.EOL}`);
524
+ const str = `${prefix}${color(`${formattedLabel}${formattedDescription}`)}${import_node_os4.EOL}`;
495
525
  this.write(str);
496
526
  }
497
527
  }
498
528
  #showAnsweredQuestion(choice) {
499
- const prefix = `${SYMBOLS.Tick} ${import_kleur4.default.bold(this.message)} ${SYMBOLS.Pointer}`;
529
+ const symbolPrefix = choice === "" ? SYMBOLS.Cross : SYMBOLS.Tick;
530
+ const prefix = `${symbolPrefix} ${import_kleur4.default.bold(this.message)} ${SYMBOLS.Pointer}`;
500
531
  const formattedChoice = import_kleur4.default.yellow(typeof choice === "string" ? choice : choice.label);
501
532
  this.write(`${prefix} ${formattedChoice}${import_node_os4.EOL}`);
502
533
  }
@@ -509,17 +540,18 @@ var SelectPrompt = class extends AbstractPrompt {
509
540
  #onKeypress(...args) {
510
541
  const [resolve, render, , key] = args;
511
542
  if (key.name === "up") {
512
- this.activeIndex = this.activeIndex === 0 ? this.choices.length - 1 : this.activeIndex - 1;
543
+ this.activeIndex = this.activeIndex === 0 ? this.filteredChoices.length - 1 : this.activeIndex - 1;
513
544
  render();
514
545
  } else if (key.name === "down") {
515
- this.activeIndex = this.activeIndex === this.choices.length - 1 ? 0 : this.activeIndex + 1;
546
+ this.activeIndex = this.activeIndex === this.filteredChoices.length - 1 ? 0 : this.activeIndex + 1;
516
547
  render();
517
548
  } else if (key.name === "return") {
549
+ const choice = this.filteredChoices[this.activeIndex] || "";
550
+ const label = typeof choice === "string" ? choice : choice.label;
551
+ const value = typeof choice === "string" ? choice : choice.value;
518
552
  render({ clearRender: true });
519
- const currentChoice = this.choices[this.activeIndex];
520
- const value = typeof currentChoice === "string" ? currentChoice : currentChoice.value;
521
553
  if (!this.options.ignoreValues?.includes(value)) {
522
- this.#showAnsweredQuestion(currentChoice);
554
+ this.#showAnsweredQuestion(label);
523
555
  }
524
556
  this.write(SYMBOLS.ShowCursor);
525
557
  this.destroy();
@@ -527,6 +559,14 @@ var SelectPrompt = class extends AbstractPrompt {
527
559
  process.off("exit", this.#boundExitEvent);
528
560
  resolve(value);
529
561
  } else {
562
+ if (!key.ctrl && this.options.autocomplete) {
563
+ this.activeIndex = 0;
564
+ if (key.name === "backspace" && this.autocompleteValue.length > 0) {
565
+ this.autocompleteValue = this.autocompleteValue.slice(0, -1);
566
+ } else if (key.name !== "backspace") {
567
+ this.autocompleteValue += key.sequence;
568
+ }
569
+ }
530
570
  render();
531
571
  }
532
572
  }
@@ -550,6 +590,15 @@ var SelectPrompt = class extends AbstractPrompt {
550
590
  this.clearLastLine();
551
591
  linesToClear--;
552
592
  }
593
+ if (this.options.autocomplete) {
594
+ let linesToClear2 = Math.ceil(
595
+ (0, import_wcwidth3.default)(`${SYMBOLS.Pointer} ${this.autocompleteValue}`) / this.stdout.columns
596
+ );
597
+ while (linesToClear2 > 0) {
598
+ this.clearLastLine();
599
+ linesToClear2--;
600
+ }
601
+ }
553
602
  }
554
603
  if (clearRender) {
555
604
  const questionLineCount = Math.ceil(
@@ -579,11 +628,12 @@ var SelectPrompt = class extends AbstractPrompt {
579
628
  var import_node_os5 = require("os");
580
629
  var import_kleur5 = __toESM(require("kleur"), 1);
581
630
  var import_wcwidth4 = __toESM(require("@topcli/wcwidth"), 1);
582
- var kRequiredChoiceProperties = ["label", "value"];
631
+ var kRequiredChoiceProperties2 = ["label", "value"];
583
632
  var MultiselectPrompt = class extends AbstractPrompt {
584
633
  #boundExitEvent = () => void 0;
585
634
  #boundKeyPressEvent = () => void 0;
586
635
  #validators;
636
+ #showHint;
587
637
  activeIndex = 0;
588
638
  selectedIndexes = [];
589
639
  questionMessage;
@@ -628,7 +678,8 @@ var MultiselectPrompt = class extends AbstractPrompt {
628
678
  stdout = process.stdout,
629
679
  choices,
630
680
  preSelectedChoices,
631
- validators = []
681
+ validators = [],
682
+ showHint = true
632
683
  } = options ?? {};
633
684
  super(message, stdin, stdout);
634
685
  if (!options) {
@@ -641,11 +692,12 @@ var MultiselectPrompt = class extends AbstractPrompt {
641
692
  throw new TypeError("Missing required param: choices");
642
693
  }
643
694
  this.#validators = validators;
695
+ this.#showHint = showHint;
644
696
  for (const choice of choices) {
645
697
  if (typeof choice === "string") {
646
698
  continue;
647
699
  }
648
- for (const prop of kRequiredChoiceProperties) {
700
+ for (const prop of kRequiredChoiceProperties2) {
649
701
  if (!choice[prop]) {
650
702
  this.destroy();
651
703
  throw new TypeError(`Missing ${prop} for choice ${JSON.stringify(choice)}`);
@@ -835,14 +887,14 @@ var MultiselectPrompt = class extends AbstractPrompt {
835
887
  });
836
888
  }
837
889
  #showQuestion(error = null) {
838
- let hint = import_kleur5.default.gray(
890
+ let hint = this.#showHint ? import_kleur5.default.gray(
839
891
  // eslint-disable-next-line max-len
840
- `(Press ${import_kleur5.default.bold("<Ctrl+A>")} to toggle all, ${import_kleur5.default.bold("<Ctrl+Space>")} to select, ${import_kleur5.default.bold("<Left/Right>")} to toggle, ${import_kleur5.default.bold("<Return>")} to submit)`
841
- );
892
+ `(Press ${import_kleur5.default.bold("<Ctrl+A>")} to toggle all, ${import_kleur5.default.bold("<Left/Right>")} to toggle, ${import_kleur5.default.bold("<Return>")} to submit)`
893
+ ) : "";
842
894
  if (error) {
843
- hint += ` ${import_kleur5.default.red().bold(`[${error}]`)}`;
895
+ hint += `${hint.length > 0 ? " " : ""}${import_kleur5.default.red().bold(`[${error}]`)}`;
844
896
  }
845
- this.questionMessage = `${SYMBOLS.QuestionMark} ${import_kleur5.default.bold(this.message)} ${hint}`;
897
+ this.questionMessage = `${SYMBOLS.QuestionMark} ${import_kleur5.default.bold(this.message)}${hint.length > 0 ? ` ${hint}` : ""}`;
846
898
  this.write(`${this.questionMessage}${import_node_os5.EOL}`);
847
899
  }
848
900
  };
package/dist/index.d.cts CHANGED
@@ -61,12 +61,15 @@ interface MultiselectOptions extends SharedOptions {
61
61
  validators?: PromptValidator[];
62
62
  autocomplete?: boolean;
63
63
  caseSensitive?: boolean;
64
+ showHint?: boolean;
64
65
  }
65
66
 
66
67
  interface SelectOptions extends SharedOptions {
67
68
  choices: (Choice | string)[];
68
69
  maxVisible?: number;
69
70
  ignoreValues?: (string | number | boolean)[];
71
+ autocomplete?: boolean;
72
+ caseSensitive?: boolean;
70
73
  }
71
74
 
72
75
  declare function question(message: string, options?: QuestionOptions): Promise<string>;
package/dist/index.d.ts CHANGED
@@ -61,12 +61,15 @@ interface MultiselectOptions extends SharedOptions {
61
61
  validators?: PromptValidator[];
62
62
  autocomplete?: boolean;
63
63
  caseSensitive?: boolean;
64
+ showHint?: boolean;
64
65
  }
65
66
 
66
67
  interface SelectOptions extends SharedOptions {
67
68
  choices: (Choice | string)[];
68
69
  maxVisible?: number;
69
70
  ignoreValues?: (string | number | boolean)[];
71
+ autocomplete?: boolean;
72
+ caseSensitive?: boolean;
70
73
  }
71
74
 
72
75
  declare function question(message: string, options?: QuestionOptions): Promise<string>;
package/dist/index.js CHANGED
@@ -339,10 +339,10 @@ var ConfirmPrompt = class extends AbstractPrompt {
339
339
  return `${query} ${this.#getHint()}`;
340
340
  }
341
341
  #onQuestionAnswer() {
342
- const defaultLines = this.fastAnswer ? 0 : 1;
342
+ this.clearLastLine();
343
343
  this.stdout.moveCursor(
344
344
  -this.stdout.columns,
345
- -(Math.floor(wcwidth2(stripAnsi(this.#getQuestionQuery())) / this.stdout.columns) || defaultLines)
345
+ -Math.floor(wcwidth2(stripAnsi(this.#getQuestionQuery())) / this.stdout.columns)
346
346
  );
347
347
  this.stdout.clearScreenDown();
348
348
  this.write(`${this.selectedValue ? SYMBOLS.Tick : SYMBOLS.Cross} ${kleur3.bold(this.message)}${EOL3}`);
@@ -373,18 +373,47 @@ var ConfirmPrompt = class extends AbstractPrompt {
373
373
  import { EOL as EOL4 } from "os";
374
374
  import kleur4 from "kleur";
375
375
  import wcwidth3 from "@topcli/wcwidth";
376
+ var kRequiredChoiceProperties = ["label", "value"];
376
377
  var SelectPrompt = class extends AbstractPrompt {
377
378
  #boundExitEvent = () => void 0;
378
379
  #boundKeyPressEvent = () => void 0;
379
380
  activeIndex = 0;
380
- selectedIndexes = [];
381
381
  questionMessage;
382
- longestChoicelength;
382
+ autocompleteValue = "";
383
383
  options;
384
384
  lastRender;
385
385
  get choices() {
386
386
  return this.options.choices;
387
387
  }
388
+ get filteredChoices() {
389
+ if (!(this.options.autocomplete && this.autocompleteValue.length > 0)) {
390
+ return this.choices;
391
+ }
392
+ const isCaseSensitive = this.options.caseSensitive;
393
+ const autocompleteValue = isCaseSensitive ? this.autocompleteValue : this.autocompleteValue.toLowerCase();
394
+ return this.choices.filter((choice) => this.#filterChoice(choice, autocompleteValue, isCaseSensitive));
395
+ }
396
+ #filterChoice(choice, autocompleteValue, isCaseSensitive = false) {
397
+ const choiceValue = typeof choice === "string" ? isCaseSensitive ? choice : choice.toLowerCase() : isCaseSensitive ? choice.label : choice.label.toLowerCase();
398
+ if (autocompleteValue.includes(" ")) {
399
+ return this.#filterMultipleWords(choiceValue, autocompleteValue, isCaseSensitive);
400
+ }
401
+ return choiceValue.includes(autocompleteValue);
402
+ }
403
+ #filterMultipleWords(choiceValue, autocompleteValue, isCaseSensitive) {
404
+ return autocompleteValue.split(" ").every((word) => {
405
+ const wordValue = isCaseSensitive ? word : word.toLowerCase();
406
+ return choiceValue.includes(wordValue) || choiceValue.includes(autocompleteValue);
407
+ });
408
+ }
409
+ get longestChoice() {
410
+ return Math.max(...this.filteredChoices.map((choice) => {
411
+ if (typeof choice === "string") {
412
+ return choice.length;
413
+ }
414
+ return choice.label.length;
415
+ }));
416
+ }
388
417
  constructor(message, options) {
389
418
  const {
390
419
  stdin = process.stdin,
@@ -401,22 +430,20 @@ var SelectPrompt = class extends AbstractPrompt {
401
430
  this.destroy();
402
431
  throw new TypeError("Missing required param: choices");
403
432
  }
404
- this.longestChoicelength = Math.max(...choices.map((choice) => {
433
+ for (const choice of choices) {
405
434
  if (typeof choice === "string") {
406
- return choice.length;
435
+ continue;
407
436
  }
408
- const kRequiredChoiceProperties2 = ["label", "value"];
409
- for (const prop of kRequiredChoiceProperties2) {
437
+ for (const prop of kRequiredChoiceProperties) {
410
438
  if (!choice[prop]) {
411
439
  this.destroy();
412
440
  throw new TypeError(`Missing ${prop} for choice ${JSON.stringify(choice)}`);
413
441
  }
414
442
  }
415
- return choice.label.length;
416
- }));
443
+ }
417
444
  }
418
445
  #getFormattedChoice(choiceIndex) {
419
- const choice = this.choices[choiceIndex];
446
+ const choice = this.filteredChoices[choiceIndex];
420
447
  if (typeof choice === "string") {
421
448
  return { value: choice, label: choice };
422
449
  }
@@ -424,21 +451,24 @@ var SelectPrompt = class extends AbstractPrompt {
424
451
  }
425
452
  #getVisibleChoices() {
426
453
  const maxVisible = this.options.maxVisible || 8;
427
- let startIndex = Math.min(this.choices.length - maxVisible, this.activeIndex - Math.floor(maxVisible / 2));
454
+ let startIndex = Math.min(this.filteredChoices.length - maxVisible, this.activeIndex - Math.floor(maxVisible / 2));
428
455
  if (startIndex < 0) {
429
456
  startIndex = 0;
430
457
  }
431
- const endIndex = Math.min(startIndex + maxVisible, this.choices.length);
458
+ const endIndex = Math.min(startIndex + maxVisible, this.filteredChoices.length);
432
459
  return { startIndex, endIndex };
433
460
  }
434
461
  #showChoices() {
435
462
  const { startIndex, endIndex } = this.#getVisibleChoices();
436
463
  this.lastRender = { startIndex, endIndex };
464
+ if (this.options.autocomplete) {
465
+ this.write(`${SYMBOLS.Pointer} ${this.autocompleteValue}${EOL4}`);
466
+ }
437
467
  for (let choiceIndex = startIndex; choiceIndex < endIndex; choiceIndex++) {
438
468
  const choice = this.#getFormattedChoice(choiceIndex);
439
469
  const isChoiceSelected = choiceIndex === this.activeIndex;
440
470
  const showPreviousChoicesArrow = startIndex > 0 && choiceIndex === startIndex;
441
- const showNextChoicesArrow = endIndex < this.choices.length && choiceIndex === endIndex - 1;
471
+ const showNextChoicesArrow = endIndex < this.filteredChoices.length && choiceIndex === endIndex - 1;
442
472
  let prefixArrow = " ";
443
473
  if (showPreviousChoicesArrow) {
444
474
  prefixArrow = SYMBOLS.Previous;
@@ -447,16 +477,17 @@ var SelectPrompt = class extends AbstractPrompt {
447
477
  }
448
478
  const prefix = `${prefixArrow}${isChoiceSelected ? `${SYMBOLS.Pointer} ` : " "}`;
449
479
  const formattedLabel = choice.label.padEnd(
450
- this.longestChoicelength < 10 ? this.longestChoicelength : 0
480
+ this.longestChoice < 10 ? this.longestChoice : 0
451
481
  );
452
482
  const formattedDescription = choice.description ? ` - ${choice.description}` : "";
453
483
  const color = isChoiceSelected ? kleur4.white().bold : kleur4.gray;
454
- const str = color(`${prefix}${formattedLabel}${formattedDescription}${EOL4}`);
484
+ const str = `${prefix}${color(`${formattedLabel}${formattedDescription}`)}${EOL4}`;
455
485
  this.write(str);
456
486
  }
457
487
  }
458
488
  #showAnsweredQuestion(choice) {
459
- const prefix = `${SYMBOLS.Tick} ${kleur4.bold(this.message)} ${SYMBOLS.Pointer}`;
489
+ const symbolPrefix = choice === "" ? SYMBOLS.Cross : SYMBOLS.Tick;
490
+ const prefix = `${symbolPrefix} ${kleur4.bold(this.message)} ${SYMBOLS.Pointer}`;
460
491
  const formattedChoice = kleur4.yellow(typeof choice === "string" ? choice : choice.label);
461
492
  this.write(`${prefix} ${formattedChoice}${EOL4}`);
462
493
  }
@@ -469,17 +500,18 @@ var SelectPrompt = class extends AbstractPrompt {
469
500
  #onKeypress(...args) {
470
501
  const [resolve, render, , key] = args;
471
502
  if (key.name === "up") {
472
- this.activeIndex = this.activeIndex === 0 ? this.choices.length - 1 : this.activeIndex - 1;
503
+ this.activeIndex = this.activeIndex === 0 ? this.filteredChoices.length - 1 : this.activeIndex - 1;
473
504
  render();
474
505
  } else if (key.name === "down") {
475
- this.activeIndex = this.activeIndex === this.choices.length - 1 ? 0 : this.activeIndex + 1;
506
+ this.activeIndex = this.activeIndex === this.filteredChoices.length - 1 ? 0 : this.activeIndex + 1;
476
507
  render();
477
508
  } else if (key.name === "return") {
509
+ const choice = this.filteredChoices[this.activeIndex] || "";
510
+ const label = typeof choice === "string" ? choice : choice.label;
511
+ const value = typeof choice === "string" ? choice : choice.value;
478
512
  render({ clearRender: true });
479
- const currentChoice = this.choices[this.activeIndex];
480
- const value = typeof currentChoice === "string" ? currentChoice : currentChoice.value;
481
513
  if (!this.options.ignoreValues?.includes(value)) {
482
- this.#showAnsweredQuestion(currentChoice);
514
+ this.#showAnsweredQuestion(label);
483
515
  }
484
516
  this.write(SYMBOLS.ShowCursor);
485
517
  this.destroy();
@@ -487,6 +519,14 @@ var SelectPrompt = class extends AbstractPrompt {
487
519
  process.off("exit", this.#boundExitEvent);
488
520
  resolve(value);
489
521
  } else {
522
+ if (!key.ctrl && this.options.autocomplete) {
523
+ this.activeIndex = 0;
524
+ if (key.name === "backspace" && this.autocompleteValue.length > 0) {
525
+ this.autocompleteValue = this.autocompleteValue.slice(0, -1);
526
+ } else if (key.name !== "backspace") {
527
+ this.autocompleteValue += key.sequence;
528
+ }
529
+ }
490
530
  render();
491
531
  }
492
532
  }
@@ -510,6 +550,15 @@ var SelectPrompt = class extends AbstractPrompt {
510
550
  this.clearLastLine();
511
551
  linesToClear--;
512
552
  }
553
+ if (this.options.autocomplete) {
554
+ let linesToClear2 = Math.ceil(
555
+ wcwidth3(`${SYMBOLS.Pointer} ${this.autocompleteValue}`) / this.stdout.columns
556
+ );
557
+ while (linesToClear2 > 0) {
558
+ this.clearLastLine();
559
+ linesToClear2--;
560
+ }
561
+ }
513
562
  }
514
563
  if (clearRender) {
515
564
  const questionLineCount = Math.ceil(
@@ -539,11 +588,12 @@ var SelectPrompt = class extends AbstractPrompt {
539
588
  import { EOL as EOL5 } from "os";
540
589
  import kleur5 from "kleur";
541
590
  import wcwidth4 from "@topcli/wcwidth";
542
- var kRequiredChoiceProperties = ["label", "value"];
591
+ var kRequiredChoiceProperties2 = ["label", "value"];
543
592
  var MultiselectPrompt = class extends AbstractPrompt {
544
593
  #boundExitEvent = () => void 0;
545
594
  #boundKeyPressEvent = () => void 0;
546
595
  #validators;
596
+ #showHint;
547
597
  activeIndex = 0;
548
598
  selectedIndexes = [];
549
599
  questionMessage;
@@ -588,7 +638,8 @@ var MultiselectPrompt = class extends AbstractPrompt {
588
638
  stdout = process.stdout,
589
639
  choices,
590
640
  preSelectedChoices,
591
- validators = []
641
+ validators = [],
642
+ showHint = true
592
643
  } = options ?? {};
593
644
  super(message, stdin, stdout);
594
645
  if (!options) {
@@ -601,11 +652,12 @@ var MultiselectPrompt = class extends AbstractPrompt {
601
652
  throw new TypeError("Missing required param: choices");
602
653
  }
603
654
  this.#validators = validators;
655
+ this.#showHint = showHint;
604
656
  for (const choice of choices) {
605
657
  if (typeof choice === "string") {
606
658
  continue;
607
659
  }
608
- for (const prop of kRequiredChoiceProperties) {
660
+ for (const prop of kRequiredChoiceProperties2) {
609
661
  if (!choice[prop]) {
610
662
  this.destroy();
611
663
  throw new TypeError(`Missing ${prop} for choice ${JSON.stringify(choice)}`);
@@ -795,14 +847,14 @@ var MultiselectPrompt = class extends AbstractPrompt {
795
847
  });
796
848
  }
797
849
  #showQuestion(error = null) {
798
- let hint = kleur5.gray(
850
+ let hint = this.#showHint ? kleur5.gray(
799
851
  // eslint-disable-next-line max-len
800
- `(Press ${kleur5.bold("<Ctrl+A>")} to toggle all, ${kleur5.bold("<Ctrl+Space>")} to select, ${kleur5.bold("<Left/Right>")} to toggle, ${kleur5.bold("<Return>")} to submit)`
801
- );
852
+ `(Press ${kleur5.bold("<Ctrl+A>")} to toggle all, ${kleur5.bold("<Left/Right>")} to toggle, ${kleur5.bold("<Return>")} to submit)`
853
+ ) : "";
802
854
  if (error) {
803
- hint += ` ${kleur5.red().bold(`[${error}]`)}`;
855
+ hint += `${hint.length > 0 ? " " : ""}${kleur5.red().bold(`[${error}]`)}`;
804
856
  }
805
- this.questionMessage = `${SYMBOLS.QuestionMark} ${kleur5.bold(this.message)} ${hint}`;
857
+ this.questionMessage = `${SYMBOLS.QuestionMark} ${kleur5.bold(this.message)}${hint.length > 0 ? ` ${hint}` : ""}`;
806
858
  this.write(`${this.questionMessage}${EOL5}`);
807
859
  }
808
860
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topcli/prompts",
3
- "version": "1.8.1",
3
+ "version": "1.9.0",
4
4
  "description": "Node.js user input library for command-line interfaces.",
5
5
  "scripts": {
6
6
  "build": "tsup index.ts --format cjs,esm --dts --clean",