@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/README.md +60 -23
- package/dist/index.cjs +306 -193
- package/dist/index.d.cts +25 -16
- package/dist/index.d.ts +25 -16
- package/dist/index.js +306 -193
- package/package.json +1 -1
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
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
this
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
this.
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
this.answer
|
|
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
|
-
|
|
293
|
-
|
|
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(
|
|
358
|
+
constructor(options) {
|
|
321
359
|
const {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
initial = false
|
|
360
|
+
initial = false,
|
|
361
|
+
...baseOptions
|
|
325
362
|
} = options;
|
|
326
|
-
super(
|
|
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
|
-
|
|
419
|
+
this.clearLastLine();
|
|
383
420
|
this.stdout.moveCursor(
|
|
384
421
|
-this.stdout.columns,
|
|
385
|
-
-
|
|
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
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
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
|
-
|
|
466
|
+
autocompleteValue = "";
|
|
423
467
|
options;
|
|
424
468
|
lastRender;
|
|
425
469
|
get choices() {
|
|
426
470
|
return this.options.choices;
|
|
427
471
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
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
|
|
513
|
+
this.#validators = validators;
|
|
514
|
+
for (const choice of choices) {
|
|
445
515
|
if (typeof choice === "string") {
|
|
446
|
-
|
|
516
|
+
continue;
|
|
447
517
|
}
|
|
448
|
-
const
|
|
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
|
-
|
|
456
|
-
}));
|
|
524
|
+
}
|
|
457
525
|
}
|
|
458
526
|
#getFormattedChoice(choiceIndex) {
|
|
459
|
-
const choice = this.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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 =
|
|
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
|
|
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.
|
|
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.
|
|
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(
|
|
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
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
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
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
738
|
+
constructor(options) {
|
|
626
739
|
const {
|
|
627
|
-
stdin = process.stdin,
|
|
628
|
-
stdout = process.stdout,
|
|
629
740
|
choices,
|
|
630
741
|
preSelectedChoices,
|
|
631
|
-
validators = []
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
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
|
-
|
|
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
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
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("<
|
|
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 +=
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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:
|