@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
@@ -1,498 +0,0 @@
1
- import type { Line } from '@pellux/goodvibes-sdk/platform/types';
2
- import { createEmptyLine, createStyledCell } from '@pellux/goodvibes-sdk/platform/types';
3
- import { BasePanel } from './base-panel.ts';
4
- import type { PanelCategory } from './types.ts';
5
- import type { ComponentHealthMonitor } from '@pellux/goodvibes-sdk/platform/runtime/observability';
6
- import {
7
- buildEmptyState,
8
- buildKeyboardHints,
9
- buildPanelWorkspace,
10
- buildSearchInputLine,
11
- DEFAULT_PANEL_PALETTE,
12
- resolveScrollablePanelSection,
13
- type PanelPalette,
14
- } from './polish.ts';
15
- import { GLYPHS } from '../renderer/ui-primitives.ts';
16
-
17
- // ---------------------------------------------------------------------------
18
- // ScrollableListPanel<T>
19
- // ---------------------------------------------------------------------------
20
-
21
- /**
22
- * Base class for all list-based panels that require scroll/cursor navigation.
23
- *
24
- * Subclasses implement:
25
- * - `getItems()`, the ordered list of items to display
26
- * - `renderItem(item, index, selected, width)`, one `Line` per item
27
- *
28
- * Optionally override:
29
- * - `getEmptyStateMessage()` / `getEmptyStateActions()`, empty-state copy
30
- * - `onSelect(item)`, called when the user presses Enter
31
- * - `onAction(item, action)`, for secondary key bindings
32
- * - `getPalette()`, colour palette (defaults to `DEFAULT_PANEL_PALETTE`)
33
- * - `getPageSize()`, rows per page-up/page-down (default 10)
34
- *
35
- * `renderList()` produces the full `Line[]` output that a trivial panel's
36
- * `render()` can return directly:
37
- *
38
- * ```ts
39
- * render(width: number, height: number): Line[] {
40
- * return this.renderList(width, height, { header: this.buildHeader(width) });
41
- * }
42
- * ```
43
- */
44
- export abstract class ScrollableListPanel<T> extends BasePanel {
45
- protected selectedIndex = 0;
46
- /** Tracks the first visible row index; kept in sync with resolveScrollablePanelSection. */
47
- protected scrollStart = 0;
48
- /**
49
- * When true, prepends a 2-column `▸ ` gutter on the selected row.
50
- * Unselected rows get ` ` (two spaces) to maintain alignment.
51
- * Opt-in; default false to avoid breaking existing panel layouts.
52
- */
53
- protected showSelectionGutter = false;
54
-
55
- // -------------------------------------------------------------------------
56
- // Opt-in inline filter ( / to filter, Esc to clear )
57
- // -------------------------------------------------------------------------
58
- // Set `filterEnabled = true` and override `filterMatches()` to give a list
59
- // panel a `/`-to-filter affordance that coexists with single-letter action
60
- // keys (filtering is modal: action keys work until you press `/`). A filter
61
- // input line is auto-rendered at the top of the list.
62
- protected filterEnabled = false;
63
- protected filterQuery = '';
64
- protected filterActive = false;
65
- /** Short noun shown in the filter hint, e.g. "Filter sessions". */
66
- protected filterLabel = 'Filter';
67
-
68
- /** Override to define what the lower-cased filter query matches for an item. */
69
- protected filterMatches(_item: T, _query: string): boolean {
70
- return false;
71
- }
72
-
73
- /**
74
- * The list after applying the active filter, used for both display and
75
- * navigation. Identical to `getItems()` unless a filter is enabled and a
76
- * non-empty query is set, so existing panels are unaffected.
77
- */
78
- protected getVisibleItems(): readonly T[] {
79
- const all = this.getItems();
80
- const q = this.filterQuery.trim().toLowerCase();
81
- if (!this.filterEnabled || !q) return all;
82
- return all.filter((item) => this.filterMatches(item, q));
83
- }
84
-
85
- /**
86
- * The item currently under the cursor, resolved against the FILTERED list
87
- * that navigation actually moves over (`getVisibleItems()`), never the raw
88
- * `getItems()` source.
89
- *
90
- * Subclasses MUST read the selected row through this method (or, when a
91
- * function also needs the list for counts/windows, through a function-scope
92
- * `const visible = this.getVisibleItems()` local, indexing that local, never
93
- * the raw source). Indexing a raw item array (`this.rows`, `this.entries`,
94
- * `getItems()`, …) with `this.selectedIndex` silently returns the wrong row
95
- * whenever a filter is active, because `selectedIndex` is an offset into the
96
- * visible list, not the raw one. The `no-raw-selectedindex-read` architecture
97
- * rule enforces this by banning the `[this.selectedIndex]` token outside the
98
- * base classes.
99
- */
100
- protected getSelectedItem(): T | undefined {
101
- return this.getVisibleItems()[this.selectedIndex];
102
- }
103
-
104
- /**
105
- * Filter-mode key handling. Returns `true`/`false` when consumed/ignored in
106
- * filter context, or `null` to fall through to normal navigation.
107
- */
108
- private _handleFilterKey(key: string): boolean | null {
109
- if (!this.filterEnabled) return null;
110
- if (this.filterActive) {
111
- if (key === 'escape') {
112
- this.filterActive = false;
113
- this.filterQuery = '';
114
- this.selectedIndex = 0;
115
- this.needsRender = true;
116
- return true;
117
- }
118
- if (key === 'return' || key === 'enter') {
119
- this.filterActive = false; // commit; keep the query applied
120
- this.needsRender = true;
121
- return true;
122
- }
123
- if (key === 'backspace' || key === 'delete') {
124
- this.filterQuery = this.filterQuery.slice(0, -1);
125
- this.selectedIndex = 0;
126
- this.needsRender = true;
127
- return true;
128
- }
129
- // Arrow/paging keys navigate the filtered list, fall through.
130
- if (key === 'up' || key === 'down' || key === 'pageup' || key === 'pagedown' || key === 'home' || key === 'end') {
131
- return null;
132
- }
133
- // Any printable character (including j/k/g/G) extends the query.
134
- if (key.length === 1 && key >= ' ') {
135
- this.filterQuery += key;
136
- this.selectedIndex = 0;
137
- this.needsRender = true;
138
- return true;
139
- }
140
- return false;
141
- }
142
- if (key === '/') {
143
- this.filterActive = true;
144
- this.needsRender = true;
145
- return true;
146
- }
147
- return null;
148
- }
149
-
150
- /** The filter input line shown at the top of the list when filtering is enabled. */
151
- protected buildFilterLine(width: number): Line {
152
- const palette = this.getPalette();
153
- const label = this.filterActive ? `[${this.filterLabel}] ` : `${this.filterLabel}: `;
154
- const value = this.filterActive
155
- ? `${this.filterQuery}_`
156
- : this.filterQuery || '(/ to filter)';
157
- return buildSearchInputLine(width, label, value, palette, {
158
- active: false,
159
- bg: this.filterActive ? palette.inputBg : palette.sectionBg,
160
- valueColor: this.filterActive ? palette.info : (this.filterQuery ? palette.value : palette.dim),
161
- });
162
- }
163
-
164
- constructor(
165
- id: string,
166
- name: string,
167
- icon: string,
168
- category: PanelCategory,
169
- componentHealthMonitor?: ComponentHealthMonitor,
170
- ) {
171
- super(id, name, icon, category, componentHealthMonitor);
172
- }
173
-
174
- // -------------------------------------------------------------------------
175
- // Abstract, subclasses must implement
176
- // -------------------------------------------------------------------------
177
-
178
- /** Return the full ordered list of items to display. */
179
- protected abstract getItems(): readonly T[];
180
-
181
- /** Render a single item as one terminal `Line`. */
182
- protected abstract renderItem(
183
- item: T,
184
- index: number,
185
- selected: boolean,
186
- width: number,
187
- ): Line;
188
-
189
- // -------------------------------------------------------------------------
190
- // Optional overrides
191
- // -------------------------------------------------------------------------
192
-
193
- /** Short label shown in the empty-state title. */
194
- protected getEmptyStateMessage(): string {
195
- return 'No items';
196
- }
197
-
198
- /** Suggested actions shown in the empty state. */
199
- protected getEmptyStateActions(): Array<{ command: string; summary: string }> {
200
- return [];
201
- }
202
-
203
- /** Called when the user presses Enter on the selected item. */
204
- protected onSelect(_item: T): void {}
205
-
206
- /** Called for secondary key bindings (e.g. 'd' for delete). */
207
- protected onAction(_item: T, _action: string): void {}
208
-
209
- /** Colour palette used by `renderList()`. */
210
- protected getPalette(): PanelPalette {
211
- return DEFAULT_PANEL_PALETTE;
212
- }
213
-
214
- /**
215
- * Rows to jump on pageup / pagedown.
216
- * Override in `render()` to pass the actual visible row count:
217
- *
218
- * ```ts
219
- * this._pageSize = Math.max(1, visibleRows - 2);
220
- * ```
221
- */
222
- protected getPageSize(): number {
223
- return 10;
224
- }
225
-
226
- // -------------------------------------------------------------------------
227
- // Navigation, consistent across ALL panels
228
- // -------------------------------------------------------------------------
229
-
230
- /**
231
- * Handle keyboard input for list navigation.
232
- *
233
- * **Auto-clearError contract**: At the top of this method, `lastError` is cleared if
234
- * non-null. This means any transient error set via `setError()` is dismissed on the
235
- * very next keystroke the user presses. Subclasses that override `handleInput()` should
236
- * either:
237
- * 1. Call `super.handleInput(key)` as a fallback (preferred), which will clear the
238
- * error when navigation keys are pressed, or
239
- * 2. Manually call `this.clearError()` at the top of their override to maintain
240
- * the same contract for their handled keys.
241
- *
242
- * Returns `true` if the key was consumed, `false` to let the panel manager try another
243
- * handler.
244
- */
245
- /**
246
- * The `/`-to-filter buffer wants every character of a burst (paste, or a
247
- * fast-typed query landing in one input.feed() call), same as it always
248
- * has, see the interface doc on `Panel.isCapturingTextBurst`.
249
- */
250
- isCapturingTextBurst(): boolean {
251
- return this.filterEnabled && this.filterActive;
252
- }
253
-
254
- handleInput(key: string): boolean {
255
- // I2: auto-clear transient errors on the next keystroke so stale errors don't linger.
256
- // Subclasses that override handleInput should call super.handleInput(key) OR manually
257
- // call this.clearError() at the start of their handler.
258
- if (this.lastError !== null) this.clearError();
259
-
260
- // Opt-in inline filter consumes input before navigation when active.
261
- const filterResult = this._handleFilterKey(key);
262
- if (filterResult !== null) return filterResult;
263
-
264
- const items = this.getVisibleItems();
265
- const total = items.length;
266
-
267
- switch (key) {
268
- case 'up':
269
- case 'k':
270
- if (total === 0) return false;
271
- this.selectedIndex = Math.max(0, this.selectedIndex - 1);
272
- this.needsRender = true;
273
- return true;
274
-
275
- case 'down':
276
- case 'j':
277
- if (total === 0) return false;
278
- this.selectedIndex = Math.min(total - 1, this.selectedIndex + 1);
279
- this.needsRender = true;
280
- return true;
281
-
282
- case 'pageup':
283
- if (total === 0) return false;
284
- this.selectedIndex = Math.max(0, this.selectedIndex - this.getPageSize());
285
- this.needsRender = true;
286
- return true;
287
-
288
- case 'pagedown':
289
- if (total === 0) return false;
290
- this.selectedIndex = Math.min(total - 1, this.selectedIndex + this.getPageSize());
291
- this.needsRender = true;
292
- return true;
293
-
294
- case 'home':
295
- case 'g':
296
- if (total === 0) return false;
297
- this.selectedIndex = 0;
298
- this.needsRender = true;
299
- return true;
300
-
301
- case 'end':
302
- case 'G':
303
- if (total === 0) return false;
304
- this.selectedIndex = total - 1;
305
- this.needsRender = true;
306
- return true;
307
-
308
- case 'return':
309
- case 'enter': {
310
- if (total === 0) return false;
311
- const item = items[this.selectedIndex];
312
- if (item !== undefined) this.onSelect(item);
313
- return true;
314
- }
315
-
316
- default:
317
- return false;
318
- }
319
- }
320
-
321
- handleScroll(deltaRows: number): boolean {
322
- if (this.lastError !== null) this.clearError();
323
- const total = this.getVisibleItems().length;
324
- const rows = Math.trunc(deltaRows);
325
- if (total === 0 || rows === 0) return false;
326
- const next = Math.max(0, Math.min(total - 1, this.selectedIndex + rows));
327
- if (next === this.selectedIndex) return false;
328
- this.selectedIndex = next;
329
- this.needsRender = true;
330
- return true;
331
- }
332
-
333
- // -------------------------------------------------------------------------
334
- // Scroll state helpers
335
- // -------------------------------------------------------------------------
336
-
337
- /**
338
- * Clamp `selectedIndex` to the current item count.
339
- * Must be called after any data refresh that may shrink the list.
340
- */
341
- protected clampSelection(): void {
342
- const total = this.getVisibleItems().length;
343
- if (total === 0) {
344
- this.selectedIndex = 0;
345
- } else {
346
- this.selectedIndex = Math.min(this.selectedIndex, total - 1);
347
- }
348
- }
349
-
350
- // -------------------------------------------------------------------------
351
- // Render helper, the main convenience entry point
352
- // -------------------------------------------------------------------------
353
-
354
- /**
355
- * Render the full panel including optional header/footer and an empty state.
356
- *
357
- * Uses `resolveScrollablePanelSection` + `buildPanelWorkspace` internally,
358
- * keeping `scrollStart` in sync after each call.
359
- *
360
- * @param width Panel width in columns.
361
- * @param height Panel height in rows.
362
- * @param options.header Lines prepended as the first workspace section.
363
- * @param options.footer Lines appended as the last workspace section.
364
- * @param options.emptyMessage Override for the empty-state title text.
365
- * @param options.title Workspace title (defaults to `this.name`).
366
- * @param options.spinnerFrame Animation frame for the loading spinner.
367
- */
368
- protected renderList(
369
- width: number,
370
- height: number,
371
- options: {
372
- readonly header?: readonly Line[];
373
- readonly footer?: readonly Line[];
374
- readonly hints?: ReadonlyArray<{ keys: string; label: string }>;
375
- readonly emptyMessage?: string;
376
- readonly title?: string;
377
- readonly spinnerFrame?: number;
378
- } = {},
379
- ): Line[] {
380
- this.needsRender = false;
381
- const palette = this.getPalette();
382
- const items = this.getVisibleItems();
383
- const title = options.title ?? this.name;
384
-
385
- // Auto-inject the filter input line at the top of the header when filtering
386
- // is enabled, so every filterable panel gets the same affordance for free.
387
- const baseHeader = options.header ? [...options.header as Line[]] : [];
388
- const header: Line[] | undefined = this.filterEnabled
389
- ? [this.buildFilterLine(width), ...baseHeader]
390
- : (options.header ? baseHeader : undefined);
391
-
392
- // Standardized keyboard-hints footer row (rendered below any explicit footer).
393
- const hintsLine = options.hints && options.hints.length > 0
394
- ? buildKeyboardHints(width, options.hints, palette)
395
- : null;
396
-
397
- // I2: inject error line into footer when present
398
- const errorLine = this.renderErrorLine(width);
399
- const baseFooter = options.footer ? [...options.footer as Line[]] : [];
400
- if (hintsLine) baseFooter.push(hintsLine);
401
- const effectiveFooter: Line[] = errorLine ? [errorLine, ...baseFooter] : baseFooter;
402
-
403
- // I3: if loading, show spinner in place of normal content
404
- const spinnerLine = this.renderLoadingLine(width, options.spinnerFrame ?? 0);
405
- if (spinnerLine) {
406
- const loadingSection = { lines: [spinnerLine] };
407
- const headerSection = header ? [{ lines: header }] : [];
408
- const lines = buildPanelWorkspace(width, height, {
409
- title,
410
- sections: [...headerSection, loadingSection],
411
- palette,
412
- });
413
- while (lines.length < height) lines.push(createEmptyLine(width));
414
- return lines.slice(0, height);
415
- }
416
-
417
- // Build all item lines (pre-render for resolveScrollablePanelSection)
418
- const scrollableLines: Line[] = items.map((item, index) =>
419
- this.renderItem(item, index, index === this.selectedIndex, width),
420
- );
421
-
422
- // I5: prepend selection gutter when opted in
423
- if (this.showSelectionGutter) {
424
- const infoColor = this.getPalette().info ?? DEFAULT_PANEL_PALETTE.info;
425
- const dimColor = this.getPalette().dim;
426
- for (let i = 0; i < scrollableLines.length; i++) {
427
- const line = scrollableLines[i]!;
428
- const isSelected = i === this.selectedIndex;
429
- // Shift all cells right by 2, drop the last 2 to preserve width
430
- const shifted = line.slice(0, width - 2);
431
- const gutterChar = isSelected ? GLYPHS.navigation.selected : ' ';
432
- const gutterFg = isSelected ? infoColor : dimColor;
433
- const g0 = createStyledCell(gutterChar, { fg: gutterFg, bold: isSelected });
434
- const g1 = createStyledCell(' ', { fg: gutterFg });
435
- scrollableLines[i] = [g0, g1, ...shifted] as Line;
436
- }
437
- }
438
-
439
- // Empty state
440
- if (scrollableLines.length === 0) {
441
- // Distinguish "filter hides everything" from "genuinely empty".
442
- const filteredToEmpty = this.filterEnabled && this.filterQuery.trim() !== '' && this.getItems().length > 0;
443
- const emptyMessage = filteredToEmpty
444
- ? ` No matches for "${this.filterQuery.trim()}"`
445
- : (options.emptyMessage ?? this.getEmptyStateMessage());
446
- const emptyLines = buildEmptyState(
447
- width,
448
- emptyMessage,
449
- filteredToEmpty ? 'Press Esc to clear the filter.' : '',
450
- filteredToEmpty ? [] : this.getEmptyStateActions(),
451
- palette,
452
- );
453
- const lines = buildPanelWorkspace(width, height, {
454
- title,
455
- sections: [
456
- ...(header ? [{ lines: header }] : []),
457
- { lines: emptyLines },
458
- ...(effectiveFooter.length > 0 ? [{ lines: effectiveFooter }] : []),
459
- ],
460
- palette,
461
- });
462
- while (lines.length < height) lines.push(createEmptyLine(width));
463
- return lines.slice(0, height);
464
- }
465
-
466
- // Resolve scrollable section (updates scrollStart)
467
- const beforeSections = header ? [{ lines: header }] : [];
468
- const afterSections = effectiveFooter.length > 0 ? [{ lines: effectiveFooter }] : [];
469
-
470
- const resolved = resolveScrollablePanelSection(width, height, {
471
- palette,
472
- beforeSections,
473
- afterSections,
474
- section: {
475
- scrollableLines,
476
- selectedIndex: this.selectedIndex,
477
- scrollOffset: this.scrollStart,
478
- guardRows: 1,
479
- appendWindowSummary: scrollableLines.length > 5 ? { dimColor: palette.dim } : undefined,
480
- },
481
- });
482
- this.scrollStart = resolved.scrollOffset;
483
-
484
- const sections = [
485
- ...beforeSections,
486
- resolved.section,
487
- ...afterSections,
488
- ];
489
-
490
- const lines = buildPanelWorkspace(width, height, {
491
- title,
492
- sections,
493
- palette,
494
- });
495
- while (lines.length < height) lines.push(createEmptyLine(width));
496
- return lines.slice(0, height);
497
- }
498
- }