@airmark/quiver 0.35.5 → 0.35.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -4
- package/quills/classic_resume/0.1.0/example.md +53 -59
- package/quills/usaf_letter/0.1.0/packages/tonguetoquill-usaf-letter/src/frontmatter.typ +34 -19
- package/quills/usaf_letter/0.1.0/packages/tonguetoquill-usaf-letter/src/mainmatter.typ +1 -1
- package/quills/usaf_letter/0.1.0/packages/tonguetoquill-usaf-letter/src/primitives.typ +27 -9
- package/quills/usaf_memo/0.2.0/packages/tonguetoquill-usaf-memo/src/body.typ +15 -1
- package/quills/usaf_memo/0.2.0/packages/tonguetoquill-usaf-memo/src/frontmatter.typ +34 -19
- package/quills/usaf_memo/0.2.0/packages/tonguetoquill-usaf-memo/src/primitives.typ +5 -1
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/body.typ +47 -15
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/frontmatter.typ +34 -19
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/primitives.typ +27 -9
- package/templates/classic_resume.md +53 -59
- package/templates/templates.json +0 -7
- package/templates/pass_request.md +0 -49
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@airmark/quiver",
|
|
3
|
-
"version": "0.35.
|
|
3
|
+
"version": "0.35.7",
|
|
4
4
|
"description": "Airmark Quiver — Air Force and DAF document Quills for Quillmark.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
"node": ">=24"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@quillmark/quiver": "0.
|
|
22
|
-
"@quillmark/wasm": "^0.
|
|
23
|
-
"quillkit": "0.
|
|
21
|
+
"@quillmark/quiver": "0.36.0",
|
|
22
|
+
"@quillmark/wasm": "^0.124.0",
|
|
23
|
+
"quillkit": "0.16.0"
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
26
26
|
"test": "quillkit test && node scripts/check-examples.mjs && node scripts/check-templates.mjs",
|
|
@@ -1,109 +1,103 @@
|
|
|
1
1
|
~~~
|
|
2
2
|
$quill: classic_resume
|
|
3
3
|
$kind: main
|
|
4
|
-
name:
|
|
4
|
+
name: Philip J. Fry
|
|
5
5
|
contacts:
|
|
6
|
-
-
|
|
7
|
-
- (
|
|
8
|
-
- github.com/
|
|
9
|
-
- linkedin.com/in/nibsbin/
|
|
6
|
+
- fry@planetexpress.example
|
|
7
|
+
- (212) 555-0199
|
|
8
|
+
- github.com/borb-sh
|
|
10
9
|
- New New York
|
|
11
10
|
paper: us-letter
|
|
12
11
|
font_size: 11.5
|
|
13
12
|
margin: 0.5
|
|
14
13
|
~~~
|
|
15
14
|
|
|
15
|
+
~~~
|
|
16
|
+
$kind: summary
|
|
17
|
+
~~~
|
|
18
|
+
|
|
19
|
+
Delivery boy with a thousand years of experience, most of it frozen. Brings 20th-century know-how to 31st-century problems and has saved Earth more than once. Wants to keep delivering anywhere, ideally with fewer space bees.
|
|
20
|
+
|
|
16
21
|
~~~
|
|
17
22
|
$kind: certifications
|
|
18
23
|
title: Certifications
|
|
19
24
|
columns: 2
|
|
20
25
|
items:
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
26
|
+
- "Career Chip: Delivery Boy"
|
|
27
|
+
- Earth Army Basic Training
|
|
28
|
+
- First Aid, taught by Dr. Zoidberg
|
|
29
|
+
- Food Handler's Permit, New York
|
|
25
30
|
~~~
|
|
26
31
|
|
|
27
32
|
~~~
|
|
28
33
|
$kind: skills
|
|
29
34
|
columns: 2
|
|
30
35
|
skills:
|
|
31
|
-
- label:
|
|
32
|
-
items:
|
|
33
|
-
- label:
|
|
34
|
-
items:
|
|
35
|
-
- label:
|
|
36
|
-
items:
|
|
37
|
-
- label:
|
|
38
|
-
items:
|
|
36
|
+
- label: Logistics
|
|
37
|
+
items: Packages, pizza, interplanetary freight
|
|
38
|
+
- label: 20th-Century Culture
|
|
39
|
+
items: TV, arcade games, Star Trek trivia
|
|
40
|
+
- label: Music
|
|
41
|
+
items: Holophonor, opera composition
|
|
42
|
+
- label: Endurance
|
|
43
|
+
items: Cryogenic sleep, 100 cups of coffee
|
|
39
44
|
~~~
|
|
40
45
|
|
|
41
46
|
~~~
|
|
42
47
|
$kind: experience
|
|
43
48
|
jobs:
|
|
44
|
-
- company: Planet Express
|
|
45
|
-
dates:
|
|
46
|
-
role:
|
|
49
|
+
- company: Planet Express
|
|
50
|
+
dates: December 2999 – Present
|
|
51
|
+
role: Delivery Boy
|
|
47
52
|
location: New New York
|
|
48
53
|
details: |-
|
|
49
|
-
-
|
|
54
|
+
- Deliver packages anywhere in the universe with Captain Turanga Leela and Bender Bending Rodríguez.
|
|
50
55
|
|
|
51
|
-
-
|
|
52
|
-
- company:
|
|
53
|
-
dates:
|
|
54
|
-
role:
|
|
55
|
-
location:
|
|
56
|
-
details: "-
|
|
57
|
-
- company:
|
|
58
|
-
dates:
|
|
59
|
-
role:
|
|
60
|
-
location:
|
|
61
|
-
details: "-
|
|
56
|
+
- Completed runs to robot-only Chapek 9 and a hive of giant space bees, and survived both.
|
|
57
|
+
- company: Earth Army
|
|
58
|
+
dates: November 3000
|
|
59
|
+
role: Private
|
|
60
|
+
location: Spheron 1
|
|
61
|
+
details: "- Enlisted for the military discount; deployed against the Spheroids under Zapp Brannigan."
|
|
62
|
+
- company: Panucci's Pizza
|
|
63
|
+
dates: June 1997 – December 1999
|
|
64
|
+
role: Delivery Boy
|
|
65
|
+
location: New York, NY
|
|
66
|
+
details: "- Delivered pizza across New York City until a prank order to Applied Cryogenics ran 1,000 years late."
|
|
62
67
|
title: Experience
|
|
63
68
|
~~~
|
|
64
69
|
|
|
65
70
|
~~~
|
|
66
71
|
$kind: education
|
|
67
72
|
schools:
|
|
68
|
-
- school:
|
|
69
|
-
dates:
|
|
70
|
-
degree:
|
|
71
|
-
location:
|
|
72
|
-
- school: United States Air Force Academy
|
|
73
|
-
dates: May 2024
|
|
74
|
-
degree: BS, Data Science
|
|
75
|
-
location: Colorado Springs, CO
|
|
76
|
-
details: |-
|
|
77
|
-
- Distinguished Graduate (top 10%); Chinese language minor (L2+/R1 on DLPT).
|
|
78
|
-
|
|
79
|
-
- Professor Bradley A. Warner Data Science Catalyst and Top Cadet in Computer Networks.
|
|
80
|
-
- school: Community College of the Air Force
|
|
81
|
-
dates: February 2029
|
|
82
|
-
degree: AS, Information Systems Technology
|
|
83
|
-
location: Online
|
|
73
|
+
- school: Mars University
|
|
74
|
+
dates: September 3000
|
|
75
|
+
degree: Undeclared, no degree
|
|
76
|
+
location: Mars
|
|
84
77
|
~~~
|
|
85
78
|
|
|
86
79
|
~~~
|
|
87
80
|
$kind: other
|
|
88
|
-
title:
|
|
81
|
+
title: Planetary Defense
|
|
89
82
|
entries:
|
|
90
|
-
- heading:
|
|
91
|
-
details: "-
|
|
92
|
-
- heading:
|
|
83
|
+
- heading: Saved Earth from the Brainspawn
|
|
84
|
+
details: "- Held the line as the only human immune to the Brainspawn, thanks to a missing Delta brainwave."
|
|
85
|
+
- heading: Ended the Omicronian Invasion
|
|
93
86
|
details: |-
|
|
94
|
-
-
|
|
87
|
+
- Restaged the lost finale of *Single Female Lawyer* for Lrrr, ruler of the planet Omicron Persei 8.
|
|
95
88
|
|
|
96
|
-
-
|
|
89
|
+
- Caused the crisis in the first place by knocking the original broadcast off the air in 1999.
|
|
90
|
+
- heading: Deflected a Giant Ball of Garbage
|
|
91
|
+
details: "- Helped knock it off course with a second ball of garbage, built on 20th-century expertise."
|
|
97
92
|
~~~
|
|
98
93
|
|
|
99
94
|
~~~
|
|
100
95
|
$kind: projects
|
|
101
96
|
title: Projects
|
|
102
97
|
projects:
|
|
103
|
-
- name: TongueToQuill
|
|
104
|
-
link: tonguetoquill.com
|
|
105
|
-
details: "- Rich markdown editor for perfectly formatted USAF and USSF documents with Claude MCP integration."
|
|
106
98
|
- name: Quillmark
|
|
107
|
-
link: github.com/
|
|
108
|
-
details: "-
|
|
99
|
+
link: github.com/borb-sh/quillmark
|
|
100
|
+
details: "- Schema-driven document engine that delivers typeset documents from Markdown, this resume included."
|
|
101
|
+
- name: Holophonor Opera
|
|
102
|
+
details: "- Wrote and performed an opera about Leela on the holophonor, using the Robot Devil's hands."
|
|
109
103
|
~~~
|
|
@@ -80,6 +80,12 @@
|
|
|
80
80
|
none
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
let tag-line = if falsey(footer-tag-line) {
|
|
84
|
+
none
|
|
85
|
+
} else {
|
|
86
|
+
align(center, text(fill: LETTERHEAD_COLOR, font: "Spectral SC", size: 15pt)[#footer-tag-line])
|
|
87
|
+
}
|
|
88
|
+
|
|
83
89
|
set par(leading: spacing.line, spacing: spacing.line, justify: false)
|
|
84
90
|
set block(above: spacing.line, below: 0em, spacing: 0em)
|
|
85
91
|
set text(font: body-font, size: font-size, fallback: true)
|
|
@@ -126,14 +132,11 @@
|
|
|
126
132
|
)
|
|
127
133
|
}
|
|
128
134
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
dy: -0.625in,
|
|
133
|
-
|
|
134
|
-
#text(fill: LETTERHEAD_COLOR, font: "Spectral SC", size: 15pt)[#footer-tag-line]
|
|
135
|
-
],
|
|
136
|
-
)
|
|
135
|
+
// Page 1's tag line rides the indicator block's float when there is one.
|
|
136
|
+
if tag-line != none {
|
|
137
|
+
context if cui-indicator == none or here().page() > 1 {
|
|
138
|
+
place(bottom + center, dy: -0.625in, tag-line)
|
|
139
|
+
}
|
|
137
140
|
}
|
|
138
141
|
},
|
|
139
142
|
)
|
|
@@ -142,7 +145,8 @@
|
|
|
142
145
|
// corner, dropped into the 0.5in page-edge band. Emitted as a bottom float so
|
|
143
146
|
// it (1) reserves flow space, raising page 1's effective bottom margin so body
|
|
144
147
|
// text never overlaps it, and (2) stays pinned to page 1 — as the first flow
|
|
145
|
-
// content it can never be bumped to page 2.
|
|
148
|
+
// content it can never be bumped to page 2. Page 1's tag line stands above the
|
|
149
|
+
// block in the same float, since both claim the band.
|
|
146
150
|
if cui-indicator != none {
|
|
147
151
|
context {
|
|
148
152
|
// The box shrink-wraps to its widest line; `set align(left)` keeps the
|
|
@@ -154,24 +158,35 @@
|
|
|
154
158
|
set align(left)
|
|
155
159
|
cui-indicator
|
|
156
160
|
})
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
161
|
+
let block-height = measure(indicator-box).height
|
|
162
|
+
let tag-gap = 0.125in
|
|
163
|
+
let stack-height = if tag-line == none {
|
|
164
|
+
block-height
|
|
165
|
+
} else {
|
|
166
|
+
block-height + tag-gap + measure(tag-line, width: page.width - 2 * spacing.margin).height
|
|
167
|
+
}
|
|
168
|
+
// Reserve only the part of the stack inside the text area (`reserved`):
|
|
169
|
+
// float a box of that height, then `place` the stack inside it pushed down
|
|
170
|
+
// by `overhang` so the surplus overflows into the edge band. (A bare
|
|
160
171
|
// `box(height: reserved, indicator-box)` overflows *upward* into the body
|
|
161
|
-
// instead.) The inner `place`
|
|
172
|
+
// instead.) The inner `place`s add no size, so the box stays `reserved`
|
|
162
173
|
// tall and the block's bottom lands 0.5in from the page edge.
|
|
163
174
|
let overhang = spacing.margin - 0.5in
|
|
164
|
-
let reserved =
|
|
175
|
+
let reserved = stack-height - overhang
|
|
165
176
|
place(
|
|
166
|
-
bottom
|
|
177
|
+
bottom,
|
|
167
178
|
float: true,
|
|
168
|
-
// Slide the right edge into the page-edge band, 0.5in from the border;
|
|
169
|
-
// the inner place right-aligns the block to that edge.
|
|
170
|
-
dx: spacing.margin - 0.5in,
|
|
171
179
|
// Minimum gap to the body's last line; the actual gap is larger when the
|
|
172
180
|
// next paragraph can't fit above the block and breaks to the next page.
|
|
173
181
|
clearance: spacing.line,
|
|
174
|
-
box(
|
|
182
|
+
box(width: 100%, height: reserved, {
|
|
183
|
+
// Slide the block's right edge into the page-edge band, 0.5in from
|
|
184
|
+
// the border.
|
|
185
|
+
place(bottom + right, dx: spacing.margin - 0.5in, dy: overhang, indicator-box)
|
|
186
|
+
if tag-line != none {
|
|
187
|
+
place(bottom + center, dy: overhang - block-height - tag-gap, tag-line)
|
|
188
|
+
}
|
|
189
|
+
}),
|
|
175
190
|
)
|
|
176
191
|
}
|
|
177
192
|
}
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
NESTED.update(n => n + 1)
|
|
77
77
|
context {
|
|
78
78
|
let last = BLOCKS.get() == BLOCKS.final()
|
|
79
|
-
block(above: par.spacing, sticky: last and keeps-signature(it), it)
|
|
79
|
+
block(above: par.spacing, below: par.spacing, sticky: last and keeps-signature(it), it)
|
|
80
80
|
}
|
|
81
81
|
NESTED.update(n => n - 1)
|
|
82
82
|
}
|
|
@@ -197,7 +197,10 @@
|
|
|
197
197
|
// spaces to the right of page center"
|
|
198
198
|
// AFH 33-337 "Do not place the signature element on a continuation page by itself"
|
|
199
199
|
// AFH 33-337 long-name example: "Signature block adjusted to the left" when a
|
|
200
|
-
// long name would otherwise exceed the right margin.
|
|
200
|
+
// long name would otherwise exceed the right margin. Only the name line moves
|
|
201
|
+
// the block; any other line that runs long wraps, its overflow beginning "under
|
|
202
|
+
// the third character of the line above". The handbook gives no measurement,
|
|
203
|
+
// so the indent is the width of the line's own first two characters.
|
|
201
204
|
//
|
|
202
205
|
// A closing line may open the section above the block, and AFH 33-337 gives it
|
|
203
206
|
// one geometry across the two documents that have one: on the second line below
|
|
@@ -215,6 +218,17 @@
|
|
|
215
218
|
/// -> content | none
|
|
216
219
|
#let format-authority-line(value) = if falsey(value) { none } else { upper(value) }
|
|
217
220
|
|
|
221
|
+
// A signature line arrives as `str` or as content; the indent measures its text.
|
|
222
|
+
#let plain-text(it) = {
|
|
223
|
+
if type(it) == str { return it }
|
|
224
|
+
if type(it) != content { return "" }
|
|
225
|
+
if it.has("text") { return plain-text(it.text) }
|
|
226
|
+
if it.has("children") { return it.children.map(plain-text).join(default: "") }
|
|
227
|
+
if it.has("body") { return plain-text(it.body) }
|
|
228
|
+
if it.has("child") { return plain-text(it.child) }
|
|
229
|
+
""
|
|
230
|
+
}
|
|
231
|
+
|
|
218
232
|
#let render-signature-block(
|
|
219
233
|
signature-lines,
|
|
220
234
|
closing-line: none,
|
|
@@ -234,11 +248,11 @@
|
|
|
234
248
|
// anchor. pad() is relative to the text area, hence (4.5in - margin).
|
|
235
249
|
let default-pad = 4.5in - spacing.margin
|
|
236
250
|
context {
|
|
237
|
-
// Measure
|
|
238
|
-
// The closing line shares the anchor, so it joins the
|
|
239
|
-
// wider of the two decides the shift and they stay aligned.
|
|
251
|
+
// Measure the name line at its rendered settings to detect long-name
|
|
252
|
+
// overflow. The closing line shares the anchor, so it joins the
|
|
253
|
+
// measurement: the wider of the two decides the shift and they stay aligned.
|
|
240
254
|
let body-width = page.width - 2 * spacing.margin
|
|
241
|
-
let anchored-lines = signature-lines
|
|
255
|
+
let anchored-lines = signature-lines.slice(0, calc.min(1, signature-lines.len()))
|
|
242
256
|
if closing-line != none { anchored-lines.push(closing-line) }
|
|
243
257
|
let widest = 0pt
|
|
244
258
|
for line in anchored-lines {
|
|
@@ -305,9 +319,9 @@
|
|
|
305
319
|
#pad(left: left-pad)[
|
|
306
320
|
#text(hyphenate: false)[
|
|
307
321
|
#for line in signature-lines {
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
par(hanging-indent: .
|
|
322
|
+
let clusters = plain-text(line).clusters()
|
|
323
|
+
let lead = clusters.slice(0, calc.min(2, clusters.len())).join()
|
|
324
|
+
par(hanging-indent: measure(lead).width, line)
|
|
311
325
|
}
|
|
312
326
|
// The page the backmatter compares its own against. Must stay
|
|
313
327
|
// inside the unbreakable block: a marker outside one travels with
|
|
@@ -393,7 +407,11 @@
|
|
|
393
407
|
/// - it (content): The table element to style and render
|
|
394
408
|
/// -> content
|
|
395
409
|
#let render-memo-table(it) = {
|
|
396
|
-
|
|
410
|
+
// A headless table's first row is a body row, which Typst also numbers 0.
|
|
411
|
+
show table: t => if t.children.any(c => c.func() == table.header) {
|
|
412
|
+
show table.cell.where(y: 0): set text(weight: "bold")
|
|
413
|
+
t
|
|
414
|
+
} else { t }
|
|
397
415
|
set table(
|
|
398
416
|
stroke: 0.5pt + black,
|
|
399
417
|
inset: (x: 0.5em, y: 0.4em),
|
|
@@ -145,6 +145,20 @@
|
|
|
145
145
|
})
|
|
146
146
|
t
|
|
147
147
|
}
|
|
148
|
+
// A `qm-table` wrapper's `align` places its table from outside it, so the
|
|
149
|
+
// placement is buffered with the table, and the emission spans the text
|
|
150
|
+
// width for it to place the table across.
|
|
151
|
+
show align: a => if a.body.func() == table {
|
|
152
|
+
PAR_BUFFER.update(pars => {
|
|
153
|
+
pars.push((
|
|
154
|
+
content: a,
|
|
155
|
+
nest_level: -1,
|
|
156
|
+
kind: "table",
|
|
157
|
+
))
|
|
158
|
+
pars
|
|
159
|
+
})
|
|
160
|
+
none
|
|
161
|
+
} else { a }
|
|
148
162
|
{
|
|
149
163
|
show heading: h => {
|
|
150
164
|
IS_HEADING.update(true)
|
|
@@ -268,7 +282,7 @@
|
|
|
268
282
|
}
|
|
269
283
|
let final_par = {
|
|
270
284
|
if kind == "table" {
|
|
271
|
-
render-memo-table(item_content)
|
|
285
|
+
block(width: 100%, render-memo-table(item_content))
|
|
272
286
|
} else if kind == "continuation" {
|
|
273
287
|
// Continuation block within a multi-block list item:
|
|
274
288
|
// indent to align with preceding numbered paragraph's text, no new number.
|
|
@@ -92,6 +92,12 @@
|
|
|
92
92
|
none
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
let tag_line = if falsey(footer_tag_line) {
|
|
96
|
+
none
|
|
97
|
+
} else {
|
|
98
|
+
align(center, text(fill: LETTERHEAD_COLOR, font: "cinzel", size: 15pt)[#footer_tag_line])
|
|
99
|
+
}
|
|
100
|
+
|
|
95
101
|
// Document-wide typography settings (inlined from configure())
|
|
96
102
|
set par(leading: spacing.line, spacing: spacing.line, justify: false)
|
|
97
103
|
set block(above: spacing.line, below: 0em, spacing: 0em)
|
|
@@ -138,14 +144,11 @@
|
|
|
138
144
|
)
|
|
139
145
|
}
|
|
140
146
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
dy: -0.625in,
|
|
145
|
-
|
|
146
|
-
#text(fill: LETTERHEAD_COLOR, font: "cinzel", size: 15pt)[#footer_tag_line]
|
|
147
|
-
],
|
|
148
|
-
)
|
|
147
|
+
// Page 1's tag line rides the indicator block's float when there is one.
|
|
148
|
+
if tag_line != none {
|
|
149
|
+
context if cui_indicator == none or here().page() > 1 {
|
|
150
|
+
place(bottom + center, dy: -0.625in, tag_line)
|
|
151
|
+
}
|
|
149
152
|
}
|
|
150
153
|
},
|
|
151
154
|
)
|
|
@@ -154,7 +157,8 @@
|
|
|
154
157
|
// corner, dropped into the 0.5in page-edge band. Emitted as a bottom float so
|
|
155
158
|
// it (1) reserves flow space, raising page 1's effective bottom margin so body
|
|
156
159
|
// text never overlaps it, and (2) stays pinned to page 1 — as the first flow
|
|
157
|
-
// content it can never be bumped to page 2.
|
|
160
|
+
// content it can never be bumped to page 2. Page 1's tag line stands above the
|
|
161
|
+
// block in the same float, since both claim the band.
|
|
158
162
|
if cui_indicator != none {
|
|
159
163
|
context {
|
|
160
164
|
// The box shrink-wraps to its widest line; `set align(left)` keeps the
|
|
@@ -166,24 +170,35 @@
|
|
|
166
170
|
set align(left)
|
|
167
171
|
cui_indicator
|
|
168
172
|
})
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
173
|
+
let block_height = measure(indicator_box).height
|
|
174
|
+
let tag_gap = 0.125in
|
|
175
|
+
let stack_height = if tag_line == none {
|
|
176
|
+
block_height
|
|
177
|
+
} else {
|
|
178
|
+
block_height + tag_gap + measure(tag_line, width: page.width - 2 * spacing.margin).height
|
|
179
|
+
}
|
|
180
|
+
// Reserve only the part of the stack inside the text area (`reserved`):
|
|
181
|
+
// float a box of that height, then `place` the stack inside it pushed down
|
|
182
|
+
// by `overhang` so the surplus overflows into the edge band. (A bare
|
|
172
183
|
// `box(height: reserved, indicator_box)` overflows *upward* into the body
|
|
173
|
-
// instead.) The inner `place`
|
|
184
|
+
// instead.) The inner `place`s add no size, so the box stays `reserved`
|
|
174
185
|
// tall and the block's bottom lands 0.5in from the page edge.
|
|
175
186
|
let overhang = spacing.margin - 0.5in
|
|
176
|
-
let reserved =
|
|
187
|
+
let reserved = stack_height - overhang
|
|
177
188
|
place(
|
|
178
|
-
bottom
|
|
189
|
+
bottom,
|
|
179
190
|
float: true,
|
|
180
|
-
// Slide the right edge into the page-edge band, 0.5in from the border;
|
|
181
|
-
// the inner place right-aligns the block to that edge.
|
|
182
|
-
dx: spacing.margin - 0.5in,
|
|
183
191
|
// Minimum gap to the body's last line; the actual gap is larger when the
|
|
184
192
|
// next paragraph can't fit above the block and breaks to the next page.
|
|
185
193
|
clearance: spacing.line,
|
|
186
|
-
box(
|
|
194
|
+
box(width: 100%, height: reserved, {
|
|
195
|
+
// Slide the block's right edge into the page-edge band, 0.5in from
|
|
196
|
+
// the border.
|
|
197
|
+
place(bottom + right, dx: spacing.margin - 0.5in, dy: overhang, indicator_box)
|
|
198
|
+
if tag_line != none {
|
|
199
|
+
place(bottom + center, dy: overhang - block_height - tag_gap, tag_line)
|
|
200
|
+
}
|
|
201
|
+
}),
|
|
187
202
|
)
|
|
188
203
|
}
|
|
189
204
|
}
|
|
@@ -358,7 +358,11 @@
|
|
|
358
358
|
#let render-memo-table(it) = {
|
|
359
359
|
// AFH 33-337 does not specify table formatting, so we follow the general
|
|
360
360
|
// aesthetic principles of the standard: bold headers for clarity.
|
|
361
|
-
|
|
361
|
+
// A headless table's first row is a body row, which Typst also numbers 0.
|
|
362
|
+
show table: t => if t.children.any(c => c.func() == table.header) {
|
|
363
|
+
show table.cell.where(y: 0): set text(weight: "bold")
|
|
364
|
+
t
|
|
365
|
+
} else { t }
|
|
362
366
|
set table(
|
|
363
367
|
stroke: 0.5pt + black,
|
|
364
368
|
inset: (x: 0.5em, y: 0.4em),
|
|
@@ -12,10 +12,20 @@
|
|
|
12
12
|
paragraph-config.numbering-formats.at(level, default: "i.")
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/// The number of the paragraph last emitted at a level. `level-counts` holds
|
|
16
|
+
/// the number the next one takes, so this is one less, and 1 before any.
|
|
17
|
+
///
|
|
18
|
+
/// - level (int): Paragraph nesting level (0-based)
|
|
19
|
+
/// - level-counts (dictionary): Maps level index strings to the next number at each level
|
|
20
|
+
/// -> int
|
|
21
|
+
#let emitted-number(level, level-counts) = {
|
|
22
|
+
calc.max(1, level-counts.at(str(level), default: 1) - 1)
|
|
23
|
+
}
|
|
24
|
+
|
|
15
25
|
/// Calculates indentation for USAF-style paragraphs from explicit counter values.
|
|
16
26
|
///
|
|
17
27
|
/// - level (int): Paragraph nesting level (0-based)
|
|
18
|
-
/// - level-counts (dictionary): Maps level index strings to
|
|
28
|
+
/// - level-counts (dictionary): Maps level index strings to the next number at each level
|
|
19
29
|
/// -> length
|
|
20
30
|
#let calculate-indent-from-counts(level, level-counts) = {
|
|
21
31
|
if level == 0 {
|
|
@@ -23,7 +33,7 @@
|
|
|
23
33
|
}
|
|
24
34
|
let total-indent = 0pt
|
|
25
35
|
for ancestor-level in range(level) {
|
|
26
|
-
let ancestor-value =
|
|
36
|
+
let ancestor-value = emitted-number(ancestor-level, level-counts)
|
|
27
37
|
let ancestor-format = get-paragraph-numbering-format(ancestor-level)
|
|
28
38
|
let ancestor-number = numbering(ancestor-format, ancestor-value)
|
|
29
39
|
total-indent += measure([#ancestor-number#" "]).width
|
|
@@ -56,18 +66,18 @@
|
|
|
56
66
|
/// the width of the number label that precedes it.
|
|
57
67
|
///
|
|
58
68
|
/// This is where a later block of the same paragraph — a continuation, a block
|
|
59
|
-
/// quote — lines up, so the
|
|
60
|
-
/// spelling it. A top-level paragraph carries no indent and its label is the
|
|
69
|
+
/// quote, a table — lines up, so the callers share one measurement rather than
|
|
70
|
+
/// each spelling it. A top-level paragraph carries no indent and its label is the
|
|
61
71
|
/// left margin itself, so the offset is zero.
|
|
62
72
|
///
|
|
63
73
|
/// - level (int): Nesting level (0-based)
|
|
64
|
-
/// - level-counts (dictionary):
|
|
74
|
+
/// - level-counts (dictionary): Maps level index strings to the next number at each level
|
|
65
75
|
/// - indent-fn (function): `(level, level-counts) -> length`
|
|
66
76
|
/// -> length
|
|
67
77
|
#let paragraph-text-offset(level, level-counts, indent-fn) = {
|
|
68
78
|
if level <= 0 { return 0pt }
|
|
69
|
-
let
|
|
70
|
-
let number-text = numbering(get-paragraph-numbering-format(level),
|
|
79
|
+
let number = emitted-number(level, level-counts)
|
|
80
|
+
let number-text = numbering(get-paragraph-numbering-format(level), number)
|
|
71
81
|
indent-fn(level, level-counts) + measure([#number-text#" "]).width
|
|
72
82
|
}
|
|
73
83
|
|
|
@@ -145,17 +155,36 @@
|
|
|
145
155
|
p
|
|
146
156
|
}
|
|
147
157
|
// Tables are buffered whole; nothing inside one takes a paragraph number.
|
|
158
|
+
// The nesting level rides along, as a block quote's does, so a table
|
|
159
|
+
// inside a list item lines up with that item's text.
|
|
148
160
|
show table: t => context {
|
|
161
|
+
let nest-level = NEST_DOWN.get().at(0) - NEST_UP.get().at(0)
|
|
149
162
|
PAR_BUFFER.update(pars => {
|
|
150
163
|
pars.push((
|
|
151
164
|
content: t,
|
|
152
|
-
nest-level: -
|
|
165
|
+
nest-level: nest-level,
|
|
153
166
|
kind: "table",
|
|
154
167
|
))
|
|
155
168
|
pars
|
|
156
169
|
})
|
|
157
170
|
t
|
|
158
171
|
}
|
|
172
|
+
// A `qm-table` wrapper's `align` places its table from outside it, so the
|
|
173
|
+
// placement is buffered with the table, and the emission spans the width
|
|
174
|
+
// left beside its indent for it to place the table across.
|
|
175
|
+
show align: a => if a.body.func() == table {
|
|
176
|
+
context {
|
|
177
|
+
let nest-level = NEST_DOWN.get().at(0) - NEST_UP.get().at(0)
|
|
178
|
+
PAR_BUFFER.update(pars => {
|
|
179
|
+
pars.push((
|
|
180
|
+
content: a,
|
|
181
|
+
nest-level: nest-level,
|
|
182
|
+
kind: "table",
|
|
183
|
+
))
|
|
184
|
+
pars
|
|
185
|
+
})
|
|
186
|
+
}
|
|
187
|
+
} else { a }
|
|
159
188
|
// AFH 33-337 numbers paragraphs and letters subparagraphs, and a body
|
|
160
189
|
// sometimes has to hold lines that are neither: a roster of names, a quoted
|
|
161
190
|
// passage, an address. The block quote is where an author says so — its
|
|
@@ -231,7 +260,7 @@
|
|
|
231
260
|
//
|
|
232
261
|
// PAR_BUFFER item dictionary layout:
|
|
233
262
|
// item.content — the paragraph body, table element, or block-quote body
|
|
234
|
-
// item.nest-level — nesting depth (
|
|
263
|
+
// item.nest-level — nesting depth (0-based)
|
|
235
264
|
// item.kind — "par", "heading", "table", "continuation", or "quote"
|
|
236
265
|
context {
|
|
237
266
|
let heading-buffer = none
|
|
@@ -265,15 +294,14 @@
|
|
|
265
294
|
// A buffered heading runs into this element only when the two belong to
|
|
266
295
|
// the same item: a later block of this list item ("continuation"), or,
|
|
267
296
|
// at top level, the next paragraph. The first block of the *next* item,
|
|
268
|
-
// a table (
|
|
297
|
+
// a table (a block, with no line for a heading to run into), a block
|
|
269
298
|
// quote (verbatim: a heading prepended to it would be ink the author did
|
|
270
299
|
// not put inside the quote), and another heading all fail the test, and
|
|
271
300
|
// each would otherwise carry the heading's text somewhere it was not
|
|
272
|
-
// authored. Those emit the heading on its own line
|
|
273
|
-
// heading before a table takes.
|
|
301
|
+
// authored. Those emit the heading on its own line.
|
|
274
302
|
if heading-buffer != none {
|
|
275
303
|
let runs-in = (
|
|
276
|
-
kind not in ("heading", "quote")
|
|
304
|
+
kind not in ("heading", "quote", "table")
|
|
277
305
|
and item.nest-level == heading-level
|
|
278
306
|
and (heading-level == 0 or kind == "continuation")
|
|
279
307
|
)
|
|
@@ -304,7 +332,12 @@
|
|
|
304
332
|
}
|
|
305
333
|
let final-par = {
|
|
306
334
|
if kind == "table" {
|
|
307
|
-
|
|
335
|
+
// Hangs under the text of the paragraph it sits in, as a block quote
|
|
336
|
+
// does. Its placement and `widths` measure against the width left
|
|
337
|
+
// beside the offset, so it stays inside the text column.
|
|
338
|
+
let placed = block(width: 100%, render-memo-table(item-content))
|
|
339
|
+
let offset = paragraph-text-offset(nest-level, level-counts, indent-fn)
|
|
340
|
+
if offset == 0pt { placed } else { pad(left: offset, placed) }
|
|
308
341
|
} else if kind == "quote" {
|
|
309
342
|
// A block quote is the body's unlabeled block: no number, no letter,
|
|
310
343
|
// no bullet — the author's lines as written. It is placed, not
|
|
@@ -337,7 +370,6 @@
|
|
|
337
370
|
} else if kind == "continuation" {
|
|
338
371
|
// Continuation block within a multi-block list item:
|
|
339
372
|
// indent to align with preceding numbered paragraph's text, no new number.
|
|
340
|
-
// level-counts still holds the value of the preceding numbered paragraph.
|
|
341
373
|
if memo-style == "daf" and nest-level == 0 {
|
|
342
374
|
item-content
|
|
343
375
|
} else {
|
|
@@ -92,6 +92,12 @@
|
|
|
92
92
|
none
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
let tag-line = if falsey(footer-tag-line) {
|
|
96
|
+
none
|
|
97
|
+
} else {
|
|
98
|
+
align(center, text(fill: LETTERHEAD_COLOR, font: "Spectral SC", size: 15pt)[#footer-tag-line])
|
|
99
|
+
}
|
|
100
|
+
|
|
95
101
|
set par(leading: spacing.line, spacing: spacing.line, justify: false)
|
|
96
102
|
set block(above: spacing.line, below: 0em, spacing: 0em)
|
|
97
103
|
set text(font: body-font, size: font-size, fallback: true)
|
|
@@ -137,14 +143,11 @@
|
|
|
137
143
|
)
|
|
138
144
|
}
|
|
139
145
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
dy: -0.625in,
|
|
144
|
-
|
|
145
|
-
#text(fill: LETTERHEAD_COLOR, font: "Spectral SC", size: 15pt)[#footer-tag-line]
|
|
146
|
-
],
|
|
147
|
-
)
|
|
146
|
+
// Page 1's tag line rides the indicator block's float when there is one.
|
|
147
|
+
if tag-line != none {
|
|
148
|
+
context if cui-indicator == none or here().page() > 1 {
|
|
149
|
+
place(bottom + center, dy: -0.625in, tag-line)
|
|
150
|
+
}
|
|
148
151
|
}
|
|
149
152
|
},
|
|
150
153
|
)
|
|
@@ -153,7 +156,8 @@
|
|
|
153
156
|
// corner, dropped into the 0.5in page-edge band. Emitted as a bottom float so
|
|
154
157
|
// it (1) reserves flow space, raising page 1's effective bottom margin so body
|
|
155
158
|
// text never overlaps it, and (2) stays pinned to page 1 — as the first flow
|
|
156
|
-
// content it can never be bumped to page 2.
|
|
159
|
+
// content it can never be bumped to page 2. Page 1's tag line stands above the
|
|
160
|
+
// block in the same float, since both claim the band.
|
|
157
161
|
if cui-indicator != none {
|
|
158
162
|
context {
|
|
159
163
|
// The box shrink-wraps to its widest line; `set align(left)` keeps the
|
|
@@ -165,24 +169,35 @@
|
|
|
165
169
|
set align(left)
|
|
166
170
|
cui-indicator
|
|
167
171
|
})
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
172
|
+
let block-height = measure(indicator-box).height
|
|
173
|
+
let tag-gap = 0.125in
|
|
174
|
+
let stack-height = if tag-line == none {
|
|
175
|
+
block-height
|
|
176
|
+
} else {
|
|
177
|
+
block-height + tag-gap + measure(tag-line, width: page.width - 2 * spacing.margin).height
|
|
178
|
+
}
|
|
179
|
+
// Reserve only the part of the stack inside the text area (`reserved`):
|
|
180
|
+
// float a box of that height, then `place` the stack inside it pushed down
|
|
181
|
+
// by `overhang` so the surplus overflows into the edge band. (A bare
|
|
171
182
|
// `box(height: reserved, indicator-box)` overflows *upward* into the body
|
|
172
|
-
// instead.) The inner `place`
|
|
183
|
+
// instead.) The inner `place`s add no size, so the box stays `reserved`
|
|
173
184
|
// tall and the block's bottom lands 0.5in from the page edge.
|
|
174
185
|
let overhang = spacing.margin - 0.5in
|
|
175
|
-
let reserved =
|
|
186
|
+
let reserved = stack-height - overhang
|
|
176
187
|
place(
|
|
177
|
-
bottom
|
|
188
|
+
bottom,
|
|
178
189
|
float: true,
|
|
179
|
-
// Slide the right edge into the page-edge band, 0.5in from the border;
|
|
180
|
-
// the inner place right-aligns the block to that edge.
|
|
181
|
-
dx: spacing.margin - 0.5in,
|
|
182
190
|
// Minimum gap to the body's last line; the actual gap is larger when the
|
|
183
191
|
// next paragraph can't fit above the block and breaks to the next page.
|
|
184
192
|
clearance: spacing.line,
|
|
185
|
-
box(
|
|
193
|
+
box(width: 100%, height: reserved, {
|
|
194
|
+
// Slide the block's right edge into the page-edge band, 0.5in from
|
|
195
|
+
// the border.
|
|
196
|
+
place(bottom + right, dx: spacing.margin - 0.5in, dy: overhang, indicator-box)
|
|
197
|
+
if tag-line != none {
|
|
198
|
+
place(bottom + center, dy: overhang - block-height - tag-gap, tag-line)
|
|
199
|
+
}
|
|
200
|
+
}),
|
|
186
201
|
)
|
|
187
202
|
}
|
|
188
203
|
}
|
|
@@ -197,7 +197,10 @@
|
|
|
197
197
|
// spaces to the right of page center"
|
|
198
198
|
// AFH 33-337 "Do not place the signature element on a continuation page by itself"
|
|
199
199
|
// AFH 33-337 long-name example: "Signature block adjusted to the left" when a
|
|
200
|
-
// long name would otherwise exceed the right margin.
|
|
200
|
+
// long name would otherwise exceed the right margin. Only the name line moves
|
|
201
|
+
// the block; any other line that runs long wraps, its overflow beginning "under
|
|
202
|
+
// the third character of the line above". The handbook gives no measurement,
|
|
203
|
+
// so the indent is the width of the line's own first two characters.
|
|
201
204
|
//
|
|
202
205
|
// A closing line may open the section above the block, and AFH 33-337 gives it
|
|
203
206
|
// one geometry across the two documents that have one: on the second line below
|
|
@@ -215,6 +218,17 @@
|
|
|
215
218
|
/// -> content | none
|
|
216
219
|
#let format-authority-line(value) = if falsey(value) { none } else { upper(value) }
|
|
217
220
|
|
|
221
|
+
// A signature line arrives as `str` or as content; the indent measures its text.
|
|
222
|
+
#let plain-text(it) = {
|
|
223
|
+
if type(it) == str { return it }
|
|
224
|
+
if type(it) != content { return "" }
|
|
225
|
+
if it.has("text") { return plain-text(it.text) }
|
|
226
|
+
if it.has("children") { return it.children.map(plain-text).join(default: "") }
|
|
227
|
+
if it.has("body") { return plain-text(it.body) }
|
|
228
|
+
if it.has("child") { return plain-text(it.child) }
|
|
229
|
+
""
|
|
230
|
+
}
|
|
231
|
+
|
|
218
232
|
#let render-signature-block(
|
|
219
233
|
signature-lines,
|
|
220
234
|
closing-line: none,
|
|
@@ -234,11 +248,11 @@
|
|
|
234
248
|
// anchor. pad() is relative to the text area, hence (4.5in - margin).
|
|
235
249
|
let default-pad = 4.5in - spacing.margin
|
|
236
250
|
context {
|
|
237
|
-
// Measure
|
|
238
|
-
// The closing line shares the anchor, so it joins the
|
|
239
|
-
// wider of the two decides the shift and they stay aligned.
|
|
251
|
+
// Measure the name line at its rendered settings to detect long-name
|
|
252
|
+
// overflow. The closing line shares the anchor, so it joins the
|
|
253
|
+
// measurement: the wider of the two decides the shift and they stay aligned.
|
|
240
254
|
let body-width = page.width - 2 * spacing.margin
|
|
241
|
-
let anchored-lines = signature-lines
|
|
255
|
+
let anchored-lines = signature-lines.slice(0, calc.min(1, signature-lines.len()))
|
|
242
256
|
if closing-line != none { anchored-lines.push(closing-line) }
|
|
243
257
|
let widest = 0pt
|
|
244
258
|
for line in anchored-lines {
|
|
@@ -305,9 +319,9 @@
|
|
|
305
319
|
#pad(left: left-pad)[
|
|
306
320
|
#text(hyphenate: false)[
|
|
307
321
|
#for line in signature-lines {
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
par(hanging-indent: .
|
|
322
|
+
let clusters = plain-text(line).clusters()
|
|
323
|
+
let lead = clusters.slice(0, calc.min(2, clusters.len())).join()
|
|
324
|
+
par(hanging-indent: measure(lead).width, line)
|
|
311
325
|
}
|
|
312
326
|
// The page the backmatter compares its own against. Must stay
|
|
313
327
|
// inside the unbreakable block: a marker outside one travels with
|
|
@@ -393,7 +407,11 @@
|
|
|
393
407
|
/// - it (content): The table element to style and render
|
|
394
408
|
/// -> content
|
|
395
409
|
#let render-memo-table(it) = {
|
|
396
|
-
|
|
410
|
+
// A headless table's first row is a body row, which Typst also numbers 0.
|
|
411
|
+
show table: t => if t.children.any(c => c.func() == table.header) {
|
|
412
|
+
show table.cell.where(y: 0): set text(weight: "bold")
|
|
413
|
+
t
|
|
414
|
+
} else { t }
|
|
397
415
|
set table(
|
|
398
416
|
stroke: 0.5pt + black,
|
|
399
417
|
inset: (x: 0.5em, y: 0.4em),
|
|
@@ -1,109 +1,103 @@
|
|
|
1
1
|
~~~
|
|
2
2
|
$quill: classic_resume@0.1
|
|
3
3
|
$kind: main
|
|
4
|
-
name:
|
|
4
|
+
name: Philip J. Fry
|
|
5
5
|
contacts:
|
|
6
|
-
-
|
|
7
|
-
- (
|
|
8
|
-
- github.com/
|
|
9
|
-
- linkedin.com/in/nibsbin/
|
|
6
|
+
- fry@planetexpress.example
|
|
7
|
+
- (212) 555-0199
|
|
8
|
+
- github.com/borb-sh
|
|
10
9
|
- New New York
|
|
11
10
|
paper: us-letter
|
|
12
11
|
font_size: 11.5
|
|
13
12
|
margin: 0.5
|
|
14
13
|
~~~
|
|
15
14
|
|
|
15
|
+
~~~
|
|
16
|
+
$kind: summary
|
|
17
|
+
~~~
|
|
18
|
+
|
|
19
|
+
Delivery boy with a thousand years of experience, most of it frozen. Brings 20th-century know-how to 31st-century problems and has saved Earth more than once. Wants to keep delivering anywhere, ideally with fewer space bees.
|
|
20
|
+
|
|
16
21
|
~~~
|
|
17
22
|
$kind: certifications
|
|
18
23
|
title: Certifications
|
|
19
24
|
columns: 2
|
|
20
25
|
items:
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
26
|
+
- "Career Chip: Delivery Boy"
|
|
27
|
+
- Earth Army Basic Training
|
|
28
|
+
- First Aid, taught by Dr. Zoidberg
|
|
29
|
+
- Food Handler's Permit, New York
|
|
25
30
|
~~~
|
|
26
31
|
|
|
27
32
|
~~~
|
|
28
33
|
$kind: skills
|
|
29
34
|
columns: 2
|
|
30
35
|
skills:
|
|
31
|
-
- label:
|
|
32
|
-
items:
|
|
33
|
-
- label:
|
|
34
|
-
items:
|
|
35
|
-
- label:
|
|
36
|
-
items:
|
|
37
|
-
- label:
|
|
38
|
-
items:
|
|
36
|
+
- label: Logistics
|
|
37
|
+
items: Packages, pizza, interplanetary freight
|
|
38
|
+
- label: 20th-Century Culture
|
|
39
|
+
items: TV, arcade games, Star Trek trivia
|
|
40
|
+
- label: Music
|
|
41
|
+
items: Holophonor, opera composition
|
|
42
|
+
- label: Endurance
|
|
43
|
+
items: Cryogenic sleep, 100 cups of coffee
|
|
39
44
|
~~~
|
|
40
45
|
|
|
41
46
|
~~~
|
|
42
47
|
$kind: experience
|
|
43
48
|
jobs:
|
|
44
|
-
- company: Planet Express
|
|
45
|
-
dates:
|
|
46
|
-
role:
|
|
49
|
+
- company: Planet Express
|
|
50
|
+
dates: December 2999 – Present
|
|
51
|
+
role: Delivery Boy
|
|
47
52
|
location: New New York
|
|
48
53
|
details: |-
|
|
49
|
-
-
|
|
54
|
+
- Deliver packages anywhere in the universe with Captain Turanga Leela and Bender Bending Rodríguez.
|
|
50
55
|
|
|
51
|
-
-
|
|
52
|
-
- company:
|
|
53
|
-
dates:
|
|
54
|
-
role:
|
|
55
|
-
location:
|
|
56
|
-
details: "-
|
|
57
|
-
- company:
|
|
58
|
-
dates:
|
|
59
|
-
role:
|
|
60
|
-
location:
|
|
61
|
-
details: "-
|
|
56
|
+
- Completed runs to robot-only Chapek 9 and a hive of giant space bees, and survived both.
|
|
57
|
+
- company: Earth Army
|
|
58
|
+
dates: November 3000
|
|
59
|
+
role: Private
|
|
60
|
+
location: Spheron 1
|
|
61
|
+
details: "- Enlisted for the military discount; deployed against the Spheroids under Zapp Brannigan."
|
|
62
|
+
- company: Panucci's Pizza
|
|
63
|
+
dates: June 1997 – December 1999
|
|
64
|
+
role: Delivery Boy
|
|
65
|
+
location: New York, NY
|
|
66
|
+
details: "- Delivered pizza across New York City until a prank order to Applied Cryogenics ran 1,000 years late."
|
|
62
67
|
title: Experience
|
|
63
68
|
~~~
|
|
64
69
|
|
|
65
70
|
~~~
|
|
66
71
|
$kind: education
|
|
67
72
|
schools:
|
|
68
|
-
- school:
|
|
69
|
-
dates:
|
|
70
|
-
degree:
|
|
71
|
-
location:
|
|
72
|
-
- school: United States Air Force Academy
|
|
73
|
-
dates: May 2024
|
|
74
|
-
degree: BS, Data Science
|
|
75
|
-
location: Colorado Springs, CO
|
|
76
|
-
details: |-
|
|
77
|
-
- Distinguished Graduate (top 10%); Chinese language minor (L2+/R1 on DLPT).
|
|
78
|
-
|
|
79
|
-
- Professor Bradley A. Warner Data Science Catalyst and Top Cadet in Computer Networks.
|
|
80
|
-
- school: Community College of the Air Force
|
|
81
|
-
dates: February 2029
|
|
82
|
-
degree: AS, Information Systems Technology
|
|
83
|
-
location: Online
|
|
73
|
+
- school: Mars University
|
|
74
|
+
dates: September 3000
|
|
75
|
+
degree: Undeclared, no degree
|
|
76
|
+
location: Mars
|
|
84
77
|
~~~
|
|
85
78
|
|
|
86
79
|
~~~
|
|
87
80
|
$kind: other
|
|
88
|
-
title:
|
|
81
|
+
title: Planetary Defense
|
|
89
82
|
entries:
|
|
90
|
-
- heading:
|
|
91
|
-
details: "-
|
|
92
|
-
- heading:
|
|
83
|
+
- heading: Saved Earth from the Brainspawn
|
|
84
|
+
details: "- Held the line as the only human immune to the Brainspawn, thanks to a missing Delta brainwave."
|
|
85
|
+
- heading: Ended the Omicronian Invasion
|
|
93
86
|
details: |-
|
|
94
|
-
-
|
|
87
|
+
- Restaged the lost finale of *Single Female Lawyer* for Lrrr, ruler of the planet Omicron Persei 8.
|
|
95
88
|
|
|
96
|
-
-
|
|
89
|
+
- Caused the crisis in the first place by knocking the original broadcast off the air in 1999.
|
|
90
|
+
- heading: Deflected a Giant Ball of Garbage
|
|
91
|
+
details: "- Helped knock it off course with a second ball of garbage, built on 20th-century expertise."
|
|
97
92
|
~~~
|
|
98
93
|
|
|
99
94
|
~~~
|
|
100
95
|
$kind: projects
|
|
101
96
|
title: Projects
|
|
102
97
|
projects:
|
|
103
|
-
- name: TongueToQuill
|
|
104
|
-
link: tonguetoquill.com
|
|
105
|
-
details: "- Rich markdown editor for perfectly formatted USAF and USSF documents with Claude MCP integration."
|
|
106
98
|
- name: Quillmark
|
|
107
|
-
link: github.com/
|
|
108
|
-
details: "-
|
|
99
|
+
link: github.com/borb-sh/quillmark
|
|
100
|
+
details: "- Schema-driven document engine that delivers typeset documents from Markdown, this resume included."
|
|
101
|
+
- name: Holophonor Opera
|
|
102
|
+
details: "- Wrote and performed an opera about Leela on the holophonor, using the Robot Devil's hands."
|
|
109
103
|
~~~
|
package/templates/templates.json
CHANGED
|
@@ -27,13 +27,6 @@
|
|
|
27
27
|
"file": "rebuttal.md",
|
|
28
28
|
"tags": ["discipline", "lor", "loc"]
|
|
29
29
|
},
|
|
30
|
-
{
|
|
31
|
-
"id": "pass-request",
|
|
32
|
-
"name": "Pass Request",
|
|
33
|
-
"description": "USAF special pass request memorandum template",
|
|
34
|
-
"file": "pass_request.md",
|
|
35
|
-
"tags": ["leave", "travel"]
|
|
36
|
-
},
|
|
37
30
|
{
|
|
38
31
|
"id": "af-form-4141",
|
|
39
32
|
"name": "AF Form 4141",
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
~~~card-yaml
|
|
2
|
-
$quill: usaf_memo@0.3
|
|
3
|
-
$kind: main
|
|
4
|
-
letterhead_title:
|
|
5
|
-
- DEPARTMENT OF THE AIR FORCE
|
|
6
|
-
- AFROTC DETACHMENT 730
|
|
7
|
-
- UNIVERSITY OF PITTSBURGH, PA
|
|
8
|
-
memo_for:
|
|
9
|
-
- AFROTC DET 730/CC
|
|
10
|
-
memo_from:
|
|
11
|
-
- 2d Lt First M. Last
|
|
12
|
-
subject: 15 May 2026 – 18 May 2026, Special Pass Request
|
|
13
|
-
date: today
|
|
14
|
-
references:
|
|
15
|
-
- DAFI 36-3003, 07 August 2024, Military Leave Program
|
|
16
|
-
signature_block:
|
|
17
|
-
- FIRST M. LAST, 2d Lt, USAF
|
|
18
|
-
- AFIT Masters Student
|
|
19
|
-
font_size: 11
|
|
20
|
-
~~~
|
|
21
|
-
|
|
22
|
-
<!--------------
|
|
23
|
-
In the data above, edit;
|
|
24
|
-
- Memorandum From
|
|
25
|
-
- Signature Block
|
|
26
|
-
- Subject
|
|
27
|
-
---------------->
|
|
28
|
-
|
|
29
|
-
**PURPOSE:** The purpose of the memorandum is to request a 4-day pass, in accordance with DAFI 36-3003, for traveling to Columbus, Ohio to visit family.
|
|
30
|
-
|
|
31
|
-
<!-------------
|
|
32
|
-
In Narrative section below, edit:
|
|
33
|
-
- Flight and transit details
|
|
34
|
-
- Document completion of AF4392 if age 25 or under
|
|
35
|
-
--------------->
|
|
36
|
-
|
|
37
|
-
**NARRATIVE:** On 15 May 2026, depart via UA2424 at 0240 ET. On 18 May 2026, return via UA1234 at 1300 ET. Transit to and from PIT with POV. I do not need to complete AF4392 to comply with travel risk requirements because I am not under the age of 26.
|
|
38
|
-
|
|
39
|
-
**REFERENCED REGULATION:** “5.3. Special Pass. Unit commanders may award 3 or 4-day special passes for special occasions or circumstances, such as reenlistment or for some type of special recognition or compensatory time off. They may delegate approval to a level no lower than squadron section commander, deputies, or equivalents. (T-1) Special passes start after normal working hours on a given day. They stop at the beginning of normal working hours on either the 4th day for a 3 -day special pass or the 5th day for a 4-day special pass. A 3-day special pass can be Friday through Sunday, Saturday through Monday, or Tuesday through Thursday. A 4-day special pass can be Thursday through Sunday or Saturday through Tuesday or Friday through Monday. This applies to a normal Monday through Friday workweek. See paragraph 2.1.2 for safe travel guidelines.”
|
|
40
|
-
|
|
41
|
-
If you have any questions, please contact me at first.last@us.af.mil or (123) 456-7890.
|
|
42
|
-
|
|
43
|
-
~~~card-yaml
|
|
44
|
-
$kind: indorsement
|
|
45
|
-
signature_block:
|
|
46
|
-
- ROBERT T. CHANCE, Lt Col, USAF
|
|
47
|
-
- Commander
|
|
48
|
-
format: informal
|
|
49
|
-
~~~
|