Oink 0.8.0:整个栏目一次取走,侧栏直接当数据读,以及谁链到了这一页
Oink 0.8.0 不改动任何组件 API,也不需要修改内容。三项新增里有两项服务于以程序身份来读的访客。
每一页本来就会多产出一份 .md,它服务的是已经知道自己要哪一页的 agent;
而想读完整份手册的 agent,仍然只能一页一页地爬,边爬边发现链接。
这两种新输出格式回答的是另一半问题:把整个栏目给我,以及在我开始抓之前,先告诉我这个站点里有什么。
第三项是给作为人的读者的:页面右栏现在可以列出有哪些页面链接到它。
概览
LLMSFULL为每个顶层栏目产出一份llms-full.txt:栏目下的每一页,按侧栏阅读顺序,装在一个文件里。NAVJSON为每种语言产出一份navigation.json:侧栏那棵树,以数据形式发布,并由 JSON Schema 定版。params.ui.backlinks在右栏列出链接到本页的页面,索引在构建时从你 Markdown 里本来就有的链接派生。- 三者都是可选的,主题绝不会替你打开。三个都不点名的站点,构建结果与此前逐字节一致。
llms.txt会列出你开启的那些文件,发现入口仍留在 agent 本来就会抓的那个文件里。data/docs_nav.json里没有children键的节点不再中断构建。
全文包:整个栏目一次取走
LLMSFULL 把整个栏目收进一个文件:栏目根目录下的 llms-full.txt,
按侧栏与翻页器呈现的顺序把栏目下每一页依次拼接,每页之前有一行分隔符标出它的来源地址。
对 agent 来说,/docs/llms-full.txt 是一次抓取,而原来的做法是每页一次抓取外加一张要跟着走的链接图;
而且结果是有序的,于是这个栏目读起来像一份手册,而不是一堆页面。
开关在栏目自己的 front matter 上,主题不会把它加进站点的输出集合:
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 一次取走全部中文文档。文件的确切形状等细节见全文包。
导航 JSON
侧栏是站点的目录,读得懂它的 agent 可以在正文上花掉第一次抓取之前,先规划好路线。
NAVJSON 把它作为数据发布出来:每种语言一份 navigation.json,放在语言根目录下。
由站点在首页打开:
这棵树不是对站点结构的第二次描述。它序列化的是侧栏与翻页器本来就在读的那份权威,
走的也是同一个 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://oink.pgsty.com/zh/navigation.json 就是实例。占位条目与完整键表等细节见导航 JSON。
谁链到了这一页
从搜索落到一个页面的读者,只能看到这一页指向哪里,看不到它自己处在什么位置。 反向链接补上的就是这另一半:右栏目录下方多出一个「反链」组,列出有哪些页面链接到它, 默认展开,超过八条折进「再显示 N 条」。一个键就能打开:
单页用 front matter 键 backlinks 覆盖,分区用 cascade 覆盖它下面的所有页面。
索引在构建时从你本来就写好的东西里派生:页面源码里的普通 Markdown 链接,以及 ref / relref。
没有 [[wikilink]] 这类新语法要采纳,没有内容要迁移,也不需要 JavaScript——
链接就在 HTML 里,也在这一页的 Markdown 输出里,关掉脚本的读者一样看得到。
扫描前先剥掉代码围栏与行内代码;指向同一目标的多个链接合并成一条;
自链接、外链与同页锚点都不计入;每种语言各有一张图。
顺序是稳定页面路径,因此同样的内容永远构建出同样的列表;没有页面链进来时,整个区块不出现。
有一处需要说清楚的遗漏:读源码看不见藏在自定义 shortcode 参数里或原始 <a href> 里的 URL,
解析不出来的目标也会被静默丢弃。这是导航,不是链接检查——查断链仍然要用链接检查器。
本站全站开启:看任何一篇文档的右栏就能看到,被引用最多的配置总览列出了四十多个入链。细节见反向链接。
发现入口仍在 llms.txt 里
两个文件都不是某个页面的替代表示,因此都不会出现在 <head> 里,也不会有对应的页面动作。
取而代之的是 llms.txt——agent 本来就会先抓的那个文件——多出一段 ## Full-text bundles
列出本语言的全部全文包,并在站点索引里列出本语言的 navigation.json。
两处条目都只在站点确实发布了对应文件时才出现:主题绝不指向自己没有产出的东西。
没有 children 的导航节点不再中断构建
data/docs_nav.json 里没有 children 键的节点,会让构建以侧栏遍历器内部抛出的一个反射错误告终。
遍历器假定每个节点都带这个键——这对生成的 JSON 成立,对人手写的 JSON 不成立,
因为手写时叶子节点很自然地就写成一个没有 children 的节点。
现在作者写的数据会降级而不是报错:没有子节点的节点,就按它本来的样子渲染成叶子。
升级
不点名就什么都不会变。组件 API 没有改动,也不需要修改内容——两种输出格式在 outputs 里声明,
反向链接是 params.ui 下的一个布尔;三个都不点名的站点,发布出来的东西和 0.7.1 一样。
两种输出格式以及它们产出的东西长什么样,都在 Agent 支持;
反向链接的开关见导航与菜单。
完整清单见 CHANGELOG.md。
