import { useMemo, useState } from "react"; import { ListTree, Search } from "lucide-react"; import { useTranslation } from "react-i18next"; import { Button } from "@/components/ui/button"; import { Sheet, SheetContent, SheetTitle, } from "@/components/ui/sheet"; import { type PromptAnchor, userPromptAnchors, } from "@/components/thread/promptNavigation"; import { fmtDateTime } from "@/lib/format"; import type { UIMessage } from "@/lib/types"; import { cn } from "@/lib/utils"; interface PromptNavigatorProps { messages: UIMessage[]; onJumpToPrompt: (promptId: string) => void; } export function PromptNavigator({ messages, onJumpToPrompt, }: PromptNavigatorProps) { const { i18n, t } = useTranslation(); const prompts = useMemo(() => userPromptAnchors(messages), [messages]); const [open, setOpen] = useState(false); const [query, setQuery] = useState(""); const filteredPrompts = useMemo(() => { const needle = query.trim().toLocaleLowerCase(); if (!needle) return prompts; return prompts.filter((prompt) => `${prompt.label}\n${prompt.preview}`.toLocaleLowerCase().includes(needle), ); }, [prompts, query]); if (prompts.length === 0) return null; const jump = (promptId: string) => { setOpen(false); onJumpToPrompt(promptId); }; return ( <> setOpen(true)} > {t("thread.promptNavigator.title")} setQuery(event.target.value)} aria-label={t("thread.promptNavigator.search")} placeholder={t("thread.promptNavigator.search")} className={cn( "h-10 w-full rounded-full border border-border bg-background pl-9 pr-3 text-sm", "outline-none transition focus:border-ring focus:ring-2 focus:ring-ring/20", )} /> {filteredPrompts.length > 0 ? ( {filteredPrompts.map((prompt) => ( ))} ) : ( {t("thread.promptNavigator.noResults")} )} > ); } interface PromptNavigatorRowProps { locale: string; onJump: (promptId: string) => void; prompt: PromptAnchor; } function PromptNavigatorRow({ locale, onJump, prompt, }: PromptNavigatorRowProps) { const { t } = useTranslation(); const timestamp = fmtDateTime(prompt.createdAt, locale); return ( onJump(prompt.id)} > {prompt.preview} {timestamp ? ( {timestamp} ) : null} ); }