md2x 0.2.2 → 0.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.
package/dist/index.mjs CHANGED
@@ -53070,10 +53070,10 @@ var init_lib4 = __esm({
53070
53070
  * @returns {undefined}
53071
53071
  * Nothing.
53072
53072
  */
53073
- set basename(basename) {
53074
- assertNonEmpty(basename, "basename");
53075
- assertPart(basename, "basename");
53076
- this.path = default2.join(this.dirname || "", basename);
53073
+ set basename(basename2) {
53074
+ assertNonEmpty(basename2, "basename");
53075
+ assertPart(basename2, "basename");
53076
+ this.path = default2.join(this.dirname || "", basename2);
53077
53077
  }
53078
53078
  /**
53079
53079
  * Get the parent path (example: `'~'`).
@@ -53119,18 +53119,18 @@ var init_lib4 = __esm({
53119
53119
  * @returns {undefined}
53120
53120
  * Nothing.
53121
53121
  */
53122
- set extname(extname) {
53123
- assertPart(extname, "extname");
53122
+ set extname(extname2) {
53123
+ assertPart(extname2, "extname");
53124
53124
  assertPath(this.dirname, "extname");
53125
- if (extname) {
53126
- if (extname.codePointAt(0) !== 46) {
53125
+ if (extname2) {
53126
+ if (extname2.codePointAt(0) !== 46) {
53127
53127
  throw new Error("`extname` must start with `.`");
53128
53128
  }
53129
- if (extname.includes(".", 1)) {
53129
+ if (extname2.includes(".", 1)) {
53130
53130
  throw new Error("`extname` cannot contain multiple dots");
53131
53131
  }
53132
53132
  }
53133
- this.path = default2.join(this.dirname, this.stem + (extname || ""));
53133
+ this.path = default2.join(this.dirname, this.stem + (extname2 || ""));
53134
53134
  }
53135
53135
  /**
53136
53136
  * Get the full path (example: `'~/index.min.js'`).
@@ -127486,6 +127486,7 @@ var init_theme_to_css = __esm({
127486
127486
  var node_exporter_exports = {};
127487
127487
  __export(node_exporter_exports, {
127488
127488
  NodeDocxExporter: () => NodeDocxExporter,
127489
+ NodeHtmlExporter: () => NodeHtmlExporter,
127489
127490
  NodePdfExporter: () => NodePdfExporter,
127490
127491
  default: () => node_exporter_default
127491
127492
  });
@@ -127524,6 +127525,94 @@ function createPluginRenderer(browserRenderer, basePath, themeConfig) {
127524
127525
  }
127525
127526
  };
127526
127527
  }
127528
+ function escapeHtmlText(text9) {
127529
+ return String(text9).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
127530
+ }
127531
+ function decodeHtmlEntities3(text9) {
127532
+ return text9.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&#x27;/g, "'").replace(/&#x2F;/g, "/").replace(/&#x3C;/gi, "<").replace(/&#x3E;/gi, ">").replace(/&#x26;/gi, "&").replace(/&#60;/g, "<").replace(/&#62;/g, ">").replace(/&#38;/g, "&");
127533
+ }
127534
+ async function processDiagrams(html7, browserRenderer, basePath, themeConfig, mode) {
127535
+ if (mode !== "img") return html7;
127536
+ if (!browserRenderer) return html7;
127537
+ const pluginLangs = plugins.filter((p3) => p3.nodeSelector.includes("code")).map((p3) => p3.language).filter((lang) => lang !== null);
127538
+ const aliases = ["graphviz", "gv", "vegalite"];
127539
+ const supportedLangs = [...pluginLangs, ...aliases].join("|");
127540
+ if (!supportedLangs) return html7;
127541
+ const codeBlockRegex = new RegExp(
127542
+ `<pre><code class="(?:hljs )?language-(${supportedLangs})">([\\s\\S]*?)<\\/code><\\/pre>`,
127543
+ "gi"
127544
+ );
127545
+ const matches = [...html7.matchAll(codeBlockRegex)];
127546
+ for (const match of matches) {
127547
+ const [fullMatch, lang, code4] = match;
127548
+ const decodedCode = decodeHtmlEntities3(code4);
127549
+ let renderType = lang.toLowerCase();
127550
+ if (renderType === "graphviz" || renderType === "gv") renderType = "dot";
127551
+ if (renderType === "vegalite") renderType = "vega-lite";
127552
+ try {
127553
+ const result = await browserRenderer.render(renderType, decodedCode, basePath, themeConfig);
127554
+ if (result && result.base64) {
127555
+ const imgTag = `<div class="md2x-diagram"><img class="md2x-diagram" src="data:image/${result.format};base64,${result.base64}" alt="${escapeHtmlText(
127556
+ `${lang} diagram`
127557
+ )}" style="max-width: 100%;" /></div>`;
127558
+ html7 = html7.replace(fullMatch, imgTag);
127559
+ }
127560
+ } catch (e) {
127561
+ console.warn(`Failed to render ${lang} diagram:`, e);
127562
+ }
127563
+ }
127564
+ return html7;
127565
+ }
127566
+ async function markdownToHtmlFragment(markdown2, browserRenderer, basePath, themeConfig, diagramMode) {
127567
+ const { unified: unified2 } = await Promise.resolve().then(() => (init_unified(), unified_exports));
127568
+ const remarkParse2 = (await Promise.resolve().then(() => (init_remark_parse(), remark_parse_exports))).default;
127569
+ const remarkGfm2 = (await Promise.resolve().then(() => (init_remark_gfm(), remark_gfm_exports))).default;
127570
+ const remarkMath2 = (await Promise.resolve().then(() => (init_remark_math(), remark_math_exports))).default;
127571
+ const remarkSuperSub2 = (await Promise.resolve().then(() => (init_remark_super_sub(), remark_super_sub_exports))).default;
127572
+ const remarkRehype2 = (await Promise.resolve().then(() => (init_remark_rehype(), remark_rehype_exports))).default;
127573
+ const rehypeKatex2 = (await Promise.resolve().then(() => (init_rehype_katex(), rehype_katex_exports))).default;
127574
+ const rehypeHighlight2 = (await Promise.resolve().then(() => (init_rehype_highlight(), rehype_highlight_exports))).default;
127575
+ const rehypeStringify2 = (await Promise.resolve().then(() => (init_rehype_stringify(), rehype_stringify_exports))).default;
127576
+ const { visit: visit2 } = await Promise.resolve().then(() => (init_unist_util_visit(), unist_util_visit_exports));
127577
+ function rehypeBlockImages() {
127578
+ return (tree) => {
127579
+ visit2(tree, "element", (node2) => {
127580
+ if (node2.tagName !== "p") return;
127581
+ const children = node2.children || [];
127582
+ if (children.length === 0) return;
127583
+ let imageCount = 0;
127584
+ let imageIndex = -1;
127585
+ let hasSubstantialTextAfterImage = false;
127586
+ let foundImage = false;
127587
+ for (let i = 0; i < children.length; i++) {
127588
+ const child = children[i];
127589
+ if (child.type === "element" && child.tagName === "img") {
127590
+ imageCount++;
127591
+ imageIndex = i;
127592
+ foundImage = true;
127593
+ } else if (foundImage) {
127594
+ if (child.type === "text" && child.value.trim() !== "") {
127595
+ hasSubstantialTextAfterImage = true;
127596
+ } else if (child.type === "element" && child.tagName !== "br") {
127597
+ hasSubstantialTextAfterImage = true;
127598
+ }
127599
+ }
127600
+ }
127601
+ if (imageCount === 1 && !hasSubstantialTextAfterImage && imageIndex >= 0) {
127602
+ const img = children[imageIndex];
127603
+ img.properties = img.properties || {};
127604
+ const existingClass = img.properties.className || [];
127605
+ img.properties.className = Array.isArray(existingClass) ? [...existingClass, "block-image"] : [existingClass, "block-image"];
127606
+ }
127607
+ });
127608
+ };
127609
+ }
127610
+ const processor = unified2().use(remarkParse2).use(remarkGfm2, { singleTilde: false }).use(remarkMath2).use(remarkSuperSub2).use(remarkRehype2, { allowDangerousHtml: true }).use(rehypeKatex2).use(rehypeHighlight2).use(rehypeBlockImages).use(rehypeStringify2, { allowDangerousHtml: true });
127611
+ const file = await processor.process(markdown2);
127612
+ let html7 = String(file);
127613
+ html7 = await processDiagrams(html7, browserRenderer, basePath, themeConfig, diagramMode);
127614
+ return html7;
127615
+ }
127527
127616
  async function loadThemeCss(themeId) {
127528
127617
  const platform = globalThis.platform;
127529
127618
  const themeManager2 = (await Promise.resolve().then(() => (init_theme_manager(), theme_manager_exports))).default;
@@ -127725,6 +127814,31 @@ img {
127725
127814
  page-break-inside: avoid;
127726
127815
  }
127727
127816
 
127817
+ .md2x-diagram .md2x-diagram-inner {
127818
+ display: inline-block;
127819
+ max-width: 100%;
127820
+ text-align: left;
127821
+ }
127822
+
127823
+ .md2x-diagram .md2x-diagram-mount {
127824
+ display: inline-block;
127825
+ max-width: 100%;
127826
+ }
127827
+
127828
+ .md2x-diagram .vega-embed {
127829
+ display: inline-block;
127830
+ max-width: 100%;
127831
+ width: auto !important;
127832
+ }
127833
+
127834
+ .md2x-diagram .md2x-diagram-inner svg,
127835
+ .md2x-diagram .md2x-diagram-inner > svg {
127836
+ display: block;
127837
+ margin-left: auto;
127838
+ margin-right: auto;
127839
+ max-width: 100%;
127840
+ }
127841
+
127728
127842
  .md2x-diagram img,
127729
127843
  img.md2x-diagram {
127730
127844
  display: block;
@@ -127877,7 +127991,7 @@ function loadKatexCss() {
127877
127991
  css2 = css2.replace(/url\((['"]?)(?:\.\/)?fonts\//g, `url($1${katexFontsHref}`);
127878
127992
  return css2;
127879
127993
  }
127880
- var NodeDocxExporter, NodePdfExporter, node_exporter_default;
127994
+ var NodeDocxExporter, NodePdfExporter, NodeHtmlExporter, node_exporter_default;
127881
127995
  var init_node_exporter = __esm({
127882
127996
  "src/host/node-exporter.ts"() {
127883
127997
  "use strict";
@@ -128002,7 +128116,7 @@ var init_node_exporter = __esm({
128002
128116
  }
128003
128117
  await browserRenderer.initialize();
128004
128118
  const themeConfig = await loadRendererThemeConfig(themeId);
128005
- const html7 = await this.processMarkdownToHtml(markdown2, browserRenderer, basePath, themeConfig);
128119
+ const html7 = await markdownToHtmlFragment(markdown2, browserRenderer, basePath, themeConfig, "img");
128006
128120
  let katexCss = "";
128007
128121
  try {
128008
128122
  katexCss = loadKatexCss();
@@ -128045,94 +128159,426 @@ var init_node_exporter = __esm({
128045
128159
  }
128046
128160
  fs3.writeFileSync(outputPath, buffer2);
128047
128161
  }
128162
+ };
128163
+ NodeHtmlExporter = class {
128048
128164
  /**
128049
- * Process markdown to HTML with diagram rendering
128165
+ * Export markdown to standalone HTML string (default) or HTML fragment.
128050
128166
  */
128051
- async processMarkdownToHtml(markdown2, browserRenderer, basePath, themeConfig) {
128052
- const { unified: unified2 } = await Promise.resolve().then(() => (init_unified(), unified_exports));
128053
- const remarkParse2 = (await Promise.resolve().then(() => (init_remark_parse(), remark_parse_exports))).default;
128054
- const remarkGfm2 = (await Promise.resolve().then(() => (init_remark_gfm(), remark_gfm_exports))).default;
128055
- const remarkMath2 = (await Promise.resolve().then(() => (init_remark_math(), remark_math_exports))).default;
128056
- const remarkSuperSub2 = (await Promise.resolve().then(() => (init_remark_super_sub(), remark_super_sub_exports))).default;
128057
- const remarkRehype2 = (await Promise.resolve().then(() => (init_remark_rehype(), remark_rehype_exports))).default;
128058
- const rehypeKatex2 = (await Promise.resolve().then(() => (init_rehype_katex(), rehype_katex_exports))).default;
128059
- const rehypeHighlight2 = (await Promise.resolve().then(() => (init_rehype_highlight(), rehype_highlight_exports))).default;
128060
- const rehypeStringify2 = (await Promise.resolve().then(() => (init_rehype_stringify(), rehype_stringify_exports))).default;
128061
- const { visit: visit2 } = await Promise.resolve().then(() => (init_unist_util_visit(), unist_util_visit_exports));
128062
- function rehypeBlockImages() {
128063
- return (tree) => {
128064
- visit2(tree, "element", (node2) => {
128065
- if (node2.tagName !== "p") return;
128066
- const children = node2.children || [];
128067
- if (children.length === 0) return;
128068
- let imageCount = 0;
128069
- let imageIndex = -1;
128070
- let hasSubstantialTextAfterImage = false;
128071
- let foundImage = false;
128072
- for (let i = 0; i < children.length; i++) {
128073
- const child = children[i];
128074
- if (child.type === "element" && child.tagName === "img") {
128075
- imageCount++;
128076
- imageIndex = i;
128077
- foundImage = true;
128078
- } else if (foundImage) {
128079
- if (child.type === "text" && child.value.trim() !== "") {
128080
- hasSubstantialTextAfterImage = true;
128081
- } else if (child.type === "element" && child.tagName !== "br") {
128082
- hasSubstantialTextAfterImage = true;
128083
- }
128084
- }
128085
- }
128086
- if (imageCount === 1 && !hasSubstantialTextAfterImage && imageIndex >= 0) {
128087
- const img = children[imageIndex];
128088
- img.properties = img.properties || {};
128089
- const existingClass = img.properties.className || [];
128090
- img.properties.className = Array.isArray(existingClass) ? [...existingClass, "block-image"] : [existingClass, "block-image"];
128091
- }
128092
- });
128167
+ async exportToString(markdown2, options = {}) {
128168
+ ensureBase64Globals();
128169
+ const themeId = options.theme || "default";
128170
+ const basePath = options.basePath || process.cwd();
128171
+ const moduleDir = path5.dirname(fileURLToPath3(import.meta.url));
128172
+ const diagramMode = options.diagramMode || "live";
128173
+ const { platform } = createNodePlatform({
128174
+ moduleDir,
128175
+ selectedThemeId: themeId,
128176
+ output: { kind: "buffer" }
128177
+ });
128178
+ const previousPlatform = globalThis.platform;
128179
+ globalThis.platform = platform;
128180
+ let browserRenderer = null;
128181
+ try {
128182
+ const themeConfig = await loadRendererThemeConfig(themeId);
128183
+ if (diagramMode === "img") {
128184
+ browserRenderer = await createBrowserRenderer();
128185
+ if (browserRenderer) {
128186
+ await browserRenderer.initialize();
128187
+ }
128188
+ }
128189
+ const fragment = await markdownToHtmlFragment(markdown2, browserRenderer, basePath, themeConfig, diagramMode);
128190
+ const standalone = options.standalone !== false;
128191
+ if (!standalone) return fragment;
128192
+ let katexCss = "";
128193
+ try {
128194
+ katexCss = loadKatexCss();
128195
+ } catch (e) {
128196
+ console.warn("Failed to load KaTeX CSS for HTML export:", e);
128197
+ }
128198
+ const baseCss = await loadBaseCss(options.htmlHrAsPageBreak ?? false);
128199
+ let themeCss = "";
128200
+ try {
128201
+ themeCss = await loadThemeCss(themeId);
128202
+ } catch (e) {
128203
+ console.warn("Failed to load theme CSS for HTML export, using base styles only:", e);
128204
+ }
128205
+ const css2 = katexCss + "\n" + baseCss + "\n" + themeCss;
128206
+ const title = options.title || "Document";
128207
+ const shouldEmitBase = options.baseTag !== false && !!basePath;
128208
+ const baseHref = shouldEmitBase ? pathToFileURL3(basePath + path5.sep).href : "";
128209
+ const baseTag = baseHref ? ` <base href="${escapeHtmlText(baseHref)}" />
128210
+ ` : "";
128211
+ const cdnBaseDefaults = {
128212
+ mermaid: "https://cdn.jsdelivr.net/npm/mermaid@11.12.2/dist/mermaid.min.js",
128213
+ // Preferred: modern Graphviz WASM build (provides window.Viz.instance()).
128214
+ vizGlobal: "https://cdn.jsdelivr.net/npm/@viz-js/viz@3.24.0/dist/viz-global.js",
128215
+ // Legacy fallback (provides window.Viz constructor).
128216
+ viz: "https://cdn.jsdelivr.net/npm/viz.js@2.1.2/viz.js",
128217
+ vizRender: "https://cdn.jsdelivr.net/npm/viz.js@2.1.2/full.render.js",
128218
+ infographic: "https://cdn.jsdelivr.net/npm/@antv/infographic@0.2.7/dist/infographic.min.js"
128219
+ };
128220
+ const cdnVegaDefaultsByMajor = {
128221
+ // Vega-Lite v5 is typically used with Vega v5 + Vega-Embed v6.
128222
+ 5: {
128223
+ vega: "https://cdn.jsdelivr.net/npm/vega@5/build/vega.min.js",
128224
+ vegaLite: "https://cdn.jsdelivr.net/npm/vega-lite@5/build/vega-lite.min.js",
128225
+ vegaEmbed: "https://cdn.jsdelivr.net/npm/vega-embed@6/build/vega-embed.min.js"
128226
+ },
128227
+ // Vega-Lite v6 is typically used with Vega v6 + Vega-Embed v7.
128228
+ 6: {
128229
+ vega: "https://cdn.jsdelivr.net/npm/vega@6/build/vega.min.js",
128230
+ vegaLite: "https://cdn.jsdelivr.net/npm/vega-lite@6/build/vega-lite.min.js",
128231
+ vegaEmbed: "https://cdn.jsdelivr.net/npm/vega-embed@7/build/vega-embed.min.js"
128232
+ }
128093
128233
  };
128234
+ const cdnOverrides = options.cdn ?? {};
128235
+ const liveBootstrap = diagramMode !== "live" ? "" : `
128236
+ <!-- md2x live diagram renderer (CDN) -->
128237
+ <script>
128238
+ (function () {
128239
+ const themeConfig = ${JSON.stringify(themeConfig ?? null)};
128240
+ const baseHref = ${JSON.stringify(baseHref)};
128241
+ const cdnOverrides = ${JSON.stringify(cdnOverrides)};
128242
+ const cdnBaseDefaults = ${JSON.stringify(cdnBaseDefaults)};
128243
+ const cdnVegaDefaultsByMajor = ${JSON.stringify(cdnVegaDefaultsByMajor)};
128244
+
128245
+ function loadScript(src) {
128246
+ return new Promise((resolve, reject) => {
128247
+ const s = document.createElement('script');
128248
+ s.src = src;
128249
+ s.async = false;
128250
+ s.onload = () => resolve();
128251
+ s.onerror = () => reject(new Error('Failed to load script: ' + src));
128252
+ document.head.appendChild(s);
128253
+ });
128254
+ }
128255
+
128256
+ function getLangFromCodeClass(codeEl) {
128257
+ const cls = (codeEl && codeEl.className) ? String(codeEl.className) : '';
128258
+ const m = cls.match(/\\blanguage-([a-z0-9-]+)\\b/i);
128259
+ return m ? m[1] : '';
128260
+ }
128261
+
128262
+ function normalizeLang(lang) {
128263
+ const l = String(lang || '').toLowerCase();
128264
+ if (l === 'graphviz' || l === 'gv') return 'dot';
128265
+ if (l === 'vegalite') return 'vega-lite';
128266
+ return l;
128267
+ }
128268
+
128269
+ function guessVegaLiteSchemaMajorFromSpec(spec) {
128270
+ if (!spec || typeof spec !== 'object') return null;
128271
+ const schema = spec.$schema;
128272
+ if (typeof schema !== 'string') return null;
128273
+ const m = schema.match(/\\/vega-lite\\/v(\\d+)(?:\\.|\\.json|$)/i) || schema.match(/\\/v(\\d+)\\.json$/i);
128274
+ if (!m) return null;
128275
+ const major = parseInt(m[1], 10);
128276
+ return Number.isFinite(major) ? major : null;
128277
+ }
128278
+
128279
+ function guessVegaLiteSchemaMajorFromText(text) {
128280
+ const t = String(text || '');
128281
+ const m = t.match(/\\/vega-lite\\/v(\\d+)(?:\\.|\\.json|$)/i) || t.match(/\\/v(\\d+)\\.json$/i);
128282
+ if (!m) return null;
128283
+ const major = parseInt(m[1], 10);
128284
+ return Number.isFinite(major) ? major : null;
128285
+ }
128286
+
128287
+ function detectVegaLiteMajorFromDocument() {
128288
+ // Scan vega-lite blocks and pick a major version based on $schema.
128289
+ const blocks = Array.from(document.querySelectorAll('pre > code'));
128290
+ let detected = null;
128291
+ for (const codeEl of blocks) {
128292
+ const langRaw = getLangFromCodeClass(codeEl);
128293
+ const lang = normalizeLang(langRaw);
128294
+ if (lang !== 'vega-lite') continue;
128295
+
128296
+ const text = (codeEl && codeEl.textContent) ? codeEl.textContent : '';
128297
+ if (!text.trim()) continue;
128298
+
128299
+ // Prefer a regex scan first so we can still detect schema versions for
128300
+ // code blocks that aren't strictly valid JSON (e.g. trailing commas).
128301
+ const majorFromText = guessVegaLiteSchemaMajorFromText(text);
128302
+ if (majorFromText && (majorFromText === 5 || majorFromText === 6)) {
128303
+ detected = majorFromText;
128304
+ if (majorFromText === 6) return 6;
128305
+ continue;
128306
+ }
128307
+
128308
+ try {
128309
+ const spec = JSON.parse(text);
128310
+ const major = guessVegaLiteSchemaMajorFromSpec(spec);
128311
+ if (major && (major === 5 || major === 6)) {
128312
+ detected = major;
128313
+ // If any block is v6, prefer v6. Otherwise keep v5.
128314
+ if (major === 6) return 6;
128315
+ }
128316
+ } catch {
128317
+ // ignore parse errors
128094
128318
  }
128095
- const processor = unified2().use(remarkParse2).use(remarkGfm2, { singleTilde: false }).use(remarkMath2).use(remarkSuperSub2).use(remarkRehype2, { allowDangerousHtml: true }).use(rehypeKatex2).use(rehypeHighlight2).use(rehypeBlockImages).use(rehypeStringify2, { allowDangerousHtml: true });
128096
- const file = await processor.process(markdown2);
128097
- let html7 = String(file);
128098
- html7 = await this.processDiagrams(html7, browserRenderer, basePath, themeConfig);
128099
- return html7;
128100
128319
  }
128101
- /**
128102
- * Process diagram code blocks and replace with rendered images
128103
- */
128104
- async processDiagrams(html7, browserRenderer, basePath, themeConfig) {
128105
- const pluginLangs = plugins.filter((p3) => p3.nodeSelector.includes("code")).map((p3) => p3.language).filter((lang) => lang !== null);
128106
- const aliases = ["graphviz", "gv", "vegalite"];
128107
- const supportedLangs = [...pluginLangs, ...aliases].join("|");
128108
- const codeBlockRegex = new RegExp(
128109
- `<pre><code class="(?:hljs )?language-(${supportedLangs})">([\\s\\S]*?)<\\/code><\\/pre>`,
128110
- "gi"
128111
- );
128112
- const matches = [...html7.matchAll(codeBlockRegex)];
128113
- for (const match of matches) {
128114
- const [fullMatch, lang, code4] = match;
128115
- const decodedCode = this.decodeHtmlEntities(code4);
128116
- let renderType = lang.toLowerCase();
128117
- if (renderType === "graphviz" || renderType === "gv") renderType = "dot";
128118
- if (renderType === "vegalite") renderType = "vega-lite";
128320
+ return detected || 6;
128321
+ }
128322
+
128323
+ async function ensureCdnLibsLoaded() {
128324
+ const major = detectVegaLiteMajorFromDocument();
128325
+ const vegaDefaults = cdnVegaDefaultsByMajor[String(major)] || cdnVegaDefaultsByMajor['6'];
128326
+ const cdn = Object.assign({}, cdnBaseDefaults, vegaDefaults, cdnOverrides || {});
128327
+
128328
+ // Load order matters.
128329
+ await loadScript(cdn.mermaid);
128330
+ if (cdn.vizGlobal) {
128331
+ await loadScript(cdn.vizGlobal);
128332
+ } else {
128333
+ await loadScript(cdn.viz);
128334
+ await loadScript(cdn.vizRender);
128335
+ }
128336
+ await loadScript(cdn.vega);
128337
+ await loadScript(cdn.vegaLite);
128338
+ await loadScript(cdn.vegaEmbed);
128339
+ await loadScript(cdn.infographic);
128340
+ }
128341
+
128342
+ function replacePreWithContainer(preEl, kind) {
128343
+ const wrapper = document.createElement('div');
128344
+ wrapper.className = 'md2x-diagram';
128345
+ wrapper.setAttribute('data-md2x-diagram-kind', kind);
128346
+ wrapper.style.maxWidth = '100%';
128347
+ const inner = document.createElement('div');
128348
+ inner.className = 'md2x-diagram-inner';
128349
+ inner.style.display = 'inline-block';
128350
+ inner.style.maxWidth = '100%';
128351
+ const mount = document.createElement('div');
128352
+ mount.className = 'md2x-diagram-mount';
128353
+ mount.style.maxWidth = '100%';
128354
+ inner.appendChild(mount);
128355
+ wrapper.appendChild(inner);
128356
+ preEl.replaceWith(wrapper);
128357
+ return mount;
128358
+ }
128359
+
128360
+ function getText(codeEl) {
128361
+ return (codeEl && codeEl.textContent) ? codeEl.textContent : '';
128362
+ }
128363
+
128364
+ async function renderMermaid(code, mount, id) {
128365
+ const mermaid = window.mermaid;
128366
+ if (!mermaid) return;
128367
+ mermaid.initialize({
128368
+ startOnLoad: false,
128369
+ theme: 'default',
128370
+ securityLevel: 'loose',
128371
+ fontFamily: themeConfig && themeConfig.fontFamily ? themeConfig.fontFamily : undefined,
128372
+ });
128373
+ const out = await mermaid.render('md2x-mermaid-' + id, code);
128374
+ mount.innerHTML = out && out.svg ? out.svg : '';
128375
+ }
128376
+
128377
+ async function renderDot(code, mount) {
128378
+ const VizGlobal = window.Viz;
128379
+ // Preferred: @viz-js/viz global build
128380
+ if (VizGlobal && typeof VizGlobal.instance === 'function') {
128381
+ const viz = await VizGlobal.instance();
128382
+ const svgEl = viz.renderSVGElement(code, { graphAttributes: { bgcolor: 'transparent' } });
128383
+ mount.appendChild(svgEl);
128384
+ return;
128385
+ }
128386
+
128387
+ // Legacy: viz.js
128388
+ if (typeof window.Viz === 'function') {
128389
+ const viz = new window.Viz();
128390
+ const svgEl = await viz.renderSVGElement(code);
128391
+ mount.appendChild(svgEl);
128392
+ }
128393
+ }
128394
+
128395
+ function applyDefaultVegaLiteSort(spec) {
128396
+ // If users mark a field as ordinal, a common expectation is "use the data order"
128397
+ // (e.g. '1\u6708'...'12\u6708'). Vega-Lite defaults to sorting discrete domains, which can
128398
+ // produce surprising orders for stringified numbers. We only change behavior
128399
+ // when the author hasn't specified an explicit sort.
128400
+ const applyOne = (s) => {
128401
+ if (!s || typeof s !== 'object') return;
128402
+ const enc = s.encoding;
128403
+ const x = enc && enc.x;
128404
+ if (x && typeof x === 'object' && x.type === 'ordinal' && !('sort' in x)) {
128405
+ x.sort = null;
128406
+ }
128407
+ };
128408
+
128409
+ const walk = (s) => {
128410
+ if (!s || typeof s !== 'object') return;
128411
+ applyOne(s);
128412
+
128413
+ const arrays = ['layer', 'hconcat', 'vconcat', 'concat'];
128414
+ for (const key of arrays) {
128415
+ const childArr = s[key];
128416
+ if (Array.isArray(childArr)) {
128417
+ for (const child of childArr) walk(child);
128418
+ }
128419
+ }
128420
+ if (s.spec) walk(s.spec);
128421
+ };
128422
+
128423
+ walk(spec);
128424
+ }
128425
+
128426
+ async function renderVegaLite(code, mount) {
128427
+ const vegaEmbed = window.vegaEmbed;
128428
+ if (typeof vegaEmbed !== 'function') return;
128429
+ let spec;
128430
+ try {
128431
+ spec = JSON.parse(code);
128432
+ } catch {
128433
+ mount.textContent = 'Invalid Vega-Lite JSON.';
128434
+ return;
128435
+ }
128436
+ applyDefaultVegaLiteSort(spec);
128437
+ await vegaEmbed(mount, spec, {
128438
+ actions: false,
128439
+ renderer: 'svg',
128440
+ defaultStyle: true,
128441
+ logLevel: (window.vega && window.vega.Warn) ? window.vega.Warn : undefined,
128442
+ });
128443
+ }
128444
+
128445
+ async function renderInfographic(code, mount) {
128446
+ const lib = window.AntVInfographic;
128447
+ if (!lib || !lib.Infographic) return;
128448
+
128449
+ try {
128450
+ if (typeof lib.setDefaultFont === 'function') {
128451
+ const ff = themeConfig && themeConfig.fontFamily ? themeConfig.fontFamily : undefined;
128452
+ if (ff) lib.setDefaultFont(ff);
128453
+ }
128454
+ } catch {}
128455
+
128456
+ const opts = {
128457
+ container: mount,
128458
+ width: 900,
128459
+ height: 600,
128460
+ padding: 24,
128461
+ };
128462
+
128463
+ if (themeConfig && themeConfig.diagramStyle === 'handDrawn') {
128464
+ opts.themeConfig = { stylize: { type: 'rough', roughness: 0.5, bowing: 0.5 } };
128465
+ }
128466
+
128467
+ const ig = new lib.Infographic(opts);
128468
+ await new Promise((resolve, reject) => {
128469
+ const timeout = setTimeout(() => reject(new Error('Infographic render timeout after 10s')), 10000);
128470
+ ig.on && ig.on('rendered', () => { clearTimeout(timeout); resolve(); });
128471
+ ig.on && ig.on('error', (err) => { clearTimeout(timeout); reject(err); });
128472
+ try {
128473
+ ig.render(code);
128474
+ } catch (e) {
128475
+ clearTimeout(timeout);
128476
+ reject(e);
128477
+ }
128478
+ }).catch(() => {
128479
+ // keep container content on errors
128480
+ });
128481
+ }
128482
+
128483
+ async function main() {
128484
+ // Best-effort base href: some renderers may load assets relative to document.
128485
+ try {
128486
+ if (baseHref) {
128487
+ let base = document.querySelector('base');
128488
+ if (!base) {
128489
+ base = document.createElement('base');
128490
+ document.head.appendChild(base);
128491
+ }
128492
+ base.href = baseHref;
128493
+ }
128494
+ } catch {}
128495
+
128496
+ try {
128497
+ await ensureCdnLibsLoaded();
128498
+ } catch (e) {
128499
+ // If CDN scripts fail to load, keep code blocks as-is.
128500
+ return;
128501
+ }
128502
+
128503
+ const blocks = Array.from(document.querySelectorAll('pre > code'));
128504
+ let idx = 0;
128505
+ for (const codeEl of blocks) {
128506
+ const pre = codeEl && codeEl.parentElement;
128507
+ if (!pre) continue;
128508
+ const langRaw = getLangFromCodeClass(codeEl);
128509
+ if (!langRaw) continue;
128510
+ const lang = normalizeLang(langRaw);
128511
+ const code = getText(codeEl);
128512
+ if (!code.trim()) continue;
128513
+
128514
+ try {
128515
+ if (lang === 'mermaid') {
128516
+ const mount = replacePreWithContainer(pre, 'mermaid');
128517
+ await renderMermaid(code, mount, idx++);
128518
+ } else if (lang === 'dot') {
128519
+ const mount = replacePreWithContainer(pre, 'dot');
128520
+ await renderDot(code, mount);
128521
+ } else if (lang === 'vega-lite') {
128522
+ const mount = replacePreWithContainer(pre, 'vega-lite');
128523
+ await renderVegaLite(code, mount);
128524
+ } else if (lang === 'infographic') {
128525
+ const mount = replacePreWithContainer(pre, 'infographic');
128526
+ await renderInfographic(code, mount);
128527
+ }
128528
+ } catch {
128529
+ // Keep original code block on error
128530
+ }
128531
+ }
128532
+ }
128533
+
128534
+ if (document.readyState === 'loading') {
128535
+ document.addEventListener('DOMContentLoaded', () => { main(); }, { once: true });
128536
+ } else {
128537
+ main();
128538
+ }
128539
+ })();
128540
+ </script>`;
128541
+ return `<!DOCTYPE html>
128542
+ <html lang="en">
128543
+ <head>
128544
+ <meta charset="UTF-8" />
128545
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
128546
+ ${baseTag} <title>${escapeHtmlText(title)}</title>
128547
+ <style>${css2}</style>
128548
+ </head>
128549
+ <body>
128550
+ <div id="markdown-content" class="markdown-body">${fragment}</div>
128551
+ ${liveBootstrap}
128552
+ </body>
128553
+ </html>`;
128554
+ } finally {
128119
128555
  try {
128120
- const result = await browserRenderer.render(renderType, decodedCode, basePath, themeConfig);
128121
- if (result && result.base64) {
128122
- const imgTag = `<div class="md2x-diagram"><img class="md2x-diagram" src="data:image/${result.format};base64,${result.base64}" alt="${lang} diagram" style="max-width: 100%;" /></div>`;
128123
- html7 = html7.replace(fullMatch, imgTag);
128556
+ if (browserRenderer) {
128557
+ await browserRenderer.close();
128124
128558
  }
128125
- } catch (e) {
128126
- console.warn(`Failed to render ${lang} diagram:`, e);
128559
+ } finally {
128560
+ globalThis.platform = previousPlatform;
128127
128561
  }
128128
128562
  }
128129
- return html7;
128130
128563
  }
128131
- /**
128132
- * Decode HTML entities in code blocks
128133
- */
128134
- decodeHtmlEntities(text9) {
128135
- return text9.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&#x27;/g, "'").replace(/&#x2F;/g, "/").replace(/&#x3C;/gi, "<").replace(/&#x3E;/gi, ">").replace(/&#x26;/gi, "&").replace(/&#60;/g, "<").replace(/&#62;/g, ">").replace(/&#38;/g, "&");
128564
+ async exportToBuffer(markdown2, options = {}) {
128565
+ const html7 = await this.exportToString(markdown2, options);
128566
+ return Buffer.from(html7, "utf8");
128567
+ }
128568
+ async exportToFile(inputPath, outputPath, options = {}) {
128569
+ const markdown2 = fs3.readFileSync(inputPath, "utf-8");
128570
+ const basePath = options.basePath || path5.dirname(path5.resolve(inputPath));
128571
+ const title = options.title || path5.basename(inputPath, path5.extname(inputPath));
128572
+ const outputDir = path5.dirname(outputPath);
128573
+ if (!fs3.existsSync(outputDir)) {
128574
+ fs3.mkdirSync(outputDir, { recursive: true });
128575
+ }
128576
+ const html7 = await this.exportToString(markdown2, {
128577
+ ...options,
128578
+ basePath,
128579
+ title
128580
+ });
128581
+ fs3.writeFileSync(outputPath, html7, "utf8");
128136
128582
  }
128137
128583
  };
128138
128584
  node_exporter_default = NodeDocxExporter;
@@ -128156,17 +128602,36 @@ async function markdownToPdfBuffer(markdown2, options = {}) {
128156
128602
  const exporter = new NodePdfExporter2();
128157
128603
  return exporter.exportToBuffer(markdown2, options);
128158
128604
  }
128605
+ async function markdownToHtmlString(markdown2, options = {}) {
128606
+ const { NodeHtmlExporter: NodeHtmlExporter2 } = await Promise.resolve().then(() => (init_node_exporter(), node_exporter_exports));
128607
+ const exporter = new NodeHtmlExporter2();
128608
+ return exporter.exportToString(markdown2, options);
128609
+ }
128610
+ async function markdownToHtmlBuffer(markdown2, options = {}) {
128611
+ const { NodeHtmlExporter: NodeHtmlExporter2 } = await Promise.resolve().then(() => (init_node_exporter(), node_exporter_exports));
128612
+ const exporter = new NodeHtmlExporter2();
128613
+ return exporter.exportToBuffer(markdown2, options);
128614
+ }
128159
128615
  async function markdownFileToPdfFile(inputPath, outputPath, options = {}) {
128160
128616
  const { NodePdfExporter: NodePdfExporter2 } = await Promise.resolve().then(() => (init_node_exporter(), node_exporter_exports));
128161
128617
  const exporter = new NodePdfExporter2();
128162
128618
  return exporter.exportToFile(inputPath, outputPath, options);
128163
128619
  }
128620
+ async function markdownFileToHtmlFile(inputPath, outputPath, options = {}) {
128621
+ const { NodeHtmlExporter: NodeHtmlExporter2 } = await Promise.resolve().then(() => (init_node_exporter(), node_exporter_exports));
128622
+ const exporter = new NodeHtmlExporter2();
128623
+ return exporter.exportToFile(inputPath, outputPath, options);
128624
+ }
128164
128625
  export {
128165
128626
  NodeDocxExporter,
128627
+ NodeHtmlExporter,
128166
128628
  NodePdfExporter,
128167
128629
  markdownFileToDocxFile,
128630
+ markdownFileToHtmlFile,
128168
128631
  markdownFileToPdfFile,
128169
128632
  markdownToDocxBuffer,
128633
+ markdownToHtmlBuffer,
128634
+ markdownToHtmlString,
128170
128635
  markdownToPdfBuffer
128171
128636
  };
128172
128637
  /*! Bundled license information: