@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,144 +1,111 @@
1
- import {Value} from '@sinclair/typebox/value';
1
+ import { Value } from "@sinclair/typebox/value";
2
+ import { stdin as input, stdout as output } from "node:process";
3
+ import readline from "node:readline/promises";
4
+ import { colorize } from "../../utils/colorize.js";
5
+ import { bar, fmt, msg } from "../../utils/messages.js";
2
6
  import {
3
- stdin as input,
4
- stdout as output,
5
- } from 'node:process';
6
- import readline from 'node:readline/promises';
7
- import {colorize} from '../../utils/colorize.js';
8
- import {
9
- bar,
10
- fmt,
11
- msg,
12
- } from '../../utils/messages.js';
13
- import {
14
- countLines,
15
- deleteLastLine,
16
- deleteLastLines,
17
- } from '../../utils/terminal.js';
18
-
7
+ countLines,
8
+ deleteLastLine,
9
+ deleteLastLines
10
+ } from "../../utils/terminal.js";
19
11
  export async function multiselectPrompt(options) {
20
- const {
21
- title,
22
- choices,
23
- schema,
24
- defaultValue,
25
- titleColor = 'cyanBright',
26
- titleTypography = 'bold',
12
+ const {
13
+ title,
14
+ choices,
15
+ schema,
16
+ defaultValue,
17
+ titleColor = "cyanBright",
18
+ titleTypography = "bold",
19
+ titleVariant,
20
+ hint,
21
+ content,
22
+ contentColor = "dim",
23
+ contentTypography,
24
+ contentVariant,
25
+ borderColor = "viceGradient",
26
+ variantOptions
27
+ } = options;
28
+ if (!choices || choices.length === 0) {
29
+ throw new Error("Choices are required for multiselect prompt.");
30
+ }
31
+ const rl = readline.createInterface({ input, output });
32
+ const formattedBar = bar({ borderColor });
33
+ let linesToDelete = 0;
34
+ let errorMessage = "";
35
+ try {
36
+ while (true) {
37
+ if (linesToDelete > 0) {
38
+ deleteLastLines(linesToDelete);
39
+ }
40
+ const question = fmt({
41
+ type: errorMessage !== "" ? "M_ERROR" : "M_GENERAL",
42
+ title: `${title}${defaultValue ? ` [Default: ${Array.isArray(defaultValue) ? defaultValue.join(", ") : defaultValue}]` : ""}`,
43
+ titleColor,
44
+ titleTypography,
27
45
  titleVariant,
28
- hint,
29
46
  content,
30
- contentColor = 'dim',
47
+ contentColor,
31
48
  contentTypography,
32
49
  contentVariant,
33
- borderColor = 'viceGradient',
34
- variantOptions,
35
- } = options;
36
-
37
- if (!choices || !choices.length) {
38
- throw Error('Choices are required for multiselect prompt.');
39
- }
40
-
41
- const rl = readline.createInterface({
42
- input,
43
- output,
44
- });
45
- const formattedBar = bar({
46
50
  borderColor,
47
- });
48
- let linesToDelete = 0;
49
- let errorMessage = '';
50
-
51
- try {
52
- while (true) {
53
- if (linesToDelete > 0) {
54
- deleteLastLines(linesToDelete);
55
- }
56
-
57
- const question = fmt({
58
- type: errorMessage !== '' ? 'M_ERROR' : 'M_GENERAL',
59
- title: `${title}${defaultValue ? ` [Default: ${Array.isArray(defaultValue) ? defaultValue.join(', ') : defaultValue}]` : ''}`,
60
- titleColor,
61
- titleTypography,
62
- titleVariant,
63
- content,
64
- contentColor,
65
- contentTypography,
66
- contentVariant,
67
- borderColor,
68
- hint,
69
- variantOptions,
70
- errorMessage,
71
- addNewLineBefore: false,
72
- addNewLineAfter: false,
73
- });
74
-
75
- const choicesText = choices
76
- .map((choice, index) => `${formattedBar} ${index + 1}) ${choice.title}${choice.description ? ` - ${choice.description}` : ''}`)
77
- .join('\n');
78
-
79
- const fullPrompt = `${question}
51
+ hint,
52
+ variantOptions,
53
+ errorMessage,
54
+ addNewLineBefore: false,
55
+ addNewLineAfter: false
56
+ });
57
+ const choicesText = choices.map(
58
+ (choice, index) => `${formattedBar} ${index + 1}) ${choice.title}${choice.description ? ` - ${choice.description}` : ""}`
59
+ ).join("\n");
60
+ const fullPrompt = `${question}
80
61
  ${choicesText}
81
62
  ${formattedBar} ${colorize(`Enter your choices (comma-separated numbers between 1-${choices.length})`, contentColor)}:
82
63
  ${formattedBar} `;
83
-
84
- const formattedPrompt = fmt({
85
- type: 'M_NULL',
86
- title: fullPrompt,
87
- });
88
-
89
- const questionLines = countLines(formattedPrompt);
90
-
91
- linesToDelete = questionLines + 1;
92
- const answer = (await rl.question(formattedPrompt)).trim();
93
-
94
- if (!answer && defaultValue !== void 0) {
95
- deleteLastLine();
96
- msg({
97
- type: 'M_MIDDLE',
98
- title: ` ${Array.isArray(defaultValue) ? defaultValue.join(', ') : defaultValue}`,
99
- titleColor: 'none',
100
- });
101
- msg({
102
- type: 'M_NEWLINE',
103
- });
104
- return defaultValue;
105
- }
106
-
107
- const selections = answer
108
- .split(',')
109
- .map((s) => s.trim());
110
-
111
- const invalidSelections = selections.filter((s) => {
112
- const num = Number(s);
113
- return isNaN(num) || num < 1 || num > choices.length;
114
- });
115
-
116
- if (invalidSelections.length > 0) {
117
- errorMessage = `Invalid selections: ${invalidSelections.join(', ')}. Please enter numbers between 1 and ${choices.length}.`;
118
- continue;
119
- }
120
-
121
- const selectedValues = selections.map((s) => choices[Number(s) - 1]?.id);
122
- let isValid = true;
123
-
124
- errorMessage = '';
125
- if (schema) {
126
- isValid = Value.Check(schema, selectedValues);
127
- if (!isValid) {
128
- const errors = Value.Errors(schema, selectedValues);
129
- errorMessage = errors.length > 0 ? errors[0]?.message ?? 'Invalid input.' : 'Invalid input.';
130
- }
131
- }
132
-
133
- if (isValid) {
134
- msg({
135
- type: 'M_NEWLINE',
136
- });
137
- rl.close();
138
- return selectedValues;
139
- }
64
+ const formattedPrompt = fmt({
65
+ type: "M_NULL",
66
+ title: fullPrompt
67
+ });
68
+ const questionLines = countLines(formattedPrompt);
69
+ linesToDelete = questionLines + 1;
70
+ const answer = (await rl.question(formattedPrompt)).trim();
71
+ if (!answer && defaultValue !== void 0) {
72
+ deleteLastLine();
73
+ msg({
74
+ type: "M_MIDDLE",
75
+ title: ` ${Array.isArray(defaultValue) ? defaultValue.join(", ") : defaultValue}`,
76
+ titleColor: "none"
77
+ });
78
+ msg({ type: "M_NEWLINE" });
79
+ return defaultValue;
80
+ }
81
+ const selections = answer.split(",").map((s) => s.trim());
82
+ const invalidSelections = selections.filter((s) => {
83
+ const num = Number(s);
84
+ return isNaN(num) || num < 1 || num > choices.length;
85
+ });
86
+ if (invalidSelections.length > 0) {
87
+ errorMessage = `Invalid selections: ${invalidSelections.join(
88
+ ", "
89
+ )}. Please enter numbers between 1 and ${choices.length}.`;
90
+ continue;
91
+ }
92
+ const selectedValues = selections.map((s) => choices[Number(s) - 1]?.id);
93
+ let isValid = true;
94
+ errorMessage = "";
95
+ if (schema) {
96
+ isValid = Value.Check(schema, selectedValues);
97
+ if (!isValid) {
98
+ const errors = [...Value.Errors(schema, selectedValues)];
99
+ errorMessage = errors.length > 0 ? errors[0]?.message ?? "Invalid input." : "Invalid input.";
140
100
  }
141
- } finally {
101
+ }
102
+ if (isValid) {
103
+ msg({ type: "M_NEWLINE" });
142
104
  rl.close();
105
+ return selectedValues;
106
+ }
143
107
  }
108
+ } finally {
109
+ rl.close();
110
+ }
144
111
  }
@@ -1,51 +1,45 @@
1
- import Prompt from '../../components/prompts/prompt.js';
2
-
1
+ import Prompt from "../../components/prompts/prompt.js";
3
2
  export default class MultiSelectPrompt extends Prompt {
4
- options;
5
- cursor = 0;
6
- get _value() {
7
- return this.options[this.cursor].value;
8
- }
9
-
10
- toggleAll() {
11
- const allSelected = this.value.length === this.options.length;
12
- this.value = allSelected ? [] : this.options.map((v) => v.value);
13
- }
14
-
15
- toggleValue() {
16
- const selected = this.value.includes(this._value);
17
- this.value = selected ? this.value.filter((value) => value !== this._value) : [
18
- ...this.value,
19
- this._value,
20
- ];
21
- }
22
-
23
- constructor(opts) {
24
- super(opts, false);
25
- this.options = opts.options;
26
- this.value = opts.initialValues ?? [].slice();
27
- this.cursor = Math.max(this.options.findIndex(({value}) => value === opts.cursorAt), 0);
28
- this.on('key', (char) => {
29
- if (char === 'a') {
30
- this.toggleAll();
31
- }
32
- });
33
- this.on('cursor', (key) => {
34
- switch(key) {
35
- case 'left':
36
- case 'up':
37
- this.cursor = !this.cursor ? this.options.length - 1 : this.cursor - 1;
38
- break;
39
-
40
- case 'down':
41
- case 'right':
42
- this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;
43
- break;
44
-
45
- case 'space':
46
- this.toggleValue();
47
- break;
48
- }
49
- });
50
- }
3
+ options;
4
+ cursor = 0;
5
+ get _value() {
6
+ return this.options[this.cursor].value;
7
+ }
8
+ toggleAll() {
9
+ const allSelected = this.value.length === this.options.length;
10
+ this.value = allSelected ? [] : this.options.map((v) => v.value);
11
+ }
12
+ toggleValue() {
13
+ const selected = this.value.includes(this._value);
14
+ this.value = selected ? this.value.filter((value) => value !== this._value) : [...this.value, this._value];
15
+ }
16
+ constructor(opts) {
17
+ super(opts, false);
18
+ this.options = opts.options;
19
+ this.value = [...opts.initialValues ?? []];
20
+ this.cursor = Math.max(
21
+ this.options.findIndex(({ value }) => value === opts.cursorAt),
22
+ 0
23
+ );
24
+ this.on("key", (char) => {
25
+ if (char === "a") {
26
+ this.toggleAll();
27
+ }
28
+ });
29
+ this.on("cursor", (key) => {
30
+ switch (key) {
31
+ case "left":
32
+ case "up":
33
+ this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1;
34
+ break;
35
+ case "down":
36
+ case "right":
37
+ this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;
38
+ break;
39
+ case "space":
40
+ this.toggleValue();
41
+ break;
42
+ }
43
+ });
44
+ }
51
45
  }
@@ -14,4 +14,5 @@ export declare function multiselectPrompt<T extends string>(params: {
14
14
  border?: boolean;
15
15
  endTitle?: string;
16
16
  endTitleColor?: ColorName;
17
+ maxItems?: number;
17
18
  }): Promise<T[]>;
@@ -1,153 +1,166 @@
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 {colorize} from '../../mod.js';
13
- import {
14
- bar,
15
- symbols,
16
- msg,
17
- } from '../../utils/messages.js';
18
- import {deleteLastLine} from '../../utils/terminal.js';
19
-
20
- export function multiselectPrompt(params) {
21
- const {
22
- title,
23
- options,
24
- required = false,
25
- initial = [],
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 pointer = 0;
36
- const selectedOptions = new Set(initial
37
- .map((opt) => options.findIndex((o) => o.value === opt))
38
- .filter((i) => i >= 0));
39
-
40
- const rl = readline.createInterface({
41
- input,
42
- output,
43
- });
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, <Space> to select/deselect, <Enter> to confirm, <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)} ${colorize(title, titleColor, titleTypography)}
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 { colorize } from "../../main.js";
5
+ import { bar, symbols, msg } from "../../utils/messages.js";
6
+ import { deleteLastLine } from "../../utils/terminal.js";
7
+ export async function multiselectPrompt(params) {
8
+ const {
9
+ title,
10
+ options,
11
+ required = false,
12
+ initial = [],
13
+ borderColor = "viceGradient",
14
+ titleColor = "cyanBright",
15
+ titleTypography = "bold",
16
+ titleVariant,
17
+ border = true,
18
+ endTitle = "\u{1F44B}",
19
+ endTitleColor = "passionGradient",
20
+ maxItems
21
+ } = params;
22
+ let pointer = 0;
23
+ const selectedOptions = new Set(
24
+ initial.map((opt) => options.findIndex((o) => o.value === opt)).filter((i) => i >= 0)
25
+ );
26
+ const rl = readline.createInterface({ input, output });
27
+ readline.emitKeypressEvents(input, rl);
28
+ if (typeof input.setRawMode === "function") {
29
+ input.setRawMode(true);
30
+ }
31
+ const formattedBar = bar({ borderColor });
32
+ let linesRendered = 0;
33
+ const instructions = `Use <\u2191/\u2193> or <k/j> to navigate, <Space> to select/deselect, <Enter> to confirm, <Ctrl+C> to exit`;
34
+ let errorMessage = "";
35
+ function renderOptions() {
36
+ if (linesRendered > 0) {
37
+ process.stdout.write(`\x1B[${linesRendered}A`);
38
+ }
39
+ let outputStr = `${greenBright(symbols.step_active)} ${colorize(title, titleColor, titleTypography)}
60
40
  `;
61
-
62
- if (errorMessage) {
63
- outputStr += redBright(`${symbols.step_error} ${errorMessage}
41
+ if (errorMessage) {
42
+ outputStr += redBright(`${symbols.step_error} ${errorMessage}
64
43
  `);
65
- } else {
66
- outputStr += `${formattedBar} ${dim(instructions)}
44
+ } else {
45
+ outputStr += `${formattedBar} ${dim(instructions)}
67
46
  `;
68
- }
69
-
70
- for (const [index, option] of options.entries()) {
71
- const isSelected = selectedOptions.has(index);
72
- const isHighlighted = index === pointer;
73
- const checkbox = isSelected ? '[x]' : '[ ]';
74
- const prefix = isHighlighted ? '> ' : ' ';
75
- const optionLabel = isHighlighted ? cyanBright(option.value) : option.value;
76
- const hint = option.hint ? ` (${option.hint})` : '';
77
-
78
- outputStr += `${formattedBar} ${prefix}${checkbox} ${optionLabel}${dim(hint)}
47
+ }
48
+ const terminalHeight = process.stdout.rows || 24;
49
+ const availableHeight = terminalHeight - 4;
50
+ const computedMaxItems = Math.min(
51
+ maxItems ?? Infinity,
52
+ availableHeight > 0 ? availableHeight : Infinity,
53
+ options.length
54
+ );
55
+ const minItems = 3;
56
+ const displayItems = Math.max(computedMaxItems, minItems);
57
+ let startIdx = 0;
58
+ let endIdx = options.length - 1;
59
+ if (options.length > displayItems) {
60
+ const half = Math.floor(displayItems / 2);
61
+ startIdx = pointer - half;
62
+ endIdx = pointer + (displayItems - half - 1);
63
+ if (startIdx < 0) {
64
+ startIdx = 0;
65
+ endIdx = displayItems - 1;
66
+ } else if (endIdx >= options.length) {
67
+ endIdx = options.length - 1;
68
+ startIdx = options.length - displayItems;
69
+ }
70
+ }
71
+ const shouldRenderTopEllipsis = startIdx > 0;
72
+ const shouldRenderBottomEllipsis = endIdx < options.length - 1;
73
+ if (shouldRenderTopEllipsis) {
74
+ outputStr += `${formattedBar} ${dim("...")}
79
75
  `;
80
- }
81
-
82
- process.stdout.write(outputStr);
83
- linesRendered = 1 + 1 + options.length;
84
76
  }
85
-
86
- renderOptions();
87
- return new Promise((resolve) => {
88
- function onKeyPress(str, key) {
89
- if (key.name === 'up' || key.name === 'k') {
90
- pointer = (pointer - 1 + options.length) % options.length;
91
- errorMessage = '';
92
- renderOptions();
93
- } else if (key.name === 'down' || key.name === 'j') {
94
- pointer = (pointer + 1) % options.length;
95
- errorMessage = '';
96
- renderOptions();
97
- } else if (key.name === 'space') {
98
- if (selectedOptions.has(pointer)) {
99
- selectedOptions.delete(pointer);
100
- } else {
101
- selectedOptions.add(pointer);
102
- }
103
-
104
- errorMessage = '';
105
- renderOptions();
106
- } else if (key.name === 'return') {
107
- if (!required || selectedOptions.size > 0) {
108
- const selectedValues = Array
109
- .from(selectedOptions)
110
- .map((index) => options[index].value);
111
-
112
- cleanup();
113
- resolve(selectedValues);
114
- deleteLastLine();
115
- deleteLastLine();
116
- msg({
117
- type: 'M_MIDDLE',
118
- });
119
- } else {
120
- deleteLastLine();
121
- errorMessage = 'You must select at least one option.\x1B[K';
122
- renderOptions();
123
- }
124
- } else if (key.name === 'c' && key.ctrl) {
125
- msg({
126
- type: 'M_NEWLINE',
127
- });
128
- msg({
129
- type: 'M_END',
130
- title: endTitle,
131
- titleColor: endTitleColor,
132
- titleTypography,
133
- titleVariant,
134
- border,
135
- borderColor,
136
- });
137
- cleanup();
138
- }
139
-
140
-
141
-
77
+ for (let index = startIdx; index <= endIdx; index++) {
78
+ const option = options[index];
79
+ const isSelected = selectedOptions.has(index);
80
+ const isHighlighted = index === pointer;
81
+ const checkbox = isSelected ? "[x]" : "[ ]";
82
+ const prefix = isHighlighted ? "> " : " ";
83
+ const optionLabel = isHighlighted ? cyanBright(option.value) : option.value;
84
+ const hint = option.hint ? ` (${option.hint})` : "";
85
+ outputStr += `${formattedBar} ${prefix}${checkbox} ${optionLabel}${dim(hint)}
86
+ `;
87
+ }
88
+ if (shouldRenderBottomEllipsis) {
89
+ outputStr += `${formattedBar} ${dim("...")}
90
+ `;
91
+ }
92
+ process.stdout.write(outputStr);
93
+ linesRendered = 1 + // Symbol + Title
94
+ 1 + // Instructions or error message
95
+ (shouldRenderTopEllipsis ? 1 : 0) + // Top ellipsis
96
+ (endIdx - startIdx + 1) + // Displayed options
97
+ (shouldRenderBottomEllipsis ? 1 : 0);
98
+ }
99
+ renderOptions();
100
+ return new Promise((resolve) => {
101
+ function onKeyPress(str, key) {
102
+ if (key.name === "up" || key.name === "k") {
103
+ pointer = (pointer - 1 + options.length) % options.length;
104
+ errorMessage = "";
105
+ renderOptions();
106
+ } else if (key.name === "down" || key.name === "j") {
107
+ pointer = (pointer + 1) % options.length;
108
+ errorMessage = "";
109
+ renderOptions();
110
+ } else if (key.name === "space") {
111
+ if (selectedOptions.has(pointer)) {
112
+ selectedOptions.delete(pointer);
113
+ } else {
114
+ selectedOptions.add(pointer);
142
115
  }
143
-
144
- function cleanup() {
145
- input.setRawMode?.(false);
146
- rl.close();
147
- input.removeListener('keypress', onKeyPress);
148
- process.stdout.write(`\x1B[${linesRendered}B`);
116
+ errorMessage = "";
117
+ renderOptions();
118
+ } else if (key.name === "return") {
119
+ if (!required || selectedOptions.size > 0) {
120
+ const selectedValues = Array.from(selectedOptions).map(
121
+ (index) => options[index].value
122
+ );
123
+ cleanup();
124
+ resolve(selectedValues);
125
+ deleteLastLine();
126
+ deleteLastLine();
127
+ msg({
128
+ type: "M_MIDDLE"
129
+ });
130
+ } else {
131
+ deleteLastLine();
132
+ errorMessage = "You must select at least one option.\x1B[K";
133
+ renderOptions();
149
134
  }
150
-
151
- input.on('keypress', onKeyPress);
152
- });
135
+ } else if (key.name === "c" && key.ctrl) {
136
+ msg({
137
+ type: "M_NEWLINE"
138
+ });
139
+ msg({
140
+ type: "M_END",
141
+ title: endTitle,
142
+ titleColor: endTitleColor,
143
+ titleTypography,
144
+ titleVariant,
145
+ border,
146
+ borderColor
147
+ });
148
+ cleanup(true);
149
+ }
150
+ }
151
+ function cleanup(isCtrlC = false) {
152
+ if (typeof input.setRawMode === "function") {
153
+ input.setRawMode(false);
154
+ }
155
+ rl.close();
156
+ input.removeListener("keypress", onKeyPress);
157
+ process.stdout.write(`\x1B[${linesRendered}B`);
158
+ if (isCtrlC) {
159
+ process.exit();
160
+ } else {
161
+ console.log();
162
+ }
163
+ }
164
+ input.on("keypress", onKeyPress);
165
+ });
153
166
  }