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.
Files changed (78) hide show
  1. package/dist/api.d.ts +23 -5
  2. package/dist/api.js +16 -4
  3. package/dist/component-factories.d.ts +59 -0
  4. package/dist/component-factories.js +141 -0
  5. package/dist/components/display/header-v2.d.ts +13 -0
  6. package/dist/components/display/header-v2.js +43 -0
  7. package/dist/components/display/header.d.ts +40 -0
  8. package/dist/components/display/header.js +331 -18
  9. package/dist/components/display/headers.d.ts +1 -0
  10. package/dist/components/display/headers.js +15 -5
  11. package/dist/components/display/hints-v2.d.ts +10 -0
  12. package/dist/components/display/hints-v2.js +34 -0
  13. package/dist/components/display/hints.d.ts +56 -0
  14. package/dist/components/display/hints.js +81 -0
  15. package/dist/components/display/index.d.ts +4 -1
  16. package/dist/components/display/index.js +17 -1
  17. package/dist/components/display/input-prompt.d.ts +35 -0
  18. package/dist/components/display/input-prompt.js +36 -0
  19. package/dist/components/display/list.d.ts +49 -0
  20. package/dist/components/display/list.js +86 -0
  21. package/dist/components/display/messages.js +5 -5
  22. package/dist/components/display/progress.d.ts +17 -0
  23. package/dist/components/display/progress.js +18 -0
  24. package/dist/components/display/summary.js +72 -10
  25. package/dist/components/display/table.d.ts +44 -0
  26. package/dist/components/display/table.js +108 -0
  27. package/dist/components/inputs/language-input.js +8 -5
  28. package/dist/components/inputs/number-input.js +19 -14
  29. package/dist/components/inputs/text-input.js +50 -13
  30. package/dist/components/menus/boolean-menu.js +34 -20
  31. package/dist/components/menus/checkbox-menu.d.ts +2 -1
  32. package/dist/components/menus/checkbox-menu.js +35 -61
  33. package/dist/components/menus/checkbox-table-menu.d.ts +12 -0
  34. package/dist/components/menus/checkbox-table-menu.js +398 -0
  35. package/dist/components/menus/index.d.ts +1 -0
  36. package/dist/components/menus/index.js +3 -1
  37. package/dist/components/menus/radio-menu-split.d.ts +34 -0
  38. package/dist/components/menus/radio-menu-split.js +258 -0
  39. package/dist/components/menus/radio-menu-v2.d.ts +11 -0
  40. package/dist/components/menus/radio-menu-v2.js +150 -0
  41. package/dist/components/menus/radio-menu.d.ts +2 -1
  42. package/dist/components/menus/radio-menu.js +100 -134
  43. package/dist/components.js +3 -3
  44. package/dist/config/index.d.ts +5 -0
  45. package/dist/config/index.js +21 -0
  46. package/dist/config/language-config.d.ts +73 -0
  47. package/dist/config/language-config.js +157 -0
  48. package/dist/config/user-config.d.ts +83 -0
  49. package/dist/config/user-config.js +185 -0
  50. package/dist/core/colors.d.ts +24 -18
  51. package/dist/core/colors.js +74 -7
  52. package/dist/core/hint-manager.d.ts +29 -0
  53. package/dist/core/hint-manager.js +65 -0
  54. package/dist/core/renderer.d.ts +2 -1
  55. package/dist/core/renderer.js +46 -22
  56. package/dist/core/screen-manager.d.ts +54 -0
  57. package/dist/core/screen-manager.js +119 -0
  58. package/dist/core/state-manager.d.ts +27 -0
  59. package/dist/core/state-manager.js +56 -0
  60. package/dist/core/terminal.d.ts +17 -1
  61. package/dist/core/terminal.js +124 -4
  62. package/dist/core/virtual-scroll.d.ts +65 -0
  63. package/dist/core/virtual-scroll.js +120 -0
  64. package/dist/features/commands.js +23 -22
  65. package/dist/i18n/languages/en.js +4 -1
  66. package/dist/i18n/languages/zh.js +4 -1
  67. package/dist/i18n/registry.d.ts +4 -3
  68. package/dist/i18n/registry.js +12 -4
  69. package/dist/i18n/types.d.ts +3 -0
  70. package/dist/index.d.ts +7 -4
  71. package/dist/index.js +49 -4
  72. package/dist/layout.d.ts +67 -0
  73. package/dist/layout.js +86 -0
  74. package/dist/page-layout.d.ts +123 -0
  75. package/dist/page-layout.js +195 -0
  76. package/dist/types/input.types.d.ts +8 -0
  77. package/dist/types/menu.types.d.ts +61 -5
  78. package/package.json +4 -1
@@ -4,7 +4,7 @@
4
4
  * Exports all display component functions
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.renderHeader = exports.createSimpleSummary = exports.createSummaryTable = exports.renderSummaryTable = exports.createMessage = exports.showQuestion = exports.showInfo = exports.showWarning = exports.showError = exports.showSuccess = exports.renderMessage = exports.createStageSeparator = exports.createStageHeader = exports.createProgressIndicator = exports.renderStageSeparator = exports.renderStageHeader = exports.renderProgressIndicator = exports.createAsciiHeader = exports.createSectionHeader = exports.createSimpleHeader = exports.renderAsciiHeader = exports.renderSectionHeader = exports.renderSimpleHeader = void 0;
7
+ exports.createNumberedList = exports.createBulletList = exports.createList = exports.renderList = exports.createTable = exports.renderTable = exports.HintTypes = exports.generateInputHints = exports.generateMenuHints = exports.createHints = exports.renderHintsComponent = exports.renderHeader = exports.createSimpleSummary = exports.createSummaryTable = exports.renderSummaryTable = exports.createMessage = exports.showQuestion = exports.showInfo = exports.showWarning = exports.showError = exports.showSuccess = exports.renderMessage = exports.createProgressCheckmark = exports.renderProgressCheckmark = exports.createStageSeparator = exports.createStageHeader = exports.createProgressIndicator = exports.renderStageSeparator = exports.renderStageHeader = exports.renderProgressIndicator = exports.createAsciiHeader = exports.createSectionHeader = exports.createSimpleHeader = exports.renderAsciiHeader = exports.renderSectionHeader = exports.renderSimpleHeader = void 0;
8
8
  var headers_js_1 = require("./headers.js");
9
9
  Object.defineProperty(exports, "renderSimpleHeader", { enumerable: true, get: function () { return headers_js_1.renderSimpleHeader; } });
10
10
  Object.defineProperty(exports, "renderSectionHeader", { enumerable: true, get: function () { return headers_js_1.renderSectionHeader; } });
@@ -19,6 +19,8 @@ Object.defineProperty(exports, "renderStageSeparator", { enumerable: true, get:
19
19
  Object.defineProperty(exports, "createProgressIndicator", { enumerable: true, get: function () { return progress_js_1.createProgressIndicator; } });
20
20
  Object.defineProperty(exports, "createStageHeader", { enumerable: true, get: function () { return progress_js_1.createStageHeader; } });
21
21
  Object.defineProperty(exports, "createStageSeparator", { enumerable: true, get: function () { return progress_js_1.createStageSeparator; } });
22
+ Object.defineProperty(exports, "renderProgressCheckmark", { enumerable: true, get: function () { return progress_js_1.renderProgressCheckmark; } });
23
+ Object.defineProperty(exports, "createProgressCheckmark", { enumerable: true, get: function () { return progress_js_1.createProgressCheckmark; } });
22
24
  var messages_js_1 = require("./messages.js");
23
25
  Object.defineProperty(exports, "renderMessage", { enumerable: true, get: function () { return messages_js_1.renderMessage; } });
24
26
  Object.defineProperty(exports, "showSuccess", { enumerable: true, get: function () { return messages_js_1.showSuccess; } });
@@ -33,3 +35,17 @@ Object.defineProperty(exports, "createSummaryTable", { enumerable: true, get: fu
33
35
  Object.defineProperty(exports, "createSimpleSummary", { enumerable: true, get: function () { return summary_js_1.createSimpleSummary; } });
34
36
  var header_js_1 = require("./header.js");
35
37
  Object.defineProperty(exports, "renderHeader", { enumerable: true, get: function () { return header_js_1.renderHeader; } });
38
+ var hints_js_1 = require("./hints.js");
39
+ Object.defineProperty(exports, "renderHintsComponent", { enumerable: true, get: function () { return hints_js_1.renderHintsComponent; } });
40
+ Object.defineProperty(exports, "createHints", { enumerable: true, get: function () { return hints_js_1.createHints; } });
41
+ Object.defineProperty(exports, "generateMenuHints", { enumerable: true, get: function () { return hints_js_1.generateMenuHints; } });
42
+ Object.defineProperty(exports, "generateInputHints", { enumerable: true, get: function () { return hints_js_1.generateInputHints; } });
43
+ Object.defineProperty(exports, "HintTypes", { enumerable: true, get: function () { return hints_js_1.HintTypes; } });
44
+ var table_js_1 = require("./table.js");
45
+ Object.defineProperty(exports, "renderTable", { enumerable: true, get: function () { return table_js_1.renderTable; } });
46
+ Object.defineProperty(exports, "createTable", { enumerable: true, get: function () { return table_js_1.createTable; } });
47
+ var list_js_1 = require("./list.js");
48
+ Object.defineProperty(exports, "renderList", { enumerable: true, get: function () { return list_js_1.renderList; } });
49
+ Object.defineProperty(exports, "createList", { enumerable: true, get: function () { return list_js_1.createList; } });
50
+ Object.defineProperty(exports, "createBulletList", { enumerable: true, get: function () { return list_js_1.createBulletList; } });
51
+ Object.defineProperty(exports, "createNumberedList", { enumerable: true, get: function () { return list_js_1.createNumberedList; } });
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Input Prompt Component
3
+ * Factory function to create input prompt configuration
4
+ */
5
+ export interface InputPromptConfig {
6
+ /** Prompt text to display */
7
+ prompt: string;
8
+ /** Default value */
9
+ defaultValue?: string;
10
+ /** Allow empty input */
11
+ allowEmpty?: boolean;
12
+ }
13
+ /**
14
+ * Create an input prompt component configuration
15
+ * Returns a configuration object that can be used in footer.input
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * const inputConfig = createInputPromptComponent({
20
+ * prompt: '请输入名称',
21
+ * defaultValue: '',
22
+ * allowEmpty: false
23
+ * });
24
+ *
25
+ * await renderPage({
26
+ * header: {...},
27
+ * mainArea: {...},
28
+ * footer: {
29
+ * input: inputConfig,
30
+ * hints: ['Enter 确认', 'Esc 取消']
31
+ * }
32
+ * });
33
+ * ```
34
+ */
35
+ export declare function createInputPromptComponent(config: InputPromptConfig): InputPromptConfig;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ /**
3
+ * Input Prompt Component
4
+ * Factory function to create input prompt configuration
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.createInputPromptComponent = createInputPromptComponent;
8
+ /**
9
+ * Create an input prompt component configuration
10
+ * Returns a configuration object that can be used in footer.input
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const inputConfig = createInputPromptComponent({
15
+ * prompt: '请输入名称',
16
+ * defaultValue: '',
17
+ * allowEmpty: false
18
+ * });
19
+ *
20
+ * await renderPage({
21
+ * header: {...},
22
+ * mainArea: {...},
23
+ * footer: {
24
+ * input: inputConfig,
25
+ * hints: ['Enter 确认', 'Esc 取消']
26
+ * }
27
+ * });
28
+ * ```
29
+ */
30
+ function createInputPromptComponent(config) {
31
+ return {
32
+ prompt: config.prompt,
33
+ defaultValue: config.defaultValue,
34
+ allowEmpty: config.allowEmpty ?? false
35
+ };
36
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * List Component
3
+ * Displays items in a list format with optional bullets/numbers
4
+ */
5
+ /**
6
+ * List item configuration
7
+ */
8
+ export interface ListItem {
9
+ /** Item text */
10
+ text: string;
11
+ /** Item indent level (0 = no indent) */
12
+ indent?: number;
13
+ /** Custom bullet/prefix */
14
+ bullet?: string;
15
+ }
16
+ /**
17
+ * List configuration
18
+ */
19
+ export interface ListConfig {
20
+ /** List items */
21
+ items: (string | ListItem)[];
22
+ /** List style */
23
+ style?: 'bullet' | 'number' | 'none';
24
+ /** Custom bullet character (for bullet style) */
25
+ bulletChar?: string;
26
+ /** Indent size in spaces */
27
+ indentSize?: number;
28
+ }
29
+ /**
30
+ * Render list component
31
+ * @param config - List configuration
32
+ */
33
+ export declare function renderList(config: ListConfig): void;
34
+ /**
35
+ * Create list configuration (factory function)
36
+ */
37
+ export declare function createList(items: (string | ListItem)[], options?: {
38
+ style?: 'bullet' | 'number' | 'none';
39
+ bulletChar?: string;
40
+ indentSize?: number;
41
+ }): ListConfig;
42
+ /**
43
+ * Create a simple bullet list
44
+ */
45
+ export declare function createBulletList(items: string[]): ListConfig;
46
+ /**
47
+ * Create a numbered list
48
+ */
49
+ export declare function createNumberedList(items: string[]): ListConfig;
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ /**
3
+ * List Component
4
+ * Displays items in a list format with optional bullets/numbers
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.renderList = renderList;
8
+ exports.createList = createList;
9
+ exports.createBulletList = createBulletList;
10
+ exports.createNumberedList = createNumberedList;
11
+ const terminal_js_1 = require("../../core/terminal.js");
12
+ /**
13
+ * Render list component
14
+ * @param config - List configuration
15
+ */
16
+ function renderList(config) {
17
+ const { items, style = 'bullet', bulletChar = '•', indentSize = 2 } = config;
18
+ if (items.length === 0) {
19
+ return;
20
+ }
21
+ items.forEach((item, index) => {
22
+ let text;
23
+ let indent;
24
+ let bullet;
25
+ // Parse item
26
+ if (typeof item === 'string') {
27
+ text = item;
28
+ indent = 0;
29
+ bullet = undefined;
30
+ }
31
+ else {
32
+ text = item.text;
33
+ indent = item.indent || 0;
34
+ bullet = item.bullet;
35
+ }
36
+ // Generate prefix based on style
37
+ let prefix;
38
+ if (bullet) {
39
+ // Custom bullet
40
+ prefix = bullet;
41
+ }
42
+ else {
43
+ switch (style) {
44
+ case 'number':
45
+ prefix = `${index + 1}.`;
46
+ break;
47
+ case 'bullet':
48
+ prefix = bulletChar;
49
+ break;
50
+ case 'none':
51
+ default:
52
+ prefix = '';
53
+ break;
54
+ }
55
+ }
56
+ // Build line with indent and prefix
57
+ const indentStr = ' '.repeat(indent * indentSize);
58
+ const line = prefix
59
+ ? `${indentStr}${prefix} ${text}`
60
+ : `${indentStr}${text}`;
61
+ (0, terminal_js_1.writeLine)(line);
62
+ });
63
+ }
64
+ /**
65
+ * Create list configuration (factory function)
66
+ */
67
+ function createList(items, options) {
68
+ return {
69
+ items,
70
+ style: options?.style ?? 'bullet',
71
+ bulletChar: options?.bulletChar ?? '•',
72
+ indentSize: options?.indentSize ?? 2
73
+ };
74
+ }
75
+ /**
76
+ * Create a simple bullet list
77
+ */
78
+ function createBulletList(items) {
79
+ return createList(items, { style: 'bullet' });
80
+ }
81
+ /**
82
+ * Create a numbered list
83
+ */
84
+ function createNumberedList(items) {
85
+ return createList(items, { style: 'number' });
86
+ }
@@ -24,23 +24,23 @@ function renderMessage(config) {
24
24
  switch (type) {
25
25
  case 'success':
26
26
  icon = '✓';
27
- color = colors_js_1.colors.green;
27
+ color = colors_js_1.uiColors.success;
28
28
  break;
29
29
  case 'error':
30
30
  icon = '✗';
31
- color = colors_js_1.colors.red;
31
+ color = colors_js_1.uiColors.error;
32
32
  break;
33
33
  case 'warning':
34
34
  icon = '⚠';
35
- color = colors_js_1.colors.yellow;
35
+ color = colors_js_1.uiColors.warning;
36
36
  break;
37
37
  case 'info':
38
38
  icon = 'ℹ';
39
- color = colors_js_1.colors.blue;
39
+ color = colors_js_1.uiColors.info;
40
40
  break;
41
41
  case 'question':
42
42
  icon = '?';
43
- color = colors_js_1.colors.yellow;
43
+ color = colors_js_1.uiColors.warning;
44
44
  break;
45
45
  }
46
46
  (0, terminal_js_1.writeLine)(`${color}${icon}${colors_js_1.colors.reset} ${message}`);
@@ -39,3 +39,20 @@ export declare function createStageHeader(stageName: string, stepNumber: number)
39
39
  * @param width - Optional width
40
40
  */
41
41
  export declare function createStageSeparator(char?: string, width?: number): void;
42
+ /**
43
+ * Single-line completed progress marker.
44
+ * Use this for execution flows that print one completed step at a time.
45
+ */
46
+ export declare function renderProgressCheckmark(step: string, options?: {
47
+ icon?: string;
48
+ indent?: string;
49
+ color?: string;
50
+ }): void;
51
+ /**
52
+ * Alias factory for renderProgressCheckmark
53
+ */
54
+ export declare function createProgressCheckmark(step: string, options?: {
55
+ icon?: string;
56
+ indent?: string;
57
+ color?: string;
58
+ }): void;
@@ -10,6 +10,8 @@ exports.renderStageSeparator = renderStageSeparator;
10
10
  exports.createProgressIndicator = createProgressIndicator;
11
11
  exports.createStageHeader = createStageHeader;
12
12
  exports.createStageSeparator = createStageSeparator;
13
+ exports.renderProgressCheckmark = renderProgressCheckmark;
14
+ exports.createProgressCheckmark = createProgressCheckmark;
13
15
  const terminal_js_1 = require("../../core/terminal.js");
14
16
  const colors_js_1 = require("../../core/colors.js");
15
17
  const terminal_js_2 = require("../../core/terminal.js");
@@ -80,3 +82,19 @@ function createStageHeader(stageName, stepNumber) {
80
82
  function createStageSeparator(char, width) {
81
83
  renderStageSeparator(char, width);
82
84
  }
85
+ /**
86
+ * Single-line completed progress marker.
87
+ * Use this for execution flows that print one completed step at a time.
88
+ */
89
+ function renderProgressCheckmark(step, options) {
90
+ const icon = options?.icon ?? '✓';
91
+ const indent = options?.indent ?? ' ';
92
+ const color = options?.color ?? colors_js_1.uiColors.primary;
93
+ (0, terminal_js_1.writeLine)(`${indent}${color}${icon}${colors_js_1.colors.reset} ${step}`);
94
+ }
95
+ /**
96
+ * Alias factory for renderProgressCheckmark
97
+ */
98
+ function createProgressCheckmark(step, options) {
99
+ renderProgressCheckmark(step, options);
100
+ }
@@ -10,6 +10,58 @@ exports.createSimpleSummary = createSimpleSummary;
10
10
  const terminal_js_1 = require("../../core/terminal.js");
11
11
  const colors_js_1 = require("../../core/colors.js");
12
12
  const terminal_js_2 = require("../../core/terminal.js");
13
+ function resolveColorSpec(spec) {
14
+ if (!spec) {
15
+ return '';
16
+ }
17
+ return spec
18
+ .split('+')
19
+ .map(part => {
20
+ const token = part.trim();
21
+ if (!token) {
22
+ return '';
23
+ }
24
+ const named = colors_js_1.colors[token];
25
+ if (named) {
26
+ return named;
27
+ }
28
+ // Accept raw ANSI sequences (e.g. values from getUIColors()).
29
+ if (/^\x1b\[[0-9;]*m$/.test(token)) {
30
+ return token;
31
+ }
32
+ return '';
33
+ })
34
+ .join('');
35
+ }
36
+ function splitLongToken(token, maxWidth) {
37
+ if (token.length <= maxWidth) {
38
+ return [token];
39
+ }
40
+ const chunks = [];
41
+ let rest = token;
42
+ const breakChars = ['/', '\\', '-', '_', '.'];
43
+ while (rest.length > maxWidth) {
44
+ let breakPos = -1;
45
+ for (const ch of breakChars) {
46
+ const idx = rest.lastIndexOf(ch, maxWidth - 1);
47
+ if (idx > breakPos) {
48
+ breakPos = idx;
49
+ }
50
+ }
51
+ if (breakPos <= 0) {
52
+ breakPos = maxWidth;
53
+ }
54
+ else {
55
+ breakPos += 1;
56
+ }
57
+ chunks.push(rest.slice(0, breakPos));
58
+ rest = rest.slice(breakPos);
59
+ }
60
+ if (rest.length > 0) {
61
+ chunks.push(rest);
62
+ }
63
+ return chunks;
64
+ }
13
65
  /**
14
66
  * Wrap text to fit within a specific width, preserving ANSI color codes
15
67
  */
@@ -17,10 +69,15 @@ function wrapTextWithColors(text, maxWidth) {
17
69
  // Extract ANSI codes and plain text
18
70
  const ansiRegex = /\x1b\[[0-9;]*m/g;
19
71
  const plainText = text.replace(ansiRegex, '');
72
+ const hasAnsi = /\x1b\[[0-9;]*m/.test(text);
20
73
  // If plain text fits, return as-is
21
74
  if (plainText.length <= maxWidth) {
22
75
  return [text];
23
76
  }
77
+ // Fast path for plain text (no ANSI): support long token wrapping (e.g. file paths).
78
+ if (!hasAnsi) {
79
+ return wrapText(text, maxWidth);
80
+ }
24
81
  // Find all ANSI codes and their positions in the original text
25
82
  const codes = [];
26
83
  let match;
@@ -94,6 +151,16 @@ function wrapText(text, maxWidth) {
94
151
  const lines = [];
95
152
  let currentLine = '';
96
153
  for (const word of words) {
154
+ if (word.length > maxWidth) {
155
+ if (currentLine) {
156
+ lines.push(currentLine);
157
+ currentLine = '';
158
+ }
159
+ const chunks = splitLongToken(word, maxWidth);
160
+ lines.push(...chunks.slice(0, -1));
161
+ currentLine = chunks[chunks.length - 1] || '';
162
+ continue;
163
+ }
97
164
  const testLine = currentLine ? `${currentLine} ${word}` : word;
98
165
  if (testLine.length <= maxWidth) {
99
166
  currentLine = testLine;
@@ -136,12 +203,7 @@ function renderSummaryTable(config) {
136
203
  (0, terminal_js_1.writeLine)(`│${' '.repeat(boxWidth - 2)}│`);
137
204
  // Title if provided
138
205
  if (title) {
139
- // Parse color configuration (supports "color" or "color+style" format)
140
- let titleColor = '';
141
- if (finalColors.title) {
142
- const parts = finalColors.title.split('+');
143
- titleColor = parts.map(part => colors_js_1.colors[part.trim()] || '').join('');
144
- }
206
+ const titleColor = resolveColorSpec(finalColors.title);
145
207
  const resetColor = titleColor ? colors_js_1.colors.reset : '';
146
208
  let titleLine;
147
209
  let remainingSpace;
@@ -173,7 +235,7 @@ function renderSummaryTable(config) {
173
235
  sections.forEach((section, sectionIndex) => {
174
236
  // Section header if provided
175
237
  if (section.header) {
176
- const headerColor = finalColors.sectionHeader ? colors_js_1.colors[finalColors.sectionHeader] || '' : '';
238
+ const headerColor = resolveColorSpec(finalColors.sectionHeader);
177
239
  const resetColor = headerColor ? colors_js_1.colors.reset : '';
178
240
  const headerLine = ` ${headerColor}${section.header}${resetColor}`;
179
241
  const remainingSpace = boxWidth - section.header.length - 4;
@@ -188,7 +250,7 @@ function renderSummaryTable(config) {
188
250
  // Wrap the value (preserving colors if present)
189
251
  const wrappedLines = wrapTextWithColors(item.value, valueMaxWidth);
190
252
  // First line with key
191
- const keyColor = finalColors.key ? colors_js_1.colors[finalColors.key] || '' : '';
253
+ const keyColor = resolveColorSpec(finalColors.key);
192
254
  const keyResetColor = keyColor ? colors_js_1.colors.reset : '';
193
255
  // wrappedLines already contain colors, don't add valueColor
194
256
  const firstLine = ` ${keyColor}${item.key}:${keyResetColor}${' '.repeat(Math.max(1, keyPadding - item.key.length))}${wrappedLines[0]}`;
@@ -205,9 +267,9 @@ function renderSummaryTable(config) {
205
267
  }
206
268
  else {
207
269
  // No wrapping needed
208
- const keyColor = finalColors.key ? colors_js_1.colors[finalColors.key] || '' : '';
270
+ const keyColor = resolveColorSpec(finalColors.key);
209
271
  const keyResetColor = keyColor ? colors_js_1.colors.reset : '';
210
- const valueColor = finalColors.value ? colors_js_1.colors[finalColors.value] || '' : '';
272
+ const valueColor = resolveColorSpec(finalColors.value);
211
273
  const valueResetColor = valueColor ? colors_js_1.colors.reset : '';
212
274
  // Only wrap value with color if valueColor is set, otherwise preserve original colors in item.value
213
275
  const coloredValue = valueColor ? `${valueColor}${item.value}${valueResetColor}` : item.value;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Table Component
3
+ * Displays data in a table format with headers and rows
4
+ */
5
+ /**
6
+ * Table column configuration
7
+ */
8
+ export interface TableColumn {
9
+ /** Column header */
10
+ header: string;
11
+ /** Column key in data object */
12
+ key: string;
13
+ /** Column width (optional, auto-calculated if not provided) */
14
+ width?: number;
15
+ /** Text alignment */
16
+ align?: 'left' | 'center' | 'right';
17
+ }
18
+ /**
19
+ * Table configuration
20
+ */
21
+ export interface TableConfig {
22
+ /** Table columns */
23
+ columns: TableColumn[];
24
+ /** Table data rows */
25
+ data: Record<string, any>[];
26
+ /** Show table borders */
27
+ showBorders?: boolean;
28
+ /** Show header separator */
29
+ showHeaderSeparator?: boolean;
30
+ /** Border color (ANSI escape code, e.g., '\x1b[2m' for dim/gray) */
31
+ borderColor?: string;
32
+ }
33
+ /**
34
+ * Render table component
35
+ * @param config - Table configuration
36
+ */
37
+ export declare function renderTable(config: TableConfig): void;
38
+ /**
39
+ * Create table configuration (factory function)
40
+ */
41
+ export declare function createTable(columns: TableColumn[], data: Record<string, any>[], options?: {
42
+ showBorders?: boolean;
43
+ showHeaderSeparator?: boolean;
44
+ }): TableConfig;
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ /**
3
+ * Table Component
4
+ * Displays data in a table format with headers and rows
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.renderTable = renderTable;
8
+ exports.createTable = createTable;
9
+ const terminal_js_1 = require("../../core/terminal.js");
10
+ /**
11
+ * Render table component
12
+ * @param config - Table configuration
13
+ */
14
+ function renderTable(config) {
15
+ const { columns, data, showBorders = true, showHeaderSeparator = true, borderColor = '' } = config;
16
+ if (columns.length === 0 || data.length === 0) {
17
+ return;
18
+ }
19
+ const resetColor = borderColor ? '\x1b[0m' : '';
20
+ // Calculate column widths
21
+ const columnWidths = columns.map((col, index) => {
22
+ if (col.width)
23
+ return col.width;
24
+ // Auto-calculate width based on header and data
25
+ const headerWidth = col.header.length;
26
+ const dataWidth = Math.max(...data.map(row => String(row[col.key] || '').length));
27
+ return Math.max(headerWidth, dataWidth) + 2; // +2 for padding
28
+ });
29
+ // Render top border
30
+ if (showBorders) {
31
+ const border = columnWidths.map(w => '─'.repeat(w)).join('┬');
32
+ (0, terminal_js_1.writeLine)(`${borderColor}┌${border}┐${resetColor}`);
33
+ }
34
+ // Render header
35
+ const headerCells = columns.map((col, index) => {
36
+ const width = columnWidths[index];
37
+ const align = col.align || 'left';
38
+ return alignText(col.header, width, align);
39
+ });
40
+ if (showBorders) {
41
+ (0, terminal_js_1.writeLine)(`${borderColor}│${resetColor}${headerCells.join(`${borderColor}│${resetColor}`)}${borderColor}│${resetColor}`);
42
+ }
43
+ else {
44
+ (0, terminal_js_1.writeLine)(headerCells.join(' '));
45
+ }
46
+ // Render header separator
47
+ if (showHeaderSeparator) {
48
+ if (showBorders) {
49
+ const separator = columnWidths.map(w => '─'.repeat(w)).join('┼');
50
+ (0, terminal_js_1.writeLine)(`${borderColor}├${separator}┤${resetColor}`);
51
+ }
52
+ else {
53
+ const separator = columnWidths.map(w => '─'.repeat(w)).join(' ');
54
+ (0, terminal_js_1.writeLine)(separator);
55
+ }
56
+ }
57
+ // Render data rows
58
+ data.forEach((row, rowIndex) => {
59
+ const cells = columns.map((col, colIndex) => {
60
+ const width = columnWidths[colIndex];
61
+ const align = col.align || 'left';
62
+ const value = String(row[col.key] || '');
63
+ return alignText(value, width, align);
64
+ });
65
+ if (showBorders) {
66
+ (0, terminal_js_1.writeLine)(`${borderColor}│${resetColor}${cells.join(`${borderColor}│${resetColor}`)}${borderColor}│${resetColor}`);
67
+ }
68
+ else {
69
+ (0, terminal_js_1.writeLine)(cells.join(' '));
70
+ }
71
+ });
72
+ // Render bottom border
73
+ if (showBorders) {
74
+ const border = columnWidths.map(w => '─'.repeat(w)).join('┴');
75
+ (0, terminal_js_1.writeLine)(`${borderColor}└${border}┘${resetColor}`);
76
+ }
77
+ }
78
+ /**
79
+ * Align text within a given width
80
+ */
81
+ function alignText(text, width, align) {
82
+ const padding = width - text.length;
83
+ if (padding <= 0) {
84
+ return text.substring(0, width);
85
+ }
86
+ switch (align) {
87
+ case 'center':
88
+ const leftPad = Math.floor(padding / 2);
89
+ const rightPad = padding - leftPad;
90
+ return ' '.repeat(leftPad) + text + ' '.repeat(rightPad);
91
+ case 'right':
92
+ return ' '.repeat(padding) + text;
93
+ case 'left':
94
+ default:
95
+ return text + ' '.repeat(padding);
96
+ }
97
+ }
98
+ /**
99
+ * Create table configuration (factory function)
100
+ */
101
+ function createTable(columns, data, options) {
102
+ return {
103
+ columns,
104
+ data,
105
+ showBorders: options?.showBorders ?? true,
106
+ showHeaderSeparator: options?.showHeaderSeparator ?? true
107
+ };
108
+ }
@@ -9,13 +9,14 @@ const terminal_js_1 = require("../../core/terminal.js");
9
9
  const keyboard_js_1 = require("../../core/keyboard.js");
10
10
  const renderer_js_1 = require("../../core/renderer.js");
11
11
  const colors_js_1 = require("../../core/colors.js");
12
+ const registry_js_1 = require("../../i18n/registry.js");
12
13
  /**
13
14
  * Show a language selector
14
15
  * @param config - Selector configuration
15
16
  * @returns Promise resolving to selected language code
16
17
  */
17
18
  async function showLanguageSelector(config) {
18
- const { languages, defaultLanguage, prompt = '选择语言 / Select Language', onExit } = config;
19
+ const { languages, defaultLanguage, prompt = 'Select Language', onExit, preserveOnExit = false } = config;
19
20
  // Validate languages
20
21
  if (!languages || languages.length === 0) {
21
22
  throw new Error('LanguageSelector requires at least one language');
@@ -33,7 +34,7 @@ async function showLanguageSelector(config) {
33
34
  (0, terminal_js_1.clearMenu)(state);
34
35
  let lineCount = 0;
35
36
  // Render header
36
- (0, renderer_js_1.renderHeader)(` ${prompt}`, colors_js_1.colors.cyan);
37
+ (0, renderer_js_1.renderHeader)(` ${prompt}`, colors_js_1.uiColors.primary);
37
38
  lineCount++;
38
39
  (0, renderer_js_1.renderBlankLines)(1);
39
40
  lineCount++;
@@ -48,7 +49,7 @@ async function showLanguageSelector(config) {
48
49
  // Render hints
49
50
  (0, renderer_js_1.renderBlankLines)(1);
50
51
  lineCount++;
51
- (0, renderer_js_1.renderHints)(['↑↓ 方向键', '1-9 输入序号', '⏎ 确认']);
52
+ (0, renderer_js_1.renderHints)([(0, registry_js_1.t)('hints.arrows'), (0, registry_js_1.t)('hints.numbers'), (0, registry_js_1.t)('hints.enter')]);
52
53
  lineCount++;
53
54
  state.renderedLines = lineCount;
54
55
  };
@@ -60,13 +61,15 @@ async function showLanguageSelector(config) {
60
61
  // Handle Ctrl+C
61
62
  if ((0, keyboard_js_1.isCtrlC)(key)) {
62
63
  state.stdin.removeListener('data', onData);
63
- (0, terminal_js_1.clearMenu)(state);
64
+ if (!preserveOnExit) {
65
+ (0, terminal_js_1.clearMenu)(state);
66
+ }
64
67
  (0, terminal_js_1.restoreTerminal)(state);
65
68
  if (onExit) {
66
69
  onExit();
67
70
  }
68
71
  else {
69
- console.log('\n👋 再见!');
72
+ console.log(`\n${(0, registry_js_1.t)('messages.goodbye')}`);
70
73
  }
71
74
  process.exit(0);
72
75
  }