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

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 (57) hide show
  1. package/README.md +5 -1
  2. package/dist/main/app/app.js +14 -2
  3. package/dist/main/index.bundle.js +596 -112
  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 +42 -8
  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/render-inspect/index.js +182 -6
  14. package/dist/main/services/service-console/console-api.d.ts +5 -6
  15. package/dist/main/services/service-console/console-api.js +5 -6
  16. package/dist/main/services/service-host-pool/pool.d.ts +2 -3
  17. package/dist/main/services/simulator-storage/index.d.ts +2 -4
  18. package/dist/main/services/simulator-storage/index.js +2 -4
  19. package/dist/main/services/simulator-temp-files/index.js +2 -2
  20. package/dist/main/services/views/devtools-tabs.d.ts +4 -1
  21. package/dist/main/services/views/devtools-tabs.js +21 -3
  22. package/dist/main/services/views/host-toolbar-session-runtime.d.ts +4 -4
  23. package/dist/main/services/views/host-toolbar-session-runtime.js +4 -4
  24. package/dist/main/services/views/view-manager.d.ts +17 -1
  25. package/dist/main/services/views/view-manager.js +74 -47
  26. package/dist/main/services/workspace/workspace-service.js +11 -0
  27. package/dist/main/windows/service-host-window/create.d.ts +1 -0
  28. package/dist/main/windows/service-host-window/create.js +1 -0
  29. package/dist/preload/instrumentation/wxml-extract.js +53 -7
  30. package/dist/preload/runtime/host-toolbar-port.d.ts +1 -1
  31. package/dist/preload/runtime/host-toolbar-port.js +1 -1
  32. package/dist/preload/runtime/host-toolbar-runtime.d.ts +8 -7
  33. package/dist/preload/runtime/host-toolbar-runtime.js +8 -7
  34. package/dist/preload/windows/host-toolbar-runtime.cjs.map +2 -2
  35. package/dist/preload/windows/main.cjs.map +2 -2
  36. package/dist/preload/windows/simulator.cjs +1 -1
  37. package/dist/preload/windows/simulator.cjs.map +1 -1
  38. package/dist/preload/windows/simulator.js +1 -1
  39. package/dist/render-host/render-inspect.js +24 -23
  40. package/dist/renderer/assets/index-BMqriQNr.js +50 -0
  41. package/dist/renderer/entries/main/index.html +1 -1
  42. package/dist/shared/appdata-accumulator.d.ts +2 -2
  43. package/dist/shared/appdata-accumulator.js +3 -3
  44. package/dist/shared/constants.d.ts +1 -1
  45. package/dist/shared/constants.js +1 -1
  46. package/dist/shared/ipc-channels.d.ts +3 -3
  47. package/dist/shared/ipc-channels.js +3 -3
  48. package/dist/shared/open-in-editor.d.ts +27 -1
  49. package/dist/shared/open-in-editor.js +369 -3
  50. package/dist/shared/vpath.d.ts +2 -2
  51. package/dist/shared/vpath.js +2 -2
  52. package/dist/simulator/assets/device-shell-CPCnCp1L.js +2 -0
  53. package/dist/simulator/assets/{simulator-sf-D0mhw.js → simulator-gMBWKDeO.js} +3 -3
  54. package/dist/simulator/simulator.html +1 -1
  55. package/package.json +6 -4
  56. package/dist/renderer/assets/index-DmgWoK8N.js +0 -50
  57. package/dist/simulator/assets/device-shell-BWX7Yopg.js +0 -2
@@ -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,322 @@ 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 excludeBuildChunk = (rel) => {
1142
+ if (!rel) return rel;
1143
+ const base2 = rel.split("/").pop();
1144
+ return base2 === "common.js" || base2 === "vendors.js" || base2 === "runtime.js" || base2 === "taro.js" || base2 === "babelHelpers.js" ? null : rel;
1145
+ };
1146
+ const normalizedRaw = normalizeSlashPath(raw);
1147
+ if (projectRoot && (normalizedRaw === projectRoot || normalizedRaw.startsWith(`${projectRoot}/`))) {
1148
+ const relative = normalizedRaw.slice(projectRoot.length).replace(/^\/+/, "");
1149
+ return excludeBuildChunk(safeRelativePath(relative.split("/").filter(Boolean)));
1150
+ }
1151
+ if (/^\/(?:Volumes|Users|home|private|tmp|var|opt|Applications)\//.test(normalizedRaw)) {
1152
+ return null;
1153
+ }
1154
+ if (!raw.startsWith("/") && !/^[a-z][a-z0-9+.-]*:/i.test(raw)) return null;
1155
+ let resource;
1156
+ try {
1157
+ resource = raw.startsWith("/") && !raw.startsWith("//") ? new URL(raw, base.origin) : new URL(raw);
1158
+ } catch {
1159
+ return null;
1160
+ }
1161
+ if (resource.protocol === "file:") {
1162
+ const filePath = normalizeSlashPath(resource.pathname);
1163
+ if (!projectRoot || filePath !== projectRoot && !filePath.startsWith(`${projectRoot}/`)) return null;
1164
+ return excludeBuildChunk(safeRelativePath(
1165
+ filePath.slice(projectRoot.length).replace(/^\/+/, "").split("/").filter(Boolean)
1166
+ ));
1167
+ }
1168
+ if (resource.protocol !== "http:" && resource.protocol !== "https:") return null;
1169
+ if (resource.origin !== base.origin) return null;
1170
+ let segments = decodePathname(resource.pathname).split("/").filter(Boolean);
1171
+ if (context.appId && segments[0] === context.appId) {
1172
+ segments = segments.slice(1);
1173
+ if (context.outputRoot && segments[0] === context.outputRoot) segments = segments.slice(1);
1174
+ }
1175
+ return excludeBuildChunk(safeRelativePath(segments));
1176
+ }
1177
+ function projectSourceContextFromServiceHostUrl(serviceHostUrl, activeProjectRoot) {
1178
+ let url;
1179
+ try {
1180
+ url = new URL(serviceHostUrl);
1181
+ } catch {
1182
+ return null;
1183
+ }
1184
+ const projectRoot = url.searchParams.get("pkgRoot") || "";
1185
+ const resourceBaseUrl = url.searchParams.get("resourceBaseUrl") ?? "";
1186
+ const appId = url.searchParams.get("appId") ?? "";
1187
+ const outputRoot = url.searchParams.get("root") ?? "main";
1188
+ if (!projectRoot || !resourceBaseUrl || !appId) return null;
1189
+ if (activeProjectRoot) {
1190
+ const authoritativeRoot = normalizeSlashPath(projectRoot).replace(/\/+$/, "");
1191
+ const currentRoot = normalizeSlashPath(activeProjectRoot).replace(/\/+$/, "");
1192
+ if (!authoritativeRoot || authoritativeRoot !== currentRoot) return null;
1193
+ }
1194
+ try {
1195
+ new URL(resourceBaseUrl);
1196
+ } catch {
1197
+ return null;
1198
+ }
1199
+ return { projectRoot, resourceBaseUrl, appId, outputRoot };
1200
+ }
1201
+ function buildDevtoolsProjectSourceLinksScript(context) {
1202
+ const config = JSON.stringify(context);
1203
+ const scheme = JSON.stringify(OPEN_IN_EDITOR_SCHEME);
1204
+ return `
1205
+ (function() {
1206
+ try {
1207
+ const stateKey = '__diminaProjectSourceLinksState__'
1208
+ const previousState = globalThis[stateKey]
1209
+ if (previousState && typeof previousState.dispose === 'function') {
1210
+ try { previousState.dispose() } catch (_) {}
1211
+ }
1212
+
1213
+ const cfg = ${config}
1214
+ const diminaProjectSourcePath = ${projectAwareResourcePath.toString()}
1215
+ const decodePathname = ${decodePathname.toString()}
1216
+ const normalizeSlashPath = ${normalizeSlashPath.toString()}
1217
+ const safeRelativePath = ${safeRelativePath.toString()}
1218
+ const observedRoots = new Set()
1219
+ const observers = []
1220
+ const clickBindings = []
1221
+ let timer = null
1222
+ const state = {
1223
+ dispose() {
1224
+ if (timer !== null) {
1225
+ clearInterval(timer)
1226
+ timer = null
1227
+ }
1228
+ for (const observer of observers.splice(0)) {
1229
+ try { observer.disconnect() } catch (_) {}
1230
+ }
1231
+ for (const binding of clickBindings.splice(0)) {
1232
+ try { binding.target.removeEventListener('click', binding.fn, true) } catch (_) {}
1233
+ }
1234
+ observedRoots.clear()
1235
+ },
1236
+ }
1237
+ globalThis[stateKey] = state
1238
+
1239
+ // The InspectorFrontendHost that owns openInNewTab. Across DevTools
1240
+ // versions it lives on the top-level \`InspectorFrontendHost\` global, the
1241
+ // legacy \`Host.InspectorFrontendHost\`, or \`DevToolsHost\`; resolve at call
1242
+ // time so a late-bootstrapped host is still found.
1243
+ function resolveOpenInNewTabHost() {
1244
+ const candidates = [
1245
+ globalThis.InspectorFrontendHost,
1246
+ globalThis.Host && globalThis.Host.InspectorFrontendHost,
1247
+ globalThis.DevToolsHost,
1248
+ ]
1249
+ for (const host of candidates) {
1250
+ if (host && typeof host.openInNewTab === 'function') return host
1251
+ }
1252
+ return null
1253
+ }
1254
+
1255
+ // Encode (url, 0-based line, 0-based column) as the sentinel the main
1256
+ // process decodes from \`devtools-open-url\` and routes to Monaco.
1257
+ function openInEditor(url, line, column) {
1258
+ const host = resolveOpenInNewTabHost()
1259
+ if (!host) return false
1260
+ const p = new URLSearchParams()
1261
+ p.set('u', String(url))
1262
+ if (typeof line === 'number' && isFinite(line)) p.set('l', String(line))
1263
+ if (typeof column === 'number' && isFinite(column)) p.set('c', String(column))
1264
+ try { host.openInNewTab(${scheme} + ':?' + p.toString()) } catch (_) { return false }
1265
+ return true
1266
+ }
1267
+
1268
+ // Resolve a clicked link element to a project source location. Returns
1269
+ // the FULL resource url (the main process re-maps it against the active
1270
+ // project) plus its display line/column when present.
1271
+ function projectLocationForLink(link) {
1272
+ if (!link) return null
1273
+ const candidates = [
1274
+ link.getAttribute && link.getAttribute('data-url'),
1275
+ link.getAttribute && link.getAttribute('href'),
1276
+ link.getAttribute && link.getAttribute('title'),
1277
+ ]
1278
+ for (const candidate of candidates) {
1279
+ if (!candidate) continue
1280
+ const location = splitLocation(candidate)
1281
+ if (!diminaProjectSourcePath(location.url, cfg)) continue
1282
+ const suffix = location.suffix
1283
+ || ((String(link.textContent || '').match(/(:\\d+(?::\\d+)?)$/) || [])[1] || '')
1284
+ return { url: location.url, suffix }
1285
+ }
1286
+ return null
1287
+ }
1288
+
1289
+ // DevTools shows 1-based line:column in link text; the main process
1290
+ // contract expects 0-based (it re-adds 1). Convert here.
1291
+ function parseSuffix(suffix) {
1292
+ const m = String(suffix || '').match(/^:(\\d+)(?::(\\d+))?$/)
1293
+ if (!m) return { line: undefined, column: undefined }
1294
+ const line = Math.max(0, parseInt(m[1], 10) - 1)
1295
+ const column = m[2] != null ? Math.max(0, parseInt(m[2], 10) - 1) : undefined
1296
+ return { line, column }
1297
+ }
1298
+
1299
+ // Capture-phase click handler: route a project-source link click to
1300
+ // Monaco instead of the DevTools Sources panel. Plain primary clicks only
1301
+ // \u2014 modified clicks (cmd/ctrl/shift/alt, middle button) fall through to
1302
+ // DevTools so its own "open in new tab" / multi-select still work.
1303
+ function onLinkClick(event) {
1304
+ if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return
1305
+ const path = typeof event.composedPath === 'function' ? event.composedPath() : []
1306
+ let link = null
1307
+ for (const node of path) {
1308
+ if (node && node.nodeType === 1 && node.classList && node.classList.contains('devtools-link')) {
1309
+ link = node
1310
+ break
1311
+ }
1312
+ }
1313
+ if (!link) return
1314
+ const location = projectLocationForLink(link)
1315
+ if (!location) return
1316
+ const { line, column } = parseSuffix(location.suffix)
1317
+ if (!openInEditor(location.url, line, column)) return
1318
+ event.preventDefault()
1319
+ event.stopImmediatePropagation()
1320
+ }
1321
+
1322
+ function bindClicks(target) {
1323
+ if (!target || !target.addEventListener) return
1324
+ for (const binding of clickBindings) {
1325
+ if (binding.target === target) return
1326
+ }
1327
+ target.addEventListener('click', onLinkClick, true)
1328
+ clickBindings.push({ target, fn: onLinkClick })
1329
+ }
1330
+
1331
+ function splitLocation(value) {
1332
+ const raw = String(value || '')
1333
+ const match = raw.match(/^(.*?)(?::(\\d+))(?::(\\d+))?$/)
1334
+ return match
1335
+ ? { url: match[1], suffix: ':' + match[2] + (match[3] ? ':' + match[3] : '') }
1336
+ : { url: raw, suffix: '' }
1337
+ }
1338
+
1339
+ function rewriteLink(link) {
1340
+ if (!link || link.dataset.diminaProjectSourcePath) return
1341
+ const candidates = [
1342
+ link.getAttribute && link.getAttribute('title'),
1343
+ link.getAttribute && link.getAttribute('href'),
1344
+ link.getAttribute && link.getAttribute('data-url'),
1345
+ ]
1346
+ for (const candidate of candidates) {
1347
+ if (!candidate) continue
1348
+ const location = splitLocation(candidate)
1349
+ const relative = diminaProjectSourcePath(location.url, cfg)
1350
+ if (!relative) continue
1351
+ const textSuffix = location.suffix || ((String(link.textContent || '').match(/(:\\d+(?::\\d+)?)$/) || [])[1] || '')
1352
+ link.textContent = relative + textSuffix
1353
+ link.dataset.diminaProjectSourcePath = relative
1354
+ return
1355
+ }
1356
+ }
1357
+
1358
+ function observeRoot(root) {
1359
+ if (!root || !root.querySelectorAll || observedRoots.has(root)) return
1360
+ observedRoots.add(root)
1361
+ bindClicks(root)
1362
+
1363
+ const observer = new MutationObserver((records) => {
1364
+ for (const record of records) {
1365
+ if (record.type === 'attributes') rewriteLink(record.target)
1366
+ for (const node of record.addedNodes || []) visitNode(node)
1367
+ }
1368
+ })
1369
+ observer.observe(root, {
1370
+ subtree: true,
1371
+ childList: true,
1372
+ attributes: true,
1373
+ attributeFilter: ['title', 'href', 'data-url'],
1374
+ })
1375
+ observers.push(observer)
1376
+
1377
+ for (const link of root.querySelectorAll('.devtools-link')) rewriteLink(link)
1378
+ for (const element of root.querySelectorAll('*')) {
1379
+ if (element.shadowRoot) observeRoot(element.shadowRoot)
1380
+ }
1381
+ }
1382
+
1383
+ function visitNode(node) {
1384
+ if (!node) return
1385
+ if (node.matches && node.matches('.devtools-link')) rewriteLink(node)
1386
+ if (node.shadowRoot) observeRoot(node.shadowRoot)
1387
+ if (!node.querySelectorAll) return
1388
+ for (const link of node.querySelectorAll('.devtools-link')) rewriteLink(link)
1389
+ for (const element of node.querySelectorAll('*')) {
1390
+ if (element.shadowRoot) observeRoot(element.shadowRoot)
1391
+ }
1392
+ }
1393
+
1394
+ observeRoot(document)
1395
+
1396
+ // Fallback for older DevTools fronts that still expose the
1397
+ // setOpenResourceHandler hook. On current Chromium the method is gone,
1398
+ // so this poll simply times out and the capture-phase click interceptor
1399
+ // is the only live path. When the hook IS present the interceptor still
1400
+ // wins (it runs in capture and stops propagation), so the two never
1401
+ // double-open the same click.
1402
+ let tries = 0
1403
+ timer = setInterval(() => {
1404
+ tries++
1405
+ try {
1406
+ const host = resolveOpenInNewTabHost()
1407
+ if (host && typeof host.setOpenResourceHandler === 'function') {
1408
+ host.setOpenResourceHandler((url, lineNumber, columnNumber) => {
1409
+ if (!diminaProjectSourcePath(String(url), cfg)) return
1410
+ openInEditor(
1411
+ String(url),
1412
+ typeof lineNumber === 'number' ? lineNumber : undefined,
1413
+ typeof columnNumber === 'number' ? columnNumber : undefined,
1414
+ )
1415
+ })
1416
+ clearInterval(timer)
1417
+ timer = null
1418
+ }
1419
+ } catch (_) {}
1420
+ if (tries > 80 && timer !== null) {
1421
+ clearInterval(timer)
1422
+ timer = null
1423
+ }
1424
+ }, 50)
1425
+ } catch (_) {}
1426
+ })()
1427
+ `;
1428
+ }
1109
1429
 
1110
1430
  // src/main/services/safe-area/index.ts
1111
1431
  function guestInsets(device, isTabPage2) {
@@ -1157,19 +1477,25 @@ function createSafeAreaController(options = {}) {
1157
1477
  }
1158
1478
 
1159
1479
  // src/main/services/views/devtools-tabs.ts
1160
- var DEVTOOLS_KEPT_VIEW_IDS = ["elements", "console", "network"];
1480
+ var DEVTOOLS_KEPT_VIEW_IDS = ["elements", "console", "network", "sources"];
1161
1481
  function buildCustomizeTabsScript(keptIds = DEVTOOLS_KEPT_VIEW_IDS) {
1162
1482
  const NAME_MAP = {
1163
1483
  elements: ["Elements", "\u5143\u7D20"],
1164
1484
  console: ["Console", "\u63A7\u5236\u53F0"],
1165
- network: ["Network", "\u7F51\u7EDC"]
1485
+ network: ["Network", "\u7F51\u7EDC"],
1486
+ sources: ["Sources", "\u6765\u6E90", "\u6E90\u4EE3\u7801"]
1166
1487
  };
1167
1488
  const keepNames = keptIds.flatMap((id) => NAME_MAP[id] ?? [id]);
1168
1489
  const keepIdsJson = JSON.stringify(JSON.stringify([...keptIds]));
1169
1490
  const keepNamesJson = JSON.stringify(JSON.stringify(keepNames));
1491
+ const srcNamesJson = JSON.stringify(JSON.stringify(NAME_MAP.sources));
1492
+ const netNamesJson = JSON.stringify(JSON.stringify(NAME_MAP.network));
1170
1493
  return `(function(){try{
1171
1494
  var KEEPID = new Set(JSON.parse(${keepIdsJson}));
1172
1495
  var KEEPNAME = new Set(JSON.parse(${keepNamesJson}));
1496
+ var SRCNAME = new Set(JSON.parse(${srcNamesJson}));
1497
+ var NETNAME = new Set(JSON.parse(${netNamesJson}));
1498
+ var WANT_SOURCES_LAST = KEEPID.has('sources');
1173
1499
  // (1) Locale infobar \u2014 official host-preference suppression (NOT localStorage).
1174
1500
  try { var IFH=globalThis.InspectorFrontendHost; if(IFH&&typeof IFH.setPreference==='function'){ IFH.setPreference('disable-locale-info-bar','true'); } }catch(_){}
1175
1501
  function deepCollect(sel,cap){ var out=[],seen=0,stack=[document]; while(stack.length&&seen<(cap||40000)){ var root=stack.pop(); try{var m=root.querySelectorAll?root.querySelectorAll(sel):[];for(var i=0;i<m.length;i++)out.push(m[i]);}catch(_){} try{var all=root.querySelectorAll?root.querySelectorAll('*'):[];for(var j=0;j<all.length;j++){seen++;if(all[j].shadowRoot)stack.push(all[j].shadowRoot);}}catch(_){} } return out; }
@@ -1197,9 +1523,18 @@ function buildCustomizeTabsScript(keptIds = DEVTOOLS_KEPT_VIEW_IDS) {
1197
1523
  handled=true;
1198
1524
  }catch(_){} }
1199
1525
  // fallback: registrations not enumerable -> at least clear the bar by id
1200
- if(!handled){ var FALL=['timeline','resources','heap-profiler','sources','security','lighthouse','chrome-recorder','coverage','linear-memory-inspector','sensors','rendering','animations','autofill-view','medias','issues-pane']; for(var fr=0;fr<FALL.length;fr++){ try{ if(!KEEPID.has(FALL[fr])) maybeRemove(FALL[fr]); }catch(_){} } }
1526
+ if(!handled){ var FALL=['timeline','resources','heap-profiler','security','lighthouse','chrome-recorder','coverage','linear-memory-inspector','sensors','rendering','animations','autofill-view','medias','issues-pane']; for(var fr=0;fr<FALL.length;fr++){ try{ if(!KEEPID.has(FALL[fr])) maybeRemove(FALL[fr]); }catch(_){} } }
1201
1527
  } else { domFallback(); }
1202
1528
  })();
1529
+ // Keep Sources LAST in the bar (after Network). Default DevTools order puts
1530
+ // Sources between Console and Network; nudge it to just after Network. Runs in
1531
+ // BOTH paths (registry removal leaves the kept tabs in default order). Self-
1532
+ // stable: once Sources follows Network the condition no longer holds.
1533
+ if(WANT_SOURCES_LAST){ var ro=0,rt=setInterval(function(){ ro++; try{
1534
+ var tabs=deepCollect('[role="tab"]'),src=null,net=null;
1535
+ for(var i=0;i<tabs.length;i++){ var n=txt(tabs[i]); if(SRCNAME.has(n))src=tabs[i]; else if(NETNAME.has(n))net=tabs[i]; }
1536
+ if(src&&net&&src.parentElement&&src.parentElement===net.parentElement&&(src.compareDocumentPosition(net)&Node.DOCUMENT_POSITION_FOLLOWING)){ net.parentElement.insertBefore(src,net.nextSibling); }
1537
+ }catch(_){} if(ro>120)clearInterval(rt); },60); }
1203
1538
  // DOM fallback: only if the ESM module couldn't be resolved at all.
1204
1539
  function domFallback(){ var cachedBar=null;
1205
1540
  function findBar(){ if(cachedBar&&cachedBar.isConnected)return cachedBar; cachedBar=null; var tabs=deepCollect('[role="tab"]'); var a=null; for(var i=0;i<tabs.length;i++){if(KEEPNAME.has(txt(tabs[i]))){a=tabs[i];break;}} if(!a)return null; var p=a.parentElement; while(p){try{if(p.querySelectorAll('[role="tab"]').length>1){cachedBar=p;return p;}}catch(_){} p=p.parentElement;} cachedBar=a.parentElement; return cachedBar; }
@@ -1454,10 +1789,19 @@ function installElementsForward(deps) {
1454
1789
  function primeGuest(wc) {
1455
1790
  if (!ensureGuestDebugger(wc)) return;
1456
1791
  wireGuestEvents(wc);
1457
- for (const domain of ["DOM.enable", "CSS.enable", "Overlay.enable"]) {
1458
- wc.debugger.sendCommand(domain).catch(() => {
1459
- });
1792
+ void enableGuestDomains(wc);
1793
+ }
1794
+ async function enableGuestDomains(wc) {
1795
+ wc.debugger.sendCommand("CSS.enable").catch(() => {
1796
+ });
1797
+ try {
1798
+ await wc.debugger.sendCommand("DOM.enable");
1799
+ } catch {
1800
+ return;
1460
1801
  }
1802
+ if (disposed || !isActiveWcId(wc.id)) return;
1803
+ wc.debugger.sendCommand("Overlay.enable").catch(() => {
1804
+ });
1461
1805
  }
1462
1806
  function routeCommand(cmd) {
1463
1807
  const wc = activeRenderWc();
@@ -1477,6 +1821,10 @@ function installElementsForward(deps) {
1477
1821
  replyError(cmd, "stale render generation");
1478
1822
  return;
1479
1823
  }
1824
+ if (cmd.method === "Overlay.disable" && isActiveWcId(cmdWcId)) {
1825
+ wc.debugger.sendCommand("Overlay.enable").catch(() => {
1826
+ });
1827
+ }
1480
1828
  replyResult(cmd, result);
1481
1829
  }).catch((err) => {
1482
1830
  if (disposed) return;
@@ -2022,12 +2370,36 @@ function parseRoute(url) {
2022
2370
  }
2023
2371
 
2024
2372
  // src/main/services/views/view-manager.ts
2373
+ function resolveProjectEditorTarget(serviceHostUrl, activeProjectRoot, req, isFile = (absolutePath) => fs4.statSync(absolutePath).isFile()) {
2374
+ const sourceContext = projectSourceContextFromServiceHostUrl(
2375
+ serviceHostUrl,
2376
+ activeProjectRoot
2377
+ );
2378
+ if (!sourceContext) return null;
2379
+ const rel = resourceUrlToProjectRelativePath(req.url, sourceContext);
2380
+ if (!rel) return null;
2381
+ const absolute = path7.resolve(sourceContext.projectRoot, ...rel.split("/"));
2382
+ const fromRoot = path7.relative(path7.resolve(sourceContext.projectRoot), absolute);
2383
+ if (!fromRoot || fromRoot.startsWith("..") || path7.isAbsolute(fromRoot)) return null;
2384
+ try {
2385
+ if (!isFile(absolute)) return null;
2386
+ } catch {
2387
+ return null;
2388
+ }
2389
+ return {
2390
+ path: rel,
2391
+ line: typeof req.line === "number" ? req.line + 1 : void 0,
2392
+ column: typeof req.column === "number" ? req.column + 1 : void 0
2393
+ };
2394
+ }
2025
2395
  function createViewManager(ctx) {
2026
2396
  const safeArea = createSafeAreaController({ connections: ctx.connections });
2027
2397
  let simulatorView = null;
2028
2398
  let simulatorViewAdded = false;
2029
2399
  let nativeSimulatorView = null;
2030
2400
  let nativeSimulatorViewAdded = false;
2401
+ let nativeSimulatorProjectPath = null;
2402
+ let settleNativeSimulatorReady = null;
2031
2403
  let lastRendererRect = null;
2032
2404
  let nativeCustomApiBridgeHandler = null;
2033
2405
  let currentZoomFactor = 1;
@@ -2260,37 +2632,13 @@ function createViewManager(ctx) {
2260
2632
  closeNativeDevtoolsSource();
2261
2633
  }
2262
2634
  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(() => {
2635
+ function injectOpenResourceHandler(serviceWc, devtoolsWc) {
2636
+ const sourceContext = projectSourceContextFromServiceHostUrl(
2637
+ serviceWc.getURL(),
2638
+ ctx.workspace?.getProjectPath?.()
2639
+ );
2640
+ if (!sourceContext) return;
2641
+ devtoolsWc.executeJavaScript(buildDevtoolsProjectSourceLinksScript(sourceContext)).catch(() => {
2294
2642
  });
2295
2643
  }
2296
2644
  function customizeDevtoolsTabs(devtoolsWc) {
@@ -2315,9 +2663,9 @@ function createViewManager(ctx) {
2315
2663
  function wireOpenInEditor(serviceWc, devtoolsWc) {
2316
2664
  if (!devtoolsWc.isDestroyed()) {
2317
2665
  if (devtoolsWc.isLoading()) {
2318
- devtoolsWc.once("dom-ready", () => injectOpenResourceHandler(devtoolsWc));
2666
+ devtoolsWc.once("dom-ready", () => injectOpenResourceHandler(serviceWc, devtoolsWc));
2319
2667
  } else {
2320
- injectOpenResourceHandler(devtoolsWc);
2668
+ injectOpenResourceHandler(serviceWc, devtoolsWc);
2321
2669
  }
2322
2670
  }
2323
2671
  if (openInEditorWiredWcIds.has(serviceWc.id)) return;
@@ -2325,11 +2673,13 @@ function createViewManager(ctx) {
2325
2673
  const onOpenUrl = (_event, url) => {
2326
2674
  const req = decodeOpenInEditorUrl(url);
2327
2675
  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 });
2676
+ const target = resolveProjectEditorTarget(
2677
+ serviceWc.getURL(),
2678
+ ctx.workspace?.getProjectPath?.(),
2679
+ req
2680
+ );
2681
+ if (!target) return;
2682
+ ctx.notify.editorOpenFile(target);
2333
2683
  };
2334
2684
  serviceWc.on("devtools-open-url", onOpenUrl);
2335
2685
  const conn = ctx.connections.acquire(serviceWc);
@@ -2489,8 +2839,10 @@ function createViewManager(ctx) {
2489
2839
  function attachNativeSimulator(simulatorUrl, _simWidth) {
2490
2840
  if (!ctx.preloadPath) {
2491
2841
  console.error("[workbench] attachNativeSimulator \u2014 preloadPath unset; cannot mount native simulator");
2492
- return;
2842
+ return Promise.resolve();
2493
2843
  }
2844
+ settleNativeSimulatorReady?.();
2845
+ settleNativeSimulatorReady = null;
2494
2846
  if (nativeSimulatorView) {
2495
2847
  detachNativeCustomApiBridge();
2496
2848
  if (nativeSimulatorViewAdded) {
@@ -2508,6 +2860,10 @@ function createViewManager(ctx) {
2508
2860
  }
2509
2861
  nativeSimulatorView = null;
2510
2862
  }
2863
+ nativeSimulatorProjectPath = null;
2864
+ const ready = new Promise((resolve) => {
2865
+ settleNativeSimulatorReady = resolve;
2866
+ });
2511
2867
  const route = parseRoute(simulatorUrl);
2512
2868
  const partition = miniappPartition(route?.appId);
2513
2869
  configureMiniappSession(partition);
@@ -2522,6 +2878,7 @@ function createViewManager(ctx) {
2522
2878
  }
2523
2879
  });
2524
2880
  nativeSimulatorView = view;
2881
+ nativeSimulatorProjectPath = ctx.workspace?.getProjectPath() || null;
2525
2882
  view.setBackgroundColor(simDeskBg());
2526
2883
  const simWc = view.webContents;
2527
2884
  const syncDeskBg = () => {
@@ -2571,6 +2928,11 @@ function createViewManager(ctx) {
2571
2928
  e.preventDefault();
2572
2929
  }
2573
2930
  });
2931
+ guestWc.once("did-finish-load", () => {
2932
+ if (nativeSimulatorView !== view || simWc.isDestroyed()) return;
2933
+ settleNativeSimulatorReady?.();
2934
+ settleNativeSimulatorReady = null;
2935
+ });
2574
2936
  followNativeDevtoolsServiceHost();
2575
2937
  });
2576
2938
  simWc.setWindowOpenHandler(({ url }) => handleWindowOpenExternal(url));
@@ -2599,8 +2961,11 @@ function createViewManager(ctx) {
2599
2961
  setNativeSimulatorViewBounds(lastRendererRect);
2600
2962
  }
2601
2963
  attachNativeSimulatorDevtoolsHost();
2964
+ return ready;
2602
2965
  }
2603
2966
  function detachSimulator() {
2967
+ settleNativeSimulatorReady?.();
2968
+ settleNativeSimulatorReady = null;
2604
2969
  stopFollowingNativeServiceHost();
2605
2970
  detachNativeCustomApiBridge();
2606
2971
  try {
@@ -2634,6 +2999,7 @@ function createViewManager(ctx) {
2634
2999
  simulatorView = null;
2635
3000
  simulatorViewAdded = false;
2636
3001
  simulatorWebContentsId = null;
3002
+ nativeSimulatorProjectPath = null;
2637
3003
  simulatorBoundsOverride = null;
2638
3004
  lastRendererRect = null;
2639
3005
  }
@@ -2785,6 +3151,7 @@ function createViewManager(ctx) {
2785
3151
  const wc = webContents.fromId(simulatorWebContentsId);
2786
3152
  return wc && !wc.isDestroyed() ? wc : null;
2787
3153
  },
3154
+ getSimulatorProjectPath: () => nativeSimulatorProjectPath,
2788
3155
  getSettingsWebContents: () => {
2789
3156
  if (!settingsView) return null;
2790
3157
  if (settingsView.webContents.isDestroyed()) return null;
@@ -2909,20 +3276,20 @@ import { z as z2 } from "zod";
2909
3276
  // src/main/services/projects/project-repository.ts
2910
3277
  import { app as app6 } from "electron";
2911
3278
  import path9 from "path";
2912
- import fs4 from "fs";
3279
+ import fs5 from "fs";
2913
3280
  var log2 = createLogger("projects");
2914
3281
  function getProjectsFile() {
2915
3282
  return path9.join(app6.getPath("userData"), "dimina-projects.json");
2916
3283
  }
2917
3284
  function load() {
2918
3285
  try {
2919
- return JSON.parse(fs4.readFileSync(getProjectsFile(), "utf-8"));
3286
+ return JSON.parse(fs5.readFileSync(getProjectsFile(), "utf-8"));
2920
3287
  } catch {
2921
3288
  return [];
2922
3289
  }
2923
3290
  }
2924
3291
  function save(projects) {
2925
- fs4.writeFileSync(getProjectsFile(), JSON.stringify(projects, null, 2));
3292
+ fs5.writeFileSync(getProjectsFile(), JSON.stringify(projects, null, 2));
2926
3293
  }
2927
3294
  function listProjects() {
2928
3295
  return load();
@@ -2931,14 +3298,14 @@ function validateProjectDir(dirPath) {
2931
3298
  if (!dirPath) {
2932
3299
  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
3300
  }
2934
- if (!fs4.existsSync(dirPath)) {
3301
+ if (!fs5.existsSync(dirPath)) {
2935
3302
  return `\u5C0F\u7A0B\u5E8F\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${dirPath}`;
2936
3303
  }
2937
- if (!fs4.existsSync(path9.join(dirPath, "app.json"))) {
3304
+ if (!fs5.existsSync(path9.join(dirPath, "app.json"))) {
2938
3305
  const configPath = path9.join(dirPath, "project.config.json");
2939
- if (fs4.existsSync(configPath)) {
3306
+ if (fs5.existsSync(configPath)) {
2940
3307
  try {
2941
- const cfg = JSON.parse(fs4.readFileSync(configPath, "utf-8"));
3308
+ const cfg = JSON.parse(fs5.readFileSync(configPath, "utf-8"));
2942
3309
  if (cfg.miniprogramRoot) {
2943
3310
  const resolvedRoot = path9.resolve(dirPath, cfg.miniprogramRoot);
2944
3311
  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 +3323,8 @@ function addProject(dirPath) {
2956
3323
  let name = path9.basename(dirPath);
2957
3324
  try {
2958
3325
  const configPath = path9.join(dirPath, "project.config.json");
2959
- if (fs4.existsSync(configPath)) {
2960
- const cfg = JSON.parse(fs4.readFileSync(configPath, "utf-8"));
3326
+ if (fs5.existsSync(configPath)) {
3327
+ const cfg = JSON.parse(fs5.readFileSync(configPath, "utf-8"));
2961
3328
  if (cfg.projectname) name = cfg.projectname;
2962
3329
  }
2963
3330
  } catch (err) {
@@ -2997,7 +3364,7 @@ function getProjectPages(dirPath) {
2997
3364
  const appJsonPath = path9.join(dirPath, "app.json");
2998
3365
  try {
2999
3366
  const appJson = JSON.parse(
3000
- fs4.readFileSync(appJsonPath, "utf-8")
3367
+ fs5.readFileSync(appJsonPath, "utf-8")
3001
3368
  );
3002
3369
  return {
3003
3370
  pages: appJson.pages || [],
@@ -3021,7 +3388,7 @@ function getProjectSettings(projectPath) {
3021
3388
  }
3022
3389
  try {
3023
3390
  const configPath = path9.join(projectPath, "project.config.json");
3024
- const config = JSON.parse(fs4.readFileSync(configPath, "utf-8"));
3391
+ const config = JSON.parse(fs5.readFileSync(configPath, "utf-8"));
3025
3392
  return {
3026
3393
  uploadWithSourceMap: !!config.setting?.uploadWithSourceMap
3027
3394
  };
@@ -3034,14 +3401,14 @@ function updateProjectSettings(projectPath, patch) {
3034
3401
  const configPath = path9.join(projectPath, "project.config.json");
3035
3402
  let config = {};
3036
3403
  try {
3037
- config = JSON.parse(fs4.readFileSync(configPath, "utf-8"));
3404
+ config = JSON.parse(fs5.readFileSync(configPath, "utf-8"));
3038
3405
  } catch {
3039
3406
  }
3040
3407
  const nextSetting = {
3041
3408
  ...config.setting ?? {},
3042
3409
  ...patch.uploadWithSourceMap === void 0 ? {} : { uploadWithSourceMap: patch.uploadWithSourceMap }
3043
3410
  };
3044
- fs4.writeFileSync(
3411
+ fs5.writeFileSync(
3045
3412
  configPath,
3046
3413
  JSON.stringify({ ...config, setting: nextSetting }, null, 2)
3047
3414
  );
@@ -3216,10 +3583,16 @@ function createWorkspaceService(ctx) {
3216
3583
  getLastClosedProjectPath: () => lastClosedProjectPath,
3217
3584
  hasActiveSession: () => currentSession !== null,
3218
3585
  async captureThumbnail(projectPath) {
3586
+ if (!currentSession || projectPath !== currentProjectPath) return null;
3219
3587
  const wc = ctx.views.getSimulatorWebContents();
3220
3588
  if (!wc) return null;
3589
+ if (ctx.views.getSimulatorProjectPath() !== projectPath) return null;
3590
+ const session5 = currentSession;
3221
3591
  try {
3222
3592
  const image = await wc.capturePage();
3593
+ if (currentSession !== session5 || currentProjectPath !== projectPath || ctx.views.getSimulatorWebContents() !== wc || ctx.views.getSimulatorProjectPath() !== projectPath) {
3594
+ return null;
3595
+ }
3223
3596
  const dataUrl = `data:image/png;base64,${image.toPNG().toString("base64")}`;
3224
3597
  if (provider.saveThumbnail) {
3225
3598
  await provider.saveThumbnail(projectPath, dataUrl);
@@ -3253,7 +3626,7 @@ function createWorkspaceService(ctx) {
3253
3626
 
3254
3627
  // src/main/services/projects/thumbnail.ts
3255
3628
  import { createHash } from "crypto";
3256
- import fs5 from "fs";
3629
+ import fs6 from "fs";
3257
3630
  import path10 from "path";
3258
3631
  import { app as app7 } from "electron";
3259
3632
  function getThumbnailDir() {
@@ -3269,13 +3642,13 @@ var DATA_URL_PNG_PREFIX = "data:image/png;base64,";
3269
3642
  function saveThumbnailFromDataUrl(projectPath, imageDataUrl) {
3270
3643
  if (!imageDataUrl.startsWith(DATA_URL_PNG_PREFIX)) return;
3271
3644
  const png = Buffer.from(imageDataUrl.slice(DATA_URL_PNG_PREFIX.length), "base64");
3272
- fs5.mkdirSync(getThumbnailDir(), { recursive: true });
3273
- fs5.writeFileSync(getThumbnailPath(projectPath), png);
3645
+ fs6.mkdirSync(getThumbnailDir(), { recursive: true });
3646
+ fs6.writeFileSync(getThumbnailPath(projectPath), png);
3274
3647
  }
3275
3648
  function loadThumbnail(projectPath) {
3276
3649
  const filePath = getThumbnailPath(projectPath);
3277
3650
  try {
3278
- const buf = fs5.readFileSync(filePath);
3651
+ const buf = fs6.readFileSync(filePath);
3279
3652
  return `${DATA_URL_PNG_PREFIX}${buf.toString("base64")}`;
3280
3653
  } catch {
3281
3654
  return null;
@@ -3635,7 +4008,7 @@ function deviceInfoToHostEnv(d) {
3635
4008
  function registerSimulatorIpc(ctx) {
3636
4009
  return new IpcRegistry(ctx.senderPolicy).handle(SimulatorChannel.AttachNative, (_, ...args) => {
3637
4010
  const [simulatorUrl, simWidth] = validate(SimulatorChannel.AttachNative, SimulatorAttachNativeSchema, args);
3638
- ctx.views.attachNativeSimulator(simulatorUrl, simWidth);
4011
+ return ctx.views.attachNativeSimulator(simulatorUrl, simWidth);
3639
4012
  }).handle(SimulatorChannel.Detach, () => {
3640
4013
  ctx.views.detachSimulator();
3641
4014
  }).handle(SimulatorChannel.SetNativeBounds, (_, ...args) => {
@@ -3677,7 +4050,7 @@ import { app as app8, dialog } from "electron";
3677
4050
  import path13 from "path";
3678
4051
 
3679
4052
  // src/main/services/projects/create-project-service.ts
3680
- import fs6 from "node:fs";
4053
+ import fs7 from "node:fs";
3681
4054
  import path12 from "node:path";
3682
4055
  var DEFAULT_TEMPLATE_ID = "blank";
3683
4056
  async function createProject(input, ctx) {
@@ -3689,12 +4062,12 @@ async function createProject(input, ctx) {
3689
4062
  if (!target || typeof target !== "string") {
3690
4063
  throw new Error("Project path is required");
3691
4064
  }
3692
- if (fs6.existsSync(target)) {
3693
- const stat = fs6.statSync(target);
4065
+ if (fs7.existsSync(target)) {
4066
+ const stat = fs7.statSync(target);
3694
4067
  if (!stat.isDirectory()) {
3695
4068
  throw new Error(`Project path exists and is not a directory: ${target}`);
3696
4069
  }
3697
- const entries = fs6.readdirSync(target);
4070
+ const entries = fs7.readdirSync(target);
3698
4071
  if (entries.length > 0) {
3699
4072
  throw new Error(
3700
4073
  `Project path is not empty (refusing to overwrite): ${target}`
@@ -3711,31 +4084,31 @@ async function createProject(input, ctx) {
3711
4084
  `Template '${templateId}' has neither a source directory nor a generate function`
3712
4085
  );
3713
4086
  }
3714
- fs6.mkdirSync(target, { recursive: true });
4087
+ fs7.mkdirSync(target, { recursive: true });
3715
4088
  if (template.generate) {
3716
4089
  await template.generate(target, { name });
3717
4090
  } else if (template.source) {
3718
- if (!fs6.existsSync(template.source.path)) {
4091
+ if (!fs7.existsSync(template.source.path)) {
3719
4092
  throw new Error(
3720
4093
  `Template source missing on disk: ${template.source.path}`
3721
4094
  );
3722
4095
  }
3723
- fs6.cpSync(template.source.path, target, {
4096
+ fs7.cpSync(template.source.path, target, {
3724
4097
  recursive: true,
3725
4098
  force: true
3726
4099
  });
3727
4100
  }
3728
4101
  const cfgPath = path12.join(target, "project.config.json");
3729
4102
  let cfg = {};
3730
- if (fs6.existsSync(cfgPath)) {
4103
+ if (fs7.existsSync(cfgPath)) {
3731
4104
  try {
3732
- cfg = JSON.parse(fs6.readFileSync(cfgPath, "utf-8"));
4105
+ cfg = JSON.parse(fs7.readFileSync(cfgPath, "utf-8"));
3733
4106
  } catch {
3734
4107
  cfg = {};
3735
4108
  }
3736
4109
  }
3737
4110
  cfg.projectname = name;
3738
- fs6.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
4111
+ fs7.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
3739
4112
  const created = await ctx.projectsProvider.addProject(target);
3740
4113
  return created;
3741
4114
  }
@@ -3997,7 +4370,7 @@ import { createDebugTap } from "@dimina-kit/electron-deck/main";
3997
4370
 
3998
4371
  // src/main/services/dimina-resource-server.ts
3999
4372
  import http from "node:http";
4000
- import fs7 from "node:fs";
4373
+ import fs8 from "node:fs";
4001
4374
  import path14 from "node:path";
4002
4375
  var MIME_TYPES = {
4003
4376
  ".css": "text/css; charset=utf-8",
@@ -4055,7 +4428,7 @@ async function handleRequest(root, req, res) {
4055
4428
  return;
4056
4429
  }
4057
4430
  try {
4058
- const stat = await fs7.promises.stat(filePath);
4431
+ const stat = await fs8.promises.stat(filePath);
4059
4432
  if (!stat.isFile()) {
4060
4433
  res.writeHead(404);
4061
4434
  res.end("Not Found");
@@ -4070,7 +4443,7 @@ async function handleRequest(root, req, res) {
4070
4443
  res.end();
4071
4444
  return;
4072
4445
  }
4073
- fs7.createReadStream(filePath).pipe(res);
4446
+ fs8.createReadStream(filePath).pipe(res);
4074
4447
  } catch {
4075
4448
  res.writeHead(404);
4076
4449
  res.end("Not Found");
@@ -4120,6 +4493,7 @@ function buildServiceHostSpawnUrl(opts) {
4120
4493
  url.searchParams.set("appId", opts.appId);
4121
4494
  url.searchParams.set("pagePath", opts.pagePath);
4122
4495
  url.searchParams.set("pkgRoot", opts.pkgRoot);
4496
+ url.searchParams.set("root", opts.root || "main");
4123
4497
  url.searchParams.set("resourceBaseUrl", opts.resourceBaseUrl);
4124
4498
  if (opts.hostEnvSnapshot) {
4125
4499
  url.searchParams.set("hostEnv", encodeURIComponent(JSON.stringify(opts.hostEnvSnapshot)));
@@ -5358,7 +5732,8 @@ async function handleSpawn(state, ctx, event, opts) {
5358
5732
  const appSessionId = bridgeId;
5359
5733
  const simulatorWc = resolveSimulatorWebContents(ctx, opts.simulatorWcId, event.sender);
5360
5734
  const pagePath = normalizePagePath(opts.pagePath || "pages/index/index");
5361
- const pkgRoot = path16.resolve(opts.pkgRoot || process.cwd());
5735
+ const workspaceProjectPath = typeof ctx.workspace.getProjectPath === "function" ? ctx.workspace.getProjectPath() : "";
5736
+ const pkgRoot = path16.resolve(opts.pkgRoot || workspaceProjectPath || process.cwd());
5362
5737
  const root = opts.root || "main";
5363
5738
  let resourceServer = null;
5364
5739
  let resourceBaseUrl;
@@ -5392,6 +5767,7 @@ async function handleSpawn(state, ctx, event, opts) {
5392
5767
  appId,
5393
5768
  pagePath,
5394
5769
  pkgRoot,
5770
+ root,
5395
5771
  resourceBaseUrl,
5396
5772
  hostEnvSnapshot: hostEnv
5397
5773
  });
@@ -5469,6 +5845,7 @@ async function handleSpawn(state, ctx, event, opts) {
5469
5845
  appId,
5470
5846
  pagePath,
5471
5847
  pkgRoot,
5848
+ root,
5472
5849
  resourceBaseUrl,
5473
5850
  hostEnvSnapshot: hostEnv
5474
5851
  })
@@ -5860,13 +6237,13 @@ function makeLoadResource(ap, page, target) {
5860
6237
  // dimina's service runtime reads hostEnv as `{ systemInfo, menuRect }`
5861
6238
  // (core/host-env.js init → getSystemInfo/getMenuRect; invokeAPI resolves
5862
6239
  // 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.
6240
+ // The snapshot must be nested under `systemInfo`: a FLAT HostEnvSnapshot
6241
+ // leaves `systemInfo` null → `wx.getSystemInfoSync()` returns null and
6242
+ // pages reading `.screenWidth` throw. (render does NOT read hostEnv, so
6243
+ // this is service-only; the devtools sync-api-patch reads the separate
6244
+ // __diminaSpawnContext.hostEnvSnapshot and is unaffected.)
6245
+ // menuRect stays null — getMenuButtonBoundingClientRect is served by the
6246
+ // sync-api-patch / DeviceShell capsule, not this path.
5870
6247
  hostEnv: { systemInfo: ap.hostEnv, menuRect: null }
5871
6248
  }
5872
6249
  };
@@ -8096,7 +8473,7 @@ var AppDataAccumulator = class {
8096
8473
  }
8097
8474
  /**
8098
8475
  * Apply one entry. Returns true if it was accepted (a mutation worth
8099
- * republishing), false if dropped (missing ids, or the R6 gate).
8476
+ * republishing), false if dropped (missing ids, or the init-gate).
8100
8477
  */
8101
8478
  apply(input) {
8102
8479
  if (!input.bridgeId || !input.moduleId) return false;
@@ -8244,10 +8621,35 @@ function setupSimulatorCurrentPage(host, options) {
8244
8621
  import { readFileSync } from "node:fs";
8245
8622
  import path17 from "node:path";
8246
8623
  var DEFAULT_SOURCE_PATH = "dist/render-host/render-inspect.js";
8624
+ var HIGHLIGHT_CONFIG = {
8625
+ showInfo: true,
8626
+ showRulers: false,
8627
+ showExtensionLines: false,
8628
+ contentColor: { r: 111, g: 168, b: 220, a: 0.66 },
8629
+ paddingColor: { r: 147, g: 196, b: 125, a: 0.55 },
8630
+ borderColor: { r: 255, g: 229, b: 153, a: 0.66 },
8631
+ marginColor: { r: 246, g: 178, b: 107, a: 0.66 }
8632
+ };
8633
+ var HOVER_OBJECT_GROUP = "render-inspect-hover";
8634
+ var ENABLE_HANDSHAKE_TIMEOUT_MS = 500;
8635
+ function withTimeout(p, ms) {
8636
+ return new Promise((resolve) => {
8637
+ const timer = setTimeout(resolve, ms);
8638
+ p.then(() => {
8639
+ clearTimeout(timer);
8640
+ resolve();
8641
+ }, () => {
8642
+ clearTimeout(timer);
8643
+ resolve();
8644
+ });
8645
+ });
8646
+ }
8247
8647
  function createRenderInspector(options = {}) {
8248
8648
  const loadSource = options.loadSource ?? (() => readFileSync(path17.join(devtoolsPackageRoot, DEFAULT_SOURCE_PATH), "utf8"));
8249
8649
  let cachedSource = null;
8250
8650
  const injected = /* @__PURE__ */ new Set();
8651
+ const selfAttached = /* @__PURE__ */ new Set();
8652
+ const enablePromises = /* @__PURE__ */ new Map();
8251
8653
  function source() {
8252
8654
  if (cachedSource === null) cachedSource = loadSource();
8253
8655
  return cachedSource;
@@ -8269,10 +8671,14 @@ function createRenderInspector(options = {}) {
8269
8671
  return false;
8270
8672
  }
8271
8673
  injected.add(wc.id);
8674
+ const forget = () => {
8675
+ injected.delete(wc.id);
8676
+ enablePromises.delete(wc.id);
8677
+ };
8272
8678
  if (options.connections) {
8273
- options.connections.acquire(wc).own(() => injected.delete(wc.id));
8679
+ options.connections.acquire(wc).own(forget);
8274
8680
  } else {
8275
- wc.once("destroyed", () => injected.delete(wc.id));
8681
+ wc.once("destroyed", forget);
8276
8682
  }
8277
8683
  return true;
8278
8684
  }
@@ -8291,22 +8697,97 @@ function createRenderInspector(options = {}) {
8291
8697
  async function highlight(wc, sid) {
8292
8698
  if (wc.isDestroyed()) return null;
8293
8699
  if (!await ensureInjected(wc)) return null;
8700
+ let inspection;
8294
8701
  try {
8295
8702
  const result = await wc.executeJavaScript(
8296
8703
  `window.__diminaRenderInspect ? window.__diminaRenderInspect.highlightElement(${JSON.stringify(sid)}) : null`
8297
8704
  );
8298
- return result ?? null;
8705
+ inspection = result ?? null;
8299
8706
  } catch {
8300
8707
  return null;
8301
8708
  }
8709
+ if (!inspection) return null;
8710
+ await drawNativeHighlight(wc, sid).catch(() => {
8711
+ });
8712
+ return inspection;
8713
+ }
8714
+ async function drawNativeHighlight(wc, sid) {
8715
+ if (wc.isDestroyed()) return;
8716
+ if (!ensureGuestDebugger(wc)) return;
8717
+ await withTimeout(ensureDomainsEnabled(wc), ENABLE_HANDSHAKE_TIMEOUT_MS);
8718
+ if (wc.isDestroyed()) return;
8719
+ const expression = `window.__diminaRenderInspect && window.__diminaRenderInspect.elementFor(${JSON.stringify(sid)})`;
8720
+ try {
8721
+ const evaluated = await wc.debugger.sendCommand("Runtime.evaluate", {
8722
+ expression,
8723
+ returnByValue: false,
8724
+ objectGroup: HOVER_OBJECT_GROUP
8725
+ });
8726
+ const objectId = evaluated?.result?.objectId;
8727
+ if (!objectId) return;
8728
+ await wc.debugger.sendCommand("Overlay.highlightNode", {
8729
+ objectId,
8730
+ highlightConfig: HIGHLIGHT_CONFIG
8731
+ });
8732
+ } finally {
8733
+ wc.debugger.sendCommand("Runtime.releaseObjectGroup", { objectGroup: HOVER_OBJECT_GROUP }).catch(() => {
8734
+ });
8735
+ }
8736
+ }
8737
+ function ensureGuestDebugger(wc) {
8738
+ try {
8739
+ if (wc.debugger.isAttached()) return true;
8740
+ } catch {
8741
+ return false;
8742
+ }
8743
+ try {
8744
+ wc.debugger.attach("1.3");
8745
+ trackSelfAttached(wc);
8746
+ return true;
8747
+ } catch {
8748
+ try {
8749
+ return wc.debugger.isAttached();
8750
+ } catch {
8751
+ return false;
8752
+ }
8753
+ }
8754
+ }
8755
+ function trackSelfAttached(wc) {
8756
+ if (selfAttached.has(wc.id)) return;
8757
+ selfAttached.add(wc.id);
8758
+ const detach = () => {
8759
+ selfAttached.delete(wc.id);
8760
+ try {
8761
+ if (!wc.isDestroyed() && wc.debugger.isAttached()) wc.debugger.detach();
8762
+ } catch {
8763
+ }
8764
+ };
8765
+ if (options.connections) options.connections.acquire(wc).own(detach);
8766
+ else {
8767
+ try {
8768
+ wc.once("destroyed", () => selfAttached.delete(wc.id));
8769
+ } catch {
8770
+ }
8771
+ }
8772
+ }
8773
+ function ensureDomainsEnabled(wc) {
8774
+ const cached = enablePromises.get(wc.id);
8775
+ if (cached) return cached;
8776
+ wc.debugger.sendCommand("CSS.enable").catch(() => {
8777
+ });
8778
+ const handshake = wc.debugger.sendCommand("DOM.enable").then(() => {
8779
+ if (wc.isDestroyed()) return;
8780
+ return wc.debugger.sendCommand("Overlay.enable");
8781
+ }).then(() => void 0).catch(() => {
8782
+ enablePromises.delete(wc.id);
8783
+ });
8784
+ enablePromises.set(wc.id, handshake);
8785
+ return handshake;
8302
8786
  }
8303
8787
  async function unhighlight(wc) {
8304
8788
  if (wc.isDestroyed()) return;
8305
- if (!await ensureInjected(wc)) return;
8306
8789
  try {
8307
- await wc.executeJavaScript(
8308
- "window.__diminaRenderInspect && window.__diminaRenderInspect.unhighlightElement && window.__diminaRenderInspect.unhighlightElement()"
8309
- );
8790
+ await wc.debugger.sendCommand("Overlay.hideHighlight");
8310
8791
  } catch {
8311
8792
  }
8312
8793
  }
@@ -8379,7 +8860,7 @@ function resolveVPath(url) {
8379
8860
  }
8380
8861
 
8381
8862
  // src/main/services/simulator-temp-files/disk.ts
8382
- import fs8 from "node:fs/promises";
8863
+ import fs9 from "node:fs/promises";
8383
8864
  import path19 from "node:path";
8384
8865
  var EXT_MIME = {
8385
8866
  ".txt": "text/plain",
@@ -8460,7 +8941,7 @@ function etagOf(mtimeMs, size) {
8460
8941
  return `W/"${Math.floor(mtimeMs)}-${size}"`;
8461
8942
  }
8462
8943
  async function readDiskFile(realPath, opts) {
8463
- const handle = await fs8.open(realPath, "r");
8944
+ const handle = await fs9.open(realPath, "r");
8464
8945
  try {
8465
8946
  const st = await handle.stat();
8466
8947
  const totalSize = st.size;
@@ -8515,15 +8996,15 @@ async function readDiskFile(realPath, opts) {
8515
8996
  }
8516
8997
  async function writeDiskFile(realPath, bytes) {
8517
8998
  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);
8999
+ await fs9.mkdir(path19.dirname(realPath), { recursive: true });
9000
+ await fs9.writeFile(realPath, buf);
8520
9001
  }
8521
9002
  async function readDiskDir(realPath) {
8522
- const entries = await fs8.readdir(realPath);
9003
+ const entries = await fs9.readdir(realPath);
8523
9004
  return entries;
8524
9005
  }
8525
9006
  async function statDiskFile(realPath) {
8526
- const st = await fs8.stat(realPath);
9007
+ const st = await fs9.stat(realPath);
8527
9008
  return {
8528
9009
  size: st.size,
8529
9010
  mtime: Math.floor(st.mtimeMs),
@@ -8656,14 +9137,14 @@ async function handleDifileRequest(ctx, req) {
8656
9137
  }
8657
9138
 
8658
9139
  // src/main/services/simulator-temp-files/fs-channels.ts
8659
- import fs9 from "node:fs/promises";
9140
+ import fs10 from "node:fs/promises";
8660
9141
  import path20 from "node:path";
8661
9142
  async function enforceSandbox(realPath) {
8662
9143
  if (typeof realPath !== "string" || realPath.length === 0) {
8663
9144
  throw new Error("sandbox: realPath must be a non-empty string");
8664
9145
  }
8665
9146
  const base = sandboxBase();
8666
- const baseReal = await fs9.realpath(base).catch(() => base);
9147
+ const baseReal = await fs10.realpath(base).catch(() => base);
8667
9148
  const normalized = path20.normalize(realPath);
8668
9149
  if (normalized !== base && !normalized.startsWith(base + path20.sep)) {
8669
9150
  throw new Error("sandbox: realPath escapes the user-data base");
@@ -8671,7 +9152,7 @@ async function enforceSandbox(realPath) {
8671
9152
  let probe = normalized;
8672
9153
  while (probe !== path20.parse(probe).root) {
8673
9154
  try {
8674
- const resolved = await fs9.realpath(probe);
9155
+ const resolved = await fs10.realpath(probe);
8675
9156
  if (resolved !== baseReal && !resolved.startsWith(baseReal + path20.sep)) {
8676
9157
  throw new Error("sandbox: realPath escapes the user-data base via symlink");
8677
9158
  }
@@ -8711,12 +9192,12 @@ async function handleFsReaddir(req) {
8711
9192
  }
8712
9193
  async function handleFsUnlink(req) {
8713
9194
  const safe = await enforceSandbox(req.realPath);
8714
- await fs9.unlink(safe);
9195
+ await fs10.unlink(safe);
8715
9196
  return { ok: true };
8716
9197
  }
8717
9198
  async function handleFsMkdir(req) {
8718
9199
  const safe = await enforceSandbox(req.realPath);
8719
- await fs9.mkdir(safe, { recursive: !!req.recursive });
9200
+ await fs10.mkdir(safe, { recursive: !!req.recursive });
8720
9201
  return { ok: true };
8721
9202
  }
8722
9203
 
@@ -9149,9 +9630,12 @@ function wireAppWindowEvents(config, instance) {
9149
9630
  onResize: () => context.views.repositionAll(),
9150
9631
  onClose: async (e) => {
9151
9632
  if (isAppQuitting()) return;
9633
+ if (closing) {
9634
+ e.preventDefault();
9635
+ return;
9636
+ }
9152
9637
  if (!context.workspace.hasActiveSession()) return;
9153
9638
  e.preventDefault();
9154
- if (closing) return;
9155
9639
  closing = true;
9156
9640
  try {
9157
9641
  if (config.onBeforeClose) {
@@ -9171,7 +9655,7 @@ function enableDevRendererAutoReload(rendererDir2) {
9171
9655
  });
9172
9656
  }
9173
9657
  let reloadTimer = null;
9174
- const watcher = fs10.watch(rendererDir2, { recursive: true }, () => {
9658
+ const watcher = fs11.watch(rendererDir2, { recursive: true }, () => {
9175
9659
  if (reloadTimer) clearTimeout(reloadTimer);
9176
9660
  reloadTimer = setTimeout(() => {
9177
9661
  for (const win of BrowserWindow8.getAllWindows()) {