@airmark/quiver 0.28.0 → 0.29.0
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/README.md +3 -2
- package/package.json +3 -3
- package/quills/daf4392/0.1.0/Quill.yaml +2 -2
- package/quills/usaf_memo/0.2.0/Quill.yaml +14 -11
- package/quills/usaf_memo/0.2.0/packages/tonguetoquill-usaf-memo/src/body.typ +37 -15
- package/quills/usaf_memo/0.2.0/packages/tonguetoquill-usaf-memo/src/frontmatter.typ +5 -6
- package/quills/usaf_memo/0.2.0/packages/tonguetoquill-usaf-memo/src/indorsement.typ +5 -3
- package/quills/usaf_memo/0.2.0/packages/tonguetoquill-usaf-memo/src/primitives.typ +87 -35
- package/quills/usaf_memo/0.2.0/packages/tonguetoquill-usaf-memo/src/utils.typ +34 -1
- package/quills/usaf_memo/0.3.0/Quill.yaml +321 -0
- package/quills/usaf_memo/0.3.0/assets/dod_seal.png +0 -0
- package/quills/usaf_memo/0.3.0/assets/dow_seal.png +0 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/.gitignore +7 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/LICENSE +21 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/fonts/Cinzel/Cinzel[wght].ttf +0 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/fonts/Cinzel/LICENSE +93 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/fonts/CopperplateCC/CopperplateCC-Bold.otf +0 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/fonts/CopperplateCC/CopperplateCC-Heavy.otf +0 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/fonts/CopperplateCC/LICENSE.md +79 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/fonts/LiberationMono/LICENSE +102 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/fonts/LiberationMono/LiberationMono-Regular.ttf +0 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/fonts/NimbusRomanNo9L/GNU General Public License.txt +340 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/fonts/NimbusRomanNo9L/NimbusRomNo9L-Med.otf +0 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/fonts/NimbusRomanNo9L/NimbusRomNo9L-MedIta.otf +0 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/fonts/NimbusRomanNo9L/NimbusRomNo9L-Reg.otf +0 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/fonts/NimbusRomanNo9L/NimbusRomNo9L-RegIta.otf +0 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/backmatter.typ +29 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/body.typ +364 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/config.typ +78 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/frontmatter.typ +242 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/indorsement.typ +170 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/lib.typ +57 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/mainmatter.typ +27 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/primitives.typ +462 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/utils.typ +419 -0
- package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/typst.toml +27 -0
- package/quills/usaf_memo/0.3.0/plate.typ +120 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// indorsement.typ: Indorsement rendering for USAF memorandum
|
|
2
|
+
//
|
|
3
|
+
// This module implements indorsements (endorsements) per AFH 33-337 Chapter 14.
|
|
4
|
+
// Indorsements are used to forward memorandums with additional commentary.
|
|
5
|
+
// They follow the format: "1st Ind", "2d Ind", "3d Ind", etc.
|
|
6
|
+
// Each indorsement includes its own body text and signature block.
|
|
7
|
+
//
|
|
8
|
+
// Note: When using #show: indorsement.with(...), the indorsement wraps the
|
|
9
|
+
// entire remainder of the document. This works for a single indorsement at
|
|
10
|
+
// the end of a file. For multiple indorsements, use the function call syntax:
|
|
11
|
+
// #indorsement(...)[Body text...]
|
|
12
|
+
|
|
13
|
+
#import "primitives.typ": *
|
|
14
|
+
#import "body.typ": *
|
|
15
|
+
|
|
16
|
+
#let indorsement(
|
|
17
|
+
from: none,
|
|
18
|
+
to: none,
|
|
19
|
+
signature_block: none,
|
|
20
|
+
signature_blank_lines: 4,
|
|
21
|
+
signing_field: none,
|
|
22
|
+
date: none,
|
|
23
|
+
// Format of indorsement: "standard" (same page), "informal" (no header), or "separate_page" (starts on new page)
|
|
24
|
+
format: "standard",
|
|
25
|
+
// Decision action. `none` (default) displays no action line at all.
|
|
26
|
+
// Approval authority pair: "approve" or "disapprove" circles the selected
|
|
27
|
+
// option; "undecided" displays the pair with neither circled.
|
|
28
|
+
// Coordination pair: "concur" or "nonconcur" circles the selected option;
|
|
29
|
+
// "undecided_concur" displays the pair with neither circled.
|
|
30
|
+
action: none,
|
|
31
|
+
content,
|
|
32
|
+
) = {
|
|
33
|
+
// Validate format parameter
|
|
34
|
+
assert(
|
|
35
|
+
format in ("standard", "informal", "separate_page"),
|
|
36
|
+
message: "format must be \"standard\", \"informal\", or \"separate_page\"",
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
if format != "informal" {
|
|
40
|
+
assert(from != none, message: "from is required")
|
|
41
|
+
assert(to != none, message: "to is required")
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
let actual_date = date
|
|
46
|
+
let ind_from = first-or-value(from)
|
|
47
|
+
let ind_for = to
|
|
48
|
+
|
|
49
|
+
// An empty body (e.g. a KIND with only an action selected and no markdown
|
|
50
|
+
// body) collapses to zero rendered layout via render-body's filter. To
|
|
51
|
+
// make the "empty body takes no layout space" guarantee end-to-end, also
|
|
52
|
+
// suppress the spacing the surrounding code reserves *for* the body:
|
|
53
|
+
// the header→body gap (when no action is present) and the action→body
|
|
54
|
+
// trailing gap. Without this, an empty body still leaves an extra
|
|
55
|
+
// blank-line stride above the signature, pushing it off the AFH 33-337
|
|
56
|
+
// "fifth line below the last line of text" anchor.
|
|
57
|
+
// The plate calls indorsement with `[]` when the markdown body is empty
|
|
58
|
+
// or whitespace-only; comparing against `[]` reliably detects that.
|
|
59
|
+
let body_empty = content == []
|
|
60
|
+
|
|
61
|
+
let effective_action = if action == none or type(action) != str or action.trim() == "" {
|
|
62
|
+
none
|
|
63
|
+
} else {
|
|
64
|
+
action
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if format != "informal" {
|
|
68
|
+
// Step the counter BEFORE the context block to avoid read-then-update loop
|
|
69
|
+
counters.indorsement.step()
|
|
70
|
+
|
|
71
|
+
context {
|
|
72
|
+
let config = query(<usaf-memo-config>).first().value
|
|
73
|
+
let memo-style = config.at("memo_style", default: "usaf")
|
|
74
|
+
let original_subject = config.subject
|
|
75
|
+
let original_date = config.original_date
|
|
76
|
+
let original_from = config.original_from
|
|
77
|
+
|
|
78
|
+
// Read the counter value (already stepped above)
|
|
79
|
+
let indorsement_number = counters.indorsement.get().at(0, default: 1)
|
|
80
|
+
let indorsement_label = format-indorsement-number(indorsement_number)
|
|
81
|
+
|
|
82
|
+
let ind_date = align(right)[#if actual_date != none { display-date(actual_date, memo-style: memo-style) } else { date-placeholder-line() }]
|
|
83
|
+
|
|
84
|
+
// Separate-page header body: restates the original memo's identity (FROM,
|
|
85
|
+
// date, subject) on its own line, since the indorsement no longer shares a
|
|
86
|
+
// page with the action document. Rendered as a non-breakable, sticky unit
|
|
87
|
+
// so it travels to the next page *with* the content it heads rather than
|
|
88
|
+
// being stranded at the bottom of a page.
|
|
89
|
+
// `original_from` / `original_subject` are content fields, and a content
|
|
90
|
+
// field lowers to a markup block whose newlines read as spaces in the
|
|
91
|
+
// enclosing paragraph — mid-sentence that lands a stray space before the
|
|
92
|
+
// following comma. `box` gives each its own paragraph context, where
|
|
93
|
+
// Typst trims the edge spaces (the same treatment the inline reference
|
|
94
|
+
// gets in `render-subject-section`), and keeps the phrase unbroken.
|
|
95
|
+
let separate-page-body = block(breakable: false, sticky: true)[
|
|
96
|
+
#[#indorsement_label to #box(original_from), #display-date(original_date, memo-style: memo-style), #box(original_subject)]
|
|
97
|
+
#blank-line()
|
|
98
|
+
#grid(columns: (auto, 1fr), ind_from, ind_date)
|
|
99
|
+
#blank-line()
|
|
100
|
+
#grid(columns: (auto, auto, 1fr), "MEMORANDUM FOR", " ", ind_for)
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
// Standard header: terse "Nth Ind, FROM date" line, used when the
|
|
104
|
+
// indorsement stays on the same page as the action document. Same
|
|
105
|
+
// non-breakable + sticky treatment so the two header lines never split
|
|
106
|
+
// across a page boundary and never detach from the body/signature below.
|
|
107
|
+
let standard-header = block(breakable: false, sticky: true)[
|
|
108
|
+
#blank-line()
|
|
109
|
+
#grid(columns: (auto, 1fr), [#indorsement_label, #box(ind_from)], ind_date)
|
|
110
|
+
#blank-line()
|
|
111
|
+
#grid(columns: (auto, auto, 1fr), "MEMORANDUM FOR", " ", ind_for)
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
if format == "separate_page" {
|
|
115
|
+
// Explicit separate-page indorsement always begins on a fresh page.
|
|
116
|
+
pagebreak()
|
|
117
|
+
separate-page-body
|
|
118
|
+
} else {
|
|
119
|
+
// AFH 33-337: a standard indorsement that no longer fits on the action
|
|
120
|
+
// document's page moves to a separate page, where it carries the fuller
|
|
121
|
+
// separate-page identifying header (it has lost its visual link to the
|
|
122
|
+
// original). Auto-upgrade to that header when the indorsement is pushed
|
|
123
|
+
// to a new page, rather than printing the terse same-page header at the
|
|
124
|
+
// top of a continuation page where it no longer makes sense.
|
|
125
|
+
//
|
|
126
|
+
// Detection: because the header is a non-breakable, sticky unit, Typst
|
|
127
|
+
// moves it wholesale to the next page when it would not fit — so a
|
|
128
|
+
// pushed indorsement lands at the top content margin. If the header's
|
|
129
|
+
// resolved position is at the top of its page, it was pushed; emit the
|
|
130
|
+
// separate-page body (no extra pagebreak — we are already at page top).
|
|
131
|
+
// Otherwise the header flows in place with the standard form.
|
|
132
|
+
let stride = {
|
|
133
|
+
let s = LINE_STRIDE.get()
|
|
134
|
+
if s == none {
|
|
135
|
+
let one-line = measure(par(spacing: 0pt)[x]).height
|
|
136
|
+
measure(par(spacing: 0pt)[x#linebreak()x]).height - one-line
|
|
137
|
+
} else { s }
|
|
138
|
+
}
|
|
139
|
+
// here().position().y is the resolved flow position of this header. On a
|
|
140
|
+
// continuation page the first content sits at the top margin; allow one
|
|
141
|
+
// line stride of tolerance for baseline/rounding.
|
|
142
|
+
let pushed-to-new-page = here().position().y <= spacing.margin + stride
|
|
143
|
+
if pushed-to-new-page {
|
|
144
|
+
separate-page-body
|
|
145
|
+
} else {
|
|
146
|
+
standard-header
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Header→content gap. Skipped when there is neither an action line nor
|
|
151
|
+
// body to follow — render-signature-block supplies its own 4-line gap.
|
|
152
|
+
if effective_action != none or not body_empty {
|
|
153
|
+
blank-line()
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Show action line only when an action decision is set (not `none`)
|
|
158
|
+
if effective_action != none {
|
|
159
|
+
render-action-line(effective_action, trailing-blank-line: not body_empty)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if not body_empty {
|
|
163
|
+
context {
|
|
164
|
+
let memo-style = query(<usaf-memo-config>).first().value.at("memo_style", default: "usaf")
|
|
165
|
+
render-body(content, memo-style: memo-style)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
render-signature-block(signature_block, signature-blank-lines: signature_blank_lines, signing-field: signing_field)
|
|
170
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// lib.typ: Public API for USAF memorandum template
|
|
2
|
+
//
|
|
3
|
+
// This module provides a composable API for creating United States Air Force
|
|
4
|
+
// memorandums that comply with AFH 33-337 "The Tongue and Quill" Chapter 14
|
|
5
|
+
// "The Official Memorandum" formatting standards.
|
|
6
|
+
//
|
|
7
|
+
// AFH 33-337 Chapter 14 specifies exact requirements for:
|
|
8
|
+
// - Margins: 1 inch on all sides (§4)
|
|
9
|
+
// - Font: 12pt Times New Roman (§5)
|
|
10
|
+
// - Date placement: 1.75 inches from top, 1 inch from right (Date section)
|
|
11
|
+
// - Heading elements: MEMORANDUM FOR, FROM, SUBJECT with 2-line spacing
|
|
12
|
+
// - Paragraph numbering: Hierarchical 1., a., (1), (a) format (§2)
|
|
13
|
+
// - Signature block: 4.5 inches from left, never orphaned (Signature Block section)
|
|
14
|
+
// - Backmatter: Attachments, cc:, distribution with specific spacing
|
|
15
|
+
//
|
|
16
|
+
// Key features:
|
|
17
|
+
// - Composable show rules for frontmatter and mainmatter
|
|
18
|
+
// - Function-based backmatter and indorsements for correct ordering
|
|
19
|
+
// - No global state - configuration flows through metadata
|
|
20
|
+
// - Reusable primitives for common rendering tasks
|
|
21
|
+
// - AFH 33-337 compliant formatting throughout
|
|
22
|
+
//
|
|
23
|
+
// Basic usage:
|
|
24
|
+
//
|
|
25
|
+
// #import "@preview/tonguetoquill-usaf-memo:3.0.0": frontmatter, mainmatter, backmatter, indorsement
|
|
26
|
+
//
|
|
27
|
+
// #show: frontmatter.with(
|
|
28
|
+
// subject: "Your Subject Here",
|
|
29
|
+
// memo_for: ("OFFICE/SYMBOL",),
|
|
30
|
+
// memo_from: ("YOUR/SYMBOL",),
|
|
31
|
+
// memo_style: "usaf", // "usaf" (default) or "daf"
|
|
32
|
+
// )
|
|
33
|
+
//
|
|
34
|
+
// #show: mainmatter
|
|
35
|
+
//
|
|
36
|
+
// Your memo body content here.
|
|
37
|
+
// (USAF style auto-numbers per AFH 33-337; DAF style uses unnumbered
|
|
38
|
+
// top-level paragraphs with fixed 0.5in first-line indentation)
|
|
39
|
+
//
|
|
40
|
+
// #backmatter(
|
|
41
|
+
// signature_block: ("NAME, Rank, USAF", "Title"),
|
|
42
|
+
// attachments: (...),
|
|
43
|
+
// cc: (...),
|
|
44
|
+
// )
|
|
45
|
+
//
|
|
46
|
+
// #indorsement(
|
|
47
|
+
// from: "ORG/SYMBOL",
|
|
48
|
+
// to: "RECIPIENT/SYMBOL",
|
|
49
|
+
// signature_block: ("NAME, Rank, USAF", "Title"),
|
|
50
|
+
// )[
|
|
51
|
+
// Indorsement content here.
|
|
52
|
+
// ]
|
|
53
|
+
|
|
54
|
+
#import "frontmatter.typ": frontmatter
|
|
55
|
+
#import "mainmatter.typ": mainmatter
|
|
56
|
+
#import "backmatter.typ": backmatter
|
|
57
|
+
#import "indorsement.typ": indorsement
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// mainmatter.typ: Mainmatter show rule for USAF memorandum
|
|
2
|
+
//
|
|
3
|
+
// This module implements the mainmatter (body text) of a USAF memorandum per
|
|
4
|
+
// AFH 33-337 Chapter 14 "The Text of the Official Memorandum" (§1-12).
|
|
5
|
+
|
|
6
|
+
#import "body.typ": *
|
|
7
|
+
|
|
8
|
+
/// Mainmatter show rule for USAF memorandum body content.
|
|
9
|
+
///
|
|
10
|
+
/// AFH 33-337 "The Text of the Official Memorandum" §1-12 requirements:
|
|
11
|
+
/// - Begin text on second line below subject/references
|
|
12
|
+
/// - Single-space text, double-space between paragraphs
|
|
13
|
+
/// - Number and letter each paragraph/subparagraph
|
|
14
|
+
/// - "A single paragraph is not numbered" (§2)
|
|
15
|
+
/// - First paragraph flush left, never indented
|
|
16
|
+
///
|
|
17
|
+
/// Applies AFH 33-337 paragraph numbering and formatting to the main body
|
|
18
|
+
/// of the memorandum. Automatically detects single vs. multiple paragraphs
|
|
19
|
+
/// to comply with AFH 33-337 numbering requirements.
|
|
20
|
+
///
|
|
21
|
+
/// - content (content): The body content to render
|
|
22
|
+
/// -> content
|
|
23
|
+
#let mainmatter(it) = context {
|
|
24
|
+
let config = query(<usaf-memo-config>).first().value
|
|
25
|
+
let memo-style = config.at("memo_style", default: "usaf")
|
|
26
|
+
render-body(it, memo-style: memo-style)
|
|
27
|
+
}
|