Merge pull request #4845 from deepseek-harness/dev/desktop-devtools

feat(desktop): enable hidden native DevTools shortcuts
This commit is contained in:
07akioni authored and GitHub committed 2026-09-21 21:07:26 +08:00
commit 8b993f43e0
5 files changed
+29 -7

No files matched your search

+2 -2
View File
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write apps/desktop/README.md
README.md: 4de8eb539bba42919fd29740bb2741566f460ab1
README.zh.md: 9e733caae70fc3fe4d0131548a14660cddbd2248
README.md: 16bf5fd64fc4ea8bfe03e6e84e37cb87115bd74b
README.zh.md: 500240148cc8fb0c90f6029caaa4fdf57881c83d
+2
View File
@@ -12,6 +12,8 @@ Creator and the Web Plugin Manager use Desktop’s bundled pnpm under Electron N
Desktop microphone access is restricted to audio requests from the primary `dsh-app://app` frame. macOS uses system microphone authorization and a packaged usage description.
Press F12 (Fn+F12 on media-key keyboards), Command+Option+I on macOS, or Ctrl+Shift+I on Windows to toggle DevTools for the focused application page, including in packaged builds. These native shortcuts use hidden application-menu items. Update overlays and packaged embedded browser guests disable DevTools.
## Key technical decisions
The original artwork lives in `resources/icon.png` and `resources/icon.svg`; platform adaptations retain the whale and gradients in `resources/icon-windows.*` and `resources/icon-macos.*`. Export each platform SVG as a transparent 1024×1024 PNG. Electron-builder generates the multi-size ICO for the Windows application, installer, and uninstaller ([Windows icon requirements](https://learn.microsoft.com/en-us/windows/apps/design/iconography/app-icon-construction)). The installation pages use matching artwork in both themes; the uninstaller's welcome and finish pages share `installer/assets/uninstaller-sidebar.png`, converted to a 164×314 BMP during preparation.
+2
View File
@@ -12,6 +12,8 @@ Creator 和 Web Plugin Manager 在 Electron Node 模式下使用 Desktop 内置
桌面麦克风访问仅允许主 `dsh-app://app` 页面发起的音频请求。macOS 使用系统麦克风授权与随包用途说明。
按 F12(多媒体功能键键盘上为 Fn+F12)、macOS 的 Command+Option+I 或 Windows 的 Ctrl+Shift+I,可切换当前获得焦点的应用页面的 DevTools,打包版本同样支持。这些原生快捷键通过隐藏的应用菜单项注册。更新遮罩和打包版本的内嵌浏览器禁用 DevTools。
## 关键技术决策
设计师原稿位于 `resources/icon.png` 和 `resources/icon.svg`;平台适配保留鲸鱼与渐变,分别位于 `resources/icon-windows.*` 和 `resources/icon-macos.*`。将各平台 SVG 导出为透明的 1024×1024 PNG。electron-builder 为 Windows 应用、安装程序和卸载程序生成多尺寸 ICO([Windows 图标要求](https://learn.microsoft.com/en-us/windows/apps/design/iconography/app-icon-construction))。安装页面在两种主题下使用匹配的图案;卸载程序的欢迎和完成页共用 `installer/assets/uninstaller-sidebar.png`,准备阶段将其转换为 164×314 BMP。
+7 -2
View File
@@ -151,6 +151,7 @@ function createWindow(preload: string, show = false, primary = false): BrowserWi
sandbox: true,
webSecurity: true,
webviewTag: primary,
devTools: true,
},
})
window.webContents.setWindowOpenHandler(({ url }) => {
@@ -672,9 +673,13 @@ async function main(): Promise<void> {
{ role: 'quit', ...(darwin ? { label: currentDesktopLocale().messages.quitApplication }
: process.platform === 'win32' ? { label: currentDesktopLocale().messages.exitApplication } : {}) },
]
Menu.setApplicationMenu(process.platform === 'win32' ? null : Menu.buildFromTemplate([{
const devToolsItems: MenuItemConstructorOptions[] = [
{ role: 'toggleDevTools', visible: false },
{ role: 'toggleDevTools', visible: false, accelerator: 'F12' },
]
Menu.setApplicationMenu(Menu.buildFromTemplate(process.platform === 'win32' ? devToolsItems : [{
label: darwin ? app.name : currentDesktopLocale().messages.application,
submenu: applicationItems(),
submenu: [...applicationItems(), ...devToolsItems],
}, ...platformMenus]))
if (process.platform === 'win32') {
+16 -3
View File
@@ -368,6 +368,16 @@ describe('desktop main startup', () => {
expect((await handler(new Request('dsh-app://unknown/update-dialog.html'))).status).toBe(404)
})
it('installs hidden native DevTools shortcuts in the macOS application menu', async () => {
vi.stubGlobal('process', { ...process, platform: 'darwin' })
await readyForUpdate()
expect(applicationMenuItems().slice(-2)).toEqual([
{ role: 'toggleDevTools', visible: false },
{ role: 'toggleDevTools', visible: false, accelerator: 'F12' },
])
expect(harness.windows[0]!.options).toMatchObject({ webPreferences: { devTools: true } })
})
it.each([
['darwin', true, 'en-US'],
['darwin', false, 'zh-CN'],
@@ -557,7 +567,10 @@ describe('desktop main startup', () => {
} else if (platform === 'win32') {
expect(window.options).toMatchObject({ titleBarStyle: 'hidden', titleBarOverlay: { height: WINDOWS_TITLEBAR_HEIGHT } })
expect(window.options).not.toHaveProperty('vibrancy')
expect(harness.menu.setApplicationMenu).toHaveBeenCalledWith(null)
expect(harness.menu.mock.calls[0]![0]).toEqual([
{ role: 'toggleDevTools', visible: false },
{ role: 'toggleDevTools', visible: false, accelerator: 'F12' },
])
} else {
expect(window.options).not.toHaveProperty('titleBarStyle')
expect(window.options).not.toHaveProperty('vibrancy')
@@ -665,7 +678,7 @@ describe('desktop main startup', () => {
window.webContents.mainFrame.url = 'dsh-app://unowned/index.html'
listener(event, 'zh-CN', '#fff', '#000')
expect(window.setTitleBarOverlay).not.toHaveBeenCalled()
expect(harness.menu.setApplicationMenu).toHaveBeenCalledExactlyOnceWith(null)
expect(harness.menu.setApplicationMenu).toHaveBeenCalledOnce()
})
it('maps Windows caption menus to localized native commands and rejects foreign popup requests', async () => {
@@ -719,7 +732,7 @@ describe('desktop main startup', () => {
? ['Desktop test', 'fileMenu', 'editMenu', 'windowMenu']
: ['Application', 'editMenu'])
const application = template[0]!.submenu as MenuItemConstructorOptions[]
expect(application.map(describeItem)).toEqual(platform === 'darwin'
expect(application.filter(item => item.visible !== false).map(describeItem)).toEqual(platform === 'darwin'
? ['about', 'separator', en.checkUpdatesMenu, 'separator', 'hide', 'hideOthers', 'unhide', 'separator', 'quit']
: ['about', 'separator', en.checkUpdatesMenu, 'separator', 'quit'])
expect(harness.menu.setApplicationMenu).toHaveBeenCalledOnce()