@airmark/quiver 0.35.7 → 0.36.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@airmark/quiver",
3
- "version": "0.35.7",
3
+ "version": "0.36.0",
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.36.0",
22
- "@quillmark/wasm": "^0.124.0",
23
- "quillkit": "0.16.0"
21
+ "@quillmark/quiver": "0.37.0",
22
+ "@quillmark/wasm": "^0.125.0",
23
+ "quillkit": "0.17.0"
24
24
  },
25
25
  "scripts": {
26
26
  "test": "quillkit test && node scripts/check-examples.mjs && node scripts/check-templates.mjs",
@@ -35,60 +35,50 @@ card_kinds:
35
35
  experience:
36
36
  title: Record of Experience
37
37
  description: One row of the Record of Experience. The form holds up to 37 rows across two pages.
38
- ui:
39
- groups: [record]
40
38
  body:
41
39
  enabled: false
42
- fields:
43
- date:
44
- type: date
45
- default: ""
46
- ui:
47
- group: record
48
- description: The date of the action.
40
+ sections:
41
+ record:
42
+ fields:
43
+ date:
44
+ type: date
45
+ default: ""
46
+ description: The date of the action.
49
47
 
50
- action:
51
- type: string
52
- default: ""
53
- ui:
54
- group: record
55
- description: The action taken. Use one row per type of action.
48
+ action:
49
+ type: string
50
+ default: ""
51
+ description: The action taken. Use one row per type of action.
56
52
 
57
- written_grade:
58
- type: string
59
- default: ""
60
- ui:
61
- group: record
62
- compact: true
63
- description: The written grade, if any, for example 3.
53
+ written_grade:
54
+ type: string
55
+ default: ""
56
+ ui:
57
+ compact: true
58
+ description: The written grade, if any, for example 3.
64
59
 
65
- written_grade_date:
66
- type: date
67
- default: ""
68
- ui:
69
- group: record
70
- compact: true
71
- description: The date of the written grade.
60
+ written_grade_date:
61
+ type: date
62
+ default: ""
63
+ ui:
64
+ compact: true
65
+ description: The date of the written grade.
72
66
 
73
- positional_grade:
74
- type: string
75
- default: ""
76
- ui:
77
- group: record
78
- compact: true
79
- description: The positional grade, if any, for example 2.
67
+ positional_grade:
68
+ type: string
69
+ default: ""
70
+ ui:
71
+ compact: true
72
+ description: The positional grade, if any, for example 2.
80
73
 
81
- positional_grade_date:
82
- type: date
83
- default: ""
84
- ui:
85
- group: record
86
- compact: true
87
- description: The date of the positional grade.
74
+ positional_grade_date:
75
+ type: date
76
+ default: ""
77
+ ui:
78
+ compact: true
79
+ description: The date of the positional grade.
88
80
 
89
- auth_or_remarks:
90
- type: string
91
- default: ""
92
- ui:
93
- group: record
94
- description: Authenticating initials, for example J.A.D., or remarks.
81
+ auth_or_remarks:
82
+ type: string
83
+ default: ""
84
+ description: Authenticating initials, for example J.A.D., or remarks.
@@ -1,11 +1,11 @@
1
- #import "@local/quillmark-helper:0.1.0": data
1
+ #import "@local/quillmark-helper:0.2.0" as qm
2
2
  #import "@local/typst-af4141:0.1.0": form
3
3
 
4
4
  #set text(font: "NimbusRomNo9L")
5
5
 
6
6
  // A `type: date` field arrives as a native `datetime`, blank as `none`. The
7
7
  // form's cells are strings, so a date is formatted here rather than placed as
8
- // ink: `display` would give the glyphs a schema address, but the form draws
8
+ // ink: `qm.ink` would give the glyphs a schema address, but the form draws
9
9
  // each cell into a fixed box it builds itself, which takes a `str`.
10
10
  #let form-cell(v) = {
11
11
  let pattern = "[month padding:none]/[day padding:none]/[year]"
@@ -29,10 +29,10 @@
29
29
  #let vals = (:)
30
30
 
31
31
  // --- Admin fields (page 1 header) ---
32
- #if data.name != "" { vals.insert("commonforms_text_p1_1", data.name) }
33
- #if data.unit != "" { vals.insert("commonforms_text_p1_2", data.unit) }
34
- #if data.grade != "" { vals.insert("commonforms_text_p1_3", data.grade) }
35
- #if data.commanders_auth != "" { vals.insert("commonforms_text_p1_116", data.commanders_auth) }
32
+ #if qm.data.name != "" { vals.insert("commonforms_text_p1_1", qm.data.name) }
33
+ #if qm.data.unit != "" { vals.insert("commonforms_text_p1_2", qm.data.unit) }
34
+ #if qm.data.grade != "" { vals.insert("commonforms_text_p1_3", qm.data.grade) }
35
+ #if qm.data.commanders_auth != "" { vals.insert("commonforms_text_p1_116", qm.data.commanders_auth) }
36
36
 
37
37
  // --- Experience table rows from CARDS ---
38
38
  // The form supports 16 rows on page 1 and 21 rows on page 2 (37 total).
@@ -40,7 +40,7 @@
40
40
  #let max-rows = 37
41
41
  #{
42
42
  let row = 0
43
- for card in data.at("$cards") {
43
+ for card in qm.data.at("$cards") {
44
44
  if card.at("$kind", default: none) == "experience" {
45
45
  if row < max-rows {
46
46
  for (col, key) in col-keys.enumerate() {
@@ -9,152 +9,127 @@ typst:
9
9
 
10
10
  main:
11
11
  description: "Add a card for each section of the agreement. Sections print in DoDI 4000.19 order, whatever order you add them in."
12
- ui:
13
- groups: [parties, correspondence, terms, financial_details, signatures]
14
12
  body:
15
13
  enabled: false
16
- fields:
17
- first_party_name:
18
- type: plaintext
19
- inline: true
20
- ui:
21
- group: parties
22
- description: "Full name and acronym of the organization proposing the agreement."
23
-
24
- second_party_name:
25
- type: plaintext
26
- inline: true
27
- ui:
28
- group: parties
29
- description: "Full name and acronym of the other organization."
30
-
31
- second_party_is_non_government:
32
- type: boolean
33
- default: false
34
- ui:
35
- group: parties
36
- compact: true
37
- description: "Whether the second party is outside the government. If so, its mailing address appears in the opening paragraph."
38
-
39
- subject:
40
- type: plaintext
41
- inline: true
42
- ui:
43
- group: parties
44
- description: "What the agreement covers. It follows 'FOR' in the title."
45
-
46
- agreement_number:
47
- type: plaintext
48
- inline: true
49
- ui:
50
- group: parties
51
- description: The agreement's identification number.
52
-
53
- second_party_mailing_address:
54
- type: array
55
- items:
56
- type: plaintext
57
- default: []
58
- ui:
59
- group: correspondence
60
- description: "The second party's mailing address for correspondence."
61
-
62
- reimbursable:
63
- type: boolean
64
- default: false
65
- ui:
66
- group: terms
67
- compact: true
68
- description: "Whether the parties reimburse each other. If so, fill in Financial Details, which prints as Attachment A."
69
-
70
- reimbursable_support:
71
- type: richtext
72
- inline: true
73
- default: ""
74
- ui:
75
- group: financial_details
76
- description: "The support being reimbursed."
77
-
78
- estimated_amount:
79
- type: plaintext
80
- inline: true
81
- default: ""
82
- ui:
83
- group: financial_details
84
- compact: true
85
- description: "Estimated amount to be reimbursed, for example $50,000."
86
-
87
- appropriation_fy:
88
- type: plaintext
89
- inline: true
90
- default: ""
91
- ui:
92
- group: financial_details
93
- compact: true
94
- description: "Fiscal year of the appropriation, for example 2027."
95
-
96
- cost_center_provider:
97
- type: plaintext
98
- inline: true
99
- default: ""
100
- ui:
101
- group: financial_details
102
- compact: true
103
- description: "The provider's cost center number, if required."
104
-
105
- cost_center_receiver:
106
- type: plaintext
107
- inline: true
108
- default: ""
109
- ui:
110
- group: financial_details
111
- compact: true
112
- description: "The receiver's cost center number, if required."
113
-
114
- financial_poc_provider:
115
- type: plaintext
116
- inline: true
117
- default: ""
118
- ui:
119
- group: financial_details
120
- description: "The provider's financial point of contact."
121
-
122
- financial_poc_receiver:
123
- type: plaintext
124
- inline: true
125
- default: ""
126
- ui:
127
- group: financial_details
128
- description: "The receiver's financial point of contact."
129
-
130
- financial_additional_info:
131
- type: richtext
132
- inline: true
133
- default: ""
134
- ui:
135
- group: financial_details
136
- description: "Any other financial terms the agreement needs."
137
-
138
- first_party_signatory:
139
- type: plaintext
140
- inline: true
141
- ui:
142
- group: signatures
143
- description: "Name and title of the first party's approving official."
144
-
145
- second_party_signatory:
146
- type: plaintext
147
- inline: true
148
- ui:
149
- group: signatures
150
- description: "Name and title of the second party's approving official."
151
-
152
- mid_point_review_due_date:
153
- type: date
154
- ui:
155
- group: signatures
156
- compact: true
157
- description: "When the mid-point review of the agreement is due."
14
+ sections:
15
+ parties:
16
+ fields:
17
+ first_party_name:
18
+ type: plaintext
19
+ inline: true
20
+ description: "Full name and acronym of the organization proposing the agreement."
21
+
22
+ second_party_name:
23
+ type: plaintext
24
+ inline: true
25
+ description: "Full name and acronym of the other organization."
26
+
27
+ second_party_is_non_government:
28
+ type: boolean
29
+ default: false
30
+ ui:
31
+ compact: true
32
+ description: "Whether the second party is outside the government. If so, its mailing address appears in the opening paragraph."
33
+
34
+ subject:
35
+ type: plaintext
36
+ inline: true
37
+ description: "What the agreement covers. It follows 'FOR' in the title."
38
+
39
+ agreement_number:
40
+ type: plaintext
41
+ inline: true
42
+ description: The agreement's identification number.
43
+ correspondence:
44
+ fields:
45
+ second_party_mailing_address:
46
+ type: array
47
+ items:
48
+ type: plaintext
49
+ default: []
50
+ description: "The second party's mailing address for correspondence."
51
+ terms:
52
+ fields:
53
+ reimbursable:
54
+ type: boolean
55
+ default: false
56
+ ui:
57
+ compact: true
58
+ description: "Whether the parties reimburse each other. If so, fill in Financial Details, which prints as Attachment A."
59
+ financial_details:
60
+ fields:
61
+ reimbursable_support:
62
+ type: richtext
63
+ inline: true
64
+ default: ""
65
+ description: "The support being reimbursed."
66
+
67
+ estimated_amount:
68
+ type: plaintext
69
+ inline: true
70
+ default: ""
71
+ ui:
72
+ compact: true
73
+ description: "Estimated amount to be reimbursed, for example $50,000."
74
+
75
+ appropriation_fy:
76
+ type: plaintext
77
+ inline: true
78
+ default: ""
79
+ ui:
80
+ compact: true
81
+ description: "Fiscal year of the appropriation, for example 2027."
82
+
83
+ cost_center_provider:
84
+ type: plaintext
85
+ inline: true
86
+ default: ""
87
+ ui:
88
+ compact: true
89
+ description: "The provider's cost center number, if required."
90
+
91
+ cost_center_receiver:
92
+ type: plaintext
93
+ inline: true
94
+ default: ""
95
+ ui:
96
+ compact: true
97
+ description: "The receiver's cost center number, if required."
98
+
99
+ financial_poc_provider:
100
+ type: plaintext
101
+ inline: true
102
+ default: ""
103
+ description: "The provider's financial point of contact."
104
+
105
+ financial_poc_receiver:
106
+ type: plaintext
107
+ inline: true
108
+ default: ""
109
+ description: "The receiver's financial point of contact."
110
+
111
+ financial_additional_info:
112
+ type: richtext
113
+ inline: true
114
+ default: ""
115
+ description: "Any other financial terms the agreement needs."
116
+ signatures:
117
+ fields:
118
+ first_party_signatory:
119
+ type: plaintext
120
+ inline: true
121
+ description: "Name and title of the first party's approving official."
122
+
123
+ second_party_signatory:
124
+ type: plaintext
125
+ inline: true
126
+ description: "Name and title of the second party's approving official."
127
+
128
+ mid_point_review_due_date:
129
+ type: date
130
+ ui:
131
+ compact: true
132
+ description: "When the mid-point review of the agreement is due."
158
133
 
159
134
  card_kinds:
160
135
  background:
@@ -252,16 +227,14 @@ card_kinds:
252
227
  attachment:
253
228
  title: Attachment
254
229
  description: "An attachment on its own page, lettered from B. Attachment A is reserved for financial details and is added for you when the agreement is reimbursable."
255
- ui:
256
- groups: [attachment]
257
230
  seed:
258
231
  title: "[Insert Attachment Title]"
259
232
  $body: |
260
233
  [Describe the attachment's content here.]
261
- fields:
262
- title:
263
- type: plaintext
264
- inline: true
265
- ui:
266
- group: attachment
267
- description: "The attachment's title."
234
+ sections:
235
+ attachment:
236
+ fields:
237
+ title:
238
+ type: plaintext
239
+ inline: true
240
+ description: "The attachment's title."
@@ -1,4 +1,4 @@
1
- #import "@local/quillmark-helper:0.1.0": data, display, signature-field
1
+ #import "@local/quillmark-helper:0.2.0" as qm
2
2
  #import "@local/typst-afmc-moa:0.1.0": (
3
3
  attachment, attachment-card, backmatter, date-pattern, frontmatter, mainmatter, trim-inline,
4
4
  )
@@ -8,21 +8,21 @@
8
8
  // package can interpolate a field straight into prose without it printing as
9
9
  // "the Foo and" or "(NASA) ." — and without boxing, which would stop a long
10
10
  // party name from wrapping.
11
- #let text-field(key) = trim-inline(data.at(key))
11
+ #let text-field(key) = trim-inline(qm.data.at(key))
12
12
 
13
13
  // Resolve optional fields with defaults; required fields are read directly
14
14
  // since Quill.yaml enforces their presence.
15
15
  #let resolved = (
16
16
  first_party_name: text-field("first_party_name"),
17
17
  second_party_name: text-field("second_party_name"),
18
- second_party_is_non_government: data.second_party_is_non_government,
18
+ second_party_is_non_government: qm.data.second_party_is_non_government,
19
19
  subject: text-field("subject"),
20
20
  agreement_number: text-field("agreement_number"),
21
21
 
22
22
  // Joined inline by the package, which trims each line itself.
23
- second_party_mailing_address: data.second_party_mailing_address,
23
+ second_party_mailing_address: qm.data.second_party_mailing_address,
24
24
 
25
- reimbursable: data.reimbursable,
25
+ reimbursable: qm.data.reimbursable,
26
26
  reimbursable_support: text-field("reimbursable_support"),
27
27
  estimated_amount: text-field("estimated_amount"),
28
28
  appropriation_fy: text-field("appropriation_fy"),
@@ -36,10 +36,10 @@
36
36
  second_party_signatory: text-field("second_party_signatory"),
37
37
  // `data.mid_point_review_due_date` is the native `datetime` and would render
38
38
  // identically, but its ink would be born inside the package and carry no
39
- // schema address. `display` places the field's content projection instead, so
39
+ // schema address. `qm.ink` places the field's content projection instead, so
40
40
  // the printed date stays click-to-edit; a blank one yields `none`, which
41
41
  // `format-date` renders as nothing.
42
- mid_point_review_due_date: display("mid_point_review_due_date", date-pattern),
42
+ mid_point_review_due_date: qm.ink(qm.data, "mid_point_review_due_date", date-pattern),
43
43
  )
44
44
 
45
45
  // ── Frontmatter: page setup, title block, intro paragraph ──────────────────
@@ -59,7 +59,7 @@
59
59
  // each kind is used.
60
60
  #let card-body(kind) = {
61
61
  let bodies = ()
62
- for card in data.at("$cards") {
62
+ for card in qm.data.at("$cards") {
63
63
  if card.at("$kind", default: none) == kind and "$body" in card {
64
64
  bodies.push(card.at("$body"))
65
65
  }
@@ -95,8 +95,8 @@
95
95
  first_party_signatory: resolved.first_party_signatory,
96
96
  second_party_signatory: resolved.second_party_signatory,
97
97
  mid_point_review_due_date: resolved.mid_point_review_due_date,
98
- first_signing_field: signature-field("first_party_signature"),
99
- second_signing_field: signature-field("second_party_signature"),
98
+ first_signing_field: qm.signature-field("first_party_signature"),
99
+ second_signing_field: qm.signature-field("second_party_signature"),
100
100
  )
101
101
 
102
102
  // ── Attachment A: only for reimbursable MOAs ────────────────────────────────
@@ -106,7 +106,7 @@
106
106
  // by their position among *attachments*. The standard sections above are also
107
107
  // cards in the same `$cards` array, so enumerating the array as a whole would
108
108
  // letter the first attachment by its global index (J, not B).
109
- #let attachment_cards = data.at("$cards").filter(card => card.at("$kind", default: none) == "attachment")
109
+ #let attachment_cards = qm.data.at("$cards").filter(card => card.at("$kind", default: none) == "attachment")
110
110
  #for (i, card) in attachment_cards.enumerate() {
111
111
  // The quillmark helper leaves an unset/whitespace-only markdown body as
112
112
  // the empty string `""`; only non-empty bodies are eval'd into content.
@@ -12,53 +12,47 @@ typst:
12
12
  # are the section's own, so an entry cannot land under the wrong heading.
13
13
  main:
14
14
  description: "Your name and contact details go at the top. Add a card for each section, in the order you want them."
15
- ui:
16
- groups: [identity, page]
17
15
  # Everything prints under a section heading, so main carries no body: a
18
16
  # summary is a Summary card.
19
17
  body:
20
18
  enabled: false
21
- fields:
22
- name:
23
- type: string
24
- ui:
25
- group: identity
26
- description: "Your name as you want it read. It also names the PDF."
19
+ sections:
20
+ identity:
21
+ fields:
22
+ name:
23
+ type: string
24
+ description: "Your name as you want it read. It also names the PDF."
27
25
 
28
- contacts:
29
- type: array
30
- items:
31
- type: string
32
- ui:
33
- group: identity
34
- description: "How to reach you, one per line, most preferred first: email, phone, a profile such as linkedin.com/in/you, your city. Email addresses, web addresses, and phone numbers become links."
35
-
36
- paper:
37
- type: enum
38
- values:
39
- - us-letter
40
- - a4
41
- default: us-letter
42
- ui:
43
- group: page
44
- compact: true
45
- description: "Page size."
26
+ contacts:
27
+ type: array
28
+ items:
29
+ type: string
30
+ description: "How to reach you, one per line, most preferred first: email, phone, a profile such as linkedin.com/in/you, your city. Email addresses, web addresses, and phone numbers become links."
31
+ page:
32
+ fields:
33
+ paper:
34
+ type: enum
35
+ values:
36
+ - us-letter
37
+ - a4
38
+ default: us-letter
39
+ ui:
40
+ compact: true
41
+ description: "Page size."
46
42
 
47
- font_size:
48
- type: number
49
- default: 12
50
- ui:
51
- group: page
52
- compact: true
53
- description: "Body text size in points. To fit one page, try a half point smaller before cutting content."
43
+ font_size:
44
+ type: number
45
+ default: 12
46
+ ui:
47
+ compact: true
48
+ description: "Body text size in points. To fit one page, try a half point smaller before cutting content."
54
49
 
55
- margin:
56
- type: number
57
- default: 0.5
58
- ui:
59
- group: page
60
- compact: true
61
- description: "Margin in inches on all sides. Below about 0.4, printers may clip the edges."
50
+ margin:
51
+ type: number
52
+ default: 0.5
53
+ ui:
54
+ compact: true
55
+ description: "Margin in inches on all sides. Below about 0.4, printers may clip the edges."
62
56
 
63
57
 
64
58
  # Each kind opens with the heading it prints. Its `default:` is the usual one,