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.
This commit is contained in:
2026-06-16 15:44:16 -04:00
parent efdc34637e
commit c24a6106bf
11 changed files with 987 additions and 41 deletions

View File

@@ -1,11 +1,15 @@
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={[
@@ -62,6 +66,56 @@ export function Sidebar({ collapsed }: SidebarProps) {
))}
</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>
);