@liqvid/katex 0.0.3 → 0.1.0
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/README.md +1 -29
- package/dist/cjs/RenderGroup.cjs +76 -0
- package/dist/cjs/fancy.cjs +29 -0
- package/dist/cjs/index.cjs +9 -0
- package/dist/cjs/loading.cjs +67 -0
- package/dist/cjs/plain.cjs +50 -0
- package/dist/esm/RenderGroup.mjs +73 -0
- package/dist/esm/fancy.mjs +26 -0
- package/dist/esm/index.mjs +3 -0
- package/dist/esm/loading.mjs +64 -0
- package/dist/esm/plain.mjs +47 -0
- package/dist/types/RenderGroup.d.ts +1 -0
- package/dist/types/fancy.d.ts +1 -1
- package/package.json +24 -27
- package/dist/index.js +0 -168
- package/dist/index.mjs +0 -206
- package/dist/plain.js +0 -117
- package/dist/plain.mjs +0 -113
package/README.md
CHANGED
|
@@ -1,31 +1,3 @@
|
|
|
1
1
|
# @liqvid/katex
|
|
2
2
|
|
|
3
|
-
[KaTeX](https://katex.org/) integration for [Liqvid](https://liqvidjs.org).
|
|
4
|
-
|
|
5
|
-
## Usage
|
|
6
|
-
|
|
7
|
-
```tsx
|
|
8
|
-
import {KTX} from "@liqvid/katex";
|
|
9
|
-
|
|
10
|
-
function Quadratic() {
|
|
11
|
-
return (
|
|
12
|
-
<div>
|
|
13
|
-
The value of <KTX>x</KTX> is given by the quadratic formula
|
|
14
|
-
<KTX display>{String.raw`x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}`}</KTX>
|
|
15
|
-
</div>
|
|
16
|
-
);
|
|
17
|
-
}
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## Macros
|
|
21
|
-
|
|
22
|
-
For convenience, this module supports loading macro definitions from a file. Simply include a `<script type="math/tex">` tag in the `<head>` of your html, pointing to a tex file containing `\newcommand`s.
|
|
23
|
-
|
|
24
|
-
```html
|
|
25
|
-
<!-- this has to go in <head> -->
|
|
26
|
-
<script src="./macros.tex" type="math/tex"></script>
|
|
27
|
-
```
|
|
28
|
-
```tex
|
|
29
|
-
% macros.tex
|
|
30
|
-
\newcommand{\C}{\mathbb C}
|
|
31
|
-
```
|
|
3
|
+
[KaTeX](https://katex.org/) integration for [Liqvid](https://liqvidjs.org). See https://liqvidjs.org/docs/integrations/katex/ for documentation.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RenderGroup = void 0;
|
|
4
|
+
const react_1 = require("@liqvid/utils/react");
|
|
5
|
+
const liqvid_1 = require("liqvid");
|
|
6
|
+
const react_2 = require("react");
|
|
7
|
+
const fancy_1 = require("./fancy.cjs");
|
|
8
|
+
const plain_1 = require("./plain.cjs");
|
|
9
|
+
/**
|
|
10
|
+
* Wait for several things to be rendered
|
|
11
|
+
*/
|
|
12
|
+
exports.RenderGroup = (0, react_2.forwardRef)(function RenderGroup(props, ref) {
|
|
13
|
+
const [ready, resolve] = (0, react_1.usePromise)();
|
|
14
|
+
// handle
|
|
15
|
+
(0, react_2.useImperativeHandle)(ref, () => ({ ready }));
|
|
16
|
+
const elements = (0, react_2.useRef)([]);
|
|
17
|
+
const promises = (0, react_2.useRef)([]);
|
|
18
|
+
// reparsing
|
|
19
|
+
const player = (0, liqvid_1.usePlayer)();
|
|
20
|
+
(0, react_2.useEffect)(() => {
|
|
21
|
+
// promises
|
|
22
|
+
Promise.all(promises.current).then(() => {
|
|
23
|
+
// reparse
|
|
24
|
+
if (props.reparse) {
|
|
25
|
+
player.reparseTree(leastCommonAncestor(elements.current));
|
|
26
|
+
}
|
|
27
|
+
// ready()
|
|
28
|
+
resolve();
|
|
29
|
+
});
|
|
30
|
+
}, []);
|
|
31
|
+
return (0, react_1.recursiveMap)(props.children, node => {
|
|
32
|
+
if (shouldInspect(node)) {
|
|
33
|
+
const originalRef = node.ref;
|
|
34
|
+
return (0, react_2.cloneElement)(node, {
|
|
35
|
+
ref: (ref) => {
|
|
36
|
+
if (!ref)
|
|
37
|
+
return;
|
|
38
|
+
elements.current.push(ref.domElement);
|
|
39
|
+
promises.current.push(ref.ready);
|
|
40
|
+
// pass along original ref
|
|
41
|
+
if (typeof originalRef === "function") {
|
|
42
|
+
originalRef(ref);
|
|
43
|
+
}
|
|
44
|
+
else if (originalRef && typeof originalRef === "object") {
|
|
45
|
+
originalRef.current = ref;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return node;
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
/**
|
|
54
|
+
* Determine whether the node is a <KTX> element
|
|
55
|
+
* @param node Element to check
|
|
56
|
+
*/
|
|
57
|
+
function shouldInspect(node) {
|
|
58
|
+
return (0, react_2.isValidElement)(node) && typeof node.type === "object" && (node.type === fancy_1.KTX || node.type === plain_1.KTX);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Find least common ancestor of an array of elements
|
|
62
|
+
* @param elements Elements
|
|
63
|
+
* @returns Deepest node containing all passed elements
|
|
64
|
+
*/
|
|
65
|
+
function leastCommonAncestor(elements) {
|
|
66
|
+
if (elements.length === 0) {
|
|
67
|
+
throw new Error("Must pass at least one element");
|
|
68
|
+
}
|
|
69
|
+
let ancestor = elements[0];
|
|
70
|
+
let failing = elements.slice(1);
|
|
71
|
+
while (failing.length > 0) {
|
|
72
|
+
ancestor = ancestor.parentElement;
|
|
73
|
+
failing = failing.filter(node => !ancestor.contains(node));
|
|
74
|
+
}
|
|
75
|
+
return ancestor;
|
|
76
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KTX = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("@liqvid/utils/react");
|
|
6
|
+
const liqvid_1 = require("liqvid");
|
|
7
|
+
const react_2 = require("react");
|
|
8
|
+
const plain_1 = require("./plain.cjs");
|
|
9
|
+
/** Component for KaTeX code */
|
|
10
|
+
exports.KTX = (0, react_2.forwardRef)(function KTX(props, ref) {
|
|
11
|
+
const { obstruct = "canplay canplaythrough", reparse = false, ...attrs } = props;
|
|
12
|
+
const plain = (0, react_2.useRef)();
|
|
13
|
+
const combined = (0, react_1.combineRefs)(plain, ref);
|
|
14
|
+
const player = (0, liqvid_1.usePlayer)();
|
|
15
|
+
(0, react_2.useEffect)(() => {
|
|
16
|
+
// obstruction
|
|
17
|
+
if (obstruct.match(/\bcanplay\b/)) {
|
|
18
|
+
player.obstruct("canplay", plain.current.ready);
|
|
19
|
+
}
|
|
20
|
+
if (obstruct.match("canplaythrough")) {
|
|
21
|
+
player.obstruct("canplaythrough", plain.current.ready);
|
|
22
|
+
}
|
|
23
|
+
// reparsing
|
|
24
|
+
if (reparse) {
|
|
25
|
+
plain.current.ready.then(() => player.reparseTree(plain.current.domElement));
|
|
26
|
+
}
|
|
27
|
+
}, []);
|
|
28
|
+
return ((0, jsx_runtime_1.jsx)(plain_1.KTX, { ref: combined, ...attrs }));
|
|
29
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RenderGroup = exports.KaTeXReady = exports.KTX = void 0;
|
|
4
|
+
var fancy_1 = require("./fancy.cjs");
|
|
5
|
+
Object.defineProperty(exports, "KTX", { enumerable: true, get: function () { return fancy_1.KTX; } });
|
|
6
|
+
var loading_1 = require("./loading.cjs");
|
|
7
|
+
Object.defineProperty(exports, "KaTeXReady", { enumerable: true, get: function () { return loading_1.KaTeXReady; } });
|
|
8
|
+
var RenderGroup_1 = require("./RenderGroup.cjs");
|
|
9
|
+
Object.defineProperty(exports, "RenderGroup", { enumerable: true, get: function () { return RenderGroup_1.RenderGroup; } });
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KaTeXReady = void 0;
|
|
4
|
+
// option of loading KaTeX asynchronously
|
|
5
|
+
const KaTeXLoad = new Promise((resolve) => {
|
|
6
|
+
const script = document.querySelector("script[src*=\"katex.js\"], script[src*=\"katex.min.js\"]");
|
|
7
|
+
if (!script)
|
|
8
|
+
return;
|
|
9
|
+
if (window.hasOwnProperty("katex")) {
|
|
10
|
+
resolve(katex);
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
script.addEventListener("load", () => resolve(katex));
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
// load macros from <head>
|
|
17
|
+
const KaTeXMacros = new Promise((resolve) => {
|
|
18
|
+
const macros = {};
|
|
19
|
+
const scripts = Array.from(document.querySelectorAll("head > script[type='math/tex']"));
|
|
20
|
+
return Promise.all(scripts.map(script => fetch(script.src)
|
|
21
|
+
.then(res => {
|
|
22
|
+
if (res.ok)
|
|
23
|
+
return res.text();
|
|
24
|
+
throw new Error(`${res.status} ${res.statusText}: ${script.src}`);
|
|
25
|
+
})
|
|
26
|
+
.then(tex => {
|
|
27
|
+
Object.assign(macros, parseMacros(tex));
|
|
28
|
+
}))).then(() => resolve(macros));
|
|
29
|
+
});
|
|
30
|
+
/**
|
|
31
|
+
* Ready Promise
|
|
32
|
+
*/
|
|
33
|
+
exports.KaTeXReady = Promise.all([KaTeXLoad, KaTeXMacros]);
|
|
34
|
+
/**
|
|
35
|
+
* Parse \newcommand macros in a file.
|
|
36
|
+
* Also supports \ktxnewcommand (for use in conjunction with MathJax).
|
|
37
|
+
* @param file TeX file to parse
|
|
38
|
+
*/
|
|
39
|
+
function parseMacros(file) {
|
|
40
|
+
const macros = {};
|
|
41
|
+
const rgx = /\\(?:ktx)?newcommand\{(.+?)\}(?:\[\d+\])?\{/g;
|
|
42
|
+
let match;
|
|
43
|
+
while (match = rgx.exec(file)) {
|
|
44
|
+
let body = "";
|
|
45
|
+
const macro = match[1];
|
|
46
|
+
let braceCount = 1;
|
|
47
|
+
for (let i = match.index + match[0].length; (braceCount > 0) && (i < file.length); ++i) {
|
|
48
|
+
const char = file[i];
|
|
49
|
+
if (char === "{") {
|
|
50
|
+
braceCount++;
|
|
51
|
+
}
|
|
52
|
+
else if (char === "}") {
|
|
53
|
+
braceCount--;
|
|
54
|
+
if (braceCount === 0)
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
else if (char === "\\") {
|
|
58
|
+
body += file.slice(i, i + 2);
|
|
59
|
+
++i;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
body += char;
|
|
63
|
+
}
|
|
64
|
+
macros[macro] = body;
|
|
65
|
+
}
|
|
66
|
+
return macros;
|
|
67
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KTX = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("@liqvid/utils/react");
|
|
6
|
+
const react_2 = require("react");
|
|
7
|
+
const loading_1 = require("./loading.cjs");
|
|
8
|
+
/** Component for KaTeX code */
|
|
9
|
+
exports.KTX = (0, react_2.forwardRef)(function KTX(props, ref) {
|
|
10
|
+
const spanRef = (0, react_2.useRef)();
|
|
11
|
+
const { children, display = false, ...attrs } = props;
|
|
12
|
+
const [ready, resolve] = (0, react_1.usePromise)();
|
|
13
|
+
// handle
|
|
14
|
+
(0, react_2.useImperativeHandle)(ref, () => ({
|
|
15
|
+
domElement: spanRef.current,
|
|
16
|
+
ready
|
|
17
|
+
}));
|
|
18
|
+
(0, react_2.useEffect)(() => {
|
|
19
|
+
loading_1.KaTeXReady.then(([katex, macros]) => {
|
|
20
|
+
katex.render(children.toString(), spanRef.current, {
|
|
21
|
+
displayMode: !!display,
|
|
22
|
+
macros,
|
|
23
|
+
strict: "ignore",
|
|
24
|
+
throwOnError: false,
|
|
25
|
+
trust: true
|
|
26
|
+
});
|
|
27
|
+
/* move katex into placeholder element */
|
|
28
|
+
const child = spanRef.current.firstElementChild;
|
|
29
|
+
// copy classes
|
|
30
|
+
for (let i = 0, len = child.classList.length; i < len; ++i) {
|
|
31
|
+
spanRef.current.classList.add(child.classList.item(i));
|
|
32
|
+
}
|
|
33
|
+
// move children
|
|
34
|
+
while (child.childNodes.length > 0) {
|
|
35
|
+
spanRef.current.appendChild(child.firstChild);
|
|
36
|
+
}
|
|
37
|
+
// delete child
|
|
38
|
+
child.remove();
|
|
39
|
+
// resolve promise
|
|
40
|
+
resolve();
|
|
41
|
+
});
|
|
42
|
+
}, [children]);
|
|
43
|
+
// Google Chrome fails without this
|
|
44
|
+
if (display) {
|
|
45
|
+
if (!attrs.style)
|
|
46
|
+
attrs.style = {};
|
|
47
|
+
attrs.style.display = "block";
|
|
48
|
+
}
|
|
49
|
+
return ((0, jsx_runtime_1.jsx)("span", { ...attrs, ref: spanRef }));
|
|
50
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { recursiveMap, usePromise } from "@liqvid/utils/react";
|
|
2
|
+
import { usePlayer } from "liqvid";
|
|
3
|
+
import { cloneElement, forwardRef, isValidElement, useEffect, useImperativeHandle, useRef } from "react";
|
|
4
|
+
import { KTX } from "./fancy.mjs";
|
|
5
|
+
import { KTX as KTXPlain } from "./plain.mjs";
|
|
6
|
+
/**
|
|
7
|
+
* Wait for several things to be rendered
|
|
8
|
+
*/
|
|
9
|
+
export const RenderGroup = forwardRef(function RenderGroup(props, ref) {
|
|
10
|
+
const [ready, resolve] = usePromise();
|
|
11
|
+
// handle
|
|
12
|
+
useImperativeHandle(ref, () => ({ ready }));
|
|
13
|
+
const elements = useRef([]);
|
|
14
|
+
const promises = useRef([]);
|
|
15
|
+
// reparsing
|
|
16
|
+
const player = usePlayer();
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
// promises
|
|
19
|
+
Promise.all(promises.current).then(() => {
|
|
20
|
+
// reparse
|
|
21
|
+
if (props.reparse) {
|
|
22
|
+
player.reparseTree(leastCommonAncestor(elements.current));
|
|
23
|
+
}
|
|
24
|
+
// ready()
|
|
25
|
+
resolve();
|
|
26
|
+
});
|
|
27
|
+
}, []);
|
|
28
|
+
return recursiveMap(props.children, node => {
|
|
29
|
+
if (shouldInspect(node)) {
|
|
30
|
+
const originalRef = node.ref;
|
|
31
|
+
return cloneElement(node, {
|
|
32
|
+
ref: (ref) => {
|
|
33
|
+
if (!ref)
|
|
34
|
+
return;
|
|
35
|
+
elements.current.push(ref.domElement);
|
|
36
|
+
promises.current.push(ref.ready);
|
|
37
|
+
// pass along original ref
|
|
38
|
+
if (typeof originalRef === "function") {
|
|
39
|
+
originalRef(ref);
|
|
40
|
+
}
|
|
41
|
+
else if (originalRef && typeof originalRef === "object") {
|
|
42
|
+
originalRef.current = ref;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return node;
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
/**
|
|
51
|
+
* Determine whether the node is a <KTX> element
|
|
52
|
+
* @param node Element to check
|
|
53
|
+
*/
|
|
54
|
+
function shouldInspect(node) {
|
|
55
|
+
return isValidElement(node) && typeof node.type === "object" && (node.type === KTX || node.type === KTXPlain);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Find least common ancestor of an array of elements
|
|
59
|
+
* @param elements Elements
|
|
60
|
+
* @returns Deepest node containing all passed elements
|
|
61
|
+
*/
|
|
62
|
+
function leastCommonAncestor(elements) {
|
|
63
|
+
if (elements.length === 0) {
|
|
64
|
+
throw new Error("Must pass at least one element");
|
|
65
|
+
}
|
|
66
|
+
let ancestor = elements[0];
|
|
67
|
+
let failing = elements.slice(1);
|
|
68
|
+
while (failing.length > 0) {
|
|
69
|
+
ancestor = ancestor.parentElement;
|
|
70
|
+
failing = failing.filter(node => !ancestor.contains(node));
|
|
71
|
+
}
|
|
72
|
+
return ancestor;
|
|
73
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { combineRefs } from "@liqvid/utils/react";
|
|
3
|
+
import { usePlayer } from "liqvid";
|
|
4
|
+
import { forwardRef, useEffect, useRef } from "react";
|
|
5
|
+
import { KTX as KTXPlain } from "./plain.mjs";
|
|
6
|
+
/** Component for KaTeX code */
|
|
7
|
+
export const KTX = forwardRef(function KTX(props, ref) {
|
|
8
|
+
const { obstruct = "canplay canplaythrough", reparse = false, ...attrs } = props;
|
|
9
|
+
const plain = useRef();
|
|
10
|
+
const combined = combineRefs(plain, ref);
|
|
11
|
+
const player = usePlayer();
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
// obstruction
|
|
14
|
+
if (obstruct.match(/\bcanplay\b/)) {
|
|
15
|
+
player.obstruct("canplay", plain.current.ready);
|
|
16
|
+
}
|
|
17
|
+
if (obstruct.match("canplaythrough")) {
|
|
18
|
+
player.obstruct("canplaythrough", plain.current.ready);
|
|
19
|
+
}
|
|
20
|
+
// reparsing
|
|
21
|
+
if (reparse) {
|
|
22
|
+
plain.current.ready.then(() => player.reparseTree(plain.current.domElement));
|
|
23
|
+
}
|
|
24
|
+
}, []);
|
|
25
|
+
return (_jsx(KTXPlain, { ref: combined, ...attrs }));
|
|
26
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// option of loading KaTeX asynchronously
|
|
2
|
+
const KaTeXLoad = new Promise((resolve) => {
|
|
3
|
+
const script = document.querySelector("script[src*=\"katex.js\"], script[src*=\"katex.min.js\"]");
|
|
4
|
+
if (!script)
|
|
5
|
+
return;
|
|
6
|
+
if (window.hasOwnProperty("katex")) {
|
|
7
|
+
resolve(katex);
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
script.addEventListener("load", () => resolve(katex));
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
// load macros from <head>
|
|
14
|
+
const KaTeXMacros = new Promise((resolve) => {
|
|
15
|
+
const macros = {};
|
|
16
|
+
const scripts = Array.from(document.querySelectorAll("head > script[type='math/tex']"));
|
|
17
|
+
return Promise.all(scripts.map(script => fetch(script.src)
|
|
18
|
+
.then(res => {
|
|
19
|
+
if (res.ok)
|
|
20
|
+
return res.text();
|
|
21
|
+
throw new Error(`${res.status} ${res.statusText}: ${script.src}`);
|
|
22
|
+
})
|
|
23
|
+
.then(tex => {
|
|
24
|
+
Object.assign(macros, parseMacros(tex));
|
|
25
|
+
}))).then(() => resolve(macros));
|
|
26
|
+
});
|
|
27
|
+
/**
|
|
28
|
+
* Ready Promise
|
|
29
|
+
*/
|
|
30
|
+
export const KaTeXReady = Promise.all([KaTeXLoad, KaTeXMacros]);
|
|
31
|
+
/**
|
|
32
|
+
* Parse \newcommand macros in a file.
|
|
33
|
+
* Also supports \ktxnewcommand (for use in conjunction with MathJax).
|
|
34
|
+
* @param file TeX file to parse
|
|
35
|
+
*/
|
|
36
|
+
function parseMacros(file) {
|
|
37
|
+
const macros = {};
|
|
38
|
+
const rgx = /\\(?:ktx)?newcommand\{(.+?)\}(?:\[\d+\])?\{/g;
|
|
39
|
+
let match;
|
|
40
|
+
while (match = rgx.exec(file)) {
|
|
41
|
+
let body = "";
|
|
42
|
+
const macro = match[1];
|
|
43
|
+
let braceCount = 1;
|
|
44
|
+
for (let i = match.index + match[0].length; (braceCount > 0) && (i < file.length); ++i) {
|
|
45
|
+
const char = file[i];
|
|
46
|
+
if (char === "{") {
|
|
47
|
+
braceCount++;
|
|
48
|
+
}
|
|
49
|
+
else if (char === "}") {
|
|
50
|
+
braceCount--;
|
|
51
|
+
if (braceCount === 0)
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
else if (char === "\\") {
|
|
55
|
+
body += file.slice(i, i + 2);
|
|
56
|
+
++i;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
body += char;
|
|
60
|
+
}
|
|
61
|
+
macros[macro] = body;
|
|
62
|
+
}
|
|
63
|
+
return macros;
|
|
64
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { usePromise } from "@liqvid/utils/react";
|
|
3
|
+
import { forwardRef, useEffect, useImperativeHandle, useRef } from "react";
|
|
4
|
+
import { KaTeXReady } from "./loading.mjs";
|
|
5
|
+
/** Component for KaTeX code */
|
|
6
|
+
export const KTX = forwardRef(function KTX(props, ref) {
|
|
7
|
+
const spanRef = useRef();
|
|
8
|
+
const { children, display = false, ...attrs } = props;
|
|
9
|
+
const [ready, resolve] = usePromise();
|
|
10
|
+
// handle
|
|
11
|
+
useImperativeHandle(ref, () => ({
|
|
12
|
+
domElement: spanRef.current,
|
|
13
|
+
ready
|
|
14
|
+
}));
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
KaTeXReady.then(([katex, macros]) => {
|
|
17
|
+
katex.render(children.toString(), spanRef.current, {
|
|
18
|
+
displayMode: !!display,
|
|
19
|
+
macros,
|
|
20
|
+
strict: "ignore",
|
|
21
|
+
throwOnError: false,
|
|
22
|
+
trust: true
|
|
23
|
+
});
|
|
24
|
+
/* move katex into placeholder element */
|
|
25
|
+
const child = spanRef.current.firstElementChild;
|
|
26
|
+
// copy classes
|
|
27
|
+
for (let i = 0, len = child.classList.length; i < len; ++i) {
|
|
28
|
+
spanRef.current.classList.add(child.classList.item(i));
|
|
29
|
+
}
|
|
30
|
+
// move children
|
|
31
|
+
while (child.childNodes.length > 0) {
|
|
32
|
+
spanRef.current.appendChild(child.firstChild);
|
|
33
|
+
}
|
|
34
|
+
// delete child
|
|
35
|
+
child.remove();
|
|
36
|
+
// resolve promise
|
|
37
|
+
resolve();
|
|
38
|
+
});
|
|
39
|
+
}, [children]);
|
|
40
|
+
// Google Chrome fails without this
|
|
41
|
+
if (display) {
|
|
42
|
+
if (!attrs.style)
|
|
43
|
+
attrs.style = {};
|
|
44
|
+
attrs.style.display = "block";
|
|
45
|
+
}
|
|
46
|
+
return (_jsx("span", { ...attrs, ref: spanRef }));
|
|
47
|
+
});
|
package/dist/types/fancy.d.ts
CHANGED
|
@@ -13,5 +13,5 @@ interface Props extends React.ComponentProps<typeof KTXPlain> {
|
|
|
13
13
|
reparse?: boolean;
|
|
14
14
|
}
|
|
15
15
|
/** Component for KaTeX code */
|
|
16
|
-
export declare const KTX: import("react").ForwardRefExoticComponent<Pick<Props, "
|
|
16
|
+
export declare const KTX: import("react").ForwardRefExoticComponent<Pick<Props, "key" | "id" | "color" | "display" | "translate" | "hidden" | "dir" | "slot" | "style" | "title" | "accessKey" | "draggable" | "lang" | "className" | "prefix" | "children" | "contentEditable" | "inputMode" | "tabIndex" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "contextMenu" | "placeholder" | "spellCheck" | "radioGroup" | "role" | "about" | "datatype" | "inlist" | "property" | "resource" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "is" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDrag" | "onDragCapture" | "onDragEnd" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStart" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerEnterCapture" | "onPointerLeave" | "onPointerLeaveCapture" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStart" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "obstruct" | "reparse"> & import("react").RefAttributes<Handle>>;
|
|
17
17
|
export {};
|
package/package.json
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liqvid/katex",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "KaTeX integration for Liqvid",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist/*"
|
|
7
7
|
],
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
|
-
"import": "./dist/index.mjs",
|
|
11
|
-
"require": "./dist/index.
|
|
10
|
+
"import": "./dist/esm/index.mjs",
|
|
11
|
+
"require": "./dist/cjs/index.cjs"
|
|
12
12
|
},
|
|
13
13
|
"./plain": {
|
|
14
|
-
"import": "./dist/plain.mjs",
|
|
15
|
-
"require": "./dist/plain.
|
|
14
|
+
"import": "./dist/esm/plain.mjs",
|
|
15
|
+
"require": "./dist/cjs/plain.cjs"
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
18
|
"typesVersions": {
|
|
19
19
|
"*": {
|
|
20
20
|
"*": [
|
|
21
|
-
"./dist/types
|
|
21
|
+
"./dist/types/*.d.ts"
|
|
22
22
|
]
|
|
23
23
|
}
|
|
24
24
|
},
|
|
@@ -27,10 +27,6 @@
|
|
|
27
27
|
"liqvid",
|
|
28
28
|
"katex"
|
|
29
29
|
],
|
|
30
|
-
"scripts": {
|
|
31
|
-
"build": "tsc --build --force",
|
|
32
|
-
"lint": "eslint --ext ts,tsx --fix src"
|
|
33
|
-
},
|
|
34
30
|
"repository": {
|
|
35
31
|
"type": "git",
|
|
36
32
|
"url": "git+https://github.com/liqvidjs/liqvid.git"
|
|
@@ -41,10 +37,10 @@
|
|
|
41
37
|
"homepage": "https://github.com/liqvidjs/liqvid/tree/main/packages/katex",
|
|
42
38
|
"license": "MIT",
|
|
43
39
|
"peerDependencies": {
|
|
44
|
-
"@types/katex": "
|
|
45
|
-
"@types/react": ">=
|
|
46
|
-
"liqvid": "^2.1.
|
|
47
|
-
"react": ">=
|
|
40
|
+
"@types/katex": ">=0.14.0",
|
|
41
|
+
"@types/react": ">=18.0.0",
|
|
42
|
+
"liqvid": "^2.1.7",
|
|
43
|
+
"react": ">=18.1.0"
|
|
48
44
|
},
|
|
49
45
|
"peerDependenciesMeta": {
|
|
50
46
|
"liqvid": {
|
|
@@ -52,18 +48,19 @@
|
|
|
52
48
|
}
|
|
53
49
|
},
|
|
54
50
|
"devDependencies": {
|
|
55
|
-
"
|
|
56
|
-
"@typescript-eslint/eslint-plugin": "^5.14.0",
|
|
57
|
-
"@typescript-eslint/parser": "^5.14.0",
|
|
58
|
-
"@yuri/eslint-config": "^1.0.1",
|
|
59
|
-
"eslint": "^8.11.0",
|
|
60
|
-
"eslint-plugin-react": "^7.29.3",
|
|
61
|
-
"liqvid": "^2.1.1",
|
|
62
|
-
"react": "^17.0.2",
|
|
63
|
-
"rollup": "^2.70.0",
|
|
64
|
-
"typescript": "^4.6.2"
|
|
51
|
+
"liqvid": "^2.1.7"
|
|
65
52
|
},
|
|
66
53
|
"dependencies": {
|
|
67
|
-
"@liqvid/utils": "^1.
|
|
68
|
-
}
|
|
69
|
-
|
|
54
|
+
"@liqvid/utils": "^1.6.0"
|
|
55
|
+
},
|
|
56
|
+
"sideEffects": false,
|
|
57
|
+
"type": "module",
|
|
58
|
+
"scripts": {
|
|
59
|
+
"build": "pnpm build:clean && pnpm build:js && pnpm build:postclean",
|
|
60
|
+
"build:clean": "rm -rf dist",
|
|
61
|
+
"build:js": "tsc --module esnext --outDir dist/esm; tsc --module commonjs --outDir dist/cjs; node ../../build.mjs",
|
|
62
|
+
"build:postclean": "rm dist/tsconfig.tsbuildinfo",
|
|
63
|
+
"lint": "eslint --ext ts,tsx --fix src"
|
|
64
|
+
},
|
|
65
|
+
"readme": "# @liqvid/katex\n\n[KaTeX](https://katex.org/) integration for [Liqvid](https://liqvidjs.org). See https://liqvidjs.org/docs/integrations/katex/ for documentation.\n"
|
|
66
|
+
}
|
package/dist/index.js
DELETED
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
-
|
|
5
|
-
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
-
var react$1 = require('@liqvid/utils/react');
|
|
7
|
-
var liqvid = require('liqvid');
|
|
8
|
-
var react = require('react');
|
|
9
|
-
var plain = require('./plain');
|
|
10
|
-
|
|
11
|
-
/** Component for KaTeX code */
|
|
12
|
-
const KTX = react.forwardRef(function KTX(props, ref) {
|
|
13
|
-
const { obstruct = "canplay canplaythrough", reparse = false, ...attrs } = props;
|
|
14
|
-
const plain$1 = react.useRef();
|
|
15
|
-
const combined = react$1.combineRefs(plain$1, ref);
|
|
16
|
-
const player = liqvid.usePlayer();
|
|
17
|
-
react.useEffect(() => {
|
|
18
|
-
// obstruction
|
|
19
|
-
if (obstruct.match(/\bcanplay\b/)) {
|
|
20
|
-
player.obstruct("canplay", plain$1.current.ready);
|
|
21
|
-
}
|
|
22
|
-
if (obstruct.match("canplaythrough")) {
|
|
23
|
-
player.obstruct("canplaythrough", plain$1.current.ready);
|
|
24
|
-
}
|
|
25
|
-
// reparsing
|
|
26
|
-
if (reparse) {
|
|
27
|
-
plain$1.current.ready.then(() => player.reparseTree(plain$1.current.domElement));
|
|
28
|
-
}
|
|
29
|
-
}, []);
|
|
30
|
-
return (jsxRuntime.jsx(plain.KTX, { ref: combined, ...attrs }));
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
// option of loading KaTeX asynchronously
|
|
34
|
-
const KaTeXLoad = new Promise((resolve) => {
|
|
35
|
-
const script = document.querySelector("script[src*=\"katex.js\"], script[src*=\"katex.min.js\"]");
|
|
36
|
-
if (!script)
|
|
37
|
-
return;
|
|
38
|
-
if (window.hasOwnProperty("katex")) {
|
|
39
|
-
resolve(katex);
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
script.addEventListener("load", () => resolve(katex));
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
// load macros from <head>
|
|
46
|
-
const KaTeXMacros = new Promise((resolve) => {
|
|
47
|
-
const macros = {};
|
|
48
|
-
const scripts = Array.from(document.querySelectorAll("head > script[type='math/tex']"));
|
|
49
|
-
return Promise.all(scripts.map(script => fetch(script.src)
|
|
50
|
-
.then(res => {
|
|
51
|
-
if (res.ok)
|
|
52
|
-
return res.text();
|
|
53
|
-
throw new Error(`${res.status} ${res.statusText}: ${script.src}`);
|
|
54
|
-
})
|
|
55
|
-
.then(tex => {
|
|
56
|
-
Object.assign(macros, parseMacros(tex));
|
|
57
|
-
}))).then(() => resolve(macros));
|
|
58
|
-
});
|
|
59
|
-
/**
|
|
60
|
-
* Ready Promise
|
|
61
|
-
*/
|
|
62
|
-
const KaTeXReady = Promise.all([KaTeXLoad, KaTeXMacros]);
|
|
63
|
-
/**
|
|
64
|
-
Parse \newcommand macros in a file.
|
|
65
|
-
Also supports \ktxnewcommand (for use in conjunction with MathJax).
|
|
66
|
-
*/
|
|
67
|
-
function parseMacros(file) {
|
|
68
|
-
const macros = {};
|
|
69
|
-
const rgx = /\\(?:ktx)?newcommand\{(.+?)\}(?:\[\d+\])?\{/g;
|
|
70
|
-
let match;
|
|
71
|
-
while (match = rgx.exec(file)) {
|
|
72
|
-
let body = "";
|
|
73
|
-
const macro = match[1];
|
|
74
|
-
let braceCount = 1;
|
|
75
|
-
for (let i = match.index + match[0].length; (braceCount > 0) && (i < file.length); ++i) {
|
|
76
|
-
const char = file[i];
|
|
77
|
-
if (char === "{") {
|
|
78
|
-
braceCount++;
|
|
79
|
-
}
|
|
80
|
-
else if (char === "}") {
|
|
81
|
-
braceCount--;
|
|
82
|
-
if (braceCount === 0)
|
|
83
|
-
break;
|
|
84
|
-
}
|
|
85
|
-
else if (char === "\\") {
|
|
86
|
-
body += file.slice(i, i + 2);
|
|
87
|
-
++i;
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
body += char;
|
|
91
|
-
}
|
|
92
|
-
macros[macro] = body;
|
|
93
|
-
}
|
|
94
|
-
return macros;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Wait for several things to be rendered
|
|
99
|
-
*/
|
|
100
|
-
const RenderGroup = react.forwardRef(function RenderGroup(props, ref) {
|
|
101
|
-
const [ready, resolve] = react$1.usePromise();
|
|
102
|
-
// handle
|
|
103
|
-
react.useImperativeHandle(ref, () => ({ ready }));
|
|
104
|
-
const elements = react.useRef([]);
|
|
105
|
-
const promises = react.useRef([]);
|
|
106
|
-
// reparsing
|
|
107
|
-
const player = liqvid.usePlayer();
|
|
108
|
-
react.useEffect(() => {
|
|
109
|
-
// promises
|
|
110
|
-
Promise.all(promises.current).then(() => {
|
|
111
|
-
// reparse
|
|
112
|
-
if (props.reparse) {
|
|
113
|
-
player.reparseTree(leastCommonAncestor(elements.current));
|
|
114
|
-
}
|
|
115
|
-
// ready()
|
|
116
|
-
resolve();
|
|
117
|
-
});
|
|
118
|
-
}, []);
|
|
119
|
-
return react$1.recursiveMap(props.children, node => {
|
|
120
|
-
if (shouldInspect(node)) {
|
|
121
|
-
const originalRef = node.ref;
|
|
122
|
-
return react.cloneElement(node, {
|
|
123
|
-
ref: (ref) => {
|
|
124
|
-
if (!ref)
|
|
125
|
-
return;
|
|
126
|
-
elements.current.push(ref.domElement);
|
|
127
|
-
promises.current.push(ref.ready);
|
|
128
|
-
// pass along original ref
|
|
129
|
-
if (typeof originalRef === "function") {
|
|
130
|
-
originalRef(ref);
|
|
131
|
-
}
|
|
132
|
-
else if (originalRef && typeof originalRef === "object") {
|
|
133
|
-
originalRef.current = ref;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
return node;
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
/**
|
|
142
|
-
* Determine whether the node is a <KTX> element
|
|
143
|
-
* @param node Element to check
|
|
144
|
-
*/
|
|
145
|
-
function shouldInspect(node) {
|
|
146
|
-
return react.isValidElement(node) && typeof node.type === "object" && (node.type === KTX || node.type === plain.KTX);
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Find least common ancestor of an array of elements
|
|
150
|
-
* @param elements Elements
|
|
151
|
-
* @returns Deepest node containing all passed elements
|
|
152
|
-
*/
|
|
153
|
-
function leastCommonAncestor(elements) {
|
|
154
|
-
if (elements.length === 0) {
|
|
155
|
-
throw new Error("Must pass at least one element");
|
|
156
|
-
}
|
|
157
|
-
let ancestor = elements[0];
|
|
158
|
-
let failing = elements.slice(1);
|
|
159
|
-
while (failing.length > 0) {
|
|
160
|
-
ancestor = ancestor.parentElement;
|
|
161
|
-
failing = failing.filter(node => !ancestor.contains(node));
|
|
162
|
-
}
|
|
163
|
-
return ancestor;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
exports.KTX = KTX;
|
|
167
|
-
exports.KaTeXReady = KaTeXReady;
|
|
168
|
-
exports.RenderGroup = RenderGroup;
|
package/dist/index.mjs
DELETED
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
import { jsx } from 'react/jsx-runtime';
|
|
2
|
-
import { combineRefs, usePromise, recursiveMap } from '@liqvid/utils/react';
|
|
3
|
-
import { usePlayer } from 'liqvid';
|
|
4
|
-
import { forwardRef, useRef, useEffect, useImperativeHandle, isValidElement, cloneElement } from 'react';
|
|
5
|
-
import { KTX as KTX$2 } from './plain.mjs';
|
|
6
|
-
|
|
7
|
-
/** Component for KaTeX code */
|
|
8
|
-
const KTX$1 = forwardRef(function KTX(props, ref) {
|
|
9
|
-
const { obstruct = "canplay canplaythrough", reparse = false, ...attrs } = props;
|
|
10
|
-
const plain = useRef();
|
|
11
|
-
const combined = combineRefs(plain, ref);
|
|
12
|
-
const player = usePlayer();
|
|
13
|
-
useEffect(() => {
|
|
14
|
-
// obstruction
|
|
15
|
-
if (obstruct.match(/\bcanplay\b/)) {
|
|
16
|
-
player.obstruct("canplay", plain.current.ready);
|
|
17
|
-
}
|
|
18
|
-
if (obstruct.match("canplaythrough")) {
|
|
19
|
-
player.obstruct("canplaythrough", plain.current.ready);
|
|
20
|
-
}
|
|
21
|
-
// reparsing
|
|
22
|
-
if (reparse) {
|
|
23
|
-
plain.current.ready.then(() => player.reparseTree(plain.current.domElement));
|
|
24
|
-
}
|
|
25
|
-
}, []);
|
|
26
|
-
return (jsx(KTX$2, { ref: combined, ...attrs }));
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
// option of loading KaTeX asynchronously
|
|
30
|
-
const KaTeXLoad = new Promise((resolve) => {
|
|
31
|
-
const script = document.querySelector("script[src*=\"katex.js\"], script[src*=\"katex.min.js\"]");
|
|
32
|
-
if (!script)
|
|
33
|
-
return;
|
|
34
|
-
if (window.hasOwnProperty("katex")) {
|
|
35
|
-
resolve(katex);
|
|
36
|
-
}
|
|
37
|
-
else {
|
|
38
|
-
script.addEventListener("load", () => resolve(katex));
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
// load macros from <head>
|
|
42
|
-
const KaTeXMacros = new Promise((resolve) => {
|
|
43
|
-
const macros = {};
|
|
44
|
-
const scripts = Array.from(document.querySelectorAll("head > script[type='math/tex']"));
|
|
45
|
-
return Promise.all(scripts.map(script => fetch(script.src)
|
|
46
|
-
.then(res => {
|
|
47
|
-
if (res.ok)
|
|
48
|
-
return res.text();
|
|
49
|
-
throw new Error(`${res.status} ${res.statusText}: ${script.src}`);
|
|
50
|
-
})
|
|
51
|
-
.then(tex => {
|
|
52
|
-
Object.assign(macros, parseMacros(tex));
|
|
53
|
-
}))).then(() => resolve(macros));
|
|
54
|
-
});
|
|
55
|
-
/**
|
|
56
|
-
* Ready Promise
|
|
57
|
-
*/
|
|
58
|
-
const KaTeXReady = Promise.all([KaTeXLoad, KaTeXMacros]);
|
|
59
|
-
/**
|
|
60
|
-
Parse \newcommand macros in a file.
|
|
61
|
-
Also supports \ktxnewcommand (for use in conjunction with MathJax).
|
|
62
|
-
*/
|
|
63
|
-
function parseMacros(file) {
|
|
64
|
-
const macros = {};
|
|
65
|
-
const rgx = /\\(?:ktx)?newcommand\{(.+?)\}(?:\[\d+\])?\{/g;
|
|
66
|
-
let match;
|
|
67
|
-
while (match = rgx.exec(file)) {
|
|
68
|
-
let body = "";
|
|
69
|
-
const macro = match[1];
|
|
70
|
-
let braceCount = 1;
|
|
71
|
-
for (let i = match.index + match[0].length; (braceCount > 0) && (i < file.length); ++i) {
|
|
72
|
-
const char = file[i];
|
|
73
|
-
if (char === "{") {
|
|
74
|
-
braceCount++;
|
|
75
|
-
}
|
|
76
|
-
else if (char === "}") {
|
|
77
|
-
braceCount--;
|
|
78
|
-
if (braceCount === 0)
|
|
79
|
-
break;
|
|
80
|
-
}
|
|
81
|
-
else if (char === "\\") {
|
|
82
|
-
body += file.slice(i, i + 2);
|
|
83
|
-
++i;
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
body += char;
|
|
87
|
-
}
|
|
88
|
-
macros[macro] = body;
|
|
89
|
-
}
|
|
90
|
-
return macros;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/** Component for KaTeX code */
|
|
94
|
-
const KTX = forwardRef(function KTX(props, ref) {
|
|
95
|
-
const spanRef = useRef();
|
|
96
|
-
const { children, display = false, ...attrs } = props;
|
|
97
|
-
const [ready, resolve] = usePromise();
|
|
98
|
-
// handle
|
|
99
|
-
useImperativeHandle(ref, () => ({
|
|
100
|
-
domElement: spanRef.current,
|
|
101
|
-
ready
|
|
102
|
-
}));
|
|
103
|
-
useEffect(() => {
|
|
104
|
-
KaTeXReady.then(([katex, macros]) => {
|
|
105
|
-
katex.render(children.toString(), spanRef.current, {
|
|
106
|
-
displayMode: !!display,
|
|
107
|
-
macros,
|
|
108
|
-
strict: "ignore",
|
|
109
|
-
throwOnError: false,
|
|
110
|
-
trust: true
|
|
111
|
-
});
|
|
112
|
-
/* move katex into placeholder element */
|
|
113
|
-
const child = spanRef.current.firstElementChild;
|
|
114
|
-
// copy classes
|
|
115
|
-
for (let i = 0, len = child.classList.length; i < len; ++i) {
|
|
116
|
-
spanRef.current.classList.add(child.classList.item(i));
|
|
117
|
-
}
|
|
118
|
-
// move children
|
|
119
|
-
while (child.childNodes.length > 0) {
|
|
120
|
-
spanRef.current.appendChild(child.firstChild);
|
|
121
|
-
}
|
|
122
|
-
// delete child
|
|
123
|
-
child.remove();
|
|
124
|
-
// resolve promise
|
|
125
|
-
resolve();
|
|
126
|
-
});
|
|
127
|
-
}, [children]);
|
|
128
|
-
// Google Chrome fails without this
|
|
129
|
-
if (display) {
|
|
130
|
-
if (!attrs.style)
|
|
131
|
-
attrs.style = {};
|
|
132
|
-
attrs.style.display = "block";
|
|
133
|
-
}
|
|
134
|
-
return (jsx("span", { ...attrs, ref: spanRef }));
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Wait for several things to be rendered
|
|
139
|
-
*/
|
|
140
|
-
const RenderGroup = forwardRef(function RenderGroup(props, ref) {
|
|
141
|
-
const [ready, resolve] = usePromise();
|
|
142
|
-
// handle
|
|
143
|
-
useImperativeHandle(ref, () => ({ ready }));
|
|
144
|
-
const elements = useRef([]);
|
|
145
|
-
const promises = useRef([]);
|
|
146
|
-
// reparsing
|
|
147
|
-
const player = usePlayer();
|
|
148
|
-
useEffect(() => {
|
|
149
|
-
// promises
|
|
150
|
-
Promise.all(promises.current).then(() => {
|
|
151
|
-
// reparse
|
|
152
|
-
if (props.reparse) {
|
|
153
|
-
player.reparseTree(leastCommonAncestor(elements.current));
|
|
154
|
-
}
|
|
155
|
-
// ready()
|
|
156
|
-
resolve();
|
|
157
|
-
});
|
|
158
|
-
}, []);
|
|
159
|
-
return recursiveMap(props.children, node => {
|
|
160
|
-
if (shouldInspect(node)) {
|
|
161
|
-
const originalRef = node.ref;
|
|
162
|
-
return cloneElement(node, {
|
|
163
|
-
ref: (ref) => {
|
|
164
|
-
if (!ref)
|
|
165
|
-
return;
|
|
166
|
-
elements.current.push(ref.domElement);
|
|
167
|
-
promises.current.push(ref.ready);
|
|
168
|
-
// pass along original ref
|
|
169
|
-
if (typeof originalRef === "function") {
|
|
170
|
-
originalRef(ref);
|
|
171
|
-
}
|
|
172
|
-
else if (originalRef && typeof originalRef === "object") {
|
|
173
|
-
originalRef.current = ref;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
return node;
|
|
179
|
-
});
|
|
180
|
-
});
|
|
181
|
-
/**
|
|
182
|
-
* Determine whether the node is a <KTX> element
|
|
183
|
-
* @param node Element to check
|
|
184
|
-
*/
|
|
185
|
-
function shouldInspect(node) {
|
|
186
|
-
return isValidElement(node) && typeof node.type === "object" && (node.type === KTX$1 || node.type === KTX);
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* Find least common ancestor of an array of elements
|
|
190
|
-
* @param elements Elements
|
|
191
|
-
* @returns Deepest node containing all passed elements
|
|
192
|
-
*/
|
|
193
|
-
function leastCommonAncestor(elements) {
|
|
194
|
-
if (elements.length === 0) {
|
|
195
|
-
throw new Error("Must pass at least one element");
|
|
196
|
-
}
|
|
197
|
-
let ancestor = elements[0];
|
|
198
|
-
let failing = elements.slice(1);
|
|
199
|
-
while (failing.length > 0) {
|
|
200
|
-
ancestor = ancestor.parentElement;
|
|
201
|
-
failing = failing.filter(node => !ancestor.contains(node));
|
|
202
|
-
}
|
|
203
|
-
return ancestor;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
export { KTX$1 as KTX, KaTeXReady, RenderGroup };
|
package/dist/plain.js
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
-
|
|
5
|
-
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
-
var react = require('react');
|
|
7
|
-
var react$1 = require('@liqvid/utils/react');
|
|
8
|
-
|
|
9
|
-
// option of loading KaTeX asynchronously
|
|
10
|
-
const KaTeXLoad = new Promise((resolve) => {
|
|
11
|
-
const script = document.querySelector("script[src*=\"katex.js\"], script[src*=\"katex.min.js\"]");
|
|
12
|
-
if (!script)
|
|
13
|
-
return;
|
|
14
|
-
if (window.hasOwnProperty("katex")) {
|
|
15
|
-
resolve(katex);
|
|
16
|
-
}
|
|
17
|
-
else {
|
|
18
|
-
script.addEventListener("load", () => resolve(katex));
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
// load macros from <head>
|
|
22
|
-
const KaTeXMacros = new Promise((resolve) => {
|
|
23
|
-
const macros = {};
|
|
24
|
-
const scripts = Array.from(document.querySelectorAll("head > script[type='math/tex']"));
|
|
25
|
-
return Promise.all(scripts.map(script => fetch(script.src)
|
|
26
|
-
.then(res => {
|
|
27
|
-
if (res.ok)
|
|
28
|
-
return res.text();
|
|
29
|
-
throw new Error(`${res.status} ${res.statusText}: ${script.src}`);
|
|
30
|
-
})
|
|
31
|
-
.then(tex => {
|
|
32
|
-
Object.assign(macros, parseMacros(tex));
|
|
33
|
-
}))).then(() => resolve(macros));
|
|
34
|
-
});
|
|
35
|
-
/**
|
|
36
|
-
* Ready Promise
|
|
37
|
-
*/
|
|
38
|
-
const KaTeXReady = Promise.all([KaTeXLoad, KaTeXMacros]);
|
|
39
|
-
/**
|
|
40
|
-
Parse \newcommand macros in a file.
|
|
41
|
-
Also supports \ktxnewcommand (for use in conjunction with MathJax).
|
|
42
|
-
*/
|
|
43
|
-
function parseMacros(file) {
|
|
44
|
-
const macros = {};
|
|
45
|
-
const rgx = /\\(?:ktx)?newcommand\{(.+?)\}(?:\[\d+\])?\{/g;
|
|
46
|
-
let match;
|
|
47
|
-
while (match = rgx.exec(file)) {
|
|
48
|
-
let body = "";
|
|
49
|
-
const macro = match[1];
|
|
50
|
-
let braceCount = 1;
|
|
51
|
-
for (let i = match.index + match[0].length; (braceCount > 0) && (i < file.length); ++i) {
|
|
52
|
-
const char = file[i];
|
|
53
|
-
if (char === "{") {
|
|
54
|
-
braceCount++;
|
|
55
|
-
}
|
|
56
|
-
else if (char === "}") {
|
|
57
|
-
braceCount--;
|
|
58
|
-
if (braceCount === 0)
|
|
59
|
-
break;
|
|
60
|
-
}
|
|
61
|
-
else if (char === "\\") {
|
|
62
|
-
body += file.slice(i, i + 2);
|
|
63
|
-
++i;
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
body += char;
|
|
67
|
-
}
|
|
68
|
-
macros[macro] = body;
|
|
69
|
-
}
|
|
70
|
-
return macros;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/** Component for KaTeX code */
|
|
74
|
-
const KTX = react.forwardRef(function KTX(props, ref) {
|
|
75
|
-
const spanRef = react.useRef();
|
|
76
|
-
const { children, display = false, ...attrs } = props;
|
|
77
|
-
const [ready, resolve] = react$1.usePromise();
|
|
78
|
-
// handle
|
|
79
|
-
react.useImperativeHandle(ref, () => ({
|
|
80
|
-
domElement: spanRef.current,
|
|
81
|
-
ready
|
|
82
|
-
}));
|
|
83
|
-
react.useEffect(() => {
|
|
84
|
-
KaTeXReady.then(([katex, macros]) => {
|
|
85
|
-
katex.render(children.toString(), spanRef.current, {
|
|
86
|
-
displayMode: !!display,
|
|
87
|
-
macros,
|
|
88
|
-
strict: "ignore",
|
|
89
|
-
throwOnError: false,
|
|
90
|
-
trust: true
|
|
91
|
-
});
|
|
92
|
-
/* move katex into placeholder element */
|
|
93
|
-
const child = spanRef.current.firstElementChild;
|
|
94
|
-
// copy classes
|
|
95
|
-
for (let i = 0, len = child.classList.length; i < len; ++i) {
|
|
96
|
-
spanRef.current.classList.add(child.classList.item(i));
|
|
97
|
-
}
|
|
98
|
-
// move children
|
|
99
|
-
while (child.childNodes.length > 0) {
|
|
100
|
-
spanRef.current.appendChild(child.firstChild);
|
|
101
|
-
}
|
|
102
|
-
// delete child
|
|
103
|
-
child.remove();
|
|
104
|
-
// resolve promise
|
|
105
|
-
resolve();
|
|
106
|
-
});
|
|
107
|
-
}, [children]);
|
|
108
|
-
// Google Chrome fails without this
|
|
109
|
-
if (display) {
|
|
110
|
-
if (!attrs.style)
|
|
111
|
-
attrs.style = {};
|
|
112
|
-
attrs.style.display = "block";
|
|
113
|
-
}
|
|
114
|
-
return (jsxRuntime.jsx("span", { ...attrs, ref: spanRef }));
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
exports.KTX = KTX;
|
package/dist/plain.mjs
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { jsx } from 'react/jsx-runtime';
|
|
2
|
-
import { forwardRef, useRef, useImperativeHandle, useEffect } from 'react';
|
|
3
|
-
import { usePromise } from '@liqvid/utils/react';
|
|
4
|
-
|
|
5
|
-
// option of loading KaTeX asynchronously
|
|
6
|
-
const KaTeXLoad = new Promise((resolve) => {
|
|
7
|
-
const script = document.querySelector("script[src*=\"katex.js\"], script[src*=\"katex.min.js\"]");
|
|
8
|
-
if (!script)
|
|
9
|
-
return;
|
|
10
|
-
if (window.hasOwnProperty("katex")) {
|
|
11
|
-
resolve(katex);
|
|
12
|
-
}
|
|
13
|
-
else {
|
|
14
|
-
script.addEventListener("load", () => resolve(katex));
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
// load macros from <head>
|
|
18
|
-
const KaTeXMacros = new Promise((resolve) => {
|
|
19
|
-
const macros = {};
|
|
20
|
-
const scripts = Array.from(document.querySelectorAll("head > script[type='math/tex']"));
|
|
21
|
-
return Promise.all(scripts.map(script => fetch(script.src)
|
|
22
|
-
.then(res => {
|
|
23
|
-
if (res.ok)
|
|
24
|
-
return res.text();
|
|
25
|
-
throw new Error(`${res.status} ${res.statusText}: ${script.src}`);
|
|
26
|
-
})
|
|
27
|
-
.then(tex => {
|
|
28
|
-
Object.assign(macros, parseMacros(tex));
|
|
29
|
-
}))).then(() => resolve(macros));
|
|
30
|
-
});
|
|
31
|
-
/**
|
|
32
|
-
* Ready Promise
|
|
33
|
-
*/
|
|
34
|
-
const KaTeXReady = Promise.all([KaTeXLoad, KaTeXMacros]);
|
|
35
|
-
/**
|
|
36
|
-
Parse \newcommand macros in a file.
|
|
37
|
-
Also supports \ktxnewcommand (for use in conjunction with MathJax).
|
|
38
|
-
*/
|
|
39
|
-
function parseMacros(file) {
|
|
40
|
-
const macros = {};
|
|
41
|
-
const rgx = /\\(?:ktx)?newcommand\{(.+?)\}(?:\[\d+\])?\{/g;
|
|
42
|
-
let match;
|
|
43
|
-
while (match = rgx.exec(file)) {
|
|
44
|
-
let body = "";
|
|
45
|
-
const macro = match[1];
|
|
46
|
-
let braceCount = 1;
|
|
47
|
-
for (let i = match.index + match[0].length; (braceCount > 0) && (i < file.length); ++i) {
|
|
48
|
-
const char = file[i];
|
|
49
|
-
if (char === "{") {
|
|
50
|
-
braceCount++;
|
|
51
|
-
}
|
|
52
|
-
else if (char === "}") {
|
|
53
|
-
braceCount--;
|
|
54
|
-
if (braceCount === 0)
|
|
55
|
-
break;
|
|
56
|
-
}
|
|
57
|
-
else if (char === "\\") {
|
|
58
|
-
body += file.slice(i, i + 2);
|
|
59
|
-
++i;
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
body += char;
|
|
63
|
-
}
|
|
64
|
-
macros[macro] = body;
|
|
65
|
-
}
|
|
66
|
-
return macros;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/** Component for KaTeX code */
|
|
70
|
-
const KTX = forwardRef(function KTX(props, ref) {
|
|
71
|
-
const spanRef = useRef();
|
|
72
|
-
const { children, display = false, ...attrs } = props;
|
|
73
|
-
const [ready, resolve] = usePromise();
|
|
74
|
-
// handle
|
|
75
|
-
useImperativeHandle(ref, () => ({
|
|
76
|
-
domElement: spanRef.current,
|
|
77
|
-
ready
|
|
78
|
-
}));
|
|
79
|
-
useEffect(() => {
|
|
80
|
-
KaTeXReady.then(([katex, macros]) => {
|
|
81
|
-
katex.render(children.toString(), spanRef.current, {
|
|
82
|
-
displayMode: !!display,
|
|
83
|
-
macros,
|
|
84
|
-
strict: "ignore",
|
|
85
|
-
throwOnError: false,
|
|
86
|
-
trust: true
|
|
87
|
-
});
|
|
88
|
-
/* move katex into placeholder element */
|
|
89
|
-
const child = spanRef.current.firstElementChild;
|
|
90
|
-
// copy classes
|
|
91
|
-
for (let i = 0, len = child.classList.length; i < len; ++i) {
|
|
92
|
-
spanRef.current.classList.add(child.classList.item(i));
|
|
93
|
-
}
|
|
94
|
-
// move children
|
|
95
|
-
while (child.childNodes.length > 0) {
|
|
96
|
-
spanRef.current.appendChild(child.firstChild);
|
|
97
|
-
}
|
|
98
|
-
// delete child
|
|
99
|
-
child.remove();
|
|
100
|
-
// resolve promise
|
|
101
|
-
resolve();
|
|
102
|
-
});
|
|
103
|
-
}, [children]);
|
|
104
|
-
// Google Chrome fails without this
|
|
105
|
-
if (display) {
|
|
106
|
-
if (!attrs.style)
|
|
107
|
-
attrs.style = {};
|
|
108
|
-
attrs.style.display = "block";
|
|
109
|
-
}
|
|
110
|
-
return (jsx("span", { ...attrs, ref: spanRef }));
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
export { KTX };
|