@dimina-kit/devtools 0.4.0-dev.20260803105710 → 0.4.0-dev.20260828092243

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 (208) hide show
  1. package/dist/main/api.d.ts +1 -1
  2. package/dist/main/app/app.js +66 -2
  3. package/dist/main/index.bundle.js +1454 -540
  4. package/dist/main/ipc/index.d.ts +3 -0
  5. package/dist/main/ipc/index.js +3 -0
  6. package/dist/main/ipc/popover.js +1 -1
  7. package/dist/main/ipc/project-create.d.ts +15 -0
  8. package/dist/main/ipc/project-create.js +26 -0
  9. package/dist/main/ipc/projects.d.ts +1 -1
  10. package/dist/main/ipc/projects.js +11 -0
  11. package/dist/main/ipc/session.js +11 -0
  12. package/dist/main/ipc/settings.js +2 -1
  13. package/dist/main/ipc/simulator-module.d.ts +4 -3
  14. package/dist/main/ipc/simulator-module.js +4 -5
  15. package/dist/main/ipc/tooltip.d.ts +13 -0
  16. package/dist/main/ipc/tooltip.js +28 -0
  17. package/dist/main/ipc/views.d.ts +15 -1
  18. package/dist/main/ipc/views.js +61 -4
  19. package/dist/main/runtime/miniapp-runtime.d.ts +87 -19
  20. package/dist/main/services/layout/index.d.ts +11 -0
  21. package/dist/main/services/layout/index.js +23 -0
  22. package/dist/main/services/network-forward/frontend-dispatch.js +2 -1
  23. package/dist/main/services/notifications/renderer-notifier.d.ts +32 -1
  24. package/dist/main/services/notifications/renderer-notifier.js +29 -1
  25. package/dist/main/services/projects/project-repository.d.ts +4 -1
  26. package/dist/main/services/projects/project-repository.js +63 -5
  27. package/dist/main/services/simulator-current-page/index.d.ts +5 -3
  28. package/dist/main/services/simulator-current-page/index.js +8 -1
  29. package/dist/main/services/update/update-manager.d.ts +17 -3
  30. package/dist/main/services/update/update-manager.js +25 -6
  31. package/dist/main/services/views/clear-console-filter.d.ts +44 -0
  32. package/dist/main/services/views/clear-console-filter.js +86 -0
  33. package/dist/main/services/views/console-filter-test-fixtures.d.ts +94 -0
  34. package/dist/main/services/views/console-filter-test-fixtures.js +82 -0
  35. package/dist/main/services/views/console-filter.d.ts +79 -130
  36. package/dist/main/services/views/console-filter.js +140 -183
  37. package/dist/main/services/views/devtools-tabs.d.ts +24 -4
  38. package/dist/main/services/views/devtools-tabs.js +40 -21
  39. package/dist/main/services/views/host-dialog-port-channel.d.ts +18 -0
  40. package/dist/main/services/views/host-dialog-port-channel.js +15 -0
  41. package/dist/main/services/views/host-dialog-session-runtime.d.ts +10 -0
  42. package/dist/main/services/views/host-dialog-session-runtime.js +17 -0
  43. package/dist/main/services/views/host-dialog-view.d.ts +95 -0
  44. package/dist/main/services/views/host-dialog-view.js +147 -0
  45. package/dist/main/services/views/host-sidebar-port-channel.d.ts +18 -0
  46. package/dist/main/services/views/host-sidebar-port-channel.js +15 -0
  47. package/dist/main/services/views/host-sidebar-session-runtime.d.ts +10 -0
  48. package/dist/main/services/views/host-sidebar-session-runtime.js +17 -0
  49. package/dist/main/services/views/host-sidebar-view.d.ts +59 -0
  50. package/dist/main/services/views/host-sidebar-view.js +44 -0
  51. package/dist/main/services/views/host-slot-port-channel.d.ts +107 -0
  52. package/dist/main/services/views/host-slot-port-channel.js +261 -0
  53. package/dist/main/services/views/host-slot-session-runtime.d.ts +40 -0
  54. package/dist/main/services/views/host-slot-session-runtime.js +50 -0
  55. package/dist/main/services/views/host-slot-view.d.ts +109 -0
  56. package/dist/main/services/views/host-slot-view.js +147 -0
  57. package/dist/main/services/views/host-toolbar-port-channel.d.ts +6 -91
  58. package/dist/main/services/views/host-toolbar-port-channel.js +10 -243
  59. package/dist/main/services/views/host-toolbar-session-runtime.d.ts +5 -30
  60. package/dist/main/services/views/host-toolbar-session-runtime.js +9 -47
  61. package/dist/main/services/views/host-toolbar-view.d.ts +34 -7
  62. package/dist/main/services/views/host-toolbar-view.js +30 -237
  63. package/dist/main/services/views/managed-web-contents-view.d.ts +48 -0
  64. package/dist/main/services/views/managed-web-contents-view.js +123 -0
  65. package/dist/main/services/views/native-simulator-devtools-host.js +64 -45
  66. package/dist/main/services/views/native-simulator-view.js +6 -24
  67. package/dist/main/services/views/native-view-tree.d.ts +25 -0
  68. package/dist/main/services/views/native-view-tree.js +69 -0
  69. package/dist/main/services/views/overlay-panels-view.d.ts +50 -4
  70. package/dist/main/services/views/overlay-panels-view.js +226 -108
  71. package/dist/main/services/views/placement-reconciler.d.ts +18 -18
  72. package/dist/main/services/views/placement-reconciler.js +91 -29
  73. package/dist/main/services/views/placement-test-driver.d.ts +27 -0
  74. package/dist/main/services/views/placement-test-driver.js +29 -0
  75. package/dist/main/services/views/view-manager-devtools-host-test-fixtures.js +3 -1
  76. package/dist/main/services/views/view-manager.d.ts +14 -86
  77. package/dist/main/services/views/view-manager.js +41 -4
  78. package/dist/main/services/views/workbench-view.js +27 -20
  79. package/dist/main/services/workbench-coi-server.d.ts +11 -0
  80. package/dist/main/services/workbench-coi-server.js +32 -0
  81. package/dist/main/services/workspace/rebuild-status.d.ts +21 -8
  82. package/dist/main/services/workspace/rebuild-status.js +24 -8
  83. package/dist/main/utils/paths.d.ts +4 -0
  84. package/dist/main/utils/paths.js +4 -0
  85. package/dist/main/utils/sender-policy.d.ts +1 -1
  86. package/dist/main/utils/sender-policy.js +16 -1
  87. package/dist/main/utils/theme.d.ts +7 -2
  88. package/dist/main/utils/theme.js +8 -3
  89. package/dist/native-host/common/common.js +65 -62
  90. package/dist/native-host/container/pageFrame.css +1 -1
  91. package/dist/native-host/render/render.js +3226 -2898
  92. package/dist/native-host/service/service.js +2 -2
  93. package/dist/preload/runtime/host-dialog-advertiser.d.ts +22 -0
  94. package/dist/preload/runtime/host-dialog-advertiser.js +37 -0
  95. package/dist/preload/runtime/host-dialog-port.d.ts +44 -0
  96. package/dist/preload/runtime/host-dialog-port.js +53 -0
  97. package/dist/preload/runtime/host-dialog-runtime.d.ts +28 -0
  98. package/dist/preload/runtime/host-dialog-runtime.js +36 -0
  99. package/dist/preload/runtime/host-sidebar-advertiser.d.ts +19 -0
  100. package/dist/preload/runtime/host-sidebar-advertiser.js +35 -0
  101. package/dist/preload/runtime/host-sidebar-port.d.ts +44 -0
  102. package/dist/preload/runtime/host-sidebar-port.js +53 -0
  103. package/dist/preload/runtime/host-sidebar-runtime.d.ts +28 -0
  104. package/dist/preload/runtime/host-sidebar-runtime.js +36 -0
  105. package/dist/preload/runtime/host-slot-port-bridge.d.ts +18 -0
  106. package/dist/preload/runtime/host-slot-port-bridge.js +112 -0
  107. package/dist/preload/runtime/host-toolbar-advertiser.d.ts +5 -13
  108. package/dist/preload/runtime/host-toolbar-advertiser.js +18 -38
  109. package/dist/preload/runtime/host-toolbar-port.d.ts +9 -0
  110. package/dist/preload/runtime/host-toolbar-port.js +11 -95
  111. package/dist/preload/runtime/wait-for-slot-root.d.ts +20 -0
  112. package/dist/preload/runtime/wait-for-slot-root.js +52 -0
  113. package/dist/preload/windows/host-dialog-runtime.cjs +353 -0
  114. package/dist/preload/windows/host-dialog-runtime.cjs.map +7 -0
  115. package/dist/preload/windows/host-dialog-runtime.d.ts +2 -0
  116. package/dist/preload/windows/host-dialog-runtime.js +13 -0
  117. package/dist/preload/windows/host-sidebar-runtime.cjs +354 -0
  118. package/dist/preload/windows/host-sidebar-runtime.cjs.map +7 -0
  119. package/dist/preload/windows/host-sidebar-runtime.d.ts +2 -0
  120. package/dist/preload/windows/host-sidebar-runtime.js +13 -0
  121. package/dist/preload/windows/host-toolbar-runtime.cjs +134 -51
  122. package/dist/preload/windows/host-toolbar-runtime.cjs.map +3 -3
  123. package/dist/preload/windows/main.cjs +3176 -14
  124. package/dist/preload/windows/main.cjs.map +4 -4
  125. package/dist/preload/windows/main.css +428 -0
  126. package/dist/preload/windows/main.css.map +7 -0
  127. package/dist/preload/windows/simulator.cjs +5 -2
  128. package/dist/preload/windows/simulator.cjs.map +2 -2
  129. package/dist/preload/windows/simulator.js +5 -2
  130. package/dist/renderer/assets/button-DsNqZm7o.js +2 -0
  131. package/dist/renderer/assets/constants-D7DfVqMp.js +2 -0
  132. package/dist/renderer/assets/constants-D8zsejFq.js +2 -0
  133. package/dist/renderer/assets/createLucideIcon-CYJeT1ji.js +2 -0
  134. package/dist/renderer/assets/dialog-pT4qWmka.js +46 -0
  135. package/dist/renderer/assets/dist-o70xmpc1.js +2 -0
  136. package/dist/renderer/assets/hostSidebarDefault-NvGZlE9O.js +2 -0
  137. package/dist/renderer/assets/index-zErhG3ta.js +5 -0
  138. package/dist/renderer/assets/input-4QxTXMZN.js +2 -0
  139. package/dist/renderer/assets/ipc-channels-overlays-Blr5TNoB.js +2 -0
  140. package/dist/renderer/assets/jsx-runtime-B-3rq1y5.css +1 -0
  141. package/dist/renderer/assets/jsx-runtime-CqVdW3eg.js +9 -0
  142. package/dist/renderer/assets/popover-CJiSWwuE.js +2 -0
  143. package/dist/renderer/assets/project-api-DZ1Zi6EC.js +2 -0
  144. package/dist/renderer/assets/projectCreateDialog-DMWNhw_1.js +2 -0
  145. package/dist/renderer/assets/select-C86pNcsu.js +2 -0
  146. package/dist/renderer/assets/settings-BoI8mLT1.js +2 -0
  147. package/dist/renderer/assets/settings-api-Bs_g4NHs.js +2 -0
  148. package/dist/renderer/assets/settings-tab-bar-DHLLygqv.js +2 -0
  149. package/dist/renderer/assets/tooltip-BhAyY0T4.js +2 -0
  150. package/dist/renderer/assets/types-YYjkvane.js +2 -0
  151. package/dist/renderer/assets/updateDialog-DPum3wx1.js +2 -0
  152. package/dist/renderer/assets/utils-kfQov6Pa.js +2 -0
  153. package/dist/renderer/assets/view-api-DFhb2R8-.js +2 -0
  154. package/dist/renderer/assets/workbenchSettings-DrajRQUk.js +8 -0
  155. package/dist/renderer/entries/host-sidebar-default/index.html +21 -0
  156. package/dist/renderer/entries/main/index.html +15 -7
  157. package/dist/renderer/entries/popover/index.html +10 -6
  158. package/dist/renderer/entries/project-create-dialog/index.html +27 -0
  159. package/dist/renderer/entries/settings/index.html +10 -5
  160. package/dist/renderer/entries/tooltip/index.html +21 -0
  161. package/dist/renderer/entries/update-dialog/index.html +25 -0
  162. package/dist/renderer/entries/workbench-settings/index.html +11 -5
  163. package/dist/service-host/preload.cjs +10 -0
  164. package/dist/service-host/sync-impls/menu-button-geometry.js +1 -0
  165. package/dist/shared/constants.d.ts +22 -0
  166. package/dist/shared/constants.js +22 -0
  167. package/dist/shared/ipc-channels-overlays.d.ts +156 -0
  168. package/dist/shared/ipc-channels-overlays.js +186 -0
  169. package/dist/shared/ipc-channels.d.ts +1 -67
  170. package/dist/shared/ipc-channels.js +9 -81
  171. package/dist/shared/ipc-schemas.d.ts +52 -0
  172. package/dist/shared/ipc-schemas.js +64 -0
  173. package/dist/shared/simulator-route.d.ts +8 -0
  174. package/dist/shared/simulator-route.js +15 -0
  175. package/dist/shared/types.d.ts +17 -0
  176. package/dist/shared/view-ids.d.ts +9 -0
  177. package/dist/shared/view-ids.js +21 -0
  178. package/dist/simulator/assets/device-shell-CmtM1QxP.js +2 -0
  179. package/dist/simulator/assets/device-shell-iFZo1igL.css +1 -0
  180. package/dist/simulator/assets/{simulator-COIE6R49.js → simulator-CIobn3Mh.js} +6 -6
  181. package/dist/simulator/assets/{simulator-mini-app-BLn7XV8e.js → simulator-mini-app-CRe6n9br.js} +2 -2
  182. package/dist/simulator/assets/simulator-ui-BupATgKo.css +1 -0
  183. package/dist/simulator/assets/simulator-ui-C_cP602X.js +2 -0
  184. package/dist/simulator/simulator.html +3 -2
  185. package/dist/vscode-workbench/assets/__vite-browser-external-Bc1vizTf.js +1 -0
  186. package/dist/vscode-workbench/assets/{dist-oHcwN53f.js → dist-CMfmHflW.js} +3 -3
  187. package/dist/vscode-workbench/assets/{iconv-lite-umd-CLMLpdGm.js → iconv-lite-umd-Bx8CgaZq.js} +1 -1
  188. package/dist/vscode-workbench/assets/{index-BE3UJdoH.js → index-CdG6YWhW.js} +436 -436
  189. package/dist/vscode-workbench/assets/{jschardet-BICW8PLJ.js → jschardet-M0qgd2lN.js} +1 -1
  190. package/dist/vscode-workbench/index.html +21 -1
  191. package/package.json +9 -8
  192. package/dist/main/services/views/destroy-child-view.d.ts +0 -9
  193. package/dist/main/services/views/destroy-child-view.js +0 -23
  194. package/dist/renderer/assets/constants-EAMiwL9_.js +0 -2
  195. package/dist/renderer/assets/index-BBg3YI5b.js +0 -49
  196. package/dist/renderer/assets/input-xdmfv-eH.js +0 -2
  197. package/dist/renderer/assets/ipc-transport-B3EpmfNS.js +0 -9
  198. package/dist/renderer/assets/ipc-transport-Blac3sOj.css +0 -1
  199. package/dist/renderer/assets/popover-B88e2hkg.js +0 -2
  200. package/dist/renderer/assets/select-CD9CJyhI.js +0 -2
  201. package/dist/renderer/assets/settings-44hf0QtO.js +0 -2
  202. package/dist/renderer/assets/settings-api-pJKPuP1M.js +0 -2
  203. package/dist/renderer/assets/workbenchSettings-DKD5U1Ql.js +0 -8
  204. package/dist/simulator/assets/device-shell-Cw7c-IkG.js +0 -2
  205. package/dist/simulator/assets/device-shell-DbC5ccs8.css +0 -1
  206. package/dist/simulator/assets/jsx-runtime-DkKmJyBb.js +0 -2
  207. package/dist/simulator/device-shell/menu-button-geometry.js +0 -18
  208. package/dist/vscode-workbench/assets/__vite-browser-external-BON7qP59.js +0 -1
@@ -31,6 +31,9 @@ var DEFAULT_CDP_PORT = 9222;
31
31
  var DEFAULT_SCENE = 1001;
32
32
  var HEADER_H = 40;
33
33
  var HOST_TOOLBAR_RUNTIME_MARKER = "--dimina-host-toolbar";
34
+ var HOST_SIDEBAR_RUNTIME_MARKER = "--dimina-host-sidebar";
35
+ var HOST_DIALOG_RUNTIME_MARKER = "--dimina-host-dialog";
36
+ var HOST_SIDEBAR_DEFAULT_WIDTH = 56;
34
37
 
35
38
  // src/main/services/settings/index.ts
36
39
  import { app, nativeTheme } from "electron";
@@ -368,8 +371,11 @@ var SimulatorChannel = {
368
371
  Detach: "simulator:detach",
369
372
  Console: "simulator:console",
370
373
  // Main → renderer push of the visible top-of-stack page route whenever the
371
- // mini-app navigates (navigateTo / switchTab / back). Payload: the page path
372
- // string (same bare format as `getCurrentPagePath`), or '' when unknown.
374
+ // mini-app navigates (navigateTo / switchTab / back). Payload: the page
375
+ // route string (`pagePath` + `?k=v&…` query, same format as
376
+ // `encodePageSpec`/`decodePageSpec`), or '' when unknown. The query lets the
377
+ // page-path bar show params (WeChat DevTools parity) and a recompile restore
378
+ // the current page's params.
373
379
  CurrentPage: "simulator:current-page"
374
380
  };
375
381
  var ServiceHostChannel = {
@@ -439,6 +445,10 @@ var WorkbenchSettingsChannel = {
439
445
  var ProjectChannel = {
440
446
  Open: "project:open",
441
447
  Close: "project:close",
448
+ // Renderer → main invoke: run one real recompile of the active session
449
+ // (the popover 重新编译 button). Resolves `{ supported: boolean }`; rejects
450
+ // on build failure or when no session is active.
451
+ Rebuild: "project:rebuild",
442
452
  GetPages: "project:getPages",
443
453
  GetCompileConfig: "project:getCompileConfig",
444
454
  SaveCompileConfig: "project:saveCompileConfig",
@@ -487,54 +497,6 @@ var ProjectsChannel = {
487
497
  var DialogChannel = {
488
498
  OpenDirectory: "dialog:openDirectory"
489
499
  };
490
- var ViewChannel = {
491
- /**
492
- * Reverse size-advertiser: the host-toolbar WCV's OWN renderer advertises its
493
- * intrinsic content height so main reserves exactly that much. Payload
494
- * `{ axis: 'block', extent }`. fire-and-forget (send), NOT invoke.
495
- */
496
- HostToolbarAdvertiseHeight: "view:host-toolbar:advertise-height",
497
- /**
498
- * main → host-toolbar WCV renderer: per-load MessagePort handshake for the
499
- * gated narrow channel. On every toolbar `did-finish-load` main creates a
500
- * `MessageChannelMain` and transfers port2 here
501
- * (`wc.postMessage(HostToolbarPort, null, [port2])`); the session-resident
502
- * toolbar runtime preload receives it via `event.ports[0]` and bridges it to
503
- * the page as `window.diminaHostToolbar`. Envelope both directions:
504
- * `{ channel: string, payload: unknown }`.
505
- */
506
- HostToolbarPort: "view:host-toolbar:port",
507
- /**
508
- * main → main-window renderer: push the reserved host-toolbar height so the
509
- * renderer placeholder div resizes (closing the dynamic-height loop).
510
- */
511
- HostToolbarHeightChanged: "view:host-toolbar:height-changed",
512
- /**
513
- * main ← main-window renderer (invoke): pull the last NOTIFIED toolbar
514
- * height retained in main. Mount-time replay companion to
515
- * `HostToolbarHeightChanged`: the push listener mounts with the project
516
- * view and the toolbar's size-advertiser deduplicates (never re-reports),
517
- * so a height pushed while no project view was mounted would otherwise be
518
- * lost forever (cold start on the project list races it; close-project →
519
- * reopen hits it deterministically). No payload; resolves a number.
520
- */
521
- HostToolbarGetHeight: "view:host-toolbar:get-height",
522
- /**
523
- * Renderer → main: the window-level placement snapshot (one monotonic epoch
524
- * per commit tick, one generation per renderer lifetime) that drives the view
525
- * reconciler. The single source of truth for every managed native view's
526
- * bounds/visibility/z-order — supersedes the per-view bounds channels above.
527
- * invoke.
528
- */
529
- PlacementSnapshot: "view:placement-snapshot"
530
- };
531
- var PopoverChannel = {
532
- Show: "popover:show",
533
- Hide: "popover:hide",
534
- Relaunch: "popover:relaunch",
535
- Closed: "popover:closed",
536
- Init: "popover:init"
537
- };
538
500
  var WindowChannel = {
539
501
  NavigateBack: "window:navigateBack",
540
502
  OpenProject: "window:openProject",
@@ -557,19 +519,6 @@ var InternalDevtoolsChannel = {
557
519
  // its own dedicated window (see internal-devtools-window/index.ts).
558
520
  Open: "internal-devtools:open"
559
521
  };
560
- var SettingsChannel = {
561
- SetVisible: "settings:setVisible",
562
- ConfigChanged: "settings:configChanged",
563
- ProjectSettingsChanged: "settings:projectSettingsChanged",
564
- Init: "settings:init"
565
- };
566
- var UpdateChannel = {
567
- Check: "updates:check",
568
- Download: "updates:download",
569
- Install: "updates:install",
570
- DownloadProgress: "updates:downloadProgress",
571
- Available: "updates:available"
572
- };
573
522
 
574
523
  // src/main/ipc/project-fs.ts
575
524
  import { WATCH_IGNORE_DIRS } from "@dimina-kit/devkit/watch-ignore";
@@ -904,9 +853,9 @@ function registerProjectFsIpc(ctx) {
904
853
  }
905
854
 
906
855
  // src/main/app/app.ts
907
- import { app as app13, BrowserWindow as BrowserWindow6, nativeImage, session as session2 } from "electron";
856
+ import { app as app13, BrowserWindow as BrowserWindow5, nativeImage, session as session2 } from "electron";
908
857
  import fs10 from "fs";
909
- import path20 from "path";
858
+ import path19 from "path";
910
859
 
911
860
  // src/main/utils/paths.ts
912
861
  import fs3 from "fs";
@@ -937,6 +886,14 @@ var hostToolbarRuntimePreloadPath = path3.join(
937
886
  devtoolsPackageRoot,
938
887
  "dist/preload/windows/host-toolbar-runtime.cjs"
939
888
  );
889
+ var hostSidebarRuntimePreloadPath = path3.join(
890
+ devtoolsPackageRoot,
891
+ "dist/preload/windows/host-sidebar-runtime.cjs"
892
+ );
893
+ var hostDialogRuntimePreloadPath = path3.join(
894
+ devtoolsPackageRoot,
895
+ "dist/preload/windows/host-dialog-runtime.cjs"
896
+ );
940
897
  var simulatorDir = path3.join(devtoolsPackageRoot, "dist/simulator");
941
898
  var runtimeAssetPaths = {
942
899
  root: path3.join(devtoolsPackageRoot, "dist"),
@@ -958,7 +915,7 @@ function themeBg() {
958
915
  return nativeTheme2.shouldUseDarkColors ? "#1a1a1a" : "#fafafa";
959
916
  }
960
917
  function simDeskBg() {
961
- return nativeTheme2.shouldUseDarkColors ? "#121212" : "#e8e8e8";
918
+ return nativeTheme2.shouldUseDarkColors ? "#282828" : "#f7f7f9";
962
919
  }
963
920
  function installThemeBackgroundSync() {
964
921
  const apply = () => {
@@ -1576,6 +1533,12 @@ function createWorkbenchSenderPolicy(ctx) {
1576
1533
  if (settingsViewId != null && sender.id === settingsViewId) return true;
1577
1534
  const popoverViewId = ctx.views.getPopoverWebContentsId();
1578
1535
  if (popoverViewId != null && sender.id === popoverViewId) return true;
1536
+ const tooltipViewId = ctx.views.getTooltipWebContentsId();
1537
+ if (tooltipViewId != null && sender.id === tooltipViewId) return true;
1538
+ const projectCreateDialogViewId = ctx.views.getProjectCreateDialogWebContentsId();
1539
+ if (projectCreateDialogViewId != null && sender.id === projectCreateDialogViewId) return true;
1540
+ const updateDialogViewId = ctx.views.getUpdateDialogWebContentsId();
1541
+ if (updateDialogViewId != null && sender.id === updateDialogViewId) return true;
1579
1542
  return false;
1580
1543
  };
1581
1544
  }
@@ -1595,6 +1558,148 @@ var defaultAdapter = {
1595
1558
  }
1596
1559
  };
1597
1560
 
1561
+ // src/shared/ipc-channels-overlays.ts
1562
+ var ViewChannel = {
1563
+ /**
1564
+ * Reverse size-advertiser: the host-toolbar WCV's OWN renderer advertises its
1565
+ * intrinsic content height so main reserves exactly that much. Payload
1566
+ * `{ axis: 'block', extent }`. fire-and-forget (send), NOT invoke.
1567
+ */
1568
+ HostToolbarAdvertiseHeight: "view:host-toolbar:advertise-height",
1569
+ /**
1570
+ * main → host-toolbar WCV renderer: per-load MessagePort handshake for the
1571
+ * gated narrow channel. On every toolbar `did-finish-load` main creates a
1572
+ * `MessageChannelMain` and transfers port2 here
1573
+ * (`wc.postMessage(HostToolbarPort, null, [port2])`); the session-resident
1574
+ * toolbar runtime preload receives it via `event.ports[0]` and bridges it to
1575
+ * the page as `window.diminaHostToolbar`. Envelope both directions:
1576
+ * `{ channel: string, payload: unknown }`.
1577
+ */
1578
+ HostToolbarPort: "view:host-toolbar:port",
1579
+ /**
1580
+ * main → main-window renderer: push the reserved host-toolbar height so the
1581
+ * renderer placeholder div resizes (closing the dynamic-height loop).
1582
+ */
1583
+ HostToolbarHeightChanged: "view:host-toolbar:height-changed",
1584
+ /**
1585
+ * main ← main-window renderer (invoke): pull the last NOTIFIED toolbar
1586
+ * height retained in main. Mount-time replay companion to
1587
+ * `HostToolbarHeightChanged`: the push listener mounts with the project
1588
+ * view and the toolbar's size-advertiser deduplicates (never re-reports),
1589
+ * so a height pushed while no project view was mounted would otherwise be
1590
+ * lost forever (cold start on the project list races it; close-project →
1591
+ * reopen hits it deterministically). No payload; resolves a number.
1592
+ */
1593
+ HostToolbarGetHeight: "view:host-toolbar:get-height",
1594
+ /**
1595
+ * Reverse size-advertiser for the host-sidebar WCV, mirroring
1596
+ * `HostToolbarAdvertiseHeight` on the inline (width) axis instead of block.
1597
+ * Payload `{ axis: 'inline', extent }`. fire-and-forget (send), NOT invoke.
1598
+ */
1599
+ HostSidebarAdvertiseWidth: "view:host-sidebar:advertise-width",
1600
+ /**
1601
+ * main → host-sidebar WCV renderer: per-load MessagePort handshake, same
1602
+ * contract as `HostToolbarPort` (see that entry).
1603
+ */
1604
+ HostSidebarPort: "view:host-sidebar:port",
1605
+ /**
1606
+ * main → main-window renderer: push the reserved host-sidebar width so the
1607
+ * renderer placeholder div resizes, mirroring `HostToolbarHeightChanged`.
1608
+ */
1609
+ HostSidebarWidthChanged: "view:host-sidebar:width-changed",
1610
+ /**
1611
+ * main ← main-window renderer (invoke): pull the last NOTIFIED sidebar
1612
+ * width, mirroring `HostToolbarGetHeight`'s mount-time replay role.
1613
+ */
1614
+ HostSidebarGetWidth: "view:host-sidebar:get-width",
1615
+ /**
1616
+ * main → main-window renderer: push the project category (`Project.type`)
1617
+ * selected in the host-sidebar's content — devtools' own default icon rail,
1618
+ * or any downstream replacement that sends on the same channel. No
1619
+ * pull/replay companion like `HostSidebarGetWidth`: the default category is
1620
+ * a stable initial value regardless of load-order, and nothing is ever sent
1621
+ * before the main-window renderer (and thus `ProjectListScreen`) is already
1622
+ * mounted.
1623
+ */
1624
+ HostSidebarCategorySelected: "view:host-sidebar:category-selected",
1625
+ /**
1626
+ * Reverse size-advertiser for the host-dialog WCV. Unlike the toolbar/sidebar
1627
+ * (single-axis, `setBaseDesired`-placed strips), the dialog is a BOTH-AXES
1628
+ * overlay positioned by `setOverlayDesired` — main centers it in the main
1629
+ * window once it knows both dimensions. One channel carries either axis'
1630
+ * report (`{ axis: 'block' | 'inline', extent }`); the dialog's own content
1631
+ * advertises both from the same root (see `host-dialog-advertiser.ts`).
1632
+ * fire-and-forget (send), NOT invoke.
1633
+ *
1634
+ * No `HostDialogSizeChanged` / `HostDialogGetSize` counterparts: those exist
1635
+ * for toolbar/sidebar so a renderer-owned PLACEHOLDER can replay a size it
1636
+ * missed. The dialog has no such placeholder — it is a main-driven overlay
1637
+ * shown/hidden on demand (`hostDialog.show()`/`hide()`), so there is nothing
1638
+ * in the renderer to replay a size into.
1639
+ */
1640
+ HostDialogAdvertiseSize: "view:host-dialog:advertise-size",
1641
+ /**
1642
+ * main → host-dialog WCV renderer: per-load MessagePort handshake, same
1643
+ * contract as `HostToolbarPort` (see that entry).
1644
+ */
1645
+ HostDialogPort: "view:host-dialog:port",
1646
+ /**
1647
+ * Renderer → main: the window-level placement snapshot (one monotonic epoch
1648
+ * per commit tick, one generation per renderer lifetime) that drives the view
1649
+ * reconciler. The single source of truth for every managed native view's
1650
+ * bounds/visibility/z-order — supersedes the per-view bounds channels above.
1651
+ * invoke.
1652
+ */
1653
+ PlacementSnapshot: "view:placement-snapshot",
1654
+ /**
1655
+ * main ← main-window renderer (invoke): allocate a fresh placement
1656
+ * generation seed for this renderer bootstrap. See
1657
+ * renderer-placement-generation.ts for why the seed comes from main
1658
+ * (a wall-clock `Date.now()` seed is not guaranteed to exceed main's
1659
+ * still-standing high-water mark across two reloads that happen faster
1660
+ * than the clock's resolution). No payload; resolves a number.
1661
+ */
1662
+ AllocatePlacementGeneration: "view:allocate-placement-generation"
1663
+ };
1664
+ var PopoverChannel = {
1665
+ Show: "popover:show",
1666
+ Hide: "popover:hide",
1667
+ Relaunch: "popover:relaunch",
1668
+ Closed: "popover:closed",
1669
+ Init: "popover:init"
1670
+ };
1671
+ var OverlayChannel = {
1672
+ Ready: "overlay:ready"
1673
+ };
1674
+ var TooltipChannel = {
1675
+ Prepare: "tooltip:prepare",
1676
+ Show: "tooltip:show",
1677
+ Hide: "tooltip:hide",
1678
+ Init: "tooltip:init",
1679
+ Measured: "tooltip:measured"
1680
+ };
1681
+ var ProjectCreateChannel = {
1682
+ Show: "projectCreate:show",
1683
+ Init: "projectCreate:init",
1684
+ Submit: "projectCreate:submit",
1685
+ Cancel: "projectCreate:cancel",
1686
+ Submitted: "projectCreate:submitted"
1687
+ };
1688
+ var SettingsChannel = {
1689
+ SetVisible: "settings:setVisible",
1690
+ ConfigChanged: "settings:configChanged",
1691
+ ProjectSettingsChanged: "settings:projectSettingsChanged",
1692
+ Init: "settings:init"
1693
+ };
1694
+ var UpdateChannel = {
1695
+ Check: "updates:check",
1696
+ Download: "updates:download",
1697
+ Install: "updates:install",
1698
+ DownloadProgress: "updates:downloadProgress",
1699
+ Available: "updates:available",
1700
+ Close: "updates:close"
1701
+ };
1702
+
1598
1703
  // src/main/services/notifications/renderer-notifier.ts
1599
1704
  function liveWebContents(wc) {
1600
1705
  if (!wc) return null;
@@ -1633,14 +1738,38 @@ function createRendererNotifier(ctx) {
1633
1738
  hostToolbarHeightChanged(height) {
1634
1739
  sendToMain(ViewChannel.HostToolbarHeightChanged, height);
1635
1740
  },
1741
+ hostSidebarWidthChanged(width) {
1742
+ sendToMain(ViewChannel.HostSidebarWidthChanged, width);
1743
+ },
1744
+ hostSidebarCategorySelected(category) {
1745
+ sendToMain(ViewChannel.HostSidebarCategorySelected, category);
1746
+ },
1636
1747
  editorOpenFile(payload) {
1637
1748
  sendToMain(EditorChannel.OpenFile, payload);
1638
1749
  },
1750
+ projectCreateSubmitted(payload) {
1751
+ sendToMain(ProjectCreateChannel.Submitted, payload);
1752
+ },
1639
1753
  popoverInit(popoverView, payload) {
1640
1754
  const wc = liveWebContents(popoverView.webContents);
1641
1755
  if (!wc) return;
1642
1756
  wc.send(PopoverChannel.Init, payload);
1643
1757
  },
1758
+ tooltipInit(tooltipView, payload) {
1759
+ const wc = liveWebContents(tooltipView.webContents);
1760
+ if (!wc) return;
1761
+ wc.send(TooltipChannel.Init, payload);
1762
+ },
1763
+ projectCreateInit(dialogView, payload) {
1764
+ const wc = liveWebContents(dialogView.webContents);
1765
+ if (!wc) return;
1766
+ wc.send(ProjectCreateChannel.Init, payload);
1767
+ },
1768
+ updateAvailable(dialogView, payload) {
1769
+ const wc = liveWebContents(dialogView.webContents);
1770
+ if (!wc) return;
1771
+ wc.send(UpdateChannel.Available, payload);
1772
+ },
1644
1773
  settingsInit(payload) {
1645
1774
  const wc = liveWebContents(ctx.views.getSettingsWebContents());
1646
1775
  if (!wc) return;
@@ -1842,14 +1971,79 @@ function applyViewOps(ops, target) {
1842
1971
  }
1843
1972
  }
1844
1973
 
1974
+ // src/main/services/views/native-view-tree.ts
1975
+ function createNativeViewTreeHost(ctx, slots) {
1976
+ const order = [];
1977
+ const mounted = /* @__PURE__ */ new Map();
1978
+ function resolve(id) {
1979
+ return slots.get(id)?.getView() ?? null;
1980
+ }
1981
+ return {
1982
+ addChildView(ref) {
1983
+ const view = resolve(ref.id);
1984
+ if (!view || view.webContents.isDestroyed() || ctx.windows.mainWindow.isDestroyed()) return false;
1985
+ try {
1986
+ ctx.windows.mainWindow.contentView.addChildView(view);
1987
+ } catch {
1988
+ return false;
1989
+ }
1990
+ const i = order.indexOf(ref.id);
1991
+ if (i >= 0) order.splice(i, 1);
1992
+ order.push(ref.id);
1993
+ mounted.set(ref.id, view);
1994
+ return true;
1995
+ },
1996
+ removeChildView(ref) {
1997
+ const view = mounted.get(ref.id) ?? null;
1998
+ if (!view || ctx.windows.mainWindow.isDestroyed()) return false;
1999
+ try {
2000
+ ctx.windows.mainWindow.contentView.removeChildView(view);
2001
+ } catch {
2002
+ return false;
2003
+ }
2004
+ const i = order.indexOf(ref.id);
2005
+ if (i >= 0) order.splice(i, 1);
2006
+ mounted.delete(ref.id);
2007
+ return true;
2008
+ },
2009
+ get isDestroyed() {
2010
+ return ctx.windows.mainWindow.isDestroyed();
2011
+ },
2012
+ children: () => order.map((id) => ({ id })),
2013
+ destroyView(viewId, view) {
2014
+ const isMountedInstance = view !== null && mounted.get(viewId) === view;
2015
+ if (view && !ctx.windows.mainWindow.isDestroyed()) {
2016
+ try {
2017
+ ctx.windows.mainWindow.contentView.removeChildView(view);
2018
+ } catch {
2019
+ }
2020
+ }
2021
+ if (isMountedInstance) {
2022
+ const i = order.indexOf(viewId);
2023
+ if (i >= 0) order.splice(i, 1);
2024
+ mounted.delete(viewId);
2025
+ }
2026
+ if (view) {
2027
+ try {
2028
+ if (!view.webContents.isDestroyed()) view.webContents.close();
2029
+ } catch {
2030
+ }
2031
+ }
2032
+ }
2033
+ };
2034
+ }
2035
+
1845
2036
  // src/main/services/views/placement-reconciler.ts
1846
2037
  function createPlacementReconciler(ctx) {
1847
2038
  let placementState = createInitialState();
1848
2039
  let epochCounter = 0;
1849
2040
  let rendererGeneration = 0;
2041
+ let allocatedGeneration = 0;
2042
+ const appliedActual = /* @__PURE__ */ new Map();
1850
2043
  const baseDesired = /* @__PURE__ */ new Map();
1851
2044
  const overlayDesired = /* @__PURE__ */ new Map();
1852
2045
  const slots = /* @__PURE__ */ new Map();
2046
+ const contentViewHost = createNativeViewTreeHost(ctx, slots);
1853
2047
  function gateReadiness(v) {
1854
2048
  const slot = slots.get(v.viewId);
1855
2049
  if (slot?.gateHidden?.()) return { ...v, placement: { visible: false } };
@@ -1860,53 +2054,54 @@ function createPlacementReconciler(ctx) {
1860
2054
  if (ctx.windows.mainWindow.isDestroyed()) return;
1861
2055
  const slot = slots.get(viewId);
1862
2056
  if (!slot) return;
1863
- if (slot.beforeAttach?.()) return;
1864
2057
  const view = slot.ensureView ? slot.ensureView() : slot.getView();
1865
2058
  if (!view) return;
1866
- ctx.windows.mainWindow.contentView.addChildView(view);
1867
- slot.setAdded?.(true);
2059
+ if (!contentViewHost.addChildView({ id: viewId })) return;
2060
+ const previous = appliedActual.get(viewId);
2061
+ appliedActual.set(viewId, {
2062
+ attached: true,
2063
+ visible: true,
2064
+ bounds: previous?.bounds,
2065
+ extra: previous?.extra
2066
+ });
1868
2067
  },
1869
2068
  detach(viewId) {
1870
- const slot = slots.get(viewId);
1871
- const view = slot?.getView() ?? null;
1872
- if (view && !ctx.windows.mainWindow.isDestroyed()) {
1873
- try {
1874
- ctx.windows.mainWindow.contentView.removeChildView(view);
1875
- } catch {
1876
- }
1877
- }
1878
- slot?.setAdded?.(false);
2069
+ if (!contentViewHost.removeChildView({ id: viewId })) return;
2070
+ appliedActual.delete(viewId);
1879
2071
  },
1880
2072
  setBounds(viewId, bounds, extra) {
1881
2073
  const slot = slots.get(viewId);
1882
2074
  const view = slot?.getView() ?? null;
1883
2075
  if (!view || view.webContents.isDestroyed()) return;
1884
- if (slot?.applyBounds) {
1885
- slot.applyBounds(view, bounds, extra);
2076
+ try {
2077
+ if (slot?.applyBounds) slot.applyBounds(view, bounds, extra);
2078
+ else view.setBounds(bounds);
2079
+ } catch {
1886
2080
  return;
1887
2081
  }
1888
- view.setBounds(bounds);
2082
+ const previous = appliedActual.get(viewId);
2083
+ appliedActual.set(viewId, {
2084
+ attached: previous?.attached ?? false,
2085
+ visible: previous?.visible ?? false,
2086
+ bounds,
2087
+ extra
2088
+ });
1889
2089
  },
1890
2090
  setVisible(viewId, visible) {
1891
2091
  const view = slots.get(viewId)?.getView() ?? null;
1892
- if (!view) return;
2092
+ if (!view || view.webContents.isDestroyed()) return;
1893
2093
  try {
1894
2094
  view.setVisible(visible);
1895
2095
  } catch {
2096
+ return;
1896
2097
  }
2098
+ const previous = appliedActual.get(viewId);
2099
+ if (!previous) return;
2100
+ appliedActual.set(viewId, { ...previous, visible });
1897
2101
  },
1898
2102
  reorder(order) {
1899
2103
  if (order.length <= 1 || ctx.windows.mainWindow.isDestroyed()) return;
1900
- const cv = ctx.windows.mainWindow.contentView;
1901
- for (const viewId of order) {
1902
- const view = slots.get(viewId)?.getView() ?? null;
1903
- if (view) {
1904
- try {
1905
- cv.addChildView(view);
1906
- } catch {
1907
- }
1908
- }
1909
- }
2104
+ for (const viewId of order) contentViewHost.addChildView({ id: viewId });
1910
2105
  }
1911
2106
  };
1912
2107
  function ensureLazyViews() {
@@ -1914,11 +2109,15 @@ function createPlacementReconciler(ctx) {
1914
2109
  slot.ensureLazy?.(baseDesired.get(viewId));
1915
2110
  }
1916
2111
  }
2112
+ function cloneAppliedActual() {
2113
+ return new Map([...appliedActual].map(([viewId, actual]) => [viewId, { ...actual }]));
2114
+ }
1917
2115
  function reconcileNow() {
1918
2116
  ensureLazyViews();
1919
2117
  const views = [];
1920
2118
  for (const v of baseDesired.values()) views.push(gateReadiness(v));
1921
2119
  for (const v of overlayDesired.values()) views.push(v);
2120
+ placementState = { ...placementState, actual: cloneAppliedActual() };
1922
2121
  const result = reconcile(placementState, {
1923
2122
  generation: rendererGeneration,
1924
2123
  epoch: ++epochCounter,
@@ -1926,8 +2125,15 @@ function createPlacementReconciler(ctx) {
1926
2125
  });
1927
2126
  placementState = result.state;
1928
2127
  applyViewOps(result.ops, viewTarget);
2128
+ placementState = { ...placementState, actual: cloneAppliedActual() };
1929
2129
  }
1930
2130
  function setPlacementSnapshot(snapshot) {
2131
+ if (snapshot.generation < rendererGeneration) {
2132
+ console.warn(
2133
+ `[placement-reconciler] snapshot generation ${snapshot.generation} is behind the current ${rendererGeneration}; dropping without applying its content`
2134
+ );
2135
+ return;
2136
+ }
1931
2137
  rendererGeneration = snapshot.generation;
1932
2138
  baseDesired.clear();
1933
2139
  for (const v of snapshot.views) baseDesired.set(v.viewId, v);
@@ -1939,7 +2145,18 @@ function createPlacementReconciler(ctx) {
1939
2145
  },
1940
2146
  reconcileNow,
1941
2147
  setPlacementSnapshot,
1942
- forgetActual: (viewId) => {
2148
+ prepareView: (viewId, view, bounds) => {
2149
+ if (slots.get(viewId)?.getView() !== view || view.webContents.isDestroyed()) return;
2150
+ try {
2151
+ view.setBounds(bounds);
2152
+ } catch {
2153
+ return;
2154
+ }
2155
+ appliedActual.set(viewId, { attached: false, visible: false, bounds });
2156
+ },
2157
+ destroyView: (viewId, view) => {
2158
+ contentViewHost.destroyView(viewId, view);
2159
+ appliedActual.delete(viewId);
1943
2160
  placementState.actual.delete(viewId);
1944
2161
  },
1945
2162
  setBaseDesired: (viewId, desired) => {
@@ -1954,7 +2171,11 @@ function createPlacementReconciler(ctx) {
1954
2171
  deleteOverlayDesired: (viewId) => {
1955
2172
  overlayDesired.delete(viewId);
1956
2173
  },
1957
- hasOverlayDesired: (viewId) => overlayDesired.has(viewId)
2174
+ hasOverlayDesired: (viewId) => overlayDesired.has(viewId),
2175
+ allocateGeneration: () => {
2176
+ allocatedGeneration = Math.max(allocatedGeneration, rendererGeneration) + 1;
2177
+ return allocatedGeneration;
2178
+ }
1958
2179
  };
1959
2180
  }
1960
2181
 
@@ -1967,33 +2188,28 @@ var VIEW_ID = {
1967
2188
  simulatorDevtools: "simulator-devtools",
1968
2189
  workbench: "workbench",
1969
2190
  hostToolbar: "host-toolbar",
2191
+ hostSidebar: "host-sidebar",
2192
+ hostDialog: "host-dialog",
1970
2193
  settings: "settings",
1971
- popover: "popover"
2194
+ popover: "popover",
2195
+ tooltip: "tooltip",
2196
+ projectCreateDialog: "project-create-dialog",
2197
+ updateDialog: "update-dialog"
1972
2198
  };
1973
2199
  var VIEW_LAYER = {
1974
2200
  base: 0,
1975
2201
  hostToolbar: 5,
2202
+ hostSidebar: 5,
1976
2203
  settings: 10,
1977
- popover: 20
2204
+ popover: 20,
2205
+ tooltip: 30,
2206
+ // devtools' own ProjectCreateDialog/UpdateDialog panels (createOverlayPanel).
2207
+ dialog: 40,
2208
+ // Host-controlled fully-custom dialog (loadURL-level takeover); sits above
2209
+ // devtools' own dialog layer so host content is never occluded by it.
2210
+ hostDialog: 45
1978
2211
  };
1979
2212
 
1980
- // src/main/services/views/destroy-child-view.ts
1981
- function destroyChildView(mainWindow, view) {
1982
- if (!view) return;
1983
- if (!mainWindow.isDestroyed()) {
1984
- try {
1985
- mainWindow.contentView.removeChildView(view);
1986
- } catch {
1987
- }
1988
- }
1989
- try {
1990
- if (!view.webContents.isDestroyed()) {
1991
- view.webContents.close();
1992
- }
1993
- } catch {
1994
- }
1995
- }
1996
-
1997
2213
  // src/main/services/views/workbench-view.ts
1998
2214
  var ATTACH_HOLD_CAP_MS = 3e3;
1999
2215
  function createWorkbenchView(ctx, reconciler) {
@@ -2009,6 +2225,10 @@ function createWorkbenchView(ctx, reconciler) {
2009
2225
  function pushWorkbenchTheme() {
2010
2226
  if (!workbenchView || workbenchView.webContents.isDestroyed()) return;
2011
2227
  const wc = workbenchView.webContents;
2228
+ try {
2229
+ if (typeof workbenchView.setBackgroundColor === "function") workbenchView.setBackgroundColor(themeBg());
2230
+ } catch {
2231
+ }
2012
2232
  if (typeof wc.executeJavaScript !== "function") return;
2013
2233
  const script = `window.__WB_SET_THEME && window.__WB_SET_THEME(${JSON.stringify(workbenchThemeScheme())})`;
2014
2234
  wc.executeJavaScript(script, true).catch(() => {
@@ -2024,6 +2244,10 @@ function createWorkbenchView(ctx, reconciler) {
2024
2244
  }
2025
2245
  });
2026
2246
  workbenchView = view;
2247
+ try {
2248
+ view.setBackgroundColor(themeBg());
2249
+ } catch {
2250
+ }
2027
2251
  if (!workbenchThemeSyncBound) {
2028
2252
  nativeTheme3.on("updated", pushWorkbenchTheme);
2029
2253
  workbenchThemeSyncBound = true;
@@ -2032,7 +2256,6 @@ function createWorkbenchView(ctx, reconciler) {
2032
2256
  view.webContents.setWindowOpenHandler(({ url: target }) => handleWindowOpenExternal(target));
2033
2257
  } catch {
2034
2258
  }
2035
- ctx.windows.mainWindow.contentView.addChildView(view);
2036
2259
  const loadUrl = `${url}index.html?theme=${workbenchThemeScheme()}`;
2037
2260
  await view.webContents.loadURL(loadUrl).catch((err2) => {
2038
2261
  console.error("[workbench] attachWorkbench \u2014 loadURL failed", err2);
@@ -2076,11 +2299,9 @@ function createWorkbenchView(ctx, reconciler) {
2076
2299
  nativeTheme3.removeListener("updated", pushWorkbenchTheme);
2077
2300
  workbenchThemeSyncBound = false;
2078
2301
  }
2079
- destroyChildView(ctx.windows.mainWindow, workbenchView);
2302
+ reconciler.destroyView(VIEW_ID.workbench, workbenchView);
2080
2303
  workbenchView = null;
2081
- if (attachHeld) {
2082
- reconciler.forgetActual(VIEW_ID.workbench);
2083
- } else {
2304
+ if (!attachHeld) {
2084
2305
  reconciler.deleteBaseDesired(VIEW_ID.workbench);
2085
2306
  }
2086
2307
  reconciler.reconcileNow();
@@ -2125,13 +2346,6 @@ function createWorkbenchView(ctx, reconciler) {
2125
2346
  reconciler.registerView(VIEW_ID.workbench, {
2126
2347
  getView: () => workbenchView,
2127
2348
  gateHidden: () => !workbenchView && (!workbenchUrl || attachHeld),
2128
- beforeAttach: () => {
2129
- if (!workbenchView && workbenchUrl && !attachHeld) {
2130
- void attachWorkbench(workbenchUrl);
2131
- return true;
2132
- }
2133
- return false;
2134
- },
2135
2349
  ensureLazy: (desired) => {
2136
2350
  if (desired?.placement.visible && !workbenchView && workbenchUrl && !attachHeld) void attachWorkbench(workbenchUrl);
2137
2351
  }
@@ -2535,25 +2749,20 @@ function buildDevtoolsProjectSourceLinksScript(context) {
2535
2749
  }
2536
2750
 
2537
2751
  // src/main/services/views/devtools-tabs.ts
2538
- var DEVTOOLS_KEPT_VIEW_IDS = ["elements", "console", "network", "sources"];
2752
+ var DEVTOOLS_KEPT_VIEW_IDS = ["elements", "console", "sources", "network"];
2539
2753
  function buildCustomizeTabsScript(keptIds = DEVTOOLS_KEPT_VIEW_IDS) {
2540
2754
  const NAME_MAP = {
2541
2755
  elements: ["Elements", "\u5143\u7D20"],
2542
2756
  console: ["Console", "\u63A7\u5236\u53F0"],
2543
- network: ["Network", "\u7F51\u7EDC"],
2544
- sources: ["Sources", "\u6765\u6E90", "\u6E90\u4EE3\u7801"]
2757
+ sources: ["Sources", "\u6765\u6E90", "\u6E90\u4EE3\u7801"],
2758
+ network: ["Network", "\u7F51\u7EDC"]
2545
2759
  };
2546
2760
  const keepNames = keptIds.flatMap((id) => NAME_MAP[id] ?? [id]);
2547
2761
  const keepIdsJson = JSON.stringify(JSON.stringify([...keptIds]));
2548
2762
  const keepNamesJson = JSON.stringify(JSON.stringify(keepNames));
2549
- const srcNamesJson = JSON.stringify(JSON.stringify(NAME_MAP.sources));
2550
- const netNamesJson = JSON.stringify(JSON.stringify(NAME_MAP.network));
2551
2763
  return `(function(){try{
2552
2764
  var KEEPID = new Set(JSON.parse(${keepIdsJson}));
2553
2765
  var KEEPNAME = new Set(JSON.parse(${keepNamesJson}));
2554
- var SRCNAME = new Set(JSON.parse(${srcNamesJson}));
2555
- var NETNAME = new Set(JSON.parse(${netNamesJson}));
2556
- var WANT_SOURCES_LAST = KEEPID.has('sources');
2557
2766
  // (1) Locale infobar \u2014 official host-preference suppression (NOT localStorage).
2558
2767
  try { var IFH=globalThis.InspectorFrontendHost; if(IFH&&typeof IFH.setPreference==='function'){ IFH.setPreference('disable-locale-info-bar','true'); } }catch(_){}
2559
2768
  function deepCollect(sel,cap){ var out=[],seen=0,stack=[document]; while(stack.length&&seen<(cap||40000)){ var root=stack.pop(); try{var m=root.querySelectorAll?root.querySelectorAll(sel):[];for(var i=0;i<m.length;i++)out.push(m[i]);}catch(_){} try{var all=root.querySelectorAll?root.querySelectorAll('*'):[];for(var j=0;j<all.length;j++){seen++;if(all[j].shadowRoot)stack.push(all[j].shadowRoot);}}catch(_){} } return out; }
@@ -2584,15 +2793,6 @@ function buildCustomizeTabsScript(keptIds = DEVTOOLS_KEPT_VIEW_IDS) {
2584
2793
  if(!handled){ var FALL=['timeline','resources','heap-profiler','security','lighthouse','chrome-recorder','coverage','linear-memory-inspector','sensors','rendering','animations','autofill-view','medias','issues-pane']; for(var fr=0;fr<FALL.length;fr++){ try{ if(!KEEPID.has(FALL[fr])) maybeRemove(FALL[fr]); }catch(_){} } }
2585
2794
  } else { domFallback(); }
2586
2795
  })();
2587
- // Keep Sources LAST in the bar (after Network). Default DevTools order puts
2588
- // Sources between Console and Network; nudge it to just after Network. Runs in
2589
- // BOTH paths (registry removal leaves the kept tabs in default order). Self-
2590
- // stable: once Sources follows Network the condition no longer holds.
2591
- if(WANT_SOURCES_LAST){ var ro=0,rt=setInterval(function(){ ro++; try{
2592
- var tabs=deepCollect('[role="tab"]'),src=null,net=null;
2593
- for(var i=0;i<tabs.length;i++){ var n=txt(tabs[i]); if(SRCNAME.has(n))src=tabs[i]; else if(NETNAME.has(n))net=tabs[i]; }
2594
- if(src&&net&&src.parentElement&&src.parentElement===net.parentElement&&(src.compareDocumentPosition(net)&Node.DOCUMENT_POSITION_FOLLOWING)){ net.parentElement.insertBefore(src,net.nextSibling); }
2595
- }catch(_){} if(ro>120)clearInterval(rt); },60); }
2596
2796
  // DOM fallback: only if the ESM module couldn't be resolved at all.
2597
2797
  function domFallback(){ var cachedBar=null;
2598
2798
  function findBar(){ if(cachedBar&&cachedBar.isConnected)return cachedBar; cachedBar=null; var tabs=deepCollect('[role="tab"]'); var a=null; for(var i=0;i<tabs.length;i++){if(KEEPNAME.has(txt(tabs[i]))){a=tabs[i];break;}} if(!a)return null; var p=a.parentElement; while(p){try{if(p.querySelectorAll('[role="tab"]').length>1){cachedBar=p;return p;}}catch(_){} p=p.parentElement;} cachedBar=a.parentElement; return cachedBar; }
@@ -2600,41 +2800,34 @@ function buildCustomizeTabsScript(keptIds = DEVTOOLS_KEPT_VIEW_IDS) {
2600
2800
  var tr=0,t=setInterval(function(){tr++;try{apply();}catch(_){}if(tr>120)clearInterval(t);},60); try{apply();}catch(_){} }
2601
2801
  }catch(_){}})()`;
2602
2802
  }
2803
+ function buildDevtoolsSoftMenuScript() {
2804
+ return `(function(){
2805
+ try {
2806
+ var C = globalThis.EUI && globalThis.EUI.ContextMenu && globalThis.EUI.ContextMenu.ContextMenu;
2807
+ if (C && C.prototype && typeof C.prototype.show === 'function') {
2808
+ var origShow = C.prototype.show;
2809
+ C.prototype.show = function() {
2810
+ this.useSoftMenu = true;
2811
+ return origShow.apply(this, arguments);
2812
+ };
2813
+ }
2814
+ } catch (_) {}
2815
+ })()`;
2816
+ }
2603
2817
 
2604
2818
  // src/main/services/views/console-filter.ts
2605
- var DEFAULT_INTERNAL_LOG_FILTER = "-/^\\[service\\]/";
2606
- function buildConsoleFilterScript(negativeFilter = DEFAULT_INTERNAL_LOG_FILTER) {
2607
- const keyJson = JSON.stringify("console.text-filter");
2608
- const markKeyJson = JSON.stringify("console.text-filter.dimina-default");
2609
- const valueJsonLiteral = JSON.stringify(JSON.stringify(negativeFilter));
2610
- return `(function(){try{
2611
- var IFH = globalThis.InspectorFrontendHost;
2612
- if (!IFH || typeof IFH.getPreferences !== 'function' || typeof IFH.setPreference !== 'function') return;
2613
- IFH.getPreferences(function(prefs){
2614
- try {
2615
- var KEY = ${keyJson};
2616
- var MARK_KEY = ${markKeyJson};
2617
- var VALUE = ${valueJsonLiteral};
2618
- var existing = prefs ? prefs[KEY] : undefined;
2619
- var mark = prefs ? prefs[MARK_KEY] : undefined;
2620
- var isUnset = existing === undefined || existing === null || existing === '' || existing === '""';
2621
- var isOurStaleDefault = !isUnset && mark !== undefined && mark !== null && existing === mark;
2622
- if (isUnset || isOurStaleDefault) {
2623
- IFH.setPreference(KEY, VALUE);
2624
- IFH.setPreference(MARK_KEY, VALUE);
2625
- }
2626
- } catch(_){}
2627
- });
2628
- }catch(_){}})()`;
2629
- }
2819
+ var INTERNAL_LOG_HIDDEN_PREFIX = "[service]";
2820
+ var INTERNAL_LOG_WRAPPER_MARK = "__diminaInternalLogHidden";
2630
2821
  var LIVE_FILTER_POLL_MAX_ATTEMPTS = 100;
2631
2822
  var LIVE_FILTER_POLL_INTERVAL_MS = 100;
2632
- function buildLiveConsoleFilterScript(negativeFilter = DEFAULT_INTERNAL_LOG_FILTER) {
2633
- const filterJson = JSON.stringify(negativeFilter);
2823
+ function buildInternalLogHideScript(hiddenPrefix = INTERNAL_LOG_HIDDEN_PREFIX) {
2824
+ const prefixJson = JSON.stringify(hiddenPrefix);
2825
+ const markJson = JSON.stringify(INTERNAL_LOG_WRAPPER_MARK);
2634
2826
  const maxAttempts = JSON.stringify(LIVE_FILTER_POLL_MAX_ATTEMPTS);
2635
2827
  const intervalMs = JSON.stringify(LIVE_FILTER_POLL_INTERVAL_MS);
2636
2828
  return `(function(){
2637
- var VALUE = ${filterJson};
2829
+ var PREFIX = ${prefixJson};
2830
+ var MARK = ${markJson};
2638
2831
  var attempts = 0;
2639
2832
  function bootstrapReady() {
2640
2833
  try {
@@ -2644,45 +2837,124 @@ function buildLiveConsoleFilterScript(negativeFilter = DEFAULT_INTERNAL_LOG_FILT
2644
2837
  return true;
2645
2838
  } catch (_) { return false; }
2646
2839
  }
2840
+ function isInternalLog(viewMessage) {
2841
+ try {
2842
+ var msg = viewMessage && typeof viewMessage.consoleMessage === 'function' ? viewMessage.consoleMessage() : null;
2843
+ var text = msg ? String(msg.messageText == null ? '' : msg.messageText) : '';
2844
+ // Exact tag (the rest of the line is in later arguments), or the tag
2845
+ // followed by a space \u2014 same shape as main's isInternalLogMessage. A
2846
+ // bare prefix test would also swallow '[service]\u4E1A\u52A1\u72B6\u6001', an ordinary
2847
+ // business log the panel must keep showing.
2848
+ return text === PREFIX || text.indexOf(PREFIX + ' ') === 0;
2849
+ } catch (_) { return false; }
2850
+ }
2647
2851
  function tryApply() {
2648
2852
  attempts++;
2649
2853
  var scheduleRetry = attempts < ${maxAttempts};
2854
+ // Distinguishes the two halves of this script when the tail throws:
2855
+ // future messages are already handled once the wrapper is on the
2856
+ // prototype, even if re-judging the rows already on screen keeps failing.
2857
+ var wrapperInstalled = false;
2650
2858
  // In-realm bootstrap probe FIRST (defense in depth on top of the
2651
2859
  // main-process whenFrontendBootstrapped gate): touching
2652
- // Console.ConsoleView.instance() before the front-end's own
2653
- // MainImpl bootstrap completes constructs IssuesManager early and
2654
- // permanently kills that bootstrap (ensureFirst conflict) \u2014 the probe
2655
- // throws BEFORE constructing anything, so a not-ready tick costs
2656
- // nothing and never touches ConsoleView.
2860
+ // Console.ConsoleView.instance() before the front-end's own MainImpl
2861
+ // bootstrap completes constructs IssuesManager early and permanently
2862
+ // kills that bootstrap (ensureFirst conflict) \u2014 the probe throws BEFORE
2863
+ // constructing anything, so a not-ready tick costs nothing.
2657
2864
  if (!bootstrapReady()) {
2658
2865
  if (scheduleRetry) { setTimeout(tryApply, ${intervalMs}); return; }
2659
- console.warn('[console-filter] gave up applying the live Console filter after ' + attempts + ' attempts \u2014 the front-end bootstrap never completed');
2866
+ console.warn('[console-filter] gave up hiding internal logs after ' + attempts + ' attempts \u2014 the front-end bootstrap never completed');
2660
2867
  return;
2661
2868
  }
2662
2869
  try {
2663
2870
  var ConsoleNS = globalThis.Console;
2664
2871
  var view = ConsoleNS && ConsoleNS.ConsoleView ? ConsoleNS.ConsoleView.instance() : null;
2665
2872
  var f = view ? view.filter : null;
2666
- if (!f || !f.textFilterUI || typeof f.textFilterUI.setValue !== 'function' || typeof f.textFilterUI.value !== 'function') {
2873
+ var currentFilter = f ? f.currentFilter : null;
2874
+ if (!currentFilter) {
2667
2875
  if (scheduleRetry) { setTimeout(tryApply, ${intervalMs}); return; }
2668
- console.warn('[console-filter] gave up applying the live Console filter after ' + attempts + ' attempts \u2014 Console.ConsoleView never became usable');
2876
+ console.warn('[console-filter] gave up hiding internal logs after ' + attempts + ' attempts \u2014 Console.ConsoleView never became usable');
2877
+ return;
2878
+ }
2879
+ // The prototype, NOT the instance: clone() mints further ConsoleFilters
2880
+ // that judge the same messages, and they all share this one.
2881
+ var proto = Object.getPrototypeOf(currentFilter);
2882
+ if (!proto || typeof proto.shouldBeVisible !== 'function') {
2883
+ console.warn('[console-filter] cannot hide internal logs \u2014 ConsoleFilter has no shouldBeVisible to wrap; framework log lines will show in the right panel');
2884
+ return;
2885
+ }
2886
+ if (!proto[MARK]) {
2887
+ var original = proto.shouldBeVisible;
2888
+ proto.shouldBeVisible = function(viewMessage) {
2889
+ if (isInternalLog(viewMessage)) return false;
2890
+ return original.apply(this, arguments);
2891
+ };
2892
+ proto[MARK] = true;
2893
+ }
2894
+ wrapperInstalled = true;
2895
+ // Re-run the panel's visibility pass so lines already on screen when
2896
+ // the wrapper lands are re-judged, not just future ones.
2897
+ if (view && typeof view.onFilterChanged === 'function') view.onFilterChanged();
2898
+ } catch(err) {
2899
+ if (scheduleRetry) { setTimeout(tryApply, ${intervalMs}); return; }
2900
+ // Name which half survived: a wrapper that installed but could never
2901
+ // refresh still hides everything logged from now on, and the panel is
2902
+ // left holding the internal lines printed before it landed. Reporting
2903
+ // that as plain "gave up" would send the next reader looking for a
2904
+ // filter that is in fact working.
2905
+ if (wrapperInstalled) {
2906
+ console.warn('[console-filter] internal logs are hidden from here on, but the rows already on screen could not be re-judged:', err);
2907
+ } else {
2908
+ console.warn('[console-filter] gave up hiding internal logs after ' + attempts + ' attempts:', err);
2909
+ }
2910
+ }
2911
+ }
2912
+ tryApply();
2913
+ })()`;
2914
+ }
2915
+
2916
+ // src/main/services/views/clear-console-filter.ts
2917
+ function buildClearConsoleFilterScript() {
2918
+ return `(function(){
2919
+ var attempts = 0;
2920
+ var MAX = 100;
2921
+ function bootstrapReady() {
2922
+ try {
2923
+ var eui = globalThis.EUI;
2924
+ if (!eui || !eui.ShortcutRegistry || !eui.ShortcutRegistry.ShortcutRegistry) return false;
2925
+ eui.ShortcutRegistry.ShortcutRegistry.instance();
2926
+ return true;
2927
+ } catch (_) { return false; }
2928
+ }
2929
+ function tryClear() {
2930
+ attempts++;
2931
+ var scheduleRetry = attempts < MAX;
2932
+ if (!bootstrapReady()) {
2933
+ if (scheduleRetry) { setTimeout(tryClear, 100); return; }
2934
+ return;
2935
+ }
2936
+ try {
2937
+ try { localStorage.removeItem('console.textFilter'); } catch(_) {}
2938
+ try { localStorage.removeItem('console.text-filter'); } catch(_) {}
2939
+ var ConsoleNS = globalThis.Console;
2940
+ var view = ConsoleNS && ConsoleNS.ConsoleView ? ConsoleNS.ConsoleView.instance() : null;
2941
+ var f = view ? view.filter : null;
2942
+ var box = f && f.textFilterUI;
2943
+ if (!box || typeof box.setValue !== 'function' || typeof box.value !== 'function') {
2944
+ if (scheduleRetry) { setTimeout(tryClear, 100); return; }
2669
2945
  return;
2670
2946
  }
2671
- var current = f.textFilterUI.value();
2672
- var isUnset = current === undefined || current === null || current === '';
2673
- var isOurDefault = current === VALUE;
2674
- if (isUnset || isOurDefault) {
2675
- f.textFilterUI.setValue(VALUE);
2947
+ var current = box.value();
2948
+ if (current) {
2949
+ box.setValue('');
2676
2950
  if (typeof f.updateCurrentFilter === 'function') f.updateCurrentFilter();
2677
2951
  if (typeof f.onFilterChanged === 'function') f.onFilterChanged();
2678
2952
  }
2679
- // Either applied, or a real user customization was detected \u2014 both
2680
- // are terminal: nothing left for a later retry to accomplish.
2681
2953
  } catch(_) {
2682
- if (scheduleRetry) setTimeout(tryApply, ${intervalMs});
2954
+ if (scheduleRetry) setTimeout(tryClear, 100);
2683
2955
  }
2684
2956
  }
2685
- tryApply();
2957
+ tryClear();
2686
2958
  })()`;
2687
2959
  }
2688
2960
 
@@ -2948,7 +3220,8 @@ function createFrontendReplyChannel(hostWc, isDisposed) {
2948
3220
  }
2949
3221
  function drainOutboundBatch(batch, handle) {
2950
3222
  if (!Array.isArray(batch)) return;
2951
- for (const raw of batch) {
3223
+ const entries = batch;
3224
+ for (const raw of entries) {
2952
3225
  if (!raw || typeof raw !== "object") continue;
2953
3226
  const cmd = raw;
2954
3227
  if (typeof cmd.method !== "string") continue;
@@ -4396,7 +4669,6 @@ function raiseExecuteJavaScriptListenerCeiling(wc) {
4396
4669
  }
4397
4670
  function createDevtoolsHost(ctx, reconciler, deps) {
4398
4671
  let simulatorView = null;
4399
- let simulatorViewAdded = false;
4400
4672
  let nativeDevtoolsSourceWc = null;
4401
4673
  let devtoolsHostUsed = false;
4402
4674
  let unsubscribeNativeRenderEvents = null;
@@ -4405,6 +4677,7 @@ function createDevtoolsHost(ctx, reconciler, deps) {
4405
4677
  let stopServiceConsole = null;
4406
4678
  let nativeDevtoolsRetryTimer = null;
4407
4679
  let nativeDevtoolsRetryToken = 0;
4680
+ let clearFilterOnNextHostBuild = false;
4408
4681
  const injectWhenReady = createLoadDeferredInjector();
4409
4682
  const openInEditorWiredWcIds = /* @__PURE__ */ new Set();
4410
4683
  function clearNativeDevtoolsRetry() {
@@ -4483,9 +4756,7 @@ function createDevtoolsHost(ctx, reconciler, deps) {
4483
4756
  void whenFrontendBootstrapped(devtoolsWc).then((ready) => {
4484
4757
  if (!ready || devtoolsWc.isDestroyed()) return;
4485
4758
  try {
4486
- void devtoolsWc.executeJavaScript(buildConsoleFilterScript()).catch(() => {
4487
- });
4488
- void devtoolsWc.executeJavaScript(buildLiveConsoleFilterScript()).catch(() => {
4759
+ void devtoolsWc.executeJavaScript(buildInternalLogHideScript()).catch(() => {
4489
4760
  });
4490
4761
  } catch {
4491
4762
  }
@@ -4525,6 +4796,7 @@ function createDevtoolsHost(ctx, reconciler, deps) {
4525
4796
  }
4526
4797
  function pointNativeDevtoolsAtServiceWc(next) {
4527
4798
  if (!simulatorView || simulatorView.webContents.isDestroyed()) return true;
4799
+ clearFilterOnNextHostBuild = false;
4528
4800
  if (nativeDevtoolsSourceWc?.id === next.id && !nativeDevtoolsSourceWc.isDestroyed()) {
4529
4801
  return true;
4530
4802
  }
@@ -4604,18 +4876,19 @@ function createDevtoolsHost(ctx, reconciler, deps) {
4604
4876
  }
4605
4877
  function rebuildDevtoolsHostView() {
4606
4878
  if (simulatorView) {
4607
- removeSimulatorDevtoolsView();
4608
- try {
4609
- if (!simulatorView.webContents.isDestroyed()) {
4610
- simulatorView.webContents.close();
4611
- }
4612
- } catch {
4613
- }
4879
+ reconciler.destroyView(VIEW_ID.simulatorDevtools, simulatorView);
4614
4880
  simulatorView = null;
4615
4881
  }
4616
4882
  simulatorView = new WebContentsView4();
4617
4883
  const devtoolsWc = simulatorView.webContents;
4618
4884
  raiseExecuteJavaScriptListenerCeiling(devtoolsWc);
4885
+ injectWhenReady(devtoolsWc, "soft-menu", () => {
4886
+ void whenFrontendBootstrapped(devtoolsWc).then((ready) => {
4887
+ if (!ready || devtoolsWc.isDestroyed()) return;
4888
+ devtoolsWc.executeJavaScript(buildDevtoolsSoftMenuScript()).catch(() => {
4889
+ });
4890
+ });
4891
+ });
4619
4892
  ctx.networkForward?.setDevtoolsHost(devtoolsWc);
4620
4893
  if (ctx.bridge) {
4621
4894
  try {
@@ -4661,10 +4934,23 @@ function createDevtoolsHost(ctx, reconciler, deps) {
4661
4934
  });
4662
4935
  });
4663
4936
  });
4937
+ const clearStaleFilter = clearFilterOnNextHostBuild;
4938
+ injectWhenReady(devtoolsWc, "clear-console-filter", () => {
4939
+ if (!clearStaleFilter) return;
4940
+ void whenFrontendBootstrapped(devtoolsWc).then((ready) => {
4941
+ if (!ready || devtoolsWc.isDestroyed()) return;
4942
+ try {
4943
+ void devtoolsWc.executeJavaScript(buildClearConsoleFilterScript()).catch(() => {
4944
+ });
4945
+ } catch {
4946
+ }
4947
+ });
4948
+ });
4664
4949
  reconciler.reconcileNow();
4665
4950
  devtoolsHostUsed = false;
4666
4951
  }
4667
4952
  function attachNativeSimulatorDevtoolsHost() {
4953
+ clearFilterOnNextHostBuild = true;
4668
4954
  stopFollowingNativeServiceHost();
4669
4955
  rebuildDevtoolsHostView();
4670
4956
  if (ctx.bridge?.isNativeHost()) {
@@ -4673,22 +4959,8 @@ function createDevtoolsHost(ctx, reconciler, deps) {
4673
4959
  followNativeDevtoolsServiceHost();
4674
4960
  }
4675
4961
  }
4676
- function removeSimulatorDevtoolsView() {
4677
- if (simulatorView && simulatorViewAdded) {
4678
- try {
4679
- ctx.windows.mainWindow.contentView.removeChildView(simulatorView);
4680
- } catch (e) {
4681
- console.error("[workbench] removeSimulatorDevtoolsView error", e);
4682
- }
4683
- simulatorViewAdded = false;
4684
- }
4685
- reconciler.forgetActual(VIEW_ID.simulatorDevtools);
4686
- }
4687
4962
  reconciler.registerView(VIEW_ID.simulatorDevtools, {
4688
4963
  getView: () => simulatorView,
4689
- setAdded: (added) => {
4690
- simulatorViewAdded = added;
4691
- },
4692
4964
  gateHidden: () => !simulatorView
4693
4965
  });
4694
4966
  return {
@@ -4703,41 +4975,50 @@ function createDevtoolsHost(ctx, reconciler, deps) {
4703
4975
  stopElementsForward = null;
4704
4976
  },
4705
4977
  destroyHostView: () => {
4706
- destroyChildView(ctx.windows.mainWindow, simulatorView);
4978
+ reconciler.destroyView(VIEW_ID.simulatorDevtools, simulatorView);
4707
4979
  simulatorView = null;
4708
- simulatorViewAdded = false;
4709
4980
  }
4710
4981
  };
4711
4982
  }
4712
4983
 
4713
- // src/main/services/views/host-toolbar-view.ts
4714
- import { WebContentsView as WebContentsView5 } from "electron";
4984
+ // src/main/services/views/host-slot-session-runtime.ts
4985
+ import { session } from "electron";
4986
+ function createHostSlotSessionRuntime(preloadPath) {
4987
+ let refCount = 0;
4988
+ let registrationId = null;
4989
+ return {
4990
+ acquire() {
4991
+ if (refCount === 0) {
4992
+ registrationId = session.defaultSession.registerPreloadScript({
4993
+ type: "frame",
4994
+ filePath: preloadPath
4995
+ });
4996
+ }
4997
+ refCount++;
4998
+ },
4999
+ release() {
5000
+ if (refCount === 0) return;
5001
+ refCount--;
5002
+ if (refCount === 0 && registrationId !== null) {
5003
+ session.defaultSession.unregisterPreloadScript(registrationId);
5004
+ registrationId = null;
5005
+ }
5006
+ }
5007
+ };
5008
+ }
4715
5009
 
4716
5010
  // src/main/services/views/host-toolbar-session-runtime.ts
4717
- import { session } from "electron";
4718
- var refCount = 0;
4719
- var registrationId = null;
5011
+ var runtime = createHostSlotSessionRuntime(hostToolbarRuntimePreloadPath);
4720
5012
  function acquireHostToolbarSessionRuntime() {
4721
- if (refCount === 0) {
4722
- registrationId = session.defaultSession.registerPreloadScript({
4723
- type: "frame",
4724
- filePath: hostToolbarRuntimePreloadPath
4725
- });
4726
- }
4727
- refCount++;
5013
+ runtime.acquire();
4728
5014
  }
4729
5015
  function releaseHostToolbarSessionRuntime() {
4730
- if (refCount === 0) return;
4731
- refCount--;
4732
- if (refCount === 0 && registrationId !== null) {
4733
- session.defaultSession.unregisterPreloadScript(registrationId);
4734
- registrationId = null;
4735
- }
5016
+ runtime.release();
4736
5017
  }
4737
5018
 
4738
- // src/main/services/views/host-toolbar-port-channel.ts
5019
+ // src/main/services/views/host-slot-port-channel.ts
4739
5020
  import { MessageChannelMain } from "electron";
4740
- function createHostToolbarPortChannel(opts) {
5021
+ function createHostSlotPortChannel(opts) {
4741
5022
  let activePort = null;
4742
5023
  let activeWc = null;
4743
5024
  let state = "absent";
@@ -4749,7 +5030,14 @@ function createHostToolbarPortChannel(opts) {
4749
5030
  const { channel, payload } = data;
4750
5031
  if (typeof channel !== "string") return;
4751
5032
  for (const entry of [...handlers2]) {
4752
- if (entry.channel === channel) entry.handler(payload);
5033
+ if (entry.channel === channel) invokeMessageHandler(entry.handler, payload);
5034
+ }
5035
+ }
5036
+ function invokeMessageHandler(handler, payload) {
5037
+ try {
5038
+ handler(payload);
5039
+ } catch (err2) {
5040
+ console.error(`${opts.logPrefix} onMessage handler threw:`, err2);
4753
5041
  }
4754
5042
  }
4755
5043
  function dropActivePort(close, next) {
@@ -4768,7 +5056,7 @@ function createHostToolbarPortChannel(opts) {
4768
5056
  try {
4769
5057
  handler();
4770
5058
  } catch (err2) {
4771
- console.error("[host-toolbar] onReady handler threw:", err2);
5059
+ console.error(`${opts.logPrefix} onReady handler threw:`, err2);
4772
5060
  }
4773
5061
  }
4774
5062
  function fireReadyHandlers() {
@@ -4787,7 +5075,7 @@ function createHostToolbarPortChannel(opts) {
4787
5075
  port1.on("close", () => {
4788
5076
  if (activePort === port1) dropActivePort(false, "absent");
4789
5077
  });
4790
- wc.postMessage(ViewChannel.HostToolbarPort, null, [port2]);
5078
+ wc.postMessage(opts.channel, null, [port2]);
4791
5079
  port1.start();
4792
5080
  activePort = port1;
4793
5081
  activeWc = wc;
@@ -4818,7 +5106,7 @@ function createHostToolbarPortChannel(opts) {
4818
5106
  onMessage(channel, handler) {
4819
5107
  if (typeof channel !== "string" || channel === "") {
4820
5108
  throw new TypeError(
4821
- "hostToolbar.onMessage: channel must be a non-empty string"
5109
+ `${opts.logPrefix} onMessage: channel must be a non-empty string`
4822
5110
  );
4823
5111
  }
4824
5112
  const entry = { channel, handler };
@@ -4866,151 +5154,407 @@ function createHostToolbarPortChannel(opts) {
4866
5154
  };
4867
5155
  }
4868
5156
 
4869
- // src/main/services/views/host-toolbar-view.ts
4870
- function createHostToolbarView(ctx, reconciler, deps) {
4871
- let hostToolbarView = null;
4872
- let hostToolbarPreloadOverride = null;
4873
- let hostToolbarViewAdded = false;
4874
- let hostToolbarRuntimeAcquired = false;
4875
- let hostToolbarHeightMode = "auto";
4876
- let hostToolbarLastHeight = 0;
4877
- const hostToolbarPort = createHostToolbarPortChannel({
4878
- isCurrent: (wc) => liveHostToolbarWebContents() === wc
5157
+ // src/main/services/views/host-toolbar-port-channel.ts
5158
+ function createHostToolbarPortChannel(opts) {
5159
+ return createHostSlotPortChannel({
5160
+ isCurrent: opts.isCurrent,
5161
+ channel: ViewChannel.HostToolbarPort,
5162
+ logPrefix: "[host-toolbar]"
4879
5163
  });
4880
- function liveHostToolbarWebContents() {
4881
- const wc = hostToolbarView?.webContents;
5164
+ }
5165
+
5166
+ // src/main/services/views/managed-web-contents-view.ts
5167
+ import { WebContentsView as WebContentsView5 } from "electron";
5168
+ function createManagedWebContentsView(opts) {
5169
+ let view = null;
5170
+ let preloadOverride = null;
5171
+ let runtimeAcquired = false;
5172
+ function liveWebContents2() {
5173
+ const wc = view?.webContents;
4882
5174
  if (!wc || wc.isDestroyed()) return null;
4883
5175
  return wc;
4884
5176
  }
4885
- function ensureHostToolbarView() {
4886
- if (hostToolbarView && liveHostToolbarWebContents()) {
4887
- return hostToolbarView;
5177
+ function teardown(current, detail) {
5178
+ if (view !== current) return;
5179
+ console.error(`[${opts.viewId}] ${detail} \u2014 destroying so the next ensureView() rebuilds`);
5180
+ view = null;
5181
+ opts.reconciler.destroyView(opts.viewId, current);
5182
+ opts.onBroken?.();
5183
+ }
5184
+ function ensureView() {
5185
+ if (view && liveWebContents2()) {
5186
+ return view;
4888
5187
  }
4889
- if (hostToolbarView && hostToolbarViewAdded) {
4890
- try {
4891
- ctx.windows.mainWindow.contentView.removeChildView(hostToolbarView);
4892
- } catch {
4893
- }
4894
- hostToolbarViewAdded = false;
5188
+ if (view) {
5189
+ opts.reconciler.destroyView(opts.viewId, view);
4895
5190
  }
4896
- reconciler.forgetActual(VIEW_ID.hostToolbar);
4897
- if (!hostToolbarRuntimeAcquired) {
4898
- acquireHostToolbarSessionRuntime();
4899
- hostToolbarRuntimeAcquired = true;
5191
+ if (!runtimeAcquired) {
5192
+ opts.sessionRuntime.acquire();
5193
+ runtimeAcquired = true;
4900
5194
  }
4901
5195
  const webPreferences = {
4902
5196
  nodeIntegration: false,
4903
5197
  contextIsolation: true,
4904
5198
  sandbox: false,
4905
- additionalArguments: [HOST_TOOLBAR_RUNTIME_MARKER]
5199
+ additionalArguments: [opts.marker]
4906
5200
  };
4907
- if (hostToolbarPreloadOverride !== null) {
4908
- webPreferences.preload = hostToolbarPreloadOverride;
5201
+ if (preloadOverride !== null) {
5202
+ webPreferences.preload = preloadOverride;
4909
5203
  }
4910
- const view = new WebContentsView5({ webPreferences });
4911
- hostToolbarView = view;
4912
- hostToolbarPort.attach(view.webContents);
5204
+ const next = new WebContentsView5({ webPreferences });
5205
+ view = next;
5206
+ opts.port.attach(next.webContents);
4913
5207
  try {
4914
- view.setBackgroundColor("#121212");
5208
+ next.setBackgroundColor("#121212");
4915
5209
  } catch {
4916
5210
  }
4917
5211
  try {
4918
- view.webContents.setWindowOpenHandler(({ url }) => handleWindowOpenExternal(url));
5212
+ next.webContents.setWindowOpenHandler(({ url }) => handleWindowOpenExternal(url));
4919
5213
  } catch {
4920
5214
  }
4921
- return view;
4922
- }
4923
- function notifyHostToolbarHeight(height) {
4924
- hostToolbarLastHeight = height;
4925
- ctx.notify.hostToolbarHeightChanged(height);
4926
- deps.reapplyPresentOverlays();
4927
- }
4928
- function setHostToolbarHeight(extent) {
4929
- if (hostToolbarHeightMode !== "auto") return;
4930
- notifyHostToolbarHeight(extent);
4931
- }
4932
- function hideHostToolbar() {
4933
- reconciler.setBaseDesired(VIEW_ID.hostToolbar, {
4934
- viewId: VIEW_ID.hostToolbar,
4935
- placement: { visible: false },
4936
- layer: VIEW_LAYER.hostToolbar
5215
+ next.webContents.on("did-fail-load", (_event, errorCode, _errorDescription, _validatedURL, isMainFrame) => {
5216
+ if (!isMainFrame || errorCode === -3) return;
5217
+ teardown(next, `did-fail-load (code ${errorCode})`);
4937
5218
  });
4938
- reconciler.reconcileNow();
4939
- notifyHostToolbarHeight(0);
5219
+ next.webContents.on("render-process-gone", (_event, details) => {
5220
+ teardown(next, `render-process-gone (${details.reason})`);
5221
+ });
5222
+ return next;
4940
5223
  }
4941
- const control = {
5224
+ return {
4942
5225
  async loadURL(url) {
4943
- const view = ensureHostToolbarView();
4944
- hostToolbarPort.invalidate();
4945
- await view.webContents.loadURL(url);
5226
+ const v = ensureView();
5227
+ opts.port.invalidate();
5228
+ await v.webContents.loadURL(url);
4946
5229
  },
4947
5230
  async loadFile(filePath) {
4948
- const view = ensureHostToolbarView();
4949
- hostToolbarPort.invalidate();
4950
- await view.webContents.loadFile(filePath);
5231
+ const v = ensureView();
5232
+ opts.port.invalidate();
5233
+ await v.webContents.loadFile(filePath);
5234
+ },
5235
+ liveWebContents: liveWebContents2,
5236
+ getView: () => view,
5237
+ setPreloadPath(path20) {
5238
+ preloadOverride = path20;
4951
5239
  },
5240
+ ensureView,
5241
+ dispose() {
5242
+ opts.port.dispose();
5243
+ opts.reconciler.destroyView(opts.viewId, view);
5244
+ view = null;
5245
+ if (runtimeAcquired) {
5246
+ opts.sessionRuntime.release();
5247
+ runtimeAcquired = false;
5248
+ }
5249
+ }
5250
+ };
5251
+ }
5252
+
5253
+ // src/main/services/views/host-slot-view.ts
5254
+ function deriveSlotControl(base, extra) {
5255
+ return {
5256
+ loadURL: (url) => base.loadURL(url),
5257
+ loadFile: (filePath) => base.loadFile(filePath),
4952
5258
  get webContents() {
4953
- return liveHostToolbarWebContents();
5259
+ return base.webContents;
4954
5260
  },
4955
- hide() {
4956
- hideHostToolbar();
5261
+ hide: () => base.hide(),
5262
+ setPreloadPath: (path20) => base.setPreloadPath(path20),
5263
+ onMessage: (channel, handler) => base.onMessage(channel, handler),
5264
+ onReady: (handler) => base.onReady(handler),
5265
+ send: (channel, payload) => base.send(channel, payload),
5266
+ ...extra
5267
+ };
5268
+ }
5269
+ function createHostSlotView(reconciler, config, deps) {
5270
+ let extentMode = "auto";
5271
+ let lastExtent = 0;
5272
+ let lastAdvertisedExtent = null;
5273
+ const port = config.portChannel;
5274
+ const managed = createManagedWebContentsView({
5275
+ reconciler,
5276
+ viewId: config.viewId,
5277
+ marker: config.marker,
5278
+ sessionRuntime: config.sessionRuntime,
5279
+ port
5280
+ });
5281
+ function notifyExtent(extent) {
5282
+ lastExtent = extent;
5283
+ deps.onExtentChanged(extent);
5284
+ deps.reapplyPresentOverlays?.();
5285
+ }
5286
+ function setExtent(extent) {
5287
+ lastAdvertisedExtent = extent;
5288
+ if (extentMode !== "auto") return;
5289
+ notifyExtent(extent);
5290
+ }
5291
+ function hideView() {
5292
+ reconciler.setBaseDesired(config.viewId, {
5293
+ viewId: config.viewId,
5294
+ placement: { visible: false },
5295
+ layer: config.layer
5296
+ });
5297
+ reconciler.reconcileNow();
5298
+ notifyExtent(0);
5299
+ lastAdvertisedExtent = null;
5300
+ }
5301
+ const control = {
5302
+ loadURL: (url) => managed.loadURL(url),
5303
+ loadFile: (filePath) => managed.loadFile(filePath),
5304
+ get webContents() {
5305
+ return managed.liveWebContents();
4957
5306
  },
4958
- setPreloadPath(path21) {
4959
- hostToolbarPreloadOverride = path21;
5307
+ hide() {
5308
+ hideView();
4960
5309
  },
4961
- setHeightMode(mode) {
5310
+ setPreloadPath: (path20) => managed.setPreloadPath(path20),
5311
+ setExtentMode(mode) {
4962
5312
  if (mode !== "auto" && !(Number.isFinite(mode.fixed) && mode.fixed >= 0)) {
4963
5313
  throw new TypeError(
4964
- `hostToolbar.setHeightMode: fixed height must be a finite, non-negative number (got ${mode.fixed})`
5314
+ `${config.setExtentModeErrorLabel}: fixed extent must be a finite, non-negative number (got ${mode.fixed})`
4965
5315
  );
4966
5316
  }
4967
- hostToolbarHeightMode = mode;
5317
+ extentMode = mode;
4968
5318
  if (mode !== "auto") {
4969
- notifyHostToolbarHeight(mode.fixed);
5319
+ notifyExtent(mode.fixed);
5320
+ } else if (lastAdvertisedExtent !== null && lastAdvertisedExtent !== lastExtent) {
5321
+ notifyExtent(lastAdvertisedExtent);
4970
5322
  }
4971
5323
  },
4972
5324
  onMessage(channel, handler) {
4973
- return hostToolbarPort.onMessage(channel, handler);
5325
+ return port.onMessage(channel, handler);
4974
5326
  },
4975
5327
  onReady(handler) {
4976
- return hostToolbarPort.onReady(handler);
5328
+ return port.onReady(handler);
4977
5329
  },
4978
5330
  send(channel, payload) {
4979
- return hostToolbarPort.send(channel, payload);
5331
+ return port.send(channel, payload);
5332
+ }
5333
+ };
5334
+ reconciler.registerView(config.viewId, {
5335
+ getView: () => managed.getView(),
5336
+ ensureView: () => managed.ensureView(),
5337
+ ensureLazy: (desired) => {
5338
+ if (desired?.placement.visible && !managed.liveWebContents()) managed.ensureView();
5339
+ }
5340
+ });
5341
+ return {
5342
+ control,
5343
+ setExtent,
5344
+ getExtent: () => lastExtent,
5345
+ getWebContentsId: () => managed.liveWebContents()?.id ?? null,
5346
+ dispose: () => managed.dispose()
5347
+ };
5348
+ }
5349
+
5350
+ // src/main/services/views/host-toolbar-view.ts
5351
+ function createHostToolbarView(ctx, reconciler, deps) {
5352
+ const port = createHostToolbarPortChannel({
5353
+ isCurrent: (wc) => slot.getWebContentsId() === wc.id
5354
+ });
5355
+ const slot = createHostSlotView(
5356
+ reconciler,
5357
+ {
5358
+ viewId: VIEW_ID.hostToolbar,
5359
+ layer: VIEW_LAYER.hostToolbar,
5360
+ marker: HOST_TOOLBAR_RUNTIME_MARKER,
5361
+ sessionRuntime: {
5362
+ acquire: acquireHostToolbarSessionRuntime,
5363
+ release: releaseHostToolbarSessionRuntime
5364
+ },
5365
+ portChannel: port,
5366
+ setExtentModeErrorLabel: "hostToolbar.setHeightMode"
5367
+ },
5368
+ {
5369
+ onExtentChanged: (height) => ctx.notify.hostToolbarHeightChanged(height),
5370
+ reapplyPresentOverlays: () => deps.reapplyPresentOverlays()
5371
+ }
5372
+ );
5373
+ const control = deriveSlotControl(slot.control, {
5374
+ setHeightMode: (mode) => slot.control.setExtentMode(mode)
5375
+ });
5376
+ return {
5377
+ control,
5378
+ setHostToolbarHeight: (extent) => slot.setExtent(extent),
5379
+ getHostToolbarHeight: () => slot.getExtent(),
5380
+ getHostToolbarWebContentsId: () => slot.getWebContentsId(),
5381
+ dispose: () => slot.dispose()
5382
+ };
5383
+ }
5384
+
5385
+ // src/main/services/views/host-sidebar-session-runtime.ts
5386
+ var runtime2 = createHostSlotSessionRuntime(hostSidebarRuntimePreloadPath);
5387
+ function acquireHostSidebarSessionRuntime() {
5388
+ runtime2.acquire();
5389
+ }
5390
+ function releaseHostSidebarSessionRuntime() {
5391
+ runtime2.release();
5392
+ }
5393
+
5394
+ // src/main/services/views/host-sidebar-port-channel.ts
5395
+ function createHostSidebarPortChannel(opts) {
5396
+ return createHostSlotPortChannel({
5397
+ isCurrent: opts.isCurrent,
5398
+ channel: ViewChannel.HostSidebarPort,
5399
+ logPrefix: "[host-sidebar]"
5400
+ });
5401
+ }
5402
+
5403
+ // src/main/services/views/host-sidebar-view.ts
5404
+ function createHostSidebarView(ctx, reconciler) {
5405
+ const port = createHostSidebarPortChannel({
5406
+ isCurrent: (wc) => slot.getWebContentsId() === wc.id
5407
+ });
5408
+ const slot = createHostSlotView(
5409
+ reconciler,
5410
+ {
5411
+ viewId: VIEW_ID.hostSidebar,
5412
+ layer: VIEW_LAYER.hostSidebar,
5413
+ marker: HOST_SIDEBAR_RUNTIME_MARKER,
5414
+ sessionRuntime: {
5415
+ acquire: acquireHostSidebarSessionRuntime,
5416
+ release: releaseHostSidebarSessionRuntime
5417
+ },
5418
+ portChannel: port,
5419
+ setExtentModeErrorLabel: "hostSidebar.setWidthMode"
5420
+ },
5421
+ {
5422
+ onExtentChanged: (width) => ctx.notify.hostSidebarWidthChanged(width)
4980
5423
  }
5424
+ );
5425
+ const control = deriveSlotControl(slot.control, {
5426
+ setWidthMode: (mode) => slot.control.setExtentMode(mode)
5427
+ });
5428
+ return {
5429
+ control,
5430
+ setHostSidebarWidth: (extent) => slot.setExtent(extent),
5431
+ getHostSidebarWidth: () => slot.getExtent(),
5432
+ getHostSidebarWebContentsId: () => slot.getWebContentsId(),
5433
+ dispose: () => slot.dispose()
4981
5434
  };
4982
- reconciler.registerView(VIEW_ID.hostToolbar, {
4983
- getView: () => hostToolbarView,
4984
- ensureView: () => ensureHostToolbarView(),
4985
- setAdded: (added) => {
4986
- hostToolbarViewAdded = added;
5435
+ }
5436
+
5437
+ // src/main/services/views/host-dialog-session-runtime.ts
5438
+ var runtime3 = createHostSlotSessionRuntime(hostDialogRuntimePreloadPath);
5439
+ function acquireHostDialogSessionRuntime() {
5440
+ runtime3.acquire();
5441
+ }
5442
+ function releaseHostDialogSessionRuntime() {
5443
+ runtime3.release();
5444
+ }
5445
+
5446
+ // src/main/services/views/host-dialog-port-channel.ts
5447
+ function createHostDialogPortChannel(opts) {
5448
+ return createHostSlotPortChannel({
5449
+ isCurrent: opts.isCurrent,
5450
+ channel: ViewChannel.HostDialogPort,
5451
+ logPrefix: "[host-dialog]"
5452
+ });
5453
+ }
5454
+
5455
+ // src/main/services/views/host-dialog-view.ts
5456
+ var DEFAULT_WIDTH = 480;
5457
+ var DEFAULT_HEIGHT = 320;
5458
+ function createHostDialogView(ctx, reconciler) {
5459
+ const port = createHostDialogPortChannel({
5460
+ isCurrent: (wc) => managed.liveWebContents()?.id === wc.id
5461
+ });
5462
+ let visible = false;
5463
+ let width = DEFAULT_WIDTH;
5464
+ let height = DEFAULT_HEIGHT;
5465
+ const managed = createManagedWebContentsView({
5466
+ reconciler,
5467
+ viewId: VIEW_ID.hostDialog,
5468
+ marker: HOST_DIALOG_RUNTIME_MARKER,
5469
+ sessionRuntime: {
5470
+ acquire: acquireHostDialogSessionRuntime,
5471
+ release: releaseHostDialogSessionRuntime
5472
+ },
5473
+ port,
5474
+ onBroken: () => {
5475
+ visible = false;
5476
+ reconciler.deleteOverlayDesired(VIEW_ID.hostDialog);
5477
+ }
5478
+ });
5479
+ function computeBounds() {
5480
+ const [winW = 0, winH = 0] = ctx.windows.mainWindow.getContentSize();
5481
+ const w = Math.min(width, Math.max(1, winW));
5482
+ const h = Math.min(height, Math.max(1, winH));
5483
+ return {
5484
+ x: Math.max(0, Math.round((winW - w) / 2)),
5485
+ y: Math.max(0, Math.round((winH - h) / 2)),
5486
+ width: w,
5487
+ height: h
5488
+ };
5489
+ }
5490
+ function present() {
5491
+ reconciler.setOverlayDesired(VIEW_ID.hostDialog, {
5492
+ viewId: VIEW_ID.hostDialog,
5493
+ placement: { visible: true, bounds: computeBounds() },
5494
+ layer: VIEW_LAYER.hostDialog
5495
+ });
5496
+ reconciler.reconcileNow();
5497
+ }
5498
+ function reportMeasuredExtent(axis, extent) {
5499
+ if (!Number.isFinite(extent) || extent <= 0) return;
5500
+ if (axis === "inline") {
5501
+ width = extent;
5502
+ } else {
5503
+ height = extent;
5504
+ }
5505
+ if (visible) present();
5506
+ }
5507
+ function resetMeasuredExtent() {
5508
+ width = DEFAULT_WIDTH;
5509
+ height = DEFAULT_HEIGHT;
5510
+ if (visible) present();
5511
+ }
5512
+ const control = {
5513
+ loadURL: (url) => {
5514
+ resetMeasuredExtent();
5515
+ return managed.loadURL(url);
5516
+ },
5517
+ loadFile: (filePath) => {
5518
+ resetMeasuredExtent();
5519
+ return managed.loadFile(filePath);
5520
+ },
5521
+ get webContents() {
5522
+ return managed.liveWebContents();
5523
+ },
5524
+ setPreloadPath: (path20) => managed.setPreloadPath(path20),
5525
+ onMessage: (channel, handler) => port.onMessage(channel, handler),
5526
+ onReady: (handler) => port.onReady(handler),
5527
+ send: (channel, payload) => port.send(channel, payload),
5528
+ show() {
5529
+ managed.ensureView();
5530
+ visible = true;
5531
+ present();
5532
+ },
5533
+ hide() {
5534
+ visible = false;
5535
+ reconciler.deleteOverlayDesired(VIEW_ID.hostDialog);
5536
+ reconciler.reconcileNow();
4987
5537
  },
4988
- ensureLazy: (desired) => {
4989
- if (desired?.placement.visible && !liveHostToolbarWebContents()) ensureHostToolbarView();
4990
- }
5538
+ isVisible: () => visible
5539
+ };
5540
+ reconciler.registerView(VIEW_ID.hostDialog, {
5541
+ getView: () => managed.getView(),
5542
+ ensureView: () => managed.ensureView()
4991
5543
  });
4992
- function dispose() {
4993
- hostToolbarPort.dispose();
4994
- destroyChildView(ctx.windows.mainWindow, hostToolbarView);
4995
- hostToolbarView = null;
4996
- hostToolbarViewAdded = false;
4997
- if (hostToolbarRuntimeAcquired) {
4998
- releaseHostToolbarSessionRuntime();
4999
- hostToolbarRuntimeAcquired = false;
5000
- }
5001
- }
5002
5544
  return {
5003
5545
  control,
5004
- setHostToolbarHeight,
5005
- getHostToolbarHeight: () => hostToolbarLastHeight,
5006
- getHostToolbarWebContentsId: () => liveHostToolbarWebContents()?.id ?? null,
5007
- dispose
5546
+ reportMeasuredExtent,
5547
+ getHostDialogWebContentsId: () => managed.liveWebContents()?.id ?? null,
5548
+ reposition: () => {
5549
+ if (visible) present();
5550
+ },
5551
+ dispose: () => managed.dispose()
5008
5552
  };
5009
5553
  }
5010
5554
 
5011
5555
  // src/main/services/views/overlay-panels-view.ts
5012
5556
  import { WebContentsView as WebContentsView6 } from "electron";
5013
- import path8 from "path";
5557
+ import { createOverlayPanel } from "@dimina-kit/electron-deck/main";
5014
5558
 
5015
5559
  // src/main/services/layout/index.ts
5016
5560
  function computeNativeSimulatorViewParams(rect, zoomPercent) {
@@ -5041,127 +5585,266 @@ function computePopoverBounds(contentWidth, contentHeight, headerHeight) {
5041
5585
  height: contentHeight - headerHeight
5042
5586
  };
5043
5587
  }
5588
+ var TOOLTIP_GAP = 6;
5589
+ var TOOLTIP_EDGE_MARGIN = 4;
5590
+ function computeTooltipMaxWidth(contentWidth) {
5591
+ return Math.max(1, contentWidth - TOOLTIP_EDGE_MARGIN * 2);
5592
+ }
5593
+ function computeTooltipBounds(anchor, contentWidth, contentHeight, measured) {
5594
+ const availableWidth = computeTooltipMaxWidth(contentWidth);
5595
+ const width = Math.min(availableWidth, Math.max(1, Math.ceil(measured.width)));
5596
+ const height = Math.max(1, Math.ceil(measured.height));
5597
+ const below = anchor.y + anchor.height + TOOLTIP_GAP;
5598
+ const fitsBelow = below + height <= contentHeight;
5599
+ const y = fitsBelow ? below : Math.max(0, anchor.y - TOOLTIP_GAP - height);
5600
+ const centeredX = anchor.x + anchor.width / 2 - width / 2;
5601
+ const maxX = Math.max(TOOLTIP_EDGE_MARGIN, contentWidth - width - TOOLTIP_EDGE_MARGIN);
5602
+ const x = Math.min(maxX, Math.max(TOOLTIP_EDGE_MARGIN, centeredX));
5603
+ return { x, y, width, height };
5604
+ }
5044
5605
 
5045
5606
  // src/main/services/views/overlay-panels-view.ts
5046
5607
  function createOverlayPanelsView(ctx, reconciler, deps) {
5047
- let settingsView = null;
5048
- let popoverView = null;
5049
5608
  function overlayHeaderHeight() {
5050
5609
  return HEADER_H + deps.getHostToolbarHeight();
5051
5610
  }
5052
- function applySettingsBounds() {
5053
- if (!settingsView || ctx.windows.mainWindow.isDestroyed()) return;
5054
- const [w = 0, h = 0] = ctx.windows.mainWindow.getContentSize();
5055
- reconciler.setOverlayDesired(VIEW_ID.settings, {
5056
- viewId: VIEW_ID.settings,
5057
- placement: {
5058
- visible: true,
5059
- bounds: computeSettingsBounds(w, h, overlayHeaderHeight())
5060
- },
5061
- layer: VIEW_LAYER.settings
5062
- });
5063
- reconciler.reconcileNow();
5064
- }
5065
- function applyPopoverBounds() {
5066
- if (!popoverView || ctx.windows.mainWindow.isDestroyed()) return;
5067
- const [w = 0, h = 0] = ctx.windows.mainWindow.getContentSize();
5068
- reconciler.setOverlayDesired(VIEW_ID.popover, {
5069
- viewId: VIEW_ID.popover,
5070
- placement: {
5071
- visible: true,
5072
- bounds: computePopoverBounds(w, h, overlayHeaderHeight())
5073
- },
5074
- layer: VIEW_LAYER.popover
5075
- });
5076
- reconciler.reconcileNow();
5611
+ function overlayDesiredSetter(viewId, layer) {
5612
+ return (bounds) => {
5613
+ if (bounds === null) {
5614
+ reconciler.deleteOverlayDesired(viewId);
5615
+ } else {
5616
+ reconciler.setOverlayDesired(viewId, {
5617
+ viewId,
5618
+ placement: { visible: true, bounds },
5619
+ layer
5620
+ });
5621
+ }
5622
+ reconciler.reconcileNow();
5623
+ };
5077
5624
  }
5078
- async function showSettings() {
5079
- if (!settingsView) {
5080
- settingsView = new WebContentsView6({
5081
- webPreferences: {
5082
- nodeIntegration: false,
5083
- contextIsolation: true,
5084
- sandbox: false,
5085
- preload: mainPreloadPath
5086
- }
5625
+ const settingsPanel = createOverlayPanel({
5626
+ electron: { createWebContentsView: (opts) => new WebContentsView6(opts) },
5627
+ rendererDir: ctx.rendererDir,
5628
+ entry: "entries/settings/index.html",
5629
+ webPreferences: {
5630
+ nodeIntegration: false,
5631
+ contextIsolation: true,
5632
+ sandbox: false,
5633
+ preload: mainPreloadPath
5634
+ },
5635
+ hardenNavigation: (wc) => applyNavigationHardening(wc, ctx.rendererDir),
5636
+ setDesired: overlayDesiredSetter(VIEW_ID.settings, VIEW_LAYER.settings),
5637
+ registerView: (getView) => reconciler.registerView(VIEW_ID.settings, { getView }),
5638
+ destroyView: (view) => reconciler.destroyView(VIEW_ID.settings, view),
5639
+ readyMode: "manual"
5640
+ });
5641
+ const popoverPanel = createOverlayPanel({
5642
+ electron: { createWebContentsView: (opts) => new WebContentsView6(opts) },
5643
+ rendererDir: ctx.rendererDir,
5644
+ entry: "entries/popover/index.html",
5645
+ webPreferences: {
5646
+ nodeIntegration: false,
5647
+ contextIsolation: true,
5648
+ sandbox: false,
5649
+ preload: mainPreloadPath
5650
+ },
5651
+ hardenNavigation: (wc) => applyNavigationHardening(wc, ctx.rendererDir),
5652
+ setDesired: overlayDesiredSetter(VIEW_ID.popover, VIEW_LAYER.popover),
5653
+ registerView: (getView) => reconciler.registerView(VIEW_ID.popover, { getView }),
5654
+ destroyView: (view) => reconciler.destroyView(VIEW_ID.popover, view),
5655
+ pushData: (view, data) => ctx.notify.popoverInit(view, data),
5656
+ readyMode: "manual"
5657
+ });
5658
+ const tooltipPanel = createOverlayPanel({
5659
+ electron: { createWebContentsView: (opts) => new WebContentsView6(opts) },
5660
+ rendererDir: ctx.rendererDir,
5661
+ entry: "entries/tooltip/index.html",
5662
+ webPreferences: {
5663
+ nodeIntegration: false,
5664
+ contextIsolation: true,
5665
+ sandbox: false,
5666
+ preload: mainPreloadPath
5667
+ },
5668
+ hardenNavigation: (wc) => applyNavigationHardening(wc, ctx.rendererDir),
5669
+ setDesired: overlayDesiredSetter(VIEW_ID.tooltip, VIEW_LAYER.tooltip),
5670
+ registerView: (getView) => reconciler.registerView(VIEW_ID.tooltip, { getView }),
5671
+ prepareView: (view) => {
5672
+ const [width = 0, height = 0] = ctx.windows.mainWindow.getContentSize();
5673
+ reconciler.prepareView(VIEW_ID.tooltip, view, {
5674
+ x: 0,
5675
+ y: 0,
5676
+ width: Math.max(1, width),
5677
+ height: Math.max(1, height)
5087
5678
  });
5088
- applyNavigationHardening(settingsView.webContents, ctx.rendererDir);
5089
- settingsView.setBackgroundColor("#00000000");
5090
- await settingsView.webContents.loadFile(
5091
- path8.join(ctx.rendererDir, "entries/settings/index.html")
5092
- );
5093
- }
5094
- applySettingsBounds();
5679
+ },
5680
+ destroyView: (view) => reconciler.destroyView(VIEW_ID.tooltip, view),
5681
+ pushData: (view, data) => ctx.notify.tooltipInit(view, data),
5682
+ readyMode: "manual"
5683
+ });
5684
+ function fullWindowBounds() {
5685
+ const [width = 0, height = 0] = ctx.windows.mainWindow.getContentSize();
5686
+ return { x: 0, y: 0, width: Math.max(1, width), height: Math.max(1, height) };
5687
+ }
5688
+ const projectCreateDialogPanel = createOverlayPanel({
5689
+ electron: { createWebContentsView: (opts) => new WebContentsView6(opts) },
5690
+ rendererDir: ctx.rendererDir,
5691
+ entry: "entries/project-create-dialog/index.html",
5692
+ webPreferences: {
5693
+ nodeIntegration: false,
5694
+ contextIsolation: true,
5695
+ sandbox: false,
5696
+ preload: mainPreloadPath
5697
+ },
5698
+ hardenNavigation: (wc) => applyNavigationHardening(wc, ctx.rendererDir),
5699
+ setDesired: overlayDesiredSetter(VIEW_ID.projectCreateDialog, VIEW_LAYER.dialog),
5700
+ registerView: (getView) => reconciler.registerView(VIEW_ID.projectCreateDialog, { getView }),
5701
+ destroyView: (view) => reconciler.destroyView(VIEW_ID.projectCreateDialog, view),
5702
+ pushData: (view, data) => ctx.notify.projectCreateInit(view, data),
5703
+ readyMode: "manual"
5704
+ });
5705
+ const updateDialogPanel = createOverlayPanel({
5706
+ electron: { createWebContentsView: (opts) => new WebContentsView6(opts) },
5707
+ rendererDir: ctx.rendererDir,
5708
+ entry: "entries/update-dialog/index.html",
5709
+ webPreferences: {
5710
+ nodeIntegration: false,
5711
+ contextIsolation: true,
5712
+ sandbox: false,
5713
+ preload: mainPreloadPath
5714
+ },
5715
+ hardenNavigation: (wc) => applyNavigationHardening(wc, ctx.rendererDir),
5716
+ setDesired: overlayDesiredSetter(VIEW_ID.updateDialog, VIEW_LAYER.dialog),
5717
+ registerView: (getView) => reconciler.registerView(VIEW_ID.updateDialog, { getView }),
5718
+ destroyView: (view) => reconciler.destroyView(VIEW_ID.updateDialog, view),
5719
+ pushData: (view, data) => ctx.notify.updateAvailable(view, data),
5720
+ readyMode: "manual"
5721
+ });
5722
+ let tooltipRequestId = 0;
5723
+ let activeTooltip = null;
5724
+ async function showSettings() {
5725
+ const [w = 0, h = 0] = ctx.windows.mainWindow.getContentSize();
5726
+ settingsPanel.show(void 0, computeSettingsBounds(w, h, overlayHeaderHeight()));
5727
+ await settingsPanel.whenReady();
5095
5728
  }
5096
5729
  function hideSettings() {
5097
- reconciler.deleteOverlayDesired(VIEW_ID.settings);
5098
- reconciler.reconcileNow();
5730
+ settingsPanel.hide();
5099
5731
  }
5100
5732
  function showPopover(data) {
5101
5733
  hidePopover();
5102
- const popover = new WebContentsView6({
5103
- webPreferences: {
5104
- nodeIntegration: false,
5105
- contextIsolation: true,
5106
- sandbox: false,
5107
- preload: mainPreloadPath
5108
- }
5109
- });
5110
- applyNavigationHardening(popover.webContents, ctx.rendererDir);
5111
- popoverView = popover;
5112
- popover.setBackgroundColor("#00000000");
5113
- applyPopoverBounds();
5114
- popover.webContents.once("did-finish-load", () => {
5115
- ctx.notify.popoverInit(popover, data);
5116
- });
5117
- popover.webContents.loadFile(
5118
- path8.join(ctx.rendererDir, "entries/popover/index.html")
5119
- );
5734
+ const [w = 0, h = 0] = ctx.windows.mainWindow.getContentSize();
5735
+ popoverPanel.show(data, computePopoverBounds(w, h, overlayHeaderHeight()));
5120
5736
  }
5121
5737
  function hidePopover() {
5122
- if (!popoverView) return;
5738
+ if (!popoverPanel.isPresent()) return;
5739
+ popoverPanel.destroy();
5123
5740
  reconciler.deleteOverlayDesired(VIEW_ID.popover);
5124
- destroyChildView(ctx.windows.mainWindow, popoverView);
5125
- popoverView = null;
5126
5741
  reconciler.reconcileNow();
5127
5742
  ctx.notify.popoverClosed();
5128
5743
  }
5744
+ function prepareTooltip() {
5745
+ tooltipPanel.prepare();
5746
+ }
5747
+ function showTooltip(data) {
5748
+ const requestId = ++tooltipRequestId;
5749
+ activeTooltip = { requestId, anchor: data.anchor };
5750
+ const [contentWidth = 0] = ctx.windows.mainWindow.getContentSize();
5751
+ tooltipPanel.show({
5752
+ requestId,
5753
+ text: data.text,
5754
+ maxWidth: computeTooltipMaxWidth(contentWidth)
5755
+ }, null);
5756
+ }
5757
+ function hideTooltip() {
5758
+ activeTooltip = null;
5759
+ tooltipPanel.hide();
5760
+ }
5761
+ function showProjectCreateDialog(data) {
5762
+ projectCreateDialogPanel.show(data, fullWindowBounds());
5763
+ }
5764
+ function hideProjectCreateDialog() {
5765
+ projectCreateDialogPanel.hide();
5766
+ }
5767
+ function showUpdateDialog(data) {
5768
+ updateDialogPanel.show(data, fullWindowBounds());
5769
+ }
5770
+ function hideUpdateDialog() {
5771
+ updateDialogPanel.hide();
5772
+ }
5773
+ function notifyUpdateDownloadProgress(percent) {
5774
+ const wc = updateDialogPanel.getWebContents();
5775
+ if (!wc) return;
5776
+ wc.send(UpdateChannel.DownloadProgress, { percent });
5777
+ }
5778
+ function markOverlayReady(webContentsId) {
5779
+ settingsPanel.markReady(webContentsId);
5780
+ popoverPanel.markReady(webContentsId);
5781
+ tooltipPanel.markReady(webContentsId);
5782
+ projectCreateDialogPanel.markReady(webContentsId);
5783
+ updateDialogPanel.markReady(webContentsId);
5784
+ }
5785
+ function applyTooltipMeasurement(webContentsId, measurement) {
5786
+ const active = activeTooltip;
5787
+ if (!active || active.requestId !== measurement.requestId) return;
5788
+ if (tooltipPanel.getWebContentsId() !== webContentsId) return;
5789
+ const [w = 0, h = 0] = ctx.windows.mainWindow.getContentSize();
5790
+ tooltipPanel.reposition(computeTooltipBounds(active.anchor, w, h, measurement));
5791
+ }
5129
5792
  function reapplyPresentOverlays() {
5130
- if (reconciler.hasOverlayDesired(VIEW_ID.settings)) applySettingsBounds();
5131
- if (reconciler.hasOverlayDesired(VIEW_ID.popover)) applyPopoverBounds();
5793
+ if (settingsPanel.isPresent() && reconciler.hasOverlayDesired(VIEW_ID.settings)) {
5794
+ showSettings().catch(() => {
5795
+ });
5796
+ }
5797
+ if (popoverPanel.isPresent() && reconciler.hasOverlayDesired(VIEW_ID.popover)) {
5798
+ const [w = 0, h = 0] = ctx.windows.mainWindow.getContentSize();
5799
+ popoverPanel.reposition(computePopoverBounds(w, h, overlayHeaderHeight()));
5800
+ }
5801
+ if (projectCreateDialogPanel.isPresent() && reconciler.hasOverlayDesired(VIEW_ID.projectCreateDialog)) {
5802
+ projectCreateDialogPanel.reposition(fullWindowBounds());
5803
+ }
5804
+ if (updateDialogPanel.isPresent() && reconciler.hasOverlayDesired(VIEW_ID.updateDialog)) {
5805
+ updateDialogPanel.reposition(fullWindowBounds());
5806
+ }
5132
5807
  }
5133
5808
  function applySettingsBoundsIfPresent() {
5134
- if (reconciler.hasOverlayDesired(VIEW_ID.settings)) applySettingsBounds();
5809
+ if (settingsPanel.isPresent() && reconciler.hasOverlayDesired(VIEW_ID.settings)) {
5810
+ showSettings().catch(() => {
5811
+ });
5812
+ }
5135
5813
  }
5136
5814
  function destroySettings() {
5137
- destroyChildView(ctx.windows.mainWindow, settingsView);
5138
- settingsView = null;
5815
+ activeTooltip = null;
5816
+ settingsPanel.destroy();
5817
+ tooltipPanel.destroy();
5818
+ }
5819
+ function destroyDialogs() {
5820
+ projectCreateDialogPanel.destroy();
5821
+ updateDialogPanel.destroy();
5139
5822
  }
5140
- reconciler.registerView(VIEW_ID.settings, { getView: () => settingsView });
5141
- reconciler.registerView(VIEW_ID.popover, { getView: () => popoverView });
5142
5823
  return {
5143
5824
  showSettings,
5144
5825
  hideSettings,
5145
5826
  showPopover,
5146
5827
  hidePopover,
5828
+ prepareTooltip,
5829
+ showTooltip,
5830
+ hideTooltip,
5831
+ markOverlayReady,
5832
+ applyTooltipMeasurement,
5833
+ showProjectCreateDialog,
5834
+ hideProjectCreateDialog,
5835
+ showUpdateDialog,
5836
+ hideUpdateDialog,
5837
+ notifyUpdateDownloadProgress,
5147
5838
  reapplyPresentOverlays,
5148
5839
  applySettingsBoundsIfPresent,
5149
5840
  destroySettings,
5150
- getSettingsWebContents: () => {
5151
- if (!settingsView) return null;
5152
- if (settingsView.webContents.isDestroyed()) return null;
5153
- return settingsView.webContents;
5154
- },
5155
- getSettingsWebContentsId: () => {
5156
- if (!settingsView) return null;
5157
- if (settingsView.webContents.isDestroyed()) return null;
5158
- return settingsView.webContents.id;
5159
- },
5160
- getPopoverWebContentsId: () => {
5161
- if (!popoverView) return null;
5162
- if (popoverView.webContents.isDestroyed()) return null;
5163
- return popoverView.webContents.id;
5164
- }
5841
+ destroyDialogs,
5842
+ getSettingsWebContents: () => settingsPanel.getWebContents(),
5843
+ getSettingsWebContentsId: () => settingsPanel.getWebContentsId(),
5844
+ getPopoverWebContentsId: () => popoverPanel.getWebContentsId(),
5845
+ getTooltipWebContentsId: () => tooltipPanel.getWebContentsId(),
5846
+ getProjectCreateDialogWebContentsId: () => projectCreateDialogPanel.getWebContentsId(),
5847
+ getUpdateDialogWebContentsId: () => updateDialogPanel.getWebContentsId()
5165
5848
  };
5166
5849
  }
5167
5850
 
@@ -5218,6 +5901,12 @@ function refreshGuestStylesheets(view) {
5218
5901
  }
5219
5902
 
5220
5903
  // src/shared/simulator-route.ts
5904
+ function encodePageSpec(spec) {
5905
+ const keys = Object.keys(spec.query);
5906
+ if (keys.length === 0) return spec.pagePath;
5907
+ const qs = keys.map((k) => `${encodeURIComponent(k)}=${encodeURIComponent(spec.query[k] ?? "")}`).join("&");
5908
+ return `${spec.pagePath}?${qs}`;
5909
+ }
5221
5910
  function decodePageSpec(value) {
5222
5911
  const qIdx = value.indexOf("?");
5223
5912
  if (qIdx < 0) return { pagePath: value, query: {} };
@@ -5261,7 +5950,6 @@ import * as bridge_channels_star from "@dimina-kit/electron-runtime/shared/bridg
5261
5950
  function createNativeSimulatorView(ctx, reconciler, deps) {
5262
5951
  const { safeArea, devtoolsHost, overlayPanels } = deps;
5263
5952
  let nativeSimulatorView = null;
5264
- let nativeSimulatorViewAdded = false;
5265
5953
  let nativeSimulatorProjectPath = null;
5266
5954
  let settleNativeSimulatorReady = null;
5267
5955
  let nativeSimulatorShellReady = false;
@@ -5283,25 +5971,18 @@ function createNativeSimulatorView(ctx, reconciler, deps) {
5283
5971
  }
5284
5972
  }
5285
5973
  function tearDownNativeSimulatorView(label) {
5286
- if (!nativeSimulatorView) return;
5287
- ctx.simulatorUiExtensions?.detach(nativeSimulatorView.webContents);
5288
- if (nativeSimulatorViewAdded && !ctx.windows.mainWindow.isDestroyed()) {
5289
- try {
5290
- ctx.windows.mainWindow.contentView.removeChildView(nativeSimulatorView);
5291
- } catch {
5292
- }
5293
- }
5974
+ const view = nativeSimulatorView;
5975
+ if (!view) return;
5976
+ ctx.simulatorUiExtensions?.detach(view.webContents);
5294
5977
  try {
5295
- if (!nativeSimulatorView.webContents.isDestroyed()) {
5296
- ctx.bridge?.disposeSessionsForSimulator?.(nativeSimulatorView.webContents.id)?.catch((err2) => console.warn(`[view-manager] dispose sessions (${label}) failed:`, err2));
5297
- nativeSimulatorView.webContents.close();
5978
+ if (!view.webContents.isDestroyed()) {
5979
+ ctx.bridge?.disposeSessionsForSimulator?.(view.webContents.id)?.catch((err2) => console.warn(`[view-manager] dispose sessions (${label}) failed:`, err2));
5298
5980
  }
5299
5981
  } catch {
5300
5982
  }
5983
+ reconciler.destroyView(VIEW_ID.simulator, view);
5301
5984
  nativeSimulatorView = null;
5302
- nativeSimulatorViewAdded = false;
5303
5985
  nativeSimulatorShellReady = false;
5304
- reconciler.forgetActual(VIEW_ID.simulator);
5305
5986
  reconciler.reconcileNow();
5306
5987
  }
5307
5988
  function detachNativeCustomApiBridge() {
@@ -5473,9 +6154,6 @@ function createNativeSimulatorView(ctx, reconciler, deps) {
5473
6154
  }
5474
6155
  reconciler.registerView(VIEW_ID.simulator, {
5475
6156
  getView: () => nativeSimulatorView,
5476
- setAdded: (added) => {
5477
- nativeSimulatorViewAdded = added;
5478
- },
5479
6157
  gateHidden: () => !nativeSimulatorView,
5480
6158
  applyBounds: (view, bounds, extra) => applyNativeSimulatorBounds(view, bounds, extra?.zoom ?? 1)
5481
6159
  });
@@ -5505,6 +6183,8 @@ function createViewManager(ctx) {
5505
6183
  const hostToolbar = createHostToolbarView(ctx, reconciler, {
5506
6184
  reapplyPresentOverlays: () => reapplyToolbarDependentOverlays()
5507
6185
  });
6186
+ const hostSidebar = createHostSidebarView(ctx, reconciler);
6187
+ const hostDialog = createHostDialogView(ctx, reconciler);
5508
6188
  const overlayPanels = createOverlayPanelsView(ctx, reconciler, {
5509
6189
  getHostToolbarHeight: hostToolbar.getHostToolbarHeight
5510
6190
  });
@@ -5525,6 +6205,9 @@ function createViewManager(ctx) {
5525
6205
  workbench.cancelWorkbenchAttachHold();
5526
6206
  disposeProjectViews();
5527
6207
  hostToolbar.dispose();
6208
+ hostSidebar.dispose();
6209
+ hostDialog.dispose();
6210
+ overlayPanels.destroyDialogs();
5528
6211
  }
5529
6212
  return {
5530
6213
  attachNativeSimulator: nativeSimulator.attachNativeSimulator,
@@ -5536,7 +6219,22 @@ function createViewManager(ctx) {
5536
6219
  hideSettings: overlayPanels.hideSettings,
5537
6220
  showPopover: overlayPanels.showPopover,
5538
6221
  hidePopover: overlayPanels.hidePopover,
5539
- repositionAll: () => overlayPanels.reapplyPresentOverlays(),
6222
+ prepareTooltip: overlayPanels.prepareTooltip,
6223
+ showTooltip: overlayPanels.showTooltip,
6224
+ hideTooltip: overlayPanels.hideTooltip,
6225
+ markOverlayReady: overlayPanels.markOverlayReady,
6226
+ applyTooltipMeasurement: overlayPanels.applyTooltipMeasurement,
6227
+ showProjectCreateDialog: overlayPanels.showProjectCreateDialog,
6228
+ hideProjectCreateDialog: overlayPanels.hideProjectCreateDialog,
6229
+ showUpdateDialog: overlayPanels.showUpdateDialog,
6230
+ hideUpdateDialog: overlayPanels.hideUpdateDialog,
6231
+ notifyUpdateDownloadProgress: overlayPanels.notifyUpdateDownloadProgress,
6232
+ getProjectCreateDialogWebContentsId: overlayPanels.getProjectCreateDialogWebContentsId,
6233
+ getUpdateDialogWebContentsId: overlayPanels.getUpdateDialogWebContentsId,
6234
+ repositionAll: () => {
6235
+ overlayPanels.reapplyPresentOverlays();
6236
+ hostDialog.reposition();
6237
+ },
5540
6238
  disposeProjectViews,
5541
6239
  disposeAll,
5542
6240
  getSimulatorWebContentsId: nativeSimulator.getSimulatorWebContentsId,
@@ -5545,10 +6243,12 @@ function createViewManager(ctx) {
5545
6243
  getSettingsWebContents: overlayPanels.getSettingsWebContents,
5546
6244
  getSettingsWebContentsId: overlayPanels.getSettingsWebContentsId,
5547
6245
  getPopoverWebContentsId: overlayPanels.getPopoverWebContentsId,
6246
+ getTooltipWebContentsId: overlayPanels.getTooltipWebContentsId,
5548
6247
  getHostToolbarWebContentsId: hostToolbar.getHostToolbarWebContentsId,
5549
6248
  getHostToolbarHeight: hostToolbar.getHostToolbarHeight,
5550
6249
  resize: () => overlayPanels.applySettingsBoundsIfPresent(),
5551
6250
  setPlacementSnapshot: reconciler.setPlacementSnapshot,
6251
+ allocatePlacementGeneration: reconciler.allocateGeneration,
5552
6252
  attachWorkbench: workbench.attachWorkbench,
5553
6253
  setWorkbenchSource: workbench.setWorkbenchSource,
5554
6254
  detachWorkbench: workbench.detachWorkbench,
@@ -5556,7 +6256,14 @@ function createViewManager(ctx) {
5556
6256
  holdWorkbenchAttach: workbench.holdWorkbenchAttach,
5557
6257
  cancelWorkbenchAttachHold: workbench.cancelWorkbenchAttachHold,
5558
6258
  setHostToolbarHeight: hostToolbar.setHostToolbarHeight,
5559
- hostToolbar: hostToolbar.control
6259
+ hostToolbar: hostToolbar.control,
6260
+ getHostSidebarWebContentsId: hostSidebar.getHostSidebarWebContentsId,
6261
+ getHostSidebarWidth: hostSidebar.getHostSidebarWidth,
6262
+ setHostSidebarWidth: hostSidebar.setHostSidebarWidth,
6263
+ hostSidebar: hostSidebar.control,
6264
+ getHostDialogWebContentsId: hostDialog.getHostDialogWebContentsId,
6265
+ reportHostDialogMeasuredExtent: hostDialog.reportMeasuredExtent,
6266
+ hostDialog: hostDialog.control
5560
6267
  };
5561
6268
  }
5562
6269
 
@@ -5588,7 +6295,7 @@ function createWindowService(mainWindow) {
5588
6295
 
5589
6296
  // src/main/windows/settings-window/create.ts
5590
6297
  import { BrowserWindow as BrowserWindow4 } from "electron";
5591
- import path9 from "path";
6298
+ import path8 from "path";
5592
6299
  async function createSettingsWindow(parent, rendererDir2) {
5593
6300
  const win = new BrowserWindow4({
5594
6301
  width: 420,
@@ -5606,7 +6313,7 @@ async function createSettingsWindow(parent, rendererDir2) {
5606
6313
  }
5607
6314
  });
5608
6315
  applyNavigationHardening(win.webContents, rendererDir2);
5609
- await win.loadFile(path9.join(rendererDir2, "entries/workbench-settings/index.html"));
6316
+ await win.loadFile(path8.join(rendererDir2, "entries/workbench-settings/index.html"));
5610
6317
  return win;
5611
6318
  }
5612
6319
 
@@ -5688,11 +6395,34 @@ __export(project_repository_exports, {
5688
6395
  validateProjectDir: () => validateProjectDir
5689
6396
  });
5690
6397
  import { app as app6 } from "electron";
5691
- import path10 from "path";
6398
+ import path9 from "path";
5692
6399
  import fs5 from "fs";
5693
6400
  var log2 = createLogger("projects");
6401
+ function readProjectConfig(dirPath) {
6402
+ let merged = {};
6403
+ for (const fileName of ["project.config.json", "project.private.config.json"]) {
6404
+ const configPath = path9.join(dirPath, fileName);
6405
+ if (!fs5.existsSync(configPath)) continue;
6406
+ try {
6407
+ merged = { ...merged, ...JSON.parse(fs5.readFileSync(configPath, "utf-8")) };
6408
+ } catch (err2) {
6409
+ log2.warn(`Failed to parse ${configPath}`, err2);
6410
+ }
6411
+ }
6412
+ return merged;
6413
+ }
6414
+ function detectRuntimeType(dirPath) {
6415
+ const compileType = readProjectConfig(dirPath).compileType;
6416
+ if (compileType === "game") return "minigame";
6417
+ if (compileType === "miniprogram") return "miniprogram";
6418
+ const hasMiniProgramConfig = fs5.existsSync(path9.join(dirPath, "app.json"));
6419
+ const hasMiniGameConfig = fs5.existsSync(path9.join(dirPath, "game.json"));
6420
+ const hasMiniGameEntry = ["game.js", "game.ts"].some((fileName) => fs5.existsSync(path9.join(dirPath, fileName)));
6421
+ if (!hasMiniProgramConfig && hasMiniGameConfig && hasMiniGameEntry) return "minigame";
6422
+ return "miniprogram";
6423
+ }
5694
6424
  function getProjectsFile() {
5695
- return path10.join(app6.getPath("userData"), "dimina-projects.json");
6425
+ return path9.join(app6.getPath("userData"), "dimina-projects.json");
5696
6426
  }
5697
6427
  function load() {
5698
6428
  try {
@@ -5714,13 +6444,19 @@ function validateProjectDir(dirPath) {
5714
6444
  if (!fs5.existsSync(dirPath)) {
5715
6445
  return `\u5C0F\u7A0B\u5E8F\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${dirPath}`;
5716
6446
  }
5717
- if (!fs5.existsSync(path10.join(dirPath, "app.json"))) {
5718
- const configPath = path10.join(dirPath, "project.config.json");
6447
+ if (detectRuntimeType(dirPath) === "minigame") {
6448
+ if (!fs5.existsSync(path9.join(dirPath, "game.json"))) {
6449
+ return "\u8BE5\u76EE\u5F55\u7F3A\u5C11 game.json\uFF0C\u8BF7\u9009\u62E9\u5305\u542B\u5C0F\u6E38\u620F\u6E90\u7801\u7684\u76EE\u5F55";
6450
+ }
6451
+ return null;
6452
+ }
6453
+ if (!fs5.existsSync(path9.join(dirPath, "app.json"))) {
6454
+ const configPath = path9.join(dirPath, "project.config.json");
5719
6455
  if (fs5.existsSync(configPath)) {
5720
6456
  try {
5721
6457
  const cfg = JSON.parse(fs5.readFileSync(configPath, "utf-8"));
5722
6458
  if (cfg.miniprogramRoot) {
5723
- const resolvedRoot = path10.resolve(dirPath, cfg.miniprogramRoot);
6459
+ const resolvedRoot = path9.resolve(dirPath, cfg.miniprogramRoot);
5724
6460
  return `\u8BE5\u76EE\u5F55\u7F3A\u5C11 app.json\uFF0Cproject.config.json \u4E2D\u6307\u5B9A\u4E86 miniprogramRoot: "${cfg.miniprogramRoot}"\uFF0C\u8BF7\u5BFC\u5165 ${resolvedRoot}`;
5725
6461
  }
5726
6462
  } catch (err2) {
@@ -5736,9 +6472,9 @@ function hasProject(dirPath) {
5736
6472
  }
5737
6473
  function addProject(dirPath) {
5738
6474
  const projects = load();
5739
- let name = path10.basename(dirPath);
6475
+ let name = path9.basename(dirPath);
5740
6476
  try {
5741
- const configPath = path10.join(dirPath, "project.config.json");
6477
+ const configPath = path9.join(dirPath, "project.config.json");
5742
6478
  if (fs5.existsSync(configPath)) {
5743
6479
  const cfg = JSON.parse(fs5.readFileSync(configPath, "utf-8"));
5744
6480
  if (cfg.projectname) name = cfg.projectname;
@@ -5746,10 +6482,11 @@ function addProject(dirPath) {
5746
6482
  } catch (err2) {
5747
6483
  log2.warn("Failed to read project name from config", err2);
5748
6484
  }
5749
- const project = { name, path: dirPath, lastOpened: null };
6485
+ const type = detectRuntimeType(dirPath);
6486
+ const project = { name, path: dirPath, lastOpened: null, type };
5750
6487
  const idx = projects.findIndex((p) => p.path === dirPath);
5751
6488
  if (idx >= 0) {
5752
- projects[idx] = { ...projects[idx], name };
6489
+ projects[idx] = { ...projects[idx], name, type };
5753
6490
  } else {
5754
6491
  projects.unshift(project);
5755
6492
  }
@@ -5770,14 +6507,18 @@ function updateLastOpened(dirPath) {
5770
6507
  function getCompileConfig(dirPath) {
5771
6508
  const projects = load();
5772
6509
  const project = projects.find((p) => p.path === dirPath);
5773
- return project?.compileConfig ?? {
5774
- startPage: "",
6510
+ if (project?.compileConfig) return project.compileConfig;
6511
+ return {
6512
+ startPage: getProjectPages(dirPath).entryPagePath,
5775
6513
  scene: DEFAULT_SCENE,
5776
6514
  queryParams: []
5777
6515
  };
5778
6516
  }
5779
6517
  function getProjectPages(dirPath) {
5780
- const appJsonPath = path10.join(dirPath, "app.json");
6518
+ if (detectRuntimeType(dirPath) === "minigame") {
6519
+ return { pages: ["game"], entryPagePath: "game" };
6520
+ }
6521
+ const appJsonPath = path9.join(dirPath, "app.json");
5781
6522
  try {
5782
6523
  const appJson = JSON.parse(
5783
6524
  fs5.readFileSync(appJsonPath, "utf-8")
@@ -5804,7 +6545,7 @@ function getProjectSettings(projectPath) {
5804
6545
  return { uploadWithSourceMap: false };
5805
6546
  }
5806
6547
  try {
5807
- const configPath = path10.join(projectPath, "project.config.json");
6548
+ const configPath = path9.join(projectPath, "project.config.json");
5808
6549
  const config = JSON.parse(fs5.readFileSync(configPath, "utf-8"));
5809
6550
  return {
5810
6551
  uploadWithSourceMap: !!config.setting?.uploadWithSourceMap
@@ -5815,7 +6556,7 @@ function getProjectSettings(projectPath) {
5815
6556
  }
5816
6557
  function updateProjectSettings(projectPath, patch) {
5817
6558
  if (!projectPath) return;
5818
- const configPath = path10.join(projectPath, "project.config.json");
6559
+ const configPath = path9.join(projectPath, "project.config.json");
5819
6560
  let config = {};
5820
6561
  try {
5821
6562
  config = JSON.parse(fs5.readFileSync(configPath, "utf-8"));
@@ -5835,6 +6576,10 @@ function updateProjectSettings(projectPath, patch) {
5835
6576
  function reportRebuildStatus(info, deps) {
5836
6577
  const { pages } = deps.repo.getProjectPages(deps.projectPath);
5837
6578
  const pageList = pages.length ? pages : void 0;
6579
+ if (info?.explicit) {
6580
+ deps.sendStatus("ready", "\u7F16\u8BD1\u5B8C\u6210", false, pageList);
6581
+ return;
6582
+ }
5838
6583
  if (info?.styleOnly && deps.autoReload && deps.refreshSimulatorStyles()) {
5839
6584
  deps.sendStatus("ready", "\u6837\u5F0F\u5DF2\u70ED\u66F4\u65B0", false, pageList);
5840
6585
  return;
@@ -6116,16 +6861,16 @@ function createWorkspaceService(ctx) {
6116
6861
  // src/main/services/projects/thumbnail.ts
6117
6862
  import { createHash } from "crypto";
6118
6863
  import fs6 from "fs";
6119
- import path11 from "path";
6864
+ import path10 from "path";
6120
6865
  import { app as app7 } from "electron";
6121
6866
  function getThumbnailDir() {
6122
- return path11.join(app7.getPath("userData"), "thumbnails");
6867
+ return path10.join(app7.getPath("userData"), "thumbnails");
6123
6868
  }
6124
6869
  function hashProjectPath(projectPath) {
6125
6870
  return createHash("sha256").update(projectPath).digest("hex").slice(0, 16);
6126
6871
  }
6127
6872
  function getThumbnailPath(projectPath) {
6128
- return path11.join(getThumbnailDir(), `${hashProjectPath(projectPath)}.png`);
6873
+ return path10.join(getThumbnailDir(), `${hashProjectPath(projectPath)}.png`);
6129
6874
  }
6130
6875
  var DATA_URL_PNG_PREFIX = "data:image/png;base64,";
6131
6876
  function saveThumbnailFromDataUrl(projectPath, imageDataUrl) {
@@ -6353,20 +7098,20 @@ function sanitizeTemplates(templates) {
6353
7098
  }
6354
7099
 
6355
7100
  // src/main/services/projects/builtin-templates.ts
6356
- import path12 from "node:path";
6357
- var TEMPLATES_DIR = path12.join(devtoolsPackageRoot, "templates");
7101
+ import path11 from "node:path";
7102
+ var TEMPLATES_DIR = path11.join(devtoolsPackageRoot, "templates");
6358
7103
  var BUILTIN_TEMPLATES = [
6359
7104
  {
6360
7105
  id: "blank",
6361
7106
  name: "Blank",
6362
7107
  description: "\u6700\u5C0F\u9AA8\u67B6\uFF1Aapp.* + \u4E00\u4E2A index \u9875\u9762",
6363
- source: { type: "directory", path: path12.join(TEMPLATES_DIR, "blank") }
7108
+ source: { type: "directory", path: path11.join(TEMPLATES_DIR, "blank") }
6364
7109
  },
6365
7110
  {
6366
7111
  id: "taro-todo",
6367
7112
  name: "Taro Todo",
6368
7113
  description: "Taro \u7F16\u8BD1\u4EA7\u7269\u7684 Todo \u793A\u4F8B",
6369
- source: { type: "directory", path: path12.join(TEMPLATES_DIR, "taro-todo") }
7114
+ source: { type: "directory", path: path11.join(TEMPLATES_DIR, "taro-todo") }
6370
7115
  }
6371
7116
  ];
6372
7117
 
@@ -6584,6 +7329,37 @@ var ProjectSaveCompileConfigSchema = z3.tuple([
6584
7329
  CompileConfigShape
6585
7330
  ]);
6586
7331
  var PopoverShowSchema = z3.tuple([z3.looseObject({})]);
7332
+ var TooltipShowSchema = z3.tuple([
7333
+ z3.object({
7334
+ anchor: z3.object({
7335
+ x: z3.number().finite(),
7336
+ y: z3.number().finite(),
7337
+ width: z3.number().finite().nonnegative(),
7338
+ height: z3.number().finite().nonnegative()
7339
+ }),
7340
+ text: z3.string().min(1).max(200)
7341
+ })
7342
+ ]);
7343
+ var TooltipMeasuredSchema = z3.tuple([
7344
+ z3.object({
7345
+ requestId: z3.number().int().positive(),
7346
+ width: z3.number().finite().positive(),
7347
+ height: z3.number().finite().positive()
7348
+ })
7349
+ ]);
7350
+ var ProjectCreateShowSchema = z3.tuple([
7351
+ z3.object({
7352
+ templates: z3.array(z3.looseObject({})),
7353
+ defaultBaseDir: z3.string()
7354
+ })
7355
+ ]);
7356
+ var ProjectCreateSubmitSchema = z3.tuple([
7357
+ z3.object({
7358
+ name: z3.string().min(1),
7359
+ path: AbsolutePath,
7360
+ templateId: z3.string().min(1)
7361
+ })
7362
+ ]);
6587
7363
  var SimWidth = z3.number().int().min(100).max(2e3);
6588
7364
  var SimulatorAttachNativeSchema = z3.tuple([
6589
7365
  z3.string().url().refine((u) => u.startsWith("http://") || u.startsWith("https://"), {
@@ -6649,6 +7425,18 @@ var HostToolbarAdvertiseHeightSchema = z3.tuple([
6649
7425
  extent: NonNegInt
6650
7426
  })
6651
7427
  ]);
7428
+ var HostSidebarAdvertiseWidthSchema = z3.tuple([
7429
+ z3.object({
7430
+ axis: z3.literal("inline"),
7431
+ extent: NonNegInt
7432
+ })
7433
+ ]);
7434
+ var HostDialogAdvertiseSizeSchema = z3.tuple([
7435
+ z3.object({
7436
+ axis: z3.union([z3.literal("block"), z3.literal("inline")]),
7437
+ extent: NonNegInt
7438
+ })
7439
+ ]);
6652
7440
  var SimulatorCustomApiInvokeSchema = z3.tuple([
6653
7441
  z3.string().min(1).max(256),
6654
7442
  z3.unknown()
@@ -6731,13 +7519,48 @@ var popoverModule = {
6731
7519
  setup: (ctx) => registerPopoverIpc(ctx)
6732
7520
  };
6733
7521
 
7522
+ // src/main/ipc/tooltip.ts
7523
+ function registerTooltipIpc(ctx) {
7524
+ return new IpcRegistry(ctx.senderPolicy).on(TooltipChannel.Prepare, () => {
7525
+ ctx.views.prepareTooltip();
7526
+ }).on(TooltipChannel.Show, (_event, ...args) => {
7527
+ const [data] = validate(TooltipChannel.Show, TooltipShowSchema, args);
7528
+ ctx.views.showTooltip(data);
7529
+ }).on(TooltipChannel.Hide, () => {
7530
+ ctx.views.hideTooltip();
7531
+ }).on(OverlayChannel.Ready, (event) => {
7532
+ ctx.views.markOverlayReady(event.sender.id);
7533
+ }).on(TooltipChannel.Measured, (event, ...args) => {
7534
+ const [measurement] = validate(
7535
+ TooltipChannel.Measured,
7536
+ TooltipMeasuredSchema,
7537
+ args
7538
+ );
7539
+ ctx.views.applyTooltipMeasurement(event.sender.id, measurement);
7540
+ });
7541
+ }
7542
+
7543
+ // src/main/ipc/project-create.ts
7544
+ function registerProjectCreateIpc(ctx) {
7545
+ return new IpcRegistry(ctx.senderPolicy).on(ProjectCreateChannel.Show, (_event, ...args) => {
7546
+ const [data] = validate(ProjectCreateChannel.Show, ProjectCreateShowSchema, args);
7547
+ ctx.views.showProjectCreateDialog(data);
7548
+ }).on(ProjectCreateChannel.Cancel, () => {
7549
+ ctx.views.hideProjectCreateDialog();
7550
+ }).on(ProjectCreateChannel.Submit, (_event, ...args) => {
7551
+ const [input] = validate(ProjectCreateChannel.Submit, ProjectCreateSubmitSchema, args);
7552
+ ctx.views.hideProjectCreateDialog();
7553
+ ctx.notify.projectCreateSubmitted(input);
7554
+ });
7555
+ }
7556
+
6734
7557
  // src/main/ipc/projects.ts
6735
7558
  import { app as app8, dialog } from "electron";
6736
- import path14 from "path";
7559
+ import path13 from "path";
6737
7560
 
6738
7561
  // src/main/services/projects/create-project-service.ts
6739
7562
  import fs7 from "node:fs";
6740
- import path13 from "node:path";
7563
+ import path12 from "node:path";
6741
7564
  var DEFAULT_TEMPLATE_ID = "blank";
6742
7565
  function resolveName(rawName) {
6743
7566
  const name = (rawName ?? "").trim();
@@ -6793,7 +7616,7 @@ async function materializeTemplate(target, template, name) {
6793
7616
  });
6794
7617
  }
6795
7618
  function writeProjectConfig(target, name) {
6796
- const cfgPath = path13.join(target, "project.config.json");
7619
+ const cfgPath = path12.join(target, "project.config.json");
6797
7620
  let cfg = {};
6798
7621
  if (fs7.existsSync(cfgPath)) {
6799
7622
  try {
@@ -6850,6 +7673,9 @@ function registerProjectsIpc(ctx) {
6850
7673
  buttons: ["\u786E\u5B9A"]
6851
7674
  });
6852
7675
  }
7676
+ const importedCategory = project.type ?? "miniprogram";
7677
+ ctx.notify.hostSidebarCategorySelected(importedCategory);
7678
+ ctx.views.hostSidebar.send("project-category-forced", { category: importedCategory });
6853
7679
  return project;
6854
7680
  }).handle(ProjectsChannel.Remove, (_event, ...args) => {
6855
7681
  const [dirPath] = validate(ProjectsChannel.Remove, ProjectsRemoveSchema, args);
@@ -6887,7 +7713,7 @@ function registerProjectsIpc(ctx) {
6887
7713
  }
6888
7714
  try {
6889
7715
  const settings = loadWorkbenchSettings();
6890
- const newBase = path14.dirname(input.path);
7716
+ const newBase = path13.dirname(input.path);
6891
7717
  if (newBase && settings.lastCreateBaseDir !== newBase) {
6892
7718
  saveWorkbenchSettings({ ...settings, lastCreateBaseDir: newBase });
6893
7719
  }
@@ -6936,6 +7762,12 @@ function registerSessionIpc(ctx) {
6936
7762
  return ctx.workspace.saveCompileConfig(projectPath, config);
6937
7763
  }).handle(ProjectChannel.Close, () => {
6938
7764
  return ctx.workspace.closeProject();
7765
+ }).handle(ProjectChannel.Rebuild, async () => {
7766
+ const session3 = ctx.workspace.getSession();
7767
+ if (!session3) throw new Error("project:rebuild \u2014 no active project session");
7768
+ if (typeof session3.rebuild !== "function") return { supported: false };
7769
+ await session3.rebuild();
7770
+ return { supported: true };
6939
7771
  }).handle(ProjectChannel.CaptureThumbnail, (_, ...args) => {
6940
7772
  const [projectPath] = validate(
6941
7773
  ProjectChannel.CaptureThumbnail,
@@ -7010,13 +7842,13 @@ function registerSettingsIpc(ctx) {
7010
7842
  };
7011
7843
  }).handle(WorkbenchSettingsChannel.GetMcpStatus, () => {
7012
7844
  const settings = loadWorkbenchSettings();
7013
- const runtime = getMcpStatus();
7845
+ const runtime4 = getMcpStatus();
7014
7846
  return {
7015
7847
  configured: settings.mcp.enabled,
7016
7848
  configuredPort: settings.mcp.port,
7017
- running: runtime.running,
7018
- activePort: runtime.port,
7019
- error: runtime.error
7849
+ running: runtime4.running,
7850
+ activePort: runtime4.port,
7851
+ error: runtime4.error
7020
7852
  };
7021
7853
  }).handle(SettingsChannel.SetVisible, async (_, ...args) => {
7022
7854
  const [visible] = validate(SettingsChannel.SetVisible, SettingsSetVisibleSchema, args);
@@ -7064,6 +7896,76 @@ function registerInternalDevtoolsIpc(ctx) {
7064
7896
  });
7065
7897
  }
7066
7898
 
7899
+ // src/main/ipc/views.ts
7900
+ import { ipcMain as ipcMain3 } from "electron";
7901
+ function registerViewsIpc(ctx) {
7902
+ const registry = new IpcRegistry(ctx.senderPolicy).handle(ViewChannel.AllocatePlacementGeneration, () => ctx.views.allocatePlacementGeneration()).handle(ViewChannel.PlacementSnapshot, (_event, ...args) => {
7903
+ const [snapshot] = validate(
7904
+ ViewChannel.PlacementSnapshot,
7905
+ PlacementSnapshotSchema,
7906
+ args
7907
+ );
7908
+ ctx.views.setPlacementSnapshot(snapshot);
7909
+ }).handle(ViewChannel.HostToolbarGetHeight, () => ctx.views.getHostToolbarHeight()).handle(ViewChannel.HostSidebarGetWidth, () => ctx.views.getHostSidebarWidth());
7910
+ const onAdvertiseHeight = (event, ...args) => {
7911
+ if (event.sender.id !== ctx.views.getHostToolbarWebContentsId()) return;
7912
+ let extent;
7913
+ try {
7914
+ ;
7915
+ [{ extent }] = validate(
7916
+ ViewChannel.HostToolbarAdvertiseHeight,
7917
+ HostToolbarAdvertiseHeightSchema,
7918
+ args
7919
+ );
7920
+ } catch {
7921
+ return;
7922
+ }
7923
+ ctx.views.setHostToolbarHeight(extent);
7924
+ };
7925
+ ipcMain3.on(ViewChannel.HostToolbarAdvertiseHeight, onAdvertiseHeight);
7926
+ const onAdvertiseWidth = (event, ...args) => {
7927
+ if (event.sender.id !== ctx.views.getHostSidebarWebContentsId()) return;
7928
+ let extent;
7929
+ try {
7930
+ ;
7931
+ [{ extent }] = validate(
7932
+ ViewChannel.HostSidebarAdvertiseWidth,
7933
+ HostSidebarAdvertiseWidthSchema,
7934
+ args
7935
+ );
7936
+ } catch {
7937
+ return;
7938
+ }
7939
+ ctx.views.setHostSidebarWidth(extent);
7940
+ };
7941
+ ipcMain3.on(ViewChannel.HostSidebarAdvertiseWidth, onAdvertiseWidth);
7942
+ const onAdvertiseDialogSize = (event, ...args) => {
7943
+ if (event.sender.id !== ctx.views.getHostDialogWebContentsId()) return;
7944
+ let axis;
7945
+ let extent;
7946
+ try {
7947
+ ;
7948
+ [{ axis, extent }] = validate(
7949
+ ViewChannel.HostDialogAdvertiseSize,
7950
+ HostDialogAdvertiseSizeSchema,
7951
+ args
7952
+ );
7953
+ } catch {
7954
+ return;
7955
+ }
7956
+ ctx.views.reportHostDialogMeasuredExtent(axis, extent);
7957
+ };
7958
+ ipcMain3.on(ViewChannel.HostDialogAdvertiseSize, onAdvertiseDialogSize);
7959
+ return {
7960
+ dispose() {
7961
+ void registry.dispose();
7962
+ ipcMain3.removeListener(ViewChannel.HostToolbarAdvertiseHeight, onAdvertiseHeight);
7963
+ ipcMain3.removeListener(ViewChannel.HostSidebarAdvertiseWidth, onAdvertiseWidth);
7964
+ ipcMain3.removeListener(ViewChannel.HostDialogAdvertiseSize, onAdvertiseDialogSize);
7965
+ }
7966
+ };
7967
+ }
7968
+
7067
7969
  // src/main/ipc/simulator-module.ts
7068
7970
  import { DisposableRegistry as DisposableRegistry3 } from "@dimina-kit/electron-deck/main";
7069
7971
 
@@ -7098,47 +8000,11 @@ function installBridgeRouter(ctx) {
7098
8000
  installRuntimeBridgeRouter(ctx);
7099
8001
  }
7100
8002
 
7101
- // src/main/ipc/views.ts
7102
- import { ipcMain as ipcMain3 } from "electron";
7103
- function registerViewsIpc(ctx) {
7104
- const registry = new IpcRegistry(ctx.senderPolicy).handle(ViewChannel.PlacementSnapshot, (_event, ...args) => {
7105
- const [snapshot] = validate(
7106
- ViewChannel.PlacementSnapshot,
7107
- PlacementSnapshotSchema,
7108
- args
7109
- );
7110
- ctx.views.setPlacementSnapshot(snapshot);
7111
- }).handle(ViewChannel.HostToolbarGetHeight, () => ctx.views.getHostToolbarHeight());
7112
- const onAdvertiseHeight = (event, ...args) => {
7113
- if (event.sender.id !== ctx.views.getHostToolbarWebContentsId()) return;
7114
- let extent;
7115
- try {
7116
- ;
7117
- [{ extent }] = validate(
7118
- ViewChannel.HostToolbarAdvertiseHeight,
7119
- HostToolbarAdvertiseHeightSchema,
7120
- args
7121
- );
7122
- } catch {
7123
- return;
7124
- }
7125
- ctx.views.setHostToolbarHeight(extent);
7126
- };
7127
- ipcMain3.on(ViewChannel.HostToolbarAdvertiseHeight, onAdvertiseHeight);
7128
- return {
7129
- dispose() {
7130
- void registry.dispose();
7131
- ipcMain3.removeListener(ViewChannel.HostToolbarAdvertiseHeight, onAdvertiseHeight);
7132
- }
7133
- };
7134
- }
7135
-
7136
8003
  // src/main/ipc/simulator-module.ts
7137
8004
  var simulatorModule = {
7138
8005
  setup: (ctx) => {
7139
8006
  const reg = new DisposableRegistry3();
7140
8007
  reg.add(registerSimulatorIpc(ctx));
7141
- reg.add(registerViewsIpc(ctx));
7142
8008
  installBridgeRouter(ctx);
7143
8009
  return reg;
7144
8010
  }
@@ -8324,7 +9190,7 @@ function registerContextTools(server) {
8324
9190
  }
8325
9191
 
8326
9192
  // src/main/services/mcp/tools/project-tools.ts
8327
- import path15 from "node:path";
9193
+ import path14 from "node:path";
8328
9194
  import { z as z5 } from "zod";
8329
9195
  var DEFAULT_OPEN_TIMEOUT_MS = 18e4;
8330
9196
  var DEFAULT_WAIT_TIMEOUT_MS = 6e4;
@@ -8360,7 +9226,7 @@ function registerProjectTools(server, host) {
8360
9226
  timeoutMs: z5.number().optional().default(DEFAULT_OPEN_TIMEOUT_MS).describe("Max time to wait for the compile to settle")
8361
9227
  },
8362
9228
  async ({ path: rawPath, timeoutMs }) => {
8363
- const dir = path15.resolve(rawPath);
9229
+ const dir = path14.resolve(rawPath);
8364
9230
  const current = host.sessionStatus.get();
8365
9231
  if (host.workspace.getProjectPath() === dir && host.workspace.hasActiveSession() && current.phase === "ready") {
8366
9232
  return text({ ...statusPayload(host), note: "project already open" });
@@ -8370,7 +9236,7 @@ function registerProjectTools(server, host) {
8370
9236
  let project;
8371
9237
  if (await host.workspace.hasProject(dir)) {
8372
9238
  const known = (await host.workspace.listProjects()).find((p) => p.path === dir);
8373
- project = known ?? { name: path15.basename(dir), path: dir };
9239
+ project = known ?? { name: path14.basename(dir), path: dir };
8374
9240
  } else {
8375
9241
  project = await host.workspace.addProject(dir);
8376
9242
  }
@@ -9241,7 +10107,9 @@ function setupSimulatorCurrentPage(host, options) {
9241
10107
  const off = options.bridge.onRenderEvent((ev) => {
9242
10108
  if (ev.kind !== "activePage" || !ev.pagePath) return;
9243
10109
  if (host.isDestroyed()) return;
9244
- host.send(SimulatorChannel.CurrentPage, ev.pagePath);
10110
+ const query = {};
10111
+ for (const [k, v] of Object.entries(ev.query ?? {})) query[k] = String(v);
10112
+ host.send(SimulatorChannel.CurrentPage, encodePageSpec({ pagePath: ev.pagePath, query }));
9245
10113
  });
9246
10114
  registry.add(off);
9247
10115
  return registry;
@@ -9249,7 +10117,7 @@ function setupSimulatorCurrentPage(host, options) {
9249
10117
 
9250
10118
  // src/main/services/render-inspect/index.ts
9251
10119
  import { readFileSync } from "node:fs";
9252
- import path16 from "node:path";
10120
+ import path15 from "node:path";
9253
10121
  var DEFAULT_SOURCE_PATH = "dist/render-host/render-inspect.js";
9254
10122
  var HIGHLIGHT_CONFIG = {
9255
10123
  showInfo: true,
@@ -9275,7 +10143,7 @@ function withTimeout(p, ms) {
9275
10143
  });
9276
10144
  }
9277
10145
  function createRenderInspector(options = {}) {
9278
- const loadSource = options.loadSource ?? (() => readFileSync(path16.join(devtoolsPackageRoot, DEFAULT_SOURCE_PATH), "utf8"));
10146
+ const loadSource = options.loadSource ?? (() => readFileSync(path15.join(devtoolsPackageRoot, DEFAULT_SOURCE_PATH), "utf8"));
9279
10147
  let cachedSource = null;
9280
10148
  const injected = /* @__PURE__ */ new Set();
9281
10149
  const broker = options.broker ?? createCdpSessionBroker({ connections: options.connections });
@@ -9397,11 +10265,11 @@ import * as session_policy_star from "@dimina-kit/electron-runtime/main/session-
9397
10265
  import http from "node:http";
9398
10266
  import nodeFs3 from "node:fs";
9399
10267
  import fs8 from "node:fs/promises";
9400
- import path18 from "node:path";
10268
+ import path17 from "node:path";
9401
10269
 
9402
10270
  // src/main/services/fs-watch-sse.ts
9403
10271
  import nodeFs2 from "node:fs";
9404
- import path17 from "node:path";
10272
+ import path16 from "node:path";
9405
10273
 
9406
10274
  // src/main/services/http-json.ts
9407
10275
  function jsonRes(res, code, obj) {
@@ -9413,7 +10281,7 @@ function jsonRes(res, code, obj) {
9413
10281
  // src/main/services/fs-watch-sse.ts
9414
10282
  var WATCH_DEBOUNCE_MS = 80;
9415
10283
  function shouldReportWatchPath(rel) {
9416
- if (rel === "" || rel.startsWith("..") || path17.isAbsolute(rel)) return false;
10284
+ if (rel === "" || rel.startsWith("..") || path16.isAbsolute(rel)) return false;
9417
10285
  return !rel.split("/").some((segment) => SKIP_DIRS.has(segment));
9418
10286
  }
9419
10287
  function handleFsWatchRequest(req, res, getProjectRoot) {
@@ -9454,7 +10322,7 @@ function handleFsWatchRequest(req, res, getProjectRoot) {
9454
10322
  `);
9455
10323
  }
9456
10324
  function onChange(_event, filename) {
9457
- const rel = filename ? filename.toString().split(path17.sep).join("/") : ".";
10325
+ const rel = filename ? filename.toString().split(path16.sep).join("/") : ".";
9458
10326
  if (rel !== "." && !shouldReportWatchPath(rel)) return;
9459
10327
  pending.add(rel);
9460
10328
  if (!debounceTimer) {
@@ -9509,8 +10377,8 @@ function setIsolationHeaders(res) {
9509
10377
  }
9510
10378
  function containedStaticPath(root, pathname) {
9511
10379
  const rel = pathname.replace(/^\/+/, "") || "index.html";
9512
- const resolved = path18.resolve(root, rel);
9513
- if (resolved !== root && !resolved.startsWith(root + path18.sep)) return null;
10380
+ const resolved = path17.resolve(root, rel);
10381
+ if (resolved !== root && !resolved.startsWith(root + path17.sep)) return null;
9514
10382
  return resolved;
9515
10383
  }
9516
10384
  function serveStaticFile(res, root, pathname) {
@@ -9521,7 +10389,7 @@ function serveStaticFile(res, root, pathname) {
9521
10389
  return;
9522
10390
  }
9523
10391
  Promise.all([fs8.realpath(root), fs8.realpath(candidate)]).then(([realRoot, realFile]) => {
9524
- if (realFile !== realRoot && !realFile.startsWith(realRoot + path18.sep)) {
10392
+ if (realFile !== realRoot && !realFile.startsWith(realRoot + path17.sep)) {
9525
10393
  res.writeHead(403);
9526
10394
  res.end("Forbidden");
9527
10395
  return;
@@ -9533,7 +10401,7 @@ function serveStaticFile(res, root, pathname) {
9533
10401
  return;
9534
10402
  }
9535
10403
  res.writeHead(200, {
9536
- "Content-Type": MIME[path18.extname(realFile)] ?? "application/octet-stream",
10404
+ "Content-Type": MIME[path17.extname(realFile)] ?? "application/octet-stream",
9537
10405
  "Content-Length": stat.size,
9538
10406
  "Cache-Control": "no-store"
9539
10407
  });
@@ -9687,7 +10555,7 @@ async function listFilesRecursive(dir, base = "") {
9687
10555
  for (const e of entries) {
9688
10556
  const rel = base ? `${base}/${e.name}` : e.name;
9689
10557
  if (e.isDirectory()) {
9690
- out.push(...await listFilesRecursive(path18.join(dir, e.name), rel));
10558
+ out.push(...await listFilesRecursive(path17.join(dir, e.name), rel));
9691
10559
  } else if (e.isFile()) {
9692
10560
  out.push(rel);
9693
10561
  }
@@ -9704,9 +10572,9 @@ async function readContributedExtensions(extensionsDir) {
9704
10572
  }
9705
10573
  for (const e of entries) {
9706
10574
  if (!e.isDirectory()) continue;
9707
- const extDir = path18.join(extensionsDir, e.name);
10575
+ const extDir = path17.join(extensionsDir, e.name);
9708
10576
  try {
9709
- const pkgRaw = await fs8.readFile(path18.join(extDir, "package.json"), "utf8");
10577
+ const pkgRaw = await fs8.readFile(path17.join(extDir, "package.json"), "utf8");
9710
10578
  const files = await listFilesRecursive(extDir);
9711
10579
  out.push({ dir: e.name, packageJson: JSON.parse(pkgRaw), files });
9712
10580
  } catch {
@@ -9714,9 +10582,13 @@ async function readContributedExtensions(extensionsDir) {
9714
10582
  }
9715
10583
  return out;
9716
10584
  }
10585
+ function workspaceIdOf(identity) {
10586
+ if (!identity?.appId) return null;
10587
+ return (0, miniapp_partition_exports.miniappPartitionKey)(identity.appId, identity.projectPath || null);
10588
+ }
9717
10589
  async function startWorkbenchCoiServer(options) {
9718
- const root = path18.resolve(options.rootDir);
9719
- const contribRoot = options.extensionsDir ? path18.resolve(options.extensionsDir) : null;
10590
+ const root = path17.resolve(options.rootDir);
10591
+ const contribRoot = options.extensionsDir ? path17.resolve(options.extensionsDir) : null;
9720
10592
  const server = http.createServer((req, res) => {
9721
10593
  setIsolationHeaders(res);
9722
10594
  const url = new URL(req.url ?? "/", "http://127.0.0.1");
@@ -9734,6 +10606,10 @@ async function startWorkbenchCoiServer(options) {
9734
10606
  jsonRes(res, 200, options.getFileTypes?.() ?? {});
9735
10607
  return;
9736
10608
  }
10609
+ if (url.pathname === "/__project") {
10610
+ jsonRes(res, 200, { workspaceId: workspaceIdOf(options.getProjectIdentity?.()) });
10611
+ return;
10612
+ }
9737
10613
  if (url.pathname.startsWith("/__contrib/")) {
9738
10614
  if (!contribRoot) {
9739
10615
  res.writeHead(404);
@@ -9772,7 +10648,9 @@ async function startWorkbenchCoiServer(options) {
9772
10648
  import { app as app11, shell as shell3 } from "electron";
9773
10649
  var UpdateManager = class {
9774
10650
  checker;
9775
- mainWindow;
10651
+ showUpdatePanel;
10652
+ notifyDownloadProgress;
10653
+ hideUpdatePanel;
9776
10654
  getCurrentVersion;
9777
10655
  latestUpdate = null;
9778
10656
  downloadedPath = null;
@@ -9789,16 +10667,26 @@ var UpdateManager = class {
9789
10667
  timer = null;
9790
10668
  initialTimer = null;
9791
10669
  ipc;
10670
+ /**
10671
+ * Set by `dispose()`. Clearing the timers only stops FUTURE ticks — an
10672
+ * in-flight `checkAndNotify()` invocation (already past its `await
10673
+ * this.check()`) has no timer left to cancel it, and would otherwise call
10674
+ * `showUpdatePanel` after dispose has torn down the overlay panel that
10675
+ * owns (e.g. `views.disposeAll()` destroying its WebContentsView).
10676
+ */
10677
+ disposed = false;
9792
10678
  constructor(opts) {
9793
10679
  this.checker = opts.checker;
9794
- this.mainWindow = opts.mainWindow;
10680
+ this.showUpdatePanel = opts.showUpdatePanel;
10681
+ this.notifyDownloadProgress = opts.notifyDownloadProgress;
10682
+ this.hideUpdatePanel = opts.hideUpdatePanel;
9795
10683
  this.getCurrentVersion = opts.getCurrentVersion ?? (() => app11.getVersion());
9796
10684
  this.ipc = new IpcRegistry(opts.senderPolicy);
9797
10685
  this.registerIpc();
9798
10686
  this.startPeriodicCheck(opts.checkInterval ?? 60 * 60 * 1e3, opts.initialDelay ?? 5e3);
9799
10687
  }
9800
10688
  registerIpc() {
9801
- this.ipc.handle(UpdateChannel.Check, async () => this.check()).handle(UpdateChannel.Download, async () => this.download()).handle(UpdateChannel.Install, async () => this.install());
10689
+ this.ipc.handle(UpdateChannel.Check, async () => this.check()).handle(UpdateChannel.Download, async () => this.download()).handle(UpdateChannel.Install, async () => this.install()).on(UpdateChannel.Close, () => this.hideUpdatePanel());
9802
10690
  }
9803
10691
  async check() {
9804
10692
  try {
@@ -9822,7 +10710,7 @@ var UpdateManager = class {
9822
10710
  }
9823
10711
  try {
9824
10712
  const filePath = await this.checker.downloadUpdate(this.latestUpdate, (percent) => {
9825
- this.mainWindow.webContents.send(UpdateChannel.DownloadProgress, { percent });
10713
+ this.notifyDownloadProgress(percent);
9826
10714
  });
9827
10715
  this.downloadedPath = filePath;
9828
10716
  return { success: true, filePath };
@@ -9850,6 +10738,7 @@ var UpdateManager = class {
9850
10738
  * the timer clears + handler removals are scheduled synchronously.
9851
10739
  */
9852
10740
  dispose() {
10741
+ this.disposed = true;
9853
10742
  if (this.timer) {
9854
10743
  clearInterval(this.timer);
9855
10744
  this.timer = null;
@@ -9866,16 +10755,17 @@ var UpdateManager = class {
9866
10755
  }
9867
10756
  async checkAndNotify() {
9868
10757
  const result = await this.check();
10758
+ if (this.disposed) return;
9869
10759
  if (!result.hasUpdate || !result.info) return;
9870
10760
  if (this.hasNotified) return;
9871
10761
  this.hasNotified = true;
9872
- this.mainWindow.webContents.send(UpdateChannel.Available, result.info);
10762
+ this.showUpdatePanel(result.info);
9873
10763
  }
9874
10764
  };
9875
10765
 
9876
10766
  // src/main/services/update/github-release-checker.ts
9877
10767
  import fs9 from "fs";
9878
- import path19 from "path";
10768
+ import path18 from "path";
9879
10769
  import https from "https";
9880
10770
  import { app as app12 } from "electron";
9881
10771
  function createGitHubReleaseChecker(opts) {
@@ -9901,10 +10791,10 @@ function createGitHubReleaseChecker(opts) {
9901
10791
  };
9902
10792
  },
9903
10793
  async downloadUpdate(info, onProgress) {
9904
- const tmpDir = path19.join(app12.getPath("temp"), `${opts.repo}-update`);
10794
+ const tmpDir = path18.join(app12.getPath("temp"), `${opts.repo}-update`);
9905
10795
  if (!fs9.existsSync(tmpDir)) fs9.mkdirSync(tmpDir, { recursive: true });
9906
- const fileName = path19.basename(new URL(info.downloadUrl).pathname);
9907
- const filePath = path19.join(tmpDir, fileName);
10796
+ const fileName = path18.basename(new URL(info.downloadUrl).pathname);
10797
+ const filePath = path18.join(tmpDir, fileName);
9908
10798
  await downloadFile(info.downloadUrl, filePath, opts.token, onProgress);
9909
10799
  return filePath;
9910
10800
  }
@@ -10187,7 +11077,7 @@ async function disposeContext(ctx) {
10187
11077
  function createConfiguredMainWindow(config, rendererDir2) {
10188
11078
  const mainWindow = createMainWindow({
10189
11079
  title: config.appName ?? "Dimina DevTools",
10190
- indexHtml: path20.join(rendererDir2, "entries/main/index.html"),
11080
+ indexHtml: path19.join(rendererDir2, "entries/main/index.html"),
10191
11081
  width: config.window?.width,
10192
11082
  height: config.window?.height,
10193
11083
  minWidth: config.window?.minWidth,
@@ -10311,7 +11201,7 @@ function enableDevRendererAutoReload(rendererDir2) {
10311
11201
  const watcher = fs10.watch(rendererDir2, { recursive: true }, () => {
10312
11202
  if (reloadTimer) clearTimeout(reloadTimer);
10313
11203
  reloadTimer = setTimeout(() => {
10314
- for (const win of BrowserWindow6.getAllWindows()) {
11204
+ for (const win of BrowserWindow5.getAllWindows()) {
10315
11205
  win.webContents.reload();
10316
11206
  }
10317
11207
  }, 300);
@@ -10350,6 +11240,9 @@ async function createDevtoolsRuntime(config = {}, onInstanceCreated) {
10350
11240
  context.internalDevtoolsWindow = createInternalDevtoolsWindow(mainWindow, { isAppQuitting });
10351
11241
  context.registry.add(toDisposable5(() => context.internalDevtoolsWindow?.dispose()));
10352
11242
  context.registry.add(registerInternalDevtoolsIpc(context));
11243
+ context.registry.add(registerTooltipIpc(context));
11244
+ context.registry.add(registerProjectCreateIpc(context));
11245
+ context.registry.add(registerViewsIpc(context));
10353
11246
  context.registry.add((0, simulator_session_policy_exports.setupSimulatorSessionPolicy)());
10354
11247
  context.registry.add(installThemeBackgroundSync());
10355
11248
  if (!config.adapter) context.registry.add(setupCompileWorkerStandby(context));
@@ -10402,13 +11295,27 @@ async function createDevtoolsRuntime(config = {}, onInstanceCreated) {
10402
11295
  instance.registerSimulatorApi("login", async () => {
10403
11296
  return "hello";
10404
11297
  });
11298
+ context.registry.add(
11299
+ context.views.hostSidebar.onMessage("project-category-selected", (payload) => {
11300
+ const category = payload?.category;
11301
+ if (category !== "miniprogram" && category !== "minigame") return;
11302
+ context.notify.hostSidebarCategorySelected(category);
11303
+ })
11304
+ );
11305
+ context.views.hostSidebar.loadFile(path19.join(rendererDir2, "entries/host-sidebar-default/index.html")).catch((err2) => {
11306
+ console.warn("[workbench] failed to load host-sidebar default content:", err2);
11307
+ });
11308
+ context.views.hostSidebar.setWidthMode({ fixed: HOST_SIDEBAR_DEFAULT_WIDTH });
11309
+ context.views.hostSidebar.setWidthMode("auto");
10405
11310
  if (config.onSetup) {
10406
11311
  await config.onSetup(instance);
10407
11312
  }
10408
11313
  if (config.updateChecker) {
10409
11314
  instance.updateManager = new UpdateManager({
10410
11315
  checker: config.updateChecker,
10411
- mainWindow,
11316
+ showUpdatePanel: (info) => context.views.showUpdateDialog(info),
11317
+ notifyDownloadProgress: (percent) => context.views.notifyUpdateDownloadProgress(percent),
11318
+ hideUpdatePanel: () => context.views.hideUpdateDialog(),
10412
11319
  senderPolicy: context.senderPolicy,
10413
11320
  checkInterval: config.updateOptions?.checkInterval,
10414
11321
  initialDelay: config.updateOptions?.initialDelay,
@@ -10534,8 +11441,8 @@ async function createDevtoolsRuntime(config = {}, onInstanceCreated) {
10534
11441
  bridge: context.bridge
10535
11442
  }));
10536
11443
  }
10537
- const bundleDir = config.editorViewConfig?.bundleDir ?? path20.join(devtoolsPackageRoot, "dist/vscode-workbench");
10538
- if (!fs10.existsSync(path20.join(bundleDir, "index.html"))) {
11444
+ const bundleDir = config.editorViewConfig?.bundleDir ?? path19.join(devtoolsPackageRoot, "dist/vscode-workbench");
11445
+ if (!fs10.existsSync(path19.join(bundleDir, "index.html"))) {
10539
11446
  console.warn(
10540
11447
  `[workbench] editor bundle not found at ${bundleDir} \u2014 skipping embedded editor assembly`
10541
11448
  );
@@ -10544,7 +11451,14 @@ async function createDevtoolsRuntime(config = {}, onInstanceCreated) {
10544
11451
  rootDir: bundleDir,
10545
11452
  getProjectRoot: () => context.workspace.getProjectPath(),
10546
11453
  extensionsDir: config.editorViewConfig?.extensionsDir,
10547
- getFileTypes: () => context.fileTypes
11454
+ getFileTypes: () => context.fileTypes,
11455
+ // Names the editor's VS Code workspace after the active miniapp, so each
11456
+ // project gets its own open-editors/view-state bucket instead of all of
11457
+ // them sharing the one derived from the constant mirror root.
11458
+ getProjectIdentity: () => ({
11459
+ appId: getActiveAppId(),
11460
+ projectPath: context.workspace.getProjectPath()
11461
+ })
10548
11462
  });
10549
11463
  context.registry.add(() => coiServer.close());
10550
11464
  context.registry.add(() => {