diff --git a/src/components/SearchDialog.tsx b/src/components/SearchDialog.tsx index 203dab2..906057a 100644 --- a/src/components/SearchDialog.tsx +++ b/src/components/SearchDialog.tsx @@ -25,8 +25,11 @@ import { } from "@/lib/search.shared"; import { createStaticSearchClient, preloadStaticSearch } from "@/lib/static-search-client"; import { closeSuperchat, isSuperchatOpen, openSuperchat } from "@/lib/superchat"; +import { cn } from "@/lib/cn"; import { buildDocsApiPath } from "@/lib/url-base"; +const SEARCH_DELAY_MS = 100; + function scheduleIdle(callback: () => void) { if (typeof window === "undefined") return () => undefined; @@ -41,6 +44,8 @@ function scheduleIdle(callback: () => void) { export function CustomSearchDialog(props: SharedProps) { const [scope, setScope] = useState(); + const [debouncedSearch, setDebouncedSearch] = useState(""); + const [isAskAIHintActive, setIsAskAIHintActive] = useState(false); const tags = getSearchGroupsForScope(scope); const client = process.env.NODE_ENV === "production" @@ -54,14 +59,48 @@ export function CustomSearchDialog(props: SharedProps) { }); const { search, setSearch, query } = useDocsSearch({ client, - delayMs: 100, + delayMs: SEARCH_DELAY_MS, }); + const isSearchSettled = search.trim() === debouncedSearch.trim(); + const hasConfirmedNoResults = + debouncedSearch.trim().length > 0 && + isSearchSettled && + !query.isLoading && + !query.error && + Array.isArray(query.data) && + query.data.length === 0; + const hasConfirmedResults = + debouncedSearch.trim().length > 0 && + isSearchSettled && + !query.isLoading && + !query.error && + Array.isArray(query.data) && + query.data.length > 0; const launchSuperchat = useEffectEvent((message = search) => { const seededMessage = message.trim(); if (!openSuperchat(seededMessage)) return; props.onOpenChange(false); }); + useEffect(() => { + const handle = window.setTimeout(() => { + setDebouncedSearch(search); + }, SEARCH_DELAY_MS); + + return () => window.clearTimeout(handle); + }, [search]); + + useEffect(() => { + if (hasConfirmedNoResults) { + setIsAskAIHintActive(true); + return; + } + + if (debouncedSearch.trim().length === 0 || query.error || hasConfirmedResults) { + setIsAskAIHintActive(false); + } + }, [debouncedSearch, hasConfirmedNoResults, hasConfirmedResults, query.error]); + useEffect(() => { if (process.env.NODE_ENV !== "production") return; const saveData = Boolean( @@ -113,13 +152,22 @@ export function CustomSearchDialog(props: SharedProps) {