feat: add command palette, accessibility, scroll animations, demo workspace, and keyboard navigation
- COMP-139: Command palette for quick navigation - COMP-140: Accessibility improvements - COMP-141: Scroll animations with animate-on-scroll component - COMP-143: Demo workspace with seed data and demo banner - COMP-145: Keyboard navigation and shortcuts help Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-Claude) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
113
apps/web/src/components/keyboard-shortcuts-help.tsx
Normal file
113
apps/web/src/components/keyboard-shortcuts-help.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { X } from "lucide-react";
|
||||
|
||||
const shortcuts = [
|
||||
{ keys: ["j"], description: "Move selection down" },
|
||||
{ keys: ["k"], description: "Move selection up" },
|
||||
{ keys: ["Enter"], description: "Open selected item" },
|
||||
{ keys: ["Escape"], description: "Clear selection / go back" },
|
||||
{ keys: ["g", "h"], description: "Go to Dashboard" },
|
||||
{ keys: ["g", "s"], description: "Go to Settings" },
|
||||
{ keys: ["g", "k"], description: "Go to API Keys" },
|
||||
{ keys: ["g", "d"], description: "Go to Decisions" },
|
||||
{ keys: ["Cmd", "K"], description: "Open command palette" },
|
||||
{ keys: ["?"], description: "Show this help" },
|
||||
];
|
||||
|
||||
export function KeyboardShortcutsHelp() {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
function handleKeyDown(e: KeyboardEvent) {
|
||||
const el = document.activeElement;
|
||||
if (el) {
|
||||
const tag = el.tagName.toLowerCase();
|
||||
if (tag === "input" || tag === "textarea" || tag === "select") return;
|
||||
if ((el as HTMLElement).isContentEditable) return;
|
||||
}
|
||||
|
||||
if (e.key === "?" && !e.metaKey && !e.ctrlKey) {
|
||||
e.preventDefault();
|
||||
setOpen((prev) => !prev);
|
||||
}
|
||||
|
||||
if (e.key === "Escape" && open) {
|
||||
setOpen(false);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("keydown", handleKeyDown);
|
||||
return () => document.removeEventListener("keydown", handleKeyDown);
|
||||
}, [open]);
|
||||
|
||||
if (!open) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-[90]">
|
||||
<div
|
||||
className="absolute inset-0 bg-black/60 backdrop-blur-sm"
|
||||
onClick={() => setOpen(false)}
|
||||
/>
|
||||
|
||||
<div className="absolute inset-0 flex items-center justify-center px-4">
|
||||
<div className="w-full max-w-md rounded-xl border border-neutral-800 bg-neutral-900/95 backdrop-blur-xl shadow-2xl shadow-black/40 overflow-hidden">
|
||||
<div className="flex items-center justify-between px-5 py-4 border-b border-neutral-800">
|
||||
<h2 className="text-sm font-semibold text-neutral-100">
|
||||
Keyboard Shortcuts
|
||||
</h2>
|
||||
<button
|
||||
onClick={() => setOpen(false)}
|
||||
aria-label="Close shortcuts help"
|
||||
className="p-1.5 rounded-lg text-neutral-500 hover:text-neutral-300 hover:bg-neutral-800 transition-colors"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="p-2 max-h-[60vh] overflow-y-auto">
|
||||
{shortcuts.map((shortcut, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex items-center justify-between px-3 py-2.5 rounded-lg hover:bg-neutral-800/50"
|
||||
>
|
||||
<span className="text-sm text-neutral-400">
|
||||
{shortcut.description}
|
||||
</span>
|
||||
<div className="flex items-center gap-1">
|
||||
{shortcut.keys.map((key, j) => (
|
||||
<span key={j}>
|
||||
{j > 0 && (
|
||||
<span className="text-neutral-600 text-xs mx-0.5">
|
||||
then
|
||||
</span>
|
||||
)}
|
||||
<kbd className="inline-flex items-center justify-center min-w-[24px] px-2 py-1 rounded bg-neutral-800 border border-neutral-700 text-xs font-mono text-neutral-300">
|
||||
{key}
|
||||
</kbd>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function ShortcutsHint() {
|
||||
return (
|
||||
<div className="fixed bottom-4 right-4 z-30">
|
||||
<span className="text-xs text-neutral-600 flex items-center gap-1.5">
|
||||
Press
|
||||
<kbd className="px-1.5 py-0.5 rounded bg-neutral-800 border border-neutral-700 text-[10px] font-mono text-neutral-500">
|
||||
?
|
||||
</kbd>
|
||||
for shortcuts
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user