Hi everyone!
I probably should’ve asked earlier about the overall design of it before trying to implement it for ~5 days xD
Anyways, I give up
Let me explain what I want with pictures (code is below ofc)
Assume we have this project structure:
.
├── preamble.typ
├── lecture-notes.typ
└── notes/
├── lecture-1.typ
└── lecture-2.typ
I basically want to have each lecture stored in a separate / compilable file. Here’s what the ideal lecture-1.pdf
would look like:
Notice that:
1. In the cover page, we include
- lecture number
- to what course it belongs,
- last edited time of that particular `lecture-1.typ`
- student and instructor's names and emails
- Now, the trickiest part I’ve been struggling with – numbering:
- (before jumping to answer though read to the end, please – it’s not that simple)
- All theorem boxes and equations must use global numbering
- I.e. instead of
Theorem 1.5.4
andEquation 2.1.7
we should haveTheorem 14
orEquation (vii)
Now, without copy-paste and so on, I’d like lecture-notes.typ
to be almost as simple as:
// ...
#include "notes/lecture-1.typ"
#include "notes/lecture-2.typ"
That will render like this:
Here, observe that:
1. The cover page contains almost the same information:
- now we display the course name, instead of lecture number
- a brief description underneath
- last edited time of `lecture-notes.typ`
- names and emails
- **new**: outline
- Each individual lecture now becomes a heading and is listed in the outline
- And again – the trickiest part. Numbering changes:
- Theorem boxes and equations use the numbering of the heading above them as a base.
- So we get stuff like
Theorem 1.2
andEquation 1.5.7
.
I will also mention for the record that I have those things in my preamble, just in case it might be relevant:
All links are underlined with:
#show link: it => {
underline(stroke: (dash: "densely-dotted"), text(fill: blue, it))
}
Only labeled equations are numbered:
// Pay attention to
// /* THE CORRECT NUMBERING DEPENDING ON CONTEXT */
// comments below
#show math.equation: it => {
if it.has("label") {
math.equation(
block: true,
numbering: /* THE CORRECT NUMBERING DEPENDING ON CONTEXT */,
it)
} else {
it
}
}
// after applying the function above referencing needs to be fixed:
#show ref: it => {
let el = it.element
let sup = if it.has("supplement") and it.supplement != auto {
it.supplement + " "
} else {
""
}
if el != none and el.func() == math.equation {
link(el.location(), sup + numbering(
/* THE CORRECT NUMBERING DEPENDING ON CONTEXT */,
counter(math.equation).at(el.location()).at(0) + 1
))
} else {
it
}
}
Theorem boxes are created like this:
// Pay attention to
// 1. the comment inside the definition of the below function....
// 2. /* DEPENDS ON CONTEXT */ comments even further below
// ....It explains how we should change ctheorems.thmbox() params,
// depending on whether it's a subfile (i.e. `notes/lecture-1.typ`) or the root file (`lecture-notes.typ`).
#import "@preview/ctheorems:1.1.0": *
/// Returns a theorem box
/// - identifier (str): is just the name of that box's counter.
/// - head (str): that actual string we see in the rendered box itself, like "Theorem" or "Lemma"
/// - side_color (color, auto): if auto, it is derived from `color` argument.
/// - supplement (str, auto): supplement for references, defaults to `head`
/// - base (str, none): base counter name, can be "heading" or none
/// - base_level: (int, none): number of base levels to use
#let mytheorem(
identifier,
head,
color,
side_color: auto,
supplement: auto,
base: auto,
base_level: auto,
) = {
// 1. If we are in the root file, i.e. `lecture-notes.typ`, we need to set:
// - base = "heading"
// - base_level = none
// - note that `identifier` (i.e. the name of the counter) is the same for all theorem boxes
// so that we count as «Theorem 1», «Remark 2»
// 2. If we are inside a subfile, i.e. `notes/lecture-1.typ`, we need to set:
// - base = none
// - base_level = 1
// - we need to set `identifier` to a unique one for each theorem box.
// so that we cound as «Theorem 1», «Lemma 1», «Theorem 2», «Lemma 2», «Remark 1»
if supplement == auto {
supplement = head
}
if side_color == auto {
side_color = color.darken(20%).saturate(50%)
}
// see https://typst.app/universe/package/ctheorems/ docs
return thmbox(
identifier,
head,
fill: color,
stroke: (left: 0.21em + side_color),
padding: (top: 0pt, bottom: 0pt),
radius: 0pt,
breakable: true,
supplement: supplement,
namefmt: x => [(#x)],
titlefmt: x => [#smallcaps(text(x, weight: "extrabold"))],
bodyfmt: x => [#x],
base: base,
base_level: base_level,
)
}
#let theorem = mytheorem(
/* DEPENDS ON CONTEXT */
"Theorem",
rgb("#edf7ff"),
base: /* DEPENDS ON CONTEXT */,
base_level: /* DEPENDS ON CONTEXT*/
),
#let lemma = mytheorem(
/* DEPENDS ON CONTEXT */
"Lemma",
rgb("#efe6ff").lighten(50%),
base: /* DEPENDS ON CONTEXT */,
base_level: /* DEPENDS ON CONTEXT*/
),
What I’ve tried:
- Just blindly applying (different) preambles both in
notes/lecture-1.typ
andlecture-notes.typ
doesn’t work at all- This is because in the
lecture-notes.typ
, after the first#include "notes/lecture-1.typ
, our styling and numbering become like those intended to be used during separate-lecture compilation. - I.e.
lecture-notes.pdf
becomes almost likenote/lecture-1.pdf
- This is because in the
- Store
state("location")
as eitherroot
orsub
, depending on the file type. And updating that accordingly.- That works for other functions with simple logic like
if (inside here) {static content} else {another static content}
- With many-many attempts didn’t manage to make it work for the preamble function, that has some set and show rules etc
- And the hardest I guess it to make it work for the theorem boxes…
- That works for other functions with simple logic like
End
Absolutely any piece of advice or suggestion on how to implement that are very much appreciated! I’m a beginner in typst tho, so please, try to make it as concrete as possible
Or maybe there are already some templates/packages available that do that or something similar, that you might direct me to
Thanks in advance!