@heroiclands/package-build 23.13.0 → 24.0.1
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 +50 -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 +248 -80
- 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 +155 -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 +265 -313
- 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-reach.mjs +213 -0
- package/engine/site-render.mjs +668 -0
- package/engine/site-root.mjs +90 -39
- package/engine/site-search.mjs +161 -0
- package/engine/site-serve.mjs +274 -0
- package/engine/site-theme-banner.mjs +154 -0
- package/engine/site-theme-urls.mjs +125 -0
- package/engine/site-theme.mjs +939 -0
- package/engine/sql-tables.mjs +9 -6
- 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-reach.d.mts +76 -0
- package/types/engine/site-render.d.mts +173 -0
- package/types/engine/site-root.d.mts +14 -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
package/engine/site-preview.mjs
CHANGED
|
@@ -13,528 +13,23 @@
|
|
|
13
13
|
|
|
14
14
|
/** Buffer-only HTML preview shared by editors and external renderers. @module */
|
|
15
15
|
|
|
16
|
-
import
|
|
17
|
-
import footnotePlugin from "markdown-it-footnote";
|
|
18
|
-
import deflistPlugin from "markdown-it-deflist";
|
|
19
|
-
import { dictionaryListPlugin } from "./definition-list.mjs";
|
|
20
|
-
import Handlebars from "handlebars";
|
|
16
|
+
import { sharedContentRules } from "./content-styles.mjs";
|
|
21
17
|
import { readMatter } from "./note-matter.mjs";
|
|
22
|
-
import { ALERT_TYPES, alertMarkdownPlugin, scanAlerts } from "./content-alerts.mjs";
|
|
23
|
-
import { spanMarkdownPlugin } from "./content-spans.mjs";
|
|
24
|
-
import { scanVerse, verseStanzas } from "./content-verse.mjs";
|
|
25
18
|
import { scanFigures } from "./content-figures.mjs";
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import { EXPRESSION, renderMarkdownExpressions } from "./markdown-expressions.mjs";
|
|
29
|
-
import { WIKILINK, parseWikilink } from "./wikilink-syntax.mjs";
|
|
19
|
+
import { createRenderer, inertHtml } from "./content-render.mjs";
|
|
20
|
+
import { PREVIEW_HELPERS, colored, wikiSource } from "./preview-source.mjs";
|
|
30
21
|
import { dataFields } from "./note-vocabulary.mjs";
|
|
31
22
|
import { applyComputedBeingAge } from "./being-age.mjs";
|
|
32
23
|
import { DEFAULT_CURRENT_DATE, normalizeCurrentDate } from "./current-date.mjs";
|
|
33
24
|
|
|
34
|
-
|
|
35
|
-
export const PREVIEW_HELPERS = Object.freeze({
|
|
36
|
-
eq: "local",
|
|
37
|
-
gt: "local",
|
|
38
|
-
gte: "local",
|
|
39
|
-
lt: "local",
|
|
40
|
-
lte: "local",
|
|
41
|
-
not: "local",
|
|
42
|
-
and: "local",
|
|
43
|
-
or: "local",
|
|
44
|
-
words: "local",
|
|
45
|
-
digits: "local",
|
|
46
|
-
sql: "dependent",
|
|
47
|
-
dateformat: "dependent",
|
|
48
|
-
ref: "reference",
|
|
49
|
-
});
|
|
25
|
+
export { PREVIEW_HELPERS };
|
|
50
26
|
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const open = raw.startsWith("!") ? "![[" : "[[";
|
|
58
|
-
const close = raw.indexOf("]]", open.length);
|
|
59
|
-
const end = close < 0 ? raw.length : close;
|
|
60
|
-
const inner = raw.slice(open.length, end);
|
|
61
|
-
const pipe = inner.indexOf("|");
|
|
62
|
-
const secret = close >= 0 && parseWikilink(inner, raw).attributes?.classes.includes("secret");
|
|
63
|
-
const label =
|
|
64
|
-
colored("address", pipe < 0 ? inner : inner.slice(0, pipe)) +
|
|
65
|
-
(pipe < 0 ? "" : colored("bracket", "|") + colored("label", inner.slice(pipe + 1)));
|
|
66
|
-
const html =
|
|
67
|
-
colored("bracket", open) +
|
|
68
|
-
(secret ? `<span class="hl-preview-secret-content">${label}</span>` : label) +
|
|
69
|
-
(close < 0 ? "" : colored("bracket", "]]") + colored("attribute", raw.slice(close + 2)));
|
|
70
|
-
return secret ? `<span class="hl-preview-secret" tabindex="0">${html}</span>` : html;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/** Read a dependent call anywhere in the Handlebars AST, including hash values. */
|
|
74
|
-
function dependentExpression(source, fm) {
|
|
75
|
-
const authoredValue = (node) => {
|
|
76
|
-
if (node?.type === "StringLiteral") return node.value;
|
|
77
|
-
if (node?.type !== "PathExpression" || node.depth || node.data || !node.parts?.length)
|
|
78
|
-
return undefined;
|
|
79
|
-
let value = fm;
|
|
80
|
-
for (const part of node.parts) {
|
|
81
|
-
if (!value || typeof value !== "object" || !Object.hasOwn(value, part))
|
|
82
|
-
return undefined;
|
|
83
|
-
value = value[part];
|
|
84
|
-
}
|
|
85
|
-
return value;
|
|
86
|
-
};
|
|
87
|
-
const visit = (node) => {
|
|
88
|
-
if (!node || typeof node !== "object") return false;
|
|
89
|
-
if (["MustacheStatement", "SubExpression"].includes(node.type)) {
|
|
90
|
-
const name = node.path?.original;
|
|
91
|
-
const policy = PREVIEW_HELPERS[name];
|
|
92
|
-
if (policy === "dependent") return true;
|
|
93
|
-
if (policy === "reference") {
|
|
94
|
-
const target = authoredValue(node.params?.[0]);
|
|
95
|
-
if (typeof target !== "string" || !target.startsWith("#")) return true;
|
|
96
|
-
// Date-valued event fields need calendar context even on this note.
|
|
97
|
-
if (
|
|
98
|
-
node.hash?.pairs?.some(
|
|
99
|
-
(pair) =>
|
|
100
|
-
pair.key === "field" &&
|
|
101
|
-
!["kind", "summary", "name"].includes(pair.value?.value),
|
|
102
|
-
)
|
|
103
|
-
)
|
|
104
|
-
return true;
|
|
105
|
-
}
|
|
106
|
-
if ((node.params?.length || node.hash?.pairs?.length) && !policy) return true;
|
|
107
|
-
}
|
|
108
|
-
return Object.values(node).some((value) =>
|
|
109
|
-
Array.isArray(value) ? value.some(visit) : visit(value),
|
|
110
|
-
);
|
|
111
|
-
};
|
|
112
|
-
try {
|
|
113
|
-
return visit(Handlebars.parse(source));
|
|
114
|
-
} catch {
|
|
115
|
-
return true;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function parserFor(fm, findings, file) {
|
|
120
|
-
const md = new MarkdownIt({ html: false, linkify: false })
|
|
121
|
-
.use(footnotePlugin)
|
|
122
|
-
.use(deflistPlugin)
|
|
123
|
-
.use(dictionaryListPlugin)
|
|
124
|
-
.use(alertMarkdownPlugin)
|
|
125
|
-
.use(spanMarkdownPlugin, { source: true });
|
|
126
|
-
const figures = new Map();
|
|
127
|
-
const inline = (text, env = {}) => {
|
|
128
|
-
const tokens = [];
|
|
129
|
-
md.inline.parse(text, md, env, tokens);
|
|
130
|
-
return md.renderer.renderInline(tokens, md.options, env);
|
|
131
|
-
};
|
|
132
|
-
md.renderer.rules.hl_preview_source = (tokens, idx) => tokens[idx].meta.html;
|
|
133
|
-
// Run before ordinary links and text so protected source becomes one opaque token.
|
|
134
|
-
md.inline.ruler.before("link", "hl_preview_source", (state, silent) => {
|
|
135
|
-
const rest = state.src.slice(state.pos);
|
|
136
|
-
let raw, html;
|
|
137
|
-
if (rest.startsWith("[[") || rest.startsWith("![[")) {
|
|
138
|
-
const match = new RegExp(`^(?:!)?${WIKILINK.source.replace("(?<!!)", "")}`).exec(rest);
|
|
139
|
-
raw = match?.[0] ?? rest.split("\n")[0];
|
|
140
|
-
// Preserve a partially entered trailing attribute too.
|
|
141
|
-
if (rest[raw.length] === "{") {
|
|
142
|
-
let quoted = false,
|
|
143
|
-
escaped = false,
|
|
144
|
-
end = raw.length + 1;
|
|
145
|
-
for (; end < rest.length && rest[end] !== "\n"; end++) {
|
|
146
|
-
const char = rest[end];
|
|
147
|
-
if (escaped) {
|
|
148
|
-
escaped = false;
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
151
|
-
if (char === "\\") {
|
|
152
|
-
escaped = true;
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
if (char === '"') quoted = !quoted;
|
|
156
|
-
if (char === "}" && !quoted) {
|
|
157
|
-
end++;
|
|
158
|
-
break;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
raw = rest.slice(0, end);
|
|
162
|
-
}
|
|
163
|
-
html = wikiSource(raw);
|
|
164
|
-
} else if (rest.startsWith("{{")) {
|
|
165
|
-
const match = new RegExp(`^${EXPRESSION.source.replace("(?<![\\\\{])", "")}`).exec(
|
|
166
|
-
rest,
|
|
167
|
-
);
|
|
168
|
-
raw = match?.[0] ?? rest.split("\n")[0];
|
|
169
|
-
if (!dependentExpression(raw, fm) && match) {
|
|
170
|
-
const entries =
|
|
171
|
-
Array.isArray(fm.data?.events) ?
|
|
172
|
-
fm.data.events.filter((entry) => entry && typeof entry === "object")
|
|
173
|
-
: [];
|
|
174
|
-
const own = {
|
|
175
|
-
events: entries.map((entry) => ({ id: entry.id, entry })),
|
|
176
|
-
anchors: entries
|
|
177
|
-
.filter((entry) => entry.id)
|
|
178
|
-
.map((entry) => ({ slug: entry.id, kind: "event" })),
|
|
179
|
-
};
|
|
180
|
-
const events = { note: () => undefined, own: () => own };
|
|
181
|
-
const result = renderMarkdownExpressions(raw, { fm, figures, events, file });
|
|
182
|
-
if (!result.findings.length)
|
|
183
|
-
html = /^{{\s*ref\b/.test(raw) ? inline(result.markdown) : result.markdown;
|
|
184
|
-
}
|
|
185
|
-
html ??= colored("dependent", raw);
|
|
186
|
-
} else return false;
|
|
187
|
-
if (!silent) {
|
|
188
|
-
const token = state.push("hl_preview_source", "", 0);
|
|
189
|
-
token.meta = { html };
|
|
190
|
-
}
|
|
191
|
-
state.pos += raw.length;
|
|
192
|
-
return true;
|
|
193
|
-
});
|
|
194
|
-
md.renderer.rules.span_source = (tokens, idx) => {
|
|
195
|
-
const token = tokens[idx];
|
|
196
|
-
const end = token.meta.labelEnd;
|
|
197
|
-
const secret = Boolean(token.meta.secret);
|
|
198
|
-
const classes = `hl-preview-span${secret ? " hl-preview-secret" : ""}`;
|
|
199
|
-
const focus = secret ? ' tabindex="0"' : "";
|
|
200
|
-
const label = colored("label", token.content.slice(1, end));
|
|
201
|
-
return `<span class="${classes}"${focus}>${
|
|
202
|
-
colored("bracket", "[") +
|
|
203
|
-
(secret ? `<span class="hl-preview-secret-content">${label}</span>` : label) +
|
|
204
|
-
colored("bracket", "]") +
|
|
205
|
-
colored("attribute", token.content.slice(end + 1))
|
|
206
|
-
}</span>`;
|
|
207
|
-
};
|
|
208
|
-
// Keep extension pipes inside their cell. Markdown's table splitter only
|
|
209
|
-
// understands escaped pipes, before inline rules have a chance to see them.
|
|
210
|
-
md.block.ruler.before(
|
|
211
|
-
"table",
|
|
212
|
-
"hl_preview_table",
|
|
213
|
-
(state, start, end, silent) => {
|
|
214
|
-
const lineAt = (line) =>
|
|
215
|
-
state.src.slice(state.bMarks[line] + state.tShift[line], state.eMarks[line]);
|
|
216
|
-
const cells = (line) => {
|
|
217
|
-
const result = [];
|
|
218
|
-
let cell = "",
|
|
219
|
-
wiki = false,
|
|
220
|
-
code = "",
|
|
221
|
-
quote = false,
|
|
222
|
-
braces = 0;
|
|
223
|
-
for (let at = 0; at < line.length; at++) {
|
|
224
|
-
const char = line[at];
|
|
225
|
-
if (char === "\\" && at + 1 < line.length) {
|
|
226
|
-
cell += char + line[++at];
|
|
227
|
-
continue;
|
|
228
|
-
}
|
|
229
|
-
if (char === "`" && !wiki) {
|
|
230
|
-
const marks = /^`+/.exec(line.slice(at))[0];
|
|
231
|
-
if (!code) code = marks;
|
|
232
|
-
else if (code === marks) code = "";
|
|
233
|
-
cell += marks;
|
|
234
|
-
at += marks.length - 1;
|
|
235
|
-
continue;
|
|
236
|
-
}
|
|
237
|
-
if (!code) {
|
|
238
|
-
if (line.slice(at, at + 2) === "[[") wiki = true;
|
|
239
|
-
if (line.slice(at - 1, at + 1) === "]]") wiki = false;
|
|
240
|
-
if (char === '"' && braces) quote = !quote;
|
|
241
|
-
if (!quote && char === "{") braces++;
|
|
242
|
-
if (!quote && char === "}" && braces) braces--;
|
|
243
|
-
}
|
|
244
|
-
if (char === "|" && !wiki && !code && !braces) {
|
|
245
|
-
result.push(cell.trim());
|
|
246
|
-
cell = "";
|
|
247
|
-
} else cell += char;
|
|
248
|
-
}
|
|
249
|
-
result.push(cell.trim());
|
|
250
|
-
if (!result[0]) result.shift();
|
|
251
|
-
if (!result.at(-1)) result.pop();
|
|
252
|
-
return result;
|
|
253
|
-
};
|
|
254
|
-
if (start + 1 >= end || state.sCount[start] - state.blkIndent >= 4) return false;
|
|
255
|
-
const header = cells(lineAt(start)),
|
|
256
|
-
separators = cells(lineAt(start + 1));
|
|
257
|
-
if (
|
|
258
|
-
!lineAt(start).includes("|") ||
|
|
259
|
-
!header.length ||
|
|
260
|
-
separators.length !== header.length ||
|
|
261
|
-
!separators.every((cell) => /^:?-+:?$/.test(cell))
|
|
262
|
-
)
|
|
263
|
-
return false;
|
|
264
|
-
if (silent) return true;
|
|
265
|
-
const row = (values, tag) => {
|
|
266
|
-
state.push("tr_open", "tr", 1);
|
|
267
|
-
for (let col = 0; col < header.length; col++) {
|
|
268
|
-
const cell = state.push(`${tag}_open`, tag, 1);
|
|
269
|
-
const alignment =
|
|
270
|
-
separators[col].startsWith(":") ?
|
|
271
|
-
separators[col].endsWith(":") ?
|
|
272
|
-
"center"
|
|
273
|
-
: "left"
|
|
274
|
-
: separators[col].endsWith(":") ? "right"
|
|
275
|
-
: "";
|
|
276
|
-
if (alignment) cell.attrSet("class", `hl-preview-align-${alignment}`);
|
|
277
|
-
const token = state.push("inline", "", 0);
|
|
278
|
-
token.content = values[col] ?? "";
|
|
279
|
-
token.children = [];
|
|
280
|
-
state.push(`${tag}_close`, tag, -1);
|
|
281
|
-
}
|
|
282
|
-
state.push("tr_close", "tr", -1);
|
|
283
|
-
};
|
|
284
|
-
const open = state.push("table_open", "table", 1);
|
|
285
|
-
open.map = [start, start + 2];
|
|
286
|
-
state.push("thead_open", "thead", 1);
|
|
287
|
-
row(header, "th");
|
|
288
|
-
state.push("thead_close", "thead", -1);
|
|
289
|
-
state.push("tbody_open", "tbody", 1);
|
|
290
|
-
let line = start + 2;
|
|
291
|
-
for (; line < end && lineAt(line).trim() && lineAt(line).includes("|"); line++)
|
|
292
|
-
row(cells(lineAt(line)), "td");
|
|
293
|
-
state.push("tbody_close", "tbody", -1);
|
|
294
|
-
state.push("table_close", "table", -1);
|
|
295
|
-
open.map[1] = line;
|
|
296
|
-
state.line = line;
|
|
297
|
-
return true;
|
|
298
|
-
},
|
|
299
|
-
{ alt: ["paragraph", "reference"] },
|
|
300
|
-
);
|
|
301
|
-
const quoteOpen = md.renderer.rules.blockquote_open;
|
|
302
|
-
md.renderer.rules.blockquote_open = (tokens, idx, options, env, self) => {
|
|
303
|
-
const block = tokens[idx].meta?.alert;
|
|
304
|
-
if (!block)
|
|
305
|
-
return quoteOpen ? quoteOpen(tokens, idx, options, env, self) : "<blockquote>\n";
|
|
306
|
-
const id = block.id ? ` id="${escape(block.id)}"` : "";
|
|
307
|
-
return `<blockquote class="alert alert-${block.type}"${id}>\n`;
|
|
308
|
-
};
|
|
309
|
-
md.renderer.rules.hl_preview_alert_title_open = (tokens, idx) =>
|
|
310
|
-
`<p class="alert-title">${ALERT_TYPES[tokens[idx].meta.type].svg}`;
|
|
311
|
-
const fence = md.renderer.rules.fence;
|
|
312
|
-
md.renderer.rules.fence = (tokens, idx, options, env, self) => {
|
|
313
|
-
const token = tokens[idx];
|
|
314
|
-
const name = token.info.trim().split(/\s/)[0];
|
|
315
|
-
if (["sql", "pagelist", "map"].includes(name)) {
|
|
316
|
-
const raw =
|
|
317
|
-
token.meta?.source ??
|
|
318
|
-
`${token.markup}${token.info}\n${token.content}${token.markup}`;
|
|
319
|
-
return `<pre class="hl-preview-${name === "map" ? "dependent" : "fence"}"><code>${escape(raw)}</code></pre>\n`;
|
|
320
|
-
}
|
|
321
|
-
return fence(tokens, idx, options, env, self);
|
|
322
|
-
};
|
|
323
|
-
// Capture fences from their original source lines, retaining all delimiters.
|
|
324
|
-
md.core.ruler.before("inline", "hl_preview_structure", (state) => {
|
|
325
|
-
const lines = state.src.split("\n");
|
|
326
|
-
const alertErrors = scanAlerts(state.src).errors;
|
|
327
|
-
for (let i = state.tokens.length - 1; i >= 0; i--) {
|
|
328
|
-
const token = state.tokens[i],
|
|
329
|
-
alert = token.meta?.alert;
|
|
330
|
-
if (
|
|
331
|
-
!alert ||
|
|
332
|
-
!alertErrors.some(
|
|
333
|
-
(error) => error.line >= alert.start + 1 && error.line <= alert.end + 1,
|
|
334
|
-
)
|
|
335
|
-
)
|
|
336
|
-
continue;
|
|
337
|
-
let depth = 1,
|
|
338
|
-
end = i + 1;
|
|
339
|
-
for (; end < state.tokens.length; end++) {
|
|
340
|
-
if (state.tokens[end].type === "blockquote_open") depth++;
|
|
341
|
-
if (state.tokens[end].type === "blockquote_close" && --depth === 0) break;
|
|
342
|
-
}
|
|
343
|
-
token.type = "hl_preview_source";
|
|
344
|
-
token.nesting = 0;
|
|
345
|
-
token.meta = {
|
|
346
|
-
html: `<pre>${escape(lines.slice(alert.start, alert.end + 1).join("\n"))}</pre>`,
|
|
347
|
-
};
|
|
348
|
-
state.tokens.splice(i + 1, end - i);
|
|
349
|
-
}
|
|
350
|
-
for (let i = state.tokens.length - 1; i >= 0; i--) {
|
|
351
|
-
const block = state.tokens[i].meta?.alert;
|
|
352
|
-
if (!block) continue;
|
|
353
|
-
const open = new state.Token("hl_preview_alert_title_open", "p", 1);
|
|
354
|
-
open.meta = block;
|
|
355
|
-
const title = new state.Token("inline", "", 0);
|
|
356
|
-
title.content = block.title;
|
|
357
|
-
title.children = [];
|
|
358
|
-
const close = new state.Token("paragraph_close", "p", -1);
|
|
359
|
-
state.tokens.splice(i + 1, 0, open, title, close);
|
|
360
|
-
}
|
|
361
|
-
const verse = scanVerse(state.src).blocks;
|
|
362
|
-
const localFigures = scanFigures(state.src).figures;
|
|
363
|
-
for (const fig of localFigures) if (fig.id) figures.set(fig.id, fig);
|
|
364
|
-
for (const token of state.tokens) {
|
|
365
|
-
if (token.type === "fence")
|
|
366
|
-
token.meta = {
|
|
367
|
-
...token.meta,
|
|
368
|
-
source: lines.slice(token.map[0], token.map[1]).join("\n"),
|
|
369
|
-
poem: verse.find((poem) => poem.start === token.map[0]),
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
// Verse must become inline tokens before footnote collection, so a
|
|
373
|
-
// definition outside the fence remains visible to a verse reference.
|
|
374
|
-
for (let i = state.tokens.length - 1; i >= 0; i--) {
|
|
375
|
-
const token = state.tokens[i];
|
|
376
|
-
const poem = token.meta?.poem;
|
|
377
|
-
if (!poem) continue;
|
|
378
|
-
const replacement = [];
|
|
379
|
-
const push = (type, tag, nesting, attrs) => {
|
|
380
|
-
const part = new state.Token(type, tag, nesting);
|
|
381
|
-
if (attrs) part.attrs = attrs;
|
|
382
|
-
replacement.push(part);
|
|
383
|
-
return part;
|
|
384
|
-
};
|
|
385
|
-
push("hl_preview_verse_open", "div", 1, [["class", "verse"]]).map = token.map;
|
|
386
|
-
for (const stanza of verseStanzas(poem.body)) {
|
|
387
|
-
push("paragraph_open", "p", 1);
|
|
388
|
-
stanza.forEach(({ text, level }, row) => {
|
|
389
|
-
if (row) push("hardbreak", "br", 0);
|
|
390
|
-
push("hl_preview_verse_line_open", "span", 1, [
|
|
391
|
-
["class", `verse-line i${level}`],
|
|
392
|
-
]);
|
|
393
|
-
const line = push("inline", "", 0);
|
|
394
|
-
line.content = text;
|
|
395
|
-
line.children = [];
|
|
396
|
-
push("hl_preview_verse_line_close", "span", -1);
|
|
397
|
-
});
|
|
398
|
-
push("paragraph_close", "p", -1);
|
|
399
|
-
}
|
|
400
|
-
push("hl_preview_verse_close", "div", -1);
|
|
401
|
-
state.tokens.splice(i, 1, ...replacement);
|
|
402
|
-
}
|
|
403
|
-
// Work backwards so figure ranges and heading removals retain source maps.
|
|
404
|
-
for (let i = state.tokens.length - 1; i >= 0; i--) {
|
|
405
|
-
const token = state.tokens[i];
|
|
406
|
-
if (token.type !== "heading_open") continue;
|
|
407
|
-
const content = state.tokens[i + 1];
|
|
408
|
-
const parsed = splitHeadingAttributes(content.content);
|
|
409
|
-
if (/^(?:see also|related pages)$/i.test(parsed.text)) {
|
|
410
|
-
const level = Number(token.tag.slice(1));
|
|
411
|
-
let end = i + 3;
|
|
412
|
-
while (
|
|
413
|
-
end < state.tokens.length &&
|
|
414
|
-
!(
|
|
415
|
-
state.tokens[end].type === "heading_open" &&
|
|
416
|
-
Number(state.tokens[end].tag.slice(1)) <= level
|
|
417
|
-
)
|
|
418
|
-
)
|
|
419
|
-
end++;
|
|
420
|
-
state.tokens.splice(i, end - i);
|
|
421
|
-
} else if (!parsed.problems.length) {
|
|
422
|
-
content.content = parsed.text;
|
|
423
|
-
if (parsed.id) token.attrSet("id", parsed.id);
|
|
424
|
-
token.meta = { ...token.meta, secret: parsed.classes.includes("secret") };
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
for (const fig of localFigures.reverse()) {
|
|
428
|
-
const first = state.tokens.findIndex(
|
|
429
|
-
(token) => token.type === "paragraph_open" && token.map?.[0] === fig.captionStart,
|
|
430
|
-
);
|
|
431
|
-
if (first < 0) continue;
|
|
432
|
-
let after = first + 3;
|
|
433
|
-
while (
|
|
434
|
-
after < state.tokens.length &&
|
|
435
|
-
(state.tokens[after].map?.[0] ?? -1) < fig.bodyEnd
|
|
436
|
-
)
|
|
437
|
-
after++;
|
|
438
|
-
// Match closing tokens of the last block as well.
|
|
439
|
-
while (
|
|
440
|
-
after < state.tokens.length &&
|
|
441
|
-
!state.tokens[after].map &&
|
|
442
|
-
state.tokens[after].nesting < 0
|
|
443
|
-
)
|
|
444
|
-
after++;
|
|
445
|
-
const open = new state.Token("hl_preview_figure_open", "div", 1);
|
|
446
|
-
open.meta = fig;
|
|
447
|
-
const caption = new state.Token("inline", "", 0);
|
|
448
|
-
caption.content = `${fig.label}${fig.label && fig.hasCaption ? ": " : ""}${fig.caption}`;
|
|
449
|
-
caption.children = [];
|
|
450
|
-
const titleOpen = new state.Token("paragraph_open", "p", 1);
|
|
451
|
-
titleOpen.attrSet("class", "content-figure-label");
|
|
452
|
-
const titleClose = new state.Token("paragraph_close", "p", -1);
|
|
453
|
-
const close = new state.Token("hl_preview_figure_close", "div", -1);
|
|
454
|
-
state.tokens.splice(after, 0, titleOpen, caption, titleClose, close);
|
|
455
|
-
state.tokens.splice(first, 3, open);
|
|
456
|
-
}
|
|
457
|
-
// Use parsed heading tokens so fenced examples are never section boundaries.
|
|
458
|
-
// Keep all inline tokens in this document for shared footnote collection.
|
|
459
|
-
const output = [],
|
|
460
|
-
sections = [];
|
|
461
|
-
const closeSection = () => {
|
|
462
|
-
output.push(new state.Token("details_close", "details", -1));
|
|
463
|
-
sections.pop();
|
|
464
|
-
};
|
|
465
|
-
for (const token of state.tokens) {
|
|
466
|
-
const level = token.type === "heading_open" ? Number(token.tag.slice(1)) : null;
|
|
467
|
-
while (
|
|
468
|
-
sections.length &&
|
|
469
|
-
((token.nesting === -1 && token.level < sections.at(-1).scope) ||
|
|
470
|
-
(level !== null &&
|
|
471
|
-
token.level <= sections.at(-1).scope &&
|
|
472
|
-
level <= sections.at(-1).level))
|
|
473
|
-
)
|
|
474
|
-
closeSection();
|
|
475
|
-
if (token.type === "heading_open" && token.meta?.secret) {
|
|
476
|
-
const open = new state.Token("details_open", "details", 1);
|
|
477
|
-
open.attrSet("class", "secret");
|
|
478
|
-
output.push(open, new state.Token("summary_open", "summary", 1));
|
|
479
|
-
const title = new state.Token("inline", "", 0);
|
|
480
|
-
title.content = "Secret";
|
|
481
|
-
title.children = [];
|
|
482
|
-
output.push(title, new state.Token("summary_close", "summary", -1));
|
|
483
|
-
sections.push({ level, scope: token.level });
|
|
484
|
-
}
|
|
485
|
-
output.push(token);
|
|
486
|
-
}
|
|
487
|
-
while (sections.length) closeSection();
|
|
488
|
-
state.tokens = output;
|
|
489
|
-
});
|
|
490
|
-
md.renderer.rules.hl_preview_figure_open = (tokens, idx) => {
|
|
491
|
-
const fig = tokens[idx].meta;
|
|
492
|
-
return `<div class="content-figure content-figure-${escape(fig.kind)}"${fig.slug ? ` id="${escape(fig.slug)}"` : ""}>\n`;
|
|
493
|
-
};
|
|
494
|
-
md.renderer.rules.hl_preview_figure_close = () => "</div>\n";
|
|
495
|
-
// Shared container parser determines complete nested ranges; tokenizing each
|
|
496
|
-
// body within the same state keeps document footnotes in scope.
|
|
497
|
-
md.block.ruler.before(
|
|
498
|
-
"fence",
|
|
499
|
-
"hl_preview_container",
|
|
500
|
-
(state, start, end, silent) => {
|
|
501
|
-
if (!state.src.slice(state.bMarks[start] + state.tShift[start]).startsWith(":::"))
|
|
502
|
-
return false;
|
|
503
|
-
const block = scanBlocks(state.src).blocks.find((block) => block.start === start);
|
|
504
|
-
if (!block || block.end >= end) return false;
|
|
505
|
-
if (silent) return true;
|
|
506
|
-
const open = state.push(
|
|
507
|
-
"hl_preview_container_open",
|
|
508
|
-
block.name === "secret" ? "details" : "div",
|
|
509
|
-
1,
|
|
510
|
-
);
|
|
511
|
-
open.meta = block;
|
|
512
|
-
if (block.name === "secret") {
|
|
513
|
-
state.push("summary_open", "summary", 1);
|
|
514
|
-
const title = state.push("inline", "", 0);
|
|
515
|
-
title.content = block.title;
|
|
516
|
-
title.children = [];
|
|
517
|
-
state.push("summary_close", "summary", -1);
|
|
518
|
-
}
|
|
519
|
-
const oldMax = state.lineMax;
|
|
520
|
-
state.lineMax = block.end;
|
|
521
|
-
state.md.block.tokenize(state, start + 1, block.end);
|
|
522
|
-
state.lineMax = oldMax;
|
|
523
|
-
const close = state.push("hl_preview_container_close", open.tag, -1);
|
|
524
|
-
close.meta = block;
|
|
525
|
-
state.line = block.end + 1;
|
|
526
|
-
return true;
|
|
527
|
-
},
|
|
528
|
-
{ alt: ["paragraph", "reference", "blockquote", "list"] },
|
|
529
|
-
);
|
|
530
|
-
md.renderer.rules.hl_preview_container_open = (tokens, idx, _options, env) => {
|
|
531
|
-
const block = tokens[idx].meta;
|
|
532
|
-
const attrs = `${block.id ? ` id="${escape(block.id)}"` : ""} class="${escape([block.name, ...block.classes].join(" "))}"`;
|
|
533
|
-
return block.name === "secret" ? `<details${attrs}>` : `<div${attrs}>\n`;
|
|
534
|
-
};
|
|
535
|
-
md.renderer.rules.hl_preview_container_close = (tokens, idx) => `</${tokens[idx].tag}>\n`;
|
|
536
|
-
return md;
|
|
537
|
-
}
|
|
27
|
+
const escape = (value) =>
|
|
28
|
+
String(value ?? "")
|
|
29
|
+
.replace(/&/g, "&")
|
|
30
|
+
.replace(/</g, "<")
|
|
31
|
+
.replace(/>/g, ">")
|
|
32
|
+
.replace(/"/g, """);
|
|
538
33
|
|
|
539
34
|
/** A small profile from authored fields; references never acquire resolved data. */
|
|
540
35
|
function localInfobox(fm, md) {
|
|
@@ -569,14 +64,23 @@ function localInfobox(fm, md) {
|
|
|
569
64
|
}
|
|
570
65
|
|
|
571
66
|
/**
|
|
572
|
-
* Render one complete buffer
|
|
67
|
+
* Render one complete buffer.
|
|
68
|
+
*
|
|
69
|
+
* Without `index` the buffer is rendered with no configuration, content index,
|
|
70
|
+
* filesystem or database: wikilinks, spans and expressions show as highlighted
|
|
71
|
+
* authored source. With `index` the buffer is expanded as the website expands
|
|
72
|
+
* a page and rendered as every target renders it, so links, embeds, marked
|
|
73
|
+
* text, icons and figure numbers read as they will on the page; only what a
|
|
74
|
+
* buffer cannot answer, such as a query, stays highlighted source.
|
|
75
|
+
*
|
|
573
76
|
* @param {string} text - Markdown, optionally beginning with YAML frontmatter.
|
|
574
|
-
* @param {{dark?: boolean, infobox?: boolean, file?: string, currentDate?: string
|
|
77
|
+
* @param {{dark?: boolean, infobox?: boolean, file?: string, currentDate?: string,
|
|
78
|
+
* index?: import("./preview-index.mjs").PreviewIndex}} [options] - Explicit palette, profile visibility, diagnostic filename, neutral current date (default "720.1") and the content index, from {@link module:engine/preview-index.previewIndex}.
|
|
575
79
|
* @returns {{ok: boolean, html: string, frontmatter: object, findings: object[]}} HTML and metadata with locally calculable ages; unavailable ages remain absent or null.
|
|
576
80
|
*/
|
|
577
81
|
export function renderPreview(
|
|
578
82
|
text,
|
|
579
|
-
{ dark = false, infobox = true, file = "", currentDate = DEFAULT_CURRENT_DATE } = {},
|
|
83
|
+
{ dark = false, infobox = true, file = "", currentDate = DEFAULT_CURRENT_DATE, index } = {},
|
|
580
84
|
) {
|
|
581
85
|
const date = normalizeCurrentDate(currentDate);
|
|
582
86
|
const findings = [];
|
|
@@ -602,9 +106,33 @@ export function renderPreview(
|
|
|
602
106
|
)
|
|
603
107
|
frontmatter.data.age = null;
|
|
604
108
|
}
|
|
605
|
-
|
|
606
|
-
const
|
|
607
|
-
const
|
|
109
|
+
// A tag the author wrote reads as text; the stages below write the only HTML.
|
|
110
|
+
const authored = inertHtml(body);
|
|
111
|
+
const indexed = Boolean(index);
|
|
112
|
+
const scan = scanFigures(authored);
|
|
113
|
+
const renderer = createRenderer("preview", {
|
|
114
|
+
source: !indexed,
|
|
115
|
+
sourceContext: {
|
|
116
|
+
fm: frontmatter,
|
|
117
|
+
file,
|
|
118
|
+
figures: new Map(scan.figures.filter((f) => f.id).map((f) => [f.id, f])),
|
|
119
|
+
},
|
|
120
|
+
icons: index?.icons,
|
|
121
|
+
images: index && { lookupAsset: index.lookupAsset },
|
|
122
|
+
});
|
|
123
|
+
let markdown = authored;
|
|
124
|
+
let figures = scan.figures;
|
|
125
|
+
if (indexed) {
|
|
126
|
+
const expanded = index.expand(authored, { fm: frontmatter, file });
|
|
127
|
+
markdown = expanded.markdown;
|
|
128
|
+
figures = expanded.figures;
|
|
129
|
+
}
|
|
130
|
+
const content = renderer.render(markdown, {
|
|
131
|
+
runs: index?.runs,
|
|
132
|
+
figures,
|
|
133
|
+
resolveSrc: index?.resolveSrc,
|
|
134
|
+
});
|
|
135
|
+
const profile = infobox ? localInfobox(frontmatter, renderer.md) : "";
|
|
608
136
|
return {
|
|
609
137
|
ok: true,
|
|
610
138
|
html: `<div class="hl-preview hl-preview-${dark ? "dark" : "light"}">${profile}<main class="hl-preview-main">${content}</main></div>`,
|
|
@@ -665,7 +193,6 @@ export function previewStyles() {
|
|
|
665
193
|
.hl-preview .alert-warning { --hl-accent:var(--hl-warning); --hl-alert-bg:var(--hl-warning-bg); }
|
|
666
194
|
.hl-preview .alert .alert-title { color:var(--hl-accent); font-weight:700; opacity:1; }
|
|
667
195
|
.hl-preview .alert .alert-icon { width:1em; height:1em; margin-right:.5em; vertical-align:-.1em; color:var(--hl-accent); fill:currentColor; }
|
|
668
|
-
.hl-preview
|
|
669
|
-
${Array.from({ length: 9 }, (_, n) => `.hl-preview .verse-line.i${n} { padding-left:${n * 2}em; }`).join("\n")}
|
|
196
|
+
${sharedContentRules(".hl-preview")}
|
|
670
197
|
`;
|
|
671
198
|
}
|