From 513bb48dc2761163989af9103f7de85665554027 Mon Sep 17 00:00:00 2001 From: bntvllnt Date: Tue, 12 May 2026 23:53:09 +0200 Subject: [PATCH] docs: expand registry documentation pages --- apps/registry/app/docs/[slug]/page.tsx | 154 ++++++++++++++++++ apps/registry/app/docs/page.tsx | 55 +++++-- apps/registry/app/llms-full.txt/route.ts | 25 ++- apps/registry/app/llms.txt/route.ts | 6 + apps/registry/app/sitemap.ts | 15 +- apps/registry/components/header/header.tsx | 23 ++- apps/registry/content/pages/docs.mdx | 15 +- apps/registry/content/pages/docs/agents.mdx | 45 +++++ .../registry/content/pages/docs/changelog.mdx | 21 +++ apps/registry/content/pages/docs/cli.mdx | 60 +++++++ .../content/pages/docs/components.mdx | 45 +++++ .../content/pages/docs/contributing.mdx | 47 ++++++ apps/registry/content/pages/docs/faq.mdx | 51 ++++++ .../content/pages/docs/installation.mdx | 65 ++++++++ apps/registry/content/pages/docs/registry.mdx | 45 +++++ apps/registry/content/pages/docs/theming.mdx | 61 +++++++ apps/registry/lib/docs-pages.ts | 70 ++++++++ apps/registry/lib/jsonld.ts | 44 ++++- apps/registry/lib/sidebar-sections.ts | 13 ++ 19 files changed, 826 insertions(+), 34 deletions(-) create mode 100644 apps/registry/app/docs/[slug]/page.tsx create mode 100644 apps/registry/content/pages/docs/agents.mdx create mode 100644 apps/registry/content/pages/docs/changelog.mdx create mode 100644 apps/registry/content/pages/docs/cli.mdx create mode 100644 apps/registry/content/pages/docs/components.mdx create mode 100644 apps/registry/content/pages/docs/contributing.mdx create mode 100644 apps/registry/content/pages/docs/faq.mdx create mode 100644 apps/registry/content/pages/docs/installation.mdx create mode 100644 apps/registry/content/pages/docs/registry.mdx create mode 100644 apps/registry/content/pages/docs/theming.mdx create mode 100644 apps/registry/lib/docs-pages.ts diff --git a/apps/registry/app/docs/[slug]/page.tsx b/apps/registry/app/docs/[slug]/page.tsx new file mode 100644 index 00000000..51cfb66a --- /dev/null +++ b/apps/registry/app/docs/[slug]/page.tsx @@ -0,0 +1,154 @@ +import { readFile } from "node:fs/promises"; +import path from "node:path"; + +import { Breadcrumb, MDXContent, Sidebar } from "@vllnt/ui"; +import type { Metadata } from "next"; +import { notFound } from "next/navigation"; +import Script from "next/script"; + +import { getPageContent } from "@/lib/content"; +import { DOCS_PAGES, getDocsPage, getDocsPath } from "@/lib/docs-pages"; +import { + breadcrumbLd, + jsonLdScriptAttributes, + techArticleLd, +} from "@/lib/jsonld"; +import { generateOGMetadata, generateTwitterMetadata } from "@/lib/og"; +import { canonical } from "@/lib/seo"; +import { getSidebarSections } from "@/lib/sidebar-sections"; + +const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL ?? "https://ui.vllnt.ai"; + +type Props = { + params: Promise<{ slug: string }>; +}; + +export function generateStaticParams(): { slug: string }[] { + return DOCS_PAGES.map((page) => ({ slug: page.slug })); +} + +const ROOT_CHANGELOG_PATH = path.join(process.cwd(), "..", "..", "CHANGELOG.md"); +const PACKAGE_CHANGELOG_PATH = path.join( + process.cwd(), + "..", + "..", + "packages", + "ui", + "CHANGELOG.md", +); + +async function readChangelogFile(filePath: string): Promise { + try { + return (await readFile(filePath, "utf8")).trim(); + } catch { + return ""; + } +} + +async function readChangelog(): Promise { + const [rootChangelog, packageChangelog] = await Promise.all([ + readChangelogFile(ROOT_CHANGELOG_PATH), + readChangelogFile(PACKAGE_CHANGELOG_PATH), + ]); + + return [ + rootChangelog ? `## Repository changelog\n\n${rootChangelog}` : "", + packageChangelog ? `## Package changelog\n\n${packageChangelog}` : "", + ] + .filter(Boolean) + .join("\n\n"); +} + +export async function generateMetadata(props: Props): Promise { + const { slug } = await props.params; + const docsPage = getDocsPage(slug); + + if (!docsPage) { + return { + title: "Documentation", + }; + } + + const { frontmatter } = await getPageContent(`docs/${docsPage.slug}`); + const og = frontmatter.og; + const href = getDocsPath(docsPage); + + return { + alternates: { canonical: canonical(href) }, + description: frontmatter.description, + openGraph: generateOGMetadata({ + description: og?.description ?? frontmatter.description, + title: og?.title ?? frontmatter.title, + type: og?.type ?? frontmatter.type, + }), + title: frontmatter.title, + twitter: generateTwitterMetadata({ + description: og?.description ?? frontmatter.description, + title: og?.title ?? frontmatter.title, + type: og?.type ?? frontmatter.type, + }), + }; +} + +export default async function DocsSlugPage(props: Props) { + const { slug } = await props.params; + const docsPage = getDocsPage(slug); + + if (!docsPage) { + notFound(); + } + + const { content, frontmatter } = await getPageContent(`docs/${docsPage.slug}`); + const pageContent = + docsPage.slug === "changelog" + ? `${content}\n\n${await readChangelog()}` + : content; + const pageUrl = `${SITE_URL}${getDocsPath(docsPage)}`; + + return ( + <> +