@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.
Files changed (62) hide show
  1. package/dist/main/app/max-listeners-diagnostic.js +5 -1
  2. package/dist/main/index.bundle.js +1532 -1236
  3. package/dist/main/ipc/bridge-router.d.ts +28 -0
  4. package/dist/main/ipc/bridge-router.js +175 -50
  5. package/dist/main/ipc/session-listener-bag.d.ts +41 -0
  6. package/dist/main/ipc/session-listener-bag.js +40 -0
  7. package/dist/main/ipc/simulator.js +5 -1
  8. package/dist/main/services/elements-forward/index.js +19 -0
  9. package/dist/main/services/network-forward/index.js +13 -0
  10. package/dist/main/services/views/destroy-child-view.d.ts +9 -0
  11. package/dist/main/services/views/destroy-child-view.js +23 -0
  12. package/dist/main/services/views/host-toolbar-view.d.ts +21 -0
  13. package/dist/main/services/views/host-toolbar-view.js +249 -0
  14. package/dist/main/services/views/inject-when-ready.d.ts +41 -0
  15. package/dist/main/services/views/inject-when-ready.js +62 -0
  16. package/dist/main/services/views/native-simulator-devtools-host.d.ts +26 -0
  17. package/dist/main/services/views/native-simulator-devtools-host.js +484 -0
  18. package/dist/main/services/views/native-simulator-view.d.ts +28 -0
  19. package/dist/main/services/views/native-simulator-view.js +428 -0
  20. package/dist/main/services/views/overlay-panels-view.d.ts +29 -0
  21. package/dist/main/services/views/overlay-panels-view.js +150 -0
  22. package/dist/main/services/views/placement-reconciler.d.ts +73 -0
  23. package/dist/main/services/views/placement-reconciler.js +120 -0
  24. package/dist/main/services/views/resolve-project-editor-target.d.ts +13 -0
  25. package/dist/main/services/views/resolve-project-editor-target.js +33 -0
  26. package/dist/main/services/views/view-manager.d.ts +20 -16
  27. package/dist/main/services/views/view-manager.js +66 -1545
  28. package/dist/main/services/views/workbench-view.d.ts +15 -0
  29. package/dist/main/services/views/workbench-view.js +170 -0
  30. package/dist/preload/windows/host-toolbar-runtime.cjs.map +2 -2
  31. package/dist/preload/windows/main.cjs +10 -1
  32. package/dist/preload/windows/main.cjs.map +2 -2
  33. package/dist/preload/windows/simulator.cjs +6 -0
  34. package/dist/preload/windows/simulator.cjs.map +2 -2
  35. package/dist/preload/windows/simulator.js +6 -0
  36. package/dist/renderer/assets/index-ChF9V4r-.js +49 -0
  37. package/dist/renderer/assets/{input-Djg2NR4U.js → input-Cjk0wSEh.js} +2 -2
  38. package/dist/renderer/assets/ipc-transport-D5dKIti1.css +1 -0
  39. package/dist/renderer/assets/{ipc-transport-Be-AzCub.js → ipc-transport-OZS-KmOW.js} +2 -2
  40. package/dist/renderer/assets/{popover-eDUh4NYe.js → popover-CQVvOe90.js} +2 -2
  41. package/dist/renderer/assets/{select-DesFfkVV.js → select-BQDXi5ih.js} +2 -2
  42. package/dist/renderer/assets/{settings-api-HQ7ZFPWi.js → settings-api-B6x2mhaD.js} +2 -2
  43. package/dist/renderer/assets/{settings-CRmO7zUG.js → settings-tP3Px2KR.js} +2 -2
  44. package/dist/renderer/assets/{workbenchSettings-CZBWsliT.js → workbenchSettings-Bim9ol9R.js} +2 -2
  45. package/dist/renderer/entries/main/index.html +6 -6
  46. package/dist/renderer/entries/popover/index.html +5 -5
  47. package/dist/renderer/entries/settings/index.html +5 -5
  48. package/dist/renderer/entries/workbench-settings/index.html +4 -4
  49. package/dist/shared/bridge-channels.d.ts +17 -0
  50. package/dist/shared/bridge-channels.js +9 -0
  51. package/dist/shared/ipc-channels.d.ts +1 -0
  52. package/dist/shared/ipc-channels.js +6 -0
  53. package/dist/shared/ipc-schemas.d.ts +6 -0
  54. package/dist/shared/ipc-schemas.js +10 -0
  55. package/dist/simulator/assets/{bridge-channels-BUQ5AbvJ.js → bridge-channels-zhsumfky.js} +2 -2
  56. package/dist/simulator/assets/{device-shell-CT3LRnTB.js → device-shell-CUl0ILfn.js} +2 -2
  57. package/dist/simulator/assets/{simulator-BizhGGE3.js → simulator-Dz8iGcgy.js} +5 -5
  58. package/dist/simulator/assets/{simulator-mini-app-BfNuDF3R.js → simulator-mini-app-Bt639XL_.js} +2 -2
  59. package/dist/simulator/simulator.html +2 -1
  60. package/package.json +5 -5
  61. package/dist/renderer/assets/index-jDrNHghH.js +0 -49
  62. 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 { type HostToolbarMessageSubscription } from './host-toolbar-port-channel.js';
4
+ import type { HostToolbarMessageSubscription } from './host-toolbar-port-channel.js';
6
5
  import type { PlacementSnapshot } from '@dimina-kit/electron-deck/layout';
7
- import { type DevtoolsExtra } from '../../../shared/view-ids.js';
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 this closure and is not exposed
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