@heroiclands/package-build 25.2.1 → 25.4.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 +26 -0
- package/README.md +1 -1
- package/assets/styles/content-base.css +97 -116
- package/docs/api.md +30 -6
- package/docs/authoring/links-and-markup.md +214 -126
- package/docs/commands.md +2 -2
- package/docs/content-format.md +31 -6
- package/docs/diagnostics.md +27 -27
- package/docs/guides/writing-in-invented-scripts.md +89 -83
- package/docs/reference/format-details.md +6 -7
- package/docs/reference/note-types.md +15 -15
- package/docs/reference/theme-pages.md +38 -57
- package/engine/anchors.mjs +14 -0
- package/engine/code-fences.mjs +15 -6
- package/engine/content-blocks.mjs +7 -4
- package/engine/content-figures.mjs +19 -5
- package/engine/content-footnotes.mjs +60 -1
- package/engine/content-index.mjs +1 -1
- package/engine/content-render.mjs +66 -48
- package/engine/content-scripts.mjs +357 -101
- package/engine/content-spans.mjs +76 -10
- package/engine/content-verse.mjs +687 -358
- 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/markup-validation.mjs +17 -0
- package/engine/name-markup.mjs +1 -0
- package/engine/note-vocabulary.mjs +2 -2
- package/engine/pdf-render.mjs +89 -130
- package/engine/prose-lint.mjs +14 -2
- package/engine/roman-verse.mjs +68 -57
- package/engine/run-rows.mjs +1 -1
- package/engine/sql-tables.mjs +1 -1
- package/engine/verse-format.mjs +77 -0
- package/engine/verse-metre.mjs +49 -47
- package/engine/verse-regions.mjs +181 -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 +68 -96
- package/types/engine/code-fences.d.mts +1 -3
- package/types/engine/content-blocks.d.mts +3 -1
- package/types/engine/content-footnotes.d.mts +23 -0
- package/types/engine/content-scripts.d.mts +12 -7
- package/types/engine/content-spans.d.mts +3 -1
- package/types/engine/content-verse.d.mts +263 -82
- package/types/engine/name-markup.d.mts +5 -0
- package/types/engine/roman-verse.d.mts +7 -6
- package/types/engine/verse-format.d.mts +9 -0
- package/types/engine/verse-metre.d.mts +4 -4
- package/types/engine/verse-regions.d.mts +78 -0
- package/types/engine/wikilink-pattern.d.mts +17 -0
- package/types/engine/wikilink-syntax.d.mts +1 -41
package/engine/content-spans.mjs
CHANGED
|
@@ -14,7 +14,10 @@
|
|
|
14
14
|
import MarkdownIt from "markdown-it";
|
|
15
15
|
import { alertMarkdownPlugin } from "./content-alerts.mjs";
|
|
16
16
|
import { slugify } from "./content-slug.mjs";
|
|
17
|
-
import { scanVerse } from "./content-verse.mjs";
|
|
17
|
+
import { scanVerse, parseVerse, inlineVerseFindings } from "./content-verse.mjs";
|
|
18
|
+
import { verseDivPlugin } from "./verse-regions.mjs";
|
|
19
|
+
import { codeRegions } from "./code-fences.mjs";
|
|
20
|
+
import { footnoteLabels } from "./content-footnotes.mjs";
|
|
18
21
|
import footnotePlugin from "markdown-it-footnote";
|
|
19
22
|
import { parseExtensionAttributes, refusedAttributes } from "./extension-attributes.mjs";
|
|
20
23
|
import { inlineText } from "./name-markup.mjs";
|
|
@@ -25,8 +28,9 @@ import { cartoucheProblems, glyphProblems } from "./content-glyphs.mjs";
|
|
|
25
28
|
* @param {object} [options] - Surface presentation.
|
|
26
29
|
* @param {boolean} [options.source] - Emit opaque authored source tokens for buffer preview.
|
|
27
30
|
* @param {boolean} [options.spoilers] - Add focusable CSS spoiler wrappers (Foundry).
|
|
31
|
+
* @param {(token: any, state: any) => void} [options.verseInline] - Populate an opaque quoted-verse token during inline parsing.
|
|
28
32
|
*/
|
|
29
|
-
export function spanMarkdownPlugin(md, { spoilers = false, source = false } = {}) {
|
|
33
|
+
export function spanMarkdownPlugin(md, { spoilers = false, source = false, verseInline } = {}) {
|
|
30
34
|
{
|
|
31
35
|
md.renderer.rules.span_open = (tokens, index, options, env, renderer) => {
|
|
32
36
|
const token = tokens[index];
|
|
@@ -110,7 +114,10 @@ export function spanMarkdownPlugin(md, { spoilers = false, source = false } = {}
|
|
|
110
114
|
state.src[start - 1] === "[" &&
|
|
111
115
|
!["lang", "font", "enc"].some((key) => Object.hasOwn(parsed.values, key)) &&
|
|
112
116
|
!parsed.classes.some(
|
|
113
|
-
(name) =>
|
|
117
|
+
(name) =>
|
|
118
|
+
name === "verse" ||
|
|
119
|
+
name === "script" ||
|
|
120
|
+
/^font-(?:mono|serif|sans)$/.test(name),
|
|
114
121
|
)
|
|
115
122
|
)
|
|
116
123
|
return false;
|
|
@@ -141,7 +148,12 @@ export function spanMarkdownPlugin(md, { spoilers = false, source = false } = {}
|
|
|
141
148
|
state.pos = finish;
|
|
142
149
|
return true;
|
|
143
150
|
}
|
|
144
|
-
const
|
|
151
|
+
const verse = parsed.classes.includes("verse") && typeof verseInline === "function";
|
|
152
|
+
const token = state.push(
|
|
153
|
+
verse ? "verse_inline" : "span_open",
|
|
154
|
+
emphasis ?? "span",
|
|
155
|
+
verse ? 0 : 1,
|
|
156
|
+
);
|
|
145
157
|
token.meta = {
|
|
146
158
|
span: true,
|
|
147
159
|
start,
|
|
@@ -164,6 +176,11 @@ export function spanMarkdownPlugin(md, { spoilers = false, source = false } = {}
|
|
|
164
176
|
if (parsed.classes.length) token.attrSet("class", parsed.classes.join(" "));
|
|
165
177
|
for (const [key, value] of Object.entries(parsed.values))
|
|
166
178
|
if (!refusedAttributes({ [key]: value }).length) token.attrSet(key, value);
|
|
179
|
+
if (verse) {
|
|
180
|
+
verseInline(token, state);
|
|
181
|
+
state.pos = finish;
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
167
184
|
const nested = [];
|
|
168
185
|
state.md.inline.parse(state.src.slice(start + 1, end), state.md, state.env, nested);
|
|
169
186
|
for (const child of nested)
|
|
@@ -184,6 +201,7 @@ export function spanMarkdownPlugin(md, { spoilers = false, source = false } = {}
|
|
|
184
201
|
}
|
|
185
202
|
|
|
186
203
|
const parser = new MarkdownIt({ html: true })
|
|
204
|
+
.use(verseDivPlugin)
|
|
187
205
|
.use(footnotePlugin)
|
|
188
206
|
.use(alertMarkdownPlugin)
|
|
189
207
|
.use(spanMarkdownPlugin);
|
|
@@ -293,13 +311,61 @@ export function scanSpans(source) {
|
|
|
293
311
|
}
|
|
294
312
|
// Verse fences preserve inline Markdown even though the block parser
|
|
295
313
|
// normally treats every code fence as literal text.
|
|
296
|
-
for (const poem of scanVerse(text).blocks) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
314
|
+
for (const poem of scanVerse(text, { checkWorks: false }).blocks) {
|
|
315
|
+
const children = [];
|
|
316
|
+
parser.inline.parse(poem.body, parser, env, children);
|
|
317
|
+
collect(children, (pos) => poem.bodyStart + pos);
|
|
318
|
+
}
|
|
319
|
+
const raw = text.split("");
|
|
320
|
+
for (const region of codeRegions(text))
|
|
321
|
+
for (let offset = region.start; offset < region.end; offset++)
|
|
322
|
+
if (!/[\r\n]/.test(raw[offset])) raw[offset] = " ";
|
|
323
|
+
const rawText = raw.join("");
|
|
324
|
+
const quoted = [];
|
|
325
|
+
parser.inline.parse(rawText, parser, env, quoted);
|
|
326
|
+
const verseTokens = quoted.filter(
|
|
327
|
+
(token) =>
|
|
328
|
+
token.meta?.span &&
|
|
329
|
+
(token.attrGet("class") ?? "").split(/\s+/).includes("verse") &&
|
|
330
|
+
!spans.some((span) => span.start === token.meta.start && span.end === token.meta.end),
|
|
331
|
+
);
|
|
332
|
+
collect(verseTokens, (pos) => pos);
|
|
333
|
+
for (const match of rawText.matchAll(/\{[^{}\r\n]*(?:\}|$)/gm)) {
|
|
334
|
+
if (
|
|
335
|
+
!parseExtensionAttributes(match[0].slice(1).replace(/\}$/, "")).classes.includes(
|
|
336
|
+
"verse",
|
|
337
|
+
)
|
|
338
|
+
)
|
|
339
|
+
continue;
|
|
340
|
+
if (rawText[match.index - 1] !== "]") continue;
|
|
341
|
+
if (
|
|
342
|
+
spans.some(
|
|
343
|
+
(span) =>
|
|
344
|
+
span.contentEnd + 1 === match.index &&
|
|
345
|
+
span.end >= match.index + match[0].length,
|
|
346
|
+
)
|
|
347
|
+
)
|
|
348
|
+
continue;
|
|
349
|
+
const { line, column } = located(match.index, match.index + match[0].length);
|
|
350
|
+
errors.push({
|
|
351
|
+
line,
|
|
352
|
+
column,
|
|
353
|
+
message:
|
|
354
|
+
"an inline .verse quotation needs balanced brackets and closing {…} attributes",
|
|
355
|
+
});
|
|
302
356
|
}
|
|
357
|
+
for (const span of spans)
|
|
358
|
+
if (
|
|
359
|
+
span.attrs.some(
|
|
360
|
+
([key, value]) => key === "class" && value.split(/\s+/).includes("verse"),
|
|
361
|
+
)
|
|
362
|
+
) {
|
|
363
|
+
span.verseModel = parseVerse(span.text, {
|
|
364
|
+
inline: true,
|
|
365
|
+
footnotes: footnoteLabels(text),
|
|
366
|
+
});
|
|
367
|
+
errors.push(...inlineVerseFindings(span, text));
|
|
368
|
+
}
|
|
303
369
|
spans.sort((a, b) => a.start - b.start);
|
|
304
370
|
errors.sort((a, b) => a.line - b.line || a.column - b.column);
|
|
305
371
|
return {
|