diff --git a/src/renderer/src/components/QuickPanel/view.tsx b/src/renderer/src/components/QuickPanel/view.tsx index 30955f96f3..59d72b2de2 100644 --- a/src/renderer/src/components/QuickPanel/view.tsx +++ b/src/renderer/src/components/QuickPanel/view.tsx @@ -341,11 +341,12 @@ export const QuickPanelView: React.FC = ({ setInputText }) => { scrollTriggerRef.current = 'none' }, [index]) - // 处理键盘事件(折叠时不拦截全局键盘) + // 处理键盘事件: + // - 可见且未折叠时:拦截 Enter 及其组合键(纯 Enter 选择项;带修饰键仅拦截不处理)。 + // - 软隐藏/折叠时:不拦截 Enter,允许输入框处理(用于发送消息等)。 + // - 不可见时:不拦截,输入框按常规处理。 useEffect(() => { - const hasSearchTextFlag = searchText.replace(/^[/@]/, '').length > 0 - const isCollapsed = hasSearchTextFlag && list.length === 0 - if (!ctx.isVisible || isCollapsed) return + if (!ctx.isVisible) return const handleKeyDown = (e: KeyboardEvent) => { if (isMac ? e.metaKey : e.ctrlKey) { @@ -438,9 +439,24 @@ export const QuickPanelView: React.FC = ({ setInputText }) => { break case 'Enter': - case 'NumpadEnter': + case 'NumpadEnter': { if (isComposing.current) return + // 折叠/软隐藏时不拦截,让输入框处理(用于发送消息) + const hasSearch = searchText.replace(/^[/@]/, '').length > 0 + const nonPinnedCount = list.filter((i) => !i.alwaysVisible).length + const isCollapsed = hasSearch && nonPinnedCount === 0 + if (isCollapsed) return + + // 面板可见且未折叠时:拦截所有 Enter 变体; + // 纯 Enter 选择项,带修饰键仅拦截不处理 + if (e.ctrlKey || e.metaKey || e.altKey || e.shiftKey) { + e.preventDefault() + e.stopPropagation() + setIsMouseOver(false) + break + } + if (list?.[index]) { e.preventDefault() e.stopPropagation() @@ -451,6 +467,7 @@ export const QuickPanelView: React.FC = ({ setInputText }) => { handleClose('enter_empty') } break + } case 'Escape': e.stopPropagation() handleClose('esc') diff --git a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx index f9ae038b56..14d209fb27 100644 --- a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx +++ b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx @@ -406,37 +406,40 @@ const Inputbar: FC = ({ assistant: _assistant, setActiveTopic, topic }) = //other keys should be ignored const isEnterPressed = event.key === 'Enter' && !event.nativeEvent.isComposing if (isEnterPressed) { - if (quickPanel.isVisible) return event.preventDefault() - + // 1) 优先判断是否为“发送”(当前仅支持纯 Enter 发送;其余 Enter 组合键均换行) if (isSendMessageKeyPressed(event, sendMessageShortcut)) { sendMessage() return event.preventDefault() - } else { - //shift+enter's default behavior is to add a new line, ignore it - if (!event.shiftKey) { - event.preventDefault() + } - const textArea = textareaRef.current?.resizableTextArea?.textArea - if (textArea) { - const start = textArea.selectionStart - const end = textArea.selectionEnd - const text = textArea.value - const newText = text.substring(0, start) + '\n' + text.substring(end) + // 2) 不再基于 quickPanel.isVisible 主动拦截。 + // 纯 Enter 的处理权交由 QuickPanel 的全局捕获(其只在纯 Enter 时拦截), + // 其它带修饰键的 Enter 则由输入框处理为换行。 - // update text by setState, not directly modify textarea.value - setText(newText) + if (event.shiftKey) { + return + } - // set cursor position in the next render cycle - setTimeoutTimer( - 'handleKeyDown', - () => { - textArea.selectionStart = textArea.selectionEnd = start + 1 - onInput() // trigger resizeTextArea - }, - 0 - ) - } - } + event.preventDefault() + const textArea = textareaRef.current?.resizableTextArea?.textArea + if (textArea) { + const start = textArea.selectionStart + const end = textArea.selectionEnd + const text = textArea.value + const newText = text.substring(0, start) + '\n' + text.substring(end) + + // update text by setState, not directly modify textarea.value + setText(newText) + + // set cursor position in the next render cycle + setTimeoutTimer( + 'handleKeyDown', + () => { + textArea.selectionStart = textArea.selectionEnd = start + 1 + onInput() // trigger resizeTextArea + }, + 0 + ) } }