@heroiclands/package-build 24.0.1 → 24.0.3

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 (65) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/assets/preview/gitea.ini +1 -1
  3. package/assets/styles/content-base.css +33 -0
  4. package/bin/content-commands.mjs +17 -17
  5. package/content-config.mjs +1 -52
  6. package/docs/api.md +20 -13
  7. package/docs/authoring/links-and-markup.md +165 -17
  8. package/docs/commands.md +20 -20
  9. package/docs/configuration.md +29 -69
  10. package/docs/content-format.md +4 -2
  11. package/docs/guides/site.md +3 -1
  12. package/docs/guides/writing-in-invented-scripts.md +68 -6
  13. package/docs/reference/format-details.md +160 -118
  14. package/docs/reference/note-types.md +49 -29
  15. package/docs/source-preview.md +2 -2
  16. package/engine/content-blocks.mjs +6 -2
  17. package/engine/content-charset.mjs +1 -0
  18. package/engine/content-format.yaml +2 -0
  19. package/engine/content-index.mjs +5 -1
  20. package/engine/content-render.mjs +2 -1
  21. package/engine/content-scripts.mjs +268 -21
  22. package/engine/content-scroll.mjs +389 -0
  23. package/engine/dictionary-layout.mjs +16 -3
  24. package/engine/font-outlines.mjs +394 -0
  25. package/engine/foreign-catalog.mjs +11 -122
  26. package/engine/language-data.mjs +154 -63
  27. package/engine/language-rows.mjs +35 -9
  28. package/engine/language-terms.mjs +39 -9
  29. package/engine/map-lexicon.mjs +31 -6
  30. package/engine/note-vocabulary.mjs +58 -6
  31. package/engine/pdf-build.mjs +9 -178
  32. package/engine/pdf-fonts.mjs +1 -1
  33. package/engine/pdf-render.mjs +52 -11
  34. package/engine/run-rows.mjs +45 -3
  35. package/engine/script-direction.mjs +67 -0
  36. package/engine/script-emphasis-terms.mjs +11 -3
  37. package/engine/script-font-delivery.mjs +42 -9
  38. package/engine/script-fonts.mjs +7 -0
  39. package/engine/site-reach.mjs +23 -0
  40. package/engine/site-render.mjs +18 -3
  41. package/engine/verse-metre.mjs +1 -0
  42. package/engine/written-in.mjs +208 -0
  43. package/package.json +2 -1
  44. package/site-theme/static/css/style.css +7 -0
  45. package/types/content-config.d.mts +1 -19
  46. package/types/engine/content-scripts.d.mts +31 -6
  47. package/types/engine/content-scroll.d.mts +179 -0
  48. package/types/engine/dictionary-layout.d.mts +5 -0
  49. package/types/engine/font-outlines.d.mts +67 -0
  50. package/types/engine/foreign-catalog.d.mts +0 -38
  51. package/types/engine/language-data.d.mts +13 -0
  52. package/types/engine/language-rows.d.mts +5 -4
  53. package/types/engine/language-terms.d.mts +7 -0
  54. package/types/engine/map-lexicon.d.mts +4 -3
  55. package/types/engine/pdf-build.d.mts +5 -27
  56. package/types/engine/pdf-fonts.d.mts +10 -0
  57. package/types/engine/run-rows.d.mts +28 -6
  58. package/types/engine/script-direction.d.mts +10 -0
  59. package/types/engine/script-emphasis-terms.d.mts +10 -3
  60. package/types/engine/script-font-delivery.d.mts +5 -3
  61. package/types/engine/script-fonts.d.mts +9 -0
  62. package/types/engine/site-reach.d.mts +11 -0
  63. package/types/engine/written-in.d.mts +76 -0
  64. package/engine/asset-replacement.mjs +0 -93
  65. package/types/engine/asset-replacement.d.mts +0 -22
@@ -1026,12 +1026,12 @@ ships as the Foundry module `harn-ensemble`) writing `images/map.webp`:
1026
1026
  And for the same note writing `sohl/assets/icons/noun/shieldnn.svg`, a file the
1027
1027
  system ships and this repository does not hold:
1028
1028
 
1029
- | Surface | Address |
1030
- | ----------- | ------------------------------------------------------------- |
1031
- | **Foundry** | `systems/sohl/icons/shieldnn.svg` |
1032
- | **Local** | `assets/icons/noun/shieldnn.svg`, in the `sohl` repository |
1033
- | **Web** | `https://cdn.heroiclands.org/sohl/icons/shieldnn.svg` |
1034
- | **Book** | not carried, unless a declared replacement carries it instead |
1029
+ | Surface | Address |
1030
+ | ----------- | ---------------------------------------------------------- |
1031
+ | **Foundry** | `systems/sohl/icons/shieldnn.svg` |
1032
+ | **Local** | `assets/icons/noun/shieldnn.svg`, in the `sohl` repository |
1033
+ | **Web** | `https://cdn.heroiclands.org/sohl/icons/shieldnn.svg` |
1034
+ | **Book** | not carried |
1035
1035
 
1036
1036
  **The content package name and Foundry id are separate values.**
1037
1037
  `harnensemble` is what the content is called, what a note writes, and what the
@@ -1049,7 +1049,7 @@ does not know still resolves on the website and in the book — those need only
1049
1049
  the name and the suffix — and has no Foundry address, which is refused rather
1050
1050
  than guessed.
1051
1051
 
1052
- An art package that replaces another package's images can declare
1052
+ A package whose image files belong to another package's notes can declare
1053
1053
  `packageBuild.assets[].bindsTo` on the relevant asset copy. Each file's basename
1054
1054
  then acts as a shortcode for the declared package and type. The related
1055
1055
  `relationships.requires[].contentIndex` supplies the address index; with that
@@ -1057,29 +1057,6 @@ index enabled, an asset with no matching note is a located build error. See
1057
1057
  [asset bindings](../configuration.md#packagebuildstagedir-and-packagebuildassets)
1058
1058
  for the configuration and cache requirements.
1059
1059
 
1060
- **A package declared as a replacement is carried into the book, from its
1061
- fetched archive, when it answers the address; every other foreign package
1062
- stays not carried.** A relationship declaring `assetReplacement: true` names a
1063
- dependency whose asset tree answers this package's own addresses first — see
1064
- [`relationships`](../configuration.md#relationships) — and the book is the one
1065
- surface that otherwise refuses a foreign file outright, so it is the one
1066
- surface this changes. A `thalorna` note embedding
1067
- `![[thalornaaltart-none-icon-anvil|An anvil]]` names `thalornaaltart`'s own
1068
- address, exactly as a note citing `sohl`'s shared bestiary art would; when
1069
- `thalornaaltart` is declared with `assetReplacement: true` and its fetched
1070
- archive carries `icon-anvil` under its own package, the book stages that file
1071
- from the archive instead of printing the caption with no picture. A note
1072
- citing a different package's own address this way — `sohl`'s bestiary art,
1073
- say — stays refused exactly as the row above states, because `sohl` was never
1074
- declared as a replacement: a package answers in place of another only when it
1075
- opted in, never because its archive happens to carry a file at the same type
1076
- and shortcode.
1077
-
1078
- A relationship declaring `assetReplacement: true` whose archive was never
1079
- fetched is a configuration mistake, not an ordinary unresolved address: the
1080
- book reports a located error naming the relationship and instructing
1081
- `package-build deps fetch`, rather than printing a caption with no picture.
1082
-
1083
1060
  **The host is configuration.** `site.assets` in `package-build.config.yaml` is
1084
1061
  the root the web form is joined onto, and a package-owned image on a page with
1085
1062
  none set is an error naming that key.
@@ -1670,16 +1647,16 @@ An **`Address` field** takes the address with no brackets:
1670
1647
  ```yaml
1671
1648
  data:
1672
1649
  parents:
1673
- - hexhodai
1650
+ - zemorhodhis
1674
1651
  seat: tashal
1675
1652
  ```
1676
1653
 
1677
- not `[[hexhodai]]`. The field is declared as an `Address`, so the schema knows
1654
+ not `[[zemorhodhis]]`. The field is declared as an `Address`, so the schema knows
1678
1655
  what the value is and reads it as one; brackets are a wikilink's punctuation,
1679
1656
  which the reader would have to strip before it could do anything.
1680
1657
 
1681
1658
  A frontmatter value is parsed by the address grammar above, so a single-segment
1682
- value such as `hexhodai` names the target by its shortcode alone, with every
1659
+ value such as `zemorhodhis` names the target by its shortcode alone, with every
1683
1660
  other segment supplied by the position. Frontmatter is structure rather than
1684
1661
  prose: a field value is a reference something else will compile against, and it
1685
1662
  should say exactly what it points at.
@@ -1993,7 +1970,7 @@ errors.
1993
1970
 
1994
1971
  - **`lang=`** is a name declared in [`languages`](../configuration.md#languages),
1995
1972
  and otherwise a BCP 47 tag checked for its form alone. It gives the language
1996
- tag and changes nothing else.
1973
+ tag, and the language's script where its note names one.
1997
1974
  - **`font=`** is a face of the installed `@heroiclands/fonts`, named by its
1998
1975
  `fonts.json` class without `hl-`, or `mono`, `serif` or `sans`.
1999
1976
  - **`map=`** is the `data.map.name` of a [mapping note](#mapping-notes). The
@@ -2003,6 +1980,14 @@ errors.
2003
1980
  - **`lang=` with `map=`** reads the language's lexicon first, when
2004
1981
  [`languages.<name>.note`](../configuration.md#languages) names its language
2005
1982
  note: see [a map reads the dictionary first](#a-map-reads-the-dictionary-first).
1983
+ - **`lang=` without `map=` or `font=`** takes the map and the face its
1984
+ language note names in `data.writtenIn`, where the note states one. A run
1985
+ that states `font=` alone takes the language's map only where the map is
1986
+ drawn in that face, and one that states `map=` alone takes the language's
1987
+ face only where that map is drawn in it. A run stating both takes nothing.
1988
+ The default is read after a div or fence has given the run its keys, and is
1989
+ its own language's. See
1990
+ [a language's script](../authoring/links-and-markup.md#a-languages-script).
2006
1991
 
2007
1992
  **A face draws the code points its `fonts.json` entry lists in `codepoints`,
2008
1993
  and whitespace.** After the map, any other character in a run set in a face is
@@ -2022,9 +2007,10 @@ with a real language's tag alone keeps its Markdown. See
2022
2007
  [marking language, font and mapping](../authoring/links-and-markup.md#marking-language-font-and-mapping).
2023
2008
 
2024
2009
  Each own-note metadata record carries `runs` in source order, an empty array
2025
- when the body marks nothing. Every entry has exactly `kind` (`span`, `block` or
2010
+ when the body marks nothing. Every entry has exactly `kind` (`span`, `block`, `scroll` or
2026
2011
  `verse`), `lang`, `tag`, `font`, `map`, `line` and `column`: `lang`, `font` and
2027
- `map` as written, its own or inherited, `tag` the BCP 47 tag `lang` resolves
2012
+ `map` as written, its own or inherited, else its language's `data.writtenIn`
2013
+ (a scroll fence that names no face takes its map's), `tag` the BCP 47 tag `lang` resolves
2028
2014
  to, each `null` when absent, and the position of the span's `[`, the div's
2029
2015
  `:::` or the fence's opening. Every value is a string or a number and none is a
2030
2016
  key, so tags that differ only in case are separate values. This is derived
@@ -2364,8 +2350,9 @@ the words' native forms; `package-build lexicon` resolves them.
2364
2350
 
2365
2351
  #### Script notes and emphasis
2366
2352
 
2367
- A `skill` note of `subType: script` states the faces its script is written in
2368
- and how the script marks a stressed word, in `data.script`:
2353
+ A `skill` note of `subType: script` states the faces its script is written in,
2354
+ how the script marks a stressed word, and the direction it is written in, in
2355
+ `data.script`:
2369
2356
 
2370
2357
  ```yaml
2371
2358
  data:
@@ -2381,6 +2368,25 @@ data:
2381
2368
  (or `_…_` and `__…__`) inside a run in one of the faces.
2382
2369
  - **`strongEmphasis`**, optional, is `rubric` or `overline`, and sets `**…**`
2383
2370
  alone.
2371
+ - **`direction`**, optional, is `vertical-rl` for a script written in columns,
2372
+ top to bottom, with the columns read from the right. A
2373
+ [`scroll` fence](../authoring/links-and-markup.md#scrolls) sets text in
2374
+ such a script, and is an error in any other. A script that states no
2375
+ direction is written in lines.
2376
+
2377
+ A `skill` note of `subType: language` may state `data.writtenIn`, the script
2378
+ the language is written in: `map`, the `data.map.name` of a mapping note, and
2379
+ `font`, one of the faces that map lists, both required.
2380
+
2381
+ ```yaml
2382
+ data:
2383
+ writtenIn: { map: semaron, font: semaron-kuriale }
2384
+ ```
2385
+
2386
+ A run marked with the language alone is mapped by `map` and set in `font`, as
2387
+ [text marked with a language, a font or a map](#text-marked-with-a-language-a-font-or-a-map)
2388
+ describes. A face a language's `writtenIn` names counts as used, so the
2389
+ package's fonts are delivered for it.
2384
2390
 
2385
2391
  A `skill` note of `subType: language` may state `data.emphasis`: `words`,
2386
2392
  `rubric` or `overline`. It overrides the script's convention in every run of the
@@ -2410,10 +2416,18 @@ Each of these is an error at the key or the entry at fault:
2410
2416
 
2411
2417
  > `` `data.script.emphasis` should be `rubric` or `overline`, but reads "rubrik". Did you mean "rubric"? ``
2412
2418
 
2419
+ > `` `data.script.direction` should be `vertical-rl`, but reads "vertical" ``
2420
+
2413
2421
  > `` `data.script` states a script's faces and emphasis, and this note's `subType` is not `script` ``
2414
2422
 
2415
2423
  > `` `data.emphasis` states how a language marks emphasis, and this note's `subType` is not `language` ``
2416
2424
 
2425
+ > `` `data.writtenIn` names the script a language is written in, and this note's `subType` is not `language` ``
2426
+
2427
+ > `` `data.writtenIn.map` is `semoran`, which no mapping note's `data.map.name` declares; did you mean `semaron`? ``
2428
+
2429
+ > `` `data.writtenIn.font` is `teng-chou`, which the map `semaron` is not drawn in; it is drawn in `semaron-kuriale` ``
2430
+
2417
2431
  > `` `data.script.fonts` is empty; a script lists each face it is written in ``
2418
2432
 
2419
2433
  > `` `data.script.fonts[0]` is `serif`, a generic family; a script lists the faces it is written in ``
@@ -2785,37 +2799,6 @@ and keeps the excluded picture's address in its flag or `data-asset`. A
2785
2799
  package that leaves out a picture a note references and ships no stand-in of
2786
2800
  that type fails the build with an error at the referencing note.
2787
2801
 
2788
- ##### A declared replacement resolves first
2789
-
2790
- A package may declare, on a `relationships.<kind>[]` entry, that a
2791
- dependency's asset tree answers this package's own asset addresses ahead of
2792
- its local record — `assetReplacement: true` in `package-build.config.yaml`.
2793
- Once declared, an asset address resolves in this order:
2794
-
2795
- 1. **Every declared replacement, in declaration order.** The package segment
2796
- of each relationship declaring `assetReplacement: true` is tried in the
2797
- order those relationships are written, and the first one carrying the
2798
- address wins.
2799
- 2. **Falling through to the package's own record** when no declared
2800
- replacement carries the address — the resolution this package already
2801
- performs with no `assetReplacement` declared at all.
2802
-
2803
- **This reaches asset types only.** A being, an item or a lore note is never
2804
- replaced this way, whatever a relationship declares: the rule tests the
2805
- address's type, not the relationship. A replacement answers only `icon`,
2806
- `image` and `audio` addresses whose package segment is this package's own; an
2807
- address naming any other type, or naming a different package, is untouched.
2808
-
2809
- **A replacement's own output address is never itself looked up against
2810
- another replacement.** Resolving `thalornaaltart-none-image-thorn` never asks
2811
- whether something replaces `thalornaaltart` — resolution is a single pass over
2812
- the declared list, not a chain, which forecloses a cycle by construction.
2813
-
2814
- This is distinct from a `foreign` lookup, which serves a declared dependency's
2815
- own content for an address this package's local index does not carry. A
2816
- replacement is consulted **before** the local address; a foreign lookup only
2817
- **after** it misses locally.
2818
-
2819
2802
  #### A font is not an asset
2820
2803
 
2821
2804
  `assets/fonts` is not a root, and a font has no address.
@@ -3546,16 +3529,16 @@ for the keys.
3546
3529
  | `tongues` | `data.tongues` | `tag`, `meaning`, and `tongue_language`, the tongue's `language` |
3547
3530
  | `retired` | `data.retired` | `form`, `instead` (always a list, `NULL` where nothing is written instead), `why` |
3548
3531
  | `scope` | `data.scope` | No `position`: `kind`, the key the value is under (`folders`, `cultures`, `lore`, `parents`, `addresses`, `paths`, `types` or `skip`), and `value`, an Address in its canonical form or a path or a type |
3549
- | `letters` | `data.sounds.letters` | `letters` (a list), `kind`, `like`, `place`, `tongue` (a list, `NULL` for every tongue) |
3532
+ | `letters` | `data.sounds.letters` | `letters` (a list), `kind`, `like`, `place`, `ipa`, `tongue` (a list, `NULL` for every tongue); a [spelling guide's](#a-spelling-guides-sounds) rows too, keyed by the guide |
3550
3533
  | `edges` | `data.sounds.edges` | `edge`, `sounds` (a list: `[any]` for every sound, empty for none) |
3551
3534
  | `clusters` | `data.sounds.clusters` | No `position`: `max`, one row per language stating it |
3552
3535
  | `stress` | `data.sounds.stress` | No `position`: `two`, `longer` and `mark` (`NULL` where unstated), one row per language stating it |
3553
- | `finals` | `data.sounds.finals` | `final`, `like` |
3536
+ | `finals` | `data.sounds.finals` | `final`, `like`, `ipa` |
3554
3537
  | `syllables` | `data.sounds.syllables` | No `position`, one row per language stating it: `onsets`, `codas`, `finals`, `doubled`, `vowelPairs`, `neverInitial` and `signature` (lists, `any` a list of one), `initialVowel` and `finalVowel` (`true` or `false`), and the harmony as `harmony_back`, `harmony_front`, `harmony_neutral`, `harmony_neutralAs` and `harmony_partners` (a list of `{ vowel, partner }`) |
3555
3538
  | `joining` | `data.sounds.joining` | `first` and `second` (lists), `written`, `example` |
3556
3539
  | `joins` | `data.sounds.joins` | `meeting`, `rule`, `letters` (a list), `where` |
3557
- | `marks` | `data.sounds.marks` | `mark`, `letters` (a list), `kind`, `limit` |
3558
- | `skeleton` | `data.sounds.skeleton` | `second` (a list, `[any]` for every other), `between` |
3540
+ | `marks` | `data.sounds.marks` | `mark`, `letters` (a list), `kind`, `limit`; a spelling guide's rows too, keyed by the guide |
3541
+ | `skeleton` | `data.sounds.skeleton` | `second` (a list, `[any]` for every other), `third` (the same), `between` |
3559
3542
  | `frames` | `data.sounds.frames` | `frame`, `shape`, `class` (a list), `makes`, `examples` (a list of `{ root, form }`) |
3560
3543
  | `read_past` | `data.sounds.readPast` | `written`, `readAs`, `what` |
3561
3544
  | `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` |
@@ -3616,7 +3599,7 @@ including on an empty relation:
3616
3599
  | Column | SQL type | Meaning |
3617
3600
  | -------- | --------- | -------------------------------------------------------------------------------------------- |
3618
3601
  | `owner` | `VARCHAR` | Containing authored page's canonical NOTE Address, or `NULL` when the record has no Address. |
3619
- | `kind` | `VARCHAR` | `span`, `block` or `verse`. |
3602
+ | `kind` | `VARCHAR` | `span`, `block`, `verse` or `scroll`. |
3620
3603
  | `lang` | `VARCHAR` | The language as written, its own or inherited, or `NULL`. |
3621
3604
  | `tag` | `VARCHAR` | The BCP 47 tag `lang` resolves to, or `NULL` when it resolves to none. |
3622
3605
  | `font` | `VARCHAR` | The font as written, its own or inherited, or `NULL`. |
@@ -6181,12 +6164,13 @@ SoHL sets `system.quantity` to `1` for each compendium item.
6181
6164
  | `scope.types` | `string[]` | The note types covered; a note of another type is out of scope whatever else catches it |
6182
6165
  | `scope.skip` | `Address[]` | Notes out of scope whatever else catches them |
6183
6166
  | `sounds` | `{ letters?, edges?, clusters?, stress?, finals?, syllables?, joining?, joins?, marks?, skeleton?, frames?, readPast? }` | `language` only: the letters it writes and the rules its words keep |
6184
- | `sounds.letters` | `{ letters, kind, like?, place?, tongue? }[]` | The letters and the sounds they stand for |
6167
+ | `sounds.letters` | `{ letters, kind, like?, place?, ipa?, tongue? }[]` | The letters and the sounds they stand for |
6185
6168
  | `sounds.letters[].letters` | `string[]`, required | The letters or digraphs the row describes |
6186
6169
  | `sounds.letters[].kind` | `string`, required | What kind of sound they write, as `consonant` or `long vowel` |
6187
6170
  | `sounds.letters[].like` | `string` | What they sound like; absent where the row states only their kind and place |
6188
6171
  | `sounds.letters[].place` | `string` | Where in the mouth they are made, as `lips` or `tongue-tip` |
6189
- | `sounds.letters[].tongue` | `string` or `string[]` | The tongues that write them, as tags `tongues` declares; every one where absent |
6172
+ | `sounds.letters[].ipa` | `string` | The sounds they write, in the International Phonetic Alphabet, as `ʃ` |
6173
+ | `sounds.letters[].tongue` | `string` or `string[]` | The tongues that write them, each a tag `tongues` declares or a language note's Address; every one where absent |
6190
6174
  | `sounds.edges` | `{ edge, sounds }[]` | The sounds that may stand at each edge of a word, one entry per edge |
6191
6175
  | `sounds.edges[].edge` | `openConsonant`, `openCluster`, `closeConsonant`, `closeCluster`, `innerCluster`, `vowelPair`, `glide` or `syllabic`, required | Which edge the sounds stand at |
6192
6176
  | `sounds.edges[].sounds` | `string[]` or `any`, required | The sounds; `any` for every one, an empty list for none |
@@ -6198,11 +6182,12 @@ SoHL sets `system.quantity` to `1` for each compendium item.
6198
6182
  | `sounds.clusters.groups[].use` | `common` or `sparing`, required | How often the group's clusters stand inside a word |
6199
6183
  | `sounds.stress` | `{ two, longer, mark? }` | Where a word's stress falls, as a rule a program reads |
6200
6184
  | `sounds.stress.two` | `first` or `last`, required | The stressed syllable of a word of two syllables |
6201
- | `sounds.stress.longer` | `antepenultimate`, `penultimate` or `final`, required | The stressed syllable of a word of three or more, counted from its end |
6185
+ | `sounds.stress.longer` | `first`, `antepenultimate`, `penultimate` or `final`, required | The stressed syllable of a word of three or more: the first, or counted from its end |
6202
6186
  | `sounds.stress.mark` | `acute`, `grave` or `circumflex` | The diacritic that marks a stress the rule would not choose; it moves the stress to the syllable it sits on |
6203
- | `sounds.finals` | `{ final, like }[]` | The syllable finals, for a language whose syllables are parsed by them |
6187
+ | `sounds.finals` | `{ final, like, ipa? }[]` | The syllable finals, for a language whose syllables are parsed by them |
6204
6188
  | `sounds.finals[].final` | `string`, required | The final, as `ang` |
6205
6189
  | `sounds.finals[].like` | `string`, required | What it sounds like |
6190
+ | `sounds.finals[].ipa` | `string` | Its sound, in the International Phonetic Alphabet |
6206
6191
  | `sounds.syllables` | map of syllable rules | What a syllable and a word may open and close on |
6207
6192
  | `sounds.syllables.onsets` | `single` | What a syllable opens on: a single consonant |
6208
6193
  | `sounds.syllables.codas` | `string[]` or `any` | The consonants that may close a syllable |
@@ -6233,10 +6218,11 @@ SoHL sets `system.quantity` to `1` for each compendium item.
6233
6218
  | `sounds.marks` | `{ mark, letters, kind, limit }[]` | The marks the language writes on its letters |
6234
6219
  | `sounds.marks[].mark` | `string`, required | The mark's name, as `acute` |
6235
6220
  | `sounds.marks[].letters` | `string[]`, required | The letters that carry it |
6236
- | `sounds.marks[].kind` | `stress` or `length`, required | What it says of its letter |
6221
+ | `sounds.marks[].kind` | `stress`, `length` or `apart`, required | What it says of its letter |
6237
6222
  | `sounds.marks[].limit` | `one` or `any`, required | How many a word may carry |
6238
- | `sounds.skeleton` | `{ second, between? }[]` | The vowel set between a root's second and third consonants |
6223
+ | `sounds.skeleton` | `{ second, third?, between? }[]` | The vowel set between a root's second and third consonants |
6239
6224
  | `sounds.skeleton[].second` | `string[]` or `any`, required | The second consonants the row holds for; `any` for every other |
6225
+ | `sounds.skeleton[].third` | `string[]` or `any` | The third consonants the row holds for; every third consonant where absent |
6240
6226
  | `sounds.skeleton[].between` | `string` | The vowel set between; nothing where absent or empty |
6241
6227
  | `sounds.frames` | `{ frame, shape, class, makes, examples? }[]` | The patterns a root is set in |
6242
6228
  | `sounds.frames[].frame` | `string`, required | The frame's name, which a word's `frame` names |
@@ -6332,10 +6318,14 @@ SoHL sets `system.quantity` to `1` for each compendium item.
6332
6318
  | `affixes[].native[].font` | `string` | The one face the writing is for, one the map lists; unstated, every face it lists |
6333
6319
  | `affixes[].native[].text` | `string`, required | The affix as the script writes it |
6334
6320
  | `emphasis` | `words`, `rubric` or `overline` | `language` only: how the language marks a stressed word, over its script's; `words` makes `*…*` and `**…**` in its runs an error — see [script notes and emphasis](#script-notes-and-emphasis) |
6335
- | `script` | `{ fonts, emphasis, strongEmphasis? }` | `script` only: the faces the script is written in and how it marks emphasis — see [script notes and emphasis](#script-notes-and-emphasis) |
6321
+ | `writtenIn` | `{ map, font }` | `language` only: the script the language is written in, which a run marked with the language alone takes — see [script notes and emphasis](#script-notes-and-emphasis) |
6322
+ | `writtenIn.map` | `string`, required | The `data.map.name` of a mapping note |
6323
+ | `writtenIn.font` | `string`, required | A face the map lists |
6324
+ | `script` | `{ fonts, emphasis, strongEmphasis?, direction? }` | `script` only: the faces the script is written in, how it marks emphasis and its direction — see [script notes and emphasis](#script-notes-and-emphasis) |
6336
6325
  | `script.fonts` | `string[]`, required | Installed faces by the slug `font=` names them by; a face belongs to one script |
6337
6326
  | `script.emphasis` | `rubric` or `overline`, required | Sets `*…*` and `**…**` in a run in one of its faces |
6338
6327
  | `script.strongEmphasis` | `rubric` or `overline` | Sets `**…**` instead of `emphasis` |
6328
+ | `script.direction` | `vertical-rl` | Written in columns, top to bottom, read from the right, which a [`scroll` fence](../authoring/links-and-markup.md#scrolls) sets |
6339
6329
 
6340
6330
  #### A language's words
6341
6331
 
@@ -6815,8 +6805,11 @@ unless `types` is stated and leaves its type out, or `skip` names it.
6815
6805
 
6816
6806
  **`sounds` states the letters and the rules a word keeps.** `letters` gives
6817
6807
  each letter or group of letters, the kind of sound it writes, what it sounds
6818
- like, where in the mouth it is made and, in a language whose tongues write
6819
- differently, the tongues that write it. A row that states only a kind and a
6808
+ like, where in the mouth it is made, its sound in the International Phonetic
6809
+ Alphabet as `ipa` and, in a language whose tongues write differently, the
6810
+ tongues that write it. Each `tongue` is a tag `tongues` declares, or else the
6811
+ Address of a language note, so a row can name another language's note
6812
+ directly. A row that states only a kind and a
6820
6813
  place — one cell of a grid of consonants by manner and place — states no
6821
6814
  `like`. `edges` gives the sounds that may stand at each edge of a word, one
6822
6815
  entry per edge, the edge from a closed list:
@@ -6846,14 +6839,15 @@ closed list where its values are words:
6846
6839
 
6847
6840
  - `stress`: the rule a program reads, `{ two, longer, mark? }`. `two` is the
6848
6841
  stressed syllable of a word of two syllables, `first` or `last`; `longer` is
6849
- the stressed syllable of a word of three or more, counted from its end,
6850
- `antepenultimate`, `penultimate` or `final`; `mark` is the diacritic, `acute`,
6842
+ the stressed syllable of a word of three or more, `first`, or counted from its
6843
+ end, `antepenultimate`, `penultimate` or `final`; `mark` is the diacritic, `acute`,
6851
6844
  `grave` or `circumflex`, that marks a stress the rule would not choose and
6852
6845
  moves it to the syllable it sits on. Syllables are vowel groups, a pair listed
6853
6846
  in `syllables.vowelPairs` counting as one. A verse fence's `scan=` counts
6854
6847
  beats by this rule; see [metres](#metres).
6855
- - `finals`: the syllable finals, each with what it sounds like, for a language
6856
- whose syllables are parsed by them.
6848
+ - `finals`: the syllable finals, each with what it sounds like and, as `ipa`,
6849
+ its sound in the International Phonetic Alphabet, for a language whose
6850
+ syllables are parsed by them.
6857
6851
  - `syllables`: what a syllable opens on (`single`, a single consonant), the
6858
6852
  `codas` that close a syllable and the `finals` that close a word (a list, or
6859
6853
  `any`), the `doubled` consonants and `vowelPairs` a word may hold (none where
@@ -6870,10 +6864,11 @@ closed list where its values are words:
6870
6864
  - `joins`: what happens where a consonant or a vowel meets another (`C+C`,
6871
6865
  `V+C`, `V+V`): `merge`, `insert` the `letters` between, `drop` the first, or
6872
6866
  `double` the consonant, everywhere or `where` stated.
6873
- - `marks`: the marks written on letters, each saying `stress` or `length`, and
6867
+ - `marks`: the marks written on letters, each saying `stress`, `length`, or `apart` (the mark sets a vowel to be said apart from its neighbour, as a diaeresis does), and
6874
6868
  whether a word carries `one` at most or `any`.
6875
6869
  - `skeleton`: the vowel set between a root's second and third consonants, by
6876
- the second consonant; nothing where none is stated.
6870
+ the second consonant and, where a row states `third`, the third; nothing
6871
+ where none is stated.
6877
6872
  - `frames`: the patterns a root is set in, each with its shape, the word
6878
6873
  classes it makes, what it makes, and the form each root takes in it.
6879
6874
  - `mutations`: the sounds a word changes where a named mutation holds — a stop
@@ -7040,6 +7035,40 @@ FROM edges WHERE language = 'thalorna-note-skill-dunharlng' ORDER BY position
7040
7035
  `cognates` read the same way; [content tables](#content-tables) gives every
7041
7036
  relation's columns.
7042
7037
 
7038
+ #### A spelling guide's sounds
7039
+
7040
+ One spelling guide can state the letters every tongue of a setting is written
7041
+ by. A `doc` note of any subType may state `data.sounds` with `letters` and
7042
+ `marks` alone, each row with a language note's keys, `ipa` included:
7043
+
7044
+ ```yaml
7045
+ type: doc
7046
+ subType: reference
7047
+ data:
7048
+ sounds:
7049
+ letters:
7050
+ - { letters: [sh], kind: consonant, like: ship, ipa: ʃ }
7051
+ - {
7052
+ letters: [ui],
7053
+ kind: vowel pair,
7054
+ like: gooey,
7055
+ ipa: ui,
7056
+ tongue: [skill-tantsoulng, skill-jurthatlng],
7057
+ }
7058
+ marks:
7059
+ - { mark: diaeresis, letters: [ë], kind: apart, limit: any }
7060
+ ```
7061
+
7062
+ A guide declares no tags, so a letter row's `tongue` names language notes by
7063
+ their Addresses, and each must resolve to a `skill` of `subType: language`. Any
7064
+ other key of `sounds` is an error at the key, since the rules of a word belong
7065
+ to its language. The rows are rows of the `letters` and `marks` relations, with
7066
+ the guide's Address as `language`. Each of these is an error at the value:
7067
+
7068
+ > `` `data.sounds.letters[1].tongue` names `vylari`, which is no language note's Address; a spelling guide names each tongue by its language note ``
7069
+
7070
+ > `` `data.sounds.letters[1].tongue[1]` names thalorna-note-skill-riding, which is not a skill whose subType is language ``
7071
+
7043
7072
  if a `sohl` property is present, a SoHL item of type "skill" is created.
7044
7073
 
7045
7074
  If an `hm3` property is present, an HM3 item of type "skill" is created.
@@ -8190,35 +8219,48 @@ Content prepared to be played — a situation with its cast, places, and possibl
8190
8219
  - settingguide: A culture's guide to the setting, which names its culture in `data.culture`. It orients a reader to a setting or region, bringing its places, peoples, institutions, and daily life into one usable frame with links to detailed notes.
8191
8220
  - post: A blog post, which states the day it was published in `data.date` and may name its writer in `data.author`. A site whose `site.feed` names a section lists its posts there, newest first, and feeds them as Atom.
8192
8221
 
8193
- | `data` property | Values | Description |
8194
- | --------------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
8195
- | `culture` | `Address` | `settingguide` only, and required there: the culture the guide introduces, naming a lore note with `subType: culture` |
8196
- | `concepts` | list of `{ id, gloss, group }` | A core vocabulary's concept list, any subType. See [the core vocabulary](#the-core-vocabulary) |
8197
- | `date` | `YYYY-MM-DD` | `post` only, and required there: the day the post was published |
8198
- | `author` | string | `post` only: who wrote the post; a post naming none carries the site's byline, the first entry of `authors` |
8199
- | `concepts[].id` | string | Required: lowercase letters, digits and `-`, unique in the package |
8200
- | `concepts[].gloss` | string | Required: what the concept means, in brief |
8201
- | `concepts[].group` | string | Required: the group the report lists the concept under |
8202
- | `map` | `{ name, vowels?, fonts, rows }` | A mapping, which text marked `map=` with its `name` is rewritten by; any subType. See [mapping notes](#mapping-notes) |
8203
- | `map.name` | string | Required: what `map=` names it by, lowercase letters, digits and `-`, unique in the index |
8204
- | `map.vowels` | string | The characters `before: vowel` reads as vowels |
8205
- | `map.rows` | list of `[from, to]` or `{ from, to, before?, at? }` | Required: the rewrites, in authored order |
8206
- | `map.rows[].from` | string of one or more characters | Required in a map row: the characters it matches |
8207
- | `map.rows[].to` | string, which may be empty | Required in a map row: what it writes in their place |
8208
- | `map.rows[].before` | `vowel \| consonant` | What must follow the match |
8209
- | `map.rows[].at` | `start \| end` | Where the match must sit |
8210
- | `map.fonts` | list of `{ font, rows? }` | Required, at least one: the faces the map is drawn in |
8211
- | `map.fonts[].font` | an installed face's slug | Required: the face, as `font=` names it; not `mono`, `serif` or `sans` |
8212
- | `map.fonts[].rows` | list of `[from, to]` or `{ from, to, before?, at? }` | Rows for this face alone, tried before the shared `rows` |
8213
- | `map.fonts[].rows[].from` | string of one or more characters | Required in a map row: the characters it matches |
8214
- | `map.fonts[].rows[].to` | string, which may be empty | Required in a map row: what it writes in their place |
8215
- | `map.fonts[].rows[].before` | `vowel \| consonant` | What must follow the match |
8216
- | `map.fonts[].rows[].at` | `start \| end` | Where the match must sit |
8217
- | `metres` | `{ name, beats, kind }[]` | The metres a verse fence's `scan=` names; any subType. See [metres](#metres) |
8218
- | `metres[].name` | string | Required: what `scan=` names it by, lowercase letters, digits and `-`, unique in the package |
8219
- | `metres[].beats` | `integer`, required | The beats of one line, above zero |
8220
- | `metres[].kind` | `accentual` | Required: how the beats are counted |
8221
- | `map.logographic` | `true` | The script writes meanings: a run's words are written by the lexicon alone, and the rows write only the text between them |
8222
+ | `data` property | Values | Description |
8223
+ | --------------------------- | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
8224
+ | `culture` | `Address` | `settingguide` only, and required there: the culture the guide introduces, naming a lore note with `subType: culture` |
8225
+ | `concepts` | list of `{ id, gloss, group }` | A core vocabulary's concept list, any subType. See [the core vocabulary](#the-core-vocabulary) |
8226
+ | `date` | `YYYY-MM-DD` | `post` only, and required there: the day the post was published |
8227
+ | `author` | string | `post` only: who wrote the post; a post naming none carries the site's byline, the first entry of `authors` |
8228
+ | `concepts[].id` | string | Required: lowercase letters, digits and `-`, unique in the package |
8229
+ | `concepts[].gloss` | string | Required: what the concept means, in brief |
8230
+ | `concepts[].group` | string | Required: the group the report lists the concept under |
8231
+ | `map` | `{ name, vowels?, fonts, rows }` | A mapping, which text marked `map=` with its `name` is rewritten by; any subType. See [mapping notes](#mapping-notes) |
8232
+ | `map.name` | string | Required: what `map=` names it by, lowercase letters, digits and `-`, unique in the index |
8233
+ | `map.vowels` | string | The characters `before: vowel` reads as vowels |
8234
+ | `map.rows` | list of `[from, to]` or `{ from, to, before?, at? }` | Required: the rewrites, in authored order |
8235
+ | `map.rows[].from` | string of one or more characters | Required in a map row: the characters it matches |
8236
+ | `map.rows[].to` | string, which may be empty | Required in a map row: what it writes in their place |
8237
+ | `map.rows[].before` | `vowel \| consonant` | What must follow the match |
8238
+ | `map.rows[].at` | `start \| end` | Where the match must sit |
8239
+ | `map.fonts` | list of `{ font, rows? }` | Required, at least one: the faces the map is drawn in |
8240
+ | `map.fonts[].font` | an installed face's slug | Required: the face, as `font=` names it; not `mono`, `serif` or `sans` |
8241
+ | `map.fonts[].rows` | list of `[from, to]` or `{ from, to, before?, at? }` | Rows for this face alone, tried before the shared `rows` |
8242
+ | `map.fonts[].rows[].from` | string of one or more characters | Required in a map row: the characters it matches |
8243
+ | `map.fonts[].rows[].to` | string, which may be empty | Required in a map row: what it writes in their place |
8244
+ | `map.fonts[].rows[].before` | `vowel \| consonant` | What must follow the match |
8245
+ | `map.fonts[].rows[].at` | `start \| end` | Where the match must sit |
8246
+ | `metres` | `{ name, beats, kind }[]` | The metres a verse fence's `scan=` names; any subType. See [metres](#metres) |
8247
+ | `metres[].name` | string | Required: what `scan=` names it by, lowercase letters, digits and `-`, unique in the package |
8248
+ | `metres[].beats` | `integer`, required | The beats of one line, above zero |
8249
+ | `metres[].kind` | `accentual` | Required: how the beats are counted |
8250
+ | `map.logographic` | `true` | The script writes meanings: a run's words are written by the lexicon alone, and the rows write only the text between them |
8251
+ | `sounds` | `{ letters?, marks? }` | A spelling guide's letters and marks, for every tongue of the setting; any subType. See [a spelling guide's sounds](#a-spelling-guides-sounds) |
8252
+ | `sounds.letters` | `{ letters, kind, like?, place?, ipa?, tongue? }[]` | The letters, each row's keys as a language note's `sounds.letters` |
8253
+ | `sounds.letters[].letters` | `string[]`, required | The letters or digraphs the row describes |
8254
+ | `sounds.letters[].kind` | `string`, required | What kind of sound they write, as `consonant` or `vowel pair` |
8255
+ | `sounds.letters[].like` | `string` | What they sound like |
8256
+ | `sounds.letters[].place` | `string` | Where in the mouth they are made |
8257
+ | `sounds.letters[].ipa` | `string` | The sounds they write, in the International Phonetic Alphabet |
8258
+ | `sounds.letters[].tongue` | an Address, or a list of them | The language notes that write the letters; every tongue where absent |
8259
+ | `sounds.marks` | `{ mark, letters, kind, limit }[]` | The marks written on the letters, each row's keys as a language note's `sounds.marks` |
8260
+ | `sounds.marks[].mark` | `string`, required | The mark's name, as `acute` |
8261
+ | `sounds.marks[].letters` | `string[]`, required | The letters that carry it |
8262
+ | `sounds.marks[].kind` | `stress`, `length` or `apart`, required | What it says of its letter |
8263
+ | `sounds.marks[].limit` | `one` or `any`, required | How many a word may carry |
8222
8264
 
8223
8265
  A setting guide introduces one culture's view of the setting, so it names that
8224
8266
  culture, and a `doc` of any other `subType` has no culture of its own to name.