@dimina-kit/devtools 0.3.2-dev.20260611063505 → 0.4.0-dev.20260612025610
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 +56 -41
- package/dist/main/api.d.ts +4 -2
- package/dist/main/api.js +5 -1
- package/dist/main/app/app.d.ts +1 -5
- package/dist/main/app/app.js +25 -30
- package/dist/main/app/launch.d.ts +4 -4
- package/dist/main/app/launch.js +5 -17
- package/dist/main/index.bundle.js +535 -593
- package/dist/main/ipc/app.d.ts +1 -1
- package/dist/main/ipc/app.js +0 -7
- package/dist/main/ipc/index.d.ts +0 -2
- package/dist/main/ipc/index.js +0 -2
- package/dist/main/ipc/settings.d.ts +1 -1
- package/dist/main/ipc/settings.js +1 -13
- package/dist/main/ipc/simulator-module.d.ts +3 -3
- package/dist/main/ipc/simulator-module.js +3 -7
- package/dist/main/ipc/simulator.js +1 -12
- package/dist/main/menu/index.d.ts +2 -2
- package/dist/main/menu/index.js +4 -2
- package/dist/main/runtime/miniapp-runtime.d.ts +206 -28
- package/dist/main/runtime/miniapp-runtime.js +6 -11
- package/dist/main/services/layout/index.d.ts +0 -11
- package/dist/main/services/layout/index.js +0 -23
- package/dist/main/services/notifications/renderer-notifier.d.ts +0 -6
- package/dist/main/services/notifications/renderer-notifier.js +1 -10
- package/dist/main/services/service-host-pool/pool.d.ts +5 -4
- package/dist/main/services/views/host-toolbar-port-channel.d.ts +103 -0
- package/dist/main/services/views/host-toolbar-port-channel.js +248 -0
- package/dist/main/services/views/host-toolbar-session-runtime.d.ts +35 -0
- package/dist/main/services/views/host-toolbar-session-runtime.js +55 -0
- package/dist/main/services/views/view-manager.d.ts +80 -39
- package/dist/main/services/views/view-manager.js +153 -182
- package/dist/main/services/workbench-context.d.ts +13 -26
- package/dist/main/services/workbench-context.js +5 -14
- package/dist/main/services/workspace/workspace-service.d.ts +3 -7
- package/dist/main/services/workspace/workspace-service.js +34 -13
- package/dist/main/utils/paths.d.ts +11 -2
- package/dist/main/utils/paths.js +11 -2
- package/dist/main/windows/settings-window/index.d.ts +33 -0
- package/dist/main/windows/settings-window/index.js +59 -0
- package/dist/preload/index.d.ts +15 -0
- package/dist/preload/index.js +15 -0
- package/dist/preload/runtime/host-toolbar-port.d.ts +49 -0
- package/dist/preload/runtime/host-toolbar-port.js +151 -0
- package/dist/preload/runtime/host-toolbar-runtime.d.ts +41 -0
- package/dist/preload/runtime/host-toolbar-runtime.js +49 -0
- package/dist/preload/windows/{host-toolbar.cjs → host-toolbar-runtime.cjs} +103 -3
- package/dist/preload/windows/host-toolbar-runtime.cjs.map +7 -0
- package/dist/preload/windows/host-toolbar-runtime.d.ts +2 -0
- package/dist/preload/windows/host-toolbar-runtime.js +13 -0
- package/dist/preload/windows/simulator.cjs +0 -2
- package/dist/preload/windows/simulator.cjs.map +2 -2
- package/dist/preload/windows/simulator.js +0 -2
- package/dist/renderer/assets/constants-DI382mTP.js +2 -0
- package/dist/renderer/assets/index-DLzjz1wv.js +50 -0
- package/dist/renderer/assets/{input-CFHmbOgz.js → input-6fpCXV-n.js} +2 -2
- package/dist/renderer/assets/ipc-transport-Bs8Sf1B2.css +1 -0
- package/dist/renderer/assets/{ipc-transport-BkKKww3u.js → ipc-transport-D22e4dv7.js} +6 -6
- package/dist/renderer/assets/popover-CdGdKkkc.js +2 -0
- package/dist/renderer/assets/select-X0sbjwdP.js +2 -0
- package/dist/renderer/assets/{settings-Cl4IcWnG.js → settings-CS4jYaCw.js} +2 -2
- package/dist/renderer/assets/settings-api-CCWTAao_.js +2 -0
- package/dist/renderer/assets/workbenchSettings-Bpzaj3bm.js +8 -0
- package/dist/renderer/entries/main/index.html +7 -6
- package/dist/renderer/entries/popover/index.html +6 -6
- package/dist/renderer/entries/settings/index.html +5 -5
- package/dist/renderer/entries/workbench-settings/index.html +5 -5
- package/dist/shared/constants.d.ts +18 -0
- package/dist/shared/constants.js +18 -0
- package/dist/shared/ipc-channels.d.ts +10 -23
- package/dist/shared/ipc-channels.js +13 -34
- package/dist/shared/ipc-schemas.d.ts +0 -12
- package/dist/shared/ipc-schemas.js +0 -12
- package/dist/shared/types.d.ts +60 -76
- package/package.json +6 -6
- package/dist/main/ipc/panels.d.ts +0 -4
- package/dist/main/ipc/panels.js +0 -46
- package/dist/main/ipc/toolbar.d.ts +0 -4
- package/dist/main/ipc/toolbar.js +0 -21
- package/dist/main/services/toolbar/toolbar-store.d.ts +0 -23
- package/dist/main/services/toolbar/toolbar-store.js +0 -40
- package/dist/preload/windows/host-toolbar.cjs.map +0 -7
- package/dist/preload/windows/host-toolbar.d.ts +0 -2
- package/dist/preload/windows/host-toolbar.js +0 -12
- package/dist/renderer/assets/constants-CLAQX6VZ.js +0 -2
- package/dist/renderer/assets/index-CZQOpCvg.js +0 -50
- package/dist/renderer/assets/ipc-transport-Bv_BVu4T.css +0 -1
- package/dist/renderer/assets/popover-h1M7n3tt.js +0 -2
- package/dist/renderer/assets/select-kj0n1oYf.js +0 -2
- package/dist/renderer/assets/settings-api-CCwo2eOi.js +0 -2
- package/dist/renderer/assets/workbenchSettings-BzlpQnve.js +0 -8
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ipcMain, shell, WebContentsView, webContents } from 'electron';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { cjsSiblingPreloadPath,
|
|
3
|
+
import { cjsSiblingPreloadPath, mainPreloadPath } from '../../utils/paths.js';
|
|
4
4
|
import { applyNavigationHardening, handleWindowOpenExternal, } from '../../windows/navigation-hardening.js';
|
|
5
5
|
import { SimulatorCustomApiBridgeChannel } from '../../../shared/ipc-channels.js';
|
|
6
6
|
import { OPEN_IN_EDITOR_SCHEME, decodeOpenInEditorUrl, resourceUrlToProjectRelativePath, } from '../../../shared/open-in-editor.js';
|
|
@@ -9,9 +9,11 @@ import { buildCustomizeTabsScript } from './devtools-tabs.js';
|
|
|
9
9
|
import { installElementsForward } from '../elements-forward/index.js';
|
|
10
10
|
import * as layout from '../layout/index.js';
|
|
11
11
|
import { handleCustomApiBridgeRequest, } from '../simulator/custom-apis.js';
|
|
12
|
-
import { getDefaultTab } from '../workbench-context.js';
|
|
13
12
|
import { configureMiniappSession, miniappPartition } from './miniapp-partition.js';
|
|
13
|
+
import { acquireHostToolbarSessionRuntime, releaseHostToolbarSessionRuntime, } from './host-toolbar-session-runtime.js';
|
|
14
|
+
import { createHostToolbarPortChannel, } from './host-toolbar-port-channel.js';
|
|
14
15
|
import { parseRoute } from '../../../shared/simulator-route.js';
|
|
16
|
+
import { HEADER_H, HOST_TOOLBAR_RUNTIME_MARKER } from '../../../shared/constants.js';
|
|
15
17
|
/**
|
|
16
18
|
* Build a ViewManager bound to the given context. The returned object is the
|
|
17
19
|
* only component allowed to instantiate or add/remove overlay WebContentsViews.
|
|
@@ -20,9 +22,6 @@ import { parseRoute } from '../../../shared/simulator-route.js';
|
|
|
20
22
|
* on the context object.
|
|
21
23
|
*/
|
|
22
24
|
export function createViewManager(ctx) {
|
|
23
|
-
// Resolve once: full WorkbenchContext always provides headerHeight; partial
|
|
24
|
-
// test contexts may omit it, in which case fall back to the default 40.
|
|
25
|
-
const headerHeight = ctx.headerHeight ?? 40;
|
|
26
25
|
// CSS env(safe-area-inset-*) simulation for render-host guests (per device).
|
|
27
26
|
// Driven from did-attach-webview below; re-pushed on device change via
|
|
28
27
|
// reapplySafeArea. Torn down in disposeAll.
|
|
@@ -56,7 +55,6 @@ export function createViewManager(ctx) {
|
|
|
56
55
|
let settingsView = null;
|
|
57
56
|
let settingsViewAdded = false;
|
|
58
57
|
let popoverView = null;
|
|
59
|
-
let lastSimWidth = 375;
|
|
60
58
|
let simulatorWebContentsId = null;
|
|
61
59
|
// NATIVE-HOST ONLY. The webContents the right-panel Chrome DevTools front-end
|
|
62
60
|
// currently inspects. We point it at the SERVICE HOST (logic layer) — the
|
|
@@ -75,10 +73,10 @@ export function createViewManager(ctx) {
|
|
|
75
73
|
let stopElementsForward = null;
|
|
76
74
|
let nativeDevtoolsRetryTimer = null;
|
|
77
75
|
let nativeDevtoolsRetryToken = 0;
|
|
78
|
-
// Renderer-driven overlay bounds for the simulator DevTools view
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
//
|
|
76
|
+
// Renderer-driven overlay bounds for the simulator DevTools view — the SOLE
|
|
77
|
+
// mount/geometry authority (no static-layout fallback). A zero-area
|
|
78
|
+
// rectangle means "hide" — the overlay is removed from the contentView but
|
|
79
|
+
// its WebContents stays alive.
|
|
82
80
|
let simulatorBoundsOverride = null;
|
|
83
81
|
// ── Host-controllable toolbar WebContentsView ───────────────────────────
|
|
84
82
|
// A strip above the devtools header that the downstream host loads its own
|
|
@@ -88,6 +86,21 @@ export function createViewManager(ctx) {
|
|
|
88
86
|
let hostToolbarView = null;
|
|
89
87
|
let hostToolbarPreloadOverride = null;
|
|
90
88
|
let hostToolbarViewAdded = false;
|
|
89
|
+
// Whether THIS manager holds a reference on the shared defaultSession
|
|
90
|
+
// registration of the toolbar-runtime preload (see
|
|
91
|
+
// host-toolbar-session-runtime.ts). Acquired on first toolbar need,
|
|
92
|
+
// released exactly once in disposeAll — a manager that never used the
|
|
93
|
+
// toolbar must not decrement a ref it never took.
|
|
94
|
+
let hostToolbarRuntimeAcquired = false;
|
|
95
|
+
// Placeholder height authority: 'auto' = advertiser reports forward to the
|
|
96
|
+
// renderer; { fixed } = host-pinned, advertiser reports are dropped.
|
|
97
|
+
let hostToolbarHeightMode = 'auto';
|
|
98
|
+
// Gated narrow channel to the toolbar PAGE (per-load MessagePort handshake;
|
|
99
|
+
// see host-toolbar-port-channel.ts). Control-level registry — created with
|
|
100
|
+
// the manager so onMessage() works before any toolbar view exists.
|
|
101
|
+
const hostToolbarPort = createHostToolbarPortChannel({
|
|
102
|
+
isCurrent: (wc) => liveHostToolbarWebContents() === wc,
|
|
103
|
+
});
|
|
91
104
|
// ── Internal helpers ────────────────────────────────────────────────────
|
|
92
105
|
function destroyViewInternal(view) {
|
|
93
106
|
if (!view)
|
|
@@ -105,16 +118,6 @@ export function createViewManager(ctx) {
|
|
|
105
118
|
}
|
|
106
119
|
catch { /* ignore */ }
|
|
107
120
|
}
|
|
108
|
-
function applySimulatorBounds(simWidth) {
|
|
109
|
-
if (!simulatorView || ctx.windows.mainWindow.isDestroyed())
|
|
110
|
-
return;
|
|
111
|
-
if (simulatorBoundsOverride) {
|
|
112
|
-
simulatorView.setBounds(simulatorBoundsOverride);
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
const [w = 0, h = 0] = ctx.windows.mainWindow.getContentSize();
|
|
116
|
-
simulatorView.setBounds(layout.computeSimulatorBounds(w, h, simWidth, headerHeight));
|
|
117
|
-
}
|
|
118
121
|
// Renderer publishes width/height = 0 to mean "hide overlay" — the
|
|
119
122
|
// surrounding React panel is unmounted/collapsed. We keep the cached
|
|
120
123
|
// value (so future republishes win over legacy layout) but remove the
|
|
@@ -145,11 +148,23 @@ export function createViewManager(ctx) {
|
|
|
145
148
|
simulatorView.setBounds(bounds);
|
|
146
149
|
}
|
|
147
150
|
// ── Host-controllable toolbar WebContentsView ───────────────────────────
|
|
151
|
+
// The toolbar's webContents lifecycle belongs to the HOST, which may close
|
|
152
|
+
// it out from under us (the documented rebuild path). In real Electron a
|
|
153
|
+
// WebContentsView whose webContents was destroyed can report `webContents`
|
|
154
|
+
// as undefined — not merely a destroyed handle — so every access must
|
|
155
|
+
// tolerate BOTH (observed in the R1 e2e: `.isDestroyed()` on undefined threw
|
|
156
|
+
// inside the control surface after the host closed the wc).
|
|
157
|
+
function liveHostToolbarWebContents() {
|
|
158
|
+
const wc = hostToolbarView?.webContents;
|
|
159
|
+
if (!wc || wc.isDestroyed())
|
|
160
|
+
return null;
|
|
161
|
+
return wc;
|
|
162
|
+
}
|
|
148
163
|
// Lazily create the host-toolbar view. Mirrors `showSettings` for the
|
|
149
164
|
// webPreferences shape and the native simulator for nav hardening +
|
|
150
165
|
// background color (the host may load arbitrary URLs / content). Idempotent.
|
|
151
166
|
function ensureHostToolbarView() {
|
|
152
|
-
if (hostToolbarView &&
|
|
167
|
+
if (hostToolbarView && liveHostToolbarWebContents()) {
|
|
153
168
|
return hostToolbarView;
|
|
154
169
|
}
|
|
155
170
|
// Rebuilding after the host destroyed the underlying webContents: detach the
|
|
@@ -163,21 +178,36 @@ export function createViewManager(ctx) {
|
|
|
163
178
|
catch { /* already removed */ }
|
|
164
179
|
hostToolbarViewAdded = false;
|
|
165
180
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
181
|
+
// The framework's height-advertiser runtime is SESSION-resident: register
|
|
182
|
+
// it on session.defaultSession (ref-counted across coexisting managers)
|
|
183
|
+
// BEFORE the view exists, so the very first load already runs it. The
|
|
184
|
+
// toolbar WCV stays on the defaultSession (no partition/session override)
|
|
185
|
+
// — moving it onto its own partition would silently detach it from this
|
|
186
|
+
// registration and height advertising would die with no error.
|
|
187
|
+
if (!hostToolbarRuntimeAcquired) {
|
|
188
|
+
acquireHostToolbarSessionRuntime();
|
|
189
|
+
hostToolbarRuntimeAcquired = true;
|
|
190
|
+
}
|
|
191
|
+
// `webPreferences.preload` is the HOST's alone (setPreloadPath); the
|
|
192
|
+
// built-in advertiser no longer rides it (it would execute twice — the
|
|
193
|
+
// session copy + the webPreferences copy). The additionalArguments marker
|
|
194
|
+
// is what the session runtime's guard keys on to activate here and stay a
|
|
195
|
+
// zero-footprint no-op in every other defaultSession renderer.
|
|
196
|
+
const webPreferences = {
|
|
197
|
+
nodeIntegration: false,
|
|
198
|
+
contextIsolation: true,
|
|
199
|
+
sandbox: false,
|
|
200
|
+
additionalArguments: [HOST_TOOLBAR_RUNTIME_MARKER],
|
|
201
|
+
};
|
|
202
|
+
if (hostToolbarPreloadOverride !== null) {
|
|
203
|
+
webPreferences.preload = hostToolbarPreloadOverride;
|
|
204
|
+
}
|
|
205
|
+
const view = new WebContentsView({ webPreferences });
|
|
180
206
|
hostToolbarView = view;
|
|
207
|
+
// Hook the per-load MessagePort handshake (did-finish-load) + dead-port
|
|
208
|
+
// cleanup (destroyed) on the fresh wc. AFTER the assignment above so the
|
|
209
|
+
// channel's isCurrent guard sees this wc as the live one.
|
|
210
|
+
hostToolbarPort.attach(view.webContents);
|
|
181
211
|
// Paint the surface a neutral color so growing the reserved strip never
|
|
182
212
|
// flashes white before the host content paints (mirrors the native
|
|
183
213
|
// simulator's setBackgroundColor anti-flash).
|
|
@@ -200,7 +230,7 @@ export function createViewManager(ctx) {
|
|
|
200
230
|
// WebContents (and the host's loaded content) alive. Do NOT create the
|
|
201
231
|
// view just to immediately hide it.
|
|
202
232
|
if (isHidden(bounds)) {
|
|
203
|
-
if (hostToolbarView && hostToolbarViewAdded &&
|
|
233
|
+
if (hostToolbarView && hostToolbarViewAdded && liveHostToolbarWebContents()) {
|
|
204
234
|
try {
|
|
205
235
|
ctx.windows.mainWindow.contentView.removeChildView(hostToolbarView);
|
|
206
236
|
}
|
|
@@ -219,6 +249,12 @@ export function createViewManager(ctx) {
|
|
|
219
249
|
view.setBounds(bounds);
|
|
220
250
|
}
|
|
221
251
|
function setHostToolbarHeight(extent) {
|
|
252
|
+
// While the host pins a fixed height, drop advertiser reports entirely —
|
|
253
|
+
// the session-resident advertiser is always installed, so forwarding its
|
|
254
|
+
// reports would make the strip oscillate between the pinned and measured
|
|
255
|
+
// heights on every content resize.
|
|
256
|
+
if (hostToolbarHeightMode !== 'auto')
|
|
257
|
+
return;
|
|
222
258
|
// Push the reserved height back to the main-window renderer so its
|
|
223
259
|
// placeholder div resizes (closing the dynamic-height loop). The height is
|
|
224
260
|
// not retained in main — the renderer placeholder is the single source of
|
|
@@ -242,37 +278,72 @@ export function createViewManager(ctx) {
|
|
|
242
278
|
const hostToolbar = {
|
|
243
279
|
async loadURL(url) {
|
|
244
280
|
const view = ensureHostToolbarView();
|
|
281
|
+
// Invalidate SYNCHRONOUSLY at initiation, before the load is issued:
|
|
282
|
+
// the current document is about to be replaced, so a same-tick send()
|
|
283
|
+
// must report false instead of confirming delivery into it. The channel
|
|
284
|
+
// recovers on the new document's did-finish-load handshake. (Cannot
|
|
285
|
+
// rely on did-start-navigation here — that only covers page-initiated
|
|
286
|
+
// navigations once the load is actually under way.)
|
|
287
|
+
hostToolbarPort.invalidate();
|
|
245
288
|
await view.webContents.loadURL(url);
|
|
246
289
|
},
|
|
247
290
|
async loadFile(filePath) {
|
|
248
291
|
const view = ensureHostToolbarView();
|
|
292
|
+
// Same initiation-invalidates contract as loadURL above.
|
|
293
|
+
hostToolbarPort.invalidate();
|
|
249
294
|
await view.webContents.loadFile(filePath);
|
|
250
295
|
},
|
|
251
296
|
get webContents() {
|
|
252
|
-
|
|
253
|
-
return null;
|
|
254
|
-
if (hostToolbarView.webContents.isDestroyed())
|
|
255
|
-
return null;
|
|
256
|
-
return hostToolbarView.webContents;
|
|
297
|
+
return liveHostToolbarWebContents();
|
|
257
298
|
},
|
|
258
299
|
hide() {
|
|
259
300
|
hideHostToolbar();
|
|
260
301
|
},
|
|
261
302
|
setPreloadPath(path) {
|
|
303
|
+
// The HOST's own webPreferences.preload, applied when the view is next
|
|
304
|
+
// (re)created. `null` = no host preload. The framework advertiser is
|
|
305
|
+
// session-resident and unaffected either way (see ensureHostToolbarView).
|
|
262
306
|
hostToolbarPreloadOverride = path;
|
|
263
307
|
},
|
|
308
|
+
setHeightMode(mode) {
|
|
309
|
+
// Validate BEFORE touching any state: a poisoned `{ fixed }` (NaN /
|
|
310
|
+
// ±Infinity / negative) must neither reach the renderer placeholder
|
|
311
|
+
// (`height: NaNpx` corrupts the strip with no error anywhere) nor
|
|
312
|
+
// clobber the standing mode — fail-closed, not fail-corrupt.
|
|
313
|
+
if (mode !== 'auto' && !(Number.isFinite(mode.fixed) && mode.fixed >= 0)) {
|
|
314
|
+
throw new TypeError(`hostToolbar.setHeightMode: fixed height must be a finite, non-negative number (got ${mode.fixed})`);
|
|
315
|
+
}
|
|
316
|
+
hostToolbarHeightMode = mode;
|
|
317
|
+
if (mode !== 'auto') {
|
|
318
|
+
// Pin immediately: a preload-less/static toolbar never advertises, so
|
|
319
|
+
// waiting for the next report would leave the strip at height 0.
|
|
320
|
+
ctx.notify.hostToolbarHeightChanged(mode.fixed);
|
|
321
|
+
}
|
|
322
|
+
// Switching back to 'auto' deliberately does NOT synthesize a notify —
|
|
323
|
+
// replaying a stale cached height would flash the old size; the NEXT
|
|
324
|
+
// advertiser report drives the placeholder again.
|
|
325
|
+
},
|
|
326
|
+
onMessage(channel, handler) {
|
|
327
|
+
return hostToolbarPort.onMessage(channel, handler);
|
|
328
|
+
},
|
|
329
|
+
onReady(handler) {
|
|
330
|
+
return hostToolbarPort.onReady(handler);
|
|
331
|
+
},
|
|
332
|
+
send(channel, payload) {
|
|
333
|
+
return hostToolbarPort.send(channel, payload);
|
|
334
|
+
},
|
|
264
335
|
};
|
|
265
336
|
function applySettingsBounds() {
|
|
266
337
|
if (!settingsView || ctx.windows.mainWindow.isDestroyed())
|
|
267
338
|
return;
|
|
268
339
|
const [w = 0, h = 0] = ctx.windows.mainWindow.getContentSize();
|
|
269
|
-
settingsView.setBounds(layout.computeSettingsBounds(w, h,
|
|
340
|
+
settingsView.setBounds(layout.computeSettingsBounds(w, h, HEADER_H));
|
|
270
341
|
}
|
|
271
342
|
function applyPopoverBounds() {
|
|
272
343
|
if (!popoverView || ctx.windows.mainWindow.isDestroyed())
|
|
273
344
|
return;
|
|
274
345
|
const [w = 0, h = 0] = ctx.windows.mainWindow.getContentSize();
|
|
275
|
-
popoverView.setBounds(layout.computePopoverBounds(w, h,
|
|
346
|
+
popoverView.setBounds(layout.computePopoverBounds(w, h, HEADER_H));
|
|
276
347
|
}
|
|
277
348
|
function clearNativeDevtoolsRetry() {
|
|
278
349
|
nativeDevtoolsRetryToken++;
|
|
@@ -511,84 +582,11 @@ export function createViewManager(ctx) {
|
|
|
511
582
|
followNativeDevtoolsServiceHost(event.appId);
|
|
512
583
|
}
|
|
513
584
|
// ── ViewManager methods ─────────────────────────────────────────────────
|
|
514
|
-
function
|
|
515
|
-
const sim = webContents.fromId(simWcId);
|
|
516
|
-
if (!sim) {
|
|
517
|
-
console.error('[workbench] attachSimulator — simWc not found for id', simWcId);
|
|
518
|
-
return;
|
|
519
|
-
}
|
|
520
|
-
lastSimWidth = simWidth;
|
|
521
|
-
simulatorWebContentsId = simWcId;
|
|
522
|
-
// Destroy old simulatorView to prevent WebContentsView leak
|
|
523
|
-
if (simulatorView) {
|
|
524
|
-
hideSimulator();
|
|
525
|
-
try {
|
|
526
|
-
if (!simulatorView.webContents.isDestroyed()) {
|
|
527
|
-
simulatorView.webContents.close();
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
catch { /* ignore */ }
|
|
531
|
-
simulatorView = null;
|
|
532
|
-
}
|
|
533
|
-
simulatorView = new WebContentsView();
|
|
534
|
-
sim.setDevToolsWebContents(simulatorView.webContents);
|
|
535
|
-
// Embedded in the right-panel host view; never bring it to the foreground
|
|
536
|
-
// (would steal focus from the user / disrupt e2e).
|
|
537
|
-
sim.openDevTools({ mode: 'detach', activate: false });
|
|
538
|
-
// Default DevTools to Console panel (Chrome DevTools defaults to Elements).
|
|
539
|
-
// The DevTools UI lives inside closed shadow roots, so a light-DOM
|
|
540
|
-
// querySelector('[role="tab"]') cannot reach the tab bar to click it.
|
|
541
|
-
// Instead drive the front-end's own view manager: the bundled DevTools
|
|
542
|
-
// exposes `UI.ViewManager.instance().showView(id)` on `globalThis.UI`
|
|
543
|
-
// once the front-end has finished bootstrapping. We poll for it and
|
|
544
|
-
// request the `console` view, and also persist the choice via the
|
|
545
|
-
// `panel-selectedTab` localStorage key so subsequent reloads honor it.
|
|
546
|
-
const devtoolsWc = simulatorView.webContents;
|
|
547
|
-
// Keep only Elements/Console/Network tabs (front-most, in that order).
|
|
548
|
-
customizeDevtoolsTabs(devtoolsWc);
|
|
549
|
-
devtoolsWc.once('dom-ready', () => {
|
|
550
|
-
devtoolsWc.executeJavaScript(`
|
|
551
|
-
(function() {
|
|
552
|
-
try { localStorage.setItem('panel-selectedTab', '"console"') } catch {}
|
|
553
|
-
let tries = 0
|
|
554
|
-
const timer = setInterval(() => {
|
|
555
|
-
tries++
|
|
556
|
-
try {
|
|
557
|
-
const UI = globalThis.UI
|
|
558
|
-
const vm = UI && UI.ViewManager && typeof UI.ViewManager.instance === 'function'
|
|
559
|
-
? UI.ViewManager.instance()
|
|
560
|
-
: null
|
|
561
|
-
if (vm && typeof vm.showView === 'function') {
|
|
562
|
-
vm.showView('console')
|
|
563
|
-
clearInterval(timer)
|
|
564
|
-
return
|
|
565
|
-
}
|
|
566
|
-
} catch {}
|
|
567
|
-
if (tries > 80) clearInterval(timer)
|
|
568
|
-
}, 50)
|
|
569
|
-
})()
|
|
570
|
-
`).catch(() => { });
|
|
571
|
-
});
|
|
572
|
-
if (getDefaultTab(ctx) === 'simulator') {
|
|
573
|
-
if (simulatorBoundsOverride) {
|
|
574
|
-
if (!isHidden(simulatorBoundsOverride)) {
|
|
575
|
-
ctx.windows.mainWindow.contentView.addChildView(simulatorView);
|
|
576
|
-
simulatorViewAdded = true;
|
|
577
|
-
simulatorView.setBounds(simulatorBoundsOverride);
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
else {
|
|
581
|
-
ctx.windows.mainWindow.contentView.addChildView(simulatorView);
|
|
582
|
-
simulatorViewAdded = true;
|
|
583
|
-
applySimulatorBounds(simWidth);
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
function attachNativeSimulatorDevtoolsHost(simWidth) {
|
|
585
|
+
function attachNativeSimulatorDevtoolsHost() {
|
|
588
586
|
stopFollowingNativeServiceHost();
|
|
589
587
|
// Destroy old simulatorView to prevent WebContentsView leak
|
|
590
588
|
if (simulatorView) {
|
|
591
|
-
|
|
589
|
+
removeSimulatorDevtoolsView();
|
|
592
590
|
try {
|
|
593
591
|
if (!simulatorView.webContents.isDestroyed()) {
|
|
594
592
|
simulatorView.webContents.close();
|
|
@@ -657,19 +655,16 @@ export function createViewManager(ctx) {
|
|
|
657
655
|
})()
|
|
658
656
|
`).catch(() => { });
|
|
659
657
|
});
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
simulatorViewAdded = true;
|
|
671
|
-
applySimulatorBounds(simWidth);
|
|
672
|
-
}
|
|
658
|
+
// Anchor-only mount: the renderer's published rect is the SOLE authority.
|
|
659
|
+
// If a non-zero rect was already published (it can land before this attach
|
|
660
|
+
// on the project-open ordering), replay it; otherwise the view stays
|
|
661
|
+
// unadded and unsized until the first publish arrives. No static-layout
|
|
662
|
+
// fallback — an attach-time computed rect raced the precise anchor rect
|
|
663
|
+
// and flashed the overlay at the wrong rectangle.
|
|
664
|
+
if (simulatorBoundsOverride && !isHidden(simulatorBoundsOverride)) {
|
|
665
|
+
ctx.windows.mainWindow.contentView.addChildView(simulatorView);
|
|
666
|
+
simulatorViewAdded = true;
|
|
667
|
+
simulatorView.setBounds(simulatorBoundsOverride);
|
|
673
668
|
}
|
|
674
669
|
if (ctx.bridge?.isNativeHost()) {
|
|
675
670
|
unsubscribeNativeRenderEvents = ctx.bridge.onRenderEvent(onNativeRenderEvent);
|
|
@@ -738,12 +733,11 @@ export function createViewManager(ctx) {
|
|
|
738
733
|
// the defensive re-attach path calling it directly remains safe.
|
|
739
734
|
ctx.connections.acquire(simWc).own(detachNativeCustomApiBridge);
|
|
740
735
|
}
|
|
741
|
-
function attachNativeSimulator(simulatorUrl,
|
|
736
|
+
function attachNativeSimulator(simulatorUrl, _simWidth) {
|
|
742
737
|
if (!ctx.preloadPath) {
|
|
743
738
|
console.error('[workbench] attachNativeSimulator — preloadPath unset; cannot mount native simulator');
|
|
744
739
|
return;
|
|
745
740
|
}
|
|
746
|
-
lastSimWidth = simWidth;
|
|
747
741
|
// Tear down any previous native simulator view (relaunch / re-open).
|
|
748
742
|
if (nativeSimulatorView) {
|
|
749
743
|
detachNativeCustomApiBridge();
|
|
@@ -901,7 +895,7 @@ export function createViewManager(ctx) {
|
|
|
901
895
|
// service host so its Console/Network(fetch)/Sources reflect the logic layer.
|
|
902
896
|
// (The UI/view layer's Elements equivalent is the native WXML panel +
|
|
903
897
|
// render-guest highlight chain, which targets the active render guest.)
|
|
904
|
-
attachNativeSimulatorDevtoolsHost(
|
|
898
|
+
attachNativeSimulatorDevtoolsHost();
|
|
905
899
|
}
|
|
906
900
|
function detachSimulator() {
|
|
907
901
|
stopFollowingNativeServiceHost();
|
|
@@ -955,23 +949,16 @@ export function createViewManager(ctx) {
|
|
|
955
949
|
// session must not be replayed onto a fresh re-attach (stale slice/offset).
|
|
956
950
|
lastRendererRect = null;
|
|
957
951
|
}
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
if (!simulatorViewAdded) {
|
|
963
|
-
ctx.windows.mainWindow.contentView.addChildView(simulatorView);
|
|
964
|
-
simulatorViewAdded = true;
|
|
965
|
-
}
|
|
966
|
-
applySimulatorBounds(simWidth);
|
|
967
|
-
}
|
|
968
|
-
function hideSimulator() {
|
|
952
|
+
// Remove (but do not destroy) the DevTools overlay from the contentView.
|
|
953
|
+
// Internal teardown helper for re-attach; user-facing visibility is the
|
|
954
|
+
// anchor 0×0 single path (`setSimulatorDevtoolsBounds`).
|
|
955
|
+
function removeSimulatorDevtoolsView() {
|
|
969
956
|
if (simulatorView && simulatorViewAdded) {
|
|
970
957
|
try {
|
|
971
958
|
ctx.windows.mainWindow.contentView.removeChildView(simulatorView);
|
|
972
959
|
}
|
|
973
960
|
catch (e) {
|
|
974
|
-
console.error('[workbench]
|
|
961
|
+
console.error('[workbench] removeSimulatorDevtoolsView error', e);
|
|
975
962
|
}
|
|
976
963
|
simulatorViewAdded = false;
|
|
977
964
|
}
|
|
@@ -1035,10 +1022,9 @@ export function createViewManager(ctx) {
|
|
|
1035
1022
|
}
|
|
1036
1023
|
}
|
|
1037
1024
|
function repositionAll() {
|
|
1038
|
-
//
|
|
1039
|
-
//
|
|
1040
|
-
|
|
1041
|
-
applySimulatorBounds(lastSimWidth);
|
|
1025
|
+
// Simulator + DevTools overlay: bounds owned by the renderer's anchor
|
|
1026
|
+
// publishes; their ResizeObserver/window-resize listeners re-measure, so
|
|
1027
|
+
// no static re-apply here.
|
|
1042
1028
|
if (settingsView && settingsViewAdded)
|
|
1043
1029
|
applySettingsBounds();
|
|
1044
1030
|
if (popoverView)
|
|
@@ -1046,11 +1032,23 @@ export function createViewManager(ctx) {
|
|
|
1046
1032
|
}
|
|
1047
1033
|
function disposeAll() {
|
|
1048
1034
|
detachSimulator();
|
|
1035
|
+
// Narrow channel first: close the live MessagePort + sweep the onMessage
|
|
1036
|
+
// registry, so a send() racing teardown reports false instead of posting
|
|
1037
|
+
// into a wc that is about to be closed.
|
|
1038
|
+
hostToolbarPort.dispose();
|
|
1049
1039
|
// Host-controllable toolbar view: removed from the contentView + its
|
|
1050
1040
|
// WebContents closed (the host's loaded content is torn down on app exit).
|
|
1051
1041
|
destroyViewInternal(hostToolbarView);
|
|
1052
1042
|
hostToolbarView = null;
|
|
1053
1043
|
hostToolbarViewAdded = false;
|
|
1044
|
+
// Release this manager's reference on the shared defaultSession
|
|
1045
|
+
// toolbar-runtime registration (only if it ever acquired one — a manager
|
|
1046
|
+
// that never used the toolbar must not drive the shared count to zero).
|
|
1047
|
+
// The LAST release unregisters; other coexisting managers keep theirs.
|
|
1048
|
+
if (hostToolbarRuntimeAcquired) {
|
|
1049
|
+
releaseHostToolbarSessionRuntime();
|
|
1050
|
+
hostToolbarRuntimeAcquired = false;
|
|
1051
|
+
}
|
|
1054
1052
|
safeArea.dispose();
|
|
1055
1053
|
}
|
|
1056
1054
|
function setNativeSimulatorViewBounds(params) {
|
|
@@ -1102,34 +1100,17 @@ export function createViewManager(ctx) {
|
|
|
1102
1100
|
}
|
|
1103
1101
|
catch { /* hostWebContents unavailable; guests get zoom on attach */ }
|
|
1104
1102
|
}
|
|
1105
|
-
function resize(
|
|
1106
|
-
|
|
1107
|
-
//
|
|
1108
|
-
//
|
|
1109
|
-
// same resize. No coarse panel-size re-apply here (it raced the precise rect).
|
|
1110
|
-
if (simulatorViewAdded)
|
|
1111
|
-
applySimulatorBounds(simWidth);
|
|
1103
|
+
function resize(_simWidth) {
|
|
1104
|
+
// Simulator + DevTools overlay bounds are owned solely by the renderer's
|
|
1105
|
+
// anchor publishes — its ResizeObserver/window-resize listeners re-measure
|
|
1106
|
+
// on this same resize. No static re-apply (it raced the precise rect).
|
|
1112
1107
|
if (settingsViewAdded)
|
|
1113
1108
|
applySettingsBounds();
|
|
1114
1109
|
}
|
|
1115
|
-
function setVisible(visible, simWidth) {
|
|
1116
|
-
lastSimWidth = simWidth;
|
|
1117
|
-
if (!simulatorView)
|
|
1118
|
-
return;
|
|
1119
|
-
if (visible && !simulatorViewAdded) {
|
|
1120
|
-
showSimulator(simWidth);
|
|
1121
|
-
}
|
|
1122
|
-
else if (!visible) {
|
|
1123
|
-
hideSimulator();
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
1110
|
return {
|
|
1127
|
-
attachSimulator,
|
|
1128
1111
|
attachNativeSimulator,
|
|
1129
1112
|
detachSimulator,
|
|
1130
1113
|
reapplySafeArea: (device) => safeArea.reapplyAll(device),
|
|
1131
|
-
showSimulator,
|
|
1132
|
-
hideSimulator,
|
|
1133
1114
|
showSettings,
|
|
1134
1115
|
hideSettings,
|
|
1135
1116
|
showPopover,
|
|
@@ -1143,9 +1124,6 @@ export function createViewManager(ctx) {
|
|
|
1143
1124
|
const wc = webContents.fromId(simulatorWebContentsId);
|
|
1144
1125
|
return wc && !wc.isDestroyed() ? wc : null;
|
|
1145
1126
|
},
|
|
1146
|
-
getLastSimWidth: () => lastSimWidth,
|
|
1147
|
-
isSimulatorAdded: () => simulatorViewAdded,
|
|
1148
|
-
hasSimulatorView: () => simulatorView !== null,
|
|
1149
1127
|
getSettingsWebContents: () => {
|
|
1150
1128
|
if (!settingsView)
|
|
1151
1129
|
return null;
|
|
@@ -1167,16 +1145,9 @@ export function createViewManager(ctx) {
|
|
|
1167
1145
|
return null;
|
|
1168
1146
|
return popoverView.webContents.id;
|
|
1169
1147
|
},
|
|
1170
|
-
getHostToolbarWebContentsId: () =>
|
|
1171
|
-
if (!hostToolbarView)
|
|
1172
|
-
return null;
|
|
1173
|
-
if (hostToolbarView.webContents.isDestroyed())
|
|
1174
|
-
return null;
|
|
1175
|
-
return hostToolbarView.webContents.id;
|
|
1176
|
-
},
|
|
1148
|
+
getHostToolbarWebContentsId: () => liveHostToolbarWebContents()?.id ?? null,
|
|
1177
1149
|
setNativeSimulatorViewBounds,
|
|
1178
1150
|
resize,
|
|
1179
|
-
setVisible,
|
|
1180
1151
|
setSimulatorDevtoolsBounds,
|
|
1181
1152
|
setHostToolbarBounds,
|
|
1182
1153
|
setHostToolbarHeight,
|
|
@@ -12,10 +12,9 @@ import { type ViewManager } from './views/view-manager.js';
|
|
|
12
12
|
import { type WindowService } from './window-service.js';
|
|
13
13
|
import { type WorkspaceService } from './workspace/workspace-service.js';
|
|
14
14
|
import { type SimulatorApiRegistry } from './simulator/custom-apis.js';
|
|
15
|
-
import { type ToolbarStore } from './toolbar/toolbar-store.js';
|
|
16
15
|
import { sanitizeTemplates } from './projects/templates.js';
|
|
17
16
|
import type { ProjectsProvider, ProjectTemplate } from './projects/types.js';
|
|
18
|
-
import type {
|
|
17
|
+
import type { CustomCreateProjectDialogResult } from '../../shared/types.js';
|
|
19
18
|
/**
|
|
20
19
|
* Shared mutable state for the workbench application.
|
|
21
20
|
* Passed to each IPC module so they can read/write shared state without closures.
|
|
@@ -26,14 +25,10 @@ export interface WorkbenchContext {
|
|
|
26
25
|
preloadPath: string;
|
|
27
26
|
/** Absolute path to the renderer dist directory */
|
|
28
27
|
rendererDir: string;
|
|
29
|
-
/** Built-in panel IDs to display (default: all) */
|
|
30
|
-
panels: string[];
|
|
31
28
|
/** Custom API namespace names (e.g. ['qd']) passed to the simulator */
|
|
32
29
|
apiNamespaces: string[];
|
|
33
30
|
/** Branding name shown in title bar and getBranding IPC */
|
|
34
31
|
appName: string;
|
|
35
|
-
/** Header bar height in px, used for view layout and exposed to the renderer */
|
|
36
|
-
headerHeight: number;
|
|
37
32
|
/** Host-injected provider for branding info (overrides default appName) */
|
|
38
33
|
brandingProvider?: () => Promise<{
|
|
39
34
|
appName: string;
|
|
@@ -46,6 +41,13 @@ export interface WorkbenchContext {
|
|
|
46
41
|
windows: WindowService;
|
|
47
42
|
/** Unified main → renderer event dispatcher */
|
|
48
43
|
notify: RendererNotifier;
|
|
44
|
+
/**
|
|
45
|
+
* Open (or re-focus) the standalone workbench-settings window. First-class
|
|
46
|
+
* member so contract holders (`MiniappRuntime` / `MenuContext`) can open
|
|
47
|
+
* settings without reaching into `windows`/`notify` plumbing. Wired by
|
|
48
|
+
* `createWorkbenchContext` to the real `openSettingsWindow` path.
|
|
49
|
+
*/
|
|
50
|
+
openSettings: () => Promise<void>;
|
|
49
51
|
/** Single source of truth for project + session + per-project settings */
|
|
50
52
|
workspace: WorkspaceService;
|
|
51
53
|
/**
|
|
@@ -72,11 +74,6 @@ export interface WorkbenchContext {
|
|
|
72
74
|
parentWindow: BrowserWindow;
|
|
73
75
|
templates: ProjectTemplate[];
|
|
74
76
|
}) => Promise<CustomCreateProjectDialogResult>;
|
|
75
|
-
/**
|
|
76
|
-
* Optional host gate for opening projects. Runs after basic directory
|
|
77
|
-
* validation and before the current project session is closed.
|
|
78
|
-
*/
|
|
79
|
-
beforeOpenProject?: (projectPath: string) => BeforeOpenProjectResult | Promise<BeforeOpenProjectResult>;
|
|
80
77
|
/**
|
|
81
78
|
* Trust predicate consulted by `IpcRegistry` for every incoming IPC.
|
|
82
79
|
* Resolves the currently-trusted senders (main renderer + overlays)
|
|
@@ -99,12 +96,6 @@ export interface WorkbenchContext {
|
|
|
99
96
|
* One registry per context — no process-global crosstalk.
|
|
100
97
|
*/
|
|
101
98
|
simulatorApis: SimulatorApiRegistry;
|
|
102
|
-
/**
|
|
103
|
-
* Per-context store of host-registered toolbar actions. Populated via
|
|
104
|
-
* `instance.toolbar.set`; read by the toolbar IPC handlers. One store per
|
|
105
|
-
* context — no process-global crosstalk.
|
|
106
|
-
*/
|
|
107
|
-
toolbar: ToolbarStore;
|
|
108
99
|
/** Aggregates dispose handlers for every IPC handler, listener, watcher, and CDP session registered by the workbench. */
|
|
109
100
|
registry: DisposableRegistry;
|
|
110
101
|
/**
|
|
@@ -162,29 +153,27 @@ export interface WorkbenchContext {
|
|
|
162
153
|
* run) and injects its raw Network.* CDP events into the DevTools FRONT-END wc
|
|
163
154
|
* (`window.DevToolsAPI.dispatchMessage`) so the native Network tab renders them;
|
|
164
155
|
* falls back to a `[网络]` service-host console line when the front-end is
|
|
165
|
-
* unavailable. The ViewManager calls `attachSimulator` +
|
|
166
|
-
* `attachNativeSimulator` once the simulator WCV +
|
|
167
|
-
* Undefined until bootstrap wires it.
|
|
156
|
+
* unavailable. The ViewManager calls the forwarder's own `attachSimulator` +
|
|
157
|
+
* `setDevtoolsHost` from `attachNativeSimulator` once the simulator WCV +
|
|
158
|
+
* DevTools host exist. Undefined until bootstrap wires it.
|
|
168
159
|
*/
|
|
169
160
|
networkForward?: NetworkForwarder;
|
|
170
161
|
}
|
|
171
162
|
/**
|
|
172
163
|
* Inputs for `createWorkbenchContext`. The scalar config fields it shares with
|
|
173
|
-
* `WorkbenchConfig` (`adapter` / `apiNamespaces` / `appName`
|
|
164
|
+
* `WorkbenchConfig` (`adapter` / `apiNamespaces` / `appName`)
|
|
174
165
|
* are derived from there via `Pick` so the two stay in lockstep — no
|
|
175
166
|
* field-by-field re-declaration. The remaining fields are kept explicit
|
|
176
167
|
* because their shapes intentionally differ from the config:
|
|
177
168
|
* - `preloadPath` / `rendererDir` are REQUIRED here (the caller resolves the
|
|
178
169
|
* defaults before constructing the context) but optional in the config;
|
|
179
|
-
* - `panels` is the structural `string[]` (the context stores raw ids);
|
|
180
170
|
* - `projectsProvider` / `projectTemplates` / `customCreateProjectDialog` use
|
|
181
171
|
* the main-process types, not the structural mirrors in `shared/types`.
|
|
182
172
|
*/
|
|
183
|
-
export interface CreateContextOptions extends Pick<WorkbenchConfig, 'adapter' | 'apiNamespaces' | 'appName'
|
|
173
|
+
export interface CreateContextOptions extends Pick<WorkbenchConfig, 'adapter' | 'apiNamespaces' | 'appName'> {
|
|
184
174
|
mainWindow: BrowserWindow;
|
|
185
175
|
preloadPath: string;
|
|
186
176
|
rendererDir: string;
|
|
187
|
-
panels?: string[];
|
|
188
177
|
brandingProvider?: WorkbenchContext['brandingProvider'];
|
|
189
178
|
/** Host-supplied project list backend. Defaults to LocalProjectsProvider. */
|
|
190
179
|
projectsProvider?: ProjectsProvider;
|
|
@@ -195,8 +184,6 @@ export interface CreateContextOptions extends Pick<WorkbenchConfig, 'adapter' |
|
|
|
195
184
|
/** Host-supplied "新建项目" dialog hook. */
|
|
196
185
|
customCreateProjectDialog?: WorkbenchContext['customCreateProjectDialog'];
|
|
197
186
|
}
|
|
198
|
-
export declare function hasBuiltinPanel(ctx: Pick<WorkbenchContext, 'panels'>, panelId: string): boolean;
|
|
199
|
-
export declare function getDefaultTab(ctx: Pick<WorkbenchContext, 'panels'>): string;
|
|
200
187
|
export declare function createWorkbenchContext(opts: CreateContextOptions): WorkbenchContext;
|
|
201
188
|
export { sanitizeTemplates };
|
|
202
189
|
//# sourceMappingURL=workbench-context.d.ts.map
|