mirror of
https://github.com/jakejarvis/stanza.git
synced 2026-07-16 18:05:58 -04:00
103 lines
4.0 KiB
TypeScript
103 lines
4.0 KiB
TypeScript
import { createFileRoute, notFound } from "@tanstack/react-router";
|
|
import { createServerFn } from "@tanstack/react-start";
|
|
import { renderServerComponent } from "@tanstack/react-start/rsc";
|
|
import { DocsBody } from "fumadocs-ui/layouts/docs/page";
|
|
import { createRelativeLink } from "fumadocs-ui/mdx";
|
|
import { RootProvider } from "fumadocs-ui/provider/tanstack";
|
|
|
|
import { DocsActions } from "@/components/docs/docs-actions";
|
|
import { DocsSidebar } from "@/components/docs/docs-sidebar";
|
|
import { DocsToc } from "@/components/docs/docs-toc";
|
|
import { getMDXComponents } from "@/components/mdx";
|
|
import { buildHead, getTechArticleJsonLd, SITE_URL } from "@/lib/seo";
|
|
import { source } from "@/lib/source";
|
|
import { getDocMeta } from "@/server/docs-meta.functions";
|
|
|
|
// Render the docs layout (sidebar + article + TOC) as one RSC fragment.
|
|
// Fumadocs' `pageTree.name` and `TOCItemType.title` are typed as `ReactNode`,
|
|
// so they can't ride along in JSON loader data — but inside an RSC fragment
|
|
// they flow through React Flight natively. Client components in the tree ship
|
|
// as `'use client'` references and hydrate normally.
|
|
const getDocLayout = createServerFn({ method: "GET" })
|
|
.inputValidator((slugs: string[]) => slugs)
|
|
.handler(async ({ data: slugs }) => {
|
|
const page = source.getPage(slugs);
|
|
if (!page) throw notFound();
|
|
const MDX = page.data.body;
|
|
const markdownPath = `${page.url}.md`;
|
|
const pageUrl = `${SITE_URL.replace(/\/$/, "")}${page.url}`;
|
|
return await renderServerComponent(
|
|
<RootProvider theme={{ enabled: false }} search={{ enabled: false }}>
|
|
<div className="mx-auto max-w-7xl px-4 sm:px-6">
|
|
<div className="md:flex md:gap-8">
|
|
<DocsSidebar tree={source.getPageTree()} />
|
|
<div className="min-w-0 flex-1">
|
|
<div className="xl:flex xl:gap-8">
|
|
<article className="min-w-0 flex-1 pt-2 pb-8 md:pt-8">
|
|
<div className="flex items-start justify-between gap-4">
|
|
<div className="min-w-0 flex-1">
|
|
<h1 className="scroll-mt-16 text-3xl font-medium tracking-tight text-balance">
|
|
{page.data.title}
|
|
</h1>
|
|
{page.data.description && (
|
|
<p className="mt-2 text-base leading-normal text-pretty text-muted-foreground">
|
|
{page.data.description}
|
|
</p>
|
|
)}
|
|
</div>
|
|
<DocsActions
|
|
markdownPath={markdownPath}
|
|
pageUrl={pageUrl}
|
|
className="mt-1 shrink-0"
|
|
/>
|
|
</div>
|
|
<DocsBody className="mt-8">
|
|
<MDX
|
|
components={getMDXComponents({
|
|
a: createRelativeLink(source, page),
|
|
})}
|
|
/>
|
|
</DocsBody>
|
|
</article>
|
|
<DocsToc toc={page.data.toc} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</RootProvider>,
|
|
);
|
|
});
|
|
|
|
export const Route = createFileRoute("/docs/$")({
|
|
component: Page,
|
|
loader: async ({ params }) => {
|
|
const slugs = params["_splat"]?.split("/").filter(Boolean) ?? [];
|
|
const [meta, Content] = await Promise.all([
|
|
getDocMeta({ data: slugs }),
|
|
getDocLayout({ data: slugs }),
|
|
]);
|
|
return { ...meta, Content };
|
|
},
|
|
head: ({ loaderData }) => {
|
|
const path = loaderData?.url ?? "/docs";
|
|
const title = loaderData?.title;
|
|
const description = loaderData?.description;
|
|
return buildHead({
|
|
title,
|
|
description,
|
|
path,
|
|
ogImage: loaderData ? `/og${path}.webp` : undefined,
|
|
markdownPath: loaderData ? `${path}.md` : undefined,
|
|
jsonLd:
|
|
loaderData && title
|
|
? [getTechArticleJsonLd({ title, description: description ?? "", path })]
|
|
: undefined,
|
|
});
|
|
},
|
|
});
|
|
|
|
function Page() {
|
|
const { Content } = Route.useLoaderData();
|
|
return <>{Content}</>;
|
|
}
|