feat: add command palette, accessibility, scroll animations, and keyboard navigation
Implements COMP-139 (command palette), COMP-140 (accessibility), COMP-141 (scroll animations), COMP-145 (keyboard navigation) Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-Claude) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
121
apps/web/src/components/keyboard-shortcuts-help.tsx
Normal file
121
apps/web/src/components/keyboard-shortcuts-help.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef } from "react";
|
||||
import { X } from "lucide-react";
|
||||
|
||||
interface KeyboardShortcutsHelpProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
function Shortcut({ keys, label }: { keys: string[]; label: string }) {
|
||||
return (
|
||||
<div className="flex items-center justify-between py-2">
|
||||
<span className="text-sm text-zinc-300">{label}</span>
|
||||
<div className="flex items-center gap-1">
|
||||
{keys.map((key) => (
|
||||
<kbd
|
||||
key={key}
|
||||
className="inline-flex h-6 min-w-[1.5rem] items-center justify-center rounded border border-white/10 bg-white/[0.05] px-1.5 font-mono text-xs text-zinc-400"
|
||||
>
|
||||
{key}
|
||||
</kbd>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function KeyboardShortcutsHelp({
|
||||
open,
|
||||
onClose,
|
||||
}: KeyboardShortcutsHelpProps) {
|
||||
const overlayRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
const handler = (e: KeyboardEvent) => {
|
||||
if (e.key === "Escape" || e.key === "?") {
|
||||
e.preventDefault();
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
document.addEventListener("keydown", handler);
|
||||
return () => document.removeEventListener("keydown", handler);
|
||||
}, [open, onClose]);
|
||||
|
||||
if (!open) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-[90]" ref={overlayRef}>
|
||||
<div
|
||||
className="fixed inset-0 bg-black/60 backdrop-blur-sm"
|
||||
onClick={onClose}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<div className="fixed inset-0 flex items-center justify-center px-4">
|
||||
<div
|
||||
role="dialog"
|
||||
aria-label="Keyboard shortcuts"
|
||||
className="w-full max-w-md rounded-xl border border-white/10 bg-[#111113] shadow-2xl shadow-black/50 animate-scale-in"
|
||||
>
|
||||
<div className="flex items-center justify-between px-5 py-4 border-b border-white/[0.06]">
|
||||
<h2 className="text-base font-semibold text-white">
|
||||
Keyboard Shortcuts
|
||||
</h2>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="p-1.5 rounded-lg text-zinc-400 hover:text-white hover:bg-white/[0.06] transition-colors"
|
||||
aria-label="Close shortcuts help"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="px-5 py-4 space-y-4">
|
||||
<div>
|
||||
<h3 className="text-xs font-medium text-zinc-500 uppercase tracking-wider mb-2">
|
||||
Navigation
|
||||
</h3>
|
||||
<div className="divide-y divide-white/[0.04]">
|
||||
<Shortcut keys={["j"]} label="Move down" />
|
||||
<Shortcut keys={["k"]} label="Move up" />
|
||||
<Shortcut keys={["Enter"]} label="Open selected" />
|
||||
<Shortcut keys={["Esc"]} label="Clear selection" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-xs font-medium text-zinc-500 uppercase tracking-wider mb-2">
|
||||
Go To
|
||||
</h3>
|
||||
<div className="divide-y divide-white/[0.04]">
|
||||
<Shortcut keys={["g", "h"]} label="Go to Home" />
|
||||
<Shortcut keys={["g", "g"]} label="Go to Generate" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-xs font-medium text-zinc-500 uppercase tracking-wider mb-2">
|
||||
General
|
||||
</h3>
|
||||
<div className="divide-y divide-white/[0.04]">
|
||||
<Shortcut
|
||||
keys={["\u2318", "K"]}
|
||||
label="Command palette"
|
||||
/>
|
||||
<Shortcut keys={["?"]} label="Show shortcuts" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-5 py-3 border-t border-white/[0.06]">
|
||||
<p className="text-xs text-zinc-500 text-center">
|
||||
Press <kbd className="inline-flex h-4 items-center rounded border border-white/10 bg-white/[0.05] px-1 font-mono text-[10px] text-zinc-400">?</kbd> or <kbd className="inline-flex h-4 items-center rounded border border-white/10 bg-white/[0.05] px-1 font-mono text-[10px] text-zinc-400">Esc</kbd> to close
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user