@topcli/prompts 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -70,8 +70,8 @@ const packageName = await question('Package name', {
70
70
  validators: [
71
71
  {
72
72
  validate: (value) => {
73
- if (!existsSync(join(process.cwd(), value))) {
74
- return `Folder ${value} already exists`
73
+ if (fs.existsSync(path.join(process.cwd(), value))) {
74
+ return `Folder ${value} already exists`;
75
75
  }
76
76
  }
77
77
  }
package/dist/index.cjs CHANGED
@@ -41,35 +41,14 @@ module.exports = __toCommonJS(index_exports);
41
41
 
42
42
  // src/prompts/question.ts
43
43
  var import_node_os2 = require("os");
44
- var import_node_util2 = require("util");
45
- var import_wcwidth = __toESM(require("@topcli/wcwidth"), 1);
44
+ var import_node_util4 = require("util");
46
45
 
47
46
  // src/prompts/abstract.ts
48
47
  var import_node_os = require("os");
49
48
  var import_node_readline = __toESM(require("readline"), 1);
50
49
  var import_node_stream = require("stream");
51
50
  var import_node_events = __toESM(require("events"), 1);
52
-
53
- // src/utils.ts
54
- var import_node_process = __toESM(require("process"), 1);
55
- var kAnsiRegex = ansiRegex();
56
- function ansiRegex({ onlyFirst = false } = {}) {
57
- const pattern = [
58
- // eslint-disable-next-line @stylistic/max-len
59
- "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
60
- "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))"
61
- ].join("|");
62
- return new RegExp(pattern, onlyFirst ? void 0 : "g");
63
- }
64
- function stripAnsi(string) {
65
- return string.replace(kAnsiRegex, "");
66
- }
67
- function isUnicodeSupported() {
68
- if (import_node_process.default.platform !== "win32") {
69
- return import_node_process.default.env.TERM !== "linux";
70
- }
71
- return Boolean(import_node_process.default.env.WT_SESSION) || Boolean(import_node_process.default.env.TERMINUS_SUBLIME) || import_node_process.default.env.ConEmuTask === "{cmd::Cmder}" || import_node_process.default.env.TERM_PROGRAM === "Terminus-Sublime" || import_node_process.default.env.TERM_PROGRAM === "vscode" || import_node_process.default.env.TERM === "xterm-256color" || import_node_process.default.env.TERM === "alacritty" || import_node_process.default.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
72
- }
51
+ var import_node_util = require("util");
73
52
 
74
53
  // src/prompt-agent.ts
75
54
  var kPrivateInstancier = Symbol("instancier");
@@ -193,7 +172,7 @@ var AbstractPrompt = class _AbstractPrompt extends import_node_events.default {
193
172
  }
194
173
  }
195
174
  write(data) {
196
- const formattedData = stripAnsi(data).replace(import_node_os.EOL, "");
175
+ const formattedData = (0, import_node_util.stripVTControlCharacters)(data).replace(import_node_os.EOL, "");
197
176
  if (formattedData) {
198
177
  this.history.push(formattedData);
199
178
  }
@@ -204,7 +183,7 @@ var AbstractPrompt = class _AbstractPrompt extends import_node_events.default {
204
183
  if (!lastLine) {
205
184
  return;
206
185
  }
207
- const lastLineRows = Math.ceil(stripAnsi(lastLine).length / this.stdout.columns);
186
+ const lastLineRows = Math.ceil((0, import_node_util.stripVTControlCharacters)(lastLine).length / this.stdout.columns);
208
187
  this.stdout.moveCursor(-this.stdout.columns, -lastLineRows);
209
188
  this.stdout.clearScreenDown();
210
189
  }
@@ -216,8 +195,31 @@ var AbstractPrompt = class _AbstractPrompt extends import_node_events.default {
216
195
  }
217
196
  };
218
197
 
198
+ // src/utils.ts
199
+ var import_node_process = __toESM(require("process"), 1);
200
+ var import_node_util2 = require("util");
201
+ var kLenSegmenter = new Intl.Segmenter();
202
+ function isUnicodeSupported() {
203
+ if (import_node_process.default.platform !== "win32") {
204
+ return import_node_process.default.env.TERM !== "linux";
205
+ }
206
+ return Boolean(import_node_process.default.env.WT_SESSION) || Boolean(import_node_process.default.env.TERMINUS_SUBLIME) || import_node_process.default.env.ConEmuTask === "{cmd::Cmder}" || import_node_process.default.env.TERM_PROGRAM === "Terminus-Sublime" || import_node_process.default.env.TERM_PROGRAM === "vscode" || import_node_process.default.env.TERM === "xterm-256color" || import_node_process.default.env.TERM === "alacritty" || import_node_process.default.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
207
+ }
208
+ function stringLength(string) {
209
+ if (string === "") {
210
+ return 0;
211
+ }
212
+ let length = 0;
213
+ for (const _ of kLenSegmenter.segment(
214
+ (0, import_node_util2.stripVTControlCharacters)(string)
215
+ )) {
216
+ length++;
217
+ }
218
+ return length;
219
+ }
220
+
219
221
  // src/constants.ts
220
- var import_node_util = require("util");
222
+ var import_node_util3 = require("util");
221
223
  var kMainSymbols = {
222
224
  tick: "\u2714",
223
225
  cross: "\u2716",
@@ -237,18 +239,18 @@ var kFallbackSymbols = {
237
239
  inactive: "(-)"
238
240
  };
239
241
  var kSymbols = isUnicodeSupported() || process.env.CI ? kMainSymbols : kFallbackSymbols;
240
- var kPointer = (0, import_node_util.styleText)("gray", kSymbols.pointer);
242
+ var kPointer = (0, import_node_util3.styleText)("gray", kSymbols.pointer);
241
243
  var SYMBOLS = {
242
- QuestionMark: (0, import_node_util.styleText)(["blue", "bold"], "?"),
243
- Tick: (0, import_node_util.styleText)(["green", "bold"], kSymbols.tick),
244
- Cross: (0, import_node_util.styleText)(["red", "bold"], kSymbols.cross),
244
+ QuestionMark: (0, import_node_util3.styleText)(["blue", "bold"], "?"),
245
+ Tick: (0, import_node_util3.styleText)(["green", "bold"], kSymbols.tick),
246
+ Cross: (0, import_node_util3.styleText)(["red", "bold"], kSymbols.cross),
245
247
  Pointer: kPointer,
246
248
  Previous: kSymbols.previous,
247
249
  Next: kSymbols.next,
248
250
  ShowCursor: "\x1B[?25h",
249
251
  HideCursor: "\x1B[?25l",
250
- Active: (0, import_node_util.styleText)("cyan", kSymbols.active),
251
- Inactive: (0, import_node_util.styleText)("gray", kSymbols.inactive)
252
+ Active: (0, import_node_util3.styleText)("cyan", kSymbols.active),
253
+ Inactive: (0, import_node_util3.styleText)("gray", kSymbols.inactive)
252
254
  };
253
255
 
254
256
  // src/validators.ts
@@ -315,20 +317,20 @@ var QuestionPrompt = class extends AbstractPrompt {
315
317
  });
316
318
  }
317
319
  #getQuestionQuery() {
318
- return `${(0, import_node_util2.styleText)("bold", `${SYMBOLS.QuestionMark} ${this.message}${this.tip}`)} ${this.questionSuffixError}`;
320
+ return `${(0, import_node_util4.styleText)("bold", `${SYMBOLS.QuestionMark} ${this.message}${this.tip}`)} ${this.questionSuffixError}`;
319
321
  }
320
322
  #setQuestionSuffixError(error) {
321
- const suffix = (0, import_node_util2.styleText)("red", `[${error}] `);
323
+ const suffix = (0, import_node_util4.styleText)("red", `[${error}] `);
322
324
  this.questionSuffixError = suffix;
323
325
  }
324
326
  #writeAnswer() {
325
327
  const prefix = this.answer ? SYMBOLS.Tick : SYMBOLS.Cross;
326
- const answer = (0, import_node_util2.styleText)("yellow", this.#secure ? "CONFIDENTIAL" : this.answer ?? "");
327
- this.write(`${prefix} ${(0, import_node_util2.styleText)("bold", this.message)} ${SYMBOLS.Pointer} ${answer}${import_node_os2.EOL}`);
328
+ const answer = (0, import_node_util4.styleText)("yellow", this.#secure ? "CONFIDENTIAL" : this.answer ?? "");
329
+ this.write(`${prefix} ${(0, import_node_util4.styleText)("bold", this.message)} ${SYMBOLS.Pointer} ${answer}${import_node_os2.EOL}`);
328
330
  }
329
331
  #onQuestionAnswer() {
330
332
  const questionLineCount = Math.ceil(
331
- (0, import_wcwidth.default)(stripAnsi(this.#getQuestionQuery() + this.answer)) / this.stdout.columns
333
+ stringLength(this.#getQuestionQuery() + this.answer) / this.stdout.columns
332
334
  );
333
335
  this.stdout.moveCursor(-this.stdout.columns, -questionLineCount);
334
336
  this.stdout.clearScreenDown();
@@ -376,8 +378,7 @@ var QuestionPrompt = class extends AbstractPrompt {
376
378
 
377
379
  // src/prompts/confirm.ts
378
380
  var import_node_os3 = require("os");
379
- var import_node_util3 = require("util");
380
- var import_wcwidth2 = __toESM(require("@topcli/wcwidth"), 1);
381
+ var import_node_util5 = require("util");
381
382
  var kToggleKeys = /* @__PURE__ */ new Set([
382
383
  "left",
383
384
  "right",
@@ -407,8 +408,8 @@ var ConfirmPrompt = class extends AbstractPrompt {
407
408
  this.selectedValue = initial;
408
409
  }
409
410
  #getHint() {
410
- const Yes = (0, import_node_util3.styleText)(["cyan", "bold", "underline"], "Yes");
411
- const No = (0, import_node_util3.styleText)(["cyan", "bold", "underline"], "No");
411
+ const Yes = (0, import_node_util5.styleText)(["cyan", "bold", "underline"], "Yes");
412
+ const No = (0, import_node_util5.styleText)(["cyan", "bold", "underline"], "No");
412
413
  return this.selectedValue ? `${Yes}/No` : `Yes/${No}`;
413
414
  }
414
415
  #render() {
@@ -427,7 +428,7 @@ var ConfirmPrompt = class extends AbstractPrompt {
427
428
  #onKeypress(resolve, _value, key) {
428
429
  this.stdout.moveCursor(
429
430
  -this.stdout.columns,
430
- -Math.floor((0, import_wcwidth2.default)(stripAnsi(this.#getQuestionQuery())) / this.stdout.columns)
431
+ -Math.floor(stringLength(this.#getQuestionQuery()) / this.stdout.columns)
431
432
  );
432
433
  this.stdout.clearScreenDown();
433
434
  if (key.name === "return") {
@@ -454,17 +455,17 @@ var ConfirmPrompt = class extends AbstractPrompt {
454
455
  this.stdin.off("keypress", this.#boundKeyPressEvent);
455
456
  }
456
457
  #getQuestionQuery() {
457
- const query = (0, import_node_util3.styleText)("bold", `${SYMBOLS.QuestionMark} ${this.message}`);
458
+ const query = (0, import_node_util5.styleText)("bold", `${SYMBOLS.QuestionMark} ${this.message}`);
458
459
  return `${query} ${this.#getHint()}`;
459
460
  }
460
461
  #onQuestionAnswer() {
461
462
  this.clearLastLine();
462
463
  this.stdout.moveCursor(
463
464
  -this.stdout.columns,
464
- -Math.floor((0, import_wcwidth2.default)(stripAnsi(this.#getQuestionQuery())) / this.stdout.columns)
465
+ -Math.floor(stringLength(this.#getQuestionQuery()) / this.stdout.columns)
465
466
  );
466
467
  this.stdout.clearScreenDown();
467
- this.write(`${this.selectedValue ? SYMBOLS.Tick : SYMBOLS.Cross} ${(0, import_node_util3.styleText)("bold", this.message)}${import_node_os3.EOL}`);
468
+ this.write(`${this.selectedValue ? SYMBOLS.Tick : SYMBOLS.Cross} ${(0, import_node_util5.styleText)("bold", this.message)}${import_node_os3.EOL}`);
468
469
  }
469
470
  async confirm() {
470
471
  if (this.skip) {
@@ -500,8 +501,7 @@ var ConfirmPrompt = class extends AbstractPrompt {
500
501
 
501
502
  // src/prompts/select.ts
502
503
  var import_node_os4 = require("os");
503
- var import_node_util4 = require("util");
504
- var import_wcwidth3 = __toESM(require("@topcli/wcwidth"), 1);
504
+ var import_node_util6 = require("util");
505
505
  var kRequiredChoiceProperties = ["label", "value"];
506
506
  var SelectPrompt = class extends AbstractPrompt {
507
507
  #boundExitEvent = () => void 0;
@@ -608,14 +608,14 @@ var SelectPrompt = class extends AbstractPrompt {
608
608
  );
609
609
  const formattedDescription = choice.description ? ` - ${choice.description}` : "";
610
610
  const styles = isChoiceSelected ? ["white", "bold"] : ["gray"];
611
- const str = `${prefix}${(0, import_node_util4.styleText)(styles, `${formattedLabel}${formattedDescription}`)}${import_node_os4.EOL}`;
611
+ const str = `${prefix}${(0, import_node_util6.styleText)(styles, `${formattedLabel}${formattedDescription}`)}${import_node_os4.EOL}`;
612
612
  this.write(str);
613
613
  }
614
614
  }
615
- #showAnsweredQuestion(choice) {
616
- const symbolPrefix = choice === "" ? SYMBOLS.Cross : SYMBOLS.Tick;
617
- const prefix = `${symbolPrefix} ${(0, import_node_util4.styleText)("bold", this.message)} ${SYMBOLS.Pointer}`;
618
- const formattedChoice = (0, import_node_util4.styleText)("yellow", typeof choice === "string" ? choice : choice.label);
615
+ #showAnsweredQuestion(label) {
616
+ const symbolPrefix = label === "" ? SYMBOLS.Cross : SYMBOLS.Tick;
617
+ const prefix = `${symbolPrefix} ${(0, import_node_util6.styleText)("bold", this.message)} ${SYMBOLS.Pointer}`;
618
+ const formattedChoice = (0, import_node_util6.styleText)("yellow", label);
619
619
  this.write(`${prefix} ${formattedChoice}${import_node_os4.EOL}`);
620
620
  }
621
621
  #onProcessExit() {
@@ -697,7 +697,7 @@ var SelectPrompt = class extends AbstractPrompt {
697
697
  }
698
698
  if (this.options.autocomplete) {
699
699
  let linesToClear2 = Math.ceil(
700
- (0, import_wcwidth3.default)(`${SYMBOLS.Pointer} ${this.autocompleteValue}`) / this.stdout.columns
700
+ stringLength(`${SYMBOLS.Pointer} ${this.autocompleteValue}`) / this.stdout.columns
701
701
  );
702
702
  while (linesToClear2 > 0) {
703
703
  this.clearLastLine();
@@ -707,14 +707,14 @@ var SelectPrompt = class extends AbstractPrompt {
707
707
  }
708
708
  if (clearRender) {
709
709
  const questionLineCount = Math.ceil(
710
- (0, import_wcwidth3.default)(stripAnsi(this.questionMessage)) / this.stdout.columns
710
+ stringLength(this.questionMessage) / this.stdout.columns
711
711
  );
712
712
  this.stdout.moveCursor(-this.stdout.columns, -(1 + questionLineCount));
713
713
  this.stdout.clearScreenDown();
714
714
  return;
715
715
  }
716
716
  if (error) {
717
- const linesToClear = Math.ceil((0, import_wcwidth3.default)(this.questionMessage) / this.stdout.columns) + 1;
717
+ const linesToClear = Math.ceil(stringLength(this.questionMessage) / this.stdout.columns) + 1;
718
718
  this.stdout.moveCursor(0, -linesToClear);
719
719
  this.stdout.clearScreenDown();
720
720
  this.#showQuestion(error);
@@ -731,17 +731,16 @@ var SelectPrompt = class extends AbstractPrompt {
731
731
  #showQuestion(error = null) {
732
732
  let hint = "";
733
733
  if (error) {
734
- hint = ` ${hint.length > 0 ? " " : ""}${(0, import_node_util4.styleText)(["red", "bold"], `[${error}]`)}`;
734
+ hint = ` ${hint.length > 0 ? " " : ""}${(0, import_node_util6.styleText)(["red", "bold"], `[${error}]`)}`;
735
735
  }
736
- this.questionMessage = `${SYMBOLS.QuestionMark} ${(0, import_node_util4.styleText)("bold", this.message)}${hint}`;
736
+ this.questionMessage = `${SYMBOLS.QuestionMark} ${(0, import_node_util6.styleText)("bold", this.message)}${hint}`;
737
737
  this.write(`${this.questionMessage}${import_node_os4.EOL}`);
738
738
  }
739
739
  };
740
740
 
741
741
  // src/prompts/multiselect.ts
742
742
  var import_node_os5 = require("os");
743
- var import_node_util5 = require("util");
744
- var import_wcwidth4 = __toESM(require("@topcli/wcwidth"), 1);
743
+ var import_node_util7 = require("util");
745
744
  var kRequiredChoiceProperties2 = ["label", "value"];
746
745
  var MultiselectPrompt = class extends AbstractPrompt {
747
746
  #boundExitEvent = () => void 0;
@@ -867,14 +866,14 @@ var MultiselectPrompt = class extends AbstractPrompt {
867
866
  );
868
867
  const formattedDescription = choice.description ? ` - ${choice.description}` : "";
869
868
  const styles = isChoiceActive ? ["white", "bold"] : ["gray"];
870
- const str = `${prefix} ${(0, import_node_util5.styleText)(styles, `${formattedLabel}${formattedDescription}`)}${import_node_os5.EOL}`;
869
+ const str = `${prefix} ${(0, import_node_util7.styleText)(styles, `${formattedLabel}${formattedDescription}`)}${import_node_os5.EOL}`;
871
870
  this.write(str);
872
871
  }
873
872
  }
874
873
  #showAnsweredQuestion(choices, isAgentAnswer = false) {
875
874
  const prefixSymbol = this.selectedIndexes.size === 0 && !isAgentAnswer ? SYMBOLS.Cross : SYMBOLS.Tick;
876
- const prefix = `${prefixSymbol} ${(0, import_node_util5.styleText)("bold", this.message)} ${SYMBOLS.Pointer}`;
877
- const formattedChoice = (0, import_node_util5.styleText)("yellow", choices);
875
+ const prefix = `${prefixSymbol} ${(0, import_node_util7.styleText)("bold", this.message)} ${SYMBOLS.Pointer}`;
876
+ const formattedChoice = (0, import_node_util7.styleText)("yellow", choices);
878
877
  this.write(`${prefix}${choices ? ` ${formattedChoice}` : ""}${import_node_os5.EOL}`);
879
878
  }
880
879
  #selectedChoices() {
@@ -982,7 +981,7 @@ var MultiselectPrompt = class extends AbstractPrompt {
982
981
  }
983
982
  if (this.options.autocomplete) {
984
983
  let linesToClear2 = Math.ceil(
985
- (0, import_wcwidth4.default)(`${SYMBOLS.Pointer} ${this.autocompleteValue}`) / this.stdout.columns
984
+ stringLength(`${SYMBOLS.Pointer} ${this.autocompleteValue}`) / this.stdout.columns
986
985
  );
987
986
  while (linesToClear2 > 0) {
988
987
  this.clearLastLine();
@@ -992,14 +991,14 @@ var MultiselectPrompt = class extends AbstractPrompt {
992
991
  }
993
992
  if (clearRender) {
994
993
  const questionLineCount = Math.ceil(
995
- (0, import_wcwidth4.default)(stripAnsi(this.questionMessage)) / this.stdout.columns
994
+ stringLength(this.questionMessage) / this.stdout.columns
996
995
  );
997
996
  this.stdout.moveCursor(-this.stdout.columns, -(1 + questionLineCount));
998
997
  this.stdout.clearScreenDown();
999
998
  return;
1000
999
  }
1001
1000
  if (error) {
1002
- const linesToClear = Math.ceil((0, import_wcwidth4.default)(this.questionMessage) / this.stdout.columns) + 1;
1001
+ const linesToClear = Math.ceil(stringLength(this.questionMessage) / this.stdout.columns) + 1;
1003
1002
  this.stdout.moveCursor(0, -linesToClear);
1004
1003
  this.stdout.clearScreenDown();
1005
1004
  this.#showQuestion(error);
@@ -1014,15 +1013,15 @@ var MultiselectPrompt = class extends AbstractPrompt {
1014
1013
  });
1015
1014
  }
1016
1015
  #showQuestion(error = null) {
1017
- let hint = this.#showHint ? (0, import_node_util5.styleText)(
1016
+ let hint = this.#showHint ? (0, import_node_util7.styleText)(
1018
1017
  "gray",
1019
1018
  // eslint-disable-next-line @stylistic/max-len
1020
- `(Press ${(0, import_node_util5.styleText)("bold", "<Ctrl+A>")} to toggle all, ${(0, import_node_util5.styleText)("bold", "<Left/Right>")} to toggle, ${(0, import_node_util5.styleText)("bold", "<Return>")} to submit)`
1019
+ `(Press ${(0, import_node_util7.styleText)("bold", "<Ctrl+A>")} to toggle all, ${(0, import_node_util7.styleText)("bold", "<Left/Right>")} to toggle, ${(0, import_node_util7.styleText)("bold", "<Return>")} to submit)`
1021
1020
  ) : "";
1022
1021
  if (error) {
1023
- hint += `${hint.length > 0 ? " " : ""}${(0, import_node_util5.styleText)(["red", "bold"], `[${error}]`)}`;
1022
+ hint += `${hint.length > 0 ? " " : ""}${(0, import_node_util7.styleText)(["red", "bold"], `[${error}]`)}`;
1024
1023
  }
1025
- this.questionMessage = `${SYMBOLS.QuestionMark} ${(0, import_node_util5.styleText)("bold", this.message)}${hint.length > 0 ? ` ${hint}` : ""}`;
1024
+ this.questionMessage = `${SYMBOLS.QuestionMark} ${(0, import_node_util7.styleText)("bold", this.message)}${hint.length > 0 ? ` ${hint}` : ""}`;
1026
1025
  this.write(`${this.questionMessage}${import_node_os5.EOL}`);
1027
1026
  }
1028
1027
  };
package/dist/index.d.cts CHANGED
@@ -69,21 +69,21 @@ interface ConfirmOptions extends AbstractPromptOptions {
69
69
  initial?: boolean;
70
70
  }
71
71
 
72
- interface MultiselectOptions<T extends string = string> extends AbstractPromptOptions {
73
- choices: (Choice | T)[];
72
+ interface MultiselectOptions<T extends string> extends AbstractPromptOptions {
73
+ choices: (Choice<T> | T)[];
74
74
  maxVisible?: number;
75
- preSelectedChoices?: (Choice | string)[];
76
- validators?: PromptValidator[];
75
+ preSelectedChoices?: (Choice | T)[];
76
+ validators?: PromptValidator<T[]>[];
77
77
  autocomplete?: boolean;
78
78
  caseSensitive?: boolean;
79
79
  showHint?: boolean;
80
80
  }
81
81
 
82
- interface SelectOptions<T extends string = string> extends AbstractPromptOptions {
83
- choices: (Choice | T)[];
82
+ interface SelectOptions<T extends string> extends AbstractPromptOptions {
83
+ choices: (Choice<T> | T)[];
84
84
  maxVisible?: number;
85
- ignoreValues?: (string | number | boolean)[];
86
- validators?: PromptValidator[];
85
+ ignoreValues?: (T | number | boolean)[];
86
+ validators?: PromptValidator<T>[];
87
87
  autocomplete?: boolean;
88
88
  caseSensitive?: boolean;
89
89
  }
package/dist/index.d.ts CHANGED
@@ -69,21 +69,21 @@ interface ConfirmOptions extends AbstractPromptOptions {
69
69
  initial?: boolean;
70
70
  }
71
71
 
72
- interface MultiselectOptions<T extends string = string> extends AbstractPromptOptions {
73
- choices: (Choice | T)[];
72
+ interface MultiselectOptions<T extends string> extends AbstractPromptOptions {
73
+ choices: (Choice<T> | T)[];
74
74
  maxVisible?: number;
75
- preSelectedChoices?: (Choice | string)[];
76
- validators?: PromptValidator[];
75
+ preSelectedChoices?: (Choice | T)[];
76
+ validators?: PromptValidator<T[]>[];
77
77
  autocomplete?: boolean;
78
78
  caseSensitive?: boolean;
79
79
  showHint?: boolean;
80
80
  }
81
81
 
82
- interface SelectOptions<T extends string = string> extends AbstractPromptOptions {
83
- choices: (Choice | T)[];
82
+ interface SelectOptions<T extends string> extends AbstractPromptOptions {
83
+ choices: (Choice<T> | T)[];
84
84
  maxVisible?: number;
85
- ignoreValues?: (string | number | boolean)[];
86
- validators?: PromptValidator[];
85
+ ignoreValues?: (T | number | boolean)[];
86
+ validators?: PromptValidator<T>[];
87
87
  autocomplete?: boolean;
88
88
  caseSensitive?: boolean;
89
89
  }
package/dist/index.js CHANGED
@@ -1,34 +1,13 @@
1
1
  // src/prompts/question.ts
2
2
  import { EOL as EOL2 } from "node:os";
3
3
  import { styleText as styleText2 } from "node:util";
4
- import wcwidth from "@topcli/wcwidth";
5
4
 
6
5
  // src/prompts/abstract.ts
7
6
  import { EOL } from "node:os";
8
7
  import readline from "node:readline";
9
8
  import { Writable } from "node:stream";
10
9
  import EventEmitter from "node:events";
11
-
12
- // src/utils.ts
13
- import process2 from "node:process";
14
- var kAnsiRegex = ansiRegex();
15
- function ansiRegex({ onlyFirst = false } = {}) {
16
- const pattern = [
17
- // eslint-disable-next-line @stylistic/max-len
18
- "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
19
- "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))"
20
- ].join("|");
21
- return new RegExp(pattern, onlyFirst ? void 0 : "g");
22
- }
23
- function stripAnsi(string) {
24
- return string.replace(kAnsiRegex, "");
25
- }
26
- function isUnicodeSupported() {
27
- if (process2.platform !== "win32") {
28
- return process2.env.TERM !== "linux";
29
- }
30
- 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";
31
- }
10
+ import { stripVTControlCharacters } from "node:util";
32
11
 
33
12
  // src/prompt-agent.ts
34
13
  var kPrivateInstancier = Symbol("instancier");
@@ -152,7 +131,7 @@ var AbstractPrompt = class _AbstractPrompt extends EventEmitter {
152
131
  }
153
132
  }
154
133
  write(data) {
155
- const formattedData = stripAnsi(data).replace(EOL, "");
134
+ const formattedData = stripVTControlCharacters(data).replace(EOL, "");
156
135
  if (formattedData) {
157
136
  this.history.push(formattedData);
158
137
  }
@@ -163,7 +142,7 @@ var AbstractPrompt = class _AbstractPrompt extends EventEmitter {
163
142
  if (!lastLine) {
164
143
  return;
165
144
  }
166
- const lastLineRows = Math.ceil(stripAnsi(lastLine).length / this.stdout.columns);
145
+ const lastLineRows = Math.ceil(stripVTControlCharacters(lastLine).length / this.stdout.columns);
167
146
  this.stdout.moveCursor(-this.stdout.columns, -lastLineRows);
168
147
  this.stdout.clearScreenDown();
169
148
  }
@@ -175,6 +154,29 @@ var AbstractPrompt = class _AbstractPrompt extends EventEmitter {
175
154
  }
176
155
  };
177
156
 
157
+ // src/utils.ts
158
+ import process2 from "node:process";
159
+ import { stripVTControlCharacters as stripVTControlCharacters2 } from "node:util";
160
+ var kLenSegmenter = new Intl.Segmenter();
161
+ function isUnicodeSupported() {
162
+ if (process2.platform !== "win32") {
163
+ return process2.env.TERM !== "linux";
164
+ }
165
+ 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";
166
+ }
167
+ function stringLength(string) {
168
+ if (string === "") {
169
+ return 0;
170
+ }
171
+ let length = 0;
172
+ for (const _ of kLenSegmenter.segment(
173
+ stripVTControlCharacters2(string)
174
+ )) {
175
+ length++;
176
+ }
177
+ return length;
178
+ }
179
+
178
180
  // src/constants.ts
179
181
  import { styleText } from "node:util";
180
182
  var kMainSymbols = {
@@ -287,7 +289,7 @@ var QuestionPrompt = class extends AbstractPrompt {
287
289
  }
288
290
  #onQuestionAnswer() {
289
291
  const questionLineCount = Math.ceil(
290
- wcwidth(stripAnsi(this.#getQuestionQuery() + this.answer)) / this.stdout.columns
292
+ stringLength(this.#getQuestionQuery() + this.answer) / this.stdout.columns
291
293
  );
292
294
  this.stdout.moveCursor(-this.stdout.columns, -questionLineCount);
293
295
  this.stdout.clearScreenDown();
@@ -336,7 +338,6 @@ var QuestionPrompt = class extends AbstractPrompt {
336
338
  // src/prompts/confirm.ts
337
339
  import { EOL as EOL3 } from "node:os";
338
340
  import { styleText as styleText3 } from "node:util";
339
- import wcwidth2 from "@topcli/wcwidth";
340
341
  var kToggleKeys = /* @__PURE__ */ new Set([
341
342
  "left",
342
343
  "right",
@@ -386,7 +387,7 @@ var ConfirmPrompt = class extends AbstractPrompt {
386
387
  #onKeypress(resolve, _value, key) {
387
388
  this.stdout.moveCursor(
388
389
  -this.stdout.columns,
389
- -Math.floor(wcwidth2(stripAnsi(this.#getQuestionQuery())) / this.stdout.columns)
390
+ -Math.floor(stringLength(this.#getQuestionQuery()) / this.stdout.columns)
390
391
  );
391
392
  this.stdout.clearScreenDown();
392
393
  if (key.name === "return") {
@@ -420,7 +421,7 @@ var ConfirmPrompt = class extends AbstractPrompt {
420
421
  this.clearLastLine();
421
422
  this.stdout.moveCursor(
422
423
  -this.stdout.columns,
423
- -Math.floor(wcwidth2(stripAnsi(this.#getQuestionQuery())) / this.stdout.columns)
424
+ -Math.floor(stringLength(this.#getQuestionQuery()) / this.stdout.columns)
424
425
  );
425
426
  this.stdout.clearScreenDown();
426
427
  this.write(`${this.selectedValue ? SYMBOLS.Tick : SYMBOLS.Cross} ${styleText3("bold", this.message)}${EOL3}`);
@@ -460,7 +461,6 @@ var ConfirmPrompt = class extends AbstractPrompt {
460
461
  // src/prompts/select.ts
461
462
  import { EOL as EOL4 } from "node:os";
462
463
  import { styleText as styleText4 } from "node:util";
463
- import wcwidth3 from "@topcli/wcwidth";
464
464
  var kRequiredChoiceProperties = ["label", "value"];
465
465
  var SelectPrompt = class extends AbstractPrompt {
466
466
  #boundExitEvent = () => void 0;
@@ -571,10 +571,10 @@ var SelectPrompt = class extends AbstractPrompt {
571
571
  this.write(str);
572
572
  }
573
573
  }
574
- #showAnsweredQuestion(choice) {
575
- const symbolPrefix = choice === "" ? SYMBOLS.Cross : SYMBOLS.Tick;
574
+ #showAnsweredQuestion(label) {
575
+ const symbolPrefix = label === "" ? SYMBOLS.Cross : SYMBOLS.Tick;
576
576
  const prefix = `${symbolPrefix} ${styleText4("bold", this.message)} ${SYMBOLS.Pointer}`;
577
- const formattedChoice = styleText4("yellow", typeof choice === "string" ? choice : choice.label);
577
+ const formattedChoice = styleText4("yellow", label);
578
578
  this.write(`${prefix} ${formattedChoice}${EOL4}`);
579
579
  }
580
580
  #onProcessExit() {
@@ -656,7 +656,7 @@ var SelectPrompt = class extends AbstractPrompt {
656
656
  }
657
657
  if (this.options.autocomplete) {
658
658
  let linesToClear2 = Math.ceil(
659
- wcwidth3(`${SYMBOLS.Pointer} ${this.autocompleteValue}`) / this.stdout.columns
659
+ stringLength(`${SYMBOLS.Pointer} ${this.autocompleteValue}`) / this.stdout.columns
660
660
  );
661
661
  while (linesToClear2 > 0) {
662
662
  this.clearLastLine();
@@ -666,14 +666,14 @@ var SelectPrompt = class extends AbstractPrompt {
666
666
  }
667
667
  if (clearRender) {
668
668
  const questionLineCount = Math.ceil(
669
- wcwidth3(stripAnsi(this.questionMessage)) / this.stdout.columns
669
+ stringLength(this.questionMessage) / this.stdout.columns
670
670
  );
671
671
  this.stdout.moveCursor(-this.stdout.columns, -(1 + questionLineCount));
672
672
  this.stdout.clearScreenDown();
673
673
  return;
674
674
  }
675
675
  if (error) {
676
- const linesToClear = Math.ceil(wcwidth3(this.questionMessage) / this.stdout.columns) + 1;
676
+ const linesToClear = Math.ceil(stringLength(this.questionMessage) / this.stdout.columns) + 1;
677
677
  this.stdout.moveCursor(0, -linesToClear);
678
678
  this.stdout.clearScreenDown();
679
679
  this.#showQuestion(error);
@@ -700,7 +700,6 @@ var SelectPrompt = class extends AbstractPrompt {
700
700
  // src/prompts/multiselect.ts
701
701
  import { EOL as EOL5 } from "node:os";
702
702
  import { styleText as styleText5 } from "node:util";
703
- import wcwidth4 from "@topcli/wcwidth";
704
703
  var kRequiredChoiceProperties2 = ["label", "value"];
705
704
  var MultiselectPrompt = class extends AbstractPrompt {
706
705
  #boundExitEvent = () => void 0;
@@ -941,7 +940,7 @@ var MultiselectPrompt = class extends AbstractPrompt {
941
940
  }
942
941
  if (this.options.autocomplete) {
943
942
  let linesToClear2 = Math.ceil(
944
- wcwidth4(`${SYMBOLS.Pointer} ${this.autocompleteValue}`) / this.stdout.columns
943
+ stringLength(`${SYMBOLS.Pointer} ${this.autocompleteValue}`) / this.stdout.columns
945
944
  );
946
945
  while (linesToClear2 > 0) {
947
946
  this.clearLastLine();
@@ -951,14 +950,14 @@ var MultiselectPrompt = class extends AbstractPrompt {
951
950
  }
952
951
  if (clearRender) {
953
952
  const questionLineCount = Math.ceil(
954
- wcwidth4(stripAnsi(this.questionMessage)) / this.stdout.columns
953
+ stringLength(this.questionMessage) / this.stdout.columns
955
954
  );
956
955
  this.stdout.moveCursor(-this.stdout.columns, -(1 + questionLineCount));
957
956
  this.stdout.clearScreenDown();
958
957
  return;
959
958
  }
960
959
  if (error) {
961
- const linesToClear = Math.ceil(wcwidth4(this.questionMessage) / this.stdout.columns) + 1;
960
+ const linesToClear = Math.ceil(stringLength(this.questionMessage) / this.stdout.columns) + 1;
962
961
  this.stdout.moveCursor(0, -linesToClear);
963
962
  this.stdout.clearScreenDown();
964
963
  this.#showQuestion(error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topcli/prompts",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "Node.js user input library for command-line interfaces.",
5
5
  "scripts": {
6
6
  "build": "tsup index.ts --format cjs,esm --dts --clean",
@@ -33,16 +33,13 @@
33
33
  "devDependencies": {
34
34
  "@openally/config.eslint": "^2.0.0",
35
35
  "@openally/config.typescript": "^1.0.3",
36
- "@types/node": "^22.10.5",
36
+ "@types/node": "^24.0.3",
37
37
  "c8": "^10.1.3",
38
38
  "glob": "^11.0.0",
39
39
  "tsup": "^8.3.5",
40
40
  "tsx": "^4.19.2",
41
41
  "typescript": "^5.7.2"
42
42
  },
43
- "dependencies": {
44
- "@topcli/wcwidth": "^1.0.1"
45
- },
46
43
  "engines": {
47
44
  "node": "^20.12.0 || >=21.7.0"
48
45
  },