@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,654 @@
1
+ /**
2
+ * Config Overlay V2
3
+ *
4
+ * Claude Code-style settings overlay with 4 tabs:
5
+ * - Status: Read-only info (version, model, provider, cwd, session)
6
+ * - Config: Interactive settings (toggle/cycle with Enter/Space)
7
+ * - Paths: Path configuration and safety settings
8
+ * - Usage: Usage statistics with progress bars
9
+ *
10
+ * Theme, Model, and Mascot selection are delegated to standalone overlays.
11
+ * Uses BaseOverlayV2 for the new Overlay interface.
12
+ */
13
+ import * as fs from 'fs';
14
+ import * as path from 'path';
15
+ import { BaseOverlayV2, isEscape, isTab, isShiftTab, isEnter, isSpace, isCtrlC, isNavigateUp, isNavigateDown, isBackspace, getNumberKey, isPrintable, extractPrintable, } from '../../base/index.js';
16
+ import { getCurrentTheme, } from '../../../themes/index.js';
17
+ import { getSettings, setSetting, permissionModeToDisplay, displayToPermissionMode, notificationModeToDisplay, displayToNotificationMode, projectStartupModeToDisplay, displayToProjectStartupMode, } from '../../../settings/index.js';
18
+ import { getResolvedPathConfig, setDeleteProtection, setProjectMatchRequired, setWorkspacePath, setProjectsPath, setDataPath, } from '../../../settings/paths.js';
19
+ import { MASCOT_LABELS, } from '../../mascot/index.js';
20
+ import { ThemeOverlayV2 } from './theme-overlay-v2.js';
21
+ import { ModelOverlayV2 } from './model-overlay-v2.js';
22
+ import { MascotOverlayV2 } from './mascot-overlay-v2.js';
23
+ // =============================================================================
24
+ // Constants
25
+ // =============================================================================
26
+ const VERSION = '0.4.0';
27
+ const TARGET_HEIGHT = 25;
28
+ const TABS = [
29
+ { id: 'status', label: 'Status' },
30
+ { id: 'config', label: 'Config' },
31
+ { id: 'paths', label: 'Paths' },
32
+ { id: 'usage', label: 'Usage' },
33
+ ];
34
+ const MODEL_OPTIONS = [
35
+ // Claude
36
+ { id: 'claude-sonnet-4-20250514', name: 'Sonnet 4', description: 'Best for everyday tasks', provider: 'claude' },
37
+ { id: 'claude-opus-4-20250514', name: 'Opus 4', description: 'Most capable for complex work', provider: 'claude' },
38
+ { id: 'claude-3-5-haiku-20241022', name: 'Haiku 3.5', description: 'Fastest for quick answers', provider: 'claude' },
39
+ // OpenAI
40
+ { id: 'gpt-4o', name: 'GPT-4o', description: 'Best overall', provider: 'openai' },
41
+ { id: 'gpt-4o-mini', name: 'GPT-4o Mini', description: 'Fast and affordable', provider: 'openai' },
42
+ // Gemini
43
+ { id: 'gemini-2.0-flash', name: 'Gemini 2.0 Flash', description: 'Fast multimodal', provider: 'gemini' },
44
+ { id: 'gemini-2.5-flash', name: 'Gemini 2.5 Flash', description: 'Latest stable flash', provider: 'gemini' },
45
+ { id: 'gemini-2.5-pro', name: 'Gemini 2.5 Pro', description: 'Most capable Gemini', provider: 'gemini' },
46
+ // Ollama
47
+ { id: 'llama3.2', name: 'Llama 3.2', description: 'Local model', provider: 'ollama' },
48
+ { id: 'mistral', name: 'Mistral', description: 'Local model', provider: 'ollama' },
49
+ { id: 'codellama', name: 'Code Llama', description: 'Code-focused local model', provider: 'ollama' },
50
+ ];
51
+ // =============================================================================
52
+ // Helper Functions
53
+ // =============================================================================
54
+ function generateSessionId() {
55
+ const chars = '0123456789abcdef';
56
+ let id = '';
57
+ for (let i = 0; i < 8; i++) {
58
+ id += chars[Math.floor(Math.random() * chars.length)];
59
+ }
60
+ id += '-';
61
+ for (let i = 0; i < 4; i++) {
62
+ id += chars[Math.floor(Math.random() * chars.length)];
63
+ }
64
+ id += '-';
65
+ for (let i = 0; i < 4; i++) {
66
+ id += chars[Math.floor(Math.random() * chars.length)];
67
+ }
68
+ id += '-';
69
+ for (let i = 0; i < 12; i++) {
70
+ id += chars[Math.floor(Math.random() * chars.length)];
71
+ }
72
+ return id;
73
+ }
74
+ function findMemoryFile(cwd) {
75
+ const dir = cwd || process.cwd();
76
+ const candidates = [
77
+ 'COMPILR.md',
78
+ '.compilr/instructions.md',
79
+ 'CLAUDE.md',
80
+ '.claude/instructions.md',
81
+ 'AGENTS.md',
82
+ ];
83
+ for (const candidate of candidates) {
84
+ const fullPath = path.join(dir, candidate);
85
+ if (fs.existsSync(fullPath)) {
86
+ return fullPath;
87
+ }
88
+ }
89
+ return null;
90
+ }
91
+ function getConfigItems(currentModel) {
92
+ const currentTheme = getCurrentTheme();
93
+ const settings = getSettings();
94
+ // Find display name for current model
95
+ const modelOption = MODEL_OPTIONS.find(m => m.id === currentModel);
96
+ const modelDisplay = modelOption ? modelOption.name : currentModel || 'Default';
97
+ return [
98
+ { id: 'autoCompact', label: 'Auto-compact', type: 'boolean', value: settings.autoCompact },
99
+ { id: 'showTips', label: 'Show tips', type: 'boolean', value: settings.showTips },
100
+ { id: 'reviseCode', label: 'Revise code (checkpoints)', type: 'boolean', value: settings.reviseCode },
101
+ { id: 'verbose', label: 'Verbose output', type: 'boolean', value: settings.verbose },
102
+ { id: 'progressBar', label: 'Terminal progress bar', type: 'boolean', value: settings.progressBar },
103
+ {
104
+ id: 'permissionMode',
105
+ label: 'Default permission mode',
106
+ type: 'cycle',
107
+ value: permissionModeToDisplay(settings.permissionMode),
108
+ options: ['Default', 'Plan', 'Bypass prompts'],
109
+ },
110
+ { id: 'gitignore', label: 'Respect .gitignore in file picker', type: 'boolean', value: settings.gitignore },
111
+ { id: 'theme', label: 'Theme', type: 'submenu', value: currentTheme.name },
112
+ { id: 'model', label: 'Model', type: 'submenu', value: modelDisplay },
113
+ {
114
+ id: 'notifications',
115
+ label: 'Notifications',
116
+ type: 'cycle',
117
+ value: notificationModeToDisplay(settings.notifications),
118
+ options: ['Auto', 'Enabled', 'Disabled'],
119
+ },
120
+ {
121
+ id: 'outputStyle',
122
+ label: 'Output style',
123
+ type: 'cycle',
124
+ value: settings.outputStyle,
125
+ options: ['default', 'compact', 'verbose'],
126
+ },
127
+ {
128
+ id: 'editorMode',
129
+ label: 'Editor mode',
130
+ type: 'cycle',
131
+ value: settings.editorMode,
132
+ options: ['normal', 'vim', 'emacs'],
133
+ },
134
+ { id: 'autoInstall', label: 'Auto-install IDE extension', type: 'boolean', value: settings.autoInstall },
135
+ {
136
+ id: 'startupMode',
137
+ label: 'Startup behavior',
138
+ type: 'cycle',
139
+ value: settings.startupMode === 'menu' ? 'Show menu' : 'Direct to REPL',
140
+ options: ['Show menu', 'Direct to REPL'],
141
+ },
142
+ {
143
+ id: 'projectStartup',
144
+ label: 'Auto-open project',
145
+ type: 'cycle',
146
+ value: projectStartupModeToDisplay(settings.projectStartup),
147
+ options: ['Last opened', 'Off'],
148
+ },
149
+ { id: 'mascot', label: 'Mascot', type: 'submenu', value: MASCOT_LABELS[settings.mascot] },
150
+ ];
151
+ }
152
+ // =============================================================================
153
+ // Config Overlay V2 Class
154
+ // =============================================================================
155
+ export class ConfigOverlayV2 extends BaseOverlayV2 {
156
+ type = 'inline';
157
+ id = 'config-overlay-v2';
158
+ minHeight = TARGET_HEIGHT;
159
+ settingsChanged = false;
160
+ constructor(options = {}) {
161
+ super({
162
+ currentTab: 1, // Start on Config tab (like Claude Code)
163
+ selectedItem: 0,
164
+ scrollOffset: 0,
165
+ // Path settings state
166
+ pathSelectedItem: 0,
167
+ pathEditMode: false,
168
+ pathEditInput: '',
169
+ configItems: getConfigItems(options.model),
170
+ statusInfo: {
171
+ version: options.version || VERSION,
172
+ sessionId: options.sessionId || generateSessionId(),
173
+ cwd: options.cwd || process.cwd(),
174
+ model: options.model || 'unknown',
175
+ provider: options.provider || 'unknown',
176
+ toolCount: options.toolCount ?? 0,
177
+ startTime: options.startTime || new Date(),
178
+ memoryFile: findMemoryFile(options.cwd),
179
+ },
180
+ usageStats: {
181
+ inputTokens: options.inputTokens ?? 0,
182
+ outputTokens: options.outputTokens ?? 0,
183
+ requests: options.requests ?? 0,
184
+ contextUsed: options.contextUsed ?? 0,
185
+ contextMax: options.contextMax ?? 200000,
186
+ messageCount: options.messageCount ?? 0,
187
+ },
188
+ });
189
+ }
190
+ // ===========================================================================
191
+ // Rendering
192
+ // ===========================================================================
193
+ renderContent(context) {
194
+ const lines = [];
195
+ lines.push(...this.renderTabHeader(context));
196
+ switch (TABS[this.state.currentTab].id) {
197
+ case 'status':
198
+ lines.push(...this.renderStatusTab(context));
199
+ break;
200
+ case 'config':
201
+ lines.push(...this.renderConfigTab(context));
202
+ break;
203
+ case 'paths':
204
+ lines.push(...this.renderPathsTab(context));
205
+ break;
206
+ case 'usage':
207
+ lines.push(...this.renderUsageTab(context));
208
+ break;
209
+ }
210
+ lines.push(...this.renderTabFooter(context));
211
+ // Pad to target height for consistent rendering
212
+ while (lines.length < TARGET_HEIGHT) {
213
+ lines.push('');
214
+ }
215
+ return lines;
216
+ }
217
+ renderTabHeader(context) {
218
+ const s = context.styles;
219
+ const lines = [];
220
+ const cols = context.width;
221
+ lines.push(s.muted('─'.repeat(Math.max(1, cols - 1))));
222
+ // Tab row
223
+ let tabLine = ' Settings: ';
224
+ for (let i = 0; i < TABS.length; i++) {
225
+ const tab = TABS[i];
226
+ if (i === this.state.currentTab) {
227
+ tabLine += s.selected(` ${tab.label} `) + ' ';
228
+ }
229
+ else {
230
+ tabLine += s.muted(` ${tab.label} `) + ' ';
231
+ }
232
+ }
233
+ tabLine += s.muted('(Tab · 1-4)');
234
+ lines.push(tabLine);
235
+ lines.push('');
236
+ return lines;
237
+ }
238
+ renderStatusTab(context) {
239
+ const s = context.styles;
240
+ const lines = [];
241
+ const info = this.state.statusInfo;
242
+ // Calculate uptime
243
+ const uptimeMs = Date.now() - info.startTime.getTime();
244
+ const uptimeSecs = Math.floor(uptimeMs / 1000);
245
+ const uptimeMins = Math.floor(uptimeSecs / 60);
246
+ const uptimeStr = uptimeMins > 0
247
+ ? `${String(uptimeMins)}m ${String(uptimeSecs % 60)}s`
248
+ : `${String(uptimeSecs)}s`;
249
+ lines.push(` Version: ${s.primary(info.version)}`);
250
+ lines.push(` Session: ${s.muted(info.sessionId)}`);
251
+ lines.push(` Uptime: ${s.muted(uptimeStr)}`);
252
+ lines.push('');
253
+ lines.push(` Provider: ${s.primary(info.provider)}`);
254
+ lines.push(` Model: ${s.primary(info.model)}`);
255
+ lines.push(` Tools: ${s.muted(String(info.toolCount))}`);
256
+ lines.push('');
257
+ lines.push(` Cwd: ${s.muted(info.cwd)}`);
258
+ lines.push(` Memory: ${s.muted(info.memoryFile || 'none')}`);
259
+ lines.push(` Settings: ${s.muted('~/.compilr-dev/settings.json')}`);
260
+ return lines;
261
+ }
262
+ renderConfigTab(context) {
263
+ const s = context.styles;
264
+ const lines = [];
265
+ lines.push(s.primaryBold(' Configure preferences'));
266
+ lines.push('');
267
+ for (let i = 0; i < this.state.configItems.length; i++) {
268
+ const item = this.state.configItems[i];
269
+ const isSelected = i === this.state.selectedItem;
270
+ const prefix = isSelected ? s.primary(' ❯ ') : ' ';
271
+ let valueStr;
272
+ if (item.type === 'boolean') {
273
+ valueStr = item.value ? s.success('true') : s.muted('false');
274
+ }
275
+ else if (item.type === 'submenu') {
276
+ valueStr = s.primary(String(item.value));
277
+ }
278
+ else {
279
+ valueStr = s.primary(String(item.value));
280
+ }
281
+ const labelStyled = isSelected
282
+ ? s.primary(item.label.padEnd(35))
283
+ : item.label.padEnd(35);
284
+ lines.push(prefix + labelStyled + valueStr);
285
+ }
286
+ return lines;
287
+ }
288
+ renderPathsTab(context) {
289
+ const s = context.styles;
290
+ const lines = [];
291
+ const pathConfig = getResolvedPathConfig();
292
+ const cols = context.width;
293
+ // Layout constants
294
+ const labelWidth = 24;
295
+ const valueWidth = Math.max(30, cols - labelWidth - 8);
296
+ lines.push(s.primaryBold(' Path Configuration'));
297
+ lines.push('');
298
+ // Helper to render a path item
299
+ const renderPathItem = (index, label, value) => {
300
+ const isSelected = this.state.pathSelectedItem === index;
301
+ const prefix = isSelected ? s.primary(' ❯ ') : ' ';
302
+ if (this.state.pathEditMode && isSelected) {
303
+ // Edit mode - show input field
304
+ const labelStyled = s.primary(label.padEnd(labelWidth));
305
+ const inputDisplay = this.state.pathEditInput + '█';
306
+ lines.push(`${prefix}${labelStyled}${inputDisplay}`);
307
+ }
308
+ else {
309
+ // Normal mode - show value
310
+ const labelStyled = isSelected ? s.primary(label.padEnd(labelWidth)) : label.padEnd(labelWidth);
311
+ const truncatedValue = value.length > valueWidth ? value.slice(0, valueWidth - 3) + '...' : value;
312
+ const valueStyled = isSelected ? s.primary(truncatedValue) : s.muted(truncatedValue);
313
+ lines.push(`${prefix}${labelStyled}${valueStyled}`);
314
+ }
315
+ };
316
+ // Path items (0-2)
317
+ renderPathItem(0, 'Workspace path', pathConfig.workspacePath);
318
+ renderPathItem(1, 'Projects path', pathConfig.projectsPath);
319
+ renderPathItem(2, 'Data path', pathConfig.dataPath);
320
+ lines.push('');
321
+ lines.push(s.primaryBold(' Safety Settings'));
322
+ lines.push('');
323
+ // Helper to render a toggle item with consistent alignment
324
+ const renderToggleItem = (index, label, enabled, hint) => {
325
+ const isSelected = this.state.pathSelectedItem === index;
326
+ const prefix = isSelected ? s.primary(' ❯ ') : ' ';
327
+ const labelStyled = isSelected ? s.primary(label.padEnd(labelWidth)) : label.padEnd(labelWidth);
328
+ const valueStyled = enabled ? s.success('enabled'.padStart(10)) : s.warning('disabled'.padStart(10));
329
+ lines.push(`${prefix}${labelStyled}${valueStyled}`);
330
+ lines.push(s.muted(` ${hint}`));
331
+ };
332
+ // Toggle items (3-4)
333
+ renderToggleItem(3, 'Delete protection', pathConfig.deleteProtection, 'Only allow file deletion within projects path');
334
+ renderToggleItem(4, 'CWD mismatch warning', pathConfig.requireProjectMatch, 'Warn when current directory differs from active project');
335
+ lines.push('');
336
+ // Allowed paths (if any)
337
+ if (pathConfig.allowedPaths.length > 0) {
338
+ lines.push(s.primaryBold(' Additional Allowed Paths'));
339
+ for (const allowed of pathConfig.allowedPaths) {
340
+ lines.push(` ${s.muted('•')} ${allowed}`);
341
+ }
342
+ lines.push('');
343
+ }
344
+ return lines;
345
+ }
346
+ renderUsageTab(context) {
347
+ const s = context.styles;
348
+ const lines = [];
349
+ const stats = this.state.usageStats;
350
+ // Format numbers with K suffix
351
+ const formatNum = (n) => {
352
+ if (n >= 1000)
353
+ return `${(n / 1000).toFixed(1)}k`;
354
+ return String(n);
355
+ };
356
+ lines.push(s.primaryBold(' Session Tokens'));
357
+ lines.push(` Input: ${s.primary(formatNum(stats.inputTokens).padStart(8))}`);
358
+ lines.push(` Output: ${s.primary(formatNum(stats.outputTokens).padStart(8))}`);
359
+ lines.push(` Total: ${s.primary(formatNum(stats.inputTokens + stats.outputTokens).padStart(8))}`);
360
+ lines.push('');
361
+ lines.push(s.primaryBold(' Context Window'));
362
+ const contextPct = stats.contextMax > 0 ? stats.contextUsed / stats.contextMax : 0;
363
+ const contextPctDisplay = Math.round(contextPct * 100);
364
+ lines.push(` ${this.renderProgressBar(contextPct, 30, s)} ${String(contextPctDisplay)}% used`);
365
+ lines.push(s.muted(` ${formatNum(Math.round(stats.contextUsed))} / ${formatNum(stats.contextMax)} tokens`));
366
+ lines.push('');
367
+ lines.push(s.primaryBold(' Session Activity'));
368
+ lines.push(` Requests: ${s.primary(String(stats.requests))}`);
369
+ lines.push(` Messages: ${s.primary(String(stats.messageCount))}`);
370
+ return lines;
371
+ }
372
+ renderProgressBar(value, width, s) {
373
+ const filled = Math.round(value * width);
374
+ const empty = width - filled;
375
+ let colorFn = s.success;
376
+ if (value > 0.8)
377
+ colorFn = s.error;
378
+ else if (value > 0.6)
379
+ colorFn = s.warning;
380
+ return colorFn('█'.repeat(filled)) + s.muted('░'.repeat(empty));
381
+ }
382
+ renderTabFooter(context) {
383
+ const s = context.styles;
384
+ const lines = [];
385
+ lines.push('');
386
+ const tabId = TABS[this.state.currentTab].id;
387
+ if (tabId === 'config') {
388
+ lines.push(s.muted(' ↑↓/jk Navigate · Enter/Space Change · q/Esc Close'));
389
+ }
390
+ else if (tabId === 'paths') {
391
+ if (this.state.pathEditMode) {
392
+ lines.push(s.muted(' Type path · Enter Save · Esc Cancel'));
393
+ }
394
+ else {
395
+ lines.push(s.muted(' ↑↓/jk Navigate · Enter Edit/Toggle · q/Esc Close'));
396
+ }
397
+ }
398
+ else {
399
+ lines.push(s.muted(' q/Esc Close'));
400
+ }
401
+ return lines;
402
+ }
403
+ // ===========================================================================
404
+ // Key Handling
405
+ // ===========================================================================
406
+ handleKey(key) {
407
+ const data = key.raw;
408
+ const char = extractPrintable(data);
409
+ // Handle path edit mode first
410
+ if (this.state.pathEditMode) {
411
+ return this.handlePathEditKey(data, char);
412
+ }
413
+ // q or Esc closes
414
+ if (isEscape(data) || isCtrlC(data) || this.isQuit(data)) {
415
+ return this.close({ settingsChanged: this.settingsChanged });
416
+ }
417
+ // Tab switching: Tab for next, Shift+Tab for previous
418
+ if (isTab(data)) {
419
+ this.state.currentTab = (this.state.currentTab + 1) % TABS.length;
420
+ this.state.selectedItem = 0;
421
+ this.state.pathSelectedItem = 0;
422
+ return this.rerender();
423
+ }
424
+ if (isShiftTab(data)) {
425
+ this.state.currentTab = (this.state.currentTab - 1 + TABS.length) % TABS.length;
426
+ this.state.selectedItem = 0;
427
+ this.state.pathSelectedItem = 0;
428
+ return this.rerender();
429
+ }
430
+ // Number keys 1-4 to directly select tabs
431
+ const tabNumKey = getNumberKey(data);
432
+ if (tabNumKey !== null && tabNumKey >= 1 && tabNumKey <= TABS.length) {
433
+ this.state.currentTab = tabNumKey - 1;
434
+ this.state.selectedItem = 0;
435
+ this.state.pathSelectedItem = 0;
436
+ return this.rerender();
437
+ }
438
+ // Tab-specific key handling
439
+ const tabId = TABS[this.state.currentTab].id;
440
+ if (tabId === 'config') {
441
+ return this.handleConfigTabKey(data);
442
+ }
443
+ if (tabId === 'paths') {
444
+ return this.handlePathsTabKey(data);
445
+ }
446
+ return this.noAction();
447
+ }
448
+ handleConfigTabKey(data) {
449
+ // Navigation
450
+ if (isNavigateUp(data) && this.state.selectedItem > 0) {
451
+ this.state.selectedItem--;
452
+ return this.rerender();
453
+ }
454
+ if (isNavigateDown(data) && this.state.selectedItem < this.state.configItems.length - 1) {
455
+ this.state.selectedItem++;
456
+ return this.rerender();
457
+ }
458
+ // Toggle/cycle/submenu
459
+ if (isEnter(data) || isSpace(data)) {
460
+ const subOverlay = this.toggleOrCycleItem();
461
+ if (subOverlay) {
462
+ return this.push(subOverlay);
463
+ }
464
+ return this.rerender();
465
+ }
466
+ return this.noAction();
467
+ }
468
+ handlePathsTabKey(data) {
469
+ const pathItemCount = 5; // 3 paths + 2 toggles
470
+ // Navigation
471
+ if (isNavigateUp(data) && this.state.pathSelectedItem > 0) {
472
+ this.state.pathSelectedItem--;
473
+ return this.rerender();
474
+ }
475
+ if (isNavigateDown(data) && this.state.pathSelectedItem < pathItemCount - 1) {
476
+ this.state.pathSelectedItem++;
477
+ return this.rerender();
478
+ }
479
+ // Edit/toggle
480
+ if (isEnter(data) || isSpace(data)) {
481
+ const currentConfig = getResolvedPathConfig();
482
+ if (this.state.pathSelectedItem <= 2) {
483
+ // Path items (0-2) - enter edit mode
484
+ this.state.pathEditMode = true;
485
+ switch (this.state.pathSelectedItem) {
486
+ case 0:
487
+ this.state.pathEditInput = currentConfig.workspacePath;
488
+ break;
489
+ case 1:
490
+ this.state.pathEditInput = currentConfig.projectsPath;
491
+ break;
492
+ case 2:
493
+ this.state.pathEditInput = currentConfig.dataPath;
494
+ break;
495
+ }
496
+ return this.rerender();
497
+ }
498
+ else if (this.state.pathSelectedItem === 3) {
499
+ // Toggle delete protection
500
+ setDeleteProtection(!currentConfig.deleteProtection);
501
+ this.settingsChanged = true;
502
+ return this.rerender();
503
+ }
504
+ else if (this.state.pathSelectedItem === 4) {
505
+ // Toggle CWD match warning
506
+ setProjectMatchRequired(!currentConfig.requireProjectMatch);
507
+ this.settingsChanged = true;
508
+ return this.rerender();
509
+ }
510
+ }
511
+ return this.noAction();
512
+ }
513
+ handlePathEditKey(data, char) {
514
+ if (isEscape(data)) {
515
+ // Cancel edit
516
+ this.state.pathEditMode = false;
517
+ this.state.pathEditInput = '';
518
+ return this.rerender();
519
+ }
520
+ if (isEnter(data)) {
521
+ // Save the path
522
+ const newPath = this.state.pathEditInput.trim();
523
+ if (newPath) {
524
+ switch (this.state.pathSelectedItem) {
525
+ case 0:
526
+ setWorkspacePath(newPath);
527
+ break;
528
+ case 1:
529
+ setProjectsPath(newPath);
530
+ break;
531
+ case 2:
532
+ setDataPath(newPath);
533
+ break;
534
+ }
535
+ this.settingsChanged = true;
536
+ }
537
+ this.state.pathEditMode = false;
538
+ this.state.pathEditInput = '';
539
+ return this.rerender();
540
+ }
541
+ if (isBackspace(data)) {
542
+ this.state.pathEditInput = this.state.pathEditInput.slice(0, -1);
543
+ return this.rerender();
544
+ }
545
+ if (isPrintable(data)) {
546
+ this.state.pathEditInput += char;
547
+ return this.rerender();
548
+ }
549
+ return this.noAction();
550
+ }
551
+ // ===========================================================================
552
+ // Config Item Actions
553
+ // ===========================================================================
554
+ /**
555
+ * Toggle/cycle a config item, or return a sub-overlay for submenus.
556
+ * Returns the sub-overlay to push, or null if handled internally.
557
+ */
558
+ toggleOrCycleItem() {
559
+ const item = this.state.configItems[this.state.selectedItem];
560
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
561
+ if (!item)
562
+ return null; // Defensive check
563
+ if (item.type === 'boolean') {
564
+ const newValue = !item.value;
565
+ item.value = newValue;
566
+ this.settingsChanged = true;
567
+ // Persist boolean settings
568
+ switch (item.id) {
569
+ case 'autoCompact':
570
+ setSetting('autoCompact', newValue);
571
+ break;
572
+ case 'showTips':
573
+ setSetting('showTips', newValue);
574
+ break;
575
+ case 'reviseCode':
576
+ setSetting('reviseCode', newValue);
577
+ break;
578
+ case 'verbose':
579
+ setSetting('verbose', newValue);
580
+ break;
581
+ case 'progressBar':
582
+ setSetting('progressBar', newValue);
583
+ break;
584
+ case 'gitignore':
585
+ setSetting('gitignore', newValue);
586
+ break;
587
+ case 'autoInstall':
588
+ setSetting('autoInstall', newValue);
589
+ break;
590
+ }
591
+ return null;
592
+ }
593
+ if (item.type === 'cycle' && item.options) {
594
+ const currentIndex = item.options.indexOf(String(item.value));
595
+ const nextIndex = (currentIndex + 1) % item.options.length;
596
+ const newValue = item.options[nextIndex];
597
+ item.value = newValue;
598
+ this.settingsChanged = true;
599
+ // Persist cycle settings
600
+ switch (item.id) {
601
+ case 'permissionMode':
602
+ setSetting('permissionMode', displayToPermissionMode(newValue));
603
+ break;
604
+ case 'notifications':
605
+ setSetting('notifications', displayToNotificationMode(newValue));
606
+ break;
607
+ case 'outputStyle':
608
+ setSetting('outputStyle', newValue);
609
+ break;
610
+ case 'editorMode':
611
+ setSetting('editorMode', newValue);
612
+ break;
613
+ case 'startupMode':
614
+ setSetting('startupMode', newValue === 'Show menu' ? 'menu' : 'repl');
615
+ break;
616
+ case 'projectStartup':
617
+ setSetting('projectStartup', displayToProjectStartupMode(newValue));
618
+ break;
619
+ }
620
+ return null;
621
+ }
622
+ // Submenus - return the overlay to push
623
+ if (item.type === 'submenu') {
624
+ if (item.id === 'theme') {
625
+ return new ThemeOverlayV2();
626
+ }
627
+ if (item.id === 'model') {
628
+ return new ModelOverlayV2({
629
+ currentModel: this.state.statusInfo.model,
630
+ currentProvider: this.state.statusInfo.provider,
631
+ });
632
+ }
633
+ if (item.id === 'mascot') {
634
+ return new MascotOverlayV2({
635
+ currentMascot: getSettings().mascot,
636
+ });
637
+ }
638
+ }
639
+ return null;
640
+ }
641
+ isQuit(data) {
642
+ return data.toString() === 'q';
643
+ }
644
+ // ===========================================================================
645
+ // Lifecycle
646
+ // ===========================================================================
647
+ /**
648
+ * When overlay closes, refresh config items to reflect any changes from sub-overlays.
649
+ */
650
+ onUnmount() {
651
+ // Refresh config items when returning from sub-overlays
652
+ this.state.configItems = getConfigItems(this.state.statusInfo.model);
653
+ }
654
+ }