@heroiclands/package-build 22.21.0 → 22.23.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 (152) hide show
  1. package/CHANGELOG.md +223 -0
  2. package/assets/styles/content-base.css +162 -0
  3. package/bin/content-commands.mjs +77 -17
  4. package/bin/package-build.mjs +15 -2
  5. package/ci/content-format-reference.mjs +68 -1
  6. package/config.mjs +26 -0
  7. package/content-config.mjs +153 -6
  8. package/docs/api.md +120 -89
  9. package/docs/authoring/assets.md +14 -0
  10. package/docs/authoring/dates-and-calendars.md +41 -3
  11. package/docs/authoring/frontmatter.md +2 -2
  12. package/docs/authoring/links-and-markup.md +325 -35
  13. package/docs/commands.md +12 -10
  14. package/docs/configuration.md +117 -14
  15. package/docs/content-format.md +4 -2
  16. package/docs/guides/book.md +5 -4
  17. package/docs/guides/reference-pages.md +15 -0
  18. package/docs/reference/format-details.md +545 -104
  19. package/docs/reference/note-types.md +30 -13
  20. package/engine/actor-compiler.mjs +23 -0
  21. package/engine/anchored-sections.mjs +35 -4
  22. package/engine/anchors.mjs +26 -30
  23. package/engine/art-fields.mjs +118 -0
  24. package/engine/art-slots.mjs +7 -1
  25. package/engine/asset-index.mjs +160 -10
  26. package/engine/asset-replacement.mjs +93 -0
  27. package/engine/asset-types.mjs +240 -0
  28. package/engine/base-compiler.mjs +139 -22
  29. package/engine/base-styles.mjs +149 -0
  30. package/engine/bundles.mjs +13 -2
  31. package/engine/compendiums.mjs +8 -1
  32. package/engine/content-blocks.mjs +410 -0
  33. package/engine/content-embeds.mjs +19 -0
  34. package/engine/content-figures.mjs +468 -0
  35. package/engine/content-footnotes.mjs +197 -0
  36. package/engine/content-format-check.mjs +15 -12
  37. package/engine/content-format.yaml +1 -1
  38. package/engine/content-images.mjs +126 -25
  39. package/engine/content-index.mjs +38 -12
  40. package/engine/content-links.mjs +14 -6
  41. package/engine/content-tables.mjs +45 -4
  42. package/engine/content-tasklists.mjs +189 -0
  43. package/engine/docs-checks.mjs +4 -4
  44. package/engine/extension-attributes.mjs +48 -2
  45. package/engine/field-reference.mjs +31 -10
  46. package/engine/field-spec.mjs +10 -8
  47. package/engine/foreign-catalog.mjs +143 -18
  48. package/engine/frontmatter-lint.mjs +120 -65
  49. package/engine/frontmatter.mjs +3 -3
  50. package/engine/heading-attributes.mjs +321 -0
  51. package/engine/helpers.mjs +109 -41
  52. package/engine/homepage.mjs +8 -8
  53. package/engine/index.mjs +3 -0
  54. package/engine/infobox-registry.mjs +4 -1
  55. package/engine/infobox-render.mjs +15 -3
  56. package/engine/infobox.mjs +129 -15
  57. package/engine/itinerary-scenes.mjs +4 -10
  58. package/engine/journals.mjs +294 -50
  59. package/engine/lore-events.mjs +197 -0
  60. package/engine/macros.mjs +1 -1
  61. package/engine/markdown-expressions.mjs +434 -59
  62. package/engine/note-dates.mjs +354 -12
  63. package/engine/note-frontmatter.mjs +7 -16
  64. package/engine/note-vocabulary.mjs +183 -42
  65. package/engine/page-lists.mjs +358 -0
  66. package/engine/pathnames.mjs +36 -0
  67. package/engine/pdf-build.mjs +431 -74
  68. package/engine/pdf-images.mjs +64 -0
  69. package/engine/pdf-render.mjs +563 -66
  70. package/engine/rank-ladder.mjs +198 -0
  71. package/engine/reckoning-markers.mjs +1 -1
  72. package/engine/retired-fields.mjs +8 -13
  73. package/engine/scenes.mjs +26 -7
  74. package/engine/site-build.mjs +166 -23
  75. package/engine/site-config.mjs +10 -1
  76. package/engine/site-index.mjs +90 -3
  77. package/engine/site-preview.mjs +20 -7
  78. package/engine/social-ties.mjs +52 -14
  79. package/engine/sql-tables.mjs +66 -9
  80. package/engine/standings.mjs +89 -6
  81. package/engine/system-block.mjs +56 -30
  82. package/engine/web-wikilinks.mjs +48 -1
  83. package/hm3/actors.mjs +8 -5
  84. package/manifest.mjs +43 -24
  85. package/package.json +1 -1
  86. package/packagebuild-metadata.jsonl +13 -13
  87. package/sohl/actors.mjs +22 -99
  88. package/sohl/item-fields.mjs +5 -5
  89. package/sohl/items.mjs +1 -1
  90. package/sohl/note-schemas.mjs +11 -21
  91. package/types/config.d.mts +9 -0
  92. package/types/content-config.d.mts +48 -1
  93. package/types/engine/actor-compiler.d.mts +11 -0
  94. package/types/engine/anchored-sections.d.mts +24 -0
  95. package/types/engine/anchors.d.mts +9 -4
  96. package/types/engine/art-fields.d.mts +64 -0
  97. package/types/engine/asset-index.d.mts +43 -0
  98. package/types/engine/asset-replacement.d.mts +22 -0
  99. package/types/engine/asset-types.d.mts +18 -0
  100. package/types/engine/base-compiler.d.mts +19 -17
  101. package/types/engine/base-styles.d.mts +93 -0
  102. package/types/engine/content-blocks.d.mts +87 -0
  103. package/types/engine/content-embeds.d.mts +16 -0
  104. package/types/engine/content-figures.d.mts +101 -0
  105. package/types/engine/content-footnotes.d.mts +80 -0
  106. package/types/engine/content-format-check.d.mts +8 -6
  107. package/types/engine/content-images.d.mts +78 -12
  108. package/types/engine/content-index.d.mts +8 -2
  109. package/types/engine/content-tables.d.mts +12 -4
  110. package/types/engine/content-tasklists.d.mts +72 -0
  111. package/types/engine/extension-attributes.d.mts +22 -1
  112. package/types/engine/field-reference.d.mts +21 -0
  113. package/types/engine/field-spec.d.mts +20 -16
  114. package/types/engine/foreign-catalog.d.mts +39 -0
  115. package/types/engine/frontmatter.d.mts +1 -1
  116. package/types/engine/heading-attributes.d.mts +179 -0
  117. package/types/engine/helpers.d.mts +29 -10
  118. package/types/engine/homepage.d.mts +7 -7
  119. package/types/engine/index.d.mts +1 -0
  120. package/types/engine/infobox-registry.d.mts +4 -1
  121. package/types/engine/infobox-render.d.mts +6 -1
  122. package/types/engine/infobox.d.mts +6 -0
  123. package/types/engine/journals.d.mts +41 -12
  124. package/types/engine/lore-events.d.mts +14 -0
  125. package/types/engine/markdown-expressions.d.mts +46 -2
  126. package/types/engine/note-dates.d.mts +64 -1
  127. package/types/engine/note-frontmatter.d.mts +1 -4
  128. package/types/engine/note-vocabulary.d.mts +82 -6
  129. package/types/engine/page-lists.d.mts +126 -0
  130. package/types/engine/pathnames.d.mts +26 -0
  131. package/types/engine/pdf-build.d.mts +28 -6
  132. package/types/engine/pdf-images.d.mts +52 -0
  133. package/types/engine/pdf-render.d.mts +84 -2
  134. package/types/engine/rank-ladder.d.mts +32 -0
  135. package/types/engine/retired-fields.d.mts +4 -6
  136. package/types/engine/site-build.d.mts +27 -4
  137. package/types/engine/site-config.d.mts +9 -0
  138. package/types/engine/site-index.d.mts +67 -1
  139. package/types/engine/site-preview.d.mts +1 -1
  140. package/types/engine/sql-tables.d.mts +31 -7
  141. package/types/engine/standings.d.mts +21 -3
  142. package/types/engine/system-block.d.mts +37 -14
  143. package/types/engine/web-wikilinks.d.mts +3 -1
  144. package/types/hm3/actors.d.mts +21 -3
  145. package/types/manifest.d.mts +19 -23
  146. package/types/sohl/actors.d.mts +6 -7
  147. package/engine/content-admonitions.mjs +0 -114
  148. package/engine/content-captions.mjs +0 -181
  149. package/engine/content-secrets.mjs +0 -112
  150. package/types/engine/content-admonitions.d.mts +0 -24
  151. package/types/engine/content-captions.d.mts +0 -45
  152. package/types/engine/content-secrets.d.mts +0 -17
@@ -55,7 +55,7 @@ around its own evaluation.
55
55
 
56
56
  ### Quick reference
57
57
 
58
- 22 top-level keys. `rootDir` is not one of them — a data configuration never
58
+ 23 top-level keys. `rootDir` is not one of them — a data configuration never
59
59
  writes it — and is documented under [Derived values](#derived-values) instead,
60
60
  alongside `foundryPackage`, `homepage`, `author` and `itemBuilders`, whose
61
61
  data-configuration behaviour is also derivation rather than ordinary
@@ -72,6 +72,7 @@ authoring.
72
72
  | [`itemBuilders`](#itembuilders) | object, or list of `{system, builders}` (or a name/list of names, in YAML) | no; refused in a `documentation` package | `{}` |
73
73
  | [`paths`](#paths) | object | no | see [`paths`](#paths) |
74
74
  | [`skipDirectories`](#skipdirectories) | string[] | no | `[]` |
75
+ | [`forbidGeneratedArt`](#forbidgeneratedart) | boolean | no | `false` |
75
76
  | [`icons`](#icons) | object, or a path to a file holding one | no | empty registry |
76
77
  | [`packs`](#packs) | array | yes, at least one entry, in a `systems` or `modules` package; refused in a `documentation` package | — |
77
78
  | [`docs`](#docs) | object | no; refused in a `documentation` package | `{}` |
@@ -87,7 +88,7 @@ authoring.
87
88
 
88
89
  Any key outside this list is refused:
89
90
 
90
- > `` `<key>` is not a recognized option (expected one of: rootDir, contentPackage, foundryPackage, homepage, author, packageKind, stats, itemBuilders, paths, skipDirectories, icons, packs, docs, site, pdf, compatibility, relationships, systems, requiresSystem, packageBuild, publish, changelog). ``
91
+ > `` `<key>` is not a recognized option (expected one of: rootDir, contentPackage, foundryPackage, homepage, author, packageKind, stats, itemBuilders, paths, skipDirectories, forbidGeneratedArt, icons, packs, docs, site, pdf, compatibility, relationships, systems, requiresSystem, packageBuild, publish, changelog). ``
91
92
 
92
93
  (`rootDir` appears in that list because it is a key `defineConfig` itself
93
94
  accepts — an `.mjs` configuration authors it directly. A YAML configuration
@@ -515,7 +516,7 @@ HeroicLands layout, resolved against `rootDir`:
515
516
  | `paths.packJson` | `build/packs-json` | Build-only per-entry JSON intermediate. |
516
517
  | `paths.stage` | `build/stage/packs` | Compiled LevelDB packs. |
517
518
  | `paths.unpack` | `build/tmp/packs` | Where `unpack` extracts JSON back to. |
518
- | `paths.foreignCache` | `build/cache/foreign` | Where a dependency declaring `itemCatalog: true` is unpacked. |
519
+ | `paths.foreignCache` | `build/cache/foreign` | Where a dependency declaring `itemCatalog: true` or `assetArchive: true` is unpacked. |
519
520
  | `paths.metadataCache` | `build/cache/metadata` | Where a dependency's published content index is fetched to, for every declared dependency. |
520
521
  | `paths.navigationCache` | `build/cache/navigation` | Where the site navigation heroiclands.org publishes is fetched to, for the generated Hugo menu. |
521
522
 
@@ -547,6 +548,26 @@ skipDirectories: [Templates]
547
548
 
548
549
  > ``package-build config: `skipDirectories[<index>]` must be a non-empty string.``
549
550
 
551
+ ### `forbidGeneratedArt`
552
+
553
+ **Type:** boolean · **Optional** · default `false`.
554
+
555
+ With it `true`, `package-build lint` refuses every asset record of this
556
+ package whose `ai` field states `true` — a licensing stance, not a build
557
+ detail: this package carries no machine-generated art. With it absent or
558
+ `false`, `lint` reports nothing extra about an `ai: true` record.
559
+
560
+ ```yaml
561
+ forbidGeneratedArt: true
562
+ ```
563
+
564
+ `lint` reports a located error naming the record and its file for each one it
565
+ finds:
566
+
567
+ > ``<file>: error: `<address>` is machine-generated art (`ai: true`), and `forbidGeneratedArt` refuses it in this package``
568
+
569
+ > ``package-build config: `forbidGeneratedArt` must be a boolean.``
570
+
550
571
  ### `icons`
551
572
 
552
573
  **Type:** object (`{families, icons, defaultFamily?}`), or a string naming a
@@ -1205,15 +1226,17 @@ system relationship it declares — see [`stats.systemVersion`](#statssystemvers
1205
1226
 
1206
1227
  Each entry, in any of the four lists:
1207
1228
 
1208
- | Key (under `relationships.<kind>[]`) | Type | Required | Default |
1209
- | ---------------------------------------- | ------------------------------- | -------- | ------- |
1210
- | `relationships.systems[].id` | string | yes | — |
1211
- | `relationships.systems[].contentPackage` | string | no | the id |
1212
- | `relationships.systems[].type` | string | no | none |
1213
- | `relationships.systems[].manifest` | string | no | none |
1214
- | `relationships.systems[].compatibility` | object, `{minimum?, verified?}` | no | none |
1215
- | `relationships.systems[].itemCatalog` | boolean | no | `false` |
1216
- | `relationships.systems[].contentIndex` | boolean | no | `true` |
1229
+ | Key (under `relationships.<kind>[]`) | Type | Required | Default |
1230
+ | ------------------------------------------ | ------------------------------- | -------- | ------- |
1231
+ | `relationships.systems[].id` | string | yes | — |
1232
+ | `relationships.systems[].contentPackage` | string | no | the id |
1233
+ | `relationships.systems[].type` | string | no | none |
1234
+ | `relationships.systems[].manifest` | string | no | none |
1235
+ | `relationships.systems[].compatibility` | object, `{minimum?, verified?}` | no | none |
1236
+ | `relationships.systems[].itemCatalog` | boolean | no | `false` |
1237
+ | `relationships.systems[].assetArchive` | boolean | no | `false` |
1238
+ | `relationships.systems[].contentIndex` | boolean | no | `true` |
1239
+ | `relationships.systems[].assetReplacement` | boolean | no | `false` |
1217
1240
 
1218
1241
  (the same keys apply under `requires[]`, `recommends[]` and
1219
1242
  `conflicts[]`.)
@@ -1222,7 +1245,7 @@ Each entry, in any of the four lists:
1222
1245
 
1223
1246
  > ``package-build config: `relationships.<kind>[<index>].id` must be a non-empty string.``
1224
1247
 
1225
- > ``package-build config: `relationships.<kind>[<index>].<key>` is not a recognized option (expected one of: id, contentPackage, type, manifest, compatibility, itemCatalog, contentIndex).``
1248
+ > ``package-build config: `relationships.<kind>[<index>].<key>` is not a recognized option (expected one of: id, contentPackage, type, manifest, compatibility, itemCatalog, assetArchive, contentIndex, assetReplacement).``
1226
1249
 
1227
1250
  `contentPackage` names what the other package's _content_ is called, where
1228
1251
  that differs from its Foundry id. A note addresses a file by the content
@@ -1241,6 +1264,31 @@ item catalogue at build time. It requires a `manifest`:
1241
1264
 
1242
1265
  > ``package-build config: `relationships.<kind>[<index>].itemCatalog` needs a `manifest` naming the package to fetch.``
1243
1266
 
1267
+ `assetArchive` opts into unpacking the named package's release archive for
1268
+ its asset bytes alone — the sibling of `itemCatalog`, for a dependency whose
1269
+ bytes are wanted and whose items are not. It builds no item catalogue from
1270
+ the archive, which is what distinguishes it from `itemCatalog: true`. It
1271
+ requires a `manifest`:
1272
+
1273
+ > ``package-build config: `relationships.<kind>[<index>].assetArchive` must be true or false.``
1274
+
1275
+ > ``package-build config: `relationships.<kind>[<index>].assetArchive` needs a `manifest` naming the package to fetch.``
1276
+
1277
+ A relationship with no items of its own to extract, such as an asset
1278
+ replacement module, declares only `assetArchive: true`:
1279
+
1280
+ ```yaml
1281
+ relationships:
1282
+ requires:
1283
+ - id: thalornaaltart
1284
+ manifest: https://…/releases/latest/download/module.json
1285
+ compatibility: { verified: "1.0.0" }
1286
+ assetArchive: true
1287
+ ```
1288
+
1289
+ A relationship declaring both `itemCatalog: true` and `assetArchive: true` is
1290
+ fetched once: `deps fetch` does not download the archive twice for one entry.
1291
+
1244
1292
  `contentIndex` and `itemCatalog` are the two edges a relationship may declare,
1245
1293
  and a package may have either without the other. `itemCatalog` says a
1246
1294
  dependency supplies _items_; `contentIndex`, `true` by default, says
@@ -1260,6 +1308,35 @@ demand.
1260
1308
 
1261
1309
  > ``package-build config: `relationships.<kind>[<index>].contentIndex` cannot be false together with `itemCatalog: true` — a catalogue is fetched from the same index.``
1262
1310
 
1311
+ `assetReplacement` opts a relationship into serving this package's own asset
1312
+ addresses from the named dependency's tree instead of its local one —
1313
+ `engine/asset-replacement.mjs`'s `resolveAssetReplacement` is what reads the
1314
+ declaration. Off by default: declaring a dependency is not the same as
1315
+ wanting its pictures in place of the ones this package ships itself. It
1316
+ requires a fetched index to check the replacement against, so it cannot pair
1317
+ with `contentIndex: false`:
1318
+
1319
+ ```yaml
1320
+ relationships:
1321
+ requires:
1322
+ - id: thalornaaltart
1323
+ manifest: https://…/releases/latest/download/module.json
1324
+ assetReplacement: true
1325
+ contentIndex: true
1326
+ ```
1327
+
1328
+ With that declared, an address `thalorna` would otherwise answer itself —
1329
+ `thalorna-none-image-thorn` — resolves against
1330
+ `thalornaaltart-none-image-thorn` first, falling back to `thalorna`'s own
1331
+ record when `thalornaaltart` carries no file at that address. A package
1332
+ declaring more than one `assetReplacement: true` relationship is tried in
1333
+ the order the relationships are declared, and the first one carrying the
1334
+ address wins.
1335
+
1336
+ > ``package-build config: `relationships.<kind>[<index>].assetReplacement` must be true or false.``
1337
+
1338
+ > ``package-build config: `relationships.<kind>[<index>].assetReplacement` cannot be true together with `contentIndex: false` — a replacement with no fetched index to check against resolves nothing.``
1339
+
1263
1340
  ### `systems`
1264
1341
 
1265
1342
  **Type:** object (`{id: spec}`) · **Optional** · default `{}`. Refused in a
@@ -1462,6 +1539,7 @@ packageBuild:
1462
1539
  | [`packageBuild.stageDir`](#packagebuildstagedir-and-packagebuildassets) | string | no | `build/stage` |
1463
1540
  | [`packageBuild.assets`](#packagebuildstagedir-and-packagebuildassets) | array | no | `[]` |
1464
1541
  | [`packageBuild.assetTransform`](#packagebuildassettransform-and-packagebuildmanifestflags) | string (path to a module) | no | `null` |
1542
+ | [`packageBuild.baseStyles`](#packagebuildbasestyles) | boolean | no | `true` |
1465
1543
  | [`packageBuild.manifest`](#packagebuildmanifest) | object, pass-through | no | `{}` |
1466
1544
  | [`packageBuild.manifestFlags`](#packagebuildassettransform-and-packagebuildmanifestflags) | string (path to a module) | no | `null` |
1467
1545
  | [`packageBuild.schema`](#packagebuildschema) | object (`{documentType: {from, registry}}`) | no | `[]` |
@@ -1475,7 +1553,7 @@ packageBuild:
1475
1553
  | [`packageBuild.proseLint`](#packagebuildproselint) | object | no | `{age: 21, threshold: 5, minWords: 8, rules: readability}` |
1476
1554
  | [`packageBuild.proseScore`](#packagebuildprosescore) | object | no | `{minWords: 80, bands: {}}` |
1477
1555
 
1478
- > ``package-build config: `packageBuild.<key>` is not a recognised key (expected one of: stageDir, assets, assetTransform, manifest, manifestFlags, schema, clean, lang, deploy, release, bundle, container, e2e, proseLint, proseScore).``
1556
+ > ``package-build config: `packageBuild.<key>` is not a recognised key (expected one of: stageDir, assets, assetTransform, baseStyles, manifest, manifestFlags, schema, clean, lang, deploy, release, bundle, container, e2e, proseLint, proseScore).``
1479
1557
 
1480
1558
  ### `packageBuild.proseLint`
1481
1559
 
@@ -1561,6 +1639,31 @@ repository has to compute. Neither declares anything, both default to
1561
1639
 
1562
1640
  > ``package-build config: `packageBuild.manifestFlags` must be a non-empty string.``
1563
1641
 
1642
+ ### `packageBuild.baseStyles`
1643
+
1644
+ Whether the package takes the shared base stylesheet. It defaults to `true`,
1645
+ is staged to `styles/package-build-base.css` under the stage root, and is
1646
+ named first in the generated manifest's `styles`.
1647
+
1648
+ The sheet carries only what every package's compiled content relies on to be
1649
+ legible — the definition-list style an infobox and the markdown `Term` /
1650
+ `: definition` syntax both emit — and is scoped to the journal, enriched-field
1651
+ and editor containers so it cannot reach Foundry's own interface.
1652
+
1653
+ It loads into the cascade layer Foundry reserves for the package kind,
1654
+ `system` or `modules`, which is the weakest layer a package can occupy. A
1655
+ package overrides any rule in it from its own stylesheet, whether that sheet
1656
+ names a layer or none at all, with no specificity contest and no
1657
+ `!important`. The term column, the gutter and the width at which the list
1658
+ stacks are custom properties — `--pb-deflist-term-width`,
1659
+ `--pb-deflist-gutter` and `--pb-deflist-row-gap` — so a package adjusts them
1660
+ without copying the rule.
1661
+
1662
+ Declare `false` to decline it; nothing is staged and nothing is added to
1663
+ `styles`:
1664
+
1665
+ > ``package-build config: `packageBuild.baseStyles` must be true or false.``
1666
+
1564
1667
  ### `packageBuild.manifest`
1565
1668
 
1566
1669
  `packageBuild.manifest.title` is a required, non-empty string for a Foundry
@@ -25,13 +25,15 @@ Start with [your first note](authoring/first-note.md). It explains the smallest
25
25
 
26
26
  A nonempty note body creates one system-agnostic Foundry JournalEntry. Content before the first H1 becomes an introduction page; every H1 starts another JournalEntryPage. A lower-level heading with an explicit `{#anchor}` starts a page too, so a link can address it. The same prose becomes a web page and can be selected for a book. The content index records the note and its frontmatter for queries and navigation.
27
27
 
28
- A `:::caption {#anchor}` fence labels the next prose, code, table, or image block. Its anchor makes the block addressable; `[[#anchor|]]` displays the generated kind and number, such as **Figure 12**. Foundry gives the captioned block a JournalEntryPage. See [captions and numbered references](authoring/links-and-markup.md#captions-and-numbered-references) for the syntax.
28
+ A `:::figure {#anchor}` fence numbers whatever it holds — prose, code, a table, or an image. Its anchor makes the fence a target `{{ref "#anchor"}}` can point to, displaying the generated kind and number, such as **Figure 12**. Foundry gives the fence its own JournalEntryPage. See [figures and numbered references](authoring/links-and-markup.md#figures-and-numbered-references) for the syntax.
29
29
 
30
30
  Body extensions use braces with space-separated `key=value` attributes:
31
31
  `![[icon-harbor|Harbor]]{float=top-left size=medium}` and an SQL fence with
32
32
  `{allow-empty=true}` are examples. Inline font glyphs use the package's icon
33
33
  registry, as in `:icon warning:{size=lg}`; image icons use asset Addresses.
34
- `:::info` and `:::warn` add labelled boxes within the prose. The
34
+ `:::info` and `:::warn` add labelled boxes within the prose. A
35
+ `pagelist` fence renders the pages carrying a tag as a list of links, so a
36
+ landing page states a group by intent rather than enumerating it. The
35
37
  [links and markup guide](authoring/links-and-markup.md) gives the complete syntax.
36
38
  Footnotes use `[^id]` references and `[^id]:` definitions, with numeric output
37
39
  markers scoped to each note. Definition lists use a term followed by one or
@@ -88,10 +88,11 @@ finding.
88
88
  Each section and each selected entry starts on its own page. The PDF has a
89
89
  contents page and an outline for section and entry navigation. A note's
90
90
  `description` appears as an epigraph when present. Wider tables span both
91
- columns; an uncaptioned wide table starts on a new page so it stays after its
92
- introduction. A captioned wide table may float to the page top, and its numbered
93
- caption stays with it. A table too tall for one page takes pages of its own.
94
- Caption numbers run through the whole book in reading order. Maps attached
91
+ columns; a wide table outside a figure fence starts on a new page so it stays
92
+ after its introduction. A wide table inside one may float to the page top, and
93
+ its label stays with it. A table too tall for one page takes pages of its own.
94
+ A figure's number runs through the whole book in reading order, counted within
95
+ its own kind. Maps attached
95
96
  to selected places use landscape pages after those entries. Regional, theatre
96
97
  of the mind, battle, and local map backgrounds also print on landscape pages.
97
98
  Battle and local maps print each Scene level's background on its own page.
@@ -45,6 +45,21 @@ WHERE type = 'affiliation'
45
45
  ```
46
46
  ````
47
47
 
48
+ Where the page introduces a _group_ rather than a type, a `pagelist` fence
49
+ states the group by its tag and lists the pages carrying it:
50
+
51
+ ````markdown
52
+ ```pagelist {tag="key-concept" descriptions=true}
53
+
54
+ ```
55
+ ````
56
+
57
+ That keeps the entry page true as notes are written: a page joins the list by
58
+ carrying the tag, so nobody has to remember to add it here. A tag no page
59
+ carries is a build error naming the tag. See
60
+ [page lists](../authoring/links-and-markup.md#page-lists) for the attributes it
61
+ takes.
62
+
48
63
  `data.pack: none` is appropriate when the page belongs on the site and in the
49
64
  index but does not need a Foundry compendium entry. A documentation package
50
65
  has no packs at all. A page with an empty body is a stub: it remains in the