@heroiclands/package-build 23.0.0 → 23.1.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 (153) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +1 -1
  3. package/bin/content-commands.mjs +1 -1
  4. package/ci/content-format-reference.mjs +46 -0
  5. package/docs/api.md +124 -141
  6. package/docs/authoring/assets.md +1 -1
  7. package/docs/authoring/dates-and-calendars.md +12 -6
  8. package/docs/authoring/frontmatter.md +2 -2
  9. package/docs/authoring/links-and-markup.md +145 -29
  10. package/docs/commands.md +8 -10
  11. package/docs/configuration.md +13 -11
  12. package/docs/content-format.md +2 -2
  13. package/docs/guides/book.md +59 -4
  14. package/docs/guides/icons.md +4 -5
  15. package/docs/guides/packs.md +6 -0
  16. package/docs/guides/reference-pages.md +21 -1
  17. package/docs/guides/release-and-deploy.md +17 -1
  18. package/docs/guides/site.md +17 -18
  19. package/docs/reference/format-details.md +877 -276
  20. package/docs/reference/note-types.md +278 -91
  21. package/docs/reference/theme-pages.md +58 -97
  22. package/engine/address-values.mjs +124 -4
  23. package/engine/address.mjs +44 -3
  24. package/engine/anchors.mjs +103 -3
  25. package/engine/art-slots.mjs +2 -9
  26. package/engine/base-compiler.mjs +25 -6
  27. package/engine/calendar-artifacts.mjs +4 -1
  28. package/engine/calendar-notes.mjs +166 -102
  29. package/engine/content-format-check.mjs +2 -3
  30. package/engine/content-format.mjs +44 -1
  31. package/engine/content-format.yaml +79 -16
  32. package/engine/content-images.mjs +10 -1
  33. package/engine/content-index.mjs +37 -9
  34. package/engine/content-links.mjs +24 -5
  35. package/engine/content-tables.mjs +24 -3
  36. package/engine/data-keys.mjs +347 -0
  37. package/engine/derived-sections.mjs +322 -0
  38. package/engine/diagnostics.mjs +28 -0
  39. package/engine/event-fields.mjs +187 -0
  40. package/engine/event-rows.mjs +126 -0
  41. package/engine/event-views.mjs +305 -0
  42. package/engine/exported-scene.mjs +29 -53
  43. package/engine/frontmatter-lint.mjs +103 -77
  44. package/engine/frontmatter.mjs +7 -2
  45. package/engine/generate.mjs +53 -0
  46. package/engine/generated-sections.mjs +223 -0
  47. package/engine/heading-attributes.mjs +4 -2
  48. package/engine/helpers.mjs +12 -1
  49. package/engine/holdings.mjs +12 -33
  50. package/engine/ids.mjs +17 -11
  51. package/engine/index.mjs +4 -4
  52. package/engine/infobox-render.mjs +21 -6
  53. package/engine/infobox.mjs +217 -99
  54. package/engine/journals.mjs +104 -47
  55. package/engine/literature-notes.mjs +4 -1
  56. package/engine/literature-works.mjs +21 -13
  57. package/engine/map-graphviz.mjs +4 -18
  58. package/engine/markdown-expressions.mjs +116 -11
  59. package/engine/metadata-index.mjs +26 -0
  60. package/engine/near-miss.mjs +90 -0
  61. package/engine/note-addresses.mjs +75 -19
  62. package/engine/note-dates.mjs +5 -3
  63. package/engine/note-event-terms.mjs +364 -0
  64. package/engine/note-events.mjs +823 -0
  65. package/engine/note-vocabulary.mjs +184 -88
  66. package/engine/office-holders.mjs +20 -54
  67. package/engine/packagebuild-index.mjs +16 -1
  68. package/engine/pdf-build.mjs +97 -60
  69. package/engine/pdf-index.mjs +263 -0
  70. package/engine/pdf-render.mjs +47 -5
  71. package/engine/place-relation-terms.mjs +174 -0
  72. package/engine/place-relations.mjs +72 -160
  73. package/engine/rank-ladder.mjs +14 -41
  74. package/engine/reckoning-markers.mjs +2 -0
  75. package/engine/region-events.mjs +6 -12
  76. package/engine/scenes.mjs +73 -468
  77. package/engine/site-build.mjs +75 -101
  78. package/engine/site-index.mjs +3 -1
  79. package/engine/site-maps.mjs +43 -29
  80. package/engine/site-preview.mjs +40 -31
  81. package/engine/social-ties.mjs +2 -1
  82. package/engine/sql-tables.mjs +212 -9
  83. package/engine/standing-terms.mjs +145 -6
  84. package/engine/standings.mjs +37 -49
  85. package/engine/web-wikilinks.mjs +21 -1
  86. package/engine/wikilink-syntax.mjs +34 -4
  87. package/engine/wikilinks.mjs +27 -2
  88. package/hugo-theme/layouts/partials/infobox/entry.html +4 -1
  89. package/hugo-theme/layouts/partials/page-body.html +0 -3
  90. package/hugo-theme/static/css/style.css +25 -55
  91. package/package.json +1 -2
  92. package/packagebuild-metadata.jsonl +13 -13
  93. package/sohl/note-schemas.mjs +8 -73
  94. package/types/engine/address-values.d.mts +50 -3
  95. package/types/engine/address.d.mts +21 -0
  96. package/types/engine/anchors.d.mts +49 -2
  97. package/types/engine/art-slots.d.mts +1 -1
  98. package/types/engine/base-compiler.d.mts +13 -4
  99. package/types/engine/calendar-notes.d.mts +7 -0
  100. package/types/engine/content-format.d.mts +52 -0
  101. package/types/engine/content-images.d.mts +5 -1
  102. package/types/engine/content-tables.d.mts +9 -1
  103. package/types/engine/data-keys.d.mts +145 -0
  104. package/types/engine/derived-sections.d.mts +73 -0
  105. package/types/engine/diagnostics.d.mts +15 -0
  106. package/types/engine/event-fields.d.mts +99 -0
  107. package/types/engine/event-rows.d.mts +12 -0
  108. package/types/engine/event-views.d.mts +67 -0
  109. package/types/engine/exported-scene.d.mts +5 -2
  110. package/types/engine/frontmatter-lint.d.mts +3 -3
  111. package/types/engine/generated-sections.d.mts +143 -0
  112. package/types/engine/holdings.d.mts +6 -20
  113. package/types/engine/ids.d.mts +15 -12
  114. package/types/engine/index.d.mts +1 -1
  115. package/types/engine/infobox.d.mts +13 -3
  116. package/types/engine/journals.d.mts +24 -11
  117. package/types/engine/literature-works.d.mts +1 -2
  118. package/types/engine/map-graphviz.d.mts +2 -10
  119. package/types/engine/markdown-expressions.d.mts +12 -3
  120. package/types/engine/near-miss.d.mts +42 -0
  121. package/types/engine/note-addresses.d.mts +7 -1
  122. package/types/engine/note-event-terms.d.mts +68 -0
  123. package/types/engine/note-events.d.mts +71 -0
  124. package/types/engine/note-vocabulary.d.mts +37 -8
  125. package/types/engine/office-holders.d.mts +3 -0
  126. package/types/engine/pdf-index.d.mts +35 -0
  127. package/types/engine/pdf-render.d.mts +8 -2
  128. package/types/engine/place-relation-terms.d.mts +83 -0
  129. package/types/engine/place-relations.d.mts +11 -66
  130. package/types/engine/rank-ladder.d.mts +2 -0
  131. package/types/engine/region-events.d.mts +6 -12
  132. package/types/engine/scenes.d.mts +6 -32
  133. package/types/engine/site-build.d.mts +8 -22
  134. package/types/engine/site-maps.d.mts +15 -7
  135. package/types/engine/sql-tables.d.mts +77 -2
  136. package/types/engine/standing-terms.d.mts +37 -4
  137. package/types/engine/standings.d.mts +14 -9
  138. package/types/engine/wikilink-syntax.d.mts +20 -0
  139. package/types/engine/wikilinks.d.mts +3 -2
  140. package/docs/guides/government-migration.md +0 -52
  141. package/engine/itinerary-scenes.mjs +0 -166
  142. package/engine/lore-events.mjs +0 -197
  143. package/engine/map-notes.mjs +0 -1309
  144. package/engine/map-raster.mjs +0 -51
  145. package/hugo-theme/layouts/partials/holdings/entry.html +0 -21
  146. package/hugo-theme/layouts/partials/holdings/kind-groups.html +0 -71
  147. package/hugo-theme/layouts/partials/holdings.html +0 -61
  148. package/hugo-theme/layouts/partials/literature-works.html +0 -29
  149. package/hugo-theme/layouts/partials/place-map.html +0 -9
  150. package/types/engine/itinerary-scenes.d.mts +0 -29
  151. package/types/engine/lore-events.d.mts +0 -14
  152. package/types/engine/map-notes.d.mts +0 -297
  153. package/types/engine/map-raster.d.mts +0 -8
@@ -37,7 +37,7 @@ The harbor faces west.
37
37
  Travelers present their papers here. See [[#harbor|the harbor]].
38
38
  ```
39
39
 
40
- A leading caption starts a captioned-item JournalEntryPage in Foundry. A solitary image with a plain caption can be an image page; other captioned items become text pages. Websites and books keep captioned items in the surrounding flow.
40
+ A leading caption gives its item a JournalEntryPage of its own in Foundry, and the body after the item resumes on a continuation of the surrounding section. A solitary image with a plain caption can be an image page; other captioned items become text pages. Websites and books keep captioned items in the surrounding flow.
41
41
 
42
42
  The prose introducing a fence and the fence itself land on two different Foundry pages, so a reference such as `{{ref "#trade"}}` written just above a fence sends a Foundry reader to a different page than the one carrying the sentence. Give the introducing prose its own heading, or accept that following the reference leaves the page, before relying on a fence to sit inside a longer page's flow.
43
43
 
@@ -59,7 +59,7 @@ The section runs from that heading to the next heading that opens a page. In Fou
59
59
 
60
60
  **On the website this is a spoiler, not access control.** The withheld section is in the published page, so anyone who opens the element or reads the page source reads it. Foundry is the only surface that withholds anything. Write nothing in a `.secret` section that would matter if a player read it on the web.
61
61
 
62
- A `:::secret` block inside the section is legal and renders as it does anywhere else. `.secret` on a heading that opens no page — a lower-level heading with no anchor — covers no page, and is reported at its own line: give that heading an anchor, or raise it to the top level.
62
+ A captioned item inside the section is withheld with it: in Foundry the item's own page, and the page that resumes the section after it, are the GM's alone. A `:::secret` block inside the section is legal and renders as it does anywhere else. `.secret` on a heading that opens no page — a lower-level heading with no anchor — covers no page, and is reported at its own line: give that heading an anchor, or raise it to the top level.
63
63
 
64
64
  The special Actor anchors `{#appearance}` and `{#dossier}` feed the same authored sections to each system's Actor fields, and take an H1 only — not the lower-level heading a journal page may otherwise start from. The section runs to the next H1, nested headings included. An anchor declared on a lower heading is still found, and is a build error rather than a quietly empty field: move the heading to the top level, or drop the anchor if the section is meant to stay unaddressed prose.
65
65
 
@@ -98,9 +98,9 @@ Each numbered type has its own counter. Websites and Foundry number within a not
98
98
 
99
99
  `{{ref "#trade"}}` displays the numbered label as a link. `form="full"` includes caption text and `form="title"` displays the caption alone. Use title form for an unnumbered caption. Cross-note references can name the note address before the anchor, such as `{{ref "place-harbor#trade"}}`; the displayed number belongs to the target on the surface rendering it. Links inside caption text contribute their visible text to references, so the reference does not contain a nested link.
100
100
 
101
- Foundry gives a captioned item its own JournalEntryPage. A solitary image with a plain caption can use an image page; captions with inline markup and other items use text pages. On the website and in the book the item stays in the flow of the surrounding page.
101
+ Foundry gives a captioned item a JournalEntryPage holding the item alone, named for its number or, unnumbered, for the caption's visible text — links and emphasis reduced to the words a reader sees. A solitary image with a plain caption can use an image page; captions with inline markup and other items use text pages. The body after the item resumes on a continuation page carrying the interrupted section's name, level and classes, with its title hidden; nothing is emitted when the next page begins straight after the item. On the website and in the book the item stays in the flow of the surrounding page.
102
102
 
103
- **Migration:** `:::figure` is no longer supported. Move the former `///` caption before the item as `:@ Caption {#id}` and remove the figure wrapper. To caption a group or passage, put it inside a generic fenced div and caption that div with an explicit type if needed. `///` is no longer a caption delimiter.
103
+ To caption a group or passage, put it inside a generic fenced div and caption that div, with an explicit type where the inferred one does not fit.
104
104
 
105
105
  ## Images and protected content
106
106
 
@@ -168,8 +168,6 @@ Attributes follow the marker and apply to the alert:
168
168
 
169
169
  Use `#id`, `.class` and named attributes in the shared braced grammar. The marker determines the default label, icon and color. An authored `title` replaces the default **Note**, **Tip**, **Important** or **Warning** heading while preserving the alert type’s icon and color. Event-handler attributes, malformed attributes, unknown alert types and empty alert bodies are errors. Ordinary blockquotes and alert examples inside literal code remain unchanged. A leading caption can describe an alert as a quote block, normally with the `prose` type or an explicit `example` type.
170
170
 
171
- Migrate former info blocks to `[!NOTE]` alerts and warn blocks to `[!WARNING]` alerts. The old named fences are errors. `:::secret` remains supported.
172
-
173
171
  ### Secret blocks
174
172
 
175
173
  `:::secret` sets GM material apart. Its optional attributes accept an identifier, classes and named attributes. `title` supplies its heading; without a title the heading is **Secret**:
@@ -244,7 +242,7 @@ The lantern marks the shore.
244
242
  ```
245
243
  ````
246
244
 
247
- This uses the **Poem** counter, so `{{ref "#watch-song"}}` reads **Poem 1**. Use `:` for a caption without a number. `:::poetry` is no longer supported.
245
+ This uses the **Poem** counter, so `{{ref "#watch-song"}}` reads **Poem 1**. Use `:` for a caption without a number.
248
246
 
249
247
  ## Footnotes and definition lists
250
248
 
@@ -311,14 +309,108 @@ A column's heading is its SQL alias, so `AS "Name"` is what a reader sees. A cel
311
309
 
312
310
  ### What a query can read
313
311
 
314
- | Relation | What it holds |
315
- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------- |
316
- | `notes` | Every note except a stub. This is the one to use unless a stub is wanted. |
317
- | `entries` | Every note, stubs included. |
318
- | `market` | The market scale as three columns — `value`, `name`, `trade` — so a table can print `village` beside the number a note wrote. |
319
- | `<package>.notes` | The same two relations for each package this one declares a dependency on, in a schema named for it. |
312
+ | Relation | What it holds |
313
+ | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
314
+ | `notes` | Every note except a stub. This is the one to use unless a stub is wanted. |
315
+ | `entries` | Every note, stubs included. |
316
+ | `events` | One row per event a `lore`, `place` or `affiliation` note writes in `data.events`, with `whenSort` to order them by date. |
317
+ | `market` | The market scale as three columns — `value`, `name`, `trade` — so a table can print `village` beside the number a note wrote. |
318
+ | `<package>.notes` | The same relations — `notes`, `entries` and `events` — for each package this one declares a dependency on, in a schema named for it. |
319
+
320
+ Nested frontmatter is addressed exactly as a note writes it: `name.full`, `sohl.weight`, `data.born`. Every Address is a string — the full canonical Address, as in `list_contains(data.parents, 'thalorna-note-place-xerathia')` — whichever package published it. Every note carries a computed `state` column of `full`, `draft` or `stub`. Notes tagged `gm` are absent from the relations on public surfaces.
321
+
322
+ ### Sections the build adds
323
+
324
+ The build adds sections to the end of a note, after everything you wrote, listing what other notes say about it. Each appears only where it has something to list, under a fixed anchor:
325
+
326
+ | Section | Anchor | Lists |
327
+ | ----------------- | ---------------- | -------------------------------------------------------------------- |
328
+ | Within | `within` | on a place, the places whose `parents` name it, grouped by kind |
329
+ | Governed by | `governedby` | on a place, the affiliation its `government` names |
330
+ | Governed places | `governedplaces` | on an affiliation, the places naming it as their `government` |
331
+ | In song and story | `insongandstory` | the works of literature whose `subjects` name the note or its events |
332
+ | From here | `fromhere` | on a place with borders or routes, the map drawn from it |
333
+
334
+ The event sections below sit among them, and the [format reference](../reference/format-details.md#generated-sections) gives the order. Each entry links to the note it names; a note with no page of its own is named in plain text. Replace a section the way you replace an event view, by writing your own with the same anchor:
335
+
336
+ ```markdown
337
+ # Within {#within}
338
+
339
+ The vale holds three villages, of which only [[place-ford|Ford]] is walled.
340
+ ```
341
+
342
+ ### Sections the build adds about events
343
+
344
+ A note's events are listed for you on the notes they concern: a place gets a **Chronology** of what happened there and below it, with every `depth: world` event as context, a being, affiliation or people the **Events** it took part in, an affiliation, place or people the **Accounts** it gives, and a note holding events **What followed** them. Each is a section appended after everything you wrote, with a fixed anchor — `chronology`, `events`, `accounts`, `followed` — and it appears only where it has something to list.
345
+
346
+ To replace one, write the section yourself with the same anchor. The build then adds nothing for it and leaves yours exactly as written:
347
+
348
+ ````markdown
349
+ # Chronology {#chronology}
350
+
351
+ The fells keep no reckoning before the raising.
352
+
353
+ ```sql
354
+ SELECT summary AS "What happened", "when" AS "When"
355
+ FROM events
356
+ WHERE list_contains("where".locus, 'thalorna-note-place-ironfells')
357
+ ORDER BY whenSort
358
+ ```
359
+ ````
360
+
361
+ The [format reference](../reference/format-details.md#event-views) lists what each view selects.
362
+
363
+ ### A century view
364
+
365
+ A view of what happened in one century is a fence an author writes, on whatever note should carry it. Filter `events` on `whenYear`, leave out events of local reach, and order by `whenSort`:
366
+
367
+ ````markdown
368
+ # The Third Century {#third-century}
369
+
370
+ ```sql
371
+ SELECT note AS _ref,
372
+ summary AS "What happened",
373
+ "when" AS "When",
374
+ array_to_string("where".locus, ', ') AS "Where"
375
+ FROM events
376
+ WHERE whenYear BETWEEN 200 AND 299
377
+ AND depth <> 'local'
378
+ ORDER BY whenSort, address
379
+ ```
380
+ ````
381
+
382
+ Each row is one event, linked to the note that records it, with its date as the note wrote it and the Addresses of the places it happened at:
383
+
384
+ | What happened | When | Where |
385
+ | -------------------------------------------------- | ---- | -------------------------------------------------- |
386
+ | [Ironfells is sacked.](/thalorna/place-ironfells/) | ~280 | thalorna-note-place-ironfells |
387
+ | [The fords are bridged.](/thalorna/lore-fords/) | ~291 | thalorna-note-place-east, thalorna-note-place-west |
388
+
389
+ `depth <> 'local'` also leaves out an event that states no `depth`; write `depth IS DISTINCT FROM 'local'` to keep those. `whenYear` is the year on the canonical axis, which counts a year `0` that written dates skip: a year written after zero keeps its number, and one written before zero sits one higher. So `whenYear BETWEEN -299 AND -200` selects the events written `-300` to `-201`.
390
+
391
+ To print each place by its name rather than its Address, join `notes` on the place's Address. The `GROUP BY` keeps one row per event when it happened in several places:
392
+
393
+ ````markdown
394
+ ```sql
395
+ SELECT e.note AS _ref,
396
+ e.summary AS "What happened",
397
+ e."when" AS "When",
398
+ string_agg(p.name.full, ', ') AS "Where"
399
+ FROM events e
400
+ LEFT JOIN notes p ON list_contains(e."where".locus, p.address.canonical)
401
+ WHERE e.whenYear BETWEEN 200 AND 299
402
+ AND e.depth <> 'local'
403
+ GROUP BY e.note, e.address, e.summary, e."when", e.whenSort
404
+ ORDER BY e.whenSort, e.address
405
+ ```
406
+ ````
407
+
408
+ | What happened | When | Where |
409
+ | -------------------------------------------------- | ---- | ---------- |
410
+ | [Ironfells is sacked.](/thalorna/place-ironfells/) | ~280 | Ironfells |
411
+ | [The fords are bridged.](/thalorna/lore-fords/) | ~291 | East, West |
320
412
 
321
- Nested frontmatter is addressed exactly as a note writes it: `name.full`, `sohl.weight`, `data.born`. Every note carries a computed `state` column of `full`, `draft` or `stub`. Notes tagged `gm` are absent from the relations on public surfaces.
413
+ An event with no `where.locus` keeps its row with an empty **Where** cell.
322
414
 
323
415
  ### Fence attributes
324
416
 
@@ -418,24 +510,48 @@ There are {{words (sql "SELECT COUNT(*) FROM notes WHERE type = 'being'")}} bein
418
510
 
419
511
  <!-- expression-helpers:start -->
420
512
 
421
- | Helper | Parameters | What it gives you |
422
- | ------------ | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
423
- | `eq` | `a b` | True when both are the same value. Compares exactly, so 1 and "1" differ. |
424
- | `gt` | `a b` | True when a is greater than b. Both must be finite numbers. |
425
- | `gte` | `a b` | True when a is greater than or equal to b. Both must be finite numbers. |
426
- | `lt` | `a b` | True when a is less than b. Both must be finite numbers. |
427
- | `lte` | `a b` | True when a is less than or equal to b. Both must be finite numbers. |
428
- | `not` | `value` | True when the value is false, zero, empty or absent. |
429
- | `and` | `value …` | True when every value given is true. Takes two or more. |
430
- | `or` | `value …` | True when any value given is true. Takes two or more. |
431
- | `words` | `number` | The number spelled out for running prose — `1200` becomes one thousand two hundred. Whole numbers only; a decimal is a finding, and `digits` takes one. |
432
- | `digits` | `number` | The number as a grouped numeral — `1200` becomes 1,200, and a fractional part is kept. |
433
- | `sql` | `"query"` | The single value a query returns, for use inside a sentence. The query is a quoted string, the same SQL a table fence takes. |
434
- | `dateformat` | `calendar date ["format"]` | A date written in a calendar. The calendar is an Address or its shortcode, the date is the value a note carries, and the optional third argument names an output pattern. |
435
- | `ref` | `address [form="number"\|"full"\|"title"]` | A link to a captioned item, by its anchor — `"#thorn"` on this note, `"note-address#thorn"` on another. `form` is `number` (the default, and admitted explicitly), rendering "Figure 13"; `full`, the number and the caption; or `title`, the caption alone. Always renders as a link to the figure, and a link inside the caption contributes only its label text. The number rendered is the one the figure carries on the surface doing the rendering, whichever note names it. |
513
+ | Helper | Parameters | What it gives you |
514
+ | ------------ | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
515
+ | `eq` | `a b` | True when both are the same value. Compares exactly, so 1 and "1" differ. |
516
+ | `gt` | `a b` | True when a is greater than b. Both must be finite numbers. |
517
+ | `gte` | `a b` | True when a is greater than or equal to b. Both must be finite numbers. |
518
+ | `lt` | `a b` | True when a is less than b. Both must be finite numbers. |
519
+ | `lte` | `a b` | True when a is less than or equal to b. Both must be finite numbers. |
520
+ | `not` | `value` | True when the value is false, zero, empty or absent. |
521
+ | `and` | `value …` | True when every value given is true. Takes two or more. |
522
+ | `or` | `value …` | True when any value given is true. Takes two or more. |
523
+ | `words` | `number` | The number spelled out for running prose — `1200` becomes one thousand two hundred. Whole numbers only; a decimal is a finding, and `digits` takes one. |
524
+ | `digits` | `number` | The number as a grouped numeral — `1200` becomes 1,200, and a fractional part is kept. |
525
+ | `sql` | `"query"` | The single value a query returns, for use inside a sentence. The query is a quoted string, the same SQL a table fence takes. |
526
+ | `dateformat` | `calendar date ["format"]` | A date written in a calendar. The calendar is an Address or its shortcode, the date is the value a note carries, and the optional third argument names an output pattern. |
527
+ | `ref` | `address [form="number"\|"full"\|"title"] \| address field="when"\|"until"\|"kind"\|"summary"\|"name"` | A link to a captioned item, by its anchor — `"#thorn"` on this note, `"note-address#thorn"` on another. `form` is `number` (the default, and admitted explicitly), rendering "Figure 13"; `full`, the number and the caption; or `title`, the caption alone. Always renders as a link to the figure, and a link inside the caption contributes only its label text. The number rendered is the one the figure carries on the surface doing the rendering, whichever note names it. With `field` in place of `form`, the address names an event — `"place-ironfells#sack"`, `"#sack"` on this note, or `"lore-founding"` for a note holding one event — and the reference prints that event's `when`, `until`, `kind`, `summary` or `name` as plain text, never a link; a date prints as the note format prints any date. |
436
528
 
437
529
  <!-- expression-helpers:end -->
438
530
 
439
531
  A helper that is given the wrong kind of value reports it rather than printing something wrong: `words` refuses a decimal, the four comparisons refuse anything that is not a finite number, and `dateformat` refuses a date it cannot resolve in the calendar it was given.
440
532
 
441
533
  See [date rules](dates-and-calendars.md) for what `dateformat` accepts and the patterns its third argument names.
534
+
535
+ ### An event's date, inline
536
+
537
+ `ref` with `field` in place of `form` prints one field of an [event](../reference/format-details.md#events) as plain text, so a sentence states a date from the one place the event records it rather than typing it a second time:
538
+
539
+ ```markdown
540
+ Ironfells fell in {{ref "place-ironfells#sack" field="when"}}, in what the fells call {{ref "place-ironfells#sack" field="name"}}.
541
+ The chronicle records the founding in one line: {{ref "lore-founding" field="summary"}}
542
+ Our own sack was a {{ref "#sack" field="kind"}}.
543
+ ```
544
+
545
+ The address names one event: `"<note>#<id>"` for an event of another note, `"#<id>"` for one of this note, or a note's Address alone when that note holds exactly one event. An event a declared dependency publishes is named by its full Address, as any cross-package link is.
546
+
547
+ | `field` | What prints |
548
+ | --------- | ------------------------------------------------------------------ |
549
+ | `when` | The event's date, as the note format prints any date — `~` is kept |
550
+ | `until` | The date it ended, printed the same way |
551
+ | `kind` | Its `kind`, such as `siege` |
552
+ | `summary` | Its `summary`; a link written inside it prints as its label |
553
+ | `name` | The first name its `names` list gives |
554
+
555
+ The value is text and never a link, on the website, in the Foundry journal and in the book alike. `field` is a quoted string, like `form`, and a reference takes one or the other.
556
+
557
+ Each of these is an error at the reference's own line and column, and the reference's text stays in the page: an address naming no note, an anchor the note does not declare, an anchor that is a heading or another `prose` anchor rather than an event, a note holding several events or none named without an `#id`, a `field` outside the five above, and a field the event does not state — `field="until"` on an event with no `until`, or `field="name"` on one with no `names`.
package/docs/commands.md CHANGED
@@ -2451,16 +2451,14 @@ not change its viewpoint.
2451
2451
 
2452
2452
  Site builds put a linked SVG in each eligible place page. PDF builds put a
2453
2453
  vector map on its own page immediately after each selected place entry with
2454
- relations. A configured Scene pack stages PNG backgrounds and emits gridless
2455
- itinerary Scenes and an Adventure containing the journals their pins open.
2456
- The installed SVG renderer also stages PNG backgrounds for authored regional SVG maps.
2457
-
2458
- Battle and local map notes can carry a Foundry Scene export under `data.scene`.
2459
- The pack compiler preserves its fields and binds pins marked `#anchor` to pages
2460
- from the map note's Markdown. A `totm` map uses an image Address and canvas
2461
- dimensions to make a gridless Scene.
2462
- `data.fixup` resolves asset Addresses into selected fields of an exported Scene;
2463
- a missing path or asset is a build error.
2454
+ relations. The drawings are pictures for a reader; no Foundry Scene is made
2455
+ from them.
2456
+
2457
+ A map note is a different thing: it carries a Scene exported from Foundry under
2458
+ `data.scene`, on every map subtype. The pack compiler passes it through, binds
2459
+ pins marked `#anchor` to pages from the map note's Markdown, and applies
2460
+ `data.fixup`, which resolves asset Addresses into selected fields of the
2461
+ exported Scene; a missing path or asset is a build error.
2464
2462
 
2465
2463
  The four drawings, any of which may be asked for in one run:
2466
2464
 
@@ -1002,15 +1002,16 @@ site:
1002
1002
 
1003
1003
  > ``package-build config: `site.search` must be a boolean.``
1004
1004
 
1005
- `site.maps` is whether every place page carries the map from that place. On
1006
- by default: `package-build site` draws the map `package-build map --from`
1007
- draws — the place's borders and routes, each neighbour at its bearing — for
1008
- every place that states a border or a route or is named in one, lays it beside
1009
- the page as `from-<shortcode>.svg`, and names the file in the page's front
1010
- matter as `map`, which the theme inlines so every place name on it is a link
1011
- to that place's page. A place with no relation carries no map and no key.
1012
- The Graphviz runtime installed with package-build draws the maps. `false`
1013
- draws nothing.
1005
+ `site.maps` is whether a place carries the map from it. On by default: every
1006
+ place that states a border or a route, or is named in one, is given a
1007
+ **From here** [generated section](reference/format-details.md#generated-sections)
1008
+ holding the map `package-build map --from` draws — the place's borders and
1009
+ routes, each neighbour at its bearing. The section reaches every surface: the
1010
+ website sets the drawing inline so every place name on it is a link to that
1011
+ place's page, the Foundry compile stages it into the module, and the book
1012
+ prints it in the place's entry. A place with no relation carries no map. The
1013
+ Graphviz runtime installed with package-build draws the maps. `false` gives no
1014
+ place the section, on any surface, and draws nothing.
1014
1015
 
1015
1016
  ```yaml
1016
1017
  site:
@@ -1084,8 +1085,9 @@ determined by the authored content tree: a tree containing only a homepage has
1084
1085
  no content pages for a book.
1085
1086
 
1086
1087
  Each selected place with a border or route receives a dedicated full-page
1087
- vector itinerary after its entry. A selected authored regional SVG map also
1088
- prints on its own page. The npm-installed Graphviz runtime draws itineraries.
1088
+ vector itinerary after its entry. A selected map note prints each of its
1089
+ exported Scene's level backgrounds on its own page. The npm-installed Graphviz
1090
+ runtime draws itineraries.
1089
1091
 
1090
1092
  ```yaml
1091
1093
  pdf:
@@ -29,7 +29,7 @@ A nonempty note body creates one system-agnostic Foundry JournalEntry. Content b
29
29
 
30
30
  Leading `:` captions describe the next supported block; `:@` captions also number it. The optional type accepts `figure`, `table`, `poetry`, `code`, `prose`, and `example`. Captions support identifiers, classes and named attributes; unsupported following blocks are errors. See [captions and numbered references](authoring/links-and-markup.md#captions-and-numbered-references).
31
31
 
32
- A code fence with language `poetry` keeps verse lines, stanza breaks and relative indentation. Its numbered caption uses the **Poem** label. Generic `:::` divs and `[inline spans]{.class}` accept attributes. The former `:::figure` and `:::poetry` forms are removed. See [poetry](authoring/links-and-markup.md#poetry), [fenced divs](authoring/links-and-markup.md#fenced-divs) and [inline spans](authoring/links-and-markup.md#inline-spans).
32
+ A code fence with language `poetry` keeps verse lines, stanza breaks and relative indentation. Its numbered caption uses the **Poem** label. Generic `:::` divs and `[inline spans]{.class}` accept attributes. See [poetry](authoring/links-and-markup.md#poetry), [fenced divs](authoring/links-and-markup.md#fenced-divs) and [inline spans](authoring/links-and-markup.md#inline-spans).
33
33
 
34
34
  Body extensions use braces with space-separated `key=value` attributes:
35
35
  `![[icon-harbor|Harbor]]{float=top-left size=medium}` and an SQL fence with
@@ -88,6 +88,6 @@ The note's readable Address is `<package>-note-lore-harbor`. In prose, link to i
88
88
 
89
89
  The [first-note guide](authoring/first-note.md) shows the build commands and how to add a system document. Use the [command reference](commands.md) for exact flags and the [diagnostics guide](diagnostics.md) for located errors.
90
90
 
91
- Place notes may declare a governing affiliation with `data.government`, or explicit `null` for complete anarchy. Positive population with an omitted government produces an advisory. See [place governments](authoring/frontmatter.md#place-governments) and [government migration](guides/government-migration.md).
91
+ Place notes may declare a governing affiliation with `data.government`, or explicit `null` for complete anarchy. Positive population with an omitted government produces an advisory. See [place governments](authoring/frontmatter.md#place-governments).
92
92
 
93
93
  Blockquote alerts use `[!NOTE]`, `[!TIP]`, `[!IMPORTANT]`, `[!WARNING]`, or `[!]`, with optional attributes after the marker. They replace the former info and warn div fences; secret blocks remain supported. See [alerts](authoring/links-and-markup.md#alerts).
@@ -92,10 +92,9 @@ columns; a wide table outside a figure fence starts on a new page so it stays
92
92
  after its introduction. A wide table inside one may float to the page top, and
93
93
  its label stays with it. A table too tall for one page takes pages of its own.
94
94
  A figure's number runs through the whole book in reading order, counted within
95
- its own kind. Maps attached
96
- to selected places use landscape pages after those entries. Regional, theatre
97
- of the mind, battle, and local map backgrounds also print on landscape pages.
98
- Battle and local maps print each Scene level's background on its own page.
95
+ its own kind. A place on a border or a route prints the map drawn from it in
96
+ its **From here** section, inside its own entry and nowhere else. A map note prints
97
+ each of its exported Scene's level backgrounds on its own landscape page.
99
98
  The
100
99
  [content format](../content-format.md) describes image sizes, floats, and
101
100
  links as they are authored in notes.
@@ -106,6 +105,49 @@ and map backgrounds fit the landscape map area. An image smaller than that
106
105
  target keeps its authored pixels and still fills its allotted space on the
107
106
  page. SVG art stays vector. The build never changes authored image files.
108
107
 
108
+ ## The index
109
+
110
+ A book that prints at least one note ends with an **Index** chapter, grouped
111
+ under letter headings. Nothing configures it: the build derives it from the
112
+ notes the book prints.
113
+
114
+ | Term | Source | Shown as |
115
+ | ------------------------ | --------------------- | ---------------------------------------------------------------------------------- |
116
+ | A note's name | `name.full` | the page of the note's entry in **bold**, then the pages of every authored mention |
117
+ | An alias | `name.aliases` | _see_ the note's name |
118
+ | An event's in-world name | `data.events[].names` | _see_ the name of the note that holds the event |
119
+
120
+ To get a term into the index, give the note a `name.full`, add an alias or an
121
+ event name, or link to the note from prose with a wikilink. A wikilink to a note
122
+ the book prints adds the page it appears on. A term mentioned several times on
123
+ one page lists that page once, and a note printed under more than one section
124
+ is listed once, with every page it occupies.
125
+
126
+ Two kinds of link add nothing:
127
+
128
+ - **Links in generated sections** such as "Within" or "Governed by". They name
129
+ notes the build chose rather than the author, and would add a page to nearly
130
+ every place and bury the authored mentions. They print the page of the note's
131
+ entry beside the name instead, as
132
+ described under [Build and inspect](#build-and-inspect).
133
+ - **Links to a note the book does not print.** There is no page to point to.
134
+
135
+ Terms sort on the ASCII fold the content index derives for names (`nameAscii`),
136
+ ignoring case, so an accented name files under its base letter and the order
137
+ matches the table of contents. A term that does not begin with a letter files
138
+ first, under `#`. An alias or event name that folds to the same text as the
139
+ note's own name is not listed again.
140
+
141
+ Two notes that print the same name are told apart. Each is qualified with the
142
+ note's subtype, or its type where it has none, as in `Ashkabel (settlement)` and
143
+ `Ashkabel (character)`. Where that still collides, the name of the place the
144
+ note is within is added: `Ashkabel (settlement, Harn)`. An alias or event name
145
+ that points at a qualified name prints the qualifier too.
146
+
147
+ Page numbers come from the typeset book, so they stay correct however the pages
148
+ reflow, and each is a link to its page in the PDF. The Index is reached from the
149
+ Contents and the bookmarks like any other chapter.
150
+
109
151
  ## Set fonts and icon fonts
110
152
 
111
153
  The build supplies its default book fonts and searches its own font directory
@@ -152,3 +194,16 @@ a note outside the selection or another package remains a web address.
152
194
  Anchors on headings are scoped to their entries, so two entries can use the
153
195
  same heading anchor. The build reports an unresolved destination while
154
196
  keeping the source available for inspection.
197
+
198
+ The [generated sections](../reference/format-details.md#generated-sections)
199
+ an entry carries — **Within**, **Governed by**, **In song and story**, the
200
+ event views and the rest — name notes the build chose rather than the author,
201
+ so their links follow their own rule, because a book is often a selection:
202
+
203
+ - A name whose note is in the book is an internal link followed by the page
204
+ that note's entry starts on, printed as the contents prints it:
205
+ `Ford (p. 12)`. The number is Typst's own, read after layout.
206
+ - A name whose note is not in the book is printed as plain text, with no link,
207
+ no page and no finding.
208
+
209
+ Links an author wrote keep the rules above and carry no page number.
@@ -39,15 +39,14 @@ data:
39
39
  Here `data.icon` resolves an `icon` asset and `data.tokenIcon` resolves an
40
40
  `image` asset. A full address, such as `sohl-none-image-smith-token`, names
41
41
  another package when that package is available to the build. `data.icon` and
42
- `data.tokenIcon` default to the `icon` type; `data.bgImage` and `data.banner`
43
- default to `image`. An explicit `image-` or `icon-` type works in any of the
44
- four slots. A pathname with slashes or an extension is not an art-slot address.
42
+ `data.tokenIcon` default to the `icon` type; `data.banner` defaults to
43
+ `image`. An explicit `image-` or `icon-` type works in any of the three
44
+ slots. A pathname with slashes or an extension is not an art-slot address.
45
45
 
46
46
  | Art slot | Its use |
47
47
  | ---------------- | ----------------------------------------------- |
48
48
  | `data.icon` | Profile art on a compiled Actor or Item |
49
49
  | `data.tokenIcon` | Actor token art; follows `data.icon` when unset |
50
- | `data.bgImage` | Background of a map Scene |
51
50
  | `data.banner` | Page hero image for the site |
52
51
 
53
52
  An Item without `data.icon` uses its builder's configured default art. An
@@ -55,7 +54,7 @@ Actor without `data.icon` uses its compiled default art. Write `data.icon`
55
54
  when the note needs a particular image. A portrait within the prose is an
56
55
  image embed, for example `![[image-smith-portrait|The smith]]{size=medium}`;
57
56
  it does not set a document's profile art. The
58
- [art-slot reference](../reference/format-details.md#the-four-art-slots) gives each
57
+ [art-slot reference](../reference/format-details.md#the-art-slots) gives each
59
58
  slot's type and document destination.
60
59
 
61
60
  A being's `subType` selects its compiled default art: `character` and `npc`
@@ -165,3 +165,9 @@ the needed output must already exist. The full compile orders those passes for
165
165
  you. `package-build package unpack` reads a built pack back into JSON for
166
166
  inspection. See the [command reference](../commands.md) for their arguments
167
167
  and output paths.
168
+
169
+ A compile also draws the map from each place given a **From here**
170
+ [generated section](../reference/format-details.md#generated-sections) and
171
+ stages it into the module at `assets/generated/from-<shortcode>.svg`, beside
172
+ the compiled packs, where the section's image resolves. Each generated section
173
+ starts its own page of the note's journal, as any H1 does.
@@ -136,7 +136,27 @@ The index is disposable build output; regenerate it from the authored tree
136
136
  when the content changes.
137
137
 
138
138
  The record's `address.canonical` is the full Address. Its `address.slug` is a
139
- local web link target. A stub has `address: null` and `anchors: null`; its
139
+ local web link target. Each entry of `anchors` carries its `slug`, `name`, `line`,
140
+ `link` and `kind` — `prose` for an anchor the body declares, `event` for an
141
+ event's `id` — so a note's events are listed there beside its body anchors. A
142
+ dependency's events are resolved through this list and through each event's
143
+ resolved date under `resolvedDates.events`.
144
+
145
+ **Every Address in the file is an object**, wherever it appears — `address.canonical`,
146
+ `documentation`, an anchor's `link`, and every Address a note writes:
147
+
148
+ ```json
149
+ { "address": "thalorna-note-place-xerathia", "anchor": null, "anchorKind": null }
150
+ { "address": "thalorna-note-lore-crown", "anchor": "founding", "anchorKind": "event" }
151
+ ```
152
+
153
+ `address` is the full canonical Address, `anchor` the anchor's slug or `null`,
154
+ and `anchorKind` its kind, `prose` or `event`, or `null`. A map keyed by
155
+ Address, such as a being's `affiliations`, keeps string keys: JSON keys are
156
+ strings, and no Address-keyed field takes an anchor. A package and every
157
+ package it depends on build with the same index format, since each reads the
158
+ others' published files. The SQL tables present each Address as a plain
159
+ string. A stub has `address: null` and `anchors: null`; its
140
160
  frontmatter remains queryable. An Item or Actor note can have both a readable
141
161
  `note` record and a game-system document destination. A generated
142
162
  `documentation` pointer connects a system document to its readable note.
@@ -31,7 +31,7 @@ document ID appears under another address, and **withdrawn** when that ID
31
31
  appears under none. Findings are warnings by default. `--strict` turns them
32
32
  into errors for a release gate. The comparison reads shipped Item packs, so it
33
33
  reports the surface consumers actually resolve rather than inferring changes
34
- from filenames. Use the [Address reference](../reference/format-details.md#addresses)
34
+ from filenames. Use the [Address reference](../content-format.md#addresses)
35
35
  when choosing a durable shortcode.
36
36
 
37
37
  ## Build the release files
@@ -112,3 +112,19 @@ publishes the version, and the workflow creates its version tag and GitHub
112
112
  Release. The workflow's manual dispatch can retry publishing a version already
113
113
  merged to `main`. The [npm package page](https://www.npmjs.com/package/@heroiclands/package-build)
114
114
  shows the published version and README.
115
+
116
+ Two scripts in this repository's own `package.json` run the same steps at a
117
+ desk:
118
+
119
+ - **`npm run build`** runs `package-build ci`: the Build & Test workflow's
120
+ `run:` steps in a `linux/amd64` container over a clean export of `HEAD`.
121
+ `npm run build -- --native` runs them on the host in the working tree instead.
122
+ Off Linux, the Typst, GraphViz and Hugo steps install nothing; each uses the
123
+ tool already on `PATH` and stops with `<tool> is not on PATH` when it is
124
+ missing, so a Mac running `--native` needs `typst`, `dot` and Hugo extended
125
+ installed first.
126
+ - **`npm run deploy:npm`** runs `npm publish`. Provenance is attested only on a
127
+ CI runner that can mint a Sigstore identity — GitHub Actions with an OIDC
128
+ token, or GitLab CI with `SIGSTORE_ID_TOKEN` — and anywhere else the publish
129
+ goes ahead without it. Arguments after `--` reach `npm publish`, so
130
+ `npm run deploy:npm -- --dry-run` lists the tarball without publishing.
@@ -115,15 +115,16 @@ notes is therefore a set of authored pages, each choosing its own contents.
115
115
  ## Use links, relations, and maps
116
116
 
117
117
  Wikilinks resolve through the address index. Each emitted page receives
118
- derived `related` entries for its outgoing links and backlinks. Place pages
119
- can receive `contains` from place parents and `governed_by` from their own explicit
120
- `data.government`; affiliation pages can receive `governed_places` from resolved government references. These values
121
- come from the content graph and should be read as generated page metadata.
122
-
123
- Place relationships can also produce a map alongside the page. The page
124
- links to that map as a local page resource; a place with no applicable
125
- relationship has no map. Set `site.maps: false` to omit these drawings. The
126
- [book guide](book.md) covers full-page maps in PDF, and the
118
+ derived `related` entries for the links its author wrote and the backlinks
119
+ other authors wrote to it.
120
+
121
+ What lies within a place, who governs it, what an affiliation governs, the
122
+ works naming a note and the map from a place reach the page as
123
+ [generated sections](../reference/format-details.md#generated-sections): H1
124
+ sections after the author's text, the same Markdown the Foundry journal and
125
+ the book receive. The map from a place is set inline in its **From here**
126
+ section, so its place names link to their pages; set `site.maps: false` to
127
+ draw none. The [book guide](book.md) covers the book, and the
127
128
  [packs guide](packs.md) covers documents compiled for Foundry.
128
129
 
129
130
  A package with a named `site.pass` can apply its own body transformation
@@ -144,16 +145,14 @@ The build always writes `title`, `slug`, `url`, `kbfolder`, `package` and
144
145
  note. `infoboxes` is assembled from the note's own fields, so no note authors
145
146
  it and nothing in the note format accepts it.
146
147
 
147
- The build writes `resolvedDates`, `related`, `contains`, `governed_by`, `governed_places`,
148
- `works` and `map` only when it has something to write. A page with no links either way
149
- carries no `related`, and a place with no drawing carries no `map`.
148
+ The build writes `resolvedDates` and `related` only when it has something to
149
+ write. A page with no links either way carries no `related`.
150
150
 
151
- An authored `aliases`, `related`, `contains`, `governed_by`, `governed_places`, `works` or
152
- `map` is dropped. Each states a fact about the whole tree rather than about one note, so
153
- the build replaces whatever a note wrote.
151
+ An authored `aliases` or `related` is dropped. Each states a fact about the whole
152
+ tree rather than about one note, so the build replaces whatever a note wrote.
154
153
 
155
154
  Artwork addresses are rewritten in place under `data`. A page's `data.icon`,
156
- `data.bgImage` and `data.banner` carry the URLs the site serves rather than the
155
+ `data.tokenIcon` and `data.banner` carry the URLs the site serves rather than the
157
156
  addresses the note authored, and there are four cases to tell apart:
158
157
 
159
158
  | The note writes | The page carries |
@@ -170,8 +169,8 @@ and both survive: `null` is a note naming no art, where a default may apply,
170
169
  and `""` is a note refusing art, where no default may replace it.
171
170
 
172
171
  The build writes these keys; [what the theme draws](../reference/theme-pages.md)
173
- describes the panel each one feeds — the infobox, the hero band, the government
174
- and related cards, the place map, and search.
172
+ describes the panel each one feeds — the infobox, the hero band, the related
173
+ card, and search — and the generated sections the theme draws as any other.
175
174
 
176
175
  ## Build and serve
177
176