@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,224 @@
|
|
|
1
|
+
import { buildFinderResults } from './finder.ts';
|
|
2
|
+
import { maxTopRowForModel } from './model.ts';
|
|
3
|
+
import type { DiffUiModel, DiffUiState, DiffUiStateAction, DiffUiViewMode } from './types.ts';
|
|
4
|
+
|
|
5
|
+
const AUTO_SPLIT_MIN_WIDTH = 120;
|
|
6
|
+
|
|
7
|
+
function clamp(value: number, min: number, max: number): number {
|
|
8
|
+
if (value < min) {
|
|
9
|
+
return min;
|
|
10
|
+
}
|
|
11
|
+
if (value > max) {
|
|
12
|
+
return max;
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function clampIndex(value: number, length: number): number {
|
|
18
|
+
if (length <= 0) {
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
21
|
+
return clamp(value, 0, length - 1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function firstHunkIndexForFile(model: DiffUiModel, fileIndex: number): number {
|
|
25
|
+
const row = model.rows.find(
|
|
26
|
+
(candidate) => candidate.kind === 'hunk-header' && candidate.fileIndex === fileIndex,
|
|
27
|
+
);
|
|
28
|
+
return row?.hunkIndex ?? 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function resolveTopForFile(model: DiffUiModel, fileIndex: number): number {
|
|
32
|
+
if (model.fileStartRows.length === 0) {
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
return model.fileStartRows[clampIndex(fileIndex, model.fileStartRows.length)] ?? 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function resolveTopForHunk(model: DiffUiModel, hunkIndex: number): number {
|
|
39
|
+
if (model.hunkStartRows.length === 0) {
|
|
40
|
+
return 0;
|
|
41
|
+
}
|
|
42
|
+
return model.hunkStartRows[clampIndex(hunkIndex, model.hunkStartRows.length)] ?? 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function resolveEffectiveViewMode(
|
|
46
|
+
viewMode: DiffUiViewMode,
|
|
47
|
+
viewportWidth: number,
|
|
48
|
+
): 'split' | 'unified' {
|
|
49
|
+
if (viewMode === 'split') {
|
|
50
|
+
return 'split';
|
|
51
|
+
}
|
|
52
|
+
if (viewMode === 'unified') {
|
|
53
|
+
return 'unified';
|
|
54
|
+
}
|
|
55
|
+
return viewportWidth >= AUTO_SPLIT_MIN_WIDTH ? 'split' : 'unified';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function createInitialDiffUiState(
|
|
59
|
+
model: DiffUiModel,
|
|
60
|
+
viewMode: DiffUiViewMode,
|
|
61
|
+
viewportWidth: number,
|
|
62
|
+
): DiffUiState {
|
|
63
|
+
return {
|
|
64
|
+
viewMode,
|
|
65
|
+
effectiveViewMode: resolveEffectiveViewMode(viewMode, viewportWidth),
|
|
66
|
+
topRow: 0,
|
|
67
|
+
activeFileIndex: model.diff.files.length === 0 ? 0 : 0,
|
|
68
|
+
activeHunkIndex: model.hunkStartRows.length === 0 ? 0 : 0,
|
|
69
|
+
finderOpen: false,
|
|
70
|
+
finderQuery: '',
|
|
71
|
+
finderSelectedIndex: 0,
|
|
72
|
+
finderResults: buildFinderResults(model, ''),
|
|
73
|
+
searchQuery: '',
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function withClampedTop(
|
|
78
|
+
state: DiffUiState,
|
|
79
|
+
model: DiffUiModel,
|
|
80
|
+
viewportHeight: number,
|
|
81
|
+
): DiffUiState {
|
|
82
|
+
const maxTop = maxTopRowForModel(model, viewportHeight);
|
|
83
|
+
return {
|
|
84
|
+
...state,
|
|
85
|
+
topRow: clamp(state.topRow, 0, maxTop),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function reduceDiffUiState(input: {
|
|
90
|
+
readonly model: DiffUiModel;
|
|
91
|
+
readonly state: DiffUiState;
|
|
92
|
+
readonly action: DiffUiStateAction;
|
|
93
|
+
readonly viewportWidth: number;
|
|
94
|
+
readonly viewportHeight: number;
|
|
95
|
+
}): DiffUiState {
|
|
96
|
+
const { model, action, viewportHeight, viewportWidth } = input;
|
|
97
|
+
let next: DiffUiState = input.state;
|
|
98
|
+
|
|
99
|
+
if (action.type === 'viewport.changed') {
|
|
100
|
+
next = {
|
|
101
|
+
...next,
|
|
102
|
+
effectiveViewMode: resolveEffectiveViewMode(next.viewMode, action.width),
|
|
103
|
+
};
|
|
104
|
+
return withClampedTop(next, model, viewportHeight);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (action.type === 'view.setMode') {
|
|
108
|
+
next = {
|
|
109
|
+
...next,
|
|
110
|
+
viewMode: action.mode,
|
|
111
|
+
effectiveViewMode: resolveEffectiveViewMode(action.mode, viewportWidth),
|
|
112
|
+
};
|
|
113
|
+
return withClampedTop(next, model, viewportHeight);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (action.type === 'nav.scroll') {
|
|
117
|
+
next = {
|
|
118
|
+
...next,
|
|
119
|
+
topRow: next.topRow + action.delta,
|
|
120
|
+
};
|
|
121
|
+
return withClampedTop(next, model, viewportHeight);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (action.type === 'nav.page') {
|
|
125
|
+
next = {
|
|
126
|
+
...next,
|
|
127
|
+
topRow: next.topRow + action.delta * Math.max(1, action.pageSize),
|
|
128
|
+
};
|
|
129
|
+
return withClampedTop(next, model, viewportHeight);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (action.type === 'nav.gotoFile') {
|
|
133
|
+
const fileIndex = clampIndex(action.fileIndex, model.diff.files.length);
|
|
134
|
+
next = {
|
|
135
|
+
...next,
|
|
136
|
+
activeFileIndex: fileIndex,
|
|
137
|
+
activeHunkIndex: firstHunkIndexForFile(model, fileIndex),
|
|
138
|
+
topRow: resolveTopForFile(model, fileIndex),
|
|
139
|
+
finderOpen: false,
|
|
140
|
+
};
|
|
141
|
+
return withClampedTop(next, model, viewportHeight);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (action.type === 'nav.gotoHunk') {
|
|
145
|
+
const hunkIndex = clampIndex(action.hunkIndex, model.hunkStartRows.length);
|
|
146
|
+
const hunkRow = model.rows.find(
|
|
147
|
+
(row) => row.kind === 'hunk-header' && row.hunkIndex === hunkIndex,
|
|
148
|
+
);
|
|
149
|
+
next = {
|
|
150
|
+
...next,
|
|
151
|
+
activeHunkIndex: hunkIndex,
|
|
152
|
+
activeFileIndex: hunkRow?.fileIndex ?? next.activeFileIndex,
|
|
153
|
+
topRow: resolveTopForHunk(model, hunkIndex),
|
|
154
|
+
finderOpen: false,
|
|
155
|
+
};
|
|
156
|
+
return withClampedTop(next, model, viewportHeight);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (action.type === 'finder.open') {
|
|
160
|
+
next = {
|
|
161
|
+
...next,
|
|
162
|
+
finderOpen: true,
|
|
163
|
+
finderResults: buildFinderResults(model, next.finderQuery),
|
|
164
|
+
finderSelectedIndex: 0,
|
|
165
|
+
};
|
|
166
|
+
return withClampedTop(next, model, viewportHeight);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (action.type === 'finder.close') {
|
|
170
|
+
next = {
|
|
171
|
+
...next,
|
|
172
|
+
finderOpen: false,
|
|
173
|
+
};
|
|
174
|
+
return withClampedTop(next, model, viewportHeight);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (action.type === 'finder.query') {
|
|
178
|
+
const results = buildFinderResults(model, action.query);
|
|
179
|
+
next = {
|
|
180
|
+
...next,
|
|
181
|
+
finderOpen: true,
|
|
182
|
+
finderQuery: action.query,
|
|
183
|
+
finderResults: results,
|
|
184
|
+
finderSelectedIndex: 0,
|
|
185
|
+
};
|
|
186
|
+
return withClampedTop(next, model, viewportHeight);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (action.type === 'finder.move') {
|
|
190
|
+
const selected = clampIndex(next.finderSelectedIndex + action.delta, next.finderResults.length);
|
|
191
|
+
next = {
|
|
192
|
+
...next,
|
|
193
|
+
finderSelectedIndex: selected,
|
|
194
|
+
};
|
|
195
|
+
return withClampedTop(next, model, viewportHeight);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (action.type === 'finder.accept') {
|
|
199
|
+
if (next.finderResults.length === 0) {
|
|
200
|
+
return withClampedTop(next, model, viewportHeight);
|
|
201
|
+
}
|
|
202
|
+
const selection =
|
|
203
|
+
next.finderResults[clampIndex(next.finderSelectedIndex, next.finderResults.length)] ??
|
|
204
|
+
next.finderResults[0]!;
|
|
205
|
+
next = {
|
|
206
|
+
...next,
|
|
207
|
+
activeFileIndex: selection.fileIndex,
|
|
208
|
+
activeHunkIndex: firstHunkIndexForFile(model, selection.fileIndex),
|
|
209
|
+
topRow: resolveTopForFile(model, selection.fileIndex),
|
|
210
|
+
finderOpen: false,
|
|
211
|
+
};
|
|
212
|
+
return withClampedTop(next, model, viewportHeight);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (action.type === 'search.set') {
|
|
216
|
+
next = {
|
|
217
|
+
...next,
|
|
218
|
+
searchQuery: action.query,
|
|
219
|
+
};
|
|
220
|
+
return withClampedTop(next, model, viewportHeight);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return withClampedTop(next, model, viewportHeight);
|
|
224
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import type { DiffCoverageReason, DiffMode, NormalizedDiff } from '../diff/types.ts';
|
|
2
|
+
|
|
3
|
+
export type DiffUiViewMode = 'auto' | 'split' | 'unified';
|
|
4
|
+
type DiffUiEffectiveViewMode = 'split' | 'unified';
|
|
5
|
+
export type DiffUiSyntaxMode = 'auto' | 'on' | 'off';
|
|
6
|
+
export type DiffUiWordDiffMode = 'auto' | 'on' | 'off';
|
|
7
|
+
|
|
8
|
+
interface DiffUiBudget {
|
|
9
|
+
readonly maxFiles: number;
|
|
10
|
+
readonly maxHunks: number;
|
|
11
|
+
readonly maxLines: number;
|
|
12
|
+
readonly maxBytes: number;
|
|
13
|
+
readonly maxRuntimeMs: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface DiffUiCliOptions {
|
|
17
|
+
readonly cwd: string;
|
|
18
|
+
readonly mode: DiffMode;
|
|
19
|
+
readonly baseRef: string | null;
|
|
20
|
+
readonly headRef: string | null;
|
|
21
|
+
readonly includeGenerated: boolean;
|
|
22
|
+
readonly includeBinary: boolean;
|
|
23
|
+
readonly noRenames: boolean;
|
|
24
|
+
readonly renameLimit: number | null;
|
|
25
|
+
readonly viewMode: DiffUiViewMode;
|
|
26
|
+
readonly syntaxMode: DiffUiSyntaxMode;
|
|
27
|
+
readonly wordDiffMode: DiffUiWordDiffMode;
|
|
28
|
+
readonly color: boolean;
|
|
29
|
+
readonly pager: boolean;
|
|
30
|
+
readonly watch: boolean;
|
|
31
|
+
readonly jsonEvents: boolean;
|
|
32
|
+
readonly rpcStdio: boolean;
|
|
33
|
+
readonly snapshot: boolean;
|
|
34
|
+
readonly width: number | null;
|
|
35
|
+
readonly height: number | null;
|
|
36
|
+
readonly theme: string | null;
|
|
37
|
+
readonly budget: DiffUiBudget;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type DiffUiRowKind =
|
|
41
|
+
| 'file-header'
|
|
42
|
+
| 'hunk-header'
|
|
43
|
+
| 'code-context'
|
|
44
|
+
| 'code-add'
|
|
45
|
+
| 'code-del'
|
|
46
|
+
| 'notice';
|
|
47
|
+
|
|
48
|
+
export interface DiffUiVirtualRow {
|
|
49
|
+
readonly kind: DiffUiRowKind;
|
|
50
|
+
readonly unified: string;
|
|
51
|
+
readonly left: string;
|
|
52
|
+
readonly right: string;
|
|
53
|
+
readonly fileId: string | null;
|
|
54
|
+
readonly hunkId: string | null;
|
|
55
|
+
readonly fileIndex: number | null;
|
|
56
|
+
readonly hunkIndex: number | null;
|
|
57
|
+
readonly language: string | null;
|
|
58
|
+
readonly oldLine: number | null;
|
|
59
|
+
readonly newLine: number | null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface DiffUiModel {
|
|
63
|
+
readonly diff: NormalizedDiff;
|
|
64
|
+
readonly rows: readonly DiffUiVirtualRow[];
|
|
65
|
+
readonly fileStartRows: readonly number[];
|
|
66
|
+
readonly hunkStartRows: readonly number[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface DiffUiFinderResult {
|
|
70
|
+
readonly fileIndex: number;
|
|
71
|
+
readonly fileId: string;
|
|
72
|
+
readonly path: string;
|
|
73
|
+
readonly score: number;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface DiffUiState {
|
|
77
|
+
readonly viewMode: DiffUiViewMode;
|
|
78
|
+
readonly effectiveViewMode: DiffUiEffectiveViewMode;
|
|
79
|
+
readonly topRow: number;
|
|
80
|
+
readonly activeFileIndex: number;
|
|
81
|
+
readonly activeHunkIndex: number;
|
|
82
|
+
readonly finderOpen: boolean;
|
|
83
|
+
readonly finderQuery: string;
|
|
84
|
+
readonly finderSelectedIndex: number;
|
|
85
|
+
readonly finderResults: readonly DiffUiFinderResult[];
|
|
86
|
+
readonly searchQuery: string;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export type DiffUiStateAction =
|
|
90
|
+
| {
|
|
91
|
+
readonly type: 'viewport.changed';
|
|
92
|
+
readonly width: number;
|
|
93
|
+
}
|
|
94
|
+
| {
|
|
95
|
+
readonly type: 'view.setMode';
|
|
96
|
+
readonly mode: DiffUiViewMode;
|
|
97
|
+
}
|
|
98
|
+
| {
|
|
99
|
+
readonly type: 'nav.scroll';
|
|
100
|
+
readonly delta: number;
|
|
101
|
+
}
|
|
102
|
+
| {
|
|
103
|
+
readonly type: 'nav.page';
|
|
104
|
+
readonly delta: number;
|
|
105
|
+
readonly pageSize: number;
|
|
106
|
+
}
|
|
107
|
+
| {
|
|
108
|
+
readonly type: 'nav.gotoFile';
|
|
109
|
+
readonly fileIndex: number;
|
|
110
|
+
}
|
|
111
|
+
| {
|
|
112
|
+
readonly type: 'nav.gotoHunk';
|
|
113
|
+
readonly hunkIndex: number;
|
|
114
|
+
}
|
|
115
|
+
| {
|
|
116
|
+
readonly type: 'finder.open';
|
|
117
|
+
}
|
|
118
|
+
| {
|
|
119
|
+
readonly type: 'finder.close';
|
|
120
|
+
}
|
|
121
|
+
| {
|
|
122
|
+
readonly type: 'finder.query';
|
|
123
|
+
readonly query: string;
|
|
124
|
+
}
|
|
125
|
+
| {
|
|
126
|
+
readonly type: 'finder.move';
|
|
127
|
+
readonly delta: number;
|
|
128
|
+
}
|
|
129
|
+
| {
|
|
130
|
+
readonly type: 'finder.accept';
|
|
131
|
+
}
|
|
132
|
+
| {
|
|
133
|
+
readonly type: 'search.set';
|
|
134
|
+
readonly query: string;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export type DiffUiCommand =
|
|
138
|
+
| {
|
|
139
|
+
readonly type: 'view.setMode';
|
|
140
|
+
readonly mode: DiffUiViewMode;
|
|
141
|
+
}
|
|
142
|
+
| {
|
|
143
|
+
readonly type: 'nav.scroll';
|
|
144
|
+
readonly delta: number;
|
|
145
|
+
}
|
|
146
|
+
| {
|
|
147
|
+
readonly type: 'nav.page';
|
|
148
|
+
readonly delta: number;
|
|
149
|
+
}
|
|
150
|
+
| {
|
|
151
|
+
readonly type: 'nav.gotoFile';
|
|
152
|
+
readonly index: number;
|
|
153
|
+
}
|
|
154
|
+
| {
|
|
155
|
+
readonly type: 'nav.gotoHunk';
|
|
156
|
+
readonly index: number;
|
|
157
|
+
}
|
|
158
|
+
| {
|
|
159
|
+
readonly type: 'finder.open';
|
|
160
|
+
}
|
|
161
|
+
| {
|
|
162
|
+
readonly type: 'finder.close';
|
|
163
|
+
}
|
|
164
|
+
| {
|
|
165
|
+
readonly type: 'finder.query';
|
|
166
|
+
readonly query: string;
|
|
167
|
+
}
|
|
168
|
+
| {
|
|
169
|
+
readonly type: 'finder.move';
|
|
170
|
+
readonly delta: number;
|
|
171
|
+
}
|
|
172
|
+
| {
|
|
173
|
+
readonly type: 'finder.accept';
|
|
174
|
+
}
|
|
175
|
+
| {
|
|
176
|
+
readonly type: 'search.set';
|
|
177
|
+
readonly query: string;
|
|
178
|
+
}
|
|
179
|
+
| {
|
|
180
|
+
readonly type: 'session.quit';
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
export type DiffUiEvent =
|
|
184
|
+
| {
|
|
185
|
+
readonly type: 'diff.loaded';
|
|
186
|
+
readonly files: number;
|
|
187
|
+
readonly hunks: number;
|
|
188
|
+
readonly lines: number;
|
|
189
|
+
readonly coverageReason: DiffCoverageReason;
|
|
190
|
+
}
|
|
191
|
+
| {
|
|
192
|
+
readonly type: 'state.changed';
|
|
193
|
+
readonly state: DiffUiState;
|
|
194
|
+
}
|
|
195
|
+
| {
|
|
196
|
+
readonly type: 'render.completed';
|
|
197
|
+
readonly rows: number;
|
|
198
|
+
readonly width: number;
|
|
199
|
+
readonly height: number;
|
|
200
|
+
readonly view: DiffUiEffectiveViewMode;
|
|
201
|
+
}
|
|
202
|
+
| {
|
|
203
|
+
readonly type: 'warning';
|
|
204
|
+
readonly message: string;
|
|
205
|
+
}
|
|
206
|
+
| {
|
|
207
|
+
readonly type: 'session.quit';
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
export interface DiffUiRenderTheme {
|
|
211
|
+
readonly headerAnsi: string;
|
|
212
|
+
readonly footerAnsi: string;
|
|
213
|
+
readonly fileHeaderAnsi: string;
|
|
214
|
+
readonly hunkHeaderAnsi: string;
|
|
215
|
+
readonly contextAnsi: string;
|
|
216
|
+
readonly addAnsi: string;
|
|
217
|
+
readonly delAnsi: string;
|
|
218
|
+
readonly noticeAnsi: string;
|
|
219
|
+
readonly gutterAnsi: string;
|
|
220
|
+
readonly resetAnsi: string;
|
|
221
|
+
readonly syntaxKeywordAnsi: string;
|
|
222
|
+
readonly syntaxStringAnsi: string;
|
|
223
|
+
readonly syntaxCommentAnsi: string;
|
|
224
|
+
readonly syntaxNumberAnsi: string;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export interface DiffUiRenderOutput {
|
|
228
|
+
readonly lines: readonly string[];
|
|
229
|
+
readonly state: DiffUiState;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export interface DiffUiRunOutput {
|
|
233
|
+
readonly exitCode: number;
|
|
234
|
+
readonly events: readonly DiffUiEvent[];
|
|
235
|
+
readonly renderedLines: readonly string[];
|
|
236
|
+
}
|
package/src/domain/workspace.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { ProjectPaneSnapshot } from '../mux/harness-core-ui.ts';
|
|
2
2
|
import type { CommandMenuState } from '../mux/live-mux/command-menu.ts';
|
|
3
3
|
import type { LeftNavSelection } from '../mux/live-mux/left-nav.ts';
|
|
4
|
+
import type { LinePromptInputState } from '../mux/live-mux/modal-input-reducers.ts';
|
|
4
5
|
import type { PaneSelection, PaneSelectionDrag } from '../mux/live-mux/selection.ts';
|
|
5
6
|
import type { createNewThreadPromptState } from '../mux/new-thread-prompt.ts';
|
|
6
7
|
import type { TaskComposerBuffer } from '../mux/task-composer.ts';
|
|
7
8
|
import type { TaskFocusedPaneView } from '../mux/task-focused-pane.ts';
|
|
8
9
|
import type { buildWorkspaceRailViewRows } from '../mux/workspace-rail-model.ts';
|
|
9
10
|
|
|
10
|
-
type MainPaneMode = 'conversation' | 'project' | 'home';
|
|
11
|
+
type MainPaneMode = 'conversation' | 'project' | 'home' | 'nim';
|
|
11
12
|
|
|
12
13
|
export interface ConversationTitleEditState {
|
|
13
14
|
conversationId: string;
|
|
@@ -25,19 +26,20 @@ export interface RepositoryPromptState {
|
|
|
25
26
|
readonly error: string | null;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
interface ApiKeyPromptState {
|
|
29
30
|
readonly keyName: string;
|
|
30
31
|
readonly displayName: string;
|
|
31
32
|
readonly value: string;
|
|
32
33
|
readonly error: string | null;
|
|
33
34
|
readonly hasExistingValue: boolean;
|
|
35
|
+
readonly lineInputState?: LinePromptInputState;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
export interface TaskEditorPromptState {
|
|
37
39
|
mode: 'create' | 'edit';
|
|
38
40
|
taskId: string | null;
|
|
39
41
|
title: string;
|
|
40
|
-
|
|
42
|
+
body: string;
|
|
41
43
|
repositoryIds: readonly string[];
|
|
42
44
|
repositoryIndex: number;
|
|
43
45
|
fieldIndex: 0 | 1 | 2;
|
|
@@ -60,7 +62,8 @@ interface WorkspaceModelInit {
|
|
|
60
62
|
latestTaskPaneView: TaskFocusedPaneView;
|
|
61
63
|
taskDraftComposer: TaskComposerBuffer;
|
|
62
64
|
repositoriesCollapsed: boolean;
|
|
63
|
-
shortcutsCollapsed
|
|
65
|
+
shortcutsCollapsed?: boolean;
|
|
66
|
+
showDebugBar?: boolean;
|
|
64
67
|
}
|
|
65
68
|
|
|
66
69
|
export class WorkspaceModel {
|
|
@@ -98,9 +101,12 @@ export class WorkspaceModel {
|
|
|
98
101
|
paneDividerDragActive = false;
|
|
99
102
|
previousSelectionRows: readonly number[] = [];
|
|
100
103
|
latestRailViewRows: ReturnType<typeof buildWorkspaceRailViewRows> = [];
|
|
104
|
+
visibleGitHubDirectoryIds = new Set<string>();
|
|
105
|
+
expandedGitHubDirectoryIds = new Set<string>();
|
|
101
106
|
|
|
102
107
|
repositoriesCollapsed: boolean;
|
|
103
108
|
shortcutsCollapsed: boolean;
|
|
109
|
+
showDebugBar: boolean;
|
|
104
110
|
|
|
105
111
|
constructor(init: WorkspaceModelInit) {
|
|
106
112
|
this.activeDirectoryId = init.activeDirectoryId;
|
|
@@ -108,7 +114,8 @@ export class WorkspaceModel {
|
|
|
108
114
|
this.latestTaskPaneView = init.latestTaskPaneView;
|
|
109
115
|
this.taskDraftComposer = init.taskDraftComposer;
|
|
110
116
|
this.repositoriesCollapsed = init.repositoriesCollapsed;
|
|
111
|
-
this.shortcutsCollapsed = init.shortcutsCollapsed;
|
|
117
|
+
this.shortcutsCollapsed = init.shortcutsCollapsed ?? false;
|
|
118
|
+
this.showDebugBar = init.showDebugBar ?? false;
|
|
112
119
|
}
|
|
113
120
|
|
|
114
121
|
selectLeftNavHome(): void {
|
|
@@ -117,6 +124,18 @@ export class WorkspaceModel {
|
|
|
117
124
|
};
|
|
118
125
|
}
|
|
119
126
|
|
|
127
|
+
selectLeftNavNim(): void {
|
|
128
|
+
this.leftNavSelection = {
|
|
129
|
+
kind: 'nim',
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
selectLeftNavTasks(): void {
|
|
134
|
+
this.leftNavSelection = {
|
|
135
|
+
kind: 'tasks',
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
120
139
|
selectLeftNavRepository(repositoryGroupId: string): void {
|
|
121
140
|
this.activeRepositorySelectionId = repositoryGroupId;
|
|
122
141
|
this.leftNavSelection = {
|
|
@@ -133,6 +152,14 @@ export class WorkspaceModel {
|
|
|
133
152
|
};
|
|
134
153
|
}
|
|
135
154
|
|
|
155
|
+
selectLeftNavGitHub(directoryId: string, repositoryGroupId: string): void {
|
|
156
|
+
this.activeRepositorySelectionId = repositoryGroupId;
|
|
157
|
+
this.leftNavSelection = {
|
|
158
|
+
kind: 'github',
|
|
159
|
+
directoryId,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
136
163
|
selectLeftNavConversation(sessionId: string): void {
|
|
137
164
|
this.leftNavSelection = {
|
|
138
165
|
kind: 'conversation',
|
|
@@ -150,6 +177,16 @@ export class WorkspaceModel {
|
|
|
150
177
|
this.projectPaneScrollTop = 0;
|
|
151
178
|
}
|
|
152
179
|
|
|
180
|
+
enterGitHubPane(directoryId: string, repositoryGroupId: string): void {
|
|
181
|
+
this.activeDirectoryId = directoryId;
|
|
182
|
+
this.selectLeftNavGitHub(directoryId, repositoryGroupId);
|
|
183
|
+
this.mainPaneMode = 'project';
|
|
184
|
+
this.homePaneDragState = null;
|
|
185
|
+
this.taskPaneTaskEditClickState = null;
|
|
186
|
+
this.taskPaneRepositoryEditClickState = null;
|
|
187
|
+
this.projectPaneScrollTop = 0;
|
|
188
|
+
}
|
|
189
|
+
|
|
153
190
|
enterHomePane(): void {
|
|
154
191
|
this.mainPaneMode = 'home';
|
|
155
192
|
this.selectLeftNavHome();
|
|
@@ -162,4 +199,30 @@ export class WorkspaceModel {
|
|
|
162
199
|
this.taskPaneRepositoryEditClickState = null;
|
|
163
200
|
this.homePaneDragState = null;
|
|
164
201
|
}
|
|
202
|
+
|
|
203
|
+
enterNimPane(): void {
|
|
204
|
+
this.mainPaneMode = 'nim';
|
|
205
|
+
this.selectLeftNavNim();
|
|
206
|
+
this.projectPaneSnapshot = null;
|
|
207
|
+
this.projectPaneScrollTop = 0;
|
|
208
|
+
this.taskPaneScrollTop = 0;
|
|
209
|
+
this.taskPaneNotice = null;
|
|
210
|
+
this.taskRepositoryDropdownOpen = false;
|
|
211
|
+
this.taskPaneTaskEditClickState = null;
|
|
212
|
+
this.taskPaneRepositoryEditClickState = null;
|
|
213
|
+
this.homePaneDragState = null;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
enterTasksPane(): void {
|
|
217
|
+
this.mainPaneMode = 'home';
|
|
218
|
+
this.selectLeftNavTasks();
|
|
219
|
+
this.projectPaneSnapshot = null;
|
|
220
|
+
this.projectPaneScrollTop = 0;
|
|
221
|
+
this.taskPaneScrollTop = 0;
|
|
222
|
+
this.taskPaneNotice = null;
|
|
223
|
+
this.taskRepositoryDropdownOpen = false;
|
|
224
|
+
this.taskPaneTaskEditClickState = null;
|
|
225
|
+
this.taskPaneRepositoryEditClickState = null;
|
|
226
|
+
this.homePaneDragState = null;
|
|
227
|
+
}
|
|
165
228
|
}
|