import { Outlet, useLocation } from "react-router"; import { ErrorBoundary } from "@/client/components/error-boundary"; import { Footer } from "@/client/components/footer"; import { Header } from "@/client/components/header"; import { useSessionWithInitialPending } from "@/client/lib/auth-client"; import { useSessionQueryCacheBoundary } from "@/client/lib/query"; // Pure unauthenticated paths — sign-in, auth error, the invite acceptance // flow. `/` is auth-aware (marketing vs launcher) so it's not in this // set; the header variant for `/` is chosen by session state below. const ALWAYS_PUBLIC_PATHS = ["/sign-in", "/auth-error", "/privacy", "/terms"]; const isPublicPath = (pathname: string): boolean => { if (ALWAYS_PUBLIC_PATHS.includes(pathname)) return true; if (pathname.startsWith("/invite/")) return true; return false; }; export const AppShell = () => { const { pathname } = useLocation(); const session = useSessionWithInitialPending(); useSessionQueryCacheBoundary(session.data?.user.id ?? null, session.isPending); // Public chrome (brand + sign-in pill) for unauthenticated paths and // for the marketing landing when no session exists. `/` keeps neutral // loading chrome only during the initial auth resolution so later // refetches don't swap mounted content back to a placeholder. const variant = pathname === "/" && session.isInitialPending ? "loading" : isPublicPath(pathname) || (pathname === "/" && !session.data) ? "public" : "app"; return (