From 1c564d57b54cd3c929bf4c4b7566eeb3c04d5f7d Mon Sep 17 00:00:00 2001 From: Nicolas Varrot Date: Sat, 14 Feb 2026 05:54:11 +0000 Subject: [PATCH] feat: configurable send shortcut (Enter vs Ctrl+Enter) Add toggle in chat input to switch between Enter-to-send (default) and Ctrl+Enter-to-send modes. Preference persists in localStorage. Keyboard shortcuts modal reflects the current setting. --- src/components/ChatInput.tsx | 27 ++++++++++++++++++++++++--- src/components/KeyboardShortcuts.tsx | 6 ++++-- src/hooks/useSendShortcut.ts | 21 +++++++++++++++++++++ src/lib/i18n.ts | 9 +++++++++ 4 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 src/hooks/useSendShortcut.ts diff --git a/src/components/ChatInput.tsx b/src/components/ChatInput.tsx index 9ca8f13..f45ad97 100644 --- a/src/components/ChatInput.tsx +++ b/src/components/ChatInput.tsx @@ -1,6 +1,7 @@ import { useState, useRef, useEffect, useCallback, lazy, Suspense } from 'react'; import { Send, Square, Paperclip, X, FileText, Eye, EyeOff } from 'lucide-react'; import { useT } from '../hooks/useLocale'; +import { useSendShortcut } from '../hooks/useSendShortcut'; const ReactMarkdown = lazy(() => import('react-markdown')); const remarkGfm = import('remark-gfm').then(m => m.default); @@ -87,6 +88,7 @@ function formatSize(bytes: number): string { export function ChatInput({ onSend, onAbort, isGenerating, disabled, sessionKey }: Props) { const t = useT(); + const { sendOnEnter, toggle: toggleSendShortcut } = useSendShortcut(); const [text, setText] = useState(''); const [files, setFiles] = useState([]); const [isDragOver, setIsDragOver] = useState(false); @@ -179,9 +181,20 @@ export function ChatInput({ onSend, onAbort, isGenerating, disabled, sessionKey }; const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'Enter' && !e.shiftKey) { - e.preventDefault(); - handleSubmit(); + if (e.key === 'Enter') { + if (sendOnEnter) { + // Enter sends, Shift+Enter for newline + if (!e.shiftKey && !e.ctrlKey && !e.metaKey) { + e.preventDefault(); + handleSubmit(); + } + } else { + // Ctrl+Enter (or Cmd+Enter on Mac) sends, Enter for newline + if (e.ctrlKey || e.metaKey) { + e.preventDefault(); + handleSubmit(); + } + } } }; @@ -305,6 +318,14 @@ export function ChatInput({ onSend, onAbort, isGenerating, disabled, sessionKey rows={1} className="flex-1 bg-transparent resize-none rounded-2xl border border-pc-border bg-pc-input/35 px-4 py-3 text-sm text-pc-text placeholder:text-pc-text-muted outline-none transition-all max-h-[200px]" /> + {/* Send shortcut toggle */} + {isGenerating ? (