Notist Book
Notist Book 由 Site 的 builtins.book 扩展和同名内置主题组成。扩展提供章节数据,主题通过统一的 SSG 模板与静态资源机制呈现阅读界面;两者可独立替换。
SUMMARY.not 或 SUMMARY.md 安排阅读顺序;select = true 时同时选择发布章节。没有 SUMMARY 时按目录组织导航。页面 URL 由 Site 路由配置与源文件路径决定。
命令与配置
Book 使用 package 的 [site]。命令行见 Notist CLI,字段和路径基准见 Notist.toml 配置。
包根就是 Notist.toml 所在目录,content 指定内容目录。build 生成站点文件;serve 提供本地预览,文件变化后自动重建,失败时保留上次可用预览。
预览中的错误和警告会显示在终端及浏览器诊断面板中。使用 --no-clear-screen 保留每轮终端输出。
项目的 Notist.toml 分别选择扩展和主题:
[]
= "my-docs"
[]
= "My Book"
= "."
= "builtins.book"
= true
= false
= ["drafts/**", "**/AGENTS.md"]
[]
= trueinclude 默认允许所有文档,exclude 默认为空;匹配对象相对 content,* 不跨目录,** 跨目录。SUMMARY 的 select 默认 false,只组织导航;上例显式启用章节选择。点开头的条目、target、node_modules、输出目录及主题输入不参与正文发现。
include / exclude 都使用 glob;AGENTS.md 只匹配根目录,**/AGENTS.md 匹配任意目录(包含根目录)。不使用 gitignore 的无斜杠简写或 ! 重新包含规则。无效 glob 使构建失败。
外部目录挂载
通过 mounts 将 Vault 外的目录加入书籍,而不必将资产归档移进文档目录。例如项目的 docs/ 是 Vault,art/ 与它同级:
[]
= "my-book"
[]
= "."
= "builtins.book"
= true
= false
= { = true }
= "../build/book"
= ["**"]
= ["**/AGENTS.md", "art/**/source/**"]
[[]]
= "../art"
= "art"source 相对于包根,也可使用绝对路径;path 是内容命名空间内的相对目录。挂载后的 art/models/drill/README.md 对应源文件 ../art/models/drill/README.md,生成 art/models/drill/index.html。include / exclude、目录派生导航和 page.route.path 使用挂载后的内容路径。
挂载内容使用相同的 include / exclude 和 SUMMARY 规则。例如在 Vault 根目录的 SUMMARY 中写 [模型](art/models/) 选择整个目录,或写 [钻头](art/models/drill/README.md) 选择单页。
发布内容包括选中的章节及其引用的资源。例如文档引用 renders/hero.png 和 model/drill.glb 时,这两个文件按挂载后的相对路径复制到输出目录。
正文相对链接与 Code 导入仍按源文件的真实位置解析。例如 ../art/models/drill/README.md 内的 renders/hero.png 指向同一资产目录中的渲染图;它也可以通过真实相对路径链接回 docs/ 中的章节或另一个已挂载目录。Vault 内的正文和 SUMMARY 还可以直接用 art/... 这样的挂载路径引用外部内容。已发布章节链接改写为相对目录 URL,资源链接改写为相对文件 URL,保留 query 与 fragment 并使用统一的 URL 编码。未挂载的外部内容仍遵守下文的 warning / error 规则。
source 必须是现有目录,且不能位于 Vault 内或包含 Vault;不同挂载的源目录不能相同或相互包含。path 必须非空,不能是绝对路径、包含上级跳转或占用 _notist;挂载路径之间不能重叠,也不能遮盖 Vault 中已有的文件或目录。输出目录不得与挂载源目录或 Vault 内的挂载路径重叠。文件系统构建仍拒绝符号链接资源。
挂载页面使用当前包的扩展配置。serve 同时监听挂载目录的文件变化。
挂载文档的相对 Path 按源文档目录解析,以 / 开头的 Path 从挂载源目录定位。Path 的用法见 Path 字面量。
输出与配置作用域
Site 配置发现、路由与展示,顶层 extensions 配置包的文档扩展,字段见 Notist.toml 配置。文档沿自身的 #import 加载依赖,规则绑定不进入文档作用域。
构建成功后更新输出目录并清理过期产物;失败时保留原有文件。输出目录的配置与安全限制见 Notist.toml 配置。
路由与导航
directory_urls = true 时,以下源路径相对于 content:
源文件 | URL | 输出文件 |
|---|---|---|
README.not | / | index.html |
guide/README.md | /guide/ | guide/index.html |
guide/start.not | /guide/start/ | guide/start/index.html |
README 的匹配区分大小写。所有已注册文档前端使用相同规则;路径段进行 URL 编码。标题不改变 URL。同一个输出路径出现多个文档时报告冲突,例如 README.not 与 README.md,或 guide.not 与 guide/README.md。
没有 SUMMARY 时,导航按目录层次与文件名自然排序,同目录的 README 排在其他页面之前。没有 README 的目录显示为无链接的分组。页面标题取第一个标题,没有标题时使用文件名;根 README 的标题还可回退到书名。
内置主题提供章节导航、面包屑、上一篇 / 下一篇、页内目录、阅读进度和代码复制,支持移动端与明暗模式。serve 在缺少根 README 时将 / 重定向到第一个页面。
SUMMARY:阅读结构
在 Vault 根目录创建 SUMMARY.not 或 SUMMARY.md,列出需要发布的章节及顺序。两种格式选择一种;SUMMARY 专用于导航,即使被 include / exclude 排除也会读取。
结构语义对齐 mdBook SUMMARY,使用 .not 的标题、列表、链接与分隔线语法:
= 目录
[首页](README.not)
= 入门
- [开始使用](guide/start.not)
- [进阶](guide/advanced.md)
= 参考
- [参考](reference.not)
- [计划中的章节]()
---
[贡献者](contributors.not)
SUMMARY.md 使用 Markdown 语法,章节可以混合指向 .not 和 Markdown;目录发现与 collapsed 注解同样可用,例如:
- --
-
章节列表使用无序列表;.not 使用减号,Markdown 支持减号、星号或加号。导航标题按用户书写的文字显示,不生成章节编号。需要编号时,直接写进链接文字或页面标题,例如:
- [1. 开始使用](guide/start.not)
- [1.1. 进阶](guide/advanced.md)
- [附录 A:参考](reference.not)
编号作为标题文字显示,增删或移动章节时需自行调整。
支持以下结构元素:
文件标题:开头可写一个
= 目录,仅说明结构文件,不设置书名;省略也可。后续的一级标题作为分节标题,其他标题级别忽略。前置章节:首个章节列表之前的独立链接,例如
[首页](README.not),只在顶层显示。分节标题:
= 入门为后续章节添加不可点击的视觉分隔,章节仍保持原来的层级,分节标题不进入面包屑。章节条目:
- [开始使用](guide/start.not),嵌套列表声明子章节,链接文字原样显示。后置章节:章节列表之后的独立链接。进入后置章节后只能继续写独立链接和分隔线。
草稿章节:空目标链接,例如
- [计划中的章节](),在导航中显示为不可用链接,保留其标签,不生成页面或源文件,也不参与上一篇 / 下一篇。分隔线:独占一行的至少三个减号
---,可以放在顶层元素之前、之间或之后,在导航中显示横线。
章节链接指向相对于结构文件的源文件,可以混合 .not 与 Markdown;目标必须存在并符合 include / exclude 等发现条件。链接文字覆盖导航和面包屑标题;文字为空时使用页面标题。页面正文和上一篇 / 下一篇保留页面自身标题。
导航与面包屑严格跟随章节列表的缩进,顶层条目互为同级;首页也遵循相同规则。上一篇 / 下一篇按深度优先前序遍历实际页面,跳过分节、分隔线、草稿及无内容的章节。上例的页面顺序为:首页 → 开始使用 → 进阶 → 参考 → 贡献者。进阶页的面包屑为:开始使用 → 进阶,URL 保持 /guide/advanced/。
纯文字条目(例如 - 玩法设计)用于分组,目录链接(例如 [历史记录](_logs/))用于自动收集页面。两者都可嵌套子章节;目录发现和折叠设置见下文。
SUMMARY 中的章节目标须为本地文档或目录。重复条目、无效目标与不支持的结构会使构建失败。
目录自动发现
适合持续增长的归档或参考资料目录,可以与手工编排的章节混用:
- [首页](README.not)
- 入门
- [开始使用](guide/start.not)
- [历史记录](_logs/)
目录条目按目录层级与文件名自然排序,例如 entry2 排在 entry10 前。有 README 时,分组标题链接到该页面;否则只显示分组文字。链接文字用作分组标题,省略时取 README 标题或目录名。新增页面后无需再修改 SUMMARY。
目录发现同样遵守 include / exclude。
显式页面条目优先:无论出现在目录条目之前、之后还是嵌套在其中,页面都留在显式声明的位置,自动发现时跳过。同一页面显式声明两次仍报错。目录条目附带的手工子条目排在自动子树之前;若其 README 已显式放到别处,该目录条目保留为无链接的章节节点,子章节仍在其下排列。
目录范围重叠时,更具体的目录优先,例如 _logs/2026/ 收集其页面,_logs/ 收集其余页面。使用 ./ 可以收集 Vault 中的其余页面。
分支折叠
默认主题中,有子项的分支都可以手动折叠,初始状态默认展开。在条目的标签前紧邻添加 @(collapsed: true),可让该分支默认折叠,例如:
- [入门](guide/)
- @(collapsed: true)[历史记录](_logs/)
折叠与目录发现相互独立:注解也适用于有手工子条目的页面、无页面章节或草稿。纯文字章节使用独占行的块注解:
- @(collapsed: true)
参考资料
- [语法](syntax.not)
collapsed 必须是 Bool,省略或为 false 时默认展开;没有子项时不显示折叠控件。
当前页面所在的祖先分支自动展开;右侧箭头可手动展开或收起。侧栏会保存滚动位置,桌面端还可拖动右侧边缘调整宽度。
完整示例见仓库 docs/SUMMARY.not。
链接、锚点与资源
Book 将 (Markdown 与 Markup 均支持)及 #embed 引用的图片渲染为 img,description 作为 alt,title 保留在外层 span。按 URL 路径扩展名识别 PNG、JPEG(jpg / jpeg)、GIF、SVG、WebP、AVIF、APNG、BMP 和 ICO,忽略大小写及 query / fragment;本地与远程图片均适用。其他资源仍显示为链接;无图片扩展名的远程 URL 也使用链接。build 与 serve 预览使用相同呈现。
本地链接按源文件位置解析,并指向对应的书籍页面;query 与 fragment 保留。资源也可通过 Path 引用。
标题自动生成锚点,重复时追加 -2、-3 等序号;也可以用 id 注解指定锚点。
正文与资源的发布范围为 Vault 及显式 mounts。缺失页面、文件或锚点的普通链接产生警告;嵌入资源缺失或超出发布范围会阻止构建。
引用的资源按书籍相对路径复制,使用的组件及辅助文件发布到 notist/modules/。notist 是生成资源的保留目录。
代码块高亮
代码块标注语言后即可在构建时生成高亮,支持 Rust、TypeScript / TSX、TOML、Dockerfile、Notist 等。未标注或未知语言按纯文本显示。rust,ignore、rust,no_run 等标签使用对应语言的高亮。
共享的 _notist/highlight.css 提供明暗配色,与页面主题同步。通过 styles 中的附加 CSS 覆盖 --notist-code-* 变量。高亮保留源码空白与末尾换行,复制按钮复制原始内容。
Tera 主题
主题支持三层定制:通过 CSS 变量调整外观,通过 styles / scripts 追加资源,通过 templates / static_dir 覆盖同名模板和静态资源。
[]
= "handbook"
[]
= "Project Handbook"
= "."
= "builtins.book"
= true
= false
= ["styles/palette.css", "styles/content.css"]
= ["scripts/reader.js"]
= {}
附加路径相对 content,也可通过已授权挂载的真实路径选择文件。CSS 在内置样式后按声明顺序加载,JS 在 book.js 后以 defer 加载。static_dir 覆盖主题同名资源;不会自动追踪 CSS url()、JS import 或组件参数中的路径。
轻量定制可只写一个样式表,例如 styles/palette.css:
}
}
配色变量还包括 --bg、--fg、--muted、--line、--code、--surface、--selection;--font-ui 与 --font-code 分别控制界面和代码字体。默认明暗配色基于系统偏好;覆写深色配色时,可结合 prefers-color-scheme 与 data-theme 显式选择规则。
--navigation-width 设置侧栏默认宽度,--book-width 设置整体布局的最大宽度(默认 1440px)。读者保存的侧栏宽度优先于主题默认值;重置后重新使用 --navigation-width。
自定义 page.tera 时,在 head 中保留主题初始化代码,并在目录节点后 include navigation-script,以恢复阅读位置与侧栏设置。
模板引擎使用 Tera 2,模板名是 templates 目录相对路径去掉 .tera。局部模板通过 include 引用,也可通过 extends / block 组织布局。内置 navigation 定义 book_navigation(node, current, ancestors) 组件,递归调用时显式传递当前输出路径和祖先 ID。Tera 不允许循环 include。
默认资源包含独立的 status.css,为 draft、staged 与 outdate 注解提供样式。主题可通过同名资源覆盖,规则见 HTML 内容状态。
theme/
├── templates/
│ ├── page.tera
│ └── title.tera
└── static/
├── book.css
└── extra.css
Book 扩展的数据位于 site.data.book;普通页面上下文沿用 SSG,不增加 Book 专用顶层字段:
beforecompile 解析 SUMMARY 并将章节结构保存在本次构建的扩展实例中;beforetemplate 根据最终 Pages 生成完整的 site.data.book,不通过模板 data 传递中间状态。
字段 | 内容 |
|---|---|
site.title | 站点标题 |
page | title、route、attrs、html、headings、directory、ancestors |
site.data.book.structure | SUMMARY 解析与解析目标后的结构;无 SUMMARY 时为 null |
site.data.book.navigation | 全站共享导航树:id、output、kind、title、attrs、url、current、expanded、children |
site.data.book.order | 阅读顺序对应的输出路径数组 |
site.data.book.pages[page.route.path] | 当前页的 home、ancestors(导航节点 ID)、breadcrumbs、previous、next |
assets | highlight、styles、scripts、components |
导航树只存一份,current 不作为当前页状态使用;主题通过 node.output 与 page.route.output 比较选中状态,通过 expanded 或节点 ID 是否位于当前 ancestors 中决定展开。导航 URL 相对输出根,可用 url(path=node.url, relative_to=page.route.output) 转成当前页相对 URL。breadcrumbs 和相邻页 URL 已相对当前页面。
attrs 来自目标文档根节点的模块注解(@!),不包含局部标题或正文注解;无目标文档的目录节点使用空对象。page、navigation 和 breadcrumbs 使用相同投影:字符串、布尔和数值为 JSON 标量,数组和字典递归转换,Some 展开,Unit、None 与非有限浮点数为 null。这是主题呈现数据,不使用组件的 typed JSON 协议;不参与章节排序或路由。模板默认转义输出,访问可选字段前使用 if 检查,例如在 navigation-label.tera 中用 {% if node.attrs.subtitle %}<small>{{node.attrs.subtitle}}</small>{% endif %}。
<!doctype html>
<html><head><meta charset="utf-8"><title>{{page.title}} · {{site.title}}</title>
{% for style in assets.styles %}<link rel="stylesheet" href="{{style}}">{% endfor %}
{% for script in assets.scripts %}<script src="{{script}}" defer></script>{% endfor %}
</head><body>
{% include 'title' %}
<article>{{page.html | safe}}</article>
{% if assets.components %}<script type="module" src="{{assets.components}}"></script>{% endif %}
</body></html>
title.tera 可使用 {{page.title}}。普通插值转义 HTML,渲染后的 page.html 使用 safe 过滤器。未定义字段与模板语法错误使构建失败。
Library 接口
let mut vault = new;
let mut config = default;
config.theme = Some;
config.extensions.builtins.book = Some;
let output = default.build?;
// output.data.book 包含导航与阅读顺序,output.pages 保持站点页面顺序。
// output.files 保存相对输出路径与完整字节,宿主负责发布。
serve 使用与 build 相同的参数和返回类型,在内存中构建并将可恢复的 Code 错误呈现为诊断节点;HTTP 服务与文件监听由 CLI 提供。
Book 扩展位于 notistssg::builtins::book,通过 SiteConfig 启用,或通过 Builder.withextension 注册。files 保存页面与资源,data 保存扩展数据,warnings 保存链接警告,diagnostics 保存预览中可恢复的内容错误。
GitHub Pages
仓库的 Documentation workflow 使用 flake.lock 固定开发环境,先构建 Grammar WASM,再在仓库根运行 notist build,上传 notist-output/notist 并部署。根 Notist.toml 同时声明 workspace、包身份与发布 docs 内容的 site;docs/README.not 发布到 /。示例包拥有独立发布目录,modules/ 提供模块与组件资源。
push 到 main 与手动运行会构建并部署;pull request 只构建并上传产物。Pages 的发布来源设置为 GitHub Actions,部署使用 github-pages environment。站点内部链接与资源使用相对 URL,可直接发布在仓库子路径下。