"use client"; import { useEffect, useRef, useState } from "react"; import { List, Plus, Pencil, Trash2, Check, X } from "lucide-react"; import { cn } from "@/lib/utils"; import { useLocale } from "@/lib/i18n/provider"; import { useConversationList } from "./use-conversation-list"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; export type Conversation = { id: string; title: string | null; createdAt: string; updatedAt: string }; /** * Dropdown skin for the general assistant's conversation list — a narrow * popover, used in the drawer/mobile layout. The desktop-fullscreen layout * uses ConversationSidebar (a persistent list) instead; both share their * fetch/rename/delete logic via useConversationList so they can't diverge. * The per-recipe chat stays single-threaded, one conversation per recipe, * by design — it never uses either of these. */ export function ConversationMenu({ activeId, onSelect, onCreated, onDeletedActive, className, }: { activeId: string | null; onSelect: (id: string) => void; onCreated: (conversation: Conversation) => void; onDeletedActive: () => void; className?: string; }) { const { locale } = useLocale(); const [open, setOpen] = useState(false); const containerRef = useRef(null); const { t, conversations, loading, load, create, renamingId, renameValue, setRenameValue, startRename, cancelRename, submitRename, deleteTargetId, setDeleteTargetId, confirmDelete, } = useConversationList(onDeletedActive, activeId); useEffect(() => { function handleClickOutside(e: MouseEvent) { if (containerRef.current && !containerRef.current.contains(e.target as Node)) { setOpen(false); cancelRename(); } } document.addEventListener("mousedown", handleClickOutside); return () => document.removeEventListener("mousedown", handleClickOutside); }, [cancelRename]); function handleToggle() { setOpen((v) => !v); void load(); } async function handleCreate() { const created = await create(); if (!created) return; onCreated(created); setOpen(false); } return (
{open && (
{loading &&

{t("loading")}

} {!loading && conversations.length === 0 && (

{t("empty")}

)} {!loading && conversations.map((c) => (
{renamingId === c.id ? ( <> setRenameValue(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") void submitRename(); if (e.key === "Escape") cancelRename(); }} maxLength={100} className="flex-1 min-w-0 bg-transparent border-b border-primary text-sm outline-none" /> ) : ( <> )}
))}
)} { if (!v) setDeleteTargetId(null); }}> {t("deleteConfirmTitle")} {t("deleteConfirmDescription")} {t("deleteCancel")} { void confirmDelete(); }} className="bg-destructive text-destructive-foreground hover:bg-destructive/90" > {t("deleteConfirm")}
); }