@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,499 @@
1
+ /**
2
+ * Projects Overlay V2
3
+ *
4
+ * Interactive overlay for viewing and managing projects.
5
+ * Uses TabbedListOverlayV2 for consistent tabbed list behavior.
6
+ *
7
+ * Features:
8
+ * - Tabbed filtering by status (All, Active, Paused, Completed, Archived)
9
+ * - Paginated list with search
10
+ * - Project detail preview
11
+ * - Archive/Restore workflow
12
+ * - Delete workflow with path validation
13
+ * - Keyboard navigation (vim-style + arrows)
14
+ * - Opens WorkflowOverlayV2 for selected project (push onto stack)
15
+ */
16
+ import * as fs from 'fs';
17
+ import { TabbedListOverlayV2 } from '../../base/tabbed-list-overlay-v2.js';
18
+ import { BaseScreen, stay, popScreen, closeOverlay, renderBorder, } from '../../base/index.js';
19
+ import { projectRepository, workItemRepository } from '../../../db/repositories/index.js';
20
+ import { clearActiveProject, getActiveProject } from '../../../tools/project-db.js';
21
+ import { validateDeletePath } from '../../../utils/path-safety.js';
22
+ import { getAllowedDeletePaths } from '../../../settings/paths.js';
23
+ import { clearProjectAnchors } from '../../../anchors/index.js';
24
+ import * as terminal from '../../terminal.js';
25
+ import { WorkflowOverlayV2 } from './workflow-overlay-v2.js';
26
+ // =============================================================================
27
+ // Constants
28
+ // =============================================================================
29
+ const PAGE_SIZE = 10;
30
+ const TABS = [
31
+ { id: 'all', label: 'All' },
32
+ { id: 'active', label: 'Active' },
33
+ { id: 'paused', label: 'Paused' },
34
+ { id: 'completed', label: 'Complete' },
35
+ { id: 'archived', label: 'Archived' },
36
+ ];
37
+ const STATUS_ICONS = {
38
+ active: '●',
39
+ paused: '◐',
40
+ completed: '✓',
41
+ archived: '◌',
42
+ };
43
+ const STATUS_LABELS = {
44
+ active: 'Active',
45
+ paused: 'Paused',
46
+ completed: 'Complete',
47
+ archived: 'Archived',
48
+ };
49
+ // =============================================================================
50
+ // Helper Functions
51
+ // =============================================================================
52
+ function truncate(str, maxLen) {
53
+ if (str.length <= maxLen)
54
+ return str;
55
+ return str.slice(0, maxLen - 3) + '...';
56
+ }
57
+ function renderProgressBar(percent, width, s) {
58
+ const filled = Math.round((percent / 100) * width);
59
+ const empty = width - filled;
60
+ return s.primary('█'.repeat(filled)) + s.muted('░'.repeat(empty));
61
+ }
62
+ // =============================================================================
63
+ // Key Detection Helpers
64
+ // =============================================================================
65
+ function isCtrlC(data) {
66
+ return data.length === 1 && data[0] === 0x03;
67
+ }
68
+ function isEscape(data) {
69
+ return data.length === 1 && data[0] === 0x1b;
70
+ }
71
+ function isEnter(data) {
72
+ return data.length === 1 && (data[0] === 0x0d || data[0] === 0x0a);
73
+ }
74
+ function isBackspace(data) {
75
+ return data.length === 1 && (data[0] === 0x7f || data[0] === 0x08);
76
+ }
77
+ function extractPrintable(data) {
78
+ if (data.length === 1 && data[0] >= 0x20 && data[0] < 0x7f) {
79
+ return String.fromCharCode(data[0]);
80
+ }
81
+ return null;
82
+ }
83
+ // =============================================================================
84
+ // Archive Confirm Screen
85
+ // =============================================================================
86
+ class ArchiveConfirmScreen extends BaseScreen {
87
+ confirmState;
88
+ styles;
89
+ onComplete;
90
+ constructor(confirmState, styles, onComplete) {
91
+ super();
92
+ this.confirmState = confirmState;
93
+ this.styles = styles;
94
+ this.onComplete = onComplete;
95
+ }
96
+ render() {
97
+ const s = this.styles;
98
+ const cols = terminal.getTerminalWidth();
99
+ const border = renderBorder(cols, s);
100
+ const project = this.confirmState.targetProject;
101
+ const lines = [];
102
+ if (!project) {
103
+ lines.push(border);
104
+ lines.push(s.error(' No project selected'));
105
+ lines.push(border);
106
+ return lines;
107
+ }
108
+ const isArchived = project.status === 'archived';
109
+ const action = isArchived ? 'RESTORE' : 'ARCHIVE';
110
+ lines.push(border);
111
+ lines.push(` ${s.primaryBold(action + ' PROJECT')}`);
112
+ lines.push('');
113
+ if (isArchived) {
114
+ lines.push(` ${s.foreground('Restore this project?')}`);
115
+ lines.push('');
116
+ lines.push(s.muted(' Project: ') + project.displayName);
117
+ lines.push('');
118
+ lines.push(s.foreground(' The project will be marked as "active" and visible again.'));
119
+ }
120
+ else {
121
+ lines.push(` ${s.warning('Archive this project?')}`);
122
+ lines.push('');
123
+ lines.push(s.muted(' Project: ') + project.displayName);
124
+ lines.push('');
125
+ lines.push(s.foreground(' The project will be hidden from the active view.'));
126
+ lines.push(s.foreground(' You can restore it later from the Archived tab.'));
127
+ }
128
+ lines.push('');
129
+ lines.push(border);
130
+ lines.push(s.muted(' Enter Confirm · Esc Cancel'));
131
+ lines.push(border);
132
+ return lines;
133
+ }
134
+ handleKey(data) {
135
+ if (isCtrlC(data)) {
136
+ return closeOverlay({ action: 'cancel' });
137
+ }
138
+ if (isEscape(data)) {
139
+ this.confirmState.targetProject = null;
140
+ return popScreen();
141
+ }
142
+ if (isEnter(data)) {
143
+ const project = this.confirmState.targetProject;
144
+ if (!project)
145
+ return popScreen();
146
+ const isArchived = project.status === 'archived';
147
+ const newStatus = isArchived ? 'active' : 'archived';
148
+ projectRepository.update(project.id, { status: newStatus });
149
+ // Clear as active if archiving the active project
150
+ if (!isArchived && getActiveProject()?.id === project.id) {
151
+ clearActiveProject();
152
+ }
153
+ this.confirmState.modified = true;
154
+ this.onComplete();
155
+ this.confirmState.targetProject = null;
156
+ return popScreen();
157
+ }
158
+ return stay(false);
159
+ }
160
+ getMinHeight() {
161
+ return 14;
162
+ }
163
+ }
164
+ // =============================================================================
165
+ // Delete Confirm Screen
166
+ // =============================================================================
167
+ class DeleteConfirmScreen extends BaseScreen {
168
+ confirmState;
169
+ styles;
170
+ onComplete;
171
+ constructor(confirmState, styles, onComplete) {
172
+ super();
173
+ this.confirmState = confirmState;
174
+ this.styles = styles;
175
+ this.onComplete = onComplete;
176
+ }
177
+ render() {
178
+ const s = this.styles;
179
+ const cols = terminal.getTerminalWidth();
180
+ const border = renderBorder(cols, s);
181
+ const project = this.confirmState.targetProject;
182
+ const validation = this.confirmState.deleteValidation;
183
+ const lines = [];
184
+ if (!project) {
185
+ lines.push(border);
186
+ lines.push(s.error(' No project selected'));
187
+ lines.push(border);
188
+ return lines;
189
+ }
190
+ lines.push(border);
191
+ lines.push(` ${s.error('DELETE PROJECT')}`);
192
+ lines.push('');
193
+ // If validation failed, show error state
194
+ if (validation && !validation.valid) {
195
+ lines.push(s.error(' CANNOT DELETE: Path validation failed'));
196
+ lines.push('');
197
+ lines.push(s.muted(' Project: ') + project.displayName);
198
+ lines.push(s.muted(' Path: ') + project.path);
199
+ lines.push('');
200
+ lines.push(s.error(` Error: ${validation.reason || 'Unknown error'}`));
201
+ lines.push('');
202
+ lines.push(s.foreground(' Allowed directories:'));
203
+ for (const allowed of getAllowedDeletePaths()) {
204
+ lines.push(s.muted(` • ${allowed}`));
205
+ }
206
+ lines.push('');
207
+ lines.push(s.foreground(' Options:'));
208
+ lines.push(s.muted(' 1. Move the project to an allowed directory'));
209
+ lines.push(s.muted(' 2. Add path to allowedPaths in /config'));
210
+ lines.push(s.muted(' 3. Delete files manually and remove from database only'));
211
+ lines.push('');
212
+ lines.push(border);
213
+ lines.push(s.muted(' Esc Back'));
214
+ lines.push(border);
215
+ return lines;
216
+ }
217
+ lines.push(s.warning(' WARNING: This action cannot be undone!'));
218
+ lines.push('');
219
+ lines.push(s.muted(' Project: ') + project.displayName);
220
+ lines.push(s.muted(' Name: ') + project.name);
221
+ lines.push(s.muted(' Path: ') + project.path);
222
+ lines.push('');
223
+ // Show validation status
224
+ if (validation && validation.valid) {
225
+ if (fs.existsSync(project.path + '/.compilr')) {
226
+ lines.push(s.success(' ✓ Path is within allowed directory'));
227
+ lines.push(s.success(' ✓ Project has .compilr marker'));
228
+ }
229
+ else if (fs.existsSync(project.path + '/.git')) {
230
+ lines.push(s.success(' ✓ Path is within allowed directory'));
231
+ lines.push(s.success(' ✓ Project has .git repository'));
232
+ }
233
+ else if (fs.existsSync(project.path + '/package.json')) {
234
+ lines.push(s.success(' ✓ Path is within allowed directory'));
235
+ lines.push(s.success(' ✓ Project has package.json'));
236
+ }
237
+ else {
238
+ lines.push(s.success(' ✓ Path is within allowed directory'));
239
+ }
240
+ if (validation.warnings && validation.warnings.length > 0) {
241
+ lines.push('');
242
+ for (const warning of validation.warnings) {
243
+ lines.push(s.warning(` ⚠ ${warning}`));
244
+ }
245
+ }
246
+ lines.push('');
247
+ }
248
+ lines.push(s.foreground(' This will permanently delete:'));
249
+ lines.push(s.muted(' • Database record (project, work items, documents)'));
250
+ lines.push(s.muted(' • Project files on disk'));
251
+ if (project.status !== 'archived') {
252
+ lines.push('');
253
+ lines.push(s.warning(' Note: Project will be archived first, then deleted.'));
254
+ }
255
+ lines.push('');
256
+ lines.push(s.muted(' ' + '─'.repeat(cols - 4)));
257
+ lines.push('');
258
+ lines.push(s.foreground(' Type the project name to confirm deletion:'));
259
+ lines.push('');
260
+ lines.push(` > ${this.confirmState.deleteConfirmInput}█`);
261
+ if (this.confirmState.deleteError) {
262
+ lines.push('');
263
+ lines.push(s.error(` ${this.confirmState.deleteError}`));
264
+ }
265
+ lines.push('');
266
+ lines.push(border);
267
+ lines.push(s.muted(' Enter Confirm · Esc Cancel'));
268
+ lines.push(border);
269
+ return lines;
270
+ }
271
+ handleKey(data) {
272
+ const char = extractPrintable(data);
273
+ if (isCtrlC(data)) {
274
+ return closeOverlay({ action: 'cancel' });
275
+ }
276
+ if (isEscape(data)) {
277
+ this.confirmState.targetProject = null;
278
+ this.confirmState.deleteConfirmInput = '';
279
+ this.confirmState.deleteError = null;
280
+ this.confirmState.deleteValidation = null;
281
+ return popScreen();
282
+ }
283
+ if (isBackspace(data)) {
284
+ this.confirmState.deleteConfirmInput = this.confirmState.deleteConfirmInput.slice(0, -1);
285
+ this.confirmState.deleteError = null;
286
+ return stay();
287
+ }
288
+ if (char) {
289
+ this.confirmState.deleteConfirmInput += char;
290
+ this.confirmState.deleteError = null;
291
+ return stay();
292
+ }
293
+ if (isEnter(data)) {
294
+ const project = this.confirmState.targetProject;
295
+ if (!project)
296
+ return popScreen();
297
+ // If validation failed, don't allow Enter to proceed
298
+ if (this.confirmState.deleteValidation && !this.confirmState.deleteValidation.valid) {
299
+ return stay(false);
300
+ }
301
+ if (this.confirmState.deleteConfirmInput !== project.name) {
302
+ this.confirmState.deleteError = `Type "${project.name}" exactly to confirm`;
303
+ return stay();
304
+ }
305
+ // Archive first if not already archived
306
+ if (project.status !== 'archived') {
307
+ projectRepository.update(project.id, { status: 'archived' });
308
+ }
309
+ // Delete from database
310
+ projectRepository.delete(project.id);
311
+ // Clear project-specific anchors
312
+ clearProjectAnchors(String(project.id));
313
+ // Clear as active project if needed
314
+ if (getActiveProject()?.id === project.id) {
315
+ clearActiveProject();
316
+ }
317
+ // Delete filesystem folder (only if validation passed and path exists)
318
+ const projectPath = project.path;
319
+ const validation = this.confirmState.deleteValidation;
320
+ if (projectPath && validation?.valid && fs.existsSync(projectPath)) {
321
+ try {
322
+ fs.rmSync(projectPath, { recursive: true, force: true });
323
+ }
324
+ catch {
325
+ // Ignore delete errors - DB is already cleaned up
326
+ }
327
+ }
328
+ this.confirmState.modified = true;
329
+ this.onComplete();
330
+ this.confirmState.targetProject = null;
331
+ this.confirmState.deleteConfirmInput = '';
332
+ this.confirmState.deleteError = null;
333
+ this.confirmState.deleteValidation = null;
334
+ return popScreen();
335
+ }
336
+ return stay(false);
337
+ }
338
+ getMinHeight() {
339
+ return 28;
340
+ }
341
+ }
342
+ // =============================================================================
343
+ // Projects Overlay V2
344
+ // =============================================================================
345
+ export class ProjectsOverlayV2 extends TabbedListOverlayV2 {
346
+ type = 'inline';
347
+ id = 'projects-overlay-v2';
348
+ confirmState = {
349
+ targetProject: null,
350
+ deleteConfirmInput: '',
351
+ deleteError: null,
352
+ deleteValidation: null,
353
+ modified: false,
354
+ };
355
+ constructor() {
356
+ // Load projects from database
357
+ const { projects } = projectRepository.list({ status: 'all' });
358
+ super({
359
+ title: 'Projects',
360
+ tabs: TABS,
361
+ items: projects,
362
+ pageSize: PAGE_SIZE,
363
+ filterByTab: (project, tabId) => {
364
+ if (tabId === 'all') {
365
+ // 'all' shows everything except archived
366
+ return project.status !== 'archived';
367
+ }
368
+ return project.status === tabId;
369
+ },
370
+ getSearchText: (project) => `${project.displayName} ${project.name} ${project.path} ${project.type}`,
371
+ renderItem: (project, isSelected, s) => {
372
+ const cols = terminal.getTerminalWidth();
373
+ const nameW = Math.min(24, Math.floor(cols * 0.25));
374
+ const typeW = 10;
375
+ const statusW = 12;
376
+ const pathW = Math.max(20, cols - nameW - typeW - statusW - 12);
377
+ const cursor = isSelected ? s.primary('❯ ') : ' ';
378
+ const statusIcon = STATUS_ICONS[project.status];
379
+ const statusLabel = `${statusIcon} ${STATUS_LABELS[project.status]}`;
380
+ const row = [
381
+ truncate(project.displayName, nameW - 1).padEnd(nameW),
382
+ project.type.padEnd(typeW),
383
+ statusLabel.padEnd(statusW),
384
+ truncate(project.path, pathW - 1),
385
+ ].join('');
386
+ if (isSelected) {
387
+ return `${cursor}${s.primary(row)}`;
388
+ }
389
+ return `${cursor}${row}`;
390
+ },
391
+ showCount: true,
392
+ emptyMessage: 'No projects found. Use /new to create one.',
393
+ noResultsMessage: 'No projects match the search.',
394
+ footerHints: (searchMode) => {
395
+ if (searchMode) {
396
+ return 'Type to filter · ↑↓/jk Navigate · Enter Open · Esc Exit search';
397
+ }
398
+ return '↑↓/jk Navigate · ←→/hl Tabs · / Search · Enter Open · x Archive · d Delete · q/Esc Close';
399
+ },
400
+ renderSelectedPreview: (project, s) => {
401
+ const lines = [];
402
+ lines.push(s.muted(' ' + '─'.repeat(60)));
403
+ // Last activity
404
+ const lastActivity = project.lastActivityAt
405
+ ? new Date(project.lastActivityAt).toLocaleDateString('en-US', {
406
+ year: 'numeric',
407
+ month: 'short',
408
+ day: 'numeric',
409
+ hour: '2-digit',
410
+ minute: '2-digit',
411
+ })
412
+ : 'Never';
413
+ lines.push(` ${s.muted('Last Activity:')} ${lastActivity}`);
414
+ // Work items progress
415
+ const { items: workItems, total } = workItemRepository.query({ project_id: project.id });
416
+ const completed = workItems.filter(w => w.status === 'completed').length;
417
+ if (total > 0) {
418
+ const percent = Math.round((completed / total) * 100);
419
+ const progressBar = renderProgressBar(percent, 20, s);
420
+ lines.push(` ${s.muted('Work Items:')} ${progressBar} ${String(completed)}/${String(total)} (${String(percent)}%)`);
421
+ }
422
+ else {
423
+ lines.push(` ${s.muted('Work Items:')} ${s.muted('None')}`);
424
+ }
425
+ return lines;
426
+ },
427
+ });
428
+ this.minHeight = 22;
429
+ }
430
+ /**
431
+ * Refresh projects from database after archive/delete
432
+ */
433
+ refreshProjects() {
434
+ const { projects } = projectRepository.list({ status: 'all' });
435
+ this.state.items = projects;
436
+ // Re-apply tab filter
437
+ const tabId = TABS[this.state.currentTab]?.id ?? 'all';
438
+ let filtered = projects.filter((p) => {
439
+ if (tabId === 'all')
440
+ return p.status !== 'archived';
441
+ return p.status === tabId;
442
+ });
443
+ // Re-apply search filter
444
+ if (this.state.searchQuery) {
445
+ const query = this.state.searchQuery.toLowerCase();
446
+ filtered = filtered.filter((p) => p.displayName.toLowerCase().includes(query) ||
447
+ p.name.toLowerCase().includes(query) ||
448
+ p.path.toLowerCase().includes(query));
449
+ }
450
+ this.state.filteredItems = filtered;
451
+ this.state.selectedIndex = Math.min(this.state.selectedIndex, Math.max(0, filtered.length - 1));
452
+ }
453
+ /**
454
+ * Override handleKey to add custom behavior (Enter opens workflow, x archive, d delete)
455
+ */
456
+ handleKey(key) {
457
+ // Ctrl+C always closes
458
+ if (this.isInterruptKey(key)) {
459
+ return this.close({ action: 'cancel' });
460
+ }
461
+ // q or Esc closes (only if not in search mode)
462
+ if (this.isCloseKey(key) && !this.state.searchMode) {
463
+ return this.close({ action: 'cancel' });
464
+ }
465
+ // Only handle custom keys when on main list (screen stack size = 1)
466
+ if (this.screenStack.size() === 1 && !this.state.searchMode) {
467
+ // Enter opens workflow overlay (pushed onto V2 overlay stack)
468
+ if (this.isEnterKey(key) && this.state.filteredItems.length > 0) {
469
+ const project = this.state.filteredItems[this.state.selectedIndex];
470
+ return this.push(new WorkflowOverlayV2(project.id));
471
+ }
472
+ // 'x' archive/restore
473
+ if ((key.char === 'x' || key.char === 'X') && this.state.filteredItems.length > 0) {
474
+ const project = this.state.filteredItems[this.state.selectedIndex];
475
+ this.confirmState.targetProject = project;
476
+ this.screenStack.push(new ArchiveConfirmScreen(this.confirmState, this.getStyles(), () => { this.refreshProjects(); }));
477
+ return this.rerender();
478
+ }
479
+ // 'd' delete
480
+ if ((key.char === 'd' || key.char === 'D') && this.state.filteredItems.length > 0) {
481
+ const project = this.state.filteredItems[this.state.selectedIndex];
482
+ this.confirmState.targetProject = project;
483
+ this.confirmState.deleteConfirmInput = '';
484
+ this.confirmState.deleteError = null;
485
+ this.confirmState.deleteValidation = validateDeletePath(project.path);
486
+ this.screenStack.push(new DeleteConfirmScreen(this.confirmState, this.getStyles(), () => { this.refreshProjects(); }));
487
+ return this.rerender();
488
+ }
489
+ }
490
+ // Let parent handle everything else
491
+ const result = super.handleKey(key);
492
+ // Convert close result to our result type
493
+ if (result.type === 'close') {
494
+ return this.close({ action: 'cancel' });
495
+ }
496
+ // Safe to return other actions (render, none, etc.)
497
+ return result;
498
+ }
499
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Theme Overlay V2
3
+ *
4
+ * Theme selection overlay using TabbedListOverlayV2 for the new Overlay interface.
5
+ * Works with TerminalUI's overlay management.
6
+ *
7
+ * Features:
8
+ * - Tabs: Curated (top picks), All, Dark, Light
9
+ * - Search mode with / toggle
10
+ * - Live preview with syntax highlighting
11
+ * - Persists selection to settings
12
+ */
13
+ import { TabbedListOverlayV2 } from '../../base/index.js';
14
+ import type { KeyEvent, OverlayAction } from '../index.js';
15
+ import { type Theme } from '../../../themes/index.js';
16
+ export interface ThemeOverlayOptions {
17
+ /** Current theme ID (for highlighting current selection) */
18
+ currentThemeId?: string;
19
+ }
20
+ export interface ThemeOverlayResult {
21
+ /** Selected theme ID (if changed) */
22
+ themeChanged?: string;
23
+ /** Selected theme name */
24
+ themeName?: string;
25
+ }
26
+ /**
27
+ * Theme overlay using the new Overlay interface.
28
+ * Displays available themes in a searchable, tabbed list with preview.
29
+ */
30
+ export declare class ThemeOverlayV2 extends TabbedListOverlayV2<Theme, ThemeOverlayResult> {
31
+ readonly type: "inline";
32
+ readonly id = "theme-overlay-v2";
33
+ private readonly currentThemeId;
34
+ private readonly curatedIds;
35
+ constructor(options?: ThemeOverlayOptions);
36
+ handleKey(key: KeyEvent): OverlayAction<ThemeOverlayResult>;
37
+ private selectTheme;
38
+ /**
39
+ * Optional summary shown after overlay closes.
40
+ */
41
+ getCloseSummary(result: ThemeOverlayResult): string | null;
42
+ }
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Theme Overlay V2
3
+ *
4
+ * Theme selection overlay using TabbedListOverlayV2 for the new Overlay interface.
5
+ * Works with TerminalUI's overlay management.
6
+ *
7
+ * Features:
8
+ * - Tabs: Curated (top picks), All, Dark, Light
9
+ * - Search mode with / toggle
10
+ * - Live preview with syntax highlighting
11
+ * - Persists selection to settings
12
+ */
13
+ import chalk from 'chalk';
14
+ import { TabbedListOverlayV2, } from '../../base/index.js';
15
+ import { getCuratedThemes, getCurrentTheme, setCurrentTheme, getAllThemes, getThemeCount, clearStyleCache, } from '../../../themes/index.js';
16
+ // =============================================================================
17
+ // Constants
18
+ // =============================================================================
19
+ const PAGE_SIZE = 10;
20
+ const TABS = [
21
+ { id: 'curated', label: 'Curated' },
22
+ { id: 'all', label: 'All' },
23
+ { id: 'dark', label: 'Dark' },
24
+ { id: 'light', label: 'Light' },
25
+ ];
26
+ // =============================================================================
27
+ // Helper Functions
28
+ // =============================================================================
29
+ function renderPreview(theme, styles) {
30
+ const lines = [];
31
+ const colors = theme.colors;
32
+ lines.push(styles.muted(' Preview:'));
33
+ // Use chalk for hex colors in preview
34
+ const primary = chalk.hex(colors.primary);
35
+ const secondary = chalk.hex(colors.secondary || colors.primary);
36
+ const muted = chalk.hex(colors.muted);
37
+ const fg = chalk.hex(colors.foreground);
38
+ lines.push(' ' + primary.bold('function') + ' ' + secondary('greet') + fg('() {'));
39
+ lines.push(' ' + muted('// Say hello'));
40
+ lines.push(' ' + fg('console.') + secondary('log') + fg('("Hello, ') + primary('World') + fg('!");'));
41
+ lines.push(' ' + fg('}'));
42
+ return lines;
43
+ }
44
+ // =============================================================================
45
+ // Theme Overlay V2 Class
46
+ // =============================================================================
47
+ /**
48
+ * Theme overlay using the new Overlay interface.
49
+ * Displays available themes in a searchable, tabbed list with preview.
50
+ */
51
+ export class ThemeOverlayV2 extends TabbedListOverlayV2 {
52
+ type = 'inline';
53
+ id = 'theme-overlay-v2';
54
+ currentThemeId;
55
+ curatedIds;
56
+ constructor(options = {}) {
57
+ const currentTheme = getCurrentTheme();
58
+ const currentThemeId = options.currentThemeId ?? currentTheme.id;
59
+ const curatedThemes = getCuratedThemes();
60
+ const curatedIds = new Set(curatedThemes.map(t => t.id));
61
+ const allThemes = getAllThemes();
62
+ const counts = getThemeCount();
63
+ super({
64
+ title: `Select Theme (${String(counts.total)})`,
65
+ tabs: TABS,
66
+ items: allThemes,
67
+ pageSize: PAGE_SIZE,
68
+ showCount: false, // We show count in title
69
+ filterByTab: (theme, tabId) => {
70
+ switch (tabId) {
71
+ case 'curated':
72
+ return curatedIds.has(theme.id);
73
+ case 'dark':
74
+ return theme.type === 'dark';
75
+ case 'light':
76
+ return theme.type === 'light';
77
+ default:
78
+ return true; // 'all'
79
+ }
80
+ },
81
+ getSearchText: (theme) => {
82
+ return `${theme.name} ${theme.id}`;
83
+ },
84
+ renderItem: (theme, isSelected, styles) => {
85
+ const isCurrent = theme.id === currentThemeId;
86
+ const prefix = isSelected ? styles.primary('> ') : ' ';
87
+ const icon = theme.type === 'dark' ? 'D' : 'L';
88
+ const checkmark = isCurrent ? styles.success(' *') : '';
89
+ // Pad the plain name first, then apply styling (ANSI codes break padEnd)
90
+ const paddedName = theme.name.padEnd(33);
91
+ const label = isSelected ? styles.primary(paddedName) : paddedName;
92
+ return ` ${prefix}${label}${icon}${checkmark}`;
93
+ },
94
+ renderSelectedPreview: (theme, styles) => {
95
+ return renderPreview(theme, styles);
96
+ },
97
+ footerHints: (searchMode) => {
98
+ if (searchMode) {
99
+ return 'Type to filter · ↑↓/jk Navigate · Enter Select · Esc Exit search';
100
+ }
101
+ return 'Tab Filter · ↑↓/jk · ←→/hl Pages · / Search · Enter Select · q/Esc Close';
102
+ },
103
+ emptyMessage: 'No themes available.',
104
+ noResultsMessage: 'No themes match the search.',
105
+ });
106
+ this.currentThemeId = currentThemeId;
107
+ this.curatedIds = curatedIds;
108
+ }
109
+ handleKey(key) {
110
+ // Handle Enter/Space to select theme
111
+ if ((key.name === 'return' || key.name === 'space') && this.state.filteredItems.length > 0) {
112
+ const theme = this.state.filteredItems[this.state.selectedIndex];
113
+ return this.selectTheme(theme);
114
+ }
115
+ // Let parent handle other keys
116
+ return super.handleKey(key);
117
+ }
118
+ selectTheme(theme) {
119
+ setCurrentTheme(theme.id);
120
+ clearStyleCache();
121
+ return this.close({
122
+ themeChanged: theme.id,
123
+ themeName: theme.name,
124
+ });
125
+ }
126
+ /**
127
+ * Optional summary shown after overlay closes.
128
+ */
129
+ getCloseSummary(result) {
130
+ if (result.themeName) {
131
+ return `Theme changed to: ${result.themeName}`;
132
+ }
133
+ return null;
134
+ }
135
+ }