Blog
Git 管理的 Blog、构建期发布索引、shadcn Typeset 与 Shiki 代码高亮。
Blog 是 Manifest 控制的 Git Content 模块。文章源位于 apps/web/content/blog,构建时校验并生成索引;Web 不在运行时读取 Git 文件,也不把 Blog 变成 D1 CMS。
新建文章
文件名必须与 slug 一致:
---
kind: blog
slug: building-clear-boundaries
title: Building clear boundaries
description: A concise description that satisfies the validated content boundary.
status: published
publishedAt: 2026-07-23
author: Tanplate
tags:
- architecture
---
Article body.draft 文章、未来 publishedAt 文章和 Manifest 关闭的内容类型不会进入生产输出。frontmatter、内部链接、允许的 MDX 节点和正文结构在构建前统一校验,失败会阻断构建。
路由与组件
pages/_app/blog/
index.tsx
$slug.tsx
-components/
blog-list.tsx
blog-detail.tsx
blog-content.tsx
blog-code-block.tsx两个路由保持薄适配:列表读取已发布索引,详情按 slug 读取并对未知文章返回 404。页面结构、正文排版和代码高亮全部留在 Blog 私有 -components。
Typeset 正文
blog-content.tsx 使用 React Markdown + GFM,并把正文放入 shadcn Typeset 的 typeset 容器。链接、表格和 fenced code 通过显式组件映射处理:
- 原始 HTML 被跳过。
- 外部链接增加
noreferrer。 - 宽表格进入
typeset-scroll横向滚动容器。 - allowlisted MDX 先经过构建期转换,不执行任意组件或脚本。
这套排版来自 shadcn Typeset 的全局样式,Blog 组件不复制一套平行的 prose 主题。
Shiki 代码高亮
SSR 首屏输出可读的纯文本 <pre><code>,浏览器随后懒加载 shiki/core、JavaScript regex engine、当前语言和 GitHub light/dark 主题。高亮失败或遇到不支持的语言时保留纯文本,不阻断文章。
当前按需支持 CSS、HTML、JavaScript/JSX、JSON、Markdown、Shell、TypeScript/TSX 和 YAML;js、ts、bash 等常用别名会先规范化。主题输出使用 Shiki 双主题 CSS 变量,跟随站点明暗模式。
验证
pnpm --filter web test
pnpm --filter web test:e2e:content
pnpm build:web新增或修改文章后同步 apps/web/content/blog/.folder.md。修改渲染组件时还要同步 pages/_app/blog/-components/.folder.md 与对应文件 Header。