Tinycast 文档网站实现:Next.js 与 Fumadocs 加 llms.md 的完整做法
【免费下载链接】tinycastTinycast — a tiny, fully native macOS launcher, hotkeys, and clipboard history.项目地址: https://gitcode.com/GitHub_Trending/ti/tinycast
Tinycast 是一款小型、全原生的 macOS 启动器(Launcher)、热键与剪贴板历史工具,而它的文档网站是一个值得参考的工程范例:用Next.js 16 + Fumadocs构建静态文档站,并通过llms.txt与llms.md路由让 AI 助手能直接"读懂"每一篇文档,最终零服务器部署到 Cloudflare。本文完整拆解这套文档站的做法,适合想给自己的项目搭建 AI 友好文档站的新手。
技术栈总览:Next.js + Fumadocs 的最小组合
整个网站位于 website/ 目录,核心依赖在 website/package.json 中一目了然:
| 依赖 | 作用 |
|---|---|
next | App Router 框架,负责页面、路由与静态导出 |
fumadocs-core/fumadocs-ui | 文档数据源模型与现成的文档 UI(侧边栏、目录、搜索) |
fumadocs-mdx | MDX 编译器,把content/docs目录变成可查询的页面树 |
tailwindcssv4 | 样式系统 |
wrangler | Cloudflare Pages 部署工具 |
可以看到没有任何 CMS、数据库或数据库驱动的路由——全部内容都是 Markdown 文件,构建时一次性生成 HTML。
文档内容组织:fumadocs-mdx 的三件套
第一步:定义文档目录。website/source.config.ts 只做两件事:
defineDocs({ dir: "content/docs" })声明 Markdown 文档的存放目录;- 通过
defineConfig注入rehypePlugins与代码高亮配置。
这里有两个很实用的细节:
rehype-raw的坑:Fumadocs 会向 AST 注入 MDX 节点,rehype-raw默认拒绝处理它们,必须显式传入passThrough: MDX_NODES白名单,否则快捷键表里的<kbd>标签会被静默吞掉;- 按需引入高亮语法:
rehypeCodeOptions.langs只声明了["bash", "json", "markdown"],避免把用不到的语法包打进构建产物,且高亮在构建期完成、浏览器端零成本。
第二步:管理页面顺序。website/content/docs/meta.json 用数组顺序声明侧边栏导航:index → install → permissions → palette → launcher → features → ai → extensions → reference。
第三步:生成数据源。website/src/lib/source.ts 用loader({ baseUrl: "/docs", source: docs.toFumadocsSource() })把文档目录变成运行时可查询的source对象,侧边栏、sitemap、llms.txt 全部复用它。
llms.md 实现:把原始 Markdown 喂给 AI
这是本仓库最有意思的部分——网站为 AI Agent 提供了两层"机器可读接口":
1.llms.txt:全站文档索引
website/src/app/llms.txt/route.ts 在构建时遍历source.getPages(),把 37 个页面按章节(Launcher、Features、AI、Reference……)分组,生成一份text/plain索引:每一行是"页面标题 + 链接 + 一句话描述",AI 只需一次请求就能了解整站结构。
2.llms.md/docs/[slug]/index.md:单页原始 Markdown
website/src/app/llms.md/docs/[...slug]/route.ts 为每个页面输出一份纯 Markdown 文件(Content-Type: text/markdown),配合 website/src/lib/get-llm-text.ts 剥掉 frontmatter、补上标题与描述,得到 AI 可直接消费的正文。
一个巧妙的工程决策在 website/src/lib/source.ts 的注释里:静态导出中"既是文件又是目录"的路径会冲突(例如/docs/launcher既要当文件夹又要当页面),所以给所有页面的 Markdown 统一命名为叶子文件index.md,从根本上消灭这类冲突。generateStaticParams()则在导出时为每个页面预生成对应的.md文件——线上没有任何运行时请求。
这套机制同时服务于人类的"Copy Markdown"按钮,一份代码、两种读者。
静态导出与部署:无 Node 进程的文档站
website/next.config.mjs 是理解部署形态的关键:
output: "export":纯静态导出,Cloudflare 直接托管文件,背后没有 Node 进程;images: { unoptimized: true }:图片优化 API 需要服务端,导出场景下必须关闭;trailingSlash: true:生成/docs/palette/index.html,这是静态主机唯一能正确服务的形态;reactCompiler: true:启用 React Compiler 自动记忆化。
部署只需一条命令:pnpm deploy(即wrangler deploy),配置见 website/wrangler.jsonc;截图类媒体由 Scripts/upload-website-media.sh 单独上传。
SEO 细节:元数据、sitemap 与结构化数据
website/src/app/layout.tsx 集中处理了分享与搜索引擎元数据:
- 标题模板:
"%s — Tinycast",所有子页自动带上品牌后缀; - Open Graph / Twitter Card:统一使用 1200×630 的
og.png分享卡片; - 结构化数据:内嵌
schema.org/SoftwareApplication的 JSON-LD,声明应用类别、操作系统与免费许可; - 自托管字体:
next/font在构建时下载 Geist 与 Instrument Serif 并生成度量匹配的 fallback,无第三方请求、无布局偏移(layout shift)。
而 website/src/app/sitemap.ts 直接从文档树生成——新增一篇文档,sitemap 自动多一条,无需维护两份清单。文档布局 website/src/app/docs/layout.tsx 则用 Fumadocs 的DocsLayout一行代码获得带侧边栏、主题切换与社交链接的完整文档框架。
快速上手:五步复刻这套文档站
- 初始化:
next@16+fumadocs-mdx+fumadocs-core+fumadocs-ui,文档写入content/docs/; - 在
source.config.ts中defineDocs并加入rehype-raw(记得passThrough白名单); - 用
loader({ baseUrl: "/docs" })生成source,所有动态功能(导航、sitemap、llms.txt)都从它取数; - 添加
llms.txt索引路由 + 每页index.md路由,全部标记force-static; next.config.mjs开启output: "export",wrangler deploy上线。
总结:这套做法好在哪
- 纯静态:构建期生成一切,托管零成本,无服务端运维;
- 一份内容三个出口:人看的 HTML、人复制的 Markdown、AI 读的
llms.md/llms.txt,全部来自同一目录的同一批文件; - 约定优于配置:新增文档后,导航、sitemap、AI 索引全部自动生效;
- 细节克制:按需的代码高亮语言、自托管字体、统一叶子文件名,都是"小决定换大省心"的典型案例。
如果你正在为开源项目搭建文档站,website/ 目录就是一个可直接对读的完整参考实现。
【免费下载链接】tinycastTinycast — a tiny, fully native macOS launcher, hotkeys, and clipboard history.项目地址: https://gitcode.com/GitHub_Trending/ti/tinycast
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考