@dimina-kit/devtools 0.4.0-dev.20260616102751 → 0.4.0-dev.20260624040247
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -25
- package/dist/main/app/app.js +14 -2
- package/dist/main/index.bundle.js +559 -111
- package/dist/main/ipc/bridge-router.js +57 -9
- package/dist/main/ipc/projects.js +1 -1
- package/dist/main/ipc/simulator.js +1 -1
- package/dist/main/runtime/devtools-backend.d.ts +1 -1
- package/dist/main/runtime/devtools-backend.js +1 -1
- package/dist/main/runtime/miniapp-runtime.d.ts +5 -5
- package/dist/main/services/console-forward/index.js +8 -1
- package/dist/main/services/elements-forward/index.js +36 -12
- package/dist/main/services/layout/index.d.ts +12 -0
- package/dist/main/services/layout/index.js +14 -2
- package/dist/main/services/network-forward/index.d.ts +5 -6
- package/dist/main/services/network-forward/index.js +9 -10
- package/dist/main/services/notifications/renderer-notifier.d.ts +3 -4
- package/dist/main/services/projects/index.js +1 -1
- package/dist/main/services/projects/types.d.ts +1 -1
- package/dist/main/services/projects/types.js +1 -1
- package/dist/main/services/service-console/console-api.d.ts +77 -0
- package/dist/main/services/service-console/console-api.js +103 -0
- package/dist/main/services/service-console/index.d.ts +47 -0
- package/dist/main/services/service-console/index.js +139 -0
- package/dist/main/services/service-host-pool/pool.d.ts +2 -3
- package/dist/main/services/simulator-storage/index.d.ts +2 -4
- package/dist/main/services/simulator-storage/index.js +2 -4
- package/dist/main/services/simulator-temp-files/index.js +2 -2
- package/dist/main/services/update/update-manager.d.ts +1 -1
- package/dist/main/services/update/update-manager.js +1 -1
- package/dist/main/services/views/host-toolbar-session-runtime.d.ts +4 -4
- package/dist/main/services/views/host-toolbar-session-runtime.js +4 -4
- package/dist/main/services/views/view-manager.d.ts +24 -1
- package/dist/main/services/views/view-manager.js +120 -41
- package/dist/main/services/workbench-context.d.ts +7 -6
- package/dist/main/services/workspace/workspace-service.js +11 -0
- package/dist/main/windows/service-host-window/create.d.ts +1 -0
- package/dist/main/windows/service-host-window/create.js +1 -0
- package/dist/preload/runtime/host-toolbar-port.d.ts +1 -1
- package/dist/preload/runtime/host-toolbar-port.js +1 -1
- package/dist/preload/runtime/host-toolbar-runtime.d.ts +8 -7
- package/dist/preload/runtime/host-toolbar-runtime.js +8 -7
- package/dist/preload/windows/host-toolbar-runtime.cjs.map +2 -2
- package/dist/preload/windows/main.cjs.map +2 -2
- package/dist/preload/windows/simulator.cjs +1 -1
- package/dist/preload/windows/simulator.cjs.map +1 -1
- package/dist/preload/windows/simulator.js +1 -1
- package/dist/renderer/assets/index-PtsdWlGu.js +50 -0
- package/dist/renderer/assets/{input-B8NDCYv3.js → input-DiCxYlqY.js} +2 -2
- package/dist/renderer/assets/{ipc-transport-DhrajiC5.js → ipc-transport-DSBmaWpJ.js} +1 -1
- package/dist/renderer/assets/ipc-transport-DZxf2YoM.css +1 -0
- package/dist/renderer/assets/{popover-DbZOg03s.js → popover-CyL_uffj.js} +2 -2
- package/dist/renderer/assets/{select-pW3Ysc9T.js → select-BSwvDMtA.js} +2 -2
- package/dist/renderer/assets/settings-Ch8XbFxf.js +2 -0
- package/dist/renderer/assets/{settings-api-BSKzKiWd.js → settings-api-e9ILe2JO.js} +2 -2
- package/dist/renderer/assets/{workbenchSettings-BGxjl25x.js → workbenchSettings-DZsHf1g_.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 +11 -5
- package/dist/renderer/entries/workbench-settings/index.html +4 -4
- package/dist/service-host/preload.cjs +13 -15
- package/dist/service-host/sourcemap-rewrite.cjs +21 -12
- package/dist/shared/appdata-accumulator.d.ts +2 -2
- package/dist/shared/appdata-accumulator.js +3 -3
- package/dist/shared/constants.d.ts +1 -1
- package/dist/shared/constants.js +1 -1
- package/dist/shared/ipc-channels.d.ts +3 -3
- package/dist/shared/ipc-channels.js +3 -3
- package/dist/shared/open-in-editor.d.ts +20 -1
- package/dist/shared/open-in-editor.js +246 -3
- package/dist/shared/vpath.d.ts +2 -2
- package/dist/shared/vpath.js +2 -2
- package/dist/simulator/assets/{device-shell-BWX7Yopg.js → device-shell-TtcSXwhb.js} +2 -2
- package/dist/simulator/assets/{simulator-sf-D0mhw.js → simulator-CtyXt_4V.js} +3 -3
- package/dist/simulator/simulator.html +1 -1
- package/package.json +8 -6
- package/dist/renderer/assets/index-Z4Rh52G8.js +0 -50
- package/dist/renderer/assets/ipc-transport-9agi76dX.css +0 -1
- package/dist/renderer/assets/settings-CZJOHt8b.js +0 -2
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { ipcMain, nativeTheme, shell, WebContentsView, webContents } from 'electron';
|
|
2
|
+
import fs from 'node:fs';
|
|
2
3
|
import path from 'path';
|
|
3
4
|
import { cjsSiblingPreloadPath, mainPreloadPath } from '../../utils/paths.js';
|
|
4
5
|
import { simDeskBg } from '../../utils/theme.js';
|
|
5
6
|
import { applyNavigationHardening, handleWindowOpenExternal, } from '../../windows/navigation-hardening.js';
|
|
6
7
|
import { SimulatorCustomApiBridgeChannel } from '../../../shared/ipc-channels.js';
|
|
7
|
-
import {
|
|
8
|
+
import { buildDevtoolsProjectSourceLinksScript, decodeOpenInEditorUrl, projectSourceContextFromServiceHostUrl, resourceUrlToProjectRelativePath, } from '../../../shared/open-in-editor.js';
|
|
8
9
|
import { createSafeAreaController } from '../safe-area/index.js';
|
|
9
10
|
import { buildCustomizeTabsScript } from './devtools-tabs.js';
|
|
10
11
|
import { installElementsForward } from '../elements-forward/index.js';
|
|
12
|
+
import { installServiceConsoleForward } from '../service-console/index.js';
|
|
11
13
|
import * as layout from '../layout/index.js';
|
|
12
14
|
import { handleCustomApiBridgeRequest, } from '../simulator/custom-apis.js';
|
|
13
15
|
import { configureMiniappSession, miniappPartition } from './miniapp-partition.js';
|
|
@@ -15,6 +17,35 @@ import { acquireHostToolbarSessionRuntime, releaseHostToolbarSessionRuntime, } f
|
|
|
15
17
|
import { createHostToolbarPortChannel, } from './host-toolbar-port-channel.js';
|
|
16
18
|
import { parseRoute } from '../../../shared/simulator-route.js';
|
|
17
19
|
import { HEADER_H, HOST_TOOLBAR_RUNTIME_MARKER } from '../../../shared/constants.js';
|
|
20
|
+
/**
|
|
21
|
+
* Resolve a DevTools source request against the service-host URL that created
|
|
22
|
+
* the inspected app. Its pkgRoot is authoritative; the workspace is only a
|
|
23
|
+
* stale-session consistency guard.
|
|
24
|
+
*/
|
|
25
|
+
export function resolveProjectEditorTarget(serviceHostUrl, activeProjectRoot, req, isFile = (absolutePath) => fs.statSync(absolutePath).isFile()) {
|
|
26
|
+
const sourceContext = projectSourceContextFromServiceHostUrl(serviceHostUrl, activeProjectRoot);
|
|
27
|
+
if (!sourceContext)
|
|
28
|
+
return null;
|
|
29
|
+
const rel = resourceUrlToProjectRelativePath(req.url, sourceContext);
|
|
30
|
+
if (!rel)
|
|
31
|
+
return null;
|
|
32
|
+
const absolute = path.resolve(sourceContext.projectRoot, ...rel.split('/'));
|
|
33
|
+
const fromRoot = path.relative(path.resolve(sourceContext.projectRoot), absolute);
|
|
34
|
+
if (!fromRoot || fromRoot.startsWith('..') || path.isAbsolute(fromRoot))
|
|
35
|
+
return null;
|
|
36
|
+
try {
|
|
37
|
+
if (!isFile(absolute))
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
path: rel,
|
|
45
|
+
line: typeof req.line === 'number' ? req.line + 1 : undefined,
|
|
46
|
+
column: typeof req.column === 'number' ? req.column + 1 : undefined,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
18
49
|
/**
|
|
19
50
|
* Build a ViewManager bound to the given context. The returned object is the
|
|
20
51
|
* only component allowed to instantiate or add/remove overlay WebContentsViews.
|
|
@@ -36,6 +67,8 @@ export function createViewManager(ctx) {
|
|
|
36
67
|
// while `simulatorView` above hosts its DevTools in the right panel region.
|
|
37
68
|
let nativeSimulatorView = null;
|
|
38
69
|
let nativeSimulatorViewAdded = false;
|
|
70
|
+
let nativeSimulatorProjectPath = null;
|
|
71
|
+
let settleNativeSimulatorReady = null;
|
|
39
72
|
// Model A: the renderer's measured inner-screen rect is the SOLE authority for
|
|
40
73
|
// the native simulator WCV bounds (see docs/simulator-render-architecture.md).
|
|
41
74
|
// Cache the last rect so a report that lands before attachNativeSimulator (the
|
|
@@ -72,6 +105,10 @@ export function createViewManager(ctx) {
|
|
|
72
105
|
// CDP traffic at the active render guest). Installed in
|
|
73
106
|
// `attachNativeSimulatorDevtoolsHost`, stopped on detach / host destroyed.
|
|
74
107
|
let stopElementsForward = null;
|
|
108
|
+
// Disposer for the service-layer console capture (CDP `consoleAPICalled` on the
|
|
109
|
+
// service host wc → console fan-out). Installed when the DevTools is pointed at
|
|
110
|
+
// a service host, stopped when that source is closed / swapped.
|
|
111
|
+
let stopServiceConsole = null;
|
|
75
112
|
let nativeDevtoolsRetryTimer = null;
|
|
76
113
|
let nativeDevtoolsRetryToken = 0;
|
|
77
114
|
// Renderer-driven overlay bounds for the simulator DevTools view — the SOLE
|
|
@@ -152,6 +189,9 @@ export function createViewManager(ctx) {
|
|
|
152
189
|
if (!simulatorViewAdded) {
|
|
153
190
|
ctx.windows.mainWindow.contentView.addChildView(simulatorView);
|
|
154
191
|
simulatorViewAdded = true;
|
|
192
|
+
// Re-attaching this base overlay moved it to the top of the z-stack; keep
|
|
193
|
+
// any open settings/popover above it.
|
|
194
|
+
raiseTopOverlays();
|
|
155
195
|
}
|
|
156
196
|
simulatorView.setBounds(bounds);
|
|
157
197
|
}
|
|
@@ -386,6 +426,11 @@ export function createViewManager(ctx) {
|
|
|
386
426
|
function closeNativeDevtoolsSource() {
|
|
387
427
|
const source = nativeDevtoolsSourceWc;
|
|
388
428
|
nativeDevtoolsSourceWc = null;
|
|
429
|
+
try {
|
|
430
|
+
stopServiceConsole?.();
|
|
431
|
+
}
|
|
432
|
+
catch { /* already stopped */ }
|
|
433
|
+
stopServiceConsole = null;
|
|
389
434
|
if (!source || source.isDestroyed())
|
|
390
435
|
return;
|
|
391
436
|
try {
|
|
@@ -416,43 +461,17 @@ export function createViewManager(ctx) {
|
|
|
416
461
|
// `devtools-open-url` on the inspected (service-host) wc, which we decode,
|
|
417
462
|
// map to a project-relative path, and broadcast to the renderer's Monaco.
|
|
418
463
|
const openInEditorWiredWcIds = new Set();
|
|
419
|
-
function injectOpenResourceHandler(devtoolsWc) {
|
|
464
|
+
function injectOpenResourceHandler(serviceWc, devtoolsWc) {
|
|
420
465
|
// `setOpenResourceHandler` is the official Chromium DevTools hook IDEs use
|
|
421
466
|
// to route source-link clicks to an external editor. We poll for the host
|
|
422
467
|
// (it appears once the front-end finishes bootstrapping, like `UI` above)
|
|
423
468
|
// and register a handler that re-emits an encoded sentinel via
|
|
424
469
|
// `openInNewTab` → Electron `devtools-open-url`. Best-effort: wrapped in
|
|
425
470
|
// try/catch and a bounded poll so a missing API never throws.
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
let tries = 0
|
|
431
|
-
const timer = setInterval(() => {
|
|
432
|
-
tries++
|
|
433
|
-
try {
|
|
434
|
-
const Host = globalThis.Host
|
|
435
|
-
const host = Host && Host.InspectorFrontendHost
|
|
436
|
-
if (host && typeof host.setOpenResourceHandler === 'function'
|
|
437
|
-
&& typeof host.openInNewTab === 'function') {
|
|
438
|
-
host.setOpenResourceHandler((url, lineNumber, columnNumber) => {
|
|
439
|
-
try {
|
|
440
|
-
const p = new URLSearchParams()
|
|
441
|
-
p.set('u', String(url))
|
|
442
|
-
if (typeof lineNumber === 'number') p.set('l', String(lineNumber))
|
|
443
|
-
if (typeof columnNumber === 'number') p.set('c', String(columnNumber))
|
|
444
|
-
host.openInNewTab(${scheme} + ':?' + p.toString())
|
|
445
|
-
} catch (_) {}
|
|
446
|
-
})
|
|
447
|
-
clearInterval(timer)
|
|
448
|
-
return
|
|
449
|
-
}
|
|
450
|
-
} catch (_) {}
|
|
451
|
-
if (tries > 80) clearInterval(timer)
|
|
452
|
-
}, 50)
|
|
453
|
-
} catch (_) {}
|
|
454
|
-
})()
|
|
455
|
-
`).catch(() => { });
|
|
471
|
+
const sourceContext = projectSourceContextFromServiceHostUrl(serviceWc.getURL(), ctx.workspace?.getProjectPath?.());
|
|
472
|
+
if (!sourceContext)
|
|
473
|
+
return;
|
|
474
|
+
devtoolsWc.executeJavaScript(buildDevtoolsProjectSourceLinksScript(sourceContext)).catch(() => { });
|
|
456
475
|
}
|
|
457
476
|
// ── DevTools tab customization: keep only Elements/Console/Network ──────────
|
|
458
477
|
// Inject into the same DevTools front-end host wc that the console/network
|
|
@@ -489,10 +508,10 @@ export function createViewManager(ctx) {
|
|
|
489
508
|
// wc is recreated per simulatorView, so a fresh host needs the handler set.
|
|
490
509
|
if (!devtoolsWc.isDestroyed()) {
|
|
491
510
|
if (devtoolsWc.isLoading()) {
|
|
492
|
-
devtoolsWc.once('dom-ready', () => injectOpenResourceHandler(devtoolsWc));
|
|
511
|
+
devtoolsWc.once('dom-ready', () => injectOpenResourceHandler(serviceWc, devtoolsWc));
|
|
493
512
|
}
|
|
494
513
|
else {
|
|
495
|
-
injectOpenResourceHandler(devtoolsWc);
|
|
514
|
+
injectOpenResourceHandler(serviceWc, devtoolsWc);
|
|
496
515
|
}
|
|
497
516
|
}
|
|
498
517
|
// Attach the `devtools-open-url` decoder to the inspected service wc once
|
|
@@ -505,13 +524,10 @@ export function createViewManager(ctx) {
|
|
|
505
524
|
const req = decodeOpenInEditorUrl(url);
|
|
506
525
|
if (!req)
|
|
507
526
|
return; // not our sentinel — leave it to Electron's default path
|
|
508
|
-
const
|
|
509
|
-
if (!
|
|
527
|
+
const target = resolveProjectEditorTarget(serviceWc.getURL(), ctx.workspace?.getProjectPath?.(), req);
|
|
528
|
+
if (!target)
|
|
510
529
|
return;
|
|
511
|
-
|
|
512
|
-
const line = typeof req.line === 'number' ? req.line + 1 : undefined;
|
|
513
|
-
const column = typeof req.column === 'number' ? req.column + 1 : undefined;
|
|
514
|
-
ctx.notify.editorOpenFile({ path: rel, line, column });
|
|
530
|
+
ctx.notify.editorOpenFile(target);
|
|
515
531
|
};
|
|
516
532
|
serviceWc.on('devtools-open-url', onOpenUrl);
|
|
517
533
|
// Consolidate teardown onto the connection layer (foundation.md §4 / P2),
|
|
@@ -557,6 +573,19 @@ export function createViewManager(ctx) {
|
|
|
557
573
|
// Re-applied on every re-point so a service-host pool swap (fresh
|
|
558
574
|
// openDevTools) re-asserts the custom tab bar.
|
|
559
575
|
customizeDevtoolsTabs(simulatorView.webContents);
|
|
576
|
+
// Capture service-layer console via CDP (NOT a preload monkeypatch, which
|
|
577
|
+
// would clobber native source attribution) and feed it to the console
|
|
578
|
+
// fan-out (automation `App.logAdded`). Bound to THIS service wc; replaced
|
|
579
|
+
// on the next re-point via closeNativeDevtoolsSource.
|
|
580
|
+
try {
|
|
581
|
+
stopServiceConsole?.();
|
|
582
|
+
}
|
|
583
|
+
catch { /* already stopped */ }
|
|
584
|
+
stopServiceConsole = installServiceConsoleForward({
|
|
585
|
+
serviceWc: next,
|
|
586
|
+
connections: ctx.connections,
|
|
587
|
+
emit: (entry) => ctx.consoleForwarder?.emit(entry),
|
|
588
|
+
}).stop;
|
|
560
589
|
return true;
|
|
561
590
|
}
|
|
562
591
|
catch {
|
|
@@ -767,8 +796,12 @@ export function createViewManager(ctx) {
|
|
|
767
796
|
function attachNativeSimulator(simulatorUrl, _simWidth) {
|
|
768
797
|
if (!ctx.preloadPath) {
|
|
769
798
|
console.error('[workbench] attachNativeSimulator — preloadPath unset; cannot mount native simulator');
|
|
770
|
-
return;
|
|
799
|
+
return Promise.resolve();
|
|
771
800
|
}
|
|
801
|
+
// Unblock a superseded IPC invocation. Its renderer effect cleanup marks
|
|
802
|
+
// that generation cancelled, so this cannot schedule a stale capture.
|
|
803
|
+
settleNativeSimulatorReady?.();
|
|
804
|
+
settleNativeSimulatorReady = null;
|
|
772
805
|
// Tear down any previous native simulator view (relaunch / re-open).
|
|
773
806
|
if (nativeSimulatorView) {
|
|
774
807
|
detachNativeCustomApiBridge();
|
|
@@ -787,6 +820,10 @@ export function createViewManager(ctx) {
|
|
|
787
820
|
catch { /* ignore */ }
|
|
788
821
|
nativeSimulatorView = null;
|
|
789
822
|
}
|
|
823
|
+
nativeSimulatorProjectPath = null;
|
|
824
|
+
const ready = new Promise((resolve) => {
|
|
825
|
+
settleNativeSimulatorReady = resolve;
|
|
826
|
+
});
|
|
790
827
|
// Derive THIS project's session partition from the simulator URL's appId so
|
|
791
828
|
// its cookies/localStorage/cache are isolated from every other project (P0
|
|
792
829
|
// debt). Same project → same partition (storage survives a relaunch);
|
|
@@ -814,6 +851,7 @@ export function createViewManager(ctx) {
|
|
|
814
851
|
},
|
|
815
852
|
});
|
|
816
853
|
nativeSimulatorView = view;
|
|
854
|
+
nativeSimulatorProjectPath = ctx.workspace?.getProjectPath() || null;
|
|
817
855
|
// Paint the WCV surface the themed desk color (simDeskBg(): dark #121212 /
|
|
818
856
|
// light #e8e8e8) so a height-resize that grows the region never flashes a
|
|
819
857
|
// mismatched strip before DeviceShell's desk repaints — the WCV, the desk,
|
|
@@ -903,6 +941,15 @@ export function createViewManager(ctx) {
|
|
|
903
941
|
e.preventDefault();
|
|
904
942
|
}
|
|
905
943
|
});
|
|
944
|
+
// The outer DeviceShell loading is insufficient: slow projects attach a
|
|
945
|
+
// render guest later. Resolve the renderer's attach IPC only after that
|
|
946
|
+
// first mini-app page has completed its own document load.
|
|
947
|
+
guestWc.once('did-finish-load', () => {
|
|
948
|
+
if (nativeSimulatorView !== view || simWc.isDestroyed())
|
|
949
|
+
return;
|
|
950
|
+
settleNativeSimulatorReady?.();
|
|
951
|
+
settleNativeSimulatorReady = null;
|
|
952
|
+
});
|
|
906
953
|
// A render-host guest only attaches after a spawn, so the service window
|
|
907
954
|
// exists by now — (re)point the right-panel DevTools at it. Belt-and-braces
|
|
908
955
|
// with the `onRenderEvent` path in case its emit lost the attach race.
|
|
@@ -958,8 +1005,11 @@ export function createViewManager(ctx) {
|
|
|
958
1005
|
// (The UI/view layer's Elements equivalent is the native WXML panel +
|
|
959
1006
|
// render-guest highlight chain, which targets the active render guest.)
|
|
960
1007
|
attachNativeSimulatorDevtoolsHost();
|
|
1008
|
+
return ready;
|
|
961
1009
|
}
|
|
962
1010
|
function detachSimulator() {
|
|
1011
|
+
settleNativeSimulatorReady?.();
|
|
1012
|
+
settleNativeSimulatorReady = null;
|
|
963
1013
|
stopFollowingNativeServiceHost();
|
|
964
1014
|
detachNativeCustomApiBridge();
|
|
965
1015
|
// Stop Elements forwarding (detaches only the debugger sessions IT attached;
|
|
@@ -1003,6 +1053,7 @@ export function createViewManager(ctx) {
|
|
|
1003
1053
|
simulatorView = null;
|
|
1004
1054
|
simulatorViewAdded = false;
|
|
1005
1055
|
simulatorWebContentsId = null;
|
|
1056
|
+
nativeSimulatorProjectPath = null;
|
|
1006
1057
|
// Drop the renderer-published rect so a stale "hidden" override doesn't
|
|
1007
1058
|
// suppress the next view before its renderer republishes.
|
|
1008
1059
|
simulatorBoundsOverride = null;
|
|
@@ -1038,6 +1089,11 @@ export function createViewManager(ctx) {
|
|
|
1038
1089
|
// Overlay loads mainPreloadPath, so the same navigation rules as the
|
|
1039
1090
|
// main window apply — see navigation-hardening.ts.
|
|
1040
1091
|
applyNavigationHardening(settingsView.webContents, ctx.rendererDir);
|
|
1092
|
+
// Transparent backing: the settings view now spans the whole content area
|
|
1093
|
+
// (computeSettingsBounds) and its renderer paints a transparent backdrop +
|
|
1094
|
+
// an opaque right-side panel, so the underlying editor/simulator show
|
|
1095
|
+
// through and a backdrop click closes the overlay (mirrors the popover).
|
|
1096
|
+
settingsView.setBackgroundColor('#00000000');
|
|
1041
1097
|
await settingsView.webContents.loadFile(path.join(ctx.rendererDir, 'entries/settings/index.html'));
|
|
1042
1098
|
}
|
|
1043
1099
|
if (!settingsViewAdded) {
|
|
@@ -1055,6 +1111,25 @@ export function createViewManager(ctx) {
|
|
|
1055
1111
|
settingsViewAdded = false;
|
|
1056
1112
|
}
|
|
1057
1113
|
}
|
|
1114
|
+
// Keep the TOP-tier overlays (settings, popover) above the BASE-tier native
|
|
1115
|
+
// overlays (the native simulator WCV + the console/DevTools WCV). Native
|
|
1116
|
+
// overlays are z-ordered by `addChildView` insertion order — the last-added
|
|
1117
|
+
// sits on top — so RE-attaching a base overlay while a top overlay is open
|
|
1118
|
+
// (e.g. the simulator re-shows on a tab switch, or the console bounds
|
|
1119
|
+
// republish re-adds it) would move the base ABOVE the open settings/popover
|
|
1120
|
+
// and occlude it. Whenever a base overlay is (re)added, re-append the open top
|
|
1121
|
+
// overlays so they stay on top. Settings is re-appended before popover so a
|
|
1122
|
+
// simultaneously-open popover ends up topmost. A no-op when neither is open
|
|
1123
|
+
// (pinned by the z-order guard test).
|
|
1124
|
+
function raiseTopOverlays() {
|
|
1125
|
+
if (ctx.windows.mainWindow.isDestroyed())
|
|
1126
|
+
return;
|
|
1127
|
+
const cv = ctx.windows.mainWindow.contentView;
|
|
1128
|
+
if (settingsView && settingsViewAdded)
|
|
1129
|
+
cv.addChildView(settingsView);
|
|
1130
|
+
if (popoverView)
|
|
1131
|
+
cv.addChildView(popoverView);
|
|
1132
|
+
}
|
|
1058
1133
|
function showPopover(data) {
|
|
1059
1134
|
hidePopover();
|
|
1060
1135
|
const popover = new WebContentsView({
|
|
@@ -1141,6 +1216,9 @@ export function createViewManager(ctx) {
|
|
|
1141
1216
|
if (!nativeSimulatorViewAdded) {
|
|
1142
1217
|
ctx.windows.mainWindow.contentView.addChildView(nativeSimulatorView);
|
|
1143
1218
|
nativeSimulatorViewAdded = true;
|
|
1219
|
+
// Re-attaching this base overlay moved it to the top of the z-stack; keep
|
|
1220
|
+
// any open settings/popover above it.
|
|
1221
|
+
raiseTopOverlays();
|
|
1144
1222
|
}
|
|
1145
1223
|
nativeSimulatorView.setBounds(p.bounds);
|
|
1146
1224
|
const simWc = nativeSimulatorView.webContents;
|
|
@@ -1186,6 +1264,7 @@ export function createViewManager(ctx) {
|
|
|
1186
1264
|
const wc = webContents.fromId(simulatorWebContentsId);
|
|
1187
1265
|
return wc && !wc.isDestroyed() ? wc : null;
|
|
1188
1266
|
},
|
|
1267
|
+
getSimulatorProjectPath: () => nativeSimulatorProjectPath,
|
|
1189
1268
|
getSettingsWebContents: () => {
|
|
1190
1269
|
if (!settingsView)
|
|
1191
1270
|
return null;
|
|
@@ -137,12 +137,13 @@ export interface WorkbenchContext {
|
|
|
137
137
|
*/
|
|
138
138
|
storageApi?: StorageApi;
|
|
139
139
|
/**
|
|
140
|
-
* Native-host console sink.
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
140
|
+
* Native-host console sink. Render-layer entries arrive from
|
|
141
|
+
* render-host/preload.cjs (`console.*` monkeypatch → `consoleLog` message);
|
|
142
|
+
* service-layer entries arrive from CDP `Runtime.consoleAPICalled`
|
|
143
|
+
* (services/service-console) so native source attribution is preserved.
|
|
144
|
+
* Owned by the `ConsoleForwarder` (set in `installBridgeRouter`), whose `emit`
|
|
145
|
+
* fans every entry out to subscribers (automation WS) AND mirrors render-layer
|
|
146
|
+
* entries into the service host's own console for the embedded DevTools.
|
|
146
147
|
*/
|
|
147
148
|
guestConsole?: {
|
|
148
149
|
emit(entry: unknown): void;
|
|
@@ -215,11 +215,22 @@ export function createWorkspaceService(ctx) {
|
|
|
215
215
|
getLastClosedProjectPath: () => lastClosedProjectPath,
|
|
216
216
|
hasActiveSession: () => currentSession !== null,
|
|
217
217
|
async captureThumbnail(projectPath) {
|
|
218
|
+
if (!currentSession || projectPath !== currentProjectPath)
|
|
219
|
+
return null;
|
|
218
220
|
const wc = ctx.views.getSimulatorWebContents();
|
|
219
221
|
if (!wc)
|
|
220
222
|
return null;
|
|
223
|
+
if (ctx.views.getSimulatorProjectPath() !== projectPath)
|
|
224
|
+
return null;
|
|
225
|
+
const session = currentSession;
|
|
221
226
|
try {
|
|
222
227
|
const image = await wc.capturePage();
|
|
228
|
+
if (currentSession !== session
|
|
229
|
+
|| currentProjectPath !== projectPath
|
|
230
|
+
|| ctx.views.getSimulatorWebContents() !== wc
|
|
231
|
+
|| ctx.views.getSimulatorProjectPath() !== projectPath) {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
223
234
|
const dataUrl = `data:image/png;base64,${image.toPNG().toString('base64')}`;
|
|
224
235
|
if (provider.saveThumbnail) {
|
|
225
236
|
await provider.saveThumbnail(projectPath, dataUrl);
|
|
@@ -52,6 +52,7 @@ export function buildServiceHostSpawnUrl(opts) {
|
|
|
52
52
|
url.searchParams.set('appId', opts.appId);
|
|
53
53
|
url.searchParams.set('pagePath', opts.pagePath);
|
|
54
54
|
url.searchParams.set('pkgRoot', opts.pkgRoot);
|
|
55
|
+
url.searchParams.set('root', opts.root || 'main');
|
|
55
56
|
url.searchParams.set('resourceBaseUrl', opts.resourceBaseUrl);
|
|
56
57
|
if (opts.hostEnvSnapshot) {
|
|
57
58
|
// Encode the resolved host-env so preload can hydrate
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* envelope stays the single waist (same posture as the main side's
|
|
20
20
|
* host-toolbar-port-channel.ts).
|
|
21
21
|
*
|
|
22
|
-
* Ordering reality this module absorbs
|
|
22
|
+
* Ordering reality this module absorbs:
|
|
23
23
|
* - The page script runs BEFORE the handshake can complete (the port is
|
|
24
24
|
* posted on did-finish-load). Page `send()`s issued before the port
|
|
25
25
|
* arrives go into a PENDING QUEUE and flush in order on handshake —
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* envelope stays the single waist (same posture as the main side's
|
|
20
20
|
* host-toolbar-port-channel.ts).
|
|
21
21
|
*
|
|
22
|
-
* Ordering reality this module absorbs
|
|
22
|
+
* Ordering reality this module absorbs:
|
|
23
23
|
* - The page script runs BEFORE the handshake can complete (the port is
|
|
24
24
|
* posted on did-finish-load). Page `send()`s issued before the port
|
|
25
25
|
* arrives go into a PENDING QUEUE and flush in order on handshake —
|
|
@@ -10,15 +10,16 @@
|
|
|
10
10
|
* - the toolbar WCV's creation injects `HOST_TOOLBAR_RUNTIME_MARKER` into its
|
|
11
11
|
* process argv via `webPreferences.additionalArguments`;
|
|
12
12
|
* - the marker is PROCESS-level (subframes of the toolbar window carry it
|
|
13
|
-
* too
|
|
14
|
-
*
|
|
13
|
+
* too), so the guard needs BOTH wings: `isMainFrame` AND
|
|
14
|
+
* `argv.includes(marker)`;
|
|
15
15
|
* - a renderer that fails the guard returns immediately with ZERO footprint
|
|
16
|
-
* (no advertiser, no listeners, no globals
|
|
16
|
+
* (no advertiser, no listeners, no globals).
|
|
17
17
|
*
|
|
18
|
-
* Why session-resident at all:
|
|
19
|
-
* `webPreferences.preload
|
|
20
|
-
* preload>)`
|
|
21
|
-
* on the session layer, the host preload and the framework runtime
|
|
18
|
+
* Why session-resident at all: riding the toolbar WCV's
|
|
19
|
+
* `webPreferences.preload` lets a host calling `setPreloadPath(<its own
|
|
20
|
+
* preload>)` REPLACE the advertiser, collapsing the strip height to 0. With the
|
|
21
|
+
* runtime on the session layer, the host preload and the framework runtime
|
|
22
|
+
* coexist.
|
|
22
23
|
*/
|
|
23
24
|
/**
|
|
24
25
|
* Pure guard predicate: should the toolbar runtime activate in a renderer with
|
|
@@ -10,15 +10,16 @@
|
|
|
10
10
|
* - the toolbar WCV's creation injects `HOST_TOOLBAR_RUNTIME_MARKER` into its
|
|
11
11
|
* process argv via `webPreferences.additionalArguments`;
|
|
12
12
|
* - the marker is PROCESS-level (subframes of the toolbar window carry it
|
|
13
|
-
* too
|
|
14
|
-
*
|
|
13
|
+
* too), so the guard needs BOTH wings: `isMainFrame` AND
|
|
14
|
+
* `argv.includes(marker)`;
|
|
15
15
|
* - a renderer that fails the guard returns immediately with ZERO footprint
|
|
16
|
-
* (no advertiser, no listeners, no globals
|
|
16
|
+
* (no advertiser, no listeners, no globals).
|
|
17
17
|
*
|
|
18
|
-
* Why session-resident at all:
|
|
19
|
-
* `webPreferences.preload
|
|
20
|
-
* preload>)`
|
|
21
|
-
* on the session layer, the host preload and the framework runtime
|
|
18
|
+
* Why session-resident at all: riding the toolbar WCV's
|
|
19
|
+
* `webPreferences.preload` lets a host calling `setPreloadPath(<its own
|
|
20
|
+
* preload>)` REPLACE the advertiser, collapsing the strip height to 0. With the
|
|
21
|
+
* runtime on the session layer, the host preload and the framework runtime
|
|
22
|
+
* coexist.
|
|
22
23
|
*/
|
|
23
24
|
import { HOST_TOOLBAR_RUNTIME_MARKER } from '../../shared/constants.js';
|
|
24
25
|
import { installHostToolbarAdvertiserWhenReady } from './host-toolbar-advertiser.js';
|