@heroiclands/package-build 25.2.0 → 25.3.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.
@@ -1380,118 +1380,66 @@ a.tag:hover {
1380
1380
  background: var(--bg-card);
1381
1381
  }
1382
1382
 
1383
- /* Verse groups are paragraphs; explicit stanza separators add their own gap. */
1384
- .single-content .verse {
1385
- margin: 1.5rem 0;
1386
- line-height: 1.7;
1387
- }
1388
-
1389
- .single-content .verse p {
1390
- margin: 0;
1391
- }
1392
-
1393
- .single-content .verse p + p {
1394
- margin-top: 1em;
1395
- }
1396
-
1397
- .single-content .verse .verse-line {
1398
- display: inline-block;
1399
- margin-inline-start: var(--verse-indent, 0em);
1400
- white-space: pre-wrap;
1401
- }
1402
-
1403
- .single-content .verse :is(.verse-line, .verse-part).i1 { --verse-indent: 1.25em; }
1404
- .single-content .verse :is(.verse-line, .verse-part).i2 { --verse-indent: 2.5em; }
1405
- .single-content .verse :is(.verse-line, .verse-part).i3 { --verse-indent: 3.75em; }
1406
- .single-content .verse :is(.verse-line, .verse-part).i4 { --verse-indent: 5em; }
1407
- .single-content .verse :is(.verse-line, .verse-part).i5 { --verse-indent: 6.25em; }
1408
- .single-content .verse :is(.verse-line, .verse-part).i6 { --verse-indent: 7.5em; }
1409
- .single-content .verse :is(.verse-line, .verse-part).i7 { --verse-indent: 8.75em; }
1410
- .single-content .verse :is(.verse-line, .verse-part).i8 { --verse-indent: 10em; }
1411
-
1412
- .single-content .verse .verse-stanza-break { display: block; height: 0.5em; }
1413
-
1414
- /* Numbered verse carries its numbers in a margin. A verse in columns is one
1415
- grid per verse line: its number, then each column's parts stacked, the
1416
- first in the normal tone and the rest muted, every part in the body serif
1417
- unless its face sets it. A narrow verse stacks the columns of each line. */
1418
- .single-content .verse.verse-numbered {
1419
- position: relative;
1420
- padding-inline-start: 3em;
1421
- }
1422
-
1423
- .single-content .verse .verse-number {
1424
- color: var(--text-muted);
1425
- font-size: 0.8em;
1426
- }
1427
-
1428
- .single-content .verse-numbered .verse-line .verse-number {
1429
- position: absolute;
1430
- inset-inline-start: 0;
1431
- width: 2.2em;
1432
- text-align: end;
1433
- }
1434
-
1435
- .single-content .verse.verse-columns {
1436
- container-type: inline-size;
1437
- }
1438
-
1439
- .single-content .verse-numbered.verse-columns {
1440
- padding-inline-start: 0;
1383
+ .single-content .verse .verse-stanza p {
1384
+ margin: 0;
1441
1385
  }
1442
1386
 
1443
- .single-content .verse-columns .verse-row {
1444
- display: grid;
1445
- grid-auto-flow: column;
1446
- grid-auto-columns: 1fr;
1447
- direction: ltr;
1448
- grid-template-columns: repeat(var(--verse-columns, 2), minmax(0, 1fr));
1449
- column-gap: 1.5rem;
1450
- margin-bottom: 0.5em;
1387
+ /* Logical stanzas share labels across native and Roman presentations. */
1388
+ .single-content .verse {
1389
+ margin: 1.5rem 0;
1390
+ line-height: 1.7;
1391
+ container-type: inline-size;
1451
1392
  }
1452
-
1453
- .single-content .verse-numbered.verse-columns .verse-row {
1454
- grid-template-columns: 2.5em repeat(var(--verse-columns, 2), minmax(0, 1fr));
1393
+ .single-content .verse .verse-stanza {
1394
+ position: relative;
1395
+ margin: 0 0 1em;
1455
1396
  }
1456
-
1457
- .single-content .verse-columns .verse-number {
1458
- text-align: end;
1459
- padding-top: 0.2em;
1397
+ .single-content .verse-labelled .verse-stanza {
1398
+ padding-inline-start: 3em;
1460
1399
  }
1461
-
1462
- .single-content .verse-columns :is(.verse-column, .verse-part) {
1463
- display: block;
1464
- white-space: pre-wrap;
1400
+ .single-content .verse .verse-label {
1401
+ position: absolute;
1402
+ inset-inline-start: 0;
1403
+ width: 2.5em;
1404
+ text-align: end;
1405
+ font-size: 0.75em;
1406
+ color: color-mix(in srgb, currentColor 60%, transparent);
1465
1407
  }
1466
-
1467
- .single-content .verse-columns .verse-part {
1468
- font-family: var(--font-body);
1469
- padding-inline-start: var(--verse-indent, 0em);
1408
+ .single-content .verse .verse-line {
1409
+ display: inline-block;
1410
+ margin-inline-start: var(--verse-indent, 0em);
1411
+ white-space: pre-wrap;
1412
+ }
1413
+ .single-content .verse .verse-line.i1 { --verse-indent: 1.25em; }
1414
+ .single-content .verse .verse-line.i2 { --verse-indent: 2.5em; }
1415
+ .single-content .verse .verse-line.i3 { --verse-indent: 3.75em; }
1416
+ .single-content .verse .verse-line.i4 { --verse-indent: 5em; }
1417
+ .single-content .verse .verse-line.i5 { --verse-indent: 6.25em; }
1418
+ .single-content .verse .verse-line.i6 { --verse-indent: 7.5em; }
1419
+ .single-content .verse .verse-line.i7 { --verse-indent: 8.75em; }
1420
+ .single-content .verse .verse-line.i8 { --verse-indent: 10em; }
1421
+ .single-content .verse .verse-canto-break {
1422
+ display: block;
1423
+ height: 0.5em;
1470
1424
  }
1471
-
1472
- .single-content .verse-columns .verse-secondary {
1473
- color: var(--text-muted);
1425
+ .single-content .verse-paired .verse-row {
1426
+ display: grid;
1427
+ direction: ltr;
1428
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1429
+ column-gap: 1.5rem;
1430
+ align-items: start;
1474
1431
  }
1475
-
1476
- .single-content .verse-columns .verse-secondary * {
1477
- color: inherit;
1432
+ .single-content .verse-paired :is(.verse-native, .verse-roman) {
1433
+ min-width: 0;
1434
+ white-space: pre-wrap;
1478
1435
  }
1479
-
1480
- .single-content .verse-columns .verse-part[dir="rtl"] {
1481
- text-align: start;
1436
+ .single-content .verse-paired .verse-roman {
1437
+ font-family: serif;
1482
1438
  }
1483
-
1484
1439
  @container (max-width: 32rem) {
1485
- .single-content .verse-columns .verse-row {
1486
- grid-auto-flow: row;
1487
- grid-template-columns: 1fr;
1488
- }
1489
- .single-content .verse-numbered.verse-columns .verse-row {
1490
- grid-template-columns: 2.5em 1fr;
1491
- }
1492
- .single-content .verse-numbered.verse-columns .verse-column {
1493
- grid-column: 2;
1494
- }
1440
+ .single-content .verse-paired .verse-row {
1441
+ grid-template-columns: 1fr;
1442
+ }
1495
1443
  }
1496
1444
 
1497
1445
  /* Ornamental horizontal rule: thin line flanking a centered gold
@@ -43,10 +43,12 @@ export function scanBlocks(source: string): {
43
43
  * @param {object} [options]
44
44
  * @param {(markdown: string) => string} [options.renderInline] - How a title
45
45
  * is rendered; omitted, a title is plain inline Markdown.
46
+ * @param {Map<string, object>} [options.verses] - Shared encoded verse presentations.
46
47
  * @returns {{markdown: string, errors: Array<{line: number, column: number, message: string}>}}
47
48
  */
48
- export function renderBlocks(source: string, target: "foundry" | "web" | "preview", { renderInline }?: {
49
+ export function renderBlocks(source: string, target: "foundry" | "web" | "preview", { renderInline, verses }?: {
49
50
  renderInline?: ((markdown: string) => string) | undefined;
51
+ verses?: Map<string, object> | undefined;
50
52
  }): {
51
53
  markdown: string;
52
54
  errors: Array<{
@@ -50,11 +50,10 @@ export function scanRuns(source: string, ctx?: RunContext, syntax?: import("./co
50
50
  };
51
51
  /**
52
52
  * Rewrite text marked with `lang=`, `font=` and `enc=` into the plain span,
53
- * div and verse grammar the rest of each chain renders, one line for one line.
53
+ * div and verse grammar the rest of each chain renders.
54
54
  *
55
- * - **Numbered verse prefixes and indentation markers** stay outside run text.
56
- * Each authored run chooses its own language and presentation; physical
57
- * lines and source positions remain unchanged.
55
+ * - **Verse layout markers** stay outside encoded text. A shared logical
56
+ * structure retains authored source positions and both presentations.
58
57
  * - **The enc, if any, is applied first.** Characters Markdown would read are
59
58
  * then backslash-escaped, so they reach every later pass as characters.
60
59
  * - **Emphasis in a face** becomes an `<em>` or a `<strong>` carrying its
@@ -81,7 +80,7 @@ export function scanRuns(source: string, ctx?: RunContext, syntax?: import("./co
81
80
  * scanner projections.
82
81
  * @param {{target?: "site"|"foundry"|"book"}} [options] - The target the
83
82
  * Markdown is rendered for; only `site` writes the search marker.
84
- * @returns {{markdown: string, errors: Array<{file?: string, line: number, column: number, message: string}>, scrolls: Map<string, import("./content-scroll.mjs").ScrollLayout>}}
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>}>}}
85
84
  * The rewritten Markdown, the findings of {@link scanRuns}, and each
86
85
  * rewritten scroll fence's layout, by its rewritten body.
87
86
  */
@@ -96,6 +95,11 @@ export function renderRuns(source: string, ctx?: RunContext, syntax?: import("./
96
95
  message: string;
97
96
  }>;
98
97
  scrolls: Map<string, import("./content-scroll.mjs").ScrollLayout>;
98
+ verses: Map<string, {
99
+ model: ReturnType<typeof import("./content-verse.mjs").parseVerse>;
100
+ classes: string[];
101
+ attributes: Record<string, string>;
102
+ }>;
99
103
  };
100
104
  /** The keys this module reads, which no construct passes through as written. */
101
105
  export const RUN_KEYS: readonly string[];
@@ -1,5 +1,3 @@
1
- /** The authored verse lines, excluding blank lines and stanza separators. */
2
- export function verseLines(body: any): string[];
3
1
  /**
4
2
  * Read one verse line. Marker offsets are zero-based in the authored line;
5
3
  * textColumn is the one-based column of its first non-space character.
@@ -12,103 +10,291 @@ export function verseLine(line: any): {
12
10
  markerLength: number;
13
11
  textColumn: number;
14
12
  };
13
+ /** A verse is inline Markdown even when it starts with block syntax. */
14
+ export function inlineVerse(text: any): string;
15
+ /**
16
+ * Whether text runs right to left: its first letter is in a right-to-left
17
+ * block. A face's right-to-left code points sit in U+1E800–U+1EFFF.
18
+ *
19
+ * @param {string} text - Text, after any map.
20
+ * @returns {boolean} Whether it starts right to left.
21
+ */
22
+ export function rightToLeft(text: string): boolean;
23
+ /** Read verse code fences; start/end are inclusive zero-based source lines.
24
+ * @param {string} source - Authored Markdown.
25
+ * @param {object} [options] - Whole-page validation choices.
26
+ * @param {boolean} [options.checkWorks] - Whether to validate page-local versions.
27
+ * @returns {{blocks: VerseBlock[], errors: VerseDiagnostic[]}} Located fences and findings.
28
+ */
29
+ export function scanVerse(source: string, { checkWorks }?: {
30
+ checkWorks?: boolean | undefined;
31
+ }): {
32
+ blocks: VerseBlock[];
33
+ errors: VerseDiagnostic[];
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
+ */
15
45
  /**
16
- * Blank lines divide verse groups. A standalone `***` is its own group and
17
- * separates stanzas. Each text line carries its period-based indentation.
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.
18
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
+ /**
90
+ * Parse logical verse lines, stanzas and cantos without losing authored offsets.
91
+ * Offsets are UTF-16 positions relative to body. Delimiters and indentation are
92
+ * layout ranges; source newlines inside a logical line contribute one space.
93
+ * @param {string} body - Authored fence body.
94
+ * @returns {VerseModel} The shared structure with offsets relative to the body.
95
+ */
96
+ export function parseVerse(body: string): VerseModel;
97
+ /** Logical line text for consumers that do not need source locations. */
98
+ export function verseLines(body: any): string[];
99
+ /** Paragraph groups with separate canto gaps. */
19
100
  export function verseStanzas(body: any): {
20
101
  text: string;
21
102
  level: number;
22
103
  }[][];
23
- /** A verse is inline Markdown even when it starts with block syntax. */
24
- export function inlineVerse(text: any): string;
25
104
  /**
26
- * Keep blank-line verse groups as paragraphs and each text line a hard break.
27
- * A standalone `***` emits a separate stanza gap.
28
- * The surrounding renderer still sees the entire note, so links and footnote
29
- * definitions remain in scope. The span carries the indent for each line.
105
+ * Compare the stanza counts of each version of a page-local work.
106
+ * @param {VerseBlock[]} blocks - Fences belonging to one page.
107
+ * @returns {VerseDiagnostic[]} Findings at affected opening fences.
30
108
  */
31
- export function verseMarkdown(body: any, step?: number): string;
109
+ export function workShapeFindings(blocks: VerseBlock[]): VerseDiagnostic[];
32
110
  /**
33
- * The number a line shows: `undefined` when the verse is not numbered, `""`
34
- * when this line is not one of every `step`th.
35
- *
36
- * @param {number} line - The line's number, 1-based across the verse.
37
- * @param {number} step - The verse's `numstep`.
38
- * @returns {string|undefined} The number shown.
111
+ * Render a shared verse structure while preserving surrounding inline Markdown.
112
+ * @param {string} body - Authored fence body.
113
+ * @param {object} [options] - Shared structure and display choices.
114
+ * @param {boolean} [options.labelled] - Whether each stanza carries its label.
115
+ * @param {VerseModel} [options.model] - Parsed structure; defaults to parsing the body.
116
+ * @param {VersePresentation} [options.presentation] - Native-cell font and language.
117
+ * @returns {string} Markdown with verse layout wrappers.
39
118
  */
40
- export function lineNumber(line: number, step: number): string | undefined;
41
- /** Read verse code fences; start/end are inclusive zero-based source lines.
42
- * @param {string} source - Authored Markdown.
119
+ export function verseMarkdown(body: string, { labelled, model, presentation }?: {
120
+ labelled?: boolean | undefined;
121
+ model?: VerseModel | undefined;
122
+ presentation?: VersePresentation | undefined;
123
+ }): string;
124
+ /**
125
+ * Replace verse fences with wrappers whose bodies remain inline Markdown.
126
+ * @param {string} source - Markdown containing verse fences.
127
+ * @param {object} [options] - Encoded display models.
128
+ * @param {Map<string, VersePresentation>} [options.verses] - Presentations keyed by rewritten body.
43
129
  */
44
- export function scanVerse(source: string): {
45
- blocks: {
46
- end: number;
47
- body: string;
48
- start: number;
49
- fence: string;
50
- name: string;
51
- attributes: {};
52
- id: string;
53
- classes: never[];
54
- indent: string;
55
- }[];
56
- errors: any[];
130
+ export function renderVerse(source: string, { verses }?: {
131
+ verses?: Map<string, VersePresentation> | undefined;
132
+ }): {
133
+ markdown: string;
134
+ errors: VerseDiagnostic[];
57
135
  };
58
- /** The number of verse columns; an omitted or invalid count uses the ordinary layout. */
59
- export function verseColumnCount(block: any): number;
60
- /** Whether a verse uses the numbered multi-column protocol. */
61
- export function hasColumns(block: any): boolean;
62
- /**
63
- * Blank-separated multi-column rows with ordered physical parts. Text retains
64
- * its authored inline markup and spacing after the numeric prefix. All source
65
- * offsets are measured before prefixes or indentation periods are removed.
66
- *
67
- * @param {object} block - A verse from scanVerse.
68
- * @param {string[]} lines - The complete source, by line.
69
- * @returns {Array<{stanzaBreak: true, line: number}|{stanzaBreak?: false, parts: Array<{column: number, line: number, sourceColumn: number, textColumn: number, text: string, indent: number, markerStart: number, markerLength: number, prefixLength: number}>}>}
136
+ /** The authored attributes of a verse fence. */
137
+ export const VERSE_KEYS: readonly string[];
138
+ /** A canto separator, including the escaped form produced by a native run. */
139
+ export const STANZA_BREAK: RegExp;
140
+ export function stanzaSeparator(group: any): boolean;
141
+ /**
142
+ * A logical line with its authored UTF-16 coordinates.
70
143
  */
71
- export function verseGroups(block: object, lines: string[]): Array<{
72
- stanzaBreak: true;
73
- line: number;
74
- } | {
75
- stanzaBreak?: false;
76
- parts: Array<{
77
- column: number;
78
- line: number;
79
- sourceColumn: number;
80
- textColumn: number;
81
- text: string;
82
- indent: number;
83
- markerStart: number;
84
- markerLength: number;
85
- prefixLength: number;
86
- }>;
87
- }>;
144
+ export type VerseLine = {
145
+ /**
146
+ * - Inline Markdown in this presentation.
147
+ */
148
+ text: string;
149
+ /**
150
+ * - Leading-period indentation depth.
151
+ */
152
+ level: number;
153
+ /**
154
+ * - First authored offset, including indentation.
155
+ */
156
+ from: number;
157
+ /**
158
+ * - Offset at the line ending.
159
+ */
160
+ to: number;
161
+ /**
162
+ * - Source offset for each UTF-16 text unit.
163
+ */
164
+ offsets: number[];
165
+ /**
166
+ * - Roman text paired with an encoded line.
167
+ */
168
+ romanText?: string | undefined;
169
+ };
88
170
  /**
89
- * Whether text runs right to left: its first letter is in a right-to-left
90
- * block. A face's right-to-left code points sit in U+1E800–U+1EFFF.
91
- *
92
- * @param {string} text - Text, after any map.
93
- * @returns {boolean} Whether it starts right to left.
171
+ * A stanza's logical lines and authored bounds.
94
172
  */
95
- export function rightToLeft(text: string): boolean;
96
- /** Replace verse fences with HTML wrappers whose body remains inline Markdown.
97
- * @param {string} source - Authored Markdown.
173
+ export type VerseStanza = {
174
+ /**
175
+ * - Lines in reading order.
176
+ */
177
+ lines: VerseLine[];
178
+ /**
179
+ * - First authored offset.
180
+ */
181
+ from: number;
182
+ /**
183
+ * - Offset at the stanza ending.
184
+ */
185
+ to: number;
186
+ };
187
+ export type VerseTextGroup = VerseStanza & {
188
+ canto: number;
189
+ stanza: number;
190
+ label: string;
191
+ cantoBreak?: false;
192
+ };
193
+ export type VerseCantoBreak = {
194
+ cantoBreak: true;
195
+ from: number;
196
+ to: number;
197
+ text?: string;
198
+ };
199
+ export type VerseBoundary = {
200
+ kind: "line" | "stanza" | "canto";
201
+ sourceOffset: number;
202
+ };
203
+ export type VerseFinding = {
204
+ offset: number;
205
+ message: string;
206
+ };
207
+ /**
208
+ * Shared authored verse structure and source locations.
98
209
  */
99
- export function renderVerse(source: string): {
100
- markdown: string;
101
- errors: any[];
210
+ export type VerseModel = {
211
+ /**
212
+ * - Cantos in reading order.
213
+ */
214
+ cantos: Array<{
215
+ stanzas: VerseStanza[];
216
+ }>;
217
+ /**
218
+ * - Stanzas and canto breaks.
219
+ */
220
+ groups: Array<VerseTextGroup | VerseCantoBreak>;
221
+ /**
222
+ * - Explicit authored boundaries.
223
+ */
224
+ boundaries: VerseBoundary[];
225
+ /**
226
+ * - Source ranges omitted from encoded letters.
227
+ */
228
+ layout: Array<[number, number]>;
229
+ /**
230
+ * - Findings relative to the fence body.
231
+ */
232
+ errors: VerseFinding[];
102
233
  };
103
- export function stanzaSeparator(group: any): boolean;
104
- /** The layout class the build gives a verse whose cols count exceeds one. */
105
- export const COLUMNS_CLASS: "verse-columns";
106
234
  /**
107
- * The keys a verse fence's attribute block takes. `lang`, `font` and `map`
108
- * are read by `scanRuns` in content-scripts.mjs; `dir`, and on the site
109
- * `data-pagefind-ignore`, are written by its rewrite of a fence set in one of
110
- * the faces, and reach the element as they are written.
235
+ * An authored verse fence with its parsed logical structure.
111
236
  */
112
- export const VERSE_KEYS: readonly string[];
113
- /** A line separating stanzas, including escaped output from a native run. */
114
- export const STANZA_BREAK: RegExp;
237
+ export type VerseBlock = {
238
+ /**
239
+ * - Opening fence's zero-based source line.
240
+ */
241
+ start: number;
242
+ /**
243
+ * - Closing fence's zero-based source line.
244
+ */
245
+ end: number;
246
+ /**
247
+ * - Authored fence delimiter.
248
+ */
249
+ fence: string;
250
+ /**
251
+ * - Fence language.
252
+ */
253
+ name: "verse";
254
+ /**
255
+ * - Authored attribute values.
256
+ */
257
+ attributes: Record<string, string>;
258
+ /**
259
+ * - Authored anchor, or the empty string.
260
+ */
261
+ id: string;
262
+ /**
263
+ * - Authored presentation classes.
264
+ */
265
+ classes: string[];
266
+ /**
267
+ * - Opening fence's leading spaces.
268
+ */
269
+ indent: string;
270
+ /**
271
+ * - Authored fence body.
272
+ */
273
+ body: string;
274
+ /**
275
+ * - Logical structure relative to the body.
276
+ */
277
+ model: VerseModel;
278
+ };
279
+ export type VerseDiagnostic = {
280
+ line: number;
281
+ column: number;
282
+ message: string;
283
+ };
284
+ /**
285
+ * An encoded verse's shared display model and native-cell presentation.
286
+ */
287
+ export type VersePresentation = {
288
+ /**
289
+ * - Logical structure with paired line text.
290
+ */
291
+ model: VerseModel;
292
+ /**
293
+ * - Native script and font classes.
294
+ */
295
+ classes: string[];
296
+ /**
297
+ * - Native language, direction and search attributes.
298
+ */
299
+ attributes: Record<string, string>;
300
+ };
@@ -3,9 +3,9 @@
3
3
  * code blocks are respected so `# foo` inside ``` or ~~~ blocks doesn't
4
4
  * trigger a split. Content before the first H1 (if non-empty) becomes a
5
5
  * leading page. Each H1 yields a page whose name is the heading
6
- * text (with any `{#anchor-id}` suffix stripped out and surfaced as
7
- * `anchorSlug`). The classes the heading declares come along as `classes`, so
8
- * a surface that honours one has it without reading the suffix again.
6
+ * text without inline markup (with any `{#anchor-id}` suffix stripped out and
7
+ * surfaced as `anchorSlug`). The classes the heading declares come along as
8
+ * `classes`, so a surface that honours one has it without reading the suffix again.
9
9
  *
10
10
  * `leadName` names that leading page. A journal note's is "Introduction",
11
11
  * because it introduces the pages that follow. An item doc's is the item — a
@@ -1,14 +1,14 @@
1
1
  /**
2
- * Remove visual indentation before checking or encoding Roman verse. Each
3
- * content line keeps its own boundary, including the last line before a stanza
4
- * or canto; those extra structural controls do not consume its line boundary.
5
- * The caller writes the boundaries through its selected encoding.
2
+ * Project verse text with explicit authored line, stanza and canto boundaries.
3
+ * Source newlines within a logical line are whitespace. Delimiters and visual
4
+ * indentation do not reach the encoder as letters.
6
5
  * @param {string} source - Complete authored source.
7
- * @param {Parameters<typeof plainRomanRun>[1] & {isCantoLine?: (line: string) => boolean}} [options] - Run bounds, spans and layout projection.
6
+ * @param {Parameters<typeof plainRomanRun>[1] & {model?: ReturnType<typeof parseVerse>, modelOffset?: number}} [options] - Run bounds, spans and layout projection.
8
7
  * @returns {ReturnType<typeof plainRomanRun> & {boundaries: Array<{kind: "line"|"stanza"|"canto", at: number, sourceOffset: number}>, errors: Array<{offset: number, message: string}>}} Visible text, positions and structural events.
9
8
  */
10
9
  export function plainRomanVerse(source: string, options?: Parameters<typeof plainRomanRun>[1] & {
11
- isCantoLine?: (line: string) => boolean;
10
+ model?: ReturnType<typeof parseVerse>;
11
+ modelOffset?: number;
12
12
  }): ReturnType<typeof plainRomanRun> & {
13
13
  boundaries: Array<{
14
14
  kind: "line" | "stanza" | "canto";
@@ -21,3 +21,4 @@ export function plainRomanVerse(source: string, options?: Parameters<typeof plai
21
21
  }>;
22
22
  };
23
23
  import { plainRomanRun } from "./roman-run.mjs";
24
+ import { parseVerse } from "./content-verse.mjs";