'use client'; import { Search } from 'lucide-react'; import { useEffect, useId, useRef, useState } from 'react'; import { EntityBadge } from '@/components/ui/badges'; import { cn } from '@/lib/cn'; import { clientApi } from '@/lib/client-api'; import type { Suggestion } from '@/lib/types'; /** Root picker: `/search/suggest` autocomplete (≤ 8 rows), keyboard navigable, calls `onPick(suggestion)`. */ export function NodeSearch({ onPick, placeholder = 'Pick a root: model, organization, benchmark…', className, autoFocus = false }: { onPick: (s: Suggestion) => void; placeholder?: string; className?: string; autoFocus?: boolean }) { const [q, setQ] = useState(''); const [items, setItems] = useState([]); const [open, setOpen] = useState(false); const [active, setActive] = useState(0); const abort = useRef(null); const listId = useId(); useEffect(() => { const term = q.trim(); if (term.length < 2) { setItems([]); return; } abort.current?.abort(); const ctl = new AbortController(); abort.current = ctl; const t = setTimeout(() => { clientApi .suggest(term, ctl.signal) .then((r) => { setItems(r.items); setActive(0); setOpen(true); }) .catch(() => undefined); }, 120); return () => clearTimeout(t); }, [q]); const pick = (s: Suggestion) => { onPick(s); setQ(''); setItems([]); setOpen(false); }; return (
setQ(e.target.value)} onFocus={() => items.length && setOpen(true)} onBlur={() => setTimeout(() => setOpen(false), 120)} onKeyDown={(e) => { if (e.key === 'ArrowDown') { e.preventDefault(); setActive((a) => Math.min(items.length - 1, a + 1)); } else if (e.key === 'ArrowUp') { e.preventDefault(); setActive((a) => Math.max(0, a - 1)); } else if (e.key === 'Enter' && items[active]) { e.preventDefault(); pick(items[active]); } else if (e.key === 'Escape') setOpen(false); }} placeholder={placeholder} aria-label="Search a root entity" aria-autocomplete="list" aria-controls={listId} aria-expanded={open} autoComplete="off" autoFocus={autoFocus} className="h-11 min-w-0 flex-1 bg-transparent px-2 text-[15px] text-ink placeholder:text-ink-3 focus:outline-none" data-graph-search />
{open && items.length > 0 && (
    {items.map((s, i) => (
  • ))}
)}
); }