diff --git a/apps/web/src/components/settings/KeybindingsSettings.tsx b/apps/web/src/components/settings/KeybindingsSettings.tsx index b710c135ab2d36aa0b8d2bccb418f5e026139f19..1aa0dc0e9d3a4d21e8abb0d4969d267f68b8d51c 100644 --- a/apps/web/src/components/settings/KeybindingsSettings.tsx +++ b/apps/web/src/components/settings/KeybindingsSettings.tsx @@ -17,7 +17,6 @@ import { type ReactNode, type RefObject, useCallback, - useEffect, useMemo, useReducer, useRef, @@ -1340,28 +1339,6 @@ export function KeybindingsSettingsPanel() { const commandOptions = useMemo(() => buildKeybindingCommandOptions(keybindings), [keybindings]); const whenVariables = useMemo(() => buildWhenVariableOptions(), []); - useEffect(() => { - const handleKeyDown = (event: globalThis.KeyboardEvent) => { - const isMod = event.metaKey || event.ctrlKey; - if (!isMod || event.altKey || event.key.toLowerCase() !== "f") return; - - const target = event.target; - if ( - target !== searchInputRef.current && - target instanceof HTMLElement && - (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable) - ) { - return; - } - - event.preventDefault(); - searchInputRef.current?.focus(); - searchInputRef.current?.select(); - }; - window.addEventListener("keydown", handleKeyDown); - return () => window.removeEventListener("keydown", handleKeyDown); - }, []); - const openKeybindingsFile = useCallback(() => { if ( !keybindingsConfigPath || diff --git a/apps/web/src/components/settings/SettingsSidebarNav.tsx b/apps/web/src/components/settings/SettingsSidebarNav.tsx index d10abed376bacd59a22282931641ecc922002b8e..035e42b53cc222138c84bb21ecf7d3e9e9f92e57 100644 --- a/apps/web/src/components/settings/SettingsSidebarNav.tsx +++ b/apps/web/src/components/settings/SettingsSidebarNav.tsx @@ -140,17 +140,28 @@ export function SettingsSidebarNav({ pathname }: { pathname: string }) { useEffect(() => { const handleKeyDown = (event: globalThis.KeyboardEvent) => { - if (event.key !== "/" || event.metaKey || event.ctrlKey || event.altKey) return; + if (event.defaultPrevented || event.isComposing) return; + const isFindShortcut = + event.key.toLowerCase() === "f" && + (event.metaKey || event.ctrlKey) && + !event.altKey && + !event.shiftKey; + const isSlashShortcut = + event.key === "/" && !event.metaKey && !event.ctrlKey && !event.altKey; + if (!isFindShortcut && !isSlashShortcut) return; const target = event.target; if ( target instanceof HTMLElement && - (target.tagName === "INPUT" || - target.tagName === "TEXTAREA" || - target.isContentEditable || - // Keep focus inside open dialogs and popups instead of escaping - // their focus trap into the sidebar search. - target.closest('[role="dialog"], [aria-modal="true"], [data-slot$="popup"]') !== null) + ((isSlashShortcut && + (target.tagName === "INPUT" || + target.tagName === "TEXTAREA" || + target.isContentEditable)) || + // Preserve dialog and popup focus traps, and let shortcut recorders + // capture the keypress without moving focus into the sidebar search. + target.closest( + '[role="dialog"], [aria-modal="true"], [data-slot$="popup"], [data-keybinding-capture]', + ) !== null) ) { return; }