CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/components/math/katex.tsx
Views: 687
1
/*
2
More complicated not-necessarily-synchronous (but actually it is sync) math formula component, which
3
works fine on the frontend but NOT on a backend with node.js.
4
5
This supports rendering using KaTeX.
6
7
Right now it is *just* katex, so in fact is synchronous.
8
*/
9
10
import { useEffect, useRef } from "react";
11
import { math_escape, math_unescape } from "@cocalc/util/markdown-utils";
12
import { remove_math, replace_math } from "@cocalc/util/mathjax-utils";
13
import { latexMathToHtmlOrError } from "@cocalc/frontend/misc/math-to-html";
14
import { replace_all } from "@cocalc/util/misc";
15
import { replaceMathBracketDelims } from "./util";
16
17
interface Props {
18
data: string;
19
inMarkdown?: boolean;
20
}
21
22
export default function KaTeX({ data, inMarkdown }: Props) {
23
const ref = useRef<any>(null);
24
data = replaceMathBracketDelims(data);
25
const [text, math] = remove_math(math_escape(data));
26
27
useEffect(() => {
28
// be no-op when math.length == 0.
29
if (ref.current == null) return;
30
// There was an error during attemptKatex below, so will fallback to the old
31
// katex + mathjaxv2 via an old jquery plugin.
32
ref.current.innerHTML = data;
33
// @ts-ignore
34
$(ref.current).katex({ preProcess: true }); // this also calls mathjax as a fallback.
35
}, [data]);
36
37
if (math.length == 0) {
38
// no math and the input is text, so return as is. Definitely do NOT wrap in a span.
39
// See https://github.com/sagemathinc/cocalc/issues/5920
40
return <>{data}</>;
41
}
42
43
if (inMarkdown) {
44
const __html = attemptKatex(text, math);
45
if (__html != null) {
46
// no error -- using katex is allowed and fully worked.
47
return <span dangerouslySetInnerHTML={{ __html }}></span>;
48
}
49
}
50
51
// didn't end up using katex, so we make a span, which we will fill in via that
52
// useEffect above.
53
return <span ref={ref}></span>;
54
}
55
56
function attemptKatex(text: string, math: string[]): undefined | string {
57
// Try to use KaTeX directly, with no jquery or useEffect doing anything:
58
for (let i = 0; i < math.length; i++) {
59
const { __html, err } = latexMathToHtmlOrError(math[i]);
60
if (!err) {
61
math[i] = __html;
62
} else {
63
// there was an error
64
const div = $("<div>")
65
.text(math[i])
66
.css("color", "red")
67
.attr("title", `${err}`);
68
const htmlString = div.prop("outerHTML");
69
math[i] = htmlString;
70
}
71
}
72
// Substitute processed math back in.
73
return replace_all(math_unescape(replace_math(text, math)), "\\$", "$");
74
}
75
76