@topcli/prompts 1.8.1 → 1.10.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/dist/index.cjs CHANGED
@@ -47,6 +47,7 @@ var import_wcwidth = __toESM(require("@topcli/wcwidth"), 1);
47
47
  var import_node_os = require("os");
48
48
  var import_node_readline = require("readline");
49
49
  var import_node_stream = require("stream");
50
+ var import_node_events = __toESM(require("events"), 1);
50
51
 
51
52
  // src/utils.ts
52
53
  var import_node_process = __toESM(require("process"), 1);
@@ -112,25 +113,43 @@ var PromptAgent = class _PromptAgent {
112
113
  }
113
114
  };
114
115
 
116
+ // src/errors/abort.ts
117
+ var AbortError = class extends Error {
118
+ constructor(message) {
119
+ super(message);
120
+ this.name = "AbortError";
121
+ }
122
+ };
123
+
115
124
  // src/prompts/abstract.ts
116
- var AbstractPrompt = class _AbstractPrompt {
125
+ var AbstractPrompt = class _AbstractPrompt extends import_node_events.default {
117
126
  stdin;
118
127
  stdout;
119
128
  message;
129
+ signal;
120
130
  history;
121
131
  agent;
122
132
  mute;
123
133
  rl;
124
- constructor(message, input = process.stdin, output = process.stdout) {
134
+ #signalHandler;
135
+ constructor(options) {
136
+ super();
125
137
  if (this.constructor === _AbstractPrompt) {
126
138
  throw new Error("AbstractPrompt can't be instantiated.");
127
139
  }
140
+ const {
141
+ stdin: input = process.stdin,
142
+ stdout: output = process.stdout,
143
+ message,
144
+ signal
145
+ } = options;
128
146
  if (typeof message !== "string") {
129
147
  throw new TypeError(`message must be string, ${typeof message} given.`);
130
148
  }
131
149
  this.stdin = input;
132
150
  this.stdout = output;
133
151
  this.message = message;
152
+ this.signal = signal;
134
153
  this.history = [];
135
154
  this.agent = PromptAgent.agent();
136
155
  this.mute = false;
@@ -141,7 +160,7 @@ var AbstractPrompt = class _AbstractPrompt {
141
160
  input,
142
161
  output: new import_node_stream.Writable({
143
162
  write: (chunk, encoding, callback) => {
144
- if (!this.mute) {
163
+ if (!this.mute && chunk) {
145
164
  this.stdout.write(chunk, encoding);
146
165
  }
147
166
  callback();
@@ -149,6 +168,16 @@ var AbstractPrompt = class _AbstractPrompt {
149
168
  }),
150
169
  terminal: true
151
170
  });
171
+ if (this.signal) {
172
+ this.#signalHandler = () => {
173
+ this.rl.close();
174
+ for (let i = 0; i < this.history.length; i++) {
175
+ this.clearLastLine();
176
+ }
177
+ this.emit("error", new AbortError("Prompt aborted"));
178
+ };
179
+ this.signal.addEventListener("abort", this.#signalHandler, { once: true });
180
+ }
152
181
  }
153
182
  write(data) {
154
183
  const formattedData = stripAnsi(data).replace(import_node_os.EOL, "");
@@ -168,6 +197,9 @@ var AbstractPrompt = class _AbstractPrompt {
168
197
  }
169
198
  destroy() {
170
199
  this.rl.close();
200
+ if (this.signal) {
201
+ this.signal.removeEventListener("abort", this.#signalHandler);
202
+ }
171
203
  }
172
204
  };
173
205
 
@@ -215,15 +247,14 @@ var QuestionPrompt = class extends AbstractPrompt {
215
247
  answerBuffer;
216
248
  #validators;
217
249
  #secure;
218
- constructor(message, options = {}) {
250
+ constructor(options) {
219
251
  const {
220
- stdin = process.stdin,
221
- stdout = process.stdout,
222
252
  defaultValue,
223
253
  validators = [],
224
- secure = false
254
+ secure = false,
255
+ ...baseOptions
225
256
  } = options;
226
- super(message, stdin, stdout);
257
+ super({ ...baseOptions });
227
258
  if (defaultValue && typeof defaultValue !== "string") {
228
259
  throw new TypeError("defaultValue must be a string");
229
260
  }
@@ -236,6 +267,7 @@ var QuestionPrompt = class extends AbstractPrompt {
236
267
  #question() {
237
268
  return new Promise((resolve) => {
238
269
  const questionQuery = this.#getQuestionQuery();
270
+ this.history.push(questionQuery);
239
271
  this.rl.question(questionQuery, (answer) => {
240
272
  this.history.push(questionQuery + answer);
241
273
  this.mute = false;
@@ -273,24 +305,30 @@ var QuestionPrompt = class extends AbstractPrompt {
273
305
  this.answerBuffer = void 0;
274
306
  this.#writeAnswer();
275
307
  }
276
- async question() {
277
- this.answer = this.agent.nextAnswers.shift();
278
- if (this.answer !== void 0) {
279
- this.#writeAnswer();
280
- this.destroy();
281
- return this.answer;
282
- }
283
- this.answer = await this.#question();
284
- if (this.answer === "" && this.defaultValue) {
285
- this.answer = this.defaultValue;
286
- }
287
- this.#onQuestionAnswer();
288
- while (this.answerBuffer !== void 0) {
289
- this.answer = await this.answerBuffer;
308
+ question() {
309
+ return new Promise(async (resolve, reject) => {
310
+ this.answer = this.agent.nextAnswers.shift();
311
+ if (this.answer !== void 0) {
312
+ this.#writeAnswer();
313
+ this.destroy();
314
+ resolve(this.answer);
315
+ return;
316
+ }
317
+ this.once("error", (error) => {
318
+ reject(error);
319
+ });
320
+ this.answer = await this.#question();
321
+ if (this.answer === "" && this.defaultValue) {
322
+ this.answer = this.defaultValue;
323
+ }
290
324
  this.#onQuestionAnswer();
291
- }
292
- this.destroy();
293
- return this.answer;
325
+ while (this.answerBuffer !== void 0) {
326
+ this.answer = await this.answerBuffer;
327
+ this.#onQuestionAnswer();
328
+ }
329
+ this.destroy();
330
+ resolve(this.answer);
331
+ });
294
332
  }
295
333
  };
296
334
 
@@ -317,13 +355,12 @@ var ConfirmPrompt = class extends AbstractPrompt {
317
355
  fastAnswer;
318
356
  #boundKeyPressEvent;
319
357
  #boundExitEvent;
320
- constructor(message, options = {}) {
358
+ constructor(options) {
321
359
  const {
322
- stdin = process.stdin,
323
- stdout = process.stdout,
324
- initial = false
360
+ initial = false,
361
+ ...baseOptions
325
362
  } = options;
326
- super(message, stdin, stdout);
363
+ super({ ...baseOptions });
327
364
  this.initial = initial;
328
365
  this.selectedValue = initial;
329
366
  }
@@ -379,33 +416,39 @@ var ConfirmPrompt = class extends AbstractPrompt {
379
416
  return `${query} ${this.#getHint()}`;
380
417
  }
381
418
  #onQuestionAnswer() {
382
- const defaultLines = this.fastAnswer ? 0 : 1;
419
+ this.clearLastLine();
383
420
  this.stdout.moveCursor(
384
421
  -this.stdout.columns,
385
- -(Math.floor((0, import_wcwidth2.default)(stripAnsi(this.#getQuestionQuery())) / this.stdout.columns) || defaultLines)
422
+ -Math.floor((0, import_wcwidth2.default)(stripAnsi(this.#getQuestionQuery())) / this.stdout.columns)
386
423
  );
387
424
  this.stdout.clearScreenDown();
388
425
  this.write(`${this.selectedValue ? SYMBOLS.Tick : SYMBOLS.Cross} ${import_kleur3.default.bold(this.message)}${import_node_os3.EOL}`);
389
426
  }
390
- async confirm() {
391
- const answer = this.agent.nextAnswers.shift();
392
- if (answer !== void 0) {
393
- this.selectedValue = answer;
394
- this.#onQuestionAnswer();
395
- this.destroy();
396
- return answer;
397
- }
398
- this.write(SYMBOLS.HideCursor);
399
- try {
400
- await this.#question();
401
- this.#onQuestionAnswer();
402
- return this.selectedValue;
403
- } finally {
404
- this.write(SYMBOLS.ShowCursor);
405
- this.#onProcessExit();
406
- process.off("exit", this.#boundExitEvent);
407
- this.destroy();
408
- }
427
+ confirm() {
428
+ return new Promise(async (resolve, reject) => {
429
+ const answer = this.agent.nextAnswers.shift();
430
+ if (answer !== void 0) {
431
+ this.selectedValue = answer;
432
+ this.#onQuestionAnswer();
433
+ this.destroy();
434
+ resolve(answer);
435
+ return;
436
+ }
437
+ this.once("error", (error) => {
438
+ reject(error);
439
+ });
440
+ this.write(SYMBOLS.HideCursor);
441
+ try {
442
+ await this.#question();
443
+ this.#onQuestionAnswer();
444
+ resolve(this.selectedValue);
445
+ } finally {
446
+ this.write(SYMBOLS.ShowCursor);
447
+ this.#onProcessExit();
448
+ process.off("exit", this.#boundExitEvent);
449
+ this.destroy();
450
+ }
451
+ });
409
452
  }
410
453
  };
411
454
 
@@ -413,50 +456,75 @@ var ConfirmPrompt = class extends AbstractPrompt {
413
456
  var import_node_os4 = require("os");
414
457
  var import_kleur4 = __toESM(require("kleur"), 1);
415
458
  var import_wcwidth3 = __toESM(require("@topcli/wcwidth"), 1);
459
+ var kRequiredChoiceProperties = ["label", "value"];
416
460
  var SelectPrompt = class extends AbstractPrompt {
417
461
  #boundExitEvent = () => void 0;
418
462
  #boundKeyPressEvent = () => void 0;
463
+ #validators;
419
464
  activeIndex = 0;
420
- selectedIndexes = [];
421
465
  questionMessage;
422
- longestChoicelength;
466
+ autocompleteValue = "";
423
467
  options;
424
468
  lastRender;
425
469
  get choices() {
426
470
  return this.options.choices;
427
471
  }
428
- constructor(message, options) {
429
- const {
430
- stdin = process.stdin,
431
- stdout = process.stdout,
432
- choices
433
- } = options ?? {};
434
- super(message, stdin, stdout);
435
- if (!options) {
436
- this.destroy();
437
- throw new TypeError("Missing required options");
472
+ get filteredChoices() {
473
+ if (!(this.options.autocomplete && this.autocompleteValue.length > 0)) {
474
+ return this.choices;
438
475
  }
476
+ const isCaseSensitive = this.options.caseSensitive;
477
+ const autocompleteValue = isCaseSensitive ? this.autocompleteValue : this.autocompleteValue.toLowerCase();
478
+ return this.choices.filter((choice) => this.#filterChoice(choice, autocompleteValue, isCaseSensitive));
479
+ }
480
+ #filterChoice(choice, autocompleteValue, isCaseSensitive = false) {
481
+ const choiceValue = typeof choice === "string" ? isCaseSensitive ? choice : choice.toLowerCase() : isCaseSensitive ? choice.label : choice.label.toLowerCase();
482
+ if (autocompleteValue.includes(" ")) {
483
+ return this.#filterMultipleWords(choiceValue, autocompleteValue, isCaseSensitive);
484
+ }
485
+ return choiceValue.includes(autocompleteValue);
486
+ }
487
+ #filterMultipleWords(choiceValue, autocompleteValue, isCaseSensitive) {
488
+ return autocompleteValue.split(" ").every((word) => {
489
+ const wordValue = isCaseSensitive ? word : word.toLowerCase();
490
+ return choiceValue.includes(wordValue) || choiceValue.includes(autocompleteValue);
491
+ });
492
+ }
493
+ get longestChoice() {
494
+ return Math.max(...this.filteredChoices.map((choice) => {
495
+ if (typeof choice === "string") {
496
+ return choice.length;
497
+ }
498
+ return choice.label.length;
499
+ }));
500
+ }
501
+ constructor(options) {
502
+ const {
503
+ choices,
504
+ validators = [],
505
+ ...baseOptions
506
+ } = options;
507
+ super({ ...baseOptions });
439
508
  this.options = options;
440
509
  if (!choices?.length) {
441
510
  this.destroy();
442
511
  throw new TypeError("Missing required param: choices");
443
512
  }
444
- this.longestChoicelength = Math.max(...choices.map((choice) => {
513
+ this.#validators = validators;
514
+ for (const choice of choices) {
445
515
  if (typeof choice === "string") {
446
- return choice.length;
516
+ continue;
447
517
  }
448
- const kRequiredChoiceProperties2 = ["label", "value"];
449
- for (const prop of kRequiredChoiceProperties2) {
518
+ for (const prop of kRequiredChoiceProperties) {
450
519
  if (!choice[prop]) {
451
520
  this.destroy();
452
521
  throw new TypeError(`Missing ${prop} for choice ${JSON.stringify(choice)}`);
453
522
  }
454
523
  }
455
- return choice.label.length;
456
- }));
524
+ }
457
525
  }
458
526
  #getFormattedChoice(choiceIndex) {
459
- const choice = this.choices[choiceIndex];
527
+ const choice = this.filteredChoices[choiceIndex];
460
528
  if (typeof choice === "string") {
461
529
  return { value: choice, label: choice };
462
530
  }
@@ -464,21 +532,24 @@ var SelectPrompt = class extends AbstractPrompt {
464
532
  }
465
533
  #getVisibleChoices() {
466
534
  const maxVisible = this.options.maxVisible || 8;
467
- let startIndex = Math.min(this.choices.length - maxVisible, this.activeIndex - Math.floor(maxVisible / 2));
535
+ let startIndex = Math.min(this.filteredChoices.length - maxVisible, this.activeIndex - Math.floor(maxVisible / 2));
468
536
  if (startIndex < 0) {
469
537
  startIndex = 0;
470
538
  }
471
- const endIndex = Math.min(startIndex + maxVisible, this.choices.length);
539
+ const endIndex = Math.min(startIndex + maxVisible, this.filteredChoices.length);
472
540
  return { startIndex, endIndex };
473
541
  }
474
542
  #showChoices() {
475
543
  const { startIndex, endIndex } = this.#getVisibleChoices();
476
544
  this.lastRender = { startIndex, endIndex };
545
+ if (this.options.autocomplete) {
546
+ this.write(`${SYMBOLS.Pointer} ${this.autocompleteValue}${import_node_os4.EOL}`);
547
+ }
477
548
  for (let choiceIndex = startIndex; choiceIndex < endIndex; choiceIndex++) {
478
549
  const choice = this.#getFormattedChoice(choiceIndex);
479
550
  const isChoiceSelected = choiceIndex === this.activeIndex;
480
551
  const showPreviousChoicesArrow = startIndex > 0 && choiceIndex === startIndex;
481
- const showNextChoicesArrow = endIndex < this.choices.length && choiceIndex === endIndex - 1;
552
+ const showNextChoicesArrow = endIndex < this.filteredChoices.length && choiceIndex === endIndex - 1;
482
553
  let prefixArrow = " ";
483
554
  if (showPreviousChoicesArrow) {
484
555
  prefixArrow = SYMBOLS.Previous;
@@ -487,16 +558,17 @@ var SelectPrompt = class extends AbstractPrompt {
487
558
  }
488
559
  const prefix = `${prefixArrow}${isChoiceSelected ? `${SYMBOLS.Pointer} ` : " "}`;
489
560
  const formattedLabel = choice.label.padEnd(
490
- this.longestChoicelength < 10 ? this.longestChoicelength : 0
561
+ this.longestChoice < 10 ? this.longestChoice : 0
491
562
  );
492
563
  const formattedDescription = choice.description ? ` - ${choice.description}` : "";
493
564
  const color = isChoiceSelected ? import_kleur4.default.white().bold : import_kleur4.default.gray;
494
- const str = color(`${prefix}${formattedLabel}${formattedDescription}${import_node_os4.EOL}`);
565
+ const str = `${prefix}${color(`${formattedLabel}${formattedDescription}`)}${import_node_os4.EOL}`;
495
566
  this.write(str);
496
567
  }
497
568
  }
498
569
  #showAnsweredQuestion(choice) {
499
- const prefix = `${SYMBOLS.Tick} ${import_kleur4.default.bold(this.message)} ${SYMBOLS.Pointer}`;
570
+ const symbolPrefix = choice === "" ? SYMBOLS.Cross : SYMBOLS.Tick;
571
+ const prefix = `${symbolPrefix} ${import_kleur4.default.bold(this.message)} ${SYMBOLS.Pointer}`;
500
572
  const formattedChoice = import_kleur4.default.yellow(typeof choice === "string" ? choice : choice.label);
501
573
  this.write(`${prefix} ${formattedChoice}${import_node_os4.EOL}`);
502
574
  }
@@ -509,17 +581,25 @@ var SelectPrompt = class extends AbstractPrompt {
509
581
  #onKeypress(...args) {
510
582
  const [resolve, render, , key] = args;
511
583
  if (key.name === "up") {
512
- this.activeIndex = this.activeIndex === 0 ? this.choices.length - 1 : this.activeIndex - 1;
584
+ this.activeIndex = this.activeIndex === 0 ? this.filteredChoices.length - 1 : this.activeIndex - 1;
513
585
  render();
514
586
  } else if (key.name === "down") {
515
- this.activeIndex = this.activeIndex === this.choices.length - 1 ? 0 : this.activeIndex + 1;
587
+ this.activeIndex = this.activeIndex === this.filteredChoices.length - 1 ? 0 : this.activeIndex + 1;
516
588
  render();
517
589
  } else if (key.name === "return") {
590
+ const choice = this.filteredChoices[this.activeIndex] || "";
591
+ const label = typeof choice === "string" ? choice : choice.label;
592
+ const value = typeof choice === "string" ? choice : choice.value;
593
+ for (const validator of this.#validators) {
594
+ if (!validator.validate(value)) {
595
+ const error = validator.error(value);
596
+ render({ error });
597
+ return;
598
+ }
599
+ }
518
600
  render({ clearRender: true });
519
- const currentChoice = this.choices[this.activeIndex];
520
- const value = typeof currentChoice === "string" ? currentChoice : currentChoice.value;
521
601
  if (!this.options.ignoreValues?.includes(value)) {
522
- this.#showAnsweredQuestion(currentChoice);
602
+ this.#showAnsweredQuestion(label);
523
603
  }
524
604
  this.write(SYMBOLS.ShowCursor);
525
605
  this.destroy();
@@ -527,50 +607,82 @@ var SelectPrompt = class extends AbstractPrompt {
527
607
  process.off("exit", this.#boundExitEvent);
528
608
  resolve(value);
529
609
  } else {
610
+ if (!key.ctrl && this.options.autocomplete) {
611
+ this.activeIndex = 0;
612
+ if (key.name === "backspace" && this.autocompleteValue.length > 0) {
613
+ this.autocompleteValue = this.autocompleteValue.slice(0, -1);
614
+ } else if (key.name !== "backspace") {
615
+ this.autocompleteValue += key.sequence;
616
+ }
617
+ }
530
618
  render();
531
619
  }
532
620
  }
533
- async select() {
534
- const answer = this.agent.nextAnswers.shift();
535
- if (answer !== void 0) {
536
- this.#showAnsweredQuestion(answer);
537
- this.destroy();
538
- return answer;
539
- }
540
- this.write(SYMBOLS.HideCursor);
541
- this.#showQuestion();
542
- const render = (options = {}) => {
543
- const {
544
- initialRender = false,
545
- clearRender = false
546
- } = options;
547
- if (!initialRender) {
548
- let linesToClear = this.lastRender.endIndex - this.lastRender.startIndex;
549
- while (linesToClear > 0) {
550
- this.clearLastLine();
551
- linesToClear--;
552
- }
553
- }
554
- if (clearRender) {
555
- const questionLineCount = Math.ceil(
556
- (0, import_wcwidth3.default)(stripAnsi(this.questionMessage)) / this.stdout.columns
557
- );
558
- this.stdout.moveCursor(-this.stdout.columns, -(1 + questionLineCount));
559
- this.stdout.clearScreenDown();
621
+ select() {
622
+ return new Promise((resolve, reject) => {
623
+ const answer = this.agent.nextAnswers.shift();
624
+ if (answer !== void 0) {
625
+ this.#showAnsweredQuestion(answer);
626
+ this.destroy();
627
+ resolve(answer);
560
628
  return;
561
629
  }
562
- this.#showChoices();
563
- };
564
- render({ initialRender: true });
565
- return new Promise((resolve) => {
630
+ this.once("error", (error) => {
631
+ reject(error);
632
+ });
633
+ this.write(SYMBOLS.HideCursor);
634
+ this.#showQuestion();
635
+ const render = (options = {}) => {
636
+ const {
637
+ initialRender = false,
638
+ clearRender = false,
639
+ error = null
640
+ } = options;
641
+ if (!initialRender) {
642
+ let linesToClear = this.lastRender.endIndex - this.lastRender.startIndex;
643
+ while (linesToClear > 0) {
644
+ this.clearLastLine();
645
+ linesToClear--;
646
+ }
647
+ if (this.options.autocomplete) {
648
+ let linesToClear2 = Math.ceil(
649
+ (0, import_wcwidth3.default)(`${SYMBOLS.Pointer} ${this.autocompleteValue}`) / this.stdout.columns
650
+ );
651
+ while (linesToClear2 > 0) {
652
+ this.clearLastLine();
653
+ linesToClear2--;
654
+ }
655
+ }
656
+ }
657
+ if (clearRender) {
658
+ const questionLineCount = Math.ceil(
659
+ (0, import_wcwidth3.default)(stripAnsi(this.questionMessage)) / this.stdout.columns
660
+ );
661
+ this.stdout.moveCursor(-this.stdout.columns, -(1 + questionLineCount));
662
+ this.stdout.clearScreenDown();
663
+ return;
664
+ }
665
+ if (error) {
666
+ const linesToClear = Math.ceil((0, import_wcwidth3.default)(this.questionMessage) / this.stdout.columns) + 1;
667
+ this.stdout.moveCursor(0, -linesToClear);
668
+ this.stdout.clearScreenDown();
669
+ this.#showQuestion(error);
670
+ }
671
+ this.#showChoices();
672
+ };
673
+ render({ initialRender: true });
566
674
  this.#boundKeyPressEvent = this.#onKeypress.bind(this, resolve, render);
567
675
  this.stdin.on("keypress", this.#boundKeyPressEvent);
568
676
  this.#boundExitEvent = this.#onProcessExit.bind(this);
569
677
  process.once("exit", this.#boundExitEvent);
570
678
  });
571
679
  }
572
- #showQuestion() {
573
- this.questionMessage = `${SYMBOLS.QuestionMark} ${import_kleur4.default.bold(this.message)}`;
680
+ #showQuestion(error = null) {
681
+ let hint = "";
682
+ if (error) {
683
+ hint = ` ${hint.length > 0 ? " " : ""}${import_kleur4.default.red().bold(`[${error}]`)}`;
684
+ }
685
+ this.questionMessage = `${SYMBOLS.QuestionMark} ${import_kleur4.default.bold(this.message)}${hint}`;
574
686
  this.write(`${this.questionMessage}${import_node_os4.EOL}`);
575
687
  }
576
688
  };
@@ -579,13 +691,14 @@ var SelectPrompt = class extends AbstractPrompt {
579
691
  var import_node_os5 = require("os");
580
692
  var import_kleur5 = __toESM(require("kleur"), 1);
581
693
  var import_wcwidth4 = __toESM(require("@topcli/wcwidth"), 1);
582
- var kRequiredChoiceProperties = ["label", "value"];
694
+ var kRequiredChoiceProperties2 = ["label", "value"];
583
695
  var MultiselectPrompt = class extends AbstractPrompt {
584
696
  #boundExitEvent = () => void 0;
585
697
  #boundKeyPressEvent = () => void 0;
586
698
  #validators;
699
+ #showHint;
587
700
  activeIndex = 0;
588
- selectedIndexes = [];
701
+ selectedIndexes = /* @__PURE__ */ new Set();
589
702
  questionMessage;
590
703
  autocompleteValue = "";
591
704
  options;
@@ -622,30 +735,27 @@ var MultiselectPrompt = class extends AbstractPrompt {
622
735
  return choice.label.length;
623
736
  }));
624
737
  }
625
- constructor(message, options) {
738
+ constructor(options) {
626
739
  const {
627
- stdin = process.stdin,
628
- stdout = process.stdout,
629
740
  choices,
630
741
  preSelectedChoices,
631
- validators = []
632
- } = options ?? {};
633
- super(message, stdin, stdout);
634
- if (!options) {
635
- this.destroy();
636
- throw new TypeError("Missing required options");
637
- }
742
+ validators = [],
743
+ showHint = true,
744
+ ...baseOptions
745
+ } = options;
746
+ super({ ...baseOptions });
638
747
  this.options = options;
639
748
  if (!choices?.length) {
640
749
  this.destroy();
641
750
  throw new TypeError("Missing required param: choices");
642
751
  }
643
752
  this.#validators = validators;
753
+ this.#showHint = showHint;
644
754
  for (const choice of choices) {
645
755
  if (typeof choice === "string") {
646
756
  continue;
647
757
  }
648
- for (const prop of kRequiredChoiceProperties) {
758
+ for (const prop of kRequiredChoiceProperties2) {
649
759
  if (!choice[prop]) {
650
760
  this.destroy();
651
761
  throw new TypeError(`Missing ${prop} for choice ${JSON.stringify(choice)}`);
@@ -666,7 +776,7 @@ var MultiselectPrompt = class extends AbstractPrompt {
666
776
  this.destroy();
667
777
  throw new Error(`Invalid pre-selected choice: ${typeof choice === "string" ? choice : choice.value}`);
668
778
  }
669
- this.selectedIndexes.push(choiceIndex);
779
+ this.selectedIndexes.add(choiceIndex);
670
780
  }
671
781
  }
672
782
  #getFormattedChoice(choiceIndex) {
@@ -694,7 +804,7 @@ var MultiselectPrompt = class extends AbstractPrompt {
694
804
  for (let choiceIndex = startIndex; choiceIndex < endIndex; choiceIndex++) {
695
805
  const choice = this.#getFormattedChoice(choiceIndex);
696
806
  const isChoiceActive = choiceIndex === this.activeIndex;
697
- const isChoiceSelected = this.selectedIndexes.includes(choiceIndex);
807
+ const isChoiceSelected = this.selectedIndexes.has(choiceIndex);
698
808
  const showPreviousChoicesArrow = startIndex > 0 && choiceIndex === startIndex;
699
809
  const showNextChoicesArrow = endIndex < this.filteredChoices.length && choiceIndex === endIndex - 1;
700
810
  let prefixArrow = " ";
@@ -714,7 +824,7 @@ var MultiselectPrompt = class extends AbstractPrompt {
714
824
  }
715
825
  }
716
826
  #showAnsweredQuestion(choices, isAgentAnswer = false) {
717
- const prefixSymbol = this.selectedIndexes.length === 0 && !isAgentAnswer ? SYMBOLS.Cross : SYMBOLS.Tick;
827
+ const prefixSymbol = this.selectedIndexes.size === 0 && !isAgentAnswer ? SYMBOLS.Cross : SYMBOLS.Tick;
718
828
  const prefix = `${prefixSymbol} ${import_kleur5.default.bold(this.message)} ${SYMBOLS.Pointer}`;
719
829
  const formattedChoice = import_kleur5.default.yellow(choices);
720
830
  this.write(`${prefix}${choices ? ` ${formattedChoice}` : ""}${import_node_os5.EOL}`);
@@ -734,20 +844,20 @@ var MultiselectPrompt = class extends AbstractPrompt {
734
844
  this.activeIndex = this.activeIndex === this.filteredChoices.length - 1 ? 0 : this.activeIndex + 1;
735
845
  render();
736
846
  } else if (key.ctrl && key.name === "a") {
737
- this.selectedIndexes = this.selectedIndexes.length === this.filteredChoices.length ? [] : this.filteredChoices.map((_, index) => index);
847
+ this.selectedIndexes = this.selectedIndexes.size === this.filteredChoices.length ? /* @__PURE__ */ new Set() : new Set(this.filteredChoices.map((_, index) => index));
738
848
  render();
739
849
  } else if (key.name === "right") {
740
- this.selectedIndexes.push(this.activeIndex);
850
+ this.selectedIndexes.add(this.activeIndex);
741
851
  render();
742
852
  } else if (key.name === "left") {
743
- this.selectedIndexes = this.selectedIndexes.filter((index) => index !== this.activeIndex);
853
+ this.selectedIndexes = new Set([...this.selectedIndexes].filter((index) => index !== this.activeIndex));
744
854
  render();
745
855
  } else if (key.name === "return") {
746
- const labels = this.selectedIndexes.map((index) => {
856
+ const labels = [...this.selectedIndexes].map((index) => {
747
857
  const choice = this.filteredChoices[index];
748
858
  return typeof choice === "string" ? choice : choice.label;
749
859
  });
750
- const values = this.selectedIndexes.map((index) => {
860
+ const values = [...this.selectedIndexes].map((index) => {
751
861
  const choice = this.filteredChoices[index];
752
862
  return typeof choice === "string" ? choice : choice.value;
753
863
  });
@@ -767,7 +877,7 @@ var MultiselectPrompt = class extends AbstractPrompt {
767
877
  resolve(values);
768
878
  } else {
769
879
  if (!key.ctrl && this.options.autocomplete) {
770
- this.selectedIndexes = [];
880
+ this.selectedIndexes.clear();
771
881
  this.activeIndex = 0;
772
882
  if (key.name === "backspace" && this.autocompleteValue.length > 0) {
773
883
  this.autocompleteValue = this.autocompleteValue.slice(0, -1);
@@ -778,56 +888,60 @@ var MultiselectPrompt = class extends AbstractPrompt {
778
888
  render();
779
889
  }
780
890
  }
781
- async multiselect() {
782
- const answer = this.agent.nextAnswers.shift();
783
- if (answer !== void 0) {
784
- const formatedAnser = Array.isArray(answer) ? answer.join(", ") : answer;
785
- this.#showAnsweredQuestion(formatedAnser, true);
786
- this.destroy();
787
- return Array.isArray(answer) ? answer : [answer];
788
- }
789
- this.write(SYMBOLS.HideCursor);
790
- this.#showQuestion();
791
- const render = (options = {}) => {
792
- const {
793
- initialRender = false,
794
- clearRender = false,
795
- error = null
796
- } = options;
797
- if (!initialRender) {
798
- let linesToClear = this.lastRender.endIndex - this.lastRender.startIndex;
799
- while (linesToClear > 0) {
800
- this.clearLastLine();
801
- linesToClear--;
802
- }
803
- if (this.options.autocomplete) {
804
- let linesToClear2 = Math.ceil(
805
- (0, import_wcwidth4.default)(`${SYMBOLS.Pointer} ${this.autocompleteValue}`) / this.stdout.columns
806
- );
807
- while (linesToClear2 > 0) {
891
+ multiselect() {
892
+ return new Promise((resolve, reject) => {
893
+ const answer = this.agent.nextAnswers.shift();
894
+ if (answer !== void 0) {
895
+ const formatedAnser = Array.isArray(answer) ? answer.join(", ") : answer;
896
+ this.#showAnsweredQuestion(formatedAnser, true);
897
+ this.destroy();
898
+ resolve(Array.isArray(answer) ? answer : [answer]);
899
+ return;
900
+ }
901
+ this.once("error", (error) => {
902
+ reject(error);
903
+ });
904
+ this.write(SYMBOLS.HideCursor);
905
+ this.#showQuestion();
906
+ const render = (options = {}) => {
907
+ const {
908
+ initialRender = false,
909
+ clearRender = false,
910
+ error = null
911
+ } = options;
912
+ if (!initialRender) {
913
+ let linesToClear = this.lastRender.endIndex - this.lastRender.startIndex;
914
+ while (linesToClear > 0) {
808
915
  this.clearLastLine();
809
- linesToClear2--;
916
+ linesToClear--;
917
+ }
918
+ if (this.options.autocomplete) {
919
+ let linesToClear2 = Math.ceil(
920
+ (0, import_wcwidth4.default)(`${SYMBOLS.Pointer} ${this.autocompleteValue}`) / this.stdout.columns
921
+ );
922
+ while (linesToClear2 > 0) {
923
+ this.clearLastLine();
924
+ linesToClear2--;
925
+ }
810
926
  }
811
927
  }
812
- }
813
- if (clearRender) {
814
- const questionLineCount = Math.ceil(
815
- (0, import_wcwidth4.default)(stripAnsi(this.questionMessage)) / this.stdout.columns
816
- );
817
- this.stdout.moveCursor(-this.stdout.columns, -(1 + questionLineCount));
818
- this.stdout.clearScreenDown();
819
- return;
820
- }
821
- if (error) {
822
- const linesToClear = Math.ceil((0, import_wcwidth4.default)(this.questionMessage) / this.stdout.columns) + 1;
823
- this.stdout.moveCursor(0, -linesToClear);
824
- this.stdout.clearScreenDown();
825
- this.#showQuestion(error);
826
- }
827
- this.#showChoices();
828
- };
829
- render({ initialRender: true });
830
- return new Promise((resolve) => {
928
+ if (clearRender) {
929
+ const questionLineCount = Math.ceil(
930
+ (0, import_wcwidth4.default)(stripAnsi(this.questionMessage)) / this.stdout.columns
931
+ );
932
+ this.stdout.moveCursor(-this.stdout.columns, -(1 + questionLineCount));
933
+ this.stdout.clearScreenDown();
934
+ return;
935
+ }
936
+ if (error) {
937
+ const linesToClear = Math.ceil((0, import_wcwidth4.default)(this.questionMessage) / this.stdout.columns) + 1;
938
+ this.stdout.moveCursor(0, -linesToClear);
939
+ this.stdout.clearScreenDown();
940
+ this.#showQuestion(error);
941
+ }
942
+ this.#showChoices();
943
+ };
944
+ render({ initialRender: true });
831
945
  this.#boundKeyPressEvent = this.#onKeypress.bind(this, resolve, render);
832
946
  this.stdin.on("keypress", this.#boundKeyPressEvent);
833
947
  this.#boundExitEvent = this.#onProcessExit.bind(this);
@@ -835,14 +949,14 @@ var MultiselectPrompt = class extends AbstractPrompt {
835
949
  });
836
950
  }
837
951
  #showQuestion(error = null) {
838
- let hint = import_kleur5.default.gray(
952
+ let hint = this.#showHint ? import_kleur5.default.gray(
839
953
  // 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
- );
954
+ `(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)`
955
+ ) : "";
842
956
  if (error) {
843
- hint += ` ${import_kleur5.default.red().bold(`[${error}]`)}`;
957
+ hint += `${hint.length > 0 ? " " : ""}${import_kleur5.default.red().bold(`[${error}]`)}`;
844
958
  }
845
- this.questionMessage = `${SYMBOLS.QuestionMark} ${import_kleur5.default.bold(this.message)} ${hint}`;
959
+ this.questionMessage = `${SYMBOLS.QuestionMark} ${import_kleur5.default.bold(this.message)}${hint.length > 0 ? ` ${hint}` : ""}`;
846
960
  this.write(`${this.questionMessage}${import_node_os5.EOL}`);
847
961
  }
848
962
  };
@@ -857,19 +971,18 @@ function required() {
857
971
 
858
972
  // index.ts
859
973
  async function question(message, options = {}) {
860
- const questionPrompt = new QuestionPrompt(message, options);
861
- return questionPrompt.question();
974
+ return new QuestionPrompt({ ...options, message }).question();
862
975
  }
863
976
  async function select(message, options) {
864
- const selectPrompt = new SelectPrompt(message, options);
977
+ const selectPrompt = new SelectPrompt({ ...options, message });
865
978
  return selectPrompt.select();
866
979
  }
867
980
  async function confirm(message, options) {
868
- const confirmPrompt = new ConfirmPrompt(message, options);
981
+ const confirmPrompt = new ConfirmPrompt({ ...options, message });
869
982
  return confirmPrompt.confirm();
870
983
  }
871
984
  async function multiselect(message, options) {
872
- const multiselectPrompt = new MultiselectPrompt(message, options);
985
+ const multiselectPrompt = new MultiselectPrompt({ ...options, message });
873
986
  return multiselectPrompt.multiselect();
874
987
  }
875
988
  // Annotate the CommonJS export names for ESM import in node: