@dimina-kit/devtools 0.3.1-dev.20260518114324 → 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 (89) hide show
  1. package/README.md +33 -71
  2. package/dist/main/app/app.js +5 -0
  3. package/dist/main/app/bootstrap.js +2 -1
  4. package/dist/main/app/launch.d.ts +1 -1
  5. package/dist/main/app/launch.js +6 -7
  6. package/dist/main/index.bundle.js +306 -317
  7. package/dist/main/ipc/panels.js +3 -5
  8. package/dist/main/ipc/projects.d.ts +1 -1
  9. package/dist/main/ipc/projects.js +5 -5
  10. package/dist/main/ipc/settings.d.ts +1 -1
  11. package/dist/main/ipc/settings.js +14 -1
  12. package/dist/main/ipc/simulator.d.ts +1 -1
  13. package/dist/main/ipc/simulator.js +1 -10
  14. package/dist/main/services/automation/exec.d.ts +2 -0
  15. package/dist/main/services/automation/exec.js +7 -9
  16. package/dist/main/services/automation/handlers/app.js +37 -51
  17. package/dist/main/services/automation/handlers/element.js +27 -41
  18. package/dist/main/services/default-adapter.js +3 -3
  19. package/dist/main/services/mcp/index.d.ts +1 -0
  20. package/dist/main/services/mcp/index.js +1 -0
  21. package/dist/main/services/mcp/server.js +5 -0
  22. package/dist/main/services/mcp/status.d.ts +10 -0
  23. package/dist/main/services/mcp/status.js +14 -0
  24. package/dist/main/services/mcp/target-manager.js +2 -1
  25. package/dist/main/services/notifications/renderer-notifier.d.ts +3 -3
  26. package/dist/main/services/notifications/renderer-notifier.js +3 -6
  27. package/dist/main/services/projects/local-provider.js +3 -3
  28. package/dist/main/services/projects/project-repository.d.ts +0 -1
  29. package/dist/main/services/settings/index.js +2 -1
  30. package/dist/main/services/simulator-storage/index.js +47 -77
  31. package/dist/main/services/views/view-manager.d.ts +3 -7
  32. package/dist/main/services/views/view-manager.js +20 -27
  33. package/dist/main/services/window-service.d.ts +3 -3
  34. package/dist/main/services/workbench-context.d.ts +3 -6
  35. package/dist/main/services/workbench-context.js +1 -3
  36. package/dist/main/services/workspace/workspace-service.js +8 -24
  37. package/dist/main/utils/theme.d.ts +24 -0
  38. package/dist/main/utils/theme.js +38 -1
  39. package/dist/preload/index.d.ts +5 -2
  40. package/dist/preload/index.js +3 -2
  41. package/dist/preload/instrumentation/app-data.d.ts +32 -2
  42. package/dist/preload/instrumentation/app-data.js +187 -172
  43. package/dist/preload/instrumentation/index.d.ts +2 -2
  44. package/dist/preload/instrumentation/index.js +2 -2
  45. package/dist/preload/instrumentation/wxml.d.ts +16 -4
  46. package/dist/preload/instrumentation/wxml.js +188 -178
  47. package/dist/preload/miniapp-snapshot/host.d.ts +12 -0
  48. package/dist/preload/miniapp-snapshot/host.js +117 -0
  49. package/dist/preload/miniapp-snapshot/types.d.ts +62 -0
  50. package/dist/preload/miniapp-snapshot/types.js +15 -0
  51. package/dist/preload/runtime/bridge.js +5 -24
  52. package/dist/preload/runtime/custom-apis.d.ts +1 -1
  53. package/dist/preload/runtime/custom-apis.js +3 -7
  54. package/dist/preload/shared/expose.d.ts +15 -0
  55. package/dist/preload/shared/expose.js +33 -0
  56. package/dist/preload/shared/page-iframe.d.ts +3 -0
  57. package/dist/preload/shared/page-iframe.js +6 -0
  58. package/dist/preload/shared/types.d.ts +2 -0
  59. package/dist/preload/windows/simulator.js +400 -348
  60. package/dist/renderer/assets/constants-DI382mTP.js +2 -0
  61. package/dist/renderer/assets/index-D8VFYBh-.js +46 -0
  62. package/dist/renderer/assets/{input-DqVQNCx_.js → input-CoWcfa8u.js} +2 -2
  63. package/dist/renderer/assets/ipc-transport-Cd2xPrsa.js +9 -0
  64. package/dist/renderer/assets/popover-Nq5gT9DO.js +2 -0
  65. package/dist/renderer/assets/select-By5KH-UG.js +2 -0
  66. package/dist/renderer/assets/{settings-BFNfnYvU.js → settings-Dj_N70qR.js} +2 -2
  67. package/dist/renderer/assets/settings-api-Do0_Smq1.js +2 -0
  68. package/dist/renderer/assets/workbenchSettings-CazSLGYt.js +8 -0
  69. package/dist/renderer/entries/main/index.html +5 -4
  70. package/dist/renderer/entries/popover/index.html +5 -4
  71. package/dist/renderer/entries/settings/index.html +4 -4
  72. package/dist/renderer/entries/workbench-settings/index.html +4 -3
  73. package/dist/shared/ipc-channels.d.ts +4 -12
  74. package/dist/shared/ipc-channels.js +10 -14
  75. package/dist/shared/simulator-route.d.ts +72 -0
  76. package/dist/shared/simulator-route.js +169 -0
  77. package/dist/shared/types.d.ts +2 -10
  78. package/dist/simulator/assets/simulator-DJCozrU5.js +9 -0
  79. package/dist/simulator/simulator.html +6 -43
  80. package/package.json +4 -4
  81. package/dist/preload/instrumentation/disposable.d.ts +0 -11
  82. package/dist/preload/instrumentation/disposable.js +0 -15
  83. package/dist/renderer/assets/index-Czz_bp6b.js +0 -46
  84. package/dist/renderer/assets/ipc-transport-CCHrCtDr.js +0 -9
  85. package/dist/renderer/assets/popover-BRg4XI_w.js +0 -2
  86. package/dist/renderer/assets/select-CvILHHZU.js +0 -2
  87. package/dist/renderer/assets/settings-api-DOAgmBst.js +0 -2
  88. package/dist/renderer/assets/workbenchSettings-DjVMgMMH.js +0 -8
  89. package/dist/simulator/assets/simulator-DL6kOCq-.js +0 -9
@@ -1,6 +1,10 @@
1
1
  // src/main/app/bootstrap.ts
2
2
  import { app as app2 } from "electron";
3
3
 
4
+ // src/shared/constants.ts
5
+ var DEFAULT_CDP_PORT = 9222;
6
+ var DEFAULT_SCENE = 1001;
7
+
4
8
  // src/main/services/settings/index.ts
5
9
  import { app, nativeTheme } from "electron";
6
10
  import fs from "fs";
@@ -8,7 +12,7 @@ import path from "path";
8
12
  var DEFAULTS = {
9
13
  cdp: {
10
14
  enabled: false,
11
- port: 9222
15
+ port: DEFAULT_CDP_PORT
12
16
  },
13
17
  mcp: {
14
18
  enabled: false,
@@ -70,12 +74,12 @@ function setupCdpPort() {
70
74
  if (settings.cdp.enabled || settings.mcp.enabled) {
71
75
  app2.commandLine.appendSwitch("remote-debugging-port", String(settings.cdp.port));
72
76
  } else if (!app2.isPackaged) {
73
- app2.commandLine.appendSwitch("remote-debugging-port", String(settings.cdp.port || 9222));
77
+ app2.commandLine.appendSwitch("remote-debugging-port", String(settings.cdp.port || DEFAULT_CDP_PORT));
74
78
  }
75
79
  }
76
80
 
77
81
  // src/main/app/app.ts
78
- import { app as app13, BrowserWindow as BrowserWindow3, nativeImage } from "electron";
82
+ import { app as app13, BrowserWindow as BrowserWindow4, nativeImage } from "electron";
79
83
  import fs7 from "fs";
80
84
  import path12 from "path";
81
85
 
@@ -103,6 +107,83 @@ var defaultPreloadPath = path2.join(devtoolsPackageRoot, "dist/preload/windows/s
103
107
  var mainPreloadPath = path2.join(devtoolsPackageRoot, "dist/preload/windows/main.cjs");
104
108
  var simulatorDir = path2.join(devtoolsPackageRoot, "dist/simulator");
105
109
 
110
+ // src/main/utils/theme.ts
111
+ import { BrowserWindow, nativeTheme as nativeTheme2 } from "electron";
112
+
113
+ // src/main/utils/disposable.ts
114
+ function toDisposable(fn) {
115
+ let done = false;
116
+ return {
117
+ dispose() {
118
+ if (done) return;
119
+ done = true;
120
+ return fn();
121
+ }
122
+ };
123
+ }
124
+ var DisposableRegistry = class {
125
+ entries = [];
126
+ _disposed = false;
127
+ add(d) {
128
+ if (this._disposed) {
129
+ throw new Error("cannot add to disposed registry");
130
+ }
131
+ const fn = typeof d === "function" ? d : () => d.dispose();
132
+ const entry = { fn, released: false };
133
+ this.entries.push(entry);
134
+ return {
135
+ dispose: () => {
136
+ if (entry.released) return;
137
+ entry.released = true;
138
+ const i = this.entries.indexOf(entry);
139
+ if (i >= 0) this.entries.splice(i, 1);
140
+ return fn();
141
+ }
142
+ };
143
+ }
144
+ async disposeAll() {
145
+ if (this._disposed) return;
146
+ this._disposed = true;
147
+ const items = this.entries.slice().reverse();
148
+ this.entries = [];
149
+ const errors = [];
150
+ for (const entry of items) {
151
+ if (entry.released) continue;
152
+ entry.released = true;
153
+ try {
154
+ await entry.fn();
155
+ } catch (e) {
156
+ errors.push(e);
157
+ }
158
+ }
159
+ if (errors.length > 0) {
160
+ throw new AggregateError(errors, "DisposableRegistry encountered errors during disposeAll");
161
+ }
162
+ }
163
+ dispose() {
164
+ return this.disposeAll();
165
+ }
166
+ };
167
+
168
+ // src/main/utils/theme.ts
169
+ function themeBg() {
170
+ return nativeTheme2.shouldUseDarkColors ? "#1a1a1a" : "#fafafa";
171
+ }
172
+ function installThemeBackgroundSync() {
173
+ const apply = () => {
174
+ const bg = themeBg();
175
+ for (const win of BrowserWindow.getAllWindows()) {
176
+ if (!win.isDestroyed()) {
177
+ win.setBackgroundColor(bg);
178
+ }
179
+ }
180
+ };
181
+ nativeTheme2.on("updated", apply);
182
+ return toDisposable(() => {
183
+ nativeTheme2.removeListener("updated", apply);
184
+ });
185
+ }
186
+
106
187
  // src/main/app/lifecycle.ts
107
188
  import { app as app3, globalShortcut } from "electron";
108
189
  function registerAppLifecycle() {
@@ -116,7 +197,7 @@ function registerAppLifecycle() {
116
197
  }
117
198
 
118
199
  // src/main/windows/main-window/create.ts
119
- import { app as app4, BrowserWindow, View, session, shell as shell2 } from "electron";
200
+ import { app as app4, BrowserWindow as BrowserWindow2, View, session, shell as shell2 } from "electron";
120
201
  import path4 from "path";
121
202
 
122
203
  // src/main/services/simulator/referer.ts
@@ -138,12 +219,6 @@ function getSimulatorServicewechatReferer() {
138
219
  return cachedRefererUrl;
139
220
  }
140
221
 
141
- // src/main/utils/theme.ts
142
- import { nativeTheme as nativeTheme2 } from "electron";
143
- function themeBg() {
144
- return nativeTheme2.shouldUseDarkColors ? "#1a1a1a" : "#fafafa";
145
- }
146
-
147
222
  // src/main/windows/navigation-hardening.ts
148
223
  import { shell } from "electron";
149
224
  import path3 from "path";
@@ -208,7 +283,7 @@ function configureSimulatorSession(simulatorPreloadPath) {
208
283
  }
209
284
  function createMainWindow(opts) {
210
285
  configureSimulatorSession(opts.simulatorPreloadPath);
211
- const mainWindow = new BrowserWindow({
286
+ const mainWindow = new BrowserWindow2({
212
287
  width: opts.width ?? 1280,
213
288
  height: opts.height ?? 980,
214
289
  minWidth: opts.minWidth ?? 1e3,
@@ -274,63 +349,6 @@ function createMainWindow(opts) {
274
349
 
275
350
  // src/main/windows/main-window/events.ts
276
351
  import { globalShortcut as globalShortcut2 } from "electron";
277
-
278
- // src/main/utils/disposable.ts
279
- function toDisposable(fn) {
280
- let done = false;
281
- return {
282
- dispose() {
283
- if (done) return;
284
- done = true;
285
- return fn();
286
- }
287
- };
288
- }
289
- var DisposableRegistry = class {
290
- entries = [];
291
- _disposed = false;
292
- add(d) {
293
- if (this._disposed) {
294
- throw new Error("cannot add to disposed registry");
295
- }
296
- const fn = typeof d === "function" ? d : () => d.dispose();
297
- const entry = { fn, released: false };
298
- this.entries.push(entry);
299
- return {
300
- dispose: () => {
301
- if (entry.released) return;
302
- entry.released = true;
303
- const i = this.entries.indexOf(entry);
304
- if (i >= 0) this.entries.splice(i, 1);
305
- return fn();
306
- }
307
- };
308
- }
309
- async disposeAll() {
310
- if (this._disposed) return;
311
- this._disposed = true;
312
- const items = this.entries.slice().reverse();
313
- this.entries = [];
314
- const errors = [];
315
- for (const entry of items) {
316
- if (entry.released) continue;
317
- entry.released = true;
318
- try {
319
- await entry.fn();
320
- } catch (e) {
321
- errors.push(e);
322
- }
323
- }
324
- if (errors.length > 0) {
325
- throw new AggregateError(errors, "DisposableRegistry encountered errors during disposeAll");
326
- }
327
- }
328
- dispose() {
329
- return this.disposeAll();
330
- }
331
- };
332
-
333
- // src/main/windows/main-window/events.ts
334
352
  function wireMainWindowEvents(win, state = {}) {
335
353
  const registry = new DisposableRegistry();
336
354
  const mainWebView = win.contentView.children[0];
@@ -387,12 +405,12 @@ import { app as app5 } from "electron";
387
405
  var defaultAdapter = {
388
406
  async openProject(opts) {
389
407
  const diminaKit = await import("@dimina-kit/devkit");
390
- const openProject = diminaKit.openProject;
391
- return openProject({
408
+ const openProjectOpts = {
392
409
  outputDir: path5.join(app5.getPath("userData"), "dimina-fe-output"),
393
410
  ...opts,
394
411
  simulatorDir
395
- });
412
+ };
413
+ return diminaKit.openProject(openProjectOpts);
396
414
  }
397
415
  };
398
416
 
@@ -402,10 +420,7 @@ var SimulatorChannel = {
402
420
  Detach: "simulator:detach",
403
421
  Resize: "simulator:resize",
404
422
  SetVisible: "simulator:setVisible",
405
- Console: "simulator:console",
406
- Wxml: "simulator:wxml",
407
- AppData: "simulator:appdata",
408
- AppDataAll: "simulator:appdata-all"
423
+ Console: "simulator:console"
409
424
  };
410
425
  var SimulatorCustomApiChannel = {
411
426
  List: "simulator:custom-apis:list",
@@ -424,16 +439,12 @@ var SimulatorElementChannel = {
424
439
  Inspect: "simulator:element:inspect",
425
440
  Clear: "simulator:element:clear"
426
441
  };
427
- var WorkbenchChannel = {
428
- GetPanelConfig: "workbench:getPanelConfig",
429
- GetApiNamespaces: "workbench:getApiNamespaces",
430
- Reset: "workbench:reset"
431
- };
432
442
  var WorkbenchSettingsChannel = {
433
443
  Get: "workbenchSettings:get",
434
444
  Save: "workbenchSettings:save",
435
445
  SetTheme: "workbenchSettings:setTheme",
436
446
  GetCdpStatus: "workbenchSettings:getCdpStatus",
447
+ GetMcpStatus: "workbenchSettings:getMcpStatus",
437
448
  SetVisible: "workbenchSettings:setVisible",
438
449
  Init: "workbenchSettings:init"
439
450
  };
@@ -516,8 +527,8 @@ function liveWebContents(wc) {
516
527
  }
517
528
  function createRendererNotifier(ctx) {
518
529
  function sendToMain(channel, ...args) {
519
- if (ctx.mainWindow.isDestroyed()) return;
520
- const wc = liveWebContents(ctx.mainWindow.webContents);
530
+ if (ctx.windows.mainWindow.isDestroyed()) return;
531
+ const wc = liveWebContents(ctx.windows.mainWindow.webContents);
521
532
  if (!wc) return;
522
533
  wc.send(channel, ...args);
523
534
  }
@@ -540,9 +551,6 @@ function createRendererNotifier(ctx) {
540
551
  popoverRelaunch(config) {
541
552
  sendToMain(PopoverChannel.Relaunch, config);
542
553
  },
543
- workbenchReset() {
544
- sendToMain(WorkbenchChannel.Reset);
545
- },
546
554
  toolbarActionsChanged() {
547
555
  sendToMain(ToolbarChannel.ActionsChanged);
548
556
  },
@@ -616,9 +624,9 @@ function createViewManager(ctx) {
616
624
  let simulatorWebContentsId = null;
617
625
  function destroyViewInternal(view) {
618
626
  if (!view) return;
619
- if (!ctx.mainWindow.isDestroyed()) {
627
+ if (!ctx.windows.mainWindow.isDestroyed()) {
620
628
  try {
621
- ctx.mainWindow.contentView.removeChildView(view);
629
+ ctx.windows.mainWindow.contentView.removeChildView(view);
622
630
  } catch {
623
631
  }
624
632
  }
@@ -630,18 +638,18 @@ function createViewManager(ctx) {
630
638
  }
631
639
  }
632
640
  function applySimulatorBounds(simWidth) {
633
- if (!simulatorView || ctx.mainWindow.isDestroyed()) return;
634
- const [w = 0, h = 0] = ctx.mainWindow.getContentSize();
641
+ if (!simulatorView || ctx.windows.mainWindow.isDestroyed()) return;
642
+ const [w = 0, h = 0] = ctx.windows.mainWindow.getContentSize();
635
643
  simulatorView.setBounds(computeSimulatorBounds(w, h, simWidth));
636
644
  }
637
645
  function applySettingsBounds() {
638
- if (!settingsView || ctx.mainWindow.isDestroyed()) return;
639
- const [w = 0, h = 0] = ctx.mainWindow.getContentSize();
646
+ if (!settingsView || ctx.windows.mainWindow.isDestroyed()) return;
647
+ const [w = 0, h = 0] = ctx.windows.mainWindow.getContentSize();
640
648
  settingsView.setBounds(computeSettingsBounds(w, h));
641
649
  }
642
650
  function applyPopoverBounds() {
643
- if (!popoverView || ctx.mainWindow.isDestroyed()) return;
644
- const [w = 0, h = 0] = ctx.mainWindow.getContentSize();
651
+ if (!popoverView || ctx.windows.mainWindow.isDestroyed()) return;
652
+ const [w = 0, h = 0] = ctx.windows.mainWindow.getContentSize();
645
653
  popoverView.setBounds(computePopoverBounds(w, h));
646
654
  }
647
655
  function attachSimulator(simWcId, simWidth) {
@@ -689,7 +697,7 @@ function createViewManager(ctx) {
689
697
  });
690
698
  });
691
699
  if (getDefaultTab(ctx) === "simulator") {
692
- ctx.mainWindow.contentView.addChildView(simulatorView);
700
+ ctx.windows.mainWindow.contentView.addChildView(simulatorView);
693
701
  simulatorViewAdded = true;
694
702
  applySimulatorBounds(simWidth);
695
703
  }
@@ -708,7 +716,7 @@ function createViewManager(ctx) {
708
716
  lastSimWidth = simWidth;
709
717
  if (!simulatorView) return;
710
718
  if (!simulatorViewAdded) {
711
- ctx.mainWindow.contentView.addChildView(simulatorView);
719
+ ctx.windows.mainWindow.contentView.addChildView(simulatorView);
712
720
  simulatorViewAdded = true;
713
721
  }
714
722
  applySimulatorBounds(simWidth);
@@ -716,7 +724,7 @@ function createViewManager(ctx) {
716
724
  function hideSimulator() {
717
725
  if (simulatorView && simulatorViewAdded) {
718
726
  try {
719
- ctx.mainWindow.contentView.removeChildView(simulatorView);
727
+ ctx.windows.mainWindow.contentView.removeChildView(simulatorView);
720
728
  } catch (e) {
721
729
  console.error("[workbench] hideSimulator error", e);
722
730
  }
@@ -739,7 +747,7 @@ function createViewManager(ctx) {
739
747
  );
740
748
  }
741
749
  if (!settingsViewAdded) {
742
- ctx.mainWindow.contentView.addChildView(settingsView);
750
+ ctx.windows.mainWindow.contentView.addChildView(settingsView);
743
751
  settingsViewAdded = true;
744
752
  }
745
753
  applySettingsBounds();
@@ -747,7 +755,7 @@ function createViewManager(ctx) {
747
755
  function hideSettings() {
748
756
  if (settingsView && settingsViewAdded) {
749
757
  try {
750
- ctx.mainWindow.contentView.removeChildView(settingsView);
758
+ ctx.windows.mainWindow.contentView.removeChildView(settingsView);
751
759
  } catch {
752
760
  }
753
761
  settingsViewAdded = false;
@@ -766,7 +774,7 @@ function createViewManager(ctx) {
766
774
  applyNavigationHardening(popover.webContents, ctx.rendererDir);
767
775
  popoverView = popover;
768
776
  popover.setBackgroundColor("#00000000");
769
- ctx.mainWindow.contentView.addChildView(popover);
777
+ ctx.windows.mainWindow.contentView.addChildView(popover);
770
778
  applyPopoverBounds();
771
779
  popover.webContents.once("did-finish-load", () => {
772
780
  ctx.notify.popoverInit(popover, data);
@@ -793,14 +801,6 @@ function createViewManager(ctx) {
793
801
  function disposeAll() {
794
802
  detachSimulator();
795
803
  }
796
- function logChildViews(_label) {
797
- }
798
- function positionSimulator(simWidth) {
799
- applySimulatorBounds(simWidth);
800
- }
801
- function positionSettings() {
802
- applySettingsBounds();
803
- }
804
804
  function resize(simWidth) {
805
805
  lastSimWidth = simWidth;
806
806
  if (simulatorViewAdded) applySimulatorBounds(simWidth);
@@ -826,10 +826,12 @@ function createViewManager(ctx) {
826
826
  hidePopover,
827
827
  repositionAll,
828
828
  disposeAll,
829
- logChildViews,
830
- positionSimulator,
831
- positionSettings,
832
829
  getSimulatorWebContentsId: () => simulatorWebContentsId,
830
+ getSimulatorWebContents: () => {
831
+ if (simulatorWebContentsId == null) return null;
832
+ const wc = webContents.fromId(simulatorWebContentsId);
833
+ return wc && !wc.isDestroyed() ? wc : null;
834
+ },
833
835
  getLastSimWidth: () => lastSimWidth,
834
836
  isSimulatorAdded: () => simulatorViewAdded,
835
837
  hasSimulatorView: () => simulatorView !== null,
@@ -879,17 +881,11 @@ function createWindowService(mainWindow) {
879
881
  };
880
882
  }
881
883
 
882
- // src/main/services/workspace/workspace-service.ts
883
- import { webContents as webContents2 } from "electron";
884
-
885
884
  // src/main/services/projects/project-repository.ts
886
885
  import { app as app6 } from "electron";
887
886
  import path7 from "path";
888
887
  import fs3 from "fs";
889
888
 
890
- // src/shared/constants.ts
891
- var DEFAULT_SCENE = 1001;
892
-
893
889
  // src/main/utils/logger.ts
894
890
  var LEVEL_PRIORITY = {
895
891
  debug: 0,
@@ -1077,8 +1073,8 @@ var DEFAULT_COMPILE_CONFIG = {
1077
1073
  function createWorkspaceService(ctx) {
1078
1074
  let currentSession = null;
1079
1075
  let currentProjectPath = "";
1080
- function sendStatus(status, message, hotReload) {
1081
- ctx.notify.projectStatus(hotReload ? { status, message, hotReload: true } : { status, message });
1076
+ function sendStatus(status2, message, hotReload) {
1077
+ ctx.notify.projectStatus(hotReload ? { status: status2, message, hotReload: true } : { status: status2, message });
1082
1078
  }
1083
1079
  function bestEffort(label, fn) {
1084
1080
  try {
@@ -1111,15 +1107,7 @@ function createWorkspaceService(ctx) {
1111
1107
  );
1112
1108
  }
1113
1109
  }
1114
- const provider = ctx.projectsProvider ?? {
1115
- listProjects: () => listProjects(),
1116
- addProject: (p) => addProject(p),
1117
- removeProject: (p) => removeProject(p),
1118
- validateProjectDir: (p) => validateProjectDir(p),
1119
- updateLastOpened: (p) => updateLastOpened(p),
1120
- getCompileConfig: (p) => getCompileConfig(p),
1121
- saveCompileConfig: (p, c) => saveCompileConfig(p, c)
1122
- };
1110
+ const provider = ctx.projectsProvider;
1123
1111
  return {
1124
1112
  listProjects: async () => provider.listProjects(),
1125
1113
  addProject: async (dirPath) => provider.addProject(dirPath),
@@ -1179,10 +1167,8 @@ function createWorkspaceService(ctx) {
1179
1167
  getProjectPath: () => currentProjectPath,
1180
1168
  hasActiveSession: () => currentSession !== null,
1181
1169
  async captureThumbnail(projectPath) {
1182
- const simWcId = ctx.views.getSimulatorWebContentsId();
1183
- if (!simWcId) return null;
1184
- const wc = webContents2.fromId(simWcId);
1185
- if (!wc || wc.isDestroyed()) return null;
1170
+ const wc = ctx.views.getSimulatorWebContents();
1171
+ if (!wc) return null;
1186
1172
  try {
1187
1173
  const image = await wc.capturePage();
1188
1174
  const dataUrl = `data:image/png;base64,${image.toPNG().toString("base64")}`;
@@ -1317,7 +1303,6 @@ function getDefaultTab(ctx) {
1317
1303
  }
1318
1304
  function createWorkbenchContext(opts) {
1319
1305
  const ctx = {
1320
- mainWindow: opts.mainWindow,
1321
1306
  adapter: opts.adapter ?? defaultAdapter,
1322
1307
  preloadPath: opts.preloadPath,
1323
1308
  rendererDir: opts.rendererDir,
@@ -1325,11 +1310,10 @@ function createWorkbenchContext(opts) {
1325
1310
  apiNamespaces: opts.apiNamespaces ?? [],
1326
1311
  appName: opts.appName ?? "Dimina DevTools",
1327
1312
  toolbarActions: opts.toolbarActions,
1328
- brandingProvider: opts.brandingProvider,
1329
- workbenchSettingsWindow: null
1313
+ brandingProvider: opts.brandingProvider
1330
1314
  };
1331
1315
  ctx.registry = new DisposableRegistry();
1332
- ctx.windows = createWindowService(ctx.mainWindow);
1316
+ ctx.windows = createWindowService(opts.mainWindow);
1333
1317
  ctx.views = createViewManager(ctx);
1334
1318
  ctx.notify = createRendererNotifier(ctx);
1335
1319
  ctx.projectsProvider = opts.projectsProvider ?? createLocalProjectsProvider();
@@ -1644,17 +1628,10 @@ function registerSimulatorIpc(ctx) {
1644
1628
  }).handle(SimulatorCustomApiChannel.Invoke, (_, ...args) => {
1645
1629
  const [name, params] = validate(SimulatorCustomApiChannel.Invoke, SimulatorCustomApiInvokeSchema, args);
1646
1630
  return simulatorApiRegistry.invoke(name, params);
1647
- }).handle(WorkbenchChannel.GetPanelConfig, () => {
1648
- return ctx.panels;
1649
- }).handle(WorkbenchChannel.GetApiNamespaces, () => {
1650
- return ctx.apiNamespaces;
1651
- }).on(WorkbenchChannel.Reset, () => {
1652
- ctx.notify.workbenchReset();
1653
1631
  });
1654
1632
  }
1655
1633
 
1656
1634
  // src/main/ipc/panels.ts
1657
- import { webContents as webContents3 } from "electron";
1658
1635
  var BUILTIN_PANELS = [
1659
1636
  { id: "wxml", label: "WXML" },
1660
1637
  { id: "appdata", label: "AppData" },
@@ -1665,10 +1642,9 @@ function registerPanelsIpc(ctx) {
1665
1642
  return BUILTIN_PANELS.filter((p) => ctx.panels.includes(p.id));
1666
1643
  }).handle(PanelChannel.Eval, async (_event, ...args) => {
1667
1644
  const [expression] = validate(PanelChannel.Eval, PanelEvalSchema, args);
1668
- const simWcId = ctx.views.getSimulatorWebContentsId();
1669
- if (!ctx.workspace.hasActiveSession() || simWcId == null) return void 0;
1670
- const sim = webContents3.fromId(simWcId);
1671
- if (!sim || sim.isDestroyed()) return void 0;
1645
+ if (!ctx.workspace.hasActiveSession()) return void 0;
1646
+ const sim = ctx.views.getSimulatorWebContents();
1647
+ if (!sim) return void 0;
1672
1648
  try {
1673
1649
  return await sim.executeJavaScript(expression);
1674
1650
  } catch {
@@ -1776,7 +1752,7 @@ function registerProjectsIpc(ctx) {
1776
1752
  return new IpcRegistry(ctx.senderPolicy).handle(ProjectsChannel.List, () => {
1777
1753
  return ctx.workspace.listProjects();
1778
1754
  }).handle(DialogChannel.OpenDirectory, async () => {
1779
- const result = await dialog.showOpenDialog(ctx.mainWindow, {
1755
+ const result = await dialog.showOpenDialog(ctx.windows.mainWindow, {
1780
1756
  properties: ["openDirectory"],
1781
1757
  title: "\u9009\u62E9\u5C0F\u7A0B\u5E8F\u9879\u76EE\u76EE\u5F55"
1782
1758
  });
@@ -1785,7 +1761,7 @@ function registerProjectsIpc(ctx) {
1785
1761
  const [dirPath] = validate(ProjectsChannel.Add, ProjectsAddSchema, args);
1786
1762
  const dirError = await ctx.workspace.validateProjectDir(dirPath);
1787
1763
  if (dirError) {
1788
- await dialog.showMessageBox(ctx.mainWindow, {
1764
+ await dialog.showMessageBox(ctx.windows.mainWindow, {
1789
1765
  type: "error",
1790
1766
  title: "\u65E0\u6CD5\u5BFC\u5165\u9879\u76EE",
1791
1767
  message: "\u8BE5\u76EE\u5F55\u4E0D\u662F\u6709\u6548\u7684\u5C0F\u7A0B\u5E8F\u9879\u76EE",
@@ -1797,7 +1773,7 @@ function registerProjectsIpc(ctx) {
1797
1773
  const duplicate = await ctx.workspace.hasProject(dirPath);
1798
1774
  const project = await ctx.workspace.addProject(dirPath);
1799
1775
  if (duplicate) {
1800
- await dialog.showMessageBox(ctx.mainWindow, {
1776
+ await dialog.showMessageBox(ctx.windows.mainWindow, {
1801
1777
  type: "info",
1802
1778
  title: "\u9879\u76EE\u5DF2\u5B58\u5728",
1803
1779
  message: "\u8BE5\u9879\u76EE\u5DF2\u5728\u5217\u8868\u4E2D",
@@ -1815,7 +1791,7 @@ function registerProjectsIpc(ctx) {
1815
1791
  if (!ctx.customCreateProjectDialog) return null;
1816
1792
  const sanitized = sanitizeTemplates(ctx.projectTemplates ?? []);
1817
1793
  return await ctx.customCreateProjectDialog({
1818
- parentWindow: ctx.mainWindow,
1794
+ parentWindow: ctx.windows.mainWindow,
1819
1795
  templates: sanitized
1820
1796
  });
1821
1797
  }).handle(ProjectsChannel.Create, async (_event, ...args) => {
@@ -1831,7 +1807,7 @@ function registerProjectsIpc(ctx) {
1831
1807
  projectsProvider: ctx.projectsProvider
1832
1808
  });
1833
1809
  } catch (err) {
1834
- await dialog.showMessageBox(ctx.mainWindow, {
1810
+ await dialog.showMessageBox(ctx.windows.mainWindow, {
1835
1811
  type: "error",
1836
1812
  title: "\u521B\u5EFA\u9879\u76EE\u5931\u8D25",
1837
1813
  message: "\u65E0\u6CD5\u521B\u5EFA\u9879\u76EE",
@@ -1913,6 +1889,23 @@ var sessionModule = {
1913
1889
 
1914
1890
  // src/main/ipc/settings.ts
1915
1891
  import { app as app9 } from "electron";
1892
+
1893
+ // src/main/services/mcp/status.ts
1894
+ var status = { running: false, port: null, error: null };
1895
+ function getMcpStatus() {
1896
+ return { ...status };
1897
+ }
1898
+ function recordMcpStarted(port) {
1899
+ status = { running: true, port, error: null };
1900
+ }
1901
+ function recordMcpFailed(error) {
1902
+ status = { running: false, port: null, error };
1903
+ }
1904
+ function recordMcpStopped() {
1905
+ status = { running: false, port: null, error: null };
1906
+ }
1907
+
1908
+ // src/main/ipc/settings.ts
1916
1909
  function registerSettingsIpc(ctx) {
1917
1910
  return new IpcRegistry(ctx.senderPolicy).handle(WorkbenchSettingsChannel.Get, () => {
1918
1911
  return loadWorkbenchSettings();
@@ -1935,7 +1928,7 @@ function registerSettingsIpc(ctx) {
1935
1928
  }).handle(WorkbenchSettingsChannel.GetCdpStatus, () => {
1936
1929
  const settings = loadWorkbenchSettings();
1937
1930
  const switchValue = app9.commandLine.getSwitchValue("remote-debugging-port");
1938
- const implicitDevDefault = !app9.isPackaged && !settings.cdp.enabled && switchValue === "9222";
1931
+ const implicitDevDefault = !app9.isPackaged && !settings.cdp.enabled && switchValue === String(DEFAULT_CDP_PORT);
1939
1932
  return {
1940
1933
  configured: settings.cdp.enabled,
1941
1934
  port: settings.cdp.port,
@@ -1943,6 +1936,16 @@ function registerSettingsIpc(ctx) {
1943
1936
  activePort: switchValue ? parseInt(switchValue, 10) : null,
1944
1937
  implicitDevDefault
1945
1938
  };
1939
+ }).handle(WorkbenchSettingsChannel.GetMcpStatus, () => {
1940
+ const settings = loadWorkbenchSettings();
1941
+ const runtime = getMcpStatus();
1942
+ return {
1943
+ configured: settings.mcp.enabled,
1944
+ configuredPort: settings.mcp.port,
1945
+ running: runtime.running,
1946
+ activePort: runtime.port,
1947
+ error: runtime.error
1948
+ };
1946
1949
  }).handle(WorkbenchSettingsChannel.SetVisible, async (_, ...args) => {
1947
1950
  const [visible] = validate(
1948
1951
  WorkbenchSettingsChannel.SetVisible,
@@ -2018,13 +2021,9 @@ var simulatorModule = {
2018
2021
  import { WebSocketServer } from "ws";
2019
2022
 
2020
2023
  // src/main/services/automation/exec.ts
2021
- import { webContents as webContents4 } from "electron";
2022
2024
  function getSimulator(ctx) {
2023
- const simWcId = ctx.views.getSimulatorWebContentsId();
2024
- if (!ctx.workspace.hasActiveSession() || simWcId == null) return null;
2025
- const sim = webContents4.fromId(simWcId);
2026
- if (!sim || sim.isDestroyed()) return null;
2027
- return sim;
2025
+ if (!ctx.workspace.hasActiveSession()) return null;
2026
+ return ctx.views.getSimulatorWebContents();
2028
2027
  }
2029
2028
  async function evalInSim(ctx, expression) {
2030
2029
  const sim = getSimulator(ctx);
@@ -2032,8 +2031,11 @@ async function evalInSim(ctx, expression) {
2032
2031
  return sim.executeJavaScript(expression);
2033
2032
  }
2034
2033
  function buildElAccess(ref, varName = "el") {
2035
- const escaped = ref.selector.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
2036
- return `const ${varName} = _doc.querySelectorAll('${escaped}')[${ref.index}]`;
2034
+ return `const ${varName} = _doc.querySelectorAll(${JSON.stringify(ref.selector)})[${ref.index}]`;
2035
+ }
2036
+ function evalInElement(ctx, ref, body, varName = "el") {
2037
+ return evalInSim(ctx, inIframe(`${buildElAccess(ref, varName)}
2038
+ ${body}`));
2037
2039
  }
2038
2040
  function inIframe(code) {
2039
2041
  return `(() => {
@@ -2053,47 +2055,57 @@ toolHandlers["Tool.close"] = async (ctx) => {
2053
2055
  return {};
2054
2056
  };
2055
2057
 
2058
+ // src/shared/simulator-route.ts
2059
+ function decodePageSpec(value) {
2060
+ const qIdx = value.indexOf("?");
2061
+ if (qIdx < 0) return { pagePath: value, query: {} };
2062
+ const pagePath = value.slice(0, qIdx);
2063
+ const query = {};
2064
+ for (const pair of value.slice(qIdx + 1).split("&")) {
2065
+ if (!pair) continue;
2066
+ const eqIdx = pair.indexOf("=");
2067
+ const k = eqIdx >= 0 ? pair.slice(0, eqIdx) : pair;
2068
+ const v = eqIdx >= 0 ? pair.slice(eqIdx + 1) : "";
2069
+ if (k) query[decodeURIComponent(k)] = decodeURIComponent(v);
2070
+ }
2071
+ return { pagePath, query };
2072
+ }
2073
+ function parseLocationRoute(search) {
2074
+ const params = new URLSearchParams(search.startsWith("?") ? search.slice(1) : search);
2075
+ const appId = params.get("appId");
2076
+ const entryRaw = params.get("entry");
2077
+ if (!appId || !entryRaw) return null;
2078
+ const entry = decodePageSpec(entryRaw);
2079
+ const pageRaw = params.get("page");
2080
+ const current = pageRaw && pageRaw !== entryRaw ? decodePageSpec(pageRaw) : entry;
2081
+ return { appId, entry, current };
2082
+ }
2083
+
2056
2084
  // src/main/services/automation/handlers/app.ts
2057
2085
  var appHandlers = {};
2086
+ async function readRoute(ctx) {
2087
+ const { search } = await evalInSim(
2088
+ ctx,
2089
+ `({ search: location.search })`
2090
+ );
2091
+ return parseLocationRoute(search);
2092
+ }
2058
2093
  appHandlers["App.getCurrentPage"] = async (ctx) => {
2059
- const hash = await evalInSim(ctx, "location.hash");
2060
- const clean = hash.replace(/^#/, "");
2061
- const qIdx = clean.indexOf("?");
2062
- const pathPart = qIdx >= 0 ? clean.substring(0, qIdx) : clean;
2063
- const queryPart = qIdx >= 0 ? clean.substring(qIdx + 1) : "";
2064
- let pagePath;
2065
- if (pathPart.includes("|")) {
2066
- const parts = pathPart.split("|");
2067
- pagePath = parts[parts.length - 1] ?? "";
2068
- } else {
2069
- const slashIdx = pathPart.indexOf("/");
2070
- pagePath = slashIdx >= 0 ? pathPart.substring(slashIdx + 1) : pathPart;
2071
- }
2072
- const query = {};
2073
- if (queryPart) {
2074
- for (const pair of queryPart.split("&")) {
2075
- const [k, v] = pair.split("=");
2076
- if (k) query[k] = decodeURIComponent(v || "");
2077
- }
2078
- }
2094
+ const route = await readRoute(ctx);
2079
2095
  const iframeCount = await evalInSim(ctx, `document.querySelectorAll('iframe').length`);
2080
- return { pageId: iframeCount, path: pagePath, query };
2096
+ return {
2097
+ pageId: iframeCount,
2098
+ path: route?.current.pagePath ?? "",
2099
+ query: route?.current.query ?? {}
2100
+ };
2081
2101
  };
2082
2102
  appHandlers["App.getPageStack"] = async (ctx) => {
2083
- const hash = await evalInSim(ctx, "location.hash");
2084
- const clean = hash.replace(/^#/, "").replace(/\?.*$/, "");
2103
+ const route = await readRoute(ctx);
2085
2104
  const pageStack = [];
2086
- if (clean.includes("|")) {
2087
- const parts = clean.split("|");
2088
- for (let i = 1; i < parts.length; i++) {
2089
- pageStack.push({ pageId: i, path: parts[i] ?? "", query: {} });
2090
- }
2091
- } else {
2092
- const slashIdx = clean.indexOf("/");
2093
- const currentPath = slashIdx >= 0 ? clean.substring(slashIdx + 1) : clean;
2094
- if (currentPath) {
2095
- pageStack.push({ pageId: 1, path: currentPath, query: {} });
2096
- }
2105
+ if (!route) return { pageStack };
2106
+ pageStack.push({ pageId: 1, path: route.entry.pagePath, query: route.entry.query });
2107
+ if (route.current.pagePath !== route.entry.pagePath) {
2108
+ pageStack.push({ pageId: 2, path: route.current.pagePath, query: route.current.query });
2097
2109
  }
2098
2110
  return { pageStack };
2099
2111
  };
@@ -2106,18 +2118,29 @@ appHandlers["App.callWxMethod"] = async (ctx, params) => {
2106
2118
  if (url) {
2107
2119
  const cleanUrl = url.startsWith("/") ? url : `/${url}`;
2108
2120
  const clicked = await evalInSim(ctx, inIframe(`
2109
- const el = _doc.querySelector('[data-path="${cleanUrl}"]')
2121
+ const path = ${JSON.stringify(cleanUrl)}
2122
+ const el = Array.from(_doc.querySelectorAll('[data-path]'))
2123
+ .find(e => e.getAttribute('data-path') === path)
2110
2124
  if (el) { el.click(); return true }
2111
2125
  return false
2112
2126
  `)).catch(() => false);
2113
2127
  if (!clicked) {
2114
- const hash = await evalInSim(ctx, "location.hash");
2115
- const appId = hash.replace(/^#/, "").split("/")[0];
2116
- if (method === "reLaunch" || method === "redirectTo") {
2117
- await evalInSim(ctx, `location.href = location.pathname + '#${appId}${cleanUrl}'`);
2118
- } else {
2119
- await evalInSim(ctx, `location.hash = '#${appId}${cleanUrl}'`);
2120
- }
2128
+ const apiName = JSON.stringify(method);
2129
+ const urlJson = JSON.stringify(cleanUrl);
2130
+ await evalInSim(
2131
+ ctx,
2132
+ `(() => {
2133
+ const iframes = Array.from(document.querySelectorAll('iframe'))
2134
+ for (const f of iframes) {
2135
+ try {
2136
+ if (f.contentWindow && f.contentWindow.wx && typeof f.contentWindow.wx[${apiName}] === 'function') {
2137
+ f.contentWindow.wx[${apiName}]({ url: ${urlJson} })
2138
+ return
2139
+ }
2140
+ } catch (_) {}
2141
+ }
2142
+ })()`
2143
+ );
2121
2144
  }
2122
2145
  await new Promise((r) => setTimeout(r, 2e3));
2123
2146
  }
@@ -2252,109 +2275,97 @@ pageHandlers["Page.getWindowProperties"] = async (ctx, params) => {
2252
2275
  var elementHandlers = {};
2253
2276
  elementHandlers["Element.tap"] = async (ctx, params) => {
2254
2277
  const ref = getElementRef(params);
2255
- await evalInSim(ctx, inIframe(`
2256
- ${buildElAccess(ref)}
2278
+ await evalInElement(ctx, ref, `
2257
2279
  if (!el) throw new Error('Element not found')
2258
2280
  el.click()
2259
- `));
2281
+ `);
2260
2282
  return {};
2261
2283
  };
2262
2284
  elementHandlers["Element.triggerEvent"] = async (ctx, params) => {
2263
2285
  const ref = getElementRef(params);
2264
2286
  const type = params.type;
2265
2287
  const detail = params.detail || {};
2266
- await evalInSim(ctx, inIframe(`
2267
- ${buildElAccess(ref)}
2288
+ await evalInElement(ctx, ref, `
2268
2289
  if (!el) throw new Error('Element not found')
2269
2290
  el.dispatchEvent(new CustomEvent('${type}', { detail: ${JSON.stringify(detail)}, bubbles: true }))
2270
- `));
2291
+ `);
2271
2292
  return {};
2272
2293
  };
2273
2294
  elementHandlers["Element.touchstart"] = async (ctx, params) => {
2274
2295
  const ref = getElementRef(params);
2275
- await evalInSim(ctx, inIframe(`
2276
- ${buildElAccess(ref)}
2296
+ await evalInElement(ctx, ref, `
2277
2297
  if (el) el.dispatchEvent(new TouchEvent('touchstart', { bubbles: true }))
2278
- `));
2298
+ `);
2279
2299
  return {};
2280
2300
  };
2281
2301
  elementHandlers["Element.touchmove"] = async (ctx, params) => {
2282
2302
  const ref = getElementRef(params);
2283
- await evalInSim(ctx, inIframe(`
2284
- ${buildElAccess(ref)}
2303
+ await evalInElement(ctx, ref, `
2285
2304
  if (el) el.dispatchEvent(new TouchEvent('touchmove', { bubbles: true }))
2286
- `));
2305
+ `);
2287
2306
  return {};
2288
2307
  };
2289
2308
  elementHandlers["Element.touchend"] = async (ctx, params) => {
2290
2309
  const ref = getElementRef(params);
2291
- await evalInSim(ctx, inIframe(`
2292
- ${buildElAccess(ref)}
2310
+ await evalInElement(ctx, ref, `
2293
2311
  if (el) el.dispatchEvent(new TouchEvent('touchend', { bubbles: true }))
2294
- `));
2312
+ `);
2295
2313
  return {};
2296
2314
  };
2297
2315
  elementHandlers["Element.getDOMProperties"] = async (ctx, params) => {
2298
2316
  const ref = getElementRef(params);
2299
2317
  const names = params.names;
2300
- const properties = await evalInSim(ctx, inIframe(`
2301
- ${buildElAccess(ref)}
2318
+ const properties = await evalInElement(ctx, ref, `
2302
2319
  if (!el) return ${JSON.stringify(names.map(() => null))}
2303
2320
  return ${JSON.stringify(names)}.map(n => el[n])
2304
- `));
2321
+ `);
2305
2322
  return { properties };
2306
2323
  };
2307
2324
  elementHandlers["Element.getAttributes"] = async (ctx, params) => {
2308
2325
  const ref = getElementRef(params);
2309
2326
  const names = params.names;
2310
- const attributes = await evalInSim(ctx, inIframe(`
2311
- ${buildElAccess(ref)}
2327
+ const attributes = await evalInElement(ctx, ref, `
2312
2328
  if (!el) return ${JSON.stringify(names.map(() => null))}
2313
2329
  return ${JSON.stringify(names)}.map(n => el.getAttribute(n))
2314
- `));
2330
+ `);
2315
2331
  return { attributes };
2316
2332
  };
2317
2333
  elementHandlers["Element.getStyles"] = async (ctx, params) => {
2318
2334
  const ref = getElementRef(params);
2319
2335
  const names = params.names;
2320
- const styles = await evalInSim(ctx, inIframe(`
2321
- ${buildElAccess(ref)}
2336
+ const styles = await evalInElement(ctx, ref, `
2322
2337
  if (!el) return ${JSON.stringify(names.map(() => ""))}
2323
2338
  const cs = getComputedStyle(el)
2324
2339
  return ${JSON.stringify(names)}.map(n => cs.getPropertyValue(n))
2325
- `));
2340
+ `);
2326
2341
  return { styles };
2327
2342
  };
2328
2343
  elementHandlers["Element.getWXML"] = async (ctx, params) => {
2329
2344
  const ref = getElementRef(params);
2330
2345
  const type = params.type || "inner";
2331
2346
  const prop = type === "outer" ? "outerHTML" : "innerHTML";
2332
- const wxml = await evalInSim(ctx, inIframe(`
2333
- ${buildElAccess(ref)}
2347
+ const wxml = await evalInElement(ctx, ref, `
2334
2348
  return el ? el.${prop} : ''
2335
- `));
2349
+ `);
2336
2350
  return { wxml };
2337
2351
  };
2338
2352
  elementHandlers["Element.getOffset"] = async (ctx, params) => {
2339
2353
  const ref = getElementRef(params);
2340
- return evalInSim(ctx, inIframe(`
2341
- ${buildElAccess(ref)}
2354
+ return evalInElement(ctx, ref, `
2342
2355
  if (!el) return { left: 0, top: 0 }
2343
2356
  const r = el.getBoundingClientRect()
2344
2357
  return { left: r.left, top: r.top }
2345
- `));
2358
+ `);
2346
2359
  };
2347
2360
  elementHandlers["Element.getElement"] = async (ctx, params) => {
2348
2361
  const parentRef = getElementRef(params);
2349
2362
  const selector = params.selector;
2350
- const escaped = selector.replace(/'/g, "\\'");
2351
- const info = await evalInSim(ctx, inIframe(`
2352
- ${buildElAccess(parentRef, "parent")}
2363
+ const info = await evalInElement(ctx, parentRef, `
2353
2364
  if (!parent) return null
2354
- const child = parent.querySelector('${escaped}')
2365
+ const child = parent.querySelector(${JSON.stringify(selector)})
2355
2366
  if (!child) return null
2356
2367
  return { tagName: child.tagName.toLowerCase() }
2357
- `));
2368
+ `, "parent");
2358
2369
  if (!info) throw new Error(`Child element not found: ${selector}`);
2359
2370
  const combinedSelector = `${parentRef.selector} ${selector}`;
2360
2371
  const elementId = registerElement(combinedSelector, 0, parentRef.pageId);
@@ -2363,14 +2374,12 @@ elementHandlers["Element.getElement"] = async (ctx, params) => {
2363
2374
  elementHandlers["Element.getElements"] = async (ctx, params) => {
2364
2375
  const parentRef = getElementRef(params);
2365
2376
  const selector = params.selector;
2366
- const escaped = selector.replace(/'/g, "\\'");
2367
- const items = await evalInSim(ctx, inIframe(`
2368
- ${buildElAccess(parentRef, "parent")}
2377
+ const items = await evalInElement(ctx, parentRef, `
2369
2378
  if (!parent) return []
2370
- return Array.from(parent.querySelectorAll('${escaped}')).map(el => ({
2379
+ return Array.from(parent.querySelectorAll(${JSON.stringify(selector)})).map(el => ({
2371
2380
  tagName: el.tagName.toLowerCase(),
2372
2381
  }))
2373
- `));
2382
+ `, "parent");
2374
2383
  const combinedSelector = `${parentRef.selector} ${selector}`;
2375
2384
  return {
2376
2385
  elements: items.map((item, i) => ({
@@ -2556,7 +2565,7 @@ import CDP from "chrome-remote-interface";
2556
2565
  var SIMULATOR_URL_PATTERN = "localhost:7788";
2557
2566
  var MAX_BUFFER = 500;
2558
2567
  var RECONNECT_INTERVAL_MS = 3e3;
2559
- var cdpPort = 9222;
2568
+ var cdpPort = DEFAULT_CDP_PORT;
2560
2569
  var targets = {
2561
2570
  simulator: { client: null, connected: false, timer: null, consoleLogs: [], networkRequests: [] },
2562
2571
  workbench: { client: null, connected: false, timer: null, consoleLogs: [], networkRequests: [] }
@@ -3174,16 +3183,20 @@ function startMcpServer(resolvedCdpPort, mcpPort) {
3174
3183
  httpServer.on("error", (err) => {
3175
3184
  if (err.code === "EADDRINUSE") {
3176
3185
  console.warn(`[MCP] Port ${mcpPort} already in use \u2014 MCP server not started`);
3186
+ recordMcpFailed("port-in-use");
3177
3187
  } else {
3178
3188
  console.error("[MCP] Server error:", err);
3189
+ recordMcpFailed(err.message);
3179
3190
  }
3180
3191
  });
3181
3192
  httpServer.listen(mcpPort, "127.0.0.1", () => {
3182
3193
  console.log(`[MCP] SSE server listening on http://127.0.0.1:${mcpPort}/sse`);
3194
+ recordMcpStarted(mcpPort);
3183
3195
  });
3184
3196
  return toDisposable(() => {
3185
3197
  transports.clear();
3186
3198
  httpServer.close();
3199
+ recordMcpStopped();
3187
3200
  });
3188
3201
  }
3189
3202
 
@@ -3316,13 +3329,7 @@ function setupSimulatorStorage(host, options) {
3316
3329
  }
3317
3330
  return { securityOrigin: cachedOrigin, isLocalStorage: true };
3318
3331
  }
3319
- function pushSyntheticEvent(evt) {
3320
- if (host.isDestroyed()) return;
3321
- const prefix = activePrefix();
3322
- if (prefix && !evt.key.startsWith(prefix)) return;
3323
- host.send(SimulatorStorageChannel.Event, evt);
3324
- }
3325
- async function setItem(key, value) {
3332
+ async function withStorageWrite(fn) {
3326
3333
  await ensureAttached();
3327
3334
  if (!attachedWc || attachedWc.isDestroyed()) {
3328
3335
  return { ok: false, error: "simulator not attached" };
@@ -3330,92 +3337,74 @@ function setupSimulatorStorage(host, options) {
3330
3337
  const storageId = await getStorageId();
3331
3338
  if (!storageId) return { ok: false, error: "failed to resolve simulator origin" };
3332
3339
  try {
3333
- await attachedWc.debugger.sendCommand("DOMStorage.setDOMStorageItem", {
3334
- storageId,
3335
- key,
3336
- value
3337
- });
3338
- pushSyntheticEvent({ type: "added", key, newValue: value });
3339
- return { ok: true };
3340
+ return await fn({ wc: attachedWc, storageId });
3340
3341
  } catch (e) {
3341
3342
  return { ok: false, error: e.message };
3342
3343
  }
3343
3344
  }
3344
- async function removeItem(key) {
3345
+ async function withStorageRead(fallback, fn) {
3345
3346
  await ensureAttached();
3346
- if (!attachedWc || attachedWc.isDestroyed()) {
3347
- return { ok: false, error: "simulator not attached" };
3348
- }
3349
- const storageId = await getStorageId();
3350
- if (!storageId) return { ok: false, error: "failed to resolve simulator origin" };
3347
+ if (!attachedWc || attachedWc.isDestroyed()) return fallback;
3351
3348
  try {
3352
- await attachedWc.debugger.sendCommand("DOMStorage.removeDOMStorageItem", {
3353
- storageId,
3354
- key
3355
- });
3349
+ const storageId = await getStorageId();
3350
+ if (!storageId) return fallback;
3351
+ return await fn({ wc: attachedWc, storageId });
3352
+ } catch {
3353
+ return fallback;
3354
+ }
3355
+ }
3356
+ function pushSyntheticEvent(evt) {
3357
+ if (host.isDestroyed()) return;
3358
+ const prefix = activePrefix();
3359
+ if (prefix && !evt.key.startsWith(prefix)) return;
3360
+ host.send(SimulatorStorageChannel.Event, evt);
3361
+ }
3362
+ async function setItem(key, value) {
3363
+ return withStorageWrite(async ({ wc, storageId }) => {
3364
+ await wc.debugger.sendCommand("DOMStorage.setDOMStorageItem", { storageId, key, value });
3365
+ pushSyntheticEvent({ type: "added", key, newValue: value });
3366
+ return { ok: true };
3367
+ });
3368
+ }
3369
+ async function removeItem(key) {
3370
+ return withStorageWrite(async ({ wc, storageId }) => {
3371
+ await wc.debugger.sendCommand("DOMStorage.removeDOMStorageItem", { storageId, key });
3356
3372
  pushSyntheticEvent({ type: "removed", key });
3357
3373
  return { ok: true };
3358
- } catch (e) {
3359
- return { ok: false, error: e.message };
3360
- }
3374
+ });
3361
3375
  }
3362
3376
  async function clearScoped() {
3363
- await ensureAttached();
3364
- if (!attachedWc || attachedWc.isDestroyed()) {
3365
- return { ok: false, error: "simulator not attached" };
3366
- }
3367
- const storageId = await getStorageId();
3368
- if (!storageId) return { ok: false, error: "failed to resolve simulator origin" };
3369
- const prefix = activePrefix();
3370
- try {
3377
+ return withStorageWrite(async ({ wc, storageId }) => {
3378
+ const prefix = activePrefix();
3371
3379
  if (!prefix) {
3372
- await attachedWc.debugger.sendCommand("DOMStorage.clear", { storageId });
3380
+ await wc.debugger.sendCommand("DOMStorage.clear", { storageId });
3373
3381
  return { ok: true };
3374
3382
  }
3375
- const result = await attachedWc.debugger.sendCommand("DOMStorage.getDOMStorageItems", {
3383
+ const result = await wc.debugger.sendCommand("DOMStorage.getDOMStorageItems", {
3376
3384
  storageId
3377
3385
  });
3378
3386
  for (const [key] of result.entries) {
3379
3387
  if (!key.startsWith(prefix)) continue;
3380
- await attachedWc.debugger.sendCommand("DOMStorage.removeDOMStorageItem", {
3381
- storageId,
3382
- key
3383
- });
3388
+ await wc.debugger.sendCommand("DOMStorage.removeDOMStorageItem", { storageId, key });
3384
3389
  }
3385
3390
  return { ok: true };
3386
- } catch (e) {
3387
- return { ok: false, error: e.message };
3388
- }
3391
+ });
3389
3392
  }
3390
3393
  async function clearAll() {
3391
- await ensureAttached();
3392
- if (!attachedWc || attachedWc.isDestroyed()) {
3393
- return { ok: false, error: "simulator not attached" };
3394
- }
3395
- const storageId = await getStorageId();
3396
- if (!storageId) return { ok: false, error: "failed to resolve simulator origin" };
3397
- try {
3398
- await attachedWc.debugger.sendCommand("DOMStorage.clear", { storageId });
3394
+ return withStorageWrite(async ({ wc, storageId }) => {
3395
+ await wc.debugger.sendCommand("DOMStorage.clear", { storageId });
3399
3396
  return { ok: true };
3400
- } catch (e) {
3401
- return { ok: false, error: e.message };
3402
- }
3397
+ });
3403
3398
  }
3404
3399
  async function getSnapshot() {
3405
- await ensureAttached();
3406
- if (!attachedWc || attachedWc.isDestroyed()) return [];
3407
- try {
3408
- const storageId = await getStorageId();
3409
- if (!storageId) return [];
3410
- const result = await attachedWc.debugger.sendCommand("DOMStorage.getDOMStorageItems", {
3400
+ return withStorageRead([], async ({ wc, storageId }) => {
3401
+ const result = await wc.debugger.sendCommand("DOMStorage.getDOMStorageItems", {
3411
3402
  storageId
3412
3403
  });
3413
3404
  const prefix = activePrefix();
3414
3405
  const filtered = prefix ? result.entries.filter(([key]) => key.startsWith(prefix)) : result.entries;
3415
3406
  return filtered.map(([key, value]) => ({ key, value }));
3416
- } catch {
3417
- return [];
3418
- }
3407
+ });
3419
3408
  }
3420
3409
  for (const wc of wcStatic.getAllWebContents()) {
3421
3410
  if (isSimulatorWebview(wc)) {
@@ -3729,7 +3718,7 @@ function enableDevRendererAutoReload(rendererDir2) {
3729
3718
  const watcher = fs7.watch(rendererDir2, { recursive: true }, () => {
3730
3719
  if (reloadTimer) clearTimeout(reloadTimer);
3731
3720
  reloadTimer = setTimeout(() => {
3732
- for (const win of BrowserWindow3.getAllWindows()) {
3721
+ for (const win of BrowserWindow4.getAllWindows()) {
3733
3722
  win.webContents.reload();
3734
3723
  }
3735
3724
  }, 300);
@@ -3754,6 +3743,7 @@ function createWorkbenchApp(config = {}) {
3754
3743
  const mainWindow = createConfiguredMainWindow(config, rendererDir2);
3755
3744
  const context = createContext(config, mainWindow, rendererDir2);
3756
3745
  context.registry.add(registerAppIpc(context));
3746
+ context.registry.add(installThemeBackgroundSync());
3757
3747
  registerBuiltinModules(config, context);
3758
3748
  installMenu(config, mainWindow, context);
3759
3749
  const instance = {
@@ -3811,10 +3801,10 @@ function createWorkbenchApp(config = {}) {
3811
3801
  }
3812
3802
 
3813
3803
  // src/main/windows/settings-window/create.ts
3814
- import { BrowserWindow as BrowserWindow4 } from "electron";
3804
+ import { BrowserWindow as BrowserWindow5 } from "electron";
3815
3805
  import path13 from "path";
3816
3806
  async function createSettingsWindow(parent, rendererDir2) {
3817
- const win = new BrowserWindow4({
3807
+ const win = new BrowserWindow5({
3818
3808
  width: 420,
3819
3809
  height: 560,
3820
3810
  minWidth: 380,
@@ -3846,18 +3836,17 @@ function launch(config = {}) {
3846
3836
  return createWorkbenchApp(config).start();
3847
3837
  }
3848
3838
  async function openSettingsWindow(ctx) {
3849
- if (!ctx.workbenchSettingsWindow || ctx.workbenchSettingsWindow.isDestroyed()) {
3850
- const win = await createSettingsWindow(ctx.mainWindow, ctx.rendererDir);
3851
- ctx.workbenchSettingsWindow = win;
3839
+ let win = ctx.windows.settingsWindow;
3840
+ if (!win || win.isDestroyed()) {
3841
+ win = await createSettingsWindow(ctx.windows.mainWindow, ctx.rendererDir);
3852
3842
  ctx.windows.setSettingsWindow(win);
3853
3843
  wireSettingsWindowEvents(win, () => {
3854
- ctx.workbenchSettingsWindow = null;
3855
3844
  ctx.windows.setSettingsWindow(null);
3856
3845
  });
3857
3846
  }
3858
- ctx.workbenchSettingsWindow.show();
3859
- ctx.workbenchSettingsWindow.focus();
3860
- ctx.notify.workbenchSettingsInit(ctx.workbenchSettingsWindow, {
3847
+ win.show();
3848
+ win.focus();
3849
+ ctx.notify.workbenchSettingsInit(win, {
3861
3850
  settings: loadWorkbenchSettings()
3862
3851
  });
3863
3852
  }