@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 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,3 @@
1
+ export { KTX } from "./fancy.mjs";
2
+ export { KaTeXReady } from "./loading.mjs";
3
+ export { RenderGroup } from "./RenderGroup.mjs";
@@ -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
+ });
@@ -5,6 +5,7 @@ interface Handle {
5
5
  ready: Promise<void>;
6
6
  }
7
7
  interface Props {
8
+ children?: React.ReactNode;
8
9
  /**
9
10
  * Whether to reparse descendants for `during()` and `from()`
10
11
  * @default false
@@ -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, "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>>;
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",
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.js"
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.js"
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": "^0.11.1",
45
- "@types/react": ">=17.0.0",
46
- "liqvid": "^2.1.1",
47
- "react": ">=17.0.0"
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
- "@types/react": "^17.0.40",
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.3.0"
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 };