T48a Docs · Frame: a Docs tab, /docs/ and /docs/$page/, Markdown guides with a rail, an on-this-page list, previous and next and code blocks; full Rust samples are compiled examples shown as text

This commit is contained in:
ZacharyZhang-NY committed 2026-10-01 11:40:50 -04:00
1 parent 157176e2fb
commit fba90c22e6
12 files changed
+305 -6

No files matched your search

+81
View File
@@ -0,0 +1,81 @@
import { Link, useNavigate, useParams } from "@tanstack/react-router";
import { useEffect, type MouseEvent } from "react";
import { guides, type Guide } from "../docs/guides";
import { NotFound } from "./NotFound";
const to = (guide: Guide) => (guide.slug ? `/docs/${guide.slug}/` : "/docs/");
async function copy(button: HTMLButtonElement) {
const code = button.parentElement?.querySelector("code")?.textContent;
if (code == null) throw new Error("docs: a copy button without code");
try {
await navigator.clipboard.writeText(code);
button.textContent = "Copied";
} catch (error) {
console.error("docs: copy failed", error);
button.textContent = "Copy failed";
}
setTimeout(() => (button.textContent = "Copy"), 1600);
}
export function Docs() {
const { page } = useParams({ strict: false });
const navigate = useNavigate();
const at = guides.findIndex((g) => g.slug === (page ?? ""));
const guide = guides[at];
useEffect(() => {
if (guide) document.title = `${guide.title} · Docs · Ely`;
}, [guide]);
if (!guide) return <NotFound what={`No guide at ${page}.`} />;
const [prev, next] = [guides[at - 1], guides[at + 1]];
// Links and copy buttons inside the rendered Markdown.
const press = (e: MouseEvent<HTMLElement>) => {
const target = e.target as HTMLElement;
const button = target.closest<HTMLButtonElement>("button.copy");
if (button) return void copy(button);
const link = target.closest<HTMLAnchorElement>("a[href^='/']");
if (!link || e.metaKey || e.ctrlKey || e.shiftKey || e.button !== 0) return;
e.preventDefault();
navigate({ to: link.getAttribute("href")! });
};
return (
<div className="docs">
<nav className="docs-rail" aria-label="Guides" data-lenis-prevent>
<ol>
{guides.map((g) => (
<li key={g.slug}>
<Link to={to(g)} aria-current={g === guide ? "page" : undefined}>
{g.title}
</Link>
</li>
))}
</ol>
</nav>
<article className="doc">
<h1>{guide.title}</h1>
<div className="prose" onClick={press} dangerouslySetInnerHTML={{ __html: guide.html }} />
<nav className="pager" aria-label="More guides">
{prev && <Link to={to(prev)}>← {prev.title}</Link>}
{next && (
<Link className="next" to={to(next)}>
{next.title} →
</Link>
)}
</nav>
</article>
{guide.toc.length > 0 && (
<nav className="doc-toc" aria-label="On this page">
<ol>
{guide.toc.map((h) => (
<li key={h.id}>
<a href={`#${h.id}`}>{h.text}</a>
</li>
))}
</ol>
</nav>
)}
</div>
);
}