@pellux/goodvibes-tui 1.1.0 → 1.9.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 (267) hide show
  1. package/CHANGELOG.md +85 -21
  2. package/README.md +28 -18
  3. package/docs/foundation-artifacts/operator-contract.json +4045 -1763
  4. package/package.json +2 -2
  5. package/src/audio/spoken-turn-controller.ts +12 -2
  6. package/src/audio/spoken-turn-wiring.ts +2 -1
  7. package/src/cli/help.ts +8 -1
  8. package/src/cli/service-posture.ts +2 -1
  9. package/src/cli/status.ts +2 -1
  10. package/src/cli/surface-command.ts +2 -2
  11. package/src/core/alert-gating.ts +67 -0
  12. package/src/core/approval-alert.ts +72 -0
  13. package/src/core/budget-breach-notifier.ts +106 -0
  14. package/src/core/composer-state.ts +11 -3
  15. package/src/core/conversation-line-cache.ts +27 -3
  16. package/src/core/conversation-rendering.ts +90 -14
  17. package/src/core/conversation.ts +18 -0
  18. package/src/core/focus-tracker.ts +41 -0
  19. package/src/core/long-task-notifier.ts +33 -6
  20. package/src/core/stream-event-wiring.ts +20 -1
  21. package/src/core/system-message-noise.ts +87 -0
  22. package/src/core/system-message-router.ts +104 -51
  23. package/src/core/turn-cancellation.ts +25 -0
  24. package/src/core/turn-event-wiring.ts +72 -3
  25. package/src/daemon/cli.ts +29 -2
  26. package/src/daemon/handlers/register.ts +8 -1
  27. package/src/daemon/service-commands.ts +329 -0
  28. package/src/export/cost-utils.ts +36 -0
  29. package/src/input/autocomplete.ts +27 -1
  30. package/src/input/command-registry.ts +84 -5
  31. package/src/input/commands/checkpoint-runtime.ts +280 -0
  32. package/src/input/commands/codebase-runtime.ts +232 -0
  33. package/src/input/commands/config.ts +43 -3
  34. package/src/input/commands/eval.ts +1 -1
  35. package/src/input/commands/health-runtime.ts +10 -2
  36. package/src/input/commands/image-runtime.ts +112 -0
  37. package/src/input/commands/intelligence-runtime.ts +11 -1
  38. package/src/input/commands/local-auth-runtime.ts +4 -1
  39. package/src/input/commands/local-runtime.ts +9 -3
  40. package/src/input/commands/marketplace-runtime.ts +2 -2
  41. package/src/input/commands/memory.ts +73 -35
  42. package/src/input/commands/operator-panel-runtime.ts +71 -31
  43. package/src/input/commands/planning-runtime.ts +116 -4
  44. package/src/input/commands/platform-sandbox-runtime.ts +1 -6
  45. package/src/input/commands/plugin-runtime.ts +2 -2
  46. package/src/input/commands/policy-dispatch.ts +21 -0
  47. package/src/input/commands/provider-accounts-runtime.ts +1 -1
  48. package/src/input/commands/qrcode-runtime.ts +26 -6
  49. package/src/input/commands/recall-review.ts +35 -0
  50. package/src/input/commands/remote-runtime-setup.ts +5 -3
  51. package/src/input/commands/remote-runtime.ts +3 -3
  52. package/src/input/commands/runtime-services.ts +54 -13
  53. package/src/input/commands/services-runtime.ts +1 -1
  54. package/src/input/commands/session-content.ts +20 -9
  55. package/src/input/commands/session-workflow.ts +16 -1
  56. package/src/input/commands/settings-sync-runtime.ts +16 -4
  57. package/src/input/commands/shell-core.ts +25 -8
  58. package/src/input/commands/skills-runtime.ts +2 -8
  59. package/src/input/commands/subscription-runtime.ts +2 -2
  60. package/src/input/commands/test-runtime.ts +277 -0
  61. package/src/input/commands/websearch-runtime.ts +101 -0
  62. package/src/input/commands/work-plan-runtime.ts +36 -10
  63. package/src/input/commands/workstream-runtime.ts +488 -0
  64. package/src/input/commands.ts +12 -0
  65. package/src/input/config-modal-types.ts +175 -0
  66. package/src/input/config-modal.ts +592 -0
  67. package/src/input/feed-context-factory.ts +10 -8
  68. package/src/input/handler-command-route.ts +37 -20
  69. package/src/input/handler-content-actions.ts +17 -2
  70. package/src/input/handler-feed-routes.ts +74 -114
  71. package/src/input/handler-feed.ts +79 -27
  72. package/src/input/handler-interactions.ts +1 -3
  73. package/src/input/handler-modal-routes.ts +136 -6
  74. package/src/input/handler-modal-stack.ts +14 -7
  75. package/src/input/handler-modal-token-routes.ts +16 -49
  76. package/src/input/handler-onboarding-daemon-adopt.ts +149 -0
  77. package/src/input/handler-onboarding.ts +71 -59
  78. package/src/input/handler-picker-routes.ts +26 -102
  79. package/src/input/handler-shortcuts.ts +74 -14
  80. package/src/input/handler-types.ts +2 -6
  81. package/src/input/handler-ui-state.ts +10 -22
  82. package/src/input/handler.ts +12 -29
  83. package/src/input/keybindings.ts +23 -8
  84. package/src/input/model-picker-types.ts +24 -6
  85. package/src/input/model-picker.ts +77 -2
  86. package/src/input/onboarding/onboarding-runtime-status.ts +10 -1
  87. package/src/input/onboarding/onboarding-wizard-apply.ts +8 -1
  88. package/src/input/onboarding/onboarding-wizard-constants.ts +4 -1
  89. package/src/input/onboarding/onboarding-wizard-network-adopt.ts +136 -0
  90. package/src/input/onboarding/onboarding-wizard-steps.ts +9 -6
  91. package/src/input/onboarding/onboarding-wizard-types.ts +3 -1
  92. package/src/input/panel-integration-actions.ts +9 -170
  93. package/src/input/panel-mouse-geometry.ts +97 -0
  94. package/src/input/panel-paste-flood-guard.ts +86 -0
  95. package/src/input/selection-modal.ts +11 -0
  96. package/src/input/session-picker-modal.ts +44 -4
  97. package/src/input/settings-modal-data.ts +146 -2
  98. package/src/input/settings-modal-mutations.ts +6 -4
  99. package/src/input/settings-modal-types.ts +4 -2
  100. package/src/main.ts +49 -51
  101. package/src/panels/agent-inspector-shared.ts +2 -1
  102. package/src/panels/base-panel.ts +22 -1
  103. package/src/panels/builtin/agent.ts +21 -107
  104. package/src/panels/builtin/development.ts +15 -61
  105. package/src/panels/builtin/knowledge.ts +8 -32
  106. package/src/panels/builtin/operations.ts +84 -428
  107. package/src/panels/builtin/session.ts +21 -112
  108. package/src/panels/builtin/shared.ts +18 -46
  109. package/src/panels/builtin-modals.ts +218 -0
  110. package/src/panels/builtin-panels.ts +5 -0
  111. package/src/panels/cost-tracker-panel.ts +36 -10
  112. package/src/panels/diff-panel.ts +12 -43
  113. package/src/panels/eval-registry.ts +60 -0
  114. package/src/panels/fleet-deep-link.ts +31 -0
  115. package/src/panels/fleet-panel-format.ts +62 -0
  116. package/src/panels/fleet-panel-worktree-detail.ts +48 -0
  117. package/src/panels/fleet-panel.ts +758 -0
  118. package/src/panels/fleet-read-model.ts +520 -0
  119. package/src/panels/fleet-steer.ts +125 -0
  120. package/src/panels/fleet-stop.ts +153 -0
  121. package/src/panels/fleet-tabs.ts +230 -0
  122. package/src/panels/fleet-transcript.ts +356 -0
  123. package/src/panels/index.ts +7 -31
  124. package/src/panels/modals/hooks-modal.ts +187 -0
  125. package/src/panels/modals/keybindings-modal.ts +166 -0
  126. package/src/panels/modals/knowledge-modal.ts +158 -0
  127. package/src/panels/modals/local-auth-modal.ts +132 -0
  128. package/src/panels/modals/marketplace-modal.ts +218 -0
  129. package/src/panels/modals/memory-modal.ts +180 -0
  130. package/src/panels/modals/modal-surface-helpers.ts +54 -0
  131. package/src/panels/modals/modal-theme.ts +42 -0
  132. package/src/panels/modals/pairing-modal.ts +163 -0
  133. package/src/panels/modals/planning-modal.ts +304 -0
  134. package/src/panels/modals/plugins-modal.ts +174 -0
  135. package/src/panels/modals/policy-modal.ts +256 -0
  136. package/src/panels/modals/provider-health-modal.ts +136 -0
  137. package/src/panels/modals/remote-modal.ts +115 -0
  138. package/src/panels/modals/sandbox-modal.ts +150 -0
  139. package/src/panels/modals/security-modal.ts +217 -0
  140. package/src/panels/modals/services-modal.ts +179 -0
  141. package/src/panels/modals/settings-sync-modal.ts +142 -0
  142. package/src/panels/modals/skills-modal.ts +189 -0
  143. package/src/panels/modals/subscription-modal.ts +172 -0
  144. package/src/panels/modals/work-plan-modal.ts +177 -0
  145. package/src/panels/panel-confirm-overlay.ts +72 -0
  146. package/src/panels/panel-manager.ts +123 -9
  147. package/src/panels/polish-core.ts +38 -25
  148. package/src/panels/project-planning-answer-actions.ts +8 -11
  149. package/src/panels/skills-panel.ts +7 -51
  150. package/src/panels/types.ts +37 -0
  151. package/src/permissions/hunk-selection.ts +179 -0
  152. package/src/permissions/prompt.ts +216 -13
  153. package/src/renderer/autocomplete-overlay.ts +28 -2
  154. package/src/renderer/compaction-history-modal.ts +19 -3
  155. package/src/renderer/compaction-preview.ts +13 -2
  156. package/src/renderer/compaction-quality.ts +100 -0
  157. package/src/renderer/compositor.ts +2 -3
  158. package/src/renderer/config-modal.ts +95 -0
  159. package/src/renderer/conversation-overlays.ts +10 -17
  160. package/src/renderer/fleet-tab-strip.ts +56 -0
  161. package/src/renderer/footer-tips.ts +15 -3
  162. package/src/renderer/fullscreen-primitives.ts +32 -22
  163. package/src/renderer/git-status.ts +43 -1
  164. package/src/renderer/help-overlay.ts +2 -2
  165. package/src/renderer/layout.ts +0 -4
  166. package/src/renderer/markdown.ts +7 -3
  167. package/src/renderer/modal-factory.ts +25 -20
  168. package/src/renderer/model-workspace.ts +62 -4
  169. package/src/renderer/overlay-box.ts +21 -17
  170. package/src/renderer/panel-workspace-bar.ts +8 -2
  171. package/src/renderer/process-indicator.ts +14 -3
  172. package/src/renderer/selection-modal-overlay.ts +6 -1
  173. package/src/renderer/session-picker-modal.ts +196 -3
  174. package/src/renderer/settings-modal-helpers.ts +2 -0
  175. package/src/renderer/settings-modal.ts +7 -0
  176. package/src/renderer/shell-surface.ts +8 -1
  177. package/src/renderer/status-glyphs.ts +14 -15
  178. package/src/renderer/system-message.ts +15 -3
  179. package/src/renderer/terminal-bg-probe.ts +339 -0
  180. package/src/renderer/terminal-escapes.ts +20 -0
  181. package/src/renderer/theme-mode-config.ts +67 -0
  182. package/src/renderer/theme.ts +91 -1
  183. package/src/renderer/thinking.ts +11 -3
  184. package/src/renderer/tool-call.ts +15 -9
  185. package/src/renderer/tool-result-summary.ts +148 -0
  186. package/src/renderer/turn-injection.ts +133 -0
  187. package/src/renderer/ui-factory.ts +154 -85
  188. package/src/renderer/ui-primitives.ts +30 -129
  189. package/src/runtime/bootstrap-command-context.ts +27 -1
  190. package/src/runtime/bootstrap-command-parts.ts +41 -10
  191. package/src/runtime/bootstrap-core.ts +47 -15
  192. package/src/runtime/bootstrap-hook-bridge.ts +7 -0
  193. package/src/runtime/bootstrap-shell.ts +48 -17
  194. package/src/runtime/bootstrap.ts +129 -22
  195. package/src/runtime/code-index-services.ts +135 -0
  196. package/src/runtime/legacy-daemon-migration.ts +516 -0
  197. package/src/runtime/memory-fold.ts +26 -0
  198. package/src/runtime/onboarding/derivation.ts +7 -2
  199. package/src/runtime/onboarding/snapshot.ts +27 -1
  200. package/src/runtime/onboarding/types.ts +31 -1
  201. package/src/runtime/operator-token-cleanup.ts +82 -1
  202. package/src/runtime/orchestrator-core-services.ts +59 -0
  203. package/src/runtime/process-lifecycle.ts +3 -1
  204. package/src/runtime/resume-notice.ts +209 -0
  205. package/src/runtime/services.ts +98 -46
  206. package/src/runtime/session-inbound-inputs.ts +252 -0
  207. package/src/runtime/session-spine-transport.ts +64 -0
  208. package/src/runtime/terminal-output-guard.ts +15 -8
  209. package/src/runtime/ui-services.ts +27 -3
  210. package/src/runtime/workstream-services.ts +327 -0
  211. package/src/runtime/wrfc-persistence.ts +124 -17
  212. package/src/shell/blocking-input.ts +68 -4
  213. package/src/shell/recovery-input-helpers.ts +170 -1
  214. package/src/shell/ui-openers.ts +171 -26
  215. package/src/utils/format-duration.ts +8 -18
  216. package/src/utils/splash-lines.ts +1 -1
  217. package/src/utils/terminal-width.ts +52 -0
  218. package/src/version.ts +1 -1
  219. package/src/panels/agent-inspector-panel.ts +0 -786
  220. package/src/panels/approval-panel.ts +0 -252
  221. package/src/panels/automation-control-panel.ts +0 -479
  222. package/src/panels/cockpit-panel.ts +0 -481
  223. package/src/panels/cockpit-read-model.ts +0 -238
  224. package/src/panels/communication-panel.ts +0 -256
  225. package/src/panels/control-plane-panel.ts +0 -470
  226. package/src/panels/debug-panel.ts +0 -615
  227. package/src/panels/docs-panel.ts +0 -384
  228. package/src/panels/eval-panel.ts +0 -627
  229. package/src/panels/file-explorer-panel.ts +0 -673
  230. package/src/panels/file-preview-panel.ts +0 -517
  231. package/src/panels/hooks-panel.ts +0 -401
  232. package/src/panels/incident-review-panel.ts +0 -406
  233. package/src/panels/intelligence-panel.ts +0 -383
  234. package/src/panels/knowledge-graph-panel.ts +0 -515
  235. package/src/panels/marketplace-panel.ts +0 -399
  236. package/src/panels/memory-panel.ts +0 -558
  237. package/src/panels/ops-control-panel.ts +0 -329
  238. package/src/panels/ops-strategy-panel.ts +0 -321
  239. package/src/panels/orchestration-panel.ts +0 -465
  240. package/src/panels/panel-list-panel.ts +0 -566
  241. package/src/panels/plan-dashboard-panel.ts +0 -707
  242. package/src/panels/policy-panel.ts +0 -517
  243. package/src/panels/project-planning-panel.ts +0 -731
  244. package/src/panels/provider-health-panel.ts +0 -678
  245. package/src/panels/provider-health-tracker.ts +0 -310
  246. package/src/panels/provider-health-views.ts +0 -567
  247. package/src/panels/qr-panel.ts +0 -280
  248. package/src/panels/remote-panel.ts +0 -534
  249. package/src/panels/routes-panel.ts +0 -241
  250. package/src/panels/sandbox-panel.ts +0 -456
  251. package/src/panels/security-panel.ts +0 -447
  252. package/src/panels/services-panel.ts +0 -329
  253. package/src/panels/session-browser-panel.ts +0 -496
  254. package/src/panels/settings-sync-panel.ts +0 -398
  255. package/src/panels/subscription-panel.ts +0 -342
  256. package/src/panels/symbol-outline-panel.ts +0 -619
  257. package/src/panels/system-messages-panel.ts +0 -364
  258. package/src/panels/tasks-panel.ts +0 -608
  259. package/src/panels/thinking-panel.ts +0 -333
  260. package/src/panels/tool-inspector-panel.ts +0 -537
  261. package/src/panels/work-plan-panel.ts +0 -540
  262. package/src/panels/worktree-panel.ts +0 -360
  263. package/src/panels/wrfc-panel.ts +0 -790
  264. package/src/renderer/agent-detail-modal.ts +0 -466
  265. package/src/renderer/live-tail-modal.ts +0 -156
  266. package/src/renderer/process-modal.ts +0 -671
  267. package/src/renderer/qr-renderer.ts +0 -120
@@ -1,241 +0,0 @@
1
- import type { Line } from '../types/grid.ts';
2
- import { createEmptyLine } from '../types/grid.ts';
3
- import { ScrollableListPanel } from './scrollable-list-panel.ts';
4
- import type { PanelIntegrationContext } from './types.ts';
5
- import { SessionBrowserPanel } from './session-browser-panel.ts';
6
- import type { UiReadModel, UiRoutesSnapshot } from '../runtime/ui-read-models.ts';
7
- import { truncateDisplay } from '../utils/terminal-width.ts';
8
- import {
9
- buildDetailBlock,
10
- buildEmptyState,
11
- buildKeyValueLine,
12
- buildKeyboardHints,
13
- buildPanelLine,
14
- buildPanelWorkspace,
15
- buildStatusPill,
16
- DEFAULT_PANEL_PALETTE,
17
- type PanelPalette,
18
- } from './polish.ts';
19
-
20
- // Base chrome only — title band, state colors, and text tokens all come
21
- // straight from DEFAULT_PANEL_PALETTE (WO-002).
22
- const C = DEFAULT_PANEL_PALETTE;
23
-
24
- function formatTime(value?: number): string {
25
- if (!value) return 'n/a';
26
- return new Date(value).toLocaleString();
27
- }
28
-
29
- type RouteBinding = UiRoutesSnapshot['bindings'][number];
30
-
31
- // Set by handleInput (enter/c) and consumed on the very next
32
- // handlePanelIntegrationAction dispatch of that same key — handleInput has
33
- // no access to the panelManager.
34
- type PendingRouteAction =
35
- | { readonly kind: 'open-session'; readonly sessionId: string }
36
- | { readonly kind: 'open-communication' };
37
-
38
- export class RoutesPanel extends ScrollableListPanel<RouteBinding> {
39
- private readonly readModel?: UiReadModel<UiRoutesSnapshot>;
40
- private readonly unsub: (() => void) | null;
41
- private pendingAction: PendingRouteAction | null = null;
42
-
43
- public constructor(readModel?: UiReadModel<UiRoutesSnapshot>) {
44
- super('routes', 'Routes', 'R', 'runtime-ops');
45
- this.showSelectionGutter = true; // I5: non-color selection affordance
46
- this.filterEnabled = true;
47
- this.filterLabel = 'Filter routes';
48
- this.readModel = readModel;
49
- this.unsub = readModel ? readModel.subscribe(() => this.markDirty()) : null;
50
- }
51
-
52
- public override handleInput(key: string): boolean {
53
- if (!this.filterActive) {
54
- // Enter jumps to the session browser focused on this binding's session
55
- // — a direct panel jump instead of a printed slash-command signpost.
56
- if (key === 'enter' || key === 'return') {
57
- const selected = this.getSelectedItem();
58
- if (selected?.sessionId) {
59
- this.pendingAction = { kind: 'open-session', sessionId: selected.sessionId };
60
- return true;
61
- }
62
- return false;
63
- }
64
- // c opens the communication panel to inspect routed message flow.
65
- if (key === 'c') {
66
- this.pendingAction = { kind: 'open-communication' };
67
- return true;
68
- }
69
- }
70
- return super.handleInput(key);
71
- }
72
-
73
- public handlePanelIntegrationAction(_key: string, ctx: PanelIntegrationContext): boolean {
74
- if (!this.pendingAction) return false;
75
- const action = this.pendingAction;
76
- this.pendingAction = null;
77
- if (action.kind === 'open-communication') {
78
- ctx.panelManager.open('communication');
79
- return true;
80
- }
81
- const panel = ctx.panelManager.open('sessions');
82
- if (panel instanceof SessionBrowserPanel) {
83
- panel.focusSession(action.sessionId);
84
- }
85
- return true;
86
- }
87
-
88
- protected override filterMatches(binding: RouteBinding, q: string): boolean {
89
- return binding.surfaceKind.toLowerCase().includes(q)
90
- || (binding.title ?? '').toLowerCase().includes(q)
91
- || binding.externalId.toLowerCase().includes(q)
92
- || (binding.sessionId ?? '').toLowerCase().includes(q)
93
- || (binding.runId ?? '').toLowerCase().includes(q);
94
- }
95
-
96
- public override onDestroy(): void {
97
- this.unsub?.();
98
- }
99
-
100
- protected override getPalette(): PanelPalette {
101
- return C;
102
- }
103
-
104
- protected getItems(): readonly RouteBinding[] {
105
- if (!this.readModel) return [];
106
- return this.readModel.getSnapshot().bindings;
107
- }
108
-
109
- protected renderItem(binding: RouteBinding, _index: number, selected: boolean, width: number): Line {
110
- const bg = selected ? C.selectBg : undefined;
111
- return buildPanelLine(width, [
112
- [' ', C.label, bg],
113
- [binding.surfaceKind.padEnd(9), C.info, bg],
114
- [` ${truncateDisplay(binding.title ?? binding.externalId, 22).padEnd(22)}`, C.value, bg],
115
- ...buildStatusPill(binding.sessionId ? 'good' : 'warn', ` ${truncateDisplay(binding.sessionId ?? binding.runId ?? 'unbound', 18).padEnd(18)}`, { bg }),
116
- [` ${truncateDisplay(formatTime(binding.lastSeenAt), Math.max(0, width - 54))}`, C.dim, bg],
117
- ]);
118
- }
119
-
120
- protected override getEmptyStateMessage(): string {
121
- return ' No route bindings recorded.';
122
- }
123
-
124
- protected override getEmptyStateActions(): Array<{ command: string; summary: string }> {
125
- return [
126
- { command: '/schedule list', summary: 'run jobs and triggers that create route bindings' },
127
- { command: '/communication', summary: 'inspect routed communication once a surface is active' },
128
- ];
129
- }
130
-
131
- public render(width: number, height: number): Line[] {
132
- const intro = 'External route bindings that preserve thread, session, and reply context across Slack, Discord, ntfy, webhook, web, and TUI surfaces.';
133
-
134
- if (!this.readModel) {
135
- const workspace = buildPanelWorkspace(width, height, {
136
- title: 'Route Bindings',
137
- intro,
138
- sections: [{
139
- lines: buildEmptyState(
140
- width,
141
- ' Runtime store not wired.',
142
- 'This panel needs the shared runtime store to inspect omnichannel route bindings.',
143
- [{ command: '/communication', summary: 'review communication posture while route state is unavailable' }],
144
- C,
145
- ),
146
- }],
147
- palette: C,
148
- });
149
- while (workspace.length < height) workspace.push(createEmptyLine(width));
150
- return workspace;
151
- }
152
-
153
- const snapshot = this.readModel.getSnapshot();
154
- const bindings = this.getItems();
155
- const surfaceEntries = Object.entries(snapshot.bindingIdsBySurface)
156
- .filter(([, ids]) => ids.length > 0)
157
- .sort((a, b) => b[1].length - a[1].length || a[0].localeCompare(b[0]));
158
-
159
- const headerLines: Line[] = [
160
- buildKeyValueLine(width, [
161
- { label: 'bindings', value: String(snapshot.totalBindings), valueColor: snapshot.totalBindings > 0 ? C.info : C.dim },
162
- { label: 'active', value: String(snapshot.activeBindingIds.length), valueColor: snapshot.activeBindingIds.length > 0 ? C.good : C.dim },
163
- { label: 'resolved', value: String(snapshot.totalResolved), valueColor: snapshot.totalResolved > 0 ? C.good : C.dim },
164
- { label: 'failures', value: String(snapshot.totalFailures), valueColor: snapshot.totalFailures > 0 ? C.bad : C.dim },
165
- ], C),
166
- ];
167
-
168
- if (bindings.length === 0) {
169
- return this.renderList(width, height, {
170
- title: 'Route Bindings',
171
- header: headerLines,
172
- emptyMessage: ' No route bindings recorded.',
173
- });
174
- }
175
-
176
- this.clampSelection();
177
- // Detail must describe the row the (possibly filtered) list highlights —
178
- // getItems() would desync under an applied filter, and a filter that
179
- // matches nothing leaves no selection at all.
180
- const selected = this.getSelectedItem();
181
-
182
- const detailRows: Line[] = selected ? [
183
- buildPanelLine(width, [
184
- [' Binding: ', C.label],
185
- [selected.id, C.value],
186
- [' Surface: ', C.label],
187
- [selected.surfaceKind, C.info],
188
- ]),
189
- buildPanelLine(width, [
190
- [' External: ', C.label],
191
- [truncateDisplay(selected.externalId, 28), C.value],
192
- [' Kind: ', C.label],
193
- [selected.kind, C.dim],
194
- ]),
195
- buildPanelLine(width, [
196
- [' Session: ', C.label],
197
- [selected.sessionId ?? 'n/a', C.value],
198
- [' Run: ', C.label],
199
- [selected.runId ?? 'n/a', C.dim],
200
- ]),
201
- buildPanelLine(width, [
202
- [' Channel: ', C.label],
203
- [selected.channelId ?? 'n/a', C.dim],
204
- [' Thread: ', C.label],
205
- [selected.threadId ?? 'n/a', C.dim],
206
- ]),
207
- buildPanelLine(width, [
208
- [' Last seen: ', C.label],
209
- [formatTime(selected.lastSeenAt), C.dim],
210
- ]),
211
- ] : [];
212
-
213
- if (selected && surfaceEntries.length > 0) {
214
- detailRows.push(
215
- ...surfaceEntries.slice(0, 4).map(([surface, ids]) => buildPanelLine(width, [
216
- [' ', C.label],
217
- [surface.padEnd(10), C.info],
218
- [` ${String(ids.length)} binding(s)`, C.value],
219
- ])),
220
- );
221
- }
222
-
223
- const hints = this.filterActive
224
- ? [{ keys: 'type', label: 'filter' }, { keys: 'Enter', label: 'apply' }, { keys: 'Esc', label: 'clear' }]
225
- : [
226
- { keys: 'Up/Down', label: 'move' },
227
- ...(selected?.sessionId ? [{ keys: 'Enter', label: 'open session' }] : []),
228
- { keys: 'c', label: 'communication' },
229
- { keys: '/', label: 'filter' },
230
- ];
231
-
232
- return this.renderList(width, height, {
233
- title: 'Route Bindings',
234
- header: headerLines,
235
- footer: [
236
- ...(selected ? buildDetailBlock(width, `Binding · ${selected.surfaceKind}`, detailRows, C) : []),
237
- buildKeyboardHints(width, hints, C),
238
- ],
239
- });
240
- }
241
- }