@akairos/myno 0.0.0-stage → 0.1.4

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.
Files changed (227) hide show
  1. package/README.md +28 -2
  2. package/dist/.vite/manifest.json +1266 -0
  3. package/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  4. package/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  5. package/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  6. package/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  7. package/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  8. package/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  9. package/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  10. package/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  11. package/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  12. package/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  13. package/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  14. package/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  15. package/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  16. package/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  17. package/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  18. package/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  19. package/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  20. package/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  21. package/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  22. package/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  23. package/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  24. package/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  25. package/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  26. package/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  27. package/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  28. package/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  29. package/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  30. package/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  31. package/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  32. package/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  33. package/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  34. package/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  35. package/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  36. package/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  37. package/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  38. package/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  39. package/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  40. package/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  41. package/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  42. package/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  43. package/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  44. package/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  45. package/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  46. package/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  47. package/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  48. package/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  49. package/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  50. package/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  51. package/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  52. package/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  53. package/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  54. package/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  55. package/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  56. package/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  57. package/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  58. package/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  59. package/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  60. package/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  61. package/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  62. package/dist/assets/apl-B4CMkyY2.js +1 -0
  63. package/dist/assets/asciiarmor-Df11BRmG.js +1 -0
  64. package/dist/assets/asn1-EdZsLKOL.js +1 -0
  65. package/dist/assets/asterisk-B-8jnY81.js +1 -0
  66. package/dist/assets/brainfuck-C4LP7Hcl.js +1 -0
  67. package/dist/assets/clike-B9uivgTg.js +1 -0
  68. package/dist/assets/clojure-BMjYHr_A.js +1 -0
  69. package/dist/assets/cmake-BQqOBYOt.js +1 -0
  70. package/dist/assets/cobol-CWcv1MsR.js +1 -0
  71. package/dist/assets/coffeescript-S37ZYGWr.js +1 -0
  72. package/dist/assets/commonlisp-DBKNyK5s.js +1 -0
  73. package/dist/assets/crystal-SjHAIU92.js +1 -0
  74. package/dist/assets/css-BnMrqG3P.js +1 -0
  75. package/dist/assets/cypher-C_CwsFkJ.js +1 -0
  76. package/dist/assets/d-pRatUO7H.js +1 -0
  77. package/dist/assets/diff-DbItnlRl.js +1 -0
  78. package/dist/assets/dockerfile-BKs6k2Af.js +1 -0
  79. package/dist/assets/dtd-DF_7sFjM.js +1 -0
  80. package/dist/assets/dylan-DwRh75JA.js +1 -0
  81. package/dist/assets/ebnf-CDyGwa7X.js +1 -0
  82. package/dist/assets/ecl-Cabwm37j.js +1 -0
  83. package/dist/assets/eiffel-CnydiIhH.js +1 -0
  84. package/dist/assets/elm-vLlmbW-K.js +1 -0
  85. package/dist/assets/erlang-BNw1qcRV.js +1 -0
  86. package/dist/assets/factor-kuTfRLto.js +1 -0
  87. package/dist/assets/fcl-Kvtd6kyn.js +1 -0
  88. package/dist/assets/forth-Ffai-XNe.js +1 -0
  89. package/dist/assets/fortran-DYz_wnZ1.js +1 -0
  90. package/dist/assets/gas-Bneqetm1.js +1 -0
  91. package/dist/assets/gherkin-heZmZLOM.js +1 -0
  92. package/dist/assets/groovy-D9Dt4D0W.js +1 -0
  93. package/dist/assets/haskell-Cw1EW3IL.js +1 -0
  94. package/dist/assets/haxe-H-WmDvRZ.js +1 -0
  95. package/dist/assets/http-DBlCnlav.js +1 -0
  96. package/dist/assets/idl-BEugSyMb.js +1 -0
  97. package/dist/assets/index-6xZjfNBY.js +1 -0
  98. package/dist/assets/index-7jqFg9Mv.js +13 -0
  99. package/dist/assets/index-AdP2vXVD.js +1 -0
  100. package/dist/assets/index-BHPv-A3N.js +1 -0
  101. package/dist/assets/index-BL9wSBtZ.js +1 -0
  102. package/dist/assets/index-BjawAflQ.js +1 -0
  103. package/dist/assets/index-C0JZnjgf.js +3 -0
  104. package/dist/assets/index-C1vHAMos.js +1 -0
  105. package/dist/assets/index-CAXLlEvK.js +1 -0
  106. package/dist/assets/index-CDgREgYC.js +2 -0
  107. package/dist/assets/index-CIYtVPt-.js +1 -0
  108. package/dist/assets/index-CddMuxp_.js +6 -0
  109. package/dist/assets/index-CmlqrlxY.js +7 -0
  110. package/dist/assets/index-Crr2UUqW.js +1 -0
  111. package/dist/assets/index-D3-l1kuh.js +1 -0
  112. package/dist/assets/index-D5AbviI1.js +1 -0
  113. package/dist/assets/index-D8eibMZn.js +1 -0
  114. package/dist/assets/index-DLh7eEmr.js +1 -0
  115. package/dist/assets/index-Q5rzr3Yl.js +1 -0
  116. package/dist/assets/index-RrNfwOco.js +1 -0
  117. package/dist/assets/index-kgpju2R2.js +1 -0
  118. package/dist/assets/index-qjQPn-9r.js +1 -0
  119. package/dist/assets/javascript-iXu5QeM3.js +1 -0
  120. package/dist/assets/julia-DuME0IfC.js +1 -0
  121. package/dist/assets/livescript-BwQOo05w.js +1 -0
  122. package/dist/assets/lua-VAEuO923.js +1 -0
  123. package/dist/assets/mathematica-DTrFuWx2.js +1 -0
  124. package/dist/assets/mbox-CNhZ1qSd.js +1 -0
  125. package/dist/assets/mirc-CjQqDB4T.js +1 -0
  126. package/dist/assets/mllike-CXdrOF99.js +1 -0
  127. package/dist/assets/modelica-Dc1JOy9r.js +1 -0
  128. package/dist/assets/mscgen-BA5vi2Kp.js +1 -0
  129. package/dist/assets/mumps-BT43cFF4.js +1 -0
  130. package/dist/assets/nginx-DdIZxoE0.js +1 -0
  131. package/dist/assets/nsis-LY0VT_u5.js +1 -0
  132. package/dist/assets/ntriples-BfvgReVJ.js +1 -0
  133. package/dist/assets/octave-Ck1zUtKM.js +1 -0
  134. package/dist/assets/oz-BzwKVEFT.js +1 -0
  135. package/dist/assets/pascal--L3eBynH.js +1 -0
  136. package/dist/assets/perl-CdXCOZ3F.js +1 -0
  137. package/dist/assets/pig-CevX1Tat.js +1 -0
  138. package/dist/assets/powershell-CFHJl5sT.js +1 -0
  139. package/dist/assets/properties-C78fOPTZ.js +1 -0
  140. package/dist/assets/protobuf-ChK-085T.js +1 -0
  141. package/dist/assets/pug-DeIclll2.js +1 -0
  142. package/dist/assets/puppet-DMA9R1ak.js +1 -0
  143. package/dist/assets/python-BuPzkPfP.js +1 -0
  144. package/dist/assets/q-pXgVlZs6.js +1 -0
  145. package/dist/assets/r-B6wPVr8A.js +1 -0
  146. package/dist/assets/rpm-CTu-6PCP.js +1 -0
  147. package/dist/assets/ruby-BK5aLJev.js +1 -0
  148. package/dist/assets/sas-B4kiWyti.js +1 -0
  149. package/dist/assets/scheme-C41bIUwD.js +1 -0
  150. package/dist/assets/shell-CjFT_Tl9.js +1 -0
  151. package/dist/assets/sieve-C3Gn_uJK.js +1 -0
  152. package/dist/assets/simple-mode-GW_nhZxv.js +1 -0
  153. package/dist/assets/smalltalk-CnHTOXQT.js +1 -0
  154. package/dist/assets/solr-DehyRSwq.js +1 -0
  155. package/dist/assets/sparql-DkYu6x3z.js +1 -0
  156. package/dist/assets/spreadsheet-BCZA_wO0.js +1 -0
  157. package/dist/assets/sql-D0XecflT.js +1 -0
  158. package/dist/assets/stex-C3f8Ysf7.js +1 -0
  159. package/dist/assets/style-Cif_gYUW.css +1 -0
  160. package/dist/assets/style-DG6dI-L6.css +1 -0
  161. package/dist/assets/stylus-B533Al4x.js +1 -0
  162. package/dist/assets/swift-BzpIVaGY.js +1 -0
  163. package/dist/assets/tcl-DVfN8rqt.js +1 -0
  164. package/dist/assets/textile-CnDTJFAw.js +1 -0
  165. package/dist/assets/tiddlywiki-DO-Gjzrf.js +1 -0
  166. package/dist/assets/tiki-DGYXhP31.js +1 -0
  167. package/dist/assets/toml-Bm5Em-hy.js +1 -0
  168. package/dist/assets/troff-wAsdV37c.js +1 -0
  169. package/dist/assets/ttcn-CfJYG6tj.js +1 -0
  170. package/dist/assets/ttcn-cfg-B9xdYoR4.js +1 -0
  171. package/dist/assets/turtle-B1tBg_DP.js +1 -0
  172. package/dist/assets/vb-CmGdzxic.js +1 -0
  173. package/dist/assets/vbscript-BuJXcnF6.js +1 -0
  174. package/dist/assets/velocity-D8B20fx6.js +1 -0
  175. package/dist/assets/verilog-C6RDOZhf.js +1 -0
  176. package/dist/assets/vhdl-lSbBsy5d.js +1 -0
  177. package/dist/assets/webidl-ZXfAyPTL.js +1 -0
  178. package/dist/assets/xquery-DzFWVndE.js +1 -0
  179. package/dist/assets/yacas-BJ4BC0dw.js +1 -0
  180. package/dist/assets/z80-Hz9HOZM7.js +1 -0
  181. package/dist/editor/article-menu.d.ts +36 -0
  182. package/dist/editor/article-menu.d.ts.map +1 -0
  183. package/dist/editor/article-menu.js +251 -0
  184. package/dist/editor/article-menu.js.map +1 -0
  185. package/dist/editor/index.d.ts +3 -0
  186. package/dist/editor/index.d.ts.map +1 -0
  187. package/dist/editor/index.js +3 -0
  188. package/dist/editor/index.js.map +1 -0
  189. package/dist/editor/myno-editor.d.ts +25 -0
  190. package/dist/editor/myno-editor.d.ts.map +1 -0
  191. package/dist/editor/myno-editor.js +98 -0
  192. package/dist/editor/myno-editor.js.map +1 -0
  193. package/dist/index.d.ts +8 -0
  194. package/dist/index.d.ts.map +1 -0
  195. package/dist/index.js +8 -0
  196. package/dist/index.js.map +1 -0
  197. package/dist/src/studio/studio.html +342 -0
  198. package/dist/studio/color-palettes.d.ts +16 -0
  199. package/dist/studio/color-palettes.d.ts.map +1 -0
  200. package/dist/studio/color-palettes.js +129 -0
  201. package/dist/studio/color-palettes.js.map +1 -0
  202. package/dist/studio/config-manager.d.ts +34 -0
  203. package/dist/studio/config-manager.d.ts.map +1 -0
  204. package/dist/studio/config-manager.js +30 -0
  205. package/dist/studio/config-manager.js.map +1 -0
  206. package/dist/studio/studio.d.ts +2 -0
  207. package/dist/studio/studio.d.ts.map +1 -0
  208. package/dist/studio/studio.js +1031 -0
  209. package/dist/studio/studio.js.map +1 -0
  210. package/dist/studio/style-packs.d.ts +24 -0
  211. package/dist/studio/style-packs.d.ts.map +1 -0
  212. package/dist/studio/style-packs.js +179 -0
  213. package/dist/studio/style-packs.js.map +1 -0
  214. package/dist/studio/syntax-presets.d.ts +16 -0
  215. package/dist/studio/syntax-presets.d.ts.map +1 -0
  216. package/dist/studio/syntax-presets.js +408 -0
  217. package/dist/studio/syntax-presets.js.map +1 -0
  218. package/dist/studio/themes.d.ts +32 -0
  219. package/dist/studio/themes.d.ts.map +1 -0
  220. package/dist/studio/themes.js +153 -0
  221. package/dist/studio/themes.js.map +1 -0
  222. package/dist/studio/wechat-renderer.d.ts +59 -0
  223. package/dist/studio/wechat-renderer.d.ts.map +1 -0
  224. package/dist/studio/wechat-renderer.js +797 -0
  225. package/dist/studio/wechat-renderer.js.map +1 -0
  226. package/dist/studio.js +2359 -0
  227. package/package.json +45 -4
@@ -0,0 +1,797 @@
1
+ import juice from "juice/client";
2
+ import { getCombinedDefaultCss, getCombinedWechatRenderers } from "@akairos/milkdown-article-plugins";
3
+ const ALLOWED_TAGS = new Set([
4
+ "section",
5
+ "p",
6
+ "blockquote",
7
+ "ul",
8
+ "ol",
9
+ "li",
10
+ "pre",
11
+ "code",
12
+ "span",
13
+ "strong",
14
+ "em",
15
+ "b",
16
+ "i",
17
+ "u",
18
+ "s",
19
+ "del",
20
+ "a",
21
+ "img",
22
+ "br",
23
+ "hr",
24
+ "table",
25
+ "thead",
26
+ "tbody",
27
+ "tr",
28
+ "th",
29
+ "td",
30
+ ]);
31
+ const DROP_WITH_CONTENT = new Set([
32
+ "script",
33
+ "style",
34
+ "iframe",
35
+ "object",
36
+ "embed",
37
+ "form",
38
+ "button",
39
+ "input",
40
+ "textarea",
41
+ "select",
42
+ "link",
43
+ "meta",
44
+ "svg",
45
+ "math",
46
+ "canvas",
47
+ "template",
48
+ "noscript",
49
+ "video",
50
+ "audio",
51
+ "source",
52
+ ]);
53
+ const ALLOWED_STYLE_PROPERTIES = new Set([
54
+ "font",
55
+ "font-family",
56
+ "font-size",
57
+ "font-weight",
58
+ "font-style",
59
+ "color",
60
+ "text-align",
61
+ "line-height",
62
+ "letter-spacing",
63
+ "text-decoration",
64
+ "text-decoration-color",
65
+ "text-decoration-line",
66
+ "text-decoration-style",
67
+ "text-indent",
68
+ "background",
69
+ "background-image",
70
+ "background-color",
71
+ "border",
72
+ "border-top",
73
+ "border-right",
74
+ "border-bottom",
75
+ "border-left",
76
+ "border-top-color",
77
+ "border-top-style",
78
+ "border-top-width",
79
+ "border-right-color",
80
+ "border-right-style",
81
+ "border-right-width",
82
+ "border-bottom-color",
83
+ "border-bottom-style",
84
+ "border-bottom-width",
85
+ "border-left-color",
86
+ "border-left-style",
87
+ "border-left-width",
88
+ "border-color",
89
+ "border-width",
90
+ "border-style",
91
+ "border-radius",
92
+ "border-top-left-radius",
93
+ "border-top-right-radius",
94
+ "border-bottom-right-radius",
95
+ "border-bottom-left-radius",
96
+ "border-collapse",
97
+ "padding",
98
+ "padding-top",
99
+ "padding-right",
100
+ "padding-bottom",
101
+ "padding-left",
102
+ "margin",
103
+ "margin-top",
104
+ "margin-right",
105
+ "margin-bottom",
106
+ "margin-left",
107
+ "width",
108
+ "min-width",
109
+ "max-width",
110
+ "height",
111
+ "max-height",
112
+ "display",
113
+ "vertical-align",
114
+ "list-style",
115
+ "list-style-type",
116
+ "list-style-position",
117
+ "white-space",
118
+ "white-space-collapse",
119
+ "text-wrap-mode",
120
+ "text-wrap-style",
121
+ "word-break",
122
+ "word-wrap",
123
+ "overflow-wrap",
124
+ "overflow",
125
+ "overflow-x",
126
+ "overflow-y",
127
+ "box-sizing",
128
+ "table-layout",
129
+ "text-size-adjust",
130
+ "-webkit-text-size-adjust",
131
+ ]);
132
+ const ALLOWED_DISPLAY_VALUES = new Set([
133
+ "block",
134
+ "inline",
135
+ "inline-block",
136
+ "list-item",
137
+ "table",
138
+ "table-row",
139
+ "table-cell",
140
+ "table-row-group",
141
+ "table-header-group",
142
+ "none",
143
+ ]);
144
+ function setNodeKind(element, kind) {
145
+ element.dataset.wechatNode = kind;
146
+ }
147
+ function replaceTag(element, tagName) {
148
+ const replacement = element.ownerDocument.createElement(tagName);
149
+ for (const attribute of Array.from(element.attributes)) {
150
+ replacement.setAttribute(attribute.name, attribute.value);
151
+ }
152
+ while (element.firstChild) {
153
+ replacement.appendChild(element.firstChild);
154
+ }
155
+ element.replaceWith(replacement);
156
+ return replacement;
157
+ }
158
+ function isExternalLink(href) {
159
+ return /^(?:https?:)?\/\//i.test(href);
160
+ }
161
+ function imageIsOnlyContent(parent, image) {
162
+ return Array.from(parent.childNodes).every((node) => {
163
+ if (node === image)
164
+ return true;
165
+ return node.nodeType === 3 && !(node.nodeValue ?? "").trim();
166
+ });
167
+ }
168
+ // 图片缩放与图注共用导出流程:Markdown 往返读两位小数 alt 与 title;
169
+ // Crepe 块级图片 DOM 读 ratio 与 caption 属性。
170
+ const imageRenderer = {
171
+ kind: "image",
172
+ selector: "img",
173
+ finalizeSelector: "figure, figcaption",
174
+ async prepare(element, context) {
175
+ setNodeKind(element, "image");
176
+ const src = element.getAttribute("src");
177
+ if (src)
178
+ element.setAttribute("data-src", src);
179
+ const parent = element.parentElement;
180
+ const inOwnParagraph = !!parent && parent.tagName.toLowerCase() === "p" && imageIsOnlyContent(parent, element);
181
+ // Crepe 块级图片是文章根的顶层裸 <img>,同样视为独立图片。
182
+ const topLevelBlock = parent === context.root;
183
+ if (!parent || (!inOwnParagraph && !topLevelBlock)) {
184
+ return;
185
+ }
186
+ const ratioRaw = element.getAttribute("ratio");
187
+ const ratioFromAttr = ratioRaw ? Number(ratioRaw) : NaN;
188
+ const alt = element.getAttribute("alt") ?? "";
189
+ const ratio = Number.isFinite(ratioFromAttr) && ratioFromAttr > 0
190
+ ? ratioFromAttr
191
+ : /^(?:0|[1-9]\d*)\.\d{2}$/.test(alt) ? Number(alt) : 0;
192
+ if (ratio > 0 && Number.isFinite(ratio)) {
193
+ // DOMParser 文档不加载图片,用浏览器图片对象读取原始尺寸。
194
+ const sourceImage = new Image();
195
+ let imageSrc = src ?? "";
196
+ if (typeof window !== "undefined" &&
197
+ window.location &&
198
+ window.location.origin &&
199
+ window.location.origin.startsWith("http") &&
200
+ imageSrc &&
201
+ !/^(?:https?:|data:|blob:|\/\/)/i.test(imageSrc)) {
202
+ if (imageSrc.startsWith("/") && !imageSrc.startsWith("/local-file")) {
203
+ imageSrc = `/local-file?path=${encodeURIComponent(imageSrc)}`;
204
+ }
205
+ }
206
+ sourceImage.src = imageSrc;
207
+ try {
208
+ await sourceImage.decode();
209
+ if (!sourceImage.naturalWidth)
210
+ throw new Error("图片原始宽度为空");
211
+ element.style.width = `${Math.min(ratio, 1) * 100}%`;
212
+ element.style.maxWidth = `${sourceImage.naturalWidth * ratio}px`;
213
+ element.style.height = "auto";
214
+ }
215
+ catch (error) {
216
+ if (context.strictImageDecode ?? true) {
217
+ throw new Error(`无法读取缩放图片的原始尺寸:${element.getAttribute("title") || "无图注"}`, { cause: error });
218
+ }
219
+ // 非严格模式(如 Studio 可视化编辑预览)降级处理,保证编辑器正常渲染
220
+ element.style.width = `${Math.min(ratio, 1) * 100}%`;
221
+ element.style.height = "auto";
222
+ }
223
+ }
224
+ const figure = context.document.createElement("figure");
225
+ setNodeKind(figure, "image-block");
226
+ if (inOwnParagraph) {
227
+ parent.replaceWith(figure);
228
+ }
229
+ else {
230
+ element.replaceWith(figure);
231
+ }
232
+ figure.appendChild(element);
233
+ const caption = (element.getAttribute("caption") || element.getAttribute("title") || "").trim();
234
+ element.removeAttribute("title");
235
+ element.removeAttribute("caption");
236
+ if (caption) {
237
+ const figcaption = context.document.createElement("figcaption");
238
+ setNodeKind(figcaption, "image-caption");
239
+ figcaption.textContent = caption;
240
+ figure.appendChild(figcaption);
241
+ }
242
+ },
243
+ finalize(element) {
244
+ const tagName = element.tagName.toLowerCase();
245
+ if (tagName === "figure")
246
+ replaceTag(element, "section");
247
+ if (tagName === "figcaption")
248
+ replaceTag(element, "p");
249
+ },
250
+ };
251
+ const codeBlockRenderer = {
252
+ kind: "code-block",
253
+ selector: "pre",
254
+ prepare(element, context) {
255
+ setNodeKind(element, "code-block-body");
256
+ const code = element.querySelector("code");
257
+ if (code)
258
+ setNodeKind(code, "code-block-code");
259
+ const wrapper = context.document.createElement("section");
260
+ setNodeKind(wrapper, "code-block");
261
+ element.replaceWith(wrapper);
262
+ const language = element.getAttribute("data-language")?.trim();
263
+ if (language) {
264
+ const label = context.document.createElement("p");
265
+ setNodeKind(label, "code-language");
266
+ label.textContent = language;
267
+ wrapper.appendChild(label);
268
+ }
269
+ wrapper.appendChild(element);
270
+ },
271
+ };
272
+ const inlineCodeRenderer = {
273
+ kind: "inline-code",
274
+ selector: "code:not(pre code)",
275
+ prepare(element) {
276
+ setNodeKind(element, "inline-code");
277
+ },
278
+ finalize(element) {
279
+ replaceTag(element, "span");
280
+ },
281
+ };
282
+ const headingRenderer = {
283
+ kind: "heading",
284
+ selector: "h1, h2, h3, h4, h5, h6",
285
+ prepare(element, context) {
286
+ setNodeKind(element, `heading-${element.tagName.slice(1)}`);
287
+ if (element.tagName.toLowerCase() === "h2" && context.structure) {
288
+ const content = context.document.createElement("span");
289
+ setNodeKind(content, "heading-text");
290
+ while (element.firstChild)
291
+ content.appendChild(element.firstChild);
292
+ element.appendChild(content);
293
+ if (context.structure.numberedHeadings) {
294
+ const number = context.document.createElement("span");
295
+ setNodeKind(number, "heading-number");
296
+ number.textContent = String(Array.from(context.root.querySelectorAll("h2")).indexOf(element) + 1).padStart(2, "0");
297
+ element.prepend(number);
298
+ }
299
+ if (context.structure.headingLabel) {
300
+ const label = context.document.createElement("span");
301
+ setNodeKind(label, "heading-label");
302
+ label.textContent = context.structure.headingLabel;
303
+ element.prepend(label);
304
+ }
305
+ }
306
+ },
307
+ finalize(element) {
308
+ replaceTag(element, "section");
309
+ },
310
+ };
311
+ const paragraphRenderer = {
312
+ kind: "paragraph",
313
+ selector: "p",
314
+ prepare(element) {
315
+ if (!element.dataset.wechatNode)
316
+ setNodeKind(element, "paragraph");
317
+ },
318
+ };
319
+ const emphasisRenderer = {
320
+ kind: "emphasis",
321
+ selector: "strong, b, em, i, s, del, u, mark",
322
+ prepare(element) {
323
+ setNodeKind(element, element.tagName.toLowerCase());
324
+ },
325
+ finalize(element) {
326
+ if (element.tagName.toLowerCase() === "mark")
327
+ replaceTag(element, "span");
328
+ },
329
+ };
330
+ const blockquoteRenderer = {
331
+ kind: "blockquote",
332
+ selector: "blockquote",
333
+ prepare(element, context) {
334
+ setNodeKind(element, "blockquote");
335
+ if (context.structure?.quoteLabel) {
336
+ const label = context.document.createElement("p");
337
+ setNodeKind(label, "quote-label");
338
+ label.textContent = context.structure.quoteLabel;
339
+ element.prepend(label);
340
+ }
341
+ },
342
+ finalize(element) {
343
+ // 微信编辑器会给原生 blockquote 强制加灰色左边框,导出时降级为 section,外观全靠内联样式。
344
+ replaceTag(element, "section");
345
+ },
346
+ };
347
+ /** Normalize URL for reference dedupe (same target → one footnote entry). */
348
+ export function normalizeReferenceUrl(href) {
349
+ const trimmed = href.trim();
350
+ if (!trimmed)
351
+ return "";
352
+ try {
353
+ const url = new URL(trimmed);
354
+ url.hash = "";
355
+ // Stable host casing; keep path/query as-is except trailing slash noise.
356
+ url.hostname = url.hostname.toLowerCase();
357
+ let path = url.pathname;
358
+ if (path.length > 1 && path.endsWith("/")) {
359
+ path = path.slice(0, -1);
360
+ }
361
+ url.pathname = path;
362
+ return url.toString();
363
+ }
364
+ catch {
365
+ return trimmed.replace(/\/+$/, "") || trimmed;
366
+ }
367
+ }
368
+ /**
369
+ * Register or reuse a related-link reference for an external URL.
370
+ * Same URL (even with different anchor text) maps to one index.
371
+ */
372
+ export function resolveLinkReference(href, text, references) {
373
+ const normalizedUrl = normalizeReferenceUrl(href);
374
+ const existing = references.find((item) => normalizeReferenceUrl(item.url) === normalizedUrl);
375
+ if (existing)
376
+ return existing;
377
+ const reference = {
378
+ index: references.length + 1,
379
+ text: text.trim() || href,
380
+ url: href,
381
+ };
382
+ references.push(reference);
383
+ return reference;
384
+ }
385
+ const linkRenderer = {
386
+ kind: "link",
387
+ selector: "a",
388
+ prepare(element, context) {
389
+ const href = element.getAttribute("href")?.trim() ?? "";
390
+ if (!isExternalLink(href)) {
391
+ setNodeKind(element, "internal-link");
392
+ return;
393
+ }
394
+ const reference = resolveLinkReference(href, element.textContent?.trim() || href, context.references);
395
+ setNodeKind(element, "external-link");
396
+ const marker = context.document.createElement("span");
397
+ setNodeKind(marker, "link-marker");
398
+ marker.textContent = `[${reference.index}]`;
399
+ element.after(marker);
400
+ },
401
+ finalize(element) {
402
+ // 微信草稿拒绝文内锚点,保留链接文字与排版。
403
+ if (element.dataset.wechatNode === "external-link" || element.getAttribute("href")?.startsWith("#"))
404
+ replaceTag(element, "span");
405
+ },
406
+ };
407
+ const listRenderer = {
408
+ kind: "list",
409
+ selector: "ul, ol, li",
410
+ prepare(element, context) {
411
+ const tagName = element.tagName.toLowerCase();
412
+ if (tagName === "ul")
413
+ setNodeKind(element, "bullet-list");
414
+ if (tagName === "ol")
415
+ setNodeKind(element, "ordered-list");
416
+ if (tagName !== "li")
417
+ return;
418
+ const isTask = element.getAttribute("data-item-type") === "task";
419
+ setNodeKind(element, isTask ? "task-list-item" : "list-item");
420
+ if (!isTask)
421
+ return;
422
+ const marker = context.document.createElement("span");
423
+ setNodeKind(marker, "task-marker");
424
+ marker.textContent = element.getAttribute("data-checked") === "true" ? "☑" : "☐";
425
+ const content = element.querySelector(":scope > p") ?? element;
426
+ content.prepend(marker);
427
+ },
428
+ };
429
+ const tableRenderer = {
430
+ kind: "table",
431
+ selector: "table, thead, tbody, tr, th, td",
432
+ prepare(element) {
433
+ setNodeKind(element, element.tagName.toLowerCase());
434
+ },
435
+ };
436
+ const dividerRenderer = {
437
+ kind: "divider",
438
+ selector: "hr",
439
+ prepare(element) {
440
+ setNodeKind(element, "divider");
441
+ },
442
+ finalize(element) {
443
+ replaceTag(element, "section");
444
+ },
445
+ };
446
+ const hardBreakRenderer = {
447
+ kind: "hard-break",
448
+ selector: "br",
449
+ prepare(element) {
450
+ setNodeKind(element, "hard-break");
451
+ },
452
+ };
453
+ const articleRenderer = {
454
+ kind: "article",
455
+ selector: ".zzhub-wechat-article",
456
+ prepare(_element, context) {
457
+ if (context.references.length > 0) {
458
+ const references = context.document.createElement("section");
459
+ setNodeKind(references, "references");
460
+ const title = context.document.createElement("p");
461
+ setNodeKind(title, "references-title");
462
+ title.textContent = "相关链接";
463
+ references.appendChild(title);
464
+ const list = context.document.createElement("ol");
465
+ setNodeKind(list, "references-list");
466
+ for (const reference of context.references) {
467
+ const item = context.document.createElement("li");
468
+ setNodeKind(item, "reference-item");
469
+ item.textContent = `[${reference.index}] ${reference.text}: ${reference.url}`;
470
+ list.appendChild(item);
471
+ }
472
+ references.appendChild(list);
473
+ context.root.appendChild(references);
474
+ }
475
+ if (context.theme.footerText) {
476
+ const footer = context.document.createElement("section");
477
+ setNodeKind(footer, "footer");
478
+ const text = context.document.createElement("p");
479
+ setNodeKind(text, "footer-text");
480
+ text.textContent = context.theme.footerText;
481
+ footer.appendChild(text);
482
+ context.root.appendChild(footer);
483
+ }
484
+ },
485
+ };
486
+ const DEFAULT_RENDERERS = [
487
+ imageRenderer,
488
+ codeBlockRenderer,
489
+ inlineCodeRenderer,
490
+ headingRenderer,
491
+ paragraphRenderer,
492
+ emphasisRenderer,
493
+ blockquoteRenderer,
494
+ linkRenderer,
495
+ listRenderer,
496
+ tableRenderer,
497
+ dividerRenderer,
498
+ hardBreakRenderer,
499
+ articleRenderer,
500
+ ];
501
+ export function createWechatRendererRegistry(overrides = {}) {
502
+ const base = DEFAULT_RENDERERS.map((renderer) => overrides[renderer.kind] ?? renderer);
503
+ const extra = overrides.extraRenderers ?? [];
504
+ return [...base, ...extra];
505
+ }
506
+ /** 按渲染器顺序执行,等待图片尺寸读取完成。 */
507
+ async function runRendererPhase(phase, renderers, context) {
508
+ for (const renderer of renderers) {
509
+ const handler = renderer[phase];
510
+ if (!handler)
511
+ continue;
512
+ const selector = phase === "finalize"
513
+ ? renderer.finalizeSelector ?? renderer.selector
514
+ : renderer.selector;
515
+ const elements = [];
516
+ if (context.root.matches(selector))
517
+ elements.push(context.root);
518
+ elements.push(...Array.from(context.root.querySelectorAll(selector)));
519
+ for (const element of elements) {
520
+ if (element.isConnected || element === context.root)
521
+ await handler(element, context);
522
+ }
523
+ }
524
+ }
525
+ function themeVariableDeclarations(editorVars, theme) {
526
+ const customVariables = Object.entries(editorVars)
527
+ .filter(([name]) => /^--[a-z0-9-_]+$/i.test(name))
528
+ .map(([name, value]) => `${name}: ${value};`)
529
+ .join("\n");
530
+ return [
531
+ customVariables,
532
+ `--wx-font-family: ${theme.fontFamily};`,
533
+ `--wx-body-color: ${theme.bodyColor};`,
534
+ `--wx-muted-color: ${theme.mutedColor};`,
535
+ `--wx-h2-color: ${theme.h2Color};`,
536
+ `--wx-h3-color: ${theme.h3Color};`,
537
+ `--wx-brand-ink: ${theme.primaryColor};`,
538
+ `--wx-brand-accent: ${editorVars["--brand"] ?? "#ca6093"};`,
539
+ `--wx-divider-color: ${theme.dividerColor};`,
540
+ `--wx-blockquote-border: ${theme.blockquoteBorderColor};`,
541
+ `--wx-body-line-height: ${theme.bodyLineHeight};`,
542
+ `--wx-body-letter-spacing: ${theme.bodyLetterSpacing};`,
543
+ `--wx-soft-surface: ${editorVars["--bg-warm"] ?? "#faf8f9"};`,
544
+ ].filter(Boolean).join("\n");
545
+ }
546
+ export function buildWechatThemeCss(editorVars, theme) {
547
+ return `
548
+ .milkdown .editor {
549
+ ${themeVariableDeclarations(editorVars, theme)}
550
+ ${theme.containerStyle};
551
+ ${theme.bodyFontSize ? `font-size: ${theme.bodyFontSize};` : ""}
552
+ }
553
+ .milkdown .editor [data-wechat-node="footer"] {
554
+ ${theme.footerStyle};
555
+ }
556
+ .milkdown .editor p {
557
+ ${theme.bodyFontSize ? `font-size: ${theme.bodyFontSize};` : ""}
558
+ ${theme.bodyParagraphSpacing ? `margin-bottom: ${theme.bodyParagraphSpacing};` : ""}
559
+ ${theme.bodyLineHeight ? `line-height: ${theme.bodyLineHeight};` : ""}
560
+ ${theme.bodyLetterSpacing ? `letter-spacing: ${theme.bodyLetterSpacing};` : ""}
561
+ ${theme.bodyColor ? `color: ${theme.bodyColor};` : ""}
562
+ }
563
+ `;
564
+ }
565
+ /**
566
+ * 结构装饰(编号 / 章节标签 / 引用标签)在导出时是真实节点,编辑器里是 ::before。
567
+ * 此处样式与 studio 编辑器的 ::before 视觉对齐,否则预览中退化为普通正文。
568
+ */
569
+ function buildStructureDecorationCss(structure) {
570
+ if (!structure?.numberedHeadings && !structure?.headingLabel && !structure?.quoteLabel)
571
+ return "";
572
+ return `
573
+ .milkdown .editor [data-wechat-node="heading-number"],
574
+ .milkdown .editor [data-wechat-node="heading-label"] {
575
+ margin-right: 0.3em;
576
+ opacity: 0.7;
577
+ font-weight: 600;
578
+ }
579
+ .milkdown .editor [data-wechat-node="quote-label"] {
580
+ margin: 0 0 6px;
581
+ font-size: 11px;
582
+ font-weight: 600;
583
+ letter-spacing: 0.05em;
584
+ color: var(--wx-brand-ink, var(--wx-brand-accent, #ca6093));
585
+ text-transform: uppercase;
586
+ }
587
+ `;
588
+ }
589
+ function parseHtml(html) {
590
+ return new DOMParser().parseFromString(`<!doctype html><html><body>${html}</body></html>`, "text/html");
591
+ }
592
+ const BORDER_SIDES = ["top", "right", "bottom", "left"];
593
+ /**
594
+ * 浏览器把 `border: none; border-left: X` 或 `border-left: none` 序列化成多值简写或
595
+ * none 边的长属性,其中 border-color 是 currentcolor——公众号编辑器解析不了,整条
596
+ * 声明作废后边框退回正文灰色。展开为可见边的单值属性,丢弃 none/hidden 边。
597
+ */
598
+ function normalizeBorderShorthands(element) {
599
+ const style = element.style;
600
+ const sides = BORDER_SIDES.map((side) => ({
601
+ side,
602
+ width: style.getPropertyValue(`border-${side}-width`),
603
+ style: style.getPropertyValue(`border-${side}-style`),
604
+ color: style.getPropertyValue(`border-${side}-color`),
605
+ }));
606
+ const invisible = (entry) => entry.style === "none" || entry.style === "hidden";
607
+ const hasShorthand = !!style.getPropertyValue("border-style") ||
608
+ !!style.getPropertyValue("border-width") ||
609
+ !!style.getPropertyValue("border-color");
610
+ if (!hasShorthand && !sides.some(invisible))
611
+ return;
612
+ if (!hasShorthand) {
613
+ // 单边 none/hidden 长属性:只删除,其余保持原样
614
+ for (const { side, style: borderStyle } of sides) {
615
+ if (borderStyle !== "none" && borderStyle !== "hidden")
616
+ continue;
617
+ style.removeProperty(`border-${side}`);
618
+ style.removeProperty(`border-${side}-width`);
619
+ style.removeProperty(`border-${side}-style`);
620
+ style.removeProperty(`border-${side}-color`);
621
+ }
622
+ return;
623
+ }
624
+ const removable = ["border", "border-width", "border-style", "border-color"];
625
+ for (const { side } of sides) {
626
+ removable.push(`border-${side}`, `border-${side}-width`, `border-${side}-style`, `border-${side}-color`);
627
+ }
628
+ for (const property of removable)
629
+ style.removeProperty(property);
630
+ for (const entry of sides) {
631
+ if (!entry.style || invisible(entry))
632
+ continue;
633
+ if (entry.width && entry.color) {
634
+ style.setProperty(`border-${entry.side}`, `${entry.width} ${entry.style} ${entry.color}`);
635
+ }
636
+ else {
637
+ if (entry.width)
638
+ style.setProperty(`border-${entry.side}-width`, entry.width);
639
+ style.setProperty(`border-${entry.side}-style`, entry.style);
640
+ if (entry.color)
641
+ style.setProperty(`border-${entry.side}-color`, entry.color);
642
+ }
643
+ }
644
+ }
645
+ function sanitizeStyle(element) {
646
+ const propertyNames = [];
647
+ for (let index = 0; index < element.style.length; index += 1) {
648
+ const name = element.style.item(index);
649
+ if (name)
650
+ propertyNames.push(name);
651
+ }
652
+ for (const property of propertyNames) {
653
+ const value = element.style.getPropertyValue(property).trim();
654
+ const unsafeValue = /(?:url\s*\(|expression\s*\(|javascript:|var\s*\()/i.test(value);
655
+ const unsafeDisplay = property === "display" && !ALLOWED_DISPLAY_VALUES.has(value.toLowerCase());
656
+ if (!ALLOWED_STYLE_PROPERTIES.has(property) || unsafeValue || unsafeDisplay) {
657
+ element.style.removeProperty(property);
658
+ }
659
+ }
660
+ if (!element.style.cssText.trim())
661
+ element.removeAttribute("style");
662
+ }
663
+ function allowedAttributesFor(element) {
664
+ const tagName = element.tagName.toLowerCase();
665
+ if (tagName === "img")
666
+ return new Set(["style", "src", "data-src", "alt", "width", "height"]);
667
+ if (tagName === "a")
668
+ return new Set(["style", "href", "title"]);
669
+ if (tagName === "ol")
670
+ return new Set(["style", "start"]);
671
+ if (tagName === "th" || tagName === "td")
672
+ return new Set(["style", "colspan", "rowspan", "align"]);
673
+ return new Set(["style"]);
674
+ }
675
+ function sanitizeImageUrl(element, attribute) {
676
+ const value = element.getAttribute(attribute)?.trim() ?? "";
677
+ if (!value) {
678
+ element.removeAttribute(attribute);
679
+ return;
680
+ }
681
+ if (/^(?:javascript|vbscript):/i.test(value)) {
682
+ element.removeAttribute(attribute);
683
+ return;
684
+ }
685
+ if (/^data:/i.test(value) && !/^data:image\/(?:avif|gif|jpe?g|png|webp);base64,/i.test(value)) {
686
+ element.removeAttribute(attribute);
687
+ }
688
+ }
689
+ function sanitizeTree(root) {
690
+ const elements = [root, ...Array.from(root.querySelectorAll("*"))].reverse();
691
+ for (const element of elements) {
692
+ const tagName = element.tagName.toLowerCase();
693
+ if (DROP_WITH_CONTENT.has(tagName)) {
694
+ if (element !== root)
695
+ element.remove();
696
+ continue;
697
+ }
698
+ if (!ALLOWED_TAGS.has(tagName)) {
699
+ if (element !== root)
700
+ element.replaceWith(...Array.from(element.childNodes));
701
+ continue;
702
+ }
703
+ const allowedAttributes = allowedAttributesFor(element);
704
+ for (const attribute of Array.from(element.attributes)) {
705
+ if (!allowedAttributes.has(attribute.name))
706
+ element.removeAttribute(attribute.name);
707
+ }
708
+ if (tagName === "a") {
709
+ const href = element.getAttribute("href") ?? "";
710
+ if (!/^(?:#|https?:\/\/|mailto:|tel:)/i.test(href))
711
+ element.removeAttribute("href");
712
+ }
713
+ if (tagName === "img") {
714
+ sanitizeImageUrl(element, "src");
715
+ sanitizeImageUrl(element, "data-src");
716
+ }
717
+ sanitizeStyle(element);
718
+ normalizeBorderShorthands(element);
719
+ }
720
+ }
721
+ /** 完成图片缩放后,将文章样式内联为公众号 HTML。 */
722
+ export async function renderWechatHtml(input) {
723
+ const sourceDocument = parseHtml("");
724
+ const compatibilityRoot = sourceDocument.createElement("section");
725
+ compatibilityRoot.className = "milkdown";
726
+ const root = sourceDocument.createElement("section");
727
+ root.className = "editor zzhub-wechat-article";
728
+ root.lang = "zh-CN";
729
+ setNodeKind(root, "article");
730
+ root.innerHTML = input.semanticHtml;
731
+ if (input.structure?.headerImageUrl) {
732
+ const paragraph = sourceDocument.createElement("p");
733
+ const image = sourceDocument.createElement("img");
734
+ image.src = input.structure.headerImageUrl;
735
+ image.alt = "";
736
+ paragraph.appendChild(image);
737
+ root.prepend(paragraph);
738
+ }
739
+ compatibilityRoot.appendChild(root);
740
+ sourceDocument.body.appendChild(compatibilityRoot);
741
+ const references = [];
742
+ const pluginRenderers = getCombinedWechatRenderers();
743
+ const pluginCss = getCombinedDefaultCss();
744
+ const mergedOverrides = {
745
+ ...input.renderers,
746
+ extraRenderers: [
747
+ ...(input.renderers?.extraRenderers ?? []),
748
+ ...pluginRenderers,
749
+ ],
750
+ };
751
+ const renderers = createWechatRendererRegistry(mergedOverrides);
752
+ await runRendererPhase("prepare", renderers, {
753
+ document: sourceDocument,
754
+ root,
755
+ theme: input.theme,
756
+ references,
757
+ structure: input.structure,
758
+ strictImageDecode: input.strictImageDecode ?? true,
759
+ });
760
+ const css = [
761
+ input.baseCss,
762
+ buildWechatThemeCss(input.editorVars ?? {}, input.theme),
763
+ buildStructureDecorationCss(input.structure),
764
+ pluginCss,
765
+ input.customCss ?? "",
766
+ ].filter(Boolean).join("\n");
767
+ const inlinedHtml = juice.inlineContent(compatibilityRoot.outerHTML, css, {
768
+ applyAttributesTableElements: false,
769
+ applyHeightAttributes: false,
770
+ applyStyleTags: false,
771
+ applyWidthAttributes: false,
772
+ inlinePseudoElements: false,
773
+ preserveFontFaces: false,
774
+ preserveImportant: false,
775
+ preserveKeyFrames: false,
776
+ preserveMediaQueries: false,
777
+ preservePseudos: false,
778
+ removeStyleTags: true,
779
+ resolveCSSVariables: true,
780
+ decodeStyleAttributes: true,
781
+ });
782
+ const finalDocument = parseHtml(inlinedHtml);
783
+ const finalRoot = finalDocument.querySelector(".zzhub-wechat-article");
784
+ if (!finalRoot)
785
+ throw new Error("Wechat article root missing after CSS inlining");
786
+ await runRendererPhase("finalize", renderers, {
787
+ document: finalDocument,
788
+ root: finalRoot,
789
+ theme: input.theme,
790
+ references,
791
+ structure: input.structure,
792
+ strictImageDecode: input.strictImageDecode ?? true,
793
+ });
794
+ sanitizeTree(finalRoot);
795
+ return finalRoot.outerHTML;
796
+ }
797
+ //# sourceMappingURL=wechat-renderer.js.map