@jmoyers/harness 0.1.11 → 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 -39
- 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/packages/harness-ui/src/modal-manager.ts +222 -0
- 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 -3872
- package/scripts/control-plane-daemon.ts +11 -0
- 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 -3019
- package/scripts/nim-tui-smoke.ts +748 -0
- package/src/cli/auth/runtime.ts +948 -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 +348 -8
- package/src/config/harness.config.template.jsonc +33 -0
- package/src/control-plane/agent-realtime-api.ts +82 -427
- 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-command.ts +376 -69
- package/src/control-plane/stream-server-session-runtime.ts +3 -2
- package/src/control-plane/stream-server.ts +864 -70
- 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/workspace.ts +68 -5
- 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 +13 -131
- 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-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 +77 -12
- package/src/mux/live-mux/modal-overlays.ts +168 -34
- package/src/mux/live-mux/modal-pointer.ts +3 -7
- package/src/mux/live-mux/modal-prompt-handlers.ts +23 -2
- 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 +10 -101
- 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 +73 -46
- package/src/services/runtime-conversation-starter.ts +53 -45
- 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 -72
- 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 +360 -56
- package/src/store/event-store.ts +366 -8
- 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 -85
- 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 -195
- package/src/services/runtime-main-pane-input.ts +0 -230
- package/src/services/runtime-modal-input.ts +0 -137
- package/src/services/runtime-navigation-input.ts +0 -197
- package/src/services/runtime-rail-input.ts +0 -279
- 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 -269
- 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/modals/manager.ts +0 -218
- package/src/ui/repository-fold-input.ts +0 -91
- package/src/ui/surface.ts +0 -224
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HARNESS_MUX_OPEN_IN_TARGET_IDS,
|
|
3
|
+
type HarnessMuxOpenInTargetId,
|
|
4
|
+
type HarnessMuxOpenInTargetOverrideConfig,
|
|
5
|
+
} from '../../config/config-core.ts';
|
|
6
|
+
import type { RegisteredCommandMenuAction } from './command-menu.ts';
|
|
7
|
+
|
|
8
|
+
interface OpenInTargetDefaults {
|
|
9
|
+
readonly title: string;
|
|
10
|
+
readonly aliases: readonly string[];
|
|
11
|
+
readonly keywords: readonly string[];
|
|
12
|
+
readonly macAppName: string | null;
|
|
13
|
+
readonly launchCommand: string | null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const OPEN_IN_TARGET_DEFAULTS: Readonly<Record<HarnessMuxOpenInTargetId, OpenInTargetDefaults>> = {
|
|
17
|
+
iterm2: {
|
|
18
|
+
title: 'iTerm2',
|
|
19
|
+
aliases: ['iterm2', 'terminal'],
|
|
20
|
+
keywords: ['iterm', 'terminal'],
|
|
21
|
+
macAppName: 'iTerm',
|
|
22
|
+
launchCommand: 'iterm2',
|
|
23
|
+
},
|
|
24
|
+
ghostty: {
|
|
25
|
+
title: 'Ghostty',
|
|
26
|
+
aliases: ['ghostty', 'terminal'],
|
|
27
|
+
keywords: ['ghostty', 'terminal'],
|
|
28
|
+
macAppName: 'Ghostty',
|
|
29
|
+
launchCommand: 'ghostty',
|
|
30
|
+
},
|
|
31
|
+
zed: {
|
|
32
|
+
title: 'Zed',
|
|
33
|
+
aliases: ['zed', 'editor'],
|
|
34
|
+
keywords: ['zed', 'editor'],
|
|
35
|
+
macAppName: 'Zed',
|
|
36
|
+
launchCommand: 'zed',
|
|
37
|
+
},
|
|
38
|
+
cursor: {
|
|
39
|
+
title: 'Cursor',
|
|
40
|
+
aliases: ['cursor', 'cursor ide', 'editor'],
|
|
41
|
+
keywords: ['cursor', 'editor', 'ide'],
|
|
42
|
+
macAppName: 'Cursor',
|
|
43
|
+
launchCommand: 'cursor',
|
|
44
|
+
},
|
|
45
|
+
vscode: {
|
|
46
|
+
title: 'VSCode',
|
|
47
|
+
aliases: ['vscode', 'vs code', 'code', 'editor'],
|
|
48
|
+
keywords: ['vscode', 'editor', 'ide'],
|
|
49
|
+
macAppName: 'Visual Studio Code',
|
|
50
|
+
launchCommand: 'code',
|
|
51
|
+
},
|
|
52
|
+
warp: {
|
|
53
|
+
title: 'Warp',
|
|
54
|
+
aliases: ['warp', 'terminal'],
|
|
55
|
+
keywords: ['warp', 'terminal'],
|
|
56
|
+
macAppName: 'Warp',
|
|
57
|
+
launchCommand: 'warp',
|
|
58
|
+
},
|
|
59
|
+
finder: {
|
|
60
|
+
title: 'Finder',
|
|
61
|
+
aliases: ['finder', 'file manager'],
|
|
62
|
+
keywords: ['finder', 'files'],
|
|
63
|
+
macAppName: 'Finder',
|
|
64
|
+
launchCommand: null,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
function normalizedCommandPart(part: string): string {
|
|
69
|
+
return part.trim();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function normalizeLaunchCommandOverride(
|
|
73
|
+
value: readonly string[] | undefined,
|
|
74
|
+
): readonly string[] | null {
|
|
75
|
+
if (value === undefined) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const normalized = value.map(normalizedCommandPart).filter((part) => part.length > 0);
|
|
79
|
+
return normalized.length > 0 ? normalized : [];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function defaultLaunchCommand(
|
|
83
|
+
platform: NodeJS.Platform,
|
|
84
|
+
defaults: OpenInTargetDefaults,
|
|
85
|
+
appName: string | null,
|
|
86
|
+
): readonly string[] {
|
|
87
|
+
if (platform === 'darwin' && appName !== null) {
|
|
88
|
+
return ['open', '-a', appName, '{path}'];
|
|
89
|
+
}
|
|
90
|
+
if (defaults.launchCommand === null) {
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
return [defaults.launchCommand, '{path}'];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function defaultDetectCommand(
|
|
97
|
+
platform: NodeJS.Platform,
|
|
98
|
+
defaults: OpenInTargetDefaults,
|
|
99
|
+
): string | null {
|
|
100
|
+
if (platform === 'darwin') {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
return defaults.launchCommand;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function isAutoDetectedTargetAvailable(options: {
|
|
107
|
+
platform: NodeJS.Platform;
|
|
108
|
+
appName: string | null;
|
|
109
|
+
detectCommand: string | null;
|
|
110
|
+
isMacApplicationInstalled: (appName: string) => boolean;
|
|
111
|
+
isCommandAvailable: (command: string) => boolean;
|
|
112
|
+
}): boolean {
|
|
113
|
+
if (options.platform === 'darwin' && options.appName !== null) {
|
|
114
|
+
return options.isMacApplicationInstalled(options.appName);
|
|
115
|
+
}
|
|
116
|
+
if (options.detectCommand === null) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
return options.isCommandAvailable(options.detectCommand);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface ResolvedCommandMenuOpenInTarget {
|
|
123
|
+
readonly id: HarnessMuxOpenInTargetId;
|
|
124
|
+
readonly title: string;
|
|
125
|
+
readonly aliases: readonly string[];
|
|
126
|
+
readonly keywords: readonly string[];
|
|
127
|
+
readonly launchCommand: readonly string[];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function resolveCommandMenuOpenInTargets(options: {
|
|
131
|
+
platform: NodeJS.Platform;
|
|
132
|
+
overrides: Readonly<
|
|
133
|
+
Partial<Record<HarnessMuxOpenInTargetId, HarnessMuxOpenInTargetOverrideConfig>>
|
|
134
|
+
>;
|
|
135
|
+
isCommandAvailable: (command: string) => boolean;
|
|
136
|
+
isMacApplicationInstalled: (appName: string) => boolean;
|
|
137
|
+
}): readonly ResolvedCommandMenuOpenInTarget[] {
|
|
138
|
+
const resolved: ResolvedCommandMenuOpenInTarget[] = [];
|
|
139
|
+
for (const targetId of HARNESS_MUX_OPEN_IN_TARGET_IDS) {
|
|
140
|
+
const defaults = OPEN_IN_TARGET_DEFAULTS[targetId];
|
|
141
|
+
const override = options.overrides[targetId];
|
|
142
|
+
if (override?.enabled === false) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const appName =
|
|
146
|
+
override?.appName === undefined || override.appName.trim().length === 0
|
|
147
|
+
? defaults.macAppName
|
|
148
|
+
: override.appName.trim();
|
|
149
|
+
const detectCommand =
|
|
150
|
+
override?.detectCommand === undefined
|
|
151
|
+
? defaultDetectCommand(options.platform, defaults)
|
|
152
|
+
: override.detectCommand === null || override.detectCommand.trim().length === 0
|
|
153
|
+
? null
|
|
154
|
+
: override.detectCommand.trim();
|
|
155
|
+
const overrideLaunchCommand = normalizeLaunchCommandOverride(override?.launchCommand);
|
|
156
|
+
const launchCommand =
|
|
157
|
+
overrideLaunchCommand === null
|
|
158
|
+
? defaultLaunchCommand(options.platform, defaults, appName)
|
|
159
|
+
: overrideLaunchCommand;
|
|
160
|
+
if (launchCommand.length === 0) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const available =
|
|
164
|
+
override?.enabled === true
|
|
165
|
+
? true
|
|
166
|
+
: isAutoDetectedTargetAvailable({
|
|
167
|
+
platform: options.platform,
|
|
168
|
+
appName,
|
|
169
|
+
detectCommand,
|
|
170
|
+
isCommandAvailable: options.isCommandAvailable,
|
|
171
|
+
isMacApplicationInstalled: options.isMacApplicationInstalled,
|
|
172
|
+
});
|
|
173
|
+
if (!available) {
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
resolved.push({
|
|
177
|
+
id: targetId,
|
|
178
|
+
title: defaults.title,
|
|
179
|
+
aliases: defaults.aliases,
|
|
180
|
+
keywords: defaults.keywords,
|
|
181
|
+
launchCommand,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
return resolved;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function resolveCommandMenuOpenInCommand(
|
|
188
|
+
target: ResolvedCommandMenuOpenInTarget,
|
|
189
|
+
directoryPath: string,
|
|
190
|
+
): { command: string; args: readonly string[] } | null {
|
|
191
|
+
const command = target.launchCommand[0]?.trim() ?? '';
|
|
192
|
+
if (command.length === 0) {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
let pathInjected = false;
|
|
196
|
+
const args = target.launchCommand.slice(1).map((part) => {
|
|
197
|
+
if (part === '{path}') {
|
|
198
|
+
pathInjected = true;
|
|
199
|
+
return directoryPath;
|
|
200
|
+
}
|
|
201
|
+
return part;
|
|
202
|
+
});
|
|
203
|
+
if (!pathInjected) {
|
|
204
|
+
args.push(directoryPath);
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
command,
|
|
208
|
+
args,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
interface CommandMenuOpenInProviderDirectory {
|
|
213
|
+
readonly directoryId: string;
|
|
214
|
+
readonly path: string;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
interface CommandMenuOpenInProviderOptions<TContext> {
|
|
218
|
+
readonly registerProvider: (
|
|
219
|
+
providerId: string,
|
|
220
|
+
provider: (context: TContext) => readonly RegisteredCommandMenuAction<TContext>[],
|
|
221
|
+
) => () => void;
|
|
222
|
+
readonly providerId?: string;
|
|
223
|
+
readonly resolveDirectories: (context: TContext) => readonly CommandMenuOpenInProviderDirectory[];
|
|
224
|
+
readonly resolveTargets: () => readonly ResolvedCommandMenuOpenInTarget[];
|
|
225
|
+
readonly projectPathTail: (directoryPath: string) => string;
|
|
226
|
+
readonly openInTarget: (
|
|
227
|
+
target: ResolvedCommandMenuOpenInTarget,
|
|
228
|
+
directoryPath: string,
|
|
229
|
+
) => boolean;
|
|
230
|
+
readonly copyPath: (directoryPath: string) => boolean;
|
|
231
|
+
readonly setNotice: (message: string) => void;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export function registerCommandMenuOpenInProvider<TContext>(
|
|
235
|
+
options: CommandMenuOpenInProviderOptions<TContext>,
|
|
236
|
+
): () => void {
|
|
237
|
+
const providerId = options.providerId ?? 'project.open-in';
|
|
238
|
+
return options.registerProvider(providerId, (context) => {
|
|
239
|
+
const actions: RegisteredCommandMenuAction<TContext>[] = [];
|
|
240
|
+
const targets = options.resolveTargets();
|
|
241
|
+
for (const directory of options.resolveDirectories(context)) {
|
|
242
|
+
const projectLabel = options.projectPathTail(directory.path);
|
|
243
|
+
for (const target of targets) {
|
|
244
|
+
actions.push({
|
|
245
|
+
id: `project.open-in.${target.id}.${directory.directoryId}`,
|
|
246
|
+
title: `Open in ${target.title}: ${projectLabel}`,
|
|
247
|
+
aliases: [
|
|
248
|
+
...target.aliases,
|
|
249
|
+
'open project in',
|
|
250
|
+
`open in ${target.title.toLowerCase()}`,
|
|
251
|
+
directory.path,
|
|
252
|
+
projectLabel,
|
|
253
|
+
],
|
|
254
|
+
keywords: ['open', 'project', 'directory', 'path', ...target.keywords],
|
|
255
|
+
detail: directory.path,
|
|
256
|
+
run: () => {
|
|
257
|
+
const opened = options.openInTarget(target, directory.path);
|
|
258
|
+
options.setNotice(
|
|
259
|
+
opened
|
|
260
|
+
? `opened ${projectLabel} in ${target.title}`
|
|
261
|
+
: `failed to open ${projectLabel} in ${target.title}`,
|
|
262
|
+
);
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
actions.push({
|
|
267
|
+
id: `project.copy-path.${directory.directoryId}`,
|
|
268
|
+
title: `Copy Path: ${projectLabel}`,
|
|
269
|
+
aliases: ['copy path', 'copy project path', directory.path, projectLabel],
|
|
270
|
+
keywords: ['copy', 'path', 'clipboard', 'project', 'directory'],
|
|
271
|
+
detail: directory.path,
|
|
272
|
+
run: () => {
|
|
273
|
+
const copied = options.copyPath(directory.path);
|
|
274
|
+
options.setNotice(copied ? `copied path: ${directory.path}` : 'failed to copy path');
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
return actions;
|
|
279
|
+
});
|
|
280
|
+
}
|
|
@@ -1,6 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
const COMMAND_MENU_MAX_RESULTS = 8;
|
|
2
|
+
const THREAD_ACTION_ID_PATTERN = /^thread\.(?:start|install)\.([a-z0-9-]+)$/u;
|
|
3
|
+
const TASK_SUMMARY_MAX_CHARS = 96;
|
|
4
|
+
const DEFAULT_TASK_SUMMARY = 'untitled task';
|
|
5
|
+
|
|
6
|
+
type CommandMenuScope = 'all' | 'thread-start' | 'theme-select' | 'shortcuts';
|
|
7
|
+
type CommandMenuInitialGroup = 'agent-types' | 'actions';
|
|
8
|
+
|
|
9
|
+
const COMMAND_MENU_AGENT_TYPE_ORDER: Readonly<Record<string, number>> = {
|
|
10
|
+
codex: 0,
|
|
11
|
+
claude: 1,
|
|
12
|
+
cursor: 2,
|
|
13
|
+
terminal: 3,
|
|
14
|
+
critique: 4,
|
|
15
|
+
};
|
|
4
16
|
|
|
5
17
|
export interface CommandMenuState {
|
|
6
18
|
readonly scope: CommandMenuScope;
|
|
@@ -14,6 +26,10 @@ export interface CommandMenuActionDescriptor {
|
|
|
14
26
|
readonly aliases?: readonly string[];
|
|
15
27
|
readonly keywords?: readonly string[];
|
|
16
28
|
readonly detail?: string;
|
|
29
|
+
readonly screenLabel?: string;
|
|
30
|
+
readonly sectionLabel?: string;
|
|
31
|
+
readonly bindingHint?: string;
|
|
32
|
+
readonly priority?: number;
|
|
17
33
|
}
|
|
18
34
|
|
|
19
35
|
export function filterThemePresetActionsForScope<TAction extends CommandMenuActionDescriptor>(
|
|
@@ -27,6 +43,27 @@ export function filterThemePresetActionsForScope<TAction extends CommandMenuActi
|
|
|
27
43
|
return actions.filter((action) => !action.id.startsWith(themeActionIdPrefix));
|
|
28
44
|
}
|
|
29
45
|
|
|
46
|
+
export function filterCommandMenuActionsForScope<TAction extends CommandMenuActionDescriptor>(
|
|
47
|
+
actions: readonly TAction[],
|
|
48
|
+
scope: CommandMenuScope,
|
|
49
|
+
options: {
|
|
50
|
+
readonly themeActionIdPrefix: string;
|
|
51
|
+
readonly shortcutsActionIdPrefix: string;
|
|
52
|
+
},
|
|
53
|
+
): readonly TAction[] {
|
|
54
|
+
const { themeActionIdPrefix, shortcutsActionIdPrefix } = options;
|
|
55
|
+
if (scope === 'theme-select') {
|
|
56
|
+
return actions.filter((action) => action.id.startsWith(themeActionIdPrefix));
|
|
57
|
+
}
|
|
58
|
+
if (scope === 'shortcuts') {
|
|
59
|
+
return actions.filter((action) => action.id.startsWith(shortcutsActionIdPrefix));
|
|
60
|
+
}
|
|
61
|
+
return actions.filter(
|
|
62
|
+
(action) =>
|
|
63
|
+
!action.id.startsWith(themeActionIdPrefix) && !action.id.startsWith(shortcutsActionIdPrefix),
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
30
67
|
export interface RegisteredCommandMenuAction<TContext> extends CommandMenuActionDescriptor {
|
|
31
68
|
readonly when?: (context: TContext) => boolean;
|
|
32
69
|
readonly run: (context: TContext) => Promise<void> | void;
|
|
@@ -37,6 +74,20 @@ interface CommandMenuMatch<TAction extends CommandMenuActionDescriptor> {
|
|
|
37
74
|
readonly score: number;
|
|
38
75
|
}
|
|
39
76
|
|
|
77
|
+
interface CommandMenuDisplayEntry<TAction extends CommandMenuActionDescriptor> {
|
|
78
|
+
readonly absoluteIndex: number;
|
|
79
|
+
readonly action: TAction;
|
|
80
|
+
readonly score: number;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
interface CommandMenuPage<TAction extends CommandMenuActionDescriptor> {
|
|
84
|
+
readonly matches: readonly CommandMenuMatch<TAction>[];
|
|
85
|
+
readonly selectedIndex: number;
|
|
86
|
+
readonly pageStart: number;
|
|
87
|
+
readonly visibleMatches: readonly CommandMenuMatch<TAction>[];
|
|
88
|
+
readonly displayEntries: readonly CommandMenuDisplayEntry<TAction>[];
|
|
89
|
+
}
|
|
90
|
+
|
|
40
91
|
type CommandMenuActionProvider<TContext> = (
|
|
41
92
|
context: TContext,
|
|
42
93
|
) => readonly RegisteredCommandMenuAction<TContext>[];
|
|
@@ -50,6 +101,29 @@ function normalizeQuery(value: string): string {
|
|
|
50
101
|
return value.trim().toLowerCase().replace(/\s+/g, ' ');
|
|
51
102
|
}
|
|
52
103
|
|
|
104
|
+
function truncateCommandMenuSummary(value: string, maxChars: number): string {
|
|
105
|
+
if (value.length <= maxChars) {
|
|
106
|
+
return value;
|
|
107
|
+
}
|
|
108
|
+
if (maxChars <= 3) {
|
|
109
|
+
return value.slice(0, Math.max(1, maxChars));
|
|
110
|
+
}
|
|
111
|
+
return `${value.slice(0, maxChars - 3)}...`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function summarizeTaskForCommandMenu(body: string, title: string): string {
|
|
115
|
+
const bodyLines = body.split('\n').map((line) => line.trim());
|
|
116
|
+
const firstBodyLine = bodyLines.find((line) => line.length > 0) ?? '';
|
|
117
|
+
const fallbackTitle = title.trim();
|
|
118
|
+
const summarySource =
|
|
119
|
+
firstBodyLine.length > 0
|
|
120
|
+
? firstBodyLine
|
|
121
|
+
: fallbackTitle.length > 0
|
|
122
|
+
? fallbackTitle
|
|
123
|
+
: DEFAULT_TASK_SUMMARY;
|
|
124
|
+
return truncateCommandMenuSummary(summarySource, TASK_SUMMARY_MAX_CHARS);
|
|
125
|
+
}
|
|
126
|
+
|
|
53
127
|
function normalizedTokens(query: string): readonly string[] {
|
|
54
128
|
const normalized = normalizeQuery(query);
|
|
55
129
|
if (normalized.length === 0) {
|
|
@@ -79,6 +153,46 @@ function searchableParts(action: CommandMenuActionDescriptor): readonly string[]
|
|
|
79
153
|
return parts;
|
|
80
154
|
}
|
|
81
155
|
|
|
156
|
+
function threadAgentTypeFromActionId(actionId: string): string | null {
|
|
157
|
+
const match = THREAD_ACTION_ID_PATTERN.exec(actionId);
|
|
158
|
+
if (match === null) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
const value = (match[1] ?? '').trim().toLowerCase();
|
|
162
|
+
return value.length > 0 ? value : null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function initialGroupForAction(action: CommandMenuActionDescriptor): CommandMenuInitialGroup {
|
|
166
|
+
return threadAgentTypeFromActionId(action.id) === null ? 'actions' : 'agent-types';
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function initialGroupRank(action: CommandMenuActionDescriptor): number {
|
|
170
|
+
return initialGroupForAction(action) === 'agent-types' ? 0 : 1;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function initialAgentTypeRank(action: CommandMenuActionDescriptor): number {
|
|
174
|
+
const agentType = threadAgentTypeFromActionId(action.id);
|
|
175
|
+
if (agentType === null) {
|
|
176
|
+
return Number.MAX_SAFE_INTEGER;
|
|
177
|
+
}
|
|
178
|
+
return COMMAND_MENU_AGENT_TYPE_ORDER[agentType] ?? 100;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function usesInitialTypeSort(query: string): boolean {
|
|
182
|
+
return normalizedTokens(query).length === 0;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function buildCommandMenuDisplayEntries<TAction extends CommandMenuActionDescriptor>(
|
|
186
|
+
visibleMatches: readonly CommandMenuMatch<TAction>[],
|
|
187
|
+
pageStart: number,
|
|
188
|
+
): readonly CommandMenuDisplayEntry<TAction>[] {
|
|
189
|
+
return visibleMatches.map((match, index) => ({
|
|
190
|
+
absoluteIndex: pageStart + index,
|
|
191
|
+
action: match.action,
|
|
192
|
+
score: match.score,
|
|
193
|
+
}));
|
|
194
|
+
}
|
|
195
|
+
|
|
82
196
|
function actionScore(action: CommandMenuActionDescriptor, query: string): number | null {
|
|
83
197
|
const tokens = normalizedTokens(query);
|
|
84
198
|
if (tokens.length === 0) {
|
|
@@ -136,7 +250,11 @@ function clampSelectedIndex(selectedIndex: number, resultCount: number): number
|
|
|
136
250
|
return selectedIndex;
|
|
137
251
|
}
|
|
138
252
|
|
|
139
|
-
function moveSelectionByDelta(
|
|
253
|
+
export function moveSelectionByDelta(
|
|
254
|
+
selectedIndex: number,
|
|
255
|
+
resultCount: number,
|
|
256
|
+
delta: number,
|
|
257
|
+
): number {
|
|
140
258
|
if (resultCount <= 0) {
|
|
141
259
|
return 0;
|
|
142
260
|
}
|
|
@@ -183,15 +301,39 @@ export function resolveCommandMenuMatches<TAction extends CommandMenuActionDescr
|
|
|
183
301
|
query: string,
|
|
184
302
|
limit: number | null = COMMAND_MENU_MAX_RESULTS,
|
|
185
303
|
): readonly CommandMenuMatch<TAction>[] {
|
|
304
|
+
const useInitialSort = usesInitialTypeSort(query);
|
|
186
305
|
const scored = actions
|
|
187
306
|
.flatMap((action) => {
|
|
188
307
|
const score = actionScore(action, query);
|
|
189
308
|
return score === null ? [] : [{ action, score }];
|
|
190
309
|
})
|
|
191
310
|
.sort((left, right) => {
|
|
311
|
+
const priorityDelta = (right.action.priority ?? 0) - (left.action.priority ?? 0);
|
|
312
|
+
if (priorityDelta !== 0) {
|
|
313
|
+
return priorityDelta;
|
|
314
|
+
}
|
|
315
|
+
if (useInitialSort) {
|
|
316
|
+
const groupCompare = initialGroupRank(left.action) - initialGroupRank(right.action);
|
|
317
|
+
if (groupCompare !== 0) {
|
|
318
|
+
return groupCompare;
|
|
319
|
+
}
|
|
320
|
+
const leftGroup = initialGroupForAction(left.action);
|
|
321
|
+
if (leftGroup === 'agent-types') {
|
|
322
|
+
const agentCompare =
|
|
323
|
+
initialAgentTypeRank(left.action) - initialAgentTypeRank(right.action);
|
|
324
|
+
if (agentCompare !== 0) {
|
|
325
|
+
return agentCompare;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return left.action.title.localeCompare(right.action.title);
|
|
329
|
+
}
|
|
192
330
|
if (left.score !== right.score) {
|
|
193
331
|
return left.score - right.score;
|
|
194
332
|
}
|
|
333
|
+
const groupCompare = initialGroupRank(left.action) - initialGroupRank(right.action);
|
|
334
|
+
if (groupCompare !== 0) {
|
|
335
|
+
return groupCompare;
|
|
336
|
+
}
|
|
195
337
|
return left.action.title.localeCompare(right.action.title);
|
|
196
338
|
});
|
|
197
339
|
if (limit === null) {
|
|
@@ -200,6 +342,27 @@ export function resolveCommandMenuMatches<TAction extends CommandMenuActionDescr
|
|
|
200
342
|
return scored.slice(0, Math.max(0, limit));
|
|
201
343
|
}
|
|
202
344
|
|
|
345
|
+
export function resolveCommandMenuPage<TAction extends CommandMenuActionDescriptor>(
|
|
346
|
+
actions: readonly TAction[],
|
|
347
|
+
menu: CommandMenuState,
|
|
348
|
+
): CommandMenuPage<TAction> {
|
|
349
|
+
const matches = resolveCommandMenuMatches(actions, menu.query, null);
|
|
350
|
+
const selectedIndex = clampSelectedIndex(menu.selectedIndex, matches.length);
|
|
351
|
+
const pageStart =
|
|
352
|
+
matches.length === 0
|
|
353
|
+
? 0
|
|
354
|
+
: Math.floor(selectedIndex / COMMAND_MENU_MAX_RESULTS) * COMMAND_MENU_MAX_RESULTS;
|
|
355
|
+
const visibleMatches = matches.slice(pageStart, pageStart + COMMAND_MENU_MAX_RESULTS);
|
|
356
|
+
const displayEntries = buildCommandMenuDisplayEntries(visibleMatches, pageStart);
|
|
357
|
+
return {
|
|
358
|
+
matches,
|
|
359
|
+
selectedIndex,
|
|
360
|
+
pageStart,
|
|
361
|
+
visibleMatches,
|
|
362
|
+
displayEntries,
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
|
|
203
366
|
export function resolveSelectedCommandMenuActionId<TAction extends CommandMenuActionDescriptor>(
|
|
204
367
|
actions: readonly TAction[],
|
|
205
368
|
menu: CommandMenuState | null,
|
|
@@ -187,3 +187,16 @@ export function debugFooterForConversation(conversation: ConversationState): str
|
|
|
187
187
|
: compactDebugText(conversation.launchCommand);
|
|
188
188
|
return `[dbg] ${launchCommand}`;
|
|
189
189
|
}
|
|
190
|
+
|
|
191
|
+
export function composeDebugStatusFooter(
|
|
192
|
+
showDebugBar: boolean,
|
|
193
|
+
githubDebugTokens: string,
|
|
194
|
+
conversation: ConversationState,
|
|
195
|
+
): string {
|
|
196
|
+
if (!showDebugBar) {
|
|
197
|
+
return '';
|
|
198
|
+
}
|
|
199
|
+
const githubPrefix = githubDebugTokens.trim();
|
|
200
|
+
const debugFooter = debugFooterForConversation(conversation);
|
|
201
|
+
return githubPrefix.length === 0 ? debugFooter : `${githubPrefix} ${debugFooter}`;
|
|
202
|
+
}
|
|
@@ -3,7 +3,7 @@ interface ResolveDirectoryForActionConversationState {
|
|
|
3
3
|
}
|
|
4
4
|
|
|
5
5
|
interface ResolveDirectoryForActionOptions {
|
|
6
|
-
mainPaneMode: 'conversation' | 'project' | 'home';
|
|
6
|
+
mainPaneMode: 'conversation' | 'project' | 'home' | 'nim';
|
|
7
7
|
activeDirectoryId: string | null;
|
|
8
8
|
activeConversationId: string | null;
|
|
9
9
|
conversations: ReadonlyMap<string, ResolveDirectoryForActionConversationState>;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { execFile } from 'node:child_process';
|
|
2
|
+
import { basename } from 'node:path';
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
2
4
|
import { promisify } from 'node:util';
|
|
3
5
|
import {
|
|
4
6
|
normalizeGitHubRemoteUrl,
|
|
@@ -120,6 +122,24 @@ export async function runGitCommand(
|
|
|
120
122
|
}
|
|
121
123
|
}
|
|
122
124
|
|
|
125
|
+
function normalizeLocalRepositoryLocator(pathValue: string): string | null {
|
|
126
|
+
const trimmed = pathValue.trim();
|
|
127
|
+
if (trimmed.length === 0) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
return pathToFileURL(trimmed).toString();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function repositoryNameFromRepositoryLocator(locator: string): string | null {
|
|
134
|
+
const normalizedGitHub = normalizeGitHubRemoteUrl(locator);
|
|
135
|
+
if (normalizedGitHub !== null) {
|
|
136
|
+
return repositoryNameFromGitHubRemoteUrl(normalizedGitHub);
|
|
137
|
+
}
|
|
138
|
+
const resolvedPath = fileURLToPath(locator);
|
|
139
|
+
const inferred = basename(resolvedPath.trim());
|
|
140
|
+
return inferred.length > 0 ? inferred : null;
|
|
141
|
+
}
|
|
142
|
+
|
|
123
143
|
async function readNormalizedGitHubRemoteUrl(
|
|
124
144
|
cwd: string,
|
|
125
145
|
runCommand: GitCommandRunner,
|
|
@@ -146,6 +166,17 @@ async function readNormalizedGitHubRemoteUrl(
|
|
|
146
166
|
return null;
|
|
147
167
|
}
|
|
148
168
|
|
|
169
|
+
async function readRepositoryLocator(
|
|
170
|
+
cwd: string,
|
|
171
|
+
runCommand: GitCommandRunner,
|
|
172
|
+
): Promise<string | null> {
|
|
173
|
+
const normalizedGitHubRemoteUrl = await readNormalizedGitHubRemoteUrl(cwd, runCommand);
|
|
174
|
+
if (normalizedGitHubRemoteUrl !== null) {
|
|
175
|
+
return normalizedGitHubRemoteUrl;
|
|
176
|
+
}
|
|
177
|
+
return normalizeLocalRepositoryLocator(await runCommand(cwd, ['rev-parse', '--show-toplevel']));
|
|
178
|
+
}
|
|
179
|
+
|
|
149
180
|
export async function readGitDirectorySnapshot(
|
|
150
181
|
cwd: string,
|
|
151
182
|
runCommand: GitCommandRunner = runGitCommand,
|
|
@@ -162,7 +193,7 @@ export async function readGitDirectorySnapshot(
|
|
|
162
193
|
const statusOutputPromise = runCommand(cwd, ['status', '--porcelain=1', '--branch']);
|
|
163
194
|
const unstagedShortstatPromise = runCommand(cwd, ['diff', '--shortstat']);
|
|
164
195
|
const stagedShortstatPromise = runCommand(cwd, ['diff', '--cached', '--shortstat']);
|
|
165
|
-
const remoteUrlPromise =
|
|
196
|
+
const remoteUrlPromise = readRepositoryLocator(cwd, runCommand);
|
|
166
197
|
const lastCommitPromise = runCommand(cwd, ['log', '-1', '--format=%ct %h']);
|
|
167
198
|
const commitCountPromise =
|
|
168
199
|
options.includeCommitCount === false
|
|
@@ -209,7 +240,7 @@ export async function readGitDirectorySnapshot(
|
|
|
209
240
|
inferredName:
|
|
210
241
|
normalizedRemoteUrl === null
|
|
211
242
|
? null
|
|
212
|
-
:
|
|
243
|
+
: repositoryNameFromRepositoryLocator(normalizedRemoteUrl),
|
|
213
244
|
defaultBranch: branch === '(detached)' ? null : branch,
|
|
214
245
|
},
|
|
215
246
|
};
|
|
@@ -6,6 +6,7 @@ interface HandleGlobalShortcutOptions {
|
|
|
6
6
|
resolveDirectoryForAction: () => string | null;
|
|
7
7
|
openNewThreadPrompt: (directoryId: string) => void;
|
|
8
8
|
toggleCommandMenu: () => void;
|
|
9
|
+
toggleDebugBar: () => void;
|
|
9
10
|
openOrCreateCritiqueConversationInDirectory: (directoryId: string) => Promise<void>;
|
|
10
11
|
toggleGatewayProfile: () => Promise<void>;
|
|
11
12
|
toggleGatewayStatusTimeline: () => Promise<void>;
|
|
@@ -30,6 +31,7 @@ export function handleGlobalShortcut(options: HandleGlobalShortcutOptions): bool
|
|
|
30
31
|
resolveDirectoryForAction,
|
|
31
32
|
openNewThreadPrompt,
|
|
32
33
|
toggleCommandMenu,
|
|
34
|
+
toggleDebugBar,
|
|
33
35
|
openOrCreateCritiqueConversationInDirectory,
|
|
34
36
|
toggleGatewayProfile,
|
|
35
37
|
toggleGatewayStatusTimeline,
|
|
@@ -64,6 +66,10 @@ export function handleGlobalShortcut(options: HandleGlobalShortcutOptions): bool
|
|
|
64
66
|
toggleCommandMenu();
|
|
65
67
|
return true;
|
|
66
68
|
}
|
|
69
|
+
if (shortcut === 'mux.debug-bar.toggle') {
|
|
70
|
+
toggleDebugBar();
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
67
73
|
if (shortcut === 'mux.conversation.critique.open-or-create') {
|
|
68
74
|
const targetDirectoryId = resolveDirectoryForAction();
|
|
69
75
|
if (targetDirectoryId !== null) {
|
|
@@ -9,7 +9,7 @@ interface HomePaneDragState {
|
|
|
9
9
|
interface HandleHomePaneDragReleaseOptions {
|
|
10
10
|
homePaneDragState: HomePaneDragState | null;
|
|
11
11
|
isMouseRelease: boolean;
|
|
12
|
-
mainPaneMode: 'conversation' | 'project' | 'home';
|
|
12
|
+
mainPaneMode: 'conversation' | 'project' | 'home' | 'nim';
|
|
13
13
|
target: string;
|
|
14
14
|
rowIndex: number;
|
|
15
15
|
taskIdAtRow: (rowIndex: number) => string | null;
|
|
@@ -30,6 +30,7 @@ interface HandleHomePanePointerClickOptions {
|
|
|
30
30
|
setTaskRepositoryDropdownOpen: (open: boolean) => void;
|
|
31
31
|
taskIdAtRow: (rowIndex: number) => string | null;
|
|
32
32
|
repositoryIdAtRow: (rowIndex: number) => string | null;
|
|
33
|
+
rowTextAtRow?: (rowIndex: number) => string | null;
|
|
33
34
|
selectTaskById: (taskId: string) => void;
|
|
34
35
|
selectRepositoryById: (repositoryId: string) => void;
|
|
35
36
|
runTaskPaneAction: (action: 'task.ready' | 'task.draft' | 'task.complete') => void;
|
|
@@ -55,6 +56,15 @@ export function handleHomePanePointerClick(options: HandleHomePanePointerClickOp
|
|
|
55
56
|
0,
|
|
56
57
|
Math.min(options.rightCols - 1, options.pointerCol - options.rightStartCol),
|
|
57
58
|
);
|
|
59
|
+
const rowText = options.rowTextAtRow?.(rowIndex) ?? null;
|
|
60
|
+
const isTaskEditorContentRow =
|
|
61
|
+
options.taskIdAtRow(rowIndex) !== null &&
|
|
62
|
+
rowText !== null &&
|
|
63
|
+
rowText.trimEnd().startsWith(' │') &&
|
|
64
|
+
rowText.trimEnd().includes('│');
|
|
65
|
+
if (isTaskEditorContentRow) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
58
68
|
const action = options.actionAtCell(rowIndex, colIndex) ?? options.actionAtRow(rowIndex);
|
|
59
69
|
if (
|
|
60
70
|
handleHomePaneActionClick({
|