@heroiclands/package-build 22.25.0 → 22.27.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 (67) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +4 -0
  3. package/assets/styles/content-base.css +46 -3
  4. package/docs/api.md +35 -12
  5. package/docs/authoring/frontmatter.md +21 -0
  6. package/docs/authoring/links-and-markup.md +96 -149
  7. package/docs/content-format.md +6 -2
  8. package/docs/guides/government-migration.md +52 -0
  9. package/docs/guides/site.md +5 -5
  10. package/docs/reference/format-details.md +152 -246
  11. package/docs/reference/note-types.md +27 -28
  12. package/docs/reference/theme-pages.md +33 -29
  13. package/engine/anchors.mjs +85 -13
  14. package/engine/art-fields.mjs +1 -1
  15. package/engine/base-compiler.mjs +9 -1
  16. package/engine/bundles.mjs +1 -1
  17. package/engine/calendar-notes.mjs +1 -1
  18. package/engine/code-fences.mjs +6 -2
  19. package/engine/content-alerts.mjs +195 -0
  20. package/engine/content-blocks.mjs +119 -241
  21. package/engine/content-embeds.mjs +1 -1
  22. package/engine/content-figures.mjs +166 -230
  23. package/engine/content-format-check.mjs +3 -2
  24. package/engine/content-format.yaml +1 -5
  25. package/engine/content-images.mjs +3 -3
  26. package/engine/content-poetry.mjs +141 -0
  27. package/engine/content-spans.mjs +177 -0
  28. package/engine/foundry-entries.mjs +2 -0
  29. package/engine/frontmatter-lint.mjs +2 -1
  30. package/engine/heading-attributes.mjs +1 -1
  31. package/engine/helpers.mjs +8 -3
  32. package/engine/holdings.mjs +197 -333
  33. package/engine/index.mjs +1 -1
  34. package/engine/infobox.mjs +11 -1
  35. package/engine/journals.mjs +8 -6
  36. package/engine/markdown-expressions.mjs +9 -3
  37. package/engine/metadata-index.mjs +5 -3
  38. package/engine/note-vocabulary.mjs +30 -13
  39. package/engine/pdf-build.mjs +1 -1
  40. package/engine/pdf-render.mjs +90 -31
  41. package/engine/populations.mjs +17 -83
  42. package/engine/site-build.mjs +44 -16
  43. package/engine/site-preview.mjs +22 -6
  44. package/engine/wikilinks.mjs +18 -4
  45. package/hugo-theme/layouts/partials/holdings/entry.html +2 -2
  46. package/hugo-theme/layouts/partials/holdings.html +14 -14
  47. package/hugo-theme/static/css/style.css +40 -2
  48. package/package.json +1 -1
  49. package/sohl/item-fields.mjs +0 -16
  50. package/types/engine/anchors.d.mts +13 -4
  51. package/types/engine/art-fields.d.mts +1 -1
  52. package/types/engine/calendar-notes.d.mts +1 -1
  53. package/types/engine/code-fences.d.mts +3 -1
  54. package/types/engine/content-alerts.d.mts +62 -0
  55. package/types/engine/content-blocks.d.mts +3 -10
  56. package/types/engine/content-embeds.d.mts +1 -1
  57. package/types/engine/content-figures.d.mts +13 -20
  58. package/types/engine/content-images.d.mts +1 -1
  59. package/types/engine/content-poetry.d.mts +32 -0
  60. package/types/engine/content-spans.d.mts +18 -0
  61. package/types/engine/holdings.d.mts +75 -118
  62. package/types/engine/infobox.d.mts +1 -0
  63. package/types/engine/markdown-expressions.d.mts +1 -1
  64. package/types/engine/note-vocabulary.d.mts +14 -2
  65. package/types/engine/populations.d.mts +4 -13
  66. package/types/engine/site-build.d.mts +3 -3
  67. package/types/engine/wikilinks.d.mts +5 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @heroiclands/package-build
2
2
 
3
+ ## 22.27.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Governing bodies list the places they govern from each place’s stated government, keeping those lists consistent with place descriptions.
8
+ - Tables that listed governing bodies through affiliation territory lists must query each place’s government instead.
9
+ - Captions now precede the item they describe; use `:@` for numbered captions or `:` for captions without numbers.
10
+ - Write poetry in a `poetry` code fence, and use attributed divs and inline spans to group or style text.
11
+ - Replace former figure wrappers with leading captions; the old figure and poetry div fences are no longer supported.
12
+ - Blockquote alerts provide note, tip, important and warning callouts; the former info and warn div fences are removed.
13
+
14
+ ## 22.26.0
15
+
16
+ ### Minor Changes
17
+
18
+ Place notes can name their governing affiliation in `data.government`, or state `null` for complete anarchy. A place with positive population and no government now receives an advisory. Places with zero, null or unspecified population no longer receive the old unheld-land warning; holdings remain separate tenure facts. Governments appear in note infoboxes and persist in published metadata.
19
+
3
20
  ## 22.25.0
4
21
 
5
22
  ### Minor Changes
package/README.md CHANGED
@@ -46,4 +46,8 @@ The documentation lives in the Markdown files in this repository. Start with [th
46
46
  | Error locations and severities | [Diagnostics](docs/diagnostics.md) |
47
47
  | Architectural rationale | [Design](docs/design.md) |
48
48
 
49
+ Place notes may declare a governing affiliation with `data.government` (`Address`, default target type `affiliation`), or explicit `null` for complete anarchy. Positive population with an omitted government produces an advisory. See [place governments](docs/authoring/frontmatter.md#place-governments) and [government migration](docs/guides/government-migration.md).
50
+
51
+ Leading captions, generic fenced divs, inline spans, and poetry code fences are described in [links and markup](docs/authoring/links-and-markup.md).
52
+
49
53
  The package is GPL-3.0-or-later for code and CC-BY-SA-4.0 for original content. See [LICENSE.md](LICENSE.md).
@@ -152,9 +152,8 @@
152
152
  }
153
153
 
154
154
  /*
155
- * Named body blocks. `:::info` and `:::warn` emit a `<section>` carrying the
156
- * block's name as its class, so the appearance belongs here rather than in an
157
- * inline `style` a consumer cannot override.
155
+ * Historical info/warn section classes remain available for styled prose.
156
+ * New alerts use the separate alert classes below.
158
157
  *
159
158
  * `secret` is deliberately absent: Foundry styles `section.secret` itself and
160
159
  * owns the reveal control on it, and a rule here would fight both.
@@ -205,3 +204,47 @@
205
204
  --pb-block-edge: #7a5100;
206
205
  }
207
206
  }
207
+
208
+ /* Alerts share ordinary body colors, with an accent on their edge and title. */
209
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) .alert {
210
+ border-inline-start: 0.25rem solid var(--pb-alert-color);
211
+ padding: 0.5rem 1rem;
212
+ margin: 1rem 0;
213
+ color: inherit;
214
+ background: transparent;
215
+ }
216
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) .alert-title {
217
+ display: flex;
218
+ align-items: center;
219
+ gap: 0.5em;
220
+ margin: 0 0 0.5rem;
221
+ font-weight: 600;
222
+ color: var(--pb-alert-color);
223
+ }
224
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) .alert-icon {
225
+ width: 1em;
226
+ height: 1em;
227
+ flex-shrink: 0;
228
+ fill: currentColor;
229
+ }
230
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
231
+ .alert
232
+ > :last-child {
233
+ margin-bottom: 0;
234
+ }
235
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) .alert-note {
236
+ --pb-alert-color: #0969da;
237
+ --pb-alert-color: light-dark(#0969da, #4493f8);
238
+ }
239
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) .alert-tip {
240
+ --pb-alert-color: #1a7f37;
241
+ --pb-alert-color: light-dark(#1a7f37, #3fb950);
242
+ }
243
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) .alert-important {
244
+ --pb-alert-color: #8250df;
245
+ --pb-alert-color: light-dark(#8250df, #ab7df8);
246
+ }
247
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) .alert-warning {
248
+ --pb-alert-color: #9a6700;
249
+ --pb-alert-color: light-dark(#9a6700, #d29922);
250
+ }
package/docs/api.md CHANGED
@@ -348,6 +348,8 @@ The note types the engine itself declares — vocabulary that is a fact about th
348
348
 
349
349
  ### `engine.noteVocabulary`
350
350
 
351
+ `DataFieldSpec.nullText` declares the infobox text for an explicitly authored null. The place `government` field uses “Complete anarchy”; omitted fields remain absent. Its Address targets must be affiliations.
352
+
351
353
  An addressed content note accepts exactly these top-level keys, in this order: `shortcode`, `name`, `type`, `subType`, `description`, `tags`, `data`, `dnd5e`, `hm3`, `sohl`. A key outside this list is an error at its own line. The system blocks are one per system the address vocabulary recognizes, sorted by name after the fixed keys, so a further system is a change to `SYSTEM_IDS` alone. No dnd5e-specific document types are declared. Within that shape, the closed vocabularies are the `data:` container and each type's `subType`.
352
354
 
353
355
  | Export | Signature | Returns | Use it when |
@@ -370,6 +372,7 @@ An addressed content note accepts exactly these top-level keys, in this order: `
370
372
  | `subTypeCharsetMessage` | `subTypeCharsetMessage(value)` | `string` | building the message for a `subType` outside the address charset |
371
373
  | `typeCharsetMessage` | `typeCharsetMessage(type)` | `string` | building the message for a `type` outside the address charset |
372
374
  | `assertVocabularyCharset` | `assertVocabularyCharset(vocabulary, where)` | throws | refusing a vocabulary declaration whose type or subType breaks the address charset |
375
+ | `isLegacyDataField` | `isLegacyDataField(type, key)` | `boolean` | testing accepted but ignored transitional affiliation `data.domains` input |
373
376
  | `dataFields` | `dataFields(type, vocabulary)` | `readonly DataFieldSpec[] \| undefined` | looking up the `data:` keys a note type may carry |
374
377
  | `subTypes` | `subTypes(type, vocabulary)` | `readonly string[] \| null \| undefined` | looking up the closed `subType` values a note type declares |
375
378
 
@@ -495,21 +498,24 @@ What a place is next to (`data.borders`) and reachable from (`data.routes`): the
495
498
 
496
499
  ### `engine.holdings`
497
500
 
498
- What lies within a place, who holds it, and what an affiliation holds — read off `data.parents` and `data.domains` across this package and every fetched index, inverted into the `contains`, `held_by` and `holdings` lists the site build writes — and the tenure check `package-build lint` runs on a place.
501
+ What lies within a place and which affiliation explicitly governs it — read off `data.parents` and resolved explicit `data.government` across this package and every fetched index, inverted into the `contains`, `governed_by` and `governed_places` lists the site build writes — and the population-based government advisory `package-build lint` runs on a place.
502
+
503
+ | Export | Signature | Returns | Use it when |
504
+ | ---------------------- | --------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
505
+ | `HOLDINGS_KEYS` | `const HOLDINGS_KEYS` | `readonly string[]` | derived `contains`, `governed_by`, `governed_places` and retired `held_by`/`holdings` keys removed from authored page metadata |
506
+ | `HELD_SUBTYPES` | `const HELD_SUBTYPES` | `readonly string[]` | deprecated historical tenure subtypes; government checks apply to every place subtype |
507
+ | `holdingsNode` | `holdingsNode(fm, { title, url, package, system })` | `HoldingsNode\|null` | reading a local note as a node of the derivation, or `null` for one that is neither a place nor an affiliation |
508
+ | `foreignHoldingsNodes` | `foreignHoldingsNodes(foreignIndex)` | `HoldingsNode[]` | reading a fetched index's places and affiliations as nodes |
509
+ | `holdingsPages` | `holdingsPages(nodes, { governmentNodes })` | `Map<string, Holdings>` | inverting `parents` and resolved explicit `government` into each page's sorted lists, keyed by URL, for every page with at least one entry |
510
+ | `shortcodesOf` | `shortcodesOf(value)` | `string[]` | the shortcodes a `parents` value names — bare or qualified Addresses — empties dropped and repeats collapsed |
511
+ | `checkGovernment` | `checkGovernment(note)` | `object[]` | warning at `data.population` for positive population with no own `data.government` key; explicit null means anarchy |
512
+ | `checkHeld` | `checkHeld(note, { index })` | `object[]` | deprecated compatibility wrapper for `checkGovernment`; holdings do not determine government |
499
513
 
500
- | Export | Signature | Returns | Use it when |
501
- | ---------------------- | ------------------------------------ | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
502
- | `HOLDINGS_KEYS` | `const HOLDINGS_KEYS` | `readonly string[]` | the three derived keys — `contains`, `held_by`, `holdings` — which a note cannot author |
503
- | `HELD_SUBTYPES` | `const HELD_SUBTYPES` | `readonly string[]` | the place subTypes tenure is checked on — `settlement`, `site`, `structure` |
504
- | `holdingsNode` | `holdingsNode(fm, { title, url })` | `HoldingsNode\|null` | reading a local note as a node of the derivation, or `null` for one that is neither a place nor an affiliation |
505
- | `foreignHoldingsNodes` | `foreignHoldingsNodes(foreignIndex)` | `HoldingsNode[]` | reading a fetched index's places and affiliations as nodes |
506
- | `holdingsPages` | `holdingsPages(nodes)` | `Map<string, Holdings>` | inverting `parents` and `domains` into each page's sorted lists, keyed by URL, for every page with at least one entry |
507
- | `shortcodesOf` | `shortcodesOf(value)` | `string[]` | the shortcodes a `parents` or `domains` value names — bare, addressed or wikilinked — empties dropped and repeats collapsed |
508
- | `checkHeld` | `checkHeld(note, { index })` | `object[]` | linting a place's tenure — a settlement, site or structure no affiliation's `domains` names is a warning at its `type:` line |
514
+ Government-only supplemental records passed in `holdingsPages` options include public local stubs in the reverse lists without adding them to the geographical graph. Published nodes retain precedence; stubs without URLs render as plain text.
509
515
 
510
516
  ### `engine.populations`
511
517
 
512
- Whether a world's population figures agree from the region down — the polities and regions inside a place against what it states, a settlement against every place containing it, and a `doc` note's cited figures against the notes that own them. `package-build lint` runs all four as warnings.
518
+ Whether a world's population figures agree from the region down — the regions inside a place against what it states, a settlement against every place containing it, and a `doc` note's cited figures against the notes that own them. `package-build lint` runs all three as warnings.
513
519
 
514
520
  | Export | Signature | Returns | Use it when |
515
521
  | ----------------------- | ---------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------ |
@@ -697,7 +703,7 @@ invokes the check during frontmatter lint.
697
703
 
698
704
  ### `engine.markdownExpressions`
699
705
 
700
- `renderMarkdownExpressions(body, { fm, dates, sqlResults, figures, file, bodyLine })` expands scalar frontmatter references and registered Handlebars helpers outside Markdown code. The `dateformat` helper uses `formatDateInCalendar`; `gt`, `gte`, `lt`, `lte`, `eq`, `and`, `or`, and `not` evaluate explicit comparisons. The `sql` helper reads a prepared scalar result, which must contain one row and one column. The `ref` helper links to a `:::figure` fence by its anchor — `"#thorn"` on the same note — in `number` (the default, and admitted explicitly), `full` or `title` form; `figures` is a `Map` of this note's own figures, by id, to `{label, caption, hasCaption}`, and an address naming another note is reported as unresolved rather than looked up. `sqlQueriesInMarkdown` discovers SQL calls, including nested calls; `prepareInlineSqlExpressions` evaluates them through the same DuckDB connection as SQL tables. `EXPRESSION_HELPERS` is the one list of helpers: the engine registers from it and the authoring document is checked against it, so each name there carries the parameters it takes and a sentence on what it does. The renderer returns `{ markdown, findings }`, with source positions for invalid expressions. Site, Foundry and PDF passes invoke it after generated tables expand and before links resolve. Hugo shortcodes remain literal for the site renderer.
706
+ `renderMarkdownExpressions(body, { fm, dates, sqlResults, figures, file, bodyLine })` expands scalar frontmatter references and registered Handlebars helpers outside Markdown code. The `dateformat` helper uses `formatDateInCalendar`; `gt`, `gte`, `lt`, `lte`, `eq`, `and`, `or`, and `not` evaluate explicit comparisons. The `sql` helper reads a prepared scalar result, which must contain one row and one column. The `ref` helper links to a captioned item by its anchor — `"#thorn"` on the same note — in `number` (the default, and admitted explicitly), `full` or `title` form; `figures` is a `Map` of this note's own figures, by id, to `{label, caption, hasCaption}`, and an address naming another note is reported as unresolved rather than looked up. `sqlQueriesInMarkdown` discovers SQL calls, including nested calls; `prepareInlineSqlExpressions` evaluates them through the same DuckDB connection as SQL tables. `EXPRESSION_HELPERS` is the one list of helpers: the engine registers from it and the authoring document is checked against it, so each name there carries the parameters it takes and a sentence on what it does. The renderer returns `{ markdown, findings }`, with source positions for invalid expressions. Site, Foundry and PDF passes invoke it after generated tables expand and before links resolve. Hugo shortcodes remain literal for the site renderer.
701
707
 
702
708
  | Export | Signature | Returns | Use it when |
703
709
  | --------------------------- | ------------------------------------------ | ---------------------- | ------------------------------------------------ |
@@ -1961,3 +1967,20 @@ The Changesets changelog generator, loaded with `require` — a CommonJS module
1961
1967
  ## `./package.json`
1962
1968
 
1963
1969
  Not a JavaScript module — this subpath entry exists so tooling (bundlers, `import.meta.resolve`, a script reading the installed version) can resolve the package's own `package.json` through Node's package-exports resolution instead of reaching outside the declared export map.
1970
+
1971
+ ## Markup parsing modules
1972
+
1973
+ The engine wildcard subpath also exposes the body parsers below. Historical figure API names now operate on leading captions; `:::figure` is removed.
1974
+
1975
+ | Module | Exports | Contract |
1976
+ | ---------------------------- | -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1977
+ | `engine/content-figures.mjs` | `FIGURE_NAMES`, `FIGURE_CLASSES`, `scanFigures`, `numberFigures`, `renderFigureBlocks` | Scan leading captions and their next supported blocks; `numbered` distinguishes `:` and `:@`. `type` may override inference; `example` is supported. `FIGURE_CLASSES` retains built-in styled classes, not a closed author vocabulary. |
1978
+ | `engine/content-blocks.mjs` | `BLOCK_NAMES`, `BLOCK_CONTAINERS`, `scanBlocks`, `renderBlocks`, `renderWithheldSections` | Read div and secret fences; info/warn names are retired; generic div records have `name: "div"` and no generated title. Scanning includes poetry errors. |
1979
+ | `engine/content-poetry.mjs` | `verseLines`, `poetryStanzas`, `inlineVerse`, `poetryMarkdown`, `scanPoetry`, `renderPoetry` | Read `poetry` code fences, validate authored metadata and tabs, and preserve relative indent levels and stanzas. |
1980
+ | `engine/content-spans.mjs` | `spanMarkdownPlugin`, `scanSpans`, `renderSpans` | Parse bracket spans after recognized links. The scanner returns spans and located errors; the renderer preserves inline Markdown in attributed HTML spans. |
1981
+
1982
+ `scanFigures` retains `line` (1-based caption line), `bodyStart`/`bodyEnd` (0-based, exclusive-end target range), and `close` (last consumed target line), plus caption attributes, kind, label and number. Unnumbered captions have an empty label and consume no number. `collectAnchors` discovers heading, caption, div, poem and span identifiers; `markupAnchorFindings(body)` reports collisions involving non-page identifiers; Foundry maps non-page anchors to the actual containing page UUID. An unnumbered `ref` requires `form="title"` or `form="full"`.
1983
+
1984
+ `engine/content-alerts.mjs` exposes `ALERT_TYPES`, `scanAlerts(source)` and `renderAlerts(source, target)` and `alertMarkdownPlugin(md)`. The scanner returns `{blocks, errors}`; alert records carry `type`, `id`, `classes`, `attributes`, `title`, `body` and inclusive zero-based `start`/`end` source lines. The renderer produces attributed `aside.alert` wrappers while leaving alert body Markdown in the document's scope. Alert validation and anchor discovery run in site, Foundry and book pipelines.
1985
+
1986
+ Alert `title` is the displayed heading override; absent, it defaults to the label in `ALERT_TYPES`. It does not change the alert type’s icon or color.
@@ -39,6 +39,27 @@ A `sohl:`, `hm3:` or `dnd5e:` block holds values for that Foundry system. Its `s
39
39
 
40
40
  One body describes the subject for both systems. A being's `{#appearance}` and `{#dossier}` sections feed SoHL Actor `system.appearance` and `system.dossier`, and HM3 Actor `system.description` and `system.biography`. An Item's `{#description}` page becomes a shared JournalEntryPage. Its resolved `@UUID[...]` pointer is stored in SoHL Item `system.docHtml` and HM3 Item `system.description`. An Item with no `{#description}` anchor uses its first page. Other H1 pages remain linkable.
41
41
 
42
+ ## Place governments
43
+
44
+ A place may state its governing affiliation in `data.government`. The field type is `Address`, with `affiliation` as the default target type when the type is omitted. Use either a local shortcode or a fully qualified affiliation Address from this package or a fetched dependency:
45
+
46
+ ```yaml
47
+ data:
48
+ population: 1200
49
+ government: crown
50
+ ```
51
+
52
+ Write `government: null` for **complete anarchy**. Omitting the key leaves government unspecified. Null is retained in published metadata and displayed as “Complete anarchy” in website, Foundry and book infoboxes; an omitted key has no Government row. A reference displays a link to the governing affiliation.
53
+
54
+ | Population | Government | Governance advisory |
55
+ | --------------------- | ------------------- | -------------------------------- |
56
+ | Positive | Omitted | Warning at `data.population` |
57
+ | Missing, zero or null | Omitted | None |
58
+ | Any | Explicit `null` | None; complete anarchy |
59
+ | Any | Affiliation Address | None; the reference must resolve |
60
+
61
+ These rules apply to every place subtype, including settlements, sites, structures, features, regions and worlds. Empty strings, numbers, lists, maps, references to another note type and unresolved references are errors. They do not declare anarchy. `domains`, `parents`, containment and capitals do not supply a government. The generated `governed_by` and `governed_places` lists use only explicit government references. Legacy affiliation `data.domains` is accepted temporarily but ignored; record any distinct ownership or influence in prose. See [government migration](../guides/government-migration.md).
62
+
42
63
  ## Publication and summaries
43
64
 
44
65
  A note infobox draws from shared `data`. A being profile shows its top-level `subType` as `Type: Character`, `Type: NPC`, or `Type: Creature`. A system infobox draws from the system's fields, and a mapped system with no document says “Not available.” The same authored body appears in Foundry, on the website, and in the book; the book places boxes after the authored body, Foundry appends one page per box, and the website places boxes in its responsive side rail. [Infobox details](../reference/format-details.md#the-infobox) describe field order and empty values.