@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.
- package/README.md +31 -35
- package/package.json +31 -11
- package/packages/harness-ai/src/anthropic-protocol.ts +68 -68
- package/packages/harness-ai/src/stream-text.ts +13 -91
- package/packages/harness-ui/src/frame-primitives.ts +158 -0
- package/packages/harness-ui/src/index.ts +18 -0
- package/packages/harness-ui/src/interaction/conversation-input-forwarder.ts +221 -0
- package/packages/harness-ui/src/interaction/conversation-selection-input.ts +213 -0
- package/packages/harness-ui/src/interaction/global-shortcut-input.ts +172 -0
- package/{src/ui → packages/harness-ui/src/interaction}/input-preflight.ts +10 -12
- package/{src/ui → packages/harness-ui/src/interaction}/input-token-router.ts +120 -69
- package/packages/harness-ui/src/interaction/input.ts +420 -0
- package/packages/harness-ui/src/interaction/left-nav-input.ts +166 -0
- package/{src/ui → packages/harness-ui/src/interaction}/main-pane-pointer-input.ts +91 -23
- package/{src/ui → packages/harness-ui/src/interaction}/pointer-routing-input.ts +112 -48
- package/packages/harness-ui/src/interaction/rail-pointer-input.ts +62 -0
- package/packages/harness-ui/src/interaction/repository-fold-input.ts +118 -0
- package/packages/harness-ui/src/kit.ts +476 -0
- package/packages/harness-ui/src/layout.ts +238 -0
- package/{src/ui/modals/manager.ts → packages/harness-ui/src/modal-manager.ts} +94 -64
- package/{src/ui → packages/harness-ui/src}/screen.ts +53 -26
- package/packages/harness-ui/src/surface.ts +252 -0
- package/packages/harness-ui/src/text-layout.ts +210 -0
- package/packages/nim-core/src/contracts.ts +239 -0
- package/packages/nim-core/src/event-store.ts +299 -0
- package/packages/nim-core/src/events.ts +53 -0
- package/packages/nim-core/src/index.ts +9 -0
- package/packages/nim-core/src/provider-router.ts +129 -0
- package/packages/nim-core/src/providers/anthropic-driver.ts +291 -0
- package/packages/nim-core/src/runtime-factory.ts +49 -0
- package/packages/nim-core/src/runtime.ts +1797 -0
- package/packages/nim-core/src/session-store.ts +516 -0
- package/packages/nim-core/src/telemetry.ts +48 -0
- package/packages/nim-test-tui/src/index.ts +150 -0
- package/packages/nim-ui-core/src/index.ts +1 -0
- package/packages/nim-ui-core/src/projection.ts +87 -0
- package/scripts/codex-live-mux-runtime.ts +2 -3721
- package/scripts/control-plane-daemon.ts +24 -2
- package/scripts/harness-bin.js +5 -0
- package/scripts/harness-commands.ts +300 -0
- package/scripts/harness-runtime.ts +82 -0
- package/scripts/harness.ts +33 -3007
- package/scripts/nim-tui-smoke.ts +748 -0
- package/src/cli/auth/runtime.ts +948 -0
- package/src/cli/default-gateway-pointer.ts +193 -0
- package/src/cli/gateway/runtime.ts +1872 -0
- package/src/cli/parsing/flags.ts +23 -0
- package/src/cli/parsing/session.ts +42 -0
- package/src/cli/runtime/context.ts +193 -0
- package/src/cli/runtime-app/application.ts +392 -0
- package/src/cli/runtime-infra/gateway-control.ts +729 -0
- package/{scripts/harness-inspector.ts → src/cli/workflows/inspector.ts} +14 -11
- package/src/cli/workflows/runtime.ts +965 -0
- package/src/clients/tui/left-rail-interactions.ts +519 -0
- package/src/clients/tui/main-pane-interactions.ts +509 -0
- package/src/clients/tui/modal-input-routing.ts +71 -0
- package/src/clients/tui/render-snapshot-adapter.ts +88 -0
- package/src/clients/web/synced-selectors.ts +132 -0
- package/src/codex/live-session.ts +82 -29
- package/src/config/config-core.ts +361 -10
- package/src/config/harness-paths.ts +4 -7
- package/src/config/harness-runtime-migration.ts +142 -19
- package/src/config/harness.config.template.jsonc +33 -0
- package/src/config/secrets-core.ts +92 -4
- package/src/control-plane/agent-realtime-api.ts +82 -427
- package/src/control-plane/prompt/thread-title-namer.ts +49 -23
- package/src/control-plane/session-summary.ts +10 -81
- package/src/control-plane/status/reducer-base.ts +12 -12
- package/src/control-plane/status/reducers/claude-status-reducer.ts +3 -3
- package/src/control-plane/status/reducers/codex-status-reducer.ts +4 -4
- package/src/control-plane/status/reducers/cursor-status-reducer.ts +3 -3
- package/src/control-plane/stream-client.ts +12 -2
- package/src/control-plane/stream-command-parser.ts +83 -143
- package/src/control-plane/stream-protocol.ts +53 -37
- package/src/control-plane/stream-server-background.ts +18 -2
- package/src/control-plane/stream-server-command.ts +376 -69
- package/src/control-plane/stream-server-session-runtime.ts +3 -2
- package/src/control-plane/stream-server.ts +943 -80
- package/src/control-plane/stream-session-runtime-types.ts +41 -0
- package/src/{mux/live-mux/control-plane-records.ts → core/contracts/records.ts} +24 -97
- package/src/core/state/observed-stream-cursor.ts +43 -0
- package/src/core/state/synced-observed-state.ts +273 -0
- package/src/core/store/harness-synced-store.ts +81 -0
- package/src/diff/budget.ts +136 -0
- package/src/diff/build.ts +289 -0
- package/src/diff/chunker.ts +146 -0
- package/src/diff/git-invoke.ts +315 -0
- package/src/diff/git-parse.ts +472 -0
- package/src/diff/hash.ts +70 -0
- package/src/diff/index.ts +24 -0
- package/src/diff/normalize.ts +134 -0
- package/src/diff/types.ts +178 -0
- package/src/diff-ui/args.ts +346 -0
- package/src/diff-ui/commands.ts +123 -0
- package/src/diff-ui/finder.ts +94 -0
- package/src/diff-ui/highlight.ts +127 -0
- package/src/diff-ui/index.ts +2 -0
- package/src/diff-ui/model.ts +141 -0
- package/src/diff-ui/pager.ts +412 -0
- package/src/diff-ui/render.ts +337 -0
- package/src/diff-ui/runtime.ts +379 -0
- package/src/diff-ui/state.ts +224 -0
- package/src/diff-ui/types.ts +236 -0
- package/src/domain/conversations.ts +11 -7
- package/src/domain/workspace.ts +76 -4
- package/src/mux/control-plane-op-queue.ts +93 -7
- package/src/mux/conversation-rail.ts +28 -71
- package/src/mux/dual-pane-core.ts +13 -13
- package/src/mux/harness-core-ui.ts +313 -42
- package/src/mux/input-shortcuts.ts +22 -112
- package/src/mux/keybinding-catalog.ts +340 -0
- package/src/mux/keybinding-registry.ts +103 -0
- package/src/mux/live-mux/command-menu-open-in.ts +280 -0
- package/src/mux/live-mux/command-menu.ts +167 -4
- package/src/mux/live-mux/conversation-state.ts +13 -0
- package/src/mux/live-mux/directory-resolution.ts +1 -1
- package/src/mux/live-mux/git-parsing.ts +16 -0
- package/src/mux/live-mux/git-snapshot.ts +33 -2
- package/src/mux/live-mux/global-shortcut-handlers.ts +6 -0
- package/src/mux/live-mux/home-pane-drop.ts +1 -1
- package/src/mux/live-mux/home-pane-pointer.ts +10 -0
- package/src/mux/live-mux/input-forwarding.ts +59 -2
- package/src/mux/live-mux/left-nav-activation.ts +124 -7
- package/src/mux/live-mux/left-nav.ts +35 -0
- package/src/mux/live-mux/link-click.ts +292 -0
- package/src/mux/live-mux/modal-command-menu-handler.ts +46 -9
- package/src/mux/live-mux/modal-conversation-handlers.ts +5 -1
- package/src/mux/live-mux/modal-input-reducers.ts +106 -8
- package/src/mux/live-mux/modal-overlays.ts +210 -31
- package/src/mux/live-mux/modal-pointer.ts +3 -7
- package/src/mux/live-mux/modal-prompt-handlers.ts +107 -1
- package/src/mux/live-mux/modal-release-notes-handler.ts +111 -0
- package/src/mux/live-mux/modal-task-editor-handler.ts +16 -11
- package/src/mux/live-mux/pointer-routing.ts +5 -2
- package/src/mux/live-mux/project-pane-pointer.ts +8 -0
- package/src/mux/live-mux/rail-layout.ts +33 -30
- package/src/mux/live-mux/release-notes.ts +383 -0
- package/src/mux/live-mux/render-trace-analysis.ts +52 -7
- package/src/mux/live-mux/repository-folding.ts +3 -0
- package/src/mux/live-mux/selection.ts +0 -4
- package/src/mux/live-mux/session-diagnostics-paths.ts +21 -0
- package/src/mux/project-pane-github-review.ts +271 -0
- package/src/mux/render-frame.ts +4 -0
- package/src/mux/runtime-app/codex-live-mux-runtime.ts +5191 -0
- package/src/mux/task-composer.ts +21 -14
- package/src/mux/task-focused-pane.ts +118 -117
- package/src/mux/task-screen-keybindings.ts +19 -82
- package/src/mux/workspace-rail-model.ts +270 -104
- package/src/mux/workspace-rail.ts +45 -22
- package/src/pty/session-broker.ts +1 -1
- package/{scripts → src/recording}/terminal-recording-gif-lib.ts +2 -2
- package/src/services/control-plane.ts +50 -32
- package/src/services/conversation-lifecycle.ts +118 -87
- package/src/services/conversation-startup-hydration.ts +20 -12
- package/src/services/directory-hydration.ts +21 -16
- package/src/services/event-persistence.ts +7 -0
- package/src/services/left-rail-pointer-handler.ts +329 -0
- package/src/services/mux-ui-state-persistence.ts +5 -1
- package/src/services/recording.ts +34 -26
- package/src/services/runtime-command-menu-agent-tools.ts +1 -1
- package/src/services/runtime-control-actions.ts +79 -61
- package/src/services/runtime-control-plane-ops.ts +122 -83
- package/src/services/runtime-conversation-actions.ts +40 -26
- package/src/services/runtime-conversation-activation.ts +82 -30
- package/src/services/runtime-conversation-starter.ts +80 -48
- package/src/services/runtime-conversation-title-edit.ts +91 -80
- package/src/services/runtime-envelope-handler.ts +107 -105
- package/src/services/runtime-git-state.ts +42 -29
- package/src/services/runtime-layout-resize.ts +3 -1
- package/src/services/runtime-left-rail-render.ts +99 -63
- package/src/services/runtime-nim-cli-session.ts +438 -0
- package/src/services/runtime-nim-session.ts +705 -0
- package/src/services/runtime-nim-tool-bridge.ts +141 -0
- package/src/services/runtime-observed-event-projection-pipeline.ts +45 -0
- package/src/services/runtime-process-wiring.ts +29 -36
- package/src/services/runtime-project-pane-github-review-cache.ts +164 -0
- package/src/services/runtime-render-flush.ts +63 -70
- package/src/services/runtime-render-lifecycle.ts +65 -64
- package/src/services/runtime-render-orchestrator.ts +55 -45
- package/src/services/runtime-render-pipeline.ts +106 -103
- package/src/services/runtime-render-state.ts +62 -49
- package/src/services/runtime-repository-actions.ts +97 -70
- package/src/services/runtime-right-pane-render.ts +80 -53
- package/src/services/runtime-shutdown.ts +38 -35
- package/src/services/runtime-stream-subscriptions.ts +35 -27
- package/src/services/runtime-task-composer-persistence.ts +71 -59
- package/src/services/runtime-task-composer-snapshot.ts +14 -0
- package/src/services/runtime-task-editor-actions.ts +46 -29
- package/src/services/runtime-task-pane-actions.ts +220 -134
- package/src/services/runtime-task-pane-shortcuts.ts +323 -123
- package/src/services/runtime-workspace-observed-effect-queue.ts +25 -0
- package/src/services/runtime-workspace-observed-events.ts +33 -184
- package/src/services/runtime-workspace-observed-transition-policy.ts +228 -0
- package/src/services/session-diagnostics-store.ts +217 -0
- package/src/services/startup-background-resume.ts +26 -21
- package/src/services/startup-orchestrator.ts +16 -13
- package/src/services/startup-paint-tracker.ts +29 -21
- package/src/services/startup-persisted-conversation-queue.ts +19 -13
- package/src/services/startup-settled-gate.ts +25 -15
- package/src/services/startup-shutdown.ts +18 -22
- package/src/services/startup-state-hydration.ts +44 -34
- package/src/services/startup-visibility.ts +12 -4
- package/src/services/task-pane-selection-actions.ts +89 -72
- package/src/services/task-planning-hydration.ts +24 -18
- package/src/services/task-planning-observed-events.ts +50 -52
- package/src/services/workspace-observed-events.ts +66 -63
- package/src/storage/storage-lifecycle-core.ts +438 -0
- package/src/store/control-plane-store-normalize.ts +33 -242
- package/src/store/control-plane-store-types.ts +1 -35
- package/src/store/control-plane-store.ts +396 -56
- package/src/store/event-store.ts +397 -3
- package/src/terminal/snapshot-oracle.ts +207 -94
- package/src/ui/mux-theme.ts +112 -8
- package/src/ui/panes/home-gridfire.ts +40 -31
- package/src/ui/panes/home.ts +10 -2
- package/src/ui/panes/nim.ts +315 -0
- package/src/mux/live-mux/actions-task.ts +0 -115
- package/src/mux/live-mux/left-rail-actions.ts +0 -118
- package/src/mux/live-mux/left-rail-conversation-click.ts +0 -82
- package/src/mux/live-mux/left-rail-pointer.ts +0 -74
- package/src/mux/live-mux/task-pane-shortcuts.ts +0 -206
- package/src/services/runtime-directory-actions.ts +0 -164
- package/src/services/runtime-input-pipeline.ts +0 -50
- package/src/services/runtime-input-router.ts +0 -189
- package/src/services/runtime-main-pane-input.ts +0 -230
- package/src/services/runtime-modal-input.ts +0 -119
- package/src/services/runtime-navigation-input.ts +0 -197
- package/src/services/runtime-rail-input.ts +0 -278
- package/src/services/runtime-task-pane.ts +0 -62
- package/src/services/runtime-workspace-actions.ts +0 -158
- package/src/ui/conversation-input-forwarder.ts +0 -114
- package/src/ui/conversation-selection-input.ts +0 -103
- package/src/ui/global-shortcut-input.ts +0 -89
- package/src/ui/input.ts +0 -238
- package/src/ui/kit.ts +0 -509
- package/src/ui/left-nav-input.ts +0 -80
- package/src/ui/left-rail-pointer-input.ts +0 -148
- package/src/ui/repository-fold-input.ts +0 -91
- 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 {
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
102
|
+
export const PROJECT_PANE_CLOSE_PROJECT_BUTTON_LABEL = UI_KIT.formatButton({
|
|
89
103
|
label: 'close project',
|
|
90
104
|
prefixIcon: '<',
|
|
91
105
|
});
|
|
92
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
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
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
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
|
),
|