feat: robust docs

This commit is contained in:
2026-08-06 17:48:53 -04:00
parent 07e3131f17
commit fde6382617
30 changed files with 1340 additions and 59 deletions
+1 -1
View File
@@ -434,7 +434,7 @@ impl RenderConfig {
///
/// # Errors
///
/// Returns [`Error::Other`](crate::error::Error::Other) if serialization
/// Returns [`Error::Other`] if serialization
/// fails.
pub fn to_yaml(&self) -> Result<String> {
serde_yaml_ng::to_string(self).map_err(Error::other)
+2 -3
View File
@@ -14,7 +14,7 @@
// coursebank:begin data
#let cb-data = (
course: (code: "COURSE 101", title: "Sample Course", term: "2026S"),
course: (code: "COURSE 101", title: "Sample Course", term: "2026s"),
assessment: (id: "sample", title: "Sample assessment", date: "2026-01-01"),
form: (id: "A", count: 1),
totals: (questions: 1, scored: 1, bonus: 0, points: 1.5, bonus-points: 0.0),
@@ -49,8 +49,7 @@
#grid(
columns: (auto, 1fr, auto, 1fr),
gutter: 0.6em,
[*Name*], box(width: 100%, repeat[.]),
[*Student ID*], box(width: 100%, repeat[.]),
[*Name*], box(width: 100%, repeat[.]), [*Student ID*], box(width: 100%, repeat[.]),
)
#v(1em)
+25 -7
View File
@@ -8,7 +8,7 @@
// typst watch templates/exam.typ restyle it against the sample data
//
// The regions ship with sample values so that last command works before any
// export has happened.
// export has happened. Two markers are in play:
//
// // coursebank:begin meta a dictionary of course and form metadata
// // coursebank:end meta
@@ -18,15 +18,19 @@
// An exported document keeps its markers, so exporting into a file you have since
// restyled replaces the questions and leaves the styling alone.
//
// There is no `correct` field on an option, because the render config for the
// paper withholds it. That is deliberate. Do not switch `reveal` to `key`
// here in order to build a solutions copy: export the `key` variant instead,
// or the day you forget an `if` is the day the class gets the answers.
// Note what is *not* in the payload for this variant: there is no `correct` field
// on an option, because the render config for the paper withholds it. That is
// deliberate. Do not switch `reveal` to `key` here in order to build a solutions
// copy — export the `key` variant instead, or the day you forget an `if` is the
// day the class gets the answers.
// ─────────────────────────────────────────────────────────────────────────────
// Metadata
// ─────────────────────────────────────────────────────────────────────────────
// coursebank:begin meta
#let cb-meta = (
course: (code: "COURSE 101", title: "Sample Course", term: "2026S"),
course: (code: "COURSE 101", title: "Sample Course", term: "2026s"),
assessment: (
id: "sample",
title: "Sample assessment",
@@ -40,6 +44,9 @@
)
// coursebank:end meta
// ─────────────────────────────────────────────────────────────────────────────
// Settings
// ─────────────────────────────────────────────────────────────────────────────
// Anything under `extra` in templates/typst.yaml arrives here untouched, which is
// how a course changes the look without editing this file at all.
@@ -71,6 +78,9 @@
#set text(font: body-font, size: body-size, lang: "en")
#set par(justify: false, leading: 0.65em)
// ─────────────────────────────────────────────────────────────────────────────
// Helpers
// ─────────────────────────────────────────────────────────────────────────────
// Markup arrives as content when the render config says `content: content`, and as
// a string when it says `content: str`. Accepting both means switching that
@@ -110,6 +120,10 @@
}
}
// ─────────────────────────────────────────────────────────────────────────────
// The renderer
// ─────────────────────────────────────────────────────────────────────────────
//
// One question, one function. Rename it if you like and set `question-fn` in
// templates/typst.yaml to match. It takes a single dictionary so that turning a
// field on or off in the config never changes this signature.
@@ -152,10 +166,14 @@
if page-per-item { pagebreak(weak: true) }
}
// ─────────────────────────────────────────────────────────────────────────────
// The page
// ─────────────────────────────────────────────────────────────────────────────
#align(center)[
#text(size: 1.4em, weight: "bold", fill: accent)[#cb-meta.assessment.title]\
#text(size: 0.95em)[
#cb-meta.course.code #cb-meta.course.title · #cb-meta.course.term
#cb-meta.course.code #cb-meta.course.title · #cb-meta.assessment.term
]\
#text(size: 0.9em)[
#cb-meta.assessment.at("date", default: "")
+7 -5
View File
@@ -15,7 +15,7 @@
// coursebank:begin data
#let cb-data = (
course: (code: "COURSE 101", title: "Sample Course", term: "2026S"),
course: (code: "COURSE 101", title: "Sample Course", term: "2026s"),
assessment: (id: "sample", title: "Sample assessment", date: "2026-01-01"),
form: (id: "A", count: 1),
totals: (questions: 1, scored: 1, bonus: 0, points: 1.5, bonus-points: 0.0),
@@ -63,7 +63,8 @@
columns: (auto, auto, auto, auto, 1fr),
align: (right, center, center, right, left),
table.header([*\#*], [*Key*], [*Level*], [*Pts*], [*Objectives*]),
..cb-data.questions
..cb-data
.questions
.map(q => (
[#q.number],
[*#q.at("key", default: ()).join("")*],
@@ -71,7 +72,7 @@
[#fmt-points(q.at("points", default: 0))],
[#objectives-of(q)],
))
.flatten()
.flatten(),
)
// ── Partial credit ──
@@ -88,7 +89,7 @@
.pairs()
.map(pair => pair.at(0) + " = " + str(int(calc.round(pair.at(1) * 100))) + "%")
[Question #q.number: #parts.join(", ")]
})
}),
)
}
}
@@ -137,7 +138,8 @@
grid(
columns: (1.2em, 1.4em, 1fr),
gutter: 0.3em,
[#marker], [#(opt.letter + ".")],
[#marker],
[#(opt.letter + ".")],
[
#markup(opt.text)
#{