diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index b403f34..667bb53 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -18,6 +18,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" import { useHistory } from "./hooks/useHistory" import HistoryPanel from "./components/HistoryPanel" @@ -93,6 +94,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]) +}