From d4a60fd56130dc72ae5cb85c56355075246b6a2b Mon Sep 17 00:00:00 2001 From: Pablo Date: Tue, 1 Sep 2026 15:54:26 -0500 Subject: [PATCH] feat(frontend): add keyboard shortcuts - useKeyboardShortcuts hook (skips when input focused) - 1/2: switch between violations/remediation tabs - Escape: dismiss error message --- frontend/src/App.tsx | 8 ++++++ frontend/src/hooks/useKeyboardShortcuts.ts | 31 ++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 frontend/src/hooks/useKeyboardShortcuts.ts diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index c311e06..a4e18fa 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -16,6 +16,7 @@ import HowItWorks from "./components/HowItWorks" import ExampleRepos from "./components/ExampleRepos" import { usePrefersReducedMotion } from "./hooks/usePrefersReducedMotion" import { useToast } from "./components/Toast" +import { useKeyboardShortcuts } from "./hooks/useKeyboardShortcuts" function useTheme() { const [dark, setDark] = useState(() => { @@ -82,6 +83,13 @@ export default function App() { } } + // Keyboard shortcuts + useKeyboardShortcuts({ + "1": () => setActiveTab("violations"), + "2": () => setActiveTab("remediation"), + "escape": () => setError(""), + }) + // Focus management: move focus to results after analysis completes useEffect(() => { if (result && !loading && resultsRef.current) { diff --git a/frontend/src/hooks/useKeyboardShortcuts.ts b/frontend/src/hooks/useKeyboardShortcuts.ts new file mode 100644 index 0000000..5b6eb70 --- /dev/null +++ b/frontend/src/hooks/useKeyboardShortcuts.ts @@ -0,0 +1,31 @@ +import { useEffect } from "react" + +interface Shortcuts { + [key: string]: () => void +} + +export function useKeyboardShortcuts(shortcuts: Shortcuts) { + useEffect(() => { + const handler = (e: KeyboardEvent) => { + const target = e.target as HTMLElement + if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable) { + return + } + + const parts: string[] = [] + if (e.ctrlKey || e.metaKey) parts.push("ctrl") + if (e.shiftKey) parts.push("shift") + if (e.altKey) parts.push("alt") + parts.push(e.key.toLowerCase()) + const combo = parts.join("+") + + if (combo in shortcuts) { + e.preventDefault() + shortcuts[combo]() + } + } + + window.addEventListener("keydown", handler) + return () => window.removeEventListener("keydown", handler) + }, [shortcuts]) +}