@dimina-kit/devtools 0.3.1 → 0.3.2-dev.20260522085907

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 (190) hide show
  1. package/README.md +297 -133
  2. package/dist/main/api.d.ts +6 -2
  3. package/dist/main/api.js +5 -2
  4. package/dist/main/app/app.d.ts +1 -1
  5. package/dist/main/app/app.js +84 -36
  6. package/dist/main/app/bootstrap.js +2 -1
  7. package/dist/main/app/launch.d.ts +2 -2
  8. package/dist/main/app/launch.js +10 -8
  9. package/dist/main/index.bundle.js +1567 -477
  10. package/dist/main/ipc/app.d.ts +2 -1
  11. package/dist/main/ipc/app.js +5 -4
  12. package/dist/main/ipc/index.d.ts +5 -4
  13. package/dist/main/ipc/index.js +5 -4
  14. package/dist/main/ipc/panels.d.ts +2 -1
  15. package/dist/main/ipc/panels.js +16 -13
  16. package/dist/main/ipc/popover.d.ts +4 -1
  17. package/dist/main/ipc/popover.js +15 -5
  18. package/dist/main/ipc/projects.d.ts +6 -1
  19. package/dist/main/ipc/projects.js +127 -6
  20. package/dist/main/ipc/session.d.ts +4 -1
  21. package/dist/main/ipc/session.js +34 -6
  22. package/dist/main/ipc/settings.d.ts +4 -1
  23. package/dist/main/ipc/settings.js +51 -23
  24. package/dist/main/ipc/simulator-module.d.ts +8 -0
  25. package/dist/main/ipc/simulator-module.js +19 -0
  26. package/dist/main/ipc/simulator.d.ts +2 -1
  27. package/dist/main/ipc/simulator.js +23 -18
  28. package/dist/main/ipc/toolbar.d.ts +2 -1
  29. package/dist/main/ipc/toolbar.js +3 -2
  30. package/dist/main/menu/index.js +11 -0
  31. package/dist/main/services/automation/exec.d.ts +2 -0
  32. package/dist/main/services/automation/exec.js +7 -9
  33. package/dist/main/services/automation/handlers/app.js +37 -51
  34. package/dist/main/services/automation/handlers/element.js +27 -41
  35. package/dist/main/services/automation/index.js +88 -13
  36. package/dist/main/services/default-adapter.js +3 -3
  37. package/dist/main/services/mcp/index.d.ts +1 -0
  38. package/dist/main/services/mcp/index.js +1 -0
  39. package/dist/main/services/mcp/server.d.ts +2 -1
  40. package/dist/main/services/mcp/server.js +10 -0
  41. package/dist/main/services/mcp/status.d.ts +10 -0
  42. package/dist/main/services/mcp/status.js +14 -0
  43. package/dist/main/services/mcp/target-manager.js +2 -1
  44. package/dist/main/services/module.d.ts +18 -0
  45. package/dist/main/services/module.js +2 -0
  46. package/dist/main/services/notifications/renderer-notifier.d.ts +5 -3
  47. package/dist/main/services/notifications/renderer-notifier.js +3 -6
  48. package/dist/main/services/projects/builtin-templates.d.ts +3 -0
  49. package/dist/main/services/projects/builtin-templates.js +24 -0
  50. package/dist/main/services/projects/create-project-service.d.ts +7 -0
  51. package/dist/main/services/projects/create-project-service.js +84 -0
  52. package/dist/main/services/projects/index.d.ts +7 -0
  53. package/dist/main/services/projects/index.js +9 -0
  54. package/dist/main/services/projects/local-provider.d.ts +3 -0
  55. package/dist/main/services/projects/local-provider.js +25 -0
  56. package/dist/main/services/projects/project-repository.d.ts +1 -1
  57. package/dist/main/services/projects/project-repository.js +3 -0
  58. package/dist/main/services/projects/templates.d.ts +21 -0
  59. package/dist/main/services/projects/templates.js +39 -0
  60. package/dist/main/services/projects/thumbnail.d.ts +8 -0
  61. package/dist/main/services/projects/thumbnail.js +37 -0
  62. package/dist/main/services/projects/types.d.ts +118 -0
  63. package/dist/main/services/projects/types.js +19 -0
  64. package/dist/main/services/settings/index.d.ts +11 -0
  65. package/dist/main/services/settings/index.js +18 -2
  66. package/dist/main/services/simulator/custom-apis.d.ts +10 -0
  67. package/dist/main/services/simulator/custom-apis.js +26 -0
  68. package/dist/main/services/simulator-storage/index.d.ts +22 -1
  69. package/dist/main/services/simulator-storage/index.js +334 -97
  70. package/dist/main/services/update/update-manager.d.ts +16 -1
  71. package/dist/main/services/update/update-manager.js +18 -15
  72. package/dist/main/services/views/view-manager.d.ts +7 -7
  73. package/dist/main/services/views/view-manager.js +53 -29
  74. package/dist/main/services/window-service.d.ts +24 -0
  75. package/dist/main/services/window-service.js +26 -0
  76. package/dist/main/services/workbench-context.d.ts +49 -2
  77. package/dist/main/services/workbench-context.js +15 -2
  78. package/dist/main/services/workspace/workspace-service.d.ts +9 -6
  79. package/dist/main/services/workspace/workspace-service.js +71 -11
  80. package/dist/main/simulator-apis.d.ts +23 -0
  81. package/dist/main/simulator-apis.js +24 -0
  82. package/dist/main/utils/disposable.d.ts +13 -0
  83. package/dist/main/utils/disposable.js +60 -0
  84. package/dist/main/utils/ipc-registry.d.ts +39 -0
  85. package/dist/main/utils/ipc-registry.js +109 -0
  86. package/dist/main/utils/ipc-schema.d.ts +28 -0
  87. package/dist/main/utils/ipc-schema.js +38 -0
  88. package/dist/main/utils/paths.d.ts +6 -0
  89. package/dist/main/utils/paths.js +6 -0
  90. package/dist/main/utils/sender-policy.d.ts +25 -0
  91. package/dist/main/utils/sender-policy.js +44 -0
  92. package/dist/main/utils/theme.d.ts +24 -0
  93. package/dist/main/utils/theme.js +38 -1
  94. package/dist/main/windows/main-window/create.d.ts +6 -0
  95. package/dist/main/windows/main-window/create.js +66 -10
  96. package/dist/main/windows/main-window/events.d.ts +2 -1
  97. package/dist/main/windows/main-window/events.js +25 -2
  98. package/dist/main/windows/navigation-hardening.d.ts +24 -0
  99. package/dist/main/windows/navigation-hardening.js +55 -0
  100. package/dist/main/windows/settings-window/create.js +11 -1
  101. package/dist/preload/index.d.ts +22 -0
  102. package/dist/preload/index.js +19 -0
  103. package/dist/preload/instrumentation/app-data.d.ts +32 -2
  104. package/dist/preload/instrumentation/app-data.js +253 -79
  105. package/dist/preload/instrumentation/index.d.ts +2 -2
  106. package/dist/preload/instrumentation/index.js +2 -2
  107. package/dist/preload/instrumentation/wxml.d.ts +16 -4
  108. package/dist/preload/instrumentation/wxml.js +188 -178
  109. package/dist/preload/miniapp-snapshot/host.d.ts +12 -0
  110. package/dist/preload/miniapp-snapshot/host.js +117 -0
  111. package/dist/preload/miniapp-snapshot/types.d.ts +62 -0
  112. package/dist/preload/miniapp-snapshot/types.js +15 -0
  113. package/dist/preload/runtime/bridge.js +5 -24
  114. package/dist/preload/runtime/custom-apis.d.ts +6 -0
  115. package/dist/preload/runtime/custom-apis.js +43 -0
  116. package/dist/preload/runtime/host.d.ts +1 -0
  117. package/dist/preload/runtime/host.js +13 -0
  118. package/dist/preload/shared/expose.d.ts +15 -0
  119. package/dist/preload/shared/expose.js +33 -0
  120. package/dist/preload/shared/page-iframe.d.ts +3 -0
  121. package/dist/preload/shared/page-iframe.js +6 -0
  122. package/dist/preload/shared/types.d.ts +2 -0
  123. package/dist/preload/windows/main.cjs +47 -0
  124. package/dist/preload/windows/main.cjs.map +7 -0
  125. package/dist/preload/windows/simulator.js +501 -272
  126. package/dist/renderer/assets/constants-DI382mTP.js +2 -0
  127. package/dist/renderer/assets/index-D8VFYBh-.js +46 -0
  128. package/dist/renderer/assets/{input-DxfYnzX_.js → input-CoWcfa8u.js} +2 -2
  129. package/dist/renderer/assets/ipc-transport-Cd2xPrsa.js +9 -0
  130. package/dist/renderer/assets/ipc-transport-DqbRdZNO.css +1 -0
  131. package/dist/renderer/assets/popover-Nq5gT9DO.js +2 -0
  132. package/dist/renderer/assets/select-By5KH-UG.js +2 -0
  133. package/dist/renderer/assets/{settings-DteGraEU.js → settings-Dj_N70qR.js} +2 -2
  134. package/dist/renderer/assets/settings-api-Do0_Smq1.js +2 -0
  135. package/dist/renderer/assets/workbenchSettings-CazSLGYt.js +8 -0
  136. package/dist/renderer/entries/main/index.html +13 -5
  137. package/dist/renderer/entries/popover/index.html +8 -5
  138. package/dist/renderer/entries/settings/index.html +7 -5
  139. package/dist/renderer/entries/workbench-settings/index.html +7 -4
  140. package/dist/shared/ipc-channels.d.ts +46 -13
  141. package/dist/shared/ipc-channels.js +60 -14
  142. package/dist/shared/ipc-schemas.d.ts +118 -0
  143. package/dist/shared/ipc-schemas.js +133 -0
  144. package/dist/shared/simulator-route.d.ts +72 -0
  145. package/dist/shared/simulator-route.js +169 -0
  146. package/dist/shared/types.d.ts +82 -8
  147. package/dist/simulator/assets/simulator-DJCozrU5.js +9 -0
  148. package/dist/simulator/simulator.html +6 -43
  149. package/package.json +18 -29
  150. package/templates/blank/app.js +6 -0
  151. package/templates/blank/app.json +11 -0
  152. package/templates/blank/app.wxss +5 -0
  153. package/templates/blank/pages/index/index.js +6 -0
  154. package/templates/blank/pages/index/index.json +3 -0
  155. package/templates/blank/pages/index/index.wxml +3 -0
  156. package/templates/blank/pages/index/index.wxss +11 -0
  157. package/templates/blank/project.config.json +13 -0
  158. package/templates/blank/sitemap.json +9 -0
  159. package/templates/taro-todo/app.js +1 -0
  160. package/templates/taro-todo/app.json +1 -0
  161. package/templates/taro-todo/app.wxss +1 -0
  162. package/templates/taro-todo/base.wxml +851 -0
  163. package/templates/taro-todo/common.js +1 -0
  164. package/templates/taro-todo/comp.js +1 -0
  165. package/templates/taro-todo/comp.json +1 -0
  166. package/templates/taro-todo/comp.wxml +2 -0
  167. package/templates/taro-todo/custom-wrapper.js +1 -0
  168. package/templates/taro-todo/custom-wrapper.json +1 -0
  169. package/templates/taro-todo/custom-wrapper.wxml +4 -0
  170. package/templates/taro-todo/pages/index/index.js +2 -0
  171. package/templates/taro-todo/pages/index/index.json +1 -0
  172. package/templates/taro-todo/pages/index/index.wxml +2 -0
  173. package/templates/taro-todo/pages/index/index.wxss +1 -0
  174. package/templates/taro-todo/project.config.json +44 -0
  175. package/templates/taro-todo/runtime.js +1 -0
  176. package/templates/taro-todo/taro.js +2 -0
  177. package/templates/taro-todo/utils.wxs +39 -0
  178. package/templates/taro-todo/vendors.js +2 -0
  179. package/dist/preload/instrumentation/disposable.d.ts +0 -11
  180. package/dist/preload/instrumentation/disposable.js +0 -15
  181. package/dist/preload/windows/main.d.ts +0 -6
  182. package/dist/preload/windows/main.js +0 -6
  183. package/dist/renderer/assets/index-D1p6crtf.js +0 -46
  184. package/dist/renderer/assets/ipc-transport-ClIvDcYY.js +0 -9
  185. package/dist/renderer/assets/ipc-transport-EiWxu_hG.css +0 -1
  186. package/dist/renderer/assets/popover-CtXcBJtM.js +0 -2
  187. package/dist/renderer/assets/select-DMfp49f1.js +0 -2
  188. package/dist/renderer/assets/settings-api-CY84UMpN.js +0 -2
  189. package/dist/renderer/assets/workbenchSettings-DjELLnSJ.js +0 -8
  190. package/dist/simulator/assets/simulator-C1eHXnuq.js +0 -9
@@ -1,3 +1,4 @@
1
1
  import type { WorkbenchContext } from '../services/workbench-context.js';
2
- export declare function registerAppIpc(ctx: Pick<WorkbenchContext, 'preloadPath' | 'brandingProvider' | 'appName'>): void;
2
+ import type { Disposable } from '../utils/disposable.js';
3
+ export declare function registerAppIpc(ctx: Pick<WorkbenchContext, 'preloadPath' | 'brandingProvider' | 'appName' | 'senderPolicy'>): Disposable;
3
4
  //# sourceMappingURL=app.d.ts.map
@@ -1,11 +1,12 @@
1
- import { ipcMain } from 'electron';
2
1
  import fs from 'fs';
3
2
  import { AppChannel } from '../../shared/ipc-channels.js';
3
+ import { IpcRegistry } from '../utils/ipc-registry.js';
4
4
  export function registerAppIpc(ctx) {
5
- ipcMain.handle(AppChannel.GetPreloadPath, () => {
5
+ return new IpcRegistry(ctx.senderPolicy)
6
+ .handle(AppChannel.GetPreloadPath, () => {
6
7
  return `file://${fs.realpathSync(ctx.preloadPath)}`;
7
- });
8
- ipcMain.handle(AppChannel.GetBranding, async () => {
8
+ })
9
+ .handle(AppChannel.GetBranding, async () => {
9
10
  if (ctx.brandingProvider)
10
11
  return ctx.brandingProvider();
11
12
  return { appName: ctx.appName };
@@ -1,9 +1,10 @@
1
1
  export { registerAppIpc } from './app.js';
2
2
  export { registerSimulatorIpc } from './simulator.js';
3
3
  export { registerPanelsIpc } from './panels.js';
4
- export { registerPopoverIpc } from './popover.js';
5
- export { registerProjectsIpc } from './projects.js';
6
- export { registerSessionIpc } from './session.js';
7
- export { registerSettingsIpc } from './settings.js';
4
+ export { registerPopoverIpc, popoverModule } from './popover.js';
5
+ export { registerProjectsIpc, projectsModule } from './projects.js';
6
+ export { registerSessionIpc, sessionModule } from './session.js';
7
+ export { registerSettingsIpc, settingsModule } from './settings.js';
8
8
  export { registerToolbarIpc } from './toolbar.js';
9
+ export { simulatorModule } from './simulator-module.js';
9
10
  //# sourceMappingURL=index.d.ts.map
@@ -1,9 +1,10 @@
1
1
  export { registerAppIpc } from './app.js';
2
2
  export { registerSimulatorIpc } from './simulator.js';
3
3
  export { registerPanelsIpc } from './panels.js';
4
- export { registerPopoverIpc } from './popover.js';
5
- export { registerProjectsIpc } from './projects.js';
6
- export { registerSessionIpc } from './session.js';
7
- export { registerSettingsIpc } from './settings.js';
4
+ export { registerPopoverIpc, popoverModule } from './popover.js';
5
+ export { registerProjectsIpc, projectsModule } from './projects.js';
6
+ export { registerSessionIpc, sessionModule } from './session.js';
7
+ export { registerSettingsIpc, settingsModule } from './settings.js';
8
8
  export { registerToolbarIpc } from './toolbar.js';
9
+ export { simulatorModule } from './simulator-module.js';
9
10
  //# sourceMappingURL=index.js.map
@@ -1,3 +1,4 @@
1
1
  import type { WorkbenchContext } from '../services/workbench-context.js';
2
- export declare function registerPanelsIpc(ctx: Pick<WorkbenchContext, 'panels' | 'views' | 'workspace'>): void;
2
+ import type { Disposable } from '../utils/disposable.js';
3
+ export declare function registerPanelsIpc(ctx: Pick<WorkbenchContext, 'panels' | 'views' | 'workspace' | 'senderPolicy'>): Disposable;
3
4
  //# sourceMappingURL=panels.d.ts.map
@@ -1,21 +1,24 @@
1
- import { ipcMain, webContents } from 'electron';
2
1
  import { PanelChannel } from '../../shared/ipc-channels.js';
2
+ import { PanelEvalSchema, PanelSelectSchema, } from '../../shared/ipc-schemas.js';
3
+ import { validate } from '../utils/ipc-schema.js';
4
+ import { IpcRegistry } from '../utils/ipc-registry.js';
3
5
  const BUILTIN_PANELS = [
4
6
  { id: 'wxml', label: 'WXML' },
5
7
  { id: 'appdata', label: 'AppData' },
6
8
  { id: 'storage', label: 'Storage' },
7
9
  ];
8
10
  export function registerPanelsIpc(ctx) {
9
- ipcMain.handle(PanelChannel.List, () => {
11
+ return new IpcRegistry(ctx.senderPolicy)
12
+ .handle(PanelChannel.List, () => {
10
13
  return BUILTIN_PANELS
11
14
  .filter((p) => ctx.panels.includes(p.id));
12
- });
13
- ipcMain.handle(PanelChannel.Eval, async (_, expression) => {
14
- const simWcId = ctx.views.getSimulatorWebContentsId();
15
- if (!ctx.workspace.hasActiveSession() || simWcId == null)
15
+ })
16
+ .handle(PanelChannel.Eval, async (_event, ...args) => {
17
+ const [expression] = validate(PanelChannel.Eval, PanelEvalSchema, args);
18
+ if (!ctx.workspace.hasActiveSession())
16
19
  return undefined;
17
- const sim = webContents.fromId(simWcId);
18
- if (!sim || sim.isDestroyed())
20
+ const sim = ctx.views.getSimulatorWebContents();
21
+ if (!sim)
19
22
  return undefined;
20
23
  try {
21
24
  return await sim.executeJavaScript(expression);
@@ -23,14 +26,14 @@ export function registerPanelsIpc(ctx) {
23
26
  catch {
24
27
  return undefined;
25
28
  }
26
- });
27
- ipcMain.handle(PanelChannel.Select, (_event, _panelId) => {
28
- // Hide the simulator overlay when switching to a built-in panel
29
+ })
30
+ .handle(PanelChannel.Select, (_event, ...args) => {
31
+ validate(PanelChannel.Select, PanelSelectSchema, args);
29
32
  if (ctx.views.hasSimulatorView() && ctx.views.isSimulatorAdded()) {
30
33
  ctx.views.hideSimulator();
31
34
  }
32
- });
33
- ipcMain.handle(PanelChannel.SelectSimulator, () => {
35
+ })
36
+ .handle(PanelChannel.SelectSimulator, () => {
34
37
  if (ctx.views.hasSimulatorView() && !ctx.views.isSimulatorAdded()) {
35
38
  ctx.views.showSimulator(ctx.views.getLastSimWidth());
36
39
  }
@@ -1,3 +1,6 @@
1
1
  import type { WorkbenchContext } from '../services/workbench-context.js';
2
- export declare function registerPopoverIpc(ctx: Pick<WorkbenchContext, 'views' | 'notify'>): void;
2
+ import type { WorkbenchModule } from '../services/module.js';
3
+ import type { Disposable } from '../utils/disposable.js';
4
+ export declare function registerPopoverIpc(ctx: Pick<WorkbenchContext, 'views' | 'notify' | 'senderPolicy'>): Disposable;
5
+ export declare const popoverModule: WorkbenchModule;
3
6
  //# sourceMappingURL=popover.d.ts.map
@@ -1,13 +1,23 @@
1
- import { ipcMain } from 'electron';
2
1
  import { PopoverChannel } from '../../shared/ipc-channels.js';
2
+ import { PopoverShowSchema, PopoverRelaunchSchema, } from '../../shared/ipc-schemas.js';
3
+ import { validate } from '../utils/ipc-schema.js';
4
+ import { IpcRegistry } from '../utils/ipc-registry.js';
3
5
  export function registerPopoverIpc(ctx) {
4
- ipcMain.handle(PopoverChannel.Show, (_event, data) => {
6
+ return new IpcRegistry(ctx.senderPolicy)
7
+ .handle(PopoverChannel.Show, (_event, ...args) => {
8
+ const [data] = validate(PopoverChannel.Show, PopoverShowSchema, args);
5
9
  ctx.views.showPopover(data);
6
- });
7
- ipcMain.handle(PopoverChannel.Hide, () => ctx.views.hidePopover());
8
- ipcMain.on(PopoverChannel.Relaunch, (_, newConfig) => {
10
+ })
11
+ .handle(PopoverChannel.Hide, () => {
12
+ ctx.views.hidePopover();
13
+ })
14
+ .on(PopoverChannel.Relaunch, (_event, ...args) => {
15
+ const [newConfig] = validate(PopoverChannel.Relaunch, PopoverRelaunchSchema, args);
9
16
  ctx.views.hidePopover();
10
17
  ctx.notify.popoverRelaunch(newConfig);
11
18
  });
12
19
  }
20
+ export const popoverModule = {
21
+ setup: (ctx) => registerPopoverIpc(ctx),
22
+ };
13
23
  //# sourceMappingURL=popover.js.map
@@ -1,3 +1,8 @@
1
1
  import type { WorkbenchContext } from '../services/workbench-context.js';
2
- export declare function registerProjectsIpc(ctx: Pick<WorkbenchContext, 'workspace' | 'mainWindow'>): void;
2
+ import type { WorkbenchModule } from '../services/module.js';
3
+ import type { Disposable } from '../utils/disposable.js';
4
+ type ProjectsIpcCtx = Pick<WorkbenchContext, 'workspace' | 'windows' | 'senderPolicy' | 'projectsProvider' | 'projectTemplates' | 'customCreateProjectDialog'>;
5
+ export declare function registerProjectsIpc(ctx: ProjectsIpcCtx): Disposable;
6
+ export declare const projectsModule: WorkbenchModule;
7
+ export {};
3
8
  //# sourceMappingURL=projects.d.ts.map
@@ -1,15 +1,136 @@
1
- import { ipcMain, dialog } from 'electron';
1
+ import { app, dialog } from 'electron';
2
+ import path from 'path';
2
3
  import { ProjectsChannel, DialogChannel } from '../../shared/ipc-channels.js';
4
+ import { ProjectsAddSchema, ProjectsRemoveSchema, } from '../../shared/ipc-schemas.js';
5
+ import { validate } from '../utils/ipc-schema.js';
6
+ import { IpcRegistry } from '../utils/ipc-registry.js';
7
+ import { sanitizeTemplates } from '../services/projects/templates.js';
8
+ import { createProject } from '../services/projects/create-project-service.js';
9
+ import { loadWorkbenchSettings, saveWorkbenchSettings, } from '../services/settings/index.js';
3
10
  export function registerProjectsIpc(ctx) {
4
- ipcMain.handle(ProjectsChannel.List, () => ctx.workspace.listProjects());
5
- ipcMain.handle(DialogChannel.OpenDirectory, async () => {
6
- const result = await dialog.showOpenDialog(ctx.mainWindow, {
11
+ return new IpcRegistry(ctx.senderPolicy)
12
+ .handle(ProjectsChannel.List, () => {
13
+ return ctx.workspace.listProjects();
14
+ })
15
+ .handle(DialogChannel.OpenDirectory, async () => {
16
+ const result = await dialog.showOpenDialog(ctx.windows.mainWindow, {
7
17
  properties: ['openDirectory'],
8
18
  title: '选择小程序项目目录',
9
19
  });
10
20
  return result.canceled ? null : result.filePaths[0];
21
+ })
22
+ .handle(ProjectsChannel.Add, async (_event, ...args) => {
23
+ const [dirPath] = validate(ProjectsChannel.Add, ProjectsAddSchema, args);
24
+ const dirError = await ctx.workspace.validateProjectDir(dirPath);
25
+ if (dirError) {
26
+ await dialog.showMessageBox(ctx.windows.mainWindow, {
27
+ type: 'error',
28
+ title: '无法导入项目',
29
+ message: '该目录不是有效的小程序项目',
30
+ detail: dirError,
31
+ buttons: ['确定'],
32
+ });
33
+ throw new Error(dirError);
34
+ }
35
+ const duplicate = await ctx.workspace.hasProject(dirPath);
36
+ const project = await ctx.workspace.addProject(dirPath);
37
+ if (duplicate) {
38
+ await dialog.showMessageBox(ctx.windows.mainWindow, {
39
+ type: 'info',
40
+ title: '项目已存在',
41
+ message: '该项目已在列表中',
42
+ detail: dirPath,
43
+ buttons: ['确定'],
44
+ });
45
+ }
46
+ return project;
47
+ })
48
+ .handle(ProjectsChannel.Remove, (_event, ...args) => {
49
+ const [dirPath] = validate(ProjectsChannel.Remove, ProjectsRemoveSchema, args);
50
+ return ctx.workspace.removeProject(dirPath);
51
+ })
52
+ // ── Phase 3: template catalog + create flow ──
53
+ .handle(ProjectsChannel.ListTemplates, () => {
54
+ // Sanitize at the IPC boundary: `generate` is a function and the
55
+ // structured-clone algorithm Electron uses for invoke would otherwise
56
+ // throw "could not be cloned" before the renderer ever sees it.
57
+ return sanitizeTemplates(ctx.projectTemplates ?? []);
58
+ })
59
+ .handle(ProjectsChannel.OpenCreateDialog, async () => {
60
+ if (!ctx.customCreateProjectDialog)
61
+ return null;
62
+ const sanitized = sanitizeTemplates(ctx.projectTemplates ?? []);
63
+ return await ctx.customCreateProjectDialog({
64
+ parentWindow: ctx.windows.mainWindow,
65
+ templates: sanitized,
66
+ });
67
+ })
68
+ .handle(ProjectsChannel.Create, async (_event, ...args) => {
69
+ // We deliberately don't run this through zod yet — the input shape is
70
+ // wide (any template can stash arbitrary `extra` fields) and the
71
+ // service does its own per-field validation. Bound size to keep this
72
+ // a cheap DoS guard.
73
+ const [raw] = args;
74
+ if (!raw || typeof raw !== 'object') {
75
+ throw new Error('projects:create expects a CreateProjectInput object');
76
+ }
77
+ const input = raw;
78
+ let project;
79
+ try {
80
+ project = await createProject(input, {
81
+ templates: ctx.projectTemplates ?? [],
82
+ projectsProvider: ctx.projectsProvider,
83
+ });
84
+ }
85
+ catch (err) {
86
+ // Mirror the Add flow: surface scaffold failures (invalid name,
87
+ // non-empty target dir, missing template, remote backend reject,
88
+ // …) as a native dialog so users see *why* the create silently
89
+ // fizzled. The renderer's catch then just bails out.
90
+ await dialog.showMessageBox(ctx.windows.mainWindow, {
91
+ type: 'error',
92
+ title: '创建项目失败',
93
+ message: '无法创建项目',
94
+ detail: err instanceof Error ? err.message : String(err),
95
+ buttons: ['确定'],
96
+ });
97
+ throw err;
98
+ }
99
+ // Remember the parent of the just-created project so the next open of
100
+ // the create dialog pre-fills its directory under the same workspace.
101
+ try {
102
+ const settings = loadWorkbenchSettings();
103
+ const newBase = path.dirname(input.path);
104
+ if (newBase && settings.lastCreateBaseDir !== newBase) {
105
+ saveWorkbenchSettings({ ...settings, lastCreateBaseDir: newBase });
106
+ }
107
+ }
108
+ catch (err) {
109
+ console.warn('[projects:create] failed to persist lastCreateBaseDir', err);
110
+ }
111
+ return project;
112
+ })
113
+ .handle(ProjectsChannel.GetCreateDefaults, () => {
114
+ // Fallback chain: persisted last parent → user's Documents → home.
115
+ // Documents covers the common "我把项目都放在 Documents/ 下" case for
116
+ // first-time users on macOS; home is just a safe final fallback.
117
+ const settings = loadWorkbenchSettings();
118
+ const baseDir = settings.lastCreateBaseDir ??
119
+ safeAppPath('documents') ??
120
+ safeAppPath('home') ??
121
+ '';
122
+ return { baseDir };
11
123
  });
12
- ipcMain.handle(ProjectsChannel.Add, (_, dirPath) => ctx.workspace.addProject(dirPath));
13
- ipcMain.handle(ProjectsChannel.Remove, (_, dirPath) => ctx.workspace.removeProject(dirPath));
14
124
  }
125
+ function safeAppPath(name) {
126
+ try {
127
+ return app.getPath(name);
128
+ }
129
+ catch {
130
+ return null;
131
+ }
132
+ }
133
+ export const projectsModule = {
134
+ setup: (ctx) => registerProjectsIpc(ctx),
135
+ };
15
136
  //# sourceMappingURL=projects.js.map
@@ -1,3 +1,6 @@
1
1
  import type { WorkbenchContext } from '../services/workbench-context.js';
2
- export declare function registerSessionIpc(ctx: Pick<WorkbenchContext, 'workspace'>): void;
2
+ import type { WorkbenchModule } from '../services/module.js';
3
+ import type { Disposable } from '../utils/disposable.js';
4
+ export declare function registerSessionIpc(ctx: Pick<WorkbenchContext, 'workspace' | 'senderPolicy'>): Disposable;
5
+ export declare const sessionModule: WorkbenchModule;
3
6
  //# sourceMappingURL=session.d.ts.map
@@ -1,10 +1,38 @@
1
- import { ipcMain } from 'electron';
2
1
  import { ProjectChannel } from '../../shared/ipc-channels.js';
2
+ import { ProjectCaptureThumbnailSchema, ProjectGetCompileConfigSchema, ProjectGetPagesSchema, ProjectGetThumbnailSchema, ProjectOpenSchema, ProjectSaveCompileConfigSchema, } from '../../shared/ipc-schemas.js';
3
+ import { validate } from '../utils/ipc-schema.js';
4
+ import { IpcRegistry } from '../utils/ipc-registry.js';
3
5
  export function registerSessionIpc(ctx) {
4
- ipcMain.handle(ProjectChannel.Open, (_, projectPath) => ctx.workspace.openProject(projectPath));
5
- ipcMain.handle(ProjectChannel.GetPages, (_, projectPath) => ctx.workspace.getProjectPages(projectPath));
6
- ipcMain.handle(ProjectChannel.GetCompileConfig, (_, projectPath) => ctx.workspace.getCompileConfig(projectPath));
7
- ipcMain.handle(ProjectChannel.SaveCompileConfig, (_, projectPath, config) => ctx.workspace.saveCompileConfig(projectPath, config));
8
- ipcMain.handle(ProjectChannel.Close, () => ctx.workspace.closeProject());
6
+ return new IpcRegistry(ctx.senderPolicy)
7
+ .handle(ProjectChannel.Open, (_, ...args) => {
8
+ const [projectPath] = validate(ProjectChannel.Open, ProjectOpenSchema, args);
9
+ return ctx.workspace.openProject(projectPath);
10
+ })
11
+ .handle(ProjectChannel.GetPages, (_, ...args) => {
12
+ const [projectPath] = validate(ProjectChannel.GetPages, ProjectGetPagesSchema, args);
13
+ return ctx.workspace.getProjectPages(projectPath);
14
+ })
15
+ .handle(ProjectChannel.GetCompileConfig, (_, ...args) => {
16
+ const [projectPath] = validate(ProjectChannel.GetCompileConfig, ProjectGetCompileConfigSchema, args);
17
+ return ctx.workspace.getCompileConfig(projectPath);
18
+ })
19
+ .handle(ProjectChannel.SaveCompileConfig, (_, ...args) => {
20
+ const [projectPath, config] = validate(ProjectChannel.SaveCompileConfig, ProjectSaveCompileConfigSchema, args);
21
+ return ctx.workspace.saveCompileConfig(projectPath, config);
22
+ })
23
+ .handle(ProjectChannel.Close, () => {
24
+ return ctx.workspace.closeProject();
25
+ })
26
+ .handle(ProjectChannel.CaptureThumbnail, (_, ...args) => {
27
+ const [projectPath] = validate(ProjectChannel.CaptureThumbnail, ProjectCaptureThumbnailSchema, args);
28
+ return ctx.workspace.captureThumbnail(projectPath);
29
+ })
30
+ .handle(ProjectChannel.GetThumbnail, (_, ...args) => {
31
+ const [projectPath] = validate(ProjectChannel.GetThumbnail, ProjectGetThumbnailSchema, args);
32
+ return ctx.workspace.getThumbnail(projectPath);
33
+ });
9
34
  }
35
+ export const sessionModule = {
36
+ setup: (ctx) => registerSessionIpc(ctx),
37
+ };
10
38
  //# sourceMappingURL=session.js.map
@@ -1,3 +1,6 @@
1
1
  import type { WorkbenchContext } from '../services/workbench-context.js';
2
- export declare function registerSettingsIpc(ctx: Pick<WorkbenchContext, 'workbenchSettingsWindow' | 'views' | 'notify' | 'workspace' | 'mainWindow' | 'rendererDir'>): void;
2
+ import type { Disposable } from '../utils/disposable.js';
3
+ import type { WorkbenchModule } from '../services/module.js';
4
+ export declare function registerSettingsIpc(ctx: Pick<WorkbenchContext, 'views' | 'notify' | 'workspace' | 'rendererDir' | 'senderPolicy' | 'windows'>): Disposable;
5
+ export declare const settingsModule: WorkbenchModule;
3
6
  //# sourceMappingURL=settings.d.ts.map
@@ -1,23 +1,31 @@
1
- import { app, ipcMain } from 'electron';
1
+ import { app } from 'electron';
2
2
  import { openSettingsWindow } from '../app/launch.js';
3
3
  import { loadWorkbenchSettings, saveWorkbenchSettings, applyTheme, } from '../services/settings/index.js';
4
4
  import { WorkbenchSettingsChannel, SettingsChannel } from '../../shared/ipc-channels.js';
5
+ import { DEFAULT_CDP_PORT } from '../../shared/constants.js';
6
+ import { getMcpStatus } from '../services/mcp/status.js';
7
+ import { SettingsConfigChangedSchema, SettingsProjectSettingsChangedSchema, SettingsSetVisibleSchema, WorkbenchSettingsSaveSchema, WorkbenchSettingsSetThemeSchema, WorkbenchSettingsSetVisibleSchema, } from '../../shared/ipc-schemas.js';
8
+ import { validate } from '../utils/ipc-schema.js';
9
+ import { IpcRegistry } from '../utils/ipc-registry.js';
5
10
  export function registerSettingsIpc(ctx) {
6
- ipcMain.handle(WorkbenchSettingsChannel.Get, () => {
11
+ return new IpcRegistry(ctx.senderPolicy)
12
+ .handle(WorkbenchSettingsChannel.Get, () => {
7
13
  return loadWorkbenchSettings();
8
- });
9
- ipcMain.handle(WorkbenchSettingsChannel.Save, (_, settings) => {
14
+ })
15
+ .handle(WorkbenchSettingsChannel.Save, (_, ...args) => {
16
+ const [settings] = validate(WorkbenchSettingsChannel.Save, WorkbenchSettingsSaveSchema, args);
10
17
  saveWorkbenchSettings(settings);
11
18
  applyTheme(settings.theme ?? 'system');
12
19
  return { success: true };
13
- });
14
- ipcMain.handle(WorkbenchSettingsChannel.SetTheme, (_, theme) => {
20
+ })
21
+ .handle(WorkbenchSettingsChannel.SetTheme, (_, ...args) => {
22
+ const [theme] = validate(WorkbenchSettingsChannel.SetTheme, WorkbenchSettingsSetThemeSchema, args);
15
23
  applyTheme(theme);
16
- });
17
- ipcMain.handle(WorkbenchSettingsChannel.GetCdpStatus, () => {
24
+ })
25
+ .handle(WorkbenchSettingsChannel.GetCdpStatus, () => {
18
26
  const settings = loadWorkbenchSettings();
19
27
  const switchValue = app.commandLine.getSwitchValue('remote-debugging-port');
20
- const implicitDevDefault = !app.isPackaged && !settings.cdp.enabled && switchValue === '9222';
28
+ const implicitDevDefault = !app.isPackaged && !settings.cdp.enabled && switchValue === String(DEFAULT_CDP_PORT);
21
29
  return {
22
30
  configured: settings.cdp.enabled,
23
31
  port: settings.cdp.port,
@@ -25,37 +33,57 @@ export function registerSettingsIpc(ctx) {
25
33
  activePort: switchValue ? parseInt(switchValue, 10) : null,
26
34
  implicitDevDefault,
27
35
  };
28
- });
29
- ipcMain.handle(WorkbenchSettingsChannel.SetVisible, async (_, visible) => {
36
+ })
37
+ .handle(WorkbenchSettingsChannel.GetMcpStatus, () => {
38
+ const settings = loadWorkbenchSettings();
39
+ const runtime = getMcpStatus();
40
+ return {
41
+ configured: settings.mcp.enabled,
42
+ configuredPort: settings.mcp.port,
43
+ running: runtime.running,
44
+ activePort: runtime.port,
45
+ error: runtime.error,
46
+ };
47
+ })
48
+ .handle(WorkbenchSettingsChannel.SetVisible, async (_, ...args) => {
49
+ const [visible] = validate(WorkbenchSettingsChannel.SetVisible, WorkbenchSettingsSetVisibleSchema, args);
30
50
  if (visible) {
31
51
  await openSettingsWindow(ctx);
32
52
  }
33
53
  else {
34
- ctx.workbenchSettingsWindow?.close();
54
+ ctx.windows.closeSettingsWindow();
35
55
  }
36
- });
37
- ipcMain.handle(SettingsChannel.SetVisible, async (_, visible) => {
56
+ })
57
+ .handle(SettingsChannel.SetVisible, async (_, ...args) => {
58
+ const [visible] = validate(SettingsChannel.SetVisible, SettingsSetVisibleSchema, args);
38
59
  if (visible) {
39
60
  await ctx.views.showSettings();
61
+ const projectPath = ctx.workspace.getProjectPath();
40
62
  ctx.notify.settingsInit({
41
- projectPath: ctx.workspace.getProjectPath(),
42
- config: ctx.workspace.getCompileConfig(ctx.workspace.getProjectPath()),
43
- projectSettings: ctx.workspace.getProjectSettings(ctx.workspace.getProjectPath()),
63
+ projectPath,
64
+ config: await ctx.workspace.getCompileConfig(projectPath),
65
+ projectSettings: ctx.workspace.getProjectSettings(projectPath),
44
66
  });
45
67
  }
46
68
  else {
47
69
  ctx.views.hideSettings();
48
70
  ctx.notify.settingsClosed();
49
71
  }
50
- });
51
- ipcMain.on(SettingsChannel.ConfigChanged, (_, config) => {
52
- if (ctx.workspace.getProjectPath()) {
53
- ctx.workspace.saveCompileConfig(ctx.workspace.getProjectPath(), config);
72
+ })
73
+ .on(SettingsChannel.ConfigChanged, async (_, ...args) => {
74
+ const [config] = validate(SettingsChannel.ConfigChanged, SettingsConfigChangedSchema, args);
75
+ const projectPath = ctx.workspace.getProjectPath();
76
+ if (projectPath) {
77
+ await ctx.workspace.saveCompileConfig(projectPath, config);
54
78
  }
55
79
  ctx.notify.settingsChanged(config);
56
- });
57
- ipcMain.on(SettingsChannel.ProjectSettingsChanged, (_, patch) => {
80
+ })
81
+ .on(SettingsChannel.ProjectSettingsChanged, (_, ...args) => {
82
+ const [patch] = validate(SettingsChannel.ProjectSettingsChanged, SettingsProjectSettingsChangedSchema, args);
58
83
  ctx.workspace.updateProjectSettings(ctx.workspace.getProjectPath(), patch);
59
84
  });
60
85
  }
86
+ export const settingsModule = {
87
+ setup: (ctx) => registerSettingsIpc(ctx),
88
+ };
61
89
  //# sourceMappingURL=settings.js.map
@@ -0,0 +1,8 @@
1
+ import type { WorkbenchModule } from '../services/module.js';
2
+ /**
3
+ * The 'simulator' built-in module fans out into three IPC registrars
4
+ * (core simulator, panels, toolbar). Bundle them under a single module
5
+ * so app.ts only sees one entry per BuiltinModuleId.
6
+ */
7
+ export declare const simulatorModule: WorkbenchModule;
8
+ //# sourceMappingURL=simulator-module.d.ts.map
@@ -0,0 +1,19 @@
1
+ import { DisposableRegistry } from '../utils/disposable.js';
2
+ import { registerSimulatorIpc } from './simulator.js';
3
+ import { registerPanelsIpc } from './panels.js';
4
+ import { registerToolbarIpc } from './toolbar.js';
5
+ /**
6
+ * The 'simulator' built-in module fans out into three IPC registrars
7
+ * (core simulator, panels, toolbar). Bundle them under a single module
8
+ * so app.ts only sees one entry per BuiltinModuleId.
9
+ */
10
+ export const simulatorModule = {
11
+ setup: (ctx) => {
12
+ const reg = new DisposableRegistry();
13
+ reg.add(registerSimulatorIpc(ctx));
14
+ reg.add(registerPanelsIpc(ctx));
15
+ reg.add(registerToolbarIpc(ctx));
16
+ return reg;
17
+ },
18
+ };
19
+ //# sourceMappingURL=simulator-module.js.map
@@ -1,3 +1,4 @@
1
1
  import type { WorkbenchContext } from '../services/workbench-context.js';
2
- export declare function registerSimulatorIpc(ctx: Pick<WorkbenchContext, 'views' | 'panels' | 'apiNamespaces' | 'notify'>): void;
2
+ import type { Disposable } from '../utils/disposable.js';
3
+ export declare function registerSimulatorIpc(ctx: Pick<WorkbenchContext, 'views' | 'notify' | 'senderPolicy'>): Disposable;
3
4
  //# sourceMappingURL=simulator.d.ts.map
@@ -1,26 +1,31 @@
1
- import { ipcMain } from 'electron';
2
- import { SimulatorChannel, WorkbenchChannel } from '../../shared/ipc-channels.js';
1
+ import { SimulatorChannel, SimulatorCustomApiChannel } from '../../shared/ipc-channels.js';
2
+ import { SimulatorAttachSchema, SimulatorCustomApiInvokeSchema, SimulatorResizeSchema, SimulatorSetVisibleSchema, } from '../../shared/ipc-schemas.js';
3
+ import { simulatorApiRegistry } from '../services/simulator/custom-apis.js';
4
+ import { validate } from '../utils/ipc-schema.js';
5
+ import { IpcRegistry } from '../utils/ipc-registry.js';
3
6
  export function registerSimulatorIpc(ctx) {
4
- ipcMain.handle(SimulatorChannel.Attach, (_, simWcId, simWidth) => {
7
+ return new IpcRegistry(ctx.senderPolicy)
8
+ .handle(SimulatorChannel.Attach, (_, ...args) => {
9
+ const [simWcId, simWidth] = validate(SimulatorChannel.Attach, SimulatorAttachSchema, args);
5
10
  ctx.views.attachSimulator(simWcId, simWidth);
6
- });
7
- ipcMain.handle(SimulatorChannel.Detach, () => {
11
+ })
12
+ .handle(SimulatorChannel.Detach, () => {
8
13
  ctx.views.detachSimulator();
9
- });
10
- ipcMain.handle(SimulatorChannel.Resize, (_, simWidth) => {
14
+ })
15
+ .handle(SimulatorChannel.Resize, (_, ...args) => {
16
+ const [simWidth] = validate(SimulatorChannel.Resize, SimulatorResizeSchema, args);
11
17
  ctx.views.resize(simWidth);
12
- });
13
- ipcMain.handle(SimulatorChannel.SetVisible, (_, visible, simWidth) => {
18
+ })
19
+ .handle(SimulatorChannel.SetVisible, (_, ...args) => {
20
+ const [visible, simWidth] = validate(SimulatorChannel.SetVisible, SimulatorSetVisibleSchema, args);
14
21
  ctx.views.setVisible(visible, simWidth);
15
- });
16
- ipcMain.handle(WorkbenchChannel.GetPanelConfig, () => {
17
- return ctx.panels;
18
- });
19
- ipcMain.handle(WorkbenchChannel.GetApiNamespaces, () => {
20
- return ctx.apiNamespaces;
21
- });
22
- ipcMain.on(WorkbenchChannel.Reset, () => {
23
- ctx.notify.workbenchReset();
22
+ })
23
+ .handle(SimulatorCustomApiChannel.List, () => {
24
+ return simulatorApiRegistry.list();
25
+ })
26
+ .handle(SimulatorCustomApiChannel.Invoke, (_, ...args) => {
27
+ const [name, params] = validate(SimulatorCustomApiChannel.Invoke, SimulatorCustomApiInvokeSchema, args);
28
+ return simulatorApiRegistry.invoke(name, params);
24
29
  });
25
30
  }
26
31
  //# sourceMappingURL=simulator.js.map
@@ -1,3 +1,4 @@
1
1
  import type { WorkbenchContext } from '../services/workbench-context.js';
2
- export declare function registerToolbarIpc(ctx: Pick<WorkbenchContext, 'toolbarActions'>): void;
2
+ import type { Disposable } from '../utils/disposable.js';
3
+ export declare function registerToolbarIpc(ctx: Pick<WorkbenchContext, 'toolbarActions' | 'senderPolicy'>): Disposable;
3
4
  //# sourceMappingURL=toolbar.d.ts.map
@@ -1,7 +1,8 @@
1
- import { ipcMain } from 'electron';
2
1
  import { ToolbarChannel } from '../../shared/ipc-channels.js';
2
+ import { IpcRegistry } from '../utils/ipc-registry.js';
3
3
  export function registerToolbarIpc(ctx) {
4
- ipcMain.handle(ToolbarChannel.GetActions, async () => {
4
+ return new IpcRegistry(ctx.senderPolicy)
5
+ .handle(ToolbarChannel.GetActions, async () => {
5
6
  if (ctx.toolbarActions)
6
7
  return ctx.toolbarActions();
7
8
  return [];
@@ -23,6 +23,17 @@ export function installAppMenu(ctx) {
23
23
  { role: 'quit' },
24
24
  ],
25
25
  },
26
+ {
27
+ label: '项目',
28
+ submenu: [
29
+ {
30
+ label: '打开项目',
31
+ click: () => {
32
+ ctx.notify.windowNavigateBack();
33
+ },
34
+ },
35
+ ],
36
+ },
26
37
  {
27
38
  label: '编辑',
28
39
  submenu: [
@@ -4,6 +4,8 @@ export declare function getSimulator(ctx: WorkbenchContext): Electron.WebContent
4
4
  export declare function evalInSim<T = unknown>(ctx: WorkbenchContext, expression: string): Promise<T>;
5
5
  /** Build JS that locates an element by selector+index inside the active page iframe. */
6
6
  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`. */
8
+ export declare function evalInElement<T = unknown>(ctx: WorkbenchContext, ref: ElementRef, body: string, varName?: string): Promise<T>;
7
9
  /** Wrap code to run inside the active page iframe (last iframe in the stack). */
8
10
  export declare function inIframe(code: string): string;
9
11
  //# sourceMappingURL=exec.d.ts.map