@reliverse/rempts 1.6.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.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +408 -0
  3. package/bin/core-impl/anykey/anykey-mod.d.ts +12 -0
  4. package/bin/core-impl/anykey/anykey-mod.js +125 -0
  5. package/bin/core-impl/date/date.d.ts +2 -0
  6. package/bin/core-impl/date/date.js +236 -0
  7. package/bin/core-impl/editor/editor-mod.d.ts +25 -0
  8. package/bin/core-impl/editor/editor-mod.js +897 -0
  9. package/bin/core-impl/figures/figures-mod.d.ts +462 -0
  10. package/bin/core-impl/figures/figures-mod.js +286 -0
  11. package/bin/core-impl/figures/figures.test.d.ts +1 -0
  12. package/bin/core-impl/figures/figures.test.js +474 -0
  13. package/bin/core-impl/input/confirm-prompt.d.ts +5 -0
  14. package/bin/core-impl/input/confirm-prompt.js +173 -0
  15. package/bin/core-impl/input/input-prompt.d.ts +16 -0
  16. package/bin/core-impl/input/input-prompt.js +370 -0
  17. package/bin/core-impl/launcher/deprecated/_parser.ts.txt +167 -0
  18. package/bin/core-impl/launcher/deprecated/_utils.ts.txt +41 -0
  19. package/bin/core-impl/launcher/deprecated/args.ts.txt +108 -0
  20. package/bin/core-impl/launcher/deprecated/command.ts.txt +95 -0
  21. package/bin/core-impl/launcher/deprecated/launcher-mod.ts.txt +50 -0
  22. package/bin/core-impl/launcher/deprecated/usage.ts.txt +157 -0
  23. package/bin/core-impl/launcher/launcher-mod.d.ts +87 -0
  24. package/bin/core-impl/launcher/launcher-mod.js +364 -0
  25. package/bin/core-impl/msg-fmt/colors.d.ts +30 -0
  26. package/bin/core-impl/msg-fmt/colors.js +42 -0
  27. package/bin/core-impl/msg-fmt/logger.d.ts +17 -0
  28. package/bin/core-impl/msg-fmt/logger.js +106 -0
  29. package/bin/core-impl/msg-fmt/mapping.d.ts +3 -0
  30. package/bin/core-impl/msg-fmt/mapping.js +49 -0
  31. package/bin/core-impl/msg-fmt/messages.d.ts +35 -0
  32. package/bin/core-impl/msg-fmt/messages.js +316 -0
  33. package/bin/core-impl/msg-fmt/terminal.d.ts +15 -0
  34. package/bin/core-impl/msg-fmt/terminal.js +60 -0
  35. package/bin/core-impl/msg-fmt/variants.d.ts +11 -0
  36. package/bin/core-impl/msg-fmt/variants.js +52 -0
  37. package/bin/core-impl/next-steps/next-steps.d.ts +14 -0
  38. package/bin/core-impl/next-steps/next-steps.js +24 -0
  39. package/bin/core-impl/number/number-mod.d.ts +28 -0
  40. package/bin/core-impl/number/number-mod.js +197 -0
  41. package/bin/core-impl/results/results.d.ts +7 -0
  42. package/bin/core-impl/results/results.js +27 -0
  43. package/bin/core-impl/select/multiselect-prompt.d.ts +2 -0
  44. package/bin/core-impl/select/multiselect-prompt.js +342 -0
  45. package/bin/core-impl/select/nummultiselect-prompt.d.ts +6 -0
  46. package/bin/core-impl/select/nummultiselect-prompt.js +105 -0
  47. package/bin/core-impl/select/numselect-prompt.d.ts +7 -0
  48. package/bin/core-impl/select/numselect-prompt.js +115 -0
  49. package/bin/core-impl/select/select-prompt.d.ts +33 -0
  50. package/bin/core-impl/select/select-prompt.js +303 -0
  51. package/bin/core-impl/select/toggle-prompt.d.ts +5 -0
  52. package/bin/core-impl/select/toggle-prompt.js +209 -0
  53. package/bin/core-impl/st-end/end.d.ts +2 -0
  54. package/bin/core-impl/st-end/end.js +42 -0
  55. package/bin/core-impl/st-end/start.d.ts +17 -0
  56. package/bin/core-impl/st-end/start.js +67 -0
  57. package/bin/core-impl/task/progress.d.ts +2 -0
  58. package/bin/core-impl/task/progress.js +57 -0
  59. package/bin/core-impl/task/spinner.d.ts +15 -0
  60. package/bin/core-impl/task/spinner.js +110 -0
  61. package/bin/core-impl/utils/colorize.d.ts +2 -0
  62. package/bin/core-impl/utils/colorize.js +135 -0
  63. package/bin/core-impl/utils/errors.d.ts +1 -0
  64. package/bin/core-impl/utils/errors.js +17 -0
  65. package/bin/core-impl/utils/prevent.d.ts +8 -0
  66. package/bin/core-impl/utils/prevent.js +65 -0
  67. package/bin/core-impl/utils/prompt-end.d.ts +8 -0
  68. package/bin/core-impl/utils/prompt-end.js +34 -0
  69. package/bin/core-impl/utils/stream-text.d.ts +18 -0
  70. package/bin/core-impl/utils/stream-text.js +136 -0
  71. package/bin/core-impl/utils/system.d.ts +6 -0
  72. package/bin/core-impl/utils/system.js +7 -0
  73. package/bin/core-impl/utils/validate.d.ts +21 -0
  74. package/bin/core-impl/utils/validate.js +17 -0
  75. package/bin/core-impl/visual/animate/animate.d.ts +14 -0
  76. package/bin/core-impl/visual/animate/animate.js +65 -0
  77. package/bin/core-impl/visual/ascii-art/ascii-art.d.ts +6 -0
  78. package/bin/core-impl/visual/ascii-art/ascii-art.js +13 -0
  79. package/bin/core-types.d.ts +334 -0
  80. package/bin/core-types.js +0 -0
  81. package/bin/main.d.ts +36 -0
  82. package/bin/main.js +86 -0
  83. package/package.json +58 -0
@@ -0,0 +1,115 @@
1
+ import { stdin as input, stdout as output } from "node:process";
2
+ import readline from "node:readline/promises";
3
+ import { bar, fmt, msg } from "../msg-fmt/messages.js";
4
+ import {
5
+ countLines,
6
+ deleteLastLine,
7
+ deleteLastLines
8
+ } from "../msg-fmt/terminal.js";
9
+ import { colorize } from "../utils/colorize.js";
10
+ export async function numSelectPrompt(opts) {
11
+ const {
12
+ title = "",
13
+ hint,
14
+ hintPlaceholderColor = "blue",
15
+ validate,
16
+ defaultValue,
17
+ titleColor = "cyan",
18
+ titleTypography = "none",
19
+ titleVariant,
20
+ content,
21
+ contentColor = "dim",
22
+ contentTypography = "italic",
23
+ contentVariant,
24
+ borderColor = "dim",
25
+ variantOptions,
26
+ inline = true,
27
+ choices
28
+ } = opts;
29
+ if (!choices || choices.length === 0) {
30
+ throw new Error("Choices are required for select prompt.");
31
+ }
32
+ const rl = readline.createInterface({ input, output });
33
+ const formattedBar = bar({ borderColor });
34
+ let linesToDelete = 0;
35
+ let errorMessage = "";
36
+ while (true) {
37
+ if (linesToDelete > 0) {
38
+ deleteLastLines(linesToDelete);
39
+ }
40
+ const { text: question } = fmt({
41
+ hintPlaceholderColor,
42
+ type: errorMessage !== "" ? "M_ERROR" : "M_GENERAL",
43
+ title,
44
+ titleColor,
45
+ titleTypography,
46
+ titleVariant,
47
+ content,
48
+ contentColor,
49
+ contentTypography,
50
+ contentVariant,
51
+ borderColor,
52
+ hint,
53
+ variantOptions,
54
+ errorMessage
55
+ });
56
+ let choicesText = "";
57
+ if (inline) {
58
+ choicesText = choices.map(
59
+ (choice, index) => `${index + 1}) ${choice.title}${choice.description ? ` (${choice.description})` : ""}`
60
+ ).join(" / ");
61
+ } else {
62
+ choicesText = choices.map(
63
+ (choice, index) => `${index + 1}) ${choice.title}${choice.description ? ` - ${choice.description}` : ""}`
64
+ ).join(`
65
+ ${formattedBar} `);
66
+ }
67
+ const { text: formattedPrompt } = fmt({
68
+ hintPlaceholderColor,
69
+ type: "M_NULL",
70
+ title: `${question}
71
+ ${choicesText}
72
+ ${formattedBar} ${colorize(
73
+ "Enter your choice:",
74
+ contentColor
75
+ )}
76
+ ${formattedBar} `
77
+ });
78
+ const questionLines = countLines(formattedPrompt);
79
+ linesToDelete = questionLines + 1;
80
+ const prompt = await rl.question(formattedPrompt);
81
+ const answer = prompt.trim() || defaultValue;
82
+ if (!prompt.trim() && defaultValue !== void 0) {
83
+ deleteLastLine();
84
+ msg({
85
+ type: "M_MIDDLE",
86
+ title: ` ${defaultValue}`,
87
+ titleColor: "none"
88
+ });
89
+ msg({ type: "M_BAR", borderColor });
90
+ }
91
+ const num = Number(answer);
92
+ if (Number.isNaN(num) || num < 1 || num > choices.length) {
93
+ errorMessage = `Please enter a number between 1 and ${choices.length}.`;
94
+ continue;
95
+ }
96
+ const selectedChoice = choices[num - 1];
97
+ const selectedValue = selectedChoice?.id ?? num;
98
+ let isValid = true;
99
+ errorMessage = "";
100
+ if (validate && isValid) {
101
+ const validation = await validate(selectedValue);
102
+ if (validation !== true) {
103
+ isValid = false;
104
+ errorMessage = typeof validation === "string" ? validation : "Invalid input.";
105
+ }
106
+ }
107
+ if (isValid) {
108
+ rl.close();
109
+ if (selectedChoice?.action) {
110
+ await selectedChoice.action();
111
+ }
112
+ return selectedValue;
113
+ }
114
+ }
115
+ }
@@ -0,0 +1,33 @@
1
+ import type { BorderColorName, ColorName, SelectOption, TypographyName, VariantName } from "../../core-types.js";
2
+ import { symbols } from "../msg-fmt/messages.js";
3
+ type SeparatorOption = {
4
+ separator: true;
5
+ width?: number;
6
+ symbol?: keyof typeof symbols;
7
+ };
8
+ type SelectPromptParams<T extends string> = {
9
+ title: string;
10
+ content?: string;
11
+ options: (SelectOption<T> | SeparatorOption)[];
12
+ defaultValue?: T;
13
+ required?: boolean;
14
+ borderColor?: BorderColorName;
15
+ titleColor?: ColorName;
16
+ titleTypography?: TypographyName;
17
+ titleVariant?: VariantName;
18
+ contentColor?: ColorName;
19
+ contentTypography?: TypographyName;
20
+ border?: boolean;
21
+ endTitle?: string;
22
+ endTitleColor?: ColorName;
23
+ debug?: boolean;
24
+ terminalWidth?: number;
25
+ displayInstructions?: boolean;
26
+ shouldStream?: boolean;
27
+ streamDelay?: number;
28
+ };
29
+ /**
30
+ * Displays a selectable prompt in the terminal and returns the chosen value.
31
+ */
32
+ export declare function selectPrompt<T extends string>(params: SelectPromptParams<T>): Promise<T>;
33
+ export {};
@@ -0,0 +1,303 @@
1
+ import { re } from "@reliverse/relico";
2
+ import { relinka } from "@reliverse/relinka";
3
+ import { stdin as input, stdout as output } from "node:process";
4
+ import readline from "node:readline";
5
+ import { msg, symbols } from "../msg-fmt/messages.js";
6
+ import { deleteLastLine } from "../msg-fmt/terminal.js";
7
+ import { completePrompt } from "../utils/prompt-end.js";
8
+ import { streamText } from "../utils/stream-text.js";
9
+ function isSelectOption(option) {
10
+ return !("separator" in option);
11
+ }
12
+ async function renderPromptUI(params) {
13
+ const {
14
+ title,
15
+ content,
16
+ options,
17
+ selectedIndex,
18
+ errorMessage,
19
+ displayInstructions,
20
+ allDisabled,
21
+ instructions,
22
+ titleColor,
23
+ contentColor,
24
+ contentTypography,
25
+ debug,
26
+ titleVariant,
27
+ titleTypography,
28
+ isRerender = false,
29
+ shouldStream = false,
30
+ streamDelay = 20
31
+ } = params;
32
+ if (!isRerender) {
33
+ if (shouldStream) {
34
+ deleteLastLine();
35
+ msg({ type: "M_BAR", title: "" });
36
+ await streamText({
37
+ text: title,
38
+ delay: streamDelay,
39
+ color: titleColor,
40
+ newline: false,
41
+ clearLine: true
42
+ });
43
+ process.stdout.write("\r");
44
+ msg({
45
+ type: "M_GENERAL",
46
+ title,
47
+ titleColor,
48
+ titleVariant,
49
+ titleTypography
50
+ });
51
+ if (content) {
52
+ msg({
53
+ type: "M_NULL",
54
+ content,
55
+ contentColor,
56
+ contentTypography
57
+ });
58
+ }
59
+ } else {
60
+ msg({ type: "M_GENERAL", title, titleColor });
61
+ if (content) {
62
+ msg({
63
+ type: "M_NULL",
64
+ content,
65
+ contentColor,
66
+ contentTypography
67
+ });
68
+ }
69
+ }
70
+ }
71
+ let uiLineCount = 0;
72
+ if (errorMessage) {
73
+ msg({
74
+ type: "M_ERROR",
75
+ title: `${re.redBright(symbols.step_error)} ${re.redBright(errorMessage)}`
76
+ });
77
+ uiLineCount++;
78
+ } else if (allDisabled) {
79
+ msg({ type: "M_ERROR", title: re.redBright("All options are disabled.") });
80
+ uiLineCount++;
81
+ } else if (displayInstructions && !isRerender) {
82
+ msg({ type: "M_NULL", title: re.blue(instructions) });
83
+ uiLineCount++;
84
+ }
85
+ for (let index = 0; index < options.length; index++) {
86
+ const option = options[index];
87
+ if (!option) continue;
88
+ if (!isSelectOption(option)) {
89
+ const width = option.width ?? 20;
90
+ const symbolKey = option.symbol ?? "line";
91
+ const lineSymbol = symbolKey in symbols ? symbols[symbolKey] : "\u2500";
92
+ msg({ type: "M_NULL", title: re.dim(lineSymbol.repeat(width)) });
93
+ uiLineCount++;
94
+ continue;
95
+ }
96
+ const isSelected = index === selectedIndex;
97
+ const isDisabled = option.disabled;
98
+ const prefix = isSelected ? re.dim(re.reset("> ")) : " ";
99
+ const labelColor = isDisabled ? re.dim(re.reset(option.label)) : isSelected ? re.reset(re.yellow(option.label)) : re.reset(option.label);
100
+ const hint = option.hint ? re.reset(
101
+ ` (${isDisabled ? re.dim(option.hint) : re.italic(re.dim(option.hint))})`
102
+ ) : "";
103
+ msg({ type: "M_NULL", title: `${prefix}${labelColor}${hint}` });
104
+ uiLineCount++;
105
+ }
106
+ if (debug) {
107
+ relinka("info", "", { optionsCount: options.length });
108
+ }
109
+ return uiLineCount;
110
+ }
111
+ export async function selectPrompt(params) {
112
+ const {
113
+ title = "",
114
+ content = "",
115
+ options,
116
+ defaultValue,
117
+ required = false,
118
+ borderColor = "dim",
119
+ titleColor = "cyan",
120
+ titleTypography = "none",
121
+ titleVariant,
122
+ contentColor = "dim",
123
+ contentTypography = "italic",
124
+ border = true,
125
+ endTitle = "",
126
+ endTitleColor = "dim",
127
+ debug = false,
128
+ terminalWidth: customTerminalWidth = 90,
129
+ displayInstructions = false,
130
+ shouldStream = false,
131
+ streamDelay = 20
132
+ } = params;
133
+ let selectedIndex = defaultValue ? options.findIndex(
134
+ (option) => isSelectOption(option) && option.value === defaultValue && !option.disabled
135
+ ) : -1;
136
+ if (selectedIndex === -1) {
137
+ selectedIndex = options.findIndex(
138
+ (option) => isSelectOption(option) && !option.disabled
139
+ );
140
+ }
141
+ if (selectedIndex === -1) {
142
+ selectedIndex = 0;
143
+ }
144
+ const rl = readline.createInterface({ input, output });
145
+ readline.emitKeypressEvents(input, rl);
146
+ if (typeof input.setRawMode === "function") {
147
+ input.setRawMode(true);
148
+ }
149
+ const instructions = "Use <\u2191/\u2193> or <k/j> to navigate, <Enter> to select, <Ctrl+C> to exit";
150
+ let errorMessage = "";
151
+ const allDisabled = options.every(
152
+ (option) => isSelectOption(option) && option.disabled
153
+ );
154
+ let lastUILineCount = 0;
155
+ function renderOptions() {
156
+ if (lastUILineCount > 0) {
157
+ for (let i = 0; i < lastUILineCount; i++) {
158
+ process.stdout.write("\x1B[1A\x1B[2K");
159
+ }
160
+ }
161
+ void renderPromptUI({
162
+ title,
163
+ content,
164
+ options,
165
+ selectedIndex,
166
+ errorMessage,
167
+ displayInstructions,
168
+ allDisabled,
169
+ instructions,
170
+ borderColor,
171
+ titleColor,
172
+ contentColor,
173
+ contentTypography,
174
+ debug,
175
+ titleVariant,
176
+ titleTypography,
177
+ terminalWidth: customTerminalWidth,
178
+ isRerender: true,
179
+ shouldStream,
180
+ streamDelay
181
+ }).then((count) => {
182
+ lastUILineCount = count;
183
+ });
184
+ }
185
+ lastUILineCount = await renderPromptUI({
186
+ title,
187
+ content,
188
+ options,
189
+ selectedIndex,
190
+ errorMessage,
191
+ displayInstructions,
192
+ allDisabled,
193
+ instructions,
194
+ borderColor,
195
+ titleColor,
196
+ contentColor,
197
+ contentTypography,
198
+ debug,
199
+ titleVariant,
200
+ titleTypography,
201
+ terminalWidth: customTerminalWidth,
202
+ isRerender: false,
203
+ shouldStream,
204
+ streamDelay
205
+ });
206
+ return new Promise((resolve) => {
207
+ function handleKeypress(_str, key) {
208
+ if (allDisabled) {
209
+ if (key.name === "c" && key.ctrl) {
210
+ void endPrompt(true);
211
+ return;
212
+ }
213
+ return;
214
+ }
215
+ if (key.name === "up" || key.name === "k") {
216
+ moveSelectionUp();
217
+ } else if (key.name === "down" || key.name === "j") {
218
+ moveSelectionDown();
219
+ } else if (key.name === "return") {
220
+ void confirmSelection();
221
+ } else if (key.name === "c" && key.ctrl) {
222
+ void endPrompt(true);
223
+ }
224
+ }
225
+ function moveSelectionUp() {
226
+ const originalPointer = selectedIndex;
227
+ do {
228
+ selectedIndex = (selectedIndex - 1 + options.length) % options.length;
229
+ const option = options[selectedIndex];
230
+ if (option && isSelectOption(option) && !option.disabled) break;
231
+ } while (selectedIndex !== originalPointer);
232
+ renderOptions();
233
+ }
234
+ function moveSelectionDown() {
235
+ const originalPointer = selectedIndex;
236
+ do {
237
+ selectedIndex = (selectedIndex + 1) % options.length;
238
+ const option = options[selectedIndex];
239
+ if (option && isSelectOption(option) && !option.disabled) break;
240
+ } while (selectedIndex !== originalPointer);
241
+ renderOptions();
242
+ }
243
+ async function confirmSelection() {
244
+ const option = options[selectedIndex];
245
+ if (!option || !isSelectOption(option)) {
246
+ errorMessage = "This option is not selectable.";
247
+ renderOptions();
248
+ return;
249
+ }
250
+ if (option.disabled) {
251
+ errorMessage = "This option is disabled";
252
+ renderOptions();
253
+ return;
254
+ }
255
+ if (required && !option.value) {
256
+ errorMessage = "You must select a valid option.";
257
+ renderOptions();
258
+ return;
259
+ }
260
+ cleanup();
261
+ resolve(option.value);
262
+ deleteLastLine();
263
+ await completePrompt(
264
+ "select",
265
+ false,
266
+ endTitle,
267
+ endTitleColor,
268
+ titleTypography,
269
+ titleVariant ? titleVariant : void 0,
270
+ border,
271
+ borderColor,
272
+ void 0,
273
+ true
274
+ );
275
+ }
276
+ async function endPrompt(isCtrlC = false) {
277
+ await completePrompt(
278
+ "select",
279
+ isCtrlC,
280
+ endTitle,
281
+ endTitleColor,
282
+ titleTypography,
283
+ titleVariant ? titleVariant : void 0,
284
+ border,
285
+ borderColor,
286
+ void 0,
287
+ false
288
+ );
289
+ cleanup(isCtrlC);
290
+ }
291
+ function cleanup(isCtrlC = false) {
292
+ if (typeof input.setRawMode === "function") {
293
+ input.setRawMode(false);
294
+ }
295
+ rl.close();
296
+ input.removeListener("keypress", handleKeypress);
297
+ if (isCtrlC) {
298
+ process.exit(0);
299
+ }
300
+ }
301
+ input.on("keypress", handleKeypress);
302
+ });
303
+ }
@@ -0,0 +1,5 @@
1
+ import type { TogglePromptParams } from "../../core-types.js";
2
+ /**
3
+ * A toggle prompt that lets the user choose between two options (Yes/No style).
4
+ */
5
+ export declare function togglePrompt<T extends string>(params: TogglePromptParams<T>): Promise<boolean>;
@@ -0,0 +1,209 @@
1
+ import { re } from "@reliverse/relico";
2
+ import { relinka } from "@reliverse/relinka";
3
+ import { stdin as input, stdout as output } from "node:process";
4
+ import readline from "node:readline";
5
+ import { msg } from "../msg-fmt/messages.js";
6
+ import { deleteLastLine } from "../msg-fmt/terminal.js";
7
+ import { completePrompt } from "../utils/prompt-end.js";
8
+ function renderTogglePrompt(params) {
9
+ const {
10
+ title,
11
+ content,
12
+ options,
13
+ selectedIndex,
14
+ errorMessage,
15
+ instructions,
16
+ titleColor,
17
+ titleTypography,
18
+ titleVariant,
19
+ contentColor,
20
+ contentTypography,
21
+ displayInstructions,
22
+ debug,
23
+ isRerender = false
24
+ } = params;
25
+ if (!isRerender) {
26
+ msg({
27
+ type: "M_GENERAL",
28
+ title,
29
+ titleColor,
30
+ titleTypography,
31
+ ...titleVariant ? { titleVariant } : {}
32
+ });
33
+ if (content) {
34
+ msg({
35
+ type: "M_NULL",
36
+ content: content.split("\n").map((line) => line.trim()).join("\n"),
37
+ contentColor,
38
+ contentTypography
39
+ });
40
+ }
41
+ }
42
+ let uiLineCount = 0;
43
+ if (errorMessage) {
44
+ msg({
45
+ type: "M_NULL",
46
+ title: re.redBright(errorMessage)
47
+ });
48
+ uiLineCount++;
49
+ } else if (displayInstructions && !isRerender) {
50
+ msg({
51
+ type: "M_NULL",
52
+ title: re.yellow(instructions)
53
+ });
54
+ uiLineCount++;
55
+ }
56
+ const displayString = options.map(
57
+ (option, index) => index === selectedIndex ? re.yellow(option) : re.reset(option)
58
+ ).join(re.dim(re.reset(" / ")));
59
+ msg({ type: "M_NULL", title: displayString });
60
+ uiLineCount++;
61
+ if (debug) {
62
+ relinka("info", "", {
63
+ selectedIndex,
64
+ displayOptions: options
65
+ });
66
+ }
67
+ return uiLineCount;
68
+ }
69
+ export async function togglePrompt(params) {
70
+ const {
71
+ title = "",
72
+ content = "",
73
+ options = ["Yes", "No"],
74
+ defaultValue = "Yes",
75
+ borderColor = "dim",
76
+ titleColor = "cyan",
77
+ titleTypography = "none",
78
+ titleVariant,
79
+ contentColor = "dim",
80
+ contentTypography = "italic",
81
+ border = true,
82
+ endTitle = "",
83
+ endTitleColor = "dim",
84
+ displayInstructions = false,
85
+ debug = false
86
+ } = params;
87
+ if (options.length !== 2) {
88
+ throw new Error("togglePrompt requires exactly two options.");
89
+ }
90
+ let selectedIndex = options.findIndex((option) => option === defaultValue);
91
+ if (selectedIndex === -1) {
92
+ selectedIndex = 0;
93
+ }
94
+ const rl = readline.createInterface({ input, output });
95
+ readline.emitKeypressEvents(input, rl);
96
+ if (typeof input.setRawMode === "function") {
97
+ input.setRawMode(true);
98
+ }
99
+ const instructions = "Use <\u2190/\u2192> or <h/l> to navigate, <Enter> to select, <Ctrl+C> to exit";
100
+ let errorMessage = "";
101
+ let lastUILineCount = 0;
102
+ function renderOptions() {
103
+ if (lastUILineCount > 0) {
104
+ for (let i = 0; i < lastUILineCount; i++) {
105
+ process.stdout.write("\x1B[1A\x1B[2K");
106
+ }
107
+ }
108
+ lastUILineCount = renderTogglePrompt({
109
+ title,
110
+ content,
111
+ options,
112
+ selectedIndex,
113
+ errorMessage,
114
+ instructions,
115
+ borderColor,
116
+ titleColor,
117
+ titleTypography,
118
+ ...titleVariant ? { titleVariant } : {},
119
+ contentColor,
120
+ contentTypography,
121
+ border,
122
+ displayInstructions,
123
+ debug,
124
+ isRerender: true
125
+ });
126
+ }
127
+ lastUILineCount = renderTogglePrompt({
128
+ title,
129
+ content,
130
+ options,
131
+ selectedIndex,
132
+ errorMessage,
133
+ instructions,
134
+ borderColor,
135
+ titleColor,
136
+ titleTypography,
137
+ ...titleVariant ? { titleVariant } : {},
138
+ contentColor,
139
+ contentTypography,
140
+ border,
141
+ displayInstructions,
142
+ debug,
143
+ isRerender: false
144
+ });
145
+ return new Promise((resolve) => {
146
+ function cleanup(isCtrlC = false) {
147
+ if (typeof input.setRawMode === "function") {
148
+ input.setRawMode(false);
149
+ }
150
+ rl.close();
151
+ input.removeListener("keypress", handleKeypress);
152
+ if (isCtrlC) {
153
+ process.exit(0);
154
+ }
155
+ }
156
+ function endPrompt(isCtrlC = false) {
157
+ if (endTitle !== "") {
158
+ msg({
159
+ type: "M_END",
160
+ title: endTitle,
161
+ titleColor: endTitleColor,
162
+ titleTypography,
163
+ ...titleVariant ? { titleVariant } : {},
164
+ border,
165
+ borderColor
166
+ });
167
+ }
168
+ cleanup(isCtrlC);
169
+ }
170
+ function handleKeypress(_str, key) {
171
+ if (key.name === "left" || key.name === "h") {
172
+ selectedIndex = (selectedIndex - 1 + options.length) % options.length;
173
+ errorMessage = "";
174
+ renderOptions();
175
+ } else if (key.name === "right" || key.name === "l") {
176
+ selectedIndex = (selectedIndex + 1) % options.length;
177
+ errorMessage = "";
178
+ renderOptions();
179
+ } else if (key.name === "return") {
180
+ const selectedOption = options[selectedIndex];
181
+ if (!selectedOption) {
182
+ errorMessage = "You must select an option.";
183
+ renderOptions();
184
+ } else {
185
+ cleanup();
186
+ const booleanValue = selectedIndex === 0;
187
+ void completePrompt(
188
+ "toggle",
189
+ false,
190
+ endTitle,
191
+ endTitleColor,
192
+ titleTypography,
193
+ titleVariant ? titleVariant : void 0,
194
+ border,
195
+ borderColor,
196
+ void 0,
197
+ booleanValue
198
+ );
199
+ resolve(booleanValue);
200
+ deleteLastLine();
201
+ msg({ type: "M_BAR", borderColor });
202
+ }
203
+ } else if (key.name === "c" && key.ctrl) {
204
+ endPrompt(true);
205
+ }
206
+ }
207
+ input.on("keypress", handleKeypress);
208
+ });
209
+ }
@@ -0,0 +1,2 @@
1
+ import type { PromptOptions } from "../../core-types.js";
2
+ export declare function endPrompt({ title, titleColor, titleTypography, titleVariant, titleAnimation, titleAnimationDelay, border, borderColor, horizontalLineLength, }: PromptOptions): Promise<void>;
@@ -0,0 +1,42 @@
1
+ import { msg } from "../msg-fmt/messages.js";
2
+ import { getExactTerminalWidth } from "../msg-fmt/terminal.js";
3
+ import { animateText } from "../visual/animate/animate.js";
4
+ export async function endPrompt({
5
+ title = "",
6
+ titleColor = "cyan",
7
+ titleTypography = "none",
8
+ titleVariant,
9
+ titleAnimation,
10
+ titleAnimationDelay,
11
+ border = true,
12
+ borderColor = "dim",
13
+ horizontalLineLength = 0
14
+ }) {
15
+ if (horizontalLineLength === 0) {
16
+ horizontalLineLength = getExactTerminalWidth() - 3;
17
+ }
18
+ if (titleAnimation) {
19
+ await animateText({
20
+ title: title ? title : " ",
21
+ anim: titleAnimation,
22
+ delay: titleAnimationDelay,
23
+ type: "M_END",
24
+ titleColor,
25
+ titleTypography,
26
+ border,
27
+ borderColor,
28
+ horizontalLineLength
29
+ });
30
+ } else {
31
+ msg({
32
+ type: "M_END",
33
+ title: title ? title : " ",
34
+ titleColor,
35
+ titleTypography,
36
+ titleVariant,
37
+ border,
38
+ borderColor,
39
+ horizontalLineLength
40
+ });
41
+ }
42
+ }