import { useState, useRef, useMemo } from 'react' import { apiFetch } from '@core/lib/api' import { useAppPath } from '@core/hooks/useAppPath' import { Card, CardContent } from '@core/components/ui/card' import { Button } from '@core/components/ui/button' import { Badge } from '@core/components/ui/badge' import { Input } from '@core/components/ui/input' import { Label } from '@core/components/ui/label' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@core/components/ui/select' import { Search, Zap, FileCode2, ChevronRight, Code2, RefreshCw, AlertTriangle, } from 'lucide-react' // ─── TYPES ──────────────────────────────────────────────────────────────────── interface SearchResult { id: string chunk_name: string file_path: string macro: string micro?: string chunk_type: string doc_confidence: string similarity: number | null match_type: 'semantic' | 'text' } // ─── HELPERS ────────────────────────────────────────────────────────────────── const CHUNK_TYPE_COLORS: Record = { function: 'bg-indigo-100 text-indigo-700 border-indigo-200', class: 'bg-violet-100 text-violet-700 border-violet-200', interface: 'bg-emerald-100 text-emerald-700 border-emerald-200', type: 'bg-cyan-100 text-cyan-700 border-cyan-200', component: 'bg-amber-100 text-amber-700 border-amber-200', hook: 'bg-pink-100 text-pink-700 border-pink-200', route: 'bg-red-100 text-red-700 border-red-200', config: 'bg-slate-100 text-slate-700 border-slate-200', } const DOC_CONFIDENCE_LABEL: Record = { human_verified: { label: 'Verified', className: 'bg-green-100 text-green-700 border-green-200' }, ai_generated: { label: 'AI Docs', className: 'bg-blue-100 text-blue-700 border-blue-200' }, unverified_legacy: { label: 'No Docs', className: 'bg-slate-100 text-slate-500 border-slate-200' }, } function SimilarityBar({ score }: { score: number | null }) { if (score === null) return null const pct = Math.round(score * 100) return (
{pct}%
) } function ResultCard({ result, projectId }: { result: SearchResult; projectId: string }) { const appPath = useAppPath() const typeClass = CHUNK_TYPE_COLORS[result.chunk_type] || CHUNK_TYPE_COLORS.config const docCfg = DOC_CONFIDENCE_LABEL[result.doc_confidence] || DOC_CONFIDENCE_LABEL.unverified_legacy return (
{result.chunk_name} {result.chunk_type} {docCfg.label} {result.match_type === 'semantic' && ( semantic )}
{result.file_path}
{result.macro && (

{result.macro}

)}
) } // ─── MAIN PAGE ──────────────────────────────────────────────────────────────── type SortBy = 'relevance' | 'name' | 'file' | 'doc_confidence' type ChunkTypeFilter = 'all' | 'function' | 'class' | 'interface' | 'type' | 'component' | 'hook' | 'route' | 'config' type DocConfidenceFilter = 'all' | 'human_verified' | 'ai_generated' | 'unverified_legacy' export default function CILSearchPage() { const appPath = useAppPath() const [query, setQuery] = useState('') const [projectId, setProjectId] = useState('') const [mode, setMode] = useState<'hybrid' | 'semantic' | 'text'>('hybrid') const [limit, setLimit] = useState('20') const [chunkTypeFilter, setChunkTypeFilter] = useState('all') const [docConfidenceFilter, setDocConfidenceFilter] = useState('all') const [sortBy, setSortBy] = useState('relevance') const [results, setResults] = useState([]) const [loading, setLoading] = useState(false) const [searched, setSearched] = useState(false) const [error, setError] = useState(null) const inputRef = useRef(null) const handleSearch = async () => { if (!query.trim()) return setLoading(true) setError(null) setSearched(true) try { const params = new URLSearchParams({ action: 'search', q: query.trim(), mode, limit, }) if (projectId) params.set('project_id', projectId) const res = await apiFetch(`/api/custom_cil-graph?${params.toString()}`) const data = await res.json() setResults(data?.results || data?.data?.results || []) } catch (err) { setError(String(err)) setResults([]) } finally { setLoading(false) } } // Client-side filter + sort (applied after server results arrive) const displayResults = useMemo(() => { let r = [...results] if (chunkTypeFilter !== 'all') r = r.filter(x => x.chunk_type === chunkTypeFilter) if (docConfidenceFilter !== 'all') r = r.filter(x => x.doc_confidence === docConfidenceFilter) if (sortBy === 'name') r.sort((a, b) => a.chunk_name.localeCompare(b.chunk_name)) else if (sortBy === 'file') r.sort((a, b) => a.file_path.localeCompare(b.file_path)) else if (sortBy === 'doc_confidence') { const order: Record = { human_verified: 0, ai_generated: 1, unverified_legacy: 2 } r.sort((a, b) => (order[a.doc_confidence] ?? 3) - (order[b.doc_confidence] ?? 3)) } // 'relevance' keeps server order (similarity desc) return r }, [results, chunkTypeFilter, docConfidenceFilter, sortBy]) const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter') handleSearch() } return (
{/* Breadcrumb */}

Chunk Search

Semantic + text search across all indexed code chunks

{/* Search bar */}
setQuery(e.target.value)} onKeyDown={handleKeyDown} className="flex-1" autoFocus />
{/* Filters — row 1: search params */}
setProjectId(e.target.value)} className="h-7 w-48 text-xs font-mono" />
{/* Filters — row 2: client-side refine + sort */}
{(chunkTypeFilter !== 'all' || docConfidenceFilter !== 'all' || sortBy !== 'relevance') && ( )}
{/* Results */} {error && (
{error}
)} {searched && !loading && !error && (

{displayResults.length === 0 ? (results.length > 0 ? `0 of ${results.length} results match filters` : 'No results found') : `${displayResults.length}${displayResults.length !== results.length ? ` of ${results.length}` : ''} result${displayResults.length !== 1 ? 's' : ''}`} {results.length > 0 && mode !== 'text' && ( semantic ranking active )}

{results.length > 0 && ( )}
{displayResults.length === 0 && (

No chunks match your query.

{mode === 'semantic' && (

Try switching to Hybrid mode to also search chunk names.

)} {mode !== 'semantic' && (

Try a different query or run Pass 3 to generate embeddings first.

)}
)} {displayResults.map(result => ( ))}
)} {!searched && (

Search across all code chunks by purpose, inputs, or behaviour

{[ 'handle authentication middleware', 'send email notification', 'validate JWT token', 'write embeddings to database', 'resolve type ID by slug', ].map(suggestion => ( ))}
)}
) }