@dimina-kit/devtools 0.3.2-dev.20260611135124 → 0.4.0-dev.20260612152115

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 (58) hide show
  1. package/README.md +11 -9
  2. package/dist/main/api.d.ts +1 -1
  3. package/dist/main/app/app.js +22 -12
  4. package/dist/main/app/launch.d.ts +4 -4
  5. package/dist/main/app/launch.js +5 -17
  6. package/dist/main/index.bundle.js +372 -225
  7. package/dist/main/ipc/bridge-router.js +35 -8
  8. package/dist/main/ipc/views.js +10 -1
  9. package/dist/main/menu/index.d.ts +2 -2
  10. package/dist/main/menu/index.js +4 -2
  11. package/dist/main/runtime/miniapp-runtime.d.ts +79 -11
  12. package/dist/main/services/notifications/renderer-notifier.d.ts +11 -0
  13. package/dist/main/services/notifications/renderer-notifier.js +3 -0
  14. package/dist/main/services/views/host-toolbar-port-channel.d.ts +10 -0
  15. package/dist/main/services/views/host-toolbar-port-channel.js +89 -10
  16. package/dist/main/services/views/view-manager.d.ts +28 -4
  17. package/dist/main/services/views/view-manager.js +42 -8
  18. package/dist/main/services/workbench-context.d.ts +7 -0
  19. package/dist/main/services/workbench-context.js +5 -0
  20. package/dist/main/services/workspace/workspace-service.d.ts +3 -7
  21. package/dist/main/services/workspace/workspace-service.js +66 -0
  22. package/dist/main/windows/settings-window/index.d.ts +33 -0
  23. package/dist/main/windows/settings-window/index.js +59 -0
  24. package/dist/preload/index.d.ts +2 -2
  25. package/dist/preload/index.js +2 -2
  26. package/dist/preload/runtime/host-toolbar-port.d.ts +10 -5
  27. package/dist/preload/runtime/host-toolbar-port.js +24 -0
  28. package/dist/preload/windows/host-toolbar-runtime.cjs +20 -1
  29. package/dist/preload/windows/host-toolbar-runtime.cjs.map +2 -2
  30. package/dist/preload/windows/main.cjs +9 -7
  31. package/dist/preload/windows/main.cjs.map +2 -2
  32. package/dist/preload/windows/simulator.cjs.map +1 -1
  33. package/dist/renderer/assets/index-Bi_6SS-L.js +50 -0
  34. package/dist/renderer/assets/{input-6fpCXV-n.js → input-DgqX2UvN.js} +2 -2
  35. package/dist/renderer/assets/{ipc-transport-D22e4dv7.js → ipc-transport-CsFhDJ7q.js} +2 -2
  36. package/dist/renderer/assets/ipc-transport-SnOAk0Jq.css +1 -0
  37. package/dist/renderer/assets/{popover-CdGdKkkc.js → popover-D_2o_52M.js} +2 -2
  38. package/dist/renderer/assets/{select-X0sbjwdP.js → select-BJYsdghn.js} +2 -2
  39. package/dist/renderer/assets/{settings-CS4jYaCw.js → settings-CivkfizP.js} +2 -2
  40. package/dist/renderer/assets/{settings-api-CCWTAao_.js → settings-api-7mXkBy5-.js} +2 -2
  41. package/dist/renderer/assets/{workbenchSettings-Bpzaj3bm.js → workbenchSettings-BwpwxuzM.js} +2 -2
  42. package/dist/renderer/entries/main/index.html +6 -6
  43. package/dist/renderer/entries/popover/index.html +5 -5
  44. package/dist/renderer/entries/settings/index.html +5 -5
  45. package/dist/renderer/entries/workbench-settings/index.html +4 -4
  46. package/dist/shared/ipc-channels.d.ts +11 -0
  47. package/dist/shared/ipc-channels.js +14 -0
  48. package/dist/shared/types.d.ts +48 -11
  49. package/dist/simulator/assets/bridge-channels-BUQ5AbvJ.js +2 -0
  50. package/dist/simulator/assets/device-shell-BEnc2k-W.js +2 -0
  51. package/dist/simulator/assets/{simulator-DHylZf9Z.js → simulator-cV1PERi6.js} +3 -3
  52. package/dist/simulator/assets/simulator-mini-app-CYtW6bMd.js +2 -0
  53. package/dist/simulator/simulator.html +1 -1
  54. package/package.json +4 -4
  55. package/dist/renderer/assets/index-DLzjz1wv.js +0 -50
  56. package/dist/renderer/assets/ipc-transport-Bs8Sf1B2.css +0 -1
  57. package/dist/simulator/assets/device-shell-fH2cI-3q.js +0 -2
  58. package/dist/simulator/assets/simulator-mini-app-BDNu5n26.js +0 -2
@@ -374,6 +374,10 @@ var ProjectChannel = {
374
374
  GetCompileConfig: "project:getCompileConfig",
375
375
  SaveCompileConfig: "project:saveCompileConfig",
376
376
  Status: "project:status",
377
+ // Main → renderer push of per-line dmcc compile logs (devkit `onLog`).
378
+ // Dedicated channel: `project:status` keeps its one-event-per-payload
379
+ // contract (compileEvents), this one carries the line stream (compileLogs).
380
+ CompileLog: "project:compileLog",
377
381
  CaptureThumbnail: "project:captureThumbnail",
378
382
  GetThumbnail: "project:getThumbnail"
379
383
  };
@@ -440,7 +444,17 @@ var ViewChannel = {
440
444
  * main → main-window renderer: push the reserved host-toolbar height so the
441
445
  * renderer placeholder div resizes (closing the dynamic-height loop).
442
446
  */
443
- HostToolbarHeightChanged: "view:host-toolbar:height-changed"
447
+ HostToolbarHeightChanged: "view:host-toolbar:height-changed",
448
+ /**
449
+ * main ← main-window renderer (invoke): pull the last NOTIFIED toolbar
450
+ * height retained in main. Mount-time replay companion to
451
+ * `HostToolbarHeightChanged`: the push listener mounts with the project
452
+ * view and the toolbar's size-advertiser deduplicates (never re-reports),
453
+ * so a height pushed while no project view was mounted would otherwise be
454
+ * lost forever (cold start on the project list races it; close-project →
455
+ * reopen hits it deterministically). No payload; resolves a number.
456
+ */
457
+ HostToolbarGetHeight: "view:host-toolbar:get-height"
444
458
  };
445
459
  var PopoverChannel = {
446
460
  Show: "popover:show",
@@ -740,9 +754,9 @@ function registerProjectFsIpc(ctx) {
740
754
  }
741
755
 
742
756
  // src/main/app/app.ts
743
- import { app as app14, BrowserWindow as BrowserWindow7, nativeImage, session as session4 } from "electron";
757
+ import { app as app14, BrowserWindow as BrowserWindow8, nativeImage, session as session4 } from "electron";
744
758
  import fs10 from "fs";
745
- import path20 from "path";
759
+ import path21 from "path";
746
760
 
747
761
  // src/main/utils/paths.ts
748
762
  import fs3 from "fs";
@@ -966,6 +980,9 @@ function createRendererNotifier(ctx) {
966
980
  projectStatus(payload) {
967
981
  sendToMain(ProjectChannel.Status, payload);
968
982
  },
983
+ compileLog(payload) {
984
+ sendToMain(ProjectChannel.CompileLog, payload);
985
+ },
969
986
  windowNavigateBack() {
970
987
  sendToMain(WindowChannel.NavigateBack);
971
988
  },
@@ -1655,8 +1672,10 @@ import { MessageChannelMain } from "electron";
1655
1672
  function createHostToolbarPortChannel(opts) {
1656
1673
  let activePort = null;
1657
1674
  let activeWc = null;
1658
- let disposed = false;
1675
+ let state = "absent";
1676
+ let generation = 0;
1659
1677
  const handlers2 = [];
1678
+ const readyHandlers = [];
1660
1679
  function dispatch(data) {
1661
1680
  if (typeof data !== "object" || data === null) return;
1662
1681
  const { channel, payload } = data;
@@ -1665,10 +1684,11 @@ function createHostToolbarPortChannel(opts) {
1665
1684
  if (entry.channel === channel) entry.handler(payload);
1666
1685
  }
1667
1686
  }
1668
- function dropActivePort(close) {
1687
+ function dropActivePort(close, next) {
1669
1688
  const port = activePort;
1670
1689
  activePort = null;
1671
1690
  activeWc = null;
1691
+ if (state !== "disposed") state = next;
1672
1692
  if (close && port) {
1673
1693
  try {
1674
1694
  port.close();
@@ -1676,21 +1696,36 @@ function createHostToolbarPortChannel(opts) {
1676
1696
  }
1677
1697
  }
1678
1698
  }
1699
+ function invokeReadyHandler(handler) {
1700
+ try {
1701
+ handler();
1702
+ } catch (err) {
1703
+ console.error("[host-toolbar] onReady handler threw:", err);
1704
+ }
1705
+ }
1706
+ function fireReadyHandlers() {
1707
+ for (const entry of [...readyHandlers]) {
1708
+ if (readyHandlers.includes(entry)) invokeReadyHandler(entry.handler);
1709
+ }
1710
+ }
1679
1711
  function handshake(wc) {
1680
- if (disposed) return;
1712
+ if (state === "disposed") return;
1681
1713
  if (!opts.isCurrent(wc)) return;
1682
- dropActivePort(true);
1714
+ dropActivePort(true, "awaitingHandshake");
1683
1715
  const { port1, port2 } = new MessageChannelMain();
1684
1716
  port1.on("message", (event) => {
1685
1717
  dispatch(event.data);
1686
1718
  });
1687
1719
  port1.on("close", () => {
1688
- if (activePort === port1) dropActivePort(false);
1720
+ if (activePort === port1) dropActivePort(false, "absent");
1689
1721
  });
1690
1722
  wc.postMessage(ViewChannel.HostToolbarPort, null, [port2]);
1691
1723
  port1.start();
1692
1724
  activePort = port1;
1693
1725
  activeWc = wc;
1726
+ state = "ready";
1727
+ generation++;
1728
+ fireReadyHandlers();
1694
1729
  }
1695
1730
  return {
1696
1731
  attach(wc) {
@@ -1702,15 +1737,15 @@ function createHostToolbarPortChannel(opts) {
1702
1737
  const isSameDocument = typeof details?.isSameDocument === "boolean" ? details.isSameDocument : isInPlace;
1703
1738
  const isMainFrame = typeof details?.isMainFrame === "boolean" ? details.isMainFrame : isMainFramePositional;
1704
1739
  if (isSameDocument || !isMainFrame) return;
1705
- dropActivePort(true);
1740
+ dropActivePort(true, "awaitingHandshake");
1706
1741
  }
1707
1742
  );
1708
1743
  wc.on("destroyed", () => {
1709
- if (activeWc === wc) dropActivePort(true);
1744
+ if (activeWc === wc) dropActivePort(true, "absent");
1710
1745
  });
1711
1746
  },
1712
1747
  invalidate() {
1713
- dropActivePort(true);
1748
+ dropActivePort(true, "awaitingHandshake");
1714
1749
  },
1715
1750
  onMessage(channel, handler) {
1716
1751
  if (typeof channel !== "string" || channel === "") {
@@ -1727,15 +1762,38 @@ function createHostToolbarPortChannel(opts) {
1727
1762
  }
1728
1763
  };
1729
1764
  },
1765
+ onReady(handler) {
1766
+ if (state === "disposed") {
1767
+ return { dispose() {
1768
+ } };
1769
+ }
1770
+ const entry = { handler };
1771
+ readyHandlers.push(entry);
1772
+ if (state === "ready") {
1773
+ const scheduledGeneration = generation;
1774
+ queueMicrotask(() => {
1775
+ if (state !== "ready" || generation !== scheduledGeneration || !activePort) return;
1776
+ if (!readyHandlers.includes(entry)) return;
1777
+ invokeReadyHandler(entry.handler);
1778
+ });
1779
+ }
1780
+ return {
1781
+ dispose() {
1782
+ const i = readyHandlers.indexOf(entry);
1783
+ if (i >= 0) readyHandlers.splice(i, 1);
1784
+ }
1785
+ };
1786
+ },
1730
1787
  send(channel, payload) {
1731
1788
  if (!activePort) return false;
1732
1789
  activePort.postMessage({ channel, payload });
1733
1790
  return true;
1734
1791
  },
1735
1792
  dispose() {
1736
- disposed = true;
1737
- dropActivePort(true);
1793
+ dropActivePort(true, "absent");
1794
+ state = "disposed";
1738
1795
  handlers2.length = 0;
1796
+ readyHandlers.length = 0;
1739
1797
  }
1740
1798
  };
1741
1799
  }
@@ -1800,6 +1858,7 @@ function createViewManager(ctx) {
1800
1858
  let hostToolbarViewAdded = false;
1801
1859
  let hostToolbarRuntimeAcquired = false;
1802
1860
  let hostToolbarHeightMode = "auto";
1861
+ let hostToolbarLastHeight = 0;
1803
1862
  const hostToolbarPort = createHostToolbarPortChannel({
1804
1863
  isCurrent: (wc) => liveHostToolbarWebContents() === wc
1805
1864
  });
@@ -1902,9 +1961,13 @@ function createViewManager(ctx) {
1902
1961
  }
1903
1962
  view.setBounds(bounds);
1904
1963
  }
1964
+ function notifyHostToolbarHeight(height) {
1965
+ hostToolbarLastHeight = height;
1966
+ ctx.notify.hostToolbarHeightChanged(height);
1967
+ }
1905
1968
  function setHostToolbarHeight(extent) {
1906
1969
  if (hostToolbarHeightMode !== "auto") return;
1907
- ctx.notify.hostToolbarHeightChanged(extent);
1970
+ notifyHostToolbarHeight(extent);
1908
1971
  }
1909
1972
  function hideHostToolbar() {
1910
1973
  if (hostToolbarView && hostToolbarViewAdded && !ctx.windows.mainWindow.isDestroyed()) {
@@ -1914,7 +1977,7 @@ function createViewManager(ctx) {
1914
1977
  }
1915
1978
  }
1916
1979
  hostToolbarViewAdded = false;
1917
- ctx.notify.hostToolbarHeightChanged(0);
1980
+ notifyHostToolbarHeight(0);
1918
1981
  }
1919
1982
  const hostToolbar = {
1920
1983
  async loadURL(url) {
@@ -1937,14 +2000,22 @@ function createViewManager(ctx) {
1937
2000
  hostToolbarPreloadOverride = path22;
1938
2001
  },
1939
2002
  setHeightMode(mode) {
2003
+ if (mode !== "auto" && !(Number.isFinite(mode.fixed) && mode.fixed >= 0)) {
2004
+ throw new TypeError(
2005
+ `hostToolbar.setHeightMode: fixed height must be a finite, non-negative number (got ${mode.fixed})`
2006
+ );
2007
+ }
1940
2008
  hostToolbarHeightMode = mode;
1941
2009
  if (mode !== "auto") {
1942
- ctx.notify.hostToolbarHeightChanged(mode.fixed);
2010
+ notifyHostToolbarHeight(mode.fixed);
1943
2011
  }
1944
2012
  },
1945
2013
  onMessage(channel, handler) {
1946
2014
  return hostToolbarPort.onMessage(channel, handler);
1947
2015
  },
2016
+ onReady(handler) {
2017
+ return hostToolbarPort.onReady(handler);
2018
+ },
1948
2019
  send(channel, payload) {
1949
2020
  return hostToolbarPort.send(channel, payload);
1950
2021
  }
@@ -2497,6 +2568,7 @@ function createViewManager(ctx) {
2497
2568
  return popoverView.webContents.id;
2498
2569
  },
2499
2570
  getHostToolbarWebContentsId: () => liveHostToolbarWebContents()?.id ?? null,
2571
+ getHostToolbarHeight: () => hostToolbarLastHeight,
2500
2572
  setNativeSimulatorViewBounds,
2501
2573
  resize,
2502
2574
  setSimulatorDevtoolsBounds,
@@ -2532,13 +2604,82 @@ function createWindowService(mainWindow) {
2532
2604
  };
2533
2605
  }
2534
2606
 
2607
+ // src/main/windows/settings-window/create.ts
2608
+ import { BrowserWindow as BrowserWindow3 } from "electron";
2609
+ import path8 from "path";
2610
+ async function createSettingsWindow(parent, rendererDir2) {
2611
+ const win = new BrowserWindow3({
2612
+ width: 420,
2613
+ height: 560,
2614
+ minWidth: 380,
2615
+ minHeight: 480,
2616
+ parent: parent ?? void 0,
2617
+ title: "\u5F00\u53D1\u5DE5\u5177\u8BBE\u7F6E",
2618
+ backgroundColor: themeBg(),
2619
+ webPreferences: {
2620
+ nodeIntegration: false,
2621
+ contextIsolation: true,
2622
+ sandbox: false,
2623
+ preload: mainPreloadPath
2624
+ }
2625
+ });
2626
+ applyNavigationHardening(win.webContents, rendererDir2);
2627
+ await win.loadFile(path8.join(rendererDir2, "entries/workbench-settings/index.html"));
2628
+ return win;
2629
+ }
2630
+
2631
+ // src/main/windows/settings-window/events.ts
2632
+ function wireSettingsWindowEvents(win, onClosed) {
2633
+ win.on("closed", () => {
2634
+ onClosed();
2635
+ });
2636
+ }
2637
+
2638
+ // src/main/windows/settings-window/index.ts
2639
+ var inFlightCreations = /* @__PURE__ */ new WeakMap();
2640
+ async function getOrCreateSettingsWindow(deps) {
2641
+ const existing = deps.windows.settingsWindow;
2642
+ if (existing && !existing.isDestroyed()) return existing;
2643
+ const inFlight = inFlightCreations.get(deps.windows);
2644
+ if (inFlight) return inFlight;
2645
+ const creation = (async () => {
2646
+ const win = await createSettingsWindow(deps.windows.mainWindow, deps.rendererDir);
2647
+ deps.windows.setSettingsWindow(win);
2648
+ wireSettingsWindowEvents(win, () => {
2649
+ if (deps.windows.settingsWindow === win) {
2650
+ deps.windows.setSettingsWindow(null);
2651
+ }
2652
+ });
2653
+ return win;
2654
+ })();
2655
+ inFlightCreations.set(deps.windows, creation);
2656
+ try {
2657
+ return await creation;
2658
+ } finally {
2659
+ if (inFlightCreations.get(deps.windows) === creation) {
2660
+ inFlightCreations.delete(deps.windows);
2661
+ }
2662
+ }
2663
+ }
2664
+ async function openSettingsWindow(deps) {
2665
+ const win = await getOrCreateSettingsWindow(deps);
2666
+ win.show();
2667
+ win.focus();
2668
+ deps.notify.workbenchSettingsInit(win, {
2669
+ settings: loadWorkbenchSettings()
2670
+ });
2671
+ }
2672
+
2673
+ // src/main/services/workspace/workspace-service.ts
2674
+ import { z as z2 } from "zod";
2675
+
2535
2676
  // src/main/services/projects/project-repository.ts
2536
2677
  import { app as app5 } from "electron";
2537
- import path8 from "path";
2678
+ import path9 from "path";
2538
2679
  import fs4 from "fs";
2539
2680
  var log2 = createLogger("projects");
2540
2681
  function getProjectsFile() {
2541
- return path8.join(app5.getPath("userData"), "dimina-projects.json");
2682
+ return path9.join(app5.getPath("userData"), "dimina-projects.json");
2542
2683
  }
2543
2684
  function load() {
2544
2685
  try {
@@ -2560,13 +2701,13 @@ function validateProjectDir(dirPath) {
2560
2701
  if (!fs4.existsSync(dirPath)) {
2561
2702
  return `\u5C0F\u7A0B\u5E8F\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${dirPath}`;
2562
2703
  }
2563
- if (!fs4.existsSync(path8.join(dirPath, "app.json"))) {
2564
- const configPath = path8.join(dirPath, "project.config.json");
2704
+ if (!fs4.existsSync(path9.join(dirPath, "app.json"))) {
2705
+ const configPath = path9.join(dirPath, "project.config.json");
2565
2706
  if (fs4.existsSync(configPath)) {
2566
2707
  try {
2567
2708
  const cfg = JSON.parse(fs4.readFileSync(configPath, "utf-8"));
2568
2709
  if (cfg.miniprogramRoot) {
2569
- const resolvedRoot = path8.resolve(dirPath, cfg.miniprogramRoot);
2710
+ const resolvedRoot = path9.resolve(dirPath, cfg.miniprogramRoot);
2570
2711
  return `\u8BE5\u76EE\u5F55\u7F3A\u5C11 app.json\uFF0Cproject.config.json \u4E2D\u6307\u5B9A\u4E86 miniprogramRoot: "${cfg.miniprogramRoot}"\uFF0C\u8BF7\u5BFC\u5165 ${resolvedRoot}`;
2571
2712
  }
2572
2713
  } catch (err) {
@@ -2579,9 +2720,9 @@ function validateProjectDir(dirPath) {
2579
2720
  }
2580
2721
  function addProject(dirPath) {
2581
2722
  const projects = load();
2582
- let name = path8.basename(dirPath);
2723
+ let name = path9.basename(dirPath);
2583
2724
  try {
2584
- const configPath = path8.join(dirPath, "project.config.json");
2725
+ const configPath = path9.join(dirPath, "project.config.json");
2585
2726
  if (fs4.existsSync(configPath)) {
2586
2727
  const cfg = JSON.parse(fs4.readFileSync(configPath, "utf-8"));
2587
2728
  if (cfg.projectname) name = cfg.projectname;
@@ -2620,7 +2761,7 @@ function getCompileConfig(dirPath) {
2620
2761
  };
2621
2762
  }
2622
2763
  function getProjectPages(dirPath) {
2623
- const appJsonPath = path8.join(dirPath, "app.json");
2764
+ const appJsonPath = path9.join(dirPath, "app.json");
2624
2765
  try {
2625
2766
  const appJson = JSON.parse(
2626
2767
  fs4.readFileSync(appJsonPath, "utf-8")
@@ -2646,7 +2787,7 @@ function getProjectSettings(projectPath) {
2646
2787
  return { uploadWithSourceMap: false };
2647
2788
  }
2648
2789
  try {
2649
- const configPath = path8.join(projectPath, "project.config.json");
2790
+ const configPath = path9.join(projectPath, "project.config.json");
2650
2791
  const config = JSON.parse(fs4.readFileSync(configPath, "utf-8"));
2651
2792
  return {
2652
2793
  uploadWithSourceMap: !!config.setting?.uploadWithSourceMap
@@ -2657,7 +2798,7 @@ function getProjectSettings(projectPath) {
2657
2798
  }
2658
2799
  function updateProjectSettings(projectPath, patch) {
2659
2800
  if (!projectPath) return;
2660
- const configPath = path8.join(projectPath, "project.config.json");
2801
+ const configPath = path9.join(projectPath, "project.config.json");
2661
2802
  let config = {};
2662
2803
  try {
2663
2804
  config = JSON.parse(fs4.readFileSync(configPath, "utf-8"));
@@ -2700,10 +2841,12 @@ function getSimulatorServicewechatReferer() {
2700
2841
  }
2701
2842
 
2702
2843
  // src/main/services/workspace/workspace-service.ts
2844
+ var SessionAppInfoSchema = z2.looseObject({ appId: z2.string() });
2703
2845
  function createWorkspaceService(ctx) {
2704
2846
  let currentSession = null;
2705
2847
  let currentProjectPath = "";
2706
2848
  let lastClosedProjectPath = "";
2849
+ let logGeneration = 0;
2707
2850
  function sendStatus(status2, message, hotReload) {
2708
2851
  ctx.notify.projectStatus(hotReload ? { status: status2, message, hotReload: true } : { status: status2, message });
2709
2852
  }
@@ -2752,6 +2895,7 @@ function createWorkspaceService(ctx) {
2752
2895
  validateProjectDir: async (dirPath) => provider.validateProjectDir ? provider.validateProjectDir(dirPath) : null,
2753
2896
  async openProject(projectPath) {
2754
2897
  clearSimulatorServicewechatReferer();
2898
+ logGeneration++;
2755
2899
  await disposeSession();
2756
2900
  currentProjectPath = "";
2757
2901
  lastClosedProjectPath = "";
@@ -2762,6 +2906,7 @@ function createWorkspaceService(ctx) {
2762
2906
  }
2763
2907
  sendStatus("compiling", "\u6B63\u5728\u7F16\u8BD1...");
2764
2908
  const { compile } = loadWorkbenchSettings();
2909
+ const sessionGeneration = ++logGeneration;
2765
2910
  let session5;
2766
2911
  try {
2767
2912
  session5 = await ctx.adapter.openProject({
@@ -2769,13 +2914,40 @@ function createWorkspaceService(ctx) {
2769
2914
  sourcemap: true,
2770
2915
  watch: compile.watch,
2771
2916
  onRebuild: () => sendStatus("ready", "\u7F16\u8BD1\u5B8C\u6210\uFF0C\u5DF2\u70ED\u66F4\u65B0", true),
2772
- onBuildError: (err) => sendStatus("error", String(err))
2917
+ onBuildError: (err) => sendStatus("error", String(err)),
2918
+ // Per-line dmcc log (already filtered in devkit). Stamp the
2919
+ // wall-clock capture time here and push verbatim on the dedicated
2920
+ // compile-log channel — never through projectStatus, whose
2921
+ // one-event-per-payload contract feeds compileEvents. Stale lines
2922
+ // (a closed/replaced session's worker flushing its buffers) are
2923
+ // dropped via the generation check.
2924
+ onLog: (entry) => {
2925
+ if (sessionGeneration !== logGeneration) return;
2926
+ ctx.notify.compileLog({
2927
+ stream: entry.stream,
2928
+ text: entry.text,
2929
+ at: Date.now()
2930
+ });
2931
+ }
2773
2932
  });
2774
2933
  } catch (err) {
2775
2934
  clearSimulatorServicewechatReferer();
2776
2935
  sendStatus("error", String(err));
2777
2936
  return { success: false, error: String(err) };
2778
2937
  }
2938
+ if (!SessionAppInfoSchema.safeParse(session5.appInfo).success) {
2939
+ try {
2940
+ await session5.close();
2941
+ } catch (closeErr) {
2942
+ console.warn(
2943
+ "[workspace] closing appId-less adapter session failed (non-fatal):",
2944
+ closeErr
2945
+ );
2946
+ }
2947
+ const error = "adapter returned session.appInfo without a string appId \u2014 the CompilationAdapter must supply appInfo.appId";
2948
+ sendStatus("error", error);
2949
+ return { success: false, error };
2950
+ }
2779
2951
  currentSession = session5;
2780
2952
  currentProjectPath = projectPath;
2781
2953
  bestEffort("updateLastOpened", () => {
@@ -2790,6 +2962,7 @@ function createWorkspaceService(ctx) {
2790
2962
  };
2791
2963
  },
2792
2964
  async closeProject() {
2965
+ logGeneration++;
2793
2966
  clearSimulatorServicewechatReferer();
2794
2967
  await disposeSession();
2795
2968
  if (currentProjectPath !== "") lastClosedProjectPath = currentProjectPath;
@@ -2839,16 +3012,16 @@ function createWorkspaceService(ctx) {
2839
3012
  // src/main/services/projects/thumbnail.ts
2840
3013
  import { createHash } from "crypto";
2841
3014
  import fs5 from "fs";
2842
- import path9 from "path";
3015
+ import path10 from "path";
2843
3016
  import { app as app6 } from "electron";
2844
3017
  function getThumbnailDir() {
2845
- return path9.join(app6.getPath("userData"), "thumbnails");
3018
+ return path10.join(app6.getPath("userData"), "thumbnails");
2846
3019
  }
2847
3020
  function hashProjectPath(projectPath) {
2848
3021
  return createHash("sha256").update(projectPath).digest("hex").slice(0, 16);
2849
3022
  }
2850
3023
  function getThumbnailPath(projectPath) {
2851
- return path9.join(getThumbnailDir(), `${hashProjectPath(projectPath)}.png`);
3024
+ return path10.join(getThumbnailDir(), `${hashProjectPath(projectPath)}.png`);
2852
3025
  }
2853
3026
  var DATA_URL_PNG_PREFIX = "data:image/png;base64,";
2854
3027
  function saveThumbnailFromDataUrl(projectPath, imageDataUrl) {
@@ -2909,20 +3082,20 @@ function sanitizeTemplates(templates) {
2909
3082
  }
2910
3083
 
2911
3084
  // src/main/services/projects/builtin-templates.ts
2912
- import path10 from "node:path";
2913
- var TEMPLATES_DIR = path10.join(devtoolsPackageRoot, "templates");
3085
+ import path11 from "node:path";
3086
+ var TEMPLATES_DIR = path11.join(devtoolsPackageRoot, "templates");
2914
3087
  var BUILTIN_TEMPLATES = [
2915
3088
  {
2916
3089
  id: "blank",
2917
3090
  name: "Blank",
2918
3091
  description: "\u6700\u5C0F\u9AA8\u67B6\uFF1Aapp.* + \u4E00\u4E2A index \u9875\u9762",
2919
- source: { type: "directory", path: path10.join(TEMPLATES_DIR, "blank") }
3092
+ source: { type: "directory", path: path11.join(TEMPLATES_DIR, "blank") }
2920
3093
  },
2921
3094
  {
2922
3095
  id: "taro-todo",
2923
3096
  name: "Taro Todo",
2924
3097
  description: "Taro \u7F16\u8BD1\u4EA7\u7269\u7684 Todo \u793A\u4F8B",
2925
- source: { type: "directory", path: path10.join(TEMPLATES_DIR, "taro-todo") }
3098
+ source: { type: "directory", path: path11.join(TEMPLATES_DIR, "taro-todo") }
2926
3099
  }
2927
3100
  ];
2928
3101
 
@@ -2943,6 +3116,7 @@ function createWorkbenchContext(opts) {
2943
3116
  ctx.windows = createWindowService(opts.mainWindow);
2944
3117
  ctx.views = createViewManager(ctx);
2945
3118
  ctx.notify = createRendererNotifier(ctx);
3119
+ ctx.openSettings = () => openSettingsWindow(ctx);
2946
3120
  ctx.projectsProvider = opts.projectsProvider ?? createLocalProjectsProvider();
2947
3121
  ctx.projectTemplates = resolveTemplates(
2948
3122
  BUILTIN_TEMPLATES,
@@ -2965,7 +3139,7 @@ function installAppMenu(ctx) {
2965
3139
  {
2966
3140
  label: "\u5F00\u53D1\u5DE5\u5177\u8BBE\u7F6E",
2967
3141
  click: () => {
2968
- void openSettingsWindow(ctx).catch(() => {
3142
+ void ctx.openSettings().catch(() => {
2969
3143
  });
2970
3144
  }
2971
3145
  },
@@ -3041,111 +3215,111 @@ function registerAppIpc(ctx) {
3041
3215
  }
3042
3216
 
3043
3217
  // src/shared/ipc-schemas.ts
3044
- import { z as z2 } from "zod";
3045
- var AbsolutePath = z2.string().min(1).refine(
3218
+ import { z as z3 } from "zod";
3219
+ var AbsolutePath = z3.string().min(1).refine(
3046
3220
  (p) => p.startsWith("/") || /^[a-zA-Z]:\\/.test(p) || /^[a-zA-Z]:\//.test(p),
3047
3221
  "must be an absolute path"
3048
3222
  );
3049
- var ProjectsAddSchema = z2.tuple([AbsolutePath]);
3050
- var ProjectOpenSchema = z2.tuple([AbsolutePath]);
3051
- var CompileConfigShape = z2.looseObject({
3052
- startPage: z2.string().optional(),
3053
- scene: z2.number().optional(),
3054
- queryParams: z2.array(z2.looseObject({ key: z2.string(), value: z2.string() })).optional()
3223
+ var ProjectsAddSchema = z3.tuple([AbsolutePath]);
3224
+ var ProjectOpenSchema = z3.tuple([AbsolutePath]);
3225
+ var CompileConfigShape = z3.looseObject({
3226
+ startPage: z3.string().optional(),
3227
+ scene: z3.number().optional(),
3228
+ queryParams: z3.array(z3.looseObject({ key: z3.string(), value: z3.string() })).optional()
3055
3229
  });
3056
- var ProjectSaveCompileConfigSchema = z2.tuple([
3230
+ var ProjectSaveCompileConfigSchema = z3.tuple([
3057
3231
  AbsolutePath,
3058
3232
  CompileConfigShape
3059
3233
  ]);
3060
- var PopoverShowSchema = z2.tuple([z2.looseObject({})]);
3061
- var SimWidth = z2.number().int().min(100).max(2e3);
3062
- var SimulatorAttachNativeSchema = z2.tuple([
3063
- z2.string().url().refine((u) => u.startsWith("http://") || u.startsWith("https://"), {
3234
+ var PopoverShowSchema = z3.tuple([z3.looseObject({})]);
3235
+ var SimWidth = z3.number().int().min(100).max(2e3);
3236
+ var SimulatorAttachNativeSchema = z3.tuple([
3237
+ z3.string().url().refine((u) => u.startsWith("http://") || u.startsWith("https://"), {
3064
3238
  message: "simulator URL must be http(s)"
3065
3239
  }),
3066
3240
  SimWidth
3067
3241
  ]);
3068
- var SimulatorSetNativeBoundsSchema = z2.tuple([
3069
- z2.object({
3070
- x: z2.number().finite(),
3071
- y: z2.number().finite(),
3072
- width: z2.number().finite(),
3073
- height: z2.number().finite(),
3074
- zoom: z2.number().finite().positive()
3242
+ var SimulatorSetNativeBoundsSchema = z3.tuple([
3243
+ z3.object({
3244
+ x: z3.number().finite(),
3245
+ y: z3.number().finite(),
3246
+ width: z3.number().finite(),
3247
+ height: z3.number().finite(),
3248
+ zoom: z3.number().finite().positive()
3075
3249
  })
3076
3250
  ]);
3077
- var SimulatorSetDeviceInfoSchema = z2.tuple([
3078
- z2.object({
3079
- brand: z2.string().max(64),
3080
- model: z2.string().max(64),
3081
- system: z2.string().max(64),
3082
- platform: z2.string().max(32),
3083
- pixelRatio: z2.number().finite().positive(),
3084
- screenWidth: z2.number().int().min(100).max(4e3),
3085
- screenHeight: z2.number().int().min(100).max(4e3),
3086
- statusBarHeight: z2.number().finite().min(0).max(400),
3087
- safeAreaBottom: z2.number().finite().min(0).max(400),
3088
- notchType: z2.enum(["none", "notch", "dynamic-island"]),
3089
- safeAreaInsets: z2.object({
3090
- top: z2.number().finite().min(0).max(400),
3091
- right: z2.number().finite().min(0).max(400),
3092
- bottom: z2.number().finite().min(0).max(400),
3093
- left: z2.number().finite().min(0).max(400)
3251
+ var SimulatorSetDeviceInfoSchema = z3.tuple([
3252
+ z3.object({
3253
+ brand: z3.string().max(64),
3254
+ model: z3.string().max(64),
3255
+ system: z3.string().max(64),
3256
+ platform: z3.string().max(32),
3257
+ pixelRatio: z3.number().finite().positive(),
3258
+ screenWidth: z3.number().int().min(100).max(4e3),
3259
+ screenHeight: z3.number().int().min(100).max(4e3),
3260
+ statusBarHeight: z3.number().finite().min(0).max(400),
3261
+ safeAreaBottom: z3.number().finite().min(0).max(400),
3262
+ notchType: z3.enum(["none", "notch", "dynamic-island"]),
3263
+ safeAreaInsets: z3.object({
3264
+ top: z3.number().finite().min(0).max(400),
3265
+ right: z3.number().finite().min(0).max(400),
3266
+ bottom: z3.number().finite().min(0).max(400),
3267
+ left: z3.number().finite().min(0).max(400)
3094
3268
  })
3095
3269
  })
3096
3270
  ]);
3097
- var NonNegInt = z2.number().int().min(0).max(1e5);
3098
- var ViewBoundsSchema = z2.tuple([
3099
- z2.object({
3271
+ var NonNegInt = z3.number().int().min(0).max(1e5);
3272
+ var ViewBoundsSchema = z3.tuple([
3273
+ z3.object({
3100
3274
  x: NonNegInt,
3101
3275
  y: NonNegInt,
3102
3276
  width: NonNegInt,
3103
3277
  height: NonNegInt
3104
3278
  })
3105
3279
  ]);
3106
- var HostToolbarAdvertiseHeightSchema = z2.tuple([
3107
- z2.object({
3108
- axis: z2.literal("block"),
3280
+ var HostToolbarAdvertiseHeightSchema = z3.tuple([
3281
+ z3.object({
3282
+ axis: z3.literal("block"),
3109
3283
  extent: NonNegInt
3110
3284
  })
3111
3285
  ]);
3112
- var SimulatorCustomApiInvokeSchema = z2.tuple([
3113
- z2.string().min(1).max(256),
3114
- z2.unknown()
3286
+ var SimulatorCustomApiInvokeSchema = z3.tuple([
3287
+ z3.string().min(1).max(256),
3288
+ z3.unknown()
3115
3289
  ]);
3116
- var ProjectGetPagesSchema = z2.tuple([AbsolutePath]);
3117
- var ProjectGetCompileConfigSchema = z2.tuple([AbsolutePath]);
3118
- var ProjectsRemoveSchema = z2.tuple([AbsolutePath]);
3119
- var WorkbenchSettingsSaveSchema = z2.tuple([
3120
- z2.looseObject({
3121
- cdp: z2.looseObject({
3122
- enabled: z2.boolean(),
3123
- port: z2.number().int().min(0).max(65535)
3290
+ var ProjectGetPagesSchema = z3.tuple([AbsolutePath]);
3291
+ var ProjectGetCompileConfigSchema = z3.tuple([AbsolutePath]);
3292
+ var ProjectsRemoveSchema = z3.tuple([AbsolutePath]);
3293
+ var WorkbenchSettingsSaveSchema = z3.tuple([
3294
+ z3.looseObject({
3295
+ cdp: z3.looseObject({
3296
+ enabled: z3.boolean(),
3297
+ port: z3.number().int().min(0).max(65535)
3124
3298
  }),
3125
- mcp: z2.looseObject({
3126
- enabled: z2.boolean(),
3127
- port: z2.number().int().min(0).max(65535)
3299
+ mcp: z3.looseObject({
3300
+ enabled: z3.boolean(),
3301
+ port: z3.number().int().min(0).max(65535)
3128
3302
  }),
3129
- compile: z2.looseObject({
3130
- watch: z2.boolean()
3303
+ compile: z3.looseObject({
3304
+ watch: z3.boolean()
3131
3305
  }),
3132
- theme: z2.enum(["system", "light", "dark"]),
3133
- lastCreateBaseDir: z2.union([z2.string(), z2.null()])
3306
+ theme: z3.enum(["system", "light", "dark"]),
3307
+ lastCreateBaseDir: z3.union([z3.string(), z3.null()])
3134
3308
  })
3135
3309
  ]);
3136
- var WorkbenchSettingsSetThemeSchema = z2.tuple([
3137
- z2.enum(["system", "light", "dark"])
3310
+ var WorkbenchSettingsSetThemeSchema = z3.tuple([
3311
+ z3.enum(["system", "light", "dark"])
3138
3312
  ]);
3139
- var SettingsSetVisibleSchema = z2.tuple([z2.boolean()]);
3140
- var SettingsConfigChangedSchema = z2.tuple([CompileConfigShape]);
3141
- var PopoverRelaunchSchema = z2.tuple([CompileConfigShape]);
3142
- var SettingsProjectSettingsChangedSchema = z2.tuple([
3143
- z2.looseObject({
3144
- uploadWithSourceMap: z2.boolean().optional()
3313
+ var SettingsSetVisibleSchema = z3.tuple([z3.boolean()]);
3314
+ var SettingsConfigChangedSchema = z3.tuple([CompileConfigShape]);
3315
+ var PopoverRelaunchSchema = z3.tuple([CompileConfigShape]);
3316
+ var SettingsProjectSettingsChangedSchema = z3.tuple([
3317
+ z3.looseObject({
3318
+ uploadWithSourceMap: z3.boolean().optional()
3145
3319
  })
3146
3320
  ]);
3147
- var ProjectCaptureThumbnailSchema = z2.tuple([AbsolutePath]);
3148
- var ProjectGetThumbnailSchema = z2.tuple([AbsolutePath]);
3321
+ var ProjectCaptureThumbnailSchema = z3.tuple([AbsolutePath]);
3322
+ var ProjectGetThumbnailSchema = z3.tuple([AbsolutePath]);
3149
3323
 
3150
3324
  // src/shared/bridge-channels.ts
3151
3325
  var BRIDGE_CHANNELS = {
@@ -3258,11 +3432,11 @@ var popoverModule = {
3258
3432
 
3259
3433
  // src/main/ipc/projects.ts
3260
3434
  import { app as app7, dialog } from "electron";
3261
- import path12 from "path";
3435
+ import path13 from "path";
3262
3436
 
3263
3437
  // src/main/services/projects/create-project-service.ts
3264
3438
  import fs6 from "node:fs";
3265
- import path11 from "node:path";
3439
+ import path12 from "node:path";
3266
3440
  var DEFAULT_TEMPLATE_ID = "blank";
3267
3441
  async function createProject(input, ctx) {
3268
3442
  const name = (input.name ?? "").trim();
@@ -3309,7 +3483,7 @@ async function createProject(input, ctx) {
3309
3483
  force: true
3310
3484
  });
3311
3485
  }
3312
- const cfgPath = path11.join(target, "project.config.json");
3486
+ const cfgPath = path12.join(target, "project.config.json");
3313
3487
  let cfg = {};
3314
3488
  if (fs6.existsSync(cfgPath)) {
3315
3489
  try {
@@ -3395,7 +3569,7 @@ function registerProjectsIpc(ctx) {
3395
3569
  }
3396
3570
  try {
3397
3571
  const settings = loadWorkbenchSettings();
3398
- const newBase = path12.dirname(input.path);
3572
+ const newBase = path13.dirname(input.path);
3399
3573
  if (newBase && settings.lastCreateBaseDir !== newBase) {
3400
3574
  saveWorkbenchSettings({ ...settings, lastCreateBaseDir: newBase });
3401
3575
  }
@@ -3567,7 +3741,7 @@ import { DisposableRegistry as DisposableRegistry6 } from "@dimina-kit/electron-
3567
3741
 
3568
3742
  // src/main/ipc/bridge-router.ts
3569
3743
  import { app as app11, ipcMain as ipcMain3, protocol as protocol2, session as electronSession, webContents as webContents2 } from "electron";
3570
- import path15 from "node:path";
3744
+ import path16 from "node:path";
3571
3745
  import { pathToFileURL as pathToFileURL3 } from "node:url";
3572
3746
 
3573
3747
  // src/shared/simulator-api-metadata.ts
@@ -3582,7 +3756,7 @@ import { createDebugTap } from "@dimina-kit/electron-deck/main";
3582
3756
  // src/main/services/dimina-resource-server.ts
3583
3757
  import http from "node:http";
3584
3758
  import fs7 from "node:fs";
3585
- import path13 from "node:path";
3759
+ import path14 from "node:path";
3586
3760
  var MIME_TYPES = {
3587
3761
  ".css": "text/css; charset=utf-8",
3588
3762
  ".html": "text/html; charset=utf-8",
@@ -3598,7 +3772,7 @@ var MIME_TYPES = {
3598
3772
  ".wasm": "application/wasm"
3599
3773
  };
3600
3774
  async function startDiminaResourceServer(rootDir) {
3601
- const root = path13.resolve(rootDir);
3775
+ const root = path14.resolve(rootDir);
3602
3776
  const server = http.createServer((req, res) => {
3603
3777
  void handleRequest(root, req, res);
3604
3778
  });
@@ -3646,7 +3820,7 @@ async function handleRequest(root, req, res) {
3646
3820
  return;
3647
3821
  }
3648
3822
  res.writeHead(200, {
3649
- "content-type": MIME_TYPES[path13.extname(filePath)] ?? "application/octet-stream",
3823
+ "content-type": MIME_TYPES[path14.extname(filePath)] ?? "application/octet-stream",
3650
3824
  "content-length": stat.size,
3651
3825
  "cache-control": "no-store"
3652
3826
  });
@@ -3662,8 +3836,8 @@ async function handleRequest(root, req, res) {
3662
3836
  }
3663
3837
  function resolveContainedPath(root, pathname) {
3664
3838
  const relative = pathname.replace(/^\/+/, "") || "index.html";
3665
- const resolved = path13.resolve(root, relative);
3666
- if (resolved !== root && !resolved.startsWith(root + path13.sep)) return null;
3839
+ const resolved = path14.resolve(root, relative);
3840
+ if (resolved !== root && !resolved.startsWith(root + path14.sep)) return null;
3667
3841
  return resolved;
3668
3842
  }
3669
3843
  function setCorsHeaders(res) {
@@ -3674,16 +3848,16 @@ function setCorsHeaders(res) {
3674
3848
  }
3675
3849
 
3676
3850
  // src/main/windows/service-host-window/create.ts
3677
- import { app as app9, BrowserWindow as BrowserWindow3 } from "electron";
3678
- import path14 from "node:path";
3851
+ import { app as app9, BrowserWindow as BrowserWindow4 } from "electron";
3852
+ import path15 from "node:path";
3679
3853
  import { pathToFileURL as pathToFileURL2 } from "node:url";
3680
3854
  var SERVICE_HOST_PARTITION = SHARED_MINIAPP_PARTITION;
3681
- var serviceHostPreloadPath = path14.join(devtoolsPackageRoot, "dist/service-host/preload.cjs");
3682
- var serviceHostHtmlPath = path14.join(devtoolsPackageRoot, "dist/service-host/service.html");
3855
+ var serviceHostPreloadPath = path15.join(devtoolsPackageRoot, "dist/service-host/preload.cjs");
3856
+ var serviceHostHtmlPath = path15.join(devtoolsPackageRoot, "dist/service-host/service.html");
3683
3857
  function constructServiceHostWindow(opts = {}) {
3684
3858
  const partition = opts.partition ?? SERVICE_HOST_PARTITION;
3685
3859
  configureMiniappSession(partition);
3686
- return new BrowserWindow3({
3860
+ return new BrowserWindow4({
3687
3861
  width: 980,
3688
3862
  height: 720,
3689
3863
  show: false,
@@ -3754,7 +3928,7 @@ function createServiceHostWindow(opts) {
3754
3928
  }
3755
3929
 
3756
3930
  // src/main/services/service-host-pool/pool.ts
3757
- import { BrowserWindow as BrowserWindow4 } from "electron";
3931
+ import { BrowserWindow as BrowserWindow5 } from "electron";
3758
3932
  var BLANK_URL = "about:blank";
3759
3933
  var HARD_MAX_POOL_SIZE = 4;
3760
3934
  var RESET_STORAGES = [
@@ -3951,7 +4125,7 @@ var ServiceHostPool = class {
3951
4125
  }
3952
4126
  }
3953
4127
  createWindow(spec) {
3954
- return new BrowserWindow4({
4128
+ return new BrowserWindow5({
3955
4129
  show: false,
3956
4130
  width: spec.size?.width,
3957
4131
  height: spec.size?.height,
@@ -4623,12 +4797,16 @@ function summarizeBridgeMsg(payload) {
4623
4797
  var API_CALL_TIMEOUT_MS = 5e3;
4624
4798
  function resolveCurrentApp(state, ctx, appId) {
4625
4799
  if (appId) {
4626
- for (const ap of state.appSessions.values()) if (ap.appId === appId) return ap;
4800
+ let match;
4801
+ for (const ap of state.appSessions.values()) if (ap.appId === appId) match = ap;
4802
+ if (match) return match;
4627
4803
  }
4628
4804
  const appInfo = ctx.workspace?.getSession?.()?.appInfo;
4629
4805
  const activeAppId = appInfo?.appId;
4630
4806
  if (activeAppId) {
4631
- for (const ap of state.appSessions.values()) if (ap.appId === activeAppId) return ap;
4807
+ let match;
4808
+ for (const ap of state.appSessions.values()) if (ap.appId === activeAppId) match = ap;
4809
+ if (match) return match;
4632
4810
  }
4633
4811
  let last;
4634
4812
  for (const ap of state.appSessions.values()) last = ap;
@@ -4645,7 +4823,11 @@ function installBridgeRouter(ctx) {
4645
4823
  emitRenderEvent: () => {
4646
4824
  },
4647
4825
  connections: ctx.connections,
4648
- debugTap: createDebugTap({ enabled: resolveDebugTapEnabled() })
4826
+ debugTap: createDebugTap({ enabled: resolveDebugTapEnabled() }),
4827
+ evictAppDataBridges: (ap) => {
4828
+ if (!ctx.appData) return;
4829
+ for (const page of ap.pages.values()) ctx.appData.evictBridge(ap.appId, page.bridgeId);
4830
+ }
4649
4831
  };
4650
4832
  const prewarmPoolSize = resolvePrewarmPoolSize();
4651
4833
  if (prewarmPoolSize > 0) {
@@ -4664,8 +4846,8 @@ function installBridgeRouter(ctx) {
4664
4846
  const onNativeHostQuery = (event) => {
4665
4847
  const reply = {
4666
4848
  enabled: true,
4667
- renderHostHtmlUrl: pathToFileURL3(path15.join(devtoolsPackageRoot, "dist/render-host/pageFrame.html")).toString(),
4668
- renderPreloadUrl: pathToFileURL3(path15.join(devtoolsPackageRoot, "dist/render-host/preload.cjs")).toString(),
4849
+ renderHostHtmlUrl: pathToFileURL3(path16.join(devtoolsPackageRoot, "dist/render-host/pageFrame.html")).toString(),
4850
+ renderPreloadUrl: pathToFileURL3(path16.join(devtoolsPackageRoot, "dist/render-host/preload.cjs")).toString(),
4669
4851
  device: currentDevice ?? void 0
4670
4852
  };
4671
4853
  event.returnValue = reply;
@@ -4817,9 +4999,6 @@ function installBridgeRouter(ctx) {
4817
4999
  console.warn(`[bridge-router] DISPOSE rejected: sender belongs to ${senderApp.appSessionId}, target ${target.appSessionId}`);
4818
5000
  return;
4819
5001
  }
4820
- if (ctx.appData) {
4821
- for (const page of target.pages.values()) ctx.appData.evictBridge(target.appId, page.bridgeId);
4822
- }
4823
5002
  void disposeAppSession(state, target.appSessionId);
4824
5003
  };
4825
5004
  ipcMain3.on(BRIDGE_CHANNELS.DISPOSE, onDispose);
@@ -4915,14 +5094,14 @@ async function handleSpawn(state, ctx, event, opts) {
4915
5094
  const appSessionId = bridgeId;
4916
5095
  const simulatorWc = resolveSimulatorWebContents(ctx, opts.simulatorWcId, event.sender);
4917
5096
  const pagePath = normalizePagePath(opts.pagePath || "pages/index/index");
4918
- const pkgRoot = path15.resolve(opts.pkgRoot || process.cwd());
5097
+ const pkgRoot = path16.resolve(opts.pkgRoot || process.cwd());
4919
5098
  const root = opts.root || "main";
4920
5099
  let resourceServer = null;
4921
5100
  let resourceBaseUrl;
4922
5101
  if (opts.resourceBaseUrl) {
4923
5102
  resourceBaseUrl = opts.resourceBaseUrl.endsWith("/") ? opts.resourceBaseUrl : `${opts.resourceBaseUrl}/`;
4924
5103
  } else {
4925
- resourceServer = await startDiminaResourceServer(path15.resolve(pkgRoot, root));
5104
+ resourceServer = await startDiminaResourceServer(path16.resolve(pkgRoot, root));
4926
5105
  resourceBaseUrl = resourceServer.baseUrl;
4927
5106
  }
4928
5107
  const selectedDevice = ctx.bridge?.getDevice?.() ?? null;
@@ -5002,6 +5181,10 @@ async function handleSpawn(state, ctx, event, opts) {
5002
5181
  };
5003
5182
  appSession.onServiceClosed = onServiceClosed;
5004
5183
  serviceWindow.once("closed", onServiceClosed);
5184
+ const onSimulatorDestroyed = () => {
5185
+ void disposeAppSession(state, appSessionId);
5186
+ };
5187
+ simulatorWc.once("destroyed", onSimulatorDestroyed);
5005
5188
  if (usedPool) {
5006
5189
  const bootOnServiceLoad = () => {
5007
5190
  if (serviceWindow.isDestroyed()) return;
@@ -5506,6 +5689,7 @@ async function disposeAppSession(state, appSessionId, opts = {}) {
5506
5689
  const ap = state.appSessions.get(appSessionId);
5507
5690
  if (!ap) return;
5508
5691
  state.appSessions.delete(appSessionId);
5692
+ state.evictAppDataBridges(ap);
5509
5693
  for (const [requestId, pending] of state.pendingApiCalls) {
5510
5694
  if (pending.appSessionId !== appSessionId) continue;
5511
5695
  clearTimeout(pending.timer);
@@ -5671,7 +5855,7 @@ function registerViewsIpc(ctx) {
5671
5855
  args
5672
5856
  );
5673
5857
  ctx.views.setHostToolbarBounds(bounds);
5674
- });
5858
+ }).handle(ViewChannel.HostToolbarGetHeight, () => ctx.views.getHostToolbarHeight());
5675
5859
  const onAdvertiseHeight = (event, ...args) => {
5676
5860
  if (event.sender.id !== ctx.views.getHostToolbarWebContentsId()) return;
5677
5861
  let extent;
@@ -6542,7 +6726,7 @@ function getClient(kind) {
6542
6726
  }
6543
6727
 
6544
6728
  // src/main/services/mcp/tool-registry.ts
6545
- import { z as z3 } from "zod";
6729
+ import { z as z4 } from "zod";
6546
6730
  function registerCommonTargetTools(server, kind) {
6547
6731
  const descriptions = kind === "simulator" ? {
6548
6732
  screenshot: "Take a screenshot of the simulator webview",
@@ -6563,9 +6747,9 @@ function registerCommonTargetTools(server, kind) {
6563
6747
  return { content: [{ type: "image", data, mimeType: "image/png" }] };
6564
6748
  });
6565
6749
  server.tool(`${kind}_console_logs`, descriptions.consoleLogs, {
6566
- limit: z3.number().optional().default(50).describe("Maximum number of log entries"),
6567
- level: z3.enum(["error", "warning", "warn", "info", "log", "debug"]).optional().describe("Only return entries with this level"),
6568
- sinceTimestamp: z3.string().optional().describe("ISO timestamp; only return entries at or after this time")
6750
+ limit: z4.number().optional().default(50).describe("Maximum number of log entries"),
6751
+ level: z4.enum(["error", "warning", "warn", "info", "log", "debug"]).optional().describe("Only return entries with this level"),
6752
+ sinceTimestamp: z4.string().optional().describe("ISO timestamp; only return entries at or after this time")
6569
6753
  }, async ({ limit, level, sinceTimestamp }) => {
6570
6754
  getClient(kind);
6571
6755
  let entries = getTargetState(kind).consoleLogs;
@@ -6580,7 +6764,7 @@ function registerCommonTargetTools(server, kind) {
6580
6764
  });
6581
6765
  if (kind !== "workbench") {
6582
6766
  server.tool(`${kind}_evaluate`, descriptions.evaluate, {
6583
- expression: z3.string().describe("JavaScript expression to evaluate")
6767
+ expression: z4.string().describe("JavaScript expression to evaluate")
6584
6768
  }, async ({ expression }) => {
6585
6769
  const c = getClient(kind);
6586
6770
  const result = await c.Runtime.evaluate({ expression, returnByValue: true, awaitPromise: true });
@@ -6593,15 +6777,15 @@ ${JSON.stringify(result.exceptionDetails, null, 2)}` }], isError: true };
6593
6777
  });
6594
6778
  }
6595
6779
  server.tool(`${kind}_get_dom`, descriptions.getDom, {
6596
- depth: z3.number().optional().default(3).describe("Maximum depth")
6780
+ depth: z4.number().optional().default(3).describe("Maximum depth")
6597
6781
  }, async ({ depth }) => {
6598
6782
  const c = getClient(kind);
6599
6783
  const { root } = await c.DOM.getDocument({ depth });
6600
6784
  return { content: [{ type: "text", text: JSON.stringify(root, null, 2) }] };
6601
6785
  });
6602
6786
  server.tool(`${kind}_network_log`, descriptions.networkLog, {
6603
- limit: z3.number().optional().default(20).describe("Maximum number of entries"),
6604
- minStatus: z3.number().optional().describe("Only return entries with status >= minStatus (status 0 = failed before response is included when minStatus >= 400)")
6787
+ limit: z4.number().optional().default(20).describe("Maximum number of entries"),
6788
+ minStatus: z4.number().optional().describe("Only return entries with status >= minStatus (status 0 = failed before response is included when minStatus >= 400)")
6605
6789
  }, async ({ limit, minStatus }) => {
6606
6790
  getClient(kind);
6607
6791
  let entries = getTargetState(kind).networkRequests;
@@ -6856,11 +7040,11 @@ function registerContextTools(server) {
6856
7040
  }
6857
7041
 
6858
7042
  // src/main/services/mcp/tools/simulator-tools.ts
6859
- import { z as z4 } from "zod";
7043
+ import { z as z5 } from "zod";
6860
7044
  function registerSimulatorTools(server) {
6861
7045
  server.tool("simulator_navigate", "Navigate the simulator to a URL, or reload the current page", {
6862
- url: z4.string().optional().describe("URL to navigate to"),
6863
- reload: z4.boolean().optional().describe("If true, reload current page instead of navigating")
7046
+ url: z5.string().optional().describe("URL to navigate to"),
7047
+ reload: z5.boolean().optional().describe("If true, reload current page instead of navigating")
6864
7048
  }, async ({ url, reload }) => {
6865
7049
  const c = getClient("simulator");
6866
7050
  if (reload) {
@@ -6875,15 +7059,15 @@ function registerSimulatorTools(server) {
6875
7059
  return { content: [{ type: "text", text: "Error: either url or reload is required" }], isError: true };
6876
7060
  });
6877
7061
  server.tool("simulator_input", "Dispatch input to the simulator. `tap_coord` requires x and y. `tap_selector` requires selector and optional nth. `type` requires selector and text. `scroll` requires x, y, deltaX, and deltaY. `key` is best-effort for common keys such as Enter, Escape, Tab, Arrow* and Space.", {
6878
- action: z4.enum(["tap_coord", "tap_selector", "type", "scroll", "key"]).describe("Input action to dispatch"),
6879
- x: z4.number().optional().describe("Viewport x coordinate for `tap_coord` or `scroll`"),
6880
- y: z4.number().optional().describe("Viewport y coordinate for `tap_coord` or `scroll`"),
6881
- selector: z4.string().optional().describe("CSS selector for `tap_selector` or `type`"),
6882
- nth: z4.number().int().optional().describe("Zero-based index when selector matches multiple elements; defaults to 0"),
6883
- text: z4.string().optional().describe("Text to insert for `type`"),
6884
- key: z4.string().optional().describe("Best-effort keyboard key name for `key`; common keys such as Enter, Escape, Tab, Arrow* and Space work best"),
6885
- deltaX: z4.number().optional().describe("Horizontal wheel delta for `scroll`"),
6886
- deltaY: z4.number().optional().describe("Vertical wheel delta for `scroll`")
7062
+ action: z5.enum(["tap_coord", "tap_selector", "type", "scroll", "key"]).describe("Input action to dispatch"),
7063
+ x: z5.number().optional().describe("Viewport x coordinate for `tap_coord` or `scroll`"),
7064
+ y: z5.number().optional().describe("Viewport y coordinate for `tap_coord` or `scroll`"),
7065
+ selector: z5.string().optional().describe("CSS selector for `tap_selector` or `type`"),
7066
+ nth: z5.number().int().optional().describe("Zero-based index when selector matches multiple elements; defaults to 0"),
7067
+ text: z5.string().optional().describe("Text to insert for `type`"),
7068
+ key: z5.string().optional().describe("Best-effort keyboard key name for `key`; common keys such as Enter, Escape, Tab, Arrow* and Space work best"),
7069
+ deltaX: z5.number().optional().describe("Horizontal wheel delta for `scroll`"),
7070
+ deltaY: z5.number().optional().describe("Vertical wheel delta for `scroll`")
6887
7071
  }, async ({ action, x, y, selector, nth, text, key, deltaX, deltaY }) => {
6888
7072
  const c = getClient("simulator");
6889
7073
  const err = (msg) => ({ content: [{ type: "text", text: `Error: ${msg}` }], isError: true });
@@ -7794,10 +7978,10 @@ function setupSimulatorCurrentPage(host, options) {
7794
7978
 
7795
7979
  // src/main/services/render-inspect/index.ts
7796
7980
  import { readFileSync } from "node:fs";
7797
- import path16 from "node:path";
7981
+ import path17 from "node:path";
7798
7982
  var DEFAULT_SOURCE_PATH = "dist/render-host/render-inspect.js";
7799
7983
  function createRenderInspector(options = {}) {
7800
- const loadSource = options.loadSource ?? (() => readFileSync(path16.join(devtoolsPackageRoot, DEFAULT_SOURCE_PATH), "utf8"));
7984
+ const loadSource = options.loadSource ?? (() => readFileSync(path17.join(devtoolsPackageRoot, DEFAULT_SOURCE_PATH), "utf8"));
7801
7985
  let cachedSource = null;
7802
7986
  const injected = /* @__PURE__ */ new Set();
7803
7987
  function source() {
@@ -7883,14 +8067,14 @@ function revokeAllTempFiles(store) {
7883
8067
 
7884
8068
  // src/shared/vpath.ts
7885
8069
  import os from "node:os";
7886
- import path17 from "node:path";
8070
+ import path18 from "node:path";
7887
8071
  var DIFILE_PREFIX = "difile://";
7888
8072
  function sandboxBase() {
7889
8073
  const env = typeof process !== "undefined" && process.env && process.env.DIMINA_HOME || "";
7890
8074
  if (env) {
7891
- return path17.join(env, "files");
8075
+ return path18.join(env, "files");
7892
8076
  }
7893
- return path17.join(os.homedir(), ".dimina", "files");
8077
+ return path18.join(os.homedir(), ".dimina", "files");
7894
8078
  }
7895
8079
  function resolveVPath(url) {
7896
8080
  if (typeof url !== "string") return null;
@@ -7924,15 +8108,15 @@ function resolveVPath(url) {
7924
8108
  return { kind, writable };
7925
8109
  }
7926
8110
  const base = sandboxBase();
7927
- const joined = path17.join(base, decoded);
7928
- const normalized = path17.normalize(joined);
7929
- if (normalized !== base && !normalized.startsWith(base + path17.sep)) return null;
8111
+ const joined = path18.join(base, decoded);
8112
+ const normalized = path18.normalize(joined);
8113
+ if (normalized !== base && !normalized.startsWith(base + path18.sep)) return null;
7930
8114
  return { kind, writable, realPath: normalized };
7931
8115
  }
7932
8116
 
7933
8117
  // src/main/services/simulator-temp-files/disk.ts
7934
8118
  import fs8 from "node:fs/promises";
7935
- import path18 from "node:path";
8119
+ import path19 from "node:path";
7936
8120
  var EXT_MIME = {
7937
8121
  ".txt": "text/plain",
7938
8122
  ".log": "text/plain",
@@ -7997,7 +8181,7 @@ function sniffMime(head) {
7997
8181
  return null;
7998
8182
  }
7999
8183
  function extMime(realPath) {
8000
- const ext = path18.extname(realPath).toLowerCase();
8184
+ const ext = path19.extname(realPath).toLowerCase();
8001
8185
  if (!ext) return null;
8002
8186
  return EXT_MIME[ext] ?? null;
8003
8187
  }
@@ -8067,7 +8251,7 @@ async function readDiskFile(realPath, opts) {
8067
8251
  }
8068
8252
  async function writeDiskFile(realPath, bytes) {
8069
8253
  const buf = Buffer.isBuffer(bytes) ? bytes : Buffer.from(new Uint8Array(bytes));
8070
- await fs8.mkdir(path18.dirname(realPath), { recursive: true });
8254
+ await fs8.mkdir(path19.dirname(realPath), { recursive: true });
8071
8255
  await fs8.writeFile(realPath, buf);
8072
8256
  }
8073
8257
  async function readDiskDir(realPath) {
@@ -8209,28 +8393,28 @@ async function handleDifileRequest(ctx, req) {
8209
8393
 
8210
8394
  // src/main/services/simulator-temp-files/fs-channels.ts
8211
8395
  import fs9 from "node:fs/promises";
8212
- import path19 from "node:path";
8396
+ import path20 from "node:path";
8213
8397
  async function enforceSandbox(realPath) {
8214
8398
  if (typeof realPath !== "string" || realPath.length === 0) {
8215
8399
  throw new Error("sandbox: realPath must be a non-empty string");
8216
8400
  }
8217
8401
  const base = sandboxBase();
8218
8402
  const baseReal = await fs9.realpath(base).catch(() => base);
8219
- const normalized = path19.normalize(realPath);
8220
- if (normalized !== base && !normalized.startsWith(base + path19.sep)) {
8403
+ const normalized = path20.normalize(realPath);
8404
+ if (normalized !== base && !normalized.startsWith(base + path20.sep)) {
8221
8405
  throw new Error("sandbox: realPath escapes the user-data base");
8222
8406
  }
8223
8407
  let probe = normalized;
8224
- while (probe !== path19.parse(probe).root) {
8408
+ while (probe !== path20.parse(probe).root) {
8225
8409
  try {
8226
8410
  const resolved = await fs9.realpath(probe);
8227
- if (resolved !== baseReal && !resolved.startsWith(baseReal + path19.sep)) {
8411
+ if (resolved !== baseReal && !resolved.startsWith(baseReal + path20.sep)) {
8228
8412
  throw new Error("sandbox: realPath escapes the user-data base via symlink");
8229
8413
  }
8230
8414
  break;
8231
8415
  } catch (err) {
8232
8416
  if (err.code === "ENOENT") {
8233
- probe = path19.dirname(probe);
8417
+ probe = path20.dirname(probe);
8234
8418
  continue;
8235
8419
  }
8236
8420
  throw err;
@@ -8614,7 +8798,7 @@ async function disposeContext(ctx) {
8614
8798
  function createConfiguredMainWindow(config, rendererDir2) {
8615
8799
  const mainWindow = createMainWindow({
8616
8800
  title: config.appName ?? "Dimina DevTools",
8617
- indexHtml: path20.join(rendererDir2, "entries/main/index.html"),
8801
+ indexHtml: path21.join(rendererDir2, "entries/main/index.html"),
8618
8802
  width: config.window?.width,
8619
8803
  height: config.window?.height,
8620
8804
  minWidth: config.window?.minWidth,
@@ -8652,12 +8836,21 @@ function registerBuiltinModules(config, context) {
8652
8836
  });
8653
8837
  }
8654
8838
  function toMenuContext(context) {
8655
- const menuContext = { ...context };
8656
- delete menuContext.registry;
8657
- delete menuContext.senderPolicy;
8658
- delete menuContext.trustedWindowSenderIds;
8659
- delete menuContext.simulatorApis;
8660
- return menuContext;
8839
+ return {
8840
+ appName: context.appName,
8841
+ workspace: {
8842
+ hasActiveSession: () => context.workspace.hasActiveSession(),
8843
+ getProjectPath: () => context.workspace.getProjectPath(),
8844
+ openProject: (projectPath) => context.workspace.openProject(projectPath),
8845
+ closeProject: () => context.workspace.closeProject(),
8846
+ getSession: () => context.workspace.getSession()
8847
+ },
8848
+ openSettings: () => context.openSettings(),
8849
+ notify: {
8850
+ projectStatus: (payload) => context.notify.projectStatus(payload),
8851
+ windowNavigateBack: () => context.notify.windowNavigateBack()
8852
+ }
8853
+ };
8661
8854
  }
8662
8855
  function installMenu(config, mainWindow, context) {
8663
8856
  if (config.menuBuilder) {
@@ -8714,7 +8907,7 @@ function enableDevRendererAutoReload(rendererDir2) {
8714
8907
  const watcher = fs10.watch(rendererDir2, { recursive: true }, () => {
8715
8908
  if (reloadTimer) clearTimeout(reloadTimer);
8716
8909
  reloadTimer = setTimeout(() => {
8717
- for (const win of BrowserWindow7.getAllWindows()) {
8910
+ for (const win of BrowserWindow8.getAllWindows()) {
8718
8911
  win.webContents.reload();
8719
8912
  }
8720
8913
  }, 300);
@@ -8932,56 +9125,10 @@ function createDevtoolsBackend(config = {}) {
8932
9125
  };
8933
9126
  }
8934
9127
 
8935
- // src/main/windows/settings-window/create.ts
8936
- import { BrowserWindow as BrowserWindow8 } from "electron";
8937
- import path21 from "path";
8938
- async function createSettingsWindow(parent, rendererDir2) {
8939
- const win = new BrowserWindow8({
8940
- width: 420,
8941
- height: 560,
8942
- minWidth: 380,
8943
- minHeight: 480,
8944
- parent: parent ?? void 0,
8945
- title: "\u5F00\u53D1\u5DE5\u5177\u8BBE\u7F6E",
8946
- backgroundColor: themeBg(),
8947
- webPreferences: {
8948
- nodeIntegration: false,
8949
- contextIsolation: true,
8950
- sandbox: false,
8951
- preload: mainPreloadPath
8952
- }
8953
- });
8954
- applyNavigationHardening(win.webContents, rendererDir2);
8955
- await win.loadFile(path21.join(rendererDir2, "entries/workbench-settings/index.html"));
8956
- return win;
8957
- }
8958
-
8959
- // src/main/windows/settings-window/events.ts
8960
- function wireSettingsWindowEvents(win, onClosed) {
8961
- win.on("closed", () => {
8962
- onClosed();
8963
- });
8964
- }
8965
-
8966
9128
  // src/main/app/launch.ts
8967
9129
  function launch(config = {}) {
8968
9130
  return frameworkElectronDeck({ backend: createDevtoolsBackend(config) });
8969
9131
  }
8970
- async function openSettingsWindow(ctx) {
8971
- let win = ctx.windows.settingsWindow;
8972
- if (!win || win.isDestroyed()) {
8973
- win = await createSettingsWindow(ctx.windows.mainWindow, ctx.rendererDir);
8974
- ctx.windows.setSettingsWindow(win);
8975
- wireSettingsWindowEvents(win, () => {
8976
- ctx.windows.setSettingsWindow(null);
8977
- });
8978
- }
8979
- win.show();
8980
- win.focus();
8981
- ctx.notify.workbenchSettingsInit(win, {
8982
- settings: loadWorkbenchSettings()
8983
- });
8984
- }
8985
9132
 
8986
9133
  // src/main/index.ts
8987
9134
  launch().catch((err) => {