@dimina-kit/devtools 0.4.0-dev.20260701083540 → 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/app.js +11 -0
- package/dist/main/app/max-listeners-diagnostic.d.ts +32 -0
- package/dist/main/app/max-listeners-diagnostic.js +63 -0
- package/dist/main/index.bundle.js +1657 -1158
- package/dist/main/ipc/bridge-router.d.ts +33 -4
- package/dist/main/ipc/bridge-router.js +200 -51
- 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 -5
- package/dist/main/ipc/views.js +8 -19
- package/dist/main/services/automation/wait-active-page.d.ts +4 -2
- package/dist/main/services/elements-forward/index.js +19 -0
- package/dist/main/services/network-forward/index.js +13 -0
- package/dist/main/services/render-inspect/index.d.ts +9 -0
- package/dist/main/services/render-inspect/index.js +13 -1
- package/dist/main/services/simulator-storage/index.d.ts +9 -0
- package/dist/main/services/simulator-storage/index.js +26 -1
- package/dist/main/services/simulator-wxml/index.js +117 -11
- package/dist/main/services/views/apply-view-ops.d.ts +11 -0
- package/dist/main/services/views/apply-view-ops.js +22 -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/placement-test-driver.d.ts +25 -0
- package/dist/main/services/views/placement-test-driver.js +48 -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 +28 -70
- package/dist/main/services/views/view-manager.js +66 -1514
- package/dist/main/services/views/workbench-view.d.ts +15 -0
- package/dist/main/services/views/workbench-view.js +170 -0
- package/dist/main/services/workbench-context.d.ts +10 -0
- package/dist/preload/windows/host-toolbar-runtime.cjs +6 -13
- 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 -3
- package/dist/preload/windows/simulator.cjs.map +2 -2
- package/dist/preload/windows/simulator.js +6 -3
- package/dist/render-host/render-inspect.js +35 -1
- package/dist/renderer/assets/index-ChF9V4r-.js +49 -0
- package/dist/renderer/assets/{input-DR7bfyQE.js → input-Cjk0wSEh.js} +2 -2
- package/dist/renderer/assets/ipc-transport-D5dKIti1.css +1 -0
- package/dist/renderer/assets/{ipc-transport-3Arg8jPC.js → ipc-transport-OZS-KmOW.js} +2 -2
- package/dist/renderer/assets/{popover-CPZX3MrT.js → popover-CQVvOe90.js} +2 -2
- package/dist/renderer/assets/{select-BxyPlVyJ.js → select-BQDXi5ih.js} +2 -2
- package/dist/renderer/assets/{settings-api-CtPPJF-F.js → settings-api-B6x2mhaD.js} +2 -2
- package/dist/renderer/assets/{settings-CG_OR03F.js → settings-tP3Px2KR.js} +2 -2
- package/dist/renderer/assets/{workbenchSettings-ClqCBGOa.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/service-host/sync-api-patch.js +33 -3
- package/dist/shared/bridge-channels.d.ts +17 -0
- package/dist/shared/bridge-channels.js +9 -0
- package/dist/shared/ipc-channels.d.ts +29 -14
- package/dist/shared/ipc-channels.js +16 -17
- package/dist/shared/ipc-schemas.d.ts +25 -17
- package/dist/shared/ipc-schemas.js +38 -22
- package/dist/shared/view-ids.d.ts +19 -0
- package/dist/shared/view-ids.js +25 -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 +4 -4
- package/dist/renderer/assets/index-HE9Xr9bQ.js +0 -49
- package/dist/renderer/assets/ipc-transport-Dt0jR7Ir.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,25 @@
|
|
|
1
|
+
import type { DesiredView } from '@dimina-kit/electron-deck/layout';
|
|
2
|
+
import { type DevtoolsExtra } from '../../../shared/view-ids.js';
|
|
3
|
+
interface Rect {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
}
|
|
9
|
+
interface SnapshotSink {
|
|
10
|
+
setPlacementSnapshot(snapshot: {
|
|
11
|
+
generation: number;
|
|
12
|
+
epoch: number;
|
|
13
|
+
views: DesiredView<DevtoolsExtra>[];
|
|
14
|
+
}): void;
|
|
15
|
+
}
|
|
16
|
+
export declare function hostToolbarBounds(mgr: SnapshotSink, rect: Rect): void;
|
|
17
|
+
export declare function simulatorDevtoolsBounds(mgr: SnapshotSink, rect: Rect): void;
|
|
18
|
+
export declare function workbenchBounds(mgr: SnapshotSink, rect: Rect): void;
|
|
19
|
+
export declare function simulatorBounds(mgr: SnapshotSink, params: Rect & {
|
|
20
|
+
zoom: number;
|
|
21
|
+
}): void;
|
|
22
|
+
/** Drop a view from this manager's desired table (reconcile detaches it). */
|
|
23
|
+
export declare function removeView(mgr: SnapshotSink, viewId: string): void;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=placement-test-driver.d.ts.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Test-only helper: drives the view manager through its real runtime entry
|
|
2
|
+
// point, `setPlacementSnapshot`, the way the renderer's central publisher does
|
|
3
|
+
// — accumulating each view's desired placement (per manager) and publishing the
|
|
4
|
+
// WHOLE table under a monotonic epoch. A single-view update thus keeps the other
|
|
5
|
+
// views' last-declared placement, matching the renderer's level-triggered stream
|
|
6
|
+
// rather than the old per-view setXxxBounds edges. These free functions let the
|
|
7
|
+
// many view-manager tests exercise reconcile behaviour with a drop-in rename
|
|
8
|
+
// (`mgr.setHostToolbarBounds(r)` → `hostToolbarBounds(mgr, r)`).
|
|
9
|
+
import { VIEW_ID, VIEW_LAYER } from '../../../shared/view-ids.js';
|
|
10
|
+
// Monotonic across every push in a test file (vitest isolates module state per
|
|
11
|
+
// file), so a fresh manager (lastEpoch -1) accepts the first push and repeated
|
|
12
|
+
// pushes on one manager never look stale.
|
|
13
|
+
let globalEpoch = 0;
|
|
14
|
+
const desiredByMgr = new WeakMap();
|
|
15
|
+
function toPlacement(r) {
|
|
16
|
+
return r.width > 0 && r.height > 0 ? { visible: true, bounds: r } : { visible: false };
|
|
17
|
+
}
|
|
18
|
+
function pushView(mgr, view, generation = 1) {
|
|
19
|
+
let desired = desiredByMgr.get(mgr);
|
|
20
|
+
if (!desired) {
|
|
21
|
+
desired = new Map();
|
|
22
|
+
desiredByMgr.set(mgr, desired);
|
|
23
|
+
}
|
|
24
|
+
desired.set(view.viewId, view);
|
|
25
|
+
mgr.setPlacementSnapshot({ generation, epoch: globalEpoch++, views: [...desired.values()] });
|
|
26
|
+
}
|
|
27
|
+
export function hostToolbarBounds(mgr, rect) {
|
|
28
|
+
pushView(mgr, { viewId: VIEW_ID.hostToolbar, placement: toPlacement(rect), layer: VIEW_LAYER.hostToolbar });
|
|
29
|
+
}
|
|
30
|
+
export function simulatorDevtoolsBounds(mgr, rect) {
|
|
31
|
+
pushView(mgr, { viewId: VIEW_ID.simulatorDevtools, placement: toPlacement(rect), layer: VIEW_LAYER.base });
|
|
32
|
+
}
|
|
33
|
+
export function workbenchBounds(mgr, rect) {
|
|
34
|
+
pushView(mgr, { viewId: VIEW_ID.workbench, placement: toPlacement(rect), layer: VIEW_LAYER.base });
|
|
35
|
+
}
|
|
36
|
+
export function simulatorBounds(mgr, params) {
|
|
37
|
+
const { zoom, ...rect } = params;
|
|
38
|
+
pushView(mgr, { viewId: VIEW_ID.simulator, placement: toPlacement(rect), layer: VIEW_LAYER.base, extra: { zoom } });
|
|
39
|
+
}
|
|
40
|
+
/** Drop a view from this manager's desired table (reconcile detaches it). */
|
|
41
|
+
export function removeView(mgr, viewId) {
|
|
42
|
+
const desired = desiredByMgr.get(mgr);
|
|
43
|
+
if (!desired)
|
|
44
|
+
return;
|
|
45
|
+
desired.delete(viewId);
|
|
46
|
+
mgr.setPlacementSnapshot({ generation: 1, epoch: globalEpoch++, views: [...desired.values()] });
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=placement-test-driver.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,8 +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';
|
|
5
|
+
import type { PlacementSnapshot } from '@dimina-kit/electron-deck/layout';
|
|
6
|
+
import type { DevtoolsExtra } from '../../../shared/view-ids.js';
|
|
7
|
+
export { resolveProjectEditorTarget, type ProjectEditorTarget, } from './resolve-project-editor-target.js';
|
|
6
8
|
/**
|
|
7
9
|
* Context surface used by the ViewManager. We only need a small slice of the
|
|
8
10
|
* full WorkbenchContext here; typing it this way documents the actual dependency.
|
|
@@ -11,10 +13,10 @@ export interface ViewManagerContext {
|
|
|
11
13
|
windows: WorkbenchContext['windows'];
|
|
12
14
|
rendererDir: string;
|
|
13
15
|
/**
|
|
14
|
-
* Per-webContents connection registry
|
|
16
|
+
* Per-webContents connection registry. The native-host
|
|
15
17
|
* simulator WebContentsView is acquired here so the registry tracks that
|
|
16
18
|
* trusted webContents and tears its per-wc resources (the custom-api bridge
|
|
17
|
-
* `ipcMain.on`) down deterministically on destroy
|
|
19
|
+
* `ipcMain.on`) down deterministically on destroy. Required:
|
|
18
20
|
* `createWorkbenchContext` always supplies it.
|
|
19
21
|
*/
|
|
20
22
|
connections: WorkbenchContext['connections'];
|
|
@@ -67,6 +69,11 @@ export interface ViewManagerContext {
|
|
|
67
69
|
* main-process WebContentsView (`attachWorkbench`) hung off the main window's
|
|
68
70
|
* contentView, like the other overlays. The main window's own renderer is the
|
|
69
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.
|
|
70
77
|
*/
|
|
71
78
|
export interface ViewManager {
|
|
72
79
|
/**
|
|
@@ -87,6 +94,16 @@ export interface ViewManager {
|
|
|
87
94
|
* geometry is anchor-published.
|
|
88
95
|
*/
|
|
89
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;
|
|
90
107
|
/**
|
|
91
108
|
* Destroy and null out the simulator view (e.g. on simulator detach).
|
|
92
109
|
* Also destroys the cached settings view and hides the popover —
|
|
@@ -135,22 +152,6 @@ export interface ViewManager {
|
|
|
135
152
|
* always on close-project → reopen) would otherwise be lost forever.
|
|
136
153
|
*/
|
|
137
154
|
getHostToolbarHeight(): number;
|
|
138
|
-
/**
|
|
139
|
-
* NATIVE-HOST ONLY. Position the simulator content WebContentsView over the
|
|
140
|
-
* renderer-measured simulator panel REGION rect (the flex:1 placeholder slot,
|
|
141
|
-
* CSS px from the main window content top-left, which maps 1:1 to overlay
|
|
142
|
-
* setBounds DIP) and apply the device zoom. The WCV fills the region as a
|
|
143
|
-
* plain rectangle; DeviceShell draws + scrolls the phone inside. No-op in the
|
|
144
|
-
* default `<webview>` path (`nativeSimulatorView` is null). See
|
|
145
|
-
* `computeNativeSimulatorViewParams`.
|
|
146
|
-
*/
|
|
147
|
-
setNativeSimulatorViewBounds(params: {
|
|
148
|
-
x: number;
|
|
149
|
-
y: number;
|
|
150
|
-
width: number;
|
|
151
|
-
height: number;
|
|
152
|
-
zoom: number;
|
|
153
|
-
}): void;
|
|
154
155
|
/**
|
|
155
156
|
* NATIVE-HOST ONLY. Re-push the selected device's CSS `env(safe-area-inset-*)`
|
|
156
157
|
* override to every attached render-host guest (on device change). New guests
|
|
@@ -164,17 +165,12 @@ export interface ViewManager {
|
|
|
164
165
|
*/
|
|
165
166
|
resize(simWidth: number): void;
|
|
166
167
|
/**
|
|
167
|
-
* Apply
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
168
|
+
* Apply the renderer's window-level placement snapshot — the single source of
|
|
169
|
+
* truth for every managed native view's bounds/visibility/z-order. Merged with
|
|
170
|
+
* main-owned settings/popover desired state and reconciled against the actual
|
|
171
|
+
* view tree.
|
|
171
172
|
*/
|
|
172
|
-
|
|
173
|
-
x: number;
|
|
174
|
-
y: number;
|
|
175
|
-
width: number;
|
|
176
|
-
height: number;
|
|
177
|
-
}): void;
|
|
173
|
+
setPlacementSnapshot(snapshot: PlacementSnapshot<DevtoolsExtra>): void;
|
|
178
174
|
/**
|
|
179
175
|
* Lazily create the workbench WebContentsView, load `<url>index.html`, and
|
|
180
176
|
* add it to the contentView. `url` is the COI server's base URL (trailing
|
|
@@ -188,19 +184,6 @@ export interface ViewManager {
|
|
|
188
184
|
* `setWorkbenchBounds`, keeping it off the app boot critical path.
|
|
189
185
|
*/
|
|
190
186
|
setWorkbenchSource(url: string): void;
|
|
191
|
-
/**
|
|
192
|
-
* Apply a renderer-measured rectangle to the workbench editor view. Mirrors
|
|
193
|
-
* `setSimulatorDevtoolsBounds`: `{ width: 0, height: 0 }` is "hide" — the view
|
|
194
|
-
* is removed from the contentView (kept under settings/popover when re-added)
|
|
195
|
-
* but its WebContents stays alive. The first non-zero rect lazily creates +
|
|
196
|
-
* loads the workbench (from the URL set by `setWorkbenchSource`).
|
|
197
|
-
*/
|
|
198
|
-
setWorkbenchBounds(bounds: {
|
|
199
|
-
x: number;
|
|
200
|
-
y: number;
|
|
201
|
-
width: number;
|
|
202
|
-
height: number;
|
|
203
|
-
}): void;
|
|
204
187
|
/** Destroy the workbench editor view (teardown). No-op if never attached. */
|
|
205
188
|
detachWorkbench(): void;
|
|
206
189
|
/**
|
|
@@ -211,20 +194,6 @@ export interface ViewManager {
|
|
|
211
194
|
* request was dispatched to the workbench.
|
|
212
195
|
*/
|
|
213
196
|
openFileInWorkbench(relPath: string, line: number, column: number): boolean;
|
|
214
|
-
/**
|
|
215
|
-
* Apply a renderer-measured rectangle to the host-controllable toolbar
|
|
216
|
-
* WebContentsView (the strip above the devtools header). Forward anchor,
|
|
217
|
-
* mirroring `setSimulatorDevtoolsBounds`. `{ width: 0, height: 0 }` is
|
|
218
|
-
* treated as "hide" — the view is removed from the contentView but its
|
|
219
|
-
* WebContents (and the host's loaded content) stays alive. Lazily creates
|
|
220
|
-
* the view on the first non-empty rect.
|
|
221
|
-
*/
|
|
222
|
-
setHostToolbarBounds(bounds: {
|
|
223
|
-
x: number;
|
|
224
|
-
y: number;
|
|
225
|
-
width: number;
|
|
226
|
-
height: number;
|
|
227
|
-
}): void;
|
|
228
197
|
/**
|
|
229
198
|
* Reverse size-advertiser sink: the toolbar WCV's own renderer advertises
|
|
230
199
|
* its intrinsic content height (block-axis extent); we retain it as the
|
|
@@ -319,23 +288,12 @@ export interface HostToolbarControl {
|
|
|
319
288
|
*/
|
|
320
289
|
setHeightMode(mode: HostToolbarHeightMode): void;
|
|
321
290
|
}
|
|
322
|
-
export interface ProjectEditorTarget {
|
|
323
|
-
path: string;
|
|
324
|
-
line?: number;
|
|
325
|
-
column?: number;
|
|
326
|
-
}
|
|
327
|
-
/**
|
|
328
|
-
* Resolve a DevTools source request against the service-host URL that created
|
|
329
|
-
* the inspected app. Its pkgRoot is authoritative; the workspace is only a
|
|
330
|
-
* stale-session consistency guard.
|
|
331
|
-
*/
|
|
332
|
-
export declare function resolveProjectEditorTarget(serviceHostUrl: string, activeProjectRoot: string | undefined, req: OpenInEditorRequest, isFile?: (absolutePath: string) => boolean): ProjectEditorTarget | null;
|
|
333
291
|
/**
|
|
334
292
|
* Build a ViewManager bound to the given context. The returned object is the
|
|
335
293
|
* only component allowed to instantiate or add/remove overlay WebContentsViews.
|
|
336
294
|
*
|
|
337
|
-
* All view-related mutable state lives inside
|
|
338
|
-
* 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.
|
|
339
297
|
*/
|
|
340
298
|
export declare function createViewManager(ctx: ViewManagerContext): ViewManager;
|
|
341
299
|
//# sourceMappingURL=view-manager.d.ts.map
|