@compilr-dev/cli 0.4.0 → 0.5.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/README.md +30 -12
- package/dist/agent.d.ts +74 -1
- package/dist/agent.js +259 -76
- package/dist/anchors/index.d.ts +9 -0
- package/dist/anchors/index.js +9 -0
- package/dist/anchors/project-anchors.d.ts +79 -0
- package/dist/anchors/project-anchors.js +202 -0
- package/dist/commands/handler-types.d.ts +68 -0
- package/dist/commands/handler-types.js +8 -0
- package/dist/commands/handlers/agent-commands.d.ts +13 -0
- package/dist/commands/handlers/agent-commands.js +305 -0
- package/dist/commands/handlers/design-commands.d.ts +15 -0
- package/dist/commands/handlers/design-commands.js +334 -0
- package/dist/commands/handlers/index.d.ts +20 -0
- package/dist/commands/handlers/index.js +43 -0
- package/dist/commands/handlers/overlay-commands.d.ts +21 -0
- package/dist/commands/handlers/overlay-commands.js +287 -0
- package/dist/commands/handlers/project-commands.d.ts +11 -0
- package/dist/commands/handlers/project-commands.js +167 -0
- package/dist/commands/handlers/simple-commands.d.ts +19 -0
- package/dist/commands/handlers/simple-commands.js +144 -0
- package/dist/commands/index.d.ts +2 -1
- package/dist/commands/registry.d.ts +50 -0
- package/dist/commands/registry.js +75 -0
- package/dist/commands-v2/handlers/context.d.ts +13 -0
- package/dist/commands-v2/handlers/context.js +348 -0
- package/dist/commands-v2/handlers/core.d.ts +13 -0
- package/dist/commands-v2/handlers/core.js +165 -0
- package/dist/commands-v2/handlers/debug.d.ts +11 -0
- package/dist/commands-v2/handlers/debug.js +159 -0
- package/dist/commands-v2/handlers/index.d.ts +12 -0
- package/dist/commands-v2/handlers/index.js +24 -0
- package/dist/commands-v2/handlers/project.d.ts +22 -0
- package/dist/commands-v2/handlers/project.js +814 -0
- package/dist/commands-v2/handlers/settings.d.ts +15 -0
- package/dist/commands-v2/handlers/settings.js +235 -0
- package/dist/commands-v2/index.d.ts +13 -0
- package/dist/commands-v2/index.js +15 -0
- package/dist/commands-v2/registry.d.ts +37 -0
- package/dist/commands-v2/registry.js +80 -0
- package/dist/commands-v2/types.d.ts +75 -0
- package/dist/commands-v2/types.js +7 -0
- package/dist/commands.js +110 -7
- package/dist/index.js +288 -29
- package/dist/input-handlers/index.d.ts +7 -0
- package/dist/input-handlers/index.js +7 -0
- package/dist/input-handlers/memory-handler.d.ts +26 -0
- package/dist/input-handlers/memory-handler.js +68 -0
- package/dist/repl-helpers.d.ts +63 -0
- package/dist/repl-helpers.js +318 -0
- package/dist/repl-v2.d.ts +155 -0
- package/dist/repl-v2.js +774 -0
- package/dist/repl.d.ts +32 -4
- package/dist/repl.js +250 -977
- package/dist/settings/index.d.ts +23 -0
- package/dist/settings/index.js +48 -0
- package/dist/settings/paths.d.ts +110 -0
- package/dist/settings/paths.js +264 -0
- package/dist/templates/compilr-md.js +7 -4
- package/dist/templates/index.js +3 -4
- package/dist/themes/colors.js +3 -1
- package/dist/themes/registry.d.ts +5 -36
- package/dist/themes/registry.js +11 -95
- package/dist/themes/types.d.ts +3 -38
- package/dist/themes/types.js +2 -2
- package/dist/tools/anchor-tools.d.ts +31 -0
- package/dist/tools/anchor-tools.js +255 -0
- package/dist/tools/backlog-wrappers.d.ts +54 -0
- package/dist/tools/backlog-wrappers.js +338 -0
- package/dist/tools/backlog.js +1 -1
- package/dist/tools/db-tools.d.ts +65 -0
- package/dist/tools/db-tools.js +19 -0
- package/dist/tools/document-db.d.ts +43 -0
- package/dist/tools/document-db.js +220 -0
- package/dist/tools/project-db.d.ts +102 -0
- package/dist/tools/project-db.js +370 -0
- package/dist/tools/workitem-db.d.ts +103 -0
- package/dist/tools/workitem-db.js +549 -0
- package/dist/tools.js +13 -3
- package/dist/ui/agents-overlay-v2.d.ts +43 -0
- package/dist/ui/agents-overlay-v2.js +809 -0
- package/dist/ui/agents-overlay.d.ts +5 -5
- package/dist/ui/agents-overlay.js +782 -420
- package/dist/ui/anchors-overlay.d.ts +12 -0
- package/dist/ui/anchors-overlay.js +775 -0
- package/dist/ui/arch-type-overlay.d.ts +1 -6
- package/dist/ui/arch-type-overlay.js +175 -203
- package/dist/ui/ask-user-overlay-v2.d.ts +26 -0
- package/dist/ui/ask-user-overlay-v2.js +555 -0
- package/dist/ui/ask-user-overlay.d.ts +2 -2
- package/dist/ui/ask-user-overlay.js +443 -535
- package/dist/ui/ask-user-simple-overlay-v2.d.ts +25 -0
- package/dist/ui/ask-user-simple-overlay-v2.js +215 -0
- package/dist/ui/ask-user-simple-overlay.d.ts +2 -2
- package/dist/ui/ask-user-simple-overlay.js +182 -209
- package/dist/ui/backlog-overlay.d.ts +16 -1
- package/dist/ui/backlog-overlay.js +525 -659
- package/dist/ui/base/index.d.ts +26 -0
- package/dist/ui/base/index.js +33 -0
- package/dist/ui/base/inline-overlay-utils.d.ts +217 -0
- package/dist/ui/base/inline-overlay-utils.js +320 -0
- package/dist/ui/base/inline-overlay.d.ts +159 -0
- package/dist/ui/base/inline-overlay.js +257 -0
- package/dist/ui/base/key-utils.d.ts +15 -0
- package/dist/ui/base/key-utils.js +30 -0
- package/dist/ui/base/overlay-base-v2.d.ts +193 -0
- package/dist/ui/base/overlay-base-v2.js +246 -0
- package/dist/ui/base/overlay-base.d.ts +156 -0
- package/dist/ui/base/overlay-base.js +238 -0
- package/dist/ui/base/overlay-lifecycle.d.ts +65 -0
- package/dist/ui/base/overlay-lifecycle.js +159 -0
- package/dist/ui/base/overlay-types.d.ts +185 -0
- package/dist/ui/base/overlay-types.js +7 -0
- package/dist/ui/base/render-utils.d.ts +8 -0
- package/dist/ui/base/render-utils.js +11 -0
- package/dist/ui/base/screen-stack.d.ts +148 -0
- package/dist/ui/base/screen-stack.js +184 -0
- package/dist/ui/base/tabbed-list-overlay-v2.d.ts +103 -0
- package/dist/ui/base/tabbed-list-overlay-v2.js +317 -0
- package/dist/ui/base/tabbed-list-overlay.d.ts +153 -0
- package/dist/ui/base/tabbed-list-overlay.js +369 -0
- package/dist/ui/commands-overlay-v2.d.ts +33 -0
- package/dist/ui/commands-overlay-v2.js +441 -0
- package/dist/ui/commands-overlay.d.ts +7 -2
- package/dist/ui/commands-overlay.js +384 -355
- package/dist/ui/config-overlay.d.ts +5 -4
- package/dist/ui/config-overlay.js +243 -513
- package/dist/ui/conversation.d.ts +75 -4
- package/dist/ui/conversation.js +374 -161
- package/dist/ui/docs-overlay.d.ts +17 -0
- package/dist/ui/docs-overlay.js +303 -0
- package/dist/ui/ephemeral.d.ts +1 -1
- package/dist/ui/ephemeral.js +1 -1
- package/dist/ui/features/index.d.ts +34 -0
- package/dist/ui/features/index.js +34 -0
- package/dist/ui/features/input-feature.d.ts +85 -0
- package/dist/ui/features/input-feature.js +238 -0
- package/dist/ui/features/list-feature.d.ts +155 -0
- package/dist/ui/features/list-feature.js +244 -0
- package/dist/ui/features/pagination-feature.d.ts +154 -0
- package/dist/ui/features/pagination-feature.js +238 -0
- package/dist/ui/features/search-feature.d.ts +148 -0
- package/dist/ui/features/search-feature.js +185 -0
- package/dist/ui/features/tab-feature.d.ts +194 -0
- package/dist/ui/features/tab-feature.js +307 -0
- package/dist/ui/footer-v2.d.ts +222 -0
- package/dist/ui/footer-v2.js +1349 -0
- package/dist/ui/footer.d.ts +107 -0
- package/dist/ui/footer.js +359 -67
- package/dist/ui/guardrail-overlay.d.ts +29 -0
- package/dist/ui/guardrail-overlay.js +145 -0
- package/dist/ui/help-overlay-v2.d.ts +34 -0
- package/dist/ui/help-overlay-v2.js +309 -0
- package/dist/ui/help-overlay.d.ts +16 -0
- package/dist/ui/help-overlay.js +316 -0
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +1 -3
- package/dist/ui/init-overlay-v2.d.ts +34 -0
- package/dist/ui/init-overlay-v2.js +600 -0
- package/dist/ui/init-overlay.d.ts +12 -2
- package/dist/ui/init-overlay.js +349 -270
- package/dist/ui/input-prompt-v2.d.ts +1 -0
- package/dist/ui/input-prompt-v2.js +14 -6
- package/dist/ui/input-prompt.d.ts +116 -33
- package/dist/ui/input-prompt.js +536 -337
- package/dist/ui/iteration-limit-overlay-v2.d.ts +21 -0
- package/dist/ui/iteration-limit-overlay-v2.js +114 -0
- package/dist/ui/iteration-limit-overlay.d.ts +2 -2
- package/dist/ui/iteration-limit-overlay.js +92 -128
- package/dist/ui/keys-overlay-v2.d.ts +41 -0
- package/dist/ui/keys-overlay-v2.js +248 -0
- package/dist/ui/keys-overlay.d.ts +1 -0
- package/dist/ui/keys-overlay.js +203 -141
- package/dist/ui/line-utils.d.ts +88 -0
- package/dist/ui/line-utils.js +150 -0
- package/dist/ui/live-region.d.ts +161 -0
- package/dist/ui/live-region.js +387 -0
- package/dist/ui/mascot/expressions.d.ts +32 -0
- package/dist/ui/mascot/expressions.js +213 -0
- package/dist/ui/mascot/index.d.ts +8 -0
- package/dist/ui/mascot/index.js +8 -0
- package/dist/ui/mascot/renderer.d.ts +19 -0
- package/dist/ui/mascot/renderer.js +97 -0
- package/dist/ui/mascot-overlay-v2.d.ts +41 -0
- package/dist/ui/mascot-overlay-v2.js +138 -0
- package/dist/ui/mascot-overlay.d.ts +21 -0
- package/dist/ui/mascot-overlay.js +146 -0
- package/dist/ui/model-overlay-v2.d.ts +49 -0
- package/dist/ui/model-overlay-v2.js +118 -0
- package/dist/ui/model-overlay.d.ts +27 -0
- package/dist/ui/model-overlay.js +221 -0
- package/dist/ui/model-warning-overlay.js +3 -5
- package/dist/ui/new-overlay.d.ts +34 -0
- package/dist/ui/new-overlay.js +604 -0
- package/dist/ui/overlay/impl/agents-overlay-v2.d.ts +45 -0
- package/dist/ui/overlay/impl/agents-overlay-v2.js +825 -0
- package/dist/ui/overlay/impl/anchors-overlay-v2.d.ts +47 -0
- package/dist/ui/overlay/impl/anchors-overlay-v2.js +783 -0
- package/dist/ui/overlay/impl/arch-type-overlay-v2.d.ts +37 -0
- package/dist/ui/overlay/impl/arch-type-overlay-v2.js +240 -0
- package/dist/ui/overlay/impl/ask-user-overlay-v2.d.ts +72 -0
- package/dist/ui/overlay/impl/ask-user-overlay-v2.js +584 -0
- package/dist/ui/overlay/impl/ask-user-simple-overlay-v2.d.ts +46 -0
- package/dist/ui/overlay/impl/ask-user-simple-overlay-v2.js +204 -0
- package/dist/ui/overlay/impl/backlog-overlay-v2.d.ts +49 -0
- package/dist/ui/overlay/impl/backlog-overlay-v2.js +642 -0
- package/dist/ui/overlay/impl/commands-overlay-v2.d.ts +33 -0
- package/dist/ui/overlay/impl/commands-overlay-v2.js +441 -0
- package/dist/ui/overlay/impl/config-overlay-v2.d.ts +100 -0
- package/dist/ui/overlay/impl/config-overlay-v2.js +654 -0
- package/dist/ui/overlay/impl/dashboard-overlay-v2.d.ts +55 -0
- package/dist/ui/overlay/impl/dashboard-overlay-v2.js +359 -0
- package/dist/ui/overlay/impl/docs-overlay-v2.d.ts +45 -0
- package/dist/ui/overlay/impl/docs-overlay-v2.js +114 -0
- package/dist/ui/overlay/impl/document-detail-overlay-v2.d.ts +77 -0
- package/dist/ui/overlay/impl/document-detail-overlay-v2.js +1071 -0
- package/dist/ui/overlay/impl/guardrail-overlay-v2.d.ts +43 -0
- package/dist/ui/overlay/impl/guardrail-overlay-v2.js +114 -0
- package/dist/ui/overlay/impl/help-overlay-v2.d.ts +34 -0
- package/dist/ui/overlay/impl/help-overlay-v2.js +309 -0
- package/dist/ui/overlay/impl/init-overlay-v2.d.ts +77 -0
- package/dist/ui/overlay/impl/init-overlay-v2.js +593 -0
- package/dist/ui/overlay/impl/init-setup-overlay-v2.d.ts +25 -0
- package/dist/ui/overlay/impl/init-setup-overlay-v2.js +97 -0
- package/dist/ui/overlay/impl/iteration-limit-overlay-v2.d.ts +35 -0
- package/dist/ui/overlay/impl/iteration-limit-overlay-v2.js +105 -0
- package/dist/ui/overlay/impl/keys-overlay-v2.d.ts +41 -0
- package/dist/ui/overlay/impl/keys-overlay-v2.js +248 -0
- package/dist/ui/overlay/impl/mascot-overlay-v2.d.ts +41 -0
- package/dist/ui/overlay/impl/mascot-overlay-v2.js +138 -0
- package/dist/ui/overlay/impl/model-overlay-v2.d.ts +49 -0
- package/dist/ui/overlay/impl/model-overlay-v2.js +118 -0
- package/dist/ui/overlay/impl/model-warning-overlay-v2.d.ts +46 -0
- package/dist/ui/overlay/impl/model-warning-overlay-v2.js +132 -0
- package/dist/ui/overlay/impl/new-overlay-v2.d.ts +77 -0
- package/dist/ui/overlay/impl/new-overlay-v2.js +593 -0
- package/dist/ui/overlay/impl/permission-overlay-v2.d.ts +36 -0
- package/dist/ui/overlay/impl/permission-overlay-v2.js +380 -0
- package/dist/ui/overlay/impl/projects-overlay-v2.d.ts +36 -0
- package/dist/ui/overlay/impl/projects-overlay-v2.js +499 -0
- package/dist/ui/overlay/impl/theme-overlay-v2.d.ts +42 -0
- package/dist/ui/overlay/impl/theme-overlay-v2.js +135 -0
- package/dist/ui/overlay/impl/tools-overlay-v2.d.ts +47 -0
- package/dist/ui/overlay/impl/tools-overlay-v2.js +218 -0
- package/dist/ui/overlay/impl/tutorial-overlay-v2.d.ts +31 -0
- package/dist/ui/overlay/impl/tutorial-overlay-v2.js +1035 -0
- package/dist/ui/overlay/impl/workflow-overlay-v2.d.ts +80 -0
- package/dist/ui/overlay/impl/workflow-overlay-v2.js +637 -0
- package/dist/ui/overlay/index.d.ts +33 -0
- package/dist/ui/overlay/index.js +35 -0
- package/dist/ui/overlay/key-utils.d.ts +6 -0
- package/dist/ui/overlay/key-utils.js +6 -0
- package/dist/ui/overlay/overlay-types.d.ts +128 -0
- package/dist/ui/overlay/overlay-types.js +22 -0
- package/dist/ui/overlay/types.d.ts +135 -0
- package/dist/ui/overlay/types.js +22 -0
- package/dist/ui/overlays/help-overlay-v2.d.ts +28 -0
- package/dist/ui/overlays/help-overlay-v2.js +198 -0
- package/dist/ui/overlays/index.d.ts +11 -0
- package/dist/ui/overlays/index.js +11 -0
- package/dist/ui/overlays.d.ts +0 -4
- package/dist/ui/overlays.js +0 -444
- package/dist/ui/permission-overlay-v2.d.ts +36 -0
- package/dist/ui/permission-overlay-v2.js +380 -0
- package/dist/ui/permission-overlay.d.ts +1 -1
- package/dist/ui/permission-overlay.js +186 -298
- package/dist/ui/projects-overlay.d.ts +19 -0
- package/dist/ui/projects-overlay.js +484 -0
- package/dist/ui/providers/types.d.ts +178 -0
- package/dist/ui/providers/types.js +9 -0
- package/dist/ui/render-modes.d.ts +36 -0
- package/dist/ui/render-modes.js +44 -0
- package/dist/ui/startup-menu.d.ts +36 -0
- package/dist/ui/startup-menu.js +236 -0
- package/dist/ui/subagent-renderer.d.ts +117 -0
- package/dist/ui/subagent-renderer.js +334 -0
- package/dist/ui/terminal-codes.d.ts +94 -0
- package/dist/ui/terminal-codes.js +124 -0
- package/dist/ui/terminal-renderer.d.ts +221 -0
- package/dist/ui/terminal-renderer.js +751 -0
- package/dist/ui/terminal-ui.d.ts +463 -0
- package/dist/ui/terminal-ui.js +2296 -0
- package/dist/ui/terminal.d.ts +20 -0
- package/dist/ui/terminal.js +72 -0
- package/dist/ui/theme-overlay-v2.d.ts +42 -0
- package/dist/ui/theme-overlay-v2.js +135 -0
- package/dist/ui/theme-overlay.d.ts +24 -0
- package/dist/ui/theme-overlay.js +127 -0
- package/dist/ui/todo-zone.js +53 -25
- package/dist/ui/tool-formatters.d.ts +16 -0
- package/dist/ui/tool-formatters.js +516 -0
- package/dist/ui/tools-overlay-v2.d.ts +47 -0
- package/dist/ui/tools-overlay-v2.js +218 -0
- package/dist/ui/tools-overlay.d.ts +10 -2
- package/dist/ui/tools-overlay.js +172 -220
- package/dist/ui/tutorial-overlay-v2.d.ts +31 -0
- package/dist/ui/tutorial-overlay-v2.js +1035 -0
- package/dist/ui/tutorial-overlay.d.ts +1 -0
- package/dist/ui/tutorial-overlay.js +400 -302
- package/dist/ui/workflow-overlay.d.ts +22 -0
- package/dist/ui/workflow-overlay.js +636 -0
- package/dist/utils/debug-log.d.ts +28 -0
- package/dist/utils/debug-log.js +57 -0
- package/dist/utils/model-tiers.js +1 -1
- package/dist/utils/path-safety.d.ts +56 -0
- package/dist/utils/path-safety.js +239 -0
- package/dist/workflow/guided-mode-injector.d.ts +42 -0
- package/dist/workflow/guided-mode-injector.js +191 -0
- package/dist/workflow/index.d.ts +8 -0
- package/dist/workflow/index.js +8 -0
- package/dist/workflow/step-criteria.d.ts +62 -0
- package/dist/workflow/step-criteria.js +150 -0
- package/dist/workflow/step-tracker.d.ts +92 -0
- package/dist/workflow/step-tracker.js +141 -0
- package/package.json +12 -5
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Overlay V2
|
|
3
|
+
*
|
|
4
|
+
* Abstract base class for overlays using the new Overlay interface.
|
|
5
|
+
* Designed to work with TerminalUI's overlay management.
|
|
6
|
+
*
|
|
7
|
+
* Key differences from BaseOverlay:
|
|
8
|
+
* - render() returns OverlayContent, not string[]
|
|
9
|
+
* - handleKey() returns OverlayAction, not void
|
|
10
|
+
* - TerminalUI manages the render loop (not the overlay itself)
|
|
11
|
+
* - Lifecycle via onMount()/onUnmount() instead of show()/close()
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* ```typescript
|
|
15
|
+
* interface MyState {
|
|
16
|
+
* selectedIndex: number;
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* class MyOverlay extends BaseOverlayV2<MyState, string | null> {
|
|
20
|
+
* readonly type = 'inline';
|
|
21
|
+
* readonly id = 'my-overlay';
|
|
22
|
+
*
|
|
23
|
+
* constructor() {
|
|
24
|
+
* super({ selectedIndex: 0 });
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* protected renderContent(context: RenderContext): string[] {
|
|
28
|
+
* return [
|
|
29
|
+
* ...this.renderHeader('My Overlay'),
|
|
30
|
+
* ' Content here...',
|
|
31
|
+
* ...this.renderFooter('↑↓ Navigate · Enter Select · q/Esc Cancel')
|
|
32
|
+
* ];
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* handleKey(key: KeyEvent): OverlayAction<string | null> {
|
|
36
|
+
* if (key.name === 'escape' || key.name === 'q') {
|
|
37
|
+
* return this.close(null);
|
|
38
|
+
* }
|
|
39
|
+
* // ...
|
|
40
|
+
* return this.rerender();
|
|
41
|
+
* }
|
|
42
|
+
* }
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
// =============================================================================
|
|
46
|
+
// BaseOverlayV2 Abstract Class
|
|
47
|
+
// =============================================================================
|
|
48
|
+
/**
|
|
49
|
+
* Abstract base class for overlays using the new Overlay interface.
|
|
50
|
+
*
|
|
51
|
+
* @template TState - State interface for the overlay
|
|
52
|
+
* @template TResult - Return type when overlay closes
|
|
53
|
+
*/
|
|
54
|
+
export class BaseOverlayV2 {
|
|
55
|
+
/** Current overlay state */
|
|
56
|
+
state;
|
|
57
|
+
/** Cached styles from last render context */
|
|
58
|
+
styles = null;
|
|
59
|
+
/** Cached terminal width from last render context */
|
|
60
|
+
termWidth = 80;
|
|
61
|
+
/** Minimum height for stable rendering (prevents visual jitter) */
|
|
62
|
+
minHeight = 0;
|
|
63
|
+
/**
|
|
64
|
+
* Create a new overlay instance.
|
|
65
|
+
*
|
|
66
|
+
* @param initialState - Initial state
|
|
67
|
+
*/
|
|
68
|
+
constructor(initialState) {
|
|
69
|
+
this.state = initialState;
|
|
70
|
+
}
|
|
71
|
+
// ===========================================================================
|
|
72
|
+
// Overlay Interface Implementation
|
|
73
|
+
// ===========================================================================
|
|
74
|
+
/**
|
|
75
|
+
* Get current state.
|
|
76
|
+
*/
|
|
77
|
+
getState() {
|
|
78
|
+
return this.state;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Render the overlay.
|
|
82
|
+
* Calls renderContent() and wraps in OverlayContent.
|
|
83
|
+
*/
|
|
84
|
+
render(context) {
|
|
85
|
+
// Cache context values for helper methods
|
|
86
|
+
this.styles = context.styles;
|
|
87
|
+
this.termWidth = context.width;
|
|
88
|
+
// Get content from subclass
|
|
89
|
+
const lines = this.renderContent(context);
|
|
90
|
+
// Include minHeight for stable rendering (prevents visual jitter when content height varies)
|
|
91
|
+
return this.minHeight > 0 ? { lines, minHeight: this.minHeight } : { lines };
|
|
92
|
+
}
|
|
93
|
+
// ===========================================================================
|
|
94
|
+
// Protected Methods - Action Helpers
|
|
95
|
+
// ===========================================================================
|
|
96
|
+
/**
|
|
97
|
+
* Return action to close with result.
|
|
98
|
+
*/
|
|
99
|
+
close(result) {
|
|
100
|
+
return { type: 'close', result };
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Return action to close as cancelled.
|
|
104
|
+
*/
|
|
105
|
+
cancel() {
|
|
106
|
+
return { type: 'close', cancelled: true };
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Return action to re-render.
|
|
110
|
+
*/
|
|
111
|
+
rerender() {
|
|
112
|
+
return { type: 'render' };
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Return action for no change.
|
|
116
|
+
*/
|
|
117
|
+
noAction() {
|
|
118
|
+
return { type: 'none' };
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Return action to push a sub-overlay.
|
|
122
|
+
*/
|
|
123
|
+
push(overlay) {
|
|
124
|
+
return { type: 'push', overlay };
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Return action to pop back to parent.
|
|
128
|
+
*/
|
|
129
|
+
pop() {
|
|
130
|
+
return { type: 'pop' };
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get the cached styles (or throw if not available).
|
|
134
|
+
*/
|
|
135
|
+
getStyles() {
|
|
136
|
+
if (!this.styles) {
|
|
137
|
+
throw new Error('Styles not available - render() must be called first');
|
|
138
|
+
}
|
|
139
|
+
return this.styles;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Render a standard header with border and title.
|
|
143
|
+
*
|
|
144
|
+
* @param title - Main title
|
|
145
|
+
* @param subtitle - Optional subtitle (shown in muted)
|
|
146
|
+
* @returns Array of lines
|
|
147
|
+
*/
|
|
148
|
+
renderHeader(title, subtitle) {
|
|
149
|
+
const s = this.getStyles();
|
|
150
|
+
const border = s.muted('─'.repeat(Math.max(1, this.termWidth - 1)));
|
|
151
|
+
const lines = [border];
|
|
152
|
+
if (subtitle) {
|
|
153
|
+
lines.push(' ' + s.primaryBold(title) + s.muted(` ${subtitle}`));
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
lines.push(' ' + s.primaryBold(title));
|
|
157
|
+
}
|
|
158
|
+
lines.push('');
|
|
159
|
+
return lines;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Render a standard footer with hints and border.
|
|
163
|
+
*
|
|
164
|
+
* @param hints - Keyboard hints string (e.g., "↑↓ Navigate · Enter Select")
|
|
165
|
+
* @returns Array of lines
|
|
166
|
+
*/
|
|
167
|
+
renderFooter(hints) {
|
|
168
|
+
const s = this.getStyles();
|
|
169
|
+
const border = s.muted('─'.repeat(Math.max(1, this.termWidth - 1)));
|
|
170
|
+
return [border, s.muted(' ' + hints), border];
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Render a border line spanning terminal width.
|
|
174
|
+
*/
|
|
175
|
+
renderBorder() {
|
|
176
|
+
const s = this.getStyles();
|
|
177
|
+
return s.muted('─'.repeat(Math.max(1, this.termWidth - 1)));
|
|
178
|
+
}
|
|
179
|
+
// ===========================================================================
|
|
180
|
+
// Protected Methods - Key Helpers
|
|
181
|
+
// ===========================================================================
|
|
182
|
+
/**
|
|
183
|
+
* Check if key is a close key (escape or 'q').
|
|
184
|
+
*/
|
|
185
|
+
isCloseKey(key) {
|
|
186
|
+
return key.name === 'escape' || key.name === 'q';
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Check if key is Ctrl+C.
|
|
190
|
+
*/
|
|
191
|
+
isInterruptKey(key) {
|
|
192
|
+
return key.ctrl && key.name === 'c';
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Check if key is Enter/Return.
|
|
196
|
+
*/
|
|
197
|
+
isEnterKey(key) {
|
|
198
|
+
return key.name === 'return';
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Check if key is up arrow or 'k' (vim).
|
|
202
|
+
*/
|
|
203
|
+
isUpKey(key) {
|
|
204
|
+
return key.name === 'up' || key.name === 'k';
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Check if key is down arrow or 'j' (vim).
|
|
208
|
+
*/
|
|
209
|
+
isDownKey(key) {
|
|
210
|
+
return key.name === 'down' || key.name === 'j';
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Check if key is left arrow or 'h' (vim).
|
|
214
|
+
*/
|
|
215
|
+
isLeftKey(key) {
|
|
216
|
+
return key.name === 'left' || key.name === 'h';
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Check if key is right arrow or 'l' (vim).
|
|
220
|
+
*/
|
|
221
|
+
isRightKey(key) {
|
|
222
|
+
return key.name === 'right' || key.name === 'l';
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Check if key is Tab.
|
|
226
|
+
*/
|
|
227
|
+
isTabKey(key) {
|
|
228
|
+
return key.name === 'tab' && !key.shift;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Check if key is Shift+Tab.
|
|
232
|
+
*/
|
|
233
|
+
isShiftTabKey(key) {
|
|
234
|
+
return key.name === 'tab' && key.shift;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Get number if key is 1-9.
|
|
238
|
+
*/
|
|
239
|
+
getNumberKey(key) {
|
|
240
|
+
const num = parseInt(key.name, 10);
|
|
241
|
+
if (num >= 1 && num <= 9) {
|
|
242
|
+
return num;
|
|
243
|
+
}
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Overlay Class
|
|
3
|
+
*
|
|
4
|
+
* Abstract base class for all overlays. Provides:
|
|
5
|
+
* - Lifecycle management (setup, cleanup)
|
|
6
|
+
* - Rendering infrastructure (frame, header, footer)
|
|
7
|
+
* - State management
|
|
8
|
+
* - Common key handling patterns
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* ```typescript
|
|
12
|
+
* interface MyState extends OverlayState {
|
|
13
|
+
* selectedIndex: number;
|
|
14
|
+
* }
|
|
15
|
+
*
|
|
16
|
+
* class MyOverlay extends BaseOverlay<MyState, string | null> {
|
|
17
|
+
* constructor() {
|
|
18
|
+
* super({ lineCount: 0, maxLineCount: 0, selectedIndex: 0 });
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* render(): string[] {
|
|
22
|
+
* return [
|
|
23
|
+
* ...this.renderHeader('My Overlay'),
|
|
24
|
+
* ' Content here...',
|
|
25
|
+
* ...this.renderFooter('↑↓ Navigate · Enter Select · q/Esc Cancel')
|
|
26
|
+
* ];
|
|
27
|
+
* }
|
|
28
|
+
*
|
|
29
|
+
* handleKey(data: Buffer): void {
|
|
30
|
+
* if (isClose(data)) { this.close(null); return; }
|
|
31
|
+
* // ... handle keys
|
|
32
|
+
* }
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* // Export function for backward compatibility
|
|
36
|
+
* export async function showMyOverlay(): Promise<string | null> {
|
|
37
|
+
* return new MyOverlay().show();
|
|
38
|
+
* }
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
import { getStyles } from '../../themes/index.js';
|
|
42
|
+
import { OverlayLifecycle } from './overlay-lifecycle.js';
|
|
43
|
+
import type { OverlayState, OverlayConfig } from './overlay-types.js';
|
|
44
|
+
export type { OverlayState, OverlayConfig };
|
|
45
|
+
/**
|
|
46
|
+
* Abstract base class for overlays.
|
|
47
|
+
*
|
|
48
|
+
* @template TState - State interface (must extend OverlayState)
|
|
49
|
+
* @template TResult - Return type when overlay closes
|
|
50
|
+
*/
|
|
51
|
+
export declare abstract class BaseOverlay<TState extends OverlayState, TResult = void> {
|
|
52
|
+
/** Current overlay state */
|
|
53
|
+
protected state: TState;
|
|
54
|
+
/** Lifecycle manager */
|
|
55
|
+
protected lifecycle: OverlayLifecycle;
|
|
56
|
+
/** Overlay configuration */
|
|
57
|
+
protected config: OverlayConfig;
|
|
58
|
+
/** Theme styles - lazily initialized */
|
|
59
|
+
private _styles;
|
|
60
|
+
/** Promise resolver for show() */
|
|
61
|
+
private resolvePromise;
|
|
62
|
+
/**
|
|
63
|
+
* Create a new overlay instance.
|
|
64
|
+
*
|
|
65
|
+
* @param initialState - Initial state (must include lineCount: 0, maxLineCount: 0)
|
|
66
|
+
* @param config - Optional configuration
|
|
67
|
+
*/
|
|
68
|
+
constructor(initialState: TState, config?: OverlayConfig);
|
|
69
|
+
/**
|
|
70
|
+
* Get theme styles (cached).
|
|
71
|
+
*/
|
|
72
|
+
protected get styles(): ReturnType<typeof getStyles>;
|
|
73
|
+
/**
|
|
74
|
+
* Show the overlay and wait for it to close.
|
|
75
|
+
* This is the main entry point.
|
|
76
|
+
*
|
|
77
|
+
* @returns Promise that resolves when the overlay closes
|
|
78
|
+
*/
|
|
79
|
+
show(): Promise<TResult>;
|
|
80
|
+
/**
|
|
81
|
+
* Close the overlay with a result.
|
|
82
|
+
*
|
|
83
|
+
* @param result - Value to return from show() (optional for void result type)
|
|
84
|
+
*/
|
|
85
|
+
protected close(result?: TResult): void;
|
|
86
|
+
/**
|
|
87
|
+
* Re-render the overlay after state changes.
|
|
88
|
+
* Call this after modifying state.
|
|
89
|
+
*/
|
|
90
|
+
protected update(): void;
|
|
91
|
+
/**
|
|
92
|
+
* Reset render state after a screen change.
|
|
93
|
+
* Call this after pushing/popping screens that may have cleared the screen.
|
|
94
|
+
* This ensures the next render starts fresh without trying to restore old cursor positions.
|
|
95
|
+
*/
|
|
96
|
+
protected resetRenderState(): void;
|
|
97
|
+
/**
|
|
98
|
+
* Render the full frame (clear + render + pad).
|
|
99
|
+
* Returns the number of PHYSICAL lines rendered.
|
|
100
|
+
*
|
|
101
|
+
* IMPORTANT: Tracks physical lines (accounting for wrapped content)
|
|
102
|
+
* not logical lines (array entries). This ensures proper clearing
|
|
103
|
+
* when navigating between screens of different heights.
|
|
104
|
+
*/
|
|
105
|
+
protected renderFrame(): number;
|
|
106
|
+
/**
|
|
107
|
+
* Render a standard header with border and title.
|
|
108
|
+
*
|
|
109
|
+
* @param title - Main title
|
|
110
|
+
* @param subtitle - Optional subtitle (shown in muted)
|
|
111
|
+
* @returns Array of lines
|
|
112
|
+
*/
|
|
113
|
+
protected renderHeader(title: string, subtitle?: string): string[];
|
|
114
|
+
/**
|
|
115
|
+
* Render a standard footer with hints and border.
|
|
116
|
+
*
|
|
117
|
+
* @param hints - Keyboard hints string (e.g., "↑↓ Navigate · Enter Select")
|
|
118
|
+
* @returns Array of lines
|
|
119
|
+
*/
|
|
120
|
+
protected renderFooter(hints: string): string[];
|
|
121
|
+
/**
|
|
122
|
+
* Render a border line spanning terminal width.
|
|
123
|
+
*/
|
|
124
|
+
protected renderBorder(): string;
|
|
125
|
+
/**
|
|
126
|
+
* Render the overlay content.
|
|
127
|
+
* Called automatically by renderFrame().
|
|
128
|
+
*
|
|
129
|
+
* @returns Array of lines to render
|
|
130
|
+
*/
|
|
131
|
+
abstract render(): string[];
|
|
132
|
+
/**
|
|
133
|
+
* Handle a key press.
|
|
134
|
+
* Called automatically when the user presses a key.
|
|
135
|
+
*
|
|
136
|
+
* Common pattern:
|
|
137
|
+
* ```typescript
|
|
138
|
+
* handleKey(data: Buffer): void {
|
|
139
|
+
* // Global close keys
|
|
140
|
+
* if (isClose(data)) {
|
|
141
|
+
* this.close(null);
|
|
142
|
+
* return;
|
|
143
|
+
* }
|
|
144
|
+
*
|
|
145
|
+
* // Feature-specific key handling
|
|
146
|
+
* // ...
|
|
147
|
+
*
|
|
148
|
+
* // Re-render if state changed
|
|
149
|
+
* this.update();
|
|
150
|
+
* }
|
|
151
|
+
* ```
|
|
152
|
+
*
|
|
153
|
+
* @param data - Raw key buffer
|
|
154
|
+
*/
|
|
155
|
+
abstract handleKey(data: Buffer): void;
|
|
156
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Overlay Class
|
|
3
|
+
*
|
|
4
|
+
* Abstract base class for all overlays. Provides:
|
|
5
|
+
* - Lifecycle management (setup, cleanup)
|
|
6
|
+
* - Rendering infrastructure (frame, header, footer)
|
|
7
|
+
* - State management
|
|
8
|
+
* - Common key handling patterns
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* ```typescript
|
|
12
|
+
* interface MyState extends OverlayState {
|
|
13
|
+
* selectedIndex: number;
|
|
14
|
+
* }
|
|
15
|
+
*
|
|
16
|
+
* class MyOverlay extends BaseOverlay<MyState, string | null> {
|
|
17
|
+
* constructor() {
|
|
18
|
+
* super({ lineCount: 0, maxLineCount: 0, selectedIndex: 0 });
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* render(): string[] {
|
|
22
|
+
* return [
|
|
23
|
+
* ...this.renderHeader('My Overlay'),
|
|
24
|
+
* ' Content here...',
|
|
25
|
+
* ...this.renderFooter('↑↓ Navigate · Enter Select · q/Esc Cancel')
|
|
26
|
+
* ];
|
|
27
|
+
* }
|
|
28
|
+
*
|
|
29
|
+
* handleKey(data: Buffer): void {
|
|
30
|
+
* if (isClose(data)) { this.close(null); return; }
|
|
31
|
+
* // ... handle keys
|
|
32
|
+
* }
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* // Export function for backward compatibility
|
|
36
|
+
* export async function showMyOverlay(): Promise<string | null> {
|
|
37
|
+
* return new MyOverlay().show();
|
|
38
|
+
* }
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
import * as terminal from '../terminal.js';
|
|
42
|
+
import { getStyles } from '../../themes/index.js';
|
|
43
|
+
import { OverlayLifecycle } from './overlay-lifecycle.js';
|
|
44
|
+
import { debugLog } from '../../utils/debug-log.js';
|
|
45
|
+
// =============================================================================
|
|
46
|
+
// BaseOverlay Abstract Class
|
|
47
|
+
// =============================================================================
|
|
48
|
+
/**
|
|
49
|
+
* Abstract base class for overlays.
|
|
50
|
+
*
|
|
51
|
+
* @template TState - State interface (must extend OverlayState)
|
|
52
|
+
* @template TResult - Return type when overlay closes
|
|
53
|
+
*/
|
|
54
|
+
export class BaseOverlay {
|
|
55
|
+
/** Current overlay state */
|
|
56
|
+
state;
|
|
57
|
+
/** Lifecycle manager */
|
|
58
|
+
lifecycle;
|
|
59
|
+
/** Overlay configuration */
|
|
60
|
+
config;
|
|
61
|
+
/** Theme styles - lazily initialized */
|
|
62
|
+
_styles = null;
|
|
63
|
+
/** Promise resolver for show() */
|
|
64
|
+
resolvePromise = null;
|
|
65
|
+
/**
|
|
66
|
+
* Create a new overlay instance.
|
|
67
|
+
*
|
|
68
|
+
* @param initialState - Initial state (must include lineCount: 0, maxLineCount: 0)
|
|
69
|
+
* @param config - Optional configuration
|
|
70
|
+
*/
|
|
71
|
+
constructor(initialState, config = {}) {
|
|
72
|
+
this.state = initialState;
|
|
73
|
+
this.config = config;
|
|
74
|
+
this.lifecycle = new OverlayLifecycle(config, (data) => {
|
|
75
|
+
this.handleKey(data);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
// ===========================================================================
|
|
79
|
+
// Protected Getters
|
|
80
|
+
// ===========================================================================
|
|
81
|
+
/**
|
|
82
|
+
* Get theme styles (cached).
|
|
83
|
+
*/
|
|
84
|
+
get styles() {
|
|
85
|
+
if (!this._styles) {
|
|
86
|
+
this._styles = getStyles();
|
|
87
|
+
}
|
|
88
|
+
return this._styles;
|
|
89
|
+
}
|
|
90
|
+
// ===========================================================================
|
|
91
|
+
// Public Entry Point
|
|
92
|
+
// ===========================================================================
|
|
93
|
+
/**
|
|
94
|
+
* Show the overlay and wait for it to close.
|
|
95
|
+
* This is the main entry point.
|
|
96
|
+
*
|
|
97
|
+
* @returns Promise that resolves when the overlay closes
|
|
98
|
+
*/
|
|
99
|
+
async show() {
|
|
100
|
+
debugLog('BaseOverlay:show', 'start');
|
|
101
|
+
// Setup lifecycle FIRST (pause footer, raw mode, hide cursor)
|
|
102
|
+
// This is done HERE (not in REPL) so constructor I/O doesn't cause blank screen
|
|
103
|
+
debugLog('BaseOverlay:show', 'calling lifecycle.setup');
|
|
104
|
+
this.lifecycle.setup();
|
|
105
|
+
debugLog('BaseOverlay:show', 'lifecycle.setup done');
|
|
106
|
+
// Save cursor position for render updates
|
|
107
|
+
terminal.saveCursor();
|
|
108
|
+
debugLog('BaseOverlay:show', 'cursor saved');
|
|
109
|
+
// Initial render
|
|
110
|
+
debugLog('BaseOverlay:show', 'calling renderFrame');
|
|
111
|
+
this.state.lineCount = this.renderFrame();
|
|
112
|
+
this.state.maxLineCount = this.state.lineCount;
|
|
113
|
+
debugLog('BaseOverlay:show', `renderFrame done, ${String(this.state.lineCount)} lines`);
|
|
114
|
+
// Return promise that resolves when closed
|
|
115
|
+
return new Promise((resolve) => {
|
|
116
|
+
this.resolvePromise = resolve;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
// ===========================================================================
|
|
120
|
+
// Protected Methods - Lifecycle
|
|
121
|
+
// ===========================================================================
|
|
122
|
+
/**
|
|
123
|
+
* Close the overlay with a result.
|
|
124
|
+
*
|
|
125
|
+
* @param result - Value to return from show() (optional for void result type)
|
|
126
|
+
*/
|
|
127
|
+
close(result) {
|
|
128
|
+
this.lifecycle.cleanup(this.state.maxLineCount);
|
|
129
|
+
if (this.resolvePromise) {
|
|
130
|
+
this.resolvePromise(result);
|
|
131
|
+
this.resolvePromise = null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Re-render the overlay after state changes.
|
|
136
|
+
* Call this after modifying state.
|
|
137
|
+
*/
|
|
138
|
+
update() {
|
|
139
|
+
this.state.lineCount = this.renderFrame();
|
|
140
|
+
this.state.maxLineCount = Math.max(this.state.maxLineCount, this.state.lineCount);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Reset render state after a screen change.
|
|
144
|
+
* Call this after pushing/popping screens that may have cleared the screen.
|
|
145
|
+
* This ensures the next render starts fresh without trying to restore old cursor positions.
|
|
146
|
+
*/
|
|
147
|
+
resetRenderState() {
|
|
148
|
+
// Reset line tracking so renderFrame() doesn't try to restore cursor
|
|
149
|
+
this.state.lineCount = 0;
|
|
150
|
+
this.state.maxLineCount = 0;
|
|
151
|
+
// Explicitly move cursor to home and save position
|
|
152
|
+
// This ensures we're at (0,0) regardless of what clearScreen() did
|
|
153
|
+
process.stdout.write('\x1b[H');
|
|
154
|
+
terminal.saveCursor();
|
|
155
|
+
}
|
|
156
|
+
// ===========================================================================
|
|
157
|
+
// Protected Methods - Rendering
|
|
158
|
+
// ===========================================================================
|
|
159
|
+
/**
|
|
160
|
+
* Render the full frame (clear + render + pad).
|
|
161
|
+
* Returns the number of PHYSICAL lines rendered.
|
|
162
|
+
*
|
|
163
|
+
* IMPORTANT: Tracks physical lines (accounting for wrapped content)
|
|
164
|
+
* not logical lines (array entries). This ensures proper clearing
|
|
165
|
+
* when navigating between screens of different heights.
|
|
166
|
+
*/
|
|
167
|
+
renderFrame() {
|
|
168
|
+
// Get content from subclass
|
|
169
|
+
const lines = this.render();
|
|
170
|
+
const termWidth = terminal.getTerminalWidth();
|
|
171
|
+
// Calculate physical lines of content (accounts for wrapped lines)
|
|
172
|
+
let physicalLines = terminal.totalPhysicalLines(lines, termWidth);
|
|
173
|
+
// Position cursor for render
|
|
174
|
+
if (this.state.lineCount > 0) {
|
|
175
|
+
// Normal re-render: restore cursor to saved position, clear to end
|
|
176
|
+
terminal.restoreCursor();
|
|
177
|
+
terminal.clearToEndOfScreen();
|
|
178
|
+
}
|
|
179
|
+
// Note: when lineCount is 0, cursor should already be at correct position
|
|
180
|
+
// (either from initial show() or from resetRenderState() after clearScreen)
|
|
181
|
+
// Pad to minimum height for consistent sizing
|
|
182
|
+
const minHeight = this.config.minHeight ?? 0;
|
|
183
|
+
while (physicalLines < minHeight) {
|
|
184
|
+
lines.push('');
|
|
185
|
+
physicalLines++;
|
|
186
|
+
}
|
|
187
|
+
// Also pad to previous max to prevent shrinking
|
|
188
|
+
while (physicalLines < this.state.maxLineCount) {
|
|
189
|
+
lines.push('');
|
|
190
|
+
physicalLines++;
|
|
191
|
+
}
|
|
192
|
+
// Render
|
|
193
|
+
terminal.write(lines.join('\n'));
|
|
194
|
+
return physicalLines;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Render a standard header with border and title.
|
|
198
|
+
*
|
|
199
|
+
* @param title - Main title
|
|
200
|
+
* @param subtitle - Optional subtitle (shown in muted)
|
|
201
|
+
* @returns Array of lines
|
|
202
|
+
*/
|
|
203
|
+
renderHeader(title, subtitle) {
|
|
204
|
+
const cols = terminal.getTerminalWidth();
|
|
205
|
+
const border = this.styles.muted('─'.repeat(Math.max(1, cols - 1)));
|
|
206
|
+
const lines = [border];
|
|
207
|
+
if (subtitle) {
|
|
208
|
+
lines.push(' ' + this.styles.primaryBold(title) + this.styles.muted(` ${subtitle}`));
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
lines.push(' ' + this.styles.primaryBold(title));
|
|
212
|
+
}
|
|
213
|
+
lines.push('');
|
|
214
|
+
return lines;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Render a standard footer with hints and border.
|
|
218
|
+
*
|
|
219
|
+
* @param hints - Keyboard hints string (e.g., "↑↓ Navigate · Enter Select")
|
|
220
|
+
* @returns Array of lines
|
|
221
|
+
*/
|
|
222
|
+
renderFooter(hints) {
|
|
223
|
+
const cols = terminal.getTerminalWidth();
|
|
224
|
+
const border = this.styles.muted('─'.repeat(Math.max(1, cols - 1)));
|
|
225
|
+
return [
|
|
226
|
+
'',
|
|
227
|
+
this.styles.muted(' ' + hints),
|
|
228
|
+
border,
|
|
229
|
+
];
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Render a border line spanning terminal width.
|
|
233
|
+
*/
|
|
234
|
+
renderBorder() {
|
|
235
|
+
const cols = terminal.getTerminalWidth();
|
|
236
|
+
return this.styles.muted('─'.repeat(Math.max(1, cols - 1)));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Overlay Lifecycle Manager
|
|
3
|
+
*
|
|
4
|
+
* Handles the common setup and cleanup operations for all overlays:
|
|
5
|
+
* - Raw mode management
|
|
6
|
+
* - Cursor visibility
|
|
7
|
+
* - Event listener setup/teardown
|
|
8
|
+
* - Line clearing on close
|
|
9
|
+
*/
|
|
10
|
+
import type { OverlayConfig } from './overlay-types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Callback for key data events.
|
|
13
|
+
*/
|
|
14
|
+
export type KeyHandler = (data: Buffer) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Manages the lifecycle of an overlay.
|
|
17
|
+
*
|
|
18
|
+
* Usage:
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const lifecycle = new OverlayLifecycle(config, (data) => handleKey(data));
|
|
21
|
+
*
|
|
22
|
+
* // Setup - call before first render
|
|
23
|
+
* lifecycle.setup();
|
|
24
|
+
*
|
|
25
|
+
* // ... overlay logic ...
|
|
26
|
+
*
|
|
27
|
+
* // Cleanup - call when closing
|
|
28
|
+
* lifecycle.cleanup(lineCount);
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare class OverlayLifecycle {
|
|
32
|
+
private readonly config;
|
|
33
|
+
private readonly onKey;
|
|
34
|
+
private wasRawMode;
|
|
35
|
+
private dataHandler;
|
|
36
|
+
private isActive;
|
|
37
|
+
constructor(config: OverlayConfig, onKey: KeyHandler);
|
|
38
|
+
/**
|
|
39
|
+
* Setup the overlay for interaction.
|
|
40
|
+
* Call this before the first render.
|
|
41
|
+
*/
|
|
42
|
+
setup(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Cleanup after overlay closes.
|
|
45
|
+
* Restores terminal state and removes event listeners.
|
|
46
|
+
*
|
|
47
|
+
* @param lineCount - Number of lines to clear (typically maxLineCount)
|
|
48
|
+
*/
|
|
49
|
+
cleanup(lineCount: number): void;
|
|
50
|
+
/**
|
|
51
|
+
* Check if the lifecycle is currently active.
|
|
52
|
+
*/
|
|
53
|
+
isSetUp(): boolean;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Create a standard overlay setup without using the class.
|
|
57
|
+
* Returns a cleanup function.
|
|
58
|
+
*
|
|
59
|
+
* This is useful for simple overlays that don't need the full class.
|
|
60
|
+
*
|
|
61
|
+
* @param onKey - Key handler callback
|
|
62
|
+
* @param config - Optional configuration
|
|
63
|
+
* @returns Cleanup function
|
|
64
|
+
*/
|
|
65
|
+
export declare function setupOverlay(onKey: KeyHandler, config?: OverlayConfig): (lineCount?: number) => void;
|