feat(template): themed blockquotes, references, chapter-based numbering for textbook
- Add themed blockquote styles (课堂小实验, 关键洞察, etc.) via quote block - Add #references() wrapper for bibliography formatting - Implement chapter-based figure/table numbering (1-1, 2-1, ...) - Start each chapter on a new page (except first) - Increase list spacing to match long-form reading rhythm - Fix first-line indent to apply on all paragraphs
This commit is contained in:
@@ -5,6 +5,51 @@
|
|||||||
#import "@preview/codly:1.3.0": *
|
#import "@preview/codly:1.3.0": *
|
||||||
#import "@preview/showybox:2.0.4": showybox
|
#import "@preview/showybox:2.0.4": showybox
|
||||||
|
|
||||||
|
// ── helpers ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
#let content-to-string(content) = {
|
||||||
|
if content.has("text") {
|
||||||
|
content.text
|
||||||
|
} else if content.has("children") {
|
||||||
|
content.children.map(content-to-string).join("")
|
||||||
|
} else if content.has("body") {
|
||||||
|
content-to-string(content.body)
|
||||||
|
} else if content == [ ] {
|
||||||
|
" "
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── blockquote themes ────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
#let blockquote-themes = (
|
||||||
|
("课堂小实验", rgb("#F0FDF4"), rgb("#16A34A")),
|
||||||
|
("关键洞察", rgb("#FFFBEB"), rgb("#D97706")),
|
||||||
|
("工程警示", rgb("#FEF2F2"), rgb("#DC2626")),
|
||||||
|
("前沿瞭望", rgb("#F3E8FF"), rgb("#9333EA")),
|
||||||
|
("历史视角", rgb("#FEF3C7"), rgb("#B45309")),
|
||||||
|
("你知道吗", rgb("#ECFEFF"), rgb("#0891B2")),
|
||||||
|
)
|
||||||
|
|
||||||
|
#let themed-blockquote(body) = {
|
||||||
|
let body-str = content-to-string(body)
|
||||||
|
let theme = blockquote-themes.find(t => body-str.contains(t.at(0)))
|
||||||
|
let (fill-color, stroke-color) = if theme != none {
|
||||||
|
(theme.at(1), theme.at(2))
|
||||||
|
} else {
|
||||||
|
(rgb("#F1F5F9"), rgb("#64748B"))
|
||||||
|
}
|
||||||
|
block(
|
||||||
|
fill: fill-color,
|
||||||
|
stroke: (left: 4pt + stroke-color),
|
||||||
|
inset: (x: 14pt, y: 10pt),
|
||||||
|
radius: (right: 4pt),
|
||||||
|
spacing: 1.2em,
|
||||||
|
body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#let primary = rgb("#1F4E79")
|
#let primary = rgb("#1F4E79")
|
||||||
#let secondary = rgb("#2C3E50")
|
#let secondary = rgb("#2C3E50")
|
||||||
#let accent = rgb("#3467A8")
|
#let accent = rgb("#3467A8")
|
||||||
@@ -92,6 +137,20 @@
|
|||||||
table(..new_children, ..kwargs)
|
table(..new_children, ..kwargs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reference-list block: smaller text, hanging indent, tighter rhythm.
|
||||||
|
// Wrap a 参考文献 / References section's body via `#references[ ... ]`.
|
||||||
|
#let references(body) = block({
|
||||||
|
set text(size: 9.5pt)
|
||||||
|
set par(
|
||||||
|
justify: true,
|
||||||
|
leading: 0.7em,
|
||||||
|
spacing: 0.6em,
|
||||||
|
first-line-indent: 0pt,
|
||||||
|
hanging-indent: 1.5em,
|
||||||
|
)
|
||||||
|
body
|
||||||
|
})
|
||||||
|
|
||||||
#let textbook(
|
#let textbook(
|
||||||
title: none,
|
title: none,
|
||||||
subtitle: none,
|
subtitle: none,
|
||||||
@@ -124,7 +183,7 @@
|
|||||||
justify: true,
|
justify: true,
|
||||||
leading: 0.86em,
|
leading: 0.86em,
|
||||||
spacing: 1.2em,
|
spacing: 1.2em,
|
||||||
first-line-indent: 2em,
|
first-line-indent: (amount: 2em, all: true),
|
||||||
)
|
)
|
||||||
|
|
||||||
show: codly-init
|
show: codly-init
|
||||||
@@ -140,12 +199,29 @@
|
|||||||
show: great-theorems-init
|
show: great-theorems-init
|
||||||
set heading(numbering: "1.1")
|
set heading(numbering: "1.1")
|
||||||
|
|
||||||
// Same 11pt body as default: match list rhythm to default template
|
// 按章编号:每章开始时重置图片/表格计数器
|
||||||
set list(indent: 1.2em, body-indent: 0.45em, spacing: 0.62em)
|
set figure(numbering: (..nums) => {
|
||||||
set enum(indent: 1.2em, body-indent: 0.45em, spacing: 0.62em)
|
let h = counter(heading).get()
|
||||||
|
let ch = if h.len() > 0 { h.first() } else { 1 }
|
||||||
|
numbering("1-1", ch, nums.pos().first())
|
||||||
|
})
|
||||||
|
|
||||||
|
// List rhythm: spacing ≥ leading (0.86em), slightly looser than body for long-form reading
|
||||||
|
set list(indent: 1.2em, body-indent: 0.45em, spacing: 0.98em)
|
||||||
|
set enum(indent: 1.2em, body-indent: 0.45em, spacing: 0.98em)
|
||||||
|
|
||||||
|
// 每章从新页开始(第一章除外,因为前面已有 pagebreak)
|
||||||
|
let first-chapter = state("first-chapter", true)
|
||||||
|
|
||||||
// Vertical rhythm matches manual (long-form); font sizes differ
|
|
||||||
show heading.where(level: 1): it => {
|
show heading.where(level: 1): it => {
|
||||||
|
counter(figure.where(kind: image)).update(0)
|
||||||
|
counter(figure.where(kind: table)).update(0)
|
||||||
|
|
||||||
|
context if not first-chapter.get() {
|
||||||
|
pagebreak()
|
||||||
|
}
|
||||||
|
first-chapter.update(false)
|
||||||
|
|
||||||
v(1.45em)
|
v(1.45em)
|
||||||
text(size: 18pt, weight: "bold", fill: primary, it)
|
text(size: 18pt, weight: "bold", fill: primary, it)
|
||||||
v(0.55em)
|
v(0.55em)
|
||||||
@@ -163,6 +239,9 @@
|
|||||||
v(0.4em)
|
v(0.4em)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Blockquote → themed block
|
||||||
|
show quote.where(block: true): it => themed-blockquote(it.body)
|
||||||
|
|
||||||
set page(
|
set page(
|
||||||
paper: "a4",
|
paper: "a4",
|
||||||
margin: (top: 2.5cm, bottom: 2.5cm, left: 2.5cm, right: 2.5cm),
|
margin: (top: 2.5cm, bottom: 2.5cm, left: 2.5cm, right: 2.5cm),
|
||||||
|
|||||||
Reference in New Issue
Block a user