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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (277) hide show
  1. package/README.md +52 -729
  2. package/dist/main/app/app.d.ts +27 -7
  3. package/dist/main/app/app.js +285 -681
  4. package/dist/main/app/editor-view.d.ts +21 -0
  5. package/dist/main/app/editor-view.js +71 -0
  6. package/dist/main/app/global-mirrors.d.ts +23 -0
  7. package/dist/main/app/global-mirrors.js +37 -0
  8. package/dist/main/app/host-sidebar-default.d.ts +19 -0
  9. package/dist/main/app/host-sidebar-default.js +44 -0
  10. package/dist/main/app/menu-setup.d.ts +13 -0
  11. package/dist/main/app/menu-setup.js +49 -0
  12. package/dist/main/app/open-project-ipc.d.ts +13 -0
  13. package/dist/main/app/open-project-ipc.js +21 -0
  14. package/dist/main/app/project-list-surface.d.ts +31 -0
  15. package/dist/main/app/project-list-surface.js +20 -0
  16. package/dist/main/app/project-window.d.ts +36 -0
  17. package/dist/main/app/project-window.js +93 -0
  18. package/dist/main/app/servers.d.ts +17 -0
  19. package/dist/main/app/servers.js +60 -0
  20. package/dist/main/app/ui-extension-targets.d.ts +42 -0
  21. package/dist/main/app/ui-extension-targets.js +101 -0
  22. package/dist/main/app/window-close-reveal.harness.d.ts +34 -0
  23. package/dist/main/app/window-close-reveal.harness.js +65 -0
  24. package/dist/main/app/window-events.d.ts +15 -0
  25. package/dist/main/app/window-events.js +45 -0
  26. package/dist/main/app/window-runtime-services.d.ts +41 -0
  27. package/dist/main/app/window-runtime-services.js +178 -0
  28. package/dist/main/app/workbench-modules.d.ts +24 -0
  29. package/dist/main/app/workbench-modules.js +51 -0
  30. package/dist/main/app/workbench-window.d.ts +85 -0
  31. package/dist/main/app/workbench-window.js +310 -0
  32. package/dist/main/index.bundle.js +10669 -9096
  33. package/dist/main/ipc/app.d.ts +4 -1
  34. package/dist/main/ipc/app.js +4 -3
  35. package/dist/main/ipc/device-picker.d.ts +16 -0
  36. package/dist/main/ipc/device-picker.js +27 -0
  37. package/dist/main/ipc/index.d.ts +1 -0
  38. package/dist/main/ipc/index.js +1 -0
  39. package/dist/main/ipc/internal-devtools.d.ts +2 -1
  40. package/dist/main/ipc/internal-devtools.js +4 -3
  41. package/dist/main/ipc/popover.d.ts +4 -1
  42. package/dist/main/ipc/popover.js +25 -9
  43. package/dist/main/ipc/project-create.d.ts +2 -1
  44. package/dist/main/ipc/project-create.js +6 -5
  45. package/dist/main/ipc/project-fs.d.ts +3 -1
  46. package/dist/main/ipc/project-fs.js +17 -17
  47. package/dist/main/ipc/projects.d.ts +3 -3
  48. package/dist/main/ipc/projects.js +13 -12
  49. package/dist/main/ipc/session.d.ts +4 -1
  50. package/dist/main/ipc/session.js +16 -11
  51. package/dist/main/ipc/settings.d.ts +4 -1
  52. package/dist/main/ipc/settings.js +12 -19
  53. package/dist/main/ipc/simulator-module.d.ts +5 -0
  54. package/dist/main/ipc/simulator-module.js +9 -5
  55. package/dist/main/ipc/simulator.d.ts +4 -1
  56. package/dist/main/ipc/simulator.js +12 -18
  57. package/dist/main/ipc/tooltip.d.ts +2 -1
  58. package/dist/main/ipc/tooltip.js +8 -7
  59. package/dist/main/ipc/views.d.ts +4 -1
  60. package/dist/main/ipc/views.js +20 -9
  61. package/dist/main/runtime/devtools-backend-before-quit.harness.d.ts +2 -20
  62. package/dist/main/runtime/devtools-backend-before-quit.harness.js +14 -283
  63. package/dist/main/runtime/devtools-backend.js +14 -3
  64. package/dist/main/runtime/devtools-runtime-mock.harness.d.ts +21 -0
  65. package/dist/main/runtime/devtools-runtime-mock.harness.js +313 -0
  66. package/dist/main/services/app-services.d.ts +62 -0
  67. package/dist/main/services/app-services.js +64 -0
  68. package/dist/main/services/automation/connection-target.d.ts +36 -0
  69. package/dist/main/services/automation/connection-target.js +29 -0
  70. package/dist/main/services/automation/console-bridge.d.ts +28 -0
  71. package/dist/main/services/automation/console-bridge.js +91 -0
  72. package/dist/main/services/automation/handlers/app.js +4 -4
  73. package/dist/main/services/automation/handlers/element.js +55 -21
  74. package/dist/main/services/automation/index.d.ts +12 -1
  75. package/dist/main/services/automation/index.js +54 -133
  76. package/dist/main/services/default-adapter.js +1 -1
  77. package/dist/main/services/fs-bridge-protocol.d.ts +61 -0
  78. package/dist/main/services/fs-bridge-protocol.js +34 -0
  79. package/dist/main/services/fs-watch-sse.d.ts +8 -1
  80. package/dist/main/services/fs-watch-sse.js +19 -5
  81. package/dist/main/services/mcp/connection-owner.d.ts +38 -0
  82. package/dist/main/services/mcp/connection-owner.js +79 -0
  83. package/dist/main/services/mcp/index.d.ts +3 -1
  84. package/dist/main/services/mcp/index.js +4 -3
  85. package/dist/main/services/mcp/opened-project.d.ts +88 -0
  86. package/dist/main/services/mcp/opened-project.js +60 -0
  87. package/dist/main/services/mcp/target-manager.d.ts +71 -23
  88. package/dist/main/services/mcp/target-manager.js +269 -142
  89. package/dist/main/services/mcp/target-selection.d.ts +47 -0
  90. package/dist/main/services/mcp/target-selection.js +71 -0
  91. package/dist/main/services/mcp/tools/context-tools.js +20 -5
  92. package/dist/main/services/mcp/tools/project-tools.d.ts +29 -6
  93. package/dist/main/services/mcp/tools/project-tools.js +37 -25
  94. package/dist/main/services/module.d.ts +13 -5
  95. package/dist/main/services/notifications/renderer-notifier.d.ts +28 -4
  96. package/dist/main/services/notifications/renderer-notifier.js +15 -3
  97. package/dist/main/services/projects/local-provider.js +2 -0
  98. package/dist/main/services/projects/project-repository.d.ts +31 -1
  99. package/dist/main/services/projects/project-repository.js +144 -20
  100. package/dist/main/services/projects/types.d.ts +31 -8
  101. package/dist/main/services/safe-area/index.d.ts +19 -5
  102. package/dist/main/services/safe-area/index.js +38 -13
  103. package/dist/main/services/simulator/ui-extensions.d.ts +8 -0
  104. package/dist/main/services/simulator/ui-extensions.js +16 -6
  105. package/dist/main/services/simulator-storage/index.d.ts +12 -1
  106. package/dist/main/services/simulator-storage/index.js +22 -7
  107. package/dist/main/services/simulator-storage/window-scope.d.ts +20 -0
  108. package/dist/main/services/simulator-storage/window-scope.js +20 -0
  109. package/dist/main/services/views/host-slot-port-channel.d.ts +20 -3
  110. package/dist/main/services/views/host-slot-port-channel.js +33 -5
  111. package/dist/main/services/views/managed-web-contents-view.js +21 -1
  112. package/dist/main/services/views/native-simulator-view.js +13 -16
  113. package/dist/main/services/views/overlay-panels-view.d.ts +13 -0
  114. package/dist/main/services/views/overlay-panels-view.js +31 -0
  115. package/dist/main/services/views/view-manager.d.ts +1 -1
  116. package/dist/main/services/views/view-manager.js +3 -0
  117. package/dist/main/services/window-contexts/context-router.d.ts +36 -0
  118. package/dist/main/services/window-contexts/context-router.js +55 -0
  119. package/dist/main/services/workbench-coi-host.d.ts +52 -0
  120. package/dist/main/services/workbench-coi-host.js +227 -0
  121. package/dist/main/services/workbench-coi-server.js +75 -133
  122. package/dist/main/services/workbench-coi-shutdown.d.ts +40 -0
  123. package/dist/main/services/workbench-coi-shutdown.js +69 -0
  124. package/dist/main/services/workbench-coi-static.d.ts +23 -0
  125. package/dist/main/services/workbench-coi-static.js +87 -0
  126. package/dist/main/services/workbench-context.d.ts +27 -3
  127. package/dist/main/services/workbench-context.js +10 -5
  128. package/dist/main/services/workspace/compile-mode-adapter.d.ts +18 -0
  129. package/dist/main/services/workspace/compile-mode-adapter.js +57 -0
  130. package/dist/main/services/workspace/compile-mode-store.d.ts +25 -0
  131. package/dist/main/services/workspace/compile-mode-store.js +71 -0
  132. package/dist/main/services/workspace/workspace-compile-modes.d.ts +59 -0
  133. package/dist/main/services/workspace/workspace-compile-modes.js +93 -0
  134. package/dist/main/services/workspace/workspace-referer.d.ts +17 -0
  135. package/dist/main/services/workspace/workspace-referer.js +26 -0
  136. package/dist/main/services/workspace/workspace-service.d.ts +4 -0
  137. package/dist/main/services/workspace/workspace-service.js +51 -84
  138. package/dist/main/services/workspace/workspace-thumbnails.d.ts +22 -0
  139. package/dist/main/services/workspace/workspace-thumbnails.js +67 -0
  140. package/dist/main/utils/ipc-context-source.d.ts +40 -0
  141. package/dist/main/utils/ipc-context-source.js +23 -0
  142. package/dist/main/utils/ipc-registry.d.ts +42 -36
  143. package/dist/main/utils/ipc-registry.js +117 -50
  144. package/dist/main/utils/sender-policy.d.ts +9 -0
  145. package/dist/main/utils/sender-policy.js +26 -3
  146. package/dist/main/utils/theme.d.ts +3 -2
  147. package/dist/main/utils/theme.js +3 -2
  148. package/dist/main/windows/main-window/create.d.ts +16 -0
  149. package/dist/main/windows/main-window/create.js +19 -11
  150. package/dist/main/windows/main-window/events.d.ts +1 -1
  151. package/dist/main/windows/main-window/events.js +26 -13
  152. package/dist/main/windows/main-window/index.d.ts +1 -1
  153. package/dist/main/windows/main-window/index.js +1 -1
  154. package/dist/native-host/common/common.js +94 -73
  155. package/dist/native-host/render/render.js +4079 -3171
  156. package/dist/native-host/service/service.js +2 -2
  157. package/dist/preload/runtime/native-host.d.ts +4 -0
  158. package/dist/preload/runtime/temp-files.js +9 -2
  159. package/dist/preload/runtime/wait-for-slot-root.d.ts +7 -4
  160. package/dist/preload/runtime/wait-for-slot-root.js +36 -8
  161. package/dist/preload/windows/host-dialog-runtime.cjs +10 -3
  162. package/dist/preload/windows/host-dialog-runtime.cjs.map +2 -2
  163. package/dist/preload/windows/host-sidebar-runtime.cjs +10 -3
  164. package/dist/preload/windows/host-sidebar-runtime.cjs.map +2 -2
  165. package/dist/preload/windows/host-toolbar-runtime.cjs +10 -3
  166. package/dist/preload/windows/host-toolbar-runtime.cjs.map +2 -2
  167. package/dist/preload/windows/main.cjs +181 -4
  168. package/dist/preload/windows/main.cjs.map +4 -4
  169. package/dist/preload/windows/simulator.cjs +10 -0
  170. package/dist/preload/windows/simulator.cjs.map +2 -2
  171. package/dist/preload/windows/simulator.js +10 -0
  172. package/dist/render-host/pageFrame.html +1 -1
  173. package/dist/render-host/preload.cjs +16 -4
  174. package/dist/renderer/assets/button-CB2MWjHY.js +2 -0
  175. package/dist/renderer/assets/constants-CkoGPQTS.js +2 -0
  176. package/dist/renderer/assets/constants-zQZ2-y21.js +2 -0
  177. package/dist/renderer/assets/{createLucideIcon-Bqi8hpdi.js → createLucideIcon-cZ9FlZZw.js} +2 -2
  178. package/dist/renderer/assets/devicePicker-H2eIO4e1.js +2 -0
  179. package/dist/renderer/assets/dialog-pHzEA8Vx.js +46 -0
  180. package/dist/renderer/assets/dist-B5HADirS.js +2 -0
  181. package/dist/renderer/assets/{dist-DKWdE1TK.js → dist-DIFlRMKe.js} +2 -2
  182. package/dist/renderer/assets/hostSidebarDefault-DRWh6OCt.js +2 -0
  183. package/dist/renderer/assets/index-CEL_XFge.js +2 -0
  184. package/dist/renderer/assets/{input-Ct3alvkq.js → input-CAObw95L.js} +2 -2
  185. package/dist/renderer/assets/ipc-channels-overlays-gIPUFnqc.js +2 -0
  186. package/dist/renderer/assets/jsx-runtime-DICEQH_0.css +1 -0
  187. package/dist/renderer/assets/{jsx-runtime-DGLHmmOq.js → jsx-runtime-DIw5wnYl.js} +1 -1
  188. package/dist/renderer/assets/plus-BQ9u5EbL.js +2 -0
  189. package/dist/renderer/assets/popover-Cw7G2Ctp.js +2 -0
  190. package/dist/renderer/assets/presets-DMZOb9ay.js +2 -0
  191. package/dist/renderer/assets/project-api-YTOr_Yvo.js +2 -0
  192. package/dist/renderer/assets/{projectCreateDialog-Bbjz75bP.js → projectCreateDialog-BmLqiYxn.js} +2 -2
  193. package/dist/renderer/assets/search-BMvuYF48.js +2 -0
  194. package/dist/renderer/assets/select-nFP95TxM.js +2 -0
  195. package/dist/renderer/assets/settings-CnpVfSWt.js +2 -0
  196. package/dist/renderer/assets/settings-api-Ccsf-Nsj.js +2 -0
  197. package/dist/renderer/assets/settings-tab-bar-CAzrv32m.js +2 -0
  198. package/dist/renderer/assets/smartphone-Bb2GdIKy.js +2 -0
  199. package/dist/renderer/assets/tabs-CPKN3Mmq.js +2 -0
  200. package/dist/renderer/assets/{tooltip-B_-_YVbw.js → tooltip-D6AxXRk5.js} +2 -2
  201. package/dist/renderer/assets/types-Bh0hA7El.js +2 -0
  202. package/dist/renderer/assets/updateDialog-CwndJ0Bl.js +2 -0
  203. package/dist/renderer/assets/{utils-kfQov6Pa.js → utils-sEB5OZEQ.js} +2 -2
  204. package/dist/renderer/assets/view-api-B-2zPBR5.js +2 -0
  205. package/dist/renderer/assets/view-ids-BfaDVV5P.js +2 -0
  206. package/dist/renderer/assets/workbench-BLJ0wKsg.js +5 -0
  207. package/dist/renderer/assets/{workbenchSettings-CV4LVA9b.js → workbenchSettings-YD0Ctbm_.js} +3 -3
  208. package/dist/renderer/entries/device-picker/index.html +28 -0
  209. package/dist/renderer/entries/host-sidebar-default/index.html +6 -5
  210. package/dist/renderer/entries/main/index.html +16 -17
  211. package/dist/renderer/entries/popover/index.html +16 -10
  212. package/dist/renderer/entries/project-create-dialog/index.html +12 -12
  213. package/dist/renderer/entries/settings/index.html +10 -10
  214. package/dist/renderer/entries/tooltip/index.html +5 -5
  215. package/dist/renderer/entries/update-dialog/index.html +10 -10
  216. package/dist/renderer/entries/workbench/index.html +49 -0
  217. package/dist/renderer/entries/workbench-settings/index.html +12 -11
  218. package/dist/service-host/host-env-update.cjs +23 -0
  219. package/dist/service-host/preload.cjs +24 -49
  220. package/dist/service-host/service-message-queue.cjs +71 -0
  221. package/dist/service-host/sync-impls/system-info.js +31 -9
  222. package/dist/shared/compile-mode-state.d.ts +73 -0
  223. package/dist/shared/compile-mode-state.js +100 -0
  224. package/dist/shared/compile-modes.d.ts +152 -0
  225. package/dist/shared/compile-modes.js +237 -0
  226. package/dist/shared/ipc-channels-overlays.d.ts +8 -2
  227. package/dist/shared/ipc-channels-overlays.js +19 -2
  228. package/dist/shared/ipc-channels.d.ts +5 -10
  229. package/dist/shared/ipc-channels.js +17 -14
  230. package/dist/shared/ipc-schemas.d.ts +76 -24
  231. package/dist/shared/ipc-schemas.js +55 -10
  232. package/dist/shared/project-window-types.d.ts +42 -0
  233. package/dist/shared/project-window-types.js +2 -0
  234. package/dist/shared/types.d.ts +43 -17
  235. package/dist/shared/view-ids.d.ts +1 -0
  236. package/dist/shared/view-ids.js +3 -1
  237. package/dist/simulator/assets/bridge-channels-B3JAmRfl.js +2 -0
  238. package/dist/simulator/assets/device-shell-ATEhHV2_.css +1 -0
  239. package/dist/simulator/assets/device-shell-pxnZZp10.js +431 -0
  240. package/dist/simulator/assets/simulator-Dwp7lnvu.js +10 -0
  241. package/dist/simulator/assets/simulator-mini-app-DFo7fhi5.js +2 -0
  242. package/dist/simulator/assets/simulator-ui-B2ddJB1R.js +2 -0
  243. package/dist/simulator/simulator.html +3 -3
  244. package/dist/simulator/temp-files.d.ts +6 -0
  245. package/dist/simulator/temp-files.js +46 -4
  246. package/dist/simulator/types.d.ts +7 -0
  247. package/dist/vscode-workbench/assets/__vite-browser-external-JIkWewFf.js +1 -0
  248. package/dist/vscode-workbench/assets/{dist-CMfmHflW.js → dist-CXG-u1VB.js} +3 -3
  249. package/dist/vscode-workbench/assets/{iconv-lite-umd-Bx8CgaZq.js → iconv-lite-umd-Dxx9Qyb4.js} +1 -1
  250. package/dist/vscode-workbench/assets/{index-CdG6YWhW.js → index-6VJMsaWj.js} +460 -460
  251. package/dist/vscode-workbench/assets/{jschardet-M0qgd2lN.js → jschardet-Ba3D16vt.js} +1 -1
  252. package/dist/vscode-workbench/index.html +1 -1
  253. package/package.json +11 -8
  254. package/dist/renderer/assets/button-D5Q2wkXq.js +0 -2
  255. package/dist/renderer/assets/constants-D7DfVqMp.js +0 -2
  256. package/dist/renderer/assets/constants-D8zsejFq.js +0 -2
  257. package/dist/renderer/assets/dialog-e1TecTOY.js +0 -46
  258. package/dist/renderer/assets/hostSidebarDefault-B3yLfy8j.js +0 -2
  259. package/dist/renderer/assets/index-CsPD68N_.js +0 -5
  260. package/dist/renderer/assets/ipc-channels-overlays-LsDzGAtV.js +0 -2
  261. package/dist/renderer/assets/jsx-runtime-2vXHouwr.css +0 -1
  262. package/dist/renderer/assets/popover-Dix5cm_b.js +0 -2
  263. package/dist/renderer/assets/project-api-k7Oq9iaq.js +0 -2
  264. package/dist/renderer/assets/select-acGb0AmJ.js +0 -2
  265. package/dist/renderer/assets/settings-CQrTMr43.js +0 -2
  266. package/dist/renderer/assets/settings-api-CrUQ9rYV.js +0 -2
  267. package/dist/renderer/assets/settings-tab-bar-Cw1kVIf2.js +0 -2
  268. package/dist/renderer/assets/types-CsEKtFyr.js +0 -2
  269. package/dist/renderer/assets/updateDialog-NsF8223A.js +0 -2
  270. package/dist/renderer/assets/view-api-CLn3rcQE.js +0 -2
  271. package/dist/simulator/assets/bridge-channels-zhsumfky.js +0 -2
  272. package/dist/simulator/assets/device-shell-CmtM1QxP.js +0 -2
  273. package/dist/simulator/assets/device-shell-iFZo1igL.css +0 -1
  274. package/dist/simulator/assets/simulator-CIobn3Mh.js +0 -10
  275. package/dist/simulator/assets/simulator-mini-app-CRe6n9br.js +0 -2
  276. package/dist/simulator/assets/simulator-ui-C_cP602X.js +0 -2
  277. package/dist/vscode-workbench/assets/__vite-browser-external-Bc1vizTf.js +0 -1
@@ -0,0 +1,313 @@
1
+ /**
2
+ * Shared electron/fs/`@dimina-kit/devkit` mock core for
3
+ * `window-close-reveal.harness.ts` (app/) and
4
+ * `devtools-backend-before-quit.harness.ts` (runtime/) — both those harnesses
5
+ * drive a REAL runtime (`createDevtoolsRuntime` or `createDevtoolsBackend`)
6
+ * against the same mock set (lifted from `quit-flag-onclose.test.ts`) and
7
+ * previously duplicated it in full; this module holds the part that's
8
+ * actually identical between them.
9
+ *
10
+ * `BrowserWindow` here tracks visible/minimized state and implements
11
+ * `hide`/`isVisible`/`minimize`/`isMinimized`/`restore` on top of `show`/
12
+ * `showInactive` — the window-close-reveal guards need all of that, while the
13
+ * before-quit/shutdown-race tests only ever `destroy()` a window and never
14
+ * observe the extra state, so carrying it there is a no-op.
15
+ *
16
+ * `vi.mock(...)` calls here run once per importing test file's own isolated
17
+ * module graph (vitest scopes mocking per test file). A harness that imports
18
+ * this module chains into that same module graph, so importing THIS module
19
+ * (directly, or transitively through one of the two harnesses above) has the
20
+ * same effect as if each `vi.mock` call were written directly in the test
21
+ * file itself.
22
+ */
23
+ import { vi } from 'vitest';
24
+ // ── Hoisted stub state ──────────────────────────────────────────────────
25
+ const stubs = vi.hoisted(() => {
26
+ const handlers = new Map();
27
+ const projectsJsonPath = '/tmp/dimina-test-userdata/dimina-projects.json';
28
+ let projectsJsonContent = null;
29
+ const projectsWithAppJson = new Set();
30
+ function makeEmitter() {
31
+ const listeners = {};
32
+ return {
33
+ listeners,
34
+ on(event, fn) {
35
+ ;
36
+ (listeners[event] ??= new Set()).add(fn);
37
+ return this;
38
+ },
39
+ once(event, fn) {
40
+ const wrap = (...args) => {
41
+ listeners[event]?.delete(wrap);
42
+ return fn(...args);
43
+ };
44
+ (listeners[event] ??= new Set()).add(wrap);
45
+ return this;
46
+ },
47
+ off(event, fn) {
48
+ listeners[event]?.delete(fn);
49
+ return this;
50
+ },
51
+ removeListener(event, fn) {
52
+ listeners[event]?.delete(fn);
53
+ return this;
54
+ },
55
+ emit(event, ...args) {
56
+ for (const fn of [...(listeners[event] ?? [])])
57
+ fn(...args);
58
+ },
59
+ };
60
+ }
61
+ function reset() {
62
+ handlers.clear();
63
+ projectsJsonContent = null;
64
+ projectsWithAppJson.clear();
65
+ }
66
+ return {
67
+ handlers,
68
+ projectsJsonPath,
69
+ getProjectsJson() {
70
+ return projectsJsonContent;
71
+ },
72
+ setProjectsJson(v) {
73
+ projectsJsonContent = v;
74
+ },
75
+ projectsWithAppJson,
76
+ makeEmitter,
77
+ reset,
78
+ };
79
+ });
80
+ // ── electron stub ────────────────────────────────────────────────────────
81
+ vi.mock('electron', () => {
82
+ const ipcEmitter = stubs.makeEmitter();
83
+ const ipcMain = {
84
+ ...ipcEmitter,
85
+ handle: vi.fn((channel, fn) => {
86
+ stubs.handlers.set(channel, fn);
87
+ }),
88
+ removeHandler: vi.fn((channel) => {
89
+ stubs.handlers.delete(channel);
90
+ }),
91
+ on: vi.fn((event, fn) => ipcEmitter.on(event, fn)),
92
+ removeListener: vi.fn((event, fn) => ipcEmitter.removeListener(event, fn)),
93
+ };
94
+ const appEmitter = stubs.makeEmitter();
95
+ const app = {
96
+ ...appEmitter,
97
+ isPackaged: true,
98
+ whenReady: vi.fn(() => Promise.resolve()),
99
+ getPath: vi.fn(() => '/tmp/dimina-test-userdata'),
100
+ quit: vi.fn(),
101
+ setName: vi.fn(),
102
+ commandLine: {
103
+ getSwitchValue: vi.fn(() => ''),
104
+ appendSwitch: vi.fn(),
105
+ },
106
+ };
107
+ // `WebContents` and `BrowserWindow` below both wrap a `stubs.makeEmitter()`
108
+ // and need the SAME on/once/off/removeListener/emit delegation (the
109
+ // emitter's own methods use `this` internally, so a plain property copy
110
+ // would call them with the wrong receiver — each needs `.bind(em)`). Shared
111
+ // base class instead of re-declaring the same 5 bound fields in both.
112
+ class EmittingStub {
113
+ em = stubs.makeEmitter();
114
+ on = this.em.on.bind(this.em);
115
+ once = this.em.once.bind(this.em);
116
+ off = this.em.off.bind(this.em);
117
+ removeListener = this.em.removeListener.bind(this.em);
118
+ emit = this.em.emit.bind(this.em);
119
+ }
120
+ class WebContents extends EmittingStub {
121
+ destroyed = false;
122
+ id = Math.floor(Math.random() * 1e6);
123
+ send = vi.fn();
124
+ isDestroyed = () => this.destroyed;
125
+ openDevTools = vi.fn();
126
+ closeDevTools = vi.fn();
127
+ setDevToolsWebContents = vi.fn();
128
+ setWindowOpenHandler = vi.fn();
129
+ loadFile = vi.fn(() => Promise.resolve());
130
+ loadURL = vi.fn(() => Promise.resolve());
131
+ executeJavaScript = vi.fn(() => Promise.resolve(undefined));
132
+ reload = vi.fn();
133
+ getType = () => 'window';
134
+ getURL = () => '';
135
+ debugger = {
136
+ attach: vi.fn(),
137
+ detach: vi.fn(),
138
+ isAttached: () => false,
139
+ on: vi.fn(),
140
+ removeListener: vi.fn(),
141
+ sendCommand: vi.fn(() => Promise.resolve({ entries: [] })),
142
+ };
143
+ close = vi.fn(() => {
144
+ this.destroyed = true;
145
+ });
146
+ }
147
+ class WebContentsView {
148
+ webContents = new WebContents();
149
+ setBounds = vi.fn();
150
+ setBackgroundColor = vi.fn();
151
+ }
152
+ class View {
153
+ children = [];
154
+ addChildView(child) {
155
+ this.children.push(child);
156
+ }
157
+ removeChildView(child) {
158
+ const i = this.children.indexOf(child);
159
+ if (i >= 0)
160
+ this.children.splice(i, 1);
161
+ }
162
+ }
163
+ class BrowserWindow extends EmittingStub {
164
+ destroyed = false;
165
+ // The list window and every workbench window start shown/restored — the
166
+ // reveal/hide guards this state exists for only ever move a window AWAY
167
+ // from it, never establish it.
168
+ visible = true;
169
+ minimized = false;
170
+ webContents = new WebContents();
171
+ contentView = new WebContentsView();
172
+ isDestroyed = () => this.destroyed;
173
+ isVisible = () => this.visible && !this.destroyed;
174
+ isMinimized = () => this.minimized;
175
+ getContentSize = () => [1280, 980];
176
+ setIcon = vi.fn();
177
+ setTitle = vi.fn();
178
+ show = vi.fn(() => {
179
+ this.visible = true;
180
+ });
181
+ showInactive = vi.fn(() => {
182
+ this.visible = true;
183
+ });
184
+ hide = vi.fn(() => {
185
+ this.visible = false;
186
+ });
187
+ minimize = vi.fn(() => {
188
+ this.minimized = true;
189
+ });
190
+ restore = vi.fn(() => {
191
+ this.minimized = false;
192
+ this.visible = true;
193
+ });
194
+ focus = vi.fn();
195
+ close = vi.fn();
196
+ destroy = vi.fn(() => {
197
+ this.destroyed = true;
198
+ });
199
+ loadFile = vi.fn(() => Promise.resolve());
200
+ loadURL = vi.fn(() => Promise.resolve());
201
+ static getAllWindows = vi.fn(() => []);
202
+ }
203
+ const sessionStub = {
204
+ fromPartition: vi.fn(() => ({
205
+ webRequest: {
206
+ onBeforeSendHeaders: vi.fn(),
207
+ onHeadersReceived: vi.fn(),
208
+ },
209
+ registerPreloadScript: vi.fn(),
210
+ protocol: { handle: vi.fn(), unhandle: vi.fn() },
211
+ })),
212
+ defaultSession: {
213
+ protocol: { handle: vi.fn(), unhandle: vi.fn() },
214
+ },
215
+ };
216
+ const dialog = {
217
+ showOpenDialog: vi.fn(() => Promise.resolve({ canceled: true, filePaths: [] })),
218
+ showMessageBox: vi.fn(() => Promise.resolve({ response: 0 })),
219
+ };
220
+ const Menu = {
221
+ buildFromTemplate: vi.fn((tpl) => ({ template: tpl })),
222
+ setApplicationMenu: vi.fn(),
223
+ };
224
+ const shell = {
225
+ openExternal: vi.fn(() => Promise.resolve()),
226
+ openPath: vi.fn(() => Promise.resolve('')),
227
+ };
228
+ const nativeImage = {
229
+ createFromPath: vi.fn(() => ({ isEmpty: () => true })),
230
+ };
231
+ const nativeTheme = { ...stubs.makeEmitter(), themeSource: 'system' };
232
+ const globalShortcut = {
233
+ register: vi.fn(() => false),
234
+ unregister: vi.fn(),
235
+ unregisterAll: vi.fn(),
236
+ };
237
+ const webContentsStatic = {
238
+ fromId: vi.fn(() => null),
239
+ getAllWebContents: vi.fn(() => []),
240
+ };
241
+ const Tray = vi.fn();
242
+ return {
243
+ app,
244
+ ipcMain,
245
+ BrowserWindow,
246
+ WebContentsView,
247
+ BrowserView: WebContentsView,
248
+ View,
249
+ webContents: webContentsStatic,
250
+ session: sessionStub,
251
+ protocol: { registerSchemesAsPrivileged: vi.fn(), handle: vi.fn(), unhandle: vi.fn() },
252
+ dialog,
253
+ Menu,
254
+ shell,
255
+ nativeImage,
256
+ nativeTheme,
257
+ globalShortcut,
258
+ Tray,
259
+ default: {},
260
+ };
261
+ });
262
+ vi.mock('fs', async () => {
263
+ const real = await vi.importActual('fs');
264
+ function existsSync(p) {
265
+ const s = String(p);
266
+ if (s === stubs.projectsJsonPath)
267
+ return stubs.getProjectsJson() !== null;
268
+ if (s.endsWith('/app.json') || s.endsWith('\\app.json')) {
269
+ const dir = s.replace(/[\\/]app\.json$/, '');
270
+ return stubs.projectsWithAppJson.has(dir);
271
+ }
272
+ return true;
273
+ }
274
+ function readFileSync(p, opts) {
275
+ const s = String(p);
276
+ if (s === stubs.projectsJsonPath) {
277
+ const content = stubs.getProjectsJson();
278
+ if (content === null)
279
+ throw Object.assign(new Error('ENOENT'), { code: 'ENOENT' });
280
+ return content;
281
+ }
282
+ return real.readFileSync(p, opts);
283
+ }
284
+ function writeFileSync(p, data) {
285
+ const s = String(p);
286
+ if (s === stubs.projectsJsonPath) {
287
+ stubs.setProjectsJson(typeof data === 'string' ? data : Buffer.from(data).toString('utf8'));
288
+ }
289
+ }
290
+ const mocked = {
291
+ ...real,
292
+ existsSync,
293
+ readFileSync,
294
+ writeFileSync,
295
+ mkdirSync: vi.fn(),
296
+ statSync: vi.fn(() => ({ isDirectory: () => true, isFile: () => false, size: 0, mtimeMs: 0 })),
297
+ watch: vi.fn(),
298
+ realpathSync: vi.fn((p) => p),
299
+ };
300
+ return { ...mocked, default: mocked };
301
+ });
302
+ const devkitStubs = vi.hoisted(() => ({
303
+ sessionClose: vi.fn(() => Promise.resolve()),
304
+ }));
305
+ vi.mock('@dimina-kit/devkit', () => ({
306
+ openProject: vi.fn(() => Promise.resolve({
307
+ port: 12345,
308
+ appInfo: { appId: 'fakeApp' },
309
+ close: devkitStubs.sessionClose,
310
+ })),
311
+ }));
312
+ export { stubs, devkitStubs };
313
+ //# sourceMappingURL=devtools-runtime-mock.harness.js.map
@@ -0,0 +1,62 @@
1
+ import type { BrowserWindow } from 'electron';
2
+ import { type Disposable } from '@dimina-kit/electron-deck/main';
3
+ import type { ProjectsProvider, ProjectTemplate } from './projects/types.js';
4
+ import { type SimulatorApiRegistry } from './simulator/custom-apis.js';
5
+ /**
6
+ * The services that belong to the whole application rather than to one window.
7
+ *
8
+ * They are all registered or configured ONCE by the host (through
9
+ * `WorkbenchAppConfig` and the `WorkbenchAppInstance` registration methods),
10
+ * so every window context reads the same instance. Anything whose owner is a
11
+ * single window — views, connections, the CDP broker, the workspace — stays on
12
+ * `WorkbenchContext` instead.
13
+ */
14
+ export interface AppServices {
15
+ /** Backing store for the project list. One list, shown by every window. */
16
+ projectsProvider: ProjectsProvider;
17
+ /** Built-in templates merged with the host's, per `builtinTemplates`. */
18
+ projectTemplates: ProjectTemplate[];
19
+ /**
20
+ * Reference-counted `webContents.id` → live registration count for
21
+ * host-owned BrowserWindows registered as trusted senders. A host window is
22
+ * registered against the app, not against whichever window happened to be
23
+ * open at the time, so the map is shared and every context's sender policy
24
+ * consults the same entries.
25
+ */
26
+ trustedWindowSenderIds: Map<number, number>;
27
+ /**
28
+ * Simulator custom APIs registered by the host. The host registers each
29
+ * handler once (in `onSetup`), so the registry is app-wide: a window opened
30
+ * later must answer the same `wx.*` calls as the one that was open during
31
+ * registration.
32
+ */
33
+ simulatorApis: SimulatorApiRegistry;
34
+ }
35
+ export interface CreateAppServicesOptions {
36
+ projectsProvider?: ProjectsProvider;
37
+ projectTemplates?: ProjectTemplate[];
38
+ builtinTemplates?: 'all' | 'none' | readonly string[];
39
+ }
40
+ export declare function createAppServices(opts?: CreateAppServicesOptions): AppServices;
41
+ /**
42
+ * Adds `win.webContents` to the trusted-sender set and returns a Disposable
43
+ * that removes it again.
44
+ *
45
+ * Trust is reference-counted: registering the SAME window N times keeps it
46
+ * trusted until every one of the N returned Disposables has been disposed.
47
+ * Each register bumps the count, each dispose decrements it, and the window is
48
+ * un-trusted only when the count reaches zero.
49
+ *
50
+ * The window's `closed` event short-circuits the ref-count: it deletes the
51
+ * map entry outright (the window is dead, so it must be un-trusted
52
+ * immediately regardless of how many Disposables are still outstanding).
53
+ * After `closed`, disposing any leftover Disposable is a safe no-op — the
54
+ * map entry is already gone, so the decrement finds `undefined` and bails
55
+ * without driving the count negative or resurrecting trust.
56
+ *
57
+ * Each returned Disposable is itself idempotent (`removed` flag), and its
58
+ * `closed` listener removes itself once fired so a long-lived app doesn't
59
+ * accumulate dead listeners on closed windows.
60
+ */
61
+ export declare function registerTrustedWindow(services: Pick<AppServices, 'trustedWindowSenderIds'>, win: BrowserWindow): Disposable;
62
+ //# sourceMappingURL=app-services.d.ts.map
@@ -0,0 +1,64 @@
1
+ import { toDisposable } from '@dimina-kit/electron-deck/main';
2
+ import { createLocalProjectsProvider } from './projects/local-provider.js';
3
+ import { resolveTemplates } from './projects/templates.js';
4
+ import { BUILTIN_TEMPLATES } from './projects/builtin-templates.js';
5
+ import { createSimulatorApiRegistry, } from './simulator/custom-apis.js';
6
+ export function createAppServices(opts = {}) {
7
+ return {
8
+ projectsProvider: opts.projectsProvider ?? createLocalProjectsProvider(),
9
+ projectTemplates: resolveTemplates(BUILTIN_TEMPLATES, opts.projectTemplates ?? [], opts.builtinTemplates ?? 'all'),
10
+ trustedWindowSenderIds: new Map(),
11
+ simulatorApis: createSimulatorApiRegistry(),
12
+ };
13
+ }
14
+ /**
15
+ * Adds `win.webContents` to the trusted-sender set and returns a Disposable
16
+ * that removes it again.
17
+ *
18
+ * Trust is reference-counted: registering the SAME window N times keeps it
19
+ * trusted until every one of the N returned Disposables has been disposed.
20
+ * Each register bumps the count, each dispose decrements it, and the window is
21
+ * un-trusted only when the count reaches zero.
22
+ *
23
+ * The window's `closed` event short-circuits the ref-count: it deletes the
24
+ * map entry outright (the window is dead, so it must be un-trusted
25
+ * immediately regardless of how many Disposables are still outstanding).
26
+ * After `closed`, disposing any leftover Disposable is a safe no-op — the
27
+ * map entry is already gone, so the decrement finds `undefined` and bails
28
+ * without driving the count negative or resurrecting trust.
29
+ *
30
+ * Each returned Disposable is itself idempotent (`removed` flag), and its
31
+ * `closed` listener removes itself once fired so a long-lived app doesn't
32
+ * accumulate dead listeners on closed windows.
33
+ */
34
+ export function registerTrustedWindow(services, win) {
35
+ const senderId = win.webContents.id;
36
+ const counts = services.trustedWindowSenderIds;
37
+ counts.set(senderId, (counts.get(senderId) ?? 0) + 1);
38
+ let removed = false;
39
+ const onClosed = () => {
40
+ // The window is gone: zero the ref-count for this sender id outright,
41
+ // regardless of any other outstanding registrations for the same window.
42
+ counts.delete(senderId);
43
+ win.removeListener('closed', onClosed);
44
+ };
45
+ function remove() {
46
+ if (removed)
47
+ return;
48
+ removed = true;
49
+ win.removeListener('closed', onClosed);
50
+ const count = counts.get(senderId);
51
+ // `undefined` → the entry was already cleared (e.g. by `closed` or by a
52
+ // prior sibling's decrement that hit zero). Nothing to do — never go
53
+ // negative, never resurrect trust.
54
+ if (count === undefined)
55
+ return;
56
+ if (count <= 1)
57
+ counts.delete(senderId);
58
+ else
59
+ counts.set(senderId, count - 1);
60
+ }
61
+ win.once('closed', onClosed);
62
+ return toDisposable(remove);
63
+ }
64
+ //# sourceMappingURL=app-services.js.map
@@ -0,0 +1,36 @@
1
+ import type { WindowService } from '../window-service.js';
2
+ import type { WorkspaceService } from '../workspace/workspace-service.js';
3
+ /** The slice of a window context that decides where a connection's commands go. */
4
+ export interface TargetContext {
5
+ windows: Pick<WindowService, 'mainWindow'>;
6
+ workspace: Pick<WorkspaceService, 'hasActiveSession'>;
7
+ }
8
+ /**
9
+ * The project window one automation connection acts on.
10
+ *
11
+ * The active context follows window focus, so resolving it afresh per message
12
+ * would let a click on another project's window redirect a script that is
13
+ * already driving one — the script does nothing wrong and its commands land
14
+ * somewhere else. A connection therefore PINS the first context it reaches
15
+ * that has a session and keeps it for the rest of its life. Which window a
16
+ * connection drives is decided by what it reached first, never by what the
17
+ * user is looking at.
18
+ *
19
+ * Nothing is pinned before a project window exists: a client may connect at
20
+ * boot and open a project afterwards, so until a context with a session shows
21
+ * up every use resolves the active one (the project list, which drives
22
+ * nothing). Pinning the list window would tie the connection to a window that
23
+ * can never run a command.
24
+ */
25
+ export interface ConnectionTarget<T extends TargetContext> {
26
+ /**
27
+ * The context to run a command against. Throws once the pinned window is
28
+ * gone, so a connection whose project was closed fails loudly instead of
29
+ * sliding onto whichever project happens to be open now.
30
+ */
31
+ resolve(): T;
32
+ /** The same target, but null instead of a throw once its window is gone. */
33
+ peek(): T | null;
34
+ }
35
+ export declare function createConnectionTarget<T extends TargetContext>(getCtx: () => T): ConnectionTarget<T>;
36
+ //# sourceMappingURL=connection-target.d.ts.map
@@ -0,0 +1,29 @@
1
+ export function createConnectionTarget(getCtx) {
2
+ let pinned = null;
3
+ // The window, not the session, is what a connection is pinned to: closing
4
+ // the project inside a window (`Tool.close`) leaves that window able to open
5
+ // another, while destroying the window ends the connection's target for good.
6
+ const isGone = (ctx) => {
7
+ const win = ctx.windows.mainWindow;
8
+ return !win || win.isDestroyed();
9
+ };
10
+ const current = () => {
11
+ if (pinned)
12
+ return isGone(pinned) ? null : pinned;
13
+ const ctx = getCtx();
14
+ if (ctx.workspace.hasActiveSession())
15
+ pinned = ctx;
16
+ return ctx;
17
+ };
18
+ return {
19
+ peek: current,
20
+ resolve() {
21
+ const ctx = current();
22
+ if (!ctx) {
23
+ throw new Error('The project window this connection was driving has been closed. Reconnect to drive another project.');
24
+ }
25
+ return ctx;
26
+ },
27
+ };
28
+ }
29
+ //# sourceMappingURL=connection-target.js.map
@@ -0,0 +1,28 @@
1
+ import type { WebContents } from 'electron';
2
+ import type { ConnectionRegistry } from '@dimina-kit/electron-deck/main';
3
+ import type { ConsoleForwarder } from '../console-forward/index.js';
4
+ import type { RpcEvent } from './shared.js';
5
+ /** The slice of a window context the two console taps read. */
6
+ export interface ConsoleTargetContext {
7
+ consoleForwarder?: ConsoleForwarder;
8
+ connections: ConnectionRegistry;
9
+ }
10
+ /**
11
+ * The `App.logAdded` stream of ONE automation connection, taken from the same
12
+ * window that connection's commands reach.
13
+ *
14
+ * Two taps, because the two architectures deliver guest console differently:
15
+ * - native-host: the render/service preloads post to main, bridge-router
16
+ * routes into the window's always-on ConsoleForwarder, and we subscribe.
17
+ * - default dimina-fe: the page/service console flows through the simulator
18
+ * guest's `ipc-message-host` channel, so we listen on that webContents.
19
+ * Both are re-pointed by `sync()`, which is what keeps the log stream and the
20
+ * command stream on the same window as the target and its simulator change.
21
+ */
22
+ export interface ConsoleBridge {
23
+ /** Re-point both taps at the connection's current target. */
24
+ sync(): void;
25
+ dispose(): void;
26
+ }
27
+ export declare function createConsoleBridge<T extends ConsoleTargetContext>(target: () => T | null, simulatorOf: (ctx: T) => WebContents | null, emit: (event: RpcEvent) => void): ConsoleBridge;
28
+ //# sourceMappingURL=console-bridge.d.ts.map
@@ -0,0 +1,91 @@
1
+ import { SimulatorChannel } from '../../../shared/ipc-channels.js';
2
+ /**
3
+ * How often an idle connection re-checks its target for a console to attach
4
+ * to. A connection that never sends a message (a script that only listens for
5
+ * logs) has no other moment to notice its window's simulator appearing or
6
+ * being replaced by a rebuild.
7
+ */
8
+ const SYNC_INTERVAL_MS = 1000;
9
+ export function createConsoleBridge(target, simulatorOf, emit) {
10
+ let subscribedForwarder = null;
11
+ let forwarderSub;
12
+ let attachedSim = null;
13
+ let simHandler = null;
14
+ // The destroyed-teardown is owned by the sim's connection rather than a
15
+ // bespoke `sim.once('destroyed')`, so it cannot accumulate across re-attaches.
16
+ let simDestroyedDisposer = null;
17
+ function logAdded(entry) {
18
+ const e = entry ?? {};
19
+ emit({ method: 'App.logAdded', params: { type: e.level || 'log', args: e.args || [] } });
20
+ }
21
+ // Keyed on the forwarder itself, not the context: a window is wired with its
22
+ // forwarder after the context exists, so a context-identity check would miss
23
+ // the forwarder arriving on a context we already looked at.
24
+ function syncForwarder(ctx) {
25
+ const forwarder = ctx?.consoleForwarder ?? null;
26
+ if (forwarder === subscribedForwarder)
27
+ return;
28
+ forwarderSub?.dispose();
29
+ subscribedForwarder = forwarder;
30
+ forwarderSub = forwarder?.subscribe((entry) => logAdded(entry));
31
+ }
32
+ function detachSim() {
33
+ if (attachedSim && simHandler) {
34
+ try {
35
+ if (!attachedSim.isDestroyed()) {
36
+ ;
37
+ attachedSim.removeListener('ipc-message-host', simHandler);
38
+ }
39
+ }
40
+ catch { /* noop */ }
41
+ }
42
+ try {
43
+ simDestroyedDisposer?.dispose();
44
+ }
45
+ catch { /* noop */ }
46
+ attachedSim = null;
47
+ simHandler = null;
48
+ simDestroyedDisposer = null;
49
+ }
50
+ function syncSim(ctx) {
51
+ const sim = ctx ? simulatorOf(ctx) : null;
52
+ const next = sim && !sim.isDestroyed() ? sim : null;
53
+ if (next === attachedSim)
54
+ return;
55
+ detachSim();
56
+ if (!next || !ctx)
57
+ return;
58
+ const onIpcMessageHost = (_event, channel, data) => {
59
+ if (channel !== SimulatorChannel.Console)
60
+ return;
61
+ logAdded(data);
62
+ };
63
+ attachedSim = next;
64
+ simHandler = onIpcMessageHost;
65
+ next.on('ipc-message-host', onIpcMessageHost);
66
+ simDestroyedDisposer = ctx.connections.acquire(next).own(() => {
67
+ // Dead webContents: drop the refs (removeListener on it would throw) and
68
+ // let the next sync attach to the replacement.
69
+ attachedSim = null;
70
+ simHandler = null;
71
+ simDestroyedDisposer = null;
72
+ });
73
+ }
74
+ function sync() {
75
+ const ctx = target();
76
+ syncForwarder(ctx);
77
+ syncSim(ctx);
78
+ }
79
+ const timer = setInterval(sync, SYNC_INTERVAL_MS);
80
+ return {
81
+ sync,
82
+ dispose() {
83
+ clearInterval(timer);
84
+ detachSim();
85
+ forwarderSub?.dispose();
86
+ forwarderSub = undefined;
87
+ subscribedForwarder = null;
88
+ },
89
+ };
90
+ }
91
+ //# sourceMappingURL=console-bridge.js.map
@@ -11,10 +11,10 @@ async function readRoute(ctx) {
11
11
  }
12
12
  /**
13
13
  * Native-host: derive the visible page's `{ pagePath, query }` from the active
14
- * render guest. The page-stack depth isn't surfaced by the bridge handle, so the
15
- * native page stack is reported as the single active page (see App.getPageStack).
16
- * Reads the guest's own `location.search` the render-host preload encodes
17
- * `pagePath` (+ per-page query) there.
14
+ * render guest. This is the source for `App.getCurrentPage` and the fallback for
15
+ * `App.getPageStack` before DeviceShell reports its full ordered stack through
16
+ * the bridge. The render-host preload encodes `pagePath` (+ per-page query) in
17
+ * the guest's own `location.search`.
18
18
  */
19
19
  async function readNativeActivePage(ctx) {
20
20
  const renderWc = getActivePageWc(ctx);