cli-menu-kit 0.1.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 (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +716 -0
  3. package/dist/api.d.ts +158 -0
  4. package/dist/api.js +128 -0
  5. package/dist/components/display/headers.d.ts +32 -0
  6. package/dist/components/display/headers.js +84 -0
  7. package/dist/components/display/index.d.ts +8 -0
  8. package/dist/components/display/index.js +31 -0
  9. package/dist/components/display/messages.d.ts +41 -0
  10. package/dist/components/display/messages.js +90 -0
  11. package/dist/components/display/progress.d.ts +41 -0
  12. package/dist/components/display/progress.js +82 -0
  13. package/dist/components/display/summary.d.ts +33 -0
  14. package/dist/components/display/summary.js +82 -0
  15. package/dist/components/inputs/index.d.ts +8 -0
  16. package/dist/components/inputs/index.js +15 -0
  17. package/dist/components/inputs/language-input.d.ts +11 -0
  18. package/dist/components/inputs/language-input.js +104 -0
  19. package/dist/components/inputs/modify-field.d.ts +11 -0
  20. package/dist/components/inputs/modify-field.js +42 -0
  21. package/dist/components/inputs/number-input.d.ts +11 -0
  22. package/dist/components/inputs/number-input.js +143 -0
  23. package/dist/components/inputs/text-input.d.ts +11 -0
  24. package/dist/components/inputs/text-input.js +114 -0
  25. package/dist/components/menus/boolean-menu.d.ts +11 -0
  26. package/dist/components/menus/boolean-menu.js +169 -0
  27. package/dist/components/menus/checkbox-menu.d.ts +11 -0
  28. package/dist/components/menus/checkbox-menu.js +161 -0
  29. package/dist/components/menus/index.d.ts +7 -0
  30. package/dist/components/menus/index.js +13 -0
  31. package/dist/components/menus/radio-menu.d.ts +11 -0
  32. package/dist/components/menus/radio-menu.js +158 -0
  33. package/dist/components.d.ts +55 -0
  34. package/dist/components.js +166 -0
  35. package/dist/core/colors.d.ts +64 -0
  36. package/dist/core/colors.js +139 -0
  37. package/dist/core/keyboard.d.ts +124 -0
  38. package/dist/core/keyboard.js +185 -0
  39. package/dist/core/renderer.d.ts +74 -0
  40. package/dist/core/renderer.js +217 -0
  41. package/dist/core/terminal.d.ts +89 -0
  42. package/dist/core/terminal.js +170 -0
  43. package/dist/features/commands.d.ts +57 -0
  44. package/dist/features/commands.js +161 -0
  45. package/dist/features/wizard.d.ts +62 -0
  46. package/dist/features/wizard.js +112 -0
  47. package/dist/i18n/languages/en.d.ts +5 -0
  48. package/dist/i18n/languages/en.js +54 -0
  49. package/dist/i18n/languages/zh.d.ts +5 -0
  50. package/dist/i18n/languages/zh.js +54 -0
  51. package/dist/i18n/registry.d.ts +36 -0
  52. package/dist/i18n/registry.js +82 -0
  53. package/dist/i18n/types.d.ts +65 -0
  54. package/dist/i18n/types.js +5 -0
  55. package/dist/index.d.ts +27 -0
  56. package/dist/index.js +104 -0
  57. package/dist/input.d.ts +40 -0
  58. package/dist/input.js +211 -0
  59. package/dist/menu-core.d.ts +52 -0
  60. package/dist/menu-core.js +201 -0
  61. package/dist/menu-multi.d.ts +21 -0
  62. package/dist/menu-multi.js +119 -0
  63. package/dist/menu-single.d.ts +18 -0
  64. package/dist/menu-single.js +138 -0
  65. package/dist/menu.d.ts +66 -0
  66. package/dist/menu.js +78 -0
  67. package/dist/types/display.types.d.ts +57 -0
  68. package/dist/types/display.types.js +5 -0
  69. package/dist/types/input.types.d.ts +88 -0
  70. package/dist/types/input.types.js +5 -0
  71. package/dist/types/layout.types.d.ts +56 -0
  72. package/dist/types/layout.types.js +36 -0
  73. package/dist/types/menu.types.d.ts +85 -0
  74. package/dist/types/menu.types.js +5 -0
  75. package/dist/types.d.ts +49 -0
  76. package/dist/types.js +5 -0
  77. package/package.json +35 -0
package/dist/index.js ADDED
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ /**
3
+ * CLI Menu Kit - Main Entry Point
4
+ * A comprehensive, modular menu system for Node.js CLI applications
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
18
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
19
+ };
20
+ var __importDefault = (this && this.__importDefault) || function (mod) {
21
+ return (mod && mod.__esModule) ? mod : { "default": mod };
22
+ };
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.applyGradient = exports.createGradient = exports.colors = exports.getCurrentLanguageMap = exports.getAvailableLanguages = exports.registerLanguage = exports.t = exports.setLanguage = exports.getCurrentLanguage = exports.showCommandHelp = exports.getAvailableCommands = exports.handleCommand = exports.parseCommand = exports.isCommand = exports.clearCustomCommands = exports.unregisterCommand = exports.registerCommand = exports.createWizard = exports.runWizard = exports.createSimpleSummary = exports.createSummaryTable = exports.renderSummaryTable = exports.createMessage = exports.showQuestion = exports.showInfo = exports.showWarning = exports.showError = exports.showSuccess = exports.renderMessage = exports.createStageSeparator = exports.createStageHeader = exports.createProgressIndicator = exports.renderStageSeparator = exports.renderStageHeader = exports.renderProgressIndicator = exports.createAsciiHeader = exports.createSimpleHeader = exports.renderAsciiHeader = exports.renderSimpleHeader = exports.showModifyField = exports.showLanguageSelector = exports.showNumberInput = exports.showTextInput = exports.showBooleanMenu = exports.showCheckboxMenu = exports.showRadioMenu = exports.default = exports.wizard = exports.input = exports.menu = void 0;
25
+ exports.LAYOUT_PRESETS = exports.KEY_CODES = exports.colorize = void 0;
26
+ // Export unified API
27
+ var api_js_1 = require("./api.js");
28
+ Object.defineProperty(exports, "menu", { enumerable: true, get: function () { return api_js_1.menuAPI; } });
29
+ Object.defineProperty(exports, "input", { enumerable: true, get: function () { return api_js_1.inputAPI; } });
30
+ Object.defineProperty(exports, "wizard", { enumerable: true, get: function () { return api_js_1.wizardAPI; } });
31
+ var api_js_2 = require("./api.js");
32
+ Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(api_js_2).default; } });
33
+ // Export menu components
34
+ var index_js_1 = require("./components/menus/index.js");
35
+ Object.defineProperty(exports, "showRadioMenu", { enumerable: true, get: function () { return index_js_1.showRadioMenu; } });
36
+ Object.defineProperty(exports, "showCheckboxMenu", { enumerable: true, get: function () { return index_js_1.showCheckboxMenu; } });
37
+ Object.defineProperty(exports, "showBooleanMenu", { enumerable: true, get: function () { return index_js_1.showBooleanMenu; } });
38
+ // Export input components
39
+ var index_js_2 = require("./components/inputs/index.js");
40
+ Object.defineProperty(exports, "showTextInput", { enumerable: true, get: function () { return index_js_2.showTextInput; } });
41
+ Object.defineProperty(exports, "showNumberInput", { enumerable: true, get: function () { return index_js_2.showNumberInput; } });
42
+ Object.defineProperty(exports, "showLanguageSelector", { enumerable: true, get: function () { return index_js_2.showLanguageSelector; } });
43
+ Object.defineProperty(exports, "showModifyField", { enumerable: true, get: function () { return index_js_2.showModifyField; } });
44
+ // Export display components
45
+ var index_js_3 = require("./components/display/index.js");
46
+ Object.defineProperty(exports, "renderSimpleHeader", { enumerable: true, get: function () { return index_js_3.renderSimpleHeader; } });
47
+ Object.defineProperty(exports, "renderAsciiHeader", { enumerable: true, get: function () { return index_js_3.renderAsciiHeader; } });
48
+ Object.defineProperty(exports, "createSimpleHeader", { enumerable: true, get: function () { return index_js_3.createSimpleHeader; } });
49
+ Object.defineProperty(exports, "createAsciiHeader", { enumerable: true, get: function () { return index_js_3.createAsciiHeader; } });
50
+ Object.defineProperty(exports, "renderProgressIndicator", { enumerable: true, get: function () { return index_js_3.renderProgressIndicator; } });
51
+ Object.defineProperty(exports, "renderStageHeader", { enumerable: true, get: function () { return index_js_3.renderStageHeader; } });
52
+ Object.defineProperty(exports, "renderStageSeparator", { enumerable: true, get: function () { return index_js_3.renderStageSeparator; } });
53
+ Object.defineProperty(exports, "createProgressIndicator", { enumerable: true, get: function () { return index_js_3.createProgressIndicator; } });
54
+ Object.defineProperty(exports, "createStageHeader", { enumerable: true, get: function () { return index_js_3.createStageHeader; } });
55
+ Object.defineProperty(exports, "createStageSeparator", { enumerable: true, get: function () { return index_js_3.createStageSeparator; } });
56
+ Object.defineProperty(exports, "renderMessage", { enumerable: true, get: function () { return index_js_3.renderMessage; } });
57
+ Object.defineProperty(exports, "showSuccess", { enumerable: true, get: function () { return index_js_3.showSuccess; } });
58
+ Object.defineProperty(exports, "showError", { enumerable: true, get: function () { return index_js_3.showError; } });
59
+ Object.defineProperty(exports, "showWarning", { enumerable: true, get: function () { return index_js_3.showWarning; } });
60
+ Object.defineProperty(exports, "showInfo", { enumerable: true, get: function () { return index_js_3.showInfo; } });
61
+ Object.defineProperty(exports, "showQuestion", { enumerable: true, get: function () { return index_js_3.showQuestion; } });
62
+ Object.defineProperty(exports, "createMessage", { enumerable: true, get: function () { return index_js_3.createMessage; } });
63
+ Object.defineProperty(exports, "renderSummaryTable", { enumerable: true, get: function () { return index_js_3.renderSummaryTable; } });
64
+ Object.defineProperty(exports, "createSummaryTable", { enumerable: true, get: function () { return index_js_3.createSummaryTable; } });
65
+ Object.defineProperty(exports, "createSimpleSummary", { enumerable: true, get: function () { return index_js_3.createSimpleSummary; } });
66
+ // Export features
67
+ var wizard_js_1 = require("./features/wizard.js");
68
+ Object.defineProperty(exports, "runWizard", { enumerable: true, get: function () { return wizard_js_1.runWizard; } });
69
+ Object.defineProperty(exports, "createWizard", { enumerable: true, get: function () { return wizard_js_1.createWizard; } });
70
+ var commands_js_1 = require("./features/commands.js");
71
+ Object.defineProperty(exports, "registerCommand", { enumerable: true, get: function () { return commands_js_1.registerCommand; } });
72
+ Object.defineProperty(exports, "unregisterCommand", { enumerable: true, get: function () { return commands_js_1.unregisterCommand; } });
73
+ Object.defineProperty(exports, "clearCustomCommands", { enumerable: true, get: function () { return commands_js_1.clearCustomCommands; } });
74
+ Object.defineProperty(exports, "isCommand", { enumerable: true, get: function () { return commands_js_1.isCommand; } });
75
+ Object.defineProperty(exports, "parseCommand", { enumerable: true, get: function () { return commands_js_1.parseCommand; } });
76
+ Object.defineProperty(exports, "handleCommand", { enumerable: true, get: function () { return commands_js_1.handleCommand; } });
77
+ Object.defineProperty(exports, "getAvailableCommands", { enumerable: true, get: function () { return commands_js_1.getAvailableCommands; } });
78
+ Object.defineProperty(exports, "showCommandHelp", { enumerable: true, get: function () { return commands_js_1.showCommandHelp; } });
79
+ // Export i18n
80
+ var registry_js_1 = require("./i18n/registry.js");
81
+ Object.defineProperty(exports, "getCurrentLanguage", { enumerable: true, get: function () { return registry_js_1.getCurrentLanguage; } });
82
+ Object.defineProperty(exports, "setLanguage", { enumerable: true, get: function () { return registry_js_1.setLanguage; } });
83
+ Object.defineProperty(exports, "t", { enumerable: true, get: function () { return registry_js_1.t; } });
84
+ Object.defineProperty(exports, "registerLanguage", { enumerable: true, get: function () { return registry_js_1.registerLanguage; } });
85
+ Object.defineProperty(exports, "getAvailableLanguages", { enumerable: true, get: function () { return registry_js_1.getAvailableLanguages; } });
86
+ Object.defineProperty(exports, "getCurrentLanguageMap", { enumerable: true, get: function () { return registry_js_1.getCurrentLanguageMap; } });
87
+ // Export core utilities (for advanced users)
88
+ var colors_js_1 = require("./core/colors.js");
89
+ Object.defineProperty(exports, "colors", { enumerable: true, get: function () { return colors_js_1.colors; } });
90
+ Object.defineProperty(exports, "createGradient", { enumerable: true, get: function () { return colors_js_1.createGradient; } });
91
+ Object.defineProperty(exports, "applyGradient", { enumerable: true, get: function () { return colors_js_1.applyGradient; } });
92
+ Object.defineProperty(exports, "colorize", { enumerable: true, get: function () { return colors_js_1.colorize; } });
93
+ var keyboard_js_1 = require("./core/keyboard.js");
94
+ Object.defineProperty(exports, "KEY_CODES", { enumerable: true, get: function () { return keyboard_js_1.KEY_CODES; } });
95
+ var layout_types_js_1 = require("./types/layout.types.js");
96
+ Object.defineProperty(exports, "LAYOUT_PRESETS", { enumerable: true, get: function () { return layout_types_js_1.LAYOUT_PRESETS; } });
97
+ // Legacy exports (for backward compatibility)
98
+ __exportStar(require("./types"), exports);
99
+ __exportStar(require("./components"), exports);
100
+ __exportStar(require("./menu-core"), exports);
101
+ __exportStar(require("./menu-single"), exports);
102
+ __exportStar(require("./menu-multi"), exports);
103
+ __exportStar(require("./input"), exports);
104
+ __exportStar(require("./menu"), exports);
@@ -0,0 +1,40 @@
1
+ /**
2
+ * CLI Menu Kit - Input Components
3
+ * Handles user input interactions
4
+ */
5
+ export interface InputOptions {
6
+ lang?: 'zh' | 'en';
7
+ defaultValue?: string;
8
+ indent?: string;
9
+ validator?: (input: string) => boolean | string;
10
+ }
11
+ /**
12
+ * Ask for user text input with optional default value and validation
13
+ *
14
+ * @param prompt - Prompt text
15
+ * @param options - Input options
16
+ * @returns User input or default value
17
+ */
18
+ export declare function askInput(prompt: string, options?: InputOptions): Promise<string>;
19
+ /**
20
+ * Ask Yes/No question with interactive selection
21
+ *
22
+ * @param prompt - Question prompt
23
+ * @param options - Options
24
+ * @returns true for Yes, false for No
25
+ */
26
+ export declare function askYesNo(prompt: string, options?: {
27
+ lang?: 'zh' | 'en';
28
+ defaultYes?: boolean;
29
+ }): Promise<boolean>;
30
+ /**
31
+ * Ask for number input with validation
32
+ *
33
+ * @param prompt - Prompt text
34
+ * @param options - Input options
35
+ * @returns Number input
36
+ */
37
+ export declare function askNumber(prompt: string, options?: InputOptions & {
38
+ min?: number;
39
+ max?: number;
40
+ }): Promise<number>;
package/dist/input.js ADDED
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ /**
3
+ * CLI Menu Kit - Input Components
4
+ * Handles user input interactions
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.askInput = askInput;
8
+ exports.askYesNo = askYesNo;
9
+ exports.askNumber = askNumber;
10
+ const components_1 = require("./components");
11
+ /**
12
+ * Ask for user text input with optional default value and validation
13
+ *
14
+ * @param prompt - Prompt text
15
+ * @param options - Input options
16
+ * @returns User input or default value
17
+ */
18
+ async function askInput(prompt, options = {}) {
19
+ const { lang = 'zh', defaultValue = '', indent = ' ', validator } = options;
20
+ return new Promise((resolve) => {
21
+ const stdin = process.stdin;
22
+ const stdout = process.stdout;
23
+ // Show prompt with grayed default value hint
24
+ const promptLine = `${indent}${prompt}: `;
25
+ const defaultHint = defaultValue
26
+ ? `${components_1.theme.muted}(${lang === 'zh' ? '默认' : 'default'}: ${defaultValue})${components_1.colors.reset} `
27
+ : '';
28
+ stdout.write(promptLine);
29
+ stdout.write(defaultHint);
30
+ // Hide cursor to avoid visual artifacts
31
+ stdout.write('\x1b[?25l');
32
+ let input = '';
33
+ stdin.setRawMode(true);
34
+ stdin.resume();
35
+ stdin.setEncoding('utf8');
36
+ const onData = (key) => {
37
+ // Ctrl+C
38
+ if (key === '\u0003') {
39
+ stdin.setRawMode(false);
40
+ stdin.removeListener('data', onData);
41
+ // Clear current line
42
+ stdout.write('\r\x1b[K');
43
+ // Show cursor
44
+ stdout.write('\x1b[?25h');
45
+ // Show goodbye message
46
+ (0, components_1.showGoodbye)(lang);
47
+ process.exit(0);
48
+ }
49
+ // Enter
50
+ if (key === '\r' || key === '\n') {
51
+ const result = input.trim() || defaultValue;
52
+ // Validate if validator provided
53
+ if (validator) {
54
+ const validationResult = validator(result);
55
+ if (validationResult !== true) {
56
+ // Show error and continue input
57
+ const errorMsg = typeof validationResult === 'string'
58
+ ? validationResult
59
+ : (lang === 'zh' ? '输入无效' : 'Invalid input');
60
+ stdout.write(`\n${indent}${components_1.colors.red}${errorMsg}${components_1.colors.reset}\n`);
61
+ stdout.write(promptLine);
62
+ stdout.write(defaultHint);
63
+ stdout.write(' ');
64
+ stdout.write(input);
65
+ return;
66
+ }
67
+ }
68
+ stdin.setRawMode(false);
69
+ stdin.removeListener('data', onData);
70
+ // Clear the entire input line
71
+ stdout.write('\r\x1b[K');
72
+ // Show cursor
73
+ stdout.write('\x1b[?25h');
74
+ resolve(result);
75
+ return;
76
+ }
77
+ // Backspace / Delete
78
+ if (key === '\u007F' || key === '\b') {
79
+ if (input.length > 0) {
80
+ input = input.slice(0, -1);
81
+ // Clear line and redraw
82
+ stdout.write('\r\x1b[K');
83
+ stdout.write(promptLine);
84
+ stdout.write(defaultHint);
85
+ stdout.write(' ');
86
+ stdout.write(input);
87
+ }
88
+ return;
89
+ }
90
+ // Ignore escape sequences (arrow keys, etc.)
91
+ if (key.charCodeAt(0) === 27) {
92
+ return;
93
+ }
94
+ // Normal character input
95
+ if (key.charCodeAt(0) >= 32) {
96
+ input += key;
97
+ // Clear line and redraw to handle multi-byte characters properly
98
+ stdout.write('\r\x1b[K');
99
+ stdout.write(promptLine);
100
+ stdout.write(defaultHint);
101
+ stdout.write(' ');
102
+ stdout.write(input);
103
+ }
104
+ };
105
+ stdin.on('data', onData);
106
+ });
107
+ }
108
+ /**
109
+ * Ask Yes/No question with interactive selection
110
+ *
111
+ * @param prompt - Question prompt
112
+ * @param options - Options
113
+ * @returns true for Yes, false for No
114
+ */
115
+ async function askYesNo(prompt, options = {}) {
116
+ const { lang = 'zh', defaultYes = true } = options;
117
+ const optionLabels = lang === 'zh' ? ['是', '否'] : ['Yes', 'No'];
118
+ return new Promise((resolve) => {
119
+ let selectedIndex = defaultYes ? 0 : 1;
120
+ const stdin = process.stdin;
121
+ stdin.setRawMode(true);
122
+ stdin.resume();
123
+ stdin.setEncoding('utf8');
124
+ // Hide cursor
125
+ process.stdout.write('\x1b[?25l');
126
+ const render = () => {
127
+ // Clear current line
128
+ process.stdout.write('\r\x1b[K');
129
+ // Render prompt with question symbol
130
+ process.stdout.write(` ${components_1.symbols.warning.color}${components_1.symbols.warning.icon} ${components_1.colors.reset}${prompt} `);
131
+ // Render options
132
+ optionLabels.forEach((option, index) => {
133
+ const isSelected = index === selectedIndex;
134
+ if (isSelected) {
135
+ process.stdout.write(`${components_1.theme.active}${option}${components_1.colors.reset}`);
136
+ }
137
+ else {
138
+ process.stdout.write(`${components_1.theme.muted}${option}${components_1.colors.reset}`);
139
+ }
140
+ if (index < optionLabels.length - 1) {
141
+ process.stdout.write(` ${components_1.theme.muted}|${components_1.colors.reset} `);
142
+ }
143
+ });
144
+ };
145
+ const cleanup = (result) => {
146
+ stdin.setRawMode(false);
147
+ stdin.removeListener('data', onKeyPress);
148
+ process.stdout.write('\r\x1b[K'); // Clear current line
149
+ process.stdout.write('\x1b[?25h'); // Show cursor
150
+ resolve(result);
151
+ };
152
+ const onKeyPress = (key) => {
153
+ if (key === '\u001b[C') { // Right arrow
154
+ selectedIndex = (selectedIndex + 1) % optionLabels.length;
155
+ render();
156
+ }
157
+ else if (key === '\u001b[D') { // Left arrow
158
+ selectedIndex = (selectedIndex - 1 + optionLabels.length) % optionLabels.length;
159
+ render();
160
+ }
161
+ else if (key === '\r') { // Enter
162
+ cleanup(selectedIndex === 0); // 0 = Yes/是, 1 = No/否
163
+ }
164
+ else if (key === '\u001b' || key === '\u001b[') { // Esc
165
+ cleanup(false); // Default to No on Esc
166
+ }
167
+ else if (key === '\u0003') { // Ctrl+C
168
+ stdin.setRawMode(false);
169
+ stdin.removeListener('data', onKeyPress);
170
+ // Clear current line
171
+ process.stdout.write('\r\x1b[K');
172
+ // Show cursor
173
+ process.stdout.write('\x1b[?25h');
174
+ // Show goodbye message
175
+ (0, components_1.showGoodbye)(lang);
176
+ process.exit(0);
177
+ }
178
+ else {
179
+ // Ignore all other keys - re-render to clear any echo
180
+ render();
181
+ }
182
+ };
183
+ stdin.on('data', onKeyPress);
184
+ render();
185
+ });
186
+ }
187
+ /**
188
+ * Ask for number input with validation
189
+ *
190
+ * @param prompt - Prompt text
191
+ * @param options - Input options
192
+ * @returns Number input
193
+ */
194
+ async function askNumber(prompt, options = {}) {
195
+ const { lang = 'zh', min, max } = options;
196
+ const validator = (input) => {
197
+ const num = parseFloat(input);
198
+ if (isNaN(num)) {
199
+ return lang === 'zh' ? '请输入有效的数字' : 'Please enter a valid number';
200
+ }
201
+ if (min !== undefined && num < min) {
202
+ return lang === 'zh' ? `数字不能小于 ${min}` : `Number cannot be less than ${min}`;
203
+ }
204
+ if (max !== undefined && num > max) {
205
+ return lang === 'zh' ? `数字不能大于 ${max}` : `Number cannot be greater than ${max}`;
206
+ }
207
+ return true;
208
+ };
209
+ const result = await askInput(prompt, { ...options, validator });
210
+ return parseFloat(result);
211
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * CLI Menu Kit - Core Utilities
3
+ * Shared rendering and terminal control functions
4
+ */
5
+ /**
6
+ * Terminal control utilities
7
+ */
8
+ export interface TerminalState {
9
+ stdin: NodeJS.ReadStream;
10
+ renderedLines: number;
11
+ isFirstRender: boolean;
12
+ }
13
+ export declare function setupTerminal(): TerminalState;
14
+ export declare function cleanupTerminal(state: TerminalState, onData: (key: string) => void): void;
15
+ export declare function moveCursorUp(state: TerminalState): void;
16
+ /**
17
+ * Header rendering
18
+ */
19
+ export declare function renderHeader(title: string | undefined): number;
20
+ /**
21
+ * Option rendering for single-select menus
22
+ */
23
+ export interface RenderOptionConfig {
24
+ index: number;
25
+ isSelected: boolean;
26
+ showNumber?: boolean;
27
+ }
28
+ export declare function renderSingleOption(option: string | {
29
+ label: string;
30
+ value?: any;
31
+ }, config: RenderOptionConfig): void;
32
+ /**
33
+ * Keyboard navigation helpers
34
+ */
35
+ export declare function handleVerticalNavigation(key: string, currentIndex: number, maxIndex: number): number | null;
36
+ export declare function handleNumberInput(key: string, maxNumber: number): number | null;
37
+ export declare function handleLetterInput(key: string, options: Array<string | {
38
+ label: string;
39
+ value?: any;
40
+ }>): number | null;
41
+ /**
42
+ * Input prompt rendering
43
+ */
44
+ export declare function renderInputPrompt(text: string, showCursor?: boolean): void;
45
+ /**
46
+ * Hints rendering
47
+ */
48
+ export declare function renderHints(hintText: string): number;
49
+ /**
50
+ * Graceful exit with goodbye message
51
+ */
52
+ export declare function exitWithGoodbye(state: TerminalState, onData: (key: string) => void, showGoodbyeFn: () => void): void;
@@ -0,0 +1,201 @@
1
+ "use strict";
2
+ /**
3
+ * CLI Menu Kit - Core Utilities
4
+ * Shared rendering and terminal control functions
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.setupTerminal = setupTerminal;
8
+ exports.cleanupTerminal = cleanupTerminal;
9
+ exports.moveCursorUp = moveCursorUp;
10
+ exports.renderHeader = renderHeader;
11
+ exports.renderSingleOption = renderSingleOption;
12
+ exports.handleVerticalNavigation = handleVerticalNavigation;
13
+ exports.handleNumberInput = handleNumberInput;
14
+ exports.handleLetterInput = handleLetterInput;
15
+ exports.renderInputPrompt = renderInputPrompt;
16
+ exports.renderHints = renderHints;
17
+ exports.exitWithGoodbye = exitWithGoodbye;
18
+ const components_1 = require("./components");
19
+ function setupTerminal() {
20
+ const stdin = process.stdin;
21
+ stdin.setRawMode(true);
22
+ stdin.resume();
23
+ stdin.setEncoding('utf8');
24
+ // Hide cursor
25
+ process.stdout.write('\x1b[?25l');
26
+ return {
27
+ stdin,
28
+ renderedLines: 0,
29
+ isFirstRender: true
30
+ };
31
+ }
32
+ function cleanupTerminal(state, onData) {
33
+ state.stdin.setRawMode(false);
34
+ state.stdin.removeListener('data', onData);
35
+ // Clear rendered content
36
+ if (state.renderedLines > 0) {
37
+ process.stdout.write(`\x1b[${state.renderedLines}A`);
38
+ process.stdout.write('\x1b[J');
39
+ }
40
+ // Show cursor
41
+ process.stdout.write('\x1b[?25h');
42
+ }
43
+ function moveCursorUp(state) {
44
+ if (!state.isFirstRender && state.renderedLines > 0) {
45
+ process.stdout.write(`\x1b[${state.renderedLines}A`);
46
+ }
47
+ }
48
+ /**
49
+ * Header rendering
50
+ */
51
+ function renderHeader(title) {
52
+ if (!title)
53
+ return 0;
54
+ const titleLines = title.split('\n');
55
+ titleLines.forEach(line => {
56
+ process.stdout.write('\x1b[2K\r');
57
+ console.log(` ${components_1.theme.primary}${line}${components_1.colors.reset}`);
58
+ });
59
+ process.stdout.write('\x1b[2K');
60
+ console.log();
61
+ return titleLines.length + 1; // +1 for empty line
62
+ }
63
+ function renderSingleOption(option, config) {
64
+ const { index, isSelected, showNumber = true } = config;
65
+ process.stdout.write('\x1b[2K\r');
66
+ const prefix = isSelected ? `${components_1.theme.active}❯ ` : ' ';
67
+ const numColor = isSelected ? components_1.theme.active : components_1.theme.primary;
68
+ const titleColor = isSelected ? components_1.theme.active : components_1.theme.title;
69
+ if (typeof option === 'string') {
70
+ // Check if option already has number prefix
71
+ const numMatch = option.match(/^(\d+\.\s*)(.+)$/);
72
+ if (numMatch) {
73
+ const num = numMatch[1];
74
+ const rest = numMatch[2];
75
+ const descMatch = rest.match(/^([^-]+)(\s*-\s*.+)?$/);
76
+ if (descMatch && descMatch[2]) {
77
+ const title = descMatch[1];
78
+ const desc = descMatch[2];
79
+ console.log(`${prefix}${numColor}${num}${titleColor}${title}${components_1.theme.muted}${desc}${components_1.colors.reset}`);
80
+ }
81
+ else {
82
+ console.log(`${prefix}${numColor}${num}${titleColor}${rest}${components_1.colors.reset}`);
83
+ }
84
+ }
85
+ else {
86
+ // No number prefix, add one if showNumber is true
87
+ const match = option.match(/^([^-]+)(\s*-\s*.+)?$/);
88
+ if (match && match[2]) {
89
+ const title = match[1];
90
+ const desc = match[2];
91
+ const numPrefix = showNumber ? `${numColor}${index + 1}.${components_1.colors.reset} ` : '';
92
+ console.log(`${prefix}${numPrefix}${titleColor}${title}${components_1.theme.muted}${desc}${components_1.colors.reset}`);
93
+ }
94
+ else {
95
+ const numPrefix = showNumber ? `${numColor}${index + 1}.${components_1.colors.reset} ` : '';
96
+ console.log(`${prefix}${numPrefix}${titleColor}${option}${components_1.colors.reset}`);
97
+ }
98
+ }
99
+ }
100
+ else if (option.label) {
101
+ // Handle MenuOption objects
102
+ const match = option.label.match(/^([^.]+\.\s*)([^-]+)(\s*-\s*.+)?$/);
103
+ if (match) {
104
+ const num = match[1];
105
+ const title = match[2];
106
+ const desc = match[3] || '';
107
+ console.log(`${prefix}${numColor}${num}${titleColor}${title}${components_1.theme.muted}${desc}${components_1.colors.reset}`);
108
+ }
109
+ else {
110
+ const numPrefix = showNumber ? `${numColor}${index + 1}.${components_1.colors.reset} ` : '';
111
+ console.log(`${prefix}${numPrefix}${titleColor}${option.label}${components_1.colors.reset}`);
112
+ }
113
+ }
114
+ }
115
+ /**
116
+ * Keyboard navigation helpers
117
+ */
118
+ function handleVerticalNavigation(key, currentIndex, maxIndex) {
119
+ if (key === '\u001b[A') {
120
+ // Up arrow
121
+ return currentIndex > 0 ? currentIndex - 1 : maxIndex;
122
+ }
123
+ else if (key === '\u001b[B') {
124
+ // Down arrow
125
+ return currentIndex < maxIndex ? currentIndex + 1 : 0;
126
+ }
127
+ return null;
128
+ }
129
+ function handleNumberInput(key, maxNumber) {
130
+ if (key.match(/^[0-9]$/)) {
131
+ const num = parseInt(key);
132
+ if (num >= 1 && num <= maxNumber) {
133
+ return num - 1; // Convert to 0-based index
134
+ }
135
+ }
136
+ return null;
137
+ }
138
+ function handleLetterInput(key, options) {
139
+ if (key.match(/^[a-zA-Z]$/)) {
140
+ const letter = key.toUpperCase();
141
+ for (let i = 0; i < options.length; i++) {
142
+ const option = options[i];
143
+ if (typeof option !== 'string' && option.label) {
144
+ const match = option.label.match(/^([A-Z])\./);
145
+ if (match && match[1] === letter) {
146
+ return i;
147
+ }
148
+ }
149
+ }
150
+ }
151
+ return null;
152
+ }
153
+ /**
154
+ * Input prompt rendering
155
+ */
156
+ function renderInputPrompt(text, showCursor = true) {
157
+ process.stdout.write('\x1b[2K');
158
+ console.log();
159
+ process.stdout.write('\x1b[2K\r');
160
+ process.stdout.write(` ${components_1.theme.muted}${text}${components_1.colors.reset}`);
161
+ if (showCursor) {
162
+ process.stdout.write(`${components_1.theme.active}_${components_1.colors.reset}`);
163
+ }
164
+ console.log();
165
+ }
166
+ /**
167
+ * Hints rendering
168
+ */
169
+ function renderHints(hintText) {
170
+ if (!hintText)
171
+ return 0;
172
+ process.stdout.write('\x1b[2K');
173
+ console.log();
174
+ const indent = ' ';
175
+ const indentedHint = hintText.split('\n').map(line => indent + line).join('\n');
176
+ const hintLinesArray = indentedHint.split('\n');
177
+ hintLinesArray.forEach(line => {
178
+ process.stdout.write('\x1b[2K\r');
179
+ console.log(line);
180
+ });
181
+ return 1 + hintLinesArray.length; // +1 for empty line before hints
182
+ }
183
+ /**
184
+ * Graceful exit with goodbye message
185
+ */
186
+ function exitWithGoodbye(state, onData, showGoodbyeFn) {
187
+ // Clean up terminal
188
+ state.stdin.setRawMode(false);
189
+ state.stdin.removeListener('data', onData);
190
+ // Clear rendered content
191
+ if (state.renderedLines > 0) {
192
+ process.stdout.write(`\x1b[${state.renderedLines}A`);
193
+ process.stdout.write('\x1b[J');
194
+ }
195
+ // Show cursor
196
+ process.stdout.write('\x1b[?25h');
197
+ // Show goodbye message
198
+ showGoodbyeFn();
199
+ // Exit
200
+ process.exit(0);
201
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * CLI Menu Kit - Multi Select Menu
3
+ * Interactive multi-select menu with checkboxes
4
+ */
5
+ import { MultiSelectConfig } from './types';
6
+ /**
7
+ * Interactive multi-select menu with checkboxes
8
+ *
9
+ * Features:
10
+ * - Arrow keys (↑/↓) to navigate
11
+ * - Space to toggle selection
12
+ * - A to select all
13
+ * - I to invert selection
14
+ * - Enter to confirm
15
+ * - Shows ◉ for selected items, ○ for unselected items
16
+ *
17
+ * @param options - Menu options
18
+ * @param config - Configuration object
19
+ * @returns Array of selected indices
20
+ */
21
+ export declare function selectMultiMenu(options: string[], config?: MultiSelectConfig): Promise<number[]>;