================ Source: https://oink.pgsty.com/zh/docs/index.md ================ # OINK 文档 > OINK 是一套本地优先的 Hugo 文档框架:组件在 Markdown 中仍然可读,资源随主题分发,多语言开箱可用,一份内容同时服务读者与 Agent。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- OINK 是一款技术文档 Hugo 主题。组件是 Markdown 语法的一部分,不是另一套模板语言;浏览器需要的字体、图标、搜索与图表运行时随主题分发;构建依赖只有一个 Hugo Extended 二进制,不需要 Node.js,不请求 CDN。当前发布版本 v1.0.0。 ## 五条入口 {#five-entries} - [快速上手](/zh/docs/start/) — 创建 OINK Starter 仓库,建立本地基线,分层定制并部署。 - [组件总览](/zh/docs/components/) — 每个组件一页,先给源码再给渲染效果。 - [使用 OINK 创作优美的内容](/zh/book/) — 从第一次预览到持续维护发布物的实战教程。 - [案例](/zh/case/) — 把生产站点拆解成可复用的设计与迁移模式。 - [设计与开发](/zh/docs/design/) — 面向 OINK 维护者的契约、已接受决策、研究证据与候选提案。 {.cards} ## 按任务导航 {#where-to-go} | 你要做的事 | 去哪 | | ---------------------------- | -------------------------------------- | | 判断是否适用 | [OINK 是什么](/zh/docs/about/) | | 安装并预览 | [快速上手](/zh/docs/start/) | | 写一页文档 | [编写页面](/zh/docs/write/pages/) | | 把目录树变成侧栏 | [组织内容](/zh/docs/write/organize/) | | 查组件写法 | [组件总览](/zh/docs/components/) | | 改站名、Logo、配色与字体 | [品牌外观](/zh/docs/customize/brand/) | | 查某个配置键的默认值 | [配置总览](/zh/docs/customize/config/) | | 做双语或多语言站 | [多语言](/zh/docs/customize/i18n/) | | 从头到尾掌握 OINK | [使用 OINK 创作优美的内容](/zh/book/) | | 研究生产环境实现 | [案例](/zh/case/) | | 部署到线上 | [发布上线](/zh/docs/admin/deploy/) | | 升级版本或从 Docsy 迁移 | [版本升级](/zh/docs/admin/upgrade/) | | 维护主题、审查契约或编写 PRD | [设计与开发](/zh/docs/design/) | Docs 的七个栏目按阅读顺序排列:了解、上手、写内容、查组件、改站点、管发布,最后理解并维护其背后的契约与设计记录。 --- 本节页面: - [OINK 是什么](/zh/docs/about/): 一套从 Docsy 演化而来的本地优先 Hugo 文档框架:组件在 Markdown 中仍然可读,资源随主题分发,十五个生产站点持续验证它。 - [快速上手](/zh/docs/start/): 从官方 OINK Starter 建立可运行的本地基线,再依次定制内容、语言、品牌、集成与部署。 - [创作内容](/zh/docs/write/): 写文档页、博客、书籍、发布页与 API 文档:一页文档长什么样,内容怎么组织。 - [组件总览](/zh/docs/components/): 写文档时可用的全部组件,一个组件一页,例子由浅入深,参数表在页尾。 - [定制站点](/zh/docs/customize/): 站点级配置:品牌、导航、布局、搜索、多语言、多版本、打印与 Agent 输出。 - [维护管理](/zh/docs/admin/): 站点从本机到线上的运维事项:本地预览、发布上线、评论、分析与 SEO、版本升级与排错。 - [设计与开发](/zh/docs/design/): 在唯一的双语专栏中管理 OINK 维护者契约、已接受决策、定期研究与候选提案。 ================ Source: https://oink.pgsty.com/zh/docs/about/index.md ================ # OINK 是什么 > 一套从 Docsy 演化而来的本地优先 Hugo 文档框架:组件在 Markdown 中仍然可读,资源随主题分发,十五个生产站点持续验证它。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- OINK 是一款独立的 [Hugo](https://gohugo.io/) 主题,用于搭建中大型技术文档站。它从 [Docsy](https://github.com/google/docsy) 演化而来:保留 Docsy 的内容模型与多语言行为,替换外壳、导航、搜索与内容组件。 消费站点的构建依赖只有一个 Hugo Extended 二进制,不需要 Node.js、npm 或 PostCSS,也不请求 CDN。Bootstrap、Font Awesome、字体、本地搜索、图表与 API 文档运行时都提交在主题仓库里,只在页面用到时下发。 组件不是另一套模板语言:`> [!NOTE]` 是提示块,表格加一行 `{.fields}` 是参数表,图片下面加 `{caption=}` 就有图注。当前有[十五个生产站点](/zh/docs/about/showcase/)在用它,本站是其中之一。 ![OINK 把 Markdown 内容、配置与本地资源汇成一个静态文档站](/images/hero-light.webp) {width="900" height="600" caption="一次 Hugo 构建,产出可直接托管的静态站点"} ## 主题的职责 {#what-oink-provides} - 文档与博客外壳:导航、侧栏树、目录、面包屑、翻页、深色模式、打印视图与无障碍交互。 - 多语言框架:译文路由、缺译回退、语言权重、RTL,以及 32 个界面语言包。 - 本地运行时:Mermaid、KaTeX、Markmap、Swagger UI、Redoc、Asciinema、ECharts、Infographic 与本地全文检索。 - 内容组件:提示块、标签页、步骤、卡片、参数表、文件树、画廊、徽章、按键等,多数有 Markdown 原生形态。 - 内容类型:普通文档之外,还内置书籍编号与交叉引用、发布与下载页、数据驱动的 Landing 首页、OpenAPI 文档页。 主题不负责源码托管与部署:站点可以放在 GitHub、GitLab 或私有 Git 上,Hugo 生成的静态文件可用任何托管平台发布。站点自己的内容、品牌与业务组件仍归站点管理,主题只提供通用外壳与可复用组件。 ## 适用范围 {#is-oink-for-me} | 这些情况适合 | 这些情况不适合 | | --- | --- | | 页面多、内容类型杂:文档、博客、书、发布页与 API 参考共处一个站点 | 只有一两页内容、不需要结构化导航;README 或更轻的 Hugo 主题更简单 | | 需要完整的多语言,而不是给英文站挂一个翻译入口 | 站点主体是应用界面而不是文档:可以用 OINK 承载文档部分,业务组件留在站点层 | | 对可复现构建与网络隔离有要求,构建机不能出网 | 需要在正文里写交互组件(React / MDX) | | 多个站点共享同一套外壳,不必复制布局与 shortcode | 想用一个开关换成另一套视觉:主题没有品牌开关,改外观要走 CSS token 与 partial 覆盖 | | 团队没有前端,也不维护 Node 工具链 | 需要主题内置内容管理后台或所见即所得编辑器 | ## 与其它文档方案的差别 {#comparison} 下表只列结构性差别,且只写能从各项目自身文档与仓库确认的部分。各项目的版本会变动,选型前以其当前文档为准。 | 维度 | OINK | Docsy | Hextra | Docusaurus | | --- | --- | --- | --- | --- | | 构建工具 | Hugo Extended,单个二进制 | Hugo Extended + Node/npm | Hugo | Node.js 工具链 | | 消费站点要不要 npm | 不要 | 要:Bootstrap 与 Font Awesome 从 `node_modules/` 挂载 | 不要 | 要 | | 前端资源从哪来 | 全部提交在主题仓库,`VENDOR.json` 记录版本、来源、许可与校验值 | 每页无条件加载 CDN 上的 jQuery;Mermaid、KaTeX 等还会在构建期请求 CDN | 预编译产物提交在仓库 | npm 依赖 | | 组件写法 | Markdown 原生属性与围栏为主,29 个 shortcode 兜底 | shortcode(19 个) | shortcode(29 个)为主,提示块有 `> [!NOTE]` 原生形态 | MDX(React 组件) | | 多语言 | Hugo 多语言 + 32 个界面语言包 | Hugo 多语言(OINK 的语言包由此继承) | Hugo 多语言 + 21 个界面语言包 | 内置 i18n 框架 | | 书籍编号与交叉引用 / 发布下载页 / 数据驱动落地页 | 主题内置 | 无 | 无 | 需自建或找插件 | 两点补充。每页 Markdown 输出与 `llms.txt` 不是 OINK 独有的能力,Docsy 与 Hextra 也有,三者都要站点在 `outputs` 里显式打开。表格最后一行的三项只有 OINK 内置,它们来自 PGSTY 自己的生产站点,不是通用文档站的必需品。主题的交互功能默认关闭,搜索、缩放、评论与反馈都要站点显式打开。 OINK 不是叠在 Docsy 上的皮肤,而是 fork 之后独立演化的主题。Docsy 的源码历史、Apache-2.0 义务与署名完整保留,细节见[开源许可与致谢](/zh/docs/about/license/)。 ## 入口 {#start-here} - [快速上手](/zh/docs/start/) — 使用官方 Starter,分层定制,再发布上线。 - [组件总览](/zh/docs/components/) — 一个组件一页,先源码后效果。 - [示例站点](/zh/docs/about/showcase/) — 十五个生产站点,各自用了 OINK 的哪部分。 {.cards} [亮点特性](/zh/docs/about/features/)按能力逐条列出主题提供的东西,每条链接到讲它的指南页。 --- 本节页面: - [亮点特性](/zh/docs/about/features/): 逐条列出 OINK 与普通 Hugo 主题的差别,每条链接到讲它的指南页。 - [Case 导览](/zh/docs/about/showcase/): 按文档、书籍、落地页与交互工具的形态,找到最接近自己需求的 OINK 生产案例。 - [开源许可与致谢](/zh/docs/about/license/): 查清哪一层适用哪份许可证:主题 Apache-2.0、文档 CC BY 4.0、随主题分发的第三方运行时各自保留原许可。 --- 反链: - [OINK 实现预览](/zh/blog/oink/oink-announcement/) - [文档](/zh/docs/) - [亮点特性](/zh/docs/about/features/) - [开源许可与致谢](/zh/docs/about/license/) ================ Source: https://oink.pgsty.com/zh/docs/about/features/index.md ================ # 亮点特性 > 逐条列出 OINK 与普通 Hugo 主题的差别,每条链接到讲它的指南页。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- 本页逐条列出 OINK 与普通 Hugo 主题的差别,每条末尾给出讲它的指南页。要立即安装,见[十分钟上手](/zh/docs/start/)。 ## 组件写在 Markdown 里 {#native-components} 提示块是 `> [!NOTE]` 块引用(十种语义类型加一个中性折叠块),参数表是表格加一行 `{.fields}`,步骤与卡片是列表加 `{.steps}` / `{.cards}`,图注是图片下面一行 `{caption="…"}`。标签页是几个相邻围栏各带一个 `{tab="…"}`;文件树、画廊、Mermaid、ECharts 是以语言命名的数据围栏。这些写法在 GitHub 或普通 Markdown 阅读器中退化为块引用、表格、列表与代码块,内容不丢失。 29 个 shortcode 覆盖原生形态表达不了的场景:卡片带图标与图片、参数表条目正文是多段 Markdown。 → [组件总览](/zh/docs/components/) ## 只要一个 Hugo 二进制 {#hugo-only} 消费站点的全部构建依赖是 Hugo Extended 0.160.1 或更新版本。SCSS 由 Hugo 内置的 Sass 转译器编译,主题不调用 `postCSS`;没有 npm、没有 webpack、没有构建期下载。用 Hugo Module 方式安装主题时需要本机有 Go 来解析模块,用离线归档或 submodule 则不需要。 「仅依赖 Hugo」指的是构建依赖。界面交互仍在浏览器中执行 JavaScript:搜索、命令面板、图表、标签页都是页面脚本,区别在于这些脚本随主题分发、按页面用到的功能下发。 → [十分钟上手](/zh/docs/start/) ## 本地优先 {#local-first} 浏览器需要的资源全部提交在主题仓库里:Bootstrap、Font Awesome、四款字体、Lunr、Mermaid、KaTeX、Markmap、Swagger UI、Redoc、Asciinema、ECharts、Infographic。`VENDOR.json` 逐项记录 26 个依赖的版本、来源、许可证文件与 SHA-256 校验值,更新某个运行时要同时更新产物、许可证与校验值。 对可能引起网络请求的功能,主题让它保持关闭而不是静默连出去:PlantUML 缺 `params.plantuml.svg_image_url`、Diagrams.net 缺 `params.drawio.drawio_server`、Algolia 缺 `appId` / `apiKey` / `indexName`,都会告警并保持禁用;带 `--panicOnWarning` 的发布关卡会把这条告警变成失败。 本地优先不覆盖作者自己添加的内容。以下都是显式的网络选择:外部链接、远程图片与视频、iframe、远程 API 规范;Algolia、Google 自定义搜索这类托管搜索;分析、评论与其它 SaaS 集成;作者主动配置远程渲染器的 PlantUML 与 Diagrams.net。用到它们的页面仍然是有效页面,但站点不应再宣称这些页面可以完全离线使用。 → [开源许可与致谢](/zh/docs/about/license/) · [配置总览](/zh/docs/customize/config/) ## 一份内容,四种输出 {#four-outputs} 每个组件在四种输出下都有确定的形态:交互式 HTML;去掉缩放与复制控件、折叠块完全展开的打印页;纯 Markdown;RSS。打印视图按栏目整份生成(本栏目是 `/zh/_print/docs/about/`),Markdown 版本是同一页面地址加 `index.md`。 站点在 `outputs` 里显式选择需要哪几种,主题不替站点决定。 → [打印支持](/zh/docs/customize/print/) · [Agent 支持](/zh/docs/customize/agents/) ## 双语与 32 个界面语言 {#multilingual} 多语言走 Hugo 原生机制:译文路由、按权重排序的语言选择器、缺译回退、RTL、以及 canonical 与 alternate 元数据。界面文案有 32 个语言包,共用同一套 key;英语、简体中文(`zh-cn` 与通用 `zh`)与繁体中文(`zh-tw`)经过人工审校,其余语言保留 Docsy 继承下来的翻译,OINK 新增的键先用英文兜底。 → [多语言](/zh/docs/customize/i18n/) ## 全文检索不出站 {#search} 打开 `params.offline_search` 后,Hugo 为每种语言生成一份索引,浏览器用本地 Lunr 检索拉丁文字、用子串回退检索中日韩文本,查询内容不发给任何第三方。页面可以用 `search_boost` 调权重、用 `search_keywords` 补同义词。 → [全文检索](/zh/docs/customize/search/) ## 命令面板 {#command-palette} `Cmd/Ctrl + K` 打开命令面板;裸按 `/` 进入搜索态,裸按 `\` 进入纯命令态。面板里同时有页面、命令与页面动作(切换语言、切换主题、复制 Markdown 等),搜索与操作共用一个入口。 → [命令面板](/zh/docs/customize/panel/) ## 键盘导航 {#keyboard} 默认开启,可按站点或按栏目关闭。`w` `s` 在侧栏树上下移动,`a` `d` 折叠展开,`q` `e` 上一篇下一篇,`j` `k` 沿页面目录跳转,`t` 切换深浅色,`l` 切换语言,`h` 隐藏阅读外壳。输入框、文本域获得焦点或输入法处于组字状态时,单键快捷键全部让行。页脚最底层栏的问号按钮打开速查卡。 → [键盘导航](/zh/docs/customize/keyboard/) ## 反向链接 {#backlinks} 打开 `params.ui.backlinks` 后,每一页都会列出有哪些页面链接到它——构建时从你本来就写的普通链接派生,没有新语法,也没有 JavaScript。本站全站开启:看本页右栏的「反链」组,越常被引用的页面列表越长,超过八条会折叠。 → [反向链接](/zh/docs/customize/navigation/#backlinks) ## 文档之外的四种内容 {#content-types} 主题还内置四类需要额外结构的页面: - 书籍:章节编号,图 / 表 / 式 / 例用 `{#id num=}` 编号、用 `xref` 交叉引用,`book-toc`、`book-figures` 一类 shortcode 生成索引,整本可打印。 - 发布与下载页:`data/download/*.yaml` 生成发布卡片、资产表与校验和,发布状态可控。 - Landing 首页:`data/home/.yaml` 拼装首页分区;任意页面加 `layout: landing` 也能用 `data/landing/` 的数据。 - API 文档:Swagger UI 与 Redoc 都是本地运行时,spec 放站内即可。 → [书籍出版](/zh/docs/write/book/) · [发布与下载页](/zh/docs/write/releases/) · [首页与落地页](/zh/docs/customize/home/) · [API 文档](/zh/docs/write/openapi/) ## 面向 AI 助手的输出 {#agent-output} `outputs` 里加上 `markdown`,每个页面就多一份 `.md`,HTML 的 `` 里带 `rel="alternate"` 指过去,页面动作里也多出「复制 Markdown」与「查看源码」。`LLMS` 输出格式在站点根目录生成 `llms.txt` 内容清单(本站是 )。 0.8.0 再加两种:栏目开启 `LLMSFULL` 后整个栏目拼成一份 `llms-full.txt`,agent 一次抓完;站点开启 `NAVJSON` 后每种语言发布一份 `navigation.json`,侧栏那棵树直接当数据读。两者都在本站开着: 就是真实产物。 「在 ChatGPT / Claude 中打开」默认关闭:读者点击时会把当前 URL 交给第三方,需要站点显式打开 `params.ui.page_context_menu.assistant_links`。 → [Agent 支持](/zh/docs/customize/agents/) ## 多版本 {#versions} 配置 `params.versions` 后顶栏出现版本菜单,旧版本站点顶部显示归档横幅,提示读者查看最新版本;菜单是否逐页跳转由站点决定。多个版本是分别构建、分别部署的静态站点,不需要运行时支持。 → [多版本](/zh/docs/customize/versions/) ## 自己验证 {#verify} 本站启用了上面多数特性,四条自查: 1. 在任意页面按 `Cmd/Ctrl + K`,输入 `postgres` 查看本地搜索结果;按 `\` 进入纯命令态。 2. 在当前页面地址后加 `index.md`,得到这一页的 Markdown 版本。 3. 打开 ,那是给 AI 助手的站点清单;顺着它能找到整个文档栏目的 `llms-full.txt` 与 `navigation.json`。 4. 看本页右栏的「反链」组,它列出链接到本页的页面。 ## 相关 {#related} - [OINK 是什么](/zh/docs/about/) — 定位、适用场景与对比 - [示例站点](/zh/docs/about/showcase/) — 这些特性在生产站点里怎么用 - [十分钟上手](/zh/docs/start/) — 从克隆到上线 - [配置总览](/zh/docs/customize/config/) — 上面提到的参数在哪查 --- 反链: - [简介](/zh/docs/about/) - [开源许可与致谢](/zh/docs/about/license/) ================ Source: https://oink.pgsty.com/zh/docs/about/showcase/index.md ================ # Case 导览 > 按文档、书籍、落地页与交互工具的形态,找到最接近自己需求的 OINK 生产案例。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- 正式的 [Case 案例库](/zh/case/) 把十五个生产站点整理成可复用的实现模式, 首页展示的也是同样这十五个。它们全都使用 OINK,本站本身也作为自举案例列入。 当你已经知道自己要搭建哪类站点时,可以从这里开始:先通过案例了解架构与 取舍,再沿页面链接进入具体配置文档。案例中的数量描述对应盘点时的快照, 不是对持续变化的线上站点作永久承诺。 ## 发行版文档 {#pigsty-sites} ### [pigsty.io](/zh/case/pigsty-io/) {#pigsty-io} 大型英文站,把发行版手册、博客、扩展目录、分类、版本导航与价格落地页放在 同一个站点中。 ### [pigsty.cc](/zh/case/pigsty-cc/) {#pigsty-cc} 独立部署的中文对等站;当两种语言的语料都已成为完整产品时,拆成两个单语站 是一种清晰的取舍。 ### [pgsty.pro](/zh/case/pgsty-pro/) {#pgsty-pro} 双语版本档案站,从可复用的结构化发布数据渲染大量版本页面。 ## 产品文档 {#product-sites} ### [PIG](/zh/case/pig/) {#pig-pgsty-com} 紧凑的双语命令行工具手册,配有数据驱动首页与体量更大的博客。 ### [SOW](/zh/case/sow/) {#sow-pgsty-com} 双语运维手册,使用独立下载内容类型展示发布元数据与产物。 ### [SILO](/zh/case/silo/) {#silo-pgsty-com} 大型上游迁移案例,通过受检查的清单生成双语文档导航。 ### [PG Exporter](/zh/case/pg-exporter/) {#exp-pgsty-com} 把生成导航、结构化指标目录与系统字体组合起来的指标手册。 ## 书籍 {#book-sites} ### [《设计数据密集型应用》](/zh/case/ddia/) {#ddia-vonng-com} 多语言、多版本书籍,也是编号图表、交叉引用、章节导航与索引最完整的案例。 ### [《The Product-Minded Engineer》](/zh/case/tpme/) {#tpme-vonng-com} 只需要 OINK Book 外壳的聚焦型双语出版物。 ### [《PG 技术内幕》](/zh/case/pg-internal/) {#pgint-vonng-com} 已完稿的中文译本,刻意做成单语 Book:没有文档树,也没有可切换的第二语言。 ## 汇编、落地页与自定义站点 {#other-sites} ### [pgsql.cc](/zh/case/pgsql-cc/) {#pgsql-cc} 聚合型运维文库,让多个上游手册与完成度不一的翻译树共享搜索和视觉体系。 ### [pgsty.com](/zh/case/pgsty-com/) {#pgsty-com} 小型双语公司站,展示 OINK 也可以主要作为数据驱动的落地页系统。 ### [Capslock](/zh/case/capslock/) {#caps-vonng-com} 每种语言只有两页,其中自定义外壳承载数据驱动交互配置生成器。 ### [oink.pgsty.com](/zh/case/oink/) {#oink-pgsty-com} 完整参考站:公开文档、实时组件示例、设计契约、多种内容外壳与回归覆盖都在 同一个仓库中。 ### [pgext.cloud](/zh/case/pgext-cloud/) {#pgext-cloud} PostgreSQL 扩展目录:把可检索的数据集作为站点主体呈现,收录 2,241 个扩展、 576 个已打包版本,覆盖 16 个 Linux 平台。 ## 如何选择起点 {#choosing-a-starting-point} - 常规产品手册:从 [PIG](/zh/case/pig/) 或 [SOW](/zh/case/sow/) 开始。 - 大型迁移:对比 [SILO](/zh/case/silo/) 与 [pgsql.cc](/zh/case/pgsql-cc/)。 - 书籍:对比精简的 [TPME](/zh/case/tpme/) 与更复杂的 [DDIA](/zh/case/ddia/), 单语场景可参考 [《PG 技术内幕》](/zh/case/pg-internal/)。 - 落地页或交互站:参考 [pgsty.com](/zh/case/pgsty-com/) 或 [Capslock](/zh/case/capslock/)。 - 最完整的参考实现:使用 [OINK Docs](/zh/case/oink/)。 - 如果读者是来查询数据集而不是来阅读的,看看 [ext.pgsty.com](/zh/case/pgext-cloud/) 如何把数据集作为站点主体呈现。 主题仓库的 `tests/site/` 是内部 CI 夹具,而不是起步模板;其中页面的职责是 触发渲染行为。上面的生产案例更适合作为架构与设计参考。 → [浏览全部案例](/zh/case/) · [十分钟上手](/zh/docs/start/) · [仓库导览](/zh/docs/start/anatomy/) --- 反链: - [简介](/zh/docs/about/) - [亮点特性](/zh/docs/about/features/) ================ Source: https://oink.pgsty.com/zh/docs/about/license/index.md ================ # 开源许可与致谢 > 查清哪一层适用哪份许可证:主题 Apache-2.0、文档 CC BY 4.0、随主题分发的第三方运行时各自保留原许可。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- OINK 由三层材料组成:主题源码、文档内容、随主题分发的第三方资源。三者各自的许可证不会被重新授权成一份统一作品。下面每张表都指向仓库里的权威文件,**摘要与许可证原文不一致时以文件为准**。 ## 许可证对应关系 {#license-map} | 范围 | 许可证 | 权威文件 | | --- | --- | --- | | OINK 主题源码(布局、partial、 shortcode、SCSS、JS、i18n) | Apache License 2.0 | 主题 [`LICENSE`](https://github.com/pgsty/oink/blob/main/LICENSE)、[`NOTICE`](https://github.com/pgsty/oink/blob/main/NOTICE) | | 本站的站点代码、构建脚本与源自 Docsy 的材料 | Apache License 2.0 | 站点 [`LICENSE`](https://github.com/pgsty/oink.pgsty.com/blob/main/LICENSE)、[`NOTICE`](https://github.com/pgsty/oink.pgsty.com/blob/main/NOTICE) | | 本站的原创文档内容(另有声明的除外) | Creative Commons Attribution 4.0 International | 站点 [`LICENSE-CC-BY-4.0`](https://github.com/pgsty/oink.pgsty.com/blob/main/LICENSE-CC-BY-4.0) | | 随主题分发的浏览器库、字体与图标 | 各组件自己的许可证 | 主题 [`VENDOR.json`](https://github.com/pgsty/oink/blob/main/VENDOR.json) 与资源旁的许可证文件 | 两条边界要分清:CC BY 4.0 只覆盖原创文档内容,不覆盖主题代码、商标、截图与第三方资源;主题采用 Apache-2.0,也不会把随附依赖变成 Apache 许可的作品。 ## 上游:Docsy {#upstream-docsy} 主题 `NOTICE` 记录的事实: - OINK 派生自 [Docsy](https://github.com/google/docsy),Copyright 2018 Google LLC and Docsy contributors。 - OINK 自身的主题工作 Copyright 2026 PGSTY contributors。 - 项目与上游同为 Apache License 2.0;第三方浏览器依赖的许可、来源、版本与校验值记录在 `VENDOR.json`,各自要求的 NOTICE 文件与对应资源放在一起分发。 - Docsy 名称与 Google 商标归各自权利人所有,此处引用只用于标识上游项目,**不表示背书**。 本站也派生自 Docsy 项目网站,这段渊源记录在站点自己的 `NOTICE` 里。Docsy 是 OINK 唯一的代码上游:源码历史、Apache-2.0 义务与版权声明完整保留,按 Apache-2.0 的要求,修改过的文件需要标注。 ## 随主题分发的第三方运行时 {#vendored-runtimes} 主题把浏览器要用的资源全部提交在仓库里(`assets/third_party/`、`assets/js/third_party/`、`static/webfonts/`),消费站点不需要 npm,也不会在构建期下载任何东西。`VENDOR.json` 是这批资源的机器可读清单,逐项记录名称、固定版本、来源 URL、许可证文件路径,以及每个选取产物的 SHA-256;清单里还有三棵资源目录的整体校验值。 下表是清单快照(`VENDOR.json` 生成于 2026-08-17,schema 1,共 26 项)。版本会随主题发布变动,**以仓库里的 `VENDOR.json` 为准**。全部来源都是 npm registry(`https://registry.npmjs.org/…`)。 | 项目 | 版本 | 许可证 | 在主题里做什么 | | --- | --- | --- | --- | | bootstrap | 5.3.8 | MIT | 栅格、组件与 RTL 样式基础 | | @popperjs/core | 2.11.8 | MIT | Bootstrap 的浮层定位 | | @fortawesome/fontawesome-free | 7.3.1 | CC-BY-4.0 AND OFL-1.1 AND MIT | 全站图标 | | @fontsource-variable/inter | 5.3.0 | OFL-1.1 | 界面与正文字体 | | @fontsource/chakra-petch | 5.3.0 | OFL-1.1 | 品牌展示字体 | | @fontsource/ibm-plex-mono | 5.3.0 | OFL-1.1 | 代码字体 | | lunr | 2.3.9 | MIT | 本地全文检索 | | @docsearch/js | 5.0.1 | MIT | 可选的 Algolia DocSearch 前端 | | @docsearch/css | 5.0.1 | MIT | 同上的样式 | | mermaid | 11.16.1 | MIT | Mermaid 图表 | | katex | 0.18.4 | MIT | 数学公式 | | markmap-autoloader | 0.18.12 | MIT | 思维导图 | | markmap-lib | 0.18.12 | MIT | 思维导图 | | markmap-view | 0.18.12 | MIT | 思维导图 | | markmap-toolbar | 0.18.12 | MIT | 思维导图工具条 | | d3 | 7.9.0 | ISC | Markmap 依赖 | | @highlightjs/cdn-assets | 11.12.0 | BSD-3-Clause | Markmap 依赖 | | webfontloader | 1.6.28 | Apache-2.0 | Markmap 依赖 | | swagger-ui-dist | 5.32.13 | Apache-2.0 | OpenAPI 文档页 | | redoc | 2.5.3 | MIT | OpenAPI 文档页 | | asciinema-player | 3.17.0 | Apache-2.0 | 终端录像回放 | | echarts | 6.1.0 | Apache-2.0 | 图表 | | @antv/infographic | 0.2.19 | MIT | 信息图 | | pako | 3.0.1 | MIT AND Zlib | 解压(图表数据) | | external-svg-loader | 1.7.1 | MIT | 内联外部 SVG | | idb-keyval | 6.2.0 | Apache-2.0 | 浏览器端缓存 | 许可证原文与各资源放在一起:例如 `assets/third_party/bootstrap/LICENSE`、`assets/third_party/katex/LICENSE`;Swagger UI、Redoc 与 ECharts 还随包带了各自的 `NOTICE` 或打包声明文件。Lunr 是唯一的例外,代码在 `assets/js/third_party/`,许可证在 `assets/third_party/lunr/LICENSE`。 再分发主题时,这些许可与声明材料必须一并保留。更新某个运行时意味着在同一次变更里同时更新产物、许可证文件、来源与校验值。 ## 字体与图标 {#fonts-and-icons} 三款字体(Inter、Chakra Petch、IBM Plex Mono)都采用 SIL Open Font License 1.1,字体文件提交在 `static/webfonts/`:Inter 十四个子集文件、品牌字体四个,加上 Font Awesome 的三个,共二十一个。Font Awesome Free 7.3.1 是复合许可:图标图形 CC BY 4.0、字体文件 SIL OFL 1.1、代码 MIT,原文在 `assets/third_party/Font-Awesome/LICENSE.txt`。 主题不向远程字体服务发请求:仓库里没有 Google Fonts 之类的外链,字体一律由站点自身 `baseURL` 下发。更换字体或改用系统字体栈见[品牌外观](/zh/docs/customize/brand/)。 ## 设计参考 {#design-references} 代码上游只有 Docsy 一个。下面这些项目是设计语言上的参考,既不是代码来源也不是运行时依赖,OINK 没有移植它们的代码: | 项目 | 借鉴之处 | | --- | --- | | [Fumadocs](https://www.fumadocs.dev/) | 以内容为中心的呈现、信息层级、文件树与参数表一类的写作组件(主题 `NOTICE` 记录了这条致敬) | | [Nextra](https://nextra.site/) | 精炼的文档外壳、代码块的文件名与复制交互、按页布局开关 | | [Hextra](https://imfing.github.io/hextra/) | Hugo 原生的实现取向、文件树、徽章、标签页 | | [Mintlify](https://mintlify.com/) | 结构化导航分层、同步的代码分组、API 参考的阅读体验 | [Hugo](https://gohugo.io/) 是构建平台,Go 在 Hugo Module 安装方式下负责解析模块。两者都是前提条件,主题不重新分发它们的可执行文件。 引用这些名字用于说明传承、依赖或灵感来源,**不表示相关项目为 OINK 背书**;各项目与产品名称归其权利人所有。 ## 复用这份文档 {#reusing-the-docs} CC BY 4.0 允许任何目的的分享与演绎,条件是给出署名、提供许可证链接、说明是否做过修改,并且不得暗示 OINK、PGSTY 或上游项目为改编内容背书。一段合格的署名可以是: > 本文改编自 PGSTY 贡献者编写的 OINK 文档,采用 [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) 许可,并做了修改。 页面里单独署名的图片或引文,要保留它们各自的署名与许可;删掉页脚不会免除署名义务。 ## 复用这个主题 {#reusing-the-theme} Apache-2.0 允许按条款使用、修改与分发主题源码及编译产物,条件是保留许可证、版权与归属声明,保留 `NOTICE` 内容,并在分发修改后的源码时标明改过哪些文件。主题发行包应当包含 `LICENSE`、`NOTICE`、`VENDOR.json`,以及清单引用的全部第三方许可证文件。 Apache-2.0 不授予商标使用权,也不会把第三方资源变成 Apache 许可的作品。 ## 相关 {#related} - [OINK 是什么](/zh/docs/about/) — 项目定位与来历 - [亮点特性](/zh/docs/about/features/) — 本地优先具体指什么 - [配置总览](/zh/docs/customize/config/) — 哪些功能会引入外部服务 - [品牌外观](/zh/docs/customize/brand/) — 换字体与图标 --- 反链: - [简介](/zh/docs/about/) - [亮点特性](/zh/docs/about/features/) ================ Source: https://oink.pgsty.com/zh/docs/start/index.md ================ # 快速上手 > 从官方 OINK Starter 建立可运行的本地基线,再依次定制内容、语言、品牌、集成与部署。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- 新站点的推荐起点是 [`pgsty/oink-starter`](https://github.com/pgsty/oink-starter),而不是复制本站这个 文档与回归测试仓库。Starter 是公开的 GitHub 模板:它固定 OINK v1.0.0,默认即可构建,只包含中性的项目示例与部署 workflow。 > [!IMPORTANT] 两个版本号承担不同职责 > OINK 声明的兼容性下限是 Hugo Extended 0.160.1。当前 > Starter 与它的 CI 固定使用 Hugo Extended 0.165.0 和 Go 1.27。下面这条路径应 > 使用 Starter 固定的工具链;只有刻意维护旧环境的既有站点才使用较低的兼容下限。 ## 选择起点 {#choose} | 当前情况 | 推荐路径 | 得到什么 | | --- | --- | --- | | 新建文档站或项目站 | [OINK Starter](/zh/docs/start/starter/) | 一套精简的三语 Docs、Blog、Book 站点与两条部署 workflow | | 已有 Hugo 站点 | [从零安装](/zh/docs/start/from-scratch/) | 不替换内容,只补 OINK 模块与 Goldmark 前置配置 | | 已有 Docsy 或旧版 OINK 站点 | [版本升级](/zh/docs/admin/upgrade/) | 保留内容,迁移受支持的语法,并审查站点覆盖 | ## 五分钟建立基线 {#baseline} 1. ### 安装工具 {#tools} 安装 Git、Go 1.27 或更新版本,以及 Hugo Extended 0.165.0 或更新版本。Hugo 输出必须包含 `extended`: ```console $ go version go version go1.27.0 darwin/arm64 $ hugo version hugo v0.165.0+extended+withdeploy darwin/arm64 ``` macOS 可以执行 `brew install git go hugo`。Linux 与 Windows 请按官方 [Hugo 安装指南](https://gohugo.io/installation/)和 [Go 下载页](https://go.dev/dl/)安装,并确认选择 Hugo **Extended**。 1. ### 创建或克隆站点 {#clone} 准备长期维护时,请打开 Starter 仓库并点击 **Use this template**,然后克隆 GitHub 为你创建的新仓库。只想在本机评估原始模板时执行: ```bash git clone https://github.com/pgsty/oink-starter.git my-docs cd my-docs hugo server ``` 1. ### 打开基线 {#open} 打开 。默认 Starter 还在 `/zh/` 发布中文,在 `/fr/` 发布法语。开始修改前,先确认 Docs、Blog、Book、本地搜索、语言切换与深浅色 模式都能工作。 1. ### 完成一个可见修改 {#first-change} 修改 `hugo.yaml` 顶部的站名与规范 URL,再修改 `data/home/en.yaml` 中的一句话。 浏览器刷新后能同时看到两处变化,才算证明配置、内容与固定版本的主题已经正确连通。 {.steps} ## 由浅入深地定制 {#learning-path} - [使用 OINK Starter](/zh/docs/start/starter/) — 先改身份,再依次处理语言、首页、 内容、导航、品牌、集成与部署。 - [Starter 仓库导览](/zh/docs/start/anatomy/) — 每个文件负责什么,哪些要替换, 哪些可以删除。 - [编写页面](/zh/docs/write/pages/) — front matter、标题、链接、图片、草稿与页尾控件。 - [组件总览](/zh/docs/components/) — 内容树稳定后,再增加表达能力。 - [品牌外观](/zh/docs/customize/brand/) — Logo、强调色、字体、页宽与 CSS 扩展点。 - [发布上线](/zh/docs/admin/deploy/) — 使用内置 GitHub Pages 或 Cloudflare Pages workflow,再验证真实公开路由。 {.cards} 这个顺序是有意的。先证明构建与内容树,再逐项增加定制,比同时修改语言、导航、 CSS、分析与托管更容易定位问题。 ## 发布门禁 {#publication-gate} 第一次推送前,执行与 Starter workflow 相同的严格生产构建: ```bash hugo --cleanDestinationDir --gc --minify --environment production \ --printPathWarnings --panicOnWarning ``` 命令以 `Total in …` 结束、没有警告或错误,而且 `public/` 中存在各语言根与代表性的 Docs、Blog、Book 路由,才算通过。此时仍只证明本地构建:本地构建、提交、推送、 workflow 变绿与公开站点正确,是彼此独立的关卡。 ## 下一步 {#next} 继续阅读[完整 Starter 教程](/zh/docs/start/starter/)。如果模板有你不需要的结构, 按[仓库导览](/zh/docs/start/anatomy/)安全删减。只有在给既有站点接入 OINK,或者 明确想亲手组装每个文件时,才走[从零建站](/zh/docs/start/from-scratch/)路径。 --- 本节页面: - [使用 OINK Starter](/zh/docs/start/starter/): 按身份、语言、首页、内容、导航、品牌、集成、部署的顺序,把官方 Starter 逐层变成你的项目站点。 - [Starter 仓库导览](/zh/docs/start/anatomy/): oink-starter 的文件级地图:身份、语言、首页、内容、导航、品牌、部署与固定主题分别由哪里管理。 - [从零建站与其它安装方式](/zh/docs/start/from-scratch/): 从空目录搭一个最小 OINK 站点,以及 Module / submodule / 离线归档 / 克隆四种安装方式的取舍。 --- 反链: - [文档](/zh/docs/) - [简介](/zh/docs/about/) - [亮点特性](/zh/docs/about/features/) - [案例](/zh/docs/about/showcase/) - [卡片](/zh/docs/components/cards/) - [品牌外观](/zh/docs/customize/brand/) - [从零建站](/zh/docs/start/from-scratch/) - [编写页面](/zh/docs/write/pages/) ================ Source: https://oink.pgsty.com/zh/docs/start/starter/index.md ================ # 使用 OINK Starter > 按身份、语言、首页、内容、导航、品牌、集成、部署的顺序,把官方 Starter 逐层变成你的项目站点。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- [`pgsty/oink-starter`](https://github.com/pgsty/oink-starter) 是新建 OINK 站点的正式起点。它刻意小于 `oink.pgsty.com`:不会把主题文档、分析账号、评论仓库、 浏览器回归套件或 PGSTY 品牌复制进你的项目。 当前模板固定 OINK v1.0.0、Go 1.27 与 Hugo Extended 0.165.0。 默认三语、仅英文、英中双语三个 profile 都已经在这个版本上完成 warning 即失败的 严格构建。 ## 模板包含什么 {#contents} | 表面 | 内置基线 | 第一个决定 | | --- | --- | --- | | 语言 | 英语、简体中文、法语 | 保留三语,或选择内置单语 / 双语 profile | | 内容 | Docs、Blog 与一本简短 Book 教程 | 重写示例;确认整个表面不需要时才整棵删除 | | 首页 | 每种语言一份精简 `data/home/.yaml` | 替换项目承诺与入口 | | 品牌 | 中性 Logo 与 favicon | 有正式项目图形之前先保留 | | 集成 | 仓库、Giscus、分析、分享、反馈示例均被注释 | 只启用你准备长期运营的完整配置 | | 部署 | GitHub Pages 与 Cloudflare Pages Direct Upload workflow | 选择一条生产路径并验证真实 URL | Starter 自己的 `/book/` 是一份从预览到部署的四章短教程。本页是维护者级版本: 说明修改顺序、各层边界,以及每层之后应执行的检查。 ## 创建自己的仓库 {#create-repository} ### 推荐使用 GitHub 模板 {#github-template} 打开 [Starter 仓库](https://github.com/pgsty/oink-starter),点击 **Use this template → Create a new repository**,再克隆 GitHub 在你的账号或组织下 创建的仓库: ```bash git clone https://github.com/OWNER/PROJECT-DOCS.git cd PROJECT-DOCS hugo server ``` 这样站点从一开始就有自己的 Git 历史,原始 Starter 只是上游参考,不会成为一个 可能误推送的 remote。 ### 克隆原始仓库进行评估 {#clone-original} 只做一次性本地评估时执行: ```bash git clone https://github.com/pgsty/oink-starter.git cd oink-starter hugo server ``` 真实项目不要从删除这个 clone 的 `.git` 目录开始。GitHub 模板操作已经创建了清晰的 项目边界,并保留可审计的初始提交。 ## 修改前先预览 {#preview} 依次打开: - `/`、`/zh/`、`/fr/`:三个首页; - `/docs/`、`/blog/`、`/book/`:三种内容表面; - 任意一组译文,再操作语言切换器; - 本地搜索、深浅色切换,以及一个窄屏视口。 同时记录实际解析的模块: ```bash hugo mod graph | grep github.com/pgsty/oink ``` 结果应当是 `github.com/pgsty/oink@v1.0.0`。这份未修改的预览,是后面 判断每次改动的基线。 ## 分层定制 {#customize} ### 第一层:站点身份 {#identity} 修改 `hugo.yaml` 顶部标有 `CHANGE ME` 的两个值: ```yaml {title="hugo.yaml"} title: &siteTitle Project Name baseURL: https://example.org/ ``` 标题的 YAML 锚点会把站名带进所有已启用语言。接着修改版权人,并在新仓库已存在后 取消仓库链接的注释: ```yaml {title="hugo.yaml"} params: copyright: authors: '[项目贡献者](https://example.org/community/)' from_year: 2026 github_repo: https://github.com/OWNER/PROJECT-DOCS github_branch: main ``` 重新运行 `hugo server`,检查浏览器标题、页脚、编辑 / 历史链接与 canonical URL。 项目图形尚未定稿时先不要改 Logo;文字身份更容易先完成评审。 ### 第二层:语言 profile {#languages} 根配置默认启用英语、中文和法语。如果这不是目标语言组合,请在其它配置修改之前 选择内置 profile: ```bash cp examples/hugo.single.yaml hugo.yaml # 仅英文 cp examples/hugo.bilingual.yaml hugo.yaml # 英文 + 中文 ``` 这两份是完整的最小配置,不是可以叠加的片段;复制会覆盖根文件里那些被注释的集成 示例。因此应在最开始做;`hugo.yaml` 已有项目修改时,只合并 `languages` 与 `disableLanguages`,不要整文件覆盖。 未启用语言仍保留声明,让 Hugo 能识别 `.zh.md` 与 `.fr.md` 是译文并安全忽略。 要永久移除一种语言,先确认所选 profile 能构建,再删除对应内容与首页数据。 ### 第三层:首页 {#home} 首页是数据,不是难以维护的整页模板覆盖: ```text data/home/en.yaml data/home/zh.yaml data/home/fr.yaml ``` 先改一种语言。每个文件里的 `sections` 决定顺序,`hero`、`cards`、`cta` 提供内容。 保持结构,替换项目承诺、目标 URL 与示例卡片。第一种语言确认无误后,再把同一组事实 翻译到已启用语言。 需要其它组合时,使用[首页与落地页](/zh/docs/customize/home/)中的完整注册表;不要复制 Starter 的首页 partial,因为这里本来就没有站点自有模板。 ### 第四层:内容与导航 {#content-navigation} 重写或删除 `content/` 下的示例叶子页面。确定整个表面不属于你的项目之前,先保留 栏目根: ```text content/docs/ 参考与任务文档 content/blog/ 文章、设计记录与发布说明 content/book/ 连续阅读的长篇指南 ``` 内容树就是侧栏。顶部导航写在各语言 `_index` 根页的 `menus.main` 里,因此给 Docs、 Blog 或 Book 改名时,修改发生在它所描述的内容旁边,而不是另一棵全局菜单树。译文 并排放置,对应标题使用相同的显式 ID: ```text page.md page.zh.md page.fr.md ``` 新增自定义导航数据之前,先读[组织内容](/zh/docs/write/organize/);大多数站点使用生成树 已经足够。 ### 第五层:品牌与阅读功能 {#brand-features} 正式图形准备好后,替换 `assets/icons/logo.svg` 与 `static/favicon.svg`。随后一次只启用 一组最小而有用的配置: ```yaml {title="hugo.yaml"} params: ui: theme_color: '#245f94' typography: system image_zoom: true share: [mastodon, linkedin, email, copy] ``` 自定义本地字体时,用 `params.ui.fonts` 写字体族,或者在站点 CSS 中声明字体文件。 布局、侧栏、搜索与组件配置应查询[配置总览](/zh/docs/customize/config/),不要复制 `oink.pgsty.com` 那份大得多的站点配置。 ### 第六层:外部集成 {#integrations} Starter 默认关闭或注释了仓库操作、Giscus、Google Analytics、反馈与分享。只有 必需事实全部明确时才启用: - 仓库链接需要真实 owner、repository 与 branch; - Giscus 需要仓库 / 分类名称和不可变 ID; - Google Analytics 需要项目自己的 measurement ID; - 反馈只有在分析存在时才记录结构化 `gtag` 事件; - 助手链接会把当前 URL 发送给第三方,因此必须做显式策略选择。 不完整的可选块应继续保持注释。各集成的运营边界见[启用评论](/zh/docs/admin/comments/)、 [分析与 SEO](/zh/docs/admin/analytics/)和[仓库与页面信息](/zh/docs/customize/repository/)。 ## 构建与部署 {#build-deploy} ### 严格本地构建 {#strict-build} 启用托管 workflow 前执行: ```bash hugo --cleanDestinationDir --gc --minify --environment production \ --printPathWarnings --panicOnWarning ``` 提交 `hugo.yaml`、`go.mod` 与 `go.sum`;不要提交生成的 `public/`、`resources/`、模块 缓存或本地模块替换。 ### GitHub Pages {#github-pages} Starter 已包含 `.github/workflows/github-pages.yaml`。在 **Settings → Pages** 中选择 **GitHub Actions** 作为 Source。推送到 `main` 后, workflow 使用固定工具链构建,向 GitHub 查询正确的项目子路径,再通过 Pages 部署 API 发布 `public/`。 ### Cloudflare Pages {#cloudflare-pages} 内置 `.github/workflows/cloudflare-pages.yaml` 使用 Direct Upload。创建 Pages Direct Upload 项目,添加 `CLOUDFLARE_ACCOUNT_ID` 与 `CLOUDFLARE_API_TOKEN`,再手动 运行一次 workflow。设置仓库变量 `CLOUDFLARE_PAGES_ENABLED=true` 后才会自动部署; 规范地址不是默认 `pages.dev` 域名时,再设置 `CLOUDFLARE_SITE_URL`。 同一个项目只选 Direct Upload 或 Cloudflare Git integration 其中一种。完整托管对比 与 `baseURL` 规则见[发布上线](/zh/docs/admin/deploy/)。 ## 验证并删除示例 {#verify} 宣布站点完成前: 1. 搜索 `Project Name`、`example.org`、`OWNER`、`PROJECT` 等占位符,逐项确认剩余位置 是否有意保留。 1. 在桌面与移动端打开每种已启用语言的根,以及代表性的 Docs、Blog、Book 页面。 1. 确认语言切换落到对页,而不是首页。 1. 验证搜索、深色模式、一个组件、Markdown 输出、打印、404、canonical URL 与仓库操作。 1. 把部署 workflow 和公开 URL 与本地构建分开检查。 {.steps} 删除示例 Book 或 Blog 之前,要同时移除对应顶部菜单根,以及首页上指向它的卡片。每整棵 删除一个表面就严格重建一次,才能让失败归因到单一改动。 ## 下一步 {#next} 用 [Starter 仓库导览](/zh/docs/start/anatomy/)查询文件职责,再继续阅读 [编写页面](/zh/docs/write/pages/)与[配置总览](/zh/docs/customize/config/)。已有站点不应 继承 Starter 内容模型时,改走[从零建站](/zh/docs/start/from-scratch/)路径。 --- 反链: - [OINK v0.8.1](/zh/blog/release/0.8.1/) - [OINK v1.0.0](/zh/blog/release/1.0.0/) - [跑起第一个站点](/zh/book/01-start/) - [快速上手](/zh/docs/start/) - [仓库导览](/zh/docs/start/anatomy/) - [从零建站](/zh/docs/start/from-scratch/) ================ Source: https://oink.pgsty.com/zh/docs/start/anatomy/index.md ================ # Starter 仓库导览 > oink-starter 的文件级地图:身份、语言、首页、内容、导航、品牌、部署与固定主题分别由哪里管理。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- 本页说明从 [`pgsty/oink-starter`](https://github.com/pgsty/oink-starter) 创建的仓库,不再介绍大得多的 `oink.pgsty.com` 文档与回归测试仓库。主题源码不会 复制进任何一个站点:`go.mod` 以 Hugo Module 形式固定版本,Hugo 把解析结果存进 Go 模块缓存。 ## 顶层地图 {#layout} ```filetree {title="oink-starter/"} - oink-starter/ - hugo.yaml # 身份、语言、输出、参数与模块导入 - go.mod # 站点模块与精确 OINK 版本 - go.sum # 模块校验和 - examples/ - hugo.single.yaml # 仅英文的完整 profile - hugo.bilingual.yaml # 英文 + 中文的完整 profile - data/ - home/ - en.yaml # 每种语言一份精简落地页 - zh.yaml - fr.yaml - content/ - _index.md # 各语言首页根 - _index.zh.md - _index.fr.md - docs/ # 简介、快速上手、教程、参考 - blog/ # 文章、设计记录、发布说明 - book/ # 介绍 Starter 的连续教程 - assets/ - icons/logo.svg # 经 Hugo 处理的项目 Logo - static/ - favicon.svg # 原样复制到站点根 - i18n/ - fr.yaml # Starter 自有法语界面覆盖 - .github/workflows/ - github-pages.yaml # 严格构建与 GitHub Pages 部署 - cloudflare-pages.yaml # 严格构建与 Cloudflare Direct Upload - README.md # 面向仓库维护者的操作摘要 - LICENSE # 模板源码许可证 ``` 生成的 `public/`、`resources/`、`.hugo_build.lock` 与模块缓存是被忽略的构建状态, 不是源码。 ## 最先修改什么 {#change-first} | 路径 | 职责 | 第一次操作 | | --- | --- | --- | | `hugo.yaml` | 身份、规范 URL、语言、输出、主题功能、可选集成 | 修改两个标记值;其它修改前先选择语言 profile | | `data/home/` | 首页承诺、卡片与行动入口 | 一种语言确认后,再重写所有已启用语言 | | `content/` | 全部读者可见内容 | 替换示例叶子;确认整个表面不要时才删除栏目根 | | `assets/icons/logo.svg` | 经处理的 Logo | 有正式图形后再替换 | | `static/favicon.svg` | 浏览器图标 | 与 Logo 一起评审后替换 | | `hugo.yaml` 中的 `params.github_*` | 编辑、历史、新建页面与 issue 链接 | 目标仓库已存在后才取消注释 | ## 哪些必须保留 {#keep} - `go.mod` 与 `go.sum`:两者共同固定并校验 OINK v1.0.0,都要提交。 - `hugo.yaml` 中三项 Goldmark 设置:原生 Steps、Cards、Fields、图片属性与 Book 目标都依赖它们。 - `outputs`:删除 `markdown`、`LLMS` 或 `print`,会有意删除对应的 Markdown、 Agent 索引或打印表面。 - workflow 中的 `fetch-depth: 0`:保留 `enableGitInfo` 时,最后修改与贡献者事实需要 完整 Git 历史。 - CI 中的 `GOWORK: off` 与 `HUGO_MODULE_WORKSPACE: off`:开发者本地 workspace 不得 替换 CI 正在验证的公开版本。 ## 可选表面 {#optional} Docs、Blog 与 Book 是彼此独立的顶层表面。安全删除其中一个的顺序是: 1. 删除对应的 `content//` 内容树; 1. 删除首页指向它的卡片或链接; 1. 确认其它页面不再链接它; 1. 严格构建,并检查剩余顶部导航。 {.steps} 不要只删除某种语言的栏目根:那会形成难以区分「有意不对称」与「漏译」的语言专属导航 和回退行为。要么在所有已启用语言中删除整个表面,要么明确记录这种不对称。 完成语言选择后,`examples/` 下两个配置 profile 可以删除,也可以作为参考保留;真正 生效的站点配置只有根目录 `hugo.yaml`。 ## 内容与导航 {#content-navigation} Docs 与 Book 下的目录结构和 `weight` 共同形成侧栏与翻页顺序。顶部导航来自栏目根的 `menus.main`。译文根重复相同的 `identifier`、`parent` 与 weight,只翻译可见标签。 Starter 刻意演示 Documentation System 内容模型: - 简介回答是什么、为什么; - 快速上手帮助新用户得到结果; - 教程带领读者完成端到端任务; - 参考记录精确的受支持行为。 可以按项目需要改名或重组,但应保留不同学习路径之间的分工,不要把所有答案混进一棵树。 ## 语言模型 {#languages} 英文源码以 `.md` 结尾,中文和法语对页分别以 `.zh.md`、`.fr.md` 结尾。首页数据按 `data/home/` 下的语言键分文件。根 profile 声明语言、locale、顺序与站点描述。 单语与双语 profile 仍声明被禁用的语言,这是有意设计:Hugo 会把未使用后缀识别为 译文,而不会把多个文件渲染到同一个英文 URL。只在项目配置开始前复制 profile;之后 应手工合并。 ## OINK 在哪里 {#theme} 两个文件建立模块边界: ```yaml {title="hugo.yaml"} module: imports: - path: github.com/pgsty/oink hugoVersion: extended: true min: '0.160.1' ``` ```go-mod {title="go.mod"} module github.com/OWNER/PROJECT-DOCS go 1.27.0 require github.com/pgsty/oink v1.0.0 ``` `hugo mod graph` 显示实际解析版本。生产使用 `go.mod` 中的精确标签;本地 `HUGO_MODULE_REPLACEMENTS` 只是开发覆盖,绝不能提交,也不能当成发布证明。 ## 部署文件 {#deployment} GitHub Pages workflow 在推送 `main` 后自动运行;仓库设置必须选择 GitHub Actions 作为 Pages Source。Cloudflare workflow 默认手动运行,只有仓库变量 `CLOUDFLARE_PAGES_ENABLED=true` 存在时才自动执行;所需账号 ID 与 API token 始终 保存在仓库 secrets 中。 只保留实际运营的部署路径。Cloudflare Direct Upload 与 Cloudflare Git integration 是同一个项目的两种所有权模型,不是应当同时运行的两道关卡。 ## 安全的定制顺序 {#order} 1. 证明未修改的预览可用。 1. 修改身份并选择语言。 1. 替换一种首页,再补齐译文。 1. 替换内容并验证导航。 1. 品牌与阅读功能一次只改一组。 1. 启用完整的外部集成。 1. 执行严格生产构建。 1. 部署,再独立验证生产环境。 {.steps} 仓库已经属于自己后,每层之间做一次提交。小边界能让后续回归与回滚明确归因到一个决定。 ## 验证 {#verify} ```bash hugo mod graph | grep github.com/pgsty/oink hugo --cleanDestinationDir --gc --minify --environment production \ --printPathWarnings --panicOnWarning git status --short ``` 模块图应显示固定发布,构建没有警告或错误,Git 状态只包含源码修改而没有 `public/` 或 缓存。之后打开所有已启用语言的根,以及代表性的 Docs、Blog、Book 路由,再进入部署。 ## 相关 {#related} - [使用 OINK Starter](/zh/docs/start/starter/) — 完整分层流程 - [从零建站](/zh/docs/start/from-scratch/) — 不采用这套内容模型,只接入 OINK - [组织内容](/zh/docs/write/organize/) — 侧栏、翻页与菜单权威 - [配置总览](/zh/docs/customize/config/) — 当前全部站点参数 - [发布上线](/zh/docs/admin/deploy/) — 托管商配置与生产检查 --- 反链: - [pig.pgsty.com](/zh/case/pig/) - [案例](/zh/docs/about/showcase/) - [快速上手](/zh/docs/start/) - [从零建站](/zh/docs/start/from-scratch/) - [OINK Starter](/zh/docs/start/starter/) ================ Source: https://oink.pgsty.com/zh/docs/start/from-scratch/index.md ================ # 从零建站与其它安装方式 > 从空目录搭一个最小 OINK 站点,以及 Module / submodule / 离线归档 / 克隆四种安装方式的取舍。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- 这是推荐路径 [OINK Starter](/zh/docs/start/starter/) 的手工替代方案。本页从空目录 搭建一个最小 OINK 站点:一份精简 `hugo.yml` 加一条 `hugo mod get`,得到一个可预览 的单语站点。代价是首页、示例内容、部署 workflow 与每种组件用法都要自己组装。 已有 Hugo 站点时不需要脚手架:装上主题模块,再补三项 goldmark 前置配置(见[写 `hugo.yml`](#config)),正文不用重写。已有 Docsy 站点见[版本升级](/zh/docs/admin/upgrade/)。 后半部分是四种安装方式的取舍:Hugo Module、Git submodule、离线归档、固定版本克隆。 当前 v1.0.0 发布路径应使用 Go 1.27 与 Hugo Extended 0.165.0;只有刻意维护旧环境的 既有站点才使用主题声明的较低兼容下限。 ## 从空目录到第一页 {#scaffold} 1. ### 建骨架并获取主题 {#skeleton} ```bash hugo new site --format yaml my-docs cd my-docs hugo mod init github.com/example/my-docs hugo mod get github.com/pgsty/oink@v1.0.0 ``` `hugo mod init` 后面跟的是你自己站点的模块路径,通常就是仓库地址。`hugo mod get` 会写出 `go.mod` 与 `go.sum`,两个都要提交。 最新版本号在 [GitHub Releases](https://github.com/pgsty/oink/releases);本页出现的 `v1.0.0` 是本站当前固定的版本。生产站点固定到发布标签,不要跟随 `main`:`@latest` 是一次性解析动作,不是版本策略。 1. ### 写 `hugo.yml` {#config} 把 `hugo new site` 生成的 `hugo.yaml` 改名为 `hugo.yml`(两个后缀 Hugo 都接受,本文统一用后者),内容替换为下面这份,可直接构建: ```yaml {title="hugo.yml" collapse=30} title: Product Docs baseURL: https://docs.example.com/ defaultContentLanguage: en # enableGitInfo: true # 页面「最后修改」时间来自 git,先 git init 再打开 languages: en: label: English locale: en-US weight: 1 title: Product Docs params: description: Everything about running Product in production menus: main: - { name: Docs, pageRef: /docs, weight: 20 } - { name: Blog, pageRef: /blog, weight: 50 } # 三项 Goldmark 前置:OINK 的原生 Markdown 组件全靠它们 markup: goldmark: renderer: unsafe: true # 允许内容里的行内 HTML parser: attribute: block: true # {.steps} {.cards} {caption=} 这类属性行 wrapStandAloneImageWithinParagraph: false # 块级图片才能带属性行 highlight: noClasses: false # 代码配色跟随深浅色模式 params: offline_search: true github_repo: https://github.com/example/product-docs copyright: authors: '[Example Inc.](https://example.com/)' from_year: 2026 ui: dark_mode: true sidebar_menu_foldable: true section_index: cards outputs: home: [HTML, markdown, LLMS] page: [HTML, markdown] section: [HTML, RSS, print, markdown] module: imports: - path: github.com/pgsty/oink hugoVersion: extended: true min: '0.160.1' ``` 五段分别管什么: | 段 | 管什么 | 少了会怎样 | | --- | --- | --- | | 顶层 + `languages` | 站名、域名、语言与顶栏菜单 | `baseURL` 不对,线上所有绝对链接指错 | | `markup.goldmark` | 三项组件前置 | 属性行变成正文里的一行 `{.steps}` | | `params` | 搜索、仓库链接、外壳开关 | 交互功能默认关闭,主题不替站点决定 | | `outputs` | 每页的 `.md`、`llms.txt`、打印页 | 页面菜单里没有「复制 Markdown」,也没有打印视图 | | `module` | 引用主题、声明 Hugo 下限 | 构建时找不到主题 | 写公式还需要 Goldmark 的 passthrough 扩展,见[公式](/zh/docs/components/math/)。每个键的完整含义与默认值见[配置总览](/zh/docs/customize/config/)。 1. ### 写第一页 {#first-page} `content/` 下的每个一级目录是一个分区,目录结构就是侧栏结构。文档分区至少要有一个 `_index.md`: ```markdown {title="content/docs/_index.md"} --- title: Docs linkTitle: Docs description: Everything about running Product in production. weight: 20 --- 从[安装](/docs/install/)开始。 ``` ````markdown {title="content/docs/install.md"} --- title: Install description: Install Product on a fresh machine. weight: 10 --- ## Prerequisites {#prerequisites} > [!IMPORTANT] > Product 需要 PostgreSQL 18 或更高版本。 ## Install {#install} ```bash curl -fsSL https://get.example.com | bash ``` ```` 标题写显式 `{#id}`:后续加译文时两种语言的锚点才能对应。页面写法见[编写页面](/zh/docs/write/pages/)。 1. ### 预览 {#preview} ```bash hugo server ``` 打开 ,侧栏里有 Docs → Install。修改文件是毫秒级热重载。 {.steps} ## 其它安装方式 {#install-methods} 上面用的是 Hugo Module。另外三种方式面向特定约束:网络隔离、平台要求构建输入包含完整主题树、组织内部需要评审主题副本。除 `hugo mod vendor` 之外,它们都不建立 Go 模块,站点用 `theme: oink` 而不是 `module.imports` 引用主题;共同的代价是版本解析与完整性校验由你自己负责。 ### Hugo Module(推荐) {#hugo-module} ```bash hugo mod init github.com/example/product-docs hugo mod get github.com/pgsty/oink@v1.0.0 ``` ```yaml {title="hugo.yml"} module: imports: - path: github.com/pgsty/oink ``` 唯一能让 Hugo 自己解析版本、校验 checksum、并在 `go.sum` 里留下审计记录的方式。`hugo mod graph` 看实际解析结果,`hugo mod get -u` 升级。需要本机有 Go。 ### Git submodule {#git-submodule} 在站点仓库里记录准确的主题 commit: ```bash git submodule add https://github.com/pgsty/oink.git themes/oink git -C themes/oink fetch --tags git -C themes/oink checkout v1.0.0 git add .gitmodules themes/oink ``` ```yaml {title="hugo.yml"} theme: oink ``` CI 必须在运行 Hugo 之前初始化 submodule,否则 `themes/oink` 是空目录: ```bash git submodule update --init --recursive ``` ### 离线归档 {#offline-archive} 网络隔离环境使用。两条路径,都先在联网机器上准备,再整体搬入。 **用 `hugo mod vendor`**:把已解析的主题源码固化进站点目录,之后构建既不联网也不需要 Go。 ```bash hugo mod vendor # 生成 _vendor/,里面是主题的完整源码树 tar czf my-docs.tgz . # 连 _vendor/ 一起搬进隔离环境 ``` `_vendor/` 存在时 Hugo 优先使用它(`hugo mod graph` 输出 `+vendor`),`hugo.yml` 里的 `module.imports` 保持不变。这一步需要 Go,之后的构建不需要。升级主题要回到联网环境重新执行 `hugo mod get` 与 `hugo mod vendor`。 `_vendor/` 只收主题挂载出来的目录(`assets` `data` `i18n` `layouts` `static`)以及 `hugo.yaml` 与 `theme.toml`,不含 `LICENSE`、`NOTICE` 与 `VENDOR.json`。要对外分发这份归档,把这三个文件从主题仓库一并取来。 **用 tag 源码归档**:不建 Go 模块,直接把某个版本的主题解压到 `themes/oink/`。 ```bash curl -L -o oink.tar.gz \ https://github.com/pgsty/oink/archive/refs/tags/v1.0.0.tar.gz mkdir -p themes/oink tar xzf oink.tar.gz -C themes/oink --strip-components=1 ``` ```yaml {title="hugo.yml"} theme: oink ``` 主题仓库的根目录就是模块根目录,解压出来直接是 `layouts/`、`assets/`、`i18n/`、`static/` 这一层,不需要再进入下一级。重新分发时必须保留 `LICENSE`、`NOTICE` 与 `VENDOR.json`。最后一个记录了每个第三方运行时的版本、来源、许可证路径与 SHA-256,是离线审计的依据。 跨机器传输时,在联网侧从不可变标签生成归档与校验值: ```bash git clone --branch v1.0.0 --depth 1 \ https://github.com/pgsty/oink.git oink git -C oink archive --format=tar.gz --prefix=oink/ \ --output=../oink-v1.0.0.tar.gz v1.0.0 shasum -a 256 oink-v1.0.0.tar.gz \ > oink-v1.0.0.tar.gz.sha256 ``` 把归档与 `.sha256` 一起传入隔离环境,先校验再解压: ```bash shasum -a 256 -c oink-v1.0.0.tar.gz.sha256 mkdir -p themes tar -xzf oink-v1.0.0.tar.gz -C themes ``` 这样得到的归档是自建产物,不是项目发行物。某个标签的发行页面是否附带归档与校验文件按发布而定,使用公开附件时独立验证其校验值。 断网构建之前确认归档内容完整,这十一项都要在: ```filetree {title="themes/oink/"} - oink/ - go.mod # 模块路径声明,Hugo Module 方式解析用 - hugo.yaml # 主题默认参数与 Hugo 版本下限 - theme.toml # 主题元数据,theme: oink 方式需要 - LICENSE # Apache-2.0 - NOTICE # 上游署名,再分发时必须保留 - VENDOR.json # 第三方运行时清单:版本、来源、许可证路径、SHA-256 - assets/ # SCSS、JS 与随主题分发的第三方运行时 - layouts/ # 模板、partial、shortcode、render hook - static/ # 字体文件,原样发布 - i18n/ # 32 份界面语言文件 - data/ # 页尾出处行用的 SPDX 许可证表 ``` ### 固定版本克隆 {#pinned-clone} 托管平台要求构建输入包含完整主题树时用: ```bash git clone https://github.com/pgsty/oink.git themes/oink git -C themes/oink checkout v1.0.0 ``` 与 submodule 的区别是主题文件直接进入你的仓库历史,没有 `.gitmodules` 这层间接。记录最终解析出的 commit 与恢复流程。 ### 四种方式对比 {#comparison} | 方式 | 需要 Go | 版本可审计 | 主题源码进你的仓库 | 适用 | | --- | --- | --- | --- | --- | | **Hugo Module** | 是 | `go.sum` 自动校验 | 否 | 默认推荐 | | Git submodule | 否 | 仓库记录 commit | 以引用形式 | 需要主题源码在库内 | | 离线归档 | 否 | 手工核对 checksum | 是 | 网络隔离 | | 固定版本克隆 | 否 | 需自行记录 | 是 | 平台要求完整树 | > [!TIP] 消费站点不需要前端工具链 > Bootstrap、Font Awesome、字体、搜索与图表运行时全部随主题分发。站点不需要 `node_modules`、PostCSS、RTLCSS,也不需要 CDN。为 Docsy 站点安装 npm 依赖的教程属于上游 Docsy 的流程,不适用于 OINK。 ## 用本地主题 checkout 开发 {#local-theme-checkout} 同时修改主题与站点时才需要这一节。把两个仓库克隆为同级目录: ```text {title="同级目录布局" copy=false} ~/pgsty/ ├── oink/ # 主题 └── product-docs/ # 你的站点 ``` 用环境变量 `HUGO_MODULE_REPLACEMENTS` 把模块临时替换为本地 checkout,`go.mod` 不变: ```bash cd ~/pgsty/product-docs HUGO_MODULE_REPLACEMENTS='github.com/pgsty/oink -> ../oink' hugo server ``` 文档站仓库的 `Makefile` 就是这几条命令的别名,`make dev` 与 `make check` 要求主题 checkout 在同级目录 `../oink`: ```makefile {title="Makefile:文档站里的写法"} build: hugo --cleanDestinationDir --minify check: HUGO_MODULE_REPLACEMENTS='github.com/pgsty/oink -> $(abspath ../oink)' npm test dev: HUGO_MODULE_REPLACEMENTS='github.com/pgsty/oink -> $(abspath ../oink)' hugo server --renderToMemory ``` Go workspace(`go work init` + `HUGO_MODULE_WORKSPACE=go.work`)是等价的另一种做法。两种做法都只作用于本机:CI 与生产构建用的是 `go.mod` 里的版本,`go.work` 不要提交。 ## 验证 {#verify} ```bash hugo mod graph # 主题实际解析到哪一版 hugo --gc --minify --printPathWarnings --panicOnWarning ``` 构建以 `Total in …` 结束、没有 `WARN` / `ERROR` 即通过。再确认: - `/docs/` 打得开,侧栏里有你写的页面 - 顶栏有搜索框,搜得到刚写的标题 - 深浅色切换按钮在,切换后代码块配色跟着变(说明 `markup.highlight.noClasses: false` 生效) - `git status` 里有 `go.mod` 与 `go.sum`,没有 `public/`、`resources/` ## 相关 {#related} - [快速上手](/zh/docs/start/) — 在 Starter、既有 Hugo 站点与迁移之间选择 - [OINK Starter](/zh/docs/start/starter/) — 推荐的新站点路径 - [Starter 仓库导览](/zh/docs/start/anatomy/) — 模板各目录的职责 - [配置总览](/zh/docs/customize/config/) — `hugo.yml` 每个键的含义与默认值 - [编写页面](/zh/docs/write/pages/) — 第一页之后怎么继续写 - [版本升级](/zh/docs/admin/upgrade/) — 升级主题模块、从 Docsy 迁移 --- 反链: - [OINK v0.8.1](/zh/blog/release/0.8.1/) - [OINK v1.0.0](/zh/blog/release/1.0.0/) - [跑起第一个站点](/zh/book/01-start/) - [发布上线](/zh/docs/admin/deploy/) - [本地预览](/zh/docs/admin/preview/) - [版本升级](/zh/docs/admin/upgrade/) - [快速上手](/zh/docs/start/) - [仓库导览](/zh/docs/start/anatomy/) - [OINK Starter](/zh/docs/start/starter/) ================ Source: https://oink.pgsty.com/zh/docs/write/index.md ================ # 创作内容 > 写文档页、博客、书籍、发布页与 API 文档:一页文档长什么样,内容怎么组织。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- 本栏覆盖 OINK 支持的几种内容类型:文档页、博客文章、书籍、发布下载页、OpenAPI 参考。它们共用同一套 Markdown 与 front matter,各自另有约定。 ## 一页文档的构成 {#anatomy} 一页文档是一个 Markdown 文件。文件开头两行 `---` 之间是 front matter,即页面元数据:标题、侧栏短名、描述、排序。其余部分是正文,内容为普通 Markdown 加 OINK 的原生组件。下面是一个完整页面: ```markdown {title="content/docs/install.zh.md"} --- title: 安装 Pigsty linkTitle: 安装 description: 在一台干净的 EL 9 机器上装出可用的 PostgreSQL 集群。 weight: 20 --- ## 前提条件 {#prerequisites} 一台能 SSH 登录的 Linux 机器,`sudo` 免密,Python 3.11 或更高版本。 > [!IMPORTANT] > 安装脚本会改写 `/etc/yum.repos.d/`,先备份。 ``` 存为 `content/docs/install.zh.md`,运行 `hugo server` 后页面出现在 `/zh/docs/install/`,侧栏出现「安装」一行。 ## 内容类型与对应页面 {#map} | 你要写的 | 去哪页 | | --- | --- | | 一页文档:front matter、标题锚点、链接、图片、草稿 | [编写页面](/zh/docs/write/pages/) | | 目录树与侧栏:`_index.md`、`weight`、图标、折叠、多根侧栏 | [组织内容](/zh/docs/write/organize/) | | 查某个 front matter 键是什么意思 | [页面参数](/zh/docs/write/frontmatter/) | | 一篇博客、发布公告、RSS | [博客与文章](/zh/docs/write/blog/) | | 一本书:章节编号、图表式例、交叉引用、整本打印 | [书籍出版](/zh/docs/write/book/) | | 一个发布下载页:版本卡片、资产表、校验和 | [发布与下载页](/zh/docs/write/releases/) | | 一份 OpenAPI 参考页 | [API 文档](/zh/docs/write/openapi/) | | 中英双语写作:对等文件、锚点对齐、缺译回退 | [多语言](/zh/docs/customize/i18n/) | | 某个组件的语法与参数 | [组件总览](/zh/docs/components/) | --- 本节页面: - [编写页面](/zh/docs/write/pages/): 新建一页文档:文件放在哪、front matter 写什么、标题锚点为什么要手写、链接与图片怎么写、页尾会自动出现什么。 - [组织内容](/zh/docs/write/organize/): 目录结构就是侧栏树:`_index.md` 与 weight、栏目首页样式、图标与折叠、隐藏页面、把文档放在任意路径。 - [页面参数](/zh/docs/write/frontmatter/): front matter 全表:主题真正读取的每一个页面键,按侧栏、外壳、搜索、输出、页尾、Book、Landing、发布页分组。 - [博客与文章](/zh/docs/write/blog/): 开一个博客栏目:目录约定、文章的 front matter、封面图、按年份分组的列表页与 RSS。 - [书籍出版](/zh/docs/write/book/): 用 `type: book` 把一棵目录树变成一本书:章节编号、图表式例编号、交叉引用、生成式索引与整本打印。 - [发布与下载页](/zh/docs/write/releases/): 把版本号、标签、归档链接、校验和与安装命令写成本地事实,再让发布卡片、资产表、下载区块和索引页从同一份记录推导出来。 - [API 文档](/zh/docs/write/openapi/): 把 OpenAPI 规范放进站点,用随主题分发的 Swagger UI 或 Redoc 渲染成可浏览的接口文档,不连 CDN。 --- 反链: - [卡片](/zh/docs/components/cards/) - [定制站点](/zh/docs/customize/) ================ Source: https://oink.pgsty.com/zh/docs/write/pages/index.md ================ # 编写页面 > 新建一页文档:文件放在哪、front matter 写什么、标题锚点为什么要手写、链接与图片怎么写、页尾会自动出现什么。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- 本页覆盖一页文档的完整写法:文件位置、front matter、标题锚点、链接、图片、草稿与页尾。前提是站点已能本地构建,尚未搭起时先看[十分钟上手](/zh/docs/start/)。 ## 新建一页 {#new-page} 页面是 `content/` 下的 Markdown 文件,URL 由它在 `content/` 里的位置决定:`content/docs/install.md` 发布为 `/docs/install/`。中文译文是同目录下的 `.zh.md` 同名文件,与英文页共享同一条逻辑路径。 没有附带资源的页面写成单个文件。页面带图片、cast、示例配置这类资源时改成一个目录,页面本身命名为 `index.md`,资源与它同放,这是 Hugo 的[页面包](https://gohugo.io/content-management/page-bundles/)(page bundle): ```filetree {title="content/ 里的两种页面形态"} - content/ - docs/ - _index.md # 栏目首页,英文 - _index.zh.md # 栏目首页,中文 - install.md # 单文件页面 → /docs/install/ - install.zh.md # 它的中文译文 - anatomy/ # 页面包 → /docs/anatomy/ - index.md - index.zh.md - shell.webp # 页面资源,两种语言共用 ``` `hugo new content docs/install.md` 用 archetype 生成一个带 front matter 的空文件,见 [Hugo 文档](https://gohugo.io/commands/hugo_new_content/);手写文件同样可行。 > [!IMPORTANT] > 中文页没有英文对等页时,Hugo 不会把无语言后缀的资源分给它。这种情况下资源文件名要带 `.zh.`(`shell.zh.webp`),正文里仍然写 `shell.webp`。 ## 必要的 front matter {#front-matter} 文件开头两行 `---` 之间是 YAML front matter。四个键每页都应写上: ```yaml {title="content/docs/install.zh.md"} --- title: 安装 Pigsty # 页面大标题、浏览器标题、搜索结果标题 linkTitle: 安装 # 侧栏与面包屑里的短名,省略时用 title description: 在一台干净的 EL 9 机器上装出可用的 PostgreSQL 集群。 weight: 20 # 同级页面的排序,用 10 的倍数留出插入空间 --- ``` `description` 用一句话说清这页让读者做成什么。它出现在栏目首页的卡片、搜索结果与社交卡片中。`weight` 决定侧栏顺序,`weight` 相同时才退回字母序。 其余的键可选:图标、草稿、搜索权重、评论开关、页面外壳等,全表见[页面参数](/zh/docs/write/frontmatter/)。 ## 标题层级与稳定锚点 {#headings} 正文用 `##` 开始分节,`#` 留给 `title`。主题已渲染页面大标题,正文里再写一个 `#` 会出现两个一级标题。右栏的页面目录从 `##` 开始收,收到第几级由 Hugo 的 `markup.tableOfContents` 决定,本站是 `####`。 每个 `##` 与 `###` 都要手写英文锚点 `{#id}`: ```markdown {title="源码"} ## 前提条件 {#prerequisites} ### 磁盘与内存 {#disk-and-memory} ``` 理由有两条: - 中英对齐。Hugo 从标题文字生成 ID,中文标题生成中文 ID:`/docs/install/#prerequisites` 与 `/zh/docs/install/#前提条件` 指向同一个语义位置,却是两个锚点,翻译审计无法比对。译文标题写上英文页的 ID,两边即同一个片段。 - 链接稳定。标题文字会随措辞调整而改变,公开链接不应随之失效。显式 ID 一旦发布即视为公开路由;需要改名时保留旧 ID 的空锚点: ```markdown {title="源码:给旧锚点留一个空目标"} ## 快速开始 {#quickstart} ``` ID 用短横线小写英文,全页唯一。本站的翻译审计脚本会比对英文页与中文页渲染出的标题 ID,不一致就报错。 ## 链接写法 {#links} 三种写法,用途不同: | 写法 | 例子 | 什么时候用 | | --- | --- | --- | | 站内绝对路径 | `[配置总览](/zh/docs/customize/config/)` | 默认写法。指向已发布的路由,便于审计与全站替换,不受源码文件移动影响 | | 相对路径 | `[另一页](../organize/)`、`![图](shell.webp)` | 同一页面包内的资源,或有意跟着源码目录走的相邻页面 | | `ref` / `relref` shortcode | `[配置总览]({{< ref "/docs/configure/overview" >}})` | 需要构建期校验目标存在时;目标缺失时构建失败,不会留下死链 | 三种写法都带尾部斜杠,指向目录形式的路由(`/zh/docs/write/pages/`),与 Hugo 的默认永久链接一致。 主题没有链接渲染钩子,链接原样交给 Goldmark:外链不会自动加 `target="_blank"`,需要新标签页时写成 HTML,或在站点自己的 `layouts/_markup/render-link.html` 里处理。 普通 Markdown 链接不做存在性检查。因此: - 站内链接优先写绝对路径,改结构后用 `grep` 全站替换; - 移动页面时给旧路径加 `aliases`,同时把站内链接改到新路由,不要让 alias 长期承担导航; - 拿不准的目标用 `ref`,让构建替你检查。 双语页面链接到逻辑页面(`/zh/docs/write/pages/`),不要链接 `.zh.md` 文件名;片段 ID 保持语言中立。 ## 图片位置 {#images} 页面自己的截图放页面包,多页共用的图放 `assets/images/`,不需要处理的大文件放 `static/`。三处在源码里都写成 `![替代文字](来源)`,属性行控制图注、尺寸、缩放与编号,见[图片](/zh/docs/components/image/)。 ## 草稿与发布 {#drafts} `draft: true` 的页面不会进入构建产物: ```yaml {title="front matter"} --- title: 尚未定稿的迁移指南 draft: true --- ``` 预览时用 `hugo server -D` 显示草稿(`-D` 即 `--buildDrafts`)。`date` 写在未来的页面同样被排除,用 `-F` 显示。生产构建不加这两个开关,`hugo` 默认只发布已定稿的内容。 ## OINK 的 Markdown 扩展一览 {#extensions} 正文是标准 Markdown(Goldmark),加上下面这些原生形态。它们都是普通 Markdown 语法加一行属性,在 GitHub 上按源码阅读同样可读: | 组件 | 最短语法 | 页面 | | --- | --- | --- | | 提示块 | 块引用首行写 `> [!NOTE]` | [提示块](/zh/docs/components/callout/) | | 标签页 | 相邻的两个围栏各加 `{tab="Homebrew"}` | [标签页](/zh/docs/components/tabs/) | | 步骤 | 有序列表后面跟一行 `{.steps}` | [步骤](/zh/docs/components/steps/) | | 卡片 | 链接列表后面跟一行 `{.cards}` | [卡片](/zh/docs/components/cards/) | | 参数表 | 表格后面跟一行 `{.fields meta="type default"}` | [参数表](/zh/docs/components/fields/) | | 表格增强 | 表格后面跟一行 `{.matrix}`、`{caption="…"}` | [表格](/zh/docs/components/table/) | | 代码块 | 围栏信息行写 `{title="hugo.yml" copy=false}` | [代码块](/zh/docs/components/code/) | | 图片 | 独立成段的图片后面跟一行 `{caption="…" width="600"}` | [图片](/zh/docs/components/image/) | | 文件树 | `filetree` 围栏,每行一个 `- 名字/ # 注释` | [文件树](/zh/docs/components/filetree/) | | 公式 | `math` 围栏,或用 `$$` 包住的块级公式 | [公式](/zh/docs/components/math/) | | 图表 | `mermaid` 围栏(还有 `plantuml`、`markmap`、`echarts`) | [Mermaid](/zh/docs/components/mermaid/) | 剩下的少数组件(徽章、按键、引用文件、终端录像、Book 的图表式例)用 shortcode,语法与参数见[组件总览](/zh/docs/components/)。 组合例子:步骤里放代码围栏与提示块。 ````markdown {title="源码"} 1. 安装 Hugo Extended,最低 0.160.1: ```bash brew install hugo ``` 1. 克隆 OINK Starter 并预览: ```bash git clone https://github.com/pgsty/oink-starter my-docs cd my-docs && hugo server ``` > [!TIP] > 加 `-D` 连草稿一起预览。 {.steps} ```` 1. 安装 Hugo Extended,最低 0.160.1: ```bash brew install hugo ``` 1. 克隆 OINK Starter 并预览: ```bash git clone https://github.com/pgsty/oink-starter my-docs cd my-docs && hugo server ``` > [!TIP] > 加 `-D` 连草稿一起预览。 {.steps} ## 页尾的自动内容 {#page-end} 页面末尾的四块内容由主题按固定顺序生成,不必在正文里写: | 位置 | 是什么 | 默认 | 怎么改 | | --- | --- | --- | --- | | 1 | 反馈:「这页有帮助吗」两个按钮 | 关 | [仓库与页面信息](/zh/docs/customize/repository/) | | 2 | 最后修改:时间加最近一次提交的标题,链到 GitHub | 有 Git 信息时开 | [仓库与页面信息](/zh/docs/customize/repository/) | | 3 | 翻页器:上一页 / 下一页,顺序与侧栏树一致 | docs / book / blog 开 | [导航与菜单](/zh/docs/customize/navigation/) | | 4 | 评论:giscus | 配置完整且开启时 | [启用评论](/zh/docs/admin/comments/) | 标题旁边的操作菜单(复制 Markdown、编辑本页、查看历史、提 issue、打印)也是自动的,同样在[仓库与页面信息](/zh/docs/customize/repository/)里配置。 单页关闭其中某一块用 front matter:`feedback: false`、`annotation: false`、`pager: false`、`comments: false`。键的含义见[页面参数](/zh/docs/write/frontmatter/)。 ## 验证 {#verify} 写完一页,运行一次严格构建: ```bash hugo --printPathWarnings --panicOnWarning ``` - 输出必须以 `Total in …` 结束,没有 ERROR、没有 WARN。属性行写了不允许的键、组件参数非法、`ref` 目标不存在,都在这一步失败并指出文件与行号;主题不做静默降级。 - `--printPathWarnings` 报出两个页面指向同一输出路径的情况,多语言站或改过 `permalinks` 时较常出现。 在浏览器里确认三项: 1. 侧栏里出现了这一页,位置符合 `weight`; 2. 右栏目录列出了你写的 `##`,点击后 URL 里的锚点是英文; 3. 中英两个版本的同名标题锚点一致(本站有 `node scripts/check-doc-translations.mjs --public public` 做这项审计)。 ## 相关 {#related} - [组织内容](/zh/docs/write/organize/) — 目录结构如何决定侧栏 - [页面参数](/zh/docs/write/frontmatter/) — front matter 全表 - [组件总览](/zh/docs/components/) — 每个组件的语法与参数 - [多语言](/zh/docs/customize/i18n/) — 双语对等文件与缺译回退 - [本地预览](/zh/docs/admin/preview/) — `hugo server` 的常用开关 --- 反链: - [组织内容](/zh/book/02-structure/) - [文档](/zh/docs/) - [提示块](/zh/docs/components/callout/) - [多语言](/zh/docs/customize/i18n/) - [快速上手](/zh/docs/start/) - [从零建站](/zh/docs/start/from-scratch/) - [OINK Starter](/zh/docs/start/starter/) - [创作内容](/zh/docs/write/) - [博客与文章](/zh/docs/write/blog/) - [页面参数](/zh/docs/write/frontmatter/) - [API 文档](/zh/docs/write/openapi/) - [组织内容](/zh/docs/write/organize/) ================ Source: https://oink.pgsty.com/zh/docs/write/organize/index.md ================ # 组织内容 > 目录结构就是侧栏树:`_index.md` 与 weight、栏目首页样式、图标与折叠、隐藏页面、把文档放在任意路径。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- OINK 不需要单独配置导航:`content/` 下的目录结构就是侧栏树。本页覆盖目录与文件的摆放、栏目首页、排序、图标、折叠、隐藏,以及多根侧栏。 ## 目录就是侧栏 {#tree-is-sidebar} 一个目录是一个栏目(Hugo 称 section),目录里的 Markdown 文件是它的页面,嵌套目录是它的子栏目。侧栏按这棵树逐层渲染,顺序由 `weight` 决定,标签取 `linkTitle`,缺省时取 `title`。左侧这棵树的源码如下: ```filetree {title="content/docs/ 的前两层"} - content/ - docs/ - _index.zh.md # 栏目根:type: docs + cascade - about/ # 简介 {open=false} - _index.zh.md - features.zh.md - start/ # 快速上手 {open=false} - _index.zh.md - write/ # 创作内容(本栏目) - _index.zh.md # weight: 30 - pages.zh.md # weight: 10 - organize.zh.md # weight: 20 - frontmatter.zh.md # weight: 30 - components/ # 组件 {open=false} - _index.zh.md ``` ## 每个目录都要有 `_index.md` {#index-pages} 栏目首页是目录里的 `_index.md`(中文为 `_index.zh.md`)。缺少它时 Hugo 仍会生成栏目,但没有标题、描述、图标与 `weight`:侧栏那一行显示目录名,排序不受控制。 ```yaml {title="content/docs/deploy/_index.zh.md"} --- title: 部署上线 linkTitle: 部署 description: 把站点发布到 GitHub Pages、Cloudflare Pages 或自己的 Nginx。 weight: 50 icon: fa-solid fa-cloud-arrow-up --- ``` 栏目 `_index.md` 另有一项专属能力:用 `cascade` 把共享设置一次下推给整棵子树,不必每页重复。 ```yaml {title="content/docs/reference/_index.zh.md"} --- title: 参考 weight: 90 cascade: pager: false # 这个子树里的页面都不显示上一页 / 下一页 search_boost: 0.8 # 参考页在搜索里排后一点 --- ``` ## 排序:weight 用 10 的倍数 {#weight} 同一栏目里的页面按 `weight` 升序排列,`weight` 相同时才退回日期与 `linkTitle` 字母序。一律用 10 的倍数(10、20、30),此后往中间插页不必改动其它页。栏目自身的 `weight` 决定它在父级里的位置。 没写 `weight` 的页面视为 0,Hugo 把它们排在所有写了 `weight` 的页面之后,彼此按日期与标题排列。这个顺序会随内容改动漂移,因此每页都写上 `weight`。 ## 单文件还是页面包 {#bundles} 没有自身资源的页面用单文件 `slug.md`;带图片、cast、示例文件的页面改成目录加 `index.md`,资源与它同放。两种形态在侧栏里没有区别,URL 也相同。详见[编写页面](/zh/docs/write/pages/#new-page)。 ## 栏目首页显示子页列表还是卡片 {#section-index} `_index.md` 的正文之后,主题自动接上子页索引,两种样式: ```yaml {title="hugo.yml:全站默认"} params: ui: section_index: cards # list | cards ``` `list` 是主题默认,每个子页一行标题加描述;`cards` 是链接卡片网格,读取子页的 `icon`、`linkTitle` 与 `description`。本站用 `cards`,本栏目首页即是例子。单个栏目需要另一种样式时在它的 front matter 里覆盖: ```yaml {title="content/docs/reference/_index.zh.md"} section_index: list cascade: section_index: list # 连同后代栏目一起 ``` 两个页面级开关不受样式影响:`simple_list: true` 渲染紧凑的项目符号列表,`no_list: true` 不生成索引,用于正文自行手写导航的场合。 > [!TIP] > 卡片样式下 `description` 即卡片正文。描述控制在一句话、单行可显示。 ## 侧栏图标 {#icons} 在页面或栏目的 front matter 里写一对 Font Awesome class: ```yaml {title="content/docs/deploy/_index.zh.md"} icon: fa-solid fa-cloud-arrow-up ``` 图标密度是站点级策略,用于避免叶子页全部带图标: ```yaml {title="hugo.yml"} params: ui: sidebar_icon_policy: groups # all | groups | none ``` | 取值 | 效果 | | --- | --- | | `all` | 每个写了 `icon` 的条目都显示(未设置时的兼容默认值) | | `groups` | 只有根节点和有子页的节点显示图标,普通叶子页不显示 | | `none` | 侧栏不显示任何条目图标 | 新站点建议显式写 `groups`:保留分组的语义标识,去掉叶子层的图标。本站使用这个设置,左侧只有六个栏目带图标。 ## 展开与折叠 {#folding} 有子页的栏目在侧栏里带一个折叠箭头,读者的展开状态保存在本地。默认行为:当前页所在的那条路径展开,其余收起;博客类栏目默认展开。 ```yaml {title="content/docs/reference/_index.zh.md"} sidebar_expanded: true # 这个栏目始终默认展开 ``` 站点级的折叠、紧凑模式、初始展开层数、宽度与截断在[布局与页面类型](/zh/docs/customize/layout/)里配;键的完整定义见[配置总览](/zh/docs/customize/config/)。 ## 从侧栏里藏起来 {#hiding} | front matter | 效果 | | --- | --- | | `toc_hide: true` | 页面不出现在侧栏树里(页面本身照常发布,链接照常可用) | | `hide_summary: true` | 页面不出现在栏目首页的子页索引里 | | `sidebar_divider: true` | 这一项不再是链接,而是侧栏里的一条分组标题 | | `manual_link: https://…` | 侧栏这一行指向别处;配 `manual_link_title`、`manual_link_target: _blank` 用 | `toc_hide` 与 `hide_summary` 控制两个不同的入口,两处都不该出现时才同时设置。 ## 外壳由 `type` 决定,不是路径 {#type-and-shell} 文档外壳(侧栏、目录、面包屑、翻页器)不取决于目录名,只取决于页面的 `type` 是否在 `params.ui.shell_types` 里: ```yaml {title="hugo.yml:主题默认"} params: ui: shell_types: [docs, book, blog, swagger] ``` 文档因此可以放在任意路径,用 cascade 指定 `type` 即可。例如把一套手册放在 `content/handbook/`,栏目根的写法如下: ```yaml {title="content/handbook/_index.zh.md"} --- title: 运维手册 type: docs sidebar_root_for: self # 侧栏树的根即本栏目,不回退到 /docs cascade: type: docs # 整棵子树都用文档外壳 --- ``` > [!IMPORTANT] > 文档目录不叫 `docs` 时,`type: docs` 之外还要写 `sidebar_root_for: self`。否则侧栏会按 `params.ui.docs_section`(默认 `docs`)去找根,读者在 `/handbook/` 下却看到 `/docs/` 的树。 ## 多根侧栏 {#sidebar-roots} 侧栏树默认以读者所在的顶层栏目为根,树上方一行标出当前的根。规模较大的子树可以自己成为一个根,例如带版本的 API 参考或一本独立的手册: ```yaml {title="content/docs/api/v2/_index.zh.md"} --- title: API 参考 v2 sidebar_root_for: self # self | children --- ``` | 取值 | 语义 | | --- | --- | | `self` | 这个栏目的首页及其全部后代都以它为侧栏根 | | `children` | 首页仍留在父级树里,只有后代以它为根 | 根节点上方的切换器是全站的:它列出所有顶层栏目,加上站内所有 `sidebar_root_for: self` 的栏目。只有一个入口时它退化成一个普通链接,两个及以上才是下拉菜单。顶层栏目不出现在切换器里时,在它的 `_index.md` 写 `sidebar_root_menu: false`。 切换器下方,栏目首页仍是树里的第一个链接:切换器选择一棵树,根链接指向一篇文档。`sidebar_root_link_self: false` 让根那一行改为指向父级栏目。 ## 验证 {#verify} ```bash hugo --printPathWarnings --panicOnWarning ``` 必须 `Total in …`,没有 ERROR / WARN。`--printPathWarnings` 报出两个页面指向同一输出路径的情况,改目录结构时较常出现。 在浏览器里逐项确认: 1. 侧栏里的顺序与写下的 `weight` 一致,新栏目出现在预期位置; 2. 栏目首页的子页索引齐全(缺项来自 `hide_summary` 或缺少 `_index.zh.md`); 3. 面包屑与翻页器的顺序与侧栏一致,翻页器读的是同一棵树; 4. 换语言之后树的形状相同(每个 `_index.md` 都要有 `.zh.md` 对等文件)。 侧栏条目超过 `params.ui.sidebar_menu_truncate` 时构建给出警告,并指出应调到多少。这个警告不可忽略:被截断的条目不会出现在侧栏里。 ## 相关 {#related} - [编写页面](/zh/docs/write/pages/) — 单页怎么写 - [页面参数](/zh/docs/write/frontmatter/) — 这页出现的每个 front matter 键的完整定义 - [布局与页面类型](/zh/docs/customize/layout/) — 站点级的外壳、侧栏与目录设置 - [导航与菜单](/zh/docs/customize/navigation/) — 顶栏菜单、面包屑与翻页器 - [多语言](/zh/docs/customize/i18n/) — 双语目录树怎么保持一致 --- 反链: - [组织内容](/zh/book/02-structure/) - [文档](/zh/docs/) - [卡片](/zh/docs/components/cards/) - [文件树](/zh/docs/components/filetree/) - [配置总览](/zh/docs/customize/config/) - [布局与页面类型](/zh/docs/customize/layout/) - [导航与菜单](/zh/docs/customize/navigation/) - [打印支持](/zh/docs/customize/print/) - [仓库导览](/zh/docs/start/anatomy/) - [OINK Starter](/zh/docs/start/starter/) - [创作内容](/zh/docs/write/) - [博客与文章](/zh/docs/write/blog/) - [书籍出版](/zh/docs/write/book/) - [页面参数](/zh/docs/write/frontmatter/) - [编写页面](/zh/docs/write/pages/) ================ Source: https://oink.pgsty.com/zh/docs/write/frontmatter/index.md ================ # 页面参数 > front matter 全表:主题真正读取的每一个页面键,按侧栏、外壳、搜索、输出、页尾、Book、Landing、发布页分组。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- 本页是页面级参数的全表,只列 OINK 主题会读取的键。主题仅为提示「已重命名或已移除」而读取的旧键不在此列——它们在[迁移](/zh/docs/design/migration/)里,也不会出现在生成的编辑器 Schema 中。Hugo 自身的 front matter 字段(`slug`、`url`、`build`、`sitemap`、`expiryDate` 等)照常可用,语义见 [Hugo 文档](https://gohugo.io/content-management/front-matter/)。站点级参数(`hugo.yml` 里的 `params.*`)见[配置总览](/zh/docs/customize/config/)。 ## 表格说明 {#how-to-read} 优先级从高到低: 1. 页面自己的 front matter; 2. 最近一层 `cascade`(多层 cascade 都设了同一个键时,离页面最近的那一层生效); 3. `hugo.yml` 里的站点参数。 「默认」列标「站点值」的键,未写时回落到同名的站点参数。 页面键一律写在 front matter 顶层,键名是站点键去掉 `ui.` 前缀:站点的 `params.ui.section_index` 对应页面的 `section_index`。front matter 里不写 `ui:` 段,键一律在顶层。写在 `ui:` 段里的键不会被读取,也不会有任何提示——某个设置看着没生效时,先对照本页核一遍键名。 ```yaml {title="content/docs/wide-reference.zh.md"} --- title: 兼容性矩阵 weight: 40 page_width: wide footer_style: slim image_zoom: true section_index: list --- ``` 放进 `cascade` 时键名不变,多包一层: ```yaml {title="content/docs/reference/_index.zh.md"} cascade: pager: false section_index: list ``` 非法值不会中断构建。主题会发一条警告,指出键名、收到的值以及实际用了哪个回退值,然后按表里的默认值把这一页渲染出来——一个笔误只降级一个设置,而不是让 `hugo server` 下每个 URL 都返回 HTTP 500。它也不会因此混进线上:所有发布关卡都带 `--panicOnWarning` 构建,那条警告在真正要紧的地方仍然是硬失败。 没有任何 front matter 键会中断构建;主题的模板从不报错。当继续构建会发布出错误内容而不只是朴素内容时——比如残缺的上游署名,半条声明读起来和完整的一模一样——警告之后是整块略去,而不是回退。这里唯一会中断构建的属于 Hugo 而不是主题:解析不到目标的引用。 ## 基本 {#basic} | 键 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `title` | 字符串 | — | 页面大标题、浏览器标题、搜索结果标题。每页必写 | | `linkTitle` | 字符串 | `title` | 侧栏、面包屑、翻页器、卡片里的短名 | | `description` | 字符串 | — | 一句话摘要:栏目卡片、搜索摘要、`meta description`;博客页里渲染成正文上方的导语 | | `weight` | 整数 | `0` | 同级排序,用 10 的倍数;`0`(不写)排在所有写了 weight 的页面之后,见[组织内容](/zh/docs/write/organize/#weight) | | `draft` | 布尔 | `false` | 草稿不进构建产物,`hugo server -D` 可预览,见[编写页面](/zh/docs/write/pages/#drafts) | | `date` | 日期 | — | 博客日期、发布页排序依据;未来日期默认不构建 | | `lastmod` | 日期 | Git 提交时间 | 页尾「最后修改」;站点启用 `enableGitInfo` 时不必手写 | | `aliases` | 字符串数组 | — | 旧路径重定向到本页;用于页面迁移,不用于日常导航 | | `type` | 字符串 | 顶层目录名 | 决定模板与外壳:`docs` `book` `blog` `swagger`,见[组织内容](/zh/docs/write/organize/#type-and-shell) | | `layout` | 字符串 | — | 为单个页面指定布局:`landing`、`releases` | | `cascade` | 映射 | — | 把下面这些键下推给整棵子树 | {.fields meta="type default"} ## 侧栏与导航 {#navigation} 指南在[组织内容](/zh/docs/write/organize/)。 | 键 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `icon` | Font Awesome class 对 | — | 侧栏、栏目卡片与搜索结果的图标,例如 `fa-solid fa-rocket` | | `toc_hide` | 布尔 | `false` | 不出现在侧栏树里,也不进翻页序列 | | `hide_summary` | 布尔 | `false` | 不出现在栏目首页的子页索引里 | | `sidebar_divider` | 布尔 | `false` | 这一行渲染成侧栏分组标题:不是链接,也不进翻页序列 | | `sidebar_expanded` | 布尔 | blog 栏目 `true`,其余 `false` | 这个栏目在侧栏里默认展开 | | `sidebar_root_for` | `self` / `children` | — | 让这个栏目成为侧栏树的根;`self` 连同栏目首页,`children` 只管后代。其它取值告警并忽略 | | `sidebar_root_link_self` | 布尔 | `true` | 根那一行链接自身;`false` 改为链接父栏目。非布尔值告警并使用 `true` | | `sidebar_root_menu` | 布尔 | `true` | 顶层栏目是否出现在根切换器里 | | `toc_root` | 布尔 | `false` | 侧栏根是站点首页时,把这个顶层栏目整个排除在树与翻页序列之外 | | `manual_link` | URL | — | 侧栏与栏目索引里这一行指向别处 | | `manual_link_relref` | 内容引用 | — | 同上,但用 `relref` 解析;目标不存在时构建失败 | | `manual_link_title` | 字符串 | `title` | 手动链接的悬停标题 | | `manual_link_target` | 字符串 | — | 例如 `_blank`,主题自动补 `noopener` | | `no_list` | 布尔 | `false` | 栏目首页不生成子页索引 | | `simple_list` | 布尔 | `false` | 子页索引渲染成紧凑的项目符号列表 | | `section_index` | `list` / `cards` | 站点值(`list`) | 子页索引的样式。非法值告警并回退 | | `section_index_columns` | 整数 | `2` | 卡片样式的列数 | | `notoc` | 布尔 | `false` | 不显示右栏页面目录 | | `pager` | 布尔 | 由 `params.ui.pager_types` 决定 | `false` 关闭本页的上一页 / 下一页。非布尔告警并忽略该覆盖 | | `navbar_enabled` | 布尔 | 站点值(`true`) | 这一页是否渲染顶栏 | | `navbar_autohide` | 布尔 | 站点值(`false`) | 顶栏在指针设备上自动隐藏 | | `breadcrumb` | 布尔 | 按外壳决定 | 本页是否渲染面包屑;Docs/Book 默认开启,Blog 默认关闭 | | `theme_color` | 字符串 | 站点值 | `#rgb`/`#rrggbb` 十六进制色,为本页的强调底着色。写在分区根的 `cascade` 里就给整个分区一个身份 —— 见[品牌外观](/zh/docs/customize/brand/#theme-color) | | `theme_color_dark` | 字符串 | 派生 | 强调色的暗色一半。若上层 cascade 同时设了这个键,只覆盖 `theme_color` 的页面会继承那个暗色,所以要两个一起写。`theme_color: false` 可让页面整体退出继承的栏目色 | | `page_context_menu` | 布尔 | 站点值(`true`) | 标题行的页面操作菜单(复制 Markdown、编辑本页、打印……) | | `page_context_menu.assistant_links` | 布尔 | 站点值(`false`) | ChatGPT / Claude 交接项,写成 `page_context_menu: { assistant_links: false }`。页面只能收窄站点策略,不能单独开启 | {.fields meta="type default"} ## 页面外壳 {#shell} 站点级的默认值与效果说明在[布局与页面类型](/zh/docs/customize/layout/)。 | 键 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `page_width` | `normal` / `wide` / `full` | `normal` | 正文栏宽度。非法值告警并回退 | | `reading_width` | `slim` / `normal` / `wide` | `normal` | Book 页的阅读行宽,只对 `type: book` 生效 | | `footer_style` | `fat` / `slim` / `none` | 站点值(`fat`) | 页脚形态。非法值告警并回退 | | `body_class` | 字符串 | — | 追加到 `` 上的 class,供站点自己的 CSS 使用 | | `reading_time` | 布尔 | 站点值 | 本页是否显示阅读时长;写 `false` 关掉 | | `sidebar_enabled` | 布尔 | `true` | 这一页是否显示左侧栏;写 `false` 关掉 | | `scroll_spy` | 布尔 | 站点值 | 目录的滚动跟随;写 `true` 打开 | | `keyboard_nav` | 布尔 | 站点值(`true`) | 单键键盘导航,见[键盘导航](/zh/docs/customize/keyboard/)。非布尔告警并回退 | | `lastmod_commit` | `subject` / `hash` / `none` | `subject` | 「最后修改」后面怎么显示提交。非法值告警并回退 | | `sidebar_expand_levels`、`sidebar_menu_compact`、`sidebar_menu_foldable`、`sidebar_item_overflow` | 同站点参数 | 站点值 | 侧栏行为也可以逐页覆盖;取值见[配置总览](/zh/docs/customize/config/) | | `sidebar_width_min`、`sidebar_width_max` | 正整数 | 站点值(`220` / `480`) | 本页桌面侧栏拖拽宽度的上下限;下限大于上限时告警并恢复站点值 | | `code_copy` | 布尔 | 站点值(`true`) | 本页代码块复制控件的默认值;围栏显式 `copy=` 仍然优先 | | `toc_style` | `fixed` / `flow` | 站点值(`fixed`) | 固定右栏面板,或从内容流开始的较宽右栏 | | `toc_taxonomies` | 布尔 | 站点值(`true`) | 分类词云是否与页面目录共同进入右栏 | | `taxonomy_icons` | map | 站点值 | 为本页或分区 cascade 覆盖各分类法图标 | {.fields meta="type default"} ## 搜索 {#search} 指南在[全文检索](/zh/docs/customize/search/)。 | 键 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `search_keywords` | 字符串或字符串数组 | — | 附加检索词,包含中英文与同义词 | | `search_boost` | 正数 | `1.0` | 排序乘数,最终得分为文本匹配分乘以该值。非数字、非有限、零或负值告警并回退 `1.0` | | `search_exclude` | 布尔 | `false` | 不进本地索引 | {.fields meta="type default"} ## 输出形态 {#outputs} 指南在 [Agent 支持](/zh/docs/customize/agents/)(`.md` 与 `llms.txt`)与[打印支持](/zh/docs/customize/print/)。 | 键 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `outputs` | 字符串数组 | 站点 `outputs` | 这一页生成哪些输出格式;写 `[HTML]` 时不再生成 `.md` | | `no_print` | 布尔 | `false` | 不进入整章 / 整书的聚合打印输出 | {.fields meta="type default"} ## 页尾:评论、反馈与出处 {#page-end} 顺序固定为反馈 → 出处 → 翻页器 → 评论,见[编写页面](/zh/docs/write/pages/#page-end)。 | 键 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `comments` | 布尔 | 站点 `params.comments.enable`(`false`) | 本页是否显示 giscus 评论区,见[启用评论](/zh/docs/admin/comments/) | | `feedback` | 布尔或映射 | 站点 `params.ui.feedback`(关) | 映射形态支持 `enable` 与 `reasons`。其它写法告警并回退 | | `annotation` | 布尔 | 站点 `params.ui.annotation`(开) | 页尾的「最后修改 / 出处」区块。只接受布尔,其它写法告警并回退 | | `backlinks` | 布尔 | 站点 `params.ui.backlinks`(关) | 右栏目录旁是否显示「反链」组,列出链接到本页的页面,分区可以 cascade。只接受布尔,其它写法告警并回退,见[导航与菜单](/zh/docs/customize/navigation/#backlinks) | | `translation_notice` | 语言代码或 `false` | 站点 `params.ui.translation_notice`(关) | 权威版本的语言代码,译文据此显示一条指回原文的说明;本页即以本语言原创时写 `false` | {.fields meta="type default"} ### 上游出处 {#upstream} 页面改写自别处的材料时,用 `upstream_link` 声明来源,页尾出处行会给出作品、版权人、许可证与完整声明的链接。这一族键的解析顺序是站点参数 → `data/upstreams` 中由 `upstream_source` 指名的条目 → 本页 front matter,最具体的声明胜出。 `upstream_link` 只从 front matter 读取(cascade 有效,站点参数无效)——站点级的值会让每一页都声称同一个来源。没有 `upstream_link` 却写了任何一个同族键,告警并略去署名。 | 键 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `upstream_link` | URL | — | 本页据以改写的材料地址。写空串退出 cascade 继承来的值 | | `upstream_name` | 字符串 | — | 上游作品名,按上游自己的写法。设了 `upstream_link` 即必填 | | `upstream_copyright` | 字符串 | — | 版权声明,保留上游原文。必填 | | `upstream_license` | SPDX 标识 | — | 必须能在 `data/licenses` 中查到,否则告警并略去署名。必填 | | `upstream_notice` | 站内路径或 URL | — | 承载完整声明(许可证全文、免责声明、上游 NOTICE、快照版本)的页面。必填 | | `upstream_ref` | 字符串 | — | 快照对应的 tag 或 commit,显示在作品名后的括号里 | | `upstream_source` | 字符串 | 站点参数 | `data/upstreams` 中的条目名,用于集中声明多页共用的上游事实;条目不存在时告警并略去署名 | | `upstream_modified` | 布尔 | `false` | 把署名动词改成「改编自」,站点配了仓库信息时在同一句里带上「查看历史」链接——是一句话,不是多加一行。非布尔值告警并按未修改处理 | {.fields meta="type default"} 四个必填键(`upstream_name`、`upstream_copyright`、`upstream_license`、`upstream_notice`)缺一即告警并略去整条署名:残缺的署名比明显的缺失更糟。主题自带一份 SPDX 表 `data/licenses.yaml`,站点用同名文件补充或覆盖条目。 ## 图片缩放 {#image-zoom} | 键 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `image_zoom` | 布尔 | 站点值(`false`) | 本页的图片是否可点击放大,见[图片](/zh/docs/components/image/)。非布尔告警并回退 | {.fields meta="type default"} ## 博客与文章 {#blog} 指南在[博客与文章](/zh/docs/write/blog/)。 | 键 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `author` | 字符串 | — | 文章署名,支持行内 Markdown。页面写了 `authors` 时忽略它 | | `authors` | 字符串数组 | — | `authors` taxonomy 的 term,顺序即署名顺序,见[作者与署名](/zh/docs/write/blog/#authors)。需要在 `taxonomies:` 下声明 `author: authors` | | `series` | 字符串数组 | — | `series` taxonomy 的 term。正文上方的横幅取第一个,见[系列](/zh/docs/write/blog/#series) | | `series_weight` | 整数 | — | 在系列中的位置。带权重的成员按升序排在前,其余按日期升序跟在后 | | `tags` | 字符串数组 | — | 标签,见[分类体系](/zh/docs/customize/taxonomy/) | | `categories` | 字符串数组 | — | 分类,同上 | | `images` | 字符串数组 | — | 第一项作为文章封面与分享卡片;写进栏目 `_index.md` 的 `cascade` 即为栏目级默认。`images: []` 让这一页不继承 cascade 里的值,但不会屏蔽页面 bundle 里已有的 `featured`、`cover` 或 `thumbnail` 图片 | | `byline` | 字符串 | — | 解析到的题图实际渲染时显示的图片署名 | | `featured_image` | `none` / `banner` / `wash` / `hero` | 站点值(`none`) | 本文正文里怎么渲染自己的题图;`hero` 使用沉浸式通栏外壳。非法值告警并回退 | | `blog_index` | `list` / `cards` / `table` | 站点值(`list`) | 写在博客根目录上,决定该栏目索引形态;`table` 不分页,列出整个栏目。非法值告警并回退 | | `blog_index_columns` | 正整数 | 站点值(`3`) | 宽视口下的卡片列数;中等与窄视口仍保留响应式限制 | | `blog_index_size` | 正整数 | 站点值(`12`) | `list` 与 `cards` 每页文章数;`table` 始终列出整个栏目 | | `blog_index_toggle` | 布尔 | 站点值(`false`) | 同时发布三种索引形态,让读者切换;隐藏形态不加载图片 | | `share` | 字符串数组或 `false` | 站点 `params.ui.share`(空) | 页尾分享目标,整体替换继承来的列表;`false` 让本页退出,见[分享](/zh/docs/write/blog/#share)。未知目标告警并丢弃 | | `summary` | 字符串 | — | 标签 / 分类页上文章行的摘要回退来源,`description` 优先 | {.fields meta="type default"} ## Book {#book} 指南在[书籍出版](/zh/docs/write/book/)。整本书通过栏目 `cascade` 设 `type: book`。 | 键 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `book_number` | 字符串 | — | 章节编号,显示在页面标题与侧栏条目前面 | | `book_status` | `draft` | — | 标记草稿章节:侧栏与目录里带草稿标记,索引里默认不列 | | `sidebar_headings` | `false` / `true` / 2–4 的整数 | 站点值(`false`) | 在侧栏当前条目下展开 h2–h4 分支。超出范围告警并回退 | | `book_draft_banner` | 布尔 | 站点值(`false`) | 草稿章节正文开头加一条横幅。非布尔告警并回退 | {.fields meta="type default"} ## Landing {#landing} 指南在[首页与落地页](/zh/docs/customize/home/)。任意页面写 `layout: landing` 就用落地页外壳。 | 键 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `landing` | 字符串 | — | 数据取自 `data/landing//<语言>.yaml` | | `sections` | 数组 | — | 在 front matter 里内联分区定义,优先于 `landing`。不是数组时告警,不渲染任何分区 | {.fields meta="type default"} ## 发布页 {#releases} 指南在[发布与下载页](/zh/docs/write/releases/)。栏目写 `layout: releases` 后忽略 `weight`,按发布日期与 SemVer 倒序排列。 | 键 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `release_url` | 字符串 | — | 一个 GitHub 发布地址,`https://github.com///releases/tag/`。主题从中解析出项目、标签、日期与资产列表。其它写法告警并跳过发布区块 | {.fields meta="type default"} ## 相关 {#related} - [编写页面](/zh/docs/write/pages/) — 每页都要写的那几个键 - [组织内容](/zh/docs/write/organize/) — 侧栏与导航键的实际效果 - [配置总览](/zh/docs/customize/config/) — `hugo.yml` 里的站点参数全表 --- 反链: - [启用评论](/zh/docs/admin/comments/) - [排错与检查](/zh/docs/admin/troubleshooting/) - [卡片](/zh/docs/components/cards/) - [参数表](/zh/docs/components/fields/) - [引用](/zh/docs/components/include/) - [Agent 支持](/zh/docs/customize/agents/) - [配置总览](/zh/docs/customize/config/) - [布局与页面类型](/zh/docs/customize/layout/) - [仓库与页面信息](/zh/docs/customize/repository/) - [全文检索](/zh/docs/customize/search/) - [分类体系](/zh/docs/customize/taxonomy/) - [创作内容](/zh/docs/write/) - [博客与文章](/zh/docs/write/blog/) - [书籍出版](/zh/docs/write/book/) - [组织内容](/zh/docs/write/organize/) - [编写页面](/zh/docs/write/pages/) ================ Source: https://oink.pgsty.com/zh/docs/write/blog/index.md ================ # 博客与文章 > 开一个博客栏目:目录约定、文章的 front matter、封面图、按年份分组的列表页与 RSS。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- 博客文章与文档页的正文写法相同,区别在外壳:文章带日期、作者、标签与封面图,列表按年份倒序排列,栏目带 RSS。本页覆盖博客栏目的建立、文章 front matter、封面图、列表分页与 Feed。 ## 博客目录结构 {#layout} 博客是 `content/` 下的一个栏目,`type: blog` 使它使用博客外壳。子目录按发布方与受众划分,文章平铺其中。不要建年份目录,年份分组由列表页自动生成: ```filetree {title="本站的 content/blog/"} - content/ - blog/ - _index.md # type: blog + cascade - _index.zh.md - oink/ # 工程实践与公告 - _index.zh.md # cascade: images: [/images/oink.webp] - oink-announcement.md - oink-announcement.zh.md - release/ # 带版本号的发布注记 - _index.zh.md # cascade: images: [/images/releasenote.webp] - 0.4.0.md - 0.4.0.zh.md ``` 栏目根把类型下推给整棵子树,并设定该栏目共用的行为: ```yaml {title="content/blog/_index.zh.md"} --- title: 博客 description: OINK 工程实践与发布注记 type: blog icon: fa-solid fa-blog sidebar_root_for: self # 博客有自己的侧栏树 cascade: type: blog feedback: false # 文章不问「这页有帮助吗」 comments: true # 但开评论 --- ``` `params.ui.blog_section`(默认 `blog`)指明博客根的位置。目录另起名字时改这个参数,或按上面的写法用 `sidebar_root_for: self`。 侧栏里博客栏目默认展开,条目按日期倒序;给某篇文章写上 `weight` 会把它固定在最前。 ## 一篇文章的 front matter {#front-matter} ```yaml {title="content/blog/release/0.4.0.zh.md"} --- title: Oink 0.4.0 — 面向完整发布流程的场景组件体系 linkTitle: Oink v0.4.0 # 侧栏与翻页器里的短名 date: 2026-08-14 # 发布日期,决定排序与分组 lastmod: 2026-08-14 description: >- Oink 0.4.0 交付连续阅读与发布界面、可复用 Landing 页面、 带稳定引用的 Book 出版能力,以及键盘优先的站点外壳。 author: OINK 维护者 categories: [发布] tags: [Oink, Release] --- ``` 与文档页不同的几点: - `date` 必填。它决定文章在列表里的位置、年份分组与 RSS 时间。写在未来的日期默认不构建,`hugo server -F` 可以预览。 - `description` 渲染成正文上方的导语,不只是搜索摘要,因此写成给读者阅读的一句话。 - `author` 支持行内 Markdown,可以写成 `[Vonng](https://vonng.com)`。需要多位作者、头像或作者主页时,改用下面的 `authors` taxonomy;两者互不干扰,没写 `authors` 的文章照旧渲染 `author`。 - 日期显示格式由 `params.time_format_blog` 决定,可以按语言分别设置(本站英文是 `Monday, January 02, 2006`,中文是 `2006年1月2日`)。 双语文章成对存放,两种语言的 `date`、`author`、`weight`、`aliases` 保持一致;标题、描述、标签要翻译,提交 ID、版本号、命令和 URL 不翻译。 ## 封面图 {#featured-image} 列表页与标签页的每一行左侧有一张缩略图,按以下顺序解析,第一个命中的生效: 1. 文章 front matter 的 `images`,取第一项; 2. 页面包里文件名含 `featured` 的图片资源(会被裁切成缩略图,图片资源自己的 `byline` 会作为图注); 3. 从祖先栏目 `cascade` 继承来的 `images`,就近生效。 栏目级默认封面用 Hugo 原生的 `cascade` 覆盖整棵子树,本站两个子栏目各设一张: ```yaml {title="content/blog/release/_index.zh.md"} cascade: images: [/images/releasenote.webp] ``` 某一篇不要封面时,在它的 front matter 写 `images: []`;整个子栏目都不要,就把 `images: []` 写进那一层的 `cascade`。站点级的 `params.images` 不受影响 —— 它只做分享卡片,不会渲染成列表缩略图。 ### 渲染到文章正文里 {#featured-image-article} 默认情况下,解析出来的这张图只出现在列表行与社交卡片里,文章本身什么都不显示——手写一个题图,迟早会和卡片对不上。`params.ui.featured_image` 让主题用同一个解析结果把它渲染出来: | 模式 | 文章里显示什么 | | --- | --- | | `none` | 什么都不显示。主题默认值,所以今天不渲染题图的站点,升级后渲染出的字节完全一样 | | `banner` | 标题上方一张固定 16:9 的图,连着读一串文章时节奏统一 | | `wash` | 图铺在文章头部背后,只留十分之一的不透明度,在正文开始之前渐隐为无——文章从自己的主题里取到一点颜色,却不消耗任何对比度 | ```yaml {title="hugo.yml"} params: ui: featured_image: banner ``` 页面键是 `featured_image`,所以某个子栏目的 `cascade` 可以只为那棵树打开它,单篇文章也可以退出。没有题图的文章在两种模式下都不渲染任何东西——正因如此,一个题图有一搭没一搭的栏目也可以整体打开这个开关。两种模式都不引入脚本,也不增加打包成员。 ```yaml {title="content/blog/release/_index.md"} cascade: featured_image: wash ``` ## 列表页与分页 {#list} 栏目 `_index.md` 的正文之后,主题自动接上文章列表:按年份分组(「撰写于 2026」),年份倒序,每条显示标题、日期、所属子栏目、标签、缩略图与正文前 250 字的摘要。 分页用 Hugo 原生的分页器,默认每页 10 篇,在 `hugo.yml` 里调整: ```yaml {title="hugo.yml"} pagination: pagerSize: 20 ``` 取值与其余分页选项见 [Hugo 文档](https://gohugo.io/configuration/pagination/)。 ### 卡片形态 {#list-cards} `params.ui.blog_index: cards` 把同一份列表渲染成内容卡片网格而不是行列表:文章题图的 16:9 裁切在上,标题、日期与子栏目行居中,下面三行摘要。 ```yaml {title="hugo.yml"} params: ui: blog_index: cards blog_index_columns: 3 ``` 这个选择纯粹是呈现层面的——按年分组、分页与 `manual_link` 的行为完全一致,行列表那一路的输出一个字节都没变。列数只在 xl 断点以上生效;md 到 xl 之间恒为两列,md 以下一列。博客根目录的 front matter `blog_index` 或它的 `cascade` 可以按栏目设置。Term 页与 taxonomy 页保持行列表,读者侧没有在两种形态之间切换的开关。 卡片题图只要资源可处理就走 Hugo 的 `.Fill`,一屏卡片不会为此下载一堆原图。 ## RSS {#rss} 哪些页面产出 Feed 由 `outputs` 决定。给 `section` 加上 `RSS`,每个栏目就有自己的 Feed: ```yaml {title="hugo.yml"} outputs: home: [HTML, markdown, LLMS] page: [HTML, markdown] section: [HTML, RSS, print, markdown] ``` `outputs` 一旦写出就整体替换 Hugo 的默认值,`RSS` 必须显式写回。漏写等于关闭该类页面的 Feed,构建不会报错。 本站因此有 `/zh/blog/index.xml`(整个博客)与 `/zh/blog/release/index.xml`(只有发布注记)。栏目 Feed 递归包含所有子栏目的文章,订阅 `/zh/blog/` 即可收到全部。单篇文章没有自己的 `.xml`。 每种语言有各自的 Feed,地址是该语言路由加 `index.xml`。条数上限由 Hugo 的 `services.rss.limit` 控制。在博客根与它的一级子栏目页上,标题行右侧操作按钮的首位是 RSS 链接,读者不必手拼地址。 全站不需要 Feed 时用 `disableKinds` 关闭这一类输出,比逐个页面类型删除 `RSS` 更彻底: ```yaml {title="hugo.yml"} disableKinds: [RSS] ``` 组件在 Feed 里退化成静态形态:折叠块展开、交互控件去掉。四态输出的规则对博客与文档一致。 ## 分类与标签 {#taxonomy} `tags` 与 `categories` 是 Hugo 的分类体系,主题把它们渲染成文章头部的 chip、右栏的标签云和顶栏的筛选菜单。启用、双语标签与按内容类型开关见[分类体系](/zh/docs/customize/taxonomy/)。 ## 发布注记 {#release-notes} 带版本号的发布公告写成普通文章,惯例放在 `blog/release/` 下,`linkTitle` 带版本号(`Oink v0.4.0`)。需要发布卡片、资产表与校验和的下载页见[发布与下载页](/zh/docs/write/releases/)。 ## 文章里用组件 {#components} 提示块、标签页、代码块、图片、表格的用法与文档页相同,语法见[组件总览](/zh/docs/components/)。文章正文的标题同样写显式英文 `{#id}`。 文章末尾的反馈 / 最后修改 / 翻页器 / 评论四块与文档页一致,见[编写页面](/zh/docs/write/pages/#page-end)。博客通常关闭反馈、保留评论。 ## 作者与署名 {#authors} 声明这个 taxonomy 就是全部开关,主题不为此增加任何参数: ```yaml {title="hugo.yml"} taxonomies: category: categories tag: tags author: authors ``` 文章按顺序写出作者: ```yaml authors: [vonng, ada-example] ``` 文章头部就按这个顺序渲染头像与带链接的名字——front matter 里的序列既是集合也是顺序——列表行渲染名字,博客 feed 为每篇文章的每位作者发一条 ``,与站点级的 `managingEditor` 并存。名字之间用 CSS 的 gap 分隔而不是连接词,因为「和」是个逐语言的决定,而这里有 32 种语言。 作者主页就是 term 页本身,所以不存在另一份 `data/authors` 和它打架: ```markdown {title="content/authors/vonng/_index.md"} --- title: Vonng description: OINK 与 Pigsty 的维护者。 images: [portrait.webp] --- 正文是长介绍,渲染在主页上名字下方。 ``` 显示名取的是 term 页的链接标题——写了 `linkTitle` 就用它,否则用 `title`——所以主页可以挂全名、署名处用短昵称。`description` 是一句话介绍,正文是长介绍,头像则是题图解析器为这一页选中的那张——`images:` 与页面包里的肖像文件,走的是文章题图那套同样的规则。双语主页就是旁边一个 `_index.zh.md`。文章写了、但没人给它建主页的名字照样出署名:链接标题、一个首字母,以及指向归档页的链接。 0.4 的 `author:` 字符串在没有 `authors` 的地方原样保留,两种写法互不告警。 ## 系列 {#series} 系列是一条穿过若干篇各自独立成文的文章的阅读路径。编号、交叉引用与聚合输出属于[书籍](/zh/docs/write/book/),这里是更轻的那个东西。声明 taxonomy 同样就是全部开关: ```yaml {title="hugo.yml"} taxonomies: series: series ``` 文章写出系列名,也可以给自己定个位置: ```yaml series: [shell-internals] series_weight: 20 ``` 它的正文上方就会出现一条横幅,写明系列名、自己是第几篇、下一篇是哪篇,以及折在 `
` 里的完整列表——不用 JavaScript,也不增加打包成员。term 页 `content/series//_index.md` 是系列的引言,旁边放一个 `_index.zh.md` 就成双语。 阅读顺序由主题自己算,因为 term 页给不出这个顺序:Hugo 的 taxonomy weight 既到不了 `Page.Weight`,也进不了 `GroupByParam`。带权重的成员按 `series_weight` 升序排在前,其余按日期升序跟在后面,同序时用 `Path` 决胜。横幅与 term 页读同一个解析结果,所以它们不可能对「第二篇是哪篇」有分歧——这也意味着系列 term 页是由旧到新排列的,和其它所有 term 页相反。这正是这个功能本身。 一篇文章属于多个系列时只显示一条横幅,取它写在最前面的那个系列。只有一篇的系列不显示横幅。 `authors` 与 `series` 都不出现在文章的通用 taxonomy 标签行里,因为它们各自有专门的呈现面。想把某一个放回去,就在 `params.taxonomy.page_header` 里写上它的名字。 ## 分享 {#share} `params.ui.share` 在页尾最前面放一条分享栏。它默认为空,所以在站点写出目标之前什么都不渲染;写出来的顺序就是渲染顺序: ```yaml {title="hugo.yml"} params: ui: share: [x, bluesky, mastodon, reddit, hackernews, email, copy] ``` 可选的目标有十六个:`x`、`bluesky`、`mastodon`、`facebook`、`linkedin`、`reddit`、`hackernews`、`telegram`、`whatsapp`、`line`、`pinterest`、`weibo`、`chatgpt`、`claude`、`email`、`copy`。未知的名字告警并丢弃。Discord 是故意没有的:它根本没有公开的 share-intent URL,与其让主题去猜一个私有 scheme,不如用 `copy` 顶上。 页面键是 `share`,所以 `cascade` 可以把这条栏限定在一棵树里,页面自己的列表会整体替换继承来的那份,`share: false` 则让单页退出: ```yaml {title="content/blog/_index.md"} cascade: share: [x, bluesky, email, copy] ``` 只有普通页面渲染分享栏——列表页、term 页与首页没有「唯一被分享的那个东西」——打印、Markdown 与 RSS 一概不带。 **它不做什么**,才是它能出现在这个主题里的原因。没有分享计数、没有平台 SDK、没有 iframe、没有第三方脚本或样式表——而那三样正是这类组件通常的形态:每一页都向一家读者从未选择过的公司发一次请求。每个目标都是一个纯粹的 `` intent 链接,只带这一页自己的 permalink 与标题,不挂任何投放参数,另加一个本地复制按钮。站点构建时不取任何东西,页面加载时也不取;一次分享唯一可能引发的请求,就是读者点下去之后自己发起的那次跳转。把十六个目标全开的构建,不加 `--third-party` 也能通过 `bin/check-output-security.py`。 `chatgpt` 与 `claude` 是把同一个构建期 permalink 交给助手,附一句「请读这一页」。它们不是页面操作菜单里的「在 ChatGPT 中打开」/「在 Claude 中打开」——那两条由运行时在激活时改写成浏览器里的实时 URL,因此留在 `page_context_menu.assistant_links` 后面。 复制按钮就是内置的 `copy_link` 动作,也就是说不管有没有配分享栏,命令面板在每个站点的每一页上都带着它。 ## 验证 {#verify} ```bash hugo --printPathWarnings --panicOnWarning ``` 必须 `Total in …`,没有 ERROR / WARN。随后确认: 1. 文章出现在 `/zh/blog/` 的正确年份分组里,日期显示为中文格式; 2. `public/zh/blog/index.xml` 存在,里面有这篇文章,链接是完整的绝对地址; 3. 缩略图出现在列表里(缺失说明三条封面来源都没命中); 4. 标签 chip 能点进对应的标签页。 ## 相关 {#related} - [编写页面](/zh/docs/write/pages/) — 正文怎么写 - [页面参数](/zh/docs/write/frontmatter/) — `author`、`images` 等键的完整定义 - [组织内容](/zh/docs/write/organize/) — 目录与侧栏 - [分类体系](/zh/docs/customize/taxonomy/) — 标签与分类 - [发布与下载页](/zh/docs/write/releases/) — 版本卡片与资产表 --- 反链: - [配置总览](/zh/docs/customize/config/) - [仓库与页面信息](/zh/docs/customize/repository/) - [分类体系](/zh/docs/customize/taxonomy/) - [创作内容](/zh/docs/write/) - [页面参数](/zh/docs/write/frontmatter/) - [发布与下载页](/zh/docs/write/releases/) ================ Source: https://oink.pgsty.com/zh/docs/write/book/index.md ================ # 书籍出版 > 用 `type: book` 把一棵目录树变成一本书:章节编号、图表式例编号、交叉引用、生成式索引与整本打印。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- 一本书是一棵 `type: book` 的内容树:目录决定章节顺序,front matter 决定章节编号,图 / 表 / 式 / 例各带一个手写编号与稳定锚点。交叉引用在四种输出里都能解析,书根页面可以生成整本打印 HTML。 前提两条:站点的 `markup.goldmark` 已开启属性行与 passthrough(见[组件总览](/zh/docs/components/));`params.ui.shell_types` 保留 `book`(主题默认包含)。 ## 一本书的目录 {#layout} 书根是一个普通的 Hugo section,章是它的子目录,节是章里的页面。没有第二份章节清单:侧栏、翻页器、生成的目录读的都是这棵树。 ```filetree {title="content/handbook/ 一本书"} - content/handbook/ - _index.md # 书首页:type: book + cascade,放 book-toc 与各类索引 - ch01/ - _index.md # 第 1 章章首页:book_number: 1 - install.md # 1.x 节 - bootstrap.md - ch02/ - _index.md # 第 2 章:编号 2(book_number),草稿可标 draft - replication.md - failover.md - appendix.md # 不编号的附录,照样进侧栏与翻页顺序 ``` 章节编号手写:`book_number` 写什么就显示什么,主题不按目录顺序自动编号。图 / 表 / 式 / 例的 `num` 同理,是作者掌握的字符串(`2-1`、`5.3`、`A-2` 均合法),不是渲染时计算的序号。重排目录因此不会让已经印出去的编号漂移。 ## 书首页与章首页 {#front-matter} 书根声明类型、级联给后代,并显式请求 `print` 输出。这项聚合输出构建代价高,主题不替消费站开启: ```yaml {title="content/handbook/_index.md"} --- title: PostgreSQL 运维手册 type: book book_number: B cascade: type: book outputs: [HTML, print, markdown] --- ``` 分区书对应 Hugo 的 `section` 输出类型,书位于站点根时才用 `home`: ```yaml {title="hugo.yml"} outputs: section: [HTML, print, markdown] params: ui: sidebar_headings: 3 # 当前章节行下投射 h2–h3 标题树 book_draft_banner: true # 草稿章节页首多一条本地化提示 ``` 章首页只需要编号与顺序: ```yaml {title="content/handbook/ch02/_index.md"} --- title: 复制与故障切换 book_number: 2 book_status: draft weight: 20 --- ``` `book_number` 显示在页面标题、侧栏与生成目录里。`book_status: draft` 是可见的编辑状态标签,不改变 Hugo 的发布状态:草稿章节照常构建、照常发布。 `sidebar_headings` 接受 `false`、`true`(只到 h2)或 2–4 的最大层级。要被引用的标题一律写显式 ID,如 `## 同步复制 {#sync-replication}`:自动生成的 slug 适合导航,不适合作为长期引用目标。 配置键的完整定义在[配置总览](/zh/docs/customize/config/),页面参数在[页面参数](/zh/docs/write/frontmatter/)。 ## 编号:原生形态 {#numbering-native} 四种编号对象各有一种原生形态:一个 Markdown 块,紧跟其后一行属性行。属性行里 `num=` 是编号,`#id` 是锚点,`caption=` 是纯文本题注。 ### 图 {#figure} 图片块后面跟属性行。`#id` 省略时默认是 `fig-`。 ```markdown {title="源码"} ![OINK 发布注记页面](/images/releasenote.webp) {#book-release-note num="2-1" caption="发布注记页面同时是发布事实的唯一来源。" width=600 height=300} ``` ![OINK 发布注记页面](/images/releasenote.webp) {#book-release-note num="2-1" caption="发布注记页面同时是发布事实的唯一来源。" width=600 height=300} 原生图形态要求站点设置 `markup.goldmark.parser.wrapStandAloneImageWithinParagraph: false`,否则属性行会挂到段落上被忽略。替代文字取自 Markdown 图片本身,不会被题注替代。 ### 表 {#table} 管道表后面跟属性行,默认 ID 是 `tbl-`。 ```markdown {title="源码"} | 隔离级别 | 脏读 | 不可重复读 | 幻读 | | --- | --- | --- | --- | | Read Committed | 不可能 | 可能 | 可能 | | Repeatable Read | 不可能 | 不可能 | 可能 | | Serializable | 不可能 | 不可能 | 不可能 | {#tbl-2-1 num="2-1" caption="PostgreSQL 各隔离级别下的异常现象。"} ``` | 隔离级别 | 脏读 | 不可重复读 | 幻读 | | --- | --- | --- | --- | | Read Committed | 不可能 | 可能 | 可能 | | Repeatable Read | 不可能 | 不可能 | 可能 | | Serializable | 不可能 | 不可能 | 不可能 | {#tbl-2-1 num="2-1" caption="PostgreSQL 各隔离级别下的异常现象。"} ### 式 {#equation} `$$` 块后面跟属性行,默认 ID 是 `eq-`。编号与题注排在公式右侧的同一行里,不换行;题注写长了会挤压公式那一列,公式随之变成需要横向滚动的区域。公式的题注要短。 ```markdown {title="源码"} $$ A = \frac{\mathrm{MTBF}}{\mathrm{MTBF} + \mathrm{MTTR}} $$ {#eq-2-1 num="2-1" caption="可用性与平均故障间隔、平均恢复时间的关系。"} ``` $$ A = \frac{\mathrm{MTBF}}{\mathrm{MTBF} + \mathrm{MTTR}} $$ {#eq-2-1 num="2-1" caption="可用性与平均故障间隔、平均恢复时间的关系。"} 原生形态依赖站点开启 Goldmark passthrough。未开启时用下面的 `eq` shortcode,它走本地服务端 KaTeX。 ### 例 {#example} 代码围栏加 `num=` 与 `caption=` 即编号例,默认 ID 是 `eg-`。围栏里写的 `#id` 命名外层 `
`,即引用目标,不是代码块本身。例的题注必填:只写 caption 时 忽略它,只写编号时丢弃编号并告警;严格发布构建拒绝这条警告。编号例渲染成一个 整体:题注是框的表头,正文在框内;正文恰好是一个代码块时贴着框排,不再另画一圈边框。 ````markdown {title="源码"} ```sql {num="2-1" caption="按天统计主库写入量。" #eg-2-1} SELECT date_trunc('day', ts) AS day, count(*) FROM pg_stat_statements_history GROUP BY 1 ORDER BY 1 DESC LIMIT 7; ``` ```` ```sql {num="2-1" caption="按天统计主库写入量。" #eg-2-1} SELECT date_trunc('day', ts) AS day, count(*) FROM pg_stat_statements_history GROUP BY 1 ORDER BY 1 DESC LIMIT 7; ``` ## 编号:shortcode 形态 {#numbering-shortcodes} 四个 shortcode `fig` `tbl` `eq` `eg` 渲染出与原生形态一致的 `
`,注册到同一个目标表,按源码位置排序。仅在原生形态做不到时使用:图片要外链跳转、表格要在一个编号下放多张表、站点未开 passthrough、例子体是多个围栏加说明文字。 `fig` 用 `src=`(也接受内部 Markdown 内容,二者互斥),并额外支持 `link` `alt` `width` `height` `class` 与迁移用的 `title` 别名: ```markdown {title="源码"} {{< fig num="2-2" src="/images/docsy.webp" alt="Docsy 主题的默认外壳" caption="OINK 的上游:Docsy 的内容模型仍在下面。" width="600" height="300" />}} ``` **图 2-2.** OINK 的上游:Docsy 的内容模型仍在下面。 ![Docsy 主题的默认外壳](/images/docsy.webp) `tbl` 把标签、表格、题注与锚点包进一个语义 figure: ```markdown {title="源码"} {{< tbl num="2-2" caption="四种输出下编号组件的形态。" >}} | 输出 | 标签 | 锚点 | | --- | --- | --- | | HTML | 可见 | 稳定 | | 打印 | 可见 | 稳定 | {{< /tbl >}} ``` **表 2-2.** 四种输出下编号组件的形态。 | 输出 | 标签 | 锚点 | | --- | --- | --- | | HTML | 可见 | 稳定 | | 打印 | 可见 | 稳定 | `eq` 的内容交给本地服务端 KaTeX,因此不依赖 passthrough: ```markdown {title="源码"} {{< eq num="2-2" caption="连接池饱和度。" >}}U = \frac{\lambda}{\mu \cdot c}{{< /eq >}} ``` **公式 2-2.** 连接池饱和度。 $$ U = \frac{\lambda}{\mu \cdot c} $$ 不带参数的 `{{< eq >}}` 是无编号的块级公式兜底:不注册目标,不能被 `xref` 引用,也不出现在公式索引里。 `eg` 是包装型 shortcode,正文按页面的 Markdown 策略渲染,通常装一个或多个围栏: ````markdown {title="源码"} {{< eg num="2-2" caption="用 pg_basebackup 拉起一个新从库。" >}} ```bash pg_basebackup -h primary -U replicator -D /pg/data -Fp -Xs -P -R ``` {{< /eg >}} ```` **示例 2-2.** 用 pg\_basebackup 拉起一个新从库。 ```bash pg_basebackup -h primary -U replicator -D /pg/data -Fp -Xs -P -R ``` 同一页里 ID 必须唯一,同一类里一个编号也只能对应一个 ID。重复时告警并保留第一项; 严格发布构建拒绝这条警告,消息指出先占用它的那一处在哪行。 > [!IMPORTANT] shortcode 正文里不能写脚注 > Hugo 把 shortcode 的正文当作独立 Goldmark 文档渲染,脚注是页面级的。`tbl`、 > `eg`、`fig`、`card`、`tab`、`field`、`include` 的正文里出现 `[^label]` 会告警, > 消息给出文件、行号与标签;严格发布构建拒绝这条警告。定义写在页面上时该引用会 > 原样印出 `[^label]`,定义写在正文里则生成第二份脚注列表、`fn:N` 与页面自身 ID > 冲突——两种结果都不该发布。 > > 需要脚注的表格或代码块改用原生形态:表格、图片、围栏加 `{num=… caption=…}`,内容留在页面文档里,脚注照常编号、跳转与回链。渲染出来的图表与 shortcode 形态一致,所以这通常是一行改动。代码里形似脚注的文本(列表里的 `[^0-9]` 字符类、行内代码)不受影响。 ## 交叉引用 {#xref} 引用同页目标可以用普通 Markdown 链接:[表 2-1](#tbl-2-1) 指向上面那张隔离级别表。代价是标签与编号手写,改编号时需要自己检索。 `xref` 把标签、编号与锚点合成一处,并支持跨页与跨语言: ```markdown {title="源码"} 参见 {{< xref fig="2-2" />}} 与 {{< xref eg="2-1" />}}; 显式锚点:{{< xref fig="2-1" anchor="book-release-note" />}}。 ``` 参见 [图 2-2](#fig-2-2) 与 [示例 2-1](#eg-2-1); 显式锚点:[图 2-1](#book-release-note)。 规则: - 最多一个类型键(`fig` `tbl` `eq` `eg`)。类型提供本地化标签(图 / 表 / 公式 / 示例)并推导出默认锚点 `-`。 - `anchor=` 覆盖推导出的锚点,用于目标写了显式 `#id` 的情况。 - `page=` 跨页引用,走 Hugo 当前语言的页面查找,源码里不必硬编码 `/zh/` 前缀。 - 不给类型时必须同时给 `anchor=` 和内部链接文字:`{{< xref page="../ch01/install" anchor="sync-replication" >}}同步复制{{< /xref >}}`。 - 引用可以出现在目标之前,渲染时不读注册表,因此前向引用合法。 跨页的普通 Markdown 链接在整本打印里仍然是站点 URL。需要在聚合文档里也能跳转的引用写成 `xref`。 ## 索引:目录与图表清单 {#indexes} 五个索引 shortcode 遍历同一棵书树,触发后代内容并聚合注册结果。它们通常放在书首页(`_index.md`)或专门的「插图目录」页上。 ```markdown {title="content/handbook/_index.md"} {{< book-toc depth=3 >}} ## 插图目录 {#lof} {{< book-figures >}} ## 表格目录 {#lot} {{< book-tables >}} ## 公式索引 {#loe} {{< book-equations >}} ## 示例索引 {#lox} {{< book-examples >}} ``` 这五个 shortcode 在本页只给源码。它们从当前页所在的导航根向下遍历,放在一棵普通文档树里会把整棵 docs 树当作书列出。真实效果见[《使用 OINK 创作优美的内容》](/zh/book/),源码位于 [`content/book/_index.md`](https://github.com/pgsty/oink.pgsty.com/blob/main/content/book/_index.md)。 - `book-toc` 的 `depth` 取 1–3:1 列章,2 加入嵌套分区,3 再投射每页的标题树;`drafts=false` 只把 `book_status: draft` 的行从这份生成列表里滤掉,不影响页面发布。 - `book-figures` / `book-tables` / `book-equations` / `book-examples` 不接受任何参数,各列一类,条目形如「图 2-1 — 题注」并链到稳定 ID。 - 整本打印时,这些链接全部变成文档内片段。 ## 顺序阅读与草稿 {#reading} 翻页器默认对 `docs`、`book`、`blog` 三种类型开启,顺序是侧栏那棵树的前序遍历:分区首页在前,子页按 `weight`。关闭整类改 `params.ui.pager_types`,关闭单页写 `pager: false`。 ```yaml {title="hugo.yml"} params: ui: pager_types: [docs, book] ``` `toc_hide`、`manual_link` 纯链接占位、`sidebar_divider` 分隔行都不会成为翻页目的地。 草稿章节除了侧栏上的「草稿」标签,还可以开启页首横幅: ```yaml {title="hugo.yml"} params: ui: book_draft_banner: true ``` 横幅只在 `type: book` 且 `book_status: draft` 的页面出现,文案来自本地化键 `book_draft_notice`。 ## 打印整本 {#print} 书根有了 `print` 输出后,按可见的阅读顺序生成封面、本地目录、根页面正文与每个后代章节,全部装在一个 HTML 文档里。`no_print: true` 的页面、纯链接节点、分隔行与隐藏占位不会成为章节。 聚合文档里,编号组件的 ID 逐字节保留。页面内的 Markdown 标题与脚注 ID 会加上来源页面前缀,避免多章共有 `summary` 这类锚点、或都从 `fn:1` 开始时冲突; 生成的链接同步改写。页面单独渲染为 Print 时,与普通 HTML 保持相同的页面局部 ID——只有多页分区或整书聚合才增加命名空间。 产物是面向打印的 HTML。可选的 `BookManifest` 输出会把同一份阅读顺序记成 JSON, 主题另外提供 `bin/book-epub.py` 与 `bin/book-pdf.py`,把清单与打印 HTML 打包成 EPUB 和 PDF。 具体开关与整章打印见[打印支持](/zh/docs/customize/print/)。 ## 迁移既有书稿 {#migrate} 已有的中文书稿通常用站点自己的 `figure` shortcode、加粗的假题注、指向 `#fig_*` 的裸链接来表示图表编号。主题仓库带一个迁移脚本,把这些旧形态改写成 `fig`、`tbl` 与 `xref`,并保留原有的公开锚点。站点先固定到一个包含 Book 组件的已发布 OINK 版本,再迁移内容。 ```bash {title="干跑:只看 diff 与报告,不改文件"} python3 ~/pgsty/oink/bin/migrations/book_figures.py \ --profile tpme \ --root /path/to/your-book \ --report /tmp/book-migrate.json > /tmp/book-migrate.diff ``` 四个配方对应三份真实书稿的旧约定(DDIA 的 v1 与 v2 各一个),只识别在那些书稿里观测到的形态: | `--profile` | 识别的旧形态 | | --- | --- | | `tpme` | 假 h6 题注加相邻图片、题注加相邻表格、`/en/...#fragment` 裸链接 | | `ddia-v2` | 站点自有的 `figure` shortcode,按编号图 / 表 / 代码例分类 | | `ddia-v1` | 裸图片加相邻的一条加粗编号题注,ID 由图片文件名推导 | | `pg-internal` | 加粗或斜体的中英文「图 N」题注紧邻一张图片,编号表题注紧邻一张表格 | | 选项 | 作用 | | --- | --- | | `--profile` | 必填,取上表四个值之一 | | `--root` | 必填,消费站仓库根目录 | | `--path` | 限定 `--root` 下的文件或目录,可重复;默认扫描整棵内容树 | | `--write` | 应用改写。默认是干跑,不写任何文件 | | `--no-diff` | 不打印 diff,仍输出摘要与报告 | | `--report` | 写出机器可读的 JSON 报告 | {.fields} diff 走标准输出,摘要走标准错误,报告含 `files_scanned`、`files_changed`、`counts`、`skipped`、`idempotent` 五项。脚本只改写能唯一确定的目标:无法确定编号、题注不唯一、标记形态不认识的地方原样保留,逐条记进 `skipped` 供人工处理。旧题注里的粗体、行内代码与公式会降级为纯文本,因为 Book 的题注契约是纯文本。 审阅 diff 之后在专用分支上应用,再运行第二遍确认幂等: ```bash {title="应用并验证幂等"} python3 ~/pgsty/oink/bin/migrations/book_figures.py \ --profile tpme --root /path/to/your-book --write \ --report /tmp/book-migrate-written.json python3 ~/pgsty/oink/bin/migrations/book_figures.py \ --profile tpme --root /path/to/your-book --no-diff \ --report /tmp/book-migrate-second.json ``` 第二份报告应当是 `files_changed: 0`、`counts` 为空、`idempotent: true`;脚本以退出码 0 表示幂等。 配方只识别这三份书稿里实际观测到的旧形态;书稿的旧约定不在这四个配方之内时,脚本不适用,需要按[编号:原生形态](#numbering-native)手工改写。主题仓库的 `bin/check-book-migrations.py` 用干跑与幂等两项检查覆盖这四个配方。 ## 验证 {#verify} 1. 构建零告警:`hugo --printPathWarnings --panicOnWarning`。编号写错、ID 重复、题注缺失都在这一步失败。 2. 页面上应看到「图 2-1」这样的本地化标签、可点的 `xref` 链接,以及点击后正确跳转的锚点。 3. 对比侧栏、翻页器、`book-toc` 与整本打印四处的章节顺序是否一致。 4. 检查 Markdown 输出:`curl -s http://localhost:1313/zh/handbook/ch02/index.md`。shortcode 形态应退化成 `**图 2-2.** 题注` 加原始正文,原生形态原样保留源码块与属性行。 5. 从主题仓库对构建产物跑一遍锚点检查: ```bash python3 ~/pgsty/oink/bin/check-book.py --site-public public ``` 它校验每个引用的目标锚点存在、类型与编号匹配、页内 ID 唯一,以及编号图片有与题注相称的替代文字。 ## Book shortcode 参数 {#reference} | 参数 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `num` | 字符串 | — | 必填(`eq` 无参形态除外)。匹配 `[0-9A-Za-z.-]+`,要加引号 | | `id` | 字符串 | `fig-` / `tbl-` / `eq-` / `eg-` | 匹配 `[A-Za-z][A-Za-z0-9_.:-]*`,逐字节保留 | | `caption` | 纯文本 | 空 | `eg` 必填;`fig` `tbl` `eq` 可选。不是 Markdown | | `class` | class token | — | 追加到 `
`;需要 `num` | | `src` | 图片路径 | — | 仅 `fig`。与内部内容互斥,走共享图片解析顺序 | | `link` `alt` `width` `height` | — | — | 仅 `fig`。宽高是正整数 | | `title` | 纯文本 | — | 仅 `fig`。`caption` 的迁移别名,二者互斥 | {.fields meta="type default"} `xref`: | 参数 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `fig` `tbl` `eq` `eg` | 编号字符串 | — | 至多一个。提供本地化标签并推导锚点 | | `anchor` | ID | 由类型与编号推导 | 无类型时必填,且必须有内部链接文字 | | `page` | 页面引用 | 当前页 | 走当前语言的页面查找,找不到时告警并渲染无链接文字 | {.fields meta="type default"} `book-toc`: | 参数 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `depth` | 整数 1–3 | `2` | 1 章 / 2 含嵌套分区 / 3 含标题树 | | `drafts` | 布尔 | `true` | `false` 时从生成列表里滤掉草稿章节 | {.fields meta="type default"} `book-figures`、`book-tables`、`book-equations`、`book-examples` 不接受任何参数。 ## 限制与常见问题 {#limits} - 没有自动编号。章节号、图号、表号都手写;改编号是一次有意的编辑,不是构建的副作用。 - 属性行必须紧贴块,中间不能有空行。被 Prettier 之类工具移动过的属性行静默失效,图退化成普通图片。 - `book_kind` 与 `book_part` 是契约认可的元数据键,当前主题模板不渲染它们;有视觉效果的是 `book_number` 与 `book_status`。 - 索引 shortcode 会触发后代内容渲染,在超大树上明显拉长构建时间。整本 `print` 需要显式开启也是同一原因。 - shortcode 正文里不能出现脚注引用;出现时告警并指出改用原生形态,严格发布构建 拒绝这条警告,见上文[编号:shortcode 形态](#numbering-shortcodes)。 - 打包是可选的,且在构建之外运行。`BookManifest` 加上 `bin/book-epub.py` / `bin/book-pdf.py` 可以产出 EPUB 与 PDF,但没有任何一次 Hugo 构建会自己生成这两个文件;专业排版的分页、字体嵌入与索引编制仍在契约之外。 ## 相关 {#related} - [组织内容](/zh/docs/write/organize/) — 目录树怎么变成侧栏与阅读顺序 - [图片](/zh/docs/components/image/) — 图注、尺寸、缩放与图片处理 - [表格](/zh/docs/components/table/) — 表格属性行与全宽表 - [公式](/zh/docs/components/math/) — KaTeX 与 passthrough 配置 - [打印支持](/zh/docs/customize/print/) — 整章与整本打印 --- 反链: - [DDIA](/zh/case/ddia/) - [pgint.vonng.com](/zh/case/pg-internal/) - [TPME](/zh/case/tpme/) - [亮点特性](/zh/docs/about/features/) - [代码块](/zh/docs/components/code/) - [Draw.io](/zh/docs/components/drawio/) - [图片](/zh/docs/components/image/) - [公式](/zh/docs/components/math/) - [表格](/zh/docs/components/table/) - [配置总览](/zh/docs/customize/config/) - [打印支持](/zh/docs/customize/print/) - [消费站证据](/zh/docs/design/research/consumer-evidence/) - [创作内容](/zh/docs/write/) - [博客与文章](/zh/docs/write/blog/) - [页面参数](/zh/docs/write/frontmatter/) ================ Source: https://oink.pgsty.com/zh/docs/write/releases/index.md ================ # 发布与下载页 > 把版本号、标签、归档链接、校验和与安装命令写成本地事实,再让发布卡片、资产表、下载区块和索引页从同一份记录推导出来。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- OINK 把发布事实集中在两处本地数据:页面 front matter 的 `release_url` 指明这一页对应哪个 GitHub 发布,`data/download/.yaml` 记录安装方式。发布卡片、资产表、下载区块与索引页都从这两处推导。构建期不访问 GitHub,也不声称某个标签或资产已经存在。 > [!NOTE] 本页自带演示用的发布事实 > front matter 里放了一个 `release_url`(OINK v0.4.0),下面的卡片、资产表与下载区块都是真实渲染。校验和与资产文件名是构造的:URL 由组件按仓库与标签本地推导,指向的文件在真实发布里不存在,不要用这里的哈希校验产物。 ## 组件与事实来源 {#overview} | 你要的 | 用什么 | 事实来自 | | --- | --- | --- | | 版本摘要卡片(标签、日期、归档、仓库) | `release-card` | 页面的 `release_url` | | 校验和资产表 | `checksums` 围栏 / `release-assets` | 正文里的 `sha*sum` 行 | | 多渠道下载区块 | `download` | `data/download/.yaml` | | 按时间排序的发布索引页 | `layout: releases` | 各页的 `release_url`,没有则用标题 | ## 页面拥有发布事实 {#release-facts} 发布页 front matter 里的一个键就是全部记录——精确到标签的 GitHub 发布 URL: ```yaml {title="content/blog/release/0.4.0.zh.md"} release_url: https://github.com/pgsty/oink/releases/tag/v0.4.0 ``` owner、项目名与标签从 URL 里解析出来,日期用页面自己的 `date`。不是精确 标签形式的 GitHub 发布 URL 会警告并跳过发布区块——`--panicOnWarning` 构建 随之失败。0.5 的 `release` 映射(product / version / repo / tag / date / prev / checksums)及其字符串简写已移除;仍携带它的页面会收到指名 `release_url` 的警告。 在需要摘要的位置放一个不带参数的 shortcode,调用里不接受任何事实: ```markdown {title="源码"} {{< release-card >}} ``` **v0\.4\.0 · 0001-01-01** - [查看发布](https://github.com/pgsty/oink/releases/tag/v0.4.0) - [源码 · tar\.gz](https://github.com/pgsty/oink/archive/refs/tags/v0.4.0.tar.gz) - [源码 · zip](https://github.com/pgsty/oink/archive/refs/tags/v0.4.0.zip) - [pgsty\/oink](https://github.com/pgsty/oink) 卡片带着仅凭 URL 就能推导的四个链接——发布页、两种源码归档、仓库——全部本地推导。校验和文件放在正文下方的资产表里,版本对比在 GitHub 上看。 ## 发布索引页 {#release-index} 一个分区可以改用发布索引布局。它列出小节里的每一个常规页面,从新到旧 ——按页面日期排序,同一天内以标签里的版本号决胜(SemVer 优先级,非 SemVer 标签用确定的字典序兜底): ```yaml {title="content/blog/release/_index.zh.md"} --- title: 版本发布 layout: releases --- ``` `release_url` 可解析的条目读作「项目名 + 标签」——如 `oink v0.4.0`——下一行 是页面描述;没有它的页面保留自己的标题,版本之间夹一篇普通短文是合法条目, 不是警告。0.5 的 `release_products` 过滤与 `release_group_by_product` 分组 已移除;写了会警告。 本站的[版本发布](/zh/blog/release/)目前用普通博客列表。需要严格时间序时改用 `layout: releases`。 ## 校验和资产 {#assets} `checksums` 围栏是校验和表的原生形态,围栏里写 `sha*sum` 命令的原样输出: ````markdown {title="源码"} ```checksums 1e2f4c8a9d05b7361f8ac25d0e7b4913a6c8df215047eb9c3a1d6b8250f9e7c4 oink-0.4.0-linux-amd64.tar.gz 7b3d9e0c145a8f26d0b7e93c48156aa2f0d9c7b31e846a5029df1b6c7a3e8250 *oink-0.4.0-darwin-arm64.tar.gz ``` ```` ```checksums 1e2f4c8a9d05b7361f8ac25d0e7b4913a6c8df215047eb9c3a1d6b8250f9e7c4 oink-0.4.0-linux-amd64.tar.gz 7b3d9e0c145a8f26d0b7e93c48156aa2f0d9c7b31e846a5029df1b6c7a3e8250 *oink-0.4.0-darwin-arm64.tar.gz ``` 只接受两种行:`<十六进制><两个空格><文件名>` 与 `<十六进制><空格>*<文件名>`。空行 与以 `#` 开头的行忽略。哈希长度决定算法(MD5 / SHA-1 / SHA-256 / SHA-512),一个块 里只能有一种算法。格式错误的行带行号告警并跳过;严格发布构建拒绝这条警告。文件名 必须是单个路径段。类型、操作系统与架构徽章由文件名推断,属于装饰,推断不出时不显示。 资产链接的基址:页面有 `release_url` front matter 时推导为 `https://github.com//releases/download//`;没有发布事实的页面必须显式写 `base=`。两者同时存在时报错。 ````markdown {title="没有 release front matter 的页面"} ```checksums {base="https://repo.pigsty.io/oink/v0.4.0/" algo="sha256"} 1e2f4c8a9d05b7361f8ac25d0e7b4913a6c8df215047eb9c3a1d6b8250f9e7c4 oink-0.4.0-linux-amd64.tar.gz ``` ```` `release-assets` 是同一个解析器与渲染器的 shortcode 形态。它多一个围栏没有的 `src=`,可以把校验和文件本身提交为页面资源或全局资产(`src` 与围栏内容互斥);`group="auto"` 按平台与架构分组: ```markdown {title="源码"} {{< release-assets group="auto" >}} 5a0c7d1e93b4826f0ad35c9e17b6402d8f1c95ae63d70b28c4e19a5f38207db6 oink-0.4.0-1.el9.x86_64.rpm c93f16a8d052b7e41ac68d3907b25fe0a41d8c7362b95e0187ac4d63f9520ea8 oink-0.4.0-1.el9.aarch64.rpm {{< /release-assets >}} ``` ### `.rpm` | 文件 | 校验和 | | --- | --- | | [oink\-0\.4\.0\-1\.el9\.x86\_64\.rpm](https://github.com/pgsty/oink/releases/download/v0.4.0/oink-0.4.0-1.el9.x86_64.rpm) | SHA-256 · `5a0c7d1e93b4826f0ad35c9e17b6402d8f1c95ae63d70b28c4e19a5f38207db6` | | [oink\-0\.4\.0\-1\.el9\.aarch64\.rpm](https://github.com/pgsty/oink/releases/download/v0.4.0/oink-0.4.0-1.el9.aarch64.rpm) | SHA-256 · `c93f16a8d052b7e41ac68d3907b25fe0a41d8c7362b95e0187ac4d63f9520ea8` | HTML 里哈希截断显示,完整哈希保留在无障碍名称与复制源里,复制按钮由按需加载的本地运行时提供。禁用 JavaScript 时仍是一张完整的带链接表格。打印展开完整哈希且不带控件,Markdown 与 RSS 是完整哈希的管道表。 ## 下载渠道数据 {#download-data} 安装方式属于产品,不属于某一次发布,因此存放在 `data/download/.yaml`。本站真实的记录是 `data/download/prd5.yaml`: ```yaml {title="data/download/prd5.yaml"} version: 0.4.0 repo: pgsty/oink published: true channels: - id: script kind: rolling title: Install script title_zh: 安装脚本 icon: fa-solid fa-bolt note: The rolling channel deliberately contains no version interpolation. note_zh: 滚动渠道刻意不插入版本号。 steps: - title: Install title_zh: 安装 code: curl -fsSL https://repo.example.org/oink/install | bash lang: bash - id: source kind: pinned title: Source archive title_zh: 源码归档 icon: fa-solid fa-code-branch url: https://github.com/pgsty/oink/archive/refs/tags/${tag}.tar.gz steps: - title: Clone the tag title_zh: 克隆标签 code: git clone --branch ${tag} https://github.com/pgsty/oink.git lang: bash - id: assets kind: pinned title: Release assets title_zh: 发布资产 icon: fa-solid fa-box-open checksums: | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa oink-0.4.0.tar.gz ``` 记录级字段只有 `version` `repo` `tag` `published` `channels` 五个。多写一个键时告警并 跳过记录,严格发布构建拒绝这条警告。`version` 也可以不写在这里,改由站点的 `params.version` 提供。 | 字段 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `version` | 字符串 | 站点 `params.version` | 两处都没有时告警并跳过区块 | | `repo` | `owner/name` | — | 固定版本渠道有链接或资产时必填 | | `tag` | 字符串 | `v{version}` | 只允许 URL 安全字符 | | `published` | 布尔 | `true` | `false` 表示不可变发布还不存在 | | `channels` | 数组 | — | 非空 | {.fields meta="type default"} 每个渠道: | 字段 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `id` | `^[a-z][a-z0-9-]*$` | — | 记录内唯一,用作锚点 | | `kind` | `rolling` \| `pinned` | — | 决定能不能插值版本事实 | | `title` | 本地化字符串 | — | 必须能解析出非空值 | | `note` | 本地化字符串 | — | 渠道下方的一行说明 | | `icon` | Font Awesome class 对 | — | 例如 `fa-solid fa-bolt` | | `url` | http(s) 或站内路径 | — | 仅 `pinned` 可插值 | | `steps[]` | `title` / `code` / `lang` | `lang: text` | 代码步骤走 OINK 的增强代码渲染器 | | `checksums` | `sha*sum` 文本 | — | 仅 `pinned`;与 `checksums_src` 互斥 | | `checksums_src` | 资产路径 | — | 把校验和文件当作 Hugo 资产读入 | {.fields meta="type default"} 两条规则: - 本地化按后缀解析:`<字段>_<精确语言>` → `<字段>_<主语言>` → `<字段>`。中文站解析 `title_zh_cn`、`title_zh`、`title`。不接受 camelCase 别名。 - 只有固定版本渠道的 `url` 与 `steps[].code` 能插值 `${version}` 与 `${tag}`。滚动渠道拒绝插值,避免稳定版安装命令被绑定到某个版本。标题与说明不插值。 ## 渲染下载区块 {#download-shortcode} `download` 接受恰好一个位置参数,即数据键: ```markdown {title="源码"} {{< download "prd5" >}} ``` ## 安装脚本 滚动渠道刻意不插入版本号。 **安装** ```bash curl -fsSL https://repo.example.org/oink/install | bash ``` ## 源码归档 [源码归档](https://github.com/pgsty/oink/archive/refs/tags/v0.4.0.tar.gz) **克隆标签** ```bash git clone --branch v0.4.0 https://github.com/pgsty/oink.git ``` ## 发布资产 | 文件 | 校验和 | | --- | --- | | [oink\-0\.4\.0\.tar\.gz](https://github.com/pgsty/oink/releases/download/v0.4.0/oink-0.4.0.tar.gz) | SHA-256 · `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa` | HTML 渲染一排锚点 chip 加各渠道分区,代码步骤复用增强代码块与按需加载的复制运行时,校验和渠道复用上面那张资产表。打印静态展开同样的内容,Markdown 输出标题、源码围栏与完整哈希,RSS 不输出这个组件。 标签未打、资产未上传时,把记录标为未发布: ```yaml {title="data/download/.yaml"} published: false ``` 滚动渠道照常可用。固定版本渠道变成不可点击的「待发布」状态,省略固定版本命令,禁用资产链接与复制控件。标签与资产可解析之后再翻转这个开关,不要先在正文里写入推测出来的链接。 同一份记录也能被 Landing 页面的 `download` 分区消费,不需要第二套版本模型,见[首页与落地页](/zh/docs/customize/home/)。 ## 与博客发布注记的关系 {#release-notes} 两者分工: - 博客里的发布注记(本站在 `content/blog/release/`)是叙事:这一版改了什么、怎么升级、有什么破坏性变更。它的 front matter 里带 `release_url`,页首可以放一张 `release-card`。写法见[博客与文章](/zh/docs/write/blog/)。 - 下载数据是操作:选哪个渠道、运行哪条命令、校验哪个哈希。它与版本号解耦,升级时只改一处。 一次发布的顺序:更新 `data/download/.yaml` 的 `version` → 新写一篇 `content/blog/release/.md` 并填 `release_url` → 标签与资产就绪后把 `published` 翻成 `true`。 ## 验证 {#verify} 1. 构建零告警:`hugo --printPathWarnings --panicOnWarning`。哈希行格式、算法混用、缺 `base`、渠道字段拼错都在这一步失败。 2. 页面上:卡片显示的标签与日期与仓库一致;资产表每行都能点开真实的下载 URL。 3. 逐条核对哈希与实际产物:组件只负责排版,不验证内容。 4. 检查非 HTML 输出里哈希是完整的: ```bash curl -s http://localhost:1313/zh/docs/write/releases/index.md | grep -c '^| ' ``` 5. 发布前先用 `published: false` 走一遍,标签与资产确实存在后再改成 `true`;每种语言、子路径部署各测一次。 ## 相关 {#related} - [博客与文章](/zh/docs/write/blog/) — 发布注记住在哪、怎么排 - [代码块](/zh/docs/components/code/) — 下载步骤里的代码渲染与复制 - [首页与落地页](/zh/docs/customize/home/) — Landing 的 `download` 分区 - [配置总览](/zh/docs/customize/config/) — `params.version` 与相关站点参数 - [版本升级](/zh/docs/admin/upgrade/) — 消费站怎么跟随主题版本 --- 反链: - [pgsty.pro](/zh/case/pgsty-pro/) - [sow.pgsty.com](/zh/case/sow/) - [亮点特性](/zh/docs/about/features/) - [ECharts](/zh/docs/components/echarts/) - [首页与落地页](/zh/docs/customize/home/) - [多版本](/zh/docs/customize/versions/) - [创作内容](/zh/docs/write/) - [博客与文章](/zh/docs/write/blog/) - [页面参数](/zh/docs/write/frontmatter/) ================ Source: https://oink.pgsty.com/zh/docs/write/openapi/index.md ================ # API 文档 > 把 OpenAPI 规范放进站点,用随主题分发的 Swagger UI 或 Redoc 渲染成可浏览的接口文档,不连 CDN。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- 一页接口文档由一份 OpenAPI 规范加一个 shortcode 构成。Swagger UI 与 Redoc 两个运行时随主题分发(版本分别是 5.32.13 与 2.5.3,见仓库 `VENDOR.json`),只有用到它们的页面、且只在 HTML 输出里加载,构建与浏览都不访问外部服务。Swagger UI 的在线 validator 已写死关闭(`validatorUrl: null`),已发布的接口页面不会把规范地址发往任何地方。 三个步骤:把规范文件放进 `static/`,新建一页写上 shortcode,需要专用外壳时把页面 `type` 改成 `swagger`。 ## 规范文件的位置 {#spec-file} 规范文件放在 `static/` 下,原样发布到站点根,两个 shortcode 得到的都是浏览器可取的 URL: ```filetree {title="规范文件的位置"} - static/ - openapi/ - docs-demo.yaml # 发布为 /openapi/docs-demo.yaml - content/ - docs/ - write/ - openapi.zh.md # 这一页 ``` 不要把规范文件放在页面旁边。`redoc` 会在内容目录里查找同名文件并据此拼出 URL,但内容目录里的 `.yaml` 是页面资源,Hugo 只在它被引用或处理时才发布。`redoc` 只拼 URL、不引用资源,浏览器因此得到 404。 远程规范(`https://…` 开头)两个 shortcode 都接受,但那是一项网络依赖,还会把读者的元数据暴露给那台主机。内网部署与有 CSP 的站点应当使用同源规范。只接受 `http` 与 `https`:其它 scheme、协议相对的 `//host` 或空值都会告警,shortcode 不渲染。 下面的例子用真实存在的 `/openapi/docs-demo.yaml`,一份演示用的集群管理 API,没有可访问的服务端。 ## Swagger UI {#swaggerui} `swagger` 只有一个具名参数 `src`,值是从站点根开始的 URL。它经过主题的 URL 校验,子路径部署同样正确: ```markdown {title="源码"} {{< swagger src="/openapi/docs-demo.yaml" >}} ``` 它渲染一个 `class="td-swagger-ui"` 的容器,规范地址放在 `data-td-spec-url` 上;页面上所有容器由一个可缓存的 `js/chunks/swagger-init.js` 统一挂载。容器 ID 由页面地址与 shortcode 序号推导(`td-swagger--`),因此同一页可以放多个。 本页只给源码,不真渲染 Swagger UI:它自己生成的标记有 axe WCAG AA 违规(服务器下拉框没有可访问名称、版本号区域是不能聚焦的可滚动区),本站的无障碍门禁要求每个页面零违规。下面的 Redoc 是真渲染的——但要知道两个控件都被排除在那道门禁之外,因为 Redoc 的接口描述文字自身有对比度缺陷。两者都不是完全无障碍的界面,见[限制](#limits)。 ## Redoc {#redoc} `redoc` 只接受一个位置参数,即规范路径。多写一个参数会告警,shortcode 不渲染。 ```markdown {title="源码"} {{< redoc "openapi/docs-demo.yaml" >}} ``` [OpenAPI 规格文件](https://oink.pgsty.com/openapi/docs-demo.yaml) 路径解析按顺序有三条分支:`http` 开头视为远程 URL;能在内容目录里找到同名文件时用 `baseURL + 页面目录 + 文件名`;否则用 `baseURL + 原样路径`。`redoc` 的路径因此不要以斜杠开头,`/openapi/…` 会拼出 `https://example.com//openapi/…` 这样的双斜杠。与 `swagger` 不同,它生成基于 `baseURL` 的绝对 URL。 主题固定了 `hide-hostname` `hide-logo` `suppress-warnings` `lazy-rendering` `native-scrollbars` 五个属性,并用 CSS 隐藏 Redocly 品牌图标。Redoc 的其余属性目前不开放给作者,需要它们时在站点里覆盖 `layouts/_shortcodes/redoc.html`。 ## 专用页面外壳 {#shell} 接口文档页通常较宽较长,可以用 `swagger` 页面类型: ```yaml {title="content/api/_index.md"} --- title: 集群管理 API type: swagger page_width: wide cascade: type: swagger --- ``` `swagger` 是主题默认的外壳类型之一(`params.ui.shell_types` 默认是 `[docs, book, blog, swagger]`,站点覆盖这个列表时需要保留它)。它与 `docs` 外壳的差别只有两处:`` 上多一个 `td-swagger` class 供样式挂钩,以及不显示版本横幅。侧栏、目录、面包屑、翻页器与页尾都照常。 外壳与页宽的完整说明见[布局与页面类型](/zh/docs/customize/layout/)。 ## 输出形态 {#outputs} | 输出 | 呈现 | | --- | --- | | HTML | 完整的交互式 Swagger UI / Redoc;运行时按需加载,本地文件,无 CDN,且只在这一种输出里 | | 打印 | 一行带标题的静态链接,规范地址可见;两套运行时都不加载 | | Markdown | 一个纯 Markdown 链接 `[OpenAPI 规格文件](/openapi/example.yaml)`,不会退化成接口清单 | | RSS | 同样的纯链接 | 在 HTML 之外,接口文档是一个指路牌而不是一份参考。要让打印或 Agent 输出里也有接口信息,在同一页用正文写关键端点的说明;shortcode 之外的正文在四种输出里都完整保留。 ## 限制与常见问题 {#limits} - 两个组件的容器 ID 都按「页面地址 + shortcode 序号」推导,同一页放多个互不冲突。 - 两者可以同页共存,但页面会很长,HTML 输出也会同时加载两套运行时。正式站点选一个。 - 两个界面都不是完全无障碍的,且都来自主题不改写的上游产物。Swagger UI 的标记有 axe WCAG AA 违规(`select-name`、`scrollable-region-focusable`);Redoc 的接口描述文字不满足 AA 对比度。本站因此把 `.td-swagger-ui` 与 `.td-redoc` 排除在零违规门禁之外——有同类门禁的站点只能照做,并且应当明说,而不是默认其中某一个能过。 - `redoc` 不接受额外属性参数:写第二个位置参数会告警,shortcode 不渲染。 - `redoc` 路径不要以 `/` 开头,否则拼出双斜杠。 - 规范文件必须能被浏览器取到:放 `static/`,构建后确认 `public/` 下存在该文件。 - 没有服务端 mock:Swagger UI 的 "Try it out" 会向 `servers` 里写的地址发起真实请求,示例规范里的地址不可访问。 ## 验证 {#verify} 1. 构建零告警:`hugo --printPathWarnings --panicOnWarning`。 2. 规范确实发布了:`ls public/openapi/docs-demo.yaml`,或访问 `http://localhost:1313/openapi/docs-demo.yaml`。 3. 页面上能展开端点、看到 schema;浏览器控制台没有 404 或跨域报错。 4. 断网后再刷新一次:运行时是本地的,规范同源时界面应照常出现。 ## 相关 {#related} - [编写页面](/zh/docs/write/pages/) — 页面 front matter 与正文的基本写法 - [布局与页面类型](/zh/docs/customize/layout/) — `shell_types`、页宽与侧栏 - [Agent 支持](/zh/docs/customize/agents/) — 为什么只在 HTML 里可交互的组件要配文字说明 - [代码块](/zh/docs/components/code/) — 用请求 / 响应示例代替整套 UI 的轻量做法 --- 反链: - [亮点特性](/zh/docs/about/features/) - [发布上线](/zh/docs/admin/deploy/) - [布局与页面类型](/zh/docs/customize/layout/) - [打印支持](/zh/docs/customize/print/) - [创作内容](/zh/docs/write/) ================ Source: https://oink.pgsty.com/zh/docs/components/index.md ================ # 组件总览 > 写文档时可用的全部组件,一个组件一页,例子由浅入深,参数表在页尾。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- 这一栏回答一个问题:某个组件在 Markdown 里怎么写。每页的顺序相同:最简例子、逐步深入的例子、输出形态、参数表、限制。查语法见下面的速查表。 ## 两种形态 {#two-forms} 组件的第一形态是 Markdown 语法本身:块引用、列表、表格、图片、围栏,加上紧跟其后的一行 `{…}` 属性。原生形态在 GitHub 与任意 Markdown 编辑器中仍然可读,Markdown 输出保留的也是源码。 原生形态表达不了的场景使用 shortcode:正文标签页、带块级描述的参数表、带图标与徽章的卡片、终端录像。规则有五条: - 所有 shortcode 都写 `{{< 名字 >}}`,只有 `{{% steps %}}` 用 `%` 分隔符,因为它的正文是页面级 Markdown。 - 嵌套名字(`tab`、`card`、`field`)只在各自的父 shortcode 里有效。 - 作者参数写错不会静悄悄降级。普通预览会发出带源码位置的警告,并采用文档规定的 回退或略去不安全部分;发布构建带 `--panicOnWarning` 时,那条警告会让门禁失败。 - 公开字符串参数(图注、标签、标题)一律是纯文本,不解析 Markdown。只有正文是 Markdown:`tab`、`card`、`field` 的正文,`include` 引入的文件,以及 Book 的 `fig`、`tbl`、`eg` 正文。 - 页面没用到的组件不下发运行时。HTML 只引用这一页真正需要的稳定能力分片,打印、 Markdown 与 RSS 不加载交互运行时。 ## 站点前置配置 {#prerequisites} 组件依赖三项 Goldmark 设置。OINK Starter 已经配好;从零建站时照抄以下片段: ```yaml {title="hugo.yml"} markup: goldmark: renderer: unsafe: true # 内容里的 HTML 不被剥掉 parser: attribute: block: true # 启用 {…} 属性行 wrapStandAloneImageWithinParagraph: false # 独立图片不再包进

``` - `renderer.unsafe: true`:Goldmark 默认丢弃内容里的原始 HTML,关闭时组件正文里嵌套的 HTML 会消失。 - `parser.attribute.block: true`:属性行的总开关。关闭时 `{.steps}`、`{caption="…"}` 只是正文里的一行字符串。 - `parser.wrapStandAloneImageWithinParagraph: false`:独立成段的图片不再包进 `

`,图片才能成为带图注的 figure,属性行才跟得上去。 个别组件另有前置条件:公式需要开启 Goldmark 的 passthrough,PlantUML 与 Draw.io 需要自建渲染服务,各页分别说明。完整的配置键见[配置总览](/zh/docs/customize/config/)。 ## 速查表 {#cheatsheet} 「形态」列的取值:原生 = Markdown 语法加属性行;围栏 = 带语言标记的代码围栏;shortcode = `{{< … >}}`。「运行时」列说明这个组件是否往页面上下发 JavaScript。 | 组件 | 一句话 | 最短写法 | 形态 | 运行时 | | --- | --- | --- | --- | --- | | [提示块](/zh/docs/components/callout/) | 把前提、警告与折叠说明从正文中分离 | `> [!NOTE]` | 原生 | 无 | | [图片](/zh/docs/components/image/) | 图注、尺寸、缩放、编号与构建期图片处理 | `![说明](oink.webp)` | 原生 | 需站点开关 | | [代码块](/zh/docs/components/code/) | 高亮、标题、复制、折叠、行链接 | ```` ```sh ```` | 围栏 | 按页加载 | | [标签页](/zh/docs/components/tabs/) | 同一件事的多个平台或语言版本 | 属性行 `{tab="Linux"}` | 原生 + shortcode | 按页加载 | | [表格](/zh/docs/components/table/) | 普通表格,加满宽、矩阵、标题与编号 | `{.full-width}` | 原生 | 无 | | [参数表](/zh/docs/components/fields/) | 参数清单,带类型 / 必填 / 默认值芯片 | `{.fields meta="type default"}` | 原生 + shortcode | 无 | | [步骤](/zh/docs/components/steps/) | 有先后的流程 | `{.steps}` | 原生 + shortcode | 无 | | [卡片](/zh/docs/components/cards/) | 一组并列的去处 | `{.cards}` | 原生 + shortcode | 无 | | [文件树](/zh/docs/components/filetree/) | 目录结构与对齐的注释列 | ```` ```filetree ```` | 围栏 | 按页加载 | | [公式](/zh/docs/components/math/) | KaTeX 行内与块级公式 | `$$ … $$` | 原生 | 按页加载 | | [Mermaid](/zh/docs/components/mermaid/) | 流程图、时序图、甘特图 | ```` ```mermaid ```` | 围栏 | 按页加载 | | [PlantUML](/zh/docs/components/plantuml/) | UML 图;需要自建渲染服务 | ```` ```plantuml ```` | 围栏 | 需站点开关 | | [思维导图](/zh/docs/components/markmap/) | Markdown 列表变成思维导图 | ```` ```markmap ```` | 围栏 | 需站点开关 | | [Draw.io](/zh/docs/components/drawio/) | 可回编辑的图;需要自建服务 | `![说明](arch.drawio.svg)` | 原生 | 需站点开关 | | [ECharts](/zh/docs/components/echarts/) | 声明式数据图表 | ```` ```echarts ```` | 围栏 | 按页加载 | | [Infographic](/zh/docs/components/infographic/) | AntV 信息图 | ```` ```infographic ```` | 围栏 | 按页加载 | | [画廊](/zh/docs/components/gallery/) | 一组图片共用一个缩放对话框 | ```` ```gallery ```` | 围栏 | 需站点开关 | | [徽章](/zh/docs/components/badge/) | 行内状态标记 | `{{< badge text="Beta" >}}` | shortcode | 无 | | [按键](/zh/docs/components/kbd/) | 键位与组合键 | `{{< kbd "Ctrl" "K" >}}` | shortcode | 无 | | [引用](/zh/docs/components/include/) | 引入文件、插入站点参数、构建期注释 | `{{< include file="parts/x.md" >}}` | shortcode | 无 | | [Asciinema](/zh/docs/components/asciinema/) | 终端录像 | `{{< asciinema file="images/x.cast" >}}` | shortcode | 按页加载 | 「运行时」列的四条细则: - 代码块只在块上有复制或折叠按钮时加载 `code-block.js`;文件树只在树带注释列时加载 `filetree.js`,它负责拖动那条分栏线。 - 图片与画廊共用一个缩放对话框运行时,需要站点开启 `ui.image_zoom`,且页面上确有候选图。 - 公式在构建期由 KaTeX 渲染成 HTML 与 MathML,页面上只多一份 KaTeX 样式表与字体,没有脚本。 - Draw.io 只在渲染内容含 PNG 或 SVG 候选图的页面加载,并且每个不同的图片 URL 只检查一次。 每个组件在 HTML、打印、Markdown、RSS 四种输出下都有确定形态,见各页的「输出形态」一节。 --- 本节页面: - [提示块](/zh/docs/components/callout/): 用 `> [!NOTE]` 这样的块引用写出带颜色、图标与标题的提示、警告与折叠块,不需要短代码。 - [图片](/zh/docs/components/image/): 用普通 Markdown 图片语法写图,加一行属性就得到图注、尺寸、缩放、链接、编号与 Hugo 图片处理。 - [代码块](/zh/docs/components/code/): 普通 Markdown 围栏加一行属性,就得到文件名标题、精确复制、行号、高亮、换行、折叠与可链接的行。 - [标签页](/zh/docs/components/tabs/): 给相邻的围栏或表格加一个 `{tab=}` 属性就得到标签页;加上 group 之后可分享链接、跨组同步、记住读者的选择。 - [表格](/zh/docs/components/table/): 普通 GFM 表格加一行属性,就得到标题、兼容矩阵、参数表、编号表或标签页;宽表格自己横向滚动,不撑宽页面。 - [参数表](/zh/docs/components/fields/): 用一张普通表格加 `{.fields}` 记录配置项、命令参数与 API 字段:名称、类型、默认值、说明各就各位,窄屏不挤,每条都能单独链接。 - [步骤](/zh/docs/components/steps/): 有序列表加 `{.steps}` 就是带编号圆点与竖线的操作步骤;步骤要带标题、要进目录时改用 steps shortcode。 - [卡片](/zh/docs/components/cards/): 用带 `{.cards}` 的链接列表排出导航卡片网格;需要图标、徽章、图片时改用 shortcode。 - [文件树](/zh/docs/components/filetree/): 用 `filetree` 围栏画带注释的目录结构:对齐的注释列、逐条目图标、可折叠目录、可拖动的分栏。 - [公式](/zh/docs/components/math/): 用 KaTeX 写行内与块级数学公式,构建期渲染完毕,读者不下载任何脚本。 - [Mermaid](/zh/docs/components/mermaid/): 用 `mermaid` 围栏把文本写成流程图、时序图、甘特图、类图与状态图,本地渲染、跟随深浅色、diff 友好。 - [PlantUML](/zh/docs/components/plantuml/): 用 `plantuml` 围栏写时序图、类图、组件图、活动图与用例图;渲染必须由你自己配置一个 PlantUML 服务。 - [思维导图](/zh/docs/components/markmap/): 用 `markmap` 围栏把一段 Markdown 大纲变成可展开、可缩放的思维导图,源码本身就是能读的提纲。 - [Draw.io](/zh/docs/components/drawio/): 把带着可编辑副本的 `.drawio.svg` 当普通图片放进页面,读者鼠标移上去就能点开 Draw.io 编辑器改图。 - [ECharts](/zh/docs/components/echarts/): 在 `echarts` 围栏里用 YAML 或 JSON 写图表选项,Hugo 构建期校验,浏览器用本地 ECharts 画出跟随深浅色的统计图。 - [Infographic](/zh/docs/components/infographic/): 用 `infographic` 围栏挑一个 AntV 模板,把标题与条目渲染成流程、时间线、漏斗、网格或层级信息图。 - [画廊](/zh/docs/components/gallery/): 用 `gallery` 围栏把一组相关截图排成响应式网格,每张可带说明或链接,并复用页面的图片缩放对话框。 - [徽章](/zh/docs/components/badge/): 在功能名、版本号或表格单元格旁边放一枚语义状态标签,五种 tone,不需要自定义颜色。 - [按键](/zh/docs/components/kbd/): 用 `kbd` 写快捷键:一个 shortcode 接一串按键名,输出语义化的按键序列,打印与 Markdown 输出里同样可读。 - [引用](/zh/docs/components/include/): 用 include 插入外部文件,用 param 插入站点参数,用 comment 写不会出现在任何输出里的注释。 - [Asciinema](/zh/docs/components/asciinema/): 把 .cast 终端录像放进页面:文字仍然是可选中的文字,播放器随主题分发,不连 CDN。 --- 反链: - [OINK 实现预览](/zh/blog/oink/oink-announcement/) - [Oink v0.2.0](/zh/blog/release/0.2.0/) - [Oink v0.3.0](/zh/blog/release/0.3.0/) - [组合页面](/zh/book/03-compose/) - [OINK 文档](/zh/case/oink/) - [文档](/zh/docs/) - [简介](/zh/docs/about/) - [亮点特性](/zh/docs/about/features/) - [版本升级](/zh/docs/admin/upgrade/) - [组件](/zh/docs/design/components/) - [快速上手](/zh/docs/start/) - [创作内容](/zh/docs/write/) - [博客与文章](/zh/docs/write/blog/) - [书籍出版](/zh/docs/write/book/) - [编写页面](/zh/docs/write/pages/) ================ Source: https://oink.pgsty.com/zh/docs/components/callout/index.md ================ # 提示块 > 用 `> [!NOTE]` 这样的块引用写出带颜色、图标与标题的提示、警告与折叠块,不需要短代码。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- 提示块(Callout)是 GitHub / Obsidian 风格的块引用:`> [!TYPE]` 起头,正文跟在后面。用于把提示、警告、前提条件从正文中分离出来;正文一句话能说清的内容不必使用提示块。 ## 最简例子 {#minimal} ```markdown {title="源码"} > [!NOTE] > Hugo Module 需要本机安装 Go;只用离线归档时不需要。 ``` > [!NOTE] > Hugo Module 需要本机安装 Go;只用离线归档时不需要。 不写标题时使用本地化的类型名(中文站显示「注意」,英文站显示 "Note")。源码在 GitHub 上按 GitHub 的提示块渲染,在普通 Markdown 阅读器中显示为块引用,内容都不会丢失。 ## 十种类型 {#types} 前五种与 GitHub 一致,后五种是 OINK 追加的语义类型。每种类型有默认图标与强调色。 ```markdown {title="源码"} > [!TIP] > 用 `hugo server -D` 可以预览草稿。 > [!IMPORTANT] > 主题下限是 Hugo Extended 0.160.1,低于它构建直接失败。 > [!WARNING] > `hugo --cleanDestinationDir` 会清空 `public/`。 > [!CAUTION] > 删除 `resources/_gen` 后第一次构建会慢很多。 > [!SUCCESS] > 构建通过、零告警——可以推上线了。 > [!DANGER] > 不要把 `go.work` 提交进仓库。 > [!QUESTION] > 站点要不要开评论?看[启用评论](/zh/docs/admin/comments/)。 > [!EXAMPLE] > `pgsty.com` 就是一个只用了提示块与表格的纯文档站。 > [!QUOTE] > Documentation is a love letter that you write to your future self. ``` > [!TIP] > 用 `hugo server -D` 可以预览草稿。 > [!IMPORTANT] > 主题下限是 Hugo Extended 0.160.1,低于它构建直接失败。 > [!WARNING] > `hugo --cleanDestinationDir` 会清空 `public/`。 > [!CAUTION] > 删除 `resources/_gen` 后第一次构建会慢很多。 > [!SUCCESS] > 构建通过、零告警——可以推上线了。 > [!DANGER] > 不要把 `go.work` 提交进仓库。 > [!QUESTION] > 站点要不要开评论?看[启用评论](/zh/docs/admin/comments/)。 > [!EXAMPLE] > `pgsty.com` 就是一个只用了提示块与表格的纯文档站。 > [!QUOTE] > Documentation is a love letter that you write to your future self. 类型名不区分大小写。 ## 自定义标题 {#title} 标记同一行的后续文字是标题,支持行内 Markdown(代码、粗体、链接)。 ```markdown {title="源码"} > [!WARNING] 会改写 `public/` > 生产构建前先确认 `baseURL` 指向正式域名,否则所有绝对链接都会指错。 ``` > [!WARNING] 会改写 `public/` > 生产构建前先确认 `baseURL` 指向正式域名,否则所有绝对链接都会指错。 ## 正文内容 {#body} 正文是页面级 Markdown:列表、代码围栏、表格、图片、嵌套的提示块。每一行都以 `>` 开头,围栏也不例外。 ````markdown {title="源码"} > [!TIP] 三条命令启动预览 > > 1. 克隆:`git clone https://github.com/pgsty/oink-starter my-docs` > 2. 进入目录并预览: > ```bash > cd my-docs && hugo server > ``` > 3. 打开 > > | 端口 | 用途 | > | --- | --- | > | 1313 | Hugo 开发服务器 | ```` > [!TIP] 三条命令启动预览 > > 1. 克隆:`git clone https://github.com/pgsty/oink-starter my-docs` > 2. 进入目录并预览: > ```bash > cd my-docs && hugo server > ``` > 3. 打开 > > | 端口 | 用途 | > | --- | --- | > | 1313 | Hugo 开发服务器 | ## 折叠 {#collapsible} 类型后加 `-` 默认收起,加 `+` 默认展开;两者都渲染为原生 `

`,不加载 JavaScript。适用于完整输出、备选方案、背景说明这类不必默认展示的内容。 ```markdown {title="源码"} > [!NOTE]- 为什么需要 Go? > Hugo 通过 Go 的模块系统下载主题(`hugo mod get`)。用 submodule 或离线归档时可以不装 Go。 > [!TIP]+ 默认展开,但读者可以收起 > 收起状态不会被记住,刷新后回到默认。 ``` > [!NOTE]- 为什么需要 Go? > Hugo 通过 Go 的模块系统下载主题(`hugo mod get`)。用 submodule 或离线归档时可以不装 Go。 > [!TIP]+ 默认展开,但读者可以收起 > 收起状态不会被记住,刷新后回到默认。 ## 中性折叠块 DETAILS {#details} `[!DETAILS]` 是没有语义颜色的折叠块:不加符号默认收起,`[!DETAILS]+` 默认展开。用于冗长输出、完整配置文件等需要折叠的内容。 ````markdown {title="源码"} > [!DETAILS] 完整的 `hugo version` 输出 > ```text > hugo v0.165.0+extended+withdeploy darwin/arm64 > ``` ```` > [!DETAILS] 完整的 `hugo version` 输出 > ```text > hugo v0.165.0+extended+withdeploy darwin/arm64 > ``` ## 自定义图标 {#icon} 块引用结束后的下一行写属性 `{icon="fa-solid fa-xxx"}`(一对 Font Awesome class),替换该类型的默认图标。属性行紧接块引用,中间不能有空行。 ```markdown {title="源码"} > [!TIP] PostgreSQL 18 已支持 > 从 Pigsty v4 起默认安装 PostgreSQL 18。 {icon="fa-solid fa-database"} ``` > [!TIP] PostgreSQL 18 已支持 > 从 Pigsty v4 起默认安装 PostgreSQL 18。 {icon="fa-solid fa-database"} ## 嵌套 {#nesting} 提示块可以嵌套(每层多一个 `>`),也可以放在列表项或步骤中。建议最多嵌套一层。 ```markdown {title="源码"} > [!WARNING] 升级前先备份 > 升级主题版本可能改变渲染结果。 > > > [!TIP]- 怎么备份 > > `git tag pre-upgrade` 就够了——回滚只是 `git checkout pre-upgrade`。 ``` > [!WARNING] 升级前先备份 > 升级主题版本可能改变渲染结果。 > > > [!TIP]- 怎么备份 > > `git tag pre-upgrade` 就够了——回滚只是 `git checkout pre-upgrade`。 ## 未知类型与易错写法 {#pitfalls} 未知的类型名不会导致构建失败,也不会丢失内容:该块渲染为普通块引用,`[!TYPE]` 标记原样可见。 ```markdown {title="源码"} > [!NOTICE] 这不是合法类型 > 标记会保留在页面上提醒你。 ``` > [!NOTICE] 这不是合法类型 > 标记会保留在页面上提醒你。 其它常见问题: - 正文与标题合并:经过 Prettier 等格式化工具的文件,在标题行下保留一个空的 `>` 行,否则工具会把标题并入正文。 - 属性行被格式化工具移动:把 `{icon=…}` 这类标记行放在 `` / `` 之间。 - `style`、`onclick` 与不支持的属性会告警并忽略:属性行只接受 `icon` 与 `class`; 严格发布构建拒绝这条警告(见下表)。 ## 输出形态 {#outputs} | 输出 | 呈现 | | --- | --- | | HTML | 静态类型是 `
`;折叠类型是原生 `
` + `` | | 打印 | 全部静态展开,折叠块带 `data-td-callout-collapsible` 标记 | | Markdown | 保留源码块引用(含 `[!TYPE]` 标记与标题) | | RSS | 与打印相同,静态展开 | 提示块不加载脚本。 ## 参数参考 {#reference} 标记行 `> [!TYPE]± 标题`: | 参数 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `TYPE` | 枚举 | — | `NOTE` `TIP` `IMPORTANT` `WARNING` `CAUTION` `SUCCESS` `DANGER` `QUESTION` `EXAMPLE` `QUOTE` `DETAILS`;大小写不敏感;未知值渲染为普通块引用 | | `±` | `-` / `+` / 无 | 无 | `-` 折叠默认收起,`+` 折叠默认展开;`DETAILS` 不加符号即收起 | | 标题 | 行内 Markdown | 类型的本地化名称 | 与标记同一行 | {.fields meta="type default"} 属性行 `{…}`(块引用之后紧接的一行): | 参数 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `icon` | Font Awesome class 对 | 类型默认图标 | 例如 `fa-solid fa-database`;`DETAILS` 默认无图标 | | `class` | 空格分隔的 class | — | 原样透传给站点 CSS | {.fields meta="type default"} `style`、`on*` 与其它键会告警并忽略;严格发布构建拒绝这条警告。 ## 限制与常见问题 {#limits} - 不能自定义颜色:颜色由类型决定,需要新语义时选最接近的类型并自定义标题。 - 折叠状态不持久化。 - 提示块可以放在 `{.steps}` 列表项与 `{{% steps %}}` 步骤中(见[步骤](/zh/docs/components/steps/)),块引用的每一行都以 `>` 开头,缩进与列表项对齐。 ## 相关 {#related} - [步骤](/zh/docs/components/steps/) — 步骤中放置提示块 - [标签页](/zh/docs/components/tabs/) — 同一提示按平台分开呈现 - [编写页面](/zh/docs/write/pages/) — 提示块与正文的取舍 --- 反链: - [组件](/zh/docs/components/) - [徽章](/zh/docs/components/badge/) - [卡片](/zh/docs/components/cards/) - [文件树](/zh/docs/components/filetree/) - [思维导图](/zh/docs/components/markmap/) - [步骤](/zh/docs/components/steps/) - [标签页](/zh/docs/components/tabs/) - [打印支持](/zh/docs/customize/print/) - [编写页面](/zh/docs/write/pages/) ================ Source: https://oink.pgsty.com/zh/docs/components/image/index.md ================ # 图片 > 用普通 Markdown 图片语法写图,加一行属性就得到图注、尺寸、缩放、链接、编号与 Hugo 图片处理。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- 图片只有一种写法:Markdown 的 `![替代文字](来源 "标题")`。独立成段的图片可以在下一行跟一行 `{…}` 属性,成为带图注的 figure、缩放候选、编号图或经 Hugo 处理的派生图。主题没有图片 shortcode。 ## 最简例子 {#minimal} ```markdown {title="源码"} ![OINK 文档外壳:侧栏、正文与目录三栏](oink-shell.webp) ``` ![OINK 文档外壳:侧栏、正文与目录三栏](oink-shell.webp) 这张图与本页放在同一目录(页面包)中,主题读取它的固有尺寸并写入 `width`/`height`,页面加载时不发生跳版;所有图片懒加载。替代文字供屏幕阅读器与搜索引擎使用,应当始终填写;空 alt 表示装饰性图片,缩放会跳过它。 ## 图片来源 {#sources} 来源按以下顺序解析,写法相同: | 放法 | 源码里怎么写 | 适合 | | --- | --- | --- | | 与页面同目录(页面包 `index.md` + 图片) | `![…](oink-shell.webp)` | 只有这一页用的截图;随页面一起移动、翻译共用 | | 全局资源 `assets/images/…` | `![…](images/logo/oink.webp)` | 多页共用、还要做处理(缩放 / 裁切)的图 | | 静态目录 `static/images/…` | `![…](/images/hero-light.webp)` | 不需要处理的大图、下载物;主题拿不到尺寸时可以用 `width`/`height` 补 | | 远程 URL | `![…](https://example.com/a.png)` | 少用:构建期不会下载,也不能处理 | 相对路径先按页面资源、再按全局资源查找,都找不到时按静态路径原样输出;主题不检查 静态路径与远程 URL 是否存在。要求处理(`command=`)却解析不到可处理资源时,普通 预览告警并保留未处理图片;严格发布构建拒绝这条警告。 ## 行内与块级 {#inline-vs-block} 位于文字中间的是行内图片,渲染为一个 ``,不能带属性;独立成段的是块级图片,可以带属性行。 ```markdown {title="源码"} 这一枚小图 ![文档外壳缩略图](oink-mini.webp) 夹在句子里,是行内图片。 ![文档外壳缩略图](oink-mini.webp) {width="100" height="64"} ``` 这一枚小图 ![文档外壳缩略图](oink-mini.webp) 夹在句子里,是行内图片。 ![文档外壳缩略图](oink-mini.webp) {width="100" height="64"} 行内图片按自身尺寸显示(这里是 50×32)。没有固有尺寸的 SVG 行内插入时会被拉伸到容器宽度,SVG 应作为块级图片使用并给出 `width`/`height`。 > [!NOTE] > 块级图片依赖站点设置 `markup.goldmark.parser.wrapStandAloneImageWithinParagraph: false`(本站已配置;见[配置总览](/zh/docs/customize/config/))。缺少它时 Goldmark 会把独立图片包进 `

`,属性行也会被当作正文。 ## 图注 {#caption} 属性行加 `caption="…"`,图片渲染为 `

` + `
`。图注是纯文本,不解析 Markdown。 ```markdown {title="源码"} ![发布卡片:版本号、发布日期与资产按钮](release-note.webp) {caption="发布卡片由 data/download 与页面的 release 记录生成"} ``` ![发布卡片:版本号、发布日期与资产按钮](release-note.webp) {caption="发布卡片由 data/download 与页面的 release 记录生成"} Markdown 里的 `"标题"` 保持原义(悬停提示),不会成为图注。 ## 尺寸 {#size} `width`/`height` 是正整数,覆盖资源自身的尺寸:为静态或远程图片提供占位框以避免跳版,或把大图缩小显示(浏览器缩放,不改文件)。 ```markdown {title="源码"} ![OINK 首页插画(浅色)](/images/hero-light.webp) {width="450" height="300" caption="static/images/ 里的 900×600 插画按一半显示"} ``` ![OINK 首页插画(浅色)](/images/hero-light.webp) {width="450" height="300" caption="static/images/ 里的 900×600 插画按一半显示"} ## 处理型图片 {#processing} 页面资源与全局资源可以在构建期由 Hugo 处理:`command` 与 `options` 必须同时给出,命令是 `Fit` `Resize` `Fill` `Crop` 之一,选项是 Hugo 的图片处理字符串。渲染出的 `src` 是派生图;启用缩放时对话框打开原图。 ```markdown {title="源码"} ![文档外壳缩略图](oink-shell.webp) {command="Fit" options="300x150" caption="Fit 300x150:按比例装进 300×150 的框"} ![文档外壳左半边](oink-shell.webp) {command="Fill" options="300x150 Left" caption="Fill 300x150 Left:填满框,从左侧裁"} ``` ![文档外壳缩略图](oink-shell.webp) {command="Fit" options="300x150" caption="Fit 300x150:按比例装进 300×150 的框"} ![文档外壳左半边](oink-shell.webp) {command="Fill" options="300x150 Left" caption="Fill 300x150 Left:填满框,从左侧裁"} 静态路径、远程 URL 与 SVG 不能处理。对它们写 `command` 时告警并保留未处理图片; 严格发布构建拒绝这条警告。选项语法(锚点、质量、格式转换,如 `300x150 webp q80`)见 [Hugo 图片处理](https://gohugo.io/content-management/image-processing/)。 ## 链接图片 {#link} 两种写法,用途不同: - 没有图注、图片本身是链接:用 Markdown 的链接包图 `[![alt](src)](href)`。 - 有图注的 figure 整体可点:属性行加 `link="…"`(必须同时有 `caption` 或 `num`)。 ```markdown {title="源码"} [![点击进入亮点特性页](oink-shell.webp)](/zh/docs/about/features/) ![发布卡片](release-note.webp) {caption="点击图片查看发布与下载页的说明" link="/zh/docs/write/releases/"} ``` [![点击进入亮点特性页](oink-shell.webp)](/zh/docs/about/features/) ![发布卡片](release-note.webp) {caption="点击图片查看发布与下载页的说明" link="/zh/docs/write/releases/"} 带链接的图不参与缩放。没有图注只写 `link=` 时告警并丢弃链接,消息提示改用 `[![…](…)](…)`;严格发布构建拒绝这条警告。 ## 编号图 {#numbered} 编号图用于书籍与长篇手册:属性行加 `num`,可选 `#id`。编号是作者书写的字符串(`2-1`、`3.4`),主题不自动计数;图注前加本地化的「图 2-1」前缀,`#id` 缺省为 `fig-`。正文用普通链接 `[图 2-1](#fig-2-1)` 或 `xref` shortcode 引用;全书图目录见[书籍出版](/zh/docs/write/book/)。 ```markdown {title="源码"} ![发布卡片](release-note.webp) {#fig-release num="2-1" caption="发布卡片:版本、日期与资产"} 见[图 2-1](#fig-release)。 ``` ![发布卡片](release-note.webp) {#fig-release num="2-1" caption="发布卡片:版本、日期与资产"} 见[图 2-1](#fig-release)。 编号图可以同时是处理型图片(`num` + `command`),也可以带 `link`。 ## 缩放 {#zoom} 图片缩放默认关闭。站点开启后,块级图片、figure、画廊中带 alt 的图成为可点击的按钮,在原生 `` 中查看大图(Esc 关闭,焦点回到原处)。本页在 front matter 中开启了它,上面的图都可以点击。 ```yaml {title="hugo.yml"} params: ui: image_zoom: true ``` ```yaml {title="某一页的 front matter:只关这一页"} image_zoom: false ``` 不缩放的图:行内图、alt 为空的装饰图、带链接的图、`data-no-zoom` 标记的图。运行时只在页面确有候选图时加载;打印 / Markdown / RSS 中没有对话框。 ```markdown {title="源码:装饰图不缩放"} ![](oink-shell.webp) {width="150" height="75"} ``` ![](oink-shell.webp) {width="150" height="75"} ## 深浅色图片 {#dark-mode} 主题没有按深浅色切换图片的参数。需要两张图时,各写一个 `class`,在站点 CSS 中按 `[data-bs-theme="dark"]` 显示其一: ```markdown {title="源码"} ![侧栏(浅色)](oink-shell.webp) {class="only-light"} ![侧栏(深色)](oink-shell.webp) {class="only-dark"} ``` ```scss {title="assets/scss/_styles_project.scss"} [data-bs-theme="dark"] .only-light, :not([data-bs-theme="dark"]) .only-dark { display: none; } ``` `class` 由主题原样透传,供站点 CSS 使用。 ## 输出形态 {#outputs} | 输出 | 呈现 | | --- | --- | | HTML | 行内 ``;块级 ``;有图注 / 编号时 `
` + `
`;缩放候选带 `data-td-image-zoom` | | 打印 | 同 HTML,去掉缩放控件 | | Markdown | 原样输出 `![alt](src)` 与属性行 | | RSS | 图片 `src` 改为绝对地址;无缩放 | ## 参数参考 {#reference} 属性行 `{…}`(块级图片之后紧接的一行): | 参数 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `caption` | 纯文本 | — | 有它就渲染成 figure;不解析 Markdown | | `#id` | 标识符 | 有 `num` 时 `fig-` | `[A-Za-z][A-Za-z0-9_.:-]*`;作为锚点与 Book 目标 ID | | `num` | 字符串 | — | `[0-9A-Za-z.-]+`;注册为 Book 图目标,图注加「图 N.」前缀 | | `width` / `height` | 正整数 | 资源固有尺寸 | 覆盖尺寸;静态 / 远程图靠它避免跳版 | | `command` | 枚举 | — | `Fit` `Resize` `Fill` `Crop`;必须与 `options` 同给;仅页面 / 全局资源 | | `options` | 字符串 | — | Hugo 图片处理选项,如 `600x300`、`300x150 Left`、`800x webp q80` | | `link` | URL | — | 把 figure 包进链接;需要 `caption` 或 `num`;带链接的图不缩放 | | `class` | class 列表 | — | 透传给站点 CSS | | `data-*` / `aria-*` | 字符串 | — | 透传 | {.fields meta="type default"} `style`、`on*`、`alt`、`title`、`src` 与不支持的键出现在属性行时告警并忽略; 严格发布构建拒绝这条警告。alt、title、src 属于 Markdown 图片本身。 ## 限制与常见问题 {#limits} - 图注不含 Markdown:所有公开字符串参数都是纯文本;富文本说明写在图片下方的段落中。 - `title` 不是图注:`![a](b "c")` 的 `c` 是悬停提示。 - 处理型图片只对资源生效:`static/` 中的图需要处理时移到页面包或 `assets/`。 - 构建期不下载远程图片。 - 缩放不支持拖拽、平移、上一张 / 下一张;一组相关图片使用[画廊](/zh/docs/components/gallery/)。 ## 相关 {#related} - [画廊](/zh/docs/components/gallery/) — 一组图片共用一个缩放对话框 - [书籍出版](/zh/docs/write/book/) — 图目录、`xref` 交叉引用 - [品牌外观](/zh/docs/customize/brand/) — 站点 logo 与 favicon 放哪 - [卡片](/zh/docs/components/cards/) — 卡片上的图片 --- 反链: - [组件](/zh/docs/components/) - [Asciinema](/zh/docs/components/asciinema/) - [卡片](/zh/docs/components/cards/) - [Draw.io](/zh/docs/components/drawio/) - [文件树](/zh/docs/components/filetree/) - [画廊](/zh/docs/components/gallery/) - [公式](/zh/docs/components/math/) - [Mermaid](/zh/docs/components/mermaid/) - [PlantUML](/zh/docs/components/plantuml/) - [品牌外观](/zh/docs/customize/brand/) - [配置总览](/zh/docs/customize/config/) - [打印支持](/zh/docs/customize/print/) - [书籍出版](/zh/docs/write/book/) - [页面参数](/zh/docs/write/frontmatter/) - [编写页面](/zh/docs/write/pages/) ================ Source: https://oink.pgsty.com/zh/docs/components/code/index.md ================ # 代码块 > 普通 Markdown 围栏加一行属性,就得到文件名标题、精确复制、行号、高亮、换行、折叠与可链接的行。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- 代码块是普通的 Markdown 围栏,高亮由 Hugo 内置的 Chroma 在构建期完成,浏览器里没有高亮器。用于命令、配置片段与源码:围栏信息行上的 `{…}` 属性决定标题栏、复制行为、行号与行锚点。图示类围栏(`mermaid`、`echarts`、`filetree` 等)不走这条路径,它们各有渲染钩子。 ## 最简例子 {#minimal} ````markdown {title="源码"} ```sql SELECT datname, numbackends FROM pg_stat_database ORDER BY numbackends DESC; ``` ```` ```sql SELECT datname, numbackends FROM pg_stat_database ORDER BY numbackends DESC; ``` 没有属性的围栏同样有完整外壳与复制按钮。无标题栏时不渲染空白横条,复制按钮浮在右上角,鼠标悬停或焦点进入块内时出现,触屏设备上始终可见。外壳不显示语言名,lexer 名字只写入 `data-language`,供样式表与测试使用。 语言标记就是 Chroma 的 lexer 名。`diff` 围栏用 Chroma 的增删行样式呈现补丁,不需要额外组件: ````markdown {title="源码"} ```diff {title="hugo.yml 的改动"} params: ui: - sidebar_menu_compact: true + sidebar_menu_compact: false sidebar_menu_foldable: true ``` ```` ```diff {title="hugo.yml 的改动"} params: ui: - sidebar_menu_compact: true + sidebar_menu_compact: false sidebar_menu_foldable: true ``` ## 文件名标题 {#title} `title` 给块加一条可见标题栏,通常写文件名或路径。它同时成为这个块的无障碍名称。 ````markdown {title="源码"} ```yaml {title="hugo.yml"} markup: goldmark: parser: attribute: block: true renderer: unsafe: true ``` ```` ```yaml {title="hugo.yml"} markup: goldmark: parser: attribute: block: true renderer: unsafe: true ``` `filename` 是 `title` 的历史别名,两个一起写时告警并使用 `filename`;严格发布构建 拒绝这条警告。 ## 行号、起始行与高亮 {#line-numbers} `lineNos` 取 `inline`(行号与代码同一列)或 `table`(行号独立成列,可单独选中不被复制)。`lineNoStart` 改显示的起始编号。`hl_lines` 标记要强调的行,计数按围栏内的源码行,从 1 开始,与 `lineNoStart` 无关。 ````markdown {title="源码"} ```ini {title="postgresql.conf" lineNos="inline" lineNoStart=120 hl_lines="2 4-5"} shared_buffers = 8GB max_connections = 200 work_mem = 64MB wal_level = replica max_wal_senders = 10 ``` ```` ```ini {title="postgresql.conf" lineNos="inline" lineNoStart=120 hl_lines="2 4-5"} shared_buffers = 8GB max_connections = 200 work_mem = 64MB wal_level = replica max_wal_senders = 10 ``` `lineNos="table"` 把行号放进独立的一列(两种模式下复制按钮都会剔除行号): ````markdown {title="源码"} ```bash {title="部署三条命令" lineNos="table"} ./configure -c rich ./install.yml pig ext install pg_duckdb ``` ```` ```bash {title="部署三条命令" lineNos="table"} ./configure -c rich ./install.yml pig ext install pg_duckdb ``` `tabWidth` 决定制表符展开成几个空格,与 `style` 一样原样转交 Chroma。本站使用基于 class 的 Chroma 调色板(深浅色各一套),`style` 只在把 Hugo 切回内联样式模式时才生效。 ## 长行换行 {#wrap} `wrap=true` 只改变显示:源码不变,复制出来的文本也不变。不加它时长行横向滚动。 ````markdown {title="源码"} ```text {title="config/artifacts.env" wrap=true} ARTIFACT_URL=https://repo.pigsty.io/pkg/infra/v3.6.0/infra-pkg-v3.6.0.el9.x86_64.tgz CHECKSUM=sha256:6d3dce4f7acb18f586469adcb80ab35f3e859f9837786e151cfbc2b3c0f587b2 ``` ```` ```text {title="config/artifacts.env" wrap=true} ARTIFACT_URL=https://repo.pigsty.io/pkg/infra/v3.6.0/infra-pkg-v3.6.0.el9.x86_64.tgz CHECKSUM=sha256:6d3dce4f7acb18f586469adcb80ab35f3e859f9837786e151cfbc2b3c0f587b2 ``` `wrap=true` 与表格行号不能共存:行号列与代码列是两个表格单元格,换行后会错位。 写在一起时告警并关闭换行,提示改用 `lineNos="inline"` 或去掉换行;严格发布构建 拒绝这条警告。 ## 折叠长代码 {#collapse} `collapse=N` 让块初始只显示 N 行,底部给一个「显示全部 N 行」按钮。服务器输出完整代码,折叠是浏览器量出第 N 行位置后的视觉裁切:没有 JavaScript 时、读屏器中、打印时代码都是完整的。 ````markdown {title="源码"} ```yaml {title="hugo.yml" collapse=8} baseURL: https://oink.pgsty.com/ title: OINK defaultContentLanguage: en languages: en: languageName: English weight: 1 zh: languageName: 简体中文 weight: 2 params: offline_search: true ui: sidebar_menu_foldable: true ``` ```` ```yaml {title="hugo.yml" collapse=8} baseURL: https://oink.pgsty.com/ title: OINK defaultContentLanguage: en languages: en: languageName: English weight: 1 zh: languageName: 简体中文 weight: 2 params: offline_search: true ui: sidebar_menu_foldable: true ``` 行数不超过 `collapse` 时按钮不出现。换行与折叠可以一起用:折叠测量的是第 N 个源码行节点的底边,换行的行不会被截断。 ## 复制内容 {#copy} 默认复制整块源码。终端会话(`console` 与 `shell-session` 两个 lexer)默认只复制命令:带提示符的行留下,提示符本身与输出行去掉。下面这个块复制出来只有两条命令,没有 `$` 也没有输出。 ````markdown {title="源码"} ```console $ pig ext list duckdb name version category pg_duckdb 1.0.0 OLAP $ pig ext install pg_duckdb INFO installing pg_duckdb ``` ```` ```console $ pig ext list duckdb name version category pg_duckdb 1.0.0 OLAP $ pig ext install pg_duckdb INFO installing pg_duckdb ``` 要连提示符与输出一起复制就写 `copy="all"`。把 `copy="command"` 用在 `bash`、`sh` 之类普通 lexer 上时告警并使用 `copy="all"`,因为它们分不出提示符、命令与输出; 严格发布构建拒绝这条警告。多行命令请在续行里写出续行提示符(通常是 `>`),否则 那一行会被当成输出而排除。 会话 lexer 的块里一行提示符都没有时,复制按钮报失败:图标转为错误状态,控制台留一条错误,剪贴板不变。它不会退化成复制全文。 `copy=false` 关掉这一块的复制按钮,用于不应被抄走的反例片段: ````markdown {title="源码"} ```yaml {title="反例:属性行离开了它的块" copy=false} params: ui: image_zoom: true # 错:image_zoom 是一张表,不是布尔值 ``` ```` ```yaml {title="反例:属性行离开了它的块" copy=false} params: ui: image_zoom: true # 错:image_zoom 是一张表,不是布尔值 ``` 整站关掉复制用 `params.ui.code_copy: false`,它优先于每个块自己写的 `copy`(见[配置总览](/zh/docs/customize/config/))。复制按钮只有图标,成功与失败会换图标并播报本地化状态;复制内容保留缩进、空行与 Unicode,去掉行号,末尾只留一个换行。 ## 行链接与稳定 ID {#line-links} 把「看第 3 行」做成链接需要两步:给围栏一个明确的 `id`,再打开 `anchorLineNos=true`。行号随即变成锚点链接,锚点是 `#-<行号>`。 ````markdown {title="源码"} ```sql {id="ex-explain" title="explain.sql" lineNos="table" anchorLineNos=true} EXPLAIN (ANALYZE, BUFFERS) SELECT relname, n_live_tup FROM pg_stat_user_tables WHERE n_live_tup > 1000 ORDER BY n_live_tup DESC; ``` 跳到 [第 4 行](#ex-explain-4)。 ```` ```sql {id="ex-explain" title="explain.sql" lineNos="table" anchorLineNos=true} EXPLAIN (ANALYZE, BUFFERS) SELECT relname, n_live_tup FROM pg_stat_user_tables WHERE n_live_tup > 1000 ORDER BY n_live_tup DESC; ``` 跳到 [第 4 行](#ex-explain-4)。 不写 `id` 时主题也会生成一个页面内唯一的 ID,但它依赖围栏在页面里的顺序,前面 插入一个新围栏就会变。只有作者书写的 `id` 才是永久链接。ID 不能含空白与控制字符, 也不能与页面上其它块的 viewport、标签、面板、标题、行锚点 ID 重复;无效或重复 ID 会告警,严格发布构建拒绝这条警告。 ## 编号例 {#numbered} 写书或长手册时给代码片段编号:`num` 加 `caption`,这个围栏就成了一条 Book「示例」目标,可以被 `xref` 引用,也会进入全书的示例目录。编号由作者书写,主题不自动计数;`id` 默认是 `eg-`。 ````markdown {title="源码"} ```sql {num="4-1" caption="按表统计膨胀率" #eg-bloat} SELECT schemaname, relname, n_dead_tup, n_live_tup FROM pg_stat_user_tables WHERE n_dead_tup > n_live_tup * 0.2; ``` 参见 {{< xref eg="4-1" anchor="eg-bloat" >}}。 ```` ```sql {num="4-1" caption="按表统计膨胀率" #eg-bloat} SELECT schemaname, relname, n_dead_tup, n_live_tup FROM pg_stat_user_tables WHERE n_dead_tup > n_live_tup * 0.2; ``` 参见 [示例 4-1](#eg-bloat)。 `num` 与 `caption` 必须成对出现。只写 caption 时忽略它,只写编号时丢弃编号并告警; 严格发布构建拒绝这条警告。`num` 与标签页属性 `tab` 互斥。图、表、公式的编号写法与 索引见[书籍出版](/zh/docs/write/book/)。 ## 一组围栏做成标签页 {#tabs} 连续几个带 `tab` 的围栏会在浏览器里合成一个标签页集,第一个围栏上的 `group` 让它可分享、可同步、可记住选择。 ````markdown {title="源码"} ```bash {tab="Homebrew" group="oink-install" value="brew"} brew install hugo ``` ```bash {tab="APT" value="apt"} sudo apt install hugo ``` ```` ```bash {tab="Homebrew" group="oink-install" value="brew"} brew install hugo ``` ```bash {tab="APT" value="apt"} sudo apt install hugo ``` 完整规则(分组语法、URL hash、跨组同步、正文标签页)在[标签页](/zh/docs/components/tabs/)。 ## 易错写法 {#pitfalls} - 在文档里展示 shortcode:围栏不阻止 Hugo 解析,写在代码块里的 `{{< tabs >}}` 仍会执行。要让它原样显示,在两侧定界符的内侧各加一对注释符号,写成 {{</* tabs */>}},百分号形式对应 {{%/* steps */%}}。本页每一处展示 shortcode 的地方都是这么写的。 - 围栏里套围栏:外层用四个反引号、内层三个,本页每一段「源码」都是这么写的;内层还有围栏时外层再加一个。 - 属性写在信息行上:围栏的属性跟在开栏那一行的语言后面,表格与图片的属性才写在块的下一行。写到下一行会变成正文里一段可见的花括号。 - 未知、不安全与主题保留属性在普通预览中告警并忽略,消息列出允许的名字;严格 发布构建拒绝每条此类警告。 - 列表项里的围栏:缩进要与列表项内容对齐(`1.` 之后恒定三个空格),否则围栏会脱离列表。 ## 输出形态 {#outputs} | 输出 | 呈现 | | --- | --- | | HTML | `
` 外壳 + Chroma 的 `.highlight`/`.chroma`;复制、折叠按钮在服务器输出里是 `hidden`,脚本确认可用后才显示 | | 打印 | 完整代码,去掉复制、折叠、渐隐;长块允许跨页;标题栏保留 | | Markdown | 原样输出源码围栏,连 `{…}` 属性一起 | | RSS | 静态代码块,无按钮 | 没有复制或折叠控件的页面不加载 `code-block.js`;打印、Markdown 与 RSS 输出不加载。 ## 参数参考 {#reference} 开栏那一行、语言之后的 `{…}` 里,OINK 自己的属性: | 参数 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `title` | 非空字符串 | 无 | 可见标题栏(通常是文件名),同时是无障碍名称 | | `filename` | 非空字符串 | 无 | `title` 的历史别名;两者同时出现时告警并使用 `filename` | | `copy` | `all` `command` `true` `false` | 会话 lexer 为 `command`,其余为 `all` | `true` 等价于 `all`;`command` 只允许 `console`/`shell-session` | | `wrap` | 布尔 | `false` | 视觉换行,不改源码;与表格行号互斥 | | `collapse` | 正整数 | 无 | 初始显示的最大行数;行数不足时不生效 | | `label` | 非空字符串 | 由标题派生 | 无障碍名称,不显示在页面上;与 `aria-label` 互斥 | | `id` | 非空 token | 自动生成 | 稳定的块 ID 与行锚点前缀;不能含空白 | | `tab` | 非空字符串 | 无 | 标签名,见[标签页](/zh/docs/components/tabs/);与 `num` 互斥 | | `group` | `^[a-z][a-z0-9_-]*$` | 无 | 写在一组的第一个围栏上,启用 hash / 同步 / 持久化;需要 `tab` | | `value` | `^[a-z0-9][a-z0-9_-]*$` | 无 | 分组内每个围栏必填,无分组时禁止;需要 `tab` | | `num` | `[0-9A-Za-z.-]+` | 无 | 编号示例(Book `eg`);必须与 `caption` 同时出现 | | `caption` | 纯文本 | 无 | 编号示例的说明;必须与 `num` 同时出现 | | `class` | class 列表 | 无 | 追加到 `.td-code` 根元素 | | `data-*` / `aria-*` / `role` | 字符串 | 无 | 透传到根元素 | {.fields meta="type default"} `title`、`filename` 与 `label` 已经为块生成了无障碍名称与 `role="group"`。它们中的 任意一个与 `aria-label`、`aria-labelledby` 或 `role` 同时出现时告警并忽略冲突属性; 严格发布构建拒绝这条警告。这三个属性只在块没有标题也没有 `label` 时可以透传。 同一行还能写 Chroma 选项,主题原样转交 Hugo: | 选项 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `lineNos` | `false` `inline` `table` | `false` | 行号形态;`table` 与 `wrap=true` 互斥 | | `lineNoStart` | 正整数 | `1` | 显示的起始行号,不影响 `hl_lines` 的计数 | | `hl_lines` | 行号与区间 | 无 | 如 `"2 4-5"`,按围栏内源码行计数 | | `anchorLineNos` | 布尔 | `false` | 行号变成锚点链接,前缀取自块的 `id` | | `tabWidth` | 正整数 | Hugo 默认 | 制表符展开的空格数 | {.fields meta="type default"} ## 限制与常见问题 {#limits} - 不换高亮器:没有 Shiki、Twoslash、浏览器端高亮,也没有可执行的代码演练场。补丁用 `diff` 围栏,Chroma 的 `.gi`/`.gd` 就是增删行的样式。 - `copy="command"` 只认会话 lexer:写在别的语言上是构建错误,不会退化成复制全部。 - 自动生成的 ID 不是永久链接:要发链接就写 `id`。 - `mermaid`、`math`、`chem`、`markmap`、`plantuml`、`echarts`、`infographic`、`checksums`、`filetree`、`gallery` 不是代码块:它们有各自的渲染钩子,不套这层外壳,也没有复制按钮。 ## 相关 {#related} - [标签页](/zh/docs/components/tabs/) — 相邻围栏合成标签页的完整规则 - [引用](/zh/docs/components/include/) — 把仓库里的真实文件当代码块插进来 - [书籍出版](/zh/docs/write/book/) — 编号示例、交叉引用与示例目录 - [打印支持](/zh/docs/customize/print/) — 长代码在打印里的形态 --- 反链: - [组件](/zh/docs/components/) - [Asciinema](/zh/docs/components/asciinema/) - [ECharts](/zh/docs/components/echarts/) - [文件树](/zh/docs/components/filetree/) - [引用](/zh/docs/components/include/) - [公式](/zh/docs/components/math/) - [PlantUML](/zh/docs/components/plantuml/) - [步骤](/zh/docs/components/steps/) - [表格](/zh/docs/components/table/) - [标签页](/zh/docs/components/tabs/) - [打印支持](/zh/docs/customize/print/) - [API 文档](/zh/docs/write/openapi/) - [编写页面](/zh/docs/write/pages/) - [发布与下载页](/zh/docs/write/releases/) ================ Source: https://oink.pgsty.com/zh/docs/components/tabs/index.md ================ # 标签页 > 给相邻的围栏或表格加一个 `{tab=}` 属性就得到标签页;加上 group 之后可分享链接、跨组同步、记住读者的选择。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- 标签页并列等价的几种写法:包管理器、发行版、YAML / TOML / JSON、环境变量与配置项。有先后的步骤、互不相关的内容不适合标签页,读者一次只看见其中一个。 原生形态是给相邻的块加 `tab` 属性。正文(多个段落、列表、提示块)要做成标签页时才用 `tabs`/`tab` shortcode。两种形态共用一个运行时、一套 DOM 与一样的键盘行为。 ## 最简例子 {#minimal} 连着写两个带 `tab` 的围栏,中间只隔空行。 ````markdown {title="源码"} ```bash {tab="Homebrew"} brew install hugo ``` ```bash {tab="Debian / Ubuntu"} sudo apt install hugo ``` ```` ```bash {tab="Homebrew"} brew install hugo ``` ```bash {tab="Debian / Ubuntu"} sudo apt install hugo ``` 服务器输出两个带标题的代码块,没有面板被隐藏;页面加载后运行时把相邻的同类块重组为标签页。在 GitHub 上、打印时、关闭 JavaScript 时,读者看到的是连续两块完整内容。 ## 分组:链接、同步与记忆 {#group} 只在第一个块上写 `group`,这一组就有了公开的 URL hash `#-`、页内同步与浏览器持久化;分组内的每个块都要写 `value`。 ````markdown {title="源码"} ```bash {tab="npm" group="pkgmgr" value="npm"} npm create hugo-site@latest ``` ```bash {tab="pnpm" value="pnpm"} pnpm create hugo-site ``` ```bash {tab="Yarn" value="yarn"} yarn create hugo-site ``` ```` ```bash {tab="npm" group="pkgmgr" value="npm"} npm create hugo-site@latest ``` ```bash {tab="pnpm" value="pnpm"} pnpm create hugo-site ``` ```bash {tab="Yarn" value="yarn"} yarn create hugo-site ``` `value` 是机器值(`^[a-z0-9][a-z0-9_-]*$`),`tab` 是给人看的标签名,两者互不相干。上面这组的 pnpm 面板对应的 hash 是 `#pkgmgr-pnpm`,带这个 hash 访问本页会直接选中它。 ## 同组联动 {#sync} 下面这组用了同一个 `group="pkgmgr"`。在上面那组切换包管理器,这组会跟着切;在这组切换,上面那组也跟着切。选择写入 `localStorage` 的 `td-tabs:v1:pkgmgr` 键,在其它页面同组的标签页上仍然生效。 ````markdown {title="源码"} ```bash {tab="npm" group="pkgmgr" value="npm"} npm run build ``` ```bash {tab="pnpm" value="pnpm"} pnpm build ``` ```` ```bash {tab="npm" group="pkgmgr" value="npm"} npm run build ``` ```bash {tab="pnpm" value="pnpm"} pnpm build ``` 这组没有 `yarn` 面板。同步时缺哪个值就保持不动,不会出现「一组没有选中项」的状态。初始选哪个的优先级是:URL hash,存储的值,shortcode 的 `default` 或第一个块,第一个标签。带 hash 打开页面只切换,不覆盖读者已经存下的偏好。 ## 表格也能做标签页 {#tables} 同一套属性写在表格的属性行上,连着的表格就组成一组标签页。 ```markdown {title="源码"} | 参数 | 默认值 | | --- | --- | | `shared_buffers` | 25% RAM | | `max_connections` | 100 | {tab="PostgreSQL 18" group="pgver" value="pg18"} | 参数 | 默认值 | | --- | --- | | `shared_buffers` | 128MB | | `max_connections` | 100 | {tab="PostgreSQL 13" value="pg13"} ``` | 参数 | 默认值 | | --- | --- | | `shared_buffers` | 25% RAM | | `max_connections` | 100 | {tab="PostgreSQL 18" group="pgver" value="pg18"} | 参数 | 默认值 | | --- | --- | | `shared_buffers` | 128MB | | `max_connections` | 100 | {tab="PostgreSQL 13" value="pg13"} 围栏与表格是两种块类型,相邻也不会合成同一组:一组标签页里只能全是围栏或全是表格。两者混排使用下面的 shortcode 形态。 ## 标签名与文件名共存 {#tab-with-title} 围栏的 `tab` 和 `title` 可以一起写:标签名进标签栏,文件名标题栏留在面板里。 ````markdown {title="源码"} ```yaml {tab="YAML" title="hugo.yml" group="conffmt" value="yaml"} params: ui: sidebar_menu_foldable: true ``` ```toml {tab="TOML" title="hugo.toml" value="toml"} [params.ui] sidebar_menu_foldable = true ``` ```` ```yaml {tab="YAML" title="hugo.yml" group="conffmt" value="yaml"} params: ui: sidebar_menu_foldable: true ``` ```toml {tab="TOML" title="hugo.toml" value="toml"} [params.ui] sidebar_menu_foldable = true ``` ## 单独一个块只是带标题的块 {#single-block} 一个块要凑够两个相邻的同类块才会变成标签页。落单的块保留标题,不会变成只有一个标签的标签栏。 ````markdown {title="源码"} ```ini {tab="只有这一块"} listen_addresses = '*' ``` ```` ```ini {tab="只有这一块"} listen_addresses = '*' ``` 块之间只允许空行。三种情况会断开一组:中间隔了正文(段落、标题、列表都算);中间有一条 HTML 注释,`` 是常见的一处;后一个块自己写了 `group`,一组里只有第一个块可以带 `group`。 ## 正文标签页 {#shortcode} 面板里要放段落、列表、提示块或多个块时,用 `tabs`/`tab` shortcode。正文是完整的 Markdown。 `````markdown {title="源码"} {{< tabs group="deploy" default="pages" label="部署方式" >}} {{< tab label="GitHub Pages" value="pages" >}} 仓库自带 `.github/workflows/`,推到 `main` 就会构建并发布。 > [!NOTE] > `baseURL` 要写成仓库的 Pages 地址。 {{< /tab >}} {{< tab label="Cloudflare Pages" value="cloudflare" >}} 在 Cloudflare 控制台里连接仓库,构建命令: ```bash hugo --gc --minify ``` {{< /tab >}} {{< /tabs >}} ````` **GitHub Pages** 仓库自带 `.github/workflows/`,推到 `main` 就会构建并发布。 > [!NOTE] > `baseURL` 要写成仓库的 Pages 地址。 **Cloudflare Pages** 在 Cloudflare 控制台里连接仓库,构建命令: ```bash hugo --gc --minify ``` `default` 指定初始选中的面板,它必须是某个子项的 `value`,并且需要 `group`。没有 `group` 时不能写 `value`,主题自动生成 `tab1`、`tab2` 等值,这组标签页只在本地切换,不动 URL 也不写存储。shortcode 形态比属性形态严格:写错的地方在构建期就报出来,不留到浏览器里。 ## 输出形态 {#outputs} | 输出 | 呈现 | | --- | --- | | HTML | `
` + `role="tablist"` 的按钮与面板;运行时接管前所有面板都可见 | | 打印 | 连续的带标题静态分节,没有标签栏 | | Markdown | 围栏形态保持源码围栏(含 `{tab=}` 属性);shortcode 形态输出 `**标签名**` 加正文 | | RSS | 与打印相同,堆叠的带标题分节 | 只有用到标签页的页面才加载 `tabs.js`;打印、Markdown 与 RSS 输出不加载。 ## 参数参考 {#reference} 写在围栏信息行或表格属性行上的属性: | 参数 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `tab` | 非空字符串 | 无 | 可见标签名;单独出现时就是这个块的标题 | | `group` | `^[a-z][a-z0-9_-]*$` | 无 | 写在一组的第一个块上,启用 hash、页内同步与持久化;需要 `tab` | | `value` | `^[a-z0-9][a-z0-9_-]*$` | 无 | 分组内每个块必填,无分组时禁止;需要 `tab` | {.fields meta="type default"} `tabs` shortcode: | 参数 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `group` | `^[a-z][a-z0-9_-]*$` | 无 | 同上,启用 hash、同步与持久化 | | `default` | 某个子项的 `value` | 第一个子项 | 初始选中的面板;需要 `group` | | `label` | 纯文本 | 本地化的「选项卡」 | 标签栏的无障碍名称,不显示在页面上 | {.fields meta="type default"} `tab` shortcode: | 参数 | 类型 | 必填 | 说明 | | --- | --- | --- | --- | | `label` | 纯文本 | 是 | 可见标签名 | | `value` | `^[a-z0-9][a-z0-9_-]*$` | 有 `group` 时 | 无分组时禁止书写,自动生成 `tab1`、`tab2` 等值 | {.fields meta="type required"} 行为约定:面板 ID 在分组里是 `-`,同一页出现第二组同名 `group` 时后续各组的 ID 加 `-2`、`-3` 后缀(深链目标始终是第一组),未分组时由主题生成;存储键是 `td-tabs:v1:`;用户点击或按键会用 `replaceState` 更新 hash 并写入存储,带 hash 访问只切换不写入。键盘上左右方向键(感知 RTL)与 Home/End 移动并激活标签,焦点停留在标签上。 ## 限制与常见问题 {#limits} - 无效分组与组合会在 Hugo 构建中告警并采用安全回退:丢弃不可用的 group/value/default、忽略夹杂正文、保留后出现的重复项,或不渲染空集合。严格发布 构建拒绝每条警告,消息带源码位置。 - 属性形态没有可用 `value` 时失去同步能力,只保留本地标签页;分组不会静默编造身份。 - 围栏与表格不会混成一组,正文与代码混排请用 shortcode 形态。 - 标签页不是折叠块。只想收起长输出用 `> [!DETAILS]`(见[提示块](/zh/docs/components/callout/))。 - 同名 `group` 是全站共享的:读者在 A 页选了 pnpm,B 页同组的标签页也会是 pnpm。这是它的用途,也意味着 `group` 名要按含义取,不用 `tabs1` 这种。 ## 相关 {#related} - [代码块](/zh/docs/components/code/) — 围栏的其余属性(标题、复制、行号、折叠) - [表格](/zh/docs/components/table/) — 表格属性行的其余取值 - [提示块](/zh/docs/components/callout/) — 折叠而不是并列时用它 - [步骤](/zh/docs/components/steps/) — 步骤里可以放标签页 --- 反链: - [提示块](/zh/docs/components/callout/) - [卡片](/zh/docs/components/cards/) - [代码块](/zh/docs/components/code/) - [文件树](/zh/docs/components/filetree/) - [画廊](/zh/docs/components/gallery/) - [引用](/zh/docs/components/include/) - [按键](/zh/docs/components/kbd/) - [步骤](/zh/docs/components/steps/) - [表格](/zh/docs/components/table/) - [Agent 支持](/zh/docs/customize/agents/) - [打印支持](/zh/docs/customize/print/) - [编写页面](/zh/docs/write/pages/) ================ Source: https://oink.pgsty.com/zh/docs/components/table/index.md ================ # 表格 > 普通 GFM 表格加一行属性,就得到标题、兼容矩阵、参数表、编号表或标签页;宽表格自己横向滚动,不撑宽页面。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- 表格是普通的 GFM 管道表格。主题的表格渲染钩子把每张表包进一块可横向滚动的区域,表格下面那一行 `{…}` 属性决定它是哪一种表:带标题的表、兼容矩阵、参数表、编号表或标签页。合并单元格、排序与筛选不在能力范围内,需要它们的场景请改换呈现方式。 ## 最简例子 {#minimal} 不写属性行就是一张普通表。对齐方式照旧来自分隔行,表头单元格是 `th scope="col"`。 ```markdown {title="源码"} | 组件 | 端口 | 用途 | | --- | :---: | --- | | PostgreSQL | 5432 | 数据库 | | Pgbouncer | 6432 | 连接池 | | Patroni | 8008 | 高可用编排 | ``` | 组件 | 端口 | 用途 | | --- | :---: | --- | | PostgreSQL | 5432 | 数据库 | | Pgbouncer | 6432 | 连接池 | | Patroni | 8008 | 高可用编排 | ## 宽表格自己滚动 {#scroll} 列太多的表不会把页面撑宽,它在自己的区域里横向滚动。这块区域可以用键盘聚焦:Tab 停入后方向键滚动,无障碍名称是本地化的「可横向滚动的表格」。 ```markdown {title="源码"} | 集群 | 角色 | 版本 | 状态 | 延迟 | 连接数 | 大小 | 备份 | | --- | --- | --- | --- | --- | --- | --- | --- | | pg-meta | primary | 18.1 | running | — | 42 | 12 GB | 2026-08-17 | | pg-test | replica | 18.1 | streaming | 12 ms | 8 | 12 GB | 2026-08-17 | ``` | 集群 | 角色 | 版本 | 状态 | 延迟 | 连接数 | 大小 | 备份 | | --- | --- | --- | --- | --- | --- | --- | --- | | pg-meta | primary | 18.1 | running | — | 42 | 12 GB | 2026-08-17 | | pg-test | replica | 18.1 | streaming | 12 ms | 8 | 12 GB | 2026-08-17 | ## 表格标题 {#caption} `{caption="…"}` 加一个可见的 ``,纯文本,不给表编号。 ```markdown {title="源码"} | 条目 | 取值 | | --- | --- | | 主题版本 | v0.8.1 | | Hugo 下限 | 0.160.1 Extended | | 许可证 | Apache-2.0 | {caption="本站当前使用的主题事实"} ``` | 条目 | 取值 | | --- | --- | | 主题版本 | v0.8.1 | | Hugo 下限 | 0.160.1 Extended | | 许可证 | Apache-2.0 | {caption="本站当前使用的主题事实"} ## 兼容矩阵 {#matrix} `{.matrix}` 用于「行 × 列 = 支持与否」的对照表:第一列成为行表头(`th scope="row"`),滚动时表头行与第一列吸附不动,其余单元格居中,分隔行另有对齐时以分隔行为准。✅ 与 ❌ 是作者写的字符,主题不解析它们。 ```markdown {title="源码"} | OS / PG | PG18 | PG17 | PG16 | PG15 | PG14 | | --- | :---: | :---: | :---: | :---: | :---: | | EL 9 | ✅ | ✅ | ✅ | ✅ | ✅ | | EL 8 | ✅ | ✅ | ✅ | ✅ | ✅ | | Debian 13 | ✅ | ✅ | ✅ | ❌ | ❌ | | Ubuntu 24.04 | ✅ | ✅ | ✅ | ✅ | ❌ | {.matrix} ``` | OS / PG | PG18 | PG17 | PG16 | PG15 | PG14 | | --- | :---: | :---: | :---: | :---: | :---: | | EL 9 | ✅ | ✅ | ✅ | ✅ | ✅ | | EL 8 | ✅ | ✅ | ✅ | ✅ | ✅ | | Debian 13 | ✅ | ✅ | ✅ | ❌ | ❌ | | Ubuntu 24.04 | ✅ | ✅ | ✅ | ✅ | ❌ | {.matrix} ## 用整个画布 {#full-width} `{.full-width}` 让表格越出正文栏宽,占满文章可用的宽度。适合列多但每列都短的表。 ```markdown {title="源码"} | 语言 | 代码 | 侧栏 | 搜索 | 目录 | 打印 | 状态 | | --- | --- | --- | --- | --- | --- | --- | | 简体中文 | `zh` | ✅ | ✅ | ✅ | ✅ | 已审校 | | English | `en` | ✅ | ✅ | ✅ | ✅ | 已审校 | {.full-width} ``` | 语言 | 代码 | 侧栏 | 搜索 | 目录 | 打印 | 状态 | | --- | --- | --- | --- | --- | --- | --- | | 简体中文 | `zh` | ✅ | ✅ | ✅ | ✅ | 已审校 | | English | `en` | ✅ | ✅ | ✅ | ✅ | 已审校 | {.full-width} ## 参数表 {#fields} `{.fields}` 把表格变成定义列表:第一列是名称,最后一列是说明,中间列是元数据。它是记录配置项、命令参数、API 字段的形态,写法见[参数表](/zh/docs/components/fields/)。 ```markdown {title="源码"} | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | `offline_search` | boolean | `false` | 构建本地搜索索引 | | `page_width` | string | `normal` | 正文栏宽度 | {.fields meta="type default"} ``` | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | `offline_search` | boolean | `false` | 构建本地搜索索引 | | `page_width` | string | `normal` | 正文栏宽度 | {.fields meta="type default"} ## 编号表 {#numbered} 写书或长手册时给表编号:`num` 加可选的 `#id` 与 `caption`。表格会被包进一个带本地化「表 N.」标签的 `
`,并注册成 Book 目标,可以被 `xref` 引用、进入全书表格目录。编号由作者书写,主题不自动计数;`id` 缺省是 `tbl-`。 ```markdown {title="源码"} | 隔离级别 | 脏读 | 不可重复读 | 幻读 | | --- | --- | --- | --- | | 读已提交 | 否 | 是 | 是 | | 可重复读 | 否 | 否 | 是 | | 可串行化 | 否 | 否 | 否 | {#tbl-iso num="9-1" caption="PostgreSQL 各隔离级别允许的异象"} 参见 {{< xref tbl="9-1" anchor="tbl-iso" >}}。 ``` | 隔离级别 | 脏读 | 不可重复读 | 幻读 | | --- | --- | --- | --- | | 读已提交 | 否 | 是 | 是 | | 可重复读 | 否 | 否 | 是 | | 可串行化 | 否 | 否 | 否 | {#tbl-iso num="9-1" caption="PostgreSQL 各隔离级别允许的异象"} 参见 [表 9-1](#tbl-iso)。 ## 表格做成标签页 {#tabs} 连着的表格加 `{tab="…"}` 就组成一组标签页,规则与相邻围栏一致:第一张表上的 `group` 启用 hash、同步与持久化,此后每张表都要 `value`。完整规则见[标签页](/zh/docs/components/tabs/)。 ```markdown {title="源码"} | 目录 | 内容 | | --- | --- | | `content/` | 页面 | | `data/` | 首页与发布数据 | {tab="内容" group="repo-layout" value="content"} | 目录 | 内容 | | --- | --- | | `assets/` | SCSS 与图片资源 | | `static/` | 原样拷贝的文件 | {tab="资源" value="assets"} ``` | 目录 | 内容 | | --- | --- | | `content/` | 页面 | | `data/` | 首页与发布数据 | {tab="内容" group="repo-layout" value="content"} | 目录 | 内容 | | --- | --- | | `assets/` | SCSS 与图片资源 | | `static/` | 原样拷贝的文件 | {tab="资源" value="assets"} ## 输出形态 {#outputs} | 输出 | 呈现 | | --- | --- | | HTML | `
` 可聚焦滚动区 + ``;矩阵与全宽是这个包装器上的修饰 class | | 打印 | 完整表格按页宽排版;包装器仍在,但标成 `td-table-scroll--static`,不再是可聚焦视口 | | Markdown | 原样输出源码表格与属性行 | | RSS | 完整静态表格 | 表格不加载任何脚本。 ## 参数参考 {#reference} 表格下一行的属性行: | 参数 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `.full-width` | 标记 | 无 | 越出正文栏宽,占满文章画布 | | `.matrix` | 标记 | 无 | 第一列作行表头,表头与首列吸附,其余单元格居中 | | `.fields` | 标记 | 无 | 渲染成定义列表,见[参数表](/zh/docs/components/fields/) | | `caption` | 纯文本 | 无 | 可见表格标题;在 `.fields` 上是列表的标签 | | `meta` | 角色列表 | 无 | 命名 `.fields` 中间列的语义,取值 `type` `required` `default` `-`;必须与 `.fields` 同用 | | `#id` | 标识符 | 有 `num` 时为 `tbl-` | `[A-Za-z][A-Za-z0-9_.:-]*`;写在 `
`(编号表则写在 `
`)上 | | `num` | 字符串 | 无 | `[0-9A-Za-z.-]+`;注册为 Book 表目标,标题前加「表 N.」 | | `tab` / `group` / `value` | 见[标签页](/zh/docs/components/tabs/) | 无 | 相邻表格组成标签页 | | `class` | class 列表 | 无 | 站点 CSS 用,原样留在 `
` 上 | | `data-*` / `aria-*` | 字符串 | 无 | 透传 | {.fields meta="type default"} `style`、`on*` 与其它键会告警并忽略;严格发布构建拒绝这条警告。 ## 限制与常见问题 {#limits} - 互斥规则:`.fields` 不能和 `.matrix`、`.full-width` 或 `num` 一起用;`num` 与 `tab` 互斥;`group`/`value` 需要 `tab`;`meta` 需要 `.fields`。 - 属性行必须紧贴表格:中间空一行,它就变成正文里一段可见的花括号。Markdown 格式化工具常移动这一行,把它包进 `` / ``。 - 没有合并单元格、没有排序、没有筛选:GFM 管道表格能表达的就是全部。需要合并表头的复杂表请拆成两张表或改成一张矩阵。 - 单元格里放不下块内容:多段说明、列表、围栏要用 `fields`/`field` shortcode。 - `.matrix` 的居中由 CSS 实现:分隔行里写了对齐就以分隔行为准。 ## 相关 {#related} - [参数表](/zh/docs/components/fields/) — `{.fields}` 的完整写法 - [标签页](/zh/docs/components/tabs/) — 相邻表格组成标签页 - [书籍出版](/zh/docs/write/book/) — 编号表、交叉引用与表格目录 - [代码块](/zh/docs/components/code/) — 属性写在信息行而不是下一行 --- 反链: - [ECharts](/zh/docs/components/echarts/) - [参数表](/zh/docs/components/fields/) - [标签页](/zh/docs/components/tabs/) - [打印支持](/zh/docs/customize/print/) - [书籍出版](/zh/docs/write/book/) - [编写页面](/zh/docs/write/pages/) ================ Source: https://oink.pgsty.com/zh/docs/components/fields/index.md ================ # 参数表 > 用一张普通表格加 `{.fields}` 记录配置项、命令参数与 API 字段:名称、类型、默认值、说明各就各位,窄屏不挤,每条都能单独链接。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- 参数表(Fields)把「一串具名值 + 元数据 + 说明」渲染成响应式定义列表:名称独占一行,类型、是否必填、默认值是名称旁边的小字,说明另起一行,每一条自带锚点。用于配置项、命令参数与 API 字段。要按同一批列横向比较很多行时用普通表格,内容是操作顺序时用步骤。 写法有两种:普通表格加 `{.fields}`(默认选它),以及 `fields`/`field` shortcode(说明需要多个段落、列表或代码块时才用)。两种形态渲染出相同的条目。 ## 最简例子 {#minimal} 一张至少两列的管道表格,下一行写 `{.fields}`。第一列是名称,最后一列是说明,中间每一列都是元数据,标签就是表头文字本身。 ```markdown {title="源码"} | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | `offline_search` | boolean | `false` | 构建本地搜索索引并启用命令面板 | | `offline_search_max_results` | integer | `10` | 搜索结果条数上限 | | `page_width` | string | `normal` | 正文栏宽度,可选 `narrow` `normal` `wide` | {.fields} ``` | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | `offline_search` | boolean | `false` | 构建本地搜索索引并启用命令面板 | | `offline_search_max_results` | integer | `10` | 搜索结果条数上限 | | `page_width` | string | `normal` | 正文栏宽度,可选 `narrow` `normal` `wide` | {.fields} 这里的元数据显示成「表头: 值」。主题不推断表头的含义,`类型` 只是一个标签;要让它变成标准芯片见下一节。单元格接受行内 Markdown(代码、强调、链接),空的中间单元格省略。 ## 语义列 `meta=` {#meta} `meta` 按顺序说明每一个中间列扮演什么角色:`type`(类型)、`required`(必填)、`default`(默认值),或者 `-`(保留表头当标签)。有了它,表格形态渲染出的芯片与 shortcode 形态一致。 ```markdown {title="源码"} | 参数 | 类型 | 必填 | 默认值 | 说明 | | --- | --- | --- | --- | --- | | `baseURL` | string | 是 | | 站点地址,含子路径 | | `title` | string | 是 | | 站点名,出现在顶栏与页签 | | `defaultContentLanguage` | string | | `en` | 默认语言,决定无前缀路径属于哪种语言 | {.fields meta="type required default"} ``` | 参数 | 类型 | 必填 | 默认值 | 说明 | | --- | --- | --- | --- | --- | | `baseURL` | string | 是 | | 站点地址,含子路径 | | `title` | string | 是 | | 站点名,出现在顶栏与页签 | | `defaultContentLanguage` | string | | `en` | 默认语言,决定无前缀路径属于哪种语言 | {.fields meta="type required default"} 规则: - `meta` 应为每一个中间列写一个角色,个数等于总列数减二;写多写少时告警并忽略 `meta`,严格发布构建拒绝这条警告。 - `required` 列是「非空即真」:单元格里写「是」「yes」「✔」都一样,渲染出来的是不翻译的 `required` 芯片;留空就不显示。 - `type` 与 `default` 单元格如果本身没有行内标记,会自动套上代码格式,与 shortcode 形态对齐。 - 三种语义芯片按 `type`、`required`、`default` 的顺序显示,与列的顺序无关;`-` 列跟在后面,按列顺序排。 `-` 可以和语义角色混用,用来保留一列自定义标签: ```markdown {title="源码"} | 环境变量 | 类型 | 作用域 | 说明 | | --- | --- | --- | --- | | `HUGO_MODULE_WORKSPACE` | string | 构建 | 指向 `go.work`,让主题从本地 checkout 解析 | | `HUGO_ENV` | string | 构建 | 设为 `production` 时启用压缩与指纹 | {.fields meta="type -"} ``` | 环境变量 | 类型 | 作用域 | 说明 | | --- | --- | --- | --- | | `HUGO_MODULE_WORKSPACE` | string | 构建 | 指向 `go.work`,让主题从本地 checkout 解析 | | `HUGO_ENV` | string | 构建 | 设为 `production` 时启用压缩与指纹 | {.fields meta="type -"} ## 标签与容器 ID {#caption-id} `caption` 给整张表加一个可见标签(同时是无障碍名称),`id` 命名外层容器,方便从别处链接过来或写站点 CSS。 ```markdown {title="源码"} | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | `enable` | boolean | `false` | 打开图片缩放 | | `selector` | string | `.td-content` | 扫描候选图片的根选择器 | {.fields caption="params.ui.image_zoom" id="zoom-params" meta="type default"} ``` | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | `enable` | boolean | `false` | 打开图片缩放 | | `selector` | string | `.td-content` | 扫描候选图片的根选择器 | {.fields caption="params.ui.image_zoom" id="zoom-params" meta="type default"} ## 每一条都能单独链接 {#anchors} 每个条目获得一个 `field-<名称>` 形式的锚点,鼠标移上去时名称右边出现自链接图标。上面第一张表里的 `page_width` 就是 [#field-page_width](#field-page_width),回答问题时可以把这一行的链接单独发出去。 同一页里重名的字段按 `-2`、`-3` 顺延,规则与 Goldmark 处理重名标题一致。锚点只在 HTML 里生成:打印和 RSS 会把很多页拼成一个文档,页内锚点在那里会冲突。 ## shortcode 形态 {#shortcode} 说明需要多个段落、列表或代码块时,表格单元格装不下,改用 `fields`/`field`: ````markdown {title="源码"} {{< fields label="pig 命令常用参数" >}} {{< field name="--config" type="path" required=true >}} 配置文件路径。相对路径按当前工作目录解析。 如果同时设置了 `PIG_CONFIG` 环境变量,命令行参数优先。 {{< /field >}} {{< field name="--log-level" type="string" default="info" >}} 日志级别,从低到高: - `debug`:打印每一次远程调用 - `info`:默认值 - `error`:只在失败时输出 {{< /field >}} {{< field name="--dry-run" type="boolean" default=false >}} 只打印将要执行的动作,不改任何东西: ```bash pig ext install pg_duckdb --dry-run ``` {{< /field >}} {{< /fields >}} ```` **pig 命令常用参数** - `--config` — `path`; required 配置文件路径。相对路径按当前工作目录解析。 如果同时设置了 `PIG_CONFIG` 环境变量,命令行参数优先。 - `--log-level` — `string`; default: `info` 日志级别,从低到高: - `debug`:打印每一次远程调用 - `info`:默认值 - `error`:只在失败时输出 - `--dry-run` — `boolean`; default: `false` 只打印将要执行的动作,不改任何东西: ```bash pig ext install pg_duckdb --dry-run ``` `required=true` 与 `default=false` 是布尔值,不加引号。`default` 接受任何标量:`default=0`、`default=""` 都会如实显示(空字符串显示成 `""`),不写 `default` 就不显示这一项。每个 `field` 必须有非空正文,并且必须是 `fields` 的直接子项。 ## 两种形态的选择 {#which} | 情况 | 用法 | | --- | --- | | 每条说明一句话,能放进表格单元格 | 表格 + `{.fields}` | | 说明要分段、带列表或代码块 | `fields`/`field` shortcode | | 读者需要按同一批列横向比较很多行 | 用普通表格,不转成参数表 | | 内容是操作顺序 | 用[步骤](/zh/docs/components/steps/) | 表格形态在 GitHub 上仍然是一张可读的表,OINK 的 Markdown 输出也保持表格原样,这是默认选它的理由。 ## 输出形态 {#outputs} | 输出 | 呈现 | | --- | --- | | HTML | `
` + 语义 `
`;条目带 `#field-<名称>` 锚点与自链接 | | 打印 | 完整定义列表,不带条目锚点 | | Markdown | 表格形态保留源码表格;shortcode 形态输出「`名称` — 类型;required;default: 值」加缩进说明的项目符号列表 | | RSS | 完整静态 `
`,不带条目锚点 | 不加载任何脚本。 ## 参数参考 {#reference} 表格属性行(写在表格下一行): | 参数 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | `.fields` | 标记 | 无 | 必需;把表格渲染成参数表 | | `meta` | 角色列表 | 无 | 空格分隔,取值 `type` `required` `default` `-`;个数等于中间列数;语义角色不可重复 | | `caption` | 纯文本 | 无 | 可见标签,同时是列表的无障碍名称 | | `id` | 标识符 | 无 | 外层容器的 ID | | `class` | class 列表 | 无 | 透传给站点 CSS | | `data-*` / `aria-*` | 字符串 | 无 | 透传 | {.fields meta="type default"} `fields` shortcode: | 参数 | 类型 | 必填 | 说明 | | --- | --- | --- | --- | | `label` | 非空字符串 | 否 | 可见标签,作用同表格的 `caption` | | `id` | 标识符 | 否 | 外层容器 ID;不能含空白、引号、`<`、`>`、`&` | | `class` / `data-*` / `aria-*` | 字符串 | 否 | 与表格属性行同一套策略 | {.fields meta="type required"} `field` shortcode: | 参数 | 类型 | 必填 | 说明 | | --- | --- | --- | --- | | `name` | 非空字符串 | 是 | 字段名 | | `type` | 非空字符串 | 否 | 类型标签,如 `boolean` `string[]` `duration` | | `required` | 布尔 | 否 | `true` 时显示不翻译的 `required` 芯片,默认 `false` | | `default` | 标量 | 否 | 字符串 / 布尔 / 整数 / 浮点;`false`、`0`、`""` 都会显示 | {.fields meta="type required"} ## 限制与常见问题 {#limits} - 第一列必须非空,且在同一张表内唯一:重名或空名时告警并跳过该行,严格发布构建 拒绝这条警告。 - `.fields` 不能与 `.matrix`、`.full-width`、`num` 组合,`meta` 不能用在没有 `.fields` 的表上。 - 表格单元格里放不下块内容:需要段落、列表、围栏就换 shortcode 形态。 - `required` 与 `default` 是不翻译的 API 词汇,在所有语言下都显示英文,它们是契约词,不是界面文案。 - 暂不支持 `kind`、`since`、`deprecated`、`location`、字段级链接与嵌套结构,也不会在构建时解析 TypeScript 或 OpenAPI schema。 ## 相关 {#related} - [表格](/zh/docs/components/table/) — 属性行的其它取值与互斥规则 - [配置总览](/zh/docs/customize/config/) — 站点参数全表就是用参数表写的 - [页面参数](/zh/docs/write/frontmatter/) — front matter 全表 - [步骤](/zh/docs/components/steps/) — 顺序动作不要写成参数表 --- 反链: - [卡片](/zh/docs/components/cards/) - [表格](/zh/docs/components/table/) - [Agent 支持](/zh/docs/customize/agents/) - [打印支持](/zh/docs/customize/print/) - [编写页面](/zh/docs/write/pages/) ================ Source: https://oink.pgsty.com/zh/docs/components/steps/index.md ================ # 步骤 > 有序列表加 `{.steps}` 就是带编号圆点与竖线的操作步骤;步骤要带标题、要进目录时改用 steps shortcode。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- 步骤(Steps)是带编号圆点与竖线的有序列表:一个普通有序列表,加一行 `{.steps}` 标记,编号圆点与串起它们的竖线由 CSS 绘制,不加载脚本。用于有先后的操作流程。并列而无先后的内容用普通列表或卡片。 写法有两种:有序列表加 `{.steps}`(默认选它),以及 `{{% steps %}}` shortcode,每一步要有自己的标题、标题还要进右侧目录时用它。 ## 最简例子 {#minimal} 每一项都写 `1.`,让 Markdown 自己数。这样插入、删除、调换步骤都不用手改编号,而且内容缩进恒定是三个空格。 ```markdown {title="源码"} 1. 安装 Hugo Extended 1. 克隆 OINK Starter 1. 启动本地预览 {.steps} ``` 1. 安装 Hugo Extended 1. 克隆 OINK Starter 1. 启动本地预览 {.steps} `{.steps}` 必须紧贴列表最后一行,中间空一行它就会变成正文里一段可见的花括号。 ## 步骤内容 {#blocks} 列表项里可以放任何块级内容:段落、代码围栏、提示块、表格、嵌套列表、图片。缩进对齐到列表项的内容列(三个空格)即可。 ````markdown {title="源码"} 1. 克隆 OINK Starter,它是面向项目的精简模板。 ```bash git clone https://github.com/pgsty/oink-starter my-docs cd my-docs ``` 1. 启动本地服务器。 ```bash hugo server ``` > [!NOTE] > 首次构建会通过 Go 模块代理拉取主题,需要本机安装 Go。 1. 替换三处内容,它就是你的站点。 | 位置 | 替换为 | | --- | --- | | `hugo.yml` 的 `title` | 你的站名 | | `hugo.yml` 的 `baseURL` | 你的域名 | | `content/` | 你的内容 | {.steps} ```` 1. 克隆 OINK Starter,它是面向项目的精简模板。 ```bash git clone https://github.com/pgsty/oink-starter my-docs cd my-docs ``` 1. 启动本地服务器。 ```bash hugo server ``` > [!NOTE] > 首次构建会通过 Go 模块代理拉取主题,需要本机安装 Go。 1. 替换三处内容,它就是你的站点。 | 位置 | 替换为 | | --- | --- | | `hugo.yml` 的 `title` | 你的站名 | | `hugo.yml` 的 `baseURL` | 你的域名 | | `content/` | 你的内容 | {.steps} `{{< … >}}` 形式的 shortcode(标签页、卡片、徽章等)也可以写在列表项里;`{{% … %}}` 形式不行,见下面的[限制](#limits)。 ## 一步里按平台分开 {#tabs-in-steps} 某一步在不同平台上命令不同时,把带 `{tab=}` 的围栏并排写进那个列表项,它们照样会合成标签页。 `````markdown {title="源码"} 1. 安装 Hugo Extended。 1. 安装依赖: ```bash {tab="EL / RHEL" group="stepdemo" value="rpm"} sudo dnf install golang git ``` ```bash {tab="Debian / Ubuntu" value="deb"} sudo apt install golang-go git ``` 1. 运行 `hugo server` 预览。 {.steps} ````` 1. 安装 Hugo Extended。 1. 安装依赖: ```bash {tab="EL / RHEL" group="stepdemo" value="rpm"} sudo dnf install golang git ``` ```bash {tab="Debian / Ubuntu" value="deb"} sudo apt install golang-go git ``` 1. 运行 `hugo server` 预览。 {.steps} ## 接着上一组往下编号 {#start} 正文隔断了一组步骤时,把新一组的第一项写成它实际的序号,Markdown 会输出 `start`,编号从那里继续(支持到 40)。 ```markdown {title="源码"} 4. 配置 `baseURL` 与部署工作流。 1. 推送到 `main`,等待 GitHub Actions 构建完成。 {.steps} ``` 4. 配置 `baseURL` 与部署工作流。 1. 推送到 `main`,等待 GitHub Actions 构建完成。 {.steps} ## 带标题的步骤 {#shortcode} 步骤本身很长、每一步该有个能被链接和被目录收录的标题时,用 `{{% steps %}}`:它的正文是页面级 Markdown,里面的每一个直接子标题就是一步,正文不用缩进。下面三步的标题就在这一页的右侧目录里。 ```markdown {title="源码"} {{% steps %}} ### 安装工具链 {#install-toolchain} 需要 Hugo Extended ≥ 0.160.1 与 Go。 ### 启动服务器 {#run-server} {{< tabs group="oink-os" default="macos" >}} {{< tab label="macOS" value="macos" >}} `brew install hugo go` {{< /tab >}} {{< tab label="Debian" value="debian" >}} `sudo apt install hugo golang-go` {{< /tab >}} {{< /tabs >}} ### 发布 {#publish} 推送到 `main`,仓库自带的工作流会构建并发布。 {{% /steps %}} ``` ### 安装工具链 {#install-toolchain} 需要 Hugo Extended ≥ 0.160.1 与 Go。 ### 启动服务器 {#run-server} **macOS** `brew install hugo go` **Debian** `sudo apt install hugo golang-go` ### 发布 {#publish} 推送到 `main`,仓库自带的工作流会构建并发布。 它是主题里唯一的 `{{% … %}}` shortcode。百分号形式的正文交给 Goldmark 当页面级 Markdown 处理:只有这样,里面的标题才能进目录,里面才能放 `tabs`、`cards`、`fields` 这些容器 shortcode。代价是它自己不能嵌进列表项,也不能嵌进另一个百分号容器。 同一组步骤的标题保持同一层级,不要把一个 `steps` 套进另一个里。 ## 两种形态的选择 {#which} | 情况 | 用法 | | --- | --- | | 步骤是一两句话加一段命令 | 有序列表 + `{.steps}` | | 每一步需要标题、需要被链接、需要进目录 | `{{% steps %}}` | | 步骤里要放 `tabs`、`cards`、`fields` 容器 | `{{% steps %}}` | | 步骤本身要嵌在另一个列表项里 | 有序列表 + `{.steps}` | ## 输出形态 {#outputs} | 输出 | 呈现 | | --- | --- | | HTML | 原生形态是 `
    `,编号与竖线由 CSS 画;shortcode 形态是 `
    ` 加各级标题 | | 打印 | 编号与内容照旧,竖线保留 | | Markdown | 原样输出源码:有序列表加 `{.steps}`,或标题加正文 | | RSS | 静态列表 / 标题分节 | 不加载脚本;关闭 JavaScript 后呈现不变。 ## 参数参考 {#reference} 两种形态都没有参数,只有写法约定: | 写法 | 位置 | 说明 | | --- | --- | --- | | `{.steps}` | 有序列表下一行 | 必需;写在无序列表上不生效 | | `1.` | 每一项 | 让 Markdown 自己数;内容缩进恒为三个空格 | | `4.`(首项) | 第一项 | 输出 `
      `,编号从 4 接着走,支持 2–40 | | `{{% steps %}}` | 包住若干标题 | 直接子标题(`##`–`######`)就是步骤;正文不缩进 | {.fields meta="-"} ## 限制与常见问题 {#limits} - 列表项里不能写 `{{% … %}}`:百分号 shortcode 的多行输出会把列表截断。要在步骤里放容器就整组改用 shortcode 形态。 - `{{% steps %}}` 不能放进列表项,也不能套在另一个百分号容器里。 - 标记要紧贴列表:`{.steps}` 与列表之间不能有空行;经过 Prettier 之类的格式化工具时,把它包进 `` / ``。 - `{.steps}` 只对有序列表有效:写在 `-` 开头的无序列表上不会有编号。 - 步骤不折叠、不记进度:没有「已完成」状态,也没有展开收起。 ## 相关 {#related} - [标签页](/zh/docs/components/tabs/) — 按平台分开的命令 - [提示块](/zh/docs/components/callout/) — 某一步里的前提与警告 - [代码块](/zh/docs/components/code/) — 步骤里的命令 - [卡片](/zh/docs/components/cards/) — 步骤做完之后的「下一步」 --- 反链: - [Oink v0.2.0](/zh/blog/release/0.2.0/) - [Asciinema](/zh/docs/components/asciinema/) - [提示块](/zh/docs/components/callout/) - [卡片](/zh/docs/components/cards/) - [参数表](/zh/docs/components/fields/) - [Infographic](/zh/docs/components/infographic/) - [按键](/zh/docs/components/kbd/) - [Mermaid](/zh/docs/components/mermaid/) - [标签页](/zh/docs/components/tabs/) - [编写页面](/zh/docs/write/pages/) ================ Source: https://oink.pgsty.com/zh/docs/components/cards/index.md ================ # 卡片 > 用带 `{.cards}` 的链接列表排出导航卡片网格;需要图标、徽章、图片时改用 shortcode。 --- LLMS 索引: [llms.txt](/zh/llms.txt) --- 卡片(Cards)是一组并列的链接:每张卡片一个链接标题加一句描述,网格随容器宽度自适应。适合栏目首页、「接下来读什么」与几条并列路径的入口。不适合排版正文段落(用普通段落)或做图片墙(用[画廊](/zh/docs/components/gallery/))。 ## 最简例子 {#minimal} 带 `{.cards}` 的链接列表就是卡片。链接是标题,` — ` 之后是描述。 ```markdown {title="源码"} - [快速上手](/zh/docs/start/) — 克隆这个文档站,删掉不需要的页面,替换为你的站点信息。 - [创作内容](/zh/docs/write/) — 页面怎么组织、front matter 有哪些键。 - [定制站点](/zh/docs/customize/) — 导航、搜索、品牌、多语言。 {.cards} ``` - [快速上手](/zh/docs/start/) — 克隆这个文档站,删掉不需要的页面,替换为你的站点信息。 - [创作内容](/zh/docs/write/) — 页面怎么组织、front matter 有哪些键。 - [定制站点](/zh/docs/customize/) — 导航、搜索、品牌、多语言。 {.cards} 整张卡片是点击热区,不只是标题文字。没有 `columns` 参数:列数由容器宽度决定,窄屏收成一列。 ## 只有标题的卡片 {#title-only} 描述可以省略。一行一个链接,`{.cards}` 收尾。 ```markdown {title="源码"} - [提示块](/zh/docs/components/callout/) - [标签页](/zh/docs/components/tabs/) - [步骤](/zh/docs/components/steps/) - [参数表](/zh/docs/components/fields/) {.cards} ``` - [提示块](/zh/docs/components/callout/) - [标签页](/zh/docs/components/tabs/) - [步骤](/zh/docs/components/steps/) - [参数表](/zh/docs/components/fields/) {.cards} ## 松散列表与多段描述 {#loose} 一句话装不下时改用松散列表:链接单独一段,描述另起一段,列表项之间空一行。标题独占一行,描述在标题下方。`{.cards}` 仍然紧贴最后一段,中间 **不能有空行**。 ```markdown {title="源码"} - [页面参数](/zh/docs/write/frontmatter/) 每个页面参数在这里有唯一定义:类型、默认值、取值范围,以及讲它的那一页。 - [配置总览](/zh/docs/customize/config/) 站点参数按功能分组,同样每行反向链接到讲它的指南页。 {.cards} ``` - [页面参数](/zh/docs/write/frontmatter/) 每个页面参数在这里有唯一定义:类型、默认值、取值范围,以及讲它的那一页。 - [配置总览](/zh/docs/customize/config/) 站点参数按功能分组,同样每行反向链接到讲它的指南页。 {.cards} ## 图标与徽章 {#icon-badge} 链接列表不支持图标、徽章、图片与多段描述,这些用 `cards` / `card` shortcode。`icon` 是恰好一对 Font Awesome class,`badge` 是一段纯文本。 ```markdown {title="源码"} {{< cards >}} {{< card title="快速上手" link="/zh/docs/start/" icon="fa-solid fa-rocket" badge="从这里开始" >}} 使用 OINK Starter,在定制前建立本地预览基线。 {{< /card >}} {{< card title="发布与下载页" link="/zh/docs/write/releases/" icon="fa-solid fa-box-open" badge="v0.5" >}} `release` 事实记录 + 资产表 + 校验和,全部本地生成。 {{< /card >}} {{< card title="键盘导航" link="/zh/docs/customize/keyboard/" icon="fa-solid fa-keyboard" >}} 全站快捷键与焦点顺序。 {{< /card >}} {{< /cards >}} ``` - [快速上手](/zh/docs/start/) (从这里开始) — 使用 OINK Starter,在定制前建立本地预览基线。 - [发布与下载页](/zh/docs/write/releases/) (v0\.5) — `release` 事实记录 + 资产表 + 校验和,全部本地生成。 - [键盘导航](/zh/docs/customize/keyboard/) — 全站快捷键与焦点顺序。 图标不是一对有效的 Font Awesome class 时,普通预览告警并丢弃图标;严格发布构建 拒绝这条警告。 ## Markdown 正文 {#markdown-body} `card` 的正文按页面级 Markdown 渲染:行内代码、强调、链接、列表都可以。`title`、`badge` 这些参数是纯文本,不解析 Markdown。 ```markdown {title="源码"} {{< cards >}} {{< card title="Hugo Module" icon="fa-brands fa-golang" >}} `hugo mod get github.com/pgsty/oink`。推荐方式,升级只需改一行版本号。 {{< /card >}} {{< card title="Git Submodule" icon="fa-solid fa-code-branch" >}} 无需安装 Go: - `git submodule add` - 主题落在 `themes/oink` {{< /card >}} {{< /cards >}} ``` - **Hugo Module** — `hugo mod get github.com/pgsty/oink`。推荐方式,升级只需改一行版本号。 - **Git Submodule** — 无需安装 Go: - `git submodule add` - 主题落在 `themes/oink` 不写 `link` 的卡片渲染成加粗标题,不生成链接。 ## 带图片的卡片 {#image} `image` 与 `![alt](src)` 的解析顺序一致:页面资源 → 全局资源 `assets/` → 静态路径 `/images/…` → 远程 URL。本地资源带上固有尺寸,避免加载跳版。 `image` 需要一个替代文字来源:`image_alt="…"`(有信息的图)或 `decorative=true`(纯装饰)。两个都写时告警并保留 alt;两个都不写时告警并按装饰图 渲染。严格发布构建会拒绝任一警告。 ```markdown {title="源码"} {{< cards >}} {{< card title="OINK 文档外壳" link="/zh/docs/about/features/" image="images/content-primitives/oink.webp" image_alt="OINK 文档页面:侧栏、正文与目录三栏" >}} 侧栏、正文、目录,三栏可以单独关闭。 {{< /card >}} {{< card title="发布说明" link="/zh/docs/write/releases/" image="/images/releasenote.webp" decorative=true >}} 装饰性封面:`decorative=true` 输出空 alt,读屏器会跳过它。 {{< /card >}} {{< /cards >}} ``` - [OINK 文档外壳](/zh/docs/about/features/) — 侧栏、正文、目录,三栏可以单独关闭。 - [发布说明](/zh/docs/write/releases/) — 装饰性封面:`decorative=true` 输出空 alt,读屏器会跳过它。 卡片图片不参与[图片缩放](/zh/docs/components/image/#zoom),整张卡片本身已经是链接。 ## 栏目首页的自动卡片 {#section-index} 栏目首页(`_index.md`)不需要手写卡片列表:主题读子页的 `title`、`description`、`icon` 自动生成一组卡片。本站在 `hugo.yml` 中全局启用: ```yaml {title="hugo.yml"} params: ui: section_index: cards # list | cards ``` 单个栏目可以在自己的 front matter 里覆盖,也可以用 `cascade` 把选择推给整棵子树: ```yaml {title="content/docs/customize/_index.zh.md"} section_index: list ``` 自动卡片与手写卡片使用同一套 `td-content-card` 样式,区别只在数据来源。栏目首页不要手写子页清单:手写清单会与侧栏不同步。要排的内容不是本栏目的子页时(例如混合站外链接、跨栏目推荐),才在正文里手写卡片。相关键的完整定义见[配置总览](/zh/docs/customize/config/)。 ## 两种形态的选择 {#forms} | 你要的 | 用哪种 | | --- | --- | | 一句话描述的链接网格 | `{.cards}` 链接列表 | | 图标、徽章、图片 | `cards` / `card` shortcode | | 描述里要列表、代码、多段 | `cards` / `card` shortcode | | 没有链接的卡片 | `cards` / `card` shortcode | | 本栏目的子页 | 什么都不写,靠 `section_index: cards` | 链接列表在 GitHub 上仍是一个链接列表,shortcode 不是。能用原生形态时用原生形态。 ## 输出形态 {#outputs} | 输出 | 呈现 | | --- | --- | | HTML | 原生形态是 `