@bdocs/plugin-math 4.0.2 → 4.0.4

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.
@@ -1,20 +1,38 @@
1
1
 
2
- import * as _$react_jsx_runtime0 from "react/jsx-runtime";
2
+ import * as _$react from "react";
3
3
 
4
4
  //#region src/client/Math.d.ts
5
5
  interface MathProps {
6
6
  children: string;
7
+ /** Pre-rendered KaTeX HTML, baked at build time by the math plugin. */
8
+ html?: string;
7
9
  }
10
+ /**
11
+ * Inline math. When the build-time transform has baked the KaTeX output into
12
+ * the `html` prop, this renders synchronously and KaTeX is never bundled into
13
+ * the critical path. For direct MDX usage without the bake (rare), KaTeX is
14
+ * imported on demand and the raw TeX stays visible until it arrives — the
15
+ * server and the first client render agree, so hydration stays consistent.
16
+ */
8
17
  declare function MathComponent({
9
- children
10
- }: MathProps): _$react_jsx_runtime0.JSX.Element;
18
+ children,
19
+ html
20
+ }: MathProps): _$react.JSX.Element;
11
21
  //#endregion
12
22
  //#region src/client/BlockMath.d.ts
13
23
  interface BlockMathProps {
14
24
  children: string;
25
+ /** Pre-rendered KaTeX HTML, baked at build time by the math plugin. */
26
+ html?: string;
15
27
  }
28
+ /**
29
+ * Block math. Same contract as `MathComponent`: the build-time transform bakes
30
+ * KaTeX into `html`, so the critical client path never bundles KaTeX; direct
31
+ * MDX usage falls back to an on-demand import.
32
+ */
16
33
  declare function BlockMath({
17
- children
18
- }: BlockMathProps): _$react_jsx_runtime0.JSX.Element;
34
+ children,
35
+ html
36
+ }: BlockMathProps): _$react.JSX.Element;
19
37
  //#endregion
20
38
  export { BlockMath, MathComponent as Math, MathComponent };
@@ -3,31 +3,74 @@
3
3
  * Copyright (c) 2026 Jesus Alcala
4
4
  * Licensed under the MIT License.
5
5
  */
6
- import { useMemo } from "react";
7
- import katex from "katex";
6
+ import { useEffect, useState } from "react";
8
7
  import { jsx } from "react/jsx-runtime";
9
8
  //#region src/client/Math.tsx
10
- function MathComponent({ children }) {
11
- return /* @__PURE__ */ jsx("span", { dangerouslySetInnerHTML: { __html: useMemo(() => {
12
- try {
13
- return katex.renderToString(children, { displayMode: false });
14
- } catch {
15
- return children;
16
- }
17
- }, [children]) } });
9
+ /**
10
+ * Inline math. When the build-time transform has baked the KaTeX output into
11
+ * the `html` prop, this renders synchronously and KaTeX is never bundled into
12
+ * the critical path. For direct MDX usage without the bake (rare), KaTeX is
13
+ * imported on demand and the raw TeX stays visible until it arrives — the
14
+ * server and the first client render agree, so hydration stays consistent.
15
+ */
16
+ function MathComponent({ children, html }) {
17
+ const [lazyHtml, setLazyHtml] = useState(null);
18
+ useEffect(() => {
19
+ if (typeof html === "string" || lazyHtml !== null) return;
20
+ let active = true;
21
+ import("katex").then((k) => {
22
+ if (!active) return;
23
+ try {
24
+ setLazyHtml(k.default.renderToString(children, { displayMode: false }));
25
+ } catch {
26
+ setLazyHtml(children);
27
+ }
28
+ }).catch(() => {
29
+ if (active) setLazyHtml(children);
30
+ });
31
+ return () => {
32
+ active = false;
33
+ };
34
+ }, [
35
+ html,
36
+ children,
37
+ lazyHtml
38
+ ]);
39
+ return /* @__PURE__ */ jsx("span", { dangerouslySetInnerHTML: { __html: typeof html === "string" ? html : lazyHtml ?? children } });
18
40
  }
19
41
  //#endregion
20
42
  //#region src/client/BlockMath.tsx
21
- function BlockMath({ children }) {
22
- return /* @__PURE__ */ jsx("div", {
23
- className: "math-block my-6 flex justify-center overflow-x-auto",
24
- dangerouslySetInnerHTML: { __html: useMemo(() => {
43
+ /**
44
+ * Block math. Same contract as `MathComponent`: the build-time transform bakes
45
+ * KaTeX into `html`, so the critical client path never bundles KaTeX; direct
46
+ * MDX usage falls back to an on-demand import.
47
+ */
48
+ function BlockMath({ children, html }) {
49
+ const [lazyHtml, setLazyHtml] = useState(null);
50
+ useEffect(() => {
51
+ if (typeof html === "string" || lazyHtml !== null) return;
52
+ let active = true;
53
+ import("katex").then((k) => {
54
+ if (!active) return;
25
55
  try {
26
- return katex.renderToString(children, { displayMode: true });
56
+ setLazyHtml(k.default.renderToString(children, { displayMode: true }));
27
57
  } catch {
28
- return children;
58
+ setLazyHtml(children);
29
59
  }
30
- }, [children]) }
60
+ }).catch(() => {
61
+ if (active) setLazyHtml(children);
62
+ });
63
+ return () => {
64
+ active = false;
65
+ };
66
+ }, [
67
+ html,
68
+ children,
69
+ lazyHtml
70
+ ]);
71
+ return /* @__PURE__ */ jsx("div", {
72
+ className: "math-block my-6 flex justify-center overflow-x-auto",
73
+ dangerouslySetInnerHTML: { __html: typeof html === "string" ? html : lazyHtml ?? children }
31
74
  });
32
75
  }
33
76
  //#endregion
@@ -3,10 +3,23 @@
3
3
  * Copyright (c) 2026 Jesus Alcala
4
4
  * Licensed under the MIT License.
5
5
  */
6
+ import katex from "katex";
6
7
  //#region src/node/source-transform.ts
7
8
  function escapeJsString(content) {
8
9
  return content.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n");
9
10
  }
11
+ function renderMathHtml(tex, displayMode) {
12
+ try {
13
+ return katex.renderToString(tex, { displayMode });
14
+ } catch {
15
+ return tex;
16
+ }
17
+ }
18
+ /**
19
+ * Bake KaTeX output at build time. The emitted `<BlockMath>`/`<MathComponent>`
20
+ * tags carry the pre-rendered HTML in a `html` prop, so the client components
21
+ * never need to bundle KaTeX (~250 KB minified) just to display equations.
22
+ */
10
23
  function preprocessMath(source) {
11
24
  const placeholders = [];
12
25
  let result = source.replace(/(`{3,})[\s\S]*?\1/g, (match) => {
@@ -22,10 +35,11 @@ function preprocessMath(source) {
22
35
  return `\0MATH_PH_${placeholders.length - 1}\0`;
23
36
  });
24
37
  result = result.replace(/\$\$([\s\S]*?)\$\$/g, (_, content) => {
25
- return `<BlockMath>{"${escapeJsString(content.trim())}"}</BlockMath>`;
38
+ const trimmed = content.trim();
39
+ return `<BlockMath html={"${escapeJsString(renderMathHtml(trimmed, true))}"}>{"${escapeJsString(trimmed)}"}</BlockMath>`;
26
40
  });
27
41
  result = result.replace(/(?<!\$)\$(?!\$)(.+?)\$(?!\$)/g, (_, content) => {
28
- return `<MathComponent>{"${escapeJsString(content)}"}</MathComponent>`;
42
+ return `<MathComponent html={"${escapeJsString(renderMathHtml(content, false))}"}>{"${escapeJsString(content)}"}</MathComponent>`;
29
43
  });
30
44
  while (result.includes("\0MATH_PH_")) result = result.replace(/\0MATH_PH_(\d+)\0/g, (_, idx) => {
31
45
  return placeholders[parseInt(idx)];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bdocs/plugin-math",
3
- "version": "4.0.2",
3
+ "version": "4.0.4",
4
4
  "description": "KaTeX math plugin for Boltdocs",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -26,10 +26,10 @@
26
26
  "katex": "^0.16.11"
27
27
  },
28
28
  "peerDependencies": {
29
- "@bdocs/dui": "^0.2.0",
29
+ "@bdocs/dui": "^0.6.0",
30
30
  "react": "^19.0.0",
31
31
  "react-dom": "^19.0.0",
32
- "boltdocs": "3.2.2"
32
+ "boltdocs": "^3.4.0"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/katex": "^0.16.7",
@@ -40,7 +40,8 @@
40
40
  "react-dom": "^19.0.0",
41
41
  "tsdown": "^0.21.7",
42
42
  "typescript": "^5.9.3",
43
- "boltdocs": "3.2.2"
43
+ "boltdocs": "^3.4.0",
44
+ "tsdown-config": "1.0.0"
44
45
  },
45
46
  "scripts": {
46
47
  "build": "tsdown --config-loader unrun && node --input-type=module -e \"import fs from 'node:fs'; fs.mkdirSync('dist/client', { recursive: true }); fs.cpSync('src/client/index.css', 'dist/client/index.css');\"",