@liqvid/katex 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +168 -4
- package/dist/index.mjs +206 -0
- package/dist/plain.js +117 -0
- package/dist/plain.mjs +113 -0
- package/dist/types/RenderGroup.d.ts +18 -0
- package/dist/types/fancy.d.ts +17 -0
- package/dist/{index.d.ts → types/index.d.ts} +2 -2
- package/dist/{loading.d.ts → types/loading.d.ts} +3 -0
- package/dist/types/plain.d.ts +20 -0
- package/package.json +21 -9
- package/dist/Blocking.d.ts +0 -2
- package/dist/Blocking.js +0 -32
- package/dist/NonBlocking.d.ts +0 -9
- package/dist/NonBlocking.js +0 -60
- package/dist/RenderGroup.d.ts +0 -10
- package/dist/RenderGroup.js +0 -76
- package/dist/loading.js +0 -54
package/dist/index.js
CHANGED
|
@@ -1,4 +1,168 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
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 };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
/** RenderGroup element API */
|
|
3
|
+
interface Handle {
|
|
4
|
+
/** Promise that resolves once all KTX descendants have finished typesetting */
|
|
5
|
+
ready: Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
interface Props {
|
|
8
|
+
/**
|
|
9
|
+
* Whether to reparse descendants for `during()` and `from()`
|
|
10
|
+
* @default false
|
|
11
|
+
*/
|
|
12
|
+
reparse?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Wait for several things to be rendered
|
|
16
|
+
*/
|
|
17
|
+
export declare const RenderGroup: React.ForwardRefExoticComponent<Props & React.RefAttributes<Handle>>;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { Handle, KTX as KTXPlain } from "./plain";
|
|
3
|
+
interface Props extends React.ComponentProps<typeof KTXPlain> {
|
|
4
|
+
/**
|
|
5
|
+
* Player events to obstruct
|
|
6
|
+
* @default "canplay canplaythrough"
|
|
7
|
+
*/
|
|
8
|
+
obstruct?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Whether to reparse descendants for `during()` and `from()`
|
|
11
|
+
* @default false
|
|
12
|
+
*/
|
|
13
|
+
reparse?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/** Component for KaTeX code */
|
|
16
|
+
export declare const KTX: import("react").ForwardRefExoticComponent<Pick<Props, "hidden" | "color" | "style" | "display" | "translate" | "slot" | "title" | "children" | "key" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "className" | "contentEditable" | "contextMenu" | "dir" | "draggable" | "id" | "lang" | "placeholder" | "spellCheck" | "tabIndex" | "radioGroup" | "role" | "about" | "datatype" | "inlist" | "prefix" | "property" | "resource" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "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
|
+
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { KTX } from "./fancy";
|
|
2
2
|
export { KaTeXReady } from "./loading";
|
|
3
|
-
export { Handle
|
|
3
|
+
export { Handle } from "./plain";
|
|
4
4
|
export { RenderGroup } from "./RenderGroup";
|
|
5
5
|
declare global {
|
|
6
6
|
const katex: typeof katex;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
/**
|
|
3
|
+
* KTX element API
|
|
4
|
+
*/
|
|
5
|
+
export interface Handle {
|
|
6
|
+
/** The underlying <span> element */
|
|
7
|
+
domElement: HTMLSpanElement;
|
|
8
|
+
/** Promise that resolves once typesetting is finished */
|
|
9
|
+
ready: Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
interface Props extends React.HTMLAttributes<HTMLSpanElement> {
|
|
12
|
+
/**
|
|
13
|
+
* Whether to render in display style
|
|
14
|
+
* @default false
|
|
15
|
+
*/
|
|
16
|
+
display?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/** Component for KaTeX code */
|
|
19
|
+
export declare const KTX: import("react").ForwardRefExoticComponent<Props & import("react").RefAttributes<Handle>>;
|
|
20
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liqvid/katex",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
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.js"
|
|
12
|
+
},
|
|
13
|
+
"./plain": {
|
|
14
|
+
"import": "./dist/plain.mjs",
|
|
15
|
+
"require": "./dist/plain.js"
|
|
16
|
+
}
|
|
10
17
|
},
|
|
11
18
|
"typesVersions": {
|
|
12
19
|
"*": {
|
|
13
20
|
"*": [
|
|
14
|
-
"./dist/*"
|
|
21
|
+
"./dist/types/*"
|
|
15
22
|
]
|
|
16
23
|
}
|
|
17
24
|
},
|
|
@@ -20,6 +27,10 @@
|
|
|
20
27
|
"liqvid",
|
|
21
28
|
"katex"
|
|
22
29
|
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsc --build --force",
|
|
32
|
+
"lint": "eslint --ext ts,tsx --fix src"
|
|
33
|
+
},
|
|
23
34
|
"repository": {
|
|
24
35
|
"type": "git",
|
|
25
36
|
"url": "git+https://github.com/liqvidjs/liqvid.git"
|
|
@@ -30,9 +41,10 @@
|
|
|
30
41
|
"homepage": "https://github.com/liqvidjs/liqvid/tree/main/packages/katex",
|
|
31
42
|
"license": "MIT",
|
|
32
43
|
"peerDependencies": {
|
|
33
|
-
"@types/
|
|
34
|
-
"
|
|
35
|
-
"
|
|
44
|
+
"@types/katex": "^0.11.1",
|
|
45
|
+
"@types/react": ">=17.0.0",
|
|
46
|
+
"liqvid": ">=2.1.0-beta.4",
|
|
47
|
+
"react": ">=17.0.0"
|
|
36
48
|
},
|
|
37
49
|
"peerDependenciesMeta": {
|
|
38
50
|
"liqvid": {
|
|
@@ -46,12 +58,12 @@
|
|
|
46
58
|
"@yuri/eslint-config": "^1.0.1",
|
|
47
59
|
"eslint": "^8.10.0",
|
|
48
60
|
"eslint-plugin-react": "^7.29.3",
|
|
49
|
-
"liqvid": "2.1.0-beta.
|
|
61
|
+
"liqvid": ">=2.1.0-beta.4",
|
|
50
62
|
"react": "^17.0.2",
|
|
63
|
+
"rollup": "^2.70.0",
|
|
51
64
|
"typescript": "^4.6.2"
|
|
52
65
|
},
|
|
53
66
|
"dependencies": {
|
|
54
|
-
"@
|
|
55
|
-
"@types/katex": "^0.11.1"
|
|
67
|
+
"@liqvid/utils": "^1.3.0"
|
|
56
68
|
}
|
|
57
69
|
}
|
package/dist/Blocking.d.ts
DELETED
package/dist/Blocking.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { usePlayer } from "liqvid";
|
|
3
|
-
import { forwardRef, useEffect, useMemo, useRef } from "react";
|
|
4
|
-
import { KTXNonBlocking } from "./NonBlocking";
|
|
5
|
-
export const KTXBlocking = forwardRef(function KTX(props, ref) {
|
|
6
|
-
const player = usePlayer();
|
|
7
|
-
const innerRef = useRef();
|
|
8
|
-
if (typeof ref === "function") {
|
|
9
|
-
ref(innerRef.current);
|
|
10
|
-
}
|
|
11
|
-
else if (ref) {
|
|
12
|
-
ref.current = innerRef.current;
|
|
13
|
-
}
|
|
14
|
-
const resolve = useRef(null);
|
|
15
|
-
useMemo(() => {
|
|
16
|
-
const promise = new Promise((res) => {
|
|
17
|
-
resolve.current = res;
|
|
18
|
-
});
|
|
19
|
-
player.obstruct("canplay", promise);
|
|
20
|
-
player.obstruct("canplaythrough", promise);
|
|
21
|
-
}, []);
|
|
22
|
-
useEffect(() => {
|
|
23
|
-
if (typeof ref === "function") {
|
|
24
|
-
ref(innerRef.current);
|
|
25
|
-
}
|
|
26
|
-
else if (ref) {
|
|
27
|
-
ref.current = innerRef.current;
|
|
28
|
-
}
|
|
29
|
-
innerRef.current.ready.then(() => resolve.current());
|
|
30
|
-
}, []);
|
|
31
|
-
return (_jsx(KTXNonBlocking, Object.assign({ ref: innerRef }, props)));
|
|
32
|
-
});
|
package/dist/NonBlocking.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/// <reference types="react" />
|
|
2
|
-
export interface Handle {
|
|
3
|
-
domElement: HTMLSpanElement;
|
|
4
|
-
ready: Promise<void>;
|
|
5
|
-
}
|
|
6
|
-
export declare const KTXNonBlocking: import("react").ForwardRefExoticComponent<{
|
|
7
|
-
display?: boolean;
|
|
8
|
-
reparse?: boolean;
|
|
9
|
-
} & import("react").HTMLAttributes<HTMLSpanElement> & import("react").RefAttributes<Handle>>;
|
package/dist/NonBlocking.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
-
var t = {};
|
|
3
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
-
t[p] = s[p];
|
|
5
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
-
t[p[i]] = s[p[i]];
|
|
9
|
-
}
|
|
10
|
-
return t;
|
|
11
|
-
};
|
|
12
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
|
-
import { usePlayer } from "liqvid";
|
|
14
|
-
import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef } from "react";
|
|
15
|
-
import { KaTeXReady } from "./loading";
|
|
16
|
-
const implementation = function KTX(props, ref) {
|
|
17
|
-
const spanRef = useRef();
|
|
18
|
-
const { children, display, reparse } = props, attrs = __rest(props, ["children", "display", "reparse"]);
|
|
19
|
-
const resolveRef = useRef();
|
|
20
|
-
const player = usePlayer();
|
|
21
|
-
const ready = useMemo(() => {
|
|
22
|
-
return new Promise((resolve) => {
|
|
23
|
-
resolveRef.current = resolve;
|
|
24
|
-
});
|
|
25
|
-
}, []);
|
|
26
|
-
useImperativeHandle(ref, () => ({
|
|
27
|
-
domElement: spanRef.current,
|
|
28
|
-
ready
|
|
29
|
-
}));
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
KaTeXReady.then(([katex, macros]) => {
|
|
32
|
-
katex.render(children.toString(), spanRef.current, {
|
|
33
|
-
displayMode: !!display,
|
|
34
|
-
macros,
|
|
35
|
-
strict: "ignore",
|
|
36
|
-
throwOnError: false,
|
|
37
|
-
trust: true
|
|
38
|
-
});
|
|
39
|
-
const child = spanRef.current.firstElementChild;
|
|
40
|
-
for (let i = 0, len = child.classList.length; i < len; ++i) {
|
|
41
|
-
spanRef.current.classList.add(child.classList.item(i));
|
|
42
|
-
}
|
|
43
|
-
while (child.childNodes.length > 0) {
|
|
44
|
-
spanRef.current.appendChild(child.firstChild);
|
|
45
|
-
}
|
|
46
|
-
child.remove();
|
|
47
|
-
if (reparse) {
|
|
48
|
-
player.reparseTree(spanRef.current);
|
|
49
|
-
}
|
|
50
|
-
resolveRef.current();
|
|
51
|
-
});
|
|
52
|
-
}, [children]);
|
|
53
|
-
if (display) {
|
|
54
|
-
if (!attrs.style)
|
|
55
|
-
attrs.style = {};
|
|
56
|
-
attrs.style.display = "block";
|
|
57
|
-
}
|
|
58
|
-
return (_jsx("span", Object.assign({}, attrs, { ref: spanRef })));
|
|
59
|
-
};
|
|
60
|
-
export const KTXNonBlocking = forwardRef(implementation);
|
package/dist/RenderGroup.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/// <reference types="react" />
|
|
2
|
-
interface Handle {
|
|
3
|
-
ready: Promise<void>;
|
|
4
|
-
}
|
|
5
|
-
interface Props {
|
|
6
|
-
reparse?: boolean;
|
|
7
|
-
}
|
|
8
|
-
export declare const RenderGroup: import("react").ForwardRefExoticComponent<Props & import("react").RefAttributes<Handle>>;
|
|
9
|
-
export declare function recursiveMap(children: React.ReactNode, fn: (child: React.ReactNode) => React.ReactNode): React.ReactNode;
|
|
10
|
-
export {};
|
package/dist/RenderGroup.js
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { Children, cloneElement, forwardRef, isValidElement, useEffect, useMemo, useRef, useImperativeHandle } from "react";
|
|
2
|
-
import { usePlayer } from "liqvid";
|
|
3
|
-
import { KTXNonBlocking } from "./NonBlocking";
|
|
4
|
-
import { KTXBlocking } from "./Blocking";
|
|
5
|
-
export const RenderGroup = forwardRef(function RenderGroup(props, ref) {
|
|
6
|
-
const [ready, resolve] = usePromise();
|
|
7
|
-
useImperativeHandle(ref, () => ({ ready }));
|
|
8
|
-
const elements = useRef([]);
|
|
9
|
-
const promises = useRef([]);
|
|
10
|
-
const player = usePlayer();
|
|
11
|
-
useEffect(() => {
|
|
12
|
-
Promise.all(promises.current).then(() => {
|
|
13
|
-
if (props.reparse) {
|
|
14
|
-
player.reparseTree(leastCommonAncestor(elements.current));
|
|
15
|
-
}
|
|
16
|
-
resolve();
|
|
17
|
-
});
|
|
18
|
-
}, []);
|
|
19
|
-
return recursiveMap(props.children, node => {
|
|
20
|
-
if (shouldInspect(node)) {
|
|
21
|
-
const originalRef = node.ref;
|
|
22
|
-
return cloneElement(node, {
|
|
23
|
-
ref: (ref) => {
|
|
24
|
-
if (!ref)
|
|
25
|
-
return;
|
|
26
|
-
elements.current.push(ref.domElement);
|
|
27
|
-
promises.current.push(ref.ready);
|
|
28
|
-
if (typeof originalRef === "function") {
|
|
29
|
-
originalRef(ref);
|
|
30
|
-
}
|
|
31
|
-
else if (originalRef && typeof originalRef === "object") {
|
|
32
|
-
originalRef.current = ref;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
return node;
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
function shouldInspect(node) {
|
|
41
|
-
return isValidElement(node) && typeof node.type === "object" && (node.type === KTXBlocking || node.type === KTXNonBlocking);
|
|
42
|
-
}
|
|
43
|
-
export function recursiveMap(children, fn) {
|
|
44
|
-
return Children.map(children, (child) => {
|
|
45
|
-
if (!isValidElement(child)) {
|
|
46
|
-
return child;
|
|
47
|
-
}
|
|
48
|
-
if ("children" in child.props) {
|
|
49
|
-
child = cloneElement(child, {
|
|
50
|
-
children: recursiveMap(child.props.children, fn)
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
return fn(child);
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
function usePromise(deps = []) {
|
|
57
|
-
const resolve = useRef();
|
|
58
|
-
const reject = useRef();
|
|
59
|
-
const promise = useMemo(() => new Promise((res, rej) => {
|
|
60
|
-
resolve.current = res;
|
|
61
|
-
reject.current = rej;
|
|
62
|
-
}), deps);
|
|
63
|
-
return [promise, resolve.current, reject.current];
|
|
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
|
-
}
|
package/dist/loading.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
const KaTeXLoad = new Promise((resolve) => {
|
|
2
|
-
const script = document.querySelector(`script[src*="katex.js"], script[src*="katex.min.js"]`);
|
|
3
|
-
if (!script)
|
|
4
|
-
return;
|
|
5
|
-
if (window.hasOwnProperty("katex")) {
|
|
6
|
-
resolve(katex);
|
|
7
|
-
}
|
|
8
|
-
else {
|
|
9
|
-
script.addEventListener("load", () => resolve(katex));
|
|
10
|
-
}
|
|
11
|
-
});
|
|
12
|
-
const KaTeXMacros = new Promise((resolve) => {
|
|
13
|
-
const macros = {};
|
|
14
|
-
const scripts = Array.from(document.querySelectorAll("head > script[type='math/tex']"));
|
|
15
|
-
return Promise.all(scripts.map(script => fetch(script.src)
|
|
16
|
-
.then(res => {
|
|
17
|
-
if (res.ok)
|
|
18
|
-
return res.text();
|
|
19
|
-
throw new Error(`${res.status} ${res.statusText}: ${script.src}`);
|
|
20
|
-
})
|
|
21
|
-
.then(tex => {
|
|
22
|
-
Object.assign(macros, parseMacros(tex));
|
|
23
|
-
}))).then(() => resolve(macros));
|
|
24
|
-
});
|
|
25
|
-
export const KaTeXReady = Promise.all([KaTeXLoad, KaTeXMacros]);
|
|
26
|
-
function parseMacros(file) {
|
|
27
|
-
const macros = {};
|
|
28
|
-
const rgx = /\\(?:ktx)?newcommand\{(.+?)\}(?:\[\d+\])?\{/g;
|
|
29
|
-
let match;
|
|
30
|
-
while (match = rgx.exec(file)) {
|
|
31
|
-
let body = "";
|
|
32
|
-
const macro = match[1];
|
|
33
|
-
let braceCount = 1;
|
|
34
|
-
for (let i = match.index + match[0].length; (braceCount > 0) && (i < file.length); ++i) {
|
|
35
|
-
const char = file[i];
|
|
36
|
-
if (char === "{") {
|
|
37
|
-
braceCount++;
|
|
38
|
-
}
|
|
39
|
-
else if (char === "}") {
|
|
40
|
-
braceCount--;
|
|
41
|
-
if (braceCount === 0)
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
else if (char === "\\") {
|
|
45
|
-
body += file.slice(i, i + 2);
|
|
46
|
-
++i;
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
body += char;
|
|
50
|
-
}
|
|
51
|
-
macros[macro] = body;
|
|
52
|
-
}
|
|
53
|
-
return macros;
|
|
54
|
-
}
|