@heroiclands/package-build 25.3.0 → 25.5.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 +39 -0
- package/README.md +1 -1
- package/assets/styles/content-base.css +32 -0
- package/docs/api.md +30 -6
- package/docs/authoring/links-and-markup.md +143 -53
- package/docs/commands.md +8 -2
- package/docs/content-format.md +14 -6
- package/docs/diagnostics.md +12 -11
- package/docs/encoding-core.md +18 -4
- package/docs/guides/writing-in-invented-scripts.md +83 -37
- package/docs/reference/format-details.md +10 -11
- package/docs/reference/note-types.md +15 -15
- package/docs/reference/theme-pages.md +26 -18
- package/engine/anchors.mjs +14 -0
- package/engine/code-fences.mjs +15 -6
- package/engine/content-blocks.mjs +4 -2
- package/engine/content-figures.mjs +19 -5
- package/engine/content-footnotes.mjs +69 -1
- package/engine/content-index.mjs +1 -1
- package/engine/content-render.mjs +59 -43
- package/engine/content-scripts.mjs +301 -58
- package/engine/content-scroll.mjs +8 -3
- package/engine/content-spans.mjs +107 -6
- package/engine/content-verse.mjs +338 -198
- package/engine/encoding-standard.mjs +1 -0
- package/engine/encoding-template.mjs +5 -2
- package/engine/encoding.mjs +19 -14
- package/engine/first-use.mjs +43 -12
- package/engine/heading-attributes.mjs +2 -1
- package/engine/journals.mjs +8 -1
- package/engine/markdown-blocks.mjs +3 -2
- package/engine/markdownlint-run.mjs +190 -5
- package/engine/markup-validation.mjs +17 -0
- package/engine/name-markup.mjs +1 -0
- package/engine/note-vocabulary.mjs +2 -2
- package/engine/pdf-render.mjs +34 -23
- package/engine/prose-lint.mjs +14 -2
- package/engine/roman-characters.mjs +32 -10
- package/engine/roman-run.mjs +3 -0
- package/engine/roman-verse.mjs +1 -0
- package/engine/run-rows.mjs +1 -1
- package/engine/sql-tables.mjs +1 -1
- package/engine/verse-format.mjs +89 -0
- package/engine/verse-metre.mjs +18 -9
- package/engine/verse-regions.mjs +190 -0
- package/engine/wikilink-pattern.mjs +31 -0
- package/engine/wikilink-syntax.mjs +2 -16
- package/package.json +1 -1
- package/site-theme/static/css/style.css +24 -0
- package/types/engine/code-fences.d.mts +1 -3
- package/types/engine/content-footnotes.d.mts +23 -0
- package/types/engine/content-scripts.d.mts +4 -3
- package/types/engine/content-scroll.d.mts +1 -1
- package/types/engine/content-spans.d.mts +3 -1
- package/types/engine/content-verse.d.mts +75 -75
- package/types/engine/name-markup.d.mts +5 -0
- package/types/engine/roman-run.d.mts +3 -1
- package/types/engine/verse-format.d.mts +10 -0
- package/types/engine/verse-metre.d.mts +4 -4
- package/types/engine/verse-regions.d.mts +82 -0
- package/types/engine/wikilink-pattern.d.mts +17 -0
- package/types/engine/wikilink-syntax.d.mts +1 -41
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of the Song of Heroic Lands (SoHL) system for Foundry VTT.
|
|
3
|
+
* Copyright (c) 2024-2026 Tom Rodriguez ("Toasty") — <toasty@heroiclands.org>
|
|
4
|
+
*
|
|
5
|
+
* This work is licensed under the GNU General Public License v3.0 (GPLv3).
|
|
6
|
+
* You may copy, modify, and distribute it under the terms of that license.
|
|
7
|
+
*
|
|
8
|
+
* For full terms, see the LICENSE.md file in the project root or visit:
|
|
9
|
+
* https://www.gnu.org/licenses/gpl-3.0.html
|
|
10
|
+
*
|
|
11
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** The dependency-free lexical wikilink pattern. @module */
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A wikilink, as authored.
|
|
18
|
+
*
|
|
19
|
+
* **Newlines are excluded deliberately.** A link is written on one line; an
|
|
20
|
+
* unclosed `[[` is a typo, and the alternative is letting it swallow arbitrary
|
|
21
|
+
* prose in search of a closer. Erring towards "not a link" leaves the author's
|
|
22
|
+
* text as written, which is the safe direction for a rewriter.
|
|
23
|
+
*
|
|
24
|
+
* **A leading `!` makes it an embed, not a link**, and the lookbehind is what
|
|
25
|
+
* keeps every reader from seeing one as the other. Stated here rather than in
|
|
26
|
+
* each reader for the reason the rest of this module exists: the checker, the
|
|
27
|
+
* resolver and the embed reader all match on this pattern, and a copy of the
|
|
28
|
+
* exclusion in each is a place for it to be forgotten. An embed's
|
|
29
|
+
* own grammar is {@link module:engine/content-embeds}.
|
|
30
|
+
*/
|
|
31
|
+
export const WIKILINK = /(?<!!)\[\[([^\]\n]+)\]\](?:\{(?:[^{}"\n]|"(?:\\.|[^"\\\n])*")*\})?/g;
|
|
@@ -42,22 +42,8 @@ import { parseAddress, isAddressTuple, splitAnchor } from "./address.mjs";
|
|
|
42
42
|
* @module
|
|
43
43
|
*/
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
*
|
|
48
|
-
* **Newlines are excluded deliberately.** A link is written on one line; an
|
|
49
|
-
* unclosed `[[` is a typo, and the alternative is letting it swallow arbitrary
|
|
50
|
-
* prose in search of a closer. Erring towards "not a link" leaves the author's
|
|
51
|
-
* text as written, which is the safe direction for a rewriter.
|
|
52
|
-
*
|
|
53
|
-
* **A leading `!` makes it an embed, not a link**, and the lookbehind is what
|
|
54
|
-
* keeps every reader from seeing one as the other. Stated here rather than in
|
|
55
|
-
* each reader for the reason the rest of this module exists: the checker, the
|
|
56
|
-
* resolver and the embed reader all match on this pattern, and a copy of the
|
|
57
|
-
* exclusion in each is a place for it to be forgotten. An embed's
|
|
58
|
-
* own grammar is {@link module:engine/content-embeds}.
|
|
59
|
-
*/
|
|
60
|
-
export const WIKILINK = /(?<!!)\[\[([^\]\n]+)\]\](?:\{(?:[^{}"\n]|"(?:\\.|[^"\\\n])*")*\})?/g;
|
|
45
|
+
import { WIKILINK } from "./wikilink-pattern.mjs";
|
|
46
|
+
export { WIKILINK } from "./wikilink-pattern.mjs";
|
|
61
47
|
|
|
62
48
|
/**
|
|
63
49
|
* The parts of a wikilink's interior.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heroiclands/package-build",
|
|
3
|
-
"version": "25.
|
|
3
|
+
"version": "25.5.0",
|
|
4
4
|
"description": "Shared toolchain for building and shipping a HeroicLands Foundry VTT package \u2014 content compilation, manifest, localization, staging, bundle, release and deployment.",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -1384,6 +1384,30 @@ a.tag:hover {
|
|
|
1384
1384
|
margin: 0;
|
|
1385
1385
|
}
|
|
1386
1386
|
|
|
1387
|
+
/* A quotation stays within its prose paragraph; canto titles retain heading type. */
|
|
1388
|
+
.single-content .verse.verse-inline {
|
|
1389
|
+
display: inline-block;
|
|
1390
|
+
vertical-align: baseline;
|
|
1391
|
+
max-inline-size: 100%;
|
|
1392
|
+
margin: 0;
|
|
1393
|
+
container-type: normal;
|
|
1394
|
+
}
|
|
1395
|
+
.single-content .verse.verse-inline .verse-stanza {
|
|
1396
|
+
display: block;
|
|
1397
|
+
margin: 0;
|
|
1398
|
+
}
|
|
1399
|
+
.single-content .verse.verse-inline .verse-stanza + .verse-stanza {
|
|
1400
|
+
margin-top: 0.5em;
|
|
1401
|
+
}
|
|
1402
|
+
.single-content .verse .verse-canto-heading {
|
|
1403
|
+
display: block;
|
|
1404
|
+
margin: 0.5em 0;
|
|
1405
|
+
font-family: var(--font-display);
|
|
1406
|
+
font-size: 1.1em;
|
|
1407
|
+
font-weight: bold;
|
|
1408
|
+
line-height: 1.3;
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1387
1411
|
/* Logical stanzas share labels across native and Roman presentations. */
|
|
1388
1412
|
.single-content .verse {
|
|
1389
1413
|
margin: 1.5rem 0;
|
|
@@ -17,13 +17,11 @@ export function parseHeaderArgs(info: string): {
|
|
|
17
17
|
* @param {object} [options]
|
|
18
18
|
* @param {boolean} [options.spans=true] - Include inline code spans. Set false
|
|
19
19
|
* to consider only block-level code.
|
|
20
|
-
* @param {boolean} [options.verseAsProse=true] - Treat verse bodies as inline prose.
|
|
21
20
|
* @returns {Array<{start: number, end: number}>} Non-overlapping regions, in
|
|
22
21
|
* source order. `end` is exclusive.
|
|
23
22
|
*/
|
|
24
|
-
export function codeRegions(markdown: string | null | undefined, { spans
|
|
23
|
+
export function codeRegions(markdown: string | null | undefined, { spans }?: {
|
|
25
24
|
spans?: boolean | undefined;
|
|
26
|
-
verseAsProse?: boolean | undefined;
|
|
27
25
|
}): Array<{
|
|
28
26
|
start: number;
|
|
29
27
|
end: number;
|
|
@@ -81,6 +81,29 @@ export function footnoteFindings(source: string): Array<{
|
|
|
81
81
|
column: number;
|
|
82
82
|
message: string;
|
|
83
83
|
}>;
|
|
84
|
+
/**
|
|
85
|
+
* Labels defined at the top level of this note.
|
|
86
|
+
* @param {string} source - Complete authored note Markdown.
|
|
87
|
+
* @returns {Set<string>} Same-note footnote labels.
|
|
88
|
+
*/
|
|
89
|
+
export function footnoteLabels(source: string): Set<string>;
|
|
90
|
+
/**
|
|
91
|
+
* Actual defined references, excluding literal code, escapes and metadata.
|
|
92
|
+
* @param {string} source - Full note or raw inline fragment.
|
|
93
|
+
* @param {object} [options] - Definition context and source grammar.
|
|
94
|
+
* @param {ReadonlySet<string>} [options.definitions] - Same-note labels; derived from source when omitted.
|
|
95
|
+
* @param {boolean} [options.inline] - Treat all source as inline verse/prose.
|
|
96
|
+
* @returns {Array<{from:number,to:number,key:string,text:string}>} Original UTF-16 source ranges.
|
|
97
|
+
*/
|
|
98
|
+
export function footnoteReferences(source: string, { definitions, inline }?: {
|
|
99
|
+
definitions?: ReadonlySet<string> | undefined;
|
|
100
|
+
inline?: boolean | undefined;
|
|
101
|
+
}): Array<{
|
|
102
|
+
from: number;
|
|
103
|
+
to: number;
|
|
104
|
+
key: string;
|
|
105
|
+
text: string;
|
|
106
|
+
}>;
|
|
84
107
|
/**
|
|
85
108
|
* A footnote reference's shape, wherever it is written — excluding a
|
|
86
109
|
* definition's own `[^id]:` opener, which this never matches.
|
|
@@ -18,7 +18,7 @@ export function runContext(config: {
|
|
|
18
18
|
};
|
|
19
19
|
} | undefined, records?: Iterable<Record<string, any>>): RunContext;
|
|
20
20
|
/**
|
|
21
|
-
* Find the spans, divs and verse
|
|
21
|
+
* Find the spans, divs and verse divs marked with `lang=`, `font=` or
|
|
22
22
|
* `enc=`, and every fault in their markup and their text.
|
|
23
23
|
*
|
|
24
24
|
* @param {string} source - Authored Markdown: a note body, or a whole file
|
|
@@ -80,7 +80,7 @@ export function scanRuns(source: string, ctx?: RunContext, syntax?: import("./co
|
|
|
80
80
|
* scanner projections.
|
|
81
81
|
* @param {{target?: "site"|"foundry"|"book"}} [options] - The target the
|
|
82
82
|
* Markdown is rendered for; only `site` writes the search marker.
|
|
83
|
-
* @returns {{markdown: string, errors: Array<{file?: string, line: number, column: number, message: string}>, scrolls: Map<string, import("./content-scroll.mjs").ScrollLayout>, verses: Map<string, {model: ReturnType<typeof import("./content-verse.mjs").parseVerse>, classes: string[], attributes: Record<string, string
|
|
83
|
+
* @returns {{markdown: string, errors: Array<{file?: string, line: number, column: number, message: string}>, scrolls: Map<string, import("./content-scroll.mjs").ScrollLayout>, verses: Map<string, {model: ReturnType<typeof import("./content-verse.mjs").parseVerse>, classes: string[], attributes: Record<string, string>, inline: boolean}>}}
|
|
84
84
|
* The rewritten Markdown, the findings of {@link scanRuns}, and each
|
|
85
85
|
* rewritten scroll fence's layout, by its rewritten body.
|
|
86
86
|
*/
|
|
@@ -99,6 +99,7 @@ export function renderRuns(source: string, ctx?: RunContext, syntax?: import("./
|
|
|
99
99
|
model: ReturnType<typeof import("./content-verse.mjs").parseVerse>;
|
|
100
100
|
classes: string[];
|
|
101
101
|
attributes: Record<string, string>;
|
|
102
|
+
inline: boolean;
|
|
102
103
|
}>;
|
|
103
104
|
};
|
|
104
105
|
/** The keys this module reads, which no construct passes through as written. */
|
|
@@ -350,7 +351,7 @@ export type RunContext = {
|
|
|
350
351
|
directions?: ReadonlyMap<string, "vertical-rl"> | undefined;
|
|
351
352
|
};
|
|
352
353
|
/**
|
|
353
|
-
* One span, div, verse
|
|
354
|
+
* One span, div, verse div or scroll fence carrying run attributes.
|
|
354
355
|
* Values combine explicit choices, class-only inheritance, encoding-document
|
|
355
356
|
* language/font defaults and the Roman default encoding.
|
|
356
357
|
*/
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Read every `scroll` fence in a source, and every fault in its opening.
|
|
3
3
|
*
|
|
4
4
|
* A fence inside a block quote or an HTML block is not read, and neither is a
|
|
5
|
-
* fence quoted inside another fence.
|
|
5
|
+
* fence quoted inside another fence. Verse lines are inline content.
|
|
6
6
|
*
|
|
7
7
|
* @param {string} source - Markdown.
|
|
8
8
|
* @returns {{blocks: Scroll[], errors: Array<{line: number, column: number, message: string}>}}
|
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
* @param {object} [options] - Surface presentation.
|
|
4
4
|
* @param {boolean} [options.source] - Emit opaque authored source tokens for buffer preview.
|
|
5
5
|
* @param {boolean} [options.spoilers] - Add focusable CSS spoiler wrappers (Foundry).
|
|
6
|
+
* @param {(token: any, state: any) => void} [options.verseInline] - Populate an opaque quoted-verse token during inline parsing.
|
|
6
7
|
*/
|
|
7
|
-
export function spanMarkdownPlugin(md: typeof import("markdown-it"), { spoilers, source }?: {
|
|
8
|
+
export function spanMarkdownPlugin(md: typeof import("markdown-it"), { spoilers, source, verseInline }?: {
|
|
8
9
|
source?: boolean | undefined;
|
|
9
10
|
spoilers?: boolean | undefined;
|
|
11
|
+
verseInline?: ((token: any, state: any) => void) | undefined;
|
|
10
12
|
}): void;
|
|
11
13
|
/** Read span anchors and located attribute diagnostics outside literal code.
|
|
12
14
|
* @param {string} source - Authored Markdown.
|
|
@@ -20,11 +20,11 @@ export function inlineVerse(text: any): string;
|
|
|
20
20
|
* @returns {boolean} Whether it starts right to left.
|
|
21
21
|
*/
|
|
22
22
|
export function rightToLeft(text: string): boolean;
|
|
23
|
-
/** Read verse
|
|
23
|
+
/** Read verse divs; start/end are inclusive zero-based source lines.
|
|
24
24
|
* @param {string} source - Authored Markdown.
|
|
25
25
|
* @param {object} [options] - Whole-page validation choices.
|
|
26
26
|
* @param {boolean} [options.checkWorks] - Whether to validate page-local versions.
|
|
27
|
-
* @returns {{blocks: VerseBlock[], errors: VerseDiagnostic[]}} Located
|
|
27
|
+
* @returns {{blocks: VerseBlock[], errors: VerseDiagnostic[]}} Located divs and findings.
|
|
28
28
|
*/
|
|
29
29
|
export function scanVerse(source: string, { checkWorks }?: {
|
|
30
30
|
checkWorks?: boolean | undefined;
|
|
@@ -32,68 +32,30 @@ export function scanVerse(source: string, { checkWorks }?: {
|
|
|
32
32
|
blocks: VerseBlock[];
|
|
33
33
|
errors: VerseDiagnostic[];
|
|
34
34
|
};
|
|
35
|
-
/**
|
|
36
|
-
* A logical line with its authored UTF-16 coordinates.
|
|
37
|
-
* @typedef {object} VerseLine
|
|
38
|
-
* @property {string} text - Inline Markdown in this presentation.
|
|
39
|
-
* @property {number} level - Leading-period indentation depth.
|
|
40
|
-
* @property {number} from - First authored offset, including indentation.
|
|
41
|
-
* @property {number} to - Offset at the line ending.
|
|
42
|
-
* @property {number[]} offsets - Source offset for each UTF-16 text unit.
|
|
43
|
-
* @property {string} [romanText] - Roman text paired with an encoded line.
|
|
44
|
-
*/
|
|
45
|
-
/**
|
|
46
|
-
* A stanza's logical lines and authored bounds.
|
|
47
|
-
* @typedef {object} VerseStanza
|
|
48
|
-
* @property {VerseLine[]} lines - Lines in reading order.
|
|
49
|
-
* @property {number} from - First authored offset.
|
|
50
|
-
* @property {number} to - Offset at the stanza ending.
|
|
51
|
-
*/
|
|
52
|
-
/**
|
|
53
|
-
* @typedef {VerseStanza & {canto: number, stanza: number, label: string, cantoBreak?: false}} VerseTextGroup
|
|
54
|
-
* @typedef {{cantoBreak: true, from: number, to: number, text?: string}} VerseCantoBreak
|
|
55
|
-
* @typedef {{kind: "line"|"stanza"|"canto", sourceOffset: number}} VerseBoundary
|
|
56
|
-
* @typedef {{offset: number, message: string}} VerseFinding
|
|
57
|
-
*/
|
|
58
|
-
/**
|
|
59
|
-
* Shared authored verse structure and source locations.
|
|
60
|
-
* @typedef {object} VerseModel
|
|
61
|
-
* @property {Array<{stanzas: VerseStanza[]}>} cantos - Cantos in reading order.
|
|
62
|
-
* @property {Array<VerseTextGroup|VerseCantoBreak>} groups - Stanzas and canto breaks.
|
|
63
|
-
* @property {VerseBoundary[]} boundaries - Explicit authored boundaries.
|
|
64
|
-
* @property {Array<[number, number]>} layout - Source ranges omitted from encoded letters.
|
|
65
|
-
* @property {VerseFinding[]} errors - Findings relative to the fence body.
|
|
66
|
-
*/
|
|
67
|
-
/**
|
|
68
|
-
* An authored verse fence with its parsed logical structure.
|
|
69
|
-
* @typedef {object} VerseBlock
|
|
70
|
-
* @property {number} start - Opening fence's zero-based source line.
|
|
71
|
-
* @property {number} end - Closing fence's zero-based source line.
|
|
72
|
-
* @property {string} fence - Authored fence delimiter.
|
|
73
|
-
* @property {"verse"} name - Fence language.
|
|
74
|
-
* @property {Record<string, string>} attributes - Authored attribute values.
|
|
75
|
-
* @property {string} id - Authored anchor, or the empty string.
|
|
76
|
-
* @property {string[]} classes - Authored presentation classes.
|
|
77
|
-
* @property {string} indent - Opening fence's leading spaces.
|
|
78
|
-
* @property {string} body - Authored fence body.
|
|
79
|
-
* @property {VerseModel} model - Logical structure relative to the body.
|
|
80
|
-
*/
|
|
81
|
-
/** @typedef {{line: number, column: number, message: string}} VerseDiagnostic */
|
|
82
|
-
/**
|
|
83
|
-
* An encoded verse's shared display model and native-cell presentation.
|
|
84
|
-
* @typedef {object} VersePresentation
|
|
85
|
-
* @property {VerseModel} model - Logical structure with paired line text.
|
|
86
|
-
* @property {string[]} classes - Native script and font classes.
|
|
87
|
-
* @property {Record<string, string>} attributes - Native language, direction and search attributes.
|
|
88
|
-
*/
|
|
89
35
|
/**
|
|
90
36
|
* Parse logical verse lines, stanzas and cantos without losing authored offsets.
|
|
91
37
|
* Offsets are UTF-16 positions relative to body. Delimiters and indentation are
|
|
92
38
|
* layout ranges; source newlines inside a logical line contribute one space.
|
|
93
|
-
* @param {string} body - Authored
|
|
39
|
+
* @param {string} body - Authored verse body.
|
|
40
|
+
* @param {{inline?: boolean, footnotes?: ReadonlySet<string>}} [options] - Inline quotations have no cantos; footnotes are same-note definition labels.
|
|
94
41
|
* @returns {VerseModel} The shared structure with offsets relative to the body.
|
|
95
42
|
*/
|
|
96
|
-
export function parseVerse(body: string
|
|
43
|
+
export function parseVerse(body: string, { inline, footnotes }?: {
|
|
44
|
+
inline?: boolean;
|
|
45
|
+
footnotes?: ReadonlySet<string>;
|
|
46
|
+
}): VerseModel;
|
|
47
|
+
/**
|
|
48
|
+
* Validate a quoted verse span using its original source coordinates.
|
|
49
|
+
* @param {{start: number, text: string, attrs: Array<[string,string]>, verseModel?: VerseModel}} span - Authored span.
|
|
50
|
+
* @param {string} source - Complete Markdown source.
|
|
51
|
+
* @returns {VerseDiagnostic[]} Located inline-only findings.
|
|
52
|
+
*/
|
|
53
|
+
export function inlineVerseFindings(span: {
|
|
54
|
+
start: number;
|
|
55
|
+
text: string;
|
|
56
|
+
attrs: Array<[string, string]>;
|
|
57
|
+
verseModel?: VerseModel;
|
|
58
|
+
}, source: string): VerseDiagnostic[];
|
|
97
59
|
/** Logical line text for consumers that do not need source locations. */
|
|
98
60
|
export function verseLines(body: any): string[];
|
|
99
61
|
/** Paragraph groups with separate canto gaps. */
|
|
@@ -103,27 +65,29 @@ export function verseStanzas(body: any): {
|
|
|
103
65
|
}[][];
|
|
104
66
|
/**
|
|
105
67
|
* Compare the stanza counts of each version of a page-local work.
|
|
106
|
-
* @param {VerseBlock[]} blocks -
|
|
107
|
-
* @returns {VerseDiagnostic[]} Findings at affected opening
|
|
68
|
+
* @param {VerseBlock[]} blocks - Divs belonging to one page.
|
|
69
|
+
* @returns {VerseDiagnostic[]} Findings at affected opening divs.
|
|
108
70
|
*/
|
|
109
71
|
export function workShapeFindings(blocks: VerseBlock[]): VerseDiagnostic[];
|
|
110
72
|
/**
|
|
111
73
|
* Render a shared verse structure while preserving surrounding inline Markdown.
|
|
112
|
-
* @param {string} body - Authored
|
|
74
|
+
* @param {string} body - Authored verse body.
|
|
113
75
|
* @param {object} [options] - Shared structure and display choices.
|
|
114
76
|
* @param {boolean} [options.labelled] - Whether each stanza carries its label.
|
|
115
77
|
* @param {VerseModel} [options.model] - Parsed structure; defaults to parsing the body.
|
|
116
78
|
* @param {VersePresentation} [options.presentation] - Native-cell font and language.
|
|
79
|
+
* @param {boolean} [options.inline] - Whether wrappers must remain inline-safe.
|
|
117
80
|
* @returns {string} Markdown with verse layout wrappers.
|
|
118
81
|
*/
|
|
119
|
-
export function verseMarkdown(body: string, { labelled, model, presentation }?: {
|
|
82
|
+
export function verseMarkdown(body: string, { labelled, model, presentation, inline }?: {
|
|
120
83
|
labelled?: boolean | undefined;
|
|
121
84
|
model?: VerseModel | undefined;
|
|
122
85
|
presentation?: VersePresentation | undefined;
|
|
86
|
+
inline?: boolean | undefined;
|
|
123
87
|
}): string;
|
|
124
88
|
/**
|
|
125
|
-
* Replace verse
|
|
126
|
-
* @param {string} source - Markdown containing verse
|
|
89
|
+
* Replace verse divs with wrappers whose bodies remain inline Markdown.
|
|
90
|
+
* @param {string} source - Markdown containing verse divs.
|
|
127
91
|
* @param {object} [options] - Encoded display models.
|
|
128
92
|
* @param {Map<string, VersePresentation>} [options.verses] - Presentations keyed by rewritten body.
|
|
129
93
|
*/
|
|
@@ -133,7 +97,13 @@ export function renderVerse(source: string, { verses }?: {
|
|
|
133
97
|
markdown: string;
|
|
134
98
|
errors: VerseDiagnostic[];
|
|
135
99
|
};
|
|
136
|
-
/**
|
|
100
|
+
/**
|
|
101
|
+
* Render quoted verse with inline-safe wrappers and shared logical structure.
|
|
102
|
+
* @param {string} source - Markdown after block verse rendering.
|
|
103
|
+
* @returns {string} Markdown retaining surrounding prose.
|
|
104
|
+
*/
|
|
105
|
+
export function renderVerseQuotes(source: string): string;
|
|
106
|
+
/** The authored attributes of a verse div. */
|
|
137
107
|
export const VERSE_KEYS: readonly string[];
|
|
138
108
|
/** A canto separator, including the escaped form produced by a native run. */
|
|
139
109
|
export const STANZA_BREAK: RegExp;
|
|
@@ -195,6 +165,8 @@ export type VerseCantoBreak = {
|
|
|
195
165
|
from: number;
|
|
196
166
|
to: number;
|
|
197
167
|
text?: string;
|
|
168
|
+
id?: string;
|
|
169
|
+
title?: string;
|
|
198
170
|
};
|
|
199
171
|
export type VerseBoundary = {
|
|
200
172
|
kind: "line" | "stanza" | "canto";
|
|
@@ -227,28 +199,56 @@ export type VerseModel = {
|
|
|
227
199
|
*/
|
|
228
200
|
layout: Array<[number, number]>;
|
|
229
201
|
/**
|
|
230
|
-
* - Findings relative to the
|
|
202
|
+
* - Findings relative to the verse body.
|
|
231
203
|
*/
|
|
232
204
|
errors: VerseFinding[];
|
|
205
|
+
/**
|
|
206
|
+
* - Defined references without substantive verse text.
|
|
207
|
+
*/
|
|
208
|
+
notes: VerseLine[];
|
|
233
209
|
};
|
|
234
210
|
/**
|
|
235
|
-
* An authored verse
|
|
211
|
+
* An authored verse div with its parsed logical structure.
|
|
236
212
|
*/
|
|
237
213
|
export type VerseBlock = {
|
|
238
214
|
/**
|
|
239
|
-
* - Opening
|
|
215
|
+
* - Opening div's zero-based source line.
|
|
240
216
|
*/
|
|
241
217
|
start: number;
|
|
242
218
|
/**
|
|
243
|
-
* -
|
|
219
|
+
* - Opening delimiter's one-based source column, when known.
|
|
220
|
+
*/
|
|
221
|
+
column?: number | undefined;
|
|
222
|
+
/**
|
|
223
|
+
* - Closing div's zero-based source line.
|
|
244
224
|
*/
|
|
245
225
|
end: number;
|
|
246
226
|
/**
|
|
247
|
-
* -
|
|
227
|
+
* - Complete div first offset.
|
|
228
|
+
*/
|
|
229
|
+
from: number;
|
|
230
|
+
/**
|
|
231
|
+
* - Offset past complete div.
|
|
232
|
+
*/
|
|
233
|
+
to: number;
|
|
234
|
+
/**
|
|
235
|
+
* - First body offset.
|
|
236
|
+
*/
|
|
237
|
+
bodyStart: number;
|
|
238
|
+
/**
|
|
239
|
+
* - Offset past body text.
|
|
240
|
+
*/
|
|
241
|
+
bodyEnd: number;
|
|
242
|
+
/**
|
|
243
|
+
* - Whether the closing delimiter exists.
|
|
244
|
+
*/
|
|
245
|
+
closed: boolean;
|
|
246
|
+
/**
|
|
247
|
+
* - Attribute syntax findings.
|
|
248
248
|
*/
|
|
249
|
-
|
|
249
|
+
problems: string[];
|
|
250
250
|
/**
|
|
251
|
-
* -
|
|
251
|
+
* - Region name.
|
|
252
252
|
*/
|
|
253
253
|
name: "verse";
|
|
254
254
|
/**
|
|
@@ -264,11 +264,11 @@ export type VerseBlock = {
|
|
|
264
264
|
*/
|
|
265
265
|
classes: string[];
|
|
266
266
|
/**
|
|
267
|
-
* - Opening
|
|
267
|
+
* - Opening div's leading spaces.
|
|
268
268
|
*/
|
|
269
269
|
indent: string;
|
|
270
270
|
/**
|
|
271
|
-
* - Authored
|
|
271
|
+
* - Authored verse body.
|
|
272
272
|
*/
|
|
273
273
|
body: string;
|
|
274
274
|
/**
|
|
@@ -278,7 +278,7 @@ export type VerseBlock = {
|
|
|
278
278
|
};
|
|
279
279
|
export type VerseDiagnostic = {
|
|
280
280
|
line: number;
|
|
281
|
-
column
|
|
281
|
+
column?: number;
|
|
282
282
|
message: string;
|
|
283
283
|
};
|
|
284
284
|
/**
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
* @property {string} visible - Visible text from the actual parsed children.
|
|
27
27
|
* @property {ReadonlyArray<readonly [string, string]>} attrs - Retained permitted HTML attributes.
|
|
28
28
|
* @property {Record<string, SpanAttributePosition>} attributePositions - Authored key/value sites by retained attribute name.
|
|
29
|
+
* @property {ReturnType<typeof import("./content-verse.mjs").parseVerse>} [verseModel] - Logical model of an inline verse quotation.
|
|
29
30
|
*/
|
|
30
31
|
/**
|
|
31
32
|
* One located span-grammar or refused-attribute finding.
|
|
@@ -139,6 +140,10 @@ export type ContentSpan = {
|
|
|
139
140
|
* - Authored key/value sites by retained attribute name.
|
|
140
141
|
*/
|
|
141
142
|
attributePositions: Record<string, SpanAttributePosition>;
|
|
143
|
+
/**
|
|
144
|
+
* - Logical model of an inline verse quotation.
|
|
145
|
+
*/
|
|
146
|
+
verseModel?: import("./content-verse.mjs").VerseModel | undefined;
|
|
142
147
|
};
|
|
143
148
|
/**
|
|
144
149
|
* One located span-grammar or refused-attribute finding.
|
|
@@ -36,9 +36,10 @@
|
|
|
36
36
|
* @param {(span: RomanSpan) => string} [options.opaqueSpan] - Placeholder for an independent child. The default empty name keeps sentence state without introducing that child's letters.
|
|
37
37
|
* @param {ReadonlyArray<readonly [number, number]>} [options.exclude] - Absolute source ranges that carry layout or presentation syntax.
|
|
38
38
|
* @param {boolean} [options.markNames] - Whether name children contribute braces.
|
|
39
|
+
* @param {boolean} [options.preserveSlashEscape] - Keep a literal verse slash distinct from a line control.
|
|
39
40
|
* @returns {{text: string, offsets: number[], spans: ProjectedSpan[]}} Logical text and source locations, with child ranges for presentation.
|
|
40
41
|
*/
|
|
41
|
-
export function plainRomanRun(source: string, { from, to, spans, includeSpan, opaqueSpan, exclude, markNames, }?: {
|
|
42
|
+
export function plainRomanRun(source: string, { from, to, spans, includeSpan, opaqueSpan, exclude, markNames, preserveSlashEscape, }?: {
|
|
42
43
|
from?: number | undefined;
|
|
43
44
|
to?: number | undefined;
|
|
44
45
|
spans?: readonly RomanSpan[] | undefined;
|
|
@@ -46,6 +47,7 @@ export function plainRomanRun(source: string, { from, to, spans, includeSpan, op
|
|
|
46
47
|
opaqueSpan?: ((span: RomanSpan) => string) | undefined;
|
|
47
48
|
exclude?: readonly (readonly [number, number])[] | undefined;
|
|
48
49
|
markNames?: boolean | undefined;
|
|
50
|
+
preserveSlashEscape?: boolean | undefined;
|
|
49
51
|
}): {
|
|
50
52
|
text: string;
|
|
51
53
|
offsets: number[];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replace verse markup with opaque placeholders, retaining container prefixes
|
|
3
|
+
* and inline display widths. Restoration follows the formatter's line endings.
|
|
4
|
+
* @param {string} source - Complete Markdown source.
|
|
5
|
+
* @returns {{markdown: string, restore: (formatted: string) => string}} Protected source and restoration.
|
|
6
|
+
*/
|
|
7
|
+
export function protectVerseSource(source: string): {
|
|
8
|
+
markdown: string;
|
|
9
|
+
restore: (formatted: string) => string;
|
|
10
|
+
};
|
|
@@ -60,7 +60,7 @@ export function lineBeats(text: string, book: MetreBook): {
|
|
|
60
60
|
* The warning for a line that cannot have its metre's beats.
|
|
61
61
|
*
|
|
62
62
|
* @param {{min: number, max: number}} beats - The line's beats.
|
|
63
|
-
* @param {import("./metre-declaration.mjs").Metre} metre - The
|
|
63
|
+
* @param {import("./metre-declaration.mjs").Metre} metre - The div's metre.
|
|
64
64
|
* @returns {string} The message.
|
|
65
65
|
*/
|
|
66
66
|
export function metreMessage({ min, max }: {
|
|
@@ -68,13 +68,13 @@ export function metreMessage({ min, max }: {
|
|
|
68
68
|
max: number;
|
|
69
69
|
}, metre: import("./metre-declaration.mjs").Metre): string;
|
|
70
70
|
/**
|
|
71
|
-
* Check the verse
|
|
71
|
+
* Check the verse divs that state `scan=`.
|
|
72
72
|
*
|
|
73
73
|
* @param {ReadonlyArray<{start: number, end: number, indent: string, attributes: Record<string, string>, classes: string[]}>} blocks -
|
|
74
|
-
* The
|
|
74
|
+
* The divs, from {@link module:engine/content-verse.scanVerse}.
|
|
75
75
|
* @param {string[]} lines - The source's lines.
|
|
76
76
|
* @param {{languages?: Readonly<Record<string, {tag: string}>>, metres?: ReadonlyMap<string, import("./metre-declaration.mjs").Metre>, metreBooks?: ReadonlyMap<string, MetreBook>, lexicons?: ReadonlyMap<string, unknown>, attributeColumns: (line: string, key: string) => {key: number, value: number}}} ctx -
|
|
77
|
-
* The metres and books, and how the
|
|
77
|
+
* The metres and books, and how the div says its columns and where its
|
|
78
78
|
* keys sit.
|
|
79
79
|
* @returns {{errors: object[], warnings: object[]}} The findings at 1-based
|
|
80
80
|
* positions.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install the opaque verse-div block rule. Tokens carry raw source regions.
|
|
3
|
+
* @param {import("markdown-it")} md - Markdown parser.
|
|
4
|
+
*/
|
|
5
|
+
export function verseDivPlugin(md: typeof import("markdown-it")): void;
|
|
6
|
+
/**
|
|
7
|
+
* Locate complete raw verse divs while leaving literal examples untouched.
|
|
8
|
+
* @param {string} source - Authored Markdown.
|
|
9
|
+
* @returns {VerseRegion[]} Regions in source order, including unclosed divs.
|
|
10
|
+
*/
|
|
11
|
+
export function verseRegions(source: string): VerseRegion[];
|
|
12
|
+
/**
|
|
13
|
+
* Source ranges belonging to inline syntax rather than visible verse text.
|
|
14
|
+
* @param {string} source - Inline Markdown.
|
|
15
|
+
* @returns {Array<[number,number]>} Protected UTF-16 ranges.
|
|
16
|
+
*/
|
|
17
|
+
export function inlineSyntaxRanges(source: string): Array<[number, number]>;
|
|
18
|
+
/**
|
|
19
|
+
* A raw verse div, opaque to ordinary Markdown block syntax.
|
|
20
|
+
*/
|
|
21
|
+
export type VerseRegion = {
|
|
22
|
+
/**
|
|
23
|
+
* - Opening line, zero based.
|
|
24
|
+
*/
|
|
25
|
+
start: number;
|
|
26
|
+
/**
|
|
27
|
+
* - Closing line, zero based and inclusive.
|
|
28
|
+
*/
|
|
29
|
+
end: number;
|
|
30
|
+
/**
|
|
31
|
+
* - First offset of the complete region.
|
|
32
|
+
*/
|
|
33
|
+
from: number;
|
|
34
|
+
/**
|
|
35
|
+
* - Offset past the complete region.
|
|
36
|
+
*/
|
|
37
|
+
to: number;
|
|
38
|
+
/**
|
|
39
|
+
* - Original-source content offsets for each region line, after container prefixes.
|
|
40
|
+
*/
|
|
41
|
+
contentStarts: number[];
|
|
42
|
+
/**
|
|
43
|
+
* - First body offset.
|
|
44
|
+
*/
|
|
45
|
+
bodyStart: number;
|
|
46
|
+
/**
|
|
47
|
+
* - Offset past body text, excluding its final newline.
|
|
48
|
+
*/
|
|
49
|
+
bodyEnd: number;
|
|
50
|
+
/**
|
|
51
|
+
* - Authored body bytes.
|
|
52
|
+
*/
|
|
53
|
+
body: string;
|
|
54
|
+
/**
|
|
55
|
+
* - Authored values.
|
|
56
|
+
*/
|
|
57
|
+
attributes: Record<string, string>;
|
|
58
|
+
/**
|
|
59
|
+
* - Anchor or empty string.
|
|
60
|
+
*/
|
|
61
|
+
id: string;
|
|
62
|
+
/**
|
|
63
|
+
* - Authored classes including verse.
|
|
64
|
+
*/
|
|
65
|
+
classes: string[];
|
|
66
|
+
/**
|
|
67
|
+
* - Opening indentation.
|
|
68
|
+
*/
|
|
69
|
+
indent: string;
|
|
70
|
+
/**
|
|
71
|
+
* - Region kind.
|
|
72
|
+
*/
|
|
73
|
+
name: "verse";
|
|
74
|
+
/**
|
|
75
|
+
* - Whether a closing delimiter is present.
|
|
76
|
+
*/
|
|
77
|
+
closed: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* - Attribute syntax findings.
|
|
80
|
+
*/
|
|
81
|
+
problems: string[];
|
|
82
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** The dependency-free lexical wikilink pattern. @module */
|
|
2
|
+
/**
|
|
3
|
+
* A wikilink, as authored.
|
|
4
|
+
*
|
|
5
|
+
* **Newlines are excluded deliberately.** A link is written on one line; an
|
|
6
|
+
* unclosed `[[` is a typo, and the alternative is letting it swallow arbitrary
|
|
7
|
+
* prose in search of a closer. Erring towards "not a link" leaves the author's
|
|
8
|
+
* text as written, which is the safe direction for a rewriter.
|
|
9
|
+
*
|
|
10
|
+
* **A leading `!` makes it an embed, not a link**, and the lookbehind is what
|
|
11
|
+
* keeps every reader from seeing one as the other. Stated here rather than in
|
|
12
|
+
* each reader for the reason the rest of this module exists: the checker, the
|
|
13
|
+
* resolver and the embed reader all match on this pattern, and a copy of the
|
|
14
|
+
* exclusion in each is a place for it to be forgotten. An embed's
|
|
15
|
+
* own grammar is {@link module:engine/content-embeds}.
|
|
16
|
+
*/
|
|
17
|
+
export const WIKILINK: RegExp;
|