import { useRef, useState } from 'react'; import { useRouter } from 'next/router'; import Modal from './Modal'; /** * CommandPaletteModal — the surface that opens on Cmd+K (or Ctrl+K). * * From the redesign-v2 mockup's top-bar Cmd+K hint. Operator decided * (§ 7.2 of the umbrella convoy) to ship the keyboard handler + a * minimum-viable palette in this convoy, deferring federated search * across cards/decks/lists to a follow-up. * * What it does today: * - Single search input, auto-focused on open * - Submit on Enter → routes to /cards?q= (the closest * existing search surface). Closes the palette. * - 3 placeholder "Quick actions" rows below the input, each * routing to an existing page (Dashboard, Cards, Scanner). * - Escape closes (delegated to the underlying ). * * What it does NOT do (deferred): * - Live result preview as you type * - "Recent searches" from a backend store * - Cross-resource ranked results (cards + decks + lists + users) * * Props: * open boolean — controls visibility * onClose () => void */ const QUICK_ACTIONS = [ { label: 'Go to Dashboard', href: '/dashboard', keywords: ['home', 'dash'] }, { label: 'Browse Cards', href: '/cards', keywords: ['catalog', 'all'] }, { label: 'Open Scanner', href: '/scanner', keywords: ['scan', 'camera'] }, ]; export default function CommandPaletteModal({ open, onClose }) { const router = useRouter(); const inputRef = useRef(null); // Reset query when the modal opens. We track the previous `open` and // clear synchronously during render (per the react-hooks/set-state- // in-effect lint rule — the same pattern used elsewhere in the // codebase, e.g. NavigationContent's path-change resetter). const [query, setQuery] = useState(''); const [wasOpen, setWasOpen] = useState(open); if (open && !wasOpen) { setWasOpen(true); setQuery(''); } else if (!open && wasOpen) { setWasOpen(false); } const handleSubmit = (event) => { event.preventDefault(); const trimmed = query.trim(); if (trimmed.length === 0) return; onClose?.(); router.push({ pathname: '/cards', query: { q: trimmed } }); }; const handleQuickAction = (href) => { onClose?.(); router.push(href); }; return (
setQuery(event.target.value)} placeholder="Search cards, decks, lists, users..." aria-label="Search query" className="w-full pl-10 pr-4 py-3 rounded-xl text-base focus:outline-none focus:ring-2" style={{ backgroundColor: 'var(--input-bg)', color: 'var(--input-text)', border: '1px solid var(--input-border)', '--tw-ring-color': 'var(--accent-ember)', }} />
Quick actions
{QUICK_ACTIONS.map((action) => ( ))}
); }