diff --git a/frontend/src/components/DashboardLayout.tsx b/frontend/src/components/DashboardLayout.tsx index 3c2b9c6..e484841 100644 --- a/frontend/src/components/DashboardLayout.tsx +++ b/frontend/src/components/DashboardLayout.tsx @@ -2,6 +2,7 @@ import { useState } from "react"; import { NavLink, Outlet } from "react-router-dom"; import { useCurrentUser } from "../hooks/useCurrentUser"; import { useTheme } from "../hooks/useTheme"; +import { KeyboardShortcutsModal } from "./KeyboardShortcutsModal"; import { signOut } from "../lib/auth-client"; const navItems = [ @@ -160,6 +161,9 @@ export function DashboardLayout() { > Sign Out +
+ Press ? for shortcuts +
@@ -167,6 +171,8 @@ export function DashboardLayout() {
+ + ); } diff --git a/frontend/src/components/KeyboardShortcutsModal.tsx b/frontend/src/components/KeyboardShortcutsModal.tsx new file mode 100644 index 0000000..8870662 --- /dev/null +++ b/frontend/src/components/KeyboardShortcutsModal.tsx @@ -0,0 +1,91 @@ +import { useEffect, useState } from "react"; + +const shortcuts = [ + { keys: ["Ctrl", "N"], action: "Create new task", context: "Queue" }, + { keys: ["Esc"], action: "Close panel / Cancel edit", context: "Global" }, + { keys: ["?"], action: "Show keyboard shortcuts", context: "Global" }, + { keys: ["Ctrl", "Enter"], action: "Submit note", context: "Task Detail" }, + { keys: ["Enter"], action: "Add subtask / Save tag", context: "Task Detail" }, + { keys: [","], action: "Add tag", context: "Task Detail" }, + { keys: ["Backspace"], action: "Remove last tag (when empty)", context: "Task Detail" }, +]; + +export function KeyboardShortcutsModal() { + const [open, setOpen] = useState(false); + + useEffect(() => { + const handleKey = (e: KeyboardEvent) => { + // Don't trigger if user is typing in an input + const target = e.target as HTMLElement; + if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.tagName === "SELECT") return; + if (target.isContentEditable) return; + + if (e.key === "?") { + e.preventDefault(); + setOpen((prev) => !prev); + } + if (e.key === "Escape" && open) { + e.preventDefault(); + e.stopPropagation(); + setOpen(false); + } + }; + window.addEventListener("keydown", handleKey); + return () => window.removeEventListener("keydown", handleKey); + }, [open]); + + if (!open) return null; + + return ( + <> +
setOpen(false)} /> +
setOpen(false)}> +
e.stopPropagation()} + > +
+

⌨️ Keyboard Shortcuts

+ +
+
+ {shortcuts.map((shortcut, i) => ( +
+
+ {shortcut.keys.map((key, j) => ( + + + {key} + + {j < shortcut.keys.length - 1 && ( + + + )} + + ))} +
+
+ {shortcut.action} + + {shortcut.context} + +
+
+ ))} +
+
+ + Press ? to toggle + +
+
+
+ + ); +}