Files
alchemist/web/src/components/Sidebar.astro
2026-04-07 22:13:43 -04:00

137 lines
4.6 KiB
Plaintext

---
import {
Activity,
Sparkles,
Settings,
Wand2,
Video,
Terminal,
BarChart3,
Menu,
X,
} from "lucide-react";
import SystemStatus from "./SystemStatus.tsx";
const currentPath = Astro.url.pathname;
const basePath = "__ALCHEMIST_BASE_URL__";
const withBase = (href: string) => `${basePath}${href === "/" ? "/" : href}`;
const strippedPath =
basePath && currentPath.startsWith(basePath)
? currentPath.slice(basePath.length) || "/"
: currentPath;
const navItems = [
{ href: "/", label: "Dashboard", Icon: Activity },
{ href: "/jobs", label: "Jobs", Icon: Video },
{ href: "/logs", label: "Logs", Icon: Terminal },
{ href: "/stats", label: "Statistics", Icon: BarChart3 },
{ href: "/intelligence", label: "Intelligence", Icon: Sparkles },
{ href: "/convert", label: "Convert", Icon: Wand2 },
{ href: "/settings", label: "Settings", Icon: Settings },
];
---
{/* Mobile top bar */}
<div class="lg:hidden flex items-center justify-between px-4 py-3 bg-helios-surface border-b border-helios-line/60">
<a href={withBase("/")} class="font-bold text-lg tracking-tight text-helios-ink">Alchemist</a>
<button
id="sidebar-hamburger"
aria-label="Open navigation"
class="p-2 rounded-md text-helios-slate hover:bg-helios-surface-soft hover:text-helios-ink transition-colors"
>
<Menu size={22} />
</button>
</div>
{/* Mobile overlay backdrop */}
<div
id="sidebar-backdrop"
class="hidden lg:hidden fixed inset-0 z-40 bg-black/50"
aria-hidden="true"
></div>
{/* Sidebar — hidden on mobile until hamburger opens it */}
<aside
id="sidebar"
class="hidden lg:flex w-64 bg-helios-surface border-r border-helios-line/60 flex-col p-4 gap-4
fixed lg:static inset-y-0 left-0 z-50 lg:z-auto
transition-transform duration-200 lg:transition-none"
>
<a
href={withBase("/")}
class="flex items-center px-3 pb-4 border-b border-helios-line/40"
>
<span class="font-bold text-lg tracking-tight text-helios-ink">
Alchemist
</span>
<button
id="sidebar-close"
aria-label="Close navigation"
class="lg:hidden ml-auto p-1 rounded-md text-helios-slate hover:bg-helios-surface-soft hover:text-helios-ink transition-colors"
>
<X size={18} />
</button>
</a>
<nav class="flex flex-col gap-2 flex-1">
{
navItems.map(({ href, label, Icon }) => {
const isActive =
strippedPath === href ||
(href !== "/" && strippedPath.startsWith(href));
return (
<a
href={withBase(href)}
class:list={[
"flex items-center gap-3 px-3 py-2 rounded-md border-l-2 border-transparent transition-colors whitespace-nowrap",
isActive
? "border-helios-solar bg-helios-solar/10 text-helios-ink font-semibold"
: "text-helios-slate hover:bg-helios-surface-soft hover:text-helios-ink",
]}
>
<Icon size={18} />
<span>{label}</span>
</a>
);
})
}
</nav>
<div class="mt-auto">
<div class="border-t border-helios-line/30 pt-3">
<SystemStatus client:load />
</div>
</div>
</aside>
<script>
function initSidebar() {
const hamburger = document.getElementById("sidebar-hamburger");
const closeBtn = document.getElementById("sidebar-close");
const backdrop = document.getElementById("sidebar-backdrop");
const sidebar = document.getElementById("sidebar");
if (!hamburger || !closeBtn || !backdrop || !sidebar) return;
function openSidebar() {
sidebar!.classList.remove("hidden");
backdrop!.classList.remove("hidden");
document.body.style.overflow = "hidden";
}
function closeSidebar() {
sidebar!.classList.add("hidden");
backdrop!.classList.add("hidden");
document.body.style.overflow = "";
}
hamburger.addEventListener("click", openSidebar);
closeBtn.addEventListener("click", closeSidebar);
backdrop.addEventListener("click", closeSidebar);
}
// Run on initial load and after Astro view transitions
initSidebar();
document.addEventListener("astro:after-swap", initSidebar);
</script>