@pellux/goodvibes-tui 2.0.21 → 2.1.1

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 (391) hide show
  1. package/CHANGELOG.md +65 -1
  2. package/README.md +30 -29
  3. package/docs/README.md +8 -5
  4. package/docs/foundation-artifacts/operator-contract.json +1 -1
  5. package/package.json +28 -19
  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-reference.ts +2 -2
  52. package/src/input/command-registry.ts +77 -35
  53. package/src/input/commands/acp-runtime.ts +19 -9
  54. package/src/input/commands/channel-runtime.ts +2 -2
  55. package/src/input/commands/ci-runtime.ts +1 -1
  56. package/src/input/commands/codebase-runtime.ts +1 -1
  57. package/src/input/commands/config.ts +7 -1
  58. package/src/input/commands/context-window.ts +25 -19
  59. package/src/input/commands/control-room-runtime.ts +18 -18
  60. package/src/input/commands/conversation-runtime.ts +0 -1
  61. package/src/input/commands/cost-runtime.ts +29 -20
  62. package/src/input/commands/diff-runtime.ts +35 -181
  63. package/src/input/commands/eval.ts +1 -1
  64. package/src/input/commands/experience-runtime.ts +3 -7
  65. package/src/input/commands/fresh-conversation.ts +86 -0
  66. package/src/input/commands/git-runtime.ts +5 -22
  67. package/src/input/commands/graph-runtime.ts +1 -1
  68. package/src/input/commands/guidance-runtime.ts +1 -1
  69. package/src/input/commands/health-runtime.ts +8 -9
  70. package/src/input/commands/hooks-runtime.ts +2 -2
  71. package/src/input/commands/hosted-runtime.ts +3 -3
  72. package/src/input/commands/incident-runtime.ts +3 -3
  73. package/src/input/commands/intelligence-runtime.ts +6 -9
  74. package/src/input/commands/knowledge.ts +2 -2
  75. package/src/input/commands/legacy-panel-command.ts +57 -0
  76. package/src/input/commands/local-auth-runtime.ts +8 -9
  77. package/src/input/commands/local-provider-runtime.ts +7 -4
  78. package/src/input/commands/local-runtime.ts +12 -15
  79. package/src/input/commands/local-setup-review.ts +1 -1
  80. package/src/input/commands/local-setup.ts +13 -19
  81. package/src/input/commands/memory.ts +1 -1
  82. package/src/input/commands/notify-runtime.ts +9 -0
  83. package/src/input/commands/operator-runtime.ts +10 -12
  84. package/src/input/commands/permissions-provenance.ts +3 -3
  85. package/src/input/commands/planning-runtime.ts +7 -7
  86. package/src/input/commands/platform-sandbox-runtime.ts +2 -2
  87. package/src/input/commands/platform-services-runtime.ts +1 -1
  88. package/src/input/commands/policy-dispatch.ts +2 -2
  89. package/src/input/commands/policy.ts +3 -3
  90. package/src/input/commands/provider.ts +1 -1
  91. package/src/input/commands/remote-runtime.ts +1 -1
  92. package/src/input/commands/review-runtime.ts +39 -94
  93. package/src/input/commands/rewind-runtime.ts +81 -108
  94. package/src/input/commands/runtime-services.ts +15 -17
  95. package/src/input/commands/services-runtime.ts +2 -2
  96. package/src/input/commands/session-content.ts +21 -5
  97. package/src/input/commands/session-workflow.ts +13 -38
  98. package/src/input/commands/settings-sync-runtime.ts +2 -2
  99. package/src/input/commands/shell-core.ts +19 -4
  100. package/src/input/commands/skills-runtime.ts +2 -2
  101. package/src/input/commands/status-runtime.ts +13 -0
  102. package/src/input/commands/subscription-runtime.ts +1 -1
  103. package/src/input/commands/tasks-runtime.ts +2 -8
  104. package/src/input/commands/work-plan-runtime.ts +4 -4
  105. package/src/input/commands/workstream-attempts.ts +24 -47
  106. package/src/input/commands/workstream-runtime.ts +2 -2
  107. package/src/input/commands/worktree-runtime.ts +3 -3
  108. package/src/input/commands.ts +3 -1
  109. package/src/input/composer-editor.ts +44 -0
  110. package/src/input/concealed-input.ts +1 -1
  111. package/src/input/config-modal-types.ts +20 -8
  112. package/src/input/config-modal.ts +106 -105
  113. package/src/input/confirm-dialog-types.ts +28 -0
  114. package/src/input/confirm-dialog.ts +70 -0
  115. package/src/input/delete-key-policy.ts +3 -3
  116. package/src/input/feed-context-factory.ts +15 -16
  117. package/src/input/handler-command-route.ts +7 -42
  118. package/src/input/handler-content-actions.ts +16 -15
  119. package/src/input/handler-feed-routes.ts +26 -211
  120. package/src/input/handler-feed.ts +68 -95
  121. package/src/input/handler-interactions.ts +5 -3
  122. package/src/input/handler-modal-routes.ts +215 -216
  123. package/src/input/handler-modal-stack.ts +81 -11
  124. package/src/input/handler-modal-token-routes.ts +4 -0
  125. package/src/input/handler-onboarding.ts +0 -1
  126. package/src/input/handler-picker-routes.ts +65 -107
  127. package/src/input/handler-session-view-route.ts +149 -0
  128. package/src/input/handler-shortcuts.ts +16 -154
  129. package/src/input/handler-types.ts +4 -2
  130. package/src/input/handler-ui-state.ts +45 -15
  131. package/src/input/handler-work-tree-route.ts +123 -0
  132. package/src/input/handler.ts +25 -70
  133. package/src/input/keybindings.ts +16 -71
  134. package/src/input/masked-entry-modal.ts +117 -0
  135. package/src/input/mcp-workspace.ts +48 -17
  136. package/src/input/model-picker-filter.ts +8 -0
  137. package/src/input/model-picker-open.ts +99 -0
  138. package/src/input/model-picker-types.ts +1 -1
  139. package/src/input/model-picker.ts +92 -9
  140. package/src/input/notifications-modal.ts +123 -0
  141. package/src/input/overlay-filter.ts +26 -0
  142. package/src/input/profile-picker-modal.ts +37 -8
  143. package/src/input/search.ts +3 -3
  144. package/src/input/selection-modal-queue.ts +2 -0
  145. package/src/input/selection-modal.ts +28 -16
  146. package/src/input/session-picker-modal.ts +33 -8
  147. package/src/input/settings-modal-activation.ts +7 -2
  148. package/src/input/settings-modal-data.ts +11 -6
  149. package/src/input/settings-modal-types.ts +1 -1
  150. package/src/input/settings-modal.ts +20 -20
  151. package/src/input/submission-intent.ts +1 -1
  152. package/src/input/submission-router.ts +4 -9
  153. package/src/input/surface-modal-host.ts +110 -0
  154. package/src/input/theme-settings-actions.ts +82 -0
  155. package/src/input/usage-modal.ts +183 -0
  156. package/src/input/views.ts +97 -0
  157. package/src/main.ts +175 -165
  158. package/src/permissions/hunk-selection.ts +1 -1
  159. package/src/permissions/prompt-card.ts +511 -0
  160. package/src/permissions/prompt.ts +59 -622
  161. package/src/renderer/agents-modal.ts +310 -0
  162. package/src/renderer/autocomplete-overlay.ts +42 -220
  163. package/src/renderer/back-to-bottom.ts +80 -0
  164. package/src/renderer/block-actions-overlay.ts +30 -48
  165. package/src/renderer/bookmark-modal.ts +48 -79
  166. package/src/renderer/changes-modal.ts +325 -0
  167. package/src/renderer/code-block.ts +184 -106
  168. package/src/renderer/command-palette.ts +93 -0
  169. package/src/renderer/composer-fragments.ts +4 -4
  170. package/src/renderer/composer.ts +146 -0
  171. package/src/renderer/compositor.ts +26 -160
  172. package/src/renderer/config-modal.ts +185 -81
  173. package/src/renderer/confirm-dialog.ts +50 -0
  174. package/src/renderer/context-inspector.ts +116 -154
  175. package/src/renderer/context-status-hint.ts +4 -4
  176. package/src/renderer/conversation-layout.ts +20 -0
  177. package/src/renderer/conversation-overlays.ts +94 -84
  178. package/src/renderer/conversation-surface.ts +19 -38
  179. package/src/renderer/diff-tint.ts +98 -0
  180. package/src/renderer/file-picker-overlay.ts +63 -90
  181. package/src/renderer/footer-targets.ts +38 -0
  182. package/src/renderer/git-status.ts +3 -4
  183. package/src/renderer/header-line.ts +140 -117
  184. package/src/renderer/help-overlay.ts +133 -219
  185. package/src/renderer/history-search-overlay.ts +15 -12
  186. package/src/renderer/lane-graph/bead.ts +800 -0
  187. package/src/renderer/lane-graph/glyphs.ts +134 -0
  188. package/src/renderer/lane-graph/layout.ts +248 -0
  189. package/src/renderer/lane-graph/paint.ts +537 -0
  190. package/src/renderer/lane-graph/semantic-memo.ts +77 -0
  191. package/src/renderer/layout-engine.ts +2 -57
  192. package/src/renderer/layout.ts +7 -13
  193. package/src/renderer/markdown-inline.ts +2 -1
  194. package/src/renderer/markdown-table.ts +17 -12
  195. package/src/renderer/markdown.ts +92 -34
  196. package/src/renderer/masked-entry-modal.ts +78 -0
  197. package/src/renderer/mcp-workspace.ts +166 -203
  198. package/src/renderer/model-workspace.ts +381 -436
  199. package/src/renderer/notifications-modal.ts +116 -0
  200. package/src/renderer/onboarding/onboarding-wizard.ts +178 -588
  201. package/src/renderer/process-view.ts +192 -0
  202. package/src/renderer/profile-picker-modal.ts +58 -110
  203. package/src/renderer/search-overlay.ts +6 -3
  204. package/src/renderer/selection-modal-overlay.ts +87 -327
  205. package/src/renderer/semantic-diff.ts +2 -2
  206. package/src/renderer/session-chips.ts +102 -0
  207. package/src/renderer/session-picker-modal.ts +177 -297
  208. package/src/renderer/settings-modal-connections.ts +14 -23
  209. package/src/renderer/settings-modal-context.ts +367 -0
  210. package/src/renderer/settings-modal-helpers.ts +6 -11
  211. package/src/renderer/settings-modal.ts +322 -726
  212. package/src/renderer/shell-surface.ts +277 -175
  213. package/src/renderer/shortcuts-overlay.ts +217 -0
  214. package/src/renderer/status-line.ts +475 -0
  215. package/src/renderer/surface-compose.ts +174 -0
  216. package/src/renderer/surface-kit-extra.ts +303 -0
  217. package/src/renderer/surface-kit-list.ts +366 -0
  218. package/src/renderer/surface-kit-parts.ts +360 -0
  219. package/src/renderer/surface-kit.ts +568 -0
  220. package/src/renderer/syntax-highlighter.ts +280 -197
  221. package/src/renderer/syntax-theme.ts +51 -0
  222. package/src/renderer/system-message.ts +0 -1
  223. package/src/renderer/term-caps.ts +42 -1
  224. package/src/renderer/terminal-bg-probe.ts +116 -19
  225. package/src/renderer/terminal-palette-probe.ts +322 -0
  226. package/src/renderer/terminal-palette-reader.ts +183 -0
  227. package/src/renderer/terminal-palette.ts +58 -0
  228. package/src/renderer/theme-mode-config.ts +16 -1
  229. package/src/renderer/theme.ts +234 -207
  230. package/src/renderer/thinking.ts +0 -1
  231. package/src/renderer/throbber.ts +188 -0
  232. package/src/renderer/toast-center.ts +119 -0
  233. package/src/renderer/ui-factory.ts +39 -602
  234. package/src/renderer/ui-primitives.ts +8 -25
  235. package/src/renderer/usage-modal.ts +353 -0
  236. package/src/renderer/user-message.ts +58 -0
  237. package/src/renderer/voice-capture-chip.ts +53 -0
  238. package/src/runtime/bootstrap-command-context.ts +0 -5
  239. package/src/runtime/bootstrap-command-parts.ts +38 -89
  240. package/src/runtime/bootstrap-core.ts +13 -15
  241. package/src/runtime/bootstrap-hook-bridge.ts +12 -21
  242. package/src/runtime/bootstrap-notifier-sync.ts +17 -1
  243. package/src/runtime/bootstrap-shell.ts +16 -50
  244. package/src/runtime/bootstrap.ts +14 -14
  245. package/src/runtime/client/fleet-union.ts +5 -5
  246. package/src/runtime/client/hosted-exit.ts +2 -2
  247. package/src/runtime/client/hosted-sessions.ts +1 -1
  248. package/src/runtime/client/spine-adoption.ts +1 -1
  249. package/src/runtime/context-accounting-source.ts +5 -4
  250. package/src/runtime/index.ts +1 -0
  251. package/src/runtime/notification-dispatch.ts +102 -29
  252. package/src/runtime/recovery-offer-wiring.ts +4 -1
  253. package/src/runtime/recovery-prompt.ts +31 -15
  254. package/src/runtime/runtime-services-types.ts +10 -6
  255. package/src/runtime/services.ts +17 -12
  256. package/src/runtime/session-continuity-hints.ts +2 -5
  257. package/src/runtime/session-inbound-inputs.ts +1 -1
  258. package/src/runtime/session-resume-liveness-confirm.ts +14 -2
  259. package/src/runtime/startup-typeahead-gate.ts +103 -0
  260. package/src/runtime/store/domains/domain-read-matrix.ts +0 -1
  261. package/src/runtime/store/domains/index.ts +0 -8
  262. package/src/runtime/store/selectors/index.ts +0 -28
  263. package/src/runtime/store/state.ts +0 -8
  264. package/src/runtime/ui-services.ts +4 -6
  265. package/src/runtime/usage-tracker.ts +348 -0
  266. package/src/runtime/wrfc-persistence.ts +4 -4
  267. package/src/shell/blocking-input.ts +92 -2
  268. package/src/shell/recovery-input-helpers.ts +1 -1
  269. package/src/shell/session-views.ts +539 -0
  270. package/src/shell/status-report.ts +86 -0
  271. package/src/shell/transcript-scroll.ts +68 -0
  272. package/src/shell/ui-openers.ts +84 -172
  273. package/src/shell/view-openers.ts +205 -0
  274. package/src/tools/wrfc-agent-guard.ts +22 -2
  275. package/src/utils/format-duration.ts +4 -8
  276. package/src/utils/splash-lines.ts +9 -1
  277. package/src/utils/terminal-width.ts +56 -0
  278. package/src/verification/live-verifier.ts +0 -10
  279. package/src/version.ts +1 -1
  280. package/src/{panels → views}/agent-inspector-shared.ts +2 -2
  281. package/src/{panels → views}/builtin-modals.ts +22 -21
  282. package/src/views/builtin-views.ts +135 -0
  283. package/src/{panels → views}/confirm-state.ts +9 -9
  284. package/src/{panels → views}/diff-review-model.ts +10 -3
  285. package/src/{panels → views}/eval-registry.ts +1 -1
  286. package/src/{panels → views}/fleet-acts.ts +49 -85
  287. package/src/{panels/fleet-panel-format.ts → views/fleet-format.ts} +40 -40
  288. package/src/{panels → views}/fleet-gateway.ts +5 -5
  289. package/src/{panels → views}/fleet-observed-render.ts +16 -16
  290. package/src/{panels → views}/fleet-read-model.ts +9 -9
  291. package/src/{panels/fleet-tabs.ts → views/fleet-session-tabs.ts} +13 -13
  292. package/src/{panels → views}/fleet-spawn.ts +33 -49
  293. package/src/{panels → views}/fleet-steer.ts +12 -12
  294. package/src/{panels → views}/fleet-stop.ts +4 -4
  295. package/src/{panels → views}/fleet-transcript.ts +26 -27
  296. package/src/{panels/fleet-panel-worktree-detail.ts → views/fleet-worktree-detail.ts} +7 -7
  297. package/src/{panels → views}/hosted-session-feed.ts +6 -6
  298. package/src/views/modal-surface-registry.ts +41 -0
  299. package/src/{panels → views}/modals/devices-modal.ts +2 -2
  300. package/src/{panels → views}/modals/hooks-modal.ts +13 -13
  301. package/src/{panels → views}/modals/keybindings-modal.ts +19 -27
  302. package/src/{panels → views}/modals/knowledge-modal.ts +6 -5
  303. package/src/{panels → views}/modals/local-auth-modal.ts +22 -21
  304. package/src/{panels → views}/modals/marketplace-modal.ts +11 -11
  305. package/src/{panels → views}/modals/memory-modal.ts +8 -8
  306. package/src/{panels → views}/modals/modal-surface-helpers.ts +15 -5
  307. package/src/{panels → views}/modals/pairing-modal.ts +9 -9
  308. package/src/{panels → views}/modals/planning-modal.ts +15 -20
  309. package/src/{panels → views}/modals/plugins-modal.ts +6 -6
  310. package/src/{panels → views}/modals/policy-modal.ts +13 -5
  311. package/src/{panels → views}/modals/provider-health-modal.ts +4 -4
  312. package/src/{panels → views}/modals/remote-modal.ts +3 -3
  313. package/src/{panels → views}/modals/sandbox-modal.ts +4 -4
  314. package/src/{panels → views}/modals/security-modal.ts +22 -16
  315. package/src/{panels → views}/modals/services-modal.ts +3 -3
  316. package/src/{panels → views}/modals/settings-sync-modal.ts +4 -4
  317. package/src/{panels → views}/modals/skills-modal.ts +9 -9
  318. package/src/{panels → views}/modals/subscription-modal.ts +3 -3
  319. package/src/{panels → views}/modals/work-plan-modal.ts +10 -13
  320. package/src/views/notifications-feed.ts +314 -0
  321. package/src/{panels → views}/polish-core.ts +38 -36
  322. package/src/{panels → views}/polish-tables.ts +35 -26
  323. package/src/{panels → views}/polish.ts +101 -95
  324. package/src/{panels → views}/project-planning-answer-actions.ts +3 -3
  325. package/src/{panels → views}/provider-health-routes.ts +14 -14
  326. package/src/{panels → views}/search-focus.ts +8 -8
  327. package/src/{panels → views}/session-maintenance.ts +11 -11
  328. package/src/views/skills-discovery.ts +159 -0
  329. package/src/views/view-deps.ts +80 -0
  330. package/src/views/view-panel-adapter.ts +53 -0
  331. package/src/{panels → views}/workstream-graph-render.ts +1 -1
  332. package/src/core/conversation-fold.ts +0 -101
  333. package/src/core/conversation-turn-structure.ts +0 -464
  334. package/src/core/conversation-user-receipts.ts +0 -42
  335. package/src/input/commands/operator-panel-runtime.ts +0 -186
  336. package/src/input/panel-integration-actions.ts +0 -33
  337. package/src/input/panel-mouse-geometry.ts +0 -97
  338. package/src/input/panel-paste-flood-guard.ts +0 -86
  339. package/src/panels/base-panel.ts +0 -301
  340. package/src/panels/builtin/agent.ts +0 -27
  341. package/src/panels/builtin/development.ts +0 -92
  342. package/src/panels/builtin/knowledge.ts +0 -12
  343. package/src/panels/builtin/operations.ts +0 -177
  344. package/src/panels/builtin/session.ts +0 -81
  345. package/src/panels/builtin/shared.ts +0 -357
  346. package/src/panels/builtin-panels.ts +0 -28
  347. package/src/panels/cost-tracker-panel.ts +0 -746
  348. package/src/panels/diff-panel.ts +0 -797
  349. package/src/panels/diff-review-panel.ts +0 -395
  350. package/src/panels/expandable-list-panel.ts +0 -189
  351. package/src/panels/fleet-deep-link.ts +0 -31
  352. package/src/panels/fleet-panel.ts +0 -799
  353. package/src/panels/git-panel.ts +0 -800
  354. package/src/panels/hosted-session-panel.ts +0 -221
  355. package/src/panels/index.ts +0 -21
  356. package/src/panels/local-auth-panel.ts +0 -478
  357. package/src/panels/notifications-feed.ts +0 -127
  358. package/src/panels/notifications-panel.ts +0 -160
  359. package/src/panels/panel-confirm-overlay.ts +0 -72
  360. package/src/panels/panel-manager.ts +0 -791
  361. package/src/panels/plugins-panel.ts +0 -371
  362. package/src/panels/scrollable-list-panel.ts +0 -498
  363. package/src/panels/skills-panel.ts +0 -454
  364. package/src/panels/token-budget-panel.ts +0 -688
  365. package/src/panels/types.ts +0 -191
  366. package/src/panels/wrfc-panel-format.ts +0 -133
  367. package/src/renderer/diff-view.ts +0 -134
  368. package/src/renderer/fleet-tab-strip.ts +0 -56
  369. package/src/renderer/footer-tips.ts +0 -53
  370. package/src/renderer/fullscreen-primitives.ts +0 -146
  371. package/src/renderer/fullscreen-workspace.ts +0 -194
  372. package/src/renderer/hint-grammar.ts +0 -52
  373. package/src/renderer/modal-factory.ts +0 -480
  374. package/src/renderer/overlay-box.ts +0 -154
  375. package/src/renderer/overlay-viewport.ts +0 -104
  376. package/src/renderer/panel-composite.ts +0 -145
  377. package/src/renderer/panel-workspace-bar.ts +0 -54
  378. package/src/renderer/process-indicator.ts +0 -163
  379. package/src/renderer/tab-strip.ts +0 -222
  380. package/src/renderer/text-layout.ts +0 -31
  381. package/src/renderer/tool-call.ts +0 -276
  382. package/src/renderer/tool-result-expanded-lines.ts +0 -48
  383. package/src/renderer/tool-result-summary.ts +0 -219
  384. package/src/runtime/diagnostics/panels/index.ts +0 -21
  385. package/src/runtime/diagnostics/panels/ops.ts +0 -156
  386. package/src/runtime/perf/panel-contracts.ts +0 -32
  387. package/src/runtime/store/domains/panels.ts +0 -117
  388. package/src/verification/verification-ledger.ts +0 -707
  389. /package/src/{panels → views}/memory-consolidation-gateway.ts +0 -0
  390. /package/src/{panels → views}/modals/modal-theme.ts +0 -0
  391. /package/src/{panels → views}/provider-health-domains.ts +0 -0
@@ -0,0 +1,360 @@
1
+ /**
2
+ * surface-kit-parts.ts, the smaller pieces of the modal surface kit:
3
+ *
4
+ * - element insets (a lighter fill inside a modal: 2 columns of side padding,
5
+ * a padding row above and below)
6
+ * - tabs (the active tab carries the gradient)
7
+ * - button chips (arrows pick, Enter confirms; the danger chip is red)
8
+ * - meters (▰▱)
9
+ * - toasts (top right, a colored ┃ on both sides, wrapped text)
10
+ * - composer popups (the slash and @ lists: anchored to the composer, no dim,
11
+ * ┃ bar, surface fill, selected row gradient, no frame and no hint rows)
12
+ *
13
+ * Colors come only from the active theme's tokens.
14
+ */
15
+
16
+ import type { Line } from '@pellux/goodvibes-sdk/platform/types';
17
+ import { getDisplayWidth } from '../utils/terminal-width.ts';
18
+ import { activeTokens } from './theme.ts';
19
+ import {
20
+ NARROW_MODAL_BREAKPOINT,
21
+ SurfaceCanvas,
22
+ clipText,
23
+ type KitHint,
24
+ wrapLines,
25
+ type KitStyle,
26
+ type SurfaceLayer,
27
+ } from './surface-kit.ts';
28
+ import { drawList, drawRow, drawScrollingList, measureRow, type KitListResult, type KitRow } from './surface-kit-list.ts';
29
+
30
+ // ---------------------------------------------------------------------------
31
+ // Element insets
32
+ // ---------------------------------------------------------------------------
33
+
34
+ /** Side padding inside an inset. */
35
+ const INSET_PAD_X = 2;
36
+
37
+ export interface KitInset {
38
+ /** Inset fill rectangle (canvas coordinates). */
39
+ readonly x: number;
40
+ readonly y: number;
41
+ readonly w: number;
42
+ readonly h: number;
43
+ /** Text area: columns l..r, rows top..bottom (inclusive). */
44
+ readonly l: number;
45
+ readonly r: number;
46
+ readonly top: number;
47
+ readonly bottom: number;
48
+ readonly bg: string;
49
+ }
50
+
51
+ /** Fill a lighter element inset and return its text area. */
52
+ export function inset(canvas: SurfaceCanvas, x: number, y: number, w: number, h: number, bg = activeTokens().backgroundElement): KitInset {
53
+ canvas.fill(x, y, w, h, bg);
54
+ return {
55
+ x, y, w, h, bg,
56
+ l: x + INSET_PAD_X,
57
+ r: x + w - 1 - INSET_PAD_X,
58
+ top: y + 1,
59
+ bottom: y + h - 2,
60
+ };
61
+ }
62
+
63
+ /** Put text inside an inset (keeps the inset bg). Returns the column after it. */
64
+ export function insetPut(canvas: SurfaceCanvas, p: KitInset, x: number, y: number, text: string, style: KitStyle = {}): number {
65
+ if (y < p.top || y > p.bottom) return x;
66
+ return canvas.put(x, y, clipText(text, p.r - x + 1), { ...style, bg: p.bg });
67
+ }
68
+
69
+ /** Wrapped text inside an inset from row y; returns the row after the last line drawn. */
70
+ export function insetWrap(canvas: SurfaceCanvas, p: KitInset, x: number, y: number, text: string, style: KitStyle = {}): number {
71
+ let yy = y;
72
+ for (const line of wrapLines(text, p.r - x + 1)) {
73
+ if (yy > p.bottom) break;
74
+ canvas.put(x, yy, line, { ...style, bg: p.bg });
75
+ yy++;
76
+ }
77
+ return yy;
78
+ }
79
+
80
+ /** One line of inset text. */
81
+ export interface InsetLine { readonly text: string; readonly style: KitStyle }
82
+
83
+ /**
84
+ * Wrap and draw paragraphs inside an inset from row y. When they do not all
85
+ * fit, the last visible row says how many lines are hidden instead of the text
86
+ * stopping silently. Returns the row after the last one drawn.
87
+ */
88
+ export function insetLines(canvas: SurfaceCanvas, p: KitInset, x: number, y: number, paragraphs: readonly InsetLine[]): number {
89
+ const t = activeTokens();
90
+ const lines: InsetLine[] = [];
91
+ for (const para of paragraphs) {
92
+ if (para.text === '') lines.push(para);
93
+ else for (const text of wrapLines(para.text, p.r - x + 1)) lines.push({ text, style: para.style });
94
+ }
95
+ const room = Math.max(0, p.bottom - y + 1);
96
+ const overflow = lines.length > room;
97
+ const shown = overflow ? lines.slice(0, Math.max(0, room - 1)) : lines;
98
+ let yy = y;
99
+ for (const line of shown) canvas.put(x, yy++, line.text, { ...line.style, bg: p.bg });
100
+ if (overflow && room > 0) canvas.put(x, yy++, `${lines.length - shown.length} more lines`, { fg: t.textFaint, bg: p.bg });
101
+ return yy;
102
+ }
103
+
104
+ // ---------------------------------------------------------------------------
105
+ // Keycap hints in a plain row (bars and status rows outside a modal)
106
+ // ---------------------------------------------------------------------------
107
+
108
+ /** Display width of a row of keycap hints (3 columns between hints). */
109
+ export function keycapHintsWidth(hints: readonly KitHint[]): number {
110
+ return hints.reduce((sum, [key, action], i) => sum + (i > 0 ? 3 : 0) + getDisplayWidth(key) + 3 + getDisplayWidth(action), 0);
111
+ }
112
+
113
+ /**
114
+ * Paint keycap hints into a Line from column x (each key a bold chip on the
115
+ * keycap fill, its action in `actionStyle`), the same look as a modal's hint
116
+ * row. Hints that do not fit before `endX` are left out whole, never cut.
117
+ * Returns the column after the last hint drawn.
118
+ */
119
+ export function paintKeycapHints(line: Line, x: number, endX: number, hints: readonly KitHint[], actionStyle: { readonly fg: string; readonly bg?: string }): number {
120
+ const t = activeTokens();
121
+ let cx = x;
122
+ const put = (text: string, style: { fg: string; bg?: string; bold?: boolean }): void => {
123
+ for (const ch of text) {
124
+ const w = getDisplayWidth(ch);
125
+ if (cx + w > endX || cx >= line.length) return;
126
+ const prev = line[cx]!;
127
+ line[cx] = { ...prev, char: ch, fg: style.fg, bg: style.bg ?? prev.bg, bold: style.bold ?? false, dim: false, underline: false, italic: false, strikethrough: false };
128
+ if (w === 2 && cx + 1 < line.length) line[cx + 1] = { ...line[cx]!, char: '' };
129
+ cx += w;
130
+ }
131
+ };
132
+ hints.forEach(([key, action], i) => {
133
+ const need = (i > 0 ? 3 : 0) + getDisplayWidth(key) + 3 + getDisplayWidth(action);
134
+ if (cx + need > endX) return;
135
+ if (i > 0) cx += 3;
136
+ put(` ${key} `, { fg: t.text, bg: t.border, bold: true });
137
+ cx += 1;
138
+ put(action, { fg: actionStyle.fg, bg: actionStyle.bg });
139
+ });
140
+ return cx;
141
+ }
142
+
143
+ // ---------------------------------------------------------------------------
144
+ // Tabs
145
+ // ---------------------------------------------------------------------------
146
+
147
+ /** Tabs across a row; the active one carries the gradient with dark bold text. Returns the column after the last tab. */
148
+ export function tabs(canvas: SurfaceCanvas, x: number, y: number, labels: readonly string[], active: number, maxX: number): number {
149
+ const t = activeTokens();
150
+ let cx = x;
151
+ labels.forEach((label, k) => {
152
+ const w = getDisplayWidth(label);
153
+ if (cx + w > maxX) return;
154
+ if (k === active) {
155
+ canvas.grad(cx - 1, y, w + 2, t.brand, t.brandEnd);
156
+ canvas.put(cx, y, label, { fg: t.selectedListItemText, bold: true });
157
+ } else {
158
+ canvas.put(cx, y, label, { fg: t.textMuted });
159
+ }
160
+ cx += w + 4;
161
+ });
162
+ return cx;
163
+ }
164
+
165
+ // ---------------------------------------------------------------------------
166
+ // Buttons, chips and meters
167
+ // ---------------------------------------------------------------------------
168
+
169
+ export type ButtonTone = 'primary' | 'warning' | 'danger';
170
+
171
+ /** A button chip. The chosen one is filled in its tone with dark bold text; the rest are quiet chips (the keycap fill). */
172
+ export function button(canvas: SurfaceCanvas, x: number, y: number, label: string, chosen: boolean, tone: ButtonTone = 'primary'): number {
173
+ const t = activeTokens();
174
+ const toneBg = tone === 'danger' ? t.error : tone === 'warning' ? t.warning : t.brand;
175
+ const style: KitStyle = chosen
176
+ ? { fg: t.selectedListItemText, bg: toneBg, bold: true }
177
+ : { fg: t.textMuted, bg: t.border };
178
+ return canvas.put(x, y, ` ${label} `, style);
179
+ }
180
+
181
+ /** Width of a button chip. */
182
+ export function buttonWidth(label: string): number {
183
+ return getDisplayWidth(label) + 2;
184
+ }
185
+
186
+ /** A small colored chip (e.g. a risk level). */
187
+ export function chip(canvas: SurfaceCanvas, x: number, y: number, label: string, bg: string): number {
188
+ const t = activeTokens();
189
+ return canvas.put(x, y, ` ${label} `, { fg: t.selectedListItemText, bg, bold: true });
190
+ }
191
+
192
+ /** A 10-step meter (▰ filled, ▱ empty) in `color`. Returns the column after it. */
193
+ export function meter(canvas: SurfaceCanvas, x: number, y: number, fraction: number, color: string, bg?: string): number {
194
+ const t = activeTokens();
195
+ const n = Math.max(0, Math.min(10, Math.round(fraction * 10)));
196
+ let cx = x;
197
+ for (let q = 0; q < 10; q++) cx = canvas.put(cx, y, q < n ? '▰' : '▱', { fg: q < n ? color : t.border, bg });
198
+ return cx;
199
+ }
200
+
201
+ // ---------------------------------------------------------------------------
202
+ // Toasts
203
+ // ---------------------------------------------------------------------------
204
+
205
+ export type ToastTone = 'info' | 'success' | 'warning' | 'error';
206
+
207
+ export interface ToastSpec {
208
+ readonly title: string;
209
+ readonly body?: string;
210
+ readonly tone: ToastTone;
211
+ }
212
+
213
+ function toastToneColor(tone: ToastTone): string {
214
+ const t = activeTokens();
215
+ return tone === 'error' ? t.error : tone === 'warning' ? t.warning : tone === 'success' ? t.success : t.info;
216
+ }
217
+
218
+ /** Width of a toast on a screen of width screenW. */
219
+ function toastWidth(screenW: number): number {
220
+ return Math.max(20, Math.min(50, screenW - 4));
221
+ }
222
+
223
+ /** Rows toasts may use: from `top` down to (not including) `bottom`. */
224
+ export interface ToastArea {
225
+ readonly top: number;
226
+ /** First row toasts must not cover: the composer's top (the status line and composer sit below it). */
227
+ readonly bottom: number;
228
+ }
229
+
230
+ /** Cleared columns left of each toast: text beside it keeps 2 columns from its fill (Measurements). */
231
+ const TOAST_GAP_COLS = 2;
232
+ /** Cleared rows between two stacked toasts and under the lowest one. */
233
+ const TOAST_GAP_ROWS = 1;
234
+
235
+ /**
236
+ * Toasts stacked in the top right corner (below the header row), newest first.
237
+ * Each toast: ┃ in its tone on both sides, a padding row above and below, the
238
+ * title bold, the body muted and wrapped in full. They stay inside `area`, so
239
+ * they never cover the composer or the status line: a toast that does not fit
240
+ * below the ones above it waits in the notification history, and a single
241
+ * toast taller than the area shows what fits and ends with where the full
242
+ * text is.
243
+ *
244
+ * Toasts float over the transcript, so the layer carries its own gap: a
245
+ * cleared row between stacked toasts and under the lowest one, and
246
+ * TOAST_GAP_COLS cleared columns on their left. The layer is `clearBlank`, so
247
+ * those cells show the plain screen, never a transcript block's fill or text
248
+ * touching a toast's edge.
249
+ */
250
+ export function renderToasts(screenW: number, screenH: number, toasts: readonly ToastSpec[], area: ToastArea = { top: 1, bottom: screenH - 2 }): SurfaceLayer | null {
251
+ const top = Math.max(0, area.top);
252
+ // Room for toasts: the area less the gap row the lowest toast keeps under it.
253
+ const maxH = Math.min(screenH, area.bottom) - top - TOAST_GAP_ROWS;
254
+ if (toasts.length === 0 || screenW < 24 || maxH < 3) return null;
255
+ const t = activeTokens();
256
+ const w = toastWidth(screenW);
257
+ const textW = w - 6;
258
+ const blocks = toasts.map((toast) => {
259
+ const lines = [
260
+ ...wrapLines(toast.title, textW).map((text) => ({ text, bold: true, fg: t.text })),
261
+ ...(toast.body ? wrapLines(toast.body, textW).map((text) => ({ text, bold: false, fg: t.textMuted })) : []),
262
+ ];
263
+ return { toast, lines, h: lines.length + 2 };
264
+ });
265
+ let total = 0;
266
+ const shown: typeof blocks = [];
267
+ for (const block of blocks) {
268
+ const need = total + (shown.length > 0 ? TOAST_GAP_ROWS : 0) + block.h;
269
+ if (need > maxH) break;
270
+ shown.push(block);
271
+ total = need;
272
+ }
273
+ if (shown.length === 0) {
274
+ // The newest toast alone is taller than the area: show what fits, and say
275
+ // where the rest is instead of cutting it off silently.
276
+ const first = blocks[0]!;
277
+ const room = maxH - 2;
278
+ const pointer = { text: 'full text in /notifications', bold: false, fg: t.textFaint };
279
+ const lines = room <= 1 ? [pointer] : [...first.lines.slice(0, room - 1), pointer];
280
+ shown.push({ toast: first.toast, lines, h: lines.length + 2 });
281
+ total = lines.length + 2;
282
+ }
283
+ // Blank cells (the left gap columns, the rows between toasts, the gap row
284
+ // under the lowest one) stay blank: the layer clears them on the screen.
285
+ const canvas = new SurfaceCanvas(w + TOAST_GAP_COLS, total + TOAST_GAP_ROWS);
286
+ let y = 0;
287
+ shown.forEach((block, k) => {
288
+ if (k > 0) y += TOAST_GAP_ROWS;
289
+ const bar = toastToneColor(block.toast.tone);
290
+ const x0 = TOAST_GAP_COLS;
291
+ for (let r = 0; r < block.h; r++) {
292
+ canvas.fill(x0, y + r, w, 1, t.backgroundPanel);
293
+ canvas.put(x0, y + r, '┃', { fg: bar });
294
+ canvas.put(x0 + w - 1, y + r, '┃', { fg: bar });
295
+ }
296
+ block.lines.forEach((line, i) => canvas.put(x0 + 3, y + 1 + i, line.text, { fg: line.fg, bold: line.bold }));
297
+ y += block.h;
298
+ });
299
+ return { x: screenW - w - 2 - TOAST_GAP_COLS, y: top, lines: canvas.lines, dim: false, clearBlank: true };
300
+ }
301
+
302
+ // ---------------------------------------------------------------------------
303
+ // Composer popups (slash commands, @ files)
304
+ // ---------------------------------------------------------------------------
305
+
306
+ /** Column of the popup's ┃ bar (the composer's own left edge). */
307
+ export const POPUP_BAR_X = 2;
308
+
309
+ export interface PopupOptions {
310
+ /** Width of the lines to produce (the conversation width). */
311
+ readonly width: number;
312
+ readonly rows: readonly KitRow[];
313
+ /** A row pinned above the list (it never scrolls away), e.g. the typed query. */
314
+ readonly header?: KitRow;
315
+ /** Maximum rows the content may use (padding rows excluded). */
316
+ readonly maxRows: number;
317
+ /** Remembers the list's scroll position between renders (the picker's state object). */
318
+ readonly scrollOwner?: object;
319
+ }
320
+
321
+ export interface PopupResult {
322
+ readonly lines: Line[];
323
+ readonly list: KitListResult;
324
+ }
325
+
326
+ /**
327
+ * A composer popup: the ┃ bar at column 2 in the faint color, a surface fill
328
+ * from column 3 to width-3 with a padding row above and below, text from
329
+ * column 5 and right-aligned text ending 2 columns inside the fill. Returns
330
+ * full-width lines (cells left of the bar and right of the fill are empty)
331
+ * for docking right above the composer. A list longer than the popup keeps
332
+ * the selected row in view and says what is hidden on its last row.
333
+ */
334
+ export function renderPopup(options: PopupOptions): PopupResult {
335
+ const t = activeTokens();
336
+ const { width } = options;
337
+ const fillX = POPUP_BAR_X + 1;
338
+ const fillW = Math.max(6, width - fillX - 2);
339
+ const x0 = fillX + 2;
340
+ const x1 = fillX + fillW - 3;
341
+ const headerRows = options.header ? measureRow(options.header, x0, x1) : 0;
342
+ const listMax = Math.max(1, options.maxRows - headerRows);
343
+ const scrollKey = options.scrollOwner ? { owner: options.scrollOwner, name: 'popup' } : undefined;
344
+ // Measure on a scratch canvas: how many rows the list really needs.
345
+ const compact = width < NARROW_MODAL_BREAKPOINT;
346
+ const scratch = new SurfaceCanvas(width, listMax + 1);
347
+ scratch.compact = compact;
348
+ const trial = drawList(scratch, { rows: options.rows, top: 0, bottom: listMax - 1, x0, x1, scrollKey });
349
+ const overflow = trial.above > 0 || trial.below > 0;
350
+ const listUsed = overflow ? listMax : Math.max(1, trial.endY);
351
+ const used = headerRows + listUsed;
352
+ const canvas = new SurfaceCanvas(width, used + 2);
353
+ canvas.compact = compact;
354
+ canvas.fill(fillX, 0, fillW, used + 2, t.backgroundPanel);
355
+ if (options.header) drawRow(canvas, 1, options.header, x0, x1);
356
+ const listOptions = { rows: options.rows, top: 1 + headerRows, bottom: used, x0, x1, scrollKey };
357
+ const list = overflow ? drawScrollingList(canvas, listOptions) : drawList(canvas, listOptions);
358
+ for (let r = 0; r < used + 2; r++) canvas.put(POPUP_BAR_X, r, '┃', { fg: t.textFaint });
359
+ return { lines: canvas.lines, list };
360
+ }