A privacy-first, self-hosted, fully open source personal knowledge management software, written in typescript and golang. (PERSONAL FORK)
1import {addScript} from "../util/addScript";
2import {addStyle} from "../util/addStyle";
3import {Constants} from "../../constants";
4import {hasNextSibling, hasPreviousSibling} from "../wysiwyg/getBlock";
5import {hasClosestBlock} from "../util/hasClosest";
6import {looseJsonParse} from "../../util/functions";
7import {genRenderFrame} from "./util";
8
9export const mathRender = (element: Element, cdn = Constants.PROTYLE_CDN, maxWidth = false) => {
10 let mathElements: Element[] = [];
11 if (element.getAttribute("data-subtype") === "math") {
12 // 编辑器内代码块编辑渲染
13 mathElements = [element];
14 } else {
15 mathElements = Array.from(element.querySelectorAll('[data-subtype="math"]'));
16 }
17 if (mathElements.length === 0) {
18 return;
19 }
20 addStyle(`${cdn}/js/katex/katex.min.css?v=0.16.9`, "protyleKatexStyle");
21 addScript(`${cdn}/js/katex/katex.min.js?v=0.16.9`, "protyleKatexScript").then(() => {
22 addScript(`${cdn}/js/katex/mhchem.min.js?v=0.16.9`, "protyleKatexMhchemScript").then(() => {
23 mathElements.forEach((mathElement: HTMLElement) => {
24 if (mathElement.getAttribute("data-render") === "true") {
25 return;
26 }
27 mathElement.setAttribute("data-render", "true");
28 let macros = {};
29 try {
30 macros = looseJsonParse(window.siyuan.config.editor.katexMacros || "{}");
31 } catch (e) {
32 console.warn("KaTex macros is not JSON", e);
33 }
34 const isBlock = mathElement.tagName === "DIV";
35 try {
36 const mathHTML = window.katex.renderToString(Lute.UnEscapeHTMLStr(mathElement.getAttribute("data-content")), {
37 displayMode: isBlock,
38 output: "html",
39 macros,
40 trust: true, // REF: https://katex.org/docs/supported#html
41 strict: (errorCode) => errorCode === "unicodeTextInMathMode" ? "ignore" : "warn",
42 });
43 const blockElement = hasClosestBlock(mathElement);
44 if (isBlock) {
45 genRenderFrame(mathElement);
46 mathElement.firstElementChild.firstElementChild.classList.remove("ft__error");
47 mathElement.firstElementChild.firstElementChild.setAttribute("contenteditable", "false");
48 mathElement.firstElementChild.firstElementChild.innerHTML = mathHTML;
49 // https://github.com/siyuan-note/siyuan/issues/3541
50 const baseElements = mathElement.querySelectorAll(".base");
51 if (baseElements.length > 0) {
52 baseElements[baseElements.length - 1].insertAdjacentHTML("afterend", "<span class='fn__flex-1'></span>");
53 }
54 // https://github.com/siyuan-note/siyuan/issues/4334
55 const newlineElement = mathElement.querySelector(".katex-html > .newline");
56 if (newlineElement) {
57 newlineElement.parentElement.style.display = "block";
58 }
59 } else {
60 mathElement.classList.remove("ft__error");
61 mathElement.innerHTML = mathHTML;
62 if (blockElement && mathElement.getBoundingClientRect().width > blockElement.clientWidth) {
63 mathElement.style.maxWidth = "100%";
64 mathElement.style.overflowX = "auto";
65 mathElement.style.overflowY = "hidden";
66 mathElement.style.display = "inline-block";
67 } else {
68 mathElement.style.maxWidth = "";
69 mathElement.style.overflowX = "";
70 mathElement.style.overflowY = "";
71 mathElement.style.display = "";
72 }
73 const nextSibling = hasNextSibling(mathElement) as HTMLElement;
74 if (!nextSibling) {
75 // 表格编辑问题 https://ld246.com/article/1629191424824
76 if (mathElement.parentElement.tagName !== "TH" && mathElement.parentElement.tagName !== "TD") {
77 // 光标无法移动到末尾 https://github.com/siyuan-note/siyuan/issues/2112
78 mathElement.insertAdjacentText("afterend", "\n");
79 } else {
80 // https://ld246.com/article/1651595975481,https://ld246.com/article/1658903123429
81 // 随着浏览器的升级,从 beforeend 修改为 afterend
82 mathElement.insertAdjacentText("afterend", Constants.ZWSP);
83 }
84 } else if (nextSibling && nextSibling.nodeType !== 3 &&
85 (
86 nextSibling.getAttribute("data-type")?.indexOf("inline-math") > -1 ||
87 nextSibling.classList.contains("img")
88 )) {
89 // 相邻的数学公式删除或光标移动有问题
90 mathElement.after(document.createTextNode(Constants.ZWSP));
91 } else if (nextSibling &&
92 !nextSibling.textContent.startsWith("\n") && // https://github.com/siyuan-note/insider/issues/1089
93 // 输入 $a$ 后,光标移动到其他块,再点击 a 后,光标不显示 https://github.com/siyuan-note/insider/issues/1076#issuecomment-1253215515
94 nextSibling.textContent !== Constants.ZWSP) {
95 // 数学公式后一个字符删除多 br https://ld246.com/article/1647157880974
96 // 数学公式后有 \n 不能再添加  https://ld246.com/article/1647329437541
97 mathElement.insertAdjacentHTML("beforeend", "");
98 }
99 // 光标无法移动到段首 https://ld246.com/article/1623551823742
100 if (mathElement.previousSibling?.textContent.endsWith("\n")) {
101 mathElement.insertAdjacentText("beforebegin", Constants.ZWSP);
102 } else if (!hasPreviousSibling(mathElement) && ["TH", "TD"].includes(mathElement.parentElement.tagName)) {
103 // 单元格中只有数学公式时,光标无法移动到数学公式前
104 mathElement.insertAdjacentText("afterbegin", Constants.ZWSP);
105 }
106 }
107
108 // export pdf
109 if (maxWidth) {
110 setTimeout(() => {
111 if (isBlock) {
112 const katexElement = mathElement.querySelector(".katex-display");
113 if (katexElement.clientWidth < katexElement.scrollWidth) {
114 katexElement.firstElementChild.setAttribute("style", `font-size:${katexElement.clientWidth * 100 / katexElement.scrollWidth}%`);
115 }
116 } else {
117 if (blockElement && mathElement.offsetWidth > blockElement.clientWidth) {
118 mathElement.firstElementChild.setAttribute("style", `font-size:${blockElement.clientWidth * 100 / mathElement.offsetWidth}%`);
119 }
120 }
121 });
122 }
123 } catch (e) {
124 if (isBlock) {
125 genRenderFrame(mathElement);
126 mathElement.firstElementChild.firstElementChild.setAttribute("contenteditable", "false");
127 mathElement.firstElementChild.firstElementChild.innerHTML = e.message;
128 mathElement.firstElementChild.firstElementChild.classList.add("ft__error");
129 } else {
130 mathElement.innerHTML = e.message;
131 mathElement.classList.add("ft__error");
132 }
133 }
134 });
135 });
136 });
137};