@dimina-kit/devtools 0.3.2-dev.20260525152421 → 0.3.2-dev.20260610082053

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 (295) hide show
  1. package/README.md +43 -40
  2. package/dist/main/api.d.ts +0 -1
  3. package/dist/main/api.js +5 -1
  4. package/dist/main/app/app.d.ts +15 -5
  5. package/dist/main/app/app.js +247 -110
  6. package/dist/main/app/bootstrap.d.ts +16 -0
  7. package/dist/main/app/bootstrap.js +20 -0
  8. package/dist/main/app/launch.d.ts +8 -4
  9. package/dist/main/app/launch.js +9 -4
  10. package/dist/main/index.bundle.js +6607 -2102
  11. package/dist/main/index.js +9 -1
  12. package/dist/main/ipc/app.d.ts +1 -1
  13. package/dist/main/ipc/bridge-router.d.ts +61 -0
  14. package/dist/main/ipc/bridge-router.js +1300 -0
  15. package/dist/main/ipc/panels.d.ts +1 -1
  16. package/dist/main/ipc/panels.js +6 -2
  17. package/dist/main/ipc/popover.d.ts +1 -1
  18. package/dist/main/ipc/project-fs.d.ts +57 -0
  19. package/dist/main/ipc/project-fs.js +448 -0
  20. package/dist/main/ipc/projects.d.ts +1 -1
  21. package/dist/main/ipc/session.d.ts +1 -1
  22. package/dist/main/ipc/settings.d.ts +1 -1
  23. package/dist/main/ipc/simulator-module.d.ts +6 -0
  24. package/dist/main/ipc/simulator-module.js +11 -1
  25. package/dist/main/ipc/simulator.d.ts +2 -2
  26. package/dist/main/ipc/simulator.js +49 -5
  27. package/dist/main/ipc/toolbar.d.ts +1 -1
  28. package/dist/main/ipc/views.d.ts +19 -0
  29. package/dist/main/ipc/views.js +65 -0
  30. package/dist/main/runtime/devtools-backend.d.ts +17 -0
  31. package/dist/main/runtime/devtools-backend.js +49 -0
  32. package/dist/main/runtime/miniapp-runtime.d.ts +39 -0
  33. package/dist/main/runtime/miniapp-runtime.js +17 -0
  34. package/dist/main/services/automation/exec.d.ts +27 -2
  35. package/dist/main/services/automation/exec.js +37 -3
  36. package/dist/main/services/automation/handlers/app.js +127 -11
  37. package/dist/main/services/automation/handlers/page.js +36 -19
  38. package/dist/main/services/automation/index.js +40 -13
  39. package/dist/main/services/automation/wait-active-page.d.ts +56 -0
  40. package/dist/main/services/automation/wait-active-page.js +71 -0
  41. package/dist/main/services/console-forward/index.d.ts +36 -0
  42. package/dist/main/services/console-forward/index.js +81 -0
  43. package/dist/main/services/dimina-resource-server.d.ts +7 -0
  44. package/dist/main/services/dimina-resource-server.js +99 -0
  45. package/dist/main/services/elements-forward/index.d.ts +66 -0
  46. package/dist/main/services/elements-forward/index.js +583 -0
  47. package/dist/main/services/layout/index.d.ts +23 -1
  48. package/dist/main/services/layout/index.js +28 -3
  49. package/dist/main/services/mcp/index.d.ts +1 -0
  50. package/dist/main/services/mcp/index.js +3 -0
  51. package/dist/main/services/mcp/server.d.ts +1 -1
  52. package/dist/main/services/mcp/server.js +1 -1
  53. package/dist/main/services/mcp/target-manager.d.ts +35 -0
  54. package/dist/main/services/mcp/target-manager.js +63 -1
  55. package/dist/main/services/mcp/tools/context-tools.js +15 -1
  56. package/dist/main/services/module.d.ts +1 -1
  57. package/dist/main/services/network-forward/index.d.ts +190 -0
  58. package/dist/main/services/network-forward/index.js +765 -0
  59. package/dist/main/services/notifications/renderer-notifier.d.ts +11 -0
  60. package/dist/main/services/notifications/renderer-notifier.js +7 -1
  61. package/dist/main/services/render-inspect/index.d.ts +41 -0
  62. package/dist/main/services/render-inspect/index.js +88 -0
  63. package/dist/main/services/safe-area/index.d.ts +17 -0
  64. package/dist/main/services/safe-area/index.js +59 -0
  65. package/dist/main/services/service-host-pool/pool.d.ts +197 -0
  66. package/dist/main/services/service-host-pool/pool.js +364 -0
  67. package/dist/main/services/simulator/custom-apis.d.ts +34 -0
  68. package/dist/main/services/simulator/custom-apis.js +22 -0
  69. package/dist/main/services/simulator-appdata/index.d.ts +35 -0
  70. package/dist/main/services/simulator-appdata/index.js +71 -0
  71. package/dist/main/services/simulator-current-page/index.d.ts +24 -0
  72. package/dist/main/services/simulator-current-page/index.js +19 -0
  73. package/dist/main/services/simulator-storage/index.d.ts +37 -2
  74. package/dist/main/services/simulator-storage/index.js +209 -32
  75. package/dist/main/services/simulator-storage/service-storage-ops.d.ts +40 -0
  76. package/dist/main/services/simulator-storage/service-storage-ops.js +67 -0
  77. package/dist/main/services/simulator-temp-files/fs-channels.js +1 -1
  78. package/dist/main/services/simulator-temp-files/index.d.ts +1 -1
  79. package/dist/main/services/simulator-temp-files/index.js +1 -1
  80. package/dist/main/services/simulator-temp-files/request-handler.js +1 -1
  81. package/dist/main/services/simulator-wxml/index.d.ts +29 -0
  82. package/dist/main/services/simulator-wxml/index.js +28 -0
  83. package/dist/main/services/update/update-manager.d.ts +10 -0
  84. package/dist/main/services/update/update-manager.js +16 -3
  85. package/dist/main/services/views/devtools-tabs.d.ts +47 -0
  86. package/dist/main/services/views/devtools-tabs.js +95 -0
  87. package/dist/main/services/views/view-manager.d.ts +142 -4
  88. package/dist/main/services/views/view-manager.js +909 -17
  89. package/dist/main/services/workbench-context.d.ts +66 -1
  90. package/dist/main/services/workbench-context.js +6 -1
  91. package/dist/main/services/workspace/workspace-service.d.ts +8 -0
  92. package/dist/main/services/workspace/workspace-service.js +12 -0
  93. package/dist/main/utils/ipc-registry.d.ts +11 -2
  94. package/dist/main/utils/ipc-registry.js +63 -3
  95. package/dist/main/utils/paths.d.ts +15 -0
  96. package/dist/main/utils/paths.js +19 -0
  97. package/dist/main/utils/sender-policy.d.ts +9 -3
  98. package/dist/main/utils/sender-policy.js +18 -4
  99. package/dist/main/utils/theme.d.ts +1 -1
  100. package/dist/main/utils/theme.js +1 -1
  101. package/dist/main/windows/main-window/create.d.ts +0 -6
  102. package/dist/main/windows/main-window/create.js +11 -59
  103. package/dist/main/windows/main-window/events.d.ts +1 -1
  104. package/dist/main/windows/main-window/events.js +1 -1
  105. package/dist/main/windows/service-host-window/create.d.ts +52 -0
  106. package/dist/main/windows/service-host-window/create.js +112 -0
  107. package/dist/native-host/common/common.js +368 -0
  108. package/dist/native-host/container/pageFrame.css +1 -0
  109. package/dist/native-host/render/render.js +9334 -0
  110. package/dist/native-host/service/service.js +6 -0
  111. package/dist/preload/index.d.ts +2 -0
  112. package/dist/preload/index.js +6 -0
  113. package/dist/preload/instrumentation/app-data.d.ts +18 -20
  114. package/dist/preload/instrumentation/app-data.js +23 -160
  115. package/dist/preload/instrumentation/wxml-extract.d.ts +1 -1
  116. package/dist/preload/instrumentation/wxml-extract.js +1 -1
  117. package/dist/preload/runtime/bridge.d.ts +3 -8
  118. package/dist/preload/runtime/bridge.js +2 -32
  119. package/dist/preload/runtime/custom-apis.js +20 -17
  120. package/dist/preload/runtime/host-toolbar-advertiser.d.ts +27 -0
  121. package/dist/preload/runtime/host-toolbar-advertiser.js +55 -0
  122. package/dist/preload/runtime/main-api-runner.d.ts +9 -0
  123. package/dist/preload/runtime/main-api-runner.js +85 -0
  124. package/dist/preload/runtime/native-host.d.ts +43 -0
  125. package/dist/preload/runtime/native-host.js +83 -0
  126. package/dist/preload/shared/expose.d.ts +10 -5
  127. package/dist/preload/shared/expose.js +29 -11
  128. package/dist/preload/shared/sid-registry.d.ts +11 -0
  129. package/dist/preload/shared/sid-registry.js +38 -0
  130. package/dist/preload/windows/host-toolbar.cjs +161 -0
  131. package/dist/preload/windows/host-toolbar.cjs.map +7 -0
  132. package/dist/preload/windows/host-toolbar.d.ts +2 -0
  133. package/dist/preload/windows/host-toolbar.js +12 -0
  134. package/dist/preload/windows/main.cjs +1939 -0
  135. package/dist/preload/windows/main.cjs.map +4 -4
  136. package/dist/preload/windows/simulator.cjs +925 -0
  137. package/dist/preload/windows/simulator.cjs.map +7 -0
  138. package/dist/preload/windows/simulator.js +243 -573
  139. package/dist/render-host/pageFrame.html +29 -0
  140. package/dist/render-host/preload.cjs +154 -0
  141. package/dist/render-host/render-inspect.js +295 -0
  142. package/dist/renderer/assets/abap-DiwvWnMr.js +2 -0
  143. package/dist/renderer/assets/apex-CmtZjKlf.js +2 -0
  144. package/dist/renderer/assets/azcli-DL2My_i-.js +2 -0
  145. package/dist/renderer/assets/bat-B-nC98wG.js +2 -0
  146. package/dist/renderer/assets/bicep-Ju5MwOgh.js +3 -0
  147. package/dist/renderer/assets/cameligo-8Eu1TyBr.js +2 -0
  148. package/dist/renderer/assets/chunk-DPI0Y_ll.js +1 -0
  149. package/dist/renderer/assets/clojure-u-RpMkH3.js +2 -0
  150. package/dist/renderer/assets/codicon-ngg6Pgfi.ttf +0 -0
  151. package/dist/renderer/assets/coffee-CdA7bbTe.js +2 -0
  152. package/dist/renderer/assets/constants-CLAQX6VZ.js +2 -0
  153. package/dist/renderer/assets/cpp-CzNFP8ks.js +2 -0
  154. package/dist/renderer/assets/csharp-j1LThmcE.js +2 -0
  155. package/dist/renderer/assets/csp-CLRC61y6.js +2 -0
  156. package/dist/renderer/assets/css-r6rC_7P2.js +4 -0
  157. package/dist/renderer/assets/css.worker-DyDu5ynT.js +90 -0
  158. package/dist/renderer/assets/cssMode-BCBgxrnA.js +2 -0
  159. package/dist/renderer/assets/cypher-CW08XVUh.js +2 -0
  160. package/dist/renderer/assets/dart-Cs9aL5T_.js +2 -0
  161. package/dist/renderer/assets/dockerfile-BWM0M184.js +2 -0
  162. package/dist/renderer/assets/ecl-MJJuer5P.js +2 -0
  163. package/dist/renderer/assets/editor-CWI9TnMM.css +1 -0
  164. package/dist/renderer/assets/editor.api2-BTaYmRq7.js +873 -0
  165. package/dist/renderer/assets/editor.worker-Dpt_xPrs.js +27 -0
  166. package/dist/renderer/assets/elixir-D2AIuXqn.js +2 -0
  167. package/dist/renderer/assets/flow9-B2H24giC.js +2 -0
  168. package/dist/renderer/assets/freemarker2-DGDGzimW.js +4 -0
  169. package/dist/renderer/assets/fsharp-CMk2OIJN.js +2 -0
  170. package/dist/renderer/assets/go-BrMkuJg0.js +2 -0
  171. package/dist/renderer/assets/graphql-PSR1UKGv.js +2 -0
  172. package/dist/renderer/assets/handlebars-bOHhw3EG.js +2 -0
  173. package/dist/renderer/assets/hcl-DAQrbDOW.js +2 -0
  174. package/dist/renderer/assets/html-BMyuX0_c.js +2 -0
  175. package/dist/renderer/assets/html.worker-IWNXhnIC.js +503 -0
  176. package/dist/renderer/assets/htmlMode-DR4kXJ7l.js +2 -0
  177. package/dist/renderer/assets/index-CcJKzatF.js +50 -0
  178. package/dist/renderer/assets/ini-0TG5BxW0.js +2 -0
  179. package/dist/renderer/assets/input-QGx6FFX1.js +2 -0
  180. package/dist/renderer/assets/ipc-transport-BT4QYQqu.css +1 -0
  181. package/dist/renderer/assets/{ipc-transport-BzPzxPwf.js → ipc-transport-DvZvwK-Z.js} +5 -5
  182. package/dist/renderer/assets/java-rgorz17v.js +2 -0
  183. package/dist/renderer/assets/javascript-Bu-tl1P7.js +2 -0
  184. package/dist/renderer/assets/json.worker-c4h5s80L.js +59 -0
  185. package/dist/renderer/assets/jsonMode-MQV_ffLR.js +8 -0
  186. package/dist/renderer/assets/julia-C8VMdHm8.js +2 -0
  187. package/dist/renderer/assets/kotlin-AhhC4Qhm.js +2 -0
  188. package/dist/renderer/assets/less-Cgca25AP.js +3 -0
  189. package/dist/renderer/assets/lexon-D0GHdBaw.js +2 -0
  190. package/dist/renderer/assets/liquid-ClkfOvhB.js +2 -0
  191. package/dist/renderer/assets/lspLanguageFeatures-Bke_4Ie9.js +5 -0
  192. package/dist/renderer/assets/lua-DmRsNG-P.js +2 -0
  193. package/dist/renderer/assets/m3-BgL5dNKT.js +2 -0
  194. package/dist/renderer/assets/markdown-BuJfycGS.js +2 -0
  195. package/dist/renderer/assets/mdx-RZdUskPu.js +2 -0
  196. package/dist/renderer/assets/mips-C9m_93PR.js +2 -0
  197. package/dist/renderer/assets/monaco.contribution-B7rUMaEv.js +3 -0
  198. package/dist/renderer/assets/msdax-CpFHC9OI.js +2 -0
  199. package/dist/renderer/assets/mysql-qFvltsqN.js +2 -0
  200. package/dist/renderer/assets/objective-c-Bnmr858J.js +2 -0
  201. package/dist/renderer/assets/pascal-WP0_D5AO.js +2 -0
  202. package/dist/renderer/assets/pascaligo-Blom4Rij.js +2 -0
  203. package/dist/renderer/assets/perl-B-vk8g64.js +2 -0
  204. package/dist/renderer/assets/pgsql-Cgvz6v67.js +2 -0
  205. package/dist/renderer/assets/php-8a3Lrw9m.js +2 -0
  206. package/dist/renderer/assets/pla-DuFqEZ8V.js +2 -0
  207. package/dist/renderer/assets/popover-BOH1If1J.js +2 -0
  208. package/dist/renderer/assets/postiats-DkLtSgkp.js +2 -0
  209. package/dist/renderer/assets/powerquery-BJ1aNepW.js +2 -0
  210. package/dist/renderer/assets/powershell-rE98k687.js +2 -0
  211. package/dist/renderer/assets/protobuf-CUheFacr.js +3 -0
  212. package/dist/renderer/assets/pug-LDcAMD8w.js +2 -0
  213. package/dist/renderer/assets/python-Cobrm_-8.js +2 -0
  214. package/dist/renderer/assets/qsharp-DUKSQoR1.js +2 -0
  215. package/dist/renderer/assets/r-D-QApv87.js +2 -0
  216. package/dist/renderer/assets/razor-BEUIQFNJ.js +2 -0
  217. package/dist/renderer/assets/redis-SXdDyWR9.js +2 -0
  218. package/dist/renderer/assets/redshift-Y6lsCryn.js +2 -0
  219. package/dist/renderer/assets/restructuredtext-edObr9a8.js +2 -0
  220. package/dist/renderer/assets/ruby-CNnUfF-8.js +2 -0
  221. package/dist/renderer/assets/rust-IHUZWzBr.js +2 -0
  222. package/dist/renderer/assets/sb-DrUvY44N.js +2 -0
  223. package/dist/renderer/assets/scala-B4hbXGLM.js +2 -0
  224. package/dist/renderer/assets/scheme-BGrd12j3.js +2 -0
  225. package/dist/renderer/assets/scss-x5G1ES4U.js +4 -0
  226. package/dist/renderer/assets/select-C7JQ8G5N.js +2 -0
  227. package/dist/renderer/assets/settings-DpAbTJyF.js +2 -0
  228. package/dist/renderer/assets/{settings-api-BXCw9vOZ.js → settings-api-RD2IZF53.js} +2 -2
  229. package/dist/renderer/assets/shell-DOehe2Y8.js +2 -0
  230. package/dist/renderer/assets/solidity-BeRvcwWV.js +2 -0
  231. package/dist/renderer/assets/sophia-DZbkUNjy.js +2 -0
  232. package/dist/renderer/assets/sparql-B7_oi5-h.js +2 -0
  233. package/dist/renderer/assets/sql-CTlsFWVE.js +2 -0
  234. package/dist/renderer/assets/st-DJVEJdPE.js +2 -0
  235. package/dist/renderer/assets/swift-CwhT3fYa.js +2 -0
  236. package/dist/renderer/assets/systemverilog-BQN63pkN.js +2 -0
  237. package/dist/renderer/assets/tcl-DqwfpskA.js +2 -0
  238. package/dist/renderer/assets/ts.worker-BwM5Ha3L.js +67720 -0
  239. package/dist/renderer/assets/tsMode-BHa7dl9v.js +12 -0
  240. package/dist/renderer/assets/twig-BiyenUgc.js +2 -0
  241. package/dist/renderer/assets/typescript-15aT0Yj8.js +2 -0
  242. package/dist/renderer/assets/typespec-CWOJribt.js +2 -0
  243. package/dist/renderer/assets/vb-Cq5F87m3.js +2 -0
  244. package/dist/renderer/assets/wgsl-BAvW2lVr.js +299 -0
  245. package/dist/renderer/assets/workbenchSettings-CNJX4Qna.js +8 -0
  246. package/dist/renderer/assets/workers-CRBS0W-v.js +2 -0
  247. package/dist/renderer/assets/xml-bR2Zh-HV.js +2 -0
  248. package/dist/renderer/assets/yaml-Bdk54Qrb.js +2 -0
  249. package/dist/renderer/entries/main/index.html +29 -6
  250. package/dist/renderer/entries/popover/index.html +7 -6
  251. package/dist/renderer/entries/settings/index.html +6 -5
  252. package/dist/renderer/entries/workbench-settings/index.html +6 -5
  253. package/dist/service-host/preload.cjs +254 -0
  254. package/dist/service-host/service.html +10 -0
  255. package/dist/service-host/sourcemap-rewrite.cjs +58 -0
  256. package/dist/service-host/sync-api-patch.js +20 -0
  257. package/dist/service-host/sync-impls/menu-button.js +16 -0
  258. package/dist/service-host/sync-impls/storage.js +50 -0
  259. package/dist/service-host/sync-impls/system-info.js +54 -0
  260. package/dist/shared/appdata-accumulator.d.ts +95 -0
  261. package/dist/shared/appdata-accumulator.js +213 -0
  262. package/dist/shared/bridge-channels.d.ts +364 -0
  263. package/dist/shared/bridge-channels.js +54 -0
  264. package/dist/shared/constants.d.ts +0 -4
  265. package/dist/shared/constants.js +0 -4
  266. package/dist/shared/ipc-channels.d.ts +108 -1
  267. package/dist/shared/ipc-channels.js +127 -10
  268. package/dist/shared/ipc-schemas.d.ts +65 -2
  269. package/dist/shared/ipc-schemas.js +79 -3
  270. package/dist/shared/open-in-editor.d.ts +71 -0
  271. package/dist/shared/open-in-editor.js +138 -0
  272. package/dist/shared/simulator-api-metadata.d.ts +21 -0
  273. package/dist/shared/simulator-api-metadata.js +23 -0
  274. package/dist/shared/types.d.ts +2 -2
  275. package/dist/shared/vpath.d.ts +42 -0
  276. package/dist/shared/vpath.js +105 -0
  277. package/dist/simulator/assets/device-shell-DsdcNEkt.css +1 -0
  278. package/dist/simulator/assets/device-shell-vwYSOVrh.js +2 -0
  279. package/dist/simulator/assets/jsx-runtime-P6ZegaOf.js +2 -0
  280. package/dist/simulator/assets/simulator-DddliUL5.js +10 -0
  281. package/dist/simulator/assets/simulator-mini-app-BDNu5n26.js +2 -0
  282. package/dist/simulator/device-shell/menu-button-geometry.js +8 -0
  283. package/dist/simulator/simulator.html +2 -112
  284. package/package.json +9 -9
  285. package/dist/main/utils/disposable.d.ts +0 -18
  286. package/dist/main/utils/disposable.js +0 -67
  287. package/dist/renderer/assets/constants-DI382mTP.js +0 -2
  288. package/dist/renderer/assets/index-4lkyF644.js +0 -46
  289. package/dist/renderer/assets/input-D4byFOLS.js +0 -2
  290. package/dist/renderer/assets/ipc-transport-DqbRdZNO.css +0 -1
  291. package/dist/renderer/assets/popover-cxKrg_os.js +0 -2
  292. package/dist/renderer/assets/select-BgSkkdHW.js +0 -2
  293. package/dist/renderer/assets/settings-oD2K1jdT.js +0 -2
  294. package/dist/renderer/assets/workbenchSettings-CTNBpTft.js +0 -8
  295. package/dist/simulator/assets/simulator-CVOFTpSj.js +0 -9
@@ -1,12 +1,33 @@
1
- import { SimulatorChannel, SimulatorCustomApiChannel } from '../../shared/ipc-channels.js';
2
- import { SimulatorAttachSchema, SimulatorCustomApiInvokeSchema, SimulatorResizeSchema, SimulatorSetVisibleSchema, } from '../../shared/ipc-schemas.js';
1
+ import { ServiceHostChannel, SimulatorChannel, SimulatorCustomApiChannel } from '../../shared/ipc-channels.js';
2
+ import { SimulatorAttachNativeSchema, SimulatorCustomApiInvokeSchema, SimulatorResizeSchema, SimulatorSetDeviceInfoSchema, SimulatorSetNativeBoundsSchema, SimulatorSetVisibleSchema, } from '../../shared/ipc-schemas.js';
3
3
  import { validate } from '../utils/ipc-schema.js';
4
4
  import { IpcRegistry } from '../utils/ipc-registry.js';
5
+ /**
6
+ * Map the renderer's logical device metrics onto the subset of a
7
+ * HostEnvSnapshot the service-host window's `getSystemInfoSync` consumes.
8
+ * `windowHeight` excludes the status bar (the page area); `windowWidth` has no
9
+ * horizontal chrome. Zoom is intentionally absent — it is a display scale, not
10
+ * a logical-size change.
11
+ */
12
+ function deviceInfoToHostEnv(d) {
13
+ return {
14
+ brand: d.brand,
15
+ model: d.model,
16
+ system: d.system,
17
+ platform: d.platform,
18
+ pixelRatio: d.pixelRatio,
19
+ screenWidth: d.screenWidth,
20
+ screenHeight: d.screenHeight,
21
+ windowWidth: d.screenWidth,
22
+ windowHeight: Math.max(0, d.screenHeight - d.statusBarHeight),
23
+ statusBarHeight: d.statusBarHeight,
24
+ };
25
+ }
5
26
  export function registerSimulatorIpc(ctx) {
6
27
  return new IpcRegistry(ctx.senderPolicy)
7
- .handle(SimulatorChannel.Attach, (_, ...args) => {
8
- const [simWcId, simWidth] = validate(SimulatorChannel.Attach, SimulatorAttachSchema, args);
9
- ctx.views.attachSimulator(simWcId, simWidth);
28
+ .handle(SimulatorChannel.AttachNative, (_, ...args) => {
29
+ const [simulatorUrl, simWidth] = validate(SimulatorChannel.AttachNative, SimulatorAttachNativeSchema, args);
30
+ ctx.views.attachNativeSimulator(simulatorUrl, simWidth);
10
31
  })
11
32
  .handle(SimulatorChannel.Detach, () => {
12
33
  ctx.views.detachSimulator();
@@ -14,6 +35,29 @@ export function registerSimulatorIpc(ctx) {
14
35
  .handle(SimulatorChannel.Resize, (_, ...args) => {
15
36
  const [simWidth] = validate(SimulatorChannel.Resize, SimulatorResizeSchema, args);
16
37
  ctx.views.resize(simWidth);
38
+ })
39
+ .handle(SimulatorChannel.SetNativeBounds, (_, ...args) => {
40
+ const [p] = validate(SimulatorChannel.SetNativeBounds, SimulatorSetNativeBoundsSchema, args);
41
+ ctx.views.setNativeSimulatorViewBounds(p);
42
+ })
43
+ .handle(SimulatorChannel.SetDeviceInfo, (_, ...args) => {
44
+ const [device] = validate(SimulatorChannel.SetDeviceInfo, SimulatorSetDeviceInfoSchema, args);
45
+ // Cache the selection (rides the next NATIVE_HOST_ENABLED reply for a
46
+ // race-free DeviceShell init) and push DEVICE_CHANGE to the live simulator
47
+ // WCV so the DeviceShell resizes the bezel + re-renders status bar / notch.
48
+ ctx.bridge?.setDevice(device);
49
+ // Re-push the CSS env(safe-area-inset-*) override to attached render-host
50
+ // guests so notch-aware page layout updates without a reload.
51
+ ctx.views.reapplySafeArea(device);
52
+ // Live-update the running service-host window's host-env snapshot so
53
+ // `wx.getSystemInfoSync()` reflects the selected device without a relaunch.
54
+ // The service-host preload mutates `__diminaSpawnContext` in place;
55
+ // `getSystemInfoSync` reads it fresh on each call. No service window yet
56
+ // (pre-spawn) → no-op.
57
+ const serviceWc = ctx.bridge?.getServiceWc();
58
+ if (serviceWc && !serviceWc.isDestroyed()) {
59
+ serviceWc.send(ServiceHostChannel.HostEnvUpdate, deviceInfoToHostEnv(device));
60
+ }
17
61
  })
18
62
  .handle(SimulatorChannel.SetVisible, (_, ...args) => {
19
63
  const [visible, simWidth] = validate(SimulatorChannel.SetVisible, SimulatorSetVisibleSchema, args);
@@ -1,4 +1,4 @@
1
1
  import type { WorkbenchContext } from '../services/workbench-context.js';
2
- import type { Disposable } from '../utils/disposable.js';
2
+ import type { Disposable } from '@dimina-kit/electron-deck/main';
3
3
  export declare function registerToolbarIpc(ctx: Pick<WorkbenchContext, 'toolbar' | 'senderPolicy'>): Disposable;
4
4
  //# sourceMappingURL=toolbar.d.ts.map
@@ -0,0 +1,19 @@
1
+ import type { WorkbenchContext } from '../services/workbench-context.js';
2
+ import type { Disposable } from '@dimina-kit/electron-deck/main';
3
+ /**
4
+ * Renderer-driven overlay bounds.
5
+ *
6
+ * The main window's React layout is the source of truth for where the
7
+ * editor view and the simulator's Chromium DevTools overlay live on
8
+ * screen. A `ResizeObserver` in the renderer watches each placeholder
9
+ * `<div>` and publishes its client rect (in CSS pixels, relative to the
10
+ * window's content area) through these channels. The view manager caches
11
+ * the latest payload per kind and applies it to the WebContentsView.
12
+ *
13
+ * Width / height = 0 is the canonical "overlay hidden" signal — the React
14
+ * panel is collapsed or the tab is not selected. The view manager removes
15
+ * the child view from the contentView but keeps the WebContents alive so
16
+ * subsequent re-shows skip the OpenSumi DI bootstrap.
17
+ */
18
+ export declare function registerViewsIpc(ctx: Pick<WorkbenchContext, 'views' | 'senderPolicy'>): Disposable;
19
+ //# sourceMappingURL=views.d.ts.map
@@ -0,0 +1,65 @@
1
+ import { ipcMain } from 'electron';
2
+ import { ViewChannel } from '../../shared/ipc-channels.js';
3
+ import { ViewBoundsSchema, HostToolbarAdvertiseHeightSchema, } from '../../shared/ipc-schemas.js';
4
+ import { validate } from '../utils/ipc-schema.js';
5
+ import { IpcRegistry } from '../utils/ipc-registry.js';
6
+ /**
7
+ * Renderer-driven overlay bounds.
8
+ *
9
+ * The main window's React layout is the source of truth for where the
10
+ * editor view and the simulator's Chromium DevTools overlay live on
11
+ * screen. A `ResizeObserver` in the renderer watches each placeholder
12
+ * `<div>` and publishes its client rect (in CSS pixels, relative to the
13
+ * window's content area) through these channels. The view manager caches
14
+ * the latest payload per kind and applies it to the WebContentsView.
15
+ *
16
+ * Width / height = 0 is the canonical "overlay hidden" signal — the React
17
+ * panel is collapsed or the tab is not selected. The view manager removes
18
+ * the child view from the contentView but keeps the WebContents alive so
19
+ * subsequent re-shows skip the OpenSumi DI bootstrap.
20
+ */
21
+ export function registerViewsIpc(ctx) {
22
+ const registry = new IpcRegistry(ctx.senderPolicy)
23
+ .handle(ViewChannel.SimulatorDevtoolsBounds, (_event, ...args) => {
24
+ const [bounds] = validate(ViewChannel.SimulatorDevtoolsBounds, ViewBoundsSchema, args);
25
+ ctx.views.setSimulatorDevtoolsBounds(bounds);
26
+ })
27
+ // Host-controllable toolbar: forward anchor (the MAIN renderer measures the
28
+ // placeholder rect → toolbar WCV bounds). invoke, mirroring
29
+ // SimulatorDevtoolsBounds. Sender is the trusted main renderer.
30
+ .handle(ViewChannel.HostToolbarBounds, (_event, ...args) => {
31
+ const [bounds] = validate(ViewChannel.HostToolbarBounds, ViewBoundsSchema, args);
32
+ ctx.views.setHostToolbarBounds(bounds);
33
+ });
34
+ // Reverse size-advertiser: the toolbar WCV's OWN renderer sends this, and the
35
+ // host loads ARBITRARY content into that WCV. We DELIBERATELY do NOT add the
36
+ // toolbar wc to the global sender policy — that would trust it for ALL ~72
37
+ // IpcRegistry channels (project-fs / session / settings / panels
38
+ // executeJavaScript / storage …), a large blast radius if the host content is
39
+ // ever compromised. Instead this is a RAW `ipcMain.on` gated on the EXACT
40
+ // current host-toolbar wc id — the same precise-sender-id trust model the
41
+ // simulator custom-api bridge uses (view-manager `attachNativeCustomApiBridge`).
42
+ // The host content can reach ONLY this one channel, carrying only a
43
+ // non-negative integer height.
44
+ const onAdvertiseHeight = (event, ...args) => {
45
+ if (event.sender.id !== ctx.views.getHostToolbarWebContentsId())
46
+ return;
47
+ let extent;
48
+ try {
49
+ ;
50
+ [{ extent }] = validate(ViewChannel.HostToolbarAdvertiseHeight, HostToolbarAdvertiseHeightSchema, args);
51
+ }
52
+ catch {
53
+ return; // malformed payload from the host's own content — drop it
54
+ }
55
+ ctx.views.setHostToolbarHeight(extent);
56
+ };
57
+ ipcMain.on(ViewChannel.HostToolbarAdvertiseHeight, onAdvertiseHeight);
58
+ return {
59
+ dispose() {
60
+ void registry.dispose();
61
+ ipcMain.removeListener(ViewChannel.HostToolbarAdvertiseHeight, onAdvertiseHeight);
62
+ },
63
+ };
64
+ }
65
+ //# sourceMappingURL=views.js.map
@@ -0,0 +1,17 @@
1
+ import type { RuntimeBackend } from '@dimina-kit/electron-deck';
2
+ import type { WorkbenchAppConfig } from '../../shared/types.js';
3
+ /**
4
+ * Adapts the devtools runtime into a {@link RuntimeBackend} the framework
5
+ * (`@dimina-kit/electron-deck`) orchestrates. The framework owns the process
6
+ * lifecycle gate (whenReady) + wire/trust primitives; the backend supplies the
7
+ * full devtools assembly — reusing the exact same `runDevtoolsBootstrap` / `createDevtoolsRuntime`
8
+ * assembly bodies (extracted from the former createWorkbenchApp), so the app is
9
+ * byte-identical to the pre-extraction behaviour (no behavioural re-creation).
10
+ *
11
+ * v2 scope: the backend owns window creation (the framework skips its own when a
12
+ * backend is present), so `assemble` ignores the framework `runtime` and builds
13
+ * the real devtools main window itself. Trust/frame unification and surfacing
14
+ * the runtime facade are deferred (see framework-extraction-v2.md §7).
15
+ */
16
+ export declare function createDevtoolsBackend(config?: WorkbenchAppConfig): RuntimeBackend;
17
+ //# sourceMappingURL=devtools-backend.d.ts.map
@@ -0,0 +1,49 @@
1
+ import { runDevtoolsBootstrap, createDevtoolsRuntime } from '../app/app.js';
2
+ import { registerAppLifecycle } from '../app/lifecycle.js';
3
+ /**
4
+ * Adapts the devtools runtime into a {@link RuntimeBackend} the framework
5
+ * (`@dimina-kit/electron-deck`) orchestrates. The framework owns the process
6
+ * lifecycle gate (whenReady) + wire/trust primitives; the backend supplies the
7
+ * full devtools assembly — reusing the exact same `runDevtoolsBootstrap` / `createDevtoolsRuntime`
8
+ * assembly bodies (extracted from the former createWorkbenchApp), so the app is
9
+ * byte-identical to the pre-extraction behaviour (no behavioural re-creation).
10
+ *
11
+ * v2 scope: the backend owns window creation (the framework skips its own when a
12
+ * backend is present), so `assemble` ignores the framework `runtime` and builds
13
+ * the real devtools main window itself. Trust/frame unification and surfacing
14
+ * the runtime facade are deferred (see framework-extraction-v2.md §7).
15
+ */
16
+ export function createDevtoolsBackend(config = {}) {
17
+ // Hoisted so `onShutdown` can reach the assembled context (assigned by `assemble`).
18
+ let instance = null;
19
+ return {
20
+ // The backend builds the devtools main window itself (framework skips its own).
21
+ // Its only construction-time needs are the host preload + `sandbox:false`
22
+ // (main-window/create.ts) and wrapping contentView in a `View` container.
23
+ // NOTE: `ownsWindows` is NOT structurally required — the framework's
24
+ // `mainWindowWebPreferences()` + `onMainWindowCreated()` hooks can supply both,
25
+ // and the project close→back lifecycle maps onto the Window facade's
26
+ // `onClose`/`newSession` (probe-validated). Retained for now; migrating to
27
+ // `runtime.windows.main` is the documented next step. (The `persist:simulator`
28
+ // partition is a fixed session used by child WCVs, not main-window state.)
29
+ ownsWindows: true,
30
+ // Pre-ready: app name / CDP port / CSP / privileged scheme — must run before
31
+ // the framework awaits app.whenReady().
32
+ beforeReady: () => {
33
+ runDevtoolsBootstrap(config);
34
+ },
35
+ // Setup (post-whenReady): app lifecycle (window-all-closed → quit) + the
36
+ // full devtools assembly.
37
+ assemble: async () => {
38
+ registerAppLifecycle();
39
+ instance = await createDevtoolsRuntime(config);
40
+ },
41
+ // Dispose the devtools context during the framework's deterministic shutdown
42
+ // (app.on('will-quit') → shutdown() → runShutdownCleanup(), awaited once).
43
+ // Without this, the compile session (a child process, torn down by
44
+ // closeProject) and the IPC registry would leak on exit. The framework awaits
45
+ // this hook, so teardown is no longer a best-effort before-quit reach-around.
46
+ onShutdown: () => instance?.dispose(),
47
+ };
48
+ }
49
+ //# sourceMappingURL=devtools-backend.js.map
@@ -0,0 +1,39 @@
1
+ /**
2
+ * MiniappRuntime — the stable, host-facing contract surface (foundation.md §3
3
+ * "MiniappRuntime 契约" layer, P4).
4
+ *
5
+ * `WorkbenchContext` is the rich internal main-process state: runtime kernel +
6
+ * host config + trust/infra plumbing, all mixed. Downstream hosts (qdmp, a
7
+ * re-skin of devtools that depends ONLY on devtools) don't consume that whole
8
+ * grab-bag — they drive the miniapp KERNEL: the view lifecycle (including the
9
+ * host-controllable toolbar at `views.hostToolbar`), the cross-wc bridge, the
10
+ * workspace, the custom-API registry, async storage, the appdata tap, the
11
+ * connection layer, and the main→renderer notifier.
12
+ *
13
+ * This module NAMES that kernel as an explicit, documented contract so the
14
+ * surface a host depends on is compiler-enforced and versionable, instead of
15
+ * "whatever fields of WorkbenchContext you happen to reach for". Config inputs
16
+ * (appName / panels / apiNamespaces / branding / project templates) are the
17
+ * host's INPUT, not part of the runtime it consumes; trust/infra
18
+ * (senderPolicy / trustedWindowSenderIds / registry / toolbar action-table)
19
+ * stay internal.
20
+ *
21
+ * Derived via `Pick<WorkbenchContext, …>` so the field TYPES never drift from
22
+ * the real implementation — `MiniappRuntime` is a view onto `WorkbenchContext`,
23
+ * and the conformance assertion below fails to compile the moment the context
24
+ * stops satisfying the contract.
25
+ *
26
+ * The host-controllable toolbar WebContentsView hangs off `views.hostToolbar`
27
+ * (a ViewManager concern), so it's reachable through the contract WITHOUT a 9th
28
+ * top-level member — `runtime.views.hostToolbar.loadURL(...)`.
29
+ */
30
+ import type { WorkbenchContext } from '../services/workbench-context.js';
31
+ /** The stable miniapp-kernel surface a downstream host (qdmp) consumes. */
32
+ export type MiniappRuntime = Pick<WorkbenchContext, 'views' | 'bridge' | 'workspace' | 'simulatorApis' | 'storageApi' | 'appData' | 'connections' | 'notify'>;
33
+ /**
34
+ * Project a full `WorkbenchContext` down to its `MiniappRuntime` view. Hosts
35
+ * call this to get a value typed to exactly the contract — no wider access to
36
+ * the context's internals leaks through.
37
+ */
38
+ export declare function asMiniappRuntime(ctx: WorkbenchContext): MiniappRuntime;
39
+ //# sourceMappingURL=miniapp-runtime.d.ts.map
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Project a full `WorkbenchContext` down to its `MiniappRuntime` view. Hosts
3
+ * call this to get a value typed to exactly the contract — no wider access to
4
+ * the context's internals leaks through.
5
+ */
6
+ export function asMiniappRuntime(ctx) {
7
+ return ctx;
8
+ }
9
+ /**
10
+ * Compile-time conformance guard: if `WorkbenchContext` ever stops structurally
11
+ * satisfying `MiniappRuntime` (a kernel field renamed / its type narrowed),
12
+ * THIS line fails `tsc`, catching the contract break at build time. The runtime
13
+ * value is unused.
14
+ */
15
+ const _conformance = (ctx) => ctx;
16
+ void _conformance;
17
+ //# sourceMappingURL=miniapp-runtime.js.map
@@ -1,10 +1,35 @@
1
+ import type { WebContents } from 'electron';
1
2
  import type { WorkbenchContext } from '../workbench-context.js';
2
3
  import type { ElementRef } from './shared.js';
3
4
  export declare function getSimulator(ctx: WorkbenchContext): Electron.WebContents | null;
4
5
  export declare function evalInSim<T = unknown>(ctx: WorkbenchContext, expression: string): Promise<T>;
5
- /** Build JS that locates an element by selector+index inside the active page iframe. */
6
+ /**
7
+ * Native-host: the active page's DOM lives directly in the visible render-host
8
+ * <webview> guest (no iframe), so element/page automation must run in that
9
+ * WebContents' main world. Returns null on the default dimina-fe arch, where the
10
+ * page lives inside an iframe of the simulator guest and automation goes through
11
+ * `evalInSim` + `inIframe` instead.
12
+ */
13
+ export declare function getActivePageWc(ctx: WorkbenchContext): WebContents | null;
14
+ /**
15
+ * Wrap guest code so `_doc` is the page document. Under native-host the render
16
+ * guest IS the page document — no iframe lookup (cf. `inIframe`, which digs into
17
+ * the simulator guest's last iframe). Mirrors `inIframe`'s `_doc` contract so the
18
+ * same handler `body` strings work in both arches unchanged.
19
+ */
20
+ export declare function wrapGuest(code: string): string;
21
+ /**
22
+ * Run page-level automation code against the right document for the current arch:
23
+ * - native-host with a live active render guest → run in that guest's main
24
+ * world via `wrapGuest` (the guest IS the page document).
25
+ * - otherwise (default dimina-fe, or native-host with no active guest yet) →
26
+ * fall back to `evalInSim(inIframe(code))` (the existing simulator-iframe path).
27
+ * The `code` is the same body either arch expects — it reads `_doc`.
28
+ */
29
+ export declare function evalInActivePage<T = unknown>(ctx: WorkbenchContext, code: string): Promise<T>;
30
+ /** Build JS that locates an element by selector+index inside the active page document. */
6
31
  export declare function buildElAccess(ref: ElementRef, varName?: string): string;
7
- /** Run `body` inside the active page iframe with `varName` bound to the element at `ref`. */
32
+ /** Run `body` inside the active page document with `varName` bound to the element at `ref`. */
8
33
  export declare function evalInElement<T = unknown>(ctx: WorkbenchContext, ref: ElementRef, body: string, varName?: string): Promise<T>;
9
34
  /** Wrap code to run inside the active page iframe (last iframe in the stack). */
10
35
  export declare function inIframe(code: string): string;
@@ -10,13 +10,47 @@ export async function evalInSim(ctx, expression) {
10
10
  throw new Error('Simulator not connected');
11
11
  return sim.executeJavaScript(expression);
12
12
  }
13
- /** Build JS that locates an element by selector+index inside the active page iframe. */
13
+ /**
14
+ * Native-host: the active page's DOM lives directly in the visible render-host
15
+ * <webview> guest (no iframe), so element/page automation must run in that
16
+ * WebContents' main world. Returns null on the default dimina-fe arch, where the
17
+ * page lives inside an iframe of the simulator guest and automation goes through
18
+ * `evalInSim` + `inIframe` instead.
19
+ */
20
+ export function getActivePageWc(ctx) {
21
+ return ctx.bridge?.isNativeHost() ? ctx.bridge.getActiveRenderWc() : null;
22
+ }
23
+ /**
24
+ * Wrap guest code so `_doc` is the page document. Under native-host the render
25
+ * guest IS the page document — no iframe lookup (cf. `inIframe`, which digs into
26
+ * the simulator guest's last iframe). Mirrors `inIframe`'s `_doc` contract so the
27
+ * same handler `body` strings work in both arches unchanged.
28
+ */
29
+ export function wrapGuest(code) {
30
+ return `(() => { const _doc = document; return (function(){ ${code} })() })()`;
31
+ }
32
+ /**
33
+ * Run page-level automation code against the right document for the current arch:
34
+ * - native-host with a live active render guest → run in that guest's main
35
+ * world via `wrapGuest` (the guest IS the page document).
36
+ * - otherwise (default dimina-fe, or native-host with no active guest yet) →
37
+ * fall back to `evalInSim(inIframe(code))` (the existing simulator-iframe path).
38
+ * The `code` is the same body either arch expects — it reads `_doc`.
39
+ */
40
+ export async function evalInActivePage(ctx, code) {
41
+ const renderWc = getActivePageWc(ctx);
42
+ if (renderWc) {
43
+ return renderWc.executeJavaScript(wrapGuest(code));
44
+ }
45
+ return evalInSim(ctx, inIframe(code));
46
+ }
47
+ /** Build JS that locates an element by selector+index inside the active page document. */
14
48
  export function buildElAccess(ref, varName = 'el') {
15
49
  return `const ${varName} = _doc.querySelectorAll(${JSON.stringify(ref.selector)})[${ref.index}]`;
16
50
  }
17
- /** Run `body` inside the active page iframe with `varName` bound to the element at `ref`. */
51
+ /** Run `body` inside the active page document with `varName` bound to the element at `ref`. */
18
52
  export function evalInElement(ctx, ref, body, varName = 'el') {
19
- return evalInSim(ctx, inIframe(`${buildElAccess(ref, varName)}\n${body}`));
53
+ return evalInActivePage(ctx, `${buildElAccess(ref, varName)}\n${body}`);
20
54
  }
21
55
  /** Wrap code to run inside the active page iframe (last iframe in the stack). */
22
56
  export function inIframe(code) {
@@ -1,12 +1,41 @@
1
- import { evalInSim, getSimulator, inIframe } from '../exec.js';
2
- import { parseLocationRoute } from '../../../../shared/simulator-route.js';
1
+ import { evalInSim, getActivePageWc, getSimulator } from '../exec.js';
2
+ import { decodePageSpec, parseLocationRoute } from '../../../../shared/simulator-route.js';
3
+ import { waitForActivePage } from '../wait-active-page.js';
3
4
  export const appHandlers = {};
5
+ /** Nav methods that take a `{ url }` arg (navigateBack is handled separately). */
6
+ const NAV_METHODS = new Set(['navigateTo', 'redirectTo', 'reLaunch', 'switchTab']);
4
7
  // -- App domain --
5
8
  async function readRoute(ctx) {
6
9
  const { search } = await evalInSim(ctx, `({ search: location.search })`);
7
10
  return parseLocationRoute(search);
8
11
  }
12
+ /**
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.
18
+ */
19
+ async function readNativeActivePage(ctx) {
20
+ const renderWc = getActivePageWc(ctx);
21
+ if (!renderWc)
22
+ return null;
23
+ const search = await renderWc
24
+ .executeJavaScript('location.search')
25
+ .catch(() => '');
26
+ const params = new URLSearchParams(search.startsWith('?') ? search.slice(1) : search);
27
+ const pagePath = params.get('pagePath') || '';
28
+ if (!pagePath)
29
+ return null;
30
+ // The guest URL carries the full page spec on `pagePath` (may include `?k=v`).
31
+ const spec = decodePageSpec(pagePath);
32
+ return { pagePath: spec.pagePath, query: spec.query };
33
+ }
9
34
  appHandlers['App.getCurrentPage'] = async (ctx) => {
35
+ if (ctx.bridge?.isNativeHost()) {
36
+ const page = await readNativeActivePage(ctx);
37
+ return { pageId: 1, path: page?.pagePath ?? '', query: page?.query ?? {} };
38
+ }
10
39
  const route = await readRoute(ctx);
11
40
  const iframeCount = await evalInSim(ctx, `document.querySelectorAll('iframe').length`);
12
41
  return {
@@ -16,8 +45,28 @@ appHandlers['App.getCurrentPage'] = async (ctx) => {
16
45
  };
17
46
  };
18
47
  appHandlers['App.getPageStack'] = async (ctx) => {
19
- const route = await readRoute(ctx);
20
48
  const pageStack = [];
49
+ if (ctx.bridge?.isNativeHost()) {
50
+ // DeviceShell reports its full ordered stack (bottom→top) via PAGE_STACK;
51
+ // the bridge stores it. Report that. Before the first signal (or in a mock
52
+ // without the accessor) fall back to the single visible page.
53
+ const stack = ctx.bridge.getPageStack?.();
54
+ if (stack && stack.length > 0) {
55
+ stack.forEach((entry, i) => {
56
+ pageStack.push({
57
+ pageId: i + 1,
58
+ path: entry.pagePath,
59
+ query: Object.fromEntries(Object.entries(entry.query ?? {}).map(([k, v]) => [k, String(v)])),
60
+ });
61
+ });
62
+ return { pageStack };
63
+ }
64
+ const page = await readNativeActivePage(ctx);
65
+ if (page)
66
+ pageStack.push({ pageId: 1, path: page.pagePath, query: page.query });
67
+ return { pageStack };
68
+ }
69
+ const route = await readRoute(ctx);
21
70
  if (!route)
22
71
  return { pageStack };
23
72
  // Mirrors upstream HashRouter.parseSearch: `[entry]` or `[entry, current]`.
@@ -30,24 +79,88 @@ appHandlers['App.getPageStack'] = async (ctx) => {
30
79
  appHandlers['App.callWxMethod'] = async (ctx, params) => {
31
80
  const method = params.method;
32
81
  const args = params.args || [];
82
+ // NATIVE-HOST: the authoritative `wx.*` runs in the hidden service-host
83
+ // window (the simulator / render-guest context has no `wx`). Run EVERY method
84
+ // there — nav so the page stack goes through the real runtime path, and
85
+ // non-nav (setNavigationBarTitle / getSystemInfoSync / tabBar APIs / …) so
86
+ // they don't fall through to the default-arch eval-in-simulator path below,
87
+ // which throws "wx is not defined" under native-host.
88
+ if (ctx.bridge?.isNativeHost()) {
89
+ const serviceWc = ctx.bridge.getServiceWc();
90
+ if (!serviceWc)
91
+ throw new Error('Service host not connected');
92
+ if (NAV_METHODS.has(method) || method === 'navigateBack') {
93
+ const arg = method === 'navigateBack'
94
+ ? (args[0] ?? { delta: 1 })
95
+ : (args[0] ?? {});
96
+ // Capture the active page BEFORE navigating so we can wait for it to
97
+ // actually change, instead of blindly sleeping a fixed duration.
98
+ const since = ctx.bridge.getActiveBridgeId();
99
+ await serviceWc.executeJavaScript(`wx.${method}(${JSON.stringify(arg)})`);
100
+ // Wait for the bridge's `activePage` signal (the new page mounted), with a
101
+ // timeout floor = the old blind-wait duration. Resolves as soon as the new
102
+ // page is active — far faster than the fixed sleep for the common case —
103
+ // and still bounded for edges (e.g. switchTab back to an already-mounted
104
+ // tab keeps the same bridgeId, so it falls through to the floor, same as
105
+ // the old behaviour). See wait-active-page.ts.
106
+ const timeoutMs = method === 'navigateBack' ? 1500 : 2000;
107
+ await waitForActivePage(ctx.bridge, {
108
+ since,
109
+ timeoutMs,
110
+ onTimeout: () => console.warn(`[automation] ${method}: activePage signal not seen within ${timeoutMs}ms — proceeding on timeout floor`),
111
+ });
112
+ return { result: undefined };
113
+ }
114
+ // Non-nav: invoke on the service-host wx and return its (sync) value. Async
115
+ // methods that report via a success callback return undefined here, same as
116
+ // the default-arch generic path below.
117
+ const argsStr = args.map((a) => JSON.stringify(a)).join(', ');
118
+ const result = await serviceWc.executeJavaScript(`
119
+ new Promise((resolve, reject) => {
120
+ try {
121
+ if (typeof wx === 'undefined' || typeof wx[${JSON.stringify(method)}] !== 'function') {
122
+ reject(new Error('wx.' + ${JSON.stringify(method)} + ' is not a function in service host'))
123
+ return
124
+ }
125
+ resolve(wx.${method}(${argsStr}))
126
+ } catch (e) { reject(e && e.message ? e.message : String(e)) }
127
+ })
128
+ `);
129
+ return { result };
130
+ }
33
131
  // For navigation methods, handle specially via DOM click / wx.* on the iframe
34
132
  if (['navigateTo', 'redirectTo', 'reLaunch', 'switchTab'].includes(method)) {
35
133
  const opts = args[0];
36
134
  const url = opts?.url;
37
135
  if (url) {
38
136
  const cleanUrl = url.startsWith('/') ? url : `/${url}`;
39
- // Try clicking a matching DOM element first
40
- const clicked = await evalInSim(ctx, inIframe(`
137
+ // Try clicking a matching DOM element first. We can't use `inIframe`
138
+ // here tabBar mini-apps keep prior tabs' iframes around (display:none)
139
+ // and `iframes[length-1]` ends up being the hidden, freshly-cached tab
140
+ // rather than the visible one. Pick the visible iframe explicitly.
141
+ const clicked = await evalInSim(ctx, `(() => {
142
+ const iframes = Array.from(document.querySelectorAll('iframe'))
143
+ const visible = iframes.filter((f) => {
144
+ const cs = window.getComputedStyle(f)
145
+ if (cs.display === 'none' || cs.visibility === 'hidden') return false
146
+ const rect = f.getBoundingClientRect()
147
+ return rect.width > 0 && rect.height > 0
148
+ })
149
+ const target = visible[visible.length - 1] || iframes[iframes.length - 1]
150
+ if (!target || !target.contentDocument) return false
41
151
  const path = ${JSON.stringify(cleanUrl)}
42
- const el = Array.from(_doc.querySelectorAll('[data-path]'))
43
- .find(e => e.getAttribute('data-path') === path)
152
+ const el = Array.from(target.contentDocument.querySelectorAll('[data-path]'))
153
+ .find((e) => e.getAttribute('data-path') === path)
44
154
  if (el) { el.click(); return true }
45
155
  return false
46
- `)).catch(() => false);
156
+ })()`).catch(() => false);
47
157
  if (!clicked) {
48
- // Fallback: drive navigation via wx.* on the rendered page iframe.
49
- // Upstream's query router doesn't react to URL mutation; the active
50
- // page's iframe exposes wx.* with the live dimina runtime bindings.
158
+ // Fallback cascade for navigation methods:
159
+ // 1. iframe wx[method] page-iframe surface (older arch, where
160
+ // jdimina installed `window.wx` on each page frame).
161
+ // 2. top-window wx[method] — set up by simulator/main.tsx, binds
162
+ // MiniApp.switchTab / navigateTo / etc. Required for switchTab
163
+ // since page-iframe wx (jdimina) doesn't expose it.
51
164
  const apiName = JSON.stringify(method);
52
165
  const urlJson = JSON.stringify(cleanUrl);
53
166
  await evalInSim(ctx, `(() => {
@@ -60,6 +173,9 @@ appHandlers['App.callWxMethod'] = async (ctx, params) => {
60
173
  }
61
174
  } catch (_) {}
62
175
  }
176
+ if (typeof wx !== 'undefined' && wx && typeof wx[${apiName}] === 'function') {
177
+ wx[${apiName}]({ url: ${urlJson} })
178
+ }
63
179
  })()`);
64
180
  }
65
181
  // Wait for navigation