@airmark/quiver 0.15.0 → 0.18.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/package.json +3 -3
- package/quills/af4141/0.1.0/Quill.yaml +1 -1
- package/quills/af4141/0.1.0/example.md +6 -6
- package/quills/af4141/0.1.0/plate.typ +3 -3
- package/quills/daf1206/0.1.0/Quill.yaml +1 -1
- package/quills/daf1206/0.1.0/example.md +1 -1
- package/quills/daf1206/0.1.0/plate.typ +2 -2
- package/quills/daf4392/0.1.0/Quill.yaml +1 -1
- package/quills/daf4392/0.1.0/example.md +2 -2
- package/quills/daf4392/0.1.0/plate.typ +4 -4
- package/quills/usaf_memo/0.2.0/Quill.yaml +5 -4
- package/quills/usaf_memo/0.2.0/example.md +2 -2
- package/quills/usaf_memo/0.2.0/packages/tonguetoquill-usaf-memo/src/backmatter.typ +2 -1
- package/quills/usaf_memo/0.2.0/packages/tonguetoquill-usaf-memo/src/body.typ +1 -1
- package/quills/usaf_memo/0.2.0/packages/tonguetoquill-usaf-memo/src/frontmatter.typ +2 -2
- package/quills/usaf_memo/0.2.0/packages/tonguetoquill-usaf-memo/src/indorsement.typ +14 -10
- package/quills/usaf_memo/0.2.0/packages/tonguetoquill-usaf-memo/src/mainmatter.typ +1 -1
- package/quills/usaf_memo/0.2.0/packages/tonguetoquill-usaf-memo/src/primitives.typ +29 -17
- package/quills/usaf_memo/0.2.0/plate.typ +6 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@airmark/quiver",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Airmark Quiver — Air Force and DAF document Quills for Quillmark.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@quillmark/quiver": "^0.7.0",
|
|
20
|
-
"@quillmark/wasm": "0.
|
|
20
|
+
"@quillmark/wasm": "^0.78.0"
|
|
21
21
|
},
|
|
22
22
|
"scripts": {
|
|
23
23
|
"test": "node --test quiver.test.js",
|
|
@@ -35,6 +35,6 @@
|
|
|
35
35
|
"license": "Apache-2.0",
|
|
36
36
|
"repository": {
|
|
37
37
|
"type": "git",
|
|
38
|
-
"url": "https://github.com/
|
|
38
|
+
"url": "https://github.com/tonguetoquill/airmark-quiver"
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -28,7 +28,7 @@ main:
|
|
|
28
28
|
type: string
|
|
29
29
|
description: Commander's authentication entry on page 1.
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
leaf_kinds:
|
|
32
32
|
experience:
|
|
33
33
|
description: Each card represents one row in the Record of Experience table. Rows fill page 1 (up to 16 rows) then page 2 (up to 21 rows), for a maximum of 37 rows.
|
|
34
34
|
ui:
|
|
@@ -11,7 +11,7 @@ commanders_auth: ""
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
<!-- ═══════════════════════════════════════════════════════════════
|
|
14
|
-
RECORD OF EXPERIENCE
|
|
14
|
+
RECORD OF EXPERIENCE LEAVES
|
|
15
15
|
Each card below becomes one row in the experience table.
|
|
16
16
|
Page 1 holds up to 16 rows; Page 2 holds up to 21 rows (37 max).
|
|
17
17
|
|
|
@@ -29,7 +29,7 @@ commanders_auth: ""
|
|
|
29
29
|
|
|
30
30
|
<!-- Example 1: Initial assignment to a unit -->
|
|
31
31
|
---
|
|
32
|
-
|
|
32
|
+
KIND: experience
|
|
33
33
|
date: 2025-01-15
|
|
34
34
|
action: "Assigned to 1 ACCS/DOT as Weapons Director"
|
|
35
35
|
written_grade: ""
|
|
@@ -41,7 +41,7 @@ auth_or_remarks: "Initial assignment"
|
|
|
41
41
|
|
|
42
42
|
<!-- Example 2: Written upgrade evaluation -->
|
|
43
43
|
---
|
|
44
|
-
|
|
44
|
+
KIND: experience
|
|
45
45
|
date: 2025-03-15
|
|
46
46
|
action: "Completed written upgrade evaluation for 3-Level"
|
|
47
47
|
written_grade: "3"
|
|
@@ -53,7 +53,7 @@ auth_or_remarks: "Per AFMAN 13-1CRCV1"
|
|
|
53
53
|
|
|
54
54
|
<!-- Example 3: Positional (live-environment) upgrade / certification -->
|
|
55
55
|
---
|
|
56
|
-
|
|
56
|
+
KIND: experience
|
|
57
57
|
date: 2025-06-20
|
|
58
58
|
action: "Certified Mission Ready — positional upgrade"
|
|
59
59
|
written_grade: ""
|
|
@@ -65,7 +65,7 @@ auth_or_remarks: "SSgt Jones, T.R."
|
|
|
65
65
|
|
|
66
66
|
<!-- Example 4: Combined written + positional upgrade (both columns filled) -->
|
|
67
67
|
---
|
|
68
|
-
|
|
68
|
+
KIND: experience
|
|
69
69
|
date: 2025-08-01
|
|
70
70
|
action: "Upgrade evaluation — written and positional"
|
|
71
71
|
written_grade: "4"
|
|
@@ -77,7 +77,7 @@ auth_or_remarks: "Verified by unit commander"
|
|
|
77
77
|
|
|
78
78
|
<!-- Example 5: PCS departure — no written/positional grade; omit grade dates (or use "") -->
|
|
79
79
|
---
|
|
80
|
-
|
|
80
|
+
KIND: experience
|
|
81
81
|
date: 2025-09-01
|
|
82
82
|
action: "PCS to 726 ACS/MOC, Tinker AFB OK"
|
|
83
83
|
written_grade: ""
|
|
@@ -35,14 +35,14 @@
|
|
|
35
35
|
#if "grade" in data { vals.insert("commonforms_text_p1_3", data.grade) }
|
|
36
36
|
#if "commanders_auth" in data { vals.insert("commonforms_text_p1_116", data.commanders_auth) }
|
|
37
37
|
|
|
38
|
-
// --- Experience table rows from
|
|
38
|
+
// --- Experience table rows from LEAVES ---
|
|
39
39
|
// The form supports 16 rows on page 1 and 21 rows on page 2 (37 total).
|
|
40
40
|
// Overflow rows are silently ignored.
|
|
41
41
|
#let max-rows = 37
|
|
42
42
|
#{
|
|
43
43
|
let row = 0
|
|
44
|
-
for card in data.
|
|
45
|
-
if card.
|
|
44
|
+
for card in data.LEAVES {
|
|
45
|
+
if card.KIND == "experience" {
|
|
46
46
|
if row < max-rows {
|
|
47
47
|
for (col, key) in col-keys.enumerate() {
|
|
48
48
|
let value = form-cell(card.at(key, default: none))
|
|
@@ -39,7 +39,7 @@ main:
|
|
|
39
39
|
unit_commander_rank_name_and_telephone:
|
|
40
40
|
type: string
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
leaf_kinds:
|
|
43
43
|
accomplishments_continued:
|
|
44
44
|
description: Optional. Add this card only when page 2 overflow is required. Use the card body for continued bullets. Rank/name on page 2 matches the main nominee line automatically.
|
|
45
45
|
ui:
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
#let continued-key = "SPECIFIC_ACCOMPLISHMENTS_Use_Performance_Statements_IAW_DAFMAN_362806_Continued"
|
|
41
|
-
#for card in data.
|
|
42
|
-
if card.
|
|
41
|
+
#for card in data.LEAVES {
|
|
42
|
+
if card.KIND == "accomplishments_continued" and continued-key not in vals and "BODY" in card {
|
|
43
43
|
vals.insert(continued-key, card.BODY)
|
|
44
44
|
}
|
|
45
45
|
}
|
|
@@ -16,7 +16,7 @@ emergency_contact_phone: "999-999-9999"
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
18
|
---
|
|
19
|
-
|
|
19
|
+
KIND: itinerary
|
|
20
20
|
date: 2026-03-26
|
|
21
21
|
departure_point: "Biloxi, MS"
|
|
22
22
|
arrival_point: "Fake City, MS"
|
|
@@ -25,7 +25,7 @@ mileage: "250"
|
|
|
25
25
|
---
|
|
26
26
|
|
|
27
27
|
---
|
|
28
|
-
|
|
28
|
+
KIND: itinerary
|
|
29
29
|
date: 2026-03-29
|
|
30
30
|
departure_point: "Fake City, MS"
|
|
31
31
|
arrival_point: "Biloxi, MS"
|
|
@@ -29,14 +29,14 @@
|
|
|
29
29
|
#tf(40pt, 100pt)[#show-date(data.at("departure_date", default: none))]
|
|
30
30
|
#tf(123pt, 100pt)[#data.at("final_destination", default: "")]
|
|
31
31
|
|
|
32
|
-
// Itinerary Rows (via
|
|
32
|
+
// Itinerary Rows (via LEAVES)
|
|
33
33
|
#{
|
|
34
34
|
let row = 0
|
|
35
35
|
let dy-start = 160pt
|
|
36
36
|
let dy-step = 40pt
|
|
37
|
-
if "
|
|
38
|
-
for card in data.
|
|
39
|
-
if card.
|
|
37
|
+
if "LEAVES" in data {
|
|
38
|
+
for card in data.LEAVES {
|
|
39
|
+
if card.KIND == "itinerary" and row < 10 {
|
|
40
40
|
let dy = dy-start + (row * dy-step)
|
|
41
41
|
tf(80pt, dy)[#show-date(card.at("date", default: none))]
|
|
42
42
|
tf(135pt, dy)[#card.at("departure_point", default: "")]
|
|
@@ -4,7 +4,7 @@ quill:
|
|
|
4
4
|
backend: typst
|
|
5
5
|
plate_file: plate.typ
|
|
6
6
|
example_file: example.md
|
|
7
|
-
description:
|
|
7
|
+
description: U.S. Air Force and Space Force Official Memorandum
|
|
8
8
|
|
|
9
9
|
main:
|
|
10
10
|
body:
|
|
@@ -182,7 +182,7 @@ main:
|
|
|
182
182
|
compact: true
|
|
183
183
|
description: "Optional marking appended after double slash (e.g. NF with classification CUI renders as CUI//NF). Leave blank to show only the classification with no //."
|
|
184
184
|
|
|
185
|
-
|
|
185
|
+
leaf_kinds:
|
|
186
186
|
indorsement:
|
|
187
187
|
description: Chain of routing endorsements. Each endorsement block adds an official response or forwarding action to the original memo.
|
|
188
188
|
ui:
|
|
@@ -225,13 +225,14 @@ card_types:
|
|
|
225
225
|
action:
|
|
226
226
|
type: string
|
|
227
227
|
enum:
|
|
228
|
-
-
|
|
228
|
+
- ""
|
|
229
229
|
- undecided
|
|
230
230
|
- approve
|
|
231
231
|
- disapprove
|
|
232
|
+
default: ""
|
|
232
233
|
ui:
|
|
233
234
|
group: Addressing
|
|
234
|
-
description: "Action taken by the endorser.
|
|
235
|
+
description: "Action taken by the endorser. Leave blank to hide the Approve/Disapprove line entirely, 'undecided' to display the line with neither option circled, 'approve' or 'disapprove' to circle the selected option."
|
|
235
236
|
attachments:
|
|
236
237
|
type: array
|
|
237
238
|
ui:
|
|
@@ -43,7 +43,7 @@ The `usaf_memo` Quill package takes care of all 33-337 formatting details. Focus
|
|
|
43
43
|
Do not include a complimentary close (e.g. "Respectfully,") in official memorandums.
|
|
44
44
|
|
|
45
45
|
---
|
|
46
|
-
|
|
46
|
+
KIND: indorsement
|
|
47
47
|
for: ORG/SYMBOL
|
|
48
48
|
format: standard
|
|
49
49
|
from: ORG/SYMBOL
|
|
@@ -53,4 +53,4 @@ signature_block:
|
|
|
53
53
|
- Duty Title
|
|
54
54
|
---
|
|
55
55
|
|
|
56
|
-
This body and the metdata above are an indorsement card. Multiple or no indorsements
|
|
56
|
+
This body and the metdata above are an indorsement card. Multiple or no indorsements leaves can be used.
|
|
@@ -12,13 +12,14 @@
|
|
|
12
12
|
#let backmatter(
|
|
13
13
|
signature_block: none,
|
|
14
14
|
signature_blank_lines: 4,
|
|
15
|
+
signing_field: none,
|
|
15
16
|
attachments: none,
|
|
16
17
|
cc: none,
|
|
17
18
|
distribution: none,
|
|
18
19
|
leading_pagebreak: false,
|
|
19
20
|
) = {
|
|
20
21
|
// Render backmatter sections without paragraph numbering
|
|
21
|
-
render-signature-block(signature_block, signature-blank-lines: signature_blank_lines)
|
|
22
|
+
render-signature-block(signature_block, signature-blank-lines: signature_blank_lines, signing-field: signing_field)
|
|
22
23
|
render-backmatter-sections(
|
|
23
24
|
attachments: attachments,
|
|
24
25
|
cc: cc,
|
|
@@ -200,7 +200,7 @@
|
|
|
200
200
|
context {
|
|
201
201
|
let heading_buffer = none
|
|
202
202
|
// Filter out zero-width paragraphs so that an empty body (e.g. empty
|
|
203
|
-
// string from a
|
|
203
|
+
// string from a KIND with no text) emits nothing and collapses to zero
|
|
204
204
|
// vertical space. Tables are always kept regardless of measured width.
|
|
205
205
|
let items = PAR_BUFFER.get().filter(item =>
|
|
206
206
|
item.kind == "table" or measure(item.content).width > 0pt
|
|
@@ -136,14 +136,14 @@
|
|
|
136
136
|
LINE_STRIDE.update(line-stride)
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
metadata((
|
|
139
|
+
[#metadata((
|
|
140
140
|
subject: subject,
|
|
141
141
|
original_date: actual_date,
|
|
142
142
|
original_from: first-or-value(memo_from),
|
|
143
143
|
body_font: body_font,
|
|
144
144
|
font_size: font_size,
|
|
145
145
|
memo_style: memo_style,
|
|
146
|
-
))
|
|
146
|
+
)) <usaf-memo-config>]
|
|
147
147
|
|
|
148
148
|
render-date-section(actual_date, memo-style: memo_style)
|
|
149
149
|
render-for-section(memo_for, memo_for_cols)
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
to: none,
|
|
19
19
|
signature_block: none,
|
|
20
20
|
signature_blank_lines: 4,
|
|
21
|
+
signing_field: none,
|
|
21
22
|
attachments: none,
|
|
22
23
|
cc: none,
|
|
23
24
|
date: none,
|
|
@@ -45,7 +46,7 @@
|
|
|
45
46
|
let ind_from = first-or-value(from)
|
|
46
47
|
let ind_for = to
|
|
47
48
|
|
|
48
|
-
// An empty body (e.g. a
|
|
49
|
+
// An empty body (e.g. a KIND with only an action selected and no markdown
|
|
49
50
|
// body) collapses to zero rendered layout via render-body's filter. To
|
|
50
51
|
// make the "empty body takes no layout space" guarantee end-to-end, also
|
|
51
52
|
// suppress the spacing the surrounding code reserves *for* the body:
|
|
@@ -57,12 +58,18 @@
|
|
|
57
58
|
// or whitespace-only; comparing against `[]` reliably detects that.
|
|
58
59
|
let body_empty = content == []
|
|
59
60
|
|
|
61
|
+
let effective_action = if action == none or type(action) != str or action.trim() == "" {
|
|
62
|
+
none
|
|
63
|
+
} else {
|
|
64
|
+
action
|
|
65
|
+
}
|
|
66
|
+
|
|
60
67
|
if format != "informal" {
|
|
61
68
|
// Step the counter BEFORE the context block to avoid read-then-update loop
|
|
62
69
|
counters.indorsement.step()
|
|
63
70
|
|
|
64
71
|
context {
|
|
65
|
-
let config = query(
|
|
72
|
+
let config = query(<usaf-memo-config>).first().value
|
|
66
73
|
let memo-style = config.at("memo_style", default: "usaf")
|
|
67
74
|
let original_subject = config.subject
|
|
68
75
|
let original_date = config.original_date
|
|
@@ -103,27 +110,24 @@
|
|
|
103
110
|
}
|
|
104
111
|
// Header→content gap. Skipped when there is neither an action line nor
|
|
105
112
|
// body to follow — render-signature-block supplies its own 4-line gap.
|
|
106
|
-
if
|
|
113
|
+
if effective_action != none or not body_empty {
|
|
107
114
|
blank-line()
|
|
108
115
|
}
|
|
109
116
|
}
|
|
110
117
|
|
|
111
118
|
// Show action line only when an action decision is set (not `none`)
|
|
112
|
-
if
|
|
113
|
-
render-action-line(
|
|
119
|
+
if effective_action != none {
|
|
120
|
+
render-action-line(effective_action, trailing-blank-line: not body_empty)
|
|
114
121
|
}
|
|
115
122
|
|
|
116
123
|
if not body_empty {
|
|
117
124
|
context {
|
|
118
|
-
let memo-style =
|
|
119
|
-
let items = query(metadata)
|
|
120
|
-
if items.len() > 0 { items.last().value.at("memo_style", default: "usaf") } else { "usaf" }
|
|
121
|
-
}
|
|
125
|
+
let memo-style = query(<usaf-memo-config>).first().value.at("memo_style", default: "usaf")
|
|
122
126
|
render-body(content, memo-style: memo-style)
|
|
123
127
|
}
|
|
124
128
|
}
|
|
125
129
|
|
|
126
|
-
render-signature-block(signature_block, signature-blank-lines: signature_blank_lines)
|
|
130
|
+
render-signature-block(signature_block, signature-blank-lines: signature_blank_lines, signing-field: signing_field)
|
|
127
131
|
|
|
128
132
|
render-backmatter-sections(attachments: attachments, cc: cc)
|
|
129
133
|
}
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
/// - content (content): The body content to render
|
|
22
22
|
/// -> content
|
|
23
23
|
#let mainmatter(it) = context {
|
|
24
|
-
let config = query(
|
|
24
|
+
let config = query(<usaf-memo-config>).first().value
|
|
25
25
|
let memo-style = config.at("memo_style", default: "usaf")
|
|
26
26
|
render-body(it, memo-style: memo-style)
|
|
27
27
|
}
|
|
@@ -54,16 +54,16 @@
|
|
|
54
54
|
} else {
|
|
55
55
|
// Isolate seal column from document `font_size`: stack `em` spacing and subtitle
|
|
56
56
|
// must not scale with body text (see frontmatter `set text(size: font_size)`).
|
|
57
|
-
|
|
57
|
+
// Subtitle is wrapped in `box` so it stays on one line and may extend past
|
|
58
|
+
// the seal's 2in column rather than wrapping.
|
|
59
|
+
block[
|
|
58
60
|
#set text(9pt, font: font, fill: LETTERHEAD_COLOR, weight: "bold")
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
)
|
|
66
|
-
]
|
|
61
|
+
// Spacing applies between positional stack children only, not one `[…]` body.
|
|
62
|
+
#stack(
|
|
63
|
+
spacing: 0.5em,
|
|
64
|
+
fit-box(width: 2in, height: 1in)[#letterhead-seal],
|
|
65
|
+
box(upper(ensure-string(letterhead-seal-subtitle))),
|
|
66
|
+
)
|
|
67
67
|
]
|
|
68
68
|
}
|
|
69
69
|
place(
|
|
@@ -148,7 +148,7 @@
|
|
|
148
148
|
// AFH 33-337 long-name example: "Signature block adjusted to the left" when a
|
|
149
149
|
// long name would otherwise exceed the right margin.
|
|
150
150
|
|
|
151
|
-
#let render-signature-block(signature-lines, signature-blank-lines: 4) = {
|
|
151
|
+
#let render-signature-block(signature-lines, signature-blank-lines: 4, signing-field: none) = {
|
|
152
152
|
signature-lines = ensure-array(signature-lines)
|
|
153
153
|
// AFH 33-337: "fifth line below" = 4 blank lines between text and signature block.
|
|
154
154
|
// breakable: false discourages orphaning the signature block onto a page by itself.
|
|
@@ -177,6 +177,20 @@
|
|
|
177
177
|
default-pad
|
|
178
178
|
}
|
|
179
179
|
block(breakable: false)[
|
|
180
|
+
#if signing-field != none {
|
|
181
|
+
let stride = {
|
|
182
|
+
let s = LINE_STRIDE.get()
|
|
183
|
+
if s == none {
|
|
184
|
+
let one-line = measure(par(spacing: 0pt)[x]).height
|
|
185
|
+
measure(par(spacing: 0pt)[x#linebreak()x]).height - one-line
|
|
186
|
+
} else { s }
|
|
187
|
+
}
|
|
188
|
+
place(
|
|
189
|
+
dx: left-pad,
|
|
190
|
+
dy: -(stride * signature-blank-lines),
|
|
191
|
+
box(width: body-width - left-pad, height: stride * signature-blank-lines, signing-field),
|
|
192
|
+
)
|
|
193
|
+
}
|
|
180
194
|
#align(left)[
|
|
181
195
|
#pad(left: left-pad)[
|
|
182
196
|
#text(hyphenate: false)[
|
|
@@ -196,16 +210,14 @@
|
|
|
196
210
|
// ACTION LINE RENDERING
|
|
197
211
|
// =============================================================================
|
|
198
212
|
// Renders the Approve / Disapprove action line for indorsement memos.
|
|
199
|
-
// action: "
|
|
200
|
-
//
|
|
201
|
-
//
|
|
202
|
-
// action is "undecided", "approve", or "disapprove". "none" suppresses
|
|
203
|
-
// the line entirely (handled by the caller).
|
|
213
|
+
// action: "undecided" = both options rendered plain (no circle),
|
|
214
|
+
// "approve" = Approve circled, "disapprove" = Disapprove circled.
|
|
215
|
+
// Empty/none suppression is handled by the caller before this is invoked.
|
|
204
216
|
|
|
205
217
|
#let render-action-line(action, trailing-blank-line: true) = {
|
|
206
218
|
assert(
|
|
207
|
-
action in ("
|
|
208
|
-
message: "action must be \"
|
|
219
|
+
action in ("undecided", "approve", "disapprove"),
|
|
220
|
+
message: "action must be \"undecided\", \"approve\", or \"disapprove\"",
|
|
209
221
|
)
|
|
210
222
|
// No leading blank-line: the caller (indorsement.typ) already emits the
|
|
211
223
|
// header→content gap once. The action line's `block(sticky: true)`
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#import "@local/quillmark-helper:0.1.0": data
|
|
1
|
+
#import "@local/quillmark-helper:0.1.0": data, signature-field
|
|
2
2
|
#import "@local/tonguetoquill-usaf-memo:3.0.0": backmatter, frontmatter, indorsement, mainmatter
|
|
3
3
|
|
|
4
4
|
// Frontmatter configuration
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
#backmatter(
|
|
58
58
|
// Signature block
|
|
59
59
|
signature_block: data.signature_block,
|
|
60
|
+
signing_field: signature-field("Signature"),
|
|
60
61
|
|
|
61
62
|
// Optional cc
|
|
62
63
|
..if "cc" in data { (cc: data.cc) },
|
|
@@ -68,9 +69,9 @@
|
|
|
68
69
|
..if "attachments" in data { (attachments: data.attachments) },
|
|
69
70
|
)
|
|
70
71
|
|
|
71
|
-
// Indorsements - iterate through
|
|
72
|
-
#for card in data.
|
|
73
|
-
if card.
|
|
72
|
+
// Indorsements - iterate through LEAVES array and filter by KIND type
|
|
73
|
+
#for (i, card) in data.LEAVES.enumerate() {
|
|
74
|
+
if card.KIND == "indorsement" {
|
|
74
75
|
// The quillmark helper leaves an unset/whitespace-only markdown body as
|
|
75
76
|
// the empty string `""`; only non-empty bodies are eval'd into content.
|
|
76
77
|
// Pass truly empty content (`[]`) in the empty case so indorsement can
|
|
@@ -90,6 +91,7 @@
|
|
|
90
91
|
from: card.at("from", default: ""),
|
|
91
92
|
to: card.at("for", default: ""),
|
|
92
93
|
signature_block: card.signature_block,
|
|
94
|
+
signing_field: signature-field("Ind_" + str(i) + "_Signature"),
|
|
93
95
|
..if "attachments" in card { (attachments: card.attachments) },
|
|
94
96
|
..if "cc" in card { (cc: card.cc) },
|
|
95
97
|
format: card.at("format", default: "standard"),
|