@dimina-kit/devtools 0.3.2-dev.20260601115343 → 0.3.2-dev.20260610082053
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 +49 -59
- package/dist/main/api.d.ts +1 -2
- package/dist/main/api.js +5 -1
- package/dist/main/app/app.d.ts +15 -5
- package/dist/main/app/app.js +242 -122
- package/dist/main/app/bootstrap.d.ts +16 -0
- package/dist/main/app/bootstrap.js +20 -0
- package/dist/main/app/launch.d.ts +8 -4
- package/dist/main/app/launch.js +9 -4
- package/dist/main/index.bundle.js +5812 -1729
- package/dist/main/index.js +9 -1
- package/dist/main/ipc/app.d.ts +1 -1
- package/dist/main/ipc/bridge-router.d.ts +61 -0
- package/dist/main/ipc/bridge-router.js +1300 -0
- package/dist/main/ipc/panels.d.ts +1 -1
- package/dist/main/ipc/panels.js +6 -2
- package/dist/main/ipc/popover.d.ts +1 -1
- package/dist/main/ipc/project-fs.d.ts +1 -1
- package/dist/main/ipc/projects.d.ts +1 -1
- package/dist/main/ipc/session.d.ts +1 -1
- package/dist/main/ipc/settings.d.ts +1 -1
- package/dist/main/ipc/simulator-module.d.ts +6 -0
- package/dist/main/ipc/simulator-module.js +9 -1
- package/dist/main/ipc/simulator.d.ts +2 -2
- package/dist/main/ipc/simulator.js +49 -5
- package/dist/main/ipc/toolbar.d.ts +1 -1
- package/dist/main/ipc/views.d.ts +1 -1
- package/dist/main/ipc/views.js +40 -2
- package/dist/main/runtime/devtools-backend.d.ts +17 -0
- package/dist/main/runtime/devtools-backend.js +49 -0
- package/dist/main/runtime/miniapp-runtime.d.ts +39 -0
- package/dist/main/runtime/miniapp-runtime.js +17 -0
- package/dist/main/services/automation/exec.d.ts +27 -2
- package/dist/main/services/automation/exec.js +37 -3
- package/dist/main/services/automation/handlers/app.js +127 -11
- package/dist/main/services/automation/handlers/page.js +36 -19
- package/dist/main/services/automation/index.js +40 -13
- package/dist/main/services/automation/wait-active-page.d.ts +56 -0
- package/dist/main/services/automation/wait-active-page.js +71 -0
- package/dist/main/services/console-forward/index.d.ts +36 -0
- package/dist/main/services/console-forward/index.js +81 -0
- package/dist/main/services/dimina-resource-server.d.ts +7 -0
- package/dist/main/services/dimina-resource-server.js +99 -0
- package/dist/main/services/elements-forward/index.d.ts +66 -0
- package/dist/main/services/elements-forward/index.js +583 -0
- package/dist/main/services/layout/index.d.ts +22 -0
- package/dist/main/services/layout/index.js +25 -0
- package/dist/main/services/mcp/index.d.ts +1 -0
- package/dist/main/services/mcp/index.js +3 -0
- package/dist/main/services/mcp/server.d.ts +1 -1
- package/dist/main/services/mcp/server.js +1 -1
- package/dist/main/services/mcp/target-manager.d.ts +35 -0
- package/dist/main/services/mcp/target-manager.js +63 -1
- package/dist/main/services/mcp/tools/context-tools.js +15 -1
- package/dist/main/services/module.d.ts +1 -1
- package/dist/main/services/network-forward/index.d.ts +190 -0
- package/dist/main/services/network-forward/index.js +765 -0
- package/dist/main/services/notifications/renderer-notifier.d.ts +11 -0
- package/dist/main/services/notifications/renderer-notifier.js +7 -1
- package/dist/main/services/render-inspect/index.d.ts +41 -0
- package/dist/main/services/render-inspect/index.js +88 -0
- package/dist/main/services/safe-area/index.d.ts +17 -0
- package/dist/main/services/safe-area/index.js +59 -0
- package/dist/main/services/service-host-pool/pool.d.ts +197 -0
- package/dist/main/services/service-host-pool/pool.js +364 -0
- package/dist/main/services/simulator/custom-apis.d.ts +34 -0
- package/dist/main/services/simulator/custom-apis.js +22 -0
- package/dist/main/services/simulator-appdata/index.d.ts +35 -0
- package/dist/main/services/simulator-appdata/index.js +71 -0
- package/dist/main/services/simulator-current-page/index.d.ts +24 -0
- package/dist/main/services/simulator-current-page/index.js +19 -0
- package/dist/main/services/simulator-storage/index.d.ts +37 -2
- package/dist/main/services/simulator-storage/index.js +209 -32
- package/dist/main/services/simulator-storage/service-storage-ops.d.ts +40 -0
- package/dist/main/services/simulator-storage/service-storage-ops.js +67 -0
- package/dist/main/services/simulator-temp-files/index.d.ts +1 -1
- package/dist/main/services/simulator-temp-files/index.js +1 -1
- package/dist/main/services/simulator-wxml/index.d.ts +29 -0
- package/dist/main/services/simulator-wxml/index.js +28 -0
- package/dist/main/services/toolbar/toolbar-store.d.ts +3 -3
- package/dist/main/services/toolbar/toolbar-store.js +1 -18
- package/dist/main/services/views/devtools-tabs.d.ts +47 -0
- package/dist/main/services/views/devtools-tabs.js +95 -0
- package/dist/main/services/views/view-manager.d.ts +125 -0
- package/dist/main/services/views/view-manager.js +839 -4
- package/dist/main/services/workbench-context.d.ts +67 -7
- package/dist/main/services/workbench-context.js +6 -1
- package/dist/main/services/workspace/workspace-service.js +7 -13
- package/dist/main/utils/ipc-registry.d.ts +1 -1
- package/dist/main/utils/ipc-registry.js +21 -4
- package/dist/main/utils/paths.d.ts +15 -0
- package/dist/main/utils/paths.js +19 -0
- package/dist/main/utils/sender-policy.d.ts +9 -3
- package/dist/main/utils/sender-policy.js +18 -4
- package/dist/main/utils/theme.d.ts +1 -1
- package/dist/main/utils/theme.js +1 -1
- package/dist/main/windows/main-window/create.d.ts +0 -6
- package/dist/main/windows/main-window/create.js +11 -59
- package/dist/main/windows/main-window/events.d.ts +1 -1
- package/dist/main/windows/main-window/events.js +1 -1
- package/dist/main/windows/service-host-window/create.d.ts +52 -0
- package/dist/main/windows/service-host-window/create.js +112 -0
- package/dist/native-host/common/common.js +368 -0
- package/dist/native-host/container/pageFrame.css +1 -0
- package/dist/native-host/render/render.js +9334 -0
- package/dist/native-host/service/service.js +6 -0
- package/dist/preload/index.d.ts +2 -0
- package/dist/preload/index.js +6 -0
- package/dist/preload/instrumentation/app-data.d.ts +18 -20
- package/dist/preload/instrumentation/app-data.js +23 -160
- package/dist/preload/instrumentation/wxml-extract.d.ts +1 -1
- package/dist/preload/instrumentation/wxml-extract.js +1 -1
- package/dist/preload/runtime/bridge.d.ts +3 -8
- package/dist/preload/runtime/bridge.js +2 -32
- package/dist/preload/runtime/custom-apis.js +20 -17
- package/dist/preload/runtime/host-toolbar-advertiser.d.ts +27 -0
- package/dist/preload/runtime/host-toolbar-advertiser.js +55 -0
- package/dist/preload/runtime/main-api-runner.d.ts +9 -0
- package/dist/preload/runtime/main-api-runner.js +85 -0
- package/dist/preload/runtime/native-host.d.ts +43 -0
- package/dist/preload/runtime/native-host.js +83 -0
- package/dist/preload/shared/expose.d.ts +10 -5
- package/dist/preload/shared/expose.js +30 -12
- package/dist/preload/shared/sid-registry.d.ts +11 -0
- package/dist/preload/shared/sid-registry.js +38 -0
- package/dist/preload/windows/host-toolbar.cjs +161 -0
- package/dist/preload/windows/host-toolbar.cjs.map +7 -0
- package/dist/preload/windows/host-toolbar.d.ts +2 -0
- package/dist/preload/windows/host-toolbar.js +12 -0
- package/dist/preload/windows/main.cjs +1935 -0
- package/dist/preload/windows/main.cjs.map +4 -4
- package/dist/preload/windows/simulator.cjs +925 -0
- package/dist/preload/windows/simulator.cjs.map +7 -0
- package/dist/preload/windows/simulator.js +244 -574
- package/dist/render-host/pageFrame.html +29 -0
- package/dist/render-host/preload.cjs +154 -0
- package/dist/render-host/render-inspect.js +295 -0
- package/dist/renderer/assets/index-CcJKzatF.js +50 -0
- package/dist/renderer/assets/{input-CCYS8Ave.js → input-QGx6FFX1.js} +2 -2
- package/dist/renderer/assets/ipc-transport-BT4QYQqu.css +1 -0
- package/dist/renderer/assets/{ipc-transport-Bn4KXvhu.js → ipc-transport-DvZvwK-Z.js} +2 -2
- package/dist/renderer/assets/{popover-CH4rwpdt.js → popover-BOH1If1J.js} +2 -2
- package/dist/renderer/assets/select-C7JQ8G5N.js +2 -0
- package/dist/renderer/assets/{settings-CMTB-t0x.js → settings-DpAbTJyF.js} +2 -2
- package/dist/renderer/assets/{settings-api-Csdngf9Z.js → settings-api-RD2IZF53.js} +2 -2
- package/dist/renderer/assets/{workbenchSettings-CODFCzOg.js → workbenchSettings-CNJX4Qna.js} +2 -2
- package/dist/renderer/entries/main/index.html +5 -5
- 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/service-host/preload.cjs +254 -0
- package/dist/service-host/service.html +10 -0
- package/dist/service-host/sourcemap-rewrite.cjs +58 -0
- package/dist/service-host/sync-api-patch.js +20 -0
- package/dist/service-host/sync-impls/menu-button.js +16 -0
- package/dist/service-host/sync-impls/storage.js +50 -0
- package/dist/service-host/sync-impls/system-info.js +54 -0
- package/dist/shared/appdata-accumulator.d.ts +95 -0
- package/dist/shared/appdata-accumulator.js +213 -0
- package/dist/shared/bridge-channels.d.ts +364 -0
- package/dist/shared/bridge-channels.js +54 -0
- package/dist/shared/ipc-channels.d.ts +84 -1
- package/dist/shared/ipc-channels.js +90 -10
- package/dist/shared/ipc-schemas.d.ts +59 -2
- package/dist/shared/ipc-schemas.js +64 -3
- package/dist/shared/open-in-editor.d.ts +71 -0
- package/dist/shared/open-in-editor.js +138 -0
- package/dist/shared/simulator-api-metadata.d.ts +21 -0
- package/dist/shared/simulator-api-metadata.js +23 -0
- package/dist/shared/types.d.ts +2 -39
- package/dist/simulator/assets/device-shell-DsdcNEkt.css +1 -0
- package/dist/simulator/assets/device-shell-vwYSOVrh.js +2 -0
- package/dist/simulator/assets/jsx-runtime-P6ZegaOf.js +2 -0
- package/dist/simulator/assets/simulator-DddliUL5.js +10 -0
- package/dist/simulator/assets/simulator-mini-app-BDNu5n26.js +2 -0
- package/dist/simulator/device-shell/menu-button-geometry.js +8 -0
- package/dist/simulator/simulator.html +2 -112
- package/package.json +8 -9
- package/dist/main/utils/disposable.d.ts +0 -18
- package/dist/main/utils/disposable.js +0 -67
- package/dist/renderer/assets/index-BDMxXUfn.js +0 -50
- package/dist/renderer/assets/ipc-transport-Cu7tubyg.css +0 -1
- package/dist/renderer/assets/select-Ct4-sijm.js +0 -2
- package/dist/simulator/assets/simulator-B4e3c6_O.js +0 -9
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
import { WebContentsView, webContents } from 'electron';
|
|
1
|
+
import { ipcMain, shell, WebContentsView, webContents } from 'electron';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { mainPreloadPath } from '../../utils/paths.js';
|
|
4
|
-
import { applyNavigationHardening } from '../../windows/navigation-hardening.js';
|
|
3
|
+
import { cjsSiblingPreloadPath, hostToolbarPreloadPath, mainPreloadPath } from '../../utils/paths.js';
|
|
4
|
+
import { applyNavigationHardening, handleWindowOpenExternal, } from '../../windows/navigation-hardening.js';
|
|
5
|
+
import { SimulatorCustomApiBridgeChannel } from '../../../shared/ipc-channels.js';
|
|
6
|
+
import { OPEN_IN_EDITOR_SCHEME, decodeOpenInEditorUrl, resourceUrlToProjectRelativePath, } from '../../../shared/open-in-editor.js';
|
|
7
|
+
import { createSafeAreaController } from '../safe-area/index.js';
|
|
8
|
+
import { buildCustomizeTabsScript } from './devtools-tabs.js';
|
|
9
|
+
import { installElementsForward } from '../elements-forward/index.js';
|
|
5
10
|
import * as layout from '../layout/index.js';
|
|
11
|
+
import { handleCustomApiBridgeRequest, } from '../simulator/custom-apis.js';
|
|
6
12
|
import { getDefaultTab } from '../workbench-context.js';
|
|
7
13
|
/**
|
|
8
14
|
* Build a ViewManager bound to the given context. The returned object is the
|
|
@@ -15,19 +21,71 @@ export function createViewManager(ctx) {
|
|
|
15
21
|
// Resolve once: full WorkbenchContext always provides headerHeight; partial
|
|
16
22
|
// test contexts may omit it, in which case fall back to the default 40.
|
|
17
23
|
const headerHeight = ctx.headerHeight ?? 40;
|
|
24
|
+
// CSS env(safe-area-inset-*) simulation for render-host guests (per device).
|
|
25
|
+
// Driven from did-attach-webview below; re-pushed on device change via
|
|
26
|
+
// reapplySafeArea. Torn down in disposeAll.
|
|
27
|
+
const safeArea = createSafeAreaController({ connections: ctx.connections });
|
|
18
28
|
// ── Private mutable state ───────────────────────────────────────────────
|
|
19
29
|
let simulatorView = null;
|
|
20
30
|
let simulatorViewAdded = false;
|
|
31
|
+
// NATIVE-HOST ONLY: the simulator content WebContentsView (the DeviceShell
|
|
32
|
+
// host). In the default path the simulator is a renderer `<webview>` and this
|
|
33
|
+
// stays null. Positioned in the simulator panel region (left of the splitter)
|
|
34
|
+
// while `simulatorView` above hosts its DevTools in the right panel region.
|
|
35
|
+
let nativeSimulatorView = null;
|
|
36
|
+
let nativeSimulatorViewAdded = false;
|
|
37
|
+
// Model A: the renderer's measured inner-screen rect is the SOLE authority for
|
|
38
|
+
// the native simulator WCV bounds (see docs/simulator-render-architecture.md).
|
|
39
|
+
// Cache the last rect so a report that lands before attachNativeSimulator (the
|
|
40
|
+
// project-open ordering) is not lost — attach replays it instead of using a
|
|
41
|
+
// coarse panel-size fallback (which raced and caused the clip/surround flips).
|
|
42
|
+
let lastRendererRect = null;
|
|
43
|
+
// NATIVE-HOST ONLY. The `ipcMain.on` listener that services the
|
|
44
|
+
// `__diminaCustomApis` bridge for the current native simulator webContents
|
|
45
|
+
// (see `attachNativeCustomApiBridge`). Tracked so we can remove it before
|
|
46
|
+
// tearing down / re-attaching the view (otherwise listeners leak across
|
|
47
|
+
// relaunch cycles).
|
|
48
|
+
let nativeCustomApiBridgeHandler = null;
|
|
49
|
+
// NATIVE-HOST ONLY. The current device zoom as a factor (zoomPercent/100),
|
|
50
|
+
// last reported by the renderer via setNativeSimulatorViewBounds. Stored so
|
|
51
|
+
// nested render-host `<webview>` guests attached AFTER a zoom change still
|
|
52
|
+
// pick up the correct scale in `did-attach-webview`. Defaults to 1 (100%).
|
|
53
|
+
let currentZoomFactor = 1;
|
|
21
54
|
let settingsView = null;
|
|
22
55
|
let settingsViewAdded = false;
|
|
23
56
|
let popoverView = null;
|
|
24
57
|
let lastSimWidth = 375;
|
|
25
58
|
let simulatorWebContentsId = null;
|
|
59
|
+
// NATIVE-HOST ONLY. The webContents the right-panel Chrome DevTools front-end
|
|
60
|
+
// currently inspects. We point it at the SERVICE HOST (logic layer) — the
|
|
61
|
+
// hidden BrowserWindow where the mini-app's page code runs (`console.log`,
|
|
62
|
+
// `wx.request`, Sources/Network(fetch) all live there). The UI/view layer
|
|
63
|
+
// (Elements/Styles/WXML tree) is served separately by the native WXML panel +
|
|
64
|
+
// render-guest highlight chain (`simulator-storage`/`simulator-wxml`), so a
|
|
65
|
+
// single DevTools front-end is enough. The service window can be swapped on
|
|
66
|
+
// respawn (pre-warm pool recycles it), so this is re-resolved fresh via
|
|
67
|
+
// `ctx.bridge.getServiceWc()` and re-pointed on every render-side event.
|
|
68
|
+
let nativeDevtoolsSourceWc = null;
|
|
69
|
+
let unsubscribeNativeRenderEvents = null;
|
|
70
|
+
// Disposer for the Elements-forward feature (routes the front-end's Elements
|
|
71
|
+
// CDP traffic at the active render guest). Installed in
|
|
72
|
+
// `attachNativeSimulatorDevtoolsHost`, stopped on detach / host destroyed.
|
|
73
|
+
let stopElementsForward = null;
|
|
74
|
+
let nativeDevtoolsRetryTimer = null;
|
|
75
|
+
let nativeDevtoolsRetryToken = 0;
|
|
26
76
|
// Renderer-driven overlay bounds for the simulator DevTools view. When
|
|
27
77
|
// non-null this takes precedence over the legacy layout computed from the
|
|
28
78
|
// window content size. A zero-area rectangle means "hide" — the overlay is
|
|
29
79
|
// removed from the contentView but its WebContents stays alive.
|
|
30
80
|
let simulatorBoundsOverride = null;
|
|
81
|
+
// ── Host-controllable toolbar WebContentsView ───────────────────────────
|
|
82
|
+
// A strip above the devtools header that the downstream host loads its own
|
|
83
|
+
// content into and fully controls. Bounds come from a renderer DOM anchor
|
|
84
|
+
// (forward anchor, like the simulator DevTools overlay); its height is
|
|
85
|
+
// dynamic via a reverse size-advertiser the toolbar's own renderer drives.
|
|
86
|
+
let hostToolbarView = null;
|
|
87
|
+
let hostToolbarPreloadOverride = null;
|
|
88
|
+
let hostToolbarViewAdded = false;
|
|
31
89
|
// ── Internal helpers ────────────────────────────────────────────────────
|
|
32
90
|
function destroyViewInternal(view) {
|
|
33
91
|
if (!view)
|
|
@@ -84,6 +142,124 @@ export function createViewManager(ctx) {
|
|
|
84
142
|
}
|
|
85
143
|
simulatorView.setBounds(bounds);
|
|
86
144
|
}
|
|
145
|
+
// ── Host-controllable toolbar WebContentsView ───────────────────────────
|
|
146
|
+
// Lazily create the host-toolbar view. Mirrors `showSettings` for the
|
|
147
|
+
// webPreferences shape and the native simulator for nav hardening +
|
|
148
|
+
// background color (the host may load arbitrary URLs / content). Idempotent.
|
|
149
|
+
function ensureHostToolbarView() {
|
|
150
|
+
if (hostToolbarView && !hostToolbarView.webContents.isDestroyed()) {
|
|
151
|
+
return hostToolbarView;
|
|
152
|
+
}
|
|
153
|
+
// Rebuilding after the host destroyed the underlying webContents: detach the
|
|
154
|
+
// dead view from the contentView and reset the added-flag so the new view
|
|
155
|
+
// gets re-mounted (otherwise the `hostToolbarViewAdded` guard would skip the
|
|
156
|
+
// addChildView and the toolbar would silently disappear).
|
|
157
|
+
if (hostToolbarView && hostToolbarViewAdded) {
|
|
158
|
+
try {
|
|
159
|
+
ctx.windows.mainWindow.contentView.removeChildView(hostToolbarView);
|
|
160
|
+
}
|
|
161
|
+
catch { /* already removed */ }
|
|
162
|
+
hostToolbarViewAdded = false;
|
|
163
|
+
}
|
|
164
|
+
const view = new WebContentsView({
|
|
165
|
+
webPreferences: {
|
|
166
|
+
nodeIntegration: false,
|
|
167
|
+
contextIsolation: true,
|
|
168
|
+
sandbox: false,
|
|
169
|
+
// Default: the reverse size-advertiser preload that measures the host
|
|
170
|
+
// content's intrinsic height and posts it on the advertise channel so
|
|
171
|
+
// main reserves exactly that strip height (dynamic height via
|
|
172
|
+
// ViewAnchor). When the host-shell supplies its own toolbar preload
|
|
173
|
+
// (workbench config.toolbar.preloadPath), use that instead — the host
|
|
174
|
+
// owns the bridge and may install the advertiser itself.
|
|
175
|
+
preload: hostToolbarPreloadOverride ?? hostToolbarPreloadPath,
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
hostToolbarView = view;
|
|
179
|
+
// Paint the surface a neutral color so growing the reserved strip never
|
|
180
|
+
// flashes white before the host content paints (mirrors the native
|
|
181
|
+
// simulator's setBackgroundColor anti-flash).
|
|
182
|
+
try {
|
|
183
|
+
view.setBackgroundColor('#121212');
|
|
184
|
+
}
|
|
185
|
+
catch { /* stub may lack it */ }
|
|
186
|
+
// The host may load arbitrary URLs; route popups + cross-origin in-place
|
|
187
|
+
// navigation to the OS browser (mirror the native simulator hardening).
|
|
188
|
+
try {
|
|
189
|
+
view.webContents.setWindowOpenHandler(({ url }) => handleWindowOpenExternal(url));
|
|
190
|
+
}
|
|
191
|
+
catch { /* stub may lack it */ }
|
|
192
|
+
return view;
|
|
193
|
+
}
|
|
194
|
+
function setHostToolbarBounds(bounds) {
|
|
195
|
+
if (ctx.windows.mainWindow.isDestroyed())
|
|
196
|
+
return;
|
|
197
|
+
// Zero-area rect means "hide" — remove the child view but keep its
|
|
198
|
+
// WebContents (and the host's loaded content) alive. Do NOT create the
|
|
199
|
+
// view just to immediately hide it.
|
|
200
|
+
if (isHidden(bounds)) {
|
|
201
|
+
if (hostToolbarView && hostToolbarViewAdded && !hostToolbarView.webContents.isDestroyed()) {
|
|
202
|
+
try {
|
|
203
|
+
ctx.windows.mainWindow.contentView.removeChildView(hostToolbarView);
|
|
204
|
+
}
|
|
205
|
+
catch { /* already removed */ }
|
|
206
|
+
}
|
|
207
|
+
hostToolbarViewAdded = false;
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const view = ensureHostToolbarView();
|
|
211
|
+
if (!hostToolbarViewAdded) {
|
|
212
|
+
// addChildView appends = topmost z-order, which is correct for a strip
|
|
213
|
+
// that sits above the devtools header.
|
|
214
|
+
ctx.windows.mainWindow.contentView.addChildView(view);
|
|
215
|
+
hostToolbarViewAdded = true;
|
|
216
|
+
}
|
|
217
|
+
view.setBounds(bounds);
|
|
218
|
+
}
|
|
219
|
+
function setHostToolbarHeight(extent) {
|
|
220
|
+
// Push the reserved height back to the main-window renderer so its
|
|
221
|
+
// placeholder div resizes (closing the dynamic-height loop). The height is
|
|
222
|
+
// not retained in main — the renderer placeholder is the single source of
|
|
223
|
+
// truth, and the forward anchor re-reports bounds from it.
|
|
224
|
+
ctx.notify.hostToolbarHeightChanged(extent);
|
|
225
|
+
}
|
|
226
|
+
function hideHostToolbar() {
|
|
227
|
+
if (hostToolbarView && hostToolbarViewAdded && !ctx.windows.mainWindow.isDestroyed()) {
|
|
228
|
+
try {
|
|
229
|
+
ctx.windows.mainWindow.contentView.removeChildView(hostToolbarView);
|
|
230
|
+
}
|
|
231
|
+
catch { /* already removed */ }
|
|
232
|
+
}
|
|
233
|
+
hostToolbarViewAdded = false;
|
|
234
|
+
// Collapse the renderer placeholder to 0 too. Otherwise its anchor keeps a
|
|
235
|
+
// non-zero reserved height and re-publishes bounds on the next window
|
|
236
|
+
// resize, silently re-adding the view we just hid (unstable hide). Zeroing
|
|
237
|
+
// the height flips the anchor to `present:false` so it stops re-publishing.
|
|
238
|
+
ctx.notify.hostToolbarHeightChanged(0);
|
|
239
|
+
}
|
|
240
|
+
const hostToolbar = {
|
|
241
|
+
async loadURL(url) {
|
|
242
|
+
const view = ensureHostToolbarView();
|
|
243
|
+
await view.webContents.loadURL(url);
|
|
244
|
+
},
|
|
245
|
+
async loadFile(filePath) {
|
|
246
|
+
const view = ensureHostToolbarView();
|
|
247
|
+
await view.webContents.loadFile(filePath);
|
|
248
|
+
},
|
|
249
|
+
get webContents() {
|
|
250
|
+
if (!hostToolbarView)
|
|
251
|
+
return null;
|
|
252
|
+
if (hostToolbarView.webContents.isDestroyed())
|
|
253
|
+
return null;
|
|
254
|
+
return hostToolbarView.webContents;
|
|
255
|
+
},
|
|
256
|
+
hide() {
|
|
257
|
+
hideHostToolbar();
|
|
258
|
+
},
|
|
259
|
+
setPreloadPath(path) {
|
|
260
|
+
hostToolbarPreloadOverride = path;
|
|
261
|
+
},
|
|
262
|
+
};
|
|
87
263
|
function applySettingsBounds() {
|
|
88
264
|
if (!settingsView || ctx.windows.mainWindow.isDestroyed())
|
|
89
265
|
return;
|
|
@@ -96,6 +272,242 @@ export function createViewManager(ctx) {
|
|
|
96
272
|
const [w = 0, h = 0] = ctx.windows.mainWindow.getContentSize();
|
|
97
273
|
popoverView.setBounds(layout.computePopoverBounds(w, h, headerHeight));
|
|
98
274
|
}
|
|
275
|
+
function clearNativeDevtoolsRetry() {
|
|
276
|
+
nativeDevtoolsRetryToken++;
|
|
277
|
+
if (nativeDevtoolsRetryTimer) {
|
|
278
|
+
clearTimeout(nativeDevtoolsRetryTimer);
|
|
279
|
+
nativeDevtoolsRetryTimer = null;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
function closeNativeDevtoolsSource() {
|
|
283
|
+
const source = nativeDevtoolsSourceWc;
|
|
284
|
+
nativeDevtoolsSourceWc = null;
|
|
285
|
+
if (!source || source.isDestroyed())
|
|
286
|
+
return;
|
|
287
|
+
try {
|
|
288
|
+
if (source.isDevToolsOpened()) {
|
|
289
|
+
source.closeDevTools();
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
catch { /* source may be mid-destroy */ }
|
|
293
|
+
}
|
|
294
|
+
function stopFollowingNativeServiceHost() {
|
|
295
|
+
clearNativeDevtoolsRetry();
|
|
296
|
+
if (unsubscribeNativeRenderEvents) {
|
|
297
|
+
try {
|
|
298
|
+
unsubscribeNativeRenderEvents();
|
|
299
|
+
}
|
|
300
|
+
catch { /* ignore */ }
|
|
301
|
+
unsubscribeNativeRenderEvents = null;
|
|
302
|
+
}
|
|
303
|
+
closeNativeDevtoolsSource();
|
|
304
|
+
}
|
|
305
|
+
// ── Open-in-editor: click a console file link → built-in Monaco ──────────
|
|
306
|
+
// The right-panel console is the embedded Chromium DevTools front-end. Once a
|
|
307
|
+
// sourcemap maps a console frame back to source (restored by the service-host
|
|
308
|
+
// importScripts sourcemap rewrite), we redirect a source-link click to OUR
|
|
309
|
+
// Monaco editor instead of the DevTools Sources panel: install an "open
|
|
310
|
+
// resource handler" in the front-end realm that encodes (url, line, col) into
|
|
311
|
+
// a sentinel URL and asks the front-end to open it; Electron surfaces that as
|
|
312
|
+
// `devtools-open-url` on the inspected (service-host) wc, which we decode,
|
|
313
|
+
// map to a project-relative path, and broadcast to the renderer's Monaco.
|
|
314
|
+
const openInEditorWiredWcIds = new Set();
|
|
315
|
+
function injectOpenResourceHandler(devtoolsWc) {
|
|
316
|
+
// `setOpenResourceHandler` is the official Chromium DevTools hook IDEs use
|
|
317
|
+
// to route source-link clicks to an external editor. We poll for the host
|
|
318
|
+
// (it appears once the front-end finishes bootstrapping, like `UI` above)
|
|
319
|
+
// and register a handler that re-emits an encoded sentinel via
|
|
320
|
+
// `openInNewTab` → Electron `devtools-open-url`. Best-effort: wrapped in
|
|
321
|
+
// try/catch and a bounded poll so a missing API never throws.
|
|
322
|
+
const scheme = JSON.stringify(OPEN_IN_EDITOR_SCHEME);
|
|
323
|
+
devtoolsWc.executeJavaScript(`
|
|
324
|
+
(function() {
|
|
325
|
+
try {
|
|
326
|
+
let tries = 0
|
|
327
|
+
const timer = setInterval(() => {
|
|
328
|
+
tries++
|
|
329
|
+
try {
|
|
330
|
+
const Host = globalThis.Host
|
|
331
|
+
const host = Host && Host.InspectorFrontendHost
|
|
332
|
+
if (host && typeof host.setOpenResourceHandler === 'function'
|
|
333
|
+
&& typeof host.openInNewTab === 'function') {
|
|
334
|
+
host.setOpenResourceHandler((url, lineNumber, columnNumber) => {
|
|
335
|
+
try {
|
|
336
|
+
const p = new URLSearchParams()
|
|
337
|
+
p.set('u', String(url))
|
|
338
|
+
if (typeof lineNumber === 'number') p.set('l', String(lineNumber))
|
|
339
|
+
if (typeof columnNumber === 'number') p.set('c', String(columnNumber))
|
|
340
|
+
host.openInNewTab(${scheme} + ':?' + p.toString())
|
|
341
|
+
} catch (_) {}
|
|
342
|
+
})
|
|
343
|
+
clearInterval(timer)
|
|
344
|
+
return
|
|
345
|
+
}
|
|
346
|
+
} catch (_) {}
|
|
347
|
+
if (tries > 80) clearInterval(timer)
|
|
348
|
+
}, 50)
|
|
349
|
+
} catch (_) {}
|
|
350
|
+
})()
|
|
351
|
+
`).catch(() => { });
|
|
352
|
+
}
|
|
353
|
+
// ── DevTools tab customization: keep only Elements/Console/Network ──────────
|
|
354
|
+
// Inject into the same DevTools front-end host wc that the console/network
|
|
355
|
+
// injectors target. Reorders Elements/Console/Network to the front and closes
|
|
356
|
+
// every other panel tab by driving the front-end's UI.ViewManager /
|
|
357
|
+
// InspectorView.tabbedLocation (see ./devtools-tabs.ts). Best-effort: the
|
|
358
|
+
// injected script bounded-polls for the lazily-registered panels, wraps
|
|
359
|
+
// everything in try/catch, and silently no-ops if the API never appears —
|
|
360
|
+
// DevTools default tab behaviour is preserved on any failure. Re-injected on
|
|
361
|
+
// every (re)point so a re-`openDevTools` (service-host pool swap) re-applies.
|
|
362
|
+
function customizeDevtoolsTabs(devtoolsWc) {
|
|
363
|
+
try {
|
|
364
|
+
if (devtoolsWc.isDestroyed())
|
|
365
|
+
return;
|
|
366
|
+
const inject = () => {
|
|
367
|
+
if (devtoolsWc.isDestroyed())
|
|
368
|
+
return;
|
|
369
|
+
try {
|
|
370
|
+
void devtoolsWc.executeJavaScript(buildCustomizeTabsScript()).catch(() => { });
|
|
371
|
+
}
|
|
372
|
+
catch { /* wc torn down mid-call */ }
|
|
373
|
+
};
|
|
374
|
+
if (devtoolsWc.isLoading()) {
|
|
375
|
+
devtoolsWc.once('dom-ready', inject);
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
inject();
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
catch { /* wc surface incomplete / torn down; degrade silently */ }
|
|
382
|
+
}
|
|
383
|
+
function wireOpenInEditor(serviceWc, devtoolsWc) {
|
|
384
|
+
// Inject the front-end handler on every (re)attach — the DevTools front-end
|
|
385
|
+
// wc is recreated per simulatorView, so a fresh host needs the handler set.
|
|
386
|
+
if (!devtoolsWc.isDestroyed()) {
|
|
387
|
+
if (devtoolsWc.isLoading()) {
|
|
388
|
+
devtoolsWc.once('dom-ready', () => injectOpenResourceHandler(devtoolsWc));
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
injectOpenResourceHandler(devtoolsWc);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
// Attach the `devtools-open-url` decoder to the inspected service wc once
|
|
395
|
+
// (the listener is keyed off the encoded sentinel scheme, so it only acts on
|
|
396
|
+
// OUR redirected links; any other devtools "open in new tab" falls through).
|
|
397
|
+
if (openInEditorWiredWcIds.has(serviceWc.id))
|
|
398
|
+
return;
|
|
399
|
+
openInEditorWiredWcIds.add(serviceWc.id);
|
|
400
|
+
const onOpenUrl = (_event, url) => {
|
|
401
|
+
const req = decodeOpenInEditorUrl(url);
|
|
402
|
+
if (!req)
|
|
403
|
+
return; // not our sentinel — leave it to Electron's default path
|
|
404
|
+
const rel = resourceUrlToProjectRelativePath(req.url);
|
|
405
|
+
if (!rel)
|
|
406
|
+
return;
|
|
407
|
+
// DevTools reports 0-based line/column; Monaco is 1-based.
|
|
408
|
+
const line = typeof req.line === 'number' ? req.line + 1 : undefined;
|
|
409
|
+
const column = typeof req.column === 'number' ? req.column + 1 : undefined;
|
|
410
|
+
ctx.notify.editorOpenFile({ path: rel, line, column });
|
|
411
|
+
};
|
|
412
|
+
serviceWc.on('devtools-open-url', onOpenUrl);
|
|
413
|
+
// Consolidate teardown onto the connection layer (foundation.md §4 / P2),
|
|
414
|
+
// but as a wc-LIFETIME resource: the open-in-editor wiring inspects this
|
|
415
|
+
// service-host wc and must SURVIVE pool reuse (`reset`) — the dedup set
|
|
416
|
+
// entry + listener stay valid across sessions reusing the same wc, and the
|
|
417
|
+
// early-return dedup above correctly skips re-wiring after a reset. So we
|
|
418
|
+
// register on `'closed'` (fires only on real wc destroy), NOT `own()`
|
|
419
|
+
// (which also fires on `reset` and would leave the wc un-wired after reuse
|
|
420
|
+
// because re-pointing the same wc.id early-returns). acquire() is
|
|
421
|
+
// idempotent — bridge-router owning session-scoped resources on the same
|
|
422
|
+
// serviceWc connection coexists cleanly.
|
|
423
|
+
const conn = ctx.connections.acquire(serviceWc);
|
|
424
|
+
conn.on('closed', () => {
|
|
425
|
+
openInEditorWiredWcIds.delete(serviceWc.id);
|
|
426
|
+
try {
|
|
427
|
+
serviceWc.removeListener('devtools-open-url', onOpenUrl);
|
|
428
|
+
}
|
|
429
|
+
catch { /* wc gone */ }
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
// Point the right-panel Chrome DevTools front-end at `next` — the SERVICE HOST
|
|
433
|
+
// webContents (logic layer). Idempotent: if we already inspect this wc, no-op.
|
|
434
|
+
function pointNativeDevtoolsAtServiceWc(next) {
|
|
435
|
+
if (!simulatorView || simulatorView.webContents.isDestroyed())
|
|
436
|
+
return true;
|
|
437
|
+
if (nativeDevtoolsSourceWc?.id === next.id && !nativeDevtoolsSourceWc.isDestroyed()) {
|
|
438
|
+
return true;
|
|
439
|
+
}
|
|
440
|
+
closeNativeDevtoolsSource();
|
|
441
|
+
nativeDevtoolsSourceWc = next;
|
|
442
|
+
try {
|
|
443
|
+
next.setDevToolsWebContents(simulatorView.webContents);
|
|
444
|
+
// DevTools renders into the right-panel host view (simulatorView); with a
|
|
445
|
+
// custom host the `mode` is overridden, and `activate:false` prevents it
|
|
446
|
+
// stealing focus — this re-points whenever the service window is swapped,
|
|
447
|
+
// so a focusing window would yank focus repeatedly (disrupting the user /
|
|
448
|
+
// e2e).
|
|
449
|
+
next.openDevTools({ mode: 'detach', activate: false });
|
|
450
|
+
// Redirect console source-link clicks to the built-in Monaco editor.
|
|
451
|
+
wireOpenInEditor(next, simulatorView.webContents);
|
|
452
|
+
// Keep only Elements/Console/Network tabs (front-most, in that order).
|
|
453
|
+
// Re-applied on every re-point so a service-host pool swap (fresh
|
|
454
|
+
// openDevTools) re-asserts the custom tab bar.
|
|
455
|
+
customizeDevtoolsTabs(simulatorView.webContents);
|
|
456
|
+
return true;
|
|
457
|
+
}
|
|
458
|
+
catch {
|
|
459
|
+
if (nativeDevtoolsSourceWc?.id === next.id) {
|
|
460
|
+
nativeDevtoolsSourceWc = null;
|
|
461
|
+
}
|
|
462
|
+
return false;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
// Resolve the SERVICE HOST webContents for the active (or named) app. This is
|
|
466
|
+
// the hidden service BrowserWindow's wc — a top-level wc that CAN host a
|
|
467
|
+
// Chrome DevTools front-end (unlike a `<webview>` guest's). Re-resolved fresh
|
|
468
|
+
// so a pre-warm-pool swap on respawn is tolerated.
|
|
469
|
+
function pointNativeDevtoolsAtActiveServiceHost(appId) {
|
|
470
|
+
if (!ctx.bridge?.isNativeHost())
|
|
471
|
+
return true;
|
|
472
|
+
if (!simulatorView || simulatorView.webContents.isDestroyed())
|
|
473
|
+
return true;
|
|
474
|
+
const wc = ctx.bridge.getServiceWc(appId);
|
|
475
|
+
if (!wc || wc.isDestroyed())
|
|
476
|
+
return false;
|
|
477
|
+
return pointNativeDevtoolsAtServiceWc(wc);
|
|
478
|
+
}
|
|
479
|
+
function scheduleNativeDevtoolsFollow(appId, attempt = 0) {
|
|
480
|
+
if (attempt >= 20)
|
|
481
|
+
return;
|
|
482
|
+
if (!ctx.bridge?.isNativeHost())
|
|
483
|
+
return;
|
|
484
|
+
const token = nativeDevtoolsRetryToken;
|
|
485
|
+
if (nativeDevtoolsRetryTimer)
|
|
486
|
+
clearTimeout(nativeDevtoolsRetryTimer);
|
|
487
|
+
nativeDevtoolsRetryTimer = setTimeout(() => {
|
|
488
|
+
nativeDevtoolsRetryTimer = null;
|
|
489
|
+
if (token !== nativeDevtoolsRetryToken)
|
|
490
|
+
return;
|
|
491
|
+
if (pointNativeDevtoolsAtActiveServiceHost(appId))
|
|
492
|
+
return;
|
|
493
|
+
scheduleNativeDevtoolsFollow(appId, attempt + 1);
|
|
494
|
+
}, 50);
|
|
495
|
+
}
|
|
496
|
+
function followNativeDevtoolsServiceHost(appId) {
|
|
497
|
+
clearNativeDevtoolsRetry();
|
|
498
|
+
if (pointNativeDevtoolsAtActiveServiceHost(appId))
|
|
499
|
+
return;
|
|
500
|
+
scheduleNativeDevtoolsFollow(appId);
|
|
501
|
+
}
|
|
502
|
+
// Render-side activity (a page DOM mounting / the visible page changing)
|
|
503
|
+
// always follows a spawn or respawn, by which point the service window exists
|
|
504
|
+
// (and may have been swapped by the pool). Re-resolve + re-point the DevTools
|
|
505
|
+
// at the now-current service host on every such event.
|
|
506
|
+
function onNativeRenderEvent(event) {
|
|
507
|
+
if (event.kind !== 'activePage' && event.kind !== 'domReady')
|
|
508
|
+
return;
|
|
509
|
+
followNativeDevtoolsServiceHost(event.appId);
|
|
510
|
+
}
|
|
99
511
|
// ── ViewManager methods ─────────────────────────────────────────────────
|
|
100
512
|
function attachSimulator(simWcId, simWidth) {
|
|
101
513
|
const sim = webContents.fromId(simWcId);
|
|
@@ -118,7 +530,9 @@ export function createViewManager(ctx) {
|
|
|
118
530
|
}
|
|
119
531
|
simulatorView = new WebContentsView();
|
|
120
532
|
sim.setDevToolsWebContents(simulatorView.webContents);
|
|
121
|
-
|
|
533
|
+
// Embedded in the right-panel host view; never bring it to the foreground
|
|
534
|
+
// (would steal focus from the user / disrupt e2e).
|
|
535
|
+
sim.openDevTools({ mode: 'detach', activate: false });
|
|
122
536
|
// Default DevTools to Console panel (Chrome DevTools defaults to Elements).
|
|
123
537
|
// The DevTools UI lives inside closed shadow roots, so a light-DOM
|
|
124
538
|
// querySelector('[role="tab"]') cannot reach the tab bar to click it.
|
|
@@ -128,6 +542,8 @@ export function createViewManager(ctx) {
|
|
|
128
542
|
// request the `console` view, and also persist the choice via the
|
|
129
543
|
// `panel-selectedTab` localStorage key so subsequent reloads honor it.
|
|
130
544
|
const devtoolsWc = simulatorView.webContents;
|
|
545
|
+
// Keep only Elements/Console/Network tabs (front-most, in that order).
|
|
546
|
+
customizeDevtoolsTabs(devtoolsWc);
|
|
131
547
|
devtoolsWc.once('dom-ready', () => {
|
|
132
548
|
devtoolsWc.executeJavaScript(`
|
|
133
549
|
(function() {
|
|
@@ -166,7 +582,349 @@ export function createViewManager(ctx) {
|
|
|
166
582
|
}
|
|
167
583
|
}
|
|
168
584
|
}
|
|
585
|
+
function attachNativeSimulatorDevtoolsHost(simWidth) {
|
|
586
|
+
stopFollowingNativeServiceHost();
|
|
587
|
+
// Destroy old simulatorView to prevent WebContentsView leak
|
|
588
|
+
if (simulatorView) {
|
|
589
|
+
hideSimulator();
|
|
590
|
+
try {
|
|
591
|
+
if (!simulatorView.webContents.isDestroyed()) {
|
|
592
|
+
simulatorView.webContents.close();
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
catch { /* ignore */ }
|
|
596
|
+
simulatorView = null;
|
|
597
|
+
}
|
|
598
|
+
simulatorView = new WebContentsView();
|
|
599
|
+
// Default DevTools to Console panel (Chrome DevTools defaults to Elements).
|
|
600
|
+
// The DevTools UI lives inside closed shadow roots, so a light-DOM
|
|
601
|
+
// querySelector('[role="tab"]') cannot reach the tab bar to click it.
|
|
602
|
+
// Instead drive the front-end's own view manager: the bundled DevTools
|
|
603
|
+
// exposes `UI.ViewManager.instance().showView(id)` on `globalThis.UI`
|
|
604
|
+
// once the front-end has finished bootstrapping. We poll for it and
|
|
605
|
+
// request the `console` view, and also persist the choice via the
|
|
606
|
+
// `panel-selectedTab` localStorage key so subsequent reloads honor it.
|
|
607
|
+
const devtoolsWc = simulatorView.webContents;
|
|
608
|
+
// Hand the network forwarder THIS front-end host wc — it injects the
|
|
609
|
+
// simulator WCV's Network.* CDP events into `window.DevToolsAPI.dispatchMessage`
|
|
610
|
+
// here so the native Network tab renders them (falls back to the service-host
|
|
611
|
+
// console line when null / the API never appears). Cleared on detach.
|
|
612
|
+
ctx.networkForward?.setDevtoolsHost(devtoolsWc);
|
|
613
|
+
// Elements forwarding (production, always on for the native simulator): route
|
|
614
|
+
// the front-end's Elements-panel CDP traffic (DOM/CSS/Overlay/DOMSnapshot/
|
|
615
|
+
// DOMDebugger) onto the ACTIVE RENDER GUEST so the panel reflects the page's
|
|
616
|
+
// live DOM tree instead of the service host it natively inspects. Reuses the
|
|
617
|
+
// safe-area-attached debugger session (never detaches one it doesn't own);
|
|
618
|
+
// Emulation.* and every other domain stay on the service-host path. Degrades
|
|
619
|
+
// to the native service-host DOM if the front-end hook is unavailable. The
|
|
620
|
+
// disposer is stopped on detach / host destroyed.
|
|
621
|
+
if (ctx.bridge) {
|
|
622
|
+
try {
|
|
623
|
+
stopElementsForward?.();
|
|
624
|
+
}
|
|
625
|
+
catch { /* prior disposer already gone */ }
|
|
626
|
+
stopElementsForward = installElementsForward({ devtoolsWc, bridge: ctx.bridge, connections: ctx.connections });
|
|
627
|
+
devtoolsWc.once('destroyed', () => {
|
|
628
|
+
try {
|
|
629
|
+
stopElementsForward?.();
|
|
630
|
+
}
|
|
631
|
+
catch { /* already stopped */ }
|
|
632
|
+
stopElementsForward = null;
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
devtoolsWc.once('dom-ready', () => {
|
|
636
|
+
devtoolsWc.executeJavaScript(`
|
|
637
|
+
(function() {
|
|
638
|
+
try { localStorage.setItem('panel-selectedTab', '"console"') } catch {}
|
|
639
|
+
let tries = 0
|
|
640
|
+
const timer = setInterval(() => {
|
|
641
|
+
tries++
|
|
642
|
+
try {
|
|
643
|
+
const UI = globalThis.UI
|
|
644
|
+
const vm = UI && UI.ViewManager && typeof UI.ViewManager.instance === 'function'
|
|
645
|
+
? UI.ViewManager.instance()
|
|
646
|
+
: null
|
|
647
|
+
if (vm && typeof vm.showView === 'function') {
|
|
648
|
+
vm.showView('console')
|
|
649
|
+
clearInterval(timer)
|
|
650
|
+
return
|
|
651
|
+
}
|
|
652
|
+
} catch {}
|
|
653
|
+
if (tries > 80) clearInterval(timer)
|
|
654
|
+
}, 50)
|
|
655
|
+
})()
|
|
656
|
+
`).catch(() => { });
|
|
657
|
+
});
|
|
658
|
+
if (getDefaultTab(ctx) === 'simulator') {
|
|
659
|
+
if (simulatorBoundsOverride) {
|
|
660
|
+
if (!isHidden(simulatorBoundsOverride)) {
|
|
661
|
+
ctx.windows.mainWindow.contentView.addChildView(simulatorView);
|
|
662
|
+
simulatorViewAdded = true;
|
|
663
|
+
simulatorView.setBounds(simulatorBoundsOverride);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
else {
|
|
667
|
+
ctx.windows.mainWindow.contentView.addChildView(simulatorView);
|
|
668
|
+
simulatorViewAdded = true;
|
|
669
|
+
applySimulatorBounds(simWidth);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
if (ctx.bridge?.isNativeHost()) {
|
|
673
|
+
unsubscribeNativeRenderEvents = ctx.bridge.onRenderEvent(onNativeRenderEvent);
|
|
674
|
+
followNativeDevtoolsServiceHost();
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* Remove the custom-apis bridge `ipcMain.on` listener, if any. Idempotent.
|
|
679
|
+
*/
|
|
680
|
+
function detachNativeCustomApiBridge() {
|
|
681
|
+
if (nativeCustomApiBridgeHandler) {
|
|
682
|
+
ipcMain.removeListener(SimulatorCustomApiBridgeChannel.Request, nativeCustomApiBridgeHandler);
|
|
683
|
+
nativeCustomApiBridgeHandler = null;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* NATIVE-HOST ONLY. The native simulator is a top-level WebContentsView with
|
|
688
|
+
* NO embedder renderer, so the simulator-side `__diminaCustomApis` bridge
|
|
689
|
+
* (`src/preload/runtime/custom-apis.ts`) cannot reach the host via
|
|
690
|
+
* `ipcRenderer.sendToHost` — that only delivers to a `<webview>`'s embedder,
|
|
691
|
+
* which is how the default path's `useCustomApiProxy` answered it. A
|
|
692
|
+
* top-level WebContentsView has no embedder, and `sendToHost` does NOT loop
|
|
693
|
+
* back as `ipc-message-host` on itself, so that channel never fires here.
|
|
694
|
+
*
|
|
695
|
+
* Under native-host the bridge instead talks to `ipcMain` directly (the same
|
|
696
|
+
* way `installNativeHostBridge` issues SPAWN/PAGE_OPEN): the preload sends
|
|
697
|
+
* `SimulatorCustomApiBridgeChannel.Request` via `ipcRenderer.send`, this
|
|
698
|
+
* `ipcMain.on` listener answers it. We do NOT route through `IpcRegistry` /
|
|
699
|
+
* the sender-policy white-list (the simulator is deliberately kept off it);
|
|
700
|
+
* instead we accept the message ONLY when `event.sender` is THIS precise
|
|
701
|
+
* simWc — the same trust model bridge-router uses for the simulator's own
|
|
702
|
+
* SPAWN messages. The result is dispatched through the shared
|
|
703
|
+
* `ctx.simulatorApis` registry and the id-correlated `Response` is posted
|
|
704
|
+
* back via `simWc.send`, which the preload's `ipcRenderer.on(Response)`
|
|
705
|
+
* listener settles.
|
|
706
|
+
*/
|
|
707
|
+
function attachNativeCustomApiBridge(simWc) {
|
|
708
|
+
// Re-attach defensively: a stale listener from a prior simWc must never
|
|
709
|
+
// linger. (attachNativeSimulator already tears the old view down first.)
|
|
710
|
+
detachNativeCustomApiBridge();
|
|
711
|
+
const apis = ctx.simulatorApis;
|
|
712
|
+
if (!apis)
|
|
713
|
+
return;
|
|
714
|
+
const simWcId = simWc.id;
|
|
715
|
+
const handler = (event, req) => {
|
|
716
|
+
// Trust gate: only the exact native simulator webContents may drive this.
|
|
717
|
+
if (event.sender.id !== simWcId)
|
|
718
|
+
return;
|
|
719
|
+
const r = req;
|
|
720
|
+
if (!r || typeof r.id !== 'number')
|
|
721
|
+
return;
|
|
722
|
+
void handleCustomApiBridgeRequest(apis, r).then((response) => {
|
|
723
|
+
if (simWc.isDestroyed())
|
|
724
|
+
return;
|
|
725
|
+
simWc.send(SimulatorCustomApiBridgeChannel.Response, response);
|
|
726
|
+
}).catch(() => { });
|
|
727
|
+
};
|
|
728
|
+
nativeCustomApiBridgeHandler = handler;
|
|
729
|
+
ipcMain.on(SimulatorCustomApiBridgeChannel.Request, handler);
|
|
730
|
+
// Consolidate this per-webContents teardown onto the connection layer
|
|
731
|
+
// (foundation.md §4 / P1 DoD #3): acquiring the simWc connection makes the
|
|
732
|
+
// registry track this trusted webContents, and `own()`-ing the detach ties
|
|
733
|
+
// the ipcMain listener's lifetime to the connection so it's torn down
|
|
734
|
+
// deterministically when the wc is destroyed (or the connection is reset),
|
|
735
|
+
// instead of a bespoke `once('destroyed')`. The detach stays idempotent, so
|
|
736
|
+
// the defensive re-attach path calling it directly remains safe.
|
|
737
|
+
ctx.connections.acquire(simWc).own(detachNativeCustomApiBridge);
|
|
738
|
+
}
|
|
739
|
+
function attachNativeSimulator(simulatorUrl, simWidth) {
|
|
740
|
+
if (!ctx.preloadPath) {
|
|
741
|
+
console.error('[workbench] attachNativeSimulator — preloadPath unset; cannot mount native simulator');
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
lastSimWidth = simWidth;
|
|
745
|
+
// Tear down any previous native simulator view (relaunch / re-open).
|
|
746
|
+
if (nativeSimulatorView) {
|
|
747
|
+
detachNativeCustomApiBridge();
|
|
748
|
+
if (nativeSimulatorViewAdded) {
|
|
749
|
+
try {
|
|
750
|
+
ctx.windows.mainWindow.contentView.removeChildView(nativeSimulatorView);
|
|
751
|
+
}
|
|
752
|
+
catch { /* already removed */ }
|
|
753
|
+
nativeSimulatorViewAdded = false;
|
|
754
|
+
}
|
|
755
|
+
try {
|
|
756
|
+
if (!nativeSimulatorView.webContents.isDestroyed()) {
|
|
757
|
+
nativeSimulatorView.webContents.close();
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
catch { /* ignore */ }
|
|
761
|
+
nativeSimulatorView = null;
|
|
762
|
+
}
|
|
763
|
+
// The simulator preload is a CJS bundle; webPreferences.preload obeys the
|
|
764
|
+
// `.js` + "type":"module" ESM rule (require would be undefined), so hand the
|
|
765
|
+
// top-level WebContentsView the `.cjs` sibling. contextIsolation:false +
|
|
766
|
+
// sandbox:false + webviewTag:true mirror what the default `<webview>` guest
|
|
767
|
+
// runs with, and `partition:'persist:simulator'` shares storage + the
|
|
768
|
+
// session-registered preload/CORS rules with the rest of the simulator.
|
|
769
|
+
const view = new WebContentsView({
|
|
770
|
+
webPreferences: {
|
|
771
|
+
nodeIntegration: false,
|
|
772
|
+
contextIsolation: false,
|
|
773
|
+
sandbox: false,
|
|
774
|
+
webviewTag: true,
|
|
775
|
+
preload: cjsSiblingPreloadPath(ctx.preloadPath),
|
|
776
|
+
partition: 'persist:simulator',
|
|
777
|
+
},
|
|
778
|
+
});
|
|
779
|
+
nativeSimulatorView = view;
|
|
780
|
+
// Paint the WCV surface the simulator-panel background (≈ --color-sim-bg
|
|
781
|
+
// hsl(0 0% 7%)) so a height-resize that grows the region never flashes white
|
|
782
|
+
// in the newly-exposed strip before DeviceShell's desk repaints — the WCV,
|
|
783
|
+
// the desk, and the renderer placeholder behind it are all the same color.
|
|
784
|
+
view.setBackgroundColor('#121212');
|
|
785
|
+
const simWc = view.webContents;
|
|
786
|
+
// Service the simulator-side `__diminaCustomApis` bridge: this top-level
|
|
787
|
+
// WebContentsView has no embedder renderer to proxy through, so dispatch its
|
|
788
|
+
// `sendToHost` requests straight to `ctx.simulatorApis` from main.
|
|
789
|
+
attachNativeCustomApiBridge(simWc);
|
|
790
|
+
// DeviceShell mounts per-page render-host `<webview>`s INSIDE this view.
|
|
791
|
+
// Mirror windows/main-window/create.ts: pin them onto persist:simulator and
|
|
792
|
+
// run them with contextIsolation/sandbox off so the render runtime + its
|
|
793
|
+
// preload share the page realm. (A top-level WebContentsView can host these
|
|
794
|
+
// guests; a `<webview>` guest cannot — that's the whole point of Option A.)
|
|
795
|
+
simWc.on('will-attach-webview', (_event, webPreferences, params) => {
|
|
796
|
+
;
|
|
797
|
+
webPreferences.partition = 'persist:simulator';
|
|
798
|
+
params.partition = 'persist:simulator';
|
|
799
|
+
webPreferences.contextIsolation = false;
|
|
800
|
+
webPreferences.sandbox = false;
|
|
801
|
+
});
|
|
802
|
+
simWc.on('did-attach-webview', (_event, guestWc) => {
|
|
803
|
+
// Scale the nested render-host page with the device zoom. The host WCV is
|
|
804
|
+
// sized to the SCALED bezel rect and runs at `currentZoomFactor`, so the
|
|
805
|
+
// guest must run at the same factor to lay the page out at the logical
|
|
806
|
+
// device width and paint at the right scale. At 100% (default) this is a
|
|
807
|
+
// no-op identity (factor 1). Newly-attached guests pick up the latest
|
|
808
|
+
// zoom here; live zoom changes re-apply via setNativeSimulatorViewBounds.
|
|
809
|
+
try {
|
|
810
|
+
guestWc.setZoomFactor(currentZoomFactor);
|
|
811
|
+
}
|
|
812
|
+
catch { /* guest not ready; setNativeSimulatorViewBounds re-applies */ }
|
|
813
|
+
// Simulate this device's CSS env(safe-area-inset-*) on the fresh guest
|
|
814
|
+
// before it paints, so notch-aware page layout resolves correctly.
|
|
815
|
+
safeArea.applyToGuest(guestWc, ctx.bridge?.getDevice() ?? null);
|
|
816
|
+
guestWc.setWindowOpenHandler(({ url }) => handleWindowOpenExternal(url));
|
|
817
|
+
guestWc.on('will-navigate', (e, url) => {
|
|
818
|
+
try {
|
|
819
|
+
const u = new URL(url);
|
|
820
|
+
if (u.protocol === 'about:')
|
|
821
|
+
return;
|
|
822
|
+
if ((u.protocol === 'http:' || u.protocol === 'https:')
|
|
823
|
+
&& (u.hostname === 'localhost' || u.hostname === '127.0.0.1')) {
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
if (u.protocol === 'file:')
|
|
827
|
+
return;
|
|
828
|
+
e.preventDefault();
|
|
829
|
+
if (u.protocol === 'http:' || u.protocol === 'https:') {
|
|
830
|
+
void shell.openExternal(url);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
catch {
|
|
834
|
+
e.preventDefault();
|
|
835
|
+
}
|
|
836
|
+
});
|
|
837
|
+
// A render-host guest only attaches after a spawn, so the service window
|
|
838
|
+
// exists by now — (re)point the right-panel DevTools at it. Belt-and-braces
|
|
839
|
+
// with the `onRenderEvent` path in case its emit lost the attach race.
|
|
840
|
+
followNativeDevtoolsServiceHost();
|
|
841
|
+
});
|
|
842
|
+
// The simulator loads http://localhost:<port>/simulator.html. Harden popups
|
|
843
|
+
// + in-place navigation: allow the dev server origin (and about:blank /
|
|
844
|
+
// file:// render hosts), route everything else to the OS browser.
|
|
845
|
+
simWc.setWindowOpenHandler(({ url }) => handleWindowOpenExternal(url));
|
|
846
|
+
simWc.on('will-navigate', (e, url) => {
|
|
847
|
+
try {
|
|
848
|
+
const u = new URL(url);
|
|
849
|
+
if (u.protocol === 'about:')
|
|
850
|
+
return;
|
|
851
|
+
if (u.protocol === 'file:')
|
|
852
|
+
return;
|
|
853
|
+
if ((u.protocol === 'http:' || u.protocol === 'https:')
|
|
854
|
+
&& (u.hostname === 'localhost' || u.hostname === '127.0.0.1')) {
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
e.preventDefault();
|
|
858
|
+
if (u.protocol === 'http:' || u.protocol === 'https:') {
|
|
859
|
+
void shell.openExternal(url);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
catch {
|
|
863
|
+
e.preventDefault();
|
|
864
|
+
}
|
|
865
|
+
});
|
|
866
|
+
void simWc.loadURL(simulatorUrl).catch((err) => {
|
|
867
|
+
console.error('[workbench] attachNativeSimulator — loadURL failed', err);
|
|
868
|
+
});
|
|
869
|
+
// This WebContentsView's webContents IS the simulator: the native-host
|
|
870
|
+
// preload runs here and issues SPAWN, so bridge-router resolves
|
|
871
|
+
// ap.simulatorWc = event.sender = this wc, and SIMULATOR_EVENTS flow back.
|
|
872
|
+
simulatorWebContentsId = simWc.id;
|
|
873
|
+
// Forward this WCV's network requests (wx.request/download/upload run here,
|
|
874
|
+
// not in the service host) into the service-host console so they show in the
|
|
875
|
+
// embedded DevTools the right panel hosts. Attaches the CDP debugger; no-op
|
|
876
|
+
// if the forwarder is unwired (partial test ctx) or the debugger is claimed.
|
|
877
|
+
ctx.networkForward?.attachSimulator(simWc);
|
|
878
|
+
// Model A: bounds come ONLY from the renderer's measured inner-screen rect.
|
|
879
|
+
// If a report already landed before this attach (project-open ordering),
|
|
880
|
+
// replay it now — setNativeSimulatorViewBounds adds + sizes the view. Else
|
|
881
|
+
// the view stays unadded until the next reportBounds. No coarse fallback.
|
|
882
|
+
if (lastRendererRect) {
|
|
883
|
+
setNativeSimulatorViewBounds(lastRendererRect);
|
|
884
|
+
}
|
|
885
|
+
// Native-host page code (console.log / wx.request / Sources) runs in the
|
|
886
|
+
// hidden SERVICE HOST window, not this DeviceShell host nor the render-host
|
|
887
|
+
// guests. Keep the right-panel DevTools host view stable and point it at the
|
|
888
|
+
// service host so its Console/Network(fetch)/Sources reflect the logic layer.
|
|
889
|
+
// (The UI/view layer's Elements equivalent is the native WXML panel +
|
|
890
|
+
// render-guest highlight chain, which targets the active render guest.)
|
|
891
|
+
attachNativeSimulatorDevtoolsHost(simWidth);
|
|
892
|
+
}
|
|
169
893
|
function detachSimulator() {
|
|
894
|
+
stopFollowingNativeServiceHost();
|
|
895
|
+
detachNativeCustomApiBridge();
|
|
896
|
+
// Stop Elements forwarding (detaches only the debugger sessions IT attached;
|
|
897
|
+
// safe-area's sessions are untouched). Idempotent — the host 'destroyed'
|
|
898
|
+
// handler may have already run.
|
|
899
|
+
try {
|
|
900
|
+
stopElementsForward?.();
|
|
901
|
+
}
|
|
902
|
+
catch { /* already stopped */ }
|
|
903
|
+
stopElementsForward = null;
|
|
904
|
+
// Stop forwarding the simulator WCV's network (its debugger is detached as
|
|
905
|
+
// the view is closed below, but drop our session deterministically first).
|
|
906
|
+
// Also drop the DevTools front-end host — its wc is destroyed below; a stale
|
|
907
|
+
// ref would make the forwarder dispatch into a dead wc instead of falling
|
|
908
|
+
// back to the console.
|
|
909
|
+
ctx.networkForward?.detachSimulator();
|
|
910
|
+
ctx.networkForward?.setDevtoolsHost(null);
|
|
911
|
+
// Native-host simulator content view (no-op in the default path).
|
|
912
|
+
if (nativeSimulatorView) {
|
|
913
|
+
if (nativeSimulatorViewAdded && !ctx.windows.mainWindow.isDestroyed()) {
|
|
914
|
+
try {
|
|
915
|
+
ctx.windows.mainWindow.contentView.removeChildView(nativeSimulatorView);
|
|
916
|
+
}
|
|
917
|
+
catch { /* already removed */ }
|
|
918
|
+
}
|
|
919
|
+
try {
|
|
920
|
+
if (!nativeSimulatorView.webContents.isDestroyed()) {
|
|
921
|
+
nativeSimulatorView.webContents.close();
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
catch { /* ignore */ }
|
|
925
|
+
nativeSimulatorView = null;
|
|
926
|
+
nativeSimulatorViewAdded = false;
|
|
927
|
+
}
|
|
170
928
|
hidePopover();
|
|
171
929
|
// Drop the settings view too — the previous detachAllViews() did.
|
|
172
930
|
destroyViewInternal(settingsView);
|
|
@@ -179,6 +937,10 @@ export function createViewManager(ctx) {
|
|
|
179
937
|
// Drop the renderer-published rect so a stale "hidden" override doesn't
|
|
180
938
|
// suppress the next view before its renderer republishes.
|
|
181
939
|
simulatorBoundsOverride = null;
|
|
940
|
+
// Also drop the cached native-simulator rect: attachNativeSimulator replays
|
|
941
|
+
// lastRendererRect on attach, so a leftover rect/offset from a torn-down
|
|
942
|
+
// session must not be replayed onto a fresh re-attach (stale slice/offset).
|
|
943
|
+
lastRendererRect = null;
|
|
182
944
|
}
|
|
183
945
|
function showSimulator(simWidth) {
|
|
184
946
|
lastSimWidth = simWidth;
|
|
@@ -260,6 +1022,8 @@ export function createViewManager(ctx) {
|
|
|
260
1022
|
}
|
|
261
1023
|
}
|
|
262
1024
|
function repositionAll() {
|
|
1025
|
+
// Native simulator: bounds owned by the renderer's reportBounds (Model A);
|
|
1026
|
+
// its window-resize listener re-measures, so no coarse re-apply here.
|
|
263
1027
|
if (simulatorView && simulatorViewAdded)
|
|
264
1028
|
applySimulatorBounds(lastSimWidth);
|
|
265
1029
|
if (settingsView && settingsViewAdded)
|
|
@@ -269,9 +1033,67 @@ export function createViewManager(ctx) {
|
|
|
269
1033
|
}
|
|
270
1034
|
function disposeAll() {
|
|
271
1035
|
detachSimulator();
|
|
1036
|
+
// Host-controllable toolbar view: removed from the contentView + its
|
|
1037
|
+
// WebContents closed (the host's loaded content is torn down on app exit).
|
|
1038
|
+
destroyViewInternal(hostToolbarView);
|
|
1039
|
+
hostToolbarView = null;
|
|
1040
|
+
hostToolbarViewAdded = false;
|
|
1041
|
+
safeArea.dispose();
|
|
1042
|
+
}
|
|
1043
|
+
function setNativeSimulatorViewBounds(params) {
|
|
1044
|
+
// Cache unconditionally so attachNativeSimulator can replay a report that
|
|
1045
|
+
// landed before the view existed (project-open ordering). This rect is the
|
|
1046
|
+
// ONLY authority for the native WCV bounds.
|
|
1047
|
+
lastRendererRect = params;
|
|
1048
|
+
if (ctx.windows.mainWindow.isDestroyed() || !nativeSimulatorView)
|
|
1049
|
+
return;
|
|
1050
|
+
const p = layout.computeNativeSimulatorViewParams(params, params.zoom);
|
|
1051
|
+
currentZoomFactor = p.zoomFactor;
|
|
1052
|
+
// A zero-area rect means "hide" — the renderer reports this when the
|
|
1053
|
+
// simulator panel/cell is toggled off or unmounts (toolbar toggle only
|
|
1054
|
+
// mutates renderer layout state, so without this the WCV would stay
|
|
1055
|
+
// painted over its old region). Mirror `setSimulatorDevtoolsBounds`:
|
|
1056
|
+
// remove the child view from the contentView but keep its WebContents
|
|
1057
|
+
// alive, so re-showing doesn't re-pay the simulator bootstrap.
|
|
1058
|
+
if (isHidden(p.bounds)) {
|
|
1059
|
+
if (nativeSimulatorViewAdded) {
|
|
1060
|
+
try {
|
|
1061
|
+
ctx.windows.mainWindow.contentView.removeChildView(nativeSimulatorView);
|
|
1062
|
+
}
|
|
1063
|
+
catch { /* already removed */ }
|
|
1064
|
+
nativeSimulatorViewAdded = false;
|
|
1065
|
+
}
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
1068
|
+
if (!nativeSimulatorViewAdded) {
|
|
1069
|
+
ctx.windows.mainWindow.contentView.addChildView(nativeSimulatorView);
|
|
1070
|
+
nativeSimulatorViewAdded = true;
|
|
1071
|
+
}
|
|
1072
|
+
nativeSimulatorView.setBounds(p.bounds);
|
|
1073
|
+
const simWc = nativeSimulatorView.webContents;
|
|
1074
|
+
if (!simWc.isDestroyed()) {
|
|
1075
|
+
simWc.setZoomFactor(p.zoomFactor);
|
|
1076
|
+
}
|
|
1077
|
+
// Propagate zoom to any already-attached nested render-host guests so the
|
|
1078
|
+
// page rescales live on zoom change (newly-attached guests get it in
|
|
1079
|
+
// did-attach-webview). `webContents.getAllWebContents()` includes guests;
|
|
1080
|
+
// filter to those hosted by this simulator wc.
|
|
1081
|
+
try {
|
|
1082
|
+
for (const wc of webContents.getAllWebContents()) {
|
|
1083
|
+
if (wc.isDestroyed())
|
|
1084
|
+
continue;
|
|
1085
|
+
if (wc.hostWebContents === simWc) {
|
|
1086
|
+
wc.setZoomFactor(p.zoomFactor);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
catch { /* hostWebContents unavailable; guests get zoom on attach */ }
|
|
272
1091
|
}
|
|
273
1092
|
function resize(simWidth) {
|
|
274
1093
|
lastSimWidth = simWidth;
|
|
1094
|
+
// Native simulator bounds are owned solely by the renderer's reportBounds
|
|
1095
|
+
// (Model A) — its ResizeObserver/window-resize listeners re-measure on this
|
|
1096
|
+
// same resize. No coarse panel-size re-apply here (it raced the precise rect).
|
|
275
1097
|
if (simulatorViewAdded)
|
|
276
1098
|
applySimulatorBounds(simWidth);
|
|
277
1099
|
if (settingsViewAdded)
|
|
@@ -290,7 +1112,9 @@ export function createViewManager(ctx) {
|
|
|
290
1112
|
}
|
|
291
1113
|
return {
|
|
292
1114
|
attachSimulator,
|
|
1115
|
+
attachNativeSimulator,
|
|
293
1116
|
detachSimulator,
|
|
1117
|
+
reapplySafeArea: (device) => safeArea.reapplyAll(device),
|
|
294
1118
|
showSimulator,
|
|
295
1119
|
hideSimulator,
|
|
296
1120
|
showSettings,
|
|
@@ -330,9 +1154,20 @@ export function createViewManager(ctx) {
|
|
|
330
1154
|
return null;
|
|
331
1155
|
return popoverView.webContents.id;
|
|
332
1156
|
},
|
|
1157
|
+
getHostToolbarWebContentsId: () => {
|
|
1158
|
+
if (!hostToolbarView)
|
|
1159
|
+
return null;
|
|
1160
|
+
if (hostToolbarView.webContents.isDestroyed())
|
|
1161
|
+
return null;
|
|
1162
|
+
return hostToolbarView.webContents.id;
|
|
1163
|
+
},
|
|
1164
|
+
setNativeSimulatorViewBounds,
|
|
333
1165
|
resize,
|
|
334
1166
|
setVisible,
|
|
335
1167
|
setSimulatorDevtoolsBounds,
|
|
1168
|
+
setHostToolbarBounds,
|
|
1169
|
+
setHostToolbarHeight,
|
|
1170
|
+
hostToolbar,
|
|
336
1171
|
};
|
|
337
1172
|
}
|
|
338
1173
|
//# sourceMappingURL=view-manager.js.map
|