@heroiclands/package-build 22.22.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.
- package/CHANGELOG.md +32 -0
- package/bin/content-commands.mjs +51 -14
- package/ci/content-format-reference.mjs +1 -1
- package/content-config.mjs +80 -2
- package/docs/api.md +85 -72
- package/docs/authoring/assets.md +14 -0
- package/docs/authoring/dates-and-calendars.md +37 -1
- package/docs/authoring/links-and-markup.md +112 -31
- package/docs/commands.md +12 -10
- package/docs/configuration.md +90 -13
- package/docs/content-format.md +1 -1
- package/docs/guides/book.md +5 -4
- package/docs/reference/format-details.md +323 -60
- package/docs/reference/note-types.md +9 -9
- package/engine/anchors.mjs +16 -10
- package/engine/art-fields.mjs +118 -0
- package/engine/art-slots.mjs +7 -1
- package/engine/asset-index.mjs +160 -10
- package/engine/asset-replacement.mjs +93 -0
- package/engine/asset-types.mjs +240 -0
- package/engine/base-compiler.mjs +54 -2
- package/engine/bundles.mjs +13 -1
- package/engine/content-blocks.mjs +4 -4
- package/engine/content-embeds.mjs +19 -0
- package/engine/content-figures.mjs +468 -0
- package/engine/content-footnotes.mjs +10 -2
- package/engine/content-format.yaml +1 -1
- package/engine/content-images.mjs +126 -25
- package/engine/content-index.mjs +36 -10
- package/engine/field-reference.mjs +31 -10
- package/engine/field-spec.mjs +10 -8
- package/engine/foreign-catalog.mjs +143 -18
- package/engine/frontmatter-lint.mjs +73 -1
- package/engine/heading-attributes.mjs +31 -18
- package/engine/helpers.mjs +97 -30
- package/engine/infobox-registry.mjs +4 -1
- package/engine/infobox.mjs +47 -8
- package/engine/itinerary-scenes.mjs +4 -10
- package/engine/journals.mjs +264 -39
- package/engine/lore-events.mjs +197 -0
- package/engine/markdown-expressions.mjs +257 -7
- package/engine/note-dates.mjs +305 -12
- package/engine/note-vocabulary.mjs +11 -5
- package/engine/pathnames.mjs +36 -0
- package/engine/pdf-build.mjs +357 -34
- package/engine/pdf-images.mjs +64 -0
- package/engine/pdf-render.mjs +160 -36
- package/engine/scenes.mjs +16 -4
- package/engine/site-build.mjs +127 -13
- package/engine/site-index.mjs +72 -1
- package/engine/site-preview.mjs +2 -0
- package/engine/system-block.mjs +44 -16
- package/hm3/actors.mjs +2 -2
- package/manifest.mjs +30 -24
- package/package.json +1 -1
- package/packagebuild-metadata.jsonl +13 -13
- package/sohl/item-fields.mjs +4 -3
- package/types/content-config.d.mts +48 -1
- package/types/engine/anchors.d.mts +9 -4
- package/types/engine/art-fields.d.mts +64 -0
- package/types/engine/asset-index.d.mts +43 -0
- package/types/engine/asset-replacement.d.mts +22 -0
- package/types/engine/asset-types.d.mts +18 -0
- package/types/engine/base-compiler.d.mts +0 -17
- package/types/engine/content-embeds.d.mts +16 -0
- package/types/engine/content-figures.d.mts +101 -0
- package/types/engine/content-footnotes.d.mts +7 -0
- package/types/engine/content-images.d.mts +78 -12
- package/types/engine/content-index.d.mts +8 -2
- package/types/engine/field-reference.d.mts +21 -0
- package/types/engine/field-spec.d.mts +20 -16
- package/types/engine/foreign-catalog.d.mts +39 -0
- package/types/engine/heading-attributes.d.mts +13 -8
- package/types/engine/helpers.d.mts +23 -7
- package/types/engine/infobox-registry.d.mts +4 -1
- package/types/engine/infobox.d.mts +6 -0
- package/types/engine/journals.d.mts +31 -9
- package/types/engine/lore-events.d.mts +14 -0
- package/types/engine/markdown-expressions.d.mts +23 -2
- package/types/engine/note-dates.d.mts +64 -1
- package/types/engine/pathnames.d.mts +26 -0
- package/types/engine/pdf-build.d.mts +28 -6
- package/types/engine/pdf-images.d.mts +52 -0
- package/types/engine/pdf-render.d.mts +12 -0
- package/types/engine/site-build.d.mts +14 -3
- package/types/engine/site-index.d.mts +54 -0
- package/types/engine/system-block.d.mts +26 -2
- package/types/hm3/actors.d.mts +18 -0
- package/types/manifest.d.mts +19 -23
- package/engine/content-captions.mjs +0 -230
- package/types/engine/content-captions.d.mts +0 -58
|
@@ -352,7 +352,7 @@ order without changing the Markdown body.
|
|
|
352
352
|
|
|
353
353
|
#### Dates and calendars
|
|
354
354
|
|
|
355
|
-
A canonical date is `<year>[.<day>[:HHMMSS]]`. The day is one-based within the world's year. `720` covers the whole year; `720.136` covers that day; `720.136:143005` identifies one second. Negative years are valid. Prefix either form with `~` to express uncertainty beyond its written interval. `unknown` is allowed only in fields that accept an unknown occurrence.
|
|
355
|
+
A canonical date is `<year>[.<day>[:HHMMSS]]`. The day is one-based within the world's year, bound against `data.year.days` on the world's `place` note. A tree whose notes declare no world year bounds no day: the value is read as written, however large. `720` covers the whole year; `720.136` covers that day; `720.136:143005` identifies one second. Negative years are valid. Prefix either form with `~` to express uncertainty beyond its written interval. `unknown` is allowed only in fields that accept an unknown occurrence.
|
|
356
356
|
|
|
357
357
|
**Quote a canonical date that states a day.** `born: "667.130"` is read as written; unquoted, YAML parses the scalar as a number and a trailing zero on the day is gone before any date reader sees it, so `667.130` and `667.13` arrive as one value. A number-valued date is an error wherever appending a zero to its day would still name a day inside the world's year — the message names the file, the line and both readings — and is read as written where no such day exists, since `675.2810` is no day of a 365-day year. A bare year loses nothing and needs no quotes.
|
|
358
358
|
|
|
@@ -701,12 +701,12 @@ exactly as `seat` declares `place` — a bare shortcode takes its type from the
|
|
|
701
701
|
declaration, and qualification climbs the same short-form ladder every other
|
|
702
702
|
link uses.
|
|
703
703
|
|
|
704
|
-
| field | default type | accepts | resolves into
|
|
705
|
-
| ----------- | ------------ | --------------- |
|
|
706
|
-
| `icon` | `icon` | `icon`, `image` | `document.img`
|
|
707
|
-
| `tokenIcon` | `icon` | `icon`, `image` | `prototypeToken.texture.src`
|
|
708
|
-
| `bgImage` | `image` | `icon`, `image` | `background.src`
|
|
709
|
-
| `banner` | `image` | `icon`, `image` | the site hero image
|
|
704
|
+
| field | default type | accepts | resolves into | on |
|
|
705
|
+
| ----------- | ------------ | --------------- | ----------------------------- | ----------------------------------------- |
|
|
706
|
+
| `icon` | `icon` | `icon`, `image` | `document.img` | every Actor/Item type, and embedded items |
|
|
707
|
+
| `tokenIcon` | `icon` | `icon`, `image` | `prototypeToken.texture.src` | Actor types |
|
|
708
|
+
| `bgImage` | `image` | `icon`, `image` | `background.src` | map types |
|
|
709
|
+
| `banner` | `image` | `icon`, `image` | the site hero image, 1792×768 | any note; not a Foundry field |
|
|
710
710
|
|
|
711
711
|
All four are authored under `data:`:
|
|
712
712
|
|
|
@@ -750,7 +750,7 @@ noticed:
|
|
|
750
750
|
| `packFolder` | `Address` or pack map | The shared compendium folder Address. |
|
|
751
751
|
| `harnworld` | `Map<string, unknown>` | HârnWorld source details. |
|
|
752
752
|
| `icon` | `Address` | The document's profile art — an `icon` address, resolved into `img`. |
|
|
753
|
-
| `banner` | `Address` | The page's hero image — an `image` address.
|
|
753
|
+
| `banner` | `Address` | The page's hero image — an `image` address, cut to 1792×768. |
|
|
754
754
|
|
|
755
755
|
`icon` is legal everywhere because most types compile into a document that
|
|
756
756
|
carries one; where a type's passes emit none, the lint says so and the value is
|
|
@@ -853,12 +853,12 @@ ships as the Foundry module `harn-ensemble`) writing `images/map.webp`:
|
|
|
853
853
|
And for the same note writing `sohl/assets/icons/noun/shield.svg`, a file the
|
|
854
854
|
system ships and this repository does not hold:
|
|
855
855
|
|
|
856
|
-
| Surface | Address
|
|
857
|
-
| ----------- |
|
|
858
|
-
| **Foundry** | `systems/sohl/assets/icons/noun/shield.svg`
|
|
859
|
-
| **Local** | `assets/icons/noun/shield.svg`, in the `sohl` repository
|
|
860
|
-
| **Web** | `https://cdn.heroiclands.org/sohl/icons/noun/shield.svg`
|
|
861
|
-
| **Book** | not carried
|
|
856
|
+
| Surface | Address |
|
|
857
|
+
| ----------- | ------------------------------------------------------------- |
|
|
858
|
+
| **Foundry** | `systems/sohl/assets/icons/noun/shield.svg` |
|
|
859
|
+
| **Local** | `assets/icons/noun/shield.svg`, in the `sohl` repository |
|
|
860
|
+
| **Web** | `https://cdn.heroiclands.org/sohl/icons/noun/shield.svg` |
|
|
861
|
+
| **Book** | not carried, unless a declared replacement carries it instead |
|
|
862
862
|
|
|
863
863
|
**The content package name and Foundry id are separate values.**
|
|
864
864
|
`harnensemble` is what the content is called, what a note writes, and what the
|
|
@@ -884,6 +884,29 @@ index enabled, an asset with no matching note is a located build error. See
|
|
|
884
884
|
[asset bindings](../configuration.md#packagebuildstagedir-and-packagebuildassets)
|
|
885
885
|
for the configuration and cache requirements.
|
|
886
886
|
|
|
887
|
+
**A package declared as a replacement is carried into the book, from its
|
|
888
|
+
fetched archive, when it answers the address; every other foreign package
|
|
889
|
+
stays not carried.** A relationship declaring `assetReplacement: true` names a
|
|
890
|
+
dependency whose asset tree answers this package's own addresses first — see
|
|
891
|
+
[`relationships`](../configuration.md#relationships) — and the book is the one
|
|
892
|
+
surface that otherwise refuses a foreign file outright, so it is the one
|
|
893
|
+
surface this changes. A `thalorna` note embedding
|
|
894
|
+
`![[thalornaaltart-none-icon-anvil|An anvil]]` names `thalornaaltart`'s own
|
|
895
|
+
address, exactly as a note citing `sohl`'s shared bestiary art would; when
|
|
896
|
+
`thalornaaltart` is declared with `assetReplacement: true` and its fetched
|
|
897
|
+
archive carries `icon-anvil` under its own package, the book stages that file
|
|
898
|
+
from the archive instead of printing the caption with no picture. A note
|
|
899
|
+
citing a different package's own address this way — `sohl`'s bestiary art,
|
|
900
|
+
say — stays refused exactly as the row above states, because `sohl` was never
|
|
901
|
+
declared as a replacement: a package answers in place of another only when it
|
|
902
|
+
opted in, never because its archive happens to carry a file at the same type
|
|
903
|
+
and shortcode.
|
|
904
|
+
|
|
905
|
+
A relationship declaring `assetReplacement: true` whose archive was never
|
|
906
|
+
fetched is a configuration mistake, not an ordinary unresolved address: the
|
|
907
|
+
book reports a located error naming the relationship and instructing
|
|
908
|
+
`package-build deps fetch`, rather than printing a caption with no picture.
|
|
909
|
+
|
|
887
910
|
**The host is configuration.** `site.assets` in `package-build.config.yaml` is
|
|
888
911
|
the root the web form is joined onto, and a package-owned image on a page with
|
|
889
912
|
none set is an error naming that key.
|
|
@@ -927,6 +950,18 @@ Foundry destination: searching a built `packs-json` tree for it turns up
|
|
|
927
950
|
nothing. It reaches the generated page and the book's section plates, and
|
|
928
951
|
nothing else.
|
|
929
952
|
|
|
953
|
+
**A hero image is 1792×768.** It fills one fixed strip wherever it is drawn, so
|
|
954
|
+
every banner is cut to that size and a picture of any other size is a finding
|
|
955
|
+
naming both sizes — resize the picture to match. The size is stated once, by the
|
|
956
|
+
slot itself, which is what gives an artist a figure to work to before any file
|
|
957
|
+
exists.
|
|
958
|
+
|
|
959
|
+
No other art slot states a size, and none of them asks anything of a picture's
|
|
960
|
+
dimensions. An `icon` is drawn at a nominal size per medium whatever its file
|
|
961
|
+
holds; a map's `bgImage` sets its own scene's dimensions, so it is whatever size
|
|
962
|
+
the map is; and a picture written in prose is fitted to the room it has. Those
|
|
963
|
+
three accept whatever they are given.
|
|
964
|
+
|
|
930
965
|
It is an `Address` all the same, defaulting to type `image`, so a section note
|
|
931
966
|
writes `banner: skillbnr` and a note borrowing another package's plate writes
|
|
932
967
|
`banner: packagebuild-none-image-skillbnr`. The resolved record carries the
|
|
@@ -1685,14 +1720,17 @@ carries no frontmatter, no anchors and no `foundry` block: a file declares
|
|
|
1685
1720
|
nothing about itself, compiles into no document, and publishes no page, so the
|
|
1686
1721
|
`address` holds the canonical key and no page slug.
|
|
1687
1722
|
|
|
1688
|
-
| `asset` field | Source | What it says
|
|
1689
|
-
| ------------- | ---------- |
|
|
1690
|
-
| `path` | the walk | Where the file sits inside the emitting package's asset directory.
|
|
1691
|
-
| `attribution` | provenance | The person holding the rights, to whom attribution is legally due.
|
|
1692
|
-
| `source` | provenance | Where it came from — a URL, or a sentence.
|
|
1693
|
-
| `ai` | provenance | Whether the file is machine-generated — `true` or `false`.
|
|
1694
|
-
| `license` | provenance | The licence it is used under — an SPDX identifier, or terms.
|
|
1695
|
-
| `notes` | provenance | Anything else a person reading the attribution needs.
|
|
1723
|
+
| `asset` field | Source | What it says |
|
|
1724
|
+
| ------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
1725
|
+
| `path` | the walk | Where the file sits inside the emitting package's asset directory. |
|
|
1726
|
+
| `attribution` | provenance | The person holding the rights, to whom attribution is legally due. |
|
|
1727
|
+
| `source` | provenance | Where it came from — a URL, or a sentence. |
|
|
1728
|
+
| `ai` | provenance | Whether the file is machine-generated — strictly the YAML boolean `true` or `false`; any other value is a finding, never a stringified record. Unstated resolves to `false`. |
|
|
1729
|
+
| `license` | provenance | The licence it is used under — an SPDX identifier, or terms. |
|
|
1730
|
+
| `notes` | provenance | Anything else a person reading the attribution needs. |
|
|
1731
|
+
| `role` | provenance | What the picture is for — `portrait`, `emblem`, `banner`, `plate` or `map`. Absent for an ordinary picture; a value outside that set is a finding. The `image` type only — a value on an `icon` address is a finding and is dropped. |
|
|
1732
|
+
| `width` | the walk | The file's pixel width, read from its own header. Blank for an SVG, which has no pixel dimensions. |
|
|
1733
|
+
| `height` | the walk | The file's pixel height, read from its own header. Blank for an SVG, which has no pixel dimensions. |
|
|
1696
1734
|
|
|
1697
1735
|
Every field is present on every record, blank where nothing states one. A fixed
|
|
1698
1736
|
shape is what lets a consumer read `asset.license` without first asking whether
|
|
@@ -1711,6 +1749,90 @@ joins its own root onto it:
|
|
|
1711
1749
|
Foundry paths use the **Foundry package id**. The two values are both `thalorna`
|
|
1712
1750
|
for this image; `harnensemble` content uses `harn-ensemble` in its Foundry paths.
|
|
1713
1751
|
|
|
1752
|
+
##### What a role declares
|
|
1753
|
+
|
|
1754
|
+
`role` names what the picture is for, from a closed set:
|
|
1755
|
+
|
|
1756
|
+
| Role | What it names |
|
|
1757
|
+
| ---------- | -------------------------------------------------------- |
|
|
1758
|
+
| `portrait` | A picture of a subject — a being, a building, an object. |
|
|
1759
|
+
| `emblem` | A device, arms, a sigil — a mark rather than a scene. |
|
|
1760
|
+
| `banner` | A wide strip across the head of a page or a book plate. |
|
|
1761
|
+
| `plate` | A full-measure illustration meant to be looked at. |
|
|
1762
|
+
| `map` | A plan or chart, read by examining its detail. |
|
|
1763
|
+
|
|
1764
|
+
An asset declaring no role is an ordinary picture: there is no default role
|
|
1765
|
+
name, only the absence of one. A value outside this set is refused with a
|
|
1766
|
+
finding naming the file, the key and the accepted values, and the record
|
|
1767
|
+
carries no role rather than the one it was given. `role` belongs to the
|
|
1768
|
+
`image` type only — an `icon` address carries one nominal size per medium
|
|
1769
|
+
whatever the file holds, so a `role` declared there is a finding and is
|
|
1770
|
+
dropped rather than carried through.
|
|
1771
|
+
|
|
1772
|
+
A declared role makes two checks possible. **Shape**, an error, flags an
|
|
1773
|
+
asset whose aspect ratio departs from the median of every other asset
|
|
1774
|
+
sharing its role — the expectation is derived from the role-sharing group
|
|
1775
|
+
itself at build time, never from a second, hand-kept table. **Resolution**,
|
|
1776
|
+
a warning, flags an asset whose pixel width falls short of what its role's
|
|
1777
|
+
largest print slot needs. Both exempt a vector asset, which carries no pixel
|
|
1778
|
+
dimensions and no aspect that survives being drawn at a nominal size.
|
|
1779
|
+
|
|
1780
|
+
`role: map` also selects a `:::figure` fence's counter — see
|
|
1781
|
+
[figures and numbered references](../authoring/links-and-markup.md#figures-and-numbered-references).
|
|
1782
|
+
A fence whose picture draws an address with that role counts as `Map` rather
|
|
1783
|
+
than `Figure`, on every surface that numbers it, because the role is the one
|
|
1784
|
+
fact the fence's own markup cannot state.
|
|
1785
|
+
|
|
1786
|
+
##### What a role draws at
|
|
1787
|
+
|
|
1788
|
+
A role names what a picture is for; it says nothing about how large one
|
|
1789
|
+
draws. Deciding that is a medium's own job — the book owns a table mapping
|
|
1790
|
+
each role to a maximum, and a website's or a Foundry system's stylesheet owns
|
|
1791
|
+
the equivalent table for its own surface, keyed off the role class and the
|
|
1792
|
+
`width`/`height` attributes the emitted markup carries (blank for a vector).
|
|
1793
|
+
Changing a role's slot is one line in that table, not an edit to every note
|
|
1794
|
+
drawing one.
|
|
1795
|
+
|
|
1796
|
+
**Every size is a maximum, never a target.** The drawn width is the smaller of
|
|
1797
|
+
the role's slot and what the file's own pixels support at the medium's print
|
|
1798
|
+
floor:
|
|
1799
|
+
|
|
1800
|
+
> drawn width = min(the role's slot, natural pixels ÷ the print floor)
|
|
1801
|
+
|
|
1802
|
+
A picture is never drawn larger than it was made: a 512px portrait stretched
|
|
1803
|
+
across the slot a `plate` fills is a soft rectangle that reads as broken. A
|
|
1804
|
+
vector carries no pixel count, so nothing narrows its role's slot; it always
|
|
1805
|
+
draws at the slot. An `icon` address takes the medium's nominal icon size
|
|
1806
|
+
whatever role it might otherwise have had — `role` never reaches one, since
|
|
1807
|
+
the asset record refuses it there.
|
|
1808
|
+
|
|
1809
|
+
The book's table, and the print floor it reads from the same constant its page
|
|
1810
|
+
geometry is set against (300 dpi):
|
|
1811
|
+
|
|
1812
|
+
| Role | The book's slot |
|
|
1813
|
+
| --------------------------- | --------------------------------- |
|
|
1814
|
+
| `portrait`, `emblem` | half the page's text measure |
|
|
1815
|
+
| `banner`, `plate`, `map` | the full page's text measure |
|
|
1816
|
+
| an `icon` address, any role | one inch, whatever the file holds |
|
|
1817
|
+
|
|
1818
|
+
On this book's US Letter page the text measure is about 7.00in, so a
|
|
1819
|
+
`portrait` or an `emblem` draws at up to about 3.50in and a `banner`, a
|
|
1820
|
+
`plate` or a `map` draws at up to about 7.00in — still the smaller of that
|
|
1821
|
+
and what the file's pixels support.
|
|
1822
|
+
|
|
1823
|
+
**A worked example.** A `portrait`-role file measuring 900×1350px, drawn with
|
|
1824
|
+
no named `size=`: at 300 dpi its pixels support 900 ÷ 300 = 3.00in, which is
|
|
1825
|
+
narrower than the role's 3.50in slot, so the book draws it at 3.00in. The same
|
|
1826
|
+
picture at 1200×1800px supports 4.00in, wider than the slot, so the book draws
|
|
1827
|
+
it at its 3.50in slot instead — the slot bounds it, exactly as a maximum
|
|
1828
|
+
should. Either way the emitted markup for the website and for a Foundry
|
|
1829
|
+
journal carries `note-image-role-portrait` as a class and `900`/`1350` (or
|
|
1830
|
+
`1200`/`1800`) as its `width`/`height` attributes, which is what a stylesheet
|
|
1831
|
+
on either surface sizes a `portrait` from.
|
|
1832
|
+
|
|
1833
|
+
A named `size=` written beside the embed overrides a role's slot outright, the
|
|
1834
|
+
same as it always has — a role decides the size nothing else states.
|
|
1835
|
+
|
|
1714
1836
|
##### Where provenance comes from
|
|
1715
1837
|
|
|
1716
1838
|
A `provenance.yaml` states the provenance fields in the table above, and nothing
|
|
@@ -1738,9 +1860,40 @@ resolving a chain by hand.
|
|
|
1738
1860
|
That is also why `attribution` and `license` are **required in any record that
|
|
1739
1861
|
exists**: a key left out is not inherited from above, it is simply absent, so a
|
|
1740
1862
|
record stating neither leaves every file it covers with no rights holder and no
|
|
1741
|
-
terms. Omitting one is a finding. `source`, `ai` and `
|
|
1863
|
+
terms. Omitting one is a finding. `source`, `ai`, `notes` and `role` stay optional,
|
|
1742
1864
|
because a blank is a truthful answer for each of them.
|
|
1743
1865
|
|
|
1866
|
+
##### A declared replacement resolves first
|
|
1867
|
+
|
|
1868
|
+
A package may declare, on a `relationships.<kind>[]` entry, that a
|
|
1869
|
+
dependency's asset tree answers this package's own asset addresses ahead of
|
|
1870
|
+
its local record — `assetReplacement: true` in `package-build.config.yaml`.
|
|
1871
|
+
Once declared, an asset address resolves in this order:
|
|
1872
|
+
|
|
1873
|
+
1. **Every declared replacement, in declaration order.** The package segment
|
|
1874
|
+
of each relationship declaring `assetReplacement: true` is tried in the
|
|
1875
|
+
order those relationships are written, and the first one carrying the
|
|
1876
|
+
address wins.
|
|
1877
|
+
2. **Falling through to the package's own record** when no declared
|
|
1878
|
+
replacement carries the address — the resolution this package already
|
|
1879
|
+
performs with no `assetReplacement` declared at all.
|
|
1880
|
+
|
|
1881
|
+
**This reaches asset types only.** A being, an item or a lore note is never
|
|
1882
|
+
replaced this way, whatever a relationship declares: the rule tests the
|
|
1883
|
+
address's type, not the relationship. A replacement answers only `icon`,
|
|
1884
|
+
`image` and `audio` addresses whose package segment is this package's own; an
|
|
1885
|
+
address naming any other type, or naming a different package, is untouched.
|
|
1886
|
+
|
|
1887
|
+
**A replacement's own output address is never itself looked up against
|
|
1888
|
+
another replacement.** Resolving `thalornaaltart-none-image-thorn` never asks
|
|
1889
|
+
whether something replaces `thalornaaltart` — resolution is a single pass over
|
|
1890
|
+
the declared list, not a chain, which forecloses a cycle by construction.
|
|
1891
|
+
|
|
1892
|
+
This is distinct from a `foreign` lookup, which serves a declared dependency's
|
|
1893
|
+
own content for an address this package's local index does not carry. A
|
|
1894
|
+
replacement is consulted **before** the local address; a foreign lookup only
|
|
1895
|
+
**after** it misses locally.
|
|
1896
|
+
|
|
1744
1897
|
#### A font is not an asset
|
|
1745
1898
|
|
|
1746
1899
|
`assets/fonts` is not a root, and a font has no address.
|
|
@@ -2089,7 +2242,7 @@ See the [authoring examples](../authoring/links-and-markup.md#footnotes-and-defi
|
|
|
2089
2242
|
```
|
|
2090
2243
|
|
|
2091
2244
|
Any header can end with a braced attribute block, in the same grammar a
|
|
2092
|
-
`:::
|
|
2245
|
+
`:::figure` fence and a named block take. Inside the braces:
|
|
2093
2246
|
|
|
2094
2247
|
- `#id` names the anchor a link resolves to (only one allowed)
|
|
2095
2248
|
- `.class1` names a CSS class (any number of classes allowed)
|
|
@@ -2171,11 +2324,14 @@ either side of it, and every surface renders it as a figure. An image sharing a
|
|
|
2171
2324
|
paragraph with prose is refused, because a width and a position mean nothing
|
|
2172
2325
|
applied to a word in the middle of a sentence.
|
|
2173
2326
|
|
|
2174
|
-
**
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2327
|
+
**Alt text renders as a caption on no surface.** On the website and in a
|
|
2328
|
+
Foundry journal it stays on the `img` element's `alt` attribute alone, and the
|
|
2329
|
+
book prints nothing beneath a picture that carries none. A visible label and
|
|
2330
|
+
caption are drawn only for a picture wrapped in [a `:::figure`
|
|
2331
|
+
fence](#the-figure-fence), and the text drawn is always the fence's own
|
|
2332
|
+
caption. A title — `` — is refused rather than dropped in
|
|
2333
|
+
silence: there is one place for those words, the alt text, and a title is not
|
|
2334
|
+
it.
|
|
2179
2335
|
|
|
2180
2336
|
##### Width is a class, and the ordinary width carries no marker
|
|
2181
2337
|
|
|
@@ -2465,34 +2621,110 @@ the tag and, where the directive restricts one, the type. An empty list under
|
|
|
2465
2621
|
an authored heading is how a misspelled tag survives a build, so the
|
|
2466
2622
|
directive's silence is what is refused rather than its output.
|
|
2467
2623
|
|
|
2468
|
-
#####
|
|
2624
|
+
##### The figure fence
|
|
2469
2625
|
|
|
2470
|
-
`:::
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
and contains letters, digits, `_`, or `-`, beginning with a letter. An empty
|
|
2474
|
-
caption, missing ID, missing closer, or missing following block is an error at
|
|
2475
|
-
the opening line.
|
|
2626
|
+
`:::figure {#anchor .border}` declares what it numbers and, optionally,
|
|
2627
|
+
captions: the fence is the unit the counter counts, not the caption text it
|
|
2628
|
+
may or may not carry.
|
|
2476
2629
|
|
|
2477
2630
|
````markdown
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
:::caption {#trade}
|
|
2481
|
-
Regional trade routes
|
|
2482
|
-
:::
|
|
2631
|
+
:::figure {#trade}
|
|
2483
2632
|
|
|
2484
2633
|
```sql
|
|
2485
2634
|
SELECT name.full AS "Market" FROM notes WHERE type = 'place'
|
|
2486
2635
|
```
|
|
2636
|
+
|
|
2637
|
+
///
|
|
2638
|
+
Regional trade routes
|
|
2639
|
+
:::
|
|
2640
|
+
|
|
2641
|
+
Refer to {{ref "#trade"}} for the market routes.
|
|
2487
2642
|
````
|
|
2488
2643
|
|
|
2489
|
-
The
|
|
2490
|
-
displays **Table 1** as a link.
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2644
|
+
The fence displays **Table 1: Regional trade routes** and `{{ref "#trade"}}`
|
|
2645
|
+
displays **Table 1** as a link.
|
|
2646
|
+
|
|
2647
|
+
**The grammar.** `:::figure` opens the fence, on a line of its own and
|
|
2648
|
+
optionally followed by a braced attribute block in the grammar a heading and
|
|
2649
|
+
a named block take too. A line of `:::` alone closes it. Between the
|
|
2650
|
+
two, the first `///` written alone on a line, read at the top level — outside
|
|
2651
|
+
any nested code fence — divides what the fence holds from its caption; a `///`
|
|
2652
|
+
written inside a nested fence is content, not a divider. A second top-level
|
|
2653
|
+
`///` is an error, naming the line: a figure holds one caption, so it carries
|
|
2654
|
+
one `///` line.
|
|
2655
|
+
|
|
2656
|
+
An opening line that is not `:::figure`, optionally followed by one braced
|
|
2657
|
+
attribute block and nothing else, is reported at its own line as
|
|
2658
|
+
`a figure's attributes are written as {#id .border}`.
|
|
2659
|
+
|
|
2660
|
+
**Every part but the fence itself is optional.**
|
|
2661
|
+
|
|
2662
|
+
- **No `{#id}`.** The fence is still numbered and drawn; it carries no anchor,
|
|
2663
|
+
so nothing can address it. An id, when written, must be a name unique within
|
|
2664
|
+
the note — a repeat is `duplicate figure id "<id>"` at the fence's opening
|
|
2665
|
+
line, and an id that collides with a heading anchor in the same note is
|
|
2666
|
+
`heading and figure declare the same anchor "<id>"` at the heading's line.
|
|
2667
|
+
- **No `///`.** The fence draws its label alone, with no caption beneath it. A
|
|
2668
|
+
`///` with nothing after it before the closing `:::` is an error at the
|
|
2669
|
+
`///` line: a figure's `///` section carries no caption — write no `///` to
|
|
2670
|
+
leave the figure uncaptioned.
|
|
2671
|
+
- **No `.border`.** The fence draws without one. `.border` is the only class
|
|
2672
|
+
the construct declares; any other class is reported at the opening line,
|
|
2673
|
+
naming it: a figure takes no such class, and the classes a figure takes
|
|
2674
|
+
are `.border`. A `key="value"` attribute is reported the same way, naming
|
|
2675
|
+
the key: a figure takes an id and classes, nothing else.
|
|
2676
|
+
|
|
2677
|
+
**What is never optional.** A fence with nothing between its opening line and
|
|
2678
|
+
its `///` or its closing `:::` is `a figure has no contents`, at the opening
|
|
2679
|
+
line. An opening `:::figure` with no matching `:::` runs to the end of the
|
|
2680
|
+
note and is `a figure needs a closing :::`, also at the opening line, and
|
|
2681
|
+
nothing after it is scanned as a second fence.
|
|
2682
|
+
|
|
2683
|
+
**The counter is derived, never authored.** The fence's contents decide which
|
|
2684
|
+
of four counters it draws from — `Code`, `Table`, `Figure`, `Prose` — read from
|
|
2685
|
+
the first top-level block inside the fence: a table is `Table`; a fenced `sql`
|
|
2686
|
+
block is `Table`, because its rendered output is one; any other fenced or
|
|
2687
|
+
indented code block is `Code`; one or more pictures and nothing else is
|
|
2688
|
+
`Figure`; everything else is `Prose`. A table or a picture written outside any
|
|
2689
|
+
`:::figure` fence draws no label and is counted nowhere. The website and a
|
|
2690
|
+
Foundry journal number each counter within the note; the book numbers each
|
|
2691
|
+
counter across the book in reading order.
|
|
2692
|
+
|
|
2693
|
+
**A heading that would start its own journal page cannot be written inside a
|
|
2694
|
+
fence.** An H1, or a heading at any level carrying an `{#anchor}`, is reported
|
|
2695
|
+
at its own line inside one: `a heading that starts a page cannot be written
|
|
2696
|
+
inside a figure — keep an H1 or an anchored heading at the top level, or drop
|
|
2697
|
+
the anchor and the level to stay inside it`.
|
|
2698
|
+
|
|
2699
|
+
**The `ref` expression renders a link to a figure's anchor.**
|
|
2700
|
+
`{{ref "#thorn"}}` renders "Figure 13"; `{{ref "#thorn" form="full"}}` renders
|
|
2701
|
+
the number and the caption; `{{ref "#thorn" form="title"}}` renders the
|
|
2702
|
+
caption alone. `form="number"` is the default and may be written explicitly;
|
|
2703
|
+
a `form` outside this closed set is reported naming the three. A link written
|
|
2704
|
+
inside the figure's own caption contributes only its label text to a `full`
|
|
2705
|
+
or `title` reference, because the reference is itself a link, and emphasis
|
|
2706
|
+
inside the caption is kept. `form="full"` or `form="title"` aimed at a figure
|
|
2707
|
+
with no caption is reported naming the anchor: that form needs a caption, and
|
|
2708
|
+
the figure has none. An address naming no figure in the note is reported
|
|
2709
|
+
naming the anchor; an address naming another note's figure —
|
|
2710
|
+
`{{ref "other-note#anchor"}}` — is reported as addressing a figure this build
|
|
2711
|
+
does not resolve outside the note carrying the reference. Only a same-note
|
|
2712
|
+
anchor resolves, on the website and in the book.
|
|
2713
|
+
|
|
2714
|
+
Foundry gives a `:::figure` fence its own JournalEntryPage. A fence holding
|
|
2715
|
+
exactly one picture, captioned in plain text with nothing else — or carrying
|
|
2716
|
+
no caption at all — becomes a page of type `image`: the picture is its `src`,
|
|
2717
|
+
a caption, when there is one, is `image.caption`, and the page is named for
|
|
2718
|
+
the figure's own number. Every other fence becomes a page of type `text`
|
|
2719
|
+
instead: a fence grouping more than one picture; an embedded sound, there
|
|
2720
|
+
being no audio page type; a fence whose caption carries inline markup —
|
|
2721
|
+
emphasis, a link, an embedded icon or image — because `image.caption` is a
|
|
2722
|
+
`StringField` with nowhere to render it; and a fence that shares its split
|
|
2723
|
+
with prose that follows it, because an `image` page has nowhere to carry that
|
|
2724
|
+
prose either. On a `text` page the render is the fence's own content, scanned
|
|
2725
|
+
and labelled as the first of its kind on that page alone and then corrected to
|
|
2726
|
+
the number the note-wide scan already assigned, so a second figure with no id
|
|
2727
|
+
is still named for its true, note-wide number rather than recounted from one.
|
|
2496
2728
|
|
|
2497
2729
|
##### Named blocks
|
|
2498
2730
|
|
|
@@ -3775,18 +4007,49 @@ the setting itself, rather than instructions or other apparatus for the GM.
|
|
|
3775
4007
|
tournament is not a matter of time-reckoning at all — and from `culture`, which is a grouping
|
|
3776
4008
|
of people rather than an occasion they attend.
|
|
3777
4009
|
|
|
3778
|
-
| `data` property
|
|
3779
|
-
|
|
|
3780
|
-
| `epoch`
|
|
3781
|
-
| `months`
|
|
3782
|
-
| `weekdays`
|
|
3783
|
-
| `seasons`
|
|
3784
|
-
| `namedDays`
|
|
3785
|
-
| `eras`
|
|
3786
|
-
| `formats`
|
|
3787
|
-
| `
|
|
3788
|
-
|
|
3789
|
-
`
|
|
4010
|
+
| `data` property | Values | Description |
|
|
4011
|
+
| ----------------------- | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
|
|
4012
|
+
| `epoch` | canonical `<year>.<day>` | The canonical day that equals calendar year 1, day 1 |
|
|
4013
|
+
| `months` | `{ name, abbreviation?, days }[]` | Ordered months; their days sum to the world's year |
|
|
4014
|
+
| `weekdays` | `{ name, abbreviation? }[]` | Ordered week days, indexed from zero; weeks run continuously |
|
|
4015
|
+
| `seasons` | `{ name, abbreviation?, start }[]` | Seasons starting on one-based days of year |
|
|
4016
|
+
| `namedDays` | `{ name, abbreviation?, day }[]` | Names assigned to one-based days of year |
|
|
4017
|
+
| `eras` | `{ shortcode, name, abbreviation?, marker?, proclaimedBy?, start, label? }[]` | Year counts; `start` is `null` or an in-calendar year |
|
|
4018
|
+
| `formats` | map of names to Calendaria patterns | Named date formats; `std` is the preferred default |
|
|
4019
|
+
| `events` | `{ when, until?, recurs? }[]` | This note's dated occurrences, each with its own relationships and chronicle fields |
|
|
4020
|
+
| `events[].when` | a date, or `"0.<day>"` | The occurrence's anchor and first instance — required |
|
|
4021
|
+
| `events[].until` | a date | Where the occurrence ran to, or where a recurring series stopped |
|
|
4022
|
+
| `events[].recurs` | `{ every }` or `{ on }` | How further occurrences are found, absent for a one-time occurrence |
|
|
4023
|
+
| `events[].recurs.every` | whole number of years, 1 or more | A period counted on the canonical axis from `when` |
|
|
4024
|
+
| `events[].recurs.on` | list of dates, strictly increasing, each later than `when` | Recorded occurrences beyond the first, in place of a period |
|
|
4025
|
+
|
|
4026
|
+
`data.events` is available on every `lore` subType. Each entry's `kind`, `depth`, `summary`, `sources`, `standing`, `accounts`, `who`, `where`, `unresolved`, `follows` and `names` hold its chronicle detail; their shapes belong to whatever design governs chronicle records, and this format only asks that they sit inside an entry. The shared format checks `when`, `until` and `recurs`; a content package can check the rest. `events` itself does not appear as an infobox row, but a recurring entry's computed next occurrence does.
|
|
4027
|
+
|
|
4028
|
+
An entry reads by what it authors beside `when`:
|
|
4029
|
+
|
|
4030
|
+
| authored | reads as |
|
|
4031
|
+
| --------------------------------- | ----------------------------------------------- |
|
|
4032
|
+
| `when` | happened once |
|
|
4033
|
+
| `when` + `until` | ran continuously from then to then |
|
|
4034
|
+
| `when` + `recurs` | happened then and happens still |
|
|
4035
|
+
| `when` + `until` + `recurs.every` | happened then, again on the period, and stopped |
|
|
4036
|
+
|
|
4037
|
+
`recurs.every` and `recurs.on` are exclusive — a period with named exceptions is written as an enumeration instead. `recurs` is refused beside `born` or `died`, and beside a `when` of year `0`. `until` is refused beside `recurs.on`, whose own last entry already bounds it.
|
|
4038
|
+
|
|
4039
|
+
**The period advances the canonical year, never the era-relative year.** There is no year `0` in era-relative numbering — era year `-1` sits immediately before era year `1` — but canonical year `0` is an ordinary integer on the continuous axis `canonicalYear` builds, so a period of whole years steps on it with no special case at an epoch crossing. Precision and approximation carry through untouched: a year-precision anchor yields year-precision occurrences, an approximate anchor's occurrences are approximate too, and an anchor inside a short intercalary month recurs on that same day in every occurrence, with no special case for the month being short. Each occurrence selects its own era by testing its position against the anchor's calendar, so a later occurrence may print in a different era than its anchor, or print bare where no count was proclaimed for that year — neither is ever a finding, since nobody authored the occurrence.
|
|
4040
|
+
|
|
4041
|
+
**Year `0` means any year.** An entry whose `when` states year `0` recurs on that day of every year — `when: "0.5"` is the fifth day of the year, in every year. The value must be quoted, for the reason every canonical date stating a day must be: an unquoted `<year>.<day>` reaches the check as a float, and a day's trailing zero is lost before a date is read. Beside a year-`0` entry, `recurs` is refused (the entry is already annual), `until` is allowed and bounds the series, and the entry carries no `canonicalYear`, era or `sort` — it cannot date its own note. A note's date is its first entry stating a real year.
|
|
4042
|
+
|
|
4043
|
+
```yaml
|
|
4044
|
+
data:
|
|
4045
|
+
events:
|
|
4046
|
+
- when: 412.1
|
|
4047
|
+
recurs: { every: 1 }
|
|
4048
|
+
- when: "0.286"
|
|
4049
|
+
until: 940.1
|
|
4050
|
+
```
|
|
4051
|
+
|
|
4052
|
+
The first entry is an anniversary counted from canonical day `412.1`, recurring every year with no end. The second is an autumn rite — the two-hundred-eighty-sixth day of every year — that was kept until canonical year 940 and no longer is.
|
|
3790
4053
|
|
|
3791
4054
|
#### Calendar note structure
|
|
3792
4055
|
|
|
@@ -32,14 +32,14 @@ These are the only keys a note writes at its top level, in the order the formatt
|
|
|
32
32
|
|
|
33
33
|
These fields are accepted by every note type. A field's value can still be irrelevant to a particular output; the build reports that where it can.
|
|
34
34
|
|
|
35
|
-
| Field | Shape | Meaning
|
|
36
|
-
| ----------------- | ----------------------------------------------- |
|
|
37
|
-
| `data.id` | string | A fixed document identity when the derived identity is unsuitable.
|
|
38
|
-
| `data.pack` | string | The shared compendium route, with a system block overriding it.
|
|
39
|
-
| `data.packFolder` | an Address, or a map of Addresses keyed by pack | The shared compendium folder Address, with a system block overriding it.
|
|
40
|
-
| `data.harnworld` | map | HârnWorld source details shared by every system.
|
|
41
|
-
| `data.icon` | an Address | The document's profile art — an `icon` address, resolved into `img`.
|
|
42
|
-
| `data.banner` | an Address | The page's hero image — an `image` address. Reaches no compiled document. |
|
|
35
|
+
| Field | Shape | Meaning |
|
|
36
|
+
| ----------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
37
|
+
| `data.id` | string | A fixed document identity when the derived identity is unsuitable. |
|
|
38
|
+
| `data.pack` | string | The shared compendium route, with a system block overriding it. |
|
|
39
|
+
| `data.packFolder` | an Address, or a map of Addresses keyed by pack | The shared compendium folder Address, with a system block overriding it. |
|
|
40
|
+
| `data.harnworld` | map | HârnWorld source details shared by every system. |
|
|
41
|
+
| `data.icon` | an Address | The document's profile art — an `icon` address, resolved into `img`. |
|
|
42
|
+
| `data.banner` | an Address | The page's hero image — an `image` address, cut to 1792×768. Reaches no compiled document. |
|
|
43
43
|
|
|
44
44
|
## Type-specific fields
|
|
45
45
|
|
|
@@ -432,7 +432,7 @@ Lore records in-world knowledge. A `culture` describes a people; a `custom` desc
|
|
|
432
432
|
| `data.namedDays` | list of `{ name, abbreviation?, day }` | Names assigned to particular days of the year. |
|
|
433
433
|
| `data.eras` | list of `{ shortcode, name, marker?, abbreviation?, proclaimedBy?, start, label? }` | The year-counts kept in this calendar. A marker names one era and uses these months. |
|
|
434
434
|
| `data.formats` | map of named Calendaria format strings | Named patterns for reading and writing this calendar's dates. |
|
|
435
|
-
| `data.
|
|
435
|
+
| `data.events` | list of `{ when, until?, recurs? }` | This note's dated occurrences — a founding once, an anniversary that recurs, or a list of recorded happenings. |
|
|
436
436
|
|
|
437
437
|
### map
|
|
438
438
|
|
package/engine/anchors.mjs
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* The anchors a note declares on headings and
|
|
15
|
+
* The anchors a note declares on headings and figure fences.
|
|
16
16
|
*
|
|
17
17
|
* **A leaf, deliberately.** This is asked by the link checker, by the content
|
|
18
18
|
* index, and by the builds that emit a link, and they cannot all import one
|
|
@@ -29,23 +29,28 @@
|
|
|
29
29
|
* @module
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
|
-
import {
|
|
32
|
+
import { scanFigures } from "./content-figures.mjs";
|
|
33
33
|
import { HEADING_LINE, splitHeadingAttributes } from "./heading-attributes.mjs";
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
* The `{#slug}` and `:::
|
|
36
|
+
* The `{#slug}` and `:::figure {#slug}` anchors a note declares.
|
|
37
37
|
*
|
|
38
|
-
* A bare `#` heading starts a journal page without declaring a slug
|
|
39
|
-
*
|
|
38
|
+
* A bare `#` heading starts a journal page without declaring a slug, and so
|
|
39
|
+
* does a figure fence that declares no id. A fence that declares one declares a
|
|
40
|
+
* slug and starts an addressable journal page.
|
|
40
41
|
*
|
|
41
42
|
* @param {string} body - The note's markdown body, frontmatter already removed.
|
|
42
43
|
* @param {number} [bodyLine] - The 1-based file line the body starts on, from
|
|
43
44
|
* `parseMarkdownFile`. Anchors are reported at their position in the **file**,
|
|
44
45
|
* so an editor can jump straight to one; passing nothing numbers from the body.
|
|
46
|
+
* @param {(address: string) => string|undefined} [resolveRole] - From a
|
|
47
|
+
* picture's address to the role its asset declares, so a figure anchor's
|
|
48
|
+
* `name` reads `Map 1` rather than `Figure 1` where it is due — see
|
|
49
|
+
* {@link module:engine/content-figures.scanFigures}.
|
|
45
50
|
* @returns {Array<{slug: string, name: string, level: number, line: number}>}
|
|
46
51
|
* In document order.
|
|
47
52
|
*/
|
|
48
|
-
export function collectAnchors(body, bodyLine = 1) {
|
|
53
|
+
export function collectAnchors(body, bodyLine = 1, resolveRole) {
|
|
49
54
|
const anchors = [];
|
|
50
55
|
let inCodeBlock = false;
|
|
51
56
|
const lines = String(body ?? "").split("\n");
|
|
@@ -70,12 +75,13 @@ export function collectAnchors(body, bodyLine = 1) {
|
|
|
70
75
|
line: bodyLine + i,
|
|
71
76
|
});
|
|
72
77
|
}
|
|
73
|
-
for (const
|
|
78
|
+
for (const figure of scanFigures(body, { resolveRole }).figures) {
|
|
79
|
+
if (!figure.id) continue;
|
|
74
80
|
anchors.push({
|
|
75
|
-
slug:
|
|
76
|
-
name:
|
|
81
|
+
slug: figure.id,
|
|
82
|
+
name: figure.label,
|
|
77
83
|
level: 1,
|
|
78
|
-
line: bodyLine +
|
|
84
|
+
line: bodyLine + figure.line - 1,
|
|
79
85
|
});
|
|
80
86
|
}
|
|
81
87
|
return anchors.sort((a, b) => a.line - b.line);
|