@dimina-kit/devtools 0.4.0-dev.20260625063832 → 0.4.0-dev.20260630033804

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 (64) hide show
  1. package/dist/main/app/app.js +2 -0
  2. package/dist/main/index.bundle.js +241 -151
  3. package/dist/main/ipc/bridge-router.d.ts +21 -0
  4. package/dist/main/ipc/bridge-router.js +63 -3
  5. package/dist/main/services/automation/index.js +0 -1
  6. package/dist/main/services/elements-forward/index.js +81 -72
  7. package/dist/main/services/mcp/target-manager.d.ts +3 -3
  8. package/dist/main/services/views/miniapp-partition.d.ts +18 -8
  9. package/dist/main/services/views/miniapp-partition.js +34 -13
  10. package/dist/main/services/views/view-manager.js +76 -41
  11. package/dist/main/services/workbench-coi-server.d.ts +7 -0
  12. package/dist/main/services/workbench-coi-server.js +6 -0
  13. package/dist/main/services/workbench-context.d.ts +4 -2
  14. package/dist/main/services/workbench-context.js +1 -0
  15. package/dist/main/services/workspace/op-lock.d.ts +26 -0
  16. package/dist/main/services/workspace/op-lock.js +23 -0
  17. package/dist/main/services/workspace/workspace-service.d.ts +7 -0
  18. package/dist/main/services/workspace/workspace-service.js +197 -103
  19. package/dist/main/windows/service-host-window/create.d.ts +5 -1
  20. package/dist/main/windows/service-host-window/create.js +6 -3
  21. package/dist/native-host/render/render.js +485 -449
  22. package/dist/native-host/service/service.js +1 -1
  23. package/dist/renderer/assets/index-BMwmmaSp.js +46 -0
  24. package/dist/renderer/entries/main/index.html +1 -1
  25. package/dist/shared/types.d.ts +15 -0
  26. package/dist/vscode-workbench/assets/__vite-browser-external-B0DTNerG.js +1 -0
  27. package/dist/vscode-workbench/assets/__vite-browser-external-BuLjVMfN.js +1 -0
  28. package/dist/vscode-workbench/assets/__vite-browser-external-thkx7I19.js +1 -0
  29. package/dist/vscode-workbench/assets/{dist-SFZ2HNEz.js → dist-CS7SQP3K.js} +4 -4
  30. package/dist/vscode-workbench/assets/dist-VSgdjUlW.js +8 -0
  31. package/dist/vscode-workbench/assets/editor.worker-DmMg51ee.js +41 -0
  32. package/dist/vscode-workbench/assets/extensionHost.worker-BMpl1cpB.js +207 -0
  33. package/dist/vscode-workbench/assets/{iconv-lite-umd-izd5l6fa.js → iconv-lite-umd-D3q-1-o6.js} +1 -1
  34. package/dist/vscode-workbench/assets/{iconv-lite-umd-COfjYshr.js → iconv-lite-umd-Jn9RTrsv.js} +2 -2
  35. package/dist/vscode-workbench/assets/index-zjigpZ25.js +2842 -0
  36. package/dist/vscode-workbench/assets/{jschardet-Bf-VMK4F.js → jschardet-CjCVc2Gw.js} +1 -1
  37. package/dist/vscode-workbench/assets/{jschardet-Dh5TyKsQ.js → jschardet-Cu4ufeHq.js} +1 -1
  38. package/dist/vscode-workbench/assets/{layout.contribution.darwin-cz2ttHTW.js → layout.contribution.darwin-CoH1BZtz.js} +1 -1
  39. package/dist/vscode-workbench/assets/{layout.contribution.linux-fEtdzbcY.js → layout.contribution.linux-Di2FF-CG.js} +1 -1
  40. package/dist/vscode-workbench/assets/{layout.contribution.win-BFyOKI8c.js → layout.contribution.win-moiNStil.js} +1 -1
  41. package/dist/vscode-workbench/assets/{main-Bh8TI5LP.js → main-DzHL45xo.js} +4 -4
  42. package/dist/vscode-workbench/assets/{main2-Bds9Fhfn.js → main2-CWVd-Hyp.js} +1 -1
  43. package/dist/vscode-workbench/assets/{worker-D9ti6A0r.js → worker-CRTb-Arc.js} +7 -7
  44. package/dist/vscode-workbench/assets/{worker-DQtXDXIZ.js → worker-CVB8WPuM.js} +2 -2
  45. package/dist/vscode-workbench/index.html +1 -4
  46. package/package.json +8 -38
  47. package/dist/renderer/assets/index-6XUSo6Su.js +0 -46
  48. package/dist/vscode-workbench/assets/__vite-browser-external-BUwiolNy.js +0 -1
  49. package/dist/vscode-workbench/assets/__vite-browser-external-CmG9GjjO.js +0 -1
  50. package/dist/vscode-workbench/assets/__vite-browser-external-DGJPTKLg.js +0 -1
  51. package/dist/vscode-workbench/assets/_commonjsHelpers-qcUHfnrg.js +0 -1
  52. package/dist/vscode-workbench/assets/chunk-B3K2TuZy.js +0 -1
  53. package/dist/vscode-workbench/assets/chunk-DECur_0Z.js +0 -1
  54. package/dist/vscode-workbench/assets/dist-tesJ1Kd2.js +0 -8
  55. package/dist/vscode-workbench/assets/editor.worker-BC1qOyXG.js +0 -41
  56. package/dist/vscode-workbench/assets/extension.api-BWA2aPAU.js +0 -1
  57. package/dist/vscode-workbench/assets/extensionHost.worker-Dvu35anN.js +0 -207
  58. package/dist/vscode-workbench/assets/index-CQ5xP0pp.js +0 -1656
  59. package/dist/vscode-workbench/assets/localExtensionHost-Ccz7QUdA.js +0 -1188
  60. package/dist/vscode-workbench/assets/main-BDgdQmvs.js +0 -4
  61. package/dist/vscode-workbench/assets/main2-DwfDVleQ.js +0 -1
  62. package/dist/vscode-workbench/assets/preload-helper-xBbMyY7u.js +0 -1
  63. /package/dist/vscode-workbench/assets/{_.contribution-C9aS_G-o.js → _.contribution-B46wcGKQ.js} +0 -0
  64. /package/dist/vscode-workbench/assets/{_commonjsHelpers-BPKu-ps5.js → _commonjsHelpers-DMmVv2OA.js} +0 -0
@@ -151,6 +151,7 @@ function createContext(config, mainWindow, rendererDir) {
151
151
  rendererDir,
152
152
  appName: config.appName,
153
153
  apiNamespaces: config.apiNamespaces,
154
+ fileTypes: config.fileTypes,
154
155
  brandingProvider: config.brandingProvider,
155
156
  // The host-supplied ProjectsProvider / template types in `shared/types`
156
157
  // are structurally compatible with the main-process equivalents —
@@ -558,6 +559,7 @@ export async function createDevtoolsRuntime(config = {}) {
558
559
  rootDir: bundleDir,
559
560
  getProjectRoot: () => context.workspace.getProjectPath(),
560
561
  extensionsDir: config.editorViewConfig?.extensionsDir,
562
+ getFileTypes: () => context.fileTypes,
561
563
  });
562
564
  // Return the close promise so the registry awaits server shutdown on dispose
563
565
  // instead of fire-and-forgetting it (a dangling http server would keep the
@@ -1653,6 +1653,9 @@ function buildElementsHookScript() {
1653
1653
  return 'partial';
1654
1654
  }catch(e){ return 'error:' + (e && e.message); }})()`;
1655
1655
  }
1656
+ function buildReconcileScript() {
1657
+ return `(function(){var status; try { status = ${buildElementsHookScript()}; } catch(e){ status = 'error:'+(e&&e.message); }var batch = []; try { if (globalThis.__diminaElementsOutbound) batch = globalThis.__diminaElementsOutbound.splice(0); } catch(_){}return { status: status, batch: batch };})()`;
1658
+ }
1656
1659
  var PROBE_DEVTOOLS_API = `(window.DevToolsAPI && typeof window.DevToolsAPI.dispatchMessage === 'function')`;
1657
1660
  var MAX_SINGLE_DISPATCH_CHARS = 1e6;
1658
1661
  var CHUNK_CHARS = 256 * 1024;
@@ -1668,13 +1671,11 @@ function buildDocumentUpdatedScript() {
1668
1671
  return buildDispatchScript(msg);
1669
1672
  }
1670
1673
  var DRAIN_INTERVAL_MS = 150;
1671
- var INSTALL_POLL_TRIES = 200;
1672
- var INSTALL_POLL_INTERVAL_MS = 50;
1673
1674
  function installElementsForward(deps) {
1674
1675
  const { devtoolsWc, bridge } = deps;
1675
1676
  let disposed = false;
1676
- let installTimer = null;
1677
- let drainTimer = null;
1677
+ let reconcileTimer = null;
1678
+ let lateCleanup = null;
1678
1679
  const isActiveWcId = (id) => {
1679
1680
  const a = activeRenderWc();
1680
1681
  return a != null && a.id === id;
@@ -1683,13 +1684,9 @@ function installElementsForward(deps) {
1683
1684
  const selfAttached = /* @__PURE__ */ new Set();
1684
1685
  const stop = () => {
1685
1686
  disposed = true;
1686
- if (installTimer) {
1687
- clearInterval(installTimer);
1688
- installTimer = null;
1689
- }
1690
- if (drainTimer) {
1691
- clearInterval(drainTimer);
1692
- drainTimer = null;
1687
+ if (reconcileTimer) {
1688
+ clearInterval(reconcileTimer);
1689
+ reconcileTimer = null;
1693
1690
  }
1694
1691
  for (const cleanup of wiredGuests.values()) {
1695
1692
  try {
@@ -1699,6 +1696,14 @@ function installElementsForward(deps) {
1699
1696
  }
1700
1697
  wiredGuests.clear();
1701
1698
  detachSelfAttached();
1699
+ if (lateCleanup) {
1700
+ const c = lateCleanup;
1701
+ lateCleanup = null;
1702
+ try {
1703
+ c();
1704
+ } catch {
1705
+ }
1706
+ }
1702
1707
  };
1703
1708
  function detachSelfAttached() {
1704
1709
  for (const wcId of selfAttached) {
@@ -1908,46 +1913,26 @@ function installElementsForward(deps) {
1908
1913
  const unsubscribeRenderEvents = bridge.onRenderEvent(onRenderEvent);
1909
1914
  const onReady = () => {
1910
1915
  if (disposed) return;
1911
- if (installTimer) {
1912
- clearInterval(installTimer);
1913
- installTimer = null;
1914
- }
1915
- if (drainTimer) {
1916
- clearInterval(drainTimer);
1917
- drainTimer = null;
1916
+ if (reconcileTimer) {
1917
+ clearInterval(reconcileTimer);
1918
+ reconcileTimer = null;
1918
1919
  }
1919
- let tries = 0;
1920
- installTimer = setInterval(() => {
1921
- tries++;
1920
+ reconcileTimer = setInterval(() => {
1922
1921
  if (disposed || devtoolsWc.isDestroyed()) {
1923
1922
  stop();
1924
1923
  return;
1925
1924
  }
1926
- devtoolsWc.executeJavaScript(buildElementsHookScript()).then((status2) => {
1925
+ devtoolsWc.executeJavaScript(buildReconcileScript()).then((res) => {
1927
1926
  if (disposed) return;
1928
- if (status2 === "installed" || status2 === "already") {
1929
- if (installTimer) {
1930
- clearInterval(installTimer);
1931
- installTimer = null;
1932
- }
1927
+ const r = res ?? {};
1928
+ if (r.status === "installed") {
1933
1929
  const wc = activeRenderWc();
1934
1930
  if (wc) primeGuest(wc);
1935
1931
  pushDocumentUpdated();
1936
1932
  }
1933
+ handleOutbound(r.batch);
1937
1934
  }).catch(() => {
1938
1935
  });
1939
- if (tries > INSTALL_POLL_TRIES && installTimer) {
1940
- clearInterval(installTimer);
1941
- installTimer = null;
1942
- }
1943
- }, INSTALL_POLL_INTERVAL_MS);
1944
- drainTimer = setInterval(() => {
1945
- if (disposed || devtoolsWc.isDestroyed()) {
1946
- stop();
1947
- return;
1948
- }
1949
- devtoolsWc.executeJavaScript("(globalThis.__diminaElementsOutbound ? __diminaElementsOutbound.splice(0) : [])").then(handleOutbound).catch(() => {
1950
- });
1951
1936
  }, DRAIN_INTERVAL_MS);
1952
1937
  };
1953
1938
  devtoolsWc.on("dom-ready", onReady);
@@ -1958,7 +1943,7 @@ function installElementsForward(deps) {
1958
1943
  else devtoolsWc.once("destroyed", stop);
1959
1944
  } catch {
1960
1945
  }
1961
- return () => {
1946
+ lateCleanup = () => {
1962
1947
  try {
1963
1948
  unsubscribeRenderEvents();
1964
1949
  } catch {
@@ -1971,8 +1956,8 @@ function installElementsForward(deps) {
1971
1956
  devtoolsWc.removeListener("dom-ready", onReady);
1972
1957
  } catch {
1973
1958
  }
1974
- stop();
1975
1959
  };
1960
+ return stop;
1976
1961
  }
1977
1962
 
1978
1963
  // src/main/services/service-console/console-api.ts
@@ -2190,15 +2175,15 @@ function createSimulatorApiRegistry() {
2190
2175
  import * as electron from "electron";
2191
2176
  var SHARED_MINIAPP_PARTITION = "persist:simulator";
2192
2177
  var PARTITION_PREFIX = "persist:miniapp-";
2193
- function miniappPartitionKey(appId) {
2178
+ function miniappPartitionKey(appId, projectPath) {
2194
2179
  const safe = appId.replace(/[^A-Za-z0-9_-]/g, "");
2195
- if (safe === appId && safe.length > 0) return safe;
2196
- const hash = djb2(appId).toString(36);
2197
- return safe.length > 0 ? `${safe}-${hash}` : hash;
2180
+ const base = safe === appId && safe.length > 0 ? safe : safe.length > 0 ? `${safe}-${djb2(appId).toString(36)}` : djb2(appId).toString(36);
2181
+ if (!projectPath) return base;
2182
+ return `${base}-p${djb2(projectPath).toString(36)}`;
2198
2183
  }
2199
- function miniappPartition(appId) {
2184
+ function miniappPartition(appId, projectPath) {
2200
2185
  if (!appId) return SHARED_MINIAPP_PARTITION;
2201
- return `${PARTITION_PREFIX}${miniappPartitionKey(appId)}`;
2186
+ return `${PARTITION_PREFIX}${miniappPartitionKey(appId, projectPath)}`;
2202
2187
  }
2203
2188
  function djb2(input) {
2204
2189
  let h = 5381;
@@ -2802,13 +2787,17 @@ function createViewManager(ctx) {
2802
2787
  }
2803
2788
  const openInEditorWiredWcIds = /* @__PURE__ */ new Set();
2804
2789
  function injectOpenResourceHandler(serviceWc, devtoolsWc) {
2805
- const sourceContext = projectSourceContextFromServiceHostUrl(
2806
- serviceWc.getURL(),
2807
- ctx.workspace?.getProjectPath?.()
2808
- );
2809
- if (!sourceContext) return;
2810
- devtoolsWc.executeJavaScript(buildDevtoolsProjectSourceLinksScript(sourceContext)).catch(() => {
2811
- });
2790
+ if (serviceWc.isDestroyed() || devtoolsWc.isDestroyed()) return;
2791
+ try {
2792
+ const sourceContext = projectSourceContextFromServiceHostUrl(
2793
+ serviceWc.getURL(),
2794
+ ctx.workspace?.getProjectPath?.()
2795
+ );
2796
+ if (!sourceContext) return;
2797
+ devtoolsWc.executeJavaScript(buildDevtoolsProjectSourceLinksScript(sourceContext)).catch(() => {
2798
+ });
2799
+ } catch {
2800
+ }
2812
2801
  }
2813
2802
  function customizeDevtoolsTabs(devtoolsWc) {
2814
2803
  try {
@@ -2938,13 +2927,14 @@ function createViewManager(ctx) {
2938
2927
  stopElementsForward?.();
2939
2928
  } catch {
2940
2929
  }
2941
- stopElementsForward = installElementsForward({ devtoolsWc, bridge: ctx.bridge, connections: ctx.connections });
2930
+ const thisForward = installElementsForward({ devtoolsWc, bridge: ctx.bridge, connections: ctx.connections });
2931
+ stopElementsForward = thisForward;
2942
2932
  devtoolsWc.once("destroyed", () => {
2943
2933
  try {
2944
- stopElementsForward?.();
2934
+ thisForward();
2945
2935
  } catch {
2946
2936
  }
2947
- stopElementsForward = null;
2937
+ if (stopElementsForward === thisForward) stopElementsForward = null;
2948
2938
  });
2949
2939
  }
2950
2940
  devtoolsWc.once("dom-ready", () => {
@@ -2987,6 +2977,24 @@ function createViewManager(ctx) {
2987
2977
  nativeCustomApiBridgeHandler = null;
2988
2978
  }
2989
2979
  }
2980
+ function tearDownNativeSimulatorView(label) {
2981
+ if (!nativeSimulatorView) return;
2982
+ if (nativeSimulatorViewAdded && !ctx.windows.mainWindow.isDestroyed()) {
2983
+ try {
2984
+ ctx.windows.mainWindow.contentView.removeChildView(nativeSimulatorView);
2985
+ } catch {
2986
+ }
2987
+ }
2988
+ try {
2989
+ if (!nativeSimulatorView.webContents.isDestroyed()) {
2990
+ ctx.bridge?.disposeSessionsForSimulator?.(nativeSimulatorView.webContents.id)?.catch((err) => console.warn(`[view-manager] dispose sessions (${label}) failed:`, err));
2991
+ nativeSimulatorView.webContents.close();
2992
+ }
2993
+ } catch {
2994
+ }
2995
+ nativeSimulatorView = null;
2996
+ nativeSimulatorViewAdded = false;
2997
+ }
2990
2998
  function attachNativeCustomApiBridge(simWc) {
2991
2999
  detachNativeCustomApiBridge();
2992
3000
  const apis = ctx.simulatorApis;
@@ -3015,27 +3023,14 @@ function createViewManager(ctx) {
3015
3023
  settleNativeSimulatorReady = null;
3016
3024
  if (nativeSimulatorView) {
3017
3025
  detachNativeCustomApiBridge();
3018
- if (nativeSimulatorViewAdded) {
3019
- try {
3020
- ctx.windows.mainWindow.contentView.removeChildView(nativeSimulatorView);
3021
- } catch {
3022
- }
3023
- nativeSimulatorViewAdded = false;
3024
- }
3025
- try {
3026
- if (!nativeSimulatorView.webContents.isDestroyed()) {
3027
- nativeSimulatorView.webContents.close();
3028
- }
3029
- } catch {
3030
- }
3031
- nativeSimulatorView = null;
3026
+ tearDownNativeSimulatorView("relaunch");
3032
3027
  }
3033
3028
  nativeSimulatorProjectPath = null;
3034
3029
  const ready = new Promise((resolve) => {
3035
3030
  settleNativeSimulatorReady = resolve;
3036
3031
  });
3037
3032
  const route = parseRoute(simulatorUrl);
3038
- const partition = miniappPartition(route?.appId);
3033
+ const partition = miniappPartition(route?.appId, ctx.workspace?.getProjectPath());
3039
3034
  configureMiniappSession(partition);
3040
3035
  const view = new WebContentsView2({
3041
3036
  webPreferences: {
@@ -3145,22 +3140,7 @@ function createViewManager(ctx) {
3145
3140
  stopElementsForward = null;
3146
3141
  ctx.networkForward?.detachSimulator();
3147
3142
  ctx.networkForward?.setDevtoolsHost(null);
3148
- if (nativeSimulatorView) {
3149
- if (nativeSimulatorViewAdded && !ctx.windows.mainWindow.isDestroyed()) {
3150
- try {
3151
- ctx.windows.mainWindow.contentView.removeChildView(nativeSimulatorView);
3152
- } catch {
3153
- }
3154
- }
3155
- try {
3156
- if (!nativeSimulatorView.webContents.isDestroyed()) {
3157
- nativeSimulatorView.webContents.close();
3158
- }
3159
- } catch {
3160
- }
3161
- nativeSimulatorView = null;
3162
- nativeSimulatorViewAdded = false;
3163
- }
3143
+ tearDownNativeSimulatorView("close");
3164
3144
  hidePopover();
3165
3145
  destroyViewInternal(settingsView);
3166
3146
  settingsView = null;
@@ -3616,13 +3596,38 @@ function getSimulatorServicewechatReferer() {
3616
3596
  return cachedRefererUrl;
3617
3597
  }
3618
3598
 
3599
+ // src/main/services/workspace/op-lock.ts
3600
+ function createOpLock() {
3601
+ let chain = Promise.resolve();
3602
+ let requestSeq = 0;
3603
+ let ownerSeq = 0;
3604
+ return {
3605
+ nextSeq: () => ++requestSeq,
3606
+ takeOwnership: (seq) => {
3607
+ if (seq > ownerSeq) ownerSeq = seq;
3608
+ },
3609
+ isOwner: (seq) => seq === ownerSeq,
3610
+ acquire: () => {
3611
+ let release;
3612
+ const next = new Promise((resolve) => {
3613
+ release = resolve;
3614
+ });
3615
+ const prior = chain;
3616
+ chain = prior.then(() => next);
3617
+ return prior.then(() => release);
3618
+ }
3619
+ };
3620
+ }
3621
+
3619
3622
  // src/main/services/workspace/workspace-service.ts
3620
- var SessionAppInfoSchema = z2.looseObject({ appId: z2.string() });
3623
+ var SessionAppInfoSchema = z2.looseObject({ appId: z2.string().min(1) });
3621
3624
  function createWorkspaceService(ctx) {
3622
3625
  let currentSession = null;
3623
3626
  let currentProjectPath = "";
3627
+ let closing = false;
3624
3628
  let lastClosedProjectPath = "";
3625
3629
  let logGeneration = 0;
3630
+ const opLock = createOpLock();
3626
3631
  function sendStatus(status2, message, hotReload) {
3627
3632
  ctx.notify.projectStatus(hotReload ? { status: status2, message, hotReload: true } : { status: status2, message });
3628
3633
  }
@@ -3648,6 +3653,65 @@ function createWorkspaceService(ctx) {
3648
3653
  console.error("[workspace] session close failed:", err);
3649
3654
  }
3650
3655
  }
3656
+ async function runOpenTeardown(mySeq) {
3657
+ const release = await opLock.acquire();
3658
+ try {
3659
+ if (!opLock.isOwner(mySeq)) return true;
3660
+ clearSimulatorServicewechatReferer();
3661
+ if (currentSession !== null) {
3662
+ ctx.views.detachWorkbench();
3663
+ ctx.views.detachSimulator();
3664
+ }
3665
+ logGeneration++;
3666
+ await disposeSession();
3667
+ currentProjectPath = "";
3668
+ lastClosedProjectPath = "";
3669
+ return false;
3670
+ } finally {
3671
+ release();
3672
+ }
3673
+ }
3674
+ async function runOpenCommit(mySeq, session5, projectPath) {
3675
+ const release = await opLock.acquire();
3676
+ try {
3677
+ if (!opLock.isOwner(mySeq)) return false;
3678
+ currentSession = session5;
3679
+ currentProjectPath = projectPath;
3680
+ return true;
3681
+ } finally {
3682
+ release();
3683
+ }
3684
+ }
3685
+ async function runCompile(projectPath, sessionGeneration) {
3686
+ const { compile } = loadWorkbenchSettings();
3687
+ try {
3688
+ const session5 = await ctx.adapter.openProject({
3689
+ projectPath,
3690
+ sourcemap: true,
3691
+ fileTypes: ctx.fileTypes,
3692
+ watch: compile.watch,
3693
+ onRebuild: () => sendStatus("ready", "\u7F16\u8BD1\u5B8C\u6210\uFF0C\u5DF2\u70ED\u66F4\u65B0", true),
3694
+ onBuildError: (err) => sendStatus("error", String(err)),
3695
+ onLog: (entry) => {
3696
+ if (sessionGeneration !== logGeneration) return;
3697
+ ctx.notify.compileLog({ stream: entry.stream, text: entry.text, at: Date.now() });
3698
+ }
3699
+ });
3700
+ return { session: session5 };
3701
+ } catch (err) {
3702
+ clearSimulatorServicewechatReferer();
3703
+ return { error: String(err) };
3704
+ }
3705
+ }
3706
+ async function rejectInvalidAppId(session5) {
3707
+ if (SessionAppInfoSchema.safeParse(session5.appInfo).success) return null;
3708
+ try {
3709
+ await session5.close();
3710
+ } catch (closeErr) {
3711
+ console.warn("[workspace] closing appId-less adapter session failed (non-fatal):", closeErr);
3712
+ }
3713
+ return "adapter returned session.appInfo without a string appId \u2014 the CompilationAdapter must supply appInfo.appId";
3714
+ }
3651
3715
  function applyRefererFromSession(session5) {
3652
3716
  const appInfo = session5.appInfo;
3653
3717
  if (appInfo && typeof appInfo.appId === "string" && appInfo.appId.length > 0) {
@@ -3670,6 +3734,7 @@ function createWorkspaceService(ctx) {
3670
3734
  },
3671
3735
  validateProjectDir: async (dirPath) => provider.validateProjectDir ? provider.validateProjectDir(dirPath) : null,
3672
3736
  async openProject(projectPath) {
3737
+ const mySeq = opLock.nextSeq();
3673
3738
  if (ctx.onBeforeOpenProject) {
3674
3739
  try {
3675
3740
  await ctx.onBeforeOpenProject(projectPath);
@@ -3679,65 +3744,36 @@ function createWorkspaceService(ctx) {
3679
3744
  return { success: false, error };
3680
3745
  }
3681
3746
  }
3682
- clearSimulatorServicewechatReferer();
3683
- if (currentSession !== null) {
3684
- ctx.views.detachWorkbench();
3747
+ opLock.takeOwnership(mySeq);
3748
+ if (await runOpenTeardown(mySeq)) {
3749
+ return { success: false, error: "superseded by a newer project open/close" };
3685
3750
  }
3686
- logGeneration++;
3687
- await disposeSession();
3688
- currentProjectPath = "";
3689
- lastClosedProjectPath = "";
3690
3751
  const dirError = provider.validateProjectDir ? await provider.validateProjectDir(projectPath) : null;
3691
3752
  if (dirError) {
3692
3753
  sendStatus("error", dirError);
3693
3754
  return { success: false, error: dirError };
3694
3755
  }
3695
3756
  sendStatus("compiling", "\u6B63\u5728\u7F16\u8BD1...");
3696
- const { compile } = loadWorkbenchSettings();
3697
3757
  const sessionGeneration = ++logGeneration;
3698
- let session5;
3699
- try {
3700
- session5 = await ctx.adapter.openProject({
3701
- projectPath,
3702
- sourcemap: true,
3703
- watch: compile.watch,
3704
- onRebuild: () => sendStatus("ready", "\u7F16\u8BD1\u5B8C\u6210\uFF0C\u5DF2\u70ED\u66F4\u65B0", true),
3705
- onBuildError: (err) => sendStatus("error", String(err)),
3706
- // Per-line dmcc log (already filtered in devkit). Stamp the
3707
- // wall-clock capture time here and push verbatim on the dedicated
3708
- // compile-log channel — never through projectStatus, whose
3709
- // one-event-per-payload contract feeds compileEvents. Stale lines
3710
- // (a closed/replaced session's worker flushing its buffers) are
3711
- // dropped via the generation check.
3712
- onLog: (entry) => {
3713
- if (sessionGeneration !== logGeneration) return;
3714
- ctx.notify.compileLog({
3715
- stream: entry.stream,
3716
- text: entry.text,
3717
- at: Date.now()
3718
- });
3719
- }
3720
- });
3721
- } catch (err) {
3722
- clearSimulatorServicewechatReferer();
3723
- sendStatus("error", String(err));
3724
- return { success: false, error: String(err) };
3725
- }
3726
- if (!SessionAppInfoSchema.safeParse(session5.appInfo).success) {
3758
+ const compiled = await runCompile(projectPath, sessionGeneration);
3759
+ if ("error" in compiled) {
3760
+ sendStatus("error", compiled.error);
3761
+ return { success: false, error: compiled.error };
3762
+ }
3763
+ const session5 = compiled.session;
3764
+ const appIdError = await rejectInvalidAppId(session5);
3765
+ if (appIdError) {
3766
+ sendStatus("error", appIdError);
3767
+ return { success: false, error: appIdError };
3768
+ }
3769
+ if (!await runOpenCommit(mySeq, session5, projectPath)) {
3727
3770
  try {
3728
3771
  await session5.close();
3729
3772
  } catch (closeErr) {
3730
- console.warn(
3731
- "[workspace] closing appId-less adapter session failed (non-fatal):",
3732
- closeErr
3733
- );
3773
+ console.warn("[workspace] closing superseded open session failed (non-fatal):", closeErr);
3734
3774
  }
3735
- const error = "adapter returned session.appInfo without a string appId \u2014 the CompilationAdapter must supply appInfo.appId";
3736
- sendStatus("error", error);
3737
- return { success: false, error };
3775
+ return { success: false, error: "superseded by a newer project open/close" };
3738
3776
  }
3739
- currentSession = session5;
3740
- currentProjectPath = projectPath;
3741
3777
  bestEffort("updateLastOpened", () => {
3742
3778
  if (provider.updateLastOpened) provider.updateLastOpened(projectPath);
3743
3779
  });
@@ -3750,26 +3786,43 @@ function createWorkspaceService(ctx) {
3750
3786
  };
3751
3787
  },
3752
3788
  async closeProject() {
3753
- logGeneration++;
3754
- clearSimulatorServicewechatReferer();
3755
- await disposeSession();
3756
- if (currentProjectPath !== "") lastClosedProjectPath = currentProjectPath;
3757
- currentProjectPath = "";
3758
- ctx.views.disposeAll();
3789
+ const mySeq = opLock.nextSeq();
3790
+ opLock.takeOwnership(mySeq);
3791
+ const release = await opLock.acquire();
3792
+ try {
3793
+ if (!opLock.isOwner(mySeq)) return;
3794
+ closing = true;
3795
+ logGeneration++;
3796
+ clearSimulatorServicewechatReferer();
3797
+ await disposeSession();
3798
+ if (currentProjectPath !== "") lastClosedProjectPath = currentProjectPath;
3799
+ currentProjectPath = "";
3800
+ ctx.views.disposeAll();
3801
+ } finally {
3802
+ closing = false;
3803
+ release();
3804
+ }
3759
3805
  },
3760
3806
  getSession: () => currentSession,
3761
3807
  getProjectPath: () => currentProjectPath,
3762
3808
  getLastClosedProjectPath: () => lastClosedProjectPath,
3763
3809
  hasActiveSession: () => currentSession !== null,
3810
+ isClosing: () => closing,
3764
3811
  async captureThumbnail(projectPath) {
3765
3812
  if (!currentSession || projectPath !== currentProjectPath) return null;
3766
- const wc = ctx.views.getSimulatorWebContents();
3767
- if (!wc) return null;
3813
+ const simulatorWc = ctx.views.getSimulatorWebContents();
3814
+ if (!simulatorWc) return null;
3768
3815
  if (ctx.views.getSimulatorProjectPath() !== projectPath) return null;
3769
3816
  const session5 = currentSession;
3817
+ const bridge = ctx.bridge;
3818
+ const nativeHost = bridge?.isNativeHost() ?? false;
3819
+ const renderGuest = nativeHost ? bridge?.getActiveRenderWc() ?? null : null;
3820
+ if (nativeHost && !renderGuest) return null;
3821
+ const captureTarget = renderGuest ?? simulatorWc;
3822
+ if (captureTarget.isDestroyed()) return null;
3770
3823
  try {
3771
- const image = await wc.capturePage();
3772
- if (currentSession !== session5 || currentProjectPath !== projectPath || ctx.views.getSimulatorWebContents() !== wc || ctx.views.getSimulatorProjectPath() !== projectPath) {
3824
+ const image = await captureTarget.capturePage();
3825
+ if (currentSession !== session5 || currentProjectPath !== projectPath || ctx.views.getSimulatorWebContents() !== simulatorWc || ctx.views.getSimulatorProjectPath() !== projectPath || (nativeHost ? bridge?.getActiveRenderWc() ?? null : simulatorWc) !== captureTarget) {
3773
3826
  return null;
3774
3827
  }
3775
3828
  const dataUrl = `data:image/png;base64,${image.toPNG().toString("base64")}`;
@@ -3900,6 +3953,7 @@ function createWorkbenchContext(opts) {
3900
3953
  preloadPath: opts.preloadPath,
3901
3954
  rendererDir: opts.rendererDir,
3902
3955
  apiNamespaces: opts.apiNamespaces ?? [],
3956
+ fileTypes: opts.fileTypes ?? {},
3903
3957
  appName: opts.appName ?? "Dimina DevTools",
3904
3958
  brandingProvider: opts.brandingProvider
3905
3959
  };
@@ -4702,12 +4756,15 @@ function navigateServiceHost(win, url) {
4702
4756
  }
4703
4757
  return loaded;
4704
4758
  }
4705
- function serviceHostSpec(appId) {
4759
+ function serviceHostSpec(appId, projectPath) {
4706
4760
  return {
4707
4761
  // Per-project partition when an appId is known (so this project's service
4708
4762
  // host shares storage ONLY with its own render side); the shared partition
4709
4763
  // for the pre-warm pool's default spec (no appId — see KNOWN BLOCKER below).
4710
- partition: appId ? miniappPartition(appId) : SERVICE_HOST_PARTITION,
4764
+ // projectPath joins the key so same-appId/different-path projects isolate;
4765
+ // it must match the simulator WCV's partition (view-manager passes the same
4766
+ // (appId, projectPath)).
4767
+ partition: appId ? miniappPartition(appId, projectPath) : SERVICE_HOST_PARTITION,
4711
4768
  preloadPath: serviceHostPreloadPath,
4712
4769
  size: { width: 980, height: 720 },
4713
4770
  contextIsolation: false,
@@ -4717,7 +4774,7 @@ function serviceHostSpec(appId) {
4717
4774
  };
4718
4775
  }
4719
4776
  function createServiceHostWindow(opts) {
4720
- const win = constructServiceHostWindow({ appId: opts.appId, partition: miniappPartition(opts.appId) });
4777
+ const win = constructServiceHostWindow({ appId: opts.appId, partition: miniappPartition(opts.appId, opts.projectPath) });
4721
4778
  void navigateServiceHost(win, buildServiceHostSpawnUrl(opts));
4722
4779
  return win;
4723
4780
  }
@@ -5625,9 +5682,14 @@ function resolveCurrentApp(state, ctx, appId) {
5625
5682
  for (const ap of state.appSessions.values()) if (ap.appId === activeAppId) match = ap;
5626
5683
  if (match) return match;
5627
5684
  }
5685
+ if (ctx.workspace?.isClosing?.()) return void 0;
5628
5686
  let last;
5629
- for (const ap of state.appSessions.values()) last = ap;
5630
- return last;
5687
+ const distinctAppIds = /* @__PURE__ */ new Set();
5688
+ for (const ap of state.appSessions.values()) {
5689
+ last = ap;
5690
+ distinctAppIds.add(ap.appId);
5691
+ }
5692
+ return distinctAppIds.size <= 1 ? last : void 0;
5631
5693
  }
5632
5694
  function installBridgeRouter(ctx) {
5633
5695
  const state = {
@@ -5731,6 +5793,14 @@ function installBridgeRouter(ctx) {
5731
5793
  }
5732
5794
  }
5733
5795
  },
5796
+ disposeSessionsForSimulator: (simulatorWcId) => {
5797
+ const ids = [];
5798
+ for (const [id, ap] of state.appSessions) {
5799
+ if (ap.simulatorWc.id === simulatorWcId) ids.push(id);
5800
+ }
5801
+ return Promise.all(ids.map((id) => disposeAppSession(state, id))).then(() => {
5802
+ });
5803
+ },
5734
5804
  debugTap: state.debugTap
5735
5805
  };
5736
5806
  ctx.bridge = bridgeHandle;
@@ -5944,6 +6014,13 @@ async function handleSpawn(state, ctx, event, opts) {
5944
6014
  serviceWindow = createServiceHostWindow({
5945
6015
  bridgeId,
5946
6016
  appId,
6017
+ // Same (appId, projectPath) pair the simulator WCV uses, so this project's
6018
+ // service host and render guests land on ONE partition while a different
6019
+ // project with the same appId is isolated. Use the path captured at spawn
6020
+ // ENTRY (workspaceProjectPath), not a re-read: a project switch during the
6021
+ // awaits above would otherwise give the service host a different path than
6022
+ // the simulator WCV was built with, splitting the partition.
6023
+ projectPath: workspaceProjectPath || void 0,
5947
6024
  pagePath,
5948
6025
  pkgRoot,
5949
6026
  root,
@@ -6004,6 +6081,9 @@ async function handleSpawn(state, ctx, event, opts) {
6004
6081
  void disposeAppSession(state, appSessionId);
6005
6082
  };
6006
6083
  simulatorWc.once("destroyed", onSimulatorDestroyed);
6084
+ if (simulatorWc.isDestroyed()) {
6085
+ void disposeAppSession(state, appSessionId);
6086
+ }
6007
6087
  if (usedPool) {
6008
6088
  const bootOnServiceLoad = () => {
6009
6089
  if (serviceWindow.isDestroyed()) return;
@@ -6518,6 +6598,10 @@ async function disposeAppSession(state, appSessionId, opts = {}) {
6518
6598
  for (const page of ap.pages.values()) {
6519
6599
  if (page.renderWc && !page.renderWc.isDestroyed()) {
6520
6600
  state.wcIdToBridgeId.delete(page.renderWc.id);
6601
+ try {
6602
+ page.renderWc.close();
6603
+ } catch {
6604
+ }
6521
6605
  }
6522
6606
  state.pageSessions.delete(page.bridgeId);
6523
6607
  }
@@ -9792,6 +9876,10 @@ async function startWorkbenchCoiServer(options) {
9792
9876
  });
9793
9877
  return;
9794
9878
  }
9879
+ if (url.pathname === "/__filetypes") {
9880
+ jsonRes(res, 200, options.getFileTypes?.() ?? {});
9881
+ return;
9882
+ }
9795
9883
  if (url.pathname.startsWith("/__contrib/")) {
9796
9884
  if (!contribRoot) {
9797
9885
  res.writeHead(404);
@@ -10017,6 +10105,7 @@ function createContext(config, mainWindow, rendererDir2) {
10017
10105
  rendererDir: rendererDir2,
10018
10106
  appName: config.appName,
10019
10107
  apiNamespaces: config.apiNamespaces,
10108
+ fileTypes: config.fileTypes,
10020
10109
  brandingProvider: config.brandingProvider,
10021
10110
  // The host-supplied ProjectsProvider / template types in `shared/types`
10022
10111
  // are structurally compatible with the main-process equivalents —
@@ -10291,7 +10380,8 @@ async function createDevtoolsRuntime(config = {}) {
10291
10380
  const coiServer = await startWorkbenchCoiServer({
10292
10381
  rootDir: bundleDir,
10293
10382
  getProjectRoot: () => context.workspace.getProjectPath(),
10294
- extensionsDir: config.editorViewConfig?.extensionsDir
10383
+ extensionsDir: config.editorViewConfig?.extensionsDir,
10384
+ getFileTypes: () => context.fileTypes
10295
10385
  });
10296
10386
  context.registry.add(() => coiServer.close());
10297
10387
  context.registry.add(() => context.views.detachWorkbench());