@heroiclands/package-build 22.26.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 (64) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +3 -1
  3. package/assets/styles/content-base.css +46 -3
  4. package/docs/api.md +33 -13
  5. package/docs/authoring/frontmatter.md +2 -2
  6. package/docs/authoring/links-and-markup.md +96 -149
  7. package/docs/content-format.md +5 -3
  8. package/docs/guides/government-migration.md +52 -0
  9. package/docs/guides/site.md +5 -5
  10. package/docs/reference/format-details.md +110 -219
  11. package/docs/reference/note-types.md +27 -29
  12. package/docs/reference/theme-pages.md +31 -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/code-fences.mjs +6 -2
  18. package/engine/content-alerts.mjs +195 -0
  19. package/engine/content-blocks.mjs +119 -241
  20. package/engine/content-embeds.mjs +1 -1
  21. package/engine/content-figures.mjs +166 -230
  22. package/engine/content-format-check.mjs +3 -2
  23. package/engine/content-format.yaml +0 -5
  24. package/engine/content-images.mjs +3 -3
  25. package/engine/content-poetry.mjs +141 -0
  26. package/engine/content-spans.mjs +177 -0
  27. package/engine/foundry-entries.mjs +2 -0
  28. package/engine/frontmatter-lint.mjs +2 -1
  29. package/engine/heading-attributes.mjs +1 -1
  30. package/engine/helpers.mjs +8 -3
  31. package/engine/holdings.mjs +165 -273
  32. package/engine/index.mjs +1 -1
  33. package/engine/infobox.mjs +1 -1
  34. package/engine/journals.mjs +8 -6
  35. package/engine/markdown-expressions.mjs +9 -3
  36. package/engine/metadata-index.mjs +2 -3
  37. package/engine/note-vocabulary.mjs +16 -11
  38. package/engine/pdf-build.mjs +1 -1
  39. package/engine/pdf-render.mjs +90 -31
  40. package/engine/populations.mjs +17 -83
  41. package/engine/site-build.mjs +44 -16
  42. package/engine/site-preview.mjs +22 -6
  43. package/engine/wikilinks.mjs +18 -4
  44. package/hugo-theme/layouts/partials/holdings/entry.html +2 -2
  45. package/hugo-theme/layouts/partials/holdings.html +14 -14
  46. package/hugo-theme/static/css/style.css +40 -2
  47. package/package.json +1 -1
  48. package/sohl/item-fields.mjs +0 -16
  49. package/types/engine/anchors.d.mts +13 -4
  50. package/types/engine/art-fields.d.mts +1 -1
  51. package/types/engine/code-fences.d.mts +3 -1
  52. package/types/engine/content-alerts.d.mts +62 -0
  53. package/types/engine/content-blocks.d.mts +3 -10
  54. package/types/engine/content-embeds.d.mts +1 -1
  55. package/types/engine/content-figures.d.mts +13 -20
  56. package/types/engine/content-images.d.mts +1 -1
  57. package/types/engine/content-poetry.d.mts +32 -0
  58. package/types/engine/content-spans.d.mts +18 -0
  59. package/types/engine/holdings.d.mts +59 -105
  60. package/types/engine/markdown-expressions.d.mts +1 -1
  61. package/types/engine/note-vocabulary.d.mts +10 -2
  62. package/types/engine/populations.d.mts +4 -13
  63. package/types/engine/site-build.d.mts +3 -3
  64. package/types/engine/wikilinks.d.mts +5 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
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
+
3
14
  ## 22.26.0
4
15
 
5
16
  ### Minor Changes
package/README.md CHANGED
@@ -46,6 +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`, or explicit `null` for complete anarchy. Positive population with an omitted government produces an advisory. See [place governments](docs/authoring/frontmatter.md#place-governments).
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).
50
52
 
51
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
@@ -372,6 +372,7 @@ An addressed content note accepts exactly these top-level keys, in this order: `
372
372
  | `subTypeCharsetMessage` | `subTypeCharsetMessage(value)` | `string` | building the message for a `subType` outside the address charset |
373
373
  | `typeCharsetMessage` | `typeCharsetMessage(type)` | `string` | building the message for a `type` outside the address charset |
374
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 |
375
376
  | `dataFields` | `dataFields(type, vocabulary)` | `readonly DataFieldSpec[] \| undefined` | looking up the `data:` keys a note type may carry |
376
377
  | `subTypes` | `subTypes(type, vocabulary)` | `readonly string[] \| null \| undefined` | looking up the closed `subType` values a note type declares |
377
378
 
@@ -497,22 +498,24 @@ What a place is next to (`data.borders`) and reachable from (`data.routes`): the
497
498
 
498
499
  ### `engine.holdings`
499
500
 
500
- 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 population-based government advisory `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.
501
502
 
502
- | Export | Signature | Returns | Use it when |
503
- | ---------------------- | ------------------------------------ | ----------------------- | --------------------------------------------------------------------------------------------------------------------------- |
504
- | `HOLDINGS_KEYS` | `const HOLDINGS_KEYS` | `readonly string[]` | the three derived keys — `contains`, `held_by`, `holdings` — which a note cannot author |
505
- | `HELD_SUBTYPES` | `const HELD_SUBTYPES` | `readonly string[]` | deprecated historical tenure subtypes; government checks apply to every place subtype |
506
- | `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 |
507
- | `foreignHoldingsNodes` | `foreignHoldingsNodes(foreignIndex)` | `HoldingsNode[]` | reading a fetched index's places and affiliations as nodes |
508
- | `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 |
509
- | `shortcodesOf` | `shortcodesOf(value)` | `string[]` | the shortcodes a `parents` or `domains` value names — bare, addressed or wikilinked — empties dropped and repeats collapsed |
510
- | `checkGovernment` | `checkGovernment(note)` | `object[]` | warning at `data.population` for positive population with no own `data.government` key; explicit null means anarchy |
511
- | `checkHeld` | `checkHeld(note, { index })` | `object[]` | deprecated compatibility wrapper for `checkGovernment`; holdings do not determine government |
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 |
513
+
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.
512
515
 
513
516
  ### `engine.populations`
514
517
 
515
- 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.
516
519
 
517
520
  | Export | Signature | Returns | Use it when |
518
521
  | ----------------------- | ---------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------ |
@@ -700,7 +703,7 @@ invokes the check during frontmatter lint.
700
703
 
701
704
  ### `engine.markdownExpressions`
702
705
 
703
- `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.
704
707
 
705
708
  | Export | Signature | Returns | Use it when |
706
709
  | --------------------------- | ------------------------------------------ | ---------------------- | ------------------------------------------------ |
@@ -1964,3 +1967,20 @@ The Changesets changelog generator, loaded with `require` — a CommonJS module
1964
1967
  ## `./package.json`
1965
1968
 
1966
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.
@@ -41,7 +41,7 @@ One body describes the subject for both systems. A being's `{#appearance}` and `
41
41
 
42
42
  ## Place governments
43
43
 
44
- A place may state its governing affiliation in `data.government`. Use an affiliation Address, either a local shortcode or a fully qualified Address from this package or a fetched dependency:
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
45
 
46
46
  ```yaml
47
47
  data:
@@ -58,7 +58,7 @@ Write `government: null` for **complete anarchy**. Omitting the key leaves gover
58
58
  | Any | Explicit `null` | None; complete anarchy |
59
59
  | Any | Affiliation Address | None; the reference must resolve |
60
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. Holdings remain tenure facts; the former unconditional “unheld land” warning is retired.
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
62
 
63
63
  ## Publication and summaries
64
64
 
@@ -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 `:::figure` fence starts a JournalEntryPage too, in Foundry alone — the web and the book keep the fence in the flow of the page around it. A fence holding exactly one picture, captioned with plain text and nothing else, becomes a page of type `image`: the picture is its `src`, the caption is `image.caption`, and the page is named for the figure's own number, such as "Figure 3". A fence carrying no caption still takes the `image` type when it holds one picture, named the same way. Every other fence takes a page of type `text` instead — a grouped fence holding more than one picture, an audio embed, a fence whose caption carries emphasis, an icon, or a link, and a fence that shares its page with trailing prose before the next heading or fence.
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.
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
 
@@ -63,178 +63,130 @@ A `:::secret` block inside the section is legal and renders as it does anywhere
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
 
66
- ## Figures and numbered references
66
+ ## Captions and numbered references
67
67
 
68
- A `:::figure` fence numbers whatever it holds and, written with an id, makes it a target `{{ref}}` can point to:
68
+ Write a caption on its own line **before** the item it describes, with a blank line before and after the caption. `:` produces an unnumbered caption; `:@` produces a numbered caption. One or more spaces separate the marker from its text. An optional attribute block at the end accepts an identifier, classes and named attributes:
69
69
 
70
- ````markdown
71
- :::figure {#trade}
72
-
73
- ```sql
74
- SELECT name.full AS "Market" FROM notes WHERE type = 'place'
75
- ```
70
+ ```markdown
71
+ :@ Regional trade routes {#trade .border type=table}
76
72
 
77
- ///
78
- Regional trade routes
79
- :::
73
+ | Route | Days |
74
+ | ----- | ---- |
75
+ | North | 4 |
80
76
 
81
77
  Refer to {{ref "#trade"}} for the market routes.
82
- ````
78
+ ```
83
79
 
84
- The fence displays **Table 1: Regional trade routes**, and `{{ref "#trade"}}` displays **Table 1** as a link. **A figure is numbered because it is a fence, not because it carries caption text** — an ordinary table or picture written outside any fence is numbered not at all.
80
+ This displays **Table 1: Regional trade routes**; the reference links to **Table 1**. An unnumbered `: Regional trade routes {#trade}` displays only its text, has an anchor, and does not consume a number. An item with no caption remains unnumbered.
85
81
 
86
- **The construct is `:::figure`**, with an optional `{#id .border}` attribute block, closed by `:::`. Everything between the opening line and the closing `:::` is the captioned thing.
82
+ The caption attaches to exactly the next supported block:
87
83
 
88
- **The first `///` at the top level of the fence separates the captioned thing from its caption.** A second `///` at the top level is an error — a figure holds one caption. A `///` written inside a nested code fence is content, not a separator, so a captioned code listing can carry `///` as a comment of its own.
84
+ | Next item | Inferred type | Numbered label |
85
+ | ------------------------------------ | ------------- | -------------- |
86
+ | Markdown table | `table` | Table |
87
+ | Image, Markdown or wikilink | `figure` | Figure |
88
+ | SQL fence, including backtick SQL | `table` | Table |
89
+ | Poetry fence | `poetry` | Poem |
90
+ | Any other code fence | `code` | Code |
91
+ | Paragraph, quote block or fenced div | `prose` | Prose |
89
92
 
90
- **The caption is optional.** A fence with no `///` draws its label alone, with nothing under it. So the counter and the label come from the fence itself, whether or not an author writes a caption.
93
+ The optional `type` attribute overrides inference and accepts `figure`, `table`, `poetry`, `code`, `prose`, and `example`. `type=example` uses **Example 1**, **Example 2**, and so on. It does not allow an unsupported target. A heading, list, thematic break, another caption or end of document after a caption is an error. Caption markers without separating spaces or caption text are errors too.
91
94
 
92
- **`{#id}` is optional too.** A fence that declares none is still numbered and drawn; it carries no anchor, so nothing can refer to it. Give a figure an id only when prose needs to point at it.
95
+ Identifiers are optional and must be unique within the note. Classes are unrestricted; `.border` uses the shared border style, while other classes can be styled by the medium. Named attributes use `key=value`, with quoted values for text containing spaces. Use `#id` and `.class` rather than `id=` and `class=`; event-handler attributes are refused.
93
96
 
94
- **`.border` draws the figure and its label inside a border.** It is the only class the construct accepts — any other class, such as `.wide`, is an error naming the class. `key="value"` is refused outright: a figure takes an id and classes, nothing else.
97
+ Each numbered type has its own counter. Websites and Foundry number within a note; books number across their reading order. Unnumbered captions neither display nor consume a number. A map image can retain its asset-derived **Map** classification when no type is specified.
95
98
 
96
- **The counter is derived from the fence's contents, never authored.** A table is a table; an `sql` fence is a table, because its rendered output is one; any other fence or code block is code; pictures alone, one or several, are a figure; a `:::poetry` fence written first makes it a poem; everything else is prose. A fence around prose with `.border` is a numbered, referable boxed aside — the `Prose` counter's whole purpose. The counters are `Code`, `Table`, `Figure`, `Map`, `Poem`, and `Prose`. The website and a Foundry journal number each counter within the note; a book numbers each counter across the book in reading order.
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.
97
100
 
98
- **A picture counts as `Map` instead of `Figure` when the asset it draws declares `role: map`** in its provenance — see [the asset provenance reference](../reference/format-details.md). The fence's own markup cannot say so; a picture of a map looks like any other picture, so the fact rides entirely on the asset's own record. A fence grouping several pictures counts as `Map` only when every one of them declares the role; one picture without it keeps the whole fence a `Figure`.
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.
99
102
 
100
- **A reference is written `{{ref "#thorn"}}`**, rendering as a link reading "Figure 13". `form="full"` renders the number and the caption; `form="title"` renders the caption alone; `form="number"` is the default, and may be written explicitly. A link written inside the caption contributes only its label text to a `full` or `title` reference, because the reference is itself a link and an anchor cannot nest inside another.
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.
101
104
 
102
- **A reference may name another note's figure**, writing the note's address before the anchor: `{{ref "place-thornford#trade"}}` points at the figure `#trade` in `place-thornford`. It renders the number that figure carries **on the surface doing the rendering** — its own count within `place-thornford` on the website and in a Foundry journal, and its place in the book's reading order in the book, which may differ from the number it carries on its own page. An anchor matching no figure in the addressed note is refused, and so is an address naming no note at all.
105
+ ## Images and protected content
103
106
 
104
- In a Foundry journal, a reference resolves to the figure's own JournalEntryPage — the `image` page a solitary, plainly captioned picture becomes, or the `text` page any other fence becomes — addressed exactly as an authored `[[#anchor|Text]]` link to the same anchor would be. A reference naming another note's figure is not resolved there: a journal addresses a page by UUID, and has none for a note outside the one compiling.
107
+ An image or icon embed stands on its own line; one that does not is a lint error naming the line, because a width or a position means nothing applied to a word in the middle of a sentence. `size` accepts `auto`, `small`, `medium`, `large`, `xlarge` and `full-width`; the medium maps those names to suitable dimensions, and `full-width` is the full page in the book and the full content width elsewhere. `float` controls placement and takes `top-left`, `top-right`, `bottom-left`, `bottom-right` or `center`; on a page a float occupies the column measure, so only the vertical half of a corner position has an effect there. The asset remains an Address, so moving the file within its asset root does not change the link. See [assets](assets.md) and [image directives](../reference/format-details.md#images).
105
108
 
106
- **A figure needs contents and a closing `:::`.** Its id, when one is given, must be unique in the note; its caption, when one is given, is the one `///` section the fence carries.
109
+ ### Fenced divs
107
110
 
108
- A few worked examples. A captioned picture:
111
+ A bare `:::` opens a div section. An optional attribute block applies to the whole fenced area. A bare `:::` closes the section:
109
112
 
110
113
  ```markdown
111
- :::figure {#thorn}
112
- ![[being-foobar|The Great Beast]]
113
- ///
114
- The great beast, as drawn by [[person-havard|Havard]].
115
- :::
116
- ```
117
-
118
- This draws **Figure 1**, labelled "Figure 1: The great beast, as drawn by Havard." beneath the picture.
114
+ ::: {#harbor-note .border lang=en}
115
+ The harbor remains open through winter.
119
116
 
120
- A grouped fence holding two pictures as one figure — a plate rather than two separate plates:
121
-
122
- ```markdown
123
- :::figure {#passes}
124
- ![[image-northpass|Northern approach]]
125
- ![[image-southpass|Southern approach]]
126
- ///
127
- The two approaches to the pass.
117
+ A second paragraph belongs to the same div.
128
118
  :::
129
119
  ```
130
120
 
131
- Both pictures draw together under one label, **Figure 2**, because the fence is the unit the counter numbers — not the pictures inside it.
121
+ An attribute-bearing opener allows a nested div to be distinguished from the bare closer. Divs carry no automatic title or number; style their classes for the medium. Use a leading caption to caption the entire div. The same attribute grammar is available on named blocks; identifiers, classes and named attributes belong to the entire section. Headings that would start a Foundry journal page cannot split a fenced section.
132
122
 
133
- A fence with no caption:
123
+ ### Inline spans
124
+
125
+ `[inline content]` represents a span. Follow it immediately with an optional attribute block to apply an identifier, classes and named attributes:
134
126
 
135
127
  ```markdown
136
- :::figure {#anvil}
137
- ![[icon-anvil|A worn anvil]]
138
- :::
128
+ The watch wears [a _blue_ sash]{#watch-sash .uniform lang=en}.
139
129
  ```
140
130
 
141
- This draws **Figure 3**, with no caption line beneath it, and still carries the anchor `#anvil`.
131
+ Inline Markdown remains active inside the span. Bracket spans have lower priority than URL links, image links, reference links, footnotes and wikilinks. A bracketed form immediately preceded by `!`, followed by `(` or another `[`, or beginning with `[[` is not a span, even when the link is unresolved. Ordinary links retain their existing syntax. Escape a bracket when it should be literal, or put examples inside a code span or fence. A span identifier addresses that point in the note; it does not start a journal page.
142
132
 
143
- A reference in running prose:
133
+ ### Alerts
134
+
135
+ Alerts replace the former `:::info` and `:::warn` fences. Start with a special blockquote marker, optionally followed by an attribute block, then write the alert body as a standard blockquote:
144
136
 
145
137
  ```markdown
146
- See {{ref "#thorn"}} for the plate, or {{ref "#thorn" form="full"}} for the full description.
147
- ```
138
+ > [!NOTE]
139
+ > Useful information that users should know, even when skimming content.
148
140
 
149
- This displays as "See [Figure 1](#thorn) for the plate, or [Figure 1: The great beast, as drawn by Havard.](#thorn) for the full description," each bracketed phrase a link to the figure.
141
+ > [!TIP]
142
+ > Helpful advice for doing things better or more easily.
150
143
 
151
- A reference to a figure in another note:
144
+ > [!IMPORTANT]
145
+ > Key information users need to know to achieve their goal.
152
146
 
153
- ```markdown
154
- The market routes are drawn in {{ref "place-thornford#trade"}}.
147
+ > [!WARNING]
148
+ > Urgent info that needs immediate user attention to avoid problems.
155
149
  ```
156
150
 
157
- On the website this links into `place-thornford`'s own page, reading "Table 1" — the number that fence carries there, whatever number it carries in the book or in a note of its own.
158
-
159
- ## Images and protected content
160
-
161
- An image or icon embed stands on its own line; one that does not is a lint error naming the line, because a width or a position means nothing applied to a word in the middle of a sentence. `size` accepts `auto`, `small`, `medium`, `large`, `xlarge` and `full-width`; the medium maps those names to suitable dimensions, and `full-width` is the full page in the book and the full content width elsewhere. `float` controls placement and takes `top-left`, `top-right`, `bottom-left`, `bottom-right` or `center`; on a page a float occupies the column measure, so only the vertical half of a corner position has an effect there. The asset remains an Address, so moving the file within its asset root does not change the link. See [assets](assets.md) and [image directives](../reference/format-details.md#images).
151
+ | Marker | Label | Appearance |
152
+ | -------------- | --------- | ------------------------------------------------- |
153
+ | `[!NOTE]` | Note | Blue rule and `fa-regular fa-message` |
154
+ | `[!TIP]` | Tip | Green rule and `fa-regular fa-lightbulb` |
155
+ | `[!IMPORTANT]` | Important | Purple rule and `fa-solid fa-circle-info` |
156
+ | `[!WARNING]` | Warning | Amber rule and `fa-solid fa-triangle-exclamation` |
162
157
 
163
- ### Named blocks
158
+ The label and icon sit above the body. Alerts are visible callouts, not expandable sections. Inline Markdown, links, multiple paragraphs, lists and code examples remain available inside the quote. Prefix blank lines within the body with `>` to keep paragraphs in the same alert.
164
159
 
165
- Three named blocks set a passage apart from the prose around it. `:::secret`
166
- marks GM material, `:::info` a neutral note, and `:::warn` a caution:
160
+ Attributes follow the marker and apply to the alert:
167
161
 
168
162
  ```markdown
169
- :::warn
170
- The bridge closes during the spring flood.
171
- :::
163
+ > [!WARNING] {#flood-warning .seasonal lang=en title="Seasonal closure"}
164
+ > The eastern road closes during spring floods.
165
+ >
166
+ > Use the ridge path instead.
172
167
  ```
173
168
 
174
- Each heads itself with its own name — **Secret**, **Info**, **Warn** — and takes
175
- a closing `:::`. Poetry has its own fence below; other names are reported.
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
+
171
+ Migrate former info blocks to `[!NOTE]` alerts and warn blocks to `[!WARNING]` alerts. The old named fences are errors. `:::secret` remains supported.
176
172
 
177
- **A GM-only section holds a box.** A `:::secret` is a container for whatever the
178
- GM reads, boxes included, and a box written inside one stays inside it. A box
179
- holds no named block of its own, so a second box inside a box is reported. A
180
- `:::figure` fence may be written inside any of them. The two draw a box for
181
- different reasons: a named block says what kind of aside a passage is, while a
182
- `:::figure` fence makes a passage numbered and referable — nesting one inside
183
- the other gives a passage both.
173
+ ### Secret blocks
184
174
 
185
- **An attribute block follows the name.** It is the same braced grammar the other
186
- body extensions use, so it carries an id, classes, and any attribute the element
187
- should have. `title` is reserved and supplies the heading in place of the default:
175
+ `:::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**:
188
176
 
189
177
  ```markdown
190
- :::secret {#harbor title="For the GM" .wide data-stage="two"}
178
+ :::secret {#harbor-secret title="For the GM"}
191
179
  The harbor master is working for the smugglers.
192
180
  :::
193
181
  ```
194
182
 
195
- That block carries the classes `secret wide`, the id `harbor`, and
196
- `data-stage="two"`, and it heads **For the GM**. A title carries emphasis of its
197
- own — `title="The *Genzet* only"` — and nothing else: a tag written there is
198
- shown rather than obeyed. Set the id with `#id` and classes with `.class` rather
199
- than through an `id=` or `class=` key, and note that an event handler such as
200
- `onclick=` is refused rather than written.
201
-
202
- **The name is the first class on the element, and the appearance belongs to a
203
- stylesheet.** No block is emitted with an inline `style`, so a package restyles
204
- one from its own sheet. `info` and `warn` are styled by the base stylesheet;
205
- `secret` is left to Foundry, which styles `section.secret` itself and owns the
206
- reveal control on it.
207
-
208
- Each surface renders the same block its own way:
209
-
210
- | Surface | Output |
211
- | ------- | ---------------------------------------------------------------------------------- |
212
- | Foundry | `<section class="secret" id="harbor">` with the title as its first line |
213
- | Web | `<details class="secret" id="harbor">` the reader opens, titled in its `<summary>` |
214
- | Book | a coloured print box headed by the title |
215
-
216
- A `secret` with no id of its own is given one derived from its body on the
217
- Foundry surface, because Foundry remembers a revealed section by its id.
218
-
219
- **A secret is a presentation distinction, not access control.** Players who
220
- deliberately inspect source or generated output may see it. The
221
- [detailed reference](../reference/format-details.md#what-a-note-produces)
222
- describes each medium's handling.
223
-
224
- **One malformed block does not silence the others.** A block whose attributes do
225
- not parse is reported at its own line and left as written; every well-formed
226
- block in the same note still renders.
227
-
228
- **A heading that would start its own journal page cannot be written inside a
229
- block, or inside a `:::figure` fence.** An H1, or a heading of any level
230
- carrying an `{#anchor}`, is reported at its own line there — starting a page
231
- would tear the block or the figure in two, and a reader could open the second
232
- half with none of the box around it. A lower heading with no anchor is
233
- unaffected and is the ordinary way to structure a long box.
183
+ A secret block can contain alerts and general divs. It cannot contain another secret block. A heading that starts its own journal page cannot split a secret block or general div; use a lower heading without an anchor within the section.
184
+
185
+ Foundry renders a secret section; the website renders an expandable spoiler, and the book renders a labeled box. An untitled secret takes its normal default title, and a Foundry secret without an id receives an id derived from its body. The presentation does not make published HTML private: deliberately inspecting generated source can reveal its content. Use YAML comments for information that must never be published.
234
186
 
235
187
  ### Poetry
236
188
 
237
- Use `:::poetry` to preserve verse lines and stanzas. Every nonblank source line
189
+ Use a code fence with the language `poetry` to preserve verse lines and stanzas. Every nonblank source line
238
190
  is one verse line; blank lines separate stanzas. Inline emphasis, links and
239
191
  footnotes work as they do in prose. The fence itself adds no title, caption or
240
192
  number.
@@ -263,15 +215,15 @@ render at levels zero and `i1` respectively. This also works when the entire
263
215
  fence is indented inside a list item. Poetry is upright by default; use normal
264
216
  Markdown emphasis when italics are part of the poem.
265
217
 
266
- ```markdown
267
- :::poetry {form=ballad meter="common meter" rhyme=ABCB syllables="8,6,8,6" lang=en}
218
+ ````markdown
219
+ ```poetry {form=ballad meter="common meter" rhyme=ABCB syllables="8,6,8,6" lang=en}
268
220
  The lantern burns beside the gate,
269
221
  The harbor sleeps below.
270
222
 
271
223
  The keeper guards the road till dawn,
272
224
  And keeps a light aglow.
273
- :::
274
225
  ```
226
+ ````
275
227
 
276
228
  The optional `form`, `meter` and `rhyme` attributes record the author's intent;
277
229
  the build does not infer stress, rhyme or pronunciation. `syllables` gives a
@@ -281,23 +233,18 @@ spoken. `lang` is a language tag for the rendered HTML. An optional `#id`
281
233
  addresses the poem itself. There are no `title` or `lines` attributes, and no
282
234
  special punctuation for stress, pauses or refrains.
283
235
 
284
- To number and caption a poem, put it first inside a `:::figure`:
236
+ To number and caption a poem, put a leading numbered caption before the poetry fence:
285
237
 
286
- ```markdown
287
- :::figure {#watch-song}
288
- :::poetry {form=ballad}
238
+ ````markdown
239
+ :@ A song sung by the harbor watch. {#watch-song}
240
+
241
+ ```poetry {form=ballad}
289
242
  The watch has gone to sea,
290
243
  The lantern marks the shore.
291
- :::
292
- ///
293
- A song sung by the harbor watch.
294
- :::
295
244
  ```
245
+ ````
296
246
 
297
- The inner `:::` closes the poem; the outer one closes the figure. That figure
298
- uses the **Poem** counter, so `{{ref "#watch-song"}}` reads **Poem 1**. The
299
- caption after `///` belongs to the figure. A poem outside a figure is
300
- unnumbered. Prose written before the poem makes the figure a **Prose** figure.
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.
301
248
 
302
249
  ## Footnotes and definition lists
303
250
 
@@ -471,21 +418,21 @@ There are {{words (sql "SELECT COUNT(*) FROM notes WHERE type = 'being'")}} bein
471
418
 
472
419
  <!-- expression-helpers:start -->
473
420
 
474
- | Helper | Parameters | What it gives you |
475
- | ------------ | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
476
- | `eq` | `a b` | True when both are the same value. Compares exactly, so 1 and "1" differ. |
477
- | `gt` | `a b` | True when a is greater than b. Both must be finite numbers. |
478
- | `gte` | `a b` | True when a is greater than or equal to b. Both must be finite numbers. |
479
- | `lt` | `a b` | True when a is less than b. Both must be finite numbers. |
480
- | `lte` | `a b` | True when a is less than or equal to b. Both must be finite numbers. |
481
- | `not` | `value` | True when the value is false, zero, empty or absent. |
482
- | `and` | `value …` | True when every value given is true. Takes two or more. |
483
- | `or` | `value …` | True when any value given is true. Takes two or more. |
484
- | `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. |
485
- | `digits` | `number` | The number as a grouped numeral — `1200` becomes 1,200, and a fractional part is kept. |
486
- | `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. |
487
- | `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. |
488
- | `ref` | `address [form="number"\|"full"\|"title"]` | A link to a `:::figure` fence, 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. |
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. |
489
436
 
490
437
  <!-- expression-helpers:end -->
491
438