Files
Space-Game/apps/docs/src/components/Sidebar.tsx
francy51 c24a6106bf feat(docs): add custom documentation pages with AI beautify
User-created dynamic doc pages live at /docs/custom/:slug, persisted in the new
backend. The editor offers "Beautify with AI", which regenerates the page as
structured HTML with Mermaid diagrams and replaces the raw markdown source (the
beautified version becomes the page's canonical content and survives edits).

Adds a DocHtml renderer that lazily renders Mermaid blocks, a purple design
token, sidebar/topbar entries for custom pages, and routing.
2026-06-16 15:44:16 -04:00

123 lines
5.3 KiB
TypeScript

import { NavLink } from "react-router-dom";
import { navSections } from "../data/nav";
import { useCustomPages } from "../lib/customPagesStore";
import { Link } from "react-router-dom";
type SidebarProps = {
collapsed: boolean;
};
export function Sidebar({ collapsed }: SidebarProps) {
const { pages } = useCustomPages();
return (
<aside
className={[
"z-10 flex h-screen flex-col overflow-hidden border-r border-border bg-surface transition-[width,min-width] duration-[250ms] max-md:h-auto max-md:max-h-[142px] max-md:w-full max-md:min-w-0 max-md:flex-shrink-0 max-md:border-r-0 max-md:border-b",
collapsed ? "w-sidebar-collapsed min-w-sidebar-collapsed max-md:w-full" : "w-sidebar min-w-sidebar",
].join(" ")}
>
<div
className={[
"flex min-h-topbar items-center gap-3 border-b border-border px-5 py-4 max-md:min-h-[38px] max-md:justify-start max-md:px-4 max-md:py-2",
collapsed ? "justify-center px-4" : "",
].join(" ")}
>
<div
className={[
"overflow-hidden whitespace-nowrap font-mono text-[0.8rem] font-bold uppercase tracking-[0.05em] text-accent max-md:text-[0.72rem]",
collapsed ? "text-0 max-md:text-[0.72rem]" : "",
].join(" ")}
>
GDD<span className="text-cyan">::</span>DOCS
</div>
</div>
<nav
className={[
"flex-1 overflow-y-auto px-3 py-3 max-md:flex max-md:gap-2 max-md:overflow-x-auto max-md:overflow-y-hidden max-md:px-3 max-md:pb-3 max-md:pt-2",
collapsed ? "px-2 py-2" : "",
].join(" ")}
>
{navSections.map((section) => (
<div key={section.title} className="max-md:flex max-md:flex-none max-md:gap-2">
<div
className={[
"overflow-hidden whitespace-nowrap px-3 pb-2 pt-4 font-mono text-[0.65rem] uppercase tracking-[0.1em] text-muted max-md:hidden",
collapsed ? "h-2 p-2 text-0" : "",
].join(" ")}
>
{section.title}
</div>
{section.items.map((item) => (
<NavLink
key={item.path}
className={({ isActive }) =>
[
"mb-0.5 flex select-none items-center gap-3 overflow-hidden whitespace-nowrap rounded-lg px-3 py-2 text-[0.85rem] text-fg-dim no-underline transition-all duration-150 hover:bg-surface-raised hover:text-fg-bright max-md:mb-0 max-md:justify-start",
collapsed ? "justify-center p-3 max-md:px-3 max-md:py-2" : "",
isActive ? "border border-accent/25 bg-accent/8 text-accent" : "",
].join(" ")
}
to={item.path}
>
<span className="min-w-5 text-center text-base">{item.icon}</span>
<span className={collapsed ? "hidden max-md:inline" : ""}>{item.label}</span>
</NavLink>
))}
</div>
))}
{/* User-created dynamic pages */}
<div className="max-md:flex max-md:flex-none max-md:gap-2">
<div
className={[
"flex items-center justify-between overflow-hidden whitespace-nowrap px-3 pb-2 pt-4 font-mono text-[0.65rem] uppercase tracking-[0.1em] text-muted max-md:hidden",
collapsed ? "h-2 p-2 text-0" : "",
].join(" ")}
>
<span>Custom Pages</span>
<Link
to="/docs/custom/new"
className="text-accent no-underline hover:text-accent-hover"
title="Add a custom page"
onClick={(e) => e.stopPropagation()}
>
+
</Link>
</div>
<NavLink
end
className={({ isActive }) =>
[
"mb-0.5 flex select-none items-center gap-3 overflow-hidden whitespace-nowrap rounded-lg px-3 py-2 text-[0.85rem] text-fg-dim no-underline transition-all duration-150 hover:bg-surface-raised hover:text-fg-bright max-md:mb-0 max-md:justify-start",
collapsed ? "justify-center p-3 max-md:px-3 max-md:py-2" : "",
isActive ? "border border-accent/25 bg-accent/8 text-accent" : "",
].join(" ")
}
to="/docs/custom"
>
<span className="min-w-5 text-center text-base"></span>
<span className={collapsed ? "hidden max-md:inline" : ""}>Custom Pages</span>
</NavLink>
{!collapsed &&
pages.map((page) => (
<NavLink
key={page.id}
className={({ isActive }) =>
[
"mb-0.5 flex select-none items-center gap-3 overflow-hidden whitespace-nowrap rounded-lg px-3 py-2 pl-7 text-[0.82rem] text-fg-dim no-underline transition-all duration-150 hover:bg-surface-raised hover:text-fg-bright max-md:mb-0",
isActive ? "border border-accent/25 bg-accent/8 text-accent" : "",
].join(" ")
}
to={`/docs/custom/${page.slug}`}
>
<span className="min-w-5 text-center text-sm">{page.icon}</span>
<span className="truncate">{page.title}</span>
</NavLink>
))}
</div>
</nav>
</aside>
);
}