"use client"; import type { SearchItemType, SharedProps, } from "fumadocs-ui/components/dialog/search"; import { SearchDialog, SearchDialogClose, SearchDialogContent, SearchDialogFooter, SearchDialogHeader, SearchDialogInput, SearchDialogList, SearchDialogListItem, SearchDialogOverlay, useSearch, } from "fumadocs-ui/components/dialog/search"; import { ArrowRight, Search } from "lucide-react"; import { useRouter } from "next/navigation"; import { useMemo } from "react"; import { Client } from "typesense"; import { useTypesenseSearch } from "typesense-fumadocs-adapter/client"; import { usePages } from "@/app/docs/provider"; const typesenseClient = (() => { const url = process.env.NEXT_PUBLIC_TYPESENSE_SERVER_URL; const apiKey = process.env.NEXT_PUBLIC_TYPESENSE_SEARCH_API_KEY; if (!url || !apiKey) { // Return a dummy client so the app works without Typesense configured return new Client({ nodes: [{ host: "localhost", port: 8108, protocol: "http" }], apiKey: "dummy", }); } let serverUrl: URL; try { serverUrl = new URL(url); } catch { return new Client({ nodes: [{ host: "localhost", port: 8108, protocol: "http" }], apiKey: "dummy", }); } return new Client({ nodes: [ { host: serverUrl.hostname, port: Number(serverUrl.port) || (serverUrl.protocol === "https:" ? 443 : 80), protocol: serverUrl.protocol.replace(":", ""), }, ], apiKey, }); })(); export default function CustomSearchDialog(props: SharedProps) { const { search, setSearch, query } = useTypesenseSearch({ typesenseCollectionName: "better-auth-docs", client: typesenseClient!, /** * Non-legacy mode leaves raw tags in content at the moment, * which renders as plain text in fumadocs-ui */ legacy: true, }); const pages = usePages(); const router = useRouter(); const pageTreeAction = useMemo(() => { if (search.length === 0) return; const normalized = search.toLowerCase(); for (const page of pages) { if (!page.name.toLowerCase().includes(normalized)) continue; return { id: "quick-action", type: "action", node: (

Jump to{" "} {page.name}

), onSelect: () => router.push(page.url), }; } }, [router, search, pages]); return ( ( div:last-child]:line-clamp-2" : undefined } /> )} /> Search powered by{" "} Typesense ); } function LoadingSearchIcon() { const { isLoading } = useSearch(); return ( ); }