vantage-md 0.6.1 → 0.7.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 +211 -0
- package/README.md +3 -2
- package/dist/index.cjs +210 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1128 -95
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +1128 -95
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +205 -32
- package/dist/index.js.map +1 -1
- package/dist/prose.css +1 -1
- package/dist/react.cjs +159 -33
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +1021 -56
- package/dist/react.d.cts.map +1 -1
- package/dist/react.d.ts +1021 -56
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +159 -33
- package/dist/react.js.map +1 -1
- package/package.json +4 -2
package/dist/prose.css
CHANGED
|
@@ -141,7 +141,7 @@
|
|
|
141
141
|
`directives.css` draws a toned section's vertical rule, and clipping it breaks
|
|
142
142
|
the rule for the height of every code fence. See the note there beside
|
|
143
143
|
`[data-vantage-tone]:is(pre, hr)`. Keeping this unconditional rather than
|
|
144
|
-
scoping it to toned fences means one
|
|
144
|
+
scoping it to toned fences means one behavior for every fence, and no
|
|
145
145
|
specificity tie between the two stylesheets. */
|
|
146
146
|
.vantage-prose pre code {
|
|
147
147
|
background: none;
|
package/dist/react.cjs
CHANGED
|
@@ -121,14 +121,14 @@ const rehypeSourceLines = (options) => {
|
|
|
121
121
|
* `remark-gfm` does not implement alerts, so until this plugin existed a
|
|
122
122
|
* `> [!WARNING]` rendered as an ordinary blockquote with the literal marker
|
|
123
123
|
* visible as its first words. Worse than merely unstyled: `@tailwindcss/typography`
|
|
124
|
-
*
|
|
124
|
+
* italicizes blockquotes and draws `open-quote`/`close-quote` around the first
|
|
125
125
|
* paragraph, so a callout came out as an italic *quotation* whose opening words
|
|
126
126
|
* were `"[!WARNING]`. That was the "Known gaps" entry in
|
|
127
127
|
* `docs/reference/inline-markup.md` and OQ-10, filed rather than fixed, while
|
|
128
128
|
* `styleGuide.ts` went on telling every agent to write them.
|
|
129
129
|
*
|
|
130
130
|
* The tokens are deliberately the ones the `tone` vocabulary already resolves —
|
|
131
|
-
* an alert *is* the six-
|
|
131
|
+
* an alert *is* the six-color light/dark treatment `tone` shipped, which is
|
|
132
132
|
* exactly what the gap entry said whoever fixed this should do rather than
|
|
133
133
|
* building a second palette. `[!WARNING]` and `<!-- vantage: block tone=warning -->`
|
|
134
134
|
* therefore agree by construction, and adding a theme still touches one
|
|
@@ -142,7 +142,7 @@ const rehypeSourceLines = (options) => {
|
|
|
142
142
|
*
|
|
143
143
|
* ## What it does not do
|
|
144
144
|
*
|
|
145
|
-
* It does not touch a blockquote that carries no marker, and an
|
|
145
|
+
* It does not touch a blockquote that carries no marker, and an unrecognized
|
|
146
146
|
* marker (`[!HINT]`) is left exactly as it was — visible literal text, which is
|
|
147
147
|
* the honest rendering of something GitHub also would not style. Silently
|
|
148
148
|
* swallowing it would hide a typo that reads as a callout on neither renderer.
|
|
@@ -364,7 +364,7 @@ VANTAGE_ANCHOR_TARGETS.filter((tag) => tag !== "pre" && tag !== "table");
|
|
|
364
364
|
* file, and requiring it everywhere would fire on every single-doc sketch.
|
|
365
365
|
*
|
|
366
366
|
* Three consumers read it from here and none of them may re-spell it: the
|
|
367
|
-
* plugin that stamps the anchor, the
|
|
367
|
+
* plugin that stamps the anchor, the sanitizer that allowlists the value, and
|
|
368
368
|
* the checker's `vantage/oq-id-format`. A fourth copy is how the checker starts
|
|
369
369
|
* calling a working anchor malformed.
|
|
370
370
|
*/
|
|
@@ -522,7 +522,7 @@ const HEADING_DEPTHS = /* @__PURE__ */ new Map([
|
|
|
522
522
|
/**
|
|
523
523
|
* Key → hast property, for the keys that treat a whole run.
|
|
524
524
|
*
|
|
525
|
-
* A camelCase hast property
|
|
525
|
+
* A camelCase hast property serializes to the kebab-case attribute, so
|
|
526
526
|
* `dataVantageTone` is `data-vantage-tone` in every renderer.
|
|
527
527
|
*
|
|
528
528
|
* `tone` and `emphasis` describe what a section *is* and how loud it is, so
|
|
@@ -557,11 +557,11 @@ const LEANING_PROPERTY = "dataVantageLeaning";
|
|
|
557
557
|
* The id, carried as a `data-` attribute rather than written straight to `id`.
|
|
558
558
|
*
|
|
559
559
|
* This plugin runs *before* `rehypeSanitize` — it has to, it reads comments and
|
|
560
|
-
* the
|
|
560
|
+
* the sanitizer deletes them — and the sanitizer's default schema clobbers `id`
|
|
561
561
|
* with the prefix `user-content-`. A bare `id` set here would reach the page as
|
|
562
562
|
* `user-content-OQ-4`, every `#OQ-4` link in every document would land nowhere,
|
|
563
563
|
* and nothing would error. `rehypeVantageAnchors` promotes this to a real `id`
|
|
564
|
-
* on the other side of the
|
|
564
|
+
* on the other side of the sanitizer, which is the same reason `rehypeSlug` is
|
|
565
565
|
* registered there (`pipeline.ts`).
|
|
566
566
|
*/
|
|
567
567
|
const OQ_ID_PROPERTY = "dataVantageOqId";
|
|
@@ -593,7 +593,7 @@ const MAX_LEANING = 500;
|
|
|
593
593
|
* A whitespace-only `text` node always does — measured, with or without a blank
|
|
594
594
|
* line in the source. Comments do too, and an unrelated `<!-- TODO -->` must not
|
|
595
595
|
* break the chain: it is invisible in every renderer and deleted by the
|
|
596
|
-
*
|
|
596
|
+
* sanitizer, so letting it change a directive's meaning would make behavior
|
|
597
597
|
* depend on something no reader can see.
|
|
598
598
|
*/
|
|
599
599
|
function isSkippable(node) {
|
|
@@ -645,7 +645,7 @@ function accepts(name, key, value) {
|
|
|
645
645
|
* two stamped paragraphs — and the section's one continuous vertical rule is
|
|
646
646
|
* drawn per member, so an unstamped member is a hole the height of the block
|
|
647
647
|
* plus its margins. Measured over the real stylesheet: 44px for a one-line
|
|
648
|
-
* `<figure>`, against the 40px a
|
|
648
|
+
* `<figure>`, against the 40px a neighbor can bleed upward, and arbitrarily
|
|
649
649
|
* large for anything taller. `collapsed=true` had the same shape of bug the
|
|
650
650
|
* other way round — it hid the paragraphs and left the figure on the page.
|
|
651
651
|
*
|
|
@@ -827,7 +827,7 @@ function classNames(node) {
|
|
|
827
827
|
* A `<pre>` `rehype-katex` will replace — its own condition, restated.
|
|
828
828
|
*
|
|
829
829
|
* `language-math` is the only class to test: `rehype-katex` keys the
|
|
830
|
-
* pre-as-scope branch on it, and the
|
|
830
|
+
* pre-as-scope branch on it, and the sanitizer strips the `math-display` that
|
|
831
831
|
* `remark-math` also emits (measured — a stamped fence arrives here with
|
|
832
832
|
* `className: ["language-math"]` alone).
|
|
833
833
|
*/
|
|
@@ -981,7 +981,7 @@ const COLLAPSE_GROUP_ID = /^[0-9]+$/;
|
|
|
981
981
|
* `false` — comments are not elements, so `tagNames` has nothing to do with it.
|
|
982
982
|
* `rehypeVantageDirectives` relies on that deletion: it consumes a
|
|
983
983
|
* `<!-- vantage: … -->` comment into attributes and deliberately leaves the node
|
|
984
|
-
* for the
|
|
984
|
+
* for the sanitizer. Turning the switch on readmits every directive comment —
|
|
985
985
|
* valid and malformed alike — into the rendered HTML, which breaks the carrier's
|
|
986
986
|
* whole premise. `vantageDirectives.test.ts` ("leaves no comment in the rendered
|
|
987
987
|
* markup") is the guard.
|
|
@@ -1208,6 +1208,49 @@ function currentMermaidTheme() {
|
|
|
1208
1208
|
return typeof document !== "undefined" && document.documentElement.classList.contains("dark") ? "dark" : "default";
|
|
1209
1209
|
}
|
|
1210
1210
|
/**
|
|
1211
|
+
* The attribute on `<html>` naming the active color theme. Absent means the
|
|
1212
|
+
* built-in look. The app sets it only once the theme's stylesheet has loaded,
|
|
1213
|
+
* so a reader of this attribute can trust the theme's variables are in effect.
|
|
1214
|
+
*/
|
|
1215
|
+
const COLOR_THEME_ATTRIBUTE = "data-vantage-theme";
|
|
1216
|
+
/**
|
|
1217
|
+
* The attribute on `<html>` saying where the active theme came from: `"user"`
|
|
1218
|
+
* for a stylesheet in the reader's themes directory, `"built-in"` for one the
|
|
1219
|
+
* app ships. Set with {@link COLOR_THEME_ATTRIBUTE}, and absent with it.
|
|
1220
|
+
*
|
|
1221
|
+
* It exists because an id alone is not a palette. A user theme may share a
|
|
1222
|
+
* built-in's id — that is how a reader tweaks one — and the app applies the
|
|
1223
|
+
* stored built-in synchronously, then swaps in the same-id user file once
|
|
1224
|
+
* /api/themes answers. Keyed on the id, the diagrams drawn in between kept the
|
|
1225
|
+
* built-in's colors: the key did not change, so neither the cache nor
|
|
1226
|
+
* `useSyncExternalStore` saw a reason to redraw.
|
|
1227
|
+
*/
|
|
1228
|
+
const COLOR_THEME_SOURCE_ATTRIBUTE = "data-vantage-theme-source";
|
|
1229
|
+
/** The active color theme's id, or `""` for the built-in look. */
|
|
1230
|
+
function currentColorTheme() {
|
|
1231
|
+
if (typeof document === "undefined") return "";
|
|
1232
|
+
return document.documentElement.getAttribute("data-vantage-theme") ?? "";
|
|
1233
|
+
}
|
|
1234
|
+
/**
|
|
1235
|
+
* Everything a rendered diagram's colors depend on, as one string: the
|
|
1236
|
+
* light/dark mode, plus the color theme when one is active.
|
|
1237
|
+
*
|
|
1238
|
+
* A diagram is baked at render time, so anything that changes its colors has
|
|
1239
|
+
* to change this key — it is what the SVG cache and the loader's "configured
|
|
1240
|
+
* for" check compare. Under the built-in look it is exactly the mode name,
|
|
1241
|
+
* which is what both keyed on before color themes existed.
|
|
1242
|
+
*/
|
|
1243
|
+
function currentMermaidPalette() {
|
|
1244
|
+
const theme = currentColorTheme();
|
|
1245
|
+
if (!theme) return currentMermaidTheme();
|
|
1246
|
+
const user = document.documentElement.getAttribute(COLOR_THEME_SOURCE_ATTRIBUTE) === "user";
|
|
1247
|
+
return `${currentMermaidTheme()} ${theme}${user ? " user" : ""}`;
|
|
1248
|
+
}
|
|
1249
|
+
/** The mermaid theme name a palette key was built from. */
|
|
1250
|
+
function mermaidThemeOf(palette) {
|
|
1251
|
+
return palette.startsWith("dark") ? "dark" : "default";
|
|
1252
|
+
}
|
|
1253
|
+
/**
|
|
1211
1254
|
* Theme variables per theme. Mermaid derives most of its palette from these, so
|
|
1212
1255
|
* the set is deliberately small: the surfaces, the ink, and the lines.
|
|
1213
1256
|
*/
|
|
@@ -1231,27 +1274,108 @@ const THEME_VARIABLES = {
|
|
|
1231
1274
|
edgeLabelBackground: "#f8fafc"
|
|
1232
1275
|
}
|
|
1233
1276
|
};
|
|
1277
|
+
/**
|
|
1278
|
+
* Where each variable comes from under a color theme: the palette step the
|
|
1279
|
+
* built-in value was chosen from. The hex above IS that step in Tailwind's own
|
|
1280
|
+
* palette, so reading the step back out of the page gives the same diagram
|
|
1281
|
+
* under the built-in look and the theme's colors under any other.
|
|
1282
|
+
*/
|
|
1283
|
+
const THEME_SOURCES = {
|
|
1284
|
+
dark: {
|
|
1285
|
+
background: "--color-slate-800",
|
|
1286
|
+
mainBkg: "--color-slate-700",
|
|
1287
|
+
nodeBorder: "--color-slate-400",
|
|
1288
|
+
nodeTextColor: "--color-slate-100",
|
|
1289
|
+
lineColor: "--color-slate-400",
|
|
1290
|
+
textColor: "--color-slate-200",
|
|
1291
|
+
edgeLabelBackground: "--color-slate-800"
|
|
1292
|
+
},
|
|
1293
|
+
default: {
|
|
1294
|
+
background: "--color-slate-50",
|
|
1295
|
+
mainBkg: "--color-slate-100",
|
|
1296
|
+
nodeBorder: "--color-slate-500",
|
|
1297
|
+
nodeTextColor: "--color-slate-900",
|
|
1298
|
+
lineColor: "--color-slate-500",
|
|
1299
|
+
textColor: "--color-slate-800",
|
|
1300
|
+
edgeLabelBackground: "--color-slate-50"
|
|
1301
|
+
}
|
|
1302
|
+
};
|
|
1234
1303
|
function mermaidThemeVariables(theme) {
|
|
1235
|
-
|
|
1304
|
+
const fixed = THEME_VARIABLES[theme];
|
|
1305
|
+
if (!currentColorTheme()) return fixed;
|
|
1306
|
+
const out = {};
|
|
1307
|
+
for (const [key, hex] of Object.entries(fixed)) out[key] = resolveCssColor(THEME_SOURCES[theme][key]) ?? hex;
|
|
1308
|
+
return out;
|
|
1309
|
+
}
|
|
1310
|
+
/**
|
|
1311
|
+
* A custom property's color as `#rrggbb`, or `null` when it cannot be read.
|
|
1312
|
+
*
|
|
1313
|
+
* Mermaid wants hex at `initialize()` time, and a theme's value can be any CSS
|
|
1314
|
+
* color — `oklch()`, `color-mix()`, a `var()` of another variable. So the
|
|
1315
|
+
* browser does the work: a probe element resolves the property to a computed
|
|
1316
|
+
* color, and a 1×1 canvas turns that into sRGB bytes whatever syntax it came
|
|
1317
|
+
* back in. No canvas (jsdom, a locked-down embed) is `null`, and the caller
|
|
1318
|
+
* falls back to the built-in value.
|
|
1319
|
+
*
|
|
1320
|
+
* A property nobody declared is `null` too, and has to be checked for up
|
|
1321
|
+
* front: `color: var(--unset)` is invalid at computed-value time, so the probe
|
|
1322
|
+
* *inherits* its color instead — the page's text color, which the canvas
|
|
1323
|
+
* would dutifully turn into a plausible hex and mermaid would paint every node
|
|
1324
|
+
* box with.
|
|
1325
|
+
*/
|
|
1326
|
+
function resolveCssColor(property) {
|
|
1327
|
+
if (typeof document === "undefined") return null;
|
|
1328
|
+
if (!getComputedStyle(document.documentElement).getPropertyValue(property).trim()) return null;
|
|
1329
|
+
const probe = document.createElement("span");
|
|
1330
|
+
probe.style.display = "none";
|
|
1331
|
+
probe.style.color = `var(${property})`;
|
|
1332
|
+
document.documentElement.appendChild(probe);
|
|
1333
|
+
const computed = getComputedStyle(probe).color;
|
|
1334
|
+
probe.remove();
|
|
1335
|
+
if (!computed) return null;
|
|
1336
|
+
const ctx = pixelContext();
|
|
1337
|
+
if (!ctx) return null;
|
|
1338
|
+
ctx.fillStyle = computed;
|
|
1339
|
+
ctx.fillRect(0, 0, 1, 1);
|
|
1340
|
+
const [r, g, b] = ctx.getImageData(0, 0, 1, 1).data;
|
|
1341
|
+
return `#${[
|
|
1342
|
+
r,
|
|
1343
|
+
g,
|
|
1344
|
+
b
|
|
1345
|
+
].map((v) => v.toString(16).padStart(2, "0")).join("")}`;
|
|
1346
|
+
}
|
|
1347
|
+
/** A 1×1 2D context to read a color back from, or `null` where there is none. */
|
|
1348
|
+
function pixelContext() {
|
|
1349
|
+
try {
|
|
1350
|
+
const canvas = document.createElement("canvas");
|
|
1351
|
+
canvas.width = canvas.height = 1;
|
|
1352
|
+
return canvas.getContext("2d", { willReadFrequently: true });
|
|
1353
|
+
} catch {
|
|
1354
|
+
return null;
|
|
1355
|
+
}
|
|
1236
1356
|
}
|
|
1237
1357
|
//#endregion
|
|
1238
1358
|
//#region src/mermaidCache.ts
|
|
1239
1359
|
const svgCache = /* @__PURE__ */ new Map();
|
|
1240
|
-
const cacheKey = (code, theme) => `${theme}
|
|
1360
|
+
const cacheKey = (code, theme) => `${theme}\u0000${code}`;
|
|
1241
1361
|
/** The SVG for this fence in the theme the page is currently asking for. */
|
|
1242
|
-
function getCachedSvg(code, theme =
|
|
1362
|
+
function getCachedSvg(code, theme = currentMermaidPalette()) {
|
|
1243
1363
|
return svgCache.get(cacheKey(code, theme));
|
|
1244
1364
|
}
|
|
1245
|
-
function setCachedSvg(code, svg, theme =
|
|
1365
|
+
function setCachedSvg(code, svg, theme = currentMermaidPalette()) {
|
|
1246
1366
|
svgCache.set(cacheKey(code, theme), svg);
|
|
1247
1367
|
}
|
|
1248
1368
|
//#endregion
|
|
1249
1369
|
//#region src/mermaidLoader.ts
|
|
1250
1370
|
let mermaidInstance = null;
|
|
1251
1371
|
let mermaidLoading = null;
|
|
1252
|
-
/**
|
|
1372
|
+
/**
|
|
1373
|
+
* The palette key (`currentMermaidPalette`) the loaded instance was last
|
|
1374
|
+
* configured for, `null` until loaded.
|
|
1375
|
+
*/
|
|
1253
1376
|
let configuredTheme = null;
|
|
1254
|
-
function configure(m,
|
|
1377
|
+
function configure(m, palette) {
|
|
1378
|
+
const theme = mermaidThemeOf(palette);
|
|
1255
1379
|
m.initialize({
|
|
1256
1380
|
startOnLoad: false,
|
|
1257
1381
|
theme,
|
|
@@ -1259,7 +1383,7 @@ function configure(m, theme) {
|
|
|
1259
1383
|
securityLevel: "strict",
|
|
1260
1384
|
suppressErrorRendering: true
|
|
1261
1385
|
});
|
|
1262
|
-
configuredTheme =
|
|
1386
|
+
configuredTheme = palette;
|
|
1263
1387
|
}
|
|
1264
1388
|
/**
|
|
1265
1389
|
* The mermaid module, configured for the theme the page is asking for *now*.
|
|
@@ -1273,19 +1397,19 @@ function configure(m, theme) {
|
|
|
1273
1397
|
* served instead (`mermaidCache.ts`).
|
|
1274
1398
|
*/
|
|
1275
1399
|
async function getMermaid() {
|
|
1276
|
-
const theme =
|
|
1400
|
+
const theme = currentMermaidPalette();
|
|
1277
1401
|
if (mermaidInstance) {
|
|
1278
1402
|
if (configuredTheme !== theme) configure(mermaidInstance, theme);
|
|
1279
1403
|
return mermaidInstance;
|
|
1280
1404
|
}
|
|
1281
1405
|
if (!mermaidLoading) mermaidLoading = import("mermaid").then((mod) => {
|
|
1282
1406
|
const m = mod.default;
|
|
1283
|
-
configure(m,
|
|
1407
|
+
configure(m, currentMermaidPalette());
|
|
1284
1408
|
mermaidInstance = m;
|
|
1285
1409
|
return m;
|
|
1286
1410
|
});
|
|
1287
1411
|
const loaded = await mermaidLoading;
|
|
1288
|
-
const wanted =
|
|
1412
|
+
const wanted = currentMermaidPalette();
|
|
1289
1413
|
if (configuredTheme !== wanted) configure(loaded, wanted);
|
|
1290
1414
|
return loaded;
|
|
1291
1415
|
}
|
|
@@ -1588,7 +1712,7 @@ function DiagramModal({ isOpen, onClose, children }) {
|
|
|
1588
1712
|
}
|
|
1589
1713
|
/**
|
|
1590
1714
|
* The palette the page is asking for, re-read whenever `<html>`'s class list
|
|
1591
|
-
* changes.
|
|
1715
|
+
* or its color-theme attribute changes.
|
|
1592
1716
|
*
|
|
1593
1717
|
* A diagram is an SVG baked at render time, so unlike everything else on the
|
|
1594
1718
|
* page it does not restyle when the theme flips — it has to be drawn again. The
|
|
@@ -1605,14 +1729,19 @@ function subscribeToTheme(onChange) {
|
|
|
1605
1729
|
const observer = new MutationObserver(onChange);
|
|
1606
1730
|
observer.observe(document.documentElement, {
|
|
1607
1731
|
attributes: true,
|
|
1608
|
-
attributeFilter: [
|
|
1732
|
+
attributeFilter: [
|
|
1733
|
+
"class",
|
|
1734
|
+
COLOR_THEME_ATTRIBUTE,
|
|
1735
|
+
COLOR_THEME_SOURCE_ATTRIBUTE
|
|
1736
|
+
]
|
|
1609
1737
|
});
|
|
1610
1738
|
return () => observer.disconnect();
|
|
1611
1739
|
}
|
|
1612
1740
|
/** Server render has no `<html>` to read, and no diagram to draw either. */
|
|
1613
1741
|
const serverTheme = () => "default";
|
|
1742
|
+
/** The palette key the page is asking for (see `currentMermaidPalette`). */
|
|
1614
1743
|
function useMermaidTheme() {
|
|
1615
|
-
return (0, react.useSyncExternalStore)(subscribeToTheme,
|
|
1744
|
+
return (0, react.useSyncExternalStore)(subscribeToTheme, currentMermaidPalette, serverTheme);
|
|
1616
1745
|
}
|
|
1617
1746
|
const MermaidDiagramInner = ({ code }) => {
|
|
1618
1747
|
const theme = useMermaidTheme();
|
|
@@ -1754,7 +1883,7 @@ const DOC_STATUSES = [
|
|
|
1754
1883
|
/** Every key this build knows under `vantage:`. Closed. */
|
|
1755
1884
|
const VANTAGE_FRONTMATTER_KEYS = ["status-chip"];
|
|
1756
1885
|
/**
|
|
1757
|
-
* Which tone each status borrows its
|
|
1886
|
+
* Which tone each status borrows its colors from.
|
|
1758
1887
|
*
|
|
1759
1888
|
* The chip has no palette of its own: it reuses the tone chips
|
|
1760
1889
|
* (`.vantage-chip--<tone>` in `styles/directives.css`), which is also what makes
|
|
@@ -1873,7 +2002,7 @@ function readStatusChip(frontmatter, raw, issues) {
|
|
|
1873
2002
|
*
|
|
1874
2003
|
* Styling comes entirely from `styles/directives.css` — `.vantage-chip` for the
|
|
1875
2004
|
* geometry, shared by selector list with the `badge=` pseudo-element, and
|
|
1876
|
-
* `.vantage-chip--<tone>` for the
|
|
2005
|
+
* `.vantage-chip--<tone>` for the colors. No Tailwind utility appears here, so
|
|
1877
2006
|
* the chip survives in a consumer that does not run Tailwind and no class name
|
|
1878
2007
|
* has to be discovered by a content scan.
|
|
1879
2008
|
*/
|
|
@@ -1988,7 +2117,7 @@ const FrontmatterDisplay = (0, react.memo)(FrontmatterDisplayInner);
|
|
|
1988
2117
|
*
|
|
1989
2118
|
* Split out from scrollToLineAnchor.ts so that non-browser consumers — the
|
|
1990
2119
|
* `vantage-check` CLI, which validates `#L42` links against the file on disk —
|
|
1991
|
-
* can share the *same* syntax the viewer
|
|
2120
|
+
* can share the *same* syntax the viewer honors instead of reimplementing it
|
|
1992
2121
|
* and drifting.
|
|
1993
2122
|
*/
|
|
1994
2123
|
/**
|
|
@@ -2305,10 +2434,6 @@ const MarkdownViewer = (0, react.memo)(MarkdownViewerInner, (prevProps, nextProp
|
|
|
2305
2434
|
//#endregion
|
|
2306
2435
|
//#region src/renderMarkdown.ts
|
|
2307
2436
|
/**
|
|
2308
|
-
* Framework-agnostic markdown -> HTML rendering pipeline.
|
|
2309
|
-
* Uses the same remark/rehype chain as the Vantage viewer.
|
|
2310
|
-
*/
|
|
2311
|
-
/**
|
|
2312
2437
|
* Render a markdown string to HTML using the full Vantage pipeline.
|
|
2313
2438
|
*
|
|
2314
2439
|
* Features (all enabled by default):
|
|
@@ -2325,7 +2450,7 @@ const MarkdownViewer = (0, react.memo)(MarkdownViewerInner, (prevProps, nextProp
|
|
|
2325
2450
|
* Use the React `<MarkdownViewer>` component for client-side mermaid rendering.
|
|
2326
2451
|
*/
|
|
2327
2452
|
async function renderMarkdown(content, options = {}) {
|
|
2328
|
-
const { gfm = true, math = true, highlight = true, sourceLines = true, sanitize = true, frontmatter: parseFm = true } = options;
|
|
2453
|
+
const { gfm = true, math = true, highlight = true, sourceLines = true, sanitize = true, frontmatter: parseFm = true, tree } = options;
|
|
2329
2454
|
let parsed;
|
|
2330
2455
|
if (parseFm) parsed = parseFrontmatter(content);
|
|
2331
2456
|
else parsed = {
|
|
@@ -2342,7 +2467,8 @@ async function renderMarkdown(content, options = {}) {
|
|
|
2342
2467
|
sanitize,
|
|
2343
2468
|
bodyLineOffset: parsed.bodyLineOffset
|
|
2344
2469
|
});
|
|
2345
|
-
const
|
|
2470
|
+
const processor = (0, unified.unified)().use(remark_parse.default).use(remarkPlugins).use(remark_rehype.default, { allowDangerousHtml: true }).use(rehypePlugins).use(rehype_stringify.default);
|
|
2471
|
+
const result = tree === void 0 ? String(await processor.process(parsed.body)) : processor.stringify(await processor.run(tree, parsed.body), parsed.body);
|
|
2346
2472
|
return {
|
|
2347
2473
|
html: String(result),
|
|
2348
2474
|
frontmatter: parsed.frontmatter,
|