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.
- package/dist/api.d.ts +23 -5
- package/dist/api.js +16 -4
- package/dist/component-factories.d.ts +59 -0
- package/dist/component-factories.js +141 -0
- package/dist/components/display/header-v2.d.ts +13 -0
- package/dist/components/display/header-v2.js +43 -0
- package/dist/components/display/header.d.ts +40 -0
- package/dist/components/display/header.js +331 -18
- package/dist/components/display/headers.d.ts +1 -0
- package/dist/components/display/headers.js +15 -5
- package/dist/components/display/hints-v2.d.ts +10 -0
- package/dist/components/display/hints-v2.js +34 -0
- package/dist/components/display/hints.d.ts +56 -0
- package/dist/components/display/hints.js +81 -0
- package/dist/components/display/index.d.ts +4 -1
- package/dist/components/display/index.js +17 -1
- package/dist/components/display/input-prompt.d.ts +35 -0
- package/dist/components/display/input-prompt.js +36 -0
- package/dist/components/display/list.d.ts +49 -0
- package/dist/components/display/list.js +86 -0
- package/dist/components/display/messages.js +5 -5
- package/dist/components/display/progress.d.ts +17 -0
- package/dist/components/display/progress.js +18 -0
- package/dist/components/display/summary.js +72 -10
- package/dist/components/display/table.d.ts +44 -0
- package/dist/components/display/table.js +108 -0
- package/dist/components/inputs/language-input.js +8 -5
- package/dist/components/inputs/number-input.js +19 -14
- package/dist/components/inputs/text-input.js +50 -13
- package/dist/components/menus/boolean-menu.js +34 -20
- package/dist/components/menus/checkbox-menu.d.ts +2 -1
- package/dist/components/menus/checkbox-menu.js +35 -61
- package/dist/components/menus/checkbox-table-menu.d.ts +12 -0
- package/dist/components/menus/checkbox-table-menu.js +398 -0
- package/dist/components/menus/index.d.ts +1 -0
- package/dist/components/menus/index.js +3 -1
- package/dist/components/menus/radio-menu-split.d.ts +34 -0
- package/dist/components/menus/radio-menu-split.js +258 -0
- package/dist/components/menus/radio-menu-v2.d.ts +11 -0
- package/dist/components/menus/radio-menu-v2.js +150 -0
- package/dist/components/menus/radio-menu.d.ts +2 -1
- package/dist/components/menus/radio-menu.js +100 -134
- package/dist/components.js +3 -3
- package/dist/config/index.d.ts +5 -0
- package/dist/config/index.js +21 -0
- package/dist/config/language-config.d.ts +73 -0
- package/dist/config/language-config.js +157 -0
- package/dist/config/user-config.d.ts +83 -0
- package/dist/config/user-config.js +185 -0
- package/dist/core/colors.d.ts +24 -18
- package/dist/core/colors.js +74 -7
- package/dist/core/hint-manager.d.ts +29 -0
- package/dist/core/hint-manager.js +65 -0
- package/dist/core/renderer.d.ts +2 -1
- package/dist/core/renderer.js +46 -22
- package/dist/core/screen-manager.d.ts +54 -0
- package/dist/core/screen-manager.js +119 -0
- package/dist/core/state-manager.d.ts +27 -0
- package/dist/core/state-manager.js +56 -0
- package/dist/core/terminal.d.ts +17 -1
- package/dist/core/terminal.js +124 -4
- package/dist/core/virtual-scroll.d.ts +65 -0
- package/dist/core/virtual-scroll.js +120 -0
- package/dist/features/commands.js +23 -22
- package/dist/i18n/languages/en.js +4 -1
- package/dist/i18n/languages/zh.js +4 -1
- package/dist/i18n/registry.d.ts +4 -3
- package/dist/i18n/registry.js +12 -4
- package/dist/i18n/types.d.ts +3 -0
- package/dist/index.d.ts +7 -4
- package/dist/index.js +49 -4
- package/dist/layout.d.ts +67 -0
- package/dist/layout.js +86 -0
- package/dist/page-layout.d.ts +123 -0
- package/dist/page-layout.js +195 -0
- package/dist/types/input.types.d.ts +8 -0
- package/dist/types/menu.types.d.ts +61 -5
- package/package.json +4 -1
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Page Layout System - Simple Configuration API
|
|
4
|
+
* Architecture: Header + Main Area + Footer
|
|
5
|
+
*
|
|
6
|
+
* This is the simple, configuration-based API that users want.
|
|
7
|
+
* Just pass config objects, no need to create components manually.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.renderPage = renderPage;
|
|
11
|
+
const api_js_1 = require("./api.js");
|
|
12
|
+
const header_js_1 = require("./components/display/header.js");
|
|
13
|
+
const headers_js_1 = require("./components/display/headers.js");
|
|
14
|
+
/**
|
|
15
|
+
* Render Header
|
|
16
|
+
*/
|
|
17
|
+
function renderHeaderSection(config) {
|
|
18
|
+
if (!config || config.type === 'none') {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const topBlankLines = Math.max(0, config.topBlankLines ?? 0);
|
|
22
|
+
for (let i = 0; i < topBlankLines; i += 1) {
|
|
23
|
+
console.log('');
|
|
24
|
+
}
|
|
25
|
+
if (config.type === 'simple' && config.text) {
|
|
26
|
+
(0, headers_js_1.renderSimpleHeader)(config.text);
|
|
27
|
+
}
|
|
28
|
+
else if (config.type === 'section' && config.text) {
|
|
29
|
+
// Add extra blank line before section header for spacing between menus
|
|
30
|
+
console.log('');
|
|
31
|
+
(0, headers_js_1.renderSectionHeader)(config.text, config.width || 50);
|
|
32
|
+
}
|
|
33
|
+
else if (config.type === 'full') {
|
|
34
|
+
// Render full header WITHOUT menuTitle
|
|
35
|
+
(0, header_js_1.renderHeader)({
|
|
36
|
+
asciiArt: config.asciiArt || [],
|
|
37
|
+
figletText: config.figletText,
|
|
38
|
+
figletFont: config.figletFont,
|
|
39
|
+
figletSize: config.figletSize,
|
|
40
|
+
figletScale: config.figletScale,
|
|
41
|
+
title: config.title || '',
|
|
42
|
+
titleColor: config.titleColor,
|
|
43
|
+
titleGradientStart: config.titleGradientStart,
|
|
44
|
+
titleGradientEnd: config.titleGradientEnd,
|
|
45
|
+
description: config.description,
|
|
46
|
+
descriptionColor: config.descriptionColor,
|
|
47
|
+
descriptionGradientStart: config.descriptionGradientStart,
|
|
48
|
+
descriptionGradientEnd: config.descriptionGradientEnd,
|
|
49
|
+
version: config.version,
|
|
50
|
+
url: config.url,
|
|
51
|
+
menuTitle: undefined, // Don't render menuTitle in header
|
|
52
|
+
boxWidth: config.boxWidth,
|
|
53
|
+
showBoxBorder: config.showBoxBorder,
|
|
54
|
+
fillBox: config.fillBox,
|
|
55
|
+
fillBoxColor: config.fillBoxColor,
|
|
56
|
+
fillBoxGradientStart: config.fillBoxGradientStart,
|
|
57
|
+
fillBoxGradientEnd: config.fillBoxGradientEnd,
|
|
58
|
+
asciiArtGradientStart: config.asciiArtGradientStart,
|
|
59
|
+
asciiArtGradientEnd: config.asciiArtGradientEnd,
|
|
60
|
+
asciiArtColor: config.asciiArtColor
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Render Menu Title (separate from header)
|
|
66
|
+
*/
|
|
67
|
+
function renderMenuTitle(title) {
|
|
68
|
+
if (title) {
|
|
69
|
+
console.log('');
|
|
70
|
+
console.log(` ${title}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Render Main Area
|
|
75
|
+
*/
|
|
76
|
+
async function renderMainArea(config, hints) {
|
|
77
|
+
if (config.type === 'menu' && config.menu) {
|
|
78
|
+
// Menu in main area - pass hints from footer
|
|
79
|
+
const result = await api_js_1.menuAPI.radio({
|
|
80
|
+
options: config.menu.options,
|
|
81
|
+
allowLetterKeys: config.menu.allowLetterKeys ?? true,
|
|
82
|
+
allowNumberKeys: config.menu.allowNumberKeys ?? true,
|
|
83
|
+
preserveOnSelect: config.menu.preserveOnSelect ?? true,
|
|
84
|
+
preserveOnExit: config.menu.preserveOnExit ?? config.menu.preserveOnSelect ?? false,
|
|
85
|
+
onExit: config.menu.onExit
|
|
86
|
+
}, hints); // Pass hints as second parameter
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
else if (config.render) {
|
|
90
|
+
// Custom render function
|
|
91
|
+
await config.render();
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Render Footer
|
|
98
|
+
*/
|
|
99
|
+
async function renderFooterSection(config, hints) {
|
|
100
|
+
if (!config) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
let result = null;
|
|
104
|
+
// 1. Menu (if present)
|
|
105
|
+
if (config.menu) {
|
|
106
|
+
result = await api_js_1.menuAPI.radio({
|
|
107
|
+
options: config.menu.options,
|
|
108
|
+
allowLetterKeys: config.menu.allowLetterKeys ?? true,
|
|
109
|
+
allowNumberKeys: config.menu.allowNumberKeys ?? true,
|
|
110
|
+
preserveOnSelect: config.menu.preserveOnSelect ?? true,
|
|
111
|
+
preserveOnExit: config.menu.preserveOnExit ?? config.menu.preserveOnSelect ?? false,
|
|
112
|
+
onExit: config.menu.onExit
|
|
113
|
+
}, hints || config.hints);
|
|
114
|
+
}
|
|
115
|
+
// 2. Input (if present)
|
|
116
|
+
else if (config.input) {
|
|
117
|
+
result = await api_js_1.inputAPI.text({
|
|
118
|
+
prompt: config.input.prompt,
|
|
119
|
+
defaultValue: config.input.defaultValue,
|
|
120
|
+
allowEmpty: config.input.allowEmpty ?? false,
|
|
121
|
+
lang: config.input.lang,
|
|
122
|
+
preserveOnExit: config.input.preserveOnExit,
|
|
123
|
+
onExit: config.input.onExit
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
// 3. Ask (if present)
|
|
127
|
+
else if (config.ask) {
|
|
128
|
+
const askResult = await api_js_1.menuAPI.boolean({
|
|
129
|
+
question: config.ask.question,
|
|
130
|
+
helperText: config.ask.helperText,
|
|
131
|
+
defaultValue: config.ask.defaultValue ?? false,
|
|
132
|
+
orientation: config.ask.horizontal ? 'horizontal' : 'vertical',
|
|
133
|
+
preserveOnSelect: config.ask.preserveOnSelect ?? false,
|
|
134
|
+
preserveOnExit: config.ask.preserveOnExit ?? config.ask.preserveOnSelect ?? false,
|
|
135
|
+
onExit: config.ask.onExit
|
|
136
|
+
});
|
|
137
|
+
result = askResult;
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Render complete page with simple configuration API
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* const result = await renderPage({
|
|
147
|
+
* header: {
|
|
148
|
+
* type: 'full',
|
|
149
|
+
* asciiArt: ['...'],
|
|
150
|
+
* title: 'My App',
|
|
151
|
+
* description: 'Description',
|
|
152
|
+
* version: '1.0.0'
|
|
153
|
+
* },
|
|
154
|
+
* mainArea: {
|
|
155
|
+
* type: 'menu',
|
|
156
|
+
* menu: {
|
|
157
|
+
* options: ['Option 1', 'Option 2'],
|
|
158
|
+
* allowLetterKeys: true,
|
|
159
|
+
* allowNumberKeys: true
|
|
160
|
+
* }
|
|
161
|
+
* },
|
|
162
|
+
* footer: {
|
|
163
|
+
* hints: ['↑↓ Navigate', 'Enter Confirm']
|
|
164
|
+
* }
|
|
165
|
+
* });
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
async function renderPage(config) {
|
|
169
|
+
// 1. Render Header (without menuTitle)
|
|
170
|
+
renderHeaderSection(config.header);
|
|
171
|
+
// 2. Render Menu Title (if provided)
|
|
172
|
+
renderMenuTitle(config.header?.menuTitle);
|
|
173
|
+
// 3. Determine where to render hints
|
|
174
|
+
let mainResult = null;
|
|
175
|
+
if (config.footer?.menu || config.footer?.ask || config.footer?.input) {
|
|
176
|
+
// Footer has interactive element - render main area first, then footer with hints
|
|
177
|
+
if (config.mainArea.type === 'menu' && config.mainArea.menu) {
|
|
178
|
+
mainResult = await renderMainArea(config.mainArea, undefined);
|
|
179
|
+
}
|
|
180
|
+
else if (config.mainArea.render) {
|
|
181
|
+
await config.mainArea.render();
|
|
182
|
+
// Add newline after custom render to separate from footer menu
|
|
183
|
+
// This ensures the escape codes from initTerminal don't interfere
|
|
184
|
+
console.log('');
|
|
185
|
+
}
|
|
186
|
+
// Render footer with hints
|
|
187
|
+
const footerResult = await renderFooterSection(config.footer, config.footer.hints);
|
|
188
|
+
return footerResult || mainResult;
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
// Footer has no interactive element - render main area with hints from footer
|
|
192
|
+
mainResult = await renderMainArea(config.mainArea, config.footer?.hints);
|
|
193
|
+
return mainResult;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* Base input configuration
|
|
6
6
|
*/
|
|
7
7
|
export interface BaseInputConfig {
|
|
8
|
+
/** UI language (default: current i18n language) */
|
|
9
|
+
lang?: 'zh' | 'en';
|
|
8
10
|
/** Input prompt text */
|
|
9
11
|
prompt: string;
|
|
10
12
|
/** Default value */
|
|
@@ -15,6 +17,8 @@ export interface BaseInputConfig {
|
|
|
15
17
|
errorMessage?: string;
|
|
16
18
|
/** Goodbye message function */
|
|
17
19
|
onExit?: () => void;
|
|
20
|
+
/** Keep current input rendered on Ctrl+C exit instead of clearing (default: false) */
|
|
21
|
+
preserveOnExit?: boolean;
|
|
18
22
|
}
|
|
19
23
|
/**
|
|
20
24
|
* Text input configuration
|
|
@@ -58,6 +62,8 @@ export interface LanguageSelectorConfig {
|
|
|
58
62
|
prompt?: string;
|
|
59
63
|
/** Goodbye message function */
|
|
60
64
|
onExit?: () => void;
|
|
65
|
+
/** Keep current selector rendered on Ctrl+C exit instead of clearing (default: false) */
|
|
66
|
+
preserveOnExit?: boolean;
|
|
61
67
|
}
|
|
62
68
|
/**
|
|
63
69
|
* Modify field configuration
|
|
@@ -75,6 +81,8 @@ export interface ModifyFieldConfig {
|
|
|
75
81
|
validate?: (value: string) => boolean | string;
|
|
76
82
|
/** Goodbye message function */
|
|
77
83
|
onExit?: () => void;
|
|
84
|
+
/** Keep current prompt rendered on Ctrl+C exit instead of clearing (default: false) */
|
|
85
|
+
preserveOnExit?: boolean;
|
|
78
86
|
}
|
|
79
87
|
/**
|
|
80
88
|
* Input result types
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Menu component types for CLI Menu Kit
|
|
3
3
|
*/
|
|
4
|
-
import { MenuLayout } from './layout.types.js';
|
|
5
4
|
/**
|
|
6
5
|
* Menu option (can be string, object with label, or section header)
|
|
7
6
|
*/
|
|
@@ -20,10 +19,6 @@ export interface BaseMenuConfig {
|
|
|
20
19
|
title?: string;
|
|
21
20
|
/** Input prompt text */
|
|
22
21
|
prompt?: string;
|
|
23
|
-
/** Hint texts to display */
|
|
24
|
-
hints?: string[];
|
|
25
|
-
/** Layout configuration */
|
|
26
|
-
layout?: MenuLayout;
|
|
27
22
|
/** Color for highlighted items */
|
|
28
23
|
highlightColor?: string;
|
|
29
24
|
/** Goodbye message function */
|
|
@@ -32,6 +27,8 @@ export interface BaseMenuConfig {
|
|
|
32
27
|
separatorWidth?: number;
|
|
33
28
|
/** Keep menu rendered after selection instead of clearing (default: false) */
|
|
34
29
|
preserveOnSelect?: boolean;
|
|
30
|
+
/** Keep current menu rendered on Ctrl+C exit instead of clearing (default: false) */
|
|
31
|
+
preserveOnExit?: boolean;
|
|
35
32
|
}
|
|
36
33
|
/**
|
|
37
34
|
* Radio menu (single-select) configuration
|
|
@@ -69,6 +66,8 @@ export interface CheckboxMenuConfig extends BaseMenuConfig {
|
|
|
69
66
|
export interface BooleanMenuConfig extends BaseMenuConfig {
|
|
70
67
|
/** Question text */
|
|
71
68
|
question: string;
|
|
69
|
+
/** Optional helper text rendered on the next line */
|
|
70
|
+
helperText?: string;
|
|
72
71
|
/** Default value */
|
|
73
72
|
defaultValue?: boolean;
|
|
74
73
|
/** Yes text */
|
|
@@ -90,3 +89,60 @@ export interface CheckboxMenuResult {
|
|
|
90
89
|
values: string[];
|
|
91
90
|
}
|
|
92
91
|
export type BooleanMenuResult = boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Checkbox table menu configuration
|
|
94
|
+
* Combines checkbox selection with table display
|
|
95
|
+
*/
|
|
96
|
+
export interface CheckboxTableMenuConfig extends BaseMenuConfig {
|
|
97
|
+
/** Table columns definition */
|
|
98
|
+
columns: Array<{
|
|
99
|
+
header: string;
|
|
100
|
+
key: string;
|
|
101
|
+
width?: number;
|
|
102
|
+
align?: 'left' | 'center' | 'right';
|
|
103
|
+
}>;
|
|
104
|
+
/** Data rows (each row is an object with column keys) */
|
|
105
|
+
data: Record<string, any>[];
|
|
106
|
+
/** Optional: Key to use as unique identifier (default: index) */
|
|
107
|
+
idKey?: string;
|
|
108
|
+
/** Default selected row indices or IDs */
|
|
109
|
+
defaultSelected?: (number | string)[];
|
|
110
|
+
/** Minimum selections required */
|
|
111
|
+
minSelections?: number;
|
|
112
|
+
/** Maximum selections allowed */
|
|
113
|
+
maxSelections?: number;
|
|
114
|
+
/** Allow select all */
|
|
115
|
+
allowSelectAll?: boolean;
|
|
116
|
+
/** Allow invert selection */
|
|
117
|
+
allowInvert?: boolean;
|
|
118
|
+
/** Show table borders (default: false for checkbox menu style) */
|
|
119
|
+
showBorders?: boolean;
|
|
120
|
+
/** Show header separator (default: true) */
|
|
121
|
+
showHeaderSeparator?: boolean;
|
|
122
|
+
/** Phase/group separators (for grouping rows) */
|
|
123
|
+
separators?: Array<{
|
|
124
|
+
/** Insert before this row index */
|
|
125
|
+
beforeIndex: number;
|
|
126
|
+
/** Separator label */
|
|
127
|
+
label: string;
|
|
128
|
+
/** Optional description shown below the separator */
|
|
129
|
+
description?: string;
|
|
130
|
+
}>;
|
|
131
|
+
/** Separator label and description alignment (default: 'center') */
|
|
132
|
+
separatorAlign?: 'left' | 'center' | 'right';
|
|
133
|
+
/** Column width calculation mode */
|
|
134
|
+
widthMode?: 'auto' | 'fixed';
|
|
135
|
+
/** Checkbox column width (default: 4) */
|
|
136
|
+
checkboxWidth?: number;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Checkbox table menu result
|
|
140
|
+
*/
|
|
141
|
+
export interface CheckboxTableMenuResult {
|
|
142
|
+
/** Selected row indices */
|
|
143
|
+
indices: number[];
|
|
144
|
+
/** Selected row data objects */
|
|
145
|
+
rows: Record<string, any>[];
|
|
146
|
+
/** Selected IDs (if idKey is provided) */
|
|
147
|
+
ids?: (string | number)[];
|
|
148
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cli-menu-kit",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
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",
|
|
@@ -31,5 +31,8 @@
|
|
|
31
31
|
},
|
|
32
32
|
"engines": {
|
|
33
33
|
"node": ">=14.0.0"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"figlet": "^1.10.0"
|
|
34
37
|
}
|
|
35
38
|
}
|