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.
123 lines
5.3 KiB
TypeScript
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>
|
|
);
|
|
}
|