mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-11-09 20:07:43 +08:00
Merge pull request #7523 from deepseek-harness/07akioni/show-devtools-menu
fix(desktop): 在应用菜单显示 DevTools 开关
This commit is contained in:
@@ -15,8 +15,8 @@
|
||||
en: 127ea90553cb6af7
|
||||
zh: 7b1c5aec8558c684
|
||||
/deepseek-harness-desktop/application-overview:
|
||||
en: b57525e17b9638ab
|
||||
zh: 274fe8344f7c2202
|
||||
en: 1667e3d6ffddea4a
|
||||
zh: 4661a42b9ef4f673
|
||||
/deepseek-harness-desktop/terminal-command:
|
||||
en: 5f2d25a1d8472647
|
||||
zh: 2acb5c5d7d1917e2
|
||||
|
||||
@@ -53,7 +53,7 @@ A server-expired account credential returns to Welcome when no official API key
|
||||
|
||||
Desktop microphone access is restricted to audio requests from the primary `dsh-app://app` frame. macOS uses system microphone authorization, a packaged usage description, and `com.apple.security.device.audio-input` in the main application and Helper signatures.
|
||||
|
||||
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.
|
||||
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. The application menu shows a localized “Developer tools” command in a separate group before Hide and Quit (before Exit on Windows). Windows exposes it in the titlebar application popup; its hidden native application menu registers the shortcuts. macOS and Linux keep a hidden F12 shortcut alias in their application menus. Update overlays and packaged embedded browser guests disable DevTools.
|
||||
|
||||
<a id="terminal-command"></a>
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ Desktop Host 的 Platform API 请求与更新策略请求用相同的 Platform
|
||||
|
||||
桌面麦克风访问仅允许主 `dsh-app://app` 页面发起的音频请求。macOS 使用系统麦克风授权、随包用途说明,以及主应用与 Helper 签名中的 `com.apple.security.device.audio-input` 权限。
|
||||
|
||||
按 F12(多媒体功能键键盘上为 Fn+F12)、macOS 的 Command+Option+I 或 Windows 的 Ctrl+Shift+I,可切换当前获得焦点的应用页面的 DevTools,打包版本同样支持。这些原生快捷键通过隐藏的应用菜单项注册。更新遮罩和打包版本的内嵌浏览器禁用 DevTools。
|
||||
按 F12(多媒体功能键键盘上为 Fn+F12)、macOS 的 Command+Option+I 或 Windows 的 Ctrl+Shift+I,可切换当前获得焦点的应用页面的 DevTools,打包版本同样支持。应用菜单在隐藏和退出命令之前的独立分组中显示本地化的“开发者工具”命令。Windows 在标题栏的应用弹出菜单中显示开关,通过隐藏的原生应用菜单注册快捷键;macOS 和 Linux 的应用菜单保留隐藏的 F12 快捷键别名。更新遮罩和打包版本的内嵌浏览器禁用 DevTools。
|
||||
|
||||
<a id="terminal-command"></a>
|
||||
|
||||
|
||||
@@ -104,6 +104,7 @@ export const en = {
|
||||
welcomeContinueFailed: 'Could not open the workspace. Please try again.',
|
||||
checkUpdatesMenu: 'Check for Updates…',
|
||||
reloadPageMenu: 'Reload Page',
|
||||
toggleDevToolsMenu: 'Developer tools',
|
||||
restartAppHostMenu: 'Restart App and Host',
|
||||
updateCheckFailedTitle: 'Update Check Failed',
|
||||
updateCheckFailed: 'Could not check for updates. Please try again later.',
|
||||
@@ -283,6 +284,7 @@ export const zh = {
|
||||
welcomeContinueFailed: '无法打开工作区,请重试。',
|
||||
checkUpdatesMenu: '检查更新…',
|
||||
reloadPageMenu: '刷新页面',
|
||||
toggleDevToolsMenu: '开发者工具',
|
||||
restartAppHostMenu: '重启应用与 Host',
|
||||
updateCheckFailedTitle: '更新检查失败',
|
||||
updateCheckFailed: '检查更新失败,请稍后重试。',
|
||||
@@ -460,6 +462,7 @@ export const ja = {
|
||||
welcomeContinueFailed: 'ワークスペースを開けませんでした。もう一度お試しください。',
|
||||
checkUpdatesMenu: 'アップデートを確認…',
|
||||
reloadPageMenu: 'ページを再読み込み',
|
||||
toggleDevToolsMenu: '開発者ツール',
|
||||
restartAppHostMenu: 'アプリとホストを再起動',
|
||||
updateCheckFailedTitle: 'アップデートの確認に失敗しました',
|
||||
updateCheckFailed: 'アップデートを確認できませんでした。後でもう一度お試しください。',
|
||||
@@ -637,6 +640,7 @@ export const es = {
|
||||
welcomeContinueFailed: 'No se pudo abrir el espacio de trabajo. Por favor inténtalo de nuevo.',
|
||||
checkUpdatesMenu: 'Buscar actualizaciones...',
|
||||
reloadPageMenu: 'Recargar página',
|
||||
toggleDevToolsMenu: 'Herramientas de desarrollo',
|
||||
restartAppHostMenu: 'Reiniciar la aplicación y el host',
|
||||
updateCheckFailedTitle: 'Error en la comprobación de actualización',
|
||||
updateCheckFailed: 'No se pudo buscar actualizaciones. Inténtelo de nuevo más tarde.',
|
||||
@@ -814,6 +818,7 @@ export const fr = {
|
||||
welcomeContinueFailed: "Impossible d'ouvrir l'espace de travail. Veuillez réessayer.",
|
||||
checkUpdatesMenu: 'Vérifiez les mises à jour…',
|
||||
reloadPageMenu: 'Recharger la page',
|
||||
toggleDevToolsMenu: 'Outils de développement',
|
||||
restartAppHostMenu: "Redémarrez l'application et l'hôte",
|
||||
updateCheckFailedTitle: 'Échec de la vérification de la mise à jour',
|
||||
updateCheckFailed: 'Impossible de vérifier les mises à jour. Veuillez réessayer plus tard.',
|
||||
@@ -991,6 +996,7 @@ export const de = {
|
||||
welcomeContinueFailed: 'Der Arbeitsbereich konnte nicht geöffnet werden. Bitte versuchen Sie es erneut.',
|
||||
checkUpdatesMenu: 'Nach Updates suchen…',
|
||||
reloadPageMenu: 'Seite neu laden',
|
||||
toggleDevToolsMenu: 'Entwicklertools',
|
||||
restartAppHostMenu: 'Anwendung und Host neu starten',
|
||||
updateCheckFailedTitle: 'Update-Überprüfung fehlgeschlagen',
|
||||
updateCheckFailed: 'Es konnte nicht nach Updates gesucht werden. Bitte versuchen Sie es später noch einmal.',
|
||||
@@ -1168,6 +1174,7 @@ export const ru = {
|
||||
welcomeContinueFailed: 'Не удалось открыть рабочую область. Пожалуйста, попробуйте еще раз.',
|
||||
checkUpdatesMenu: 'Проверить наличие обновлений…',
|
||||
reloadPageMenu: 'Обновить страницу',
|
||||
toggleDevToolsMenu: 'Инструменты разработчика',
|
||||
restartAppHostMenu: 'Перезапустите приложение и хост.',
|
||||
updateCheckFailedTitle: 'Проверка обновления не удалась',
|
||||
updateCheckFailed: 'Не удалось проверить наличие обновлений. Пожалуйста, повторите попытку позже.',
|
||||
@@ -1345,6 +1352,7 @@ export const pt = {
|
||||
welcomeContinueFailed: 'Não foi possível abrir o espaço de trabalho. Por favor, tente novamente.',
|
||||
checkUpdatesMenu: 'Verifique se há atualizações…',
|
||||
reloadPageMenu: 'Recarregar página',
|
||||
toggleDevToolsMenu: 'Ferramentas do desenvolvedor',
|
||||
restartAppHostMenu: 'Reinicie o aplicativo e o host',
|
||||
updateCheckFailedTitle: 'Falha na verificação de atualização',
|
||||
updateCheckFailed: 'Não foi possível verificar se há atualizações. Por favor, tente novamente mais tarde.',
|
||||
|
||||
@@ -1060,8 +1060,8 @@ async function main(): Promise<void> {
|
||||
{ label: currentDesktopLocale().messages.checkUpdatesMenu, click: () => { void openUpdatePrompt(true) } },
|
||||
...process.platform === 'darwin' || process.platform === 'win32'
|
||||
? [{ label: currentDesktopLocale().messages.cliCommandMenu, click: () => { void commandManager.show() } }] : [],
|
||||
{ type: 'separator' },
|
||||
...development ? [
|
||||
{ type: 'separator' as const },
|
||||
{ label: currentDesktopLocale().messages.reloadPageMenu, role: 'reload' as const },
|
||||
{ label: currentDesktopLocale().messages.restartAppHostMenu, click: () => {
|
||||
if (quitting) return
|
||||
@@ -1069,20 +1069,23 @@ async function main(): Promise<void> {
|
||||
quitWithoutConfirmation()
|
||||
} },
|
||||
] : [],
|
||||
{ role: 'toggleDevTools', label: currentDesktopLocale().messages.toggleDevToolsMenu },
|
||||
{ type: 'separator' },
|
||||
...hideCommands,
|
||||
{ role: 'quit', ...(darwin ? { label: currentDesktopLocale().messages.quitApplication }
|
||||
: process.platform === 'win32' ? { label: currentDesktopLocale().messages.exitApplication } : {}) },
|
||||
]
|
||||
const devToolsItems: MenuItemConstructorOptions[] = [
|
||||
const devToolsF12Shortcut: MenuItemConstructorOptions = { role: 'toggleDevTools', visible: false, accelerator: 'F12' }
|
||||
const windowsDevToolsShortcuts: MenuItemConstructorOptions[] = [
|
||||
{ role: 'toggleDevTools', visible: false },
|
||||
{ role: 'toggleDevTools', visible: false, accelerator: 'F12' },
|
||||
devToolsF12Shortcut,
|
||||
]
|
||||
const refreshApplicationMenu = (): void => {
|
||||
Menu.setApplicationMenu(Menu.buildFromTemplate(process.platform === 'win32' ? devToolsItems : [{
|
||||
label: darwin ? app.name : currentDesktopLocale().messages.application,
|
||||
submenu: [...applicationItems(), ...devToolsItems],
|
||||
}, ...platformMenus()]))
|
||||
Menu.setApplicationMenu(Menu.buildFromTemplate(process.platform === 'win32'
|
||||
? windowsDevToolsShortcuts : [{
|
||||
label: darwin ? app.name : currentDesktopLocale().messages.application,
|
||||
submenu: [...applicationItems(), devToolsF12Shortcut],
|
||||
}, ...platformMenus()]))
|
||||
tray?.relabel()
|
||||
}
|
||||
refreshApplicationMenu()
|
||||
|
||||
@@ -1,10 +1,27 @@
|
||||
[
|
||||
{
|
||||
"label": "About DeepSeek Harness",
|
||||
"role": "about"
|
||||
},
|
||||
{
|
||||
"type": "separator"
|
||||
},
|
||||
{
|
||||
"label": "Check for Updates…"
|
||||
},
|
||||
{
|
||||
"label": "Manage dsh Command…"
|
||||
},
|
||||
{
|
||||
"type": "separator"
|
||||
},
|
||||
{
|
||||
"role": "toggleDevTools",
|
||||
"label": "Developer tools"
|
||||
},
|
||||
{
|
||||
"type": "separator"
|
||||
},
|
||||
{
|
||||
"role": "hide",
|
||||
"label": "Hide DeepSeek Harness"
|
||||
@@ -17,6 +34,9 @@
|
||||
"role": "unhide",
|
||||
"label": "Show All"
|
||||
},
|
||||
{
|
||||
"type": "separator"
|
||||
},
|
||||
{
|
||||
"role": "quit",
|
||||
"label": "Quit DeepSeek Harness"
|
||||
|
||||
@@ -1,10 +1,27 @@
|
||||
[
|
||||
{
|
||||
"label": "关于 DeepSeek Harness",
|
||||
"role": "about"
|
||||
},
|
||||
{
|
||||
"type": "separator"
|
||||
},
|
||||
{
|
||||
"label": "检查更新…"
|
||||
},
|
||||
{
|
||||
"label": "管理 dsh 命令…"
|
||||
},
|
||||
{
|
||||
"type": "separator"
|
||||
},
|
||||
{
|
||||
"role": "toggleDevTools",
|
||||
"label": "开发者工具"
|
||||
},
|
||||
{
|
||||
"type": "separator"
|
||||
},
|
||||
{
|
||||
"role": "hide",
|
||||
"label": "隐藏 DeepSeek Harness"
|
||||
@@ -17,6 +34,9 @@
|
||||
"role": "unhide",
|
||||
"label": "显示全部"
|
||||
},
|
||||
{
|
||||
"type": "separator"
|
||||
},
|
||||
{
|
||||
"role": "quit",
|
||||
"label": "退出 DeepSeek Harness"
|
||||
|
||||
@@ -550,12 +550,49 @@ 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' })
|
||||
it.each(([
|
||||
['darwin', 'en'], ['darwin', 'zh-CN'], ['win32', 'en'], ['win32', 'zh-CN'], ['linux', 'en'], ['linux', 'zh-CN'],
|
||||
] as const).flatMap(([platform, locale]) => [
|
||||
[platform, locale, true] as const, [platform, locale, false] as const,
|
||||
]))('groups localized DevTools before quit on %s (%s, packaged=%s)', async (platform, locale, packaged) => {
|
||||
vi.stubGlobal('process', { ...process, platform })
|
||||
harness.app.isPackaged = packaged
|
||||
vi.spyOn(harness.app, 'getPreferredSystemLanguages').mockReturnValue([locale])
|
||||
await readyForUpdate()
|
||||
expect(applicationMenuItems().slice(-2)).toEqual([
|
||||
{ role: 'toggleDevTools', visible: false },
|
||||
{ role: 'toggleDevTools', visible: false, accelerator: 'F12' },
|
||||
const messages = locale === 'en' ? en : zh
|
||||
const menu = applicationMenuItems()
|
||||
const visible = menu.filter(item => item.visible !== false)
|
||||
expect(visible.map(item => item.role === 'quit' ? 'quit' : item.label ?? item.type)).toEqual([
|
||||
messages.aboutMenu, 'separator', messages.checkUpdatesMenu,
|
||||
...platform === 'linux' ? [] : [messages.cliCommandMenu],
|
||||
'separator', ...packaged ? [] : [messages.reloadPageMenu, messages.restartAppHostMenu],
|
||||
messages.toggleDevToolsMenu, 'separator',
|
||||
...platform === 'darwin' ? [messages.hideApplication, messages.hideOtherApplications, messages.showAllApplications, 'separator'] : [],
|
||||
'quit',
|
||||
])
|
||||
expect(visible.filter(item => item.role === 'toggleDevTools')).toEqual([
|
||||
{ role: 'toggleDevTools', label: messages.toggleDevToolsMenu },
|
||||
])
|
||||
if (platform === 'win32') {
|
||||
expect(harness.menu.mock.calls[0]![0]).toEqual([
|
||||
{ role: 'toggleDevTools', visible: false },
|
||||
{ role: 'toggleDevTools', visible: false, accelerator: 'F12' },
|
||||
])
|
||||
expect(menu.every(item => item.visible !== false)).toBe(true)
|
||||
} else {
|
||||
expect(menu.filter(item => item.visible === false)).toEqual([
|
||||
{ role: 'toggleDevTools', visible: false, accelerator: 'F12' },
|
||||
])
|
||||
}
|
||||
const sender = harness.windows[0]!.webContents
|
||||
const nextLocale = locale === 'en' ? 'zh' : 'en'
|
||||
harness.ipcOn.mock.calls.find(([channel]) => channel === DESKTOP_IPC.localeChanged)
|
||||
const refreshed = platform === 'win32' ? applicationMenuItems()
|
||||
: harness.menu.mock.lastCall![0][0]!.submenu as MenuItemConstructorOptions[]
|
||||
expect(refreshed.filter(item => item.role === 'toggleDevTools' && item.visible !== false)).toEqual([
|
||||
{ role: 'toggleDevTools', label: nextLocale === 'en' ? en.toggleDevToolsMenu : zh.toggleDevToolsMenu },
|
||||
])
|
||||
expect(harness.windows[0]!.options).toMatchObject({ webPreferences: { devTools: true } })
|
||||
})
|
||||
@@ -1031,7 +1068,7 @@ describe('desktop main startup', () => {
|
||||
expect(() => handler(event, 'application', NaN, 34)).toThrow('invalid popup request')
|
||||
const application = handler(event, 'application', 48, 34)
|
||||
expect(harness.menu.buildFromTemplate.mock.lastCall![0].map(item => item.label ?? item.type)).toEqual([
|
||||
'关于 DeepSeek Harness', 'separator', '检查更新…', '管理 dsh 命令…', 'separator', '退出',
|
||||
'关于 DeepSeek Harness', 'separator', '检查更新…', '管理 dsh 命令…', 'separator', zh.toggleDevToolsMenu, 'separator', '退出',
|
||||
])
|
||||
expect(harness.popup.mock.lastCall![0]).toMatchObject({ window, x: 48, y: 34 })
|
||||
expect(harness.popup.mock.lastCall![0].callback).toBeTypeOf('function')
|
||||
@@ -1068,8 +1105,8 @@ describe('desktop main startup', () => {
|
||||
: ['Application', 'editMenu'])
|
||||
const application = template[0]!.submenu as MenuItemConstructorOptions[]
|
||||
expect(application.filter(item => item.visible !== false).map(describeItem)).toEqual(platform === 'darwin'
|
||||
? ['about', 'separator', en.checkUpdatesMenu, en.cliCommandMenu, 'separator', 'hide', 'hideOthers', 'unhide', 'separator', 'quit']
|
||||
: ['about', 'separator', en.checkUpdatesMenu, 'separator', 'quit'])
|
||||
? ['about', 'separator', en.checkUpdatesMenu, en.cliCommandMenu, 'separator', 'toggleDevTools', 'separator', 'hide', 'hideOthers', 'unhide', 'separator', 'quit']
|
||||
: ['about', 'separator', en.checkUpdatesMenu, 'separator', 'toggleDevTools', 'separator', 'quit'])
|
||||
expect(harness.menu.setApplicationMenu).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
@@ -1082,10 +1119,7 @@ describe('desktop main startup', () => {
|
||||
try {
|
||||
await import('../src/main.ts')
|
||||
await harness.preparing.promise
|
||||
const commands = applicationMenuItems().filter(item =>
|
||||
item.role === 'hide' || item.role === 'hideOthers' || item.role === 'unhide' || item.role === 'quit'
|
||||
|| item.label === en.checkUpdatesMenu || item.label === zh.checkUpdatesMenu
|
||||
|| item.label === en.cliCommandMenu || item.label === zh.cliCommandMenu)
|
||||
const commands = applicationMenuItems().filter(item => item.visible !== false)
|
||||
await expect(JSON.stringify(commands, null, 2) + '\n')
|
||||
.toMatchFileSnapshot(`./expected/application-menu-${locale}.json`)
|
||||
expect(harness.app.name).toBe('@deepseek-ai/dsh-desktop')
|
||||
|
||||
Reference in New Issue
Block a user