@dimina-kit/devtools 0.4.0-dev.20260701155140 → 0.4.0-dev.20260702145840
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main/app/max-listeners-diagnostic.js +5 -1
- package/dist/main/index.bundle.js +1532 -1236
- package/dist/main/ipc/bridge-router.d.ts +28 -0
- package/dist/main/ipc/bridge-router.js +175 -50
- package/dist/main/ipc/session-listener-bag.d.ts +41 -0
- package/dist/main/ipc/session-listener-bag.js +40 -0
- package/dist/main/ipc/simulator.js +5 -1
- package/dist/main/services/elements-forward/index.js +19 -0
- package/dist/main/services/network-forward/index.js +13 -0
- package/dist/main/services/views/destroy-child-view.d.ts +9 -0
- package/dist/main/services/views/destroy-child-view.js +23 -0
- package/dist/main/services/views/host-toolbar-view.d.ts +21 -0
- package/dist/main/services/views/host-toolbar-view.js +249 -0
- package/dist/main/services/views/inject-when-ready.d.ts +41 -0
- package/dist/main/services/views/inject-when-ready.js +62 -0
- package/dist/main/services/views/native-simulator-devtools-host.d.ts +26 -0
- package/dist/main/services/views/native-simulator-devtools-host.js +484 -0
- package/dist/main/services/views/native-simulator-view.d.ts +28 -0
- package/dist/main/services/views/native-simulator-view.js +428 -0
- package/dist/main/services/views/overlay-panels-view.d.ts +29 -0
- package/dist/main/services/views/overlay-panels-view.js +150 -0
- package/dist/main/services/views/placement-reconciler.d.ts +73 -0
- package/dist/main/services/views/placement-reconciler.js +120 -0
- package/dist/main/services/views/resolve-project-editor-target.d.ts +13 -0
- package/dist/main/services/views/resolve-project-editor-target.js +33 -0
- package/dist/main/services/views/view-manager.d.ts +20 -16
- package/dist/main/services/views/view-manager.js +66 -1545
- package/dist/main/services/views/workbench-view.d.ts +15 -0
- package/dist/main/services/views/workbench-view.js +170 -0
- package/dist/preload/windows/host-toolbar-runtime.cjs.map +2 -2
- package/dist/preload/windows/main.cjs +10 -1
- package/dist/preload/windows/main.cjs.map +2 -2
- package/dist/preload/windows/simulator.cjs +6 -0
- package/dist/preload/windows/simulator.cjs.map +2 -2
- package/dist/preload/windows/simulator.js +6 -0
- package/dist/renderer/assets/index-ChF9V4r-.js +49 -0
- package/dist/renderer/assets/{input-Djg2NR4U.js → input-Cjk0wSEh.js} +2 -2
- package/dist/renderer/assets/ipc-transport-D5dKIti1.css +1 -0
- package/dist/renderer/assets/{ipc-transport-Be-AzCub.js → ipc-transport-OZS-KmOW.js} +2 -2
- package/dist/renderer/assets/{popover-eDUh4NYe.js → popover-CQVvOe90.js} +2 -2
- package/dist/renderer/assets/{select-DesFfkVV.js → select-BQDXi5ih.js} +2 -2
- package/dist/renderer/assets/{settings-api-HQ7ZFPWi.js → settings-api-B6x2mhaD.js} +2 -2
- package/dist/renderer/assets/{settings-CRmO7zUG.js → settings-tP3Px2KR.js} +2 -2
- package/dist/renderer/assets/{workbenchSettings-CZBWsliT.js → workbenchSettings-Bim9ol9R.js} +2 -2
- package/dist/renderer/entries/main/index.html +6 -6
- package/dist/renderer/entries/popover/index.html +5 -5
- package/dist/renderer/entries/settings/index.html +5 -5
- package/dist/renderer/entries/workbench-settings/index.html +4 -4
- package/dist/shared/bridge-channels.d.ts +17 -0
- package/dist/shared/bridge-channels.js +9 -0
- package/dist/shared/ipc-channels.d.ts +1 -0
- package/dist/shared/ipc-channels.js +6 -0
- package/dist/shared/ipc-schemas.d.ts +6 -0
- package/dist/shared/ipc-schemas.js +10 -0
- package/dist/simulator/assets/{bridge-channels-BUQ5AbvJ.js → bridge-channels-zhsumfky.js} +2 -2
- package/dist/simulator/assets/{device-shell-CT3LRnTB.js → device-shell-CUl0ILfn.js} +2 -2
- package/dist/simulator/assets/{simulator-BizhGGE3.js → simulator-Dz8iGcgy.js} +5 -5
- package/dist/simulator/assets/{simulator-mini-app-BfNuDF3R.js → simulator-mini-app-Bt639XL_.js} +2 -2
- package/dist/simulator/simulator.html +2 -1
- package/package.json +5 -5
- package/dist/renderer/assets/index-jDrNHghH.js +0 -49
- package/dist/renderer/assets/ipc-transport-C6-KPMqw.css +0 -1
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { WebContentsView } from 'electron';
|
|
2
|
+
import * as layout from '../layout/index.js';
|
|
3
|
+
import type { DesiredView, PlacementSnapshot } from '@dimina-kit/electron-deck/layout';
|
|
4
|
+
import type { DevtoolsExtra } from '../../../shared/view-ids.js';
|
|
5
|
+
import type { ViewManagerContext } from './view-manager.js';
|
|
6
|
+
/**
|
|
7
|
+
* The per-view wiring the reconciler needs to converge the main-process view
|
|
8
|
+
* tree. Each view domain registers exactly one slot; the reconciler owns no
|
|
9
|
+
* view references itself, so the domain's own `let viewRef` stays the single
|
|
10
|
+
* source of truth (the slot exposes accessors into it, never a copy).
|
|
11
|
+
*/
|
|
12
|
+
export interface ViewSlot {
|
|
13
|
+
/** The live WebContentsView for this slot, or null when not yet created. */
|
|
14
|
+
getView(): WebContentsView | null;
|
|
15
|
+
/** Record whether the view is currently added to the contentView. */
|
|
16
|
+
setAdded?(added: boolean): void;
|
|
17
|
+
/**
|
|
18
|
+
* When true, force the desired placement to hidden — the view-creation site
|
|
19
|
+
* has not produced the WCV yet, so the reconciler must never record an attach
|
|
20
|
+
* that addChildView can't perform. The creation site calls `reconcileNow()`
|
|
21
|
+
* to re-open the gate.
|
|
22
|
+
*/
|
|
23
|
+
gateHidden?(): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Run before the generic attach. Return true when the slot fully handled the
|
|
26
|
+
* attach itself (e.g. a lazy async load that adds the view on completion), so
|
|
27
|
+
* the reconciler must NOT addChildView here.
|
|
28
|
+
*/
|
|
29
|
+
beforeAttach?(): boolean;
|
|
30
|
+
/** Lazily create the view on attach (host-toolbar); used instead of getView. */
|
|
31
|
+
ensureView?(): WebContentsView | null;
|
|
32
|
+
/** Domain-specific setBounds (simulator zoom rides here). */
|
|
33
|
+
applyBounds?(view: WebContentsView, bounds: layout.Bounds, extra: DevtoolsExtra | undefined): void;
|
|
34
|
+
/**
|
|
35
|
+
* Create the lazily-built view BEFORE reconcile when its desired placement is
|
|
36
|
+
* visible, so the setBounds op — which the core emits before attach — lands
|
|
37
|
+
* on a live view rather than a not-yet-created one.
|
|
38
|
+
*/
|
|
39
|
+
ensureLazy?(desired: DesiredView<DevtoolsExtra> | undefined): void;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Level-triggered placement reconciler (docs/view-placement-reconciler.md). The
|
|
43
|
+
* renderer publishes a window-level snapshot into `baseDesired`;
|
|
44
|
+
* settings/popover are main-owned and live in `overlayDesired`. Any change
|
|
45
|
+
* merges the two, runs the pure reconcile core, and applies the ordered ops
|
|
46
|
+
* through `viewTarget`. `epochCounter` is a single monotonic tick — main is the
|
|
47
|
+
* only (serial) reconcile caller, so the core's stale guard passes by
|
|
48
|
+
* construction; `rendererGeneration` still drives the reset on renderer restart.
|
|
49
|
+
*/
|
|
50
|
+
export interface PlacementReconciler {
|
|
51
|
+
registerView(viewId: string, slot: ViewSlot): void;
|
|
52
|
+
reconcileNow(): void;
|
|
53
|
+
/**
|
|
54
|
+
* Apply the renderer's window-level placement snapshot — the single source of
|
|
55
|
+
* truth for every managed native view's bounds/visibility/z-order.
|
|
56
|
+
*/
|
|
57
|
+
setPlacementSnapshot(snapshot: PlacementSnapshot<DevtoolsExtra>): void;
|
|
58
|
+
/**
|
|
59
|
+
* Forget a view's reconciled mount state so the NEXT rebuilt view is treated
|
|
60
|
+
* as a fresh attach. Required wherever a view instance is replaced/destroyed
|
|
61
|
+
* via a manual removeChildView that bypasses the reconciler (otherwise the
|
|
62
|
+
* level-triggered core still records the old instance as attached and never
|
|
63
|
+
* emits the attach op for the replacement — a sticky invisible view).
|
|
64
|
+
*/
|
|
65
|
+
forgetActual(viewId: string): void;
|
|
66
|
+
setBaseDesired(viewId: string, desired: DesiredView<DevtoolsExtra>): void;
|
|
67
|
+
deleteBaseDesired(viewId: string): void;
|
|
68
|
+
setOverlayDesired(viewId: string, desired: DesiredView<DevtoolsExtra>): void;
|
|
69
|
+
deleteOverlayDesired(viewId: string): void;
|
|
70
|
+
hasOverlayDesired(viewId: string): boolean;
|
|
71
|
+
}
|
|
72
|
+
export declare function createPlacementReconciler(ctx: ViewManagerContext): PlacementReconciler;
|
|
73
|
+
//# sourceMappingURL=placement-reconciler.d.ts.map
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { reconcile, createInitialState } from '@dimina-kit/electron-deck/layout';
|
|
2
|
+
import { applyViewOps } from './apply-view-ops.js';
|
|
3
|
+
export function createPlacementReconciler(ctx) {
|
|
4
|
+
let placementState = createInitialState();
|
|
5
|
+
let epochCounter = 0;
|
|
6
|
+
let rendererGeneration = 0;
|
|
7
|
+
const baseDesired = new Map();
|
|
8
|
+
const overlayDesired = new Map();
|
|
9
|
+
const slots = new Map();
|
|
10
|
+
function gateReadiness(v) {
|
|
11
|
+
const slot = slots.get(v.viewId);
|
|
12
|
+
if (slot?.gateHidden?.())
|
|
13
|
+
return { ...v, placement: { visible: false } };
|
|
14
|
+
return v;
|
|
15
|
+
}
|
|
16
|
+
const viewTarget = {
|
|
17
|
+
attach(viewId) {
|
|
18
|
+
if (ctx.windows.mainWindow.isDestroyed())
|
|
19
|
+
return;
|
|
20
|
+
const slot = slots.get(viewId);
|
|
21
|
+
if (!slot)
|
|
22
|
+
return;
|
|
23
|
+
if (slot.beforeAttach?.())
|
|
24
|
+
return;
|
|
25
|
+
const view = slot.ensureView ? slot.ensureView() : slot.getView();
|
|
26
|
+
if (!view)
|
|
27
|
+
return;
|
|
28
|
+
ctx.windows.mainWindow.contentView.addChildView(view);
|
|
29
|
+
slot.setAdded?.(true);
|
|
30
|
+
},
|
|
31
|
+
detach(viewId) {
|
|
32
|
+
const slot = slots.get(viewId);
|
|
33
|
+
const view = slot?.getView() ?? null;
|
|
34
|
+
if (view && !ctx.windows.mainWindow.isDestroyed()) {
|
|
35
|
+
try {
|
|
36
|
+
ctx.windows.mainWindow.contentView.removeChildView(view);
|
|
37
|
+
}
|
|
38
|
+
catch { /* already removed */ }
|
|
39
|
+
}
|
|
40
|
+
slot?.setAdded?.(false);
|
|
41
|
+
},
|
|
42
|
+
setBounds(viewId, bounds, extra) {
|
|
43
|
+
const slot = slots.get(viewId);
|
|
44
|
+
const view = slot?.getView() ?? null;
|
|
45
|
+
if (!view || view.webContents.isDestroyed())
|
|
46
|
+
return;
|
|
47
|
+
if (slot?.applyBounds) {
|
|
48
|
+
slot.applyBounds(view, bounds, extra);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
view.setBounds(bounds);
|
|
52
|
+
},
|
|
53
|
+
setVisible(viewId, visible) {
|
|
54
|
+
const view = slots.get(viewId)?.getView() ?? null;
|
|
55
|
+
if (!view)
|
|
56
|
+
return;
|
|
57
|
+
try {
|
|
58
|
+
view.setVisible(visible);
|
|
59
|
+
}
|
|
60
|
+
catch { /* stub may lack setVisible */ }
|
|
61
|
+
},
|
|
62
|
+
reorder(order) {
|
|
63
|
+
// A single attached view is already in place — nothing to reorder.
|
|
64
|
+
if (order.length <= 1 || ctx.windows.mainWindow.isDestroyed())
|
|
65
|
+
return;
|
|
66
|
+
const cv = ctx.windows.mainWindow.contentView;
|
|
67
|
+
for (const viewId of order) {
|
|
68
|
+
const view = slots.get(viewId)?.getView() ?? null;
|
|
69
|
+
if (view) {
|
|
70
|
+
try {
|
|
71
|
+
cv.addChildView(view);
|
|
72
|
+
}
|
|
73
|
+
catch { /* already attached */ }
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
// Create the lazily-built views (host-toolbar, workbench) BEFORE reconcile so
|
|
79
|
+
// the setBounds op — which the core emits before attach — lands on a live view
|
|
80
|
+
// rather than a not-yet-created one.
|
|
81
|
+
function ensureLazyViews() {
|
|
82
|
+
for (const [viewId, slot] of slots) {
|
|
83
|
+
slot.ensureLazy?.(baseDesired.get(viewId));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function reconcileNow() {
|
|
87
|
+
ensureLazyViews();
|
|
88
|
+
const views = [];
|
|
89
|
+
for (const v of baseDesired.values())
|
|
90
|
+
views.push(gateReadiness(v));
|
|
91
|
+
for (const v of overlayDesired.values())
|
|
92
|
+
views.push(v);
|
|
93
|
+
const result = reconcile(placementState, {
|
|
94
|
+
generation: rendererGeneration,
|
|
95
|
+
epoch: ++epochCounter,
|
|
96
|
+
views,
|
|
97
|
+
});
|
|
98
|
+
placementState = result.state;
|
|
99
|
+
applyViewOps(result.ops, viewTarget);
|
|
100
|
+
}
|
|
101
|
+
function setPlacementSnapshot(snapshot) {
|
|
102
|
+
rendererGeneration = snapshot.generation;
|
|
103
|
+
baseDesired.clear();
|
|
104
|
+
for (const v of snapshot.views)
|
|
105
|
+
baseDesired.set(v.viewId, v);
|
|
106
|
+
reconcileNow();
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
registerView: (viewId, slot) => { slots.set(viewId, slot); },
|
|
110
|
+
reconcileNow,
|
|
111
|
+
setPlacementSnapshot,
|
|
112
|
+
forgetActual: (viewId) => { placementState.actual.delete(viewId); },
|
|
113
|
+
setBaseDesired: (viewId, desired) => { baseDesired.set(viewId, desired); },
|
|
114
|
+
deleteBaseDesired: (viewId) => { baseDesired.delete(viewId); },
|
|
115
|
+
setOverlayDesired: (viewId, desired) => { overlayDesired.set(viewId, desired); },
|
|
116
|
+
deleteOverlayDesired: (viewId) => { overlayDesired.delete(viewId); },
|
|
117
|
+
hasOverlayDesired: (viewId) => overlayDesired.has(viewId),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=placement-reconciler.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type OpenInEditorRequest } from '../../../shared/open-in-editor.js';
|
|
2
|
+
export interface ProjectEditorTarget {
|
|
3
|
+
path: string;
|
|
4
|
+
line?: number;
|
|
5
|
+
column?: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Resolve a DevTools source request against the service-host URL that created
|
|
9
|
+
* the inspected app. Its pkgRoot is authoritative; the workspace is only a
|
|
10
|
+
* stale-session consistency guard.
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveProjectEditorTarget(serviceHostUrl: string, activeProjectRoot: string | undefined, req: OpenInEditorRequest, isFile?: (absolutePath: string) => boolean): ProjectEditorTarget | null;
|
|
13
|
+
//# sourceMappingURL=resolve-project-editor-target.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { projectSourceContextFromServiceHostUrl, resourceUrlToProjectRelativePath, } from '../../../shared/open-in-editor.js';
|
|
4
|
+
/**
|
|
5
|
+
* Resolve a DevTools source request against the service-host URL that created
|
|
6
|
+
* the inspected app. Its pkgRoot is authoritative; the workspace is only a
|
|
7
|
+
* stale-session consistency guard.
|
|
8
|
+
*/
|
|
9
|
+
export function resolveProjectEditorTarget(serviceHostUrl, activeProjectRoot, req, isFile = (absolutePath) => fs.statSync(absolutePath).isFile()) {
|
|
10
|
+
const sourceContext = projectSourceContextFromServiceHostUrl(serviceHostUrl, activeProjectRoot);
|
|
11
|
+
if (!sourceContext)
|
|
12
|
+
return null;
|
|
13
|
+
const rel = resourceUrlToProjectRelativePath(req.url, sourceContext);
|
|
14
|
+
if (!rel)
|
|
15
|
+
return null;
|
|
16
|
+
const absolute = path.resolve(sourceContext.projectRoot, ...rel.split('/'));
|
|
17
|
+
const fromRoot = path.relative(path.resolve(sourceContext.projectRoot), absolute);
|
|
18
|
+
if (!fromRoot || fromRoot.startsWith('..') || path.isAbsolute(fromRoot))
|
|
19
|
+
return null;
|
|
20
|
+
try {
|
|
21
|
+
if (!isFile(absolute))
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
path: rel,
|
|
29
|
+
line: typeof req.line === 'number' ? req.line + 1 : undefined,
|
|
30
|
+
column: typeof req.column === 'number' ? req.column + 1 : undefined,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=resolve-project-editor-target.js.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { WebContents } from 'electron';
|
|
2
2
|
import type { NativeDeviceInfo } from '../../../shared/ipc-channels.js';
|
|
3
|
-
import { type OpenInEditorRequest } from '../../../shared/open-in-editor.js';
|
|
4
3
|
import { type WorkbenchContext } from '../workbench-context.js';
|
|
5
|
-
import {
|
|
4
|
+
import type { HostToolbarMessageSubscription } from './host-toolbar-port-channel.js';
|
|
6
5
|
import type { PlacementSnapshot } from '@dimina-kit/electron-deck/layout';
|
|
7
|
-
import {
|
|
6
|
+
import type { DevtoolsExtra } from '../../../shared/view-ids.js';
|
|
7
|
+
export { resolveProjectEditorTarget, type ProjectEditorTarget, } from './resolve-project-editor-target.js';
|
|
8
8
|
/**
|
|
9
9
|
* Context surface used by the ViewManager. We only need a small slice of the
|
|
10
10
|
* full WorkbenchContext here; typing it this way documents the actual dependency.
|
|
@@ -69,6 +69,11 @@ export interface ViewManagerContext {
|
|
|
69
69
|
* main-process WebContentsView (`attachWorkbench`) hung off the main window's
|
|
70
70
|
* contentView, like the other overlays. The main window's own renderer is the
|
|
71
71
|
* content root and is not managed here.
|
|
72
|
+
*
|
|
73
|
+
* This module is the composition root: each view domain lives in its own
|
|
74
|
+
* factory module (simulator, devtools-host, workbench, host-toolbar, overlay
|
|
75
|
+
* panels) and shares a single injected placement reconciler; this file wires
|
|
76
|
+
* them together and exposes their combined method surface.
|
|
72
77
|
*/
|
|
73
78
|
export interface ViewManager {
|
|
74
79
|
/**
|
|
@@ -89,6 +94,16 @@ export interface ViewManager {
|
|
|
89
94
|
* geometry is anchor-published.
|
|
90
95
|
*/
|
|
91
96
|
attachNativeSimulator(simulatorUrl: string, simWidth: number): Promise<void>;
|
|
97
|
+
/**
|
|
98
|
+
* Soft-reload the LIVE native simulator after a watcher rebuild: forward a
|
|
99
|
+
* SIMULATOR_EVENTS.RELAUNCH (carrying the rebuilt simulator URL) into the
|
|
100
|
+
* existing WCV so the shell boots a new app session in place and swaps when
|
|
101
|
+
* it is ready — the phone shell never unmounts. Returns false (no event
|
|
102
|
+
* sent) when there is no live simulator view or its shell has not finished
|
|
103
|
+
* its first boot (first render guest did-finish-load); the caller then falls
|
|
104
|
+
* back to the hard attachNativeSimulator rebuild.
|
|
105
|
+
*/
|
|
106
|
+
softReloadNativeSimulator(simulatorUrl: string): boolean;
|
|
92
107
|
/**
|
|
93
108
|
* Destroy and null out the simulator view (e.g. on simulator detach).
|
|
94
109
|
* Also destroys the cached settings view and hides the popover —
|
|
@@ -273,23 +288,12 @@ export interface HostToolbarControl {
|
|
|
273
288
|
*/
|
|
274
289
|
setHeightMode(mode: HostToolbarHeightMode): void;
|
|
275
290
|
}
|
|
276
|
-
export interface ProjectEditorTarget {
|
|
277
|
-
path: string;
|
|
278
|
-
line?: number;
|
|
279
|
-
column?: number;
|
|
280
|
-
}
|
|
281
|
-
/**
|
|
282
|
-
* Resolve a DevTools source request against the service-host URL that created
|
|
283
|
-
* the inspected app. Its pkgRoot is authoritative; the workspace is only a
|
|
284
|
-
* stale-session consistency guard.
|
|
285
|
-
*/
|
|
286
|
-
export declare function resolveProjectEditorTarget(serviceHostUrl: string, activeProjectRoot: string | undefined, req: OpenInEditorRequest, isFile?: (absolutePath: string) => boolean): ProjectEditorTarget | null;
|
|
287
291
|
/**
|
|
288
292
|
* Build a ViewManager bound to the given context. The returned object is the
|
|
289
293
|
* only component allowed to instantiate or add/remove overlay WebContentsViews.
|
|
290
294
|
*
|
|
291
|
-
* All view-related mutable state lives inside
|
|
292
|
-
* on the context object.
|
|
295
|
+
* All view-related mutable state lives inside the per-domain factory closures
|
|
296
|
+
* (and the single placement reconciler) below, never on the context object.
|
|
293
297
|
*/
|
|
294
298
|
export declare function createViewManager(ctx: ViewManagerContext): ViewManager;
|
|
295
299
|
//# sourceMappingURL=view-manager.d.ts.map
|