import { Disclosure, DisclosureButton, DisclosurePanel, Menu, MenuButton, MenuItem, MenuItems, } from "@headlessui/react"; import { Bars3Icon, XMarkIcon, } from "@heroicons/react/24/outline"; import { Link, Outlet, useLocation, useMatches } from "react-router"; import { ErrorState } from "../components/state"; import { TooltipProvider } from "../components/ui"; import { DemoModeProvider } from "../lib/demo-mode"; import { useAuthMe } from "../lib/queries"; import { navigation } from "./navigation"; function activeFor(item: (typeof navigation)[number], pathname: string): boolean { return pathname.startsWith(item.href); } function classNames(...classes: Array) { return classes.filter(Boolean).join(" "); } export default function AppShell() { const { data: auth, error, isLoading } = useAuthMe(); const { pathname } = useLocation(); const matches = useMatches(); if (isLoading && !auth) { return
; } if (error || !auth) { return (
); } const { user, provider, demoMode } = auth; const currentNav = navigation.find((item) => activeFor(item, pathname)); const title = currentNav?.name ?? ""; const lastMatch = matches[matches.length - 1]; const handle = lastMatch?.handle as { headerExtra?: React.ReactNode } | undefined; const headerExtra = handle?.headerExtra; const hideHeader = matches.some((m) => (m.handle as { hideHeader?: boolean } | undefined)?.hideHeader); const hideTitle = matches.some((m) => (m.handle as { hideTitle?: boolean } | undefined)?.hideTitle); const wide = matches.some((m) => (m.handle as { wide?: boolean } | undefined)?.wide); const fullHeight = matches.some( (m) => (m.handle as { fullHeight?: boolean } | undefined)?.fullHeight, ); const maxWidth = wide ? "" : "max-w-5xl"; return (
Fabro
{navigation.map((item) => { const current = activeFor(item, pathname); return (
Open user menu Profile
Open main menu
{navigation.map((item) => { const current = activeFor(item, pathname); return ( ); })}
{user.name}
{user.email}
Profile {provider !== "tailscale" && (
Sign out
)}
{!hideHeader && (
{!hideTitle && (

{title}

)} {headerExtra &&
{headerExtra}
}
)}
); } /** * The scrollable content region. Inset on the right by the docked Ask Fabro * sidebar's width so opening the sidebar shifts the page content left rather * than covering it. */ function ShellMain({ fullHeight, maxWidth, }: { fullHeight: boolean; maxWidth: string; }) { return (
); }