@vantran-se/openchamber-web 2.0.3 → 2.0.5

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.
Files changed (265) hide show
  1. package/README.md +20 -28
  2. package/bin/cli.js +3 -1
  3. package/bin/cli.test.js +13 -4
  4. package/bin/lib/DOCUMENTATION.md +4 -1
  5. package/bin/lib/commands-serve.js +9 -0
  6. package/dist/assets/{CodeMirrorCommentWidgets-CWhdh0wf.js → CodeMirrorCommentWidgets-DCAQTLMM.js} +1 -1
  7. package/dist/assets/DiffView-uzNpHBlV.js +4 -0
  8. package/dist/assets/FilesView-Bkq0sjNx.js +17 -0
  9. package/dist/assets/{GitView-DKIjpnu4.js → GitView-BmibZC7n.js} +4 -4
  10. package/dist/assets/{GuestAttachDialog-BIa9jCfL.js → GuestAttachDialog-D7TJG1rt.js} +1 -1
  11. package/dist/assets/GuestSurfacePane-PAcYaFzP.js +1 -0
  12. package/dist/assets/{JsonTreeView-CtI1Mqwo.js → JsonTreeView-BNT2yUVu.js} +1 -1
  13. package/dist/assets/{LinearIssuesView-BD2zLu-r.js → LinearIssuesView-D3Azksju.js} +1 -1
  14. package/dist/assets/MarkdownImageGallery-BGY6tuiK.js +1 -0
  15. package/dist/assets/MarkdownRendererImpl-DJmhQ37f.js +12 -0
  16. package/dist/assets/{OnboardingScreen-B8TxloXZ.js → OnboardingScreen-D_41H2qC.js} +1 -1
  17. package/dist/assets/{PatchDiff--Civs1xY.js → PatchDiff-C0RSqa9W.js} +1 -1
  18. package/dist/assets/{PierreDiffViewer-BlDmw3-A.js → PierreDiffViewer-BBE7tTY2.js} +3 -3
  19. package/dist/assets/{PlanView-DyH2SyCP.js → PlanView-Bqc-xuDe.js} +4 -4
  20. package/dist/assets/PluginPane-DQ0k41Q-.js +3 -0
  21. package/dist/assets/SettingsView-BAiJU5hJ.js +30 -0
  22. package/dist/assets/{SettingsWindow-y9WpZqK6.js → SettingsWindow-DuC4ecLw.js} +1 -1
  23. package/dist/assets/{SubmoduleDiffSummary-DeFmhPFZ.js → SubmoduleDiffSummary-0HgmcMXz.js} +1 -1
  24. package/dist/assets/{ToolOutputDialog-8DmF7_PZ.js → ToolOutputDialog-DdHkQJEE.js} +6 -6
  25. package/dist/assets/{ToolPartDiffPreview-Ca15DKE1.js → ToolPartDiffPreview-DWdN0r5K.js} +2 -2
  26. package/dist/assets/{VirtualizedFileDiff-BfEBad09.js → VirtualizedFileDiff-ClDezf2d.js} +1 -1
  27. package/dist/assets/{WalkthroughView-Cj3K8Xz6.js → WalkthroughView-BJicp0RC.js} +2 -2
  28. package/dist/assets/{appThemeRegistry-DAdawJZn.js → appThemeRegistry-CiuyzfSW.js} +1 -1
  29. package/dist/assets/changesTree-CePHbFcj.js +1 -0
  30. package/dist/assets/commitAndPush-DTKb6IwT.js +1 -0
  31. package/dist/assets/{computeVirtualFileMetrics-RF-KjfUH.js → computeVirtualFileMetrics-kM_zJNMt.js} +2 -2
  32. package/dist/assets/de-BMnfXm-G.js +10 -0
  33. package/dist/assets/es-DKEvCCL6.js +16 -0
  34. package/dist/assets/fr-D9fgE2xE.js +14 -0
  35. package/dist/assets/index-DUke1nme.css +1 -0
  36. package/dist/assets/{index-CL7ZQ94-.js → index-DWB8bR_g.js} +1 -1
  37. package/dist/assets/index-Di6zUe5h.js +41 -0
  38. package/dist/assets/{isDiffPlainText-D2yJ9fO6.js → isDiffPlainText-DOoIsGqs.js} +1 -1
  39. package/dist/assets/{isFilePlainText-BwMEq1Ey.js → isFilePlainText-bt5VHvcd.js} +1 -1
  40. package/dist/assets/ja-BEXad-KW.js +16 -0
  41. package/dist/assets/ko-DGrQ0lEe.js +16 -0
  42. package/dist/assets/{main-quLSA7jc.js → main-BpLQbZC2.js} +2 -2
  43. package/dist/assets/main-CI9DtHJv.js +896 -0
  44. package/dist/assets/{markdown-shiki.worker-CahLvHIq.js → markdown-shiki.worker-Bg8SoAJJ.js} +8 -5
  45. package/dist/assets/markdownCore-DK2KK5RW.js +7 -0
  46. package/dist/assets/{miniChat-D-lJJibr.js → miniChat-CIWaR8wG.js} +2 -2
  47. package/dist/assets/{mobile-VTtphbW6.js → mobile-CnWKEYIP.js} +2 -2
  48. package/dist/assets/patchFileDiff-Cqn0C0az.js +13 -0
  49. package/dist/assets/pl-BlhkmR5b.js +16 -0
  50. package/dist/assets/pt-BR-Bs96Ew9I.js +16 -0
  51. package/dist/assets/{renderElectronMiniChatApp-DF7ch5eM.js → renderElectronMiniChatApp-D9t0-9nV.js} +2 -2
  52. package/dist/assets/renderMobileApp-CW7fVs_s.js +25 -0
  53. package/dist/assets/runtimeEndpointReset-D0PcCb3K.js +27 -0
  54. package/dist/assets/scheduledTasksApi-Dahv3ws0.js +1 -0
  55. package/dist/assets/{shikiHighlight-_HeCCHx1.js → shikiHighlight-l-MbaNS4.js} +1 -1
  56. package/dist/assets/{textMateThemeFromAppTheme-C95XdFGj.js → textMateThemeFromAppTheme-C-kGcOpD.js} +1 -1
  57. package/dist/assets/tr-CehUK1M9.js +7 -0
  58. package/dist/assets/uk-Bno5WGNz.js +16 -0
  59. package/dist/assets/{useAppFontEffects-ZM9ij19-.js → useAppFontEffects-DDjuwVqi.js} +160 -156
  60. package/dist/assets/{useInlineCommentController-CkfxMJPy.js → useInlineCommentController-Clg8EO3A.js} +2 -2
  61. package/dist/assets/{useRootScrollLock-BtiAC1fj.js → useRootScrollLock-nDcRrgJ1.js} +2 -2
  62. package/dist/assets/{vendor-codemirror-language-data-U-lXzXNK.js → vendor-codemirror-language-data-oKF0MAwd.js} +2 -2
  63. package/dist/assets/vendor-opencode-client-DSZl7jSi.js +12 -0
  64. package/dist/assets/vendor-zod-BSoePkpP.js +39 -0
  65. package/dist/assets/{web-components-C583Pmfm.js → web-components-Bc1WPd4i.js} +1 -1
  66. package/dist/assets/zh-CN-DnAweIWo.js +16 -0
  67. package/dist/assets/zh-TW-BbBBK25p.js +16 -0
  68. package/dist/index.html +5 -5
  69. package/dist/mini-chat.html +5 -5
  70. package/dist/mobile.html +5 -5
  71. package/package.json +3 -3
  72. package/server/index.d.ts +2 -0
  73. package/server/index.js +196 -52
  74. package/server/lib/agent-tool/DOCUMENTATION.md +5 -0
  75. package/server/lib/agent-tool/runtime.js +89 -51
  76. package/server/lib/agent-tool/runtime.test.js +17 -0
  77. package/server/lib/browser-control/DOCUMENTATION.md +14 -2
  78. package/server/lib/event-stream/DOCUMENTATION.md +2 -2
  79. package/server/lib/event-stream/global-hub.js +45 -7
  80. package/server/lib/event-stream/global-hub.test.js +128 -2
  81. package/server/lib/event-stream/global-ws-bridge.js +3 -1
  82. package/server/lib/event-stream/rebind.test.js +1 -0
  83. package/server/lib/event-stream/runtime.js +1 -2
  84. package/server/lib/event-stream/upstream-reader.js +3 -2
  85. package/server/lib/git/DOCUMENTATION.md +1 -1
  86. package/server/lib/git/service.js +22 -0
  87. package/server/lib/git/service.test.js +8 -0
  88. package/server/lib/guests/DOCUMENTATION.md +4 -3
  89. package/server/lib/guests/catalog.js +19 -1
  90. package/server/lib/guests/catalog.test.js +22 -0
  91. package/server/lib/guests/html-styles.js +5 -3
  92. package/server/lib/guests/html-styles.test.js +3 -1
  93. package/server/lib/guests/routes.js +11 -3
  94. package/server/lib/guests/service.js +5 -0
  95. package/server/lib/guests/surface.js +55 -9
  96. package/server/lib/guests/surface.test.js +56 -6
  97. package/server/lib/notifications/DOCUMENTATION.md +10 -0
  98. package/server/lib/notifications/emit-route.js +118 -0
  99. package/server/lib/notifications/emit-route.test.js +112 -0
  100. package/server/lib/openchamber-control/actions.js +16 -2
  101. package/server/lib/openchamber-control/service.js +23 -0
  102. package/server/lib/openchamber-control/service.test.js +31 -0
  103. package/server/lib/openchamber-sessions/fork-inheritance.js +103 -0
  104. package/server/lib/openchamber-sessions/fork-inheritance.test.js +69 -0
  105. package/server/lib/openchamber-sessions/routes.js +11 -0
  106. package/server/lib/openchamber-sessions/routes.test.js +18 -0
  107. package/server/lib/opencode/DOCUMENTATION.md +23 -23
  108. package/server/lib/opencode/bootstrap-runtime.js +14 -1
  109. package/server/lib/opencode/compatibility.js +6 -3
  110. package/server/lib/opencode/compatibility.test.js +8 -0
  111. package/server/lib/opencode/config-entity-v2.test.js +27 -0
  112. package/server/lib/opencode/config-paths.test.js +12 -0
  113. package/server/lib/opencode/config-v2.d.ts +2 -0
  114. package/server/lib/opencode/config-v2.js +42 -0
  115. package/server/lib/opencode/config-v2.test.js +17 -0
  116. package/server/lib/opencode/core-routes.js +20 -4
  117. package/server/lib/opencode/core-routes.test.js +41 -0
  118. package/server/lib/opencode/env-runtime.js +1 -8
  119. package/server/lib/opencode/feature-routes-runtime.js +2 -1
  120. package/server/lib/opencode/lifecycle.js +156 -2
  121. package/server/lib/opencode/lifecycle.test.js +255 -1
  122. package/server/lib/opencode/managed-config-file.js +18 -6
  123. package/server/lib/opencode/managed-config-file.test.js +23 -7
  124. package/server/lib/opencode/mcp.js +7 -2
  125. package/server/lib/opencode/network-runtime.js +16 -7
  126. package/server/lib/opencode/network-runtime.test.js +29 -1
  127. package/server/lib/opencode/npm-registry-config.d.ts +1 -0
  128. package/server/lib/opencode/npm-registry-config.js +123 -0
  129. package/server/lib/opencode/npm-registry-config.test.js +167 -0
  130. package/server/lib/opencode/npm-registry.js +5 -6
  131. package/server/lib/opencode/npm-registry.test.js +30 -0
  132. package/server/lib/opencode/openchamber-routes.js +29 -11
  133. package/server/lib/opencode/openchamber-routes.test.js +18 -0
  134. package/server/lib/opencode/project-directory-runtime.js +8 -0
  135. package/server/lib/opencode/project-directory-runtime.test.js +13 -0
  136. package/server/lib/opencode/provider-source-route.test.js +59 -0
  137. package/server/lib/opencode/providers.js +3 -0
  138. package/server/lib/opencode/providers.test.js +62 -0
  139. package/server/lib/opencode/proxy.js +53 -16
  140. package/server/lib/opencode/routes-upgrade.test.js +1 -1
  141. package/server/lib/opencode/routes.js +36 -1
  142. package/server/lib/opencode/server-startup-runtime.js +25 -0
  143. package/server/lib/opencode/server-startup-runtime.test.js +67 -1
  144. package/server/lib/opencode/server-utils-runtime.js +5 -0
  145. package/server/lib/opencode/settings-helpers.js +10 -0
  146. package/server/lib/opencode/settings-helpers.test.js +2 -2
  147. package/server/lib/opencode/settings-registry.json +17 -0
  148. package/server/lib/opencode/settings-runtime.js +1 -0
  149. package/server/lib/opencode/shared-service-runtime.js +155 -0
  150. package/server/lib/opencode/shared-service-runtime.test.js +180 -0
  151. package/server/lib/opencode/shared.js +49 -4
  152. package/server/lib/opencode/shared.test.js +78 -1
  153. package/server/lib/opencode/shutdown-runtime.js +14 -5
  154. package/server/lib/opencode/shutdown-runtime.test.js +77 -2
  155. package/server/lib/opencode/skill-routes.js +28 -12
  156. package/server/lib/opencode/skill-routes.test.js +121 -1
  157. package/server/lib/opencode/startup-pipeline-runtime.js +5 -4
  158. package/server/lib/opencode/startup-pipeline-runtime.test.js +18 -7
  159. package/server/lib/opencode/upgrade-capability.js +0 -14
  160. package/server/lib/opencode/v1-migration-topup.js +21 -15
  161. package/server/lib/opencode/v1-migration-topup.test.js +14 -6
  162. package/server/lib/opencode/v2-install.d.ts +1 -0
  163. package/server/lib/opencode/v2-install.js +8 -7
  164. package/server/lib/opencode/v2-install.test.js +13 -4
  165. package/server/lib/opencode/watcher.js +3 -1
  166. package/server/lib/opencode/watcher.test.js +54 -0
  167. package/server/lib/opencode/websearch-config.js +13 -1
  168. package/server/lib/quota/providers/kimi.js +1 -1
  169. package/server/lib/realtime-proxy.js +5 -1
  170. package/server/lib/realtime-proxy.test.js +29 -0
  171. package/server/lib/relay/tunnel-host.js +4 -1
  172. package/server/lib/relay/tunnel-host.test.js +11 -0
  173. package/server/lib/session-assist/DOCUMENTATION.md +17 -5
  174. package/server/lib/session-assist/runtime.js +19 -2
  175. package/server/lib/session-assist/runtime.test.js +8 -0
  176. package/server/lib/session-goal/DOCUMENTATION.md +11 -0
  177. package/server/lib/small-model/DOCUMENTATION.md +11 -10
  178. package/server/lib/small-model/index.js +27 -32
  179. package/server/lib/small-model/index.test.js +28 -16
  180. package/server/lib/spaces/DOCUMENTATION.md +404 -32
  181. package/server/lib/spaces/code-in-bait.js +145 -6
  182. package/server/lib/spaces/code-in.js +10 -150
  183. package/server/lib/spaces/code-in.test.js +7 -83
  184. package/server/lib/spaces/code-out.js +1819 -0
  185. package/server/lib/spaces/code-out.test.js +3945 -0
  186. package/server/lib/spaces/code-transfer.js +173 -0
  187. package/server/lib/spaces/command-stream.test.js +181 -0
  188. package/server/lib/spaces/dispatcher.js +670 -0
  189. package/server/lib/spaces/dispatcher.test.js +582 -0
  190. package/server/lib/spaces/gatekeeper-channel.js +61 -7
  191. package/server/lib/spaces/gatekeeper-channel.test.js +43 -7
  192. package/server/lib/spaces/gatekeeper-program.cjs +68 -14
  193. package/server/lib/spaces/gatekeeper-program.test.js +118 -3
  194. package/server/lib/spaces/hardening.js +20 -4
  195. package/server/lib/spaces/hardening.test.js +18 -3
  196. package/server/lib/spaces/host-git.js +7 -4
  197. package/server/lib/spaces/host-git.test.js +6 -2
  198. package/server/lib/spaces/host.js +310 -0
  199. package/server/lib/spaces/host.test.js +273 -0
  200. package/server/lib/spaces/journey.js +552 -0
  201. package/server/lib/spaces/journey.test.js +589 -0
  202. package/server/lib/spaces/layout.js +42 -8
  203. package/server/lib/spaces/manager.js +4 -3
  204. package/server/lib/spaces/manager.test.js +6 -0
  205. package/server/lib/spaces/places/code-out.docker.live.test.js +412 -0
  206. package/server/lib/spaces/places/contract-suite.js +36 -0
  207. package/server/lib/spaces/places/dispatcher.docker.live.test.js +296 -0
  208. package/server/lib/spaces/places/docker-live-support.js +27 -4
  209. package/server/lib/spaces/places/docker.js +33 -5
  210. package/server/lib/spaces/places/docker.test.js +42 -3
  211. package/server/lib/spaces/places/escape-suite.js +118 -14
  212. package/server/lib/spaces/places/fake-docker.js +3 -1
  213. package/server/lib/spaces/places/grants.docker.live.test.js +197 -0
  214. package/server/lib/spaces/places/memory-place.js +24 -1
  215. package/server/lib/spaces/places/registry.js +1 -1
  216. package/server/lib/spaces/places/server.docker.live.test.js +69 -33
  217. package/server/lib/spaces/places/sessions.docker.live.test.js +222 -0
  218. package/server/lib/spaces/routes.js +216 -0
  219. package/server/lib/spaces/routes.test.js +213 -0
  220. package/server/lib/spaces/run-command.js +261 -8
  221. package/server/lib/spaces/run-command.test.js +193 -3
  222. package/server/lib/spaces/space-events.js +153 -0
  223. package/server/lib/spaces/space-events.test.js +160 -0
  224. package/server/lib/spaces/space-opencode.js +52 -0
  225. package/server/lib/spaces/space-opencode.test.js +41 -0
  226. package/server/lib/spaces/space-records.js +150 -0
  227. package/server/lib/spaces/space-records.test.js +113 -0
  228. package/server/lib/spaces/space-sessions.js +210 -0
  229. package/server/lib/spaces/space-sessions.test.js +161 -0
  230. package/server/lib/spaces/websocket.js +329 -0
  231. package/server/lib/spaces/websocket.test.js +345 -0
  232. package/server/lib/ui-auth/ui-auth.js +8 -2
  233. package/server/lib/ui-auth/ui-auth.test.js +29 -0
  234. package/server/opencode-proxy.test.js +271 -0
  235. package/server/proxy-headers.js +8 -2
  236. package/server/proxy-headers.test.js +20 -0
  237. package/dist/assets/DiffView-CX3wAX2S.js +0 -4
  238. package/dist/assets/FilesView-OL3CON3r.js +0 -17
  239. package/dist/assets/GuestSurfacePane-B_cqX5I5.js +0 -1
  240. package/dist/assets/MarkdownImageGallery-DFiTFZgj.js +0 -1
  241. package/dist/assets/MarkdownRendererImpl-DZm5sf1W.js +0 -12
  242. package/dist/assets/PluginPane-BibJit86.js +0 -3
  243. package/dist/assets/SettingsView-h-VpVO47.js +0 -29
  244. package/dist/assets/commitAndPush-BIPfOMXY.js +0 -1
  245. package/dist/assets/de-BqCNGROu.js +0 -10
  246. package/dist/assets/es-BTPpTRgN.js +0 -16
  247. package/dist/assets/fr-hxbzMdAg.js +0 -14
  248. package/dist/assets/index-CLcM8HKx.css +0 -1
  249. package/dist/assets/index-CvLgnuNI.js +0 -41
  250. package/dist/assets/ja-_zLwwYiy.js +0 -16
  251. package/dist/assets/ko-CFn3Dzhc.js +0 -16
  252. package/dist/assets/main-BLJaug9I.js +0 -896
  253. package/dist/assets/markdownCore-DS9FdwrI.js +0 -5
  254. package/dist/assets/patchFileDiff-D9iBcTEi.js +0 -13
  255. package/dist/assets/pl-DFHE7xac.js +0 -16
  256. package/dist/assets/pt-BR-CUjZF179.js +0 -16
  257. package/dist/assets/renderMobileApp-CNQhdXsi.js +0 -25
  258. package/dist/assets/runtimeEndpointReset-DeaERc0M.js +0 -27
  259. package/dist/assets/scheduledTasksApi-BD6Vx9pu.js +0 -1
  260. package/dist/assets/tr-BB6sA_zH.js +0 -7
  261. package/dist/assets/uk-CB4o8g-o.js +0 -16
  262. package/dist/assets/vendor-opencode-client-_BAHQo4R.js +0 -12
  263. package/dist/assets/vendor-zod-Gea9uBlY.js +0 -39
  264. package/dist/assets/zh-CN-BRyDuWhc.js +0 -16
  265. package/dist/assets/zh-TW-BDGbWA1Q.js +0 -16
@@ -3,6 +3,8 @@ import { EventEmitter } from 'node:events';
3
3
  import express from 'express';
4
4
  import path from 'path';
5
5
 
6
+ import { createOpenCodeConnectionAdapter } from './lib/opencode/lifecycle.js';
7
+ import { createOpenCodeNetworkRuntime } from './lib/opencode/network-runtime.js';
6
8
  import { createSseBoundaryTracker, registerOpenCodeProxy, writeSseChunkWithBackpressure } from './lib/opencode/proxy.js';
7
9
 
8
10
  const listen = (app, host = '127.0.0.1') => new Promise((resolve, reject) => {
@@ -26,6 +28,7 @@ const closeServer = (server) => new Promise((resolve, reject) => {
26
28
 
27
29
  describe('OpenCode proxy SSE forwarding', () => {
28
30
  let upstreamServer;
31
+ let replacementUpstreamServer;
29
32
  let proxyServer;
30
33
  const originalPlatform = Object.getOwnPropertyDescriptor(process, 'platform');
31
34
 
@@ -33,8 +36,10 @@ describe('OpenCode proxy SSE forwarding', () => {
33
36
  Object.defineProperty(process, 'platform', originalPlatform);
34
37
  await closeServer(proxyServer);
35
38
  await closeServer(upstreamServer);
39
+ await closeServer(replacementUpstreamServer);
36
40
  proxyServer = undefined;
37
41
  upstreamServer = undefined;
42
+ replacementUpstreamServer = undefined;
38
43
  });
39
44
 
40
45
  it('forwards event streams with nginx-safe headers', async () => {
@@ -262,6 +267,159 @@ describe('OpenCode proxy SSE forwarding', () => {
262
267
  expect(await response.json()).toEqual({ ok: true, source: 'external-host' });
263
268
  });
264
269
 
270
+ it('does not forward browser authorization when OpenCode has no auth', async () => {
271
+ let seenAuthorization = 'not-requested';
272
+ const upstream = express();
273
+ upstream.get('/api/config/providers', (req, res) => {
274
+ seenAuthorization = req.headers.authorization;
275
+ res.json({ ok: true });
276
+ });
277
+ upstreamServer = await listen(upstream);
278
+ const upstreamPort = upstreamServer.address().port;
279
+
280
+ const app = express();
281
+ registerOpenCodeProxy(app, {
282
+ fs: {},
283
+ os: {},
284
+ path,
285
+ OPEN_CODE_READY_GRACE_MS: 0,
286
+ getRuntime: () => ({
287
+ openCodePort: upstreamPort,
288
+ isOpenCodeReady: true,
289
+ openCodeNotReadySince: 0,
290
+ isRestartingOpenCode: false,
291
+ }),
292
+ getOpenCodeAuthHeaders: () => ({}),
293
+ buildOpenCodeUrl: (requestPath) => `http://127.0.0.1:${upstreamPort}${requestPath}`,
294
+ ensureOpenCodeApiPrefix: () => {},
295
+ });
296
+ proxyServer = await listen(app);
297
+
298
+ const response = await fetch(`http://127.0.0.1:${proxyServer.address().port}/api/config/providers`, {
299
+ headers: { Authorization: 'Bearer browser-secret' },
300
+ });
301
+
302
+ expect(response.status).toBe(200);
303
+ expect(seenAuthorization).toBeUndefined();
304
+ });
305
+
306
+ it('lets mixed-case service headers override browser headers', async () => {
307
+ let seenServiceHeader;
308
+ const upstream = express();
309
+ upstream.get('/api/config/providers', (req, res) => {
310
+ seenServiceHeader = req.headers['x-service-header'];
311
+ res.json({ ok: true });
312
+ });
313
+ upstreamServer = await listen(upstream);
314
+ const upstreamPort = upstreamServer.address().port;
315
+
316
+ const app = express();
317
+ registerOpenCodeProxy(app, {
318
+ fs: {},
319
+ os: {},
320
+ path,
321
+ OPEN_CODE_READY_GRACE_MS: 0,
322
+ getRuntime: () => ({
323
+ openCodePort: upstreamPort,
324
+ isOpenCodeReady: true,
325
+ openCodeNotReadySince: 0,
326
+ isRestartingOpenCode: false,
327
+ }),
328
+ getOpenCodeAuthHeaders: () => ({ 'X-Service-Header': 'trusted-value' }),
329
+ buildOpenCodeUrl: (requestPath) => `http://127.0.0.1:${upstreamPort}${requestPath}`,
330
+ ensureOpenCodeApiPrefix: () => {},
331
+ });
332
+ proxyServer = await listen(app);
333
+
334
+ const response = await fetch(`http://127.0.0.1:${proxyServer.address().port}/api/config/providers`, {
335
+ headers: { 'x-service-header': 'browser-value' },
336
+ });
337
+
338
+ expect(response.status).toBe(200);
339
+ expect(seenServiceHeader).toBe('trusted-value');
340
+ });
341
+
342
+ it('routes JSON and global events through rotated shared-service URL and auth', async () => {
343
+ const credentials = ['Basic first-secret', 'Basic replacement-secret'];
344
+ const received = [[], []];
345
+ const upstreamServers = await Promise.all(credentials.map(async (credential, index) => {
346
+ const upstream = express();
347
+ upstream.get('/api/config/providers', (req, res) => {
348
+ received[index].push({
349
+ path: req.path,
350
+ authorization: req.headers.authorization,
351
+ serviceHeader: req.headers['x-service-header'],
352
+ });
353
+ res.json({ source: index });
354
+ });
355
+ upstream.get('/api/event', (req, res) => {
356
+ received[index].push({
357
+ path: req.path,
358
+ authorization: req.headers.authorization,
359
+ serviceHeader: req.headers['x-service-header'],
360
+ });
361
+ res.type('text/event-stream').send(`data: {"source":${index}}\n\n`);
362
+ });
363
+ return listen(upstream);
364
+ }));
365
+ [upstreamServer, replacementUpstreamServer] = upstreamServers;
366
+ let active = 0;
367
+ const sharedRuntime = {
368
+ getBaseUrl: () => `http://127.0.0.1:${upstreamServers[active].address().port}`,
369
+ getHeaders: () => ({ authorization: credentials[active], 'x-service-header': `service-${active}` }),
370
+ };
371
+ const connection = createOpenCodeConnectionAdapter({ kind: 'shared-local', sharedRuntime });
372
+ const network = createOpenCodeNetworkRuntime({
373
+ state: {},
374
+ getOpenCodeBaseUrl: connection.getOpenCodeBaseUrl,
375
+ getOpenCodeAuthHeaders: connection.getOpenCodeAuthHeaders,
376
+ });
377
+ const app = express();
378
+ registerOpenCodeProxy(app, {
379
+ fs: {},
380
+ os: {},
381
+ path,
382
+ OPEN_CODE_READY_GRACE_MS: 0,
383
+ getRuntime: () => ({
384
+ openCodePort: upstreamServers[active].address().port,
385
+ isOpenCodeReady: true,
386
+ openCodeNotReadySince: 0,
387
+ isRestartingOpenCode: false,
388
+ }),
389
+ getOpenCodeAuthHeaders: network.getOpenCodeAuthHeaders,
390
+ buildOpenCodeUrl: network.buildOpenCodeUrl,
391
+ ensureOpenCodeApiPrefix: () => {},
392
+ });
393
+ proxyServer = await listen(app);
394
+ const proxyBase = `http://127.0.0.1:${proxyServer.address().port}`;
395
+
396
+ const firstJson = await fetch(`${proxyBase}/api/config/providers`);
397
+ const firstBody = await firstJson.text();
398
+ expect(JSON.parse(firstBody)).toEqual({ source: 0 });
399
+ active = 1;
400
+ const secondJson = await fetch(`${proxyBase}/api/config/providers`);
401
+ const secondBody = await secondJson.text();
402
+ const eventResponse = await fetch(`${proxyBase}/api/global/event`);
403
+ const eventBody = await eventResponse.text();
404
+
405
+ expect(JSON.parse(secondBody)).toEqual({ source: 1 });
406
+ expect(eventBody).toBe('data: {"source":1}\n\n');
407
+ expect(received).toEqual([
408
+ [{ path: '/api/config/providers', authorization: credentials[0], serviceHeader: 'service-0' }],
409
+ [
410
+ { path: '/api/config/providers', authorization: credentials[1], serviceHeader: 'service-1' },
411
+ { path: '/api/event', authorization: credentials[1], serviceHeader: 'service-1' },
412
+ ],
413
+ ]);
414
+ expect(firstJson.url).toBe(`${proxyBase}/api/config/providers`);
415
+ expect(secondJson.url).toBe(`${proxyBase}/api/config/providers`);
416
+ expect(eventResponse.url).toBe(`${proxyBase}/api/global/event`);
417
+ for (const browserValue of [firstJson.url, secondJson.url, eventResponse.url, firstBody, secondBody, eventBody]) {
418
+ expect(browserValue).not.toContain('first-secret');
419
+ expect(browserValue).not.toContain('replacement-secret');
420
+ }
421
+ });
422
+
265
423
  it('replays parsed urlencoded bodies to generic API proxy requests', async () => {
266
424
  const upstream = express();
267
425
  upstream.post('/api/form', express.urlencoded({ extended: true }), (req, res) => {
@@ -647,6 +805,119 @@ describe('OpenCode proxy SSE forwarding', () => {
647
805
  expect(cleared.metadata).toEqual({});
648
806
  });
649
807
 
808
+ it('hands the first page of the session list to the spaces merge, and later pages not', async () => {
809
+ const upstream = express();
810
+ upstream.get('/api/session', (req, res) => res.json({ data: [{ id: 'h1', location: { directory: '/repo' }, title: 'host', permissions: {} }], cursor: { next: req.query.cursor ? undefined : 'p2' } }));
811
+ upstreamServer = await listen(upstream);
812
+ const upstreamPort = upstreamServer.address().port;
813
+ const merges = [];
814
+ const app = express();
815
+ registerOpenCodeProxy(app, {
816
+ fs: {}, os: {}, path, OPEN_CODE_READY_GRACE_MS: 0,
817
+ getRuntime: () => ({ openCodePort: upstreamPort, isOpenCodeReady: true, openCodeNotReadySince: 0, isRestartingOpenCode: false }),
818
+ getOpenCodeAuthHeaders: () => ({}),
819
+ buildOpenCodeUrl: (requestPath) => `http://127.0.0.1:${upstreamPort}${requestPath}`,
820
+ ensureOpenCodeApiPrefix: () => {},
821
+ mergeSpaceSessionList: async (payload) => {
822
+ merges.push(payload);
823
+ return { ...payload, data: [...payload.data, { id: 's1', location: { directory: '/spaces/a1b2c3d4e5f6/repo' } }], spaces: [{ id: 'a1b2c3d4e5f6', state: 'complete', sessions: 1 }] };
824
+ },
825
+ });
826
+ proxyServer = await listen(app);
827
+ const base = `http://127.0.0.1:${proxyServer.address().port}`;
828
+
829
+ const first = await (await fetch(`${base}/api/session?limit=50`)).json();
830
+ expect(first.data.map((item) => item.id)).toEqual(['h1', 's1']);
831
+ expect(first.spaces).toEqual([{ id: 'a1b2c3d4e5f6', state: 'complete', sessions: 1 }]);
832
+ // The merge sees the host's list already sanitized.
833
+ expect(merges[0].data[0]).not.toHaveProperty('permissions');
834
+ const second = await (await fetch(`${base}/api/session?limit=50&cursor=p2`)).json();
835
+ expect(second.data.map((item) => item.id)).toEqual(['h1']);
836
+ expect(second.spaces).toBeUndefined();
837
+ // A list scoped to one directory, as the sidebar reads per project, is the host's alone.
838
+ const scoped = await (await fetch(`${base}/api/session?limit=50&directory=${encodeURIComponent('/repo')}`)).json();
839
+ expect(scoped.data.map((item) => item.id)).toEqual(['h1']);
840
+ expect(scoped.spaces).toBeUndefined();
841
+ const scopedByHeader = await (await fetch(`${base}/api/session?limit=50`, { headers: { 'x-opencode-directory': '/repo' } })).json();
842
+ expect(scopedByHeader.spaces).toBeUndefined();
843
+ expect(merges).toHaveLength(1);
844
+ });
845
+
846
+ it('writes the events of isolated spaces into the global event stream between the upstream\'s blocks', async () => {
847
+ const upstream = express();
848
+ let upstreamRes = null;
849
+ upstream.get('/api/event', (req, res) => {
850
+ res.setHeader('Content-Type', 'text/event-stream');
851
+ res.flushHeaders();
852
+ res.write('data: {"id":"h1","type":"host"}\n\n');
853
+ upstreamRes = res;
854
+ req.on('close', () => { upstreamRes = null; });
855
+ });
856
+ upstreamServer = await listen(upstream);
857
+ const upstreamPort = upstreamServer.address().port;
858
+ const subscribers = new Set();
859
+ const spaceEventHub = {
860
+ subscribeEvent: (subscriber, options) => {
861
+ const entry = { subscriber, options };
862
+ subscribers.add(entry);
863
+ return () => subscribers.delete(entry);
864
+ },
865
+ };
866
+ const app = express();
867
+ registerOpenCodeProxy(app, {
868
+ fs: {}, os: {}, path, OPEN_CODE_READY_GRACE_MS: 0,
869
+ getRuntime: () => ({ openCodePort: upstreamPort, isOpenCodeReady: true, openCodeNotReadySince: 0, isRestartingOpenCode: false }),
870
+ getOpenCodeAuthHeaders: () => ({}),
871
+ buildOpenCodeUrl: (requestPath) => `http://127.0.0.1:${upstreamPort}${requestPath}`,
872
+ ensureOpenCodeApiPrefix: () => {},
873
+ spaceEventHub,
874
+ });
875
+ proxyServer = await listen(app);
876
+ const base = `http://127.0.0.1:${proxyServer.address().port}`;
877
+
878
+ // A stream scoped by the directory header is the host's alone: no subscription for it.
879
+ const scopedController = new AbortController();
880
+ const scoped = await fetch(`${base}/api/event`, { headers: { Accept: 'text/event-stream', 'x-opencode-directory': '/repo' }, signal: scopedController.signal });
881
+ expect(scoped.status).toBe(200);
882
+ await new Promise((resolve) => setTimeout(resolve, 50));
883
+ expect(subscribers.size).toBe(0);
884
+ scopedController.abort();
885
+
886
+ const controller = new AbortController();
887
+ const response = await fetch(`${base}/api/global/event`, { headers: { Accept: 'text/event-stream' }, signal: controller.signal });
888
+ const reader = response.body.getReader();
889
+ const decoder = new TextDecoder();
890
+ let text = '';
891
+ const readUntil = async (needle) => {
892
+ while (!text.includes(needle)) {
893
+ const { value, done } = await reader.read();
894
+ if (done) break;
895
+ text += decoder.decode(value, { stream: true });
896
+ }
897
+ };
898
+ await readUntil('"h1"');
899
+ expect(subscribers.size).toBe(1);
900
+ const [{ subscriber, options }] = subscribers;
901
+ expect(options).toEqual({ spaces: true });
902
+ // A host event of the hub is not written twice; a space event is written as one block.
903
+ subscriber({ spaceId: null, payload: { id: 'x', type: 'host-from-hub' } });
904
+ subscriber({ spaceId: 'a1b2c3d4e5f6', payload: { id: 's1', type: 'session.execution.started', location: { directory: '/spaces/a1b2c3d4e5f6/repo' } } });
905
+ await readUntil('"s1"');
906
+ expect(text).toBe('data: {"id":"h1","type":"host"}\n\ndata: {"id":"s1","type":"session.execution.started","location":{"directory":"/spaces/a1b2c3d4e5f6/repo"}}\n\n');
907
+ // Half a block of the host's holds a space event back until the block is whole.
908
+ upstreamRes.write('data: {"id":"h2",');
909
+ await readUntil('"h2"');
910
+ subscriber({ spaceId: 'a1b2c3d4e5f6', payload: { id: 's2', type: 'session.execution.succeeded' } });
911
+ await new Promise((resolve) => setTimeout(resolve, 50));
912
+ expect(text).not.toContain('"s2"');
913
+ upstreamRes.write('"type":"host"}\n\n');
914
+ await readUntil('"s2"');
915
+ expect(text.endsWith('data: {"id":"h2","type":"host"}\n\ndata: {"id":"s2","type":"session.execution.succeeded"}\n\n')).toBe(true);
916
+ controller.abort();
917
+ await new Promise((resolve) => setTimeout(resolve, 50));
918
+ expect(subscribers.size).toBe(0);
919
+ });
920
+
650
921
  it('forwards unparsed SDK JSON bodies to generic API proxy requests', async () => {
651
922
  const upstream = express();
652
923
  upstream.post('/api/session/abc/revert', express.json(), (req, res) => {
@@ -36,8 +36,14 @@ export const collectForwardProxyHeaders = (requestHeaders, authHeaders = {}) =>
36
36
  headers[normalizedKey] = Array.isArray(value) ? value.join(', ') : String(value);
37
37
  }
38
38
 
39
- if (authHeaders.Authorization) {
40
- headers.Authorization = authHeaders.Authorization;
39
+ for (const [key, value] of Object.entries(authHeaders || {})) {
40
+ if (value === undefined || value === null) continue;
41
+ const lowerKey = key.toLowerCase();
42
+ for (const existingKey of Object.keys(headers)) {
43
+ if (existingKey.toLowerCase() === lowerKey) delete headers[existingKey];
44
+ }
45
+ const normalizedKey = lowerKey === 'authorization' ? 'Authorization' : key;
46
+ headers[normalizedKey] = Array.isArray(value) ? value.join(', ') : String(value);
41
47
  }
42
48
 
43
49
  return headers;
@@ -28,6 +28,17 @@ describe('OpenCode proxy header handling', () => {
28
28
  expect(headers['authorization']).toBeUndefined();
29
29
  });
30
30
 
31
+ it('normalizes lowercase service authorization and preserves service headers', () => {
32
+ const headers = collectForwardProxyHeaders(
33
+ { authorization: 'Bearer oc_client_stale-ui-token' },
34
+ { authorization: 'Basic shared-service-token', 'x-service-header': 'service-value' },
35
+ );
36
+
37
+ expect(headers.Authorization).toBe('Basic shared-service-token');
38
+ expect(headers['authorization']).toBeUndefined();
39
+ expect(headers['x-service-header']).toBe('service-value');
40
+ });
41
+
31
42
  it('drops client authorization when upstream has no managed auth', () => {
32
43
  const headers = collectForwardProxyHeaders({
33
44
  accept: 'application/json',
@@ -39,6 +50,15 @@ describe('OpenCode proxy header handling', () => {
39
50
  expect(headers.accept).toBe('application/json');
40
51
  });
41
52
 
53
+ it('lets trusted service headers replace browser headers case-insensitively', () => {
54
+ const headers = collectForwardProxyHeaders(
55
+ { 'x-service-header': 'browser-value' },
56
+ { 'X-Service-Header': 'trusted-value' },
57
+ );
58
+
59
+ expect(headers).toEqual({ 'X-Service-Header': 'trusted-value' });
60
+ });
61
+
42
62
  it('drops content-encoding from forwarded response headers', () => {
43
63
  expect(shouldForwardProxyResponseHeader('content-encoding')).toBe(false);
44
64
  expect(shouldForwardProxyResponseHeader('Content-Encoding')).toBe(false);
@@ -1,4 +0,0 @@
1
- import{R as i,f as tt,j as n}from"./vendor-react-BUCujlKS.js";import{u as me,O as Fe,B as Ln,a as De,G as Mn}from"./index-CvLgnuNI.js";import{c as Tn,b as Bn,u as In,a as Pn,B as _n,C as On,P as Gt,h as zn,e as Kn,g as Gn,i as Hn,f as $n}from"./patchFileDiff-D9iBcTEi.js";import{c as qn,b as Wn,i as Un,u as Jn,a as Xn,d as Yn,S as Qn}from"./SubmoduleDiffSummary-DeFmhPFZ.js";import{f as qe,B as ke,I as z,h as Ae,_ as en,ai as Zn,K as ae,aj as es,bj as ts,J as ns,ak as ss,am as rs,z as Ht,k as is,d9 as tn,t as $e,kX as os,az as et,kM as nn,kY as as,aA as sn,gV as ls,ax as Ze,ay as cs,c8 as rn,at as us,av as ds,aw as fs,as as ms}from"./useAppFontEffects-ZM9ij19-.js";import{j as ps,f as hs}from"./runtimeEndpointReset-DeaERc0M.js";import{P as gs}from"./PierreDiffViewer-BlDmw3-A.js";import{u as xs,v as ys,g as ws,W as bs}from"./main-BLJaug9I.js";import{p as js}from"./VirtualizedFileDiff-BfEBad09.js";import"./vendor-scheduler-7OC5HNn7.js";import"./vendor-zod-Gea9uBlY.js";import"./vendor-vite-runtime-DD1OZmLC.js";import"./vendor-zustand-BAGL70zf.js";import"./vendor-opencode-client-_BAHQo4R.js";import"./vendor-simplewebauthn-browser-VDA66rT_.js";import"./vendor-base-ui-CsXYnR5c.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"./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-Bq3iTZ6r.js";import"./vendor-motion-dom-DcCgeG69.js";import"./vendor-motion-utils-BGh8QBgx.js";import"./vendor-tanstack-react-virtual-ClYN8CzB.js";import"./vendor-tanstack-virtual-core-BewVQryB.js";import"./contextPanelEmbeddedChat-CirXEPJq.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-U-lXzXNK.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-BuBPBC3R.js";import"./vendor-tailwind-merge-CPcsbTWB.js";import"./vendor-fuse.js-Dw8P0qyA.js";import"./GuestAttachDialog-BIa9jCfL.js";import"./PluginPane-BibJit86.js";import"./web-components-C583Pmfm.js";import"./computeVirtualFileMetrics-RF-KjfUH.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"./Virtualizer-D_a8gKmg.js";import"./useInlineCommentController-CkfxMJPy.js";import"./appThemeRegistry-DAdawJZn.js";import"./textMateThemeFromAppTheme-C95XdFGj.js";import"./vendor-katex-CHD9BdDO.js";import"./useRootScrollLock-BtiAC1fj.js";import"./scheduledTasksApi-BD6Vx9pu.js";import"./vendor-cron-parser-CjvYVigk.js";import"./vendor-luxon-BZ0puJYg.js";import"./isDiffPlainText-D2yJ9fO6.js";import"./vendor-diff-C3JhlQdm.js";const vs=i.memo(function(e){const s=tt.c(13),{index:c,staged:y,busyHunk:p,disabled:b,onAction:v}=e,{t:F}=qe();let l;s[0]!==y?(l=y?["unstage"]:["discard","stage"],s[0]=y,s[1]=l):l=s[1];const j=l;let N;s[2]===Symbol.for("react.memo_cache_sentinel")?(N={top:"var(--oc-hunk-action-offset, 0.25rem)"},s[2]=N):N=s[2];let f;s[3]!==j||s[4]!==p||s[5]!==b||s[6]!==c||s[7]!==v||s[8]!==F?(f=n.jsx("div",{className:"pointer-events-auto 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:j.map(w=>{const D=F(w==="stage"?"diffView.hunk.stageTitle":w==="unstage"?"diffView.hunk.unstageTitle":"diffView.hunk.discardTitle",{index:c+1}),_=p?.index===c&&p.action===w;return n.jsx(ke,{variant:"ghost",size:"xs",className:Ae("rounded-full bg-transparent text-muted-foreground hover:bg-transparent hover:text-foreground",w==="discard"&&"text-[var(--status-error)] hover:text-[var(--status-error)]",w==="stage"&&"text-[var(--status-success)] hover:text-[var(--status-success)]"),disabled:b||p!==null,title:D,"aria-label":D,onPointerDown:Ss,onClick:m=>{m.stopPropagation(),v(c,w)},children:n.jsx(z,{name:_?"loader-4":w==="stage"?"add":"arrow-go-back",className:Ae(w==="stage"?"size-4":"size-3.5",_&&"animate-spin")})},w)})}),s[3]=j,s[4]=p,s[5]=b,s[6]=c,s[7]=v,s[8]=F,s[9]=f):f=s[9];let V;return s[10]!==c||s[11]!==f?(V=n.jsx("div",{className:"pointer-events-none absolute right-3 z-20",style:N,"data-hunk-actions":c,children:f}),s[10]=c,s[11]=f,s[12]=V):V=s[12],V});function Ss(o){return o.stopPropagation()}const $t=(o,e)=>{if(e.length===0)return null;let s=null,c=null;for(const p of e)p.top<=o?(!s||p.top>s.top)&&(s=p):(!c||p.top<c.top)&&(c=p);const y=s??c;return y?{path:y.path,topOffset:y.top-o}:null},ks=(o,e,s,c)=>Math.min(Math.max(0,c),Math.max(0,o+s-e)),qt=o=>{if(!o)return null;const e=o.split(`
2
- `);let s=null;for(const c of e){const y=c.match(/^@@\s*-\d+(?:,\d+)?\s+\+(\d+)(?:,\d+)?\s*@@/);if(y){const p=Number.parseInt(y[1]??"",10);s=Number.isFinite(p)&&p>=1?p:null;continue}if(s!==null){if(c.startsWith(" ")){s+=1;continue}if(c.startsWith("+"))return s;if(c.startsWith("-"))return Math.max(1,s)}}return null},Ns=1100,Cs=15e3,Wt=500,Ut=300,on=1e6,Ct=3,Rs=o=>o<=6?o:o<=12?6:o<=25?4:2,Jt={status:"loading"},Fs=3,Ds=i.memo(()=>{const o=tt.c(4),{t:e}=qe();let s;o[0]!==e?(s=e("diffView.binary.unavailable"),o[0]=e,o[1]=s):s=o[1];let c;return o[2]!==s?(c=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:s})}),o[2]=s,o[3]=c):c=o[3],c}),an={"?":{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"}},As=an.M,Es=o=>{const e=o.index?.trim(),s=o.working_dir?.trim();return e&&e!=="?"?e.charAt(0):s?s.charAt(0):e?.charAt(0)||s?.charAt(0)||"M"},ln=o=>{const e=Es(o);return an[e]??As},Vs=o=>{const{index:e,working_dir:s}=o;return e==="A"||s==="A"||e==="?"||s==="?"},Xt=o=>{const e=o.index?.trim();return!!(e&&e!=="?")},Yt=o=>!!o.working_dir?.trim()||o.index==="?",Ls=(o,e)=>ls(o,e),Qt=o=>(o||"").replace(/\\/g,"/").replace(/\/+$/,""),Zt=(o,e)=>{const s=o.split(`
3
- `),c=e.split(`
4
- `),y=Math.min(s.length,c.length);for(let p=0;p<y;p+=1)if(s[p]!==c[p])return p+1;return c.length>s.length?s.length+1:s.length>c.length?Math.max(1,c.length):1},Ms=o=>o==="added"?"A":o==="deleted"?"D":"M",Rt=(o,e,s)=>Hn(e)?{original:"",modified:"",isBinary:!0,patch:e,contextMode:s}:{original:"",modified:"",patch:e,fileDiff:$n(o,e),contextMode:s},cn=(o,e,s)=>{const c=o??0,y=e??0;return!c&&!y?null:n.jsxs("span",{className:Ae("typography-meta flex items-center gap-1 text-xs whitespace-nowrap","flex-shrink-0",s?.className),children:[c?n.jsxs("span",{style:{color:"var(--status-success)"},children:["+",c]}):null,y?n.jsxs("span",{style:{color:"var(--status-error)"},children:["-",y]}):null]})},Ts=i.memo(o=>{const e=tt.c(68),{scope:s,workingCount:c,stagedCount:y,turnCount:p,branchCount:b,commitCount:v,prCount:F,showCommitOption:l,showBranchOption:j,onScopeChange:N}=o,{t:f}=qe(),[V,w]=i.useState(!1),D=s==="pr"?F??0:s==="staged"?y:s==="turn"?p:s==="branch"?b??0:s==="commit"?v??0:c;let _;e[0]!==s||e[1]!==f?(_=f(s==="pr"?"session.githubIntegration.tabs.pullRequests":s==="staged"?"diffView.scope.staged":s==="turn"?"diffView.scope.lastTurn":s==="branch"?"diffView.scope.branch":s==="commit"?"commitComparison.mode":"diffView.scope.changed"),e[0]=s,e[1]=f,e[2]=_):_=e[2];const m=_;let be;e[3]!==f?(be=f("diffView.scope.selectorAria"),e[3]=f,e[4]=be):be=e[4];let Z;e[5]!==D?(Z=n.jsxs("span",{className:"diff-toolbar__scope-count",children:[": ",D]}),e[5]=D,e[6]=Z):Z=e[6];let ee;e[7]!==m||e[8]!==Z?(ee=n.jsxs("span",{className:"whitespace-nowrap",children:[m,Z]}),e[7]=m,e[8]=Z,e[9]=ee):ee=e[9];let Ne;e[10]===Symbol.for("react.memo_cache_sentinel")?(Ne=n.jsx(z,{name:"arrow-down-s",className:"size-4 flex-shrink-0 opacity-60"}),e[10]=Ne):Ne=e[10];let q;e[11]!==be||e[12]!==ee?(q=n.jsx(us,{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":be,children:[ee,Ne]})}),e[11]=be,e[12]=ee,e[13]=q):q=e[13];let x;e[14]!==N?(x=H=>{(H==="working"||H==="staged"||H==="turn"||H==="branch"||H==="commit"||H==="pr")&&(N?.(H),w(!1))},e[14]=N,e[15]=x):x=e[15];let P;e[16]!==f?(P=f("diffView.scope.changed"),e[16]=f,e[17]=P):P=e[17];let W;e[18]!==P?(W=n.jsx("span",{children:P}),e[18]=P,e[19]=W):W=e[19];let le;e[20]!==c?(le=n.jsx("span",{className:"typography-meta text-muted-foreground",children:c}),e[20]=c,e[21]=le):le=e[21];let te;e[22]!==le||e[23]!==W?(te=n.jsx(Ze,{value:"working",children:n.jsxs("span",{className:"flex min-w-0 flex-1 items-center justify-between gap-3",children:[W,le]})}),e[22]=le,e[23]=W,e[24]=te):te=e[24];let pe;e[25]!==f?(pe=f("diffView.scope.staged"),e[25]=f,e[26]=pe):pe=e[26];let ne;e[27]!==pe?(ne=n.jsx("span",{children:pe}),e[27]=pe,e[28]=ne):ne=e[28];let he;e[29]!==y?(he=n.jsx("span",{className:"typography-meta text-muted-foreground",children:y}),e[29]=y,e[30]=he):he=e[30];let J;e[31]!==ne||e[32]!==he?(J=n.jsx(Ze,{value:"staged",children:n.jsxs("span",{className:"flex min-w-0 flex-1 items-center justify-between gap-3",children:[ne,he]})}),e[31]=ne,e[32]=he,e[33]=J):J=e[33];let I;e[34]!==f?(I=f("diffView.scope.lastTurn"),e[34]=f,e[35]=I):I=e[35];let se;e[36]!==I?(se=n.jsx("span",{children:I}),e[36]=I,e[37]=se):se=e[37];let X;e[38]!==p?(X=n.jsx("span",{className:"typography-meta text-muted-foreground",children:p}),e[38]=p,e[39]=X):X=e[39];let K;e[40]!==se||e[41]!==X?(K=n.jsx(Ze,{value:"turn",children:n.jsxs("span",{className:"flex min-w-0 flex-1 items-center justify-between gap-3",children:[se,X]})}),e[40]=se,e[41]=X,e[42]=K):K=e[42];let je;e[43]!==b||e[44]!==j||e[45]!==f?(je=j?n.jsx(Ze,{value:"branch",children:n.jsxs("span",{className:"flex min-w-0 flex-1 items-center justify-between gap-3",children:[n.jsx("span",{children:f("diffView.scope.branch")}),n.jsx("span",{className:"typography-meta text-muted-foreground",children:b??"…"})]})}):null,e[43]=b,e[44]=j,e[45]=f,e[46]=je):je=e[46];let A;e[47]!==v||e[48]!==l||e[49]!==f?(A=l&&n.jsx(Ze,{value:"commit",children:n.jsxs("span",{className:"flex min-w-0 flex-1 items-center justify-between gap-3",children:[n.jsx("span",{children:f("commitComparison.mode")}),n.jsx("span",{className:"typography-meta text-muted-foreground",children:v??"…"})]})}),e[47]=v,e[48]=l,e[49]=f,e[50]=A):A=e[50];let G;e[51]!==F||e[52]!==l||e[53]!==f?(G=l&&n.jsx(Ze,{value:"pr",children:n.jsxs("span",{className:"flex min-w-0 flex-1 items-center justify-between gap-3",children:[n.jsx("span",{children:f("session.githubIntegration.tabs.pullRequests")}),n.jsx("span",{className:"typography-meta text-muted-foreground",children:F??"…"})]})}),e[51]=F,e[52]=l,e[53]=f,e[54]=G):G=e[54];let C;e[55]!==s||e[56]!==te||e[57]!==J||e[58]!==K||e[59]!==je||e[60]!==A||e[61]!==G||e[62]!==x?(C=n.jsx(ds,{align:"start",className:"w-40",children:n.jsxs(fs,{value:s,onValueChange:x,children:[te,J,K,je,A,G]})}),e[55]=s,e[56]=te,e[57]=J,e[58]=K,e[59]=je,e[60]=A,e[61]=G,e[62]=x,e[63]=C):C=e[63];let ce;return e[64]!==V||e[65]!==C||e[66]!==q?(ce=n.jsxs(ms,{open:V,onOpenChange:w,children:[q,C]}),e[64]=V,e[65]=C,e[66]=q,e[67]=ce):ce=e[67],ce}),Bs=i.memo(o=>{const e=tt.c(11),{changedFiles:s,selectedFile:c,onSelectFile:y}=o,{t:p}=qe();if(s.length===0)return null;let b;if(e[0]!==s||e[1]!==y||e[2]!==c||e[3]!==p){let F;e[5]!==y||e[6]!==c||e[7]!==p?(F=l=>{const j=ln(l),N=c===l.path;return n.jsx("li",{children:n.jsxs("button",{type:"button",onClick:()=>y(l.path),className:Ae("flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left transition-colors",N?"bg-interactive-selection text-interactive-selection-foreground":"text-muted-foreground hover:bg-interactive-hover hover:text-foreground"),children:[n.jsx(sn,{filePath:l.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:j.color},title:p(j.descriptionKey),"aria-label":p(j.descriptionKey),children:j.code}),n.jsx("span",{className:"min-w-0 flex-1 truncate typography-meta",style:{direction:"rtl",textAlign:"left",unicodeBidi:"plaintext"},title:l.path,children:l.path}),cn(l.insertions,l.deletions)]})},l.path)},e[5]=y,e[6]=c,e[7]=p,e[8]=F):F=e[8],b=s.map(F),e[0]=s,e[1]=y,e[2]=c,e[3]=p,e[4]=b}else b=e[4];let v;return e[9]!==b?(v=n.jsx(rn,{outerClassName:"flex-1 min-h-0",className:"px-2 py-2",children:n.jsx("ul",{className:"flex flex-col gap-1",children:b})}),e[9]=b,e[10]=v):v=e[10],v}),Is=i.memo(o=>{const e=tt.c(20),{filePath:s,diff:c,renderSideBySide:y}=o,{t:p}=qe(),b=c.original.length>0,v=c.modified.length>0,F=y?"flex flex-row gap-6 items-start justify-center":"flex flex-col gap-4 items-center",l=y?"flex flex-col items-center gap-2 flex-1 min-w-0":"flex flex-col items-center gap-2";let j;e[0]===Symbol.for("react.memo_cache_sentinel")?(j={contain:"layout"},e[0]=j):j=e[0];let N;e[1]!==c.original||e[2]!==s||e[3]!==b||e[4]!==l||e[5]!==y||e[6]!==p?(N=b&&n.jsxs("div",{className:l,children:[n.jsx("span",{className:"typography-meta text-muted-foreground font-medium",children:p("diffView.image.original")}),n.jsx("img",{src:c.original,alt:p("diffView.image.originalAlt",{path:s}),className:y?"max-w-full max-h-[70vh] object-contain":"max-w-full object-contain",style:{imageRendering:"auto"}})]}),e[1]=c.original,e[2]=s,e[3]=b,e[4]=l,e[5]=y,e[6]=p,e[7]=N):N=e[7];let f;e[8]!==c.modified||e[9]!==s||e[10]!==v||e[11]!==b||e[12]!==l||e[13]!==y||e[14]!==p?(f=v&&n.jsxs("div",{className:l,children:[n.jsx("span",{className:"typography-meta text-muted-foreground font-medium",children:p(b?"diffView.image.modified":"diffView.image.new")}),n.jsx("img",{src:c.modified,alt:p("diffView.image.modifiedAlt",{path:s}),className:y?"max-w-full max-h-[70vh] object-contain":"max-w-full object-contain",style:{imageRendering:"auto"}})]}),e[8]=c.modified,e[9]=s,e[10]=v,e[11]=b,e[12]=l,e[13]=y,e[14]=p,e[15]=f):f=e[15];let V;return e[16]!==F||e[17]!==N||e[18]!==f?(V=n.jsx("div",{className:"w-full overflow-auto p-4",style:j,children:n.jsxs("div",{className:F,children:[N,f]})}),e[16]=F,e[17]=N,e[18]=f,e[19]=V):V=e[19],V}),Ps=i.memo(o=>{const e=tt.c(23),{filePath:s,diff:c,staged:y,renderSideBySide:p,wrapLines:b,hunkActions:v,onExpandContextRequest:F,pendingContextExpansion:l,contextLoading:j}=o;let N;e[0]!==s?(N=cs(s)||"text",e[0]=s,e[1]=N):N=e[1];const f=N;if(c.submodule){let D;return e[2]!==c.submodule||e[3]!==y?(D=n.jsx(Qn,{state:c.submodule,staged:y}),e[2]=c.submodule,e[3]=y,e[4]=D):D=e[4],D}if(c.isBinary){let D;return e[5]===Symbol.for("react.memo_cache_sentinel")?(D=n.jsx(Ds,{}),e[5]=D):D=e[5],D}if(et(s)){let D;return e[6]!==c||e[7]!==s||e[8]!==p?(D=n.jsx(Is,{filePath:s,diff:c,renderSideBySide:p}),e[6]=c,e[7]=s,e[8]=p,e[9]=D):D=e[9],D}let V;e[10]===Symbol.for("react.memo_cache_sentinel")?(V={contain:"layout"},e[10]=V):V=e[10];let w;return e[11]!==j||e[12]!==c.fileDiff||e[13]!==c.modified||e[14]!==c.original||e[15]!==s||e[16]!==v||e[17]!==f||e[18]!==F||e[19]!==l||e[20]!==p||e[21]!==b?(w=n.jsx("div",{className:"w-full",style:V,children:n.jsx(gs,{original:c.original,modified:c.modified,fileDiff:c.fileDiff,language:f,fileName:s,renderSideBySide:p,wrapLines:b,layout:"inline",hunkActions:v,onExpandContextRequest:F,pendingContextExpansion:l,contextLoading:j})}),e[11]=j,e[12]=c.fileDiff,e[13]=c.modified,e[14]=c.original,e[15]=s,e[16]=v,e[17]=f,e[18]=F,e[19]=l,e[20]=p,e[21]=b,e[22]=w):w=e[22],w}),_s=i.memo(({visible:o=!0,directory:e,file:s,layout:c,wrapLines:y,isSelected:p,isExpanded:b,isMounted:v,onSelect:F,onExpandedChange:l,registerSectionRef:j,showOpenInEditorAction:N=!1,isOpeningInEditor:f=!1,onOpenInEditor:V,staged:w=!1,loadFullFiles:D=!1,initialDiffData:_=null,comparisonDiff:m,onRetryComparisonDiff:be,loadFullComparisonDiff:Z,readOnlyActions:ee=!1,hunkActionsEnabled:Ne=!1,contentRevision:q=0})=>{const{t:x}=qe(),{git:P}=en(),W=ae(i.useCallback(g=>g.directories.get(e)?.diffCache.get(s.path)??null,[e,s.path])),le=ae(g=>g.setDiff),te=ae(g=>g.fetchStatus),pe=me(g=>g.setDiffFileLayout),[ne,he]=i.useState(0),[J,I]=i.useState(null),[se,X]=i.useState(!1),[K,je]=i.useState(null),A=K!==null&&K.source===m?K.result:m,G=!!e&&!_&&(!m||!!Z),C=i.useMemo(()=>A?A.status==="error"?{kind:"error",message:A.message}:null:J,[A,J]),ce=A?A.status==="loading":se,[H,lt]=i.useState(null),Be=i.useRef(!1),[Ie,nt]=i.useState(null),[ct,xt]=i.useState(!1),[st,Pe]=i.useState(null),[We,_e]=i.useState(null),[a,Ee]=i.useState(null),re=D||a!==null;i.useEffect(()=>{if(!a||!Z||!m||m.status!=="ready"||K?.source===m)return;let g=!1;return Z(s.path).then(M=>{if(!g){if(M.status==="error"){$e.error(M.message),Ee(null);return}je({source:m,result:M})}}),()=>{g=!0}},[a,s.path,K,Z,m]);const ge=i.useRef(null),Ve=i.useRef(null),Oe=i.useMemo(()=>ln(s),[s]),Ue=c==="side-by-side",ze=re?"full":"patch",ve=`${s.index}:${s.working_dir}:${s.insertions}:${s.deletions}`,ue=Ne&&!ee&&!_&&!A&&!et(s.path),Je=JSON.stringify([De(),e,s.path,w,ve,ne,q]),U=Ie?.scope===Je?Ie.patch:null,de=Ie?.scope===Je?Ie.submodule:null,ie=i.useMemo(()=>A?A.status==="ready"?A.data:null:_||(w?We:st||(W?{original:W.original,modified:W.modified,isBinary:W.isBinary,submodule:W.submodule,contextMode:"full"}:null)),[A,W,_,st,w,We]),Xe=ie?.contextMode===ze,rt=i.useCallback(g=>{Ve.current=g,j(s.path,g)},[s.path,j]),Ke=i.useCallback(g=>{l(s.path,g)},[s.path,l]),Ye=i.useCallback(()=>{F(s.path)},[s.path,F]),ut=i.useRef({fileStatusKey:ve,staged:w});i.useEffect(()=>{if(!o)return;const g=ut.current;g.fileStatusKey===ve&&g.staged===w||(ut.current={fileStatusKey:ve,staged:w},w?_e(null):Pe(null),I(null),ge.current=null)},[ve,w,o]);const[yt,wt]=i.useState(q),xe=yt===q;i.useEffect(()=>{xe||(I(null),ge.current=null)},[xe]),i.useEffect(()=>{if(!o||!b||!v||J)return;if(!e||A||_||ie&&Xe&&xe&&(!ue||U!==null)){ge.current=null,X(!1);return}const g=`${e}::${s.path}::${w?"staged":"unstaged"}::${ve}::${ze}::${ne}::${q}`;if(ge.current===g)return;ge.current=g,I(null),X(!0);let M=!1;const ye=De(),we=re?on:Ct,O=et(s.path)?P.getGitFileDiff(e,{path:s.path,staged:w}):!re&&U!==null?Promise.resolve({diff:U,submodule:de}):P.getGitDiff(e,{path:s.path,staged:w,contextLines:we}),Ge=ue&&re&&U===null?P.getGitDiff(e,{path:s.path,staged:w,contextLines:Ct}).then(E=>E.diff):Promise.resolve(U),ft=Promise.all([O,Ge]),mt=Cs;let it;const bt=new Promise((E,ot)=>{it=setTimeout(()=>ot(new Error(`Timed out after ${mt}ms`)),mt)});return Promise.race([ft,bt]).then(([E,ot])=>{if(M||ye!==De())return;const L="diff"in E&&!re?E.diff:ot;if(ue&&re&&L!==null&&/^@@\s/m.test(L)&&"diff"in E&&E.diff!==L&&!zn(E.diff,L))throw nt(null),new Error(x("diffView.hunk.unavailable"));if(ue&&L!==null&&nt({scope:Je,patch:L,submodule:E.submodule}),"diff"in E){const Y={...Rt(s.path,E.diff,ze),submodule:E.submodule};w?_e(Y):Pe(Y)}else{const Y={original:E.original??"",modified:E.modified??"",isBinary:E.isBinary,submodule:E.submodule,contextMode:"full"};w?_e(Y):le(e,s.path,Y,ye)}wt(q),X(!1)}).catch(E=>{if(!(M||ye!==De())){if(X(!1),E instanceof Mn){I({kind:"unavailable",reason:E.reason}),E.reason==="path_not_found"&&te(e,P,{force:!0,silent:!0});return}I({kind:"error",message:E instanceof Error?E.message:String(E)})}}).finally(()=>clearTimeout(it)),()=>{M=!0,clearTimeout(it),ge.current===g&&(ge.current=null)}},[U,de,q,ue,xe,Je,A,ze,ie,Xe,ne,e,te,s.path,ve,P,_,b,v,re,J,le,w,x,o]);const Le=i.useCallback(()=>{Ke(!b),Ye()},[Ke,Ye,b]),oe=i.useCallback(()=>{I(null),nt(null),Pe(null),_e(null),ge.current=null,he(g=>g+1)},[]),$=i.useCallback(async(g,M)=>{if(!e||!ue||ce||C||Be.current||H!==null)return;const ye=U?Kn(U,g):null;if(!ye){$e.error(x("diffView.hunk.unavailable"));return}if(w&&M!=="unstage"||!w&&M==="unstage")return;Be.current=!0;const we=De();lt({index:g,action:M});try{const O=M==="stage"?P.stageGitHunk:M==="unstage"?P.unstageGitHunk:P.revertGitHunk;if(!O){$e.error(x("diffView.hunk.unsupported"));return}if(await O(e,s.path,ye),we!==De())return;oe(),tn.requestGitRefresh({directory:e,paths:[s.path]}),await te(e,P)}catch(O){if(we!==De())return;oe(),$e.error(O instanceof Error&&O.message?O.message:x("diffView.hunk.unavailable"))}finally{Be.current=!1,lt(O=>O?.index===g&&O.action===M?null:O)}},[U,ue,ce,C,e,te,s.path,P,H,oe,w,x]),Se=i.useMemo(()=>ue&&U!==null?Gn(U):[],[U,ue]),Ce=i.useCallback(g=>n.jsx(vs,{index:g,staged:w,busyHunk:H,disabled:ce||!!C,onAction:$}),[C,$,H,ce,w]),dt=i.useMemo(()=>Se.length>0?{anchors:Se,render:Ce}:void 0,[Se,Ce]);return n.jsxs("div",{ref:rt,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:Le,onKeyDown:g=>{g.target===g.currentTarget&&(g.key==="Enter"||g.key===" ")&&(g.preventDefault(),Le())},className:Ae("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",p?"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:b?n.jsx(z,{name:"arrow-down-s",className:"size-4"}):n.jsx(z,{name:"arrow-right-s",className:"size-4"})}),n.jsx("span",{className:"typography-micro font-semibold leading-none w-4 text-center uppercase",style:{color:Oe.color},title:x(Oe.descriptionKey),"aria-label":x(Oe.descriptionKey),children:Oe.code}),n.jsx("span",{className:"min-w-0 flex-1 overflow-hidden typography-ui-label",title:s.path,children:n.jsxs("span",{className:"flex min-w-0 items-center gap-2",children:[n.jsx(sn,{filePath:s.path,className:"h-3.5 w-3.5 flex-shrink-0 align-middle"}),(()=>{const g=s.path.lastIndexOf("/");if(g===-1)return n.jsx("span",{className:"block min-w-0 truncate typography-ui-label text-foreground",style:{direction:"rtl",textAlign:"left",unicodeBidi:"plaintext"},children:s.path});const M=s.path.slice(0,g),ye=s.path.slice(g+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:M}),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:ye})]})]})})()]})})]}),n.jsxs("div",{className:"relative flex shrink-0 items-center justify-self-end gap-2",children:[cn(s.insertions,s.deletions),N&&V?n.jsx(ke,{variant:"ghost",size:"sm",className:"h-5 w-5 p-0 opacity-70 hover:opacity-100",title:x("diffView.actions.openFileInEditorAtChange"),onClick:g=>{g.stopPropagation(),V(s.path,ie)},disabled:f,children:f?n.jsx(z,{name:"loader-4",className:"size-3.5 animate-spin"}):n.jsx(z,{name:"edit",className:"size-3.5"})}):null,n.jsx(nn,{mode:Ue?"side-by-side":"unified",onModeChange:g=>{const M=g==="side-by-side"?"side-by-side":"inline";pe(s.path,M)},className:"opacity-70"})]})]})}),b&&n.jsxs("div",{className:"relative bg-background overflow-hidden",children:[!v&&!C?n.jsx("div",{className:"h-40 border border-border/40 bg-background/40"}):null,C?.kind==="unavailable"?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:C.reason==="nested_repository"?x("diffView.unavailable.nestedRepositoryTitle"):C.reason==="untracked_directory"?x("diffView.unavailable.untrackedDirectoryTitle"):x("diffView.unavailable.missingTitle")}),n.jsx("div",{className:"typography-meta text-muted-foreground max-w-[32rem] text-center",children:C.reason==="nested_repository"?x("diffView.unavailable.nestedRepositoryDescription"):C.reason==="untracked_directory"?x("diffView.unavailable.untrackedDirectoryDescription"):x("diffView.unavailable.missingDescription")}),C.reason==="path_not_found"?n.jsx("button",{type:"button",className:"typography-ui-label text-primary hover:underline",onClick:oe,children:x("diffView.actions.retry")}):null]}):null,C?.kind==="error"?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:x("diffView.state.failedToLoadDiff")}),n.jsx("div",{className:"typography-meta text-muted-foreground max-w-[32rem] text-center",children:C.message}),n.jsx("button",{type:"button",className:"typography-ui-label text-primary hover:underline",onClick:()=>A?be?.():oe(),children:x("diffView.actions.retry")})]}):null,v&&ce&&!ie&&!C?n.jsxs("div",{className:"flex items-center justify-center gap-2 px-4 py-8 text-sm text-muted-foreground",children:[n.jsx(z,{name:"loader-4",className:"size-4 animate-spin"}),x("diffView.state.loadingDiff")]}):null,v&&ie&&!ct&&s.insertions+s.deletions>Wt?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:x("diffView.state.largeDiff",{count:s.insertions+s.deletions})}),n.jsx("div",{className:"typography-meta text-muted-foreground",children:x("diffView.state.largeDiffDescription")}),n.jsx("button",{type:"button",className:"typography-ui-label text-primary hover:underline",onClick:()=>xt(!0),children:x("diffView.actions.renderAnyway")})]}):null,v&&ie&&(ct||s.insertions+s.deletions<=Wt)?n.jsx(n.Fragment,{children:n.jsx(Ps,{filePath:s.path,diff:ie,staged:w,renderSideBySide:Ue,wrapLines:y,hunkActions:dt,onExpandContextRequest:G?Ee:void 0,pendingContextExpansion:a,contextLoading:a!==null&&ie.contextMode!=="full"&&!C})}):null]})]})}),io=({visible:o=!0,hideStackedFileSidebar:e=!1,stackedDefaultCollapsedAll:s=!1,pinSelectedFileHeaderToTopOnNavigate:c=!1,showOpenInEditorAction:y=!1,diffScope:p="all",onDiffScopeChange:b,targetFilePath:v=null,flushContent:F=!1})=>{const{t:l}=qe(),{git:j,files:N}=en(),f=Zn(),V=ae(t=>t.runtimeKey),{rootIsGitRepo:w,gitDirectory:D,nestedRepos:_}=es(f??null,{enabled:o}),m=D??f,be=me(t=>t.openContextSurface),Z=xs(t=>t.requestSource),{screenWidth:ee,isMobile:Ne}=ts(),q=ns(m??null),x=ss(m??null),P=rs(m??null),W=ae(t=>t.setActiveDirectory),le=ae(t=>t.ensureStatus),te=ae(t=>t.selectNestedRepo),pe=ae(t=>t.fetchStatus),ne=ae(t=>t.fetchBranches),he=ae(t=>t.clearDiffCache),J=ae(t=>t.setDiff),[I,se]=i.useState(null),[X,K]=i.useState(!1),[je,A]=i.useState(null),[G,C]=i.useState(()=>new Set),[ce,H]=i.useState(()=>new Set),[lt,Be]=i.useState(0),[Ie,nt]=i.useState(()=>new Map),[ct,xt]=i.useState(0),[st,Pe]=i.useState(!1),[We,_e]=i.useState(!1),[a,Ee]=i.useState(p);i.useEffect(()=>{Ee(p)},[p]);const re=me(t=>t.pendingDiffFile),ge=me(t=>t.pendingDiffStaged),Ve=me(t=>t.pendingDiffScope),Oe=me(t=>t.setPendingDiffFile),Ue=me(t=>t.diffLayoutPreference),ze=me(t=>t.diffFileLayout),ve=me(t=>t.setDiffFileLayout),ue=me(t=>t.diffWrapLines),Je=me(t=>t.setDiffWrapLines),U=me(t=>t.openContextFileAtLine),de=Ht(t=>t.currentSessionId),ie=is(a==="turn"?de??"":"",f??void 0),Xe=ue,rt=a==="staged"?!0:a==="working"?!1:null,Ke=rt??X,Ye=Ne||ee<=768,ut=!!de&&a!=="turn"&&a!=="commit"&&a!=="pr"&&!Ye&&!Fe(),yt=a!=="turn"&&!Ye&&!Fe(),wt=!e&&!Ye&&ee>=1024,xe=i.useRef(null),Le=i.useRef(new Map),oe=i.useRef(null),$=i.useRef(null),Se=i.useRef(!1),Ce=i.useRef(null),dt=i.useRef(null),g=i.useRef(null),M=i.useRef(null),ye=i.useCallback(()=>{const t=xe.current;if(!t)return null;const r=t.getBoundingClientRect().top,u=[];for(const[d,h]of Le.current)h&&u.push({path:d,top:h.getBoundingClientRect().top});return $t(r,u)},[]),we=i.useCallback(()=>{oe.current=null,Se.current=!1,A(null),$.current!==null&&(window.cancelAnimationFrame($.current),$.current=null)},[]),O=i.useCallback(t=>{C(r=>{if(r.has(t))return r;const u=new Set(r);return u.add(t),u})},[]),[Ge,ft]=i.useState([]),mt=ie.length>0?ie[ie.length-1].id:"";i.useEffect(()=>{if(a!=="turn"||!de||!o)return;let t=!1;return Ln.getSessionTurnDiff(de,{directory:f??void 0}).then(r=>{t||ft(r.map(u=>({file:u.file,patch:u.patch,status:u.status,additions:u.additions,deletions:u.deletions})))}).catch(r=>{t||(console.warn("[diff-view] turn diff unavailable:",r instanceof Error?r.message:r),ft([]))}),()=>{t=!0}},[a,de,mt,f,o]);const it=i.useMemo(()=>{const t=new Map;for(const r of Ge)if(r.file){if(typeof r.patch=="string"){t.set(r.file,Rt(r.file,r.patch,"patch"));continue}t.set(r.file,{original:r.before??"",modified:r.after??"",contextMode:"full"})}return t},[Ge]),bt=i.useMemo(()=>x?.files?x.files.filter(Yt).length:0,[x]),E=i.useMemo(()=>x?.files?x.files.filter(Xt).length:0,[x]),ot=Ge.length,L=x?.current??null,Y=Tn(m??null,L,o&&a==="pr"&&!Fe()),Me=Y.selectedSource,Te=Bn(m??null,L,o&&a==="commit"&&!Fe()),He=Te.selectedCommit?.hash??null;i.useEffect(()=>{(a==="commit"||a==="pr")&&Fe()&&(Ee("working"),b?.("working"))},[a,b]);const Qe=ae(t=>m?t.directories.get(m)?.branches??null:null),un=ae(t=>m?t.directories.get(m)?.isLoadingBranches??!1:!1),dn=i.useCallback(()=>{m&&ne(m,j)},[m,ne,j]),fn=qn(m??null,o&&q!==!1,un,!!Qe,dn,Fs),Ft=i.useMemo(()=>{const t=x?.tracking?.trim().split("/")[0];return(t&&Qe?.defaultBranches?.[t])??Qe?.defaultBranches?.origin??null},[Qe,x?.tracking]),Dt=!Fe()&&Wn(L,Ft),mn=x!==null,At=Fe()||fn||Un(L,Ft,mn,Qe!==null),pn=In(t=>t.setOverride),{base:Re,resolved:hn,revision:Et}=Pn(m??null,L,o&&Dt&&a==="branch"),[gn,xn]=i.useState(0);i.useEffect(()=>{const t=Yn(a,!At);t!==a&&(Ee(t),t==="working"&&b?.("working"))},[a,At,b]);const yn=i.useMemo(()=>a==="pr"?Me:a==="commit"&&He?{kind:"commit",hash:He}:a==="branch"&&Re&&L?{kind:"branch",baseRef:Re,headRef:L}:null,[a,Re,L,He,Me]),T=Jn(m??null,yn,o&&!Fe(),a==="branch"?Et:""),{fetchDiff:Vt,fetchFullFile:Lt}=T,Mt=a==="commit"?T.files:null,Tt=a==="commit"?T.error:null,pt=a==="branch"?T.files:null,Bt=a==="branch"?T.error:null,It=T.files?a==="pr"?JSON.stringify([T.key,T.revision]):T.key:null,wn=i.useMemo(()=>a==="branch"||a==="commit"||a==="pr"?Array.from(G).sort().join("\0"):"",[a,G]),jt=i.useCallback(async(t,r=!1)=>{try{const u=await Vt(t,r?on:Ct);return{status:"ready",data:Rt(t,u.diff,r?"full":"patch")}}catch(u){return{status:"error",message:u instanceof Error?u.message:l("diffView.state.failedToLoadDiff")}}},[Vt,l]),Pt=i.useCallback(async t=>{try{const{original:r,modified:u}=await Lt(t),d=js({name:t,contents:r},{name:t,contents:u});return{status:"ready",data:{original:r,modified:u,fileDiff:d,contextMode:"full"}}}catch(r){return{status:"error",message:r instanceof Error?r.message:l("diffView.state.failedToLoadDiff")}}},[Lt,l]),bn=i.useMemo(()=>a==="branch"||a==="commit"?t=>jt(t,!0):a==="pr"?Pt:void 0,[a,jt,Pt]),jn=Xn(It,o?wn:"",It?jt:null,Jt,JSON.stringify([a==="branch"?Et:"",a==="pr"?T.revision:0,gn])),vn=pt?.length??null,k=i.useMemo(()=>{if(a==="commit"||a==="pr")return(T.files??[]).map(d=>({path:d.path,index:"",working_dir:d.status,insertions:d.insertions,deletions:d.deletions,isNew:d.status==="A"}));if(a==="branch")return(pt??[]).map(d=>({path:d.path,index:"",working_dir:d.status,insertions:0,deletions:0,isNew:d.status==="A"})).sort((d,h)=>d.path.localeCompare(h.path));if(a==="turn")return Ge.map(d=>({path:d.file??"",index:"",working_dir:Ms(d.status),insertions:d.additions??0,deletions:d.deletions??0,isNew:d.status==="added"})).filter(d=>d.path).sort((d,h)=>d.path.localeCompare(h.path));if(!x?.files)return[];const t=x.diffStats,r=a==="staged"?Xt:a==="working"?Yt:()=>!0,u=d=>{const h=t?.staged?.[d],R=t?.working?.[d];return a==="staged"?{insertions:h?.insertions??0,deletions:h?.deletions??0}:a==="working"?{insertions:R?.insertions??0,deletions:R?.deletions??0}:{insertions:(h?.insertions??0)+(R?.insertions??0),deletions:(h?.deletions??0)+(R?.deletions??0)}};return x.files.filter(r).map(d=>{const h=u(d.path);return{...d,insertions:h.insertions,deletions:h.deletions,isNew:Vs(d)}}).sort((d,h)=>d.path.localeCompare(h.path))},[a,pt,T.files,Ge,x]),vt=i.useMemo(()=>k.map(t=>t.path).join("\0"),[k]);i.useEffect(()=>{const t=vt?vt.split("\0"):[],r=new Set(t),u=`${m??""}:${a}:${s?"collapsed":"default"}`,d=dt.current!==u;dt.current=u,C(h=>{if(d){const B=s?0:Rs(t.length);return new Set(t.slice(0,B))}let R=!1;const S=new Set;for(const B of h){if(!r.has(B)){R=!0;continue}S.add(B)}return R?S:h}),H(h=>{if(d)return new Set;let R=!1;const S=new Set;for(const B of h){if(!r.has(B)){R=!0;continue}S.add(B)}return R?S:h})},[a,vt,m,s]);const _t=i.useCallback(()=>{if(Ce.current=null,!o)return;const t=xe.current;if(!t)return;const r=t.getBoundingClientRect(),u=r.top-Ut,d=r.bottom+Ut,h={},R=[];for(const[S,B]of Le.current){if(!B)continue;const Q=B.getBoundingClientRect();R.push({path:S,top:Q.top}),G.has(S)&&(Q.bottom<u||Q.top>d||(h[S]=!0))}g.current=$t(r.top,R),H(S=>{let B=!1;const Q=new Set(S);for(const at of Object.keys(h))Q.has(at)||(Q.add(at),B=!0);return B?Q:S})},[G,o]),fe=i.useCallback(()=>{o&&(typeof window>"u"||Ce.current===null&&(Ce.current=window.requestAnimationFrame(_t)))},[_t,o]);i.useEffect(()=>{const t=xe.current;if(!(!o||!t))return fe(),t.addEventListener("scroll",fe,{passive:!0}),window.addEventListener("resize",fe),()=>{t.removeEventListener("scroll",fe),window.removeEventListener("resize",fe),Ce.current!==null&&(window.cancelAnimationFrame(Ce.current),Ce.current=null)}},[k,G,fe,o]);const St=i.useCallback(t=>{const r=ze[t.path];if(r)return r;if(Ue==="inline")return"inline";if(Ue==="side-by-side")return"side-by-side";const u=ee<Ns;return t.isNew||u?"inline":"side-by-side"},[ze,Ue,ee]),Ot=i.useMemo(()=>k.length===0?null:k.every(t=>St(t)==="side-by-side")?"side-by-side":"inline",[k,St]);i.useEffect(()=>{o&&m&&(W(m),le(m,j))},[m,W,le,j,o]);const ht=JSON.stringify([V,m]),kt=i.useRef({scope:ht,paths:new Set,dirty:!1,unscoped:!1}),zt=i.useRef(o);i.useEffect(()=>{const t=o&&!zt.current;if(zt.current=o,kt.current.scope!==ht&&(kt.current={scope:ht,paths:new Set,dirty:!1,unscoped:!1}),!m)return;const r=(d,h)=>{d.length&&(M.current=ye()??g.current,he(m,d),nt(R=>{const S=new Map(R);for(const B of d)S.set(B,(S.get(B)??0)+1);return S})),h&&xt(R=>R+1),pe(m,j,{silent:!0})},u=kt.current;return o&&(t||u.dirty)&&(r([...u.paths],u.unscoped),u.paths.clear(),u.dirty=!1,u.unscoped=!1),tn.onGitRefreshHint(d=>{if(Qt(d.directory)!==Qt(m))return;const h=d.paths??[],R=h.length===0;if(!o){u.dirty=!0,u.unscoped||=R;for(const S of h)u.paths.add(S);return}r(h,R)})},[ye,he,m,pe,j,ht,o]),i.useLayoutEffect(()=>{const t=M.current;if(!t)return;M.current=null;const r=xe.current,u=Le.current.get(t.path);if(!r||!u)return;const d=r.getBoundingClientRect().top,h=u.getBoundingClientRect().top-d;r.scrollTop=ks(r.scrollTop,t.topOffset,h,r.scrollHeight-r.clientHeight),g.current=t},[Ie]),i.useEffect(()=>{!o||a!=="all"&&!Ve||re&&(Ve&&Ee(Ve),se(re),K(Ve==="staged"||!Ve&&ge),Oe(null),Se.current=!0,oe.current=re,O(re),Be(t=>t+1))},[a,O,re,Ve,ge,Oe,o]),i.useEffect(()=>{if(a==="all")return;const t=v?.trim();t&&(se(t),K(a==="staged"),Se.current=!0,oe.current=t,O(t),Be(r=>r+1))},[a,O,v]),i.useEffect(()=>{if(!I)return;k.some(r=>r.path===I)||(se(null),K(!1))},[k,I]);const Sn=i.useCallback((t,r)=>{const u=Le.current;r?u.set(t,r):u.delete(t),fe()},[fe]),kn=i.useCallback((t,r)=>{we(),C(u=>{const d=u.has(t);if(r===d)return u;const h=new Set(u);return r?h.add(t):h.delete(t),h}),r||H(u=>{if(!u.has(t))return u;const d=new Set(u);return d.delete(t),d}),fe()},[we,fe]),Nn=i.useCallback(()=>{we(),C(t=>t.size>0?new Set:new Set(k.map(r=>r.path))),H(new Set),fe()},[we,k,fe]),Cn=i.useCallback(async t=>{if(!de)return;const r=Ht.getState().getDirectoryForSession(de)||f||"";if(!r){$e.error(l("diffView.reviewDialog.toast.noSessionDirectory"));return}_e(!0);try{await os({originalSessionID:de,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}),Pe(!1)}catch(u){console.error("[review-flow] failed to start review flow",u),$e.error(u instanceof Error?u.message:l("diffView.reviewDialog.toast.startFailed"))}finally{_e(!1)}},[de,f,l]),gt=i.useCallback(t=>{const r=Le.current.get(t),u=xe.current;if(!r||!u)return!1;const d=r.getBoundingClientRect().top-u.getBoundingClientRect().top;return u.scrollTo({top:u.scrollTop+d,behavior:"auto"}),!0},[]);i.useEffect(()=>{const t=oe.current;if(!o||!t)return;let r=0;const u=20;let d=!1;const h=(S=!0)=>{d||(d=!0,oe.current=null,Se.current=!1,S&&A(null),$.current!==null&&(window.cancelAnimationFrame($.current),$.current=null))},R=()=>{if(d){$.current=null;return}const S=oe.current;if(!S){h(),$.current=null;return}if(!gt(S)){r+=1,r<u?$.current=window.requestAnimationFrame(R):(h(),$.current=null);return}if(c&&Se.current){A(S),h(!1);return}h()};return $.current=window.requestAnimationFrame(R),()=>{d=!0,$.current!==null&&(window.cancelAnimationFrame($.current),$.current=null)}},[c,lt,gt,o]);const Rn=i.useCallback(t=>{},[]),Nt=i.useCallback(t=>{we(),se(t),K(!1),Se.current=!0,oe.current=t,O(t),Be(r=>r+1),gt(t)},[we,O,gt]);i.useEffect(()=>{const t=r=>{if(!o||!r.altKey||r.metaKey||r.ctrlKey||r.shiftKey||r.key!=="ArrowDown"&&r.key!=="ArrowUp")return;const u=r.target;if(u instanceof HTMLElement&&(u.isContentEditable||u.tagName==="INPUT"||u.tagName==="TEXTAREA"||u.closest('[role="dialog"]'))||k.length===0)return;const d=r.key==="ArrowDown"?1:-1,h=I?k.findIndex(B=>B.path===I):-1,R=h===-1?d>0?0:k.length-1:h+d,S=k[R];S&&(r.preventDefault(),Nt(S.path))};return window.addEventListener("keydown",t),()=>window.removeEventListener("keydown",t)},[k,I,Nt,o]);const Fn=i.useCallback(t=>{const r=t==="side-by-side"?"side-by-side":"inline";k.forEach(u=>{ve(u.path,r)})},[k,ve]),[Dn,Kt]=i.useState(null),An=i.useCallback(async(t,r)=>{if(!m||!t)return;Kt(t);const u=De();try{let d=null;if(r?.patch&&!r.isBinary&&!et(t)?d=qt(r.patch):r&&r.contextMode==="full"&&!r.isBinary&&!et(t)&&(d=Zt(r.original,r.modified)),d===null)try{const Q=await j.getGitDiff(m,{path:t,staged:Ke,contextLines:3});d=qt(Q.diff)}catch{d=null}let h=r;if(d===null||!h){const Q=await j.getGitFileDiff(m,{path:t,staged:Ke}),at={original:Q.original??"",modified:Q.modified??"",isBinary:Q.isBinary,submodule:Q.submodule};h=at,Ke||J(m,t,at,u)}const R=d??(h.isBinary||et(t)?1:Zt(h.original,h.modified)),S=Ls(m,t),B=await ys(N,S,{directory:m});if(!B.ok){$e.error(ws(B.reason));return}U(m,S,R,1)}finally{Kt(d=>d===t?null:d)}},[Ke,m,N,j,U,J]),En=()=>{if(!m)return null;const t=r=>rt!==null?rt:X&&r===I;return n.jsxs("div",{className:Ae("flex min-w-0 flex-1 min-h-0 h-full",F?"gap-0":"gap-3 px-3 pb-3 pt-2"),children:[wt&&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:l("diffView.section.files")}),n.jsx("span",{className:"typography-meta text-muted-foreground",children:k.length})]}),n.jsx(Bs,{changedFiles:k,selectedFile:null,onSelectFile:Nt})]}),n.jsx("div",{className:"relative flex-1 min-w-0 min-h-0 h-full",children:n.jsx(rn,{ref:xe,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:k.map(r=>n.jsx(_s,{visible:o,directory:m,file:r,layout:St(r),wrapLines:Xe,isSelected:!1,isExpanded:G.has(r.path),isMounted:ce.has(r.path)||r.path===je,onSelect:Rn,onExpandedChange:kn,registerSectionRef:Sn,showOpenInEditorAction:y&&a!=="turn",isOpeningInEditor:Dn===r.path,onOpenInEditor:(u,d)=>{An(u,d)},staged:t(r.path),readOnlyActions:a==="branch"||a==="commit"||a==="pr",hunkActionsEnabled:a==="all"||a==="working"||a==="staged",contentRevision:ct,comparisonDiff:a==="branch"||a==="commit"||a==="pr"?jn.get(r.path)??Jt:void 0,onRetryComparisonDiff:()=>xn(u=>u+1),loadFullComparisonDiff:bn,initialDiffData:a==="turn"?it.get(r.path)??null:null},`${De()}:${m}:${r.path}:${Ie.get(r.path)??0}`))})})})]})},Vn=()=>{if(!m)return n.jsx("div",{className:"flex flex-1 items-center justify-center text-sm text-muted-foreground",children:l("diffView.state.selectSessionDirectory")});if(a!=="turn"&&P&&!x)return n.jsxs("div",{className:"flex flex-1 items-center justify-center gap-2 text-sm text-muted-foreground",children:[n.jsx(z,{name:"loader-4",className:"size-4 animate-spin"}),l("diffView.state.loadingRepositoryStatus")]});if(a!=="turn"&&q===!1)return n.jsx("div",{className:"flex flex-1 items-center justify-center text-sm text-muted-foreground",children:l("diffView.state.notGitRepository")});if(a==="pr"){if(!Me||T.error)return n.jsxs("div",{className:"flex flex-1 flex-col items-center justify-center gap-3 p-6 text-center",children:[n.jsx("p",{className:"typography-meta text-muted-foreground",children:T.error??Y.error??(Y.loading?l("session.githubPrPicker.loading.pullRequests"):l("pullRequestComparison.select"))}),(T.error||Y.error)&&n.jsx(ke,{variant:"outline",size:"sm",onClick:()=>{Me?T.refresh():Y.refresh()},children:l("diffView.actions.retry")}),!Me&&!Y.loading&&n.jsx(Gt,{comparison:Y})]});if(!T.files)return n.jsxs("div",{className:"flex flex-1 items-center justify-center gap-2 typography-meta text-muted-foreground",children:[n.jsx(z,{name:"loader-4",className:"size-4 animate-spin"}),l("diffView.state.loadingDiff")]})}if(a==="commit"){if(Tt)return n.jsxs("div",{className:"flex flex-1 flex-col items-center justify-center gap-3 p-6 text-center",children:[n.jsx("p",{className:"typography-meta text-muted-foreground",children:Tt}),n.jsx(ke,{variant:"outline",size:"sm",onClick:()=>{T.refresh()},children:l("diffView.actions.retry")})]});if(!He&&!Te.loading)return n.jsx("div",{className:"flex flex-1 items-center justify-center text-sm text-muted-foreground",children:Te.error??l("commitComparison.noCommits")});if(!Mt)return n.jsxs("div",{className:"flex flex-1 items-center justify-center gap-2 text-sm text-muted-foreground",children:[n.jsx(z,{name:"loader-4",className:"size-4 animate-spin"}),l("diffView.state.loadingDiff")]})}if(a==="branch"){if(!hn)return n.jsxs("div",{className:"flex flex-1 items-center justify-center gap-2 text-sm text-muted-foreground",children:[n.jsx(z,{name:"loader-4",className:"size-4 animate-spin"}),l("diffView.branch.resolvingBase")]});if(!Re)return n.jsxs("div",{className:"flex flex-1 flex-col items-center justify-center gap-3 px-6 text-center",children:[n.jsx(z,{name:"git-branch",className:"size-6 text-muted-foreground"}),n.jsx("div",{className:"typography-ui-label font-semibold text-foreground",children:l("diffView.branch.noBaseTitle")}),n.jsx("div",{className:"max-w-sm typography-micro text-muted-foreground",children:l("gitView.pr.toast.baseBranchRequired")})]});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:l("diffView.branch.loadError")}),n.jsx("div",{className:"max-w-sm typography-micro text-muted-foreground",children:Bt}),n.jsx(ke,{variant:"outline",size:"sm",onClick:()=>{T.refresh()},children:l("diffView.actions.retry")})]});if(pt===null)return n.jsxs("div",{className:"flex flex-1 items-center justify-center gap-2 text-sm text-muted-foreground",children:[n.jsx(z,{name:"loader-4",className:"size-4 animate-spin"}),l("diffView.branch.loadingFiles")]})}return k.length===0?n.jsx("div",{className:"flex flex-1 items-center justify-center text-sm text-muted-foreground",children:a==="turn"?l("diffView.state.noLastTurnChanges"):a==="pr"?l("walkthrough.blocked.emptyDiff.description"):a==="commit"?l("commitComparison.emptyDiff"):a==="branch"&&Re?l("diffView.branch.empty",{base:hs(Re)}):l("diffView.state.cleanWorkingTree")}):En()};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:[w===!1&&Array.isArray(_)&&_.length>0?n.jsx(ps,{repositories:_,selectedRepository:D??null,onSelectRepository:t=>{f&&te(f,t)},repositoryRoot:f??void 0}):null,!Ne&&(a!=="all"?n.jsx(Ts,{scope:a,workingCount:bt,stagedCount:E,turnCount:ot,branchCount:vn,commitCount:a==="commit"?Mt?.length??null:null,prCount:a==="pr"?T.files?.length??null:null,showCommitOption:!Fe(),showBranchOption:Dt,onScopeChange:t=>{Ee(t),b?.(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:P&&!x?l("diffView.state.loadingChanges"):k.length===1?l("diffView.summary.changedFilesSingle",{count:k.length}):l("diffView.summary.changedFilesPlural",{count:k.length})})})),a==="branch"&&n.jsx(_n,{branches:Qe?.all??[],currentBranch:L,base:Re,onSelect:t=>{m&&L&&pn(m,L,t)}},JSON.stringify([m,L])),a==="commit"&&n.jsx(On,{commits:Te.commits,selectedHash:He,loading:Te.loading,error:Te.error,onSelect:Te.select,onRefresh:()=>{Te.refresh()}},JSON.stringify([m,L])),a==="pr"&&n.jsxs(n.Fragment,{children:[n.jsx(Gt,{comparison:Y},JSON.stringify([V,m,L])),Me&&n.jsx(ke,{variant:"ghost",size:"sm",disabled:T.loading,"aria-label":l("session.githubIssuePicker.actions.refresh"),title:l("session.githubIssuePicker.actions.refresh"),onClick:()=>{T.refresh()},children:n.jsx(z,{name:"refresh",className:"size-4"})})]}),k.length>0&&n.jsxs(ke,{variant:"ghost",size:"sm",onClick:Nn,className:Ae("diff-toolbar__expand-button h-7 flex-shrink-0 gap-1 px-1.5 text-muted-foreground hover:text-foreground","ml-auto"),title:G.size>0?l("diffView.actions.collapseAll"):l("diffView.actions.expandAll"),children:[n.jsx(z,{name:"expand-up-down",className:"size-4"}),n.jsx("span",{className:"diff-toolbar__expand-label typography-ui-label",children:G.size>0?l("diffView.actions.collapseAll"):l("diffView.actions.expandAll")})]}),k.length>0&&ut&&n.jsxs(ke,{variant:"default",size:"sm",onClick:()=>Pe(!0),disabled:We,className:"diff-toolbar__review-button h-7 flex-shrink-0 gap-1.5 px-2","aria-label":l("diffView.actions.reviewAria"),children:[We?n.jsx(z,{name:"loader-4",className:"size-4 animate-spin"}):n.jsx(z,{name:"search-eye",className:"size-4"}),n.jsx("span",{className:"diff-toolbar__review-label typography-ui-label",children:l("diffView.actions.review")})]}),k.length>0&&yt&&n.jsxs(ke,{variant:"outline",size:"sm",onClick:()=>{const t=m??"";Z(t,a==="pr"&&Me?Me:a==="commit"&&He?{kind:"commit",hash:He}:a==="branch"&&Re&&L?{kind:"branch",baseRef:Re,headRef:L}:{kind:"working-tree",scope:a==="staged"||a==="working"?a:"all"}),be(f??t,"walkthrough")},className:Ae("diff-toolbar__walkthrough-button h-7 flex-shrink-0 gap-1.5 px-2",bs),"aria-label":l("walkthrough.action.open"),children:[n.jsx(z,{name:"route",className:"size-4"}),n.jsx("span",{className:"diff-toolbar__walkthrough-label typography-ui-label",children:l("walkthrough.action.open")})]}),k.length>0&&n.jsx(ke,{variant:"ghost",size:"sm",onClick:()=>Je(!ue),className:Ae("h-5 w-5 p-0 transition-opacity",Xe?"text-foreground opacity-100":"text-muted-foreground opacity-60 hover:opacity-100"),title:l(Xe?"diffView.actions.disableLineWrap":"diffView.actions.enableLineWrap"),children:n.jsx(z,{name:"text-wrap",className:"size-4"})}),Ot&&n.jsx(nn,{mode:Ot==="side-by-side"?"side-by-side":"unified",onModeChange:Fn})]}),n.jsx(as,{open:st,onOpenChange:Pe,projectDirectory:m??null,submitting:We,onConfirm:Cn}),Vn()]})};export{io as DiffView,_s as MultiFileDiffEntry};