@dimina-kit/devtools 0.4.0-dev.20260831134112 → 0.4.0-dev.20260907055341

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 (275) hide show
  1. package/README.md +52 -729
  2. package/dist/main/app/app.d.ts +27 -7
  3. package/dist/main/app/app.js +285 -681
  4. package/dist/main/app/editor-view.d.ts +21 -0
  5. package/dist/main/app/editor-view.js +71 -0
  6. package/dist/main/app/global-mirrors.d.ts +23 -0
  7. package/dist/main/app/global-mirrors.js +37 -0
  8. package/dist/main/app/host-sidebar-default.d.ts +19 -0
  9. package/dist/main/app/host-sidebar-default.js +44 -0
  10. package/dist/main/app/menu-setup.d.ts +13 -0
  11. package/dist/main/app/menu-setup.js +49 -0
  12. package/dist/main/app/open-project-ipc.d.ts +13 -0
  13. package/dist/main/app/open-project-ipc.js +21 -0
  14. package/dist/main/app/project-list-surface.d.ts +31 -0
  15. package/dist/main/app/project-list-surface.js +20 -0
  16. package/dist/main/app/project-window.d.ts +36 -0
  17. package/dist/main/app/project-window.js +93 -0
  18. package/dist/main/app/servers.d.ts +17 -0
  19. package/dist/main/app/servers.js +60 -0
  20. package/dist/main/app/ui-extension-targets.d.ts +42 -0
  21. package/dist/main/app/ui-extension-targets.js +101 -0
  22. package/dist/main/app/window-close-reveal.harness.d.ts +34 -0
  23. package/dist/main/app/window-close-reveal.harness.js +65 -0
  24. package/dist/main/app/window-events.d.ts +15 -0
  25. package/dist/main/app/window-events.js +45 -0
  26. package/dist/main/app/window-runtime-services.d.ts +41 -0
  27. package/dist/main/app/window-runtime-services.js +178 -0
  28. package/dist/main/app/workbench-modules.d.ts +24 -0
  29. package/dist/main/app/workbench-modules.js +51 -0
  30. package/dist/main/app/workbench-window.d.ts +85 -0
  31. package/dist/main/app/workbench-window.js +310 -0
  32. package/dist/main/index.bundle.js +10669 -9096
  33. package/dist/main/ipc/app.d.ts +4 -1
  34. package/dist/main/ipc/app.js +4 -3
  35. package/dist/main/ipc/device-picker.d.ts +16 -0
  36. package/dist/main/ipc/device-picker.js +27 -0
  37. package/dist/main/ipc/index.d.ts +1 -0
  38. package/dist/main/ipc/index.js +1 -0
  39. package/dist/main/ipc/internal-devtools.d.ts +2 -1
  40. package/dist/main/ipc/internal-devtools.js +4 -3
  41. package/dist/main/ipc/popover.d.ts +4 -1
  42. package/dist/main/ipc/popover.js +25 -9
  43. package/dist/main/ipc/project-create.d.ts +2 -1
  44. package/dist/main/ipc/project-create.js +6 -5
  45. package/dist/main/ipc/project-fs.d.ts +3 -1
  46. package/dist/main/ipc/project-fs.js +17 -17
  47. package/dist/main/ipc/projects.d.ts +3 -3
  48. package/dist/main/ipc/projects.js +13 -12
  49. package/dist/main/ipc/session.d.ts +4 -1
  50. package/dist/main/ipc/session.js +16 -11
  51. package/dist/main/ipc/settings.d.ts +4 -1
  52. package/dist/main/ipc/settings.js +12 -19
  53. package/dist/main/ipc/simulator-module.d.ts +5 -0
  54. package/dist/main/ipc/simulator-module.js +9 -5
  55. package/dist/main/ipc/simulator.d.ts +4 -1
  56. package/dist/main/ipc/simulator.js +12 -18
  57. package/dist/main/ipc/tooltip.d.ts +2 -1
  58. package/dist/main/ipc/tooltip.js +8 -7
  59. package/dist/main/ipc/views.d.ts +4 -1
  60. package/dist/main/ipc/views.js +20 -9
  61. package/dist/main/runtime/devtools-backend-before-quit.harness.d.ts +2 -20
  62. package/dist/main/runtime/devtools-backend-before-quit.harness.js +14 -283
  63. package/dist/main/runtime/devtools-backend.js +14 -3
  64. package/dist/main/runtime/devtools-runtime-mock.harness.d.ts +21 -0
  65. package/dist/main/runtime/devtools-runtime-mock.harness.js +313 -0
  66. package/dist/main/services/app-services.d.ts +62 -0
  67. package/dist/main/services/app-services.js +64 -0
  68. package/dist/main/services/automation/connection-target.d.ts +36 -0
  69. package/dist/main/services/automation/connection-target.js +29 -0
  70. package/dist/main/services/automation/console-bridge.d.ts +28 -0
  71. package/dist/main/services/automation/console-bridge.js +91 -0
  72. package/dist/main/services/automation/handlers/app.js +4 -4
  73. package/dist/main/services/automation/handlers/element.js +55 -21
  74. package/dist/main/services/automation/index.d.ts +12 -1
  75. package/dist/main/services/automation/index.js +54 -133
  76. package/dist/main/services/default-adapter.js +1 -1
  77. package/dist/main/services/fs-bridge-protocol.d.ts +61 -0
  78. package/dist/main/services/fs-bridge-protocol.js +34 -0
  79. package/dist/main/services/fs-watch-sse.d.ts +8 -1
  80. package/dist/main/services/fs-watch-sse.js +19 -5
  81. package/dist/main/services/mcp/connection-owner.d.ts +38 -0
  82. package/dist/main/services/mcp/connection-owner.js +79 -0
  83. package/dist/main/services/mcp/index.d.ts +3 -1
  84. package/dist/main/services/mcp/index.js +4 -3
  85. package/dist/main/services/mcp/opened-project.d.ts +88 -0
  86. package/dist/main/services/mcp/opened-project.js +60 -0
  87. package/dist/main/services/mcp/target-manager.d.ts +71 -23
  88. package/dist/main/services/mcp/target-manager.js +269 -142
  89. package/dist/main/services/mcp/target-selection.d.ts +47 -0
  90. package/dist/main/services/mcp/target-selection.js +71 -0
  91. package/dist/main/services/mcp/tools/context-tools.js +20 -5
  92. package/dist/main/services/mcp/tools/project-tools.d.ts +29 -6
  93. package/dist/main/services/mcp/tools/project-tools.js +37 -25
  94. package/dist/main/services/module.d.ts +13 -5
  95. package/dist/main/services/notifications/renderer-notifier.d.ts +28 -4
  96. package/dist/main/services/notifications/renderer-notifier.js +15 -3
  97. package/dist/main/services/projects/local-provider.js +2 -0
  98. package/dist/main/services/projects/project-repository.d.ts +31 -1
  99. package/dist/main/services/projects/project-repository.js +144 -20
  100. package/dist/main/services/projects/types.d.ts +31 -8
  101. package/dist/main/services/safe-area/index.d.ts +19 -5
  102. package/dist/main/services/safe-area/index.js +38 -13
  103. package/dist/main/services/simulator/ui-extensions.d.ts +8 -0
  104. package/dist/main/services/simulator/ui-extensions.js +16 -6
  105. package/dist/main/services/simulator-storage/index.d.ts +12 -1
  106. package/dist/main/services/simulator-storage/index.js +22 -7
  107. package/dist/main/services/simulator-storage/window-scope.d.ts +20 -0
  108. package/dist/main/services/simulator-storage/window-scope.js +20 -0
  109. package/dist/main/services/views/host-slot-port-channel.d.ts +20 -3
  110. package/dist/main/services/views/host-slot-port-channel.js +33 -5
  111. package/dist/main/services/views/managed-web-contents-view.js +21 -1
  112. package/dist/main/services/views/native-simulator-view.js +13 -16
  113. package/dist/main/services/views/overlay-panels-view.d.ts +13 -0
  114. package/dist/main/services/views/overlay-panels-view.js +31 -0
  115. package/dist/main/services/views/view-manager.d.ts +1 -1
  116. package/dist/main/services/views/view-manager.js +3 -0
  117. package/dist/main/services/window-contexts/context-router.d.ts +36 -0
  118. package/dist/main/services/window-contexts/context-router.js +55 -0
  119. package/dist/main/services/workbench-coi-host.d.ts +52 -0
  120. package/dist/main/services/workbench-coi-host.js +227 -0
  121. package/dist/main/services/workbench-coi-server.js +75 -133
  122. package/dist/main/services/workbench-coi-shutdown.d.ts +40 -0
  123. package/dist/main/services/workbench-coi-shutdown.js +69 -0
  124. package/dist/main/services/workbench-coi-static.d.ts +23 -0
  125. package/dist/main/services/workbench-coi-static.js +87 -0
  126. package/dist/main/services/workbench-context.d.ts +27 -3
  127. package/dist/main/services/workbench-context.js +10 -5
  128. package/dist/main/services/workspace/compile-mode-adapter.d.ts +18 -0
  129. package/dist/main/services/workspace/compile-mode-adapter.js +57 -0
  130. package/dist/main/services/workspace/compile-mode-store.d.ts +25 -0
  131. package/dist/main/services/workspace/compile-mode-store.js +71 -0
  132. package/dist/main/services/workspace/workspace-compile-modes.d.ts +59 -0
  133. package/dist/main/services/workspace/workspace-compile-modes.js +93 -0
  134. package/dist/main/services/workspace/workspace-referer.d.ts +17 -0
  135. package/dist/main/services/workspace/workspace-referer.js +26 -0
  136. package/dist/main/services/workspace/workspace-service.d.ts +4 -0
  137. package/dist/main/services/workspace/workspace-service.js +51 -84
  138. package/dist/main/services/workspace/workspace-thumbnails.d.ts +22 -0
  139. package/dist/main/services/workspace/workspace-thumbnails.js +67 -0
  140. package/dist/main/utils/ipc-context-source.d.ts +40 -0
  141. package/dist/main/utils/ipc-context-source.js +23 -0
  142. package/dist/main/utils/ipc-registry.d.ts +42 -36
  143. package/dist/main/utils/ipc-registry.js +117 -50
  144. package/dist/main/utils/sender-policy.d.ts +9 -0
  145. package/dist/main/utils/sender-policy.js +26 -3
  146. package/dist/main/utils/theme.d.ts +3 -2
  147. package/dist/main/utils/theme.js +3 -2
  148. package/dist/main/windows/main-window/create.d.ts +16 -0
  149. package/dist/main/windows/main-window/create.js +19 -11
  150. package/dist/main/windows/main-window/events.d.ts +1 -1
  151. package/dist/main/windows/main-window/events.js +26 -13
  152. package/dist/main/windows/main-window/index.d.ts +1 -1
  153. package/dist/main/windows/main-window/index.js +1 -1
  154. package/dist/native-host/common/common.js +94 -73
  155. package/dist/native-host/render/render.js +4079 -3171
  156. package/dist/native-host/service/service.js +2 -2
  157. package/dist/preload/runtime/native-host.d.ts +4 -0
  158. package/dist/preload/runtime/temp-files.js +9 -2
  159. package/dist/preload/runtime/wait-for-slot-root.d.ts +7 -4
  160. package/dist/preload/runtime/wait-for-slot-root.js +36 -8
  161. package/dist/preload/windows/host-dialog-runtime.cjs +10 -3
  162. package/dist/preload/windows/host-dialog-runtime.cjs.map +2 -2
  163. package/dist/preload/windows/host-sidebar-runtime.cjs +10 -3
  164. package/dist/preload/windows/host-sidebar-runtime.cjs.map +2 -2
  165. package/dist/preload/windows/host-toolbar-runtime.cjs +10 -3
  166. package/dist/preload/windows/host-toolbar-runtime.cjs.map +2 -2
  167. package/dist/preload/windows/main.cjs +181 -4
  168. package/dist/preload/windows/main.cjs.map +4 -4
  169. package/dist/preload/windows/simulator.cjs +10 -0
  170. package/dist/preload/windows/simulator.cjs.map +2 -2
  171. package/dist/preload/windows/simulator.js +10 -0
  172. package/dist/renderer/assets/button-CB2MWjHY.js +2 -0
  173. package/dist/renderer/assets/constants-CkoGPQTS.js +2 -0
  174. package/dist/renderer/assets/constants-zQZ2-y21.js +2 -0
  175. package/dist/renderer/assets/{createLucideIcon-Bqi8hpdi.js → createLucideIcon-cZ9FlZZw.js} +2 -2
  176. package/dist/renderer/assets/devicePicker-H2eIO4e1.js +2 -0
  177. package/dist/renderer/assets/dialog-pHzEA8Vx.js +46 -0
  178. package/dist/renderer/assets/dist-B5HADirS.js +2 -0
  179. package/dist/renderer/assets/{dist-DKWdE1TK.js → dist-DIFlRMKe.js} +2 -2
  180. package/dist/renderer/assets/hostSidebarDefault-DRWh6OCt.js +2 -0
  181. package/dist/renderer/assets/index-CEL_XFge.js +2 -0
  182. package/dist/renderer/assets/{input-Ct3alvkq.js → input-CAObw95L.js} +2 -2
  183. package/dist/renderer/assets/ipc-channels-overlays-gIPUFnqc.js +2 -0
  184. package/dist/renderer/assets/jsx-runtime-DICEQH_0.css +1 -0
  185. package/dist/renderer/assets/{jsx-runtime-DGLHmmOq.js → jsx-runtime-DIw5wnYl.js} +1 -1
  186. package/dist/renderer/assets/plus-BQ9u5EbL.js +2 -0
  187. package/dist/renderer/assets/popover-Cw7G2Ctp.js +2 -0
  188. package/dist/renderer/assets/presets-DMZOb9ay.js +2 -0
  189. package/dist/renderer/assets/project-api-YTOr_Yvo.js +2 -0
  190. package/dist/renderer/assets/{projectCreateDialog-Bbjz75bP.js → projectCreateDialog-BmLqiYxn.js} +2 -2
  191. package/dist/renderer/assets/search-BMvuYF48.js +2 -0
  192. package/dist/renderer/assets/select-nFP95TxM.js +2 -0
  193. package/dist/renderer/assets/settings-CnpVfSWt.js +2 -0
  194. package/dist/renderer/assets/settings-api-Ccsf-Nsj.js +2 -0
  195. package/dist/renderer/assets/settings-tab-bar-CAzrv32m.js +2 -0
  196. package/dist/renderer/assets/smartphone-Bb2GdIKy.js +2 -0
  197. package/dist/renderer/assets/tabs-CPKN3Mmq.js +2 -0
  198. package/dist/renderer/assets/{tooltip-B_-_YVbw.js → tooltip-D6AxXRk5.js} +2 -2
  199. package/dist/renderer/assets/types-Bh0hA7El.js +2 -0
  200. package/dist/renderer/assets/updateDialog-CwndJ0Bl.js +2 -0
  201. package/dist/renderer/assets/{utils-kfQov6Pa.js → utils-sEB5OZEQ.js} +2 -2
  202. package/dist/renderer/assets/view-api-B-2zPBR5.js +2 -0
  203. package/dist/renderer/assets/view-ids-BfaDVV5P.js +2 -0
  204. package/dist/renderer/assets/workbench-BLJ0wKsg.js +5 -0
  205. package/dist/renderer/assets/{workbenchSettings-CV4LVA9b.js → workbenchSettings-YD0Ctbm_.js} +3 -3
  206. package/dist/renderer/entries/device-picker/index.html +28 -0
  207. package/dist/renderer/entries/host-sidebar-default/index.html +6 -5
  208. package/dist/renderer/entries/main/index.html +16 -17
  209. package/dist/renderer/entries/popover/index.html +16 -10
  210. package/dist/renderer/entries/project-create-dialog/index.html +12 -12
  211. package/dist/renderer/entries/settings/index.html +10 -10
  212. package/dist/renderer/entries/tooltip/index.html +5 -5
  213. package/dist/renderer/entries/update-dialog/index.html +10 -10
  214. package/dist/renderer/entries/workbench/index.html +49 -0
  215. package/dist/renderer/entries/workbench-settings/index.html +12 -11
  216. package/dist/service-host/host-env-update.cjs +23 -0
  217. package/dist/service-host/preload.cjs +24 -49
  218. package/dist/service-host/service-message-queue.cjs +71 -0
  219. package/dist/service-host/sync-impls/system-info.js +31 -9
  220. package/dist/shared/compile-mode-state.d.ts +73 -0
  221. package/dist/shared/compile-mode-state.js +100 -0
  222. package/dist/shared/compile-modes.d.ts +152 -0
  223. package/dist/shared/compile-modes.js +237 -0
  224. package/dist/shared/ipc-channels-overlays.d.ts +8 -2
  225. package/dist/shared/ipc-channels-overlays.js +19 -2
  226. package/dist/shared/ipc-channels.d.ts +5 -10
  227. package/dist/shared/ipc-channels.js +17 -14
  228. package/dist/shared/ipc-schemas.d.ts +76 -24
  229. package/dist/shared/ipc-schemas.js +55 -10
  230. package/dist/shared/project-window-types.d.ts +42 -0
  231. package/dist/shared/project-window-types.js +2 -0
  232. package/dist/shared/types.d.ts +43 -17
  233. package/dist/shared/view-ids.d.ts +1 -0
  234. package/dist/shared/view-ids.js +3 -1
  235. package/dist/simulator/assets/bridge-channels-B3JAmRfl.js +2 -0
  236. package/dist/simulator/assets/device-shell-ATEhHV2_.css +1 -0
  237. package/dist/simulator/assets/device-shell-pxnZZp10.js +431 -0
  238. package/dist/simulator/assets/simulator-Dwp7lnvu.js +10 -0
  239. package/dist/simulator/assets/simulator-mini-app-DFo7fhi5.js +2 -0
  240. package/dist/simulator/assets/simulator-ui-B2ddJB1R.js +2 -0
  241. package/dist/simulator/simulator.html +3 -3
  242. package/dist/simulator/temp-files.d.ts +6 -0
  243. package/dist/simulator/temp-files.js +46 -4
  244. package/dist/simulator/types.d.ts +7 -0
  245. package/dist/vscode-workbench/assets/__vite-browser-external-JIkWewFf.js +1 -0
  246. package/dist/vscode-workbench/assets/{dist-CMfmHflW.js → dist-CXG-u1VB.js} +3 -3
  247. package/dist/vscode-workbench/assets/{iconv-lite-umd-Bx8CgaZq.js → iconv-lite-umd-Dxx9Qyb4.js} +1 -1
  248. package/dist/vscode-workbench/assets/{index-CdG6YWhW.js → index-6VJMsaWj.js} +460 -460
  249. package/dist/vscode-workbench/assets/{jschardet-M0qgd2lN.js → jschardet-Ba3D16vt.js} +1 -1
  250. package/dist/vscode-workbench/index.html +1 -1
  251. package/package.json +11 -8
  252. package/dist/renderer/assets/button-D5Q2wkXq.js +0 -2
  253. package/dist/renderer/assets/constants-D7DfVqMp.js +0 -2
  254. package/dist/renderer/assets/constants-D8zsejFq.js +0 -2
  255. package/dist/renderer/assets/dialog-e1TecTOY.js +0 -46
  256. package/dist/renderer/assets/hostSidebarDefault-B3yLfy8j.js +0 -2
  257. package/dist/renderer/assets/index-CsPD68N_.js +0 -5
  258. package/dist/renderer/assets/ipc-channels-overlays-LsDzGAtV.js +0 -2
  259. package/dist/renderer/assets/jsx-runtime-2vXHouwr.css +0 -1
  260. package/dist/renderer/assets/popover-Dix5cm_b.js +0 -2
  261. package/dist/renderer/assets/project-api-k7Oq9iaq.js +0 -2
  262. package/dist/renderer/assets/select-acGb0AmJ.js +0 -2
  263. package/dist/renderer/assets/settings-CQrTMr43.js +0 -2
  264. package/dist/renderer/assets/settings-api-CrUQ9rYV.js +0 -2
  265. package/dist/renderer/assets/settings-tab-bar-Cw1kVIf2.js +0 -2
  266. package/dist/renderer/assets/types-CsEKtFyr.js +0 -2
  267. package/dist/renderer/assets/updateDialog-NsF8223A.js +0 -2
  268. package/dist/renderer/assets/view-api-CLn3rcQE.js +0 -2
  269. package/dist/simulator/assets/bridge-channels-zhsumfky.js +0 -2
  270. package/dist/simulator/assets/device-shell-CmtM1QxP.js +0 -2
  271. package/dist/simulator/assets/device-shell-iFZo1igL.css +0 -1
  272. package/dist/simulator/assets/simulator-CIobn3Mh.js +0 -10
  273. package/dist/simulator/assets/simulator-mini-app-CRe6n9br.js +0 -2
  274. package/dist/simulator/assets/simulator-ui-C_cP602X.js +0 -2
  275. package/dist/vscode-workbench/assets/__vite-browser-external-Bc1vizTf.js +0 -1
@@ -0,0 +1,310 @@
1
+ import path from 'path';
2
+ import { toDisposable } from '@dimina-kit/electron-deck/main';
3
+ import { createInternalDevtoolsWindow } from '../windows/internal-devtools-window/index.js';
4
+ import { revealMainWindow, wireMainWindowEvents } from '../windows/main-window/index.js';
5
+ import { isAppQuitting } from './lifecycle.js';
6
+ import { installGlobalMirrors } from './global-mirrors.js';
7
+ import { setupEditorView } from './editor-view.js';
8
+ import { createActiveAppIdResolver, setupWindowRuntimeServices } from './window-runtime-services.js';
9
+ import { createWorkbenchWindow } from './project-window.js';
10
+ /**
11
+ * Close semantics for a workbench window. Unlike the old single-window app —
12
+ * where closing meant "return this window to the project list" — closing a
13
+ * workbench window means the window itself goes away, so teardown must finish
14
+ * BEFORE the window is destroyed.
15
+ *
16
+ * Three guards, each covering a failure this app has actually shipped:
17
+ *
18
+ * 1. A real application quit (⌘Q / menu Quit / app.quit()) fires `before-quit`
19
+ * first. Let it through: converting a quit into a project close swallows the
20
+ * quit and the app can never exit with a project open.
21
+ * 2. A close arriving while teardown is already in flight (the user
22
+ * rapid-double-clicked) MUST keep the window. This runs BEFORE any
23
+ * session check on purpose: `closeProject()` → `disposeSession()`
24
+ * synchronously nulls the active session before it finishes awaiting
25
+ * `session.close()`, so a session-presence check would already read false
26
+ * by the time the second close arrives, fall through with no
27
+ * `preventDefault()`, and let Chromium destroy the window out from under a
28
+ * live teardown.
29
+ * 3. Teardown is UNCONDITIONAL — deliberately not gated on
30
+ * `hasActiveSession()`. A failed open (invalid/non-existent project) never
31
+ * creates a session, yet the window still owns views, an editor server and
32
+ * IPC registrations. Gating on session presence let that case skip teardown
33
+ * entirely, which is how closing an invalid project used to take the whole
34
+ * app down with it.
35
+ */
36
+ function wireWorkbenchWindowEvents(win, ctx, teardown) {
37
+ let closing = false;
38
+ let torndown = false;
39
+ return wireMainWindowEvents(win, {
40
+ context: ctx,
41
+ onResize: () => ctx.views.repositionAll(),
42
+ onClose: async (e) => {
43
+ if (isAppQuitting())
44
+ return;
45
+ if (closing) {
46
+ e.preventDefault();
47
+ return;
48
+ }
49
+ // Safety net for a `close()` that arrives after teardown finished (this
50
+ // module destroys the window itself, which emits no `close`).
51
+ if (torndown)
52
+ return;
53
+ e.preventDefault();
54
+ closing = true;
55
+ try {
56
+ await teardown();
57
+ }
58
+ catch (err) {
59
+ console.error('[workbench] workbench window teardown failed:', err);
60
+ }
61
+ finally {
62
+ closing = false;
63
+ torndown = true;
64
+ }
65
+ if (!win.isDestroyed())
66
+ win.destroy();
67
+ },
68
+ });
69
+ }
70
+ export function createWorkbenchWindowManager(deps) {
71
+ const { config, rendererDir, appServices, router } = deps;
72
+ // Keyed by project path: one window per project, so a second open of the
73
+ // same project focuses the existing window instead of racing two sessions
74
+ // (and two compile workers) over the same directory.
75
+ const windows = new Map();
76
+ // Serializes every open and close of one project path. The map alone cannot
77
+ // carry "one window per project": an open has awaits in it, and a close
78
+ // awaits host code, so overlapping requests (a re-click on the project list,
79
+ // MCP's project_open) must queue rather than each find "no window" and build
80
+ // one. Entries are dropped once a path goes idle.
81
+ const pathQueues = new Map();
82
+ // Terminal state owned by this manager, deliberately not `isAppQuitting()`:
83
+ // a runtime can be disposed while the app keeps running, and a manager that
84
+ // has torn everything down must never build another window either way.
85
+ let disposed = false;
86
+ function disposedError() {
87
+ return new Error('workbench window manager is disposed');
88
+ }
89
+ function enqueue(path, task) {
90
+ const previous = pathQueues.get(path) ?? Promise.resolve();
91
+ // Same task on either outcome: a failed open or close must not wedge the
92
+ // queue for its path.
93
+ const result = previous.then(task, task);
94
+ const settled = result.then(() => { }, () => { });
95
+ pathQueues.set(path, settled);
96
+ void settled.then(() => {
97
+ if (pathQueues.get(path) === settled)
98
+ pathQueues.delete(path);
99
+ });
100
+ return result;
101
+ }
102
+ async function openWindow(project) {
103
+ // Checked where the queued task starts running, not where it was queued:
104
+ // an open sitting behind a close on the same path can be dequeued long
105
+ // after `disposeAll()` began, and must fail instead of resurrecting a
106
+ // project the app just tore down.
107
+ if (disposed)
108
+ throw disposedError();
109
+ const existing = windows.get(project.path);
110
+ if (existing && !existing.window.isDestroyed()) {
111
+ if (existing.window.isMinimized())
112
+ existing.window.restore();
113
+ existing.window.focus();
114
+ router.setActive(existing.context);
115
+ deps.onActiveContextChanged?.();
116
+ return existing.window;
117
+ }
118
+ const projectWindow = createWorkbenchWindow({ config, rendererDir, appServices, router }, project);
119
+ const { window, context } = projectWindow;
120
+ windows.set(project.path, projectWindow);
121
+ // Reveal gate: the window is created hidden (see `createWorkbenchWindow`)
122
+ // and must stay hidden until BOTH the renderer has painted its first
123
+ // frame (`ready-to-show`) and `setupProjectWindow` has resolved —
124
+ // whichever finishes second triggers the reveal, exactly once. Wired
125
+ // before any `await` below so a renderer that paints before the hook
126
+ // settles (the common case) is never missed.
127
+ let rendererReady = false;
128
+ let setupDone = false;
129
+ let revealed = false;
130
+ // Flipped the moment a close is requested, not when teardown runs: the
131
+ // close queues behind this open on the same path, so the hook can still
132
+ // resolve first and would otherwise show a window whose close was
133
+ // already asked for, only for the queued teardown to destroy it.
134
+ let closeRequested = false;
135
+ const revealIfReady = () => {
136
+ if (revealed || !rendererReady || !setupDone)
137
+ return;
138
+ if (window.isDestroyed())
139
+ return;
140
+ // Same for a manager already tearing everything down: `disposeAll()`
141
+ // waits for this open to settle, and the window must not flash on the
142
+ // way out.
143
+ if (closeRequested || disposed)
144
+ return;
145
+ // Permanent opt-out: the framework must never show this window, no
146
+ // matter how many times the two gates above are satisfied.
147
+ if (config.projectWindow?.autoShow === false)
148
+ return;
149
+ revealed = true;
150
+ revealMainWindow(window);
151
+ };
152
+ window.once('ready-to-show', () => {
153
+ rendererReady = true;
154
+ revealIfReady();
155
+ });
156
+ const teardown = async () => {
157
+ // A close queued behind an open that then failed (or lost to
158
+ // `disposeAll()`) finds the window already removed and torn down by
159
+ // that open's own undo path. Nothing is left to close, and the host
160
+ // hook must not be handed a disposed context and a destroyed window.
161
+ if (windows.get(project.path) !== projectWindow)
162
+ return;
163
+ try {
164
+ await deps.onBeforeClose?.(projectWindow, project);
165
+ }
166
+ catch (err) {
167
+ // The hook is the host's chance to react, not a veto: everything below
168
+ // (compile session, bridge router, editor server, IPC and app-level
169
+ // listeners) has to go, and the map entry with it, or the window's
170
+ // resources outlive a window nothing can reach any more.
171
+ console.error('[workbench] host onBeforeClose hook failed:', err);
172
+ }
173
+ try {
174
+ await projectWindow.dispose();
175
+ }
176
+ finally {
177
+ // Dropped LAST: while teardown is in flight this project still has a
178
+ // window, and an open arriving mid-close must queue behind it (see
179
+ // `enqueue`) instead of finding an empty map and building a second one.
180
+ if (windows.get(project.path) === projectWindow)
181
+ windows.delete(project.path);
182
+ }
183
+ // After the map entry is gone, so listeners read the window that is left.
184
+ deps.onActiveContextChanged?.();
185
+ };
186
+ try {
187
+ // The standalone internal DevTools window debugs THIS window's renderer,
188
+ // so it is per-window wiring. `isAppQuitting` lets the controller stop
189
+ // intercepting 'close' during a real quit.
190
+ context.internalDevtoolsWindow = createInternalDevtoolsWindow(window, { isAppQuitting });
191
+ context.registry.add(toDisposable(() => context.internalDevtoolsWindow?.dispose()));
192
+ // Wired before the awaited steps below: the window is already closable
193
+ // even though it's still hidden behind the reveal gate above, so it
194
+ // must never exist without the close handling that disposes it.
195
+ context.registry.add(wireWorkbenchWindowEvents(window, context, () => {
196
+ closeRequested = true;
197
+ return enqueue(project.path, teardown);
198
+ }));
199
+ // Whichever workbench window the user is looking at answers the IPC that
200
+ // no single window owns (menus, app-level host windows).
201
+ const onFocus = () => {
202
+ router.setActive(context);
203
+ deps.onActiveContextChanged?.();
204
+ };
205
+ window.on('focus', onFocus);
206
+ context.registry.add(toDisposable(() => {
207
+ if (!window.isDestroyed())
208
+ window.removeListener('focus', onFocus);
209
+ }));
210
+ // Order matters: the bridge router assigns ctx.bridge / ctx.consoleForwarder
211
+ // / ctx.diagnostics, which both the mirrors and the runtime services read.
212
+ deps.setupWindowModules(context);
213
+ installGlobalMirrors(context, window);
214
+ const getActiveAppId = createActiveAppIdResolver(context);
215
+ setupWindowRuntimeServices(context, window, getActiveAppId, project.path);
216
+ await setupEditorView(config, context, getActiveAppId);
217
+ // `disposeAll()` can land while this open is parked above. It waits for
218
+ // this task to settle rather than reaching into a half-built window, so
219
+ // the undo below is this open's own job.
220
+ if (disposed)
221
+ throw disposedError();
222
+ // Last, so the host configures a window whose own services are all up.
223
+ // It runs while the window is already in `windows`: a hook that opens
224
+ // views or drives the session needs the window reachable, and a failure
225
+ // path below removes the entry again.
226
+ await deps.setupProjectWindow?.(projectWindow, project);
227
+ // Re-checked after the hook for the same reason as before it: the hook
228
+ // is host code of unbounded duration, and an open that lost the race
229
+ // must reject rather than hand back a window `disposeAll()` is about
230
+ // to destroy.
231
+ if (disposed)
232
+ throw disposedError();
233
+ // Does not block `open()` returning: if `ready-to-show` already fired,
234
+ // this reveals now; otherwise it just marks the gate and the pending
235
+ // `ready-to-show` listener above reveals once Chromium paints.
236
+ setupDone = true;
237
+ revealIfReady();
238
+ }
239
+ catch (err) {
240
+ // A half-built window (the editor's http server can fail to bind) leaves
241
+ // live services behind and keeps the project counted as open — which is
242
+ // what the list window reads to decide whether to stay hidden. Undo the
243
+ // whole thing before handing the failure back.
244
+ windows.delete(project.path);
245
+ await projectWindow.dispose().catch((disposeErr) => {
246
+ console.warn('[workbench] failed to dispose a partially opened window:', disposeErr);
247
+ });
248
+ if (!window.isDestroyed())
249
+ window.destroy();
250
+ throw err;
251
+ }
252
+ deps.onActiveContextChanged?.();
253
+ return window;
254
+ }
255
+ return {
256
+ // One project window per RESOLVED absolute path: two differently-spelled
257
+ // strings pointing at the same directory ('/a/b' vs '/a/b/.') must not
258
+ // race two windows (and two compile workers) over one project. Resolving
259
+ // here, before the path reaches `windows`/`pathQueues`/`openWindow`,
260
+ // keeps every downstream lookup keyed on the one canonical spelling.
261
+ open: (project, options) => {
262
+ const normalized = { ...project, path: path.resolve(project.path) };
263
+ // The readiness gate is awaited inside the queued task, not before
264
+ // queuing: opens of one path must keep their arrival order, and a
265
+ // `disposeAll()` landing meanwhile still finds this open in the queue it
266
+ // drains rather than floating loose outside it.
267
+ const awaitReady = options?.awaitReady ?? true;
268
+ return enqueue(normalized.path, async () => {
269
+ if (awaitReady)
270
+ await deps.ready;
271
+ return openWindow(normalized);
272
+ });
273
+ },
274
+ list: () => [...windows.values()],
275
+ activeContext: () => {
276
+ const active = router.active();
277
+ const all = [...windows.values()];
278
+ if (all.some((pw) => pw.context === active))
279
+ return active;
280
+ return all.length > 0 ? all[all.length - 1].context : null;
281
+ },
282
+ disposeAll: async () => {
283
+ // Set before waiting, never after: the in-flight opens and closes being
284
+ // waited on are exactly the code that has to observe the terminal state
285
+ // in order to finish instead of building on.
286
+ disposed = true;
287
+ // Drained repeatedly because settling one entry releases whatever was
288
+ // queued behind it on the same path; new opens reject at their task
289
+ // start, so the queues run dry.
290
+ while (pathQueues.size > 0) {
291
+ await Promise.allSettled([...pathQueues.values()]);
292
+ }
293
+ // Snapshotted only now: an open that lost the race above already removed
294
+ // and disposed itself, and a finished close already dropped its entry,
295
+ // so nothing here is disposed twice.
296
+ const all = [...windows.values()];
297
+ windows.clear();
298
+ // Serial: each teardown closes a devkit session and an http server, and
299
+ // a parallel storm during quit is exactly when those races bite.
300
+ for (const projectWindow of all) {
301
+ await projectWindow.dispose().catch((err) => {
302
+ console.warn('[workbench] failed to tear down workbench window:', err);
303
+ });
304
+ if (!projectWindow.window.isDestroyed())
305
+ projectWindow.window.destroy();
306
+ }
307
+ },
308
+ };
309
+ }
310
+ //# sourceMappingURL=workbench-window.js.map