| import { Outlet, NavLink, useLocation } from "react-router-dom"; |
| import { |
| Activity, |
| Github, |
| Headphones, |
| BarChart3, |
| Info, |
| Home, |
| } from "lucide-react"; |
| import { cn } from "@/lib/utils"; |
| import WaveformBackground from "./WaveformBackground"; |
| import ThemeToggle from "./ThemeToggle"; |
|
|
| const NAV = [ |
| { to: "/", label: "Home", icon: Home, end: true }, |
| { to: "/demo", label: "Demo", icon: Headphones }, |
| { to: "/benchmarks", label: "Benchmarks", icon: BarChart3 }, |
| { to: "/about", label: "About", icon: Info }, |
| ]; |
|
|
| export default function Layout() { |
| const location = useLocation(); |
| |
| return ( |
| <div className="relative min-h-full"> |
| <WaveformBackground /> |
| |
| <div className="relative z-10 flex min-h-screen flex-col"> |
| {/* ββ Header ββββββββββββββββββββββββββββββββββββββββββββββββ */} |
| <header className="sticky top-0 z-30 border-b border-line/80 bg-bg/85 backdrop-blur-md"> |
| <div className="mx-auto flex max-w-7xl items-center justify-between px-4 py-3 sm:px-6"> |
| {/* Brand */} |
| <NavLink |
| to="/" |
| className="group flex items-center gap-2.5 transition hover:opacity-90" |
| > |
| <span className="relative flex h-9 w-9 items-center justify-center rounded-md border border-cyber/40 bg-cyber/10 text-cyber shadow-cyber"> |
| <Activity className="h-4 w-4" /> |
| <span className="absolute inset-0 animate-pulse rounded-md ring-1 ring-cyber/30" /> |
| </span> |
| <div className="flex flex-col leading-tight"> |
| <span className="font-display text-base font-bold tracking-tight text-ink"> |
| ADFD<span className="text-cyber">.</span> |
| </span> |
| <span className="font-mono text-[9px] uppercase tracking-[0.18em] text-ink-mute"> |
| forensic audio |
| </span> |
| </div> |
| </NavLink> |
| |
| {/* Nav */} |
| <nav className="flex items-center gap-1"> |
| {NAV.map(({ to, label, icon: Icon, end }) => ( |
| <NavLink |
| key={to} |
| to={to} |
| end={end} |
| className={({ isActive }) => |
| cn( |
| "relative flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium transition", |
| isActive |
| ? "bg-cyber/10 text-cyber" |
| : "text-ink-dim hover:bg-surface-alt hover:text-ink" |
| ) |
| } |
| > |
| {({ isActive }) => ( |
| <> |
| <Icon className="h-3.5 w-3.5" /> |
| <span className="hidden sm:inline">{label}</span> |
| {isActive && ( |
| <span className="absolute inset-x-2 -bottom-[7px] h-0.5 bg-cyber" /> |
| )} |
| </> |
| )} |
| </NavLink> |
| ))} |
| <a |
| href="https://github.com" |
| target="_blank" |
| rel="noreferrer" |
| className="ml-1 hidden items-center gap-1.5 rounded-md px-3 py-1.5 text-sm text-ink-dim transition hover:text-ink sm:flex" |
| aria-label="Source" |
| > |
| <Github className="h-3.5 w-3.5" /> |
| </a> |
| </nav> |
| </div> |
| </header> |
| |
| {/* Sub-bar showing current path crumbs (subtle) */} |
| <div className="hidden border-b border-line/40 bg-bg/40 lg:block"> |
| <div className="mx-auto flex max-w-7xl items-center justify-between px-4 py-1 sm:px-6"> |
| <div className="flex items-center gap-1.5 font-mono text-[10px] text-ink-mute"> |
| <span className="text-ink-dim">workspace</span> |
| <span>/</span> |
| <span className="text-cyber">{location.pathname === "/" ? "home" : location.pathname.slice(1)}</span> |
| </div> |
| <div className="flex items-center gap-3 font-mono text-[10px] text-ink-mute"> |
| <span> |
| <span className="mr-1 inline-block h-1.5 w-1.5 animate-pulse rounded-full bg-cyber" /> |
| backend live |
| </span> |
| <span>Β·</span> |
| <span>8 detectors</span> |
| <span>Β·</span> |
| <span>meta-LR ensemble</span> |
| </div> |
| </div> |
| </div> |
| |
| {/* ββ Main ββββββββββββββββββββββββββββββββββββββββββββββββββ */} |
| <main className="flex-1"> |
| <Outlet /> |
| </main> |
| |
| {/* ββ Footer ββββββββββββββββββββββββββββββββββββββββββββββββ */} |
| <footer className="relative z-10 mt-12 border-t border-line/80 bg-bg/85 py-6 text-xs text-ink-dim backdrop-blur-md"> |
| <div className="mx-auto flex max-w-7xl flex-col items-start justify-between gap-3 px-4 sm:flex-row sm:items-center sm:px-6"> |
| <div className="font-mono"> |
| <span className="text-ink-mute">based on</span>{" "} |
| <span className="text-ink"> |
| The Generalization Gap in Audio Deepfake Detection |
| </span> |
| </div> |
| <div className="flex flex-wrap items-center gap-3 font-mono text-ink-mute"> |
| <span className="inline-flex items-center gap-1.5"> |
| <span className="h-1.5 w-1.5 rounded-full bg-cyber" /> |
| live |
| </span> |
| <span>Β·</span> |
| <span>CPU + GPU ready</span> |
| <span>Β·</span> |
| <span>architecture-honest</span> |
| <span className="ml-2"> |
| <ThemeToggle size="sm" showLabel={false} /> |
| </span> |
| </div> |
| </div> |
| </footer> |
| </div> |
| </div> |
| ); |
| } |
|
|