@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.
Files changed (315) hide show
  1. package/README.md +30 -12
  2. package/dist/agent.d.ts +74 -1
  3. package/dist/agent.js +259 -76
  4. package/dist/anchors/index.d.ts +9 -0
  5. package/dist/anchors/index.js +9 -0
  6. package/dist/anchors/project-anchors.d.ts +79 -0
  7. package/dist/anchors/project-anchors.js +202 -0
  8. package/dist/commands/handler-types.d.ts +68 -0
  9. package/dist/commands/handler-types.js +8 -0
  10. package/dist/commands/handlers/agent-commands.d.ts +13 -0
  11. package/dist/commands/handlers/agent-commands.js +305 -0
  12. package/dist/commands/handlers/design-commands.d.ts +15 -0
  13. package/dist/commands/handlers/design-commands.js +334 -0
  14. package/dist/commands/handlers/index.d.ts +20 -0
  15. package/dist/commands/handlers/index.js +43 -0
  16. package/dist/commands/handlers/overlay-commands.d.ts +21 -0
  17. package/dist/commands/handlers/overlay-commands.js +287 -0
  18. package/dist/commands/handlers/project-commands.d.ts +11 -0
  19. package/dist/commands/handlers/project-commands.js +167 -0
  20. package/dist/commands/handlers/simple-commands.d.ts +19 -0
  21. package/dist/commands/handlers/simple-commands.js +144 -0
  22. package/dist/commands/index.d.ts +2 -1
  23. package/dist/commands/registry.d.ts +50 -0
  24. package/dist/commands/registry.js +75 -0
  25. package/dist/commands-v2/handlers/context.d.ts +13 -0
  26. package/dist/commands-v2/handlers/context.js +348 -0
  27. package/dist/commands-v2/handlers/core.d.ts +13 -0
  28. package/dist/commands-v2/handlers/core.js +165 -0
  29. package/dist/commands-v2/handlers/debug.d.ts +11 -0
  30. package/dist/commands-v2/handlers/debug.js +159 -0
  31. package/dist/commands-v2/handlers/index.d.ts +12 -0
  32. package/dist/commands-v2/handlers/index.js +24 -0
  33. package/dist/commands-v2/handlers/project.d.ts +22 -0
  34. package/dist/commands-v2/handlers/project.js +814 -0
  35. package/dist/commands-v2/handlers/settings.d.ts +15 -0
  36. package/dist/commands-v2/handlers/settings.js +235 -0
  37. package/dist/commands-v2/index.d.ts +13 -0
  38. package/dist/commands-v2/index.js +15 -0
  39. package/dist/commands-v2/registry.d.ts +37 -0
  40. package/dist/commands-v2/registry.js +80 -0
  41. package/dist/commands-v2/types.d.ts +75 -0
  42. package/dist/commands-v2/types.js +7 -0
  43. package/dist/commands.js +110 -7
  44. package/dist/index.js +288 -29
  45. package/dist/input-handlers/index.d.ts +7 -0
  46. package/dist/input-handlers/index.js +7 -0
  47. package/dist/input-handlers/memory-handler.d.ts +26 -0
  48. package/dist/input-handlers/memory-handler.js +68 -0
  49. package/dist/repl-helpers.d.ts +63 -0
  50. package/dist/repl-helpers.js +318 -0
  51. package/dist/repl-v2.d.ts +155 -0
  52. package/dist/repl-v2.js +774 -0
  53. package/dist/repl.d.ts +32 -4
  54. package/dist/repl.js +250 -977
  55. package/dist/settings/index.d.ts +23 -0
  56. package/dist/settings/index.js +48 -0
  57. package/dist/settings/paths.d.ts +110 -0
  58. package/dist/settings/paths.js +264 -0
  59. package/dist/templates/compilr-md.js +7 -4
  60. package/dist/templates/index.js +3 -4
  61. package/dist/themes/colors.js +3 -1
  62. package/dist/themes/registry.d.ts +5 -36
  63. package/dist/themes/registry.js +11 -95
  64. package/dist/themes/types.d.ts +3 -38
  65. package/dist/themes/types.js +2 -2
  66. package/dist/tools/anchor-tools.d.ts +31 -0
  67. package/dist/tools/anchor-tools.js +255 -0
  68. package/dist/tools/backlog-wrappers.d.ts +54 -0
  69. package/dist/tools/backlog-wrappers.js +338 -0
  70. package/dist/tools/backlog.js +1 -1
  71. package/dist/tools/db-tools.d.ts +65 -0
  72. package/dist/tools/db-tools.js +19 -0
  73. package/dist/tools/document-db.d.ts +43 -0
  74. package/dist/tools/document-db.js +220 -0
  75. package/dist/tools/project-db.d.ts +102 -0
  76. package/dist/tools/project-db.js +370 -0
  77. package/dist/tools/workitem-db.d.ts +103 -0
  78. package/dist/tools/workitem-db.js +549 -0
  79. package/dist/tools.js +13 -3
  80. package/dist/ui/agents-overlay-v2.d.ts +43 -0
  81. package/dist/ui/agents-overlay-v2.js +809 -0
  82. package/dist/ui/agents-overlay.d.ts +5 -5
  83. package/dist/ui/agents-overlay.js +782 -420
  84. package/dist/ui/anchors-overlay.d.ts +12 -0
  85. package/dist/ui/anchors-overlay.js +775 -0
  86. package/dist/ui/arch-type-overlay.d.ts +1 -6
  87. package/dist/ui/arch-type-overlay.js +175 -203
  88. package/dist/ui/ask-user-overlay-v2.d.ts +26 -0
  89. package/dist/ui/ask-user-overlay-v2.js +555 -0
  90. package/dist/ui/ask-user-overlay.d.ts +2 -2
  91. package/dist/ui/ask-user-overlay.js +443 -535
  92. package/dist/ui/ask-user-simple-overlay-v2.d.ts +25 -0
  93. package/dist/ui/ask-user-simple-overlay-v2.js +215 -0
  94. package/dist/ui/ask-user-simple-overlay.d.ts +2 -2
  95. package/dist/ui/ask-user-simple-overlay.js +182 -209
  96. package/dist/ui/backlog-overlay.d.ts +16 -1
  97. package/dist/ui/backlog-overlay.js +525 -659
  98. package/dist/ui/base/index.d.ts +26 -0
  99. package/dist/ui/base/index.js +33 -0
  100. package/dist/ui/base/inline-overlay-utils.d.ts +217 -0
  101. package/dist/ui/base/inline-overlay-utils.js +320 -0
  102. package/dist/ui/base/inline-overlay.d.ts +159 -0
  103. package/dist/ui/base/inline-overlay.js +257 -0
  104. package/dist/ui/base/key-utils.d.ts +15 -0
  105. package/dist/ui/base/key-utils.js +30 -0
  106. package/dist/ui/base/overlay-base-v2.d.ts +193 -0
  107. package/dist/ui/base/overlay-base-v2.js +246 -0
  108. package/dist/ui/base/overlay-base.d.ts +156 -0
  109. package/dist/ui/base/overlay-base.js +238 -0
  110. package/dist/ui/base/overlay-lifecycle.d.ts +65 -0
  111. package/dist/ui/base/overlay-lifecycle.js +159 -0
  112. package/dist/ui/base/overlay-types.d.ts +185 -0
  113. package/dist/ui/base/overlay-types.js +7 -0
  114. package/dist/ui/base/render-utils.d.ts +8 -0
  115. package/dist/ui/base/render-utils.js +11 -0
  116. package/dist/ui/base/screen-stack.d.ts +148 -0
  117. package/dist/ui/base/screen-stack.js +184 -0
  118. package/dist/ui/base/tabbed-list-overlay-v2.d.ts +103 -0
  119. package/dist/ui/base/tabbed-list-overlay-v2.js +317 -0
  120. package/dist/ui/base/tabbed-list-overlay.d.ts +153 -0
  121. package/dist/ui/base/tabbed-list-overlay.js +369 -0
  122. package/dist/ui/commands-overlay-v2.d.ts +33 -0
  123. package/dist/ui/commands-overlay-v2.js +441 -0
  124. package/dist/ui/commands-overlay.d.ts +7 -2
  125. package/dist/ui/commands-overlay.js +384 -355
  126. package/dist/ui/config-overlay.d.ts +5 -4
  127. package/dist/ui/config-overlay.js +243 -513
  128. package/dist/ui/conversation.d.ts +75 -4
  129. package/dist/ui/conversation.js +374 -161
  130. package/dist/ui/docs-overlay.d.ts +17 -0
  131. package/dist/ui/docs-overlay.js +303 -0
  132. package/dist/ui/ephemeral.d.ts +1 -1
  133. package/dist/ui/ephemeral.js +1 -1
  134. package/dist/ui/features/index.d.ts +34 -0
  135. package/dist/ui/features/index.js +34 -0
  136. package/dist/ui/features/input-feature.d.ts +85 -0
  137. package/dist/ui/features/input-feature.js +238 -0
  138. package/dist/ui/features/list-feature.d.ts +155 -0
  139. package/dist/ui/features/list-feature.js +244 -0
  140. package/dist/ui/features/pagination-feature.d.ts +154 -0
  141. package/dist/ui/features/pagination-feature.js +238 -0
  142. package/dist/ui/features/search-feature.d.ts +148 -0
  143. package/dist/ui/features/search-feature.js +185 -0
  144. package/dist/ui/features/tab-feature.d.ts +194 -0
  145. package/dist/ui/features/tab-feature.js +307 -0
  146. package/dist/ui/footer-v2.d.ts +222 -0
  147. package/dist/ui/footer-v2.js +1349 -0
  148. package/dist/ui/footer.d.ts +107 -0
  149. package/dist/ui/footer.js +359 -67
  150. package/dist/ui/guardrail-overlay.d.ts +29 -0
  151. package/dist/ui/guardrail-overlay.js +145 -0
  152. package/dist/ui/help-overlay-v2.d.ts +34 -0
  153. package/dist/ui/help-overlay-v2.js +309 -0
  154. package/dist/ui/help-overlay.d.ts +16 -0
  155. package/dist/ui/help-overlay.js +316 -0
  156. package/dist/ui/index.d.ts +1 -1
  157. package/dist/ui/index.js +1 -3
  158. package/dist/ui/init-overlay-v2.d.ts +34 -0
  159. package/dist/ui/init-overlay-v2.js +600 -0
  160. package/dist/ui/init-overlay.d.ts +12 -2
  161. package/dist/ui/init-overlay.js +349 -270
  162. package/dist/ui/input-prompt-v2.d.ts +1 -0
  163. package/dist/ui/input-prompt-v2.js +14 -6
  164. package/dist/ui/input-prompt.d.ts +116 -33
  165. package/dist/ui/input-prompt.js +536 -337
  166. package/dist/ui/iteration-limit-overlay-v2.d.ts +21 -0
  167. package/dist/ui/iteration-limit-overlay-v2.js +114 -0
  168. package/dist/ui/iteration-limit-overlay.d.ts +2 -2
  169. package/dist/ui/iteration-limit-overlay.js +92 -128
  170. package/dist/ui/keys-overlay-v2.d.ts +41 -0
  171. package/dist/ui/keys-overlay-v2.js +248 -0
  172. package/dist/ui/keys-overlay.d.ts +1 -0
  173. package/dist/ui/keys-overlay.js +203 -141
  174. package/dist/ui/line-utils.d.ts +88 -0
  175. package/dist/ui/line-utils.js +150 -0
  176. package/dist/ui/live-region.d.ts +161 -0
  177. package/dist/ui/live-region.js +387 -0
  178. package/dist/ui/mascot/expressions.d.ts +32 -0
  179. package/dist/ui/mascot/expressions.js +213 -0
  180. package/dist/ui/mascot/index.d.ts +8 -0
  181. package/dist/ui/mascot/index.js +8 -0
  182. package/dist/ui/mascot/renderer.d.ts +19 -0
  183. package/dist/ui/mascot/renderer.js +97 -0
  184. package/dist/ui/mascot-overlay-v2.d.ts +41 -0
  185. package/dist/ui/mascot-overlay-v2.js +138 -0
  186. package/dist/ui/mascot-overlay.d.ts +21 -0
  187. package/dist/ui/mascot-overlay.js +146 -0
  188. package/dist/ui/model-overlay-v2.d.ts +49 -0
  189. package/dist/ui/model-overlay-v2.js +118 -0
  190. package/dist/ui/model-overlay.d.ts +27 -0
  191. package/dist/ui/model-overlay.js +221 -0
  192. package/dist/ui/model-warning-overlay.js +3 -5
  193. package/dist/ui/new-overlay.d.ts +34 -0
  194. package/dist/ui/new-overlay.js +604 -0
  195. package/dist/ui/overlay/impl/agents-overlay-v2.d.ts +45 -0
  196. package/dist/ui/overlay/impl/agents-overlay-v2.js +825 -0
  197. package/dist/ui/overlay/impl/anchors-overlay-v2.d.ts +47 -0
  198. package/dist/ui/overlay/impl/anchors-overlay-v2.js +783 -0
  199. package/dist/ui/overlay/impl/arch-type-overlay-v2.d.ts +37 -0
  200. package/dist/ui/overlay/impl/arch-type-overlay-v2.js +240 -0
  201. package/dist/ui/overlay/impl/ask-user-overlay-v2.d.ts +72 -0
  202. package/dist/ui/overlay/impl/ask-user-overlay-v2.js +584 -0
  203. package/dist/ui/overlay/impl/ask-user-simple-overlay-v2.d.ts +46 -0
  204. package/dist/ui/overlay/impl/ask-user-simple-overlay-v2.js +204 -0
  205. package/dist/ui/overlay/impl/backlog-overlay-v2.d.ts +49 -0
  206. package/dist/ui/overlay/impl/backlog-overlay-v2.js +642 -0
  207. package/dist/ui/overlay/impl/commands-overlay-v2.d.ts +33 -0
  208. package/dist/ui/overlay/impl/commands-overlay-v2.js +441 -0
  209. package/dist/ui/overlay/impl/config-overlay-v2.d.ts +100 -0
  210. package/dist/ui/overlay/impl/config-overlay-v2.js +654 -0
  211. package/dist/ui/overlay/impl/dashboard-overlay-v2.d.ts +55 -0
  212. package/dist/ui/overlay/impl/dashboard-overlay-v2.js +359 -0
  213. package/dist/ui/overlay/impl/docs-overlay-v2.d.ts +45 -0
  214. package/dist/ui/overlay/impl/docs-overlay-v2.js +114 -0
  215. package/dist/ui/overlay/impl/document-detail-overlay-v2.d.ts +77 -0
  216. package/dist/ui/overlay/impl/document-detail-overlay-v2.js +1071 -0
  217. package/dist/ui/overlay/impl/guardrail-overlay-v2.d.ts +43 -0
  218. package/dist/ui/overlay/impl/guardrail-overlay-v2.js +114 -0
  219. package/dist/ui/overlay/impl/help-overlay-v2.d.ts +34 -0
  220. package/dist/ui/overlay/impl/help-overlay-v2.js +309 -0
  221. package/dist/ui/overlay/impl/init-overlay-v2.d.ts +77 -0
  222. package/dist/ui/overlay/impl/init-overlay-v2.js +593 -0
  223. package/dist/ui/overlay/impl/init-setup-overlay-v2.d.ts +25 -0
  224. package/dist/ui/overlay/impl/init-setup-overlay-v2.js +97 -0
  225. package/dist/ui/overlay/impl/iteration-limit-overlay-v2.d.ts +35 -0
  226. package/dist/ui/overlay/impl/iteration-limit-overlay-v2.js +105 -0
  227. package/dist/ui/overlay/impl/keys-overlay-v2.d.ts +41 -0
  228. package/dist/ui/overlay/impl/keys-overlay-v2.js +248 -0
  229. package/dist/ui/overlay/impl/mascot-overlay-v2.d.ts +41 -0
  230. package/dist/ui/overlay/impl/mascot-overlay-v2.js +138 -0
  231. package/dist/ui/overlay/impl/model-overlay-v2.d.ts +49 -0
  232. package/dist/ui/overlay/impl/model-overlay-v2.js +118 -0
  233. package/dist/ui/overlay/impl/model-warning-overlay-v2.d.ts +46 -0
  234. package/dist/ui/overlay/impl/model-warning-overlay-v2.js +132 -0
  235. package/dist/ui/overlay/impl/new-overlay-v2.d.ts +77 -0
  236. package/dist/ui/overlay/impl/new-overlay-v2.js +593 -0
  237. package/dist/ui/overlay/impl/permission-overlay-v2.d.ts +36 -0
  238. package/dist/ui/overlay/impl/permission-overlay-v2.js +380 -0
  239. package/dist/ui/overlay/impl/projects-overlay-v2.d.ts +36 -0
  240. package/dist/ui/overlay/impl/projects-overlay-v2.js +499 -0
  241. package/dist/ui/overlay/impl/theme-overlay-v2.d.ts +42 -0
  242. package/dist/ui/overlay/impl/theme-overlay-v2.js +135 -0
  243. package/dist/ui/overlay/impl/tools-overlay-v2.d.ts +47 -0
  244. package/dist/ui/overlay/impl/tools-overlay-v2.js +218 -0
  245. package/dist/ui/overlay/impl/tutorial-overlay-v2.d.ts +31 -0
  246. package/dist/ui/overlay/impl/tutorial-overlay-v2.js +1035 -0
  247. package/dist/ui/overlay/impl/workflow-overlay-v2.d.ts +80 -0
  248. package/dist/ui/overlay/impl/workflow-overlay-v2.js +637 -0
  249. package/dist/ui/overlay/index.d.ts +33 -0
  250. package/dist/ui/overlay/index.js +35 -0
  251. package/dist/ui/overlay/key-utils.d.ts +6 -0
  252. package/dist/ui/overlay/key-utils.js +6 -0
  253. package/dist/ui/overlay/overlay-types.d.ts +128 -0
  254. package/dist/ui/overlay/overlay-types.js +22 -0
  255. package/dist/ui/overlay/types.d.ts +135 -0
  256. package/dist/ui/overlay/types.js +22 -0
  257. package/dist/ui/overlays/help-overlay-v2.d.ts +28 -0
  258. package/dist/ui/overlays/help-overlay-v2.js +198 -0
  259. package/dist/ui/overlays/index.d.ts +11 -0
  260. package/dist/ui/overlays/index.js +11 -0
  261. package/dist/ui/overlays.d.ts +0 -4
  262. package/dist/ui/overlays.js +0 -444
  263. package/dist/ui/permission-overlay-v2.d.ts +36 -0
  264. package/dist/ui/permission-overlay-v2.js +380 -0
  265. package/dist/ui/permission-overlay.d.ts +1 -1
  266. package/dist/ui/permission-overlay.js +186 -298
  267. package/dist/ui/projects-overlay.d.ts +19 -0
  268. package/dist/ui/projects-overlay.js +484 -0
  269. package/dist/ui/providers/types.d.ts +178 -0
  270. package/dist/ui/providers/types.js +9 -0
  271. package/dist/ui/render-modes.d.ts +36 -0
  272. package/dist/ui/render-modes.js +44 -0
  273. package/dist/ui/startup-menu.d.ts +36 -0
  274. package/dist/ui/startup-menu.js +236 -0
  275. package/dist/ui/subagent-renderer.d.ts +117 -0
  276. package/dist/ui/subagent-renderer.js +334 -0
  277. package/dist/ui/terminal-codes.d.ts +94 -0
  278. package/dist/ui/terminal-codes.js +124 -0
  279. package/dist/ui/terminal-renderer.d.ts +221 -0
  280. package/dist/ui/terminal-renderer.js +751 -0
  281. package/dist/ui/terminal-ui.d.ts +463 -0
  282. package/dist/ui/terminal-ui.js +2296 -0
  283. package/dist/ui/terminal.d.ts +20 -0
  284. package/dist/ui/terminal.js +72 -0
  285. package/dist/ui/theme-overlay-v2.d.ts +42 -0
  286. package/dist/ui/theme-overlay-v2.js +135 -0
  287. package/dist/ui/theme-overlay.d.ts +24 -0
  288. package/dist/ui/theme-overlay.js +127 -0
  289. package/dist/ui/todo-zone.js +53 -25
  290. package/dist/ui/tool-formatters.d.ts +16 -0
  291. package/dist/ui/tool-formatters.js +516 -0
  292. package/dist/ui/tools-overlay-v2.d.ts +47 -0
  293. package/dist/ui/tools-overlay-v2.js +218 -0
  294. package/dist/ui/tools-overlay.d.ts +10 -2
  295. package/dist/ui/tools-overlay.js +172 -220
  296. package/dist/ui/tutorial-overlay-v2.d.ts +31 -0
  297. package/dist/ui/tutorial-overlay-v2.js +1035 -0
  298. package/dist/ui/tutorial-overlay.d.ts +1 -0
  299. package/dist/ui/tutorial-overlay.js +400 -302
  300. package/dist/ui/workflow-overlay.d.ts +22 -0
  301. package/dist/ui/workflow-overlay.js +636 -0
  302. package/dist/utils/debug-log.d.ts +28 -0
  303. package/dist/utils/debug-log.js +57 -0
  304. package/dist/utils/model-tiers.js +1 -1
  305. package/dist/utils/path-safety.d.ts +56 -0
  306. package/dist/utils/path-safety.js +239 -0
  307. package/dist/workflow/guided-mode-injector.d.ts +42 -0
  308. package/dist/workflow/guided-mode-injector.js +191 -0
  309. package/dist/workflow/index.d.ts +8 -0
  310. package/dist/workflow/index.js +8 -0
  311. package/dist/workflow/step-criteria.d.ts +62 -0
  312. package/dist/workflow/step-criteria.js +150 -0
  313. package/dist/workflow/step-tracker.d.ts +92 -0
  314. package/dist/workflow/step-tracker.js +141 -0
  315. 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;