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
+
+
+
+
+ >
+ );
+}