@topcli/prompts 2.4.1 → 3.1.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/LICENSE +13 -13
- package/README.md +387 -275
- package/dist/index.cjs +1166 -1042
- package/dist/index.d.cts +105 -68
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +139 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +1175 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +68 -55
- package/dist/index.d.ts +0 -99
- package/dist/index.js +0 -1039
package/dist/index.js
DELETED
|
@@ -1,1039 +0,0 @@
|
|
|
1
|
-
// src/validators.ts
|
|
2
|
-
function required() {
|
|
3
|
-
return {
|
|
4
|
-
validate: (input) => {
|
|
5
|
-
const isValid2 = Array.isArray(input) ? input.length > 0 : Boolean(input);
|
|
6
|
-
return isValid2 ? null : { isValid: isValid2, error: "required" };
|
|
7
|
-
}
|
|
8
|
-
};
|
|
9
|
-
}
|
|
10
|
-
function isValid(result) {
|
|
11
|
-
if (typeof result === "object") {
|
|
12
|
-
return result?.isValid !== false;
|
|
13
|
-
}
|
|
14
|
-
if (typeof result === "string") {
|
|
15
|
-
return result.length > 0;
|
|
16
|
-
}
|
|
17
|
-
return true;
|
|
18
|
-
}
|
|
19
|
-
function resultError(result) {
|
|
20
|
-
if (typeof result === "object") {
|
|
21
|
-
return result.error;
|
|
22
|
-
}
|
|
23
|
-
return result;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// src/prompt-agent.ts
|
|
27
|
-
var kPrivateInstancier = Symbol("instancier");
|
|
28
|
-
var PromptAgent = class _PromptAgent {
|
|
29
|
-
/**
|
|
30
|
-
* The prompts answers queue.
|
|
31
|
-
* When not empty, any prompt will be answered by the first answer in this list.
|
|
32
|
-
*/
|
|
33
|
-
nextAnswers = [];
|
|
34
|
-
/**
|
|
35
|
-
* The shared PromptAgent.
|
|
36
|
-
*/
|
|
37
|
-
static #this;
|
|
38
|
-
static agent() {
|
|
39
|
-
return this.#this ??= new _PromptAgent(kPrivateInstancier);
|
|
40
|
-
}
|
|
41
|
-
constructor(instancier) {
|
|
42
|
-
if (instancier !== kPrivateInstancier) {
|
|
43
|
-
throw new Error("Cannot instanciate PromptAgent, use PromptAgent.agent() instead");
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Programmatically set the next answer for any prompt (`question()`, `confirm()`, `select()`)
|
|
48
|
-
*
|
|
49
|
-
* This is useful for testing.
|
|
50
|
-
*
|
|
51
|
-
* @example
|
|
52
|
-
* ```js
|
|
53
|
-
* const promptAgent = PromptAgent.agent();
|
|
54
|
-
* promptAgent.nextAnswer("toto");
|
|
55
|
-
*
|
|
56
|
-
* const input = await question("what is your name?");
|
|
57
|
-
* assert.equal(input, "toto");
|
|
58
|
-
* ```
|
|
59
|
-
*/
|
|
60
|
-
nextAnswer(value) {
|
|
61
|
-
if (Array.isArray(value)) {
|
|
62
|
-
this.nextAnswers.push(...value);
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
this.nextAnswers.push(value);
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
// src/prompts/abstract.ts
|
|
70
|
-
import { EOL } from "node:os";
|
|
71
|
-
import readline from "node:readline";
|
|
72
|
-
import { Writable } from "node:stream";
|
|
73
|
-
import EventEmitter from "node:events";
|
|
74
|
-
import { stripVTControlCharacters } from "node:util";
|
|
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
|
-
|
|
84
|
-
// src/prompts/abstract.ts
|
|
85
|
-
function kNoopTransformer(input) {
|
|
86
|
-
return input;
|
|
87
|
-
}
|
|
88
|
-
var AbstractPrompt = class _AbstractPrompt extends EventEmitter {
|
|
89
|
-
stdin;
|
|
90
|
-
stdout;
|
|
91
|
-
message;
|
|
92
|
-
signal;
|
|
93
|
-
skip;
|
|
94
|
-
history;
|
|
95
|
-
agent;
|
|
96
|
-
transformer = kNoopTransformer;
|
|
97
|
-
rl;
|
|
98
|
-
#signalHandler;
|
|
99
|
-
constructor(options) {
|
|
100
|
-
super();
|
|
101
|
-
if (this.constructor === _AbstractPrompt) {
|
|
102
|
-
throw new Error("AbstractPrompt can't be instantiated.");
|
|
103
|
-
}
|
|
104
|
-
const {
|
|
105
|
-
stdin: input = process.stdin,
|
|
106
|
-
stdout: output = process.stdout,
|
|
107
|
-
message,
|
|
108
|
-
signal,
|
|
109
|
-
skip = false
|
|
110
|
-
} = options;
|
|
111
|
-
if (typeof message !== "string") {
|
|
112
|
-
throw new TypeError(`message must be string, ${typeof message} given.`);
|
|
113
|
-
}
|
|
114
|
-
if (!output.isTTY) {
|
|
115
|
-
Object.assign(output, {
|
|
116
|
-
moveCursor: () => void 0,
|
|
117
|
-
clearScreenDown: () => void 0
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
this.stdin = input;
|
|
121
|
-
this.stdout = output;
|
|
122
|
-
this.message = message;
|
|
123
|
-
this.signal = signal;
|
|
124
|
-
this.skip = skip;
|
|
125
|
-
this.history = [];
|
|
126
|
-
this.agent = PromptAgent.agent();
|
|
127
|
-
if (this.stdout.isTTY) {
|
|
128
|
-
this.stdin.setRawMode(true);
|
|
129
|
-
}
|
|
130
|
-
this.rl = readline.createInterface({
|
|
131
|
-
input,
|
|
132
|
-
output: new Writable({
|
|
133
|
-
write: (chunk, encoding, callback) => {
|
|
134
|
-
if (chunk) {
|
|
135
|
-
const transformed = this.transformer(
|
|
136
|
-
Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
|
|
137
|
-
);
|
|
138
|
-
if (transformed !== null) {
|
|
139
|
-
this.stdout.write(transformed, encoding);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
callback();
|
|
143
|
-
}
|
|
144
|
-
}),
|
|
145
|
-
terminal: true
|
|
146
|
-
});
|
|
147
|
-
if (this.signal) {
|
|
148
|
-
this.#signalHandler = () => {
|
|
149
|
-
this.rl.close();
|
|
150
|
-
for (let i = 0; i < this.history.length; i++) {
|
|
151
|
-
this.clearLastLine();
|
|
152
|
-
}
|
|
153
|
-
this.emit("error", new AbortError("Prompt aborted"));
|
|
154
|
-
};
|
|
155
|
-
if (this.signal.aborted) {
|
|
156
|
-
this.#signalHandler();
|
|
157
|
-
}
|
|
158
|
-
this.signal.addEventListener("abort", this.#signalHandler, { once: true });
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
reset() {
|
|
162
|
-
this.transformer = kNoopTransformer;
|
|
163
|
-
}
|
|
164
|
-
write(data) {
|
|
165
|
-
const formattedData = stripVTControlCharacters(data).replace(EOL, "");
|
|
166
|
-
if (formattedData) {
|
|
167
|
-
this.history.push(formattedData);
|
|
168
|
-
}
|
|
169
|
-
return this.stdout.write(data);
|
|
170
|
-
}
|
|
171
|
-
clearLastLine() {
|
|
172
|
-
const lastLine = this.history.pop();
|
|
173
|
-
if (!lastLine) {
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
const lastLineRows = Math.ceil(stripVTControlCharacters(lastLine).length / this.stdout.columns);
|
|
177
|
-
this.stdout.moveCursor(-this.stdout.columns, -lastLineRows);
|
|
178
|
-
this.stdout.clearScreenDown();
|
|
179
|
-
}
|
|
180
|
-
destroy() {
|
|
181
|
-
this.rl.close();
|
|
182
|
-
if (this.signal) {
|
|
183
|
-
this.signal.removeEventListener("abort", this.#signalHandler);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
// src/prompts/question.ts
|
|
189
|
-
import { EOL as EOL2 } from "node:os";
|
|
190
|
-
import { styleText as styleText2 } from "node:util";
|
|
191
|
-
|
|
192
|
-
// src/utils.ts
|
|
193
|
-
import process2 from "node:process";
|
|
194
|
-
import { stripVTControlCharacters as stripVTControlCharacters2 } from "node:util";
|
|
195
|
-
var kLenSegmenter = new Intl.Segmenter();
|
|
196
|
-
function isUnicodeSupported() {
|
|
197
|
-
if (process2.platform !== "win32") {
|
|
198
|
-
return process2.env.TERM !== "linux";
|
|
199
|
-
}
|
|
200
|
-
return Boolean(process2.env.WT_SESSION) || Boolean(process2.env.TERMINUS_SUBLIME) || process2.env.ConEmuTask === "{cmd::Cmder}" || process2.env.TERM_PROGRAM === "Terminus-Sublime" || process2.env.TERM_PROGRAM === "vscode" || process2.env.TERM === "xterm-256color" || process2.env.TERM === "alacritty" || process2.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
|
|
201
|
-
}
|
|
202
|
-
function stringLength(string) {
|
|
203
|
-
if (string === "") {
|
|
204
|
-
return 0;
|
|
205
|
-
}
|
|
206
|
-
let length = 0;
|
|
207
|
-
for (const _ of kLenSegmenter.segment(
|
|
208
|
-
stripVTControlCharacters2(string)
|
|
209
|
-
)) {
|
|
210
|
-
length++;
|
|
211
|
-
}
|
|
212
|
-
return length;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// src/constants.ts
|
|
216
|
-
import { styleText } from "node:util";
|
|
217
|
-
var kMainSymbols = {
|
|
218
|
-
tick: "\u2714",
|
|
219
|
-
cross: "\u2716",
|
|
220
|
-
pointer: "\u203A",
|
|
221
|
-
previous: "\u2B61",
|
|
222
|
-
next: "\u2B63",
|
|
223
|
-
active: "\u25CF",
|
|
224
|
-
inactive: "\u25CB"
|
|
225
|
-
};
|
|
226
|
-
var kFallbackSymbols = {
|
|
227
|
-
tick: "\u221A",
|
|
228
|
-
cross: "\xD7",
|
|
229
|
-
pointer: ">",
|
|
230
|
-
previous: "\u2191",
|
|
231
|
-
next: "\u2193",
|
|
232
|
-
active: "(+)",
|
|
233
|
-
inactive: "(-)"
|
|
234
|
-
};
|
|
235
|
-
var kSymbols = isUnicodeSupported() || process.env.CI ? kMainSymbols : kFallbackSymbols;
|
|
236
|
-
var kPointer = styleText("gray", kSymbols.pointer);
|
|
237
|
-
var SYMBOLS = {
|
|
238
|
-
QuestionMark: styleText(["blue", "bold"], "?"),
|
|
239
|
-
Tick: styleText(["green", "bold"], kSymbols.tick),
|
|
240
|
-
Cross: styleText(["red", "bold"], kSymbols.cross),
|
|
241
|
-
Pointer: kPointer,
|
|
242
|
-
Previous: kSymbols.previous,
|
|
243
|
-
Next: kSymbols.next,
|
|
244
|
-
ShowCursor: "\x1B[?25h",
|
|
245
|
-
HideCursor: "\x1B[?25l",
|
|
246
|
-
Active: styleText("cyan", kSymbols.active),
|
|
247
|
-
Inactive: styleText("gray", kSymbols.inactive)
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
// src/prompts/question.ts
|
|
251
|
-
var QuestionPrompt = class extends AbstractPrompt {
|
|
252
|
-
defaultValue;
|
|
253
|
-
tip;
|
|
254
|
-
questionSuffixError;
|
|
255
|
-
answer;
|
|
256
|
-
answerBuffer;
|
|
257
|
-
#validators;
|
|
258
|
-
#secure;
|
|
259
|
-
#securePlaceholder = null;
|
|
260
|
-
constructor(options) {
|
|
261
|
-
const {
|
|
262
|
-
defaultValue,
|
|
263
|
-
validators = [],
|
|
264
|
-
secure = false,
|
|
265
|
-
...baseOptions
|
|
266
|
-
} = options;
|
|
267
|
-
super({ ...baseOptions });
|
|
268
|
-
if (defaultValue && typeof defaultValue !== "string") {
|
|
269
|
-
throw new TypeError("defaultValue must be a string");
|
|
270
|
-
}
|
|
271
|
-
this.defaultValue = defaultValue;
|
|
272
|
-
this.tip = this.defaultValue ? ` (${this.defaultValue})` : "";
|
|
273
|
-
this.#validators = validators;
|
|
274
|
-
if (typeof secure === "object") {
|
|
275
|
-
this.#secure = true;
|
|
276
|
-
this.#securePlaceholder = secure.placeholder;
|
|
277
|
-
} else {
|
|
278
|
-
this.#secure = Boolean(secure);
|
|
279
|
-
}
|
|
280
|
-
this.questionSuffixError = "";
|
|
281
|
-
}
|
|
282
|
-
#question() {
|
|
283
|
-
return new Promise((resolve) => {
|
|
284
|
-
const questionQuery = this.#getQuestionQuery();
|
|
285
|
-
this.history.push(questionQuery);
|
|
286
|
-
this.rl.question(questionQuery, (answer) => {
|
|
287
|
-
this.history.push(questionQuery + answer);
|
|
288
|
-
this.reset();
|
|
289
|
-
resolve(answer);
|
|
290
|
-
});
|
|
291
|
-
if (this.#securePlaceholder !== null) {
|
|
292
|
-
this.transformer = (input) => Buffer.from(this.#securePlaceholder.repeat(input.length), "utf-8");
|
|
293
|
-
}
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
#getQuestionQuery() {
|
|
297
|
-
return `${styleText2("bold", `${SYMBOLS.QuestionMark} ${this.message}${this.tip}`)} ${this.questionSuffixError}`;
|
|
298
|
-
}
|
|
299
|
-
#setQuestionSuffixError(error) {
|
|
300
|
-
const suffix = styleText2("red", `[${error}] `);
|
|
301
|
-
this.questionSuffixError = suffix;
|
|
302
|
-
}
|
|
303
|
-
#writeAnswer() {
|
|
304
|
-
const prefix = this.answer ? SYMBOLS.Tick : SYMBOLS.Cross;
|
|
305
|
-
const answer = this.answer ?? "";
|
|
306
|
-
const maskedAnswer = this.#securePlaceholder ? this.#securePlaceholder.repeat(answer.length) : answer;
|
|
307
|
-
const stylizedAnswer = styleText2(
|
|
308
|
-
"yellow",
|
|
309
|
-
this.#secure && this.#securePlaceholder === null ? "CONFIDENTIAL" : maskedAnswer
|
|
310
|
-
);
|
|
311
|
-
this.write(`${prefix} ${styleText2("bold", this.message)} ${SYMBOLS.Pointer} ${stylizedAnswer}${EOL2}`);
|
|
312
|
-
}
|
|
313
|
-
#onQuestionAnswer() {
|
|
314
|
-
const questionLineCount = Math.ceil(
|
|
315
|
-
stringLength(this.#getQuestionQuery() + this.answer) / this.stdout.columns
|
|
316
|
-
);
|
|
317
|
-
this.stdout.moveCursor(-this.stdout.columns, -questionLineCount);
|
|
318
|
-
this.stdout.clearScreenDown();
|
|
319
|
-
for (const validator of this.#validators) {
|
|
320
|
-
const validationResult = validator.validate(this.answer);
|
|
321
|
-
if (isValid(validationResult) === false) {
|
|
322
|
-
this.#setQuestionSuffixError(resultError(validationResult));
|
|
323
|
-
this.answerBuffer = this.#question();
|
|
324
|
-
return;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
this.answerBuffer = void 0;
|
|
328
|
-
this.#writeAnswer();
|
|
329
|
-
}
|
|
330
|
-
async question() {
|
|
331
|
-
if (this.skip) {
|
|
332
|
-
this.destroy();
|
|
333
|
-
return this.defaultValue ?? "";
|
|
334
|
-
}
|
|
335
|
-
return new Promise(async (resolve, reject) => {
|
|
336
|
-
this.answer = this.agent.nextAnswers.shift();
|
|
337
|
-
if (this.answer !== void 0) {
|
|
338
|
-
this.#writeAnswer();
|
|
339
|
-
this.destroy();
|
|
340
|
-
resolve(this.answer);
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
this.once("error", (error) => {
|
|
344
|
-
reject(error);
|
|
345
|
-
});
|
|
346
|
-
this.answer = await this.#question();
|
|
347
|
-
if (this.answer === "" && this.defaultValue) {
|
|
348
|
-
this.answer = this.defaultValue;
|
|
349
|
-
}
|
|
350
|
-
this.#onQuestionAnswer();
|
|
351
|
-
while (this.answerBuffer !== void 0) {
|
|
352
|
-
this.answer = await this.answerBuffer;
|
|
353
|
-
this.#onQuestionAnswer();
|
|
354
|
-
}
|
|
355
|
-
this.destroy();
|
|
356
|
-
resolve(this.answer);
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
};
|
|
360
|
-
|
|
361
|
-
// src/prompts/confirm.ts
|
|
362
|
-
import { EOL as EOL3 } from "node:os";
|
|
363
|
-
import { styleText as styleText3 } from "node:util";
|
|
364
|
-
var kToggleKeys = /* @__PURE__ */ new Set([
|
|
365
|
-
"left",
|
|
366
|
-
"right",
|
|
367
|
-
"tab",
|
|
368
|
-
"q",
|
|
369
|
-
"a",
|
|
370
|
-
"d",
|
|
371
|
-
"h",
|
|
372
|
-
"j",
|
|
373
|
-
"k",
|
|
374
|
-
"l",
|
|
375
|
-
"space"
|
|
376
|
-
]);
|
|
377
|
-
var ConfirmPrompt = class extends AbstractPrompt {
|
|
378
|
-
initial;
|
|
379
|
-
selectedValue;
|
|
380
|
-
fastAnswer;
|
|
381
|
-
#boundKeyPressEvent;
|
|
382
|
-
#boundExitEvent;
|
|
383
|
-
constructor(options) {
|
|
384
|
-
const {
|
|
385
|
-
initial = false,
|
|
386
|
-
...baseOptions
|
|
387
|
-
} = options;
|
|
388
|
-
super({ ...baseOptions });
|
|
389
|
-
this.initial = initial;
|
|
390
|
-
this.selectedValue = initial;
|
|
391
|
-
}
|
|
392
|
-
#getHint() {
|
|
393
|
-
const Yes = styleText3(["cyan", "bold", "underline"], "Yes");
|
|
394
|
-
const No = styleText3(["cyan", "bold", "underline"], "No");
|
|
395
|
-
return this.selectedValue ? `${Yes}/No` : `Yes/${No}`;
|
|
396
|
-
}
|
|
397
|
-
#render() {
|
|
398
|
-
this.write(this.#getQuestionQuery());
|
|
399
|
-
}
|
|
400
|
-
#question() {
|
|
401
|
-
return new Promise((resolve) => {
|
|
402
|
-
const questionQuery = this.#getQuestionQuery();
|
|
403
|
-
this.write(questionQuery);
|
|
404
|
-
this.#boundKeyPressEvent = this.#onKeypress.bind(this, resolve);
|
|
405
|
-
this.stdin.on("keypress", this.#boundKeyPressEvent);
|
|
406
|
-
this.#boundExitEvent = this.#onProcessExit.bind(this);
|
|
407
|
-
process.once("exit", this.#boundExitEvent);
|
|
408
|
-
});
|
|
409
|
-
}
|
|
410
|
-
#onKeypress(resolve, _value, key) {
|
|
411
|
-
this.stdout.moveCursor(
|
|
412
|
-
-this.stdout.columns,
|
|
413
|
-
-Math.floor(stringLength(this.#getQuestionQuery()) / this.stdout.columns)
|
|
414
|
-
);
|
|
415
|
-
this.stdout.clearScreenDown();
|
|
416
|
-
if (key.name === "return") {
|
|
417
|
-
resolve(this.selectedValue);
|
|
418
|
-
return;
|
|
419
|
-
}
|
|
420
|
-
if (kToggleKeys.has(key.name ?? "")) {
|
|
421
|
-
this.selectedValue = !this.selectedValue;
|
|
422
|
-
}
|
|
423
|
-
if (key.name === "y") {
|
|
424
|
-
this.selectedValue = true;
|
|
425
|
-
resolve(true);
|
|
426
|
-
this.fastAnswer = true;
|
|
427
|
-
} else if (key.name === "n") {
|
|
428
|
-
this.selectedValue = false;
|
|
429
|
-
resolve(false);
|
|
430
|
-
this.fastAnswer = true;
|
|
431
|
-
}
|
|
432
|
-
if (!this.fastAnswer) {
|
|
433
|
-
this.#render();
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
#onProcessExit() {
|
|
437
|
-
this.stdin.off("keypress", this.#boundKeyPressEvent);
|
|
438
|
-
}
|
|
439
|
-
#getQuestionQuery() {
|
|
440
|
-
const query = styleText3("bold", `${SYMBOLS.QuestionMark} ${this.message}`);
|
|
441
|
-
return `${query} ${this.#getHint()}`;
|
|
442
|
-
}
|
|
443
|
-
#onQuestionAnswer() {
|
|
444
|
-
this.clearLastLine();
|
|
445
|
-
this.stdout.moveCursor(
|
|
446
|
-
-this.stdout.columns,
|
|
447
|
-
-Math.floor(stringLength(this.#getQuestionQuery()) / this.stdout.columns)
|
|
448
|
-
);
|
|
449
|
-
this.stdout.clearScreenDown();
|
|
450
|
-
this.write(`${this.selectedValue ? SYMBOLS.Tick : SYMBOLS.Cross} ${styleText3("bold", this.message)}${EOL3}`);
|
|
451
|
-
}
|
|
452
|
-
async confirm() {
|
|
453
|
-
if (this.skip) {
|
|
454
|
-
this.destroy();
|
|
455
|
-
return this.initial;
|
|
456
|
-
}
|
|
457
|
-
return new Promise(async (resolve, reject) => {
|
|
458
|
-
const answer = this.agent.nextAnswers.shift();
|
|
459
|
-
if (answer !== void 0) {
|
|
460
|
-
this.selectedValue = answer;
|
|
461
|
-
this.#onQuestionAnswer();
|
|
462
|
-
this.destroy();
|
|
463
|
-
resolve(answer);
|
|
464
|
-
return;
|
|
465
|
-
}
|
|
466
|
-
this.once("error", (error) => {
|
|
467
|
-
reject(error);
|
|
468
|
-
});
|
|
469
|
-
this.write(SYMBOLS.HideCursor);
|
|
470
|
-
try {
|
|
471
|
-
await this.#question();
|
|
472
|
-
this.#onQuestionAnswer();
|
|
473
|
-
resolve(this.selectedValue);
|
|
474
|
-
} finally {
|
|
475
|
-
this.write(SYMBOLS.ShowCursor);
|
|
476
|
-
this.#onProcessExit();
|
|
477
|
-
process.off("exit", this.#boundExitEvent);
|
|
478
|
-
this.destroy();
|
|
479
|
-
}
|
|
480
|
-
});
|
|
481
|
-
}
|
|
482
|
-
};
|
|
483
|
-
|
|
484
|
-
// src/prompts/select.ts
|
|
485
|
-
import { EOL as EOL4 } from "node:os";
|
|
486
|
-
import { styleText as styleText4 } from "node:util";
|
|
487
|
-
var kRequiredChoiceProperties = ["label", "value"];
|
|
488
|
-
var SelectPrompt = class extends AbstractPrompt {
|
|
489
|
-
#boundExitEvent = () => void 0;
|
|
490
|
-
#boundKeyPressEvent = () => void 0;
|
|
491
|
-
#validators;
|
|
492
|
-
activeIndex = 0;
|
|
493
|
-
questionMessage;
|
|
494
|
-
autocompleteValue = "";
|
|
495
|
-
options;
|
|
496
|
-
lastRender;
|
|
497
|
-
get choices() {
|
|
498
|
-
return this.options.choices;
|
|
499
|
-
}
|
|
500
|
-
get filteredChoices() {
|
|
501
|
-
if (!(this.options.autocomplete && this.autocompleteValue.length > 0)) {
|
|
502
|
-
return this.choices;
|
|
503
|
-
}
|
|
504
|
-
const isCaseSensitive = this.options.caseSensitive;
|
|
505
|
-
const autocompleteValue = isCaseSensitive ? this.autocompleteValue : this.autocompleteValue.toLowerCase();
|
|
506
|
-
return this.choices.filter((choice) => this.#filterChoice(choice, autocompleteValue, isCaseSensitive));
|
|
507
|
-
}
|
|
508
|
-
#filterChoice(choice, autocompleteValue, isCaseSensitive = false) {
|
|
509
|
-
const choiceValue = typeof choice === "string" ? isCaseSensitive ? choice : choice.toLowerCase() : isCaseSensitive ? choice.label : choice.label.toLowerCase();
|
|
510
|
-
if (autocompleteValue.includes(" ")) {
|
|
511
|
-
return this.#filterMultipleWords(choiceValue, autocompleteValue, isCaseSensitive);
|
|
512
|
-
}
|
|
513
|
-
return choiceValue.includes(autocompleteValue);
|
|
514
|
-
}
|
|
515
|
-
#filterMultipleWords(choiceValue, autocompleteValue, isCaseSensitive) {
|
|
516
|
-
return autocompleteValue.split(" ").every((word) => {
|
|
517
|
-
const wordValue = isCaseSensitive ? word : word.toLowerCase();
|
|
518
|
-
return choiceValue.includes(wordValue) || choiceValue.includes(autocompleteValue);
|
|
519
|
-
});
|
|
520
|
-
}
|
|
521
|
-
get longestChoice() {
|
|
522
|
-
return Math.max(...this.filteredChoices.map((choice) => {
|
|
523
|
-
if (typeof choice === "string") {
|
|
524
|
-
return choice.length;
|
|
525
|
-
}
|
|
526
|
-
return choice.label.length;
|
|
527
|
-
}));
|
|
528
|
-
}
|
|
529
|
-
constructor(options) {
|
|
530
|
-
const {
|
|
531
|
-
choices,
|
|
532
|
-
validators = [],
|
|
533
|
-
...baseOptions
|
|
534
|
-
} = options;
|
|
535
|
-
super({ ...baseOptions });
|
|
536
|
-
this.options = options;
|
|
537
|
-
if (!choices?.length) {
|
|
538
|
-
this.destroy();
|
|
539
|
-
throw new TypeError("Missing required param: choices");
|
|
540
|
-
}
|
|
541
|
-
this.#validators = validators;
|
|
542
|
-
for (const choice of choices) {
|
|
543
|
-
if (typeof choice === "string") {
|
|
544
|
-
continue;
|
|
545
|
-
}
|
|
546
|
-
for (const prop of kRequiredChoiceProperties) {
|
|
547
|
-
if (!choice[prop]) {
|
|
548
|
-
this.destroy();
|
|
549
|
-
throw new TypeError(`Missing ${prop} for choice ${JSON.stringify(choice)}`);
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
#getFormattedChoice(choiceIndex) {
|
|
555
|
-
const choice = this.filteredChoices[choiceIndex];
|
|
556
|
-
if (typeof choice === "string") {
|
|
557
|
-
return { value: choice, label: choice };
|
|
558
|
-
}
|
|
559
|
-
return choice;
|
|
560
|
-
}
|
|
561
|
-
#getVisibleChoices() {
|
|
562
|
-
const maxVisible = this.options.maxVisible || 8;
|
|
563
|
-
let startIndex = Math.min(this.filteredChoices.length - maxVisible, this.activeIndex - Math.floor(maxVisible / 2));
|
|
564
|
-
if (startIndex < 0) {
|
|
565
|
-
startIndex = 0;
|
|
566
|
-
}
|
|
567
|
-
const endIndex = Math.min(startIndex + maxVisible, this.filteredChoices.length);
|
|
568
|
-
return { startIndex, endIndex };
|
|
569
|
-
}
|
|
570
|
-
#showChoices() {
|
|
571
|
-
const { startIndex, endIndex } = this.#getVisibleChoices();
|
|
572
|
-
this.lastRender = { startIndex, endIndex };
|
|
573
|
-
if (this.options.autocomplete) {
|
|
574
|
-
this.write(`${SYMBOLS.Pointer} ${this.autocompleteValue}${EOL4}`);
|
|
575
|
-
}
|
|
576
|
-
for (let choiceIndex = startIndex; choiceIndex < endIndex; choiceIndex++) {
|
|
577
|
-
const choice = this.#getFormattedChoice(choiceIndex);
|
|
578
|
-
const isChoiceSelected = choiceIndex === this.activeIndex;
|
|
579
|
-
const showPreviousChoicesArrow = startIndex > 0 && choiceIndex === startIndex;
|
|
580
|
-
const showNextChoicesArrow = endIndex < this.filteredChoices.length && choiceIndex === endIndex - 1;
|
|
581
|
-
let prefixArrow = " ";
|
|
582
|
-
if (showPreviousChoicesArrow) {
|
|
583
|
-
prefixArrow = SYMBOLS.Previous;
|
|
584
|
-
} else if (showNextChoicesArrow) {
|
|
585
|
-
prefixArrow = SYMBOLS.Next;
|
|
586
|
-
}
|
|
587
|
-
const prefix = `${prefixArrow}${isChoiceSelected ? `${SYMBOLS.Pointer} ` : " "}`;
|
|
588
|
-
const formattedLabel = choice.label.padEnd(
|
|
589
|
-
this.longestChoice < 10 ? this.longestChoice : 0
|
|
590
|
-
);
|
|
591
|
-
const formattedDescription = choice.description ? ` - ${choice.description}` : "";
|
|
592
|
-
const styles = isChoiceSelected ? ["white", "bold"] : ["gray"];
|
|
593
|
-
const str = `${prefix}${styleText4(styles, `${formattedLabel}${formattedDescription}`)}${EOL4}`;
|
|
594
|
-
this.write(str);
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
#showAnsweredQuestion(label) {
|
|
598
|
-
const symbolPrefix = label === "" ? SYMBOLS.Cross : SYMBOLS.Tick;
|
|
599
|
-
const prefix = `${symbolPrefix} ${styleText4("bold", this.message)} ${SYMBOLS.Pointer}`;
|
|
600
|
-
const formattedChoice = styleText4("yellow", label);
|
|
601
|
-
this.write(`${prefix} ${formattedChoice}${EOL4}`);
|
|
602
|
-
}
|
|
603
|
-
#onProcessExit() {
|
|
604
|
-
this.stdin.off("keypress", this.#boundKeyPressEvent);
|
|
605
|
-
this.stdout.moveCursor(-this.stdout.columns, 0);
|
|
606
|
-
this.stdout.clearScreenDown();
|
|
607
|
-
this.write(SYMBOLS.ShowCursor);
|
|
608
|
-
}
|
|
609
|
-
#onKeypress(...args) {
|
|
610
|
-
const [resolve, render, , key] = args;
|
|
611
|
-
if (key.name === "up") {
|
|
612
|
-
this.activeIndex = this.activeIndex === 0 ? this.filteredChoices.length - 1 : this.activeIndex - 1;
|
|
613
|
-
render();
|
|
614
|
-
} else if (key.name === "down") {
|
|
615
|
-
this.activeIndex = this.activeIndex === this.filteredChoices.length - 1 ? 0 : this.activeIndex + 1;
|
|
616
|
-
render();
|
|
617
|
-
} else if (key.name === "return") {
|
|
618
|
-
const choice = this.filteredChoices[this.activeIndex] || "";
|
|
619
|
-
const label = typeof choice === "string" ? choice : choice.label;
|
|
620
|
-
const value = typeof choice === "string" ? choice : choice.value;
|
|
621
|
-
for (const validator of this.#validators) {
|
|
622
|
-
const validationResult = validator.validate(value);
|
|
623
|
-
if (isValid(validationResult) === false) {
|
|
624
|
-
render({ error: resultError(validationResult) });
|
|
625
|
-
return;
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
render({ clearRender: true });
|
|
629
|
-
if (!this.options.ignoreValues?.includes(value)) {
|
|
630
|
-
this.#showAnsweredQuestion(label);
|
|
631
|
-
}
|
|
632
|
-
this.write(SYMBOLS.ShowCursor);
|
|
633
|
-
this.destroy();
|
|
634
|
-
this.#onProcessExit();
|
|
635
|
-
process.off("exit", this.#boundExitEvent);
|
|
636
|
-
resolve(value);
|
|
637
|
-
} else {
|
|
638
|
-
if (!key.ctrl && this.options.autocomplete) {
|
|
639
|
-
this.activeIndex = 0;
|
|
640
|
-
if (key.name === "backspace" && this.autocompleteValue.length > 0) {
|
|
641
|
-
this.autocompleteValue = this.autocompleteValue.slice(0, -1);
|
|
642
|
-
} else if (key.name !== "backspace") {
|
|
643
|
-
this.autocompleteValue += key.sequence;
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
render();
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
async select() {
|
|
650
|
-
if (this.skip) {
|
|
651
|
-
this.destroy();
|
|
652
|
-
const answer = this.options.choices[0];
|
|
653
|
-
return typeof answer === "string" ? answer : answer.value;
|
|
654
|
-
}
|
|
655
|
-
return new Promise((resolve, reject) => {
|
|
656
|
-
const answer = this.agent.nextAnswers.shift();
|
|
657
|
-
if (answer !== void 0) {
|
|
658
|
-
this.#showAnsweredQuestion(answer);
|
|
659
|
-
this.destroy();
|
|
660
|
-
resolve(answer);
|
|
661
|
-
return;
|
|
662
|
-
}
|
|
663
|
-
this.once("error", (error) => {
|
|
664
|
-
reject(error);
|
|
665
|
-
});
|
|
666
|
-
this.write(SYMBOLS.HideCursor);
|
|
667
|
-
this.#showQuestion();
|
|
668
|
-
const render = (options = {}) => {
|
|
669
|
-
const {
|
|
670
|
-
initialRender = false,
|
|
671
|
-
clearRender = false,
|
|
672
|
-
error = null
|
|
673
|
-
} = options;
|
|
674
|
-
if (!initialRender) {
|
|
675
|
-
let linesToClear = this.lastRender.endIndex - this.lastRender.startIndex;
|
|
676
|
-
while (linesToClear > 0) {
|
|
677
|
-
this.clearLastLine();
|
|
678
|
-
linesToClear--;
|
|
679
|
-
}
|
|
680
|
-
if (this.options.autocomplete) {
|
|
681
|
-
let linesToClear2 = Math.ceil(
|
|
682
|
-
stringLength(`${SYMBOLS.Pointer} ${this.autocompleteValue}`) / this.stdout.columns
|
|
683
|
-
);
|
|
684
|
-
while (linesToClear2 > 0) {
|
|
685
|
-
this.clearLastLine();
|
|
686
|
-
linesToClear2--;
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
if (clearRender) {
|
|
691
|
-
const questionLineCount = Math.ceil(
|
|
692
|
-
stringLength(this.questionMessage) / this.stdout.columns
|
|
693
|
-
);
|
|
694
|
-
this.stdout.moveCursor(-this.stdout.columns, -(1 + questionLineCount));
|
|
695
|
-
this.stdout.clearScreenDown();
|
|
696
|
-
return;
|
|
697
|
-
}
|
|
698
|
-
if (error) {
|
|
699
|
-
const linesToClear = Math.ceil(stringLength(this.questionMessage) / this.stdout.columns) + 1;
|
|
700
|
-
this.stdout.moveCursor(0, -linesToClear);
|
|
701
|
-
this.stdout.clearScreenDown();
|
|
702
|
-
this.#showQuestion(error);
|
|
703
|
-
}
|
|
704
|
-
this.#showChoices();
|
|
705
|
-
};
|
|
706
|
-
render({ initialRender: true });
|
|
707
|
-
this.#boundKeyPressEvent = this.#onKeypress.bind(this, resolve, render);
|
|
708
|
-
this.stdin.on("keypress", this.#boundKeyPressEvent);
|
|
709
|
-
this.#boundExitEvent = this.#onProcessExit.bind(this);
|
|
710
|
-
process.once("exit", this.#boundExitEvent);
|
|
711
|
-
});
|
|
712
|
-
}
|
|
713
|
-
#showQuestion(error = null) {
|
|
714
|
-
let hint = "";
|
|
715
|
-
if (error) {
|
|
716
|
-
hint = ` ${hint.length > 0 ? " " : ""}${styleText4(["red", "bold"], `[${error}]`)}`;
|
|
717
|
-
}
|
|
718
|
-
this.questionMessage = `${SYMBOLS.QuestionMark} ${styleText4("bold", this.message)}${hint}`;
|
|
719
|
-
this.write(`${this.questionMessage}${EOL4}`);
|
|
720
|
-
}
|
|
721
|
-
};
|
|
722
|
-
|
|
723
|
-
// src/prompts/multiselect.ts
|
|
724
|
-
import { EOL as EOL5 } from "node:os";
|
|
725
|
-
import { styleText as styleText5 } from "node:util";
|
|
726
|
-
var kRequiredChoiceProperties2 = ["label", "value"];
|
|
727
|
-
var MultiselectPrompt = class extends AbstractPrompt {
|
|
728
|
-
#boundExitEvent = () => void 0;
|
|
729
|
-
#boundKeyPressEvent = () => void 0;
|
|
730
|
-
#validators;
|
|
731
|
-
#showHint;
|
|
732
|
-
activeIndex = 0;
|
|
733
|
-
selectedIndexes = /* @__PURE__ */ new Set();
|
|
734
|
-
questionMessage;
|
|
735
|
-
autocompleteValue = "";
|
|
736
|
-
options;
|
|
737
|
-
lastRender;
|
|
738
|
-
get choices() {
|
|
739
|
-
return this.options.choices;
|
|
740
|
-
}
|
|
741
|
-
get filteredChoices() {
|
|
742
|
-
if (!(this.options.autocomplete && this.autocompleteValue.length > 0)) {
|
|
743
|
-
return this.choices;
|
|
744
|
-
}
|
|
745
|
-
const isCaseSensitive = this.options.caseSensitive;
|
|
746
|
-
const autocompleteValue = isCaseSensitive ? this.autocompleteValue : this.autocompleteValue.toLowerCase();
|
|
747
|
-
return this.choices.filter((choice) => this.#filterChoice(choice, autocompleteValue, isCaseSensitive));
|
|
748
|
-
}
|
|
749
|
-
#filterChoice(choice, autocompleteValue, isCaseSensitive = false) {
|
|
750
|
-
const choiceValue = typeof choice === "string" ? isCaseSensitive ? choice : choice.toLowerCase() : isCaseSensitive ? choice.label : choice.label.toLowerCase();
|
|
751
|
-
if (autocompleteValue.includes(" ")) {
|
|
752
|
-
return this.#filterMultipleWords(choiceValue, autocompleteValue, isCaseSensitive);
|
|
753
|
-
}
|
|
754
|
-
return choiceValue.includes(autocompleteValue);
|
|
755
|
-
}
|
|
756
|
-
#filterMultipleWords(choiceValue, autocompleteValue, isCaseSensitive) {
|
|
757
|
-
return autocompleteValue.split(" ").every((word) => {
|
|
758
|
-
const wordValue = isCaseSensitive ? word : word.toLowerCase();
|
|
759
|
-
return choiceValue.includes(wordValue) || choiceValue.includes(autocompleteValue);
|
|
760
|
-
});
|
|
761
|
-
}
|
|
762
|
-
get longestChoice() {
|
|
763
|
-
return Math.max(...this.filteredChoices.map((choice) => {
|
|
764
|
-
if (typeof choice === "string") {
|
|
765
|
-
return choice.length;
|
|
766
|
-
}
|
|
767
|
-
return choice.label.length;
|
|
768
|
-
}));
|
|
769
|
-
}
|
|
770
|
-
constructor(options) {
|
|
771
|
-
const {
|
|
772
|
-
choices,
|
|
773
|
-
preSelectedChoices = [],
|
|
774
|
-
validators = [],
|
|
775
|
-
showHint = true,
|
|
776
|
-
...baseOptions
|
|
777
|
-
} = options;
|
|
778
|
-
super({ ...baseOptions });
|
|
779
|
-
this.options = options;
|
|
780
|
-
if (!choices?.length) {
|
|
781
|
-
this.destroy();
|
|
782
|
-
throw new TypeError("Missing required param: choices");
|
|
783
|
-
}
|
|
784
|
-
this.#validators = validators;
|
|
785
|
-
this.#showHint = showHint;
|
|
786
|
-
for (const choice of choices) {
|
|
787
|
-
if (typeof choice === "string") {
|
|
788
|
-
continue;
|
|
789
|
-
}
|
|
790
|
-
for (const prop of kRequiredChoiceProperties2) {
|
|
791
|
-
if (!choice[prop]) {
|
|
792
|
-
this.destroy();
|
|
793
|
-
throw new TypeError(`Missing ${prop} for choice ${JSON.stringify(choice)}`);
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
for (const choice of preSelectedChoices) {
|
|
798
|
-
const choiceIndex = this.filteredChoices.findIndex((item) => {
|
|
799
|
-
if (typeof item === "string") {
|
|
800
|
-
return item === choice;
|
|
801
|
-
}
|
|
802
|
-
return item.value === choice;
|
|
803
|
-
});
|
|
804
|
-
if (choiceIndex === -1) {
|
|
805
|
-
this.destroy();
|
|
806
|
-
throw new Error(`Invalid pre-selected choice: ${typeof choice === "string" ? choice : choice.value}`);
|
|
807
|
-
}
|
|
808
|
-
this.selectedIndexes.add(choiceIndex);
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
#getFormattedChoice(choiceIndex) {
|
|
812
|
-
const choice = this.filteredChoices[choiceIndex];
|
|
813
|
-
if (typeof choice === "string") {
|
|
814
|
-
return { value: choice, label: choice };
|
|
815
|
-
}
|
|
816
|
-
return choice;
|
|
817
|
-
}
|
|
818
|
-
#getVisibleChoices() {
|
|
819
|
-
const maxVisible = this.options.maxVisible || 8;
|
|
820
|
-
let startIndex = Math.min(this.filteredChoices.length - maxVisible, this.activeIndex - Math.floor(maxVisible / 2));
|
|
821
|
-
if (startIndex < 0) {
|
|
822
|
-
startIndex = 0;
|
|
823
|
-
}
|
|
824
|
-
const endIndex = Math.min(startIndex + maxVisible, this.filteredChoices.length);
|
|
825
|
-
return { startIndex, endIndex };
|
|
826
|
-
}
|
|
827
|
-
#showChoices() {
|
|
828
|
-
const { startIndex, endIndex } = this.#getVisibleChoices();
|
|
829
|
-
this.lastRender = { startIndex, endIndex };
|
|
830
|
-
if (this.options.autocomplete) {
|
|
831
|
-
this.write(`${SYMBOLS.Pointer} ${this.autocompleteValue}${EOL5}`);
|
|
832
|
-
}
|
|
833
|
-
for (let choiceIndex = startIndex; choiceIndex < endIndex; choiceIndex++) {
|
|
834
|
-
const choice = this.#getFormattedChoice(choiceIndex);
|
|
835
|
-
const isChoiceActive = choiceIndex === this.activeIndex;
|
|
836
|
-
const isChoiceSelected = this.selectedIndexes.has(choiceIndex);
|
|
837
|
-
const showPreviousChoicesArrow = startIndex > 0 && choiceIndex === startIndex;
|
|
838
|
-
const showNextChoicesArrow = endIndex < this.filteredChoices.length && choiceIndex === endIndex - 1;
|
|
839
|
-
let prefixArrow = " ";
|
|
840
|
-
if (showPreviousChoicesArrow) {
|
|
841
|
-
prefixArrow = SYMBOLS.Previous + " ";
|
|
842
|
-
} else if (showNextChoicesArrow) {
|
|
843
|
-
prefixArrow = SYMBOLS.Next + " ";
|
|
844
|
-
}
|
|
845
|
-
const prefix = `${prefixArrow}${isChoiceSelected ? SYMBOLS.Active : SYMBOLS.Inactive}`;
|
|
846
|
-
const formattedLabel = choice.label.padEnd(
|
|
847
|
-
this.longestChoice < 10 ? this.longestChoice : 0
|
|
848
|
-
);
|
|
849
|
-
const formattedDescription = choice.description ? ` - ${choice.description}` : "";
|
|
850
|
-
const styles = isChoiceActive ? ["white", "bold"] : ["gray"];
|
|
851
|
-
const str = `${prefix} ${styleText5(styles, `${formattedLabel}${formattedDescription}`)}${EOL5}`;
|
|
852
|
-
this.write(str);
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
#showAnsweredQuestion(choices, isAgentAnswer = false) {
|
|
856
|
-
const prefixSymbol = this.selectedIndexes.size === 0 && !isAgentAnswer ? SYMBOLS.Cross : SYMBOLS.Tick;
|
|
857
|
-
const prefix = `${prefixSymbol} ${styleText5("bold", this.message)} ${SYMBOLS.Pointer}`;
|
|
858
|
-
const formattedChoice = styleText5("yellow", choices);
|
|
859
|
-
this.write(`${prefix}${choices ? ` ${formattedChoice}` : ""}${EOL5}`);
|
|
860
|
-
}
|
|
861
|
-
#selectedChoices() {
|
|
862
|
-
return [...this.selectedIndexes].reduce(
|
|
863
|
-
(acc, index) => {
|
|
864
|
-
const choice = this.filteredChoices[index];
|
|
865
|
-
if (typeof choice === "string") {
|
|
866
|
-
acc.values.push(choice);
|
|
867
|
-
acc.labels.push(choice);
|
|
868
|
-
} else {
|
|
869
|
-
acc.values.push(choice.value);
|
|
870
|
-
acc.labels.push(choice.label);
|
|
871
|
-
}
|
|
872
|
-
return acc;
|
|
873
|
-
},
|
|
874
|
-
{
|
|
875
|
-
values: [],
|
|
876
|
-
labels: []
|
|
877
|
-
}
|
|
878
|
-
);
|
|
879
|
-
}
|
|
880
|
-
#onProcessExit() {
|
|
881
|
-
this.stdin.off("keypress", this.#boundKeyPressEvent);
|
|
882
|
-
this.stdout.moveCursor(-this.stdout.columns, 0);
|
|
883
|
-
this.stdout.clearScreenDown();
|
|
884
|
-
this.write(SYMBOLS.ShowCursor);
|
|
885
|
-
}
|
|
886
|
-
#onKeypress(...args) {
|
|
887
|
-
const [resolve, render, , key] = args;
|
|
888
|
-
if (key.name === "up") {
|
|
889
|
-
this.activeIndex = this.activeIndex === 0 ? this.filteredChoices.length - 1 : this.activeIndex - 1;
|
|
890
|
-
render();
|
|
891
|
-
} else if (key.name === "down") {
|
|
892
|
-
this.activeIndex = this.activeIndex === this.filteredChoices.length - 1 ? 0 : this.activeIndex + 1;
|
|
893
|
-
render();
|
|
894
|
-
} else if (key.ctrl && key.name === "a") {
|
|
895
|
-
this.selectedIndexes = this.selectedIndexes.size === this.filteredChoices.length ? /* @__PURE__ */ new Set() : new Set(this.filteredChoices.map((_, index) => index));
|
|
896
|
-
render();
|
|
897
|
-
} else if (key.name === "right") {
|
|
898
|
-
this.selectedIndexes.add(this.activeIndex);
|
|
899
|
-
render();
|
|
900
|
-
} else if (key.name === "left") {
|
|
901
|
-
this.selectedIndexes = new Set([...this.selectedIndexes].filter((index) => index !== this.activeIndex));
|
|
902
|
-
render();
|
|
903
|
-
} else if (key.name === "return") {
|
|
904
|
-
const { values, labels } = this.#selectedChoices();
|
|
905
|
-
for (const validator of this.#validators) {
|
|
906
|
-
const validationResult = validator.validate(values);
|
|
907
|
-
if (isValid(validationResult) === false) {
|
|
908
|
-
render({ error: resultError(validationResult) });
|
|
909
|
-
return;
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
render({ clearRender: true });
|
|
913
|
-
this.#showAnsweredQuestion(labels.join(", "));
|
|
914
|
-
this.write(SYMBOLS.ShowCursor);
|
|
915
|
-
this.destroy();
|
|
916
|
-
this.#onProcessExit();
|
|
917
|
-
process.off("exit", this.#boundExitEvent);
|
|
918
|
-
resolve(values);
|
|
919
|
-
} else {
|
|
920
|
-
if (!key.ctrl && this.options.autocomplete) {
|
|
921
|
-
this.selectedIndexes.clear();
|
|
922
|
-
this.activeIndex = 0;
|
|
923
|
-
if (key.name === "backspace" && this.autocompleteValue.length > 0) {
|
|
924
|
-
this.autocompleteValue = this.autocompleteValue.slice(0, -1);
|
|
925
|
-
} else if (key.name !== "backspace") {
|
|
926
|
-
this.autocompleteValue += key.sequence;
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
render();
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
async multiselect() {
|
|
933
|
-
if (this.skip) {
|
|
934
|
-
this.destroy();
|
|
935
|
-
const { values } = this.#selectedChoices();
|
|
936
|
-
return values;
|
|
937
|
-
}
|
|
938
|
-
return new Promise((resolve, reject) => {
|
|
939
|
-
const answer = this.agent.nextAnswers.shift();
|
|
940
|
-
if (answer !== void 0) {
|
|
941
|
-
const formatedAnser = Array.isArray(answer) ? answer.join(", ") : answer;
|
|
942
|
-
this.#showAnsweredQuestion(formatedAnser, true);
|
|
943
|
-
this.destroy();
|
|
944
|
-
resolve(Array.isArray(answer) ? answer : [answer]);
|
|
945
|
-
return;
|
|
946
|
-
}
|
|
947
|
-
this.once("error", (error) => {
|
|
948
|
-
reject(error);
|
|
949
|
-
});
|
|
950
|
-
this.write(SYMBOLS.HideCursor);
|
|
951
|
-
this.#showQuestion();
|
|
952
|
-
const render = (options = {}) => {
|
|
953
|
-
const {
|
|
954
|
-
initialRender = false,
|
|
955
|
-
clearRender = false,
|
|
956
|
-
error = null
|
|
957
|
-
} = options;
|
|
958
|
-
if (!initialRender) {
|
|
959
|
-
let linesToClear = this.lastRender.endIndex - this.lastRender.startIndex;
|
|
960
|
-
while (linesToClear > 0) {
|
|
961
|
-
this.clearLastLine();
|
|
962
|
-
linesToClear--;
|
|
963
|
-
}
|
|
964
|
-
if (this.options.autocomplete) {
|
|
965
|
-
let linesToClear2 = Math.ceil(
|
|
966
|
-
stringLength(`${SYMBOLS.Pointer} ${this.autocompleteValue}`) / this.stdout.columns
|
|
967
|
-
);
|
|
968
|
-
while (linesToClear2 > 0) {
|
|
969
|
-
this.clearLastLine();
|
|
970
|
-
linesToClear2--;
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
if (clearRender) {
|
|
975
|
-
const questionLineCount = Math.ceil(
|
|
976
|
-
stringLength(this.questionMessage) / this.stdout.columns
|
|
977
|
-
);
|
|
978
|
-
this.stdout.moveCursor(-this.stdout.columns, -(1 + questionLineCount));
|
|
979
|
-
this.stdout.clearScreenDown();
|
|
980
|
-
return;
|
|
981
|
-
}
|
|
982
|
-
if (error) {
|
|
983
|
-
const linesToClear = Math.ceil(stringLength(this.questionMessage) / this.stdout.columns) + 1;
|
|
984
|
-
this.stdout.moveCursor(0, -linesToClear);
|
|
985
|
-
this.stdout.clearScreenDown();
|
|
986
|
-
this.#showQuestion(error);
|
|
987
|
-
}
|
|
988
|
-
this.#showChoices();
|
|
989
|
-
};
|
|
990
|
-
render({ initialRender: true });
|
|
991
|
-
this.#boundKeyPressEvent = this.#onKeypress.bind(this, resolve, render);
|
|
992
|
-
this.stdin.on("keypress", this.#boundKeyPressEvent);
|
|
993
|
-
this.#boundExitEvent = this.#onProcessExit.bind(this);
|
|
994
|
-
process.once("exit", this.#boundExitEvent);
|
|
995
|
-
});
|
|
996
|
-
}
|
|
997
|
-
#showQuestion(error = null) {
|
|
998
|
-
let hint = this.#showHint ? styleText5(
|
|
999
|
-
"gray",
|
|
1000
|
-
// eslint-disable-next-line @stylistic/max-len
|
|
1001
|
-
`(Press ${styleText5("bold", "<Ctrl+A>")} to toggle all, ${styleText5("bold", "<Left/Right>")} to toggle, ${styleText5("bold", "<Return>")} to submit)`
|
|
1002
|
-
) : "";
|
|
1003
|
-
if (error) {
|
|
1004
|
-
hint += `${hint.length > 0 ? " " : ""}${styleText5(["red", "bold"], `[${error}]`)}`;
|
|
1005
|
-
}
|
|
1006
|
-
this.questionMessage = `${SYMBOLS.QuestionMark} ${styleText5("bold", this.message)}${hint.length > 0 ? ` ${hint}` : ""}`;
|
|
1007
|
-
this.write(`${this.questionMessage}${EOL5}`);
|
|
1008
|
-
}
|
|
1009
|
-
};
|
|
1010
|
-
|
|
1011
|
-
// src/index.ts
|
|
1012
|
-
function question(message, options = {}) {
|
|
1013
|
-
return new QuestionPrompt(
|
|
1014
|
-
{ ...options, message }
|
|
1015
|
-
).question();
|
|
1016
|
-
}
|
|
1017
|
-
function select(message, options) {
|
|
1018
|
-
return new SelectPrompt(
|
|
1019
|
-
{ ...options, message }
|
|
1020
|
-
).select();
|
|
1021
|
-
}
|
|
1022
|
-
function confirm(message, options = {}) {
|
|
1023
|
-
return new ConfirmPrompt(
|
|
1024
|
-
{ ...options, message }
|
|
1025
|
-
).confirm();
|
|
1026
|
-
}
|
|
1027
|
-
function multiselect(message, options) {
|
|
1028
|
-
return new MultiselectPrompt(
|
|
1029
|
-
{ ...options, message }
|
|
1030
|
-
).multiselect();
|
|
1031
|
-
}
|
|
1032
|
-
export {
|
|
1033
|
-
PromptAgent,
|
|
1034
|
-
confirm,
|
|
1035
|
-
multiselect,
|
|
1036
|
-
question,
|
|
1037
|
-
required,
|
|
1038
|
-
select
|
|
1039
|
-
};
|