@heroiclands/package-build 23.13.0 → 24.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/README.md +2 -1
- package/assets/preview/gitea.ini +12 -7
- package/assets/styles/content-base.css +150 -1
- package/bin/content-commands.mjs +356 -210
- package/bin/package-build.mjs +3 -2
- package/ci/check-banners.mjs +7 -7
- package/config.mjs +21 -6
- package/content-config.mjs +199 -80
- package/docs/api.md +93 -72
- package/docs/authoring/links-and-markup.md +316 -78
- package/docs/commands.md +244 -78
- package/docs/configuration.md +210 -102
- package/docs/content-format.md +2 -2
- package/docs/diagnostics.md +2 -2
- package/docs/getting-started.md +1 -1
- package/docs/guides/icons.md +1 -1
- package/docs/guides/module-project.md +5 -8
- package/docs/guides/release-and-deploy.md +2 -2
- package/docs/guides/site.md +112 -23
- package/docs/guides/system-project.md +1 -1
- package/docs/guides/writing-in-invented-scripts.md +1247 -0
- package/docs/index.md +1 -0
- package/docs/project-setup.md +28 -30
- package/docs/reference/format-details.md +589 -308
- package/docs/reference/note-types.md +40 -10
- package/docs/reference/theme-pages.md +127 -108
- package/docs/source-preview.md +15 -1
- package/engine/asset-shipping.mjs +22 -10
- package/engine/base-compiler.mjs +62 -104
- package/engine/changelog-lint.mjs +1 -1
- package/engine/content-address.mjs +1 -1
- package/engine/content-alerts.mjs +8 -4
- package/engine/content-blocks.mjs +26 -11
- package/engine/content-embeds.mjs +1 -1
- package/engine/content-expand.mjs +376 -0
- package/engine/content-figures.mjs +13 -6
- package/engine/content-format.yaml +8 -0
- package/engine/content-glyphs.mjs +213 -0
- package/engine/content-images.mjs +6 -7
- package/engine/content-links.mjs +3 -3
- package/engine/content-lint.mjs +1 -1
- package/engine/content-render.mjs +453 -0
- package/engine/content-scripts.mjs +326 -27
- package/engine/content-spans.mjs +4 -1
- package/engine/content-styles.mjs +91 -0
- package/engine/content-symbols.mjs +186 -0
- package/engine/content-verse.mjs +6 -18
- package/engine/core-report.mjs +169 -0
- package/engine/core-vocabulary.mjs +208 -0
- package/engine/dictionary-html.mjs +119 -0
- package/engine/field-reference.mjs +1 -1
- package/engine/frontmatter-lint.mjs +1 -1
- package/engine/heading-attributes.mjs +89 -0
- package/engine/helpers.mjs +61 -101
- package/engine/homepage.mjs +17 -29
- package/engine/html-minify.mjs +70 -0
- package/engine/index.mjs +8 -5
- package/engine/language-rows.mjs +8 -2
- package/engine/language-terms.mjs +115 -14
- package/engine/language-words.mjs +12 -0
- package/engine/logographic.mjs +255 -0
- package/engine/map-lexicon.mjs +221 -25
- package/engine/map-places.mjs +5 -4
- package/engine/markdown-expressions.mjs +0 -2
- package/engine/markdownlint-run.mjs +9 -3
- package/engine/markup-validation.mjs +21 -0
- package/engine/metre-declaration.mjs +134 -0
- package/engine/note-matter.mjs +2 -2
- package/engine/note-vocabulary.mjs +187 -3
- package/engine/pdf-build.mjs +68 -133
- package/engine/pdf-render.mjs +102 -44
- package/engine/post-fields.mjs +157 -0
- package/engine/preview-index.mjs +130 -0
- package/engine/preview-source.mjs +450 -0
- package/engine/project-init.mjs +5 -12
- package/engine/prose-config.mjs +3 -3
- package/engine/run-words.mjs +3 -2
- package/engine/script-emphasis-terms.mjs +38 -0
- package/engine/script-emphasis.mjs +295 -0
- package/engine/script-font-delivery.mjs +4 -4
- package/engine/site-build.mjs +259 -314
- package/engine/site-config.mjs +26 -238
- package/engine/site-files.mjs +175 -0
- package/engine/site-index.mjs +9 -4
- package/engine/site-preview.mjs +50 -523
- package/engine/site-render.mjs +593 -0
- package/engine/site-root.mjs +22 -13
- package/engine/site-search.mjs +161 -0
- package/engine/site-serve.mjs +273 -0
- package/engine/site-theme-banner.mjs +154 -0
- package/engine/site-theme-urls.mjs +125 -0
- package/engine/site-theme.mjs +926 -0
- package/engine/text-maps.mjs +57 -10
- package/engine/typographer.mjs +248 -0
- package/engine/verse-metre.mjs +357 -0
- package/engine/wikilink-syntax.mjs +29 -47
- package/engine/wikilinks.mjs +758 -279
- package/engine/word-forms.mjs +39 -24
- package/engine/word-rows.mjs +12 -2
- package/engine/word-terms.mjs +29 -4
- package/manifest.mjs +3 -4
- package/package.json +10 -7
- package/{hugo-theme → site-theme}/data/banners.yaml +6 -10
- package/site-theme/layouts/base.hbs +31 -0
- package/site-theme/layouts/blog-list.hbs +30 -0
- package/site-theme/layouts/blog-post.hbs +40 -0
- package/site-theme/layouts/home.hbs +35 -0
- package/site-theme/layouts/list.hbs +20 -0
- package/site-theme/layouts/not-found.hbs +31 -0
- package/site-theme/layouts/page.hbs +7 -0
- package/site-theme/partials/copy-buttons.hbs +37 -0
- package/site-theme/partials/draft-notice.hbs +5 -0
- package/site-theme/partials/exturl.hbs +2 -0
- package/{hugo-theme/layouts/partials/footer.html → site-theme/partials/footer.hbs} +10 -7
- package/site-theme/partials/header.hbs +44 -0
- package/site-theme/partials/hero-banner.hbs +11 -0
- package/site-theme/partials/infobox-entry.hbs +2 -0
- package/site-theme/partials/infobox-section.hbs +31 -0
- package/site-theme/partials/infobox-value.hbs +2 -0
- package/site-theme/partials/infobox.hbs +18 -0
- package/site-theme/partials/list-members.hbs +9 -0
- package/site-theme/partials/nav-links.hbs +5 -0
- package/site-theme/partials/page-body.hbs +48 -0
- package/site-theme/partials/page-list.hbs +14 -0
- package/site-theme/partials/related.hbs +24 -0
- package/site-theme/partials/search-meta.hbs +6 -0
- package/site-theme/partials/search-names.hbs +2 -0
- package/site-theme/partials/search.hbs +52 -0
- package/site-theme/partials/tags.hbs +4 -0
- package/site-theme/partials/toc.hbs +7 -0
- package/{hugo-theme → site-theme}/static/css/style.css +14 -0
- package/stage.mjs +1 -2
- package/types/config.d.mts +8 -4
- package/types/content-config.d.mts +15 -15
- package/types/engine/asset-shipping.d.mts +5 -2
- package/types/engine/base-compiler.d.mts +20 -2
- package/types/engine/content-address.d.mts +1 -1
- package/types/engine/content-alerts.d.mts +8 -2
- package/types/engine/content-blocks.d.mts +11 -4
- package/types/engine/content-expand.d.mts +288 -0
- package/types/engine/content-figures.d.mts +11 -3
- package/types/engine/content-glyphs.d.mts +85 -0
- package/types/engine/content-images.d.mts +6 -7
- package/types/engine/content-links.d.mts +1 -1
- package/types/engine/content-render.d.mts +199 -0
- package/types/engine/content-scripts.d.mts +62 -0
- package/types/engine/content-spans.d.mts +1 -1
- package/types/engine/content-styles.d.mts +33 -0
- package/types/engine/content-symbols.d.mts +35 -0
- package/types/engine/core-report.d.mts +45 -0
- package/types/engine/core-vocabulary.d.mts +53 -0
- package/types/engine/dictionary-html.d.mts +8 -0
- package/types/engine/field-reference.d.mts +1 -1
- package/types/engine/heading-attributes.d.mts +20 -0
- package/types/engine/helpers.d.mts +3 -10
- package/types/engine/homepage.d.mts +11 -24
- package/types/engine/html-minify.d.mts +9 -0
- package/types/engine/index.d.mts +2 -1
- package/types/engine/language-terms.d.mts +24 -2
- package/types/engine/logographic.d.mts +45 -0
- package/types/engine/map-lexicon.d.mts +48 -8
- package/types/engine/map-places.d.mts +8 -0
- package/types/engine/markdown-expressions.d.mts +0 -2
- package/types/engine/metre-declaration.d.mts +39 -0
- package/types/engine/pdf-render.d.mts +6 -2
- package/types/engine/post-fields.d.mts +44 -0
- package/types/engine/preview-index.d.mts +72 -0
- package/types/engine/preview-source.d.mts +51 -0
- package/types/engine/script-emphasis-terms.d.mts +21 -0
- package/types/engine/script-emphasis.d.mts +84 -0
- package/types/engine/script-font-delivery.d.mts +4 -4
- package/types/engine/site-build.d.mts +71 -113
- package/types/engine/site-config.d.mts +14 -134
- package/types/engine/site-files.d.mts +73 -0
- package/types/engine/site-index.d.mts +11 -3
- package/types/engine/site-preview.d.mts +15 -19
- package/types/engine/site-render.d.mts +171 -0
- package/types/engine/site-root.d.mts +12 -4
- package/types/engine/site-search.d.mts +54 -0
- package/types/engine/site-serve.d.mts +44 -0
- package/types/engine/site-theme-banner.d.mts +51 -0
- package/types/engine/site-theme-urls.d.mts +60 -0
- package/types/engine/site-theme.d.mts +127 -0
- package/types/engine/text-maps.d.mts +17 -3
- package/types/engine/typographer.d.mts +11 -0
- package/types/engine/verse-metre.d.mts +131 -0
- package/types/engine/wikilink-syntax.d.mts +30 -48
- package/types/engine/wikilinks.d.mts +330 -14
- package/types/engine/word-forms.d.mts +3 -2
- package/types/manifest.d.mts +3 -4
- package/types/stage.d.mts +1 -2
- package/types/vendor/markdownlint.d.mts +8 -0
- package/vendor/markdownlint.mjs +20388 -0
- package/engine/web-wikilinks.mjs +0 -594
- package/hugo-theme/layouts/404.html +0 -85
- package/hugo-theme/layouts/_default/baseof.html +0 -67
- package/hugo-theme/layouts/_default/list.html +0 -139
- package/hugo-theme/layouts/_default/single.html +0 -4
- package/hugo-theme/layouts/blog/list.html +0 -54
- package/hugo-theme/layouts/blog-post/single.html +0 -36
- package/hugo-theme/layouts/index.html +0 -75
- package/hugo-theme/layouts/partials/banner-url.html +0 -141
- package/hugo-theme/layouts/partials/cdn-url.html +0 -36
- package/hugo-theme/layouts/partials/dictionary-lists.html +0 -58
- package/hugo-theme/layouts/partials/draft-notice.html +0 -41
- package/hugo-theme/layouts/partials/draft-state.html +0 -26
- package/hugo-theme/layouts/partials/header.html +0 -35
- package/hugo-theme/layouts/partials/hero-banner.html +0 -85
- package/hugo-theme/layouts/partials/infobox/entry.html +0 -18
- package/hugo-theme/layouts/partials/infobox/items.html +0 -26
- package/hugo-theme/layouts/partials/infobox/section.html +0 -56
- package/hugo-theme/layouts/partials/infobox/value.html +0 -43
- package/hugo-theme/layouts/partials/infobox.html +0 -57
- package/hugo-theme/layouts/partials/page-body.html +0 -152
- package/hugo-theme/layouts/partials/related.html +0 -138
- package/hugo-theme/layouts/partials/search.html +0 -84
- package/hugo-theme/layouts/partials/site-url.html +0 -32
- package/hugo-theme/layouts/partials/tags.html +0 -30
- package/hugo-theme/layouts/partials/toc.html +0 -28
- package/hugo-theme/layouts/partials/warn-missing-banner.html +0 -3
- package/hugo-theme/layouts/shortcodes/cartouche.html +0 -64
- package/hugo-theme/layouts/shortcodes/exturl.html +0 -12
- package/hugo-theme/layouts/shortcodes/glyph.html +0 -43
- package/hugo-theme/layouts/shortcodes/pagelist.html +0 -63
- package/hugo-theme/theme.toml +0 -11
- package/types/engine/web-wikilinks.d.mts +0 -107
- /package/{hugo-theme → site-theme}/data/banner-kinds.yaml +0 -0
- /package/{hugo-theme → site-theme}/static/css/sohl-icons.css +0 -0
- /package/{hugo-theme → site-theme}/static/fonts/game-icons-kb.woff2 +0 -0
- /package/{hugo-theme → site-theme}/static/img/banners/takheperu-hieroglyphs.svg +0 -0
- /package/{hugo-theme → site-theme}/static/img/cartouches/takheperu.svg +0 -0
- /package/{hugo-theme → site-theme}/static/js/inline-spoilers.js +0 -0
|
@@ -18,13 +18,13 @@
|
|
|
18
18
|
*
|
|
19
19
|
* ```markdown
|
|
20
20
|
* [Daimela, helthos.]{lang=vyl}
|
|
21
|
-
* [Zaron tholmeza]{lang=vyl font=semaron-kuriale map=
|
|
21
|
+
* [Zaron tholmeza]{lang=vyl font=semaron-kuriale map=semaron}
|
|
22
22
|
*
|
|
23
|
-
* ::: {lang=vyl font=semaron-kuriale map=
|
|
23
|
+
* ::: {lang=vyl font=semaron-kuriale map=semaron}
|
|
24
24
|
* Zaron tholmeza.
|
|
25
25
|
* :::
|
|
26
26
|
*
|
|
27
|
-
* ```verse {form=epic lang=vyl font=semaron-kuriale map=
|
|
27
|
+
* ```verse {form=epic lang=vyl font=semaron-kuriale map=semaron}
|
|
28
28
|
* ```
|
|
29
29
|
*
|
|
30
30
|
* - **`lang=`** gives the language tag and changes nothing else. It is a name
|
|
@@ -39,6 +39,10 @@
|
|
|
39
39
|
* - **`lang=` with `map=` reads the language's lexicon first**: a word whose
|
|
40
40
|
* entry states a native form for the map is written by it, and the rows
|
|
41
41
|
* spell every other word; see {@link module:engine/map-lexicon}.
|
|
42
|
+
* - **A logographic map writes every word from the lexicon**: by the word's
|
|
43
|
+
* own writing or its parts', and a word with neither, or whose form stands
|
|
44
|
+
* for entries written differently, is a finding at the word, once per run;
|
|
45
|
+
* see {@link module:engine/logographic}.
|
|
42
46
|
* - **Every word of a run with `lang=` and `map=` is a word of its language**
|
|
43
47
|
* where the language's `note` names its language note: a lexicon form, a
|
|
44
48
|
* word of a name its own register lists, or a regular form of a lexicon
|
|
@@ -50,6 +54,10 @@
|
|
|
50
54
|
* - **Text with a font, a map or a declared language is plain.** A span or a
|
|
51
55
|
* verse holds no emphasis, link, image, footnote, wikilink, code or HTML,
|
|
52
56
|
* and no span but a marked one; a div holds paragraphs and line breaks only.
|
|
57
|
+
* - **Text set in a face takes `*…*` and `**…**`**, set by the convention its
|
|
58
|
+
* language's note states, else its face's script note, else plainly; see
|
|
59
|
+
* {@link module:engine/script-emphasis}. A language that marks emphasis
|
|
60
|
+
* with a word makes either a finding at the emphasis.
|
|
53
61
|
* The text is read as written, so `[`, `]`, `"`, `<<`, `>>`, `!` and
|
|
54
62
|
* `@#$%^&` are characters, and need no escaping. A real language's tag alone
|
|
55
63
|
* marks text and leaves its Markdown alone.
|
|
@@ -68,18 +76,30 @@
|
|
|
68
76
|
import MarkdownIt from "markdown-it";
|
|
69
77
|
|
|
70
78
|
import { scanBlocks, BLOCK_CONTAINERS } from "./content-blocks.mjs";
|
|
71
|
-
import {
|
|
79
|
+
import {
|
|
80
|
+
COLUMN_KEYS,
|
|
81
|
+
COLUMNS_CLASS,
|
|
82
|
+
expandVerse,
|
|
83
|
+
expandVerseOrigins,
|
|
84
|
+
scanVerse,
|
|
85
|
+
sourcePosition,
|
|
86
|
+
} from "./content-verse.mjs";
|
|
72
87
|
import { scanSpans } from "./content-spans.mjs";
|
|
73
88
|
import { isLanguageTag } from "./language-tag.mjs";
|
|
74
89
|
import { distance, nearest } from "./near-miss.mjs";
|
|
90
|
+
import { emphasesFromRecords, languageEmphasesOf } from "./script-emphasis.mjs";
|
|
91
|
+
import { EMPHASIS_CLASSES } from "./script-emphasis-terms.mjs";
|
|
75
92
|
import { FONTS_PACKAGE, resolveScriptFonts } from "./script-fonts.mjs";
|
|
76
93
|
import {
|
|
77
94
|
fontProblem,
|
|
95
|
+
languageRecordsByName,
|
|
78
96
|
lexiconsFromRecords,
|
|
79
97
|
mappingFor,
|
|
80
98
|
uncapitalisedMessage,
|
|
99
|
+
unwrittenMessage,
|
|
81
100
|
} from "./map-lexicon.mjs";
|
|
82
101
|
import { unknownWordMessage, unknownWords, wordBooksFromRecords } from "./run-words.mjs";
|
|
102
|
+
import { metreBooksFromRecords, metreFindings, metresFromRecords } from "./verse-metre.mjs";
|
|
83
103
|
import { applyMap, mapsFromRecords } from "./text-maps.mjs";
|
|
84
104
|
|
|
85
105
|
/** The keys this module reads, which no construct passes through as written. */
|
|
@@ -113,6 +133,17 @@ const CONFIG = "package-build.config.yaml";
|
|
|
113
133
|
* @property {ReadonlyMap<string, import("./run-words.mjs").WordBook>} [wordBooks] -
|
|
114
134
|
* What each configured language's mapped runs are checked against, from
|
|
115
135
|
* {@link module:engine/run-words.wordBooksFromRecords}.
|
|
136
|
+
* @property {ReadonlyMap<string, import("./metre-declaration.mjs").Metre>} [metres] -
|
|
137
|
+
* The metres the `doc` notes declare, by name.
|
|
138
|
+
* @property {ReadonlyMap<string, import("./verse-metre.mjs").MetreBook>} [metreBooks] -
|
|
139
|
+
* What each configured language's verse is counted by, from
|
|
140
|
+
* {@link module:engine/verse-metre.metreBooksFromRecords}.
|
|
141
|
+
* @property {ReadonlyMap<string, import("./script-emphasis.mjs").Emphasis>} [emphases] -
|
|
142
|
+
* Each face's emphasis conventions, from the script notes, by
|
|
143
|
+
* {@link module:engine/script-emphasis.emphasesFromRecords}.
|
|
144
|
+
* @property {ReadonlyMap<string, import("./script-emphasis.mjs").LanguageEmphasis>} [languageEmphases] -
|
|
145
|
+
* The convention each configured language's note states, by language name,
|
|
146
|
+
* from {@link module:engine/script-emphasis.languageEmphasesOf}.
|
|
116
147
|
*/
|
|
117
148
|
|
|
118
149
|
/**
|
|
@@ -181,6 +212,10 @@ export function runContext(config, records) {
|
|
|
181
212
|
maps: mapsFromRecords(records),
|
|
182
213
|
lexicons: lexiconsFromRecords(records, config),
|
|
183
214
|
wordBooks: wordBooksFromRecords(records, config),
|
|
215
|
+
metres: metresFromRecords(records),
|
|
216
|
+
metreBooks: metreBooksFromRecords(records, config),
|
|
217
|
+
emphases: emphasesFromRecords(records),
|
|
218
|
+
languageEmphases: languageEmphasesOf(languageRecordsByName(records, config)),
|
|
184
219
|
});
|
|
185
220
|
byRecords.set(records, context);
|
|
186
221
|
return context;
|
|
@@ -298,9 +333,11 @@ function closing(text, open) {
|
|
|
298
333
|
* @param {ReadonlyMap<number, number>} [holes] - Marked spans inside the
|
|
299
334
|
* text, each its offset of `[` to the offset past its attributes. They are
|
|
300
335
|
* runs of their own and checked as such.
|
|
336
|
+
* @param {boolean} [stars] - Whether `*…*`, `_…_`, `**…**` and `__…__` are
|
|
337
|
+
* emphasis the text may hold, as in a run in a script face; `~~` is not.
|
|
301
338
|
* @returns {Array<{offset: number, what: string}>} Each fault, in order.
|
|
302
339
|
*/
|
|
303
|
-
function inlineMarkup(text, footnotes, holes = new Map()) {
|
|
340
|
+
function inlineMarkup(text, footnotes, holes = new Map(), stars = false) {
|
|
304
341
|
const faults = [];
|
|
305
342
|
const skip = new Map();
|
|
306
343
|
const runs = [];
|
|
@@ -384,6 +421,10 @@ function inlineMarkup(text, footnotes, holes = new Map()) {
|
|
|
384
421
|
if (char === "*" || char === "_" || (char === "~" && text[i + 1] === "~")) {
|
|
385
422
|
let j = i;
|
|
386
423
|
while (text[j] === char) j++;
|
|
424
|
+
if (stars && char !== "~") {
|
|
425
|
+
i = j - 1;
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
387
428
|
const before = text[i - 1] ?? " ",
|
|
388
429
|
after = text[j] ?? " ";
|
|
389
430
|
const left =
|
|
@@ -401,7 +442,7 @@ function inlineMarkup(text, footnotes, holes = new Map()) {
|
|
|
401
442
|
i = j - 1;
|
|
402
443
|
}
|
|
403
444
|
}
|
|
404
|
-
let outside = text;
|
|
445
|
+
let outside = stars ? text.replace(/[*_]/g, " ") : text;
|
|
405
446
|
for (const [from, to] of holes)
|
|
406
447
|
outside = outside.slice(0, from) + " ".repeat(to - from) + outside.slice(to);
|
|
407
448
|
if (runs.length && holdsEmphasis(outside)) {
|
|
@@ -422,6 +463,91 @@ function inlineMarkup(text, footnotes, holes = new Map()) {
|
|
|
422
463
|
return faults.sort((a, b) => a.offset - b.offset);
|
|
423
464
|
}
|
|
424
465
|
|
|
466
|
+
/** A parser that reads emphasis and nothing else in text whose other punctuation is escaped. */
|
|
467
|
+
const starParser = new MarkdownIt({ html: false, linkify: false, typographer: false });
|
|
468
|
+
|
|
469
|
+
/** ASCII punctuation, which a backslash escapes. */
|
|
470
|
+
const ASCII_PUNCTUATION = /[!-/:-@[-`{-~]/;
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* One `*`, `_`, `**` or `__` that Markdown reads as opening or closing emphasis.
|
|
474
|
+
*
|
|
475
|
+
* @typedef {object} Delimiter
|
|
476
|
+
* @property {number} offset - Its first character's offset.
|
|
477
|
+
* @property {number} length - 1 for `*` or `_`, 2 for `**` or `__`.
|
|
478
|
+
* @property {"em"|"strong"} tag - Which level it opens or closes.
|
|
479
|
+
* @property {boolean} open - Whether it opens.
|
|
480
|
+
*/
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* The stars and underscores Markdown reads as emphasis in a run's text, as
|
|
484
|
+
* CommonMark pairs them. Every other character is read as itself: an escaped
|
|
485
|
+
* delimiter, or one that pairs with none, is a character of the text. A marked span inside the text
|
|
486
|
+
* reads as one letter, so emphasis may enclose it.
|
|
487
|
+
*
|
|
488
|
+
* @param {string} text - Inline text, as written.
|
|
489
|
+
* @param {ReadonlyMap<number, number>} [holes] - Marked spans inside it, by
|
|
490
|
+
* offset in `text`.
|
|
491
|
+
* @returns {Delimiter[]} Each delimiter, in order, at its offset in `text`.
|
|
492
|
+
*/
|
|
493
|
+
function emphasisDelimiters(text, holes = new Map()) {
|
|
494
|
+
let masked = "";
|
|
495
|
+
const stars = [];
|
|
496
|
+
for (let i = 0; i < text.length; i++) {
|
|
497
|
+
if (holes.has(i)) {
|
|
498
|
+
masked += "x";
|
|
499
|
+
i = /** @type {number} */ (holes.get(i)) - 1;
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
502
|
+
const char = text[i];
|
|
503
|
+
if (char === "\\" && ASCII_PUNCTUATION.test(text[i + 1] ?? "")) {
|
|
504
|
+
masked += text[i + 1] === "*" || text[i + 1] === "_" ? "\\!" : `\\${text[i + 1]}`;
|
|
505
|
+
i++;
|
|
506
|
+
} else if (char === "*" || char === "_") {
|
|
507
|
+
masked += char;
|
|
508
|
+
stars.push(i);
|
|
509
|
+
} else masked += ASCII_PUNCTUATION.test(char) ? `\\${char}` : char;
|
|
510
|
+
}
|
|
511
|
+
if (!stars.length) return [];
|
|
512
|
+
const delimiters = [];
|
|
513
|
+
let at = 0;
|
|
514
|
+
for (const token of starParser.parseInline(masked, {})[0]?.children ?? []) {
|
|
515
|
+
if (token.type === "text") at += (token.content.match(/[*_]/g) ?? []).length;
|
|
516
|
+
else if (/^(?:em|strong)_(?:open|close)$/.test(token.type)) {
|
|
517
|
+
delimiters.push({
|
|
518
|
+
offset: stars[at],
|
|
519
|
+
length: token.markup.length,
|
|
520
|
+
tag: /** @type {"em"|"strong"} */ (token.tag),
|
|
521
|
+
open: token.nesting === 1,
|
|
522
|
+
});
|
|
523
|
+
at += token.markup.length;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
return delimiters;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* The parts of ranges of text that no delimiter covers.
|
|
531
|
+
*
|
|
532
|
+
* @param {Array<[number, number]>} parts - The ranges, as offsets.
|
|
533
|
+
* @param {readonly Delimiter[]} delimiters - Delimiters, by absolute offset.
|
|
534
|
+
* @returns {Array<[number, number]>} The ranges, split around each delimiter.
|
|
535
|
+
*/
|
|
536
|
+
function outsideDelimiters(parts, delimiters) {
|
|
537
|
+
if (!delimiters.length) return parts;
|
|
538
|
+
const out = [];
|
|
539
|
+
for (const [from, to] of parts) {
|
|
540
|
+
let at = from;
|
|
541
|
+
for (const delimiter of delimiters) {
|
|
542
|
+
if (delimiter.offset < at || delimiter.offset >= to) continue;
|
|
543
|
+
if (delimiter.offset > at) out.push([at, delimiter.offset]);
|
|
544
|
+
at = delimiter.offset + delimiter.length;
|
|
545
|
+
}
|
|
546
|
+
if (at < to) out.push([at, to]);
|
|
547
|
+
}
|
|
548
|
+
return out;
|
|
549
|
+
}
|
|
550
|
+
|
|
425
551
|
/** A line split into its indentation, its text, a trailing break and its trailing space. */
|
|
426
552
|
const LINE_PARTS = /^(\s*)(.*?)(\\?)(\s*)$/;
|
|
427
553
|
|
|
@@ -474,7 +600,7 @@ function unescapeRoman(core) {
|
|
|
474
600
|
* @param {import("./map-lexicon.mjs").Mapping|undefined} mapping - The run's
|
|
475
601
|
* map, face and lexicon.
|
|
476
602
|
* @param {boolean} lineStart - Whether `raw` starts its line.
|
|
477
|
-
* @returns {{text: string, pieces: Array<{offset: number, from: string, to: string, row: boolean, native?: boolean, uncapitalised?: string}>}}
|
|
603
|
+
* @returns {{text: string, pieces: Array<{offset: number, from: string, to: string, row: boolean, native?: boolean, uncapitalised?: string, unwritten?: object}>}}
|
|
478
604
|
* The set text, and each piece of it with the offset in `raw` it came from.
|
|
479
605
|
*/
|
|
480
606
|
function setText(raw, mapping, lineStart) {
|
|
@@ -491,6 +617,7 @@ function setText(raw, mapping, lineStart) {
|
|
|
491
617
|
const mapped = applyMap(mapping?.map, plain.text, {
|
|
492
618
|
font: mapping?.font,
|
|
493
619
|
native: mapping?.native,
|
|
620
|
+
writing: mapping?.writing,
|
|
494
621
|
});
|
|
495
622
|
for (const piece of mapped.pieces)
|
|
496
623
|
pieces.push({
|
|
@@ -502,6 +629,7 @@ function setText(raw, mapping, lineStart) {
|
|
|
502
629
|
...(piece.uncapitalised === undefined ?
|
|
503
630
|
{}
|
|
504
631
|
: { uncapitalised: piece.uncapitalised }),
|
|
632
|
+
...(piece.unwritten === undefined ? {} : { unwritten: piece.unwritten }),
|
|
505
633
|
});
|
|
506
634
|
out.push(
|
|
507
635
|
lead +
|
|
@@ -574,7 +702,40 @@ const BLOCK_WHAT = Object.freeze({
|
|
|
574
702
|
* @property {[number, number]|undefined} body - A div's or fence's body, as
|
|
575
703
|
* offsets: the first body line's start to the end of the last.
|
|
576
704
|
* @property {number|undefined} contentEnd - A span's offset of its closing `]`.
|
|
705
|
+
* @property {import("./script-emphasis.mjs").LanguageEmphasis|undefined} languageEmphasis -
|
|
706
|
+
* The convention its language's note states.
|
|
707
|
+
* @property {import("./script-emphasis.mjs").Emphasis|undefined} conventions -
|
|
708
|
+
* What its emphasis is set by: its language's convention, else its face's
|
|
709
|
+
* script's; `undefined` sets it plain.
|
|
710
|
+
* @property {Delimiter[]} emphasis - The emphasis it holds, by absolute offset.
|
|
711
|
+
*/
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* The finding for emphasis in a language that marks it with a word.
|
|
715
|
+
*
|
|
716
|
+
* @param {string} lang - The language, as `lang=` names it.
|
|
717
|
+
* @returns {string} The message.
|
|
718
|
+
*/
|
|
719
|
+
function wordsMessage(lang) {
|
|
720
|
+
return `\`${lang}\` marks emphasis with a word, not a mark; write the word`;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* What a run's emphasis is set by: the convention its language's note states,
|
|
725
|
+
* else the one its face's script note states, else none. A language that
|
|
726
|
+
* marks emphasis with a word sets none, and its emphasis is a finding.
|
|
727
|
+
*
|
|
728
|
+
* @param {{face: import("./script-fonts.mjs").Face|undefined, languageEmphasis: string|undefined}} detail -
|
|
729
|
+
* The run.
|
|
730
|
+
* @param {RunContext} ctx - The conventions.
|
|
731
|
+
* @returns {import("./script-emphasis.mjs").Emphasis|undefined} The conventions.
|
|
577
732
|
*/
|
|
733
|
+
function conventionsOf({ face, languageEmphasis }, ctx) {
|
|
734
|
+
if (!face || languageEmphasis === "words") return undefined;
|
|
735
|
+
if (languageEmphasis === "rubric" || languageEmphasis === "overline")
|
|
736
|
+
return { em: languageEmphasis, strong: languageEmphasis };
|
|
737
|
+
return ctx.emphases?.get(face.slug);
|
|
738
|
+
}
|
|
578
739
|
|
|
579
740
|
/**
|
|
580
741
|
* Read every marked construct in a source, and every fault in it.
|
|
@@ -790,25 +951,44 @@ function analyseRuns(source, ctx, syntax) {
|
|
|
790
951
|
);
|
|
791
952
|
|
|
792
953
|
/**
|
|
793
|
-
* Report the markup in inline text that must be plain
|
|
954
|
+
* Report the markup in inline text that must be plain, and collect the
|
|
955
|
+
* emphasis a run in a script face holds.
|
|
794
956
|
*
|
|
795
957
|
* @param {string} inline - The text.
|
|
796
958
|
* @param {number} offset - Its offset in the source.
|
|
797
959
|
* @param {string} marking - How its construct is marked.
|
|
960
|
+
* @param {RunDetail} detail - The run, whose `emphasis` collects each
|
|
961
|
+
* delimiter by absolute offset.
|
|
798
962
|
* @param {ReadonlyMap<number, number>} [holes] - Marked spans in it, by
|
|
799
963
|
* absolute offset.
|
|
800
964
|
*/
|
|
801
|
-
const plainInline = (inline, offset, marking, holes = new Map()) => {
|
|
965
|
+
const plainInline = (inline, offset, marking, detail, holes = new Map()) => {
|
|
802
966
|
const relative = new Map(
|
|
803
967
|
[...holes]
|
|
804
968
|
.filter(([start]) => start >= offset && start < offset + inline.length)
|
|
805
969
|
.map(([start, end]) => [start - offset, end - offset]),
|
|
806
970
|
);
|
|
807
|
-
|
|
971
|
+
const { lang } = detail.run;
|
|
972
|
+
const words = detail.languageEmphasis === "words";
|
|
973
|
+
const stars = words || detail.face !== undefined;
|
|
974
|
+
for (const fault of inlineMarkup(inline, footnotes, relative, stars))
|
|
808
975
|
report(
|
|
809
976
|
positionOf(starts, offset + fault.offset),
|
|
810
|
-
|
|
977
|
+
fault.what !== "emphasis" || !stars ?
|
|
978
|
+
`text marked \`${marking}\` is plain text and holds no ${fault.what}`
|
|
979
|
+
: words ? wordsMessage(/** @type {string} */ (lang))
|
|
980
|
+
: `text marked \`${marking}\` takes emphasis only as \`*…*\`, \`_…_\`, \`**…**\` or \`__…__\``,
|
|
811
981
|
);
|
|
982
|
+
if (!stars) return;
|
|
983
|
+
for (const delimiter of emphasisDelimiters(inline, relative)) {
|
|
984
|
+
if (words) {
|
|
985
|
+
if (delimiter.open)
|
|
986
|
+
report(
|
|
987
|
+
positionOf(starts, offset + delimiter.offset),
|
|
988
|
+
wordsMessage(/** @type {string} */ (lang)),
|
|
989
|
+
);
|
|
990
|
+
} else detail.emphasis.push({ ...delimiter, offset: offset + delimiter.offset });
|
|
991
|
+
}
|
|
812
992
|
};
|
|
813
993
|
|
|
814
994
|
/**
|
|
@@ -825,6 +1005,8 @@ function analyseRuns(source, ctx, syntax) {
|
|
|
825
1005
|
for (const [from, to] of parts) {
|
|
826
1006
|
const lineStart = from === 0 || text[from - 1] === "\n";
|
|
827
1007
|
for (const piece of setText(text.slice(from, to), mapping, lineStart).pieces) {
|
|
1008
|
+
// A word with no writing is its own finding, not a face's.
|
|
1009
|
+
if (piece.unwritten) continue;
|
|
828
1010
|
for (const char of piece.to) {
|
|
829
1011
|
const point = /** @type {number} */ (char.codePointAt(0));
|
|
830
1012
|
if (/\s/u.test(char) || face.draws.has(point)) continue;
|
|
@@ -884,6 +1066,35 @@ function analyseRuns(source, ctx, syntax) {
|
|
|
884
1066
|
}
|
|
885
1067
|
};
|
|
886
1068
|
|
|
1069
|
+
/**
|
|
1070
|
+
* Report each word a logographic map gives no writing, once per run, at
|
|
1071
|
+
* the word.
|
|
1072
|
+
*
|
|
1073
|
+
* @param {Array<[number, number]>} parts - The text to check, as offsets.
|
|
1074
|
+
* @param {RunDetail} detail - The run.
|
|
1075
|
+
*/
|
|
1076
|
+
const writings = (parts, detail) => {
|
|
1077
|
+
const { mapping } = detail;
|
|
1078
|
+
if (!mapping?.map.logographic) return;
|
|
1079
|
+
const said = new Set();
|
|
1080
|
+
for (const [from, to] of parts) {
|
|
1081
|
+
const lineStart = from === 0 || text[from - 1] === "\n";
|
|
1082
|
+
for (const piece of setText(text.slice(from, to), mapping, lineStart).pieces) {
|
|
1083
|
+
if (piece.unwritten === undefined || said.has(piece.from)) continue;
|
|
1084
|
+
said.add(piece.from);
|
|
1085
|
+
report(
|
|
1086
|
+
positionOf(starts, from + piece.offset),
|
|
1087
|
+
unwrittenMessage(
|
|
1088
|
+
piece.from,
|
|
1089
|
+
piece.unwritten,
|
|
1090
|
+
detail.run.lang,
|
|
1091
|
+
mapping.map.name,
|
|
1092
|
+
),
|
|
1093
|
+
);
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
};
|
|
1097
|
+
|
|
887
1098
|
/**
|
|
888
1099
|
* Report each word of a mapped run that is not a word of its language, once
|
|
889
1100
|
* per run, at its first character.
|
|
@@ -963,7 +1174,11 @@ function analyseRuns(source, ctx, syntax) {
|
|
|
963
1174
|
holes: new Map(),
|
|
964
1175
|
body: undefined,
|
|
965
1176
|
contentEnd: construct.kind === "span" ? construct.node.contentEnd : undefined,
|
|
1177
|
+
languageEmphasis: lang === undefined ? undefined : ctx.languageEmphases?.get(lang),
|
|
1178
|
+
conventions: undefined,
|
|
1179
|
+
emphasis: [],
|
|
966
1180
|
};
|
|
1181
|
+
detail.conventions = conventionsOf(detail, ctx);
|
|
967
1182
|
details.push(detail);
|
|
968
1183
|
if (!plain) continue;
|
|
969
1184
|
const marking = [
|
|
@@ -976,11 +1191,16 @@ function analyseRuns(source, ctx, syntax) {
|
|
|
976
1191
|
if (construct.kind === "span") {
|
|
977
1192
|
const span = construct.node;
|
|
978
1193
|
const inline = text.slice(span.start + 1, span.contentEnd);
|
|
979
|
-
plainInline(inline, span.start + 1, marking);
|
|
1194
|
+
plainInline(inline, span.start + 1, marking, detail);
|
|
980
1195
|
if (named && errors.length === before) {
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
1196
|
+
const parts = outsideDelimiters(
|
|
1197
|
+
[[span.start + 1, span.contentEnd]],
|
|
1198
|
+
detail.emphasis,
|
|
1199
|
+
);
|
|
1200
|
+
lexical(parts, detail);
|
|
1201
|
+
capitals(parts, detail);
|
|
1202
|
+
writings(parts, detail);
|
|
1203
|
+
covered(parts, detail);
|
|
984
1204
|
}
|
|
985
1205
|
continue;
|
|
986
1206
|
}
|
|
@@ -1014,6 +1234,7 @@ function analyseRuns(source, ctx, syntax) {
|
|
|
1014
1234
|
body.slice(top, past).join("\n"),
|
|
1015
1235
|
starts[first + top],
|
|
1016
1236
|
marking,
|
|
1237
|
+
detail,
|
|
1017
1238
|
detail.holes,
|
|
1018
1239
|
);
|
|
1019
1240
|
continue;
|
|
@@ -1029,12 +1250,14 @@ function analyseRuns(source, ctx, syntax) {
|
|
|
1029
1250
|
}
|
|
1030
1251
|
} else {
|
|
1031
1252
|
for (let line = first; line < last; line++)
|
|
1032
|
-
plainInline(lines[line], starts[line], marking, detail.holes);
|
|
1253
|
+
plainInline(lines[line], starts[line], marking, detail, detail.holes);
|
|
1033
1254
|
}
|
|
1034
1255
|
if (named && errors.length === before) {
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1256
|
+
const parts = outsideDelimiters(outsideHoles(from, to, detail.holes), detail.emphasis);
|
|
1257
|
+
lexical(parts, detail);
|
|
1258
|
+
capitals(parts, detail);
|
|
1259
|
+
writings(parts, detail);
|
|
1260
|
+
covered(parts, detail);
|
|
1038
1261
|
}
|
|
1039
1262
|
}
|
|
1040
1263
|
|
|
@@ -1066,7 +1289,25 @@ export function scanRuns(source, ctx = {}, syntax) {
|
|
|
1066
1289
|
const text = String(source ?? "");
|
|
1067
1290
|
const { markdown, origins } = expandVerseOrigins(text);
|
|
1068
1291
|
const authored = analyseRuns(text, ctx, syntax);
|
|
1069
|
-
|
|
1292
|
+
const metre = metreFindings(scanVerse(text).blocks, text.split("\n"), {
|
|
1293
|
+
metres: ctx.metres,
|
|
1294
|
+
metreBooks: ctx.metreBooks,
|
|
1295
|
+
lexicons: ctx.lexicons,
|
|
1296
|
+
columnsClass: COLUMNS_CLASS,
|
|
1297
|
+
columnKeys: COLUMN_KEYS,
|
|
1298
|
+
attributeColumns,
|
|
1299
|
+
});
|
|
1300
|
+
/** @param {object[]} found @param {object[]} extra */
|
|
1301
|
+
const merged = (found, extra) =>
|
|
1302
|
+
extra.length ?
|
|
1303
|
+
[...found, ...extra].sort((a, b) => a.line - b.line || a.column - b.column)
|
|
1304
|
+
: found;
|
|
1305
|
+
if (markdown === text)
|
|
1306
|
+
return {
|
|
1307
|
+
...authored,
|
|
1308
|
+
errors: merged(authored.errors, metre.errors),
|
|
1309
|
+
warnings: merged(authored.warnings, metre.warnings),
|
|
1310
|
+
};
|
|
1070
1311
|
// A verse in columns is checked as the spans each entry sets, which are
|
|
1071
1312
|
// written only in the expansion; every finding is placed back where the
|
|
1072
1313
|
// note wrote the text or the entry it comes from, once.
|
|
@@ -1086,8 +1327,8 @@ export function scanRuns(source, ctx = {}, syntax) {
|
|
|
1086
1327
|
};
|
|
1087
1328
|
return {
|
|
1088
1329
|
runs: authored.runs,
|
|
1089
|
-
errors: placed(checked.errors),
|
|
1090
|
-
warnings: placed(checked.warnings),
|
|
1330
|
+
errors: merged(placed(checked.errors), metre.errors),
|
|
1331
|
+
warnings: merged(placed(checked.warnings), metre.warnings),
|
|
1091
1332
|
};
|
|
1092
1333
|
}
|
|
1093
1334
|
|
|
@@ -1156,6 +1397,57 @@ function rewrittenAttributes(detail, search) {
|
|
|
1156
1397
|
return attributeText({ lang: run.tag, id: own.id, classes, values });
|
|
1157
1398
|
}
|
|
1158
1399
|
|
|
1400
|
+
/**
|
|
1401
|
+
* The tag a delimiter is written as: an `<em>` or a `<strong>` carrying its
|
|
1402
|
+
* convention's class. The book's parser reads only the classed form, so a
|
|
1403
|
+
* delimiter with no convention writes nothing there, and the text is set in
|
|
1404
|
+
* the face's Regular.
|
|
1405
|
+
*
|
|
1406
|
+
* @param {Delimiter} delimiter - The delimiter.
|
|
1407
|
+
* @param {import("./script-emphasis.mjs").Emphasis|undefined} conventions -
|
|
1408
|
+
* The run's conventions.
|
|
1409
|
+
* @param {boolean} book - Whether the target is the book.
|
|
1410
|
+
* @returns {string} The tag.
|
|
1411
|
+
*/
|
|
1412
|
+
function emphasisTag(delimiter, conventions, book) {
|
|
1413
|
+
const convention = conventions?.[delimiter.tag];
|
|
1414
|
+
if (!convention && book) return "";
|
|
1415
|
+
if (!delimiter.open) return `</${delimiter.tag}>`;
|
|
1416
|
+
return convention ?
|
|
1417
|
+
`<${delimiter.tag} class="${EMPHASIS_CLASSES[convention]}">`
|
|
1418
|
+
: `<${delimiter.tag}>`;
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
/**
|
|
1422
|
+
* Set a range of a run's text, each piece between its emphasis delimiters by
|
|
1423
|
+
* {@link setText}, and each delimiter as its tag.
|
|
1424
|
+
*
|
|
1425
|
+
* @param {string} text - The source.
|
|
1426
|
+
* @param {number} from - The range's first offset.
|
|
1427
|
+
* @param {number} to - The offset past it.
|
|
1428
|
+
* @param {RunDetail} detail - The run.
|
|
1429
|
+
* @param {boolean} lineStart - Whether `from` starts its line.
|
|
1430
|
+
* @param {boolean} book - Whether the target is the book.
|
|
1431
|
+
* @returns {string} The set text.
|
|
1432
|
+
*/
|
|
1433
|
+
function setRange(text, from, to, detail, lineStart, book) {
|
|
1434
|
+
let out = "";
|
|
1435
|
+
let at = from;
|
|
1436
|
+
for (const delimiter of detail.emphasis) {
|
|
1437
|
+
if (delimiter.offset < from || delimiter.offset >= to) continue;
|
|
1438
|
+
if (delimiter.offset > at)
|
|
1439
|
+
out += setText(
|
|
1440
|
+
text.slice(at, delimiter.offset),
|
|
1441
|
+
detail.mapping,
|
|
1442
|
+
at === from && lineStart,
|
|
1443
|
+
).text;
|
|
1444
|
+
out += emphasisTag(delimiter, detail.conventions, book);
|
|
1445
|
+
at = delimiter.offset + delimiter.length;
|
|
1446
|
+
}
|
|
1447
|
+
if (at < to) out += setText(text.slice(at, to), detail.mapping, at === from && lineStart).text;
|
|
1448
|
+
return out;
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1159
1451
|
/**
|
|
1160
1452
|
* Rewrite text marked with `lang=`, `font=` and `map=` into the plain span,
|
|
1161
1453
|
* div and verse grammar the rest of each chain renders, one line for one line.
|
|
@@ -1166,6 +1458,9 @@ function rewrittenAttributes(detail, search) {
|
|
|
1166
1458
|
* rewrite that changes the number of lines.
|
|
1167
1459
|
* - **The map, if any, is applied first.** Characters Markdown would read are
|
|
1168
1460
|
* then backslash-escaped, so they reach every later pass as characters.
|
|
1461
|
+
* - **Emphasis in a face** becomes an `<em>` or a `<strong>` carrying its
|
|
1462
|
+
* convention's class, or no class where none is stated. The book reads
|
|
1463
|
+
* only the classed form, so a classless one writes nothing there.
|
|
1169
1464
|
* - **The construct carries** the language's tag as `lang`, the face's class
|
|
1170
1465
|
* (with `script`, and `script-block` on a div) or the generic family's
|
|
1171
1466
|
* class. On the site, a face's construct also carries
|
|
@@ -1188,6 +1483,7 @@ function rewrittenAttributes(detail, search) {
|
|
|
1188
1483
|
*/
|
|
1189
1484
|
export function renderRuns(source, ctx = {}, syntax, options = {}) {
|
|
1190
1485
|
const search = options.target === "site";
|
|
1486
|
+
const book = options.target === "book";
|
|
1191
1487
|
const authored = String(source ?? "");
|
|
1192
1488
|
const text = expandVerse(authored);
|
|
1193
1489
|
if (text !== authored) syntax = undefined;
|
|
@@ -1219,11 +1515,14 @@ export function renderRuns(source, ctx = {}, syntax, options = {}) {
|
|
|
1219
1515
|
const contentEnd = /** @type {number} */ (detail.contentEnd);
|
|
1220
1516
|
let inner = text.slice(run.start + 1, contentEnd);
|
|
1221
1517
|
if (detail.plain)
|
|
1222
|
-
inner =
|
|
1223
|
-
|
|
1224
|
-
|
|
1518
|
+
inner = setRange(
|
|
1519
|
+
text,
|
|
1520
|
+
run.start + 1,
|
|
1521
|
+
contentEnd,
|
|
1522
|
+
detail,
|
|
1225
1523
|
run.start === 0 || text[run.start - 1] === "\n",
|
|
1226
|
-
|
|
1524
|
+
book,
|
|
1525
|
+
);
|
|
1227
1526
|
else if (attributes === undefined) continue;
|
|
1228
1527
|
const braces = attributes ?? text.slice(contentEnd + 2, run.end - 1);
|
|
1229
1528
|
edits.push({ from: run.start, to: run.end, text: `[${inner}]{${braces}}` });
|
|
@@ -1241,7 +1540,7 @@ export function renderRuns(source, ctx = {}, syntax, options = {}) {
|
|
|
1241
1540
|
if (detail.plain && detail.body)
|
|
1242
1541
|
for (const [from, to] of outsideHoles(detail.body[0], detail.body[1], detail.holes)) {
|
|
1243
1542
|
const lineStart = from === 0 || text[from - 1] === "\n";
|
|
1244
|
-
const set =
|
|
1543
|
+
const set = setRange(text, from, to, detail, lineStart, book);
|
|
1245
1544
|
if (set !== text.slice(from, to)) edits.push({ from, to, text: set });
|
|
1246
1545
|
}
|
|
1247
1546
|
}
|
package/engine/content-spans.mjs
CHANGED
|
@@ -18,6 +18,7 @@ import { scanVerse } from "./content-verse.mjs";
|
|
|
18
18
|
import footnotePlugin from "markdown-it-footnote";
|
|
19
19
|
import { parseExtensionAttributes, refusedAttributes } from "./extension-attributes.mjs";
|
|
20
20
|
import { inlineText } from "./name-markup.mjs";
|
|
21
|
+
import { cartoucheProblems, glyphProblems } from "./content-glyphs.mjs";
|
|
21
22
|
|
|
22
23
|
/** Add bracket spans after recognized Markdown links and footnotes.
|
|
23
24
|
* @param {import("markdown-it")} md - Markdown parser to extend.
|
|
@@ -91,6 +92,8 @@ export function spanMarkdownPlugin(md, { spoilers = false, source = false } = {}
|
|
|
91
92
|
}
|
|
92
93
|
}
|
|
93
94
|
const problems = [...parsed.problems, ...refusedAttributes(parsed.values)];
|
|
95
|
+
if (parsed.classes.includes("glyph")) problems.push(...glyphProblems(parsed));
|
|
96
|
+
else if (parsed.classes.includes("cartouche")) problems.push(...cartoucheProblems(parsed));
|
|
94
97
|
if (silent) {
|
|
95
98
|
state.pos = finish;
|
|
96
99
|
return true;
|
|
@@ -283,7 +286,7 @@ export function scanSpans(source) {
|
|
|
283
286
|
};
|
|
284
287
|
}
|
|
285
288
|
|
|
286
|
-
/** Emit span tags while leaving their inline content as Markdown
|
|
289
|
+
/** Emit span tags while leaving their inline content as Markdown.
|
|
287
290
|
* @param {string} source - Authored Markdown.
|
|
288
291
|
*/
|
|
289
292
|
export function renderSpans(source) {
|