@jmoyers/harness 0.1.10 → 0.1.20

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 (239) hide show
  1. package/README.md +31 -35
  2. package/package.json +31 -11
  3. package/packages/harness-ai/src/anthropic-protocol.ts +68 -68
  4. package/packages/harness-ai/src/stream-text.ts +13 -91
  5. package/packages/harness-ui/src/frame-primitives.ts +158 -0
  6. package/packages/harness-ui/src/index.ts +18 -0
  7. package/packages/harness-ui/src/interaction/conversation-input-forwarder.ts +221 -0
  8. package/packages/harness-ui/src/interaction/conversation-selection-input.ts +213 -0
  9. package/packages/harness-ui/src/interaction/global-shortcut-input.ts +172 -0
  10. package/{src/ui → packages/harness-ui/src/interaction}/input-preflight.ts +10 -12
  11. package/{src/ui → packages/harness-ui/src/interaction}/input-token-router.ts +120 -69
  12. package/packages/harness-ui/src/interaction/input.ts +420 -0
  13. package/packages/harness-ui/src/interaction/left-nav-input.ts +166 -0
  14. package/{src/ui → packages/harness-ui/src/interaction}/main-pane-pointer-input.ts +91 -23
  15. package/{src/ui → packages/harness-ui/src/interaction}/pointer-routing-input.ts +112 -48
  16. package/packages/harness-ui/src/interaction/rail-pointer-input.ts +62 -0
  17. package/packages/harness-ui/src/interaction/repository-fold-input.ts +118 -0
  18. package/packages/harness-ui/src/kit.ts +476 -0
  19. package/packages/harness-ui/src/layout.ts +238 -0
  20. package/{src/ui/modals/manager.ts → packages/harness-ui/src/modal-manager.ts} +94 -64
  21. package/{src/ui → packages/harness-ui/src}/screen.ts +53 -26
  22. package/packages/harness-ui/src/surface.ts +252 -0
  23. package/packages/harness-ui/src/text-layout.ts +210 -0
  24. package/packages/nim-core/src/contracts.ts +239 -0
  25. package/packages/nim-core/src/event-store.ts +299 -0
  26. package/packages/nim-core/src/events.ts +53 -0
  27. package/packages/nim-core/src/index.ts +9 -0
  28. package/packages/nim-core/src/provider-router.ts +129 -0
  29. package/packages/nim-core/src/providers/anthropic-driver.ts +291 -0
  30. package/packages/nim-core/src/runtime-factory.ts +49 -0
  31. package/packages/nim-core/src/runtime.ts +1797 -0
  32. package/packages/nim-core/src/session-store.ts +516 -0
  33. package/packages/nim-core/src/telemetry.ts +48 -0
  34. package/packages/nim-test-tui/src/index.ts +150 -0
  35. package/packages/nim-ui-core/src/index.ts +1 -0
  36. package/packages/nim-ui-core/src/projection.ts +87 -0
  37. package/scripts/codex-live-mux-runtime.ts +2 -3721
  38. package/scripts/control-plane-daemon.ts +24 -2
  39. package/scripts/harness-bin.js +5 -0
  40. package/scripts/harness-commands.ts +300 -0
  41. package/scripts/harness-runtime.ts +82 -0
  42. package/scripts/harness.ts +33 -3007
  43. package/scripts/nim-tui-smoke.ts +748 -0
  44. package/src/cli/auth/runtime.ts +948 -0
  45. package/src/cli/default-gateway-pointer.ts +193 -0
  46. package/src/cli/gateway/runtime.ts +1872 -0
  47. package/src/cli/parsing/flags.ts +23 -0
  48. package/src/cli/parsing/session.ts +42 -0
  49. package/src/cli/runtime/context.ts +193 -0
  50. package/src/cli/runtime-app/application.ts +392 -0
  51. package/src/cli/runtime-infra/gateway-control.ts +729 -0
  52. package/{scripts/harness-inspector.ts → src/cli/workflows/inspector.ts} +14 -11
  53. package/src/cli/workflows/runtime.ts +965 -0
  54. package/src/clients/tui/left-rail-interactions.ts +519 -0
  55. package/src/clients/tui/main-pane-interactions.ts +509 -0
  56. package/src/clients/tui/modal-input-routing.ts +71 -0
  57. package/src/clients/tui/render-snapshot-adapter.ts +88 -0
  58. package/src/clients/web/synced-selectors.ts +132 -0
  59. package/src/codex/live-session.ts +82 -29
  60. package/src/config/config-core.ts +361 -10
  61. package/src/config/harness-paths.ts +4 -7
  62. package/src/config/harness-runtime-migration.ts +142 -19
  63. package/src/config/harness.config.template.jsonc +33 -0
  64. package/src/config/secrets-core.ts +92 -4
  65. package/src/control-plane/agent-realtime-api.ts +82 -427
  66. package/src/control-plane/prompt/thread-title-namer.ts +49 -23
  67. package/src/control-plane/session-summary.ts +10 -81
  68. package/src/control-plane/status/reducer-base.ts +12 -12
  69. package/src/control-plane/status/reducers/claude-status-reducer.ts +3 -3
  70. package/src/control-plane/status/reducers/codex-status-reducer.ts +4 -4
  71. package/src/control-plane/status/reducers/cursor-status-reducer.ts +3 -3
  72. package/src/control-plane/stream-client.ts +12 -2
  73. package/src/control-plane/stream-command-parser.ts +83 -143
  74. package/src/control-plane/stream-protocol.ts +53 -37
  75. package/src/control-plane/stream-server-background.ts +18 -2
  76. package/src/control-plane/stream-server-command.ts +376 -69
  77. package/src/control-plane/stream-server-session-runtime.ts +3 -2
  78. package/src/control-plane/stream-server.ts +943 -80
  79. package/src/control-plane/stream-session-runtime-types.ts +41 -0
  80. package/src/{mux/live-mux/control-plane-records.ts → core/contracts/records.ts} +24 -97
  81. package/src/core/state/observed-stream-cursor.ts +43 -0
  82. package/src/core/state/synced-observed-state.ts +273 -0
  83. package/src/core/store/harness-synced-store.ts +81 -0
  84. package/src/diff/budget.ts +136 -0
  85. package/src/diff/build.ts +289 -0
  86. package/src/diff/chunker.ts +146 -0
  87. package/src/diff/git-invoke.ts +315 -0
  88. package/src/diff/git-parse.ts +472 -0
  89. package/src/diff/hash.ts +70 -0
  90. package/src/diff/index.ts +24 -0
  91. package/src/diff/normalize.ts +134 -0
  92. package/src/diff/types.ts +178 -0
  93. package/src/diff-ui/args.ts +346 -0
  94. package/src/diff-ui/commands.ts +123 -0
  95. package/src/diff-ui/finder.ts +94 -0
  96. package/src/diff-ui/highlight.ts +127 -0
  97. package/src/diff-ui/index.ts +2 -0
  98. package/src/diff-ui/model.ts +141 -0
  99. package/src/diff-ui/pager.ts +412 -0
  100. package/src/diff-ui/render.ts +337 -0
  101. package/src/diff-ui/runtime.ts +379 -0
  102. package/src/diff-ui/state.ts +224 -0
  103. package/src/diff-ui/types.ts +236 -0
  104. package/src/domain/conversations.ts +11 -7
  105. package/src/domain/workspace.ts +76 -4
  106. package/src/mux/control-plane-op-queue.ts +93 -7
  107. package/src/mux/conversation-rail.ts +28 -71
  108. package/src/mux/dual-pane-core.ts +13 -13
  109. package/src/mux/harness-core-ui.ts +313 -42
  110. package/src/mux/input-shortcuts.ts +22 -112
  111. package/src/mux/keybinding-catalog.ts +340 -0
  112. package/src/mux/keybinding-registry.ts +103 -0
  113. package/src/mux/live-mux/command-menu-open-in.ts +280 -0
  114. package/src/mux/live-mux/command-menu.ts +167 -4
  115. package/src/mux/live-mux/conversation-state.ts +13 -0
  116. package/src/mux/live-mux/directory-resolution.ts +1 -1
  117. package/src/mux/live-mux/git-parsing.ts +16 -0
  118. package/src/mux/live-mux/git-snapshot.ts +33 -2
  119. package/src/mux/live-mux/global-shortcut-handlers.ts +6 -0
  120. package/src/mux/live-mux/home-pane-drop.ts +1 -1
  121. package/src/mux/live-mux/home-pane-pointer.ts +10 -0
  122. package/src/mux/live-mux/input-forwarding.ts +59 -2
  123. package/src/mux/live-mux/left-nav-activation.ts +124 -7
  124. package/src/mux/live-mux/left-nav.ts +35 -0
  125. package/src/mux/live-mux/link-click.ts +292 -0
  126. package/src/mux/live-mux/modal-command-menu-handler.ts +46 -9
  127. package/src/mux/live-mux/modal-conversation-handlers.ts +5 -1
  128. package/src/mux/live-mux/modal-input-reducers.ts +106 -8
  129. package/src/mux/live-mux/modal-overlays.ts +210 -31
  130. package/src/mux/live-mux/modal-pointer.ts +3 -7
  131. package/src/mux/live-mux/modal-prompt-handlers.ts +107 -1
  132. package/src/mux/live-mux/modal-release-notes-handler.ts +111 -0
  133. package/src/mux/live-mux/modal-task-editor-handler.ts +16 -11
  134. package/src/mux/live-mux/pointer-routing.ts +5 -2
  135. package/src/mux/live-mux/project-pane-pointer.ts +8 -0
  136. package/src/mux/live-mux/rail-layout.ts +33 -30
  137. package/src/mux/live-mux/release-notes.ts +383 -0
  138. package/src/mux/live-mux/render-trace-analysis.ts +52 -7
  139. package/src/mux/live-mux/repository-folding.ts +3 -0
  140. package/src/mux/live-mux/selection.ts +0 -4
  141. package/src/mux/live-mux/session-diagnostics-paths.ts +21 -0
  142. package/src/mux/project-pane-github-review.ts +271 -0
  143. package/src/mux/render-frame.ts +4 -0
  144. package/src/mux/runtime-app/codex-live-mux-runtime.ts +5191 -0
  145. package/src/mux/task-composer.ts +21 -14
  146. package/src/mux/task-focused-pane.ts +118 -117
  147. package/src/mux/task-screen-keybindings.ts +19 -82
  148. package/src/mux/workspace-rail-model.ts +270 -104
  149. package/src/mux/workspace-rail.ts +45 -22
  150. package/src/pty/session-broker.ts +1 -1
  151. package/{scripts → src/recording}/terminal-recording-gif-lib.ts +2 -2
  152. package/src/services/control-plane.ts +50 -32
  153. package/src/services/conversation-lifecycle.ts +118 -87
  154. package/src/services/conversation-startup-hydration.ts +20 -12
  155. package/src/services/directory-hydration.ts +21 -16
  156. package/src/services/event-persistence.ts +7 -0
  157. package/src/services/left-rail-pointer-handler.ts +329 -0
  158. package/src/services/mux-ui-state-persistence.ts +5 -1
  159. package/src/services/recording.ts +34 -26
  160. package/src/services/runtime-command-menu-agent-tools.ts +1 -1
  161. package/src/services/runtime-control-actions.ts +79 -61
  162. package/src/services/runtime-control-plane-ops.ts +122 -83
  163. package/src/services/runtime-conversation-actions.ts +40 -26
  164. package/src/services/runtime-conversation-activation.ts +82 -30
  165. package/src/services/runtime-conversation-starter.ts +80 -48
  166. package/src/services/runtime-conversation-title-edit.ts +91 -80
  167. package/src/services/runtime-envelope-handler.ts +107 -105
  168. package/src/services/runtime-git-state.ts +42 -29
  169. package/src/services/runtime-layout-resize.ts +3 -1
  170. package/src/services/runtime-left-rail-render.ts +99 -63
  171. package/src/services/runtime-nim-cli-session.ts +438 -0
  172. package/src/services/runtime-nim-session.ts +705 -0
  173. package/src/services/runtime-nim-tool-bridge.ts +141 -0
  174. package/src/services/runtime-observed-event-projection-pipeline.ts +45 -0
  175. package/src/services/runtime-process-wiring.ts +29 -36
  176. package/src/services/runtime-project-pane-github-review-cache.ts +164 -0
  177. package/src/services/runtime-render-flush.ts +63 -70
  178. package/src/services/runtime-render-lifecycle.ts +65 -64
  179. package/src/services/runtime-render-orchestrator.ts +55 -45
  180. package/src/services/runtime-render-pipeline.ts +106 -103
  181. package/src/services/runtime-render-state.ts +62 -49
  182. package/src/services/runtime-repository-actions.ts +97 -70
  183. package/src/services/runtime-right-pane-render.ts +80 -53
  184. package/src/services/runtime-shutdown.ts +38 -35
  185. package/src/services/runtime-stream-subscriptions.ts +35 -27
  186. package/src/services/runtime-task-composer-persistence.ts +71 -59
  187. package/src/services/runtime-task-composer-snapshot.ts +14 -0
  188. package/src/services/runtime-task-editor-actions.ts +46 -29
  189. package/src/services/runtime-task-pane-actions.ts +220 -134
  190. package/src/services/runtime-task-pane-shortcuts.ts +323 -123
  191. package/src/services/runtime-workspace-observed-effect-queue.ts +25 -0
  192. package/src/services/runtime-workspace-observed-events.ts +33 -184
  193. package/src/services/runtime-workspace-observed-transition-policy.ts +228 -0
  194. package/src/services/session-diagnostics-store.ts +217 -0
  195. package/src/services/startup-background-resume.ts +26 -21
  196. package/src/services/startup-orchestrator.ts +16 -13
  197. package/src/services/startup-paint-tracker.ts +29 -21
  198. package/src/services/startup-persisted-conversation-queue.ts +19 -13
  199. package/src/services/startup-settled-gate.ts +25 -15
  200. package/src/services/startup-shutdown.ts +18 -22
  201. package/src/services/startup-state-hydration.ts +44 -34
  202. package/src/services/startup-visibility.ts +12 -4
  203. package/src/services/task-pane-selection-actions.ts +89 -72
  204. package/src/services/task-planning-hydration.ts +24 -18
  205. package/src/services/task-planning-observed-events.ts +50 -52
  206. package/src/services/workspace-observed-events.ts +66 -63
  207. package/src/storage/storage-lifecycle-core.ts +438 -0
  208. package/src/store/control-plane-store-normalize.ts +33 -242
  209. package/src/store/control-plane-store-types.ts +1 -35
  210. package/src/store/control-plane-store.ts +396 -56
  211. package/src/store/event-store.ts +397 -3
  212. package/src/terminal/snapshot-oracle.ts +207 -94
  213. package/src/ui/mux-theme.ts +112 -8
  214. package/src/ui/panes/home-gridfire.ts +40 -31
  215. package/src/ui/panes/home.ts +10 -2
  216. package/src/ui/panes/nim.ts +315 -0
  217. package/src/mux/live-mux/actions-task.ts +0 -115
  218. package/src/mux/live-mux/left-rail-actions.ts +0 -118
  219. package/src/mux/live-mux/left-rail-conversation-click.ts +0 -82
  220. package/src/mux/live-mux/left-rail-pointer.ts +0 -74
  221. package/src/mux/live-mux/task-pane-shortcuts.ts +0 -206
  222. package/src/services/runtime-directory-actions.ts +0 -164
  223. package/src/services/runtime-input-pipeline.ts +0 -50
  224. package/src/services/runtime-input-router.ts +0 -189
  225. package/src/services/runtime-main-pane-input.ts +0 -230
  226. package/src/services/runtime-modal-input.ts +0 -119
  227. package/src/services/runtime-navigation-input.ts +0 -197
  228. package/src/services/runtime-rail-input.ts +0 -278
  229. package/src/services/runtime-task-pane.ts +0 -62
  230. package/src/services/runtime-workspace-actions.ts +0 -158
  231. package/src/ui/conversation-input-forwarder.ts +0 -114
  232. package/src/ui/conversation-selection-input.ts +0 -103
  233. package/src/ui/global-shortcut-input.ts +0 -89
  234. package/src/ui/input.ts +0 -238
  235. package/src/ui/kit.ts +0 -509
  236. package/src/ui/left-nav-input.ts +0 -80
  237. package/src/ui/left-rail-pointer-input.ts +0 -148
  238. package/src/ui/repository-fold-input.ts +0 -91
  239. package/src/ui/surface.ts +0 -224
@@ -2,9 +2,22 @@ import { basename } from 'node:path';
2
2
  import { padOrTrimDisplay } from './dual-pane-core.ts';
3
3
  import { buildProjectTreeLines } from './project-tree.ts';
4
4
  import { wrapTextForColumns } from '../terminal/snapshot-oracle.ts';
5
- import { formatUiButton } from '../ui/kit.ts';
5
+ import { UiKit } from '../../packages/harness-ui/src/kit.ts';
6
+ import {
7
+ buildProjectPaneGitHubReviewLines,
8
+ type ProjectPaneGitHubReviewComment,
9
+ type ProjectPaneGitHubReviewSummary,
10
+ type ProjectPaneGitHubReviewThread,
11
+ type ProjectPaneGitHubToggleAction,
12
+ } from './project-pane-github-review.ts';
6
13
 
7
- export type ProjectPaneAction = 'conversation.new' | 'project.close';
14
+ const UI_KIT = new UiKit();
15
+
16
+ export type ProjectPaneAction =
17
+ | 'conversation.new'
18
+ | 'project.close'
19
+ | 'project.github.refresh'
20
+ | ProjectPaneGitHubToggleAction;
8
21
  export type TaskStatus = 'draft' | 'ready' | 'in-progress' | 'completed';
9
22
  export type TaskPaneAction =
10
23
  | 'task.create'
@@ -23,6 +36,7 @@ export interface ProjectPaneSnapshot {
23
36
  readonly directoryId: string;
24
37
  readonly path: string;
25
38
  readonly lines: readonly string[];
39
+ readonly actionBySourceLineIndex: Readonly<Record<number, ProjectPaneAction>>;
26
40
  readonly actionLineIndexByKind: {
27
41
  readonly conversationNew: number;
28
42
  readonly projectClose: number;
@@ -47,7 +61,7 @@ export interface TaskPaneTaskRecord {
47
61
  readonly taskId: string;
48
62
  readonly repositoryId: string | null;
49
63
  readonly title: string;
50
- readonly description: string;
64
+ readonly body: string;
51
65
  readonly status: TaskStatus;
52
66
  readonly orderIndex: number;
53
67
  readonly completedAt: string | null;
@@ -81,103 +95,107 @@ export interface TaskPaneView {
81
95
  readonly top: number;
82
96
  }
83
97
 
84
- export const PROJECT_PANE_NEW_CONVERSATION_BUTTON_LABEL = formatUiButton({
98
+ export const PROJECT_PANE_NEW_CONVERSATION_BUTTON_LABEL = UI_KIT.formatButton({
85
99
  label: 'new thread',
86
100
  prefixIcon: '+',
87
101
  });
88
- export const PROJECT_PANE_CLOSE_PROJECT_BUTTON_LABEL = formatUiButton({
102
+ export const PROJECT_PANE_CLOSE_PROJECT_BUTTON_LABEL = UI_KIT.formatButton({
89
103
  label: 'close project',
90
104
  prefixIcon: '<',
91
105
  });
92
- export const TASKS_PANE_ADD_TASK_BUTTON_LABEL = formatUiButton({
106
+ const PROJECT_PANE_REFRESH_GITHUB_BUTTON_LABEL = UI_KIT.formatButton({
107
+ label: 'refresh review',
108
+ prefixIcon: 'R',
109
+ });
110
+ export const TASKS_PANE_ADD_TASK_BUTTON_LABEL = UI_KIT.formatButton({
93
111
  label: 'add task',
94
112
  prefixIcon: '+',
95
113
  });
96
- export const TASKS_PANE_ADD_REPOSITORY_BUTTON_LABEL = formatUiButton({
114
+ export const TASKS_PANE_ADD_REPOSITORY_BUTTON_LABEL = UI_KIT.formatButton({
97
115
  label: 'add repository',
98
116
  prefixIcon: '+',
99
117
  });
100
- export const TASKS_PANE_EDIT_REPOSITORY_BUTTON_LABEL = formatUiButton({
118
+ export const TASKS_PANE_EDIT_REPOSITORY_BUTTON_LABEL = UI_KIT.formatButton({
101
119
  label: 'edit repository',
102
120
  prefixIcon: 'e',
103
121
  });
104
- export const TASKS_PANE_ARCHIVE_REPOSITORY_BUTTON_LABEL = formatUiButton({
122
+ export const TASKS_PANE_ARCHIVE_REPOSITORY_BUTTON_LABEL = UI_KIT.formatButton({
105
123
  label: 'archive repository',
106
124
  prefixIcon: 'x',
107
125
  });
108
- export const TASKS_PANE_EDIT_TASK_BUTTON_LABEL = formatUiButton({
126
+ export const TASKS_PANE_EDIT_TASK_BUTTON_LABEL = UI_KIT.formatButton({
109
127
  label: 'edit task',
110
128
  prefixIcon: 'e',
111
129
  });
112
- export const TASKS_PANE_DELETE_TASK_BUTTON_LABEL = formatUiButton({
130
+ export const TASKS_PANE_DELETE_TASK_BUTTON_LABEL = UI_KIT.formatButton({
113
131
  label: 'delete task',
114
132
  prefixIcon: 'x',
115
133
  });
116
- export const TASKS_PANE_READY_TASK_BUTTON_LABEL = formatUiButton({
134
+ export const TASKS_PANE_READY_TASK_BUTTON_LABEL = UI_KIT.formatButton({
117
135
  label: 'mark ready',
118
136
  prefixIcon: 'r',
119
137
  });
120
- export const TASKS_PANE_DRAFT_TASK_BUTTON_LABEL = formatUiButton({
138
+ export const TASKS_PANE_DRAFT_TASK_BUTTON_LABEL = UI_KIT.formatButton({
121
139
  label: 'mark draft',
122
140
  prefixIcon: 'd',
123
141
  });
124
- export const TASKS_PANE_COMPLETE_TASK_BUTTON_LABEL = formatUiButton({
142
+ export const TASKS_PANE_COMPLETE_TASK_BUTTON_LABEL = UI_KIT.formatButton({
125
143
  label: 'mark complete',
126
144
  prefixIcon: 'c',
127
145
  });
128
- export const TASKS_PANE_REORDER_UP_BUTTON_LABEL = formatUiButton({
146
+ export const TASKS_PANE_REORDER_UP_BUTTON_LABEL = UI_KIT.formatButton({
129
147
  label: 'move up',
130
148
  prefixIcon: '^',
131
149
  });
132
- export const TASKS_PANE_REORDER_DOWN_BUTTON_LABEL = formatUiButton({
150
+ export const TASKS_PANE_REORDER_DOWN_BUTTON_LABEL = UI_KIT.formatButton({
133
151
  label: 'move down',
134
152
  prefixIcon: 'v',
135
153
  });
136
- export const TASKS_PANE_FOOTER_EDIT_BUTTON_LABEL = formatUiButton({
154
+ export const TASKS_PANE_FOOTER_EDIT_BUTTON_LABEL = UI_KIT.formatButton({
137
155
  label: 'edit ^E',
138
156
  prefixIcon: '✎',
139
157
  });
140
- export const TASKS_PANE_FOOTER_DELETE_BUTTON_LABEL = formatUiButton({
158
+ export const TASKS_PANE_FOOTER_DELETE_BUTTON_LABEL = UI_KIT.formatButton({
141
159
  label: 'delete ^?',
142
160
  prefixIcon: '⌫',
143
161
  });
144
- export const TASKS_PANE_FOOTER_COMPLETE_BUTTON_LABEL = formatUiButton({
162
+ export const TASKS_PANE_FOOTER_COMPLETE_BUTTON_LABEL = UI_KIT.formatButton({
145
163
  label: 'complete ^S',
146
164
  prefixIcon: '✓',
147
165
  });
148
- export const TASKS_PANE_FOOTER_DRAFT_BUTTON_LABEL = formatUiButton({
166
+ export const TASKS_PANE_FOOTER_DRAFT_BUTTON_LABEL = UI_KIT.formatButton({
149
167
  label: 'draft ^R',
150
168
  prefixIcon: '◇',
151
169
  });
152
- export const TASKS_PANE_FOOTER_REPOSITORY_EDIT_BUTTON_LABEL = formatUiButton({
170
+ export const TASKS_PANE_FOOTER_REPOSITORY_EDIT_BUTTON_LABEL = UI_KIT.formatButton({
153
171
  label: 'repo edit E',
154
172
  prefixIcon: '✎',
155
173
  });
156
- export const TASKS_PANE_FOOTER_REPOSITORY_ARCHIVE_BUTTON_LABEL = formatUiButton({
174
+ export const TASKS_PANE_FOOTER_REPOSITORY_ARCHIVE_BUTTON_LABEL = UI_KIT.formatButton({
157
175
  label: 'repo archive X',
158
176
  prefixIcon: '⌫',
159
177
  });
160
- export const CONVERSATION_EDIT_ARCHIVE_BUTTON_LABEL = formatUiButton({
178
+ export const CONVERSATION_EDIT_ARCHIVE_BUTTON_LABEL = UI_KIT.formatButton({
161
179
  label: 'archive thread',
162
180
  prefixIcon: 'x',
163
181
  });
164
- export const NEW_THREAD_MODAL_CODEX_BUTTON = formatUiButton({
182
+ export const NEW_THREAD_MODAL_CODEX_BUTTON = UI_KIT.formatButton({
165
183
  label: 'codex',
166
184
  prefixIcon: '◆',
167
185
  });
168
- export const NEW_THREAD_MODAL_CLAUDE_BUTTON = formatUiButton({
186
+ export const NEW_THREAD_MODAL_CLAUDE_BUTTON = UI_KIT.formatButton({
169
187
  label: 'claude',
170
188
  prefixIcon: '◇',
171
189
  });
172
- export const NEW_THREAD_MODAL_CURSOR_BUTTON = formatUiButton({
190
+ export const NEW_THREAD_MODAL_CURSOR_BUTTON = UI_KIT.formatButton({
173
191
  label: 'cursor',
174
192
  prefixIcon: '◈',
175
193
  });
176
- export const NEW_THREAD_MODAL_TERMINAL_BUTTON = formatUiButton({
194
+ export const NEW_THREAD_MODAL_TERMINAL_BUTTON = UI_KIT.formatButton({
177
195
  label: 'terminal',
178
196
  prefixIcon: '▣',
179
197
  });
180
- export const NEW_THREAD_MODAL_CRITIQUE_BUTTON = formatUiButton({
198
+ export const NEW_THREAD_MODAL_CRITIQUE_BUTTON = UI_KIT.formatButton({
181
199
  label: 'critique',
182
200
  prefixIcon: '▤',
183
201
  });
@@ -434,6 +452,96 @@ function normalizeTaskPaneRow(text: string, cols: number): string {
434
452
  return `│${padOrTrimDisplay(text, innerCols)}│`;
435
453
  }
436
454
 
455
+ function sanitizeInlineText(value: string): string {
456
+ return value.replace(/\s+/gu, ' ').trim();
457
+ }
458
+
459
+ function formatGitHubAuthor(login: string | null): string {
460
+ const normalized = login?.trim() ?? '';
461
+ return normalized.length > 0 ? `@${normalized}` : '@unknown';
462
+ }
463
+
464
+ function githubPrStateLabel(review: NonNullable<ProjectPaneGitHubReviewSummary['pr']>): string {
465
+ if (review.isDraft || review.state === 'draft') {
466
+ return 'draft';
467
+ }
468
+ if (review.state === 'merged') {
469
+ return 'merged';
470
+ }
471
+ if (review.state === 'closed') {
472
+ return 'closed';
473
+ }
474
+ return 'open';
475
+ }
476
+
477
+ function threadCommentCount(threads: readonly ProjectPaneGitHubReviewThread[]): number {
478
+ let count = 0;
479
+ for (const thread of threads) {
480
+ count += thread.comments.length;
481
+ }
482
+ return count;
483
+ }
484
+
485
+ function appendCommentDetails(
486
+ pushLine: (line: string, action?: ProjectPaneAction | null) => void,
487
+ comment: ProjectPaneGitHubReviewComment,
488
+ index: number,
489
+ ): void {
490
+ pushLine(
491
+ ` comment ${String(index + 1)} (${comment.commentId}) by ${formatGitHubAuthor(comment.authorLogin)}`,
492
+ );
493
+ pushLine(` created ${comment.createdAt}`);
494
+ if (comment.updatedAt !== comment.createdAt) {
495
+ pushLine(` updated ${comment.updatedAt}`);
496
+ }
497
+ if (comment.url !== null) {
498
+ pushLine(` url ${comment.url}`);
499
+ }
500
+ const bodyLines = comment.body.split(/\r?\n/gu);
501
+ pushLine(' body');
502
+ let wroteBodyLine = false;
503
+ for (const bodyLine of bodyLines) {
504
+ const trimmedEnd = bodyLine.trimEnd();
505
+ if (trimmedEnd.length === 0) {
506
+ continue;
507
+ }
508
+ pushLine(` ${trimmedEnd}`);
509
+ wroteBodyLine = true;
510
+ }
511
+ if (!wroteBodyLine) {
512
+ pushLine(' (empty)');
513
+ }
514
+ }
515
+
516
+ function appendThreadDetails(
517
+ pushLine: (line: string, action?: ProjectPaneAction | null) => void,
518
+ label: string,
519
+ threads: readonly ProjectPaneGitHubReviewThread[],
520
+ ): void {
521
+ pushLine(`${label} (${String(threads.length)})`);
522
+ if (threads.length === 0) {
523
+ pushLine(' (none)');
524
+ return;
525
+ }
526
+ for (const thread of threads) {
527
+ const parts = [
528
+ thread.isResolved ? 'resolved' : 'open',
529
+ ...(thread.isOutdated ? ['outdated'] : []),
530
+ ...(thread.resolvedByLogin === null
531
+ ? []
532
+ : [`resolved by ${formatGitHubAuthor(thread.resolvedByLogin)}`]),
533
+ ];
534
+ pushLine(`[thread ${thread.threadId}] ${parts.join(', ')}`);
535
+ if (thread.comments.length === 0) {
536
+ pushLine(' (no comments)');
537
+ continue;
538
+ }
539
+ for (let commentIndex = 0; commentIndex < thread.comments.length; commentIndex += 1) {
540
+ appendCommentDetails(pushLine, thread.comments[commentIndex]!, commentIndex);
541
+ }
542
+ }
543
+ }
544
+
437
545
  export function resolveGoldenModalSize(
438
546
  viewportCols: number,
439
547
  viewportRows: number,
@@ -461,23 +569,182 @@ export function resolveGoldenModalSize(
461
569
 
462
570
  export function buildProjectPaneSnapshot(directoryId: string, path: string): ProjectPaneSnapshot {
463
571
  const projectName = basename(path) || path;
464
- const actionLineIndexByKind = {
465
- conversationNew: 3,
466
- projectClose: 4,
467
- } as const;
572
+ const lines: string[] = [];
573
+ const actionBySourceLineIndex: Record<number, ProjectPaneAction> = {};
574
+ const pushLine = (line: string, action: ProjectPaneAction | null = null): number => {
575
+ const index = lines.length;
576
+ lines.push(line);
577
+ if (action !== null) {
578
+ actionBySourceLineIndex[index] = action;
579
+ }
580
+ return index;
581
+ };
582
+ pushLine(`project ${projectName}`);
583
+ pushLine(`path ${path}`);
584
+ pushLine('');
585
+ const conversationNewLineIndex = pushLine(
586
+ PROJECT_PANE_NEW_CONVERSATION_BUTTON_LABEL,
587
+ 'conversation.new',
588
+ );
589
+ const projectCloseLineIndex = pushLine(PROJECT_PANE_CLOSE_PROJECT_BUTTON_LABEL, 'project.close');
590
+ pushLine(PROJECT_PANE_REFRESH_GITHUB_BUTTON_LABEL, 'project.github.refresh');
591
+ pushLine('');
592
+ for (const line of buildProjectTreeLines(path)) {
593
+ pushLine(line);
594
+ }
468
595
  return {
469
596
  directoryId,
470
597
  path,
471
- lines: [
472
- `project ${projectName}`,
473
- `path ${path}`,
474
- '',
475
- PROJECT_PANE_NEW_CONVERSATION_BUTTON_LABEL,
476
- PROJECT_PANE_CLOSE_PROJECT_BUTTON_LABEL,
477
- '',
478
- ...buildProjectTreeLines(path),
479
- ],
480
- actionLineIndexByKind,
598
+ lines,
599
+ actionBySourceLineIndex,
600
+ actionLineIndexByKind: {
601
+ conversationNew: conversationNewLineIndex,
602
+ projectClose: projectCloseLineIndex,
603
+ },
604
+ };
605
+ }
606
+
607
+ interface BuildProjectPaneSnapshotOptions {
608
+ readonly githubReview?: ProjectPaneGitHubReviewSummary | null;
609
+ readonly expandedNodeIds?: ReadonlySet<string>;
610
+ }
611
+
612
+ export function buildProjectPaneSnapshotWithOptions(
613
+ directoryId: string,
614
+ path: string,
615
+ options: BuildProjectPaneSnapshotOptions = {},
616
+ ): ProjectPaneSnapshot {
617
+ const base = buildProjectPaneSnapshot(directoryId, path);
618
+ if (options.githubReview === undefined || options.githubReview === null) {
619
+ return base;
620
+ }
621
+ const lines: string[] = [];
622
+ const actionBySourceLineIndex: Record<number, ProjectPaneAction> = {};
623
+ const pushBaseLine = (line: string, action: ProjectPaneAction | null = null): number => {
624
+ const index = lines.length;
625
+ lines.push(line);
626
+ if (action !== null) {
627
+ actionBySourceLineIndex[index] = action;
628
+ }
629
+ return index;
630
+ };
631
+ const lineCountBeforeTree = Math.min(
632
+ base.lines.length,
633
+ base.actionLineIndexByKind.projectClose + 2,
634
+ );
635
+ for (let index = 0; index < lineCountBeforeTree; index += 1) {
636
+ const action = base.actionBySourceLineIndex[index] ?? null;
637
+ pushBaseLine(base.lines[index]!, action);
638
+ }
639
+ const reviewLines = buildProjectPaneGitHubReviewLines({
640
+ review: options.githubReview,
641
+ expandedNodeIds: options.expandedNodeIds ?? new Set<string>(),
642
+ });
643
+ for (let index = 0; index < reviewLines.lines.length; index += 1) {
644
+ const action = reviewLines.actionByRelativeLineIndex[index] ?? null;
645
+ pushBaseLine(reviewLines.lines[index]!, action);
646
+ }
647
+ pushBaseLine('');
648
+ const treeStartIndex = lineCountBeforeTree;
649
+ for (let index = treeStartIndex; index < base.lines.length; index += 1) {
650
+ pushBaseLine(base.lines[index]!);
651
+ }
652
+ return {
653
+ directoryId: base.directoryId,
654
+ path: base.path,
655
+ lines,
656
+ actionBySourceLineIndex,
657
+ actionLineIndexByKind: {
658
+ conversationNew: base.actionLineIndexByKind.conversationNew,
659
+ projectClose: base.actionLineIndexByKind.projectClose,
660
+ },
661
+ };
662
+ }
663
+
664
+ export function buildGitHubReviewPaneSnapshot(
665
+ directoryId: string,
666
+ path: string,
667
+ review: ProjectPaneGitHubReviewSummary | null,
668
+ ): ProjectPaneSnapshot {
669
+ const base = buildProjectPaneSnapshot(directoryId, path);
670
+ const lines: string[] = [];
671
+ const actionBySourceLineIndex: Record<number, ProjectPaneAction> = {};
672
+ const pushLine = (line: string, action: ProjectPaneAction | null = null): void => {
673
+ const index = lines.length;
674
+ lines.push(line);
675
+ if (action !== null) {
676
+ actionBySourceLineIndex[index] = action;
677
+ }
678
+ };
679
+ const lineCountBeforeTree = Math.min(
680
+ base.lines.length,
681
+ base.actionLineIndexByKind.projectClose + 2,
682
+ );
683
+ for (let index = 0; index < lineCountBeforeTree; index += 1) {
684
+ const action = base.actionBySourceLineIndex[index] ?? null;
685
+ pushLine(base.lines[index]!, action);
686
+ }
687
+ pushLine('github pull request');
688
+ pushLine(PROJECT_PANE_REFRESH_GITHUB_BUTTON_LABEL, 'project.github.refresh');
689
+ pushLine('');
690
+
691
+ if (review === null) {
692
+ pushLine('status not loaded');
693
+ pushLine('select refresh review to load latest state');
694
+ } else if (review.status === 'loading') {
695
+ pushLine('status loading GitHub review data…');
696
+ } else if (review.status === 'error') {
697
+ const message =
698
+ review.errorMessage === null ? 'unknown error' : sanitizeInlineText(review.errorMessage);
699
+ pushLine(`status error ${message}`);
700
+ } else {
701
+ const branchName = review.branchName?.trim() ?? '';
702
+ const branchSource = review.branchSource === null ? '' : ` (${review.branchSource})`;
703
+ pushLine(`branch ${branchName.length > 0 ? branchName : '(none)'}${branchSource}`);
704
+ if (review.pr === null) {
705
+ pushLine('pr none for tracked branch');
706
+ } else {
707
+ const pr = review.pr;
708
+ const openCommentCount = threadCommentCount(review.openThreads);
709
+ const resolvedCommentCount = threadCommentCount(review.resolvedThreads);
710
+ pushLine(
711
+ `pr #${String(pr.number)} ${githubPrStateLabel(pr)} ${sanitizeInlineText(pr.title)}`,
712
+ );
713
+ pushLine(`url ${pr.url}`);
714
+ pushLine(`author ${formatGitHubAuthor(pr.authorLogin)}`);
715
+ pushLine(`branches ${pr.headBranch} -> ${pr.baseBranch}`);
716
+ pushLine(`created ${pr.createdAt}`);
717
+ pushLine(`updated ${pr.updatedAt}`);
718
+ if (pr.mergedAt !== null) {
719
+ pushLine(`merged ${pr.mergedAt}`);
720
+ }
721
+ if (pr.closedAt !== null) {
722
+ pushLine(`closed ${pr.closedAt}`);
723
+ }
724
+ pushLine(
725
+ `review threads ${String(review.openThreads.length)} open / ${String(review.resolvedThreads.length)} resolved`,
726
+ );
727
+ pushLine(
728
+ `review comments ${String(openCommentCount + resolvedCommentCount)} total (${String(
729
+ openCommentCount,
730
+ )} open, ${String(resolvedCommentCount)} resolved)`,
731
+ );
732
+ pushLine('');
733
+ appendThreadDetails(pushLine, 'open threads', review.openThreads);
734
+ pushLine('');
735
+ appendThreadDetails(pushLine, 'resolved threads', review.resolvedThreads);
736
+ }
737
+ }
738
+
739
+ return {
740
+ directoryId: base.directoryId,
741
+ path: base.path,
742
+ lines,
743
+ actionBySourceLineIndex,
744
+ actionLineIndexByKind: {
745
+ conversationNew: base.actionLineIndexByKind.conversationNew,
746
+ projectClose: base.actionLineIndexByKind.projectClose,
747
+ },
481
748
  };
482
749
  }
483
750
 
@@ -518,6 +785,10 @@ export function projectPaneActionAtRow(
518
785
  const nextTop = Math.max(0, Math.min(maxTop, scrollTop));
519
786
  const normalizedRow = Math.max(0, Math.min(safeRows - 1, rowIndex));
520
787
  const line = wrappedLines[nextTop + normalizedRow]!;
788
+ const mappedAction = snapshot.actionBySourceLineIndex[line.sourceLineIndex];
789
+ if (mappedAction !== undefined) {
790
+ return mappedAction;
791
+ }
521
792
  if (line.sourceLineIndex === snapshot.actionLineIndexByKind.conversationNew) {
522
793
  return 'conversation.new';
523
794
  }
@@ -1,21 +1,8 @@
1
- type MuxGlobalShortcutAction =
2
- | 'mux.app.quit'
3
- | 'mux.app.interrupt-all'
4
- | 'mux.command-menu.toggle'
5
- | 'mux.gateway.profile.toggle'
6
- | 'mux.gateway.status-timeline.toggle'
7
- | 'mux.gateway.render-trace.toggle'
8
- | 'mux.conversation.new'
9
- | 'mux.conversation.critique.open-or-create'
10
- | 'mux.conversation.next'
11
- | 'mux.conversation.previous'
12
- | 'mux.conversation.titles.refresh-all'
13
- | 'mux.conversation.interrupt'
14
- | 'mux.conversation.archive'
15
- | 'mux.conversation.takeover'
16
- | 'mux.conversation.delete'
17
- | 'mux.directory.add'
18
- | 'mux.directory.close';
1
+ import {
2
+ DEFAULT_MUX_SHORTCUT_BINDINGS_RAW,
3
+ MUX_GLOBAL_SHORTCUT_ACTION_ORDER,
4
+ type MuxGlobalShortcutAction,
5
+ } from './keybinding-registry.ts';
19
6
 
20
7
  interface KeyStroke {
21
8
  readonly key: string;
@@ -30,54 +17,14 @@ interface ParsedShortcutBinding {
30
17
  readonly originalText: string;
31
18
  }
32
19
 
33
- interface ResolvedMuxShortcutBindings {
20
+ export interface ResolvedMuxShortcutBindings {
34
21
  readonly rawByAction: Readonly<Record<MuxGlobalShortcutAction, readonly string[]>>;
35
22
  readonly parsedByAction: Readonly<
36
23
  Record<MuxGlobalShortcutAction, readonly ParsedShortcutBinding[]>
37
24
  >;
38
25
  }
39
26
 
40
- const ACTION_ORDER: readonly MuxGlobalShortcutAction[] = [
41
- 'mux.app.quit',
42
- 'mux.app.interrupt-all',
43
- 'mux.command-menu.toggle',
44
- 'mux.gateway.profile.toggle',
45
- 'mux.gateway.status-timeline.toggle',
46
- 'mux.gateway.render-trace.toggle',
47
- 'mux.conversation.new',
48
- 'mux.conversation.critique.open-or-create',
49
- 'mux.conversation.next',
50
- 'mux.conversation.previous',
51
- 'mux.conversation.titles.refresh-all',
52
- 'mux.conversation.interrupt',
53
- 'mux.conversation.archive',
54
- 'mux.conversation.takeover',
55
- 'mux.conversation.delete',
56
- 'mux.directory.add',
57
- 'mux.directory.close',
58
- ];
59
-
60
- const DEFAULT_MUX_SHORTCUT_BINDINGS_RAW: Readonly<
61
- Record<MuxGlobalShortcutAction, readonly string[]>
62
- > = {
63
- 'mux.app.quit': [],
64
- 'mux.app.interrupt-all': ['ctrl+c'],
65
- 'mux.command-menu.toggle': ['ctrl+p', 'cmd+p'],
66
- 'mux.gateway.profile.toggle': ['ctrl+shift+p'],
67
- 'mux.gateway.status-timeline.toggle': ['alt+r'],
68
- 'mux.gateway.render-trace.toggle': ['ctrl+]'],
69
- 'mux.conversation.new': ['ctrl+t'],
70
- 'mux.conversation.critique.open-or-create': ['ctrl+g'],
71
- 'mux.conversation.next': ['ctrl+j'],
72
- 'mux.conversation.previous': ['ctrl+k'],
73
- 'mux.conversation.titles.refresh-all': ['ctrl+r'],
74
- 'mux.conversation.interrupt': [],
75
- 'mux.conversation.archive': [],
76
- 'mux.conversation.takeover': ['ctrl+l'],
77
- 'mux.conversation.delete': ['ctrl+x'],
78
- 'mux.directory.add': ['ctrl+o'],
79
- 'mux.directory.close': ['ctrl+w'],
80
- };
27
+ const ACTION_ORDER = MUX_GLOBAL_SHORTCUT_ACTION_ORDER;
81
28
 
82
29
  const KEY_TOKEN_ALIASES = new Map<string, string>([
83
30
  ['cmd', 'meta'],
@@ -541,10 +488,18 @@ function strokesEqual(left: KeyStroke, right: KeyStroke): boolean {
541
488
 
542
489
  function parseBindingsForAction(rawBindings: readonly string[]): readonly ParsedShortcutBinding[] {
543
490
  const parsed: ParsedShortcutBinding[] = [];
491
+
492
+ const pushIfUnique = (candidate: ParsedShortcutBinding): void => {
493
+ if (parsed.some((existing) => strokesEqual(existing.stroke, candidate.stroke))) {
494
+ return;
495
+ }
496
+ parsed.push(candidate);
497
+ };
498
+
544
499
  for (const raw of rawBindings) {
545
500
  const normalized = parseShortcutBinding(raw);
546
501
  if (normalized !== null) {
547
- parsed.push(normalized);
502
+ pushIfUnique(normalized);
548
503
  }
549
504
  }
550
505
  return parsed;
@@ -553,57 +508,11 @@ function parseBindingsForAction(rawBindings: readonly string[]): readonly Parsed
553
508
  function withDefaultBindings(
554
509
  overrides: Readonly<Record<string, readonly string[]> | undefined>,
555
510
  ): Readonly<Record<MuxGlobalShortcutAction, readonly string[]>> {
556
- return {
557
- 'mux.app.quit':
558
- overrides?.['mux.app.quit'] ?? DEFAULT_MUX_SHORTCUT_BINDINGS_RAW['mux.app.quit'],
559
- 'mux.app.interrupt-all':
560
- overrides?.['mux.app.interrupt-all'] ??
561
- DEFAULT_MUX_SHORTCUT_BINDINGS_RAW['mux.app.interrupt-all'],
562
- 'mux.command-menu.toggle':
563
- overrides?.['mux.command-menu.toggle'] ??
564
- DEFAULT_MUX_SHORTCUT_BINDINGS_RAW['mux.command-menu.toggle'],
565
- 'mux.gateway.profile.toggle':
566
- overrides?.['mux.gateway.profile.toggle'] ??
567
- DEFAULT_MUX_SHORTCUT_BINDINGS_RAW['mux.gateway.profile.toggle'],
568
- 'mux.gateway.status-timeline.toggle':
569
- overrides?.['mux.gateway.status-timeline.toggle'] ??
570
- DEFAULT_MUX_SHORTCUT_BINDINGS_RAW['mux.gateway.status-timeline.toggle'],
571
- 'mux.gateway.render-trace.toggle':
572
- overrides?.['mux.gateway.render-trace.toggle'] ??
573
- DEFAULT_MUX_SHORTCUT_BINDINGS_RAW['mux.gateway.render-trace.toggle'],
574
- 'mux.conversation.new':
575
- overrides?.['mux.conversation.new'] ??
576
- DEFAULT_MUX_SHORTCUT_BINDINGS_RAW['mux.conversation.new'],
577
- 'mux.conversation.critique.open-or-create':
578
- overrides?.['mux.conversation.critique.open-or-create'] ??
579
- DEFAULT_MUX_SHORTCUT_BINDINGS_RAW['mux.conversation.critique.open-or-create'],
580
- 'mux.conversation.next':
581
- overrides?.['mux.conversation.next'] ??
582
- DEFAULT_MUX_SHORTCUT_BINDINGS_RAW['mux.conversation.next'],
583
- 'mux.conversation.previous':
584
- overrides?.['mux.conversation.previous'] ??
585
- DEFAULT_MUX_SHORTCUT_BINDINGS_RAW['mux.conversation.previous'],
586
- 'mux.conversation.titles.refresh-all':
587
- overrides?.['mux.conversation.titles.refresh-all'] ??
588
- DEFAULT_MUX_SHORTCUT_BINDINGS_RAW['mux.conversation.titles.refresh-all'],
589
- 'mux.conversation.interrupt':
590
- overrides?.['mux.conversation.interrupt'] ??
591
- DEFAULT_MUX_SHORTCUT_BINDINGS_RAW['mux.conversation.interrupt'],
592
- 'mux.conversation.archive':
593
- overrides?.['mux.conversation.archive'] ??
594
- DEFAULT_MUX_SHORTCUT_BINDINGS_RAW['mux.conversation.archive'],
595
- 'mux.conversation.takeover':
596
- overrides?.['mux.conversation.takeover'] ??
597
- DEFAULT_MUX_SHORTCUT_BINDINGS_RAW['mux.conversation.takeover'],
598
- 'mux.conversation.delete':
599
- overrides?.['mux.conversation.delete'] ??
600
- DEFAULT_MUX_SHORTCUT_BINDINGS_RAW['mux.conversation.delete'],
601
- 'mux.directory.add':
602
- overrides?.['mux.directory.add'] ?? DEFAULT_MUX_SHORTCUT_BINDINGS_RAW['mux.directory.add'],
603
- 'mux.directory.close':
604
- overrides?.['mux.directory.close'] ??
605
- DEFAULT_MUX_SHORTCUT_BINDINGS_RAW['mux.directory.close'],
606
- };
511
+ const rawByAction = {} as Record<MuxGlobalShortcutAction, readonly string[]>;
512
+ for (const action of ACTION_ORDER) {
513
+ rawByAction[action] = overrides?.[action] ?? DEFAULT_MUX_SHORTCUT_BINDINGS_RAW[action];
514
+ }
515
+ return rawByAction;
607
516
  }
608
517
 
609
518
  export function resolveMuxShortcutBindings(
@@ -616,6 +525,7 @@ export function resolveMuxShortcutBindings(
616
525
  'mux.app.quit': parseBindingsForAction(rawByAction['mux.app.quit']),
617
526
  'mux.app.interrupt-all': parseBindingsForAction(rawByAction['mux.app.interrupt-all']),
618
527
  'mux.command-menu.toggle': parseBindingsForAction(rawByAction['mux.command-menu.toggle']),
528
+ 'mux.debug-bar.toggle': parseBindingsForAction(rawByAction['mux.debug-bar.toggle']),
619
529
  'mux.gateway.profile.toggle': parseBindingsForAction(
620
530
  rawByAction['mux.gateway.profile.toggle'],
621
531
  ),