import { AgentSidebar, focusAgentChat, isAgentChatHomeHandoffActive, navigateWithAgentChatViewTransition, useAgentChatHomeHandoff, useAgentChatHomeHandoffLinks, } from "@agent-native/core/client/agent-chat"; import { getBrowserTabId } from "@agent-native/core/client/hooks"; import { isEmbedAuthActive } from "@agent-native/core/client/host"; import { useT } from "@agent-native/core/client/i18n"; import { InvitationBanner } from "@agent-native/core/client/org"; import { EMBED_MODE_QUERY_PARAM, EMBED_TOKEN_QUERY_PARAM, } from "@agent-native/core/shared"; import { CreativeContextComposerChip } from "@agent-native/creative-context/client"; 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 { GenerationResults } from "@/components/generation/GenerationResults"; import { useImageModelMenu } from "@/hooks/use-image-model-menu"; import { useNavigationState } from "@/hooks/use-navigation-state"; import { ASSETS_CHAT_STORAGE_KEY } from "@/lib/chat"; import { cn } from "@/lib/utils"; import { Header } from "./Header"; import { Sidebar } from "./Sidebar"; interface LayoutProps { children: React.ReactNode; } function isEmbeddedWindow() { if (typeof window === "undefined") return false; try { return window.self !== window.top; } catch { return true; } } function searchParamsEnableEmbeddedMode(search: string): boolean { const params = new URLSearchParams(search); const embedMode = params.get(EMBED_MODE_QUERY_PARAM); return ( params.has(EMBED_TOKEN_QUERY_PARAM) || embedMode === "1" || embedMode === "true" ); } export function Layout({ children }: LayoutProps) { useNavigationState(); const location = useLocation(); const navigate = useNavigate(); const t = useT(); const imageModelMenu = useImageModelMenu(); const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false); const isCreateRoute = location.pathname === "/" || location.pathname.startsWith("/chat/"); const chatHomeHandoffActive = useAgentChatHomeHandoff({ storageKey: ASSETS_CHAT_STORAGE_KEY, activePath: location.pathname, enabled: !isCreateRoute, }); const chatHomeHandoffPending = isAgentChatHomeHandoffActive( ASSETS_CHAT_STORAGE_KEY, ); useAgentChatHomeHandoffLinks({ storageKey: ASSETS_CHAT_STORAGE_KEY, isChatPath: (pathname) => pathname === "/" || pathname.startsWith("/chat/"), // Only preserve the transition when chat activity has recorded an active // handoff; an empty home chat should keep the destination sidebar closed. requireActiveHandoff: true, }); useEffect(() => { setMobileSidebarOpen(false); }, [location.pathname]); const isPicker = location.pathname === "/library"; const hideHeader = location.pathname === "/library" || location.pathname.startsWith("/library/") || location.pathname === "/extensions" || location.pathname.startsWith("/extensions/"); const chromeless = (isPicker && (searchParamsEnableEmbeddedMode(location.search) || isEmbeddedWindow() || isEmbedAuthActive())) || location.pathname.endsWith("/embed"); if (chromeless) { return (
{children}
); } const appFrame = (
{mobileSidebarOpen && (
setMobileSidebarOpen(false)} /> )}
{/* Mobile-only top bar with hamburger */}
{t("navigation.brand")}
{!hideHeader &&
}
{children}
); if (isCreateRoute) { return {appFrame}; } function openCreateChatFullscreen() { focusAgentChat(); navigateWithAgentChatViewTransition(navigate, "/"); } return ( ( )} imageModelMenu={imageModelMenu} composerSlot={} > {appFrame} ); }