"use client" import { useCallback, useEffect, useRef, useState } from "react" import { ChevronsDownUp, ChevronsUpDown, Crosshair, FolderPlus, FolderTree, Plus, Rows3, Search, X, } from "lucide-react" import { useTranslations } from "next-intl" import { toast } from "sonner" import { useActiveFolder } from "@/contexts/active-folder-context" import { useAppWorkspace } from "@/contexts/app-workspace-context" import { useTabContext } from "@/contexts/tab-context" import { useSidebarContext } from "@/contexts/sidebar-context" import { SidebarConversationList, type SidebarConversationListHandle, } from "@/components/conversations/sidebar-conversation-list" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip" import { useIsMobile } from "@/hooks/use-mobile" import { isDesktop, openFileDialog } from "@/lib/platform" import { loadSidebarViewMode, saveSidebarViewMode, type SidebarViewMode, } from "@/lib/sidebar-view-mode-storage" import { cn } from "@/lib/utils" export function Sidebar() { const t = useTranslations("Folder.sidebar") const { activeFolder } = useActiveFolder() const { allFolders, conversations, openFolder } = useAppWorkspace() const { openNewConversationTab } = useTabContext() const { isOpen, toggle } = useSidebarContext() const isMobile = useIsMobile() const listRef = useRef(null) const [viewMode, setViewMode] = useState("flat") const [searchQuery, setSearchQuery] = useState("") useEffect(() => { // Hydrate from localStorage after mount to keep SSR/CSR markup consistent. // eslint-disable-next-line react-hooks/set-state-in-effect setViewMode(loadSidebarViewMode()) }, []) const handleSetViewMode = useCallback((mode: SidebarViewMode) => { setViewMode(mode) saveSidebarViewMode(mode) }, []) useEffect(() => { const onReveal = (e: Event) => { const detail = (e as CustomEvent<{ folderId: number }>).detail if (!detail) return if (viewMode !== "grouped") { setViewMode("grouped") saveSidebarViewMode("grouped") } listRef.current?.revealFolder(detail.folderId) } window.addEventListener("sidebar:reveal-folder", onReveal) return () => { window.removeEventListener("sidebar:reveal-folder", onReveal) } }, [viewMode]) const handleNewConversation = useCallback(() => { if (!activeFolder) return openNewConversationTab(activeFolder.id, activeFolder.path) }, [activeFolder, openNewConversationTab]) const handleOpenFolder = useCallback(async () => { try { if (!isDesktop()) { toast.error(t("toasts.openFolderFailed")) return } const result = await openFileDialog({ directory: true, multiple: false, }) if (!result) return const selected = Array.isArray(result) ? result[0] : result const detail = await openFolder(selected) toast.success(t("toasts.folderOpened", { name: detail.name })) } catch (err) { console.error("[Sidebar] open folder failed:", err) toast.error(t("toasts.openFolderFailed")) } }, [openFolder, t]) if (!isOpen) return null return ( ) }