@dimina-kit/devtools 0.4.0-dev.20260612152115 → 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.
Files changed (32) hide show
  1. package/README.md +82 -0
  2. package/dist/main/api.d.ts +1 -1
  3. package/dist/main/app/app.d.ts +4 -0
  4. package/dist/main/app/app.js +6 -0
  5. package/dist/main/index.bundle.js +84 -2
  6. package/dist/main/ipc/app.d.ts +3 -1
  7. package/dist/main/ipc/app.js +80 -0
  8. package/dist/main/services/notifications/renderer-notifier.d.ts +4 -0
  9. package/dist/main/services/notifications/renderer-notifier.js +7 -1
  10. package/dist/main/services/workbench-context.d.ts +13 -1
  11. package/dist/main/services/workbench-context.js +4 -0
  12. package/dist/preload/windows/host-toolbar-runtime.cjs.map +1 -1
  13. package/dist/preload/windows/simulator.cjs.map +1 -1
  14. package/dist/renderer/assets/index-Bx4XqFQ2.js +50 -0
  15. package/dist/renderer/assets/{input-DgqX2UvN.js → input-J1MQ5fra.js} +2 -2
  16. package/dist/renderer/assets/{ipc-transport-CsFhDJ7q.js → ipc-transport-21qDpt9Y.js} +2 -2
  17. package/dist/renderer/assets/ipc-transport-CVp38I_M.css +1 -0
  18. package/dist/renderer/assets/{popover-D_2o_52M.js → popover-ChIUUEdj.js} +2 -2
  19. package/dist/renderer/assets/{select-BJYsdghn.js → select-BgWAbMot.js} +2 -2
  20. package/dist/renderer/assets/{settings-CivkfizP.js → settings-CGea5k_R.js} +2 -2
  21. package/dist/renderer/assets/{settings-api-7mXkBy5-.js → settings-api-Dit4oL6S.js} +2 -2
  22. package/dist/renderer/assets/{workbenchSettings-BwpwxuzM.js → workbenchSettings-CFrdVOiP.js} +2 -2
  23. package/dist/renderer/entries/main/index.html +6 -6
  24. package/dist/renderer/entries/popover/index.html +5 -5
  25. package/dist/renderer/entries/settings/index.html +5 -5
  26. package/dist/renderer/entries/workbench-settings/index.html +4 -4
  27. package/dist/shared/ipc-channels.d.ts +6 -0
  28. package/dist/shared/ipc-channels.js +6 -0
  29. package/dist/shared/types.d.ts +61 -0
  30. package/package.json +4 -4
  31. package/dist/renderer/assets/index-Bi_6SS-L.js +0 -50
  32. package/dist/renderer/assets/ipc-transport-SnOAk0Jq.css +0 -1
package/README.md CHANGED
@@ -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 相关状态)
@@ -181,6 +187,10 @@ src/
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
 
@@ -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
@@ -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>;
@@ -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) {
@@ -467,7 +467,13 @@ var WindowChannel = {
467
467
  NavigateBack: "window:navigateBack"
468
468
  };
469
469
  var AppChannel = {
470
- 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"
471
477
  };
472
478
  var AutomationChannel = {
473
479
  GetPort: "automation:port"
@@ -995,6 +1001,12 @@ function createRendererNotifier(ctx) {
995
1001
  hostToolbarHeightChanged(height) {
996
1002
  sendToMain(ViewChannel.HostToolbarHeightChanged, height);
997
1003
  },
1004
+ headerAvatarChanged() {
1005
+ sendToMain(AppChannel.HeaderAvatarChanged);
1006
+ },
1007
+ headerActionsChanged() {
1008
+ sendToMain(AppChannel.HeaderActionsChanged);
1009
+ },
998
1010
  editorOpenFile(payload) {
999
1011
  sendToMain(EditorChannel.OpenFile, payload);
1000
1012
  },
@@ -3107,7 +3119,11 @@ function createWorkbenchContext(opts) {
3107
3119
  rendererDir: opts.rendererDir,
3108
3120
  apiNamespaces: opts.apiNamespaces ?? [],
3109
3121
  appName: opts.appName ?? "Dimina DevTools",
3110
- brandingProvider: opts.brandingProvider
3122
+ brandingProvider: opts.brandingProvider,
3123
+ headerAvatarProvider: opts.headerAvatarProvider,
3124
+ headerAvatarActionHandler: opts.headerAvatarActionHandler,
3125
+ headerActionsProvider: opts.headerActionsProvider,
3126
+ headerActionHandler: opts.headerActionHandler
3111
3127
  };
3112
3128
  ctx.registry = new DisposableRegistry3();
3113
3129
  ctx.connections = createConnectionRegistry();
@@ -3207,10 +3223,70 @@ function installAppMenu(ctx) {
3207
3223
  }
3208
3224
 
3209
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
+ }
3210
3274
  function registerAppIpc(ctx) {
3211
3275
  return new IpcRegistry(ctx.senderPolicy).handle(AppChannel.GetBranding, async () => {
3212
3276
  if (ctx.brandingProvider) return ctx.brandingProvider();
3213
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);
3214
3290
  });
3215
3291
  }
3216
3292
 
@@ -8819,6 +8895,10 @@ function createContext(config, mainWindow, rendererDir2) {
8819
8895
  appName: config.appName,
8820
8896
  apiNamespaces: config.apiNamespaces,
8821
8897
  brandingProvider: config.brandingProvider,
8898
+ headerAvatarProvider: config.headerAvatarProvider,
8899
+ headerAvatarActionHandler: config.headerAvatarActionHandler,
8900
+ headerActionsProvider: config.headerActionsProvider,
8901
+ headerActionHandler: config.headerActionHandler,
8822
8902
  // The host-supplied ProjectsProvider / template types in `shared/types`
8823
8903
  // are structurally compatible with the main-process equivalents —
8824
8904
  // these casts are safe; we re-narrow at the workspace-service /
@@ -8955,6 +9035,8 @@ async function createDevtoolsRuntime(config = {}) {
8955
9035
  // teardown, so a single dispose leaves no dead entry behind.
8956
9036
  registerTrustedWindow: (win) => context.registry.add(registerTrustedWindow(context, win)),
8957
9037
  registerSimulatorApi: (name, handler) => context.registry.add(toDisposable8(context.simulatorApis.register(name, handler))),
9038
+ refreshHeaderAvatar: () => context.notify.headerAvatarChanged(),
9039
+ refreshHeaderActions: () => context.notify.headerActionsChanged(),
8958
9040
  dispose: () => disposeContext(context)
8959
9041
  };
8960
9042
  if (config.onSetup) {
@@ -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
- export declare function registerAppIpc(ctx: Pick<WorkbenchContext, 'brandingProvider' | 'appName' | 'senderPolicy'>): Disposable;
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
@@ -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
@@ -65,6 +65,10 @@ export interface RendererNotifier {
65
65
  * placeholder div resizes (closes the host-toolbar dynamic-height loop).
66
66
  */
67
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;
68
72
  /**
69
73
  * Ask the main renderer's Monaco editor to open a project file at a position.
70
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)
@@ -39,6 +39,12 @@ export function createRendererNotifier(ctx) {
39
39
  hostToolbarHeightChanged(height) {
40
40
  sendToMain(ViewChannel.HostToolbarHeightChanged, height);
41
41
  },
42
+ headerAvatarChanged() {
43
+ sendToMain(AppChannel.HeaderAvatarChanged);
44
+ },
45
+ headerActionsChanged() {
46
+ sendToMain(AppChannel.HeaderActionsChanged);
47
+ },
42
48
  editorOpenFile(payload) {
43
49
  sendToMain(EditorChannel.OpenFile, payload);
44
50
  },
@@ -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