From a21fc9191521f61107bfd6a06cb2623aa9044577 Mon Sep 17 00:00:00 2001 From: Phantom <59059173+EurFelux@users.noreply.github.com> Date: Tue, 19 Aug 2025 21:32:53 +0800 Subject: [PATCH] fix: unexpected quitting full screen mode (#9200) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(Inputbar): 修正拼写错误,将expend改为expand * fix: 修复Escape键事件冒泡问题并改进全屏处理 修复多个组件中Escape键事件未阻止冒泡的问题 添加全屏控制IPC通道 将全屏退出逻辑移至渲染进程处理 移除主进程中冗余的全屏退出处理代码 * fix(SelectModelPopup): 修复键盘事件处理并移除无效的useEffect 将键盘事件监听从window移动到Modal容器,避免事件冒泡问题 移除无效的useEffect并更新键盘事件类型定义 * fix(QuickPanel): 拦截window上的keydown事件 * fix(QuickPanel): 修复事件监听器移除时未使用相同参数的问题 * fix(TopView): 修复左侧导航栏布局崩坏问题 * fix: 修正变量名拼写错误,将expended改为expanded * Revert "fix(SelectModelPopup): 修复键盘事件处理并移除无效的useEffect" This reverts commit 4211780b95c737989a62e6598728ff6e17b6b61b. --- packages/shared/IpcChannel.ts | 1 + src/main/ipc.ts | 4 ++ src/main/services/WindowService.ts | 40 +++++++++---------- src/preload/index.ts | 1 + .../src/components/CollapsibleSearchBar.tsx | 1 + src/renderer/src/components/ContentSearch.tsx | 1 + .../src/components/EditableNumber/index.tsx | 1 + .../Popups/SelectModelPopup/popup.tsx | 1 + .../src/components/QuickPanel/view.tsx | 13 +++--- src/renderer/src/components/TopView/index.tsx | 21 ++++++++++ src/renderer/src/hooks/useInPlaceEdit.ts | 1 + .../src/pages/home/Inputbar/Inputbar.tsx | 25 ++++++------ .../src/pages/home/Inputbar/InputbarTools.tsx | 8 ++-- .../pages/settings/ProviderSettings/index.tsx | 1 + 14 files changed, 77 insertions(+), 42 deletions(-) diff --git a/packages/shared/IpcChannel.ts b/packages/shared/IpcChannel.ts index bae5c54a7e..20bc852e7e 100644 --- a/packages/shared/IpcChannel.ts +++ b/packages/shared/IpcChannel.ts @@ -35,6 +35,7 @@ export enum IpcChannel { App_InstallBunBinary = 'app:install-bun-binary', App_LogToMain = 'app:log-to-main', App_SaveData = 'app:save-data', + App_SetFullScreen = 'app:set-full-screen', App_MacIsProcessTrusted = 'app:mac-is-process-trusted', App_MacRequestProcessTrust = 'app:mac-request-process-trust', diff --git a/src/main/ipc.ts b/src/main/ipc.ts index 98d384c7cc..f094e8d580 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -191,6 +191,10 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) { }) } + ipcMain.handle(IpcChannel.App_SetFullScreen, (_, value: boolean): void => { + mainWindow.setFullScreen(value) + }) + ipcMain.handle(IpcChannel.Config_Set, (_, key: string, value: any, isNotify: boolean = false) => { configManager.set(key, value, isNotify) }) diff --git a/src/main/services/WindowService.ts b/src/main/services/WindowService.ts index 185901322f..e2c2dc4866 100644 --- a/src/main/services/WindowService.ts +++ b/src/main/services/WindowService.ts @@ -223,26 +223,26 @@ export class WindowService { }) // 添加Escape键退出全屏的支持 - mainWindow.webContents.on('before-input-event', (event, input) => { - // 当按下Escape键且窗口处于全屏状态时退出全屏 - if (input.key === 'Escape' && !input.alt && !input.control && !input.meta && !input.shift) { - if (mainWindow.isFullScreen()) { - // 获取 shortcuts 配置 - const shortcuts = configManager.getShortcuts() - const exitFullscreenShortcut = shortcuts.find((s) => s.key === 'exit_fullscreen') - if (exitFullscreenShortcut == undefined) { - mainWindow.setFullScreen(false) - return - } - if (exitFullscreenShortcut?.enabled) { - event.preventDefault() - mainWindow.setFullScreen(false) - return - } - } - } - return - }) + // mainWindow.webContents.on('before-input-event', (event, input) => { + // // 当按下Escape键且窗口处于全屏状态时退出全屏 + // if (input.key === 'Escape' && !input.alt && !input.control && !input.meta && !input.shift) { + // if (mainWindow.isFullScreen()) { + // // 获取 shortcuts 配置 + // const shortcuts = configManager.getShortcuts() + // const exitFullscreenShortcut = shortcuts.find((s) => s.key === 'exit_fullscreen') + // if (exitFullscreenShortcut == undefined) { + // mainWindow.setFullScreen(false) + // return + // } + // if (exitFullscreenShortcut?.enabled) { + // event.preventDefault() + // mainWindow.setFullScreen(false) + // return + // } + // } + // } + // return + // }) } private setupWebContentsHandlers(mainWindow: BrowserWindow) { diff --git a/src/preload/index.ts b/src/preload/index.ts index 9cf68543a8..1a630a19ee 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -76,6 +76,7 @@ const api = { clearCache: () => ipcRenderer.invoke(IpcChannel.App_ClearCache), logToMain: (source: LogSourceWithContext, level: LogLevel, message: string, data: any[]) => ipcRenderer.invoke(IpcChannel.App_LogToMain, source, level, message, data), + setFullScreen: (value: boolean): Promise => ipcRenderer.invoke(IpcChannel.App_SetFullScreen, value), mac: { isProcessTrusted: (): Promise => ipcRenderer.invoke(IpcChannel.App_MacIsProcessTrusted), requestProcessTrust: (): Promise => ipcRenderer.invoke(IpcChannel.App_MacRequestProcessTrust) diff --git a/src/renderer/src/components/CollapsibleSearchBar.tsx b/src/renderer/src/components/CollapsibleSearchBar.tsx index 0b7c038a68..3ce3b436be 100644 --- a/src/renderer/src/components/CollapsibleSearchBar.tsx +++ b/src/renderer/src/components/CollapsibleSearchBar.tsx @@ -62,6 +62,7 @@ const CollapsibleSearchBar: React.FC = ({ onSearch, i onChange={(e) => handleTextChange(e.target.value)} onKeyDown={(e) => { if (e.key === 'Escape') { + e.stopPropagation() handleTextChange('') if (!searchText) setSearchVisible(false) } diff --git a/src/renderer/src/components/ContentSearch.tsx b/src/renderer/src/components/ContentSearch.tsx index 6842312137..637af96de4 100644 --- a/src/renderer/src/components/ContentSearch.tsx +++ b/src/renderer/src/components/ContentSearch.tsx @@ -282,6 +282,7 @@ export const ContentSearch = React.forwardRef( implementation.searchNext() } } else if (event.key === 'Escape') { + event.stopPropagation() implementation.disable() } }, diff --git a/src/renderer/src/components/EditableNumber/index.tsx b/src/renderer/src/components/EditableNumber/index.tsx index 3cc0f09507..220cf5fb57 100644 --- a/src/renderer/src/components/EditableNumber/index.tsx +++ b/src/renderer/src/components/EditableNumber/index.tsx @@ -63,6 +63,7 @@ const EditableNumber: FC = ({ if (e.key === 'Enter') { handleBlur() } else if (e.key === 'Escape') { + e.stopPropagation() setInputValue(value) setIsEditing(false) } diff --git a/src/renderer/src/components/Popups/SelectModelPopup/popup.tsx b/src/renderer/src/components/Popups/SelectModelPopup/popup.tsx index e7557f5e5e..3ea309f6a0 100644 --- a/src/renderer/src/components/Popups/SelectModelPopup/popup.tsx +++ b/src/renderer/src/components/Popups/SelectModelPopup/popup.tsx @@ -256,6 +256,7 @@ const PopupContainer: React.FC = ({ model, resolve, modelFilter }) => { break case 'Escape': e.preventDefault() + e.stopPropagation() setOpen(false) resolve(undefined) break diff --git a/src/renderer/src/components/QuickPanel/view.tsx b/src/renderer/src/components/QuickPanel/view.tsx index 34ebf07080..7119f75f36 100644 --- a/src/renderer/src/components/QuickPanel/view.tsx +++ b/src/renderer/src/components/QuickPanel/view.tsx @@ -458,6 +458,7 @@ export const QuickPanelView: React.FC = ({ setInputText }) => { } break case 'Escape': + e.stopPropagation() handleClose('esc') break } @@ -477,14 +478,14 @@ export const QuickPanelView: React.FC = ({ setInputText }) => { } } - window.addEventListener('keydown', handleKeyDown) - window.addEventListener('keyup', handleKeyUp) - window.addEventListener('click', handleClickOutside) + window.addEventListener('keydown', handleKeyDown, true) + window.addEventListener('keyup', handleKeyUp, true) + window.addEventListener('click', handleClickOutside, true) return () => { - window.removeEventListener('keydown', handleKeyDown) - window.removeEventListener('keyup', handleKeyUp) - window.removeEventListener('click', handleClickOutside) + window.removeEventListener('keydown', handleKeyDown, true) + window.removeEventListener('keyup', handleKeyUp, true) + window.removeEventListener('click', handleClickOutside, true) } }, [index, isAssistiveKeyPressed, historyPanel, ctx, list, handleItemAction, handleClose, clearSearchText]) diff --git a/src/renderer/src/components/TopView/index.tsx b/src/renderer/src/components/TopView/index.tsx index 91d5cc42cd..b138ab5f08 100644 --- a/src/renderer/src/components/TopView/index.tsx +++ b/src/renderer/src/components/TopView/index.tsx @@ -1,5 +1,7 @@ +import { loggerService } from '@logger' import TopViewMinappContainer from '@renderer/components/MinApp/TopViewMinappContainer' import { useAppInit } from '@renderer/hooks/useAppInit' +import { useShortcuts } from '@renderer/hooks/useShortcuts' import { message, Modal } from 'antd' import React, { PropsWithChildren, useCallback, useEffect, useRef, useState } from 'react' @@ -24,6 +26,8 @@ type ElementItem = { element: React.FC | React.ReactNode } +const logger = loggerService.withContext('TopView') + const TopViewContainer: React.FC = ({ children }) => { const [elements, setElements] = useState([]) const elementsRef = useRef([]) @@ -31,6 +35,8 @@ const TopViewContainer: React.FC = ({ children }) => { const [messageApi, messageContextHolder] = message.useMessage() const [modal, modalContextHolder] = Modal.useModal() + const { shortcuts } = useShortcuts() + const enableQuitFullScreen = shortcuts.find((item) => item.key === 'exit_fullscreen')?.enabled useAppInit() @@ -72,6 +78,21 @@ const TopViewContainer: React.FC = ({ children }) => { ) }, []) + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + logger.debug('keydown', e) + if (!enableQuitFullScreen) return + + if (e.key === 'Escape' && !e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey) { + window.api.setFullScreen(false) + } + } + window.addEventListener('keydown', handleKeyDown) + return () => { + window.removeEventListener('keydown', handleKeyDown) + } + }) + return ( <> {children} diff --git a/src/renderer/src/hooks/useInPlaceEdit.ts b/src/renderer/src/hooks/useInPlaceEdit.ts index 3af085f99a..077b6d21cb 100644 --- a/src/renderer/src/hooks/useInPlaceEdit.ts +++ b/src/renderer/src/hooks/useInPlaceEdit.ts @@ -66,6 +66,7 @@ export function useInPlaceEdit(options: UseInPlaceEditOptions): UseInPlaceEditRe saveEdit() } else if (e.key === 'Escape') { e.preventDefault() + e.stopPropagation() cancelEdit() } }, diff --git a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx index 07d537eacb..df690c03d0 100644 --- a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx +++ b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx @@ -87,7 +87,7 @@ const Inputbar: FC = ({ assistant: _assistant, setActiveTopic, topic }) = enableQuickPanelTriggers, enableSpellCheck } = useSettings() - const [expended, setExpend] = useState(false) + const [expanded, setExpand] = useState(false) const [estimateTokenCount, setEstimateTokenCount] = useState(0) const [contextCount, setContextCount] = useState({ current: 0, max: 0 }) const textareaRef = useRef(null) @@ -256,7 +256,7 @@ const Inputbar: FC = ({ assistant: _assistant, setActiveTopic, topic }) = setFiles([]) setTimeout(() => setText(''), 500) setTimeout(() => resizeTextArea(true), 0) - setExpend(false) + setExpand(false) } catch (error) { logger.warn('Failed to send message:', error as Error) parent?.recordException(error as Error) @@ -396,9 +396,10 @@ const Inputbar: FC = ({ assistant: _assistant, setActiveTopic, topic }) = } } - if (expended) { + if (expanded) { if (event.key === 'Escape') { - return onToggleExpended() + event.stopPropagation() + return onToggleExpanded() } } @@ -493,7 +494,7 @@ const Inputbar: FC = ({ assistant: _assistant, setActiveTopic, topic }) = EventEmitter.emit(EVENT_NAMES.NEW_CONTEXT) } - const onInput = () => !expended && resizeTextArea() + const onInput = () => !expanded && resizeTextArea() const onChange = useCallback( (e: React.ChangeEvent) => { @@ -633,7 +634,7 @@ const Inputbar: FC = ({ assistant: _assistant, setActiveTopic, topic }) = if (textArea) { textArea.style.height = `${newHeight}px` - setExpend(newHeight == maxHeightInPixels) + setExpand(newHeight == maxHeightInPixels) setTextareaHeight(newHeight) } }, @@ -795,10 +796,10 @@ const Inputbar: FC = ({ assistant: _assistant, setActiveTopic, topic }) = const onClearMentionModels = useCallback(() => setMentionedModels([]), [setMentionedModels]) - const onToggleExpended = () => { - const currentlyExpanded = expended || !!textareaHeight + const onToggleExpanded = () => { + const currentlyExpanded = expanded || !!textareaHeight const shouldExpand = !currentlyExpanded - setExpend(shouldExpand) + setExpand(shouldExpand) const textArea = textareaRef.current?.resizableTextArea?.textArea if (!textArea) return if (shouldExpand) { @@ -818,7 +819,7 @@ const Inputbar: FC = ({ assistant: _assistant, setActiveTopic, topic }) = focusTextarea() } - const isExpended = expended || !!textareaHeight + const isExpanded = expanded || !!textareaHeight const showThinkingButton = isSupportedThinkingTokenModel(model) || isSupportedReasoningEffortModel(model) if (isMultiSelectMode) { @@ -899,8 +900,8 @@ const Inputbar: FC = ({ assistant: _assistant, setActiveTopic, topic }) = couldMentionNotVisionModel={couldMentionNotVisionModel} couldAddImageFile={couldAddImageFile} onEnableGenerateImage={onEnableGenerateImage} - isExpended={isExpended} - onToggleExpended={onToggleExpended} + isExpanded={isExpanded} + onToggleExpanded={onToggleExpanded} addNewTopic={addNewTopic} clearTopic={clearTopic} onNewContext={onNewContext} diff --git a/src/renderer/src/pages/home/Inputbar/InputbarTools.tsx b/src/renderer/src/pages/home/Inputbar/InputbarTools.tsx index d2a1fcbaa2..426bd8957c 100644 --- a/src/renderer/src/pages/home/Inputbar/InputbarTools.tsx +++ b/src/renderer/src/pages/home/Inputbar/InputbarTools.tsx @@ -71,8 +71,8 @@ export interface InputbarToolsProps { couldMentionNotVisionModel: boolean couldAddImageFile: boolean onEnableGenerateImage: () => void - isExpended: boolean - onToggleExpended: () => void + isExpanded: boolean + onToggleExpanded: () => void addNewTopic: () => void clearTopic: () => void @@ -113,8 +113,8 @@ const InputbarTools = ({ couldMentionNotVisionModel, couldAddImageFile, onEnableGenerateImage, - isExpended, - onToggleExpended, + isExpanded: isExpended, + onToggleExpanded: onToggleExpended, addNewTopic, clearTopic, onNewContext, diff --git a/src/renderer/src/pages/settings/ProviderSettings/index.tsx b/src/renderer/src/pages/settings/ProviderSettings/index.tsx index 98d8120557..5fdfaecd64 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/index.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/index.tsx @@ -466,6 +466,7 @@ const ProvidersList: FC = () => { onChange={(e) => setSearchText(e.target.value)} onKeyDown={(e) => { if (e.key === 'Escape') { + e.stopPropagation() setSearchText('') } }}