cli-menu-kit 0.1.13 → 0.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -42,6 +42,89 @@ A comprehensive, modular CLI menu system for Node.js with full TypeScript suppor
42
42
  npm install cli-menu-kit
43
43
  ```
44
44
 
45
+ ## Configuration
46
+
47
+ CLI Menu Kit is highly customizable with sensible defaults. Configure colors, language, and UI elements to match your application's style.
48
+
49
+ ### 🎨 Color Customization
50
+
51
+ Customize all UI colors globally:
52
+
53
+ ```javascript
54
+ import { setUIColors, colors } from 'cli-menu-kit';
55
+
56
+ // Override specific colors (all optional)
57
+ setUIColors({
58
+ primary: colors.blue, // Main interactive elements, highlights
59
+ textSecondary: colors.dim, // Descriptions, hints
60
+ error: colors.red, // Errors, exit options
61
+ border: colors.magenta, // Borders, frames
62
+ separator: colors.dim, // Section separators
63
+ // ... see full list in documentation
64
+ });
65
+
66
+ // Reset to defaults
67
+ import { resetUIColors } from 'cli-menu-kit';
68
+ resetUIColors();
69
+ ```
70
+
71
+ ### 🌍 Language Support
72
+
73
+ Switch between English and Chinese (or add custom languages):
74
+
75
+ ```javascript
76
+ import { setLanguage } from 'cli-menu-kit';
77
+
78
+ setLanguage('en'); // English (default: 'zh')
79
+ ```
80
+
81
+ ### 🎯 Header Styles
82
+
83
+ Three header modes for different contexts:
84
+
85
+ ```javascript
86
+ import { renderHeader, renderSectionHeader, renderSimpleHeader } from 'cli-menu-kit';
87
+
88
+ // Full header (main menu, initialization)
89
+ renderHeader({
90
+ asciiArt: ['...'], // Optional
91
+ title: 'Product Name', // Optional
92
+ description: '...', // Optional
93
+ version: '1.0.0', // Optional - omit to hide
94
+ url: 'https://...', // Optional - omit to hide
95
+ menuTitle: 'Select option:' // Optional - omit to hide
96
+ });
97
+
98
+ // Section header (sub-menus)
99
+ renderSectionHeader('Section Title', 50); // Width configurable
100
+
101
+ // Simple header (quick prompts)
102
+ renderSimpleHeader('Simple Title');
103
+ ```
104
+
105
+ ### ⚙️ Menu Options
106
+
107
+ All menu options are configurable:
108
+
109
+ ```javascript
110
+ menu.radio({
111
+ options: [
112
+ // Optional grouping with separators
113
+ { type: 'separator', label: 'Setup' },
114
+ '1. Option 1',
115
+ '2. Option 2',
116
+ { type: 'separator', label: 'Advanced' },
117
+ '3. Option 3'
118
+ ],
119
+
120
+ title: 'Menu Title', // Optional
121
+ hints: ['↑↓ Navigate'], // Optional - omit or pass [] to hide
122
+ separatorWidth: 40, // Optional - default: 30
123
+ allowNumberKeys: true, // Optional - default: true
124
+ allowLetterKeys: false // Optional - default: false
125
+ });
126
+ ```
127
+
45
128
  ## Quick Start
46
129
 
47
130
  ### Unified API (Recommended)
@@ -35,8 +35,9 @@ function renderHeader(config) {
35
35
  }
36
36
  // Description (left-aligned with 2 spaces padding, gray text)
37
37
  if (description) {
38
- const paddedDesc = ` ${colors_js_1.uiColors.textSecondary}${description}${colors_js_1.colors.reset}`.padEnd(boxWidth - 2 + colors_js_1.uiColors.textSecondary.length + colors_js_1.colors.reset.length, ' ');
39
- (0, terminal_js_1.writeLine)(`${boldColor}║${paddedDesc}║${colors_js_1.colors.reset}`);
38
+ const textContent = ` ${description}`;
39
+ const paddedText = textContent.padEnd(boxWidth - 2, ' ');
40
+ (0, terminal_js_1.writeLine)(`${boldColor}║${colors_js_1.uiColors.textSecondary}${paddedText}${colors_js_1.colors.reset}${boldColor}║${colors_js_1.colors.reset}`);
40
41
  (0, terminal_js_1.writeLine)(`${boldColor}║${' '.repeat(boxWidth - 2)}║${colors_js_1.colors.reset}`);
41
42
  }
42
43
  // Bottom border
@@ -45,8 +46,8 @@ function renderHeader(config) {
45
46
  (0, terminal_js_1.writeLine)('');
46
47
  // Version and URL (outside the box, with colors)
47
48
  if (version || url) {
48
- const versionText = version ? `${colors_js_1.uiColors.info}Version: ${version}${colors_js_1.colors.reset}` : '';
49
- const urlText = url ? `${colors_js_1.uiColors.accent}${url}${colors_js_1.colors.reset}` : '';
49
+ const versionText = version ? `${colors_js_1.uiColors.textSecondary}Version: ${colors_js_1.colors.reset}${colors_js_1.uiColors.primary}${version}${colors_js_1.colors.reset}` : '';
50
+ const urlText = url ? `${colors_js_1.uiColors.primary}${url}${colors_js_1.colors.reset}` : '';
50
51
  const separator = version && url ? `${colors_js_1.uiColors.textSecondary} | ${colors_js_1.colors.reset}` : '';
51
52
  (0, terminal_js_1.writeLine)(` ${versionText}${separator}${urlText}`);
52
53
  }
@@ -30,7 +30,7 @@ function generateHints(allowSelectAll, allowInvert) {
30
30
  * @returns Promise resolving to selected options
31
31
  */
32
32
  async function showCheckboxMenu(config) {
33
- const { options, title, prompt, hints, layout = { ...layout_types_js_1.LAYOUT_PRESETS.SUB_MENU, order: ['input', 'options', 'hints'] }, defaultSelected = [], minSelections = 0, maxSelections, allowSelectAll = true, allowInvert = true, onExit } = config;
33
+ const { options, title, prompt, hints, layout = { ...layout_types_js_1.LAYOUT_PRESETS.SUB_MENU, order: ['input', 'options', 'hints'] }, defaultSelected = [], minSelections = 0, maxSelections, allowSelectAll = true, allowInvert = true, separatorWidth = 30, onExit } = config;
34
34
  // Use i18n for default prompt if not provided
35
35
  const displayPrompt = prompt || (0, registry_js_1.t)('menus.multiSelectPrompt');
36
36
  // Generate hints dynamically if not provided
@@ -110,7 +110,7 @@ async function showCheckboxMenu(config) {
110
110
  optionData.forEach((item, index) => {
111
111
  if (item.isSeparator) {
112
112
  // Render section label
113
- (0, renderer_js_1.renderSectionLabel)(item.label);
113
+ (0, renderer_js_1.renderSectionLabel)(item.label, separatorWidth);
114
114
  }
115
115
  else {
116
116
  (0, renderer_js_1.renderOption)(item.value, selected.has(index), index === cursorIndex);
@@ -31,7 +31,7 @@ function generateHints(allowNumberKeys, allowLetterKeys) {
31
31
  * @returns Promise resolving to selected option
32
32
  */
33
33
  async function showRadioMenu(config) {
34
- const { options, title, prompt, hints, layout = layout_types_js_1.LAYOUT_PRESETS.MAIN_MENU, defaultIndex = 0, allowNumberKeys = true, allowLetterKeys = false, onExit } = config;
34
+ const { options, title, prompt, hints, layout = layout_types_js_1.LAYOUT_PRESETS.MAIN_MENU, defaultIndex = 0, allowNumberKeys = true, allowLetterKeys = false, separatorWidth = 30, onExit } = config;
35
35
  // Use i18n for default prompt if not provided
36
36
  const displayPrompt = prompt || (0, registry_js_1.t)('menus.selectPrompt');
37
37
  // Generate hints dynamically if not provided
@@ -107,8 +107,8 @@ async function showRadioMenu(config) {
107
107
  case 'options':
108
108
  optionData.forEach((item, index) => {
109
109
  if (item.isSeparator) {
110
- // Render section label
111
- (0, renderer_js_1.renderSectionLabel)(item.label);
110
+ // Render section label with configured width
111
+ (0, renderer_js_1.renderSectionLabel)(item.label, separatorWidth);
112
112
  }
113
113
  else {
114
114
  // Check if option starts with a number or letter prefix
@@ -43,6 +43,55 @@ export declare const colors: {
43
43
  * Semantic color hierarchy for UI elements
44
44
  * Defines consistent colors for different levels of information
45
45
  */
46
+ export declare const defaultUIColors: {
47
+ readonly primary: "\u001B[36m";
48
+ readonly accent: "\u001B[34m";
49
+ readonly textPrimary: "\u001B[0m";
50
+ readonly textSecondary: "\u001B[2m";
51
+ readonly textMuted: "\u001B[90m";
52
+ readonly success: "\u001B[32m";
53
+ readonly error: "\u001B[31m";
54
+ readonly warning: "\u001B[33m";
55
+ readonly info: "\u001B[34m";
56
+ readonly cursor: "\u001B[36m";
57
+ readonly selected: "\u001B[32m";
58
+ readonly disabled: "\u001B[2m";
59
+ readonly border: "\u001B[36m";
60
+ readonly separator: "\u001B[2m";
61
+ readonly prefix: "\u001B[2m";
62
+ };
63
+ /**
64
+ * Get current UI colors
65
+ */
66
+ export declare function getUIColors(): {
67
+ primary: "\u001B[36m";
68
+ accent: "\u001B[34m";
69
+ textPrimary: "\u001B[0m";
70
+ textSecondary: "\u001B[2m";
71
+ textMuted: "\u001B[90m";
72
+ success: "\u001B[32m";
73
+ error: "\u001B[31m";
74
+ warning: "\u001B[33m";
75
+ info: "\u001B[34m";
76
+ cursor: "\u001B[36m";
77
+ selected: "\u001B[32m";
78
+ disabled: "\u001B[2m";
79
+ border: "\u001B[36m";
80
+ separator: "\u001B[2m";
81
+ prefix: "\u001B[2m";
82
+ };
83
+ /**
84
+ * Set custom UI colors (partial override)
85
+ * @param customColors - Partial UI colors to override defaults
86
+ */
87
+ export declare function setUIColors(customColors: Partial<typeof defaultUIColors>): void;
88
+ /**
89
+ * Reset UI colors to defaults
90
+ */
91
+ export declare function resetUIColors(): void;
92
+ /**
93
+ * UI colors accessor (always returns current colors)
94
+ */
46
95
  export declare const uiColors: {
47
96
  readonly primary: "\u001B[36m";
48
97
  readonly accent: "\u001B[34m";
@@ -4,7 +4,10 @@
4
4
  * Supports single colors and two-color gradients
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.uiColors = exports.colors = void 0;
7
+ exports.uiColors = exports.defaultUIColors = exports.colors = void 0;
8
+ exports.getUIColors = getUIColors;
9
+ exports.setUIColors = setUIColors;
10
+ exports.resetUIColors = resetUIColors;
8
11
  exports.createGradient = createGradient;
9
12
  exports.applyGradient = applyGradient;
10
13
  exports.colorize = colorize;
@@ -54,7 +57,7 @@ exports.colors = {
54
57
  * Semantic color hierarchy for UI elements
55
58
  * Defines consistent colors for different levels of information
56
59
  */
57
- exports.uiColors = {
60
+ exports.defaultUIColors = {
58
61
  // Primary elements
59
62
  primary: exports.colors.cyan, // Main interactive elements, highlights
60
63
  accent: exports.colors.blue, // Secondary highlights, links
@@ -76,6 +79,40 @@ exports.uiColors = {
76
79
  separator: exports.colors.dim, // Separators, dividers
77
80
  prefix: exports.colors.dim // Number/letter prefixes
78
81
  };
82
+ /**
83
+ * Current UI colors (can be customized)
84
+ */
85
+ let currentUIColors = { ...exports.defaultUIColors };
86
+ /**
87
+ * Get current UI colors
88
+ */
89
+ function getUIColors() {
90
+ return currentUIColors;
91
+ }
92
+ /**
93
+ * Set custom UI colors (partial override)
94
+ * @param customColors - Partial UI colors to override defaults
95
+ */
96
+ function setUIColors(customColors) {
97
+ currentUIColors = {
98
+ ...currentUIColors,
99
+ ...customColors
100
+ };
101
+ }
102
+ /**
103
+ * Reset UI colors to defaults
104
+ */
105
+ function resetUIColors() {
106
+ currentUIColors = { ...exports.defaultUIColors };
107
+ }
108
+ /**
109
+ * UI colors accessor (always returns current colors)
110
+ */
111
+ exports.uiColors = new Proxy({}, {
112
+ get(_target, prop) {
113
+ return currentUIColors[prop];
114
+ }
115
+ });
79
116
  /**
80
117
  * Parse ANSI color code to RGB values
81
118
  * Supports basic 8 colors and bright variants
@@ -42,8 +42,9 @@ export declare function renderSeparator(char?: string, width?: number): void;
42
42
  /**
43
43
  * Render a section label (menu grouping)
44
44
  * @param label - Label text (optional)
45
+ * @param width - Total width of the separator (default: 30)
45
46
  */
46
- export declare function renderSectionLabel(label?: string): void;
47
+ export declare function renderSectionLabel(label?: string, width?: number): void;
47
48
  /**
48
49
  * Render a message with icon
49
50
  * @param type - Message type (success, error, warning, info, question)
@@ -70,13 +70,13 @@ function renderOption(text, isSelected, isHighlighted, prefix) {
70
70
  const mainText = parts[0];
71
71
  const description = parts.length > 1 ? parts.slice(1).join(' - ') : '';
72
72
  if (isHighlighted) {
73
- line += `${colors_js_1.uiColors.primary}${mainText}${colors_js_1.colors.reset}`;
73
+ line += `${colors_js_1.uiColors.primary}${colors_js_1.colors.bold}${mainText}${colors_js_1.colors.reset}`;
74
74
  if (description) {
75
75
  line += ` ${colors_js_1.uiColors.textSecondary}- ${description}${colors_js_1.colors.reset}`;
76
76
  }
77
77
  }
78
78
  else {
79
- line += mainText;
79
+ line += `${colors_js_1.colors.bold}${mainText}${colors_js_1.colors.reset}`;
80
80
  if (description) {
81
81
  line += ` ${colors_js_1.uiColors.textSecondary}- ${description}${colors_js_1.colors.reset}`;
82
82
  }
@@ -134,10 +134,11 @@ function renderSeparator(char = '─', width) {
134
134
  /**
135
135
  * Render a section label (menu grouping)
136
136
  * @param label - Label text (optional)
137
+ * @param width - Total width of the separator (default: 30)
137
138
  */
138
- function renderSectionLabel(label) {
139
+ function renderSectionLabel(label, width = 30) {
139
140
  if (label) {
140
- const totalWidth = 30; // Fixed total width for consistency
141
+ const totalWidth = width; // Use configured width
141
142
  const padding = 2; // Spaces around label
142
143
  const labelWithPadding = ` ${label} `;
143
144
  const labelLength = labelWithPadding.length;
package/dist/index.d.ts CHANGED
@@ -10,12 +10,12 @@ export { renderSimpleHeader, renderSectionHeader, renderAsciiHeader, createSimpl
10
10
  export { runWizard, createWizard, WizardConfig, WizardStep, WizardResult } from './features/wizard.js';
11
11
  export { registerCommand, unregisterCommand, clearCustomCommands, isCommand, parseCommand, handleCommand, getAvailableCommands, showCommandHelp } from './features/commands.js';
12
12
  export { getCurrentLanguage, setLanguage, t, registerLanguage, getAvailableLanguages, getCurrentLanguageMap } from './i18n/registry.js';
13
+ export { colors, uiColors, defaultUIColors, getUIColors, setUIColors, resetUIColors, createGradient, applyGradient, colorize } from './core/colors.js';
13
14
  export type { MenuLayout, LayoutElement, LayoutVisibility, LayoutSpacing } from './types/layout.types.js';
14
15
  export type { MenuOption, BaseMenuConfig, RadioMenuConfig, CheckboxMenuConfig, BooleanMenuConfig, RadioMenuResult, CheckboxMenuResult, BooleanMenuResult } from './types/menu.types.js';
15
16
  export type { BaseInputConfig, TextInputConfig, NumberInputConfig, LanguageSelectorConfig, ModifyFieldConfig, TextInputResult, NumberInputResult, LanguageSelectorResult, ModifyFieldResult } from './types/input.types.js';
16
17
  export type { HeaderType, SimpleHeaderConfig, AsciiHeaderConfig, ProgressConfig, MessageType, MessageConfig, SummaryTableConfig } from './types/display.types.js';
17
18
  export type { LanguageCode, LanguageMap, I18nRegistry } from './i18n/types.js';
18
- export { colors, createGradient, applyGradient, colorize } from './core/colors.js';
19
19
  export { KEY_CODES } from './core/keyboard.js';
20
20
  export { LAYOUT_PRESETS } from './types/layout.types.js';
21
21
  export * from './types';
package/dist/index.js CHANGED
@@ -22,7 +22,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
22
22
  };
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
24
  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.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.showCheckboxMenu = exports.showRadioMenu = exports.default = exports.wizard = exports.input = exports.menu = void 0;
25
- exports.LAYOUT_PRESETS = exports.KEY_CODES = exports.colorize = exports.applyGradient = exports.createGradient = exports.colors = void 0;
25
+ exports.LAYOUT_PRESETS = exports.KEY_CODES = exports.colorize = exports.applyGradient = exports.createGradient = exports.resetUIColors = exports.setUIColors = exports.getUIColors = exports.defaultUIColors = exports.uiColors = exports.colors = 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; } });
@@ -87,12 +87,18 @@ Object.defineProperty(exports, "t", { enumerable: true, get: function () { retur
87
87
  Object.defineProperty(exports, "registerLanguage", { enumerable: true, get: function () { return registry_js_1.registerLanguage; } });
88
88
  Object.defineProperty(exports, "getAvailableLanguages", { enumerable: true, get: function () { return registry_js_1.getAvailableLanguages; } });
89
89
  Object.defineProperty(exports, "getCurrentLanguageMap", { enumerable: true, get: function () { return registry_js_1.getCurrentLanguageMap; } });
90
- // Export core utilities (for advanced users)
90
+ // Export color configuration
91
91
  var colors_js_1 = require("./core/colors.js");
92
92
  Object.defineProperty(exports, "colors", { enumerable: true, get: function () { return colors_js_1.colors; } });
93
+ Object.defineProperty(exports, "uiColors", { enumerable: true, get: function () { return colors_js_1.uiColors; } });
94
+ Object.defineProperty(exports, "defaultUIColors", { enumerable: true, get: function () { return colors_js_1.defaultUIColors; } });
95
+ Object.defineProperty(exports, "getUIColors", { enumerable: true, get: function () { return colors_js_1.getUIColors; } });
96
+ Object.defineProperty(exports, "setUIColors", { enumerable: true, get: function () { return colors_js_1.setUIColors; } });
97
+ Object.defineProperty(exports, "resetUIColors", { enumerable: true, get: function () { return colors_js_1.resetUIColors; } });
93
98
  Object.defineProperty(exports, "createGradient", { enumerable: true, get: function () { return colors_js_1.createGradient; } });
94
99
  Object.defineProperty(exports, "applyGradient", { enumerable: true, get: function () { return colors_js_1.applyGradient; } });
95
100
  Object.defineProperty(exports, "colorize", { enumerable: true, get: function () { return colors_js_1.colorize; } });
101
+ // Export core utilities (for advanced users)
96
102
  var keyboard_js_1 = require("./core/keyboard.js");
97
103
  Object.defineProperty(exports, "KEY_CODES", { enumerable: true, get: function () { return keyboard_js_1.KEY_CODES; } });
98
104
  var layout_types_js_1 = require("./types/layout.types.js");
@@ -28,6 +28,8 @@ export interface BaseMenuConfig {
28
28
  highlightColor?: string;
29
29
  /** Goodbye message function */
30
30
  onExit?: () => void;
31
+ /** Separator width for section labels (default: 30) */
32
+ separatorWidth?: number;
31
33
  }
32
34
  /**
33
35
  * Radio menu (single-select) configuration
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cli-menu-kit",
3
- "version": "0.1.13",
3
+ "version": "0.1.17",
4
4
  "description": "A lightweight, customizable CLI menu system with keyboard shortcuts and real-time rendering",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",