@topcli/prompts 1.5.1 → 1.7.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
@@ -12,7 +12,8 @@ Node.js user prompt library for command-line interfaces.
12
12
 
13
13
  ## Getting Started
14
14
 
15
- > **Note** This package is ESM only.
15
+ > [!CAUTION]
16
+ > This package is ESM only.
16
17
 
17
18
  This package is available in the Node Package Repository and can be easily installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or [yarn](https://yarnpkg.com).
18
19
 
@@ -94,7 +95,7 @@ Use `ignoreValues` to skip result render & clear lines after a selected one.
94
95
  multiselect(message: string, options: MultiselectOptions): Promise<[string]>
95
96
  ```
96
97
 
97
- Scrollable multiselect depending `maxVisible` (default `8`).
98
+ Scrollable multiselect depending `maxVisible` (default `8`).<br>
98
99
  Use `preSelectedChoices` to pre-select choices.
99
100
 
100
101
  Use `validators` to handle user input.
@@ -108,6 +109,10 @@ const os = await multiselect('Choose OS', {
108
109
  });
109
110
  ```
110
111
 
112
+ Use `autocomplete` to allow filtered choices. This can be usefull for a large list of choices.
113
+
114
+ Use `caseSensitive` to make autocomplete filters case sensitive. Default `false`
115
+
111
116
  ### `confirm()`
112
117
 
113
118
  ```ts
@@ -134,7 +139,7 @@ assert.equal(input, "John");
134
139
  > - When using `question()`, `validators` functions will not be executed.
135
140
  > - When using `select()`, the answer can be different from the available choices.
136
141
  > - When using `confirm()`, the answer can be any type other than boolean.
137
- > - etc
142
+ > - etc<br>
138
143
  > **Use with caution**
139
144
 
140
145
  ## Interfaces
@@ -177,6 +182,8 @@ export interface MultiselectOptions extends SharedOptions {
177
182
  maxVisible?: number;
178
183
  preSelectedChoices?: (Choice | string)[];
179
184
  validators?: Validator[];
185
+ autocomplete?: boolean;
186
+ caseSensitive?: boolean;
180
187
  }
181
188
 
182
189
  export interface ConfirmOptions extends SharedOptions {
@@ -193,7 +200,7 @@ export interface ConfirmOptions extends SharedOptions {
193
200
  <tbody>
194
201
  <tr>
195
202
  <td align="center" valign="top" width="14.28%"><a href="https://github.com/PierreDemailly"><img src="https://avatars.githubusercontent.com/u/39910767?v=4?s=100" width="100px;" alt="PierreDemailly"/><br /><sub><b>PierreDemailly</b></sub></a><br /><a href="https://github.com/TopCli/prompts/commits?author=PierreDemailly" title="Code">💻</a> <a href="https://github.com/TopCli/prompts/commits?author=PierreDemailly" title="Tests">⚠️</a></td>
196
- <td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/thomas-gentilhomme/"><img src="https://avatars.githubusercontent.com/u/4438263?v=4?s=100" width="100px;" alt="Gentilhomme"/><br /><sub><b>Gentilhomme</b></sub></a><br /><a href="https://github.com/TopCli/prompts/pulls?q=is%3Apr+reviewed-by%3Afraxken" title="Reviewed Pull Requests">👀</a></td>
203
+ <td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/thomas-gentilhomme/"><img src="https://avatars.githubusercontent.com/u/4438263?v=4?s=100" width="100px;" alt="Gentilhomme"/><br /><sub><b>Gentilhomme</b></sub></a><br /><a href="https://github.com/TopCli/prompts/pulls?q=is%3Apr+reviewed-by%3Afraxken" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/TopCli/prompts/commits?author=fraxken" title="Code">💻</a> <a href="https://github.com/TopCli/prompts/commits?author=fraxken" title="Documentation">📖</a></td>
197
204
  <td align="center" valign="top" width="14.28%"><a href="http://tonygo.dev"><img src="https://avatars0.githubusercontent.com/u/22824417?v=4?s=100" width="100px;" alt="Tony Gorez"/><br /><sub><b>Tony Gorez</b></sub></a><br /><a href="https://github.com/TopCli/prompts/pulls?q=is%3Apr+reviewed-by%3Atony-go" title="Reviewed Pull Requests">👀</a></td>
198
205
  <td align="center" valign="top" width="14.28%"><a href="http://sofiand.github.io/portfolio-client/"><img src="https://avatars.githubusercontent.com/u/39944043?v=4?s=100" width="100px;" alt="Yefis"/><br /><sub><b>Yefis</b></sub></a><br /><a href="https://github.com/TopCli/prompts/commits?author=SofianD" title="Code">💻</a> <a href="https://github.com/TopCli/prompts/commits?author=SofianD" title="Documentation">📖</a></td>
199
206
  <td align="center" valign="top" width="14.28%"><a href="http://justie.dev"><img src="https://avatars.githubusercontent.com/u/7118300?v=4?s=100" width="100px;" alt="Ben"/><br /><sub><b>Ben</b></sub></a><br /><a href="https://github.com/TopCli/prompts/commits?author=JUSTIVE" title="Documentation">📖</a> <a href="#maintenance-JUSTIVE" title="Maintenance">🚧</a></td>
package/index.d.ts CHANGED
@@ -35,6 +35,8 @@ export interface MultiselectOptions extends SharedOptions {
35
35
  maxVisible?: number;
36
36
  preSelectedChoices?: (Choice | string)[];
37
37
  validators?: Validator[];
38
+ autocomplete?: boolean;
39
+ caseSensitive?: boolean;
38
40
  }
39
41
 
40
42
  export interface ConfirmOptions extends SharedOptions {
package/index.js CHANGED
@@ -1,31 +1,28 @@
1
1
  // Import Internal Dependencies
2
- import { ConfirmPrompt } from "./src/confirm-prompt.js";
3
- import { SelectPrompt } from "./src/select-prompt.js";
4
- import { QuestionPrompt } from "./src/question-prompt.js";
2
+ import * as prompts from "./src/prompts/index.js";
5
3
  import { required } from "./src/validators.js";
6
4
  import { PromptAgent } from "./src/prompt-agent.js";
7
- import { MultiselectPrompt } from "./src/multiselect-prompt.js";
8
5
 
9
6
  export async function question(message, options = {}) {
10
- const questionPrompt = new QuestionPrompt(message, options);
7
+ const questionPrompt = new prompts.QuestionPrompt(message, options);
11
8
 
12
9
  return questionPrompt.question();
13
10
  }
14
11
 
15
12
  export async function select(message, options) {
16
- const selectPrompt = new SelectPrompt(message, options);
13
+ const selectPrompt = new prompts.SelectPrompt(message, options);
17
14
 
18
15
  return selectPrompt.select();
19
16
  }
20
17
 
21
18
  export async function confirm(message, options) {
22
- const confirmPrompt = new ConfirmPrompt(message, options);
19
+ const confirmPrompt = new prompts.ConfirmPrompt(message, options);
23
20
 
24
21
  return confirmPrompt.confirm();
25
22
  }
26
23
 
27
24
  export async function multiselect(message, options) {
28
- const multiselectPrompt = new MultiselectPrompt(message, options);
25
+ const multiselectPrompt = new prompts.MultiselectPrompt(message, options);
29
26
 
30
27
  return multiselectPrompt.multiselect();
31
28
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topcli/prompts",
3
- "version": "1.5.1",
3
+ "version": "1.7.0",
4
4
  "description": "Node.js user input library for command-line interfaces.",
5
5
  "scripts": {
6
6
  "test": "node --no-warnings=ExperimentalWarning --loader=esmock --test test/",
@@ -31,10 +31,7 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@topcli/wcwidth": "^1.0.1",
34
- "is-ci": "^3.0.1",
35
- "is-unicode-supported": "^2.0.0",
36
- "kleur": "^4.1.5",
37
- "strip-ansi": "^7.1.0"
34
+ "kleur": "^4.1.5"
38
35
  },
39
36
  "engines": {
40
37
  "node": ">=14"
package/src/constants.js CHANGED
@@ -1,7 +1,8 @@
1
1
  // Import Third-party Dependencies
2
2
  import kleur from "kleur";
3
- import isUnicodeSupported from "is-unicode-supported";
4
- import isCI from "is-ci";
3
+
4
+ // Import Internal Dependencies
5
+ import { isUnicodeSupported } from "./utils.js";
5
6
 
6
7
  const kMainSymbols = {
7
8
  tick: "✔",
@@ -21,7 +22,7 @@ const kFallbackSymbols = {
21
22
  active: "(+)",
22
23
  inactive: "(-)"
23
24
  };
24
- const kSymbols = isUnicodeSupported() || isCI ? kMainSymbols : kFallbackSymbols;
25
+ const kSymbols = isUnicodeSupported() || process.env.CI ? kMainSymbols : kFallbackSymbols;
25
26
  const kPointer = kleur.gray(kSymbols.pointer);
26
27
 
27
28
  export const SYMBOLS = {
@@ -3,11 +3,9 @@ import { EOL } from "node:os";
3
3
  import { createInterface } from "node:readline";
4
4
  import { Writable } from "node:stream";
5
5
 
6
- // Import Third-party Dependencies
7
- import stripAnsi from "strip-ansi";
8
-
9
6
  // Import Internal Dependencies
10
- import { PromptAgent } from "./prompt-agent.js";
7
+ import { stripAnsi } from "../utils.js";
8
+ import { PromptAgent } from "../prompt-agent.js";
11
9
 
12
10
  export class AbstractPrompt {
13
11
  constructor(message, input = process.stdin, output = process.stdout) {
@@ -3,12 +3,12 @@ import { EOL } from "node:os";
3
3
 
4
4
  // Import Third-party Dependencies
5
5
  import kleur from "kleur";
6
- import stripAnsi from "strip-ansi";
7
6
  import wcwidth from "@topcli/wcwidth";
8
7
 
9
8
  // Import Internal Dependencies
10
- import { AbstractPrompt } from "./abstract-prompt.js";
11
- import { SYMBOLS } from "./constants.js";
9
+ import { AbstractPrompt } from "./abstract.js";
10
+ import { stripAnsi } from "../utils.js";
11
+ import { SYMBOLS } from "../constants.js";
12
12
 
13
13
  // CONSTANTS
14
14
  const kToggleKeys = new Set([
@@ -0,0 +1,12 @@
1
+ // Import Internal Dependencies
2
+ import { QuestionPrompt } from "./question.js";
3
+ import { ConfirmPrompt } from "./confirm.js";
4
+ import { SelectPrompt } from "./select.js";
5
+ import { MultiselectPrompt } from "./multiselect.js";
6
+
7
+ export {
8
+ QuestionPrompt,
9
+ ConfirmPrompt,
10
+ SelectPrompt,
11
+ MultiselectPrompt
12
+ };
@@ -3,12 +3,15 @@ import { EOL } from "node:os";
3
3
 
4
4
  // Import Third-party Dependencies
5
5
  import kleur from "kleur";
6
- import stripAnsi from "strip-ansi";
7
6
  import wcwidth from "@topcli/wcwidth";
8
7
 
9
8
  // Import Internal Dependencies
10
- import { AbstractPrompt } from "./abstract-prompt.js";
11
- import { SYMBOLS } from "./constants.js";
9
+ import { AbstractPrompt } from "./abstract.js";
10
+ import { stripAnsi } from "../utils.js";
11
+ import { SYMBOLS } from "../constants.js";
12
+
13
+ // CONSTANTS
14
+ const kRequiredChoiceProperties = ["label", "value"];
12
15
 
13
16
  export class MultiselectPrompt extends AbstractPrompt {
14
17
  #boundExitEvent = () => void 0;
@@ -18,11 +21,54 @@ export class MultiselectPrompt extends AbstractPrompt {
18
21
  activeIndex = 0;
19
22
  selectedIndexes = [];
20
23
  questionMessage;
24
+ autocompleteValue = "";
21
25
 
22
26
  get choices() {
23
27
  return this.options.choices;
24
28
  }
25
29
 
30
+ get filteredChoices() {
31
+ if (!(this.options.autocomplete && this.autocompleteValue.length > 0)) {
32
+ return this.choices;
33
+ }
34
+
35
+ const isCaseSensitive = this.options.caseSensitive;
36
+ const autocompleteValue = isCaseSensitive ? this.autocompleteValue : this.autocompleteValue.toLowerCase();
37
+
38
+ return this.choices.filter((choice) => this.#filterChoice(choice, autocompleteValue, isCaseSensitive));
39
+ }
40
+
41
+ #filterChoice(choice, autocompleteValue, isCaseSensitive) {
42
+ // eslint-disable-next-line no-nested-ternary
43
+ const choiceValue = typeof choice === "string" ?
44
+ (isCaseSensitive ? choice : choice.toLowerCase()) :
45
+ (isCaseSensitive ? choice.label : choice.label.toLowerCase());
46
+
47
+ if (autocompleteValue.includes(" ")) {
48
+ return this.#filterMultipleWords(choiceValue, autocompleteValue, isCaseSensitive);
49
+ }
50
+
51
+ return choiceValue.includes(autocompleteValue);
52
+ }
53
+
54
+ #filterMultipleWords(choiceValue, autocompleteValue, isCaseSensitive) {
55
+ return autocompleteValue.split(" ").every((word) => {
56
+ const wordValue = isCaseSensitive ? word : word.toLowerCase();
57
+
58
+ return choiceValue.includes(wordValue) || choiceValue.includes(autocompleteValue);
59
+ });
60
+ }
61
+
62
+ get longestChoice() {
63
+ return Math.max(...this.filteredChoices.map((choice) => {
64
+ if (typeof choice === "string") {
65
+ return choice.length;
66
+ }
67
+
68
+ return choice.label.length;
69
+ }));
70
+ }
71
+
26
72
  constructor(message, options) {
27
73
  const {
28
74
  stdin = process.stdin,
@@ -46,31 +92,27 @@ export class MultiselectPrompt extends AbstractPrompt {
46
92
  throw new TypeError("Missing required param: choices");
47
93
  }
48
94
 
49
- this.longestChoice = Math.max(...choices.map((choice) => {
95
+ this.#validators = validators;
96
+
97
+ for (const choice of choices) {
50
98
  if (typeof choice === "string") {
51
- return choice.length;
99
+ continue;
52
100
  }
53
101
 
54
- const kRequiredChoiceProperties = ["label", "value"];
55
-
56
102
  for (const prop of kRequiredChoiceProperties) {
57
103
  if (!choice[prop]) {
58
104
  this.destroy();
59
105
  throw new TypeError(`Missing ${prop} for choice ${JSON.stringify(choice)}`);
60
106
  }
61
107
  }
62
-
63
- return choice.label.length;
64
- }));
65
-
66
- this.#validators = validators;
108
+ }
67
109
 
68
110
  if (!preSelectedChoices) {
69
111
  return;
70
112
  }
71
113
 
72
114
  for (const choice of preSelectedChoices) {
73
- const choiceIndex = this.choices.findIndex((item) => {
115
+ const choiceIndex = this.filteredChoices.findIndex((item) => {
74
116
  if (typeof item === "string") {
75
117
  return item === choice;
76
118
  }
@@ -79,6 +121,7 @@ export class MultiselectPrompt extends AbstractPrompt {
79
121
  });
80
122
 
81
123
  if (choiceIndex === -1) {
124
+ this.destroy();
82
125
  throw new Error(`Invalid pre-selected choice: ${choice.value ?? choice}`);
83
126
  }
84
127
 
@@ -87,7 +130,7 @@ export class MultiselectPrompt extends AbstractPrompt {
87
130
  }
88
131
 
89
132
  #getFormattedChoice(choiceIndex) {
90
- const choice = this.choices[choiceIndex];
133
+ const choice = this.filteredChoices[choiceIndex];
91
134
 
92
135
  if (typeof choice === "string") {
93
136
  return { value: choice, label: choice };
@@ -98,12 +141,12 @@ export class MultiselectPrompt extends AbstractPrompt {
98
141
 
99
142
  #getVisibleChoices() {
100
143
  const maxVisible = this.options.maxVisible || 8;
101
- let startIndex = Math.min(this.choices.length - maxVisible, this.activeIndex - Math.floor(maxVisible / 2));
144
+ let startIndex = Math.min(this.filteredChoices.length - maxVisible, this.activeIndex - Math.floor(maxVisible / 2));
102
145
  if (startIndex < 0) {
103
146
  startIndex = 0;
104
147
  }
105
148
 
106
- const endIndex = Math.min(startIndex + maxVisible, this.choices.length);
149
+ const endIndex = Math.min(startIndex + maxVisible, this.filteredChoices.length);
107
150
 
108
151
  return { startIndex, endIndex };
109
152
  }
@@ -112,12 +155,15 @@ export class MultiselectPrompt extends AbstractPrompt {
112
155
  const { startIndex, endIndex } = this.#getVisibleChoices();
113
156
  this.lastRender = { startIndex, endIndex };
114
157
 
158
+ if (this.options.autocomplete) {
159
+ this.write(`${SYMBOLS.Pointer} ${this.autocompleteValue}${EOL}`);
160
+ }
115
161
  for (let choiceIndex = startIndex; choiceIndex < endIndex; choiceIndex++) {
116
162
  const choice = this.#getFormattedChoice(choiceIndex);
117
163
  const isChoiceActive = choiceIndex === this.activeIndex;
118
164
  const isChoiceSelected = this.selectedIndexes.includes(choiceIndex);
119
165
  const showPreviousChoicesArrow = startIndex > 0 && choiceIndex === startIndex;
120
- const showNextChoicesArrow = endIndex < this.choices.length && choiceIndex === endIndex - 1;
166
+ const showNextChoicesArrow = endIndex < this.filteredChoices.length && choiceIndex === endIndex - 1;
121
167
 
122
168
  let prefixArrow = " ";
123
169
  if (showPreviousChoicesArrow) {
@@ -156,34 +202,30 @@ export class MultiselectPrompt extends AbstractPrompt {
156
202
 
157
203
  #onKeypress(...args) {
158
204
  const [resolve, render, _, key] = args;
159
-
160
205
  if (key.name === "up") {
161
- this.activeIndex = this.activeIndex === 0 ? this.choices.length - 1 : this.activeIndex - 1;
206
+ this.activeIndex = this.activeIndex === 0 ? this.filteredChoices.length - 1 : this.activeIndex - 1;
162
207
  render();
163
208
  }
164
209
  else if (key.name === "down") {
165
- this.activeIndex = this.activeIndex === this.choices.length - 1 ? 0 : this.activeIndex + 1;
210
+ this.activeIndex = this.activeIndex === this.filteredChoices.length - 1 ? 0 : this.activeIndex + 1;
166
211
  render();
167
212
  }
168
- else if (key.name === "a") {
169
- this.selectedIndexes = this.selectedIndexes.length === this.choices.length ? [] : this.choices.map((_, index) => index);
213
+ else if (key.ctrl && key.name === "a") {
214
+ // eslint-disable-next-line max-len
215
+ this.selectedIndexes = this.selectedIndexes.length === this.filteredChoices.length ? [] : this.filteredChoices.map((_, index) => index);
170
216
  render();
171
217
  }
172
- else if (key.name === "space") {
173
- const isChoiceSelected = this.selectedIndexes.includes(this.activeIndex);
174
-
175
- if (isChoiceSelected) {
176
- this.selectedIndexes = this.selectedIndexes.filter((index) => index !== this.activeIndex);
177
- }
178
- else {
179
- this.selectedIndexes.push(this.activeIndex);
180
- }
181
-
218
+ else if (key.name === "right") {
219
+ this.selectedIndexes.push(this.activeIndex);
220
+ render();
221
+ }
222
+ else if (key.name === "left") {
223
+ this.selectedIndexes = this.selectedIndexes.filter((index) => index !== this.activeIndex);
182
224
  render();
183
225
  }
184
226
  else if (key.name === "return") {
185
- const labels = this.selectedIndexes.map((index) => this.choices[index].label ?? this.choices[index]);
186
- const values = this.selectedIndexes.map((index) => this.choices[index].value ?? this.choices[index]);
227
+ const labels = this.selectedIndexes.map((index) => this.filteredChoices[index].label ?? this.filteredChoices[index]);
228
+ const values = this.selectedIndexes.map((index) => this.filteredChoices[index].value ?? this.filteredChoices[index]);
187
229
 
188
230
  for (const validator of this.#validators) {
189
231
  if (!validator.validate(values)) {
@@ -207,6 +249,17 @@ export class MultiselectPrompt extends AbstractPrompt {
207
249
  resolve(values);
208
250
  }
209
251
  else {
252
+ if (!key.ctrl && this.options.autocomplete) {
253
+ // reset selected choices when user type
254
+ this.selectedIndexes = [];
255
+ this.activeIndex = 0;
256
+ if (key.name === "backspace" && this.autocompleteValue.length > 0) {
257
+ this.autocompleteValue = this.autocompleteValue.slice(0, -1);
258
+ }
259
+ else if (key.name !== "backspace") {
260
+ this.autocompleteValue += key.sequence;
261
+ }
262
+ }
210
263
  render();
211
264
  }
212
265
  }
@@ -236,6 +289,15 @@ export class MultiselectPrompt extends AbstractPrompt {
236
289
  this.clearLastLine();
237
290
  linesToClear--;
238
291
  }
292
+ if (this.options.autocomplete) {
293
+ let linesToClear = Math.ceil(
294
+ wcwidth(`${SYMBOLS.Pointer} ${this.autocompleteValue}`) / this.stdout.columns
295
+ );
296
+ while (linesToClear > 0) {
297
+ this.clearLastLine();
298
+ linesToClear--;
299
+ }
300
+ }
239
301
  }
240
302
 
241
303
  if (clearRender) {
@@ -249,7 +311,8 @@ export class MultiselectPrompt extends AbstractPrompt {
249
311
  }
250
312
 
251
313
  if (error) {
252
- this.stdout.moveCursor(0, -2);
314
+ const linesToClear = Math.ceil(wcwidth(this.questionMessage) / this.stdout.columns) + 1;
315
+ this.stdout.moveCursor(0, -linesToClear);
253
316
  this.stdout.clearScreenDown();
254
317
  this.#showQuestion(error);
255
318
  }
@@ -270,7 +333,8 @@ export class MultiselectPrompt extends AbstractPrompt {
270
333
 
271
334
  #showQuestion(error = null) {
272
335
  let hint = kleur.gray(
273
- `(Press ${kleur.bold("<a>")} to toggle all, ${kleur.bold("<space>")} to select, ${kleur.bold("<return>")} to submit)`
336
+ // eslint-disable-next-line max-len
337
+ `(Press ${kleur.bold("<Ctrl+A>")} to toggle all, ${kleur.bold("<Ctrl+Space>")} to select, ${kleur.bold("<Left/Right>")} to toggle, ${kleur.bold("<Return>")} to submit)`
274
338
  );
275
339
  if (error) {
276
340
  hint += ` ${kleur.red().bold(`[${error}]`)}`;
@@ -3,12 +3,12 @@ import { EOL } from "node:os";
3
3
 
4
4
  // Import Third-party Dependencies
5
5
  import kleur from "kleur";
6
- import stripAnsi from "strip-ansi";
7
6
  import wcwidth from "@topcli/wcwidth";
8
7
 
9
8
  // Import Internal Dependencies
10
- import { AbstractPrompt } from "./abstract-prompt.js";
11
- import { SYMBOLS } from "./constants.js";
9
+ import { AbstractPrompt } from "./abstract.js";
10
+ import { stripAnsi } from "../utils.js";
11
+ import { SYMBOLS } from "../constants.js";
12
12
 
13
13
  export class QuestionPrompt extends AbstractPrompt {
14
14
  #validators;
@@ -3,12 +3,12 @@ import { EOL } from "node:os";
3
3
 
4
4
  // Import Third-party Dependencies
5
5
  import kleur from "kleur";
6
- import stripAnsi from "strip-ansi";
7
6
  import wcwidth from "@topcli/wcwidth";
8
7
 
9
8
  // Import Internal Dependencies
10
- import { AbstractPrompt } from "./abstract-prompt.js";
11
- import { SYMBOLS } from "./constants.js";
9
+ import { AbstractPrompt } from "./abstract.js";
10
+ import { stripAnsi } from "../utils.js";
11
+ import { SYMBOLS } from "../constants.js";
12
12
 
13
13
  export class SelectPrompt extends AbstractPrompt {
14
14
  #boundExitEvent = () => void 0;
package/src/utils.js ADDED
@@ -0,0 +1,43 @@
1
+ import process from "node:process";
2
+
3
+ // CONSTANTS
4
+ const kAnsiRegex = ansiRegex();
5
+
6
+ /**
7
+ * @see https://github.com/chalk/ansi-regex
8
+ */
9
+ export function ansiRegex({onlyFirst = false} = {}) {
10
+ const pattern = [
11
+ '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
12
+ '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))'
13
+ ].join('|');
14
+
15
+ return new RegExp(pattern, onlyFirst ? undefined : 'g');
16
+ }
17
+
18
+ /**
19
+ * @param {!string} string
20
+ * @returns {string}
21
+ */
22
+ export function stripAnsi(string) {
23
+ return string.replace(kAnsiRegex, "");
24
+ }
25
+
26
+ /**
27
+ * @see https://github.com/sindresorhus/is-unicode-supported
28
+ * @returns {boolean}
29
+ */
30
+ export function isUnicodeSupported() {
31
+ if (process.platform !== 'win32') {
32
+ return process.env.TERM !== 'linux'; // Linux console (kernel)
33
+ }
34
+
35
+ return Boolean(process.env.WT_SESSION) // Windows Terminal
36
+ || Boolean(process.env.TERMINUS_SUBLIME) // Terminus (<0.2.27)
37
+ || process.env.ConEmuTask === '{cmd::Cmder}' // ConEmu and cmder
38
+ || process.env.TERM_PROGRAM === 'Terminus-Sublime'
39
+ || process.env.TERM_PROGRAM === 'vscode'
40
+ || process.env.TERM === 'xterm-256color'
41
+ || process.env.TERM === 'alacritty'
42
+ || process.env.TERMINAL_EMULATOR === 'JetBrains-JediTerm';
43
+ }