cli-menu-kit 0.1.26 → 0.2.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.
- package/dist/api.d.ts +23 -5
- package/dist/api.js +16 -4
- package/dist/component-factories.d.ts +59 -0
- package/dist/component-factories.js +141 -0
- package/dist/components/display/header-v2.d.ts +13 -0
- package/dist/components/display/header-v2.js +43 -0
- package/dist/components/display/header.d.ts +40 -0
- package/dist/components/display/header.js +331 -18
- package/dist/components/display/headers.d.ts +1 -0
- package/dist/components/display/headers.js +15 -5
- package/dist/components/display/hints-v2.d.ts +10 -0
- package/dist/components/display/hints-v2.js +34 -0
- package/dist/components/display/hints.d.ts +56 -0
- package/dist/components/display/hints.js +81 -0
- package/dist/components/display/index.d.ts +4 -1
- package/dist/components/display/index.js +17 -1
- package/dist/components/display/input-prompt.d.ts +35 -0
- package/dist/components/display/input-prompt.js +36 -0
- package/dist/components/display/list.d.ts +49 -0
- package/dist/components/display/list.js +86 -0
- package/dist/components/display/messages.js +5 -5
- package/dist/components/display/progress.d.ts +17 -0
- package/dist/components/display/progress.js +18 -0
- package/dist/components/display/summary.js +72 -10
- package/dist/components/display/table.d.ts +44 -0
- package/dist/components/display/table.js +108 -0
- package/dist/components/inputs/language-input.js +8 -5
- package/dist/components/inputs/number-input.js +19 -14
- package/dist/components/inputs/text-input.js +50 -13
- package/dist/components/menus/boolean-menu.js +34 -20
- package/dist/components/menus/checkbox-menu.d.ts +2 -1
- package/dist/components/menus/checkbox-menu.js +35 -61
- package/dist/components/menus/checkbox-table-menu.d.ts +12 -0
- package/dist/components/menus/checkbox-table-menu.js +398 -0
- package/dist/components/menus/index.d.ts +1 -0
- package/dist/components/menus/index.js +3 -1
- package/dist/components/menus/radio-menu-split.d.ts +34 -0
- package/dist/components/menus/radio-menu-split.js +258 -0
- package/dist/components/menus/radio-menu-v2.d.ts +11 -0
- package/dist/components/menus/radio-menu-v2.js +150 -0
- package/dist/components/menus/radio-menu.d.ts +2 -1
- package/dist/components/menus/radio-menu.js +100 -134
- package/dist/components.js +3 -3
- package/dist/config/index.d.ts +5 -0
- package/dist/config/index.js +21 -0
- package/dist/config/language-config.d.ts +73 -0
- package/dist/config/language-config.js +157 -0
- package/dist/config/user-config.d.ts +83 -0
- package/dist/config/user-config.js +185 -0
- package/dist/core/colors.d.ts +24 -18
- package/dist/core/colors.js +74 -7
- package/dist/core/hint-manager.d.ts +29 -0
- package/dist/core/hint-manager.js +65 -0
- package/dist/core/renderer.d.ts +2 -1
- package/dist/core/renderer.js +46 -22
- package/dist/core/screen-manager.d.ts +54 -0
- package/dist/core/screen-manager.js +119 -0
- package/dist/core/state-manager.d.ts +27 -0
- package/dist/core/state-manager.js +56 -0
- package/dist/core/terminal.d.ts +17 -1
- package/dist/core/terminal.js +124 -4
- package/dist/core/virtual-scroll.d.ts +65 -0
- package/dist/core/virtual-scroll.js +120 -0
- package/dist/features/commands.js +23 -22
- package/dist/i18n/languages/en.js +4 -1
- package/dist/i18n/languages/zh.js +4 -1
- package/dist/i18n/registry.d.ts +4 -3
- package/dist/i18n/registry.js +12 -4
- package/dist/i18n/types.d.ts +3 -0
- package/dist/index.d.ts +7 -4
- package/dist/index.js +49 -4
- package/dist/layout.d.ts +67 -0
- package/dist/layout.js +86 -0
- package/dist/page-layout.d.ts +123 -0
- package/dist/page-layout.js +195 -0
- package/dist/types/input.types.d.ts +8 -0
- package/dist/types/menu.types.d.ts +61 -5
- package/package.json +4 -1
|
@@ -8,13 +8,15 @@ exports.showNumberInput = showNumberInput;
|
|
|
8
8
|
const terminal_js_1 = require("../../core/terminal.js");
|
|
9
9
|
const keyboard_js_1 = require("../../core/keyboard.js");
|
|
10
10
|
const colors_js_1 = require("../../core/colors.js");
|
|
11
|
+
const registry_js_1 = require("../../i18n/registry.js");
|
|
11
12
|
/**
|
|
12
13
|
* Show a number input prompt
|
|
13
14
|
* @param config - Input configuration
|
|
14
15
|
* @returns Promise resolving to entered number
|
|
15
16
|
*/
|
|
16
17
|
async function showNumberInput(config) {
|
|
17
|
-
const { prompt, defaultValue, min, max, allowDecimals = false, allowNegative = false, validate, errorMessage, onExit } = config;
|
|
18
|
+
const { lang: langInput, prompt, defaultValue, min, max, allowDecimals = false, allowNegative = false, validate, errorMessage, onExit, preserveOnExit = false } = config;
|
|
19
|
+
const lang = langInput ?? (0, registry_js_1.getCurrentLanguage)();
|
|
18
20
|
const state = (0, terminal_js_1.initTerminal)();
|
|
19
21
|
let inputValue = '';
|
|
20
22
|
let errorMsg = '';
|
|
@@ -26,20 +28,21 @@ async function showNumberInput(config) {
|
|
|
26
28
|
if (min !== undefined || max !== undefined) {
|
|
27
29
|
const constraints = [];
|
|
28
30
|
if (min !== undefined)
|
|
29
|
-
constraints.push(
|
|
31
|
+
constraints.push(`${(0, registry_js_1.t)('inputs.minValue')}: ${min}`);
|
|
30
32
|
if (max !== undefined)
|
|
31
|
-
constraints.push(
|
|
32
|
-
promptLine += ` ${colors_js_1.
|
|
33
|
+
constraints.push(`${(0, registry_js_1.t)('inputs.maxValue')}: ${max}`);
|
|
34
|
+
promptLine += ` ${colors_js_1.uiColors.textSecondary}(${constraints.join(', ')})${colors_js_1.colors.reset}`;
|
|
33
35
|
}
|
|
34
36
|
if (defaultValue !== undefined && !inputValue) {
|
|
35
|
-
|
|
37
|
+
const defaultLabel = lang === 'en' ? 'default' : (0, registry_js_1.t)('inputs.defaultValue');
|
|
38
|
+
promptLine += ` ${colors_js_1.uiColors.textSecondary}(${defaultLabel}: ${defaultValue})${colors_js_1.colors.reset}`;
|
|
36
39
|
}
|
|
37
|
-
promptLine += `: ${colors_js_1.
|
|
40
|
+
promptLine += `: ${colors_js_1.uiColors.primary}${inputValue}_${colors_js_1.colors.reset}`;
|
|
38
41
|
(0, terminal_js_1.writeLine)(promptLine);
|
|
39
42
|
lineCount++;
|
|
40
43
|
// Render error message if any
|
|
41
44
|
if (errorMsg) {
|
|
42
|
-
(0, terminal_js_1.writeLine)(` ${colors_js_1.
|
|
45
|
+
(0, terminal_js_1.writeLine)(` ${colors_js_1.uiColors.error}✗ ${errorMsg}${colors_js_1.colors.reset}`);
|
|
43
46
|
lineCount++;
|
|
44
47
|
}
|
|
45
48
|
state.renderedLines = lineCount;
|
|
@@ -52,13 +55,15 @@ async function showNumberInput(config) {
|
|
|
52
55
|
// Handle Ctrl+C
|
|
53
56
|
if ((0, keyboard_js_1.isCtrlC)(key)) {
|
|
54
57
|
state.stdin.removeListener('data', onData);
|
|
55
|
-
|
|
58
|
+
if (!preserveOnExit) {
|
|
59
|
+
(0, terminal_js_1.clearMenu)(state);
|
|
60
|
+
}
|
|
56
61
|
(0, terminal_js_1.restoreTerminal)(state);
|
|
57
62
|
if (onExit) {
|
|
58
63
|
onExit();
|
|
59
64
|
}
|
|
60
65
|
else {
|
|
61
|
-
console.log(
|
|
66
|
+
console.log(`\n${(0, registry_js_1.t)('messages.goodbye')}`);
|
|
62
67
|
}
|
|
63
68
|
process.exit(0);
|
|
64
69
|
}
|
|
@@ -67,7 +72,7 @@ async function showNumberInput(config) {
|
|
|
67
72
|
const finalValue = inputValue || (defaultValue !== undefined ? String(defaultValue) : '');
|
|
68
73
|
// Check if empty
|
|
69
74
|
if (!finalValue) {
|
|
70
|
-
errorMsg = errorMessage || '
|
|
75
|
+
errorMsg = errorMessage || (0, registry_js_1.t)('inputs.enterNumber');
|
|
71
76
|
render();
|
|
72
77
|
return;
|
|
73
78
|
}
|
|
@@ -75,19 +80,19 @@ async function showNumberInput(config) {
|
|
|
75
80
|
const num = allowDecimals ? parseFloat(finalValue) : parseInt(finalValue, 10);
|
|
76
81
|
// Check if valid number
|
|
77
82
|
if (isNaN(num)) {
|
|
78
|
-
errorMsg = errorMessage || '
|
|
83
|
+
errorMsg = errorMessage || (0, registry_js_1.t)('inputs.invalidInput');
|
|
79
84
|
render();
|
|
80
85
|
return;
|
|
81
86
|
}
|
|
82
87
|
// Check min
|
|
83
88
|
if (min !== undefined && num < min) {
|
|
84
|
-
errorMsg = errorMessage ||
|
|
89
|
+
errorMsg = errorMessage || `${(0, registry_js_1.t)('inputs.minValue')}: ${min}`;
|
|
85
90
|
render();
|
|
86
91
|
return;
|
|
87
92
|
}
|
|
88
93
|
// Check max
|
|
89
94
|
if (max !== undefined && num > max) {
|
|
90
|
-
errorMsg = errorMessage ||
|
|
95
|
+
errorMsg = errorMessage || `${(0, registry_js_1.t)('inputs.maxValue')}: ${max}`;
|
|
91
96
|
render();
|
|
92
97
|
return;
|
|
93
98
|
}
|
|
@@ -95,7 +100,7 @@ async function showNumberInput(config) {
|
|
|
95
100
|
if (validate) {
|
|
96
101
|
const validationResult = validate(String(num));
|
|
97
102
|
if (validationResult !== true) {
|
|
98
|
-
errorMsg = typeof validationResult === 'string' ? validationResult : (errorMessage || '
|
|
103
|
+
errorMsg = typeof validationResult === 'string' ? validationResult : (errorMessage || (0, registry_js_1.t)('inputs.invalidInput'));
|
|
99
104
|
render();
|
|
100
105
|
return;
|
|
101
106
|
}
|
|
@@ -8,13 +8,36 @@ exports.showTextInput = showTextInput;
|
|
|
8
8
|
const terminal_js_1 = require("../../core/terminal.js");
|
|
9
9
|
const keyboard_js_1 = require("../../core/keyboard.js");
|
|
10
10
|
const colors_js_1 = require("../../core/colors.js");
|
|
11
|
+
const registry_js_1 = require("../../i18n/registry.js");
|
|
12
|
+
/**
|
|
13
|
+
* Normalize a terminal data chunk to printable text.
|
|
14
|
+
* Supports bracketed paste and ignores control/escape sequences.
|
|
15
|
+
*/
|
|
16
|
+
function normalizeTextChunk(raw) {
|
|
17
|
+
if (!raw) {
|
|
18
|
+
return '';
|
|
19
|
+
}
|
|
20
|
+
let chunk = raw;
|
|
21
|
+
// Bracketed paste wrappers used by many terminals.
|
|
22
|
+
chunk = chunk.replace(/\x1b\[200~/g, '');
|
|
23
|
+
chunk = chunk.replace(/\x1b\[201~/g, '');
|
|
24
|
+
// Remove CSI escape sequences (arrows, function keys, etc.).
|
|
25
|
+
chunk = chunk.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, '');
|
|
26
|
+
// Remove remaining ESC-prefixed control bytes.
|
|
27
|
+
chunk = chunk.replace(/\x1b./g, '');
|
|
28
|
+
// Single-line input: strip CR/LF and other control chars.
|
|
29
|
+
chunk = chunk.replace(/[\r\n]/g, '');
|
|
30
|
+
chunk = chunk.replace(/[\x00-\x1F\x7F]/g, '');
|
|
31
|
+
return chunk;
|
|
32
|
+
}
|
|
11
33
|
/**
|
|
12
34
|
* Show a text input prompt
|
|
13
35
|
* @param config - Input configuration
|
|
14
36
|
* @returns Promise resolving to entered text
|
|
15
37
|
*/
|
|
16
38
|
async function showTextInput(config) {
|
|
17
|
-
const { prompt, defaultValue = '', placeholder, maxLength, minLength = 0, allowEmpty = false, validate, errorMessage, onExit } = config;
|
|
39
|
+
const { lang: langInput, prompt, defaultValue = '', placeholder, maxLength, minLength = 0, allowEmpty = false, validate, errorMessage, onExit, preserveOnExit = false } = config;
|
|
40
|
+
const lang = langInput ?? (0, registry_js_1.getCurrentLanguage)();
|
|
18
41
|
const state = (0, terminal_js_1.initTerminal)();
|
|
19
42
|
let inputValue = '';
|
|
20
43
|
let errorMsg = '';
|
|
@@ -24,17 +47,18 @@ async function showTextInput(config) {
|
|
|
24
47
|
// Render prompt with default value hint
|
|
25
48
|
let promptLine = ` ${prompt}`;
|
|
26
49
|
if (defaultValue && !inputValue) {
|
|
27
|
-
|
|
50
|
+
const defaultLabel = lang === 'en' ? 'default' : (0, registry_js_1.t)('inputs.defaultValue');
|
|
51
|
+
promptLine += ` ${colors_js_1.uiColors.textSecondary}(${defaultLabel}: ${defaultValue})${colors_js_1.colors.reset}`;
|
|
28
52
|
}
|
|
29
53
|
if (placeholder && !inputValue) {
|
|
30
|
-
promptLine += ` ${colors_js_1.
|
|
54
|
+
promptLine += ` ${colors_js_1.uiColors.textSecondary}${placeholder}${colors_js_1.colors.reset}`;
|
|
31
55
|
}
|
|
32
|
-
promptLine += `: ${colors_js_1.
|
|
56
|
+
promptLine += `: ${colors_js_1.uiColors.primary}${inputValue}_${colors_js_1.colors.reset}`;
|
|
33
57
|
(0, terminal_js_1.writeLine)(promptLine);
|
|
34
58
|
lineCount++;
|
|
35
59
|
// Render error message if any
|
|
36
60
|
if (errorMsg) {
|
|
37
|
-
(0, terminal_js_1.writeLine)(` ${colors_js_1.
|
|
61
|
+
(0, terminal_js_1.writeLine)(` ${colors_js_1.uiColors.error}✗ ${errorMsg}${colors_js_1.colors.reset}`);
|
|
38
62
|
lineCount++;
|
|
39
63
|
}
|
|
40
64
|
state.renderedLines = lineCount;
|
|
@@ -47,13 +71,15 @@ async function showTextInput(config) {
|
|
|
47
71
|
// Handle Ctrl+C
|
|
48
72
|
if ((0, keyboard_js_1.isCtrlC)(key)) {
|
|
49
73
|
state.stdin.removeListener('data', onData);
|
|
50
|
-
|
|
74
|
+
if (!preserveOnExit) {
|
|
75
|
+
(0, terminal_js_1.clearMenu)(state);
|
|
76
|
+
}
|
|
51
77
|
(0, terminal_js_1.restoreTerminal)(state);
|
|
52
78
|
if (onExit) {
|
|
53
79
|
onExit();
|
|
54
80
|
}
|
|
55
81
|
else {
|
|
56
|
-
console.log(
|
|
82
|
+
console.log(`\n${(0, registry_js_1.t)('messages.goodbye')}`);
|
|
57
83
|
}
|
|
58
84
|
process.exit(0);
|
|
59
85
|
}
|
|
@@ -62,13 +88,13 @@ async function showTextInput(config) {
|
|
|
62
88
|
const finalValue = inputValue || defaultValue;
|
|
63
89
|
// Check if empty
|
|
64
90
|
if (!allowEmpty && !finalValue) {
|
|
65
|
-
errorMsg = errorMessage || '
|
|
91
|
+
errorMsg = errorMessage || (0, registry_js_1.t)('inputs.cannotBeEmpty');
|
|
66
92
|
render();
|
|
67
93
|
return;
|
|
68
94
|
}
|
|
69
95
|
// Check min length
|
|
70
96
|
if (minLength && finalValue.length < minLength) {
|
|
71
|
-
errorMsg = errorMessage ||
|
|
97
|
+
errorMsg = errorMessage || `${(0, registry_js_1.t)('inputs.minLength')}: ${minLength}`;
|
|
72
98
|
render();
|
|
73
99
|
return;
|
|
74
100
|
}
|
|
@@ -76,7 +102,7 @@ async function showTextInput(config) {
|
|
|
76
102
|
if (validate) {
|
|
77
103
|
const validationResult = validate(finalValue);
|
|
78
104
|
if (validationResult !== true) {
|
|
79
|
-
errorMsg = typeof validationResult === 'string' ? validationResult : (errorMessage || '
|
|
105
|
+
errorMsg = typeof validationResult === 'string' ? validationResult : (errorMessage || (0, registry_js_1.t)('inputs.invalidInput'));
|
|
80
106
|
render();
|
|
81
107
|
return;
|
|
82
108
|
}
|
|
@@ -97,13 +123,24 @@ async function showTextInput(config) {
|
|
|
97
123
|
}
|
|
98
124
|
return;
|
|
99
125
|
}
|
|
100
|
-
// Handle
|
|
101
|
-
|
|
126
|
+
// Handle single-key typing and multi-char paste chunks.
|
|
127
|
+
const chunk = (0, keyboard_js_1.isPrintable)(key) ? key : normalizeTextChunk(key);
|
|
128
|
+
if (chunk.length > 0) {
|
|
102
129
|
// Check max length
|
|
103
130
|
if (maxLength && inputValue.length >= maxLength) {
|
|
104
131
|
return;
|
|
105
132
|
}
|
|
106
|
-
|
|
133
|
+
let next = chunk;
|
|
134
|
+
if (maxLength) {
|
|
135
|
+
const remaining = maxLength - inputValue.length;
|
|
136
|
+
if (remaining <= 0) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (next.length > remaining) {
|
|
140
|
+
next = next.slice(0, remaining);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
inputValue += next;
|
|
107
144
|
errorMsg = ''; // Clear error on edit
|
|
108
145
|
render();
|
|
109
146
|
return;
|
|
@@ -8,36 +8,43 @@ exports.showBooleanMenu = showBooleanMenu;
|
|
|
8
8
|
const terminal_js_1 = require("../../core/terminal.js");
|
|
9
9
|
const keyboard_js_1 = require("../../core/keyboard.js");
|
|
10
10
|
const colors_js_1 = require("../../core/colors.js");
|
|
11
|
+
const registry_js_1 = require("../../i18n/registry.js");
|
|
11
12
|
/**
|
|
12
13
|
* Show a boolean menu (yes/no selection)
|
|
13
14
|
* @param config - Menu configuration
|
|
14
15
|
* @returns Promise resolving to boolean result
|
|
15
16
|
*/
|
|
16
17
|
async function showBooleanMenu(config) {
|
|
17
|
-
const { question, defaultValue = true, yesText = '
|
|
18
|
+
const { question, helperText, defaultValue = true, yesText = (0, registry_js_1.t)('menus.yes'), noText = (0, registry_js_1.t)('menus.no'), orientation = 'horizontal', onExit, preserveOnSelect = false } = config;
|
|
19
|
+
const preserveOnExit = config.preserveOnExit ?? preserveOnSelect;
|
|
18
20
|
if (orientation === 'horizontal') {
|
|
19
|
-
return showBooleanMenuHorizontal(question, defaultValue, yesText, noText, onExit, preserveOnSelect);
|
|
21
|
+
return showBooleanMenuHorizontal(question, helperText, defaultValue, yesText, noText, onExit, preserveOnSelect, preserveOnExit);
|
|
20
22
|
}
|
|
21
23
|
else {
|
|
22
|
-
return showBooleanMenuVertical(question, defaultValue, yesText, noText, onExit, preserveOnSelect);
|
|
24
|
+
return showBooleanMenuVertical(question, helperText, defaultValue, yesText, noText, onExit, preserveOnSelect, preserveOnExit);
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
27
|
/**
|
|
26
28
|
* Show horizontal boolean menu (side by side)
|
|
27
29
|
*/
|
|
28
|
-
async function showBooleanMenuHorizontal(question, defaultValue, yesText, noText, onExit, preserveOnSelect = false) {
|
|
30
|
+
async function showBooleanMenuHorizontal(question, helperText, defaultValue, yesText, noText, onExit, preserveOnSelect = false, preserveOnExit = false) {
|
|
29
31
|
const state = (0, terminal_js_1.initTerminal)();
|
|
30
32
|
let selected = defaultValue;
|
|
31
33
|
const render = () => {
|
|
32
34
|
(0, terminal_js_1.clearMenu)(state);
|
|
33
35
|
// Render question with options on same line
|
|
34
36
|
const yesOption = selected
|
|
35
|
-
? `${colors_js_1.
|
|
36
|
-
: `${colors_js_1.
|
|
37
|
+
? `${colors_js_1.uiColors.primary}${yesText}${colors_js_1.colors.reset}`
|
|
38
|
+
: `${colors_js_1.uiColors.textSecondary}${yesText}${colors_js_1.colors.reset}`;
|
|
37
39
|
const noOption = !selected
|
|
38
|
-
? `${colors_js_1.
|
|
39
|
-
: `${colors_js_1.
|
|
40
|
-
(0, terminal_js_1.writeLine)(`${colors_js_1.
|
|
40
|
+
? `${colors_js_1.uiColors.primary}${noText}${colors_js_1.colors.reset}`
|
|
41
|
+
: `${colors_js_1.uiColors.textSecondary}${noText}${colors_js_1.colors.reset}`;
|
|
42
|
+
(0, terminal_js_1.writeLine)(`${colors_js_1.uiColors.warning}?${colors_js_1.colors.reset} ${question} ${yesOption} | ${noOption}`);
|
|
43
|
+
if (helperText && helperText.trim().length > 0) {
|
|
44
|
+
(0, terminal_js_1.writeLine)(` ${colors_js_1.uiColors.textSecondary}${helperText}${colors_js_1.colors.reset}`);
|
|
45
|
+
state.renderedLines = 2;
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
41
48
|
state.renderedLines = 1;
|
|
42
49
|
};
|
|
43
50
|
// Initial render
|
|
@@ -48,13 +55,15 @@ async function showBooleanMenuHorizontal(question, defaultValue, yesText, noText
|
|
|
48
55
|
// Handle Ctrl+C
|
|
49
56
|
if ((0, keyboard_js_1.isCtrlC)(key)) {
|
|
50
57
|
state.stdin.removeListener('data', onData);
|
|
51
|
-
|
|
58
|
+
if (!preserveOnExit) {
|
|
59
|
+
(0, terminal_js_1.clearMenu)(state);
|
|
60
|
+
}
|
|
52
61
|
(0, terminal_js_1.restoreTerminal)(state);
|
|
53
62
|
if (onExit) {
|
|
54
63
|
onExit();
|
|
55
64
|
}
|
|
56
65
|
else {
|
|
57
|
-
console.log(
|
|
66
|
+
console.log(`\n${(0, registry_js_1.t)('messages.goodbye')}`);
|
|
58
67
|
}
|
|
59
68
|
process.exit(0);
|
|
60
69
|
}
|
|
@@ -98,23 +107,26 @@ async function showBooleanMenuHorizontal(question, defaultValue, yesText, noText
|
|
|
98
107
|
/**
|
|
99
108
|
* Show vertical boolean menu (stacked)
|
|
100
109
|
*/
|
|
101
|
-
async function showBooleanMenuVertical(question, defaultValue, yesText, noText, onExit, preserveOnSelect = false) {
|
|
110
|
+
async function showBooleanMenuVertical(question, helperText, defaultValue, yesText, noText, onExit, preserveOnSelect = false, preserveOnExit = false) {
|
|
102
111
|
const state = (0, terminal_js_1.initTerminal)();
|
|
103
112
|
let selected = defaultValue;
|
|
104
113
|
const render = () => {
|
|
105
114
|
(0, terminal_js_1.clearMenu)(state);
|
|
106
115
|
// Render question
|
|
107
|
-
(0, terminal_js_1.writeLine)(`${colors_js_1.
|
|
116
|
+
(0, terminal_js_1.writeLine)(`${colors_js_1.uiColors.warning}?${colors_js_1.colors.reset} ${question}`);
|
|
117
|
+
if (helperText && helperText.trim().length > 0) {
|
|
118
|
+
(0, terminal_js_1.writeLine)(` ${colors_js_1.uiColors.textSecondary}${helperText}${colors_js_1.colors.reset}`);
|
|
119
|
+
}
|
|
108
120
|
(0, terminal_js_1.writeLine)('');
|
|
109
121
|
// Render yes option
|
|
110
|
-
const yesCursor = selected ? `${colors_js_1.
|
|
111
|
-
const yesColor = selected ? colors_js_1.
|
|
122
|
+
const yesCursor = selected ? `${colors_js_1.uiColors.cursor}❯ ${colors_js_1.colors.reset}` : ' ';
|
|
123
|
+
const yesColor = selected ? colors_js_1.uiColors.primary : colors_js_1.uiColors.textPrimary;
|
|
112
124
|
(0, terminal_js_1.writeLine)(`${yesCursor}${yesColor}${yesText}${colors_js_1.colors.reset}`);
|
|
113
125
|
// Render no option
|
|
114
|
-
const noCursor = !selected ? `${colors_js_1.
|
|
115
|
-
const noColor = !selected ? colors_js_1.
|
|
126
|
+
const noCursor = !selected ? `${colors_js_1.uiColors.cursor}❯ ${colors_js_1.colors.reset}` : ' ';
|
|
127
|
+
const noColor = !selected ? colors_js_1.uiColors.primary : colors_js_1.uiColors.textPrimary;
|
|
116
128
|
(0, terminal_js_1.writeLine)(`${noCursor}${noColor}${noText}${colors_js_1.colors.reset}`);
|
|
117
|
-
state.renderedLines = 4;
|
|
129
|
+
state.renderedLines = helperText && helperText.trim().length > 0 ? 5 : 4;
|
|
118
130
|
};
|
|
119
131
|
// Initial render
|
|
120
132
|
render();
|
|
@@ -124,13 +136,15 @@ async function showBooleanMenuVertical(question, defaultValue, yesText, noText,
|
|
|
124
136
|
// Handle Ctrl+C
|
|
125
137
|
if ((0, keyboard_js_1.isCtrlC)(key)) {
|
|
126
138
|
state.stdin.removeListener('data', onData);
|
|
127
|
-
|
|
139
|
+
if (!preserveOnExit) {
|
|
140
|
+
(0, terminal_js_1.clearMenu)(state);
|
|
141
|
+
}
|
|
128
142
|
(0, terminal_js_1.restoreTerminal)(state);
|
|
129
143
|
if (onExit) {
|
|
130
144
|
onExit();
|
|
131
145
|
}
|
|
132
146
|
else {
|
|
133
|
-
console.log(
|
|
147
|
+
console.log(`\n${(0, registry_js_1.t)('messages.goodbye')}`);
|
|
134
148
|
}
|
|
135
149
|
process.exit(0);
|
|
136
150
|
}
|
|
@@ -6,6 +6,7 @@ import { CheckboxMenuConfig, CheckboxMenuResult } from '../../types/menu.types.j
|
|
|
6
6
|
/**
|
|
7
7
|
* Show a checkbox menu (multi-select)
|
|
8
8
|
* @param config - Menu configuration
|
|
9
|
+
* @param hints - Optional hints to display at the bottom (for Page Layout use)
|
|
9
10
|
* @returns Promise resolving to selected options
|
|
10
11
|
*/
|
|
11
|
-
export declare function showCheckboxMenu(config: CheckboxMenuConfig): Promise<CheckboxMenuResult>;
|
|
12
|
+
export declare function showCheckboxMenu(config: CheckboxMenuConfig, hints?: string[]): Promise<CheckboxMenuResult>;
|
|
@@ -5,36 +5,21 @@
|
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.showCheckboxMenu = showCheckboxMenu;
|
|
8
|
-
const layout_types_js_1 = require("../../types/layout.types.js");
|
|
9
8
|
const terminal_js_1 = require("../../core/terminal.js");
|
|
10
9
|
const keyboard_js_1 = require("../../core/keyboard.js");
|
|
11
10
|
const renderer_js_1 = require("../../core/renderer.js");
|
|
12
11
|
const registry_js_1 = require("../../i18n/registry.js");
|
|
13
|
-
/**
|
|
14
|
-
* Generate hints based on menu configuration
|
|
15
|
-
*/
|
|
16
|
-
function generateHints(allowSelectAll, allowInvert) {
|
|
17
|
-
const hints = [(0, registry_js_1.t)('hints.arrows'), (0, registry_js_1.t)('hints.space')];
|
|
18
|
-
if (allowSelectAll) {
|
|
19
|
-
hints.push((0, registry_js_1.t)('hints.selectAll'));
|
|
20
|
-
}
|
|
21
|
-
if (allowInvert) {
|
|
22
|
-
hints.push((0, registry_js_1.t)('hints.invert'));
|
|
23
|
-
}
|
|
24
|
-
hints.push((0, registry_js_1.t)('hints.enter'));
|
|
25
|
-
return hints;
|
|
26
|
-
}
|
|
27
12
|
/**
|
|
28
13
|
* Show a checkbox menu (multi-select)
|
|
29
14
|
* @param config - Menu configuration
|
|
15
|
+
* @param hints - Optional hints to display at the bottom (for Page Layout use)
|
|
30
16
|
* @returns Promise resolving to selected options
|
|
31
17
|
*/
|
|
32
|
-
async function showCheckboxMenu(config) {
|
|
33
|
-
const { options, title, prompt,
|
|
18
|
+
async function showCheckboxMenu(config, hints) {
|
|
19
|
+
const { options, title, prompt, defaultSelected = [], minSelections = 0, maxSelections, allowSelectAll = true, allowInvert = true, separatorWidth = 30, onExit, preserveOnSelect = false } = config;
|
|
20
|
+
const preserveOnExit = config.preserveOnExit ?? preserveOnSelect;
|
|
34
21
|
// Use i18n for default prompt if not provided
|
|
35
22
|
const displayPrompt = prompt || (0, registry_js_1.t)('menus.multiSelectPrompt');
|
|
36
|
-
// Generate hints dynamically if not provided
|
|
37
|
-
const displayHints = hints || generateHints(allowSelectAll, allowInvert);
|
|
38
23
|
// Validate options
|
|
39
24
|
if (!options || options.length === 0) {
|
|
40
25
|
throw new Error('CheckboxMenu requires at least one option');
|
|
@@ -89,49 +74,36 @@ async function showCheckboxMenu(config) {
|
|
|
89
74
|
const render = () => {
|
|
90
75
|
(0, terminal_js_1.clearMenu)(state);
|
|
91
76
|
let lineCount = 0;
|
|
92
|
-
// Render
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
case 'options':
|
|
110
|
-
optionData.forEach((item, index) => {
|
|
111
|
-
if (item.isSeparator) {
|
|
112
|
-
// Render section label
|
|
113
|
-
(0, renderer_js_1.renderSectionLabel)(item.label, separatorWidth);
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
(0, renderer_js_1.renderOption)(item.value, selected.has(index), index === cursorIndex);
|
|
117
|
-
}
|
|
118
|
-
lineCount++;
|
|
119
|
-
});
|
|
120
|
-
break;
|
|
121
|
-
case 'hints':
|
|
122
|
-
if (layout.visible.hints && displayHints.length > 0) {
|
|
123
|
-
(0, renderer_js_1.renderHints)(displayHints);
|
|
124
|
-
lineCount++;
|
|
125
|
-
}
|
|
126
|
-
break;
|
|
77
|
+
// Render title if provided
|
|
78
|
+
if (title) {
|
|
79
|
+
(0, renderer_js_1.renderBlankLines)(1);
|
|
80
|
+
lineCount++;
|
|
81
|
+
}
|
|
82
|
+
// Render input prompt (show selected count)
|
|
83
|
+
const selectedCount = selected.size;
|
|
84
|
+
const displayValue = `${selectedCount} ${(0, registry_js_1.t)('menus.selectedCount')}`;
|
|
85
|
+
(0, renderer_js_1.renderInputPrompt)(displayPrompt, displayValue);
|
|
86
|
+
lineCount++;
|
|
87
|
+
(0, renderer_js_1.renderBlankLines)(1);
|
|
88
|
+
lineCount++;
|
|
89
|
+
// Render options
|
|
90
|
+
optionData.forEach((item, index) => {
|
|
91
|
+
if (item.isSeparator) {
|
|
92
|
+
// Render section label
|
|
93
|
+
(0, renderer_js_1.renderSectionLabel)(item.label, separatorWidth);
|
|
127
94
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if (layout.spacing?.[afterSpacingKey]) {
|
|
131
|
-
(0, renderer_js_1.renderBlankLines)(layout.spacing[afterSpacingKey]);
|
|
132
|
-
lineCount += layout.spacing[afterSpacingKey];
|
|
95
|
+
else {
|
|
96
|
+
(0, renderer_js_1.renderOption)(item.value, selected.has(index), index === cursorIndex);
|
|
133
97
|
}
|
|
98
|
+
lineCount++;
|
|
134
99
|
});
|
|
100
|
+
// Render hints if provided
|
|
101
|
+
if (hints && hints.length > 0) {
|
|
102
|
+
(0, renderer_js_1.renderBlankLines)(1);
|
|
103
|
+
lineCount++;
|
|
104
|
+
(0, renderer_js_1.renderHints)(hints);
|
|
105
|
+
lineCount += 1;
|
|
106
|
+
}
|
|
135
107
|
state.renderedLines = lineCount;
|
|
136
108
|
};
|
|
137
109
|
// Initial render
|
|
@@ -142,13 +114,15 @@ async function showCheckboxMenu(config) {
|
|
|
142
114
|
// Handle Ctrl+C
|
|
143
115
|
if ((0, keyboard_js_1.isCtrlC)(key)) {
|
|
144
116
|
state.stdin.removeListener('data', onData);
|
|
145
|
-
|
|
117
|
+
if (!preserveOnExit) {
|
|
118
|
+
(0, terminal_js_1.clearMenu)(state);
|
|
119
|
+
}
|
|
146
120
|
(0, terminal_js_1.restoreTerminal)(state);
|
|
147
121
|
if (onExit) {
|
|
148
122
|
onExit();
|
|
149
123
|
}
|
|
150
124
|
else {
|
|
151
|
-
console.log(
|
|
125
|
+
console.log(`\n${(0, registry_js_1.t)('messages.goodbye')}`);
|
|
152
126
|
}
|
|
153
127
|
process.exit(0);
|
|
154
128
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CheckboxTableMenu - Multi-select menu with table display
|
|
3
|
+
* Combines checkbox selection with formatted table layout
|
|
4
|
+
*/
|
|
5
|
+
import { CheckboxTableMenuConfig, CheckboxTableMenuResult } from '../../types/menu.types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Show a checkbox table menu (multi-select with table display)
|
|
8
|
+
* @param config - Menu configuration
|
|
9
|
+
* @param hints - Optional hints to display at the bottom (for Page Layout use)
|
|
10
|
+
* @returns Promise resolving to selected rows
|
|
11
|
+
*/
|
|
12
|
+
export declare function showCheckboxTableMenu(config: CheckboxTableMenuConfig, hints?: string[]): Promise<CheckboxTableMenuResult>;
|