@pellux/goodvibes-tui 2.0.21 → 2.1.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 (388) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +16 -16
  3. package/docs/README.md +4 -4
  4. package/docs/foundation-artifacts/operator-contract.json +1 -1
  5. package/package.json +16 -6
  6. package/src/cli/management.ts +5 -2
  7. package/src/cli/tui-startup.ts +34 -39
  8. package/src/core/agent-view-render.ts +157 -0
  9. package/src/core/approval-alert.ts +49 -11
  10. package/src/core/bookmark-navigation.ts +7 -1
  11. package/src/core/budget-breach-notifier.ts +27 -8
  12. package/src/core/compaction-receipt.ts +1 -1
  13. package/src/core/context-usage.ts +1 -1
  14. package/src/core/conversation-compaction-render.ts +3 -3
  15. package/src/core/conversation-display-only.ts +86 -0
  16. package/src/core/conversation-event-navigation.ts +27 -0
  17. package/src/core/conversation-line-cache.ts +216 -447
  18. package/src/core/conversation-render-context.ts +20 -27
  19. package/src/core/conversation-rendering.ts +157 -575
  20. package/src/core/conversation-splash-state.ts +6 -1
  21. package/src/core/conversation-types.ts +35 -0
  22. package/src/core/conversation-usage.ts +12 -0
  23. package/src/core/conversation.ts +130 -131
  24. package/src/core/long-task-notifier.ts +100 -31
  25. package/src/core/notices.ts +75 -0
  26. package/src/core/pairing-tailscale-gateway.ts +1 -1
  27. package/src/core/process-output-log.ts +143 -0
  28. package/src/core/rewind-receipt.ts +1 -1
  29. package/src/core/session-focus.ts +190 -0
  30. package/src/core/session-resume-core.ts +23 -81
  31. package/src/core/stream-event-wiring.ts +31 -5
  32. package/src/core/system-message-noise.ts +2 -2
  33. package/src/core/system-message-router.ts +16 -18
  34. package/src/core/turn-event-wiring.ts +272 -33
  35. package/src/core/work-tree-focus.ts +290 -0
  36. package/src/core/work-tree-fold-store.ts +143 -0
  37. package/src/core/work-tree-model.ts +799 -0
  38. package/src/core/work-tree-render.ts +384 -0
  39. package/src/core/work-tree-sources.ts +184 -0
  40. package/src/core/work-tree-wiring.ts +176 -0
  41. package/src/core/workstream-notification.ts +36 -6
  42. package/src/core/wrfc-notice-titles.ts +37 -0
  43. package/src/input/agents-modal-text.ts +119 -0
  44. package/src/input/agents-modal.ts +699 -0
  45. package/src/input/bookmark-modal.ts +33 -9
  46. package/src/input/changes-git.ts +251 -0
  47. package/src/input/changes-modal.ts +629 -0
  48. package/src/input/changes-model.ts +125 -0
  49. package/src/input/command-palette-types.ts +41 -0
  50. package/src/input/command-palette.ts +272 -0
  51. package/src/input/command-registry.ts +77 -35
  52. package/src/input/commands/acp-runtime.ts +19 -9
  53. package/src/input/commands/channel-runtime.ts +2 -2
  54. package/src/input/commands/ci-runtime.ts +1 -1
  55. package/src/input/commands/codebase-runtime.ts +1 -1
  56. package/src/input/commands/config.ts +7 -1
  57. package/src/input/commands/context-window.ts +25 -19
  58. package/src/input/commands/control-room-runtime.ts +18 -18
  59. package/src/input/commands/conversation-runtime.ts +0 -1
  60. package/src/input/commands/cost-runtime.ts +29 -20
  61. package/src/input/commands/diff-runtime.ts +35 -181
  62. package/src/input/commands/eval.ts +1 -1
  63. package/src/input/commands/experience-runtime.ts +3 -7
  64. package/src/input/commands/fresh-conversation.ts +86 -0
  65. package/src/input/commands/git-runtime.ts +5 -22
  66. package/src/input/commands/graph-runtime.ts +1 -1
  67. package/src/input/commands/guidance-runtime.ts +1 -1
  68. package/src/input/commands/health-runtime.ts +8 -9
  69. package/src/input/commands/hooks-runtime.ts +2 -2
  70. package/src/input/commands/hosted-runtime.ts +3 -3
  71. package/src/input/commands/incident-runtime.ts +3 -3
  72. package/src/input/commands/intelligence-runtime.ts +6 -9
  73. package/src/input/commands/knowledge.ts +2 -2
  74. package/src/input/commands/legacy-panel-command.ts +57 -0
  75. package/src/input/commands/local-auth-runtime.ts +8 -9
  76. package/src/input/commands/local-provider-runtime.ts +7 -4
  77. package/src/input/commands/local-runtime.ts +12 -15
  78. package/src/input/commands/local-setup-review.ts +1 -1
  79. package/src/input/commands/local-setup.ts +13 -19
  80. package/src/input/commands/memory.ts +1 -1
  81. package/src/input/commands/notify-runtime.ts +9 -0
  82. package/src/input/commands/operator-runtime.ts +10 -12
  83. package/src/input/commands/permissions-provenance.ts +3 -3
  84. package/src/input/commands/planning-runtime.ts +7 -7
  85. package/src/input/commands/platform-sandbox-runtime.ts +2 -2
  86. package/src/input/commands/platform-services-runtime.ts +1 -1
  87. package/src/input/commands/policy-dispatch.ts +2 -2
  88. package/src/input/commands/policy.ts +3 -3
  89. package/src/input/commands/provider.ts +1 -1
  90. package/src/input/commands/remote-runtime.ts +1 -1
  91. package/src/input/commands/review-runtime.ts +39 -94
  92. package/src/input/commands/rewind-runtime.ts +81 -108
  93. package/src/input/commands/runtime-services.ts +15 -17
  94. package/src/input/commands/services-runtime.ts +2 -2
  95. package/src/input/commands/session-content.ts +21 -5
  96. package/src/input/commands/session-workflow.ts +13 -38
  97. package/src/input/commands/settings-sync-runtime.ts +2 -2
  98. package/src/input/commands/shell-core.ts +19 -4
  99. package/src/input/commands/skills-runtime.ts +2 -2
  100. package/src/input/commands/status-runtime.ts +13 -0
  101. package/src/input/commands/subscription-runtime.ts +1 -1
  102. package/src/input/commands/tasks-runtime.ts +2 -8
  103. package/src/input/commands/work-plan-runtime.ts +4 -4
  104. package/src/input/commands/workstream-attempts.ts +24 -47
  105. package/src/input/commands/workstream-runtime.ts +2 -2
  106. package/src/input/commands/worktree-runtime.ts +3 -3
  107. package/src/input/commands.ts +3 -1
  108. package/src/input/composer-editor.ts +44 -0
  109. package/src/input/concealed-input.ts +1 -1
  110. package/src/input/config-modal-types.ts +20 -8
  111. package/src/input/config-modal.ts +106 -105
  112. package/src/input/confirm-dialog-types.ts +28 -0
  113. package/src/input/confirm-dialog.ts +70 -0
  114. package/src/input/delete-key-policy.ts +3 -3
  115. package/src/input/feed-context-factory.ts +15 -16
  116. package/src/input/handler-command-route.ts +7 -42
  117. package/src/input/handler-content-actions.ts +16 -15
  118. package/src/input/handler-feed-routes.ts +26 -211
  119. package/src/input/handler-feed.ts +68 -95
  120. package/src/input/handler-interactions.ts +5 -3
  121. package/src/input/handler-modal-routes.ts +215 -216
  122. package/src/input/handler-modal-stack.ts +81 -11
  123. package/src/input/handler-modal-token-routes.ts +4 -0
  124. package/src/input/handler-onboarding.ts +0 -1
  125. package/src/input/handler-picker-routes.ts +65 -107
  126. package/src/input/handler-session-view-route.ts +149 -0
  127. package/src/input/handler-shortcuts.ts +16 -154
  128. package/src/input/handler-types.ts +4 -2
  129. package/src/input/handler-ui-state.ts +45 -15
  130. package/src/input/handler-work-tree-route.ts +123 -0
  131. package/src/input/handler.ts +25 -70
  132. package/src/input/keybindings.ts +16 -71
  133. package/src/input/masked-entry-modal.ts +117 -0
  134. package/src/input/mcp-workspace.ts +48 -17
  135. package/src/input/model-picker-filter.ts +8 -0
  136. package/src/input/model-picker-open.ts +99 -0
  137. package/src/input/model-picker-types.ts +1 -1
  138. package/src/input/model-picker.ts +92 -9
  139. package/src/input/notifications-modal.ts +123 -0
  140. package/src/input/overlay-filter.ts +26 -0
  141. package/src/input/profile-picker-modal.ts +37 -8
  142. package/src/input/search.ts +3 -3
  143. package/src/input/selection-modal-queue.ts +2 -0
  144. package/src/input/selection-modal.ts +28 -16
  145. package/src/input/session-picker-modal.ts +33 -8
  146. package/src/input/settings-modal-activation.ts +7 -2
  147. package/src/input/settings-modal-data.ts +11 -6
  148. package/src/input/settings-modal-types.ts +1 -1
  149. package/src/input/settings-modal.ts +20 -20
  150. package/src/input/submission-intent.ts +1 -1
  151. package/src/input/submission-router.ts +4 -9
  152. package/src/input/surface-modal-host.ts +110 -0
  153. package/src/input/theme-settings-actions.ts +82 -0
  154. package/src/input/usage-modal.ts +183 -0
  155. package/src/input/views.ts +97 -0
  156. package/src/main.ts +175 -165
  157. package/src/permissions/hunk-selection.ts +1 -1
  158. package/src/permissions/prompt-card.ts +511 -0
  159. package/src/permissions/prompt.ts +59 -622
  160. package/src/renderer/agents-modal.ts +310 -0
  161. package/src/renderer/autocomplete-overlay.ts +42 -220
  162. package/src/renderer/back-to-bottom.ts +80 -0
  163. package/src/renderer/block-actions-overlay.ts +30 -48
  164. package/src/renderer/bookmark-modal.ts +48 -79
  165. package/src/renderer/changes-modal.ts +325 -0
  166. package/src/renderer/code-block.ts +184 -106
  167. package/src/renderer/command-palette.ts +93 -0
  168. package/src/renderer/composer-fragments.ts +4 -4
  169. package/src/renderer/composer.ts +146 -0
  170. package/src/renderer/compositor.ts +26 -160
  171. package/src/renderer/config-modal.ts +185 -81
  172. package/src/renderer/confirm-dialog.ts +50 -0
  173. package/src/renderer/context-inspector.ts +116 -154
  174. package/src/renderer/context-status-hint.ts +4 -4
  175. package/src/renderer/conversation-layout.ts +20 -0
  176. package/src/renderer/conversation-overlays.ts +94 -84
  177. package/src/renderer/conversation-surface.ts +19 -38
  178. package/src/renderer/diff-tint.ts +98 -0
  179. package/src/renderer/file-picker-overlay.ts +63 -90
  180. package/src/renderer/footer-targets.ts +38 -0
  181. package/src/renderer/git-status.ts +3 -4
  182. package/src/renderer/header-line.ts +140 -117
  183. package/src/renderer/help-overlay.ts +133 -219
  184. package/src/renderer/history-search-overlay.ts +15 -12
  185. package/src/renderer/lane-graph/bead.ts +800 -0
  186. package/src/renderer/lane-graph/glyphs.ts +134 -0
  187. package/src/renderer/lane-graph/layout.ts +248 -0
  188. package/src/renderer/lane-graph/paint.ts +537 -0
  189. package/src/renderer/lane-graph/semantic-memo.ts +77 -0
  190. package/src/renderer/layout-engine.ts +2 -57
  191. package/src/renderer/layout.ts +7 -13
  192. package/src/renderer/markdown-inline.ts +2 -1
  193. package/src/renderer/markdown-table.ts +17 -12
  194. package/src/renderer/markdown.ts +92 -34
  195. package/src/renderer/masked-entry-modal.ts +78 -0
  196. package/src/renderer/mcp-workspace.ts +166 -203
  197. package/src/renderer/model-workspace.ts +381 -436
  198. package/src/renderer/notifications-modal.ts +116 -0
  199. package/src/renderer/onboarding/onboarding-wizard.ts +178 -588
  200. package/src/renderer/process-view.ts +192 -0
  201. package/src/renderer/profile-picker-modal.ts +58 -110
  202. package/src/renderer/search-overlay.ts +6 -3
  203. package/src/renderer/selection-modal-overlay.ts +87 -327
  204. package/src/renderer/semantic-diff.ts +2 -2
  205. package/src/renderer/session-chips.ts +102 -0
  206. package/src/renderer/session-picker-modal.ts +177 -297
  207. package/src/renderer/settings-modal-connections.ts +14 -23
  208. package/src/renderer/settings-modal-context.ts +367 -0
  209. package/src/renderer/settings-modal-helpers.ts +6 -11
  210. package/src/renderer/settings-modal.ts +322 -726
  211. package/src/renderer/shell-surface.ts +277 -175
  212. package/src/renderer/shortcuts-overlay.ts +217 -0
  213. package/src/renderer/status-line.ts +475 -0
  214. package/src/renderer/surface-compose.ts +174 -0
  215. package/src/renderer/surface-kit-extra.ts +303 -0
  216. package/src/renderer/surface-kit-list.ts +366 -0
  217. package/src/renderer/surface-kit-parts.ts +360 -0
  218. package/src/renderer/surface-kit.ts +568 -0
  219. package/src/renderer/syntax-highlighter.ts +280 -197
  220. package/src/renderer/syntax-theme.ts +51 -0
  221. package/src/renderer/system-message.ts +0 -1
  222. package/src/renderer/term-caps.ts +42 -1
  223. package/src/renderer/terminal-bg-probe.ts +116 -19
  224. package/src/renderer/terminal-palette-probe.ts +322 -0
  225. package/src/renderer/terminal-palette-reader.ts +183 -0
  226. package/src/renderer/terminal-palette.ts +58 -0
  227. package/src/renderer/theme-mode-config.ts +16 -1
  228. package/src/renderer/theme.ts +234 -207
  229. package/src/renderer/thinking.ts +0 -1
  230. package/src/renderer/throbber.ts +188 -0
  231. package/src/renderer/toast-center.ts +119 -0
  232. package/src/renderer/ui-factory.ts +39 -602
  233. package/src/renderer/ui-primitives.ts +8 -25
  234. package/src/renderer/usage-modal.ts +353 -0
  235. package/src/renderer/user-message.ts +58 -0
  236. package/src/renderer/voice-capture-chip.ts +53 -0
  237. package/src/runtime/bootstrap-command-context.ts +0 -5
  238. package/src/runtime/bootstrap-command-parts.ts +38 -89
  239. package/src/runtime/bootstrap-core.ts +13 -15
  240. package/src/runtime/bootstrap-hook-bridge.ts +12 -21
  241. package/src/runtime/bootstrap-notifier-sync.ts +17 -1
  242. package/src/runtime/bootstrap-shell.ts +16 -50
  243. package/src/runtime/bootstrap.ts +14 -14
  244. package/src/runtime/client/fleet-union.ts +5 -5
  245. package/src/runtime/client/hosted-exit.ts +2 -2
  246. package/src/runtime/client/hosted-sessions.ts +1 -1
  247. package/src/runtime/client/spine-adoption.ts +1 -1
  248. package/src/runtime/context-accounting-source.ts +5 -4
  249. package/src/runtime/index.ts +1 -0
  250. package/src/runtime/notification-dispatch.ts +102 -29
  251. package/src/runtime/recovery-offer-wiring.ts +4 -1
  252. package/src/runtime/recovery-prompt.ts +31 -15
  253. package/src/runtime/runtime-services-types.ts +10 -6
  254. package/src/runtime/services.ts +17 -12
  255. package/src/runtime/session-continuity-hints.ts +2 -5
  256. package/src/runtime/session-inbound-inputs.ts +1 -1
  257. package/src/runtime/session-resume-liveness-confirm.ts +14 -2
  258. package/src/runtime/startup-typeahead-gate.ts +103 -0
  259. package/src/runtime/store/domains/domain-read-matrix.ts +0 -1
  260. package/src/runtime/store/domains/index.ts +0 -8
  261. package/src/runtime/store/selectors/index.ts +0 -28
  262. package/src/runtime/store/state.ts +0 -8
  263. package/src/runtime/ui-services.ts +4 -6
  264. package/src/runtime/usage-tracker.ts +348 -0
  265. package/src/runtime/wrfc-persistence.ts +4 -4
  266. package/src/shell/blocking-input.ts +92 -2
  267. package/src/shell/recovery-input-helpers.ts +1 -1
  268. package/src/shell/session-views.ts +539 -0
  269. package/src/shell/status-report.ts +86 -0
  270. package/src/shell/transcript-scroll.ts +68 -0
  271. package/src/shell/ui-openers.ts +84 -172
  272. package/src/shell/view-openers.ts +205 -0
  273. package/src/tools/wrfc-agent-guard.ts +22 -2
  274. package/src/utils/format-duration.ts +4 -8
  275. package/src/utils/splash-lines.ts +9 -1
  276. package/src/utils/terminal-width.ts +56 -0
  277. package/src/verification/verification-ledger.ts +18 -16
  278. package/src/{panels → views}/agent-inspector-shared.ts +2 -2
  279. package/src/{panels → views}/builtin-modals.ts +22 -21
  280. package/src/views/builtin-views.ts +135 -0
  281. package/src/{panels → views}/confirm-state.ts +9 -9
  282. package/src/{panels → views}/diff-review-model.ts +10 -3
  283. package/src/{panels → views}/eval-registry.ts +1 -1
  284. package/src/{panels → views}/fleet-acts.ts +49 -85
  285. package/src/{panels/fleet-panel-format.ts → views/fleet-format.ts} +40 -40
  286. package/src/{panels → views}/fleet-gateway.ts +5 -5
  287. package/src/{panels → views}/fleet-observed-render.ts +16 -16
  288. package/src/{panels → views}/fleet-read-model.ts +9 -9
  289. package/src/{panels/fleet-tabs.ts → views/fleet-session-tabs.ts} +13 -13
  290. package/src/{panels → views}/fleet-spawn.ts +33 -49
  291. package/src/{panels → views}/fleet-steer.ts +12 -12
  292. package/src/{panels → views}/fleet-stop.ts +4 -4
  293. package/src/{panels → views}/fleet-transcript.ts +26 -27
  294. package/src/{panels/fleet-panel-worktree-detail.ts → views/fleet-worktree-detail.ts} +7 -7
  295. package/src/{panels → views}/hosted-session-feed.ts +6 -6
  296. package/src/views/modal-surface-registry.ts +41 -0
  297. package/src/{panels → views}/modals/devices-modal.ts +2 -2
  298. package/src/{panels → views}/modals/hooks-modal.ts +13 -13
  299. package/src/{panels → views}/modals/keybindings-modal.ts +19 -27
  300. package/src/{panels → views}/modals/knowledge-modal.ts +6 -5
  301. package/src/{panels → views}/modals/local-auth-modal.ts +22 -21
  302. package/src/{panels → views}/modals/marketplace-modal.ts +11 -11
  303. package/src/{panels → views}/modals/memory-modal.ts +8 -8
  304. package/src/{panels → views}/modals/modal-surface-helpers.ts +15 -5
  305. package/src/{panels → views}/modals/pairing-modal.ts +9 -9
  306. package/src/{panels → views}/modals/planning-modal.ts +15 -20
  307. package/src/{panels → views}/modals/plugins-modal.ts +6 -6
  308. package/src/{panels → views}/modals/policy-modal.ts +13 -5
  309. package/src/{panels → views}/modals/provider-health-modal.ts +4 -4
  310. package/src/{panels → views}/modals/remote-modal.ts +3 -3
  311. package/src/{panels → views}/modals/sandbox-modal.ts +4 -4
  312. package/src/{panels → views}/modals/security-modal.ts +22 -16
  313. package/src/{panels → views}/modals/services-modal.ts +3 -3
  314. package/src/{panels → views}/modals/settings-sync-modal.ts +4 -4
  315. package/src/{panels → views}/modals/skills-modal.ts +9 -9
  316. package/src/{panels → views}/modals/subscription-modal.ts +3 -3
  317. package/src/{panels → views}/modals/work-plan-modal.ts +10 -13
  318. package/src/views/notifications-feed.ts +314 -0
  319. package/src/{panels → views}/polish-core.ts +38 -36
  320. package/src/{panels → views}/polish-tables.ts +35 -26
  321. package/src/{panels → views}/polish.ts +101 -95
  322. package/src/{panels → views}/project-planning-answer-actions.ts +3 -3
  323. package/src/{panels → views}/provider-health-routes.ts +14 -14
  324. package/src/{panels → views}/search-focus.ts +8 -8
  325. package/src/{panels → views}/session-maintenance.ts +11 -11
  326. package/src/views/skills-discovery.ts +159 -0
  327. package/src/views/view-deps.ts +80 -0
  328. package/src/views/view-panel-adapter.ts +53 -0
  329. package/src/{panels → views}/workstream-graph-render.ts +1 -1
  330. package/src/core/conversation-fold.ts +0 -101
  331. package/src/core/conversation-turn-structure.ts +0 -464
  332. package/src/core/conversation-user-receipts.ts +0 -42
  333. package/src/input/commands/operator-panel-runtime.ts +0 -186
  334. package/src/input/panel-integration-actions.ts +0 -33
  335. package/src/input/panel-mouse-geometry.ts +0 -97
  336. package/src/input/panel-paste-flood-guard.ts +0 -86
  337. package/src/panels/base-panel.ts +0 -301
  338. package/src/panels/builtin/agent.ts +0 -27
  339. package/src/panels/builtin/development.ts +0 -92
  340. package/src/panels/builtin/knowledge.ts +0 -12
  341. package/src/panels/builtin/operations.ts +0 -177
  342. package/src/panels/builtin/session.ts +0 -81
  343. package/src/panels/builtin/shared.ts +0 -357
  344. package/src/panels/builtin-panels.ts +0 -28
  345. package/src/panels/cost-tracker-panel.ts +0 -746
  346. package/src/panels/diff-panel.ts +0 -797
  347. package/src/panels/diff-review-panel.ts +0 -395
  348. package/src/panels/expandable-list-panel.ts +0 -189
  349. package/src/panels/fleet-deep-link.ts +0 -31
  350. package/src/panels/fleet-panel.ts +0 -799
  351. package/src/panels/git-panel.ts +0 -800
  352. package/src/panels/hosted-session-panel.ts +0 -221
  353. package/src/panels/index.ts +0 -21
  354. package/src/panels/local-auth-panel.ts +0 -478
  355. package/src/panels/notifications-feed.ts +0 -127
  356. package/src/panels/notifications-panel.ts +0 -160
  357. package/src/panels/panel-confirm-overlay.ts +0 -72
  358. package/src/panels/panel-manager.ts +0 -791
  359. package/src/panels/plugins-panel.ts +0 -371
  360. package/src/panels/scrollable-list-panel.ts +0 -498
  361. package/src/panels/skills-panel.ts +0 -454
  362. package/src/panels/token-budget-panel.ts +0 -688
  363. package/src/panels/types.ts +0 -191
  364. package/src/panels/wrfc-panel-format.ts +0 -133
  365. package/src/renderer/diff-view.ts +0 -134
  366. package/src/renderer/fleet-tab-strip.ts +0 -56
  367. package/src/renderer/footer-tips.ts +0 -53
  368. package/src/renderer/fullscreen-primitives.ts +0 -146
  369. package/src/renderer/fullscreen-workspace.ts +0 -194
  370. package/src/renderer/hint-grammar.ts +0 -52
  371. package/src/renderer/modal-factory.ts +0 -480
  372. package/src/renderer/overlay-box.ts +0 -154
  373. package/src/renderer/overlay-viewport.ts +0 -104
  374. package/src/renderer/panel-composite.ts +0 -145
  375. package/src/renderer/panel-workspace-bar.ts +0 -54
  376. package/src/renderer/process-indicator.ts +0 -163
  377. package/src/renderer/tab-strip.ts +0 -222
  378. package/src/renderer/text-layout.ts +0 -31
  379. package/src/renderer/tool-call.ts +0 -276
  380. package/src/renderer/tool-result-expanded-lines.ts +0 -48
  381. package/src/renderer/tool-result-summary.ts +0 -219
  382. package/src/runtime/diagnostics/panels/index.ts +0 -21
  383. package/src/runtime/diagnostics/panels/ops.ts +0 -156
  384. package/src/runtime/perf/panel-contracts.ts +0 -32
  385. package/src/runtime/store/domains/panels.ts +0 -117
  386. /package/src/{panels → views}/memory-consolidation-gateway.ts +0 -0
  387. /package/src/{panels → views}/modals/modal-theme.ts +0 -0
  388. /package/src/{panels → views}/provider-health-domains.ts +0 -0
@@ -0,0 +1,38 @@
1
+ /**
2
+ * footer-targets.ts, which footer rows open a view when clicked.
3
+ *
4
+ * The status line (the row carrying the context bar and cost) opens the
5
+ * Usage modal on a click. The footer builder tags those Line arrays with a symbol-keyed
6
+ * property (invisible to rendering, iteration and golden frames); the shell
7
+ * turns the tags into screen rows each frame and the mouse route reads them.
8
+ */
9
+
10
+ import type { Line } from '@pellux/goodvibes-sdk/platform/types';
11
+
12
+ /** What a clickable footer row opens. */
13
+ export type FooterTarget = 'usage';
14
+
15
+ const FOOTER_TARGET = Symbol('footer-target');
16
+
17
+ type TaggedLine = Line & { [FOOTER_TARGET]?: FooterTarget };
18
+
19
+ /** Mark a footer row as opening `target` when clicked; returns the same line. */
20
+ export function tagFooterLine(line: Line, target: FooterTarget): Line {
21
+ (line as TaggedLine)[FOOTER_TARGET] = target;
22
+ return line;
23
+ }
24
+
25
+ /** The target a footer row opens, or undefined. */
26
+ function footerTargetOf(line: Line): FooterTarget | undefined {
27
+ return (line as TaggedLine)[FOOTER_TARGET];
28
+ }
29
+
30
+ /** Screen row → target for a footer drawn with its first row at `startRow`. */
31
+ export function footerTargetRows(footer: readonly Line[], startRow: number): ReadonlyMap<number, FooterTarget> {
32
+ const rows = new Map<number, FooterTarget>();
33
+ footer.forEach((line, index) => {
34
+ const target = footerTargetOf(line);
35
+ if (target) rows.set(startRow + index, target);
36
+ });
37
+ return rows;
38
+ }
@@ -55,10 +55,9 @@ export class GitStatusProvider {
55
55
  }
56
56
 
57
57
  /**
58
- * Start a lightweight live-repo-state poll. Mirrors DiffPanel's principle of
59
- * never trusting a cached "is this a repo" flag (see diff-panel.ts
60
- * showGitDiff/showFileDiffs/showStagedDiff, all gated on a fresh
61
- * GitService.isGitRepo() check) but amortizes the cost for the header: the
58
+ * Start a lightweight live-repo-state poll. Follows the Changes modal's principle of
59
+ * never trusting a cached "is this a repo" flag (its git diff actions are
60
+ * all gated on a fresh GitService.isGitRepo() check) but amortizes the cost for the header: the
62
61
  * cheap synchronous isGitRepo() spawn runs every tick; the heavier async
63
62
  * status()+branch() fetch (this.refresh()) only runs when that boolean
64
63
  * actually flips, e.g. an external `git init`, or `.git` removed.
@@ -1,146 +1,169 @@
1
1
  /**
2
- * header-line.ts, the top-of-screen header row: brand, version, optional
3
- * conversation title, optional git segment, and the right-hand model/provider
4
- * pair.
2
+ * header-line.ts, the one-row header.
5
3
  *
6
- * Extracted from ui-factory.ts (which sits on the 800-line source gate) when
7
- * the header gained the failover divergence marker. `UIFactory.createHeader`
8
- * remains the call name everywhere and delegates here.
4
+ * col 1 GoodVibes, bold, in the theme's brand -> brandEnd gradient
5
+ * (the theme's gradient, never the protected splash constant)
6
+ * then the version (faint) and the session title (muted)
7
+ * right the branch with a warning-colored dirty dot (ahead/behind counts
8
+ * muted), 3 columns, then the serving model ending at width-2,
9
+ * followed by the failover marker in the warning color while the
10
+ * serving backend is not the configured one
9
11
  *
10
- * The model/provider pair rendered here is the SERVING backend, resolved by
11
- * core/active-model-identity.ts, the same resolution the footer uses, so the
12
- * two surfaces can no longer disagree about who is answering. `note` carries
13
- * the divergence marker naming the user's configured selection when serving
14
- * has moved off it.
12
+ * No rule row under it; on the main screen one empty row (withHeaderGap)
13
+ * keeps the output off it. The header is the one place for session identity:
14
+ * title, branch, model. Inside an agent or process view the title gives way
15
+ * to the breadcrumb (main › engineer, the last name in its own color) and the
16
+ * right side names that session: the agent's model, or the process's pid,
17
+ * uptime and port. On a narrow row the marker falls back to its short
18
+ * form, then is dropped whole (a half-cut marker reads worse than none); the
19
+ * branch goes before the marker does, the title before either.
15
20
  */
16
21
 
17
22
  import { type Line, createEmptyLine } from '@pellux/goodvibes-sdk/platform/types';
18
23
  import { VERSION } from '../version.ts';
19
- import { getDisplayWidth } from '../utils/terminal-width.ts';
24
+ import { getDisplayWidth, interpolateColor, truncateDisplay } from '../utils/terminal-width.ts';
20
25
  import type { GitHeaderInfo } from './git-status.ts';
21
- import { activeUiTones } from './theme.ts';
26
+ import { activeTokens } from './theme.ts';
22
27
 
23
- /** Build the git segment string and its display width. Single source of truth for header layout. */
24
- function buildGitSegment(gitInfo: GitHeaderInfo): { text: string; width: number } {
25
- const branch = ` git:${gitInfo.branch}`;
26
- if (gitInfo.dirty) {
27
- const text = `${branch} * `;
28
- return { text, width: getDisplayWidth(text) };
29
- }
30
- if (gitInfo.ahead > 0 || gitInfo.behind > 0) {
31
- const arrows = (gitInfo.ahead > 0 ? ` +${gitInfo.ahead}` : '') + (gitInfo.behind > 0 ? ` -${gitInfo.behind}` : '');
32
- const text = `${branch}${arrows} `;
33
- return { text, width: getDisplayWidth(text) };
28
+ const BRAND = 'GoodVibes';
29
+ const BRAND_X = 1;
30
+ const GAP = 3;
31
+
32
+ interface Seg { readonly text: string; readonly fg: string; readonly bold?: boolean }
33
+
34
+ /** An agent or process view: the breadcrumb in place of the title, the session's own right side. */
35
+ export interface HeaderView {
36
+ /** main, then each level down to the one shown; the last is drawn bold in its color. */
37
+ readonly crumbs: ReadonlyArray<{ readonly text: string; readonly fg: string; readonly bold?: boolean }>;
38
+ /** Replaces the branch and model on the right (the agent's model; pid · uptime · port). */
39
+ readonly right: string;
40
+ readonly rightFg?: string;
41
+ }
42
+
43
+ function put(line: Line, x: number, endX: number, seg: Seg): number {
44
+ let cx = x;
45
+ for (const ch of seg.text) {
46
+ const w = getDisplayWidth(ch);
47
+ if (w <= 0) continue;
48
+ if (cx + w > endX) break;
49
+ line[cx] = { char: ch, fg: seg.fg, bg: '', bold: seg.bold ?? false, dim: false, underline: false, italic: false, strikethrough: false };
50
+ if (w === 2 && cx + 1 < line.length) line[cx + 1] = { ...line[cx]!, char: '' };
51
+ cx += w;
34
52
  }
35
- const text = `${branch} `;
36
- return { text, width: getDisplayWidth(text) };
53
+ return cx;
37
54
  }
38
55
 
39
- /**
40
- * Pick the widest divergence marker that still fits beside the brand and the
41
- * model/provider pair.
42
- *
43
- * Graded, never truncated mid-word: the full marker (which names the
44
- * configured selection) is preferred; a bare "divergent" keyword is the
45
- * fallback on a narrow terminal; on a very narrow terminal the marker is
46
- * dropped entirely. Dropping it is safe, what remains is still the SERVING
47
- * backend, so the header never claims the configured backend is answering.
48
- * The footer carries the full marker with far more room (see ui-factory's
49
- * prioritized context segments).
50
- */
51
- function fitHeaderNote(note: string, usedWidth: number, width: number): string {
52
- if (!note) return '';
53
- const full = `· ${note} `;
54
- if (usedWidth + getDisplayWidth(full) <= width) return full;
55
- const short = '· divergent ';
56
- return usedWidth + getDisplayWidth(short) <= width ? short : '';
56
+ function branchSegments(gitInfo: GitHeaderInfo): Seg[] {
57
+ const t = activeTokens();
58
+ const segs: Seg[] = [{ text: gitInfo.branch, fg: t.textMuted }];
59
+ if (gitInfo.ahead > 0) segs.push({ text: ` +${gitInfo.ahead}`, fg: t.textMuted });
60
+ if (gitInfo.behind > 0) segs.push({ text: ` -${gitInfo.behind}`, fg: t.textMuted });
61
+ if (gitInfo.dirty) segs.push({ text: ' ●', fg: t.warning });
62
+ return segs;
57
63
  }
58
64
 
59
65
  /**
60
- * Render the header row plus its underline.
66
+ * Render the header row.
61
67
  *
62
- * @param width - Terminal columns.
63
- * @param model - Serving model id (already resolved; see file doc).
64
- * @param provider - Serving provider id.
65
- * @param title - Optional conversation title, truncated to fit.
66
- * @param gitInfo - Optional git branch/dirty/ahead-behind segment.
67
- * @param version - Defaults to the live build VERSION; tests pass a pinned
68
- * fixture so golden snapshots survive release bumps.
69
- * @param note - Divergence marker naming the configured selection when the
70
- * serving backend is not the one the user chose; '' otherwise.
68
+ * @param width - Terminal columns.
69
+ * @param model - Serving model id (core/active-model-identity.ts).
70
+ * @param title - Optional session title, truncated to fit.
71
+ * @param gitInfo - Optional branch/dirty/ahead-behind.
72
+ * @param version - Defaults to the live build VERSION; tests pin a fixture.
73
+ * @param modelNote - The failover marker (core/active-model-identity.ts), set
74
+ * only while serving differs from the configured model.
71
75
  */
72
76
  export function renderHeaderLine(
73
77
  width: number,
74
78
  model: string,
75
- provider: string,
76
79
  title?: string,
77
80
  gitInfo?: GitHeaderInfo,
78
81
  version: string = VERSION,
79
- note: string = '',
82
+ modelNote?: string,
83
+ view?: HeaderView,
80
84
  ): Line[] {
81
- const lines: Line[] = [];
82
- // Header/footer/thinking paint on the transparent terminal background, so
83
- // they read chrome.* (light-terminal-aware), NOT fg.*/state.*, which stay
84
- // tuned for the opaque dark modal/panel boxes. Read live per render so a
85
- // mode flip re-resolves without any module reload (see theme.ts).
86
- const t = activeUiTones();
87
- const CYAN = t.accent.brand;
88
- const GREY = t.chrome.faint;
89
- const TITLE_COLOR = t.chrome.label;
90
- const brand = ` GoodVibes `;
91
- const ver = `v${version} `;
92
- const stats = ` ${model} `;
93
- const prov = `(${provider}) `;
94
- const gitWidth = gitInfo ? buildGitSegment(gitInfo).width : 0;
95
- // Resolve the marker before the title so the title's fit calculation
96
- // reserves room for it too.
97
- const noteStr = fitHeaderNote(note, getDisplayWidth(brand + ver + stats + prov) + gitWidth, width);
85
+ const t = activeTokens();
98
86
  const line = createEmptyLine(width);
99
- let curX = 0;
100
- for (const char of brand) { line[curX++] = { char, fg: CYAN, bg: '', bold: true, dim: false, underline: false, italic: false, strikethrough: false }; }
101
- for (const char of ver) { line[curX++] = { char, fg: GREY, bg: '', bold: false, dim: true, underline: false, italic: false, strikethrough: false }; }
102
- // Optional conversation title, shown after brand/ver, truncated to fit
87
+ for (const cell of line) cell.bg = '';
88
+ const end = width - 1; // exclusive: the model ends at width-2
89
+ if (view) return [renderViewHeader(line, width, version, view)];
90
+
91
+ // Right side first, so the title knows how much room it has.
92
+ const leftMin = BRAND_X + BRAND.length + 1 + getDisplayWidth(version);
93
+ const fits = (w: number): boolean => leftMin + GAP + w <= end;
94
+ let note = '';
95
+ if (modelNote) {
96
+ const full = ` · ${modelNote}`;
97
+ const short = ' · divergent';
98
+ const baseW = getDisplayWidth(model);
99
+ if (fits(baseW + getDisplayWidth(full))) note = full;
100
+ else if (fits(baseW + getDisplayWidth(short))) note = short;
101
+ }
102
+ const modelW = getDisplayWidth(model) + getDisplayWidth(note);
103
+ const branch = gitInfo ? branchSegments(gitInfo) : [];
104
+ const branchW = branch.reduce((s, seg) => s + getDisplayWidth(seg.text), 0);
105
+ const showBranch = branchW > 0 && fits(branchW + GAP + modelW);
106
+ const rightW = modelW + (showBranch ? branchW + GAP : 0);
107
+ const rightX = Math.max(leftMin + GAP, end - rightW);
108
+
109
+ // Brand, version, title.
110
+ let x = BRAND_X;
111
+ [...BRAND].forEach((ch, i) => {
112
+ x = put(line, x, end, { text: ch, fg: interpolateColor(t.brand, t.brandEnd, i / (BRAND.length - 1)), bold: true });
113
+ });
114
+ x = put(line, x + 1, end, { text: version, fg: t.textFaint });
103
115
  if (title) {
104
- const titleStr = `│ ${title} `;
105
- // Reserve space for git info (if present) + model/provider + marker on the right
106
- const rightReserved = getDisplayWidth(stats + prov + noteStr) + gitWidth;
107
- const maxTitleW = width - curX - rightReserved - 1;
108
- let displayTitle: string;
109
- if (getDisplayWidth(titleStr) <= maxTitleW) {
110
- displayTitle = titleStr;
111
- } else {
112
- let truncated = '';
113
- let w = 0;
114
- for (const ch of titleStr) {
115
- const cw = getDisplayWidth(ch);
116
- if (w + cw > maxTitleW - 3) { truncated += '...'; break; }
117
- truncated += ch;
118
- w += cw;
119
- }
120
- displayTitle = truncated;
121
- }
122
- for (const char of displayTitle) { if (curX < width) line[curX++] = { char, fg: TITLE_COLOR, bg: '', bold: false, dim: true, underline: false, italic: false, strikethrough: false }; }
116
+ const room = rightX - GAP - (x + 2);
117
+ if (room >= 4) put(line, x + 2, rightX - GAP, { text: truncateDisplay(title, room), fg: t.textMuted });
123
118
  }
124
- // Build git info segment
125
- let gitStr = '';
126
- let gitFg: string = t.chrome.faint;
127
- if (gitInfo) {
128
- gitStr = buildGitSegment(gitInfo).text;
129
- if (gitInfo.dirty || gitInfo.ahead > 0 || gitInfo.behind > 0) {
130
- gitFg = t.chrome.warn; // yellow when dirty or out-of-sync
131
- }
119
+
120
+ let rx = rightX;
121
+ if (showBranch) {
122
+ for (const seg of branch) rx = put(line, rx, end, seg);
123
+ rx += GAP;
132
124
  }
133
- const rightSideW = getDisplayWidth(stats + prov + noteStr) + getDisplayWidth(gitStr);
134
- let rightX = width - rightSideW;
135
- for (const char of gitStr) { if (rightX >= 0 && rightX < width) line[rightX++] = { char, fg: gitFg, bg: '', bold: false, dim: !gitInfo?.dirty && !(gitInfo?.ahead || gitInfo?.behind), underline: false, italic: false, strikethrough: false }; }
136
- for (const char of stats) { if (rightX < width) line[rightX++] = { char, fg: CYAN, bg: '', bold: true, dim: false, underline: false, italic: false, strikethrough: false }; }
137
- for (const char of prov) { if (rightX < width) line[rightX++] = { char, fg: GREY, bg: '', bold: false, dim: true, underline: false, italic: false, strikethrough: false }; }
138
- // The marker reads as a warning, not as chrome: serving has left the
139
- // selection the user made and that is worth noticing.
140
- for (const char of noteStr) { if (rightX >= 0 && rightX < width) line[rightX++] = { char, fg: t.chrome.warn, bg: '', bold: false, dim: false, underline: false, italic: false, strikethrough: false }; }
141
- lines.push(line);
142
- const underline = createEmptyLine(width);
143
- for (let x = 0; x < width; x++) underline[x] = { char: '━', fg: t.chrome.faint, bg: '', bold: false, dim: false, underline: false, italic: false, strikethrough: false };
144
- lines.push(underline);
145
- return lines;
125
+ rx = put(line, rx, end, { text: model, fg: t.text });
126
+ if (note) put(line, rx, end, { text: note, fg: t.warning });
127
+ return [line];
146
128
  }
129
+
130
+ /** The header inside an agent or process view. */
131
+ function renderViewHeader(line: Line, width: number, version: string, view: HeaderView): Line {
132
+ const t = activeTokens();
133
+ const end = width - 1;
134
+ let x = BRAND_X;
135
+ [...BRAND].forEach((ch, i) => {
136
+ x = put(line, x, end, { text: ch, fg: interpolateColor(t.brand, t.brandEnd, i / (BRAND.length - 1)), bold: true });
137
+ });
138
+ x = put(line, x + 1, end, { text: version, fg: t.textFaint });
139
+ const crumbsW = view.crumbs.reduce((s, c, i) => s + (i > 0 ? 3 : 0) + getDisplayWidth(c.text), 0);
140
+ // The right side yields before the breadcrumb does: where you are matters more than the model name.
141
+ const roomForRight = end - (x + 2 + crumbsW + GAP);
142
+ const right = roomForRight >= 4 ? truncateDisplay(view.right, roomForRight) : '';
143
+ const rightX = end - getDisplayWidth(right);
144
+ const crumbEnd = right ? rightX - GAP : end;
145
+ x += 2;
146
+ view.crumbs.forEach((crumb, i) => {
147
+ if (i > 0) x = put(line, x + 1, crumbEnd, { text: '›', fg: t.textFaint }) + 1;
148
+ const last = i === view.crumbs.length - 1;
149
+ const room = crumbEnd - x;
150
+ x = put(line, x, crumbEnd, { text: last ? truncateDisplay(crumb.text, Math.max(0, room)) : crumb.text, fg: crumb.fg, bold: crumb.bold });
151
+ });
152
+ if (right) put(line, rightX, end, { text: right, fg: view.rightFg ?? t.text });
153
+ return line;
154
+ }
155
+
156
+ /**
157
+ * The main screen's header block with its gap: the header row (and the
158
+ * session chips row, when it shows), then one empty row, so output never
159
+ * touches the header. An agent or process view's body starts with its own
160
+ * empty row, so a view does not take this one.
161
+ */
162
+ export function withHeaderGap(header: readonly Line[], width: number): Line[] {
163
+ const gap = createEmptyLine(width);
164
+ for (const cell of gap) cell.bg = '';
165
+ return [...header, gap];
166
+ }
167
+
168
+ /** Rows the gap under the main screen's header adds. */
169
+ export const HEADER_GAP_ROWS = 1;