// DEFAULT TEACHER TEMPLATE (framework default; ADR-0011). // // Lives in an engineering file at `exports/teacher.typ`. Compiled AS MAIN with // the manifest injected: // typst compile --root --input manifest= exports/teacher.typ // // Self-contained (copyable into an engineering file). Identical to student.typ // except the fixed target is "teacher" (so the (kind x target) render matrix in // cph-render shows solutions and proofs). See student.typ for the full notes on // why the include loop lives here (package virtual-root resolution) and on the // OPEN optional-content (`fields`) contract point. #import "@local/cph-render:0.1.0": render-lesson, part-fields, default-heading-numbering // This template IS the teacher build, so the target is fixed. #let target = "teacher" // Read the injected manifest (a path string relative to typst --root). #let manifest = toml(sys.inputs.manifest) #let info = manifest.at("info", default: (:)) #let raw-parts = manifest.at("parts", default: ()) // Assemble each part: include its content fields (computed absolute paths, // resolved against --root) and read scalar fields from /element.toml. // `part-fields` (from cph-render) is the single source of truth for kind->fields. #let parts = raw-parts.map(raw => { let kind = raw.at("kind", default: none) let path = raw.at("path", default: none) let spec = part-fields.at(kind, default: (content: (), optional-content: (), scalars: ())) // Which optional content fields are present on disk (manifest-declared). let present = raw.at("fields", default: ()) let part = (kind: kind) // Required content fields: /.typ (absolute, root-relative). for field in spec.content { part.insert(field, include "/" + path + "/" + field + ".typ") } // Optional content fields: only when the manifest says the file exists. for field in spec.optional-content { if field in present { part.insert(field, include "/" + path + "/" + field + ".typ") } } // Scalar fields come from /element.toml. if spec.scalars.len() > 0 { let element = toml("/" + path + "/element.toml") for field in spec.scalars { let v = element.at(field, default: none) if v != none and v != "" { part.insert(field, v) } } } part }) // Presentation: per-level heading numbering. Default (一、 / 1.1 / 1.1.1) comes // from cph-render; override here per engineering file if desired. #render-lesson( info: info, target: target, parts: parts, heading-numbering: default-heading-numbering, )