@reliverse/relinka 1.1.7 → 1.1.8

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 (121) hide show
  1. package/dist-npm/components/anykey/index.js +57 -77
  2. package/dist-npm/components/block/block.js +48 -61
  3. package/dist-npm/components/checkbox/index.js +178 -211
  4. package/dist-npm/components/confirm/confirm-main.js +85 -122
  5. package/dist-npm/components/confirm/confirm-three.js +24 -27
  6. package/dist-npm/components/confirm/index.js +36 -45
  7. package/dist-npm/components/core/Separator.js +15 -17
  8. package/dist-npm/components/core/create-prompt.js +101 -124
  9. package/dist-npm/components/core/errors.js +19 -15
  10. package/dist-npm/components/core/hook-engine.js +91 -120
  11. package/dist-npm/components/core/index.js +12 -12
  12. package/dist-npm/components/core/key.js +16 -6
  13. package/dist-npm/components/core/lines.js +45 -54
  14. package/dist-npm/components/core/make-theme.js +21 -30
  15. package/dist-npm/components/core/position.js +27 -23
  16. package/dist-npm/components/core/promise-polyfill.js +11 -14
  17. package/dist-npm/components/core/screen-manager.js +59 -74
  18. package/dist-npm/components/core/theme.js +22 -32
  19. package/dist-npm/components/core/use-effect.js +9 -15
  20. package/dist-npm/components/core/use-keypress.js +19 -23
  21. package/dist-npm/components/core/use-memo.js +10 -16
  22. package/dist-npm/components/core/use-pagination.js +33 -32
  23. package/dist-npm/components/core/use-prefix.js +40 -38
  24. package/dist-npm/components/core/use-ref.js +2 -5
  25. package/dist-npm/components/core/use-state.js +15 -23
  26. package/dist-npm/components/core/useKeyPress.js +14 -17
  27. package/dist-npm/components/core/usePromptState.js +8 -14
  28. package/dist-npm/components/core/utils.js +7 -16
  29. package/dist-npm/components/date/date.js +173 -204
  30. package/dist-npm/components/editor/index.js +71 -92
  31. package/dist-npm/components/expand/index.js +96 -124
  32. package/dist-npm/components/figures/index.js +283 -294
  33. package/dist-npm/components/input/index.js +61 -87
  34. package/dist-npm/components/input/text-main.js +97 -124
  35. package/dist-npm/components/input/text.js +24 -28
  36. package/dist-npm/components/instance/basic.js +17 -25
  37. package/dist-npm/components/instance/browser.js +14 -18
  38. package/dist-npm/components/instance/reporter/basic.js +46 -65
  39. package/dist-npm/components/instance/reporter/browser.js +44 -47
  40. package/dist-npm/components/instance/reporter/fancy.js +83 -96
  41. package/dist-npm/components/instance/shared.js +2 -2
  42. package/dist-npm/components/mono/mono.js +52 -62
  43. package/dist-npm/components/mono/monoTwo.js +35 -49
  44. package/dist-npm/components/multiselect/group-multiselect.js +55 -71
  45. package/dist-npm/components/multiselect/multi-select-two.js +97 -130
  46. package/dist-npm/components/multiselect/multi-select.js +43 -49
  47. package/dist-npm/components/multiselect/multiselect-main.d.ts +1 -0
  48. package/dist-npm/components/multiselect/multiselect-main.js +158 -145
  49. package/dist-npm/components/multiselect/num-multi-select.js +97 -130
  50. package/dist-npm/components/multiselect/num-multiselect-main.js +24 -35
  51. package/dist-npm/components/next-steps/next-steps.js +23 -25
  52. package/dist-npm/components/number/index.js +78 -112
  53. package/dist-npm/components/number/number-main.js +95 -2
  54. package/dist-npm/components/password/index.js +54 -73
  55. package/dist-npm/components/password/password-main.js +119 -2
  56. package/dist-npm/components/password/password-three.js +26 -30
  57. package/dist-npm/components/progressbar/ProgressBar.js +45 -64
  58. package/dist-npm/components/progressbar/helper.js +33 -40
  59. package/dist-npm/components/progressbar/index.js +1 -1
  60. package/dist-npm/components/prompts/create.js +29 -44
  61. package/dist-npm/components/prompts/index.d.ts +1 -0
  62. package/dist-npm/components/prompts/index.js +46 -45
  63. package/dist-npm/components/prompts/prompt.js +211 -260
  64. package/dist-npm/components/prompts/promptTwo.js +561 -605
  65. package/dist-npm/components/prompts/relinka.js +237 -295
  66. package/dist-npm/components/range/range.js +247 -294
  67. package/dist-npm/components/rawlist/index.js +87 -107
  68. package/dist-npm/components/results/results.js +31 -37
  69. package/dist-npm/components/search/index.js +148 -193
  70. package/dist-npm/components/select/index.js +148 -186
  71. package/dist-npm/components/select/num-select-main.js +27 -27
  72. package/dist-npm/components/select/num-select.js +124 -5
  73. package/dist-npm/components/select/select-key.js +24 -25
  74. package/dist-npm/components/select/select-main.d.ts +1 -0
  75. package/dist-npm/components/select/select-main.js +143 -133
  76. package/dist-npm/components/select/select-three.js +32 -36
  77. package/dist-npm/components/select/select-two.js +94 -87
  78. package/dist-npm/components/spinner/index.js +107 -136
  79. package/dist-npm/components/st-end/end.js +34 -26
  80. package/dist-npm/components/st-end/start.js +29 -15
  81. package/dist-npm/components/toggle/index.js +113 -137
  82. package/dist-npm/components/visual/animate/animate.js +53 -10
  83. package/dist-npm/components/visual/ascii-art/ascii-art.js +12 -1
  84. package/dist-npm/main.js +1 -0
  85. package/dist-npm/testing/index.js +58 -83
  86. package/dist-npm/types/general.d.ts +1 -1
  87. package/dist-npm/types/general.js +0 -1
  88. package/dist-npm/types/index.js +2 -3
  89. package/dist-npm/types/keypress.js +35 -36
  90. package/dist-npm/types/readline.js +0 -1
  91. package/dist-npm/types/relinka.js +0 -1
  92. package/dist-npm/types/utils.js +0 -1
  93. package/dist-npm/utils/box.js +135 -137
  94. package/dist-npm/utils/color.js +65 -74
  95. package/dist-npm/utils/colorize.js +124 -156
  96. package/dist-npm/utils/component.js +532 -657
  97. package/dist-npm/utils/constants.js +64 -63
  98. package/dist-npm/utils/core.js +2 -3
  99. package/dist-npm/utils/decoder.js +244 -223
  100. package/dist-npm/utils/error.js +4 -9
  101. package/dist-npm/utils/errors.js +14 -4
  102. package/dist-npm/utils/format.js +19 -24
  103. package/dist-npm/utils/keypress.js +316 -414
  104. package/dist-npm/utils/log.js +11 -15
  105. package/dist-npm/utils/mapping.js +45 -52
  106. package/dist-npm/utils/messages.js +196 -183
  107. package/dist-npm/utils/platforms.js +16 -20
  108. package/dist-npm/utils/prompt-tmp.js +235 -286
  109. package/dist-npm/utils/prompt-two.js +235 -286
  110. package/dist-npm/utils/readline.js +5 -7
  111. package/dist-npm/utils/skeleton.js +130 -170
  112. package/dist-npm/utils/stream.js +2 -2
  113. package/dist-npm/utils/string.js +44 -58
  114. package/dist-npm/utils/terminal.js +23 -34
  115. package/dist-npm/utils/tree.js +30 -41
  116. package/dist-npm/utils/types.js +0 -1
  117. package/dist-npm/utils/utils.js +8 -8
  118. package/dist-npm/utils/variants.js +36 -44
  119. package/package.json +14 -14
  120. package/dist-npm/mod.js +0 -2
  121. /package/dist-npm/{mod.d.ts → main.d.ts} +0 -0
@@ -1,141 +1,151 @@
1
- import process, {
2
- stdin as input,
3
- stdout as output,
4
- } from 'node:process';
5
- import readline from 'node:readline';
6
- import {
7
- cyanBright,
8
- dim,
9
- greenBright,
10
- redBright,
11
- } from 'picocolors';
12
- import {
13
- bar,
14
- fmt,
15
- msg,
16
- symbols,
17
- } from '../../utils/messages.js';
18
- import {deleteLastLine} from '../../utils/terminal.js';
19
-
20
- export function selectPrompt(params) {
21
- const {
22
- title,
23
- options,
24
- initial,
25
- required = false,
26
- borderColor = 'viceGradient',
27
- titleColor = 'cyanBright',
28
- titleTypography = 'bold',
29
- titleVariant,
30
- border = true,
31
- endTitle = '\u{1F44B}',
32
- endTitleColor = 'passionGradient',
33
- } = params;
34
-
35
- let selectedIndex = initial ? options.findIndex((option) => option.value === initial) : 0;
36
-
37
- if (selectedIndex === -1) {
38
- selectedIndex = 0;
1
+ import { stdin as input, stdout as output } from "node:process";
2
+ import readline from "node:readline";
3
+ import { cyanBright, dim, greenBright, redBright } from "picocolors";
4
+ import { deleteLastLine } from "../../main.js";
5
+ import { bar, fmt, msg, symbols } from "../../utils/messages.js";
6
+ export async function selectPrompt(params) {
7
+ const {
8
+ title,
9
+ options,
10
+ initial,
11
+ required = false,
12
+ borderColor = "viceGradient",
13
+ titleColor = "cyanBright",
14
+ titleTypography = "bold",
15
+ titleVariant,
16
+ border = true,
17
+ endTitle = "\u{1F44B}",
18
+ endTitleColor = "passionGradient",
19
+ maxItems
20
+ } = params;
21
+ let selectedIndex = initial ? options.findIndex((option) => option.value === initial) : 0;
22
+ if (selectedIndex === -1) {
23
+ selectedIndex = 0;
24
+ }
25
+ const rl = readline.createInterface({ input, output });
26
+ readline.emitKeypressEvents(input, rl);
27
+ if (typeof input.setRawMode === "function") {
28
+ input.setRawMode(true);
29
+ }
30
+ const formattedBar = bar({ borderColor });
31
+ let linesRendered = 0;
32
+ const instructions = `Use <\u2191/\u2193> or <k/j> to navigate, <Enter> to select, <Ctrl+C> to exit`;
33
+ let errorMessage = "";
34
+ function renderOptions() {
35
+ if (linesRendered > 0) {
36
+ process.stdout.write(`\x1B[${linesRendered}A`);
39
37
  }
40
-
41
- const rl = readline.createInterface({
42
- input,
43
- output,
44
- });
45
- readline.emitKeypressEvents(input, rl);
46
- input.setRawMode?.(true);
47
- const formattedBar = bar({
48
- borderColor,
49
- });
50
- let linesRendered = 0;
51
- const instructions = `Use <\u2191/\u2193> or <k/j> to navigate, <Enter> to select, <Ctrl+C> to exit`;
52
- let errorMessage = '';
53
-
54
- function renderOptions() {
55
- if (linesRendered > 0) {
56
- process.stdout.write(`\x1B[${linesRendered}A`);
57
- }
58
-
59
- let outputStr = `${greenBright(symbols.step_active)} ${fmt({
60
- type: 'M_NULL',
61
- title,
62
- titleColor,
63
- })}
38
+ let outputStr = `${greenBright(symbols.step_active)} ${fmt({
39
+ type: "M_NULL",
40
+ title,
41
+ titleColor
42
+ })}
64
43
  `;
65
-
66
- if (errorMessage) {
67
- outputStr += `${redBright(symbols.step_error)} ${errorMessage}
44
+ if (errorMessage) {
45
+ outputStr += `${redBright(symbols.step_error)} ${errorMessage}
68
46
  `;
69
- } else {
70
- outputStr += `${formattedBar} ${dim(instructions)}
47
+ } else {
48
+ outputStr += `${formattedBar} ${dim(instructions)}
71
49
  `;
72
- }
73
-
74
- for (const [index, option] of options.entries()) {
75
- const isSelected = index === selectedIndex;
76
- const prefix = isSelected ? '> ' : ' ';
77
- const optionLabel = isSelected ? cyanBright(option.label) : option.label;
78
- const hint = option.hint ? ` (${option.hint})` : '';
79
-
80
- outputStr += `${formattedBar} ${prefix}${optionLabel}${dim(hint)}
50
+ }
51
+ const terminalHeight = process.stdout.rows || 24;
52
+ const availableHeight = terminalHeight - 4;
53
+ const computedMaxItems = Math.min(
54
+ maxItems ?? Infinity,
55
+ availableHeight > 0 ? availableHeight : Infinity,
56
+ options.length
57
+ );
58
+ const minItems = 3;
59
+ const displayItems = Math.max(computedMaxItems, minItems);
60
+ let startIdx = 0;
61
+ let endIdx = options.length - 1;
62
+ if (options.length > displayItems) {
63
+ const half = Math.floor(displayItems / 2);
64
+ startIdx = selectedIndex - half;
65
+ endIdx = selectedIndex + (displayItems - half - 1);
66
+ if (startIdx < 0) {
67
+ startIdx = 0;
68
+ endIdx = displayItems - 1;
69
+ } else if (endIdx >= options.length) {
70
+ endIdx = options.length - 1;
71
+ startIdx = options.length - displayItems;
72
+ }
73
+ }
74
+ const shouldRenderTopEllipsis = startIdx > 0;
75
+ const shouldRenderBottomEllipsis = endIdx < options.length - 1;
76
+ if (shouldRenderTopEllipsis) {
77
+ outputStr += `${formattedBar} ${dim("...")}
81
78
  `;
82
- }
83
-
84
- process.stdout.write(outputStr);
85
- linesRendered = 1 + 1 + options.length;
86
79
  }
87
-
88
- renderOptions();
89
- return new Promise((resolve) => {
90
- function onKeyPress(str, key) {
91
- if (key.name === 'up' || key.name === 'k') {
92
- selectedIndex = (selectedIndex - 1 + options.length) % options.length;
93
- errorMessage = '';
94
- renderOptions();
95
- } else if (key.name === 'down' || key.name === 'j') {
96
- selectedIndex = (selectedIndex + 1) % options.length;
97
- errorMessage = '';
98
- renderOptions();
99
- } else if (key.name === 'return') {
100
- if (required && !options[selectedIndex].value) {
101
- deleteLastLine();
102
- errorMessage = 'You must select an option.';
103
- renderOptions();
104
- } else {
105
- cleanup();
106
- resolve(options[selectedIndex].value);
107
- deleteLastLine();
108
- deleteLastLine();
109
- msg({
110
- type: 'M_MIDDLE',
111
- });
112
- }
113
- } else if (key.name === 'c' && key.ctrl) {
114
- msg({
115
- type: 'M_NEWLINE',
116
- });
117
- msg({
118
- type: 'M_END',
119
- title: endTitle,
120
- titleColor: endTitleColor,
121
- titleTypography,
122
- titleVariant,
123
- border,
124
- borderColor,
125
- });
126
- cleanup();
127
- }
128
-
129
-
130
- }
131
-
132
- function cleanup() {
133
- input.setRawMode?.(false);
134
- rl.close();
135
- input.removeListener('keypress', onKeyPress);
136
- process.stdout.write(`\x1B[${linesRendered}B`);
80
+ for (let index = startIdx; index <= endIdx; index++) {
81
+ const option = options[index];
82
+ const isSelected = index === selectedIndex;
83
+ const prefix = isSelected ? "> " : " ";
84
+ const optionLabel = isSelected ? cyanBright(option.label) : option.label;
85
+ const hint = option.hint ? ` (${option.hint})` : "";
86
+ outputStr += `${formattedBar} ${prefix}${optionLabel}${dim(hint)}
87
+ `;
88
+ }
89
+ if (shouldRenderBottomEllipsis) {
90
+ outputStr += `${formattedBar} ${dim("...")}
91
+ `;
92
+ }
93
+ linesRendered = 1 + // Title
94
+ 1 + // Instructions or error message
95
+ (shouldRenderTopEllipsis ? 1 : 0) + // Top ellipsis
96
+ (endIdx - startIdx + 1) + // Displayed options
97
+ (shouldRenderBottomEllipsis ? 1 : 0);
98
+ process.stdout.write(outputStr);
99
+ }
100
+ renderOptions();
101
+ return new Promise((resolve) => {
102
+ function onKeyPress(str, key) {
103
+ if (key.name === "up" || key.name === "k") {
104
+ selectedIndex = (selectedIndex - 1 + options.length) % options.length;
105
+ errorMessage = "";
106
+ renderOptions();
107
+ } else if (key.name === "down" || key.name === "j") {
108
+ selectedIndex = (selectedIndex + 1) % options.length;
109
+ errorMessage = "";
110
+ renderOptions();
111
+ } else if (key.name === "return") {
112
+ if (required && !options[selectedIndex].value) {
113
+ deleteLastLine();
114
+ errorMessage = "You must select an option.";
115
+ renderOptions();
116
+ } else {
117
+ cleanup();
118
+ resolve(options[selectedIndex].value);
119
+ deleteLastLine();
120
+ deleteLastLine();
137
121
  }
138
-
139
- input.on('keypress', onKeyPress);
140
- });
122
+ } else if (key.name === "c" && key.ctrl) {
123
+ msg({ type: "M_NEWLINE" });
124
+ msg({
125
+ type: "M_END",
126
+ title: endTitle,
127
+ titleColor: endTitleColor,
128
+ titleTypography,
129
+ titleVariant,
130
+ border,
131
+ borderColor
132
+ });
133
+ cleanup(true);
134
+ }
135
+ }
136
+ function cleanup(isCtrlC = false) {
137
+ if (typeof input.setRawMode === "function") {
138
+ input.setRawMode(false);
139
+ }
140
+ rl.close();
141
+ input.removeListener("keypress", onKeyPress);
142
+ process.stdout.write(`\x1B[${linesRendered}B`);
143
+ if (isCtrlC) {
144
+ process.exit();
145
+ } else {
146
+ console.log();
147
+ }
148
+ }
149
+ input.on("keypress", onKeyPress);
150
+ });
141
151
  }
@@ -1,39 +1,35 @@
1
- import Prompt from '../../components/prompts/prompt.js';
2
-
1
+ import Prompt from "../../components/prompts/prompt.js";
3
2
  export default class SelectPrompt extends Prompt {
4
- options;
5
- cursor = 0;
6
- get _value() {
7
- return this.options[this.cursor];
8
- }
9
-
10
- changeValue() {
11
- this.value = this._value.value;
12
- }
13
-
14
- constructor(opts) {
15
- super(opts, false);
16
- this.options = opts.options;
17
- this.cursor = this.options.findIndex(({value}) => value === opts.initialValue);
18
- if (this.cursor === -1) {
19
- this.cursor = 0;
20
- }
21
-
22
- this.changeValue();
23
- this.on('cursor', (key) => {
24
- switch(key) {
25
- case 'left':
26
- case 'up':
27
- this.cursor = !this.cursor ? this.options.length - 1 : this.cursor - 1;
28
- break;
29
-
30
- case 'down':
31
- case 'right':
32
- this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;
33
- break;
34
- }
35
-
36
- this.changeValue();
37
- });
3
+ options;
4
+ cursor = 0;
5
+ get _value() {
6
+ return this.options[this.cursor];
7
+ }
8
+ changeValue() {
9
+ this.value = this._value.value;
10
+ }
11
+ constructor(opts) {
12
+ super(opts, false);
13
+ this.options = opts.options;
14
+ this.cursor = this.options.findIndex(
15
+ ({ value }) => value === opts.initialValue
16
+ );
17
+ if (this.cursor === -1) {
18
+ this.cursor = 0;
38
19
  }
20
+ this.changeValue();
21
+ this.on("cursor", (key) => {
22
+ switch (key) {
23
+ case "left":
24
+ case "up":
25
+ this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1;
26
+ break;
27
+ case "down":
28
+ case "right":
29
+ this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;
30
+ break;
31
+ }
32
+ this.changeValue();
33
+ });
34
+ }
39
35
  }
@@ -1,91 +1,98 @@
1
- import {Value} from '@sinclair/typebox/value';
2
- import {stdout} from 'node:process';
3
- import {useKeyPress} from '../../components/core/useKeyPress.js';
4
- import {
5
- resetCursorAndClear,
6
- moveCursorAndClear,
7
- } from '../../utils/readline.js';
8
-
1
+ import { Value } from "@sinclair/typebox/value";
2
+ import { stdout } from "node:process";
3
+ import color from "picocolors";
4
+ import { useKeyPress } from "../../components/core/useKeyPress.js";
5
+ import { colorize } from "../../utils/colorize.js";
6
+ import { resetCursorAndClear, moveCursorAndClear } from "../../utils/readline.js";
9
7
  export async function selectPrompt(options) {
10
- const {
11
- choices,
12
- defaultValue,
13
- schema,
14
- } = options;
15
-
16
- if (schema) {
17
- throw Error(`Schema providing is currently not supported for selectPrompt().\n│ But don't worry, we're already handling some validations for you.`);
8
+ const {
9
+ title,
10
+ choices,
11
+ defaultValue,
12
+ schema,
13
+ titleColor = "cyanBright",
14
+ titleTypography = "bold"
15
+ } = options;
16
+ if (schema) {
17
+ throw new Error(
18
+ "Schema providing is currently not supported for selectPrompt().\n\u2502 But don't worry, we're already handling some validations for you."
19
+ );
20
+ }
21
+ if (!choices || choices.length === 0) {
22
+ throw new Error("Choices are required for select prompt.");
23
+ }
24
+ const coloredTitle = colorize(title, titleColor, titleTypography);
25
+ let selectedIndex = defaultValue !== void 0 ? choices.findIndex(
26
+ (choice, index) => choice.id === defaultValue || index + 1 === Number(defaultValue)
27
+ ) : 0;
28
+ if (selectedIndex === -1) {
29
+ selectedIndex = 0;
30
+ }
31
+ function renderChoices() {
32
+ if (!choices) {
33
+ throw new Error("Choices are required for select prompt.");
18
34
  }
19
-
20
- if (!choices || !choices.length) {
21
- throw Error('Choices are required for select prompt.');
22
- }
23
-
24
- let selectedIndex = defaultValue !== void 0 ? choices.findIndex((choice, index) => choice.id === defaultValue || Number(defaultValue) === index + 1) : 0;
25
-
26
- if (selectedIndex === -1) {
27
- selectedIndex = 0;
28
- }
29
-
30
- function renderChoices() {
31
- if (!choices) {
32
- throw Error('Choices are required for select prompt.');
33
- }
34
-
35
- resetCursorAndClear(stdout, 0, 0);
36
- }
37
-
38
- renderChoices();
39
- return new Promise((resolve, reject) => {
40
- const finalizeSelection = () => {
41
- cleanupKeyPress();
42
- moveCursorAndClear(stdout, 0, choices.length + 2);
43
- const selectedChoice = choices[selectedIndex];
44
- const selectedValue = selectedChoice?.id;
45
- let isValid = true;
46
-
47
- if (schema) {
48
- try {
49
- isValid = Value.Check(schema, selectedValue);
50
- } catch {
51
- isValid = false;
52
- }
53
- }
54
-
55
- if (isValid) {
56
- if (selectedChoice?.action) {
57
- selectedChoice
58
- .action()
59
- .then(() => resolve(selectedValue ?? ''))
60
- .catch(reject);
61
- } else {
62
- resolve(selectedValue ?? '');
63
- }
64
- } else {
65
- renderChoices();
66
- }
67
- };
68
-
69
- const handleKeyPress = (str, key) => {
70
- if (key.name === 'up') {
71
- selectedIndex = (selectedIndex - 1 + choices.length) % choices.length;
72
- renderChoices();
73
- } else if (key.name === 'down') {
74
- selectedIndex = (selectedIndex + 1) % choices.length;
75
- renderChoices();
76
- } else if (key.name === 'return') {
77
- finalizeSelection();
78
- } else if (key.ctrl && key.name === 'c') {
79
- cleanupKeyPress();
80
- } else if (!isNaN(Number(str)) && Number(str) >= 1 && Number(str) <= choices.length) {
81
- selectedIndex = Number(str) - 1;
82
- finalizeSelection();
83
- }
84
-
85
-
86
-
87
- };
88
-
89
- const cleanupKeyPress = useKeyPress(handleKeyPress);
35
+ resetCursorAndClear(stdout, 0, 0);
36
+ console.log(color.cyanBright(color.bold(coloredTitle)));
37
+ choices.forEach((choice, index) => {
38
+ const isSelected = index === selectedIndex;
39
+ const prefix = isSelected ? color.greenBright(">") : " ";
40
+ const choiceText = isSelected ? color.bgGreen(color.black(choice.title)) : choice.title;
41
+ console.log(`${prefix} ${choiceText}`);
90
42
  });
43
+ }
44
+ renderChoices();
45
+ return new Promise((resolve, reject) => {
46
+ const finalizeSelection = () => {
47
+ cleanupKeyPress();
48
+ moveCursorAndClear(stdout, 0, choices.length + 2);
49
+ const selectedChoice = choices[selectedIndex];
50
+ const selectedValue = selectedChoice?.id;
51
+ let isValid = true;
52
+ let errorMessage = "Invalid input.";
53
+ if (schema) {
54
+ try {
55
+ isValid = Value.Check(schema, selectedValue);
56
+ } catch (error) {
57
+ isValid = false;
58
+ errorMessage = "Validation error.";
59
+ console.error(error);
60
+ }
61
+ if (!isValid) {
62
+ const errors = [...Value.Errors(schema, selectedValue)];
63
+ if (errors.length > 0) {
64
+ errorMessage = errors[0]?.message ?? "Invalid input.";
65
+ }
66
+ }
67
+ }
68
+ if (isValid) {
69
+ if (selectedChoice?.action) {
70
+ selectedChoice.action().then(() => resolve(selectedValue ?? "")).catch(reject);
71
+ } else {
72
+ resolve(selectedValue ?? "");
73
+ }
74
+ } else {
75
+ console.log(errorMessage);
76
+ renderChoices();
77
+ }
78
+ };
79
+ const handleKeyPress = (str, key) => {
80
+ if (key.name === "up") {
81
+ selectedIndex = (selectedIndex - 1 + choices.length) % choices.length;
82
+ renderChoices();
83
+ } else if (key.name === "down") {
84
+ selectedIndex = (selectedIndex + 1) % choices.length;
85
+ renderChoices();
86
+ } else if (key.name === "return") {
87
+ finalizeSelection();
88
+ } else if (key.ctrl && key.name === "c") {
89
+ cleanupKeyPress();
90
+ process.exit();
91
+ } else if (!isNaN(Number(str)) && Number(str) >= 1 && Number(str) <= choices.length) {
92
+ selectedIndex = Number(str) - 1;
93
+ finalizeSelection();
94
+ }
95
+ };
96
+ const cleanupKeyPress = useKeyPress(handleKeyPress);
97
+ });
91
98
  }