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
@@ -4,6 +4,9 @@
4
4
  * Handles cursor movement, screen clearing, and terminal state
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.stripAnsi = stripAnsi;
8
+ exports.getDisplayWidth = getDisplayWidth;
9
+ exports.countVisualLines = countVisualLines;
7
10
  exports.initTerminal = initTerminal;
8
11
  exports.restoreTerminal = restoreTerminal;
9
12
  exports.clearMenu = clearMenu;
@@ -20,6 +23,90 @@ exports.getTerminalWidth = getTerminalWidth;
20
23
  exports.getTerminalHeight = getTerminalHeight;
21
24
  exports.clearScreen = clearScreen;
22
25
  exports.exitWithGoodbye = exitWithGoodbye;
26
+ const ANSI_ESCAPE_PATTERN = /[\u001B\u009B][[\]()#;?]*(?:(?:[a-zA-Z\d]*(?:;[a-zA-Z\d]*)*)?\u0007|(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~])/g;
27
+ const TAB_WIDTH = 4;
28
+ /**
29
+ * Remove ANSI escape sequences from a string.
30
+ * This is required when calculating the visible width in terminal cells.
31
+ */
32
+ function stripAnsi(text) {
33
+ return text.replace(ANSI_ESCAPE_PATTERN, '');
34
+ }
35
+ function isCombiningCodePoint(codePoint) {
36
+ return ((codePoint >= 0x0300 && codePoint <= 0x036F) || // Combining Diacritical Marks
37
+ (codePoint >= 0x1AB0 && codePoint <= 0x1AFF) || // Combining Diacritical Marks Extended
38
+ (codePoint >= 0x1DC0 && codePoint <= 0x1DFF) || // Combining Diacritical Marks Supplement
39
+ (codePoint >= 0x20D0 && codePoint <= 0x20FF) || // Combining Diacritical Marks for Symbols
40
+ (codePoint >= 0xFE20 && codePoint <= 0xFE2F) // Combining Half Marks
41
+ );
42
+ }
43
+ function isFullWidthCodePoint(codePoint) {
44
+ if (codePoint < 0x1100) {
45
+ return false;
46
+ }
47
+ return (codePoint <= 0x115F ||
48
+ codePoint === 0x2329 ||
49
+ codePoint === 0x232A ||
50
+ ((codePoint >= 0x2E80 && codePoint <= 0x3247) && codePoint !== 0x303F) ||
51
+ (codePoint >= 0x3250 && codePoint <= 0x4DBF) ||
52
+ (codePoint >= 0x4E00 && codePoint <= 0xA4C6) ||
53
+ (codePoint >= 0xA960 && codePoint <= 0xA97C) ||
54
+ (codePoint >= 0xAC00 && codePoint <= 0xD7A3) ||
55
+ (codePoint >= 0xF900 && codePoint <= 0xFAFF) ||
56
+ (codePoint >= 0xFE10 && codePoint <= 0xFE19) ||
57
+ (codePoint >= 0xFE30 && codePoint <= 0xFE6B) ||
58
+ (codePoint >= 0xFF01 && codePoint <= 0xFF60) ||
59
+ (codePoint >= 0xFFE0 && codePoint <= 0xFFE6) ||
60
+ (codePoint >= 0x1B000 && codePoint <= 0x1B001) ||
61
+ (codePoint >= 0x1F200 && codePoint <= 0x1F251) ||
62
+ (codePoint >= 0x20000 && codePoint <= 0x3FFFD));
63
+ }
64
+ function getCharacterWidth(char, currentLineWidth) {
65
+ if (char === '\t') {
66
+ const remainder = currentLineWidth % TAB_WIDTH;
67
+ return remainder === 0 ? TAB_WIDTH : TAB_WIDTH - remainder;
68
+ }
69
+ const codePoint = char.codePointAt(0);
70
+ if (codePoint === undefined) {
71
+ return 0;
72
+ }
73
+ // Control characters and zero-width joiners/modifiers.
74
+ if (codePoint <= 0x001F ||
75
+ (codePoint >= 0x007F && codePoint <= 0x009F) ||
76
+ codePoint === 0x200D ||
77
+ (codePoint >= 0xFE00 && codePoint <= 0xFE0F) ||
78
+ isCombiningCodePoint(codePoint)) {
79
+ return 0;
80
+ }
81
+ if (isFullWidthCodePoint(codePoint) || (codePoint >= 0x1F300 && codePoint <= 0x1FAFF)) {
82
+ return 2;
83
+ }
84
+ return 1;
85
+ }
86
+ /**
87
+ * Calculate the visible width of a string in terminal cells.
88
+ */
89
+ function getDisplayWidth(text) {
90
+ const plain = stripAnsi(text);
91
+ let width = 0;
92
+ for (const char of plain) {
93
+ width += getCharacterWidth(char, width);
94
+ }
95
+ return width;
96
+ }
97
+ /**
98
+ * Count how many visual rows the text occupies after terminal wrapping.
99
+ */
100
+ function countVisualLines(text, terminalWidth = getTerminalWidth()) {
101
+ const width = Math.max(1, terminalWidth);
102
+ const lines = text.split(/\r\n|\r|\n/);
103
+ let lineCount = 0;
104
+ for (const line of lines) {
105
+ const visualWidth = getDisplayWidth(line);
106
+ lineCount += Math.max(1, Math.ceil(visualWidth / width));
107
+ }
108
+ return lineCount;
109
+ }
23
110
  /**
24
111
  * Initialize terminal for interactive mode
25
112
  * @param useAltScreen - Whether to use alternate screen buffer (prevents scroll issues)
@@ -14,40 +14,41 @@ exports.getAvailableCommands = getAvailableCommands;
14
14
  exports.showCommandHelp = showCommandHelp;
15
15
  const terminal_js_1 = require("../core/terminal.js");
16
16
  const colors_js_1 = require("../core/colors.js");
17
+ const registry_js_1 = require("../i18n/registry.js");
17
18
  /**
18
19
  * Default command registry
19
20
  */
20
21
  const defaultCommands = {
21
22
  quit: {
22
23
  handler: () => {
23
- (0, terminal_js_1.writeLine)('\n👋 再见!');
24
+ (0, terminal_js_1.writeLine)(`\n${(0, registry_js_1.t)('messages.goodbye')}`);
24
25
  process.exit(0);
25
26
  },
26
- description: '退出应用程序'
27
+ descriptionKey: 'commands.quit'
27
28
  },
28
29
  help: {
29
30
  handler: () => {
30
- (0, terminal_js_1.writeLine)('\n可用命令:');
31
- Object.entries(defaultCommands).forEach(([cmd, { description }]) => {
32
- (0, terminal_js_1.writeLine)(` ${colors_js_1.colors.cyan}/${cmd}${colors_js_1.colors.reset} - ${description}`);
31
+ (0, terminal_js_1.writeLine)(`\n${(0, registry_js_1.t)('commands.availableCommands')}:`);
32
+ Object.entries(defaultCommands).forEach(([cmd, { descriptionKey }]) => {
33
+ (0, terminal_js_1.writeLine)(` ${colors_js_1.uiColors.primary}/${cmd}${colors_js_1.colors.reset} - ${(0, registry_js_1.t)(descriptionKey)}`);
33
34
  });
34
35
  (0, terminal_js_1.writeLine)('');
35
36
  return false; // Don't exit, continue
36
37
  },
37
- description: '显示帮助信息'
38
+ descriptionKey: 'commands.help'
38
39
  },
39
40
  clear: {
40
41
  handler: () => {
41
42
  (0, terminal_js_1.clearScreen)();
42
43
  return false; // Don't exit, continue
43
44
  },
44
- description: '清除屏幕'
45
+ descriptionKey: 'commands.clear'
45
46
  },
46
47
  back: {
47
48
  handler: () => {
48
49
  return true; // Signal to go back
49
50
  },
50
- description: '返回上一级菜单'
51
+ descriptionKey: 'commands.back'
51
52
  }
52
53
  };
53
54
  /**
@@ -61,7 +62,7 @@ let customCommands = {};
61
62
  * @param description - Command description
62
63
  */
63
64
  function registerCommand(command, handler, description) {
64
- customCommands[command.toLowerCase()] = { handler, description };
65
+ customCommands[command.toLowerCase()] = { handler, descriptionKey: description };
65
66
  }
66
67
  /**
67
68
  * Unregister a custom command
@@ -120,8 +121,8 @@ function handleCommand(input) {
120
121
  return result === undefined ? false : result;
121
122
  }
122
123
  // Unknown command
123
- (0, terminal_js_1.writeLine)(`${colors_js_1.colors.red}✗ 未知命令: /${command}${colors_js_1.colors.reset}`);
124
- (0, terminal_js_1.writeLine)(`${colors_js_1.colors.dim}输入 /help 查看可用命令${colors_js_1.colors.reset}\n`);
124
+ (0, terminal_js_1.writeLine)(`${colors_js_1.uiColors.error}✗ ${(0, registry_js_1.t)('messages.unknownCommand')}: /${command}${colors_js_1.colors.reset}`);
125
+ (0, terminal_js_1.writeLine)(`${colors_js_1.uiColors.textSecondary}${(0, registry_js_1.t)('messages.helpPrompt')}${colors_js_1.colors.reset}\n`);
125
126
  return false;
126
127
  }
127
128
  /**
@@ -131,12 +132,12 @@ function handleCommand(input) {
131
132
  function getAvailableCommands() {
132
133
  const commands = [];
133
134
  // Add default commands
134
- Object.entries(defaultCommands).forEach(([cmd, { description }]) => {
135
- commands.push({ command: cmd, description });
135
+ Object.entries(defaultCommands).forEach(([cmd, { descriptionKey }]) => {
136
+ commands.push({ command: cmd, description: (0, registry_js_1.t)(descriptionKey) });
136
137
  });
137
138
  // Add custom commands
138
- Object.entries(customCommands).forEach(([cmd, { description }]) => {
139
- commands.push({ command: cmd, description });
139
+ Object.entries(customCommands).forEach(([cmd, { descriptionKey }]) => {
140
+ commands.push({ command: cmd, description: descriptionKey });
140
141
  });
141
142
  return commands;
142
143
  }
@@ -144,17 +145,17 @@ function getAvailableCommands() {
144
145
  * Show help for all commands
145
146
  */
146
147
  function showCommandHelp() {
147
- (0, terminal_js_1.writeLine)('\n可用命令:');
148
+ (0, terminal_js_1.writeLine)(`\n${(0, registry_js_1.t)('commands.availableCommands')}:`);
148
149
  // Show default commands
149
- (0, terminal_js_1.writeLine)(`\n${colors_js_1.colors.cyan}默认命令:${colors_js_1.colors.reset}`);
150
- Object.entries(defaultCommands).forEach(([cmd, { description }]) => {
151
- (0, terminal_js_1.writeLine)(` ${colors_js_1.colors.cyan}/${cmd}${colors_js_1.colors.reset} - ${description}`);
150
+ (0, terminal_js_1.writeLine)(`\n${colors_js_1.uiColors.primary}${(0, registry_js_1.t)('commands.defaultCommands')}:${colors_js_1.colors.reset}`);
151
+ Object.entries(defaultCommands).forEach(([cmd, { descriptionKey }]) => {
152
+ (0, terminal_js_1.writeLine)(` ${colors_js_1.uiColors.primary}/${cmd}${colors_js_1.colors.reset} - ${(0, registry_js_1.t)(descriptionKey)}`);
152
153
  });
153
154
  // Show custom commands if any
154
155
  if (Object.keys(customCommands).length > 0) {
155
- (0, terminal_js_1.writeLine)(`\n${colors_js_1.colors.cyan}自定义命令:${colors_js_1.colors.reset}`);
156
- Object.entries(customCommands).forEach(([cmd, { description }]) => {
157
- (0, terminal_js_1.writeLine)(` ${colors_js_1.colors.cyan}/${cmd}${colors_js_1.colors.reset} - ${description}`);
156
+ (0, terminal_js_1.writeLine)(`\n${colors_js_1.uiColors.primary}${(0, registry_js_1.t)('commands.customCommands')}:${colors_js_1.colors.reset}`);
157
+ Object.entries(customCommands).forEach(([cmd, { descriptionKey }]) => {
158
+ (0, terminal_js_1.writeLine)(` ${colors_js_1.uiColors.primary}/${cmd}${colors_js_1.colors.reset} - ${descriptionKey}`);
158
159
  });
159
160
  }
160
161
  (0, terminal_js_1.writeLine)('');
package/dist/index.d.ts CHANGED
@@ -8,10 +8,12 @@ export { renderPage, type PageLayoutConfig, type HeaderConfig as PageHeaderConfi
8
8
  export { createFullHeaderComponent, createSimpleHeaderComponent, createSectionHeaderComponent, createHintsComponent, createTableComponent, createListComponent, createSummaryTableComponent, createRadioMenuComponent, createInputPromptComponent } from './component-factories.js';
9
9
  export { showRadioMenu, showCheckboxMenu, showCheckboxTableMenu, showBooleanMenu } from './components/menus/index.js';
10
10
  export { showTextInput, showNumberInput, showLanguageSelector, showModifyField } from './components/inputs/index.js';
11
- export { renderSimpleHeader, renderSectionHeader, renderAsciiHeader, createSimpleHeader, createSectionHeader, createAsciiHeader, renderProgressIndicator, renderStageHeader, renderStageSeparator, createProgressIndicator, createStageHeader, createStageSeparator, renderMessage, showSuccess, showError, showWarning, showInfo, showQuestion, createMessage, renderSummaryTable, createSummaryTable, createSimpleSummary, renderHeader, type HeaderConfig, renderHintsComponent, createHints, generateMenuHints, generateInputHints, HintTypes, type HintsConfig, renderTable, createTable, type TableConfig, type TableColumn, renderList, createList, createBulletList, createNumberedList, type ListConfig, type ListItem } from './components/display/index.js';
11
+ export { renderSimpleHeader, renderSectionHeader, renderAsciiHeader, createSimpleHeader, createSectionHeader, createAsciiHeader, renderProgressIndicator, renderStageHeader, renderStageSeparator, createProgressIndicator, createStageHeader, createStageSeparator, renderProgressCheckmark, createProgressCheckmark, renderMessage, showSuccess, showError, showWarning, showInfo, showQuestion, createMessage, renderSummaryTable, createSummaryTable, createSimpleSummary, renderHeader, type HeaderConfig, renderHintsComponent, createHints, generateMenuHints, generateInputHints, HintTypes, type HintsConfig, renderTable, createTable, type TableConfig, type TableColumn, renderList, createList, createBulletList, createNumberedList, type ListConfig, type ListItem } from './components/display/index.js';
12
12
  export { runWizard, createWizard, WizardConfig, WizardStep, WizardResult } from './features/wizard.js';
13
13
  export { registerCommand, unregisterCommand, clearCustomCommands, isCommand, parseCommand, handleCommand, getAvailableCommands, showCommandHelp } from './features/commands.js';
14
14
  export { getCurrentLanguage, setLanguage, t, registerLanguage, getAvailableLanguages, getCurrentLanguageMap } from './i18n/registry.js';
15
+ export { initConfig, getConfigManager, loadConfig, saveConfig, getConfig, setConfig, resetConfig, type UserConfig, type ConfigOptions } from './config/user-config.js';
16
+ export { initLanguages, loadLanguagesFromFile, getLanguageManager, getSupportedLanguages, getLanguageConfig, getDefaultLanguage, getCLILanguageCode, type LanguageConfig, type LanguagesConfig } from './config/language-config.js';
15
17
  export { colors, uiColors, defaultUIColors, getUIColors, setUIColors, resetUIColors, createGradient, applyGradient, colorize } from './core/colors.js';
16
18
  export type { MenuOption, BaseMenuConfig, RadioMenuConfig, CheckboxMenuConfig, BooleanMenuConfig, RadioMenuResult, CheckboxMenuResult, BooleanMenuResult } from './types/menu.types.js';
17
19
  export type { BaseInputConfig, TextInputConfig, NumberInputConfig, LanguageSelectorConfig, ModifyFieldConfig, TextInputResult, NumberInputResult, LanguageSelectorResult, ModifyFieldResult } from './types/input.types.js';
package/dist/index.js CHANGED
@@ -21,8 +21,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
21
21
  return (mod && mod.__esModule) ? mod : { "default": mod };
22
22
  };
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
- 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.createStageSeparator = exports.createStageHeader = exports.createProgressIndicator = exports.renderStageSeparator = exports.renderStageHeader = exports.renderProgressIndicator = exports.createAsciiHeader = exports.createSectionHeader = exports.createSimpleHeader = exports.renderAsciiHeader = exports.renderSectionHeader = exports.renderSimpleHeader = exports.showModifyField = exports.showLanguageSelector = exports.showNumberInput = exports.showTextInput = exports.showBooleanMenu = exports.showCheckboxTableMenu = exports.showCheckboxMenu = exports.showRadioMenu = exports.createInputPromptComponent = exports.createRadioMenuComponent = exports.createSummaryTableComponent = exports.createListComponent = exports.createTableComponent = exports.createHintsComponent = exports.createSectionHeaderComponent = exports.createSimpleHeaderComponent = exports.createFullHeaderComponent = exports.renderPage = exports.default = exports.wizard = exports.input = exports.menu = void 0;
25
- exports.calculateVirtualScroll = exports.KEY_CODES = exports.colorize = exports.applyGradient = exports.createGradient = exports.resetUIColors = exports.setUIColors = exports.getUIColors = exports.defaultUIColors = exports.uiColors = exports.colors = exports.getCurrentLanguageMap = exports.getAvailableLanguages = exports.registerLanguage = exports.t = exports.setLanguage = exports.getCurrentLanguage = exports.showCommandHelp = exports.getAvailableCommands = exports.handleCommand = exports.parseCommand = exports.isCommand = exports.clearCustomCommands = exports.unregisterCommand = exports.registerCommand = exports.createWizard = exports.runWizard = exports.createNumberedList = exports.createBulletList = exports.createList = exports.renderList = exports.createTable = exports.renderTable = void 0;
24
+ 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 = exports.showModifyField = exports.showLanguageSelector = exports.showNumberInput = exports.showTextInput = exports.showBooleanMenu = exports.showCheckboxTableMenu = exports.showCheckboxMenu = exports.showRadioMenu = exports.createInputPromptComponent = exports.createRadioMenuComponent = exports.createSummaryTableComponent = exports.createListComponent = exports.createTableComponent = exports.createHintsComponent = exports.createSectionHeaderComponent = exports.createSimpleHeaderComponent = exports.createFullHeaderComponent = exports.renderPage = exports.default = exports.wizard = exports.input = exports.menu = void 0;
25
+ exports.calculateVirtualScroll = exports.KEY_CODES = exports.colorize = exports.applyGradient = exports.createGradient = exports.resetUIColors = exports.setUIColors = exports.getUIColors = exports.defaultUIColors = exports.uiColors = exports.colors = exports.getCLILanguageCode = exports.getDefaultLanguage = exports.getLanguageConfig = exports.getSupportedLanguages = exports.getLanguageManager = exports.loadLanguagesFromFile = exports.initLanguages = exports.resetConfig = exports.setConfig = exports.getConfig = exports.saveConfig = exports.loadConfig = exports.getConfigManager = exports.initConfig = exports.getCurrentLanguageMap = exports.getAvailableLanguages = exports.registerLanguage = exports.t = exports.setLanguage = exports.getCurrentLanguage = exports.showCommandHelp = exports.getAvailableCommands = exports.handleCommand = exports.parseCommand = exports.isCommand = exports.clearCustomCommands = exports.unregisterCommand = exports.registerCommand = exports.createWizard = exports.runWizard = exports.createNumberedList = exports.createBulletList = exports.createList = exports.renderList = exports.createTable = exports.renderTable = exports.HintTypes = exports.generateInputHints = void 0;
26
26
  // Export unified API
27
27
  var api_js_1 = require("./api.js");
28
28
  Object.defineProperty(exports, "menu", { enumerable: true, get: function () { return api_js_1.menuAPI; } });
@@ -70,6 +70,8 @@ Object.defineProperty(exports, "renderStageSeparator", { enumerable: true, get:
70
70
  Object.defineProperty(exports, "createProgressIndicator", { enumerable: true, get: function () { return index_js_3.createProgressIndicator; } });
71
71
  Object.defineProperty(exports, "createStageHeader", { enumerable: true, get: function () { return index_js_3.createStageHeader; } });
72
72
  Object.defineProperty(exports, "createStageSeparator", { enumerable: true, get: function () { return index_js_3.createStageSeparator; } });
73
+ Object.defineProperty(exports, "renderProgressCheckmark", { enumerable: true, get: function () { return index_js_3.renderProgressCheckmark; } });
74
+ Object.defineProperty(exports, "createProgressCheckmark", { enumerable: true, get: function () { return index_js_3.createProgressCheckmark; } });
73
75
  Object.defineProperty(exports, "renderMessage", { enumerable: true, get: function () { return index_js_3.renderMessage; } });
74
76
  Object.defineProperty(exports, "showSuccess", { enumerable: true, get: function () { return index_js_3.showSuccess; } });
75
77
  Object.defineProperty(exports, "showError", { enumerable: true, get: function () { return index_js_3.showError; } });
@@ -113,6 +115,23 @@ Object.defineProperty(exports, "t", { enumerable: true, get: function () { retur
113
115
  Object.defineProperty(exports, "registerLanguage", { enumerable: true, get: function () { return registry_js_1.registerLanguage; } });
114
116
  Object.defineProperty(exports, "getAvailableLanguages", { enumerable: true, get: function () { return registry_js_1.getAvailableLanguages; } });
115
117
  Object.defineProperty(exports, "getCurrentLanguageMap", { enumerable: true, get: function () { return registry_js_1.getCurrentLanguageMap; } });
118
+ // Export configuration system
119
+ var user_config_js_1 = require("./config/user-config.js");
120
+ Object.defineProperty(exports, "initConfig", { enumerable: true, get: function () { return user_config_js_1.initConfig; } });
121
+ Object.defineProperty(exports, "getConfigManager", { enumerable: true, get: function () { return user_config_js_1.getConfigManager; } });
122
+ Object.defineProperty(exports, "loadConfig", { enumerable: true, get: function () { return user_config_js_1.loadConfig; } });
123
+ Object.defineProperty(exports, "saveConfig", { enumerable: true, get: function () { return user_config_js_1.saveConfig; } });
124
+ Object.defineProperty(exports, "getConfig", { enumerable: true, get: function () { return user_config_js_1.getConfig; } });
125
+ Object.defineProperty(exports, "setConfig", { enumerable: true, get: function () { return user_config_js_1.setConfig; } });
126
+ Object.defineProperty(exports, "resetConfig", { enumerable: true, get: function () { return user_config_js_1.resetConfig; } });
127
+ var language_config_js_1 = require("./config/language-config.js");
128
+ Object.defineProperty(exports, "initLanguages", { enumerable: true, get: function () { return language_config_js_1.initLanguages; } });
129
+ Object.defineProperty(exports, "loadLanguagesFromFile", { enumerable: true, get: function () { return language_config_js_1.loadLanguagesFromFile; } });
130
+ Object.defineProperty(exports, "getLanguageManager", { enumerable: true, get: function () { return language_config_js_1.getLanguageManager; } });
131
+ Object.defineProperty(exports, "getSupportedLanguages", { enumerable: true, get: function () { return language_config_js_1.getSupportedLanguages; } });
132
+ Object.defineProperty(exports, "getLanguageConfig", { enumerable: true, get: function () { return language_config_js_1.getLanguageConfig; } });
133
+ Object.defineProperty(exports, "getDefaultLanguage", { enumerable: true, get: function () { return language_config_js_1.getDefaultLanguage; } });
134
+ Object.defineProperty(exports, "getCLILanguageCode", { enumerable: true, get: function () { return language_config_js_1.getCLILanguageCode; } });
116
135
  // Export color configuration
117
136
  var colors_js_1 = require("./core/colors.js");
118
137
  Object.defineProperty(exports, "colors", { enumerable: true, get: function () { return colors_js_1.colors; } });
package/dist/layout.d.ts CHANGED
@@ -1,68 +1,67 @@
1
1
  /**
2
- * Page Layout V2 - Component-Based Architecture with Fixed Regions
3
- *
4
- * Core Principles:
5
- * 1. Complete component decoupling
6
- * 2. Fixed-height regions (Header/Footer fixed, Main fills rest)
7
- * 3. Components return string arrays (lines)
8
- * 4. Diff-based rendering - only update changed regions
9
- * 5. Two-phase execution: render (non-blocking) + interact (blocking)
2
+ * Page Layout System
3
+ * 通用页面布局:Header + Main Area + Footer
10
4
  */
11
- import { ScreenManager, Rect } from './core/screen-manager.js';
12
- import { HintManager } from './core/hint-manager.js';
13
- export declare const screenManager: ScreenManager;
14
- export declare const hintManager: HintManager;
15
- export { ScreenManager, HintManager, Rect };
16
5
  /**
17
- * Component interface - components return lines instead of writing to stdout
6
+ * Header 配置
18
7
  */
19
- export interface Component {
20
- /** Component type identifier */
21
- type: string;
22
- /** Region ID for this component */
23
- regionId: string;
24
- /** Render function - returns lines to display */
25
- render: (rect: Rect) => string[] | Promise<string[]>;
26
- /** Optional interact function - handles user input (blocking) */
27
- interact?: () => void | Promise<void>;
28
- /** Optional component-specific configuration */
29
- config?: any;
8
+ export interface HeaderConfig {
9
+ type: 'simple' | 'section' | 'none';
10
+ text?: string;
11
+ width?: number;
30
12
  }
31
13
  /**
32
- * Layout configuration with fixed heights
14
+ * Main Area 配置
33
15
  */
34
- export interface Layout {
35
- header: Rect;
36
- main: Rect;
37
- footerHints: Rect;
38
- footerPrompt: Rect;
16
+ export interface MainAreaConfig {
17
+ type: 'menu' | 'display' | 'interactive';
18
+ render: () => void | Promise<void>;
39
19
  }
40
20
  /**
41
- * Compute layout based on terminal size
42
- */
43
- export declare function computeLayout(rows?: number, cols?: number): Layout;
44
- /**
45
- * Area configuration
21
+ * Footer 配置
46
22
  */
47
- export interface AreaConfig {
48
- components: Component[];
23
+ export interface FooterConfig {
24
+ menu?: {
25
+ options: string[];
26
+ allowLetterKeys?: boolean;
27
+ allowNumberKeys?: boolean;
28
+ };
29
+ input?: {
30
+ prompt: string;
31
+ defaultValue?: string;
32
+ allowEmpty?: boolean;
33
+ };
34
+ ask?: {
35
+ question: string;
36
+ defaultValue?: boolean;
37
+ horizontal?: boolean;
38
+ };
39
+ hints?: string[];
49
40
  }
50
41
  /**
51
- * Page layout configuration
42
+ * 完整页面布局配置
52
43
  */
53
- export interface PageLayoutConfigV2 {
54
- header?: AreaConfig;
55
- mainArea?: AreaConfig;
56
- footer?: AreaConfig;
44
+ export interface PageLayoutConfig {
45
+ header?: HeaderConfig;
46
+ mainArea: MainAreaConfig;
47
+ footer?: FooterConfig;
57
48
  }
58
49
  /**
59
- * Render complete page with fixed-region architecture
50
+ * 渲染完整页面
60
51
  *
61
- * Phase 1: Render all components (non-blocking)
62
- * Phase 2: Handle interactions (blocking)
63
- */
64
- export declare function renderPageV2(config: PageLayoutConfigV2): Promise<void>;
65
- /**
66
- * Helper to create a custom component
52
+ * @example
53
+ * ```typescript
54
+ * const result = await renderPage({
55
+ * header: { type: 'simple', text: 'My Page' },
56
+ * mainArea: {
57
+ * type: 'display',
58
+ * render: () => console.log('Content')
59
+ * },
60
+ * footer: {
61
+ * menu: { options: ['1. Save', 'b. Back'] },
62
+ * hints: ['↑↓ Navigate Enter Confirm']
63
+ * }
64
+ * });
65
+ * ```
67
66
  */
68
- export declare function createCustomComponent(type: string, regionId: string, render: (rect: Rect) => string[] | Promise<string[]>, interact?: () => void | Promise<void>): Component;
67
+ export declare function renderPage(config: PageLayoutConfig): Promise<any>;
package/dist/layout.js CHANGED
@@ -1,134 +1,86 @@
1
1
  "use strict";
2
2
  /**
3
- * Page Layout V2 - Component-Based Architecture with Fixed Regions
4
- *
5
- * Core Principles:
6
- * 1. Complete component decoupling
7
- * 2. Fixed-height regions (Header/Footer fixed, Main fills rest)
8
- * 3. Components return string arrays (lines)
9
- * 4. Diff-based rendering - only update changed regions
10
- * 5. Two-phase execution: render (non-blocking) + interact (blocking)
3
+ * Page Layout System
4
+ * 通用页面布局:Header + Main Area + Footer
11
5
  */
12
6
  Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.HintManager = exports.ScreenManager = exports.hintManager = exports.screenManager = void 0;
14
- exports.computeLayout = computeLayout;
15
- exports.renderPageV2 = renderPageV2;
16
- exports.createCustomComponent = createCustomComponent;
17
- const screen_manager_js_1 = require("./core/screen-manager.js");
18
- Object.defineProperty(exports, "ScreenManager", { enumerable: true, get: function () { return screen_manager_js_1.ScreenManager; } });
19
- const hint_manager_js_1 = require("./core/hint-manager.js");
20
- Object.defineProperty(exports, "HintManager", { enumerable: true, get: function () { return hint_manager_js_1.HintManager; } });
21
- // Global instances
22
- exports.screenManager = new screen_manager_js_1.ScreenManager();
23
- exports.hintManager = new hint_manager_js_1.HintManager();
24
- /**
25
- * Compute layout based on terminal size
26
- */
27
- function computeLayout(rows, cols) {
28
- const termRows = rows || process.stdout.rows || 24;
29
- const termCols = cols || process.stdout.columns || 80;
30
- const headerHeight = 13; // Fixed header height (6 ASCII art + title + desc + separator)
31
- const footerHintsHeight = 1; // Fixed hints height
32
- const footerPromptHeight = 1; // Fixed prompt height
33
- const mainHeight = Math.max(1, termRows - headerHeight - footerHintsHeight - footerPromptHeight);
34
- return {
35
- header: { top: 1, left: 1, width: termCols, height: headerHeight },
36
- main: { top: 1 + headerHeight, left: 1, width: termCols, height: mainHeight },
37
- footerHints: { top: 1 + headerHeight + mainHeight, left: 1, width: termCols, height: footerHintsHeight },
38
- footerPrompt: { top: 1 + headerHeight + mainHeight + footerHintsHeight, left: 1, width: termCols, height: footerPromptHeight }
39
- };
40
- }
7
+ exports.renderPage = renderPage;
8
+ const api_js_1 = require("./api.js");
9
+ const headers_js_1 = require("./components/display/headers.js");
41
10
  /**
42
- * Render complete page with fixed-region architecture
43
- *
44
- * Phase 1: Render all components (non-blocking)
45
- * Phase 2: Handle interactions (blocking)
11
+ * 渲染 Header
46
12
  */
47
- async function renderPageV2(config) {
48
- // Enter alt screen
49
- exports.screenManager.enter();
50
- // Compute layout
51
- const layout = computeLayout();
52
- // Register regions
53
- if (config.header?.components) {
54
- for (const component of config.header.components) {
55
- exports.screenManager.registerRegion(component.regionId, layout.header);
56
- }
57
- }
58
- if (config.mainArea?.components) {
59
- for (const component of config.mainArea.components) {
60
- exports.screenManager.registerRegion(component.regionId, layout.main);
61
- }
13
+ function renderHeader(config) {
14
+ if (!config || config.type === 'none') {
15
+ return;
62
16
  }
63
- if (config.footer?.components) {
64
- for (const component of config.footer.components) {
65
- // Map components to their specific footer regions
66
- if (component.type === 'hints') {
67
- exports.screenManager.registerRegion(component.regionId, layout.footerHints);
68
- }
69
- else if (component.type === 'prompt') {
70
- exports.screenManager.registerRegion(component.regionId, layout.footerPrompt);
71
- }
72
- }
17
+ if (config.type === 'simple' && config.text) {
18
+ (0, headers_js_1.renderSimpleHeader)(config.text);
73
19
  }
74
- // Phase 1: Initial render
75
- const renderComponent = async (component, rect) => {
76
- const lines = await component.render(rect);
77
- exports.screenManager.renderRegion(component.regionId, lines);
78
- };
79
- if (config.header?.components) {
80
- for (const component of config.header.components) {
81
- await renderComponent(component, layout.header);
82
- }
20
+ else if (config.type === 'section' && config.text) {
21
+ (0, headers_js_1.renderSectionHeader)(config.text, config.width || 50);
83
22
  }
84
- if (config.mainArea?.components) {
85
- for (const component of config.mainArea.components) {
86
- await renderComponent(component, layout.main);
87
- }
88
- }
89
- if (config.footer?.components) {
90
- for (const component of config.footer.components) {
91
- const rect = component.type === 'hints' ? layout.footerHints : layout.footerPrompt;
92
- await renderComponent(component, rect);
93
- }
94
- }
95
- // Setup hint manager listener
96
- exports.hintManager.on('change', (text) => {
97
- const hintsComponent = config.footer?.components.find(c => c.type === 'hints');
98
- if (hintsComponent) {
99
- exports.screenManager.renderRegion(hintsComponent.regionId, [text]);
100
- }
101
- });
102
- // Handle terminal resize
103
- process.stdout.on('resize', () => {
104
- const newLayout = computeLayout();
105
- exports.screenManager.invalidateAll();
106
- // Re-register and re-render all regions
107
- // (Implementation can be enhanced to avoid code duplication)
108
- });
109
- // Phase 2: Handle interactions
110
- const interactiveComponents = [];
111
- if (config.header?.components) {
112
- interactiveComponents.push(...config.header.components.filter(c => c.interact));
23
+ }
24
+ /**
25
+ * 渲染 Footer
26
+ * 返回用户的选择/输入结果
27
+ */
28
+ async function renderFooter(config) {
29
+ if (!config) {
30
+ return null;
113
31
  }
114
- if (config.mainArea?.components) {
115
- interactiveComponents.push(...config.mainArea.components.filter(c => c.interact));
32
+ let result = null;
33
+ // 1. Menu (如果有)
34
+ if (config.menu) {
35
+ result = await api_js_1.menuAPI.radio({
36
+ options: config.menu.options,
37
+ allowLetterKeys: config.menu.allowLetterKeys ?? true,
38
+ allowNumberKeys: config.menu.allowNumberKeys ?? true,
39
+ hints: config.hints,
40
+ preserveOnSelect: true
41
+ });
116
42
  }
117
- if (config.footer?.components) {
118
- interactiveComponents.push(...config.footer.components.filter(c => c.interact));
43
+ // 2. Input (如果有)
44
+ else if (config.input) {
45
+ result = await api_js_1.inputAPI.text({
46
+ prompt: config.input.prompt,
47
+ defaultValue: config.input.defaultValue,
48
+ allowEmpty: config.input.allowEmpty ?? false
49
+ });
119
50
  }
120
- // Execute interact phase
121
- for (const component of interactiveComponents) {
122
- if (component.interact) {
123
- await component.interact();
124
- }
51
+ // 3. Ask (如果有 - 通常在 Menu 或 Input 之后)
52
+ if (config.ask) {
53
+ const askResult = config.ask.horizontal
54
+ ? await api_js_1.menuAPI.booleanH(config.ask.question, config.ask.defaultValue ?? false)
55
+ : await api_js_1.menuAPI.booleanV(config.ask.question, config.ask.defaultValue ?? false);
56
+ return { ...result, confirmed: askResult };
125
57
  }
126
- // Exit alt screen
127
- exports.screenManager.exit();
58
+ return result;
128
59
  }
129
60
  /**
130
- * Helper to create a custom component
61
+ * 渲染完整页面
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * const result = await renderPage({
66
+ * header: { type: 'simple', text: 'My Page' },
67
+ * mainArea: {
68
+ * type: 'display',
69
+ * render: () => console.log('Content')
70
+ * },
71
+ * footer: {
72
+ * menu: { options: ['1. Save', 'b. Back'] },
73
+ * hints: ['↑↓ Navigate Enter Confirm']
74
+ * }
75
+ * });
76
+ * ```
131
77
  */
132
- function createCustomComponent(type, regionId, render, interact) {
133
- return { type, regionId, render, interact };
78
+ async function renderPage(config) {
79
+ // 1. Render Header
80
+ renderHeader(config.header);
81
+ // 2. Render Main Area
82
+ await config.mainArea.render();
83
+ // 3. Render Footer
84
+ const footerResult = await renderFooter(config.footer);
85
+ return footerResult;
134
86
  }