@dimina-kit/devtools 0.4.0-dev.20260701155140 → 0.4.0-dev.20260702164903

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 (63) hide show
  1. package/dist/main/app/max-listeners-diagnostic.js +5 -1
  2. package/dist/main/index.bundle.js +1533 -1237
  3. package/dist/main/ipc/bridge-router.d.ts +28 -0
  4. package/dist/main/ipc/bridge-router.js +175 -50
  5. package/dist/main/ipc/session-listener-bag.d.ts +41 -0
  6. package/dist/main/ipc/session-listener-bag.js +40 -0
  7. package/dist/main/ipc/simulator.js +5 -1
  8. package/dist/main/services/elements-forward/index.js +19 -0
  9. package/dist/main/services/network-forward/index.js +13 -0
  10. package/dist/main/services/views/destroy-child-view.d.ts +9 -0
  11. package/dist/main/services/views/destroy-child-view.js +23 -0
  12. package/dist/main/services/views/host-toolbar-view.d.ts +21 -0
  13. package/dist/main/services/views/host-toolbar-view.js +249 -0
  14. package/dist/main/services/views/inject-when-ready.d.ts +41 -0
  15. package/dist/main/services/views/inject-when-ready.js +62 -0
  16. package/dist/main/services/views/native-simulator-devtools-host.d.ts +26 -0
  17. package/dist/main/services/views/native-simulator-devtools-host.js +484 -0
  18. package/dist/main/services/views/native-simulator-view.d.ts +28 -0
  19. package/dist/main/services/views/native-simulator-view.js +428 -0
  20. package/dist/main/services/views/overlay-panels-view.d.ts +29 -0
  21. package/dist/main/services/views/overlay-panels-view.js +150 -0
  22. package/dist/main/services/views/placement-reconciler.d.ts +73 -0
  23. package/dist/main/services/views/placement-reconciler.js +120 -0
  24. package/dist/main/services/views/resolve-project-editor-target.d.ts +13 -0
  25. package/dist/main/services/views/resolve-project-editor-target.js +33 -0
  26. package/dist/main/services/views/view-manager.d.ts +20 -16
  27. package/dist/main/services/views/view-manager.js +66 -1545
  28. package/dist/main/services/views/workbench-view.d.ts +15 -0
  29. package/dist/main/services/views/workbench-view.js +170 -0
  30. package/dist/main/services/workspace/workspace-service.js +1 -1
  31. package/dist/preload/windows/host-toolbar-runtime.cjs.map +2 -2
  32. package/dist/preload/windows/main.cjs +10 -1
  33. package/dist/preload/windows/main.cjs.map +2 -2
  34. package/dist/preload/windows/simulator.cjs +6 -0
  35. package/dist/preload/windows/simulator.cjs.map +2 -2
  36. package/dist/preload/windows/simulator.js +6 -0
  37. package/dist/renderer/assets/index-B-Dqb7G0.js +49 -0
  38. package/dist/renderer/assets/{input-Djg2NR4U.js → input-Cjk0wSEh.js} +2 -2
  39. package/dist/renderer/assets/ipc-transport-D5dKIti1.css +1 -0
  40. package/dist/renderer/assets/{ipc-transport-Be-AzCub.js → ipc-transport-OZS-KmOW.js} +2 -2
  41. package/dist/renderer/assets/{popover-eDUh4NYe.js → popover-CQVvOe90.js} +2 -2
  42. package/dist/renderer/assets/{select-DesFfkVV.js → select-BQDXi5ih.js} +2 -2
  43. package/dist/renderer/assets/{settings-api-HQ7ZFPWi.js → settings-api-B6x2mhaD.js} +2 -2
  44. package/dist/renderer/assets/{settings-CRmO7zUG.js → settings-tP3Px2KR.js} +2 -2
  45. package/dist/renderer/assets/{workbenchSettings-CZBWsliT.js → workbenchSettings-Bim9ol9R.js} +2 -2
  46. package/dist/renderer/entries/main/index.html +6 -6
  47. package/dist/renderer/entries/popover/index.html +5 -5
  48. package/dist/renderer/entries/settings/index.html +5 -5
  49. package/dist/renderer/entries/workbench-settings/index.html +4 -4
  50. package/dist/shared/bridge-channels.d.ts +17 -0
  51. package/dist/shared/bridge-channels.js +9 -0
  52. package/dist/shared/ipc-channels.d.ts +1 -0
  53. package/dist/shared/ipc-channels.js +6 -0
  54. package/dist/shared/ipc-schemas.d.ts +6 -0
  55. package/dist/shared/ipc-schemas.js +10 -0
  56. package/dist/simulator/assets/{bridge-channels-BUQ5AbvJ.js → bridge-channels-zhsumfky.js} +2 -2
  57. package/dist/simulator/assets/{device-shell-CT3LRnTB.js → device-shell-CUl0ILfn.js} +2 -2
  58. package/dist/simulator/assets/{simulator-BizhGGE3.js → simulator-Dz8iGcgy.js} +5 -5
  59. package/dist/simulator/assets/{simulator-mini-app-BfNuDF3R.js → simulator-mini-app-Bt639XL_.js} +2 -2
  60. package/dist/simulator/simulator.html +2 -1
  61. package/package.json +5 -5
  62. package/dist/renderer/assets/index-jDrNHghH.js +0 -49
  63. package/dist/renderer/assets/ipc-transport-C6-KPMqw.css +0 -1
@@ -130,7 +130,7 @@ function describeMaxListenersWarning(warning) {
130
130
  stack: warning.stack
131
131
  };
132
132
  }
133
- function installMaxListenersWarningDiagnostic(log3 = (r) => console.warn("[max-listeners]", r)) {
133
+ function installMaxListenersWarningDiagnostic(log3 = (r) => console.warn("[max-listeners]", JSON.stringify(r))) {
134
134
  const onWarning = (warning) => {
135
135
  const report = describeMaxListenersWarning(warning);
136
136
  if (report) log3(report);
@@ -345,6 +345,12 @@ var SimulatorChannel = {
345
345
  // window — the authoritative `wx.getSystemInfoSync()` source — so the
346
346
  // mini-app sees the selected device without a relaunch.
347
347
  SetDeviceInfo: "simulator:set-device-info",
348
+ // Ask main to soft-reload the LIVE simulator WCV after a watcher rebuild:
349
+ // main forwards a SIMULATOR_EVENTS.RELAUNCH into the shell (which boots a
350
+ // new app session and swaps when ready) instead of destroying the view.
351
+ // Resolves false when there is no live+ready shell — the renderer then falls
352
+ // back to the hard AttachNative rebuild.
353
+ SoftReload: "simulator:soft-reload",
348
354
  Detach: "simulator:detach",
349
355
  Console: "simulator:console",
350
356
  // Main → renderer push of the visible top-of-stack page route whenever the
@@ -847,7 +853,7 @@ function registerProjectFsIpc(ctx) {
847
853
  // src/main/app/app.ts
848
854
  import { app as app15, BrowserWindow as BrowserWindow8, nativeImage, session as session4 } from "electron";
849
855
  import fs12 from "fs";
850
- import path22 from "path";
856
+ import path23 from "path";
851
857
 
852
858
  // src/main/utils/paths.ts
853
859
  import fs3 from "fs";
@@ -1134,10 +1140,341 @@ function createRendererNotifier(ctx) {
1134
1140
  };
1135
1141
  }
1136
1142
 
1137
- // src/main/services/views/view-manager.ts
1138
- import { ipcMain as ipcMain2, nativeTheme as nativeTheme3, shell as shell2, WebContentsView as WebContentsView2, webContents } from "electron";
1139
- import fs4 from "node:fs";
1140
- import path7 from "path";
1143
+ // src/main/services/safe-area/index.ts
1144
+ function guestInsets(device, isTabPage2) {
1145
+ const top = device?.safeAreaInsets.top ?? 0;
1146
+ const bottom = isTabPage2 ? 0 : device?.safeAreaInsets.bottom ?? 0;
1147
+ return { top, topMax: top, right: 0, rightMax: 0, bottom, bottomMax: bottom, left: 0, leftMax: 0 };
1148
+ }
1149
+ function createSafeAreaController(options = {}) {
1150
+ const attached = /* @__PURE__ */ new Map();
1151
+ function override(wc, device, isTabPage2) {
1152
+ if (wc.isDestroyed()) return;
1153
+ void wc.debugger.sendCommand("Emulation.setSafeAreaInsetsOverride", { insets: guestInsets(device, isTabPage2) }).catch((err) => {
1154
+ console.warn("[safe-area] setSafeAreaInsetsOverride failed:", err instanceof Error ? err.message : err);
1155
+ });
1156
+ }
1157
+ return {
1158
+ applyToGuest: (wc, device, isTabPage2) => {
1159
+ if (!wc || wc.isDestroyed() || attached.has(wc)) {
1160
+ if (wc && !wc.isDestroyed() && attached.has(wc)) override(wc, device, isTabPage2);
1161
+ return;
1162
+ }
1163
+ try {
1164
+ wc.debugger.attach("1.3");
1165
+ } catch (err) {
1166
+ console.warn("[safe-area] debugger.attach failed; env(safe-area-inset-*) stays 0:", err instanceof Error ? err.message : err);
1167
+ return;
1168
+ }
1169
+ attached.set(wc, isTabPage2);
1170
+ if (options.connections) {
1171
+ options.connections.acquire(wc).own(() => attached.delete(wc));
1172
+ } else {
1173
+ wc.once("destroyed", () => attached.delete(wc));
1174
+ }
1175
+ override(wc, device, isTabPage2);
1176
+ },
1177
+ reapplyAll: (device) => {
1178
+ for (const [wc, isTabPage2] of attached) override(wc, device, isTabPage2);
1179
+ },
1180
+ dispose: () => {
1181
+ for (const wc of attached.keys()) {
1182
+ try {
1183
+ if (!wc.isDestroyed()) wc.debugger.detach();
1184
+ } catch {
1185
+ }
1186
+ }
1187
+ attached.clear();
1188
+ }
1189
+ };
1190
+ }
1191
+
1192
+ // src/main/services/views/placement-reconciler.ts
1193
+ import { reconcile, createInitialState } from "@dimina-kit/electron-deck/layout";
1194
+
1195
+ // src/main/services/views/apply-view-ops.ts
1196
+ function applyViewOps(ops, target) {
1197
+ for (const op of ops) {
1198
+ switch (op.kind) {
1199
+ case "detach":
1200
+ target.detach(op.viewId);
1201
+ break;
1202
+ case "setVisible":
1203
+ target.setVisible(op.viewId, op.visible);
1204
+ break;
1205
+ case "setBounds":
1206
+ target.setBounds(op.viewId, op.bounds, op.extra);
1207
+ break;
1208
+ case "attach":
1209
+ target.attach(op.viewId);
1210
+ break;
1211
+ case "reorder":
1212
+ target.reorder(op.order);
1213
+ break;
1214
+ }
1215
+ }
1216
+ }
1217
+
1218
+ // src/main/services/views/placement-reconciler.ts
1219
+ function createPlacementReconciler(ctx) {
1220
+ let placementState = createInitialState();
1221
+ let epochCounter = 0;
1222
+ let rendererGeneration = 0;
1223
+ const baseDesired = /* @__PURE__ */ new Map();
1224
+ const overlayDesired = /* @__PURE__ */ new Map();
1225
+ const slots = /* @__PURE__ */ new Map();
1226
+ function gateReadiness(v) {
1227
+ const slot = slots.get(v.viewId);
1228
+ if (slot?.gateHidden?.()) return { ...v, placement: { visible: false } };
1229
+ return v;
1230
+ }
1231
+ const viewTarget = {
1232
+ attach(viewId) {
1233
+ if (ctx.windows.mainWindow.isDestroyed()) return;
1234
+ const slot = slots.get(viewId);
1235
+ if (!slot) return;
1236
+ if (slot.beforeAttach?.()) return;
1237
+ const view = slot.ensureView ? slot.ensureView() : slot.getView();
1238
+ if (!view) return;
1239
+ ctx.windows.mainWindow.contentView.addChildView(view);
1240
+ slot.setAdded?.(true);
1241
+ },
1242
+ detach(viewId) {
1243
+ const slot = slots.get(viewId);
1244
+ const view = slot?.getView() ?? null;
1245
+ if (view && !ctx.windows.mainWindow.isDestroyed()) {
1246
+ try {
1247
+ ctx.windows.mainWindow.contentView.removeChildView(view);
1248
+ } catch {
1249
+ }
1250
+ }
1251
+ slot?.setAdded?.(false);
1252
+ },
1253
+ setBounds(viewId, bounds, extra) {
1254
+ const slot = slots.get(viewId);
1255
+ const view = slot?.getView() ?? null;
1256
+ if (!view || view.webContents.isDestroyed()) return;
1257
+ if (slot?.applyBounds) {
1258
+ slot.applyBounds(view, bounds, extra);
1259
+ return;
1260
+ }
1261
+ view.setBounds(bounds);
1262
+ },
1263
+ setVisible(viewId, visible) {
1264
+ const view = slots.get(viewId)?.getView() ?? null;
1265
+ if (!view) return;
1266
+ try {
1267
+ view.setVisible(visible);
1268
+ } catch {
1269
+ }
1270
+ },
1271
+ reorder(order) {
1272
+ if (order.length <= 1 || ctx.windows.mainWindow.isDestroyed()) return;
1273
+ const cv = ctx.windows.mainWindow.contentView;
1274
+ for (const viewId of order) {
1275
+ const view = slots.get(viewId)?.getView() ?? null;
1276
+ if (view) {
1277
+ try {
1278
+ cv.addChildView(view);
1279
+ } catch {
1280
+ }
1281
+ }
1282
+ }
1283
+ }
1284
+ };
1285
+ function ensureLazyViews() {
1286
+ for (const [viewId, slot] of slots) {
1287
+ slot.ensureLazy?.(baseDesired.get(viewId));
1288
+ }
1289
+ }
1290
+ function reconcileNow() {
1291
+ ensureLazyViews();
1292
+ const views = [];
1293
+ for (const v of baseDesired.values()) views.push(gateReadiness(v));
1294
+ for (const v of overlayDesired.values()) views.push(v);
1295
+ const result = reconcile(placementState, {
1296
+ generation: rendererGeneration,
1297
+ epoch: ++epochCounter,
1298
+ views
1299
+ });
1300
+ placementState = result.state;
1301
+ applyViewOps(result.ops, viewTarget);
1302
+ }
1303
+ function setPlacementSnapshot(snapshot) {
1304
+ rendererGeneration = snapshot.generation;
1305
+ baseDesired.clear();
1306
+ for (const v of snapshot.views) baseDesired.set(v.viewId, v);
1307
+ reconcileNow();
1308
+ }
1309
+ return {
1310
+ registerView: (viewId, slot) => {
1311
+ slots.set(viewId, slot);
1312
+ },
1313
+ reconcileNow,
1314
+ setPlacementSnapshot,
1315
+ forgetActual: (viewId) => {
1316
+ placementState.actual.delete(viewId);
1317
+ },
1318
+ setBaseDesired: (viewId, desired) => {
1319
+ baseDesired.set(viewId, desired);
1320
+ },
1321
+ deleteBaseDesired: (viewId) => {
1322
+ baseDesired.delete(viewId);
1323
+ },
1324
+ setOverlayDesired: (viewId, desired) => {
1325
+ overlayDesired.set(viewId, desired);
1326
+ },
1327
+ deleteOverlayDesired: (viewId) => {
1328
+ overlayDesired.delete(viewId);
1329
+ },
1330
+ hasOverlayDesired: (viewId) => overlayDesired.has(viewId)
1331
+ };
1332
+ }
1333
+
1334
+ // src/main/services/views/workbench-view.ts
1335
+ import { nativeTheme as nativeTheme3, WebContentsView as WebContentsView2 } from "electron";
1336
+
1337
+ // src/shared/view-ids.ts
1338
+ var VIEW_ID = {
1339
+ simulator: "simulator",
1340
+ simulatorDevtools: "simulator-devtools",
1341
+ workbench: "workbench",
1342
+ hostToolbar: "host-toolbar",
1343
+ settings: "settings",
1344
+ popover: "popover"
1345
+ };
1346
+ var VIEW_LAYER = {
1347
+ base: 0,
1348
+ hostToolbar: 5,
1349
+ settings: 10,
1350
+ popover: 20
1351
+ };
1352
+
1353
+ // src/main/services/views/destroy-child-view.ts
1354
+ function destroyChildView(mainWindow, view) {
1355
+ if (!view) return;
1356
+ if (!mainWindow.isDestroyed()) {
1357
+ try {
1358
+ mainWindow.contentView.removeChildView(view);
1359
+ } catch {
1360
+ }
1361
+ }
1362
+ try {
1363
+ if (!view.webContents.isDestroyed()) {
1364
+ view.webContents.close();
1365
+ }
1366
+ } catch {
1367
+ }
1368
+ }
1369
+
1370
+ // src/main/services/views/workbench-view.ts
1371
+ function createWorkbenchView(ctx, reconciler) {
1372
+ let workbenchView = null;
1373
+ let workbenchThemeSyncBound = false;
1374
+ let workbenchUrl = null;
1375
+ function workbenchThemeScheme() {
1376
+ return nativeTheme3.shouldUseDarkColors ? "dark" : "light";
1377
+ }
1378
+ function pushWorkbenchTheme() {
1379
+ if (!workbenchView || workbenchView.webContents.isDestroyed()) return;
1380
+ const wc = workbenchView.webContents;
1381
+ if (typeof wc.executeJavaScript !== "function") return;
1382
+ const script = `window.__WB_SET_THEME && window.__WB_SET_THEME(${JSON.stringify(workbenchThemeScheme())})`;
1383
+ wc.executeJavaScript(script, true).catch(() => {
1384
+ });
1385
+ }
1386
+ async function attachWorkbench(url) {
1387
+ if (workbenchView) return;
1388
+ const view = new WebContentsView2({
1389
+ webPreferences: {
1390
+ nodeIntegration: false,
1391
+ contextIsolation: true,
1392
+ sandbox: false
1393
+ }
1394
+ });
1395
+ workbenchView = view;
1396
+ if (!workbenchThemeSyncBound) {
1397
+ nativeTheme3.on("updated", pushWorkbenchTheme);
1398
+ workbenchThemeSyncBound = true;
1399
+ }
1400
+ try {
1401
+ view.webContents.setWindowOpenHandler(({ url: target }) => handleWindowOpenExternal(target));
1402
+ } catch {
1403
+ }
1404
+ ctx.windows.mainWindow.contentView.addChildView(view);
1405
+ const loadUrl = `${url}index.html?theme=${workbenchThemeScheme()}`;
1406
+ await view.webContents.loadURL(loadUrl).catch((err) => {
1407
+ console.error("[workbench] attachWorkbench \u2014 loadURL failed", err);
1408
+ });
1409
+ }
1410
+ function setWorkbenchSource(url) {
1411
+ workbenchUrl = url;
1412
+ }
1413
+ function detachWorkbench() {
1414
+ if (workbenchThemeSyncBound) {
1415
+ nativeTheme3.removeListener("updated", pushWorkbenchTheme);
1416
+ workbenchThemeSyncBound = false;
1417
+ }
1418
+ destroyChildView(ctx.windows.mainWindow, workbenchView);
1419
+ workbenchView = null;
1420
+ reconciler.deleteBaseDesired(VIEW_ID.workbench);
1421
+ reconciler.reconcileNow();
1422
+ }
1423
+ function workspaceUriFor(rel) {
1424
+ const encoded = rel.split("/").map(encodeURIComponent).join("/");
1425
+ return `file:///workspace/${encoded}`;
1426
+ }
1427
+ function tryRevealInWorkbench(uri, zeroLine, zeroCol) {
1428
+ if (!workbenchView || workbenchView.webContents.isDestroyed()) {
1429
+ return Promise.resolve(false);
1430
+ }
1431
+ const script = `(async () => {
1432
+ try {
1433
+ const P = window.__WB_PROBE; if (!P) return false
1434
+ const vscode = P.vscode
1435
+ const uri = vscode.Uri.parse(${JSON.stringify(uri)})
1436
+ const doc = await vscode.workspace.openTextDocument(uri)
1437
+ const pos = new vscode.Position(${zeroLine}, ${zeroCol})
1438
+ await vscode.window.showTextDocument(doc, { selection: new vscode.Range(pos, pos) })
1439
+ return true
1440
+ } catch (e) { return false }
1441
+ })()`;
1442
+ return workbenchView.webContents.executeJavaScript(script, true).then((ok) => ok === true).catch(() => false);
1443
+ }
1444
+ function openFileInWorkbench(relPath, line, column) {
1445
+ if (!workbenchView || workbenchView.webContents.isDestroyed()) return false;
1446
+ const uri = workspaceUriFor(relPath.replace(/^\/+/, ""));
1447
+ const zeroLine = Math.max(0, Math.floor(line) - 1);
1448
+ const zeroCol = Math.max(0, Math.floor(column) - 1);
1449
+ void (async () => {
1450
+ for (let attempt = 0; attempt < 10; attempt++) {
1451
+ if (!workbenchView || workbenchView.webContents.isDestroyed()) return;
1452
+ if (await tryRevealInWorkbench(uri, zeroLine, zeroCol)) return;
1453
+ await new Promise((resolve) => setTimeout(resolve, 150));
1454
+ }
1455
+ console.error("[workbench] openFileInWorkbench: workbench never became ready for", uri);
1456
+ })();
1457
+ return true;
1458
+ }
1459
+ reconciler.registerView(VIEW_ID.workbench, {
1460
+ getView: () => workbenchView,
1461
+ gateHidden: () => !workbenchView && !workbenchUrl,
1462
+ beforeAttach: () => {
1463
+ if (!workbenchView && workbenchUrl) {
1464
+ void attachWorkbench(workbenchUrl);
1465
+ return true;
1466
+ }
1467
+ return false;
1468
+ },
1469
+ ensureLazy: (desired) => {
1470
+ if (desired?.placement.visible && !workbenchView && workbenchUrl) void attachWorkbench(workbenchUrl);
1471
+ }
1472
+ });
1473
+ return { attachWorkbench, setWorkbenchSource, detachWorkbench, openFileInWorkbench };
1474
+ }
1475
+
1476
+ // src/main/services/views/native-simulator-devtools-host.ts
1477
+ import { WebContentsView as WebContentsView3 } from "electron";
1141
1478
 
1142
1479
  // src/shared/open-in-editor.ts
1143
1480
  var OPEN_IN_EDITOR_SCHEME = "dimina-open-in-editor";
@@ -1508,56 +1845,7 @@ function buildDevtoolsProjectSourceLinksScript(context) {
1508
1845
  }, 50)
1509
1846
  } catch (_) {}
1510
1847
  })()
1511
- `;
1512
- }
1513
-
1514
- // src/main/services/safe-area/index.ts
1515
- function guestInsets(device, isTabPage2) {
1516
- const top = device?.safeAreaInsets.top ?? 0;
1517
- const bottom = isTabPage2 ? 0 : device?.safeAreaInsets.bottom ?? 0;
1518
- return { top, topMax: top, right: 0, rightMax: 0, bottom, bottomMax: bottom, left: 0, leftMax: 0 };
1519
- }
1520
- function createSafeAreaController(options = {}) {
1521
- const attached = /* @__PURE__ */ new Map();
1522
- function override(wc, device, isTabPage2) {
1523
- if (wc.isDestroyed()) return;
1524
- void wc.debugger.sendCommand("Emulation.setSafeAreaInsetsOverride", { insets: guestInsets(device, isTabPage2) }).catch((err) => {
1525
- console.warn("[safe-area] setSafeAreaInsetsOverride failed:", err instanceof Error ? err.message : err);
1526
- });
1527
- }
1528
- return {
1529
- applyToGuest: (wc, device, isTabPage2) => {
1530
- if (!wc || wc.isDestroyed() || attached.has(wc)) {
1531
- if (wc && !wc.isDestroyed() && attached.has(wc)) override(wc, device, isTabPage2);
1532
- return;
1533
- }
1534
- try {
1535
- wc.debugger.attach("1.3");
1536
- } catch (err) {
1537
- console.warn("[safe-area] debugger.attach failed; env(safe-area-inset-*) stays 0:", err instanceof Error ? err.message : err);
1538
- return;
1539
- }
1540
- attached.set(wc, isTabPage2);
1541
- if (options.connections) {
1542
- options.connections.acquire(wc).own(() => attached.delete(wc));
1543
- } else {
1544
- wc.once("destroyed", () => attached.delete(wc));
1545
- }
1546
- override(wc, device, isTabPage2);
1547
- },
1548
- reapplyAll: (device) => {
1549
- for (const [wc, isTabPage2] of attached) override(wc, device, isTabPage2);
1550
- },
1551
- dispose: () => {
1552
- for (const wc of attached.keys()) {
1553
- try {
1554
- if (!wc.isDestroyed()) wc.debugger.detach();
1555
- } catch {
1556
- }
1557
- }
1558
- attached.clear();
1559
- }
1560
- };
1848
+ `;
1561
1849
  }
1562
1850
 
1563
1851
  // src/main/services/views/devtools-tabs.ts
@@ -1629,6 +1917,44 @@ function buildCustomizeTabsScript(keptIds = DEVTOOLS_KEPT_VIEW_IDS) {
1629
1917
 
1630
1918
  // src/main/services/elements-forward/index.ts
1631
1919
  import { webContents as electronWebContents } from "electron";
1920
+
1921
+ // src/main/services/views/inject-when-ready.ts
1922
+ function isFrontendSettled(wc) {
1923
+ try {
1924
+ if (wc.isDestroyed()) return false;
1925
+ const url = wc.getURL();
1926
+ const mainFrameLoading = typeof wc.isLoadingMainFrame === "function" ? wc.isLoadingMainFrame() : wc.isLoading();
1927
+ return !mainFrameLoading && url !== "" && url !== "about:blank";
1928
+ } catch {
1929
+ return false;
1930
+ }
1931
+ }
1932
+ function createLoadDeferredInjector() {
1933
+ const pending = /* @__PURE__ */ new WeakMap();
1934
+ return function injectWhenReady(wc, kind, run) {
1935
+ if (wc.isDestroyed()) return;
1936
+ if (isFrontendSettled(wc)) {
1937
+ run();
1938
+ return;
1939
+ }
1940
+ let kinds = pending.get(wc);
1941
+ if (!kinds) {
1942
+ kinds = /* @__PURE__ */ new Map();
1943
+ pending.set(wc, kinds);
1944
+ }
1945
+ const alreadyHooked = kinds.has(kind);
1946
+ kinds.set(kind, run);
1947
+ if (alreadyHooked) return;
1948
+ wc.once("did-stop-loading", () => {
1949
+ const runner = kinds.get(kind);
1950
+ kinds.delete(kind);
1951
+ if (!runner || wc.isDestroyed()) return;
1952
+ runner();
1953
+ });
1954
+ };
1955
+ }
1956
+
1957
+ // src/main/services/elements-forward/index.ts
1632
1958
  var RENDER_DOMAIN_PREFIXES = [
1633
1959
  "DOM.",
1634
1960
  "CSS.",
@@ -1745,6 +2071,7 @@ function installElementsForward(deps) {
1745
2071
  }
1746
2072
  function dispatchToFrontend(message) {
1747
2073
  if (disposed || devtoolsWc.isDestroyed()) return;
2074
+ if (!isFrontendSettled(devtoolsWc)) return;
1748
2075
  let json;
1749
2076
  try {
1750
2077
  json = JSON.stringify(message);
@@ -1777,6 +2104,7 @@ function installElementsForward(deps) {
1777
2104
  }
1778
2105
  function pushDocumentUpdated() {
1779
2106
  if (disposed || devtoolsWc.isDestroyed()) return;
2107
+ if (!isFrontendSettled(devtoolsWc)) return;
1780
2108
  let script;
1781
2109
  try {
1782
2110
  script = buildDocumentUpdatedScript();
@@ -1949,6 +2277,7 @@ function installElementsForward(deps) {
1949
2277
  stop();
1950
2278
  return;
1951
2279
  }
2280
+ if (!isFrontendSettled(devtoolsWc)) return;
1952
2281
  devtoolsWc.executeJavaScript(buildReconcileScript()).then((res) => {
1953
2282
  if (disposed) return;
1954
2283
  const r = res ?? {};
@@ -2058,429 +2387,84 @@ function installServiceConsoleForward(deps) {
2058
2387
  }
2059
2388
  }
2060
2389
  }
2061
- async function resolveArg(ro) {
2062
- if (!needsDeepFetch(ro)) return remoteObjectToValue(ro);
2063
- try {
2064
- const res = await serviceWc.debugger.sendCommand("Runtime.callFunctionOn", {
2065
- objectId: ro.objectId,
2066
- functionDeclaration: "function () { return this }",
2067
- returnByValue: true
2068
- });
2069
- if (res && res.result && "value" in res.result) return res.result.value;
2070
- } catch {
2071
- }
2072
- return remoteObjectToValue(ro);
2073
- }
2074
- async function handleConsoleApi(params) {
2075
- if (disposed) return;
2076
- if (isRenderForwardEvent(params, RENDER_FORWARD_SOURCE_URL)) return;
2077
- const level = mapConsoleApiType(params.type);
2078
- const rawArgs = Array.isArray(params.args) ? params.args : [];
2079
- let args;
2080
- try {
2081
- args = await Promise.all(rawArgs.map((a) => resolveArg(a)));
2082
- } catch {
2083
- args = rawArgs.map((a) => remoteObjectToValue(a));
2084
- }
2085
- if (disposed) return;
2086
- emit({ source: "service", level, args, ts: Date.now() });
2087
- }
2088
- const onMessage = (_event, method, params) => {
2089
- if (disposed) return;
2090
- if (method !== "Runtime.consoleAPICalled") return;
2091
- const p = params;
2092
- tail = tail.then(() => handleConsoleApi(p)).catch(() => {
2093
- });
2094
- };
2095
- function stop() {
2096
- if (disposed) return;
2097
- disposed = true;
2098
- try {
2099
- closedSub?.dispose();
2100
- } catch {
2101
- }
2102
- try {
2103
- serviceWc.debugger.removeListener("message", onMessage);
2104
- } catch {
2105
- }
2106
- if (selfAttached) {
2107
- try {
2108
- if (!serviceWc.isDestroyed() && serviceWc.debugger.isAttached()) serviceWc.debugger.detach();
2109
- } catch {
2110
- }
2111
- selfAttached = false;
2112
- }
2113
- }
2114
- if (!usableDebugger()) {
2115
- disposed = true;
2116
- return { stop() {
2117
- } };
2118
- }
2119
- try {
2120
- serviceWc.debugger.on("message", onMessage);
2121
- } catch {
2122
- stop();
2123
- return { stop() {
2124
- } };
2125
- }
2126
- serviceWc.debugger.sendCommand("Runtime.enable").catch(() => {
2127
- });
2128
- try {
2129
- if (connections) closedSub = connections.acquire(serviceWc).on("closed", stop);
2130
- else serviceWc.once("destroyed", stop);
2131
- } catch {
2132
- }
2133
- return { stop };
2134
- }
2135
-
2136
- // src/main/services/layout/index.ts
2137
- function computeNativeSimulatorViewParams(rect, zoomPercent) {
2138
- const zoomFactor = zoomPercent / 100;
2139
- return {
2140
- bounds: {
2141
- x: Math.round(rect.x),
2142
- y: Math.round(rect.y),
2143
- width: Math.max(0, Math.round(rect.width)),
2144
- height: Math.max(0, Math.round(rect.height))
2145
- },
2146
- zoomFactor
2147
- };
2148
- }
2149
- function computeSettingsBounds(contentWidth, contentHeight, headerHeight) {
2150
- return {
2151
- x: 0,
2152
- y: headerHeight,
2153
- width: Math.max(1, contentWidth),
2154
- height: Math.max(1, contentHeight - headerHeight)
2155
- };
2156
- }
2157
- function computePopoverBounds(contentWidth, contentHeight, headerHeight) {
2158
- return {
2159
- x: 0,
2160
- y: headerHeight,
2161
- width: Math.max(1, contentWidth),
2162
- height: contentHeight - headerHeight
2163
- };
2164
- }
2165
-
2166
- // src/main/services/simulator/custom-apis.ts
2167
- async function handleCustomApiBridgeRequest(apis, req) {
2168
- try {
2169
- const result = req.op === "list" ? apis.list() : await apis.invoke(req.name, req.params);
2170
- return { id: req.id, result };
2171
- } catch (err) {
2172
- return { id: req.id, error: err instanceof Error ? err.message : String(err) };
2173
- }
2174
- }
2175
- function createSimulatorApiRegistry() {
2176
- const handlers2 = /* @__PURE__ */ new Map();
2177
- return {
2178
- register(name, handler) {
2179
- handlers2.set(name, handler);
2180
- return () => {
2181
- if (handlers2.get(name) === handler) handlers2.delete(name);
2182
- };
2183
- },
2184
- list() {
2185
- return Array.from(handlers2.keys());
2186
- },
2187
- has(name) {
2188
- return handlers2.has(name);
2189
- },
2190
- async invoke(name, params) {
2191
- const handler = handlers2.get(name);
2192
- if (!handler) throw new Error(`Simulator API "${name}" is not registered`);
2193
- return await handler(params);
2194
- },
2195
- clear() {
2196
- handlers2.clear();
2197
- }
2198
- };
2199
- }
2200
-
2201
- // src/main/services/views/miniapp-partition.ts
2202
- import * as electron from "electron";
2203
- var SHARED_MINIAPP_PARTITION = "persist:simulator";
2204
- var PARTITION_PREFIX = "persist:miniapp-";
2205
- function miniappPartitionKey(appId, projectPath) {
2206
- const safe = appId.replace(/[^A-Za-z0-9_-]/g, "");
2207
- const base = safe === appId && safe.length > 0 ? safe : safe.length > 0 ? `${safe}-${djb2(appId).toString(36)}` : djb2(appId).toString(36);
2208
- if (!projectPath) return base;
2209
- return `${base}-p${djb2(projectPath).toString(36)}`;
2210
- }
2211
- function miniappPartition(appId, projectPath) {
2212
- if (!appId) return SHARED_MINIAPP_PARTITION;
2213
- return `${PARTITION_PREFIX}${miniappPartitionKey(appId, projectPath)}`;
2214
- }
2215
- function djb2(input) {
2216
- let h = 5381;
2217
- for (let i = 0; i < input.length; i++) {
2218
- h = (h << 5) + h + input.charCodeAt(i) >>> 0;
2219
- }
2220
- return h;
2221
- }
2222
- var configurators = /* @__PURE__ */ new Set();
2223
- var configuredPartitions = /* @__PURE__ */ new Set();
2224
- function registerMiniappSessionConfigurator(fn) {
2225
- configurators.add(fn);
2226
- for (const partition of configuredPartitions) {
2227
- try {
2228
- fn(electron.session.fromPartition(partition), partition);
2229
- } catch (err) {
2230
- console.warn("[miniapp-partition] configurator failed for", partition, err);
2231
- }
2232
- }
2233
- return () => {
2234
- configurators.delete(fn);
2235
- };
2236
- }
2237
- function configureMiniappSession(partition) {
2238
- const alreadyConfigured = configuredPartitions.has(partition);
2239
- configuredPartitions.add(partition);
2240
- if (configurators.size === 0) return null;
2241
- const sess = electron.session.fromPartition(partition);
2242
- if (alreadyConfigured) return sess;
2243
- for (const fn of configurators) {
2244
- try {
2245
- fn(sess, partition);
2246
- } catch (err) {
2247
- console.warn("[miniapp-partition] configurator failed for", partition, err);
2248
- }
2249
- }
2250
- return sess;
2251
- }
2252
-
2253
- // src/main/services/views/host-toolbar-session-runtime.ts
2254
- import { session as session2 } from "electron";
2255
- var refCount = 0;
2256
- var registrationId = null;
2257
- function acquireHostToolbarSessionRuntime() {
2258
- if (refCount === 0) {
2259
- registrationId = session2.defaultSession.registerPreloadScript({
2260
- type: "frame",
2261
- filePath: hostToolbarRuntimePreloadPath
2262
- });
2263
- }
2264
- refCount++;
2265
- }
2266
- function releaseHostToolbarSessionRuntime() {
2267
- if (refCount === 0) return;
2268
- refCount--;
2269
- if (refCount === 0 && registrationId !== null) {
2270
- session2.defaultSession.unregisterPreloadScript(registrationId);
2271
- registrationId = null;
2272
- }
2273
- }
2274
-
2275
- // src/main/services/views/host-toolbar-port-channel.ts
2276
- import { MessageChannelMain } from "electron";
2277
- function createHostToolbarPortChannel(opts) {
2278
- let activePort = null;
2279
- let activeWc = null;
2280
- let state = "absent";
2281
- let generation = 0;
2282
- const handlers2 = [];
2283
- const readyHandlers = [];
2284
- function dispatch(data) {
2285
- if (typeof data !== "object" || data === null) return;
2286
- const { channel, payload } = data;
2287
- if (typeof channel !== "string") return;
2288
- for (const entry of [...handlers2]) {
2289
- if (entry.channel === channel) entry.handler(payload);
2290
- }
2291
- }
2292
- function dropActivePort(close, next) {
2293
- const port = activePort;
2294
- activePort = null;
2295
- activeWc = null;
2296
- if (state !== "disposed") state = next;
2297
- if (close && port) {
2298
- try {
2299
- port.close();
2300
- } catch {
2301
- }
2302
- }
2303
- }
2304
- function invokeReadyHandler(handler) {
2390
+ async function resolveArg(ro) {
2391
+ if (!needsDeepFetch(ro)) return remoteObjectToValue(ro);
2305
2392
  try {
2306
- handler();
2307
- } catch (err) {
2308
- console.error("[host-toolbar] onReady handler threw:", err);
2393
+ const res = await serviceWc.debugger.sendCommand("Runtime.callFunctionOn", {
2394
+ objectId: ro.objectId,
2395
+ functionDeclaration: "function () { return this }",
2396
+ returnByValue: true
2397
+ });
2398
+ if (res && res.result && "value" in res.result) return res.result.value;
2399
+ } catch {
2309
2400
  }
2401
+ return remoteObjectToValue(ro);
2310
2402
  }
2311
- function fireReadyHandlers() {
2312
- for (const entry of [...readyHandlers]) {
2313
- if (readyHandlers.includes(entry)) invokeReadyHandler(entry.handler);
2403
+ async function handleConsoleApi(params) {
2404
+ if (disposed) return;
2405
+ if (isRenderForwardEvent(params, RENDER_FORWARD_SOURCE_URL)) return;
2406
+ const level = mapConsoleApiType(params.type);
2407
+ const rawArgs = Array.isArray(params.args) ? params.args : [];
2408
+ let args;
2409
+ try {
2410
+ args = await Promise.all(rawArgs.map((a) => resolveArg(a)));
2411
+ } catch {
2412
+ args = rawArgs.map((a) => remoteObjectToValue(a));
2314
2413
  }
2414
+ if (disposed) return;
2415
+ emit({ source: "service", level, args, ts: Date.now() });
2315
2416
  }
2316
- function handshake(wc) {
2317
- if (state === "disposed") return;
2318
- if (!opts.isCurrent(wc)) return;
2319
- dropActivePort(true, "awaitingHandshake");
2320
- const { port1, port2 } = new MessageChannelMain();
2321
- port1.on("message", (event) => {
2322
- dispatch(event.data);
2323
- });
2324
- port1.on("close", () => {
2325
- if (activePort === port1) dropActivePort(false, "absent");
2417
+ const onMessage = (_event, method, params) => {
2418
+ if (disposed) return;
2419
+ if (method !== "Runtime.consoleAPICalled") return;
2420
+ const p = params;
2421
+ tail = tail.then(() => handleConsoleApi(p)).catch(() => {
2326
2422
  });
2327
- wc.postMessage(ViewChannel.HostToolbarPort, null, [port2]);
2328
- port1.start();
2329
- activePort = port1;
2330
- activeWc = wc;
2331
- state = "ready";
2332
- generation++;
2333
- fireReadyHandlers();
2334
- }
2335
- return {
2336
- attach(wc) {
2337
- wc.on("did-finish-load", () => handshake(wc));
2338
- wc.on(
2339
- "did-start-navigation",
2340
- (details, _url, isInPlace, isMainFramePositional) => {
2341
- if (activeWc !== wc) return;
2342
- const isSameDocument = typeof details?.isSameDocument === "boolean" ? details.isSameDocument : isInPlace;
2343
- const isMainFrame = typeof details?.isMainFrame === "boolean" ? details.isMainFrame : isMainFramePositional;
2344
- if (isSameDocument || !isMainFrame) return;
2345
- dropActivePort(true, "awaitingHandshake");
2346
- }
2347
- );
2348
- wc.on("destroyed", () => {
2349
- if (activeWc === wc) dropActivePort(true, "absent");
2350
- });
2351
- },
2352
- invalidate() {
2353
- dropActivePort(true, "awaitingHandshake");
2354
- },
2355
- onMessage(channel, handler) {
2356
- if (typeof channel !== "string" || channel === "") {
2357
- throw new TypeError(
2358
- "hostToolbar.onMessage: channel must be a non-empty string"
2359
- );
2360
- }
2361
- const entry = { channel, handler };
2362
- handlers2.push(entry);
2363
- return {
2364
- dispose() {
2365
- const i = handlers2.indexOf(entry);
2366
- if (i >= 0) handlers2.splice(i, 1);
2367
- }
2368
- };
2369
- },
2370
- onReady(handler) {
2371
- if (state === "disposed") {
2372
- return { dispose() {
2373
- } };
2374
- }
2375
- const entry = { handler };
2376
- readyHandlers.push(entry);
2377
- if (state === "ready") {
2378
- const scheduledGeneration = generation;
2379
- queueMicrotask(() => {
2380
- if (state !== "ready" || generation !== scheduledGeneration || !activePort) return;
2381
- if (!readyHandlers.includes(entry)) return;
2382
- invokeReadyHandler(entry.handler);
2383
- });
2423
+ };
2424
+ function stop() {
2425
+ if (disposed) return;
2426
+ disposed = true;
2427
+ try {
2428
+ closedSub?.dispose();
2429
+ } catch {
2430
+ }
2431
+ try {
2432
+ serviceWc.debugger.removeListener("message", onMessage);
2433
+ } catch {
2434
+ }
2435
+ if (selfAttached) {
2436
+ try {
2437
+ if (!serviceWc.isDestroyed() && serviceWc.debugger.isAttached()) serviceWc.debugger.detach();
2438
+ } catch {
2384
2439
  }
2385
- return {
2386
- dispose() {
2387
- const i = readyHandlers.indexOf(entry);
2388
- if (i >= 0) readyHandlers.splice(i, 1);
2389
- }
2390
- };
2391
- },
2392
- send(channel, payload) {
2393
- if (!activePort) return false;
2394
- activePort.postMessage({ channel, payload });
2395
- return true;
2396
- },
2397
- dispose() {
2398
- dropActivePort(true, "absent");
2399
- state = "disposed";
2400
- handlers2.length = 0;
2401
- readyHandlers.length = 0;
2440
+ selfAttached = false;
2402
2441
  }
2403
- };
2404
- }
2405
-
2406
- // src/shared/simulator-route.ts
2407
- function decodePageSpec(value) {
2408
- const qIdx = value.indexOf("?");
2409
- if (qIdx < 0) return { pagePath: value, query: {} };
2410
- const pagePath = value.slice(0, qIdx);
2411
- const query = {};
2412
- for (const pair of value.slice(qIdx + 1).split("&")) {
2413
- if (!pair) continue;
2414
- const eqIdx = pair.indexOf("=");
2415
- const k = eqIdx >= 0 ? pair.slice(0, eqIdx) : pair;
2416
- const v = eqIdx >= 0 ? pair.slice(eqIdx + 1) : "";
2417
- if (k) query[decodeURIComponent(k)] = decodeURIComponent(v);
2418
2442
  }
2419
- return { pagePath, query };
2420
- }
2421
- function parseLocationRoute(search) {
2422
- const params = new URLSearchParams(search.startsWith("?") ? search.slice(1) : search);
2423
- const appId = params.get("appId");
2424
- const entryRaw = params.get("entry");
2425
- if (!appId || !entryRaw) return null;
2426
- const entry = decodePageSpec(entryRaw);
2427
- const pageRaw = params.get("page");
2428
- const current = pageRaw && pageRaw !== entryRaw ? decodePageSpec(pageRaw) : entry;
2429
- return { appId, entry, current };
2430
- }
2431
- function parseRoute(url) {
2432
- if (!url) return null;
2443
+ if (!usableDebugger()) {
2444
+ disposed = true;
2445
+ return { stop() {
2446
+ } };
2447
+ }
2433
2448
  try {
2434
- const u = new URL(url);
2435
- return parseLocationRoute(u.search);
2449
+ serviceWc.debugger.on("message", onMessage);
2436
2450
  } catch {
2437
- return null;
2451
+ stop();
2452
+ return { stop() {
2453
+ } };
2438
2454
  }
2439
- }
2440
-
2441
- // src/main/services/views/view-manager.ts
2442
- import { reconcile, createInitialState } from "@dimina-kit/electron-deck/layout";
2443
-
2444
- // src/main/services/views/apply-view-ops.ts
2445
- function applyViewOps(ops, target) {
2446
- for (const op of ops) {
2447
- switch (op.kind) {
2448
- case "detach":
2449
- target.detach(op.viewId);
2450
- break;
2451
- case "setVisible":
2452
- target.setVisible(op.viewId, op.visible);
2453
- break;
2454
- case "setBounds":
2455
- target.setBounds(op.viewId, op.bounds, op.extra);
2456
- break;
2457
- case "attach":
2458
- target.attach(op.viewId);
2459
- break;
2460
- case "reorder":
2461
- target.reorder(op.order);
2462
- break;
2463
- }
2455
+ serviceWc.debugger.sendCommand("Runtime.enable").catch(() => {
2456
+ });
2457
+ try {
2458
+ if (connections) closedSub = connections.acquire(serviceWc).on("closed", stop);
2459
+ else serviceWc.once("destroyed", stop);
2460
+ } catch {
2464
2461
  }
2462
+ return { stop };
2465
2463
  }
2466
2464
 
2467
- // src/shared/view-ids.ts
2468
- var VIEW_ID = {
2469
- simulator: "simulator",
2470
- simulatorDevtools: "simulator-devtools",
2471
- workbench: "workbench",
2472
- hostToolbar: "host-toolbar",
2473
- settings: "settings",
2474
- popover: "popover"
2475
- };
2476
- var VIEW_LAYER = {
2477
- base: 0,
2478
- hostToolbar: 5,
2479
- settings: 10,
2480
- popover: 20
2481
- };
2482
-
2483
- // src/main/services/views/view-manager.ts
2465
+ // src/main/services/views/resolve-project-editor-target.ts
2466
+ import fs4 from "node:fs";
2467
+ import path7 from "path";
2484
2468
  function resolveProjectEditorTarget(serviceHostUrl, activeProjectRoot, req, isFile = (absolutePath) => fs4.statSync(absolutePath).isFile()) {
2485
2469
  const sourceContext = projectSourceContextFromServiceHostUrl(
2486
2470
  serviceHostUrl,
@@ -2503,6 +2487,8 @@ function resolveProjectEditorTarget(serviceHostUrl, activeProjectRoot, req, isFi
2503
2487
  column: typeof req.column === "number" ? req.column + 1 : void 0
2504
2488
  };
2505
2489
  }
2490
+
2491
+ // src/main/services/views/native-simulator-devtools-host.ts
2506
2492
  var DEVTOOLS_HOST_EXEC_JS_MAX_LISTENERS = 50;
2507
2493
  function raiseExecuteJavaScriptListenerCeiling(wc) {
2508
2494
  try {
@@ -2512,279 +2498,440 @@ function raiseExecuteJavaScriptListenerCeiling(wc) {
2512
2498
  } catch {
2513
2499
  }
2514
2500
  }
2515
- function createViewManager(ctx) {
2516
- const safeArea = createSafeAreaController({ connections: ctx.connections });
2501
+ function createDevtoolsHost(ctx, reconciler, deps) {
2517
2502
  let simulatorView = null;
2518
2503
  let simulatorViewAdded = false;
2519
- let nativeSimulatorView = null;
2520
- let nativeSimulatorViewAdded = false;
2521
- let nativeSimulatorProjectPath = null;
2522
- let settleNativeSimulatorReady = null;
2523
- let nativeCustomApiBridgeHandler = null;
2524
- let currentZoomFactor = 1;
2525
- let settingsView = null;
2526
- let popoverView = null;
2527
- let simulatorWebContentsId = null;
2528
2504
  let nativeDevtoolsSourceWc = null;
2529
- let unsubscribeNativeRenderEvents = null;
2530
- let stopElementsForward = null;
2531
- let stopServiceConsole = null;
2532
- let nativeDevtoolsRetryTimer = null;
2533
- let nativeDevtoolsRetryToken = 0;
2534
- let workbenchView = null;
2535
- let workbenchThemeSyncBound = false;
2536
- let workbenchUrl = null;
2537
- let hostToolbarView = null;
2538
- let hostToolbarPreloadOverride = null;
2539
- let hostToolbarViewAdded = false;
2540
- let hostToolbarRuntimeAcquired = false;
2541
- let hostToolbarHeightMode = "auto";
2542
- let hostToolbarLastHeight = 0;
2543
- const hostToolbarPort = createHostToolbarPortChannel({
2544
- isCurrent: (wc) => liveHostToolbarWebContents() === wc
2545
- });
2546
- function destroyViewInternal(view) {
2547
- if (!view) return;
2548
- if (!ctx.windows.mainWindow.isDestroyed()) {
2505
+ let devtoolsHostUsed = false;
2506
+ let unsubscribeNativeRenderEvents = null;
2507
+ let stopElementsForward = null;
2508
+ let stopServiceConsole = null;
2509
+ let nativeDevtoolsRetryTimer = null;
2510
+ let nativeDevtoolsRetryToken = 0;
2511
+ const injectWhenReady = createLoadDeferredInjector();
2512
+ const openInEditorWiredWcIds = /* @__PURE__ */ new Set();
2513
+ function clearNativeDevtoolsRetry() {
2514
+ nativeDevtoolsRetryToken++;
2515
+ if (nativeDevtoolsRetryTimer) {
2516
+ clearTimeout(nativeDevtoolsRetryTimer);
2517
+ nativeDevtoolsRetryTimer = null;
2518
+ }
2519
+ }
2520
+ function closeNativeDevtoolsSource() {
2521
+ const source = nativeDevtoolsSourceWc;
2522
+ nativeDevtoolsSourceWc = null;
2523
+ try {
2524
+ stopServiceConsole?.();
2525
+ } catch {
2526
+ }
2527
+ stopServiceConsole = null;
2528
+ if (!source || source.isDestroyed()) return;
2529
+ try {
2530
+ if (source.isDevToolsOpened()) {
2531
+ source.closeDevTools();
2532
+ }
2533
+ } catch {
2534
+ }
2535
+ }
2536
+ function stopFollowingNativeServiceHost() {
2537
+ clearNativeDevtoolsRetry();
2538
+ if (unsubscribeNativeRenderEvents) {
2549
2539
  try {
2550
- ctx.windows.mainWindow.contentView.removeChildView(view);
2540
+ unsubscribeNativeRenderEvents();
2551
2541
  } catch {
2552
2542
  }
2543
+ unsubscribeNativeRenderEvents = null;
2553
2544
  }
2545
+ closeNativeDevtoolsSource();
2546
+ }
2547
+ function injectOpenResourceHandler(serviceWc, devtoolsWc) {
2548
+ if (serviceWc.isDestroyed() || devtoolsWc.isDestroyed()) return;
2554
2549
  try {
2555
- if (!view.webContents.isDestroyed()) {
2556
- view.webContents.close();
2557
- }
2550
+ const sourceContext = projectSourceContextFromServiceHostUrl(
2551
+ serviceWc.getURL(),
2552
+ ctx.workspace?.getProjectPath?.()
2553
+ );
2554
+ if (!sourceContext) return;
2555
+ devtoolsWc.executeJavaScript(buildDevtoolsProjectSourceLinksScript(sourceContext)).catch(() => {
2556
+ });
2558
2557
  } catch {
2559
2558
  }
2560
2559
  }
2561
- let placementState = createInitialState();
2562
- let epochCounter = 0;
2563
- let rendererGeneration = 0;
2564
- const baseDesired = /* @__PURE__ */ new Map();
2565
- const overlayDesired = /* @__PURE__ */ new Map();
2566
- function gateReadiness(v) {
2567
- if (v.viewId === VIEW_ID.simulator && !nativeSimulatorView)
2568
- return { ...v, placement: { visible: false } };
2569
- if (v.viewId === VIEW_ID.simulatorDevtools && !simulatorView)
2570
- return { ...v, placement: { visible: false } };
2571
- if (v.viewId === VIEW_ID.workbench && !workbenchView && !workbenchUrl)
2572
- return { ...v, placement: { visible: false } };
2573
- return v;
2574
- }
2575
- function resolveView(viewId) {
2576
- switch (viewId) {
2577
- case VIEW_ID.simulator:
2578
- return nativeSimulatorView;
2579
- case VIEW_ID.simulatorDevtools:
2580
- return simulatorView;
2581
- case VIEW_ID.workbench:
2582
- return workbenchView;
2583
- case VIEW_ID.hostToolbar:
2584
- return hostToolbarView;
2585
- case VIEW_ID.settings:
2586
- return settingsView;
2587
- case VIEW_ID.popover:
2588
- return popoverView;
2589
- default:
2590
- return null;
2560
+ function customizeDevtoolsTabs(devtoolsWc) {
2561
+ try {
2562
+ if (devtoolsWc.isDestroyed()) return;
2563
+ const inject = () => {
2564
+ if (devtoolsWc.isDestroyed()) return;
2565
+ try {
2566
+ void devtoolsWc.executeJavaScript(buildCustomizeTabsScript()).catch(() => {
2567
+ });
2568
+ } catch {
2569
+ }
2570
+ };
2571
+ injectWhenReady(devtoolsWc, "customize-tabs", inject);
2572
+ } catch {
2591
2573
  }
2592
2574
  }
2593
- function markAdded(viewId, added) {
2594
- switch (viewId) {
2595
- case VIEW_ID.simulator:
2596
- nativeSimulatorViewAdded = added;
2597
- break;
2598
- case VIEW_ID.simulatorDevtools:
2599
- simulatorViewAdded = added;
2600
- break;
2601
- case VIEW_ID.hostToolbar:
2602
- hostToolbarViewAdded = added;
2603
- break;
2575
+ function wireOpenInEditor(serviceWc, devtoolsWc) {
2576
+ if (!devtoolsWc.isDestroyed()) {
2577
+ injectWhenReady(devtoolsWc, "open-in-editor", () => injectOpenResourceHandler(serviceWc, devtoolsWc));
2604
2578
  }
2579
+ if (openInEditorWiredWcIds.has(serviceWc.id)) return;
2580
+ openInEditorWiredWcIds.add(serviceWc.id);
2581
+ const onOpenUrl = (_event, url) => {
2582
+ const req = decodeOpenInEditorUrl(url);
2583
+ if (!req) return;
2584
+ const target = resolveProjectEditorTarget(
2585
+ serviceWc.getURL(),
2586
+ ctx.workspace?.getProjectPath?.(),
2587
+ req
2588
+ );
2589
+ if (!target) return;
2590
+ deps.openFileInWorkbench(target.path, target.line ?? 1, target.column ?? 1);
2591
+ ctx.notify.editorOpenFile(target);
2592
+ };
2593
+ serviceWc.on("devtools-open-url", onOpenUrl);
2594
+ const conn = ctx.connections.acquire(serviceWc);
2595
+ conn.on("closed", () => {
2596
+ openInEditorWiredWcIds.delete(serviceWc.id);
2597
+ try {
2598
+ serviceWc.removeListener("devtools-open-url", onOpenUrl);
2599
+ } catch {
2600
+ }
2601
+ });
2605
2602
  }
2606
- function applyNativeSimulatorBounds(view, bounds, zoom) {
2607
- const p = computeNativeSimulatorViewParams(bounds, zoom);
2608
- currentZoomFactor = p.zoomFactor;
2609
- view.setBounds(p.bounds);
2610
- const simWc = view.webContents;
2611
- if (!simWc.isDestroyed()) simWc.setZoomFactor(p.zoomFactor);
2603
+ function pointNativeDevtoolsAtServiceWc(next) {
2604
+ if (!simulatorView || simulatorView.webContents.isDestroyed()) return true;
2605
+ if (nativeDevtoolsSourceWc?.id === next.id && !nativeDevtoolsSourceWc.isDestroyed()) {
2606
+ return true;
2607
+ }
2608
+ closeNativeDevtoolsSource();
2609
+ nativeDevtoolsSourceWc = next;
2610
+ if (devtoolsHostUsed) {
2611
+ rebuildDevtoolsHostView();
2612
+ }
2613
+ if (!simulatorView || simulatorView.webContents.isDestroyed()) return true;
2612
2614
  try {
2613
- for (const wc of webContents.getAllWebContents()) {
2614
- if (wc.isDestroyed()) continue;
2615
- if (wc.hostWebContents === simWc) wc.setZoomFactor(p.zoomFactor);
2615
+ next.setDevToolsWebContents(simulatorView.webContents);
2616
+ next.openDevTools({ mode: "detach", activate: false });
2617
+ devtoolsHostUsed = true;
2618
+ wireOpenInEditor(next, simulatorView.webContents);
2619
+ customizeDevtoolsTabs(simulatorView.webContents);
2620
+ try {
2621
+ stopServiceConsole?.();
2622
+ } catch {
2616
2623
  }
2617
- } catch {
2624
+ stopServiceConsole = installServiceConsoleForward({
2625
+ serviceWc: next,
2626
+ connections: ctx.connections,
2627
+ emit: (entry) => ctx.consoleForwarder?.emit(entry)
2628
+ }).stop;
2629
+ return true;
2630
+ } catch (err) {
2631
+ console.warn("[devtools-host] point-at-service-wc failed; retry will rebuild:", err);
2632
+ if (nativeDevtoolsSourceWc?.id === next.id) {
2633
+ nativeDevtoolsSourceWc = null;
2634
+ }
2635
+ return false;
2618
2636
  }
2619
2637
  }
2620
- const viewTarget = {
2621
- attach(viewId) {
2622
- if (ctx.windows.mainWindow.isDestroyed()) return;
2623
- if (viewId === VIEW_ID.workbench && !workbenchView && workbenchUrl) {
2624
- void attachWorkbench(workbenchUrl);
2625
- return;
2638
+ function pointNativeDevtoolsAtActiveServiceHost(appId) {
2639
+ if (!ctx.bridge?.isNativeHost()) return true;
2640
+ if (!simulatorView || simulatorView.webContents.isDestroyed()) return true;
2641
+ const wc = ctx.bridge.getServiceWc(appId);
2642
+ if (!wc || wc.isDestroyed()) return false;
2643
+ return pointNativeDevtoolsAtServiceWc(wc);
2644
+ }
2645
+ function scheduleNativeDevtoolsFollow(appId, attempt = 0) {
2646
+ if (attempt >= 20) return;
2647
+ if (!ctx.bridge?.isNativeHost()) return;
2648
+ const token = nativeDevtoolsRetryToken;
2649
+ if (nativeDevtoolsRetryTimer) clearTimeout(nativeDevtoolsRetryTimer);
2650
+ nativeDevtoolsRetryTimer = setTimeout(() => {
2651
+ nativeDevtoolsRetryTimer = null;
2652
+ if (token !== nativeDevtoolsRetryToken) return;
2653
+ if (pointNativeDevtoolsAtActiveServiceHost(appId)) return;
2654
+ scheduleNativeDevtoolsFollow(appId, attempt + 1);
2655
+ }, 50);
2656
+ }
2657
+ function followNativeDevtoolsServiceHost(appId) {
2658
+ clearNativeDevtoolsRetry();
2659
+ if (pointNativeDevtoolsAtActiveServiceHost(appId)) return;
2660
+ scheduleNativeDevtoolsFollow(appId);
2661
+ }
2662
+ function onNativeRenderEvent(event) {
2663
+ if (event.kind !== "activePage" && event.kind !== "domReady") return;
2664
+ followNativeDevtoolsServiceHost(event.appId);
2665
+ }
2666
+ function rebuildDevtoolsHostView() {
2667
+ if (simulatorView) {
2668
+ removeSimulatorDevtoolsView();
2669
+ try {
2670
+ if (!simulatorView.webContents.isDestroyed()) {
2671
+ simulatorView.webContents.close();
2672
+ }
2673
+ } catch {
2626
2674
  }
2627
- if (viewId === VIEW_ID.hostToolbar) {
2628
- const view2 = ensureHostToolbarView();
2629
- ctx.windows.mainWindow.contentView.addChildView(view2);
2630
- hostToolbarViewAdded = true;
2631
- return;
2675
+ simulatorView = null;
2676
+ }
2677
+ simulatorView = new WebContentsView3();
2678
+ const devtoolsWc = simulatorView.webContents;
2679
+ raiseExecuteJavaScriptListenerCeiling(devtoolsWc);
2680
+ ctx.networkForward?.setDevtoolsHost(devtoolsWc);
2681
+ if (ctx.bridge) {
2682
+ try {
2683
+ stopElementsForward?.();
2684
+ } catch {
2632
2685
  }
2633
- const view = resolveView(viewId);
2634
- if (!view) return;
2635
- ctx.windows.mainWindow.contentView.addChildView(view);
2636
- markAdded(viewId, true);
2637
- },
2638
- detach(viewId) {
2639
- const view = resolveView(viewId);
2640
- if (view && !ctx.windows.mainWindow.isDestroyed()) {
2686
+ const thisForward = installElementsForward({ devtoolsWc, bridge: ctx.bridge, connections: ctx.connections });
2687
+ stopElementsForward = thisForward;
2688
+ devtoolsWc.once("destroyed", () => {
2641
2689
  try {
2642
- ctx.windows.mainWindow.contentView.removeChildView(view);
2690
+ thisForward();
2643
2691
  } catch {
2644
2692
  }
2645
- }
2646
- markAdded(viewId, false);
2647
- },
2648
- setBounds(viewId, bounds, extra) {
2649
- const view = resolveView(viewId);
2650
- if (!view || view.webContents.isDestroyed()) return;
2651
- if (viewId === VIEW_ID.simulator) {
2652
- applyNativeSimulatorBounds(view, bounds, extra?.zoom ?? 1);
2653
- return;
2654
- }
2655
- view.setBounds(bounds);
2656
- },
2657
- setVisible(viewId, visible) {
2658
- const view = resolveView(viewId);
2659
- if (!view) return;
2693
+ if (stopElementsForward === thisForward) stopElementsForward = null;
2694
+ });
2695
+ }
2696
+ injectWhenReady(devtoolsWc, "console-default", () => {
2697
+ devtoolsWc.executeJavaScript(`
2698
+ (function() {
2699
+ try { localStorage.setItem('panel-selectedTab', '"console"') } catch {}
2700
+ let tries = 0
2701
+ const timer = setInterval(() => {
2702
+ tries++
2703
+ try {
2704
+ const UI = globalThis.UI
2705
+ const vm = UI && UI.ViewManager && typeof UI.ViewManager.instance === 'function'
2706
+ ? UI.ViewManager.instance()
2707
+ : null
2708
+ if (vm && typeof vm.showView === 'function') {
2709
+ vm.showView('console')
2710
+ clearInterval(timer)
2711
+ return
2712
+ }
2713
+ } catch {}
2714
+ if (tries > 80) clearInterval(timer)
2715
+ }, 50)
2716
+ })()
2717
+ `).catch(() => {
2718
+ });
2719
+ });
2720
+ reconciler.reconcileNow();
2721
+ devtoolsHostUsed = false;
2722
+ }
2723
+ function attachNativeSimulatorDevtoolsHost() {
2724
+ stopFollowingNativeServiceHost();
2725
+ rebuildDevtoolsHostView();
2726
+ if (ctx.bridge?.isNativeHost()) {
2727
+ unsubscribeNativeRenderEvents = ctx.bridge.onRenderEvent(onNativeRenderEvent);
2728
+ followNativeDevtoolsServiceHost();
2729
+ }
2730
+ }
2731
+ function removeSimulatorDevtoolsView() {
2732
+ if (simulatorView && simulatorViewAdded) {
2660
2733
  try {
2661
- view.setVisible(visible);
2662
- } catch {
2734
+ ctx.windows.mainWindow.contentView.removeChildView(simulatorView);
2735
+ } catch (e) {
2736
+ console.error("[workbench] removeSimulatorDevtoolsView error", e);
2663
2737
  }
2664
- },
2665
- reorder(order) {
2666
- if (order.length <= 1 || ctx.windows.mainWindow.isDestroyed()) return;
2667
- const cv = ctx.windows.mainWindow.contentView;
2668
- for (const viewId of order) {
2669
- const view = resolveView(viewId);
2670
- if (view) {
2671
- try {
2672
- cv.addChildView(view);
2673
- } catch {
2674
- }
2675
- }
2738
+ simulatorViewAdded = false;
2739
+ }
2740
+ reconciler.forgetActual(VIEW_ID.simulatorDevtools);
2741
+ }
2742
+ reconciler.registerView(VIEW_ID.simulatorDevtools, {
2743
+ getView: () => simulatorView,
2744
+ setAdded: (added) => {
2745
+ simulatorViewAdded = added;
2746
+ },
2747
+ gateHidden: () => !simulatorView
2748
+ });
2749
+ return {
2750
+ attach: attachNativeSimulatorDevtoolsHost,
2751
+ followServiceHost: followNativeDevtoolsServiceHost,
2752
+ stopFollowing: stopFollowingNativeServiceHost,
2753
+ stopElementsForwarding: () => {
2754
+ try {
2755
+ stopElementsForward?.();
2756
+ } catch {
2676
2757
  }
2758
+ stopElementsForward = null;
2759
+ },
2760
+ destroyHostView: () => {
2761
+ destroyChildView(ctx.windows.mainWindow, simulatorView);
2762
+ simulatorView = null;
2763
+ simulatorViewAdded = false;
2677
2764
  }
2678
2765
  };
2679
- function ensureLazyViews() {
2680
- const ht = baseDesired.get(VIEW_ID.hostToolbar);
2681
- if (ht?.placement.visible && !liveHostToolbarWebContents()) ensureHostToolbarView();
2682
- const wb = baseDesired.get(VIEW_ID.workbench);
2683
- if (wb?.placement.visible && !workbenchView && workbenchUrl) void attachWorkbench(workbenchUrl);
2684
- }
2685
- function reconcileNow() {
2686
- ensureLazyViews();
2687
- const views = [];
2688
- for (const v of baseDesired.values()) views.push(gateReadiness(v));
2689
- for (const v of overlayDesired.values()) views.push(v);
2690
- const result = reconcile(placementState, {
2691
- generation: rendererGeneration,
2692
- epoch: ++epochCounter,
2693
- views
2766
+ }
2767
+
2768
+ // src/main/services/views/host-toolbar-view.ts
2769
+ import { WebContentsView as WebContentsView4 } from "electron";
2770
+
2771
+ // src/main/services/views/host-toolbar-session-runtime.ts
2772
+ import { session } from "electron";
2773
+ var refCount = 0;
2774
+ var registrationId = null;
2775
+ function acquireHostToolbarSessionRuntime() {
2776
+ if (refCount === 0) {
2777
+ registrationId = session.defaultSession.registerPreloadScript({
2778
+ type: "frame",
2779
+ filePath: hostToolbarRuntimePreloadPath
2694
2780
  });
2695
- placementState = result.state;
2696
- applyViewOps(result.ops, viewTarget);
2697
- }
2698
- function setPlacementSnapshot(snapshot) {
2699
- rendererGeneration = snapshot.generation;
2700
- baseDesired.clear();
2701
- for (const v of snapshot.views) baseDesired.set(v.viewId, v);
2702
- reconcileNow();
2703
2781
  }
2704
- function workbenchThemeScheme() {
2705
- return nativeTheme3.shouldUseDarkColors ? "dark" : "light";
2782
+ refCount++;
2783
+ }
2784
+ function releaseHostToolbarSessionRuntime() {
2785
+ if (refCount === 0) return;
2786
+ refCount--;
2787
+ if (refCount === 0 && registrationId !== null) {
2788
+ session.defaultSession.unregisterPreloadScript(registrationId);
2789
+ registrationId = null;
2706
2790
  }
2707
- function pushWorkbenchTheme() {
2708
- if (!workbenchView || workbenchView.webContents.isDestroyed()) return;
2709
- const wc = workbenchView.webContents;
2710
- if (typeof wc.executeJavaScript !== "function") return;
2711
- const script = `window.__WB_SET_THEME && window.__WB_SET_THEME(${JSON.stringify(workbenchThemeScheme())})`;
2712
- wc.executeJavaScript(script, true).catch(() => {
2713
- });
2791
+ }
2792
+
2793
+ // src/main/services/views/host-toolbar-port-channel.ts
2794
+ import { MessageChannelMain } from "electron";
2795
+ function createHostToolbarPortChannel(opts) {
2796
+ let activePort = null;
2797
+ let activeWc = null;
2798
+ let state = "absent";
2799
+ let generation = 0;
2800
+ const handlers2 = [];
2801
+ const readyHandlers = [];
2802
+ function dispatch(data) {
2803
+ if (typeof data !== "object" || data === null) return;
2804
+ const { channel, payload } = data;
2805
+ if (typeof channel !== "string") return;
2806
+ for (const entry of [...handlers2]) {
2807
+ if (entry.channel === channel) entry.handler(payload);
2808
+ }
2714
2809
  }
2715
- async function attachWorkbench(url) {
2716
- if (workbenchView) return;
2717
- const view = new WebContentsView2({
2718
- webPreferences: {
2719
- nodeIntegration: false,
2720
- contextIsolation: true,
2721
- sandbox: false
2810
+ function dropActivePort(close, next) {
2811
+ const port = activePort;
2812
+ activePort = null;
2813
+ activeWc = null;
2814
+ if (state !== "disposed") state = next;
2815
+ if (close && port) {
2816
+ try {
2817
+ port.close();
2818
+ } catch {
2722
2819
  }
2723
- });
2724
- workbenchView = view;
2725
- if (!workbenchThemeSyncBound) {
2726
- nativeTheme3.on("updated", pushWorkbenchTheme);
2727
- workbenchThemeSyncBound = true;
2728
2820
  }
2821
+ }
2822
+ function invokeReadyHandler(handler) {
2729
2823
  try {
2730
- view.webContents.setWindowOpenHandler(({ url: target }) => handleWindowOpenExternal(target));
2731
- } catch {
2824
+ handler();
2825
+ } catch (err) {
2826
+ console.error("[host-toolbar] onReady handler threw:", err);
2732
2827
  }
2733
- ctx.windows.mainWindow.contentView.addChildView(view);
2734
- const loadUrl = `${url}index.html?theme=${workbenchThemeScheme()}`;
2735
- await view.webContents.loadURL(loadUrl).catch((err) => {
2736
- console.error("[workbench] attachWorkbench \u2014 loadURL failed", err);
2737
- });
2738
- }
2739
- function setWorkbenchSource(url) {
2740
- workbenchUrl = url;
2741
2828
  }
2742
- function detachWorkbench() {
2743
- if (workbenchThemeSyncBound) {
2744
- nativeTheme3.removeListener("updated", pushWorkbenchTheme);
2745
- workbenchThemeSyncBound = false;
2829
+ function fireReadyHandlers() {
2830
+ for (const entry of [...readyHandlers]) {
2831
+ if (readyHandlers.includes(entry)) invokeReadyHandler(entry.handler);
2746
2832
  }
2747
- destroyViewInternal(workbenchView);
2748
- workbenchView = null;
2749
- baseDesired.delete(VIEW_ID.workbench);
2750
- reconcileNow();
2751
- }
2752
- function workspaceUriFor(rel) {
2753
- const encoded = rel.split("/").map(encodeURIComponent).join("/");
2754
- return `file:///workspace/${encoded}`;
2755
2833
  }
2756
- function tryRevealInWorkbench(uri, zeroLine, zeroCol) {
2757
- if (!workbenchView || workbenchView.webContents.isDestroyed()) {
2758
- return Promise.resolve(false);
2759
- }
2760
- const script = `(async () => {
2761
- try {
2762
- const P = window.__WB_PROBE; if (!P) return false
2763
- const vscode = P.vscode
2764
- const uri = vscode.Uri.parse(${JSON.stringify(uri)})
2765
- const doc = await vscode.workspace.openTextDocument(uri)
2766
- const pos = new vscode.Position(${zeroLine}, ${zeroCol})
2767
- await vscode.window.showTextDocument(doc, { selection: new vscode.Range(pos, pos) })
2768
- return true
2769
- } catch (e) { return false }
2770
- })()`;
2771
- return workbenchView.webContents.executeJavaScript(script, true).then((ok) => ok === true).catch(() => false);
2834
+ function handshake(wc) {
2835
+ if (state === "disposed") return;
2836
+ if (!opts.isCurrent(wc)) return;
2837
+ dropActivePort(true, "awaitingHandshake");
2838
+ const { port1, port2 } = new MessageChannelMain();
2839
+ port1.on("message", (event) => {
2840
+ dispatch(event.data);
2841
+ });
2842
+ port1.on("close", () => {
2843
+ if (activePort === port1) dropActivePort(false, "absent");
2844
+ });
2845
+ wc.postMessage(ViewChannel.HostToolbarPort, null, [port2]);
2846
+ port1.start();
2847
+ activePort = port1;
2848
+ activeWc = wc;
2849
+ state = "ready";
2850
+ generation++;
2851
+ fireReadyHandlers();
2772
2852
  }
2773
- function openFileInWorkbench(relPath, line, column) {
2774
- if (!workbenchView || workbenchView.webContents.isDestroyed()) return false;
2775
- const uri = workspaceUriFor(relPath.replace(/^\/+/, ""));
2776
- const zeroLine = Math.max(0, Math.floor(line) - 1);
2777
- const zeroCol = Math.max(0, Math.floor(column) - 1);
2778
- void (async () => {
2779
- for (let attempt = 0; attempt < 10; attempt++) {
2780
- if (!workbenchView || workbenchView.webContents.isDestroyed()) return;
2781
- if (await tryRevealInWorkbench(uri, zeroLine, zeroCol)) return;
2782
- await new Promise((resolve) => setTimeout(resolve, 150));
2853
+ return {
2854
+ attach(wc) {
2855
+ wc.on("did-finish-load", () => handshake(wc));
2856
+ wc.on(
2857
+ "did-start-navigation",
2858
+ (details, _url, isInPlace, isMainFramePositional) => {
2859
+ if (activeWc !== wc) return;
2860
+ const isSameDocument = typeof details?.isSameDocument === "boolean" ? details.isSameDocument : isInPlace;
2861
+ const isMainFrame = typeof details?.isMainFrame === "boolean" ? details.isMainFrame : isMainFramePositional;
2862
+ if (isSameDocument || !isMainFrame) return;
2863
+ dropActivePort(true, "awaitingHandshake");
2864
+ }
2865
+ );
2866
+ wc.on("destroyed", () => {
2867
+ if (activeWc === wc) dropActivePort(true, "absent");
2868
+ });
2869
+ },
2870
+ invalidate() {
2871
+ dropActivePort(true, "awaitingHandshake");
2872
+ },
2873
+ onMessage(channel, handler) {
2874
+ if (typeof channel !== "string" || channel === "") {
2875
+ throw new TypeError(
2876
+ "hostToolbar.onMessage: channel must be a non-empty string"
2877
+ );
2783
2878
  }
2784
- console.error("[workbench] openFileInWorkbench: workbench never became ready for", uri);
2785
- })();
2786
- return true;
2787
- }
2879
+ const entry = { channel, handler };
2880
+ handlers2.push(entry);
2881
+ return {
2882
+ dispose() {
2883
+ const i = handlers2.indexOf(entry);
2884
+ if (i >= 0) handlers2.splice(i, 1);
2885
+ }
2886
+ };
2887
+ },
2888
+ onReady(handler) {
2889
+ if (state === "disposed") {
2890
+ return { dispose() {
2891
+ } };
2892
+ }
2893
+ const entry = { handler };
2894
+ readyHandlers.push(entry);
2895
+ if (state === "ready") {
2896
+ const scheduledGeneration = generation;
2897
+ queueMicrotask(() => {
2898
+ if (state !== "ready" || generation !== scheduledGeneration || !activePort) return;
2899
+ if (!readyHandlers.includes(entry)) return;
2900
+ invokeReadyHandler(entry.handler);
2901
+ });
2902
+ }
2903
+ return {
2904
+ dispose() {
2905
+ const i = readyHandlers.indexOf(entry);
2906
+ if (i >= 0) readyHandlers.splice(i, 1);
2907
+ }
2908
+ };
2909
+ },
2910
+ send(channel, payload) {
2911
+ if (!activePort) return false;
2912
+ activePort.postMessage({ channel, payload });
2913
+ return true;
2914
+ },
2915
+ dispose() {
2916
+ dropActivePort(true, "absent");
2917
+ state = "disposed";
2918
+ handlers2.length = 0;
2919
+ readyHandlers.length = 0;
2920
+ }
2921
+ };
2922
+ }
2923
+
2924
+ // src/main/services/views/host-toolbar-view.ts
2925
+ function createHostToolbarView(ctx, reconciler, deps) {
2926
+ let hostToolbarView = null;
2927
+ let hostToolbarPreloadOverride = null;
2928
+ let hostToolbarViewAdded = false;
2929
+ let hostToolbarRuntimeAcquired = false;
2930
+ let hostToolbarHeightMode = "auto";
2931
+ let hostToolbarLastHeight = 0;
2932
+ const hostToolbarPort = createHostToolbarPortChannel({
2933
+ isCurrent: (wc) => liveHostToolbarWebContents() === wc
2934
+ });
2788
2935
  function liveHostToolbarWebContents() {
2789
2936
  const wc = hostToolbarView?.webContents;
2790
2937
  if (!wc || wc.isDestroyed()) return null;
@@ -2801,7 +2948,7 @@ function createViewManager(ctx) {
2801
2948
  }
2802
2949
  hostToolbarViewAdded = false;
2803
2950
  }
2804
- placementState.actual.delete(VIEW_ID.hostToolbar);
2951
+ reconciler.forgetActual(VIEW_ID.hostToolbar);
2805
2952
  if (!hostToolbarRuntimeAcquired) {
2806
2953
  acquireHostToolbarSessionRuntime();
2807
2954
  hostToolbarRuntimeAcquired = true;
@@ -2815,7 +2962,7 @@ function createViewManager(ctx) {
2815
2962
  if (hostToolbarPreloadOverride !== null) {
2816
2963
  webPreferences.preload = hostToolbarPreloadOverride;
2817
2964
  }
2818
- const view = new WebContentsView2({ webPreferences });
2965
+ const view = new WebContentsView4({ webPreferences });
2819
2966
  hostToolbarView = view;
2820
2967
  hostToolbarPort.attach(view.webContents);
2821
2968
  try {
@@ -2831,23 +2978,22 @@ function createViewManager(ctx) {
2831
2978
  function notifyHostToolbarHeight(height) {
2832
2979
  hostToolbarLastHeight = height;
2833
2980
  ctx.notify.hostToolbarHeightChanged(height);
2834
- if (overlayDesired.has(VIEW_ID.settings)) applySettingsBounds();
2835
- if (overlayDesired.has(VIEW_ID.popover)) applyPopoverBounds();
2981
+ deps.reapplyPresentOverlays();
2836
2982
  }
2837
2983
  function setHostToolbarHeight(extent) {
2838
2984
  if (hostToolbarHeightMode !== "auto") return;
2839
2985
  notifyHostToolbarHeight(extent);
2840
2986
  }
2841
2987
  function hideHostToolbar() {
2842
- baseDesired.set(VIEW_ID.hostToolbar, {
2988
+ reconciler.setBaseDesired(VIEW_ID.hostToolbar, {
2843
2989
  viewId: VIEW_ID.hostToolbar,
2844
2990
  placement: { visible: false },
2845
2991
  layer: VIEW_LAYER.hostToolbar
2846
2992
  });
2847
- reconcileNow();
2993
+ reconciler.reconcileNow();
2848
2994
  notifyHostToolbarHeight(0);
2849
2995
  }
2850
- const hostToolbar = {
2996
+ const control = {
2851
2997
  async loadURL(url) {
2852
2998
  const view = ensureHostToolbarView();
2853
2999
  hostToolbarPort.invalidate();
@@ -2864,8 +3010,8 @@ function createViewManager(ctx) {
2864
3010
  hide() {
2865
3011
  hideHostToolbar();
2866
3012
  },
2867
- setPreloadPath(path23) {
2868
- hostToolbarPreloadOverride = path23;
3013
+ setPreloadPath(path24) {
3014
+ hostToolbarPreloadOverride = path24;
2869
3015
  },
2870
3016
  setHeightMode(mode) {
2871
3017
  if (mode !== "auto" && !(Number.isFinite(mode.fixed) && mode.fixed >= 0)) {
@@ -2888,13 +3034,80 @@ function createViewManager(ctx) {
2888
3034
  return hostToolbarPort.send(channel, payload);
2889
3035
  }
2890
3036
  };
3037
+ reconciler.registerView(VIEW_ID.hostToolbar, {
3038
+ getView: () => hostToolbarView,
3039
+ ensureView: () => ensureHostToolbarView(),
3040
+ setAdded: (added) => {
3041
+ hostToolbarViewAdded = added;
3042
+ },
3043
+ ensureLazy: (desired) => {
3044
+ if (desired?.placement.visible && !liveHostToolbarWebContents()) ensureHostToolbarView();
3045
+ }
3046
+ });
3047
+ function dispose() {
3048
+ hostToolbarPort.dispose();
3049
+ destroyChildView(ctx.windows.mainWindow, hostToolbarView);
3050
+ hostToolbarView = null;
3051
+ hostToolbarViewAdded = false;
3052
+ if (hostToolbarRuntimeAcquired) {
3053
+ releaseHostToolbarSessionRuntime();
3054
+ hostToolbarRuntimeAcquired = false;
3055
+ }
3056
+ }
3057
+ return {
3058
+ control,
3059
+ setHostToolbarHeight,
3060
+ getHostToolbarHeight: () => hostToolbarLastHeight,
3061
+ getHostToolbarWebContentsId: () => liveHostToolbarWebContents()?.id ?? null,
3062
+ dispose
3063
+ };
3064
+ }
3065
+
3066
+ // src/main/services/views/overlay-panels-view.ts
3067
+ import { WebContentsView as WebContentsView5 } from "electron";
3068
+ import path8 from "path";
3069
+
3070
+ // src/main/services/layout/index.ts
3071
+ function computeNativeSimulatorViewParams(rect, zoomPercent) {
3072
+ const zoomFactor = zoomPercent / 100;
3073
+ return {
3074
+ bounds: {
3075
+ x: Math.round(rect.x),
3076
+ y: Math.round(rect.y),
3077
+ width: Math.max(0, Math.round(rect.width)),
3078
+ height: Math.max(0, Math.round(rect.height))
3079
+ },
3080
+ zoomFactor
3081
+ };
3082
+ }
3083
+ function computeSettingsBounds(contentWidth, contentHeight, headerHeight) {
3084
+ return {
3085
+ x: 0,
3086
+ y: headerHeight,
3087
+ width: Math.max(1, contentWidth),
3088
+ height: Math.max(1, contentHeight - headerHeight)
3089
+ };
3090
+ }
3091
+ function computePopoverBounds(contentWidth, contentHeight, headerHeight) {
3092
+ return {
3093
+ x: 0,
3094
+ y: headerHeight,
3095
+ width: Math.max(1, contentWidth),
3096
+ height: contentHeight - headerHeight
3097
+ };
3098
+ }
3099
+
3100
+ // src/main/services/views/overlay-panels-view.ts
3101
+ function createOverlayPanelsView(ctx, reconciler, deps) {
3102
+ let settingsView = null;
3103
+ let popoverView = null;
2891
3104
  function overlayHeaderHeight() {
2892
- return HEADER_H + hostToolbarLastHeight;
3105
+ return HEADER_H + deps.getHostToolbarHeight();
2893
3106
  }
2894
3107
  function applySettingsBounds() {
2895
3108
  if (!settingsView || ctx.windows.mainWindow.isDestroyed()) return;
2896
3109
  const [w = 0, h = 0] = ctx.windows.mainWindow.getContentSize();
2897
- overlayDesired.set(VIEW_ID.settings, {
3110
+ reconciler.setOverlayDesired(VIEW_ID.settings, {
2898
3111
  viewId: VIEW_ID.settings,
2899
3112
  placement: {
2900
3113
  visible: true,
@@ -2902,12 +3115,12 @@ function createViewManager(ctx) {
2902
3115
  },
2903
3116
  layer: VIEW_LAYER.settings
2904
3117
  });
2905
- reconcileNow();
3118
+ reconciler.reconcileNow();
2906
3119
  }
2907
3120
  function applyPopoverBounds() {
2908
3121
  if (!popoverView || ctx.windows.mainWindow.isDestroyed()) return;
2909
3122
  const [w = 0, h = 0] = ctx.windows.mainWindow.getContentSize();
2910
- overlayDesired.set(VIEW_ID.popover, {
3123
+ reconciler.setOverlayDesired(VIEW_ID.popover, {
2911
3124
  viewId: VIEW_ID.popover,
2912
3125
  placement: {
2913
3126
  visible: true,
@@ -2915,229 +3128,323 @@ function createViewManager(ctx) {
2915
3128
  },
2916
3129
  layer: VIEW_LAYER.popover
2917
3130
  });
2918
- reconcileNow();
3131
+ reconciler.reconcileNow();
2919
3132
  }
2920
- function clearNativeDevtoolsRetry() {
2921
- nativeDevtoolsRetryToken++;
2922
- if (nativeDevtoolsRetryTimer) {
2923
- clearTimeout(nativeDevtoolsRetryTimer);
2924
- nativeDevtoolsRetryTimer = null;
3133
+ async function showSettings() {
3134
+ if (!settingsView) {
3135
+ settingsView = new WebContentsView5({
3136
+ webPreferences: {
3137
+ nodeIntegration: false,
3138
+ contextIsolation: true,
3139
+ sandbox: false,
3140
+ preload: mainPreloadPath
3141
+ }
3142
+ });
3143
+ applyNavigationHardening(settingsView.webContents, ctx.rendererDir);
3144
+ settingsView.setBackgroundColor("#00000000");
3145
+ await settingsView.webContents.loadFile(
3146
+ path8.join(ctx.rendererDir, "entries/settings/index.html")
3147
+ );
2925
3148
  }
3149
+ applySettingsBounds();
2926
3150
  }
2927
- function closeNativeDevtoolsSource() {
2928
- const source = nativeDevtoolsSourceWc;
2929
- nativeDevtoolsSourceWc = null;
2930
- try {
2931
- stopServiceConsole?.();
2932
- } catch {
2933
- }
2934
- stopServiceConsole = null;
2935
- if (!source || source.isDestroyed()) return;
2936
- try {
2937
- if (source.isDevToolsOpened()) {
2938
- source.closeDevTools();
2939
- }
2940
- } catch {
2941
- }
3151
+ function hideSettings() {
3152
+ reconciler.deleteOverlayDesired(VIEW_ID.settings);
3153
+ reconciler.reconcileNow();
2942
3154
  }
2943
- function stopFollowingNativeServiceHost() {
2944
- clearNativeDevtoolsRetry();
2945
- if (unsubscribeNativeRenderEvents) {
2946
- try {
2947
- unsubscribeNativeRenderEvents();
2948
- } catch {
3155
+ function showPopover(data) {
3156
+ hidePopover();
3157
+ const popover = new WebContentsView5({
3158
+ webPreferences: {
3159
+ nodeIntegration: false,
3160
+ contextIsolation: true,
3161
+ sandbox: false,
3162
+ preload: mainPreloadPath
2949
3163
  }
2950
- unsubscribeNativeRenderEvents = null;
2951
- }
2952
- closeNativeDevtoolsSource();
3164
+ });
3165
+ applyNavigationHardening(popover.webContents, ctx.rendererDir);
3166
+ popoverView = popover;
3167
+ popover.setBackgroundColor("#00000000");
3168
+ applyPopoverBounds();
3169
+ popover.webContents.once("did-finish-load", () => {
3170
+ ctx.notify.popoverInit(popover, data);
3171
+ });
3172
+ popover.webContents.loadFile(
3173
+ path8.join(ctx.rendererDir, "entries/popover/index.html")
3174
+ );
2953
3175
  }
2954
- const openInEditorWiredWcIds = /* @__PURE__ */ new Set();
2955
- function injectOpenResourceHandler(serviceWc, devtoolsWc) {
2956
- if (serviceWc.isDestroyed() || devtoolsWc.isDestroyed()) return;
2957
- try {
2958
- const sourceContext = projectSourceContextFromServiceHostUrl(
2959
- serviceWc.getURL(),
2960
- ctx.workspace?.getProjectPath?.()
2961
- );
2962
- if (!sourceContext) return;
2963
- devtoolsWc.executeJavaScript(buildDevtoolsProjectSourceLinksScript(sourceContext)).catch(() => {
2964
- });
2965
- } catch {
2966
- }
3176
+ function hidePopover() {
3177
+ if (!popoverView) return;
3178
+ reconciler.deleteOverlayDesired(VIEW_ID.popover);
3179
+ destroyChildView(ctx.windows.mainWindow, popoverView);
3180
+ popoverView = null;
3181
+ reconciler.reconcileNow();
3182
+ ctx.notify.popoverClosed();
2967
3183
  }
2968
- function customizeDevtoolsTabs(devtoolsWc) {
2969
- try {
2970
- if (devtoolsWc.isDestroyed()) return;
2971
- const inject = () => {
2972
- if (devtoolsWc.isDestroyed()) return;
2973
- try {
2974
- void devtoolsWc.executeJavaScript(buildCustomizeTabsScript()).catch(() => {
2975
- });
2976
- } catch {
2977
- }
2978
- };
2979
- if (devtoolsWc.isLoading()) {
2980
- devtoolsWc.once("dom-ready", inject);
2981
- } else {
2982
- inject();
2983
- }
2984
- } catch {
2985
- }
3184
+ function reapplyPresentOverlays() {
3185
+ if (reconciler.hasOverlayDesired(VIEW_ID.settings)) applySettingsBounds();
3186
+ if (reconciler.hasOverlayDesired(VIEW_ID.popover)) applyPopoverBounds();
2986
3187
  }
2987
- function wireOpenInEditor(serviceWc, devtoolsWc) {
2988
- if (!devtoolsWc.isDestroyed()) {
2989
- if (devtoolsWc.isLoading()) {
2990
- devtoolsWc.once("dom-ready", () => injectOpenResourceHandler(serviceWc, devtoolsWc));
2991
- } else {
2992
- injectOpenResourceHandler(serviceWc, devtoolsWc);
2993
- }
3188
+ function applySettingsBoundsIfPresent() {
3189
+ if (reconciler.hasOverlayDesired(VIEW_ID.settings)) applySettingsBounds();
3190
+ }
3191
+ function destroySettings() {
3192
+ destroyChildView(ctx.windows.mainWindow, settingsView);
3193
+ settingsView = null;
3194
+ }
3195
+ reconciler.registerView(VIEW_ID.settings, { getView: () => settingsView });
3196
+ reconciler.registerView(VIEW_ID.popover, { getView: () => popoverView });
3197
+ return {
3198
+ showSettings,
3199
+ hideSettings,
3200
+ showPopover,
3201
+ hidePopover,
3202
+ reapplyPresentOverlays,
3203
+ applySettingsBoundsIfPresent,
3204
+ destroySettings,
3205
+ getSettingsWebContents: () => {
3206
+ if (!settingsView) return null;
3207
+ if (settingsView.webContents.isDestroyed()) return null;
3208
+ return settingsView.webContents;
3209
+ },
3210
+ getSettingsWebContentsId: () => {
3211
+ if (!settingsView) return null;
3212
+ if (settingsView.webContents.isDestroyed()) return null;
3213
+ return settingsView.webContents.id;
3214
+ },
3215
+ getPopoverWebContentsId: () => {
3216
+ if (!popoverView) return null;
3217
+ if (popoverView.webContents.isDestroyed()) return null;
3218
+ return popoverView.webContents.id;
2994
3219
  }
2995
- if (openInEditorWiredWcIds.has(serviceWc.id)) return;
2996
- openInEditorWiredWcIds.add(serviceWc.id);
2997
- const onOpenUrl = (_event, url) => {
2998
- const req = decodeOpenInEditorUrl(url);
2999
- if (!req) return;
3000
- const target = resolveProjectEditorTarget(
3001
- serviceWc.getURL(),
3002
- ctx.workspace?.getProjectPath?.(),
3003
- req
3004
- );
3005
- if (!target) return;
3006
- openFileInWorkbench(target.path, target.line ?? 1, target.column ?? 1);
3007
- ctx.notify.editorOpenFile(target);
3008
- };
3009
- serviceWc.on("devtools-open-url", onOpenUrl);
3010
- const conn = ctx.connections.acquire(serviceWc);
3011
- conn.on("closed", () => {
3012
- openInEditorWiredWcIds.delete(serviceWc.id);
3013
- try {
3014
- serviceWc.removeListener("devtools-open-url", onOpenUrl);
3015
- } catch {
3016
- }
3017
- });
3220
+ };
3221
+ }
3222
+
3223
+ // src/main/services/views/native-simulator-view.ts
3224
+ import { ipcMain as ipcMain2, nativeTheme as nativeTheme4, shell as shell2, WebContentsView as WebContentsView6, webContents } from "electron";
3225
+
3226
+ // src/main/services/simulator/custom-apis.ts
3227
+ async function handleCustomApiBridgeRequest(apis, req) {
3228
+ try {
3229
+ const result = req.op === "list" ? apis.list() : await apis.invoke(req.name, req.params);
3230
+ return { id: req.id, result };
3231
+ } catch (err) {
3232
+ return { id: req.id, error: err instanceof Error ? err.message : String(err) };
3018
3233
  }
3019
- function pointNativeDevtoolsAtServiceWc(next) {
3020
- if (!simulatorView || simulatorView.webContents.isDestroyed()) return true;
3021
- if (nativeDevtoolsSourceWc?.id === next.id && !nativeDevtoolsSourceWc.isDestroyed()) {
3022
- return true;
3234
+ }
3235
+ function createSimulatorApiRegistry() {
3236
+ const handlers2 = /* @__PURE__ */ new Map();
3237
+ return {
3238
+ register(name, handler) {
3239
+ handlers2.set(name, handler);
3240
+ return () => {
3241
+ if (handlers2.get(name) === handler) handlers2.delete(name);
3242
+ };
3243
+ },
3244
+ list() {
3245
+ return Array.from(handlers2.keys());
3246
+ },
3247
+ has(name) {
3248
+ return handlers2.has(name);
3249
+ },
3250
+ async invoke(name, params) {
3251
+ const handler = handlers2.get(name);
3252
+ if (!handler) throw new Error(`Simulator API "${name}" is not registered`);
3253
+ return await handler(params);
3254
+ },
3255
+ clear() {
3256
+ handlers2.clear();
3023
3257
  }
3024
- closeNativeDevtoolsSource();
3025
- nativeDevtoolsSourceWc = next;
3258
+ };
3259
+ }
3260
+
3261
+ // src/main/services/views/miniapp-partition.ts
3262
+ import * as electron from "electron";
3263
+ var SHARED_MINIAPP_PARTITION = "persist:simulator";
3264
+ var PARTITION_PREFIX = "persist:miniapp-";
3265
+ function miniappPartitionKey(appId, projectPath) {
3266
+ const safe = appId.replace(/[^A-Za-z0-9_-]/g, "");
3267
+ const base = safe === appId && safe.length > 0 ? safe : safe.length > 0 ? `${safe}-${djb2(appId).toString(36)}` : djb2(appId).toString(36);
3268
+ if (!projectPath) return base;
3269
+ return `${base}-p${djb2(projectPath).toString(36)}`;
3270
+ }
3271
+ function miniappPartition(appId, projectPath) {
3272
+ if (!appId) return SHARED_MINIAPP_PARTITION;
3273
+ return `${PARTITION_PREFIX}${miniappPartitionKey(appId, projectPath)}`;
3274
+ }
3275
+ function djb2(input) {
3276
+ let h = 5381;
3277
+ for (let i = 0; i < input.length; i++) {
3278
+ h = (h << 5) + h + input.charCodeAt(i) >>> 0;
3279
+ }
3280
+ return h;
3281
+ }
3282
+ var configurators = /* @__PURE__ */ new Set();
3283
+ var configuredPartitions = /* @__PURE__ */ new Set();
3284
+ function registerMiniappSessionConfigurator(fn) {
3285
+ configurators.add(fn);
3286
+ for (const partition of configuredPartitions) {
3026
3287
  try {
3027
- next.setDevToolsWebContents(simulatorView.webContents);
3028
- next.openDevTools({ mode: "detach", activate: false });
3029
- wireOpenInEditor(next, simulatorView.webContents);
3030
- customizeDevtoolsTabs(simulatorView.webContents);
3031
- try {
3032
- stopServiceConsole?.();
3033
- } catch {
3034
- }
3035
- stopServiceConsole = installServiceConsoleForward({
3036
- serviceWc: next,
3037
- connections: ctx.connections,
3038
- emit: (entry) => ctx.consoleForwarder?.emit(entry)
3039
- }).stop;
3040
- return true;
3041
- } catch {
3042
- if (nativeDevtoolsSourceWc?.id === next.id) {
3043
- nativeDevtoolsSourceWc = null;
3044
- }
3045
- return false;
3288
+ fn(electron.session.fromPartition(partition), partition);
3289
+ } catch (err) {
3290
+ console.warn("[miniapp-partition] configurator failed for", partition, err);
3046
3291
  }
3047
3292
  }
3048
- function pointNativeDevtoolsAtActiveServiceHost(appId) {
3049
- if (!ctx.bridge?.isNativeHost()) return true;
3050
- if (!simulatorView || simulatorView.webContents.isDestroyed()) return true;
3051
- const wc = ctx.bridge.getServiceWc(appId);
3052
- if (!wc || wc.isDestroyed()) return false;
3053
- return pointNativeDevtoolsAtServiceWc(wc);
3054
- }
3055
- function scheduleNativeDevtoolsFollow(appId, attempt = 0) {
3056
- if (attempt >= 20) return;
3057
- if (!ctx.bridge?.isNativeHost()) return;
3058
- const token = nativeDevtoolsRetryToken;
3059
- if (nativeDevtoolsRetryTimer) clearTimeout(nativeDevtoolsRetryTimer);
3060
- nativeDevtoolsRetryTimer = setTimeout(() => {
3061
- nativeDevtoolsRetryTimer = null;
3062
- if (token !== nativeDevtoolsRetryToken) return;
3063
- if (pointNativeDevtoolsAtActiveServiceHost(appId)) return;
3064
- scheduleNativeDevtoolsFollow(appId, attempt + 1);
3065
- }, 50);
3293
+ return () => {
3294
+ configurators.delete(fn);
3295
+ };
3296
+ }
3297
+ function configureMiniappSession(partition) {
3298
+ const alreadyConfigured = configuredPartitions.has(partition);
3299
+ configuredPartitions.add(partition);
3300
+ if (configurators.size === 0) return null;
3301
+ const sess = electron.session.fromPartition(partition);
3302
+ if (alreadyConfigured) return sess;
3303
+ for (const fn of configurators) {
3304
+ try {
3305
+ fn(sess, partition);
3306
+ } catch (err) {
3307
+ console.warn("[miniapp-partition] configurator failed for", partition, err);
3308
+ }
3066
3309
  }
3067
- function followNativeDevtoolsServiceHost(appId) {
3068
- clearNativeDevtoolsRetry();
3069
- if (pointNativeDevtoolsAtActiveServiceHost(appId)) return;
3070
- scheduleNativeDevtoolsFollow(appId);
3310
+ return sess;
3311
+ }
3312
+
3313
+ // src/shared/simulator-route.ts
3314
+ function decodePageSpec(value) {
3315
+ const qIdx = value.indexOf("?");
3316
+ if (qIdx < 0) return { pagePath: value, query: {} };
3317
+ const pagePath = value.slice(0, qIdx);
3318
+ const query = {};
3319
+ for (const pair of value.slice(qIdx + 1).split("&")) {
3320
+ if (!pair) continue;
3321
+ const eqIdx = pair.indexOf("=");
3322
+ const k = eqIdx >= 0 ? pair.slice(0, eqIdx) : pair;
3323
+ const v = eqIdx >= 0 ? pair.slice(eqIdx + 1) : "";
3324
+ if (k) query[decodeURIComponent(k)] = decodeURIComponent(v);
3071
3325
  }
3072
- function onNativeRenderEvent(event) {
3073
- if (event.kind !== "activePage" && event.kind !== "domReady") return;
3074
- followNativeDevtoolsServiceHost(event.appId);
3326
+ return { pagePath, query };
3327
+ }
3328
+ function parseLocationRoute(search) {
3329
+ const params = new URLSearchParams(search.startsWith("?") ? search.slice(1) : search);
3330
+ const appId = params.get("appId");
3331
+ const entryRaw = params.get("entry");
3332
+ if (!appId || !entryRaw) return null;
3333
+ const entry = decodePageSpec(entryRaw);
3334
+ const pageRaw = params.get("page");
3335
+ const current = pageRaw && pageRaw !== entryRaw ? decodePageSpec(pageRaw) : entry;
3336
+ return { appId, entry, current };
3337
+ }
3338
+ function parseRoute(url) {
3339
+ if (!url) return null;
3340
+ try {
3341
+ const u = new URL(url);
3342
+ return parseLocationRoute(u.search);
3343
+ } catch {
3344
+ return null;
3075
3345
  }
3076
- function attachNativeSimulatorDevtoolsHost() {
3077
- stopFollowingNativeServiceHost();
3078
- if (simulatorView) {
3079
- removeSimulatorDevtoolsView();
3080
- try {
3081
- if (!simulatorView.webContents.isDestroyed()) {
3082
- simulatorView.webContents.close();
3083
- }
3084
- } catch {
3085
- }
3086
- simulatorView = null;
3087
- }
3088
- simulatorView = new WebContentsView2();
3089
- const devtoolsWc = simulatorView.webContents;
3090
- raiseExecuteJavaScriptListenerCeiling(devtoolsWc);
3091
- ctx.networkForward?.setDevtoolsHost(devtoolsWc);
3092
- if (ctx.bridge) {
3093
- try {
3094
- stopElementsForward?.();
3095
- } catch {
3346
+ }
3347
+
3348
+ // src/shared/bridge-channels.ts
3349
+ var BRIDGE_CHANNELS = {
3350
+ SPAWN: "dmb:spawn",
3351
+ DISPOSE: "dmb:dispose",
3352
+ PAGE_OPEN: "dmb:page:open",
3353
+ PAGE_CLOSE: "dmb:page:close",
3354
+ PAGE_LIFECYCLE: "dmb:page:lifecycle",
3355
+ NAV_CALLBACK: "dmb:nav:callback",
3356
+ SERVICE_INVOKE: "dmb:service:invoke",
3357
+ SERVICE_PUBLISH: "dmb:service:publish",
3358
+ RENDER_INVOKE: "dmb:render:invoke",
3359
+ RENDER_PUBLISH: "dmb:render:publish",
3360
+ TO_SERVICE: "dmb:to-service",
3361
+ TO_RENDER: "dmb:to-render",
3362
+ SIMULATOR_API: "dmb:simulator-api",
3363
+ /** simulator → main: ack of an API_CALL request (carries success/fail args). */
3364
+ API_RESPONSE: "dmb:api:response",
3365
+ /**
3366
+ * simulator webview preload → main (sendSync): "is native-host mode on?".
3367
+ * The guest preload can't read the launch `process.env`, so it asks main
3368
+ * (which can) at install time. Reply is `e.returnValue = boolean`.
3369
+ */
3370
+ NATIVE_HOST_ENABLED: "dmb:native-host-enabled",
3371
+ /**
3372
+ * simulator (DeviceShell) → main: the visible top-of-stack page bridgeId.
3373
+ * Main has no z-order concept — the active page lives only in DeviceShell's
3374
+ * ShellState — so devtools panels / automation that must target "the current
3375
+ * page's render webContents" resolve it through this signal. Fire-and-forget.
3376
+ */
3377
+ ACTIVE_PAGE: "dmb:active-page",
3378
+ /**
3379
+ * simulator (DeviceShell) → main: the FULL ordered page stack (bottom→top)
3380
+ * whenever it changes. Main has no stack of its own (it only learns the
3381
+ * active bridgeId via ACTIVE_PAGE), so automation's `App.getPageStack` needs
3382
+ * this to report multi-page stacks. Fire-and-forget.
3383
+ */
3384
+ PAGE_STACK: "dmb:page-stack"
3385
+ };
3386
+ var SIMULATOR_EVENTS = {
3387
+ DOM_READY: "simulator:dom-ready",
3388
+ NAV_BAR: "simulator:navigation-bar",
3389
+ NAV_ACTION: "simulator:nav-action",
3390
+ TAB_ACTION: "simulator:tab-action",
3391
+ /** main → simulator: invoke a wx.* API on the simulator-resident MiniApp. */
3392
+ API_CALL: "simulator:api-call",
3393
+ /**
3394
+ * main → simulator: the renderer toolbar picked a different device. Carries a
3395
+ * NativeDeviceInfo; DeviceShell resizes the bezel + re-renders status bar /
3396
+ * notch. The race-free INITIAL device rides NativeHostConfig.device (read
3397
+ * synchronously at preload bridge-install); this event covers live changes.
3398
+ */
3399
+ DEVICE_CHANGE: "simulator:device-change",
3400
+ /**
3401
+ * main → simulator: a watcher rebuild finished; boot a NEW app session for
3402
+ * the carried url ({@link RelaunchPayload}) IN PLACE, keeping the live
3403
+ * DeviceShell painted, and swap once the new session's root page reports
3404
+ * DOM_READY (ready-then-swap soft reload). Main sends this only when the
3405
+ * shell is live+ready; otherwise the renderer falls back to the hard
3406
+ * attachNativeSimulator rebuild.
3407
+ */
3408
+ RELAUNCH: "simulator:relaunch"
3409
+ };
3410
+ function deviceInfoToHostEnv(d) {
3411
+ return {
3412
+ brand: d.brand,
3413
+ model: d.model,
3414
+ system: d.system,
3415
+ platform: d.platform,
3416
+ pixelRatio: d.pixelRatio,
3417
+ screenWidth: d.screenWidth,
3418
+ screenHeight: d.screenHeight,
3419
+ windowWidth: d.screenWidth,
3420
+ windowHeight: Math.max(0, d.screenHeight - d.statusBarHeight),
3421
+ statusBarHeight: d.statusBarHeight
3422
+ };
3423
+ }
3424
+
3425
+ // src/main/services/views/native-simulator-view.ts
3426
+ function createNativeSimulatorView(ctx, reconciler, deps) {
3427
+ const { safeArea, devtoolsHost, overlayPanels } = deps;
3428
+ let nativeSimulatorView = null;
3429
+ let nativeSimulatorViewAdded = false;
3430
+ let nativeSimulatorProjectPath = null;
3431
+ let settleNativeSimulatorReady = null;
3432
+ let nativeSimulatorShellReady = false;
3433
+ let nativeCustomApiBridgeHandler = null;
3434
+ let currentZoomFactor = 1;
3435
+ let simulatorWebContentsId = null;
3436
+ function applyNativeSimulatorBounds(view, bounds, zoom) {
3437
+ const p = computeNativeSimulatorViewParams(bounds, zoom);
3438
+ currentZoomFactor = p.zoomFactor;
3439
+ view.setBounds(p.bounds);
3440
+ const simWc = view.webContents;
3441
+ if (!simWc.isDestroyed()) simWc.setZoomFactor(p.zoomFactor);
3442
+ try {
3443
+ for (const wc of webContents.getAllWebContents()) {
3444
+ if (wc.isDestroyed()) continue;
3445
+ if (wc.hostWebContents === simWc) wc.setZoomFactor(p.zoomFactor);
3096
3446
  }
3097
- const thisForward = installElementsForward({ devtoolsWc, bridge: ctx.bridge, connections: ctx.connections });
3098
- stopElementsForward = thisForward;
3099
- devtoolsWc.once("destroyed", () => {
3100
- try {
3101
- thisForward();
3102
- } catch {
3103
- }
3104
- if (stopElementsForward === thisForward) stopElementsForward = null;
3105
- });
3106
- }
3107
- devtoolsWc.once("dom-ready", () => {
3108
- devtoolsWc.executeJavaScript(`
3109
- (function() {
3110
- try { localStorage.setItem('panel-selectedTab', '"console"') } catch {}
3111
- let tries = 0
3112
- const timer = setInterval(() => {
3113
- tries++
3114
- try {
3115
- const UI = globalThis.UI
3116
- const vm = UI && UI.ViewManager && typeof UI.ViewManager.instance === 'function'
3117
- ? UI.ViewManager.instance()
3118
- : null
3119
- if (vm && typeof vm.showView === 'function') {
3120
- vm.showView('console')
3121
- clearInterval(timer)
3122
- return
3123
- }
3124
- } catch {}
3125
- if (tries > 80) clearInterval(timer)
3126
- }, 50)
3127
- })()
3128
- `).catch(() => {
3129
- });
3130
- });
3131
- reconcileNow();
3132
- if (ctx.bridge?.isNativeHost()) {
3133
- unsubscribeNativeRenderEvents = ctx.bridge.onRenderEvent(onNativeRenderEvent);
3134
- followNativeDevtoolsServiceHost();
3135
- }
3136
- }
3137
- function detachNativeCustomApiBridge() {
3138
- if (nativeCustomApiBridgeHandler) {
3139
- ipcMain2.removeListener(SimulatorCustomApiBridgeChannel.Request, nativeCustomApiBridgeHandler);
3140
- nativeCustomApiBridgeHandler = null;
3447
+ } catch {
3141
3448
  }
3142
3449
  }
3143
3450
  function tearDownNativeSimulatorView(label) {
@@ -3157,7 +3464,15 @@ function createViewManager(ctx) {
3157
3464
  }
3158
3465
  nativeSimulatorView = null;
3159
3466
  nativeSimulatorViewAdded = false;
3160
- reconcileNow();
3467
+ nativeSimulatorShellReady = false;
3468
+ reconciler.forgetActual(VIEW_ID.simulator);
3469
+ reconciler.reconcileNow();
3470
+ }
3471
+ function detachNativeCustomApiBridge() {
3472
+ if (nativeCustomApiBridgeHandler) {
3473
+ ipcMain2.removeListener(SimulatorCustomApiBridgeChannel.Request, nativeCustomApiBridgeHandler);
3474
+ nativeCustomApiBridgeHandler = null;
3475
+ }
3161
3476
  }
3162
3477
  function attachNativeCustomApiBridge(simWc) {
3163
3478
  detachNativeCustomApiBridge();
@@ -3196,7 +3511,7 @@ function createViewManager(ctx) {
3196
3511
  const route = parseRoute(simulatorUrl);
3197
3512
  const partition = miniappPartition(route?.appId, ctx.workspace?.getProjectPath());
3198
3513
  configureMiniappSession(partition);
3199
- const view = new WebContentsView2({
3514
+ const view = new WebContentsView6({
3200
3515
  webPreferences: {
3201
3516
  nodeIntegration: false,
3202
3517
  contextIsolation: false,
@@ -3216,8 +3531,8 @@ function createViewManager(ctx) {
3216
3531
  } catch {
3217
3532
  }
3218
3533
  };
3219
- nativeTheme3.on("updated", syncDeskBg);
3220
- ctx.connections.acquire(simWc).own(() => nativeTheme3.removeListener("updated", syncDeskBg));
3534
+ nativeTheme4.on("updated", syncDeskBg);
3535
+ ctx.connections.acquire(simWc).own(() => nativeTheme4.removeListener("updated", syncDeskBg));
3221
3536
  attachNativeCustomApiBridge(simWc);
3222
3537
  const pendingGuestIsTab = [];
3223
3538
  simWc.on("will-attach-webview", (_event, webPreferences, params) => {
@@ -3259,10 +3574,11 @@ function createViewManager(ctx) {
3259
3574
  });
3260
3575
  guestWc.once("did-finish-load", () => {
3261
3576
  if (nativeSimulatorView !== view || simWc.isDestroyed()) return;
3577
+ nativeSimulatorShellReady = true;
3262
3578
  settleNativeSimulatorReady?.();
3263
3579
  settleNativeSimulatorReady = null;
3264
3580
  });
3265
- followNativeDevtoolsServiceHost();
3581
+ devtoolsHost.followServiceHost();
3266
3582
  });
3267
3583
  simWc.setWindowOpenHandler(({ url }) => handleWindowOpenExternal(url));
3268
3584
  simWc.on("will-navigate", (e, url) => {
@@ -3286,155 +3602,109 @@ function createViewManager(ctx) {
3286
3602
  });
3287
3603
  simulatorWebContentsId = simWc.id;
3288
3604
  ctx.networkForward?.attachSimulator(simWc);
3289
- reconcileNow();
3290
- attachNativeSimulatorDevtoolsHost();
3605
+ reconciler.reconcileNow();
3606
+ devtoolsHost.attach();
3291
3607
  return ready;
3292
3608
  }
3609
+ function softReloadNativeSimulator(simulatorUrl) {
3610
+ if (!nativeSimulatorView || nativeSimulatorView.webContents.isDestroyed()) return false;
3611
+ if (!nativeSimulatorShellReady) return false;
3612
+ const payload = { url: simulatorUrl };
3613
+ nativeSimulatorView.webContents.send(SIMULATOR_EVENTS.RELAUNCH, payload);
3614
+ return true;
3615
+ }
3293
3616
  function detachSimulator() {
3294
3617
  settleNativeSimulatorReady?.();
3295
3618
  settleNativeSimulatorReady = null;
3296
- stopFollowingNativeServiceHost();
3619
+ devtoolsHost.stopFollowing();
3297
3620
  detachNativeCustomApiBridge();
3298
- try {
3299
- stopElementsForward?.();
3300
- } catch {
3301
- }
3302
- stopElementsForward = null;
3621
+ devtoolsHost.stopElementsForwarding();
3303
3622
  ctx.networkForward?.detachSimulator();
3304
3623
  ctx.networkForward?.setDevtoolsHost(null);
3305
3624
  tearDownNativeSimulatorView("close");
3306
- hidePopover();
3307
- destroyViewInternal(settingsView);
3308
- settingsView = null;
3309
- destroyViewInternal(simulatorView);
3310
- simulatorView = null;
3311
- simulatorViewAdded = false;
3625
+ overlayPanels.hidePopover();
3626
+ overlayPanels.destroySettings();
3627
+ devtoolsHost.destroyHostView();
3312
3628
  simulatorWebContentsId = null;
3313
3629
  nativeSimulatorProjectPath = null;
3314
3630
  }
3315
- function removeSimulatorDevtoolsView() {
3316
- if (simulatorView && simulatorViewAdded) {
3317
- try {
3318
- ctx.windows.mainWindow.contentView.removeChildView(simulatorView);
3319
- } catch (e) {
3320
- console.error("[workbench] removeSimulatorDevtoolsView error", e);
3321
- }
3322
- simulatorViewAdded = false;
3323
- }
3324
- }
3325
- async function showSettings() {
3326
- if (!settingsView) {
3327
- settingsView = new WebContentsView2({
3328
- webPreferences: {
3329
- nodeIntegration: false,
3330
- contextIsolation: true,
3331
- sandbox: false,
3332
- preload: mainPreloadPath
3333
- }
3334
- });
3335
- applyNavigationHardening(settingsView.webContents, ctx.rendererDir);
3336
- settingsView.setBackgroundColor("#00000000");
3337
- await settingsView.webContents.loadFile(
3338
- path7.join(ctx.rendererDir, "entries/settings/index.html")
3339
- );
3340
- }
3341
- applySettingsBounds();
3342
- }
3343
- function hideSettings() {
3344
- overlayDesired.delete(VIEW_ID.settings);
3345
- reconcileNow();
3346
- }
3347
- function showPopover(data) {
3348
- hidePopover();
3349
- const popover = new WebContentsView2({
3350
- webPreferences: {
3351
- nodeIntegration: false,
3352
- contextIsolation: true,
3353
- sandbox: false,
3354
- preload: mainPreloadPath
3355
- }
3356
- });
3357
- applyNavigationHardening(popover.webContents, ctx.rendererDir);
3358
- popoverView = popover;
3359
- popover.setBackgroundColor("#00000000");
3360
- applyPopoverBounds();
3361
- popover.webContents.once("did-finish-load", () => {
3362
- ctx.notify.popoverInit(popover, data);
3363
- });
3364
- popover.webContents.loadFile(
3365
- path7.join(ctx.rendererDir, "entries/popover/index.html")
3366
- );
3367
- }
3368
- function hidePopover() {
3369
- if (!popoverView) return;
3370
- overlayDesired.delete(VIEW_ID.popover);
3371
- destroyViewInternal(popoverView);
3372
- popoverView = null;
3373
- reconcileNow();
3374
- ctx.notify.popoverClosed();
3375
- }
3376
- function repositionAll() {
3377
- if (overlayDesired.has(VIEW_ID.settings)) applySettingsBounds();
3378
- if (overlayDesired.has(VIEW_ID.popover)) applyPopoverBounds();
3379
- }
3380
- function disposeAll() {
3381
- detachSimulator();
3382
- detachWorkbench();
3383
- hostToolbarPort.dispose();
3384
- destroyViewInternal(hostToolbarView);
3385
- hostToolbarView = null;
3386
- hostToolbarViewAdded = false;
3387
- if (hostToolbarRuntimeAcquired) {
3388
- releaseHostToolbarSessionRuntime();
3389
- hostToolbarRuntimeAcquired = false;
3390
- }
3391
- safeArea.dispose();
3392
- }
3393
- function resize(_simWidth) {
3394
- if (overlayDesired.has(VIEW_ID.settings)) applySettingsBounds();
3395
- }
3631
+ reconciler.registerView(VIEW_ID.simulator, {
3632
+ getView: () => nativeSimulatorView,
3633
+ setAdded: (added) => {
3634
+ nativeSimulatorViewAdded = added;
3635
+ },
3636
+ gateHidden: () => !nativeSimulatorView,
3637
+ applyBounds: (view, bounds, extra) => applyNativeSimulatorBounds(view, bounds, extra?.zoom ?? 1)
3638
+ });
3396
3639
  return {
3397
3640
  attachNativeSimulator,
3641
+ softReloadNativeSimulator,
3398
3642
  detachSimulator,
3399
- reapplySafeArea: (device) => safeArea.reapplyAll(device),
3400
- showSettings,
3401
- hideSettings,
3402
- showPopover,
3403
- hidePopover,
3404
- repositionAll,
3405
- disposeAll,
3406
3643
  getSimulatorWebContentsId: () => simulatorWebContentsId,
3407
3644
  getSimulatorWebContents: () => {
3408
3645
  if (simulatorWebContentsId == null) return null;
3409
3646
  const wc = webContents.fromId(simulatorWebContentsId);
3410
3647
  return wc && !wc.isDestroyed() ? wc : null;
3411
3648
  },
3412
- getSimulatorProjectPath: () => nativeSimulatorProjectPath,
3413
- getSettingsWebContents: () => {
3414
- if (!settingsView) return null;
3415
- if (settingsView.webContents.isDestroyed()) return null;
3416
- return settingsView.webContents;
3417
- },
3418
- getSettingsWebContentsId: () => {
3419
- if (!settingsView) return null;
3420
- if (settingsView.webContents.isDestroyed()) return null;
3421
- return settingsView.webContents.id;
3422
- },
3423
- getPopoverWebContentsId: () => {
3424
- if (!popoverView) return null;
3425
- if (popoverView.webContents.isDestroyed()) return null;
3426
- return popoverView.webContents.id;
3427
- },
3428
- getHostToolbarWebContentsId: () => liveHostToolbarWebContents()?.id ?? null,
3429
- getHostToolbarHeight: () => hostToolbarLastHeight,
3430
- resize,
3431
- setPlacementSnapshot,
3432
- attachWorkbench,
3433
- setWorkbenchSource,
3434
- detachWorkbench,
3435
- openFileInWorkbench,
3436
- setHostToolbarHeight,
3437
- hostToolbar
3649
+ getSimulatorProjectPath: () => nativeSimulatorProjectPath
3650
+ };
3651
+ }
3652
+
3653
+ // src/main/services/views/view-manager.ts
3654
+ function createViewManager(ctx) {
3655
+ const safeArea = createSafeAreaController({ connections: ctx.connections });
3656
+ const reconciler = createPlacementReconciler(ctx);
3657
+ const workbench = createWorkbenchView(ctx, reconciler);
3658
+ const devtoolsHost = createDevtoolsHost(ctx, reconciler, {
3659
+ openFileInWorkbench: workbench.openFileInWorkbench
3660
+ });
3661
+ const hostToolbar = createHostToolbarView(ctx, reconciler, {
3662
+ reapplyPresentOverlays: () => reapplyToolbarDependentOverlays()
3663
+ });
3664
+ const overlayPanels = createOverlayPanelsView(ctx, reconciler, {
3665
+ getHostToolbarHeight: hostToolbar.getHostToolbarHeight
3666
+ });
3667
+ const nativeSimulator = createNativeSimulatorView(ctx, reconciler, {
3668
+ safeArea,
3669
+ devtoolsHost,
3670
+ overlayPanels
3671
+ });
3672
+ function reapplyToolbarDependentOverlays() {
3673
+ overlayPanels.reapplyPresentOverlays();
3674
+ }
3675
+ function disposeAll() {
3676
+ nativeSimulator.detachSimulator();
3677
+ workbench.detachWorkbench();
3678
+ hostToolbar.dispose();
3679
+ safeArea.dispose();
3680
+ }
3681
+ return {
3682
+ attachNativeSimulator: nativeSimulator.attachNativeSimulator,
3683
+ softReloadNativeSimulator: nativeSimulator.softReloadNativeSimulator,
3684
+ detachSimulator: nativeSimulator.detachSimulator,
3685
+ reapplySafeArea: (device) => safeArea.reapplyAll(device),
3686
+ showSettings: overlayPanels.showSettings,
3687
+ hideSettings: overlayPanels.hideSettings,
3688
+ showPopover: overlayPanels.showPopover,
3689
+ hidePopover: overlayPanels.hidePopover,
3690
+ repositionAll: () => overlayPanels.reapplyPresentOverlays(),
3691
+ disposeAll,
3692
+ getSimulatorWebContentsId: nativeSimulator.getSimulatorWebContentsId,
3693
+ getSimulatorWebContents: nativeSimulator.getSimulatorWebContents,
3694
+ getSimulatorProjectPath: nativeSimulator.getSimulatorProjectPath,
3695
+ getSettingsWebContents: overlayPanels.getSettingsWebContents,
3696
+ getSettingsWebContentsId: overlayPanels.getSettingsWebContentsId,
3697
+ getPopoverWebContentsId: overlayPanels.getPopoverWebContentsId,
3698
+ getHostToolbarWebContentsId: hostToolbar.getHostToolbarWebContentsId,
3699
+ getHostToolbarHeight: hostToolbar.getHostToolbarHeight,
3700
+ resize: () => overlayPanels.applySettingsBoundsIfPresent(),
3701
+ setPlacementSnapshot: reconciler.setPlacementSnapshot,
3702
+ attachWorkbench: workbench.attachWorkbench,
3703
+ setWorkbenchSource: workbench.setWorkbenchSource,
3704
+ detachWorkbench: workbench.detachWorkbench,
3705
+ openFileInWorkbench: workbench.openFileInWorkbench,
3706
+ setHostToolbarHeight: hostToolbar.setHostToolbarHeight,
3707
+ hostToolbar: hostToolbar.control
3438
3708
  };
3439
3709
  }
3440
3710
 
@@ -3466,7 +3736,7 @@ function createWindowService(mainWindow) {
3466
3736
 
3467
3737
  // src/main/windows/settings-window/create.ts
3468
3738
  import { BrowserWindow as BrowserWindow3 } from "electron";
3469
- import path8 from "path";
3739
+ import path9 from "path";
3470
3740
  async function createSettingsWindow(parent, rendererDir2) {
3471
3741
  const win = new BrowserWindow3({
3472
3742
  width: 420,
@@ -3484,7 +3754,7 @@ async function createSettingsWindow(parent, rendererDir2) {
3484
3754
  }
3485
3755
  });
3486
3756
  applyNavigationHardening(win.webContents, rendererDir2);
3487
- await win.loadFile(path8.join(rendererDir2, "entries/workbench-settings/index.html"));
3757
+ await win.loadFile(path9.join(rendererDir2, "entries/workbench-settings/index.html"));
3488
3758
  return win;
3489
3759
  }
3490
3760
 
@@ -3535,11 +3805,11 @@ import { z as z2 } from "zod";
3535
3805
 
3536
3806
  // src/main/services/projects/project-repository.ts
3537
3807
  import { app as app6 } from "electron";
3538
- import path9 from "path";
3808
+ import path10 from "path";
3539
3809
  import fs5 from "fs";
3540
3810
  var log2 = createLogger("projects");
3541
3811
  function getProjectsFile() {
3542
- return path9.join(app6.getPath("userData"), "dimina-projects.json");
3812
+ return path10.join(app6.getPath("userData"), "dimina-projects.json");
3543
3813
  }
3544
3814
  function load() {
3545
3815
  try {
@@ -3561,13 +3831,13 @@ function validateProjectDir(dirPath) {
3561
3831
  if (!fs5.existsSync(dirPath)) {
3562
3832
  return `\u5C0F\u7A0B\u5E8F\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${dirPath}`;
3563
3833
  }
3564
- if (!fs5.existsSync(path9.join(dirPath, "app.json"))) {
3565
- const configPath = path9.join(dirPath, "project.config.json");
3834
+ if (!fs5.existsSync(path10.join(dirPath, "app.json"))) {
3835
+ const configPath = path10.join(dirPath, "project.config.json");
3566
3836
  if (fs5.existsSync(configPath)) {
3567
3837
  try {
3568
3838
  const cfg = JSON.parse(fs5.readFileSync(configPath, "utf-8"));
3569
3839
  if (cfg.miniprogramRoot) {
3570
- const resolvedRoot = path9.resolve(dirPath, cfg.miniprogramRoot);
3840
+ const resolvedRoot = path10.resolve(dirPath, cfg.miniprogramRoot);
3571
3841
  return `\u8BE5\u76EE\u5F55\u7F3A\u5C11 app.json\uFF0Cproject.config.json \u4E2D\u6307\u5B9A\u4E86 miniprogramRoot: "${cfg.miniprogramRoot}"\uFF0C\u8BF7\u5BFC\u5165 ${resolvedRoot}`;
3572
3842
  }
3573
3843
  } catch (err) {
@@ -3580,9 +3850,9 @@ function validateProjectDir(dirPath) {
3580
3850
  }
3581
3851
  function addProject(dirPath) {
3582
3852
  const projects = load();
3583
- let name = path9.basename(dirPath);
3853
+ let name = path10.basename(dirPath);
3584
3854
  try {
3585
- const configPath = path9.join(dirPath, "project.config.json");
3855
+ const configPath = path10.join(dirPath, "project.config.json");
3586
3856
  if (fs5.existsSync(configPath)) {
3587
3857
  const cfg = JSON.parse(fs5.readFileSync(configPath, "utf-8"));
3588
3858
  if (cfg.projectname) name = cfg.projectname;
@@ -3621,7 +3891,7 @@ function getCompileConfig(dirPath) {
3621
3891
  };
3622
3892
  }
3623
3893
  function getProjectPages(dirPath) {
3624
- const appJsonPath = path9.join(dirPath, "app.json");
3894
+ const appJsonPath = path10.join(dirPath, "app.json");
3625
3895
  try {
3626
3896
  const appJson = JSON.parse(
3627
3897
  fs5.readFileSync(appJsonPath, "utf-8")
@@ -3647,7 +3917,7 @@ function getProjectSettings(projectPath) {
3647
3917
  return { uploadWithSourceMap: false };
3648
3918
  }
3649
3919
  try {
3650
- const configPath = path9.join(projectPath, "project.config.json");
3920
+ const configPath = path10.join(projectPath, "project.config.json");
3651
3921
  const config = JSON.parse(fs5.readFileSync(configPath, "utf-8"));
3652
3922
  return {
3653
3923
  uploadWithSourceMap: !!config.setting?.uploadWithSourceMap
@@ -3658,7 +3928,7 @@ function getProjectSettings(projectPath) {
3658
3928
  }
3659
3929
  function updateProjectSettings(projectPath, patch) {
3660
3930
  if (!projectPath) return;
3661
- const configPath = path9.join(projectPath, "project.config.json");
3931
+ const configPath = path10.join(projectPath, "project.config.json");
3662
3932
  let config = {};
3663
3933
  try {
3664
3934
  config = JSON.parse(fs5.readFileSync(configPath, "utf-8"));
@@ -3794,7 +4064,7 @@ function createWorkspaceService(ctx) {
3794
4064
  sourcemap: true,
3795
4065
  fileTypes: ctx.fileTypes,
3796
4066
  watch: compile.watch,
3797
- onRebuild: () => sendStatus("ready", "\u7F16\u8BD1\u5B8C\u6210\uFF0C\u5DF2\u70ED\u66F4\u65B0", true),
4067
+ onRebuild: () => sendStatus("ready", "\u7F16\u8BD1\u5B8C\u6210\uFF0C\u5DF2\u91CD\u542F", true),
3798
4068
  onBuildError: (err) => sendStatus("error", String(err)),
3799
4069
  onLog: (entry) => {
3800
4070
  if (sessionGeneration !== logGeneration) return;
@@ -3963,16 +4233,16 @@ function createWorkspaceService(ctx) {
3963
4233
  // src/main/services/projects/thumbnail.ts
3964
4234
  import { createHash } from "crypto";
3965
4235
  import fs6 from "fs";
3966
- import path10 from "path";
4236
+ import path11 from "path";
3967
4237
  import { app as app7 } from "electron";
3968
4238
  function getThumbnailDir() {
3969
- return path10.join(app7.getPath("userData"), "thumbnails");
4239
+ return path11.join(app7.getPath("userData"), "thumbnails");
3970
4240
  }
3971
4241
  function hashProjectPath(projectPath) {
3972
4242
  return createHash("sha256").update(projectPath).digest("hex").slice(0, 16);
3973
4243
  }
3974
4244
  function getThumbnailPath(projectPath) {
3975
- return path10.join(getThumbnailDir(), `${hashProjectPath(projectPath)}.png`);
4245
+ return path11.join(getThumbnailDir(), `${hashProjectPath(projectPath)}.png`);
3976
4246
  }
3977
4247
  var DATA_URL_PNG_PREFIX = "data:image/png;base64,";
3978
4248
  function saveThumbnailFromDataUrl(projectPath, imageDataUrl) {
@@ -4033,20 +4303,20 @@ function sanitizeTemplates(templates) {
4033
4303
  }
4034
4304
 
4035
4305
  // src/main/services/projects/builtin-templates.ts
4036
- import path11 from "node:path";
4037
- var TEMPLATES_DIR = path11.join(devtoolsPackageRoot, "templates");
4306
+ import path12 from "node:path";
4307
+ var TEMPLATES_DIR = path12.join(devtoolsPackageRoot, "templates");
4038
4308
  var BUILTIN_TEMPLATES = [
4039
4309
  {
4040
4310
  id: "blank",
4041
4311
  name: "Blank",
4042
4312
  description: "\u6700\u5C0F\u9AA8\u67B6\uFF1Aapp.* + \u4E00\u4E2A index \u9875\u9762",
4043
- source: { type: "directory", path: path11.join(TEMPLATES_DIR, "blank") }
4313
+ source: { type: "directory", path: path12.join(TEMPLATES_DIR, "blank") }
4044
4314
  },
4045
4315
  {
4046
4316
  id: "taro-todo",
4047
4317
  name: "Taro Todo",
4048
4318
  description: "Taro \u7F16\u8BD1\u4EA7\u7269\u7684 Todo \u793A\u4F8B",
4049
- source: { type: "directory", path: path11.join(TEMPLATES_DIR, "taro-todo") }
4319
+ source: { type: "directory", path: path12.join(TEMPLATES_DIR, "taro-todo") }
4050
4320
  }
4051
4321
  ];
4052
4322
 
@@ -4192,6 +4462,11 @@ var SimulatorAttachNativeSchema = z3.tuple([
4192
4462
  }),
4193
4463
  SimWidth
4194
4464
  ]);
4465
+ var SimulatorSoftReloadSchema = z3.tuple([
4466
+ z3.string().url().refine((u) => u.startsWith("http://") || u.startsWith("https://"), {
4467
+ message: "simulator URL must be http(s)"
4468
+ })
4469
+ ]);
4195
4470
  var SimulatorSetDeviceInfoSchema = z3.tuple([
4196
4471
  z3.object({
4197
4472
  brand: z3.string().max(64),
@@ -4283,79 +4558,14 @@ var SettingsProjectSettingsChangedSchema = z3.tuple([
4283
4558
  var ProjectCaptureThumbnailSchema = z3.tuple([AbsolutePath]);
4284
4559
  var ProjectGetThumbnailSchema = z3.tuple([AbsolutePath]);
4285
4560
 
4286
- // src/shared/bridge-channels.ts
4287
- var BRIDGE_CHANNELS = {
4288
- SPAWN: "dmb:spawn",
4289
- DISPOSE: "dmb:dispose",
4290
- PAGE_OPEN: "dmb:page:open",
4291
- PAGE_CLOSE: "dmb:page:close",
4292
- PAGE_LIFECYCLE: "dmb:page:lifecycle",
4293
- NAV_CALLBACK: "dmb:nav:callback",
4294
- SERVICE_INVOKE: "dmb:service:invoke",
4295
- SERVICE_PUBLISH: "dmb:service:publish",
4296
- RENDER_INVOKE: "dmb:render:invoke",
4297
- RENDER_PUBLISH: "dmb:render:publish",
4298
- TO_SERVICE: "dmb:to-service",
4299
- TO_RENDER: "dmb:to-render",
4300
- SIMULATOR_API: "dmb:simulator-api",
4301
- /** simulator → main: ack of an API_CALL request (carries success/fail args). */
4302
- API_RESPONSE: "dmb:api:response",
4303
- /**
4304
- * simulator webview preload → main (sendSync): "is native-host mode on?".
4305
- * The guest preload can't read the launch `process.env`, so it asks main
4306
- * (which can) at install time. Reply is `e.returnValue = boolean`.
4307
- */
4308
- NATIVE_HOST_ENABLED: "dmb:native-host-enabled",
4309
- /**
4310
- * simulator (DeviceShell) → main: the visible top-of-stack page bridgeId.
4311
- * Main has no z-order concept — the active page lives only in DeviceShell's
4312
- * ShellState — so devtools panels / automation that must target "the current
4313
- * page's render webContents" resolve it through this signal. Fire-and-forget.
4314
- */
4315
- ACTIVE_PAGE: "dmb:active-page",
4316
- /**
4317
- * simulator (DeviceShell) → main: the FULL ordered page stack (bottom→top)
4318
- * whenever it changes. Main has no stack of its own (it only learns the
4319
- * active bridgeId via ACTIVE_PAGE), so automation's `App.getPageStack` needs
4320
- * this to report multi-page stacks. Fire-and-forget.
4321
- */
4322
- PAGE_STACK: "dmb:page-stack"
4323
- };
4324
- var SIMULATOR_EVENTS = {
4325
- DOM_READY: "simulator:dom-ready",
4326
- NAV_BAR: "simulator:navigation-bar",
4327
- NAV_ACTION: "simulator:nav-action",
4328
- TAB_ACTION: "simulator:tab-action",
4329
- /** main → simulator: invoke a wx.* API on the simulator-resident MiniApp. */
4330
- API_CALL: "simulator:api-call",
4331
- /**
4332
- * main → simulator: the renderer toolbar picked a different device. Carries a
4333
- * NativeDeviceInfo; DeviceShell resizes the bezel + re-renders status bar /
4334
- * notch. The race-free INITIAL device rides NativeHostConfig.device (read
4335
- * synchronously at preload bridge-install); this event covers live changes.
4336
- */
4337
- DEVICE_CHANGE: "simulator:device-change"
4338
- };
4339
- function deviceInfoToHostEnv(d) {
4340
- return {
4341
- brand: d.brand,
4342
- model: d.model,
4343
- system: d.system,
4344
- platform: d.platform,
4345
- pixelRatio: d.pixelRatio,
4346
- screenWidth: d.screenWidth,
4347
- screenHeight: d.screenHeight,
4348
- windowWidth: d.screenWidth,
4349
- windowHeight: Math.max(0, d.screenHeight - d.statusBarHeight),
4350
- statusBarHeight: d.statusBarHeight
4351
- };
4352
- }
4353
-
4354
4561
  // src/main/ipc/simulator.ts
4355
4562
  function registerSimulatorIpc(ctx) {
4356
4563
  return new IpcRegistry(ctx.senderPolicy).handle(SimulatorChannel.AttachNative, (_, ...args) => {
4357
4564
  const [simulatorUrl, simWidth] = validate(SimulatorChannel.AttachNative, SimulatorAttachNativeSchema, args);
4358
4565
  return ctx.views.attachNativeSimulator(simulatorUrl, simWidth);
4566
+ }).handle(SimulatorChannel.SoftReload, (_, ...args) => {
4567
+ const [simulatorUrl] = validate(SimulatorChannel.SoftReload, SimulatorSoftReloadSchema, args);
4568
+ return ctx.views.softReloadNativeSimulator(simulatorUrl);
4359
4569
  }).handle(SimulatorChannel.Detach, () => {
4360
4570
  ctx.views.detachSimulator();
4361
4571
  }).handle(SimulatorChannel.SetDeviceInfo, (_, ...args) => {
@@ -4391,11 +4601,11 @@ var popoverModule = {
4391
4601
 
4392
4602
  // src/main/ipc/projects.ts
4393
4603
  import { app as app8, dialog } from "electron";
4394
- import path13 from "path";
4604
+ import path14 from "path";
4395
4605
 
4396
4606
  // src/main/services/projects/create-project-service.ts
4397
4607
  import fs7 from "node:fs";
4398
- import path12 from "node:path";
4608
+ import path13 from "node:path";
4399
4609
  var DEFAULT_TEMPLATE_ID = "blank";
4400
4610
  async function createProject(input, ctx) {
4401
4611
  const name = (input.name ?? "").trim();
@@ -4442,7 +4652,7 @@ async function createProject(input, ctx) {
4442
4652
  force: true
4443
4653
  });
4444
4654
  }
4445
- const cfgPath = path12.join(target, "project.config.json");
4655
+ const cfgPath = path13.join(target, "project.config.json");
4446
4656
  let cfg = {};
4447
4657
  if (fs7.existsSync(cfgPath)) {
4448
4658
  try {
@@ -4528,7 +4738,7 @@ function registerProjectsIpc(ctx) {
4528
4738
  }
4529
4739
  try {
4530
4740
  const settings = loadWorkbenchSettings();
4531
- const newBase = path13.dirname(input.path);
4741
+ const newBase = path14.dirname(input.path);
4532
4742
  if (newBase && settings.lastCreateBaseDir !== newBase) {
4533
4743
  saveWorkbenchSettings({ ...settings, lastCreateBaseDir: newBase });
4534
4744
  }
@@ -4700,7 +4910,7 @@ import { DisposableRegistry as DisposableRegistry6 } from "@dimina-kit/electron-
4700
4910
 
4701
4911
  // src/main/ipc/bridge-router.ts
4702
4912
  import { app as app12, ipcMain as ipcMain3, protocol as protocol2, session as electronSession, webContents as webContents2 } from "electron";
4703
- import path16 from "node:path";
4913
+ import path17 from "node:path";
4704
4914
  import { pathToFileURL as pathToFileURL3 } from "node:url";
4705
4915
 
4706
4916
  // src/shared/simulator-api-metadata.ts
@@ -4709,13 +4919,40 @@ function isPersistentSimulatorApi(name) {
4709
4919
  return PERSISTENT_SIMULATOR_APIS.has(name);
4710
4920
  }
4711
4921
 
4922
+ // src/main/ipc/session-listener-bag.ts
4923
+ function createSessionListenerBag() {
4924
+ const records = [];
4925
+ let disposed = false;
4926
+ return {
4927
+ on(emitter, event, fn) {
4928
+ if (disposed) return;
4929
+ emitter.on(event, fn);
4930
+ records.push({ emitter, event, fn });
4931
+ },
4932
+ once(emitter, event, fn) {
4933
+ if (disposed) return;
4934
+ emitter.once(event, fn);
4935
+ records.push({ emitter, event, fn });
4936
+ },
4937
+ dispose() {
4938
+ if (disposed) return;
4939
+ disposed = true;
4940
+ for (const { emitter, event, fn } of records) {
4941
+ if (emitter.isDestroyed?.()) continue;
4942
+ emitter.removeListener(event, fn);
4943
+ }
4944
+ records.length = 0;
4945
+ }
4946
+ };
4947
+ }
4948
+
4712
4949
  // src/main/ipc/bridge-router.ts
4713
4950
  import { createDebugTap } from "@dimina-kit/electron-deck/main";
4714
4951
 
4715
4952
  // src/main/services/dimina-resource-server.ts
4716
4953
  import http from "node:http";
4717
4954
  import fs8 from "node:fs";
4718
- import path14 from "node:path";
4955
+ import path15 from "node:path";
4719
4956
  var MIME_TYPES = {
4720
4957
  ".css": "text/css; charset=utf-8",
4721
4958
  ".html": "text/html; charset=utf-8",
@@ -4731,7 +4968,7 @@ var MIME_TYPES = {
4731
4968
  ".wasm": "application/wasm"
4732
4969
  };
4733
4970
  async function startDiminaResourceServer(rootDir) {
4734
- const root = path14.resolve(rootDir);
4971
+ const root = path15.resolve(rootDir);
4735
4972
  const server = http.createServer((req, res) => {
4736
4973
  void handleRequest(root, req, res);
4737
4974
  });
@@ -4779,7 +5016,7 @@ async function handleRequest(root, req, res) {
4779
5016
  return;
4780
5017
  }
4781
5018
  res.writeHead(200, {
4782
- "content-type": MIME_TYPES[path14.extname(filePath)] ?? "application/octet-stream",
5019
+ "content-type": MIME_TYPES[path15.extname(filePath)] ?? "application/octet-stream",
4783
5020
  "content-length": stat.size,
4784
5021
  "cache-control": "no-store"
4785
5022
  });
@@ -4795,8 +5032,8 @@ async function handleRequest(root, req, res) {
4795
5032
  }
4796
5033
  function resolveContainedPath(root, pathname) {
4797
5034
  const relative = pathname.replace(/^\/+/, "") || "index.html";
4798
- const resolved = path14.resolve(root, relative);
4799
- if (resolved !== root && !resolved.startsWith(root + path14.sep)) return null;
5035
+ const resolved = path15.resolve(root, relative);
5036
+ if (resolved !== root && !resolved.startsWith(root + path15.sep)) return null;
4800
5037
  return resolved;
4801
5038
  }
4802
5039
  function setCorsHeaders(res) {
@@ -4808,11 +5045,11 @@ function setCorsHeaders(res) {
4808
5045
 
4809
5046
  // src/main/windows/service-host-window/create.ts
4810
5047
  import { app as app10, BrowserWindow as BrowserWindow4 } from "electron";
4811
- import path15 from "node:path";
5048
+ import path16 from "node:path";
4812
5049
  import { pathToFileURL as pathToFileURL2 } from "node:url";
4813
5050
  var SERVICE_HOST_PARTITION = SHARED_MINIAPP_PARTITION;
4814
- var serviceHostPreloadPath = path15.join(devtoolsPackageRoot, "dist/service-host/preload.cjs");
4815
- var serviceHostHtmlPath = path15.join(devtoolsPackageRoot, "dist/service-host/service.html");
5051
+ var serviceHostPreloadPath = path16.join(devtoolsPackageRoot, "dist/service-host/preload.cjs");
5052
+ var serviceHostHtmlPath = path16.join(devtoolsPackageRoot, "dist/service-host/service.html");
4816
5053
  function constructServiceHostWindow(opts = {}) {
4817
5054
  const partition = opts.partition ?? SERVICE_HOST_PARTITION;
4818
5055
  configureMiniappSession(partition);
@@ -5870,7 +6107,8 @@ function installBridgeRouter(ctx) {
5870
6107
  const state = {
5871
6108
  appSessions: /* @__PURE__ */ new Map(),
5872
6109
  pageSessions: /* @__PURE__ */ new Map(),
5873
- wcIdToAppSessionId: /* @__PURE__ */ new Map(),
6110
+ serviceWcIdToAppSessionId: /* @__PURE__ */ new Map(),
6111
+ simulatorWcIdToAppSessionIds: /* @__PURE__ */ new Map(),
5874
6112
  wcIdToBridgeId: /* @__PURE__ */ new Map(),
5875
6113
  pendingApiCalls: /* @__PURE__ */ new Map(),
5876
6114
  pool: null,
@@ -5902,8 +6140,8 @@ function installBridgeRouter(ctx) {
5902
6140
  const onNativeHostQuery = (event) => {
5903
6141
  const reply = {
5904
6142
  enabled: true,
5905
- renderHostHtmlUrl: pathToFileURL3(path16.join(devtoolsPackageRoot, "dist/render-host/pageFrame.html")).toString(),
5906
- renderPreloadUrl: pathToFileURL3(path16.join(devtoolsPackageRoot, "dist/render-host/preload.cjs")).toString(),
6143
+ renderHostHtmlUrl: pathToFileURL3(path17.join(devtoolsPackageRoot, "dist/render-host/pageFrame.html")).toString(),
6144
+ renderPreloadUrl: pathToFileURL3(path17.join(devtoolsPackageRoot, "dist/render-host/preload.cjs")).toString(),
5907
6145
  device: currentDevice ?? void 0
5908
6146
  };
5909
6147
  event.returnValue = reply;
@@ -5978,9 +6216,34 @@ function installBridgeRouter(ctx) {
5978
6216
  return Promise.all(ids.map((id) => disposeAppSession(state, id))).then(() => {
5979
6217
  });
5980
6218
  },
5981
- debugTap: state.debugTap
6219
+ debugTap: state.debugTap,
6220
+ census: () => {
6221
+ const simulatorDestroyedListeners = {};
6222
+ let simulatorWcBindings = 0;
6223
+ for (const ids of state.simulatorWcIdToAppSessionIds.values()) simulatorWcBindings += ids.size;
6224
+ for (const ap of state.appSessions.values()) {
6225
+ const wc = ap.simulatorWc;
6226
+ if (!wc.isDestroyed() && simulatorDestroyedListeners[wc.id] === void 0) {
6227
+ simulatorDestroyedListeners[wc.id] = wc.listenerCount("destroyed");
6228
+ }
6229
+ }
6230
+ return {
6231
+ appSessions: state.appSessions.size,
6232
+ pageSessions: state.pageSessions.size,
6233
+ serviceWcBindings: state.serviceWcIdToAppSessionId.size,
6234
+ simulatorWcs: state.simulatorWcIdToAppSessionIds.size,
6235
+ simulatorWcBindings,
6236
+ renderWcBindings: state.wcIdToBridgeId.size,
6237
+ pendingApiCalls: state.pendingApiCalls.size,
6238
+ simulatorDestroyedListeners
6239
+ };
6240
+ }
5982
6241
  };
5983
6242
  ctx.bridge = bridgeHandle;
6243
+ if (process.env.NODE_ENV === "test") {
6244
+ ;
6245
+ globalThis.__diminaResourceCensus = () => bridgeHandle.census?.();
6246
+ }
5984
6247
  const consoleForwarder = createConsoleForwarder(bridgeHandle);
5985
6248
  ctx.consoleForwarder = consoleForwarder;
5986
6249
  ctx.guestConsole = consoleForwarder;
@@ -5992,8 +6255,7 @@ function installBridgeRouter(ctx) {
5992
6255
  const onActivePage = (event, payload) => {
5993
6256
  const ap = state.appSessions.get(payload.appSessionId);
5994
6257
  if (!ap) return;
5995
- const senderApp = appByWc(state, event.sender);
5996
- if (!senderApp || senderApp.appSessionId !== ap.appSessionId) return;
6258
+ if (!senderBoundToSession(state, event.sender, ap)) return;
5997
6259
  if (ap.pages.has(payload.bridgeId)) {
5998
6260
  ap.activeBridgeId = payload.bridgeId;
5999
6261
  const pagePath = state.pageSessions.get(payload.bridgeId)?.pagePath;
@@ -6007,8 +6269,7 @@ function installBridgeRouter(ctx) {
6007
6269
  const onPageStack = (event, payload) => {
6008
6270
  const ap = state.appSessions.get(payload.appSessionId);
6009
6271
  if (!ap) return;
6010
- const senderApp = appByWc(state, event.sender);
6011
- if (!senderApp || senderApp.appSessionId !== ap.appSessionId) return;
6272
+ if (!senderBoundToSession(state, event.sender, ap)) return;
6012
6273
  ap.pageStack = payload.stack;
6013
6274
  };
6014
6275
  ipcMain3.on(BRIDGE_CHANNELS.PAGE_STACK, onPageStack);
@@ -6038,8 +6299,7 @@ function installBridgeRouter(ctx) {
6038
6299
  handlePageLifecycle(state, event.sender, payload);
6039
6300
  if (payload.event === "pageUnload" && ctx.appData) {
6040
6301
  const ap = state.appSessions.get(payload.appSessionId);
6041
- const senderApp = appByWc(state, event.sender);
6042
- if (ap && senderApp && senderApp.appSessionId === ap.appSessionId) {
6302
+ if (ap && senderBoundToSession(state, event.sender, ap)) {
6043
6303
  ctx.appData.evictBridge(ap.appId, payload.bridgeId);
6044
6304
  }
6045
6305
  }
@@ -6058,9 +6318,8 @@ function installBridgeRouter(ctx) {
6058
6318
  const onDispose = (event, payload) => {
6059
6319
  const target = resolveAppByBridgeId(state, payload.bridgeId);
6060
6320
  if (!target) return;
6061
- const senderApp = appByWc(state, event.sender);
6062
- if (senderApp && senderApp.appSessionId !== target.appSessionId) {
6063
- console.warn(`[bridge-router] DISPOSE rejected: sender belongs to ${senderApp.appSessionId}, target ${target.appSessionId}`);
6321
+ if (!senderBoundToSession(state, event.sender, target) && appByWc(state, event.sender)) {
6322
+ console.warn(`[bridge-router] DISPOSE rejected: sender not bound to target ${target.appSessionId}`);
6064
6323
  return;
6065
6324
  }
6066
6325
  void disposeAppSession(state, target.appSessionId);
@@ -6159,7 +6418,7 @@ async function handleSpawn(state, ctx, event, opts) {
6159
6418
  const simulatorWc = resolveSimulatorWebContents(ctx, opts.simulatorWcId, event.sender);
6160
6419
  const pagePath = normalizePagePath(opts.pagePath || "pages/index/index");
6161
6420
  const workspaceProjectPath = typeof ctx.workspace.getProjectPath === "function" ? ctx.workspace.getProjectPath() : "";
6162
- const pkgRoot = path16.resolve(opts.pkgRoot || workspaceProjectPath || process.cwd());
6421
+ const pkgRoot = path17.resolve(opts.pkgRoot || workspaceProjectPath || process.cwd());
6163
6422
  const root = opts.root || "main";
6164
6423
  const apiNamespaces = ctx.apiNamespaces ?? [];
6165
6424
  let resourceServer = null;
@@ -6167,7 +6426,7 @@ async function handleSpawn(state, ctx, event, opts) {
6167
6426
  if (opts.resourceBaseUrl) {
6168
6427
  resourceBaseUrl = opts.resourceBaseUrl.endsWith("/") ? opts.resourceBaseUrl : `${opts.resourceBaseUrl}/`;
6169
6428
  } else {
6170
- resourceServer = await startDiminaResourceServer(path16.resolve(pkgRoot, root));
6429
+ resourceServer = await startDiminaResourceServer(path17.resolve(pkgRoot, root));
6171
6430
  resourceBaseUrl = resourceServer.baseUrl;
6172
6431
  }
6173
6432
  const selectedDevice = ctx.bridge?.getDevice?.() ?? null;
@@ -6226,8 +6485,8 @@ async function handleSpawn(state, ctx, event, opts) {
6226
6485
  pages: /* @__PURE__ */ new Map(),
6227
6486
  activeBridgeId: null,
6228
6487
  poolEntryId,
6229
- onServiceClosed: null,
6230
6488
  onServiceBoot: null,
6489
+ listenerBag: createSessionListenerBag(),
6231
6490
  registryHandle: null
6232
6491
  };
6233
6492
  const rootPage = {
@@ -6246,23 +6505,22 @@ async function handleSpawn(state, ctx, event, opts) {
6246
6505
  state.appSessions.set(appSessionId, appSession);
6247
6506
  state.pageSessions.set(bridgeId, rootPage);
6248
6507
  appSession.pages.set(bridgeId, rootPage);
6249
- bindWc(state.wcIdToAppSessionId, serviceWindow.webContents, appSessionId);
6250
- bindWc(state.wcIdToAppSessionId, simulatorWc, appSessionId);
6508
+ bindWc(state.serviceWcIdToAppSessionId, serviceWindow.webContents, appSessionId);
6509
+ bindSimulatorWc(state.simulatorWcIdToAppSessionIds, simulatorWc, appSessionId);
6251
6510
  state.connections.acquire(serviceWindow.webContents).own(() => {
6252
- if (state.wcIdToAppSessionId.get(appSession.serviceWc.id) === appSessionId) {
6253
- state.wcIdToAppSessionId.delete(appSession.serviceWc.id);
6511
+ if (state.serviceWcIdToAppSessionId.get(appSession.serviceWc.id) === appSessionId) {
6512
+ state.serviceWcIdToAppSessionId.delete(appSession.serviceWc.id);
6254
6513
  }
6255
6514
  });
6256
6515
  appSession.registryHandle = ctx.registry.add(() => disposeAppSession(state, appSessionId));
6257
6516
  const onServiceClosed = () => {
6258
6517
  void disposeAppSession(state, appSessionId, { serviceAlreadyClosed: true });
6259
6518
  };
6260
- appSession.onServiceClosed = onServiceClosed;
6261
- serviceWindow.once("closed", onServiceClosed);
6519
+ appSession.listenerBag.once(serviceWindow, "closed", onServiceClosed);
6262
6520
  const onSimulatorDestroyed = () => {
6263
6521
  void disposeAppSession(state, appSessionId);
6264
6522
  };
6265
- simulatorWc.once("destroyed", onSimulatorDestroyed);
6523
+ appSession.listenerBag.once(simulatorWc, "destroyed", onSimulatorDestroyed);
6266
6524
  if (simulatorWc.isDestroyed()) {
6267
6525
  void disposeAppSession(state, appSessionId);
6268
6526
  }
@@ -6310,8 +6568,7 @@ async function handleSpawn(state, ctx, event, opts) {
6310
6568
  async function handlePageOpen(state, event, opts) {
6311
6569
  const ap = state.appSessions.get(opts.appSessionId);
6312
6570
  if (!ap) throw new Error(`[bridge-router] PAGE_OPEN unknown appSession ${opts.appSessionId}`);
6313
- const senderApp = appByWc(state, event.sender);
6314
- if (!senderApp || senderApp.appSessionId !== opts.appSessionId) {
6571
+ if (!senderBoundToSession(state, event.sender, ap)) {
6315
6572
  throw new Error("[bridge-router] PAGE_OPEN rejected: caller not bound to app session");
6316
6573
  }
6317
6574
  const pagePath = normalizePagePath(opts.pagePath);
@@ -6344,8 +6601,7 @@ function handlePageClose(state, sender, payload) {
6344
6601
  }
6345
6602
  const ap = state.appSessions.get(page.appSessionId);
6346
6603
  if (!ap) return;
6347
- const senderApp = appByWc(state, sender);
6348
- if (!senderApp || senderApp.appSessionId !== ap.appSessionId) {
6604
+ if (!senderBoundToSession(state, sender, ap)) {
6349
6605
  console.warn("[bridge-router] PAGE_CLOSE rejected: caller not bound to app session");
6350
6606
  return;
6351
6607
  }
@@ -6354,8 +6610,7 @@ function handlePageClose(state, sender, payload) {
6354
6610
  function handlePageLifecycle(state, sender, payload) {
6355
6611
  const ap = state.appSessions.get(payload.appSessionId);
6356
6612
  if (!ap) return;
6357
- const senderApp = appByWc(state, sender);
6358
- if (!senderApp || senderApp.appSessionId !== ap.appSessionId) return;
6613
+ if (!senderBoundToSession(state, sender, ap)) return;
6359
6614
  forwardToService(ap, {
6360
6615
  type: payload.event,
6361
6616
  target: "service",
@@ -6365,8 +6620,7 @@ function handlePageLifecycle(state, sender, payload) {
6365
6620
  function handleNavCallback(state, sender, payload) {
6366
6621
  const ap = state.appSessions.get(payload.appSessionId);
6367
6622
  if (!ap) return;
6368
- const senderApp = appByWc(state, sender);
6369
- if (!senderApp || senderApp.appSessionId !== ap.appSessionId) return;
6623
+ if (!senderBoundToSession(state, sender, ap)) return;
6370
6624
  const result = { errMsg: payload.errMsg };
6371
6625
  if (payload.ok) {
6372
6626
  sendCallback(ap, payload.callbacks.success, result);
@@ -6382,18 +6636,23 @@ async function bootServiceHost(state, ap, ctx) {
6382
6636
  reportLogicLoadFailure(ap, ctx);
6383
6637
  return;
6384
6638
  }
6385
- forwardToService(ap, makeLoadResource(ap, ap.pages.get(ap.appSessionId), "service"));
6639
+ const rootPage = ap.pages.get(ap.appSessionId);
6640
+ const rootMissing = !!rootPage && !pageInManifest(ap, rootPage.pagePath);
6641
+ if (rootMissing) {
6642
+ reportPageNotFound(ap, ctx, rootPage.pagePath);
6643
+ } else if (rootPage) {
6644
+ forwardToService(ap, makeLoadResource(ap, rootPage, "service"));
6645
+ }
6386
6646
  for (const page of ap.pages.values()) {
6387
- if (page.renderLoadPending) {
6388
- page.renderLoadPending = false;
6389
- sendRenderLoadResource(ap, page);
6390
- }
6647
+ if (!page.renderLoadPending) continue;
6648
+ page.renderLoadPending = false;
6649
+ sendRenderLoadResource(ap, page);
6391
6650
  }
6392
6651
  }
6393
6652
  function sendRenderLoadResource(ap, page) {
6394
- if (page.renderWc && !page.renderWc.isDestroyed()) {
6395
- page.renderWc.send(BRIDGE_CHANNELS.TO_RENDER, { msg: makeLoadResource(ap, page, "render") });
6396
- }
6653
+ if (!page.renderWc || page.renderWc.isDestroyed()) return;
6654
+ if (!pageInManifest(ap, page.pagePath)) return;
6655
+ page.renderWc.send(BRIDGE_CHANNELS.TO_RENDER, { msg: makeLoadResource(ap, page, "render") });
6397
6656
  }
6398
6657
  function logicBundleUrl(ap) {
6399
6658
  return ap.resourceServer ? new URL("logic.js", ap.resourceBaseUrl).toString() : new URL(`${ap.appId}/${ap.root}/logic.js`, ap.resourceBaseUrl).toString();
@@ -6418,6 +6677,14 @@ function reportLogicLoadFailure(ap, ctx) {
6418
6677
  console.error(message);
6419
6678
  ctx.guestConsole?.emit({ source: "service", level: "error", args: [message] });
6420
6679
  }
6680
+ function pageInManifest(ap, pagePath) {
6681
+ return ap.manifest.pages.includes(normalizePagePath(pagePath));
6682
+ }
6683
+ function reportPageNotFound(ap, ctx, pagePath) {
6684
+ const message = `Page[${pagePath}] not found. May be caused by: 1. Forgetting to add page route in app.json. 2. Invoking Page() in async task.`;
6685
+ console.error(`[bridge-router] ${message}`);
6686
+ ctx.guestConsole?.emit({ source: "service", level: "error", args: [message] });
6687
+ }
6421
6688
  function maybeSendResourceLoaded(ap, page) {
6422
6689
  if (page.resourceLoadedSent || !ap.serviceLoaded || !page.renderLoaded) return;
6423
6690
  page.resourceLoadedSent = true;
@@ -6723,8 +6990,7 @@ function handleApiResponse(state, sender, payload) {
6723
6990
  clearTimeout(pending.timer);
6724
6991
  return;
6725
6992
  }
6726
- const senderApp = appByWc(state, sender);
6727
- if (!senderApp || senderApp.appSessionId !== ap.appSessionId) {
6993
+ if (!senderBoundToSession(state, sender, ap)) {
6728
6994
  console.warn("[bridge-router] API_RESPONSE rejected: sender not bound to app session");
6729
6995
  return;
6730
6996
  }
@@ -6829,8 +7095,14 @@ function pageFromMsg(state, ap, msg) {
6829
7095
  }
6830
7096
  function appByWc(state, wc) {
6831
7097
  if (wc.isDestroyed()) return void 0;
6832
- const appSessionId = state.wcIdToAppSessionId.get(wc.id);
7098
+ const appSessionId = state.serviceWcIdToAppSessionId.get(wc.id);
6833
7099
  if (appSessionId) return state.appSessions.get(appSessionId);
7100
+ const hosted = state.simulatorWcIdToAppSessionIds.get(wc.id);
7101
+ if (hosted) {
7102
+ let latest;
7103
+ for (const id of hosted) latest = state.appSessions.get(id) ?? latest;
7104
+ if (latest) return latest;
7105
+ }
6834
7106
  const bridgeId = state.wcIdToBridgeId.get(wc.id);
6835
7107
  if (bridgeId) {
6836
7108
  const page = state.pageSessions.get(bridgeId);
@@ -6838,6 +7110,12 @@ function appByWc(state, wc) {
6838
7110
  }
6839
7111
  return void 0;
6840
7112
  }
7113
+ function senderBoundToSession(state, sender, ap) {
7114
+ if (sender.isDestroyed()) return false;
7115
+ const hosted = state.simulatorWcIdToAppSessionIds.get(sender.id);
7116
+ if (hosted) return hosted.has(ap.appSessionId);
7117
+ return appByWc(state, sender)?.appSessionId === ap.appSessionId;
7118
+ }
6841
7119
  function resolveAppByBridgeId(state, bridgeId) {
6842
7120
  if (state.appSessions.has(bridgeId)) return state.appSessions.get(bridgeId);
6843
7121
  const page = state.pageSessions.get(bridgeId);
@@ -6848,6 +7126,15 @@ function bindWc(map, wc, id) {
6848
7126
  if (wc.isDestroyed()) return;
6849
7127
  map.set(wc.id, id);
6850
7128
  }
7129
+ function bindSimulatorWc(map, wc, id) {
7130
+ if (wc.isDestroyed()) return;
7131
+ let hosted = map.get(wc.id);
7132
+ if (!hosted) {
7133
+ hosted = /* @__PURE__ */ new Set();
7134
+ map.set(wc.id, hosted);
7135
+ }
7136
+ hosted.add(id);
7137
+ }
6851
7138
  function readBridgeId(msg) {
6852
7139
  return typeof msg.body?.bridgeId === "string" ? msg.body.bridgeId : void 0;
6853
7140
  }
@@ -6890,11 +7177,9 @@ async function disposeAppSession(state, appSessionId, opts = {}) {
6890
7177
  state.pageSessions.delete(page.bridgeId);
6891
7178
  }
6892
7179
  ap.pages.clear();
7180
+ ap.listenerBag.dispose();
6893
7181
  if (ap.poolEntryId !== null && state.pool && !opts.serviceAlreadyClosed) {
6894
7182
  state.connections.reset(ap.serviceWc.id);
6895
- if (ap.onServiceClosed && !ap.serviceWindow.isDestroyed()) {
6896
- ap.serviceWindow.removeListener("closed", ap.onServiceClosed);
6897
- }
6898
7183
  if (ap.onServiceBoot && !ap.serviceWindow.isDestroyed()) {
6899
7184
  ap.serviceWindow.webContents.removeListener("did-finish-load", ap.onServiceBoot);
6900
7185
  }
@@ -6906,8 +7191,14 @@ async function disposeAppSession(state, appSessionId, opts = {}) {
6906
7191
  } else if (!opts.serviceAlreadyClosed && !ap.serviceWindow.isDestroyed()) {
6907
7192
  ap.serviceWindow.close();
6908
7193
  }
6909
- state.wcIdToAppSessionId.delete(ap.serviceWc.id);
6910
- state.wcIdToAppSessionId.delete(ap.simulatorWc.id);
7194
+ if (state.serviceWcIdToAppSessionId.get(ap.serviceWc.id) === appSessionId) {
7195
+ state.serviceWcIdToAppSessionId.delete(ap.serviceWc.id);
7196
+ }
7197
+ const hosted = state.simulatorWcIdToAppSessionIds.get(ap.simulatorWc.id);
7198
+ if (hosted) {
7199
+ hosted.delete(appSessionId);
7200
+ if (hosted.size === 0) state.simulatorWcIdToAppSessionIds.delete(ap.simulatorWc.id);
7201
+ }
6911
7202
  if (ap.resourceServer) {
6912
7203
  await ap.resourceServer.close().catch((error) => {
6913
7204
  console.warn("[bridge-router] resource server close failed:", error);
@@ -8625,6 +8916,11 @@ function createNetworkForwarder(bridge) {
8625
8916
  trimQueue();
8626
8917
  return;
8627
8918
  }
8919
+ if (!isFrontendSettled(wc)) {
8920
+ trimQueue();
8921
+ scheduleReadyRetry();
8922
+ return;
8923
+ }
8628
8924
  if (sink === "idle") beginProbing();
8629
8925
  const batch = [];
8630
8926
  let batchChars = 0;
@@ -9225,7 +9521,7 @@ function setupSimulatorCurrentPage(host, options) {
9225
9521
 
9226
9522
  // src/main/services/render-inspect/index.ts
9227
9523
  import { readFileSync } from "node:fs";
9228
- import path17 from "node:path";
9524
+ import path18 from "node:path";
9229
9525
  var DEFAULT_SOURCE_PATH = "dist/render-host/render-inspect.js";
9230
9526
  var HIGHLIGHT_CONFIG = {
9231
9527
  showInfo: true,
@@ -9251,7 +9547,7 @@ function withTimeout(p, ms) {
9251
9547
  });
9252
9548
  }
9253
9549
  function createRenderInspector(options = {}) {
9254
- const loadSource = options.loadSource ?? (() => readFileSync(path17.join(devtoolsPackageRoot, DEFAULT_SOURCE_PATH), "utf8"));
9550
+ const loadSource = options.loadSource ?? (() => readFileSync(path18.join(devtoolsPackageRoot, DEFAULT_SOURCE_PATH), "utf8"));
9255
9551
  let cachedSource = null;
9256
9552
  const injected = /* @__PURE__ */ new Set();
9257
9553
  const selfAttached = /* @__PURE__ */ new Set();
@@ -9414,13 +9710,13 @@ function createRenderInspector(options = {}) {
9414
9710
  import { toDisposable as toDisposable6 } from "@dimina-kit/electron-deck/main";
9415
9711
 
9416
9712
  // src/main/services/simulator-temp-files/store.ts
9417
- function registerTempFile(store, path23, mime, bytes) {
9713
+ function registerTempFile(store, path24, mime, bytes) {
9418
9714
  const buf = Buffer.isBuffer(bytes) ? bytes : Buffer.from(new Uint8Array(bytes));
9419
- store.delete(path23);
9420
- store.set(path23, { bytes: buf, mime });
9715
+ store.delete(path24);
9716
+ store.set(path24, { bytes: buf, mime });
9421
9717
  }
9422
- function revokeTempFile(store, path23) {
9423
- store.delete(path23);
9718
+ function revokeTempFile(store, path24) {
9719
+ store.delete(path24);
9424
9720
  }
9425
9721
  function revokeAllTempFiles(store) {
9426
9722
  store.clear();
@@ -9428,14 +9724,14 @@ function revokeAllTempFiles(store) {
9428
9724
 
9429
9725
  // src/shared/vpath.ts
9430
9726
  import os from "node:os";
9431
- import path18 from "node:path";
9727
+ import path19 from "node:path";
9432
9728
  var DIFILE_PREFIX = "difile://";
9433
9729
  function sandboxBase() {
9434
9730
  const env = typeof process !== "undefined" && process.env && process.env.DIMINA_HOME || "";
9435
9731
  if (env) {
9436
- return path18.join(env, "files");
9732
+ return path19.join(env, "files");
9437
9733
  }
9438
- return path18.join(os.homedir(), ".dimina", "files");
9734
+ return path19.join(os.homedir(), ".dimina", "files");
9439
9735
  }
9440
9736
  function resolveVPath(url) {
9441
9737
  if (typeof url !== "string") return null;
@@ -9469,15 +9765,15 @@ function resolveVPath(url) {
9469
9765
  return { kind, writable };
9470
9766
  }
9471
9767
  const base = sandboxBase();
9472
- const joined = path18.join(base, decoded);
9473
- const normalized = path18.normalize(joined);
9474
- if (normalized !== base && !normalized.startsWith(base + path18.sep)) return null;
9768
+ const joined = path19.join(base, decoded);
9769
+ const normalized = path19.normalize(joined);
9770
+ if (normalized !== base && !normalized.startsWith(base + path19.sep)) return null;
9475
9771
  return { kind, writable, realPath: normalized };
9476
9772
  }
9477
9773
 
9478
9774
  // src/main/services/simulator-temp-files/disk.ts
9479
9775
  import fs9 from "node:fs/promises";
9480
- import path19 from "node:path";
9776
+ import path20 from "node:path";
9481
9777
  var EXT_MIME = {
9482
9778
  ".txt": "text/plain",
9483
9779
  ".log": "text/plain",
@@ -9542,7 +9838,7 @@ function sniffMime(head) {
9542
9838
  return null;
9543
9839
  }
9544
9840
  function extMime(realPath) {
9545
- const ext = path19.extname(realPath).toLowerCase();
9841
+ const ext = path20.extname(realPath).toLowerCase();
9546
9842
  if (!ext) return null;
9547
9843
  return EXT_MIME[ext] ?? null;
9548
9844
  }
@@ -9612,7 +9908,7 @@ async function readDiskFile(realPath, opts) {
9612
9908
  }
9613
9909
  async function writeDiskFile(realPath, bytes) {
9614
9910
  const buf = Buffer.isBuffer(bytes) ? bytes : Buffer.from(new Uint8Array(bytes));
9615
- await fs9.mkdir(path19.dirname(realPath), { recursive: true });
9911
+ await fs9.mkdir(path20.dirname(realPath), { recursive: true });
9616
9912
  await fs9.writeFile(realPath, buf);
9617
9913
  }
9618
9914
  async function readDiskDir(realPath) {
@@ -9754,28 +10050,28 @@ async function handleDifileRequest(ctx, req) {
9754
10050
 
9755
10051
  // src/main/services/simulator-temp-files/fs-channels.ts
9756
10052
  import fs10 from "node:fs/promises";
9757
- import path20 from "node:path";
10053
+ import path21 from "node:path";
9758
10054
  async function enforceSandbox(realPath) {
9759
10055
  if (typeof realPath !== "string" || realPath.length === 0) {
9760
10056
  throw new Error("sandbox: realPath must be a non-empty string");
9761
10057
  }
9762
10058
  const base = sandboxBase();
9763
10059
  const baseReal = await fs10.realpath(base).catch(() => base);
9764
- const normalized = path20.normalize(realPath);
9765
- if (normalized !== base && !normalized.startsWith(base + path20.sep)) {
10060
+ const normalized = path21.normalize(realPath);
10061
+ if (normalized !== base && !normalized.startsWith(base + path21.sep)) {
9766
10062
  throw new Error("sandbox: realPath escapes the user-data base");
9767
10063
  }
9768
10064
  let probe = normalized;
9769
- while (probe !== path20.parse(probe).root) {
10065
+ while (probe !== path21.parse(probe).root) {
9770
10066
  try {
9771
10067
  const resolved = await fs10.realpath(probe);
9772
- if (resolved !== baseReal && !resolved.startsWith(baseReal + path20.sep)) {
10068
+ if (resolved !== baseReal && !resolved.startsWith(baseReal + path21.sep)) {
9773
10069
  throw new Error("sandbox: realPath escapes the user-data base via symlink");
9774
10070
  }
9775
10071
  break;
9776
10072
  } catch (err) {
9777
10073
  if (err.code === "ENOENT") {
9778
- probe = path20.dirname(probe);
10074
+ probe = path21.dirname(probe);
9779
10075
  continue;
9780
10076
  }
9781
10077
  throw err;
@@ -9831,10 +10127,10 @@ function setupSimulatorTempFiles(simSession) {
9831
10127
  const store = /* @__PURE__ */ new Map();
9832
10128
  const pendingWaiters = /* @__PURE__ */ new Map();
9833
10129
  let disposed = false;
9834
- function drainWaiters(path23) {
9835
- const list = pendingWaiters.get(path23);
10130
+ function drainWaiters(path24) {
10131
+ const list = pendingWaiters.get(path24);
9836
10132
  if (!list) return;
9837
- pendingWaiters.delete(path23);
10133
+ pendingWaiters.delete(path24);
9838
10134
  for (const fn of list) fn();
9839
10135
  }
9840
10136
  function drainAllWaiters() {
@@ -9847,15 +10143,15 @@ function setupSimulatorTempFiles(simSession) {
9847
10143
  const registry = new IpcRegistry(simulatorOnlyPolicy);
9848
10144
  registry.on("simulator:temp-file:write", (_event, payload) => {
9849
10145
  if (disposed) return;
9850
- const { path: path23, mime, bytes } = payload;
9851
- registerTempFile(store, path23, mime, bytes);
10146
+ const { path: path24, mime, bytes } = payload;
10147
+ registerTempFile(store, path24, mime, bytes);
9852
10148
  enforceStoreCap(store);
9853
- drainWaiters(path23);
10149
+ drainWaiters(path24);
9854
10150
  });
9855
10151
  registry.on("simulator:temp-file:revoke", (_event, payload) => {
9856
10152
  if (disposed) return;
9857
- const { path: path23 } = payload;
9858
- revokeTempFile(store, path23);
10153
+ const { path: path24 } = payload;
10154
+ revokeTempFile(store, path24);
9859
10155
  });
9860
10156
  registry.on("simulator:temp-file:revoke-all", () => {
9861
10157
  if (disposed) return;
@@ -9993,7 +10289,7 @@ function setupSimulatorSessionPolicy() {
9993
10289
  import http2 from "node:http";
9994
10290
  import nodeFs2 from "node:fs";
9995
10291
  import fs11 from "node:fs/promises";
9996
- import path21 from "node:path";
10292
+ import path22 from "node:path";
9997
10293
  var MAX_FS_WRITE_BYTES = 32 * 1024 * 1024;
9998
10294
  var MIME = {
9999
10295
  ".html": "text/html; charset=utf-8",
@@ -10017,8 +10313,8 @@ function setIsolationHeaders(res) {
10017
10313
  }
10018
10314
  function containedStaticPath(root, pathname) {
10019
10315
  const rel = pathname.replace(/^\/+/, "") || "index.html";
10020
- const resolved = path21.resolve(root, rel);
10021
- if (resolved !== root && !resolved.startsWith(root + path21.sep)) return null;
10316
+ const resolved = path22.resolve(root, rel);
10317
+ if (resolved !== root && !resolved.startsWith(root + path22.sep)) return null;
10022
10318
  return resolved;
10023
10319
  }
10024
10320
  function serveStaticFile(res, root, pathname) {
@@ -10029,7 +10325,7 @@ function serveStaticFile(res, root, pathname) {
10029
10325
  return;
10030
10326
  }
10031
10327
  Promise.all([fs11.realpath(root), fs11.realpath(candidate)]).then(([realRoot, realFile]) => {
10032
- if (realFile !== realRoot && !realFile.startsWith(realRoot + path21.sep)) {
10328
+ if (realFile !== realRoot && !realFile.startsWith(realRoot + path22.sep)) {
10033
10329
  res.writeHead(403);
10034
10330
  res.end("Forbidden");
10035
10331
  return;
@@ -10041,7 +10337,7 @@ function serveStaticFile(res, root, pathname) {
10041
10337
  return;
10042
10338
  }
10043
10339
  res.writeHead(200, {
10044
- "Content-Type": MIME[path21.extname(realFile)] ?? "application/octet-stream",
10340
+ "Content-Type": MIME[path22.extname(realFile)] ?? "application/octet-stream",
10045
10341
  "Content-Length": stat.size,
10046
10342
  "Cache-Control": "no-store"
10047
10343
  });
@@ -10183,7 +10479,7 @@ async function listFilesRecursive(dir, base = "") {
10183
10479
  for (const e of entries) {
10184
10480
  const rel = base ? `${base}/${e.name}` : e.name;
10185
10481
  if (e.isDirectory()) {
10186
- out.push(...await listFilesRecursive(path21.join(dir, e.name), rel));
10482
+ out.push(...await listFilesRecursive(path22.join(dir, e.name), rel));
10187
10483
  } else if (e.isFile()) {
10188
10484
  out.push(rel);
10189
10485
  }
@@ -10200,9 +10496,9 @@ async function readContributedExtensions(extensionsDir) {
10200
10496
  }
10201
10497
  for (const e of entries) {
10202
10498
  if (!e.isDirectory()) continue;
10203
- const extDir = path21.join(extensionsDir, e.name);
10499
+ const extDir = path22.join(extensionsDir, e.name);
10204
10500
  try {
10205
- const pkgRaw = await fs11.readFile(path21.join(extDir, "package.json"), "utf8");
10501
+ const pkgRaw = await fs11.readFile(path22.join(extDir, "package.json"), "utf8");
10206
10502
  const files = await listFilesRecursive(extDir);
10207
10503
  out.push({ dir: e.name, packageJson: JSON.parse(pkgRaw), files });
10208
10504
  } catch {
@@ -10211,8 +10507,8 @@ async function readContributedExtensions(extensionsDir) {
10211
10507
  return out;
10212
10508
  }
10213
10509
  async function startWorkbenchCoiServer(options) {
10214
- const root = path21.resolve(options.rootDir);
10215
- const contribRoot = options.extensionsDir ? path21.resolve(options.extensionsDir) : null;
10510
+ const root = path22.resolve(options.rootDir);
10511
+ const contribRoot = options.extensionsDir ? path22.resolve(options.extensionsDir) : null;
10216
10512
  const server = http2.createServer((req, res) => {
10217
10513
  setIsolationHeaders(res);
10218
10514
  const url = new URL(req.url ?? "/", "http://127.0.0.1");
@@ -10430,7 +10726,7 @@ async function disposeContext(ctx) {
10430
10726
  function createConfiguredMainWindow(config, rendererDir2) {
10431
10727
  const mainWindow = createMainWindow({
10432
10728
  title: config.appName ?? "Dimina DevTools",
10433
- indexHtml: path22.join(rendererDir2, "entries/main/index.html"),
10729
+ indexHtml: path23.join(rendererDir2, "entries/main/index.html"),
10434
10730
  width: config.window?.width,
10435
10731
  height: config.window?.height,
10436
10732
  minWidth: config.window?.minWidth,
@@ -10725,8 +11021,8 @@ async function createDevtoolsRuntime(config = {}) {
10725
11021
  bridge: context.bridge
10726
11022
  }));
10727
11023
  }
10728
- const bundleDir = config.editorViewConfig?.bundleDir ?? path22.join(devtoolsPackageRoot, "dist/vscode-workbench");
10729
- if (!fs12.existsSync(path22.join(bundleDir, "index.html"))) {
11024
+ const bundleDir = config.editorViewConfig?.bundleDir ?? path23.join(devtoolsPackageRoot, "dist/vscode-workbench");
11025
+ if (!fs12.existsSync(path23.join(bundleDir, "index.html"))) {
10730
11026
  console.warn(
10731
11027
  `[workbench] editor bundle not found at ${bundleDir} \u2014 skipping embedded editor assembly`
10732
11028
  );