@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
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
export type LeftNavSelection =
|
|
2
|
+
| {
|
|
3
|
+
readonly kind: 'home';
|
|
4
|
+
}
|
|
5
|
+
| {
|
|
6
|
+
readonly kind: 'nim';
|
|
7
|
+
}
|
|
8
|
+
| {
|
|
9
|
+
readonly kind: 'tasks';
|
|
10
|
+
}
|
|
11
|
+
| {
|
|
12
|
+
readonly kind: 'repository';
|
|
13
|
+
readonly repositoryId: string;
|
|
14
|
+
}
|
|
15
|
+
| {
|
|
16
|
+
readonly kind: 'project';
|
|
17
|
+
readonly directoryId: string;
|
|
18
|
+
}
|
|
19
|
+
| {
|
|
20
|
+
readonly kind: 'github';
|
|
21
|
+
readonly directoryId: string;
|
|
22
|
+
}
|
|
23
|
+
| {
|
|
24
|
+
readonly kind: 'conversation';
|
|
25
|
+
readonly sessionId: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export interface LeftNavState {
|
|
29
|
+
readonly railViewState: {
|
|
30
|
+
readLatestRows(): unknown;
|
|
31
|
+
};
|
|
32
|
+
readonly currentSelection: () => LeftNavSelection;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface LeftNavActions {
|
|
36
|
+
readonly enterHomePane: () => void;
|
|
37
|
+
readonly enterNimPane?: () => void;
|
|
38
|
+
readonly enterTasksPane?: () => void;
|
|
39
|
+
readonly firstDirectoryForRepositoryGroup: (repositoryGroupId: string) => string | null;
|
|
40
|
+
readonly enterProjectPane: (directoryId: string) => void;
|
|
41
|
+
readonly enterGitHubPane?: (directoryId: string) => void;
|
|
42
|
+
readonly setMainPaneProjectMode: () => void;
|
|
43
|
+
readonly selectLeftNavRepository: (repositoryGroupId: string) => void;
|
|
44
|
+
readonly selectLeftNavConversation?: (sessionId: string) => void;
|
|
45
|
+
readonly markDirty: () => void;
|
|
46
|
+
readonly directoriesHas: (directoryId: string) => boolean;
|
|
47
|
+
readonly conversationDirectoryId: (sessionId: string) => string | null;
|
|
48
|
+
readonly queueControlPlaneOp: (task: () => Promise<void>, label: string) => void;
|
|
49
|
+
readonly queueLatestControlPlaneOp?: (
|
|
50
|
+
key: string,
|
|
51
|
+
task: (options: { readonly signal: AbortSignal }) => Promise<void>,
|
|
52
|
+
label: string,
|
|
53
|
+
) => void;
|
|
54
|
+
readonly activateConversation: (
|
|
55
|
+
sessionId: string,
|
|
56
|
+
options?: { readonly signal?: AbortSignal },
|
|
57
|
+
) => Promise<void>;
|
|
58
|
+
readonly conversationsHas: (sessionId: string) => boolean;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface ActivateLeftNavTargetInput {
|
|
62
|
+
readonly target: LeftNavSelection;
|
|
63
|
+
readonly direction: 'next' | 'previous';
|
|
64
|
+
readonly enterHomePane: () => void;
|
|
65
|
+
readonly enterNimPane?: () => void;
|
|
66
|
+
readonly enterTasksPane?: () => void;
|
|
67
|
+
readonly firstDirectoryForRepositoryGroup: (repositoryGroupId: string) => string | null;
|
|
68
|
+
readonly enterProjectPane: (directoryId: string) => void;
|
|
69
|
+
readonly enterGitHubPane?: (directoryId: string) => void;
|
|
70
|
+
readonly setMainPaneProjectMode: () => void;
|
|
71
|
+
readonly selectLeftNavRepository: (repositoryGroupId: string) => void;
|
|
72
|
+
readonly selectLeftNavConversation?: (sessionId: string) => void;
|
|
73
|
+
readonly markDirty: () => void;
|
|
74
|
+
readonly directoriesHas: (directoryId: string) => boolean;
|
|
75
|
+
readonly visibleTargetsForState: () => readonly LeftNavSelection[];
|
|
76
|
+
readonly conversationDirectoryId: (sessionId: string) => string | null;
|
|
77
|
+
readonly queueControlPlaneOp: (task: () => Promise<void>, label: string) => void;
|
|
78
|
+
readonly queueLatestControlPlaneOp?: (
|
|
79
|
+
key: string,
|
|
80
|
+
task: (options: { readonly signal: AbortSignal }) => Promise<void>,
|
|
81
|
+
label: string,
|
|
82
|
+
) => void;
|
|
83
|
+
readonly activateConversation: (
|
|
84
|
+
sessionId: string,
|
|
85
|
+
options?: { readonly signal?: AbortSignal },
|
|
86
|
+
) => Promise<void>;
|
|
87
|
+
readonly conversationsHas: (sessionId: string) => boolean;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface CycleLeftNavSelectionInput {
|
|
91
|
+
readonly visibleTargets: readonly LeftNavSelection[];
|
|
92
|
+
readonly currentSelection: LeftNavSelection;
|
|
93
|
+
readonly direction: 'next' | 'previous';
|
|
94
|
+
readonly activateTarget: (target: LeftNavSelection, direction: 'next' | 'previous') => void;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface LeftNavStrategies {
|
|
98
|
+
visibleTargets(rows: unknown): readonly LeftNavSelection[];
|
|
99
|
+
activateTarget(input: ActivateLeftNavTargetInput): void;
|
|
100
|
+
cycleSelection(input: CycleLeftNavSelectionInput): boolean;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export class LeftNavInput {
|
|
104
|
+
constructor(
|
|
105
|
+
private readonly state: LeftNavState,
|
|
106
|
+
private readonly actions: LeftNavActions,
|
|
107
|
+
private readonly strategies: LeftNavStrategies,
|
|
108
|
+
) {}
|
|
109
|
+
|
|
110
|
+
visibleTargets(): readonly LeftNavSelection[] {
|
|
111
|
+
return this.strategies.visibleTargets(this.state.railViewState.readLatestRows());
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
activateTarget(target: LeftNavSelection, direction: 'next' | 'previous'): void {
|
|
115
|
+
this.strategies.activateTarget({
|
|
116
|
+
target,
|
|
117
|
+
direction,
|
|
118
|
+
enterHomePane: this.actions.enterHomePane,
|
|
119
|
+
...(this.actions.enterNimPane === undefined
|
|
120
|
+
? {}
|
|
121
|
+
: {
|
|
122
|
+
enterNimPane: this.actions.enterNimPane,
|
|
123
|
+
}),
|
|
124
|
+
firstDirectoryForRepositoryGroup: this.actions.firstDirectoryForRepositoryGroup,
|
|
125
|
+
enterProjectPane: this.actions.enterProjectPane,
|
|
126
|
+
...(this.actions.enterGitHubPane === undefined
|
|
127
|
+
? {}
|
|
128
|
+
: {
|
|
129
|
+
enterGitHubPane: this.actions.enterGitHubPane,
|
|
130
|
+
}),
|
|
131
|
+
setMainPaneProjectMode: this.actions.setMainPaneProjectMode,
|
|
132
|
+
selectLeftNavRepository: this.actions.selectLeftNavRepository,
|
|
133
|
+
...(this.actions.selectLeftNavConversation === undefined
|
|
134
|
+
? {}
|
|
135
|
+
: {
|
|
136
|
+
selectLeftNavConversation: this.actions.selectLeftNavConversation,
|
|
137
|
+
}),
|
|
138
|
+
markDirty: this.actions.markDirty,
|
|
139
|
+
directoriesHas: this.actions.directoriesHas,
|
|
140
|
+
visibleTargetsForState: () => this.visibleTargets(),
|
|
141
|
+
conversationDirectoryId: this.actions.conversationDirectoryId,
|
|
142
|
+
queueControlPlaneOp: this.actions.queueControlPlaneOp,
|
|
143
|
+
...(this.actions.queueLatestControlPlaneOp === undefined
|
|
144
|
+
? {}
|
|
145
|
+
: {
|
|
146
|
+
queueLatestControlPlaneOp: this.actions.queueLatestControlPlaneOp,
|
|
147
|
+
}),
|
|
148
|
+
activateConversation: this.actions.activateConversation,
|
|
149
|
+
conversationsHas: this.actions.conversationsHas,
|
|
150
|
+
...(this.actions.enterTasksPane === undefined
|
|
151
|
+
? {}
|
|
152
|
+
: {
|
|
153
|
+
enterTasksPane: this.actions.enterTasksPane,
|
|
154
|
+
}),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
cycleSelection(direction: 'next' | 'previous'): boolean {
|
|
159
|
+
return this.strategies.cycleSelection({
|
|
160
|
+
visibleTargets: this.visibleTargets(),
|
|
161
|
+
currentSelection: this.state.currentSelection(),
|
|
162
|
+
direction,
|
|
163
|
+
activateTarget: (target, nextDirection) => this.activateTarget(target, nextDirection),
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import { hasAltModifier, isLeftButtonPress, isMotionMouseCode } from '../mux/live-mux/selection.ts';
|
|
2
|
-
import { handleHomePanePointerClick } from '../mux/live-mux/home-pane-pointer.ts';
|
|
3
|
-
import { handleProjectPaneActionClick } from '../mux/live-mux/project-pane-pointer.ts';
|
|
4
|
-
|
|
5
1
|
interface EntityDoubleClickState {
|
|
6
2
|
readonly entityId: string;
|
|
7
3
|
readonly atMs: number;
|
|
@@ -15,10 +11,32 @@ interface HomePaneDragState {
|
|
|
15
11
|
readonly hasDragged: boolean;
|
|
16
12
|
}
|
|
17
13
|
|
|
18
|
-
type MainPaneMode = 'conversation' | 'project' | 'home';
|
|
14
|
+
type MainPaneMode = 'conversation' | 'project' | 'home' | 'nim';
|
|
19
15
|
type PointerTarget = 'left' | 'right' | 'separator' | 'status' | 'outside';
|
|
20
16
|
|
|
21
|
-
|
|
17
|
+
function isWheelMouseCode(code: number): boolean {
|
|
18
|
+
return (code & 0b0100_0000) !== 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function hasAltModifier(code: number): boolean {
|
|
22
|
+
return (code & 0b0000_1000) !== 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function isMotionMouseCode(code: number): boolean {
|
|
26
|
+
return (code & 0b0010_0000) !== 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isLeftButtonPress(code: number, final: 'M' | 'm'): boolean {
|
|
30
|
+
if (final !== 'M') {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
if (isWheelMouseCode(code) || isMotionMouseCode(code)) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
return (code & 0b0000_0011) === 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface MainPanePointerInputOptions<TProjectSnapshot extends { directoryId: string }> {
|
|
22
40
|
readonly getMainPaneMode: () => MainPaneMode;
|
|
23
41
|
readonly getProjectPaneSnapshot: () => TProjectSnapshot | null;
|
|
24
42
|
readonly getProjectPaneScrollTop: () => number;
|
|
@@ -40,6 +58,7 @@ interface MainPanePointerInputOptions<TProjectSnapshot extends { directoryId: st
|
|
|
40
58
|
readonly setTaskRepositoryDropdownOpen: (open: boolean) => void;
|
|
41
59
|
readonly taskIdAtRow: (rowIndex: number) => string | null;
|
|
42
60
|
readonly repositoryIdAtRow: (rowIndex: number) => string | null;
|
|
61
|
+
readonly rowTextAtRow?: (rowIndex: number) => string | null;
|
|
43
62
|
readonly selectTaskById: (taskId: string) => void;
|
|
44
63
|
readonly selectRepositoryById: (repositoryId: string) => void;
|
|
45
64
|
readonly runTaskPaneAction: (action: 'task.ready' | 'task.draft' | 'task.complete') => void;
|
|
@@ -56,11 +75,6 @@ interface MainPanePointerInputOptions<TProjectSnapshot extends { directoryId: st
|
|
|
56
75
|
readonly markDirty: () => void;
|
|
57
76
|
}
|
|
58
77
|
|
|
59
|
-
interface MainPanePointerInputDependencies {
|
|
60
|
-
readonly handleProjectPaneActionClick?: typeof handleProjectPaneActionClick;
|
|
61
|
-
readonly handleHomePanePointerClick?: typeof handleHomePanePointerClick;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
78
|
interface PointerEventInput {
|
|
65
79
|
readonly target: PointerTarget;
|
|
66
80
|
readonly code: number;
|
|
@@ -72,19 +86,68 @@ interface PointerEventInput {
|
|
|
72
86
|
readonly rightStartCol: number;
|
|
73
87
|
}
|
|
74
88
|
|
|
75
|
-
export
|
|
76
|
-
|
|
77
|
-
|
|
89
|
+
export interface HandleProjectPaneActionClickInput<TSnapshot extends { directoryId: string }> {
|
|
90
|
+
readonly clickEligible: boolean;
|
|
91
|
+
readonly snapshot: TSnapshot | null;
|
|
92
|
+
readonly rightCols: number;
|
|
93
|
+
readonly paneRows: number;
|
|
94
|
+
readonly projectPaneScrollTop: number;
|
|
95
|
+
readonly rowIndex: number;
|
|
96
|
+
readonly projectPaneActionAtRow: (
|
|
97
|
+
snapshot: TSnapshot,
|
|
98
|
+
rightCols: number,
|
|
99
|
+
paneRows: number,
|
|
100
|
+
projectPaneScrollTop: number,
|
|
101
|
+
rowIndex: number,
|
|
102
|
+
) => string | null;
|
|
103
|
+
readonly openNewThreadPrompt: (directoryId: string) => void;
|
|
104
|
+
readonly queueCloseDirectory: (directoryId: string) => void;
|
|
105
|
+
readonly markDirty: () => void;
|
|
106
|
+
}
|
|
78
107
|
|
|
108
|
+
export interface HandleHomePanePointerClickInput {
|
|
109
|
+
readonly clickEligible: boolean;
|
|
110
|
+
readonly paneRows: number;
|
|
111
|
+
readonly rightCols: number;
|
|
112
|
+
readonly rightStartCol: number;
|
|
113
|
+
readonly pointerRow: number;
|
|
114
|
+
readonly pointerCol: number;
|
|
115
|
+
readonly actionAtCell: (rowIndex: number, colIndex: number) => string | null;
|
|
116
|
+
readonly actionAtRow: (rowIndex: number) => string | null;
|
|
117
|
+
readonly clearTaskEditClickState: () => void;
|
|
118
|
+
readonly clearRepositoryEditClickState: () => void;
|
|
119
|
+
readonly clearHomePaneDragState: () => void;
|
|
120
|
+
readonly getTaskRepositoryDropdownOpen: () => boolean;
|
|
121
|
+
readonly setTaskRepositoryDropdownOpen: (open: boolean) => void;
|
|
122
|
+
readonly taskIdAtRow: (rowIndex: number) => string | null;
|
|
123
|
+
readonly repositoryIdAtRow: (rowIndex: number) => string | null;
|
|
124
|
+
readonly rowTextAtRow?: (rowIndex: number) => string | null;
|
|
125
|
+
readonly selectTaskById: (taskId: string) => void;
|
|
126
|
+
readonly selectRepositoryById: (repositoryId: string) => void;
|
|
127
|
+
readonly runTaskPaneAction: (action: 'task.ready' | 'task.draft' | 'task.complete') => void;
|
|
128
|
+
readonly nowMs: number;
|
|
129
|
+
readonly homePaneEditDoubleClickWindowMs: number;
|
|
130
|
+
readonly taskEditClickState: EntityDoubleClickState | null;
|
|
131
|
+
readonly repositoryEditClickState: EntityDoubleClickState | null;
|
|
132
|
+
readonly clearTaskPaneNotice: () => void;
|
|
133
|
+
readonly setTaskEditClickState: (next: EntityDoubleClickState | null) => void;
|
|
134
|
+
readonly setRepositoryEditClickState: (next: EntityDoubleClickState | null) => void;
|
|
135
|
+
readonly setHomePaneDragState: (next: HomePaneDragState | null) => void;
|
|
136
|
+
readonly openTaskEditPrompt: (taskId: string) => void;
|
|
137
|
+
readonly openRepositoryPromptForEdit: (repositoryId: string) => void;
|
|
138
|
+
readonly markDirty: () => void;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface MainPanePointerStrategies<TProjectSnapshot extends { directoryId: string }> {
|
|
142
|
+
handleProjectPaneActionClick(input: HandleProjectPaneActionClickInput<TProjectSnapshot>): boolean;
|
|
143
|
+
handleHomePanePointerClick(input: HandleHomePanePointerClickInput): boolean;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export class MainPanePointerInput<TProjectSnapshot extends { directoryId: string }> {
|
|
79
147
|
constructor(
|
|
80
148
|
private readonly options: MainPanePointerInputOptions<TProjectSnapshot>,
|
|
81
|
-
|
|
82
|
-
) {
|
|
83
|
-
this.projectPaneActionClick =
|
|
84
|
-
dependencies.handleProjectPaneActionClick ?? handleProjectPaneActionClick;
|
|
85
|
-
this.homePanePointerClick =
|
|
86
|
-
dependencies.handleHomePanePointerClick ?? handleHomePanePointerClick;
|
|
87
|
-
}
|
|
149
|
+
private readonly strategies: MainPanePointerStrategies<TProjectSnapshot>,
|
|
150
|
+
) {}
|
|
88
151
|
|
|
89
152
|
handleProjectPanePointerClick(input: PointerEventInput): boolean {
|
|
90
153
|
const clickEligible =
|
|
@@ -94,7 +157,7 @@ export class MainPanePointerInput<TProjectSnapshot extends { directoryId: string
|
|
|
94
157
|
!hasAltModifier(input.code) &&
|
|
95
158
|
!isMotionMouseCode(input.code);
|
|
96
159
|
const rowIndex = Math.max(0, Math.min(input.paneRows - 1, input.row - 1));
|
|
97
|
-
return this.
|
|
160
|
+
return this.strategies.handleProjectPaneActionClick({
|
|
98
161
|
clickEligible,
|
|
99
162
|
snapshot: this.options.getProjectPaneSnapshot(),
|
|
100
163
|
rightCols: input.rightCols,
|
|
@@ -115,7 +178,7 @@ export class MainPanePointerInput<TProjectSnapshot extends { directoryId: string
|
|
|
115
178
|
isLeftButtonPress(input.code, input.final) &&
|
|
116
179
|
!hasAltModifier(input.code) &&
|
|
117
180
|
!isMotionMouseCode(input.code);
|
|
118
|
-
return this.
|
|
181
|
+
return this.strategies.handleHomePanePointerClick({
|
|
119
182
|
clickEligible,
|
|
120
183
|
paneRows: input.paneRows,
|
|
121
184
|
rightCols: input.rightCols,
|
|
@@ -145,6 +208,11 @@ export class MainPanePointerInput<TProjectSnapshot extends { directoryId: string
|
|
|
145
208
|
openTaskEditPrompt: this.options.openTaskEditPrompt,
|
|
146
209
|
openRepositoryPromptForEdit: this.options.openRepositoryPromptForEdit,
|
|
147
210
|
markDirty: this.options.markDirty,
|
|
211
|
+
...(this.options.rowTextAtRow === undefined
|
|
212
|
+
? {}
|
|
213
|
+
: {
|
|
214
|
+
rowTextAtRow: this.options.rowTextAtRow,
|
|
215
|
+
}),
|
|
148
216
|
});
|
|
149
217
|
}
|
|
150
218
|
}
|
|
@@ -1,20 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import { handleHomePaneDragRelease as handleHomePaneDragReleaseFrame } from '../mux/live-mux/home-pane-drop.ts';
|
|
3
|
-
import {
|
|
4
|
-
handleHomePaneDragMove as handleHomePaneDragMoveFrame,
|
|
5
|
-
handleMainPaneWheelInput as handleMainPaneWheelInputFrame,
|
|
6
|
-
handlePaneDividerDragInput as handlePaneDividerDragInputFrame,
|
|
7
|
-
handleSeparatorPointerPress as handleSeparatorPointerPressFrame,
|
|
8
|
-
} from '../mux/live-mux/pointer-routing.ts';
|
|
9
|
-
import {
|
|
10
|
-
hasAltModifier,
|
|
11
|
-
isLeftButtonPress,
|
|
12
|
-
isMouseRelease,
|
|
13
|
-
isSelectionDrag,
|
|
14
|
-
isWheelMouseCode,
|
|
15
|
-
} from '../mux/live-mux/selection.ts';
|
|
16
|
-
|
|
17
|
-
type MainPaneMode = 'conversation' | 'project' | 'home';
|
|
1
|
+
type MainPaneMode = 'conversation' | 'project' | 'home' | 'nim';
|
|
18
2
|
type PointerTarget = 'left' | 'right' | 'separator' | 'status' | 'outside';
|
|
19
3
|
|
|
20
4
|
interface HomePaneDragState {
|
|
@@ -25,7 +9,44 @@ interface HomePaneDragState {
|
|
|
25
9
|
readonly hasDragged: boolean;
|
|
26
10
|
}
|
|
27
11
|
|
|
28
|
-
|
|
12
|
+
function isWheelMouseCode(code: number): boolean {
|
|
13
|
+
return (code & 0b0100_0000) !== 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function hasAltModifier(code: number): boolean {
|
|
17
|
+
return (code & 0b0000_1000) !== 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function isMotionMouseCode(code: number): boolean {
|
|
21
|
+
return (code & 0b0010_0000) !== 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function isLeftButtonPress(code: number, final: 'M' | 'm'): boolean {
|
|
25
|
+
if (final !== 'M') {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (isWheelMouseCode(code) || isMotionMouseCode(code)) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
return (code & 0b0000_0011) === 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function isMouseRelease(final: 'M' | 'm'): boolean {
|
|
35
|
+
return final === 'm';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function isSelectionDrag(code: number, final: 'M' | 'm'): boolean {
|
|
39
|
+
return final === 'M' && isMotionMouseCode(code);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function wheelDeltaRowsFromCode(code: number): number | null {
|
|
43
|
+
if (!isWheelMouseCode(code)) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
return (code & 0b0000_0001) === 0 ? -1 : 1;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface PointerRoutingInputOptions {
|
|
29
50
|
readonly getPaneDividerDragActive: () => boolean;
|
|
30
51
|
readonly setPaneDividerDragActive: (active: boolean) => void;
|
|
31
52
|
readonly applyPaneDividerAtCol: (col: number) => void;
|
|
@@ -41,15 +62,74 @@ interface PointerRoutingInputOptions {
|
|
|
41
62
|
) => void;
|
|
42
63
|
readonly onProjectWheel: (delta: number) => void;
|
|
43
64
|
readonly onHomeWheel: (delta: number) => void;
|
|
65
|
+
readonly onNimWheel: (delta: number) => void;
|
|
44
66
|
readonly markDirty: () => void;
|
|
45
67
|
}
|
|
46
68
|
|
|
47
|
-
interface
|
|
48
|
-
readonly
|
|
49
|
-
readonly
|
|
50
|
-
readonly
|
|
51
|
-
readonly
|
|
52
|
-
readonly
|
|
69
|
+
export interface HandlePaneDividerDragInput {
|
|
70
|
+
readonly paneDividerDragActive: boolean;
|
|
71
|
+
readonly isMouseRelease: boolean;
|
|
72
|
+
readonly isWheelMouseCode: boolean;
|
|
73
|
+
readonly mouseCol: number;
|
|
74
|
+
readonly setPaneDividerDragActive: (active: boolean) => void;
|
|
75
|
+
readonly applyPaneDividerAtCol: (col: number) => void;
|
|
76
|
+
readonly markDirty: () => void;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface HandleHomePaneDragReleaseInput {
|
|
80
|
+
readonly homePaneDragState: HomePaneDragState | null;
|
|
81
|
+
readonly isMouseRelease: boolean;
|
|
82
|
+
readonly mainPaneMode: MainPaneMode;
|
|
83
|
+
readonly target: PointerTarget;
|
|
84
|
+
readonly rowIndex: number;
|
|
85
|
+
readonly taskIdAtRow: (rowIndex: number) => string | null;
|
|
86
|
+
readonly repositoryIdAtRow: (rowIndex: number) => string | null;
|
|
87
|
+
readonly reorderTaskByDrop: (draggedTaskId: string, targetTaskId: string) => void;
|
|
88
|
+
readonly reorderRepositoryByDrop: (
|
|
89
|
+
draggedRepositoryId: string,
|
|
90
|
+
targetRepositoryId: string,
|
|
91
|
+
) => void;
|
|
92
|
+
readonly setHomePaneDragState: (next: HomePaneDragState | null) => void;
|
|
93
|
+
readonly markDirty: () => void;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface HandleSeparatorPointerPressInput {
|
|
97
|
+
readonly target: PointerTarget;
|
|
98
|
+
readonly isLeftButtonPress: boolean;
|
|
99
|
+
readonly hasAltModifier: boolean;
|
|
100
|
+
readonly mouseCol: number;
|
|
101
|
+
readonly setPaneDividerDragActive: (active: boolean) => void;
|
|
102
|
+
readonly applyPaneDividerAtCol: (col: number) => void;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface HandleMainPaneWheelInput {
|
|
106
|
+
readonly target: PointerTarget;
|
|
107
|
+
readonly wheelDelta: number | null;
|
|
108
|
+
readonly mainPaneMode: MainPaneMode;
|
|
109
|
+
readonly onProjectWheel: (delta: number) => void;
|
|
110
|
+
readonly onHomeWheel: (delta: number) => void;
|
|
111
|
+
readonly onNimWheel: (delta: number) => void;
|
|
112
|
+
readonly onConversationWheel: (delta: number) => void;
|
|
113
|
+
readonly markDirty: () => void;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface HandleHomePaneDragMoveInput {
|
|
117
|
+
readonly homePaneDragState: HomePaneDragState | null;
|
|
118
|
+
readonly mainPaneMode: MainPaneMode;
|
|
119
|
+
readonly target: PointerTarget;
|
|
120
|
+
readonly isSelectionDrag: boolean;
|
|
121
|
+
readonly hasAltModifier: boolean;
|
|
122
|
+
readonly rowIndex: number;
|
|
123
|
+
readonly setHomePaneDragState: (next: HomePaneDragState) => void;
|
|
124
|
+
readonly markDirty: () => void;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface PointerRoutingStrategies {
|
|
128
|
+
handlePaneDividerDragInput(options: HandlePaneDividerDragInput): boolean;
|
|
129
|
+
handleHomePaneDragRelease(options: HandleHomePaneDragReleaseInput): boolean;
|
|
130
|
+
handleSeparatorPointerPress(options: HandleSeparatorPointerPressInput): boolean;
|
|
131
|
+
handleMainPaneWheelInput(options: HandleMainPaneWheelInput): boolean;
|
|
132
|
+
handleHomePaneDragMove(options: HandleHomePaneDragMoveInput): boolean;
|
|
53
133
|
}
|
|
54
134
|
|
|
55
135
|
interface PointerEventInput {
|
|
@@ -61,30 +141,13 @@ interface PointerEventInput {
|
|
|
61
141
|
}
|
|
62
142
|
|
|
63
143
|
export class PointerRoutingInput {
|
|
64
|
-
private readonly handlePaneDividerDragInput: typeof handlePaneDividerDragInputFrame;
|
|
65
|
-
private readonly handleHomePaneDragReleaseInput: typeof handleHomePaneDragReleaseFrame;
|
|
66
|
-
private readonly handleSeparatorPointerPressInput: typeof handleSeparatorPointerPressFrame;
|
|
67
|
-
private readonly handleMainPaneWheelInput: typeof handleMainPaneWheelInputFrame;
|
|
68
|
-
private readonly handleHomePaneDragMoveInput: typeof handleHomePaneDragMoveFrame;
|
|
69
|
-
|
|
70
144
|
constructor(
|
|
71
145
|
private readonly options: PointerRoutingInputOptions,
|
|
72
|
-
|
|
73
|
-
) {
|
|
74
|
-
this.handlePaneDividerDragInput =
|
|
75
|
-
dependencies.handlePaneDividerDragInput ?? handlePaneDividerDragInputFrame;
|
|
76
|
-
this.handleHomePaneDragReleaseInput =
|
|
77
|
-
dependencies.handleHomePaneDragRelease ?? handleHomePaneDragReleaseFrame;
|
|
78
|
-
this.handleSeparatorPointerPressInput =
|
|
79
|
-
dependencies.handleSeparatorPointerPress ?? handleSeparatorPointerPressFrame;
|
|
80
|
-
this.handleMainPaneWheelInput =
|
|
81
|
-
dependencies.handleMainPaneWheelInput ?? handleMainPaneWheelInputFrame;
|
|
82
|
-
this.handleHomePaneDragMoveInput =
|
|
83
|
-
dependencies.handleHomePaneDragMove ?? handleHomePaneDragMoveFrame;
|
|
84
|
-
}
|
|
146
|
+
private readonly strategies: PointerRoutingStrategies,
|
|
147
|
+
) {}
|
|
85
148
|
|
|
86
149
|
handlePaneDividerDrag(event: Pick<PointerEventInput, 'code' | 'final' | 'col'>): boolean {
|
|
87
|
-
return this.handlePaneDividerDragInput({
|
|
150
|
+
return this.strategies.handlePaneDividerDragInput({
|
|
88
151
|
paneDividerDragActive: this.options.getPaneDividerDragActive(),
|
|
89
152
|
isMouseRelease: isMouseRelease(event.final),
|
|
90
153
|
isWheelMouseCode: isWheelMouseCode(event.code),
|
|
@@ -98,7 +161,7 @@ export class PointerRoutingInput {
|
|
|
98
161
|
handleHomePaneDragRelease(
|
|
99
162
|
event: Pick<PointerEventInput, 'final' | 'target' | 'rowIndex'>,
|
|
100
163
|
): boolean {
|
|
101
|
-
return this.
|
|
164
|
+
return this.strategies.handleHomePaneDragRelease({
|
|
102
165
|
homePaneDragState: this.options.getHomePaneDragState(),
|
|
103
166
|
isMouseRelease: isMouseRelease(event.final),
|
|
104
167
|
mainPaneMode: this.options.getMainPaneMode(),
|
|
@@ -116,7 +179,7 @@ export class PointerRoutingInput {
|
|
|
116
179
|
handleSeparatorPointerPress(
|
|
117
180
|
event: Pick<PointerEventInput, 'target' | 'code' | 'final' | 'col'>,
|
|
118
181
|
): boolean {
|
|
119
|
-
return this.
|
|
182
|
+
return this.strategies.handleSeparatorPointerPress({
|
|
120
183
|
target: event.target,
|
|
121
184
|
isLeftButtonPress: isLeftButtonPress(event.code, event.final),
|
|
122
185
|
hasAltModifier: hasAltModifier(event.code),
|
|
@@ -130,12 +193,13 @@ export class PointerRoutingInput {
|
|
|
130
193
|
event: Pick<PointerEventInput, 'target' | 'code'>,
|
|
131
194
|
onConversationWheel: (delta: number) => void,
|
|
132
195
|
): boolean {
|
|
133
|
-
return this.handleMainPaneWheelInput({
|
|
196
|
+
return this.strategies.handleMainPaneWheelInput({
|
|
134
197
|
target: event.target,
|
|
135
198
|
wheelDelta: wheelDeltaRowsFromCode(event.code),
|
|
136
199
|
mainPaneMode: this.options.getMainPaneMode(),
|
|
137
200
|
onProjectWheel: this.options.onProjectWheel,
|
|
138
201
|
onHomeWheel: this.options.onHomeWheel,
|
|
202
|
+
onNimWheel: this.options.onNimWheel,
|
|
139
203
|
onConversationWheel,
|
|
140
204
|
markDirty: this.options.markDirty,
|
|
141
205
|
});
|
|
@@ -144,7 +208,7 @@ export class PointerRoutingInput {
|
|
|
144
208
|
handleHomePaneDragMove(
|
|
145
209
|
event: Pick<PointerEventInput, 'target' | 'code' | 'final' | 'rowIndex'>,
|
|
146
210
|
): boolean {
|
|
147
|
-
return this.
|
|
211
|
+
return this.strategies.handleHomePaneDragMove({
|
|
148
212
|
homePaneDragState: this.options.getHomePaneDragState(),
|
|
149
213
|
mainPaneMode: this.options.getMainPaneMode(),
|
|
150
214
|
target: event.target,
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export interface HandlePointerClickInput {
|
|
2
|
+
readonly clickEligible: boolean;
|
|
3
|
+
readonly paneRows: number;
|
|
4
|
+
readonly leftCols: number;
|
|
5
|
+
readonly pointerRow: number;
|
|
6
|
+
readonly pointerCol: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface RailPointerHitResolver<THit> {
|
|
10
|
+
resolveHit(rowIndex: number, colIndex: number, railCols: number): THit | null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface RailPointerHitDispatcher<THit> {
|
|
14
|
+
dispatchHit(hit: THit): boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface RailPointerEditController<THit> {
|
|
18
|
+
hasActiveEdit(): boolean;
|
|
19
|
+
shouldKeepActiveEdit(hit: THit): boolean;
|
|
20
|
+
stopActiveEdit(): void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface RailPointerSelectionController {
|
|
24
|
+
hasSelection(): boolean;
|
|
25
|
+
clearSelection(): void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class RailPointerInput<THit> {
|
|
29
|
+
constructor(
|
|
30
|
+
private readonly hitResolver: RailPointerHitResolver<THit>,
|
|
31
|
+
private readonly hitDispatcher: RailPointerHitDispatcher<THit>,
|
|
32
|
+
private readonly editController: RailPointerEditController<THit> | null = null,
|
|
33
|
+
private readonly selectionController: RailPointerSelectionController | null = null,
|
|
34
|
+
) {}
|
|
35
|
+
|
|
36
|
+
handlePointerClick(input: HandlePointerClickInput): boolean {
|
|
37
|
+
if (!input.clickEligible) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const rowIndex = Math.max(0, Math.min(input.paneRows - 1, input.pointerRow - 1));
|
|
42
|
+
const colIndex = Math.max(0, Math.min(input.leftCols - 1, input.pointerCol - 1));
|
|
43
|
+
const hit = this.hitResolver.resolveHit(rowIndex, colIndex, input.leftCols);
|
|
44
|
+
if (hit === null) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (
|
|
49
|
+
this.editController !== null &&
|
|
50
|
+
this.editController.hasActiveEdit() &&
|
|
51
|
+
!this.editController.shouldKeepActiveEdit(hit)
|
|
52
|
+
) {
|
|
53
|
+
this.editController.stopActiveEdit();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (this.selectionController !== null && this.selectionController.hasSelection()) {
|
|
57
|
+
this.selectionController.clearSelection();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return this.hitDispatcher.dispatchHit(hit);
|
|
61
|
+
}
|
|
62
|
+
}
|