@reliverse/rempts-core 1.6.1

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