@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
@@ -1,46 +1,61 @@
1
1
  /**
2
- * renderHelpOverlay, renders the help overlay with keyboard shortcuts and slash commands.
2
+ * renderHelpOverlay, the /commands reference: keyboard shortcuts plus the
3
+ * full slash-command list, drawn with the modal surface kit as one grouped,
4
+ * filterable kit list (✦ group headers, each entry's description on the left
5
+ * and its key or command right-aligned, muted). The search row is always
6
+ * live; ↑↓ scroll.
3
7
  *
4
- * Opened via `/commands`. `?` and `/help` open the searchable command
5
- * browser (a selection modal that runs the picked command) instead, they
6
- * are a different, complementary surface, not this one.
8
+ * `?` and `/help` open the searchable command browser (a selection modal that
9
+ * runs the picked command) instead; that is a different, complementary
10
+ * surface, not this one.
7
11
  */
8
12
 
9
- import { type Line } from '@pellux/goodvibes-sdk/platform/types';
10
- import { ModalFactory } from './modal-factory.ts';
11
13
  import type { SlashCommand } from '../input/command-registry.ts';
12
14
  import type { KeybindingsManager } from '../input/keybindings.ts';
13
- import { getOverlaySurfaceMetrics } from './overlay-viewport.ts';
14
- import { getVisibleWindow } from './surface-layout.ts';
15
- import { formatHints } from './hint-grammar.ts';
15
+ import type { OverlayFilter } from '../input/overlay-filter.ts';
16
16
  import { logger } from '@pellux/goodvibes-sdk/platform/utils';
17
+ import { activeTokens } from './theme.ts';
18
+ import {
19
+ beginModal,
20
+ finishModal,
21
+ searchRow,
22
+ scrollCountText,
23
+ type KitHint,
24
+ type SurfaceLayer,
25
+ } from './surface-kit.ts';
26
+ import { drawList, listScrollEnd, type KitRow } from './surface-kit-list.ts';
27
+ import { drawTextBlock } from './surface-kit-extra.ts';
17
28
 
18
- function toModalSections(rows: readonly string[]): import('./modal-factory.ts').ModalSection[] {
19
- return rows.map((row) => {
20
- if (row === '') return { type: 'spacer' as const };
21
- if (row.startsWith(' ') && !row.slice(2).includes(' ')) {
22
- return { type: 'title' as const, content: row.trim() };
23
- }
24
- if (row.startsWith(' \u2500')) return { type: 'separator' as const };
25
- return { type: 'text' as const, content: row };
26
- });
29
+ export { renderShortcutsOverlay } from './shortcuts-overlay.ts';
30
+
31
+ interface HelpGroup {
32
+ readonly title: string;
33
+ readonly entries: Array<{ readonly label: string; readonly right: string }>;
27
34
  }
28
35
 
29
36
  /**
30
- * Render the help overlay as Line[].
31
- * Shows keyboard shortcuts summary and slash commands.
32
- *
33
- * @param width Terminal width.
34
- * @param commands List of registered slash commands.
35
- * @param scrollOffset Number of lines scrolled (for navigation).
37
+ * The row strings (" Title", " ────", " key\tdescription", "") become
38
+ * groups: a title row opens a group, rule rows are dropped, entry rows split
39
+ * at the tab into key (right-aligned) and description (the row's label).
36
40
  */
37
- export function renderHelpOverlay(
38
- width: number,
39
- keybindingsManager: KeybindingsManager,
40
- commands?: SlashCommand[],
41
- scrollOffset = 0,
42
- viewportHeight = process.stdout.rows || 24,
43
- ): Line[] {
41
+ function groupsFromRows(rows: readonly string[]): HelpGroup[] {
42
+ const groups: HelpGroup[] = [];
43
+ for (const row of rows) {
44
+ const text = row.trim();
45
+ if (!text || text.startsWith('\u2500')) continue;
46
+ const tab = text.indexOf('\t');
47
+ if (tab < 0) {
48
+ groups.push({ title: text, entries: [] });
49
+ continue;
50
+ }
51
+ if (groups.length === 0) groups.push({ title: 'Commands', entries: [] });
52
+ groups[groups.length - 1]!.entries.push({ right: text.slice(0, tab).trim(), label: text.slice(tab + 1).trim() });
53
+ }
54
+ return groups.filter((g) => g.entries.length > 0);
55
+ }
56
+
57
+ /** Every help group for the live keybindings and registry. */
58
+ function helpGroups(keybindingsManager: KeybindingsManager, commands?: SlashCommand[]): HelpGroup[] {
44
59
  const kb = (action: Parameters<typeof keybindingsManager.getComboLabel>[0]) => keybindingsManager.getComboLabel(action);
45
60
 
46
61
  const hasCommand = (name: string): boolean => {
@@ -55,53 +70,38 @@ export function renderHelpOverlay(
55
70
  return false;
56
71
  };
57
72
 
58
- const shortcutLine = (label: string, desc: string): string => ` ${label.padEnd(20)} ${desc}`;
59
-
60
- // Enumerate EVERY workspace/panel binding straight from the live keybindings
61
- // table so each rebindable action (including user overrides) is discoverable
62
- // here and stays in lockstep with /keybindings. Any action id prefixed
63
- // `panel-` is a workspace affordance.
64
- const panelBindingRows: string[] = keybindingsManager
65
- .getAll()
66
- .filter((entry) => entry.action.startsWith('panel-'))
67
- .map((entry) => shortcutLine(keybindingsManager.getComboLabel(entry.action), entry.description));
73
+ const shortcutLine = (label: string, desc: string): string => ` ${label}\t${desc}`;
68
74
 
69
75
  // Keyboard shortcut sections
70
76
  const shortcutRows: string[] = [
71
77
  ' Core Navigation',
72
78
  ' ' + '\u2500'.repeat(40),
73
- ` ${'Up / Down'.padEnd(20)} Recall input history; Down at bottom focuses the process indicator`,
74
- ` ${'PageUp / PageDn'.padEnd(20)} Scroll by full page`,
75
- ` ${kb('search').padEnd(20)} Search conversation (Ctrl+F)`,
79
+ ` ${'Up / Down'}\tRecall input history; Down at bottom focuses the process indicator`,
80
+ ` ${'PageUp / PageDn'}\tScroll by full page`,
81
+ ` ${kb('search')}\tSearch conversation (Ctrl+F)`,
76
82
  '',
77
83
  ' Prompt And Editing',
78
84
  ' ' + '\u2500'.repeat(40),
79
- ` ${'Enter'.padEnd(20)} Submit message (composer empty: open the block-actions menu)`,
80
- ` ${'Shift+Enter'.padEnd(20)} Insert newline`,
81
- ` ${kb('paste').padEnd(20)} Paste (image priority)`,
82
- ` ${(kb('undo') + ' / ' + kb('redo')).padEnd(20)} Undo / redo`,
85
+ ` ${'Enter'}\tSubmit message (composer empty: open the block-actions menu)`,
86
+ ` ${'Shift+Enter'}\tInsert newline`,
87
+ ` ${kb('paste')}\tPaste (image priority)`,
88
+ ` ${(kb('undo') + ' / ' + kb('redo'))}\tUndo / redo`,
83
89
  shortcutLine('(paste >8 lines)', 'Folds to [TEXT: pN, M lines]; /pastes previews it before you submit'),
84
90
  '',
85
- ' Overlays And Panels',
91
+ ' Overlays And Views',
86
92
  ' ' + '\u2500'.repeat(40),
87
93
  shortcutLine('? or /help', 'Open the command browser (search & run any command)'),
88
94
  shortcutLine('/commands', 'This reference: keyboard shortcuts + full command list'),
89
95
  shortcutLine('/shortcuts', 'Keyboard-shortcuts-only reference (a separate, generated overlay)'),
90
- // Precedence (highest first): a visible panel workspace claims Tab for
91
- // focus-swap; failing that, a partial path under the cursor claims it for
92
- // completion; only then does it fall through to collapse/expand.
93
- shortcutLine('Tab', 'Panel focus-swap, else path-complete, else collapse/expand block'),
94
- ...panelBindingRows,
95
- '',
96
- // The shared in-panel contract every workspace panel honors. These are not
97
- // rebindable global actions \u2014 they are the common controls a focused panel
98
- // interprets \u2014 so they are documented here rather than pulled from getAll().
99
- ' In-Panel Controls',
100
- ' ' + '\u2500'.repeat(40),
101
- shortcutLine('j / k', 'Move selection down / up'),
102
- shortcutLine('g / G', 'Jump to top / bottom'),
103
- shortcutLine('Enter / Esc', 'Activate / dismiss or leave panel'),
104
- shortcutLine('/', 'Filter the list'),
96
+ shortcutLine(kb('command-palette'), 'Command palette: every command and view'),
97
+ shortcutLine(`F2 / ${kb('open-agents')}`, 'Agents: running agents, chains and hosted sessions'),
98
+ shortcutLine('/usage', 'Usage: context, tokens and cost'),
99
+ shortcutLine('/changes', 'Changes: files, diff, review'),
100
+ shortcutLine('/notifications', 'Notification history'),
101
+ shortcutLine('Esc', 'Close the top modal (one level)'),
102
+ // A partial path under the cursor claims Tab for completion; otherwise it
103
+ // collapses or expands the nearest block.
104
+ shortcutLine('Tab', 'Path-complete, else collapse/expand block'),
105
105
  '',
106
106
  ];
107
107
 
@@ -110,7 +110,10 @@ export function renderHelpOverlay(
110
110
  // Commands not registered in the live registry are omitted at render time.
111
111
  const FEATURED_COMMANDS: Array<[name: string, argHint: string, desc: string]> = [
112
112
  ['onboarding', '', 'Open the onboarding wizard with current settings preloaded'],
113
- ['cockpit', '', 'Unified runtime control room'],
113
+ ['agents', '', 'Agents: running agents, chains and hosted sessions'],
114
+ ['usage', '', 'Usage: context, tokens and cost'],
115
+ ['changes', '', 'Changes: files, tinted diff, review and staging'],
116
+ ['notifications','', 'Notification history'],
114
117
  ['settings', '', 'Settings and config browser'],
115
118
  ['provider', '', 'Choose provider or model family'],
116
119
  ['subscription', '', 'Review provider logins and subscriptions'],
@@ -122,15 +125,13 @@ export function renderHelpOverlay(
122
125
  ['incident', '', 'Incident workspace and export flows'],
123
126
  ['knowledge', '', 'Durable knowledge and review queue'],
124
127
  ['hooks', '', 'Hook workbench and runtime activity'],
125
- ['orchestration','', 'Graph and recursive-agent control room'],
126
- ['communication','', 'Structured agent communication workspace'],
127
128
  ['tasks', '', 'Task surface for list/show/pause/resume/output'],
128
129
  ];
129
130
 
130
131
  // Build command rows from featured list, filtering out unregistered commands.
131
132
  function featuredRow(name: string, argHint: string, desc: string): string {
132
133
  const invocation = argHint ? `/${name} ${argHint}` : `/${name}`;
133
- return ` ${invocation.padEnd(23)} ${desc}`;
134
+ return ` ${invocation}\t${desc}`;
134
135
  }
135
136
 
136
137
  const quickStartRows: string[] = [];
@@ -156,9 +157,9 @@ export function renderHelpOverlay(
156
157
  ['sessions', 'Browse and resume saved sessions'],
157
158
  ['save', 'Save the current session'],
158
159
  ['compact', 'Compact the conversation history'],
159
- ['clear', 'Clear the conversation display (keeps LLM context)'],
160
+ ['clear', 'Start a fresh conversation (current one saved)'],
160
161
  ['keybindings', 'List and customize key bindings'],
161
- ['panel', 'Open, focus, or manage panels'],
162
+ ['panel', 'Open a view by its old pane name'],
162
163
  ];
163
164
 
164
165
  const commandRows: string[] = [];
@@ -170,7 +171,7 @@ export function renderHelpOverlay(
170
171
  for (const [name, desc] of ESSENTIAL_COMMANDS) {
171
172
  if (!hasCommand(name)) continue;
172
173
  seen.add(name);
173
- essentialRows.push(` ${`/${name}`.padEnd(18)} ${desc}`);
174
+ essentialRows.push(` ${`/${name}`}\t${desc}`);
174
175
  }
175
176
  if (essentialRows.length > 0) {
176
177
  commandRows.push(' Essentials', ' ' + '\u2500'.repeat(40), ...essentialRows, '');
@@ -187,10 +188,10 @@ export function renderHelpOverlay(
187
188
  const cmd = commands.find((entry) => entry.name === name);
188
189
  if (!cmd || seen.has(cmd.name)) continue;
189
190
  seen.add(cmd.name);
190
- const nameCol = `/${cmd.name}`.padEnd(18);
191
- commandRows.push(` ${nameCol} ${cmd.description}`);
191
+ const nameCol = `/${cmd.name}`;
192
+ commandRows.push(` ${nameCol}\t${cmd.description}`);
192
193
  }
193
- // No command cap: the overlay windows and scrolls (getVisibleWindow below),
194
+ // No command cap: the overlay list scrolls,
194
195
  // so the full remaining registry is listed rather than truncated at 24.
195
196
  const remainder = [...commands]
196
197
  .filter((cmd) => !seen.has(cmd.name))
@@ -198,164 +199,77 @@ export function renderHelpOverlay(
198
199
  if (remainder.length > 0) {
199
200
  commandRows.push('', ' More Commands', ' ' + '\u2500'.repeat(40));
200
201
  for (const cmd of remainder) {
201
- const nameCol = `/${cmd.name}`.padEnd(18);
202
- commandRows.push(` ${nameCol} ${cmd.description}`);
202
+ const nameCol = `/${cmd.name}`;
203
+ commandRows.push(` ${nameCol}\t${cmd.description}`);
203
204
  }
204
205
  }
205
206
  } else if (!hasCommand('help')) {
206
207
  commandRows.push('', ' Essentials', ' ' + '\u2500'.repeat(40));
207
- commandRows.push(' /commands Show this help overlay');
208
- commandRows.push(' /help Browse & run any command');
209
- commandRows.push(' /shortcuts Keyboard shortcut reference');
210
- commandRows.push(' /model Select LLM model');
211
- commandRows.push(' /clear Clear the conversation display (keeps LLM context)');
208
+ commandRows.push(' /commands\tShow this help overlay');
209
+ commandRows.push(' /help\tBrowse & run any command');
210
+ commandRows.push(' /shortcuts\tKeyboard shortcut reference');
211
+ commandRows.push(' /model\tSelect LLM model');
212
+ commandRows.push(' /clear\tStart a fresh conversation (current one saved)');
212
213
  }
213
214
 
214
- const allRows = [...shortcutRows, ...commandRows];
215
-
216
- // Apply scroll offset, show a window of rows
217
- const metrics = getOverlaySurfaceMetrics(width, viewportHeight, {
218
- chromeRows: 4,
219
- minContentRows: 8,
220
- maxContentRows: 12,
221
- });
222
- const maxVisible = metrics.contentRows;
223
- const clampedOffset = Math.max(0, Math.min(scrollOffset, Math.max(0, allRows.length - maxVisible)));
224
- const visibleRows = allRows.slice(clampedOffset, clampedOffset + maxVisible);
225
- const window = getVisibleWindow(allRows.length, clampedOffset, maxVisible);
215
+ return groupsFromRows([...shortcutRows, ...commandRows]);
216
+ }
226
217
 
227
- return ModalFactory.createModal(
228
- {
229
- title: 'Help',
230
- width: metrics.boxWidth,
231
- margin: metrics.margin,
232
- targetContentRows: metrics.contentRows,
233
- // Single tab: the old second 'Commands' tab had no switch handler (Left/
234
- // Right did nothing), so it was a dead affordance. The overlay is one
235
- // scrolling surface, so it advertises exactly one tab.
236
- tabs: [{ label: 'Overview', active: true }],
237
- sections: toModalSections(visibleRows),
238
- helpers: allRows.length > maxVisible
239
- ? [{ content: `[${window.start + 1}-${Math.min(allRows.length, clampedOffset + visibleRows.length)} of ${allRows.length}]` }]
240
- : undefined,
241
- hints: [formatHints([
242
- { key: 'Up/Down', verb: 'Scroll' },
243
- { key: '?', verb: 'Help' },
244
- { key: 'Esc', verb: 'Close' },
245
- ])],
246
- },
247
- width,
248
- );
218
+ function filterGroups(groups: readonly HelpGroup[], query: string): HelpGroup[] {
219
+ const q = query.trim().toLowerCase();
220
+ if (!q) return [...groups];
221
+ return groups
222
+ .map((g) => ({
223
+ title: g.title,
224
+ entries: g.title.toLowerCase().includes(q)
225
+ ? g.entries
226
+ : g.entries.filter((e) => e.label.toLowerCase().includes(q) || e.right.toLowerCase().includes(q)),
227
+ }))
228
+ .filter((g) => g.entries.length > 0);
249
229
  }
250
230
 
231
+ const HINTS: readonly KitHint[] = [['↑↓', 'scroll'], ['?', 'close']];
232
+
251
233
  /**
252
- * renderShortcutsOverlay, renders keyboard shortcuts as Line[].
253
- * Accessed via /shortcuts command. Reflects live keybindings (user overrides included).
234
+ * Render the help overlay as a SurfaceLayer in screen coordinates.
235
+ *
236
+ * @param scrollOffset Rows scrolled past the top of the list.
237
+ * @param filter The search row's query; the renderer records how far the list can scroll in it.
254
238
  */
255
- export function renderShortcutsOverlay(
256
- width: number,
239
+ export function renderHelpOverlay(
240
+ screenWidth: number,
241
+ screenHeight: number,
257
242
  keybindingsManager: KeybindingsManager,
243
+ commands?: SlashCommand[],
258
244
  scrollOffset = 0,
259
- viewportHeight = process.stdout.rows || 24,
260
- ): Line[] {
261
- function row(key: string, desc: string): string {
262
- const keyCol = key.length > 20 ? key.slice(0, 19) + '\u2026' : key.padEnd(20);
263
- return ` ${keyCol} ${desc}`;
264
- }
265
-
266
- // Keys that are real, but not KeyAction entries in keybindingsManager
267
- // (context-dependent chords, or literal characters rather than bindable
268
- // actions) \u2014 a maintained table rather than hand-picked getAll() calls, so
269
- // adding a new KeyAction automatically shows up below without anyone
270
- // remembering to also add a row for it here.
271
- const HARDCODED_ROWS: ReadonlyArray<[key: string, desc: string]> = [
272
- ['Up / Down', 'Recall input history; Down at bottom focuses the process indicator'],
273
- ['PageUp / PageDn', 'Scroll by full page'],
274
- ['Home / End', 'Jump to start / end of line'],
275
- ['n / N (search)', 'Next / previous match'],
276
- ['Mouse wheel', 'Scroll conversation or hovered panel'],
277
- ['Enter', 'Submit message (composer empty: open the block-actions menu)'],
278
- ['Shift+Enter', 'Insert newline'],
279
- ['@', 'Open file picker'],
280
- ['/', 'Slash command mode'],
281
- ['Esc', 'Close overlay / cancel generation / clear prompt (context-dependent)'],
282
- ['Shift+Tab', 'Cycle permission mode (auto-approve / prompt / manual)'],
283
- ['F2', 'Open the Fleet panel'],
284
- ['? or /help', 'Open the command browser (search & run any command)'],
285
- // Precedence (highest first): panel focus-swap, else path-completion,
286
- // else collapse/expand \u2014 see the Panels section's Tab row below for the
287
- // full chain; this row is the "what does it do here" short form.
288
- ['Tab', 'Collapse/expand block (composer empty \u2014 see Panels: Tab for full precedence)'],
289
- ];
290
-
291
- const IN_PANEL_ROWS: ReadonlyArray<[key: string, desc: string]> = [
292
- ['j / k', 'Move selection down / up'],
293
- ['g / G', 'Jump to top / bottom'],
294
- ['/', 'Filter the list'],
295
- ];
296
-
297
- // Generated from the live keybindings table (default bindings AND any user
298
- // overrides) so this listing can never drift from what a key actually
299
- // does \u2014 no action is hand-picked, none can be silently missed.
300
- const allBindings = keybindingsManager.getAll();
301
- const panelActionRows = allBindings
302
- .filter((entry) => entry.action.startsWith('panel-'))
303
- .map((entry) => row(entry.combos.map((c) => keybindingsManager.formatCombo(c)).join(', '), entry.description));
304
- const otherActionRows = allBindings
305
- .filter((entry) => !entry.action.startsWith('panel-'))
306
- .map((entry) => row(entry.combos.map((c) => keybindingsManager.formatCombo(c)).join(', '), entry.description));
245
+ filter?: OverlayFilter,
246
+ ): SurfaceLayer {
247
+ const t = activeTokens();
248
+ const query = filter?.query ?? '';
249
+ const all = helpGroups(keybindingsManager, commands);
250
+ const groups = filterGroups(all, query);
251
+ const f = beginModal(screenWidth, screenHeight, { title: 'Help', hints: HINTS });
252
+ const total = all.reduce((n, g) => n + g.entries.length, 0);
253
+ const shown = groups.reduce((n, g) => n + g.entries.length, 0);
254
+ searchRow(f, f.top, query, 'Filter commands and shortcuts', query ? `${shown} of ${total}` : `${total} entries`);
307
255
 
308
- const allRows: string[] = [
309
- ' Navigation & Editing',
310
- ' ' + '\u2500'.repeat(40),
311
- ...HARDCODED_ROWS.map(([key, desc]) => row(key, desc)),
312
- '',
313
- ' Actions & Shortcuts (customize via /keybindings)',
314
- ' ' + '\u2500'.repeat(40),
315
- ...otherActionRows,
316
- '',
317
- ' Panels',
318
- ' ' + '\u2500'.repeat(40),
319
- // Full Tab precedence, stated once here as the authoritative chain: a
320
- // visible panel workspace claims Tab for focus-swap first; failing that,
321
- // a partial path under the cursor claims it for completion; only then
322
- // does it fall through to collapse/expand (the Actions section's row).
323
- row('Tab', 'Panel focus-swap (1st) > path-complete (2nd) > collapse/expand block (3rd)'),
324
- ...panelActionRows,
325
- '',
326
- ' In-Panel Controls',
327
- ' ' + '\u2500'.repeat(40),
328
- ...IN_PANEL_ROWS.map(([key, desc]) => row(key, desc)),
329
- '',
330
- ` Config: /keybindings to list and customize`,
331
- ];
332
-
333
- const metrics = getOverlaySurfaceMetrics(width, viewportHeight, {
334
- chromeRows: 4,
335
- minContentRows: 8,
336
- maxContentRows: 12,
337
- });
338
- const maxVisible = metrics.contentRows;
339
- const clampedOffset = Math.max(0, Math.min(scrollOffset, Math.max(0, allRows.length - maxVisible)));
340
- const visibleRows = allRows.slice(clampedOffset, clampedOffset + maxVisible);
341
- const window = getVisibleWindow(allRows.length, clampedOffset, maxVisible);
256
+ const top = f.top + 2;
257
+ if (groups.length === 0) {
258
+ if (filter) filter.maxScroll = 0;
259
+ drawTextBlock(f.canvas, f.l, top, f.r - f.l + 1, [{ text: `Nothing matches "${query}".`, style: { fg: t.textMuted } }], f.bottom);
260
+ return finishModal(f);
261
+ }
342
262
 
343
- return ModalFactory.createModal(
344
- {
345
- title: 'Keyboard Shortcuts',
346
- width: metrics.boxWidth,
347
- margin: metrics.margin,
348
- targetContentRows: metrics.contentRows,
349
- tabs: [{ label: 'Shortcuts', active: true }],
350
- sections: toModalSections(visibleRows),
351
- helpers: allRows.length > maxVisible
352
- ? [{ content: `[${window.start + 1}-${Math.min(allRows.length, clampedOffset + visibleRows.length)} of ${allRows.length}]` }]
353
- : undefined,
354
- hints: [formatHints([
355
- { key: 'Up/Down', verb: 'Scroll' },
356
- { key: 'Esc', verb: 'Close' },
357
- ])],
358
- },
359
- width,
360
- );
263
+ const rows: KitRow[] = [];
264
+ for (const g of groups) {
265
+ rows.push({ header: g.title });
266
+ for (const e of g.entries) rows.push({ label: e.label, right: e.right });
267
+ }
268
+ // The furthest start that still fills the list, by drawList's own spacing rules.
269
+ const listOptions = { rows, top, bottom: f.bottom, x0: f.l, x1: f.r };
270
+ const maxStart = listScrollEnd(f.canvas, listOptions);
271
+ if (filter) filter.maxScroll = maxStart;
272
+ const res = drawList(f.canvas, { ...listOptions, scrollStart: Math.min(scrollOffset, maxStart) });
273
+ f.hintRight = scrollCountText(res.above, res.below);
274
+ return finishModal(f);
361
275
  }
@@ -2,7 +2,9 @@ import type { Line } from '@pellux/goodvibes-sdk/platform/types';
2
2
  import { fitDisplay, getDisplayWidth } from '../utils/terminal-width.ts';
3
3
  import type { HistorySearch } from '../input/input-history.ts';
4
4
  import { createBottomBarLine, writeBottomBarText } from '@pellux/goodvibes-terminal-shell';
5
- import { formatHints } from './hint-grammar.ts';
5
+ import { keycapHintsWidth, paintKeycapHints } from './surface-kit-parts.ts';
6
+ import type { KitHint } from './surface-kit.ts';
7
+ import { activeTokens } from './theme.ts';
6
8
 
7
9
  /**
8
10
  * Truncate `text` to at most `maxWidth` display columns, then pad with spaces
@@ -37,16 +39,17 @@ export function renderHistorySearchOverlay(
37
39
 
38
40
  // Build the display string
39
41
  const label = prefix + queryPart;
40
- const hints = formatHints([
41
- { key: 'Ctrl+R/↑', verb: 'Older' },
42
- { key: 'Ctrl+S/↓', verb: 'Newer' },
43
- { key: 'Enter', verb: 'Accept' },
44
- { key: 'Esc', verb: 'Cancel' },
45
- ]);
46
- const full = truncateToWidth(`${label}${matchText} ${hints}`, width);
42
+ const hints: KitHint[] = [['ctrl+r ↑', 'older'], ['ctrl+s ↓', 'newer'], ['⏎', 'accept'], ['esc', 'cancel']];
43
+ // Keycap hints sit at the right end of the bar when they fit beside the search.
44
+ const hintsW = keycapHintsWidth(hints);
45
+ const showHints = width - hintsW - 2 >= getDisplayWidth(label) + 8;
46
+ const textW = showHints ? width - hintsW - 4 : width;
47
+ const full = truncateToWidth(`${label}${matchText}`, textW);
47
48
 
48
- const line = createBottomBarLine(width, { fg: '#000000', bg: '#00ffcc' });
49
- writeBottomBarText(line, 0, width, full, { fg: '#000000', bg: '#00ffcc' });
49
+ const p = activeTokens();
50
+ const line = createBottomBarLine(width, { fg: p.selectedListItemText, bg: p.accent });
51
+ writeBottomBarText(line, 0, textW, full, { fg: p.selectedListItemText, bg: p.accent });
52
+ if (showHints) paintKeycapHints(line, width - hintsW - 2, width - 2, hints, { fg: p.selectedListItemText, bg: p.accent });
50
53
 
51
54
  // Highlight the matched region in the match text with dim styling
52
55
  if (hasMatch && match) {
@@ -56,8 +59,8 @@ export function renderHistorySearchOverlay(
56
59
  const highlightWidth = Math.max(0, matchEndCol - matchStartCol);
57
60
  const matchedSlice = truncateToWidth(match.entry.slice(match.matchStart, match.matchStart + match.matchLength), highlightWidth);
58
61
  writeBottomBarText(line, matchStartCol, highlightWidth, matchedSlice, {
59
- fg: '#000000',
60
- bg: '#00ffcc',
62
+ fg: p.selectedListItemText,
63
+ bg: p.accent,
61
64
  bold: true,
62
65
  underline: true,
63
66
  });