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