import { useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { Link, Outlet, useNavigate } from "@tanstack/react-router"; import * as stylex from "@stylexjs/stylex"; import { useAuth } from "@/auth/store"; import InlineError from "@/lib/InlineError"; import { versionQuery } from "@/lib/queries"; import PauseWidget from "../features/pause/PauseWidget"; import ReadOnlyConfigBanner from "../features/settings/ReadOnlyConfigBanner"; import RestartBanner from "../features/settings/RestartBanner"; import { styles as shared } from "@/ui/styles"; import { colors } from "@/ui/tokens.stylex"; /** The one breakpoint the shell has: below it the sidebar becomes a drawer. */ const WIDE = "@media (min-width: 768px)"; const DARK = "@media (prefers-color-scheme: dark)"; const NAV_ITEMS = [ { to: "/", label: "Dashboard" }, { to: "/activity", label: "Activity" }, { to: "/clients", label: "Clients" }, { to: "/groups", label: "Groups" }, { to: "/blocklists", label: "Blocklists" }, { to: "/rules", label: "Rules" }, { to: "/local-dns", label: "Local DNS" }, { to: "/upstreams", label: "Upstreams" }, { to: "/diagnostics", label: "Diagnostics" }, { to: "/settings", label: "Settings" }, ] as const; const styles = stylex.create({ navList: { display: "flex", flexDirection: "column", gap: "0.25rem", }, navLink: { display: "block", borderRadius: "0.25rem", paddingInline: "0.75rem", paddingBlock: "0.375rem", textDecorationLine: "none", }, /** The current page reads as a filled chip, heavier than the hover fill. */ navActive: { backgroundColor: { default: "oklch(92% 0.004 286.32)", [DARK]: "oklch(27.4% 0.006 286.033)" }, color: colors.text, fontWeight: 500, }, navIdle: { backgroundColor: { default: "transparent", ":hover": colors.surfaceHover }, color: { default: colors.textSecondary, ":hover": colors.text }, }, versionFooter: { paddingInline: "1rem", paddingBlock: "0.75rem", fontSize: "0.75rem", lineHeight: "1rem", color: colors.textMuted, }, logoutRow: { display: "flex", alignItems: "center", gap: "0.5rem", }, shell: { minHeight: "100dvh", backgroundColor: colors.surface, color: colors.text, display: { default: "block", [WIDE]: "grid" }, gridTemplateColumns: { default: null, [WIDE]: "14rem 1fr" }, }, sidebar: { display: { default: "none", [WIDE]: "flex" }, flexDirection: { default: null, [WIDE]: "column" }, borderRightWidth: 1, borderRightStyle: "solid", borderRightColor: colors.border, }, brand: { paddingInline: "1rem", paddingBlock: "1rem", fontSize: "1.125rem", lineHeight: "1.75rem", fontWeight: 600, }, sidebarNav: { flex: 1, paddingInline: "0.5rem", }, column: { display: "flex", minHeight: "100dvh", flexDirection: "column", }, header: { display: "flex", alignItems: "center", gap: "0.75rem", borderBottomWidth: 1, borderBottomStyle: "solid", borderBottomColor: colors.border, paddingInline: "1rem", paddingBlock: "0.5rem", }, narrowOnly: { display: { default: null, [WIDE]: "none" }, }, narrowBrand: { fontSize: "1.125rem", lineHeight: "1.75rem", fontWeight: 600, display: { default: null, [WIDE]: "none" }, }, headerRight: { marginLeft: "auto", display: "flex", alignItems: "center", gap: "0.75rem", }, drawer: { borderBottomWidth: 1, borderBottomStyle: "solid", borderBottomColor: colors.border, display: { default: null, [WIDE]: "none" }, }, drawerNav: { paddingInline: "0.5rem", paddingBlock: "0.5rem", }, main: { flex: 1, padding: "1rem", }, }); function NavLinks({ onNavigate }: { onNavigate?: () => void }) { return (