"use client"; import { Menu } from "@base-ui/react/menu"; import { ChevronDown, ChevronRight, Clock3, FileText, LogOut, Settings, WalletCards, MessageSquareText, SquarePen, Star, UserPlus, UserRound, Users, } from "lucide-react"; import { useEffect, useRef } from "react"; import type { Ref } from "react"; import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarRail, useSidebar, } from "@/components/ui/sidebar"; import { SidebarSessionRow, type SidebarSession, type SidebarSessionControls, } from "@/components/sidebar-session-row"; import { ThemePreferenceMenu } from "@/components/theme-preference-menu"; import { UserAvatar } from "@/components/user-avatar"; import type { BeamAvatar } from "@/lib/beam-avatar"; import { groupSessionsByRecency } from "@/lib/session-groups"; export type SidebarAccount = { name: string; email: string; credits: number; initial: string; avatar: BeamAvatar | null; }; export type SidebarChart = { readonly id: string; readonly name: string; readonly role: "self" | "other"; }; export type AppSidebarProps = { sessions: readonly SidebarSession[]; charts: readonly SidebarChart[]; activeSessionId: string | null; /** A rectification session whose Case is being opened and hydrated; its row says so, statically. */ openingSessionId?: string | null; /** Open failed for this history session; shown under that row only. */ openErrorSessionId?: string | null; openErrorMessage?: string; account: SidebarAccount; accountMenuOpen: boolean; accountTriggerRef: Ref; newChatDisabled: boolean; creatingSession: boolean; sessionControls: SidebarSessionControls; onAccountMenuOpenChange: (open: boolean) => void; onNewChat: () => void; onOpenReports: () => void; onSelectSession: (sessionId: string) => void; onSelectChart: (chartId: string) => void; onAddChart: () => void; onOpenProfile: () => void; onOpenChartLibrary: () => void; onOpenGeneral: () => void; onOpenBilling: () => void; onOpenLogout: () => void; }; export function AppSidebar({ sessions, charts, activeSessionId, openingSessionId = null, openErrorSessionId = null, openErrorMessage = "", account, accountMenuOpen, accountTriggerRef, newChatDisabled, creatingSession, sessionControls, onAccountMenuOpenChange, onNewChat, onOpenReports, onSelectSession, onSelectChart, onAddChart, onOpenProfile, onOpenChartLibrary, onOpenGeneral, onOpenBilling, onOpenLogout, }: AppSidebarProps) { const { isMobile, setOpen, setOpenMobile, state, viewport } = useSidebar(); const firstSessionRef = useRef(null); const historyHeadingRef = useRef(null); const loadMoreRef = useRef(null); const isCollapsedDesktop = state === "collapsed" && !isMobile; const showExpandedContent = !isCollapsedDesktop; const menuPlacement = `${viewport}:${state}`; const previousMenuPlacement = useRef(menuPlacement); const favoriteSessions = sessions.filter((session) => session.pinned); const historySessions = sessions.filter((session) => !session.pinned); const historyGroups = groupSessionsByRecency(historySessions); useEffect(() => { if (!sessionControls.hasMore || !sessionControls.onLoadMore) return; const node = loadMoreRef.current; if (!node) return; const observer = new IntersectionObserver((entries) => { if (entries.some((entry) => entry.isIntersecting)) sessionControls.onLoadMore?.(); }); observer.observe(node); return () => observer.disconnect(); }, [historySessions.length, sessionControls.hasMore, sessionControls.onLoadMore]); useEffect(() => { if (previousMenuPlacement.current !== menuPlacement && accountMenuOpen) { onAccountMenuOpenChange(false); } previousMenuPlacement.current = menuPlacement; }, [accountMenuOpen, menuPlacement, onAccountMenuOpenChange]); function handleNewChat() { onNewChat(); if (isMobile) setOpenMobile(false); } function handleOpenReports() { onOpenReports(); if (isMobile) setOpenMobile(false); } function handleExpandHistory() { setOpen(true); window.requestAnimationFrame(() => { (firstSessionRef.current ?? historyHeadingRef.current)?.focus(); }); } function renderSession(session: SidebarSession, index: number) { return ( sessionControls.onMenuSessionChange(open ? session.id : null)} onSelect={() => { onSelectSession(session.id); if (isMobile) setOpenMobile(false); }} onTogglePinned={() => sessionControls.onTogglePinned(session.id)} onRename={() => sessionControls.onRename(session.id)} onShare={() => sessionControls.onShare(session.id)} onToggleArchived={() => sessionControls.onToggleArchived(session.id)} onDelete={() => sessionControls.onDelete(session.id)} /> ); } return (
{showExpandedContent ? ( <>
{charts.length === 0 ?

还没有星盘

: (
{charts.map((chart) => ( ))}
)}
{favoriteSessions.length === 0 ?

还没有收藏,可在对话的「更多操作」里收藏

: ( {favoriteSessions.map((session, index) => renderSession(session, index))} )}
{historySessions.length === 0 ?

暂无对话,点上方「新建对话」开始

: ( {historyGroups.map((group) => (

{group.label}

{group.sessions.map((session) => renderSession( session, favoriteSessions.length + historySessions.findIndex((item) => item.id === session.id), ))}
))} {sessionControls.hasMore ?
: null} )}
) : ( )}
{account.avatar ? : } {showExpandedContent ? {account.name} : null} {showExpandedContent ?
{account.avatar ? : } {account.name}{account.email}
); }