feat: add global keyboard shortcuts for undo/redo and command palette

This commit is contained in:
Your Name
2026-02-15 19:13:36 +02:00
parent e9318df6f6
commit 491c089be6
2 changed files with 60 additions and 0 deletions

View File

@@ -5,6 +5,7 @@ import { BoardList } from "@/components/boards/BoardList";
import { BoardView } from "@/components/board/BoardView"; import { BoardView } from "@/components/board/BoardView";
import { CommandPalette } from "@/components/command-palette/CommandPalette"; import { CommandPalette } from "@/components/command-palette/CommandPalette";
import { SettingsDialog } from "@/components/settings/SettingsDialog"; import { SettingsDialog } from "@/components/settings/SettingsDialog";
import { useKeyboardShortcuts } from "@/hooks/useKeyboardShortcuts";
export default function App() { export default function App() {
const initialized = useAppStore((s) => s.initialized); const initialized = useAppStore((s) => s.initialized);
@@ -32,6 +33,8 @@ export default function App() {
setSettingsOpen(true); setSettingsOpen(true);
}, []); }, []);
useKeyboardShortcuts();
if (!initialized) { if (!initialized) {
return ( return (
<div className="flex h-screen items-center justify-center bg-pylon-bg"> <div className="flex h-screen items-center justify-center bg-pylon-bg">

View File

@@ -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);
};
}, []);
}