模板¶
模板负责呈现,不重复源文档的正文。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 下的全部文件和子目录。包含或导入模板时,路径以该目录为基准:
{% extends "base.html" %}
{% block header %}
{% include "partials/header.html" %}
{% endblock %}
{% block footer %}
{% include "partials/footer.html" %}
{% endblock %}
Jinja 宏也可定义在单独的文件中,按需导入:
{% macro badge(label, type="info") %}
<span class="badge badge-{{ type }}">{{ label }}</span>
{% endmacro %}
{% import "macros/components.html" as ui %}
{{ ui.badge("New", type="success") }}
用 !layout.html 继承主题¶
若只需修改默认主题的部分内容,无需重写整页。用 Sphinx 风格的感叹号语法继承 Guidedog 的内置布局:
{# 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 中的模板:
root_doc = "contents"
[html_additional_pages]
index = "indexcontent.html"
download = "download.html"
页面模板通常继承 layout.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 组合:
#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 生成的模板开始,其中展示了各部分的用法:
- 保留默认样式表依赖的
layout.html标记,或同时替换_static/guidedog.css。 - 将重复部分移入单独文件并用
include引入,或建立base.html,让layout.html通过extends继承。 - 图书可修改
book函数中的规则,或写同名、同参数的新函数。
编辑时用 guidedog serve 预览。保存源文件后刷新浏览器,服务器会先重建变化的输入,再提供页面。