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.
- package/LICENSE +21 -0
- package/README.md +716 -0
- package/dist/api.d.ts +158 -0
- package/dist/api.js +128 -0
- package/dist/components/display/headers.d.ts +32 -0
- package/dist/components/display/headers.js +84 -0
- package/dist/components/display/index.d.ts +8 -0
- package/dist/components/display/index.js +31 -0
- package/dist/components/display/messages.d.ts +41 -0
- package/dist/components/display/messages.js +90 -0
- package/dist/components/display/progress.d.ts +41 -0
- package/dist/components/display/progress.js +82 -0
- package/dist/components/display/summary.d.ts +33 -0
- package/dist/components/display/summary.js +82 -0
- package/dist/components/inputs/index.d.ts +8 -0
- package/dist/components/inputs/index.js +15 -0
- package/dist/components/inputs/language-input.d.ts +11 -0
- package/dist/components/inputs/language-input.js +104 -0
- package/dist/components/inputs/modify-field.d.ts +11 -0
- package/dist/components/inputs/modify-field.js +42 -0
- package/dist/components/inputs/number-input.d.ts +11 -0
- package/dist/components/inputs/number-input.js +143 -0
- package/dist/components/inputs/text-input.d.ts +11 -0
- package/dist/components/inputs/text-input.js +114 -0
- package/dist/components/menus/boolean-menu.d.ts +11 -0
- package/dist/components/menus/boolean-menu.js +169 -0
- package/dist/components/menus/checkbox-menu.d.ts +11 -0
- package/dist/components/menus/checkbox-menu.js +161 -0
- package/dist/components/menus/index.d.ts +7 -0
- package/dist/components/menus/index.js +13 -0
- package/dist/components/menus/radio-menu.d.ts +11 -0
- package/dist/components/menus/radio-menu.js +158 -0
- package/dist/components.d.ts +55 -0
- package/dist/components.js +166 -0
- package/dist/core/colors.d.ts +64 -0
- package/dist/core/colors.js +139 -0
- package/dist/core/keyboard.d.ts +124 -0
- package/dist/core/keyboard.js +185 -0
- package/dist/core/renderer.d.ts +74 -0
- package/dist/core/renderer.js +217 -0
- package/dist/core/terminal.d.ts +89 -0
- package/dist/core/terminal.js +170 -0
- package/dist/features/commands.d.ts +57 -0
- package/dist/features/commands.js +161 -0
- package/dist/features/wizard.d.ts +62 -0
- package/dist/features/wizard.js +112 -0
- package/dist/i18n/languages/en.d.ts +5 -0
- package/dist/i18n/languages/en.js +54 -0
- package/dist/i18n/languages/zh.d.ts +5 -0
- package/dist/i18n/languages/zh.js +54 -0
- package/dist/i18n/registry.d.ts +36 -0
- package/dist/i18n/registry.js +82 -0
- package/dist/i18n/types.d.ts +65 -0
- package/dist/i18n/types.js +5 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +104 -0
- package/dist/input.d.ts +40 -0
- package/dist/input.js +211 -0
- package/dist/menu-core.d.ts +52 -0
- package/dist/menu-core.js +201 -0
- package/dist/menu-multi.d.ts +21 -0
- package/dist/menu-multi.js +119 -0
- package/dist/menu-single.d.ts +18 -0
- package/dist/menu-single.js +138 -0
- package/dist/menu.d.ts +66 -0
- package/dist/menu.js +78 -0
- package/dist/types/display.types.d.ts +57 -0
- package/dist/types/display.types.js +5 -0
- package/dist/types/input.types.d.ts +88 -0
- package/dist/types/input.types.js +5 -0
- package/dist/types/layout.types.d.ts +56 -0
- package/dist/types/layout.types.js +36 -0
- package/dist/types/menu.types.d.ts +85 -0
- package/dist/types/menu.types.js +5 -0
- package/dist/types.d.ts +49 -0
- package/dist/types.js +5 -0
- package/package.json +35 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CLI Menu Kit - Multi Select Menu
|
|
4
|
+
* Interactive multi-select menu with checkboxes
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.selectMultiMenu = selectMultiMenu;
|
|
8
|
+
const components_1 = require("./components");
|
|
9
|
+
const menu_core_1 = require("./menu-core");
|
|
10
|
+
/**
|
|
11
|
+
* Interactive multi-select menu with checkboxes
|
|
12
|
+
*
|
|
13
|
+
* Features:
|
|
14
|
+
* - Arrow keys (↑/↓) to navigate
|
|
15
|
+
* - Space to toggle selection
|
|
16
|
+
* - A to select all
|
|
17
|
+
* - I to invert selection
|
|
18
|
+
* - Enter to confirm
|
|
19
|
+
* - Shows ◉ for selected items, ○ for unselected items
|
|
20
|
+
*
|
|
21
|
+
* @param options - Menu options
|
|
22
|
+
* @param config - Configuration object
|
|
23
|
+
* @returns Array of selected indices
|
|
24
|
+
*/
|
|
25
|
+
async function selectMultiMenu(options, config = {}) {
|
|
26
|
+
const { lang = 'zh', defaultSelected = [] } = config;
|
|
27
|
+
const inputPrompt = lang === 'zh'
|
|
28
|
+
? `${components_1.symbols.success.color}${components_1.symbols.success.icon}${components_1.colors.reset} 空格选中/取消,回车确认: `
|
|
29
|
+
: `${components_1.symbols.success.color}${components_1.symbols.success.icon}${components_1.colors.reset} Space to toggle, Enter to confirm: `;
|
|
30
|
+
const hintText = (0, components_1.buildHint)(['arrows', 'space', 'all', 'invert', 'enter'], lang);
|
|
31
|
+
return new Promise((resolve) => {
|
|
32
|
+
let selectedIndex = 0;
|
|
33
|
+
let selectedItems = new Set(defaultSelected);
|
|
34
|
+
const state = (0, menu_core_1.setupTerminal)();
|
|
35
|
+
const cleanup = () => {
|
|
36
|
+
(0, menu_core_1.cleanupTerminal)(state, onKeyPress);
|
|
37
|
+
resolve(Array.from(selectedItems).sort((a, b) => a - b));
|
|
38
|
+
};
|
|
39
|
+
const render = () => {
|
|
40
|
+
let totalLines = 0;
|
|
41
|
+
// Move cursor up if not first render
|
|
42
|
+
(0, menu_core_1.moveCursorUp)(state);
|
|
43
|
+
// Render input line
|
|
44
|
+
process.stdout.write('\x1b[2K\r');
|
|
45
|
+
process.stdout.write(`${components_1.theme.muted}${inputPrompt}${components_1.colors.reset}`);
|
|
46
|
+
const selectedText = lang === 'zh' ? `${selectedItems.size} 项已选` : `${selectedItems.size} selected`;
|
|
47
|
+
process.stdout.write(`${components_1.theme.active}${selectedText}${components_1.colors.reset}`);
|
|
48
|
+
console.log();
|
|
49
|
+
console.log();
|
|
50
|
+
totalLines += 2;
|
|
51
|
+
// Render options with checkboxes
|
|
52
|
+
options.forEach((option, index) => {
|
|
53
|
+
const isCurrentLine = index === selectedIndex;
|
|
54
|
+
const isChecked = selectedItems.has(index);
|
|
55
|
+
const prefix = isCurrentLine ? `${components_1.theme.active}❯ ` : ' ';
|
|
56
|
+
const checkbox = isChecked ? `${components_1.theme.success}◉` : `${components_1.theme.muted}○`;
|
|
57
|
+
const textColor = isCurrentLine ? components_1.theme.active : (isChecked ? components_1.theme.title : components_1.colors.reset);
|
|
58
|
+
console.log(`${prefix}${checkbox} ${textColor}${option}${components_1.colors.reset}`);
|
|
59
|
+
});
|
|
60
|
+
totalLines += options.length;
|
|
61
|
+
// Render hints
|
|
62
|
+
totalLines += (0, menu_core_1.renderHints)(hintText);
|
|
63
|
+
state.renderedLines = totalLines;
|
|
64
|
+
state.isFirstRender = false;
|
|
65
|
+
};
|
|
66
|
+
const onKeyPress = (key) => {
|
|
67
|
+
// Handle vertical navigation
|
|
68
|
+
const newIndex = (0, menu_core_1.handleVerticalNavigation)(key, selectedIndex, options.length - 1);
|
|
69
|
+
if (newIndex !== null) {
|
|
70
|
+
selectedIndex = newIndex;
|
|
71
|
+
render();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// Handle space (toggle selection)
|
|
75
|
+
if (key === ' ') {
|
|
76
|
+
if (selectedItems.has(selectedIndex)) {
|
|
77
|
+
selectedItems.delete(selectedIndex);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
selectedItems.add(selectedIndex);
|
|
81
|
+
}
|
|
82
|
+
render();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// Handle 'A' (select all)
|
|
86
|
+
if (key.toUpperCase() === 'A') {
|
|
87
|
+
selectedItems.clear();
|
|
88
|
+
for (let i = 0; i < options.length; i++) {
|
|
89
|
+
selectedItems.add(i);
|
|
90
|
+
}
|
|
91
|
+
render();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
// Handle 'I' (invert selection)
|
|
95
|
+
if (key.toUpperCase() === 'I') {
|
|
96
|
+
const newSelected = new Set();
|
|
97
|
+
for (let i = 0; i < options.length; i++) {
|
|
98
|
+
if (!selectedItems.has(i)) {
|
|
99
|
+
newSelected.add(i);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
selectedItems = newSelected;
|
|
103
|
+
render();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
// Handle Enter key
|
|
107
|
+
if (key === '\r') {
|
|
108
|
+
cleanup();
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
// Handle Ctrl+C
|
|
112
|
+
if (key === '\u0003') {
|
|
113
|
+
(0, menu_core_1.exitWithGoodbye)(state, onKeyPress, () => (0, components_1.showGoodbye)(lang));
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
state.stdin.on('data', onKeyPress);
|
|
117
|
+
render();
|
|
118
|
+
});
|
|
119
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Menu Kit - Single Select Menu
|
|
3
|
+
* Interactive menu selection with arrow keys and keyboard shortcuts
|
|
4
|
+
*/
|
|
5
|
+
import { MenuConfig, MenuOption } from './types';
|
|
6
|
+
/**
|
|
7
|
+
* Interactive menu selection with BOTH arrow keys AND number input
|
|
8
|
+
*
|
|
9
|
+
* Features:
|
|
10
|
+
* - Arrow keys (↑/↓) to navigate with live highlight
|
|
11
|
+
* - Number/letter keys to directly jump and select
|
|
12
|
+
* - Enter key (⏎) to confirm selection
|
|
13
|
+
*
|
|
14
|
+
* @param options - Menu options (strings or objects with label/value)
|
|
15
|
+
* @param config - Configuration object
|
|
16
|
+
* @returns Selected index (0-based)
|
|
17
|
+
*/
|
|
18
|
+
export declare function selectMenu(options: Array<string | MenuOption>, config?: MenuConfig): Promise<number>;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CLI Menu Kit - Single Select Menu
|
|
4
|
+
* Interactive menu selection with arrow keys and keyboard shortcuts
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.selectMenu = selectMenu;
|
|
8
|
+
const components_1 = require("./components");
|
|
9
|
+
const menu_core_1 = require("./menu-core");
|
|
10
|
+
/**
|
|
11
|
+
* Interactive menu selection with BOTH arrow keys AND number input
|
|
12
|
+
*
|
|
13
|
+
* Features:
|
|
14
|
+
* - Arrow keys (↑/↓) to navigate with live highlight
|
|
15
|
+
* - Number/letter keys to directly jump and select
|
|
16
|
+
* - Enter key (⏎) to confirm selection
|
|
17
|
+
*
|
|
18
|
+
* @param options - Menu options (strings or objects with label/value)
|
|
19
|
+
* @param config - Configuration object
|
|
20
|
+
* @returns Selected index (0-based)
|
|
21
|
+
*/
|
|
22
|
+
async function selectMenu(options, config = {}) {
|
|
23
|
+
const { lang = 'zh', type = 'main', title, prompt, showPrompt = (type === 'main'), showHints = true } = config;
|
|
24
|
+
const inputPromptText = lang === 'zh'
|
|
25
|
+
? '输入选项或用↑↓选择,回车确认: '
|
|
26
|
+
: 'Type option or use ↑↓ to select, Enter to confirm: ';
|
|
27
|
+
let hintKeys;
|
|
28
|
+
if (type === 'main') {
|
|
29
|
+
hintKeys = ['arrows', 'number', 'letter', 'enter'];
|
|
30
|
+
}
|
|
31
|
+
else if (type === 'firstRun') {
|
|
32
|
+
hintKeys = ['arrows', 'number', 'enter', 'esc'];
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
hintKeys = ['arrows', 'number', 'enter'];
|
|
36
|
+
}
|
|
37
|
+
const hintText = showHints ? (0, components_1.buildHint)(hintKeys, lang) : '';
|
|
38
|
+
return new Promise((resolve) => {
|
|
39
|
+
let selectedIndex = 0;
|
|
40
|
+
const state = (0, menu_core_1.setupTerminal)();
|
|
41
|
+
const cleanup = (finalIndex) => {
|
|
42
|
+
(0, menu_core_1.cleanupTerminal)(state, onKeyPress);
|
|
43
|
+
resolve(finalIndex);
|
|
44
|
+
};
|
|
45
|
+
const render = () => {
|
|
46
|
+
let totalLines = 0;
|
|
47
|
+
// Move cursor up if not first render
|
|
48
|
+
(0, menu_core_1.moveCursorUp)(state);
|
|
49
|
+
// Render title
|
|
50
|
+
if (title) {
|
|
51
|
+
totalLines += (0, menu_core_1.renderHeader)(title);
|
|
52
|
+
}
|
|
53
|
+
// Render options
|
|
54
|
+
options.forEach((option, index) => {
|
|
55
|
+
(0, menu_core_1.renderSingleOption)(option, {
|
|
56
|
+
index,
|
|
57
|
+
isSelected: index === selectedIndex,
|
|
58
|
+
showNumber: true
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
totalLines += options.length;
|
|
62
|
+
// Render input prompt with current selection value (BELOW options)
|
|
63
|
+
if (showPrompt) {
|
|
64
|
+
// Calculate display value (current selection number/letter)
|
|
65
|
+
let displayValue = '';
|
|
66
|
+
if (options[selectedIndex]) {
|
|
67
|
+
const option = options[selectedIndex];
|
|
68
|
+
if (typeof option === 'string') {
|
|
69
|
+
displayValue = String(selectedIndex + 1);
|
|
70
|
+
}
|
|
71
|
+
else if (option.label) {
|
|
72
|
+
const match = option.label.match(/^([^.]+)\./);
|
|
73
|
+
if (match) {
|
|
74
|
+
displayValue = match[1];
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
displayValue = String(selectedIndex + 1);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Render prompt with display value
|
|
82
|
+
console.log();
|
|
83
|
+
process.stdout.write('\x1b[2K\r');
|
|
84
|
+
process.stdout.write(` ${components_1.theme.muted}${inputPromptText}${components_1.colors.reset}`);
|
|
85
|
+
process.stdout.write(`${components_1.theme.active}${displayValue}${components_1.colors.reset}`);
|
|
86
|
+
console.log();
|
|
87
|
+
totalLines += 2;
|
|
88
|
+
}
|
|
89
|
+
// Render hints
|
|
90
|
+
if (showHints && hintText) {
|
|
91
|
+
totalLines += (0, menu_core_1.renderHints)(hintText);
|
|
92
|
+
}
|
|
93
|
+
state.renderedLines = totalLines;
|
|
94
|
+
state.isFirstRender = false;
|
|
95
|
+
};
|
|
96
|
+
const onKeyPress = (key) => {
|
|
97
|
+
// Handle vertical navigation
|
|
98
|
+
const newIndex = (0, menu_core_1.handleVerticalNavigation)(key, selectedIndex, options.length - 1);
|
|
99
|
+
if (newIndex !== null) {
|
|
100
|
+
selectedIndex = newIndex;
|
|
101
|
+
render();
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
// Handle number input
|
|
105
|
+
const numIndex = (0, menu_core_1.handleNumberInput)(key, options.length);
|
|
106
|
+
if (numIndex !== null) {
|
|
107
|
+
selectedIndex = numIndex;
|
|
108
|
+
render();
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
// Handle letter input
|
|
112
|
+
const letterIndex = (0, menu_core_1.handleLetterInput)(key, options);
|
|
113
|
+
if (letterIndex !== null) {
|
|
114
|
+
selectedIndex = letterIndex;
|
|
115
|
+
render();
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
// Handle Enter key
|
|
119
|
+
if (key === '\r') {
|
|
120
|
+
cleanup(selectedIndex);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
// Handle Escape key (for firstRun type)
|
|
124
|
+
if (key === '\u001b' || key === '\u001b[') {
|
|
125
|
+
if (type === 'firstRun') {
|
|
126
|
+
(0, menu_core_1.exitWithGoodbye)(state, onKeyPress, () => (0, components_1.showGoodbye)(lang));
|
|
127
|
+
}
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
// Handle Ctrl+C
|
|
131
|
+
if (key === '\u0003') {
|
|
132
|
+
(0, menu_core_1.exitWithGoodbye)(state, onKeyPress, () => (0, components_1.showGoodbye)(lang));
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
state.stdin.on('data', onKeyPress);
|
|
136
|
+
render();
|
|
137
|
+
});
|
|
138
|
+
}
|
package/dist/menu.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Menu Kit - Unified Menu Wrapper
|
|
3
|
+
* Convenient API for all menu types
|
|
4
|
+
*/
|
|
5
|
+
import { selectMenu } from './menu-single';
|
|
6
|
+
import { selectMultiMenu } from './menu-multi';
|
|
7
|
+
import { askYesNo, askInput, askNumber } from './input';
|
|
8
|
+
import { MenuConfig, MenuOption, MultiSelectConfig } from './types';
|
|
9
|
+
/**
|
|
10
|
+
* Unified menu interface
|
|
11
|
+
* Provides convenient access to all menu types
|
|
12
|
+
*/
|
|
13
|
+
export declare const menu: {
|
|
14
|
+
/**
|
|
15
|
+
* Single-select menu (vertical)
|
|
16
|
+
* @param options - Menu options
|
|
17
|
+
* @param config - Configuration
|
|
18
|
+
* @returns Selected index
|
|
19
|
+
*/
|
|
20
|
+
select: typeof selectMenu;
|
|
21
|
+
/**
|
|
22
|
+
* Multi-select menu (vertical with checkboxes)
|
|
23
|
+
* @param options - Menu options
|
|
24
|
+
* @param config - Configuration
|
|
25
|
+
* @returns Array of selected indices
|
|
26
|
+
*/
|
|
27
|
+
multiSelect: typeof selectMultiMenu;
|
|
28
|
+
/**
|
|
29
|
+
* Yes/No confirmation (horizontal single-select)
|
|
30
|
+
* @param prompt - Question to ask
|
|
31
|
+
* @param options - Configuration
|
|
32
|
+
* @returns true for Yes, false for No
|
|
33
|
+
*/
|
|
34
|
+
confirm: typeof askYesNo;
|
|
35
|
+
/**
|
|
36
|
+
* Text input
|
|
37
|
+
* @param prompt - Input prompt
|
|
38
|
+
* @param options - Configuration with validation
|
|
39
|
+
* @returns User input string
|
|
40
|
+
*/
|
|
41
|
+
input: typeof askInput;
|
|
42
|
+
/**
|
|
43
|
+
* Number input
|
|
44
|
+
* @param prompt - Input prompt
|
|
45
|
+
* @param options - Configuration with min/max
|
|
46
|
+
* @returns User input number
|
|
47
|
+
*/
|
|
48
|
+
number: typeof askNumber;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Create a parent-child menu relationship
|
|
52
|
+
* Parent is single-select, child is multi-select
|
|
53
|
+
*
|
|
54
|
+
* @param parentOptions - Parent menu options
|
|
55
|
+
* @param getChildOptions - Function to get child options based on parent selection
|
|
56
|
+
* @param config - Configuration
|
|
57
|
+
* @returns Object with parentIndex and childIndices
|
|
58
|
+
*/
|
|
59
|
+
export declare function selectWithChildren(parentOptions: Array<string | MenuOption>, getChildOptions: (parentIndex: number) => string[], config?: {
|
|
60
|
+
parentConfig?: MenuConfig;
|
|
61
|
+
childConfig?: MultiSelectConfig;
|
|
62
|
+
}): Promise<{
|
|
63
|
+
parentIndex: number;
|
|
64
|
+
childIndices: number[];
|
|
65
|
+
}>;
|
|
66
|
+
export { selectMenu, selectMultiMenu, askYesNo, askInput, askNumber };
|
package/dist/menu.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CLI Menu Kit - Unified Menu Wrapper
|
|
4
|
+
* Convenient API for all menu types
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.askNumber = exports.askInput = exports.askYesNo = exports.selectMultiMenu = exports.selectMenu = exports.menu = void 0;
|
|
8
|
+
exports.selectWithChildren = selectWithChildren;
|
|
9
|
+
const menu_single_1 = require("./menu-single");
|
|
10
|
+
Object.defineProperty(exports, "selectMenu", { enumerable: true, get: function () { return menu_single_1.selectMenu; } });
|
|
11
|
+
const menu_multi_1 = require("./menu-multi");
|
|
12
|
+
Object.defineProperty(exports, "selectMultiMenu", { enumerable: true, get: function () { return menu_multi_1.selectMultiMenu; } });
|
|
13
|
+
const input_1 = require("./input");
|
|
14
|
+
Object.defineProperty(exports, "askYesNo", { enumerable: true, get: function () { return input_1.askYesNo; } });
|
|
15
|
+
Object.defineProperty(exports, "askInput", { enumerable: true, get: function () { return input_1.askInput; } });
|
|
16
|
+
Object.defineProperty(exports, "askNumber", { enumerable: true, get: function () { return input_1.askNumber; } });
|
|
17
|
+
/**
|
|
18
|
+
* Unified menu interface
|
|
19
|
+
* Provides convenient access to all menu types
|
|
20
|
+
*/
|
|
21
|
+
exports.menu = {
|
|
22
|
+
/**
|
|
23
|
+
* Single-select menu (vertical)
|
|
24
|
+
* @param options - Menu options
|
|
25
|
+
* @param config - Configuration
|
|
26
|
+
* @returns Selected index
|
|
27
|
+
*/
|
|
28
|
+
select: menu_single_1.selectMenu,
|
|
29
|
+
/**
|
|
30
|
+
* Multi-select menu (vertical with checkboxes)
|
|
31
|
+
* @param options - Menu options
|
|
32
|
+
* @param config - Configuration
|
|
33
|
+
* @returns Array of selected indices
|
|
34
|
+
*/
|
|
35
|
+
multiSelect: menu_multi_1.selectMultiMenu,
|
|
36
|
+
/**
|
|
37
|
+
* Yes/No confirmation (horizontal single-select)
|
|
38
|
+
* @param prompt - Question to ask
|
|
39
|
+
* @param options - Configuration
|
|
40
|
+
* @returns true for Yes, false for No
|
|
41
|
+
*/
|
|
42
|
+
confirm: input_1.askYesNo,
|
|
43
|
+
/**
|
|
44
|
+
* Text input
|
|
45
|
+
* @param prompt - Input prompt
|
|
46
|
+
* @param options - Configuration with validation
|
|
47
|
+
* @returns User input string
|
|
48
|
+
*/
|
|
49
|
+
input: input_1.askInput,
|
|
50
|
+
/**
|
|
51
|
+
* Number input
|
|
52
|
+
* @param prompt - Input prompt
|
|
53
|
+
* @param options - Configuration with min/max
|
|
54
|
+
* @returns User input number
|
|
55
|
+
*/
|
|
56
|
+
number: input_1.askNumber
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Create a parent-child menu relationship
|
|
60
|
+
* Parent is single-select, child is multi-select
|
|
61
|
+
*
|
|
62
|
+
* @param parentOptions - Parent menu options
|
|
63
|
+
* @param getChildOptions - Function to get child options based on parent selection
|
|
64
|
+
* @param config - Configuration
|
|
65
|
+
* @returns Object with parentIndex and childIndices
|
|
66
|
+
*/
|
|
67
|
+
async function selectWithChildren(parentOptions, getChildOptions, config = {}) {
|
|
68
|
+
// Show parent menu
|
|
69
|
+
const parentIndex = await (0, menu_single_1.selectMenu)(parentOptions, config.parentConfig);
|
|
70
|
+
// Get child options based on parent selection
|
|
71
|
+
const childOptions = getChildOptions(parentIndex);
|
|
72
|
+
// Show child menu if there are options
|
|
73
|
+
let childIndices = [];
|
|
74
|
+
if (childOptions.length > 0) {
|
|
75
|
+
childIndices = await (0, menu_multi_1.selectMultiMenu)(childOptions, config.childConfig);
|
|
76
|
+
}
|
|
77
|
+
return { parentIndex, childIndices };
|
|
78
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Display component types for CLI Menu Kit
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Header types
|
|
6
|
+
*/
|
|
7
|
+
export type HeaderType = 'simple' | 'ascii';
|
|
8
|
+
/**
|
|
9
|
+
* Simple header configuration
|
|
10
|
+
*/
|
|
11
|
+
export interface SimpleHeaderConfig {
|
|
12
|
+
text: string;
|
|
13
|
+
color?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* ASCII header configuration
|
|
17
|
+
*/
|
|
18
|
+
export interface AsciiHeaderConfig {
|
|
19
|
+
asciiArt: string;
|
|
20
|
+
subtitle?: string;
|
|
21
|
+
version?: string;
|
|
22
|
+
url?: string;
|
|
23
|
+
borderChar?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Progress indicator configuration
|
|
27
|
+
*/
|
|
28
|
+
export interface ProgressConfig {
|
|
29
|
+
steps: string[];
|
|
30
|
+
currentStep: number;
|
|
31
|
+
separator?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Message types
|
|
35
|
+
*/
|
|
36
|
+
export type MessageType = 'success' | 'error' | 'warning' | 'info' | 'question';
|
|
37
|
+
/**
|
|
38
|
+
* Message configuration
|
|
39
|
+
*/
|
|
40
|
+
export interface MessageConfig {
|
|
41
|
+
type: MessageType;
|
|
42
|
+
message: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Summary table configuration
|
|
46
|
+
*/
|
|
47
|
+
export interface SummaryTableConfig {
|
|
48
|
+
title?: string;
|
|
49
|
+
sections: Array<{
|
|
50
|
+
header?: string;
|
|
51
|
+
items: Array<{
|
|
52
|
+
key: string;
|
|
53
|
+
value: string;
|
|
54
|
+
}>;
|
|
55
|
+
}>;
|
|
56
|
+
width?: number;
|
|
57
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input component types for CLI Menu Kit
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Base input configuration
|
|
6
|
+
*/
|
|
7
|
+
export interface BaseInputConfig {
|
|
8
|
+
/** Input prompt text */
|
|
9
|
+
prompt: string;
|
|
10
|
+
/** Default value */
|
|
11
|
+
defaultValue?: string;
|
|
12
|
+
/** Validation function */
|
|
13
|
+
validate?: (value: string) => boolean | string;
|
|
14
|
+
/** Error message for validation failure */
|
|
15
|
+
errorMessage?: string;
|
|
16
|
+
/** Goodbye message function */
|
|
17
|
+
onExit?: () => void;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Text input configuration
|
|
21
|
+
*/
|
|
22
|
+
export interface TextInputConfig extends BaseInputConfig {
|
|
23
|
+
/** Placeholder text */
|
|
24
|
+
placeholder?: string;
|
|
25
|
+
/** Maximum length */
|
|
26
|
+
maxLength?: number;
|
|
27
|
+
/** Minimum length */
|
|
28
|
+
minLength?: number;
|
|
29
|
+
/** Allow empty input */
|
|
30
|
+
allowEmpty?: boolean;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Number input configuration
|
|
34
|
+
*/
|
|
35
|
+
export interface NumberInputConfig extends BaseInputConfig {
|
|
36
|
+
/** Minimum value */
|
|
37
|
+
min?: number;
|
|
38
|
+
/** Maximum value */
|
|
39
|
+
max?: number;
|
|
40
|
+
/** Allow decimals */
|
|
41
|
+
allowDecimals?: boolean;
|
|
42
|
+
/** Allow negative numbers */
|
|
43
|
+
allowNegative?: boolean;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Language selector configuration
|
|
47
|
+
*/
|
|
48
|
+
export interface LanguageSelectorConfig {
|
|
49
|
+
/** Available languages */
|
|
50
|
+
languages: Array<{
|
|
51
|
+
code: string;
|
|
52
|
+
name: string;
|
|
53
|
+
nativeName?: string;
|
|
54
|
+
}>;
|
|
55
|
+
/** Default language code */
|
|
56
|
+
defaultLanguage?: string;
|
|
57
|
+
/** Prompt text */
|
|
58
|
+
prompt?: string;
|
|
59
|
+
/** Goodbye message function */
|
|
60
|
+
onExit?: () => void;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Modify field configuration
|
|
64
|
+
*/
|
|
65
|
+
export interface ModifyFieldConfig {
|
|
66
|
+
/** Field name */
|
|
67
|
+
fieldName: string;
|
|
68
|
+
/** Current value */
|
|
69
|
+
currentValue: string;
|
|
70
|
+
/** Prompt for modification */
|
|
71
|
+
modifyPrompt?: string;
|
|
72
|
+
/** Prompt for new value */
|
|
73
|
+
newValuePrompt?: string;
|
|
74
|
+
/** Validation function */
|
|
75
|
+
validate?: (value: string) => boolean | string;
|
|
76
|
+
/** Goodbye message function */
|
|
77
|
+
onExit?: () => void;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Input result types
|
|
81
|
+
*/
|
|
82
|
+
export type TextInputResult = string;
|
|
83
|
+
export type NumberInputResult = number;
|
|
84
|
+
export type LanguageSelectorResult = string;
|
|
85
|
+
export interface ModifyFieldResult {
|
|
86
|
+
modified: boolean;
|
|
87
|
+
value: string;
|
|
88
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layout system types for CLI Menu Kit
|
|
3
|
+
* Defines how components are composed and rendered
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Layout element types that can be arranged
|
|
7
|
+
*/
|
|
8
|
+
export type LayoutElement = 'header' | 'options' | 'input' | 'hints' | 'progress';
|
|
9
|
+
/**
|
|
10
|
+
* Visibility configuration for layout elements
|
|
11
|
+
*/
|
|
12
|
+
export interface LayoutVisibility {
|
|
13
|
+
header?: boolean;
|
|
14
|
+
input?: boolean;
|
|
15
|
+
hints?: boolean;
|
|
16
|
+
progress?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Spacing configuration for layout elements
|
|
20
|
+
* Values represent number of blank lines
|
|
21
|
+
*/
|
|
22
|
+
export interface LayoutSpacing {
|
|
23
|
+
beforeHeader?: number;
|
|
24
|
+
afterHeader?: number;
|
|
25
|
+
beforeOptions?: number;
|
|
26
|
+
afterOptions?: number;
|
|
27
|
+
beforeInput?: number;
|
|
28
|
+
afterInput?: number;
|
|
29
|
+
beforeHints?: number;
|
|
30
|
+
beforeProgress?: number;
|
|
31
|
+
afterProgress?: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Complete menu layout configuration
|
|
35
|
+
*/
|
|
36
|
+
export interface MenuLayout {
|
|
37
|
+
/** Order in which elements are rendered */
|
|
38
|
+
order: LayoutElement[];
|
|
39
|
+
/** Which elements are visible */
|
|
40
|
+
visible: LayoutVisibility;
|
|
41
|
+
/** Spacing between elements */
|
|
42
|
+
spacing?: LayoutSpacing;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Preset layout configurations
|
|
46
|
+
*/
|
|
47
|
+
export declare const LAYOUT_PRESETS: {
|
|
48
|
+
/** Main menu with header */
|
|
49
|
+
MAIN_MENU: MenuLayout;
|
|
50
|
+
/** Sub menu without header */
|
|
51
|
+
SUB_MENU: MenuLayout;
|
|
52
|
+
/** Wizard step with progress indicator */
|
|
53
|
+
WIZARD_STEP: MenuLayout;
|
|
54
|
+
/** Minimal layout (options only) */
|
|
55
|
+
MINIMAL: MenuLayout;
|
|
56
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Layout system types for CLI Menu Kit
|
|
4
|
+
* Defines how components are composed and rendered
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.LAYOUT_PRESETS = void 0;
|
|
8
|
+
/**
|
|
9
|
+
* Preset layout configurations
|
|
10
|
+
*/
|
|
11
|
+
exports.LAYOUT_PRESETS = {
|
|
12
|
+
/** Main menu with header */
|
|
13
|
+
MAIN_MENU: {
|
|
14
|
+
order: ['header', 'options', 'input', 'hints'],
|
|
15
|
+
visible: { header: true, input: true, hints: true },
|
|
16
|
+
spacing: { afterHeader: 1, afterOptions: 1, beforeHints: 1 }
|
|
17
|
+
},
|
|
18
|
+
/** Sub menu without header */
|
|
19
|
+
SUB_MENU: {
|
|
20
|
+
order: ['options', 'input', 'hints'],
|
|
21
|
+
visible: { header: false, input: true, hints: true },
|
|
22
|
+
spacing: { afterOptions: 1, beforeHints: 1 }
|
|
23
|
+
},
|
|
24
|
+
/** Wizard step with progress indicator */
|
|
25
|
+
WIZARD_STEP: {
|
|
26
|
+
order: ['header', 'progress', 'options', 'input', 'hints'],
|
|
27
|
+
visible: { header: true, progress: true, input: true, hints: true },
|
|
28
|
+
spacing: { afterHeader: 1, afterProgress: 1, afterOptions: 1, beforeHints: 1 }
|
|
29
|
+
},
|
|
30
|
+
/** Minimal layout (options only) */
|
|
31
|
+
MINIMAL: {
|
|
32
|
+
order: ['options'],
|
|
33
|
+
visible: { header: false, input: false, hints: false },
|
|
34
|
+
spacing: {}
|
|
35
|
+
}
|
|
36
|
+
};
|