@aiworker/web 1.22.2 → 1.23.1
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/bin/cli.js +7 -0
- package/dist/assets/{CodeMirrorCommentWidgets-CSICpWQD.js → CodeMirrorCommentWidgets-teB3HmBT.js} +1 -1
- package/dist/assets/DiffView-DC9RX3MF.js +4 -0
- package/dist/assets/{DirtyBranchSwitchDialog-C22HdWj4.js → DirtyBranchSwitchDialog-DwPkzXsS.js} +1 -1
- package/dist/assets/FilesView-BFZkuu4O.js +13 -0
- package/dist/assets/{GitView-VQQkYuUw.js → GitView-B_x52MHY.js} +4 -4
- package/dist/assets/{JsonTreeView-6lrhwD3q.js → JsonTreeView-cmAzUTiw.js} +1 -1
- package/dist/assets/{LinearIssuesView-Ba6Q9569.js → LinearIssuesView-Dehh5afm.js} +1 -1
- package/dist/assets/MarkdownImageGallery-DgNOqqhE.js +1 -0
- package/dist/assets/MarkdownRendererImpl-DusmMZ_p.js +12 -0
- package/dist/assets/OnboardingScreen-B8fm1H4m.js +1 -0
- package/dist/assets/{PatchDiff-BzcZPlOq.js → PatchDiff-BVr7gJU0.js} +1 -1
- package/dist/assets/PierreDiffViewer-DzLt-nel.js +161 -0
- package/dist/assets/PlanView-BmQUYT_z.js +5 -0
- package/dist/assets/SettingsView-ChWmAcZT.js +28 -0
- package/dist/assets/{SettingsWindow-DDQYaKWQ.js → SettingsWindow-Dy3M6ZB9.js} +1 -1
- package/dist/assets/SymbolsNerdFontMono-Regular-aK5vsLov.woff2 +0 -0
- package/dist/assets/{ToolOutputDialog-iYF8Y2bz.js → ToolOutputDialog-cTl_eLIH.js} +5 -5
- package/dist/assets/{ToolPartDiffPreview-CdjbOVMM.js → ToolPartDiffPreview-C_3iYNJQ.js} +1 -1
- package/dist/assets/{VirtualizedFileDiff-fFFm2QgH.js → VirtualizedFileDiff-CAWthkoy.js} +4 -4
- package/dist/assets/WalkthroughView-C7AHOpCA.js +4 -0
- package/dist/assets/{computeVirtualFileMetrics-CzgLum_K.js → computeVirtualFileMetrics-BkiDSlTW.js} +2 -2
- package/dist/assets/contextPanelEmbeddedChat-BdA6bdky.js +1 -0
- package/dist/assets/de-DJsQr-p7.js +10 -0
- package/dist/assets/es-BuYMlA9g.js +16 -0
- package/dist/assets/fr-ePDay33C.js +14 -0
- package/dist/assets/ghostty-vt-DdA0Zryv.wasm +0 -0
- package/dist/assets/index-BD8ILbxF.js +40 -0
- package/dist/assets/{index-Bl3LSbjn.js → index-n6O3hklM.js} +1 -1
- package/dist/assets/index-zhgg-SDz.css +1 -0
- package/dist/assets/{isDiffPlainText-Dx-dB_8l.js → isDiffPlainText-GMdPz-71.js} +1 -1
- package/dist/assets/{isFilePlainText-CWFPwmnU.js → isFilePlainText-BVuH1zH9.js} +1 -1
- package/dist/assets/ja-BzR9xOY_.js +16 -0
- package/dist/assets/ko-BEjHAm14.js +16 -0
- package/dist/assets/main-BydQ1tr_.js +930 -0
- package/dist/assets/{main-DnBAoktx.js → main-D3ZDQglc.js} +2 -2
- package/dist/assets/markdownCore-Cb2MdZnV.js +5 -0
- package/dist/assets/{miniChat-BKyvLseN.js → miniChat-d01k801U.js} +2 -2
- package/dist/assets/{mobile-DKjeFgMN.js → mobile-D7VV-Ufz.js} +2 -2
- package/dist/assets/patchFileDiff-CdztVMz0.js +13 -0
- package/dist/assets/pl-rQmpxNIF.js +16 -0
- package/dist/assets/pt-BR-CorbsSW-.js +16 -0
- package/dist/assets/renderElectronMiniChatApp-BGG9UIlh.js +2 -0
- package/dist/assets/renderMobileApp-BRP3J1iv.js +25 -0
- package/dist/assets/runtimeEndpointReset-BhT_-c52.js +41 -0
- package/dist/assets/scheduledTasksApi-BEuUF_cj.js +1 -0
- package/dist/assets/{shikiHighlight-TEQCmzW8.js → shikiHighlight-BzXUjlby.js} +1 -1
- package/dist/assets/surface-COKb77Ua.js +1 -0
- package/dist/assets/tr-CoAMXPtI.js +7 -0
- package/dist/assets/uk-YlAX0rRU.js +16 -0
- package/dist/assets/{useAppFontEffects-D7sNpuL2.js → useAppFontEffects-Cwyhjfl5.js} +141 -140
- package/dist/assets/useGitComparison-BCZVefdE.js +1 -0
- package/dist/assets/useInlineCommentController-DDkpBEU8.js +2 -0
- package/dist/assets/useRootScrollLock-CwjoWuC4.js +1 -0
- package/dist/assets/{vendor-base-ui-BeMD8X_w.js → vendor-base-ui-BY_5qla_.js} +1 -1
- package/dist/assets/{vendor-codemirror-language-data-Dp3KMRQG.js → vendor-codemirror-language-data-Cb1YnICj.js} +2 -2
- package/dist/assets/vendor-legendapp-list-BuBPBC3R.js +1 -0
- package/dist/assets/vendor-zod-B6551dwG.js +39 -0
- package/dist/assets/{web-components-_mNEMjFY.js → web-components-LV3vjTxy.js} +1 -1
- package/dist/assets/zh-CN-DDYg0Jl7.js +16 -0
- package/dist/assets/zh-TW-CQKaCayr.js +16 -0
- package/dist/index.html +5 -43
- package/dist/mini-chat.html +4 -4
- package/dist/mobile.html +4 -4
- package/package.json +2 -3
- package/server/index.d.ts +15 -0
- package/server/index.js +48 -22
- package/server/lib/agent-memory/runtime.js +7 -2
- package/server/lib/agent-memory/runtime.test.js +22 -0
- package/server/lib/data-dir-migration.js +40 -0
- package/server/lib/data-dir-migration.test.js +59 -0
- package/server/lib/event-stream/DOCUMENTATION.md +3 -0
- package/server/lib/event-stream/global-hub.js +29 -4
- package/server/lib/event-stream/global-hub.test.js +22 -0
- package/server/lib/event-stream/global-ws-bridge.js +15 -18
- package/server/lib/event-stream/protocol.js +21 -7
- package/server/lib/event-stream/runtime.js +9 -6
- package/server/lib/event-stream/runtime.test.js +72 -12
- package/server/lib/fs/DOCUMENTATION.md +3 -0
- package/server/lib/fs/routes.js +31 -0
- package/server/lib/fs/routes.test.js +110 -1
- package/server/lib/git/DOCUMENTATION.md +39 -6
- package/server/lib/git/routes.js +45 -12
- package/server/lib/git/routes.test.js +78 -0
- package/server/lib/git/service.js +343 -202
- package/server/lib/git/service.test.js +496 -25
- package/server/lib/github/DOCUMENTATION.md +5 -0
- package/server/lib/github/routes.js +2 -2
- package/server/lib/inherited-env.js +16 -9
- package/server/lib/inherited-env.test.js +8 -9
- package/server/lib/message-queue/DOCUMENTATION.md +11 -1
- package/server/lib/message-queue/runtime.js +13 -0
- package/server/lib/message-queue/runtime.test.js +31 -1
- package/server/lib/network-defaults.js +23 -0
- package/server/lib/network-defaults.test.js +52 -0
- package/server/lib/notifications/DOCUMENTATION.md +1 -1
- package/server/lib/notifications/emitter-runtime.js +4 -3
- package/server/lib/opencode/DOCUMENTATION.md +35 -5
- package/server/lib/opencode/bootstrap-runtime.js +2 -0
- package/server/lib/opencode/env-runtime.js +5 -1
- package/server/lib/opencode/env-runtime.test.js +72 -0
- package/server/lib/opencode/feature-routes-runtime.js +17 -1
- package/server/lib/opencode/lifecycle.js +4 -3
- package/server/lib/opencode/lifecycle.test.js +29 -0
- package/server/lib/opencode/managed-plugin-config.js +1 -1
- package/server/lib/opencode/openchamber-routes.js +64 -4
- package/server/lib/opencode/openchamber-routes.test.js +128 -1
- package/server/lib/opencode/proxy.js +33 -0
- package/server/lib/opencode/routes.js +5 -3
- package/server/lib/opencode/settings-files.js +214 -0
- package/server/lib/opencode/settings-helpers.js +88 -41
- package/server/lib/opencode/settings-helpers.test.js +176 -1
- package/server/lib/opencode/settings-registry.json +750 -0
- package/server/lib/opencode/settings-runtime.js +175 -27
- package/server/lib/opencode/settings-runtime.test.js +321 -7
- package/server/lib/project-context/DOCUMENTATION.md +38 -4
- package/server/lib/project-context/routes.http.test.js +18 -0
- package/server/lib/project-context/routes.js +15 -0
- package/server/lib/project-context/runtime.js +256 -25
- package/server/lib/project-context/runtime.test.js +160 -3
- package/server/lib/projects/DOCUMENTATION.md +77 -0
- package/server/lib/projects/project-config.js +179 -17
- package/server/lib/projects/project-config.test.js +97 -0
- package/server/lib/projects/project-id.js +43 -0
- package/server/lib/projects/project-setup.js +409 -0
- package/server/lib/projects/project-setup.test.js +557 -0
- package/server/lib/projects/routes.js +59 -0
- package/server/lib/quota/DOCUMENTATION.md +31 -1
- package/server/lib/quota/providers/cline-pass.js +125 -0
- package/server/lib/quota/providers/cline-pass.test.js +103 -0
- package/server/lib/quota/providers/index.js +7 -0
- package/server/lib/quota/providers/neuralwatt.js +3 -6
- package/server/lib/quota/providers/neuralwatt.test.js +7 -7
- package/server/lib/quota/providers/ollama-cloud.js +31 -0
- package/server/lib/quota/providers/ollama-cloud.test.js +33 -1
- package/server/lib/quota/providers/opencode-go.js +1 -1
- package/server/lib/quota/providers/opencode-go.test.js +26 -1
- package/server/lib/quota/providers/openrouter.js +119 -24
- package/server/lib/quota/providers/openrouter.test.js +330 -0
- package/server/lib/relay/DOCUMENTATION.md +75 -0
- package/server/lib/relay/cross-compat.test.js +26 -0
- package/server/lib/relay/downstream-scheduler.js +154 -0
- package/server/lib/relay/downstream-scheduler.test.js +162 -0
- package/server/lib/relay/e2ee.js +10 -8
- package/server/lib/relay/flow-control.test.js +177 -0
- package/server/lib/relay/host-client.js +44 -8
- package/server/lib/relay/host-client.test.js +14 -0
- package/server/lib/relay/tunnel-codec.js +9 -0
- package/server/lib/relay/tunnel-host.js +66 -17
- package/server/lib/scheduled-tasks/DOCUMENTATION.md +9 -1
- package/server/lib/scheduled-tasks/issue-2710-double-execution.test.js +28 -0
- package/server/lib/scheduled-tasks/runtime.js +12 -2
- package/server/lib/scheduled-tasks/runtime.test.js +53 -0
- package/server/lib/session-assist/DOCUMENTATION.md +131 -66
- package/server/lib/session-assist/context.js +127 -0
- package/server/lib/session-assist/context.test.js +132 -0
- package/server/lib/session-assist/prompt.js +54 -0
- package/server/lib/session-assist/runtime.js +117 -267
- package/server/lib/session-assist/runtime.test.js +203 -0
- package/server/lib/session-goal/runtime.js +4 -9
- package/server/lib/small-model/DOCUMENTATION.md +3 -2
- package/server/lib/small-model/call.js +9 -4
- package/server/lib/small-model/call.test.js +29 -0
- package/server/lib/small-model/index.js +5 -10
- package/server/lib/small-model/runtime-providers.js +15 -7
- package/server/lib/small-model/runtime-providers.test.js +7 -1
- package/server/lib/terminal/DOCUMENTATION.md +4 -4
- package/server/lib/terminal/runtime.js +11 -6
- package/server/lib/terminal/runtime.test.js +20 -13
- package/server/lib/walkthrough/DOCUMENTATION.md +49 -20
- package/server/lib/walkthrough/model-settings.js +8 -12
- package/server/lib/walkthrough/pr-source.test.js +21 -0
- package/server/lib/walkthrough/prompt.js +3 -1
- package/server/lib/walkthrough/pull-request.js +7 -4
- package/server/lib/walkthrough/pull-request.test.js +16 -0
- package/server/lib/walkthrough/routes.js +19 -0
- package/server/lib/walkthrough/routes.test.js +26 -0
- package/server/lib/walkthrough/sources.js +36 -4
- package/server/opencode-worktree-readiness.test.js +130 -0
- package/dist/assets/DiffView-hDBKesA_.js +0 -4
- package/dist/assets/FilesView-CdWzeOp9.js +0 -17
- package/dist/assets/MarkdownImageGallery-Czv_mhvS.js +0 -1
- package/dist/assets/MarkdownRendererImpl-BrMcswiy.js +0 -12
- package/dist/assets/OnboardingScreen-ChxQoAkx.js +0 -1
- package/dist/assets/PierreDiffViewer-BchBtZlp.js +0 -157
- package/dist/assets/PlanView-DyDt_nZ_.js +0 -5
- package/dist/assets/SettingsView-BKf5xBsP.js +0 -28
- package/dist/assets/WalkthroughView-DOeCsMju.js +0 -4
- package/dist/assets/contextPanelEmbeddedChat-C0ZLAepL.js +0 -1
- package/dist/assets/de-B5JaA3SA.js +0 -10
- package/dist/assets/es-EPIH88ii.js +0 -16
- package/dist/assets/fr-CAMfxUdZ.js +0 -14
- package/dist/assets/index-DF5kMP-i.js +0 -39
- package/dist/assets/index-Dl3xbAB_.css +0 -1
- package/dist/assets/ja-CifOE1VN.js +0 -16
- package/dist/assets/ko-BBDyAyYQ.js +0 -16
- package/dist/assets/main-MhSaOZoB.js +0 -930
- package/dist/assets/markdownCore-BrBwOOMn.js +0 -3
- package/dist/assets/patchFileDiff-o8ms1me8.js +0 -10
- package/dist/assets/pl-DgEHYfFM.js +0 -16
- package/dist/assets/pt-BR-DuWHojUw.js +0 -16
- package/dist/assets/renderElectronMiniChatApp-CDgBwwCS.js +0 -2
- package/dist/assets/renderMobileApp-DJIKIK1r.js +0 -25
- package/dist/assets/runtimeEndpointReset-CYJ9O_xX.js +0 -17
- package/dist/assets/scheduledTasksApi-D8yamh1b.js +0 -1
- package/dist/assets/tr-D5m2yIWL.js +0 -7
- package/dist/assets/uk-D_q60oog.js +0 -16
- package/dist/assets/useInlineCommentController-Brz_cLcQ.js +0 -2
- package/dist/assets/useRootScrollLock-lXEnWlaU.js +0 -1
- package/dist/assets/vendor-ghostty-web-CrxinGGH.js +0 -13
- package/dist/assets/vendor-legendapp-list-gg7YLRW7.js +0 -1
- package/dist/assets/vendor-zod-CK21ihK-.js +0 -39
- package/dist/assets/zh-CN-D8QvsVeg.js +0 -16
- package/dist/assets/zh-TW-B9t7rQQO.js +0 -16
- package/server/lib/mcp-reconnect/DOCUMENTATION.md +0 -55
- package/server/lib/mcp-reconnect/runtime.js +0 -122
- package/server/lib/mcp-reconnect/runtime.test.js +0 -140
|
@@ -52,26 +52,55 @@ written against staged code never silently re-anchors onto an unstaged edit.
|
|
|
52
52
|
| Kind | Sections | Notes |
|
|
53
53
|
|---|---|---|
|
|
54
54
|
| `working-tree` (`all` \| `staged` \| `working`) | `staged`, `working` | Untracked files are fetched individually because `git diff` omits them |
|
|
55
|
-
| `branch` | `branch` | `getRangeDiff`
|
|
56
|
-
| `
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
the current branch's
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
the
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
55
|
+
| `branch` | `branch` | `getRangeDiff` with `includeWorkingTree: true` compares the selected merge base with current files, including committed and local work in one net diff |
|
|
56
|
+
| `commit` | `commit` | `getCommitDiff` compares the full selected commit hash with its first parent; root commits compare with an empty tree |
|
|
57
|
+
| `pr` | `pr:<number>` | GitHub's committed pull-request diff, without local working-tree changes |
|
|
58
|
+
|
|
59
|
+
Changes and walkthrough resolve the current branch's base through
|
|
60
|
+
`packages/ui/src/hooks/useBranchComparisonBase.ts`. An explicit choice in Changes
|
|
61
|
+
outranks reflog detection. Both toolbars use
|
|
62
|
+
`packages/ui/src/components/views/git/BranchComparisonSelector.tsx` to select or
|
|
63
|
+
change the base directly. Walkthrough allows selecting Branch before a base is
|
|
64
|
+
known and waits for a valid choice before loading or generating. Opening
|
|
65
|
+
walkthrough from Changes carries the selected base and head; later selections
|
|
66
|
+
in either toolbar update both comparisons.
|
|
67
|
+
|
|
68
|
+
Commit mode uses the shared `CommitComparisonSelector` in both toolbars. It
|
|
69
|
+
lists the latest 50 commits reachable from the checked-out branch, with subject,
|
|
70
|
+
author, date, and short hash. Opening the picker refreshes that list; selecting a
|
|
71
|
+
commit changes the comparison, not the checkout. Changes hands the selected full
|
|
72
|
+
hash to walkthrough. The server accepts full object IDs for commit sources and
|
|
73
|
+
keys their cache entries and generation jobs as `commit:<hash>`, so reviews of
|
|
74
|
+
different commits cannot overwrite each other. Existing source keys keep their
|
|
75
|
+
format. Commit reads have no working-tree freshness dependency, and selecting a
|
|
76
|
+
commit never starts model generation.
|
|
77
|
+
|
|
78
|
+
The Git module owns exact-ref and working-tree comparison semantics. Local and
|
|
79
|
+
remote bases remain distinct, and a checkout during a branch review requires
|
|
80
|
+
the source to be resolved for the new branch rather than including another
|
|
81
|
+
branch's local files.
|
|
82
|
+
|
|
83
|
+
Successful status refreshes invalidate the visible branch comparison even when
|
|
84
|
+
file names and insertion/deletion counts stay the same. Walkthrough refreshes
|
|
85
|
+
its current hunk index while visible; regeneration remains user-initiated. The
|
|
86
|
+
content-addressed cache continues to reuse an old review only when its hunks
|
|
87
|
+
match, and otherwise reports stale anchors and uncovered current hunks.
|
|
88
|
+
|
|
89
|
+
PR mode uses the same searchable, paginated selector as Changes. Its list loads
|
|
90
|
+
only while PR mode is visible. Selection ownership and handoff rules are in
|
|
91
|
+
`packages/ui/src/stores/DOCUMENTATION.md`.
|
|
92
|
+
|
|
93
|
+
PR sources may include `sourceRepo: { owner, repo }`. This qualifies both the
|
|
94
|
+
GitHub request and the cache/job key as `pr:<owner>/<repo>:<number>`. Existing
|
|
95
|
+
number-only sources retain `pr:<number>` and resolve the directory's repository.
|
|
96
|
+
The PR panel forwards its resolved repository when opening walkthrough.
|
|
97
|
+
|
|
98
|
+
`GET /api/walkthrough/pr-diff` accepts `directory` and a JSON `source` restricted
|
|
99
|
+
to PRs. It returns GitHub's complete published diff as text, with no model
|
|
100
|
+
readiness checks or generation. Successful empty patches return 200; auth,
|
|
101
|
+
GitHub and malformed-response failures remain errors. Walkthrough generation
|
|
102
|
+
keeps its existing empty-diff refusal. UI comparison behavior is documented in
|
|
103
|
+
`packages/ui/src/components/views/DOCUMENTATION.md`.
|
|
75
104
|
|
|
76
105
|
## No truncation
|
|
77
106
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import os from 'os';
|
|
3
3
|
import path from 'path';
|
|
4
|
+
import { readMergedSettingsSync } from '../opencode/settings-files.js';
|
|
4
5
|
|
|
5
6
|
// The walkthrough may run on a different model than the rest of the small-model
|
|
6
7
|
// callers. Those callers want cheap and fast; this one needs structured output
|
|
@@ -23,16 +24,11 @@ const SETTINGS_FILE = path.join(
|
|
|
23
24
|
* not use the small model" with nothing to use instead.
|
|
24
25
|
*/
|
|
25
26
|
export function readWalkthroughModelOverride() {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
} catch {
|
|
34
|
-
// No settings file, unreadable, or malformed all mean the same thing: no
|
|
35
|
-
// override, use the small model.
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
27
|
+
// No settings file, unreadable, or malformed all mean the same thing: no
|
|
28
|
+
// override, use the small model.
|
|
29
|
+
const settings = readMergedSettingsSync({ fs, path, settingsFilePath: SETTINGS_FILE });
|
|
30
|
+
const override = typeof settings.walkthroughModelOverride === 'string'
|
|
31
|
+
? settings.walkthroughModelOverride.trim()
|
|
32
|
+
: '';
|
|
33
|
+
return override || null;
|
|
38
34
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { parseSource, sourceKey, loadSourceSections } from './sources.js';
|
|
3
|
+
|
|
4
|
+
describe('repository-qualified PR sources', () => {
|
|
5
|
+
it('keeps old cache keys and separates equal PR numbers in different repositories', () => {
|
|
6
|
+
expect(sourceKey(parseSource({ kind: 'pr', number: 42 }))).toBe('pr:42');
|
|
7
|
+
const upstream = parseSource({ kind: 'pr', number: 42, sourceRepo: { owner: 'upstream', repo: 'project' } });
|
|
8
|
+
const fork = parseSource({ kind: 'pr', number: 42, sourceRepo: { owner: 'fork', repo: 'project' } });
|
|
9
|
+
expect(sourceKey(upstream)).not.toBe(sourceKey(fork));
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('hands the selected repository to the walkthrough diff loader', async () => {
|
|
13
|
+
const source = parseSource({ kind: 'pr', number: 42, sourceRepo: { owner: 'upstream', repo: 'project' } });
|
|
14
|
+
let received;
|
|
15
|
+
await loadSourceSections('/repo', source, { getPullRequestDiff: async (...args) => {
|
|
16
|
+
received = args;
|
|
17
|
+
return { patch: 'published patch', meta: {} };
|
|
18
|
+
} });
|
|
19
|
+
expect(received).toEqual(['/repo', 42, source.sourceRepo]);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -86,7 +86,9 @@ export function buildPrompt({ digest, fileCount, hunkCount, source, previousWalk
|
|
|
86
86
|
? `Uncommitted local changes (${source.scope === 'all' ? 'staged and unstaged' : source.scope}).`
|
|
87
87
|
: source.kind === 'branch'
|
|
88
88
|
? `All work on branch "${source.headRef}" that is not in "${source.baseRef}". Changes merged in from ${source.baseRef} are already excluded.`
|
|
89
|
-
:
|
|
89
|
+
: source.kind === 'commit'
|
|
90
|
+
? `Only the changes introduced by commit ${source.hash}, relative to its first parent (or the empty tree for a root commit).`
|
|
91
|
+
: `Pull request #${source.number}.`;
|
|
90
92
|
|
|
91
93
|
const prompt = `Reviewing: ${sourceLine}
|
|
92
94
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getOctokitOrNull } from '../github/octokit.js';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
2
3
|
import { resolveGitHubRepoFromDirectory } from '../github/repo/index.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -8,7 +9,7 @@ import { resolveGitHubRepoFromDirectory } from '../github/repo/index.js';
|
|
|
8
9
|
* three-dot semantics used for local branch reviews: work merged in from the
|
|
9
10
|
* base branch is not part of it.
|
|
10
11
|
*/
|
|
11
|
-
export async function getPullRequestDiff(directory, number) {
|
|
12
|
+
export async function getPullRequestDiff(directory, number, sourceRepo, { allowEmpty = false } = {}) {
|
|
12
13
|
const octokit = getOctokitOrNull();
|
|
13
14
|
if (!octokit) {
|
|
14
15
|
throw Object.assign(new Error('Connect a GitHub account to review pull requests'), {
|
|
@@ -19,7 +20,8 @@ export async function getPullRequestDiff(directory, number) {
|
|
|
19
20
|
|
|
20
21
|
// The resolver returns `{ repo, remoteUrl }`, not the repo itself. Reading
|
|
21
22
|
// `.owner` off the wrapper made this check fail for every repository.
|
|
22
|
-
const { repo }
|
|
23
|
+
const resolved = sourceRepo ? { repo: sourceRepo } : await resolveGitHubRepoFromDirectory(directory);
|
|
24
|
+
const { repo } = resolved;
|
|
23
25
|
if (!repo?.owner || !repo?.repo) {
|
|
24
26
|
throw Object.assign(new Error('This directory has no GitHub remote'), {
|
|
25
27
|
statusCode: 400,
|
|
@@ -34,8 +36,9 @@ export async function getPullRequestDiff(directory, number) {
|
|
|
34
36
|
headers: { accept: 'application/vnd.github.v3.diff' },
|
|
35
37
|
});
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
assert.match(response.data, /^(?:diff --git |\s*$)/, 'GitHub returned an invalid pull request diff');
|
|
40
|
+
const patch = response.data;
|
|
41
|
+
if (!allowEmpty && !patch.trim()) {
|
|
39
42
|
throw Object.assign(new Error(`Pull request #${number} has no diff`), {
|
|
40
43
|
statusCode: 404,
|
|
41
44
|
code: 'empty-diff',
|
|
@@ -56,6 +56,22 @@ describe('getPullRequestDiff', () => {
|
|
|
56
56
|
expect(request).not.toHaveBeenCalled();
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
+
it('uses the selected upstream repository rather than the local fork', async () => {
|
|
60
|
+
const result = await getPullRequestDiff('/repo', 42, { owner: 'upstream', repo: 'project' });
|
|
61
|
+
expect(result.meta).toEqual({ owner: 'upstream', repo: 'project', number: 42 });
|
|
62
|
+
expect(resolveGitHubRepoFromDirectory).not.toHaveBeenCalled();
|
|
63
|
+
expect(request).toHaveBeenCalledWith('GET /repos/{owner}/{repo}/pulls/{pull_number}', {
|
|
64
|
+
owner: 'upstream', repo: 'project', pull_number: 42, headers: { accept: 'application/vnd.github.v3.diff' },
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('allows an empty comparison but rejects malformed GitHub bodies', async () => {
|
|
69
|
+
request.mockResolvedValue({ data: '' });
|
|
70
|
+
expect((await getPullRequestDiff('/repo', 42, undefined, { allowEmpty: true })).patch).toBe('');
|
|
71
|
+
request.mockResolvedValue({ data: { message: 'Not a diff' } });
|
|
72
|
+
await expect(getPullRequestDiff('/repo', 42)).rejects.toThrow();
|
|
73
|
+
});
|
|
74
|
+
|
|
59
75
|
it('asks the user to connect GitHub before anything else', async () => {
|
|
60
76
|
getOctokitOrNull.mockReturnValue(null);
|
|
61
77
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { parseSource } from './sources.js';
|
|
2
|
+
|
|
1
3
|
// `req.destroyed` is true for every healthy request once the body parser has
|
|
2
4
|
// consumed the stream, so using it as a disconnect check silently swallows every
|
|
3
5
|
// response. The response socket is the one that actually reflects whether the
|
|
@@ -51,6 +53,23 @@ export function registerWalkthroughRoutes(app, { getWalkthroughService }) {
|
|
|
51
53
|
}
|
|
52
54
|
});
|
|
53
55
|
|
|
56
|
+
// The comparison view needs the complete published patch, without model
|
|
57
|
+
// readiness checks, generated-file filtering, or local working-tree reads.
|
|
58
|
+
app.get('/api/walkthrough/pr-diff', async (req, res) => {
|
|
59
|
+
try {
|
|
60
|
+
const query = new URL(req.originalUrl, 'http://localhost').searchParams;
|
|
61
|
+
const directory = query.get('directory')?.trim() ?? '';
|
|
62
|
+
if (!directory) return res.status(400).json({ error: 'directory parameter is required' });
|
|
63
|
+
const source = parseSource(readSource(query.get('source')));
|
|
64
|
+
if (source.kind !== 'pr') return res.status(400).json({ error: 'A pull request source is required' });
|
|
65
|
+
const { getPullRequestDiff } = await getWalkthroughService();
|
|
66
|
+
const { patch } = await getPullRequestDiff(directory, source.number, source.sourceRepo, { allowEmpty: true });
|
|
67
|
+
res.type('text/plain').send(patch);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
respondWithError(res, error, 'Failed to load pull request diff');
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
54
73
|
// Deliberately not aborted when the client disconnects: generation runs for
|
|
55
74
|
// minutes and a refresh must not throw the work away. Leaving detaches the
|
|
56
75
|
// client; the job finishes and caches its result. Stopping is an explicit
|
|
@@ -18,6 +18,11 @@ describe('walkthrough routes', () => {
|
|
|
18
18
|
let generateCalls = 0;
|
|
19
19
|
|
|
20
20
|
const service = {
|
|
21
|
+
async getPullRequestDiff(directory, number, sourceRepo, options) {
|
|
22
|
+
lastArgs = { directory, number, sourceRepo, options };
|
|
23
|
+
if (number === 99) throw Object.assign(new Error('GitHub unavailable'), { statusCode: 503 });
|
|
24
|
+
return { patch: number === 1 ? '' : 'diff --git a/a.ts b/a.ts\n' };
|
|
25
|
+
},
|
|
21
26
|
async getWalkthrough(args) {
|
|
22
27
|
lastArgs = args;
|
|
23
28
|
return { walkthrough: null, hunks: [], hunkCount: 0, generating: Boolean(job) };
|
|
@@ -84,6 +89,27 @@ describe('walkthrough routes', () => {
|
|
|
84
89
|
expect(body.walkthrough).toEqual({ title: 'DONE' });
|
|
85
90
|
});
|
|
86
91
|
|
|
92
|
+
it('serves the published PR snapshot with its repository, without generating', async () => {
|
|
93
|
+
const source = { kind: 'pr', number: 42, sourceRepo: { owner: 'upstream', repo: 'project' } };
|
|
94
|
+
const before = generateCalls;
|
|
95
|
+
const response = await fetch(`${base}/api/walkthrough/pr-diff?directory=/repo&source=${encodeURIComponent(JSON.stringify(source))}`);
|
|
96
|
+
expect(response.headers.get('content-type')).toContain('text/plain');
|
|
97
|
+
expect(await response.text()).toBe('diff --git a/a.ts b/a.ts\n');
|
|
98
|
+
expect(lastArgs).toEqual({ directory: '/repo', number: 42, sourceRepo: source.sourceRepo, options: { allowEmpty: true } });
|
|
99
|
+
expect(generateCalls).toBe(before);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('distinguishes empty PRs, upstream failure, and invalid sources', async () => {
|
|
103
|
+
const request = (source) => fetch(`${base}/api/walkthrough/pr-diff?directory=/repo&source=${encodeURIComponent(JSON.stringify(source))}`);
|
|
104
|
+
const empty = await request({ kind: 'pr', number: 1 });
|
|
105
|
+
expect(empty.status).toBe(200);
|
|
106
|
+
expect(await empty.text()).toBe('');
|
|
107
|
+
expect((await request({ kind: 'pr', number: 99 })).status).toBe(503);
|
|
108
|
+
for (const source of [{ kind: 'pr', number: -1 }, { kind: 'branch', baseRef: 'main', headRef: 'feature' }, { kind: 'pr', number: 1, sourceRepo: { owner: '../bad', repo: 'repo' } }]) {
|
|
109
|
+
expect((await request(source)).status).toBe(400);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
87
113
|
it('delivers the result to a client that reconnected after a refresh', async () => {
|
|
88
114
|
const controller = new AbortController();
|
|
89
115
|
const seen = generateCalls;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { getDiff, getRangeDiff, getUntrackedDiffs, listUntrackedPaths } from '../git/service.js';
|
|
1
|
+
import { getDiff, getRangeDiff, getCommitDiff, getUntrackedDiffs, listUntrackedPaths } from '../git/service.js';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
2
3
|
|
|
3
4
|
// A walkthrough source resolves to one or more diff *sections*. A section is a
|
|
4
5
|
// patch plus the scope its hunk ids live in; keeping staged and working-tree
|
|
@@ -45,9 +46,31 @@ export function parseSource(raw) {
|
|
|
45
46
|
if (!Number.isInteger(number) || number <= 0) {
|
|
46
47
|
throw new WalkthroughSourceError('pr sources require a positive number');
|
|
47
48
|
}
|
|
49
|
+
if (raw.sourceRepo !== undefined) {
|
|
50
|
+
const { owner, repo } = raw.sourceRepo ?? {};
|
|
51
|
+
try {
|
|
52
|
+
assert.match(owner, /^[a-zA-Z0-9-]+$/);
|
|
53
|
+
assert.match(repo, /^[a-zA-Z0-9_.-]+$/);
|
|
54
|
+
} catch {
|
|
55
|
+
throw new WalkthroughSourceError('pr sources require a valid repository');
|
|
56
|
+
}
|
|
57
|
+
return { kind: 'pr', number, sourceRepo: { owner, repo } };
|
|
58
|
+
}
|
|
48
59
|
return { kind: 'pr', number };
|
|
49
60
|
}
|
|
50
61
|
|
|
62
|
+
if (raw.kind === 'commit') {
|
|
63
|
+
// Sources are content-addressed: accept a full object id, never a moving ref.
|
|
64
|
+
if (!/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i.test(raw.hash)) {
|
|
65
|
+
throw new WalkthroughSourceError('commit sources require a full commit hash');
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
return { kind: 'commit', hash: raw.hash.toLowerCase() };
|
|
69
|
+
} catch {
|
|
70
|
+
throw new WalkthroughSourceError('commit sources require a full commit hash');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
51
74
|
throw new WalkthroughSourceError(`Unknown source kind "${String(raw.kind)}"`);
|
|
52
75
|
}
|
|
53
76
|
|
|
@@ -58,7 +81,8 @@ export function parseSource(raw) {
|
|
|
58
81
|
export function sourceKey(source) {
|
|
59
82
|
if (source.kind === 'working-tree') return `working-tree:${source.scope}`;
|
|
60
83
|
if (source.kind === 'branch') return `branch:${source.baseRef}...${source.headRef}`;
|
|
61
|
-
return `
|
|
84
|
+
if (source.kind === 'commit') return `commit:${source.hash}`;
|
|
85
|
+
return source.sourceRepo ? `pr:${source.sourceRepo.owner}/${source.sourceRepo.repo}:${source.number}` : `pr:${source.number}`;
|
|
62
86
|
}
|
|
63
87
|
|
|
64
88
|
// `git diff` never reports untracked files, so a brand-new file would be
|
|
@@ -97,18 +121,26 @@ export async function loadSourceSections(directory, source, { getPullRequestDiff
|
|
|
97
121
|
}
|
|
98
122
|
|
|
99
123
|
if (source.kind === 'branch') {
|
|
100
|
-
const patch = await getRangeDiff(directory, { base: source.baseRef, head: source.headRef });
|
|
124
|
+
const patch = await getRangeDiff(directory, { base: source.baseRef, head: source.headRef, includeWorkingTree: true });
|
|
101
125
|
return {
|
|
102
126
|
sections: patch && patch.trim() ? [{ scope: 'branch', patch }] : [],
|
|
103
127
|
meta: { baseRef: source.baseRef, headRef: source.headRef },
|
|
104
128
|
};
|
|
105
129
|
}
|
|
106
130
|
|
|
131
|
+
if (source.kind === 'commit') {
|
|
132
|
+
const patch = await getCommitDiff(directory, { hash: source.hash });
|
|
133
|
+
return {
|
|
134
|
+
sections: patch.trim() ? [{ scope: 'commit', patch }] : [],
|
|
135
|
+
meta: { hash: source.hash },
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
107
139
|
if (typeof getPullRequestDiff !== 'function') {
|
|
108
140
|
throw new WalkthroughSourceError('Pull request diffs are unavailable', 500);
|
|
109
141
|
}
|
|
110
142
|
|
|
111
|
-
const { patch, meta } = await getPullRequestDiff(directory, source.number);
|
|
143
|
+
const { patch, meta } = await getPullRequestDiff(directory, source.number, source.sourceRepo);
|
|
112
144
|
return {
|
|
113
145
|
sections: patch && patch.trim() ? [{ scope: `pr:${source.number}`, patch }] : [],
|
|
114
146
|
meta: meta || {},
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import express from 'express';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { registerOpenCodeProxy } from './lib/opencode/proxy.js';
|
|
5
|
+
|
|
6
|
+
const servers = [];
|
|
7
|
+
const listen = async (app) => {
|
|
8
|
+
const server = await new Promise((resolve) => {
|
|
9
|
+
const listening = app.listen(0, '127.0.0.1', () => resolve(listening));
|
|
10
|
+
});
|
|
11
|
+
servers.push(server);
|
|
12
|
+
return `http://127.0.0.1:${server.address().port}`;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
afterEach(async () => {
|
|
16
|
+
await Promise.all(servers.splice(0).map((server) => new Promise((resolve) => {
|
|
17
|
+
server.closeAllConnections();
|
|
18
|
+
server.close(resolve);
|
|
19
|
+
})));
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const worktree = '/repo/worktree with spaces';
|
|
23
|
+
const projectConfig = { mcp: { projectOnly: { type: 'local', command: ['test-mcp'], enabled: false } } };
|
|
24
|
+
|
|
25
|
+
async function createFixture({ timeoutMs = 1000 } = {}) {
|
|
26
|
+
let bootstrap = { status: 'pending', phase: 'directory-created', error: null };
|
|
27
|
+
let instance;
|
|
28
|
+
const forwarded = [];
|
|
29
|
+
const probed = [];
|
|
30
|
+
const upstream = express();
|
|
31
|
+
upstream.use(express.json());
|
|
32
|
+
upstream.use((req, res) => {
|
|
33
|
+
forwarded.push(req.path);
|
|
34
|
+
// Model OpenCode's instance cache: even a GET can permanently capture the
|
|
35
|
+
// empty directory, and a later session inherits that project's identity.
|
|
36
|
+
instance ??= bootstrap.phase === 'directory-created'
|
|
37
|
+
? { projectID: 'global', config: {} }
|
|
38
|
+
: { projectID: 'project-3464', config: projectConfig };
|
|
39
|
+
if (req.path === '/config') return res.json(instance.config);
|
|
40
|
+
if (req.path === '/session' && req.method === 'POST') {
|
|
41
|
+
return res.json({ projectID: instance.projectID, directory: req.query.directory, title: req.body.title });
|
|
42
|
+
}
|
|
43
|
+
res.json([]);
|
|
44
|
+
});
|
|
45
|
+
const upstreamUrl = await listen(upstream);
|
|
46
|
+
const app = express();
|
|
47
|
+
app.use(express.json());
|
|
48
|
+
registerOpenCodeProxy(app, {
|
|
49
|
+
fs: {}, os: {}, path,
|
|
50
|
+
OPEN_CODE_READY_GRACE_MS: 0,
|
|
51
|
+
WORKTREE_READY_TIMEOUT_MS: timeoutMs,
|
|
52
|
+
getRuntime: () => ({ openCodePort: new URL(upstreamUrl).port, isOpenCodeReady: true }),
|
|
53
|
+
getOpenCodeAuthHeaders: () => ({}),
|
|
54
|
+
buildOpenCodeUrl: (requestPath) => `${upstreamUrl}${requestPath}`,
|
|
55
|
+
ensureOpenCodeApiPrefix: () => {},
|
|
56
|
+
readWorktreeBootstrapStatus: async (directory) => {
|
|
57
|
+
probed.push(directory);
|
|
58
|
+
return directory === worktree ? bootstrap : { status: 'ready', phase: 'setup-ready' };
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
return {
|
|
62
|
+
url: await listen(app), forwarded, probed,
|
|
63
|
+
setBootstrap: (next) => { bootstrap = next; },
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
describe('issue #3464 worktree OpenCode initialization', () => {
|
|
68
|
+
it('holds config and session creation until Git is ready, while setup remains pending', async () => {
|
|
69
|
+
const fixture = await createFixture();
|
|
70
|
+
const config = fetch(`${fixture.url}/api/config?directory=${encodeURIComponent(worktree)}`);
|
|
71
|
+
const session = fetch(`${fixture.url}/api/session?directory=${encodeURIComponent(worktree)}`, {
|
|
72
|
+
method: 'POST', headers: { 'content-type': 'application/json' },
|
|
73
|
+
body: JSON.stringify({ title: 'First worktree session' }),
|
|
74
|
+
});
|
|
75
|
+
await expect.poll(() => fixture.probed.length).toBeGreaterThanOrEqual(2);
|
|
76
|
+
expect(fixture.forwarded).toEqual([]);
|
|
77
|
+
fixture.setBootstrap({ status: 'pending', phase: 'git-ready', error: null });
|
|
78
|
+
expect(await (await config).json()).toEqual(projectConfig);
|
|
79
|
+
expect(await (await session).json()).toEqual({
|
|
80
|
+
projectID: 'project-3464', directory: worktree, title: 'First worktree session',
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('gates encoded directory headers and the separately forwarded session list', async () => {
|
|
85
|
+
const fixture = await createFixture();
|
|
86
|
+
const response = fetch(`${fixture.url}/api/session`, { headers: {
|
|
87
|
+
'x-opencode-directory': encodeURIComponent(worktree), 'x-opencode-directory-encoding': 'uri',
|
|
88
|
+
} });
|
|
89
|
+
await expect.poll(() => fixture.probed.length).toBeGreaterThan(0);
|
|
90
|
+
expect(fixture.probed[0]).toBe(worktree);
|
|
91
|
+
expect(fixture.forwarded).toEqual([]);
|
|
92
|
+
fixture.setBootstrap({ status: 'ready', phase: 'setup-ready', error: null });
|
|
93
|
+
expect((await response).status).toBe(200);
|
|
94
|
+
expect(fixture.forwarded).toEqual(['/session']);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('does not block unrelated directories while a worktree is pending', async () => {
|
|
98
|
+
const fixture = await createFixture();
|
|
99
|
+
expect((await fetch(`${fixture.url}/api/session?directory=/other-project`)).status).toBe(200);
|
|
100
|
+
expect(fixture.probed).toEqual(['/other-project']);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('does not forward an abandoned request when checkout later finishes', async () => {
|
|
104
|
+
const fixture = await createFixture();
|
|
105
|
+
const controller = new AbortController();
|
|
106
|
+
const response = fetch(`${fixture.url}/api/config?directory=${encodeURIComponent(worktree)}`, {
|
|
107
|
+
signal: controller.signal,
|
|
108
|
+
});
|
|
109
|
+
const rejected = expect(response).rejects.toThrow();
|
|
110
|
+
await expect.poll(() => fixture.probed.length).toBeGreaterThan(0);
|
|
111
|
+
controller.abort();
|
|
112
|
+
await rejected;
|
|
113
|
+
// Allow the server to observe the closed client before releasing checkout.
|
|
114
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
115
|
+
fixture.setBootstrap({ status: 'ready', phase: 'setup-ready', error: null });
|
|
116
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
117
|
+
expect(fixture.forwarded).toEqual([]);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('fails closed on bootstrap failure or timeout without initializing OpenCode', async () => {
|
|
121
|
+
const fixture = await createFixture({ timeoutMs: 0 });
|
|
122
|
+
const timedOut = await fetch(`${fixture.url}/api/config?directory=${encodeURIComponent(worktree)}`);
|
|
123
|
+
expect(timedOut.status).toBe(503);
|
|
124
|
+
fixture.setBootstrap({ status: 'failed', phase: 'directory-created', error: 'checkout failed' });
|
|
125
|
+
const failed = await fetch(`${fixture.url}/api/session?directory=${encodeURIComponent(worktree)}`, { method: 'POST' });
|
|
126
|
+
expect(failed.status).toBe(503);
|
|
127
|
+
expect(await failed.json()).toEqual({ error: 'checkout failed' });
|
|
128
|
+
expect(fixture.forwarded).toEqual([]);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import{R as o,j as n,f as he}from"./vendor-react-BUCujlKS.js";import{a0 as wn,a as ct,u as Y,G as Pe}from"./index-DF5kMP-i.js";import{f as ge,W as Tt,ac as yn,ad as vn,b7 as jn,ag as Sn,ae as Nn,ah as Fn,ai as Z,y as Ft,k as kn,hQ as Cn,hR as Rn,hS as Dn,cD as An,t as Ke,hT as En,ap as Ve,B as pe,I as A,h as ue,a7 as Bn,a8 as Vn,a9 as Ln,hD as Mt,hU as Tn,fF as Mn,fK as Ge,a1 as _n,c0 as at,ar as _t,ao as In,b3 as On,b5 as zn,fL as Pn,b6 as Gn}from"./useAppFontEffects-D7sNpuL2.js";import{b as Kn}from"./runtimeEndpointReset-CYJ9O_xX.js";import{c as $n,p as Wn}from"./vendor-zustand-BAGL70zf.js";import{P as Hn}from"./PierreDiffViewer-BchBtZlp.js";import{u as Un,v as qn,g as Xn,W as Jn}from"./main-MhSaOZoB.js";import{f as Qn}from"./patchFileDiff-o8ms1me8.js";import"./vendor-scheduler-7OC5HNn7.js";import"./vendor-vite-runtime-DD1OZmLC.js";import"./vendor-zod-CK21ihK-.js";import"./vendor-opencode-sdk-BJ73u-bH.js";import"./vendor-simplewebauthn-browser-VDA66rT_.js";import"./vendor-base-ui-BeMD8X_w.js";import"./vendor-floating-ui-utils-D6FqdtJt.js";import"./vendor-floating-ui-react-dom-DxYCWdHj.js";import"./vendor-floating-ui-dom-BTvzWks8.js";import"./vendor-floating-ui-core-PuGzNPnN.js";import"./vendor-use-sync-external-store-D6wYuSt-.js";import"./contextPanelEmbeddedChat-C0ZLAepL.js";import"./vendor-class-variance-authority-5VPnzWs2.js";import"./vendor-clsx-B-dksMZM.js";import"./vendor-dnd-kit-core-BZknOX2w.js";import"./vendor-dnd-kit-utilities-B_MdXhJC.js";import"./vendor-dnd-kit-accessibility-BHxJPORy.js";import"./vendor-dnd-kit-sortable-CsEagZ81.js";import"./vendor-framer-motion-livSKyyV.js";import"./vendor-motion-dom-D_o3BTI3.js";import"./vendor-motion-utils-BGh8QBgx.js";import"./vendor-tanstack-react-virtual-g05Cx3z9.js";import"./vendor-tanstack-virtual-core-Dubg10g5.js";import"./vendor-sonner-DFyL3AvE.js";import"./vendor-cmdk-DgLInKvT.js";import"./vendor-radix-ui-react-dialog-B94Bt2qq.js";import"./vendor-radix-ui-primitive-Dc_FVRD7.js";import"./vendor-radix-ui-react-compose-refs-DcAh5WdT.js";import"./vendor-radix-ui-react-context-Czxm7Z62.js";import"./vendor-radix-ui-react-id-f5BLKEVw.js";import"./vendor-radix-ui-react-use-layout-effect-D_3IhJdJ.js";import"./vendor-radix-ui-react-use-controllable-state-BFT5YmkA.js";import"./vendor-radix-ui-react-dismissable-layer-D7kXcwgJ.js";import"./vendor-radix-ui-react-primitive-DhVoX9SD.js";import"./vendor-radix-ui-react-slot-BQgQOasS.js";import"./vendor-radix-ui-react-use-callback-ref-BZPnuXAC.js";import"./vendor-radix-ui-react-use-escape-keydown-oph88g-F.js";import"./vendor-radix-ui-react-focus-scope-CiaxW4dD.js";import"./vendor-radix-ui-react-portal-BMJYHWck.js";import"./vendor-radix-ui-react-presence-CNxf-uq4.js";import"./vendor-radix-ui-react-focus-guards-B6qJXYua.js";import"./vendor-react-remove-scroll-DO7dCTnK.js";import"./vendor-tslib-De9GV7Vy.js";import"./vendor-react-remove-scroll-bar-BcFYyj3E.js";import"./vendor-react-style-singleton-q6EMHnwf.js";import"./vendor-get-nonce-C-Z93AgS.js";import"./vendor-use-sidecar-zDC0RhA3.js";import"./vendor-use-callback-ref-D3RC8gqK.js";import"./vendor-aria-hidden-DvXkyWUv.js";import"./vendor-codemirror-language-BgPj_McA.js";import"./vendor-lezer-common-B3-cUCR_.js";import"./vendor-codemirror-state-C9awkmn-.js";import"./vendor-marijn-find-cluster-break-DXwl3gUT.js";import"./vendor-codemirror-view-CiLROUVV.js";import"./vendor-style-mod-NZ5MEPvj.js";import"./vendor-w3c-keyname-Vcq4gwWv.js";import"./vendor-crelt-C8TCjufn.js";import"./vendor-lezer-highlight-CDMHUZ74.js";import"./vendor-codemirror-lang-javascript-Cc2ZYbgX.js";import"./vendor-lezer-javascript-D7Tj24ot.js";import"./vendor-lezer-lr-bhwg1HMg.js";import"./vendor-codemirror-autocomplete-DzPz9Wgq.js";import"./vendor-codemirror-lang-json-CMBS2tG9.js";import"./vendor-lezer-json-Iaex0UN-.js";import"./vendor-codemirror-lang-css-O0cZX78y.js";import"./vendor-lezer-css-C5xB6jxd.js";import"./vendor-codemirror-lang-html-CfgK872x.js";import"./vendor-lezer-html-C0I1p52t.js";import"./vendor-codemirror-lang-markdown-BPGOH9ye.js";import"./vendor-lezer-markdown-C6g-d6P3.js";import"./vendor-codemirror-language-data-Dp3KMRQG.js";import"./vendor-codemirror-lang-python-HahnQaAS.js";import"./vendor-lezer-python-CvuFxeZl.js";import"./vendor-codemirror-commands-B7H_j0iU.js";import"./vendor-legendapp-list-gg7YLRW7.js";import"./vendor-tailwind-merge-CPcsbTWB.js";import"./vendor-fuse.js-Dw8P0qyA.js";import"./web-components-_mNEMjFY.js";import"./computeVirtualFileMetrics-CzgLum_K.js";import"./vendor-shiki-Bbf2GEcE.js";import"./vendor-shikijs-core-CGM_jqPK.js";import"./vendor-shikijs-types-CPSbSeK6.js";import"./vendor-shikijs-vscode-textmate-BgLrS_2D.js";import"./vendor-hast-util-to-html-BKBhbTDb.js";import"./vendor-html-void-elements-CkdplsNl.js";import"./vendor-property-information-rJCbAzvX.js";import"./vendor-zwitch-C2o2j-tx.js";import"./vendor-stringify-entities-CsH1VEn9.js";import"./vendor-character-entities-legacy-BpoLgcEt.js";import"./vendor-character-entities-html4-D7taRCQ7.js";import"./vendor-ccount-c2V3InAJ.js";import"./vendor-comma-separated-tokens-D8TqSuIK.js";import"./vendor-space-separated-tokens-DD3iYX1K.js";import"./vendor-hast-util-whitespace-D4Wp6AEg.js";import"./vendor-shikijs-engine-oniguruma-DPWuIF1G.js";import"./themeResolver-BmJyTmjJ.js";import"./vendor-pierre-theming-C6Z4M5_u.js";import"./vendor-shikijs-engine-javascript-Cme4830A.js";import"./vendor-oniguruma-to-es-DLBnFDCE.js";import"./vendor-oniguruma-parser-C0D4_9rT.js";import"./vendor-regex-BBWcyTkg.js";import"./vendor-regex-utilities-2BQyy0xI.js";import"./vendor-regex-recursion-JU4mF-dD.js";import"./vendor-shikijs-transformers-CZWsLLKm.js";import"./VirtualizedFileDiff-fFFm2QgH.js";import"./isDiffPlainText-Dx-dB_8l.js";import"./vendor-diff-C3JhlQdm.js";import"./Virtualizer-D_a8gKmg.js";import"./useInlineCommentController-Brz_cLcQ.js";import"./appThemeRegistry-BK0dHKa6.js";import"./registerCustomTheme-BBXUCRnG.js";import"./useRootScrollLock-lXEnWlaU.js";import"./scheduledTasksApi-D8yamh1b.js";import"./vendor-cron-parser-CUcP7OfX.js";import"./vendor-luxon-BZ0puJYg.js";const Yn="aiworker.git-base-branch",Zn=100,$e=(s,e)=>JSON.stringify([ct(),s,e]),kt=$n()(Wn((s,e)=>({overrides:{},getOverride:(i,a)=>!i||!a?null:e().overrides[$e(i,a)]??null,setOverride:(i,a,d)=>{!i||!a||!d||s(f=>{const h=$e(i,a),x=Object.entries({...f.overrides,[h]:d});for(;x.length>Zn;)x.shift();return{overrides:Object.fromEntries(x)}})},clearOverride:(i,a)=>{!i||!a||s(d=>{const f=$e(i,a);if(!(f in d.overrides))return d;const h={...d.overrides};return delete h[f],{overrides:h}})}}),{name:Yn,storage:wn(),partialize:s=>({overrides:s.overrides})})),es=(s,e)=>!!s&&e!==null&&s!==e,ts=(s,e,i,a)=>i?s===null||a&&e===null?!0:e!==null&&s===e:!1,ns=(s,e)=>s==="branch"&&!e?"working":s,ss=(s,e,i)=>JSON.stringify([s,e,i]),rs=(s,e,i,a,d,f)=>{const h=o.useRef({directory:"",attempts:0}),[x,u]=o.useState(()=>({directory:"",exhausted:!1})),p=o.useRef(d);p.current=d;const b=!!s&&x.directory===s&&x.exhausted;return o.useEffect(()=>{if(!s||!e||a||i)return;const w=h.current.directory===s?h.current.attempts:0;if(w>=f){x.directory===s&&x.exhausted||u({directory:s,exhausted:!0});return}h.current={directory:s,attempts:w+1},p.current()},[s,x,a,e,i,f]),b},is=(s,e,i,a)=>{const[d,f]=o.useState(()=>new Map),h=o.useRef(d);h.current=d;const x=o.useRef(i);x.current=i;const u=o.useCallback((p,b)=>{const w=new Map(h.current);if(b===null){if(!w.delete(p))return}else w.set(p,b);h.current=w,f(w)},[]);return o.useEffect(()=>{s&&(h.current=new Map,f(h.current))},[s]),o.useEffect(()=>{const p=x.current;if(!s||!p||!e)return;let b=!1;const w=new Set;for(const y of e.split("\0"))h.current.has(y)||(w.add(y),u(y,a),p(y).then(j=>{b||(w.delete(y),u(y,j))}).catch(()=>{b||(w.delete(y),u(y,null))}));return()=>{b=!0;for(const y of w)u(y,null)}},[e,a,s,u]),d},Ct=(s,e)=>{if(e.length===0)return null;let i=null,a=null;for(const f of e)f.top<=s?(!i||f.top>i.top)&&(i=f):(!a||f.top<a.top)&&(a=f);const d=i??a;return d?{path:d.path,topOffset:d.top-s}:null},os=(s,e,i,a)=>Math.min(Math.max(0,a),Math.max(0,s+i-e)),Rt=s=>{if(!s)return null;const e=s.split(`
|
|
2
|
-
`);let i=null;for(const a of e){const d=a.match(/^@@\s*-\d+(?:,\d+)?\s+\+(\d+)(?:,\d+)?\s*@@/);if(d){const f=Number.parseInt(d[1]??"",10);i=Number.isFinite(f)&&f>=1?f:null;continue}if(i!==null){if(a.startsWith(" ")){i+=1;continue}if(a.startsWith("+"))return i;if(a.startsWith("-"))return Math.max(1,i)}}return null},as=1100,ls=15e3,Dt=500,At=300,cs=1e6,us=3,fs=s=>s<=6?s:s<=12?6:s<=25?4:2,ds={original:"",modified:"",isBinary:!1,contextMode:"patch"},ps=3,ms=o.memo(()=>{const s=he.c(4),{t:e}=ge();let i;s[0]!==e?(i=e("diffView.binary.unavailable"),s[0]=e,s[1]=i):i=s[1];let a;return s[2]!==i?(a=n.jsx("div",{className:"rounded-lg border border-border/60 bg-background px-3 py-2",children:n.jsx("div",{className:"typography-meta text-muted-foreground",children:i})}),s[2]=i,s[3]=a):a=s[3],a}),It={"?":{code:"?",color:"var(--status-info)",descriptionKey:"diffView.change.untracked"},A:{code:"A",color:"var(--status-success)",descriptionKey:"diffView.change.new"},D:{code:"D",color:"var(--status-error)",descriptionKey:"diffView.change.deleted"},R:{code:"R",color:"var(--status-info)",descriptionKey:"diffView.change.renamed"},C:{code:"C",color:"var(--status-info)",descriptionKey:"diffView.change.copied"},M:{code:"M",color:"var(--status-warning)",descriptionKey:"diffView.change.modified"}},hs=It.M,gs=s=>{const e=s.index?.trim(),i=s.working_dir?.trim();return e&&e!=="?"?e.charAt(0):i?i.charAt(0):e?.charAt(0)||i?.charAt(0)||"M"},Ot=s=>{const e=gs(s);return It[e]??hs},xs=s=>{const{index:e,working_dir:i}=s;return e==="A"||i==="A"||e==="?"||i==="?"},Et=s=>{const e=s.index?.trim();return!!(e&&e!=="?")},Bt=s=>!!s.working_dir?.trim()||s.index==="?",bs=(s,e)=>Mn(s,e),Vt=s=>(s||"").replace(/\\/g,"/").replace(/\/+$/,""),Lt=(s,e)=>{const i=s.split(`
|
|
3
|
-
`),a=e.split(`
|
|
4
|
-
`),d=Math.min(i.length,a.length);for(let f=0;f<d;f+=1)if(i[f]!==a[f])return f+1;return a.length>i.length?i.length+1:i.length>a.length?Math.max(1,a.length):1},ws=s=>/^Binary files .+ differ$/m.test(s)||/^GIT binary patch$/m.test(s),ys=s=>Array.isArray(s)?s.filter(e=>!e||typeof e!="object"?!1:typeof e.file=="string"):[],vs=s=>s==="added"?"A":s==="deleted"?"D":"M",lt=(s,e,i)=>ws(e)?{original:"",modified:"",isBinary:!0,patch:e,contextMode:i}:{original:"",modified:"",patch:e,fileDiff:Qn(s,e),contextMode:i},zt=(s,e,i)=>{const a=s??0,d=e??0;return!a&&!d?null:n.jsxs("span",{className:ue("typography-meta flex items-center gap-1 text-xs whitespace-nowrap","flex-shrink-0",i?.className),children:[a?n.jsxs("span",{style:{color:"var(--status-success)"},children:["+",a]}):null,d?n.jsxs("span",{style:{color:"var(--status-error)"},children:["-",d]}):null]})},js=o.memo(s=>{const e=he.c(58),{scope:i,workingCount:a,stagedCount:d,turnCount:f,branchCount:h,showBranchOption:x,onScopeChange:u}=s,{t:p}=ge(),[b,w]=o.useState(!1),y=i==="staged"?d:i==="turn"?f:i==="branch"?h??0:a;let j;e[0]!==i||e[1]!==p?(j=p(i==="staged"?"diffView.scope.staged":i==="turn"?"diffView.scope.lastTurn":i==="branch"?"diffView.scope.branch":"diffView.scope.changed"),e[0]=i,e[1]=p,e[2]=j):j=e[2];const P=j;let m;e[3]!==p?(m=p("diffView.scope.selectorAria"),e[3]=p,e[4]=m):m=e[4];let le;e[5]!==y?(le=n.jsxs("span",{className:"diff-toolbar__scope-count",children:[": ",y]}),e[5]=y,e[6]=le):le=e[6];let k;e[7]!==P||e[8]!==le?(k=n.jsxs("span",{className:"whitespace-nowrap",children:[P,le]}),e[7]=P,e[8]=le,e[9]=k):k=e[9];let E;e[10]===Symbol.for("react.memo_cache_sentinel")?(E=n.jsx(A,{name:"arrow-down-s",className:"size-4 flex-shrink-0 opacity-60"}),e[10]=E):E=e[10];let L;e[11]!==m||e[12]!==k?(L=n.jsx(On,{asChild:!0,children:n.jsxs("button",{type:"button",className:"flex h-7 flex-shrink-0 items-center gap-1.5 rounded-md px-2 typography-ui-label font-semibold text-foreground outline-none hover:bg-interactive-hover focus-visible:ring-2 focus-visible:ring-ring","aria-label":m,children:[k,E]})}),e[11]=m,e[12]=k,e[13]=L):L=e[13];let H;e[14]!==u?(H=B=>{(B==="working"||B==="staged"||B==="turn"||B==="branch")&&(u?.(B),w(!1))},e[14]=u,e[15]=H):H=e[15];let N;e[16]!==p?(N=p("diffView.scope.changed"),e[16]=p,e[17]=N):N=e[17];let ee;e[18]!==N?(ee=n.jsx("span",{children:N}),e[18]=N,e[19]=ee):ee=e[19];let U;e[20]!==a?(U=n.jsx("span",{className:"typography-meta text-muted-foreground",children:a}),e[20]=a,e[21]=U):U=e[21];let q;e[22]!==U||e[23]!==ee?(q=n.jsx(Ge,{value:"working",children:n.jsxs("span",{className:"flex min-w-0 flex-1 items-center justify-between gap-3",children:[ee,U]})}),e[22]=U,e[23]=ee,e[24]=q):q=e[24];let G;e[25]!==p?(G=p("diffView.scope.staged"),e[25]=p,e[26]=G):G=e[26];let K;e[27]!==G?(K=n.jsx("span",{children:G}),e[27]=G,e[28]=K):K=e[28];let te;e[29]!==d?(te=n.jsx("span",{className:"typography-meta text-muted-foreground",children:d}),e[29]=d,e[30]=te):te=e[30];let _;e[31]!==K||e[32]!==te?(_=n.jsx(Ge,{value:"staged",children:n.jsxs("span",{className:"flex min-w-0 flex-1 items-center justify-between gap-3",children:[K,te]})}),e[31]=K,e[32]=te,e[33]=_):_=e[33];let I;e[34]!==p?(I=p("diffView.scope.lastTurn"),e[34]=p,e[35]=I):I=e[35];let V;e[36]!==I?(V=n.jsx("span",{children:I}),e[36]=I,e[37]=V):V=e[37];let O;e[38]!==f?(O=n.jsx("span",{className:"typography-meta text-muted-foreground",children:f}),e[38]=f,e[39]=O):O=e[39];let ne;e[40]!==V||e[41]!==O?(ne=n.jsx(Ge,{value:"turn",children:n.jsxs("span",{className:"flex min-w-0 flex-1 items-center justify-between gap-3",children:[V,O]})}),e[40]=V,e[41]=O,e[42]=ne):ne=e[42];let T;e[43]!==h||e[44]!==x||e[45]!==p?(T=x?n.jsx(Ge,{value:"branch",children:n.jsxs("span",{className:"flex min-w-0 flex-1 items-center justify-between gap-3",children:[n.jsx("span",{children:p("diffView.scope.branch")}),n.jsx("span",{className:"typography-meta text-muted-foreground",children:h??"…"})]})}):null,e[43]=h,e[44]=x,e[45]=p,e[46]=T):T=e[46];let se;e[47]!==i||e[48]!==q||e[49]!==_||e[50]!==ne||e[51]!==T||e[52]!==H?(se=n.jsx(zn,{align:"start",className:"w-40",children:n.jsxs(Pn,{value:i,onValueChange:H,children:[q,_,ne,T]})}),e[47]=i,e[48]=q,e[49]=_,e[50]=ne,e[51]=T,e[52]=H,e[53]=se):se=e[53];let re;return e[54]!==b||e[55]!==se||e[56]!==L?(re=n.jsxs(Gn,{open:b,onOpenChange:w,children:[L,se]}),e[54]=b,e[55]=se,e[56]=L,e[57]=re):re=e[57],re}),Ss=o.memo(s=>{const e=he.c(11),{changedFiles:i,selectedFile:a,onSelectFile:d}=s,{t:f}=ge();if(i.length===0)return null;let h;if(e[0]!==i||e[1]!==d||e[2]!==a||e[3]!==f){let u;e[5]!==d||e[6]!==a||e[7]!==f?(u=p=>{const b=Ot(p),w=a===p.path;return n.jsx("li",{children:n.jsxs("button",{type:"button",onClick:()=>d(p.path),className:ue("flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left transition-colors",w?"bg-interactive-selection text-interactive-selection-foreground":"text-muted-foreground hover:bg-interactive-hover hover:text-foreground"),children:[n.jsx(_t,{filePath:p.path,className:"h-3.5 w-3.5 flex-shrink-0"}),n.jsx("span",{className:"typography-micro font-semibold w-4 text-center uppercase",style:{color:b.color},title:f(b.descriptionKey),"aria-label":f(b.descriptionKey),children:b.code}),n.jsx("span",{className:"min-w-0 flex-1 truncate typography-meta",style:{direction:"rtl",textAlign:"left",unicodeBidi:"plaintext"},title:p.path,children:p.path}),zt(p.insertions,p.deletions)]})},p.path)},e[5]=d,e[6]=a,e[7]=f,e[8]=u):u=e[8],h=i.map(u),e[0]=i,e[1]=d,e[2]=a,e[3]=f,e[4]=h}else h=e[4];let x;return e[9]!==h?(x=n.jsx(at,{outerClassName:"flex-1 min-h-0",className:"px-2 py-2",children:n.jsx("ul",{className:"flex flex-col gap-1",children:h})}),e[9]=h,e[10]=x):x=e[10],x}),Ns=o.memo(s=>{const e=he.c(20),{filePath:i,diff:a,renderSideBySide:d}=s,{t:f}=ge(),h=a.original.length>0,x=a.modified.length>0,u=d?"flex flex-row gap-6 items-start justify-center":"flex flex-col gap-4 items-center",p=d?"flex flex-col items-center gap-2 flex-1 min-w-0":"flex flex-col items-center gap-2";let b;e[0]===Symbol.for("react.memo_cache_sentinel")?(b={contain:"layout"},e[0]=b):b=e[0];let w;e[1]!==a.original||e[2]!==i||e[3]!==h||e[4]!==p||e[5]!==d||e[6]!==f?(w=h&&n.jsxs("div",{className:p,children:[n.jsx("span",{className:"typography-meta text-muted-foreground font-medium",children:f("diffView.image.original")}),n.jsx("img",{src:a.original,alt:f("diffView.image.originalAlt",{path:i}),className:d?"max-w-full max-h-[70vh] object-contain":"max-w-full object-contain",style:{imageRendering:"auto"}})]}),e[1]=a.original,e[2]=i,e[3]=h,e[4]=p,e[5]=d,e[6]=f,e[7]=w):w=e[7];let y;e[8]!==a.modified||e[9]!==i||e[10]!==x||e[11]!==h||e[12]!==p||e[13]!==d||e[14]!==f?(y=x&&n.jsxs("div",{className:p,children:[n.jsx("span",{className:"typography-meta text-muted-foreground font-medium",children:f(h?"diffView.image.modified":"diffView.image.new")}),n.jsx("img",{src:a.modified,alt:f("diffView.image.modifiedAlt",{path:i}),className:d?"max-w-full max-h-[70vh] object-contain":"max-w-full object-contain",style:{imageRendering:"auto"}})]}),e[8]=a.modified,e[9]=i,e[10]=x,e[11]=h,e[12]=p,e[13]=d,e[14]=f,e[15]=y):y=e[15];let j;return e[16]!==u||e[17]!==w||e[18]!==y?(j=n.jsx("div",{className:"w-full overflow-auto p-4",style:b,children:n.jsxs("div",{className:u,children:[w,y]})}),e[16]=u,e[17]=w,e[18]=y,e[19]=j):j=e[19],j}),Fs=o.memo(s=>{const e=he.c(16),{filePath:i,diff:a,renderSideBySide:d,wrapLines:f}=s;let h;e[0]!==i?(h=In(i)||"text",e[0]=i,e[1]=h):h=e[1];const x=h;if(a.isBinary){let b;return e[2]===Symbol.for("react.memo_cache_sentinel")?(b=n.jsx(ms,{}),e[2]=b):b=e[2],b}if(Ve(i)){let b;return e[3]!==a||e[4]!==i||e[5]!==d?(b=n.jsx(Ns,{filePath:i,diff:a,renderSideBySide:d}),e[3]=a,e[4]=i,e[5]=d,e[6]=b):b=e[6],b}let u;e[7]===Symbol.for("react.memo_cache_sentinel")?(u={contain:"layout"},e[7]=u):u=e[7];let p;return e[8]!==a.fileDiff||e[9]!==a.modified||e[10]!==a.original||e[11]!==i||e[12]!==x||e[13]!==d||e[14]!==f?(p=n.jsx("div",{className:"w-full",style:u,children:n.jsx(Hn,{original:a.original,modified:a.modified,fileDiff:a.fileDiff,language:x,fileName:i,renderSideBySide:d,wrapLines:f,layout:"inline"})}),e[8]=a.fileDiff,e[9]=a.modified,e[10]=a.original,e[11]=i,e[12]=x,e[13]=d,e[14]=f,e[15]=p):p=e[15],p}),ks=o.memo(s=>{const e=he.c(7),{filePath:i,staged:a,busyAction:d,disabled:f,onAction:h}=s,{t:x}=ge();let u;return e[0]!==d||e[1]!==f||e[2]!==i||e[3]!==h||e[4]!==a||e[5]!==x?(u=n.jsx("div",{className:"flex items-center gap-0.5 rounded-full border border-[var(--interactive-border)]/45 bg-[var(--surface-background)]/95 px-1 py-0.5 shadow-sm backdrop-blur-md",children:a?n.jsx(ot,{label:x("gitView.changes.unstageFileAria",{path:i}),icon:"arrow-go-back",loading:d==="unstage",disabled:f,onClick:()=>h("unstage")}):n.jsxs(n.Fragment,{children:[n.jsx(ot,{label:x("gitView.changes.revertFileAria",{path:i}),icon:"arrow-go-back",loading:d==="discard",disabled:f,tone:"failure",onClick:()=>h("discard")}),n.jsx(ot,{label:x("gitView.changes.stageFileAria",{path:i}),icon:"add",loading:d==="stage",disabled:f,tone:"success",onClick:()=>h("stage")})]})}),e[0]=d,e[1]=f,e[2]=i,e[3]=h,e[4]=a,e[5]=x,e[6]=u):u=e[6],u}),ot=s=>{const e=he.c(14),{label:i,icon:a,loading:d,disabled:f,tone:h,onClick:x}=s,u=h==="failure"&&"text-[var(--status-error)] hover:text-[var(--status-error)]",p=h==="success"&&"text-[var(--status-success)] hover:text-[var(--status-success)]";let b;e[0]!==u||e[1]!==p?(b=ue("h-6 w-6 rounded-none bg-transparent p-0 text-muted-foreground opacity-70 hover:bg-transparent hover:text-foreground hover:opacity-100",u,p),e[0]=u,e[1]=p,e[2]=b):b=e[2];let w;e[3]!==x?(w=P=>{P.stopPropagation(),x()},e[3]=x,e[4]=w):w=e[4];let y;e[5]!==a||e[6]!==d?(y=d?n.jsx(A,{name:"loader-4",className:"size-3.5 animate-spin"}):n.jsx(A,{name:a,className:a==="add"?"size-4":"size-3.5"}),e[5]=a,e[6]=d,e[7]=y):y=e[7];let j;return e[8]!==f||e[9]!==i||e[10]!==b||e[11]!==w||e[12]!==y?(j=n.jsx(pe,{variant:"ghost",size:"sm",className:b,disabled:f,title:i,"aria-label":i,onClick:w,children:y}),e[8]=f,e[9]=i,e[10]=b,e[11]=w,e[12]=y,e[13]=j):j=e[13],j},Cs=o.memo(({directory:s,file:e,layout:i,wrapLines:a,isSelected:d,isExpanded:f,isMounted:h,onSelect:x,onExpandedChange:u,registerSectionRef:p,showOpenInEditorAction:b=!1,isOpeningInEditor:w=!1,onOpenInEditor:y,staged:j=!1,loadFullFiles:P=!1,initialDiffData:m=null,readOnlyActions:le=!1})=>{const{t:k}=ge(),{git:E}=Tt(),L=Z(o.useCallback(c=>c.directories.get(s)?.diffCache.get(e.path)??null,[s,e.path])),H=Z(c=>c.setDiff),N=Z(c=>c.fetchStatus),ee=Y(c=>c.setDiffFileLayout),[U,q]=o.useState(0),[G,K]=o.useState(null),[te,_]=o.useState(!1),[I,V]=o.useState(null),[O,ne]=o.useState(!1),[T,se]=o.useState(null),[re,B]=o.useState(null),ie=o.useRef(null),We=o.useRef(null),ce=o.useMemo(()=>Ot(e),[e]),fe=i==="side-by-side",xe=P?"full":"patch",Ce=`${e.index}:${e.working_dir}:${e.insertions}:${e.deletions}`,X=o.useMemo(()=>m||(j?re:T||(L?{original:L.original,modified:L.modified,isBinary:L.isBinary,contextMode:"full"}:null)),[L,m,T,j,re]),Re=X?.contextMode===xe,He=o.useCallback(c=>{We.current=c,p(e.path,c)},[e.path,p]),Le=o.useCallback(c=>{u(e.path,c)},[e.path,u]),be=o.useCallback(()=>{x(e.path)},[e.path,x]);o.useEffect(()=>{j?B(null):se(null),K(null),ie.current=null},[Ce,j]),o.useEffect(()=>{if(!f||!h)return;if(!s||m||X&&Re){ie.current=null,_(!1);return}const c=`${s}::${e.path}::${j?"staged":"unstaged"}::${Ce}::${xe}::${U}`;if(ie.current===c)return;ie.current=c,K(null),_(!0);let C=!1;const oe=ct(),Me=P?cs:us,de=Ve(e.path)?E.getGitFileDiff(s,{path:e.path,staged:j}):E.getGitDiff(s,{path:e.path,staged:j,contextLines:Me}),De=ls,Ae=new Promise(($,J)=>{setTimeout(()=>J(new Error(`Timed out after ${De}ms`)),De)});return Promise.race([de,Ae]).then($=>{if(!C){if("diff"in $){const J=lt(e.path,$.diff,xe);j?B(J):se(J)}else{const J={original:$.original??"",modified:$.modified??"",isBinary:$.isBinary,contextMode:"full"};j?B(J):H(s,e.path,J,oe)}_(!1)}}).catch($=>{if(C)return;const J=$ instanceof Error?$.message:String($);K(J),_(!1)}),()=>{C=!0,ie.current===c&&(ie.current=null)}},[xe,X,Re,U,s,e.path,Ce,E,m,f,h,P,H,j]);const we=o.useCallback(()=>{Le(!f),be()},[Le,be,f]),Te=o.useCallback(async c=>{if(!(!s||I!==null)){V(c);try{c==="stage"?await E.stageGitFile(s,e.path):c==="unstage"?await E.unstageGitFile(s,e.path):await E.revertGitFile(s,e.path,{scope:"working"}),q(C=>C+1),await N(s,E)}catch(C){const oe=c==="unstage"?"gitView.toast.unstageFileFailed":c==="stage"?"gitView.toast.stageFileFailed":"gitView.toast.revertFailed";Ke.error(C instanceof Error?C.message:k(oe))}finally{V(C=>C===c?null:C)}}},[s,N,e.path,I,E,k]);return n.jsxs("div",{ref:He,className:"scroll-mt-9 border-b border-[var(--interactive-border)]/40 last:border-b-0",children:[n.jsx("div",{className:"sticky top-0 z-30 border-b border-[var(--interactive-border)]/35 bg-[var(--surface-elevated)]/90 backdrop-blur-md supports-[backdrop-filter]:bg-[var(--surface-elevated)]/80",children:n.jsxs("div",{role:"button",tabIndex:0,onClick:we,onKeyDown:c=>{c.target===c.currentTarget&&(c.key==="Enter"||c.key===" ")&&(c.preventDefault(),we())},className:ue("cursor-pointer","group/header relative grid min-h-9 w-full min-w-0 grid-cols-[minmax(0,1fr)_auto] items-center gap-2 overflow-hidden px-3 py-2","bg-transparent","text-muted-foreground hover:text-foreground",d?"bg-[var(--interactive-selection)]/35":null),children:[n.jsx("div",{className:"absolute inset-0 pointer-events-none group-hover/header:bg-[var(--interactive-hover)]/50"}),n.jsxs("div",{className:"relative flex min-w-0 flex-1 items-center gap-2",children:[n.jsx("span",{className:"flex size-5 items-center justify-center opacity-70 group-hover/header:opacity-100",children:f?n.jsx(A,{name:"arrow-down-s",className:"size-4"}):n.jsx(A,{name:"arrow-right-s",className:"size-4"})}),n.jsx("span",{className:"typography-micro font-semibold leading-none w-4 text-center uppercase",style:{color:ce.color},title:k(ce.descriptionKey),"aria-label":k(ce.descriptionKey),children:ce.code}),n.jsx("span",{className:"min-w-0 flex-1 overflow-hidden typography-ui-label",title:e.path,children:n.jsxs("span",{className:"flex min-w-0 items-center gap-2",children:[n.jsx(_t,{filePath:e.path,className:"h-3.5 w-3.5 flex-shrink-0 align-middle"}),(()=>{const c=e.path.lastIndexOf("/");if(c===-1)return n.jsx("span",{className:"block min-w-0 truncate typography-ui-label text-foreground",style:{direction:"rtl",textAlign:"left",unicodeBidi:"plaintext"},children:e.path});const C=e.path.slice(0,c),oe=e.path.slice(c+1);return n.jsxs("span",{className:"flex min-w-0 items-baseline overflow-hidden",children:[n.jsx("span",{className:"min-w-0 truncate typography-ui-label text-muted-foreground",style:{direction:"rtl",textAlign:"left",unicodeBidi:"plaintext"},children:C}),n.jsxs("span",{className:"flex-shrink-0 typography-ui-label",children:[n.jsx("span",{className:"text-muted-foreground",children:"/"}),n.jsx("span",{className:"text-foreground",children:oe})]})]})})()]})})]}),n.jsxs("div",{className:"relative flex shrink-0 items-center justify-self-end gap-2",children:[zt(e.insertions,e.deletions),b&&y?n.jsx(pe,{variant:"ghost",size:"sm",className:"h-5 w-5 p-0 opacity-70 hover:opacity-100",title:k("diffView.actions.openFileInEditorAtChange"),onClick:c=>{c.stopPropagation(),y(e.path,X)},disabled:w,children:w?n.jsx(A,{name:"loader-4",className:"size-3.5 animate-spin"}):n.jsx(A,{name:"edit",className:"size-3.5"})}):null,n.jsx(Mt,{mode:fe?"side-by-side":"unified",onModeChange:c=>{const C=c==="side-by-side"?"side-by-side":"inline";ee(e.path,C)},className:"opacity-70"})]})]})}),f&&n.jsxs("div",{className:"relative bg-background overflow-hidden",children:[!h&&!G?n.jsx("div",{className:"h-40 border border-border/40 bg-background/40"}):null,G?n.jsxs("div",{className:"flex flex-col items-center gap-2 px-4 py-8 text-sm text-muted-foreground",children:[n.jsx("div",{className:"typography-ui-label font-semibold text-foreground",children:k("diffView.state.failedToLoadDiff")}),n.jsx("div",{className:"typography-meta text-muted-foreground max-w-[32rem] text-center",children:G}),n.jsx("button",{type:"button",className:"typography-ui-label text-primary hover:underline",onClick:()=>q(c=>c+1),children:k("diffView.actions.retry")})]}):null,h&&te&&!X&&!G?n.jsxs("div",{className:"flex items-center justify-center gap-2 px-4 py-8 text-sm text-muted-foreground",children:[n.jsx(A,{name:"loader-4",className:"size-4 animate-spin"}),k("diffView.state.loadingDiff")]}):null,h&&X&&!O&&e.insertions+e.deletions>Dt?n.jsxs("div",{className:"flex flex-col items-center gap-2 px-4 py-8 text-sm text-muted-foreground",children:[n.jsx("div",{className:"typography-ui-label font-semibold text-foreground",children:k("diffView.state.largeDiff",{count:e.insertions+e.deletions})}),n.jsx("div",{className:"typography-meta text-muted-foreground",children:k("diffView.state.largeDiffDescription")}),n.jsx("button",{type:"button",className:"typography-ui-label text-primary hover:underline",onClick:()=>ne(!0),children:k("diffView.actions.renderAnyway")})]}):null,h&&X&&(O||e.insertions+e.deletions<=Dt)?n.jsxs(n.Fragment,{children:[n.jsx(Fs,{filePath:e.path,diff:X,renderSideBySide:fe,wrapLines:a}),n.jsx("div",{className:"pointer-events-none absolute bottom-3 right-3 z-20",children:n.jsx("div",{className:"pointer-events-auto",children:le?null:n.jsx(ks,{filePath:e.path,staged:j,busyAction:I,disabled:I!==null,onAction:Te})})})]}):null]})]})}),Ki=({hideStackedFileSidebar:s=!1,stackedDefaultCollapsedAll:e=!1,pinSelectedFileHeaderToTopOnNavigate:i=!1,showOpenInEditorAction:a=!1,diffScope:d="all",onDiffScopeChange:f,targetFilePath:h=null,flushContent:x=!1})=>{const{t:u}=ge(),{git:p,files:b}=Tt(),w=yn(),{rootIsGitRepo:y,gitDirectory:j,nestedRepos:P}=vn(w??null),m=j??w,le=Y(t=>t.openContextSurface),k=Un(t=>t.requestSource),{screenWidth:E,isMobile:L}=jn(),H=Sn(m??null),N=Nn(m??null),ee=Fn(m??null),U=Z(t=>t.setActiveDirectory),q=Z(t=>t.ensureStatus),G=Z(t=>t.selectNestedRepo),K=Z(t=>t.fetchStatus),te=Z(t=>t.fetchBranches),_=Z(t=>t.clearDiffCache),I=Z(t=>t.setDiff),[V,O]=o.useState(null),[ne,T]=o.useState(!1),[se,re]=o.useState(null),[B,ie]=o.useState(()=>new Set),[We,ce]=o.useState(()=>new Set),[fe,xe]=o.useState(!1),[Ce,X]=o.useState(0),[Re,He]=o.useState(()=>new Map),[Le,be]=o.useState(!1),[we,Te]=o.useState(!1),[c,C]=o.useState(d);o.useEffect(()=>{C(d)},[d]);const oe=Y(t=>t.pendingDiffFile),Me=Y(t=>t.pendingDiffStaged),de=Y(t=>t.pendingDiffScope),De=Y(t=>t.setPendingDiffFile),Ae=Y(t=>t.diffLayoutPreference),$=Y(t=>t.diffFileLayout),J=Y(t=>t.setDiffFileLayout),ut=Y(t=>t.diffWrapLines),Pt=Y(t=>t.setDiffWrapLines),ft=Y(t=>t.openContextFileAtLine),ye=Ft(t=>t.currentSessionId),Ue=kn(ye??"",w??void 0),qe=ut,Xe=c==="staged"?!0:c==="working"?!1:null,_e=Xe??ne,Je=L||E<=768,Gt=!!ye&&c!=="turn"&&!Je&&!Pe(),Kt=c!=="turn"&&!Je&&!Pe(),$t=!s&&!Je&&E>=1024,ve=o.useRef(null),Ee=o.useRef(new Map),me=o.useRef(null),z=o.useRef(null),je=o.useRef(!1),Se=o.useRef(null),dt=o.useRef(null),Qe=o.useRef(null),Ye=o.useRef(null),pt=o.useCallback(()=>{const t=ve.current;if(!t)return null;const r=t.getBoundingClientRect().top,l=[];for(const[g,v]of Ee.current)v&&l.push({path:g,top:v.getBoundingClientRect().top});return Ct(r,l)},[]),Ne=o.useCallback(()=>{me.current=null,je.current=!1,re(null),z.current!==null&&(window.cancelAnimationFrame(z.current),z.current=null)},[]),Fe=o.useCallback(t=>{ie(r=>{if(r.has(t))return r;const l=new Set(r);return l.add(t),l})},[]),Be=o.useMemo(()=>{for(let t=Ue.length-1;t>=0;t-=1){const r=Ue[t];if(r.role==="user")return ys(r.summary?.diffs)}return[]},[Ue]),Wt=o.useMemo(()=>{const t=new Map;for(const r of Be)if(r.file){if(typeof r.patch=="string"){t.set(r.file,lt(r.file,r.patch,"patch"));continue}t.set(r.file,{original:r.before??"",modified:r.after??"",contextMode:"full"})}return t},[Be]),Ht=o.useMemo(()=>N?.files?N.files.filter(Bt).length:0,[N]),Ut=o.useMemo(()=>N?.files?N.files.filter(Et).length:0,[N]),qt=Be.length,R=N?.current??null,ke=Z(t=>m?t.directories.get(m)?.branches??null:null),Xt=Z(t=>m?t.directories.get(m)?.isLoadingBranches??!1:!1),Jt=o.useCallback(()=>{m&&te(m,p)},[m,te,p]),Qt=rs(m??null,H!==!1,Xt,!!ke,Jt,ps),mt=o.useMemo(()=>{const t=N?.tracking?.trim().split("/")[0];return(t&&ke?.defaultBranches?.[t])??ke?.defaultBranches?.origin??null},[ke,N?.tracking]),Ze=!Pe()&&es(R,mt),Yt=N!==null,ht=Pe()||Qt||ts(R,mt,Yt,ke!==null),Zt=kt(t=>t.setOverride),en=kt(o.useCallback(t=>m&&R?t.overrides[$e(m,R)]??null:null,[R,m])),[tn,et]=o.useState(null),[nn,tt]=o.useState(!1),[gt,sn]=o.useState("");o.useEffect(()=>{const t=ns(c,!ht);t!==c&&(C(t),t==="working"&&f?.("working"))},[c,ht,f]),o.useEffect(()=>{if(!Ze||!m||!R){et(null),tt(!1);return}let t=!1;return tt(!1),Cn(m,R).then(r=>{t||et(r.base)}).catch(()=>{t||et(null)}).finally(()=>{t||tt(!0)}),()=>{t=!0}},[R,m,Ze]);const ae=en??tn,[Ie,xt]=o.useState(null),[bt,wt]=o.useState(null),Oe=o.useRef(0),nt=o.useCallback(()=>{if(!m||!R||!ae)return;const t=Oe.current+1;Oe.current=t,xt(null),wt(null),Rn(m,{base:ae,head:R}).then(r=>{Oe.current===t&&xt(r)}).catch(r=>{Oe.current===t&&wt(r instanceof Error?r.message:u("diffView.branch.loadError"))})},[ae,R,m,u]);o.useEffect(()=>{c==="branch"&&nt()},[c,nt]);const yt=c==="branch"&&m&&R&&ae?ss(m,ae,R):null,rn=o.useMemo(()=>c==="branch"?Array.from(B).sort().join("\0"):"",[c,B]),on=o.useCallback(t=>!m||!ae||!R?Promise.reject(new Error("branch range is unavailable")):Dn(m,{base:ae,head:R,path:t}).then(r=>lt(t,r.diff,"patch")),[ae,R,m]),an=is(yt,rn,yt?on:null,ds),ln=Ie?.length??null,S=o.useMemo(()=>{if(c==="branch")return(Ie??[]).map(l=>({path:l.path,index:"",working_dir:l.status,insertions:0,deletions:0,isNew:l.status==="A"})).sort((l,g)=>l.path.localeCompare(g.path));if(c==="turn")return Be.map(l=>({path:l.file??"",index:"",working_dir:vs(l.status),insertions:l.additions??0,deletions:l.deletions??0,isNew:l.status==="added"})).filter(l=>l.path).sort((l,g)=>l.path.localeCompare(g.path));if(!N?.files)return[];const t=N.diffStats??{},r=c==="staged"?Et:c==="working"?Bt:()=>!0;return N.files.filter(r).map(l=>({...l,insertions:t[l.path]?.insertions??0,deletions:t[l.path]?.deletions??0,isNew:xs(l)})).sort((l,g)=>l.path.localeCompare(g.path))},[c,Ie,Be,N]),st=o.useMemo(()=>S.map(t=>t.path).join("\0"),[S]);o.useEffect(()=>{const t=st?st.split("\0"):[],r=new Set(t),l=`${m??""}:${c}:${e?"collapsed":"default"}`,g=dt.current!==l;dt.current=l,ie(v=>{if(g){const D=e?0:fs(t.length);return new Set(t.slice(0,D))}let M=!1;const F=new Set;for(const D of v){if(!r.has(D)){M=!0;continue}F.add(D)}return M?F:v}),ce(v=>{if(g)return new Set;let M=!1;const F=new Set;for(const D of v){if(!r.has(D)){M=!0;continue}F.add(D)}return M?F:v})},[c,st,m,e]);const vt=o.useCallback(()=>{Se.current=null;const t=ve.current;if(!t)return;const r=t.getBoundingClientRect(),l=r.top-At,g=r.bottom+At,v={},M=[];for(const[F,D]of Ee.current){if(!D)continue;const W=D.getBoundingClientRect();M.push({path:F,top:W.top}),B.has(F)&&(W.bottom<l||W.top>g||(v[F]=!0))}Qe.current=Ct(r.top,M),ce(F=>{let D=!1;const W=new Set(F);for(const Nt of Object.keys(v))W.has(Nt)||(W.add(Nt),D=!0);return D?W:F})},[B]),Q=o.useCallback(()=>{typeof window>"u"||Se.current===null&&(Se.current=window.requestAnimationFrame(vt))},[vt]);o.useEffect(()=>{const t=ve.current;if(t)return Q(),t.addEventListener("scroll",Q,{passive:!0}),window.addEventListener("resize",Q),()=>{t.removeEventListener("scroll",Q),window.removeEventListener("resize",Q),Se.current!==null&&(window.cancelAnimationFrame(Se.current),Se.current=null)}},[S,B,Q]);const rt=o.useCallback(t=>{const r=$[t.path];if(r)return r;if(Ae==="inline")return"inline";if(Ae==="side-by-side")return"side-by-side";const l=E<as;return t.isNew||l?"inline":"side-by-side"},[$,Ae,E]),jt=o.useMemo(()=>S.length===0?null:S.every(t=>rt(t)==="side-by-side")?"side-by-side":"inline",[S,rt]);o.useEffect(()=>{m&&(U(m),q(m,p))},[m,U,q,p]),o.useEffect(()=>{if(m)return An.onGitRefreshHint(t=>{Vt(t.directory)===Vt(m)&&(t.paths?.length&&(Ye.current=pt()??Qe.current,_(m,t.paths),He(r=>{const l=new Map(r);for(const g of t.paths??[])l.set(g,(l.get(g)??0)+1);return l})),K(m,p,{silent:!0}))})},[pt,_,m,K,p]),o.useLayoutEffect(()=>{const t=Ye.current;if(!t)return;Ye.current=null;const r=ve.current,l=Ee.current.get(t.path);if(!r||!l)return;const g=r.getBoundingClientRect().top,v=l.getBoundingClientRect().top-g;r.scrollTop=os(r.scrollTop,t.topOffset,v,r.scrollHeight-r.clientHeight),Qe.current=t},[Re]),o.useEffect(()=>{c!=="all"&&!de||oe&&(de&&C(de),O(oe),T(de==="staged"||!de&&Me),De(null),je.current=!0,me.current=oe,Fe(oe),X(t=>t+1))},[c,Fe,oe,de,Me,De]),o.useEffect(()=>{if(c==="all")return;const t=h?.trim();t&&(O(t),T(c==="staged"),je.current=!0,me.current=t,Fe(t),X(r=>r+1))},[c,Fe,h]),o.useEffect(()=>{if(!V)return;S.some(r=>r.path===V)||(O(null),T(!1))},[S,V]);const cn=o.useCallback((t,r)=>{const l=Ee.current;r?l.set(t,r):l.delete(t),Q()},[Q]),un=o.useCallback((t,r)=>{Ne(),ie(l=>{const g=l.has(t);if(r===g)return l;const v=new Set(l);return r?v.add(t):v.delete(t),v}),r||ce(l=>{if(!l.has(t))return l;const g=new Set(l);return g.delete(t),g}),Q()},[Ne,Q]),fn=o.useCallback(()=>{Ne(),ie(t=>t.size>0?new Set:new Set(S.map(r=>r.path))),ce(new Set),Q()},[Ne,S,Q]),dn=o.useCallback(async t=>{if(!ye)return;const r=Ft.getState().getDirectoryForSession(ye)||w||"";if(!r){Ke.error(u("diffView.reviewDialog.toast.noSessionDirectory"));return}Te(!0);try{await En({originalSessionID:ye,directory:r,providerID:t.providerID,modelID:t.modelID,agent:t.agent||void 0,variant:t.variant||void 0,generateHandoff:t.generateHandoff,returnAfterHandoffRequest:t.generateHandoff,autoReview:t.autoReview}),be(!1)}catch(l){console.error("[review-flow] failed to start review flow",l),Ke.error(l instanceof Error?l.message:u("diffView.reviewDialog.toast.startFailed"))}finally{Te(!1)}},[ye,w,u]),ze=o.useCallback(t=>{const r=Ee.current.get(t),l=ve.current;if(!r||!l)return!1;const g=r.getBoundingClientRect().top-l.getBoundingClientRect().top;return l.scrollTo({top:l.scrollTop+g,behavior:"auto"}),!0},[]);o.useEffect(()=>{if(!me.current)return;let r=0;const l=20;let g=!1;const v=(F=!0)=>{g||(g=!0,me.current=null,je.current=!1,F&&re(null),z.current!==null&&(window.cancelAnimationFrame(z.current),z.current=null))},M=()=>{if(g){z.current=null;return}const F=me.current;if(!F){v(),z.current=null;return}if(!ze(F)){r+=1,r<l?z.current=window.requestAnimationFrame(M):(v(),z.current=null);return}if(i&&je.current){re(F),v(!1);return}v()};return z.current=window.requestAnimationFrame(M),()=>{g=!0,z.current!==null&&(window.cancelAnimationFrame(z.current),z.current=null)}},[i,Ce,ze]);const pn=o.useCallback(t=>{},[]),it=o.useCallback(t=>{Ne(),O(t),T(!1),je.current=!0,me.current=t,Fe(t),X(r=>r+1),ze(t)},[Ne,Fe,ze]);o.useEffect(()=>{const t=r=>{if(!r.altKey||r.metaKey||r.ctrlKey||r.shiftKey||r.key!=="ArrowDown"&&r.key!=="ArrowUp")return;const l=r.target;if(l instanceof HTMLElement&&(l.isContentEditable||l.tagName==="INPUT"||l.tagName==="TEXTAREA"||l.closest('[role="dialog"]'))||S.length===0)return;const g=r.key==="ArrowDown"?1:-1,v=V?S.findIndex(D=>D.path===V):-1,M=v===-1?g>0?0:S.length-1:v+g,F=S[M];F&&(r.preventDefault(),it(F.path))};return window.addEventListener("keydown",t),()=>window.removeEventListener("keydown",t)},[S,V,it]);const mn=o.useCallback(t=>{const r=t==="side-by-side"?"side-by-side":"inline";S.forEach(l=>{J(l.path,r)})},[S,J]),[hn,St]=o.useState(null),gn=o.useCallback(async(t,r)=>{if(!m||!t)return;St(t);const l=ct();try{let g=null;if(r?.patch&&!r.isBinary&&!Ve(t)?g=Rt(r.patch):r&&r.contextMode==="full"&&!r.isBinary&&!Ve(t)&&(g=Lt(r.original,r.modified)),g===null)try{const W=await p.getGitDiff(m,{path:t,staged:_e,contextLines:3});g=Rt(W.diff)}catch{g=null}let v=r;if(g===null||!v){const W=await p.getGitFileDiff(m,{path:t,staged:_e});v={original:W.original??"",modified:W.modified??"",isBinary:W.isBinary},_e||I(m,t,v,l)}const M=g??(v.isBinary||Ve(t)?1:Lt(v.original,v.modified)),F=bs(m,t),D=await qn(b,F,{directory:m});if(!D.ok){Ke.error(Xn(D.reason));return}ft(m,F,M,1)}finally{St(g=>g===t?null:g)}},[_e,m,b,p,ft,I]),xn=()=>{if(!m)return null;const t=r=>Xe!==null?Xe:ne&&r===V;return n.jsxs("div",{className:ue("flex min-w-0 flex-1 min-h-0 h-full",x?"gap-0":"gap-3 px-3 pb-3 pt-2"),children:[$t&&n.jsxs("section",{className:"hidden lg:flex w-72 flex-col rounded-xl border border-border/60 bg-background/70 overflow-hidden",children:[n.jsxs("div",{className:"flex items-center justify-between px-3 py-1.5 border-b border-border/40",children:[n.jsx("span",{className:"typography-ui-header font-semibold text-foreground",children:u("diffView.section.files")}),n.jsx("span",{className:"typography-meta text-muted-foreground",children:S.length})]}),n.jsx(Ss,{changedFiles:S,selectedFile:null,onSelectFile:it})]}),n.jsx("div",{className:"relative flex-1 min-w-0 min-h-0 h-full",children:n.jsx(at,{ref:ve,outerClassName:"min-h-0 h-full",className:"[overflow-anchor:none] pb-16",disableHorizontal:!0,observeMutations:!1,preventOverscroll:!0,"data-diff-virtual-root":!0,children:n.jsx("div",{className:"flex flex-col [overflow-anchor:none]","data-diff-virtual-content":!0,children:S.map(r=>n.jsx(Cs,{directory:m,file:r,layout:rt(r),wrapLines:qe,isSelected:!1,isExpanded:B.has(r.path),isMounted:We.has(r.path)||r.path===se,onSelect:pn,onExpandedChange:un,registerSectionRef:cn,showOpenInEditorAction:a&&c!=="turn",isOpeningInEditor:hn===r.path,onOpenInEditor:(l,g)=>{gn(l,g)},staged:t(r.path),loadFullFiles:fe,readOnlyActions:c==="branch",initialDiffData:c==="turn"?Wt.get(r.path)??null:c==="branch"?an.get(r.path)??null:null},`${r.path}:${Re.get(r.path)??0}`))})})})]})},bn=()=>{if(!m)return n.jsx("div",{className:"flex flex-1 items-center justify-center text-sm text-muted-foreground",children:u("diffView.state.selectSessionDirectory")});if(c!=="turn"&&ee&&!N)return n.jsxs("div",{className:"flex flex-1 items-center justify-center gap-2 text-sm text-muted-foreground",children:[n.jsx(A,{name:"loader-4",className:"size-4 animate-spin"}),u("diffView.state.loadingRepositoryStatus")]});if(c!=="turn"&&H===!1)return n.jsx("div",{className:"flex flex-1 items-center justify-center text-sm text-muted-foreground",children:u("diffView.state.notGitRepository")});if(c==="branch"){if(!nn)return n.jsxs("div",{className:"flex flex-1 items-center justify-center gap-2 text-sm text-muted-foreground",children:[n.jsx(A,{name:"loader-4",className:"size-4 animate-spin"}),u("diffView.branch.resolvingBase")]});if(!ae){const t=(ke?.all??[]).map(l=>l.replace(/^remotes\//,"")).filter(l=>l!==R&&!l.endsWith(`/${R}`)).sort(),r=_n(t,gt,l=>[l]);return n.jsxs("div",{className:"flex flex-1 flex-col items-center justify-center gap-3 px-6 text-center",children:[n.jsx(A,{name:"git-branch",className:"size-6 text-muted-foreground"}),n.jsx("div",{className:"typography-ui-label font-semibold text-foreground",children:u("diffView.branch.noBaseTitle")}),n.jsx("div",{className:"max-w-sm typography-micro text-muted-foreground",children:u("diffView.branch.noBaseDescription")}),n.jsx("input",{type:"text",value:gt,onChange:l=>sn(l.target.value),placeholder:u("gitView.branch.searchPlaceholder"),"aria-label":u("gitView.branch.searchPlaceholder"),className:"w-full max-w-sm rounded-md border border-border/60 bg-[var(--surface-elevated)] px-2.5 py-1.5 typography-meta text-foreground outline-none placeholder:text-muted-foreground focus-visible:ring-2 focus-visible:ring-[var(--interactive-focus-ring)]"}),n.jsx(at,{outerClassName:"max-h-48 w-full max-w-sm min-h-0",className:"px-1 py-1",children:r.length===0?n.jsx("div",{className:"px-2 py-3 typography-meta text-muted-foreground",children:u("gitView.branch.empty")}):n.jsx("div",{className:"flex flex-col gap-0.5",children:r.map(l=>n.jsxs("button",{type:"button",onClick:()=>m&&R&&Zt(m,R,l),className:"flex items-center gap-2 rounded-md px-2 py-1.5 text-left hover:bg-interactive-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--interactive-focus-ring)]",children:[n.jsx(A,{name:"git-branch",className:"size-3.5 text-primary"}),n.jsx("span",{className:"truncate typography-ui-label text-foreground",title:l,children:l})]},l))})})]})}if(bt)return n.jsxs("div",{className:"flex flex-1 flex-col items-center justify-center gap-3 px-6 text-center",children:[n.jsx("div",{className:"typography-ui-label font-semibold text-foreground",children:u("diffView.branch.loadError")}),n.jsx("div",{className:"max-w-sm typography-micro text-muted-foreground",children:bt}),n.jsx(pe,{variant:"outline",size:"sm",onClick:()=>nt(),children:u("diffView.actions.retry")})]});if(Ie===null)return n.jsxs("div",{className:"flex flex-1 items-center justify-center gap-2 text-sm text-muted-foreground",children:[n.jsx(A,{name:"loader-4",className:"size-4 animate-spin"}),u("diffView.branch.loadingFiles")]})}return S.length===0?n.jsx("div",{className:"flex flex-1 items-center justify-center text-sm text-muted-foreground",children:c==="turn"?u("diffView.state.noLastTurnChanges"):c==="branch"&&ae?u("diffView.branch.empty",{base:ae}):u("diffView.state.cleanWorkingTree")}):xn()};return n.jsxs("div",{className:"flex h-full flex-col overflow-hidden bg-background",children:[n.jsxs("div",{className:"@container/diff-toolbar flex min-w-0 items-center gap-2 px-3 py-2 bg-background",children:[y===!1&&Array.isArray(P)&&P.length>0?n.jsx(Kn,{repositories:P,selectedRepository:j??null,onSelectRepository:t=>{w&&G(w,t)},repositoryRoot:w??void 0}):null,!L&&(c==="working"||c==="staged"||c==="turn"||c==="branch"?n.jsx(js,{scope:c,workingCount:Ht,stagedCount:Ut,turnCount:qt,branchCount:ln,showBranchOption:Ze,onScopeChange:t=>{C(t),f?.(t)}}):n.jsx("div",{className:"flex items-center gap-1 rounded-md px-2 py-1 text-muted-foreground shrink-0",children:n.jsx("span",{className:"typography-ui-label font-semibold text-foreground",children:ee&&!N?u("diffView.state.loadingChanges"):S.length===1?u("diffView.summary.changedFilesSingle",{count:S.length}):u("diffView.summary.changedFilesPlural",{count:S.length})})})),S.length>0&&n.jsxs(pe,{variant:"ghost",size:"sm",onClick:fn,className:ue("diff-toolbar__expand-button h-7 flex-shrink-0 gap-1 px-1.5 text-muted-foreground hover:text-foreground","ml-auto"),title:B.size>0?u("diffView.actions.collapseAll"):u("diffView.actions.expandAll"),children:[n.jsx(A,{name:"expand-up-down",className:"size-4"}),n.jsx("span",{className:"diff-toolbar__expand-label typography-ui-label",children:B.size>0?u("diffView.actions.collapseAll"):u("diffView.actions.expandAll")})]}),S.length>0&&Gt&&n.jsxs(pe,{variant:"default",size:"sm",onClick:()=>be(!0),disabled:we,className:"diff-toolbar__review-button h-7 flex-shrink-0 gap-1.5 px-2","aria-label":u("diffView.actions.reviewAria"),children:[we?n.jsx(A,{name:"loader-4",className:"size-4 animate-spin"}):n.jsx(A,{name:"search-eye",className:"size-4"}),n.jsx("span",{className:"diff-toolbar__review-label typography-ui-label",children:u("diffView.actions.review")})]}),S.length>0&&Kt&&n.jsxs(pe,{variant:"outline",size:"sm",onClick:()=>{const t=m??"";k(t,{kind:"working-tree",scope:c==="staged"||c==="working"?c:"all"}),le(t,"walkthrough")},className:ue("diff-toolbar__walkthrough-button h-7 flex-shrink-0 gap-1.5 px-2",Jn),"aria-label":u("walkthrough.action.open"),children:[n.jsx(A,{name:"route",className:"size-4"}),n.jsx("span",{className:"diff-toolbar__walkthrough-label typography-ui-label",children:u("walkthrough.action.open")})]}),S.length>0&&n.jsxs(Bn,{children:[n.jsx(Vn,{asChild:!0,children:n.jsx(pe,{variant:"ghost",size:"sm",onClick:()=>xe(t=>!t),"aria-pressed":fe,"aria-label":u(fe?"diffView.actions.disableFullFiles":"diffView.actions.loadFullFiles"),className:ue("h-7 w-7 flex-shrink-0 p-0 text-muted-foreground hover:text-foreground",fe&&"bg-interactive-selection text-interactive-selection-foreground"),children:n.jsx(A,{name:"file-download",className:"size-4"})})}),n.jsx(Ln,{children:n.jsx("p",{children:u(fe?"diffView.actions.disableFullFiles":"diffView.actions.loadFullFiles")})})]}),S.length>0&&n.jsx(pe,{variant:"ghost",size:"sm",onClick:()=>Pt(!ut),className:ue("h-5 w-5 p-0 transition-opacity",qe?"text-foreground opacity-100":"text-muted-foreground opacity-60 hover:opacity-100"),title:u(qe?"diffView.actions.disableLineWrap":"diffView.actions.enableLineWrap"),children:n.jsx(A,{name:"text-wrap",className:"size-4"})}),jt&&n.jsx(Mt,{mode:jt==="side-by-side"?"side-by-side":"unified",onModeChange:mn})]}),n.jsx(Tn,{open:Le,onOpenChange:be,projectDirectory:m??null,submitting:we,onConfirm:dn}),bn()]})};export{Ki as DiffView};
|