@dimina-kit/devtools 0.4.0-dev.20260612025610 → 0.4.0-dev.20260615070430
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 +86 -4
- package/dist/main/api.d.ts +1 -1
- package/dist/main/app/app.d.ts +4 -0
- package/dist/main/app/app.js +6 -0
- package/dist/main/index.bundle.js +147 -14
- package/dist/main/ipc/app.d.ts +3 -1
- package/dist/main/ipc/app.js +80 -0
- package/dist/main/ipc/bridge-router.js +35 -8
- package/dist/main/ipc/views.js +10 -1
- package/dist/main/services/notifications/renderer-notifier.d.ts +15 -0
- package/dist/main/services/notifications/renderer-notifier.js +10 -1
- package/dist/main/services/views/view-manager.d.ts +16 -4
- package/dist/main/services/views/view-manager.js +32 -8
- package/dist/main/services/workbench-context.d.ts +13 -1
- package/dist/main/services/workbench-context.js +4 -0
- package/dist/main/services/workspace/workspace-service.js +39 -0
- package/dist/preload/index.d.ts +2 -2
- package/dist/preload/index.js +2 -2
- package/dist/preload/windows/host-toolbar-runtime.cjs +11 -1
- package/dist/preload/windows/host-toolbar-runtime.cjs.map +2 -2
- package/dist/preload/windows/main.cjs +9 -7
- package/dist/preload/windows/main.cjs.map +2 -2
- package/dist/preload/windows/simulator.cjs.map +1 -1
- package/dist/renderer/assets/index-Bx4XqFQ2.js +50 -0
- package/dist/renderer/assets/{input-6fpCXV-n.js → input-J1MQ5fra.js} +2 -2
- package/dist/renderer/assets/{ipc-transport-D22e4dv7.js → ipc-transport-21qDpt9Y.js} +2 -2
- package/dist/renderer/assets/ipc-transport-CVp38I_M.css +1 -0
- package/dist/renderer/assets/{popover-CdGdKkkc.js → popover-ChIUUEdj.js} +2 -2
- package/dist/renderer/assets/{select-X0sbjwdP.js → select-BgWAbMot.js} +2 -2
- package/dist/renderer/assets/{settings-CS4jYaCw.js → settings-CGea5k_R.js} +2 -2
- package/dist/renderer/assets/{settings-api-CCWTAao_.js → settings-api-Dit4oL6S.js} +2 -2
- package/dist/renderer/assets/{workbenchSettings-Bpzaj3bm.js → workbenchSettings-CFrdVOiP.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/ipc-channels.d.ts +17 -0
- package/dist/shared/ipc-channels.js +20 -0
- package/dist/shared/types.d.ts +61 -0
- package/dist/simulator/assets/bridge-channels-BUQ5AbvJ.js +2 -0
- package/dist/simulator/assets/device-shell-BEnc2k-W.js +2 -0
- package/dist/simulator/assets/{simulator-DHylZf9Z.js → simulator-cV1PERi6.js} +3 -3
- package/dist/simulator/assets/simulator-mini-app-CYtW6bMd.js +2 -0
- package/dist/simulator/simulator.html +1 -1
- package/package.json +4 -4
- package/dist/renderer/assets/index-DLzjz1wv.js +0 -50
- package/dist/renderer/assets/ipc-transport-Bs8Sf1B2.css +0 -1
- package/dist/simulator/assets/device-shell-fH2cI-3q.js +0 -2
- package/dist/simulator/assets/simulator-mini-app-BDNu5n26.js +0 -2
|
@@ -58,17 +58,26 @@ const API_CALL_TIMEOUT_MS = 5_000;
|
|
|
58
58
|
* rather than assuming a single session.
|
|
59
59
|
*/
|
|
60
60
|
function resolveCurrentApp(state, ctx, appId) {
|
|
61
|
+
// Same-appId matches prefer the MOST RECENT spawn (Maps preserve insertion
|
|
62
|
+
// order): after a respawn/reopen the newest session is the live one — the
|
|
63
|
+
// first match could be a just-superseded session mid-teardown.
|
|
61
64
|
if (appId) {
|
|
65
|
+
let match;
|
|
62
66
|
for (const ap of state.appSessions.values())
|
|
63
67
|
if (ap.appId === appId)
|
|
64
|
-
|
|
68
|
+
match = ap;
|
|
69
|
+
if (match)
|
|
70
|
+
return match;
|
|
65
71
|
}
|
|
66
72
|
const appInfo = ctx.workspace?.getSession?.()?.appInfo;
|
|
67
73
|
const activeAppId = appInfo?.appId;
|
|
68
74
|
if (activeAppId) {
|
|
75
|
+
let match;
|
|
69
76
|
for (const ap of state.appSessions.values())
|
|
70
77
|
if (ap.appId === activeAppId)
|
|
71
|
-
|
|
78
|
+
match = ap;
|
|
79
|
+
if (match)
|
|
80
|
+
return match;
|
|
72
81
|
}
|
|
73
82
|
// Maps preserve insertion order; the last entry is the most recent spawn.
|
|
74
83
|
let last;
|
|
@@ -87,6 +96,12 @@ export function installBridgeRouter(ctx) {
|
|
|
87
96
|
emitRenderEvent: () => { },
|
|
88
97
|
connections: ctx.connections,
|
|
89
98
|
debugTap: createDebugTap({ enabled: resolveDebugTapEnabled() }),
|
|
99
|
+
evictAppDataBridges: (ap) => {
|
|
100
|
+
if (!ctx.appData)
|
|
101
|
+
return;
|
|
102
|
+
for (const page of ap.pages.values())
|
|
103
|
+
ctx.appData.evictBridge(ap.appId, page.bridgeId);
|
|
104
|
+
},
|
|
90
105
|
};
|
|
91
106
|
// Opt-in (default OFF) pre-warm pool for service-host windows. When enabled,
|
|
92
107
|
// handleSpawn acquires a warm window instead of constructing one per spawn.
|
|
@@ -298,12 +313,8 @@ export function installBridgeRouter(ctx) {
|
|
|
298
313
|
console.warn(`[bridge-router] DISPOSE rejected: sender belongs to ${senderApp.appSessionId}, target ${target.appSessionId}`);
|
|
299
314
|
return;
|
|
300
315
|
}
|
|
301
|
-
//
|
|
302
|
-
//
|
|
303
|
-
if (ctx.appData) {
|
|
304
|
-
for (const page of target.pages.values())
|
|
305
|
-
ctx.appData.evictBridge(target.appId, page.bridgeId);
|
|
306
|
-
}
|
|
316
|
+
// AppData bridge eviction happens inside disposeAppSession (single
|
|
317
|
+
// chokepoint shared with the simulator-WCV 'destroyed' path).
|
|
307
318
|
void disposeAppSession(state, target.appSessionId);
|
|
308
319
|
};
|
|
309
320
|
ipcMain.on(C.DISPOSE, onDispose);
|
|
@@ -517,6 +528,19 @@ async function handleSpawn(state, ctx, event, opts) {
|
|
|
517
528
|
};
|
|
518
529
|
appSession.onServiceClosed = onServiceClosed;
|
|
519
530
|
serviceWindow.once('closed', onServiceClosed);
|
|
531
|
+
// The simulator WCV owns the app's UI lifetime. When it is destroyed —
|
|
532
|
+
// project close (`views.disposeAll`/detach) or a DeviceShell respawn
|
|
533
|
+
// (`attachNativeSimulator`, e.g. watcher hot reload) — the guest never gets
|
|
534
|
+
// to send its graceful `C.DISPOSE`, so without this hook the app session and
|
|
535
|
+
// its hidden service-host window leak, and `resolveCurrentApp` keeps
|
|
536
|
+
// resolving the STALE session for the same appId: `getActiveRenderWc` then
|
|
537
|
+
// dereferences dead pages and every panel pull (WXML/elements) returns null
|
|
538
|
+
// for the whole next session. Disposing here is idempotent with the graceful
|
|
539
|
+
// path (`disposeAppSession` early-returns once the session is gone).
|
|
540
|
+
const onSimulatorDestroyed = () => {
|
|
541
|
+
void disposeAppSession(state, appSessionId);
|
|
542
|
+
};
|
|
543
|
+
simulatorWc.once('destroyed', onSimulatorDestroyed);
|
|
520
544
|
if (usedPool) {
|
|
521
545
|
// A pooled/fallback window passes through about:blank (warm load or the
|
|
522
546
|
// fallback's initial load). Boot only once the REAL service.html navigation
|
|
@@ -1144,6 +1168,9 @@ async function disposeAppSession(state, appSessionId, opts = {}) {
|
|
|
1144
1168
|
if (!ap)
|
|
1145
1169
|
return;
|
|
1146
1170
|
state.appSessions.delete(appSessionId);
|
|
1171
|
+
// Evict AppData bridges FIRST — eviction enumerates `ap.pages`, which the
|
|
1172
|
+
// page teardown below progressively empties (and finally clears).
|
|
1173
|
+
state.evictAppDataBridges(ap);
|
|
1147
1174
|
// Drain any pending API calls owned by this app session. One-shot calls
|
|
1148
1175
|
// normally self-clean on response/timeout, but persistent (`keep: true`)
|
|
1149
1176
|
// subscriptions (e.g. audioListen) live with their timer cleared until
|
package/dist/main/ipc/views.js
CHANGED
|
@@ -30,7 +30,16 @@ export function registerViewsIpc(ctx) {
|
|
|
30
30
|
.handle(ViewChannel.HostToolbarBounds, (_event, ...args) => {
|
|
31
31
|
const [bounds] = validate(ViewChannel.HostToolbarBounds, ViewBoundsSchema, args);
|
|
32
32
|
ctx.views.setHostToolbarBounds(bounds);
|
|
33
|
-
})
|
|
33
|
+
})
|
|
34
|
+
// Height replay pull: a freshly-mounted main-renderer placeholder asks for
|
|
35
|
+
// the last NOTIFIED toolbar height (main retains it — the toolbar's
|
|
36
|
+
// size-advertiser deduplicates and never re-pushes, so a push that fired
|
|
37
|
+
// while no project view was mounted is otherwise lost: cold start races
|
|
38
|
+
// it, close-project → reopen hits it always). Rides the SAME
|
|
39
|
+
// senderPolicy-gated registry as HostToolbarBounds: the toolbar WCV's
|
|
40
|
+
// arbitrary host content must not reach this — only the trusted main
|
|
41
|
+
// renderer pulls. Live delegation, not a registration-time snapshot.
|
|
42
|
+
.handle(ViewChannel.HostToolbarGetHeight, () => ctx.views.getHostToolbarHeight());
|
|
34
43
|
// Reverse size-advertiser: the toolbar WCV's OWN renderer sends this, and the
|
|
35
44
|
// host loads ARBITRARY content into that WCV. We DELIBERATELY do NOT add the
|
|
36
45
|
// toolbar wc to the global sender policy — that would trust it for ALL ~72
|
|
@@ -12,6 +12,15 @@ export interface ProjectStatusPayload {
|
|
|
12
12
|
/** True when the status update is emitted by the file-watcher rebuild loop. */
|
|
13
13
|
hotReload?: boolean;
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Payload for the `project:compileLog` push — one filtered dmcc log line.
|
|
17
|
+
* `at` is stamped in the main process when the line is captured.
|
|
18
|
+
*/
|
|
19
|
+
export interface CompileLogPayload {
|
|
20
|
+
at: number;
|
|
21
|
+
stream: 'stdout' | 'stderr';
|
|
22
|
+
text: string;
|
|
23
|
+
}
|
|
15
24
|
/**
|
|
16
25
|
* Payload for the `settings:init` event sent into the embedded settings overlay
|
|
17
26
|
* right after it is shown.
|
|
@@ -43,6 +52,8 @@ export interface WorkbenchSettingsInitPayload {
|
|
|
43
52
|
export interface RendererNotifier {
|
|
44
53
|
/** Broadcast project compile status transitions to the main renderer. */
|
|
45
54
|
projectStatus(payload: ProjectStatusPayload): void;
|
|
55
|
+
/** Push one per-line dmcc compile-log entry to the main renderer. */
|
|
56
|
+
compileLog(payload: CompileLogPayload): void;
|
|
46
57
|
/** Ask the main renderer to navigate back to its landing screen. */
|
|
47
58
|
windowNavigateBack(): void;
|
|
48
59
|
/** Tell the main renderer the compile popover has been closed. */
|
|
@@ -54,6 +65,10 @@ export interface RendererNotifier {
|
|
|
54
65
|
* placeholder div resizes (closes the host-toolbar dynamic-height loop).
|
|
55
66
|
*/
|
|
56
67
|
hostToolbarHeightChanged(height: number): void;
|
|
68
|
+
/** Ask the main renderer to re-fetch the host-provided header avatar DTO. */
|
|
69
|
+
headerAvatarChanged(): void;
|
|
70
|
+
/** Ask the main renderer to re-fetch the host-provided header actions. */
|
|
71
|
+
headerActionsChanged(): void;
|
|
57
72
|
/**
|
|
58
73
|
* Ask the main renderer's Monaco editor to open a project file at a position.
|
|
59
74
|
* Drives the "click a console file link → open in editor" pipeline.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ProjectChannel, WindowChannel, SettingsChannel, PopoverChannel, WorkbenchSettingsChannel, EditorChannel, ViewChannel, } from '../../../shared/ipc-channels.js';
|
|
1
|
+
import { ProjectChannel, WindowChannel, SettingsChannel, PopoverChannel, WorkbenchSettingsChannel, EditorChannel, ViewChannel, AppChannel, } from '../../../shared/ipc-channels.js';
|
|
2
2
|
/** Safely resolve a WebContents, skipping destroyed / missing targets. */
|
|
3
3
|
function liveWebContents(wc) {
|
|
4
4
|
if (!wc)
|
|
@@ -24,6 +24,9 @@ export function createRendererNotifier(ctx) {
|
|
|
24
24
|
projectStatus(payload) {
|
|
25
25
|
sendToMain(ProjectChannel.Status, payload);
|
|
26
26
|
},
|
|
27
|
+
compileLog(payload) {
|
|
28
|
+
sendToMain(ProjectChannel.CompileLog, payload);
|
|
29
|
+
},
|
|
27
30
|
windowNavigateBack() {
|
|
28
31
|
sendToMain(WindowChannel.NavigateBack);
|
|
29
32
|
},
|
|
@@ -36,6 +39,12 @@ export function createRendererNotifier(ctx) {
|
|
|
36
39
|
hostToolbarHeightChanged(height) {
|
|
37
40
|
sendToMain(ViewChannel.HostToolbarHeightChanged, height);
|
|
38
41
|
},
|
|
42
|
+
headerAvatarChanged() {
|
|
43
|
+
sendToMain(AppChannel.HeaderAvatarChanged);
|
|
44
|
+
},
|
|
45
|
+
headerActionsChanged() {
|
|
46
|
+
sendToMain(AppChannel.HeaderActionsChanged);
|
|
47
|
+
},
|
|
39
48
|
editorOpenFile(payload) {
|
|
40
49
|
sendToMain(EditorChannel.OpenFile, payload);
|
|
41
50
|
},
|
|
@@ -112,6 +112,16 @@ export interface ViewManager {
|
|
|
112
112
|
* must trust its id — see `createWorkbenchSenderPolicy`.
|
|
113
113
|
*/
|
|
114
114
|
getHostToolbarWebContentsId(): number | null;
|
|
115
|
+
/**
|
|
116
|
+
* Return the last host-toolbar height NOTIFIED to the main-window renderer
|
|
117
|
+
* (an advertiser report in `'auto'` mode, a `setHeightMode({ fixed })` pin,
|
|
118
|
+
* or 0 after `hostToolbar.hide()`); 0 before any notify. The renderer pulls
|
|
119
|
+
* this on project-view mount to REPLAY a height whose push it missed — the
|
|
120
|
+
* toolbar's size-advertiser deduplicates and never re-reports, so a notify
|
|
121
|
+
* fired while no project view is mounted (cold start on the project list;
|
|
122
|
+
* always on close-project → reopen) would otherwise be lost forever.
|
|
123
|
+
*/
|
|
124
|
+
getHostToolbarHeight(): number;
|
|
115
125
|
/**
|
|
116
126
|
* NATIVE-HOST ONLY. Position the simulator content WebContentsView over the
|
|
117
127
|
* renderer-measured simulator panel REGION rect (the flex:1 placeholder slot,
|
|
@@ -168,10 +178,12 @@ export interface ViewManager {
|
|
|
168
178
|
}): void;
|
|
169
179
|
/**
|
|
170
180
|
* Reverse size-advertiser sink: the toolbar WCV's own renderer advertises
|
|
171
|
-
* its intrinsic content height (block-axis extent); we
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
181
|
+
* its intrinsic content height (block-axis extent); we retain it as the
|
|
182
|
+
* last-notified height (`getHostToolbarHeight`) and push it to the
|
|
183
|
+
* main-window renderer so the placeholder div resizes (closing the
|
|
184
|
+
* dynamic-height loop). Ignored ENTIRELY while a `{ fixed }` height mode is
|
|
185
|
+
* pinned via `hostToolbar.setHeightMode` — dropped reports neither notify
|
|
186
|
+
* nor touch the retained value (the session-resident advertiser always
|
|
175
187
|
* runs, so its reports must not fight a host-pinned height).
|
|
176
188
|
*/
|
|
177
189
|
setHostToolbarHeight(extent: number): void;
|
|
@@ -95,6 +95,13 @@ export function createViewManager(ctx) {
|
|
|
95
95
|
// Placeholder height authority: 'auto' = advertiser reports forward to the
|
|
96
96
|
// renderer; { fixed } = host-pinned, advertiser reports are dropped.
|
|
97
97
|
let hostToolbarHeightMode = 'auto';
|
|
98
|
+
// Last toolbar height NOTIFIED to the main-window renderer — the replay
|
|
99
|
+
// source behind `getHostToolbarHeight()`. Updated ONLY inside
|
|
100
|
+
// `notifyHostToolbarHeight` so the retained value can never diverge from
|
|
101
|
+
// what the renderer was told (an advertiser report dropped by a `{ fixed }`
|
|
102
|
+
// pin must not pollute it, and a setHeightMode validation reject leaves it
|
|
103
|
+
// untouched).
|
|
104
|
+
let hostToolbarLastHeight = 0;
|
|
98
105
|
// Gated narrow channel to the toolbar PAGE (per-load MessagePort handshake;
|
|
99
106
|
// see host-toolbar-port-channel.ts). Control-level registry — created with
|
|
100
107
|
// the manager so onMessage() works before any toolbar view exists.
|
|
@@ -248,18 +255,30 @@ export function createViewManager(ctx) {
|
|
|
248
255
|
}
|
|
249
256
|
view.setBounds(bounds);
|
|
250
257
|
}
|
|
258
|
+
// Single funnel for the height notify: retain-then-push, so the retained
|
|
259
|
+
// value is exactly the last value the renderer was told. Every height
|
|
260
|
+
// notify site MUST go through here — the renderer pulls the retained value
|
|
261
|
+
// on project-view mount to replay a push it missed (the toolbar's
|
|
262
|
+
// size-advertiser deduplicates and never re-reports).
|
|
263
|
+
function notifyHostToolbarHeight(height) {
|
|
264
|
+
hostToolbarLastHeight = height;
|
|
265
|
+
ctx.notify.hostToolbarHeightChanged(height);
|
|
266
|
+
}
|
|
251
267
|
function setHostToolbarHeight(extent) {
|
|
252
268
|
// While the host pins a fixed height, drop advertiser reports entirely —
|
|
253
269
|
// the session-resident advertiser is always installed, so forwarding its
|
|
254
270
|
// reports would make the strip oscillate between the pinned and measured
|
|
255
|
-
// heights on every content resize.
|
|
271
|
+
// heights on every content resize. Dropped reports must not touch the
|
|
272
|
+
// retained value either: retention records what was NOTIFIED, not what
|
|
273
|
+
// was reported.
|
|
256
274
|
if (hostToolbarHeightMode !== 'auto')
|
|
257
275
|
return;
|
|
258
276
|
// Push the reserved height back to the main-window renderer so its
|
|
259
|
-
// placeholder div resizes (closing the dynamic-height loop). The
|
|
260
|
-
//
|
|
261
|
-
//
|
|
262
|
-
|
|
277
|
+
// placeholder div resizes (closing the dynamic-height loop). The notified
|
|
278
|
+
// height IS retained in main (`getHostToolbarHeight`) so a renderer that
|
|
279
|
+
// mounts later can pull/replay it; the renderer placeholder remains the
|
|
280
|
+
// geometry authority — the forward anchor re-reports bounds from it.
|
|
281
|
+
notifyHostToolbarHeight(extent);
|
|
263
282
|
}
|
|
264
283
|
function hideHostToolbar() {
|
|
265
284
|
if (hostToolbarView && hostToolbarViewAdded && !ctx.windows.mainWindow.isDestroyed()) {
|
|
@@ -273,7 +292,9 @@ export function createViewManager(ctx) {
|
|
|
273
292
|
// non-zero reserved height and re-publishes bounds on the next window
|
|
274
293
|
// resize, silently re-adding the view we just hid (unstable hide). Zeroing
|
|
275
294
|
// the height flips the anchor to `present:false` so it stops re-publishing.
|
|
276
|
-
|
|
295
|
+
// Through the funnel so the retained value follows to 0 — a renderer
|
|
296
|
+
// mounting after the hide must replay 0, not the stale pre-hide height.
|
|
297
|
+
notifyHostToolbarHeight(0);
|
|
277
298
|
}
|
|
278
299
|
const hostToolbar = {
|
|
279
300
|
async loadURL(url) {
|
|
@@ -317,11 +338,13 @@ export function createViewManager(ctx) {
|
|
|
317
338
|
if (mode !== 'auto') {
|
|
318
339
|
// Pin immediately: a preload-less/static toolbar never advertises, so
|
|
319
340
|
// waiting for the next report would leave the strip at height 0.
|
|
320
|
-
|
|
341
|
+
notifyHostToolbarHeight(mode.fixed);
|
|
321
342
|
}
|
|
322
343
|
// Switching back to 'auto' deliberately does NOT synthesize a notify —
|
|
323
344
|
// replaying a stale cached height would flash the old size; the NEXT
|
|
324
|
-
// advertiser report drives the placeholder again.
|
|
345
|
+
// advertiser report drives the placeholder again. The RETAINED value
|
|
346
|
+
// survives the switch though: a freshly-mounting renderer still needs
|
|
347
|
+
// the pinned height until that next report lands.
|
|
325
348
|
},
|
|
326
349
|
onMessage(channel, handler) {
|
|
327
350
|
return hostToolbarPort.onMessage(channel, handler);
|
|
@@ -1146,6 +1169,7 @@ export function createViewManager(ctx) {
|
|
|
1146
1169
|
return popoverView.webContents.id;
|
|
1147
1170
|
},
|
|
1148
1171
|
getHostToolbarWebContentsId: () => liveHostToolbarWebContents()?.id ?? null,
|
|
1172
|
+
getHostToolbarHeight: () => hostToolbarLastHeight,
|
|
1149
1173
|
setNativeSimulatorViewBounds,
|
|
1150
1174
|
resize,
|
|
1151
1175
|
setSimulatorDevtoolsBounds,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BrowserWindow } from 'electron';
|
|
2
|
-
import type { CompilationAdapter, WorkbenchConfig } from '../../shared/types.js';
|
|
2
|
+
import type { CompilationAdapter, HeaderActionsProvider, HeaderAvatarProvider, WorkbenchConfig } from '../../shared/types.js';
|
|
3
3
|
import type { BridgeRouterHandle } from '../ipc/bridge-router.js';
|
|
4
4
|
import type { ConsoleForwarder } from './console-forward/index.js';
|
|
5
5
|
import type { NetworkForwarder } from './network-forward/index.js';
|
|
@@ -35,6 +35,14 @@ export interface WorkbenchContext {
|
|
|
35
35
|
}> | {
|
|
36
36
|
appName: string;
|
|
37
37
|
};
|
|
38
|
+
/** Host-injected profile provider for the built-in header avatar slot. */
|
|
39
|
+
headerAvatarProvider?: HeaderAvatarProvider;
|
|
40
|
+
/** Host-injected click handler for the built-in header avatar slot. */
|
|
41
|
+
headerAvatarActionHandler?: () => void | Promise<void>;
|
|
42
|
+
/** Host-injected compact action provider for the built-in project header. */
|
|
43
|
+
headerActionsProvider?: HeaderActionsProvider;
|
|
44
|
+
/** Host-injected click handler for the built-in project header actions. */
|
|
45
|
+
headerActionHandler?: (id: string) => void | Promise<void>;
|
|
38
46
|
/** Unified lifecycle manager for all overlay WebContentsViews */
|
|
39
47
|
views: ViewManager;
|
|
40
48
|
/** Owns BrowserWindow instances (main + settings) and their sender-id checks. */
|
|
@@ -175,6 +183,10 @@ export interface CreateContextOptions extends Pick<WorkbenchConfig, 'adapter' |
|
|
|
175
183
|
preloadPath: string;
|
|
176
184
|
rendererDir: string;
|
|
177
185
|
brandingProvider?: WorkbenchContext['brandingProvider'];
|
|
186
|
+
headerAvatarProvider?: WorkbenchContext['headerAvatarProvider'];
|
|
187
|
+
headerAvatarActionHandler?: WorkbenchContext['headerAvatarActionHandler'];
|
|
188
|
+
headerActionsProvider?: WorkbenchContext['headerActionsProvider'];
|
|
189
|
+
headerActionHandler?: WorkbenchContext['headerActionHandler'];
|
|
178
190
|
/** Host-supplied project list backend. Defaults to LocalProjectsProvider. */
|
|
179
191
|
projectsProvider?: ProjectsProvider;
|
|
180
192
|
/** Templates injected by the host; same-id overrides a built-in. */
|
|
@@ -18,6 +18,10 @@ export function createWorkbenchContext(opts) {
|
|
|
18
18
|
apiNamespaces: opts.apiNamespaces ?? [],
|
|
19
19
|
appName: opts.appName ?? 'Dimina DevTools',
|
|
20
20
|
brandingProvider: opts.brandingProvider,
|
|
21
|
+
headerAvatarProvider: opts.headerAvatarProvider,
|
|
22
|
+
headerAvatarActionHandler: opts.headerAvatarActionHandler,
|
|
23
|
+
headerActionsProvider: opts.headerActionsProvider,
|
|
24
|
+
headerActionHandler: opts.headerActionHandler,
|
|
21
25
|
};
|
|
22
26
|
ctx.registry = new DisposableRegistry();
|
|
23
27
|
// Empty connection registry as a first-class context field. Connections are
|
|
@@ -18,6 +18,16 @@ export function createWorkspaceService(ctx) {
|
|
|
18
18
|
// accept an in-flight write that targets the just-closed project. Reset at
|
|
19
19
|
// the start of every `openProject` so it never accumulates a stale root.
|
|
20
20
|
let lastClosedProjectPath = '';
|
|
21
|
+
// Session generation for the onLog closure handed to the adapter: a closed
|
|
22
|
+
// (or switched-away-from) session's compile worker dies asynchronously, so
|
|
23
|
+
// buffered log lines can still arrive through the OLD closure after
|
|
24
|
+
// closeProject/openProject. Each openProject claims a new generation and
|
|
25
|
+
// closeProject invalidates the current one; a stale closure sees the
|
|
26
|
+
// mismatch and drops the line instead of polluting the next project's
|
|
27
|
+
// compile panel. (Deliberately NOT a `currentSession !== null` check: the
|
|
28
|
+
// first compile's log lines arrive while the adapter promise is still
|
|
29
|
+
// pending, before currentSession is assigned.)
|
|
30
|
+
let logGeneration = 0;
|
|
21
31
|
function sendStatus(status, message, hotReload) {
|
|
22
32
|
ctx.notify.projectStatus(hotReload ? { status, message, hotReload: true } : { status, message });
|
|
23
33
|
}
|
|
@@ -71,6 +81,14 @@ export function createWorkspaceService(ctx) {
|
|
|
71
81
|
: null,
|
|
72
82
|
async openProject(projectPath) {
|
|
73
83
|
clearSimulatorServicewechatReferer();
|
|
84
|
+
// Invalidate the outgoing session's onLog BEFORE teardown starts (same
|
|
85
|
+
// order as closeProject below): the dying compile worker flushes
|
|
86
|
+
// buffered lines DURING disposeSession(), and with the old generation
|
|
87
|
+
// still current they would pass the staleness guard and pollute the
|
|
88
|
+
// incoming project's compile-log timeline. The new session claims its
|
|
89
|
+
// own fresh generation further down (after dispose), so its onLog
|
|
90
|
+
// forwards normally.
|
|
91
|
+
logGeneration++;
|
|
74
92
|
await disposeSession();
|
|
75
93
|
currentProjectPath = '';
|
|
76
94
|
// A fresh open starts a clean sandbox window — drop any previously
|
|
@@ -92,6 +110,9 @@ export function createWorkspaceService(ctx) {
|
|
|
92
110
|
}
|
|
93
111
|
sendStatus('compiling', '正在编译...');
|
|
94
112
|
const { compile } = loadWorkbenchSettings();
|
|
113
|
+
// This open owns the log channel until the next open/close bumps the
|
|
114
|
+
// generation — the onLog closure below checks it per line.
|
|
115
|
+
const sessionGeneration = ++logGeneration;
|
|
95
116
|
let session;
|
|
96
117
|
try {
|
|
97
118
|
session = await ctx.adapter.openProject({
|
|
@@ -100,6 +121,21 @@ export function createWorkspaceService(ctx) {
|
|
|
100
121
|
watch: compile.watch,
|
|
101
122
|
onRebuild: () => sendStatus('ready', '编译完成,已热更新', true),
|
|
102
123
|
onBuildError: (err) => sendStatus('error', String(err)),
|
|
124
|
+
// Per-line dmcc log (already filtered in devkit). Stamp the
|
|
125
|
+
// wall-clock capture time here and push verbatim on the dedicated
|
|
126
|
+
// compile-log channel — never through projectStatus, whose
|
|
127
|
+
// one-event-per-payload contract feeds compileEvents. Stale lines
|
|
128
|
+
// (a closed/replaced session's worker flushing its buffers) are
|
|
129
|
+
// dropped via the generation check.
|
|
130
|
+
onLog: (entry) => {
|
|
131
|
+
if (sessionGeneration !== logGeneration)
|
|
132
|
+
return;
|
|
133
|
+
ctx.notify.compileLog({
|
|
134
|
+
stream: entry.stream,
|
|
135
|
+
text: entry.text,
|
|
136
|
+
at: Date.now(),
|
|
137
|
+
});
|
|
138
|
+
},
|
|
103
139
|
});
|
|
104
140
|
}
|
|
105
141
|
catch (err) {
|
|
@@ -141,6 +177,9 @@ export function createWorkspaceService(ctx) {
|
|
|
141
177
|
};
|
|
142
178
|
},
|
|
143
179
|
async closeProject() {
|
|
180
|
+
// Invalidate the active session's onLog BEFORE teardown starts — lines
|
|
181
|
+
// flushed by the dying compile worker must not reach the panel.
|
|
182
|
+
logGeneration++;
|
|
144
183
|
clearSimulatorServicewechatReferer();
|
|
145
184
|
await disposeSession();
|
|
146
185
|
// Record the root being torn down BEFORE clearing it, so a teardown/
|
package/dist/preload/index.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export { installConsoleInstrumentation } from './instrumentation/console.js';
|
|
|
13
13
|
export { createAppDataSource } from './instrumentation/app-data.js';
|
|
14
14
|
export type { AppDataSnapshot } from './instrumentation/app-data.js';
|
|
15
15
|
/**
|
|
16
|
-
* @deprecated Will be removed in
|
|
16
|
+
* @deprecated Will be removed in 0.5.0. Under the native-host (sole)
|
|
17
17
|
* runtime the simulator top frame is a top-level WebContentsView — the page DOM
|
|
18
18
|
* lives in child render-host `<webview>` guests, so this top-frame DOM observer
|
|
19
19
|
* only ever publishes `null`. Panel WXML flows over the main-process
|
|
@@ -21,7 +21,7 @@ export type { AppDataSnapshot } from './instrumentation/app-data.js';
|
|
|
21
21
|
*/
|
|
22
22
|
export { createWxmlSource } from './instrumentation/wxml.js';
|
|
23
23
|
/**
|
|
24
|
-
* @deprecated Will be removed in
|
|
24
|
+
* @deprecated Will be removed in 0.5.0. The host's
|
|
25
25
|
* `miniapp-snapshot:push/pull` IPC has no receiving end under the native-host
|
|
26
26
|
* (sole) runtime: the simulator is a top-level WebContentsView without an
|
|
27
27
|
* embedder (its `sendToHost` fires into the void) and the renderer-side puller
|
package/dist/preload/index.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
export { installConsoleInstrumentation } from './instrumentation/console.js';
|
|
13
13
|
export { createAppDataSource } from './instrumentation/app-data.js';
|
|
14
14
|
/**
|
|
15
|
-
* @deprecated Will be removed in
|
|
15
|
+
* @deprecated Will be removed in 0.5.0. Under the native-host (sole)
|
|
16
16
|
* runtime the simulator top frame is a top-level WebContentsView — the page DOM
|
|
17
17
|
* lives in child render-host `<webview>` guests, so this top-frame DOM observer
|
|
18
18
|
* only ever publishes `null`. Panel WXML flows over the main-process
|
|
@@ -20,7 +20,7 @@ export { createAppDataSource } from './instrumentation/app-data.js';
|
|
|
20
20
|
*/
|
|
21
21
|
export { createWxmlSource } from './instrumentation/wxml.js';
|
|
22
22
|
/**
|
|
23
|
-
* @deprecated Will be removed in
|
|
23
|
+
* @deprecated Will be removed in 0.5.0. The host's
|
|
24
24
|
* `miniapp-snapshot:push/pull` IPC has no receiving end under the native-host
|
|
25
25
|
* (sole) runtime: the simulator is a top-level WebContentsView without an
|
|
26
26
|
* embedder (its `sendToHost` fires into the void) and the renderer-side puller
|
|
@@ -134,7 +134,17 @@ var ViewChannel = {
|
|
|
134
134
|
* main → main-window renderer: push the reserved host-toolbar height so the
|
|
135
135
|
* renderer placeholder div resizes (closing the dynamic-height loop).
|
|
136
136
|
*/
|
|
137
|
-
HostToolbarHeightChanged: "view:host-toolbar:height-changed"
|
|
137
|
+
HostToolbarHeightChanged: "view:host-toolbar:height-changed",
|
|
138
|
+
/**
|
|
139
|
+
* main ← main-window renderer (invoke): pull the last NOTIFIED toolbar
|
|
140
|
+
* height retained in main. Mount-time replay companion to
|
|
141
|
+
* `HostToolbarHeightChanged`: the push listener mounts with the project
|
|
142
|
+
* view and the toolbar's size-advertiser deduplicates (never re-reports),
|
|
143
|
+
* so a height pushed while no project view was mounted would otherwise be
|
|
144
|
+
* lost forever (cold start on the project list races it; close-project →
|
|
145
|
+
* reopen hits it deterministically). No payload; resolves a number.
|
|
146
|
+
*/
|
|
147
|
+
HostToolbarGetHeight: "view:host-toolbar:get-height"
|
|
138
148
|
};
|
|
139
149
|
|
|
140
150
|
// src/preload/runtime/host-toolbar-advertiser.ts
|