cli-menu-kit 0.2.0 → 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/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/index.d.ts +1 -1
- package/dist/components/display/index.js +3 -1
- 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 +2 -0
- package/dist/components/display/table.js +7 -6
- 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 +33 -20
- package/dist/components/menus/checkbox-menu.js +5 -2
- package/dist/components/menus/checkbox-table-menu.js +12 -9
- package/dist/components/menus/radio-menu-split.d.ts +1 -0
- package/dist/components/menus/radio-menu-split.js +26 -16
- package/dist/components/menus/radio-menu.js +67 -38
- 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/renderer.js +26 -18
- package/dist/core/terminal.d.ts +13 -0
- package/dist/core/terminal.js +87 -0
- package/dist/features/commands.js +23 -22
- package/dist/index.d.ts +3 -1
- package/dist/index.js +21 -2
- package/dist/layout.d.ts +50 -51
- package/dist/layout.js +69 -117
- package/dist/page-layout.d.ts +31 -0
- package/dist/page-layout.js +46 -7
- package/dist/types/input.types.d.ts +8 -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 +4 -0
- package/package.json +4 -1
|
@@ -67,14 +67,14 @@ function renderTableHeader(columns, columnWidths, checkboxWidth, showHeaderSepar
|
|
|
67
67
|
const width = columnWidths[index];
|
|
68
68
|
const align = col.align || 'left';
|
|
69
69
|
const paddedHeader = (0, renderer_js_1.padText)(col.header, width, align);
|
|
70
|
-
headerLine += `${colors_js_1.
|
|
70
|
+
headerLine += `${colors_js_1.uiColors.primary}${colors_js_1.colors.bold}${paddedHeader}${colors_js_1.colors.reset}`;
|
|
71
71
|
});
|
|
72
72
|
process.stdout.write(` ${headerLine}\n`);
|
|
73
73
|
lineCount++;
|
|
74
74
|
// Separator line
|
|
75
75
|
if (showHeaderSeparator) {
|
|
76
76
|
const totalWidth = checkboxWidth + columnWidths.reduce((sum, w) => sum + w, 0);
|
|
77
|
-
process.stdout.write(` ${colors_js_1.
|
|
77
|
+
process.stdout.write(` ${colors_js_1.uiColors.separator}${'─'.repeat(totalWidth)}${colors_js_1.colors.reset}\n`);
|
|
78
78
|
lineCount++;
|
|
79
79
|
}
|
|
80
80
|
// Blank line after header
|
|
@@ -89,15 +89,15 @@ function renderTableRow(rowData, columns, columnWidths, isSelected, isHighlighte
|
|
|
89
89
|
let line = '';
|
|
90
90
|
// Cursor indicator with background for highlighted row
|
|
91
91
|
if (isHighlighted) {
|
|
92
|
-
line += `${colors_js_1.
|
|
92
|
+
line += `${colors_js_1.uiColors.cursor}${colors_js_1.colors.bold}❯ ${colors_js_1.colors.reset}`;
|
|
93
93
|
}
|
|
94
94
|
else {
|
|
95
95
|
line += ' ';
|
|
96
96
|
}
|
|
97
97
|
// Checkbox
|
|
98
98
|
line += isSelected
|
|
99
|
-
? `${colors_js_1.
|
|
100
|
-
: `${colors_js_1.
|
|
99
|
+
? `${colors_js_1.uiColors.selected}◉${colors_js_1.colors.reset} `
|
|
100
|
+
: `${colors_js_1.uiColors.disabled}○${colors_js_1.colors.reset} `;
|
|
101
101
|
// Table cells with background for highlighted row
|
|
102
102
|
columns.forEach((col, colIndex) => {
|
|
103
103
|
const value = String(rowData[col.key] || '');
|
|
@@ -109,15 +109,15 @@ function renderTableRow(rowData, columns, columnWidths, isSelected, isHighlighte
|
|
|
109
109
|
// Apply color and background based on state
|
|
110
110
|
if (isHighlighted) {
|
|
111
111
|
// Highlighted row: cyan text with reverse video (background)
|
|
112
|
-
line += `${colors_js_1.
|
|
112
|
+
line += `${colors_js_1.uiColors.primary}${colors_js_1.colors.bold}\x1b[7m${paddedValue}\x1b[27m${colors_js_1.colors.reset}`;
|
|
113
113
|
}
|
|
114
114
|
else if (isSelected) {
|
|
115
115
|
// Selected but not highlighted: normal text
|
|
116
|
-
line += `${colors_js_1.
|
|
116
|
+
line += `${colors_js_1.uiColors.textPrimary}${paddedValue}${colors_js_1.colors.reset}`;
|
|
117
117
|
}
|
|
118
118
|
else {
|
|
119
119
|
// Not selected: dim text
|
|
120
|
-
line += `${colors_js_1.
|
|
120
|
+
line += `${colors_js_1.uiColors.textSecondary}${paddedValue}${colors_js_1.colors.reset}`;
|
|
121
121
|
}
|
|
122
122
|
});
|
|
123
123
|
process.stdout.write(line + '\n');
|
|
@@ -130,6 +130,7 @@ function renderTableRow(rowData, columns, columnWidths, isSelected, isHighlighte
|
|
|
130
130
|
*/
|
|
131
131
|
async function showCheckboxTableMenu(config, hints) {
|
|
132
132
|
const { columns, data, idKey, defaultSelected = [], minSelections = 0, maxSelections, allowSelectAll = true, allowInvert = true, showBorders = false, showHeaderSeparator = true, separators, separatorAlign = 'center', widthMode = 'fixed', checkboxWidth = 4, title, prompt, separatorWidth = 30, onExit, preserveOnSelect = false } = config;
|
|
133
|
+
const preserveOnExit = config.preserveOnExit ?? preserveOnSelect;
|
|
133
134
|
// Validate data
|
|
134
135
|
if (!data || data.length === 0) {
|
|
135
136
|
throw new Error('CheckboxTableMenu requires at least one data row');
|
|
@@ -297,7 +298,9 @@ async function showCheckboxTableMenu(config, hints) {
|
|
|
297
298
|
const onData = (key) => {
|
|
298
299
|
if ((0, keyboard_js_1.isCtrlC)(key)) {
|
|
299
300
|
state.stdin.removeListener('data', onData);
|
|
300
|
-
|
|
301
|
+
if (!preserveOnExit) {
|
|
302
|
+
(0, terminal_js_1.clearMenu)(state);
|
|
303
|
+
}
|
|
301
304
|
(0, terminal_js_1.restoreTerminal)(state);
|
|
302
305
|
if (onExit)
|
|
303
306
|
onExit();
|
|
@@ -31,20 +31,24 @@ function renderRadioMenuUI(config) {
|
|
|
31
31
|
const optionData = [];
|
|
32
32
|
options.forEach((opt, index) => {
|
|
33
33
|
if (typeof opt === 'object' && 'type' in opt && opt.type === 'separator') {
|
|
34
|
-
optionData.push({ value: '', isSeparator: true, label: opt.label });
|
|
34
|
+
optionData.push({ display: '', value: '', isSeparator: true, label: opt.label });
|
|
35
35
|
}
|
|
36
36
|
else {
|
|
37
|
+
let display;
|
|
37
38
|
let value;
|
|
38
39
|
if (typeof opt === 'string') {
|
|
40
|
+
display = opt;
|
|
39
41
|
value = opt;
|
|
40
42
|
}
|
|
41
43
|
else if ('value' in opt) {
|
|
44
|
+
display = opt.label ?? String(opt.value ?? '');
|
|
42
45
|
value = opt.value ?? opt.label ?? '';
|
|
43
46
|
}
|
|
44
47
|
else {
|
|
48
|
+
display = opt.label ?? '';
|
|
45
49
|
value = opt.label ?? '';
|
|
46
50
|
}
|
|
47
|
-
optionData.push({ value, isSeparator: false });
|
|
51
|
+
optionData.push({ display, value, isSeparator: false });
|
|
48
52
|
selectableIndices.push(index);
|
|
49
53
|
}
|
|
50
54
|
});
|
|
@@ -56,7 +60,7 @@ function renderRadioMenuUI(config) {
|
|
|
56
60
|
let lineCount = 0;
|
|
57
61
|
// Render menu title if provided
|
|
58
62
|
if (title) {
|
|
59
|
-
(0, renderer_js_1.renderHeader)(` ${title}`, colors_js_1.
|
|
63
|
+
(0, renderer_js_1.renderHeader)(` ${title}`, colors_js_1.uiColors.primary);
|
|
60
64
|
lineCount++;
|
|
61
65
|
(0, renderer_js_1.renderBlankLines)(1);
|
|
62
66
|
lineCount++;
|
|
@@ -67,12 +71,15 @@ function renderRadioMenuUI(config) {
|
|
|
67
71
|
(0, renderer_js_1.renderSectionLabel)(item.label, separatorWidth);
|
|
68
72
|
}
|
|
69
73
|
else {
|
|
70
|
-
const numberMatch = item.
|
|
71
|
-
const letterMatch = item.
|
|
74
|
+
const numberMatch = item.display.match(/^(\d+)\.\s*/);
|
|
75
|
+
const letterMatch = item.display.match(/^([a-zA-Z])\.\s*/);
|
|
72
76
|
const prefix = (numberMatch || letterMatch) ? '' : `${selectableIndices.indexOf(index) + 1}. `;
|
|
73
|
-
const isExitOption = /\b(exit|quit)\b/i.test(item.
|
|
74
|
-
const
|
|
75
|
-
|
|
77
|
+
const isExitOption = /\b(exit|quit)\b/i.test(item.display);
|
|
78
|
+
const isCurrent = index === selectedIndex;
|
|
79
|
+
const displayValue = isExitOption && !isCurrent
|
|
80
|
+
? `${colors_js_1.uiColors.error}${item.display}${colors_js_1.colors.reset}`
|
|
81
|
+
: item.display;
|
|
82
|
+
(0, renderer_js_1.renderOption)(displayValue, undefined, isCurrent, prefix);
|
|
76
83
|
const nextIndex = index + 1;
|
|
77
84
|
if (nextIndex < optionData.length && optionData[nextIndex].isSeparator) {
|
|
78
85
|
(0, terminal_js_1.writeLine)('');
|
|
@@ -130,7 +137,7 @@ async function waitForRadioMenuInput(menuState) {
|
|
|
130
137
|
let lineCount = 0;
|
|
131
138
|
// Render menu title if provided
|
|
132
139
|
if (config.title) {
|
|
133
|
-
(0, renderer_js_1.renderHeader)(` ${config.title}`, colors_js_1.
|
|
140
|
+
(0, renderer_js_1.renderHeader)(` ${config.title}`, colors_js_1.uiColors.primary);
|
|
134
141
|
lineCount++;
|
|
135
142
|
(0, renderer_js_1.renderBlankLines)(1);
|
|
136
143
|
lineCount++;
|
|
@@ -141,12 +148,15 @@ async function waitForRadioMenuInput(menuState) {
|
|
|
141
148
|
(0, renderer_js_1.renderSectionLabel)(item.label, config.separatorWidth || 30);
|
|
142
149
|
}
|
|
143
150
|
else {
|
|
144
|
-
const numberMatch = item.
|
|
145
|
-
const letterMatch = item.
|
|
151
|
+
const numberMatch = item.display.match(/^(\d+)\.\s*/);
|
|
152
|
+
const letterMatch = item.display.match(/^([a-zA-Z])\.\s*/);
|
|
146
153
|
const prefix = (numberMatch || letterMatch) ? '' : `${selectableIndices.indexOf(index) + 1}. `;
|
|
147
|
-
const isExitOption = /\b(exit|quit)\b/i.test(item.
|
|
148
|
-
const
|
|
149
|
-
|
|
154
|
+
const isExitOption = /\b(exit|quit)\b/i.test(item.display);
|
|
155
|
+
const isCurrent = index === selectedIndex;
|
|
156
|
+
const displayValue = isExitOption && !isCurrent
|
|
157
|
+
? `${colors_js_1.uiColors.error}${item.display}${colors_js_1.colors.reset}`
|
|
158
|
+
: item.display;
|
|
159
|
+
(0, renderer_js_1.renderOption)(displayValue, undefined, isCurrent, prefix);
|
|
150
160
|
const nextIndex = index + 1;
|
|
151
161
|
if (nextIndex < optionData.length && optionData[nextIndex].isSeparator) {
|
|
152
162
|
(0, terminal_js_1.writeLine)('');
|
|
@@ -188,7 +198,7 @@ async function waitForRadioMenuInput(menuState) {
|
|
|
188
198
|
onExit();
|
|
189
199
|
}
|
|
190
200
|
else {
|
|
191
|
-
console.log(
|
|
201
|
+
console.log(`\n${(0, registry_js_1.t)('messages.goodbye')}`);
|
|
192
202
|
}
|
|
193
203
|
process.exit(0);
|
|
194
204
|
}
|
|
@@ -233,7 +243,7 @@ async function waitForRadioMenuInput(menuState) {
|
|
|
233
243
|
const item = optionData[idx];
|
|
234
244
|
if (!item || item.isSeparator)
|
|
235
245
|
return false;
|
|
236
|
-
const normalized = (0, keyboard_js_1.normalizeLetter)(item.
|
|
246
|
+
const normalized = (0, keyboard_js_1.normalizeLetter)(item.display.charAt(0));
|
|
237
247
|
return normalized === letter;
|
|
238
248
|
});
|
|
239
249
|
if (matchingIndex !== undefined) {
|
|
@@ -11,6 +11,30 @@ const renderer_js_1 = require("../../core/renderer.js");
|
|
|
11
11
|
const renderer_js_2 = require("../../core/renderer.js");
|
|
12
12
|
const colors_js_1 = require("../../core/colors.js");
|
|
13
13
|
const registry_js_1 = require("../../i18n/registry.js");
|
|
14
|
+
function buildSectionLabelText(label, width) {
|
|
15
|
+
if (!label) {
|
|
16
|
+
return '';
|
|
17
|
+
}
|
|
18
|
+
const labelWithPadding = ` ${label} `;
|
|
19
|
+
const dashesTotal = Math.max(0, width - labelWithPadding.length);
|
|
20
|
+
const dashesLeft = Math.floor(dashesTotal / 2);
|
|
21
|
+
const dashesRight = dashesTotal - dashesLeft;
|
|
22
|
+
return ` ${'─'.repeat(dashesLeft)}${labelWithPadding}${'─'.repeat(dashesRight)}`;
|
|
23
|
+
}
|
|
24
|
+
function buildOptionRenderText(text, isHighlighted, prefix) {
|
|
25
|
+
const parts = text.split(' - ');
|
|
26
|
+
const mainText = parts[0] || '';
|
|
27
|
+
const description = parts.length > 1 ? parts.slice(1).join(' - ') : '';
|
|
28
|
+
let line = isHighlighted ? '❯ ' : ' ';
|
|
29
|
+
if (prefix) {
|
|
30
|
+
line += prefix;
|
|
31
|
+
}
|
|
32
|
+
line += mainText;
|
|
33
|
+
if (description) {
|
|
34
|
+
line += ` - ${description}`;
|
|
35
|
+
}
|
|
36
|
+
return line;
|
|
37
|
+
}
|
|
14
38
|
/**
|
|
15
39
|
* Show a radio menu (single-select)
|
|
16
40
|
* @param config - Menu configuration
|
|
@@ -19,6 +43,7 @@ const registry_js_1 = require("../../i18n/registry.js");
|
|
|
19
43
|
*/
|
|
20
44
|
async function showRadioMenu(config, hints) {
|
|
21
45
|
const { options, title, prompt, defaultIndex = 0, allowNumberKeys = true, allowLetterKeys = false, separatorWidth = 30, onExit, preserveOnSelect = false } = config;
|
|
46
|
+
const preserveOnExit = config.preserveOnExit ?? preserveOnSelect;
|
|
22
47
|
// Use i18n for default prompt if not provided
|
|
23
48
|
const displayPrompt = prompt || (0, registry_js_1.t)('menus.selectPrompt');
|
|
24
49
|
// Validate options
|
|
@@ -33,20 +58,24 @@ async function showRadioMenu(config, hints) {
|
|
|
33
58
|
const optionData = [];
|
|
34
59
|
options.forEach((opt, index) => {
|
|
35
60
|
if (typeof opt === 'object' && 'type' in opt && opt.type === 'separator') {
|
|
36
|
-
optionData.push({ value: '', isSeparator: true, label: opt.label });
|
|
61
|
+
optionData.push({ display: '', value: '', isSeparator: true, label: opt.label });
|
|
37
62
|
}
|
|
38
63
|
else {
|
|
64
|
+
let display;
|
|
39
65
|
let value;
|
|
40
66
|
if (typeof opt === 'string') {
|
|
67
|
+
display = opt;
|
|
41
68
|
value = opt;
|
|
42
69
|
}
|
|
43
70
|
else if ('value' in opt) {
|
|
71
|
+
display = opt.label ?? String(opt.value ?? '');
|
|
44
72
|
value = opt.value ?? opt.label ?? '';
|
|
45
73
|
}
|
|
46
74
|
else {
|
|
75
|
+
display = opt.label ?? '';
|
|
47
76
|
value = opt.label ?? '';
|
|
48
77
|
}
|
|
49
|
-
optionData.push({ value, isSeparator: false });
|
|
78
|
+
optionData.push({ display, value, isSeparator: false });
|
|
50
79
|
selectableIndices.push(index);
|
|
51
80
|
}
|
|
52
81
|
});
|
|
@@ -74,47 +103,55 @@ async function showRadioMenu(config, hints) {
|
|
|
74
103
|
const render = () => {
|
|
75
104
|
(0, terminal_js_1.clearMenu)(state);
|
|
76
105
|
let lineCount = 0;
|
|
106
|
+
const addVisualLines = (text) => {
|
|
107
|
+
lineCount += (0, terminal_js_1.countVisualLines)(text);
|
|
108
|
+
};
|
|
77
109
|
// Render title if provided
|
|
78
110
|
if (title) {
|
|
79
|
-
(0, renderer_js_1.renderHeader)(` ${title}`, colors_js_1.
|
|
80
|
-
|
|
111
|
+
(0, renderer_js_1.renderHeader)(` ${title}`, colors_js_1.uiColors.primary);
|
|
112
|
+
addVisualLines(` ${title}`);
|
|
81
113
|
(0, renderer_js_1.renderBlankLines)(1);
|
|
82
|
-
|
|
114
|
+
addVisualLines('');
|
|
83
115
|
}
|
|
84
116
|
// Render options
|
|
85
117
|
optionData.forEach((item, index) => {
|
|
86
118
|
if (item.isSeparator) {
|
|
87
119
|
// Render section label with configured width
|
|
88
120
|
(0, renderer_js_1.renderSectionLabel)(item.label, separatorWidth);
|
|
121
|
+
addVisualLines(buildSectionLabelText(item.label, separatorWidth));
|
|
89
122
|
}
|
|
90
123
|
else {
|
|
91
124
|
// Check if option starts with a number or letter prefix
|
|
92
|
-
const numberMatch = item.
|
|
93
|
-
const letterMatch = item.
|
|
125
|
+
const numberMatch = item.display.match(/^(\d+)\.\s*/);
|
|
126
|
+
const letterMatch = item.display.match(/^([a-zA-Z])\.\s*/);
|
|
94
127
|
// Don't add prefix if option already has number or letter prefix
|
|
95
128
|
const prefix = (numberMatch || letterMatch) ? '' : `${selectableIndices.indexOf(index) + 1}. `;
|
|
96
|
-
// Check if this is an Exit option (contains "Exit" or "
|
|
97
|
-
|
|
98
|
-
const
|
|
129
|
+
// Check if this is an Exit option (contains "Exit", "Quit", or Chinese "退出")
|
|
130
|
+
// Keep unselected exit entries in error color, but let selected row use normal highlight style.
|
|
131
|
+
const isExitOption = /\b(exit|quit)\b|退出/i.test(item.display);
|
|
132
|
+
const isCurrent = index === selectedIndex;
|
|
133
|
+
const displayValue = isExitOption && !isCurrent
|
|
134
|
+
? `${colors_js_1.uiColors.error}${item.display}${colors_js_1.colors.reset}`
|
|
135
|
+
: item.display;
|
|
99
136
|
// For radio menus, don't show selection indicator (pass undefined instead of false)
|
|
100
|
-
(0, renderer_js_1.renderOption)(displayValue, undefined,
|
|
137
|
+
(0, renderer_js_1.renderOption)(displayValue, undefined, isCurrent, prefix);
|
|
138
|
+
addVisualLines(buildOptionRenderText(displayValue, isCurrent, prefix));
|
|
101
139
|
// Add blank line after last item before next separator
|
|
102
140
|
const nextIndex = index + 1;
|
|
103
141
|
if (nextIndex < optionData.length && optionData[nextIndex].isSeparator) {
|
|
104
142
|
(0, terminal_js_1.writeLine)('');
|
|
105
|
-
|
|
143
|
+
addVisualLines('');
|
|
106
144
|
}
|
|
107
145
|
}
|
|
108
|
-
lineCount++;
|
|
109
146
|
});
|
|
110
147
|
// Render input prompt
|
|
111
148
|
(0, renderer_js_1.renderBlankLines)(1);
|
|
112
|
-
|
|
149
|
+
addVisualLines('');
|
|
113
150
|
// Calculate display value (current selection number)
|
|
114
151
|
let displayValue = '';
|
|
115
152
|
const currentItem = optionData[selectedIndex];
|
|
116
153
|
if (currentItem && !currentItem.isSeparator) {
|
|
117
|
-
const match = currentItem.
|
|
154
|
+
const match = currentItem.display.match(/^([^.]+)\./);
|
|
118
155
|
if (match) {
|
|
119
156
|
displayValue = match[1];
|
|
120
157
|
}
|
|
@@ -123,13 +160,13 @@ async function showRadioMenu(config, hints) {
|
|
|
123
160
|
}
|
|
124
161
|
}
|
|
125
162
|
(0, renderer_js_1.renderInputPrompt)(displayPrompt, displayValue);
|
|
126
|
-
|
|
163
|
+
addVisualLines(` ${displayPrompt} ${displayValue}`);
|
|
127
164
|
// Render hints if provided (for Page Layout footer)
|
|
128
165
|
if (hints && hints.length > 0) {
|
|
129
166
|
(0, renderer_js_1.renderBlankLines)(1);
|
|
130
|
-
|
|
167
|
+
addVisualLines('');
|
|
131
168
|
(0, renderer_js_2.renderHints)(hints);
|
|
132
|
-
|
|
169
|
+
addVisualLines(` ${hints.join(' • ')}`);
|
|
133
170
|
}
|
|
134
171
|
state.renderedLines = lineCount;
|
|
135
172
|
};
|
|
@@ -141,9 +178,16 @@ async function showRadioMenu(config, hints) {
|
|
|
141
178
|
// Handle Ctrl+C
|
|
142
179
|
if ((0, keyboard_js_1.isCtrlC)(key)) {
|
|
143
180
|
state.stdin.removeListener('data', onData);
|
|
181
|
+
if (!preserveOnExit) {
|
|
182
|
+
(0, terminal_js_1.clearMenu)(state);
|
|
183
|
+
}
|
|
144
184
|
(0, terminal_js_1.restoreTerminal)(state);
|
|
145
|
-
|
|
146
|
-
|
|
185
|
+
if (onExit) {
|
|
186
|
+
onExit();
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
console.log(`\n${(0, registry_js_1.t)('messages.goodbye')}`);
|
|
190
|
+
}
|
|
147
191
|
process.exit(0);
|
|
148
192
|
}
|
|
149
193
|
// Handle Enter
|
|
@@ -152,24 +196,9 @@ async function showRadioMenu(config, hints) {
|
|
|
152
196
|
if (!preserveOnSelect) {
|
|
153
197
|
(0, terminal_js_1.clearMenu)(state);
|
|
154
198
|
}
|
|
155
|
-
else {
|
|
156
|
-
(0, terminal_js_1.writeLine)('');
|
|
157
|
-
}
|
|
158
199
|
(0, terminal_js_1.restoreTerminal)(state);
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
if (typeof selectedOption === 'string') {
|
|
162
|
-
value = selectedOption;
|
|
163
|
-
}
|
|
164
|
-
else if ('type' in selectedOption && selectedOption.type === 'separator') {
|
|
165
|
-
value = '';
|
|
166
|
-
}
|
|
167
|
-
else if ('value' in selectedOption) {
|
|
168
|
-
value = selectedOption.value ?? selectedOption.label ?? '';
|
|
169
|
-
}
|
|
170
|
-
else {
|
|
171
|
-
value = selectedOption.label ?? '';
|
|
172
|
-
}
|
|
200
|
+
const selectedItem = optionData[selectedIndex];
|
|
201
|
+
const value = selectedItem && !selectedItem.isSeparator ? selectedItem.value : '';
|
|
173
202
|
resolve({
|
|
174
203
|
index: selectedIndex,
|
|
175
204
|
value
|
|
@@ -203,7 +232,7 @@ async function showRadioMenu(config, hints) {
|
|
|
203
232
|
const item = optionData[idx];
|
|
204
233
|
if (item.isSeparator)
|
|
205
234
|
return false;
|
|
206
|
-
const match = item.
|
|
235
|
+
const match = item.display.match(/^([a-zA-Z])\./i);
|
|
207
236
|
return match && match[1].toLowerCase() === letter;
|
|
208
237
|
});
|
|
209
238
|
if (index !== undefined) {
|
package/dist/components.js
CHANGED
|
@@ -33,8 +33,8 @@ const colorCodes = {
|
|
|
33
33
|
};
|
|
34
34
|
// Theme colors
|
|
35
35
|
exports.theme = {
|
|
36
|
-
active: `${colorCodes.bright}${exports.colors.
|
|
37
|
-
primary: exports.colors.
|
|
36
|
+
active: `${colorCodes.bright}${exports.colors.reset}`, // Selected item - bright default
|
|
37
|
+
primary: exports.colors.reset, // Standard text color
|
|
38
38
|
title: exports.colors.reset, // Title - white (menu titles)
|
|
39
39
|
muted: exports.colors.gray, // Auxiliary - gray (descriptions, hints)
|
|
40
40
|
success: exports.colors.green, // Success - green
|
|
@@ -46,7 +46,7 @@ exports.symbols = {
|
|
|
46
46
|
success: { icon: '✓', color: exports.colors.green },
|
|
47
47
|
error: { icon: 'x', color: exports.colors.red },
|
|
48
48
|
warning: { icon: '!', color: exports.colors.yellow },
|
|
49
|
-
info: { icon: 'ℹ', color: exports.colors.
|
|
49
|
+
info: { icon: 'ℹ', color: exports.colors.reset },
|
|
50
50
|
};
|
|
51
51
|
/**
|
|
52
52
|
* Format section title with separator
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Configuration module exports
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
17
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
__exportStar(require("./user-config"), exports);
|
|
21
|
+
__exportStar(require("./language-config"), exports);
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Language Configuration System
|
|
3
|
+
* Supports dynamic language loading from JSON
|
|
4
|
+
*/
|
|
5
|
+
export interface LanguageConfig {
|
|
6
|
+
code: string;
|
|
7
|
+
cliCode?: string;
|
|
8
|
+
label: string;
|
|
9
|
+
nativeLabel: string;
|
|
10
|
+
[key: string]: any;
|
|
11
|
+
}
|
|
12
|
+
export interface LanguagesConfig {
|
|
13
|
+
default: string;
|
|
14
|
+
languages: LanguageConfig[];
|
|
15
|
+
}
|
|
16
|
+
declare class LanguageManager {
|
|
17
|
+
private config;
|
|
18
|
+
private languageMap;
|
|
19
|
+
constructor(config: LanguagesConfig);
|
|
20
|
+
/**
|
|
21
|
+
* Get all supported languages
|
|
22
|
+
*/
|
|
23
|
+
getLanguages(): LanguageConfig[];
|
|
24
|
+
/**
|
|
25
|
+
* Get language config by code
|
|
26
|
+
*/
|
|
27
|
+
getLanguage(code: string): LanguageConfig | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Get default language code
|
|
30
|
+
*/
|
|
31
|
+
getDefaultLanguage(): string;
|
|
32
|
+
/**
|
|
33
|
+
* Get CLI language code
|
|
34
|
+
*/
|
|
35
|
+
getCLICode(code: string): string;
|
|
36
|
+
/**
|
|
37
|
+
* Check if language is supported
|
|
38
|
+
*/
|
|
39
|
+
isSupported(code: string): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Get all language codes
|
|
42
|
+
*/
|
|
43
|
+
getLanguageCodes(): string[];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Initialize language manager from config
|
|
47
|
+
*/
|
|
48
|
+
export declare function initLanguages(config: LanguagesConfig): LanguageManager;
|
|
49
|
+
/**
|
|
50
|
+
* Load languages from JSON file
|
|
51
|
+
*/
|
|
52
|
+
export declare function loadLanguagesFromFile(filePath: string): LanguageManager;
|
|
53
|
+
/**
|
|
54
|
+
* Get global language manager
|
|
55
|
+
*/
|
|
56
|
+
export declare function getLanguageManager(): LanguageManager;
|
|
57
|
+
/**
|
|
58
|
+
* Get all supported languages
|
|
59
|
+
*/
|
|
60
|
+
export declare function getSupportedLanguages(): LanguageConfig[];
|
|
61
|
+
/**
|
|
62
|
+
* Get language config
|
|
63
|
+
*/
|
|
64
|
+
export declare function getLanguageConfig(code: string): LanguageConfig | undefined;
|
|
65
|
+
/**
|
|
66
|
+
* Get default language
|
|
67
|
+
*/
|
|
68
|
+
export declare function getDefaultLanguage(): string;
|
|
69
|
+
/**
|
|
70
|
+
* Get CLI language code
|
|
71
|
+
*/
|
|
72
|
+
export declare function getCLILanguageCode(code: string): string;
|
|
73
|
+
export {};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Language Configuration System
|
|
4
|
+
* Supports dynamic language loading from JSON
|
|
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.initLanguages = initLanguages;
|
|
41
|
+
exports.loadLanguagesFromFile = loadLanguagesFromFile;
|
|
42
|
+
exports.getLanguageManager = getLanguageManager;
|
|
43
|
+
exports.getSupportedLanguages = getSupportedLanguages;
|
|
44
|
+
exports.getLanguageConfig = getLanguageConfig;
|
|
45
|
+
exports.getDefaultLanguage = getDefaultLanguage;
|
|
46
|
+
exports.getCLILanguageCode = getCLILanguageCode;
|
|
47
|
+
const fs = __importStar(require("fs"));
|
|
48
|
+
class LanguageManager {
|
|
49
|
+
constructor(config) {
|
|
50
|
+
this.config = config;
|
|
51
|
+
this.languageMap = new Map();
|
|
52
|
+
// Build language map
|
|
53
|
+
for (const lang of config.languages) {
|
|
54
|
+
this.languageMap.set(lang.code, lang);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Get all supported languages
|
|
59
|
+
*/
|
|
60
|
+
getLanguages() {
|
|
61
|
+
return this.config.languages;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Get language config by code
|
|
65
|
+
*/
|
|
66
|
+
getLanguage(code) {
|
|
67
|
+
return this.languageMap.get(code);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Get default language code
|
|
71
|
+
*/
|
|
72
|
+
getDefaultLanguage() {
|
|
73
|
+
return this.config.default;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Get CLI language code
|
|
77
|
+
*/
|
|
78
|
+
getCLICode(code) {
|
|
79
|
+
const lang = this.getLanguage(code);
|
|
80
|
+
return lang?.cliCode || lang?.code || this.getDefaultLanguage();
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Check if language is supported
|
|
84
|
+
*/
|
|
85
|
+
isSupported(code) {
|
|
86
|
+
return this.languageMap.has(code);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get all language codes
|
|
90
|
+
*/
|
|
91
|
+
getLanguageCodes() {
|
|
92
|
+
return Array.from(this.languageMap.keys());
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Global language manager
|
|
96
|
+
let globalLanguageManager = null;
|
|
97
|
+
/**
|
|
98
|
+
* Initialize language manager from config
|
|
99
|
+
*/
|
|
100
|
+
function initLanguages(config) {
|
|
101
|
+
globalLanguageManager = new LanguageManager(config);
|
|
102
|
+
return globalLanguageManager;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Load languages from JSON file
|
|
106
|
+
*/
|
|
107
|
+
function loadLanguagesFromFile(filePath) {
|
|
108
|
+
try {
|
|
109
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
110
|
+
const config = JSON.parse(content);
|
|
111
|
+
return initLanguages(config);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
console.error('Failed to load languages config:', error);
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Get global language manager
|
|
120
|
+
*/
|
|
121
|
+
function getLanguageManager() {
|
|
122
|
+
if (!globalLanguageManager) {
|
|
123
|
+
// Return default manager if not initialized
|
|
124
|
+
return new LanguageManager({
|
|
125
|
+
default: 'en',
|
|
126
|
+
languages: [
|
|
127
|
+
{ code: 'en', label: 'English', nativeLabel: 'English' },
|
|
128
|
+
{ code: 'zh', label: 'Chinese', nativeLabel: '中文' }
|
|
129
|
+
]
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return globalLanguageManager;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Get all supported languages
|
|
136
|
+
*/
|
|
137
|
+
function getSupportedLanguages() {
|
|
138
|
+
return getLanguageManager().getLanguages();
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get language config
|
|
142
|
+
*/
|
|
143
|
+
function getLanguageConfig(code) {
|
|
144
|
+
return getLanguageManager().getLanguage(code);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get default language
|
|
148
|
+
*/
|
|
149
|
+
function getDefaultLanguage() {
|
|
150
|
+
return getLanguageManager().getDefaultLanguage();
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get CLI language code
|
|
154
|
+
*/
|
|
155
|
+
function getCLILanguageCode(code) {
|
|
156
|
+
return getLanguageManager().getCLICode(code);
|
|
157
|
+
}
|