@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,103 @@
1
+ /**
2
+ * Tabbed List Overlay V2
3
+ *
4
+ * A reusable base for overlays using the new Overlay interface.
5
+ * Provides tabs, pagination, search, and detail views.
6
+ *
7
+ * Key differences from TabbedListOverlay:
8
+ * - Extends BaseOverlayV2 (implements new Overlay interface)
9
+ * - handleKey returns OverlayAction instead of void
10
+ * - Works with TerminalUI's overlay management
11
+ * - Keeps ScreenStack internally for detail screen navigation
12
+ */
13
+ import { BaseOverlayV2 } from './overlay-base-v2.js';
14
+ import { BaseScreen, ScreenStack } from './screen-stack.js';
15
+ import { type PaginationState } from '../features/pagination-feature.js';
16
+ import { type SearchState } from '../features/search-feature.js';
17
+ import type { TabDefinition, TabState } from './overlay-types.js';
18
+ import type { RenderContext, OverlayAction, KeyEvent } from '../overlay/index.js';
19
+ import type { ThemeStyleFunctions } from '../../themes/index.js';
20
+ /**
21
+ * Configuration for a TabbedListOverlayV2.
22
+ */
23
+ export interface TabbedListConfigV2<TItem> {
24
+ /** Title shown in header */
25
+ title: string;
26
+ /** Tab definitions */
27
+ tabs: TabDefinition[];
28
+ /** All items (will be filtered by tab and search) */
29
+ items: TItem[];
30
+ /** Number of items per page (default: 12) */
31
+ pageSize?: number;
32
+ /**
33
+ * Filter items by tab.
34
+ * Return true to include item in the current tab.
35
+ */
36
+ filterByTab: (item: TItem, tabId: string) => boolean;
37
+ /**
38
+ * Get searchable text from an item.
39
+ * Used for search filtering.
40
+ */
41
+ getSearchText: (item: TItem) => string;
42
+ /**
43
+ * Render an item as a string line.
44
+ */
45
+ renderItem: (item: TItem, isSelected: boolean, styles: ThemeStyleFunctions) => string;
46
+ /** Show count badge in header (default: true) */
47
+ showCount?: boolean;
48
+ /** Custom empty state message */
49
+ emptyMessage?: string;
50
+ /** Custom no-results message */
51
+ noResultsMessage?: string;
52
+ /** Custom footer hints */
53
+ footerHints?: (searchMode: boolean) => string;
54
+ /**
55
+ * Render preview content for the selected item.
56
+ * Displayed between the list and footer.
57
+ */
58
+ renderSelectedPreview?: (item: TItem, styles: ThemeStyleFunctions) => string[];
59
+ }
60
+ /**
61
+ * State for TabbedListOverlayV2.
62
+ */
63
+ export interface TabbedListStateV2<TItem> extends TabState, PaginationState, SearchState {
64
+ /** All items */
65
+ items: TItem[];
66
+ /** Filtered items (by tab and search) */
67
+ filteredItems: TItem[];
68
+ /** Whether search mode is active */
69
+ searchMode: boolean;
70
+ }
71
+ /**
72
+ * Abstract base class for tabbed list overlays using the new Overlay interface.
73
+ */
74
+ export declare abstract class TabbedListOverlayV2<TItem, TResult = void> extends BaseOverlayV2<TabbedListStateV2<TItem>, TResult> {
75
+ abstract readonly type: 'fullscreen' | 'inline';
76
+ abstract readonly id: string;
77
+ protected readonly screenStack: ScreenStack;
78
+ protected readonly listConfig: TabbedListConfigV2<TItem>;
79
+ constructor(config: TabbedListConfigV2<TItem>);
80
+ /**
81
+ * Override to create a detail screen for an item.
82
+ * Return null to disable detail view.
83
+ */
84
+ protected createDetailScreen(_item: TItem): BaseScreen | null;
85
+ /**
86
+ * Override to handle item selection.
87
+ * Return a result to close the overlay, or undefined to fall back to detail screen.
88
+ */
89
+ protected onItemSelected(_item: TItem): TResult | undefined;
90
+ protected renderContent(_context: RenderContext): string[];
91
+ /**
92
+ * Override render to use the effective minHeight (max of overlay and current screen).
93
+ */
94
+ render(context: RenderContext): {
95
+ lines: string[];
96
+ minHeight?: number;
97
+ };
98
+ handleKey(key: KeyEvent): OverlayAction<TResult>;
99
+ /**
100
+ * Get the currently selected item.
101
+ */
102
+ protected getSelectedItem(): TItem | undefined;
103
+ }
@@ -0,0 +1,317 @@
1
+ /**
2
+ * Tabbed List Overlay V2
3
+ *
4
+ * A reusable base for overlays using the new Overlay interface.
5
+ * Provides tabs, pagination, search, and detail views.
6
+ *
7
+ * Key differences from TabbedListOverlay:
8
+ * - Extends BaseOverlayV2 (implements new Overlay interface)
9
+ * - handleKey returns OverlayAction instead of void
10
+ * - Works with TerminalUI's overlay management
11
+ * - Keeps ScreenStack internally for detail screen navigation
12
+ */
13
+ // Terminal width is passed via getWidth callback
14
+ import { BaseOverlayV2 } from './overlay-base-v2.js';
15
+ import { BaseScreen, ScreenStack } from './screen-stack.js';
16
+ import { TabFeature } from '../features/tab-feature.js';
17
+ import { PaginationFeature } from '../features/pagination-feature.js';
18
+ import { SearchFeature } from '../features/search-feature.js';
19
+ import { isCtrlC, isClose, isEscape, isSlash, isEnter } from './key-utils.js';
20
+ import { renderBorder } from './render-utils.js';
21
+ // =============================================================================
22
+ // List Screen V2 (Main View)
23
+ // =============================================================================
24
+ /**
25
+ * The main list screen for TabbedListOverlayV2.
26
+ */
27
+ class ListScreenV2 extends BaseScreen {
28
+ config;
29
+ state;
30
+ getStyles;
31
+ getWidth;
32
+ createDetailScreen;
33
+ onItemSelected;
34
+ tabFeature;
35
+ pagination;
36
+ searchFeature;
37
+ constructor(config, state, getStyles, getWidth, createDetailScreen, onItemSelected) {
38
+ super();
39
+ this.config = config;
40
+ this.state = state;
41
+ this.getStyles = getStyles;
42
+ this.getWidth = getWidth;
43
+ this.createDetailScreen = createDetailScreen;
44
+ this.onItemSelected = onItemSelected;
45
+ this.tabFeature = new TabFeature(config.tabs, () => {
46
+ this.onTabChange();
47
+ });
48
+ this.pagination = new PaginationFeature({
49
+ pageSize: config.pageSize ?? 12,
50
+ });
51
+ this.searchFeature = new SearchFeature({
52
+ onSearch: () => {
53
+ this.applyFilters();
54
+ },
55
+ escapeClearsSearch: false,
56
+ });
57
+ }
58
+ onTabChange() {
59
+ this.state.selectedIndex = 0;
60
+ this.state.scrollOffset = 0;
61
+ this.state.searchMode = false;
62
+ this.state.searchQuery = '';
63
+ this.applyFilters();
64
+ }
65
+ applyFilters() {
66
+ const tabId = this.tabFeature.getCurrentTabId(this.state);
67
+ let filtered = this.state.items.filter((item) => this.config.filterByTab(item, tabId));
68
+ if (this.state.searchQuery) {
69
+ filtered = this.searchFeature.filterItems(filtered, this.state, this.config.getSearchText);
70
+ }
71
+ this.state.filteredItems = filtered;
72
+ this.state.selectedIndex = 0;
73
+ this.state.scrollOffset = 0;
74
+ }
75
+ render() {
76
+ const s = this.getStyles();
77
+ const cols = this.getWidth();
78
+ const border = renderBorder(cols, s);
79
+ const pageSize = this.config.pageSize ?? 12;
80
+ const lines = [];
81
+ // Header
82
+ lines.push(border);
83
+ const showCount = this.config.showCount ?? true;
84
+ const countBadge = showCount
85
+ ? s.muted(` [${String(this.state.filteredItems.length)}/${String(this.state.items.length)}]`)
86
+ : '';
87
+ lines.push(` ${s.primaryBold(this.config.title)}${countBadge}`);
88
+ lines.push('');
89
+ // Tab bar with hint
90
+ const tabBar = this.tabFeature.renderTabs(this.state, s);
91
+ const tabHint = s.muted(`(${this.tabFeature.renderHints()})`);
92
+ lines.push(` ${tabBar} ${tabHint}`);
93
+ lines.push('');
94
+ // Search box (if in search mode)
95
+ if (this.state.searchMode) {
96
+ const query = this.state.searchQuery;
97
+ lines.push(` ${s.muted('Search:')} ${query ? s.primary(query) : ''}█`);
98
+ lines.push('');
99
+ }
100
+ // Items
101
+ const visibleItems = this.pagination.getVisibleItems(this.state, this.state.filteredItems);
102
+ if (visibleItems.length === 0) {
103
+ const msg = this.state.searchQuery
104
+ ? (this.config.noResultsMessage ?? 'No items match the search.')
105
+ : (this.config.emptyMessage ?? 'No items available.');
106
+ lines.push(` ${s.muted(msg)}`);
107
+ for (let i = 0; i < pageSize - 1; i++) {
108
+ lines.push('');
109
+ }
110
+ }
111
+ else {
112
+ for (let i = 0; i < visibleItems.length; i++) {
113
+ const item = visibleItems[i];
114
+ const absoluteIndex = this.state.scrollOffset + i;
115
+ const isSelected = absoluteIndex === this.state.selectedIndex;
116
+ lines.push(this.config.renderItem(item, isSelected, s));
117
+ }
118
+ // Pad to fixed height
119
+ for (let i = visibleItems.length; i < pageSize; i++) {
120
+ lines.push('');
121
+ }
122
+ }
123
+ // Pagination indicator
124
+ lines.push('');
125
+ const indicator = this.pagination.renderIndicator(this.state, this.state.filteredItems.length, s);
126
+ if (indicator) {
127
+ lines.push(indicator);
128
+ }
129
+ // Selected item preview
130
+ if (this.config.renderSelectedPreview && visibleItems.length > 0) {
131
+ const selectedItem = this.state.filteredItems[this.state.selectedIndex];
132
+ if (selectedItem) {
133
+ lines.push('');
134
+ const previewLines = this.config.renderSelectedPreview(selectedItem, s);
135
+ lines.push(...previewLines);
136
+ }
137
+ }
138
+ // Footer
139
+ lines.push(border);
140
+ lines.push(` ${s.muted(this.getFooterHints())}`);
141
+ lines.push(border);
142
+ return lines;
143
+ }
144
+ getFooterHints() {
145
+ if (this.config.footerHints) {
146
+ return this.config.footerHints(this.state.searchMode);
147
+ }
148
+ if (this.state.searchMode) {
149
+ return 'Type to filter · ↑↓/jk Navigate · Enter Details · Esc Exit search';
150
+ }
151
+ return this.pagination.renderHints() + ' · / Search · Enter Details · q/Esc Close';
152
+ }
153
+ handleKey(data) {
154
+ // Ctrl+C always closes
155
+ if (isCtrlC(data)) {
156
+ return { action: 'close', result: undefined };
157
+ }
158
+ // Search mode handling
159
+ if (this.state.searchMode) {
160
+ if (isEscape(data)) {
161
+ this.state.searchMode = false;
162
+ if (this.state.searchQuery) {
163
+ this.state.searchQuery = '';
164
+ this.applyFilters();
165
+ }
166
+ return { action: 'stay', render: true };
167
+ }
168
+ const searchResult = this.searchFeature.handleKey(data, this.state);
169
+ if (searchResult?.handled) {
170
+ return { action: 'stay', render: searchResult.action === 'render' };
171
+ }
172
+ if (isEnter(data)) {
173
+ return this.handleEnter();
174
+ }
175
+ const paginationResult = this.pagination.handleKey(data, this.state, this.state.filteredItems.length);
176
+ if (paginationResult?.handled) {
177
+ return { action: 'stay', render: paginationResult.action === 'render' };
178
+ }
179
+ return { action: 'stay', render: false };
180
+ }
181
+ // Not in search mode
182
+ if (isSlash(data)) {
183
+ this.state.searchMode = true;
184
+ return { action: 'stay', render: true };
185
+ }
186
+ if (isClose(data)) {
187
+ return { action: 'close', result: undefined };
188
+ }
189
+ // Tab navigation
190
+ const tabResult = this.tabFeature.handleKey(data, this.state);
191
+ if (tabResult?.handled) {
192
+ return { action: 'stay', render: tabResult.action === 'render' };
193
+ }
194
+ // Enter shows detail
195
+ if (isEnter(data)) {
196
+ return this.handleEnter();
197
+ }
198
+ // Pagination
199
+ const paginationResult = this.pagination.handleKey(data, this.state, this.state.filteredItems.length);
200
+ if (paginationResult?.handled) {
201
+ return { action: 'stay', render: paginationResult.action === 'render' };
202
+ }
203
+ return { action: 'stay', render: false };
204
+ }
205
+ handleEnter() {
206
+ if (this.state.filteredItems.length === 0) {
207
+ return { action: 'stay', render: false };
208
+ }
209
+ const item = this.state.filteredItems[this.state.selectedIndex];
210
+ // First, check if there's an item selection handler
211
+ if (this.onItemSelected) {
212
+ const result = this.onItemSelected(item);
213
+ if (result !== undefined) {
214
+ // Item was selected, close overlay with result
215
+ return { action: 'close', result };
216
+ }
217
+ }
218
+ // Fall back to detail screen
219
+ const detailScreen = this.createDetailScreen(item);
220
+ if (detailScreen) {
221
+ return { action: 'push', screen: detailScreen };
222
+ }
223
+ return { action: 'stay', render: false };
224
+ }
225
+ }
226
+ // =============================================================================
227
+ // TabbedListOverlayV2 Base Class
228
+ // =============================================================================
229
+ /**
230
+ * Abstract base class for tabbed list overlays using the new Overlay interface.
231
+ */
232
+ export class TabbedListOverlayV2 extends BaseOverlayV2 {
233
+ screenStack;
234
+ listConfig;
235
+ constructor(config) {
236
+ const initialState = {
237
+ currentTab: 0,
238
+ selectedIndex: 0,
239
+ scrollOffset: 0,
240
+ searchQuery: '',
241
+ searchMode: false,
242
+ items: config.items,
243
+ filteredItems: config.items.filter((item) => config.filterByTab(item, config.tabs[0]?.id ?? 'all')),
244
+ };
245
+ super(initialState);
246
+ this.listConfig = config;
247
+ // Calculate minimum height for stable rendering:
248
+ // Header (5) + pageSize items + pagination (2) + footer (3) = 10 + pageSize
249
+ const pageSize = config.pageSize ?? 12;
250
+ this.minHeight = 10 + pageSize;
251
+ this.screenStack = new ScreenStack();
252
+ this.screenStack.push(new ListScreenV2(config, this.state, () => this.getStyles(), () => this.termWidth, (item) => this.createDetailScreen(item), (item) => this.onItemSelected(item)));
253
+ }
254
+ /**
255
+ * Override to create a detail screen for an item.
256
+ * Return null to disable detail view.
257
+ */
258
+ createDetailScreen(_item) {
259
+ return null;
260
+ }
261
+ /**
262
+ * Override to handle item selection.
263
+ * Return a result to close the overlay, or undefined to fall back to detail screen.
264
+ */
265
+ onItemSelected(_item) {
266
+ return undefined;
267
+ }
268
+ renderContent(_context) {
269
+ const screen = this.screenStack.current();
270
+ return screen?.render() ?? [];
271
+ }
272
+ /**
273
+ * Override render to use the effective minHeight (max of overlay and current screen).
274
+ */
275
+ render(context) {
276
+ // Cache context values for helper methods
277
+ this.styles = context.styles;
278
+ this.termWidth = context.width;
279
+ // Get content from subclass
280
+ const lines = this.renderContent(context);
281
+ // Calculate effective minHeight: max of overlay's base and current screen's minHeight
282
+ const screen = this.screenStack.current();
283
+ const screenMinHeight = screen?.getMinHeight?.() ?? 0;
284
+ const effectiveMinHeight = Math.max(this.minHeight, screenMinHeight);
285
+ return effectiveMinHeight > 0 ? { lines, minHeight: effectiveMinHeight } : { lines };
286
+ }
287
+ handleKey(key) {
288
+ // Convert KeyEvent to Buffer for old-style BaseScreen.handleKey
289
+ const data = key.raw;
290
+ const screen = this.screenStack.current();
291
+ if (!screen) {
292
+ return this.cancel();
293
+ }
294
+ const result = screen.handleKey(data);
295
+ switch (result.action) {
296
+ case 'stay':
297
+ return result.render ? this.rerender() : this.noAction();
298
+ case 'push':
299
+ this.screenStack.push(result.screen);
300
+ return this.rerender();
301
+ case 'pop':
302
+ this.screenStack.pop();
303
+ return this.rerender();
304
+ case 'close':
305
+ if (result.result !== undefined) {
306
+ return this.close(result.result);
307
+ }
308
+ return this.cancel();
309
+ }
310
+ }
311
+ /**
312
+ * Get the currently selected item.
313
+ */
314
+ getSelectedItem() {
315
+ return this.state.filteredItems[this.state.selectedIndex];
316
+ }
317
+ }
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Tabbed List Overlay Base Class
3
+ *
4
+ * A reusable base for overlays that display:
5
+ * - Tabs for filtering/categorizing
6
+ * - A paginated list of items
7
+ * - Search mode (activated with /)
8
+ * - Detail view (via ScreenStack)
9
+ *
10
+ * This provides ~80% of the common overlay functionality,
11
+ * leaving only item-specific rendering to be customized.
12
+ *
13
+ * Usage:
14
+ * ```typescript
15
+ * interface MyItem { name: string; description: string; }
16
+ *
17
+ * const TABS = [
18
+ * { id: 'all', label: 'All' },
19
+ * { id: 'active', label: 'Active' },
20
+ * ];
21
+ *
22
+ * class MyOverlay extends TabbedListOverlay<MyItem, MyResult> {
23
+ * constructor(items: MyItem[]) {
24
+ * super({
25
+ * title: 'My Items',
26
+ * tabs: TABS,
27
+ * items,
28
+ * pageSize: 12,
29
+ * filterByTab: (item, tabId) => tabId === 'all' || item.active,
30
+ * getSearchText: (item) => `${item.name} ${item.description}`,
31
+ * renderItem: (item, isSelected, styles) => {
32
+ * const prefix = isSelected ? '❯ ' : ' ';
33
+ * return prefix + item.name;
34
+ * },
35
+ * });
36
+ * }
37
+ *
38
+ * // Optional: Override to add detail view
39
+ * protected createDetailScreen(item: MyItem): BaseScreen | null {
40
+ * return new MyDetailScreen(item, this.styles);
41
+ * }
42
+ * }
43
+ * ```
44
+ */
45
+ import { BaseOverlay, type OverlayState } from './overlay-base.js';
46
+ import { BaseScreen, ScreenStack } from './screen-stack.js';
47
+ import { type PaginationState } from '../features/pagination-feature.js';
48
+ import { type SearchState } from '../features/search-feature.js';
49
+ import type { TabDefinition, TabState } from './overlay-types.js';
50
+ /**
51
+ * Configuration for a TabbedListOverlay.
52
+ */
53
+ export interface TabbedListConfig<TItem> {
54
+ /** Title shown in header */
55
+ title: string;
56
+ /** Tab definitions */
57
+ tabs: TabDefinition[];
58
+ /** All items (will be filtered by tab and search) */
59
+ items: TItem[];
60
+ /** Number of items per page (default: 12) */
61
+ pageSize?: number;
62
+ /**
63
+ * Filter items by tab.
64
+ * Return true to include item in the current tab.
65
+ */
66
+ filterByTab: (item: TItem, tabId: string) => boolean;
67
+ /**
68
+ * Get searchable text from an item.
69
+ * Used for search filtering.
70
+ */
71
+ getSearchText: (item: TItem) => string;
72
+ /**
73
+ * Render an item as a string line.
74
+ * @param item - The item to render
75
+ * @param isSelected - Whether this item is currently selected
76
+ * @param styles - Theme styles
77
+ * @returns Single line string
78
+ */
79
+ renderItem: (item: TItem, isSelected: boolean, styles: ReturnType<typeof import('../../themes/index.js').getStyles>) => string;
80
+ /**
81
+ * Optional: Show count badge in header (default: true)
82
+ */
83
+ showCount?: boolean;
84
+ /**
85
+ * Optional: Custom empty state message
86
+ */
87
+ emptyMessage?: string;
88
+ /**
89
+ * Optional: Custom no-results message
90
+ */
91
+ noResultsMessage?: string;
92
+ /**
93
+ * Optional: Custom footer hints
94
+ * @param searchMode - Whether search mode is active
95
+ * @returns Footer hints string
96
+ */
97
+ footerHints?: (searchMode: boolean) => string;
98
+ /**
99
+ * Optional: Render preview content for the selected item
100
+ * Displayed between the list and footer
101
+ * @param item - The currently selected item
102
+ * @param styles - Theme styles
103
+ * @returns Array of lines to render
104
+ */
105
+ renderSelectedPreview?: (item: TItem, styles: ReturnType<typeof import('../../themes/index.js').getStyles>) => string[];
106
+ /**
107
+ * Optional: Whether detail screens take over full screen.
108
+ * When true (default), returning from a detail screen clears the screen.
109
+ * When false, screens render in-place without clearing.
110
+ */
111
+ detailScreensFullScreen?: boolean;
112
+ }
113
+ /**
114
+ * State for TabbedListOverlay.
115
+ */
116
+ export interface TabbedListState<TItem> extends OverlayState, TabState, PaginationState, SearchState {
117
+ /** All items */
118
+ items: TItem[];
119
+ /** Filtered items (by tab and search) */
120
+ filteredItems: TItem[];
121
+ /** Whether search mode is active */
122
+ searchMode: boolean;
123
+ }
124
+ /**
125
+ * Abstract base class for tabbed list overlays.
126
+ *
127
+ * Provides:
128
+ * - Tab-based filtering
129
+ * - Search mode with / toggle
130
+ * - Paginated item display
131
+ * - ScreenStack for detail views
132
+ *
133
+ * Subclasses can override createDetailScreen() to add detail views.
134
+ */
135
+ export declare abstract class TabbedListOverlay<TItem, TResult = void> extends BaseOverlay<TabbedListState<TItem>, TResult> {
136
+ protected readonly screenStack: ScreenStack;
137
+ protected readonly listConfig: TabbedListConfig<TItem>;
138
+ private resizeHandler;
139
+ constructor(config: TabbedListConfig<TItem>);
140
+ /**
141
+ * Override to create a detail screen for an item.
142
+ * Return null to disable detail view.
143
+ */
144
+ protected createDetailScreen(_item: TItem): BaseScreen | null;
145
+ show(): Promise<TResult>;
146
+ protected close(result: TResult): void;
147
+ render(): string[];
148
+ handleKey(data: Buffer): void;
149
+ /**
150
+ * Get the currently selected item.
151
+ */
152
+ protected getSelectedItem(): TItem | undefined;
153
+ }