import { AgentSidebar, focusAgentChat, isAgentChatHomeHandoffActive, navigateWithAgentChatViewTransition, useAgentChatHomeHandoff, useAgentChatHomeHandoffLinks, } from "@agent-native/core/client/agent-chat"; import { useT } from "@agent-native/core/client/i18n"; import { HeaderActionsProvider } from "@agent-native/toolkit/app-shell"; import { IconMenu2 } from "@tabler/icons-react"; import { useState, useEffect } from "react"; import { useLocation, useNavigate } from "react-router"; import { Button } from "@/components/ui/button"; import { Sheet, SheetContent, SheetDescription, SheetTitle, } from "@/components/ui/sheet"; import { APP_TITLE } from "@/lib/app-config"; import { TAB_ID } from "@/lib/tab-id"; import { Header } from "./Header"; import { Sidebar } from "./Sidebar"; interface LayoutProps { children: React.ReactNode; } const SIDEBAR_COLLAPSE_KEY = "chat.sidebar.collapsed"; /** * Routes whose page renders its own toolbar. Layout still wraps these with the * left Sidebar and agent surfaces but skips the global Header so they don't * double-stack chrome. */ function routeOwnsToolbar(pathname: string): boolean { return ( pathname === "/" || pathname.startsWith("/chat/") || pathname === "/database" || pathname.startsWith("/extensions") ); } export function Layout({ children }: LayoutProps) { const location = useLocation(); const navigate = useNavigate(); const t = useT(); const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false); const [sidebarCollapsed, setSidebarCollapsed] = useState(true); const isChatRoute = location.pathname === "/" || location.pathname.startsWith("/chat/"); const chatHomeHandoffActive = useAgentChatHomeHandoff({ storageKey: "chat", activePath: location.pathname, enabled: !isChatRoute, }); const chatHomeHandoffPending = isAgentChatHomeHandoffActive("chat"); useAgentChatHomeHandoffLinks({ storageKey: "chat", isChatPath: (pathname) => pathname === "/" || pathname.startsWith("/chat/"), requireActiveHandoff: true, }); useEffect(() => { setMobileSidebarOpen(false); }, [location.pathname]); useEffect(() => { const closeMobileSidebar = () => setMobileSidebarOpen(false); window.addEventListener("agent-chat:open-thread", closeMobileSidebar); return () => { window.removeEventListener("agent-chat:open-thread", closeMobileSidebar); }; }, []); useEffect(() => { try { const stored = window.localStorage.getItem(SIDEBAR_COLLAPSE_KEY); if (stored !== null) setSidebarCollapsed(stored === "1"); } catch { // Ignore storage access errors; the default collapsed state still works. } }, []); useEffect(() => { try { window.localStorage.setItem( SIDEBAR_COLLAPSE_KEY, sidebarCollapsed ? "1" : "0", ); } catch { // Ignore storage access errors. } }, [sidebarCollapsed]); const ownsToolbar = routeOwnsToolbar(location.pathname); function openAskAgentFullscreen() { focusAgentChat(); navigateWithAgentChatViewTransition(navigate, "/"); } const contentFrame = (
{isChatRoute ? (
{APP_TITLE}
) : ownsToolbar ? (
) : (
setMobileSidebarOpen(true)} /> )}
{children}
); return (
{t("navigation.navigation")} {t("navigation.navigationDescription")} {isChatRoute ? (
{contentFrame}
) : ( {contentFrame} )}
); }