"use client"; import { useState, useEffect, useCallback, Suspense } from "react"; import Link from "next/link"; import { useSearchParams } from "next/navigation"; import { DEFAULT_SECTIONS, type SectionConfig } from "@mcpedia/config/sections"; interface DocHit { slug: string; title: string; section: string; score: number; snippet: string; } type SearchMode = "hybrid" | "keyword" | "semantic"; function SearchContent() { const searchParams = useSearchParams(); const initialQ = searchParams.get("q") ?? ""; const initialMode = (searchParams.get("mode") as SearchMode) ?? "hybrid"; const [query, setQuery] = useState(initialQ); const [mode, setMode] = useState( ["hybrid", "keyword", "semantic"].includes(initialMode) ? initialMode : "hybrid", ); const [sections, setSections] = useState(DEFAULT_SECTIONS); const [selectedSection, setSelectedSection] = useState("all"); const [results, setResults] = useState([]); const [loading, setLoading] = useState(false); useEffect(() => { fetch("/api/sections") .then((r) => r.json()) .then((data) => { if (Array.isArray(data) && data.length > 0) { setSections(data); } }) .catch(() => {}); }, []); const handleSearch = useCallback(async (q: string, searchMode: SearchMode) => { if (!q.trim()) { setResults([]); return; } setLoading(true); try { const res = await fetch( `/api/search?q=${encodeURIComponent(q)}&mode=${searchMode}&limit=30`, ); const data = await res.json(); setResults(data.results || []); } catch { setResults([]); } finally { setLoading(false); } }, []); useEffect(() => { if (initialQ) { handleSearch(initialQ, mode); } }, [initialQ, handleSearch, mode]); function handleChange(e: React.ChangeEvent) { const val = e.target.value; setQuery(val); handleSearch(val, mode); } function handleModeChange(newMode: SearchMode) { setMode(newMode); if (query.trim()) { handleSearch(query, newMode); } } const filteredResults = selectedSection === "all" ? results : results.filter((r) => r.section === selectedSection); return (
{/* Header */}

Search Knowledge Base

Find answers across all documentation, CTF writeups, research notes, and semantic vector chunks.

{/* Search Box & Controls */}
{/* Search Input */}
{query && ( )}
{/* Search Mode & Section Filters */}
{/* Mode Selector */}
Algorithm: {[ { id: "hybrid", label: "⚡ Hybrid (RRF)", desc: "FTS + Vector Fusion" }, { id: "keyword", label: "🔍 Keyword", desc: "PostgreSQL FTS" }, { id: "semantic", label: "🧠 Semantic", desc: "Embeddings Cosine" }, ].map((m) => ( ))}
{/* Section Filter Pills */}
{sections.map((s) => ( ))}
{/* Loading State */} {loading && (
Searching knowledge base...
)} {/* Empty State */} {!loading && query && filteredResults.length === 0 && (
🔍

No matching documents

Try switching algorithm mode to Hybrid or Semantic, or search with different keywords.

)} {/* Initial Hint */} {!loading && !query && (

Popular search queries:

{["stack alignment", "ret2win", "bullmq worker", "streamable http", "full-text search", "caddy reverse proxy"].map((term) => ( ))}
)} {/* Results List */} {!loading && filteredResults.length > 0 && (
Found {filteredResults.length} result{filteredResults.length !== 1 ? "s" : ""} Ranked by {mode} score
{filteredResults.map((r) => (
{r.section} /{r.slug}
Score: {r.score.toFixed(3)}

{r.title}

{r.snippet && (

)} ))}

)}
); } export default function SearchPage() { return (

Search

Loading search module...

} > ); }