@dimina-kit/devtools 0.4.0-dev.20260615070430 → 0.4.0-dev.20260615083030
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 +0 -82
- package/dist/main/api.d.ts +1 -1
- package/dist/main/app/app.d.ts +0 -4
- package/dist/main/app/app.js +0 -6
- package/dist/main/index.bundle.js +51 -101
- package/dist/main/ipc/app.d.ts +1 -3
- package/dist/main/ipc/app.js +0 -80
- package/dist/main/services/notifications/renderer-notifier.d.ts +0 -4
- package/dist/main/services/notifications/renderer-notifier.js +1 -7
- package/dist/main/services/safe-area/index.d.ts +6 -4
- package/dist/main/services/safe-area/index.js +20 -14
- package/dist/main/services/simulator-storage/index.js +4 -2
- package/dist/main/services/views/view-manager.js +40 -8
- package/dist/main/services/workbench-context.d.ts +1 -13
- package/dist/main/services/workbench-context.js +0 -4
- package/dist/main/utils/theme.d.ts +15 -0
- package/dist/main/utils/theme.js +34 -1
- package/dist/preload/runtime/native-host.d.ts +3 -0
- package/dist/preload/runtime/native-host.js +2 -0
- package/dist/preload/windows/host-toolbar-runtime.cjs.map +2 -2
- package/dist/preload/windows/main.cjs +3 -3
- package/dist/preload/windows/main.cjs.map +2 -2
- package/dist/preload/windows/simulator.cjs +1 -0
- package/dist/preload/windows/simulator.cjs.map +2 -2
- package/dist/preload/windows/simulator.js +1 -0
- package/dist/renderer/assets/index-BFwsvw4p.js +50 -0
- package/dist/renderer/assets/{input-J1MQ5fra.js → input-hGF71bn3.js} +2 -2
- package/dist/renderer/assets/{ipc-transport-21qDpt9Y.js → ipc-transport-BKWkvwKK.js} +2 -2
- package/dist/renderer/assets/ipc-transport-Cg1q1gwD.css +1 -0
- package/dist/renderer/assets/{popover-ChIUUEdj.js → popover-NNQNyjgI.js} +2 -2
- package/dist/renderer/assets/select-Bb1AKeSq.js +2 -0
- package/dist/renderer/assets/{settings-CGea5k_R.js → settings-7uwimev-.js} +2 -2
- package/dist/renderer/assets/settings-api-DkrPHroq.js +2 -0
- package/dist/renderer/assets/{workbenchSettings-CFrdVOiP.js → workbenchSettings-EaMYvKHG.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 +1 -7
- package/dist/shared/ipc-channels.js +7 -6
- package/dist/shared/ipc-schemas.d.ts +0 -1
- package/dist/shared/ipc-schemas.js +0 -1
- package/dist/shared/types.d.ts +0 -61
- package/dist/simulator/assets/device-shell-BWX7Yopg.js +2 -0
- package/dist/simulator/assets/{device-shell-DsdcNEkt.css → device-shell-BiFE9Otv.css} +1 -1
- package/dist/simulator/assets/{simulator-mini-app-CYtW6bMd.js → simulator-mini-app-BfNuDF3R.js} +2 -2
- package/dist/simulator/assets/{simulator-cV1PERi6.js → simulator-sf-D0mhw.js} +3 -3
- package/dist/simulator/simulator.html +1 -1
- package/package.json +4 -4
- package/dist/renderer/assets/index-Bx4XqFQ2.js +0 -50
- package/dist/renderer/assets/ipc-transport-CVp38I_M.css +0 -1
- package/dist/renderer/assets/select-BgWAbMot.js +0 -2
- package/dist/renderer/assets/settings-api-Dit4oL6S.js +0 -2
- package/dist/simulator/assets/device-shell-BEnc2k-W.js +0 -2
package/README.md
CHANGED
|
@@ -42,12 +42,6 @@ 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
|
-
},
|
|
51
45
|
icon: '/path/to/icon.png',
|
|
52
46
|
menuBuilder: (mainWindow, menuCtx) => {
|
|
53
47
|
// menuCtx 是 MenuContext(只读 menu 相关状态)
|
|
@@ -187,10 +181,6 @@ src/
|
|
|
187
181
|
| `preloadPath` | `string` | 内置 | 统一的 host 级 preload 入口;native-host simulator(WCV)自动跑其 `.cjs` sibling(`cjsSiblingPreloadPath`) |
|
|
188
182
|
| `apiNamespaces` | `string[]` | `[]` | 自定义 API 命名空间(如 `['qd']`) |
|
|
189
183
|
| `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` 返回按钮的点击 |
|
|
194
184
|
| `headerHeight` | `number` | — | **已废弃,运行时忽略**:头部栏恒为 40px(`HEADER_H`);需要自定义工具栏请用 host toolbar WCV |
|
|
195
185
|
|
|
196
186
|
### WorkbenchAppConfig(扩展 WorkbenchConfig)
|
|
@@ -218,78 +208,6 @@ src/
|
|
|
218
208
|
| `instance.registerSimulatorApi(name, handler)` | 注册 simulator 自定义 API,小程序里 `wx.<name>()` 调用(详见下方"Simulator 自定义 API")。返回 `Disposable` |
|
|
219
209
|
| `instance.ipc` | `IpcRegistry` 实例,`instance.ipc.handle(channel, fn)` 注册自定义 IPC;已绑定 `senderPolicy` 网关 |
|
|
220
210
|
| `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` | 可选禁用态 |
|
|
293
211
|
|
|
294
212
|
### 内置面板 ID
|
|
295
213
|
|
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,
|
|
19
|
+
export type { WorkbenchConfig, WorkbenchAppConfig, CompilationAdapter, ProjectSession, BuiltinPanelId, BuiltinModuleId, UpdateChecker, UpdateInfo, } from '../shared/types.js';
|
|
20
20
|
//# sourceMappingURL=api.d.ts.map
|
package/dist/main/app/app.d.ts
CHANGED
|
@@ -15,10 +15,6 @@ 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;
|
|
22
18
|
automationServer?: AutomationServer;
|
|
23
19
|
updateManager?: UpdateManager;
|
|
24
20
|
dispose: () => Promise<void>;
|
package/dist/main/app/app.js
CHANGED
|
@@ -149,10 +149,6 @@ 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,
|
|
156
152
|
// The host-supplied ProjectsProvider / template types in `shared/types`
|
|
157
153
|
// are structurally compatible with the main-process equivalents —
|
|
158
154
|
// these casts are safe; we re-narrow at the workspace-service /
|
|
@@ -356,8 +352,6 @@ export async function createDevtoolsRuntime(config = {}) {
|
|
|
356
352
|
// teardown, so a single dispose leaves no dead entry behind.
|
|
357
353
|
registerTrustedWindow: (win) => context.registry.add(registerTrustedWindow(context, win)),
|
|
358
354
|
registerSimulatorApi: (name, handler) => context.registry.add(toDisposable(context.simulatorApis.register(name, handler))),
|
|
359
|
-
refreshHeaderAvatar: () => context.notify.headerAvatarChanged(),
|
|
360
|
-
refreshHeaderActions: () => context.notify.headerActionsChanged(),
|
|
361
355
|
dispose: () => disposeContext(context),
|
|
362
356
|
};
|
|
363
357
|
if (config.onSetup) {
|
|
@@ -365,7 +365,14 @@ var WorkbenchSettingsChannel = {
|
|
|
365
365
|
SetTheme: "workbenchSettings:setTheme",
|
|
366
366
|
GetCdpStatus: "workbenchSettings:getCdpStatus",
|
|
367
367
|
GetMcpStatus: "workbenchSettings:getMcpStatus",
|
|
368
|
-
Init: "workbenchSettings:init"
|
|
368
|
+
Init: "workbenchSettings:init",
|
|
369
|
+
// Main → renderer push: the active color scheme flipped (OS change or in-app
|
|
370
|
+
// SetTheme). Payload is `isDark: boolean`. The app's CSS reacts to
|
|
371
|
+
// `prefers-color-scheme` automatically; this exists for the few JS consumers
|
|
372
|
+
// (Monaco's theme) that can't observe that media change — Electron does NOT
|
|
373
|
+
// dispatch the renderer's `matchMedia('(prefers-color-scheme)')` change event
|
|
374
|
+
// for programmatic `nativeTheme.themeSource` assignments.
|
|
375
|
+
ThemeChanged: "workbenchSettings:themeChanged"
|
|
369
376
|
};
|
|
370
377
|
var ProjectChannel = {
|
|
371
378
|
Open: "project:open",
|
|
@@ -467,13 +474,7 @@ var WindowChannel = {
|
|
|
467
474
|
NavigateBack: "window:navigateBack"
|
|
468
475
|
};
|
|
469
476
|
var AppChannel = {
|
|
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"
|
|
477
|
+
GetBranding: "app:getBranding"
|
|
477
478
|
};
|
|
478
479
|
var AutomationChannel = {
|
|
479
480
|
GetPort: "automation:port"
|
|
@@ -801,12 +802,21 @@ import { toDisposable } from "@dimina-kit/electron-deck/main";
|
|
|
801
802
|
function themeBg() {
|
|
802
803
|
return nativeTheme2.shouldUseDarkColors ? "#1a1a1a" : "#fafafa";
|
|
803
804
|
}
|
|
805
|
+
function simDeskBg() {
|
|
806
|
+
return nativeTheme2.shouldUseDarkColors ? "#121212" : "#e8e8e8";
|
|
807
|
+
}
|
|
804
808
|
function installThemeBackgroundSync() {
|
|
805
809
|
const apply = () => {
|
|
806
810
|
const bg = themeBg();
|
|
811
|
+
const isDark = nativeTheme2.shouldUseDarkColors;
|
|
807
812
|
for (const win of BrowserWindow.getAllWindows()) {
|
|
808
|
-
if (
|
|
813
|
+
if (win.isDestroyed()) continue;
|
|
814
|
+
try {
|
|
809
815
|
win.setBackgroundColor(bg);
|
|
816
|
+
if (!win.webContents.isDestroyed()) {
|
|
817
|
+
win.webContents.send(WorkbenchSettingsChannel.ThemeChanged, isDark);
|
|
818
|
+
}
|
|
819
|
+
} catch {
|
|
810
820
|
}
|
|
811
821
|
}
|
|
812
822
|
};
|
|
@@ -1001,12 +1011,6 @@ function createRendererNotifier(ctx) {
|
|
|
1001
1011
|
hostToolbarHeightChanged(height) {
|
|
1002
1012
|
sendToMain(ViewChannel.HostToolbarHeightChanged, height);
|
|
1003
1013
|
},
|
|
1004
|
-
headerAvatarChanged() {
|
|
1005
|
-
sendToMain(AppChannel.HeaderAvatarChanged);
|
|
1006
|
-
},
|
|
1007
|
-
headerActionsChanged() {
|
|
1008
|
-
sendToMain(AppChannel.HeaderActionsChanged);
|
|
1009
|
-
},
|
|
1010
1014
|
editorOpenFile(payload) {
|
|
1011
1015
|
sendToMain(EditorChannel.OpenFile, payload);
|
|
1012
1016
|
},
|
|
@@ -1030,7 +1034,7 @@ function createRendererNotifier(ctx) {
|
|
|
1030
1034
|
}
|
|
1031
1035
|
|
|
1032
1036
|
// src/main/services/views/view-manager.ts
|
|
1033
|
-
import { ipcMain as ipcMain2, shell as shell2, WebContentsView as WebContentsView2, webContents } from "electron";
|
|
1037
|
+
import { ipcMain as ipcMain2, nativeTheme as nativeTheme3, shell as shell2, WebContentsView as WebContentsView2, webContents } from "electron";
|
|
1034
1038
|
import path7 from "path";
|
|
1035
1039
|
|
|
1036
1040
|
// src/shared/open-in-editor.ts
|
|
@@ -1087,22 +1091,23 @@ function resourceUrlToProjectRelativePath(resourceUrl, expectedOrigin) {
|
|
|
1087
1091
|
}
|
|
1088
1092
|
|
|
1089
1093
|
// src/main/services/safe-area/index.ts
|
|
1090
|
-
function guestInsets(device) {
|
|
1094
|
+
function guestInsets(device, isTabPage2) {
|
|
1091
1095
|
const top = device?.safeAreaInsets.top ?? 0;
|
|
1092
|
-
|
|
1096
|
+
const bottom = isTabPage2 ? 0 : device?.safeAreaInsets.bottom ?? 0;
|
|
1097
|
+
return { top, topMax: top, right: 0, rightMax: 0, bottom, bottomMax: bottom, left: 0, leftMax: 0 };
|
|
1093
1098
|
}
|
|
1094
1099
|
function createSafeAreaController(options = {}) {
|
|
1095
|
-
const attached = /* @__PURE__ */ new
|
|
1096
|
-
function override(wc, device) {
|
|
1100
|
+
const attached = /* @__PURE__ */ new Map();
|
|
1101
|
+
function override(wc, device, isTabPage2) {
|
|
1097
1102
|
if (wc.isDestroyed()) return;
|
|
1098
|
-
void wc.debugger.sendCommand("Emulation.setSafeAreaInsetsOverride", { insets: guestInsets(device) }).catch((err) => {
|
|
1103
|
+
void wc.debugger.sendCommand("Emulation.setSafeAreaInsetsOverride", { insets: guestInsets(device, isTabPage2) }).catch((err) => {
|
|
1099
1104
|
console.warn("[safe-area] setSafeAreaInsetsOverride failed:", err instanceof Error ? err.message : err);
|
|
1100
1105
|
});
|
|
1101
1106
|
}
|
|
1102
1107
|
return {
|
|
1103
|
-
applyToGuest: (wc, device) => {
|
|
1108
|
+
applyToGuest: (wc, device, isTabPage2) => {
|
|
1104
1109
|
if (!wc || wc.isDestroyed() || attached.has(wc)) {
|
|
1105
|
-
if (wc && !wc.isDestroyed() && attached.has(wc)) override(wc, device);
|
|
1110
|
+
if (wc && !wc.isDestroyed() && attached.has(wc)) override(wc, device, isTabPage2);
|
|
1106
1111
|
return;
|
|
1107
1112
|
}
|
|
1108
1113
|
try {
|
|
@@ -1111,19 +1116,19 @@ function createSafeAreaController(options = {}) {
|
|
|
1111
1116
|
console.warn("[safe-area] debugger.attach failed; env(safe-area-inset-*) stays 0:", err instanceof Error ? err.message : err);
|
|
1112
1117
|
return;
|
|
1113
1118
|
}
|
|
1114
|
-
attached.
|
|
1119
|
+
attached.set(wc, isTabPage2);
|
|
1115
1120
|
if (options.connections) {
|
|
1116
1121
|
options.connections.acquire(wc).own(() => attached.delete(wc));
|
|
1117
1122
|
} else {
|
|
1118
1123
|
wc.once("destroyed", () => attached.delete(wc));
|
|
1119
1124
|
}
|
|
1120
|
-
override(wc, device);
|
|
1125
|
+
override(wc, device, isTabPage2);
|
|
1121
1126
|
},
|
|
1122
1127
|
reapplyAll: (device) => {
|
|
1123
|
-
for (const wc of attached) override(wc, device);
|
|
1128
|
+
for (const [wc, isTabPage2] of attached) override(wc, device, isTabPage2);
|
|
1124
1129
|
},
|
|
1125
1130
|
dispose: () => {
|
|
1126
|
-
for (const wc of attached) {
|
|
1131
|
+
for (const wc of attached.keys()) {
|
|
1127
1132
|
try {
|
|
1128
1133
|
if (!wc.isDestroyed()) wc.debugger.detach();
|
|
1129
1134
|
} catch {
|
|
@@ -2325,22 +2330,38 @@ function createViewManager(ctx) {
|
|
|
2325
2330
|
}
|
|
2326
2331
|
});
|
|
2327
2332
|
nativeSimulatorView = view;
|
|
2328
|
-
view.setBackgroundColor(
|
|
2333
|
+
view.setBackgroundColor(simDeskBg());
|
|
2329
2334
|
const simWc = view.webContents;
|
|
2335
|
+
const syncDeskBg = () => {
|
|
2336
|
+
try {
|
|
2337
|
+
if (!simWc.isDestroyed()) view.setBackgroundColor(simDeskBg());
|
|
2338
|
+
} catch {
|
|
2339
|
+
}
|
|
2340
|
+
};
|
|
2341
|
+
nativeTheme3.on("updated", syncDeskBg);
|
|
2342
|
+
ctx.connections.acquire(simWc).own(() => nativeTheme3.removeListener("updated", syncDeskBg));
|
|
2330
2343
|
attachNativeCustomApiBridge(simWc);
|
|
2344
|
+
const pendingGuestIsTab = [];
|
|
2331
2345
|
simWc.on("will-attach-webview", (_event, webPreferences, params) => {
|
|
2332
2346
|
;
|
|
2333
2347
|
webPreferences.partition = partition;
|
|
2334
2348
|
params.partition = partition;
|
|
2335
2349
|
webPreferences.contextIsolation = false;
|
|
2336
2350
|
webPreferences.sandbox = false;
|
|
2351
|
+
let isTab = false;
|
|
2352
|
+
try {
|
|
2353
|
+
isTab = new URL(params.src).searchParams.get("isTab") === "1";
|
|
2354
|
+
} catch {
|
|
2355
|
+
}
|
|
2356
|
+
pendingGuestIsTab.push(isTab);
|
|
2337
2357
|
});
|
|
2338
2358
|
simWc.on("did-attach-webview", (_event, guestWc) => {
|
|
2339
2359
|
try {
|
|
2340
2360
|
guestWc.setZoomFactor(currentZoomFactor);
|
|
2341
2361
|
} catch {
|
|
2342
2362
|
}
|
|
2343
|
-
|
|
2363
|
+
const isTabGuest = pendingGuestIsTab.shift() ?? false;
|
|
2364
|
+
safeArea.applyToGuest(guestWc, ctx.bridge?.getDevice() ?? null, isTabGuest);
|
|
2344
2365
|
guestWc.setWindowOpenHandler(({ url }) => handleWindowOpenExternal(url));
|
|
2345
2366
|
guestWc.on("will-navigate", (e, url) => {
|
|
2346
2367
|
try {
|
|
@@ -3119,11 +3140,7 @@ function createWorkbenchContext(opts) {
|
|
|
3119
3140
|
rendererDir: opts.rendererDir,
|
|
3120
3141
|
apiNamespaces: opts.apiNamespaces ?? [],
|
|
3121
3142
|
appName: opts.appName ?? "Dimina DevTools",
|
|
3122
|
-
brandingProvider: opts.brandingProvider
|
|
3123
|
-
headerAvatarProvider: opts.headerAvatarProvider,
|
|
3124
|
-
headerAvatarActionHandler: opts.headerAvatarActionHandler,
|
|
3125
|
-
headerActionsProvider: opts.headerActionsProvider,
|
|
3126
|
-
headerActionHandler: opts.headerActionHandler
|
|
3143
|
+
brandingProvider: opts.brandingProvider
|
|
3127
3144
|
};
|
|
3128
3145
|
ctx.registry = new DisposableRegistry3();
|
|
3129
3146
|
ctx.connections = createConnectionRegistry();
|
|
@@ -3223,70 +3240,10 @@ function installAppMenu(ctx) {
|
|
|
3223
3240
|
}
|
|
3224
3241
|
|
|
3225
3242
|
// 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
|
-
}
|
|
3274
3243
|
function registerAppIpc(ctx) {
|
|
3275
3244
|
return new IpcRegistry(ctx.senderPolicy).handle(AppChannel.GetBranding, async () => {
|
|
3276
3245
|
if (ctx.brandingProvider) return ctx.brandingProvider();
|
|
3277
3246
|
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);
|
|
3290
3247
|
});
|
|
3291
3248
|
}
|
|
3292
3249
|
|
|
@@ -3334,7 +3291,6 @@ var SimulatorSetDeviceInfoSchema = z3.tuple([
|
|
|
3334
3291
|
screenWidth: z3.number().int().min(100).max(4e3),
|
|
3335
3292
|
screenHeight: z3.number().int().min(100).max(4e3),
|
|
3336
3293
|
statusBarHeight: z3.number().finite().min(0).max(400),
|
|
3337
|
-
safeAreaBottom: z3.number().finite().min(0).max(400),
|
|
3338
3294
|
notchType: z3.enum(["none", "notch", "dynamic-island"]),
|
|
3339
3295
|
safeAreaInsets: z3.object({
|
|
3340
3296
|
top: z3.number().finite().min(0).max(400),
|
|
@@ -8895,10 +8851,6 @@ function createContext(config, mainWindow, rendererDir2) {
|
|
|
8895
8851
|
appName: config.appName,
|
|
8896
8852
|
apiNamespaces: config.apiNamespaces,
|
|
8897
8853
|
brandingProvider: config.brandingProvider,
|
|
8898
|
-
headerAvatarProvider: config.headerAvatarProvider,
|
|
8899
|
-
headerAvatarActionHandler: config.headerAvatarActionHandler,
|
|
8900
|
-
headerActionsProvider: config.headerActionsProvider,
|
|
8901
|
-
headerActionHandler: config.headerActionHandler,
|
|
8902
8854
|
// The host-supplied ProjectsProvider / template types in `shared/types`
|
|
8903
8855
|
// are structurally compatible with the main-process equivalents —
|
|
8904
8856
|
// these casts are safe; we re-narrow at the workspace-service /
|
|
@@ -9035,8 +8987,6 @@ async function createDevtoolsRuntime(config = {}) {
|
|
|
9035
8987
|
// teardown, so a single dispose leaves no dead entry behind.
|
|
9036
8988
|
registerTrustedWindow: (win) => context.registry.add(registerTrustedWindow(context, win)),
|
|
9037
8989
|
registerSimulatorApi: (name, handler) => context.registry.add(toDisposable8(context.simulatorApis.register(name, handler))),
|
|
9038
|
-
refreshHeaderAvatar: () => context.notify.headerAvatarChanged(),
|
|
9039
|
-
refreshHeaderActions: () => context.notify.headerActionsChanged(),
|
|
9040
8990
|
dispose: () => disposeContext(context)
|
|
9041
8991
|
};
|
|
9042
8992
|
if (config.onSetup) {
|
package/dist/main/ipc/app.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import type { WorkbenchContext } from '../services/workbench-context.js';
|
|
2
2
|
import type { Disposable } from '@dimina-kit/electron-deck/main';
|
|
3
|
-
|
|
4
|
-
export declare function registerAppIpc(ctx: AppIpcContext): Disposable;
|
|
5
|
-
export {};
|
|
3
|
+
export declare function registerAppIpc(ctx: Pick<WorkbenchContext, 'brandingProvider' | 'appName' | 'senderPolicy'>): Disposable;
|
|
6
4
|
//# sourceMappingURL=app.d.ts.map
|
package/dist/main/ipc/app.js
CHANGED
|
@@ -1,91 +1,11 @@
|
|
|
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
|
-
}
|
|
64
3
|
export function registerAppIpc(ctx) {
|
|
65
4
|
return new IpcRegistry(ctx.senderPolicy)
|
|
66
5
|
.handle(AppChannel.GetBranding, async () => {
|
|
67
6
|
if (ctx.brandingProvider)
|
|
68
7
|
return ctx.brandingProvider();
|
|
69
8
|
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);
|
|
89
9
|
});
|
|
90
10
|
}
|
|
91
11
|
//# sourceMappingURL=app.js.map
|
|
@@ -65,10 +65,6 @@ 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;
|
|
72
68
|
/**
|
|
73
69
|
* Ask the main renderer's Monaco editor to open a project file at a position.
|
|
74
70
|
* Drives the "click a console file link → open in editor" pipeline.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ProjectChannel, WindowChannel, SettingsChannel, PopoverChannel, WorkbenchSettingsChannel, EditorChannel, ViewChannel,
|
|
1
|
+
import { ProjectChannel, WindowChannel, SettingsChannel, PopoverChannel, WorkbenchSettingsChannel, EditorChannel, ViewChannel, } 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,12 +39,6 @@ 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
|
-
},
|
|
48
42
|
editorOpenFile(payload) {
|
|
49
43
|
sendToMain(EditorChannel.OpenFile, payload);
|
|
50
44
|
},
|
|
@@ -3,10 +3,12 @@ import type { ConnectionRegistry } from '@dimina-kit/electron-deck/main';
|
|
|
3
3
|
import type { NativeDeviceInfo } from '../../../shared/ipc-channels.js';
|
|
4
4
|
export interface SafeAreaController {
|
|
5
5
|
/** Attach the debugger to a freshly-attached render-host guest and push the
|
|
6
|
-
* current device's insets.
|
|
7
|
-
*
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
* current device's insets. `isTabPage` selects the bottom-inset policy (0 for
|
|
7
|
+
* tab pages, the real inset for full-bleed non-tab pages). No-op (warn) if the
|
|
8
|
+
* guest is already claimed by an external CDP client — env then stays 0. */
|
|
9
|
+
applyToGuest(guestWc: WebContents, device: NativeDeviceInfo | null, isTabPage: boolean): void;
|
|
10
|
+
/** Re-push insets to every still-attached guest after a device change (each
|
|
11
|
+
* guest keeps the page type it attached with). */
|
|
10
12
|
reapplyAll(device: NativeDeviceInfo | null): void;
|
|
11
13
|
/** Detach from all guests (teardown). */
|
|
12
14
|
dispose(): void;
|
|
@@ -1,25 +1,31 @@
|
|
|
1
|
-
function guestInsets(device) {
|
|
1
|
+
function guestInsets(device, isTabPage) {
|
|
2
2
|
const top = device?.safeAreaInsets.top ?? 0;
|
|
3
|
-
|
|
3
|
+
// A tab page's content sits above the shell-drawn tabBar (which fills the
|
|
4
|
+
// bottom safe area), so it never borders the bottom unsafe zone. A non-tab
|
|
5
|
+
// page is full-bleed to the device bottom, so surface the real inset for its
|
|
6
|
+
// own `env(safe-area-inset-bottom)` opt-in.
|
|
7
|
+
const bottom = isTabPage ? 0 : (device?.safeAreaInsets.bottom ?? 0);
|
|
8
|
+
return { top, topMax: top, right: 0, rightMax: 0, bottom, bottomMax: bottom, left: 0, leftMax: 0 };
|
|
4
9
|
}
|
|
5
10
|
export function createSafeAreaController(options = {}) {
|
|
6
|
-
// Guests we successfully attached `wc.debugger` to (
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
// Guests we successfully attached `wc.debugger` to (value = the page's
|
|
12
|
+
// `isTabPage`, fixed for the guest's life — it's one page). So we don't
|
|
13
|
+
// re-attach (throws), and a device-change reapply reuses the same policy.
|
|
14
|
+
const attached = new Map();
|
|
15
|
+
function override(wc, device, isTabPage) {
|
|
10
16
|
if (wc.isDestroyed())
|
|
11
17
|
return;
|
|
12
18
|
void wc.debugger
|
|
13
|
-
.sendCommand('Emulation.setSafeAreaInsetsOverride', { insets: guestInsets(device) })
|
|
19
|
+
.sendCommand('Emulation.setSafeAreaInsetsOverride', { insets: guestInsets(device, isTabPage) })
|
|
14
20
|
.catch((err) => {
|
|
15
21
|
console.warn('[safe-area] setSafeAreaInsetsOverride failed:', err instanceof Error ? err.message : err);
|
|
16
22
|
});
|
|
17
23
|
}
|
|
18
24
|
return {
|
|
19
|
-
applyToGuest: (wc, device) => {
|
|
25
|
+
applyToGuest: (wc, device, isTabPage) => {
|
|
20
26
|
if (!wc || wc.isDestroyed() || attached.has(wc)) {
|
|
21
27
|
if (wc && !wc.isDestroyed() && attached.has(wc))
|
|
22
|
-
override(wc, device);
|
|
28
|
+
override(wc, device, isTabPage);
|
|
23
29
|
return;
|
|
24
30
|
}
|
|
25
31
|
try {
|
|
@@ -31,21 +37,21 @@ export function createSafeAreaController(options = {}) {
|
|
|
31
37
|
console.warn('[safe-area] debugger.attach failed; env(safe-area-inset-*) stays 0:', err instanceof Error ? err.message : err);
|
|
32
38
|
return;
|
|
33
39
|
}
|
|
34
|
-
attached.
|
|
40
|
+
attached.set(wc, isTabPage);
|
|
35
41
|
if (options.connections) {
|
|
36
42
|
options.connections.acquire(wc).own(() => attached.delete(wc));
|
|
37
43
|
}
|
|
38
44
|
else {
|
|
39
45
|
wc.once('destroyed', () => attached.delete(wc));
|
|
40
46
|
}
|
|
41
|
-
override(wc, device);
|
|
47
|
+
override(wc, device, isTabPage);
|
|
42
48
|
},
|
|
43
49
|
reapplyAll: (device) => {
|
|
44
|
-
for (const wc of attached)
|
|
45
|
-
override(wc, device);
|
|
50
|
+
for (const [wc, isTabPage] of attached)
|
|
51
|
+
override(wc, device, isTabPage);
|
|
46
52
|
},
|
|
47
53
|
dispose: () => {
|
|
48
|
-
for (const wc of attached) {
|
|
54
|
+
for (const wc of attached.keys()) {
|
|
49
55
|
try {
|
|
50
56
|
if (!wc.isDestroyed())
|
|
51
57
|
wc.debugger.detach();
|
|
@@ -403,8 +403,10 @@ export function setupSimulatorStorage(host, options) {
|
|
|
403
403
|
const filtered = prefix
|
|
404
404
|
? result.entries.filter(([key]) => key.startsWith(prefix))
|
|
405
405
|
: result.entries;
|
|
406
|
-
// Keys are returned with their `${appId}_` prefix intact
|
|
407
|
-
//
|
|
406
|
+
// Keys are returned with their `${appId}_` prefix intact: every write
|
|
407
|
+
// path (Set/Remove) keys off the raw storage key. The Storage panel
|
|
408
|
+
// display-strips the prefix for readability (storage-panel.tsx), so the
|
|
409
|
+
// wire keys stay raw while the UI shows clean, Chrome-like keys.
|
|
408
410
|
return filtered.map(([key, value]) => ({ key, value }));
|
|
409
411
|
});
|
|
410
412
|
}
|