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.
Files changed (58) hide show
  1. package/dist/api.d.ts +23 -5
  2. package/dist/api.js +16 -4
  3. package/dist/component-factories.d.ts +59 -0
  4. package/dist/component-factories.js +141 -0
  5. package/dist/components/display/header-v2.d.ts +13 -0
  6. package/dist/components/display/header-v2.js +43 -0
  7. package/dist/components/display/hints-v2.d.ts +10 -0
  8. package/dist/components/display/hints-v2.js +34 -0
  9. package/dist/components/display/hints.d.ts +56 -0
  10. package/dist/components/display/hints.js +81 -0
  11. package/dist/components/display/index.d.ts +3 -0
  12. package/dist/components/display/index.js +15 -1
  13. package/dist/components/display/input-prompt.d.ts +35 -0
  14. package/dist/components/display/input-prompt.js +36 -0
  15. package/dist/components/display/list.d.ts +49 -0
  16. package/dist/components/display/list.js +86 -0
  17. package/dist/components/display/table.d.ts +42 -0
  18. package/dist/components/display/table.js +107 -0
  19. package/dist/components/menus/boolean-menu.js +2 -1
  20. package/dist/components/menus/checkbox-menu.d.ts +2 -1
  21. package/dist/components/menus/checkbox-menu.js +30 -59
  22. package/dist/components/menus/checkbox-table-menu.d.ts +12 -0
  23. package/dist/components/menus/checkbox-table-menu.js +395 -0
  24. package/dist/components/menus/index.d.ts +1 -0
  25. package/dist/components/menus/index.js +3 -1
  26. package/dist/components/menus/radio-menu-split.d.ts +33 -0
  27. package/dist/components/menus/radio-menu-split.js +248 -0
  28. package/dist/components/menus/radio-menu-v2.d.ts +11 -0
  29. package/dist/components/menus/radio-menu-v2.js +150 -0
  30. package/dist/components/menus/radio-menu.d.ts +2 -1
  31. package/dist/components/menus/radio-menu.js +60 -123
  32. package/dist/core/hint-manager.d.ts +29 -0
  33. package/dist/core/hint-manager.js +65 -0
  34. package/dist/core/renderer.d.ts +2 -1
  35. package/dist/core/renderer.js +22 -6
  36. package/dist/core/screen-manager.d.ts +54 -0
  37. package/dist/core/screen-manager.js +119 -0
  38. package/dist/core/state-manager.d.ts +27 -0
  39. package/dist/core/state-manager.js +56 -0
  40. package/dist/core/terminal.d.ts +4 -1
  41. package/dist/core/terminal.js +37 -4
  42. package/dist/core/virtual-scroll.d.ts +65 -0
  43. package/dist/core/virtual-scroll.js +120 -0
  44. package/dist/i18n/languages/en.js +4 -1
  45. package/dist/i18n/languages/zh.js +4 -1
  46. package/dist/i18n/registry.d.ts +4 -3
  47. package/dist/i18n/registry.js +12 -4
  48. package/dist/i18n/types.d.ts +3 -0
  49. package/dist/index.d.ts +5 -4
  50. package/dist/index.js +30 -4
  51. package/dist/layout.d.ts +68 -0
  52. package/dist/layout.js +134 -0
  53. package/dist/page-layout.d.ts +92 -0
  54. package/dist/page-layout.js +156 -0
  55. package/dist/types/menu.types.d.ts +57 -5
  56. package/package.json +1 -1
  57. package/dist/types/layout.types.d.ts +0 -56
  58. 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: '↑↓ 方向键',
@@ -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
- * @returns Translated string
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
@@ -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
- * @returns Translated string
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
- return typeof value === 'string' ? value : keyPath;
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
@@ -14,6 +14,9 @@ export interface LanguageMap {
14
14
  multiSelectPrompt: string;
15
15
  confirmPrompt: string;
16
16
  selectedCount: string;
17
+ yes: string;
18
+ no: string;
19
+ scrollIndicator: string;
17
20
  };
18
21
  hints: {
19
22
  arrows: string;
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 { showRadioMenu, showCheckboxMenu, showBooleanMenu } from './components/menus/index.js';
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 { LAYOUT_PRESETS } from './types/layout.types.js';
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.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.resetUIColors = exports.setUIColors = exports.getUIColors = exports.defaultUIColors = exports.uiColors = exports.colors = void 0;
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 layout_types_js_1 = require("./types/layout.types.js");
105
- Object.defineProperty(exports, "LAYOUT_PRESETS", { enumerable: true, get: function () { return layout_types_js_1.LAYOUT_PRESETS; } });
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);
@@ -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>;