"use client"; import { useAtomValue } from "jotai"; import dynamic from "next/dynamic"; import { useMemo, useState } from "react"; import { activeTabAtom, type Tab } from "@/atoms/tabs/tabs.atom"; import { Logo } from "@/components/Logo"; import { Spinner } from "@/components/ui/spinner"; import { TooltipProvider } from "@/components/ui/tooltip"; import { useIsMobile } from "@/hooks/use-mobile"; import { useElectronAPI } from "@/hooks/use-platform"; import { cn } from "@/lib/utils"; import { SidebarProvider, useSidebarState } from "../../hooks"; import { SIDEBAR_MAX_WIDTH, SIDEBAR_MIN_WIDTH, useSidebarResize, } from "../../hooks/useSidebarResize"; import type { ChatItem, NavItem, PageUsage, User, Workspace } from "../../types/layout.types"; import { Header } from "../header"; import { IconRail } from "../icon-rail"; import { RightPanel, RightPanelExpandButton, RightPanelToggleButton, } from "../right-panel/RightPanel"; import { MobileSidebar, MobileSidebarTrigger, Sidebar, SidebarCollapseButton, } from "../sidebar"; import type { NotificationsDropdownData } from "../sidebar/NotificationsDropdown"; import { TabBar } from "../tabs/TabBar"; import { WorkspacePanel } from "./WorkspacePanel"; const DocumentTabContent = dynamic( () => import("../tabs/DocumentTabContent").then((m) => ({ default: m.DocumentTabContent })), { ssr: false, loading: () => (
), } ); function MacDesktopTitleBar({ isSidebarCollapsed, onToggleSidebar, disableRightPanelToggle = false, }: { isSidebarCollapsed: boolean; onToggleSidebar: () => void; disableRightPanelToggle?: boolean; }) { return (
); } interface LayoutShellProps { workspaces: Workspace[]; activeWorkspaceId: number | null; onWorkspaceSelect: (id: number) => void; onWorkspaceDelete?: (workspace: Workspace) => void; onWorkspaceSettings?: (workspace: Workspace) => void; onAddWorkspace: () => void; workspace: Workspace | null; navItems: NavItem[]; onNavItemClick?: (item: NavItem) => void; chats: ChatItem[]; activeChatId?: number | null; onNewChat: () => void; onChatSelect: (chat: ChatItem) => void; onChatPrefetch?: (chat: ChatItem) => void; onChatRename?: (chat: ChatItem) => void; onChatDelete?: (chat: ChatItem) => void; onChatArchive?: (chat: ChatItem) => void; onViewAllChats?: () => void; user: User; onSettings?: () => void; onManageMembers?: () => void; onUserSettings?: () => void; onAnnouncements?: () => void; announcementUnreadCount?: number; onLogout?: () => void; pageUsage?: PageUsage; theme?: string; setTheme?: (theme: "light" | "dark" | "system") => void; defaultCollapsed?: boolean; isChatPage?: boolean; isAllChatsPage?: boolean; useWorkspacePanel?: boolean; workspacePanelViewportClassName?: string; workspacePanelContentClassName?: string; children: React.ReactNode; className?: string; notifications?: NotificationsDropdownData; isLoadingChats?: boolean; onTabSwitch?: (tab: Tab) => void; onTabPrefetch?: (tab: Tab) => void; } function MainContentPanel({ isChatPage, onTabSwitch, onTabPrefetch, onNewChat, showRightPanelExpandButton = true, showTopBorder = false, children, }: { isChatPage: boolean; onTabSwitch?: (tab: Tab) => void; onTabPrefetch?: (tab: Tab) => void; onNewChat?: () => void; showRightPanelExpandButton?: boolean; showTopBorder?: boolean; children: React.ReactNode; }) { const activeTab = useAtomValue(activeTabAtom); const isDocumentTab = activeTab?.type === "document"; return (
: null} className="min-w-0" />
{isDocumentTab && activeTab.documentId && activeTab.workspaceId ? (
) : (
{children}
)}
); } function DesktopWorkspaceRegion({ children }: { children: React.ReactNode }) { return
{children}
; } export function LayoutShell({ workspaces, activeWorkspaceId, onWorkspaceSelect, onWorkspaceDelete, onWorkspaceSettings, onAddWorkspace, workspace, navItems, onNavItemClick, chats, activeChatId, onNewChat, onChatSelect, onChatPrefetch, onChatRename, onChatDelete, onChatArchive, onViewAllChats, user, onSettings, onManageMembers, onUserSettings, onAnnouncements, announcementUnreadCount = 0, onLogout, pageUsage, theme, setTheme, defaultCollapsed = false, isChatPage = false, isAllChatsPage = false, useWorkspacePanel = false, workspacePanelViewportClassName, workspacePanelContentClassName, children, className, notifications, isLoadingChats = false, onTabSwitch, onTabPrefetch, }: LayoutShellProps) { const isMobile = useIsMobile(); const electronAPI = useElectronAPI(); const isMacDesktop = electronAPI?.versions.platform === "darwin"; const [mobileMenuOpen, setMobileMenuOpen] = useState(false); const { isCollapsed, setIsCollapsed, toggleCollapsed } = useSidebarState(defaultCollapsed); const { sidebarWidth, handlePointerDown: onResizePointerDown, isDragging: isResizing, } = useSidebarResize(); // Memoize context value to prevent unnecessary re-renders const sidebarContextValue = useMemo( () => ({ isCollapsed, setIsCollapsed, toggleCollapsed }), [isCollapsed, setIsCollapsed, toggleCollapsed] ); // Mobile layout if (isMobile) { return (
setMobileMenuOpen(true)} />} /> {useWorkspacePanel ? ( {children} ) : (
{children}
)}
); } // Desktop layout return (
{isMacDesktop ? ( ) : null}
{/* Sidebar + slide-out panels share one container; overflow visible so panels can overlay main content. Negative right margin closes the flex gap so the sidebar sits flush against the main panel, separated only by a border. */} {useWorkspacePanel ? ( {children} ) : ( <> {/* Main content panel */} {children} {/* Right panel — Report/Editor/Citations/Artifacts (desktop only) */} )}
); }