@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,395 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // DiffReviewPanel, comment-on-hunk review loop.
3
- //
4
- // Shows the current session's file changes per-file and hunk-boundaried, lets
5
- // the user select a hunk, attach a comment, and submit that comment to the
6
- // session as a steering message carrying structured context (file path, new-file
7
- // line range, and a short patch excerpt) so the model knows exactly what the
8
- // comment targets.
9
- //
10
- // Data source (labelled honestly in the panel): `git diff <base> -- <files>`,
11
- // where <files> is the set the SDK SessionChangeTracker recorded this session.
12
- // That is CURRENT working-tree content vs the base ref, not a turn-start
13
- // snapshot. The panel never presents a stale diff as current.
14
- // ---------------------------------------------------------------------------
15
-
16
- import type { Line } from '@pellux/goodvibes-sdk/platform/types';
17
- import { createEmptyLine } from '@pellux/goodvibes-sdk/platform/types';
18
- import { truncateDisplay } from '../utils/terminal-width.ts';
19
- import { GitService } from '@pellux/goodvibes-sdk/platform/git';
20
- import { BasePanel } from './base-panel.ts';
21
- import { DIFF_TONES, UI_TONES } from '../renderer/ui-primitives.ts';
22
- import {
23
- buildEmptyState,
24
- buildPanelWorkspace,
25
- buildStyledPanelLine,
26
- resolveScrollablePanelSection,
27
- DEFAULT_PANEL_PALETTE,
28
- extendPalette,
29
- } from './polish.ts';
30
- import { isPanelSearchBackspace, isPanelSearchCancel, isPanelSearchCommit, isPanelSearchPrintable } from './search-focus.ts';
31
- import { appendSteerText } from './fleet-tabs.ts';
32
- import {
33
- parseReviewDiff,
34
- flattenHunks,
35
- hunkLineRange,
36
- buildSteerMessage,
37
- type ReviewFile,
38
- type ReviewHunk,
39
- type HunkComment,
40
- } from './diff-review-model.ts';
41
-
42
- const COLOR = extendPalette(DEFAULT_PANEL_PALETTE, {
43
- addition: DIFF_TONES.add,
44
- deletion: DIFF_TONES.del,
45
- hunk: DIFF_TONES.hunk,
46
- } as const);
47
-
48
- /** A comment attached to a hunk plus whether it has already been steered to the session. */
49
- interface CommentState {
50
- comment: string;
51
- submitted: boolean;
52
- }
53
-
54
- function basename(p: string): string {
55
- const parts = p.replace(/\\/g, '/').split('/');
56
- return parts[parts.length - 1] ?? p;
57
- }
58
-
59
- export class DiffReviewPanel extends BasePanel {
60
- public override isTransient = true;
61
-
62
- private readonly workingDirectory: string;
63
- private readonly getChangedFiles: (() => string[]) | undefined;
64
- private files: ReviewFile[] = [];
65
- private hunks: ReviewHunk[] = [];
66
- private cursor = 0;
67
- private scrollOffset = 0;
68
- /** Comments keyed by `${fileIndex}:${hunkIndex}`. */
69
- private comments = new Map<string, CommentState>();
70
- private composing = false;
71
- private draft = '';
72
- private submitSteer: ((text: string) => void) | null = null;
73
- /** Injected by the /review command: reverse-apply (reject) the given hunk via checkpoints.revertHunk. */
74
- private revertHunk: ((hunk: ReviewHunk) => void) | null = null;
75
- private sourceLabel = 'working tree vs HEAD (files edited this session)';
76
- private status: string | null = null;
77
- /** True once a load has completed (so the empty state can distinguish "no changes" from "not loaded"). */
78
- private loaded = false;
79
-
80
- constructor(
81
- workingDirectory: string,
82
- private readonly requestRender: () => void = () => {},
83
- getChangedFiles?: () => string[],
84
- ) {
85
- super('review', 'Review', 'R', 'development');
86
- this.workingDirectory = workingDirectory;
87
- this.getChangedFiles = getChangedFiles;
88
- }
89
-
90
- // -------------------------------------------------------------------------
91
- // Public API, driven by the /review command
92
- // -------------------------------------------------------------------------
93
-
94
- /** Wire the steering submit path (the /review command passes ctx.submitInput). */
95
- setSubmit(fn: (text: string) => void): void {
96
- this.submitSteer = fn;
97
- }
98
-
99
- /** Wire the hunk-revert path (the /review command drives checkpoints.revertHunk). */
100
- setRevertHandler(fn: (hunk: ReviewHunk) => void): void {
101
- this.revertHunk = fn;
102
- }
103
-
104
- /** Surface a status line (the revert flow reports conflicts/outcomes here). */
105
- note(message: string): void {
106
- this.status = message;
107
- this.markDirty();
108
- this.requestRender();
109
- }
110
-
111
- /** Re-load the session diff, used after a revert mutates the working tree. */
112
- async refresh(): Promise<void> {
113
- await this.loadSessionReview();
114
- this.requestRender();
115
- }
116
-
117
- private keyFor(hunk: ReviewHunk): string {
118
- return `${hunk.fileIndex}:${hunk.hunkIndex}`;
119
- }
120
-
121
- private currentHunk(): ReviewHunk | null {
122
- return this.hunks[this.cursor] ?? null;
123
- }
124
-
125
- /** Adopt a parsed review set with an explicit provenance label. */
126
- loadReview(files: ReviewFile[], sourceLabel: string): void {
127
- this.files = files;
128
- this.hunks = flattenHunks(files);
129
- this.cursor = 0;
130
- this.scrollOffset = 0;
131
- this.comments.clear();
132
- this.composing = false;
133
- this.draft = '';
134
- this.sourceLabel = sourceLabel;
135
- this.loaded = true;
136
- this.markDirty();
137
- }
138
-
139
- /**
140
- * Load the session's changed files as a git diff against `ref` (default HEAD)
141
- * and parse it into per-file hunks. Honest source: current working-tree
142
- * content vs the base ref, restricted to files this session edited.
143
- */
144
- async loadSessionReview(ref = 'HEAD'): Promise<void> {
145
- if (!GitService.isGitRepo(this.workingDirectory)) {
146
- this.status = 'Not a git repository: no working-tree diff to review.';
147
- this.loaded = true;
148
- this.markDirty();
149
- return;
150
- }
151
- const files = (this.getChangedFiles?.() ?? []).filter(Boolean);
152
- const args = ['diff', ref, '--', ...files];
153
- const wholeRepo = files.length === 0;
154
- const proc = Bun.spawn(['git', ...(wholeRepo ? ['diff', ref] : args)], { stdout: 'pipe', stderr: 'pipe', cwd: this.workingDirectory });
155
- const [raw, errText] = await Promise.all([new Response(proc.stdout).text(), new Response(proc.stderr).text()]);
156
- const exitCode = await proc.exited;
157
- if (exitCode !== 0) {
158
- this.status = `git diff failed: ${errText.trim() || 'unknown error'}`;
159
- this.loaded = true;
160
- this.markDirty();
161
- return;
162
- }
163
- const label = wholeRepo
164
- ? `working tree vs ${ref} (no session-tracked files; showing whole working tree)`
165
- : `working tree vs ${ref} (${files.length} file${files.length === 1 ? '' : 's'} edited this session)`;
166
- this.loadReview(parseReviewDiff(raw), label);
167
- }
168
-
169
- // -------------------------------------------------------------------------
170
- // Input
171
- // -------------------------------------------------------------------------
172
-
173
- handleInput(key: string): boolean {
174
- if (this.composing) return this.handleComposerInput(key);
175
- switch (key) {
176
- case 'up': case 'k': this.moveCursor(-1); return true;
177
- case 'down': case 'j': this.moveCursor(1); return true;
178
- case 'tab': this.jumpFile(1); return true;
179
- case '\x1b[Z': case 'shift-tab': case 'backtab': this.jumpFile(-1); return true;
180
- case 'pageup': this.moveCursor(-5); return true;
181
- case 'pagedown': this.moveCursor(5); return true;
182
- case 'c': this.openComposer(); return true;
183
- case 'x': this.clearComment(); return true;
184
- case 'a': this.submitAll(); return true;
185
- case 'r': this.rejectCurrent(); return true;
186
- case 'enter': case 'return': this.submitCurrent(); return true;
187
- default: return false;
188
- }
189
- }
190
-
191
- private handleComposerInput(key: string): boolean {
192
- if (isPanelSearchCancel(key)) {
193
- this.composing = false;
194
- this.draft = '';
195
- this.status = 'Comment cancelled.';
196
- this.markDirty();
197
- return true;
198
- }
199
- if (isPanelSearchCommit(key)) {
200
- const hunk = this.currentHunk();
201
- const text = this.draft.trim();
202
- if (hunk && text.length > 0) {
203
- this.comments.set(this.keyFor(hunk), { comment: text, submitted: false });
204
- this.status = 'Comment attached. Press Enter to send it, or a to send all.';
205
- } else {
206
- this.status = 'Empty comment discarded.';
207
- }
208
- this.composing = false;
209
- this.draft = '';
210
- this.markDirty();
211
- return true;
212
- }
213
- if (isPanelSearchBackspace(key)) {
214
- this.draft = this.draft.slice(0, -1);
215
- this.markDirty();
216
- return true;
217
- }
218
- if (key.length === 1 && (isPanelSearchPrintable(key) || key === '\r' || key === '\n')) {
219
- this.draft = appendSteerText(this.draft, key);
220
- this.markDirty();
221
- return true;
222
- }
223
- return true; // swallow other keys while composing
224
- }
225
-
226
- private moveCursor(delta: number): void {
227
- if (this.hunks.length === 0) return;
228
- this.cursor = Math.max(0, Math.min(this.hunks.length - 1, this.cursor + delta));
229
- this.markDirty();
230
- }
231
-
232
- private jumpFile(direction: number): void {
233
- const cur = this.currentHunk();
234
- if (!cur) return;
235
- const targetFile = cur.fileIndex + direction;
236
- const idx = this.hunks.findIndex((h) => h.fileIndex === targetFile);
237
- if (idx >= 0) {
238
- this.cursor = idx;
239
- this.markDirty();
240
- }
241
- }
242
-
243
- private openComposer(): void {
244
- const hunk = this.currentHunk();
245
- if (!hunk) return;
246
- this.composing = true;
247
- this.draft = this.comments.get(this.keyFor(hunk))?.comment ?? '';
248
- this.status = null;
249
- this.markDirty();
250
- }
251
-
252
- private clearComment(): void {
253
- const hunk = this.currentHunk();
254
- if (!hunk) return;
255
- if (this.comments.delete(this.keyFor(hunk))) {
256
- this.status = 'Comment removed.';
257
- this.markDirty();
258
- }
259
- }
260
-
261
- private steer(items: HunkComment[]): boolean {
262
- if (!this.submitSteer) {
263
- this.status = 'No session is wired to receive steering here.';
264
- this.markDirty();
265
- return false;
266
- }
267
- if (items.length === 0) {
268
- this.status = 'No comment to send. Press c to write one.';
269
- this.markDirty();
270
- return false;
271
- }
272
- this.submitSteer(buildSteerMessage(items, this.sourceLabel));
273
- for (const { hunk } of items) {
274
- const state = this.comments.get(this.keyFor(hunk));
275
- if (state) state.submitted = true;
276
- }
277
- this.status = items.length === 1
278
- ? 'Comment sent to the session as a steering message.'
279
- : `${items.length} comments sent to the session as one steering message.`;
280
- this.markDirty();
281
- this.requestRender();
282
- return true;
283
- }
284
-
285
- private submitCurrent(): void {
286
- const hunk = this.currentHunk();
287
- if (!hunk) return;
288
- const state = this.comments.get(this.keyFor(hunk));
289
- if (!state || state.comment.length === 0) {
290
- this.status = 'This hunk has no comment yet. Press c to write one.';
291
- this.markDirty();
292
- return;
293
- }
294
- this.steer([{ hunk, comment: state.comment }]);
295
- }
296
-
297
- /** Reject the selected hunk: hand it to the revert flow (preview → confirm → revertHunk). */
298
- private rejectCurrent(): void {
299
- const hunk = this.currentHunk();
300
- if (!hunk) return;
301
- if (!this.revertHunk) {
302
- this.note('Hunk revert is not wired in this session.');
303
- return;
304
- }
305
- this.revertHunk(hunk);
306
- }
307
-
308
- private submitAll(): void {
309
- const items: HunkComment[] = [];
310
- for (const hunk of this.hunks) {
311
- const state = this.comments.get(this.keyFor(hunk));
312
- if (state && !state.submitted && state.comment.length > 0) items.push({ hunk, comment: state.comment });
313
- }
314
- this.steer(items);
315
- }
316
-
317
- // -------------------------------------------------------------------------
318
- // Render
319
- // -------------------------------------------------------------------------
320
-
321
- private hunkRow(hunk: ReviewHunk, isCursor: boolean, width: number): Line {
322
- const state = this.comments.get(this.keyFor(hunk));
323
- const marker = !state ? ' ' : state.submitted ? '[✓]' : '[c]';
324
- const range = hunkLineRange(hunk);
325
- const label = `${marker} ${basename(hunk.filePath)}:${range.start}-${range.end} +${hunk.added} -${hunk.removed}`;
326
- return buildStyledPanelLine(width, [{
327
- text: truncateDisplay(`${isCursor ? '▸ ' : ' '}${label}`, width),
328
- fg: state?.submitted ? COLOR.addition : isCursor ? COLOR.value : COLOR.dim,
329
- bg: isCursor ? UI_TONES.bg.selected : undefined,
330
- bold: isCursor,
331
- }]);
332
- }
333
-
334
- private detailLines(hunk: ReviewHunk, width: number): Line[] {
335
- const lines: Line[] = [];
336
- lines.push(buildStyledPanelLine(width, [{ text: truncateDisplay(` ${hunk.filePath}`, width), fg: COLOR.value, bold: true }]));
337
- lines.push(buildStyledPanelLine(width, [{ text: truncateDisplay(` ${hunk.header}`, width), fg: COLOR.hunk }]));
338
- for (const raw of hunk.bodyLines.slice(0, 8)) {
339
- const fg = raw.startsWith('+') ? COLOR.addition : raw.startsWith('-') ? COLOR.deletion : COLOR.dim;
340
- lines.push(buildStyledPanelLine(width, [{ text: truncateDisplay(` ${raw}`, width), fg }]));
341
- }
342
- const state = this.comments.get(this.keyFor(hunk));
343
- if (this.composing) {
344
- lines.push(buildStyledPanelLine(width, [{ text: truncateDisplay(` comment> ${this.draft}_`, width), fg: COLOR.value, bold: true }]));
345
- } else if (state) {
346
- const tag = state.submitted ? '(sent)' : '(unsent)';
347
- lines.push(buildStyledPanelLine(width, [{ text: truncateDisplay(` comment ${tag}: ${state.comment}`, width), fg: state.submitted ? COLOR.addition : COLOR.value }]));
348
- }
349
- return lines;
350
- }
351
-
352
- private footer(width: number): Line {
353
- const hints = this.composing
354
- ? ' Enter attach Esc cancel'
355
- : ' ↑/↓ hunk Tab file c comment Enter send a send all r revert x clear';
356
- const text = this.status ? `${hints} • ${this.status}` : hints;
357
- return buildStyledPanelLine(width, [{ text: truncateDisplay(text, width), fg: COLOR.dim, bg: COLOR.sectionBg }], { fillBg: COLOR.sectionBg });
358
- }
359
-
360
- render(width: number, height: number): Line[] {
361
- return this.trackedRender(() => {
362
- if (height <= 0 || width <= 0) return [];
363
- if (this.hunks.length === 0) {
364
- const detail = this.loaded
365
- ? (this.status ?? 'No changes to review. Files edited this session appear here as hunks.')
366
- : 'Loading session changes…';
367
- return buildPanelWorkspace(width, height, {
368
- title: 'Review',
369
- palette: COLOR,
370
- sections: [{ title: 'Review', lines: buildEmptyState(width, ' Nothing to review yet.', detail, [{ command: '/review', summary: 'load this session\'s file changes as commentable hunks' }], COLOR) }],
371
- });
372
- }
373
-
374
- const hunk = this.currentHunk();
375
- const listLines = this.hunks.map((h, i) => this.hunkRow(h, i === this.cursor, width));
376
- const section = resolveScrollablePanelSection(width, height, {
377
- palette: COLOR,
378
- footerLines: [this.footer(width)],
379
- beforeSections: [{ title: `Selected hunk (${this.sourceLabel})`, lines: hunk ? this.detailLines(hunk, width) : [] }],
380
- section: { title: `Hunks (${this.cursor + 1}/${this.hunks.length})`, scrollableLines: listLines, scrollOffset: this.scrollOffset, selectedIndex: this.cursor, minRows: 1 },
381
- });
382
- this.scrollOffset = section.scrollOffset;
383
-
384
- return buildPanelWorkspace(width, height, {
385
- title: 'Review',
386
- palette: COLOR,
387
- footerLines: [this.footer(width)],
388
- sections: [
389
- { title: `Selected hunk (${this.sourceLabel})`, lines: hunk ? this.detailLines(hunk, width) : [createEmptyLine(width)] },
390
- { title: section.section.title, lines: section.section.lines },
391
- ],
392
- });
393
- });
394
- }
395
- }
@@ -1,189 +0,0 @@
1
- import type { Line } from '@pellux/goodvibes-sdk/platform/types';
2
- import { createEmptyLine } from '@pellux/goodvibes-sdk/platform/types';
3
- import { ScrollableListPanel } from './scrollable-list-panel.ts';
4
- import {
5
- buildKeyboardHints,
6
- buildPanelWorkspace,
7
- resolveScrollablePanelSection,
8
- } from './polish.ts';
9
-
10
- // ---------------------------------------------------------------------------
11
- // ExpandableListPanel<T>
12
- // ---------------------------------------------------------------------------
13
-
14
- /**
15
- * A list panel with a built-in master → detail drill-down.
16
- *
17
- * Encapsulates the collapse/expand + detail-view pattern that was previously
18
- * hand-rolled in git, tool-inspector, agent-inspector, approval, and wrfc
19
- * panels: a scrollable list where pressing Enter/→ on a row opens a scrollable
20
- * detail view for that item, and Esc/← returns to the list.
21
- *
22
- * Subclasses implement (in addition to the `ScrollableListPanel` contract):
23
- * - `getDetailLines(item, width)`, the detail body for the expanded item.
24
- *
25
- * Optionally override:
26
- * - `getDetailTitle(item)`, detail-view title (defaults to `this.name`).
27
- * - `getListHints()` / `getDetailHints()`, footer keyboard hints per mode.
28
- * - `renderListView(width, height)`, the list-mode body (defaults to
29
- * `renderList` with the list hints footer).
30
- *
31
- * Do NOT override `render()`; it dispatches between list and detail modes.
32
- */
33
- export abstract class ExpandableListPanel<T> extends ScrollableListPanel<T> {
34
- /** The item currently expanded into the detail view, or null in list mode. */
35
- protected expandedItem: T | null = null;
36
- /** First visible row of the detail view. */
37
- protected detailScroll = 0;
38
-
39
- /** Render the detail body lines for an expanded item. */
40
- protected abstract getDetailLines(item: T, width: number): readonly Line[];
41
-
42
- /** Title shown above the detail view. */
43
- protected getDetailTitle(_item: T): string {
44
- return this.name;
45
- }
46
-
47
- /** Keyboard hints shown in list mode. */
48
- protected getListHints(): ReadonlyArray<{ keys: string; label: string }> {
49
- return [
50
- { keys: '↑/↓', label: 'move' },
51
- { keys: 'Enter', label: 'open' },
52
- ];
53
- }
54
-
55
- /** Keyboard hints shown in detail mode. */
56
- protected getDetailHints(): ReadonlyArray<{ keys: string; label: string }> {
57
- return [
58
- { keys: '↑/↓', label: 'scroll' },
59
- { keys: 'Esc', label: 'back' },
60
- ];
61
- }
62
-
63
- protected isExpanded(): boolean {
64
- return this.expandedItem !== null;
65
- }
66
-
67
- /** Expand the currently selected item into the detail view. */
68
- protected expandSelected(): void {
69
- const items = this.getItems();
70
- const item = items[this.selectedIndex];
71
- if (item === undefined) return;
72
- this.expandedItem = item;
73
- this.detailScroll = 0;
74
- this.needsRender = true;
75
- }
76
-
77
- /** Collapse the detail view back to the list. */
78
- protected collapse(): void {
79
- if (this.expandedItem === null) return;
80
- this.expandedItem = null;
81
- this.detailScroll = 0;
82
- this.needsRender = true;
83
- }
84
-
85
- // -------------------------------------------------------------------------
86
- // Input
87
- // -------------------------------------------------------------------------
88
-
89
- handleInput(key: string): boolean {
90
- if (this.expandedItem !== null) {
91
- // Detail mode: scroll the detail body; Esc/← collapses.
92
- if (this.lastError !== null) this.clearError();
93
- switch (key) {
94
- case 'escape':
95
- case 'left':
96
- case 'h':
97
- this.collapse();
98
- return true;
99
- case 'up':
100
- case 'k':
101
- this.detailScroll = Math.max(0, this.detailScroll - 1);
102
- this.needsRender = true;
103
- return true;
104
- case 'down':
105
- case 'j':
106
- this.detailScroll += 1;
107
- this.needsRender = true;
108
- return true;
109
- case 'pageup':
110
- this.detailScroll = Math.max(0, this.detailScroll - this.getPageSize());
111
- this.needsRender = true;
112
- return true;
113
- case 'pagedown':
114
- this.detailScroll += this.getPageSize();
115
- this.needsRender = true;
116
- return true;
117
- case 'home':
118
- case 'g':
119
- this.detailScroll = 0;
120
- this.needsRender = true;
121
- return true;
122
- default:
123
- return false;
124
- }
125
- }
126
-
127
- // List mode: Enter/→ expands; everything else is list navigation.
128
- if (key === 'enter' || key === 'return' || key === 'right' || key === 'l') {
129
- if (this.getItems().length === 0) return false;
130
- this.expandSelected();
131
- return true;
132
- }
133
- return super.handleInput(key);
134
- }
135
-
136
- handleScroll(deltaRows: number): boolean {
137
- if (this.expandedItem !== null) {
138
- const rows = Math.trunc(deltaRows);
139
- if (rows === 0) return false;
140
- this.detailScroll = Math.max(0, this.detailScroll + rows);
141
- this.needsRender = true;
142
- return true;
143
- }
144
- return super.handleScroll(deltaRows);
145
- }
146
-
147
- // -------------------------------------------------------------------------
148
- // Render
149
- // -------------------------------------------------------------------------
150
-
151
- render(width: number, height: number): Line[] {
152
- if (this.expandedItem !== null) {
153
- return this.renderDetailView(width, height, this.expandedItem);
154
- }
155
- return this.renderListView(width, height);
156
- }
157
-
158
- /** List-mode body. Override for a custom header; defaults to renderList. */
159
- protected renderListView(width: number, height: number): Line[] {
160
- return this.renderList(width, height, { hints: this.getListHints() });
161
- }
162
-
163
- /** Detail-mode body, scrollable detail lines + standardized chrome. */
164
- protected renderDetailView(width: number, height: number, item: T): Line[] {
165
- this.needsRender = false;
166
- const palette = this.getPalette();
167
- const detailLines = [...this.getDetailLines(item, width)];
168
- const footer = buildKeyboardHints(width, this.getDetailHints(), palette);
169
-
170
- const resolved = resolveScrollablePanelSection(width, height, {
171
- palette,
172
- afterSections: [{ lines: [footer] }],
173
- section: {
174
- scrollableLines: detailLines,
175
- scrollOffset: this.detailScroll,
176
- appendWindowSummary: detailLines.length > 5 ? { dimColor: palette.dim } : undefined,
177
- },
178
- });
179
- this.detailScroll = resolved.scrollOffset;
180
-
181
- const lines = buildPanelWorkspace(width, height, {
182
- title: this.getDetailTitle(item),
183
- sections: [resolved.section, { lines: [footer] }],
184
- palette,
185
- });
186
- while (lines.length < height) lines.push(createEmptyLine(width));
187
- return lines.slice(0, height);
188
- }
189
- }
@@ -1,31 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // fleet-deep-link.ts, item 4.
3
- //
4
- // PanelManager.open('fleet', pane, target) hands FleetPanel a
5
- // PanelDeepLinkTarget (a process id + optional ProcessKind) so a cross-panel
6
- // jump (the work-plan modal's 'i'/'w' agent/WRFC-chain jumps, the docs
7
- // modal's tools-tab Enter) can select + reveal a SPECIFIC node instead of
8
- // just opening Fleet generically. Extracted from fleet-panel.ts (already at
9
- // the 800-line architecture cap) so the panel stays a thin caller.
10
- //
11
- // `kind`, when given, must also match: ProcessNode ids are unique per the
12
- // SDK's own contract, so this is a cheap extra honesty check, not the
13
- // primary lookup key. Reveal is "free" once selectedIndex is set,
14
- // ScrollableListPanel's render-time scroll-window resolution keeps the
15
- // selected row visible, so FleetPanel.receiveDeepLink does not need its own
16
- // scroll math.
17
- //
18
- // A target that isn't found (pruned, killed, never existed, or a domain,
19
- // like the docs Tools tab's static tool definitions, that has no
20
- // corresponding fleet node at all) resolves to -1; the caller degrades
21
- // honestly (FleetPanel.setError('node no longer present.')) rather than
22
- // silently landing on an unrelated row.
23
- // ---------------------------------------------------------------------------
24
-
25
- import type { PanelDeepLinkTarget } from './types.ts';
26
- import type { FleetTreeRow } from './fleet-read-model.ts';
27
-
28
- /** The row index matching `target`'s id (+ kind, if given), or -1. Pure, no I/O, no mutation. */
29
- export function resolveFleetDeepLinkIndex(rows: readonly FleetTreeRow[], target: PanelDeepLinkTarget): number {
30
- return rows.findIndex((row) => row.node.id === target.id && (!target.kind || row.node.kind === target.kind));
31
- }