cli-menu-kit 0.1.0
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/LICENSE +21 -0
- package/README.md +716 -0
- package/dist/api.d.ts +158 -0
- package/dist/api.js +128 -0
- package/dist/components/display/headers.d.ts +32 -0
- package/dist/components/display/headers.js +84 -0
- package/dist/components/display/index.d.ts +8 -0
- package/dist/components/display/index.js +31 -0
- package/dist/components/display/messages.d.ts +41 -0
- package/dist/components/display/messages.js +90 -0
- package/dist/components/display/progress.d.ts +41 -0
- package/dist/components/display/progress.js +82 -0
- package/dist/components/display/summary.d.ts +33 -0
- package/dist/components/display/summary.js +82 -0
- package/dist/components/inputs/index.d.ts +8 -0
- package/dist/components/inputs/index.js +15 -0
- package/dist/components/inputs/language-input.d.ts +11 -0
- package/dist/components/inputs/language-input.js +104 -0
- package/dist/components/inputs/modify-field.d.ts +11 -0
- package/dist/components/inputs/modify-field.js +42 -0
- package/dist/components/inputs/number-input.d.ts +11 -0
- package/dist/components/inputs/number-input.js +143 -0
- package/dist/components/inputs/text-input.d.ts +11 -0
- package/dist/components/inputs/text-input.js +114 -0
- package/dist/components/menus/boolean-menu.d.ts +11 -0
- package/dist/components/menus/boolean-menu.js +169 -0
- package/dist/components/menus/checkbox-menu.d.ts +11 -0
- package/dist/components/menus/checkbox-menu.js +161 -0
- package/dist/components/menus/index.d.ts +7 -0
- package/dist/components/menus/index.js +13 -0
- package/dist/components/menus/radio-menu.d.ts +11 -0
- package/dist/components/menus/radio-menu.js +158 -0
- package/dist/components.d.ts +55 -0
- package/dist/components.js +166 -0
- package/dist/core/colors.d.ts +64 -0
- package/dist/core/colors.js +139 -0
- package/dist/core/keyboard.d.ts +124 -0
- package/dist/core/keyboard.js +185 -0
- package/dist/core/renderer.d.ts +74 -0
- package/dist/core/renderer.js +217 -0
- package/dist/core/terminal.d.ts +89 -0
- package/dist/core/terminal.js +170 -0
- package/dist/features/commands.d.ts +57 -0
- package/dist/features/commands.js +161 -0
- package/dist/features/wizard.d.ts +62 -0
- package/dist/features/wizard.js +112 -0
- package/dist/i18n/languages/en.d.ts +5 -0
- package/dist/i18n/languages/en.js +54 -0
- package/dist/i18n/languages/zh.d.ts +5 -0
- package/dist/i18n/languages/zh.js +54 -0
- package/dist/i18n/registry.d.ts +36 -0
- package/dist/i18n/registry.js +82 -0
- package/dist/i18n/types.d.ts +65 -0
- package/dist/i18n/types.js +5 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +104 -0
- package/dist/input.d.ts +40 -0
- package/dist/input.js +211 -0
- package/dist/menu-core.d.ts +52 -0
- package/dist/menu-core.js +201 -0
- package/dist/menu-multi.d.ts +21 -0
- package/dist/menu-multi.js +119 -0
- package/dist/menu-single.d.ts +18 -0
- package/dist/menu-single.js +138 -0
- package/dist/menu.d.ts +66 -0
- package/dist/menu.js +78 -0
- package/dist/types/display.types.d.ts +57 -0
- package/dist/types/display.types.js +5 -0
- package/dist/types/input.types.d.ts +88 -0
- package/dist/types/input.types.js +5 -0
- package/dist/types/layout.types.d.ts +56 -0
- package/dist/types/layout.types.js +36 -0
- package/dist/types/menu.types.d.ts +85 -0
- package/dist/types/menu.types.js +5 -0
- package/dist/types.d.ts +49 -0
- package/dist/types.js +5 -0
- package/package.json +35 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Progress - Progress indicator components
|
|
4
|
+
* Displays step flow and stage information
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.renderProgressIndicator = renderProgressIndicator;
|
|
8
|
+
exports.renderStageHeader = renderStageHeader;
|
|
9
|
+
exports.renderStageSeparator = renderStageSeparator;
|
|
10
|
+
exports.createProgressIndicator = createProgressIndicator;
|
|
11
|
+
exports.createStageHeader = createStageHeader;
|
|
12
|
+
exports.createStageSeparator = createStageSeparator;
|
|
13
|
+
const terminal_js_1 = require("../../core/terminal.js");
|
|
14
|
+
const colors_js_1 = require("../../core/colors.js");
|
|
15
|
+
const terminal_js_2 = require("../../core/terminal.js");
|
|
16
|
+
/**
|
|
17
|
+
* Render a progress indicator showing steps
|
|
18
|
+
* @param config - Progress configuration
|
|
19
|
+
*/
|
|
20
|
+
function renderProgressIndicator(config) {
|
|
21
|
+
const { steps, currentStep, separator = '→' } = config;
|
|
22
|
+
const parts = [];
|
|
23
|
+
steps.forEach((step, index) => {
|
|
24
|
+
if (index === currentStep) {
|
|
25
|
+
// Current step - highlighted
|
|
26
|
+
parts.push(`${colors_js_1.colors.cyan}${step}${colors_js_1.colors.reset}`);
|
|
27
|
+
}
|
|
28
|
+
else if (index < currentStep) {
|
|
29
|
+
// Completed step - normal
|
|
30
|
+
parts.push(step);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
// Future step - dimmed
|
|
34
|
+
parts.push(`${colors_js_1.colors.dim}${step}${colors_js_1.colors.reset}`);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
const progressLine = ` ${parts.join(` ${colors_js_1.colors.dim}${separator}${colors_js_1.colors.reset} `)}`;
|
|
38
|
+
(0, terminal_js_1.writeLine)(progressLine);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Render a stage header with step number
|
|
42
|
+
* @param stageName - Name of the stage
|
|
43
|
+
* @param stepNumber - Step number (1-based)
|
|
44
|
+
*/
|
|
45
|
+
function renderStageHeader(stageName, stepNumber) {
|
|
46
|
+
(0, terminal_js_1.writeLine)(` ${colors_js_1.colors.cyan}步骤${stepNumber}: ${stageName}${colors_js_1.colors.reset}`);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Render a stage separator
|
|
50
|
+
* @param char - Character to use for separator
|
|
51
|
+
* @param width - Width of separator (default: terminal width)
|
|
52
|
+
*/
|
|
53
|
+
function renderStageSeparator(char = '─', width) {
|
|
54
|
+
const termWidth = (0, terminal_js_2.getTerminalWidth)();
|
|
55
|
+
const sepWidth = width || termWidth;
|
|
56
|
+
(0, terminal_js_1.writeLine)(colors_js_1.colors.dim + char.repeat(sepWidth) + colors_js_1.colors.reset);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Create a progress indicator
|
|
60
|
+
* @param steps - Array of step names
|
|
61
|
+
* @param currentStep - Index of current step (0-based)
|
|
62
|
+
* @param separator - Optional separator character
|
|
63
|
+
*/
|
|
64
|
+
function createProgressIndicator(steps, currentStep, separator) {
|
|
65
|
+
renderProgressIndicator({ steps, currentStep, separator });
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Create a stage header
|
|
69
|
+
* @param stageName - Name of the stage
|
|
70
|
+
* @param stepNumber - Step number (1-based)
|
|
71
|
+
*/
|
|
72
|
+
function createStageHeader(stageName, stepNumber) {
|
|
73
|
+
renderStageHeader(stageName, stepNumber);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Create a stage separator
|
|
77
|
+
* @param char - Optional separator character
|
|
78
|
+
* @param width - Optional width
|
|
79
|
+
*/
|
|
80
|
+
function createStageSeparator(char, width) {
|
|
81
|
+
renderStageSeparator(char, width);
|
|
82
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Summary - Summary table component
|
|
3
|
+
* Displays statistics and information in a bordered box
|
|
4
|
+
*/
|
|
5
|
+
import { SummaryTableConfig } from '../../types/display.types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Render a summary table
|
|
8
|
+
* @param config - Summary table configuration
|
|
9
|
+
*/
|
|
10
|
+
export declare function renderSummaryTable(config: SummaryTableConfig): void;
|
|
11
|
+
/**
|
|
12
|
+
* Create a summary table
|
|
13
|
+
* @param title - Optional title
|
|
14
|
+
* @param sections - Array of sections with items
|
|
15
|
+
* @param width - Optional width
|
|
16
|
+
*/
|
|
17
|
+
export declare function createSummaryTable(title: string | undefined, sections: Array<{
|
|
18
|
+
header?: string;
|
|
19
|
+
items: Array<{
|
|
20
|
+
key: string;
|
|
21
|
+
value: string;
|
|
22
|
+
}>;
|
|
23
|
+
}>, width?: number): void;
|
|
24
|
+
/**
|
|
25
|
+
* Create a simple summary table with one section
|
|
26
|
+
* @param title - Optional title
|
|
27
|
+
* @param items - Key-value pairs
|
|
28
|
+
* @param width - Optional width
|
|
29
|
+
*/
|
|
30
|
+
export declare function createSimpleSummary(title: string | undefined, items: Array<{
|
|
31
|
+
key: string;
|
|
32
|
+
value: string;
|
|
33
|
+
}>, width?: number): void;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Summary - Summary table component
|
|
4
|
+
* Displays statistics and information in a bordered box
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.renderSummaryTable = renderSummaryTable;
|
|
8
|
+
exports.createSummaryTable = createSummaryTable;
|
|
9
|
+
exports.createSimpleSummary = createSimpleSummary;
|
|
10
|
+
const terminal_js_1 = require("../../core/terminal.js");
|
|
11
|
+
const colors_js_1 = require("../../core/colors.js");
|
|
12
|
+
const terminal_js_2 = require("../../core/terminal.js");
|
|
13
|
+
/**
|
|
14
|
+
* Render a summary table
|
|
15
|
+
* @param config - Summary table configuration
|
|
16
|
+
*/
|
|
17
|
+
function renderSummaryTable(config) {
|
|
18
|
+
const { title, sections, width } = config;
|
|
19
|
+
const termWidth = (0, terminal_js_2.getTerminalWidth)();
|
|
20
|
+
const boxWidth = width || Math.min(termWidth - 4, 60);
|
|
21
|
+
// Calculate content width (excluding borders and padding)
|
|
22
|
+
const contentWidth = boxWidth - 4;
|
|
23
|
+
// Top border
|
|
24
|
+
(0, terminal_js_1.writeLine)(`╭${'─'.repeat(boxWidth - 2)}╮`);
|
|
25
|
+
// Empty line
|
|
26
|
+
(0, terminal_js_1.writeLine)(`│${' '.repeat(boxWidth - 2)}│`);
|
|
27
|
+
// Title if provided
|
|
28
|
+
if (title) {
|
|
29
|
+
const titlePadding = Math.floor((contentWidth - title.length) / 2);
|
|
30
|
+
const titleLine = ' '.repeat(titlePadding + 2) + colors_js_1.colors.cyan + title + colors_js_1.colors.reset;
|
|
31
|
+
const remainingSpace = boxWidth - titlePadding - title.length - 4;
|
|
32
|
+
(0, terminal_js_1.writeLine)(`│${titleLine}${' '.repeat(remainingSpace)}│`);
|
|
33
|
+
(0, terminal_js_1.writeLine)(`│${' '.repeat(boxWidth - 2)}│`);
|
|
34
|
+
}
|
|
35
|
+
// Sections
|
|
36
|
+
sections.forEach((section, sectionIndex) => {
|
|
37
|
+
// Section header if provided
|
|
38
|
+
if (section.header) {
|
|
39
|
+
const headerLine = ` ${colors_js_1.colors.brightCyan}${section.header}${colors_js_1.colors.reset}`;
|
|
40
|
+
const remainingSpace = boxWidth - section.header.length - 4;
|
|
41
|
+
(0, terminal_js_1.writeLine)(`│${headerLine}${' '.repeat(remainingSpace)}│`);
|
|
42
|
+
}
|
|
43
|
+
// Section items
|
|
44
|
+
section.items.forEach(item => {
|
|
45
|
+
const itemLine = ` ${item.key}:${' '.repeat(Math.max(1, 15 - item.key.length))}${item.value}`;
|
|
46
|
+
// Remove ANSI codes for length calculation
|
|
47
|
+
const plainItemLine = itemLine.replace(/\x1b\[[0-9;]*m/g, '');
|
|
48
|
+
const remainingSpace = boxWidth - plainItemLine.length - 2;
|
|
49
|
+
(0, terminal_js_1.writeLine)(`│${itemLine}${' '.repeat(Math.max(0, remainingSpace))}│`);
|
|
50
|
+
});
|
|
51
|
+
// Add spacing between sections (except after last section)
|
|
52
|
+
if (sectionIndex < sections.length - 1) {
|
|
53
|
+
(0, terminal_js_1.writeLine)(`│${' '.repeat(boxWidth - 2)}│`);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
// Empty line
|
|
57
|
+
(0, terminal_js_1.writeLine)(`│${' '.repeat(boxWidth - 2)}│`);
|
|
58
|
+
// Bottom border
|
|
59
|
+
(0, terminal_js_1.writeLine)(`╰${'─'.repeat(boxWidth - 2)}╯`);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Create a summary table
|
|
63
|
+
* @param title - Optional title
|
|
64
|
+
* @param sections - Array of sections with items
|
|
65
|
+
* @param width - Optional width
|
|
66
|
+
*/
|
|
67
|
+
function createSummaryTable(title, sections, width) {
|
|
68
|
+
renderSummaryTable({ title, sections, width });
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Create a simple summary table with one section
|
|
72
|
+
* @param title - Optional title
|
|
73
|
+
* @param items - Key-value pairs
|
|
74
|
+
* @param width - Optional width
|
|
75
|
+
*/
|
|
76
|
+
function createSimpleSummary(title, items, width) {
|
|
77
|
+
renderSummaryTable({
|
|
78
|
+
title,
|
|
79
|
+
sections: [{ items }],
|
|
80
|
+
width
|
|
81
|
+
});
|
|
82
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input components index
|
|
3
|
+
* Exports all input component functions
|
|
4
|
+
*/
|
|
5
|
+
export { showTextInput } from './text-input.js';
|
|
6
|
+
export { showNumberInput } from './number-input.js';
|
|
7
|
+
export { showLanguageSelector } from './language-input.js';
|
|
8
|
+
export { showModifyField } from './modify-field.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Input components index
|
|
4
|
+
* Exports all input component functions
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.showModifyField = exports.showLanguageSelector = exports.showNumberInput = exports.showTextInput = void 0;
|
|
8
|
+
var text_input_js_1 = require("./text-input.js");
|
|
9
|
+
Object.defineProperty(exports, "showTextInput", { enumerable: true, get: function () { return text_input_js_1.showTextInput; } });
|
|
10
|
+
var number_input_js_1 = require("./number-input.js");
|
|
11
|
+
Object.defineProperty(exports, "showNumberInput", { enumerable: true, get: function () { return number_input_js_1.showNumberInput; } });
|
|
12
|
+
var language_input_js_1 = require("./language-input.js");
|
|
13
|
+
Object.defineProperty(exports, "showLanguageSelector", { enumerable: true, get: function () { return language_input_js_1.showLanguageSelector; } });
|
|
14
|
+
var modify_field_js_1 = require("./modify-field.js");
|
|
15
|
+
Object.defineProperty(exports, "showModifyField", { enumerable: true, get: function () { return modify_field_js_1.showModifyField; } });
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LanguageSelector - Language selection component
|
|
3
|
+
* Specialized selector for choosing a language
|
|
4
|
+
*/
|
|
5
|
+
import { LanguageSelectorConfig, LanguageSelectorResult } from '../../types/input.types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Show a language selector
|
|
8
|
+
* @param config - Selector configuration
|
|
9
|
+
* @returns Promise resolving to selected language code
|
|
10
|
+
*/
|
|
11
|
+
export declare function showLanguageSelector(config: LanguageSelectorConfig): Promise<LanguageSelectorResult>;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* LanguageSelector - Language selection component
|
|
4
|
+
* Specialized selector for choosing a language
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.showLanguageSelector = showLanguageSelector;
|
|
8
|
+
const terminal_js_1 = require("../../core/terminal.js");
|
|
9
|
+
const keyboard_js_1 = require("../../core/keyboard.js");
|
|
10
|
+
const renderer_js_1 = require("../../core/renderer.js");
|
|
11
|
+
const colors_js_1 = require("../../core/colors.js");
|
|
12
|
+
/**
|
|
13
|
+
* Show a language selector
|
|
14
|
+
* @param config - Selector configuration
|
|
15
|
+
* @returns Promise resolving to selected language code
|
|
16
|
+
*/
|
|
17
|
+
async function showLanguageSelector(config) {
|
|
18
|
+
const { languages, defaultLanguage, prompt = '选择语言 / Select Language', onExit } = config;
|
|
19
|
+
// Validate languages
|
|
20
|
+
if (!languages || languages.length === 0) {
|
|
21
|
+
throw new Error('LanguageSelector requires at least one language');
|
|
22
|
+
}
|
|
23
|
+
// Find default index
|
|
24
|
+
let selectedIndex = 0;
|
|
25
|
+
if (defaultLanguage) {
|
|
26
|
+
const index = languages.findIndex(lang => lang.code === defaultLanguage);
|
|
27
|
+
if (index !== -1) {
|
|
28
|
+
selectedIndex = index;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const state = (0, terminal_js_1.initTerminal)();
|
|
32
|
+
const render = () => {
|
|
33
|
+
(0, terminal_js_1.clearMenu)(state);
|
|
34
|
+
let lineCount = 0;
|
|
35
|
+
// Render header
|
|
36
|
+
(0, renderer_js_1.renderHeader)(` ${prompt}`, colors_js_1.colors.cyan);
|
|
37
|
+
lineCount++;
|
|
38
|
+
(0, renderer_js_1.renderBlankLines)(1);
|
|
39
|
+
lineCount++;
|
|
40
|
+
// Render language options
|
|
41
|
+
languages.forEach((lang, index) => {
|
|
42
|
+
const displayText = lang.nativeName
|
|
43
|
+
? `${lang.name} (${lang.nativeName})`
|
|
44
|
+
: lang.name;
|
|
45
|
+
(0, renderer_js_1.renderOption)(displayText, false, index === selectedIndex, `${index + 1}. `);
|
|
46
|
+
lineCount++;
|
|
47
|
+
});
|
|
48
|
+
// Render hints
|
|
49
|
+
(0, renderer_js_1.renderBlankLines)(1);
|
|
50
|
+
lineCount++;
|
|
51
|
+
(0, renderer_js_1.renderHints)(['↑↓ 方向键', '1-9 输入序号', '⏎ 确认']);
|
|
52
|
+
lineCount++;
|
|
53
|
+
state.renderedLines = lineCount;
|
|
54
|
+
};
|
|
55
|
+
// Initial render
|
|
56
|
+
render();
|
|
57
|
+
// Handle keyboard input
|
|
58
|
+
return new Promise((resolve) => {
|
|
59
|
+
const onData = (key) => {
|
|
60
|
+
// Handle Ctrl+C
|
|
61
|
+
if ((0, keyboard_js_1.isCtrlC)(key)) {
|
|
62
|
+
state.stdin.removeListener('data', onData);
|
|
63
|
+
(0, terminal_js_1.clearMenu)(state);
|
|
64
|
+
(0, terminal_js_1.restoreTerminal)(state);
|
|
65
|
+
if (onExit) {
|
|
66
|
+
onExit();
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
console.log('\n👋 再见!');
|
|
70
|
+
}
|
|
71
|
+
process.exit(0);
|
|
72
|
+
}
|
|
73
|
+
// Handle Enter
|
|
74
|
+
if ((0, keyboard_js_1.isEnter)(key)) {
|
|
75
|
+
state.stdin.removeListener('data', onData);
|
|
76
|
+
(0, terminal_js_1.clearMenu)(state);
|
|
77
|
+
(0, terminal_js_1.restoreTerminal)(state);
|
|
78
|
+
resolve(languages[selectedIndex].code);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// Handle arrow keys
|
|
82
|
+
if (key === keyboard_js_1.KEY_CODES.UP) {
|
|
83
|
+
selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : languages.length - 1;
|
|
84
|
+
render();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (key === keyboard_js_1.KEY_CODES.DOWN) {
|
|
88
|
+
selectedIndex = selectedIndex < languages.length - 1 ? selectedIndex + 1 : 0;
|
|
89
|
+
render();
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
// Handle number keys
|
|
93
|
+
if ((0, keyboard_js_1.isNumberKey)(key)) {
|
|
94
|
+
const num = parseInt(key, 10);
|
|
95
|
+
if (num > 0 && num <= languages.length) {
|
|
96
|
+
selectedIndex = num - 1;
|
|
97
|
+
render();
|
|
98
|
+
}
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
state.stdin.on('data', onData);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ModifyField - Field modification composite component
|
|
3
|
+
* Shows current value and asks if user wants to modify it
|
|
4
|
+
*/
|
|
5
|
+
import { ModifyFieldConfig, ModifyFieldResult } from '../../types/input.types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Show a modify field prompt
|
|
8
|
+
* @param config - Field configuration
|
|
9
|
+
* @returns Promise resolving to modification result
|
|
10
|
+
*/
|
|
11
|
+
export declare function showModifyField(config: ModifyFieldConfig): Promise<ModifyFieldResult>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ModifyField - Field modification composite component
|
|
4
|
+
* Shows current value and asks if user wants to modify it
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.showModifyField = showModifyField;
|
|
8
|
+
const boolean_menu_js_1 = require("../menus/boolean-menu.js");
|
|
9
|
+
const text_input_js_1 = require("./text-input.js");
|
|
10
|
+
/**
|
|
11
|
+
* Show a modify field prompt
|
|
12
|
+
* @param config - Field configuration
|
|
13
|
+
* @returns Promise resolving to modification result
|
|
14
|
+
*/
|
|
15
|
+
async function showModifyField(config) {
|
|
16
|
+
const { fieldName, currentValue, modifyPrompt = `是否修改 ${fieldName}? (当前: ${currentValue})`, newValuePrompt = `请输入新的 ${fieldName}`, validate, onExit } = config;
|
|
17
|
+
// Ask if user wants to modify
|
|
18
|
+
const shouldModify = await (0, boolean_menu_js_1.showBooleanMenu)({
|
|
19
|
+
question: modifyPrompt,
|
|
20
|
+
orientation: 'horizontal',
|
|
21
|
+
defaultValue: false,
|
|
22
|
+
onExit
|
|
23
|
+
});
|
|
24
|
+
// If user doesn't want to modify, return current value
|
|
25
|
+
if (!shouldModify) {
|
|
26
|
+
return {
|
|
27
|
+
modified: false,
|
|
28
|
+
value: currentValue
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
// Get new value
|
|
32
|
+
const newValue = await (0, text_input_js_1.showTextInput)({
|
|
33
|
+
prompt: newValuePrompt,
|
|
34
|
+
defaultValue: currentValue,
|
|
35
|
+
validate,
|
|
36
|
+
onExit
|
|
37
|
+
});
|
|
38
|
+
return {
|
|
39
|
+
modified: true,
|
|
40
|
+
value: newValue
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NumberInput - Numeric input component
|
|
3
|
+
* Allows user to enter numbers with validation
|
|
4
|
+
*/
|
|
5
|
+
import { NumberInputConfig, NumberInputResult } from '../../types/input.types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Show a number input prompt
|
|
8
|
+
* @param config - Input configuration
|
|
9
|
+
* @returns Promise resolving to entered number
|
|
10
|
+
*/
|
|
11
|
+
export declare function showNumberInput(config: NumberInputConfig): Promise<NumberInputResult>;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* NumberInput - Numeric input component
|
|
4
|
+
* Allows user to enter numbers with validation
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.showNumberInput = showNumberInput;
|
|
8
|
+
const terminal_js_1 = require("../../core/terminal.js");
|
|
9
|
+
const keyboard_js_1 = require("../../core/keyboard.js");
|
|
10
|
+
const colors_js_1 = require("../../core/colors.js");
|
|
11
|
+
/**
|
|
12
|
+
* Show a number input prompt
|
|
13
|
+
* @param config - Input configuration
|
|
14
|
+
* @returns Promise resolving to entered number
|
|
15
|
+
*/
|
|
16
|
+
async function showNumberInput(config) {
|
|
17
|
+
const { prompt, defaultValue, min, max, allowDecimals = false, allowNegative = false, validate, errorMessage, onExit } = config;
|
|
18
|
+
const state = (0, terminal_js_1.initTerminal)();
|
|
19
|
+
let inputValue = '';
|
|
20
|
+
let errorMsg = '';
|
|
21
|
+
const render = () => {
|
|
22
|
+
(0, terminal_js_1.clearMenu)(state);
|
|
23
|
+
let lineCount = 0;
|
|
24
|
+
// Render prompt with constraints
|
|
25
|
+
let promptLine = ` ${prompt}`;
|
|
26
|
+
if (min !== undefined || max !== undefined) {
|
|
27
|
+
const constraints = [];
|
|
28
|
+
if (min !== undefined)
|
|
29
|
+
constraints.push(`最小: ${min}`);
|
|
30
|
+
if (max !== undefined)
|
|
31
|
+
constraints.push(`最大: ${max}`);
|
|
32
|
+
promptLine += ` ${colors_js_1.colors.dim}(${constraints.join(', ')})${colors_js_1.colors.reset}`;
|
|
33
|
+
}
|
|
34
|
+
if (defaultValue !== undefined && !inputValue) {
|
|
35
|
+
promptLine += ` ${colors_js_1.colors.dim}(默认: ${defaultValue})${colors_js_1.colors.reset}`;
|
|
36
|
+
}
|
|
37
|
+
promptLine += `: ${colors_js_1.colors.cyan}${inputValue}_${colors_js_1.colors.reset}`;
|
|
38
|
+
(0, terminal_js_1.writeLine)(promptLine);
|
|
39
|
+
lineCount++;
|
|
40
|
+
// Render error message if any
|
|
41
|
+
if (errorMsg) {
|
|
42
|
+
(0, terminal_js_1.writeLine)(` ${colors_js_1.colors.red}✗ ${errorMsg}${colors_js_1.colors.reset}`);
|
|
43
|
+
lineCount++;
|
|
44
|
+
}
|
|
45
|
+
state.renderedLines = lineCount;
|
|
46
|
+
};
|
|
47
|
+
// Initial render
|
|
48
|
+
render();
|
|
49
|
+
// Handle keyboard input
|
|
50
|
+
return new Promise((resolve) => {
|
|
51
|
+
const onData = (key) => {
|
|
52
|
+
// Handle Ctrl+C
|
|
53
|
+
if ((0, keyboard_js_1.isCtrlC)(key)) {
|
|
54
|
+
state.stdin.removeListener('data', onData);
|
|
55
|
+
(0, terminal_js_1.clearMenu)(state);
|
|
56
|
+
(0, terminal_js_1.restoreTerminal)(state);
|
|
57
|
+
if (onExit) {
|
|
58
|
+
onExit();
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
console.log('\n👋 再见!');
|
|
62
|
+
}
|
|
63
|
+
process.exit(0);
|
|
64
|
+
}
|
|
65
|
+
// Handle Enter
|
|
66
|
+
if ((0, keyboard_js_1.isEnter)(key)) {
|
|
67
|
+
const finalValue = inputValue || (defaultValue !== undefined ? String(defaultValue) : '');
|
|
68
|
+
// Check if empty
|
|
69
|
+
if (!finalValue) {
|
|
70
|
+
errorMsg = errorMessage || '请输入数字';
|
|
71
|
+
render();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// Parse number
|
|
75
|
+
const num = allowDecimals ? parseFloat(finalValue) : parseInt(finalValue, 10);
|
|
76
|
+
// Check if valid number
|
|
77
|
+
if (isNaN(num)) {
|
|
78
|
+
errorMsg = errorMessage || '输入必须是有效的数字';
|
|
79
|
+
render();
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
// Check min
|
|
83
|
+
if (min !== undefined && num < min) {
|
|
84
|
+
errorMsg = errorMessage || `数字不能小于 ${min}`;
|
|
85
|
+
render();
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
// Check max
|
|
89
|
+
if (max !== undefined && num > max) {
|
|
90
|
+
errorMsg = errorMessage || `数字不能大于 ${max}`;
|
|
91
|
+
render();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
// Custom validation
|
|
95
|
+
if (validate) {
|
|
96
|
+
const validationResult = validate(String(num));
|
|
97
|
+
if (validationResult !== true) {
|
|
98
|
+
errorMsg = typeof validationResult === 'string' ? validationResult : (errorMessage || '输入无效');
|
|
99
|
+
render();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Success
|
|
104
|
+
state.stdin.removeListener('data', onData);
|
|
105
|
+
(0, terminal_js_1.clearMenu)(state);
|
|
106
|
+
(0, terminal_js_1.restoreTerminal)(state);
|
|
107
|
+
resolve(num);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
// Handle Backspace
|
|
111
|
+
if ((0, keyboard_js_1.isBackspace)(key)) {
|
|
112
|
+
if (inputValue.length > 0) {
|
|
113
|
+
inputValue = inputValue.slice(0, -1);
|
|
114
|
+
errorMsg = ''; // Clear error on edit
|
|
115
|
+
render();
|
|
116
|
+
}
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
// Handle number keys
|
|
120
|
+
if ((0, keyboard_js_1.isNumberKey)(key)) {
|
|
121
|
+
inputValue += key;
|
|
122
|
+
errorMsg = ''; // Clear error on edit
|
|
123
|
+
render();
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
// Handle decimal point
|
|
127
|
+
if (allowDecimals && key === '.' && !inputValue.includes('.')) {
|
|
128
|
+
inputValue += key;
|
|
129
|
+
errorMsg = ''; // Clear error on edit
|
|
130
|
+
render();
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
// Handle negative sign
|
|
134
|
+
if (allowNegative && key === '-' && inputValue.length === 0) {
|
|
135
|
+
inputValue += key;
|
|
136
|
+
errorMsg = ''; // Clear error on edit
|
|
137
|
+
render();
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
state.stdin.on('data', onData);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TextInput - Single-line text input component
|
|
3
|
+
* Allows user to enter text with validation
|
|
4
|
+
*/
|
|
5
|
+
import { TextInputConfig, TextInputResult } from '../../types/input.types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Show a text input prompt
|
|
8
|
+
* @param config - Input configuration
|
|
9
|
+
* @returns Promise resolving to entered text
|
|
10
|
+
*/
|
|
11
|
+
export declare function showTextInput(config: TextInputConfig): Promise<TextInputResult>;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* TextInput - Single-line text input component
|
|
4
|
+
* Allows user to enter text with validation
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.showTextInput = showTextInput;
|
|
8
|
+
const terminal_js_1 = require("../../core/terminal.js");
|
|
9
|
+
const keyboard_js_1 = require("../../core/keyboard.js");
|
|
10
|
+
const colors_js_1 = require("../../core/colors.js");
|
|
11
|
+
/**
|
|
12
|
+
* Show a text input prompt
|
|
13
|
+
* @param config - Input configuration
|
|
14
|
+
* @returns Promise resolving to entered text
|
|
15
|
+
*/
|
|
16
|
+
async function showTextInput(config) {
|
|
17
|
+
const { prompt, defaultValue = '', placeholder, maxLength, minLength = 0, allowEmpty = false, validate, errorMessage, onExit } = config;
|
|
18
|
+
const state = (0, terminal_js_1.initTerminal)();
|
|
19
|
+
let inputValue = '';
|
|
20
|
+
let errorMsg = '';
|
|
21
|
+
const render = () => {
|
|
22
|
+
(0, terminal_js_1.clearMenu)(state);
|
|
23
|
+
let lineCount = 0;
|
|
24
|
+
// Render prompt with default value hint
|
|
25
|
+
let promptLine = ` ${prompt}`;
|
|
26
|
+
if (defaultValue && !inputValue) {
|
|
27
|
+
promptLine += ` ${colors_js_1.colors.dim}(默认: ${defaultValue})${colors_js_1.colors.reset}`;
|
|
28
|
+
}
|
|
29
|
+
if (placeholder && !inputValue) {
|
|
30
|
+
promptLine += ` ${colors_js_1.colors.dim}${placeholder}${colors_js_1.colors.reset}`;
|
|
31
|
+
}
|
|
32
|
+
promptLine += `: ${colors_js_1.colors.cyan}${inputValue}_${colors_js_1.colors.reset}`;
|
|
33
|
+
(0, terminal_js_1.writeLine)(promptLine);
|
|
34
|
+
lineCount++;
|
|
35
|
+
// Render error message if any
|
|
36
|
+
if (errorMsg) {
|
|
37
|
+
(0, terminal_js_1.writeLine)(` ${colors_js_1.colors.red}✗ ${errorMsg}${colors_js_1.colors.reset}`);
|
|
38
|
+
lineCount++;
|
|
39
|
+
}
|
|
40
|
+
state.renderedLines = lineCount;
|
|
41
|
+
};
|
|
42
|
+
// Initial render
|
|
43
|
+
render();
|
|
44
|
+
// Handle keyboard input
|
|
45
|
+
return new Promise((resolve) => {
|
|
46
|
+
const onData = (key) => {
|
|
47
|
+
// Handle Ctrl+C
|
|
48
|
+
if ((0, keyboard_js_1.isCtrlC)(key)) {
|
|
49
|
+
state.stdin.removeListener('data', onData);
|
|
50
|
+
(0, terminal_js_1.clearMenu)(state);
|
|
51
|
+
(0, terminal_js_1.restoreTerminal)(state);
|
|
52
|
+
if (onExit) {
|
|
53
|
+
onExit();
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
console.log('\n👋 再见!');
|
|
57
|
+
}
|
|
58
|
+
process.exit(0);
|
|
59
|
+
}
|
|
60
|
+
// Handle Enter
|
|
61
|
+
if ((0, keyboard_js_1.isEnter)(key)) {
|
|
62
|
+
const finalValue = inputValue || defaultValue;
|
|
63
|
+
// Check if empty
|
|
64
|
+
if (!allowEmpty && !finalValue) {
|
|
65
|
+
errorMsg = errorMessage || '输入不能为空';
|
|
66
|
+
render();
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
// Check min length
|
|
70
|
+
if (minLength && finalValue.length < minLength) {
|
|
71
|
+
errorMsg = errorMessage || `输入长度不能少于 ${minLength} 个字符`;
|
|
72
|
+
render();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
// Custom validation
|
|
76
|
+
if (validate) {
|
|
77
|
+
const validationResult = validate(finalValue);
|
|
78
|
+
if (validationResult !== true) {
|
|
79
|
+
errorMsg = typeof validationResult === 'string' ? validationResult : (errorMessage || '输入无效');
|
|
80
|
+
render();
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Success
|
|
85
|
+
state.stdin.removeListener('data', onData);
|
|
86
|
+
(0, terminal_js_1.clearMenu)(state);
|
|
87
|
+
(0, terminal_js_1.restoreTerminal)(state);
|
|
88
|
+
resolve(finalValue);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
// Handle Backspace
|
|
92
|
+
if ((0, keyboard_js_1.isBackspace)(key)) {
|
|
93
|
+
if (inputValue.length > 0) {
|
|
94
|
+
inputValue = inputValue.slice(0, -1);
|
|
95
|
+
errorMsg = ''; // Clear error on edit
|
|
96
|
+
render();
|
|
97
|
+
}
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
// Handle printable characters
|
|
101
|
+
if ((0, keyboard_js_1.isPrintable)(key)) {
|
|
102
|
+
// Check max length
|
|
103
|
+
if (maxLength && inputValue.length >= maxLength) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
inputValue += key;
|
|
107
|
+
errorMsg = ''; // Clear error on edit
|
|
108
|
+
render();
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
state.stdin.on('data', onData);
|
|
113
|
+
});
|
|
114
|
+
}
|