@wenyan-md/core 3.0.6 → 3.0.7
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/core.js +443 -7
- package/dist/types/core/index.d.ts +6 -2
- package/dist/types/core/mermaid.d.ts +19 -0
- package/dist/types/core/parser/mermaidParser.d.ts +9 -0
- package/dist/types/node/mermaidRenderer.d.ts +2 -0
- package/dist/types/node/render.d.ts +2 -2
- package/dist/types/node/types.d.ts +3 -11
- package/dist/wrapper.js +169 -6
- package/package.json +3 -2
package/dist/core.js
CHANGED
|
@@ -434,6 +434,28 @@ function createMarkedClient() {
|
|
|
434
434
|
md.use(highlightExtension);
|
|
435
435
|
md.use({
|
|
436
436
|
extensions: [
|
|
437
|
+
// 宽松 link/image tokenizer,允许 URL 中包含空格(不要求用 <> 包裹)
|
|
438
|
+
{
|
|
439
|
+
name: "looseLink",
|
|
440
|
+
level: "inline",
|
|
441
|
+
start(src) {
|
|
442
|
+
return src.match(/!?\[/)?.index;
|
|
443
|
+
},
|
|
444
|
+
tokenizer(src) {
|
|
445
|
+
const rule = /^(!?)\[([^\]]*)\]\(([^)]+)\)/;
|
|
446
|
+
const match = rule.exec(src);
|
|
447
|
+
if (!match) return;
|
|
448
|
+
const isImage = !!match[1];
|
|
449
|
+
return {
|
|
450
|
+
type: isImage ? "image" : "link",
|
|
451
|
+
raw: match[0],
|
|
452
|
+
text: match[2],
|
|
453
|
+
href: match[3],
|
|
454
|
+
tokens: this.lexer.inlineTokens(match[2])
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
},
|
|
458
|
+
// 自定义图片语法扩展 ![](){...}
|
|
437
459
|
{
|
|
438
460
|
name: "attributeImage",
|
|
439
461
|
level: "inline",
|
|
@@ -462,7 +484,8 @@ function createMarkedClient() {
|
|
|
462
484
|
renderer(token) {
|
|
463
485
|
const attrs = stringToMap(token.attrs);
|
|
464
486
|
const styleStr = Array.from(attrs).map(([k, v]) => /^\d+$/.test(v) ? `${k}:${v}px` : `${k}:${v}`).join("; ");
|
|
465
|
-
|
|
487
|
+
const href = normalizeHref(token.href);
|
|
488
|
+
return `<img src="${href}" alt="${token.text || ""}" title="${token.text || ""}" style="${styleStr}">`;
|
|
466
489
|
}
|
|
467
490
|
}
|
|
468
491
|
]
|
|
@@ -490,7 +513,12 @@ function createMarkedClient() {
|
|
|
490
513
|
},
|
|
491
514
|
// 重写普通图片 (处理标准 Markdown 图片)
|
|
492
515
|
image(token) {
|
|
493
|
-
|
|
516
|
+
const href = normalizeHref(token.href);
|
|
517
|
+
return `<img src="${href}" alt="${token.text || ""}" title="${token.title || token.text || ""}">`;
|
|
518
|
+
},
|
|
519
|
+
link(token) {
|
|
520
|
+
const href = normalizeHref(token.href);
|
|
521
|
+
return `<a href="${href}">${this.parser.parseInline(token.tokens)}</a>`;
|
|
494
522
|
}
|
|
495
523
|
}
|
|
496
524
|
});
|
|
@@ -503,10 +531,21 @@ function createMarkedClient() {
|
|
|
503
531
|
*/
|
|
504
532
|
async parse(markdown) {
|
|
505
533
|
await configure();
|
|
506
|
-
return md.parse(markdown);
|
|
534
|
+
return await md.parse(markdown);
|
|
507
535
|
}
|
|
508
536
|
};
|
|
509
537
|
}
|
|
538
|
+
function normalizeHref(href) {
|
|
539
|
+
href = href.trim();
|
|
540
|
+
if (href.startsWith("<") && href.endsWith(">")) {
|
|
541
|
+
href = href.slice(1, -1);
|
|
542
|
+
}
|
|
543
|
+
try {
|
|
544
|
+
return encodeURI(href);
|
|
545
|
+
} catch {
|
|
546
|
+
return href;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
510
549
|
let htmlHandlerRegistered = false;
|
|
511
550
|
function createMathJaxParser(options = {}) {
|
|
512
551
|
const adaptor = liteAdaptor();
|
|
@@ -565,6 +604,36 @@ function createMathJaxParser(options = {}) {
|
|
|
565
604
|
}
|
|
566
605
|
};
|
|
567
606
|
}
|
|
607
|
+
function createMermaidParser(options) {
|
|
608
|
+
const resolvedOptions = resolveMermaidOptions(options);
|
|
609
|
+
return {
|
|
610
|
+
async parser(html) {
|
|
611
|
+
if (!resolvedOptions.enabled || !containsMermaidCodeBlock(html)) {
|
|
612
|
+
return html;
|
|
613
|
+
}
|
|
614
|
+
if (!resolvedOptions.renderer) {
|
|
615
|
+
throw new Error("Mermaid 渲染已启用,但未配置 renderer");
|
|
616
|
+
}
|
|
617
|
+
return await resolvedOptions.renderer.renderHtml(html);
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
function resolveMermaidOptions(options) {
|
|
622
|
+
if (options === void 0) {
|
|
623
|
+
return { enabled: false };
|
|
624
|
+
}
|
|
625
|
+
if (typeof options === "boolean") {
|
|
626
|
+
return { enabled: options };
|
|
627
|
+
}
|
|
628
|
+
return {
|
|
629
|
+
enabled: options.enabled ?? true,
|
|
630
|
+
renderer: options.renderer
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
function containsMermaidCodeBlock(html) {
|
|
634
|
+
return html.includes("language-mermaid");
|
|
635
|
+
}
|
|
636
|
+
const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
|
568
637
|
function wechatPostRender(element) {
|
|
569
638
|
const mathElements = element.querySelectorAll("mjx-container");
|
|
570
639
|
mathElements.forEach((mathContainer) => {
|
|
@@ -583,6 +652,13 @@ function wechatPostRender(element) {
|
|
|
583
652
|
}
|
|
584
653
|
}
|
|
585
654
|
});
|
|
655
|
+
const mermaidSvgs = element.querySelectorAll('[data-wenyan-diagram="mermaid"] svg');
|
|
656
|
+
mermaidSvgs.forEach((svg) => {
|
|
657
|
+
svg.style.maxWidth = "100%";
|
|
658
|
+
svg.style.height = "auto";
|
|
659
|
+
inlineMermaidSvgStyles(svg);
|
|
660
|
+
flattenMermaidMarkers(svg);
|
|
661
|
+
});
|
|
586
662
|
const codeElements = element.querySelectorAll("pre code");
|
|
587
663
|
codeElements.forEach((codeEl) => {
|
|
588
664
|
codeEl.innerHTML = codeEl.innerHTML.replace(/\n/g, "<br>").replace(/(>[^<]+)|(^[^<]+)/g, (str) => str.replace(/\s/g, " "));
|
|
@@ -599,6 +675,268 @@ function wechatPostRender(element) {
|
|
|
599
675
|
element.style.color = "rgb(0, 0, 0)";
|
|
600
676
|
element.style.caretColor = "rgb(0, 0, 0)";
|
|
601
677
|
}
|
|
678
|
+
function inlineMermaidSvgStyles(svg) {
|
|
679
|
+
const styleElements = Array.from(svg.querySelectorAll("style"));
|
|
680
|
+
styleElements.forEach((styleElement) => {
|
|
681
|
+
applyInlineStylesFromCss(svg, styleElement.textContent ?? "");
|
|
682
|
+
styleElement.remove();
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
function flattenMermaidMarkers(svg) {
|
|
686
|
+
const convertedMarkerIds = /* @__PURE__ */ new Set();
|
|
687
|
+
const markedElements = Array.from(svg.querySelectorAll("[marker-start], [marker-end]"));
|
|
688
|
+
markedElements.forEach((markedElement) => {
|
|
689
|
+
convertMarkerReference(svg, markedElement, "start", convertedMarkerIds);
|
|
690
|
+
convertMarkerReference(svg, markedElement, "end", convertedMarkerIds);
|
|
691
|
+
});
|
|
692
|
+
convertedMarkerIds.forEach((markerId) => {
|
|
693
|
+
findMarkerById(svg, markerId)?.remove();
|
|
694
|
+
});
|
|
695
|
+
svg.querySelectorAll("defs").forEach((defs) => {
|
|
696
|
+
if (!defs.children.length) {
|
|
697
|
+
defs.remove();
|
|
698
|
+
}
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
function convertMarkerReference(svg, sourceElement, position, convertedMarkerIds) {
|
|
702
|
+
const markerAttribute = `marker-${position}`;
|
|
703
|
+
const markerValue = sourceElement.getAttribute(markerAttribute);
|
|
704
|
+
if (!markerValue) {
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
const markerId = extractMarkerId(markerValue);
|
|
708
|
+
if (!markerId) {
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
const marker = findMarkerById(svg, markerId);
|
|
712
|
+
if (!marker) {
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
const flattenedMarker = createFlattenedMarker(svg, sourceElement, marker, position);
|
|
716
|
+
if (!flattenedMarker) {
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
sourceElement.parentNode?.appendChild(flattenedMarker);
|
|
720
|
+
sourceElement.removeAttribute(markerAttribute);
|
|
721
|
+
convertedMarkerIds.add(markerId);
|
|
722
|
+
}
|
|
723
|
+
function createFlattenedMarker(svg, sourceElement, marker, position) {
|
|
724
|
+
const terminalPoints = getTerminalPoints(sourceElement, position);
|
|
725
|
+
if (!terminalPoints) {
|
|
726
|
+
return null;
|
|
727
|
+
}
|
|
728
|
+
const ownerDocument = svg.ownerDocument;
|
|
729
|
+
const markerGroup = ownerDocument.createElementNS(SVG_NAMESPACE, "g");
|
|
730
|
+
const markerStyle = marker.getAttribute("style");
|
|
731
|
+
const angle = getMarkerAngleDegrees(position, terminalPoints.anchor, terminalPoints.reference, marker);
|
|
732
|
+
markerGroup.setAttribute("data-wenyan-marker", position);
|
|
733
|
+
markerGroup.setAttribute("transform", buildMarkerTransform(marker, terminalPoints.anchor, angle, position));
|
|
734
|
+
if (markerStyle) {
|
|
735
|
+
markerGroup.setAttribute("style", markerStyle);
|
|
736
|
+
}
|
|
737
|
+
Array.from(marker.children).forEach((child) => {
|
|
738
|
+
markerGroup.appendChild(child.cloneNode(true));
|
|
739
|
+
});
|
|
740
|
+
return markerGroup;
|
|
741
|
+
}
|
|
742
|
+
function getTerminalPoints(sourceElement, position) {
|
|
743
|
+
const points = getEdgePoints(sourceElement);
|
|
744
|
+
if (points.length < 2) {
|
|
745
|
+
return null;
|
|
746
|
+
}
|
|
747
|
+
if (position === "end") {
|
|
748
|
+
const anchor2 = points.at(-1);
|
|
749
|
+
if (!anchor2) {
|
|
750
|
+
return null;
|
|
751
|
+
}
|
|
752
|
+
const reference2 = findDistinctPoint(points, points.length - 2, -1, anchor2);
|
|
753
|
+
return reference2 ? { anchor: anchor2, reference: reference2 } : null;
|
|
754
|
+
}
|
|
755
|
+
const anchor = points[0];
|
|
756
|
+
if (!anchor) {
|
|
757
|
+
return null;
|
|
758
|
+
}
|
|
759
|
+
const reference = findDistinctPoint(points, 1, 1, anchor);
|
|
760
|
+
return reference ? { anchor, reference } : null;
|
|
761
|
+
}
|
|
762
|
+
function getEdgePoints(sourceElement) {
|
|
763
|
+
const encodedPoints = sourceElement.getAttribute("data-points");
|
|
764
|
+
if (encodedPoints) {
|
|
765
|
+
const decodedPoints = decodeMermaidPoints(sourceElement, encodedPoints);
|
|
766
|
+
if (decodedPoints.length >= 2) {
|
|
767
|
+
return decodedPoints;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
const polyPoints = sourceElement.getAttribute("points");
|
|
771
|
+
if (polyPoints) {
|
|
772
|
+
return parseCoordinatePairs(polyPoints);
|
|
773
|
+
}
|
|
774
|
+
const pathData = sourceElement.getAttribute("d");
|
|
775
|
+
if (pathData) {
|
|
776
|
+
return parseCoordinatePairs(pathData);
|
|
777
|
+
}
|
|
778
|
+
return [];
|
|
779
|
+
}
|
|
780
|
+
function decodeMermaidPoints(sourceElement, encodedPoints) {
|
|
781
|
+
const defaultView = sourceElement.ownerDocument.defaultView;
|
|
782
|
+
try {
|
|
783
|
+
const decoded = defaultView?.atob?.(encodedPoints) ?? (typeof atob === "function" ? atob(encodedPoints) : "");
|
|
784
|
+
const parsed = JSON.parse(decoded);
|
|
785
|
+
return parsed.filter((point) => typeof point.x === "number" && typeof point.y === "number").map((point) => ({
|
|
786
|
+
x: point.x,
|
|
787
|
+
y: point.y
|
|
788
|
+
}));
|
|
789
|
+
} catch {
|
|
790
|
+
return [];
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
function parseCoordinatePairs(value) {
|
|
794
|
+
const matches = Array.from(value.matchAll(/-?\d*\.?\d+(?:e[-+]?\d+)?/gi), (match) => Number(match[0]));
|
|
795
|
+
const points = [];
|
|
796
|
+
for (let index = 0; index + 1 < matches.length; index += 2) {
|
|
797
|
+
points.push({
|
|
798
|
+
x: matches[index],
|
|
799
|
+
y: matches[index + 1]
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
return points;
|
|
803
|
+
}
|
|
804
|
+
function findDistinctPoint(points, startIndex, step, anchor) {
|
|
805
|
+
for (let index = startIndex; index >= 0 && index < points.length; index += step) {
|
|
806
|
+
const point = points[index];
|
|
807
|
+
if (!point) {
|
|
808
|
+
continue;
|
|
809
|
+
}
|
|
810
|
+
if (point.x !== anchor.x || point.y !== anchor.y) {
|
|
811
|
+
return point;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
return null;
|
|
815
|
+
}
|
|
816
|
+
function getMarkerAngleDegrees(position, anchor, reference, marker) {
|
|
817
|
+
const baseAngle = position === "end" ? Math.atan2(anchor.y - reference.y, anchor.x - reference.x) : Math.atan2(reference.y - anchor.y, reference.x - anchor.x);
|
|
818
|
+
const orient = marker.getAttribute("orient")?.trim();
|
|
819
|
+
let angle = baseAngle * 180 / Math.PI;
|
|
820
|
+
if (orient === "auto-start-reverse" && position === "start") {
|
|
821
|
+
angle += 180;
|
|
822
|
+
} else if (orient && orient !== "auto") {
|
|
823
|
+
const offset = Number.parseFloat(orient);
|
|
824
|
+
if (Number.isFinite(offset)) {
|
|
825
|
+
angle += offset;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
return angle;
|
|
829
|
+
}
|
|
830
|
+
function buildMarkerTransform(marker, anchor, angle, position) {
|
|
831
|
+
const viewBox = parseViewBox(marker.getAttribute("viewBox"));
|
|
832
|
+
const markerWidth = parseNumericAttribute(marker.getAttribute("markerWidth"), viewBox?.width ?? 1);
|
|
833
|
+
const markerHeight = parseNumericAttribute(marker.getAttribute("markerHeight"), viewBox?.height ?? 1);
|
|
834
|
+
const viewBoxWidth = viewBox?.width ?? markerWidth;
|
|
835
|
+
const viewBoxHeight = viewBox?.height ?? markerHeight;
|
|
836
|
+
const scaleX = viewBoxWidth === 0 ? 1 : markerWidth / viewBoxWidth;
|
|
837
|
+
const scaleY = viewBoxHeight === 0 ? 1 : markerHeight / viewBoxHeight;
|
|
838
|
+
const offsetX = parseNumericAttribute(marker.getAttribute("refX")) + (viewBox?.minX ?? 0);
|
|
839
|
+
const offsetY = parseNumericAttribute(marker.getAttribute("refY")) + (viewBox?.minY ?? 0);
|
|
840
|
+
const adjustedAnchor = adjustMarkerAnchor(anchor, angle, scaleX, viewBox, offsetX, position);
|
|
841
|
+
return [
|
|
842
|
+
`translate(${adjustedAnchor.x} ${adjustedAnchor.y})`,
|
|
843
|
+
`rotate(${angle})`,
|
|
844
|
+
`scale(${scaleX} ${scaleY})`,
|
|
845
|
+
`translate(${-offsetX} ${-offsetY})`
|
|
846
|
+
].join(" ");
|
|
847
|
+
}
|
|
848
|
+
function adjustMarkerAnchor(anchor, angle, scaleX, viewBox, refX, position) {
|
|
849
|
+
const minX = viewBox?.minX ?? 0;
|
|
850
|
+
const maxX = viewBox ? viewBox.minX + viewBox.width : refX;
|
|
851
|
+
const overlap = position === "start" ? Math.max(0, refX - minX) : Math.max(0, maxX - refX);
|
|
852
|
+
if (overlap === 0) {
|
|
853
|
+
return anchor;
|
|
854
|
+
}
|
|
855
|
+
const distance = overlap * scaleX;
|
|
856
|
+
const radians = angle * Math.PI / 180;
|
|
857
|
+
const direction = position === "start" ? 1 : -1;
|
|
858
|
+
return {
|
|
859
|
+
x: anchor.x + Math.cos(radians) * distance * direction,
|
|
860
|
+
y: anchor.y + Math.sin(radians) * distance * direction
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
function parseViewBox(viewBoxValue) {
|
|
864
|
+
if (!viewBoxValue) {
|
|
865
|
+
return null;
|
|
866
|
+
}
|
|
867
|
+
const values = viewBoxValue.trim().split(/[\s,]+/).map((value) => Number.parseFloat(value));
|
|
868
|
+
if (values.length !== 4 || values.some((value) => !Number.isFinite(value))) {
|
|
869
|
+
return null;
|
|
870
|
+
}
|
|
871
|
+
return {
|
|
872
|
+
minX: values[0],
|
|
873
|
+
minY: values[1],
|
|
874
|
+
width: values[2],
|
|
875
|
+
height: values[3]
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
function parseNumericAttribute(value, fallback = 0) {
|
|
879
|
+
if (!value) {
|
|
880
|
+
return fallback;
|
|
881
|
+
}
|
|
882
|
+
const parsed = Number.parseFloat(value);
|
|
883
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
884
|
+
}
|
|
885
|
+
function extractMarkerId(markerValue) {
|
|
886
|
+
const match = markerValue.match(/url\(#([^)]+)\)/);
|
|
887
|
+
return match?.[1] ?? null;
|
|
888
|
+
}
|
|
889
|
+
function findMarkerById(svg, markerId) {
|
|
890
|
+
return Array.from(svg.querySelectorAll("marker")).find((marker) => marker.getAttribute("id") === markerId);
|
|
891
|
+
}
|
|
892
|
+
function applyInlineStylesFromCss(svg, cssText) {
|
|
893
|
+
const ast = csstree.parse(cssText, parseOptions);
|
|
894
|
+
csstree.walk(ast, {
|
|
895
|
+
visit: "Rule",
|
|
896
|
+
enter(node) {
|
|
897
|
+
if (node.prelude.type !== "SelectorList") return;
|
|
898
|
+
const declarations = node.block.children.toArray().filter((decl) => decl.type === "Declaration");
|
|
899
|
+
if (declarations.length === 0) return;
|
|
900
|
+
node.prelude.children.forEach((selectorNode) => {
|
|
901
|
+
const selector = csstree.generate(selectorNode).trim();
|
|
902
|
+
if (!selector || selector.includes("::")) return;
|
|
903
|
+
const targets = resolveInlineTargets(svg, selector);
|
|
904
|
+
targets.forEach((target) => {
|
|
905
|
+
declarations.forEach((decl) => {
|
|
906
|
+
const value = csstree.generate(decl.value);
|
|
907
|
+
const priority = decl.important ? "important" : "";
|
|
908
|
+
target.style.setProperty(decl.property, value, priority);
|
|
909
|
+
});
|
|
910
|
+
});
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
function resolveInlineTargets(svg, selector) {
|
|
916
|
+
if (selector === ":root") {
|
|
917
|
+
return [svg];
|
|
918
|
+
}
|
|
919
|
+
if (selector.includes(":")) {
|
|
920
|
+
return [];
|
|
921
|
+
}
|
|
922
|
+
const targets = /* @__PURE__ */ new Set();
|
|
923
|
+
if (svg.matches(selector)) {
|
|
924
|
+
targets.add(svg);
|
|
925
|
+
}
|
|
926
|
+
svg.querySelectorAll(selector).forEach((node) => {
|
|
927
|
+
if (isInlineStyleTarget(node)) {
|
|
928
|
+
targets.add(node);
|
|
929
|
+
}
|
|
930
|
+
});
|
|
931
|
+
return Array.from(targets);
|
|
932
|
+
}
|
|
933
|
+
function isInlineStyleTarget(node) {
|
|
934
|
+
const defaultView = node.ownerDocument.defaultView;
|
|
935
|
+
if (!defaultView) {
|
|
936
|
+
return false;
|
|
937
|
+
}
|
|
938
|
+
return node instanceof defaultView.SVGElement || node instanceof defaultView.HTMLElement;
|
|
939
|
+
}
|
|
602
940
|
const themeModifier = createCssModifier({});
|
|
603
941
|
function renderTheme(wenyanElement, themeCss) {
|
|
604
942
|
const modifiedCss = themeModifier(themeCss);
|
|
@@ -1169,10 +1507,104 @@ function getContentForToutiao(wenyanElement) {
|
|
|
1169
1507
|
});
|
|
1170
1508
|
return wenyanElement.outerHTML;
|
|
1171
1509
|
}
|
|
1510
|
+
const DEFAULT_MERMAID_CONFIG = {
|
|
1511
|
+
htmlLabels: false,
|
|
1512
|
+
flowchart: {
|
|
1513
|
+
htmlLabels: false
|
|
1514
|
+
}
|
|
1515
|
+
};
|
|
1516
|
+
async function replaceMermaidCodeBlocks(root, renderDiagram) {
|
|
1517
|
+
const codeBlocks = Array.from(root.querySelectorAll("pre > code.language-mermaid"));
|
|
1518
|
+
if (codeBlocks.length === 0) {
|
|
1519
|
+
return false;
|
|
1520
|
+
}
|
|
1521
|
+
const ownerDocument = getOwnerDocument(root);
|
|
1522
|
+
for (const [index, codeBlock] of codeBlocks.entries()) {
|
|
1523
|
+
const pre = codeBlock.parentElement;
|
|
1524
|
+
const parent = pre?.parentElement;
|
|
1525
|
+
if (!pre || !parent) {
|
|
1526
|
+
continue;
|
|
1527
|
+
}
|
|
1528
|
+
const code = codeBlock.textContent ?? "";
|
|
1529
|
+
const svg = await renderDiagram({
|
|
1530
|
+
id: `wenyan-mermaid-${index + 1}`,
|
|
1531
|
+
code
|
|
1532
|
+
});
|
|
1533
|
+
const figure = ownerDocument.createElement("figure");
|
|
1534
|
+
figure.setAttribute("data-wenyan-diagram", "mermaid");
|
|
1535
|
+
figure.innerHTML = svg.trim();
|
|
1536
|
+
pre.replaceWith(figure);
|
|
1537
|
+
}
|
|
1538
|
+
return true;
|
|
1539
|
+
}
|
|
1540
|
+
function createBrowserMermaidRenderer(options = {}) {
|
|
1541
|
+
let mermaidModulePromise = null;
|
|
1542
|
+
return {
|
|
1543
|
+
async renderHtml(html) {
|
|
1544
|
+
if (typeof DOMParser === "undefined") {
|
|
1545
|
+
throw new Error("当前环境不支持浏览器 Mermaid 渲染");
|
|
1546
|
+
}
|
|
1547
|
+
const parser = new DOMParser();
|
|
1548
|
+
const document = parser.parseFromString(`<body>${html}</body>`, "text/html");
|
|
1549
|
+
const root = document.body;
|
|
1550
|
+
const mermaid = await getMermaidModule();
|
|
1551
|
+
mermaid.initialize(createMermaidConfig(options.mermaidConfig));
|
|
1552
|
+
try {
|
|
1553
|
+
await replaceMermaidCodeBlocks(root, async ({ id, code }) => {
|
|
1554
|
+
const { svg } = await mermaid.render(id, code);
|
|
1555
|
+
return svg;
|
|
1556
|
+
});
|
|
1557
|
+
} catch (error) {
|
|
1558
|
+
throw createMermaidRenderError(error);
|
|
1559
|
+
}
|
|
1560
|
+
return root.innerHTML;
|
|
1561
|
+
}
|
|
1562
|
+
};
|
|
1563
|
+
async function getMermaidModule() {
|
|
1564
|
+
if (!mermaidModulePromise) {
|
|
1565
|
+
mermaidModulePromise = import("mermaid");
|
|
1566
|
+
}
|
|
1567
|
+
const module = await mermaidModulePromise;
|
|
1568
|
+
return module.default;
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
function createMermaidRenderError(error) {
|
|
1572
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1573
|
+
return new Error(`Mermaid 图表渲染失败: ${message}`);
|
|
1574
|
+
}
|
|
1575
|
+
function createMermaidConfig(overrides = {}) {
|
|
1576
|
+
const flowchartOverrides = getRecord(overrides.flowchart);
|
|
1577
|
+
return {
|
|
1578
|
+
...DEFAULT_MERMAID_CONFIG,
|
|
1579
|
+
startOnLoad: false,
|
|
1580
|
+
securityLevel: "strict",
|
|
1581
|
+
...overrides,
|
|
1582
|
+
flowchart: {
|
|
1583
|
+
...getRecord(DEFAULT_MERMAID_CONFIG.flowchart),
|
|
1584
|
+
...flowchartOverrides
|
|
1585
|
+
}
|
|
1586
|
+
};
|
|
1587
|
+
}
|
|
1588
|
+
function getOwnerDocument(root) {
|
|
1589
|
+
if ("createElement" in root) {
|
|
1590
|
+
return root;
|
|
1591
|
+
}
|
|
1592
|
+
if ("ownerDocument" in root && root.ownerDocument) {
|
|
1593
|
+
return root.ownerDocument;
|
|
1594
|
+
}
|
|
1595
|
+
throw new Error("无法获取 Mermaid 渲染所需的 Document");
|
|
1596
|
+
}
|
|
1597
|
+
function getRecord(value) {
|
|
1598
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
1599
|
+
return value;
|
|
1600
|
+
}
|
|
1601
|
+
return {};
|
|
1602
|
+
}
|
|
1172
1603
|
async function createWenyanCore(options = {}) {
|
|
1173
|
-
const { isConvertMathJax = true, isWechat = true } = options;
|
|
1604
|
+
const { isConvertMathJax = true, isWechat = true, mermaid } = options;
|
|
1174
1605
|
const markedClient = createMarkedClient();
|
|
1175
1606
|
const mathJaxParser = createMathJaxParser();
|
|
1607
|
+
const mermaidParser = createMermaidParser(mermaid);
|
|
1176
1608
|
registerAllBuiltInThemes();
|
|
1177
1609
|
registerBuiltInHlThemes();
|
|
1178
1610
|
registerBuiltInMacStyle();
|
|
@@ -1181,11 +1613,11 @@ async function createWenyanCore(options = {}) {
|
|
|
1181
1613
|
return await handleFrontMatter(markdown);
|
|
1182
1614
|
},
|
|
1183
1615
|
async renderMarkdown(markdown) {
|
|
1184
|
-
|
|
1616
|
+
let html = await markedClient.parse(markdown);
|
|
1185
1617
|
if (isConvertMathJax) {
|
|
1186
|
-
|
|
1618
|
+
html = mathJaxParser.parser(html);
|
|
1187
1619
|
}
|
|
1188
|
-
return html;
|
|
1620
|
+
return await mermaidParser.parser(html);
|
|
1189
1621
|
},
|
|
1190
1622
|
async applyStylesWithTheme(wenyanElement, options2 = {}) {
|
|
1191
1623
|
const {
|
|
@@ -1288,7 +1720,10 @@ const DEFAULT_CSS_UPDATES = {
|
|
|
1288
1720
|
};
|
|
1289
1721
|
export {
|
|
1290
1722
|
addFootnotes,
|
|
1723
|
+
createBrowserMermaidRenderer,
|
|
1291
1724
|
createCssModifier,
|
|
1725
|
+
createMermaidConfig,
|
|
1726
|
+
createMermaidRenderError,
|
|
1292
1727
|
createWenyanCore,
|
|
1293
1728
|
getAllGzhThemes,
|
|
1294
1729
|
getAllHlThemes,
|
|
@@ -1305,6 +1740,7 @@ export {
|
|
|
1305
1740
|
registerHlTheme,
|
|
1306
1741
|
registerMacStyle,
|
|
1307
1742
|
registerTheme,
|
|
1743
|
+
replaceMermaidCodeBlocks,
|
|
1308
1744
|
sansSerif,
|
|
1309
1745
|
serif
|
|
1310
1746
|
};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type FrontMatterResult } from "./parser/frontMatterParser.js";
|
|
2
|
+
import type { MermaidOptions } from "./mermaid.js";
|
|
2
3
|
export interface WenyanOptions {
|
|
3
4
|
isConvertMathJax?: boolean;
|
|
4
5
|
isWechat?: boolean;
|
|
6
|
+
mermaid?: boolean | MermaidOptions;
|
|
5
7
|
}
|
|
6
8
|
export interface ApplyStylesOptions {
|
|
7
9
|
themeId?: string;
|
|
@@ -12,7 +14,7 @@ export interface ApplyStylesOptions {
|
|
|
12
14
|
isAddFootnote?: boolean;
|
|
13
15
|
}
|
|
14
16
|
export declare function createWenyanCore(options?: WenyanOptions): Promise<{
|
|
15
|
-
handleFrontMatter(markdown: string): Promise<
|
|
17
|
+
handleFrontMatter(markdown: string): Promise<FrontMatterResult>;
|
|
16
18
|
renderMarkdown(markdown: string): Promise<string>;
|
|
17
19
|
applyStylesWithTheme(wenyanElement: HTMLElement, options?: ApplyStylesOptions): Promise<string>;
|
|
18
20
|
applyStylesWithResolvedCss(wenyanElement: HTMLElement, options: {
|
|
@@ -31,4 +33,6 @@ export * from "./platform/medium.js";
|
|
|
31
33
|
export * from "./platform/zhihu.js";
|
|
32
34
|
export * from "./platform/toutiao.js";
|
|
33
35
|
export { addFootnotes } from "./renderer/footnotesRender.js";
|
|
36
|
+
export * from "./mermaid.js";
|
|
34
37
|
export type WenyanCoreInstance = Awaited<ReturnType<typeof createWenyanCore>>;
|
|
38
|
+
export type { FrontMatterResult } from "./parser/frontMatterParser.js";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface MermaidDiagram {
|
|
2
|
+
id: string;
|
|
3
|
+
code: string;
|
|
4
|
+
}
|
|
5
|
+
export type MermaidDiagramRenderer = (diagram: MermaidDiagram) => Promise<string>;
|
|
6
|
+
export interface MermaidRenderer {
|
|
7
|
+
renderHtml(html: string): Promise<string>;
|
|
8
|
+
}
|
|
9
|
+
export interface MermaidOptions {
|
|
10
|
+
enabled?: boolean;
|
|
11
|
+
renderer?: MermaidRenderer;
|
|
12
|
+
}
|
|
13
|
+
export interface MermaidRendererFactoryOptions {
|
|
14
|
+
mermaidConfig?: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
export declare function replaceMermaidCodeBlocks(root: ParentNode, renderDiagram: MermaidDiagramRenderer): Promise<boolean>;
|
|
17
|
+
export declare function createBrowserMermaidRenderer(options?: MermaidRendererFactoryOptions): MermaidRenderer;
|
|
18
|
+
export declare function createMermaidRenderError(error: unknown): Error;
|
|
19
|
+
export declare function createMermaidConfig(overrides?: Record<string, unknown>): Record<string, unknown>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { MermaidOptions, MermaidRenderer } from "../mermaid.js";
|
|
2
|
+
export interface ResolvedMermaidOptions {
|
|
3
|
+
enabled: boolean;
|
|
4
|
+
renderer?: MermaidRenderer;
|
|
5
|
+
}
|
|
6
|
+
export declare function createMermaidParser(options?: boolean | MermaidOptions): {
|
|
7
|
+
parser(html: string): Promise<string>;
|
|
8
|
+
};
|
|
9
|
+
export declare function resolveMermaidOptions(options?: boolean | MermaidOptions): ResolvedMermaidOptions;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ApplyStylesOptions } from "../core/index.js";
|
|
1
|
+
import { ApplyStylesOptions, WenyanCoreInstance } from "../core/index.js";
|
|
2
2
|
import { GetInputContentFn, RenderContext, RenderOptions } from "./types.js";
|
|
3
|
-
export declare function renderStyledContent(content: string, options?: ApplyStylesOptions): Promise<string>;
|
|
3
|
+
export declare function renderStyledContent(content: string, options?: ApplyStylesOptions, coreInstance?: WenyanCoreInstance): Promise<string>;
|
|
4
4
|
export declare function prepareRenderContext(inputContent: string | undefined, options: RenderOptions, getInputContent: GetInputContentFn): Promise<RenderContext>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FrontMatterResult } from "../core/parser/frontMatterParser.js";
|
|
1
2
|
export interface RenderOptions {
|
|
2
3
|
file?: string;
|
|
3
4
|
theme?: string;
|
|
@@ -5,6 +6,7 @@ export interface RenderOptions {
|
|
|
5
6
|
highlight: string;
|
|
6
7
|
macStyle: boolean;
|
|
7
8
|
footnote: boolean;
|
|
9
|
+
mermaid?: boolean;
|
|
8
10
|
}
|
|
9
11
|
export interface PublishOptions extends RenderOptions {
|
|
10
12
|
appId?: string;
|
|
@@ -18,17 +20,7 @@ export interface RenderContext {
|
|
|
18
20
|
gzhContent: StyledContent;
|
|
19
21
|
absoluteDirPath: string | undefined;
|
|
20
22
|
}
|
|
21
|
-
export
|
|
22
|
-
content: string;
|
|
23
|
-
title?: string;
|
|
24
|
-
description?: string;
|
|
25
|
-
cover?: string;
|
|
26
|
-
author?: string;
|
|
27
|
-
source_url?: string;
|
|
28
|
-
need_open_comment?: boolean;
|
|
29
|
-
only_fans_can_comment?: boolean;
|
|
30
|
-
image_list?: string[];
|
|
31
|
-
}
|
|
23
|
+
export type StyledContent = FrontMatterResult;
|
|
32
24
|
export type GetInputContentFn = (inputContent?: string, filePath?: string) => Promise<{
|
|
33
25
|
content: string;
|
|
34
26
|
absoluteDirPath?: string;
|
package/dist/wrapper.js
CHANGED
|
@@ -10,7 +10,7 @@ import { FormDataEncoder } from "form-data-encoder";
|
|
|
10
10
|
import { FormData } from "formdata-node";
|
|
11
11
|
import { Readable } from "node:stream";
|
|
12
12
|
import { defaultTokenCache, defaultCredential, WechatPublisher, CredentialStore } from "./publish.js";
|
|
13
|
-
import { createWenyanCore, getAllGzhThemes } from "./core.js";
|
|
13
|
+
import { replaceMermaidCodeBlocks, createMermaidRenderError, createMermaidConfig, createWenyanCore, getAllGzhThemes } from "./core.js";
|
|
14
14
|
const configDir = (() => {
|
|
15
15
|
if (process.env.XDG_CONFIG_HOME) {
|
|
16
16
|
return path.join(process.env.XDG_CONFIG_HOME, "wenyan-md");
|
|
@@ -660,7 +660,169 @@ class ConfigStore {
|
|
|
660
660
|
}
|
|
661
661
|
}
|
|
662
662
|
const configStore = new ConfigStore();
|
|
663
|
-
|
|
663
|
+
function createNodeMermaidRenderer(options = {}) {
|
|
664
|
+
const dom = new JSDOM("<body></body>", { pretendToBeVisual: true });
|
|
665
|
+
const runtimeWindow = dom.window;
|
|
666
|
+
let renderQueue = Promise.resolve();
|
|
667
|
+
let mermaidPromise = null;
|
|
668
|
+
installMermaidGlobals(runtimeWindow);
|
|
669
|
+
return {
|
|
670
|
+
async renderHtml(html) {
|
|
671
|
+
const task = renderQueue.then(async () => {
|
|
672
|
+
const mermaid = await getMermaid();
|
|
673
|
+
dom.window.document.body.innerHTML = html;
|
|
674
|
+
try {
|
|
675
|
+
await replaceMermaidCodeBlocks(dom.window.document.body, async ({ id, code }) => {
|
|
676
|
+
const { svg } = await mermaid.render(id, code);
|
|
677
|
+
return svg;
|
|
678
|
+
});
|
|
679
|
+
return dom.window.document.body.innerHTML;
|
|
680
|
+
} catch (error) {
|
|
681
|
+
throw createMermaidRenderError(error);
|
|
682
|
+
} finally {
|
|
683
|
+
dom.window.document.body.innerHTML = "";
|
|
684
|
+
}
|
|
685
|
+
});
|
|
686
|
+
renderQueue = task.then(
|
|
687
|
+
() => void 0,
|
|
688
|
+
() => void 0
|
|
689
|
+
);
|
|
690
|
+
return await task;
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
async function getMermaid() {
|
|
694
|
+
if (!mermaidPromise) {
|
|
695
|
+
mermaidPromise = import("mermaid").then((module) => module.default);
|
|
696
|
+
}
|
|
697
|
+
const mermaid = await mermaidPromise;
|
|
698
|
+
mermaid.initialize(createMermaidConfig(options.mermaidConfig));
|
|
699
|
+
return mermaid;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
function installMermaidGlobals(window) {
|
|
703
|
+
ensureSvgPolyfills(window);
|
|
704
|
+
const globals = [
|
|
705
|
+
["window", window],
|
|
706
|
+
["document", window.document],
|
|
707
|
+
["navigator", window.navigator],
|
|
708
|
+
["Element", window.Element],
|
|
709
|
+
["HTMLElement", window.HTMLElement],
|
|
710
|
+
["SVGElement", window.SVGElement],
|
|
711
|
+
["Node", window.Node],
|
|
712
|
+
["Document", window.Document],
|
|
713
|
+
["DOMParser", window.DOMParser],
|
|
714
|
+
["XMLSerializer", window.XMLSerializer],
|
|
715
|
+
["getComputedStyle", window.getComputedStyle.bind(window)]
|
|
716
|
+
];
|
|
717
|
+
for (const [key, value] of globals) {
|
|
718
|
+
Object.defineProperty(globalThis, key, {
|
|
719
|
+
configurable: true,
|
|
720
|
+
writable: true,
|
|
721
|
+
value
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
function ensureSvgPolyfills(window) {
|
|
726
|
+
const svgElementPrototype = window.SVGElement.prototype;
|
|
727
|
+
if (!svgElementPrototype.getBBox) {
|
|
728
|
+
Object.defineProperty(svgElementPrototype, "getBBox", {
|
|
729
|
+
configurable: true,
|
|
730
|
+
writable: true,
|
|
731
|
+
value() {
|
|
732
|
+
const tagName = this.tagName.toLowerCase();
|
|
733
|
+
if (tagName === "text" || tagName === "tspan") {
|
|
734
|
+
const text = this.textContent ?? "";
|
|
735
|
+
const width = Math.max(text.length * 8, 16);
|
|
736
|
+
return {
|
|
737
|
+
x: 0,
|
|
738
|
+
y: -16,
|
|
739
|
+
width,
|
|
740
|
+
height: 16,
|
|
741
|
+
top: -16,
|
|
742
|
+
right: width,
|
|
743
|
+
bottom: 0,
|
|
744
|
+
left: 0,
|
|
745
|
+
toJSON() {
|
|
746
|
+
return this;
|
|
747
|
+
}
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
if (tagName === "style" || tagName === "defs" || tagName === "marker") {
|
|
751
|
+
return { x: 0, y: 0, width: 0, height: 0, top: 0, right: 0, bottom: 0, left: 0, toJSON() {
|
|
752
|
+
return this;
|
|
753
|
+
} };
|
|
754
|
+
}
|
|
755
|
+
if (tagName === "rect" || tagName === "image") {
|
|
756
|
+
const w = parseFloat(this.getAttribute("width") || "0");
|
|
757
|
+
const h = parseFloat(this.getAttribute("height") || "0");
|
|
758
|
+
const x = parseFloat(this.getAttribute("x") || "0");
|
|
759
|
+
const y = parseFloat(this.getAttribute("y") || "0");
|
|
760
|
+
return { x, y, width: w, height: h, top: y, right: x + w, bottom: y + h, left: x, toJSON() {
|
|
761
|
+
return this;
|
|
762
|
+
} };
|
|
763
|
+
}
|
|
764
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
765
|
+
let hasChildren = false;
|
|
766
|
+
for (let i = 0; i < this.children.length; i++) {
|
|
767
|
+
const child = this.children[i];
|
|
768
|
+
if (!child.getBBox) continue;
|
|
769
|
+
const childBox = child.getBBox();
|
|
770
|
+
if (childBox.width === 0 && childBox.height === 0) continue;
|
|
771
|
+
let tx = 0, ty = 0;
|
|
772
|
+
const transform = child.getAttribute("transform");
|
|
773
|
+
if (transform) {
|
|
774
|
+
const match = transform.match(/translate\(([-0-9.]+)(?:[,\s]+([-0-9.]+))?\)/);
|
|
775
|
+
if (match) {
|
|
776
|
+
tx = parseFloat(match[1]);
|
|
777
|
+
ty = match[2] ? parseFloat(match[2]) : 0;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
minX = Math.min(minX, childBox.x + tx);
|
|
781
|
+
minY = Math.min(minY, childBox.y + ty);
|
|
782
|
+
maxX = Math.max(maxX, childBox.x + childBox.width + tx);
|
|
783
|
+
maxY = Math.max(maxY, childBox.y + childBox.height + ty);
|
|
784
|
+
hasChildren = true;
|
|
785
|
+
}
|
|
786
|
+
if (!hasChildren) {
|
|
787
|
+
return { x: 0, y: 0, width: 0, height: 0, top: 0, right: 0, bottom: 0, left: 0, toJSON() {
|
|
788
|
+
return this;
|
|
789
|
+
} };
|
|
790
|
+
}
|
|
791
|
+
return {
|
|
792
|
+
x: minX,
|
|
793
|
+
y: minY,
|
|
794
|
+
width: maxX - minX,
|
|
795
|
+
height: maxY - minY,
|
|
796
|
+
top: minY,
|
|
797
|
+
right: maxX,
|
|
798
|
+
bottom: maxY,
|
|
799
|
+
left: minX,
|
|
800
|
+
toJSON() {
|
|
801
|
+
return this;
|
|
802
|
+
}
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
if (!svgElementPrototype.getComputedTextLength) {
|
|
808
|
+
Object.defineProperty(svgElementPrototype, "getComputedTextLength", {
|
|
809
|
+
configurable: true,
|
|
810
|
+
writable: true,
|
|
811
|
+
value() {
|
|
812
|
+
const text = this.textContent ?? "";
|
|
813
|
+
return Math.max(text.length * 8, 16);
|
|
814
|
+
}
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
const nodeMermaidRenderer = createNodeMermaidRenderer();
|
|
819
|
+
const wenyanCoreInstance = await createWenyanCore({
|
|
820
|
+
mermaid: {
|
|
821
|
+
enabled: true,
|
|
822
|
+
renderer: nodeMermaidRenderer
|
|
823
|
+
}
|
|
824
|
+
});
|
|
825
|
+
const wenyanCoreInstanceWithoutMermaid = await createWenyanCore();
|
|
664
826
|
async function renderWithTheme(markdownContent, options) {
|
|
665
827
|
if (!markdownContent) {
|
|
666
828
|
throw new Error("No content provided for rendering.");
|
|
@@ -676,21 +838,22 @@ async function renderWithTheme(markdownContent, options) {
|
|
|
676
838
|
if (!handledCustomTheme && !theme) {
|
|
677
839
|
throw new Error(`theme "${theme}" not found.`);
|
|
678
840
|
}
|
|
841
|
+
const coreInstance = options.mermaid === false ? wenyanCoreInstanceWithoutMermaid : wenyanCoreInstance;
|
|
679
842
|
const gzhContent = await renderStyledContent(markdownContent, {
|
|
680
843
|
themeId: theme,
|
|
681
844
|
hlThemeId: highlight,
|
|
682
845
|
isMacStyle: macStyle,
|
|
683
846
|
isAddFootnote: footnote,
|
|
684
847
|
themeCss: handledCustomTheme
|
|
685
|
-
});
|
|
848
|
+
}, coreInstance);
|
|
686
849
|
return gzhContent;
|
|
687
850
|
}
|
|
688
|
-
async function renderStyledContent(content, options = {}) {
|
|
689
|
-
const html = await
|
|
851
|
+
async function renderStyledContent(content, options = {}, coreInstance = wenyanCoreInstance) {
|
|
852
|
+
const html = await coreInstance.renderMarkdown(content);
|
|
690
853
|
const dom = new JSDOM(`<body><section id="wenyan">${html}</section></body>`);
|
|
691
854
|
const document = dom.window.document;
|
|
692
855
|
const wenyan = document.getElementById("wenyan");
|
|
693
|
-
const result = await
|
|
856
|
+
const result = await coreInstance.applyStylesWithTheme(wenyan, options);
|
|
694
857
|
return result;
|
|
695
858
|
}
|
|
696
859
|
async function prepareRenderContext(inputContent, options, getInputContent) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wenyan-md/core",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.7",
|
|
4
4
|
"description": "Core library for Wenyan markdown rendering & publishing",
|
|
5
5
|
"author": "Lei <caol64@gmail.com> (https://github.com/caol64)",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -65,7 +65,8 @@
|
|
|
65
65
|
"highlight.js": "11.10.0",
|
|
66
66
|
"marked": "^15.0.12",
|
|
67
67
|
"marked-highlight": "^2.2.1",
|
|
68
|
-
"mathjax-full": "3.2.2"
|
|
68
|
+
"mathjax-full": "3.2.2",
|
|
69
|
+
"mermaid": "11.14.0"
|
|
69
70
|
},
|
|
70
71
|
"peerDependencies": {
|
|
71
72
|
"form-data-encoder": "^4.1.0",
|