import { useEffect, useRef, useState } from "react"; import { LayoutDashboard, MessageSquarePlus, MoreVertical, PanelLeftClose, Pencil, Search, Settings, Share2, Tag, Trash2, X, } from "lucide-react"; import type { Conversation, User } from "../lib/types.ts"; import { api } from "../lib/api.ts"; import { Avatar, Button, ContextMenu, FlowerMark, IconButton } from "./ui.tsx"; import { cn } from "../lib/cn.ts"; interface MenuState { x: number; y: number; conv: Conversation; } export function Sidebar({ user, convs, activeId, collapsed, onToggle, onNew, onSelect, onRename, onTopic, onShare, onDelete, onOpenSettings, onOpenAdmin, onLogout, mobileOpen, onCloseMobile, }: { user: User; convs: Conversation[]; activeId: string | null; collapsed: boolean; onToggle: () => void; onNew: () => void; onSelect: (id: string) => void; onRename: (conv: Conversation) => void; onTopic: (conv: Conversation) => void; onShare: (conv: Conversation) => void; onDelete: (conv: Conversation) => void; onOpenSettings: () => void; onOpenAdmin: () => void; onLogout: () => void; mobileOpen: boolean; onCloseMobile: () => void; }) { const [menu, setMenu] = useState(null); const [query, setQuery] = useState(""); const [results, setResults] = useState<(Conversation & { snippet: string | null; snippetRole: string | null })[] | null>(null); const [searching, setSearching] = useState(false); const touchX = useRef(null); // Long-press (mobile right-click): open the context menu, suppress the tap. const pressTimer = useRef | null>(null); const pressPos = useRef<{ x: number; y: number } | null>(null); const suppressClick = useRef(false); function openMenuAt(x: number, y: number, conv: Conversation) { setMenu({ x, y, conv }); } function openMenu(e: React.MouseEvent, conv: Conversation) { e.preventDefault(); e.stopPropagation(); openMenuAt(e.clientX, e.clientY, conv); } function cancelPress() { if (pressTimer.current) clearTimeout(pressTimer.current); pressTimer.current = null; pressPos.current = null; } // Debounced search over titles, topics and old prompts/replies. useEffect(() => { const q = query.trim(); if (q.length < 2) { setResults(null); setSearching(false); return; } setSearching(true); const t = setTimeout(() => { api.searchConvs(q) .then((res) => setResults(res.conversations)) .catch(() => setResults([])) .finally(() => setSearching(false)); }, 250); return () => clearTimeout(t); }, [query]); if (collapsed && !mobileOpen) return null; return ( <> {mobileOpen && (