@wenyan-md/core 3.0.7 → 3.0.9
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 +177 -51
- package/dist/publish.js +60 -22
- package/dist/types/core/index.d.ts +1 -1
- package/dist/types/core/parser/frontMatterParser.d.ts +1 -0
- package/dist/types/core/renderer/mermaidPostRender.d.ts +1 -0
- package/dist/types/core/utils.d.ts +1 -0
- package/dist/types/credentialStore.d.ts +18 -5
- package/dist/types/publish.d.ts +1 -0
- package/dist/types/tokenStore.d.ts +3 -2
- package/dist/wrapper.js +63 -7
- package/package.json +1 -1
package/dist/core.js
CHANGED
|
@@ -100,6 +100,12 @@ async function loadCssBySource(source) {
|
|
|
100
100
|
throw new Error("Unknown source type");
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
|
+
function createSvgDataUrl(svg) {
|
|
104
|
+
if (!svg.hasAttribute("xmlns")) {
|
|
105
|
+
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
106
|
+
}
|
|
107
|
+
return `data:image/svg+xml,${encodeURIComponent(svg.outerHTML)}`;
|
|
108
|
+
}
|
|
103
109
|
const __vite_glob_0_0$1 = "pre{background:#282c34}pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#abb2bf;background:#282c34}.hljs-comment,.hljs-quote{color:#5c6370;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#c678dd}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e06c75}.hljs-literal{color:#56b6c2}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#98c379}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#d19a66}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#61aeee}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#e6c07b}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline}";
|
|
104
110
|
const __vite_glob_0_1$1 = "pre{background:#fafafa}pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#383a42;background:#fafafa}.hljs-comment,.hljs-quote{color:#a0a1a7;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#a626a4}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e45649}.hljs-literal{color:#0184bb}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#50a14f}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#986801}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#4078f2}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#c18401}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline}";
|
|
105
111
|
const __vite_glob_0_2$1 = "pre{background:#282936}pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#e9e9f4;background:#282936}.hljs ::selection,.hljs::selection{background-color:#4d4f68;color:#e9e9f4}.hljs-comment{color:#626483}.hljs-tag{color:#62d6e8}.hljs-operator,.hljs-punctuation,.hljs-subst{color:#e9e9f4}.hljs-operator{opacity:.7}.hljs-bullet,.hljs-deletion,.hljs-name,.hljs-selector-tag,.hljs-template-variable,.hljs-variable{color:#ea51b2}.hljs-attr,.hljs-link,.hljs-literal,.hljs-number,.hljs-symbol,.hljs-variable.constant_{color:#b45bcf}.hljs-class .hljs-title,.hljs-title,.hljs-title.class_{color:#00f769}.hljs-strong{font-weight:700;color:#00f769}.hljs-addition,.hljs-code,.hljs-string,.hljs-title.class_.inherited__{color:#ebff87}.hljs-built_in,.hljs-doctag,.hljs-keyword.hljs-atrule,.hljs-quote,.hljs-regexp{color:#a1efe4}.hljs-attribute,.hljs-function .hljs-title,.hljs-section,.hljs-title.function_,.ruby .hljs-property{color:#62d6e8}.diff .hljs-meta,.hljs-keyword,.hljs-template-tag,.hljs-type{color:#b45bcf}.hljs-emphasis{color:#b45bcf;font-style:italic}.hljs-meta,.hljs-meta .hljs-keyword,.hljs-meta .hljs-string{color:#00f769}.hljs-meta .hljs-keyword,.hljs-meta-keyword{font-weight:700}\n";
|
|
@@ -196,7 +202,7 @@ async function handleFrontMatter(markdown) {
|
|
|
196
202
|
const { attributes, body } = fm(markdown);
|
|
197
203
|
const result = { content: body || "" };
|
|
198
204
|
let head = "";
|
|
199
|
-
const { title, description, cover, author, source_url, need_open_comment, only_fans_can_comment, image_list } = attributes;
|
|
205
|
+
const { title, description, cover, author, source_url, need_open_comment, only_fans_can_comment, image_list, type } = attributes;
|
|
200
206
|
if (title) {
|
|
201
207
|
result.title = title;
|
|
202
208
|
}
|
|
@@ -207,9 +213,6 @@ async function handleFrontMatter(markdown) {
|
|
|
207
213
|
if (cover) {
|
|
208
214
|
result.cover = cover;
|
|
209
215
|
}
|
|
210
|
-
if (head) {
|
|
211
|
-
result.content = head + result.content;
|
|
212
|
-
}
|
|
213
216
|
if (author) {
|
|
214
217
|
result.author = author;
|
|
215
218
|
}
|
|
@@ -225,6 +228,12 @@ async function handleFrontMatter(markdown) {
|
|
|
225
228
|
if (image_list) {
|
|
226
229
|
result.image_list = image_list;
|
|
227
230
|
}
|
|
231
|
+
if (type) {
|
|
232
|
+
result.type = type;
|
|
233
|
+
}
|
|
234
|
+
if (head) {
|
|
235
|
+
result.content = head + result.content;
|
|
236
|
+
}
|
|
228
237
|
return result;
|
|
229
238
|
}
|
|
230
239
|
const parseOptions = {
|
|
@@ -446,15 +455,65 @@ function createMarkedClient() {
|
|
|
446
455
|
const match = rule.exec(src);
|
|
447
456
|
if (!match) return;
|
|
448
457
|
const isImage = !!match[1];
|
|
458
|
+
const text = match[2];
|
|
459
|
+
const inner = match[3].trim();
|
|
460
|
+
let href = "";
|
|
461
|
+
let title = "";
|
|
462
|
+
const titleMatch = inner.match(/(.*?)\s+["']([^"']*)["']$/);
|
|
463
|
+
if (titleMatch) {
|
|
464
|
+
href = titleMatch[1].trim();
|
|
465
|
+
title = titleMatch[2];
|
|
466
|
+
} else {
|
|
467
|
+
href = inner;
|
|
468
|
+
}
|
|
449
469
|
return {
|
|
450
470
|
type: isImage ? "image" : "link",
|
|
451
471
|
raw: match[0],
|
|
452
|
-
text
|
|
453
|
-
href
|
|
454
|
-
|
|
472
|
+
text,
|
|
473
|
+
href,
|
|
474
|
+
title,
|
|
475
|
+
tokens: this.lexer.inlineTokens(text)
|
|
455
476
|
};
|
|
456
477
|
}
|
|
457
478
|
},
|
|
479
|
+
// Obsidian WikiLinks 图片语法扩展 ![[filename]] / ![[filename|alt]] / ![[filename|width]] / ![[filename|widthxheight]]
|
|
480
|
+
{
|
|
481
|
+
name: "wikiImage",
|
|
482
|
+
level: "inline",
|
|
483
|
+
start(src) {
|
|
484
|
+
return src.match(/!\[\[/)?.index;
|
|
485
|
+
},
|
|
486
|
+
tokenizer(src) {
|
|
487
|
+
const rule = /^!\[\[([^\]|]+?)(?:\|([^\]]*))?\]\]/;
|
|
488
|
+
const match = rule.exec(src);
|
|
489
|
+
if (!match) return;
|
|
490
|
+
const href = match[1].trim();
|
|
491
|
+
const modifier = match[2]?.trim() ?? "";
|
|
492
|
+
const dimOnly = /^(\d+)(?:x(\d+))?$/.exec(modifier);
|
|
493
|
+
const alt = dimOnly ? "" : modifier;
|
|
494
|
+
const width = dimOnly ? dimOnly[1] : "";
|
|
495
|
+
const height = dimOnly ? dimOnly[2] ?? "" : "";
|
|
496
|
+
return {
|
|
497
|
+
type: "wikiImage",
|
|
498
|
+
raw: match[0],
|
|
499
|
+
href,
|
|
500
|
+
alt,
|
|
501
|
+
width,
|
|
502
|
+
height,
|
|
503
|
+
tokens: []
|
|
504
|
+
};
|
|
505
|
+
},
|
|
506
|
+
renderer(token) {
|
|
507
|
+
const href = normalizeHref(token.href);
|
|
508
|
+
const altAttr = token.alt ? ` alt="${token.alt}"` : "";
|
|
509
|
+
const titleAttr = token.alt ? ` title="${token.alt}"` : "";
|
|
510
|
+
const styleParts = [];
|
|
511
|
+
if (token.width) styleParts.push(`width:${token.width}px`);
|
|
512
|
+
if (token.height) styleParts.push(`height:${token.height}px`);
|
|
513
|
+
const styleAttr = styleParts.length ? ` style="${styleParts.join("; ")}"` : "";
|
|
514
|
+
return `<img src="${href}"${altAttr}${titleAttr}${styleAttr}>`;
|
|
515
|
+
}
|
|
516
|
+
},
|
|
458
517
|
// 自定义图片语法扩展 ![](){...}
|
|
459
518
|
{
|
|
460
519
|
name: "attributeImage",
|
|
@@ -634,24 +693,7 @@ function containsMermaidCodeBlock(html) {
|
|
|
634
693
|
return html.includes("language-mermaid");
|
|
635
694
|
}
|
|
636
695
|
const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
|
637
|
-
function
|
|
638
|
-
const mathElements = element.querySelectorAll("mjx-container");
|
|
639
|
-
mathElements.forEach((mathContainer) => {
|
|
640
|
-
const svg = mathContainer.querySelector("svg");
|
|
641
|
-
if (!svg) return;
|
|
642
|
-
svg.style.width = svg.getAttribute("width") || "";
|
|
643
|
-
svg.style.height = svg.getAttribute("height") || "";
|
|
644
|
-
svg.removeAttribute("width");
|
|
645
|
-
svg.removeAttribute("height");
|
|
646
|
-
const parent = mathContainer.parentElement;
|
|
647
|
-
if (parent) {
|
|
648
|
-
mathContainer.remove();
|
|
649
|
-
parent.appendChild(svg);
|
|
650
|
-
if (parent.classList.contains("block-equation")) {
|
|
651
|
-
parent.setAttribute("style", "text-align: center; margin-bottom: 1rem;");
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
});
|
|
696
|
+
function postRenderMermaidDiagrams(element) {
|
|
655
697
|
const mermaidSvgs = element.querySelectorAll('[data-wenyan-diagram="mermaid"] svg');
|
|
656
698
|
mermaidSvgs.forEach((svg) => {
|
|
657
699
|
svg.style.maxWidth = "100%";
|
|
@@ -659,21 +701,6 @@ function wechatPostRender(element) {
|
|
|
659
701
|
inlineMermaidSvgStyles(svg);
|
|
660
702
|
flattenMermaidMarkers(svg);
|
|
661
703
|
});
|
|
662
|
-
const codeElements = element.querySelectorAll("pre code");
|
|
663
|
-
codeElements.forEach((codeEl) => {
|
|
664
|
-
codeEl.innerHTML = codeEl.innerHTML.replace(/\n/g, "<br>").replace(/(>[^<]+)|(^[^<]+)/g, (str) => str.replace(/\s/g, " "));
|
|
665
|
-
});
|
|
666
|
-
const listElements = element.querySelectorAll("li");
|
|
667
|
-
listElements.forEach((li) => {
|
|
668
|
-
const doc = element.ownerDocument;
|
|
669
|
-
const section = doc.createElement("section");
|
|
670
|
-
while (li.firstChild) {
|
|
671
|
-
section.appendChild(li.firstChild);
|
|
672
|
-
}
|
|
673
|
-
li.appendChild(section);
|
|
674
|
-
});
|
|
675
|
-
element.style.color = "rgb(0, 0, 0)";
|
|
676
|
-
element.style.caretColor = "rgb(0, 0, 0)";
|
|
677
704
|
}
|
|
678
705
|
function inlineMermaidSvgStyles(svg) {
|
|
679
706
|
const styleElements = Array.from(svg.querySelectorAll("style"));
|
|
@@ -937,6 +964,41 @@ function isInlineStyleTarget(node) {
|
|
|
937
964
|
}
|
|
938
965
|
return node instanceof defaultView.SVGElement || node instanceof defaultView.HTMLElement;
|
|
939
966
|
}
|
|
967
|
+
function wechatPostRender(element) {
|
|
968
|
+
const mathElements = element.querySelectorAll("mjx-container");
|
|
969
|
+
mathElements.forEach((mathContainer) => {
|
|
970
|
+
const svg = mathContainer.querySelector("svg");
|
|
971
|
+
if (!svg) return;
|
|
972
|
+
svg.style.width = svg.getAttribute("width") || "";
|
|
973
|
+
svg.style.height = svg.getAttribute("height") || "";
|
|
974
|
+
svg.removeAttribute("width");
|
|
975
|
+
svg.removeAttribute("height");
|
|
976
|
+
const parent = mathContainer.parentElement;
|
|
977
|
+
if (parent) {
|
|
978
|
+
mathContainer.remove();
|
|
979
|
+
parent.appendChild(svg);
|
|
980
|
+
if (parent.classList.contains("block-equation")) {
|
|
981
|
+
parent.setAttribute("style", "text-align: center; margin-bottom: 1rem;");
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
});
|
|
985
|
+
postRenderMermaidDiagrams(element);
|
|
986
|
+
const codeElements = element.querySelectorAll("pre code");
|
|
987
|
+
codeElements.forEach((codeEl) => {
|
|
988
|
+
codeEl.innerHTML = codeEl.innerHTML.replace(/\n/g, "<br>").replace(/(>[^<]+)|(^[^<]+)/g, (str) => str.replace(/\s/g, " "));
|
|
989
|
+
});
|
|
990
|
+
const listElements = element.querySelectorAll("li");
|
|
991
|
+
listElements.forEach((li) => {
|
|
992
|
+
const doc = element.ownerDocument;
|
|
993
|
+
const section = doc.createElement("section");
|
|
994
|
+
while (li.firstChild) {
|
|
995
|
+
section.appendChild(li.firstChild);
|
|
996
|
+
}
|
|
997
|
+
li.appendChild(section);
|
|
998
|
+
});
|
|
999
|
+
element.style.color = "rgb(0, 0, 0)";
|
|
1000
|
+
element.style.caretColor = "rgb(0, 0, 0)";
|
|
1001
|
+
}
|
|
940
1002
|
const themeModifier = createCssModifier({});
|
|
941
1003
|
function renderTheme(wenyanElement, themeCss) {
|
|
942
1004
|
const modifiedCss = themeModifier(themeCss);
|
|
@@ -1480,6 +1542,8 @@ function getContentForZhihu(wenyanElement) {
|
|
|
1480
1542
|
return wenyanElement.outerHTML;
|
|
1481
1543
|
}
|
|
1482
1544
|
function getContentForToutiao(wenyanElement) {
|
|
1545
|
+
postRenderMermaidDiagrams(wenyanElement);
|
|
1546
|
+
processMermaid(wenyanElement);
|
|
1483
1547
|
const containers = wenyanElement.querySelectorAll("mjx-container");
|
|
1484
1548
|
const doc = wenyanElement.ownerDocument;
|
|
1485
1549
|
containers.forEach((container) => {
|
|
@@ -1488,11 +1552,7 @@ function getContentForToutiao(wenyanElement) {
|
|
|
1488
1552
|
return;
|
|
1489
1553
|
}
|
|
1490
1554
|
const img = doc.createElement("img");
|
|
1491
|
-
|
|
1492
|
-
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
1493
|
-
}
|
|
1494
|
-
const encodedSVG = encodeURIComponent(svg.outerHTML);
|
|
1495
|
-
img.src = `data:image/svg+xml,${encodedSVG}`;
|
|
1555
|
+
img.src = createSvgDataUrl(svg);
|
|
1496
1556
|
const style = svg.getAttribute("style");
|
|
1497
1557
|
if (style) {
|
|
1498
1558
|
img.setAttribute("style", style);
|
|
@@ -1507,10 +1567,67 @@ function getContentForToutiao(wenyanElement) {
|
|
|
1507
1567
|
});
|
|
1508
1568
|
return wenyanElement.outerHTML;
|
|
1509
1569
|
}
|
|
1570
|
+
function processMermaid(root) {
|
|
1571
|
+
const figures = root.querySelectorAll('[data-wenyan-diagram="mermaid"]');
|
|
1572
|
+
const doc = root.ownerDocument;
|
|
1573
|
+
figures.forEach((figure) => {
|
|
1574
|
+
const svg = figure.querySelector("svg");
|
|
1575
|
+
if (!svg) {
|
|
1576
|
+
return;
|
|
1577
|
+
}
|
|
1578
|
+
let svgString = svg.outerHTML;
|
|
1579
|
+
svgString = fixMermaidSvgForServerRender(svgString);
|
|
1580
|
+
const img = doc.createElement("img");
|
|
1581
|
+
const encodedData = btoa(unescape(encodeURIComponent(svgString)));
|
|
1582
|
+
img.src = `data:image/svg+xml;base64,${encodedData}`;
|
|
1583
|
+
const style = svg.getAttribute("style");
|
|
1584
|
+
if (style) {
|
|
1585
|
+
img.setAttribute("style", style);
|
|
1586
|
+
}
|
|
1587
|
+
const ariaLabel = figure.getAttribute("aria-label") || figure.getAttribute("title") || svg.getAttribute("aria-label") || svg.getAttribute("title");
|
|
1588
|
+
if (ariaLabel) {
|
|
1589
|
+
img.alt = ariaLabel;
|
|
1590
|
+
}
|
|
1591
|
+
figure.replaceWith(img);
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1594
|
+
function fixMermaidSvgForServerRender(svgString) {
|
|
1595
|
+
const baseFontSize = 16;
|
|
1596
|
+
let fixedSvg = svgString;
|
|
1597
|
+
fixedSvg = fixedSvg.replace(/([xy]|dx|dy)="([-0-9.]+)em"/g, (_, attr, val) => {
|
|
1598
|
+
const pxValue = parseFloat(val) * baseFontSize;
|
|
1599
|
+
return `${attr}="${pxValue}px"`;
|
|
1600
|
+
});
|
|
1601
|
+
let previousSvg;
|
|
1602
|
+
do {
|
|
1603
|
+
previousSvg = fixedSvg;
|
|
1604
|
+
fixedSvg = fixedSvg.replace(/<tspan(?![^>]*\b[xy]=)[^>]*>([\s\S]*?)<\/tspan>/g, "$1");
|
|
1605
|
+
} while (fixedSvg !== previousSvg);
|
|
1606
|
+
fixedSvg = fixedSvg.replace(/<tspan([^>]+)>/g, (match, attrs) => {
|
|
1607
|
+
const yMatch = attrs.match(/y="([-0-9.]+)(?:px)?"/);
|
|
1608
|
+
const dyMatch = attrs.match(/dy="([-0-9.]+)(?:px)?"/);
|
|
1609
|
+
if (yMatch && dyMatch) {
|
|
1610
|
+
const yVal = parseFloat(yMatch[1]);
|
|
1611
|
+
const dyVal = parseFloat(dyMatch[1]);
|
|
1612
|
+
const finalY = yVal + dyVal;
|
|
1613
|
+
let newAttrs = attrs.replace(/y="[-0-9.]+(?:px)?"\s*/, "").replace(/dy="[-0-9.]+(?:px)?"\s*/, "");
|
|
1614
|
+
return `<tspan ${newAttrs.trim()} y="${finalY}">`;
|
|
1615
|
+
}
|
|
1616
|
+
return match;
|
|
1617
|
+
});
|
|
1618
|
+
fixedSvg = fixedSvg.replace(/class="([^"]*?)text-inner-tspan([^"]*?)"/g, 'class="$1$2"');
|
|
1619
|
+
fixedSvg = fixedSvg.replace(/\sclass=""/g, "");
|
|
1620
|
+
return fixedSvg;
|
|
1621
|
+
}
|
|
1622
|
+
const DEFAULT_MERMAID_FONT_FAMILY = "'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Noto Sans CJK SC', 'Source Han Sans SC', Arial, sans-serif";
|
|
1510
1623
|
const DEFAULT_MERMAID_CONFIG = {
|
|
1624
|
+
fontFamily: DEFAULT_MERMAID_FONT_FAMILY,
|
|
1511
1625
|
htmlLabels: false,
|
|
1512
1626
|
flowchart: {
|
|
1513
1627
|
htmlLabels: false
|
|
1628
|
+
},
|
|
1629
|
+
themeVariables: {
|
|
1630
|
+
fontFamily: DEFAULT_MERMAID_FONT_FAMILY
|
|
1514
1631
|
}
|
|
1515
1632
|
};
|
|
1516
1633
|
async function replaceMermaidCodeBlocks(root, renderDiagram) {
|
|
@@ -1519,7 +1636,7 @@ async function replaceMermaidCodeBlocks(root, renderDiagram) {
|
|
|
1519
1636
|
return false;
|
|
1520
1637
|
}
|
|
1521
1638
|
const ownerDocument = getOwnerDocument(root);
|
|
1522
|
-
for (const [
|
|
1639
|
+
for (const [_, codeBlock] of codeBlocks.entries()) {
|
|
1523
1640
|
const pre = codeBlock.parentElement;
|
|
1524
1641
|
const parent = pre?.parentElement;
|
|
1525
1642
|
if (!pre || !parent) {
|
|
@@ -1527,7 +1644,7 @@ async function replaceMermaidCodeBlocks(root, renderDiagram) {
|
|
|
1527
1644
|
}
|
|
1528
1645
|
const code = codeBlock.textContent ?? "";
|
|
1529
1646
|
const svg = await renderDiagram({
|
|
1530
|
-
id: `wenyan-mermaid-${
|
|
1647
|
+
id: `wenyan-mermaid-${crypto.randomUUID()}`,
|
|
1531
1648
|
code
|
|
1532
1649
|
});
|
|
1533
1650
|
const figure = ownerDocument.createElement("figure");
|
|
@@ -1548,7 +1665,6 @@ function createBrowserMermaidRenderer(options = {}) {
|
|
|
1548
1665
|
const document = parser.parseFromString(`<body>${html}</body>`, "text/html");
|
|
1549
1666
|
const root = document.body;
|
|
1550
1667
|
const mermaid = await getMermaidModule();
|
|
1551
|
-
mermaid.initialize(createMermaidConfig(options.mermaidConfig));
|
|
1552
1668
|
try {
|
|
1553
1669
|
await replaceMermaidCodeBlocks(root, async ({ id, code }) => {
|
|
1554
1670
|
const { svg } = await mermaid.render(id, code);
|
|
@@ -1565,7 +1681,9 @@ function createBrowserMermaidRenderer(options = {}) {
|
|
|
1565
1681
|
mermaidModulePromise = import("mermaid");
|
|
1566
1682
|
}
|
|
1567
1683
|
const module = await mermaidModulePromise;
|
|
1568
|
-
|
|
1684
|
+
const mermaid = module.default;
|
|
1685
|
+
mermaid.initialize(createMermaidConfig(options.mermaidConfig));
|
|
1686
|
+
return mermaid;
|
|
1569
1687
|
}
|
|
1570
1688
|
}
|
|
1571
1689
|
function createMermaidRenderError(error) {
|
|
@@ -1574,6 +1692,7 @@ function createMermaidRenderError(error) {
|
|
|
1574
1692
|
}
|
|
1575
1693
|
function createMermaidConfig(overrides = {}) {
|
|
1576
1694
|
const flowchartOverrides = getRecord(overrides.flowchart);
|
|
1695
|
+
const themeVariablesOverrides = getRecord(overrides.themeVariables);
|
|
1577
1696
|
return {
|
|
1578
1697
|
...DEFAULT_MERMAID_CONFIG,
|
|
1579
1698
|
startOnLoad: false,
|
|
@@ -1582,6 +1701,10 @@ function createMermaidConfig(overrides = {}) {
|
|
|
1582
1701
|
flowchart: {
|
|
1583
1702
|
...getRecord(DEFAULT_MERMAID_CONFIG.flowchart),
|
|
1584
1703
|
...flowchartOverrides
|
|
1704
|
+
},
|
|
1705
|
+
themeVariables: {
|
|
1706
|
+
...getRecord(DEFAULT_MERMAID_CONFIG.themeVariables),
|
|
1707
|
+
...themeVariablesOverrides
|
|
1585
1708
|
}
|
|
1586
1709
|
};
|
|
1587
1710
|
}
|
|
@@ -1612,12 +1735,15 @@ async function createWenyanCore(options = {}) {
|
|
|
1612
1735
|
async handleFrontMatter(markdown) {
|
|
1613
1736
|
return await handleFrontMatter(markdown);
|
|
1614
1737
|
},
|
|
1615
|
-
async renderMarkdown(markdown) {
|
|
1738
|
+
async renderMarkdown(markdown, disableMermaid = false) {
|
|
1616
1739
|
let html = await markedClient.parse(markdown);
|
|
1617
1740
|
if (isConvertMathJax) {
|
|
1618
1741
|
html = mathJaxParser.parser(html);
|
|
1619
1742
|
}
|
|
1620
|
-
|
|
1743
|
+
if (!disableMermaid) {
|
|
1744
|
+
html = await mermaidParser.parser(html);
|
|
1745
|
+
}
|
|
1746
|
+
return html;
|
|
1621
1747
|
},
|
|
1622
1748
|
async applyStylesWithTheme(wenyanElement, options2 = {}) {
|
|
1623
1749
|
const {
|
package/dist/publish.js
CHANGED
|
@@ -16,7 +16,7 @@ class TokenStore {
|
|
|
16
16
|
try {
|
|
17
17
|
const loadedData = await this.adapter.loadToken();
|
|
18
18
|
if (loadedData) {
|
|
19
|
-
this.cache = loadedData;
|
|
19
|
+
this.cache = { ...defaultTokenCache, ...loadedData };
|
|
20
20
|
}
|
|
21
21
|
} catch (error) {
|
|
22
22
|
throw new Error(`无法加载 token: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -32,15 +32,16 @@ class TokenStore {
|
|
|
32
32
|
async waitForInit() {
|
|
33
33
|
await this.initPromise;
|
|
34
34
|
}
|
|
35
|
-
isValid(appid) {
|
|
35
|
+
async isValid(appid) {
|
|
36
|
+
await this.initPromise;
|
|
36
37
|
const currentTime = Math.floor(Date.now() / 1e3);
|
|
37
38
|
const bufferTime = 600;
|
|
38
39
|
const isAppidMatch = this.cache.appid === appid;
|
|
39
|
-
const isNotExpired = this.cache.expireAt > currentTime + bufferTime;
|
|
40
|
+
const isNotExpired = this.cache.expireAt < 0 ? true : this.cache.expireAt > currentTime + bufferTime;
|
|
40
41
|
return isAppidMatch && isNotExpired;
|
|
41
42
|
}
|
|
42
|
-
getToken(appid) {
|
|
43
|
-
return this.isValid(appid) ? this.cache.accessToken : null;
|
|
43
|
+
async getToken(appid) {
|
|
44
|
+
return await this.isValid(appid) ? this.cache.accessToken : null;
|
|
44
45
|
}
|
|
45
46
|
async setToken(appid, accessToken, expiresIn) {
|
|
46
47
|
await this.initPromise;
|
|
@@ -52,6 +53,16 @@ class TokenStore {
|
|
|
52
53
|
};
|
|
53
54
|
await this.save();
|
|
54
55
|
}
|
|
56
|
+
async setExternalToken(appid, accessToken) {
|
|
57
|
+
await this.initPromise;
|
|
58
|
+
this.cache = {
|
|
59
|
+
appid,
|
|
60
|
+
accessToken,
|
|
61
|
+
expireAt: -1
|
|
62
|
+
// 标记为外部托管,永不校验过期
|
|
63
|
+
};
|
|
64
|
+
await this.save();
|
|
65
|
+
}
|
|
55
66
|
async clear() {
|
|
56
67
|
await this.initPromise;
|
|
57
68
|
this.cache = { ...defaultTokenCache };
|
|
@@ -115,12 +126,14 @@ class CredentialStore {
|
|
|
115
126
|
constructor(adapter) {
|
|
116
127
|
this.adapter = adapter;
|
|
117
128
|
this.initPromise = this.load();
|
|
129
|
+
this.initPromise.catch(() => {
|
|
130
|
+
});
|
|
118
131
|
}
|
|
119
132
|
async load() {
|
|
120
133
|
try {
|
|
121
134
|
const loadedData = await this.adapter.loadCredential();
|
|
122
135
|
if (loadedData) {
|
|
123
|
-
this.credential = loadedData;
|
|
136
|
+
this.credential = { ...defaultCredential, ...loadedData };
|
|
124
137
|
}
|
|
125
138
|
} catch (error) {
|
|
126
139
|
throw new Error(`无法加载凭据: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -133,27 +146,47 @@ class CredentialStore {
|
|
|
133
146
|
throw new Error(`无法保存凭据: ${error instanceof Error ? error.message : String(error)}`);
|
|
134
147
|
}
|
|
135
148
|
}
|
|
136
|
-
|
|
149
|
+
/**
|
|
150
|
+
* 获取微信凭据 (通过 appId 或 alias)
|
|
151
|
+
*/
|
|
152
|
+
async getWechatCredential(appIdOrAlias) {
|
|
137
153
|
await this.initPromise;
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
154
|
+
const wechat = this.credential.wechat ?? {};
|
|
155
|
+
if (wechat[appIdOrAlias]) {
|
|
156
|
+
return {
|
|
157
|
+
appId: appIdOrAlias,
|
|
158
|
+
...wechat[appIdOrAlias]
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
const entry = Object.entries(wechat).find(([_, item]) => item.alias === appIdOrAlias);
|
|
162
|
+
if (entry) {
|
|
163
|
+
return {
|
|
164
|
+
appId: entry[0],
|
|
165
|
+
...entry[1]
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
146
169
|
}
|
|
147
|
-
|
|
170
|
+
/**
|
|
171
|
+
* 保存或更新微信凭据
|
|
172
|
+
*/
|
|
173
|
+
async saveWechatCredential(appId, appSecret, alias) {
|
|
148
174
|
await this.initPromise;
|
|
149
175
|
this.credential.wechat ??= {};
|
|
150
|
-
|
|
176
|
+
const item = { appSecret };
|
|
177
|
+
if (alias) {
|
|
178
|
+
item.alias = alias;
|
|
179
|
+
}
|
|
180
|
+
this.credential.wechat[appId] = item;
|
|
151
181
|
await this.save();
|
|
152
182
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
183
|
+
/**
|
|
184
|
+
* 删除微信凭据 (通过 appId 或 alias)
|
|
185
|
+
*/
|
|
186
|
+
async deleteWechatCredential(appIdOrAlias) {
|
|
187
|
+
const target = await this.getWechatCredential(appIdOrAlias);
|
|
188
|
+
if (target && this.credential.wechat) {
|
|
189
|
+
delete this.credential.wechat[target.appId];
|
|
157
190
|
await this.save();
|
|
158
191
|
}
|
|
159
192
|
}
|
|
@@ -177,7 +210,7 @@ class WechatPublisher {
|
|
|
177
210
|
const result2 = await this.fetchAccessToken(appId, appSecret);
|
|
178
211
|
return result2.access_token;
|
|
179
212
|
}
|
|
180
|
-
const cached = this.tokenStore.getToken(appId);
|
|
213
|
+
const cached = await this.tokenStore.getToken(appId);
|
|
181
214
|
if (cached) {
|
|
182
215
|
return cached;
|
|
183
216
|
}
|
|
@@ -217,6 +250,11 @@ class WechatPublisher {
|
|
|
217
250
|
await this.uploadCacheStore.clear();
|
|
218
251
|
}
|
|
219
252
|
}
|
|
253
|
+
async setExternalToken(appid, accessToken) {
|
|
254
|
+
if (this.tokenStore) {
|
|
255
|
+
await this.tokenStore.setExternalToken(appid, accessToken);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
220
258
|
}
|
|
221
259
|
export {
|
|
222
260
|
CredentialStore,
|
|
@@ -15,7 +15,7 @@ export interface ApplyStylesOptions {
|
|
|
15
15
|
}
|
|
16
16
|
export declare function createWenyanCore(options?: WenyanOptions): Promise<{
|
|
17
17
|
handleFrontMatter(markdown: string): Promise<FrontMatterResult>;
|
|
18
|
-
renderMarkdown(markdown: string): Promise<string>;
|
|
18
|
+
renderMarkdown(markdown: string, disableMermaid?: boolean): Promise<string>;
|
|
19
19
|
applyStylesWithTheme(wenyanElement: HTMLElement, options?: ApplyStylesOptions): Promise<string>;
|
|
20
20
|
applyStylesWithResolvedCss(wenyanElement: HTMLElement, options: {
|
|
21
21
|
themeCss: string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function postRenderMermaidDiagrams(element: HTMLElement): void;
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
export interface WechatCredentialItem {
|
|
2
|
+
appSecret: string;
|
|
3
|
+
alias?: string;
|
|
4
|
+
}
|
|
1
5
|
export interface WenyanCredential {
|
|
2
|
-
wechat?: Record<string,
|
|
6
|
+
wechat?: Record<string, WechatCredentialItem>;
|
|
3
7
|
}
|
|
4
8
|
export declare const defaultCredential: WenyanCredential;
|
|
5
9
|
export interface CredentialStorageAdapter {
|
|
@@ -14,11 +18,20 @@ export declare class CredentialStore {
|
|
|
14
18
|
constructor(adapter: CredentialStorageAdapter);
|
|
15
19
|
private load;
|
|
16
20
|
private save;
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
/**
|
|
22
|
+
* 获取微信凭据 (通过 appId 或 alias)
|
|
23
|
+
*/
|
|
24
|
+
getWechatCredential(appIdOrAlias: string): Promise<{
|
|
19
25
|
appId: string;
|
|
20
26
|
appSecret: string;
|
|
27
|
+
alias?: string;
|
|
21
28
|
} | null>;
|
|
22
|
-
|
|
23
|
-
|
|
29
|
+
/**
|
|
30
|
+
* 保存或更新微信凭据
|
|
31
|
+
*/
|
|
32
|
+
saveWechatCredential(appId: string, appSecret: string, alias?: string | null): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* 删除微信凭据 (通过 appId 或 alias)
|
|
35
|
+
*/
|
|
36
|
+
deleteWechatCredential(appIdOrAlias: string): Promise<void>;
|
|
24
37
|
}
|
package/dist/types/publish.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export declare class WechatPublisher {
|
|
|
25
25
|
uploadImage(file: Blob, filename: string, accessToken: string, appId?: string): Promise<WechatUploadResponse>;
|
|
26
26
|
publishToDraft(accessToken: string, options: WechatPublishOptions): Promise<WechatPublishResponse>;
|
|
27
27
|
clearCache(): Promise<void>;
|
|
28
|
+
setExternalToken(appid: string, accessToken: string): Promise<void>;
|
|
28
29
|
}
|
|
29
30
|
export * from "./tokenStore.js";
|
|
30
31
|
export * from "./uploadCacheStore.js";
|
|
@@ -17,8 +17,9 @@ export declare class TokenStore {
|
|
|
17
17
|
private load;
|
|
18
18
|
private save;
|
|
19
19
|
waitForInit(): Promise<void>;
|
|
20
|
-
isValid(appid: string): boolean
|
|
21
|
-
getToken(appid: string): string | null
|
|
20
|
+
isValid(appid: string): Promise<boolean>;
|
|
21
|
+
getToken(appid: string): Promise<string | null>;
|
|
22
22
|
setToken(appid: string, accessToken: string, expiresIn: number): Promise<void>;
|
|
23
|
+
setExternalToken(appid: string, accessToken: string): Promise<void>;
|
|
23
24
|
clear(): Promise<void>;
|
|
24
25
|
}
|
package/dist/wrapper.js
CHANGED
|
@@ -731,14 +731,14 @@ function ensureSvgPolyfills(window) {
|
|
|
731
731
|
value() {
|
|
732
732
|
const tagName = this.tagName.toLowerCase();
|
|
733
733
|
if (tagName === "text" || tagName === "tspan") {
|
|
734
|
-
const
|
|
735
|
-
const
|
|
734
|
+
const width = getEstimatedTextWidth(this);
|
|
735
|
+
const height = getEstimatedFontSize(this);
|
|
736
736
|
return {
|
|
737
737
|
x: 0,
|
|
738
|
-
y: -
|
|
738
|
+
y: -height,
|
|
739
739
|
width,
|
|
740
|
-
height
|
|
741
|
-
top: -
|
|
740
|
+
height,
|
|
741
|
+
top: -height,
|
|
742
742
|
right: width,
|
|
743
743
|
bottom: 0,
|
|
744
744
|
left: 0,
|
|
@@ -809,12 +809,61 @@ function ensureSvgPolyfills(window) {
|
|
|
809
809
|
configurable: true,
|
|
810
810
|
writable: true,
|
|
811
811
|
value() {
|
|
812
|
-
|
|
813
|
-
return Math.max(text.length * 8, 16);
|
|
812
|
+
return getEstimatedTextWidth(this);
|
|
814
813
|
}
|
|
815
814
|
});
|
|
816
815
|
}
|
|
817
816
|
}
|
|
817
|
+
function getEstimatedTextWidth(element) {
|
|
818
|
+
const text = element.textContent ?? "";
|
|
819
|
+
const fontSize = getEstimatedFontSize(element);
|
|
820
|
+
const visualLength = Array.from(text).reduce((length, character) => {
|
|
821
|
+
return length + (isWideCharacter(character) ? 2 : 1);
|
|
822
|
+
}, 0);
|
|
823
|
+
return Math.max(visualLength * (fontSize / 2), fontSize);
|
|
824
|
+
}
|
|
825
|
+
function getEstimatedFontSize(element) {
|
|
826
|
+
const inlineFontSize = element.getAttribute("font-size");
|
|
827
|
+
if (inlineFontSize) {
|
|
828
|
+
const parsed = Number.parseFloat(inlineFontSize);
|
|
829
|
+
if (Number.isFinite(parsed)) {
|
|
830
|
+
return parsed;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
const computedFontSize = element.ownerDocument.defaultView?.getComputedStyle(element).fontSize;
|
|
834
|
+
if (computedFontSize) {
|
|
835
|
+
const parsed = Number.parseFloat(computedFontSize);
|
|
836
|
+
if (Number.isFinite(parsed)) {
|
|
837
|
+
return parsed;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
return 16;
|
|
841
|
+
}
|
|
842
|
+
function isWideCharacter(character) {
|
|
843
|
+
return character.codePointAt(0) > 255;
|
|
844
|
+
}
|
|
845
|
+
async function extractAndCleanImages(body) {
|
|
846
|
+
const html = await wenyanCoreInstance.renderMarkdown(body);
|
|
847
|
+
const dom = new JSDOM(`<body><section id="wenyan">${html}</section></body>`);
|
|
848
|
+
const document = dom.window.document;
|
|
849
|
+
const wenyan = document.getElementById("wenyan");
|
|
850
|
+
const images = Array.from(wenyan.querySelectorAll("img"));
|
|
851
|
+
const imagePaths = images.map((img) => img.getAttribute("src")).filter((src) => !!src).map((src) => {
|
|
852
|
+
try {
|
|
853
|
+
return decodeURI(src);
|
|
854
|
+
} catch {
|
|
855
|
+
return src;
|
|
856
|
+
}
|
|
857
|
+
});
|
|
858
|
+
for (const img of images) {
|
|
859
|
+
const parent = img.parentElement;
|
|
860
|
+
img.remove();
|
|
861
|
+
if (parent && parent.tagName === "P" && parent.textContent?.trim() === "") {
|
|
862
|
+
parent.remove();
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
return { imagePaths, cleanedHtml: wenyan.outerHTML };
|
|
866
|
+
}
|
|
818
867
|
const nodeMermaidRenderer = createNodeMermaidRenderer();
|
|
819
868
|
const wenyanCoreInstance = await createWenyanCore({
|
|
820
869
|
mermaid: {
|
|
@@ -859,6 +908,13 @@ async function renderStyledContent(content, options = {}, coreInstance = wenyanC
|
|
|
859
908
|
async function prepareRenderContext(inputContent, options, getInputContent) {
|
|
860
909
|
const { content, absoluteDirPath } = await getInputContent(inputContent, options.file);
|
|
861
910
|
const preHandlerContent = await wenyanCoreInstance.handleFrontMatter(content);
|
|
911
|
+
if (preHandlerContent.type === "image" && !preHandlerContent.image_list) {
|
|
912
|
+
const { imagePaths, cleanedHtml } = await extractAndCleanImages(preHandlerContent.content);
|
|
913
|
+
if (imagePaths.length > 0) {
|
|
914
|
+
preHandlerContent.image_list = imagePaths;
|
|
915
|
+
preHandlerContent.content = cleanedHtml;
|
|
916
|
+
}
|
|
917
|
+
}
|
|
862
918
|
if (preHandlerContent.image_list && preHandlerContent.image_list.length > 0) {
|
|
863
919
|
return { gzhContent: preHandlerContent, absoluteDirPath };
|
|
864
920
|
}
|
package/package.json
CHANGED