diff --git a/src/App.tsx b/src/App.tsx index 2e1397f..10b129e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,6 +5,7 @@ import { BoardList } from "@/components/boards/BoardList"; import { BoardView } from "@/components/board/BoardView"; import { CommandPalette } from "@/components/command-palette/CommandPalette"; import { SettingsDialog } from "@/components/settings/SettingsDialog"; +import { useKeyboardShortcuts } from "@/hooks/useKeyboardShortcuts"; export default function App() { const initialized = useAppStore((s) => s.initialized); @@ -32,6 +33,8 @@ export default function App() { setSettingsOpen(true); }, []); + useKeyboardShortcuts(); + if (!initialized) { return (
diff --git a/src/hooks/useKeyboardShortcuts.ts b/src/hooks/useKeyboardShortcuts.ts new file mode 100644 index 0000000..ea3722a --- /dev/null +++ b/src/hooks/useKeyboardShortcuts.ts @@ -0,0 +1,57 @@ +import { useEffect } from "react"; +import { useBoardStore } from "@/stores/board-store"; + +function isInputFocused(): boolean { + const el = document.activeElement; + if (!el) return false; + const tag = el.tagName.toLowerCase(); + return ( + tag === "input" || + tag === "textarea" || + (el as HTMLElement).isContentEditable + ); +} + +export function useKeyboardShortcuts(): void { + useEffect(() => { + function handleKeyDown(e: KeyboardEvent) { + const ctrl = e.ctrlKey || e.metaKey; + + // Ctrl+K: open command palette (always works, even in inputs) + if (ctrl && e.key === "k") { + e.preventDefault(); + document.dispatchEvent(new CustomEvent("open-command-palette")); + return; + } + + // Skip remaining shortcuts when an input is focused + if (isInputFocused()) return; + + // Ctrl+Shift+Z: redo + if (ctrl && e.shiftKey && e.key === "Z") { + e.preventDefault(); + useBoardStore.temporal.getState().redo(); + return; + } + + // Ctrl+Z: undo + if (ctrl && e.key === "z") { + e.preventDefault(); + useBoardStore.temporal.getState().undo(); + return; + } + + // Escape: close any open modal/dialog + // Note: Radix dialogs handle their own Escape, but this covers custom modals + if (e.key === "Escape") { + document.dispatchEvent(new CustomEvent("close-all-modals")); + return; + } + } + + document.addEventListener("keydown", handleKeyDown); + return () => { + document.removeEventListener("keydown", handleKeyDown); + }; + }, []); +}