@heroiclands/package-build 22.20.0 → 22.22.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-content-build.md +0 -8
- package/CHANGELOG.md +204 -0
- package/assets/styles/content-base.css +162 -0
- package/bin/content-commands.mjs +26 -3
- package/bin/package-build.mjs +15 -2
- package/ci/content-format-reference.mjs +68 -1
- package/config.mjs +26 -0
- package/content-config.mjs +73 -4
- package/docs/api.md +46 -28
- package/docs/authoring/dates-and-calendars.md +4 -2
- package/docs/authoring/frontmatter.md +2 -2
- package/docs/authoring/links-and-markup.md +237 -28
- package/docs/configuration.md +27 -1
- package/docs/content-format.md +3 -1
- package/docs/guides/reference-pages.md +15 -0
- package/docs/reference/format-details.md +265 -42
- package/docs/reference/note-types.md +45 -28
- package/engine/actor-compiler.mjs +23 -0
- package/engine/anchored-sections.mjs +35 -4
- package/engine/anchors.mjs +10 -20
- package/engine/base-compiler.mjs +86 -21
- package/engine/base-styles.mjs +149 -0
- package/engine/bundles.mjs +0 -1
- package/engine/compendiums.mjs +8 -1
- package/engine/content-blocks.mjs +410 -0
- package/engine/content-captions.mjs +57 -8
- package/engine/content-footnotes.mjs +189 -0
- package/engine/content-format-check.mjs +15 -12
- package/engine/content-index.mjs +2 -2
- package/engine/content-links.mjs +14 -6
- package/engine/content-tables.mjs +45 -4
- package/engine/content-tasklists.mjs +189 -0
- package/engine/docs-checks.mjs +4 -4
- package/engine/extension-attributes.mjs +48 -2
- package/engine/frontmatter-lint.mjs +62 -66
- package/engine/frontmatter.mjs +3 -3
- package/engine/heading-attributes.mjs +308 -0
- package/engine/helpers.mjs +19 -13
- package/engine/homepage.mjs +8 -8
- package/engine/index.mjs +3 -0
- package/engine/infobox-render.mjs +21 -4
- package/engine/infobox.mjs +167 -7
- package/engine/journals.mjs +36 -17
- package/engine/macros.mjs +1 -1
- package/engine/markdown-expressions.mjs +178 -53
- package/engine/note-addresses.mjs +17 -8
- package/engine/note-dates.mjs +49 -0
- package/engine/note-frontmatter.mjs +7 -16
- package/engine/note-vocabulary.mjs +216 -42
- package/engine/page-lists.mjs +358 -0
- package/engine/pdf-build.mjs +76 -42
- package/engine/pdf-render.mjs +422 -49
- package/engine/rank-ladder.mjs +198 -0
- package/engine/reckoning-markers.mjs +1 -1
- package/engine/reference-targets.mjs +13 -1
- package/engine/retired-fields.mjs +8 -13
- package/engine/scenes.mjs +10 -3
- package/engine/site-build.mjs +41 -12
- package/engine/site-config.mjs +10 -1
- package/engine/site-index.mjs +39 -4
- package/engine/site-preview.mjs +18 -7
- package/engine/social-ties.mjs +52 -14
- package/engine/sql-tables.mjs +66 -9
- package/engine/standing-terms.mjs +19 -0
- package/engine/standings.mjs +538 -0
- package/engine/svg-theme.mjs +15 -4
- package/engine/system-block.mjs +12 -14
- package/engine/web-wikilinks.mjs +48 -1
- package/engine/wikilinks.mjs +3 -1
- package/hm3/actors.mjs +6 -3
- package/manifest.mjs +13 -0
- package/package.json +1 -1
- package/sohl/actors.mjs +82 -103
- package/sohl/item-fields.mjs +4 -5
- package/sohl/items.mjs +1 -1
- package/sohl/note-schemas.mjs +11 -21
- package/types/config.d.mts +9 -0
- package/types/engine/actor-compiler.d.mts +11 -0
- package/types/engine/anchored-sections.d.mts +24 -0
- package/types/engine/base-compiler.d.mts +19 -0
- package/types/engine/base-styles.d.mts +93 -0
- package/types/engine/content-blocks.d.mts +87 -0
- package/types/engine/content-captions.d.mts +14 -1
- package/types/engine/content-footnotes.d.mts +73 -0
- package/types/engine/content-format-check.d.mts +8 -6
- package/types/engine/content-tables.d.mts +12 -4
- package/types/engine/content-tasklists.d.mts +72 -0
- package/types/engine/extension-attributes.d.mts +22 -1
- package/types/engine/frontmatter.d.mts +1 -1
- package/types/engine/heading-attributes.d.mts +174 -0
- package/types/engine/helpers.d.mts +7 -4
- package/types/engine/homepage.d.mts +7 -7
- package/types/engine/index.d.mts +1 -0
- package/types/engine/infobox-render.d.mts +6 -1
- package/types/engine/journals.d.mts +14 -7
- package/types/engine/markdown-expressions.d.mts +23 -0
- package/types/engine/note-frontmatter.d.mts +1 -4
- package/types/engine/note-vocabulary.d.mts +98 -8
- package/types/engine/page-lists.d.mts +126 -0
- package/types/engine/pdf-render.d.mts +72 -2
- package/types/engine/rank-ladder.d.mts +32 -0
- package/types/engine/retired-fields.d.mts +4 -6
- package/types/engine/site-build.d.mts +13 -1
- package/types/engine/site-config.d.mts +9 -0
- package/types/engine/site-index.d.mts +13 -1
- package/types/engine/site-preview.d.mts +1 -1
- package/types/engine/sql-tables.d.mts +31 -7
- package/types/engine/standing-terms.d.mts +16 -0
- package/types/engine/standings.d.mts +147 -0
- package/types/engine/system-block.d.mts +11 -12
- package/types/engine/web-wikilinks.d.mts +3 -1
- package/types/engine/wikilinks.d.mts +2 -1
- package/types/hm3/actors.d.mts +3 -3
- package/types/sohl/actors.d.mts +25 -7
- package/engine/content-admonitions.mjs +0 -114
- package/engine/content-secrets.mjs +0 -112
- package/types/engine/content-admonitions.d.mts +0 -24
- package/types/engine/content-secrets.d.mts +0 -17
|
@@ -354,6 +354,8 @@ order without changing the Markdown body.
|
|
|
354
354
|
|
|
355
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.
|
|
356
356
|
|
|
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
|
+
|
|
357
359
|
A named date uses `datefrom <calendar> <date>`, where the final text follows the calendar's `data.formats.std` pattern, or its first named pattern when `std` is absent. For example, a calendar with `std: "MM/DD/Y GGG"` reads `datefrom commoncal 01/23/326 VR`, `datefrom commoncal 01/326 VR`, and `datefrom commoncal 326 VR`. Omitting the day or month retains month or year precision in frontmatter. The CLI `datefrom` command requires a day to produce one canonical day. The calendar argument is a shortcode or Address. A time follows the day only when specified in the pattern, or as a trailing `HH:mm:ss` when the pattern has no clock tokens. A named date with an era-relative year names its era.
|
|
358
360
|
|
|
359
361
|
`data.epoch` on a calendar is a canonical `<year>.<day>`: the day on which **calendar year 1, day 1** occurs. It cannot be year-only, approximate, or expressed through its own calendar. `data.moon.newOn` also locates a particular day. The `data.eras[].start` values are `null` or positive **calendar years**. Exactly one null era covers dates before calendar year 1, and exactly one era starts at year 1. Further era starts are distinct positive integers. Numeric start order determines era boundaries, regardless of array order. The null era numbers its years backwards; later eras begin at their own displayed year 1. An era may have a unique uppercase `marker` for resolving authored dates.
|
|
@@ -2086,11 +2088,69 @@ See the [authoring examples](../authoring/links-and-markup.md#footnotes-and-defi
|
|
|
2086
2088
|
# Heading {#id .class1 .class2 attr="value"}
|
|
2087
2089
|
```
|
|
2088
2090
|
|
|
2089
|
-
Any header can
|
|
2091
|
+
Any header can end with a braced attribute block, in the same grammar a
|
|
2092
|
+
`:::caption` and a named block take. Inside the braces:
|
|
2093
|
+
|
|
2094
|
+
- `#id` names the anchor a link resolves to (only one allowed)
|
|
2095
|
+
- `.class1` names a CSS class (any number of classes allowed)
|
|
2096
|
+
- `attr="value"` names an HTML attribute whose value is `value` (any number of
|
|
2097
|
+
key/value pairs allowed)
|
|
2098
|
+
|
|
2099
|
+
The id is what every surface publishes; a class and an attribute reach only the
|
|
2100
|
+
surfaces that have somewhere to put them.
|
|
2101
|
+
|
|
2102
|
+
| | an `#id` | a `.class` | an `attr="value"` |
|
|
2103
|
+
| --------------- | ----------------------------------------------------------- | --------------------- | ---------------------- |
|
|
2104
|
+
| Website | the heading's `id`, and the target of `[[note#id\|…]]` | the heading's `class` | written on the heading |
|
|
2105
|
+
| Foundry journal | the anchor the page is addressed by, and the heading's `id` | the heading's `class` | written on the heading |
|
|
2106
|
+
| Book | the Typst label the heading is linked to | dropped | dropped |
|
|
2107
|
+
|
|
2108
|
+
**The book drops both**, because Typst has no stylesheet for a class to name
|
|
2109
|
+
and no element for an attribute to sit on. A heading whose appearance matters in
|
|
2110
|
+
print is set by the book's own template, not by a class an author writes.
|
|
2111
|
+
|
|
2112
|
+
An id contains letters, digits, `_` or `-`. A class and an attribute key begin
|
|
2113
|
+
with a letter. Braces holding nothing the grammar recognises — a set, a die
|
|
2114
|
+
expression, a measurement — are one finding at the heading's line, and the
|
|
2115
|
+
heading is set as it was written so nothing is lost while the author reads the
|
|
2116
|
+
message.
|
|
2117
|
+
|
|
2118
|
+
`id=` and `class=` are refused in favour of `#id` and `.class`, and an attribute
|
|
2119
|
+
beginning `on` is refused as an event handler, exactly as on a named block: a
|
|
2120
|
+
heading's attributes are written onto the element, so nothing authored in a note
|
|
2121
|
+
becomes code on a published page.
|
|
2122
|
+
|
|
2123
|
+
#### A section withheld from players
|
|
2124
|
+
|
|
2125
|
+
The class `secret` on the heading that opens a section withholds that section.
|
|
2126
|
+
The heading keeps its id, so the section stays linkable: secrecy is a property
|
|
2127
|
+
of the section, not of the name a link resolves to.
|
|
2128
|
+
|
|
2129
|
+
```markdown
|
|
2130
|
+
# The Cellar {#cellar .secret}
|
|
2131
|
+
|
|
2132
|
+
The contraband is behind the false wall.
|
|
2133
|
+
```
|
|
2134
|
+
|
|
2135
|
+
The section runs from the heading to the next heading that opens a page, which
|
|
2136
|
+
is the span Foundry makes one page of.
|
|
2090
2137
|
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2138
|
+
| Surface | What a reader meets |
|
|
2139
|
+
| ------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
2140
|
+
| Foundry | The journal page is visible to the GM alone — `ownership: { default: 0 }`, `CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE` |
|
|
2141
|
+
| Website | The heading and everything under it sit inside the expandable `<details>` a `:::secret` block renders as |
|
|
2142
|
+
| Book | The heading and everything under it are set in the labelled block a `:::secret` is set in |
|
|
2143
|
+
|
|
2144
|
+
**On the website this is a spoiler and not access control.** The withheld
|
|
2145
|
+
section is in the published page, so a reader who opens the element or reads the
|
|
2146
|
+
source reads it — exactly as a `:::secret` block is. Foundry is the only surface
|
|
2147
|
+
that withholds anything from anybody. Write nothing in a `.secret` section that
|
|
2148
|
+
would matter if a player read it on the web.
|
|
2149
|
+
|
|
2150
|
+
A `:::secret` block inside a withheld section is legal and renders as it does
|
|
2151
|
+
anywhere else. `.secret` on a heading that opens **no** page covers no page, so
|
|
2152
|
+
no surface can honour it: that is a finding at the heading's own line, answered
|
|
2153
|
+
by giving the heading an anchor or raising it to the top level.
|
|
2094
2154
|
|
|
2095
2155
|
#### Images
|
|
2096
2156
|
|
|
@@ -2352,6 +2412,59 @@ errors. Duplicate and unknown attributes are errors.
|
|
|
2352
2412
|
| `allow-empty=true` | Permits a query selecting no rows. Default `false`. |
|
|
2353
2413
|
| `section-level=1` through `section-level=6` | Sets the heading level for `_section`. Default `2`. |
|
|
2354
2414
|
|
|
2415
|
+
##### Page lists
|
|
2416
|
+
|
|
2417
|
+
A fenced `pagelist` block renders a list of the pages carrying a tag. It is the
|
|
2418
|
+
companion of a content table and answers the other question: a table says what
|
|
2419
|
+
a set of notes contains, a page list says which pages belong to a group.
|
|
2420
|
+
|
|
2421
|
+
````markdown
|
|
2422
|
+
```pagelist {tag="key-concept" type=lore sort=type descriptions=true}
|
|
2423
|
+
|
|
2424
|
+
```
|
|
2425
|
+
````
|
|
2426
|
+
|
|
2427
|
+
**The group is the tag, not an enumeration.** A hand-written list of links is
|
|
2428
|
+
a second place the membership is recorded, and the two drift the moment a page
|
|
2429
|
+
is written and nobody edits the landing that should name it. A page list reads
|
|
2430
|
+
`tags:` instead, so tagging a page files it and untagging it removes it.
|
|
2431
|
+
|
|
2432
|
+
**The fence takes no body.** Every statement about the directive is an
|
|
2433
|
+
attribute in the shared braced grammar, so there is one grammar to learn; text
|
|
2434
|
+
written inside the fence is reported rather than printed, and a fence nobody
|
|
2435
|
+
closed is reported rather than swallowing the rest of the note.
|
|
2436
|
+
|
|
2437
|
+
| Attribute | What it does |
|
|
2438
|
+
| ------------------- | ------------------------------------------------------------------------- |
|
|
2439
|
+
| `tag=` | Required. The tag a page carries to join the list. |
|
|
2440
|
+
| `type=` | Restricts the list to one note type. Every type by default. |
|
|
2441
|
+
| `sort=name` | Alphabetical, case and accents folded, tiebroken on address. The default. |
|
|
2442
|
+
| `sort=type` | Groups the note types and orders by name within each. |
|
|
2443
|
+
| `descriptions=true` | Follows each link with the page's `description`. Default `false`. |
|
|
2444
|
+
| `allow-empty=true` | Permits a tag no page carries. Default `false`. |
|
|
2445
|
+
|
|
2446
|
+
The tag is matched the way `tags:` is read everywhere else — a leading `#`,
|
|
2447
|
+
surrounding space and letter case are all insignificant, while the spelling of
|
|
2448
|
+
the tag itself is exact. Nothing guesses at a near miss.
|
|
2449
|
+
|
|
2450
|
+
**What a list holds.** Every note of this package that carries the tag and
|
|
2451
|
+
publishes a page. A **stub** publishes no page, so it holds no address and joins
|
|
2452
|
+
no list. A documentation journal is not listed beside the note it documents,
|
|
2453
|
+
because they are one page. A note tagged `gm` is listed in Foundry and left out
|
|
2454
|
+
of the website and the book, which is the same audience rule a content table's
|
|
2455
|
+
relations follow.
|
|
2456
|
+
|
|
2457
|
+
**Each entry is a link, resolved by the surface that is building.** The
|
|
2458
|
+
directive is answered before the body is walked and spliced in where the fence
|
|
2459
|
+
sat, so the list reaches a reader as compendium journal links in Foundry and
|
|
2460
|
+
page links on the website and in the book — the same resolution an authored
|
|
2461
|
+
`[[…]]` gets, including the cue on a link into a `draft` note.
|
|
2462
|
+
|
|
2463
|
+
**A tag no page carries is an error at the fence's line and column**, naming
|
|
2464
|
+
the tag and, where the directive restricts one, the type. An empty list under
|
|
2465
|
+
an authored heading is how a misspelled tag survives a build, so the
|
|
2466
|
+
directive's silence is what is refused rather than its output.
|
|
2467
|
+
|
|
2355
2468
|
##### Captioned blocks
|
|
2356
2469
|
|
|
2357
2470
|
`:::caption {#anchor}` labels the next Markdown block, whether it is prose,
|
|
@@ -2381,48 +2494,74 @@ block is prose. The website and Foundry number each category within a note;
|
|
|
2381
2494
|
the PDF numbers each category through the book. Foundry gives a captioned block
|
|
2382
2495
|
its own JournalEntryPage, so its anchor has a page UUID.
|
|
2383
2496
|
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2497
|
+
##### Named blocks
|
|
2498
|
+
|
|
2499
|
+
`:::secret`, `:::info` and `:::warn` set a passage apart from the prose around
|
|
2500
|
+
it. The opener and the closing `:::` each take a whole line, the content is
|
|
2501
|
+
Markdown and renders normally, and the fences stay outside code blocks. An
|
|
2502
|
+
empty, nested or unclosed block is an error at its opening line, as is a name
|
|
2503
|
+
that is not one of the three.
|
|
2504
|
+
|
|
2505
|
+
A braced attribute block may follow the name. It takes an id as `#id`, classes
|
|
2506
|
+
as `.class`, and any other attribute as `key="value"`. `title` is reserved and
|
|
2507
|
+
supplies the heading; without it a block heads itself **Secret**, **Info** or
|
|
2508
|
+
**Warn**. A title carries its own emphasis and nothing else — a tag written
|
|
2509
|
+
there is escaped. `id=` and `class=` are refused in favour of `#id` and
|
|
2510
|
+
`.class`, and an attribute beginning `on` is refused as an event handler.
|
|
2389
2511
|
|
|
2390
2512
|
```markdown
|
|
2391
2513
|
:::info
|
|
2392
2514
|
The harbor remains open through winter.
|
|
2393
2515
|
:::
|
|
2394
2516
|
|
|
2395
|
-
:::warn {#flood-warning}
|
|
2517
|
+
:::warn {#flood-warning title="Seasonal closure"}
|
|
2396
2518
|
Spring floods close the eastern road.
|
|
2397
2519
|
:::
|
|
2398
|
-
```
|
|
2399
2520
|
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
This is secret text
|
|
2521
|
+
:::secret {#harbor .wide}
|
|
2522
|
+
The harbor master is working for the smugglers.
|
|
2403
2523
|
:::
|
|
2404
2524
|
```
|
|
2405
2525
|
|
|
2406
|
-
|
|
2526
|
+
The block's name is the first class on the emitted element and the author's
|
|
2527
|
+
classes follow it. **No block carries an inline `style`**, so the appearance is
|
|
2528
|
+
a stylesheet's. The base stylesheet styles `section.info` and `section.warn`;
|
|
2529
|
+
`section.secret` is left to Foundry, which styles it and owns the reveal control
|
|
2530
|
+
on it.
|
|
2407
2531
|
|
|
2408
|
-
In FoundryVTT
|
|
2532
|
+
In FoundryVTT a block is a section whose first line is its heading, and a
|
|
2533
|
+
`secret` is the section Foundry shows to the GM and to owners of the journal:
|
|
2409
2534
|
|
|
2410
2535
|
```html
|
|
2411
|
-
<section class="secret" id="
|
|
2412
|
-
<
|
|
2536
|
+
<section class="secret wide" id="harbor">
|
|
2537
|
+
<strong>Secret</strong><br />
|
|
2538
|
+
<p>The harbor master is working for the smugglers.</p>
|
|
2413
2539
|
</section>
|
|
2414
2540
|
```
|
|
2415
2541
|
|
|
2416
|
-
|
|
2542
|
+
A `secret` that states no id of its own is given one derived from its body, so
|
|
2543
|
+
that Foundry can remember the section once a reader reveals it.
|
|
2544
|
+
|
|
2545
|
+
On webpages a block is a disclosure any reader can open, titled in its summary:
|
|
2417
2546
|
|
|
2418
2547
|
```html
|
|
2419
|
-
<details>
|
|
2420
|
-
<summary>
|
|
2421
|
-
<p>
|
|
2548
|
+
<details class="secret wide" id="harbor">
|
|
2549
|
+
<summary class="secret">Secret</summary>
|
|
2550
|
+
<p>The harbor master is working for the smugglers.</p>
|
|
2422
2551
|
</details>
|
|
2423
2552
|
```
|
|
2424
2553
|
|
|
2425
|
-
In
|
|
2554
|
+
In a book each block is a coloured box headed by its title. A printed page has
|
|
2555
|
+
no reader permissions and no interactive disclosure control, so a `secret` is
|
|
2556
|
+
visible there.
|
|
2557
|
+
|
|
2558
|
+
**A secret is a presentation distinction and not access control.** It is in the
|
|
2559
|
+
same authored note as the prose around it, and a reader who inspects source or
|
|
2560
|
+
generated output may see it.
|
|
2561
|
+
|
|
2562
|
+
**A malformed block does not silence the blocks around it.** It is reported at
|
|
2563
|
+
its own line and left as the author wrote it, and every well-formed block in the
|
|
2564
|
+
note still renders.
|
|
2426
2565
|
|
|
2427
2566
|
#### Actors
|
|
2428
2567
|
|
|
@@ -2432,11 +2571,6 @@ The following H1 headers are treated specially:
|
|
|
2432
2571
|
|
|
2433
2572
|
- `# ... {#appearance}`: The contents of this header become the `doc.appearance` property in sohl and `doc.description` in hm3.
|
|
2434
2573
|
- `# ... {#dossier}`: The contents of this header become the `doc.dossier` property in sohl and `doc.biography` in hm3.
|
|
2435
|
-
- `# ... {#spoilers}`: The contents of this header are not written to the actor at all.
|
|
2436
|
-
|
|
2437
|
-
For JournalEntries, the following rules apply:
|
|
2438
|
-
|
|
2439
|
-
- `# ... {#spoilers}`: The contents of this header go into a page which is viewable only by the GM (`CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE`).
|
|
2440
2574
|
|
|
2441
2575
|
**Archetypes** describe characters in broad terms and help match them to an adventure. The list is:
|
|
2442
2576
|
|
|
@@ -2487,7 +2621,7 @@ Generates a living (or undead, or spirit) being.
|
|
|
2487
2621
|
| `lore` | `Address[]` | Other lore concerning this being, such as social standing or law |
|
|
2488
2622
|
| `culture` | `Address` | Primary culture, naming a lore note with `subType: culture` |
|
|
2489
2623
|
| `homes` | `Address[]` | Place the being calls home |
|
|
2490
|
-
| `affiliations` | `Address
|
|
2624
|
+
| `affiliations` | `Map<Address, Membership>` | Bodies the being belongs to, keyed by Address, each entry the standing it holds there |
|
|
2491
2625
|
| `socialTies` | `Map<Address, Tie>` | Defining support and opposition, directed from this being to others |
|
|
2492
2626
|
| `gender` | `male \| female \| other` | Gender of the character |
|
|
2493
2627
|
| `species` | `Address` | Being's species (lore) |
|
|
@@ -2517,15 +2651,70 @@ describe a being's body. Item gear weight remains a numeric `data.weight`.
|
|
|
2517
2651
|
`data.culture` names the culture in which a character belongs. It is one
|
|
2518
2652
|
Address to a `type: lore`, `subType: culture` note. A named character with a
|
|
2519
2653
|
known culture states it here; generic beings and templates can omit it.
|
|
2520
|
-
`data.lore` holds
|
|
2521
|
-
|
|
2654
|
+
`data.lore` holds the law the being lives under, the customs it is subject to
|
|
2655
|
+
and the traditions it was raised in. Record the primary culture in
|
|
2656
|
+
`data.culture` only, and a standing in `data.affiliations`.
|
|
2522
2657
|
|
|
2523
2658
|
```yaml
|
|
2524
2659
|
data:
|
|
2525
2660
|
culture: lore-vedyariclt
|
|
2526
|
-
lore: [lore-
|
|
2661
|
+
lore: [lore-marchlaw]
|
|
2527
2662
|
```
|
|
2528
2663
|
|
|
2664
|
+
#### The bodies a being belongs to, and its standing in each
|
|
2665
|
+
|
|
2666
|
+
**Membership**: `{rank: number, office: string}` — `rank` required, `office` not.
|
|
2667
|
+
|
|
2668
|
+
`data.affiliations` is a **map keyed by the body's Address**, and each entry is
|
|
2669
|
+
the standing the being holds in that body. The key is what makes the pair
|
|
2670
|
+
expressible: a being in two bodies holds a standing in each, and two bodies may
|
|
2671
|
+
call a rung the same word.
|
|
2672
|
+
|
|
2673
|
+
An entry carries two keys and nothing else:
|
|
2674
|
+
|
|
2675
|
+
- **`rank`** — **required**, a **number**, the level on that body's own
|
|
2676
|
+
`governance.ranks` ladder. The body declares `{level, title, description}` for
|
|
2677
|
+
every rung, so a number indexes into it and the body stays the single source
|
|
2678
|
+
for what its level 5 is called.
|
|
2679
|
+
- **`office`** — optional, a **string**, matched against the keys of that body's
|
|
2680
|
+
`governance.offices` map.
|
|
2681
|
+
|
|
2682
|
+
**Every entry states a `rank`**, because belonging to a body is holding some
|
|
2683
|
+
standing in it. Where nothing more is known the rung is `1`, the ordinary
|
|
2684
|
+
member; `0` is the rung for someone cast out, which is a real answer rather than
|
|
2685
|
+
a default — the ladders carry `0 Níding`, `0 Outlaw` and `0 Struck from the
|
|
2686
|
+
Roll`. Between them the two ends cover every standing a member can hold, so an
|
|
2687
|
+
entry that states no rank is an **error** at the entry's own line, and the
|
|
2688
|
+
message names both numbers rather than only the gap.
|
|
2689
|
+
|
|
2690
|
+
An entry carrying an **office and no rank** is that same error and not a second
|
|
2691
|
+
one, reported once: an office distinguishes a person within a standing rather
|
|
2692
|
+
than standing in for one, so such an entry puts a person in a post in nothing.
|
|
2693
|
+
An entry carrying a rank and no office is complete.
|
|
2694
|
+
|
|
2695
|
+
```yaml
|
|
2696
|
+
data:
|
|
2697
|
+
affiliations:
|
|
2698
|
+
affiliation-vrystwldtrbs: { rank: 5, office: War Chief }
|
|
2699
|
+
affiliation-greenwardens: { rank: 1 }
|
|
2700
|
+
affiliation-silverguild: { rank: 0 }
|
|
2701
|
+
```
|
|
2702
|
+
|
|
2703
|
+
Both values are checked against the body the entry is keyed by. A `rank` that
|
|
2704
|
+
is not a level the body confers and an `office` that is not one of its keys are
|
|
2705
|
+
each an error naming the file, line and column — so a renamed rung or a renamed
|
|
2706
|
+
post fails on every being pointing at it. Naming one body twice, by its short
|
|
2707
|
+
and full Address, still names one body and is an error.
|
|
2708
|
+
|
|
2709
|
+
The infobox prints the standing closing on its body — `War Chief, Hárár, of
|
|
2710
|
+
Vrystwald Tribes` — and links an office to the description the body itself
|
|
2711
|
+
declares, so recording the post needs no note about the post.
|
|
2712
|
+
|
|
2713
|
+
For SoHL, a being embeds one affiliation item per entry: `rank` **is** that
|
|
2714
|
+
item's `system.level` and `office` its `system.office`. A standing is a fact
|
|
2715
|
+
about the setting rather than about one system, so the `sohl:` block states
|
|
2716
|
+
none of it.
|
|
2717
|
+
|
|
2529
2718
|
#### The people who matter to a being
|
|
2530
2719
|
|
|
2531
2720
|
`data.socialTies` records relationships that define the subject's dossier, not
|
|
@@ -2558,8 +2747,8 @@ an error.
|
|
|
2558
2747
|
Each tie is one-sided. The target need not name the subject back, even when
|
|
2559
2748
|
`patron` and `dependent` would describe opposite ends of the same support.
|
|
2560
2749
|
Kinship, household membership, organizational membership, rank, and office are
|
|
2561
|
-
distinct facts. Their relevant effects may appear as ties, while
|
|
2562
|
-
belong in
|
|
2750
|
+
distinct facts. Their relevant effects may appear as ties, while a membership
|
|
2751
|
+
and the standing held in it belong in `data.affiliations`. A tie has no
|
|
2563
2752
|
mechanical modifier.
|
|
2564
2753
|
|
|
2565
2754
|
#### When a being was born, when it died, and how old it is
|
|
@@ -2897,12 +3086,46 @@ two is the commonest way to get a non-monarchy wrong.
|
|
|
2897
3086
|
**Rank**
|
|
2898
3087
|
Definition of a level within the organization (e.g., Priest, Layperson, Member, Gang Leader, Master, etc.)
|
|
2899
3088
|
|
|
2900
|
-
| Property | Values | Description |
|
|
2901
|
-
| ------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
2902
|
-
| `level` |
|
|
2903
|
-
| `title` | string | Title associated with the Rank |
|
|
2904
|
-
| `description` | string | Description of the Rank |
|
|
2905
|
-
| `lore` | `Address` | The standing this rank _is_ — a `lore` note of subType `law`, shared with every other body that confers the same thing |
|
|
3089
|
+
| Property | Required | Values | Description |
|
|
3090
|
+
| ------------- | -------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
3091
|
+
| `level` | yes | integer | The ranking within the affiliation, increasing values starting with 1, 0 indicates intentional exclusion (expulsion/excommunication) |
|
|
3092
|
+
| `title` | yes | string | Title associated with the Rank |
|
|
3093
|
+
| `description` | yes | string | Description of the Rank |
|
|
3094
|
+
| `lore` | no | `Address` | The standing this rank _is_ — a `lore` note of subType `law`, shared with every other body that confers the same thing |
|
|
3095
|
+
|
|
3096
|
+
**`level`, `title` and `description` together are a complete statement of a
|
|
3097
|
+
rung**, and nothing further is required for a ladder to be correct. Each is an
|
|
3098
|
+
**error** when it is missing or empty, and a `level` that is not a whole number
|
|
3099
|
+
is an error too: the ladder is what a member's `rank` indexes into, so a rung
|
|
3100
|
+
with no title makes a standing that resolves to nothing, and one with no
|
|
3101
|
+
description ships a rung that says nothing on a sheet or a page. A rung accepts
|
|
3102
|
+
only these four keys, and each problem on a rung is reported separately, so a
|
|
3103
|
+
ladder with three is fixed in one pass rather than three builds.
|
|
3104
|
+
|
|
3105
|
+
**A level belongs to one rung.** Two rungs claiming one level is an **error** on
|
|
3106
|
+
the second, naming the first, because the ladder is read by level: a member whose
|
|
3107
|
+
`rank` is that level answers to whichever rung is written higher in the file, and
|
|
3108
|
+
moving two lines renames the standing. A body that confers two titles at one
|
|
3109
|
+
standing writes them as one rung — `title: Knight or Dame` — or gives each its
|
|
3110
|
+
own level.
|
|
3111
|
+
|
|
3112
|
+
```yaml
|
|
3113
|
+
data:
|
|
3114
|
+
governance:
|
|
3115
|
+
ranks:
|
|
3116
|
+
- { level: 0, title: Vrystrith, description: Kinless and owed nothing. }
|
|
3117
|
+
- { level: 4, title: Fródrád, description: A householder whose word carries. }
|
|
3118
|
+
- level: 5
|
|
3119
|
+
title: Hárár
|
|
3120
|
+
description: First among them, by the assent of the householders.
|
|
3121
|
+
lore: lore-harar-standing
|
|
3122
|
+
```
|
|
3123
|
+
|
|
3124
|
+
**Reach for `lore` only where the standing needs more said about it than a
|
|
3125
|
+
description can hold** — obligations, rights, what it takes to enter it, what
|
|
3126
|
+
ends it. Most rungs do not: a description that states what the standing is has
|
|
3127
|
+
already said the whole of it, and a note that restates the rung it hangs off
|
|
3128
|
+
costs a reader a click and tells them nothing.
|
|
2906
3129
|
|
|
2907
3130
|
**A rank's `lore` is shared; its `title` is not.** A Normen kingdom calls it
|
|
2908
3131
|
`Thrall` and a Vylarian province calls it `Slave`, and they mean one standing:
|
|
@@ -3575,7 +3798,7 @@ name: { full: The Common Calendar }
|
|
|
3575
3798
|
type: lore
|
|
3576
3799
|
subType: calendar
|
|
3577
3800
|
data:
|
|
3578
|
-
epoch: 1.1
|
|
3801
|
+
epoch: "1.1"
|
|
3579
3802
|
months:
|
|
3580
3803
|
- { name: Floralis, abbreviation: Flor, days: 30 }
|
|
3581
3804
|
- { name: Lusenar, abbreviation: Luse, days: 31 }
|
|
@@ -3824,7 +4047,7 @@ data:
|
|
|
3824
4047
|
# its moon — a note of its own, because a moon is a body and not a property
|
|
3825
4048
|
data:
|
|
3826
4049
|
body: { diameterKm: 3800, orbitalRadiusKm: 388600, orbit: circular, inclined: true }
|
|
3827
|
-
moon: { cycle: 30, newOn: 720.1, eclipses: rare }
|
|
4050
|
+
moon: { cycle: 30, newOn: "720.1", eclipses: rare }
|
|
3828
4051
|
```
|
|
3829
4052
|
|
|
3830
4053
|
**`year.days` is where the year's length lives, and every calendar in the
|
|
@@ -11,6 +11,23 @@ Choose a note's `type` for the subject it describes. Use `subType` when its type
|
|
|
11
11
|
|
|
12
12
|
The [first-note guide](../authoring/first-note.md) teaches the shape of a note. [Frontmatter](../authoring/frontmatter.md) explains shared and system-specific values. The [detailed reference](format-details.md) gives the full behavior and examples.
|
|
13
13
|
|
|
14
|
+
## Top-level keys
|
|
15
|
+
|
|
16
|
+
These are the only keys a note writes at its top level, in the order the formatter puts them. A key absent from this table is a lint error at its own line: the region is closed, so the key reaches no document and no page. Facts about the subject belong under `data`, and values for one game system belong inside that system's block.
|
|
17
|
+
|
|
18
|
+
| Key | Meaning |
|
|
19
|
+
| ------------- | ----------------------------------------------------------------------------------------- |
|
|
20
|
+
| `shortcode` | The note's own address segment, unique within its type. |
|
|
21
|
+
| `name` | The display names — a required `full`, and any `aliases`. |
|
|
22
|
+
| `type` | What the note is about, which decides its vocabulary and its document. |
|
|
23
|
+
| `subType` | The type's own genre, where it declares one. |
|
|
24
|
+
| `description` | The short page summary. |
|
|
25
|
+
| `tags` | Draft state, GM routing, and the descriptive labels a page list reads. |
|
|
26
|
+
| `data` | The facts about the subject itself, shared by every system. |
|
|
27
|
+
| `dnd5e` | What the `dnd5e` system makes of the subject — its document's mechanics, routing and art. |
|
|
28
|
+
| `hm3` | What the `hm3` system makes of the subject — its document's mechanics, routing and art. |
|
|
29
|
+
| `sohl` | What the `sohl` system makes of the subject — its document's mechanics, routing and art. |
|
|
30
|
+
|
|
14
31
|
## Shared `data` fields
|
|
15
32
|
|
|
16
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.
|
|
@@ -30,34 +47,34 @@ These fields are accepted by every note type. A field's value can still be irrel
|
|
|
30
47
|
|
|
31
48
|
**Subtypes:** `npc`, `character`, `creature`.
|
|
32
49
|
|
|
33
|
-
| Field | Shape
|
|
34
|
-
| -------------------------------- |
|
|
35
|
-
| `data.social` | map
|
|
36
|
-
| `data.tokenIcon` | an Address
|
|
37
|
-
| `data.templatePriority` | number
|
|
38
|
-
| `data.archetypes` | list
|
|
39
|
-
| `data.occupation` | string
|
|
40
|
-
| `data.stations` | list of Addresses
|
|
41
|
-
| `data.lore` | list of Addresses
|
|
42
|
-
| `data.culture` | an Address
|
|
43
|
-
| `data.homes` | list of Addresses
|
|
44
|
-
| `data.affiliations` | list of Addresses
|
|
45
|
-
| `data.socialTies` | a map keyed by Address
|
|
46
|
-
| `data.gender` | string
|
|
47
|
-
| `data.species` | an Address
|
|
48
|
-
| `data.born` | string
|
|
49
|
-
| `data.calendar` | an Address
|
|
50
|
-
| `data.died` | string
|
|
51
|
-
| `data.age` | string
|
|
52
|
-
| `data.ageYears` | number
|
|
53
|
-
| `data.height` | metres or feet and inches
|
|
54
|
-
| `data.weight` | kilograms or pounds
|
|
55
|
-
| `data.frame` | string
|
|
56
|
-
| `data.appearance.eye_color` | string
|
|
57
|
-
| `data.appearance.hair_color` | string
|
|
58
|
-
| `data.appearance.skin_color` | string
|
|
59
|
-
| `data.appearance.complexion` | string
|
|
60
|
-
| `data.appearance.extra_features` | list
|
|
50
|
+
| Field | Shape | Meaning |
|
|
51
|
+
| -------------------------------- | ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
52
|
+
| `data.social` | map | The being's social profile. |
|
|
53
|
+
| `data.tokenIcon` | an Address | What a token on the canvas wears — an `icon` address; unset, it follows `icon`. |
|
|
54
|
+
| `data.templatePriority` | number | Template priority; unset means the note is not a template. |
|
|
55
|
+
| `data.archetypes` | list | Archetypal behaviours the being fits. |
|
|
56
|
+
| `data.occupation` | string | What the being does for a living. |
|
|
57
|
+
| `data.stations` | list of Addresses | Stations the being holds. |
|
|
58
|
+
| `data.lore` | list of Addresses | Lore concerning this being — the law it lives under, the customs it is subject to, the traditions it was raised in. |
|
|
59
|
+
| `data.culture` | an Address | The being's primary culture, as a culture lore note. |
|
|
60
|
+
| `data.homes` | list of Addresses | Places the being calls home. |
|
|
61
|
+
| `data.affiliations` | a standing map keyed by Address, or a list of Addresses | Bodies the being belongs to, keyed by Address, each entry holding the standing it holds there — `rank`, required, a level on that body's own ladder, and `office`, optional, a post that body names. |
|
|
62
|
+
| `data.socialTies` | a map keyed by Address | Defining ties directed from this being to others: `patron` (Supports the subject from greater power, wealth, or status.); `friend` (Supports the subject out of goodwill.); `dependent` (Relies on the subject's support or protection.); `acquaintance` (Knows the subject without a strong disposition.); `rival` (Opposes the subject without implacable hostility.); `nemesis` (Opposes the subject personally and implacably.) |
|
|
63
|
+
| `data.gender` | string | One of `male`, `female`, `nonbinary`, `none` or `other`. `none` says the being has no gender; an absent field says its gender is unrecorded. |
|
|
64
|
+
| `data.species` | an Address | The being's species, as a lore note. |
|
|
65
|
+
| `data.born` | string | When the being was born — a date, or `unknown` where the birth is unrecorded. Absent, the being was never born. |
|
|
66
|
+
| `data.calendar` | an Address | Calendar used to display this being's dates. |
|
|
67
|
+
| `data.died` | string | When the being died — a date, or `unknown` where the death is unrecorded. Absent, the being is alive. |
|
|
68
|
+
| `data.age` | string | Age in years, stated only to override what `born` says — `34`, or `~34` for an estimate. Unstated beside a dated `born` it is computed; unstated beside an unknown or absent `born` the age is unknown. |
|
|
69
|
+
| `data.ageYears` | number | Written by the compiler beside an `age` estimate — the `~` stripped, the magnitude alone. A note never authors this. |
|
|
70
|
+
| `data.height` | metres or feet and inches | Height in metres or feet and inches. |
|
|
71
|
+
| `data.weight` | kilograms or pounds | Body weight in kilograms or pounds. |
|
|
72
|
+
| `data.frame` | string | Relative frame — one of `scant`, `light`, `medium`, `heavy` or `massive`. |
|
|
73
|
+
| `data.appearance.eye_color` | string | Eye colour. |
|
|
74
|
+
| `data.appearance.hair_color` | string | Hair colour. |
|
|
75
|
+
| `data.appearance.skin_color` | string | Skin colour. |
|
|
76
|
+
| `data.appearance.complexion` | string or list | The skin's condition — one value, or several, because a face carries more than one at once. A single value means a list of one. |
|
|
77
|
+
| `data.appearance.extra_features` | list | Anything else a stranger would notice. |
|
|
61
78
|
|
|
62
79
|
**System mappings**
|
|
63
80
|
|
|
@@ -56,6 +56,7 @@ import { emitDiagnostic } from "./diagnostics.mjs";
|
|
|
56
56
|
// The `{#appearance}` / `{#dossier}` convention is the note format's, so the
|
|
57
57
|
// extraction is shared; which field a section lands in stays the system's.
|
|
58
58
|
export { extractAnchorSection, renderSection } from "./anchored-sections.mjs";
|
|
59
|
+
import { misplacedAnchorSection } from "./anchored-sections.mjs";
|
|
59
60
|
import { BasePackCompiler } from "./base-compiler.mjs";
|
|
60
61
|
import { beingDefaultArt } from "./art-fields.mjs";
|
|
61
62
|
import { contentPackage } from "./content-package.mjs";
|
|
@@ -472,6 +473,28 @@ export class SystemActorCompiler extends BasePackCompiler {
|
|
|
472
473
|
return { img, token: this.artPath(fm, "tokenIcon") ?? img };
|
|
473
474
|
}
|
|
474
475
|
|
|
476
|
+
/**
|
|
477
|
+
* Report an `{#appearance}` or `{#dossier}` anchor on a heading
|
|
478
|
+
* {@link extractAnchorSection} cannot read, rather than letting the field
|
|
479
|
+
* it feeds compile blank with nothing to say why.
|
|
480
|
+
*
|
|
481
|
+
* @param {string} body - The note body.
|
|
482
|
+
* @param {string} anchorId - `"appearance"` or `"dossier"`.
|
|
483
|
+
* @param {string} ctx - The actor's label, for the message.
|
|
484
|
+
* @returns {void}
|
|
485
|
+
*/
|
|
486
|
+
checkAnchoredSection(body, anchorId, ctx) {
|
|
487
|
+
const misplaced = misplacedAnchorSection(body, anchorId, this.currentNote?.bodyLine);
|
|
488
|
+
if (!misplaced) return;
|
|
489
|
+
this.noteError(
|
|
490
|
+
`${ctx}: {#${anchorId}} sits on a heading below the top level, and only ` +
|
|
491
|
+
`an H1 feeds the ${anchorId} field — move the heading to the top level ` +
|
|
492
|
+
"or drop the anchor",
|
|
493
|
+
{ line: misplaced.line },
|
|
494
|
+
);
|
|
495
|
+
this.errorCount++;
|
|
496
|
+
}
|
|
497
|
+
|
|
475
498
|
/**
|
|
476
499
|
* Which `(actor, subType:identity)` each resolved entry claimed, and the
|
|
477
500
|
* entry that claimed it first.
|