@dimina-kit/devtools 0.4.0-dev.20260618090552 → 0.4.0-dev.20260624040247

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 (51) hide show
  1. package/README.md +5 -1
  2. package/dist/main/app/app.js +14 -2
  3. package/dist/main/index.bundle.js +347 -99
  4. package/dist/main/ipc/bridge-router.js +13 -8
  5. package/dist/main/ipc/projects.js +1 -1
  6. package/dist/main/ipc/simulator.js +1 -1
  7. package/dist/main/runtime/miniapp-runtime.d.ts +3 -3
  8. package/dist/main/services/elements-forward/index.js +5 -5
  9. package/dist/main/services/network-forward/index.d.ts +5 -6
  10. package/dist/main/services/network-forward/index.js +9 -10
  11. package/dist/main/services/notifications/renderer-notifier.d.ts +3 -4
  12. package/dist/main/services/projects/index.js +1 -1
  13. package/dist/main/services/service-console/console-api.d.ts +5 -6
  14. package/dist/main/services/service-console/console-api.js +5 -6
  15. package/dist/main/services/service-host-pool/pool.d.ts +2 -3
  16. package/dist/main/services/simulator-storage/index.d.ts +2 -4
  17. package/dist/main/services/simulator-storage/index.js +2 -4
  18. package/dist/main/services/simulator-temp-files/index.js +2 -2
  19. package/dist/main/services/views/host-toolbar-session-runtime.d.ts +4 -4
  20. package/dist/main/services/views/host-toolbar-session-runtime.js +4 -4
  21. package/dist/main/services/views/view-manager.d.ts +17 -1
  22. package/dist/main/services/views/view-manager.js +67 -41
  23. package/dist/main/services/workspace/workspace-service.js +11 -0
  24. package/dist/main/windows/service-host-window/create.d.ts +1 -0
  25. package/dist/main/windows/service-host-window/create.js +1 -0
  26. package/dist/preload/runtime/host-toolbar-port.d.ts +1 -1
  27. package/dist/preload/runtime/host-toolbar-port.js +1 -1
  28. package/dist/preload/runtime/host-toolbar-runtime.d.ts +8 -7
  29. package/dist/preload/runtime/host-toolbar-runtime.js +8 -7
  30. package/dist/preload/windows/host-toolbar-runtime.cjs.map +2 -2
  31. package/dist/preload/windows/main.cjs.map +2 -2
  32. package/dist/preload/windows/simulator.cjs +1 -1
  33. package/dist/preload/windows/simulator.cjs.map +1 -1
  34. package/dist/preload/windows/simulator.js +1 -1
  35. package/dist/renderer/assets/index-PtsdWlGu.js +50 -0
  36. package/dist/renderer/entries/main/index.html +1 -1
  37. package/dist/shared/appdata-accumulator.d.ts +2 -2
  38. package/dist/shared/appdata-accumulator.js +3 -3
  39. package/dist/shared/constants.d.ts +1 -1
  40. package/dist/shared/constants.js +1 -1
  41. package/dist/shared/ipc-channels.d.ts +3 -3
  42. package/dist/shared/ipc-channels.js +3 -3
  43. package/dist/shared/open-in-editor.d.ts +20 -1
  44. package/dist/shared/open-in-editor.js +246 -3
  45. package/dist/shared/vpath.d.ts +2 -2
  46. package/dist/shared/vpath.js +2 -2
  47. package/dist/simulator/assets/{device-shell-BWX7Yopg.js → device-shell-TtcSXwhb.js} +2 -2
  48. package/dist/simulator/assets/{simulator-sf-D0mhw.js → simulator-CtyXt_4V.js} +3 -3
  49. package/dist/simulator/simulator.html +1 -1
  50. package/package.json +8 -6
  51. package/dist/renderer/assets/index-DmgWoK8N.js +0 -50
@@ -410,11 +410,11 @@ var ProjectsChannel = {
410
410
  List: "projects:list",
411
411
  Add: "projects:add",
412
412
  Remove: "projects:remove",
413
- /** Phase 3: merged + sanitized template catalog for the create-project dialog. */
413
+ /** Merged + sanitized template catalog for the create-project dialog. */
414
414
  ListTemplates: "projects:listTemplates",
415
- /** Phase 3: host-supplied create-project dialog hook (returns input or null). */
415
+ /** Host-supplied create-project dialog hook (returns input or null). */
416
416
  OpenCreateDialog: "projects:openCreateDialog",
417
- /** Phase 3: server-side scaffold + register (delegates to create-project-service). */
417
+ /** Server-side scaffold + register (delegates to create-project-service). */
418
418
  Create: "projects:create",
419
419
  /** Default values used to pre-fill the create-project dialog (baseDir). */
420
420
  GetCreateDefaults: "projects:getCreateDefaults"
@@ -762,7 +762,7 @@ function registerProjectFsIpc(ctx) {
762
762
 
763
763
  // src/main/app/app.ts
764
764
  import { app as app15, BrowserWindow as BrowserWindow8, nativeImage, session as session4 } from "electron";
765
- import fs10 from "fs";
765
+ import fs11 from "fs";
766
766
  import path21 from "path";
767
767
 
768
768
  // src/main/utils/paths.ts
@@ -1052,6 +1052,7 @@ function createRendererNotifier(ctx) {
1052
1052
 
1053
1053
  // src/main/services/views/view-manager.ts
1054
1054
  import { ipcMain as ipcMain2, nativeTheme as nativeTheme3, shell as shell2, WebContentsView as WebContentsView2, webContents } from "electron";
1055
+ import fs4 from "node:fs";
1055
1056
  import path7 from "path";
1056
1057
 
1057
1058
  // src/shared/open-in-editor.ts
@@ -1078,8 +1079,11 @@ function decodeOpenInEditorUrl(raw) {
1078
1079
  }
1079
1080
  return out;
1080
1081
  }
1081
- function resourceUrlToProjectRelativePath(resourceUrl, expectedOrigin) {
1082
+ function resourceUrlToProjectRelativePath(resourceUrl, expectedOriginOrContext) {
1082
1083
  if (typeof resourceUrl !== "string" || !resourceUrl) return null;
1084
+ if (expectedOriginOrContext && typeof expectedOriginOrContext === "object") {
1085
+ return projectAwareResourcePath(resourceUrl, expectedOriginOrContext);
1086
+ }
1083
1087
  let resource;
1084
1088
  try {
1085
1089
  resource = new URL(resourceUrl);
@@ -1087,10 +1091,10 @@ function resourceUrlToProjectRelativePath(resourceUrl, expectedOrigin) {
1087
1091
  return null;
1088
1092
  }
1089
1093
  if (resource.protocol !== "http:" && resource.protocol !== "https:") return null;
1090
- if (expectedOrigin) {
1094
+ if (expectedOriginOrContext) {
1091
1095
  let base;
1092
1096
  try {
1093
- base = new URL(expectedOrigin);
1097
+ base = new URL(expectedOriginOrContext);
1094
1098
  } catch {
1095
1099
  return null;
1096
1100
  }
@@ -1106,6 +1110,218 @@ function resourceUrlToProjectRelativePath(resourceUrl, expectedOrigin) {
1106
1110
  if (segments.length < 2) return null;
1107
1111
  return segments.slice(1).join("/");
1108
1112
  }
1113
+ function decodePathname(pathname) {
1114
+ return pathname.split("/").map((segment) => {
1115
+ try {
1116
+ return decodeURIComponent(segment);
1117
+ } catch {
1118
+ return segment;
1119
+ }
1120
+ }).join("/");
1121
+ }
1122
+ function normalizeSlashPath(value) {
1123
+ return decodePathname(value.replace(/\\/g, "/")).replace(/\/+/g, "/");
1124
+ }
1125
+ function safeRelativePath(segments) {
1126
+ if (segments.length === 0 || segments.some((segment) => !segment || segment === "." || segment === "..")) {
1127
+ return null;
1128
+ }
1129
+ return segments.join("/");
1130
+ }
1131
+ function projectAwareResourcePath(resourceUrl, context) {
1132
+ let base;
1133
+ try {
1134
+ base = new URL(context.resourceBaseUrl);
1135
+ } catch {
1136
+ return null;
1137
+ }
1138
+ const projectRoot = normalizeSlashPath(context.projectRoot).replace(/\/$/, "");
1139
+ const raw = resourceUrl.trim();
1140
+ if (!raw) return null;
1141
+ const normalizedRaw = normalizeSlashPath(raw);
1142
+ if (projectRoot && (normalizedRaw === projectRoot || normalizedRaw.startsWith(`${projectRoot}/`))) {
1143
+ const relative = normalizedRaw.slice(projectRoot.length).replace(/^\/+/, "");
1144
+ return safeRelativePath(relative.split("/").filter(Boolean));
1145
+ }
1146
+ if (/^\/(?:Volumes|Users|home|private|tmp|var|opt|Applications)\//.test(normalizedRaw)) {
1147
+ return null;
1148
+ }
1149
+ if (!raw.startsWith("/") && !/^[a-z][a-z0-9+.-]*:/i.test(raw)) return null;
1150
+ let resource;
1151
+ try {
1152
+ resource = raw.startsWith("/") && !raw.startsWith("//") ? new URL(raw, base.origin) : new URL(raw);
1153
+ } catch {
1154
+ return null;
1155
+ }
1156
+ if (resource.protocol === "file:") {
1157
+ const filePath = normalizeSlashPath(resource.pathname);
1158
+ if (!projectRoot || filePath !== projectRoot && !filePath.startsWith(`${projectRoot}/`)) return null;
1159
+ return safeRelativePath(
1160
+ filePath.slice(projectRoot.length).replace(/^\/+/, "").split("/").filter(Boolean)
1161
+ );
1162
+ }
1163
+ if (resource.protocol !== "http:" && resource.protocol !== "https:") return null;
1164
+ if (resource.origin !== base.origin) return null;
1165
+ let segments = decodePathname(resource.pathname).split("/").filter(Boolean);
1166
+ if (context.appId && segments[0] === context.appId) {
1167
+ segments = segments.slice(1);
1168
+ if (context.outputRoot && segments[0] === context.outputRoot) segments = segments.slice(1);
1169
+ }
1170
+ return safeRelativePath(segments);
1171
+ }
1172
+ function projectSourceContextFromServiceHostUrl(serviceHostUrl, activeProjectRoot) {
1173
+ let url;
1174
+ try {
1175
+ url = new URL(serviceHostUrl);
1176
+ } catch {
1177
+ return null;
1178
+ }
1179
+ const projectRoot = url.searchParams.get("pkgRoot") || "";
1180
+ const resourceBaseUrl = url.searchParams.get("resourceBaseUrl") ?? "";
1181
+ const appId = url.searchParams.get("appId") ?? "";
1182
+ const outputRoot = url.searchParams.get("root") ?? "main";
1183
+ if (!projectRoot || !resourceBaseUrl || !appId) return null;
1184
+ if (activeProjectRoot) {
1185
+ const authoritativeRoot = normalizeSlashPath(projectRoot).replace(/\/+$/, "");
1186
+ const currentRoot = normalizeSlashPath(activeProjectRoot).replace(/\/+$/, "");
1187
+ if (!authoritativeRoot || authoritativeRoot !== currentRoot) return null;
1188
+ }
1189
+ try {
1190
+ new URL(resourceBaseUrl);
1191
+ } catch {
1192
+ return null;
1193
+ }
1194
+ return { projectRoot, resourceBaseUrl, appId, outputRoot };
1195
+ }
1196
+ function buildDevtoolsProjectSourceLinksScript(context) {
1197
+ const config = JSON.stringify(context);
1198
+ const scheme = JSON.stringify(OPEN_IN_EDITOR_SCHEME);
1199
+ return `
1200
+ (function() {
1201
+ try {
1202
+ const stateKey = '__diminaProjectSourceLinksState__'
1203
+ const previousState = globalThis[stateKey]
1204
+ if (previousState && typeof previousState.dispose === 'function') {
1205
+ try { previousState.dispose() } catch (_) {}
1206
+ }
1207
+
1208
+ const cfg = ${config}
1209
+ const diminaProjectSourcePath = ${projectAwareResourcePath.toString()}
1210
+ const decodePathname = ${decodePathname.toString()}
1211
+ const normalizeSlashPath = ${normalizeSlashPath.toString()}
1212
+ const safeRelativePath = ${safeRelativePath.toString()}
1213
+ const observedRoots = new Set()
1214
+ const observers = []
1215
+ let timer = null
1216
+ const state = {
1217
+ dispose() {
1218
+ if (timer !== null) {
1219
+ clearInterval(timer)
1220
+ timer = null
1221
+ }
1222
+ for (const observer of observers.splice(0)) {
1223
+ try { observer.disconnect() } catch (_) {}
1224
+ }
1225
+ observedRoots.clear()
1226
+ },
1227
+ }
1228
+ globalThis[stateKey] = state
1229
+
1230
+ function splitLocation(value) {
1231
+ const raw = String(value || '')
1232
+ const match = raw.match(/^(.*?)(?::(\\d+))(?::(\\d+))?$/)
1233
+ return match
1234
+ ? { url: match[1], suffix: ':' + match[2] + (match[3] ? ':' + match[3] : '') }
1235
+ : { url: raw, suffix: '' }
1236
+ }
1237
+
1238
+ function rewriteLink(link) {
1239
+ if (!link || link.dataset.diminaProjectSourcePath) return
1240
+ const candidates = [
1241
+ link.getAttribute && link.getAttribute('title'),
1242
+ link.getAttribute && link.getAttribute('href'),
1243
+ link.getAttribute && link.getAttribute('data-url'),
1244
+ ]
1245
+ for (const candidate of candidates) {
1246
+ if (!candidate) continue
1247
+ const location = splitLocation(candidate)
1248
+ const relative = diminaProjectSourcePath(location.url, cfg)
1249
+ if (!relative) continue
1250
+ const textSuffix = location.suffix || ((String(link.textContent || '').match(/(:\\d+(?::\\d+)?)$/) || [])[1] || '')
1251
+ link.textContent = relative + textSuffix
1252
+ link.dataset.diminaProjectSourcePath = relative
1253
+ return
1254
+ }
1255
+ }
1256
+
1257
+ function observeRoot(root) {
1258
+ if (!root || !root.querySelectorAll || observedRoots.has(root)) return
1259
+ observedRoots.add(root)
1260
+
1261
+ const observer = new MutationObserver((records) => {
1262
+ for (const record of records) {
1263
+ if (record.type === 'attributes') rewriteLink(record.target)
1264
+ for (const node of record.addedNodes || []) visitNode(node)
1265
+ }
1266
+ })
1267
+ observer.observe(root, {
1268
+ subtree: true,
1269
+ childList: true,
1270
+ attributes: true,
1271
+ attributeFilter: ['title', 'href', 'data-url'],
1272
+ })
1273
+ observers.push(observer)
1274
+
1275
+ for (const link of root.querySelectorAll('.devtools-link')) rewriteLink(link)
1276
+ for (const element of root.querySelectorAll('*')) {
1277
+ if (element.shadowRoot) observeRoot(element.shadowRoot)
1278
+ }
1279
+ }
1280
+
1281
+ function visitNode(node) {
1282
+ if (!node) return
1283
+ if (node.matches && node.matches('.devtools-link')) rewriteLink(node)
1284
+ if (node.shadowRoot) observeRoot(node.shadowRoot)
1285
+ if (!node.querySelectorAll) return
1286
+ for (const link of node.querySelectorAll('.devtools-link')) rewriteLink(link)
1287
+ for (const element of node.querySelectorAll('*')) {
1288
+ if (element.shadowRoot) observeRoot(element.shadowRoot)
1289
+ }
1290
+ }
1291
+
1292
+ observeRoot(document)
1293
+
1294
+ let tries = 0
1295
+ timer = setInterval(() => {
1296
+ tries++
1297
+ try {
1298
+ const Host = globalThis.Host
1299
+ const host = Host && Host.InspectorFrontendHost
1300
+ if (host && typeof host.setOpenResourceHandler === 'function'
1301
+ && typeof host.openInNewTab === 'function') {
1302
+ host.setOpenResourceHandler((url, lineNumber, columnNumber) => {
1303
+ try {
1304
+ if (!diminaProjectSourcePath(String(url), cfg)) return
1305
+ const p = new URLSearchParams()
1306
+ p.set('u', String(url))
1307
+ if (typeof lineNumber === 'number') p.set('l', String(lineNumber))
1308
+ if (typeof columnNumber === 'number') p.set('c', String(columnNumber))
1309
+ host.openInNewTab(${scheme} + ':?' + p.toString())
1310
+ } catch (_) {}
1311
+ })
1312
+ clearInterval(timer)
1313
+ timer = null
1314
+ }
1315
+ } catch (_) {}
1316
+ if (tries > 80 && timer !== null) {
1317
+ clearInterval(timer)
1318
+ timer = null
1319
+ }
1320
+ }, 50)
1321
+ } catch (_) {}
1322
+ })()
1323
+ `;
1324
+ }
1109
1325
 
1110
1326
  // src/main/services/safe-area/index.ts
1111
1327
  function guestInsets(device, isTabPage2) {
@@ -2022,12 +2238,36 @@ function parseRoute(url) {
2022
2238
  }
2023
2239
 
2024
2240
  // src/main/services/views/view-manager.ts
2241
+ function resolveProjectEditorTarget(serviceHostUrl, activeProjectRoot, req, isFile = (absolutePath) => fs4.statSync(absolutePath).isFile()) {
2242
+ const sourceContext = projectSourceContextFromServiceHostUrl(
2243
+ serviceHostUrl,
2244
+ activeProjectRoot
2245
+ );
2246
+ if (!sourceContext) return null;
2247
+ const rel = resourceUrlToProjectRelativePath(req.url, sourceContext);
2248
+ if (!rel) return null;
2249
+ const absolute = path7.resolve(sourceContext.projectRoot, ...rel.split("/"));
2250
+ const fromRoot = path7.relative(path7.resolve(sourceContext.projectRoot), absolute);
2251
+ if (!fromRoot || fromRoot.startsWith("..") || path7.isAbsolute(fromRoot)) return null;
2252
+ try {
2253
+ if (!isFile(absolute)) return null;
2254
+ } catch {
2255
+ return null;
2256
+ }
2257
+ return {
2258
+ path: rel,
2259
+ line: typeof req.line === "number" ? req.line + 1 : void 0,
2260
+ column: typeof req.column === "number" ? req.column + 1 : void 0
2261
+ };
2262
+ }
2025
2263
  function createViewManager(ctx) {
2026
2264
  const safeArea = createSafeAreaController({ connections: ctx.connections });
2027
2265
  let simulatorView = null;
2028
2266
  let simulatorViewAdded = false;
2029
2267
  let nativeSimulatorView = null;
2030
2268
  let nativeSimulatorViewAdded = false;
2269
+ let nativeSimulatorProjectPath = null;
2270
+ let settleNativeSimulatorReady = null;
2031
2271
  let lastRendererRect = null;
2032
2272
  let nativeCustomApiBridgeHandler = null;
2033
2273
  let currentZoomFactor = 1;
@@ -2260,37 +2500,13 @@ function createViewManager(ctx) {
2260
2500
  closeNativeDevtoolsSource();
2261
2501
  }
2262
2502
  const openInEditorWiredWcIds = /* @__PURE__ */ new Set();
2263
- function injectOpenResourceHandler(devtoolsWc) {
2264
- const scheme = JSON.stringify(OPEN_IN_EDITOR_SCHEME);
2265
- devtoolsWc.executeJavaScript(`
2266
- (function() {
2267
- try {
2268
- let tries = 0
2269
- const timer = setInterval(() => {
2270
- tries++
2271
- try {
2272
- const Host = globalThis.Host
2273
- const host = Host && Host.InspectorFrontendHost
2274
- if (host && typeof host.setOpenResourceHandler === 'function'
2275
- && typeof host.openInNewTab === 'function') {
2276
- host.setOpenResourceHandler((url, lineNumber, columnNumber) => {
2277
- try {
2278
- const p = new URLSearchParams()
2279
- p.set('u', String(url))
2280
- if (typeof lineNumber === 'number') p.set('l', String(lineNumber))
2281
- if (typeof columnNumber === 'number') p.set('c', String(columnNumber))
2282
- host.openInNewTab(${scheme} + ':?' + p.toString())
2283
- } catch (_) {}
2284
- })
2285
- clearInterval(timer)
2286
- return
2287
- }
2288
- } catch (_) {}
2289
- if (tries > 80) clearInterval(timer)
2290
- }, 50)
2291
- } catch (_) {}
2292
- })()
2293
- `).catch(() => {
2503
+ function injectOpenResourceHandler(serviceWc, devtoolsWc) {
2504
+ const sourceContext = projectSourceContextFromServiceHostUrl(
2505
+ serviceWc.getURL(),
2506
+ ctx.workspace?.getProjectPath?.()
2507
+ );
2508
+ if (!sourceContext) return;
2509
+ devtoolsWc.executeJavaScript(buildDevtoolsProjectSourceLinksScript(sourceContext)).catch(() => {
2294
2510
  });
2295
2511
  }
2296
2512
  function customizeDevtoolsTabs(devtoolsWc) {
@@ -2315,9 +2531,9 @@ function createViewManager(ctx) {
2315
2531
  function wireOpenInEditor(serviceWc, devtoolsWc) {
2316
2532
  if (!devtoolsWc.isDestroyed()) {
2317
2533
  if (devtoolsWc.isLoading()) {
2318
- devtoolsWc.once("dom-ready", () => injectOpenResourceHandler(devtoolsWc));
2534
+ devtoolsWc.once("dom-ready", () => injectOpenResourceHandler(serviceWc, devtoolsWc));
2319
2535
  } else {
2320
- injectOpenResourceHandler(devtoolsWc);
2536
+ injectOpenResourceHandler(serviceWc, devtoolsWc);
2321
2537
  }
2322
2538
  }
2323
2539
  if (openInEditorWiredWcIds.has(serviceWc.id)) return;
@@ -2325,11 +2541,13 @@ function createViewManager(ctx) {
2325
2541
  const onOpenUrl = (_event, url) => {
2326
2542
  const req = decodeOpenInEditorUrl(url);
2327
2543
  if (!req) return;
2328
- const rel = resourceUrlToProjectRelativePath(req.url);
2329
- if (!rel) return;
2330
- const line = typeof req.line === "number" ? req.line + 1 : void 0;
2331
- const column = typeof req.column === "number" ? req.column + 1 : void 0;
2332
- ctx.notify.editorOpenFile({ path: rel, line, column });
2544
+ const target = resolveProjectEditorTarget(
2545
+ serviceWc.getURL(),
2546
+ ctx.workspace?.getProjectPath?.(),
2547
+ req
2548
+ );
2549
+ if (!target) return;
2550
+ ctx.notify.editorOpenFile(target);
2333
2551
  };
2334
2552
  serviceWc.on("devtools-open-url", onOpenUrl);
2335
2553
  const conn = ctx.connections.acquire(serviceWc);
@@ -2489,8 +2707,10 @@ function createViewManager(ctx) {
2489
2707
  function attachNativeSimulator(simulatorUrl, _simWidth) {
2490
2708
  if (!ctx.preloadPath) {
2491
2709
  console.error("[workbench] attachNativeSimulator \u2014 preloadPath unset; cannot mount native simulator");
2492
- return;
2710
+ return Promise.resolve();
2493
2711
  }
2712
+ settleNativeSimulatorReady?.();
2713
+ settleNativeSimulatorReady = null;
2494
2714
  if (nativeSimulatorView) {
2495
2715
  detachNativeCustomApiBridge();
2496
2716
  if (nativeSimulatorViewAdded) {
@@ -2508,6 +2728,10 @@ function createViewManager(ctx) {
2508
2728
  }
2509
2729
  nativeSimulatorView = null;
2510
2730
  }
2731
+ nativeSimulatorProjectPath = null;
2732
+ const ready = new Promise((resolve) => {
2733
+ settleNativeSimulatorReady = resolve;
2734
+ });
2511
2735
  const route = parseRoute(simulatorUrl);
2512
2736
  const partition = miniappPartition(route?.appId);
2513
2737
  configureMiniappSession(partition);
@@ -2522,6 +2746,7 @@ function createViewManager(ctx) {
2522
2746
  }
2523
2747
  });
2524
2748
  nativeSimulatorView = view;
2749
+ nativeSimulatorProjectPath = ctx.workspace?.getProjectPath() || null;
2525
2750
  view.setBackgroundColor(simDeskBg());
2526
2751
  const simWc = view.webContents;
2527
2752
  const syncDeskBg = () => {
@@ -2571,6 +2796,11 @@ function createViewManager(ctx) {
2571
2796
  e.preventDefault();
2572
2797
  }
2573
2798
  });
2799
+ guestWc.once("did-finish-load", () => {
2800
+ if (nativeSimulatorView !== view || simWc.isDestroyed()) return;
2801
+ settleNativeSimulatorReady?.();
2802
+ settleNativeSimulatorReady = null;
2803
+ });
2574
2804
  followNativeDevtoolsServiceHost();
2575
2805
  });
2576
2806
  simWc.setWindowOpenHandler(({ url }) => handleWindowOpenExternal(url));
@@ -2599,8 +2829,11 @@ function createViewManager(ctx) {
2599
2829
  setNativeSimulatorViewBounds(lastRendererRect);
2600
2830
  }
2601
2831
  attachNativeSimulatorDevtoolsHost();
2832
+ return ready;
2602
2833
  }
2603
2834
  function detachSimulator() {
2835
+ settleNativeSimulatorReady?.();
2836
+ settleNativeSimulatorReady = null;
2604
2837
  stopFollowingNativeServiceHost();
2605
2838
  detachNativeCustomApiBridge();
2606
2839
  try {
@@ -2634,6 +2867,7 @@ function createViewManager(ctx) {
2634
2867
  simulatorView = null;
2635
2868
  simulatorViewAdded = false;
2636
2869
  simulatorWebContentsId = null;
2870
+ nativeSimulatorProjectPath = null;
2637
2871
  simulatorBoundsOverride = null;
2638
2872
  lastRendererRect = null;
2639
2873
  }
@@ -2785,6 +3019,7 @@ function createViewManager(ctx) {
2785
3019
  const wc = webContents.fromId(simulatorWebContentsId);
2786
3020
  return wc && !wc.isDestroyed() ? wc : null;
2787
3021
  },
3022
+ getSimulatorProjectPath: () => nativeSimulatorProjectPath,
2788
3023
  getSettingsWebContents: () => {
2789
3024
  if (!settingsView) return null;
2790
3025
  if (settingsView.webContents.isDestroyed()) return null;
@@ -2909,20 +3144,20 @@ import { z as z2 } from "zod";
2909
3144
  // src/main/services/projects/project-repository.ts
2910
3145
  import { app as app6 } from "electron";
2911
3146
  import path9 from "path";
2912
- import fs4 from "fs";
3147
+ import fs5 from "fs";
2913
3148
  var log2 = createLogger("projects");
2914
3149
  function getProjectsFile() {
2915
3150
  return path9.join(app6.getPath("userData"), "dimina-projects.json");
2916
3151
  }
2917
3152
  function load() {
2918
3153
  try {
2919
- return JSON.parse(fs4.readFileSync(getProjectsFile(), "utf-8"));
3154
+ return JSON.parse(fs5.readFileSync(getProjectsFile(), "utf-8"));
2920
3155
  } catch {
2921
3156
  return [];
2922
3157
  }
2923
3158
  }
2924
3159
  function save(projects) {
2925
- fs4.writeFileSync(getProjectsFile(), JSON.stringify(projects, null, 2));
3160
+ fs5.writeFileSync(getProjectsFile(), JSON.stringify(projects, null, 2));
2926
3161
  }
2927
3162
  function listProjects() {
2928
3163
  return load();
@@ -2931,14 +3166,14 @@ function validateProjectDir(dirPath) {
2931
3166
  if (!dirPath) {
2932
3167
  return "\u5C0F\u7A0B\u5E8F\u76EE\u5F55\u8DEF\u5F84\u4E3A\u7A7A\uFF0C\u8BF7\u9009\u62E9\u5305\u542B\u5C0F\u7A0B\u5E8F\u6E90\u7801\u7684\u76EE\u5F55";
2933
3168
  }
2934
- if (!fs4.existsSync(dirPath)) {
3169
+ if (!fs5.existsSync(dirPath)) {
2935
3170
  return `\u5C0F\u7A0B\u5E8F\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${dirPath}`;
2936
3171
  }
2937
- if (!fs4.existsSync(path9.join(dirPath, "app.json"))) {
3172
+ if (!fs5.existsSync(path9.join(dirPath, "app.json"))) {
2938
3173
  const configPath = path9.join(dirPath, "project.config.json");
2939
- if (fs4.existsSync(configPath)) {
3174
+ if (fs5.existsSync(configPath)) {
2940
3175
  try {
2941
- const cfg = JSON.parse(fs4.readFileSync(configPath, "utf-8"));
3176
+ const cfg = JSON.parse(fs5.readFileSync(configPath, "utf-8"));
2942
3177
  if (cfg.miniprogramRoot) {
2943
3178
  const resolvedRoot = path9.resolve(dirPath, cfg.miniprogramRoot);
2944
3179
  return `\u8BE5\u76EE\u5F55\u7F3A\u5C11 app.json\uFF0Cproject.config.json \u4E2D\u6307\u5B9A\u4E86 miniprogramRoot: "${cfg.miniprogramRoot}"\uFF0C\u8BF7\u5BFC\u5165 ${resolvedRoot}`;
@@ -2956,8 +3191,8 @@ function addProject(dirPath) {
2956
3191
  let name = path9.basename(dirPath);
2957
3192
  try {
2958
3193
  const configPath = path9.join(dirPath, "project.config.json");
2959
- if (fs4.existsSync(configPath)) {
2960
- const cfg = JSON.parse(fs4.readFileSync(configPath, "utf-8"));
3194
+ if (fs5.existsSync(configPath)) {
3195
+ const cfg = JSON.parse(fs5.readFileSync(configPath, "utf-8"));
2961
3196
  if (cfg.projectname) name = cfg.projectname;
2962
3197
  }
2963
3198
  } catch (err) {
@@ -2997,7 +3232,7 @@ function getProjectPages(dirPath) {
2997
3232
  const appJsonPath = path9.join(dirPath, "app.json");
2998
3233
  try {
2999
3234
  const appJson = JSON.parse(
3000
- fs4.readFileSync(appJsonPath, "utf-8")
3235
+ fs5.readFileSync(appJsonPath, "utf-8")
3001
3236
  );
3002
3237
  return {
3003
3238
  pages: appJson.pages || [],
@@ -3021,7 +3256,7 @@ function getProjectSettings(projectPath) {
3021
3256
  }
3022
3257
  try {
3023
3258
  const configPath = path9.join(projectPath, "project.config.json");
3024
- const config = JSON.parse(fs4.readFileSync(configPath, "utf-8"));
3259
+ const config = JSON.parse(fs5.readFileSync(configPath, "utf-8"));
3025
3260
  return {
3026
3261
  uploadWithSourceMap: !!config.setting?.uploadWithSourceMap
3027
3262
  };
@@ -3034,14 +3269,14 @@ function updateProjectSettings(projectPath, patch) {
3034
3269
  const configPath = path9.join(projectPath, "project.config.json");
3035
3270
  let config = {};
3036
3271
  try {
3037
- config = JSON.parse(fs4.readFileSync(configPath, "utf-8"));
3272
+ config = JSON.parse(fs5.readFileSync(configPath, "utf-8"));
3038
3273
  } catch {
3039
3274
  }
3040
3275
  const nextSetting = {
3041
3276
  ...config.setting ?? {},
3042
3277
  ...patch.uploadWithSourceMap === void 0 ? {} : { uploadWithSourceMap: patch.uploadWithSourceMap }
3043
3278
  };
3044
- fs4.writeFileSync(
3279
+ fs5.writeFileSync(
3045
3280
  configPath,
3046
3281
  JSON.stringify({ ...config, setting: nextSetting }, null, 2)
3047
3282
  );
@@ -3216,10 +3451,16 @@ function createWorkspaceService(ctx) {
3216
3451
  getLastClosedProjectPath: () => lastClosedProjectPath,
3217
3452
  hasActiveSession: () => currentSession !== null,
3218
3453
  async captureThumbnail(projectPath) {
3454
+ if (!currentSession || projectPath !== currentProjectPath) return null;
3219
3455
  const wc = ctx.views.getSimulatorWebContents();
3220
3456
  if (!wc) return null;
3457
+ if (ctx.views.getSimulatorProjectPath() !== projectPath) return null;
3458
+ const session5 = currentSession;
3221
3459
  try {
3222
3460
  const image = await wc.capturePage();
3461
+ if (currentSession !== session5 || currentProjectPath !== projectPath || ctx.views.getSimulatorWebContents() !== wc || ctx.views.getSimulatorProjectPath() !== projectPath) {
3462
+ return null;
3463
+ }
3223
3464
  const dataUrl = `data:image/png;base64,${image.toPNG().toString("base64")}`;
3224
3465
  if (provider.saveThumbnail) {
3225
3466
  await provider.saveThumbnail(projectPath, dataUrl);
@@ -3253,7 +3494,7 @@ function createWorkspaceService(ctx) {
3253
3494
 
3254
3495
  // src/main/services/projects/thumbnail.ts
3255
3496
  import { createHash } from "crypto";
3256
- import fs5 from "fs";
3497
+ import fs6 from "fs";
3257
3498
  import path10 from "path";
3258
3499
  import { app as app7 } from "electron";
3259
3500
  function getThumbnailDir() {
@@ -3269,13 +3510,13 @@ var DATA_URL_PNG_PREFIX = "data:image/png;base64,";
3269
3510
  function saveThumbnailFromDataUrl(projectPath, imageDataUrl) {
3270
3511
  if (!imageDataUrl.startsWith(DATA_URL_PNG_PREFIX)) return;
3271
3512
  const png = Buffer.from(imageDataUrl.slice(DATA_URL_PNG_PREFIX.length), "base64");
3272
- fs5.mkdirSync(getThumbnailDir(), { recursive: true });
3273
- fs5.writeFileSync(getThumbnailPath(projectPath), png);
3513
+ fs6.mkdirSync(getThumbnailDir(), { recursive: true });
3514
+ fs6.writeFileSync(getThumbnailPath(projectPath), png);
3274
3515
  }
3275
3516
  function loadThumbnail(projectPath) {
3276
3517
  const filePath = getThumbnailPath(projectPath);
3277
3518
  try {
3278
- const buf = fs5.readFileSync(filePath);
3519
+ const buf = fs6.readFileSync(filePath);
3279
3520
  return `${DATA_URL_PNG_PREFIX}${buf.toString("base64")}`;
3280
3521
  } catch {
3281
3522
  return null;
@@ -3635,7 +3876,7 @@ function deviceInfoToHostEnv(d) {
3635
3876
  function registerSimulatorIpc(ctx) {
3636
3877
  return new IpcRegistry(ctx.senderPolicy).handle(SimulatorChannel.AttachNative, (_, ...args) => {
3637
3878
  const [simulatorUrl, simWidth] = validate(SimulatorChannel.AttachNative, SimulatorAttachNativeSchema, args);
3638
- ctx.views.attachNativeSimulator(simulatorUrl, simWidth);
3879
+ return ctx.views.attachNativeSimulator(simulatorUrl, simWidth);
3639
3880
  }).handle(SimulatorChannel.Detach, () => {
3640
3881
  ctx.views.detachSimulator();
3641
3882
  }).handle(SimulatorChannel.SetNativeBounds, (_, ...args) => {
@@ -3677,7 +3918,7 @@ import { app as app8, dialog } from "electron";
3677
3918
  import path13 from "path";
3678
3919
 
3679
3920
  // src/main/services/projects/create-project-service.ts
3680
- import fs6 from "node:fs";
3921
+ import fs7 from "node:fs";
3681
3922
  import path12 from "node:path";
3682
3923
  var DEFAULT_TEMPLATE_ID = "blank";
3683
3924
  async function createProject(input, ctx) {
@@ -3689,12 +3930,12 @@ async function createProject(input, ctx) {
3689
3930
  if (!target || typeof target !== "string") {
3690
3931
  throw new Error("Project path is required");
3691
3932
  }
3692
- if (fs6.existsSync(target)) {
3693
- const stat = fs6.statSync(target);
3933
+ if (fs7.existsSync(target)) {
3934
+ const stat = fs7.statSync(target);
3694
3935
  if (!stat.isDirectory()) {
3695
3936
  throw new Error(`Project path exists and is not a directory: ${target}`);
3696
3937
  }
3697
- const entries = fs6.readdirSync(target);
3938
+ const entries = fs7.readdirSync(target);
3698
3939
  if (entries.length > 0) {
3699
3940
  throw new Error(
3700
3941
  `Project path is not empty (refusing to overwrite): ${target}`
@@ -3711,31 +3952,31 @@ async function createProject(input, ctx) {
3711
3952
  `Template '${templateId}' has neither a source directory nor a generate function`
3712
3953
  );
3713
3954
  }
3714
- fs6.mkdirSync(target, { recursive: true });
3955
+ fs7.mkdirSync(target, { recursive: true });
3715
3956
  if (template.generate) {
3716
3957
  await template.generate(target, { name });
3717
3958
  } else if (template.source) {
3718
- if (!fs6.existsSync(template.source.path)) {
3959
+ if (!fs7.existsSync(template.source.path)) {
3719
3960
  throw new Error(
3720
3961
  `Template source missing on disk: ${template.source.path}`
3721
3962
  );
3722
3963
  }
3723
- fs6.cpSync(template.source.path, target, {
3964
+ fs7.cpSync(template.source.path, target, {
3724
3965
  recursive: true,
3725
3966
  force: true
3726
3967
  });
3727
3968
  }
3728
3969
  const cfgPath = path12.join(target, "project.config.json");
3729
3970
  let cfg = {};
3730
- if (fs6.existsSync(cfgPath)) {
3971
+ if (fs7.existsSync(cfgPath)) {
3731
3972
  try {
3732
- cfg = JSON.parse(fs6.readFileSync(cfgPath, "utf-8"));
3973
+ cfg = JSON.parse(fs7.readFileSync(cfgPath, "utf-8"));
3733
3974
  } catch {
3734
3975
  cfg = {};
3735
3976
  }
3736
3977
  }
3737
3978
  cfg.projectname = name;
3738
- fs6.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
3979
+ fs7.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
3739
3980
  const created = await ctx.projectsProvider.addProject(target);
3740
3981
  return created;
3741
3982
  }
@@ -3997,7 +4238,7 @@ import { createDebugTap } from "@dimina-kit/electron-deck/main";
3997
4238
 
3998
4239
  // src/main/services/dimina-resource-server.ts
3999
4240
  import http from "node:http";
4000
- import fs7 from "node:fs";
4241
+ import fs8 from "node:fs";
4001
4242
  import path14 from "node:path";
4002
4243
  var MIME_TYPES = {
4003
4244
  ".css": "text/css; charset=utf-8",
@@ -4055,7 +4296,7 @@ async function handleRequest(root, req, res) {
4055
4296
  return;
4056
4297
  }
4057
4298
  try {
4058
- const stat = await fs7.promises.stat(filePath);
4299
+ const stat = await fs8.promises.stat(filePath);
4059
4300
  if (!stat.isFile()) {
4060
4301
  res.writeHead(404);
4061
4302
  res.end("Not Found");
@@ -4070,7 +4311,7 @@ async function handleRequest(root, req, res) {
4070
4311
  res.end();
4071
4312
  return;
4072
4313
  }
4073
- fs7.createReadStream(filePath).pipe(res);
4314
+ fs8.createReadStream(filePath).pipe(res);
4074
4315
  } catch {
4075
4316
  res.writeHead(404);
4076
4317
  res.end("Not Found");
@@ -4120,6 +4361,7 @@ function buildServiceHostSpawnUrl(opts) {
4120
4361
  url.searchParams.set("appId", opts.appId);
4121
4362
  url.searchParams.set("pagePath", opts.pagePath);
4122
4363
  url.searchParams.set("pkgRoot", opts.pkgRoot);
4364
+ url.searchParams.set("root", opts.root || "main");
4123
4365
  url.searchParams.set("resourceBaseUrl", opts.resourceBaseUrl);
4124
4366
  if (opts.hostEnvSnapshot) {
4125
4367
  url.searchParams.set("hostEnv", encodeURIComponent(JSON.stringify(opts.hostEnvSnapshot)));
@@ -5358,7 +5600,8 @@ async function handleSpawn(state, ctx, event, opts) {
5358
5600
  const appSessionId = bridgeId;
5359
5601
  const simulatorWc = resolveSimulatorWebContents(ctx, opts.simulatorWcId, event.sender);
5360
5602
  const pagePath = normalizePagePath(opts.pagePath || "pages/index/index");
5361
- const pkgRoot = path16.resolve(opts.pkgRoot || process.cwd());
5603
+ const workspaceProjectPath = typeof ctx.workspace.getProjectPath === "function" ? ctx.workspace.getProjectPath() : "";
5604
+ const pkgRoot = path16.resolve(opts.pkgRoot || workspaceProjectPath || process.cwd());
5362
5605
  const root = opts.root || "main";
5363
5606
  let resourceServer = null;
5364
5607
  let resourceBaseUrl;
@@ -5392,6 +5635,7 @@ async function handleSpawn(state, ctx, event, opts) {
5392
5635
  appId,
5393
5636
  pagePath,
5394
5637
  pkgRoot,
5638
+ root,
5395
5639
  resourceBaseUrl,
5396
5640
  hostEnvSnapshot: hostEnv
5397
5641
  });
@@ -5469,6 +5713,7 @@ async function handleSpawn(state, ctx, event, opts) {
5469
5713
  appId,
5470
5714
  pagePath,
5471
5715
  pkgRoot,
5716
+ root,
5472
5717
  resourceBaseUrl,
5473
5718
  hostEnvSnapshot: hostEnv
5474
5719
  })
@@ -5860,13 +6105,13 @@ function makeLoadResource(ap, page, target) {
5860
6105
  // dimina's service runtime reads hostEnv as `{ systemInfo, menuRect }`
5861
6106
  // (core/host-env.js init → getSystemInfo/getMenuRect; invokeAPI resolves
5862
6107
  // getSystemInfoSync/getWindowInfo/getDeviceInfo from hostEnv.systemInfo).
5863
- // We previously sent the FLAT HostEnvSnapshot, so `systemInfo` was null
5864
- // `wx.getSystemInfoSync()` returned null and pages reading
5865
- // `.screenWidth` threw. Nest it under `systemInfo`. (render does NOT read
5866
- // hostEnv, so this is service-only; the devtools sync-api-patch reads the
5867
- // separate __diminaSpawnContext.hostEnvSnapshot and is unaffected.)
5868
- // menuRect stays null as before — getMenuButtonBoundingClientRect is
5869
- // served by the sync-api-patch / DeviceShell capsule, not this path.
6108
+ // The snapshot must be nested under `systemInfo`: a FLAT HostEnvSnapshot
6109
+ // leaves `systemInfo` null → `wx.getSystemInfoSync()` returns null and
6110
+ // pages reading `.screenWidth` throw. (render does NOT read hostEnv, so
6111
+ // this is service-only; the devtools sync-api-patch reads the separate
6112
+ // __diminaSpawnContext.hostEnvSnapshot and is unaffected.)
6113
+ // menuRect stays null — getMenuButtonBoundingClientRect is served by the
6114
+ // sync-api-patch / DeviceShell capsule, not this path.
5870
6115
  hostEnv: { systemInfo: ap.hostEnv, menuRect: null }
5871
6116
  }
5872
6117
  };
@@ -8096,7 +8341,7 @@ var AppDataAccumulator = class {
8096
8341
  }
8097
8342
  /**
8098
8343
  * Apply one entry. Returns true if it was accepted (a mutation worth
8099
- * republishing), false if dropped (missing ids, or the R6 gate).
8344
+ * republishing), false if dropped (missing ids, or the init-gate).
8100
8345
  */
8101
8346
  apply(input) {
8102
8347
  if (!input.bridgeId || !input.moduleId) return false;
@@ -8379,7 +8624,7 @@ function resolveVPath(url) {
8379
8624
  }
8380
8625
 
8381
8626
  // src/main/services/simulator-temp-files/disk.ts
8382
- import fs8 from "node:fs/promises";
8627
+ import fs9 from "node:fs/promises";
8383
8628
  import path19 from "node:path";
8384
8629
  var EXT_MIME = {
8385
8630
  ".txt": "text/plain",
@@ -8460,7 +8705,7 @@ function etagOf(mtimeMs, size) {
8460
8705
  return `W/"${Math.floor(mtimeMs)}-${size}"`;
8461
8706
  }
8462
8707
  async function readDiskFile(realPath, opts) {
8463
- const handle = await fs8.open(realPath, "r");
8708
+ const handle = await fs9.open(realPath, "r");
8464
8709
  try {
8465
8710
  const st = await handle.stat();
8466
8711
  const totalSize = st.size;
@@ -8515,15 +8760,15 @@ async function readDiskFile(realPath, opts) {
8515
8760
  }
8516
8761
  async function writeDiskFile(realPath, bytes) {
8517
8762
  const buf = Buffer.isBuffer(bytes) ? bytes : Buffer.from(new Uint8Array(bytes));
8518
- await fs8.mkdir(path19.dirname(realPath), { recursive: true });
8519
- await fs8.writeFile(realPath, buf);
8763
+ await fs9.mkdir(path19.dirname(realPath), { recursive: true });
8764
+ await fs9.writeFile(realPath, buf);
8520
8765
  }
8521
8766
  async function readDiskDir(realPath) {
8522
- const entries = await fs8.readdir(realPath);
8767
+ const entries = await fs9.readdir(realPath);
8523
8768
  return entries;
8524
8769
  }
8525
8770
  async function statDiskFile(realPath) {
8526
- const st = await fs8.stat(realPath);
8771
+ const st = await fs9.stat(realPath);
8527
8772
  return {
8528
8773
  size: st.size,
8529
8774
  mtime: Math.floor(st.mtimeMs),
@@ -8656,14 +8901,14 @@ async function handleDifileRequest(ctx, req) {
8656
8901
  }
8657
8902
 
8658
8903
  // src/main/services/simulator-temp-files/fs-channels.ts
8659
- import fs9 from "node:fs/promises";
8904
+ import fs10 from "node:fs/promises";
8660
8905
  import path20 from "node:path";
8661
8906
  async function enforceSandbox(realPath) {
8662
8907
  if (typeof realPath !== "string" || realPath.length === 0) {
8663
8908
  throw new Error("sandbox: realPath must be a non-empty string");
8664
8909
  }
8665
8910
  const base = sandboxBase();
8666
- const baseReal = await fs9.realpath(base).catch(() => base);
8911
+ const baseReal = await fs10.realpath(base).catch(() => base);
8667
8912
  const normalized = path20.normalize(realPath);
8668
8913
  if (normalized !== base && !normalized.startsWith(base + path20.sep)) {
8669
8914
  throw new Error("sandbox: realPath escapes the user-data base");
@@ -8671,7 +8916,7 @@ async function enforceSandbox(realPath) {
8671
8916
  let probe = normalized;
8672
8917
  while (probe !== path20.parse(probe).root) {
8673
8918
  try {
8674
- const resolved = await fs9.realpath(probe);
8919
+ const resolved = await fs10.realpath(probe);
8675
8920
  if (resolved !== baseReal && !resolved.startsWith(baseReal + path20.sep)) {
8676
8921
  throw new Error("sandbox: realPath escapes the user-data base via symlink");
8677
8922
  }
@@ -8711,12 +8956,12 @@ async function handleFsReaddir(req) {
8711
8956
  }
8712
8957
  async function handleFsUnlink(req) {
8713
8958
  const safe = await enforceSandbox(req.realPath);
8714
- await fs9.unlink(safe);
8959
+ await fs10.unlink(safe);
8715
8960
  return { ok: true };
8716
8961
  }
8717
8962
  async function handleFsMkdir(req) {
8718
8963
  const safe = await enforceSandbox(req.realPath);
8719
- await fs9.mkdir(safe, { recursive: !!req.recursive });
8964
+ await fs10.mkdir(safe, { recursive: !!req.recursive });
8720
8965
  return { ok: true };
8721
8966
  }
8722
8967
 
@@ -9149,9 +9394,12 @@ function wireAppWindowEvents(config, instance) {
9149
9394
  onResize: () => context.views.repositionAll(),
9150
9395
  onClose: async (e) => {
9151
9396
  if (isAppQuitting()) return;
9397
+ if (closing) {
9398
+ e.preventDefault();
9399
+ return;
9400
+ }
9152
9401
  if (!context.workspace.hasActiveSession()) return;
9153
9402
  e.preventDefault();
9154
- if (closing) return;
9155
9403
  closing = true;
9156
9404
  try {
9157
9405
  if (config.onBeforeClose) {
@@ -9171,7 +9419,7 @@ function enableDevRendererAutoReload(rendererDir2) {
9171
9419
  });
9172
9420
  }
9173
9421
  let reloadTimer = null;
9174
- const watcher = fs10.watch(rendererDir2, { recursive: true }, () => {
9422
+ const watcher = fs11.watch(rendererDir2, { recursive: true }, () => {
9175
9423
  if (reloadTimer) clearTimeout(reloadTimer);
9176
9424
  reloadTimer = setTimeout(() => {
9177
9425
  for (const win of BrowserWindow8.getAllWindows()) {