@airmark/quiver 0.30.0 → 0.32.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.
Files changed (30) hide show
  1. package/README.md +1 -0
  2. package/package.json +4 -4
  3. package/quills/af4141/0.1.0/plate.typ +11 -19
  4. package/quills/afmc_moa/0.0.1/Quill.yaml +289 -0
  5. package/quills/afmc_moa/0.0.1/packages/typst-afmc-moa/fonts/NimbusRomanNo9L/GNU General Public License.txt +340 -0
  6. package/quills/afmc_moa/0.0.1/packages/typst-afmc-moa/fonts/NimbusRomanNo9L/NimbusRomNo9L-Med.otf +0 -0
  7. package/quills/afmc_moa/0.0.1/packages/typst-afmc-moa/fonts/NimbusRomanNo9L/NimbusRomNo9L-MedIta.otf +0 -0
  8. package/quills/afmc_moa/0.0.1/packages/typst-afmc-moa/fonts/NimbusRomanNo9L/NimbusRomNo9L-Reg.otf +0 -0
  9. package/quills/afmc_moa/0.0.1/packages/typst-afmc-moa/fonts/NimbusRomanNo9L/NimbusRomNo9L-RegIta.otf +0 -0
  10. package/quills/afmc_moa/0.0.1/packages/typst-afmc-moa/src/attachment.typ +51 -0
  11. package/quills/afmc_moa/0.0.1/packages/typst-afmc-moa/src/backmatter.typ +66 -0
  12. package/quills/afmc_moa/0.0.1/packages/typst-afmc-moa/src/body.typ +126 -0
  13. package/quills/afmc_moa/0.0.1/packages/typst-afmc-moa/src/config.typ +31 -0
  14. package/quills/afmc_moa/0.0.1/packages/typst-afmc-moa/src/frontmatter.typ +74 -0
  15. package/quills/afmc_moa/0.0.1/packages/typst-afmc-moa/src/lib.typ +41 -0
  16. package/quills/afmc_moa/0.0.1/packages/typst-afmc-moa/src/mainmatter.typ +5 -0
  17. package/quills/afmc_moa/0.0.1/packages/typst-afmc-moa/src/utils.typ +126 -0
  18. package/quills/afmc_moa/0.0.1/packages/typst-afmc-moa/typst.toml +19 -0
  19. package/quills/afmc_moa/0.0.1/plate.typ +116 -0
  20. package/quills/daf1206/0.1.0/plate.typ +10 -10
  21. package/quills/daf4392/0.1.0/plate.typ +13 -13
  22. package/quills/usaf_memo/0.2.0/Quill.yaml +0 -2
  23. package/quills/usaf_memo/0.2.0/plate.typ +33 -28
  24. package/quills/usaf_memo/0.3.0/Quill.yaml +25 -44
  25. package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/body.typ +30 -25
  26. package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/indorsement.typ +18 -6
  27. package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/lib.typ +1 -0
  28. package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/primitives.typ +56 -31
  29. package/quills/usaf_memo/0.3.0/packages/tonguetoquill-usaf-memo/src/utils.typ +41 -33
  30. package/quills/usaf_memo/0.3.0/plate.typ +108 -44
@@ -1,5 +1,11 @@
1
- #import "@local/quillmark-helper:0.1.0": data, signature-field
2
- #import "@local/tonguetoquill-usaf-memo:4.0.0": backmatter, frontmatter, indorsement, mainmatter
1
+ #import "@local/quillmark-helper:0.1.0": data, display, form-field, signature-field
2
+ #import "@local/tonguetoquill-usaf-memo:4.0.0": (
3
+ backmatter, date-pattern, frontmatter, indorsement, mainmatter,
4
+ )
5
+
6
+ // A memo has no "no style" state, so the blank takes the package's default.
7
+ // Resolved once here: `frontmatter` and the date's pattern must agree.
8
+ #let memo_style = if data.memo_style != "" { data.memo_style } else { "usaf" }
3
9
 
4
10
  // The letterhead arrives as one ordered array of lines. Element 0 is the title,
5
11
  // set larger; the rest are caption lines beneath it. A single element renders
@@ -8,54 +14,77 @@
8
14
  // leaves no glyphs and takes no space from the flow, and the seal stands alone.
9
15
  #let letterhead_lines = data.letterhead_title
10
16
 
17
+ // Body text size, in points. Also the height of one line of the indorsement
18
+ // header, which is what an omitted indorsement date reserves for its fill-in
19
+ // widget (`date-placeholder-slot`, whose slot is `1em` tall and 1in wide).
20
+ #let body_font_size = data.font_size * 1pt
21
+
11
22
  // Frontmatter configuration
12
23
  #show: frontmatter.with(
13
24
  // Letterhead configuration
14
25
  letterhead_title: letterhead_lines.at(0, default: ""),
15
26
  letterhead_caption: if letterhead_lines.len() > 1 { letterhead_lines.slice(1) } else { () },
16
- letterhead_seal_subtitle: data.at("letterhead_seal_subtitle", default: none),
17
- letterhead_seal: image(
18
- if data.at("letterhead_seal", default: "dow") == "dod" {
19
- "assets/dod_seal.png"
20
- } else {
21
- "assets/dow_seal.png"
22
- }
23
- ),
24
-
25
- // Date
26
- date: data.at("date", default: none),
27
+ letterhead_seal_subtitle: data.letterhead_seal_subtitle,
28
+ // Enum blank is `""`, not a seal. Omit so the package renders none rather
29
+ // than treating the blank as DoW.
30
+ ..if data.letterhead_seal != "" {
31
+ (letterhead_seal: image(
32
+ if data.letterhead_seal == "dod" {
33
+ "assets/dod_seal.png"
34
+ } else {
35
+ "assets/dow_seal.png"
36
+ }
37
+ ))
38
+ },
39
+
40
+ // Date. `data.date` is the native `datetime` and would render identically,
41
+ // but its ink would be born inside the package and carry no schema address.
42
+ // `display` places the field's *content* projection instead: the glyphs are
43
+ // born in the generated helper, so the memo date stays click-to-edit however
44
+ // deep the package formats it. A blank date yields `none`, which is what
45
+ // `frontmatter`'s `datetime.today()` fallback keys on.
46
+ date: display("date", date-pattern(memo-style: memo_style)),
27
47
 
28
48
  // Receiver information
29
49
  memo_for: data.memo_for,
30
50
 
31
51
  // Sender information (omitted for Memorandum for Record)
32
- ..if data.at("memo_from", default: ()).len() > 0 { (memo_from: data.memo_from) },
52
+ ..if data.memo_from.len() > 0 { (memo_from: data.memo_from) },
33
53
 
34
54
  // Subject line
35
55
  subject: data.subject,
36
56
 
37
57
  // Optional references
38
- ..if "references" in data { (references: data.references) },
58
+ ..if data.references.len() > 0 { (references: data.references) },
39
59
 
40
60
  // Optional footer tag line
41
- ..if "tag_line" in data { (footer_tag_line: data.tag_line) },
42
-
43
- // Optional classification level
44
- ..if "classification" in data { (classification_level: data.classification) },
45
-
46
- ..if "dissemination" in data { (dissemination: data.dissemination) },
47
-
48
- // CUI designation indicator block fields (DoDM 5200.48)
49
- ..if "cui_controlled_by" in data { (cui_controlled_by: data.cui_controlled_by) },
50
- ..if "cui_category" in data { (cui_category: data.cui_category) },
51
- ..if "cui_limited_dissemination" in data { (cui_limited_dissemination: data.cui_limited_dissemination) },
52
- ..if "cui_poc" in data { (cui_poc: data.cui_poc) },
61
+ footer_tag_line: data.tag_line,
62
+
63
+ // The blank reads as no banner, which is what the package's own
64
+ // `classification_level: none` default means.
65
+ classification_level: data.classification.value,
66
+
67
+ dissemination: data.dissemination,
68
+
69
+ // CUI designation indicator block fields (DoDM 5200.48). `classification`
70
+ // declares a `CUI` variant, so these four exist only where the discriminant
71
+ // reads CUI, and the branch is what makes reading them total: inside it every
72
+ // declared field of that world is present, outside it none is. The package's
73
+ // own `cui_*: none` defaults cover the worlds that omit them.
74
+ ..if data.classification.value == "CUI" {
75
+ (
76
+ cui_controlled_by: data.classification.controlled_by,
77
+ cui_category: data.classification.category,
78
+ cui_limited_dissemination: data.classification.limited_dissemination,
79
+ cui_poc: data.classification.poc,
80
+ )
81
+ },
53
82
 
54
- // USAF vs DAF memorandum style (date format, body indentation)
55
- memo_style: data.at("memo_style", default: "usaf"),
83
+ // USAF vs DAF memorandum style (date format, body indentation).
84
+ memo_style: memo_style,
56
85
 
57
86
  // Font size
58
- font_size: data.at("font_size", default: 12) * 1pt,
87
+ font_size: body_font_size,
59
88
 
60
89
  // List recipients in vertical list
61
90
  memo_for_cols: 1,
@@ -76,18 +105,38 @@
76
105
  signing_field: signature-field("Signature", field: "signature_block"),
77
106
 
78
107
  // Optional cc
79
- ..if "cc" in data { (cc: data.cc) },
108
+ ..if data.cc.len() > 0 { (cc: data.cc) },
80
109
 
81
110
  // Optional distribution
82
- ..if "distribution" in data { (distribution: data.distribution) },
111
+ ..if data.distribution.len() > 0 { (distribution: data.distribution) },
83
112
 
84
113
  // Optional attachments
85
- ..if "attachments" in data { (attachments: data.attachments) },
114
+ ..if data.attachments.len() > 0 { (attachments: data.attachments) },
115
+ )
116
+
117
+ // The action line's wording follows the indorsement's place in the chain, not
118
+ // an endorser's choice: the last indorsement is the approval authority and
119
+ // reads Approve / Disapprove, every one before it is a coordinating official
120
+ // and reads Concur / Nonconcur. Only this loop can see whether an indorsement
121
+ // is the last one, so the position is resolved here and passed down. Cards of
122
+ // other kinds may interleave, so the last indorsement is the last card *of
123
+ // this kind*, not the last card. `-1` never matches a real index, which is the
124
+ // wanted result when there are no indorsements at all.
125
+ // Parenthesized so the method chain can span lines: in markup, a `#let` whose
126
+ // expression ends at a line break stops there, and the next line's leading
127
+ // `.at(…)` would be read as text.
128
+ #let last_indorsement_index = (
129
+ data
130
+ .at("$cards")
131
+ .enumerate()
132
+ .filter(entry => entry.at(1).at("$kind", default: none) == "indorsement")
133
+ .map(entry => entry.at(0))
134
+ .at(-1, default: -1)
86
135
  )
87
136
 
88
137
  // Indorsements - iterate through CARDS array and filter by CARD tag
89
138
  #for (i, card) in data.at("$cards").enumerate() {
90
- if card.at("$kind") == "indorsement" {
139
+ if card.at("$kind", default: none) == "indorsement" {
91
140
  // The quillmark helper leaves an unset/whitespace-only markdown body as
92
141
  // the empty string `""`; only non-empty bodies are eval'd into content.
93
142
  // Pass truly empty content (`[]`) in the empty case so indorsement can
@@ -96,14 +145,15 @@
96
145
  let body_content = if type(body) == str { [] } else { body }
97
146
  // Per AFH 33-337 Ch. 14, an indorsement is dated when the endorser signs
98
147
  // it (distinct from the originating memo's date). The signing date is
99
- // generally unknown at compile time and filled in by hand, so a blank or
100
- // omitted date renders a fill-in line rather than stamping the compile date.
101
- let card_date = card.at("date", default: none)
102
- let resolved_date = if card_date == none or card_date == "" {
103
- none
104
- } else {
105
- card_date
106
- }
148
+ // generally unknown at compile time, so a blank or omitted date leaves the
149
+ // date slot fillable rather than stamping the compile date into it.
150
+ // `display` takes an address, so a per-card call yields a per-card region
151
+ // even though every iteration shares one `card` loop variable, and it
152
+ // returns `none` for a blank date — the fill-in case below.
153
+ let resolved_date = display(
154
+ card.at("$path") + "date",
155
+ date-pattern(memo-style: memo_style),
156
+ )
107
157
  // The card's `$path` prefix composes its canonical schema addresses
108
158
  // (`$cards.indorsement.<n>.…`, per-kind ordinal) — the absolute loop
109
159
  // index `i` is NOT that ordinal once kinds interleave, so it stays a
@@ -118,9 +168,23 @@
118
168
  "Ind_" + str(i) + "_Signature",
119
169
  field: card.at("$path") + "signature_block",
120
170
  ),
121
- format: card.at("format", default: "standard"),
171
+ ..if card.format != "" { (format: card.format) },
122
172
  date: resolved_date,
123
- ..if "action" in card { (action: card.action) },
173
+ // An omitted date becomes an empty AcroForm text box the endorser types
174
+ // the signing date into, sized to the slot the package reserves for it.
175
+ // Built only when there is no date to print: a widget over a printed date
176
+ // would offer an edit that the rendered document does not carry back.
177
+ ..if resolved_date == none {
178
+ (date_field: form-field(
179
+ "Ind_" + str(i) + "_Date",
180
+ type: "text",
181
+ width: 1in,
182
+ height: body_font_size,
183
+ field: card.at("$path") + "date",
184
+ ))
185
+ },
186
+ ..if card.action != "" { (action: card.action) },
187
+ approval_authority: i == last_indorsement_index,
124
188
  body_content,
125
189
  )
126
190
  }