@jmoyers/harness 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +145 -0
- package/native/ptyd/Cargo.lock +16 -0
- package/native/ptyd/Cargo.toml +7 -0
- package/native/ptyd/src/main.rs +257 -0
- package/package.json +90 -0
- package/scripts/build-ptyd.sh +73 -0
- package/scripts/control-plane-daemon.ts +277 -0
- package/scripts/cursor-hook-relay.ts +82 -0
- package/scripts/harness-animate.ts +469 -0
- package/scripts/harness-bin.js +77 -0
- package/scripts/harness-core.ts +1 -0
- package/scripts/harness-inspector.ts +439 -0
- package/scripts/harness.ts +2493 -0
- package/src/adapters/agent-session-state.ts +390 -0
- package/src/cli/gateway-record.ts +173 -0
- package/src/codex/live-session.ts +872 -0
- package/src/config/config-core.ts +1359 -0
- package/src/config/secrets-core.ts +170 -0
- package/src/control-plane/agent-realtime-api.ts +2441 -0
- package/src/control-plane/codex-session-stream.ts +392 -0
- package/src/control-plane/codex-telemetry.ts +1325 -0
- package/src/control-plane/lifecycle-hooks.ts +706 -0
- package/src/control-plane/session-summary.ts +380 -0
- package/src/control-plane/status/agent-status-reducer.ts +21 -0
- package/src/control-plane/status/reducer-base.ts +170 -0
- package/src/control-plane/status/reducers/claude-status-reducer.ts +37 -0
- package/src/control-plane/status/reducers/codex-status-reducer.ts +48 -0
- package/src/control-plane/status/reducers/critique-status-reducer.ts +15 -0
- package/src/control-plane/status/reducers/cursor-status-reducer.ts +37 -0
- package/src/control-plane/status/reducers/terminal-status-reducer.ts +15 -0
- package/src/control-plane/status/session-status-engine.ts +76 -0
- package/src/control-plane/stream-client.ts +396 -0
- package/src/control-plane/stream-command-parser.ts +1673 -0
- package/src/control-plane/stream-protocol.ts +1808 -0
- package/src/control-plane/stream-server-background.ts +486 -0
- package/src/control-plane/stream-server-command.ts +2557 -0
- package/src/control-plane/stream-server-connection.ts +234 -0
- package/src/control-plane/stream-server-observed-filter.ts +112 -0
- package/src/control-plane/stream-server-session-runtime.ts +566 -0
- package/src/control-plane/stream-server-state-store.ts +15 -0
- package/src/control-plane/stream-server.ts +3192 -0
- package/src/cursor/managed-hooks.ts +282 -0
- package/src/domain/conversations.ts +414 -0
- package/src/domain/directories.ts +78 -0
- package/src/domain/repositories.ts +123 -0
- package/src/domain/tasks.ts +148 -0
- package/src/domain/workspace.ts +156 -0
- package/src/events/normalized-events.ts +124 -0
- package/src/mux/ansi-integrity.ts +103 -0
- package/src/mux/control-plane-op-queue.ts +212 -0
- package/src/mux/conversation-rail.ts +339 -0
- package/src/mux/double-click.ts +78 -0
- package/src/mux/dual-pane-core.ts +435 -0
- package/src/mux/harness-core-ui.ts +817 -0
- package/src/mux/input-shortcuts.ts +667 -0
- package/src/mux/live-mux/actions-conversation.ts +344 -0
- package/src/mux/live-mux/actions-repository.ts +246 -0
- package/src/mux/live-mux/actions-task.ts +115 -0
- package/src/mux/live-mux/args.ts +142 -0
- package/src/mux/live-mux/command-menu.ts +298 -0
- package/src/mux/live-mux/control-plane-records.ts +546 -0
- package/src/mux/live-mux/conversation-state.ts +188 -0
- package/src/mux/live-mux/directory-resolution.ts +34 -0
- package/src/mux/live-mux/event-mapping.ts +96 -0
- package/src/mux/live-mux/gateway-profiler.ts +152 -0
- package/src/mux/live-mux/gateway-render-trace.ts +177 -0
- package/src/mux/live-mux/gateway-status-timeline.ts +166 -0
- package/src/mux/live-mux/git-parsing.ts +131 -0
- package/src/mux/live-mux/git-snapshot.ts +263 -0
- package/src/mux/live-mux/git-state.ts +136 -0
- package/src/mux/live-mux/global-shortcut-handlers.ts +143 -0
- package/src/mux/live-mux/home-pane-actions.ts +58 -0
- package/src/mux/live-mux/home-pane-drop.ts +44 -0
- package/src/mux/live-mux/home-pane-entity-click.ts +96 -0
- package/src/mux/live-mux/home-pane-pointer.ts +96 -0
- package/src/mux/live-mux/input-forwarding.ts +112 -0
- package/src/mux/live-mux/layout.ts +30 -0
- package/src/mux/live-mux/left-nav-activation.ts +103 -0
- package/src/mux/live-mux/left-nav.ts +85 -0
- package/src/mux/live-mux/left-rail-actions.ts +118 -0
- package/src/mux/live-mux/left-rail-conversation-click.ts +82 -0
- package/src/mux/live-mux/left-rail-pointer.ts +74 -0
- package/src/mux/live-mux/modal-command-menu-handler.ts +101 -0
- package/src/mux/live-mux/modal-conversation-handlers.ts +217 -0
- package/src/mux/live-mux/modal-input-reducers.ts +94 -0
- package/src/mux/live-mux/modal-overlays.ts +287 -0
- package/src/mux/live-mux/modal-pointer.ts +70 -0
- package/src/mux/live-mux/modal-prompt-handlers.ts +187 -0
- package/src/mux/live-mux/modal-task-editor-handler.ts +156 -0
- package/src/mux/live-mux/observed-stream.ts +87 -0
- package/src/mux/live-mux/palette-parsing.ts +128 -0
- package/src/mux/live-mux/pointer-routing.ts +108 -0
- package/src/mux/live-mux/process-usage.ts +53 -0
- package/src/mux/live-mux/project-pane-pointer.ts +44 -0
- package/src/mux/live-mux/rail-layout.ts +244 -0
- package/src/mux/live-mux/render-trace-analysis.ts +213 -0
- package/src/mux/live-mux/render-trace-state.ts +84 -0
- package/src/mux/live-mux/repository-folding.ts +207 -0
- package/src/mux/live-mux/runtime-shutdown.ts +51 -0
- package/src/mux/live-mux/selection.ts +411 -0
- package/src/mux/live-mux/startup-utils.ts +187 -0
- package/src/mux/live-mux/status-timeline-state.ts +82 -0
- package/src/mux/live-mux/task-pane-shortcuts.ts +206 -0
- package/src/mux/live-mux/terminal-palette.ts +79 -0
- package/src/mux/new-thread-prompt.ts +165 -0
- package/src/mux/project-tree.ts +295 -0
- package/src/mux/render-frame.ts +113 -0
- package/src/mux/runtime-wiring.ts +185 -0
- package/src/mux/selector-index.ts +160 -0
- package/src/mux/startup-sequencer.ts +238 -0
- package/src/mux/task-composer.ts +289 -0
- package/src/mux/task-focused-pane.ts +417 -0
- package/src/mux/task-screen-keybindings.ts +539 -0
- package/src/mux/terminal-input-modes.ts +35 -0
- package/src/mux/workspace-path.ts +55 -0
- package/src/mux/workspace-rail-model.ts +701 -0
- package/src/mux/workspace-rail.ts +247 -0
- package/src/perf/perf-core.ts +307 -0
- package/src/pty/pty_host.ts +217 -0
- package/src/pty/session-broker.ts +158 -0
- package/src/recording/terminal-recording.ts +383 -0
- package/src/services/control-plane.ts +567 -0
- package/src/services/conversation-lifecycle.ts +176 -0
- package/src/services/conversation-startup-hydration.ts +47 -0
- package/src/services/directory-hydration.ts +49 -0
- package/src/services/event-persistence.ts +104 -0
- package/src/services/mux-ui-state-persistence.ts +82 -0
- package/src/services/output-load-sampler.ts +231 -0
- package/src/services/process-usage-refresh.ts +88 -0
- package/src/services/recording.ts +75 -0
- package/src/services/render-trace-recorder.ts +177 -0
- package/src/services/runtime-control-actions.ts +123 -0
- package/src/services/runtime-control-plane-ops.ts +131 -0
- package/src/services/runtime-conversation-actions.ts +113 -0
- package/src/services/runtime-conversation-activation.ts +78 -0
- package/src/services/runtime-conversation-starter.ts +171 -0
- package/src/services/runtime-conversation-title-edit.ts +149 -0
- package/src/services/runtime-directory-actions.ts +164 -0
- package/src/services/runtime-envelope-handler.ts +198 -0
- package/src/services/runtime-git-state.ts +92 -0
- package/src/services/runtime-input-pipeline.ts +50 -0
- package/src/services/runtime-input-router.ts +202 -0
- package/src/services/runtime-layout-resize.ts +236 -0
- package/src/services/runtime-left-rail-render.ts +159 -0
- package/src/services/runtime-main-pane-input.ts +230 -0
- package/src/services/runtime-modal-input.ts +119 -0
- package/src/services/runtime-navigation-input.ts +207 -0
- package/src/services/runtime-process-wiring.ts +68 -0
- package/src/services/runtime-rail-input.ts +287 -0
- package/src/services/runtime-render-flush.ts +146 -0
- package/src/services/runtime-render-lifecycle.ts +104 -0
- package/src/services/runtime-render-orchestrator.ts +108 -0
- package/src/services/runtime-render-pipeline.ts +167 -0
- package/src/services/runtime-render-state.ts +72 -0
- package/src/services/runtime-repository-actions.ts +197 -0
- package/src/services/runtime-right-pane-render.ts +132 -0
- package/src/services/runtime-shutdown.ts +79 -0
- package/src/services/runtime-stream-subscriptions.ts +56 -0
- package/src/services/runtime-task-composer-persistence.ts +139 -0
- package/src/services/runtime-task-editor-actions.ts +83 -0
- package/src/services/runtime-task-pane-actions.ts +198 -0
- package/src/services/runtime-task-pane-shortcuts.ts +189 -0
- package/src/services/runtime-task-pane.ts +62 -0
- package/src/services/runtime-workspace-actions.ts +153 -0
- package/src/services/runtime-workspace-observed-events.ts +190 -0
- package/src/services/session-projection-instrumentation.ts +190 -0
- package/src/services/startup-background-probe.ts +91 -0
- package/src/services/startup-background-resume.ts +65 -0
- package/src/services/startup-orchestrator.ts +166 -0
- package/src/services/startup-output-tracker.ts +54 -0
- package/src/services/startup-paint-tracker.ts +115 -0
- package/src/services/startup-persisted-conversation-queue.ts +45 -0
- package/src/services/startup-settled-gate.ts +67 -0
- package/src/services/startup-shutdown.ts +53 -0
- package/src/services/startup-span-tracker.ts +77 -0
- package/src/services/startup-state-hydration.ts +94 -0
- package/src/services/startup-visibility.ts +35 -0
- package/src/services/status-timeline-recorder.ts +144 -0
- package/src/services/task-pane-selection-actions.ts +153 -0
- package/src/services/task-planning-hydration.ts +58 -0
- package/src/services/task-planning-observed-events.ts +89 -0
- package/src/services/workspace-observed-events.ts +113 -0
- package/src/store/control-plane-store-normalize.ts +760 -0
- package/src/store/control-plane-store-types.ts +224 -0
- package/src/store/control-plane-store.ts +2951 -0
- package/src/store/event-store.ts +253 -0
- package/src/store/sqlite.ts +81 -0
- package/src/terminal/compat-matrix.ts +345 -0
- package/src/terminal/differential-checkpoints.ts +132 -0
- package/src/terminal/parity-suite.ts +441 -0
- package/src/terminal/snapshot-oracle.ts +1840 -0
- package/src/ui/conversation-input-forwarder.ts +114 -0
- package/src/ui/conversation-selection-input.ts +103 -0
- package/src/ui/debug-footer-notice.ts +39 -0
- package/src/ui/global-shortcut-input.ts +126 -0
- package/src/ui/input-preflight.ts +68 -0
- package/src/ui/input-token-router.ts +312 -0
- package/src/ui/input.ts +238 -0
- package/src/ui/kit.ts +509 -0
- package/src/ui/left-nav-input.ts +80 -0
- package/src/ui/left-rail-pointer-input.ts +148 -0
- package/src/ui/main-pane-pointer-input.ts +150 -0
- package/src/ui/modals/manager.ts +192 -0
- package/src/ui/mux-theme.ts +529 -0
- package/src/ui/panes/conversation.ts +19 -0
- package/src/ui/panes/home-gridfire.ts +302 -0
- package/src/ui/panes/home.ts +109 -0
- package/src/ui/panes/left-rail.ts +12 -0
- package/src/ui/panes/project.ts +44 -0
- package/src/ui/pointer-routing-input.ts +158 -0
- package/src/ui/repository-fold-input.ts +91 -0
- package/src/ui/screen.ts +210 -0
- package/src/ui/surface.ts +224 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { WorkspaceModel } from '../domain/workspace.ts';
|
|
2
|
+
import type { CommandMenuActionDescriptor } from '../mux/live-mux/command-menu.ts';
|
|
3
|
+
import { InputRouter } from '../ui/input.ts';
|
|
4
|
+
|
|
5
|
+
type InputRouterOptions = ConstructorParameters<typeof InputRouter>[0];
|
|
6
|
+
|
|
7
|
+
interface RuntimeModalInputWorkspaceActions {
|
|
8
|
+
archiveConversation(sessionId: string): Promise<void>;
|
|
9
|
+
createAndActivateConversationInDirectory(directoryId: string, agentType: string): Promise<void>;
|
|
10
|
+
addDirectoryByPath(rawPath: string): Promise<void>;
|
|
11
|
+
upsertRepositoryByRemoteUrl(remoteUrl: string, existingRepositoryId?: string): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface RuntimeModalInputTaskEditorActions {
|
|
15
|
+
submitTaskEditorPayload(
|
|
16
|
+
payload: Parameters<InputRouterOptions['submitTaskEditorPayload']>[0],
|
|
17
|
+
): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface RuntimeModalInputOptions {
|
|
21
|
+
readonly workspace: WorkspaceModel;
|
|
22
|
+
readonly conversations: ReadonlyMap<string, { title: string }>;
|
|
23
|
+
readonly workspaceActions: RuntimeModalInputWorkspaceActions;
|
|
24
|
+
readonly taskEditorActions: RuntimeModalInputTaskEditorActions;
|
|
25
|
+
readonly isModalDismissShortcut: (input: Buffer) => boolean;
|
|
26
|
+
readonly isCommandMenuToggleShortcut: (input: Buffer) => boolean;
|
|
27
|
+
readonly isArchiveConversationShortcut: (input: Buffer) => boolean;
|
|
28
|
+
readonly dismissOnOutsideClick: InputRouterOptions['dismissOnOutsideClick'];
|
|
29
|
+
readonly buildCommandMenuModalOverlay: InputRouterOptions['buildCommandMenuModalOverlay'];
|
|
30
|
+
readonly buildConversationTitleModalOverlay: InputRouterOptions['buildConversationTitleModalOverlay'];
|
|
31
|
+
readonly buildNewThreadModalOverlay: InputRouterOptions['buildNewThreadModalOverlay'];
|
|
32
|
+
readonly resolveNewThreadPromptAgentByRow: InputRouterOptions['resolveNewThreadPromptAgentByRow'];
|
|
33
|
+
readonly stopConversationTitleEdit: (persistPending: boolean) => void;
|
|
34
|
+
readonly queueControlPlaneOp: (task: () => Promise<void>, label: string) => void;
|
|
35
|
+
readonly normalizeGitHubRemoteUrl: (remoteUrl: string) => string | null;
|
|
36
|
+
readonly repositoriesHas: (repositoryId: string) => boolean;
|
|
37
|
+
readonly scheduleConversationTitlePersist: () => void;
|
|
38
|
+
readonly resolveCommandMenuActions: () => readonly CommandMenuActionDescriptor[];
|
|
39
|
+
readonly executeCommandMenuAction: (actionId: string) => void;
|
|
40
|
+
readonly markDirty: () => void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface RuntimeModalInputDependencies {
|
|
44
|
+
readonly createInputRouter?: (
|
|
45
|
+
options: InputRouterOptions,
|
|
46
|
+
) => Pick<InputRouter, 'routeModalInput'>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class RuntimeModalInput {
|
|
50
|
+
private readonly inputRouter: Pick<InputRouter, 'routeModalInput'>;
|
|
51
|
+
|
|
52
|
+
constructor(options: RuntimeModalInputOptions, dependencies: RuntimeModalInputDependencies = {}) {
|
|
53
|
+
const createInputRouter =
|
|
54
|
+
dependencies.createInputRouter ??
|
|
55
|
+
((routerOptions: InputRouterOptions) => new InputRouter(routerOptions));
|
|
56
|
+
this.inputRouter = createInputRouter({
|
|
57
|
+
isModalDismissShortcut: options.isModalDismissShortcut,
|
|
58
|
+
isCommandMenuToggleShortcut: options.isCommandMenuToggleShortcut,
|
|
59
|
+
isArchiveConversationShortcut: options.isArchiveConversationShortcut,
|
|
60
|
+
dismissOnOutsideClick: options.dismissOnOutsideClick,
|
|
61
|
+
buildCommandMenuModalOverlay: options.buildCommandMenuModalOverlay,
|
|
62
|
+
buildConversationTitleModalOverlay: options.buildConversationTitleModalOverlay,
|
|
63
|
+
buildNewThreadModalOverlay: options.buildNewThreadModalOverlay,
|
|
64
|
+
resolveNewThreadPromptAgentByRow: options.resolveNewThreadPromptAgentByRow,
|
|
65
|
+
stopConversationTitleEdit: options.stopConversationTitleEdit,
|
|
66
|
+
queueControlPlaneOp: options.queueControlPlaneOp,
|
|
67
|
+
archiveConversation: async (sessionId) => {
|
|
68
|
+
await options.workspaceActions.archiveConversation(sessionId);
|
|
69
|
+
},
|
|
70
|
+
createAndActivateConversationInDirectory: async (directoryId, agentType) => {
|
|
71
|
+
await options.workspaceActions.createAndActivateConversationInDirectory(
|
|
72
|
+
directoryId,
|
|
73
|
+
agentType,
|
|
74
|
+
);
|
|
75
|
+
},
|
|
76
|
+
addDirectoryByPath: async (rawPath) => {
|
|
77
|
+
await options.workspaceActions.addDirectoryByPath(rawPath);
|
|
78
|
+
},
|
|
79
|
+
normalizeGitHubRemoteUrl: options.normalizeGitHubRemoteUrl,
|
|
80
|
+
upsertRepositoryByRemoteUrl: async (remoteUrl, existingRepositoryId) => {
|
|
81
|
+
await options.workspaceActions.upsertRepositoryByRemoteUrl(remoteUrl, existingRepositoryId);
|
|
82
|
+
},
|
|
83
|
+
repositoriesHas: options.repositoriesHas,
|
|
84
|
+
markDirty: options.markDirty,
|
|
85
|
+
conversations: options.conversations,
|
|
86
|
+
scheduleConversationTitlePersist: options.scheduleConversationTitlePersist,
|
|
87
|
+
getCommandMenu: () => options.workspace.commandMenu,
|
|
88
|
+
setCommandMenu: (menu) => {
|
|
89
|
+
options.workspace.commandMenu = menu;
|
|
90
|
+
},
|
|
91
|
+
resolveCommandMenuActions: options.resolveCommandMenuActions,
|
|
92
|
+
executeCommandMenuAction: options.executeCommandMenuAction,
|
|
93
|
+
getTaskEditorPrompt: () => options.workspace.taskEditorPrompt,
|
|
94
|
+
setTaskEditorPrompt: (next) => {
|
|
95
|
+
options.workspace.taskEditorPrompt = next;
|
|
96
|
+
},
|
|
97
|
+
submitTaskEditorPayload: (payload) => {
|
|
98
|
+
options.taskEditorActions.submitTaskEditorPayload(payload);
|
|
99
|
+
},
|
|
100
|
+
getConversationTitleEdit: () => options.workspace.conversationTitleEdit,
|
|
101
|
+
getNewThreadPrompt: () => options.workspace.newThreadPrompt,
|
|
102
|
+
setNewThreadPrompt: (prompt) => {
|
|
103
|
+
options.workspace.newThreadPrompt = prompt;
|
|
104
|
+
},
|
|
105
|
+
getAddDirectoryPrompt: () => options.workspace.addDirectoryPrompt,
|
|
106
|
+
setAddDirectoryPrompt: (next) => {
|
|
107
|
+
options.workspace.addDirectoryPrompt = next;
|
|
108
|
+
},
|
|
109
|
+
getRepositoryPrompt: () => options.workspace.repositoryPrompt,
|
|
110
|
+
setRepositoryPrompt: (next) => {
|
|
111
|
+
options.workspace.repositoryPrompt = next;
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
routeModalInput(input: Buffer): boolean {
|
|
117
|
+
return this.inputRouter.routeModalInput(input);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import type { WorkspaceModel } from '../domain/workspace.ts';
|
|
2
|
+
import type { resolveMuxShortcutBindings } from '../mux/input-shortcuts.ts';
|
|
3
|
+
import { GlobalShortcutInput } from '../ui/global-shortcut-input.ts';
|
|
4
|
+
import { LeftNavInput } from '../ui/left-nav-input.ts';
|
|
5
|
+
import { RepositoryFoldInput } from '../ui/repository-fold-input.ts';
|
|
6
|
+
|
|
7
|
+
type LeftNavInputOptions = ConstructorParameters<typeof LeftNavInput>[0];
|
|
8
|
+
type RepositoryFoldInputOptions = ConstructorParameters<typeof RepositoryFoldInput>[0];
|
|
9
|
+
type GlobalShortcutInputOptions = ConstructorParameters<typeof GlobalShortcutInput>[0];
|
|
10
|
+
|
|
11
|
+
type MainPaneMode = 'conversation' | 'project' | 'home';
|
|
12
|
+
|
|
13
|
+
interface RuntimeNavigationWorkspaceActions {
|
|
14
|
+
activateConversation(sessionId: string): Promise<void>;
|
|
15
|
+
openOrCreateCritiqueConversationInDirectory(directoryId: string): Promise<void>;
|
|
16
|
+
toggleGatewayProfiler(): Promise<void>;
|
|
17
|
+
toggleGatewayStatusTimeline(): Promise<void>;
|
|
18
|
+
toggleGatewayRenderTrace(conversationId: string | null): Promise<void>;
|
|
19
|
+
archiveConversation(sessionId: string): Promise<void>;
|
|
20
|
+
interruptConversation(sessionId: string): Promise<void>;
|
|
21
|
+
takeoverConversation(sessionId: string): Promise<void>;
|
|
22
|
+
closeDirectory(directoryId: string): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface RuntimeNavigationInputOptions {
|
|
26
|
+
readonly workspace: WorkspaceModel;
|
|
27
|
+
readonly shortcutBindings: ReturnType<typeof resolveMuxShortcutBindings>;
|
|
28
|
+
readonly requestStop: () => void;
|
|
29
|
+
readonly resolveDirectoryForAction: () => string | null;
|
|
30
|
+
readonly openNewThreadPrompt: (directoryId: string) => void;
|
|
31
|
+
readonly toggleCommandMenu: () => void;
|
|
32
|
+
readonly openAddDirectoryPrompt: () => void;
|
|
33
|
+
readonly queueControlPlaneOp: (task: () => Promise<void>, label: string) => void;
|
|
34
|
+
readonly firstDirectoryForRepositoryGroup: (repositoryGroupId: string) => string | null;
|
|
35
|
+
readonly enterHomePane: () => void;
|
|
36
|
+
readonly enterProjectPane: (directoryId: string) => void;
|
|
37
|
+
readonly markDirty: () => void;
|
|
38
|
+
readonly conversations: ReadonlyMap<string, { directoryId: string | null }>;
|
|
39
|
+
readonly repositoryGroupIdForDirectory: (directoryId: string) => string;
|
|
40
|
+
readonly collapseRepositoryGroup: (repositoryGroupId: string) => void;
|
|
41
|
+
readonly expandRepositoryGroup: (repositoryGroupId: string) => void;
|
|
42
|
+
readonly collapseAllRepositoryGroups: () => void;
|
|
43
|
+
readonly expandAllRepositoryGroups: () => void;
|
|
44
|
+
readonly directoriesHas: (directoryId: string) => boolean;
|
|
45
|
+
readonly conversationDirectoryId: (sessionId: string) => string | null;
|
|
46
|
+
readonly conversationsHas: (sessionId: string) => boolean;
|
|
47
|
+
readonly getMainPaneMode: () => MainPaneMode;
|
|
48
|
+
readonly getActiveConversationId: () => string | null;
|
|
49
|
+
readonly getActiveDirectoryId: () => string | null;
|
|
50
|
+
readonly forwardInterruptAllToActiveConversation?: (input: Buffer) => boolean;
|
|
51
|
+
readonly interruptAllDoubleTapWindowMs?: number;
|
|
52
|
+
readonly workspaceActions: RuntimeNavigationWorkspaceActions;
|
|
53
|
+
readonly chordTimeoutMs: number;
|
|
54
|
+
readonly collapseAllChordPrefix: Buffer;
|
|
55
|
+
readonly nowMs?: () => number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface RuntimeNavigationInputDependencies {
|
|
59
|
+
readonly createLeftNavInput?: (
|
|
60
|
+
options: LeftNavInputOptions,
|
|
61
|
+
) => Pick<LeftNavInput, 'cycleSelection'>;
|
|
62
|
+
readonly createRepositoryFoldInput?: (
|
|
63
|
+
options: RepositoryFoldInputOptions,
|
|
64
|
+
) => Pick<RepositoryFoldInput, 'handleRepositoryFoldChords' | 'handleRepositoryTreeArrow'>;
|
|
65
|
+
readonly createGlobalShortcutInput?: (
|
|
66
|
+
options: GlobalShortcutInputOptions,
|
|
67
|
+
) => Pick<GlobalShortcutInput, 'handleInput'>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export class RuntimeNavigationInput {
|
|
71
|
+
private readonly leftNavInput: Pick<LeftNavInput, 'cycleSelection'>;
|
|
72
|
+
private readonly repositoryFoldInput: Pick<
|
|
73
|
+
RepositoryFoldInput,
|
|
74
|
+
'handleRepositoryFoldChords' | 'handleRepositoryTreeArrow'
|
|
75
|
+
>;
|
|
76
|
+
private readonly globalShortcutInput: Pick<GlobalShortcutInput, 'handleInput'>;
|
|
77
|
+
|
|
78
|
+
constructor(
|
|
79
|
+
options: RuntimeNavigationInputOptions,
|
|
80
|
+
dependencies: RuntimeNavigationInputDependencies = {},
|
|
81
|
+
) {
|
|
82
|
+
const nowMs = options.nowMs ?? (() => Date.now());
|
|
83
|
+
const createLeftNavInput =
|
|
84
|
+
dependencies.createLeftNavInput ??
|
|
85
|
+
((leftNavOptions: LeftNavInputOptions) => new LeftNavInput(leftNavOptions));
|
|
86
|
+
const createRepositoryFoldInput =
|
|
87
|
+
dependencies.createRepositoryFoldInput ??
|
|
88
|
+
((repositoryFoldOptions: RepositoryFoldInputOptions) =>
|
|
89
|
+
new RepositoryFoldInput(repositoryFoldOptions));
|
|
90
|
+
const createGlobalShortcutInput =
|
|
91
|
+
dependencies.createGlobalShortcutInput ??
|
|
92
|
+
((globalShortcutOptions: GlobalShortcutInputOptions) =>
|
|
93
|
+
new GlobalShortcutInput(globalShortcutOptions));
|
|
94
|
+
const selectLeftNavRepository = options.workspace.selectLeftNavRepository.bind(
|
|
95
|
+
options.workspace,
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
this.leftNavInput = createLeftNavInput({
|
|
99
|
+
getLatestRailRows: () => options.workspace.latestRailViewRows,
|
|
100
|
+
getCurrentSelection: () => options.workspace.leftNavSelection,
|
|
101
|
+
enterHomePane: options.enterHomePane,
|
|
102
|
+
firstDirectoryForRepositoryGroup: options.firstDirectoryForRepositoryGroup,
|
|
103
|
+
enterProjectPane: options.enterProjectPane,
|
|
104
|
+
setMainPaneProjectMode: () => {
|
|
105
|
+
options.workspace.mainPaneMode = 'project';
|
|
106
|
+
},
|
|
107
|
+
selectLeftNavRepository,
|
|
108
|
+
markDirty: options.markDirty,
|
|
109
|
+
directoriesHas: options.directoriesHas,
|
|
110
|
+
conversationDirectoryId: options.conversationDirectoryId,
|
|
111
|
+
queueControlPlaneOp: options.queueControlPlaneOp,
|
|
112
|
+
activateConversation: async (sessionId) => {
|
|
113
|
+
await options.workspaceActions.activateConversation(sessionId);
|
|
114
|
+
},
|
|
115
|
+
conversationsHas: options.conversationsHas,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
this.repositoryFoldInput = createRepositoryFoldInput({
|
|
119
|
+
getLeftNavSelection: () => options.workspace.leftNavSelection,
|
|
120
|
+
getRepositoryToggleChordPrefixAtMs: () => options.workspace.repositoryToggleChordPrefixAtMs,
|
|
121
|
+
setRepositoryToggleChordPrefixAtMs: (value) => {
|
|
122
|
+
options.workspace.repositoryToggleChordPrefixAtMs = value;
|
|
123
|
+
},
|
|
124
|
+
conversations: options.conversations,
|
|
125
|
+
repositoryGroupIdForDirectory: options.repositoryGroupIdForDirectory,
|
|
126
|
+
collapseRepositoryGroup: options.collapseRepositoryGroup,
|
|
127
|
+
expandRepositoryGroup: options.expandRepositoryGroup,
|
|
128
|
+
collapseAllRepositoryGroups: options.collapseAllRepositoryGroups,
|
|
129
|
+
expandAllRepositoryGroups: options.expandAllRepositoryGroups,
|
|
130
|
+
selectLeftNavRepository,
|
|
131
|
+
markDirty: options.markDirty,
|
|
132
|
+
chordTimeoutMs: options.chordTimeoutMs,
|
|
133
|
+
collapseAllChordPrefix: options.collapseAllChordPrefix,
|
|
134
|
+
nowMs,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const globalShortcutOptions: GlobalShortcutInputOptions = {
|
|
138
|
+
shortcutBindings: options.shortcutBindings,
|
|
139
|
+
requestStop: options.requestStop,
|
|
140
|
+
resolveDirectoryForAction: options.resolveDirectoryForAction,
|
|
141
|
+
openNewThreadPrompt: options.openNewThreadPrompt,
|
|
142
|
+
toggleCommandMenu: options.toggleCommandMenu,
|
|
143
|
+
openOrCreateCritiqueConversationInDirectory: async (directoryId) => {
|
|
144
|
+
await options.workspaceActions.openOrCreateCritiqueConversationInDirectory(directoryId);
|
|
145
|
+
},
|
|
146
|
+
toggleGatewayProfile: async () => {
|
|
147
|
+
await options.workspaceActions.toggleGatewayProfiler();
|
|
148
|
+
},
|
|
149
|
+
toggleGatewayStatusTimeline: async () => {
|
|
150
|
+
await options.workspaceActions.toggleGatewayStatusTimeline();
|
|
151
|
+
},
|
|
152
|
+
toggleGatewayRenderTrace: async (conversationId) => {
|
|
153
|
+
await options.workspaceActions.toggleGatewayRenderTrace(conversationId);
|
|
154
|
+
},
|
|
155
|
+
getMainPaneMode: options.getMainPaneMode,
|
|
156
|
+
getActiveConversationId: options.getActiveConversationId,
|
|
157
|
+
conversationsHas: options.conversationsHas,
|
|
158
|
+
queueControlPlaneOp: options.queueControlPlaneOp,
|
|
159
|
+
archiveConversation: async (sessionId) => {
|
|
160
|
+
await options.workspaceActions.archiveConversation(sessionId);
|
|
161
|
+
},
|
|
162
|
+
interruptConversation: async (sessionId) => {
|
|
163
|
+
await options.workspaceActions.interruptConversation(sessionId);
|
|
164
|
+
},
|
|
165
|
+
takeoverConversation: async (sessionId) => {
|
|
166
|
+
await options.workspaceActions.takeoverConversation(sessionId);
|
|
167
|
+
},
|
|
168
|
+
openAddDirectoryPrompt: options.openAddDirectoryPrompt,
|
|
169
|
+
getActiveDirectoryId: options.getActiveDirectoryId,
|
|
170
|
+
directoryExists: options.directoriesHas,
|
|
171
|
+
closeDirectory: async (directoryId) => {
|
|
172
|
+
await options.workspaceActions.closeDirectory(directoryId);
|
|
173
|
+
},
|
|
174
|
+
cycleLeftNavSelection: (direction) => {
|
|
175
|
+
this.leftNavInput.cycleSelection(direction);
|
|
176
|
+
},
|
|
177
|
+
nowMs,
|
|
178
|
+
...(options.forwardInterruptAllToActiveConversation === undefined
|
|
179
|
+
? {}
|
|
180
|
+
: {
|
|
181
|
+
forwardInterruptAllToActiveConversation:
|
|
182
|
+
options.forwardInterruptAllToActiveConversation,
|
|
183
|
+
}),
|
|
184
|
+
...(options.interruptAllDoubleTapWindowMs === undefined
|
|
185
|
+
? {}
|
|
186
|
+
: {
|
|
187
|
+
interruptAllDoubleTapWindowMs: options.interruptAllDoubleTapWindowMs,
|
|
188
|
+
}),
|
|
189
|
+
};
|
|
190
|
+
this.globalShortcutInput = createGlobalShortcutInput(globalShortcutOptions);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
cycleLeftNavSelection(direction: 'next' | 'previous'): boolean {
|
|
194
|
+
return this.leftNavInput.cycleSelection(direction);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
handleRepositoryFoldInput(input: Buffer): boolean {
|
|
198
|
+
return (
|
|
199
|
+
this.repositoryFoldInput.handleRepositoryFoldChords(input) ||
|
|
200
|
+
this.repositoryFoldInput.handleRepositoryTreeArrow(input)
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
handleGlobalShortcutInput(input: Buffer): boolean {
|
|
205
|
+
return this.globalShortcutInput.handleInput(input);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
interface RuntimeProcessTarget {
|
|
2
|
+
readonly stdin: Pick<NodeJS.ReadStream, 'on' | 'off'>;
|
|
3
|
+
readonly stdout: Pick<NodeJS.WriteStream, 'on' | 'off'>;
|
|
4
|
+
once(event: 'SIGINT' | 'SIGTERM', listener: () => void): RuntimeProcessTarget;
|
|
5
|
+
once(event: 'uncaughtException', listener: (error: Error) => void): RuntimeProcessTarget;
|
|
6
|
+
once(event: 'unhandledRejection', listener: (reason: unknown) => void): RuntimeProcessTarget;
|
|
7
|
+
off(event: 'SIGINT' | 'SIGTERM', listener: () => void): RuntimeProcessTarget;
|
|
8
|
+
off(event: 'uncaughtException', listener: (error: Error) => void): RuntimeProcessTarget;
|
|
9
|
+
off(event: 'unhandledRejection', listener: (reason: unknown) => void): RuntimeProcessTarget;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface RuntimeProcessWiringOptions {
|
|
13
|
+
readonly onInput: (chunk: Buffer) => void;
|
|
14
|
+
readonly onResize: () => void;
|
|
15
|
+
readonly requestStop: () => void;
|
|
16
|
+
readonly handleRuntimeFatal: (origin: string, error: unknown) => void;
|
|
17
|
+
readonly target?: RuntimeProcessTarget;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class RuntimeProcessWiring {
|
|
21
|
+
private readonly target: RuntimeProcessTarget;
|
|
22
|
+
|
|
23
|
+
constructor(private readonly options: RuntimeProcessWiringOptions) {
|
|
24
|
+
this.target = options.target ?? process;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
attach(): void {
|
|
28
|
+
this.target.stdin.on('data', this.onInputSafe);
|
|
29
|
+
this.target.stdout.on('resize', this.onResizeSafe);
|
|
30
|
+
this.target.once('SIGINT', this.options.requestStop);
|
|
31
|
+
this.target.once('SIGTERM', this.options.requestStop);
|
|
32
|
+
this.target.once('uncaughtException', this.onUncaughtException);
|
|
33
|
+
this.target.once('unhandledRejection', this.onUnhandledRejection);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
detach(): void {
|
|
37
|
+
this.target.stdin.off('data', this.onInputSafe);
|
|
38
|
+
this.target.stdout.off('resize', this.onResizeSafe);
|
|
39
|
+
this.target.off('SIGINT', this.options.requestStop);
|
|
40
|
+
this.target.off('SIGTERM', this.options.requestStop);
|
|
41
|
+
this.target.off('uncaughtException', this.onUncaughtException);
|
|
42
|
+
this.target.off('unhandledRejection', this.onUnhandledRejection);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private readonly onInputSafe = (chunk: Buffer): void => {
|
|
46
|
+
try {
|
|
47
|
+
this.options.onInput(chunk);
|
|
48
|
+
} catch (error: unknown) {
|
|
49
|
+
this.options.handleRuntimeFatal('stdin-data', error);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
private readonly onResizeSafe = (): void => {
|
|
54
|
+
try {
|
|
55
|
+
this.options.onResize();
|
|
56
|
+
} catch (error: unknown) {
|
|
57
|
+
this.options.handleRuntimeFatal('stdout-resize', error);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
private readonly onUncaughtException = (error: Error): void => {
|
|
62
|
+
this.options.handleRuntimeFatal('uncaught-exception', error);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
private readonly onUnhandledRejection = (reason: unknown): void => {
|
|
66
|
+
this.options.handleRuntimeFatal('unhandled-rejection', reason);
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import type { WorkspaceModel } from '../domain/workspace.ts';
|
|
2
|
+
import type { resolveMuxShortcutBindings } from '../mux/input-shortcuts.ts';
|
|
3
|
+
import { LeftRailPointerInput } from '../ui/left-rail-pointer-input.ts';
|
|
4
|
+
import { RuntimeNavigationInput } from './runtime-navigation-input.ts';
|
|
5
|
+
|
|
6
|
+
type MainPaneMode = 'conversation' | 'project' | 'home';
|
|
7
|
+
type RuntimeNavigationInputOptions = ConstructorParameters<typeof RuntimeNavigationInput>[0];
|
|
8
|
+
type LeftRailPointerInputOptions = ConstructorParameters<typeof LeftRailPointerInput>[0];
|
|
9
|
+
type LeftRailPointerClickInput = Parameters<LeftRailPointerInput['handlePointerClick']>[0];
|
|
10
|
+
|
|
11
|
+
interface RuntimeRailWorkspaceActions {
|
|
12
|
+
activateConversation(sessionId: string): Promise<void>;
|
|
13
|
+
openOrCreateCritiqueConversationInDirectory(directoryId: string): Promise<void>;
|
|
14
|
+
toggleGatewayProfiler(): Promise<void>;
|
|
15
|
+
toggleGatewayStatusTimeline(): Promise<void>;
|
|
16
|
+
toggleGatewayRenderTrace(conversationId: string | null): Promise<void>;
|
|
17
|
+
archiveConversation(sessionId: string): Promise<void>;
|
|
18
|
+
interruptConversation(sessionId: string): Promise<void>;
|
|
19
|
+
takeoverConversation(sessionId: string): Promise<void>;
|
|
20
|
+
closeDirectory(directoryId: string): Promise<void>;
|
|
21
|
+
openRepositoryPromptForCreate(): void;
|
|
22
|
+
openRepositoryPromptForEdit(repositoryId: string): void;
|
|
23
|
+
archiveRepositoryById(repositoryId: string): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface RuntimeRailInputOptions {
|
|
27
|
+
readonly workspace: WorkspaceModel;
|
|
28
|
+
readonly shortcutBindings: ReturnType<typeof resolveMuxShortcutBindings>;
|
|
29
|
+
readonly queueControlPlaneOp: (task: () => Promise<void>, label: string) => void;
|
|
30
|
+
readonly runtimeWorkspaceActions: RuntimeRailWorkspaceActions;
|
|
31
|
+
readonly requestStop: () => void;
|
|
32
|
+
readonly resolveDirectoryForAction: () => string | null;
|
|
33
|
+
readonly openNewThreadPrompt: (directoryId: string) => void;
|
|
34
|
+
readonly toggleCommandMenu: () => void;
|
|
35
|
+
readonly firstDirectoryForRepositoryGroup: (repositoryGroupId: string) => string | null;
|
|
36
|
+
readonly enterHomePane: () => void;
|
|
37
|
+
readonly enterProjectPane: (directoryId: string) => void;
|
|
38
|
+
readonly markDirty: () => void;
|
|
39
|
+
readonly queuePersistMuxUiState: () => void;
|
|
40
|
+
readonly conversations: RuntimeNavigationInputOptions['conversations'];
|
|
41
|
+
readonly repositoryGroupIdForDirectory: RuntimeNavigationInputOptions['repositoryGroupIdForDirectory'];
|
|
42
|
+
readonly toggleRepositoryGroup: (repositoryGroupId: string) => void;
|
|
43
|
+
readonly collapseRepositoryGroup: RuntimeNavigationInputOptions['collapseRepositoryGroup'];
|
|
44
|
+
readonly expandRepositoryGroup: RuntimeNavigationInputOptions['expandRepositoryGroup'];
|
|
45
|
+
readonly collapseAllRepositoryGroups: RuntimeNavigationInputOptions['collapseAllRepositoryGroups'];
|
|
46
|
+
readonly expandAllRepositoryGroups: RuntimeNavigationInputOptions['expandAllRepositoryGroups'];
|
|
47
|
+
readonly directoriesHas: RuntimeNavigationInputOptions['directoriesHas'];
|
|
48
|
+
readonly conversationDirectoryId: RuntimeNavigationInputOptions['conversationDirectoryId'];
|
|
49
|
+
readonly conversationsHas: RuntimeNavigationInputOptions['conversationsHas'];
|
|
50
|
+
readonly getMainPaneMode: () => MainPaneMode;
|
|
51
|
+
readonly getActiveConversationId: () => string | null;
|
|
52
|
+
readonly getActiveDirectoryId: () => string | null;
|
|
53
|
+
readonly forwardInterruptAllToActiveConversation?: (input: Buffer) => boolean;
|
|
54
|
+
readonly interruptAllDoubleTapWindowMs?: number;
|
|
55
|
+
readonly repositoriesHas: (repositoryId: string) => boolean;
|
|
56
|
+
readonly chordTimeoutMs: number;
|
|
57
|
+
readonly collapseAllChordPrefix: Buffer;
|
|
58
|
+
readonly stopConversationTitleEdit: (persistPending: boolean) => void;
|
|
59
|
+
readonly releaseViewportPinForSelection: () => void;
|
|
60
|
+
readonly beginConversationTitleEdit: (conversationId: string) => void;
|
|
61
|
+
readonly resetConversationPaneFrameCache: () => void;
|
|
62
|
+
readonly conversationTitleEditDoubleClickWindowMs: number;
|
|
63
|
+
readonly nowMs?: () => number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
interface RuntimeRailInputDependencies {
|
|
67
|
+
readonly createRuntimeNavigationInput?: (
|
|
68
|
+
options: RuntimeNavigationInputOptions,
|
|
69
|
+
) => Pick<
|
|
70
|
+
RuntimeNavigationInput,
|
|
71
|
+
'cycleLeftNavSelection' | 'handleRepositoryFoldInput' | 'handleGlobalShortcutInput'
|
|
72
|
+
>;
|
|
73
|
+
readonly createLeftRailPointerInput?: (
|
|
74
|
+
options: LeftRailPointerInputOptions,
|
|
75
|
+
) => Pick<LeftRailPointerInput, 'handlePointerClick'>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export class RuntimeRailInput {
|
|
79
|
+
private readonly navigationInput: Pick<
|
|
80
|
+
RuntimeNavigationInput,
|
|
81
|
+
'cycleLeftNavSelection' | 'handleRepositoryFoldInput' | 'handleGlobalShortcutInput'
|
|
82
|
+
>;
|
|
83
|
+
private readonly leftRailPointerInput: Pick<LeftRailPointerInput, 'handlePointerClick'>;
|
|
84
|
+
|
|
85
|
+
constructor(
|
|
86
|
+
private readonly options: RuntimeRailInputOptions,
|
|
87
|
+
dependencies: RuntimeRailInputDependencies = {},
|
|
88
|
+
) {
|
|
89
|
+
const nowMs = options.nowMs ?? (() => Date.now());
|
|
90
|
+
const createRuntimeNavigationInput =
|
|
91
|
+
dependencies.createRuntimeNavigationInput ??
|
|
92
|
+
((navigationOptions: RuntimeNavigationInputOptions) =>
|
|
93
|
+
new RuntimeNavigationInput(navigationOptions));
|
|
94
|
+
const createLeftRailPointerInput =
|
|
95
|
+
dependencies.createLeftRailPointerInput ??
|
|
96
|
+
((leftRailOptions: LeftRailPointerInputOptions) => new LeftRailPointerInput(leftRailOptions));
|
|
97
|
+
|
|
98
|
+
const runtimeNavigationOptions: RuntimeNavigationInputOptions = {
|
|
99
|
+
workspace: options.workspace,
|
|
100
|
+
shortcutBindings: options.shortcutBindings,
|
|
101
|
+
requestStop: options.requestStop,
|
|
102
|
+
resolveDirectoryForAction: options.resolveDirectoryForAction,
|
|
103
|
+
openNewThreadPrompt: options.openNewThreadPrompt,
|
|
104
|
+
toggleCommandMenu: options.toggleCommandMenu,
|
|
105
|
+
openAddDirectoryPrompt: () => {
|
|
106
|
+
this.openAddDirectoryPrompt();
|
|
107
|
+
options.markDirty();
|
|
108
|
+
},
|
|
109
|
+
queueControlPlaneOp: options.queueControlPlaneOp,
|
|
110
|
+
firstDirectoryForRepositoryGroup: options.firstDirectoryForRepositoryGroup,
|
|
111
|
+
enterHomePane: options.enterHomePane,
|
|
112
|
+
enterProjectPane: options.enterProjectPane,
|
|
113
|
+
markDirty: options.markDirty,
|
|
114
|
+
conversations: options.conversations,
|
|
115
|
+
repositoryGroupIdForDirectory: options.repositoryGroupIdForDirectory,
|
|
116
|
+
collapseRepositoryGroup: options.collapseRepositoryGroup,
|
|
117
|
+
expandRepositoryGroup: options.expandRepositoryGroup,
|
|
118
|
+
collapseAllRepositoryGroups: options.collapseAllRepositoryGroups,
|
|
119
|
+
expandAllRepositoryGroups: options.expandAllRepositoryGroups,
|
|
120
|
+
directoriesHas: options.directoriesHas,
|
|
121
|
+
conversationDirectoryId: options.conversationDirectoryId,
|
|
122
|
+
conversationsHas: options.conversationsHas,
|
|
123
|
+
getMainPaneMode: options.getMainPaneMode,
|
|
124
|
+
getActiveConversationId: options.getActiveConversationId,
|
|
125
|
+
getActiveDirectoryId: options.getActiveDirectoryId,
|
|
126
|
+
workspaceActions: {
|
|
127
|
+
activateConversation: async (sessionId) => {
|
|
128
|
+
await options.runtimeWorkspaceActions.activateConversation(sessionId);
|
|
129
|
+
},
|
|
130
|
+
openOrCreateCritiqueConversationInDirectory: async (directoryId) => {
|
|
131
|
+
await options.runtimeWorkspaceActions.openOrCreateCritiqueConversationInDirectory(
|
|
132
|
+
directoryId,
|
|
133
|
+
);
|
|
134
|
+
},
|
|
135
|
+
toggleGatewayProfiler: async () => {
|
|
136
|
+
await options.runtimeWorkspaceActions.toggleGatewayProfiler();
|
|
137
|
+
},
|
|
138
|
+
toggleGatewayStatusTimeline: async () => {
|
|
139
|
+
await options.runtimeWorkspaceActions.toggleGatewayStatusTimeline();
|
|
140
|
+
},
|
|
141
|
+
toggleGatewayRenderTrace: async (conversationId) => {
|
|
142
|
+
await options.runtimeWorkspaceActions.toggleGatewayRenderTrace(conversationId);
|
|
143
|
+
},
|
|
144
|
+
archiveConversation: async (sessionId) => {
|
|
145
|
+
await options.runtimeWorkspaceActions.archiveConversation(sessionId);
|
|
146
|
+
},
|
|
147
|
+
interruptConversation: async (sessionId) => {
|
|
148
|
+
await options.runtimeWorkspaceActions.interruptConversation(sessionId);
|
|
149
|
+
},
|
|
150
|
+
takeoverConversation: async (sessionId) => {
|
|
151
|
+
await options.runtimeWorkspaceActions.takeoverConversation(sessionId);
|
|
152
|
+
},
|
|
153
|
+
closeDirectory: async (directoryId) => {
|
|
154
|
+
await options.runtimeWorkspaceActions.closeDirectory(directoryId);
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
chordTimeoutMs: options.chordTimeoutMs,
|
|
158
|
+
collapseAllChordPrefix: options.collapseAllChordPrefix,
|
|
159
|
+
nowMs,
|
|
160
|
+
...(options.forwardInterruptAllToActiveConversation === undefined
|
|
161
|
+
? {}
|
|
162
|
+
: {
|
|
163
|
+
forwardInterruptAllToActiveConversation:
|
|
164
|
+
options.forwardInterruptAllToActiveConversation,
|
|
165
|
+
}),
|
|
166
|
+
...(options.interruptAllDoubleTapWindowMs === undefined
|
|
167
|
+
? {}
|
|
168
|
+
: {
|
|
169
|
+
interruptAllDoubleTapWindowMs: options.interruptAllDoubleTapWindowMs,
|
|
170
|
+
}),
|
|
171
|
+
};
|
|
172
|
+
this.navigationInput = createRuntimeNavigationInput(runtimeNavigationOptions);
|
|
173
|
+
|
|
174
|
+
this.leftRailPointerInput = createLeftRailPointerInput({
|
|
175
|
+
getLatestRailRows: () => options.workspace.latestRailViewRows,
|
|
176
|
+
hasConversationTitleEdit: () => options.workspace.conversationTitleEdit !== null,
|
|
177
|
+
conversationTitleEditConversationId: () =>
|
|
178
|
+
options.workspace.conversationTitleEdit?.conversationId ?? null,
|
|
179
|
+
stopConversationTitleEdit: () => {
|
|
180
|
+
options.stopConversationTitleEdit(true);
|
|
181
|
+
},
|
|
182
|
+
hasSelection: () =>
|
|
183
|
+
options.workspace.selection !== null || options.workspace.selectionDrag !== null,
|
|
184
|
+
clearSelection: () => {
|
|
185
|
+
options.workspace.selection = null;
|
|
186
|
+
options.workspace.selectionDrag = null;
|
|
187
|
+
options.releaseViewportPinForSelection();
|
|
188
|
+
},
|
|
189
|
+
activeConversationId: options.getActiveConversationId,
|
|
190
|
+
repositoriesCollapsed: () => options.workspace.repositoriesCollapsed,
|
|
191
|
+
clearConversationTitleEditClickState: () => {
|
|
192
|
+
options.workspace.conversationTitleEditClickState = null;
|
|
193
|
+
},
|
|
194
|
+
resolveDirectoryForAction: options.resolveDirectoryForAction,
|
|
195
|
+
openNewThreadPrompt: options.openNewThreadPrompt,
|
|
196
|
+
queueArchiveConversation: (conversationId) => {
|
|
197
|
+
options.queueControlPlaneOp(async () => {
|
|
198
|
+
await options.runtimeWorkspaceActions.archiveConversation(conversationId);
|
|
199
|
+
}, 'mouse-archive-conversation');
|
|
200
|
+
},
|
|
201
|
+
openAddDirectoryPrompt: () => {
|
|
202
|
+
this.openAddDirectoryPrompt();
|
|
203
|
+
},
|
|
204
|
+
openRepositoryPromptForCreate: () => {
|
|
205
|
+
options.runtimeWorkspaceActions.openRepositoryPromptForCreate();
|
|
206
|
+
},
|
|
207
|
+
repositoryExists: options.repositoriesHas,
|
|
208
|
+
openRepositoryPromptForEdit: (repositoryId) => {
|
|
209
|
+
options.runtimeWorkspaceActions.openRepositoryPromptForEdit(repositoryId);
|
|
210
|
+
},
|
|
211
|
+
queueArchiveRepository: (repositoryId) => {
|
|
212
|
+
options.queueControlPlaneOp(async () => {
|
|
213
|
+
await options.runtimeWorkspaceActions.archiveRepositoryById(repositoryId);
|
|
214
|
+
}, 'mouse-archive-repository');
|
|
215
|
+
},
|
|
216
|
+
queueCloseDirectory: (directoryId) => {
|
|
217
|
+
options.queueControlPlaneOp(async () => {
|
|
218
|
+
await options.runtimeWorkspaceActions.closeDirectory(directoryId);
|
|
219
|
+
}, 'mouse-close-directory');
|
|
220
|
+
},
|
|
221
|
+
toggleRepositoryGroup: options.toggleRepositoryGroup,
|
|
222
|
+
selectLeftNavRepository: (repositoryGroupId) => {
|
|
223
|
+
options.workspace.selectLeftNavRepository(repositoryGroupId);
|
|
224
|
+
},
|
|
225
|
+
expandAllRepositoryGroups: options.expandAllRepositoryGroups,
|
|
226
|
+
collapseAllRepositoryGroups: options.collapseAllRepositoryGroups,
|
|
227
|
+
enterHomePane: options.enterHomePane,
|
|
228
|
+
toggleShortcutsCollapsed: () => {
|
|
229
|
+
options.workspace.shortcutsCollapsed = !options.workspace.shortcutsCollapsed;
|
|
230
|
+
options.queuePersistMuxUiState();
|
|
231
|
+
},
|
|
232
|
+
previousConversationClickState: () => options.workspace.conversationTitleEditClickState,
|
|
233
|
+
setConversationClickState: (next) => {
|
|
234
|
+
options.workspace.conversationTitleEditClickState = next;
|
|
235
|
+
},
|
|
236
|
+
nowMs,
|
|
237
|
+
conversationTitleEditDoubleClickWindowMs: options.conversationTitleEditDoubleClickWindowMs,
|
|
238
|
+
isConversationPaneActive: () => options.getMainPaneMode() === 'conversation',
|
|
239
|
+
ensureConversationPaneActive: (conversationId) => {
|
|
240
|
+
options.workspace.mainPaneMode = 'conversation';
|
|
241
|
+
options.workspace.selectLeftNavConversation(conversationId);
|
|
242
|
+
options.workspace.projectPaneSnapshot = null;
|
|
243
|
+
options.workspace.projectPaneScrollTop = 0;
|
|
244
|
+
options.resetConversationPaneFrameCache();
|
|
245
|
+
},
|
|
246
|
+
beginConversationTitleEdit: options.beginConversationTitleEdit,
|
|
247
|
+
queueActivateConversation: (conversationId) => {
|
|
248
|
+
options.queueControlPlaneOp(async () => {
|
|
249
|
+
await options.runtimeWorkspaceActions.activateConversation(conversationId);
|
|
250
|
+
}, 'mouse-activate-conversation');
|
|
251
|
+
},
|
|
252
|
+
queueActivateConversationAndEdit: (conversationId) => {
|
|
253
|
+
options.queueControlPlaneOp(async () => {
|
|
254
|
+
await options.runtimeWorkspaceActions.activateConversation(conversationId);
|
|
255
|
+
options.beginConversationTitleEdit(conversationId);
|
|
256
|
+
}, 'mouse-activate-edit-conversation');
|
|
257
|
+
},
|
|
258
|
+
directoriesHas: options.directoriesHas,
|
|
259
|
+
enterProjectPane: options.enterProjectPane,
|
|
260
|
+
markDirty: options.markDirty,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
cycleLeftNavSelection(direction: 'next' | 'previous'): boolean {
|
|
265
|
+
return this.navigationInput.cycleLeftNavSelection(direction);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
handleRepositoryFoldInput(input: Buffer): boolean {
|
|
269
|
+
return this.navigationInput.handleRepositoryFoldInput(input);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
handleGlobalShortcutInput(input: Buffer): boolean {
|
|
273
|
+
return this.navigationInput.handleGlobalShortcutInput(input);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
handlePointerClick(input: LeftRailPointerClickInput): boolean {
|
|
277
|
+
return this.leftRailPointerInput.handlePointerClick(input);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
private openAddDirectoryPrompt(): void {
|
|
281
|
+
this.options.workspace.repositoryPrompt = null;
|
|
282
|
+
this.options.workspace.addDirectoryPrompt = {
|
|
283
|
+
value: '',
|
|
284
|
+
error: null,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
}
|