@heroiclands/package-build 23.12.6 → 23.12.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/assets/styles/content-base.css +25 -0
  3. package/bin/content-commands.mjs +264 -3
  4. package/bin/package-build.mjs +25 -0
  5. package/content-config.mjs +72 -0
  6. package/docs/api.md +10 -9
  7. package/docs/authoring/links-and-markup.md +439 -47
  8. package/docs/commands.md +127 -0
  9. package/docs/configuration.md +66 -3
  10. package/docs/content-format.md +3 -1
  11. package/docs/guides/book.md +11 -0
  12. package/docs/guides/module-project.md +16 -0
  13. package/docs/guides/site.md +19 -0
  14. package/docs/reference/format-details.md +372 -6
  15. package/docs/reference/note-types.md +36 -11
  16. package/engine/base-compiler.mjs +2 -0
  17. package/engine/bundles.mjs +7 -2
  18. package/engine/compile-corpus.mjs +3 -1
  19. package/engine/content-charset.mjs +21 -2
  20. package/engine/content-format.yaml +1 -0
  21. package/engine/content-index.mjs +12 -0
  22. package/engine/content-poetry.mjs +22 -4
  23. package/engine/content-scripts.mjs +1219 -0
  24. package/engine/content-session.mjs +2 -0
  25. package/engine/data-keys.mjs +5 -1
  26. package/engine/dictionary-layout.mjs +18 -12
  27. package/engine/dictionary-rows.mjs +11 -2
  28. package/engine/helpers.mjs +8 -2
  29. package/engine/index-records.mjs +1 -0
  30. package/engine/journals.mjs +14 -3
  31. package/engine/language-data.mjs +137 -1
  32. package/engine/language-tag.mjs +103 -0
  33. package/engine/language-terms.mjs +16 -4
  34. package/engine/map-lexicon.mjs +762 -0
  35. package/engine/markup-validation.mjs +17 -3
  36. package/engine/names.mjs +1 -1
  37. package/engine/note-vocabulary.mjs +113 -0
  38. package/engine/pdf-build.mjs +28 -10
  39. package/engine/pdf-render.mjs +116 -11
  40. package/engine/run-rows.mjs +119 -0
  41. package/engine/run-words.mjs +266 -0
  42. package/engine/scenes.mjs +1 -0
  43. package/engine/script-font-delivery.mjs +321 -0
  44. package/engine/script-fonts.mjs +172 -0
  45. package/engine/site-build.mjs +10 -3
  46. package/engine/site-config.mjs +5 -0
  47. package/engine/site-root.mjs +19 -5
  48. package/engine/sql-tables.mjs +111 -1
  49. package/engine/text-maps.mjs +607 -0
  50. package/engine/verb-pattern.mjs +99 -0
  51. package/engine/word-pattern.mjs +27 -0
  52. package/engine/word-rows.mjs +8 -2
  53. package/engine/word-terms.mjs +44 -0
  54. package/hugo-theme/layouts/_default/baseof.html +3 -0
  55. package/hugo-theme/static/css/style.css +10 -0
  56. package/manifest.mjs +9 -1
  57. package/package.json +1 -1
  58. package/types/content-config.d.mts +26 -0
  59. package/types/engine/compile-corpus.d.mts +2 -1
  60. package/types/engine/content-index.d.mts +11 -3
  61. package/types/engine/content-poetry.d.mts +7 -0
  62. package/types/engine/content-scripts.d.mts +226 -0
  63. package/types/engine/data-keys.d.mts +2 -2
  64. package/types/engine/dictionary-rows.d.mts +15 -0
  65. package/types/engine/helpers.d.mts +5 -1
  66. package/types/engine/journals.d.mts +7 -2
  67. package/types/engine/language-data.d.mts +2 -0
  68. package/types/engine/language-tag.d.mts +8 -0
  69. package/types/engine/language-terms.d.mts +2 -1
  70. package/types/engine/map-lexicon.d.mts +274 -0
  71. package/types/engine/markup-validation.d.mts +7 -2
  72. package/types/engine/pdf-build.d.mts +26 -3
  73. package/types/engine/pdf-render.d.mts +17 -2
  74. package/types/engine/run-rows.d.mts +89 -0
  75. package/types/engine/run-words.d.mts +86 -0
  76. package/types/engine/script-font-delivery.d.mts +134 -0
  77. package/types/engine/script-fonts.d.mts +168 -0
  78. package/types/engine/site-build.d.mts +2 -1
  79. package/types/engine/site-root.d.mts +11 -3
  80. package/types/engine/text-maps.d.mts +175 -0
  81. package/types/engine/verb-pattern.d.mts +65 -0
  82. package/types/engine/word-pattern.d.mts +13 -0
  83. package/types/engine/word-terms.d.mts +6 -0
package/docs/commands.md CHANGED
@@ -2485,6 +2485,12 @@ with visible text, nullable type/subtype and original source line/column.
2485
2485
  Local and dependency SQL schemas expose them through `names`; they travel in
2486
2486
  the metadata artifact.
2487
2487
 
2488
+ Metadata also includes each own note's spans, divs and poetry fences marked with
2489
+ `lang=`, `font=` or `map=` under `runs`, with the language as written, the tag
2490
+ it resolves to, the font, the map and the original source line/column. Local
2491
+ and dependency SQL schemas expose them through `runs`, and each mapping note's
2492
+ rows through `maps`.
2493
+
2488
2494
  The command prepares SQL tables, page lists, automatic sections and scalar
2489
2495
  expressions before publishing their prose targets. Each source uses its own
2490
2496
  audience for prose declarations: public notes use public providers and usage,
@@ -2970,6 +2976,127 @@ $ package-build reachability assets/content
2970
2976
  `package-build links [root]`, `package-build lint [root]`, [Diagnostics](diagnostics.md),
2971
2977
  [Configuration](configuration.md).
2972
2978
 
2979
+ ### `package-build transliterate <map> [text..]`
2980
+
2981
+ **NAME**
2982
+
2983
+ Map a passage into a script exactly as the build maps a marked run.
2984
+
2985
+ **SYNOPSIS**
2986
+
2987
+ ```
2988
+ package-build transliterate <map> [--lang <language>] [--font <slug>] [--hex] [text..]
2989
+ ```
2990
+
2991
+ **DESCRIPTION**
2992
+
2993
+ Reads the content index, as `package-build lint` does, and prints the passage
2994
+ rewritten by the mapping note whose `data.map.name` is `<map>`, through the
2995
+ same code the build sets a run marked `map=<map> lang=<language> font=<slug>`
2996
+ with. With `--lang`, each word the language's lexicon states a native form for
2997
+ is written by it and every other word is spelled by the rows; without it, the
2998
+ rows alone apply. See
2999
+ [a map reads the dictionary first](reference/format-details.md#a-map-reads-the-dictionary-first).
3000
+ The text is the words given, joined by single spaces; `-` or no text reads it
3001
+ from standard input, less one trailing newline. Writes nothing.
3002
+
3003
+ **OPTIONS**
3004
+
3005
+ | Positional | Type | Default | Description |
3006
+ | ---------- | ---------------- | ------- | -------------------------------------- |
3007
+ | `map` | string | — | The `data.map.name` of a mapping note. |
3008
+ | `text` | string, variadic | stdin | The passage; `-` reads it from stdin. |
3009
+
3010
+ | Option | Type | Default | Description |
3011
+ | -------- | ------- | -------------------------- | ------------------------------------------------------------------------------- |
3012
+ | `--lang` | string | — | A language declared in `languages`, whose lexicon is read first. |
3013
+ | `--font` | string | the map's one face, if one | The face, whose own rows come first; required when the map lists more than one. |
3014
+ | `--hex` | boolean | `false` | Print the code points, as `U+XXXX` separated by spaces. |
3015
+
3016
+ **EXIT STATUS**
3017
+
3018
+ 1 if the content index could not be built for any note, or the map, the
3019
+ language or the face names nothing, each reported as a finding: an unknown
3020
+ language at `languages` in the configuration, an unknown map or face at the
3021
+ content tree. 1, with the build's own message, if a capitalised word of the
3022
+ passage has a native form the rows cannot capitalise. Otherwise 0.
3023
+
3024
+ **EXAMPLES**
3025
+
3026
+ With Vylari's `zaron` stating a native form for `semaron`, and `lonak` stating
3027
+ none:
3028
+
3029
+ ```
3030
+ $ package-build transliterate semaron --lang vyl --hex zaron lonak
3031
+ U+F024F U+F0230 U+F023F U+F023C U+0020 U+F023A U+F023D U+F023C U+F0230 U+F0239
3032
+ $ package-build transliterate semaron --hex zaron
3033
+ U+F0255 U+F0230 U+F023F U+F023D U+F023C
3034
+ $ package-build transliterate semoran zaron
3035
+ assets/content: error: `semoran` is not a map: a map is named by the `data.map.name` of a mapping note; did you mean `semaron`?
3036
+ ```
3037
+
3038
+ The first run writes `zaron` by its exception; the second, with no `--lang`,
3039
+ spells it by the rows.
3040
+
3041
+ **SEE ALSO**
3042
+
3043
+ `package-build lexicon <language>`, [Mapping notes](reference/format-details.md#mapping-notes).
3044
+
3045
+ ### `package-build lexicon <language>`
3046
+
3047
+ **NAME**
3048
+
3049
+ List a language's words with their native forms under a map.
3050
+
3051
+ **SYNOPSIS**
3052
+
3053
+ ```
3054
+ package-build lexicon <language> --map <map> [--font <slug>] [--json]
3055
+ ```
3056
+
3057
+ **DESCRIPTION**
3058
+
3059
+ Reads the content index and lists every word of the language note
3060
+ [`languages.<language>.note`](configuration.md#languages) names, in `data.words`
3061
+ order, each with its native form under `<map>`: the stated exception where the
3062
+ word has one, else the rows' spelling, each exactly as
3063
+ `package-build transliterate` and the build give it. Each line holds, separated
3064
+ by tabs, the word's `form`, its native form, `exception` or `rows`, its `class`
3065
+ and its `gloss`. Writes nothing.
3066
+
3067
+ **OPTIONS**
3068
+
3069
+ | Positional | Type | Default | Description |
3070
+ | ---------- | ------ | ------- | --------------------------------------------------- |
3071
+ | `language` | string | — | A declared language whose `note` names its lexicon. |
3072
+
3073
+ | Option | Type | Default | Description |
3074
+ | -------- | ------- | -------------------------- | -------------------------------------------------------------------------- |
3075
+ | `--map` | string | — | Required: the `data.map.name` of a mapping note. |
3076
+ | `--font` | string | the map's one face, if one | The face; required when the map lists more than one. |
3077
+ | `--json` | boolean | `false` | Print a JSON array of `{ form, native, exception, class, gloss }` instead. |
3078
+
3079
+ **EXIT STATUS**
3080
+
3081
+ 1 if the content index could not be built for any note, or the language, the
3082
+ map or the face names nothing, the language states no `note`, or a capitalised
3083
+ word has a native form the rows cannot capitalise, each reported as a finding.
3084
+ Otherwise 0.
3085
+
3086
+ **EXAMPLES**
3087
+
3088
+ ```
3089
+ $ package-build lexicon vyl --map semaron | head -3
3090
+ zaron <native form> exception n an oath
3091
+ lonak <native form> rows n a road
3092
+ thizaron <native form> rows n a seal
3093
+ ```
3094
+
3095
+ **SEE ALSO**
3096
+
3097
+ `package-build transliterate <map> [text..]`,
3098
+ [A language's words](reference/format-details.md#a-languages-words).
3099
+
2973
3100
  ### `package-build addresses diff`
2974
3101
 
2975
3102
  **NAME**
@@ -54,7 +54,7 @@ around its own evaluation.
54
54
 
55
55
  ### Quick reference
56
56
 
57
- 25 top-level keys. `rootDir` is not one of them — a data configuration never
57
+ 26 top-level keys. `rootDir` is not one of them — a data configuration never
58
58
  writes it — and is documented under [Derived values](#derived-values) instead,
59
59
  alongside `foundryPackage`, `homepage`, `author` and `itemBuilders`, whose
60
60
  data-configuration behaviour is also derivation rather than ordinary
@@ -75,6 +75,7 @@ authoring.
75
75
  | [`skipDirectories`](#skipdirectories) | string[] | no | `[]` |
76
76
  | [`forbidGeneratedArt`](#forbidgeneratedart) | boolean | no | `false` |
77
77
  | [`icons`](#icons) | object, or a path to a file holding one | no | empty registry |
78
+ | [`languages`](#languages) | object (name → `{tag, note?}`) | no | `{}` |
78
79
  | [`packs`](#packs) | array | yes, at least one entry, in a `systems` or `modules` package; refused in a `documentation` package | — |
79
80
  | [`docs`](#docs) | object | no; refused in a `documentation` package | `{}` |
80
81
  | [`site`](#site) | object | no | see [`site`](#site) |
@@ -89,7 +90,7 @@ authoring.
89
90
 
90
91
  Any key outside this list is refused:
91
92
 
92
- > `` `<key>` is not a recognized option (expected one of: rootDir, contentPackage, currentDate, defaultCalendar, foundryPackage, homepage, authors, packageKind, stats, itemBuilders, paths, skipDirectories, forbidGeneratedArt, icons, packs, docs, site, pdf, compatibility, relationships, systems, requiresSystem, packageBuild, publish, changelog). ``
93
+ > `` `<key>` is not a recognized option (expected one of: rootDir, contentPackage, currentDate, defaultCalendar, foundryPackage, homepage, authors, packageKind, stats, itemBuilders, paths, skipDirectories, forbidGeneratedArt, icons, languages, packs, docs, site, pdf, compatibility, relationships, systems, requiresSystem, packageBuild, publish, changelog). ``
93
94
 
94
95
  (`rootDir` appears in that list because it is a key `defineConfig` itself
95
96
  accepts — an `.mjs` configuration authors it directly. A YAML configuration
@@ -196,7 +197,7 @@ build writes its `name` as `params.author`. `null` when `authors` is empty.
196
197
  as an unknown key, at the key's own line, because `authors` is the one list a
197
198
  package names its people in:
198
199
 
199
- > ``package-build config: `author` is not a recognized option (expected one of: rootDir, contentPackage, currentDate, defaultCalendar, foundryPackage, homepage, authors, packageKind, stats, itemBuilders, paths, skipDirectories, forbidGeneratedArt, icons, packs, docs, site, pdf, compatibility, relationships, systems, requiresSystem, packageBuild, publish, changelog).``
200
+ > ``package-build config: `author` is not a recognized option (expected one of: rootDir, contentPackage, currentDate, defaultCalendar, foundryPackage, homepage, authors, packageKind, stats, itemBuilders, paths, skipDirectories, forbidGeneratedArt, icons, languages, packs, docs, site, pdf, compatibility, relationships, systems, requiresSystem, packageBuild, publish, changelog).``
200
201
 
201
202
  `package.json`'s own `author` is npm's and is not read.
202
203
 
@@ -722,6 +723,68 @@ any finding it reports is a refusal, joined into one message:
722
723
 
723
724
  > ``package-build config: `icons` <findings, semicolon-joined>.``
724
725
 
726
+ ### `languages`
727
+
728
+ **Type:** object (name → `{tag, note?}`) · **Optional** · default: `{}`.
729
+
730
+ The languages a note may tag text with, by short name. A note writes the short
731
+ name, as in `[Daimela, helthos.]{lang=vyl}`; the build gives the text the
732
+ language's tag. See
733
+ [marking language, font and mapping](authoring/links-and-markup.md#marking-language-font-and-mapping).
734
+
735
+ ```yaml
736
+ languages:
737
+ vyl: { tag: art-x-vyl, note: skill-vylarilng }
738
+ khel: { tag: art-x-khel }
739
+ ```
740
+
741
+ | Key | Type | Required |
742
+ | ----------------------- | -------------------------- | -------- |
743
+ | `languages.<name>.tag` | BCP 47 language tag | yes |
744
+ | `languages.<name>.note` | Address of a language note | no |
745
+
746
+ **The name** is lowercase letters, digits and single hyphens, starting with a
747
+ letter. Lowercase is part of the rule, so no two names differ only in case. A
748
+ real language needs no entry: `lang=la` is read as its tag.
749
+
750
+ **`tag`** is the BCP 47 tag the language's text carries. A real language takes
751
+ its own tag (`la`, `grc`). A constructed language takes `art-x-<name>`: `art`
752
+ is the registered code for artificial languages, and the private-use part
753
+ after `x` says which one. The tag is checked for its form and for repeated
754
+ variants or extension singletons. Whether each subtag is registered is not
755
+ checked.
756
+
757
+ **`note`** names the language note whose `data.words` is the language's
758
+ lexicon, as an Address in this package: `skill-vylarilng`, or the bare
759
+ shortcode, whose type defaults to `skill`. A run marked with the language and a
760
+ `map=` writes each word the lexicon states a native form for by that form, and
761
+ spells every other word by the map's rows; see
762
+ [a map reads the dictionary first](reference/format-details.md#a-map-reads-the-dictionary-first).
763
+ Every word of such a run is a word of the lexicon, of a name the language note's
764
+ own register lists, or a regular form of a lexicon word, and any other word is an error; see
765
+ [a mapped run's words](authoring/links-and-markup.md#a-mapped-runs-words).
766
+ A language with no `note` tags its text and has no lexicon. Whether the note
767
+ resolves is checked by `package-build lint`, at the key:
768
+
769
+ > ``package-build.config.yaml:5:5: error: `languages.vyl.note` is `skill-vylarilg`, which names no language note in thalorna; it names a skill note whose subType is language``
770
+
771
+ The shape is checked when the configuration loads, and each fault is a
772
+ configuration error at its key:
773
+
774
+ > ``package-build config: `languages` must be a mapping of language name to `{tag}`.``
775
+
776
+ > ``package-build config: `languages.Vyl` is not a name: a name is lowercase letters, digits and single hyphens, starting with a letter.``
777
+
778
+ > ``package-build config: `languages.vyl` must be a mapping with `tag`.``
779
+
780
+ > ``package-build config: `languages.vyl.script` is not a recognized option (expected one of: tag, note).``
781
+
782
+ > ``package-build config: `languages.vyl.note` must be the Address of a language note, such as `skill-vylarilng`.``
783
+
784
+ > ``package-build config: `languages.vyl.tag` must be a BCP 47 language tag, such as `art-x-vyl` for a constructed language.``
785
+
786
+ > ``package-build config: `languages.vyl.tag` art_x_vyl is not a valid BCP 47 language tag; a constructed language takes `art-x-<name>`.``
787
+
725
788
  ### `packs`
726
789
 
727
790
  **Type:** array of pack specs · **Required, at least one entry**, in a
@@ -31,6 +31,8 @@ Leading `:` captions describe the next supported block; `:@` captions also numbe
31
31
 
32
32
  A code fence with language `poetry` keeps verse lines, stanza breaks and relative indentation. Its numbered caption uses the **Poem** label. Generic `:::` divs and `[inline spans]{.class}` accept attributes. See [poetry](authoring/links-and-markup.md#poetry), [fenced divs](authoring/links-and-markup.md#fenced-divs) and [inline spans](authoring/links-and-markup.md#inline-spans).
33
33
 
34
+ A span, a generic div or a poetry fence marks its text's language with `lang=`, the font it is set in with `font=` and a mapping with `map=`, each optional, and the runs inside a div or a fence inherit its keys. `lang=` is a name the package declares in `languages` or a BCP 47 tag, and gives the language tag alone. `font=` is a face of the installed `@heroiclands/fonts`, named by its class without `hl-`, or `mono`, `serif` or `sans`. `map=` names a mapping note: a `doc` note whose `data.map` lists the fonts it is drawn in and rows that rewrite the text before it is set, a font's own rows first. With `lang=` naming a language whose configured `note` names its language note, a word whose entry states a `native` form for the map is written by it, and the rows spell every other word. Every word of such a run is a lexicon form, a word of a name the language note's own register lists under a tongue other than `translation`, or a form a plural row, the verb pattern or an affix stating `replaces` derives from a lexicon form, and any other word is an error at the word. A character a face does not draw, after the map, is an error at the character it came from. Text with a font, a map or a declared language is plain. Every marked construct is a row of the `runs` relation, every mapping row of the `maps` relation, and every native form of the `natives` relation. See [marking language, font and mapping](authoring/links-and-markup.md#marking-language-font-and-mapping).
35
+
34
36
  Mark a name without a note as `[Name]{.name data-type="being"}`; the optional
35
37
  classification uses the declared note vocabulary. The [name-marking
36
38
  guide](authoring/links-and-markup.md#marking-names-in-prose) explains its syntax,
@@ -173,6 +175,6 @@ Places, affiliations, lore and beings may name their culture in `data.culture`,
173
175
 
174
176
  An affiliation may name the trade, temple or order tongue it keeps in its own `data.language`. An affiliation that reaches no culture is a warning, in a tree holding a culture note. A language needs no culture and no affiliation. See [type: affiliation](reference/format-details.md#type-affiliation).
175
177
 
176
- A language note (`type: skill`, `subType: language`) states its words in frontmatter: `data.words` states each word as a map of its form, class, field and gloss — one sense or a list of them, which a dictionary numbers — with its source, its variant forms, the pieces it is built from and a note, beside `data.classModifiers`, `data.fields` and `data.affixes`, each affix with the `kind` it makes, for a given name's ending who the name is `given` to, and the stem closes it is barred after; a zero affix states `zero: true` and no form. The notes a word is cited from are derived, not written: a note cites a word by marking it in italics in its prose, matched by form or a variant with diacritics and case set aside. The language of a word is the note it sits on, and none of it reaches the compiled Item. A daughter language names its parent in `data.family` and reads the parent's words as its own; the `language_lineage` relation joins a language to its ancestors. Classes come from the [master word classes](reference/format-details.md#word-classes). A culture or an affiliation states its own glossary entries in `data.glossary`: common words in its core vocabulary, dictionary words with a context meaning, or phrases, compounds and terms of art outside the dictionary with their own gloss. An entry naming a word of its resolved language takes the word's senses and class and links to its language entry; a `definition` supplies the entry's meaning instead. Common marks belong to the glossary's owner. A language note with nonempty `data.words` receives its full Dictionary section automatically: one alphabetical list with field tags and one key. A culture or affiliation with nonempty `data.glossary` receives only its explicitly declared entries in its Glossary section; ranks and offices stand in the affiliation's own sections. A note without the relevant frontmatter receives no such section. All surfaces show these sections after the authored text, or under an existing `{#dictionary}`, `{#lexicon}` or `{#glossary}` heading after its introduction. There are no setting-wide dictionary or glossary pages. Every entry resolves to a language — its own `language`, else the affiliation's, its parents' or its culture's, else the culture's — or is an error at the entry. The `words`, `affixes`, `fields`, `glossary` and `language_lineage` relations hold them for `sql` fences, the `dictionary` relation lists each word under every language that has it, inherited words included, and the `terms` relation each glossary entry, rank and office with its culture associations; a fence sets custom views with `layout=dictionary` or `layout=glossary`. A language note names the tag a dictionary prints for it in `data.abbreviation`. Every fact a check reads about a language's names, sounds and history is frontmatter too: `data.tongues` and `data.register` (every name the setting gives in the language's reach, the notes carrying each derived from the index), `data.retired`, `data.scope`, `data.sounds` (letters with their place, word edges from a closed list, the cluster limit and the groups of clusters inside a word, the stress rule, syllable finals, syllable rules and vowel harmony with each vowel's front partner, the joining of parts, written marks, a root's skeleton vowel and frames, the spellings read past, and named mutations such as wearing and voicing), `data.soundChanges` (named sets of ordered steps), `data.elements` (each standing `opening`, `closing`, `either` or `link`, for one kind of name, with the word it is the stem of and its joining form), `data.roots`, `data.cognates` (with a reconstructed `proto` form), `data.callingNames`, `data.older` (the forms older than the rules), `data.negation` (the negative particle and the mutation it brings) and `data.naming` (how long each class of name runs, the edges names keep that words do not, the element kinds each kind of name draws on, what a place name takes from what it is held from, and the near name), each a relation — `sounds` as `letters`, `edges`, `clusters`, `cluster_groups`, `stress`, `finals`, `syllables`, `joining`, `joins`, `marks`, `skeleton`, `frames`, `read_past` and `mutations`, `soundChanges` as `sound_changes`, `callingNames` as `calling_names` — that a fence sets back on the page. A word's citations also come from a note whose name, alias or rank or office title spells it whole. See [a language's words](reference/format-details.md#a-languages-words), [a language's register, sounds and history](reference/format-details.md#a-languages-register-sounds-and-history), [glossary terms](reference/format-details.md#glossary-terms) and [dictionaries and glossaries](reference/format-details.md#dictionaries-and-glossaries).
178
+ A language note (`type: skill`, `subType: language`) states its words in frontmatter: `data.words` states each word as a map of its form, class, field and gloss — one sense or a list of them, which a dictionary numbers — with its source, its variant forms, the pieces it is built from and a note, beside `data.classModifiers`, `data.fields` and `data.affixes`, each affix with the `kind` it makes, for a given name's ending who the name is `given` to, and the stem closes it is barred after; a zero affix states `zero: true` and no form. The notes a word is cited from are derived, not written: a note cites a word by marking it in italics in its prose, matched by form or a variant with diacritics and case set aside. The language of a word is the note it sits on, and none of it reaches the compiled Item. A daughter language names its parent in `data.family` and reads the parent's words as its own; the `language_lineage` relation joins a language to its ancestors. Classes come from the [master word classes](reference/format-details.md#word-classes). A culture or an affiliation states its own glossary entries in `data.glossary`: common words in its core vocabulary, dictionary words with a context meaning, or phrases, compounds and terms of art outside the dictionary with their own gloss. An entry naming a word of its resolved language takes the word's senses and class and links to its language entry; a `definition` supplies the entry's meaning instead. Common marks belong to the glossary's owner. A language note with nonempty `data.words` receives its full Dictionary section automatically: one alphabetical list with field tags and one key. A culture or affiliation with nonempty `data.glossary` receives only its explicitly declared entries in its Glossary section; ranks and offices stand in the affiliation's own sections. A note without the relevant frontmatter receives no such section. All surfaces show these sections after the authored text, or under an existing `{#dictionary}`, `{#lexicon}` or `{#glossary}` heading after its introduction. There are no setting-wide dictionary or glossary pages. Every entry resolves to a language — its own `language`, else the affiliation's, its parents' or its culture's, else the culture's — or is an error at the entry. The `words`, `affixes`, `fields`, `glossary` and `language_lineage` relations hold them for `sql` fences, the `dictionary` relation lists each word under every language that has it, inherited words included, and the `terms` relation each glossary entry, rank and office with its culture associations; a fence sets custom views with `layout=dictionary` or `layout=glossary`. A language note names the tag a dictionary prints for it in `data.abbreviation`. Every fact a check reads about a language's names, sounds and history is frontmatter too: `data.tongues` and `data.register` (every name the setting gives in the language's reach, the notes carrying each derived from the index, and a name tagged `other:<tongue>` or `exonym:<tongue>` one that the tongue's own language note registers under that tongue), `data.retired`, `data.scope`, `data.sounds` (letters with their place, word edges from a closed list, the cluster limit and the groups of clusters inside a word, the stress rule, syllable finals, syllable rules and vowel harmony with each vowel's front partner, the joining of parts, written marks, a root's skeleton vowel and frames, the spellings read past, and named mutations such as wearing and voicing), `data.soundChanges` (named sets of ordered steps), `data.elements` (each standing `opening`, `closing`, `either` or `link`, for one kind of name, with the word it is the stem of and its joining form), `data.roots`, `data.cognates` (with a reconstructed `proto` form), `data.callingNames`, `data.older` (the forms older than the rules), `data.negation` (the negative particle and the mutation it brings) and `data.naming` (how long each class of name runs, the edges names keep that words do not, the element kinds each kind of name draws on, what a place name takes from what it is held from, and the near name), each a relation — `sounds` as `letters`, `edges`, `clusters`, `cluster_groups`, `stress`, `finals`, `syllables`, `joining`, `joins`, `marks`, `skeleton`, `frames`, `read_past` and `mutations`, `soundChanges` as `sound_changes`, `callingNames` as `calling_names` — that a fence sets back on the page. A word's citations also come from a note whose name, alias or rank or office title spells it whole. See [a language's words](reference/format-details.md#a-languages-words), [a language's register, sounds and history](reference/format-details.md#a-languages-register-sounds-and-history), [glossary terms](reference/format-details.md#glossary-terms) and [dictionaries and glossaries](reference/format-details.md#dictionaries-and-glossaries).
177
179
 
178
180
  Blockquote alerts use `[!NOTE]`, `[!TIP]`, `[!IMPORTANT]`, `[!WARNING]`, or `[!]`, with optional attributes after the marker. They replace the former info and warn div fences; secret blocks remain supported. See [alerts](authoring/links-and-markup.md#alerts).
@@ -503,6 +503,17 @@ successfully. The [content format](../content-format.md) covers icon names
503
503
  written in notes; the [configuration reference](../configuration.md) covers
504
504
  the font registry.
505
505
 
506
+ ### Fonts for marked text
507
+
508
+ Text marked with `font=` and one of the faces of `@heroiclands/fonts` is set in
509
+ that face once the package installs the fonts package in `devDependencies`.
510
+ The build then adds the fonts package's `fonts/` directory, which holds the
511
+ TrueType files, to the compiler's font search path after the shipped faces and
512
+ `fonts.path`. `font=serif`, `sans` and `mono` set the book's own serif, sans and
513
+ mono. A package without the fonts, or whose notes set no text in a face,
514
+ searches the same directories as before. See
515
+ [marking language, font and mapping](../authoring/links-and-markup.md#marking-language-font-and-mapping).
516
+
506
517
  ## Build and inspect
507
518
 
508
519
  ```bash
@@ -689,6 +689,22 @@ package-build: Cannot stage assets — these paths do not exist:
689
689
  lang
690
690
  ```
691
691
 
692
+ ### Fonts for marked text
693
+
694
+ A module shows text marked with `font=` and one of the faces of
695
+ `@heroiclands/fonts` in that face once the package installs the fonts package
696
+ in `devDependencies`. Nothing is configured: the build reads which faces the
697
+ notes use from their `font=` keys.
698
+
699
+ `package-build assets` then stages the font stylesheet, the WOFF2 files and the
700
+ licence under `fonts/heroiclands/` in the package; the staged stylesheet lists
701
+ only the WOFF2 sources, since the TrueType files are not staged. The generated manifest
702
+ names the stylesheet in `styles`, after the shared base sheet and before the
703
+ package's own. A package that has not installed the fonts, or whose notes set
704
+ no text in a face, stages nothing and its manifest names nothing. The stage
705
+ refuses a fonts package that lacks its stylesheet, its licence or a file its
706
+ manifest lists, with a finding at the fonts package's `fonts.json`.
707
+
692
708
  ## Step 9 — generate the manifest
693
709
 
694
710
  ```console
@@ -182,6 +182,25 @@ The build writes these keys; [what the theme draws](../reference/theme-pages.md)
182
182
  describes the panel each one feeds — the infobox, the hero band, the related
183
183
  card, and search — and the generated sections the theme draws as any other.
184
184
 
185
+ ## Show marked text in its font
186
+
187
+ A site shows text marked with `font=` and one of the faces of
188
+ `@heroiclands/fonts` in that face once the package installs the fonts package
189
+ in `devDependencies`, as the
190
+ [authoring guide](../authoring/links-and-markup.md#marking-language-font-and-mapping)
191
+ describes. Nothing else is configured: the build reads which faces the notes use
192
+ from their `font=` keys.
193
+
194
+ `package-build site` copies the font stylesheet, the WOFF2 files and the
195
+ licence to a path that names the fonts package's version,
196
+ `fonts/heroiclands/<version>/` beside the pages, and the theme links the
197
+ stylesheet from every page. The copied stylesheet lists only the WOFF2
198
+ sources, since the TrueType files are not copied. `package-build site-root` gives that path a
199
+ one-year, immutable cache lifetime in `_headers`. A package that has not
200
+ installed the fonts, or whose notes set no text in a face, ships no font files
201
+ and its pages carry no link. `font=serif`, `sans` and `mono` need no fonts
202
+ package.
203
+
185
204
  ## Build and serve
186
205
 
187
206
  ```bash