import { Link, useRouterState } from "@tanstack/react-router";
import {
Activity,
GaugeCircle,
KeyRound,
LibraryBig,
MonitorPlay,
MoreHorizontal,
ScrollText,
Server,
Settings,
} from "lucide-react";
import { motion, stagger } from "motion/react";
import { type ReactNode, useState } from "react";
import { BrandMark } from "@/components/brand-mark";
import { Wordmark } from "@/components/wordmark";
import { changeLocale, type Locale, locales, useLocale } from "@/lib/i18n";
import { cn } from "@/lib/utils";
import { m } from "@/paraglide/messages";
const MLink = motion(Link);
const NAV = [
{ to: "/", icon: Activity, label: () => m.nav_dashboard() },
{ to: "/host", icon: Server, label: () => m.nav_host() },
{ to: "/displays", icon: MonitorPlay, label: () => m.nav_displays() },
{ to: "/library", icon: LibraryBig, label: () => m.nav_library() },
{ to: "/stats", icon: GaugeCircle, label: () => m.nav_stats() },
{ to: "/logs", icon: ScrollText, label: () => m.nav_logs() },
{ to: "/pairing", icon: KeyRound, label: () => m.nav_pairing() },
{ to: "/settings", icon: Settings, label: () => m.nav_settings() },
] as const;
// On phones a flat 8-tab bar is too cramped, so the first four are pinned and the rest live behind a
// "More" tab that opens a sheet above the bar. Keep it ≤ 5 slots including "More".
const MOBILE_PRIMARY = NAV.slice(0, 4);
const MOBILE_OVERFLOW = NAV.slice(4);
export function AppShell({ children }: { children: ReactNode }) {
// Read the locale so the whole shell re-renders on a language switch.
useLocale();
return (
{/* Desktop sidebar (≥ sm). Sticky at viewport height: the page (body) scrolls with
long content, but the sidebar stays pinned — the explicit h-dvh stops the flex
stretch that would otherwise grow it (and push the language switcher) below the
fold. overflow-y-auto lets the nav itself scroll on very short viewports. */}
{/* Mobile top bar (< sm): brand + language. The sidebar is hidden here. */}
{/* pb-24 leaves room for the fixed bottom nav on mobile. */}
{children}
);
}
/** Mobile bottom navigation (< sm): four pinned tabs + a "More" tab whose sheet holds the rest. */
function MobileNav() {
const [moreOpen, setMoreOpen] = useState(false);
const pathname = useRouterState({ select: (s) => s.location.pathname });
// Highlight "More" when the current route lives in the overflow.
const overflowActive = MOBILE_OVERFLOW.some(
(n) => pathname === n.to || pathname.startsWith(`${n.to}/`),
);
// Fixed two-line-tall label box so a 1- or 2-line label (labels vary by locale) keeps every icon
// at the same height.
const tab =
"flex flex-1 flex-col items-center justify-center gap-1 px-0.5 py-2 text-muted-foreground transition-colors";
const lbl =
"flex h-7 w-full items-center justify-center text-center text-[10px] leading-tight";
return (
<>
{/* Tap-outside backdrop, under the bar (z-50) but over the page. */}
{moreOpen && (