@airmark/quiver 0.26.0 → 0.28.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 (38) hide show
  1. package/README.md +79 -31
  2. package/package.json +7 -5
  3. package/quills/af4141/0.1.0/Quill.yaml +21 -11
  4. package/quills/af4141/0.1.0/packages/typst-af4141/lib.typ +17 -3
  5. package/quills/af4141/0.1.0/plate.typ +12 -5
  6. package/quills/daf1206/0.1.0/Quill.yaml +3 -1
  7. package/quills/daf4392/0.1.0/Quill.yaml +12 -9
  8. package/quills/daf4392/0.1.0/packages/daf4392page2_pkg/fonts/GNU General Public License.txt +340 -0
  9. package/quills/daf4392/0.1.0/packages/daf4392page2_pkg/fonts/NimbusRomNo9L-Med.otf +0 -0
  10. package/quills/daf4392/0.1.0/packages/daf4392page2_pkg/fonts/NimbusRomNo9L-MedIta.otf +0 -0
  11. package/quills/daf4392/0.1.0/packages/daf4392page2_pkg/fonts/NimbusRomNo9L-Reg.otf +0 -0
  12. package/quills/daf4392/0.1.0/packages/daf4392page2_pkg/fonts/NimbusRomNo9L-RegIta.otf +0 -0
  13. package/quills/daf4392/0.1.0/packages/daf4392page2_pkg/lib.typ +21 -4
  14. package/quills/daf4392/0.1.0/plate.typ +1 -1
  15. package/quills/usaf_memo/0.2.0/Quill.yaml +47 -42
  16. package/quills/usaf_memo/0.2.0/packages/tonguetoquill-usaf-memo/fonts/Cinzel/Cinzel[wght].ttf +0 -0
  17. package/quills/usaf_memo/0.2.0/packages/tonguetoquill-usaf-memo/src/indorsement.typ +57 -24
  18. package/quills/usaf_memo/0.2.0/packages/tonguetoquill-usaf-memo/src/utils.typ +33 -4
  19. package/quills/usaf_memo/0.2.0/plate.typ +19 -14
  20. package/quills/af4141/0.1.0/assets/Figtree-Bold.ttf +0 -0
  21. package/quills/af4141/0.1.0/assets/Figtree-Italic.ttf +0 -0
  22. package/quills/af4141/0.1.0/assets/Figtree-Regular.ttf +0 -0
  23. package/quills/af4141/0.1.0/design/TASK.md +0 -20
  24. package/quills/af4141/0.1.0/design/af4141.pdf +0 -0
  25. package/quills/daf1206/0.1.0/DAFForm_1206_prepared.pdf +0 -0
  26. package/quills/daf4392/0.1.0/assets/arimo-v35-latin-700.ttf +0 -0
  27. package/quills/daf4392/0.1.0/assets/arimo-v35-latin-700italic.ttf +0 -0
  28. package/quills/daf4392/0.1.0/assets/arimo-v35-latin-italic.ttf +0 -0
  29. package/quills/daf4392/0.1.0/assets/arimo-v35-latin-regular.ttf +0 -0
  30. package/quills/usaf_memo/0.2.0/.quillignore +0 -31
  31. package/quills/usaf_memo/0.2.0/assets/Freedom250_USAF.png +0 -0
  32. package/quills/usaf_memo/0.2.0/assets/Freedom250_USSF.png +0 -0
  33. package/quills/usaf_memo/0.2.0/packages/tonguetoquill-usaf-memo/fonts/Cinzel/Cinzel-Regular.ttf +0 -0
  34. /package/quills/af4141/0.1.0/{assets → packages/typst-af4141/fonts}/GNU General Public License.txt +0 -0
  35. /package/quills/af4141/0.1.0/{assets → packages/typst-af4141/fonts}/NimbusRomNo9L-Med.otf +0 -0
  36. /package/quills/af4141/0.1.0/{assets → packages/typst-af4141/fonts}/NimbusRomNo9L-MedIta.otf +0 -0
  37. /package/quills/af4141/0.1.0/{assets → packages/typst-af4141/fonts}/NimbusRomNo9L-Reg.otf +0 -0
  38. /package/quills/af4141/0.1.0/{assets → packages/typst-af4141/fonts}/NimbusRomNo9L-RegIta.otf +0 -0
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @airmark/quiver
2
2
 
3
- A [Quillmark](https://github.com/nibsbin/quillmark) Source Quiver of Air Force
4
- and DAF official-document Quills, sourced from
3
+ A [Quillmark](https://github.com/nibsbin/quillmark) source quiver of Air Force
4
+ and DAF official-document quills, sourced from
5
5
  [tonguetoquill-collection](https://github.com/nibsbin/tonguetoquill-collection)
6
6
  and aligned to the current Quillmark spec.
7
7
 
@@ -20,28 +20,31 @@ and aligned to the current Quillmark spec.
20
20
  npm install @airmark/quiver @quillmark/quiver @quillmark/wasm
21
21
  ```
22
22
 
23
- This package ships only the Source Quiver assets (`Quiver.yaml` + `quills/`)
24
- and exposes no JavaScript API. All loading is performed by `@quillmark/quiver`,
25
- either from the on-disk package directory or from a packed archive fetched
26
- over HTTP.
23
+ This package ships only the source-quiver assets (`Quiver.yaml` + `quills/`) and
24
+ exposes no JavaScript API. Loading is `@quillmark/quiver`'s, rendering is
25
+ `@quillmark/wasm`'s.
27
26
 
28
27
  ## Usage
29
28
 
30
- ### Load from the exported directory
29
+ ### Load from the installed directory (Node)
31
30
 
32
- Resolve the package's `Quiver.yaml` via `import.meta.resolve` and hand it to
33
- `@quillmark/quiver`:
31
+ Resolve this package's `Quiver.yaml` from your own module — your dependencies are
32
+ reachable from you, not from the loader's install location — and hand its
33
+ directory to `fromDir`:
34
34
 
35
35
  ```ts
36
- import { fileURLToPath } from 'node:url';
37
- import path from 'node:path';
38
- import { Quillmark, Document } from '@quillmark/wasm';
39
- import { Quiver } from '@quillmark/quiver/node';
36
+ import { createRequire } from 'node:module';
37
+ import { dirname } from 'node:path';
38
+ import { Engine, init } from '@quillmark/wasm';
39
+ import { fromDir } from '@quillmark/quiver/node';
40
40
 
41
- const quiverYaml = fileURLToPath(import.meta.resolve('@airmark/quiver/Quiver.yaml'));
42
- const quiver = await Quiver.fromDir(path.dirname(quiverYaml));
41
+ // `init` is the only door to `Document`: there is no static export to reach early.
42
+ const { Document } = await init();
43
+
44
+ const root = dirname(createRequire(import.meta.url).resolve('@airmark/quiver/Quiver.yaml'));
45
+ const quiver = await fromDir(root);
46
+ const engine = new Engine();
43
47
 
44
- const engine = new Quillmark();
45
48
  const doc = Document.fromMarkdown(`~~~
46
49
  $quill: usaf_memo@0.2
47
50
  $kind: main
@@ -53,19 +56,31 @@ signature_block: ["FIRST M. LAST, Rank, USAF", "Duty Title"]
53
56
 
54
57
  Body of the memo.`);
55
58
 
56
- const quill = await quiver.getQuill(doc.quillRef, { engine });
57
- const { artifacts } = quill.render(doc, { format: 'pdf' });
59
+ const quill = await quiver.getQuill(doc.quillRef);
60
+ const { artifacts } = await engine.render(quill, doc, { format: 'pdf' });
58
61
  ```
59
62
 
60
- ### Load a packed Quiver via HTTP
63
+ A quill from `getQuill` is borrowed: every caller asking for that ref gets the
64
+ same instance, so it is not yours to `free()`.
65
+
66
+ ### Load over HTTP (browser)
67
+
68
+ A browser cannot read the source layout, so pack it at deploy time and serve the
69
+ output as static files:
70
+
71
+ ```ts
72
+ import { build } from '@quillmark/quiver/node'; // build step
73
+ await build(root, './public/quivers/airmark');
74
+ ```
61
75
 
62
- Publish a packed Quiver archive (e.g. as a CDN or GitHub release asset) and
63
- load it in the browser or any fetch-capable runtime through `@quillmark/quiver`.
64
- See the upstream `@quillmark/quiver` docs for the packed-format and HTTP API.
76
+ ```ts
77
+ import { Quiver } from '@quillmark/quiver'; // browser runtime
78
+ const quiver = await Quiver.fromBuiltUrl('/quivers/airmark/');
79
+ ```
65
80
 
66
81
  ## Layout
67
82
 
68
- This package is a [Source Quiver](https://www.npmjs.com/package/@quillmark/quiver)
83
+ This package is a [source quiver](https://www.npmjs.com/package/@quillmark/quiver)
69
84
  conforming to the canonical layout:
70
85
 
71
86
  ```
@@ -79,20 +94,53 @@ quills/
79
94
  packages/
80
95
  ```
81
96
 
82
- ## Testing
97
+ ## Working on a quill
83
98
 
84
- `quiver.test.js` at the repo root runs the
85
- [`@quillmark/quiver/testing`](https://www.npmjs.com/package/@quillmark/quiver)
86
- suite under the built-in [`node:test`](https://nodejs.org/api/test.html) runner,
87
- validating each `(quill, version)` pair through the full load +
88
- `engine.quill(tree)` compile pipeline:
99
+ Two verbs, and they answer different questions.
100
+
101
+ Both verbs are [`quillkit`](https://www.npmjs.com/package/quillkit)'s, the quill
102
+ author's toolchain. It resolves `@quillmark/quiver` and `@quillmark/wasm` out of
103
+ this collection's own `node_modules`, so the versions pinned here are the format
104
+ the quiver is packed in and the wasm the gate renders through.
105
+
106
+ **Does it work?** `quillkit test` loads the collection with `fromDir`, compiles
107
+ every quill, and renders each one's example document — the blueprint seeded from
108
+ the `example:` values in `Quill.yaml`. It is the gate CI runs, so a validation
109
+ failure surfaces here rather than on a consumer's build:
89
110
 
90
111
  ```bash
91
112
  npm install
92
113
  npm test
93
114
  ```
94
115
 
116
+ The gate renders nothing the schema did not write, so the coverage is the
117
+ `example:` block: a field with no example is a field no render exercises.
118
+
119
+ **What is it like to use?** `npm run dev` is `quillkit studio`: it packs this
120
+ quiver, serves the studio client over it, and repacks on every save — pick a
121
+ quill, edit the seeded document, watch it paint, read the diagnostics. The
122
+ client is quillkit's own, so there is nothing to install for it and nothing to
123
+ keep in step.
124
+
125
+ Reload to pick up a repack. Editing a quill into an invalid state is not a
126
+ failure of the loop: it packs, and the client says what is wrong with that quill
127
+ and where, while the others stay usable.
128
+
129
+ The two verbs answer to different authorities. `quillkit test` renders through
130
+ the wasm pinned here and is what CI blocks on; the client renders through the
131
+ wasm it was built against, and nothing at runtime reconciles the two. The gate
132
+ is authoritative, studio is advisory.
133
+
134
+ `npm run site` writes the arrangement a deploy serves into `site/` — the client
135
+ at the root, a built quiver at `quiver/` beneath it — without serving it. CI
136
+ uploads that on every run, so a pull request — a fork's included — is reviewed
137
+ by downloading the artifact and serving the directory.
138
+
139
+ `main` is deployed to GitHub Pages by `.github/workflows/studio.yml`, which runs
140
+ that same command and keeps the deploy here: nothing outside this repository
141
+ holds `pages: write`.
142
+
95
143
  ## License
96
144
 
97
- Apache-2.0. Individual Quills carry their own licensing terms; see the
98
- `packages/` directory inside each Quill for upstream font and template licenses.
145
+ Apache-2.0. Individual quills carry their own licensing terms; see the
146
+ `packages/` directory inside each quill for upstream font and template licenses.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@airmark/quiver",
3
- "version": "0.26.0",
3
+ "version": "0.28.0",
4
4
  "description": "Airmark Quiver — Air Force and DAF document Quills for Quillmark.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -16,12 +16,14 @@
16
16
  "node": ">=24"
17
17
  },
18
18
  "devDependencies": {
19
- "@quillmark/quiver": "0.15.0",
20
- "@quillmark/wasm": "^0.90.0"
19
+ "@quillmark/quiver": "0.21.0",
20
+ "@quillmark/wasm": "^0.103.0",
21
+ "quillkit": "0.2.0"
21
22
  },
22
23
  "scripts": {
23
- "test": "quiver test",
24
- "preview": "quiver preview"
24
+ "test": "quillkit test",
25
+ "site": "quillkit site --out site",
26
+ "dev": "quillkit studio"
25
27
  },
26
28
  "keywords": [
27
29
  "quillmark",
@@ -2,9 +2,11 @@ quill:
2
2
  name: af4141
3
3
  version: 0.1.0
4
4
  backend: typst
5
- plate_file: plate.typ
6
5
  description: "AF Form 4141 — Individual's Record of Duties and Experience, Ground Environment Personnel"
7
6
 
7
+ typst:
8
+ plate_file: plate.typ
9
+
8
10
  main:
9
11
  body:
10
12
  enabled: false
@@ -36,58 +38,66 @@ card_kinds:
36
38
  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.
37
39
  ui:
38
40
  title: Record of Experience
41
+ groups: [record]
39
42
  body:
40
43
  enabled: false
41
44
  fields:
42
45
  date:
43
- type: datetime
46
+ type: date
44
47
  default: ""
48
+ example: "2026-03-14"
45
49
  ui:
46
- group: Record
50
+ group: record
47
51
  description: Date of the action (column A).
48
52
 
49
53
  action:
50
54
  type: string
51
55
  default: ""
56
+ example: Upgraded to 5-level; assumed ADO duties
52
57
  ui:
53
- group: Record
58
+ group: record
54
59
  description: One type of action per line (column B).
55
60
 
56
61
  written_grade:
57
62
  type: string
58
63
  default: ""
64
+ example: "3"
59
65
  ui:
60
- group: Record
66
+ group: record
61
67
  compact: true
62
68
  description: Written grade, if applicable (column C).
63
69
 
64
70
  written_grade_date:
65
- type: datetime
71
+ type: date
66
72
  default: ""
73
+ example: "2026-02-28"
67
74
  ui:
68
- group: Record
75
+ group: record
69
76
  compact: true
70
77
  description: Date of written grade (column D). Omit or leave blank when there is no written grade.
71
78
 
72
79
  positional_grade:
73
80
  type: string
74
81
  default: ""
82
+ example: "2"
75
83
  ui:
76
- group: Record
84
+ group: record
77
85
  compact: true
78
86
  description: Positional grade, if applicable (column E).
79
87
 
80
88
  positional_grade_date:
81
- type: datetime
89
+ type: date
82
90
  default: ""
91
+ example: "2026-03-14"
83
92
  ui:
84
- group: Record
93
+ group: record
85
94
  compact: true
86
95
  description: Date of positional grade (column F). Omit or leave blank when there is no positional grade.
87
96
 
88
97
  auth_or_remarks:
89
98
  type: string
90
99
  default: ""
100
+ example: "J.A.D."
91
101
  ui:
92
- group: Record
102
+ group: record
93
103
  description: Authentication or remarks entry (column G).
@@ -11,7 +11,15 @@
11
11
  /// True for short/narrow fields with brief content (grades, ranks, dates).
12
12
  #let should-shrink-to-fit(display, width, height) = {
13
13
  let aspect = width / height
14
- let char-count = display.len()
14
+ // A date field's content is a single `text` element, and the string inside it
15
+ // is what decides the shrink, as a plain value's own does. Any other content
16
+ // (a styled label, a sequence) has no such string and counts as long, so only
17
+ // the dimension conditions apply.
18
+ let char-count = if type(display) == str {
19
+ display.len()
20
+ } else {
21
+ display.at("text", default: "0" * 99).len()
22
+ }
15
23
  char-count <= 10 or height < 20pt or aspect > 4.0
16
24
  }
17
25
 
@@ -81,8 +89,14 @@
81
89
  /// - field (dictionary): raw field entry from the schema
82
90
  #let render-field(field-type, value, width, height, field) = {
83
91
  if field-type == "text" {
84
- if value != none and str(value) != "" {
85
- render-text-field(str(value), width, height, 1.5pt, 1pt)
92
+ if value != none {
93
+ // A date field arrives as content carrying its click-to-edit region, so it
94
+ // is placed as it stands; a plain string is placed only when non-empty.
95
+ if type(value) == content {
96
+ render-text-field(value, width, height, 1.5pt, 1pt)
97
+ } else if str(value) != "" {
98
+ render-text-field(str(value), width, height, 1.5pt, 1pt)
99
+ }
86
100
  }
87
101
  } else if field-type == "checkbox" {
88
102
  if value == true {
@@ -1,15 +1,22 @@
1
1
  #import "@local/quillmark-helper:0.1.0": data
2
2
  #import "@local/typst-af4141:0.1.0": form
3
3
 
4
- #set text(font: ("NimbusRomNo9L", "Times New Roman", "serif"))
4
+ #set text(font: "NimbusRomNo9L")
5
5
 
6
- // `type: date` fields arrive as Typst `datetime`; PDF overlay expects strings.
7
- // Missing / blank dates: helper uses `none`; empty strings may also appear from input.
6
+ // A `type: date` field lowers to a click-to-edit value object
7
+ // `(value: datetime, display: closure)`; `display` is a stored closure on a dict
8
+ // rather than a method, so it is called through parentheses, and the content it
9
+ // returns carries the region that makes the glyphs clickable. A native datetime
10
+ // is still accepted. Missing / blank dates arrive as `none`; empty strings may
11
+ // also appear from input.
8
12
  #let form-cell(v) = {
13
+ let pattern = "[month padding:none]/[day padding:none]/[year]"
9
14
  if v == none { "" }
10
15
  else if type(v) == str and v == "" { "" }
11
- else if type(v) == datetime {
12
- v.display("[month padding:none]/[day padding:none]/[year]")
16
+ else if type(v) == dictionary {
17
+ (v.display)(pattern)
18
+ } else if type(v) == datetime {
19
+ v.display(pattern)
13
20
  } else {
14
21
  str(v)
15
22
  }
@@ -2,9 +2,11 @@ quill:
2
2
  name: daf1206
3
3
  version: 0.1.0
4
4
  backend: typst
5
- plate_file: plate.typ
6
5
  description: "DAF Form 1206 — Nomination for Award"
7
6
 
7
+ typst:
8
+ plate_file: plate.typ
9
+
8
10
  main:
9
11
  body:
10
12
  example: |
@@ -2,9 +2,11 @@ quill:
2
2
  name: daf4392
3
3
  version: 0.1.0
4
4
  backend: typst
5
- plate_file: plate.typ
6
5
  description: "DAF Form 4392 - Pre-Departure Safety Briefing (Page 2)"
7
6
 
7
+ typst:
8
+ plate_file: plate.typ
9
+
8
10
  main:
9
11
  body:
10
12
  enabled: false
@@ -16,7 +18,7 @@ main:
16
18
  description: "Matches the check box."
17
19
 
18
20
  departure_date:
19
- type: datetime
21
+ type: date
20
22
  default: ""
21
23
  example: "2026-06-01"
22
24
  ui:
@@ -59,7 +61,7 @@ main:
59
61
  description: "Unit / Organization."
60
62
 
61
63
  briefed_date:
62
- type: datetime
64
+ type: date
63
65
  default: ""
64
66
  example: "2026-05-22"
65
67
  ui:
@@ -96,41 +98,42 @@ card_kinds:
96
98
  description: "Each card represents one row in the travel itinerary table. Up to 10 rows are supported."
97
99
  ui:
98
100
  title: Proposed Travel Itinerary
101
+ groups: [itinerary_entry]
99
102
  body:
100
103
  enabled: false
101
104
  fields:
102
105
  date:
103
- type: datetime
106
+ type: date
104
107
  default: ""
105
108
  ui:
106
- group: Itinerary Entry
109
+ group: itinerary_entry
107
110
  compact: true
108
111
  description: "Date for the itinerary row."
109
112
  departure_point:
110
113
  type: string
111
114
  default: ""
112
115
  ui:
113
- group: Itinerary Entry
116
+ group: itinerary_entry
114
117
  compact: true
115
118
  description: "Point of departure."
116
119
  arrival_point:
117
120
  type: string
118
121
  default: ""
119
122
  ui:
120
- group: Itinerary Entry
123
+ group: itinerary_entry
121
124
  compact: true
122
125
  description: "Point of arrival."
123
126
  rest_length:
124
127
  type: string
125
128
  default: ""
126
129
  ui:
127
- group: Itinerary Entry
130
+ group: itinerary_entry
128
131
  compact: true
129
132
  description: "Duration of stay at arrival point"
130
133
  mileage:
131
134
  type: string
132
135
  default: ""
133
136
  ui:
134
- group: Itinerary Entry
137
+ group: itinerary_entry
135
138
  compact: true
136
139
  description: "Approximate mileage for this leg (leave blank if not applicable, e.g., flights)."