@heroiclands/package-build 22.21.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.md +191 -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 +36 -18
- 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 +224 -46
- package/docs/reference/note-types.md +21 -4
- 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 +47 -64
- package/engine/frontmatter.mjs +3 -3
- package/engine/heading-attributes.mjs +308 -0
- package/engine/helpers.mjs +14 -13
- package/engine/homepage.mjs +8 -8
- package/engine/index.mjs +3 -0
- package/engine/infobox-render.mjs +15 -3
- package/engine/infobox.mjs +83 -8
- package/engine/journals.mjs +36 -17
- package/engine/macros.mjs +1 -1
- package/engine/markdown-expressions.mjs +178 -53
- package/engine/note-dates.mjs +49 -0
- package/engine/note-frontmatter.mjs +7 -16
- package/engine/note-vocabulary.mjs +172 -37
- 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/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 +18 -2
- package/engine/site-preview.mjs +18 -7
- package/engine/social-ties.mjs +52 -14
- package/engine/sql-tables.mjs +66 -9
- package/engine/standings.mjs +89 -6
- package/engine/system-block.mjs +12 -14
- package/engine/web-wikilinks.mjs +48 -1
- package/hm3/actors.mjs +6 -3
- package/manifest.mjs +13 -0
- package/package.json +1 -1
- package/sohl/actors.mjs +22 -99
- 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 +82 -6
- 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/standings.d.mts +21 -3
- package/types/engine/system-block.d.mts +11 -12
- package/types/engine/web-wikilinks.d.mts +3 -1
- package/types/hm3/actors.d.mts +3 -3
- package/types/sohl/actors.d.mts +6 -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
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,196 @@
|
|
|
1
1
|
# @heroiclands/package-build
|
|
2
2
|
|
|
3
|
+
## 22.22.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
**Diagnostics**
|
|
8
|
+
|
|
9
|
+
- A dead `#anchor` now fails the website build, matching the compendium build
|
|
10
|
+
and the link checker — a link that used to publish a dead section quietly now
|
|
11
|
+
reports it.
|
|
12
|
+
- A note with more than one problem in its body now reports every one of them
|
|
13
|
+
in a single build, instead of only the first.
|
|
14
|
+
|
|
15
|
+
**The book**
|
|
16
|
+
|
|
17
|
+
- A page link in a book now goes where it says, and one into a package the book
|
|
18
|
+
depends on reaches that package's pages; the address they are built from is
|
|
19
|
+
`package.json`'s `homepage`, which a book now needs.
|
|
20
|
+
- A note written for the next author — a `markdownlint` pragma, an aside about a
|
|
21
|
+
quoted notice — no longer appears on the page.
|
|
22
|
+
- A box written inside a GM-only section prints as a box.
|
|
23
|
+
- A box's title prints as written, emphasis included, and one carrying a bracket,
|
|
24
|
+
a `#` or a `$` no longer stops the book being made.
|
|
25
|
+
- Two captions sharing one id each print their own block once, under their own
|
|
26
|
+
number.
|
|
27
|
+
- A link that resolves nowhere now fails the book build, as it already fails the
|
|
28
|
+
compendium and website builds, and the book is still written so the page
|
|
29
|
+
carrying the link can be found.
|
|
30
|
+
- Markup the book cannot set is reported at the file and line it is written on
|
|
31
|
+
rather than printed on the page, in a title page and a prose file as well as in
|
|
32
|
+
a note.
|
|
33
|
+
- A table query that selects no rows now says so, and names the line, where it
|
|
34
|
+
read `[object Object]`.
|
|
35
|
+
- A link the book cannot resolve names the line and column it is written on.
|
|
36
|
+
|
|
37
|
+
**Affiliations**
|
|
38
|
+
|
|
39
|
+
- A rank with no title or no description is now reported instead of shipping, so
|
|
40
|
+
a member's standing can no longer resolve to a blank on a sheet or a page.
|
|
41
|
+
- A rank level that is not a whole number, and a key a rank does not take, are
|
|
42
|
+
reported too, each naming the line the rank is written on.
|
|
43
|
+
- Every problem on one rank is reported at once, so a ladder is fixed in a single
|
|
44
|
+
pass.
|
|
45
|
+
- A rank needs only its level, title and description; the lore note is for a
|
|
46
|
+
standing that needs more said about it than a description can hold.
|
|
47
|
+
- An affiliation's rank ladder now appears on its page, alongside its offices,
|
|
48
|
+
ordered by level.
|
|
49
|
+
- A note's `lore`, `parents`, `domains` and `economy` lists now reach the page
|
|
50
|
+
wherever they are authored, instead of vanishing silently.
|
|
51
|
+
- A being's entry in a body states the rung it stands on — an ordinary member is
|
|
52
|
+
1, someone cast out is 0 — and an entry that states none is reported.
|
|
53
|
+
- An office is not a rung, so an entry naming a post and no standing is reported
|
|
54
|
+
with the rest of them.
|
|
55
|
+
- A body that gives two rungs one level is reported on the second, so what a
|
|
56
|
+
standing is called does not depend on which rung is written first.
|
|
57
|
+
|
|
58
|
+
**Authoring**
|
|
59
|
+
|
|
60
|
+
- A note's frontmatter accepts only the documented keys; an unrecognised one is an error at its own line, naming every accepted key and the one it was probably meant to be.
|
|
61
|
+
- A page takes its heading from the note's name, so a homepage stating a title any other way publishes the name its package manifest carries.
|
|
62
|
+
- Active Effects and document flags are read from the game-system block that carries them, so a note stating either outside one ships a document without them.
|
|
63
|
+
- Info boxes, warning boxes and captions can be written again: any note using
|
|
64
|
+
one failed its build with an error about a secret block it did not contain.
|
|
65
|
+
- A note may now mix boxes, captions and GM-only sections freely, and a box
|
|
66
|
+
inside a GM-only section stays inside it.
|
|
67
|
+
- A `:::` line that closes nothing says so plainly instead of naming the wrong
|
|
68
|
+
construct.
|
|
69
|
+
- An image may be sized `full-width`, and a float takes a named corner — both
|
|
70
|
+
always worked and neither was written down.
|
|
71
|
+
|
|
72
|
+
**Beings**
|
|
73
|
+
|
|
74
|
+
- A being's complexion can now hold several values, because a face is weathered
|
|
75
|
+
and ruddy at once rather than only one of them.
|
|
76
|
+
- Several of them read as one phrase on a sheet or a page — "weathered, ruddy and
|
|
77
|
+
scarred complexion" — rather than saying the word once per value.
|
|
78
|
+
- A being may be recorded as `nonbinary`, or as having no gender at all, where
|
|
79
|
+
before only `male`, `female` and `other` were named.
|
|
80
|
+
- A frame is `scant`, `light`, `medium`, `heavy` or `massive`. The reference named
|
|
81
|
+
a `heavy` frame `large`, which nothing wrote.
|
|
82
|
+
- A being's body and movement are authored under `sohl.system`, and its
|
|
83
|
+
attributes and skills as `sohl.items` entries; a note writing either
|
|
84
|
+
elsewhere in the block is reported.
|
|
85
|
+
|
|
86
|
+
**Actors**
|
|
87
|
+
|
|
88
|
+
- An `{#appearance}` or `{#dossier}` anchor written on a heading below the top
|
|
89
|
+
level now fails the build naming the file and the line, instead of compiling
|
|
90
|
+
a blank appearance or biography with nothing to say why.
|
|
91
|
+
|
|
92
|
+
**Dates**
|
|
93
|
+
|
|
94
|
+
- A birth or death date written without quotes is reported where a day's
|
|
95
|
+
trailing zero could have been lost, naming both days the value could mean. A
|
|
96
|
+
calendar's epoch and a moon's new day read the same way.
|
|
97
|
+
|
|
98
|
+
**Definition lists**
|
|
99
|
+
|
|
100
|
+
- Every package now ships a definition-list style, so a term and its definition
|
|
101
|
+
line up in two columns with the terms sharing one right edge.
|
|
102
|
+
- A narrow journal window stacks each term above its own definition instead of
|
|
103
|
+
cramping both into columns, and widening the window restores them.
|
|
104
|
+
- A term is bold wherever it appears, so writing one needs no `**` around it.
|
|
105
|
+
- Infobox rows in the book sit against the same right edge, with space between
|
|
106
|
+
rows rather than run together.
|
|
107
|
+
|
|
108
|
+
**Packages**
|
|
109
|
+
|
|
110
|
+
- `packageBuild.baseStyles: false` declines the shared style for a package that
|
|
111
|
+
wants the surface entirely to itself.
|
|
112
|
+
|
|
113
|
+
**Named blocks**
|
|
114
|
+
|
|
115
|
+
- A `:::secret`, `:::info` or `:::warn` block now takes a title of its own, so a
|
|
116
|
+
GM passage can say "For the GM" or "If they ask about the harbour" instead of
|
|
117
|
+
one fixed word.
|
|
118
|
+
- A block also takes an id, classes and other attributes, so a note can link to
|
|
119
|
+
one and a package can style a particular kind its own way.
|
|
120
|
+
- Without a title a block heads itself **Secret**, **Info** or **Warn**. A
|
|
121
|
+
warning that used to head "Warning" now heads "Warn", and a secret that used to
|
|
122
|
+
head "GM note" now heads "Secret"; writing `title="GM note"` keeps the old
|
|
123
|
+
wording.
|
|
124
|
+
- A secret on a web page now opens with its own title rather than the word
|
|
125
|
+
"Spoiler", and carries its kind so a site can style it.
|
|
126
|
+
- A block's colours come from a stylesheet rather than from the block itself,
|
|
127
|
+
so a light panel no longer keeps a light background against dark page text.
|
|
128
|
+
- A misspelled block name is now reported instead of being left in the page as
|
|
129
|
+
the literal `:::name` an author typed.
|
|
130
|
+
- One malformed block no longer stops every other block in the same note from
|
|
131
|
+
rendering.
|
|
132
|
+
- A box written inside a GM-only section stays inside it, and a box inside
|
|
133
|
+
another box says so rather than rendering as a muddle.
|
|
134
|
+
|
|
135
|
+
**Configuration**
|
|
136
|
+
|
|
137
|
+
- A `compatibility.verified` build below the package's own `compatibility.minimum`
|
|
138
|
+
is now reported instead of shipping a manifest that claims a build was verified
|
|
139
|
+
against a floor it cannot install on.
|
|
140
|
+
- The same check applies to a declared system's and a related package's own
|
|
141
|
+
`compatibility`, naming the file and line of the offending `verified`.
|
|
142
|
+
|
|
143
|
+
### Patch Changes
|
|
144
|
+
|
|
145
|
+
**Lists**
|
|
146
|
+
|
|
147
|
+
- A list item opened with `- [ ]` or `- [x]` reads as its own words everywhere
|
|
148
|
+
— on the website, in the compendium, and in the book — never as a checkbox.
|
|
149
|
+
- Writing one that way is reported at its own line, naming the ordinary list
|
|
150
|
+
it belongs in.
|
|
151
|
+
|
|
152
|
+
**Footnotes**
|
|
153
|
+
|
|
154
|
+
- A footnote referenced inside a box or a caption now prints and numbers with
|
|
155
|
+
the rest of the note instead of showing as literal text.
|
|
156
|
+
- A definition written below the top level of a note is reported, as is a
|
|
157
|
+
reference nothing defines.
|
|
158
|
+
- A definition no reference uses is reported rather than dropped from the page.
|
|
159
|
+
|
|
160
|
+
**Named blocks and captions**
|
|
161
|
+
|
|
162
|
+
- A heading that would open a page of its own is reported when it is written
|
|
163
|
+
inside a box or a caption.
|
|
164
|
+
|
|
165
|
+
**Headings**
|
|
166
|
+
|
|
167
|
+
- A heading carrying classes or attributes beside its anchor now anchors on the
|
|
168
|
+
anchor alone, so a link written to it resolves on every surface.
|
|
169
|
+
- A Foundry journal and a web page carry a heading's classes and attributes onto
|
|
170
|
+
the heading, and no surface prints the braces.
|
|
171
|
+
- A heading whose braces hold something other than an attribute block is
|
|
172
|
+
reported at its own line instead of being typeset.
|
|
173
|
+
- `.secret` on the heading that opens a section makes its Foundry page the
|
|
174
|
+
GM's alone.
|
|
175
|
+
- A withheld section reads as a spoiler on the web and a labelled block in the
|
|
176
|
+
book, which conceal it rather than withhold it.
|
|
177
|
+
- A heading refuses `id=`, `class=` and any key beginning `on`, as a named block
|
|
178
|
+
does, so no note writes an event handler onto a page.
|
|
179
|
+
|
|
180
|
+
**Page lists**
|
|
181
|
+
|
|
182
|
+
- A note can now write a page list: name a tag, and the pages carrying it come out as a list of links.
|
|
183
|
+
- The list reaches a reader in Foundry, on the website and in the book alike, from the one directive.
|
|
184
|
+
- A page joins the list by carrying its tag and leaves by losing it, so a landing page stays true as notes are written.
|
|
185
|
+
- A list can print each page's description beside its link, and can be restricted to one note type.
|
|
186
|
+
- A tag that no page carries is reported with the tag it looked for, rather than printing nothing.
|
|
187
|
+
|
|
188
|
+
**Beings**
|
|
189
|
+
|
|
190
|
+
- A being's `socialTies` map now builds without the two findings it always
|
|
191
|
+
produced, so a patron, friend, dependent, acquaintance, rival or nemesis
|
|
192
|
+
can be written.
|
|
193
|
+
|
|
3
194
|
## 22.21.0
|
|
4
195
|
|
|
5
196
|
### Minor Changes
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of the Song of Heroic Lands (SoHL) system for Foundry VTT.
|
|
3
|
+
* Copyright (c) 2024-2026 Tom Rodriguez ("Toasty") — <toasty@heroiclands.org>
|
|
4
|
+
*
|
|
5
|
+
* This work is licensed under the GNU General Public License v3.0 (GPLv3).
|
|
6
|
+
* You may copy, modify, and distribute it under the terms of that license.
|
|
7
|
+
*
|
|
8
|
+
* For full terms, see the LICENSE.md file in the project root or visit:
|
|
9
|
+
* https://www.gnu.org/licenses/gpl-3.0.html
|
|
10
|
+
*
|
|
11
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* The base stylesheet every package ships, staged into the package root and
|
|
16
|
+
* named first in the manifest's `styles`.
|
|
17
|
+
*
|
|
18
|
+
* It carries only what every package needs for its compiled content to be
|
|
19
|
+
* legible, which today is the definition list: an infobox emits one, and so
|
|
20
|
+
* does the ordinary markdown `Term` / `: definition` syntax. A package keeps
|
|
21
|
+
* its own stylesheet for its own needs and overrides anything here from it.
|
|
22
|
+
*
|
|
23
|
+
* `packageBuild.baseStyles: false` declines it.
|
|
24
|
+
*
|
|
25
|
+
* **The scope is the content, not the interface.** Foundry styles `dl dt` and
|
|
26
|
+
* `dl dd` itself, application-wide, and a package stylesheet loads into a later
|
|
27
|
+
* cascade layer than those rules sit in — so an unscoped `dl` here would
|
|
28
|
+
* restyle every definition list in Foundry's own chrome and in every other
|
|
29
|
+
* package's sheets. The four roots are where compiled content renders: a
|
|
30
|
+
* journal page, a journal entry, an enriched field, and the editor.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/*
|
|
34
|
+
* Definition lists, in two states of one style.
|
|
35
|
+
*
|
|
36
|
+
* **Wide.** Two columns, the term right-aligned against a fixed gutter so every
|
|
37
|
+
* term in the list shares one right edge, the definition in the remaining
|
|
38
|
+
* space. The fixed track is what produces the shared edge; a `max-content`
|
|
39
|
+
* track cannot, because each row then sizes itself.
|
|
40
|
+
*
|
|
41
|
+
* **Narrow.** Stacked, the term above its own definition, with space between
|
|
42
|
+
* pairs and none between a term and the definition it belongs to.
|
|
43
|
+
*
|
|
44
|
+
* **The measurement is the list, not the screen.** A Foundry journal is a
|
|
45
|
+
* resizable window, usually far narrower than the viewport, so a viewport media
|
|
46
|
+
* query would hold a journal stacked while the screen said otherwise — or cramp
|
|
47
|
+
* it into two columns at 300px. `container-type: inline-size` on the `dl` and a
|
|
48
|
+
* `@container` query on its children measure the list itself, and give the same
|
|
49
|
+
* two states on a page, in a window and in a sidebar.
|
|
50
|
+
*
|
|
51
|
+
* The narrow state is reached by spanning both tracks rather than by changing
|
|
52
|
+
* the template, because an element cannot query the container it establishes:
|
|
53
|
+
* the `dl` holds the grid and the container, so only `dt` and `dd` can respond.
|
|
54
|
+
* Row spacing is the definition's bottom margin rather than the grid's
|
|
55
|
+
* `row-gap` for the same reason — `row-gap` sits on the `dl`, and the two
|
|
56
|
+
* states need different values.
|
|
57
|
+
*
|
|
58
|
+
* The three tunable values are custom properties, so a package adjusts the term
|
|
59
|
+
* column, the gutter or the stacking point without copying the rule.
|
|
60
|
+
*/
|
|
61
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) dl {
|
|
62
|
+
display: grid;
|
|
63
|
+
/* `minmax(0, …)` keeps the track at its full width wherever there is room
|
|
64
|
+
* and lets it shrink rather than overflow where there is not. */
|
|
65
|
+
grid-template-columns: minmax(0, var(--pb-deflist-term-width, 11rem)) 1fr;
|
|
66
|
+
column-gap: var(--pb-deflist-gutter, 2rem);
|
|
67
|
+
container-type: inline-size;
|
|
68
|
+
container-name: pb-deflist;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) dl > dt {
|
|
72
|
+
grid-column: 1;
|
|
73
|
+
/* The one rule that holds on every surface, so an author writes the term
|
|
74
|
+
* and nothing else rather than bolding it by hand. */
|
|
75
|
+
font-weight: 600;
|
|
76
|
+
text-align: right;
|
|
77
|
+
line-height: 1.6;
|
|
78
|
+
margin: 0;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) dl > dd {
|
|
82
|
+
grid-column: 2;
|
|
83
|
+
margin: 0 0 var(--pb-deflist-row-gap, 1.5rem);
|
|
84
|
+
line-height: 1.6;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) dl > dd:last-child {
|
|
88
|
+
margin-bottom: 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/*
|
|
92
|
+
* Stacked, below the width two columns need: the term column, the gutter, and
|
|
93
|
+
* enough left for a definition to read as prose rather than as a word per line.
|
|
94
|
+
*
|
|
95
|
+
* Named, so the query resolves against a list this sheet set up rather than
|
|
96
|
+
* against whichever ancestor container happens to be nearest.
|
|
97
|
+
*/
|
|
98
|
+
@container pb-deflist (max-width: 32rem) {
|
|
99
|
+
dt {
|
|
100
|
+
grid-column: 1 / -1;
|
|
101
|
+
text-align: left;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
dd {
|
|
105
|
+
grid-column: 1 / -1;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/*
|
|
110
|
+
* Named body blocks. `:::info` and `:::warn` emit a `<section>` carrying the
|
|
111
|
+
* block's name as its class, so the appearance belongs here rather than in an
|
|
112
|
+
* inline `style` a consumer cannot override.
|
|
113
|
+
*
|
|
114
|
+
* `secret` is deliberately absent: Foundry styles `section.secret` itself and
|
|
115
|
+
* owns the reveal control on it, and a rule here would fight both.
|
|
116
|
+
*
|
|
117
|
+
* Every colour is a custom property, so a package overrides one from its own
|
|
118
|
+
* sheet without restating the layout.
|
|
119
|
+
*/
|
|
120
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
|
|
121
|
+
:is(section.info, section.warn) {
|
|
122
|
+
border-left: 3px solid var(--pb-block-edge);
|
|
123
|
+
background: var(--pb-block-fill);
|
|
124
|
+
color: var(--pb-block-text, inherit);
|
|
125
|
+
padding: 0.5em 0.8em;
|
|
126
|
+
margin: 0.75em 0;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
|
|
130
|
+
:is(section.info, section.warn)
|
|
131
|
+
> strong:first-child {
|
|
132
|
+
color: var(--pb-block-edge);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) section.info {
|
|
136
|
+
--pb-block-edge: #2f6f9f;
|
|
137
|
+
--pb-block-fill: #eef6fb;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) section.warn {
|
|
141
|
+
--pb-block-edge: #9a6700;
|
|
142
|
+
--pb-block-fill: #fff5db;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/*
|
|
146
|
+
* A light panel keeps a dark foreground whatever the surrounding theme does to
|
|
147
|
+
* the inherited colour, because the fill does not follow it.
|
|
148
|
+
*/
|
|
149
|
+
@media (prefers-color-scheme: dark) {
|
|
150
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
|
|
151
|
+
:is(section.info, section.warn) {
|
|
152
|
+
--pb-block-text: #1b1b1b;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) section.info {
|
|
156
|
+
--pb-block-edge: #1d4f75;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) section.warn {
|
|
160
|
+
--pb-block-edge: #7a5100;
|
|
161
|
+
}
|
|
162
|
+
}
|
package/bin/content-commands.mjs
CHANGED
|
@@ -70,6 +70,7 @@ import { lintContentCharset } from "../engine/content-charset.mjs";
|
|
|
70
70
|
import { lintContentHtml } from "../engine/content-html.mjs";
|
|
71
71
|
import { lintContentIcons } from "../engine/content-icons.mjs";
|
|
72
72
|
import { lintContentImages } from "../engine/content-images.mjs";
|
|
73
|
+
import { lintContentTaskLists } from "../engine/content-tasklists.mjs";
|
|
73
74
|
import {
|
|
74
75
|
declaredSystems,
|
|
75
76
|
lintFrontmatter,
|
|
@@ -1156,6 +1157,13 @@ function lintCommand() {
|
|
|
1156
1157
|
config,
|
|
1157
1158
|
});
|
|
1158
1159
|
|
|
1160
|
+
// A list item opened with `[ ]` or `[x]` — nothing here is
|
|
1161
|
+
// miscompiled, since every surface sets the marker as the
|
|
1162
|
+
// item's own text and all three agree, so this is advisory.
|
|
1163
|
+
const taskLists = lintContentTaskLists(root, {
|
|
1164
|
+
skipDirectories: config.skipDirectories,
|
|
1165
|
+
});
|
|
1166
|
+
|
|
1159
1167
|
// Whether every empty body is a stub on purpose, and how
|
|
1160
1168
|
// finished the tree is. The counts and the oldest drafts are
|
|
1161
1169
|
// prose rather than findings: a note nobody has finished is not
|
|
@@ -1175,6 +1183,7 @@ function lintCommand() {
|
|
|
1175
1183
|
...icons.findings,
|
|
1176
1184
|
...html.findings,
|
|
1177
1185
|
...images.findings,
|
|
1186
|
+
...taskLists.findings,
|
|
1178
1187
|
...states.findings,
|
|
1179
1188
|
];
|
|
1180
1189
|
// Only an **error** fails the run. Every finding was an error
|
|
@@ -1461,14 +1470,20 @@ function linksCommand() {
|
|
|
1461
1470
|
}),
|
|
1462
1471
|
);
|
|
1463
1472
|
|
|
1473
|
+
// Worded by the shared table, like every finding below it: a
|
|
1474
|
+
// sentence written here is free to describe the same defect
|
|
1475
|
+
// differently from the build that also refuses it, which is the
|
|
1476
|
+
// drift the table exists to prevent.
|
|
1464
1477
|
for (const d of deadAnchors) {
|
|
1465
1478
|
emitDiagnostic({
|
|
1466
1479
|
file: d.note.file,
|
|
1467
1480
|
...positionOfLiteral(d.note.raw, d.text, d.occurrence),
|
|
1468
1481
|
severity: "error",
|
|
1469
|
-
message:
|
|
1470
|
-
|
|
1471
|
-
|
|
1482
|
+
message: linkFindingMessage({
|
|
1483
|
+
reason: "unknown-anchor",
|
|
1484
|
+
target: d.target,
|
|
1485
|
+
anchor: d.anchor,
|
|
1486
|
+
}),
|
|
1472
1487
|
});
|
|
1473
1488
|
}
|
|
1474
1489
|
// Every link is an address and every address must
|
|
@@ -1799,6 +1814,12 @@ function siteCommand() {
|
|
|
1799
1814
|
for (const e of result.captionErrors) {
|
|
1800
1815
|
emitDiagnostic({ ...e, severity: "error" });
|
|
1801
1816
|
}
|
|
1817
|
+
for (const e of result.headingErrors) {
|
|
1818
|
+
emitDiagnostic({ ...e, severity: "error" });
|
|
1819
|
+
}
|
|
1820
|
+
for (const e of result.footnoteErrors) {
|
|
1821
|
+
emitDiagnostic({ ...e, severity: "error" });
|
|
1822
|
+
}
|
|
1802
1823
|
// Reported the way the pack build reports the very same
|
|
1803
1824
|
// finding: `file:line:column: error: message`, path first, and
|
|
1804
1825
|
// the message from the shared table, not a
|
|
@@ -1838,6 +1859,8 @@ function siteCommand() {
|
|
|
1838
1859
|
result.expressionErrors.length ||
|
|
1839
1860
|
result.secretErrors.length ||
|
|
1840
1861
|
result.captionErrors.length ||
|
|
1862
|
+
result.headingErrors.length ||
|
|
1863
|
+
result.footnoteErrors.length ||
|
|
1841
1864
|
result.wikiErrors.length ||
|
|
1842
1865
|
result.imageErrors.length
|
|
1843
1866
|
) {
|
package/bin/package-build.mjs
CHANGED
|
@@ -93,6 +93,7 @@ import { DEPLOY_ROOT } from "../engine/site-config.mjs";
|
|
|
93
93
|
import { compilesFoundryDocuments } from "../content-config.mjs";
|
|
94
94
|
import { loadPackConfig, packConfigPath, resolveConfigFile } from "../engine/pack-config.mjs";
|
|
95
95
|
import { cleanBuildArtifacts, resetStage, stageAssets } from "../stage.mjs";
|
|
96
|
+
import { baseStyleStageEntry } from "../engine/base-styles.mjs";
|
|
96
97
|
import { buildSchemaArtifact } from "../engine/schema-extract.mjs";
|
|
97
98
|
import { emitCalendarArtifacts } from "../engine/calendar-artifacts.mjs";
|
|
98
99
|
import { dateFromCalendar, dateToCalendar } from "../engine/date-conversion.mjs";
|
|
@@ -424,7 +425,7 @@ function assetsCommand() {
|
|
|
424
425
|
builder: (y) => y,
|
|
425
426
|
handler: handler(async () => {
|
|
426
427
|
const config = loadPackageBuildConfig();
|
|
427
|
-
if (!config.assets.length) {
|
|
428
|
+
if (!config.assets.length && !config.baseStyles) {
|
|
428
429
|
console.log("package-build: no `packageBuild.assets` declared; nothing to stage.");
|
|
429
430
|
return;
|
|
430
431
|
}
|
|
@@ -462,10 +463,22 @@ function assetsCommand() {
|
|
|
462
463
|
from,
|
|
463
464
|
path.join(config.stageDir, to),
|
|
464
465
|
]);
|
|
465
|
-
|
|
466
|
+
let { entries: count, files } = stageAssets(entries, {
|
|
466
467
|
cwd: config.rootDir,
|
|
467
468
|
transform,
|
|
468
469
|
});
|
|
470
|
+
|
|
471
|
+
// Staged in its own pass, without the repository's transform: the
|
|
472
|
+
// sheet is this toolchain's file rather than one of the
|
|
473
|
+
// repository's assets, and a `transform` written to retheme a
|
|
474
|
+
// package's own SVGs has no business being handed it.
|
|
475
|
+
if (config.baseStyles) {
|
|
476
|
+
const staged = stageAssets([baseStyleStageEntry(config.stageDir)], {
|
|
477
|
+
cwd: config.rootDir,
|
|
478
|
+
});
|
|
479
|
+
count += staged.entries;
|
|
480
|
+
files += staged.files;
|
|
481
|
+
}
|
|
469
482
|
console.log(`✅ Static assets staged (${count} entries, ${files} files).`);
|
|
470
483
|
}),
|
|
471
484
|
};
|
|
@@ -9,9 +9,12 @@ import { formatGenerated } from "../engine/format-generated.mjs";
|
|
|
9
9
|
import { loadContentFormat } from "../engine/content-format.mjs";
|
|
10
10
|
import {
|
|
11
11
|
BEING_ARCHETYPES,
|
|
12
|
+
NOTE_TOP_LEVEL_FIELDS,
|
|
12
13
|
NOTE_VOCABULARY,
|
|
13
14
|
SHARED_DATA_FIELDS,
|
|
14
15
|
} from "../engine/note-vocabulary.mjs";
|
|
16
|
+
import { EXPRESSION_HELPERS } from "../engine/markdown-expressions.mjs";
|
|
17
|
+
import { PAGE_LIST_ATTRIBUTES } from "../engine/page-lists.mjs";
|
|
15
18
|
|
|
16
19
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
17
20
|
const output = path.join(root, "docs/reference/note-types.md");
|
|
@@ -39,6 +42,16 @@ const rows = [
|
|
|
39
42
|
"",
|
|
40
43
|
"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.",
|
|
41
44
|
"",
|
|
45
|
+
"## Top-level keys",
|
|
46
|
+
"",
|
|
47
|
+
"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.",
|
|
48
|
+
"",
|
|
49
|
+
"| Key | Meaning |",
|
|
50
|
+
"| --- | --- |",
|
|
51
|
+
...NOTE_TOP_LEVEL_FIELDS.map(
|
|
52
|
+
(field) => `| \`${cell(field.name)}\` | ${cell(field.describe)} |`,
|
|
53
|
+
),
|
|
54
|
+
"",
|
|
42
55
|
"## Shared `data` fields",
|
|
43
56
|
"",
|
|
44
57
|
"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.",
|
|
@@ -106,7 +119,61 @@ if (process.argv.includes("--check")) {
|
|
|
106
119
|
process.exitCode = 1;
|
|
107
120
|
}
|
|
108
121
|
} else {
|
|
109
|
-
|
|
122
|
+
fs.writeFileSync(output, result);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// The two tables `docs/authoring/links-and-markup.md` generates: every helper
|
|
126
|
+
// an expression may call, and every attribute a page-list fence takes. Both are
|
|
127
|
+
// written from the one list the engine reads, so neither document can fall
|
|
128
|
+
// behind a new entry. Replaced in one pass and written once, so a `--check` run
|
|
129
|
+
// names the file rather than one of the tables in it.
|
|
130
|
+
const markupPath = path.join(root, "docs/authoring/links-and-markup.md");
|
|
131
|
+
const markup = fs.readFileSync(markupPath, "utf8");
|
|
132
|
+
const GENERATED_TABLES = [
|
|
133
|
+
{
|
|
134
|
+
marker: "expression-helpers",
|
|
135
|
+
rows: [
|
|
136
|
+
"| Helper | Parameters | What it gives you |",
|
|
137
|
+
"| ------ | ---------- | ----------------- |",
|
|
138
|
+
...Object.entries(EXPRESSION_HELPERS).map(
|
|
139
|
+
([name, { params, summary }]) =>
|
|
140
|
+
`| \`${name}\` | \`${params}\` | ${cell(summary)} |`,
|
|
141
|
+
),
|
|
142
|
+
],
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
marker: "page-list-attributes",
|
|
146
|
+
rows: [
|
|
147
|
+
"| Attribute | Value | Default | What it does |",
|
|
148
|
+
"| --------- | ----- | ------- | ------------ |",
|
|
149
|
+
...Object.entries(PAGE_LIST_ATTRIBUTES).map(
|
|
150
|
+
([name, { value, default: fallback, summary }]) =>
|
|
151
|
+
`| \`${name}\` | ${cell(value)} | ${cell(fallback)} | ${cell(summary)} |`,
|
|
152
|
+
),
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
];
|
|
156
|
+
let markupBody = markup;
|
|
157
|
+
for (const { marker, rows } of GENERATED_TABLES) {
|
|
158
|
+
const region = new RegExp(`<!-- ${marker}:start -->[\\s\\S]*?<!-- ${marker}:end -->`);
|
|
159
|
+
if (!region.test(markupBody)) {
|
|
160
|
+
throw new Error(`The ${marker} reference requires its generation markers`);
|
|
161
|
+
}
|
|
162
|
+
markupBody = markupBody.replace(
|
|
163
|
+
region,
|
|
164
|
+
`<!-- ${marker}:start -->\n\n${rows.join("\n")}\n\n<!-- ${marker}:end -->`,
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
const updatedMarkup = await formatGenerated(markupBody, markupPath);
|
|
168
|
+
if (process.argv.includes("--check")) {
|
|
169
|
+
if (markup !== updatedMarkup) {
|
|
170
|
+
console.error(
|
|
171
|
+
"docs/authoring/links-and-markup.md: error: run node ci/content-format-reference.mjs to refresh its generated tables",
|
|
172
|
+
);
|
|
173
|
+
process.exitCode = 1;
|
|
174
|
+
}
|
|
175
|
+
} else {
|
|
176
|
+
fs.writeFileSync(markupPath, updatedMarkup);
|
|
110
177
|
}
|
|
111
178
|
|
|
112
179
|
const detailsPath = path.join(root, "docs/reference/format-details.md");
|
package/config.mjs
CHANGED
|
@@ -65,6 +65,7 @@ const SECTION_KEYS = [
|
|
|
65
65
|
"stageDir",
|
|
66
66
|
"assets",
|
|
67
67
|
"assetTransform",
|
|
68
|
+
"baseStyles",
|
|
68
69
|
"manifest",
|
|
69
70
|
"manifestFlags",
|
|
70
71
|
"schema",
|
|
@@ -205,6 +206,23 @@ function requireNonEmptyString(value, where) {
|
|
|
205
206
|
return /** @type {string} */ (value);
|
|
206
207
|
}
|
|
207
208
|
|
|
209
|
+
/**
|
|
210
|
+
* A declared switch, with the behaviour that applies when it is not declared.
|
|
211
|
+
*
|
|
212
|
+
* A truthy string is refused rather than coerced: `baseStyles: "false"` reads
|
|
213
|
+
* as off and would be on, which is the one wrong answer available here.
|
|
214
|
+
*
|
|
215
|
+
* @param {unknown} value - What was declared, or `undefined`.
|
|
216
|
+
* @param {boolean} fallback - The undeclared behaviour.
|
|
217
|
+
* @param {string} where - Dotted path, for the error.
|
|
218
|
+
* @returns {boolean} The setting.
|
|
219
|
+
*/
|
|
220
|
+
function requireBoolean(value, fallback, where) {
|
|
221
|
+
if (value === undefined) return fallback;
|
|
222
|
+
if (typeof value !== "boolean") fail(where, "must be true or false");
|
|
223
|
+
return value;
|
|
224
|
+
}
|
|
225
|
+
|
|
208
226
|
/**
|
|
209
227
|
* One staging copy: a source path in the repository, and where it lands under
|
|
210
228
|
* the staged package root.
|
|
@@ -537,6 +555,9 @@ function normalizeExceptions(value, field, where) {
|
|
|
537
555
|
* @property {string} stageDir The staged package root, relative to
|
|
538
556
|
* `rootDir`. Every asset `to:` lands under it.
|
|
539
557
|
* @property {readonly Readonly<AssetSpec>[]} assets
|
|
558
|
+
* @property {boolean} baseStyles Whether the package takes the shared base
|
|
559
|
+
* stylesheet, staged into `stageDir` and
|
|
560
|
+
* named first in the manifest's `styles`.
|
|
540
561
|
* @property {Readonly<Record<string, unknown>>} manifest The manifest
|
|
541
562
|
* specification, emitted as declared.
|
|
542
563
|
* @property {string|null} manifestFlags Module to load a `flags` function
|
|
@@ -877,6 +898,11 @@ export function resolvePackageBuildConfig(shared) {
|
|
|
877
898
|
ARTIFACT_OF_KIND[/** @type {"systems"|"modules"} */ (shared.packageKind)]
|
|
878
899
|
: requireNonEmptyString(releaseInput.artifact, "packageBuild.release.artifact"),
|
|
879
900
|
assets: Object.freeze(assets),
|
|
901
|
+
// On by default, because the rules it carries are what compiled content
|
|
902
|
+
// needs to be legible and a package that declined them by omission
|
|
903
|
+
// would ship the content unstyled without anything saying so. A package
|
|
904
|
+
// that wants the surface entirely to itself says so.
|
|
905
|
+
baseStyles: requireBoolean(section.baseStyles, true, "packageBuild.baseStyles"),
|
|
880
906
|
schema: normalizeSchema(section.schema),
|
|
881
907
|
assetTransform:
|
|
882
908
|
section.assetTransform === undefined ?
|