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

@@ -197,4 +197,161 @@
opacity: 0.4;
}
}
/* ------------------------------------------------------------------
* .doc-prose — styling for AI-generated / rendered HTML documentation.
* Used by <DocHtml>. Targets bare semantic tags (no classes needed on
* the generated markup) and keeps the dark sci-fi look of the rest of
* the docs site.
* ------------------------------------------------------------------ */
.doc-prose {
color: var(--color-fg);
font-size: 0.95rem;
line-height: 1.7;
}
.doc-prose > :first-child {
margin-top: 0;
}
.doc-prose h1 {
font-size: 1.6rem;
font-weight: 700;
color: var(--color-fg-bright);
margin: 0 0 var(--sp-4) 0;
letter-spacing: -0.01em;
}
.doc-prose h2 {
font-size: 1.25rem;
font-weight: 600;
color: var(--color-accent);
margin: var(--sp-6) 0 var(--sp-3) 0;
padding-bottom: var(--sp-2);
border-bottom: 1px solid var(--color-border);
}
.doc-prose h3 {
font-size: 1.05rem;
font-weight: 600;
color: var(--color-cyan);
margin: var(--sp-5) 0 var(--sp-2) 0;
}
.doc-prose p {
margin: 0 0 var(--sp-3) 0;
max-width: 72ch;
}
.doc-prose ul,
.doc-prose ol {
margin: 0 0 var(--sp-3) 0;
padding-left: 1.4rem;
}
.doc-prose ul { list-style: disc; }
.doc-prose ol { list-style: decimal; }
.doc-prose li {
margin: var(--sp-1) 0;
}
.doc-prose a {
color: var(--color-cyan);
text-decoration: underline;
text-underline-offset: 2px;
}
.doc-prose a:hover {
color: var(--color-accent-hover);
}
.doc-prose strong { color: var(--color-fg-bright); }
.doc-prose em { color: var(--color-fg); }
.doc-prose code {
font-family: var(--font-mono);
font-size: 0.85em;
background: var(--color-surface-raised);
border: 1px solid var(--color-border);
border-radius: 4px;
padding: 0.1rem 0.35rem;
color: var(--color-accent);
}
.doc-prose pre {
background: var(--color-bg-subtle);
border: 1px solid var(--color-border);
border-radius: 8px;
padding: var(--sp-3) var(--sp-4);
overflow-x: auto;
margin: 0 0 var(--sp-4) 0;
}
.doc-prose pre code {
background: none;
border: none;
padding: 0;
color: var(--color-fg);
font-size: 0.85rem;
}
.doc-prose blockquote {
border-left: 3px solid var(--color-accent);
background: var(--color-surface);
margin: 0 0 var(--sp-3) 0;
padding: var(--sp-2) var(--sp-4);
color: var(--color-fg-dim);
border-radius: 0 8px 8px 0;
}
.doc-prose hr {
border: none;
border-top: 1px solid var(--color-border);
margin: var(--sp-6) 0;
}
.doc-prose table {
width: 100%;
border-collapse: collapse;
margin: 0 0 var(--sp-4) 0;
font-size: 0.88rem;
}
.doc-prose th,
.doc-prose td {
border-bottom: 1px solid var(--color-border);
padding: var(--sp-2) var(--sp-3);
text-align: left;
}
.doc-prose th {
font-family: var(--font-mono);
font-size: 0.72rem;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--color-muted);
}
.doc-prose td { color: var(--color-fg); }
.doc-prose tr:hover td {
background: var(--color-surface-raised);
}
.doc-prose div.mermaid {
display: flex;
justify-content: center;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: 10px;
padding: var(--sp-4);
margin: 0 0 var(--sp-4) 0;
overflow-x: auto;
}
.doc-prose div.mermaid svg {
max-width: 100%;
height: auto;
}
}