Guidedog 手册 0.2.0
语言
本页内容
Guidedog / 文档 0.2.0

模板

模板负责呈现,不重复源文档的正文。Guidedog 使用 Jinja 生成 HTML,使用 Typst 排版 PDF。

Guidedog 的模板就是普通项目文件,可直接查看和编辑。guidedog quickstart 会将实际使用的模板写入项目:

  • _templates/layout.html 定义 HTML 网站的结构,使用 Jinja。
  • _templates/book.typ 定义 PDF 的版式、字体和封面,使用 Typst。
  • _static/guidedog.css 和 _static/guidedog.js 提供默认样式和浏览器交互。

模板保存在项目目录中,不隐藏在外部包或二进制缓存里。直接编辑后,用 guidedog build 重建,用 guidedog serve 预览。

HTML 模板

Guidedog 用内置 Jinja 引擎渲染根页面模板 layout.html。布局无需挤在一个文件中;也可以拆成局部模板、宏库和多层继承结构。

模块化模板结构

模板可分散在 templates_path 指定目录下的多个文件和子目录中;默认路径为 ["_templates"]:

my-docs/
├── conf.toml
├── index.rst
└── _templates/
    ├── layout.html
    ├── base.html
    ├── partials/
    │   ├── header.html
    │   ├── navigation.html
    │   ├── searchbox.html
    │   └── footer.html
    └── macros/
        └── components.html

Guidedog 会扫描 templates_path 下的全部文件和子目录。包含或导入模板时,路径以该目录为基准:

代码 1 _templates/layout.html
{% extends "base.html" %}

{% block header %}
  {% include "partials/header.html" %}
{% endblock %}

{% block footer %}
  {% include "partials/footer.html" %}
{% endblock %}

Jinja 宏也可定义在单独的文件中,按需导入:

代码 2 _templates/macros/components.html
{% macro badge(label, type="info") %}
  <span class="badge badge-{{ type }}">{{ label }}</span>
{% endmacro %}
代码 3 在模板中使用宏
{% import "macros/components.html" as ui %}
{{ ui.badge("New", type="success") }}

用 !layout.html 继承主题

若只需修改默认主题的部分内容,无需重写整页。用 Sphinx 风格的感叹号语法继承 Guidedog 的内置布局:

代码 4 _templates/layout.html
{# Inherit Guidedog's built-in layout #}
{% extends "!layout.html" %}

{# Inject custom metadata or web fonts into the document head #}
{% block extrahead %}
  {{ super() }}
  <link rel="stylesheet" href="{{ pathto('_static/custom.css', 1) }}">
{% endblock %}

{# Replace or extend the footer with custom content #}
{% block footer %}
  {% include "partials/footer.html" %}
{% endblock %}

{% extends "!layout.html" %} 或 {% extends "basic/layout.html" %} 会读取内置主题模板,避免再次加载项目自身的 _templates/layout.html 而形成递归。

在覆盖的块中调用 {{ super() }},即可保留父模板内容,并在其前后追加内容。

布局块

内置 layout.html 使用与 Sphinx 约定对应的块名:

块 用途
doctype 文档类型声明,默认为 <!DOCTYPE html>。
htmltitle <head> 内的 <title> 元素。
linktags 导航和元信息链接:favicon、index、search、prev、next。
css 样式表链接及内联 CSS 根变量。
scripts JavaScript 脚本,包括搜索索引和交互功能。
extrahead <head> 末尾的插入位置,用于字体、分析脚本或自定义元信息。
header 紧邻主导航栏之前的页头位置。
relbar1 顶部导航栏,包含品牌、版本、搜索和主题切换。
rootrellink 相关链接之前的导航插入位置。
relbaritems 导航栏中的自定义项目。
sidebar1 左侧导航栏容器。
sidebartoc sidebar1 内的目录导航树。
breadcrumbs 正文上方的分层导航路径。
document 包裹页面正文的容器。
body 当前文档的 HTML 正文,即 {{ body }}。
relbar2 底部翻页导航,链接到上一章和下一章。
footer 页脚,包含版权、更新日期和源文件链接。
sidebar2 右侧辅助栏,显示本页目录。

查找模板

Guidedog 按 conf.toml 中 templates_path 的顺序搜索目录,最后搜索内置模板。模板名相对于这些目录,不能用 .. 越出目录。

templates_path 下的每个文件和子目录都是构建依赖。添加、修改或删除模板后,guidedog build 和 guidedog serve 会检测变化并重新渲染网站。

页面变量

Sphinx 已有名称的变量,Guidedog 采用相同名称,因此可迁移部分 Sphinx 主题。

变量 值
body HTML 格式的文档。
title 文档标题。
pagename, docname 文档名称,如 usage/install。非文档页面的 docname 为空,pagename 是页面名,与 Sphinx 一样,例如 genindex、py-modindex、search 或模板生成的页面名。
toc 由目录树生成的 HTML 导航。
outline 文档自身各节的 HTML;没有节时为空。
prev, next 阅读顺序中的相邻页面,含 url(也可用 link)和 title;首尾无相邻页时为空。
project, version, release, copyright, language 同名的配置项。
html_title, docstitle html_title,默认为“<project> <release> documentation”。
html_short_title, shorttitle html_short_title.
root_doc, master_doc 根文档的名称。
pathto_root 从当前页面到站点根目录的路径,例如 ../。
root_url, search_url, genindex_url 根页面、搜索页面和通用索引的链接。
css_files, js_files 带有 url 的文件列表。先列 Guidedog 自有文件,再列 html_css_files 和 html_js_files。
logo_url, favicon_url _static 下的 html_logo 和 html_favicon;未设置时为空。
accent 主题颜色 html_theme_options.accent。
sourcelink_url 显示源码时,指向 _sources 下的文档源文件;否则为空。
last_updated 设置了 html_last_updated_fmt 时,按该格式显示构建日期;否则为空。
show_copyright, show_sphinx, show_guidedog, has_source, show_source html_show_* 和 html_copy_source 配置项。
builder, file_suffix 构建器名称,例如 html,以及页面后缀。
html_context 中的每个键 值及其类型与 Sphinx 传递 Python 值的方式相同:false 在 {% if %} 中为假,数值仍为数值,数组为列表,表为字典,其键保留 conf.toml 顺序。guidedog migrate 迁移 conf.py 的 html_context 字面量条目;计算值保持未定义,模板将其视为假。

pathto 是 Sphinx 的模板函数。pathto("usage/install") 返回文档页面的 URL,pathto("_static/logo.svg", 1) 返回站点根目录下文件的 URL,均相对于当前页面。singlehtml 与 Sphinx 一样,把文档作为单页中的一节:该页上 pathto("usage/install") 为 #document-usage-install;索引和搜索页上为 index.html#document-usage-install。genindex 或下述模板页等非文档名称,在所有构建器中都是站点根目录页面。

由模板生成的页面

html_additional_pages 与 Sphinx 一样,仅用模板创建页面。每个键是页面名称,值是 templates_path 中的模板:

代码 5 conf.toml
root_doc = "contents"

[html_additional_pages]
index = "indexcontent.html"
download = "download.html"

页面模板通常继承 layout.html 并填充其区块,让页面与其他页面风格一致:

代码 6 _templates/indexcontent.html
{% extends "layout.html" %}
{% block htmltitle %}<title>{{ shorttitle }}</title>{% endblock %}
{% block body %}
  <h1>{{ docstitle|e }}</h1>
  <p><a href="{{ pathto("tutorial/index") }}">Tutorial</a></p>
{% endblock %}

模板获得非文档页面的变量:pagename 是页面名(index),title 和 body 为空;pathto、toc、toctree() 和 html_context 值照常可用。html、dirhtml、singlehtml 都将其写为根目录 <name>.html(html_file_suffix),每次构建重写。该模板或 templates_path 任意文件改变,都会重建所有页面。

页面位置及与 Sphinx 的差异:

  • 与 Sphinx 最后写入的行为一样,同名模板页面替换文档页面:index = "landing.html" 使 index.html 成为落地页,但 index.rst 仍提供目录条目。singlehtml 保留单页,不写模板页面,并发出警告。
  • dirhtml 将页面写为 download.html,与 genindex.html 和 search.html 并列;Sphinx 则写为 download/index.html。pathto("download") 返回该文件。
  • 名称必须是根目录文件名。"sub/page" 会被警告拒绝(build.additional_page),因为页面链接以根目录为基准。genindex 等已占用文件名也会被拒绝;Sphinx 在此会覆盖其中一页。
  • guidedog migrate 会迁移 conf.py 中的 html_additional_pages。

错误

模板出错会停止构建,并报告文件、行号、该行内容和修改建议:

TEMPLATE ERROR                                                  template.error

  _templates/base.html:1

No filter named 'defualt'.

Did you mean the filter 'default'?

未定义的变量渲染为空,与 Jinja 一致。

失控模板也会报错停止,而非崩溃:语法嵌套超过 100 层(括号、标签,以及长运算符、过滤器或 elif 链的每一环),宏、包含或递归循环超过 200 层,或任何递归所需栈超过 512 KiB。自包含列表与 Python 一样显示为 [...]。

PDF 模板

PDF 书籍由 Typst 排版。_templates/book.typ 是定义 book 函数的普通 Typst 文件。Guidedog 按以下形式生成书籍源文:

#import "/_templates/book.typ": book
#show: book.with(title: ..., author: ..., version: ..., date: ...,
  lang: ..., paper: ..., numbering: ..., logo: ...)
// the chapters, one per document of the root toctree

因此,决定图书外观的一切都在该文件中:字体、页面尺寸与边距、标题、扉页、页眉和目录。

参数

参数 值
title pdf_documents 中的图书 title,否则为 project。
author 图书的 author,否则为 author 配置项。
version release.
date |today|:若设置了 today 则使用其值,否则按 today_fmt 显示构建日期。
lang language.
paper "a4";若 pdf_paper_size 为 letter,则使用 "us-letter"。
numbering 当 toctree 含 numbered 选项时为 true。
logo pdf_logo 的路径;未设置则不传入。
copyright 供版权页使用的 copyright,仅传给接受此参数的模板。
body 章节内容。

模板可增加带默认值的参数。quickstart 模板包含 accent、字体 serif、sans、mono、字号 size,以及 page-ref。将后者设为 n => [p. #n] 等函数,可为跨页引用附上页码。

模块化 Typst 模板

Typst 样式也无需放在一个文件中。可将样式、宏和封面拆成 _templates/ 下的多个 .typ 文件,再用 #import 和 #include 组合:

代码 7 _templates/book.typ
#import "cover.typ": title-page
#import "typography.typ": apply-styles

#let book(
  title: "",
  author: "",
  version: "",
  date: "",
  lang: "en",
  paper: "a4",
  numbering: false,
  logo: none,
  body,
) = {
  apply-styles()
  title-page(title: title, author: author, version: version, logo: logo)
  body
}

模板可以使用什么

Typst 包

#import "@preview/cetz:0.4.2" 等包可用,首次使用时下载。设置 pdf_packages = "offline",仅使用磁盘上已有的包构建。

字体

可使用系统字体,与 typst 命令相同。通过 pdf_font_paths 增加目录;或设 pdf_fonts = "embedded",只用 Typst 内置字体,实现逐字节可重复的书籍。

前置内容

pdf_preamble 指定 Typst 文件,其内容放在 #show: book 之后。若只需几条 set 或 show 规则,则无需自写完整模板。

文档中的 Typst

文档是 reStructuredText 或 Markdown;Typst 文件不是文档。要将 Typst 标记放入书籍,写入原始块;网页会忽略该块:

.. raw:: typst

   #align(center)[#text(size: 14pt)[Only in the book]]

.. only:: pdf 也可将普通内容限制在图书中。

Typst 报错时,构建会指出文件和行号,无论错误位于模板还是生成书籍。每个 PDF 的源文位于 _build/pdf/sources/<pdf-filename>.typ,如 reference-en.pdf 对应 sources/reference-en.pdf.typ。单本构建也保留熟悉的 _build/pdf/book-<language>.typ 检查副本。失败不发布内容,源码保留在 _build/.doctrees/failed,错误会给出路径。多本书各自保留独立失败源码。

编写新模板

从 quickstart 生成的模板开始,其中展示了各部分的用法:

  1. 保留默认样式表依赖的 layout.html 标记,或同时替换 _static/guidedog.css。
  2. 将重复部分移入单独文件并用 include 引入,或建立 base.html,让 layout.html 通过 extends 继承。
  3. 图书可修改 book 函数中的规则,或写同名、同参数的新函数。

编辑时用 guidedog serve 预览。保存源文件后刷新浏览器,服务器会先重建变化的输入,再提供页面。