@pellux/goodvibes-tui 2.0.20 → 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 +70 -0
  2. package/README.md +16 -16
  3. package/docs/README.md +4 -4
  4. package/docs/foundation-artifacts/operator-contract.json +59 -4
  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,146 @@
1
+ /**
2
+ * composer.ts, the prompt box, built exactly like a user message. It holds
3
+ * input and nothing else.
4
+ *
5
+ * col 2 ┃ in the mode color, on every row of the block; ╻ and ╹ on
6
+ * the cap rows, so the bar spans exactly the visible fill
7
+ * cols 3..w-3 the element fill
8
+ * rows ▄ cap, padding, text (one or more), padding, ▀ cap
9
+ * text from column 5, wrapped to width-9 (see prompt-content-width.ts)
10
+ *
11
+ * The cap rows are half rows: a ▄ row is empty on top and fill below, a ▀ row
12
+ * fill on top and empty below (the modal caps' technique, surface-kit.ts).
13
+ * They keep half a row of empty space between the input area and whatever
14
+ * sits above it (the throbber, or the transcript at rest) and below it (the
15
+ * status line), so text never sits directly on the input area.
16
+ *
17
+ * Multi-line input grows the text area; nothing else moves. The approval mode,
18
+ * the auto-approve warning and the safety chips are on the status line; the
19
+ * model and the failover marker are in the header.
20
+ */
21
+
22
+ import { type Line, createEmptyLine } from '@pellux/goodvibes-sdk/platform/types';
23
+ import { getDisplayWidth, truncateDisplay } from '../utils/terminal-width.ts';
24
+ import { GLYPHS } from './ui-primitives.ts';
25
+ import { activeTokens } from './theme.ts';
26
+
27
+ /** Column of the mode bar. */
28
+ const COMPOSER_BAR_X = 2;
29
+ /** First fill column. */
30
+ const COMPOSER_FILL_X = 3;
31
+ /** First text column. */
32
+ const COMPOSER_TEXT_X = 5;
33
+ /** Rows the composer adds around its text rows (a cap row and a padding row above and below). */
34
+ export const COMPOSER_FIXED_ROWS = 4;
35
+
36
+ const COMPOSER_PLACEHOLDER = 'Ask anything, or type / for commands and @ for files';
37
+
38
+ export interface ComposerOptions {
39
+ readonly width: number;
40
+ /** Visible prompt rows joined with '\n'. */
41
+ readonly promptText: string;
42
+ /** Cursor offset into promptText (rows joined with one separator), when on screen. */
43
+ readonly cursorPos?: number;
44
+ readonly focused: boolean;
45
+ /** Faint hint after the cursor on the last row (a command's arguments). */
46
+ readonly argsHint?: string;
47
+ /** The bar's color: the session mode (brand, plan, auto-approving modes, shell), or the agent or process shown. */
48
+ readonly modeColor: string;
49
+ /** The empty composer's placeholder (an agent view: "Message engineer. This steers it; main keeps running."). */
50
+ readonly placeholder?: string;
51
+ /**
52
+ * The composer takes no input here; the reason is drawn in its place (a
53
+ * process whose stdin is not reachable). No cursor is drawn.
54
+ */
55
+ readonly disabledReason?: string;
56
+ }
57
+
58
+ interface Style { readonly fg: string; readonly bg: string; readonly bold?: boolean }
59
+
60
+ function put(line: Line, x: number, endX: number, text: string, style: Style): number {
61
+ let cx = x;
62
+ for (const ch of text) {
63
+ const w = getDisplayWidth(ch);
64
+ if (w <= 0) continue;
65
+ if (cx + w > endX) break;
66
+ line[cx] = { char: ch, fg: style.fg, bg: style.bg, bold: style.bold ?? false, dim: false, underline: false, italic: false, strikethrough: false };
67
+ if (w === 2 && cx + 1 < line.length) line[cx + 1] = { ...line[cx]!, char: '' };
68
+ cx += w;
69
+ }
70
+ return cx;
71
+ }
72
+
73
+ function blockRow(width: number, bar: string, bg: string): Line {
74
+ const line = createEmptyLine(width);
75
+ for (const cell of line) cell.bg = '';
76
+ if (width > COMPOSER_BAR_X) line[COMPOSER_BAR_X] = { char: '┃', fg: bar, bg: '', bold: false, dim: false, underline: false, italic: false, strikethrough: false };
77
+ for (let x = COMPOSER_FILL_X; x <= width - 3; x++) line[x] = { char: ' ', fg: '', bg, bold: false, dim: false, underline: false, italic: false, strikethrough: false };
78
+ return line;
79
+ }
80
+
81
+ /**
82
+ * A half row: ▄ (the fill's lower half, above the block) or ▀ (its upper
83
+ * half, below the block) across the fill columns, in the fill color on the
84
+ * terminal background, with the bar's matching half (╻ or ╹) in column 2.
85
+ */
86
+ function capRow(width: number, bar: string, bg: string, edge: 'top' | 'bottom'): Line {
87
+ const line = createEmptyLine(width);
88
+ for (const cell of line) cell.bg = '';
89
+ const base = { bg: '', bold: false, dim: false, underline: false, italic: false, strikethrough: false };
90
+ if (width > COMPOSER_BAR_X) line[COMPOSER_BAR_X] = { ...base, char: edge === 'top' ? '╻' : '╹', fg: bar };
91
+ for (let x = COMPOSER_FILL_X; x <= width - 3; x++) line[x] = { ...base, char: edge === 'top' ? '▄' : '▀', fg: bg };
92
+ return line;
93
+ }
94
+
95
+ /** Render the composer block (at least 5 rows: cap, padding, text, padding, cap). */
96
+ export function renderComposer(options: ComposerOptions): Line[] {
97
+ const t = activeTokens();
98
+ const { width } = options;
99
+ const bg = t.backgroundElement;
100
+ const bar = options.modeColor;
101
+ const textEnd = width - 4; // exclusive: text keeps 2 columns from the fill's right edge
102
+ if (options.disabledReason !== undefined) {
103
+ const line = blockRow(width, bar, bg);
104
+ put(line, COMPOSER_TEXT_X, textEnd, truncateDisplay(options.disabledReason, Math.max(0, textEnd - COMPOSER_TEXT_X)), { fg: t.textFaint, bg });
105
+ return [capRow(width, bar, bg, 'top'), blockRow(width, bar, bg), line, blockRow(width, bar, bg), capRow(width, bar, bg, 'bottom')];
106
+ }
107
+ const rows = options.promptText.split('\n');
108
+ const lines: Line[] = [capRow(width, bar, bg, 'top'), blockRow(width, bar, bg)];
109
+ const textFg = options.focused ? t.text : t.textFaint;
110
+ const placeholder = options.placeholder ?? COMPOSER_PLACEHOLDER;
111
+
112
+ let offset = 0;
113
+ rows.forEach((text, i) => {
114
+ const line = blockRow(width, bar, bg);
115
+ const last = i === rows.length - 1;
116
+ const empty = rows.length === 1 && text === '';
117
+ if (empty) {
118
+ // The input area holds only input: its placeholder, focused or not (the status line says where the keys are).
119
+ put(line, COMPOSER_TEXT_X, textEnd, truncateDisplay(placeholder, Math.max(0, textEnd - COMPOSER_TEXT_X)), { fg: t.textFaint, bg });
120
+ } else {
121
+ put(line, COMPOSER_TEXT_X, textEnd + 1, text, { fg: textFg, bg });
122
+ }
123
+ if (options.focused) {
124
+ const posInRow = options.cursorPos !== undefined ? options.cursorPos - offset : (last ? text.length : -1);
125
+ if (posInRow >= 0 && posInRow <= text.length && (options.cursorPos !== undefined || last)) {
126
+ const cx = COMPOSER_TEXT_X + getDisplayWidth(text.slice(0, posInRow));
127
+ if (cx <= width - 4) {
128
+ const cell = line[cx]!;
129
+ const blank = empty || cell.char === ' ' || cell.char === '';
130
+ line[cx] = blank && !empty
131
+ ? { ...cell, char: GLYPHS.surface.cursor, fg: t.text, bg }
132
+ : { ...cell, char: cell.char === '' ? ' ' : cell.char, fg: bg, bg: t.text };
133
+ // A command's argument hint trails the cursor while it sits at the end.
134
+ if (last && options.argsHint && posInRow >= text.length && !empty) {
135
+ put(line, cx + 2, textEnd, truncateDisplay(options.argsHint, Math.max(0, textEnd - cx - 2)), { fg: t.textFaint, bg });
136
+ }
137
+ }
138
+ }
139
+ }
140
+ offset += text.length + 1;
141
+ lines.push(line);
142
+ });
143
+
144
+ lines.push(blockRow(width, bar, bg), capRow(width, bar, bg, 'bottom'));
145
+ return lines;
146
+ }
@@ -1,17 +1,12 @@
1
1
  import { TerminalBuffer } from './buffer.ts';
2
2
  import { DiffEngine } from './diff.ts';
3
- import { type Line, createEmptyCell, createEmptyLine, createStyledCell } from '@pellux/goodvibes-sdk/platform/types';
4
- import { getDisplayWidth } from '../utils/terminal-width.ts';
3
+ import { type Line, createEmptyLine } from '@pellux/goodvibes-sdk/platform/types';
5
4
  import type { SearchManager } from '../input/search.ts';
6
5
  import { allowTerminalWrite } from '@pellux/goodvibes-terminal-shell/terminal-output-guard';
7
6
  import { probeTermCaps, type TermColorCaps } from './term-caps.ts';
8
- import { activeTheme } from './theme.ts';
9
- import { UI_TONES } from './ui-primitives.ts';
10
-
11
- // Accent / dim colors for the panel focus border. The focused pane's left
12
- // border column is drawn in the accent tone; the unfocused pane stays dim.
13
- const PANEL_FOCUS_ACCENT = UI_TONES.state.active; // bright blue
14
- const PANEL_BORDER_DIM = '238';
7
+ import { activeTheme, activeTokens } from './theme.ts';
8
+ import type { SurfaceLayer } from './surface-kit.ts';
9
+ import { composeLayers } from './surface-compose.ts';
15
10
 
16
11
  export interface SelectionInfo {
17
12
  isCellSelected: (col: number, absoluteRow: number) => boolean;
@@ -25,29 +20,6 @@ export interface SearchInfo {
25
20
  viewportStartY: number;
26
21
  }
27
22
 
28
- export interface PanelCompositeData {
29
- /**
30
- * The single consolidated workspace tab bar spanning all open panels across
31
- * both panes. There are no per-pane tab bars, pane focus is shown by the
32
- * accent border (see `topFocused`/`bottomFocused`).
33
- */
34
- workspaceBar: Line;
35
- /** Top pane: panel content lines */
36
- topContent: Line[];
37
- /** Whether the top pane is focused (drives the accent border) */
38
- topFocused: boolean;
39
- /** Whether a bottom pane is present (splits the panel area). */
40
- hasBottomPane: boolean;
41
- /** Bottom pane content lines. Undefined = no bottom pane. */
42
- bottomContent?: Line[];
43
- /** Whether the bottom pane is focused */
44
- bottomFocused?: boolean;
45
- /** Separator between left and right panel area */
46
- separator: boolean;
47
- /** Ratio of panel height for the top pane (0–1). Only used when bottom pane is present. */
48
- verticalSplitRatio: number;
49
- }
50
-
51
23
  export interface CompositeRequest {
52
24
  width: number;
53
25
  height: number;
@@ -56,8 +28,11 @@ export interface CompositeRequest {
56
28
  footer: Line[];
57
29
  selection?: SelectionInfo;
58
30
  search?: SearchInfo;
59
- panel?: PanelCompositeData;
60
- panelWidth?: number; // width of the right panel area (0 = no panel)
31
+ /**
32
+ * Surfaces stamped over the finished screen, in order: modals (which dim
33
+ * everything underneath first), toasts. Screen coordinates.
34
+ */
35
+ layers?: readonly SurfaceLayer[];
61
36
  }
62
37
 
63
38
  /**
@@ -124,7 +99,7 @@ export class Compositor {
124
99
  }
125
100
 
126
101
  public composite(params: CompositeRequest): void {
127
- const { width, height, header, viewport, footer, selection, search, panel, panelWidth } = params;
102
+ const { width, height, header, viewport, footer, selection, search, layers } = params;
128
103
  // A size change reallocates the buffer, which drops every record of what
129
104
  // the terminal is currently showing, repaint in full rather than diff
130
105
  // against a model that no longer describes the screen.
@@ -144,10 +119,6 @@ export class Compositor {
144
119
  }
145
120
  const newBuffer = this.backBuffer;
146
121
 
147
- const hasPanel = panel !== undefined && panelWidth !== undefined && panelWidth > 0;
148
- const leftWidth = hasPanel ? Math.max(1, width - panelWidth - 1) : width;
149
- const sepX = hasPanel ? leftWidth : -1;
150
-
151
122
  // 1. Draw Header, always full width
152
123
  header.forEach((line, i) => newBuffer.blitLine(i, line));
153
124
 
@@ -159,33 +130,6 @@ export class Compositor {
159
130
  const lineCount = selection?.lineCount ?? 0;
160
131
  const offset = Math.max(0, vHeight - lineCount);
161
132
 
162
- // --- Pre-compute panel row layout when split pane is active ---
163
- // A single consolidated workspace bar heads the panel area; there are no
164
- // per-pane tab bars. When both panes are visible the layout is:
165
- // row 0: workspace tab bar
166
- // rows 1..topH: top content
167
- // row topH+1: horizontal separator (───)
168
- // rows topH+2..end: bottom content
169
- const hasBottomPane = hasPanel && panel!.hasBottomPane;
170
- let topPaneHeight = 0; // number of content rows in top pane
171
- let hSepRow = -1; // viewport row of the horizontal separator
172
- if (hasPanel && hasBottomPane) {
173
- const panelAreaRows = Math.max(0, vHeight - 1); // subtract workspace bar
174
- const contentRows = Math.max(0, panelAreaRows - 1); // subtract h-separator
175
- topPaneHeight = Math.max(1, Math.floor(contentRows * panel!.verticalSplitRatio));
176
- hSepRow = 1 + topPaneHeight; // workspace bar + top content rows
177
- }
178
-
179
- const panelFocused = hasPanel && (panel!.topFocused || panel!.bottomFocused);
180
- // Per-row left-border color: the focused pane's rows get the accent tone.
181
- const borderFgForRow = (i: number): string => {
182
- if (!hasPanel || !panel!.separator || !panelFocused) return PANEL_BORDER_DIM;
183
- if (!hasBottomPane) return panel!.topFocused ? PANEL_FOCUS_ACCENT : PANEL_BORDER_DIM;
184
- if (i === 0) return PANEL_FOCUS_ACCENT; // workspace bar, panel is focused
185
- if (i <= topPaneHeight) return panel!.topFocused ? PANEL_FOCUS_ACCENT : PANEL_BORDER_DIM;
186
- return panel!.bottomFocused ? PANEL_FOCUS_ACCENT : PANEL_BORDER_DIM;
187
- };
188
-
189
133
  // Every body row is written every frame, including rows the caller did not
190
134
  // supply a line for. A viewport array shorter than the body (a docked
191
135
  // overlay reserves a bottom inset, an overlay-row reservation overshoots
@@ -201,113 +145,30 @@ export class Compositor {
201
145
  const screenY = viewportStartY + i;
202
146
  if (screenY >= height) break;
203
147
 
204
- if (!hasPanel) {
205
- // No panel: existing fast path
206
- newBuffer.blitLine(screenY, line);
207
- } else {
208
- // Panel active: write cells individually to support split layout
209
- // Left side: viewport cells 0..leftWidth-1
210
- for (let x = 0; x < leftWidth; x++) {
211
- const cell = line[x];
212
- if (cell !== undefined) {
213
- // If this is a wide char (2-cell) at the last left-side column,
214
- // it would bleed into the separator column visually.
215
- // Replace with a space to keep the separator aligned.
216
- if (x === leftWidth - 1 && cell.char && cell.char.length > 0 && getDisplayWidth(cell.char) > 1) {
217
- newBuffer.setCell(x, screenY, { ...cell, char: ' ' });
218
- continue;
219
- }
220
- newBuffer.setCell(x, screenY, cell);
221
- }
222
- }
223
-
224
- const p = panel!;
225
-
226
- // Separator column (vertical bar between left and panel area).
227
- // Colored per-row so the focused pane shows a bright accent border.
228
- if (p.separator) {
229
- newBuffer.setCell(sepX, screenY, createStyledCell('│', { fg: borderFgForRow(i) }));
230
- }
231
-
232
- const panelStartX = sepX + 1;
233
- const clearPanelRemainder = (fromX = 0) => {
234
- for (let x = Math.max(0, fromX); x < panelWidth; x++) {
235
- newBuffer.setCell(panelStartX + x, screenY, createEmptyCell());
236
- }
237
- };
238
- const drawPanelLine = (panelLine: Line | undefined) => {
239
- if (panelLine === undefined) {
240
- clearPanelRemainder();
241
- return;
242
- }
243
- const limit = Math.min(panelLine.length, panelWidth);
244
- for (let x = 0; x < limit; x++) {
245
- const cell = panelLine[x];
246
- if (cell !== undefined) {
247
- newBuffer.setCell(panelStartX + x, screenY, cell);
248
- }
249
- }
250
- clearPanelRemainder(limit);
251
- };
252
-
253
- if (!hasBottomPane) {
254
- // --- Single pane mode ---
255
- // viewport row 0 → workspace bar, viewport rows 1+ → panel content
256
- const panelLine = i === 0 ? p.workspaceBar : p.topContent[i - 1];
257
- drawPanelLine(panelLine);
258
- } else {
259
- // --- Two pane mode (single consolidated workspace bar) ---
260
- // Row layout (by viewport row i):
261
- // i = 0: workspace tab bar
262
- // 1 <= i <= topPaneHeight: top content[i-1]
263
- // i = hSepRow: horizontal separator
264
- // i >= hSepRow+1: bottom content[i - (hSepRow+1)]
265
- let panelLine: Line | undefined;
266
-
267
- if (i === 0) {
268
- panelLine = p.workspaceBar;
269
- } else if (i <= topPaneHeight) {
270
- panelLine = p.topContent[i - 1];
271
- } else if (i === hSepRow) {
272
- // Horizontal separator between the two panes. Accent when the bottom
273
- // pane has focus so the divider reinforces the focus border.
274
- const focusFg = p.bottomFocused ? PANEL_FOCUS_ACCENT : PANEL_BORDER_DIM;
275
- for (let x = 0; x < panelWidth; x++) {
276
- newBuffer.setCell(panelStartX + x, screenY, createStyledCell('─', { fg: focusFg }));
277
- }
278
- // T-junction (├) joins the vertical left-border with the pane divider.
279
- if (p.separator) {
280
- newBuffer.setCell(sepX, screenY, createStyledCell('├', { fg: focusFg }));
281
- }
282
- } else {
283
- panelLine = p.bottomContent?.[i - (hSepRow + 1)];
284
- }
285
-
286
- if (i !== hSepRow) {
287
- drawPanelLine(panelLine);
288
- }
289
- }
290
- }
148
+ newBuffer.blitLine(screenY, line);
291
149
 
292
- // Apply Selection Highlighting Overlay (left side only)
150
+ // Apply Selection Highlighting Overlay
293
151
  // Only highlight rows that actually contain history (past the bottom-anchor offset)
294
152
  if (selection && i >= offset) {
295
153
  const absoluteRow = selection.scrollTop + (i - offset);
296
- for (let x = 0; x < leftWidth; x++) {
154
+ for (let x = 0; x < width; x++) {
297
155
  if (selection.isCellSelected(x, absoluteRow)) {
298
- newBuffer.setCell(x, screenY, { bg: '4', fg: '0', bold: false, dim: false });
156
+ // Mouse selection: the theme's selection fill with body text (the
157
+ // inverse selectedListItemText is unreadable on this fill).
158
+ const sel = activeTokens();
159
+ newBuffer.setCell(x, screenY, { bg: sel.backgroundSelected, fg: sel.text, bold: false, dim: false });
299
160
  }
300
161
  }
301
162
  }
302
163
 
303
- // Apply Search Match Highlighting Overlay (left side only)
164
+ // Apply Search Match Highlighting Overlay
304
165
  if (search && search.manager.active && search.manager.query.length > 0 && i >= offset) {
305
166
  const T = activeTheme();
306
167
  const absoluteRow = search.scrollTop + (i - offset);
307
168
  const lineMatches = search.manager.getMatchesOnLine(absoluteRow);
308
169
  for (const match of lineMatches) {
309
170
  const isCurrent = search.manager.isCurrentMatch(absoluteRow, match.col);
310
- for (let x = match.col; x < match.col + match.length && x < leftWidth; x++) {
171
+ for (let x = match.col; x < match.col + match.length && x < width; x++) {
311
172
  if (isCurrent) {
312
173
  newBuffer.setCell(x, screenY, { bg: T.searchCurrentBg, fg: T.searchCurrentFg, bold: true, dim: false });
313
174
  } else {
@@ -318,7 +179,7 @@ export class Compositor {
318
179
  }
319
180
  }
320
181
  // (rows past the supplied viewport lines are covered by the loop above,
321
- // separator column included, so they can no longer keep a stale frame.)
182
+ // so they can no longer keep a stale frame.)
322
183
 
323
184
  // 3. Draw Footer (Pinned to Bottom), always full width
324
185
  const footerStart = height - footer.length;
@@ -328,7 +189,12 @@ export class Compositor {
328
189
  newBuffer.blitLine(screenY, line);
329
190
  });
330
191
 
331
- // 4. Diff and Render
192
+ // 4. Modal passes: dim the composed screen, then stamp each surface over
193
+ // it (cells outside a surface keep their dimmed content). Runs after the
194
+ // selection and search passes so those dim along with everything else.
195
+ if (layers && layers.length > 0) composeLayers(newBuffer, layers);
196
+
197
+ // 5. Diff and Render
332
198
  // Diff against front-buffer (last-rendered), then swap front/back, no clone() needed.
333
199
  // On a full repaint the SGR run-state is reset (the erase below leaves the
334
200
  // terminal's attributes unknown) and the diff runs against no previous