cli-menu-kit 0.1.26 → 0.2.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/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/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 +3 -0
- package/dist/components/display/index.js +15 -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/table.d.ts +42 -0
- package/dist/components/display/table.js +107 -0
- package/dist/components/menus/boolean-menu.js +2 -1
- package/dist/components/menus/checkbox-menu.d.ts +2 -1
- package/dist/components/menus/checkbox-menu.js +30 -59
- package/dist/components/menus/checkbox-table-menu.d.ts +12 -0
- package/dist/components/menus/checkbox-table-menu.js +395 -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 +33 -0
- package/dist/components/menus/radio-menu-split.js +248 -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 +60 -123
- 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 +22 -6
- 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 +4 -1
- package/dist/core/terminal.js +37 -4
- package/dist/core/virtual-scroll.d.ts +65 -0
- package/dist/core/virtual-scroll.js +120 -0
- 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 +5 -4
- package/dist/index.js +30 -4
- package/dist/layout.d.ts +68 -0
- package/dist/layout.js +134 -0
- package/dist/page-layout.d.ts +92 -0
- package/dist/page-layout.js +156 -0
- package/dist/types/menu.types.d.ts +57 -5
- package/package.json +1 -1
- package/dist/types/layout.types.d.ts +0 -56
- package/dist/types/layout.types.js +0 -36
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Virtual scrolling utilities for rendering large lists efficiently
|
|
4
|
+
* by only displaying a visible window of items.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.calculateVirtualScroll = calculateVirtualScroll;
|
|
8
|
+
/**
|
|
9
|
+
* Calculate visible range for virtual scrolling based on line count.
|
|
10
|
+
*
|
|
11
|
+
* This function maintains a stable viewport height by calculating which items
|
|
12
|
+
* to display based on their actual line count, not just item count. This prevents
|
|
13
|
+
* height jumping when items have varying heights (e.g., separators with descriptions).
|
|
14
|
+
*
|
|
15
|
+
* Algorithm:
|
|
16
|
+
* 1. Calculate total lines needed for all items
|
|
17
|
+
* 2. If total <= targetLines, show everything (no scrolling)
|
|
18
|
+
* 3. Otherwise, create a window centered on cursor:
|
|
19
|
+
* - Start from cursor position
|
|
20
|
+
* - Expand downward until reaching target or end
|
|
21
|
+
* - Expand upward to fill remaining space
|
|
22
|
+
* - Expand downward again if space remains
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const result = calculateVirtualScroll({
|
|
27
|
+
* items: menuOptions,
|
|
28
|
+
* cursorIndex: 10,
|
|
29
|
+
* targetLines: 30,
|
|
30
|
+
* getItemLineCount: (item, index) => {
|
|
31
|
+
* if (item.type === 'separator') {
|
|
32
|
+
* return 1 + (item.description ? 1 : 0) + (index > 0 ? 1 : 0);
|
|
33
|
+
* }
|
|
34
|
+
* return 1;
|
|
35
|
+
* }
|
|
36
|
+
* });
|
|
37
|
+
*
|
|
38
|
+
* // Render only visible items
|
|
39
|
+
* for (let i = result.visibleStart; i < result.visibleEnd; i++) {
|
|
40
|
+
* renderItem(items[i]);
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
function calculateVirtualScroll(options) {
|
|
45
|
+
const { items, cursorIndex, targetLines, getItemLineCount } = options;
|
|
46
|
+
// Validate inputs
|
|
47
|
+
if (items.length === 0) {
|
|
48
|
+
return {
|
|
49
|
+
visibleStart: 0,
|
|
50
|
+
visibleEnd: 0,
|
|
51
|
+
actualLines: 0,
|
|
52
|
+
isScrolled: false,
|
|
53
|
+
hasItemsBefore: false,
|
|
54
|
+
hasItemsAfter: false
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (cursorIndex < 0 || cursorIndex >= items.length) {
|
|
58
|
+
throw new Error(`cursorIndex ${cursorIndex} is out of bounds [0, ${items.length})`);
|
|
59
|
+
}
|
|
60
|
+
// Calculate total lines for all items
|
|
61
|
+
const estimatedTotalLines = items.reduce((sum, item, idx) => {
|
|
62
|
+
return sum + getItemLineCount(item, idx);
|
|
63
|
+
}, 0);
|
|
64
|
+
// If content fits within target, show everything
|
|
65
|
+
if (estimatedTotalLines <= targetLines) {
|
|
66
|
+
return {
|
|
67
|
+
visibleStart: 0,
|
|
68
|
+
visibleEnd: items.length,
|
|
69
|
+
actualLines: estimatedTotalLines,
|
|
70
|
+
isScrolled: false,
|
|
71
|
+
hasItemsBefore: false,
|
|
72
|
+
hasItemsAfter: false
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// Virtual scrolling: maintain constant line count
|
|
76
|
+
let visibleStart = cursorIndex;
|
|
77
|
+
let visibleEnd = cursorIndex + 1;
|
|
78
|
+
let currentLines = getItemLineCount(items[cursorIndex], cursorIndex);
|
|
79
|
+
// Phase 1: Expand downward from cursor
|
|
80
|
+
while (visibleEnd < items.length && currentLines < targetLines) {
|
|
81
|
+
const nextLines = getItemLineCount(items[visibleEnd], visibleEnd);
|
|
82
|
+
if (currentLines + nextLines <= targetLines) {
|
|
83
|
+
currentLines += nextLines;
|
|
84
|
+
visibleEnd++;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Phase 2: Expand upward to fill remaining space
|
|
91
|
+
while (visibleStart > 0 && currentLines < targetLines) {
|
|
92
|
+
const prevLines = getItemLineCount(items[visibleStart - 1], visibleStart - 1);
|
|
93
|
+
if (currentLines + prevLines <= targetLines) {
|
|
94
|
+
visibleStart--;
|
|
95
|
+
currentLines += prevLines;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Phase 3: Try expanding downward again if space remains
|
|
102
|
+
while (visibleEnd < items.length && currentLines < targetLines) {
|
|
103
|
+
const nextLines = getItemLineCount(items[visibleEnd], visibleEnd);
|
|
104
|
+
if (currentLines + nextLines <= targetLines) {
|
|
105
|
+
currentLines += nextLines;
|
|
106
|
+
visibleEnd++;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
visibleStart,
|
|
114
|
+
visibleEnd,
|
|
115
|
+
actualLines: currentLines,
|
|
116
|
+
isScrolled: true,
|
|
117
|
+
hasItemsBefore: visibleStart > 0,
|
|
118
|
+
hasItemsAfter: visibleEnd < items.length
|
|
119
|
+
};
|
|
120
|
+
}
|
|
@@ -9,7 +9,10 @@ exports.en = {
|
|
|
9
9
|
selectPrompt: 'Enter option or use ↑↓ to select, press Enter to confirm',
|
|
10
10
|
multiSelectPrompt: 'Space to toggle, Enter to confirm',
|
|
11
11
|
confirmPrompt: 'Press Enter to confirm',
|
|
12
|
-
selectedCount: 'selected'
|
|
12
|
+
selectedCount: 'selected',
|
|
13
|
+
yes: 'Yes',
|
|
14
|
+
no: 'No',
|
|
15
|
+
scrollIndicator: 'Item {current}/{total} | ↑↓ Scroll for more'
|
|
13
16
|
},
|
|
14
17
|
hints: {
|
|
15
18
|
arrows: '↑↓ Arrow keys',
|
|
@@ -9,7 +9,10 @@ exports.zh = {
|
|
|
9
9
|
selectPrompt: '输入选项或用↑↓选择,回车确认',
|
|
10
10
|
multiSelectPrompt: '空格选中/取消,回车确认',
|
|
11
11
|
confirmPrompt: '回车确认',
|
|
12
|
-
selectedCount: '项已选'
|
|
12
|
+
selectedCount: '项已选',
|
|
13
|
+
yes: '是',
|
|
14
|
+
no: '否',
|
|
15
|
+
scrollIndicator: '第 {current}/{total} 项 | ↑↓ 滚动查看更多'
|
|
13
16
|
},
|
|
14
17
|
hints: {
|
|
15
18
|
arrows: '↑↓ 方向键',
|
package/dist/i18n/registry.d.ts
CHANGED
|
@@ -13,11 +13,12 @@ export declare function getCurrentLanguage(): LanguageCode;
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function setLanguage(lang: LanguageCode): void;
|
|
15
15
|
/**
|
|
16
|
-
* Get translation for a key path
|
|
16
|
+
* Get translation for a key path with optional parameter substitution
|
|
17
17
|
* @param keyPath - Dot-separated key path (e.g., 'menus.selectPrompt')
|
|
18
|
-
* @
|
|
18
|
+
* @param params - Optional parameters for string interpolation (e.g., { current: '1', total: '10' })
|
|
19
|
+
* @returns Translated string with parameters substituted
|
|
19
20
|
*/
|
|
20
|
-
export declare function t(keyPath: string): string;
|
|
21
|
+
export declare function t(keyPath: string, params?: Record<string, string>): string;
|
|
21
22
|
/**
|
|
22
23
|
* Register a new language
|
|
23
24
|
* @param code - Language code
|
package/dist/i18n/registry.js
CHANGED
|
@@ -39,11 +39,12 @@ function setLanguage(lang) {
|
|
|
39
39
|
registry.current = lang;
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
|
-
* Get translation for a key path
|
|
42
|
+
* Get translation for a key path with optional parameter substitution
|
|
43
43
|
* @param keyPath - Dot-separated key path (e.g., 'menus.selectPrompt')
|
|
44
|
-
* @
|
|
44
|
+
* @param params - Optional parameters for string interpolation (e.g., { current: '1', total: '10' })
|
|
45
|
+
* @returns Translated string with parameters substituted
|
|
45
46
|
*/
|
|
46
|
-
function t(keyPath) {
|
|
47
|
+
function t(keyPath, params) {
|
|
47
48
|
const keys = keyPath.split('.');
|
|
48
49
|
const langMap = registry.languages[registry.current];
|
|
49
50
|
let value = langMap;
|
|
@@ -56,7 +57,14 @@ function t(keyPath) {
|
|
|
56
57
|
return keyPath; // Return key path as fallback
|
|
57
58
|
}
|
|
58
59
|
}
|
|
59
|
-
|
|
60
|
+
let result = typeof value === 'string' ? value : keyPath;
|
|
61
|
+
// Substitute parameters if provided
|
|
62
|
+
if (params) {
|
|
63
|
+
for (const [key, val] of Object.entries(params)) {
|
|
64
|
+
result = result.replace(new RegExp(`\\{${key}\\}`, 'g'), val);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
60
68
|
}
|
|
61
69
|
/**
|
|
62
70
|
* Register a new language
|
package/dist/i18n/types.d.ts
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -4,20 +4,21 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export { menuAPI as menu, inputAPI as input, wizardAPI as wizard } from './api.js';
|
|
6
6
|
export { default } from './api.js';
|
|
7
|
-
export {
|
|
7
|
+
export { renderPage, type PageLayoutConfig, type HeaderConfig as PageHeaderConfig, type MainAreaConfig, type FooterConfig } from './page-layout.js';
|
|
8
|
+
export { createFullHeaderComponent, createSimpleHeaderComponent, createSectionHeaderComponent, createHintsComponent, createTableComponent, createListComponent, createSummaryTableComponent, createRadioMenuComponent, createInputPromptComponent } from './component-factories.js';
|
|
9
|
+
export { showRadioMenu, showCheckboxMenu, showCheckboxTableMenu, showBooleanMenu } from './components/menus/index.js';
|
|
8
10
|
export { showTextInput, showNumberInput, showLanguageSelector, showModifyField } from './components/inputs/index.js';
|
|
9
|
-
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 } from './components/display/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';
|
|
10
12
|
export { runWizard, createWizard, WizardConfig, WizardStep, WizardResult } from './features/wizard.js';
|
|
11
13
|
export { registerCommand, unregisterCommand, clearCustomCommands, isCommand, parseCommand, handleCommand, getAvailableCommands, showCommandHelp } from './features/commands.js';
|
|
12
14
|
export { getCurrentLanguage, setLanguage, t, registerLanguage, getAvailableLanguages, getCurrentLanguageMap } from './i18n/registry.js';
|
|
13
15
|
export { colors, uiColors, defaultUIColors, getUIColors, setUIColors, resetUIColors, createGradient, applyGradient, colorize } from './core/colors.js';
|
|
14
|
-
export type { MenuLayout, LayoutElement, LayoutVisibility, LayoutSpacing } from './types/layout.types.js';
|
|
15
16
|
export type { MenuOption, BaseMenuConfig, RadioMenuConfig, CheckboxMenuConfig, BooleanMenuConfig, RadioMenuResult, CheckboxMenuResult, BooleanMenuResult } from './types/menu.types.js';
|
|
16
17
|
export type { BaseInputConfig, TextInputConfig, NumberInputConfig, LanguageSelectorConfig, ModifyFieldConfig, TextInputResult, NumberInputResult, LanguageSelectorResult, ModifyFieldResult } from './types/input.types.js';
|
|
17
18
|
export type { HeaderType, SimpleHeaderConfig, AsciiHeaderConfig, ProgressConfig, MessageType, MessageConfig, SummaryTableConfig } from './types/display.types.js';
|
|
18
19
|
export type { LanguageCode, LanguageMap, I18nRegistry } from './i18n/types.js';
|
|
19
20
|
export { KEY_CODES } from './core/keyboard.js';
|
|
20
|
-
export {
|
|
21
|
+
export { calculateVirtualScroll, type VirtualScrollOptions, type VirtualScrollResult } from './core/virtual-scroll.js';
|
|
21
22
|
export * from './types';
|
|
22
23
|
export * from './components';
|
|
23
24
|
export * from './menu-core';
|
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.
|
|
25
|
-
exports.
|
|
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;
|
|
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; } });
|
|
@@ -30,10 +30,25 @@ Object.defineProperty(exports, "input", { enumerable: true, get: function () { r
|
|
|
30
30
|
Object.defineProperty(exports, "wizard", { enumerable: true, get: function () { return api_js_1.wizardAPI; } });
|
|
31
31
|
var api_js_2 = require("./api.js");
|
|
32
32
|
Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(api_js_2).default; } });
|
|
33
|
+
// Export Page Layout (Simple Configuration API)
|
|
34
|
+
var page_layout_js_1 = require("./page-layout.js");
|
|
35
|
+
Object.defineProperty(exports, "renderPage", { enumerable: true, get: function () { return page_layout_js_1.renderPage; } });
|
|
36
|
+
// Export Component Factories (Legacy - for backward compatibility)
|
|
37
|
+
var component_factories_js_1 = require("./component-factories.js");
|
|
38
|
+
Object.defineProperty(exports, "createFullHeaderComponent", { enumerable: true, get: function () { return component_factories_js_1.createFullHeaderComponent; } });
|
|
39
|
+
Object.defineProperty(exports, "createSimpleHeaderComponent", { enumerable: true, get: function () { return component_factories_js_1.createSimpleHeaderComponent; } });
|
|
40
|
+
Object.defineProperty(exports, "createSectionHeaderComponent", { enumerable: true, get: function () { return component_factories_js_1.createSectionHeaderComponent; } });
|
|
41
|
+
Object.defineProperty(exports, "createHintsComponent", { enumerable: true, get: function () { return component_factories_js_1.createHintsComponent; } });
|
|
42
|
+
Object.defineProperty(exports, "createTableComponent", { enumerable: true, get: function () { return component_factories_js_1.createTableComponent; } });
|
|
43
|
+
Object.defineProperty(exports, "createListComponent", { enumerable: true, get: function () { return component_factories_js_1.createListComponent; } });
|
|
44
|
+
Object.defineProperty(exports, "createSummaryTableComponent", { enumerable: true, get: function () { return component_factories_js_1.createSummaryTableComponent; } });
|
|
45
|
+
Object.defineProperty(exports, "createRadioMenuComponent", { enumerable: true, get: function () { return component_factories_js_1.createRadioMenuComponent; } });
|
|
46
|
+
Object.defineProperty(exports, "createInputPromptComponent", { enumerable: true, get: function () { return component_factories_js_1.createInputPromptComponent; } });
|
|
33
47
|
// Export menu components
|
|
34
48
|
var index_js_1 = require("./components/menus/index.js");
|
|
35
49
|
Object.defineProperty(exports, "showRadioMenu", { enumerable: true, get: function () { return index_js_1.showRadioMenu; } });
|
|
36
50
|
Object.defineProperty(exports, "showCheckboxMenu", { enumerable: true, get: function () { return index_js_1.showCheckboxMenu; } });
|
|
51
|
+
Object.defineProperty(exports, "showCheckboxTableMenu", { enumerable: true, get: function () { return index_js_1.showCheckboxTableMenu; } });
|
|
37
52
|
Object.defineProperty(exports, "showBooleanMenu", { enumerable: true, get: function () { return index_js_1.showBooleanMenu; } });
|
|
38
53
|
// Export input components
|
|
39
54
|
var index_js_2 = require("./components/inputs/index.js");
|
|
@@ -66,6 +81,17 @@ Object.defineProperty(exports, "renderSummaryTable", { enumerable: true, get: fu
|
|
|
66
81
|
Object.defineProperty(exports, "createSummaryTable", { enumerable: true, get: function () { return index_js_3.createSummaryTable; } });
|
|
67
82
|
Object.defineProperty(exports, "createSimpleSummary", { enumerable: true, get: function () { return index_js_3.createSimpleSummary; } });
|
|
68
83
|
Object.defineProperty(exports, "renderHeader", { enumerable: true, get: function () { return index_js_3.renderHeader; } });
|
|
84
|
+
Object.defineProperty(exports, "renderHintsComponent", { enumerable: true, get: function () { return index_js_3.renderHintsComponent; } });
|
|
85
|
+
Object.defineProperty(exports, "createHints", { enumerable: true, get: function () { return index_js_3.createHints; } });
|
|
86
|
+
Object.defineProperty(exports, "generateMenuHints", { enumerable: true, get: function () { return index_js_3.generateMenuHints; } });
|
|
87
|
+
Object.defineProperty(exports, "generateInputHints", { enumerable: true, get: function () { return index_js_3.generateInputHints; } });
|
|
88
|
+
Object.defineProperty(exports, "HintTypes", { enumerable: true, get: function () { return index_js_3.HintTypes; } });
|
|
89
|
+
Object.defineProperty(exports, "renderTable", { enumerable: true, get: function () { return index_js_3.renderTable; } });
|
|
90
|
+
Object.defineProperty(exports, "createTable", { enumerable: true, get: function () { return index_js_3.createTable; } });
|
|
91
|
+
Object.defineProperty(exports, "renderList", { enumerable: true, get: function () { return index_js_3.renderList; } });
|
|
92
|
+
Object.defineProperty(exports, "createList", { enumerable: true, get: function () { return index_js_3.createList; } });
|
|
93
|
+
Object.defineProperty(exports, "createBulletList", { enumerable: true, get: function () { return index_js_3.createBulletList; } });
|
|
94
|
+
Object.defineProperty(exports, "createNumberedList", { enumerable: true, get: function () { return index_js_3.createNumberedList; } });
|
|
69
95
|
// Export features
|
|
70
96
|
var wizard_js_1 = require("./features/wizard.js");
|
|
71
97
|
Object.defineProperty(exports, "runWizard", { enumerable: true, get: function () { return wizard_js_1.runWizard; } });
|
|
@@ -101,8 +127,8 @@ Object.defineProperty(exports, "colorize", { enumerable: true, get: function ()
|
|
|
101
127
|
// Export core utilities (for advanced users)
|
|
102
128
|
var keyboard_js_1 = require("./core/keyboard.js");
|
|
103
129
|
Object.defineProperty(exports, "KEY_CODES", { enumerable: true, get: function () { return keyboard_js_1.KEY_CODES; } });
|
|
104
|
-
var
|
|
105
|
-
Object.defineProperty(exports, "
|
|
130
|
+
var virtual_scroll_js_1 = require("./core/virtual-scroll.js");
|
|
131
|
+
Object.defineProperty(exports, "calculateVirtualScroll", { enumerable: true, get: function () { return virtual_scroll_js_1.calculateVirtualScroll; } });
|
|
106
132
|
// Legacy exports (for backward compatibility)
|
|
107
133
|
__exportStar(require("./types"), exports);
|
|
108
134
|
__exportStar(require("./components"), exports);
|
package/dist/layout.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
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)
|
|
10
|
+
*/
|
|
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
|
+
/**
|
|
17
|
+
* Component interface - components return lines instead of writing to stdout
|
|
18
|
+
*/
|
|
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;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Layout configuration with fixed heights
|
|
33
|
+
*/
|
|
34
|
+
export interface Layout {
|
|
35
|
+
header: Rect;
|
|
36
|
+
main: Rect;
|
|
37
|
+
footerHints: Rect;
|
|
38
|
+
footerPrompt: Rect;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Compute layout based on terminal size
|
|
42
|
+
*/
|
|
43
|
+
export declare function computeLayout(rows?: number, cols?: number): Layout;
|
|
44
|
+
/**
|
|
45
|
+
* Area configuration
|
|
46
|
+
*/
|
|
47
|
+
export interface AreaConfig {
|
|
48
|
+
components: Component[];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Page layout configuration
|
|
52
|
+
*/
|
|
53
|
+
export interface PageLayoutConfigV2 {
|
|
54
|
+
header?: AreaConfig;
|
|
55
|
+
mainArea?: AreaConfig;
|
|
56
|
+
footer?: AreaConfig;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Render complete page with fixed-region architecture
|
|
60
|
+
*
|
|
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
|
|
67
|
+
*/
|
|
68
|
+
export declare function createCustomComponent(type: string, regionId: string, render: (rect: Rect) => string[] | Promise<string[]>, interact?: () => void | Promise<void>): Component;
|
package/dist/layout.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
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)
|
|
11
|
+
*/
|
|
12
|
+
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
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Render complete page with fixed-region architecture
|
|
43
|
+
*
|
|
44
|
+
* Phase 1: Render all components (non-blocking)
|
|
45
|
+
* Phase 2: Handle interactions (blocking)
|
|
46
|
+
*/
|
|
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
|
+
}
|
|
62
|
+
}
|
|
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
|
+
}
|
|
73
|
+
}
|
|
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
|
+
}
|
|
83
|
+
}
|
|
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));
|
|
113
|
+
}
|
|
114
|
+
if (config.mainArea?.components) {
|
|
115
|
+
interactiveComponents.push(...config.mainArea.components.filter(c => c.interact));
|
|
116
|
+
}
|
|
117
|
+
if (config.footer?.components) {
|
|
118
|
+
interactiveComponents.push(...config.footer.components.filter(c => c.interact));
|
|
119
|
+
}
|
|
120
|
+
// Execute interact phase
|
|
121
|
+
for (const component of interactiveComponents) {
|
|
122
|
+
if (component.interact) {
|
|
123
|
+
await component.interact();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Exit alt screen
|
|
127
|
+
exports.screenManager.exit();
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Helper to create a custom component
|
|
131
|
+
*/
|
|
132
|
+
function createCustomComponent(type, regionId, render, interact) {
|
|
133
|
+
return { type, regionId, render, interact };
|
|
134
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page Layout System - Simple Configuration API
|
|
3
|
+
* Architecture: Header + Main Area + Footer
|
|
4
|
+
*
|
|
5
|
+
* This is the simple, configuration-based API that users want.
|
|
6
|
+
* Just pass config objects, no need to create components manually.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Header configuration
|
|
10
|
+
*/
|
|
11
|
+
export interface HeaderConfig {
|
|
12
|
+
type: 'simple' | 'section' | 'full' | 'none';
|
|
13
|
+
text?: string;
|
|
14
|
+
width?: number;
|
|
15
|
+
asciiArt?: string[];
|
|
16
|
+
title?: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
version?: string;
|
|
19
|
+
url?: string;
|
|
20
|
+
menuTitle?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Main Area configuration
|
|
24
|
+
*/
|
|
25
|
+
export interface MainAreaConfig {
|
|
26
|
+
type: 'menu' | 'display' | 'interactive';
|
|
27
|
+
menu?: {
|
|
28
|
+
options: any[];
|
|
29
|
+
allowLetterKeys?: boolean;
|
|
30
|
+
allowNumberKeys?: boolean;
|
|
31
|
+
preserveOnSelect?: boolean;
|
|
32
|
+
};
|
|
33
|
+
render?: () => void | Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Footer configuration
|
|
37
|
+
*/
|
|
38
|
+
export interface FooterConfig {
|
|
39
|
+
menu?: {
|
|
40
|
+
options: any[];
|
|
41
|
+
allowLetterKeys?: boolean;
|
|
42
|
+
allowNumberKeys?: boolean;
|
|
43
|
+
preserveOnSelect?: boolean;
|
|
44
|
+
};
|
|
45
|
+
ask?: {
|
|
46
|
+
question: string;
|
|
47
|
+
defaultValue?: boolean;
|
|
48
|
+
horizontal?: boolean;
|
|
49
|
+
};
|
|
50
|
+
input?: {
|
|
51
|
+
prompt: string;
|
|
52
|
+
defaultValue?: string;
|
|
53
|
+
allowEmpty?: boolean;
|
|
54
|
+
};
|
|
55
|
+
hints?: string[];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Complete page layout configuration
|
|
59
|
+
*/
|
|
60
|
+
export interface PageLayoutConfig {
|
|
61
|
+
header?: HeaderConfig;
|
|
62
|
+
mainArea: MainAreaConfig;
|
|
63
|
+
footer?: FooterConfig;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Render complete page with simple configuration API
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```typescript
|
|
70
|
+
* const result = await renderPage({
|
|
71
|
+
* header: {
|
|
72
|
+
* type: 'full',
|
|
73
|
+
* asciiArt: ['...'],
|
|
74
|
+
* title: 'My App',
|
|
75
|
+
* description: 'Description',
|
|
76
|
+
* version: '1.0.0'
|
|
77
|
+
* },
|
|
78
|
+
* mainArea: {
|
|
79
|
+
* type: 'menu',
|
|
80
|
+
* menu: {
|
|
81
|
+
* options: ['Option 1', 'Option 2'],
|
|
82
|
+
* allowLetterKeys: true,
|
|
83
|
+
* allowNumberKeys: true
|
|
84
|
+
* }
|
|
85
|
+
* },
|
|
86
|
+
* footer: {
|
|
87
|
+
* hints: ['↑↓ Navigate', 'Enter Confirm']
|
|
88
|
+
* }
|
|
89
|
+
* });
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export declare function renderPage(config: PageLayoutConfig): Promise<any>;
|