crates/nornir-docbook/assets/typst/report.typ
// Default nornir documentation template.
// The Rust side reads `sys.inputs` for `title`, `version`, `generated`, and `body`
// (where `body` is already valid typst markup translated from markdown).
//
// `mode` selects how the document is laid out — this is what lets the SVG book be
// rendered PER CHAPTER (small, ~linear typst inputs) instead of as one giant doc
// (typst layout is superlinear in a single huge document). All three modes share
// the exact same styling (fonts, heading rules, colours), so the concatenated
// per-chapter pages are visually identical to the one-shot render:
// * "full" — cover + typst `#outline` TOC + body (the PDF / single-doc path;
// byte-identical to the historical template).
// * "front" — cover + a MANUAL chapter-level TOC (`toc_titles`/`toc_pages`),
// no body — the SVG book's front matter, rendered on its own.
// * "chapter" — no cover, no TOC; the body only, with the page number and the
// heading counter OFFSET (`page_start` / `heading_offset`) so a
// chapter rendered in isolation numbers continuously with its
// siblings.
#import sys: inputs
#let title = inputs.at("title", default: "Documentation")
#let version = inputs.at("version", default: "0.0.0")
#let generated = inputs.at("generated", default: "")
#let cover_image = inputs.at("cover_image", default: "")
#let body = inputs.at("body", default: "")
#let mode = inputs.at("mode", default: "full")
// Page offset (1-based) of this fragment's first page within the whole book, and
// the number of level-1 headings that precede it — both 0/1 for the single-doc
// "full" render, non-trivial only for a per-chapter fragment.
#let page_start = inputs.at("page_start", default: 1)
#let heading_offset = inputs.at("heading_offset", default: 0)
// Manual TOC data for "front" mode: parallel arrays of chapter titles and their
// 1-based start pages.
#let toc_titles = inputs.at("toc_titles", default: ())
#let toc_pages = inputs.at("toc_pages", default: ())
// The book-global page number of the page currently being laid out. In "full"
// mode typst's own page counter is authoritative; in the split modes each
// fragment restarts at 1 locally, so we shift by `page_start`.
#let global_page() = {
if mode == "full" { counter(page).get().first() }
else { page_start + counter(page).get().first() - 1 }
}
#set document(title: title, author: "nornir")
#set page(
paper: "a4",
margin: (x: 2.2cm, y: 2.5cm),
numbering: "1 / 1",
header: context {
if global_page() > 1 [
#set text(8pt, fill: gray)
#smallcaps(title) #h(1fr) v#version
]
},
footer: context {
set text(8pt, fill: gray)
if mode == "full" [
nornir #h(1fr) #counter(page).display("1 / 1", both: true)
] else [
nornir #h(1fr) #global_page()
]
},
)
#set text(font: ("DejaVu Sans", "Inter", "Liberation Sans"), size: 10pt, lang: "en")
#set par(justify: true, leading: 0.65em)
#set heading(numbering: "1.1")
#show heading.where(level: 1): it => {
pagebreak(weak: true)
block(below: 1em, text(weight: "bold", size: 20pt, it.body))
}
#show heading.where(level: 2): it => block(above: 1.2em, below: 0.6em,
text(weight: "bold", size: 14pt, it.body))
#show heading.where(level: 3): it => block(above: 1em, below: 0.4em,
text(weight: "bold", size: 11.5pt, it.body))
#show link: it => text(fill: rgb("#0b66c2"), underline(it))
#show raw.where(block: false): it => box(
fill: rgb("#f2f2f4"), inset: (x: 3pt, y: 1pt), outset: (y: 2pt),
radius: 2pt, text(font: ("DejaVu Sans Mono", "Liberation Mono"), size: 9pt, it),
)
#show raw.where(block: true): it => block(
fill: rgb("#f6f6f8"), inset: 8pt, radius: 4pt, width: 100%,
text(font: ("DejaVu Sans Mono", "Liberation Mono"), size: 8.5pt, it),
)
// ---------- Cover ----------
#let cover() = align(center)[
#if cover_image != "" {
v(2cm)
image(cover_image, width: 70%)
v(0.8cm)
} else {
v(3cm)
}
#text(size: 28pt, weight: "bold")[#title]
#v(0.6cm)
#text(size: 14pt, fill: gray)[version #version]
#v(0.3cm)
#if generated != "" {
text(size: 10pt, fill: gray)[generated #generated]
}
#v(1.5cm)
#line(length: 40%, stroke: 0.5pt + gray)
#v(0.4cm)
#text(size: 9pt, fill: gray)[rendered by *nornir* · typst engine]
]
#if mode == "full" {
// ----- Cover + typst outline + body: the single-doc / PDF path -----
cover()
pagebreak()
outline(title: [Contents], depth: 3, indent: auto)
pagebreak()
eval(body, mode: "markup")
} else if mode == "front" {
// ----- SVG-book front matter: cover + a manual chapter-level TOC -----
cover()
pagebreak()
block(below: 1em, text(weight: "bold", size: 20pt)[Contents])
for i in range(toc_titles.len()) {
let t = toc_titles.at(i)
let p = if i < toc_pages.len() { toc_pages.at(i) } else { 0 }
[#t #box(width: 1fr, repeat[.]) #p\ ]
}
} else {
// ----- One chapter, rendered in isolation with continuous numbering -----
counter(heading).update(heading_offset)
eval(body, mode: "markup")
}