1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
|
/*
https://github.com/tudborg/notes.typ/raw/main/notes.typ
Keep track of a running note counter, and associated notes.
*/
#let note_state_prefix = "notes-"
#let note_default_group = "default"
#let note_default_display_fn(note) = {
h(0pt, weak: true)
super([[#note.index]])
}
#let add_note(
// The location of the note. This is used to derive
// what the note counter should be for this note.
loc,
// The note itself.
text,
// The offset which will be added to the 0-based counter index
// when the index stored in the state.
offset: 1,
// the display function that creates the returned content from put.
// Put can't return a pure index number because the counter
// and state updates need to output content.
display: note_default_display_fn,
// The state group to track notes in.
// A group acts independent (both counter and set of notes)
// from other groups.
group: note_default_group
) = {
let s = state(note_state_prefix + group, ())
let c = counter(note_state_prefix + group)
// find any existing note that hasn't been printed yet,
// containing the exact same text:
let existing = s.at(loc).filter((n) => n.text == text)
// If we found an existing note use that,
// otherwise the note is the current location's counter + offset
// and the given text (the counter is 0-based, we want 1-based indices)
let note = if existing.len() > 0 {
existing.first()
} else {
(text: text, index: c.at(loc).first() + offset, page: loc.page())
}
// If we didn't find an existing index, increment the counter
// and add the note to the "notes" state.
if existing.len() == 0 {
c.step()
s.update(notes => notes + (note,))
}
// Output the note marker
display(note)
}
// get notes at specific location
#let get_notes(loc, group: note_default_group) = {
state(note_state_prefix + group, ()).at(loc)
}
// Reset the note group to empty.
// Note: The counter does not reset by default.
#let reset_notes(group: note_default_group, reset_counter: false) = {
if reset_counter {
counter(note_state_prefix + group).update(0)
}
state(note_state_prefix + group, ()).update(())
}
//
// Helpers for nicer in-document ergonomics
//
#let render_notes(fn, group: note_default_group, reset: true, reset_counter: false) = {
locate(loc => fn(get_notes(loc, group: group)))
if reset {
reset_notes(group: group, reset_counter: reset_counter)
}
}
// Create a new note at the current location
#let note(note, ..args) = {
locate((loc) => add_note(loc, note, ..args))
}
// The quick-start option that outputs something useful by default.
// This is a sane-defaults call to `render_notes`.
#let notes(
size: 8pt,
font: "Roboto",
line: line(length: 100%, stroke: 1pt + gray),
padding: (top: 3mm),
alignment: bottom,
numberings: "1",
group: note_default_group,
reset: true,
reset_counter: false
) = {
let render(notes) = {
if notes.len() > 0 {
set align(alignment)
block(breakable: false, pad(..padding, {
if line != none { line }
set text(size: size, font: font)
for note in notes {
[/ #text(font: "Roboto Mono")[[#numbering(numberings, note.index)]]: #note.text]
}
}))
}
}
render_notes(group: group, reset: reset, reset_counter: reset_counter, render)
}
//
// Examples
//
#set page(height: 5cm, width: 15cm)
= Example
- Having a flexible note-keeping system is important #note[Citation needed]
- It's pretty easy to implement with Typst #note[Fact]
- Everyone really likes the name "Typst" #note[Citation needed]
// Print notes since last print:
#notes()
#pagebreak()
These notes won't be printed on this page, so they accumulate onto next page.
- Hello World #note[Your first program]
- Foo Bar #note[Does not serve beverages]
#pagebreak()
- Orange #note[A Color]
- Blue #note[A Color]
// Notice that the "Citation needed" gets a new index
// because we've re-used it since we printed the initial "Citation needed"
- All colors are great #note[Citation needed]
#notes()
#pagebreak()
#set page(
height: 8cm,
footer: notes(padding: (bottom: 5mm)),
margin: (rest: 5mm, bottom: 3cm)
)
= Footnotes
It is of course also possible to place the notes _in_ the page footer.
This is the way to implement footnotes.
- Black#note[Debateably a color]
- White#note[Debateably a color]
#pagebreak()
- Orange#note[A Color]
- Blue#note[A Color]
- Purple#note[Also a color]
#pagebreak()
= Note groups
This page still has footnotes (using the default note group)
but we can also name a new group for custom notes.
#let mynote = note.with(group: "custom")
#let mynotes = notes.with(
group: "custom",
font: "Comic Neue",
size: 12pt,
numberings: "a.",
line: none,
alignment: right + top
)
- This is in it's own group#mynote[Custom]
- Regular footnote here#note[Regular footnote]
- Same custom note#mynote[Custom]
#mynotes()
|