@pellux/goodvibes-tui 2.0.21 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (388) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +16 -16
  3. package/docs/README.md +4 -4
  4. package/docs/foundation-artifacts/operator-contract.json +1 -1
  5. package/package.json +16 -6
  6. package/src/cli/management.ts +5 -2
  7. package/src/cli/tui-startup.ts +34 -39
  8. package/src/core/agent-view-render.ts +157 -0
  9. package/src/core/approval-alert.ts +49 -11
  10. package/src/core/bookmark-navigation.ts +7 -1
  11. package/src/core/budget-breach-notifier.ts +27 -8
  12. package/src/core/compaction-receipt.ts +1 -1
  13. package/src/core/context-usage.ts +1 -1
  14. package/src/core/conversation-compaction-render.ts +3 -3
  15. package/src/core/conversation-display-only.ts +86 -0
  16. package/src/core/conversation-event-navigation.ts +27 -0
  17. package/src/core/conversation-line-cache.ts +216 -447
  18. package/src/core/conversation-render-context.ts +20 -27
  19. package/src/core/conversation-rendering.ts +157 -575
  20. package/src/core/conversation-splash-state.ts +6 -1
  21. package/src/core/conversation-types.ts +35 -0
  22. package/src/core/conversation-usage.ts +12 -0
  23. package/src/core/conversation.ts +130 -131
  24. package/src/core/long-task-notifier.ts +100 -31
  25. package/src/core/notices.ts +75 -0
  26. package/src/core/pairing-tailscale-gateway.ts +1 -1
  27. package/src/core/process-output-log.ts +143 -0
  28. package/src/core/rewind-receipt.ts +1 -1
  29. package/src/core/session-focus.ts +190 -0
  30. package/src/core/session-resume-core.ts +23 -81
  31. package/src/core/stream-event-wiring.ts +31 -5
  32. package/src/core/system-message-noise.ts +2 -2
  33. package/src/core/system-message-router.ts +16 -18
  34. package/src/core/turn-event-wiring.ts +272 -33
  35. package/src/core/work-tree-focus.ts +290 -0
  36. package/src/core/work-tree-fold-store.ts +143 -0
  37. package/src/core/work-tree-model.ts +799 -0
  38. package/src/core/work-tree-render.ts +384 -0
  39. package/src/core/work-tree-sources.ts +184 -0
  40. package/src/core/work-tree-wiring.ts +176 -0
  41. package/src/core/workstream-notification.ts +36 -6
  42. package/src/core/wrfc-notice-titles.ts +37 -0
  43. package/src/input/agents-modal-text.ts +119 -0
  44. package/src/input/agents-modal.ts +699 -0
  45. package/src/input/bookmark-modal.ts +33 -9
  46. package/src/input/changes-git.ts +251 -0
  47. package/src/input/changes-modal.ts +629 -0
  48. package/src/input/changes-model.ts +125 -0
  49. package/src/input/command-palette-types.ts +41 -0
  50. package/src/input/command-palette.ts +272 -0
  51. package/src/input/command-registry.ts +77 -35
  52. package/src/input/commands/acp-runtime.ts +19 -9
  53. package/src/input/commands/channel-runtime.ts +2 -2
  54. package/src/input/commands/ci-runtime.ts +1 -1
  55. package/src/input/commands/codebase-runtime.ts +1 -1
  56. package/src/input/commands/config.ts +7 -1
  57. package/src/input/commands/context-window.ts +25 -19
  58. package/src/input/commands/control-room-runtime.ts +18 -18
  59. package/src/input/commands/conversation-runtime.ts +0 -1
  60. package/src/input/commands/cost-runtime.ts +29 -20
  61. package/src/input/commands/diff-runtime.ts +35 -181
  62. package/src/input/commands/eval.ts +1 -1
  63. package/src/input/commands/experience-runtime.ts +3 -7
  64. package/src/input/commands/fresh-conversation.ts +86 -0
  65. package/src/input/commands/git-runtime.ts +5 -22
  66. package/src/input/commands/graph-runtime.ts +1 -1
  67. package/src/input/commands/guidance-runtime.ts +1 -1
  68. package/src/input/commands/health-runtime.ts +8 -9
  69. package/src/input/commands/hooks-runtime.ts +2 -2
  70. package/src/input/commands/hosted-runtime.ts +3 -3
  71. package/src/input/commands/incident-runtime.ts +3 -3
  72. package/src/input/commands/intelligence-runtime.ts +6 -9
  73. package/src/input/commands/knowledge.ts +2 -2
  74. package/src/input/commands/legacy-panel-command.ts +57 -0
  75. package/src/input/commands/local-auth-runtime.ts +8 -9
  76. package/src/input/commands/local-provider-runtime.ts +7 -4
  77. package/src/input/commands/local-runtime.ts +12 -15
  78. package/src/input/commands/local-setup-review.ts +1 -1
  79. package/src/input/commands/local-setup.ts +13 -19
  80. package/src/input/commands/memory.ts +1 -1
  81. package/src/input/commands/notify-runtime.ts +9 -0
  82. package/src/input/commands/operator-runtime.ts +10 -12
  83. package/src/input/commands/permissions-provenance.ts +3 -3
  84. package/src/input/commands/planning-runtime.ts +7 -7
  85. package/src/input/commands/platform-sandbox-runtime.ts +2 -2
  86. package/src/input/commands/platform-services-runtime.ts +1 -1
  87. package/src/input/commands/policy-dispatch.ts +2 -2
  88. package/src/input/commands/policy.ts +3 -3
  89. package/src/input/commands/provider.ts +1 -1
  90. package/src/input/commands/remote-runtime.ts +1 -1
  91. package/src/input/commands/review-runtime.ts +39 -94
  92. package/src/input/commands/rewind-runtime.ts +81 -108
  93. package/src/input/commands/runtime-services.ts +15 -17
  94. package/src/input/commands/services-runtime.ts +2 -2
  95. package/src/input/commands/session-content.ts +21 -5
  96. package/src/input/commands/session-workflow.ts +13 -38
  97. package/src/input/commands/settings-sync-runtime.ts +2 -2
  98. package/src/input/commands/shell-core.ts +19 -4
  99. package/src/input/commands/skills-runtime.ts +2 -2
  100. package/src/input/commands/status-runtime.ts +13 -0
  101. package/src/input/commands/subscription-runtime.ts +1 -1
  102. package/src/input/commands/tasks-runtime.ts +2 -8
  103. package/src/input/commands/work-plan-runtime.ts +4 -4
  104. package/src/input/commands/workstream-attempts.ts +24 -47
  105. package/src/input/commands/workstream-runtime.ts +2 -2
  106. package/src/input/commands/worktree-runtime.ts +3 -3
  107. package/src/input/commands.ts +3 -1
  108. package/src/input/composer-editor.ts +44 -0
  109. package/src/input/concealed-input.ts +1 -1
  110. package/src/input/config-modal-types.ts +20 -8
  111. package/src/input/config-modal.ts +106 -105
  112. package/src/input/confirm-dialog-types.ts +28 -0
  113. package/src/input/confirm-dialog.ts +70 -0
  114. package/src/input/delete-key-policy.ts +3 -3
  115. package/src/input/feed-context-factory.ts +15 -16
  116. package/src/input/handler-command-route.ts +7 -42
  117. package/src/input/handler-content-actions.ts +16 -15
  118. package/src/input/handler-feed-routes.ts +26 -211
  119. package/src/input/handler-feed.ts +68 -95
  120. package/src/input/handler-interactions.ts +5 -3
  121. package/src/input/handler-modal-routes.ts +215 -216
  122. package/src/input/handler-modal-stack.ts +81 -11
  123. package/src/input/handler-modal-token-routes.ts +4 -0
  124. package/src/input/handler-onboarding.ts +0 -1
  125. package/src/input/handler-picker-routes.ts +65 -107
  126. package/src/input/handler-session-view-route.ts +149 -0
  127. package/src/input/handler-shortcuts.ts +16 -154
  128. package/src/input/handler-types.ts +4 -2
  129. package/src/input/handler-ui-state.ts +45 -15
  130. package/src/input/handler-work-tree-route.ts +123 -0
  131. package/src/input/handler.ts +25 -70
  132. package/src/input/keybindings.ts +16 -71
  133. package/src/input/masked-entry-modal.ts +117 -0
  134. package/src/input/mcp-workspace.ts +48 -17
  135. package/src/input/model-picker-filter.ts +8 -0
  136. package/src/input/model-picker-open.ts +99 -0
  137. package/src/input/model-picker-types.ts +1 -1
  138. package/src/input/model-picker.ts +92 -9
  139. package/src/input/notifications-modal.ts +123 -0
  140. package/src/input/overlay-filter.ts +26 -0
  141. package/src/input/profile-picker-modal.ts +37 -8
  142. package/src/input/search.ts +3 -3
  143. package/src/input/selection-modal-queue.ts +2 -0
  144. package/src/input/selection-modal.ts +28 -16
  145. package/src/input/session-picker-modal.ts +33 -8
  146. package/src/input/settings-modal-activation.ts +7 -2
  147. package/src/input/settings-modal-data.ts +11 -6
  148. package/src/input/settings-modal-types.ts +1 -1
  149. package/src/input/settings-modal.ts +20 -20
  150. package/src/input/submission-intent.ts +1 -1
  151. package/src/input/submission-router.ts +4 -9
  152. package/src/input/surface-modal-host.ts +110 -0
  153. package/src/input/theme-settings-actions.ts +82 -0
  154. package/src/input/usage-modal.ts +183 -0
  155. package/src/input/views.ts +97 -0
  156. package/src/main.ts +175 -165
  157. package/src/permissions/hunk-selection.ts +1 -1
  158. package/src/permissions/prompt-card.ts +511 -0
  159. package/src/permissions/prompt.ts +59 -622
  160. package/src/renderer/agents-modal.ts +310 -0
  161. package/src/renderer/autocomplete-overlay.ts +42 -220
  162. package/src/renderer/back-to-bottom.ts +80 -0
  163. package/src/renderer/block-actions-overlay.ts +30 -48
  164. package/src/renderer/bookmark-modal.ts +48 -79
  165. package/src/renderer/changes-modal.ts +325 -0
  166. package/src/renderer/code-block.ts +184 -106
  167. package/src/renderer/command-palette.ts +93 -0
  168. package/src/renderer/composer-fragments.ts +4 -4
  169. package/src/renderer/composer.ts +146 -0
  170. package/src/renderer/compositor.ts +26 -160
  171. package/src/renderer/config-modal.ts +185 -81
  172. package/src/renderer/confirm-dialog.ts +50 -0
  173. package/src/renderer/context-inspector.ts +116 -154
  174. package/src/renderer/context-status-hint.ts +4 -4
  175. package/src/renderer/conversation-layout.ts +20 -0
  176. package/src/renderer/conversation-overlays.ts +94 -84
  177. package/src/renderer/conversation-surface.ts +19 -38
  178. package/src/renderer/diff-tint.ts +98 -0
  179. package/src/renderer/file-picker-overlay.ts +63 -90
  180. package/src/renderer/footer-targets.ts +38 -0
  181. package/src/renderer/git-status.ts +3 -4
  182. package/src/renderer/header-line.ts +140 -117
  183. package/src/renderer/help-overlay.ts +133 -219
  184. package/src/renderer/history-search-overlay.ts +15 -12
  185. package/src/renderer/lane-graph/bead.ts +800 -0
  186. package/src/renderer/lane-graph/glyphs.ts +134 -0
  187. package/src/renderer/lane-graph/layout.ts +248 -0
  188. package/src/renderer/lane-graph/paint.ts +537 -0
  189. package/src/renderer/lane-graph/semantic-memo.ts +77 -0
  190. package/src/renderer/layout-engine.ts +2 -57
  191. package/src/renderer/layout.ts +7 -13
  192. package/src/renderer/markdown-inline.ts +2 -1
  193. package/src/renderer/markdown-table.ts +17 -12
  194. package/src/renderer/markdown.ts +92 -34
  195. package/src/renderer/masked-entry-modal.ts +78 -0
  196. package/src/renderer/mcp-workspace.ts +166 -203
  197. package/src/renderer/model-workspace.ts +381 -436
  198. package/src/renderer/notifications-modal.ts +116 -0
  199. package/src/renderer/onboarding/onboarding-wizard.ts +178 -588
  200. package/src/renderer/process-view.ts +192 -0
  201. package/src/renderer/profile-picker-modal.ts +58 -110
  202. package/src/renderer/search-overlay.ts +6 -3
  203. package/src/renderer/selection-modal-overlay.ts +87 -327
  204. package/src/renderer/semantic-diff.ts +2 -2
  205. package/src/renderer/session-chips.ts +102 -0
  206. package/src/renderer/session-picker-modal.ts +177 -297
  207. package/src/renderer/settings-modal-connections.ts +14 -23
  208. package/src/renderer/settings-modal-context.ts +367 -0
  209. package/src/renderer/settings-modal-helpers.ts +6 -11
  210. package/src/renderer/settings-modal.ts +322 -726
  211. package/src/renderer/shell-surface.ts +277 -175
  212. package/src/renderer/shortcuts-overlay.ts +217 -0
  213. package/src/renderer/status-line.ts +475 -0
  214. package/src/renderer/surface-compose.ts +174 -0
  215. package/src/renderer/surface-kit-extra.ts +303 -0
  216. package/src/renderer/surface-kit-list.ts +366 -0
  217. package/src/renderer/surface-kit-parts.ts +360 -0
  218. package/src/renderer/surface-kit.ts +568 -0
  219. package/src/renderer/syntax-highlighter.ts +280 -197
  220. package/src/renderer/syntax-theme.ts +51 -0
  221. package/src/renderer/system-message.ts +0 -1
  222. package/src/renderer/term-caps.ts +42 -1
  223. package/src/renderer/terminal-bg-probe.ts +116 -19
  224. package/src/renderer/terminal-palette-probe.ts +322 -0
  225. package/src/renderer/terminal-palette-reader.ts +183 -0
  226. package/src/renderer/terminal-palette.ts +58 -0
  227. package/src/renderer/theme-mode-config.ts +16 -1
  228. package/src/renderer/theme.ts +234 -207
  229. package/src/renderer/thinking.ts +0 -1
  230. package/src/renderer/throbber.ts +188 -0
  231. package/src/renderer/toast-center.ts +119 -0
  232. package/src/renderer/ui-factory.ts +39 -602
  233. package/src/renderer/ui-primitives.ts +8 -25
  234. package/src/renderer/usage-modal.ts +353 -0
  235. package/src/renderer/user-message.ts +58 -0
  236. package/src/renderer/voice-capture-chip.ts +53 -0
  237. package/src/runtime/bootstrap-command-context.ts +0 -5
  238. package/src/runtime/bootstrap-command-parts.ts +38 -89
  239. package/src/runtime/bootstrap-core.ts +13 -15
  240. package/src/runtime/bootstrap-hook-bridge.ts +12 -21
  241. package/src/runtime/bootstrap-notifier-sync.ts +17 -1
  242. package/src/runtime/bootstrap-shell.ts +16 -50
  243. package/src/runtime/bootstrap.ts +14 -14
  244. package/src/runtime/client/fleet-union.ts +5 -5
  245. package/src/runtime/client/hosted-exit.ts +2 -2
  246. package/src/runtime/client/hosted-sessions.ts +1 -1
  247. package/src/runtime/client/spine-adoption.ts +1 -1
  248. package/src/runtime/context-accounting-source.ts +5 -4
  249. package/src/runtime/index.ts +1 -0
  250. package/src/runtime/notification-dispatch.ts +102 -29
  251. package/src/runtime/recovery-offer-wiring.ts +4 -1
  252. package/src/runtime/recovery-prompt.ts +31 -15
  253. package/src/runtime/runtime-services-types.ts +10 -6
  254. package/src/runtime/services.ts +17 -12
  255. package/src/runtime/session-continuity-hints.ts +2 -5
  256. package/src/runtime/session-inbound-inputs.ts +1 -1
  257. package/src/runtime/session-resume-liveness-confirm.ts +14 -2
  258. package/src/runtime/startup-typeahead-gate.ts +103 -0
  259. package/src/runtime/store/domains/domain-read-matrix.ts +0 -1
  260. package/src/runtime/store/domains/index.ts +0 -8
  261. package/src/runtime/store/selectors/index.ts +0 -28
  262. package/src/runtime/store/state.ts +0 -8
  263. package/src/runtime/ui-services.ts +4 -6
  264. package/src/runtime/usage-tracker.ts +348 -0
  265. package/src/runtime/wrfc-persistence.ts +4 -4
  266. package/src/shell/blocking-input.ts +92 -2
  267. package/src/shell/recovery-input-helpers.ts +1 -1
  268. package/src/shell/session-views.ts +539 -0
  269. package/src/shell/status-report.ts +86 -0
  270. package/src/shell/transcript-scroll.ts +68 -0
  271. package/src/shell/ui-openers.ts +84 -172
  272. package/src/shell/view-openers.ts +205 -0
  273. package/src/tools/wrfc-agent-guard.ts +22 -2
  274. package/src/utils/format-duration.ts +4 -8
  275. package/src/utils/splash-lines.ts +9 -1
  276. package/src/utils/terminal-width.ts +56 -0
  277. package/src/verification/verification-ledger.ts +18 -16
  278. package/src/{panels → views}/agent-inspector-shared.ts +2 -2
  279. package/src/{panels → views}/builtin-modals.ts +22 -21
  280. package/src/views/builtin-views.ts +135 -0
  281. package/src/{panels → views}/confirm-state.ts +9 -9
  282. package/src/{panels → views}/diff-review-model.ts +10 -3
  283. package/src/{panels → views}/eval-registry.ts +1 -1
  284. package/src/{panels → views}/fleet-acts.ts +49 -85
  285. package/src/{panels/fleet-panel-format.ts → views/fleet-format.ts} +40 -40
  286. package/src/{panels → views}/fleet-gateway.ts +5 -5
  287. package/src/{panels → views}/fleet-observed-render.ts +16 -16
  288. package/src/{panels → views}/fleet-read-model.ts +9 -9
  289. package/src/{panels/fleet-tabs.ts → views/fleet-session-tabs.ts} +13 -13
  290. package/src/{panels → views}/fleet-spawn.ts +33 -49
  291. package/src/{panels → views}/fleet-steer.ts +12 -12
  292. package/src/{panels → views}/fleet-stop.ts +4 -4
  293. package/src/{panels → views}/fleet-transcript.ts +26 -27
  294. package/src/{panels/fleet-panel-worktree-detail.ts → views/fleet-worktree-detail.ts} +7 -7
  295. package/src/{panels → views}/hosted-session-feed.ts +6 -6
  296. package/src/views/modal-surface-registry.ts +41 -0
  297. package/src/{panels → views}/modals/devices-modal.ts +2 -2
  298. package/src/{panels → views}/modals/hooks-modal.ts +13 -13
  299. package/src/{panels → views}/modals/keybindings-modal.ts +19 -27
  300. package/src/{panels → views}/modals/knowledge-modal.ts +6 -5
  301. package/src/{panels → views}/modals/local-auth-modal.ts +22 -21
  302. package/src/{panels → views}/modals/marketplace-modal.ts +11 -11
  303. package/src/{panels → views}/modals/memory-modal.ts +8 -8
  304. package/src/{panels → views}/modals/modal-surface-helpers.ts +15 -5
  305. package/src/{panels → views}/modals/pairing-modal.ts +9 -9
  306. package/src/{panels → views}/modals/planning-modal.ts +15 -20
  307. package/src/{panels → views}/modals/plugins-modal.ts +6 -6
  308. package/src/{panels → views}/modals/policy-modal.ts +13 -5
  309. package/src/{panels → views}/modals/provider-health-modal.ts +4 -4
  310. package/src/{panels → views}/modals/remote-modal.ts +3 -3
  311. package/src/{panels → views}/modals/sandbox-modal.ts +4 -4
  312. package/src/{panels → views}/modals/security-modal.ts +22 -16
  313. package/src/{panels → views}/modals/services-modal.ts +3 -3
  314. package/src/{panels → views}/modals/settings-sync-modal.ts +4 -4
  315. package/src/{panels → views}/modals/skills-modal.ts +9 -9
  316. package/src/{panels → views}/modals/subscription-modal.ts +3 -3
  317. package/src/{panels → views}/modals/work-plan-modal.ts +10 -13
  318. package/src/views/notifications-feed.ts +314 -0
  319. package/src/{panels → views}/polish-core.ts +38 -36
  320. package/src/{panels → views}/polish-tables.ts +35 -26
  321. package/src/{panels → views}/polish.ts +101 -95
  322. package/src/{panels → views}/project-planning-answer-actions.ts +3 -3
  323. package/src/{panels → views}/provider-health-routes.ts +14 -14
  324. package/src/{panels → views}/search-focus.ts +8 -8
  325. package/src/{panels → views}/session-maintenance.ts +11 -11
  326. package/src/views/skills-discovery.ts +159 -0
  327. package/src/views/view-deps.ts +80 -0
  328. package/src/views/view-panel-adapter.ts +53 -0
  329. package/src/{panels → views}/workstream-graph-render.ts +1 -1
  330. package/src/core/conversation-fold.ts +0 -101
  331. package/src/core/conversation-turn-structure.ts +0 -464
  332. package/src/core/conversation-user-receipts.ts +0 -42
  333. package/src/input/commands/operator-panel-runtime.ts +0 -186
  334. package/src/input/panel-integration-actions.ts +0 -33
  335. package/src/input/panel-mouse-geometry.ts +0 -97
  336. package/src/input/panel-paste-flood-guard.ts +0 -86
  337. package/src/panels/base-panel.ts +0 -301
  338. package/src/panels/builtin/agent.ts +0 -27
  339. package/src/panels/builtin/development.ts +0 -92
  340. package/src/panels/builtin/knowledge.ts +0 -12
  341. package/src/panels/builtin/operations.ts +0 -177
  342. package/src/panels/builtin/session.ts +0 -81
  343. package/src/panels/builtin/shared.ts +0 -357
  344. package/src/panels/builtin-panels.ts +0 -28
  345. package/src/panels/cost-tracker-panel.ts +0 -746
  346. package/src/panels/diff-panel.ts +0 -797
  347. package/src/panels/diff-review-panel.ts +0 -395
  348. package/src/panels/expandable-list-panel.ts +0 -189
  349. package/src/panels/fleet-deep-link.ts +0 -31
  350. package/src/panels/fleet-panel.ts +0 -799
  351. package/src/panels/git-panel.ts +0 -800
  352. package/src/panels/hosted-session-panel.ts +0 -221
  353. package/src/panels/index.ts +0 -21
  354. package/src/panels/local-auth-panel.ts +0 -478
  355. package/src/panels/notifications-feed.ts +0 -127
  356. package/src/panels/notifications-panel.ts +0 -160
  357. package/src/panels/panel-confirm-overlay.ts +0 -72
  358. package/src/panels/panel-manager.ts +0 -791
  359. package/src/panels/plugins-panel.ts +0 -371
  360. package/src/panels/scrollable-list-panel.ts +0 -498
  361. package/src/panels/skills-panel.ts +0 -454
  362. package/src/panels/token-budget-panel.ts +0 -688
  363. package/src/panels/types.ts +0 -191
  364. package/src/panels/wrfc-panel-format.ts +0 -133
  365. package/src/renderer/diff-view.ts +0 -134
  366. package/src/renderer/fleet-tab-strip.ts +0 -56
  367. package/src/renderer/footer-tips.ts +0 -53
  368. package/src/renderer/fullscreen-primitives.ts +0 -146
  369. package/src/renderer/fullscreen-workspace.ts +0 -194
  370. package/src/renderer/hint-grammar.ts +0 -52
  371. package/src/renderer/modal-factory.ts +0 -480
  372. package/src/renderer/overlay-box.ts +0 -154
  373. package/src/renderer/overlay-viewport.ts +0 -104
  374. package/src/renderer/panel-composite.ts +0 -145
  375. package/src/renderer/panel-workspace-bar.ts +0 -54
  376. package/src/renderer/process-indicator.ts +0 -163
  377. package/src/renderer/tab-strip.ts +0 -222
  378. package/src/renderer/text-layout.ts +0 -31
  379. package/src/renderer/tool-call.ts +0 -276
  380. package/src/renderer/tool-result-expanded-lines.ts +0 -48
  381. package/src/renderer/tool-result-summary.ts +0 -219
  382. package/src/runtime/diagnostics/panels/index.ts +0 -21
  383. package/src/runtime/diagnostics/panels/ops.ts +0 -156
  384. package/src/runtime/perf/panel-contracts.ts +0 -32
  385. package/src/runtime/store/domains/panels.ts +0 -117
  386. /package/src/{panels → views}/memory-consolidation-gateway.ts +0 -0
  387. /package/src/{panels → views}/modals/modal-theme.ts +0 -0
  388. /package/src/{panels → views}/provider-health-domains.ts +0 -0
@@ -22,13 +22,13 @@ import { InputHistory, HistorySearch } from './input-history.ts';
22
22
  import type { BlockMeta, ConversationManager } from '../core/conversation';
23
23
  import { BlockActionsMenu } from '../renderer/block-actions.ts';
24
24
  import { ContextInspectorModal } from '../renderer/context-inspector.ts';
25
+ import { OverlayFilters } from './overlay-filter.ts';
25
26
  import { BookmarkModal } from './bookmark-modal.ts';
26
27
  import { SettingsModal } from './settings-modal.ts';
27
28
  import { McpWorkspace } from './mcp-workspace.ts';
28
29
  import { SessionPickerModal, getSharedHostedSessionRoster } from './session-picker-modal.ts';
29
30
  import { ConfigModal } from './config-modal.ts';
30
31
  import { ProfilePickerModal } from './profile-picker-modal.ts';
31
- import type { PanelBurstGuardState } from './panel-paste-flood-guard.ts';
32
32
  import { OnboardingWizardController, type OnboardingWizardAction, type OnboardingWizardMode } from './onboarding/onboarding-wizard.ts';
33
33
  import {
34
34
  applyOnboardingRequest,
@@ -64,10 +64,8 @@ import { pasteFromClipboard, pasteImageFromClipboard } from '../utils/clipboard.
64
64
  import {
65
65
  handleIndicatorFocusToken,
66
66
  handleMouseToken,
67
- handlePanelFocusToken,
68
67
  handlePromptKeyToken,
69
68
  handlePromptTextToken,
70
- type PanelMouseLayout,
71
69
  } from './handler-feed-routes.ts';
72
70
  import {
73
71
  ensureInputCursorVisible,
@@ -115,11 +113,9 @@ import { handleGlobalShortcutToken } from './handler-shortcuts.ts';
115
113
  import { feedInputTokens } from './handler-feed.ts';
116
114
  import type { InputHandlerLike } from './handler-types.ts';
117
115
  import { buildInitialFeedContext, syncFeedContextMutableFields } from './feed-context-factory.ts';
118
- import { handlePanelIntegrationAction as runPanelIntegrationAction } from './panel-integration-actions.ts';
119
- import type { Panel } from '../panels/types.ts';
120
116
  import type { UiRuntimeServices } from '../runtime/ui-services.ts';
121
- export { handlePanelIntegrationAction } from './panel-integration-actions.ts';
122
117
  import type { ModelPickerTarget } from './model-picker.ts';
118
+ import { SurfaceModalHost } from './surface-modal-host.ts';
123
119
 
124
120
  type SelectionModalCallback = (result: SelectionResult | null) => void;
125
121
 
@@ -148,34 +144,6 @@ export class InputHandler implements InputHandlerLike {
148
144
  public commandMode = false;
149
145
  /** True when the process indicator bar has keyboard focus. */
150
146
  public indicatorFocused = false;
151
- /**
152
- * Fallback focus store used only when the panel manager does not implement
153
- * focus ownership (lightweight test stubs). Production always delegates to
154
- * PanelManager, which is the single source of truth.
155
- */
156
- private _panelFocusedFallback = false;
157
- /**
158
- * True when keyboard focus is on the active panel (arrow/enter go to panel,
159
- * not prompt). Ownership lives in PanelManager (focusTarget); this reads and
160
- * writes through to it so handler focus can never disagree with what the
161
- * panel workspace actually shows.
162
- */
163
- public get panelFocused(): boolean {
164
- const pm = this.uiServices.shell.panelManager;
165
- return typeof pm.getFocusTarget === 'function'
166
- ? pm.getFocusTarget() === 'panel'
167
- : this._panelFocusedFallback;
168
- }
169
- public set panelFocused(value: boolean) {
170
- const pm = this.uiServices.shell.panelManager;
171
- if (typeof pm.focusPanels === 'function' && typeof pm.focusPrompt === 'function') {
172
- if (value) pm.focusPanels();
173
- else pm.focusPrompt();
174
- } else {
175
- this._panelFocusedFallback = value;
176
- }
177
- }
178
-
179
147
  public tokenizer = new InputTokenizer();
180
148
  public pasteRegistry = new Map<string, string>();
181
149
  public nextPasteId = 1;
@@ -193,12 +161,18 @@ export class InputHandler implements InputHandlerLike {
193
161
  public selectionModal = new SelectionModal();
194
162
  public searchManager = new SearchManager();
195
163
  public contextInspectorModal = new ContextInspectorModal();
164
+ public readonly overlayFilters = new OverlayFilters();
196
165
  public bookmarkModal: BookmarkModal;
197
166
  public blockActionsMenu = new BlockActionsMenu();
167
+ public surfaceModals = new SurfaceModalHost();
168
+ /** Agent and process views (shell/session-views.ts); null until the shell wires them. */
169
+ public sessionView: import('./handler-session-view-route.ts').SessionViewControls | null = null;
170
+ /** The main transcript's scroll, for Esc while scrolled back (handler-modal-stack.ts); null until the shell wires it. */
171
+ public transcriptScroll: import('./handler-modal-stack.ts').TranscriptScrollControls | null = null;
172
+ /** Screen rows above the transcript, set each frame by the shell: the header, the chips, the gap under them. */
173
+ public bodyTopRow = 2;
198
174
  public settingsModal = new SettingsModal();
199
175
  public configModal = new ConfigModal();
200
- /** item 5, paste-flood guard state, mutated in place across tokens (never reallocated). */
201
- private panelBurstGuard: PanelBurstGuardState = { timestamps: [], suspended: false, hintShown: false };
202
176
  public mcpWorkspace = new McpWorkspace();
203
177
  public onboardingWizard = new OnboardingWizardController();
204
178
  public onboardingModelPickerCancelSnapshot: OnboardingWizardSnapshot | null = null;
@@ -211,7 +185,7 @@ export class InputHandler implements InputHandlerLike {
211
185
  * Used to support back-navigation via Escape.
212
186
  */
213
187
  public modalStack: string[] = [];
214
- public modalReturnFocus: 'prompt' | 'panel' | 'indicator' = 'prompt';
188
+ public modalReturnFocus: 'prompt' | 'indicator' = 'prompt';
215
189
  public sessionPickerModal: SessionPickerModal;
216
190
  public profilePickerModal: ProfilePickerModal;
217
191
  /** True when the help overlay is visible. */
@@ -231,6 +205,9 @@ export class InputHandler implements InputHandlerLike {
231
205
  public lastBlockCopyTime = 0;
232
206
  public mouseDownRow = -1;
233
207
  public mouseDownCol = -1;
208
+ /** Clickable footer screen rows (set by the shell each frame) and what opens them. */
209
+ public footerTargets: ReadonlyMap<number, import('../renderer/footer-targets.ts').FooterTarget> = new Map();
210
+ public onFooterTarget: ((target: import('../renderer/footer-targets.ts').FooterTarget) => void) | null = null;
234
211
 
235
212
  /** Pasted images: maps marker IDs to base64 image data. */
236
213
  public imageRegistry = new Map<string, { data: string; mediaType: string }>();
@@ -282,8 +259,6 @@ export class InputHandler implements InputHandlerLike {
282
259
  uiServices.providers.benchmarkStore,
283
260
  uiServices.providers.providerRegistry,
284
261
  );
285
- // retirement: ProcessModal/LiveTailModal/AgentDetailModal were removed
286
- //, F2 now opens the Fleet panel, which subsumes the live process tree.
287
262
  this.bookmarkModal = new BookmarkModal(uiServices.shell.bookmarkManager);
288
263
  this.sessionPickerModal = new SessionPickerModal(uiServices.sessions.sessionManager, uiServices.sessions.sessionBroker, getSharedHostedSessionRoster(), () => this.requestRender()); // third source: the daemon-hosted roster
289
264
  this.profilePickerModal = new ProfilePickerModal(uiServices.shell.profileManager);
@@ -299,12 +274,12 @@ export class InputHandler implements InputHandlerLike {
299
274
  this.feedContext = buildInitialFeedContext(
300
275
  {
301
276
  prompt: this.prompt, cursorPos: this.cursorPos, inputScrollTop: this.inputScrollTop, commandMode: this.commandMode,
302
- panelFocused: this.panelFocused, indicatorFocused: this.indicatorFocused,
277
+ indicatorFocused: this.indicatorFocused,
303
278
  helpOverlayActive: this.helpOverlayActive, helpScrollOffset: this.helpScrollOffset,
304
279
  shortcutsOverlayActive: this.shortcutsOverlayActive, shortcutsScrollOffset: this.shortcutsScrollOffset,
305
280
  nextPasteId: this.nextPasteId, nextImageId: this.nextImageId,
306
281
  mouseDownRow: this.mouseDownRow, mouseDownCol: this.mouseDownCol,
307
- contentWidth: this.contentWidth, panelMouseLayout: this.panelMouseLayout,
282
+ contentWidth: this.contentWidth,
308
283
  selectionCallback: this.selectionCallback,
309
284
  },
310
285
  {
@@ -327,16 +302,16 @@ export class InputHandler implements InputHandlerLike {
327
302
  modelPicker: this.modelPicker,
328
303
  onboardingWizard: this.onboardingWizard,
329
304
  contextInspectorModal: this.contextInspectorModal,
305
+ overlayFilters: this.overlayFilters,
330
306
  blockActionsMenu: this.blockActionsMenu,
307
+ surfaceModals: this.surfaceModals,
331
308
  searchManager: this.searchManager,
332
309
  modalStack: this.modalStack,
333
310
  inputHistory: this.inputHistory,
334
311
  conversationManager: this.conversationManager,
335
- panelManager: this.uiServices.shell.panelManager,
336
312
  keybindingsManager: this.uiServices.shell.keybindingsManager,
337
313
  killRing: this.killRing,
338
314
  focusTracker: this.uiServices.platform.focusTracker,
339
- panelBurstGuard: this.panelBurstGuard,
340
315
  getHistory: this.getHistory,
341
316
  getViewportHeight: this.getViewportHeight,
342
317
  getScrollTop: this.getScrollTop,
@@ -345,6 +320,7 @@ export class InputHandler implements InputHandlerLike {
345
320
  },
346
321
  {
347
322
  modalOpened: (name: string) => this.modalOpened(name),
323
+ getSessionView: () => this.sessionView,
348
324
  handleEscape: () => { if (!this.cancelConcealedInput()) this.handleEscape(); this.syncFeedContextMutableFields(); },
349
325
  submitConcealedInput: (value: string) => this.submitConcealedInput(value),
350
326
  handleCopy: () => this.handleCopy(),
@@ -362,11 +338,12 @@ export class InputHandler implements InputHandlerLike {
362
338
  ensureInputCursorVisible: (contentWidth?: number) => this.ensureInputCursorVisible(contentWidth),
363
339
  registerPaste: (content: string) => this.registerPaste(content),
364
340
  executeBlockAction: (id: string) => this.executeBlockAction(id),
365
- cyclePanelTab: (direction: 'next' | 'prev') => this.cyclePanelTab(direction),
366
- onPanelInputConsumed: (activePanel: Panel | null, key: string) => this.handlePanelIntegrationAction(activePanel, key),
367
341
  getWrappedPromptInfo: (contentWidth: number) => this.getWrappedPromptInfo(contentWidth),
368
342
  moveCursorVertical: (direction: -1 | 1) => this.moveCursorVertical(direction),
369
343
  handlePathCompletion: () => this.handlePathCompletion(),
344
+ footerTargetAt: (row: number) => this.footerTargets.get(row),
345
+ openFooterTarget: (target) => { this.onFooterTarget?.(target); },
346
+ bodyTopRow: () => this.bodyTopRow,
370
347
  handleBlockToggle: () => this.handleBlockToggle(),
371
348
  findMarkerAtPos: (pos: number) => this.findMarkerAtPos(pos),
372
349
  cleanupMarkerRegistry: (text: string) => this.cleanupMarkerRegistry(text),
@@ -386,12 +363,11 @@ export class InputHandler implements InputHandlerLike {
386
363
  public syncFeedContextMutableFields(): void {
387
364
  const h = this;
388
365
  syncFeedContextMutableFields({ prompt: h.prompt, cursorPos: h.cursorPos, inputScrollTop: h.inputScrollTop, commandMode: h.commandMode,
389
- panelFocused: h.panelFocused, indicatorFocused: h.indicatorFocused, helpOverlayActive: h.helpOverlayActive,
366
+ indicatorFocused: h.indicatorFocused, helpOverlayActive: h.helpOverlayActive,
390
367
  helpScrollOffset: h.helpScrollOffset, shortcutsOverlayActive: h.shortcutsOverlayActive,
391
368
  shortcutsScrollOffset: h.shortcutsScrollOffset, selectionCallback: h.selectionCallback,
392
369
  nextPasteId: h.nextPasteId, nextImageId: h.nextImageId, mouseDownRow: h.mouseDownRow,
393
- mouseDownCol: h.mouseDownCol, contentWidth: h.contentWidth,
394
- panelMouseLayout: h.panelMouseLayout }, this.feedContext);
370
+ mouseDownCol: h.mouseDownCol, contentWidth: h.contentWidth }, this.feedContext);
395
371
  }
396
372
 
397
373
  /** Wire in the InputHistory instance. Optional; disables history navigation if unset. */
@@ -421,6 +397,7 @@ export class InputHandler implements InputHandlerLike {
421
397
  allowSearch?: boolean;
422
398
  customActions?: Map<string, SelectionAction>;
423
399
  primaryVerbLabel?: string;
400
+ onHighlight?: (item: import('./selection-modal.ts').SelectionItem | null) => void;
424
401
  } | undefined,
425
402
  callback: SelectionModalCallback,
426
403
  ): void {
@@ -465,7 +442,6 @@ export class InputHandler implements InputHandlerLike {
465
442
  public openModelPickerWithTarget(target: ModelPickerTarget, source: 'settings' | 'onboarding' = 'settings'): boolean { return openModelPickerWithTargetForHandler(this, target, source); }
466
443
  public openProviderModelPickerWithTarget(target: ModelPickerTarget, source: 'settings' | 'onboarding' = 'settings'): boolean { return openProviderModelPickerWithTargetForHandler(this, target, source); }
467
444
  public openMcpWorkspace(context: CommandContext): void {
468
- this.panelFocused = false;
469
445
  this.indicatorFocused = false;
470
446
  this.modalOpened('mcpWorkspace');
471
447
  this.mcpWorkspace.open(context);
@@ -517,7 +493,6 @@ export class InputHandler implements InputHandlerLike {
517
493
  context.cursorPos = this.cursorPos;
518
494
  context.inputScrollTop = this.inputScrollTop;
519
495
  context.commandMode = this.commandMode;
520
- context.panelFocused = this.panelFocused;
521
496
  context.indicatorFocused = this.indicatorFocused;
522
497
  context.helpOverlayActive = this.helpOverlayActive;
523
498
  context.helpScrollOffset = this.helpScrollOffset;
@@ -529,7 +504,6 @@ export class InputHandler implements InputHandlerLike {
529
504
  context.mouseDownRow = this.mouseDownRow;
530
505
  context.mouseDownCol = this.mouseDownCol;
531
506
  context.contentWidth = this.contentWidth;
532
- context.panelMouseLayout = this.panelMouseLayout;
533
507
  // Sync semi-stable refs that may be wired after construction.
534
508
  context.commandRegistry = this.commandRegistry;
535
509
  context.commandContext = this.commandContext;
@@ -546,7 +520,6 @@ export class InputHandler implements InputHandlerLike {
546
520
  this.cursorPos = context.cursorPos;
547
521
  this.inputScrollTop = context.inputScrollTop;
548
522
  this.commandMode = context.commandMode;
549
- this.panelFocused = context.panelFocused;
550
523
  this.indicatorFocused = context.indicatorFocused;
551
524
  // Overlay flags: only apply the pipeline's value when the pipeline
552
525
  // itself changed it (e.g. Escape closing the overlay). Otherwise keep
@@ -587,17 +560,12 @@ export class InputHandler implements InputHandlerLike {
587
560
 
588
561
  /** Content width for wrapping, set by main.ts via setContentWidth(). */
589
562
  public contentWidth = 76;
590
- public panelMouseLayout: PanelMouseLayout | null = null;
591
563
 
592
564
  /** Set the content width used for wrapping calculations. Call from main.ts. */
593
565
  public setContentWidth(w: number): void {
594
566
  this.contentWidth = w;
595
567
  }
596
568
 
597
- public setPanelMouseLayout(layout: PanelMouseLayout | null): void {
598
- this.panelMouseLayout = layout;
599
- }
600
-
601
569
  /**
602
570
  * Move cursor up or down by one WRAPPED line.
603
571
  * Uses the segment table to navigate visual lines, not raw \n lines.
@@ -770,19 +738,6 @@ export class InputHandler implements InputHandlerLike {
770
738
  * Word-wrap a single line to fit within maxW columns.
771
739
  * Breaks at spaces; words wider than maxW are force-broken.
772
740
  */
773
- public cyclePanelTab(direction: 'next' | 'prev'): void {
774
- const pm = this.uiServices.shell.panelManager;
775
- if (pm.isVisible()) {
776
- if (direction === 'next') pm.nextWorkspaceTab();
777
- else pm.prevWorkspaceTab();
778
- this.requestRender();
779
- }
780
- }
781
-
782
- public handlePanelIntegrationAction(activePanel: Panel | null, key: string): void {
783
- runPanelIntegrationAction(this.uiServices.shell.panelManager, activePanel, key, this.commandContext);
784
- }
785
-
786
741
  public wordWrapLine(line: string, maxW: number): string[] {
787
742
  return wordWrapLine(line, maxW);
788
743
  }
@@ -34,22 +34,7 @@ export type KeyAction =
34
34
  | 'clear-cancel'
35
35
  | 'screen-clear'
36
36
  | 'command-palette'
37
- | 'panel-picker'
38
- | 'panel-close'
39
- | 'panel-close-all'
40
- | 'panel-tab-next'
41
- | 'panel-tab-prev'
42
- | 'panel-tab-1'
43
- | 'panel-tab-2'
44
- | 'panel-tab-3'
45
- | 'panel-tab-4'
46
- | 'panel-tab-5'
47
- | 'panel-tab-6'
48
- | 'panel-tab-7'
49
- | 'panel-tab-8'
50
- | 'panel-tab-9'
51
- | 'panel-ops'
52
- | 'panel-focus-toggle'
37
+ | 'open-agents'
53
38
  | 'history-search'
54
39
  | 'search'
55
40
  | 'block-copy'
@@ -72,7 +57,8 @@ export type KeyAction =
72
57
  | 'cancel-tool-call'
73
58
  | 'toggle-keep-awake'
74
59
  | 'toggle-memory-provenance'
75
- | 'voice-input';
60
+ | 'voice-input'
61
+ | 'focus-work-tree';
76
62
 
77
63
  /** Human-readable description for each action (used in /keybindings display). */
78
64
  export const ACTION_DESCRIPTIONS: Record<KeyAction, string> = {
@@ -80,22 +66,7 @@ export const ACTION_DESCRIPTIONS: Record<KeyAction, string> = {
80
66
  'clear-cancel': 'Clear input / cancel generation / exit (double)',
81
67
  'screen-clear': 'Repaint the screen',
82
68
  'command-palette': 'Open the command palette (search & run any command)',
83
- 'panel-picker': 'Open, focus, or hide the panel workspace',
84
- 'panel-close': 'Close the currently active panel',
85
- 'panel-close-all': 'Close all open panels',
86
- 'panel-tab-next': 'Next workspace panel tab',
87
- 'panel-tab-prev': 'Previous workspace panel tab',
88
- 'panel-tab-1': 'Jump to workspace panel tab 1',
89
- 'panel-tab-2': 'Jump to workspace panel tab 2',
90
- 'panel-tab-3': 'Jump to workspace panel tab 3',
91
- 'panel-tab-4': 'Jump to workspace panel tab 4',
92
- 'panel-tab-5': 'Jump to workspace panel tab 5',
93
- 'panel-tab-6': 'Jump to workspace panel tab 6',
94
- 'panel-tab-7': 'Jump to workspace panel tab 7',
95
- 'panel-tab-8': 'Jump to workspace panel tab 8',
96
- 'panel-tab-9': 'Jump to workspace panel tab 9',
97
- 'panel-ops': 'Open, focus, or close the Fleet panel (toggle)',
98
- 'panel-focus-toggle': 'Switch keyboard focus between top and bottom pane',
69
+ 'open-agents': 'Open the Agents view (same as F2)',
99
70
  'history-search': 'Reverse input history search',
100
71
  'search': 'Toggle conversation search',
101
72
  'block-copy': 'Copy nearest block to clipboard',
@@ -122,6 +93,7 @@ export const ACTION_DESCRIPTIONS: Record<KeyAction, string> = {
122
93
  // state, there is no release event to watch, so this is two discrete presses,
123
94
  // and the description says so instead of implying a hold that cannot exist.
124
95
  'voice-input': 'Voice input: press to start recording, press again to stop and transcribe into the composer',
96
+ 'focus-work-tree': 'Move the keyboard onto the conversation work tree (arrows move and fold, Enter opens, y copies, Esc returns)',
125
97
  };
126
98
 
127
99
  /** Default key bindings for all actions. */
@@ -129,43 +101,14 @@ export const DEFAULT_KEYBINDINGS: Record<KeyAction, KeyCombo[]> = {
129
101
  'copy-selection': [{ key: 'c', ctrl: true, shift: true }],
130
102
  'clear-cancel': [{ key: 'c', ctrl: true }],
131
103
  'screen-clear': [{ key: 'l', ctrl: true }],
132
- // Ctrl+K opens the command palette (the standard palette chord across editors).
133
- // The readline kill-to-end-of-line that historically owned Ctrl+K is repointed
134
- // to Alt+K below so the capability is kept, not lost.
135
- 'command-palette': [{ key: 'k', ctrl: true }],
136
- 'panel-picker': [{ key: 'p', ctrl: true }],
137
- 'panel-close': [{ key: 'x', ctrl: true }],
138
- 'panel-close-all': [{ key: 'x', ctrl: true, shift: true }],
139
- // Ctrl+] stays the primary next-tab chord; Ctrl+PageDown is added as a second
140
- // binding. Ctrl+[ was REMOVED as prev-tab: it is byte 0x1B (ESC), so the
141
- // legacy tokenizer path emits it as 'escape', the binding never matched on
142
- // most terminals AND the key fired Escape (a split-brain chord). Ctrl+PageUp
143
- // replaces it: Ctrl+PageUp/PageDown tokenize consistently (\x1b[5;5~ /
144
- // \x1b[6;5~ -> pageup/pagedown ctrl:true) in both the legacy and CSI-u paths.
145
- 'panel-tab-next': [{ key: ']', ctrl: true }, { key: 'pagedown', ctrl: true }],
146
- 'panel-tab-prev': [{ key: 'pageup', ctrl: true }],
147
- // Alt+1..9: jump directly to the Nth workspace panel tab (across both panes).
148
- // The tokenizer delivers Alt as the token's `meta` modifier; comboMatches /
149
- // lookup treat `meta` as an alias for `alt`, so these alt-combos route through
150
- // the same rebindable path as every other action.
151
- 'panel-tab-1': [{ key: '1', alt: true }],
152
- 'panel-tab-2': [{ key: '2', alt: true }],
153
- 'panel-tab-3': [{ key: '3', alt: true }],
154
- 'panel-tab-4': [{ key: '4', alt: true }],
155
- 'panel-tab-5': [{ key: '5', alt: true }],
156
- 'panel-tab-6': [{ key: '6', alt: true }],
157
- 'panel-tab-7': [{ key: '7', alt: true }],
158
- 'panel-tab-8': [{ key: '8', alt: true }],
159
- 'panel-tab-9': [{ key: '9', alt: true }],
160
- // Ctrl+O: TOGGLE the Fleet panel (open+focus / bring-to-front+focus / close
161
- //, see toggleFleetPanel in handler-shortcuts.ts). The former Ops
162
- // Control panel was retired to an 'ops-control' -> 'fleet' alias; the
163
- // binding is KEPT (repointed, not removed) so the Ctrl+O muscle memory still
164
- // lands somewhere useful. Routed globally in handleGlobalShortcutToken.
165
- 'panel-ops': [{ key: 'o', ctrl: true }],
166
- // Ctrl+G: toggle keyboard focus between the top and bottom panes. Ctrl+G is
167
- // otherwise unbound in the default table.
168
- 'panel-focus-toggle': [{ key: 'g', ctrl: true }],
104
+ // Ctrl+P opens the command palette, the one entry point for every command,
105
+ // view and setting; Ctrl+K (the other common palette chord) opens it too. The
106
+ // readline kill-to-end-of-line that historically owned Ctrl+K is repointed to
107
+ // Alt+K below so the capability is kept, not lost.
108
+ 'command-palette': [{ key: 'p', ctrl: true }, { key: 'k', ctrl: true }],
109
+ // Ctrl+O: the Agents modal (same as F2), where the old Fleet and Ops
110
+ // consoles' content lives now. Routed globally in handleGlobalShortcutToken.
111
+ 'open-agents': [{ key: 'o', ctrl: true }],
169
112
  'history-search': [{ key: 'r', ctrl: true }],
170
113
  'search': [{ key: 'f', ctrl: true }],
171
114
  'block-copy': [{ key: 'y', ctrl: true }],
@@ -216,6 +159,8 @@ export const DEFAULT_KEYBINDINGS: Record<KeyAction, KeyCombo[]> = {
216
159
  // is paste and stays paste). Two presses rather than a hold: see the action's
217
160
  // description, a terminal has no key-release event to hold against.
218
161
  'voice-input': [{ key: 'v', alt: true }],
162
+ // Alt+Up and Ctrl+Up: into the work tree from the composer (plain Up keeps input history).
163
+ 'focus-work-tree': [{ key: 'up', alt: true }, { key: 'up', ctrl: true }],
219
164
  };
220
165
 
221
166
  /** Resolved overrides type: each key can be a single combo or array. */
@@ -327,7 +272,7 @@ export class KeybindingsManager {
327
272
  if (!token.logicalName) return null;
328
273
  // The tokenizer delivers the Alt modifier as `meta`; the binding table stores
329
274
  // it as `alt`. Treat the two as one modifier at the matching boundary so
330
- // Alt-based combos (word-nav, kill-ring, panel-tab-1..9) resolve at runtime.
275
+ // Alt-based combos (word-nav, kill-ring) resolve at runtime.
331
276
  const alt = token.alt ?? token.meta;
332
277
  const key = `${token.logicalName}:${token.ctrl ? 1 : 0}:${token.shift ? 1 : 0}:${alt ? 1 : 0}`;
333
278
  return this.lookupMap.get(key) ?? null;
@@ -0,0 +1,117 @@
1
+ /**
2
+ * masked-entry-modal.ts, the local-auth password prompt as a kit modal.
3
+ *
4
+ * `/local-auth add-user <name>` and `/local-auth rotate-password <name>` with
5
+ * no password argument, and the Local Auth modal's a / p actions, open this.
6
+ * It is the only way to set a local password that keeps the plaintext out of
7
+ * argv, input history, the transcript and scrollback: keystrokes land in a
8
+ * private buffer that is never rendered (the row shows dots), and the buffer
9
+ * is dropped before the auth call runs, so it never outlives the attempt.
10
+ */
11
+
12
+ import type { InputToken } from '@pellux/goodvibes-sdk/platform/core';
13
+ import type { UserAuthManager } from '@pellux/goodvibes-sdk/platform/security';
14
+ import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
15
+ import type { SurfaceModal, SurfaceModalHost } from './surface-modal-host.ts';
16
+ import type { SurfaceLayer } from '../renderer/surface-kit.ts';
17
+ import { renderMaskedEntryModal, type MaskedEntryView } from '../renderer/masked-entry-modal.ts';
18
+ import { isTextBackspace } from './delete-key-policy.ts';
19
+
20
+ export type MaskedEntryKind = 'add-user' | 'rotate-password';
21
+
22
+ export interface MaskedEntryOptions {
23
+ readonly kind: MaskedEntryKind;
24
+ /** The user; for add-user it may be empty, and the modal asks for it first. */
25
+ readonly username?: string;
26
+ readonly auth: Pick<UserAuthManager, 'addUser' | 'rotatePassword'>;
27
+ /** Report the outcome (never includes the password). */
28
+ readonly onDone?: (message: string) => void;
29
+ }
30
+
31
+ /** Printable characters of a text token (control characters are dropped). */
32
+ function printable(text: string): string {
33
+ return [...text].filter((ch) => ch >= ' ' && ch !== '\x7f').join('');
34
+ }
35
+
36
+ export class MaskedEntryModal implements SurfaceModal, MaskedEntryView {
37
+ readonly name = 'masked-entry';
38
+ readonly kind: MaskedEntryKind;
39
+ username: string;
40
+ step: 'username' | 'password';
41
+ error: string | null = null;
42
+ /** The password being typed. Private, never rendered, cleared on submit, cancel and close. */
43
+ private buffer = '';
44
+
45
+ constructor(private readonly options: MaskedEntryOptions) {
46
+ this.kind = options.kind;
47
+ this.username = (options.username ?? '').trim();
48
+ this.step = this.kind === 'add-user' && this.username.length === 0 ? 'username' : 'password';
49
+ }
50
+
51
+ get passwordLength(): number {
52
+ return [...this.buffer].length;
53
+ }
54
+
55
+ onClose(): void {
56
+ this.buffer = '';
57
+ }
58
+
59
+ escape(): boolean {
60
+ // Esc from the password step of a new user goes back to the username.
61
+ if (this.step === 'password' && this.kind === 'add-user' && !this.options.username) {
62
+ this.buffer = '';
63
+ this.error = null;
64
+ this.step = 'username';
65
+ return true;
66
+ }
67
+ return false;
68
+ }
69
+
70
+ private submit(host: SurfaceModalHost): void {
71
+ if (this.step === 'username') {
72
+ if (this.username.length === 0) return;
73
+ this.step = 'password';
74
+ this.error = null;
75
+ return;
76
+ }
77
+ if (this.buffer.length === 0) return;
78
+ const password = this.buffer;
79
+ // Drop the secret before the call so it never lingers after an exception.
80
+ this.buffer = '';
81
+ try {
82
+ if (this.kind === 'add-user') {
83
+ const added = this.options.auth.addUser(this.username, password, ['admin']);
84
+ host.close(this, 'done');
85
+ this.options.onDone?.(`Added local auth user ${added.username}.`);
86
+ } else {
87
+ this.options.auth.rotatePassword(this.username, password);
88
+ host.close(this, 'done');
89
+ this.options.onDone?.(`Rotated the password for ${this.username}. Existing sessions were revoked.`);
90
+ }
91
+ } catch (error) {
92
+ this.error = summarizeError(error);
93
+ }
94
+ }
95
+
96
+ handleToken(token: InputToken, host: SurfaceModalHost): void {
97
+ if (token.type === 'text') {
98
+ const text = printable(token.value);
99
+ if (!text) return;
100
+ if (this.step === 'username') this.username += text.replace(/\s+/g, '');
101
+ else this.buffer += text;
102
+ this.error = null;
103
+ return;
104
+ }
105
+ if (token.type !== 'key') return;
106
+ const key = token.logicalName ?? '';
107
+ if (key === 'enter') this.submit(host);
108
+ else if (isTextBackspace(key)) {
109
+ if (this.step === 'username') this.username = this.username.slice(0, -1);
110
+ else this.buffer = [...this.buffer].slice(0, -1).join('');
111
+ }
112
+ }
113
+
114
+ render(screenWidth: number, screenHeight: number): SurfaceLayer {
115
+ return renderMaskedEntryModal(this, screenWidth, screenHeight);
116
+ }
117
+ }
@@ -218,6 +218,11 @@ export class McpWorkspace {
218
218
  public tools: readonly RegisteredTool[] = [];
219
219
  public loadingTools = false;
220
220
  public lastError: string | null = null;
221
+ /**
222
+ * The browser's always-live search row. Narrows the server and action rows;
223
+ * selectedIndex indexes the narrowed list (visibleRows).
224
+ */
225
+ public query = '';
221
226
  private context: CommandContext | null = null;
222
227
  private snapshot: McpWorkspaceSnapshot = {
223
228
  projectPath: '',
@@ -233,6 +238,7 @@ export class McpWorkspace {
233
238
  this.selectedIndex = 0;
234
239
  this.formIndex = 0;
235
240
  this.lastError = null;
241
+ this.query = '';
236
242
  this.refreshSnapshot();
237
243
  void this.refreshTools();
238
244
  }
@@ -264,8 +270,23 @@ export class McpWorkspace {
264
270
  ];
265
271
  }
266
272
 
273
+ /** The rows the search row lets through (servers by name, role or source; actions by label). */
274
+ get visibleRows(): readonly McpWorkspaceRow[] {
275
+ const q = this.query.trim().toLowerCase();
276
+ if (!q) return this.rows;
277
+ return this.rows.filter((row) => row.type === 'server'
278
+ ? [row.server.name, row.server.role, row.server.source].some((v) => v.toLowerCase().includes(q))
279
+ : row.label.toLowerCase().includes(q));
280
+ }
281
+
282
+ /** Replace the browser query; the selection moves to the first match. */
283
+ setQuery(query: string): void {
284
+ this.query = query;
285
+ this.selectedIndex = 0;
286
+ }
287
+
267
288
  get selectedRow(): McpWorkspaceRow | null {
268
- return this.rows[this.selectedIndex] ?? null;
289
+ return this.visibleRows[this.selectedIndex] ?? null;
269
290
  }
270
291
 
271
292
  get selectedServer(): McpWorkspaceServerRow | null {
@@ -303,7 +324,7 @@ export class McpWorkspace {
303
324
  effectiveConfig,
304
325
  servers: mergeServers(effectiveConfig, runtimeServers),
305
326
  };
306
- this.selectedIndex = Math.max(0, Math.min(this.selectedIndex, this.rows.length - 1));
327
+ this.selectedIndex = Math.max(0, Math.min(this.selectedIndex, this.visibleRows.length - 1));
307
328
  }
308
329
 
309
330
  async refreshTools(): Promise<void> {
@@ -417,7 +438,7 @@ export class McpWorkspace {
417
438
  }
418
439
 
419
440
  moveSelection(delta: number): void {
420
- const total = this.mode === 'form' ? this.formFields.length : this.rows.length;
441
+ const total = this.mode === 'form' ? this.formFields.length : this.visibleRows.length;
421
442
  if (total <= 0) return;
422
443
  if (this.mode === 'form') {
423
444
  this.formIndex = Math.max(0, Math.min(total - 1, this.formIndex + delta));
@@ -496,6 +517,16 @@ export function handleMcpWorkspaceToken(
496
517
  return true;
497
518
  }
498
519
 
520
+ const browseAction = (key: string): boolean => {
521
+ if (key === 'a') workspace.openAddForm();
522
+ else if (key === 'e' && workspace.selectedServer) workspace.openEditForm(workspace.selectedServer.name);
523
+ else if (key === 'd' && workspace.selectedServer) workspace.requestDelete(workspace.selectedServer.name);
524
+ else if (key === 'r') void workspace.reloadRuntime();
525
+ else if (key === 't') void workspace.refreshTools();
526
+ else return false;
527
+ return true;
528
+ };
529
+
499
530
  if (token.type === 'text') {
500
531
  if (workspace.mode === 'form') {
501
532
  workspace.appendFormText(token.value);
@@ -503,12 +534,11 @@ export function handleMcpWorkspaceToken(
503
534
  if (token.value.toLowerCase() === 'y') void workspace.confirmDelete();
504
535
  else if (token.value.toLowerCase() === 'n') workspace.cancelForm();
505
536
  } else {
537
+ // Browse: the search row is always live. An action letter fires while
538
+ // the query is empty; everything else is typed into the query.
506
539
  const value = token.value.toLowerCase();
507
- if (value === 'a') workspace.openAddForm();
508
- else if (value === 'e' && workspace.selectedServer) workspace.openEditForm(workspace.selectedServer.name);
509
- else if (value === 'd' && workspace.selectedServer) workspace.requestDelete(workspace.selectedServer.name);
510
- else if (value === 'r') void workspace.reloadRuntime();
511
- else if (value === 't') void workspace.refreshTools();
540
+ const claimed = workspace.query.length === 0 && [...token.value].length === 1 && 'aedrt'.includes(value);
541
+ if (!claimed || !browseAction(value)) workspace.setQuery(workspace.query + token.value);
512
542
  }
513
543
  requestRender();
514
544
  return true;
@@ -516,6 +546,8 @@ export function handleMcpWorkspaceToken(
516
546
 
517
547
  if (token.type !== 'key') return true;
518
548
  if (token.logicalName === 'escape') {
549
+ // One level per Esc: the form or the remove confirmation returns to the
550
+ // browser; the browser closes the workspace.
519
551
  if (workspace.mode === 'form' || workspace.mode === 'delete-confirm') {
520
552
  workspace.cancelForm();
521
553
  requestRender();
@@ -530,7 +562,10 @@ export function handleMcpWorkspaceToken(
530
562
  else if (token.logicalName === 'pageup') workspace.moveSelection(-10);
531
563
  else if (token.logicalName === 'pagedown') workspace.moveSelection(10);
532
564
  else if (token.logicalName === 'enter') void workspace.activateSelected();
533
- else if (token.logicalName === 'backspace' || token.logicalName === 'delete') {
565
+ else if (token.logicalName === 'backspace') {
566
+ if (workspace.mode === 'form') workspace.backspaceFormText();
567
+ else if (workspace.mode === 'browse' && workspace.query.length > 0) workspace.setQuery(workspace.query.slice(0, -1));
568
+ } else if (token.logicalName === 'delete') {
534
569
  if (workspace.mode === 'form') workspace.backspaceFormText();
535
570
  else if (workspace.mode === 'browse' && workspace.selectedServer) workspace.requestDelete(workspace.selectedServer.name);
536
571
  } else if (token.logicalName === 'left') {
@@ -539,14 +574,10 @@ export function handleMcpWorkspaceToken(
539
574
  if (workspace.mode === 'form') workspace.adjustFormEnum(1);
540
575
  } else if (token.logicalName === 'space') {
541
576
  if (workspace.mode === 'form') workspace.appendFormText(' ');
542
- } else if (token.logicalName === 'a' && workspace.mode === 'browse') {
543
- workspace.openAddForm();
544
- } else if (token.logicalName === 'd' && workspace.mode === 'browse' && workspace.selectedServer) {
545
- workspace.requestDelete(workspace.selectedServer.name);
546
- } else if (token.logicalName === 'r' && workspace.mode === 'browse') {
547
- void workspace.reloadRuntime();
548
- } else if (token.logicalName === 't' && workspace.mode === 'browse') {
549
- void workspace.refreshTools();
577
+ else if (workspace.mode === 'browse') workspace.setQuery(workspace.query + ' ');
578
+ } else if (workspace.mode === 'browse' && token.logicalName) {
579
+ // Modified letters (CSI-u chords) fire their action whatever the query holds.
580
+ browseAction(token.logicalName);
550
581
  }
551
582
 
552
583
  requestRender();