From fc13237023f155292c0445feaed10be68c917ff3 Mon Sep 17 00:00:00 2001 From: paisley <8197966+su8su@users.noreply.github.com> Date: Thu, 7 May 2026 11:02:02 +0800 Subject: [PATCH] fix: Windows zoom shortcuts and keep chat preview file after workspace tab (#982) --- electron/main/index.ts | 3 + electron/main/zoom-shortcuts.ts | 47 +++++++ src/components/file-preview/ArtifactPanel.tsx | 10 +- src/i18n/locales/en/skills.json | 1 + src/i18n/locales/ja/skills.json | 1 + src/i18n/locales/ru/skills.json | 1 + src/i18n/locales/zh/skills.json | 1 + src/pages/Skills/index.tsx | 117 +++++++++++++--- src/stores/artifact-panel.ts | 13 +- src/stores/skills.ts | 41 +++++- tests/e2e/chat-file-changes.spec.ts | 96 +++++++++++++ tests/e2e/skills-gateway-readiness.spec.ts | 49 +++++++ tests/e2e/zoom-shortcuts.spec.ts | 46 +++++++ tests/unit/artifact-panel.test.tsx | 33 ++++- .../skills-page-gateway-readiness.test.tsx | 129 ++++++++++++++++++ tests/unit/zoom-shortcuts.test.ts | 36 +++++ 16 files changed, 585 insertions(+), 39 deletions(-) create mode 100644 electron/main/zoom-shortcuts.ts create mode 100644 tests/e2e/skills-gateway-readiness.spec.ts create mode 100644 tests/e2e/zoom-shortcuts.spec.ts create mode 100644 tests/unit/skills-page-gateway-readiness.test.tsx create mode 100644 tests/unit/zoom-shortcuts.test.ts diff --git a/electron/main/index.ts b/electron/main/index.ts index 06a4bc0..35f853d 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -9,6 +9,7 @@ import { GatewayManager } from '../gateway/manager'; import { registerIpcHandlers } from './ipc-handlers'; import { createTray } from './tray'; import { createMenu } from './menu'; +import { registerZoomShortcuts } from './zoom-shortcuts'; import { appUpdater, registerUpdateHandlers } from './updater'; import { logger } from '../utils/logger'; @@ -182,6 +183,8 @@ function createWindow(): BrowserWindow { show: false, }); + registerZoomShortcuts(win); + // Handle external links — only allow safe protocols to prevent arbitrary // command execution via shell.openExternal() (e.g. file://, ms-msdt:, etc.) win.webContents.setWindowOpenHandler(({ url }) => { diff --git a/electron/main/zoom-shortcuts.ts b/electron/main/zoom-shortcuts.ts new file mode 100644 index 0000000..61ca3d5 --- /dev/null +++ b/electron/main/zoom-shortcuts.ts @@ -0,0 +1,47 @@ +import type { BrowserWindow } from 'electron'; + +export type ZoomShortcutAction = 'in' | 'out' | 'reset'; + +type ZoomShortcutInput = Pick; + +export function getZoomShortcutAction(input: ZoomShortcutInput): ZoomShortcutAction | null { + if ((!input.control && !input.meta) || input.alt) { + return null; + } + + const key = input.key.toLowerCase(); + + if (key === '+' || key === '=' || input.code === 'Equal' || input.code === 'NumpadAdd') { + return 'in'; + } + + if (key === '-' || input.code === 'Minus' || input.code === 'NumpadSubtract') { + return 'out'; + } + + if (key === '0' || input.code === 'Digit0' || input.code === 'Numpad0') { + return 'reset'; + } + + return null; +} + +export function registerZoomShortcuts(win: BrowserWindow): void { + win.webContents.on('before-input-event', (event, input) => { + const action = getZoomShortcutAction(input); + + if (!action) { + return; + } + + event.preventDefault(); + + if (action === 'reset') { + win.webContents.setZoomLevel(0); + return; + } + + const delta = action === 'in' ? 1 : -1; + win.webContents.setZoomLevel(win.webContents.getZoomLevel() + delta); + }); +} diff --git a/src/components/file-preview/ArtifactPanel.tsx b/src/components/file-preview/ArtifactPanel.tsx index 8f2dc59..a8c7ef1 100644 --- a/src/components/file-preview/ArtifactPanel.tsx +++ b/src/components/file-preview/ArtifactPanel.tsx @@ -61,11 +61,12 @@ export function ArtifactPanel({ files, agent, runStartedAt, refreshSignal }: Art }; return ( -
+
{richFocusedFile ? ( } label={t('generatedFiles.openFolder', '打开文件夹')} active={false} @@ -73,6 +74,7 @@ export function ArtifactPanel({ files, agent, runStartedAt, refreshSignal }: Art /> ) : ( } label={t('artifactPanel.tabs.changes', '变更')} active={visibleTab === 'changes'} @@ -80,6 +82,7 @@ export function ArtifactPanel({ files, agent, runStartedAt, refreshSignal }: Art /> )} } label={t('artifactPanel.tabs.preview', '预览')} active={visibleTab === 'preview'} @@ -87,6 +90,7 @@ export function ArtifactPanel({ files, agent, runStartedAt, refreshSignal }: Art /> {WORKSPACE_BROWSER_ENABLED && ( } label={t('artifactPanel.tabs.browser', '工作空间')} active={visibleTab === 'browser'} @@ -129,15 +133,17 @@ export function ArtifactPanel({ files, agent, runStartedAt, refreshSignal }: Art } interface PanelTabButtonProps { + testId?: string; icon: React.ReactNode; label: string; active: boolean; onClick: () => void; } -function PanelTabButton({ icon, label, active, onClick }: PanelTabButtonProps) { +function PanelTabButton({ testId, icon, label, active, onClick }: PanelTabButtonProps) { return (