File: notes.typ

package info (click to toggle)
cloc 2.04-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 7,776 kB
  • sloc: perl: 29,368; cpp: 1,219; ansic: 334; asm: 267; makefile: 240; sh: 186; sql: 144; java: 136; ruby: 111; cs: 104; python: 84; pascal: 52; lisp: 50; cobol: 35; f90: 35; haskell: 35; objc: 25; php: 22; javascript: 15; fortran: 9; ml: 8; xml: 7; tcl: 2
file content (187 lines) | stat: -rw-r--r-- 4,920 bytes parent folder | download | duplicates (2)
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()