@heroiclands/package-build 24.0.4 → 24.1.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.
@@ -1962,32 +1962,45 @@ exit status. Supplied-value errors prevent publication and compilation.
1962
1962
 
1963
1963
  #### Text marked with a language, a font or a map
1964
1964
 
1965
- `lang=`, `font=` and `map=` on a span, a generic div or a verse fence are read
1966
- by the build and never written through as ordinary attributes. Each is
1967
- optional, and a run inside a div or a fence inherits the keys the div or fence
1968
- states, its own keys overriding them. On a named block the three keys are
1969
- errors.
1965
+ `lang=` identifies a span, generic div, verse fence or scroll fence's
1966
+ language while retaining authored spelling and the surrounding prose font.
1967
+ `.nativefont` explicitly requests native presentation and requires `lang=` on
1968
+ that same construct; an inherited language does not satisfy the requirement.
1969
+ On a named block the language, font and map keys are errors.
1970
1970
 
1971
1971
  - **`lang=`** is a name declared in [`languages`](../configuration.md#languages),
1972
- and otherwise a BCP 47 tag checked for its form alone. It gives the language
1973
- tag, and the language's script where its note names one.
1974
- - **`font=`** is a face of the installed `@heroiclands/fonts`, named by its
1975
- `fonts.json` class without `hl-`, or `mono`, `serif` or `sans`.
1976
- - **`map=`** is the `data.map.name` of a [mapping note](#mapping-notes). The
1977
- text is rewritten by the map before it is set; without `map=` it is set as
1978
- typed. With `font=`, the face is one the map lists in `fonts`, and its own
1979
- rows come first.
1980
- - **`lang=` with `map=`** reads the language's lexicon first, when
1981
- [`languages.<name>.note`](../configuration.md#languages) names its language
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).
1972
+ otherwise a BCP 47 tag checked for its form alone. Language identification
1973
+ and its existing validation do not depend on native presentation.
1974
+ - **`.nativefont`** selects that language's `data.writtenIn` map and font.
1975
+ A native run needs an effective font; a missing default reports the
1976
+ required `font=` override. The map is optional when the language supplies
1977
+ none, so directly typed script code points can be set without mapping. The
1978
+ build does not guess a font from a map's list of faces.
1979
+ - **`font=`** overrides the native font independently. Without `.nativefont`,
1980
+ only `mono`, `serif` and `sans` are permitted.
1981
+ - **`map=`** overrides the native map independently and requires `.nativefont`.
1982
+ It names a [mapping note](#mapping-notes). With a configured language note,
1983
+ mapping reads that language's lexicon first; see
1984
+ [a map reads the dictionary first](#a-map-reads-the-dictionary-first).
1985
+
1986
+ Unmarked text follows its passage. Each marked child chooses its presentation:
1987
+ a language-only child stays Roman, even inside a native passage of that same
1988
+ language. A native child requires its own explicit `lang=` and `.nativefont`,
1989
+ uses its own language's defaults and applies only its own overrides.
1990
+
1991
+ Standalone wikilinks accept the same attributes. `[[address|Name]]{lang=vyl}`
1992
+ stays Roman, while `[[address|Name]]{lang=vyl .nativefont}` applies native
1993
+ mapping and font to the displayed name alone. Targets, spoilers, `.show`,
1994
+ draft annotations and book page cues retain their behavior. Adjacent prose
1995
+ belongs in sibling spans; an embedded link inside a longer native run is an error.
1996
+ Parallel verse uses `cols=N`, a positive whole number defaulting to one.
1997
+ With multiple columns, each content line starts with its column number and
1998
+ one or more spaces. Blank-separated logical rows accept nondecreasing
1999
+ numbers in `1..cols`, preserving omitted cells and any number of repeated
2000
+ readings. The first reading in each column is foreground and all later ones
2001
+ in that column are grey; each row resets that choice. `numstep` counts logical
2002
+ rows, and `***` separates stanzas. Language and native script choices remain
2003
+ on the authored inline runs, independently for every reading.
1991
2004
 
1992
2005
  **A face draws the code points its `fonts.json` entry lists in `codepoints`,
1993
2006
  and whitespace.** After the map, any other character in a run set in a face is
@@ -2007,13 +2020,15 @@ with a real language's tag alone keeps its Markdown. See
2007
2020
  [marking language, font and mapping](../authoring/links-and-markup.md#marking-language-font-and-mapping).
2008
2021
 
2009
2022
  Each own-note metadata record carries `runs` in source order, an empty array
2010
- when the body marks nothing. Every entry has exactly `kind` (`span`, `block`, `scroll` or
2023
+ when the body marks nothing. Every entry has `kind` (`span`, `block`, `scroll` or
2011
2024
  `verse`), `lang`, `tag`, `font`, `map`, `line` and `column`: `lang`, `font` and
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
2025
+ `map` as written, or its language's `data.writtenIn` defaults only for an
2026
+ explicit `.nativefont` run, `tag` the BCP 47 tag `lang` resolves
2014
2027
  to, each `null` when absent, and the position of the span's `[`, the div's
2015
- `:::` or the fence's opening. Every value is a string or a number and none is a
2016
- key, so tags that differ only in case are separate values. This is derived
2028
+ `:::` or the fence's opening. An opted-in run also carries optional `nativefont: true`; Roman runs omit
2029
+ that flag and leave unused script defaults null. The `runs` SQL relation
2030
+ retains its existing columns and projects no flag column. Tags differing
2031
+ only in case remain separate values. This is derived
2017
2032
  metadata, not frontmatter. The [`runs` relation](#content-tables) flattens
2018
2033
  these entries with their containing NOTE Address.
2019
2034
 
@@ -2167,7 +2182,7 @@ is written word by word:
2167
2182
  plural or a verb ending of an exception word is a different word, and the
2168
2183
  rows spell it.
2169
2184
 
2170
- A run with `map=` and no `lang=`, a `lang=` that is a bare BCP 47 tag, and a
2185
+ A native run with an explicit bare BCP 47 `lang=` tag, and a
2171
2186
  declared language with no `note` are spelled by the rows alone. A character the
2172
2187
  face does not draw inside a word written by its native form is an error at the
2173
2188
  word's start:
@@ -2176,7 +2191,9 @@ word's start:
2176
2191
 
2177
2192
  > `` `zaron` is written U+F024F by the lexicon of `vyl` for the map `semaron`, which the font `semaron-kuriale` does not draw ``
2178
2193
 
2179
- **Every word of such a run is a word of the language**, case folded: a
2194
+ **Language word-book validation applies to Roman and native runs alike**,
2195
+ independently of `.nativefont` or mapping. Every word is a word of the
2196
+ language, case folded: a
2180
2197
  `data.words[].form`, a word of a name the language note's own `data.register`
2181
2198
  lists under a `tongue` other than `translation`, or a form one rule derives from a lexicon form —
2182
2199
  the `data.grammar.plural` plural of a noun or its own `plural`, a verb's form
@@ -2383,7 +2400,8 @@ data:
2383
2400
  writtenIn: { map: semaron, font: semaron-kuriale }
2384
2401
  ```
2385
2402
 
2386
- A run marked with the language alone is mapped by `map` and set in `font`, as
2403
+ A run explicitly marked with the language and `.nativefont` is mapped by
2404
+ `map` and set in `font`, as
2387
2405
  [text marked with a language, a font or a map](#text-marked-with-a-language-a-font-or-a-map)
2388
2406
  describes. A face a language's `writtenIn` names counts as used, so the
2389
2407
  package's fonts are delivered for it.
@@ -3602,8 +3620,8 @@ including on an empty relation:
3602
3620
  | `kind` | `VARCHAR` | `span`, `block`, `verse` or `scroll`. |
3603
3621
  | `lang` | `VARCHAR` | The language as written, its own or inherited, or `NULL`. |
3604
3622
  | `tag` | `VARCHAR` | The BCP 47 tag `lang` resolves to, or `NULL` when it resolves to none. |
3605
- | `font` | `VARCHAR` | The font as written, its own or inherited, or `NULL`. |
3606
- | `map` | `VARCHAR` | The map as written, its own or inherited, or `NULL`. |
3623
+ | `font` | `VARCHAR` | The selected font; language defaults apply only to native runs, else `NULL`. |
3624
+ | `map` | `VARCHAR` | The selected map; language defaults apply only to native runs, else `NULL`. |
3607
3625
  | `line` | `BIGINT` | Original 1-based source line of the span's `[`, the div's `:::` or the fence's opening. |
3608
3626
  | `column` | `BIGINT` | Original 1-based UTF-16 source column there. |
3609
3627
 
@@ -6318,7 +6336,7 @@ SoHL sets `system.quantity` to `1` for each compendium item.
6318
6336
  | `affixes[].native[].font` | `string` | The one face the writing is for, one the map lists; unstated, every face it lists |
6319
6337
  | `affixes[].native[].text` | `string`, required | The affix as the script writes it |
6320
6338
  | `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) |
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) |
6339
+ | `writtenIn` | `{ map, font }` | `language` only: the script the language is written in, which a run explicitly marked with the language and `.nativefont` takes — see [script notes and emphasis](#script-notes-and-emphasis) |
6322
6340
  | `writtenIn.map` | `string`, required | The `data.map.name` of a mapping note |
6323
6341
  | `writtenIn.font` | `string`, required | A face the map lists |
6324
6342
  | `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) |
@@ -483,7 +483,7 @@ These fields are accepted by every note type. A field's value can still be irrel
483
483
  | `data.negation` | a map — `{ particle, mutation? }` | How a verb is negated. A language skill note only. |
484
484
  | `data.naming` | a map — `{ shapes?, edges?, draws?, heldFrom?, nearName? }` | The shapes, edges and sources the language's names keep beyond its words' rules. A language skill note only. |
485
485
  | `data.emphasis` | `words`, `rubric` or `overline` | How the language marks a stressed word, over its script's `emphasis`: `rubric` or `overline` sets `*…*` and `**…**` so in a run of the language, and `words` marks it with a word, so either is a finding. Only a skill of `subType: language` states it. |
486
- | `data.writtenIn` | a map — `{ map, font }` | The script the language is written in, as the map and the face a run marked with its name alone takes; a run's own `map=` or `font=` overrides it. Only a skill of `subType: language` states it. |
486
+ | `data.writtenIn` | a map — `{ map, font }` | The script the language is written in, as the default map and face selected by a run with explicit `lang=` and `.nativefont`; a run's own `map=` or `font=` overrides its half. Only a skill of `subType: language` states it. |
487
487
  | `data.script` | a map — `{ fonts, emphasis, strongEmphasis?, direction? }` | A script note's faces, how it marks emphasis and the direction it is written in. Only a skill of `subType: script` states it. |
488
488
 
489
489
  **Inner keys** — a key a field's value does not declare is an error at its own line and column.
@@ -1000,15 +1000,15 @@ Lore records in-world knowledge. A `culture` describes a people; a `custom` desc
1000
1000
 
1001
1001
  **Subtypes:** `rules`, `userguide`, `reference`, `howto`, `concept`, `settingguide`, `post`.
1002
1002
 
1003
- | Field | Shape | Meaning |
1004
- | --------------- | ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1005
- | `data.concepts` | list of `{ id, gloss, group }` | The concepts of a core vocabulary, in the order the report lists them. See [the core vocabulary](format-details.md#the-core-vocabulary). |
1006
- | `data.date` | string | The day a post was published, as `YYYY-MM-DD`. Required on a `post`; no other subType states it. Posts are listed and fed newest first by it. |
1007
- | `data.author` | string | Who wrote a post, as the byline prints them. A post that names none carries the site's byline, the first entry of `authors`; no other subType states it. |
1008
- | `data.culture` | an Address | The culture a setting guide introduces, as a culture lore note. Required on `settingguide`; no other subType states it. |
1009
- | `data.sounds` | a map — `{ letters?, marks? }` | A spelling guide's letters and the marks written on them, for every tongue of the setting: each letter row's `tongue` names the language notes that write it. |
1010
- | `data.map` | a map — `{ name, logographic?, vowels?, fonts, rows }` | A mapping, which text marked `map=` with its `name` is rewritten by before it is set. See [mapping notes](format-details.md#mapping-notes). |
1011
- | `data.metres` | list of `{ name, beats, kind }` | The metres a verse fence's `scan=` names. A fence that states one is warned at each line that cannot have its beats. See [metres](../authoring/links-and-markup.md#metre). |
1003
+ | Field | Shape | Meaning |
1004
+ | --------------- | ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1005
+ | `data.concepts` | list of `{ id, gloss, group }` | The concepts of a core vocabulary, in the order the report lists them. See [the core vocabulary](format-details.md#the-core-vocabulary). |
1006
+ | `data.date` | string | The day a post was published, as `YYYY-MM-DD`. Required on a `post`; no other subType states it. Posts are listed and fed newest first by it. |
1007
+ | `data.author` | string | Who wrote a post, as the byline prints them. A post that names none carries the site's byline, the first entry of `authors`; no other subType states it. |
1008
+ | `data.culture` | an Address | The culture a setting guide introduces, as a culture lore note. Required on `settingguide`; no other subType states it. |
1009
+ | `data.sounds` | a map — `{ letters?, marks? }` | A spelling guide's letters and the marks written on them, for every tongue of the setting: each letter row's `tongue` names the language notes that write it. |
1010
+ | `data.map` | a map — `{ name, logographic?, vowels?, fonts, rows }` | A mapping, which native text marked with `lang=`, `.nativefont` and its `map=` name is rewritten by before it is set. See [mapping notes](format-details.md#mapping-notes). |
1011
+ | `data.metres` | list of `{ name, beats, kind }` | The metres a verse fence's `scan=` names. A fence that states one is warned at each line that cannot have its beats. See [metres](../authoring/links-and-markup.md#metre). |
1012
1012
 
1013
1013
  **Inner keys** — a key a field's value does not declare is an error at its own line and column.
1014
1014
 
@@ -340,10 +340,18 @@ keys reach the block as written, `lang` as its language and the rest as
340
340
  numbered line starts with its `.verse-number`.
341
341
 
342
342
  A [verse in columns](../authoring/links-and-markup.md#verse-in-columns) also
343
- carries `.verse-columns`, and each verse line is one `.verse-row`:
343
+ carries `.verse-columns`, and each logical row is one `.verse-row`. The
344
+ block carries `data-cols` and `--verse-columns` with its positive column
345
+ count. Every row contains that many `.verse-column` cells, including empty
346
+ ones:
344
347
 
345
348
  ```html
346
- <div class="verse verse-columns verse-numbered" lang="art-x-vyl" data-numstep="5">
349
+ <div
350
+ class="verse verse-columns verse-numbered"
351
+ lang="art-x-vyl"
352
+ data-numstep="5"
353
+ data-cols="3"
354
+ style="--verse-columns: 3">
347
355
  <p>
348
356
  <span class="verse-row"
349
357
  ><span class="verse-number"></span
@@ -351,6 +359,7 @@ carries `.verse-columns`, and each verse line is one `.verse-row`:
351
359
  ><span class="verse-part"
352
360
  ><span class="script hl-semaron-kuriale" lang="art-x-vyl">…</span></span
353
361
  ></span
362
+ ><span class="verse-column"></span
354
363
  ><span class="verse-column"
355
364
  ><span class="verse-part"><span lang="en">The hallowed boundary I sing,</span></span
356
365
  ><span class="verse-part verse-secondary"
@@ -363,10 +372,10 @@ carries `.verse-columns`, and each verse line is one `.verse-row`:
363
372
  </div>
364
373
  ```
365
374
 
366
- A numbered verse in columns gives every row a `.verse-number`, empty on the
367
- lines it does not number, so the columns stay aligned. A part whose text runs
368
- right to left carries `dir="rtl"`. Each row is its own grid, so a long line
369
- keeps its partner level with it. Below a block width of `32rem` a row stacks
375
+ A numbered verse in columns gives every logical row a `.verse-number`, empty
376
+ on rows it does not number, so repeated readings do not advance the counter.
377
+ The grid remains left to right while inline text follows its own script
378
+ direction. Each row has its own grid and aligns all its cells at the top. Below a block width of `32rem` a row stacks
370
379
  its columns in order; it is a container query, so a narrow journal window
371
380
  stacks as well. The Foundry stylesheet draws the same.
372
381
 
@@ -377,10 +386,10 @@ stacks as well. The Foundry stylesheet draws the same.
377
386
  | `.verse-numbered` | a numbered poem, with a margin for its numbers |
378
387
  | `.verse-number` | a line's number, muted |
379
388
  | `.verse-columns` | a poem in columns, and the container its rows are measured by |
380
- | `.verse-row` | one verse line: its number and its columns |
381
- | `.verse-column` | one column, its entries stacked |
389
+ | `.verse-row` | one logical verse row: its number and its cells |
390
+ | `.verse-column` | one numbered cell, possibly empty, with its readings stacked |
382
391
  | `.verse-part` | one entry's text, in the body serif unless its face sets it |
383
- | `.verse-secondary` | every entry after a column's first, muted |
392
+ | `.verse-secondary` | every reading after a column's first in that row, grey |
384
393
 
385
394
  ## Emphasis in a script
386
395
 
@@ -77,7 +77,7 @@ import { imagePlugin } from "./content-images.mjs";
77
77
  import { renderRuns } from "./content-scripts.mjs";
78
78
  import { renderSpans, spanMarkdownPlugin } from "./content-spans.mjs";
79
79
  import { cartoucheFigures, renderSymbolSpans } from "./content-symbols.mjs";
80
- import { inlineVerse, verseStanzas } from "./content-verse.mjs";
80
+ import { hasColumns, inlineVerse, scanVerse, verseGroups, verseStanzas } from "./content-verse.mjs";
81
81
  import { replaceOutsideCode } from "./code-fences.mjs";
82
82
  import { dictionaryEntryPlugin } from "./dictionary-layout.mjs";
83
83
  import { headingAttributesPlugin, headingIdsPlugin } from "./heading-attributes.mjs";
@@ -129,11 +129,32 @@ function verseInlinePlugin(md) {
129
129
  if (token.type !== "fence" || !/^verse(?:\s|$)/.test(token.info?.trim() || ""))
130
130
  continue;
131
131
  token.meta ??= {};
132
+ const source = `${token.markup}${token.info}\n${token.content}${token.markup}`;
133
+ const poem = scanVerse(source).blocks[0];
134
+ if (hasColumns(poem)) {
135
+ token.meta.verseGroups = verseGroups(poem, source.split("\n")).map((group) => {
136
+ if (group.stanzaBreak) return group;
137
+ return {
138
+ ...group,
139
+ parts: group.parts.map((part) => {
140
+ const children = [];
141
+ state.md.inline.parse(
142
+ inlineVerse(part.text),
143
+ state.md,
144
+ state.env,
145
+ children,
146
+ );
147
+ return { ...part, children };
148
+ }),
149
+ };
150
+ });
151
+ continue;
152
+ }
132
153
  token.meta.verseStanzas = verseStanzas(token.content).map((stanza) =>
133
154
  stanza.map(({ text, level }) => {
134
155
  const children = [];
135
156
  state.md.inline.parse(inlineVerse(text), state.md, state.env, children);
136
- return { children, level };
157
+ return { children, level, stanzaBreak: /^\*{3}$/.test(text) };
137
158
  }),
138
159
  );
139
160
  }