@dimina-kit/devtools 0.4.0-dev.20260616102751 → 0.4.0-dev.20260618090552
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 +25 -24
- package/dist/main/index.bundle.js +212 -12
- package/dist/main/ipc/bridge-router.js +44 -1
- package/dist/main/runtime/devtools-backend.d.ts +1 -1
- package/dist/main/runtime/devtools-backend.js +1 -1
- package/dist/main/runtime/miniapp-runtime.d.ts +2 -2
- package/dist/main/services/console-forward/index.js +8 -1
- package/dist/main/services/elements-forward/index.js +31 -7
- package/dist/main/services/layout/index.d.ts +12 -0
- package/dist/main/services/layout/index.js +14 -2
- package/dist/main/services/projects/types.d.ts +1 -1
- package/dist/main/services/projects/types.js +1 -1
- package/dist/main/services/service-console/console-api.d.ts +78 -0
- package/dist/main/services/service-console/console-api.js +104 -0
- package/dist/main/services/service-console/index.d.ts +47 -0
- package/dist/main/services/service-console/index.js +139 -0
- package/dist/main/services/update/update-manager.d.ts +1 -1
- package/dist/main/services/update/update-manager.js +1 -1
- package/dist/main/services/views/view-manager.d.ts +7 -0
- package/dist/main/services/views/view-manager.js +53 -0
- package/dist/main/services/workbench-context.d.ts +7 -6
- package/dist/renderer/assets/index-DmgWoK8N.js +50 -0
- package/dist/renderer/assets/{input-B8NDCYv3.js → input-DiCxYlqY.js} +2 -2
- package/dist/renderer/assets/{ipc-transport-DhrajiC5.js → ipc-transport-DSBmaWpJ.js} +1 -1
- package/dist/renderer/assets/ipc-transport-DZxf2YoM.css +1 -0
- package/dist/renderer/assets/{popover-DbZOg03s.js → popover-CyL_uffj.js} +2 -2
- package/dist/renderer/assets/{select-pW3Ysc9T.js → select-BSwvDMtA.js} +2 -2
- package/dist/renderer/assets/settings-Ch8XbFxf.js +2 -0
- package/dist/renderer/assets/{settings-api-BSKzKiWd.js → settings-api-e9ILe2JO.js} +2 -2
- package/dist/renderer/assets/{workbenchSettings-BGxjl25x.js → workbenchSettings-DZsHf1g_.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 +11 -5
- package/dist/renderer/entries/workbench-settings/index.html +4 -4
- package/dist/service-host/preload.cjs +13 -15
- package/dist/service-host/sourcemap-rewrite.cjs +21 -12
- package/package.json +4 -4
- package/dist/renderer/assets/index-Z4Rh52G8.js +0 -50
- package/dist/renderer/assets/ipc-transport-9agi76dX.css +0 -1
- package/dist/renderer/assets/settings-CZJOHt8b.js +0 -2
|
@@ -26,7 +26,19 @@ export declare function computeNativeSimulatorViewParams(rect: {
|
|
|
26
26
|
bounds: Bounds;
|
|
27
27
|
zoomFactor: number;
|
|
28
28
|
};
|
|
29
|
+
/** Renderer-side width of the settings PANEL (the opaque right strip). The
|
|
30
|
+
* settings WebContentsView itself spans the full content area (see
|
|
31
|
+
* `computeSettingsBounds`) as a transparent backdrop; the panel is positioned
|
|
32
|
+
* inside it at this width by `settings.tsx`. */
|
|
29
33
|
export declare const SETTINGS_W = 320;
|
|
34
|
+
/**
|
|
35
|
+
* The settings overlay is a FULL-content-area transparent backdrop (header
|
|
36
|
+
* excluded), identical to the popover region — NOT a right-edge strip. The
|
|
37
|
+
* renderer paints a transparent backdrop over the whole area and an opaque
|
|
38
|
+
* `SETTINGS_W`-wide panel on the right; a click on the transparent backdrop
|
|
39
|
+
* closes the overlay (clicks outside the panel must reach the backdrop, which is
|
|
40
|
+
* only possible when the view spans the whole area rather than just the strip).
|
|
41
|
+
*/
|
|
30
42
|
export declare function computeSettingsBounds(contentWidth: number, contentHeight: number, headerHeight: number): Bounds;
|
|
31
43
|
export declare function computePopoverBounds(contentWidth: number, contentHeight: number, headerHeight: number): Bounds;
|
|
32
44
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -23,12 +23,24 @@ export function computeNativeSimulatorViewParams(rect, zoomPercent) {
|
|
|
23
23
|
zoomFactor,
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
|
+
/** Renderer-side width of the settings PANEL (the opaque right strip). The
|
|
27
|
+
* settings WebContentsView itself spans the full content area (see
|
|
28
|
+
* `computeSettingsBounds`) as a transparent backdrop; the panel is positioned
|
|
29
|
+
* inside it at this width by `settings.tsx`. */
|
|
26
30
|
export const SETTINGS_W = 320;
|
|
31
|
+
/**
|
|
32
|
+
* The settings overlay is a FULL-content-area transparent backdrop (header
|
|
33
|
+
* excluded), identical to the popover region — NOT a right-edge strip. The
|
|
34
|
+
* renderer paints a transparent backdrop over the whole area and an opaque
|
|
35
|
+
* `SETTINGS_W`-wide panel on the right; a click on the transparent backdrop
|
|
36
|
+
* closes the overlay (clicks outside the panel must reach the backdrop, which is
|
|
37
|
+
* only possible when the view spans the whole area rather than just the strip).
|
|
38
|
+
*/
|
|
27
39
|
export function computeSettingsBounds(contentWidth, contentHeight, headerHeight) {
|
|
28
40
|
return {
|
|
29
|
-
x:
|
|
41
|
+
x: 0,
|
|
30
42
|
y: headerHeight,
|
|
31
|
-
width: Math.
|
|
43
|
+
width: Math.max(1, contentWidth),
|
|
32
44
|
height: Math.max(1, contentHeight - headerHeight),
|
|
33
45
|
};
|
|
34
46
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Public extensibility surface for the project list panel. Hosts that embed
|
|
3
|
-
* dimina-devtools (
|
|
3
|
+
* dimina-devtools (downstream hosts) can inject implementations of these types via
|
|
4
4
|
* `WorkbenchAppConfig` to fully take over the project source-of-truth, the
|
|
5
5
|
* template catalog, and the "新建项目" dialog.
|
|
6
6
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Public extensibility surface for the project list panel. Hosts that embed
|
|
3
|
-
* dimina-devtools (
|
|
3
|
+
* dimina-devtools (downstream hosts) can inject implementations of these types via
|
|
4
4
|
* `WorkbenchAppConfig` to fully take over the project source-of-truth, the
|
|
5
5
|
* template catalog, and the "新建项目" dialog.
|
|
6
6
|
*/
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers for the native-host service-console forwarder.
|
|
3
|
+
*
|
|
4
|
+
* Under native-host the service-host's console used to be captured by a
|
|
5
|
+
* `console.*` monkeypatch in `service-host/preload.cjs`. That wrapper added a
|
|
6
|
+
* stack frame, so Chrome DevTools (attached natively to the service host)
|
|
7
|
+
* attributed EVERY service-layer log to the wrapper line (`preload.cjs:237`)
|
|
8
|
+
* instead of the developer's source. We now capture via CDP
|
|
9
|
+
* `Runtime.consoleAPICalled` instead, which preserves native source attribution.
|
|
10
|
+
*
|
|
11
|
+
* These functions turn the CDP event shape into the `GuestConsoleEntry` shape
|
|
12
|
+
* the existing console fan-out (automation `App.logAdded`) expects, WITHOUT any
|
|
13
|
+
* electron / IO dependency so they are unit-testable in isolation. The async
|
|
14
|
+
* deep-fetch (`Runtime.callFunctionOn`) for object args lives in `index.ts`.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* sourceURL stamped on the render→service `[视图]` re-injection script
|
|
18
|
+
* (`console-forward.buildForwardScript`). The CDP capture skips any
|
|
19
|
+
* `consoleAPICalled` whose top frame carries this URL so a forwarded render line
|
|
20
|
+
* is not re-captured and re-broadcast as a service entry (duplicate / loop).
|
|
21
|
+
*/
|
|
22
|
+
export declare const RENDER_FORWARD_SOURCE_URL = "dimina://render-console-forward";
|
|
23
|
+
export type ConsoleLevel = 'log' | 'warn' | 'error' | 'info' | 'debug';
|
|
24
|
+
/** Subset of a CDP `Runtime.RemoteObject` we read. */
|
|
25
|
+
export interface RemoteObjectLike {
|
|
26
|
+
type?: string;
|
|
27
|
+
subtype?: string;
|
|
28
|
+
value?: unknown;
|
|
29
|
+
unserializableValue?: string;
|
|
30
|
+
description?: string;
|
|
31
|
+
objectId?: string;
|
|
32
|
+
preview?: {
|
|
33
|
+
properties?: Array<{
|
|
34
|
+
name: string;
|
|
35
|
+
value: string;
|
|
36
|
+
type: string;
|
|
37
|
+
}>;
|
|
38
|
+
subtype?: string;
|
|
39
|
+
overflow?: boolean;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** Subset of a CDP `Runtime.consoleAPICalled` params we read. */
|
|
43
|
+
export interface ConsoleApiParamsLike {
|
|
44
|
+
type?: string;
|
|
45
|
+
args?: RemoteObjectLike[];
|
|
46
|
+
stackTrace?: {
|
|
47
|
+
callFrames?: Array<{
|
|
48
|
+
url?: string;
|
|
49
|
+
}>;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* CDP `consoleAPICalled.type` → our console level. CDP emits `'warning'` where
|
|
54
|
+
* we use `'warn'`; every other known level passes through; anything else (e.g.
|
|
55
|
+
* `'dir'`, `'table'`, `'trace'`, `undefined`) maps to `'log'`.
|
|
56
|
+
*/
|
|
57
|
+
export declare function mapConsoleApiType(type: string | undefined): ConsoleLevel;
|
|
58
|
+
/**
|
|
59
|
+
* A single RemoteObject → a JSON-serializable value WITHOUT a CDP round-trip
|
|
60
|
+
* (shallow). Objects that need their full contents are flagged by
|
|
61
|
+
* {@link needsDeepFetch} and deep-serialized by the caller; this is the
|
|
62
|
+
* inline/best-effort fallback.
|
|
63
|
+
*/
|
|
64
|
+
export declare function remoteObjectToValue(ro: RemoteObjectLike): unknown;
|
|
65
|
+
/**
|
|
66
|
+
* Whether this RemoteObject must be deep-fetched via `Runtime.callFunctionOn`
|
|
67
|
+
* (returnByValue) to be fully serialized — i.e. a real object/array referenced
|
|
68
|
+
* by `objectId` with no inline `value`. Functions, primitives, `null`, and
|
|
69
|
+
* already-inlined values do not.
|
|
70
|
+
*/
|
|
71
|
+
export declare function needsDeepFetch(ro: RemoteObjectLike): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* True when a `consoleAPICalled` event is the render→service `[视图]`
|
|
74
|
+
* re-injection (its top call frame URL === the sentinel) and must NOT be
|
|
75
|
+
* re-forwarded — the original render entry already reached every consumer.
|
|
76
|
+
*/
|
|
77
|
+
export declare function isRenderForwardEvent(params: ConsoleApiParamsLike, sentinelUrl: string): boolean;
|
|
78
|
+
//# sourceMappingURL=console-api.d.ts.map
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers for the native-host service-console forwarder.
|
|
3
|
+
*
|
|
4
|
+
* Under native-host the service-host's console used to be captured by a
|
|
5
|
+
* `console.*` monkeypatch in `service-host/preload.cjs`. That wrapper added a
|
|
6
|
+
* stack frame, so Chrome DevTools (attached natively to the service host)
|
|
7
|
+
* attributed EVERY service-layer log to the wrapper line (`preload.cjs:237`)
|
|
8
|
+
* instead of the developer's source. We now capture via CDP
|
|
9
|
+
* `Runtime.consoleAPICalled` instead, which preserves native source attribution.
|
|
10
|
+
*
|
|
11
|
+
* These functions turn the CDP event shape into the `GuestConsoleEntry` shape
|
|
12
|
+
* the existing console fan-out (automation `App.logAdded`) expects, WITHOUT any
|
|
13
|
+
* electron / IO dependency so they are unit-testable in isolation. The async
|
|
14
|
+
* deep-fetch (`Runtime.callFunctionOn`) for object args lives in `index.ts`.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* sourceURL stamped on the render→service `[视图]` re-injection script
|
|
18
|
+
* (`console-forward.buildForwardScript`). The CDP capture skips any
|
|
19
|
+
* `consoleAPICalled` whose top frame carries this URL so a forwarded render line
|
|
20
|
+
* is not re-captured and re-broadcast as a service entry (duplicate / loop).
|
|
21
|
+
*/
|
|
22
|
+
export const RENDER_FORWARD_SOURCE_URL = 'dimina://render-console-forward';
|
|
23
|
+
const KNOWN_LEVELS = new Set(['log', 'warn', 'error', 'info', 'debug']);
|
|
24
|
+
/**
|
|
25
|
+
* CDP `consoleAPICalled.type` → our console level. CDP emits `'warning'` where
|
|
26
|
+
* we use `'warn'`; every other known level passes through; anything else (e.g.
|
|
27
|
+
* `'dir'`, `'table'`, `'trace'`, `undefined`) maps to `'log'`.
|
|
28
|
+
*/
|
|
29
|
+
export function mapConsoleApiType(type) {
|
|
30
|
+
if (type === 'warning')
|
|
31
|
+
return 'warn';
|
|
32
|
+
if (type && KNOWN_LEVELS.has(type))
|
|
33
|
+
return type;
|
|
34
|
+
return 'log';
|
|
35
|
+
}
|
|
36
|
+
const BIGINT_LITERAL = /^-?\d+n$/;
|
|
37
|
+
/**
|
|
38
|
+
* A single RemoteObject → a JSON-serializable value WITHOUT a CDP round-trip
|
|
39
|
+
* (shallow). Objects that need their full contents are flagged by
|
|
40
|
+
* {@link needsDeepFetch} and deep-serialized by the caller; this is the
|
|
41
|
+
* inline/best-effort fallback.
|
|
42
|
+
*/
|
|
43
|
+
export function remoteObjectToValue(ro) {
|
|
44
|
+
if (!ro)
|
|
45
|
+
return ro;
|
|
46
|
+
// 1. An inlined value (CDP includes `value` for JSON-serializable primitives
|
|
47
|
+
// and small arrays). Use `in` so falsy values (0, '', false) and an
|
|
48
|
+
// explicit `null` are honoured rather than falling through.
|
|
49
|
+
if ('value' in ro)
|
|
50
|
+
return ro.value;
|
|
51
|
+
// 2. Specials that can't ride in `value`.
|
|
52
|
+
if (typeof ro.unserializableValue === 'string') {
|
|
53
|
+
const u = ro.unserializableValue;
|
|
54
|
+
if (u === 'Infinity')
|
|
55
|
+
return Infinity;
|
|
56
|
+
if (u === '-Infinity')
|
|
57
|
+
return -Infinity;
|
|
58
|
+
if (u === 'NaN')
|
|
59
|
+
return NaN;
|
|
60
|
+
if (u === '-0')
|
|
61
|
+
return -0;
|
|
62
|
+
if (BIGINT_LITERAL.test(u))
|
|
63
|
+
return u;
|
|
64
|
+
return u;
|
|
65
|
+
}
|
|
66
|
+
// 3. Fall back by type.
|
|
67
|
+
switch (ro.type) {
|
|
68
|
+
case 'undefined':
|
|
69
|
+
return undefined;
|
|
70
|
+
case 'function':
|
|
71
|
+
return ro.description ?? '[Function]';
|
|
72
|
+
case 'symbol':
|
|
73
|
+
return ro.description ?? '[Symbol]';
|
|
74
|
+
case 'object':
|
|
75
|
+
if (ro.subtype === 'null')
|
|
76
|
+
return null;
|
|
77
|
+
return ro.description ?? '[Object]';
|
|
78
|
+
default:
|
|
79
|
+
return ro.description ?? '[Unknown]';
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Whether this RemoteObject must be deep-fetched via `Runtime.callFunctionOn`
|
|
84
|
+
* (returnByValue) to be fully serialized — i.e. a real object/array referenced
|
|
85
|
+
* by `objectId` with no inline `value`. Functions, primitives, `null`, and
|
|
86
|
+
* already-inlined values do not.
|
|
87
|
+
*/
|
|
88
|
+
export function needsDeepFetch(ro) {
|
|
89
|
+
if (!ro)
|
|
90
|
+
return false;
|
|
91
|
+
if ('value' in ro)
|
|
92
|
+
return false;
|
|
93
|
+
return ro.type === 'object' && ro.subtype !== 'null' && typeof ro.objectId === 'string';
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* True when a `consoleAPICalled` event is the render→service `[视图]`
|
|
97
|
+
* re-injection (its top call frame URL === the sentinel) and must NOT be
|
|
98
|
+
* re-forwarded — the original render entry already reached every consumer.
|
|
99
|
+
*/
|
|
100
|
+
export function isRenderForwardEvent(params, sentinelUrl) {
|
|
101
|
+
const top = params?.stackTrace?.callFrames?.[0]?.url;
|
|
102
|
+
return typeof top === 'string' && top === sentinelUrl;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=console-api.js.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native-host SERVICE-layer console capture via CDP.
|
|
3
|
+
*
|
|
4
|
+
* The embedded Chrome DevTools front-end is attached natively to the service
|
|
5
|
+
* host, so the service layer's `console.*` already displays there with correct
|
|
6
|
+
* source attribution + sourcemaps — UNLESS something rewrites `console.*`. The
|
|
7
|
+
* old `service-host/preload.cjs` monkeypatch did exactly that (wrapping each
|
|
8
|
+
* level to also post the entry to main), which made DevTools attribute every
|
|
9
|
+
* service log to the wrapper line (`preload.cjs:237`) instead of the developer's
|
|
10
|
+
* source. That monkeypatch is removed; this service replaces its ONE remaining
|
|
11
|
+
* job — feeding service-layer entries to the console fan-out (automation
|
|
12
|
+
* `App.logAdded`) — by capturing `Runtime.consoleAPICalled` over an in-process
|
|
13
|
+
* CDP session instead. Capturing at the CDP layer adds no stack frame, so native
|
|
14
|
+
* attribution in the DevTools console is preserved (verified: an in-process
|
|
15
|
+
* `debugger.attach('1.3')` coexists with the custom-host DevTools front-end on
|
|
16
|
+
* the same wc — Electron multi-client CDP).
|
|
17
|
+
*
|
|
18
|
+
* Lifecycle: installed when the right-panel DevTools is pointed at a service host
|
|
19
|
+
* wc (`view-manager.pointNativeDevtoolsAtServiceWc`) and stopped when that source
|
|
20
|
+
* is closed / swapped (pre-warm pool recycles the service window). `stop()` is
|
|
21
|
+
* idempotent.
|
|
22
|
+
*/
|
|
23
|
+
import type { WebContents } from 'electron';
|
|
24
|
+
import type { ConnectionRegistry } from '@dimina-kit/electron-deck/main';
|
|
25
|
+
import type { GuestConsoleEntry } from '../console-forward/index.js';
|
|
26
|
+
export interface ServiceConsoleForwardDeps {
|
|
27
|
+
/** The service host wc whose `console.*` we capture (top-level BrowserWindow wc). */
|
|
28
|
+
serviceWc: WebContents;
|
|
29
|
+
/** Sink for each captured service entry (wired to `consoleForwarder.emit`). */
|
|
30
|
+
emit: (entry: GuestConsoleEntry) => void;
|
|
31
|
+
/** Connection registry — binds teardown to the service wc's destroy ('closed'). */
|
|
32
|
+
connections?: ConnectionRegistry;
|
|
33
|
+
}
|
|
34
|
+
export interface ServiceConsoleForwardHandle {
|
|
35
|
+
/** Detach the CDP session (if we attached it) and stop capturing. Idempotent. */
|
|
36
|
+
stop(): void;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Attach an in-process CDP session to `serviceWc`, enable Runtime, and forward
|
|
40
|
+
* every `Runtime.consoleAPICalled` to `emit` as a `source:'service'` entry —
|
|
41
|
+
* EXCEPT the render→service `[视图]` re-injection (skipped via sentinel so it is
|
|
42
|
+
* not double-broadcast). Object args are deep-serialized via
|
|
43
|
+
* `Runtime.callFunctionOn` on our OWN session (the objectId can't be released by
|
|
44
|
+
* the front-end), falling back to the shallow inline value on failure.
|
|
45
|
+
*/
|
|
46
|
+
export declare function installServiceConsoleForward(deps: ServiceConsoleForwardDeps): ServiceConsoleForwardHandle;
|
|
47
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { RENDER_FORWARD_SOURCE_URL, mapConsoleApiType, needsDeepFetch, remoteObjectToValue, isRenderForwardEvent, } from './console-api.js';
|
|
2
|
+
/**
|
|
3
|
+
* Attach an in-process CDP session to `serviceWc`, enable Runtime, and forward
|
|
4
|
+
* every `Runtime.consoleAPICalled` to `emit` as a `source:'service'` entry —
|
|
5
|
+
* EXCEPT the render→service `[视图]` re-injection (skipped via sentinel so it is
|
|
6
|
+
* not double-broadcast). Object args are deep-serialized via
|
|
7
|
+
* `Runtime.callFunctionOn` on our OWN session (the objectId can't be released by
|
|
8
|
+
* the front-end), falling back to the shallow inline value on failure.
|
|
9
|
+
*/
|
|
10
|
+
export function installServiceConsoleForward(deps) {
|
|
11
|
+
const { serviceWc, emit, connections } = deps;
|
|
12
|
+
let disposed = false;
|
|
13
|
+
// Did WE attach the session (vs. it already being attached by someone else)?
|
|
14
|
+
// Only detach what we own.
|
|
15
|
+
let selfAttached = false;
|
|
16
|
+
// Process events in arrival order even though arg resolution is async, so the
|
|
17
|
+
// forwarded sequence matches the order the developer logged.
|
|
18
|
+
let tail = Promise.resolve();
|
|
19
|
+
let closedSub;
|
|
20
|
+
function usableDebugger() {
|
|
21
|
+
try {
|
|
22
|
+
if (serviceWc.isDestroyed())
|
|
23
|
+
return false;
|
|
24
|
+
if (serviceWc.debugger.isAttached())
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
serviceWc.debugger.attach('1.3');
|
|
32
|
+
selfAttached = true;
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Race: someone attached between the check and here → still usable.
|
|
37
|
+
try {
|
|
38
|
+
return serviceWc.debugger.isAttached();
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** Deep-serialize one arg; best-effort, never throws. */
|
|
46
|
+
async function resolveArg(ro) {
|
|
47
|
+
if (!needsDeepFetch(ro))
|
|
48
|
+
return remoteObjectToValue(ro);
|
|
49
|
+
try {
|
|
50
|
+
const res = (await serviceWc.debugger.sendCommand('Runtime.callFunctionOn', {
|
|
51
|
+
objectId: ro.objectId,
|
|
52
|
+
functionDeclaration: 'function () { return this }',
|
|
53
|
+
returnByValue: true,
|
|
54
|
+
}));
|
|
55
|
+
if (res && res.result && 'value' in res.result)
|
|
56
|
+
return res.result.value;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Object released / host navigating — fall back to the shallow value.
|
|
60
|
+
}
|
|
61
|
+
return remoteObjectToValue(ro);
|
|
62
|
+
}
|
|
63
|
+
async function handleConsoleApi(params) {
|
|
64
|
+
if (disposed)
|
|
65
|
+
return;
|
|
66
|
+
// Skip the render→service `[视图]` re-injection — the original render entry
|
|
67
|
+
// already reached every consumer; re-forwarding would duplicate it.
|
|
68
|
+
if (isRenderForwardEvent(params, RENDER_FORWARD_SOURCE_URL))
|
|
69
|
+
return;
|
|
70
|
+
const level = mapConsoleApiType(params.type);
|
|
71
|
+
const rawArgs = Array.isArray(params.args) ? params.args : [];
|
|
72
|
+
let args;
|
|
73
|
+
try {
|
|
74
|
+
args = await Promise.all(rawArgs.map((a) => resolveArg(a)));
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
args = rawArgs.map((a) => remoteObjectToValue(a));
|
|
78
|
+
}
|
|
79
|
+
if (disposed)
|
|
80
|
+
return;
|
|
81
|
+
emit({ source: 'service', level, args, ts: Date.now() });
|
|
82
|
+
}
|
|
83
|
+
const onMessage = (_event, method, params) => {
|
|
84
|
+
if (disposed)
|
|
85
|
+
return;
|
|
86
|
+
if (method !== 'Runtime.consoleAPICalled')
|
|
87
|
+
return;
|
|
88
|
+
const p = params;
|
|
89
|
+
tail = tail.then(() => handleConsoleApi(p)).catch(() => { });
|
|
90
|
+
};
|
|
91
|
+
function stop() {
|
|
92
|
+
if (disposed)
|
|
93
|
+
return;
|
|
94
|
+
disposed = true;
|
|
95
|
+
try {
|
|
96
|
+
closedSub?.dispose();
|
|
97
|
+
}
|
|
98
|
+
catch { /* gone */ }
|
|
99
|
+
try {
|
|
100
|
+
serviceWc.debugger.removeListener('message', onMessage);
|
|
101
|
+
}
|
|
102
|
+
catch { /* gone */ }
|
|
103
|
+
if (selfAttached) {
|
|
104
|
+
try {
|
|
105
|
+
if (!serviceWc.isDestroyed() && serviceWc.debugger.isAttached())
|
|
106
|
+
serviceWc.debugger.detach();
|
|
107
|
+
}
|
|
108
|
+
catch { /* already detached / destroyed */ }
|
|
109
|
+
selfAttached = false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (!usableDebugger()) {
|
|
113
|
+
disposed = true;
|
|
114
|
+
return { stop() { } };
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
serviceWc.debugger.on('message', onMessage);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
stop();
|
|
121
|
+
return { stop() { } };
|
|
122
|
+
}
|
|
123
|
+
// Enable Runtime so consoleAPICalled flows. Best-effort — a mid-destroy host
|
|
124
|
+
// rejects, in which case there's simply nothing to capture.
|
|
125
|
+
serviceWc.debugger.sendCommand('Runtime.enable').catch(() => { });
|
|
126
|
+
// Bind teardown to the service wc's real destroy. The service window is
|
|
127
|
+
// pool-recycled, but 'closed' fires only on hard destroy — the right lifetime
|
|
128
|
+
// for a session we attached to THIS wc. `on('closed')` returns a handle whose
|
|
129
|
+
// dispose() removes the listener WITHOUT firing it.
|
|
130
|
+
try {
|
|
131
|
+
if (connections)
|
|
132
|
+
closedSub = connections.acquire(serviceWc).on('closed', stop);
|
|
133
|
+
else
|
|
134
|
+
serviceWc.once('destroyed', stop);
|
|
135
|
+
}
|
|
136
|
+
catch { /* fake/minimal wc in tests */ }
|
|
137
|
+
return { stop };
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -26,7 +26,7 @@ export declare class UpdateManager {
|
|
|
26
26
|
private downloadedPath;
|
|
27
27
|
/**
|
|
28
28
|
* Whether this instance has already emitted `Available` once. The periodic
|
|
29
|
-
* check fires forever (interval default 1h), and downstream UI (
|
|
29
|
+
* check fires forever (interval default 1h), and downstream UI (a downstream host shell
|
|
30
30
|
* renders a native toast / Notification) is not idempotent — every event
|
|
31
31
|
* stacks. We dedupe at the source: at most one Available per instance
|
|
32
32
|
* lifetime, regardless of how the upstream `info.version` shifts. The user
|
|
@@ -9,7 +9,7 @@ export class UpdateManager {
|
|
|
9
9
|
downloadedPath = null;
|
|
10
10
|
/**
|
|
11
11
|
* Whether this instance has already emitted `Available` once. The periodic
|
|
12
|
-
* check fires forever (interval default 1h), and downstream UI (
|
|
12
|
+
* check fires forever (interval default 1h), and downstream UI (a downstream host shell
|
|
13
13
|
* renders a native toast / Notification) is not idempotent — every event
|
|
14
14
|
* stacks. We dedupe at the source: at most one Available per instance
|
|
15
15
|
* lifetime, regardless of how the upstream `info.version` shifts. The user
|
|
@@ -34,6 +34,13 @@ export interface ViewManagerContext {
|
|
|
34
34
|
* console line is the fallback). Optional so partial test contexts compile.
|
|
35
35
|
*/
|
|
36
36
|
networkForward?: WorkbenchContext['networkForward'];
|
|
37
|
+
/**
|
|
38
|
+
* Always-on console fan-out (set by `installBridgeRouter`). The service-console
|
|
39
|
+
* capture feeds service-layer `consoleAPICalled` entries here so they reach
|
|
40
|
+
* automation; mirrors how render entries arrive via the bridge. Optional so
|
|
41
|
+
* partial test contexts compile.
|
|
42
|
+
*/
|
|
43
|
+
consoleForwarder?: WorkbenchContext['consoleForwarder'];
|
|
37
44
|
/**
|
|
38
45
|
* Per-context registry of host-registered simulator custom APIs. The
|
|
39
46
|
* native-host simulator is a top-level WebContentsView with no embedder
|
|
@@ -8,6 +8,7 @@ import { OPEN_IN_EDITOR_SCHEME, decodeOpenInEditorUrl, resourceUrlToProjectRelat
|
|
|
8
8
|
import { createSafeAreaController } from '../safe-area/index.js';
|
|
9
9
|
import { buildCustomizeTabsScript } from './devtools-tabs.js';
|
|
10
10
|
import { installElementsForward } from '../elements-forward/index.js';
|
|
11
|
+
import { installServiceConsoleForward } from '../service-console/index.js';
|
|
11
12
|
import * as layout from '../layout/index.js';
|
|
12
13
|
import { handleCustomApiBridgeRequest, } from '../simulator/custom-apis.js';
|
|
13
14
|
import { configureMiniappSession, miniappPartition } from './miniapp-partition.js';
|
|
@@ -72,6 +73,10 @@ export function createViewManager(ctx) {
|
|
|
72
73
|
// CDP traffic at the active render guest). Installed in
|
|
73
74
|
// `attachNativeSimulatorDevtoolsHost`, stopped on detach / host destroyed.
|
|
74
75
|
let stopElementsForward = null;
|
|
76
|
+
// Disposer for the service-layer console capture (CDP `consoleAPICalled` on the
|
|
77
|
+
// service host wc → console fan-out). Installed when the DevTools is pointed at
|
|
78
|
+
// a service host, stopped when that source is closed / swapped.
|
|
79
|
+
let stopServiceConsole = null;
|
|
75
80
|
let nativeDevtoolsRetryTimer = null;
|
|
76
81
|
let nativeDevtoolsRetryToken = 0;
|
|
77
82
|
// Renderer-driven overlay bounds for the simulator DevTools view — the SOLE
|
|
@@ -152,6 +157,9 @@ export function createViewManager(ctx) {
|
|
|
152
157
|
if (!simulatorViewAdded) {
|
|
153
158
|
ctx.windows.mainWindow.contentView.addChildView(simulatorView);
|
|
154
159
|
simulatorViewAdded = true;
|
|
160
|
+
// Re-attaching this base overlay moved it to the top of the z-stack; keep
|
|
161
|
+
// any open settings/popover above it.
|
|
162
|
+
raiseTopOverlays();
|
|
155
163
|
}
|
|
156
164
|
simulatorView.setBounds(bounds);
|
|
157
165
|
}
|
|
@@ -386,6 +394,11 @@ export function createViewManager(ctx) {
|
|
|
386
394
|
function closeNativeDevtoolsSource() {
|
|
387
395
|
const source = nativeDevtoolsSourceWc;
|
|
388
396
|
nativeDevtoolsSourceWc = null;
|
|
397
|
+
try {
|
|
398
|
+
stopServiceConsole?.();
|
|
399
|
+
}
|
|
400
|
+
catch { /* already stopped */ }
|
|
401
|
+
stopServiceConsole = null;
|
|
389
402
|
if (!source || source.isDestroyed())
|
|
390
403
|
return;
|
|
391
404
|
try {
|
|
@@ -557,6 +570,19 @@ export function createViewManager(ctx) {
|
|
|
557
570
|
// Re-applied on every re-point so a service-host pool swap (fresh
|
|
558
571
|
// openDevTools) re-asserts the custom tab bar.
|
|
559
572
|
customizeDevtoolsTabs(simulatorView.webContents);
|
|
573
|
+
// Capture service-layer console via CDP (NOT a preload monkeypatch, which
|
|
574
|
+
// would clobber native source attribution) and feed it to the console
|
|
575
|
+
// fan-out (automation `App.logAdded`). Bound to THIS service wc; replaced
|
|
576
|
+
// on the next re-point via closeNativeDevtoolsSource.
|
|
577
|
+
try {
|
|
578
|
+
stopServiceConsole?.();
|
|
579
|
+
}
|
|
580
|
+
catch { /* already stopped */ }
|
|
581
|
+
stopServiceConsole = installServiceConsoleForward({
|
|
582
|
+
serviceWc: next,
|
|
583
|
+
connections: ctx.connections,
|
|
584
|
+
emit: (entry) => ctx.consoleForwarder?.emit(entry),
|
|
585
|
+
}).stop;
|
|
560
586
|
return true;
|
|
561
587
|
}
|
|
562
588
|
catch {
|
|
@@ -1038,6 +1064,11 @@ export function createViewManager(ctx) {
|
|
|
1038
1064
|
// Overlay loads mainPreloadPath, so the same navigation rules as the
|
|
1039
1065
|
// main window apply — see navigation-hardening.ts.
|
|
1040
1066
|
applyNavigationHardening(settingsView.webContents, ctx.rendererDir);
|
|
1067
|
+
// Transparent backing: the settings view now spans the whole content area
|
|
1068
|
+
// (computeSettingsBounds) and its renderer paints a transparent backdrop +
|
|
1069
|
+
// an opaque right-side panel, so the underlying editor/simulator show
|
|
1070
|
+
// through and a backdrop click closes the overlay (mirrors the popover).
|
|
1071
|
+
settingsView.setBackgroundColor('#00000000');
|
|
1041
1072
|
await settingsView.webContents.loadFile(path.join(ctx.rendererDir, 'entries/settings/index.html'));
|
|
1042
1073
|
}
|
|
1043
1074
|
if (!settingsViewAdded) {
|
|
@@ -1055,6 +1086,25 @@ export function createViewManager(ctx) {
|
|
|
1055
1086
|
settingsViewAdded = false;
|
|
1056
1087
|
}
|
|
1057
1088
|
}
|
|
1089
|
+
// Keep the TOP-tier overlays (settings, popover) above the BASE-tier native
|
|
1090
|
+
// overlays (the native simulator WCV + the console/DevTools WCV). Native
|
|
1091
|
+
// overlays are z-ordered by `addChildView` insertion order — the last-added
|
|
1092
|
+
// sits on top — so RE-attaching a base overlay while a top overlay is open
|
|
1093
|
+
// (e.g. the simulator re-shows on a tab switch, or the console bounds
|
|
1094
|
+
// republish re-adds it) would move the base ABOVE the open settings/popover
|
|
1095
|
+
// and occlude it. Whenever a base overlay is (re)added, re-append the open top
|
|
1096
|
+
// overlays so they stay on top. Settings is re-appended before popover so a
|
|
1097
|
+
// simultaneously-open popover ends up topmost. A no-op when neither is open
|
|
1098
|
+
// (pinned by the z-order guard test).
|
|
1099
|
+
function raiseTopOverlays() {
|
|
1100
|
+
if (ctx.windows.mainWindow.isDestroyed())
|
|
1101
|
+
return;
|
|
1102
|
+
const cv = ctx.windows.mainWindow.contentView;
|
|
1103
|
+
if (settingsView && settingsViewAdded)
|
|
1104
|
+
cv.addChildView(settingsView);
|
|
1105
|
+
if (popoverView)
|
|
1106
|
+
cv.addChildView(popoverView);
|
|
1107
|
+
}
|
|
1058
1108
|
function showPopover(data) {
|
|
1059
1109
|
hidePopover();
|
|
1060
1110
|
const popover = new WebContentsView({
|
|
@@ -1141,6 +1191,9 @@ export function createViewManager(ctx) {
|
|
|
1141
1191
|
if (!nativeSimulatorViewAdded) {
|
|
1142
1192
|
ctx.windows.mainWindow.contentView.addChildView(nativeSimulatorView);
|
|
1143
1193
|
nativeSimulatorViewAdded = true;
|
|
1194
|
+
// Re-attaching this base overlay moved it to the top of the z-stack; keep
|
|
1195
|
+
// any open settings/popover above it.
|
|
1196
|
+
raiseTopOverlays();
|
|
1144
1197
|
}
|
|
1145
1198
|
nativeSimulatorView.setBounds(p.bounds);
|
|
1146
1199
|
const simWc = nativeSimulatorView.webContents;
|
|
@@ -137,12 +137,13 @@ export interface WorkbenchContext {
|
|
|
137
137
|
*/
|
|
138
138
|
storageApi?: StorageApi;
|
|
139
139
|
/**
|
|
140
|
-
* Native-host console sink.
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
140
|
+
* Native-host console sink. Render-layer entries arrive from
|
|
141
|
+
* render-host/preload.cjs (`console.*` monkeypatch → `consoleLog` message);
|
|
142
|
+
* service-layer entries arrive from CDP `Runtime.consoleAPICalled`
|
|
143
|
+
* (services/service-console) so native source attribution is preserved.
|
|
144
|
+
* Owned by the `ConsoleForwarder` (set in `installBridgeRouter`), whose `emit`
|
|
145
|
+
* fans every entry out to subscribers (automation WS) AND mirrors render-layer
|
|
146
|
+
* entries into the service host's own console for the embedded DevTools.
|
|
146
147
|
*/
|
|
147
148
|
guestConsole?: {
|
|
148
149
|
emit(entry: unknown): void;
|