'use client'; import { ArrowRight, CornerDownLeft, Search, X } from 'lucide-react'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; import { useEffect, useRef, useState } from 'react'; import { EntityBadge } from '@/components/ui/badges'; import { clientApi } from '@/lib/client-api'; import { cn } from '@/lib/cn'; import { EXAMPLE_QUERIES, exploreNav, routes } from '@/lib/site'; import type { Suggestion } from '@/lib/types'; import { useSearch } from './search-context'; /** Command-palette global search (⌘K or /): prefix suggestions from /search/suggest; Enter → /search?q=. Mobile: full-screen sheet. */ export function SearchDialog() { const { open, setOpen } = useSearch(); const router = useRouter(); const [q, setQ] = useState(''); const [items, setItems] = useState([]); const [active, setActive] = useState(0); const [loading, setLoading] = useState(false); const [failed, setFailed] = useState(false); const inputRef = useRef(null); useEffect(() => { if (open) { setTimeout(() => inputRef.current?.focus(), 20); document.body.style.overflow = 'hidden'; } else { document.body.style.overflow = ''; setQ(''); setItems([]); setFailed(false); } return () => { document.body.style.overflow = ''; }; }, [open]); useEffect(() => { if (!open) return; const term = q.trim(); if (term.length < 1) { setItems([]); return; } const ctrl = new AbortController(); const t = setTimeout(async () => { setLoading(true); try { const res = await clientApi.suggest(term, ctrl.signal); setItems(res.items ?? []); setFailed(false); setActive(0); } catch (e) { if ((e as Error).name !== 'AbortError') setFailed(true); } finally { setLoading(false); } }, 120); return () => { clearTimeout(t); ctrl.abort(); }; }, [q, open]); if (!open) return null; const rows: { key: string; href: string; node: React.ReactNode }[] = items.map((s) => ({ key: s.id, href: routes.entity(s), node: ( <> {s.name} {s.organization_name && {s.organization_name}} ), })); const term = q.trim(); const allRows = term ? [...rows, { key: '__all', href: routes.search(term), node: (<>Search everything for “{term}”) }] : rows; const go = (href: string) => { router.push(href); setOpen(false); }; const onKey = (e: React.KeyboardEvent) => { if (e.key === 'ArrowDown') { e.preventDefault(); setActive((a) => Math.min(a + 1, allRows.length - 1)); } else if (e.key === 'ArrowUp') { e.preventDefault(); setActive((a) => Math.max(a - 1, 0)); } else if (e.key === 'Enter') { e.preventDefault(); const it = allRows[active]; if (it) go(it.href); else if (term) go(routes.search(term)); } else if (e.key === 'Escape') setOpen(false); }; return (
setOpen(false)}>
e.stopPropagation()}>
setQ(e.target.value)} onKeyDown={onKey} placeholder="Search models, companies, papers, benchmarks… or ask in plain English" className="h-11 min-w-0 flex-1 bg-transparent text-[16px] text-ink placeholder:text-ink-3 focus:outline-none" autoComplete="off" spellCheck={false} aria-label="Search" aria-activedescendant={allRows[active] ? `sugg-${allRows[active].key}` : undefined} />
{term === '' ? (

Try asking

    {EXAMPLE_QUERIES.map((ex) => (
  • ))}

Browse

    {exploreNav.map((n) => (
  • setOpen(false)} className="flex h-10 items-center gap-2 text-sm text-ink-2 hover:text-accent"> {n.label}
  • ))}

↵ full results · esc close · ⌘K anywhere

) : (
    {failed &&
  • Suggestions unavailable — press Enter to search.
  • } {!loading && !failed && rows.length === 0 &&
  • No direct match — search everything below.
  • } {allRows.map((r, i) => (
  • setOpen(false)} onMouseEnter={() => setActive(i)} className={cn('flex min-h-[46px] items-center gap-3 px-4 py-2', i === active ? 'bg-surface-3' : 'hover:bg-surface-2')}> {r.node}
  • ))}
)}
); }