@heroiclands/package-build 23.9.0 → 23.11.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 +33 -0
- package/README.md +2 -0
- package/bin/content-commands.mjs +197 -68
- package/docs/api.md +365 -70
- package/docs/authoring/first-note.md +10 -0
- package/docs/authoring/frontmatter.md +1 -1
- package/docs/authoring/links-and-markup.md +186 -49
- package/docs/commands.md +92 -3
- package/docs/content-format.md +10 -3
- package/docs/design.md +18 -0
- package/docs/guides/book.md +7 -0
- package/docs/guides/site.md +7 -0
- package/docs/reference/format-details.md +186 -15
- package/docs/reference/note-types.md +4 -4
- package/engine/anchors.mjs +43 -9
- package/engine/base-compiler.mjs +6 -4
- package/engine/changelog-group.mjs +58 -34
- package/engine/changelog-lint.mjs +6 -2
- package/engine/compendiums.mjs +15 -1
- package/engine/compile-corpus.mjs +14 -1
- package/engine/content-charset.mjs +13 -1
- package/engine/content-html.mjs +18 -4
- package/engine/content-icons.mjs +15 -1
- package/engine/content-images.mjs +28 -9
- package/engine/content-index.mjs +102 -23
- package/engine/content-links.mjs +68 -30
- package/engine/content-session.mjs +247 -0
- package/engine/content-sources.mjs +110 -0
- package/engine/content-spans.mjs +118 -14
- package/engine/content-tables.mjs +1 -0
- package/engine/content-tasklists.mjs +20 -4
- package/engine/diagnostics.mjs +17 -6
- package/engine/extension-attributes.mjs +28 -5
- package/engine/first-use.mjs +371 -0
- package/engine/foundry-entries.mjs +12 -9
- package/engine/generate.mjs +13 -4
- package/engine/helpers.mjs +47 -16
- package/engine/index-records.mjs +1 -0
- package/engine/index.mjs +3 -0
- package/engine/journals.mjs +9 -5
- package/engine/language-data.mjs +2 -2
- package/engine/language-rows.mjs +4 -4
- package/engine/language-terms.mjs +4 -4
- package/engine/lexical-sections.mjs +5 -5
- package/engine/markup-validation.mjs +7 -2
- package/engine/metadata-index.mjs +1 -0
- package/engine/name-markup.mjs +114 -0
- package/engine/name-rows.mjs +70 -0
- package/engine/names.mjs +460 -0
- package/engine/note-addresses.mjs +25 -4
- package/engine/path-charset.mjs +24 -1
- package/engine/pdf-render.mjs +15 -1
- package/engine/site-index.mjs +25 -2
- package/engine/sql-tables.mjs +76 -15
- package/engine/stub-lint.mjs +4 -3
- package/engine/web-wikilinks.mjs +33 -12
- package/engine/wikilink-syntax.mjs +65 -8
- package/engine/wikilinks.mjs +29 -11
- package/engine/yaml-lint.mjs +3 -5
- package/hugo-theme/layouts/partials/footer.html +2 -0
- package/hugo-theme/static/css/style.css +20 -0
- package/hugo-theme/static/js/inline-spoilers.js +23 -0
- package/package.json +1 -1
- package/types/engine/anchors.d.mts +6 -3
- package/types/engine/compendiums.d.mts +5 -1
- package/types/engine/compile-corpus.d.mts +5 -2
- package/types/engine/content-charset.d.mts +5 -1
- package/types/engine/content-html.d.mts +5 -1
- package/types/engine/content-icons.d.mts +5 -1
- package/types/engine/content-images.d.mts +5 -1
- package/types/engine/content-index.d.mts +39 -19
- package/types/engine/content-links.d.mts +22 -8
- package/types/engine/content-session.d.mts +108 -0
- package/types/engine/content-sources.d.mts +32 -0
- package/types/engine/content-spans.d.mts +8 -6
- package/types/engine/content-tasklists.d.mts +5 -1
- package/types/engine/diagnostics.d.mts +10 -0
- package/types/engine/extension-attributes.d.mts +16 -2
- package/types/engine/first-use.d.mts +31 -0
- package/types/engine/foundry-entries.d.mts +11 -4
- package/types/engine/generate.d.mts +5 -1
- package/types/engine/helpers.d.mts +30 -7
- package/types/engine/index.d.mts +1 -0
- package/types/engine/journals.d.mts +5 -2
- package/types/engine/lexical-sections.d.mts +3 -2
- package/types/engine/markup-validation.d.mts +2 -1
- package/types/engine/name-markup.d.mts +176 -0
- package/types/engine/name-rows.d.mts +35 -0
- package/types/engine/names.d.mts +356 -0
- package/types/engine/note-addresses.d.mts +3 -3
- package/types/engine/path-charset.d.mts +16 -1
- package/types/engine/site-build.d.mts +2 -2
- package/types/engine/site-index.d.mts +3 -1
- package/types/engine/sql-tables.d.mts +7 -2
- package/types/engine/stub-lint.d.mts +3 -1
- package/types/engine/wikilink-syntax.d.mts +40 -3
package/docs/design.md
CHANGED
|
@@ -21,6 +21,24 @@ An Address identifies a subject by package, system, type, and shortcode. It is i
|
|
|
21
21
|
|
|
22
22
|
The content index uses JSON Lines. A record can retain the note's full frontmatter, anchors, and outgoing links, and streams without loading one giant JSON array. It is derived, disposable, and stable for a fixed source tree and toolchain version. SQL queries and editor navigation read that index rather than guessing from filenames.
|
|
23
23
|
|
|
24
|
+
## Reference presentation and access
|
|
25
|
+
|
|
26
|
+
An indexed subject has one ordinary wikilink syntax regardless of its audience.
|
|
27
|
+
The renderer derives spoiler presentation from the actual destination, so an
|
|
28
|
+
author does not duplicate a privacy declaration on every mention. The `.show`
|
|
29
|
+
override reveals a particular reference without changing the destination's
|
|
30
|
+
audience. A separate inline `.secret` span marks authored text as a spoiler.
|
|
31
|
+
|
|
32
|
+
Concealment and access remain separate decisions. Public site and book builds
|
|
33
|
+
omit GM-only notes; a reference to an omitted destination carries no unusable
|
|
34
|
+
link. Foundry's private packs and page permissions govern destination access.
|
|
35
|
+
Web inline spoilers use a separate show/hide control beside their content, so
|
|
36
|
+
a revealed link is independently usable. Foundry and book inline spoilers are
|
|
37
|
+
visible labelled text; an inline Foundry span has no native secret-section
|
|
38
|
+
permission behavior. A spoiler in published HTML remains readable in the source. See the
|
|
39
|
+
[authoring contract](authoring/links-and-markup.md#references-to-secret-subjects)
|
|
40
|
+
for syntax and surface presentation.
|
|
41
|
+
|
|
24
42
|
## Build boundaries
|
|
25
43
|
|
|
26
44
|
The content pass reads `assets/content/` and addressable assets. It emits the content index, page source, and Foundry documents. Site rendering and book typesetting consume those outputs; their layout decisions do not become fields of a Foundry DataModel. Packaging stages only the files a Foundry manifest declares. The build checks emitted fields against the target system schema because Foundry can otherwise discard an unknown field without an error.
|
package/docs/guides/book.md
CHANGED
|
@@ -522,6 +522,13 @@ Anchors on headings are scoped to their entries, so two entries can use the
|
|
|
522
522
|
same heading anchor. The build reports an unresolved destination while
|
|
523
523
|
keeping the source available for inspection.
|
|
524
524
|
|
|
525
|
+
A reference to a GM-only note or secret heading prints with secret styling
|
|
526
|
+
and a **Secret:** label. GM-only notes remain omitted, and references to them
|
|
527
|
+
print as text without a link. `.show` removes the automatic spoiler presentation
|
|
528
|
+
of that reference, without adding the destination to the book. An authored
|
|
529
|
+
`[text]{.secret}` uses the same static inline styling. Printed text remains
|
|
530
|
+
visible; see [secret references](../authoring/links-and-markup.md#references-to-secret-subjects).
|
|
531
|
+
|
|
525
532
|
The [generated sections](../reference/format-details.md#generated-sections)
|
|
526
533
|
an entry carries — **Within**, **Governed by**, **In song and story**, the
|
|
527
534
|
event views and the rest — name notes the build chose rather than the author,
|
package/docs/guides/site.md
CHANGED
|
@@ -118,6 +118,13 @@ Wikilinks resolve through the address index. Each emitted page receives
|
|
|
118
118
|
derived `related` entries for the links its author wrote and the backlinks
|
|
119
119
|
other authors wrote to it.
|
|
120
120
|
|
|
121
|
+
A wikilink to a GM-only note or secret heading conceals its whole reference in
|
|
122
|
+
an inline show/hide spoiler. `.show` reveals the reference without changing the
|
|
123
|
+
destination's audience. GM-only pages remain omitted, so references to them
|
|
124
|
+
have no link; published secret sections retain theirs. Spoiler text remains in
|
|
125
|
+
the generated HTML and is readable in page source. See
|
|
126
|
+
[secret references](../authoring/links-and-markup.md#references-to-secret-subjects).
|
|
127
|
+
|
|
121
128
|
What lies within a place, who governs it, what an affiliation governs, the
|
|
122
129
|
works naming a note and the map from a place reach the page as
|
|
123
130
|
[generated sections](../reference/format-details.md#generated-sections): H1
|
|
@@ -339,8 +339,11 @@ infoboxes:
|
|
|
339
339
|
### Frontmatter has three regions
|
|
340
340
|
|
|
341
341
|
A HeroicLands note has opening YAML frontmatter with nonempty `shortcode` and
|
|
342
|
-
`type` values.
|
|
343
|
-
|
|
342
|
+
`type` values. The opening and closing `---` delimiters accept LF or CRLF line
|
|
343
|
+
endings. Parsing keeps the original source text and diagnostic positions;
|
|
344
|
+
malformed YAML is an error with either line ending. The charset check recommends
|
|
345
|
+
LF and reports carriage returns as warnings. Other Markdown files are ordinary
|
|
346
|
+
Markdown. A note's frontmatter has three regions:
|
|
344
347
|
|
|
345
348
|
| Region | Describes | Unknown keys |
|
|
346
349
|
| -------------------- | ------------------------------------------------------- | ------------ |
|
|
@@ -512,7 +515,10 @@ is declared, so a near miss is a finding that names what you probably meant.
|
|
|
512
515
|
|
|
513
516
|
`gm` marks a note for GM access. It is excluded from public site pages, SQL
|
|
514
517
|
tables on the public site, and the book. Foundry compiles it only to private
|
|
515
|
-
compendiums.
|
|
518
|
+
compendiums. Any note may reference it with an ordinary wikilink; the
|
|
519
|
+
reference is automatically a spoiler. The `.show` override reveals that
|
|
520
|
+
reference without changing the destination's audience or access permissions;
|
|
521
|
+
see [references to secret subjects](#references-to-secret-subjects).
|
|
516
522
|
|
|
517
523
|
**`draft` marks unfinished content.** It does two things and nothing
|
|
518
524
|
else: a link _into_ a draft note renders marked, and the note itself states that
|
|
@@ -1805,6 +1811,120 @@ name at every citation with no link edited. `[[x|]]` is labelled; `[[x]]` is not
|
|
|
1805
1811
|
It is the label that is required, not a target: the anchor-only form above
|
|
1806
1812
|
carries its pipe like every other link.
|
|
1807
1813
|
|
|
1814
|
+
#### References to secret subjects
|
|
1815
|
+
|
|
1816
|
+
Use `[[doc-divineactn|The Truth of Divine Action]]` for an indexed subject,
|
|
1817
|
+
including one with a GM-only audience. The renderer derives spoiler presentation
|
|
1818
|
+
from the resolved destination and conceals the whole reference, including its
|
|
1819
|
+
label. A public destination has no automatic concealment. An anchor inside secret
|
|
1820
|
+
content is a secret destination, including a same-note anchor reference. A
|
|
1821
|
+
link to the whole public note does not inherit a section's secrecy.
|
|
1822
|
+
|
|
1823
|
+
The website renders an inline show/hide spoiler; an omitted GM-only target
|
|
1824
|
+
retains its reference as text without a dead link. A published secret section
|
|
1825
|
+
retains its link. Foundry renders a visible inline spoiler with a **Secret:**
|
|
1826
|
+
label, with access to its destination controlled by private packs and page
|
|
1827
|
+
permissions. Books print secret styling and a **Secret:** label, retaining text
|
|
1828
|
+
without a link when the GM-only destination is omitted. `.show` bypasses the
|
|
1829
|
+
automatic spoiler presentation on each surface; an unavailable destination
|
|
1830
|
+
still has no link.
|
|
1831
|
+
|
|
1832
|
+
An immediately following `.show` attribute explicitly reveals the reference:
|
|
1833
|
+
|
|
1834
|
+
```markdown
|
|
1835
|
+
[[doc-divineactn|The Truth of Divine Action]]{.show}
|
|
1836
|
+
[[doc-magictruth|The Truth of Magic]]{.show}
|
|
1837
|
+
```
|
|
1838
|
+
|
|
1839
|
+
This override changes the reference's presentation only. It does not publish a
|
|
1840
|
+
GM-only note, alter Foundry permissions or reveal the destination's content.
|
|
1841
|
+
It is redundant for public destinations. `[text]{.secret}` separately marks
|
|
1842
|
+
text itself as a spoiler; it declares no destination audience. See
|
|
1843
|
+
[inline spans](#inline-spans) for inline spoiler syntax and surface behavior.
|
|
1844
|
+
|
|
1845
|
+
#### Marking names in prose
|
|
1846
|
+
|
|
1847
|
+
`package-build lint` reports name-marking warnings in authored body prose.
|
|
1848
|
+
The warnings leave a valid package passing. Headings, frontmatter, tables,
|
|
1849
|
+
infoboxes, code, comments and embeds are outside this check.
|
|
1850
|
+
|
|
1851
|
+
A name is explicit: a `[Name]{.name}` bracket span or an authored link label.
|
|
1852
|
+
Use typed `.name` spans for unwritten subjects and ordinary wikilinks for
|
|
1853
|
+
indexed subjects, including private ones. Introduce another subject with its
|
|
1854
|
+
full canonical name at its first prose use; use plain text for the note's own
|
|
1855
|
+
subject and for later mentions and aliases.
|
|
1856
|
+
Plain prose is not searched for names. Wikilinks, ordinary Markdown links and
|
|
1857
|
+
reference links all supply names; a resolved empty wikilink label uses the
|
|
1858
|
+
note's full name. Images, code, footnote markers and bare autolinks without
|
|
1859
|
+
authored labels supply none.
|
|
1860
|
+
|
|
1861
|
+
1. A `.name` span whose whole text matches an indexed `name.full` or
|
|
1862
|
+
`name.aliases` should be a link. Use `[[place-arden|Arden Vale]]` or
|
|
1863
|
+
`[[place-arden|]]` to introduce that note.
|
|
1864
|
+
2. Mark or link each name at most once per note. An indexed full name and its
|
|
1865
|
+
declared aliases share one identity, including across different heading
|
|
1866
|
+
anchors. Other labels use their whole text as their identity: different
|
|
1867
|
+
labels linking to one page are separate names, and a label is not
|
|
1868
|
+
automatically an alias of its destination.
|
|
1869
|
+
3. Introduce the full name before a short form or declared alias.
|
|
1870
|
+
Possessives and regular plurals count as uses; other indexed forms can be
|
|
1871
|
+
declared aliases.
|
|
1872
|
+
|
|
1873
|
+
Bold is emphasis. A whole bold span exactly matching an indexed full name or
|
|
1874
|
+
alias receives one advisory to mark or link the name. It supplies no name
|
|
1875
|
+
occurrence or introduction, and bold containing a marked or linked name
|
|
1876
|
+
receives no such advisory. Unknown bold phrases supply no names.
|
|
1877
|
+
|
|
1878
|
+
Inline Markdown remains active inside name spans, and name spans remain
|
|
1879
|
+
valid inside emphasis:
|
|
1880
|
+
|
|
1881
|
+
```markdown
|
|
1882
|
+
**This is a phrase with [Joe Blow]{.name} in it**
|
|
1883
|
+
```
|
|
1884
|
+
|
|
1885
|
+
The name is `Joe Blow`; the surrounding phrase is emphasis. Sites and books display
|
|
1886
|
+
`.name` text in bold.
|
|
1887
|
+
|
|
1888
|
+
Each affected occurrence produces one warning at its authored
|
|
1889
|
+
`file:line:column`, naming the full name or explicit label. A warning
|
|
1890
|
+
can describe several marking problems at that occurrence.
|
|
1891
|
+
|
|
1892
|
+
An optional `data-type` on a `.name` span states what the named thing is:
|
|
1893
|
+
|
|
1894
|
+
```markdown
|
|
1895
|
+
[Ravenmoor]{.name data-type="place/settlement"}
|
|
1896
|
+
[Aldric Venn]{.name data-type="being/npc"}
|
|
1897
|
+
```
|
|
1898
|
+
|
|
1899
|
+
Use a declared [note type](note-types.md), optionally followed by `/` and one
|
|
1900
|
+
of that type's subtypes. An enumerated declaration requires a listed subtype;
|
|
1901
|
+
an open declaration accepts a well-formed subtype, and an absent subtype
|
|
1902
|
+
declaration admits none. An unknown or malformed supplied value is an error
|
|
1903
|
+
at the `data-type` attribute's file, line and UTF-16 column. The same attribute
|
|
1904
|
+
on a span without the exact `name` class is an ordinary attribute. An untyped
|
|
1905
|
+
`.name` is valid; its type is not inferred from a matching note.
|
|
1906
|
+
|
|
1907
|
+
The index captures actual `.name` spans throughout the authored body, including
|
|
1908
|
+
headings, tables, verse and spans within links. The prose-only marking checks
|
|
1909
|
+
above keep their narrower scope. Frontmatter, literal code, escaped syntax,
|
|
1910
|
+
unmarked link labels, bold, plain prose and generated content add no names.
|
|
1911
|
+
The rendered visible text preserves its spelling and inline content.
|
|
1912
|
+
|
|
1913
|
+
Each own-note metadata record carries `names` in authored order. Every entry
|
|
1914
|
+
has exactly `name`, `type`, `subType`, `line` and `column`; absent
|
|
1915
|
+
classifications are explicit `null`, and no occurrences gives an empty array.
|
|
1916
|
+
This is derived metadata, not frontmatter. Documentation mirrors and assets
|
|
1917
|
+
do not duplicate a note's occurrences. The [`names` relation](#content-tables)
|
|
1918
|
+
flattens these entries with their containing NOTE Address.
|
|
1919
|
+
|
|
1920
|
+
Lint groups names without an indexed full-name or alias match by supplied
|
|
1921
|
+
classification and lists all their source uses. It also groups conflicting
|
|
1922
|
+
annotations of the same visible name. Untyped uses do not conflict. A bare
|
|
1923
|
+
type and its subtype agree; different supplied types or different supplied
|
|
1924
|
+
subtypes conflict. One package advisory contains both groups and is located
|
|
1925
|
+
at the earliest reported use. Advisory findings preserve a successful lint
|
|
1926
|
+
exit status. Supplied-value errors prevent publication and compilation.
|
|
1927
|
+
|
|
1808
1928
|
### Assets are types
|
|
1809
1929
|
|
|
1810
1930
|
`icon`, `image` and `audio` are types in the type vocabulary, and an address
|
|
@@ -2911,7 +3031,7 @@ for the keys.
|
|
|
2911
3031
|
| `frames` | `data.sounds.frames` | `frame`, `shape`, `class` (a list), `makes`, `examples` (a list of `{ root, form }`) |
|
|
2912
3032
|
| `read_past` | `data.sounds.readPast` | `written`, `readAs`, `what` |
|
|
2913
3033
|
| `sound_changes` | `data.soundChanges` | One row per step: `position` (the set's), `name`, `from_language`, `to_language`, `takes`, `step_position` (the step's in its set), `step`, `change`, `where` |
|
|
2914
|
-
| `elements` | `data.elements` | `form`, `variants` (a list), `element_position` (the element's `position`), `kind`, `sense`, `examples` (a list), `word`, `
|
|
3034
|
+
| `elements` | `data.elements` | `form`, `variants` (a list), `element_position` (the element's `position`), `kind`, `sense`, `examples` (a list), `word`, `joining` |
|
|
2915
3035
|
| `roots` | `data.roots` | `root`, `sense`, `field` |
|
|
2916
3036
|
| `cognates` | `data.cognates` | One row per form of a set: `position` (the set's), `gloss`, `proto`, `form_language` (the form's language note), `form` |
|
|
2917
3037
|
| `calling_names` | `data.callingNames` | One row per closing: `form`, `doubled` (`true` or `false`), `usual`, and the language's `min_syllables` |
|
|
@@ -2922,7 +3042,7 @@ for the keys.
|
|
|
2922
3042
|
| `name_shapes` | `data.naming.shapes` | `of`, `syllables_min`, `syllables_max`, `per_vowel_min`, `per_vowel_max` |
|
|
2923
3043
|
| `name_edges` | `data.naming.edges` | `edge`, `sounds` (a list), `vowel` (`false` where unstated), `of` (`name` where unstated) |
|
|
2924
3044
|
| `name_draws` | `data.naming.draws` | `kind`, `elements` (a list) |
|
|
2925
|
-
| `held_from` | `data.naming.heldFrom` | `source`, `takes`, `clip`, `
|
|
3045
|
+
| `held_from` | `data.naming.heldFrom` | `source`, `takes`, `clip`, `joining` and `elements` (lists) |
|
|
2926
3046
|
| `near_names` | `data.naming.nearName.examples` | `near`, `given`, and the language's `min_syllables` |
|
|
2927
3047
|
|
|
2928
3048
|
A register name is carried by a note in the language's `data.scope` — any
|
|
@@ -2937,6 +3057,30 @@ office title, and by an anchor that has it as its name, which the row cites as
|
|
|
2937
3057
|
`max(form) FILTER (WHERE form_language = 'thalorna-note-skill-helonclng') AS "Helonic"`,
|
|
2938
3058
|
grouped by `position` and `gloss`.
|
|
2939
3059
|
|
|
3060
|
+
**`names`** is one row per actual `.name` span in an authored note body. Each
|
|
3061
|
+
row has these columns, including on an empty relation:
|
|
3062
|
+
|
|
3063
|
+
| Column | SQL type | Meaning |
|
|
3064
|
+
| --------- | --------- | -------------------------------------------------------------------------------------------- |
|
|
3065
|
+
| `owner` | `VARCHAR` | Containing authored page's canonical NOTE Address, or `NULL` when the record has no Address. |
|
|
3066
|
+
| `name` | `VARCHAR` | Rendered visible text, preserving its spelling. |
|
|
3067
|
+
| `type` | `VARCHAR` | Supplied note type, or `NULL`. |
|
|
3068
|
+
| `subType` | `VARCHAR` | Supplied subtype, or `NULL`. |
|
|
3069
|
+
| `line` | `BIGINT` | Original 1-based source line at the span's opening `[`. |
|
|
3070
|
+
| `column` | `BIGINT` | Original 1-based UTF-16 source column at that `[`. |
|
|
3071
|
+
|
|
3072
|
+
```sql
|
|
3073
|
+
SELECT owner, name, type, subType, line, "column"
|
|
3074
|
+
FROM names
|
|
3075
|
+
WHERE type = 'place'
|
|
3076
|
+
ORDER BY owner, line, "column"
|
|
3077
|
+
```
|
|
3078
|
+
|
|
3079
|
+
A native Item's source uses its NOTE page as `owner`. Declared dependency
|
|
3080
|
+
metadata supplies `names` in that package's SQL schema. Public queries exclude
|
|
3081
|
+
GM-only owners; all/native contexts retain them. Only authored occurrences
|
|
3082
|
+
contribute rows, so generated queries and sections add no further names.
|
|
3083
|
+
|
|
2940
3084
|
**`language_lineage`** is one row per language note and each language it
|
|
2941
3085
|
inherits words from through [`data.family`](#a-languages-words): `language`,
|
|
2942
3086
|
`ancestor` and `depth`, with the language itself at depth `0`, its parent at
|
|
@@ -3374,6 +3518,28 @@ An attribute-bearing opener allows a nested div to be distinguished from the bar
|
|
|
3374
3518
|
The watch wears [a _blue_ sash]{#watch-sash .uniform lang=en}.
|
|
3375
3519
|
```
|
|
3376
3520
|
|
|
3521
|
+
The exact `.name` class marks a name, with optional `data-type="type/subType"`;
|
|
3522
|
+
see [marking names in prose](#marking-names-in-prose) for the vocabulary and index.
|
|
3523
|
+
The exact `.secret` class marks the enclosed text as an inline spoiler:
|
|
3524
|
+
|
|
3525
|
+
```markdown
|
|
3526
|
+
[The hidden **passage**]{.secret}
|
|
3527
|
+
[Aldric Venn]{.name .secret data-type="being/npc"}
|
|
3528
|
+
```
|
|
3529
|
+
|
|
3530
|
+
The website provides an inline show/hide button and preserves interactive links
|
|
3531
|
+
inside the revealed content. Foundry prints a visible inline `secret` span
|
|
3532
|
+
with a **Secret:** label; the span does not withhold its text from players.
|
|
3533
|
+
The book prints the text with secret styling and a **Secret:** label.
|
|
3534
|
+
Inline emphasis and combined `.name` classification survive rendering. Published HTML retains the text, so opening
|
|
3535
|
+
the spoiler or inspecting the source reveals it.
|
|
3536
|
+
|
|
3537
|
+
This span has no effect on a destination's audience. Ordinary wikilinks derive
|
|
3538
|
+
spoilers from their resolved destinations; `.show` bypasses that automatic
|
|
3539
|
+
concealment without cancelling an enclosing authored `.secret`. An inline span
|
|
3540
|
+
does not change the scope of `.secret` headings or `:::secret` blocks, and it
|
|
3541
|
+
does not start or withhold a JournalEntryPage.
|
|
3542
|
+
|
|
3377
3543
|
Inline Markdown remains active inside the span. Bracket spans have lower priority than URL links, image links, reference links, footnotes and wikilinks. A bracketed form immediately preceded by `!`, followed by `(` or another `[`, or beginning with `[[` is not a span, even when the link is unresolved. Ordinary links retain their existing syntax. Escape a bracket when it should be literal, or put examples inside a code span or fence. A span identifier addresses that point in the note; it does not start a journal page.
|
|
3378
3544
|
|
|
3379
3545
|
##### Alerts
|
|
@@ -5492,7 +5658,7 @@ SoHL sets `system.quantity` to `1` for each compendium item.
|
|
|
5492
5658
|
| `soundChanges[].steps[].step` | `string`, required | The step's label, as `A1` or `"1"`; no label stands twice in a set |
|
|
5493
5659
|
| `soundChanges[].steps[].change` | `string`, required | The change in arrow notation, as `` `ph` > `f`; `kh` > `h` ``, or the operation it names in words |
|
|
5494
5660
|
| `soundChanges[].steps[].where` | `string` | Where it holds, as `word end`; everywhere where absent |
|
|
5495
|
-
| `elements` | `{ form, variants?, position?, kind?, sense, examples?, word?,
|
|
5661
|
+
| `elements` | `{ form, variants?, position?, kind?, sense, examples?, word?, joining? }[]` | `language` only: the elements its names are built from |
|
|
5496
5662
|
| `elements[].form` | `string`, required | The element, as `arn-` |
|
|
5497
5663
|
| `elements[].variants` | `string[]` | The element's other forms, as `aldr-` beside `ald-` |
|
|
5498
5664
|
| `elements[].position` | `opening`, `closing`, `either` or `link` | Where in a name it stands |
|
|
@@ -5500,7 +5666,7 @@ SoHL sets `system.quantity` to `1` for each compendium item.
|
|
|
5500
5666
|
| `elements[].sense` | `string`, required | What it means |
|
|
5501
5667
|
| `elements[].examples` | `string[]` | The names it is read from |
|
|
5502
5668
|
| `elements[].word` | `string` | The word it is the stem of, a form of `words` of the language or an ancestor |
|
|
5503
|
-
| `elements[].
|
|
5669
|
+
| `elements[].joining` | `string` | The element's joining form, as `-borgar`, when it qualifies another word |
|
|
5504
5670
|
| `roots` | `{ root, sense, field? }[]` | `language` only: the consonantal roots its words are set from |
|
|
5505
5671
|
| `roots[].root` | `string`, required | The root, as `d-l-k` |
|
|
5506
5672
|
| `roots[].sense` | `string`, required | What it means |
|
|
@@ -5540,11 +5706,11 @@ SoHL sets `system.quantity` to `1` for each compendium item.
|
|
|
5540
5706
|
| `naming.draws` | `{ kind, elements }[]` | The element kinds each kind of name is built from |
|
|
5541
5707
|
| `naming.draws[].kind` | `byname`, `given` or `place`, required | The kind of name; once per kind |
|
|
5542
5708
|
| `naming.draws[].elements` | list of `byname`, `given` or `place`, required | The element kinds, as `elements[].kind` states them |
|
|
5543
|
-
| `naming.heldFrom` | `{ source, takes, clip?,
|
|
5709
|
+
| `naming.heldFrom` | `{ source, takes, clip?, joining?, elements? }[]` | What a place name takes first, by what the place is held from |
|
|
5544
5710
|
| `naming.heldFrom[].source` | `string`, required | What the place is held from, as `a founder` |
|
|
5545
5711
|
| `naming.heldFrom[].takes` | `stem`, `godName`, `givenName` or `element`, required | What stands first: a name-stem in any grade, a god's name, a founder's given name, or one of `elements` |
|
|
5546
5712
|
| `naming.heldFrom[].clip` | `first` | How the name is clipped: to its first element; whole where absent. A name row only |
|
|
5547
|
-
| `naming.heldFrom[].
|
|
5713
|
+
| `naming.heldFrom[].joining` | `string[]` | The joining endings, one of which stands at the seam after the name. A name row only |
|
|
5548
5714
|
| `naming.heldFrom[].elements` | `string[]` | The elements that may stand first; required on, and only on, a row that takes an `element` |
|
|
5549
5715
|
| `naming.nearName` | `{ minSyllables, examples? }` | The near name a long given name carries |
|
|
5550
5716
|
| `naming.nearName.minSyllables` | `integer`, required | The fewest syllables a given name has that carries a near name |
|
|
@@ -5856,7 +6022,7 @@ data:
|
|
|
5856
6022
|
- { kind: byname, elements: [byname, place] }
|
|
5857
6023
|
heldFrom:
|
|
5858
6024
|
- { source: a god, takes: godName, clip: first }
|
|
5859
|
-
- { source: a founder, takes: givenName,
|
|
6025
|
+
- { source: a founder, takes: givenName, joining: ["-s", "-a"] }
|
|
5860
6026
|
- { source: the ting and its law, takes: element, elements: ["ting-", "lög-"] }
|
|
5861
6027
|
nearName:
|
|
5862
6028
|
minSyllables: 4
|
|
@@ -5972,9 +6138,8 @@ form and its other forms, where in a name it stands (`opening`, `closing`,
|
|
|
5972
6138
|
`either` or `link`), its sense and the names it is read from. In a language
|
|
5973
6139
|
that keeps one set of elements for each kind of name, `kind` says which
|
|
5974
6140
|
(`byname`, `given` or `place`). `word` names the word of `words` the element is
|
|
5975
|
-
the stem of, the language's or an ancestor's, and `
|
|
5976
|
-
|
|
5977
|
-
is also a word stays a word.
|
|
6141
|
+
the stem of, the language's or an ancestor's, and `joining` gives its form
|
|
6142
|
+
when it qualifies another word. An element that is also a word stays a word.
|
|
5978
6143
|
|
|
5979
6144
|
**`roots` lists consonantal roots**, each with its sense and the field it
|
|
5980
6145
|
belongs to. **`cognates`** lists, on the eldest language of a set, the form one
|
|
@@ -6007,7 +6172,7 @@ there too and `of` where the edge holds for one class of name only; an edge
|
|
|
6007
6172
|
stands once for each class. `draws` gives the element kinds each kind of name
|
|
6008
6173
|
is built from. `heldFrom` gives what a place name takes first by what the
|
|
6009
6174
|
place is held from: a name-stem, a god's name, a founder's given name — that
|
|
6010
|
-
name `clip`ped to its `first` element, or followed by one of its `
|
|
6175
|
+
name `clip`ped to its `first` element, or followed by one of its `joining`
|
|
6011
6176
|
endings — or one of the `elements` listed. `nearName` states the fewest
|
|
6012
6177
|
syllables a given name has that carries a near name, with worked examples.
|
|
6013
6178
|
|
|
@@ -6056,7 +6221,7 @@ Each finding is located at the key at fault:
|
|
|
6056
6221
|
| A naming edge stated twice for one class of name, at the second | error |
|
|
6057
6222
|
| A kind of name whose draws are stated twice, at the second | error |
|
|
6058
6223
|
| A held-from row that takes an `element` and names no `elements`, at the row | error |
|
|
6059
|
-
| `elements` on a held-from row that takes no element, or `clip` or `
|
|
6224
|
+
| `elements` on a held-from row that takes no element, or `clip` or `joining` on one that takes no name, at the key | error |
|
|
6060
6225
|
| A `scope` Address that does not resolve, or a type that is no note type | error |
|
|
6061
6226
|
| A tongue `language` or a cognate form's language that is no language note | error |
|
|
6062
6227
|
| A register row with neither a `tongue` nor a `kind`, at the row | error |
|
|
@@ -6391,6 +6556,12 @@ word.
|
|
|
6391
6556
|
them, then its culture's `data.language`;
|
|
6392
6557
|
3. on a culture, its own `data.language`.
|
|
6393
6558
|
|
|
6559
|
+
On lore and affiliation notes, `data.language` and
|
|
6560
|
+
`data.glossary[].language` name language notes. Use `skill-provnzlng` for a
|
|
6561
|
+
language in this package, or `<pkg>-note-skill-provnzlng` for its complete
|
|
6562
|
+
NOTE Address. Any other system segment, including `sohl`, is an error at the
|
|
6563
|
+
authored value. A SOHL skill Address names a compiled Item.
|
|
6564
|
+
|
|
6394
6565
|
An affiliation's culture is the one it states or inherits, as
|
|
6395
6566
|
[Culture in the index](#culture-in-the-index) describes, and a culture's
|
|
6396
6567
|
`data.language` includes one it inherits from its
|
|
@@ -474,7 +474,7 @@ These fields are accepted by every note type. A field's value can still be irrel
|
|
|
474
474
|
| `data.scope` | a map — `{ folders?, cultures?, lore?, parents?, addresses?, paths?, types?, skip? }` | The notes the register covers. A language skill note only. |
|
|
475
475
|
| `data.sounds` | a map — `{ letters?, edges?, clusters?, stress?, finals?, syllables?, joining?, joins?, marks?, skeleton?, frames?, readPast?, mutations? }` | The letters the language writes and the rules its words keep. A language skill note only. |
|
|
476
476
|
| `data.soundChanges` | list of `{ name, from?, to?, takes?, steps }` | Named sets of ordered changes, each making one tongue's words from another's. A language skill note only. |
|
|
477
|
-
| `data.elements` | list of `{ form, variants?, position?, kind?, sense, examples?, word?,
|
|
477
|
+
| `data.elements` | list of `{ form, variants?, position?, kind?, sense, examples?, word?, joining? }` | The elements the language's names are built from. A language skill note only. |
|
|
478
478
|
| `data.roots` | list of `{ root, sense, field? }` | The consonantal roots the language's words are set from. A language skill note only. |
|
|
479
479
|
| `data.cognates` | list of `{ forms, proto?, gloss }` | Sets of forms one older word takes in several languages, stated on the eldest. A language skill note only. |
|
|
480
480
|
| `data.callingNames` | a map — `{ closings, minSyllables }` | The short name a long given name is called by. A language skill note only. |
|
|
@@ -635,7 +635,7 @@ These fields are accepted by every note type. A field's value can still be irrel
|
|
|
635
635
|
| `data.elements[].sense` | a string | yes | What the element means. |
|
|
636
636
|
| `data.elements[].examples` | a list of names | no | The names the element is read from. |
|
|
637
637
|
| `data.elements[].word` | a form of the language's words | no | The word the element is the stem of, a form of `data.words` of the language or an ancestor. |
|
|
638
|
-
| `data.elements[].
|
|
638
|
+
| `data.elements[].joining` | the element as written, such as `-borgar` | no | The element's joining form, as written when it qualifies another word. |
|
|
639
639
|
| `data.roots[].root` | a consonantal skeleton, such as `d-l-k` | yes | The root. |
|
|
640
640
|
| `data.roots[].sense` | a string | yes | What the root means. |
|
|
641
641
|
| `data.roots[].field` | a string, such as `stone` | no | The field the root belongs to. |
|
|
@@ -668,11 +668,11 @@ These fields are accepted by every note type. A field's value can still be irrel
|
|
|
668
668
|
| `data.naming.draws` | list of `{ kind, elements }` | no | The element kinds each kind of name is built from. |
|
|
669
669
|
| `data.naming.draws[].kind` | one of byname, given, place | yes | The kind of name; once per kind. |
|
|
670
670
|
| `data.naming.draws[].elements` | a list of element kinds | yes | The kinds of element, as `data.elements` states them, the name is built from. |
|
|
671
|
-
| `data.naming.heldFrom` | list of `{ source, takes, clip?,
|
|
671
|
+
| `data.naming.heldFrom` | list of `{ source, takes, clip?, joining?, elements? }` | no | What a place name takes first, by what the place is held from. |
|
|
672
672
|
| `data.naming.heldFrom[].source` | a string, such as `a founder` | yes | What the place is held from. |
|
|
673
673
|
| `data.naming.heldFrom[].takes` | one of stem, godName, givenName, element | yes | What stands first: a name-stem in any grade, a god's name, a founder's given name, or one of `elements`. |
|
|
674
674
|
| `data.naming.heldFrom[].clip` | one of first | no | How the name is clipped: `first`, to its first element; whole where absent. |
|
|
675
|
-
| `data.naming.heldFrom[].
|
|
675
|
+
| `data.naming.heldFrom[].joining` | a list of endings | no | The joining endings, one of which stands at the seam after the name. |
|
|
676
676
|
| `data.naming.heldFrom[].elements` | a list of elements | no | The elements that may stand first, for a row that takes an `element`. |
|
|
677
677
|
| `data.naming.nearName` | a map — `{ minSyllables, examples? }` | no | The near name a long given name carries. |
|
|
678
678
|
| `data.naming.nearName.minSyllables` | a whole number | yes | The fewest syllables a given name has that carries a near name. |
|
package/engine/anchors.mjs
CHANGED
|
@@ -41,7 +41,7 @@ import { scanPoetry } from "./content-poetry.mjs";
|
|
|
41
41
|
import { scanSpans } from "./content-spans.mjs";
|
|
42
42
|
import { slugify } from "./content-slug.mjs";
|
|
43
43
|
import { scanFigures } from "./content-figures.mjs";
|
|
44
|
-
import { scanHeadingAttributes } from "./heading-attributes.mjs";
|
|
44
|
+
import { withheldSections, scanHeadingAttributes } from "./heading-attributes.mjs";
|
|
45
45
|
import { positionOfFrontmatterPath } from "./diagnostics.mjs";
|
|
46
46
|
|
|
47
47
|
/**
|
|
@@ -65,12 +65,14 @@ export const ANCHOR_KINDS = Object.freeze(["prose", "event"]);
|
|
|
65
65
|
* picture's address to the role its asset declares, so a figure anchor's
|
|
66
66
|
* `name` reads `Map 1` rather than `Figure 1` where it is due — see
|
|
67
67
|
* {@link module:engine/content-figures.scanFigures}.
|
|
68
|
+
* @param {import("./content-session.mjs").ContentSyntax} [syntax] - Exact-text scanner projections.
|
|
68
69
|
* @returns {Array<{slug: string, name: string, level: number, line: number,
|
|
69
|
-
* kind: string}>} In document order.
|
|
70
|
+
* kind: string, secret?: boolean}>} In document order.
|
|
70
71
|
*/
|
|
71
|
-
export function collectAnchors(body, bodyLine = 1, resolveRole) {
|
|
72
|
+
export function collectAnchors(body, bodyLine = 1, resolveRole, syntax) {
|
|
72
73
|
const anchors = [];
|
|
73
|
-
|
|
74
|
+
const headings = scanHeadingAttributes(body, bodyLine).headings;
|
|
75
|
+
for (const heading of headings) {
|
|
74
76
|
if (!heading.id) continue;
|
|
75
77
|
anchors.push({
|
|
76
78
|
slug: heading.id,
|
|
@@ -90,9 +92,12 @@ export function collectAnchors(body, bodyLine = 1, resolveRole) {
|
|
|
90
92
|
kind: "prose",
|
|
91
93
|
});
|
|
92
94
|
}
|
|
95
|
+
const secretBlocks = [];
|
|
93
96
|
const blockAnchors = (source, firstLine) => {
|
|
94
97
|
const sourceLines = source.split("\n");
|
|
95
|
-
for (const block of scanBlocks(source).blocks) {
|
|
98
|
+
for (const block of (syntax ? syntax(source).blocks : scanBlocks(source)).blocks) {
|
|
99
|
+
if (block.name === "secret")
|
|
100
|
+
secretBlocks.push({ start: firstLine + block.start, end: firstLine + block.end });
|
|
96
101
|
if (block.id)
|
|
97
102
|
anchors.push({
|
|
98
103
|
slug: slugify(block.id),
|
|
@@ -108,7 +113,7 @@ export function collectAnchors(body, bodyLine = 1, resolveRole) {
|
|
|
108
113
|
}
|
|
109
114
|
};
|
|
110
115
|
blockAnchors(String(body ?? ""), bodyLine);
|
|
111
|
-
for (const alert of scanAlerts(body).blocks) {
|
|
116
|
+
for (const alert of (syntax ? syntax(body).alerts : scanAlerts(body)).blocks) {
|
|
112
117
|
if (alert.id)
|
|
113
118
|
anchors.push({
|
|
114
119
|
slug: slugify(alert.id),
|
|
@@ -128,7 +133,8 @@ export function collectAnchors(body, bodyLine = 1, resolveRole) {
|
|
|
128
133
|
kind: "prose",
|
|
129
134
|
});
|
|
130
135
|
}
|
|
131
|
-
|
|
136
|
+
const spans = (syntax ? syntax(body).spans : scanSpans(body)).spans;
|
|
137
|
+
for (const span of spans) {
|
|
132
138
|
if (span.id)
|
|
133
139
|
anchors.push({
|
|
134
140
|
slug: slugify(span.id),
|
|
@@ -138,17 +144,45 @@ export function collectAnchors(body, bodyLine = 1, resolveRole) {
|
|
|
138
144
|
kind: "prose",
|
|
139
145
|
});
|
|
140
146
|
}
|
|
147
|
+
const sections =
|
|
148
|
+
headings.some((heading) => heading.classes.includes("secret")) ?
|
|
149
|
+
withheldSections(body).sections
|
|
150
|
+
: [];
|
|
151
|
+
const secretSpans = spans.filter((span) =>
|
|
152
|
+
span.attrs.some(([key, value]) => key === "class" && value.split(/\s+/).includes("secret")),
|
|
153
|
+
);
|
|
154
|
+
const secretSpanAnchors = new Set(
|
|
155
|
+
spans
|
|
156
|
+
.filter(
|
|
157
|
+
(target) =>
|
|
158
|
+
target.id &&
|
|
159
|
+
secretSpans.some(
|
|
160
|
+
(parent) => parent.start <= target.start && parent.end >= target.end,
|
|
161
|
+
),
|
|
162
|
+
)
|
|
163
|
+
.map((span) => span.id),
|
|
164
|
+
);
|
|
165
|
+
for (const anchor of anchors) {
|
|
166
|
+
const line = anchor.line - bodyLine;
|
|
167
|
+
if (
|
|
168
|
+
sections.some((section) => line >= section.start && line < section.end) ||
|
|
169
|
+
secretBlocks.some((block) => anchor.line >= block.start && anchor.line <= block.end) ||
|
|
170
|
+
secretSpanAnchors.has(anchor.slug)
|
|
171
|
+
)
|
|
172
|
+
anchor.secret = true;
|
|
173
|
+
}
|
|
141
174
|
return anchors.sort((a, b) => a.line - b.line);
|
|
142
175
|
}
|
|
143
176
|
|
|
144
177
|
/** Collisions involving a div, verse or inline-span anchor.
|
|
145
178
|
* Heading and caption collisions are diagnosed by their existing scanners.
|
|
146
179
|
* @param {string} body - Authored Markdown.
|
|
180
|
+
* @param {import("./content-session.mjs").ContentSyntax} [syntax] - Exact-text scanner projections.
|
|
147
181
|
* @returns {Array<{line: number, column: number, message: string}>} Located collisions.
|
|
148
182
|
*/
|
|
149
|
-
export function markupAnchorFindings(body) {
|
|
183
|
+
export function markupAnchorFindings(body, syntax) {
|
|
150
184
|
const groups = new Map();
|
|
151
|
-
for (const anchor of collectAnchors(body)) {
|
|
185
|
+
for (const anchor of collectAnchors(body, 1, undefined, syntax)) {
|
|
152
186
|
const slug = slugify(anchor.slug);
|
|
153
187
|
if (!groups.has(slug)) groups.set(slug, []);
|
|
154
188
|
groups.get(slug).push(anchor);
|
package/engine/base-compiler.mjs
CHANGED
|
@@ -686,11 +686,13 @@ export class BasePackCompiler {
|
|
|
686
686
|
// it made — `errors` or `findings`, never just the first — so nothing
|
|
687
687
|
// here re-scans; it only stops taking `[0]`.
|
|
688
688
|
const findings = [];
|
|
689
|
+
const syntax = this.corpus?.syntax;
|
|
690
|
+
const scanned = syntax?.(body);
|
|
689
691
|
for (const secretError of [
|
|
690
|
-
...scanBlocks(body).errors,
|
|
691
|
-
...scanAlerts(body).errors,
|
|
692
|
-
...scanSpans(body).errors,
|
|
693
|
-
...markupAnchorFindings(body),
|
|
692
|
+
...(scanned?.blocks ?? scanBlocks(body)).errors,
|
|
693
|
+
...(scanned?.alerts ?? scanAlerts(body)).errors,
|
|
694
|
+
...(scanned?.spans ?? scanSpans(body)).errors,
|
|
695
|
+
...markupAnchorFindings(body, syntax),
|
|
694
696
|
]) {
|
|
695
697
|
findings.push({
|
|
696
698
|
message: secretError.message,
|