@dimina-kit/devtools 0.4.0-dev.20260616102751 → 0.4.0-dev.20260624040247

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 (78) hide show
  1. package/README.md +30 -25
  2. package/dist/main/app/app.js +14 -2
  3. package/dist/main/index.bundle.js +559 -111
  4. package/dist/main/ipc/bridge-router.js +57 -9
  5. package/dist/main/ipc/projects.js +1 -1
  6. package/dist/main/ipc/simulator.js +1 -1
  7. package/dist/main/runtime/devtools-backend.d.ts +1 -1
  8. package/dist/main/runtime/devtools-backend.js +1 -1
  9. package/dist/main/runtime/miniapp-runtime.d.ts +5 -5
  10. package/dist/main/services/console-forward/index.js +8 -1
  11. package/dist/main/services/elements-forward/index.js +36 -12
  12. package/dist/main/services/layout/index.d.ts +12 -0
  13. package/dist/main/services/layout/index.js +14 -2
  14. package/dist/main/services/network-forward/index.d.ts +5 -6
  15. package/dist/main/services/network-forward/index.js +9 -10
  16. package/dist/main/services/notifications/renderer-notifier.d.ts +3 -4
  17. package/dist/main/services/projects/index.js +1 -1
  18. package/dist/main/services/projects/types.d.ts +1 -1
  19. package/dist/main/services/projects/types.js +1 -1
  20. package/dist/main/services/service-console/console-api.d.ts +77 -0
  21. package/dist/main/services/service-console/console-api.js +103 -0
  22. package/dist/main/services/service-console/index.d.ts +47 -0
  23. package/dist/main/services/service-console/index.js +139 -0
  24. package/dist/main/services/service-host-pool/pool.d.ts +2 -3
  25. package/dist/main/services/simulator-storage/index.d.ts +2 -4
  26. package/dist/main/services/simulator-storage/index.js +2 -4
  27. package/dist/main/services/simulator-temp-files/index.js +2 -2
  28. package/dist/main/services/update/update-manager.d.ts +1 -1
  29. package/dist/main/services/update/update-manager.js +1 -1
  30. package/dist/main/services/views/host-toolbar-session-runtime.d.ts +4 -4
  31. package/dist/main/services/views/host-toolbar-session-runtime.js +4 -4
  32. package/dist/main/services/views/view-manager.d.ts +24 -1
  33. package/dist/main/services/views/view-manager.js +120 -41
  34. package/dist/main/services/workbench-context.d.ts +7 -6
  35. package/dist/main/services/workspace/workspace-service.js +11 -0
  36. package/dist/main/windows/service-host-window/create.d.ts +1 -0
  37. package/dist/main/windows/service-host-window/create.js +1 -0
  38. package/dist/preload/runtime/host-toolbar-port.d.ts +1 -1
  39. package/dist/preload/runtime/host-toolbar-port.js +1 -1
  40. package/dist/preload/runtime/host-toolbar-runtime.d.ts +8 -7
  41. package/dist/preload/runtime/host-toolbar-runtime.js +8 -7
  42. package/dist/preload/windows/host-toolbar-runtime.cjs.map +2 -2
  43. package/dist/preload/windows/main.cjs.map +2 -2
  44. package/dist/preload/windows/simulator.cjs +1 -1
  45. package/dist/preload/windows/simulator.cjs.map +1 -1
  46. package/dist/preload/windows/simulator.js +1 -1
  47. package/dist/renderer/assets/index-PtsdWlGu.js +50 -0
  48. package/dist/renderer/assets/{input-B8NDCYv3.js → input-DiCxYlqY.js} +2 -2
  49. package/dist/renderer/assets/{ipc-transport-DhrajiC5.js → ipc-transport-DSBmaWpJ.js} +1 -1
  50. package/dist/renderer/assets/ipc-transport-DZxf2YoM.css +1 -0
  51. package/dist/renderer/assets/{popover-DbZOg03s.js → popover-CyL_uffj.js} +2 -2
  52. package/dist/renderer/assets/{select-pW3Ysc9T.js → select-BSwvDMtA.js} +2 -2
  53. package/dist/renderer/assets/settings-Ch8XbFxf.js +2 -0
  54. package/dist/renderer/assets/{settings-api-BSKzKiWd.js → settings-api-e9ILe2JO.js} +2 -2
  55. package/dist/renderer/assets/{workbenchSettings-BGxjl25x.js → workbenchSettings-DZsHf1g_.js} +2 -2
  56. package/dist/renderer/entries/main/index.html +6 -6
  57. package/dist/renderer/entries/popover/index.html +5 -5
  58. package/dist/renderer/entries/settings/index.html +11 -5
  59. package/dist/renderer/entries/workbench-settings/index.html +4 -4
  60. package/dist/service-host/preload.cjs +13 -15
  61. package/dist/service-host/sourcemap-rewrite.cjs +21 -12
  62. package/dist/shared/appdata-accumulator.d.ts +2 -2
  63. package/dist/shared/appdata-accumulator.js +3 -3
  64. package/dist/shared/constants.d.ts +1 -1
  65. package/dist/shared/constants.js +1 -1
  66. package/dist/shared/ipc-channels.d.ts +3 -3
  67. package/dist/shared/ipc-channels.js +3 -3
  68. package/dist/shared/open-in-editor.d.ts +20 -1
  69. package/dist/shared/open-in-editor.js +246 -3
  70. package/dist/shared/vpath.d.ts +2 -2
  71. package/dist/shared/vpath.js +2 -2
  72. package/dist/simulator/assets/{device-shell-BWX7Yopg.js → device-shell-TtcSXwhb.js} +2 -2
  73. package/dist/simulator/assets/{simulator-sf-D0mhw.js → simulator-CtyXt_4V.js} +3 -3
  74. package/dist/simulator/simulator.html +1 -1
  75. package/package.json +8 -6
  76. package/dist/renderer/assets/index-Z4Rh52G8.js +0 -50
  77. package/dist/renderer/assets/ipc-transport-9agi76dX.css +0 -1
  78. package/dist/renderer/assets/settings-CZJOHt8b.js +0 -2
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  基于 Electron 的小程序开发者工具。提供模拟器、Chrome DevTools 面板、WXML/AppData/Storage/编译 面板、编译配置等功能。
4
4
 
5
- 下游 host 通过 `launch(config)` 集成并定制 devtools(零配置直接 `launch()`,配置驱动 `launch({...})`;见下方「两种用法」)。两种用法都经领域中立的 [`@dimina-kit/electron-deck`](../electron-deck) 框架编排——框架接管 Electron 进程生命周期(whenReady / will-quit)、wire/trust 原语,devtools 作为 `RuntimeBackend` 注入完整运行时(见 [`framework-extraction-v2.md`](../electron-deck/docs/framework-extraction-v2.md))。
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))。
6
6
 
7
7
  ---
8
8
 
@@ -195,7 +195,8 @@ src/
195
195
  | `menuBuilder` | `(mainWindow, menuContext: MenuContext) => void` | 内置菜单 | 自定义菜单构建器;`menuContext` 为手写窄契约 `MenuContext`(`appName` + workspace 窄集 + `openSettings` + `notify.{projectStatus, windowNavigateBack}`),不含内部管线 |
196
196
  | `onSetup` | `(instance) => void` | — | 窗口和 context 创建后的回调,用于注册 Contribution(见下文)|
197
197
  | `onBeforeClose` | `(instance) => void` | — | 窗口关闭前的回调,session 关闭由框架自动处理 |
198
- | `window` | `WorkbenchWindowConfig` | — | 窗口尺寸覆盖 |
198
+ | `onBeforeOpenProject` | `(projectPath: string) => void \| Promise<void>` | — | 打开项目前、任何副作用(旧会话拆除 / 编译 / dev-server)之前运行的声明式权限门控。抛错即否决:`openProject` 返回 `{ success: false, error }`,当前活动会话保持不动、适配器不被调用,且框架自动把 error 透到状态条(`notify.projectStatus`)。覆盖 IPC / 菜单 / 内部直调三个打开入口 |
199
+ | `window` | `WorkbenchWindowConfig`(`width`/`height`/`autoShow?`) | — | 主窗口尺寸覆盖;`autoShow`(默认 `true`)控制 `ready-to-show` 是否自动显示窗口。`autoShow: false` 时窗口创建即隐藏(test / 非 test 一致),由 host 在登录通过后自行 `instance.mainWindow.show()` |
199
200
  | `updateChecker` | `UpdateChecker` | — | 自定义更新检查器;提供后启用"检查更新"功能 |
200
201
  | `updateOptions` | `{ checkInterval?, initialDelay?, getCurrentVersion? }` | — | 仅当 `updateChecker` 提供时生效,默认 1h / 5s |
201
202
 
@@ -221,11 +222,11 @@ src/
221
222
 
222
223
  ## Embedding & Extending the Project Panel
223
224
 
224
- 宿主(如 qdmp)通过 `launch({...})` 嵌入 devtools 时,可对项目面板做三个正交扩展:
225
+ 下游宿主通过 `launch({...})` 嵌入 devtools 时,可对项目面板做三个正交扩展:
225
226
 
226
227
  | 扩展点 | 用途 |
227
228
  | --- | --- |
228
- | `projectsProvider` | 接管项目列表存储 —— 替换默认 `<userData>/dimina-projects.json`,对接 qdmp 后台 / IDE workspace / 远端工程库 |
229
+ | `projectsProvider` | 接管项目列表存储 —— 替换默认 `<userData>/dimina-projects.json`,对接下游宿主后台 / IDE workspace / 远端工程库 |
229
230
  | `projectTemplates` + `builtinTemplates` | 注入/覆盖/裁剪"新建项目"模板列表(同 id 覆盖内置;`builtinTemplates` 控制内置策略) |
230
231
  | `customCreateProjectDialog` | 用宿主自家页面替换内置"新建项目"对话框(main 进程 hook,可 `new BrowserWindow` 加载任意 URL,通过 IPC/postMessage 回传结果) |
231
232
 
@@ -233,8 +234,8 @@ src/
233
234
 
234
235
  | 场景 | 需要的扩展点 |
235
236
  | --- | --- |
236
- | 项目列表来自 qdmp 后台 / 团队工程库 | `projectsProvider` |
237
- | 仅替换可选模板(e.g. 只提供 taro / qdmp 自家脚手架) | `projectTemplates` + `builtinTemplates` |
237
+ | 项目列表来自下游宿主后台 / 团队工程库 | `projectsProvider` |
238
+ | 仅替换可选模板(e.g. 只提供 taro / 宿主自家脚手架) | `projectTemplates` + `builtinTemplates` |
238
239
  | 创建项目流程要走宿主自己的 wizard / 登录态 | `customCreateProjectDialog` |
239
240
 
240
241
  ### 最小示例
@@ -246,51 +247,51 @@ import { BrowserWindow } from 'electron'
246
247
 
247
248
  const provider: ProjectsProvider = {
248
249
  async listProjects() {
249
- return await qdmp.api.listProjects()
250
+ return await host.api.listProjects()
250
251
  },
251
252
  async validateProjectDir(dirPath) {
252
- return (await qdmp.api.isMiniApp(dirPath)) ? null : '不是合法的小程序工程'
253
+ return (await host.api.isMiniApp(dirPath)) ? null : '不是合法的小程序工程'
253
254
  },
254
255
  async addProject(dirPath) {
255
- return await qdmp.api.addProject(dirPath)
256
+ return await host.api.addProject(dirPath)
256
257
  },
257
258
  async removeProject(dirPath) {
258
- await qdmp.api.removeProject(dirPath)
259
+ await host.api.removeProject(dirPath)
259
260
  },
260
261
  async updateLastOpened(dirPath) {
261
- await qdmp.api.touchLastOpened(dirPath)
262
+ await host.api.touchLastOpened(dirPath)
262
263
  },
263
264
  async getCompileConfig(dirPath) {
264
- return await qdmp.api.getCompileConfig(dirPath)
265
+ return await host.api.getCompileConfig(dirPath)
265
266
  },
266
267
  async saveCompileConfig(dirPath, cfg) {
267
- await qdmp.api.saveCompileConfig(dirPath, cfg)
268
+ await host.api.saveCompileConfig(dirPath, cfg)
268
269
  },
269
270
  // 缩略图走云端存储——dataUrl 形如 'data:image/png;base64,...'
270
271
  async saveThumbnail(dirPath, imageDataUrl) {
271
- await qdmp.api.uploadThumbnail(dirPath, imageDataUrl)
272
+ await host.api.uploadThumbnail(dirPath, imageDataUrl)
272
273
  },
273
274
  async getThumbnail(dirPath) {
274
- return await qdmp.api.fetchThumbnail(dirPath)
275
+ return await host.api.fetchThumbnail(dirPath)
275
276
  },
276
277
  }
277
278
 
278
- const qdmpBlank: ProjectTemplate = {
279
+ const hostBlank: ProjectTemplate = {
279
280
  id: 'blank', // 同 id 覆盖内置 blank
280
- name: 'qdmp 空白工程',
281
- description: '由 qdmp 维护的官方空白模板',
281
+ name: '宿主空白工程',
282
+ description: '由下游宿主维护的官方空白模板',
282
283
  source: { type: 'directory', path: '/abs/path/to/template' },
283
284
  }
284
285
 
285
286
  launch({
286
287
  projectsProvider: provider,
287
- projectTemplates: [qdmpBlank],
288
+ projectTemplates: [hostBlank],
288
289
  builtinTemplates: ['taro-todo'], // 只保留 taro-todo,干掉默认 blank
289
290
  customCreateProjectDialog: async ({ parentWindow }) => {
290
291
  const win = new BrowserWindow({ parent: parentWindow, modal: true, width: 720, height: 520 })
291
- await win.loadURL('https://qdmp.example.com/devtools/new-project')
292
+ await win.loadURL('https://host.example.com/devtools/new-project')
292
293
  return await new Promise((resolve) => {
293
- win.webContents.ipc.once('qdmp:create-project:done', (_e, payload) => {
294
+ win.webContents.ipc.once('host:create-project:done', (_e, payload) => {
294
295
  win.close()
295
296
  // payload 三选一(CustomCreateProjectDialogResult):
296
297
  // null → 用户取消
@@ -323,7 +324,7 @@ devtools 自带两个 `source`-style 模板,位于 `packages/devtools/template
323
324
  - `blank` — 最小空白小程序骨架(pages/index + app.js/json/wxss + project.config.json,约 8 个文件)
324
325
  - `taro-todo` — 复制自 `dimina/fe/example/taro-todo`,作为 Taro 编译产物的端到端演示工程
325
326
 
326
- `builtinTemplates: 'none'` 排除全部内置;`builtinTemplates: ['taro-todo']` 仅保留指定 id;`projectTemplates` 同 id 注入会覆盖(例如上文 `qdmpBlank` 覆盖了内置 `blank`)。
327
+ `builtinTemplates: 'none'` 排除全部内置;`builtinTemplates: ['taro-todo']` 仅保留指定 id;`projectTemplates` 同 id 注入会覆盖(例如上文 `hostBlank` 覆盖了内置 `blank`)。
327
328
 
328
329
  ### Provider 部分实现 / fallback 行为
329
330
 
@@ -547,7 +548,7 @@ instance.context.views.hostToolbar.send('host:cmd', { theme: 'dark' }) // false
547
548
 
548
549
  ## MiniappRuntime(宿主集成推荐契约)
549
550
 
550
- 下游宿主(如 qdmp)集成时,推荐依赖手写的稳定契约 `MiniappRuntime`,而不是整个 `WorkbenchContext`——后者携带全部内部 service 类型,内部重构会直接破坏下游编译。`asMiniappRuntime(ctx)` 恒等返回(typed view,非拷贝),宿主对 `runtime.workspace.openProject` 的 monkey-patch 会落在真实对象上:
551
+ 下游宿主集成时,推荐依赖手写的稳定契约 `MiniappRuntime`,而不是整个 `WorkbenchContext`——后者携带全部内部 service 类型,内部重构会直接破坏下游编译。`asMiniappRuntime(ctx)` 恒等返回(typed view,非拷贝),宿主对 `runtime.workspace.openProject` 的 monkey-patch 会落在真实对象上:
551
552
 
552
553
  ```typescript
553
554
  import { asMiniappRuntime, type MiniappRuntime } from '@dimina-kit/devtools'
@@ -669,9 +670,13 @@ pnpm build:preload # 仅构建 preload TypeScript
669
670
  pnpm build:renderer # Vite 构建渲染层
670
671
  pnpm dev # Watch 模式 + Electron
671
672
  pnpm check-types # 类型检查
672
- pnpm test # 运行测试
673
+ pnpm test # 运行单元测试(vitest)
674
+ pnpm test:coverage # 单元测试 + 覆盖率报告(终端表格 + coverage/index.html)
675
+ pnpm test:e2e # 运行 Playwright e2e 测试
673
676
  ```
674
677
 
678
+ > 覆盖率:`pnpm test:coverage`(单包)或仓库根 `pnpm test:coverage`(全部包),报告输出到各包 `coverage/`,浏览器打开 `coverage/index.html` 看逐行高亮。
679
+
675
680
  ---
676
681
 
677
682
  ## 安全说明
@@ -679,7 +684,7 @@ pnpm test # 运行测试
679
684
  本工具仅用于本地开发调试。当前的安全配置:
680
685
 
681
686
  - **Workbench 窗口**(main / settings / popover overlay)均启用 `contextIsolation` 并禁用 `nodeIntegration`;renderer 通过 main preload 经 `contextBridge` 拿到 `window.devtools.ipc`,每个 IPC handler 经 sender whitelist 校验调用方
682
- - **Simulator 本体** 是主进程顶层 `WebContentsView`(`nativeSimulatorView`,`partition='persist:simulator'`、`nodeIntegration=false`),其 preload 由 `webPreferences.preload = cjsSiblingPreloadPath(ctx.preloadPath)` 在创建时(`attachNativeSimulator`)固定;session 层的 `configureSimulatorSession()` 只设 Referer/CORS 头,不注册 frame preload。**小程序的每页代码** 运行在该 WCV 内部 DeviceShell 托管的 render-host `<webview>` guest 里(同 `persist:simulator` partition、`nodeIntegration=false`),承载逐页沙箱;guest contextIsolation/sandbox 由主进程 `will-attach-webview` 钉死,渲染进程无法替换
687
+ - **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,渲染进程无法替换
683
688
  - **renderer 入口** 启用了保守 CSP;`will-navigate` / `setWindowOpenHandler` 屏蔽外部跳转
684
689
 
685
690
  仍然不要在 devtools 窗口中加载不受信任的远程内容;本工具不打算在非本地环境部署。
@@ -240,6 +240,20 @@ function wireAppWindowEvents(config, instance) {
240
240
  // project is open.
241
241
  if (isAppQuitting())
242
242
  return;
243
+ // A close arriving while a project teardown is already in flight (the
244
+ // user rapid-double-clicked the close button) MUST keep the window open.
245
+ // This guard runs BEFORE hasActiveSession() on purpose: `closeProject()`
246
+ // → `disposeSession()` synchronously nulls the active session before it
247
+ // finishes awaiting `session.close()`, so by the time the second close
248
+ // arrives `hasActiveSession()` is already false. With the old guard order
249
+ // that second close fell straight through the hasActiveSession() check
250
+ // WITHOUT `preventDefault()`, so Electron destroyed the last window →
251
+ // `window-all-closed` → `app.quit()`, quitting the whole app on a
252
+ // double-click. Swallow the re-entrant close and keep the window.
253
+ if (closing) {
254
+ e.preventDefault();
255
+ return;
256
+ }
243
257
  if (!context.workspace.hasActiveSession())
244
258
  return;
245
259
  // Close button while a project session is open: stay in the workbench
@@ -249,8 +263,6 @@ function wireAppWindowEvents(config, instance) {
249
263
  // unable to invoke anything, so subsequent clicks on Import/etc. would
250
264
  // raise `No handler registered for ...`.
251
265
  e.preventDefault();
252
- if (closing)
253
- return;
254
266
  closing = true;
255
267
  try {
256
268
  if (config.onBeforeClose) {