@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
package/README.md CHANGED
@@ -1,766 +1,89 @@
1
- # Dimina DevTools
1
+ # @dimina-kit/devtools
2
2
 
3
- 基于 Electron 的小程序开发者工具。提供模拟器、Chrome DevTools 面板、WXML/AppData/Storage/编译 面板、编译配置等功能。
3
+ Dimina DevTools 是调试 [Dimina](https://github.com/didi/dimina) 小程序的桌面工具。它把小程序模拟器、Chrome DevToolsWXMLAppDataStorage、编译日志和 VS Code 工作台放在同一个 Electron 应用中。
4
4
 
5
- 下游 host 通过 `launch(config)` 集成并定制 devtools(零配置直接 `launch()`,配置驱动 `launch({...})`;见下方「两种用法」)。两种用法都经领域中立的 [`@dimina-kit/electron-deck`](../electron-deck) 框架编排——框架接管 Electron 进程生命周期(whenReady / will-quit)、wire/trust 原语,devtools 作为 `RuntimeBackend` 注入完整运行时(见 [`electron-deck 架构`](../electron-deck/docs/architecture.md))。
5
+ ![Dimina DevTools](../../docs/devtools.png)
6
6
 
7
- ---
7
+ ## 获取和运行
8
8
 
9
- ## 两种用法
9
+ 直接使用桌面应用,可以从项目的 [Releases](https://github.com/EchoTechFE/dimina-kit/releases) 下载已发布的桌面构建。要从源码运行,请先按[仓库根 README](../../README.md#快速开始)准备仓库,然后执行:
10
10
 
11
- 单一入口 `launch(config?)`——不传配置即零配置直接运行,传 `WorkbenchAppConfig` 即配置驱动定制。
11
+ ```bash
12
+ pnpm --filter @dimina-kit/devtools build
13
+ pnpm --filter @dimina-kit/devtools start
14
+ ```
12
15
 
13
- | 用法 | 调用 | 控制力 | 适合场景 |
14
- | ------------ | ---------------------- | --------------------- | -------------- |
15
- | **零配置** | `launch()` | 无 | 直接运行 |
16
- | **配置驱动** | `launch({...})` | 配置 Provider + 扩展点 | 品牌化、深度定制 |
16
+ ## 作为 Electron 库使用
17
17
 
18
- ### 零配置
18
+ `@dimina-kit/devtools` 也可以作为 Electron 应用的基础包,用于替换品牌、编译后端、项目来源,或注册宿主自己的小程序 API 和模拟器界面。
19
19
 
20
- ```typescript
21
- import { launch } from '@dimina-kit/devtools/launch'
22
- launch()
20
+ ```bash
21
+ pnpm add @dimina-kit/devtools electron@^43.2.0
23
22
  ```
24
23
 
25
- ### 配置驱动
24
+ 当前包声明的 Electron peer 版本为 `^43.2.0`。
26
25
 
27
- 扩展点分两类:**配置 Provider**(构造期、一对一,替换某个内置能力)走 `launch` 配置字段;**Contribution**(`onSetup` 期、一对多,添加多个同类条目)走 `onSetup(instance)` 上的 typed 方法。所有 Contribution 都 per-context,随 context 自动销毁。
26
+ 最小入口是 `launch(config?)`:
28
27
 
29
- ```typescript
30
- import { suppressEpipe } from '@dimina-kit/devtools/bootstrap'
28
+ ```ts
31
29
  import { launch } from '@dimina-kit/devtools'
32
- import { rendererDir } from '@dimina-kit/devtools/paths'
33
- import { Menu } from 'electron'
34
30
 
35
- suppressEpipe()
36
-
37
- launch({
38
- // ── 配置 Provider(构造期,一对一)──
39
- appName: '我的开发工具',
40
- adapter: myAdapter,
41
- preloadPath: '/path/to/my-preload.js',
42
- rendererDir,
43
- apiNamespaces: ['my'],
44
- brandingProvider: () => ({ appName: '我的开发工具' }),
45
- icon: '/path/to/icon.png',
46
- menuBuilder: (mainWindow, menuCtx) => {
47
- // menuCtx 是 MenuContext(只读 menu 相关状态)
48
- Menu.setApplicationMenu(/* ... */)
49
- },
50
-
51
- // ── Contribution(onSetup 期,一对多)──
52
- onSetup: (instance) => {
53
- // simulator 自定义 API:per-context
54
- instance.registerSimulatorApi('login', (params) => myLogin(params))
55
-
56
- // simulator 下游 UI:框架管理 DeviceShell 挂载点与生命周期
57
- const nativeUi = instance.registerSimulatorUiExtension({
58
- id: 'my.native-ui',
59
- rendererScriptPath: '/absolute/path/to/native-ui.js',
60
- })
61
- instance.registerSimulatorApi('share', params => nativeUi.invoke('share', params))
62
-
63
- // 自定义 IPC:经 gated 的 IpcRegistry,不再裸 ipcMain.handle
64
- instance.ipc.handle('my:action', () => collectStats())
65
-
66
- // host 自己的弹窗须注册为受信 sender 后才能调 instance.ipc
67
- // const win = createDialogWindow(/* ... */)
68
- // instance.registerTrustedWindow(win)
69
- },
70
- onBeforeClose: ({ context }) => {
71
- // 窗口关闭前的自定义清理;session 关闭和 view 销毁由框架自动处理
72
- },
31
+ launch().catch((err) => {
32
+ console.error(err)
33
+ process.exit(1)
73
34
  })
74
35
  ```
75
36
 
76
- > Contribution 注册物全部自动进 `context.registry`,host 无需手写 cleanup。devtools 内部仍用 `WorkbenchModule` 组织内置 IPC 模块,但这是实现细节,不再作为对外接入方式导出。
77
-
78
- ---
79
-
80
- ## 目录结构
81
-
82
- ```
83
- src/
84
- shared/
85
- types.ts # 跨层共享类型
86
- constants.ts # 跨层共享常量
87
- ipc-channels.ts # IPC 频道名称常量
88
- main/
89
- api.ts # 公共 API 聚合入口(package main)
90
- app/
91
- launch.ts # 零配置入口
92
- app.ts # createDevtoolsRuntime 装配体 + runDevtoolsBootstrap
93
- bootstrap.ts # suppressEpipe / setupCdpPort
94
- lifecycle.ts # Electron app 生命周期注册
95
- ipc/ # 只做 handler 注册,不写业务
96
- index.ts # 聚合 register 函数导出
97
- simulator.ts # simulator attach/detach/设备信息/custom-api invoke
98
- popover.ts # 编译配置弹窗
99
- settings.ts # 项目设置 + workbench 设置
100
- projects.ts # 项目列表管理
101
- session.ts # 编译会话 open/close/status
102
- services/ # 按业务领域分,真正的业务能力
103
- settings/ # 用户级 workbench 设置读写 + 主题应用
104
- projects/ # 项目列表 CRUD(持久化)
105
- layout/ # 布局常量 + bounds 计算
106
- views/ # ViewManager(settings / popover overlay 等)
107
- workspace/ # 项目会话生命周期
108
- notifications/ # 主进程 → renderer 的事件通知
109
- mcp/ # MCP server
110
- automation/ # 自动化 WebSocket 服务
111
- simulator/ # simulator dir / referer
112
- simulator-storage/ # CDP DOMStorage + 元素 inspect 桥接
113
- update/ # UpdateManager + GitHubReleaseChecker
114
- workbench-context.ts # 共享状态类型 + 工厂 + 辅助函数
115
- window-service.ts # main / settings 窗口聚合访问
116
- module.ts # WorkbenchModule 协议
117
- default-adapter.ts # 默认 CompilationAdapter
118
- menu/ # 应用菜单
119
- utils/
120
- paths.ts # rendererDir / defaultPreloadPath / simulatorDir / cjsSiblingPreloadPath
121
- ipc-registry.ts # ipcMain.handle 包装 + SenderPolicy 闸门
122
- sender-policy.ts # sender webContents id 白名单
123
- ipc-schema.ts # zod schema 校验工具
124
- windows/
125
- main-window/ # 主窗口创建 + 事件接线
126
- settings-window/ # 独立设置窗口
127
- navigation-hardening.ts # will-navigate / setWindowOpenHandler 限流
128
-
129
- preload/ # 按注入目标分
130
- windows/ # contextBridge 类型 preload
131
- main.ts # workbench 三窗口 preload,仅暴露 window.devtools.ipc
132
- simulator.ts # simulator(WCV)frame preload(native-host 跑其 .cjs sibling)
133
- shared/ # preload 之间共享
134
- api-compat.ts # setupApiCompatHook
135
- constants.ts
136
- types.ts
137
- instrumentation/ # 注入到小程序 runtime 的探针
138
- console.ts # 控制台拦截
139
- app-data.ts # Worker setData 拦截 → createAppDataSource
140
- wxml.ts # Vue 组件树提取 → createWxmlSource
141
- miniapp-snapshot/ # 面板数据统一快照框架(docs/miniapp-snapshot.md)
142
- types.ts # MiniappSnapshotSource / SnapshotEnvelope
143
- host.ts # createMiniappSnapshotHost → push/pull/__miniappSnapshot
144
- runtime/
145
- bridge.ts # installSimulatorBridge → window.__simulatorData
146
- custom-apis.ts # installCustomApisBridge → window.__diminaCustomApis
147
- host.ts # sendToHost 推送封装
148
-
149
- renderer/
150
- entries/ # 各窗口 HTML + React 挂载点
151
- main/ popover/ settings/ workbench-settings/
152
- modules/ # 先按窗口分,再按区域
153
- main/
154
- main.tsx # 主窗口 React 根
155
- features/ # 主窗口内的业务区域
156
- project-runtime/ # 项目视图 + 工具栏 + 右侧面板切换
157
- right-panel/ # WXML / AppData / Storage / 编译 面板
158
- popover/ settings/ workbench-settings/
159
- shared/
160
- components/ # UI 组件(ui / layout / json-viewer / ...)
161
- lib/ # ipc / simulator-url / utils / ...
162
-
163
- simulator/ # 小程序容器运行时(独立 webview 入口)
164
- simulator.html
165
- main.tsx
166
- simulator-api.ts
167
- simulator-api-storage.ts
168
- simulator-api-device.ts
169
- simulator-api-media.ts
170
- simulator-api-fs.ts
171
- ```
172
-
173
- 渲染层使用 **React + shadcn/ui + Tailwind CSS**,通过 Vite 构建,产物输出到 `dist/renderer/`。
174
-
175
- ---
176
-
177
- ## 配置参考
178
-
179
- ### WorkbenchConfig
37
+ 别在 main 模块顶层 `await launch()`:它内部等 `app.whenReady()`,而 Electron 要等 main 模块求值完成才触发 ready,顶层 await 会死锁。
180
38
 
181
- `launch()` 接受的基础字段(`WorkbenchConfig`):
39
+ 带宿主配置的最小形状:
182
40
 
183
- | 字段 | 类型 | 默认值 | 说明 |
184
- | ------------------ | -------------------- | ------------------- | ---------------------------------- |
185
- | `appName` | `string` | `'Dimina DevTools'` | 窗口标题 |
186
- | `adapter` | `CompilationAdapter` | 内置 | 项目编译适配器 |
187
- | `panels` | `BuiltinPanelId[]` | — | **已废弃,运行时忽略**:界面恒显示全部内置面板(WXML / AppData / Storage / Console / 编译);保留字段仅为兼容仍传它的 host |
188
- | `preloadPath` | `string` | 内置 | 统一的 host 级 preload 入口;native-host simulator(WCV)自动跑其 `.cjs` sibling(`cjsSiblingPreloadPath`) |
189
- | `apiNamespaces` | `string[]` | `[]` | 自定义 API 命名空间(如 `['qd']`) |
190
- | `brandingProvider` | `() => { appName }` | — | 品牌信息 provider |
191
- | `headerHeight` | `number` | — | **已废弃,运行时忽略**:头部栏恒为 40px(`HEADER_H`);需要自定义工具栏请用 host toolbar WCV |
192
-
193
- ### WorkbenchAppConfig(扩展 WorkbenchConfig)
194
-
195
- `launch()` 完整接受的 `WorkbenchAppConfig` 额外支持:
196
-
197
- | 字段 | 类型 | 默认值 | 说明 |
198
- | --------------- | ------------------------------------------- | ----------- | ------------------------------------------------- |
199
- | `modules` | `Partial<Record<BuiltinModuleId, boolean>>` | 全部 `true` | 开关内置 IPC 模块组 |
200
- | `rendererDir` | `string` | 内置 | 自定义 renderer HTML 目录 |
201
- | `icon` | `string` | — | 窗口/任务栏图标路径(macOS 使用 app bundle 图标) |
202
- | `menuBuilder` | `(mainWindow, menuContext: MenuContext) => void` | 内置菜单 | 自定义菜单构建器;`menuContext` 为手写窄契约 `MenuContext`(`appName` + workspace 窄集 + `openSettings` + `notify.{projectStatus, windowNavigateBack}`),不含内部管线 |
203
- | `onSetup` | `(instance) => void` | — | 窗口和 context 创建后的回调,用于注册 Contribution(见下文)|
204
- | `onBeforeClose` | `(instance) => void` | — | 窗口关闭前的回调,session 关闭由框架自动处理 |
205
- | `onBeforeOpenProject` | `(projectPath: string) => void \| Promise<void>` | — | 打开项目前、任何副作用(旧会话拆除 / 编译 / dev-server)之前运行的声明式权限门控。抛错即否决:`openProject` 返回 `{ success: false, error }`,当前活动会话保持不动、适配器不被调用,且框架自动把 error 透到状态条(`notify.projectStatus`)。覆盖 IPC / 菜单 / 内部直调三个打开入口 |
206
- | `window` | `WorkbenchWindowConfig`(`width`/`height`/`autoShow?`) | — | 主窗口尺寸覆盖;`autoShow`(默认 `true`)控制 `ready-to-show` 是否自动显示窗口。`autoShow: false` 时窗口创建即隐藏(test / 非 test 一致),由 host 在登录通过后自行 `instance.mainWindow.show()` |
207
- | `updateChecker` | `UpdateChecker` | — | 自定义更新检查器;提供后启用"检查更新"功能 |
208
- | `updateOptions` | `{ checkInterval?, initialDelay?, getCurrentVersion? }` | — | 仅当 `updateChecker` 提供时生效,默认 1h / 5s |
209
-
210
- ### onSetup Contribution
211
-
212
- `onSetup(instance)` 收到的 `instance` 上有一组 typed 注册方法,全部 per-context、自动进 `context.registry`:
213
-
214
- | 方法 | 说明 |
215
- | --- | --- |
216
- | `instance.registerSimulatorApi(name, handler)` | 注册 simulator 自定义 API,小程序里 `wx.<name>()` 调用(详见下方"Simulator 自定义 API")。返回 `Disposable` |
217
- | `instance.registerSimulatorUiExtension({ id, rendererScriptPath })` | 注册可信 renderer bundle。框架负责注入当前 simulator、稳定 Overlay Root、soft reload 的 current/pending 切换和销毁;返回可 `invoke()` 的句柄 |
218
- | `instance.ipc` | `IpcRegistry` 实例,`instance.ipc.handle(channel, fn)` 注册自定义 IPC;已绑定 `senderPolicy` 网关 |
219
- | `instance.registerTrustedWindow(win)` | 把 host 自有弹窗 `BrowserWindow` 加入受信 sender 集,否则其发起的 `instance.ipc` 调用会被网关拒绝。窗口关闭即移除 |
220
-
221
- ### 内置面板 ID
222
-
223
- `'wxml'` \| `'console'` \| `'appdata'` \| `'storage'`
224
-
225
- ### 内置模块 ID
226
-
227
- `'projects'` \| `'session'` \| `'simulator'` \| `'popover'` \| `'settings'`
228
-
229
- ---
230
-
231
- ## Embedding & Extending the Project Panel
232
-
233
- 下游宿主通过 `launch({...})` 嵌入 devtools 时,可对项目面板做三个正交扩展:
234
-
235
- | 扩展点 | 用途 |
236
- | --- | --- |
237
- | `projectsProvider` | 接管项目列表存储 —— 替换默认 `<userData>/dimina-projects.json`,对接下游宿主后台 / IDE workspace / 远端工程库 |
238
- | `projectTemplates` + `builtinTemplates` | 注入/覆盖/裁剪"新建项目"模板列表(同 id 覆盖内置;`builtinTemplates` 控制内置策略) |
239
- | `customCreateProjectDialog` | 用宿主自家页面替换内置"新建项目"对话框(main 进程 hook,可 `new BrowserWindow` 加载任意 URL,通过 IPC/postMessage 回传结果) |
240
-
241
- ### 何时需要哪个扩展点
242
-
243
- | 场景 | 需要的扩展点 |
244
- | --- | --- |
245
- | 项目列表来自下游宿主后台 / 团队工程库 | `projectsProvider` |
246
- | 仅替换可选模板(e.g. 只提供 taro / 宿主自家脚手架) | `projectTemplates` + `builtinTemplates` |
247
- | 创建项目流程要走宿主自己的 wizard / 登录态 | `customCreateProjectDialog` |
248
-
249
- ### 最小示例
250
-
251
- ```typescript
41
+ ```ts
252
42
  import { launch } from '@dimina-kit/devtools'
253
- import type { ProjectsProvider, ProjectTemplate } from '@dimina-kit/devtools/projects-provider'
254
- import { BrowserWindow } from 'electron'
255
-
256
- const provider: ProjectsProvider = {
257
- async listProjects() {
258
- return await host.api.listProjects()
259
- },
260
- async validateProjectDir(dirPath) {
261
- return (await host.api.isMiniApp(dirPath)) ? null : '不是合法的小程序工程'
262
- },
263
- async addProject(dirPath) {
264
- return await host.api.addProject(dirPath)
265
- },
266
- async removeProject(dirPath) {
267
- await host.api.removeProject(dirPath)
268
- },
269
- async updateLastOpened(dirPath) {
270
- await host.api.touchLastOpened(dirPath)
271
- },
272
- async getCompileConfig(dirPath) {
273
- return await host.api.getCompileConfig(dirPath)
274
- },
275
- async saveCompileConfig(dirPath, cfg) {
276
- await host.api.saveCompileConfig(dirPath, cfg)
277
- },
278
- // 缩略图走云端存储——dataUrl 形如 'data:image/png;base64,...'
279
- async saveThumbnail(dirPath, imageDataUrl) {
280
- await host.api.uploadThumbnail(dirPath, imageDataUrl)
281
- },
282
- async getThumbnail(dirPath) {
283
- return await host.api.fetchThumbnail(dirPath)
284
- },
285
- }
286
-
287
- const hostBlank: ProjectTemplate = {
288
- id: 'blank', // 同 id 覆盖内置 blank
289
- name: '宿主空白工程',
290
- description: '由下游宿主维护的官方空白模板',
291
- source: { type: 'directory', path: '/abs/path/to/template' },
292
- }
293
43
 
294
44
  launch({
295
- projectsProvider: provider,
296
- projectTemplates: [hostBlank],
297
- builtinTemplates: ['taro-todo'], // 只保留 taro-todo,干掉默认 blank
298
- customCreateProjectDialog: async ({ parentWindow }) => {
299
- const win = new BrowserWindow({ parent: parentWindow, modal: true, width: 720, height: 520 })
300
- await win.loadURL('https://host.example.com/devtools/new-project')
301
- return await new Promise((resolve) => {
302
- win.webContents.ipc.once('host:create-project:done', (_e, payload) => {
303
- win.close()
304
- // payload 三选一(CustomCreateProjectDialogResult):
305
- // null → 用户取消
306
- // CreateProjectInput → 让 devtools 在本地物化模板并注册到 provider
307
- // { ready: Project } → 宿主后端已经创建好,devtools 直接刷新列表
308
- resolve(payload ?? null)
309
- })
310
- win.on('closed', () => resolve(null))
311
- })
312
- },
313
- })
314
- ```
315
-
316
- ### `customCreateProjectDialog` 返回值
317
-
318
- `hook` 返回 `CustomCreateProjectDialogResult`,三种情形分别对应不同后续动作:
319
-
320
- | 返回 | 行为 |
321
- | --- | --- |
322
- | `null` | 用户取消,无副作用 |
323
- | `CreateProjectInput`(`{ name, path, templateId?, extra? }`) | devtools 在本地拷贝/生成模板 → 改写 `project.config.json` → `provider.addProject(path)`;适合"只想换 UI 但物化由 devtools 兜底" |
324
- | `{ ready: Project }` | devtools 跳过物化,只刷新列表并打开该项目;适合宿主后端已经远端创建好项目,本地完全无需落盘 |
325
-
326
- 物化失败(路径已存在/模板缺失/`provider.addProject` reject)会经 native dialog 弹出错误,再 reject 给渲染层;宿主端无需自己 toast。
327
-
328
- ### 内置模板
329
-
330
- devtools 自带两个 `source`-style 模板,位于 `packages/devtools/templates/`:
331
-
332
- - `blank` — 最小空白小程序骨架(pages/index + app.js/json/wxss + project.config.json,约 8 个文件)
333
- - `taro-todo` — 复制自 `dimina/fe/example/taro-todo`,作为 Taro 编译产物的端到端演示工程
334
-
335
- `builtinTemplates: 'none'` 排除全部内置;`builtinTemplates: ['taro-todo']` 仅保留指定 id;`projectTemplates` 同 id 注入会覆盖(例如上文 `hostBlank` 覆盖了内置 `blank`)。
336
-
337
- ### Provider 部分实现 / fallback 行为
338
-
339
- `ProjectsProvider` 的多数方法是可选的;当宿主只实现 `listProjects` / `addProject` / `removeProject` 时:
340
-
341
- - `validateProjectDir` 缺省 → 返回 `null`(不校验)
342
- - `updateLastOpened` 缺省 → 静默 no-op,"最近"排序退化为 `listProjects` 返回顺序
343
- - `getCompileConfig` 缺省 → 返回 `DEFAULT_COMPILE_CONFIG`(`{ startPage: '', scene: 1001, queryParams: [] }`)
344
- - `saveCompileConfig` 缺省 → 静默 no-op,编译配置面板的编辑不会持久化
345
- - `saveThumbnail` / `getThumbnail` 缺省 → save 静默 no-op,get 返回 `null`;项目卡缩略图不显示。实现这两个方法可让远端项目用云端缩略图(详见示例)
346
-
347
- > 完整契约、参数语义和默认值见 `src/main/services/projects/types.ts` 的 JSDoc,那是 source of truth。
348
-
349
- ---
350
-
351
- ## CompilationAdapter
352
-
353
- 实现此接口接入自定义构建流程:
354
-
355
- ```typescript
356
- import type { CompilationAdapter } from '@dimina-kit/devtools/types'
357
-
358
- const myAdapter: CompilationAdapter = {
359
- async openProject(opts) {
360
- // opts.projectPath — 小程序项目绝对路径
361
- // opts.sourcemap — 是否生成 sourcemap
362
- // opts.onRebuild — 热更新回调
363
- // opts.onBuildError — 构建错误回调
364
- return {
365
- port: 3000,
366
- appInfo: { appName: 'my-app' },
367
- close: async () => {},
368
- }
369
- },
370
- }
371
- ```
372
-
373
- ---
374
-
375
- ## Preload Instrumentation
376
-
377
- 模拟器 frame 注入 preload 脚本,在其全局上安装两类东西:**桥接**(把数据通道暴露到运行时全局)+ **instrumentation**(监听运行时事件并写入桥接)。内置 preload 见 `src/preload/windows/simulator.ts`(native-host simulator 跑其 `.cjs` sibling)。
378
-
379
- ### 必装:桥接
380
-
381
- | 函数 | 作用 | 不装会怎样 |
382
- | --- | --- | --- |
383
- | `installSimulatorBridge()` | 暴露 `window.__simulatorData`,承载 `highlightElement` / `unhighlightElement` / `getAppdata` / `getWxml` / `getStorageSnapshot` 等只读 API(供主进程 `executeJavaScript` 拉取) | 元素选区 overlay 不显示;automation `Page.getData` 返回空对象;MCP `simulator_get_overview` 在 hints 中追加 `simulator bridge not ready (window.__simulatorData missing)`。注:三个右侧面板的数据都另有来源(Storage 走主进程 CDP;AppData/WXML 走主进程 `SimulatorAppDataChannel` / `SimulatorWxmlChannel`;Console 走 render-host/service-host guest preload → 主进程 `consoleLog`),均不依赖 `window.__simulatorData`,不受影响 |
384
- | `installCustomApisBridge()` | 暴露 `window.__diminaCustomApis`,承载 `list` / `invoke`,让模拟器侧把主进程注册的 API 代理回小程序 runtime | `registerSimulatorApi` 注册的业务 API 在小程序里调用全部 no-op(参见下方 "Simulator 自定义 API") |
385
- | `setupApiCompatHook()` | 兼容上游 API 名称变更 | 部分 API 调用兼容性问题 |
386
-
387
- 下游写自定义 preload 时,这三项原则上都要装。若不需要 `registerSimulatorApi`,`installCustomApisBridge` 可省略。
388
-
389
- ### 按需:instrumentation
390
-
391
- 这些探针是**可组合的 preload API**:`@dimina-kit/devtools/preload` 把它们导出,供下游自己拼装自定义 preload。下表是各探针自身的契约(捕获什么 + 推到哪个通道)。
392
-
393
- | 探针 | 捕获内容 | 数据通道 |
394
- | --- | --- | --- |
395
- | `installConsoleInstrumentation()` | `console.*`、`window.onerror`、未捕获 rejection | `sendToHost`(guest → embedder) |
396
- | `createAppDataSource()` | Worker `ub` 消息、setData / page init | miniappSnapshot 框架(`miniapp-snapshot:push`) |
397
- | `createWxmlSource()` | Vue 组件树提取、MutationObserver 自动更新 | miniappSnapshot 框架(`miniapp-snapshot:push`) |
398
-
399
- > ⚠️ **内置 native-host 运行时并不靠这条 `miniapp-snapshot:push` 通道喂面板**:右侧 WXML / AppData 面板的数据由主进程服务(`SimulatorWxmlChannel` / `SimulatorAppDataChannel`)直接供给(见下方「数据流」)。内置 simulator preload 仍调用 `createAppDataSource().start()`,但只为副作用(填 `window.__simulatorData.getAppdata()` 供 automation / MCP 读取),并**不**把它 wrap 进 `MiniappSnapshotHost`——所以 `miniapp-snapshot:push` 在内置路径下无消费者。该框架契约(含 `createWxmlSource`)仍为外部/组合式 preload 保留导出(详见 `docs/miniapp-snapshot.md`)。
400
-
401
- > Storage 面板的数据由主进程通过 CDP `DOMStorage` 域抓取(`src/main/services/simulator-storage`),**不需要 preload 侧 instrumentation**。
402
-
403
- 各面板的数据流详见下方 "数据流" 章节。
404
-
405
- ### 自定义 Preload
406
-
407
- 参考 `src/preload/windows/simulator.ts`:
408
-
409
- ```typescript
410
- // my-preload.ts
411
- import {
412
- installSimulatorBridge,
413
- installCustomApisBridge,
414
- installConsoleInstrumentation,
415
- createMiniappSnapshotHost,
416
- createAppDataSource,
417
- createWxmlSource,
418
- setupApiCompatHook,
419
- } from '@dimina-kit/devtools/preload'
420
-
421
- // 1. 兼容 hook + 桥接(必装,且应在 instrumentation 之前)
422
- setupApiCompatHook()
423
- installSimulatorBridge()
424
- installCustomApisBridge() // 若用到 registerSimulatorApi,必装
425
-
426
- // 2. Console instrumentation(按需)
427
- installConsoleInstrumentation()
428
-
429
- // 3. miniappSnapshot 框架:注册面板数据源后 install()
430
- const snapshotHost = createMiniappSnapshotHost()
431
- snapshotHost.register(createAppDataSource())
432
- snapshotHost.register(createWxmlSource())
433
- snapshotHost.install()
434
-
435
- // 4. 自定义 hook
436
- window.addEventListener('error', (e) => {
437
- console.error('[my-preload]', e.message)
45
+ appName: 'My Miniapp Studio',
46
+ adapter: myCompilationAdapter,
47
+ onSetup(instance) {
48
+ instance.registerSimulatorApi('login', params => login(params))
49
+ },
50
+ }).catch((err) => {
51
+ console.error(err)
52
+ process.exit(1)
438
53
  })
439
54
  ```
440
55
 
441
- 编译后传入路径:
442
-
443
- ```typescript
444
- launch({ preloadPath: '/absolute/path/to/my-preload.js' })
445
- ```
446
-
447
- ---
448
-
449
- ## Simulator 自定义 API
450
-
451
- 下游通过 `onSetup` 里的 `instance.registerSimulatorApi(name, handler)` 把业务 API 注入到模拟器内运行的小程序。注册 per-context,随 context 销毁。Handler 在 Electron **主进程**执行,能用任何 Node 能力(fs、net、原生模块、持久状态);模拟器侧拿到 `wx.<name>(params)` 调用后通过 IPC 转发到主进程,await 返回结果,handler 抛错则原型回传给小程序。
56
+ 配置字段、`CompilationAdapter`、项目 Provider、preload、模拟器 UI、host toolbar 和公共导出已经移到[库集成参考](./docs/library-integration.md)。
452
57
 
453
- > ⚠️ 使用本功能要求模拟器 preload 调用 `installCustomApisBridge()`(详见上方 "Preload Instrumentation")。内置 preload 已包含此调用;若用了自定义 preload 又漏装,注册的 API 在小程序运行时**没有任何反应**,simulator 端会有 `console.warn`。
58
+ ## 先读哪些文档
454
59
 
455
- ### 命名约定
456
-
457
- `name` 是 dimina service 层 `invokeAPI(name, opts)` 用的**裸名**,不带 `wx.` 前缀。注册之后小程序里通过 `wx.<name>(...)`(以及在 `apiNamespaces` 配的其它命名空间)调用都会命中:
458
-
459
- | 小程序里写 | `registerSimulatorApi` 的 name |
60
+ | 目标 | 文档 |
460
61
  | --- | --- |
461
- | `wx.login(...)` | `'login'` |
462
- | `wx.request(...)` | `'request'` |
463
- | `wx.getStorage(...)` 覆盖内置 | `'getStorage'`(小心:这会**屏蔽** dimina 容器的实现) |
464
-
465
- ```typescript
466
- launch({
467
- onSetup: (instance) => {
468
- // 提供 wx.login 的实现(dimina 容器默认未实现,注册后才能用)
469
- const dispose = instance.registerSimulatorApi('login', async ({ success }) => {
470
- // 主进程上下文:可调用内部 HTTP、读取凭证文件、访问原生模块
471
- const code = await callInternalAuth()
472
- return { code }
473
- })
474
-
475
- // 注册物已进 context.registry,随 context 销毁;
476
- // 提前取消用返回的 dispose()
477
- },
478
- })
479
- ```
480
-
481
- ### 行为约定
482
-
483
- - **注册时机**:模拟器在启动小程序之前会 `await` 主进程的自定义 API 全量列表并完成注册,因此运行时框架在初始化时一次性枚举 API 表(典型如 Taro 的 `Object.keys(wx)`)也能枚举到 `registerSimulatorApi` 注册的 name。bridge 异常或超时(3s)则降级为"无自定义 API",不会阻塞小程序启动。
484
- - **命中顺序**:小程序运行时按 `apiRegistry[name] → MiniApp 实例方法 → 扩展模块兜底` 查找,所以 `registerSimulatorApi` 注册的 name **优先于内置实现**——既能给"未实现"的 wx.* 补功能(如 `wx.login`),也能覆盖默认的 wx.* 行为(如 `wx.request`,dimina-kit 本身就在用这条路把 `request` 接到主进程 fetch)。
485
- - **回调风格自动桥接**:小程序用微信风格 `wx.<name>({ success, fail, complete })` 调用时,模拟器会把 handler 的 resolve 派发给 `success`、reject 派发给 `fail`,并在两种情况下都触发 `complete`。handler 本身只需 promise 风格 resolve/reject —— `success/fail/complete` 是渲染端回调 id,转发到主进程前已被剔除,handler 不会收到它们。
486
- - 同名重复注册:后注册的覆盖前者,静默替换。
487
- - `dispose()` 只会移除自己创建的那次注册——如果之后被别人覆盖了,旧的 disposer 是 no-op。
488
- - 调用未注册且容器内也没有的 name:小程序侧拿到 reject,错误信息里带 name。
489
- - Handler 可同步或异步返回;**返回值需可 JSON 序列化**(IPC 限制)。函数、Symbol、Map/Set、循环引用都会丢失。
490
-
491
- ---
492
-
493
- ## Simulator UI 扩展
494
-
495
- 需要分享面板、授权弹窗等产品 UI 的下游 host,应同时使用两个正交接口:
496
-
497
- - `registerSimulatorApi` 定义小程序侧 `wx.*` 命令。
498
- - `registerSimulatorUiExtension` 安装下游 renderer bundle,并通过返回句柄把命令送到 UI。
499
-
500
- 主进程只注册绝对路径指向的可信构建产物:
501
-
502
- ```typescript
503
- const nativeUi = instance.registerSimulatorUiExtension({
504
- id: 'my.native-ui',
505
- rendererScriptPath: fileURLToPath(
506
- new URL('../renderer/native-ui.js', import.meta.url),
507
- ),
508
- })
509
-
510
- instance.registerSimulatorApi('share', params =>
511
- nativeUi.invoke('share', params),
512
- )
513
- ```
514
-
515
- renderer bundle 使用 `@dimina-kit/devtools/simulator-ui` 注册实现:
516
-
517
- ```typescript
518
- import { registerSimulatorUiExtension } from '@dimina-kit/devtools/simulator-ui'
519
-
520
- let runtime
521
-
522
- registerSimulatorUiExtension({
523
- id: 'my.native-ui',
524
- mount({ overlayRoot, appId, signal }) {
525
- runtime = createNativeUi({ root: overlayRoot, appId })
526
- signal.addEventListener('abort', () => runtime.destroy(), { once: true })
527
- return () => runtime.destroy()
528
- },
529
- invoke(method, params) {
530
- return runtime.invoke(method, params)
531
- },
532
- onChromeAction(action) {
533
- if (action.name !== 'capsule.more') return false
534
- runtime.showMoreMenu(action)
535
- return true
536
- },
537
- })
538
- ```
539
-
540
- 契约边界:
541
-
542
- - 框架只提供设备内的稳定 Overlay Root,不接收 React Component,因而不绑定下游 React 版本。
543
- - 只有可见的 current `DeviceShell` 持有挂载点;soft reload 提升 pending shell 时,旧 mount 先收到 abort/cleanup,再在新 root 上 mount。
544
- - Overlay Root 默认不拦截页面点击;扩展实际创建的交互层需要设置 `pointer-events: auto`。
545
- - 胶囊菜单通过语义动作 `capsule.more` 分发,不应查询 `.menu-capsule__more` 或拦截 DOM click。
546
- - `id` 在一个 context 内唯一;script 必须用相同 `id` 注册 renderer 实现,否则安装失败。
547
- - 每次 simulator 顶层文档完成加载时都会重新读取 `rendererScriptPath`,因此开发态重建
548
- bundle 后执行硬 reload 即可加载新代码;soft reload 只切换 DeviceShell 挂载点,不重复执行 bundle。
549
- - `dispose()` 会注销 renderer 实现;context 销毁时自动执行。`invoke()` 参数和返回值必须可跨 Electron IPC/`executeJavaScript` 序列化。
550
-
551
- ---
552
-
553
- ## Host Toolbar(宿主自定义工具栏)
554
-
555
- 下游通过 `instance.context.views.hostToolbar` 拥有 devtools 头部下方的工具栏条(一个 WebContentsView):`loadURL` / `loadFile` 加载自己的内容,`setPreloadPath` 注入自己的 preload,`setHeightMode` 钉死或自动跟随内容高度(自动模式要求内容自带 shrink-to-fit 的 `[data-host-toolbar-root]` 包裹元素;`{ fixed }` 校验入参——非有限数或负数同步抛 `TypeError` 且不污染既有模式)。主进程保留最后一次下发的高度(`views.getHostToolbarHeight()`),项目视图的占位条挂载时会主动拉取并回放——广播器对已上报的高度去重不再重发,没有这一步,冷启动在项目列表期间的上报、以及关闭项目再打开后的高度都会永久丢失(工具栏条塌缩为 0)。
556
-
557
- ### 双向消息:onMessage / send
558
-
559
- 主进程与工具栏页面之间有一条门控窄通道(按 load 握手的 MessagePort,信封 `{ channel, payload }`):
560
-
561
- - `hostToolbar.onMessage(channel, handler): { dispose() }` — 接收页面消息。控制面级注册:可在 view 创建前调用,且跨页面 reload 自动续接,无需重新注册。空串/非 string channel 抛错。
562
- - `hostToolbar.send(channel, payload): boolean` — 发消息给页面。门控不排队:没有活的工具栏 webContents、本次 load 的握手未完成、或正处于换文档导航窗口期(`loadURL`/`loadFile` 发起后、以及页面自发的主框架跨文档导航开始后,直到新文档握手完成)时返回 `false`(不投递、不建 view);返回 `true` 即已发出。返回值本身就是就绪信号,下游不再需要拿 `getHostToolbarWebContentsId()` 手工判断工具栏是否就绪(该 API 保留)。
563
- - `hostToolbar.onReady(handler): { dispose() }` — 握手完成的推送信号(每个 load generation fire 一次;已就绪后注册在微任务上补发一次,补发前复查订阅与 generation)。在 handler 内 `send()` 必然返回 `true`,可用于推送初始状态,替代轮询 retry loop。
564
-
565
- 页面侧由框架自动注入 `window.diminaHostToolbar`(仅 `{ send, onMessage }` 两个函数;类型可从根入口导入 `DiminaHostToolbarPageBridge`,并附带 optional 的 `Window` 增强。握手前的 `send` 进队列、握手后按序送达。队列上限 128 条:超限丢弃最新一条、首次超限 console.warn 一次,不抛错。`send`/`onMessage` 对空串/非 string channel 同步抛 `TypeError`,与主进程同语义):
566
-
567
- ```js
568
- // 工具栏页面内
569
- window.diminaHostToolbar.onMessage('host:cmd', (payload) => { /* ... */ })
570
- window.diminaHostToolbar.send('page:event', { clicked: 'compile' })
571
- ```
572
-
573
- ```ts
574
- // 主进程(onSetup 内)
575
- const sub = instance.context.views.hostToolbar.onMessage('page:event', (payload) => { /* ... */ })
576
- instance.context.views.hostToolbar.send('host:cmd', { theme: 'dark' }) // false = 未送达
577
- ```
62
+ | devtools 嵌入自己的 Electron 应用 | [库集成参考](./docs/library-integration.md) |
63
+ | 了解目录、构建、调试和安全边界 | [贡献者指南](./docs/contributing.md) |
64
+ | 了解窗口、WebContentsView、IPC 和生命周期 | [Electron Container 架构](./docs/electron-container.md) |
65
+ | 了解模拟器与页面 render host | [模拟器渲染架构](./docs/simulator-render-architecture.md) |
66
+ | 了解 Chrome DevTools 的 Console、Network、Elements 和源码跳转 | [DevTools CDP 路由](./docs/devtools-cdp-routing.mdx) |
67
+ | 了解内置 VS Code 工作台 | [编辑器集成](./docs/editor-integration.md) |
68
+ | 了解项目窗口的 dock、分屏和原生视图定位 | [项目窗口布局](./docs/project-window-layout.md) [视图 Placement 对账](./docs/view-placement-reconciler.md) |
69
+ | 了解页面栈或 TabBar | [页面栈](./docs/page-stack.md) [TabBar](./docs/tab-bar.md) |
578
70
 
579
- ---
580
-
581
- ## 模块导出一览
582
-
583
- `@dimina-kit/devtools` 的公共入口。`api.ts`(根入口)聚合了所有公共 API,优先从根入口导入。
584
-
585
- ```
586
- @dimina-kit/devtools launch, buildDefaultMenu,
587
- openSettingsWindow, suppressEpipe, setupCdpPort,
588
- asMiniappRuntime, MiniappRuntime / MiniappSessionAppInfo /
589
- DiminaHostToolbarPageBridge(type),
590
- createWorkbenchContext, createMainWindow,
591
- createViewManager, IpcRegistry,
592
- UpdateManager, createGitHubReleaseChecker, ...
593
- @dimina-kit/devtools/launch launch(config?), buildDefaultMenu, openSettingsWindow
594
- @dimina-kit/devtools/types TypeScript 类型定义
595
- @dimina-kit/devtools/bootstrap suppressEpipe(), setupCdpPort()
596
- @dimina-kit/devtools/context createWorkbenchContext(opts)
597
- @dimina-kit/devtools/create-window createMainWindow(opts)
598
- @dimina-kit/devtools/paths rendererDir, defaultPreloadPath, simulatorDir,
599
- getRendererDir, getPreloadDir, getRendererHtml
600
- @dimina-kit/devtools/projects-provider ProjectsProvider / ProjectTemplate 等类型
601
- @dimina-kit/devtools/workbench-settings loadWorkbenchSettings(), saveWorkbenchSettings(), applyTheme()
602
- @dimina-kit/devtools/preload installSimulatorBridge,
603
- installCustomApisBridge,
604
- installConsoleInstrumentation,
605
- createMiniappSnapshotHost,
606
- createAppDataSource, createWxmlSource,
607
- setupApiCompatHook
608
- (storage 面板数据由主进程 CDP 抓取,无 preload 侧 hook)
609
- ```
610
-
611
- > `IpcRegistry` 是 host 写自定义 IPC 的网关基类——`onSetup` 里通过 `instance.ipc` 拿到一个已绑定 `senderPolicy` 的实例,无需自行构造。
612
- > 自定义 simulator API 通过 `instance.registerSimulatorApi` 注册,没有独立的导出入口。
613
- > 内置 IPC 模块(`register*Ipc` / `WorkbenchModule`)是 devtools 内部实现,不再对外导出。
614
-
615
- ---
616
-
617
- ## MiniappRuntime(宿主集成推荐契约)
618
-
619
- 下游宿主集成时,推荐依赖手写的稳定契约 `MiniappRuntime`,而不是整个 `WorkbenchContext`——后者携带全部内部 service 类型,内部重构会直接破坏下游编译。`asMiniappRuntime(ctx)` 恒等返回(typed view,非拷贝),宿主对 `runtime.workspace.openProject` 的 monkey-patch 会落在真实对象上:
620
-
621
- ```typescript
622
- import { asMiniappRuntime, type MiniappRuntime } from '@dimina-kit/devtools'
623
-
624
- const runtime: MiniappRuntime = asMiniappRuntime(instance.context)
625
- runtime.workspace.openProject = gated(runtime.workspace.openProject) // 权限闸(契约保证可写)
626
- runtime.views.hostToolbar.send('host:cmd', { theme: 'dark' })
627
- runtime.notify.projectStatus({ status: 'ready', message: '编译完成' })
628
- ```
629
-
630
- 契约面:`views.hostToolbar`(7 成员,含 `onReady`,无 `webContents`)/ `workspace`(7 方法,`getSession().appInfo` 为结构化 `MiniappSessionAppInfo`,`appId` 必有)/ `notify.projectStatus` / `registry.add`(接受 `{ dispose }` 对象或裸函数两种习语)/ `openSettings()`。零 Electron 类型。0.4.0 起不再含 `rendererDir`(用 `/paths` 导出)与 `windows`(用 `openSettings()`)。
631
-
632
- ## WorkbenchContext
633
-
634
- `WorkbenchContext` 是所有扩展点的唯一容器。配置字段直接暴露,运行时状态通过 service 访问。host hook 拿到的 `instance.context` 即此类型;`menuBuilder` 拿到的是手写窄契约 `MenuContext`(`appName` + workspace 窄集 + `openSettings` + `notify.{projectStatus, windowNavigateBack}`,不再是 `WorkbenchContext` 的 Omit 投影)。
635
-
636
- ```typescript
637
- interface WorkbenchContext {
638
- adapter: CompilationAdapter
639
- preloadPath: string
640
- rendererDir: string
641
- apiNamespaces: string[]
642
- appName: string
643
- brandingProvider?: () => Promise<{ appName: string }> | { appName: string }
644
-
645
- // ── Services(运行时状态封装在内部,通过方法访问)──
646
- windows: WindowService // ctx.windows.mainWindow, ctx.windows.settingsWindow, ...
647
- views: ViewManager // ctx.views.repositionAll(), ctx.views.getSimulatorWebContentsId(), ...
648
- notify: RendererNotifier // ctx.notify.projectStatus(), ctx.notify.windowNavigateBack(), ...
649
- workspace: WorkspaceService // ctx.workspace.getProjectPath(), ctx.workspace.hasActiveSession(), ...
650
-
651
- // ── 扩展容器(host 通过 instance 上的方法写入,不直接操作)──
652
- simulatorApis: SimulatorApiRegistry // instance.registerSimulatorApi 写入
653
- trustedWindowSenderIds: Map<number, number> // instance.registerTrustedWindow 写入(webContents.id → 引用计数)
654
-
655
- // ── Internal lifecycle / security (host 不直接消费) ──
656
- registry: DisposableRegistry // 框架在创建时统一聚合 dispose
657
- senderPolicy: SenderPolicy // IpcRegistry 自动校验 sender 白名单
658
- }
659
- ```
660
-
661
- 会话和视图状态封装在对应 service 的私有闭包中,通过方法访问,例如:
662
-
663
- - `ctx.workspace.getProjectPath()` / `ctx.workspace.getSession()` / `ctx.workspace.hasActiveSession()`
664
- - `ctx.views.getSimulatorWebContentsId()` / `ctx.views.getSimulatorWebContents()`
665
- - `ctx.windows.mainWindow` / `ctx.windows.settingsWindow`
666
-
667
- ---
668
-
669
- ## 整体架构
670
-
671
- ### 视图层级(从上到下 = 从顶层到底层)
672
-
673
- ```mermaid
674
- flowchart TB
675
- subgraph BW["BrowserWindow"]
676
- direction TB
677
- Z4["⑤ popoverView &nbsp;[顶层]<br/>entries/popover/index.html<br/>全屏透明遮罩 + 编译配置表单"]
678
- Z3["④ settingsView<br/>entries/settings/index.html<br/>固定宽度 320px,右侧对齐"]
679
- Z2["② simulatorView<br/>Chrome DevTools 面板(绑到活跃 render-host guest / service host)"]
680
- Z1["① nativeSimulatorView : WebContentsView<br/>模拟器主进程顶层 WCV(跑 simulator/main.tsx 的 DeviceShell)<br/>partition=persist:simulator,托管每页 render-host &lt;webview&gt; guest"]
681
- subgraph Z0["⓪ mainWindow &nbsp;[底层]&nbsp;entries/main/index.html"]
682
- direction LR
683
- SIM["&lt;SimulatorPanel&gt; 占位 / 锚点<br/>(仅 nativeSimulatorView 的 bounds target,无 webview)"]
684
- PNL["WXML / AppData / Storage<br/>React 中直接渲染"]
685
- end
686
- Z4 -. 覆盖 .-> Z3
687
- Z3 -. 覆盖 .-> Z2
688
- Z2 -. 覆盖 .-> Z1
689
- Z1 -. 覆盖 .-> Z0
690
- end
691
- ```
692
-
693
- 层级由 `addChildView()` 调用顺序决定,后添加的 View 在上层。overlay 栈里有**两个不同的** simulator 相关 view:`nativeSimulatorView`(模拟器本体,主进程顶层 `WebContentsView`,跑 `simulator/main.tsx` 的 DeviceShell,内部托管每页 render-host `<webview>` guest)和 `simulatorView`(Chrome DevTools 面板本身,绑到活跃 render-host guest / service host)。模拟器**不是** mainWindow renderer 里的 `<webview>`——主 renderer 的 `<SimulatorPanel>` 只画一个空占位 div 当 `nativeSimulatorView` 的 bounds 锚点(手机框 / 圆角 / 刘海 / 页面 `<webview>` 都在那个 WCV 内部的 DeviceShell 里)。内置面板(WXML / AppData / Storage)不使用独立 WebContentsView,直接在主窗口 React 中渲染;只有 nativeSimulator / Chrome DevTools / settings / popover 需要 View 覆盖。
694
-
695
- ### 数据流
696
-
697
- 按面板 / 场景拆分:
698
-
699
- | 面板 / 场景 | 模式 | 路径 |
700
- | --- | --- | --- |
701
- | Console | 推送 | render-host / service-host preload monkeypatch `console.*` → 主进程 `consoleLog` 容器消息 → bridge-router → `ctx.guestConsole.emit` → `services/console-forward`。render(视图)层条目以 `[视图]` 前缀注入挂在 service host 上的 Chrome DevTools,service 层本就原生显示在同一面板;automation WS 等外部订阅者也从这里 fan-out |
702
- | AppData / WXML 树 | 推送 + 拉取 | 主进程服务,镜像 Storage 的 main→renderer 契约:WXML 由 `services/simulator-wxml` 经 render-inspect 从活跃 render-host `<webview>` guest 拉树(`SimulatorWxmlChannel.GetSnapshot`),render 活动时推 `SimulatorWxmlChannel.Event`;AppData 由 `services/simulator-appdata` 在 bridge-router 里 tap service→render 的 setData 流,推 `SimulatorAppDataChannel.Event` |
703
- | Storage | 拉取 + 推送 | renderer `ipcInvoke(SimulatorStorageChannel.GetSnapshot)` → 主进程 CDP `DOMStorage` 域;变更事件由主进程通过 `SimulatorStorageChannel.Event` 推回 renderer |
704
- | 元素选区 / `Page.getData` / MCP overview | 拉取 | 主进程 `webContents.executeJavaScript(...)` 或 `Runtime.evaluate(...)` 读 `window.__simulatorData.*`(由 `installSimulatorBridge` 暴露) |
705
-
706
- ---
707
-
708
- ## IPC 通信总览
709
-
710
- IPC 频道名常量集中定义在 `src/shared/ipc-channels.ts`,按域分组(`SimulatorChannel` / `ProjectChannel` / `WorkbenchSettingsChannel` 等)。该文件是频道清单的唯一真相源;此处不再维护手抄表,以免与代码漂移。
711
-
712
- ---
713
-
714
- ## 已知限制:Electron `chrome.devtools.panels.create()` 不可用
715
-
716
- Electron 对 `chrome.devtools.panels.create()` 的支持**长期不稳定**。该 API 调用成功(无报错,返回 panel 对象),但面板 tab 永远不出现在 DevTools UI 中。
717
-
718
- **影响范围**:从 Electron 7 到 39 均有报告([#23662](https://github.com/electron/electron/issues/23662)、[#41613](https://github.com/electron/electron/issues/41613)、[#48705](https://github.com/electron/electron/issues/48705)),非特定版本 bug,而是系统性实现缺陷。官方文档声称完全支持,但实际因竞态条件和实现不完整导致面板无法可靠渲染。Electron 团队无修复计划。
719
-
720
- **当前方案**:内置面板(WXML / AppData / Storage)直接在主窗口 React 中渲染,数据由主进程服务直接供给——WXML / AppData 走 `SimulatorWxmlChannel` / `SimulatorAppDataChannel`(main → renderer 的 seed `GetSnapshot` + push `Event`),Storage 走 CDP `DOMStorage` 域(详见上方「数据流」);Chrome DevTools 仍作为独立 `WebContentsView` 通过 `setDevToolsWebContents()` 挂载。面板切换由 renderer toolbar 的 tab 栏控制,主进程负责 nativeSimulator/settings/popover View 的创建、定位和显隐。详见 `src/main/ipc/simulator.ts`。
721
-
722
- ---
723
-
724
- ## 调试
725
-
726
- - **Cmd+Shift+I** / **Ctrl+Shift+I** — 打开 mainWindow 的 Electron DevTools(调试 React 渲染层)
727
- - 模拟器活跃 render-host `<webview>` guest 的 Chrome DevTools 在编译完成、`dom-ready` 后自动打开
728
- - 开发模式(`!app.isPackaged`)下 mainWindow DevTools 自动以 detach 模式打开
729
-
730
- ---
731
-
732
- ## 开发
71
+ ## 在仓库中开发
733
72
 
734
73
  ```bash
735
- pnpm build # 构建全部(main + preload + renderer)
736
- pnpm build:main # 仅构建主进程 TypeScript
737
- pnpm build:preload # 仅构建 preload TypeScript
738
- pnpm build:renderer # Vite 构建渲染层
739
- pnpm dev # Watch 模式 + Electron
740
- pnpm check-types # 类型检查
741
- pnpm test # 运行单元测试(vitest)
742
- pnpm test:coverage # 单元测试 + 覆盖率报告(终端表格 + coverage/index.html)
743
- pnpm test:e2e # 运行 Playwright e2e 测试
74
+ pnpm --filter @dimina-kit/devtools build
75
+ pnpm --filter @dimina-kit/devtools dev
76
+ pnpm --filter @dimina-kit/devtools check-types
77
+ pnpm --filter @dimina-kit/devtools test
78
+ pnpm --filter @dimina-kit/devtools test:e2e
744
79
  ```
745
80
 
746
- > 覆盖率:`pnpm test:coverage`(单包)或仓库根 `pnpm test:coverage`(全部包),报告输出到各包 `coverage/`,浏览器打开 `coverage/index.html` 看逐行高亮。
747
-
748
- ---
749
-
750
- ## 安全说明
751
-
752
- 本工具仅用于本地开发调试。当前的安全配置:
753
-
754
- - **Workbench 窗口**(main / settings / popover overlay)均启用 `contextIsolation` 并禁用 `nodeIntegration`;renderer 通过 main preload 经 `contextBridge` 拿到 `window.devtools.ipc`,每个 IPC handler 经 sender whitelist 校验调用方
755
- - **Simulator 本体** 是主进程顶层 `WebContentsView`(`nativeSimulatorView`,per-project `persist:miniapp-<key>` partition、无 appId 时回落共享 `persist:simulator`、`nodeIntegration=false`),其 preload 由 `webPreferences.preload = cjsSiblingPreloadPath(ctx.preloadPath)` 在创建时(`attachNativeSimulator`)固定。**小程序的每页代码** 运行在该 WCV 内部 DeviceShell 托管的 render-host `<webview>` guest 里(`nodeIntegration=false`),承载逐页沙箱;guest 不带静态 `partition` 属性,由主进程 `will-attach-webview` 钉到同一 per-project partition,并钉死 contextIsolation/sandbox,渲染进程无法替换
756
- - **renderer 入口** 启用了保守 CSP;`will-navigate` / `setWindowOpenHandler` 屏蔽外部跳转
757
-
758
- 仍然不要在 devtools 窗口中加载不受信任的远程内容;本工具不打算在非本地环境部署。
81
+ `dev` 会先完整构建,再启动 renderer、simulator、主进程和 preload 的监听任务,最后打开 Electron。
759
82
 
760
- ### MCP Server 风险
83
+ ## 安全提醒
761
84
 
762
- 内置的 MCP server 默认关闭,由 `startMcpServer` 入口显式启动,或通过 workbench 设置中的 `mcp.enabled` 开启。开启后会在本机监听 SSE 端点,向任何可以连接到该端点的 MCP 客户端暴露一组工具,能力包括:读取 DOM 结构、截屏、拉取网络日志、触发导航等。
85
+ devtools 面向本地开发。不要在它的窗口或宿主扩展中加载不受信任的远程内容。自定义 IPC 应通过 `onSetup(instance)` 提供的 `instance.ipc` 注册;宿主自己创建的窗口还要先调用 `instance.registerTrustedWindow(win)`。更完整的边界见[贡献者指南](./docs/contributing.md#安全边界)。
763
86
 
764
- `workbench_evaluate` 已从 MCP 工具集中移除,workbench 只暴露只读诊断工具(截屏、console 日志、DOM、网络日志等)。`simulator_evaluate` 仍然保留,但小程序页面跑在 render-host `<webview>` guest 里(`nodeIntegration=false`),其 `evaluate` 仅在页面 JS 上下文执行,不具备 Node API 访问能力。
87
+ ## License
765
88
 
766
- 仅在连接到可信的本机 MCP 客户端或被信任的 AI 工具时启用该功能;不要在公共 WiFi、共享机器或存在远程 SSH 端口转发的场景下开启,以免监听端口被非预期的客户端访问。
89
+ [MIT](../../LICENSE)