import React, { useState, useEffect, useCallback, useRef } from "react"; import { useSearchParams } from "react-router-dom"; import { Search as SearchIcon, Loader2, SlidersHorizontal, MessageSquareText, Highlighter, Bookmark, } from "lucide-react"; import { clsx } from "clsx"; import { useStore } from "@nanostores/react"; import { searchItems } from "../../api/client"; import type { AnnotationItem } from "../../types"; import Card from "../../components/common/Card"; import { EmptyState } from "../../components/ui"; import LayoutToggle from "../../components/ui/LayoutToggle"; import { $user } from "../../store/auth"; import { $feedLayout } from "../../store/feedLayout"; export default function Search() { const [searchParams, setSearchParams] = useSearchParams(); const initialQuery = searchParams.get("q") || ""; const user = useStore($user); const layout = useStore($feedLayout); const [query, setQuery] = useState(initialQuery); const [results, setResults] = useState([]); const [loading, setLoading] = useState(false); const [hasMore, setHasMore] = useState(false); const [offset, setOffset] = useState(0); const [myItemsOnly, setMyItemsOnly] = useState(false); const [activeFilter, setActiveFilter] = useState( undefined, ); const [platform, setPlatform] = useState<"all" | "margin" | "semble">("all"); const inputRef = useRef(null); const myItemsRef = useRef(myItemsOnly); const fetchIdRef = useRef(0); useEffect(() => { myItemsRef.current = myItemsOnly; }, [myItemsOnly]); const filters = [ { id: "all", label: "All", icon: null }, { id: "commenting", label: "Annotations", icon: MessageSquareText }, { id: "highlighting", label: "Highlights", icon: Highlighter }, { id: "bookmarking", label: "Bookmarks", icon: Bookmark }, ]; const doSearch = useCallback( async (q: string, newOffset = 0, append = false) => { if (!q.trim()) { setResults([]); return; } const id = ++fetchIdRef.current; setLoading(true); const data = await searchItems(q.trim(), { creator: myItemsRef.current && user ? user.did : undefined, limit: 30, offset: newOffset, }); if (id !== fetchIdRef.current) return; if (append) { setResults((prev) => [...prev, ...data.items]); } else { setResults(data.items); } setHasMore(data.hasMore); setOffset(newOffset + data.items.length); setLoading(false); }, [user], ); useEffect(() => { if (initialQuery) { // eslint-disable-next-line react-hooks/set-state-in-effect doSearch(initialQuery); } }, [initialQuery, doSearch]); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (query.trim()) { setSearchParams({ q: query.trim() }); doSearch(query.trim()); } }; const handleDelete = (uri: string) => { setResults((prev) => prev.filter((item) => item.uri !== uri)); }; const handleFilterChange = (id: string) => { setActiveFilter(id === "all" ? undefined : id); }; const filteredResults = results.filter((item) => { if (activeFilter && item.motivation !== activeFilter) return false; if (platform === "margin" && item.uri?.includes("network.cosmik")) return false; if (platform === "semble" && !item.uri?.includes("network.cosmik")) return false; return true; }); return (
setQuery(e.target.value)} placeholder="Search annotations, highlights, bookmarks..." autoFocus className="w-full pl-11 pr-4 py-3 bg-surface-50 dark:bg-surface-800 border border-surface-200 dark:border-surface-700 rounded-xl text-sm focus:outline-none focus:border-primary-400 focus:ring-2 focus:ring-primary-400/20 placeholder:text-surface-400" />
{initialQuery && (
{filters.map((f) => { const isActive = f.id === "all" ? !activeFilter : activeFilter === f.id; return ( ); })} {user && ( )}
)} {loading && results.length === 0 && (
)} {loading && results.length > 0 && (
)} {!loading && initialQuery && filteredResults.length === 0 && ( } title="No results found" message={`Nothing matched "${initialQuery}". Try different keywords.`} /> )} {filteredResults.length > 0 && (

{filteredResults.length} {hasMore ? "+" : ""} results for “{initialQuery}”

{layout === "mosaic" ? (
{filteredResults.map((item) => (
))}
) : (
{filteredResults.map((item) => ( ))}
)} {hasMore && ( )}
)} {!initialQuery && !loading && ( } title="Search your library" message="Find annotations, highlights, and bookmarks by keyword, URL, or tag." /> )}
); }