@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.js
CHANGED
|
@@ -7,6 +7,7 @@ import wcwidth from "@topcli/wcwidth";
|
|
|
7
7
|
import { EOL } from "os";
|
|
8
8
|
import { createInterface } from "readline";
|
|
9
9
|
import { Writable } from "stream";
|
|
10
|
+
import EventEmitter from "events";
|
|
10
11
|
|
|
11
12
|
// src/utils.ts
|
|
12
13
|
import process2 from "process";
|
|
@@ -72,25 +73,43 @@ var PromptAgent = class _PromptAgent {
|
|
|
72
73
|
}
|
|
73
74
|
};
|
|
74
75
|
|
|
76
|
+
// src/errors/abort.ts
|
|
77
|
+
var AbortError = class extends Error {
|
|
78
|
+
constructor(message) {
|
|
79
|
+
super(message);
|
|
80
|
+
this.name = "AbortError";
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
75
84
|
// src/prompts/abstract.ts
|
|
76
|
-
var AbstractPrompt = class _AbstractPrompt {
|
|
85
|
+
var AbstractPrompt = class _AbstractPrompt extends EventEmitter {
|
|
77
86
|
stdin;
|
|
78
87
|
stdout;
|
|
79
88
|
message;
|
|
89
|
+
signal;
|
|
80
90
|
history;
|
|
81
91
|
agent;
|
|
82
92
|
mute;
|
|
83
93
|
rl;
|
|
84
|
-
|
|
94
|
+
#signalHandler;
|
|
95
|
+
constructor(options) {
|
|
96
|
+
super();
|
|
85
97
|
if (this.constructor === _AbstractPrompt) {
|
|
86
98
|
throw new Error("AbstractPrompt can't be instantiated.");
|
|
87
99
|
}
|
|
100
|
+
const {
|
|
101
|
+
stdin: input = process.stdin,
|
|
102
|
+
stdout: output = process.stdout,
|
|
103
|
+
message,
|
|
104
|
+
signal
|
|
105
|
+
} = options;
|
|
88
106
|
if (typeof message !== "string") {
|
|
89
107
|
throw new TypeError(`message must be string, ${typeof message} given.`);
|
|
90
108
|
}
|
|
91
109
|
this.stdin = input;
|
|
92
110
|
this.stdout = output;
|
|
93
111
|
this.message = message;
|
|
112
|
+
this.signal = signal;
|
|
94
113
|
this.history = [];
|
|
95
114
|
this.agent = PromptAgent.agent();
|
|
96
115
|
this.mute = false;
|
|
@@ -101,7 +120,7 @@ var AbstractPrompt = class _AbstractPrompt {
|
|
|
101
120
|
input,
|
|
102
121
|
output: new Writable({
|
|
103
122
|
write: (chunk, encoding, callback) => {
|
|
104
|
-
if (!this.mute) {
|
|
123
|
+
if (!this.mute && chunk) {
|
|
105
124
|
this.stdout.write(chunk, encoding);
|
|
106
125
|
}
|
|
107
126
|
callback();
|
|
@@ -109,6 +128,16 @@ var AbstractPrompt = class _AbstractPrompt {
|
|
|
109
128
|
}),
|
|
110
129
|
terminal: true
|
|
111
130
|
});
|
|
131
|
+
if (this.signal) {
|
|
132
|
+
this.#signalHandler = () => {
|
|
133
|
+
this.rl.close();
|
|
134
|
+
for (let i = 0; i < this.history.length; i++) {
|
|
135
|
+
this.clearLastLine();
|
|
136
|
+
}
|
|
137
|
+
this.emit("error", new AbortError("Prompt aborted"));
|
|
138
|
+
};
|
|
139
|
+
this.signal.addEventListener("abort", this.#signalHandler, { once: true });
|
|
140
|
+
}
|
|
112
141
|
}
|
|
113
142
|
write(data) {
|
|
114
143
|
const formattedData = stripAnsi(data).replace(EOL, "");
|
|
@@ -128,6 +157,9 @@ var AbstractPrompt = class _AbstractPrompt {
|
|
|
128
157
|
}
|
|
129
158
|
destroy() {
|
|
130
159
|
this.rl.close();
|
|
160
|
+
if (this.signal) {
|
|
161
|
+
this.signal.removeEventListener("abort", this.#signalHandler);
|
|
162
|
+
}
|
|
131
163
|
}
|
|
132
164
|
};
|
|
133
165
|
|
|
@@ -175,15 +207,14 @@ var QuestionPrompt = class extends AbstractPrompt {
|
|
|
175
207
|
answerBuffer;
|
|
176
208
|
#validators;
|
|
177
209
|
#secure;
|
|
178
|
-
constructor(
|
|
210
|
+
constructor(options) {
|
|
179
211
|
const {
|
|
180
|
-
stdin = process.stdin,
|
|
181
|
-
stdout = process.stdout,
|
|
182
212
|
defaultValue,
|
|
183
213
|
validators = [],
|
|
184
|
-
secure = false
|
|
214
|
+
secure = false,
|
|
215
|
+
...baseOptions
|
|
185
216
|
} = options;
|
|
186
|
-
super(
|
|
217
|
+
super({ ...baseOptions });
|
|
187
218
|
if (defaultValue && typeof defaultValue !== "string") {
|
|
188
219
|
throw new TypeError("defaultValue must be a string");
|
|
189
220
|
}
|
|
@@ -196,6 +227,7 @@ var QuestionPrompt = class extends AbstractPrompt {
|
|
|
196
227
|
#question() {
|
|
197
228
|
return new Promise((resolve) => {
|
|
198
229
|
const questionQuery = this.#getQuestionQuery();
|
|
230
|
+
this.history.push(questionQuery);
|
|
199
231
|
this.rl.question(questionQuery, (answer) => {
|
|
200
232
|
this.history.push(questionQuery + answer);
|
|
201
233
|
this.mute = false;
|
|
@@ -233,24 +265,30 @@ var QuestionPrompt = class extends AbstractPrompt {
|
|
|
233
265
|
this.answerBuffer = void 0;
|
|
234
266
|
this.#writeAnswer();
|
|
235
267
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
this
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
this.
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
this.answer
|
|
268
|
+
question() {
|
|
269
|
+
return new Promise(async (resolve, reject) => {
|
|
270
|
+
this.answer = this.agent.nextAnswers.shift();
|
|
271
|
+
if (this.answer !== void 0) {
|
|
272
|
+
this.#writeAnswer();
|
|
273
|
+
this.destroy();
|
|
274
|
+
resolve(this.answer);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
this.once("error", (error) => {
|
|
278
|
+
reject(error);
|
|
279
|
+
});
|
|
280
|
+
this.answer = await this.#question();
|
|
281
|
+
if (this.answer === "" && this.defaultValue) {
|
|
282
|
+
this.answer = this.defaultValue;
|
|
283
|
+
}
|
|
250
284
|
this.#onQuestionAnswer();
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
285
|
+
while (this.answerBuffer !== void 0) {
|
|
286
|
+
this.answer = await this.answerBuffer;
|
|
287
|
+
this.#onQuestionAnswer();
|
|
288
|
+
}
|
|
289
|
+
this.destroy();
|
|
290
|
+
resolve(this.answer);
|
|
291
|
+
});
|
|
254
292
|
}
|
|
255
293
|
};
|
|
256
294
|
|
|
@@ -277,13 +315,12 @@ var ConfirmPrompt = class extends AbstractPrompt {
|
|
|
277
315
|
fastAnswer;
|
|
278
316
|
#boundKeyPressEvent;
|
|
279
317
|
#boundExitEvent;
|
|
280
|
-
constructor(
|
|
318
|
+
constructor(options) {
|
|
281
319
|
const {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
initial = false
|
|
320
|
+
initial = false,
|
|
321
|
+
...baseOptions
|
|
285
322
|
} = options;
|
|
286
|
-
super(
|
|
323
|
+
super({ ...baseOptions });
|
|
287
324
|
this.initial = initial;
|
|
288
325
|
this.selectedValue = initial;
|
|
289
326
|
}
|
|
@@ -339,33 +376,39 @@ var ConfirmPrompt = class extends AbstractPrompt {
|
|
|
339
376
|
return `${query} ${this.#getHint()}`;
|
|
340
377
|
}
|
|
341
378
|
#onQuestionAnswer() {
|
|
342
|
-
|
|
379
|
+
this.clearLastLine();
|
|
343
380
|
this.stdout.moveCursor(
|
|
344
381
|
-this.stdout.columns,
|
|
345
|
-
-
|
|
382
|
+
-Math.floor(wcwidth2(stripAnsi(this.#getQuestionQuery())) / this.stdout.columns)
|
|
346
383
|
);
|
|
347
384
|
this.stdout.clearScreenDown();
|
|
348
385
|
this.write(`${this.selectedValue ? SYMBOLS.Tick : SYMBOLS.Cross} ${kleur3.bold(this.message)}${EOL3}`);
|
|
349
386
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
387
|
+
confirm() {
|
|
388
|
+
return new Promise(async (resolve, reject) => {
|
|
389
|
+
const answer = this.agent.nextAnswers.shift();
|
|
390
|
+
if (answer !== void 0) {
|
|
391
|
+
this.selectedValue = answer;
|
|
392
|
+
this.#onQuestionAnswer();
|
|
393
|
+
this.destroy();
|
|
394
|
+
resolve(answer);
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
this.once("error", (error) => {
|
|
398
|
+
reject(error);
|
|
399
|
+
});
|
|
400
|
+
this.write(SYMBOLS.HideCursor);
|
|
401
|
+
try {
|
|
402
|
+
await this.#question();
|
|
403
|
+
this.#onQuestionAnswer();
|
|
404
|
+
resolve(this.selectedValue);
|
|
405
|
+
} finally {
|
|
406
|
+
this.write(SYMBOLS.ShowCursor);
|
|
407
|
+
this.#onProcessExit();
|
|
408
|
+
process.off("exit", this.#boundExitEvent);
|
|
409
|
+
this.destroy();
|
|
410
|
+
}
|
|
411
|
+
});
|
|
369
412
|
}
|
|
370
413
|
};
|
|
371
414
|
|
|
@@ -373,50 +416,75 @@ var ConfirmPrompt = class extends AbstractPrompt {
|
|
|
373
416
|
import { EOL as EOL4 } from "os";
|
|
374
417
|
import kleur4 from "kleur";
|
|
375
418
|
import wcwidth3 from "@topcli/wcwidth";
|
|
419
|
+
var kRequiredChoiceProperties = ["label", "value"];
|
|
376
420
|
var SelectPrompt = class extends AbstractPrompt {
|
|
377
421
|
#boundExitEvent = () => void 0;
|
|
378
422
|
#boundKeyPressEvent = () => void 0;
|
|
423
|
+
#validators;
|
|
379
424
|
activeIndex = 0;
|
|
380
|
-
selectedIndexes = [];
|
|
381
425
|
questionMessage;
|
|
382
|
-
|
|
426
|
+
autocompleteValue = "";
|
|
383
427
|
options;
|
|
384
428
|
lastRender;
|
|
385
429
|
get choices() {
|
|
386
430
|
return this.options.choices;
|
|
387
431
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
stdout = process.stdout,
|
|
392
|
-
choices
|
|
393
|
-
} = options ?? {};
|
|
394
|
-
super(message, stdin, stdout);
|
|
395
|
-
if (!options) {
|
|
396
|
-
this.destroy();
|
|
397
|
-
throw new TypeError("Missing required options");
|
|
432
|
+
get filteredChoices() {
|
|
433
|
+
if (!(this.options.autocomplete && this.autocompleteValue.length > 0)) {
|
|
434
|
+
return this.choices;
|
|
398
435
|
}
|
|
436
|
+
const isCaseSensitive = this.options.caseSensitive;
|
|
437
|
+
const autocompleteValue = isCaseSensitive ? this.autocompleteValue : this.autocompleteValue.toLowerCase();
|
|
438
|
+
return this.choices.filter((choice) => this.#filterChoice(choice, autocompleteValue, isCaseSensitive));
|
|
439
|
+
}
|
|
440
|
+
#filterChoice(choice, autocompleteValue, isCaseSensitive = false) {
|
|
441
|
+
const choiceValue = typeof choice === "string" ? isCaseSensitive ? choice : choice.toLowerCase() : isCaseSensitive ? choice.label : choice.label.toLowerCase();
|
|
442
|
+
if (autocompleteValue.includes(" ")) {
|
|
443
|
+
return this.#filterMultipleWords(choiceValue, autocompleteValue, isCaseSensitive);
|
|
444
|
+
}
|
|
445
|
+
return choiceValue.includes(autocompleteValue);
|
|
446
|
+
}
|
|
447
|
+
#filterMultipleWords(choiceValue, autocompleteValue, isCaseSensitive) {
|
|
448
|
+
return autocompleteValue.split(" ").every((word) => {
|
|
449
|
+
const wordValue = isCaseSensitive ? word : word.toLowerCase();
|
|
450
|
+
return choiceValue.includes(wordValue) || choiceValue.includes(autocompleteValue);
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
get longestChoice() {
|
|
454
|
+
return Math.max(...this.filteredChoices.map((choice) => {
|
|
455
|
+
if (typeof choice === "string") {
|
|
456
|
+
return choice.length;
|
|
457
|
+
}
|
|
458
|
+
return choice.label.length;
|
|
459
|
+
}));
|
|
460
|
+
}
|
|
461
|
+
constructor(options) {
|
|
462
|
+
const {
|
|
463
|
+
choices,
|
|
464
|
+
validators = [],
|
|
465
|
+
...baseOptions
|
|
466
|
+
} = options;
|
|
467
|
+
super({ ...baseOptions });
|
|
399
468
|
this.options = options;
|
|
400
469
|
if (!choices?.length) {
|
|
401
470
|
this.destroy();
|
|
402
471
|
throw new TypeError("Missing required param: choices");
|
|
403
472
|
}
|
|
404
|
-
this
|
|
473
|
+
this.#validators = validators;
|
|
474
|
+
for (const choice of choices) {
|
|
405
475
|
if (typeof choice === "string") {
|
|
406
|
-
|
|
476
|
+
continue;
|
|
407
477
|
}
|
|
408
|
-
const
|
|
409
|
-
for (const prop of kRequiredChoiceProperties2) {
|
|
478
|
+
for (const prop of kRequiredChoiceProperties) {
|
|
410
479
|
if (!choice[prop]) {
|
|
411
480
|
this.destroy();
|
|
412
481
|
throw new TypeError(`Missing ${prop} for choice ${JSON.stringify(choice)}`);
|
|
413
482
|
}
|
|
414
483
|
}
|
|
415
|
-
|
|
416
|
-
}));
|
|
484
|
+
}
|
|
417
485
|
}
|
|
418
486
|
#getFormattedChoice(choiceIndex) {
|
|
419
|
-
const choice = this.
|
|
487
|
+
const choice = this.filteredChoices[choiceIndex];
|
|
420
488
|
if (typeof choice === "string") {
|
|
421
489
|
return { value: choice, label: choice };
|
|
422
490
|
}
|
|
@@ -424,21 +492,24 @@ var SelectPrompt = class extends AbstractPrompt {
|
|
|
424
492
|
}
|
|
425
493
|
#getVisibleChoices() {
|
|
426
494
|
const maxVisible = this.options.maxVisible || 8;
|
|
427
|
-
let startIndex = Math.min(this.
|
|
495
|
+
let startIndex = Math.min(this.filteredChoices.length - maxVisible, this.activeIndex - Math.floor(maxVisible / 2));
|
|
428
496
|
if (startIndex < 0) {
|
|
429
497
|
startIndex = 0;
|
|
430
498
|
}
|
|
431
|
-
const endIndex = Math.min(startIndex + maxVisible, this.
|
|
499
|
+
const endIndex = Math.min(startIndex + maxVisible, this.filteredChoices.length);
|
|
432
500
|
return { startIndex, endIndex };
|
|
433
501
|
}
|
|
434
502
|
#showChoices() {
|
|
435
503
|
const { startIndex, endIndex } = this.#getVisibleChoices();
|
|
436
504
|
this.lastRender = { startIndex, endIndex };
|
|
505
|
+
if (this.options.autocomplete) {
|
|
506
|
+
this.write(`${SYMBOLS.Pointer} ${this.autocompleteValue}${EOL4}`);
|
|
507
|
+
}
|
|
437
508
|
for (let choiceIndex = startIndex; choiceIndex < endIndex; choiceIndex++) {
|
|
438
509
|
const choice = this.#getFormattedChoice(choiceIndex);
|
|
439
510
|
const isChoiceSelected = choiceIndex === this.activeIndex;
|
|
440
511
|
const showPreviousChoicesArrow = startIndex > 0 && choiceIndex === startIndex;
|
|
441
|
-
const showNextChoicesArrow = endIndex < this.
|
|
512
|
+
const showNextChoicesArrow = endIndex < this.filteredChoices.length && choiceIndex === endIndex - 1;
|
|
442
513
|
let prefixArrow = " ";
|
|
443
514
|
if (showPreviousChoicesArrow) {
|
|
444
515
|
prefixArrow = SYMBOLS.Previous;
|
|
@@ -447,16 +518,17 @@ var SelectPrompt = class extends AbstractPrompt {
|
|
|
447
518
|
}
|
|
448
519
|
const prefix = `${prefixArrow}${isChoiceSelected ? `${SYMBOLS.Pointer} ` : " "}`;
|
|
449
520
|
const formattedLabel = choice.label.padEnd(
|
|
450
|
-
this.
|
|
521
|
+
this.longestChoice < 10 ? this.longestChoice : 0
|
|
451
522
|
);
|
|
452
523
|
const formattedDescription = choice.description ? ` - ${choice.description}` : "";
|
|
453
524
|
const color = isChoiceSelected ? kleur4.white().bold : kleur4.gray;
|
|
454
|
-
const str =
|
|
525
|
+
const str = `${prefix}${color(`${formattedLabel}${formattedDescription}`)}${EOL4}`;
|
|
455
526
|
this.write(str);
|
|
456
527
|
}
|
|
457
528
|
}
|
|
458
529
|
#showAnsweredQuestion(choice) {
|
|
459
|
-
const
|
|
530
|
+
const symbolPrefix = choice === "" ? SYMBOLS.Cross : SYMBOLS.Tick;
|
|
531
|
+
const prefix = `${symbolPrefix} ${kleur4.bold(this.message)} ${SYMBOLS.Pointer}`;
|
|
460
532
|
const formattedChoice = kleur4.yellow(typeof choice === "string" ? choice : choice.label);
|
|
461
533
|
this.write(`${prefix} ${formattedChoice}${EOL4}`);
|
|
462
534
|
}
|
|
@@ -469,17 +541,25 @@ var SelectPrompt = class extends AbstractPrompt {
|
|
|
469
541
|
#onKeypress(...args) {
|
|
470
542
|
const [resolve, render, , key] = args;
|
|
471
543
|
if (key.name === "up") {
|
|
472
|
-
this.activeIndex = this.activeIndex === 0 ? this.
|
|
544
|
+
this.activeIndex = this.activeIndex === 0 ? this.filteredChoices.length - 1 : this.activeIndex - 1;
|
|
473
545
|
render();
|
|
474
546
|
} else if (key.name === "down") {
|
|
475
|
-
this.activeIndex = this.activeIndex === this.
|
|
547
|
+
this.activeIndex = this.activeIndex === this.filteredChoices.length - 1 ? 0 : this.activeIndex + 1;
|
|
476
548
|
render();
|
|
477
549
|
} else if (key.name === "return") {
|
|
550
|
+
const choice = this.filteredChoices[this.activeIndex] || "";
|
|
551
|
+
const label = typeof choice === "string" ? choice : choice.label;
|
|
552
|
+
const value = typeof choice === "string" ? choice : choice.value;
|
|
553
|
+
for (const validator of this.#validators) {
|
|
554
|
+
if (!validator.validate(value)) {
|
|
555
|
+
const error = validator.error(value);
|
|
556
|
+
render({ error });
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
478
560
|
render({ clearRender: true });
|
|
479
|
-
const currentChoice = this.choices[this.activeIndex];
|
|
480
|
-
const value = typeof currentChoice === "string" ? currentChoice : currentChoice.value;
|
|
481
561
|
if (!this.options.ignoreValues?.includes(value)) {
|
|
482
|
-
this.#showAnsweredQuestion(
|
|
562
|
+
this.#showAnsweredQuestion(label);
|
|
483
563
|
}
|
|
484
564
|
this.write(SYMBOLS.ShowCursor);
|
|
485
565
|
this.destroy();
|
|
@@ -487,50 +567,82 @@ var SelectPrompt = class extends AbstractPrompt {
|
|
|
487
567
|
process.off("exit", this.#boundExitEvent);
|
|
488
568
|
resolve(value);
|
|
489
569
|
} else {
|
|
570
|
+
if (!key.ctrl && this.options.autocomplete) {
|
|
571
|
+
this.activeIndex = 0;
|
|
572
|
+
if (key.name === "backspace" && this.autocompleteValue.length > 0) {
|
|
573
|
+
this.autocompleteValue = this.autocompleteValue.slice(0, -1);
|
|
574
|
+
} else if (key.name !== "backspace") {
|
|
575
|
+
this.autocompleteValue += key.sequence;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
490
578
|
render();
|
|
491
579
|
}
|
|
492
580
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
this.write(SYMBOLS.HideCursor);
|
|
501
|
-
this.#showQuestion();
|
|
502
|
-
const render = (options = {}) => {
|
|
503
|
-
const {
|
|
504
|
-
initialRender = false,
|
|
505
|
-
clearRender = false
|
|
506
|
-
} = options;
|
|
507
|
-
if (!initialRender) {
|
|
508
|
-
let linesToClear = this.lastRender.endIndex - this.lastRender.startIndex;
|
|
509
|
-
while (linesToClear > 0) {
|
|
510
|
-
this.clearLastLine();
|
|
511
|
-
linesToClear--;
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
if (clearRender) {
|
|
515
|
-
const questionLineCount = Math.ceil(
|
|
516
|
-
wcwidth3(stripAnsi(this.questionMessage)) / this.stdout.columns
|
|
517
|
-
);
|
|
518
|
-
this.stdout.moveCursor(-this.stdout.columns, -(1 + questionLineCount));
|
|
519
|
-
this.stdout.clearScreenDown();
|
|
581
|
+
select() {
|
|
582
|
+
return new Promise((resolve, reject) => {
|
|
583
|
+
const answer = this.agent.nextAnswers.shift();
|
|
584
|
+
if (answer !== void 0) {
|
|
585
|
+
this.#showAnsweredQuestion(answer);
|
|
586
|
+
this.destroy();
|
|
587
|
+
resolve(answer);
|
|
520
588
|
return;
|
|
521
589
|
}
|
|
522
|
-
this
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
590
|
+
this.once("error", (error) => {
|
|
591
|
+
reject(error);
|
|
592
|
+
});
|
|
593
|
+
this.write(SYMBOLS.HideCursor);
|
|
594
|
+
this.#showQuestion();
|
|
595
|
+
const render = (options = {}) => {
|
|
596
|
+
const {
|
|
597
|
+
initialRender = false,
|
|
598
|
+
clearRender = false,
|
|
599
|
+
error = null
|
|
600
|
+
} = options;
|
|
601
|
+
if (!initialRender) {
|
|
602
|
+
let linesToClear = this.lastRender.endIndex - this.lastRender.startIndex;
|
|
603
|
+
while (linesToClear > 0) {
|
|
604
|
+
this.clearLastLine();
|
|
605
|
+
linesToClear--;
|
|
606
|
+
}
|
|
607
|
+
if (this.options.autocomplete) {
|
|
608
|
+
let linesToClear2 = Math.ceil(
|
|
609
|
+
wcwidth3(`${SYMBOLS.Pointer} ${this.autocompleteValue}`) / this.stdout.columns
|
|
610
|
+
);
|
|
611
|
+
while (linesToClear2 > 0) {
|
|
612
|
+
this.clearLastLine();
|
|
613
|
+
linesToClear2--;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
if (clearRender) {
|
|
618
|
+
const questionLineCount = Math.ceil(
|
|
619
|
+
wcwidth3(stripAnsi(this.questionMessage)) / this.stdout.columns
|
|
620
|
+
);
|
|
621
|
+
this.stdout.moveCursor(-this.stdout.columns, -(1 + questionLineCount));
|
|
622
|
+
this.stdout.clearScreenDown();
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
if (error) {
|
|
626
|
+
const linesToClear = Math.ceil(wcwidth3(this.questionMessage) / this.stdout.columns) + 1;
|
|
627
|
+
this.stdout.moveCursor(0, -linesToClear);
|
|
628
|
+
this.stdout.clearScreenDown();
|
|
629
|
+
this.#showQuestion(error);
|
|
630
|
+
}
|
|
631
|
+
this.#showChoices();
|
|
632
|
+
};
|
|
633
|
+
render({ initialRender: true });
|
|
526
634
|
this.#boundKeyPressEvent = this.#onKeypress.bind(this, resolve, render);
|
|
527
635
|
this.stdin.on("keypress", this.#boundKeyPressEvent);
|
|
528
636
|
this.#boundExitEvent = this.#onProcessExit.bind(this);
|
|
529
637
|
process.once("exit", this.#boundExitEvent);
|
|
530
638
|
});
|
|
531
639
|
}
|
|
532
|
-
#showQuestion() {
|
|
533
|
-
|
|
640
|
+
#showQuestion(error = null) {
|
|
641
|
+
let hint = "";
|
|
642
|
+
if (error) {
|
|
643
|
+
hint = ` ${hint.length > 0 ? " " : ""}${kleur4.red().bold(`[${error}]`)}`;
|
|
644
|
+
}
|
|
645
|
+
this.questionMessage = `${SYMBOLS.QuestionMark} ${kleur4.bold(this.message)}${hint}`;
|
|
534
646
|
this.write(`${this.questionMessage}${EOL4}`);
|
|
535
647
|
}
|
|
536
648
|
};
|
|
@@ -539,13 +651,14 @@ var SelectPrompt = class extends AbstractPrompt {
|
|
|
539
651
|
import { EOL as EOL5 } from "os";
|
|
540
652
|
import kleur5 from "kleur";
|
|
541
653
|
import wcwidth4 from "@topcli/wcwidth";
|
|
542
|
-
var
|
|
654
|
+
var kRequiredChoiceProperties2 = ["label", "value"];
|
|
543
655
|
var MultiselectPrompt = class extends AbstractPrompt {
|
|
544
656
|
#boundExitEvent = () => void 0;
|
|
545
657
|
#boundKeyPressEvent = () => void 0;
|
|
546
658
|
#validators;
|
|
659
|
+
#showHint;
|
|
547
660
|
activeIndex = 0;
|
|
548
|
-
selectedIndexes =
|
|
661
|
+
selectedIndexes = /* @__PURE__ */ new Set();
|
|
549
662
|
questionMessage;
|
|
550
663
|
autocompleteValue = "";
|
|
551
664
|
options;
|
|
@@ -582,30 +695,27 @@ var MultiselectPrompt = class extends AbstractPrompt {
|
|
|
582
695
|
return choice.label.length;
|
|
583
696
|
}));
|
|
584
697
|
}
|
|
585
|
-
constructor(
|
|
698
|
+
constructor(options) {
|
|
586
699
|
const {
|
|
587
|
-
stdin = process.stdin,
|
|
588
|
-
stdout = process.stdout,
|
|
589
700
|
choices,
|
|
590
701
|
preSelectedChoices,
|
|
591
|
-
validators = []
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
throw new TypeError("Missing required options");
|
|
597
|
-
}
|
|
702
|
+
validators = [],
|
|
703
|
+
showHint = true,
|
|
704
|
+
...baseOptions
|
|
705
|
+
} = options;
|
|
706
|
+
super({ ...baseOptions });
|
|
598
707
|
this.options = options;
|
|
599
708
|
if (!choices?.length) {
|
|
600
709
|
this.destroy();
|
|
601
710
|
throw new TypeError("Missing required param: choices");
|
|
602
711
|
}
|
|
603
712
|
this.#validators = validators;
|
|
713
|
+
this.#showHint = showHint;
|
|
604
714
|
for (const choice of choices) {
|
|
605
715
|
if (typeof choice === "string") {
|
|
606
716
|
continue;
|
|
607
717
|
}
|
|
608
|
-
for (const prop of
|
|
718
|
+
for (const prop of kRequiredChoiceProperties2) {
|
|
609
719
|
if (!choice[prop]) {
|
|
610
720
|
this.destroy();
|
|
611
721
|
throw new TypeError(`Missing ${prop} for choice ${JSON.stringify(choice)}`);
|
|
@@ -626,7 +736,7 @@ var MultiselectPrompt = class extends AbstractPrompt {
|
|
|
626
736
|
this.destroy();
|
|
627
737
|
throw new Error(`Invalid pre-selected choice: ${typeof choice === "string" ? choice : choice.value}`);
|
|
628
738
|
}
|
|
629
|
-
this.selectedIndexes.
|
|
739
|
+
this.selectedIndexes.add(choiceIndex);
|
|
630
740
|
}
|
|
631
741
|
}
|
|
632
742
|
#getFormattedChoice(choiceIndex) {
|
|
@@ -654,7 +764,7 @@ var MultiselectPrompt = class extends AbstractPrompt {
|
|
|
654
764
|
for (let choiceIndex = startIndex; choiceIndex < endIndex; choiceIndex++) {
|
|
655
765
|
const choice = this.#getFormattedChoice(choiceIndex);
|
|
656
766
|
const isChoiceActive = choiceIndex === this.activeIndex;
|
|
657
|
-
const isChoiceSelected = this.selectedIndexes.
|
|
767
|
+
const isChoiceSelected = this.selectedIndexes.has(choiceIndex);
|
|
658
768
|
const showPreviousChoicesArrow = startIndex > 0 && choiceIndex === startIndex;
|
|
659
769
|
const showNextChoicesArrow = endIndex < this.filteredChoices.length && choiceIndex === endIndex - 1;
|
|
660
770
|
let prefixArrow = " ";
|
|
@@ -674,7 +784,7 @@ var MultiselectPrompt = class extends AbstractPrompt {
|
|
|
674
784
|
}
|
|
675
785
|
}
|
|
676
786
|
#showAnsweredQuestion(choices, isAgentAnswer = false) {
|
|
677
|
-
const prefixSymbol = this.selectedIndexes.
|
|
787
|
+
const prefixSymbol = this.selectedIndexes.size === 0 && !isAgentAnswer ? SYMBOLS.Cross : SYMBOLS.Tick;
|
|
678
788
|
const prefix = `${prefixSymbol} ${kleur5.bold(this.message)} ${SYMBOLS.Pointer}`;
|
|
679
789
|
const formattedChoice = kleur5.yellow(choices);
|
|
680
790
|
this.write(`${prefix}${choices ? ` ${formattedChoice}` : ""}${EOL5}`);
|
|
@@ -694,20 +804,20 @@ var MultiselectPrompt = class extends AbstractPrompt {
|
|
|
694
804
|
this.activeIndex = this.activeIndex === this.filteredChoices.length - 1 ? 0 : this.activeIndex + 1;
|
|
695
805
|
render();
|
|
696
806
|
} else if (key.ctrl && key.name === "a") {
|
|
697
|
-
this.selectedIndexes = this.selectedIndexes.
|
|
807
|
+
this.selectedIndexes = this.selectedIndexes.size === this.filteredChoices.length ? /* @__PURE__ */ new Set() : new Set(this.filteredChoices.map((_, index) => index));
|
|
698
808
|
render();
|
|
699
809
|
} else if (key.name === "right") {
|
|
700
|
-
this.selectedIndexes.
|
|
810
|
+
this.selectedIndexes.add(this.activeIndex);
|
|
701
811
|
render();
|
|
702
812
|
} else if (key.name === "left") {
|
|
703
|
-
this.selectedIndexes = this.selectedIndexes.filter((index) => index !== this.activeIndex);
|
|
813
|
+
this.selectedIndexes = new Set([...this.selectedIndexes].filter((index) => index !== this.activeIndex));
|
|
704
814
|
render();
|
|
705
815
|
} else if (key.name === "return") {
|
|
706
|
-
const labels = this.selectedIndexes.map((index) => {
|
|
816
|
+
const labels = [...this.selectedIndexes].map((index) => {
|
|
707
817
|
const choice = this.filteredChoices[index];
|
|
708
818
|
return typeof choice === "string" ? choice : choice.label;
|
|
709
819
|
});
|
|
710
|
-
const values = this.selectedIndexes.map((index) => {
|
|
820
|
+
const values = [...this.selectedIndexes].map((index) => {
|
|
711
821
|
const choice = this.filteredChoices[index];
|
|
712
822
|
return typeof choice === "string" ? choice : choice.value;
|
|
713
823
|
});
|
|
@@ -727,7 +837,7 @@ var MultiselectPrompt = class extends AbstractPrompt {
|
|
|
727
837
|
resolve(values);
|
|
728
838
|
} else {
|
|
729
839
|
if (!key.ctrl && this.options.autocomplete) {
|
|
730
|
-
this.selectedIndexes
|
|
840
|
+
this.selectedIndexes.clear();
|
|
731
841
|
this.activeIndex = 0;
|
|
732
842
|
if (key.name === "backspace" && this.autocompleteValue.length > 0) {
|
|
733
843
|
this.autocompleteValue = this.autocompleteValue.slice(0, -1);
|
|
@@ -738,56 +848,60 @@ var MultiselectPrompt = class extends AbstractPrompt {
|
|
|
738
848
|
render();
|
|
739
849
|
}
|
|
740
850
|
}
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
wcwidth4(`${SYMBOLS.Pointer} ${this.autocompleteValue}`) / this.stdout.columns
|
|
766
|
-
);
|
|
767
|
-
while (linesToClear2 > 0) {
|
|
851
|
+
multiselect() {
|
|
852
|
+
return new Promise((resolve, reject) => {
|
|
853
|
+
const answer = this.agent.nextAnswers.shift();
|
|
854
|
+
if (answer !== void 0) {
|
|
855
|
+
const formatedAnser = Array.isArray(answer) ? answer.join(", ") : answer;
|
|
856
|
+
this.#showAnsweredQuestion(formatedAnser, true);
|
|
857
|
+
this.destroy();
|
|
858
|
+
resolve(Array.isArray(answer) ? answer : [answer]);
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
this.once("error", (error) => {
|
|
862
|
+
reject(error);
|
|
863
|
+
});
|
|
864
|
+
this.write(SYMBOLS.HideCursor);
|
|
865
|
+
this.#showQuestion();
|
|
866
|
+
const render = (options = {}) => {
|
|
867
|
+
const {
|
|
868
|
+
initialRender = false,
|
|
869
|
+
clearRender = false,
|
|
870
|
+
error = null
|
|
871
|
+
} = options;
|
|
872
|
+
if (!initialRender) {
|
|
873
|
+
let linesToClear = this.lastRender.endIndex - this.lastRender.startIndex;
|
|
874
|
+
while (linesToClear > 0) {
|
|
768
875
|
this.clearLastLine();
|
|
769
|
-
|
|
876
|
+
linesToClear--;
|
|
877
|
+
}
|
|
878
|
+
if (this.options.autocomplete) {
|
|
879
|
+
let linesToClear2 = Math.ceil(
|
|
880
|
+
wcwidth4(`${SYMBOLS.Pointer} ${this.autocompleteValue}`) / this.stdout.columns
|
|
881
|
+
);
|
|
882
|
+
while (linesToClear2 > 0) {
|
|
883
|
+
this.clearLastLine();
|
|
884
|
+
linesToClear2--;
|
|
885
|
+
}
|
|
770
886
|
}
|
|
771
887
|
}
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
render({ initialRender: true });
|
|
790
|
-
return new Promise((resolve) => {
|
|
888
|
+
if (clearRender) {
|
|
889
|
+
const questionLineCount = Math.ceil(
|
|
890
|
+
wcwidth4(stripAnsi(this.questionMessage)) / this.stdout.columns
|
|
891
|
+
);
|
|
892
|
+
this.stdout.moveCursor(-this.stdout.columns, -(1 + questionLineCount));
|
|
893
|
+
this.stdout.clearScreenDown();
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
if (error) {
|
|
897
|
+
const linesToClear = Math.ceil(wcwidth4(this.questionMessage) / this.stdout.columns) + 1;
|
|
898
|
+
this.stdout.moveCursor(0, -linesToClear);
|
|
899
|
+
this.stdout.clearScreenDown();
|
|
900
|
+
this.#showQuestion(error);
|
|
901
|
+
}
|
|
902
|
+
this.#showChoices();
|
|
903
|
+
};
|
|
904
|
+
render({ initialRender: true });
|
|
791
905
|
this.#boundKeyPressEvent = this.#onKeypress.bind(this, resolve, render);
|
|
792
906
|
this.stdin.on("keypress", this.#boundKeyPressEvent);
|
|
793
907
|
this.#boundExitEvent = this.#onProcessExit.bind(this);
|
|
@@ -795,14 +909,14 @@ var MultiselectPrompt = class extends AbstractPrompt {
|
|
|
795
909
|
});
|
|
796
910
|
}
|
|
797
911
|
#showQuestion(error = null) {
|
|
798
|
-
let hint = kleur5.gray(
|
|
912
|
+
let hint = this.#showHint ? kleur5.gray(
|
|
799
913
|
// eslint-disable-next-line max-len
|
|
800
|
-
`(Press ${kleur5.bold("<Ctrl+A>")} to toggle all, ${kleur5.bold("<
|
|
801
|
-
);
|
|
914
|
+
`(Press ${kleur5.bold("<Ctrl+A>")} to toggle all, ${kleur5.bold("<Left/Right>")} to toggle, ${kleur5.bold("<Return>")} to submit)`
|
|
915
|
+
) : "";
|
|
802
916
|
if (error) {
|
|
803
|
-
hint +=
|
|
917
|
+
hint += `${hint.length > 0 ? " " : ""}${kleur5.red().bold(`[${error}]`)}`;
|
|
804
918
|
}
|
|
805
|
-
this.questionMessage = `${SYMBOLS.QuestionMark} ${kleur5.bold(this.message)} ${hint}`;
|
|
919
|
+
this.questionMessage = `${SYMBOLS.QuestionMark} ${kleur5.bold(this.message)}${hint.length > 0 ? ` ${hint}` : ""}`;
|
|
806
920
|
this.write(`${this.questionMessage}${EOL5}`);
|
|
807
921
|
}
|
|
808
922
|
};
|
|
@@ -817,19 +931,18 @@ function required() {
|
|
|
817
931
|
|
|
818
932
|
// index.ts
|
|
819
933
|
async function question(message, options = {}) {
|
|
820
|
-
|
|
821
|
-
return questionPrompt.question();
|
|
934
|
+
return new QuestionPrompt({ ...options, message }).question();
|
|
822
935
|
}
|
|
823
936
|
async function select(message, options) {
|
|
824
|
-
const selectPrompt = new SelectPrompt(
|
|
937
|
+
const selectPrompt = new SelectPrompt({ ...options, message });
|
|
825
938
|
return selectPrompt.select();
|
|
826
939
|
}
|
|
827
940
|
async function confirm(message, options) {
|
|
828
|
-
const confirmPrompt = new ConfirmPrompt(
|
|
941
|
+
const confirmPrompt = new ConfirmPrompt({ ...options, message });
|
|
829
942
|
return confirmPrompt.confirm();
|
|
830
943
|
}
|
|
831
944
|
async function multiselect(message, options) {
|
|
832
|
-
const multiselectPrompt = new MultiselectPrompt(
|
|
945
|
+
const multiselectPrompt = new MultiselectPrompt({ ...options, message });
|
|
833
946
|
return multiselectPrompt.multiselect();
|
|
834
947
|
}
|
|
835
948
|
export {
|