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.
Files changed (45) hide show
  1. package/dist/components/display/header.d.ts +40 -0
  2. package/dist/components/display/header.js +331 -18
  3. package/dist/components/display/headers.d.ts +1 -0
  4. package/dist/components/display/headers.js +15 -5
  5. package/dist/components/display/index.d.ts +1 -1
  6. package/dist/components/display/index.js +3 -1
  7. package/dist/components/display/messages.js +5 -5
  8. package/dist/components/display/progress.d.ts +17 -0
  9. package/dist/components/display/progress.js +18 -0
  10. package/dist/components/display/summary.js +72 -10
  11. package/dist/components/display/table.d.ts +2 -0
  12. package/dist/components/display/table.js +7 -6
  13. package/dist/components/inputs/language-input.js +8 -5
  14. package/dist/components/inputs/number-input.js +19 -14
  15. package/dist/components/inputs/text-input.js +50 -13
  16. package/dist/components/menus/boolean-menu.js +33 -20
  17. package/dist/components/menus/checkbox-menu.js +5 -2
  18. package/dist/components/menus/checkbox-table-menu.js +12 -9
  19. package/dist/components/menus/radio-menu-split.d.ts +1 -0
  20. package/dist/components/menus/radio-menu-split.js +26 -16
  21. package/dist/components/menus/radio-menu.js +67 -38
  22. package/dist/components.js +3 -3
  23. package/dist/config/index.d.ts +5 -0
  24. package/dist/config/index.js +21 -0
  25. package/dist/config/language-config.d.ts +73 -0
  26. package/dist/config/language-config.js +157 -0
  27. package/dist/config/user-config.d.ts +83 -0
  28. package/dist/config/user-config.js +185 -0
  29. package/dist/core/colors.d.ts +24 -18
  30. package/dist/core/colors.js +74 -7
  31. package/dist/core/renderer.js +26 -18
  32. package/dist/core/terminal.d.ts +13 -0
  33. package/dist/core/terminal.js +87 -0
  34. package/dist/features/commands.js +23 -22
  35. package/dist/index.d.ts +3 -1
  36. package/dist/index.js +21 -2
  37. package/dist/layout.d.ts +50 -51
  38. package/dist/layout.js +69 -117
  39. package/dist/page-layout.d.ts +31 -0
  40. package/dist/page-layout.js +46 -7
  41. package/dist/types/input.types.d.ts +8 -0
  42. package/dist/types/layout.types.d.ts +56 -0
  43. package/dist/types/layout.types.js +36 -0
  44. package/dist/types/menu.types.d.ts +4 -0
  45. 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.colors.cyan}${colors_js_1.colors.bold}${paddedHeader}${colors_js_1.colors.reset}`;
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.colors.dim}${'─'.repeat(totalWidth)}${colors_js_1.colors.reset}\n`);
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.colors.cyan}${colors_js_1.colors.bold}❯ ${colors_js_1.colors.reset}`;
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.colors.green}◉${colors_js_1.colors.reset} `
100
- : `${colors_js_1.colors.dim}○${colors_js_1.colors.reset} `;
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.colors.cyan}${colors_js_1.colors.bold}\x1b[7m${paddedValue}\x1b[27m${colors_js_1.colors.reset}`;
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.colors.reset}${paddedValue}${colors_js_1.colors.reset}`;
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.colors.dim}${paddedValue}${colors_js_1.colors.reset}`;
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
- (0, terminal_js_1.clearMenu)(state);
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();
@@ -13,6 +13,7 @@ export interface RadioMenuState {
13
13
  selectedIndex: number;
14
14
  selectableIndices: number[];
15
15
  optionData: Array<{
16
+ display: string;
16
17
  value: string;
17
18
  isSeparator: boolean;
18
19
  label?: string;
@@ -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.colors.cyan);
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.value.match(/^(\d+)\.\s*/);
71
- const letterMatch = item.value.match(/^([a-zA-Z])\.\s*/);
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.value);
74
- const displayValue = isExitOption ? `${colors_js_1.uiColors.error}${item.value}${colors_js_1.colors.reset}` : item.value;
75
- (0, renderer_js_1.renderOption)(displayValue, undefined, index === selectedIndex, prefix);
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.colors.cyan);
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.value.match(/^(\d+)\.\s*/);
145
- const letterMatch = item.value.match(/^([a-zA-Z])\.\s*/);
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.value);
148
- const displayValue = isExitOption ? `${colors_js_1.uiColors.error}${item.value}${colors_js_1.colors.reset}` : item.value;
149
- (0, renderer_js_1.renderOption)(displayValue, undefined, index === selectedIndex, prefix);
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('\n👋 再见!');
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.value.charAt(0));
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.colors.cyan);
80
- lineCount++;
111
+ (0, renderer_js_1.renderHeader)(` ${title}`, colors_js_1.uiColors.primary);
112
+ addVisualLines(` ${title}`);
81
113
  (0, renderer_js_1.renderBlankLines)(1);
82
- lineCount++;
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.value.match(/^(\d+)\.\s*/);
93
- const letterMatch = item.value.match(/^([a-zA-Z])\.\s*/);
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 "Quit")
97
- const isExitOption = /\b(exit|quit)\b/i.test(item.value);
98
- const displayValue = isExitOption ? `${colors_js_1.uiColors.error}${item.value}${colors_js_1.colors.reset}` : item.value;
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, index === selectedIndex, prefix);
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
- lineCount++; // Count the blank line
143
+ addVisualLines('');
106
144
  }
107
145
  }
108
- lineCount++;
109
146
  });
110
147
  // Render input prompt
111
148
  (0, renderer_js_1.renderBlankLines)(1);
112
- lineCount++;
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.value.match(/^([^.]+)\./);
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
- lineCount++;
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
- lineCount++;
167
+ addVisualLines('');
131
168
  (0, renderer_js_2.renderHints)(hints);
132
- lineCount++;
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
- // Don't clear menu on Ctrl+C - just exit directly
146
- console.log('\n');
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 selectedOption = options[selectedIndex];
160
- let value;
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.value.match(/^([a-zA-Z])\./i);
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) {
@@ -33,8 +33,8 @@ const colorCodes = {
33
33
  };
34
34
  // Theme colors
35
35
  exports.theme = {
36
- active: `${colorCodes.bright}${exports.colors.cyan}`, // Selected item - bright cyan
37
- primary: exports.colors.cyan, // Standard - cyan (numbers, separators)
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.blue },
49
+ info: { icon: 'ℹ', color: exports.colors.reset },
50
50
  };
51
51
  /**
52
52
  * Format section title with separator
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Configuration module exports
3
+ */
4
+ export * from './user-config';
5
+ export * from './language-config';
@@ -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
+ }