# Oink 0.8.0：整个栏目一次取走，侧栏直接当数据读，以及谁链到了这一页

> Oink 0.8.0 为「以程序身份来读」的访客加了两种可选输出格式：把整个栏目装进一个文件的 全文包，以及以 JSON 发布的导航树；再加上静态反向链接，在右栏列出链接到本页的页面。 三者都默认关闭，不点名就不会出现。

---

LLMS 索引： [llms.txt](/zh/llms.txt)

---

Oink 0.8.0 不改动任何组件 API，也不需要修改内容。三项新增里有两项服务于以程序身份来读的访客。
每一页本来就会多产出一份 `.md`，它服务的是已经知道自己要哪一页的 agent；
而想读完整份手册的 agent，仍然只能一页一页地爬，边爬边发现链接。
这两种新输出格式回答的是另一半问题：把整个栏目给我，以及在我开始抓之前，先告诉我这个站点里有什么。
第三项是给作为人的读者的：页面右栏现在可以列出有哪些页面链接到它。

**v0\.8\.0 · 2026-08-27**
- [查看发布](https://github.com/pgsty/oink/releases/tag/v0.8.0)
- [源码 · tar\.gz](https://github.com/pgsty/oink/archive/refs/tags/v0.8.0.tar.gz)
- [源码 · zip](https://github.com/pgsty/oink/archive/refs/tags/v0.8.0.zip)
- [pgsty\/oink](https://github.com/pgsty/oink)

## 概览 {#at-a-glance}

- `LLMSFULL` 为每个顶层栏目产出一份 `llms-full.txt`：栏目下的每一页，按侧栏阅读顺序，装在一个文件里。
- `NAVJSON` 为每种语言产出一份 `navigation.json`：侧栏那棵树，以数据形式发布，并由 JSON Schema 定版。
- `params.ui.backlinks` 在右栏列出链接到本页的页面，索引在构建时从你 Markdown 里本来就有的链接派生。
- 三者都是可选的，主题绝不会替你打开。三个都不点名的站点，构建结果与此前逐字节一致。
- `llms.txt` 会列出你开启的那些文件，发现入口仍留在 agent 本来就会抓的那个文件里。
- `data/docs_nav.json` 里没有 `children` 键的节点不再中断构建。

## 全文包：整个栏目一次取走 {#full-text-bundle}

`LLMSFULL` 把整个栏目收进一个文件：栏目根目录下的 `llms-full.txt`，
按侧栏与翻页器呈现的顺序把栏目下每一页依次拼接，每页之前有一行分隔符标出它的来源地址。
对 agent 来说，`/docs/llms-full.txt` 是一次抓取，而原来的做法是每页一次抓取外加一张要跟着走的链接图；
而且结果是有序的，于是这个栏目读起来像一份手册，而不是一堆页面。

开关在栏目自己的 front matter 上，主题不会把它加进站点的输出集合：

```yaml {title="content/docs/_index.zh.md"}
---
title: 文档
outputs: [HTML, print, RSS, markdown, LLMSFULL]
---
```

front matter 里的 `outputs` 是对站点级列表的整体替换，因此要把这个栏目原本就有的格式一并写回。
它按语言生效，所以中文全文包需要在 `_index.zh.md` 里同样写一遍。

每一页贡献进来的，就是它自己 `.md` 里那份语义 Markdown，而不是第二次渲染的结果。
每页 Markdown 正文已经挪进两种输出共用的同一个 partial，因此全文包中的一段与该页的 `.md` 逐字节相同，
两者不可能各走各的。顺序同样来自侧栏读的那份权威：`docs`、`book` 栏目声明了
`data/docs_nav.json` 显式树时以显式树为准，其余按内容树的 `weight`。不在侧栏里的页面，也不会进全文包。

全文包属于顶层栏目，没有整站版本：想要全部内容的 agent，一个栏目读一份。
写在更深一层的栏目上会告警并且什么都不产出，于是 `hugo server` 照常能用，
而加了 `--panicOnWarning` 的发布构建会停在这里。

本站的文档栏目已经开启，<https://oink.pgsty.com/zh/docs/llms-full.txt> 一次取走全部中文文档。文件的确切形状等细节见[全文包](/zh/docs/customize/agents/#full-text-bundle)。

## 导航 JSON {#navigation-json}

侧栏是站点的目录，读得懂它的 agent 可以在正文上花掉第一次抓取之前，先规划好路线。
`NAVJSON` 把它作为数据发布出来：每种语言一份 `navigation.json`，放在语言根目录下。
由站点在首页打开：

```yaml {title="hugo.yml"}
outputs:
  home: [HTML, LLMS, NAVJSON]
```

这棵树不是对站点结构的第二次描述。它序列化的是侧栏与翻页器本来就在读的那份权威，
走的也是同一个 partial：声明了 `data/docs_nav.json` 显式树的地方以显式树为准，其余按内容树的 `weight`。
有一项检查断言 docs 子树展平后恰好等于全文包产出的页面序列——两条模板路径，一份权威。

每个节点带 `id`（去掉语言前缀的路径，因此同一页在每种语言里 `id` 相同）、绝对地址 `url`、
页面确实产出 `.md` 时的 `markdown` 地址、`title`、`description`、`kind`，以及有序的 `children`。
其中两条值得当作承诺而不是实现细节来读：

- 数组顺序就是契约。顺序已经算好了，`weight` 不会被序列化——消费方再排一次，只会与它来源的侧栏对不上。
- 格式带版本。`schemaVersion` 是 `1`，契约随主题仓库发布，见 [`schema/nav.v1.schema.json`](https://github.com/pgsty/oink/blob/main/schema/nav.v1.schema.json)。要消费这个文件，就拿它做校验。

本站的 <https://oink.pgsty.com/zh/navigation.json> 就是实例。占位条目与完整键表等细节见[导航 JSON](/zh/docs/customize/agents/#navigation-json)。

## 谁链到了这一页 {#backlinks}

从搜索落到一个页面的读者，只能看到这一页指向哪里，看不到它自己处在什么位置。
反向链接补上的就是这另一半：右栏目录下方多出一个「反链」组，列出有哪些页面链接到它，
默认展开，超过八条折进「再显示 N 条」。一个键就能打开：

```yaml {title="hugo.yml"}
params:
  ui:
    backlinks: true
```

单页用 front matter 键 `backlinks` 覆盖，分区用 cascade 覆盖它下面的所有页面。

索引在构建时从你本来就写好的东西里派生：页面源码里的普通 Markdown 链接，以及 `ref` / `relref`。
没有 `[[wikilink]]` 这类新语法要采纳，没有内容要迁移，也不需要 JavaScript——
链接就在 HTML 里，也在这一页的 Markdown 输出里，关掉脚本的读者一样看得到。
扫描前先剥掉代码围栏与行内代码；指向同一目标的多个链接合并成一条；
自链接、外链与同页锚点都不计入；每种语言各有一张图。
顺序是稳定页面路径，因此同样的内容永远构建出同样的列表；没有页面链进来时，整个区块不出现。

有一处需要说清楚的遗漏：读源码看不见藏在自定义 shortcode 参数里或原始 `<a href>` 里的 URL，
解析不出来的目标也会被静默丢弃。这是导航，不是链接检查——查断链仍然要用链接检查器。

本站全站开启：看任何一篇文档的右栏就能看到，被引用最多的[配置总览](/zh/docs/customize/config/)列出了四十多个入链。细节见[反向链接](/zh/docs/customize/navigation/#backlinks)。

## 发现入口仍在 `llms.txt` 里 {#discovery}

两个文件都不是某个页面的替代表示，因此都不会出现在 `<head>` 里，也不会有对应的页面动作。
取而代之的是 `llms.txt`——agent 本来就会先抓的那个文件——多出一段 `## Full-text bundles`
列出本语言的全部全文包，并在站点索引里列出本语言的 `navigation.json`。
两处条目都只在站点确实发布了对应文件时才出现：主题绝不指向自己没有产出的东西。

## 没有 children 的导航节点不再中断构建 {#docs-nav-leaf}

`data/docs_nav.json` 里没有 `children` 键的节点，会让构建以侧栏遍历器内部抛出的一个反射错误告终。
遍历器假定每个节点都带这个键——这对生成的 JSON 成立，对人手写的 JSON 不成立，
因为手写时叶子节点很自然地就写成一个没有 children 的节点。
现在作者写的数据会降级而不是报错：没有子节点的节点，就按它本来的样子渲染成叶子。

## 升级 {#upgrading}

```bash
hugo mod get github.com/pgsty/oink@v0.8.0
hugo mod tidy
```

不点名就什么都不会变。组件 API 没有改动，也不需要修改内容——两种输出格式在 `outputs` 里声明，
反向链接是 `params.ui` 下的一个布尔；三个都不点名的站点，发布出来的东西和 0.7.1 一样。
两种输出格式以及它们产出的东西长什么样，都在 [Agent 支持](/zh/docs/customize/agents/)；
反向链接的开关见[导航与菜单](/zh/docs/customize/navigation/#backlinks)。

完整清单见 [CHANGELOG.md](https://github.com/pgsty/oink/blob/main/CHANGELOG.md)。
