@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
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Dimina DevTools
|
|
2
2
|
|
|
3
|
-
基于 Electron 的小程序开发者工具。提供模拟器、Chrome DevTools 面板、WXML/AppData/Storage 面板、编译配置等功能。
|
|
3
|
+
基于 Electron 的小程序开发者工具。提供模拟器、Chrome DevTools 面板、WXML/AppData/Storage/编译 面板、编译配置等功能。
|
|
4
4
|
|
|
5
5
|
下游 host 通过 `launch(config)` 集成并定制 devtools(零配置直接 `launch()`,配置驱动 `launch({...})`;见下方「两种用法」)。两种用法都经领域中立的 [`@dimina-kit/electron-deck`](../electron-deck) 框架编排——框架接管 Electron 进程生命周期(whenReady / will-quit)、wire/trust 原语,devtools 作为 `RuntimeBackend` 注入完整运行时(见 [`framework-extraction-v2.md`](../electron-deck/docs/framework-extraction-v2.md))。
|
|
6
6
|
|
|
@@ -42,6 +42,12 @@ launch({
|
|
|
42
42
|
rendererDir,
|
|
43
43
|
apiNamespaces: ['my'],
|
|
44
44
|
brandingProvider: () => ({ appName: '我的开发工具' }),
|
|
45
|
+
headerAvatarProvider: () => {
|
|
46
|
+
const user = getCurrentUser()
|
|
47
|
+
return user
|
|
48
|
+
? { displayName: user.name, displayInitial: user.name, avatarUrl: user.avatarUrl }
|
|
49
|
+
: null
|
|
50
|
+
},
|
|
45
51
|
icon: '/path/to/icon.png',
|
|
46
52
|
menuBuilder: (mainWindow, menuCtx) => {
|
|
47
53
|
// menuCtx 是 MenuContext(只读 menu 相关状态)
|
|
@@ -147,7 +153,7 @@ src/
|
|
|
147
153
|
main.tsx # 主窗口 React 根
|
|
148
154
|
features/ # 主窗口内的业务区域
|
|
149
155
|
project-runtime/ # 项目视图 + 工具栏 + 右侧面板切换
|
|
150
|
-
right-panel/ # WXML / AppData / Storage 面板
|
|
156
|
+
right-panel/ # WXML / AppData / Storage / 编译 面板
|
|
151
157
|
popover/ settings/ workbench-settings/
|
|
152
158
|
shared/
|
|
153
159
|
components/ # UI 组件(ui / layout / json-viewer / ...)
|
|
@@ -177,10 +183,14 @@ src/
|
|
|
177
183
|
| ------------------ | -------------------- | ------------------- | ---------------------------------- |
|
|
178
184
|
| `appName` | `string` | `'Dimina DevTools'` | 窗口标题 |
|
|
179
185
|
| `adapter` | `CompilationAdapter` | 内置 | 项目编译适配器 |
|
|
180
|
-
| `panels` | `BuiltinPanelId[]` | — |
|
|
186
|
+
| `panels` | `BuiltinPanelId[]` | — | **已废弃,运行时忽略**:界面恒显示全部内置面板(WXML / AppData / Storage / Console / 编译);保留字段仅为兼容仍传它的 host |
|
|
181
187
|
| `preloadPath` | `string` | 内置 | 统一的 host 级 preload 入口;native-host simulator(WCV)自动跑其 `.cjs` sibling(`cjsSiblingPreloadPath`) |
|
|
182
188
|
| `apiNamespaces` | `string[]` | `[]` | 自定义 API 命名空间(如 `['qd']`) |
|
|
183
189
|
| `brandingProvider` | `() => { appName }` | — | 品牌信息 provider |
|
|
190
|
+
| `headerAvatarProvider` | `() => HeaderAvatarInfo \| null \| Promise<HeaderAvatarInfo \| null>` | — | 内置 header 头像 slot 的宿主数据源;返回 `null` / `undefined` 时隐藏 |
|
|
191
|
+
| `headerAvatarActionHandler` | `() => void \| Promise<void>` | — | 内置 header 头像 slot 的点击处理;未提供时点击无业务副作用 |
|
|
192
|
+
| `headerActionsProvider` | `() => HeaderActionInfo[] \| null \| Promise<HeaderActionInfo[] \| null>` | — | 内置 header 三栏里的少量宿主命令按钮 |
|
|
193
|
+
| `headerActionHandler` | `(id: string) => void \| Promise<void>` | — | 处理 `headerActionsProvider` 返回按钮的点击 |
|
|
184
194
|
| `headerHeight` | `number` | — | **已废弃,运行时忽略**:头部栏恒为 40px(`HEADER_H`);需要自定义工具栏请用 host toolbar WCV |
|
|
185
195
|
|
|
186
196
|
### WorkbenchAppConfig(扩展 WorkbenchConfig)
|
|
@@ -208,6 +218,78 @@ src/
|
|
|
208
218
|
| `instance.registerSimulatorApi(name, handler)` | 注册 simulator 自定义 API,小程序里 `wx.<name>()` 调用(详见下方"Simulator 自定义 API")。返回 `Disposable` |
|
|
209
219
|
| `instance.ipc` | `IpcRegistry` 实例,`instance.ipc.handle(channel, fn)` 注册自定义 IPC;已绑定 `senderPolicy` 网关 |
|
|
210
220
|
| `instance.registerTrustedWindow(win)` | 把 host 自有弹窗 `BrowserWindow` 加入受信 sender 集,否则其发起的 `instance.ipc` 调用会被网关拒绝。窗口关闭即移除 |
|
|
221
|
+
| `instance.refreshHeaderAvatar()` | 通知 main renderer 重新调用 `headerAvatarProvider`,用于宿主登录态 / 头像变更后刷新内置 header 头像 |
|
|
222
|
+
| `instance.refreshHeaderActions()` | 通知 main renderer 重新调用 `headerActionsProvider`,用于宿主 action 文案 / disabled 状态变化后刷新 |
|
|
223
|
+
|
|
224
|
+
### Header Avatar
|
|
225
|
+
|
|
226
|
+
`headerAvatarProvider` 是内置 project header 头像 slot 的唯一数据源。devtools 会通过 `app:getHeaderAvatar` 拉取一个可序列化 DTO 并在固定 header toolbar 内渲染;宿主不需要、也不应该依赖 renderer 内部组件。
|
|
227
|
+
|
|
228
|
+
该 slot 只承载当前账号 / 身份入口,不是通用业务按钮扩展点。少量必须贴合内置 header 三栏布局的命令按钮使用下方的 `headerActionsProvider`;完整自定义工具栏、复杂状态展示、表单/筛选器、可变高度内容仍应使用 host toolbar WCV(`instance.context.views.hostToolbar.loadURL/loadFile` + `send/onMessage`),由宿主拥有内容、样式和高度。
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
launch({
|
|
232
|
+
headerAvatarProvider: () => {
|
|
233
|
+
const user = qdmpSession.currentUser()
|
|
234
|
+
return user
|
|
235
|
+
? {
|
|
236
|
+
displayName: user.nickname,
|
|
237
|
+
displayInitial: user.nickname,
|
|
238
|
+
avatarUrl: user.avatarUrl,
|
|
239
|
+
tooltip: user.nickname,
|
|
240
|
+
}
|
|
241
|
+
: null
|
|
242
|
+
},
|
|
243
|
+
onSetup(instance) {
|
|
244
|
+
qdmpSession.onUserChanged(() => instance.refreshHeaderAvatar())
|
|
245
|
+
},
|
|
246
|
+
})
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
`HeaderAvatarInfo` 字段:
|
|
250
|
+
|
|
251
|
+
| 字段 | 说明 |
|
|
252
|
+
| --- | --- |
|
|
253
|
+
| `displayName` | 用户名;用于 tooltip / aria 文本和 fallback 首字 |
|
|
254
|
+
| `displayInitial` | 可选 fallback 文本;头像图片缺失或加载失败时取首字显示 |
|
|
255
|
+
| `avatarUrl` | 头像图片 URL 或 data URL |
|
|
256
|
+
| `tooltip` | 可选 tooltip 覆盖;默认用 `displayName` |
|
|
257
|
+
|
|
258
|
+
### Header Actions
|
|
259
|
+
|
|
260
|
+
`headerActionsProvider` 用于把少量宿主命令渲染到内置 project header 的三栏中。它不是 host toolbar WCV 的替代品:只适合 1-4 个短文本命令,且按钮高度固定、宽度受 header 约束。
|
|
261
|
+
|
|
262
|
+
placement 对应内置 header 的三个区域:
|
|
263
|
+
|
|
264
|
+
| placement | 渲染位置 |
|
|
265
|
+
| --- | --- |
|
|
266
|
+
| `left` | 左区最右侧,位于头像 / 面板显隐 controls 之后 |
|
|
267
|
+
| `center` | 中区最右侧,位于编译模式 / 重新编译 / 编译状态之后 |
|
|
268
|
+
| `right` | 右区最左侧,位于布局 controls / 设置按钮之前 |
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
launch({
|
|
272
|
+
headerActionsProvider: () => [
|
|
273
|
+
{ id: 'open', label: '打开', placement: 'left' },
|
|
274
|
+
{ id: 'bindApp', label: '绑定应用', placement: 'left' },
|
|
275
|
+
{ id: 'preview', label: '真机预览', placement: 'center' },
|
|
276
|
+
{ id: 'upload', label: '上传', placement: 'right' },
|
|
277
|
+
],
|
|
278
|
+
headerActionHandler: async (id) => {
|
|
279
|
+
await commands.run(id)
|
|
280
|
+
},
|
|
281
|
+
})
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
`HeaderActionInfo` 字段:
|
|
285
|
+
|
|
286
|
+
| 字段 | 说明 |
|
|
287
|
+
| --- | --- |
|
|
288
|
+
| `id` | 稳定 action id;点击时传给 `headerActionHandler` |
|
|
289
|
+
| `label` | 短按钮文案 |
|
|
290
|
+
| `tooltip` | 可选 tooltip 覆盖;默认用 `label` |
|
|
291
|
+
| `placement` | `left` / `center` / `right`;默认 `right` |
|
|
292
|
+
| `disabled` | 可选禁用态 |
|
|
211
293
|
|
|
212
294
|
### 内置面板 ID
|
|
213
295
|
|
|
@@ -483,7 +565,7 @@ launch({
|
|
|
483
565
|
|
|
484
566
|
## Host Toolbar(宿主自定义工具栏)
|
|
485
567
|
|
|
486
|
-
下游通过 `instance.context.views.hostToolbar` 拥有 devtools 头部下方的工具栏条(一个 WebContentsView):`loadURL` / `loadFile` 加载自己的内容,`setPreloadPath` 注入自己的 preload,`setHeightMode` 钉死或自动跟随内容高度(自动模式要求内容自带 shrink-to-fit 的 `[data-host-toolbar-root]` 包裹元素;`{ fixed }` 校验入参——非有限数或负数同步抛 `TypeError`
|
|
568
|
+
下游通过 `instance.context.views.hostToolbar` 拥有 devtools 头部下方的工具栏条(一个 WebContentsView):`loadURL` / `loadFile` 加载自己的内容,`setPreloadPath` 注入自己的 preload,`setHeightMode` 钉死或自动跟随内容高度(自动模式要求内容自带 shrink-to-fit 的 `[data-host-toolbar-root]` 包裹元素;`{ fixed }` 校验入参——非有限数或负数同步抛 `TypeError` 且不污染既有模式)。主进程保留最后一次下发的高度(`views.getHostToolbarHeight()`),项目视图的占位条挂载时会主动拉取并回放——广播器对已上报的高度去重不再重发,没有这一步,冷启动在项目列表期间的上报、以及关闭项目再打开后的高度都会永久丢失(工具栏条塌缩为 0)。
|
|
487
569
|
|
|
488
570
|
### 双向消息:onMessage / send
|
|
489
571
|
|
package/dist/main/api.d.ts
CHANGED
|
@@ -16,5 +16,5 @@ export { IpcRegistry } from './utils/ipc-registry.js';
|
|
|
16
16
|
export type { SenderPolicy } from './utils/ipc-registry.js';
|
|
17
17
|
export { UpdateManager, createGitHubReleaseChecker } from './services/update/index.js';
|
|
18
18
|
export type { UpdateManagerOptions, GitHubReleaseCheckerOptions, PickAssetContext, VersionScheme, } from './services/update/index.js';
|
|
19
|
-
export type { WorkbenchConfig, WorkbenchAppConfig, CompilationAdapter, ProjectSession, BuiltinPanelId, BuiltinModuleId, UpdateChecker, UpdateInfo, } from '../shared/types.js';
|
|
19
|
+
export type { WorkbenchConfig, WorkbenchAppConfig, CompilationAdapter, ProjectSession, HeaderAvatarInfo, HeaderAvatarProvider, HeaderActionInfo, HeaderActionPlacement, HeaderActionsProvider, BuiltinPanelId, BuiltinModuleId, UpdateChecker, UpdateInfo, } from '../shared/types.js';
|
|
20
20
|
//# sourceMappingURL=api.d.ts.map
|
package/dist/main/app/app.d.ts
CHANGED
|
@@ -15,6 +15,10 @@ export interface WorkbenchAppInstance {
|
|
|
15
15
|
registerTrustedWindow: (win: BrowserWindow) => Disposable;
|
|
16
16
|
/** Registers a simulator custom API into this context's registry. */
|
|
17
17
|
registerSimulatorApi: (name: string, handler: SimulatorApiHandler) => Disposable;
|
|
18
|
+
/** Ask the main renderer to re-read the header avatar provider. */
|
|
19
|
+
refreshHeaderAvatar: () => void;
|
|
20
|
+
/** Ask the main renderer to re-read the header actions provider. */
|
|
21
|
+
refreshHeaderActions: () => void;
|
|
18
22
|
automationServer?: AutomationServer;
|
|
19
23
|
updateManager?: UpdateManager;
|
|
20
24
|
dispose: () => Promise<void>;
|
package/dist/main/app/app.js
CHANGED
|
@@ -149,6 +149,10 @@ function createContext(config, mainWindow, rendererDir) {
|
|
|
149
149
|
appName: config.appName,
|
|
150
150
|
apiNamespaces: config.apiNamespaces,
|
|
151
151
|
brandingProvider: config.brandingProvider,
|
|
152
|
+
headerAvatarProvider: config.headerAvatarProvider,
|
|
153
|
+
headerAvatarActionHandler: config.headerAvatarActionHandler,
|
|
154
|
+
headerActionsProvider: config.headerActionsProvider,
|
|
155
|
+
headerActionHandler: config.headerActionHandler,
|
|
152
156
|
// The host-supplied ProjectsProvider / template types in `shared/types`
|
|
153
157
|
// are structurally compatible with the main-process equivalents —
|
|
154
158
|
// these casts are safe; we re-narrow at the workspace-service /
|
|
@@ -352,6 +356,8 @@ export async function createDevtoolsRuntime(config = {}) {
|
|
|
352
356
|
// teardown, so a single dispose leaves no dead entry behind.
|
|
353
357
|
registerTrustedWindow: (win) => context.registry.add(registerTrustedWindow(context, win)),
|
|
354
358
|
registerSimulatorApi: (name, handler) => context.registry.add(toDisposable(context.simulatorApis.register(name, handler))),
|
|
359
|
+
refreshHeaderAvatar: () => context.notify.headerAvatarChanged(),
|
|
360
|
+
refreshHeaderActions: () => context.notify.headerActionsChanged(),
|
|
355
361
|
dispose: () => disposeContext(context),
|
|
356
362
|
};
|
|
357
363
|
if (config.onSetup) {
|
|
@@ -374,6 +374,10 @@ var ProjectChannel = {
|
|
|
374
374
|
GetCompileConfig: "project:getCompileConfig",
|
|
375
375
|
SaveCompileConfig: "project:saveCompileConfig",
|
|
376
376
|
Status: "project:status",
|
|
377
|
+
// Main → renderer push of per-line dmcc compile logs (devkit `onLog`).
|
|
378
|
+
// Dedicated channel: `project:status` keeps its one-event-per-payload
|
|
379
|
+
// contract (compileEvents), this one carries the line stream (compileLogs).
|
|
380
|
+
CompileLog: "project:compileLog",
|
|
377
381
|
CaptureThumbnail: "project:captureThumbnail",
|
|
378
382
|
GetThumbnail: "project:getThumbnail"
|
|
379
383
|
};
|
|
@@ -440,7 +444,17 @@ var ViewChannel = {
|
|
|
440
444
|
* main → main-window renderer: push the reserved host-toolbar height so the
|
|
441
445
|
* renderer placeholder div resizes (closing the dynamic-height loop).
|
|
442
446
|
*/
|
|
443
|
-
HostToolbarHeightChanged: "view:host-toolbar:height-changed"
|
|
447
|
+
HostToolbarHeightChanged: "view:host-toolbar:height-changed",
|
|
448
|
+
/**
|
|
449
|
+
* main ← main-window renderer (invoke): pull the last NOTIFIED toolbar
|
|
450
|
+
* height retained in main. Mount-time replay companion to
|
|
451
|
+
* `HostToolbarHeightChanged`: the push listener mounts with the project
|
|
452
|
+
* view and the toolbar's size-advertiser deduplicates (never re-reports),
|
|
453
|
+
* so a height pushed while no project view was mounted would otherwise be
|
|
454
|
+
* lost forever (cold start on the project list races it; close-project →
|
|
455
|
+
* reopen hits it deterministically). No payload; resolves a number.
|
|
456
|
+
*/
|
|
457
|
+
HostToolbarGetHeight: "view:host-toolbar:get-height"
|
|
444
458
|
};
|
|
445
459
|
var PopoverChannel = {
|
|
446
460
|
Show: "popover:show",
|
|
@@ -453,7 +467,13 @@ var WindowChannel = {
|
|
|
453
467
|
NavigateBack: "window:navigateBack"
|
|
454
468
|
};
|
|
455
469
|
var AppChannel = {
|
|
456
|
-
GetBranding: "app:getBranding"
|
|
470
|
+
GetBranding: "app:getBranding",
|
|
471
|
+
GetHeaderAvatar: "app:getHeaderAvatar",
|
|
472
|
+
InvokeHeaderAvatar: "app:invokeHeaderAvatar",
|
|
473
|
+
HeaderAvatarChanged: "app:headerAvatarChanged",
|
|
474
|
+
GetHeaderActions: "app:getHeaderActions",
|
|
475
|
+
InvokeHeaderAction: "app:invokeHeaderAction",
|
|
476
|
+
HeaderActionsChanged: "app:headerActionsChanged"
|
|
457
477
|
};
|
|
458
478
|
var AutomationChannel = {
|
|
459
479
|
GetPort: "automation:port"
|
|
@@ -966,6 +986,9 @@ function createRendererNotifier(ctx) {
|
|
|
966
986
|
projectStatus(payload) {
|
|
967
987
|
sendToMain(ProjectChannel.Status, payload);
|
|
968
988
|
},
|
|
989
|
+
compileLog(payload) {
|
|
990
|
+
sendToMain(ProjectChannel.CompileLog, payload);
|
|
991
|
+
},
|
|
969
992
|
windowNavigateBack() {
|
|
970
993
|
sendToMain(WindowChannel.NavigateBack);
|
|
971
994
|
},
|
|
@@ -978,6 +1001,12 @@ function createRendererNotifier(ctx) {
|
|
|
978
1001
|
hostToolbarHeightChanged(height) {
|
|
979
1002
|
sendToMain(ViewChannel.HostToolbarHeightChanged, height);
|
|
980
1003
|
},
|
|
1004
|
+
headerAvatarChanged() {
|
|
1005
|
+
sendToMain(AppChannel.HeaderAvatarChanged);
|
|
1006
|
+
},
|
|
1007
|
+
headerActionsChanged() {
|
|
1008
|
+
sendToMain(AppChannel.HeaderActionsChanged);
|
|
1009
|
+
},
|
|
981
1010
|
editorOpenFile(payload) {
|
|
982
1011
|
sendToMain(EditorChannel.OpenFile, payload);
|
|
983
1012
|
},
|
|
@@ -1841,6 +1870,7 @@ function createViewManager(ctx) {
|
|
|
1841
1870
|
let hostToolbarViewAdded = false;
|
|
1842
1871
|
let hostToolbarRuntimeAcquired = false;
|
|
1843
1872
|
let hostToolbarHeightMode = "auto";
|
|
1873
|
+
let hostToolbarLastHeight = 0;
|
|
1844
1874
|
const hostToolbarPort = createHostToolbarPortChannel({
|
|
1845
1875
|
isCurrent: (wc) => liveHostToolbarWebContents() === wc
|
|
1846
1876
|
});
|
|
@@ -1943,9 +1973,13 @@ function createViewManager(ctx) {
|
|
|
1943
1973
|
}
|
|
1944
1974
|
view.setBounds(bounds);
|
|
1945
1975
|
}
|
|
1976
|
+
function notifyHostToolbarHeight(height) {
|
|
1977
|
+
hostToolbarLastHeight = height;
|
|
1978
|
+
ctx.notify.hostToolbarHeightChanged(height);
|
|
1979
|
+
}
|
|
1946
1980
|
function setHostToolbarHeight(extent) {
|
|
1947
1981
|
if (hostToolbarHeightMode !== "auto") return;
|
|
1948
|
-
|
|
1982
|
+
notifyHostToolbarHeight(extent);
|
|
1949
1983
|
}
|
|
1950
1984
|
function hideHostToolbar() {
|
|
1951
1985
|
if (hostToolbarView && hostToolbarViewAdded && !ctx.windows.mainWindow.isDestroyed()) {
|
|
@@ -1955,7 +1989,7 @@ function createViewManager(ctx) {
|
|
|
1955
1989
|
}
|
|
1956
1990
|
}
|
|
1957
1991
|
hostToolbarViewAdded = false;
|
|
1958
|
-
|
|
1992
|
+
notifyHostToolbarHeight(0);
|
|
1959
1993
|
}
|
|
1960
1994
|
const hostToolbar = {
|
|
1961
1995
|
async loadURL(url) {
|
|
@@ -1985,7 +2019,7 @@ function createViewManager(ctx) {
|
|
|
1985
2019
|
}
|
|
1986
2020
|
hostToolbarHeightMode = mode;
|
|
1987
2021
|
if (mode !== "auto") {
|
|
1988
|
-
|
|
2022
|
+
notifyHostToolbarHeight(mode.fixed);
|
|
1989
2023
|
}
|
|
1990
2024
|
},
|
|
1991
2025
|
onMessage(channel, handler) {
|
|
@@ -2546,6 +2580,7 @@ function createViewManager(ctx) {
|
|
|
2546
2580
|
return popoverView.webContents.id;
|
|
2547
2581
|
},
|
|
2548
2582
|
getHostToolbarWebContentsId: () => liveHostToolbarWebContents()?.id ?? null,
|
|
2583
|
+
getHostToolbarHeight: () => hostToolbarLastHeight,
|
|
2549
2584
|
setNativeSimulatorViewBounds,
|
|
2550
2585
|
resize,
|
|
2551
2586
|
setSimulatorDevtoolsBounds,
|
|
@@ -2823,6 +2858,7 @@ function createWorkspaceService(ctx) {
|
|
|
2823
2858
|
let currentSession = null;
|
|
2824
2859
|
let currentProjectPath = "";
|
|
2825
2860
|
let lastClosedProjectPath = "";
|
|
2861
|
+
let logGeneration = 0;
|
|
2826
2862
|
function sendStatus(status2, message, hotReload) {
|
|
2827
2863
|
ctx.notify.projectStatus(hotReload ? { status: status2, message, hotReload: true } : { status: status2, message });
|
|
2828
2864
|
}
|
|
@@ -2871,6 +2907,7 @@ function createWorkspaceService(ctx) {
|
|
|
2871
2907
|
validateProjectDir: async (dirPath) => provider.validateProjectDir ? provider.validateProjectDir(dirPath) : null,
|
|
2872
2908
|
async openProject(projectPath) {
|
|
2873
2909
|
clearSimulatorServicewechatReferer();
|
|
2910
|
+
logGeneration++;
|
|
2874
2911
|
await disposeSession();
|
|
2875
2912
|
currentProjectPath = "";
|
|
2876
2913
|
lastClosedProjectPath = "";
|
|
@@ -2881,6 +2918,7 @@ function createWorkspaceService(ctx) {
|
|
|
2881
2918
|
}
|
|
2882
2919
|
sendStatus("compiling", "\u6B63\u5728\u7F16\u8BD1...");
|
|
2883
2920
|
const { compile } = loadWorkbenchSettings();
|
|
2921
|
+
const sessionGeneration = ++logGeneration;
|
|
2884
2922
|
let session5;
|
|
2885
2923
|
try {
|
|
2886
2924
|
session5 = await ctx.adapter.openProject({
|
|
@@ -2888,7 +2926,21 @@ function createWorkspaceService(ctx) {
|
|
|
2888
2926
|
sourcemap: true,
|
|
2889
2927
|
watch: compile.watch,
|
|
2890
2928
|
onRebuild: () => sendStatus("ready", "\u7F16\u8BD1\u5B8C\u6210\uFF0C\u5DF2\u70ED\u66F4\u65B0", true),
|
|
2891
|
-
onBuildError: (err) => sendStatus("error", String(err))
|
|
2929
|
+
onBuildError: (err) => sendStatus("error", String(err)),
|
|
2930
|
+
// Per-line dmcc log (already filtered in devkit). Stamp the
|
|
2931
|
+
// wall-clock capture time here and push verbatim on the dedicated
|
|
2932
|
+
// compile-log channel — never through projectStatus, whose
|
|
2933
|
+
// one-event-per-payload contract feeds compileEvents. Stale lines
|
|
2934
|
+
// (a closed/replaced session's worker flushing its buffers) are
|
|
2935
|
+
// dropped via the generation check.
|
|
2936
|
+
onLog: (entry) => {
|
|
2937
|
+
if (sessionGeneration !== logGeneration) return;
|
|
2938
|
+
ctx.notify.compileLog({
|
|
2939
|
+
stream: entry.stream,
|
|
2940
|
+
text: entry.text,
|
|
2941
|
+
at: Date.now()
|
|
2942
|
+
});
|
|
2943
|
+
}
|
|
2892
2944
|
});
|
|
2893
2945
|
} catch (err) {
|
|
2894
2946
|
clearSimulatorServicewechatReferer();
|
|
@@ -2922,6 +2974,7 @@ function createWorkspaceService(ctx) {
|
|
|
2922
2974
|
};
|
|
2923
2975
|
},
|
|
2924
2976
|
async closeProject() {
|
|
2977
|
+
logGeneration++;
|
|
2925
2978
|
clearSimulatorServicewechatReferer();
|
|
2926
2979
|
await disposeSession();
|
|
2927
2980
|
if (currentProjectPath !== "") lastClosedProjectPath = currentProjectPath;
|
|
@@ -3066,7 +3119,11 @@ function createWorkbenchContext(opts) {
|
|
|
3066
3119
|
rendererDir: opts.rendererDir,
|
|
3067
3120
|
apiNamespaces: opts.apiNamespaces ?? [],
|
|
3068
3121
|
appName: opts.appName ?? "Dimina DevTools",
|
|
3069
|
-
brandingProvider: opts.brandingProvider
|
|
3122
|
+
brandingProvider: opts.brandingProvider,
|
|
3123
|
+
headerAvatarProvider: opts.headerAvatarProvider,
|
|
3124
|
+
headerAvatarActionHandler: opts.headerAvatarActionHandler,
|
|
3125
|
+
headerActionsProvider: opts.headerActionsProvider,
|
|
3126
|
+
headerActionHandler: opts.headerActionHandler
|
|
3070
3127
|
};
|
|
3071
3128
|
ctx.registry = new DisposableRegistry3();
|
|
3072
3129
|
ctx.connections = createConnectionRegistry();
|
|
@@ -3166,10 +3223,70 @@ function installAppMenu(ctx) {
|
|
|
3166
3223
|
}
|
|
3167
3224
|
|
|
3168
3225
|
// src/main/ipc/app.ts
|
|
3226
|
+
function readString(value) {
|
|
3227
|
+
if (typeof value !== "string") return void 0;
|
|
3228
|
+
const trimmed = value.trim();
|
|
3229
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
3230
|
+
}
|
|
3231
|
+
function normalizeHeaderAvatar(value) {
|
|
3232
|
+
if (!value || typeof value !== "object") return null;
|
|
3233
|
+
const raw = value;
|
|
3234
|
+
const avatar = {};
|
|
3235
|
+
const displayName = readString(raw.displayName);
|
|
3236
|
+
const displayInitial = readString(raw.displayInitial);
|
|
3237
|
+
const avatarUrl = readString(raw.avatarUrl);
|
|
3238
|
+
const tooltip = readString(raw.tooltip);
|
|
3239
|
+
if (displayName) avatar.displayName = displayName;
|
|
3240
|
+
if (displayInitial) avatar.displayInitial = displayInitial;
|
|
3241
|
+
if (avatarUrl) avatar.avatarUrl = avatarUrl;
|
|
3242
|
+
if (tooltip) avatar.tooltip = tooltip;
|
|
3243
|
+
return avatar.displayName || avatar.displayInitial || avatar.avatarUrl ? avatar : null;
|
|
3244
|
+
}
|
|
3245
|
+
function readPlacement(value) {
|
|
3246
|
+
return value === "left" || value === "center" || value === "right" ? value : void 0;
|
|
3247
|
+
}
|
|
3248
|
+
function normalizeHeaderAction(value) {
|
|
3249
|
+
if (!value || typeof value !== "object") return null;
|
|
3250
|
+
const raw = value;
|
|
3251
|
+
const id = readString(raw.id);
|
|
3252
|
+
const label = readString(raw.label);
|
|
3253
|
+
if (!id || !label) return null;
|
|
3254
|
+
const action = { id, label };
|
|
3255
|
+
const tooltip = readString(raw.tooltip);
|
|
3256
|
+
const placement = readPlacement(raw.placement);
|
|
3257
|
+
if (tooltip) action.tooltip = tooltip;
|
|
3258
|
+
if (placement) action.placement = placement;
|
|
3259
|
+
if (typeof raw.disabled === "boolean") action.disabled = raw.disabled;
|
|
3260
|
+
return action;
|
|
3261
|
+
}
|
|
3262
|
+
function normalizeHeaderActions(value) {
|
|
3263
|
+
if (!Array.isArray(value)) return [];
|
|
3264
|
+
const actions = [];
|
|
3265
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3266
|
+
for (const item of value) {
|
|
3267
|
+
const action = normalizeHeaderAction(item);
|
|
3268
|
+
if (!action || seen.has(action.id)) continue;
|
|
3269
|
+
seen.add(action.id);
|
|
3270
|
+
actions.push(action);
|
|
3271
|
+
}
|
|
3272
|
+
return actions.slice(0, 8);
|
|
3273
|
+
}
|
|
3169
3274
|
function registerAppIpc(ctx) {
|
|
3170
3275
|
return new IpcRegistry(ctx.senderPolicy).handle(AppChannel.GetBranding, async () => {
|
|
3171
3276
|
if (ctx.brandingProvider) return ctx.brandingProvider();
|
|
3172
3277
|
return { appName: ctx.appName };
|
|
3278
|
+
}).handle(AppChannel.GetHeaderAvatar, async () => {
|
|
3279
|
+
if (!ctx.headerAvatarProvider) return null;
|
|
3280
|
+
return normalizeHeaderAvatar(await ctx.headerAvatarProvider());
|
|
3281
|
+
}).handle(AppChannel.InvokeHeaderAvatar, async () => {
|
|
3282
|
+
await ctx.headerAvatarActionHandler?.();
|
|
3283
|
+
}).handle(AppChannel.GetHeaderActions, async () => {
|
|
3284
|
+
if (!ctx.headerActionsProvider) return [];
|
|
3285
|
+
return normalizeHeaderActions(await ctx.headerActionsProvider());
|
|
3286
|
+
}).handle(AppChannel.InvokeHeaderAction, async (_event, id) => {
|
|
3287
|
+
const actionId = readString(id);
|
|
3288
|
+
if (!actionId) return;
|
|
3289
|
+
await ctx.headerActionHandler?.(actionId);
|
|
3173
3290
|
});
|
|
3174
3291
|
}
|
|
3175
3292
|
|
|
@@ -4756,12 +4873,16 @@ function summarizeBridgeMsg(payload) {
|
|
|
4756
4873
|
var API_CALL_TIMEOUT_MS = 5e3;
|
|
4757
4874
|
function resolveCurrentApp(state, ctx, appId) {
|
|
4758
4875
|
if (appId) {
|
|
4759
|
-
|
|
4876
|
+
let match;
|
|
4877
|
+
for (const ap of state.appSessions.values()) if (ap.appId === appId) match = ap;
|
|
4878
|
+
if (match) return match;
|
|
4760
4879
|
}
|
|
4761
4880
|
const appInfo = ctx.workspace?.getSession?.()?.appInfo;
|
|
4762
4881
|
const activeAppId = appInfo?.appId;
|
|
4763
4882
|
if (activeAppId) {
|
|
4764
|
-
|
|
4883
|
+
let match;
|
|
4884
|
+
for (const ap of state.appSessions.values()) if (ap.appId === activeAppId) match = ap;
|
|
4885
|
+
if (match) return match;
|
|
4765
4886
|
}
|
|
4766
4887
|
let last;
|
|
4767
4888
|
for (const ap of state.appSessions.values()) last = ap;
|
|
@@ -4778,7 +4899,11 @@ function installBridgeRouter(ctx) {
|
|
|
4778
4899
|
emitRenderEvent: () => {
|
|
4779
4900
|
},
|
|
4780
4901
|
connections: ctx.connections,
|
|
4781
|
-
debugTap: createDebugTap({ enabled: resolveDebugTapEnabled() })
|
|
4902
|
+
debugTap: createDebugTap({ enabled: resolveDebugTapEnabled() }),
|
|
4903
|
+
evictAppDataBridges: (ap) => {
|
|
4904
|
+
if (!ctx.appData) return;
|
|
4905
|
+
for (const page of ap.pages.values()) ctx.appData.evictBridge(ap.appId, page.bridgeId);
|
|
4906
|
+
}
|
|
4782
4907
|
};
|
|
4783
4908
|
const prewarmPoolSize = resolvePrewarmPoolSize();
|
|
4784
4909
|
if (prewarmPoolSize > 0) {
|
|
@@ -4950,9 +5075,6 @@ function installBridgeRouter(ctx) {
|
|
|
4950
5075
|
console.warn(`[bridge-router] DISPOSE rejected: sender belongs to ${senderApp.appSessionId}, target ${target.appSessionId}`);
|
|
4951
5076
|
return;
|
|
4952
5077
|
}
|
|
4953
|
-
if (ctx.appData) {
|
|
4954
|
-
for (const page of target.pages.values()) ctx.appData.evictBridge(target.appId, page.bridgeId);
|
|
4955
|
-
}
|
|
4956
5078
|
void disposeAppSession(state, target.appSessionId);
|
|
4957
5079
|
};
|
|
4958
5080
|
ipcMain3.on(BRIDGE_CHANNELS.DISPOSE, onDispose);
|
|
@@ -5135,6 +5257,10 @@ async function handleSpawn(state, ctx, event, opts) {
|
|
|
5135
5257
|
};
|
|
5136
5258
|
appSession.onServiceClosed = onServiceClosed;
|
|
5137
5259
|
serviceWindow.once("closed", onServiceClosed);
|
|
5260
|
+
const onSimulatorDestroyed = () => {
|
|
5261
|
+
void disposeAppSession(state, appSessionId);
|
|
5262
|
+
};
|
|
5263
|
+
simulatorWc.once("destroyed", onSimulatorDestroyed);
|
|
5138
5264
|
if (usedPool) {
|
|
5139
5265
|
const bootOnServiceLoad = () => {
|
|
5140
5266
|
if (serviceWindow.isDestroyed()) return;
|
|
@@ -5639,6 +5765,7 @@ async function disposeAppSession(state, appSessionId, opts = {}) {
|
|
|
5639
5765
|
const ap = state.appSessions.get(appSessionId);
|
|
5640
5766
|
if (!ap) return;
|
|
5641
5767
|
state.appSessions.delete(appSessionId);
|
|
5768
|
+
state.evictAppDataBridges(ap);
|
|
5642
5769
|
for (const [requestId, pending] of state.pendingApiCalls) {
|
|
5643
5770
|
if (pending.appSessionId !== appSessionId) continue;
|
|
5644
5771
|
clearTimeout(pending.timer);
|
|
@@ -5804,7 +5931,7 @@ function registerViewsIpc(ctx) {
|
|
|
5804
5931
|
args
|
|
5805
5932
|
);
|
|
5806
5933
|
ctx.views.setHostToolbarBounds(bounds);
|
|
5807
|
-
});
|
|
5934
|
+
}).handle(ViewChannel.HostToolbarGetHeight, () => ctx.views.getHostToolbarHeight());
|
|
5808
5935
|
const onAdvertiseHeight = (event, ...args) => {
|
|
5809
5936
|
if (event.sender.id !== ctx.views.getHostToolbarWebContentsId()) return;
|
|
5810
5937
|
let extent;
|
|
@@ -8768,6 +8895,10 @@ function createContext(config, mainWindow, rendererDir2) {
|
|
|
8768
8895
|
appName: config.appName,
|
|
8769
8896
|
apiNamespaces: config.apiNamespaces,
|
|
8770
8897
|
brandingProvider: config.brandingProvider,
|
|
8898
|
+
headerAvatarProvider: config.headerAvatarProvider,
|
|
8899
|
+
headerAvatarActionHandler: config.headerAvatarActionHandler,
|
|
8900
|
+
headerActionsProvider: config.headerActionsProvider,
|
|
8901
|
+
headerActionHandler: config.headerActionHandler,
|
|
8771
8902
|
// The host-supplied ProjectsProvider / template types in `shared/types`
|
|
8772
8903
|
// are structurally compatible with the main-process equivalents —
|
|
8773
8904
|
// these casts are safe; we re-narrow at the workspace-service /
|
|
@@ -8904,6 +9035,8 @@ async function createDevtoolsRuntime(config = {}) {
|
|
|
8904
9035
|
// teardown, so a single dispose leaves no dead entry behind.
|
|
8905
9036
|
registerTrustedWindow: (win) => context.registry.add(registerTrustedWindow(context, win)),
|
|
8906
9037
|
registerSimulatorApi: (name, handler) => context.registry.add(toDisposable8(context.simulatorApis.register(name, handler))),
|
|
9038
|
+
refreshHeaderAvatar: () => context.notify.headerAvatarChanged(),
|
|
9039
|
+
refreshHeaderActions: () => context.notify.headerActionsChanged(),
|
|
8907
9040
|
dispose: () => disposeContext(context)
|
|
8908
9041
|
};
|
|
8909
9042
|
if (config.onSetup) {
|
package/dist/main/ipc/app.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { WorkbenchContext } from '../services/workbench-context.js';
|
|
2
2
|
import type { Disposable } from '@dimina-kit/electron-deck/main';
|
|
3
|
-
|
|
3
|
+
type AppIpcContext = Pick<WorkbenchContext, 'brandingProvider' | 'appName' | 'headerAvatarProvider' | 'headerAvatarActionHandler' | 'headerActionsProvider' | 'headerActionHandler' | 'senderPolicy'>;
|
|
4
|
+
export declare function registerAppIpc(ctx: AppIpcContext): Disposable;
|
|
5
|
+
export {};
|
|
4
6
|
//# sourceMappingURL=app.d.ts.map
|
package/dist/main/ipc/app.js
CHANGED
|
@@ -1,11 +1,91 @@
|
|
|
1
1
|
import { AppChannel } from '../../shared/ipc-channels.js';
|
|
2
2
|
import { IpcRegistry } from '../utils/ipc-registry.js';
|
|
3
|
+
function readString(value) {
|
|
4
|
+
if (typeof value !== 'string')
|
|
5
|
+
return undefined;
|
|
6
|
+
const trimmed = value.trim();
|
|
7
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
8
|
+
}
|
|
9
|
+
function normalizeHeaderAvatar(value) {
|
|
10
|
+
if (!value || typeof value !== 'object')
|
|
11
|
+
return null;
|
|
12
|
+
const raw = value;
|
|
13
|
+
const avatar = {};
|
|
14
|
+
const displayName = readString(raw.displayName);
|
|
15
|
+
const displayInitial = readString(raw.displayInitial);
|
|
16
|
+
const avatarUrl = readString(raw.avatarUrl);
|
|
17
|
+
const tooltip = readString(raw.tooltip);
|
|
18
|
+
if (displayName)
|
|
19
|
+
avatar.displayName = displayName;
|
|
20
|
+
if (displayInitial)
|
|
21
|
+
avatar.displayInitial = displayInitial;
|
|
22
|
+
if (avatarUrl)
|
|
23
|
+
avatar.avatarUrl = avatarUrl;
|
|
24
|
+
if (tooltip)
|
|
25
|
+
avatar.tooltip = tooltip;
|
|
26
|
+
return avatar.displayName || avatar.displayInitial || avatar.avatarUrl ? avatar : null;
|
|
27
|
+
}
|
|
28
|
+
function readPlacement(value) {
|
|
29
|
+
return value === 'left' || value === 'center' || value === 'right' ? value : undefined;
|
|
30
|
+
}
|
|
31
|
+
function normalizeHeaderAction(value) {
|
|
32
|
+
if (!value || typeof value !== 'object')
|
|
33
|
+
return null;
|
|
34
|
+
const raw = value;
|
|
35
|
+
const id = readString(raw.id);
|
|
36
|
+
const label = readString(raw.label);
|
|
37
|
+
if (!id || !label)
|
|
38
|
+
return null;
|
|
39
|
+
const action = { id, label };
|
|
40
|
+
const tooltip = readString(raw.tooltip);
|
|
41
|
+
const placement = readPlacement(raw.placement);
|
|
42
|
+
if (tooltip)
|
|
43
|
+
action.tooltip = tooltip;
|
|
44
|
+
if (placement)
|
|
45
|
+
action.placement = placement;
|
|
46
|
+
if (typeof raw.disabled === 'boolean')
|
|
47
|
+
action.disabled = raw.disabled;
|
|
48
|
+
return action;
|
|
49
|
+
}
|
|
50
|
+
function normalizeHeaderActions(value) {
|
|
51
|
+
if (!Array.isArray(value))
|
|
52
|
+
return [];
|
|
53
|
+
const actions = [];
|
|
54
|
+
const seen = new Set();
|
|
55
|
+
for (const item of value) {
|
|
56
|
+
const action = normalizeHeaderAction(item);
|
|
57
|
+
if (!action || seen.has(action.id))
|
|
58
|
+
continue;
|
|
59
|
+
seen.add(action.id);
|
|
60
|
+
actions.push(action);
|
|
61
|
+
}
|
|
62
|
+
return actions.slice(0, 8);
|
|
63
|
+
}
|
|
3
64
|
export function registerAppIpc(ctx) {
|
|
4
65
|
return new IpcRegistry(ctx.senderPolicy)
|
|
5
66
|
.handle(AppChannel.GetBranding, async () => {
|
|
6
67
|
if (ctx.brandingProvider)
|
|
7
68
|
return ctx.brandingProvider();
|
|
8
69
|
return { appName: ctx.appName };
|
|
70
|
+
})
|
|
71
|
+
.handle(AppChannel.GetHeaderAvatar, async () => {
|
|
72
|
+
if (!ctx.headerAvatarProvider)
|
|
73
|
+
return null;
|
|
74
|
+
return normalizeHeaderAvatar(await ctx.headerAvatarProvider());
|
|
75
|
+
})
|
|
76
|
+
.handle(AppChannel.InvokeHeaderAvatar, async () => {
|
|
77
|
+
await ctx.headerAvatarActionHandler?.();
|
|
78
|
+
})
|
|
79
|
+
.handle(AppChannel.GetHeaderActions, async () => {
|
|
80
|
+
if (!ctx.headerActionsProvider)
|
|
81
|
+
return [];
|
|
82
|
+
return normalizeHeaderActions(await ctx.headerActionsProvider());
|
|
83
|
+
})
|
|
84
|
+
.handle(AppChannel.InvokeHeaderAction, async (_event, id) => {
|
|
85
|
+
const actionId = readString(id);
|
|
86
|
+
if (!actionId)
|
|
87
|
+
return;
|
|
88
|
+
await ctx.headerActionHandler?.(actionId);
|
|
9
89
|
});
|
|
10
90
|
}
|
|
11
91
|
//# sourceMappingURL=app.js.map
|