@uniweb/scholar 0.2.1 → 0.2.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/README.md +42 -32
- package/package.json +16 -7
- package/src/index.js +6 -2
- package/src/math/Equation.jsx +45 -75
- package/src/math/index.js +19 -4
- package/src/math/Math.jsx +0 -115
- package/src/math/katex-loader.js +0 -105
package/README.md
CHANGED
|
@@ -12,28 +12,21 @@ pnpm add @uniweb/scholar
|
|
|
12
12
|
|
|
13
13
|
## Features
|
|
14
14
|
|
|
15
|
-
- **
|
|
15
|
+
- **Numbered Equations** - Cross-referenceable display equations
|
|
16
16
|
- **Bibliography** - Format references in APA, MLA, Chicago, and IEEE styles
|
|
17
17
|
- **Citations** - Track inline citations with automatic bibliography generation
|
|
18
18
|
- **BibTeX** - Parse and export BibTeX format
|
|
19
19
|
- **UI Components** - Ready-to-use citation buttons, author lists, and DOI links
|
|
20
20
|
|
|
21
|
+
> **Plain LaTeX math** (inline `$x$`, display `$$x$$`, fenced ```` ```math ````) is handled by Uniweb's content pipeline — the markdown parser compiles LaTeX to MathML Core at build time and the browser renders it natively. No runtime math library needed; no `<Math>` component required.
|
|
22
|
+
|
|
21
23
|
## Quick Start
|
|
22
24
|
|
|
23
|
-
###
|
|
25
|
+
### Numbered Equations
|
|
24
26
|
|
|
25
27
|
```jsx
|
|
26
|
-
import {
|
|
27
|
-
|
|
28
|
-
// Inline math
|
|
29
|
-
<p>The equation <Math>E = mc^2</Math> shows mass-energy equivalence.</p>
|
|
28
|
+
import { Equation, EquationRef, EquationProvider } from '@uniweb/scholar/math'
|
|
30
29
|
|
|
31
|
-
// Display (block) math
|
|
32
|
-
<Math display>
|
|
33
|
-
\int_0^\infty e^{-x^2} dx = \frac{\sqrt{\pi}}{2}
|
|
34
|
-
</Math>
|
|
35
|
-
|
|
36
|
-
// Numbered equations with cross-references
|
|
37
30
|
<EquationProvider>
|
|
38
31
|
<Equation id="einstein">E = mc^2</Equation>
|
|
39
32
|
<p>As shown in <EquationRef id="einstein" />, energy equals mass times the speed of light squared.</p>
|
|
@@ -97,29 +90,49 @@ const bibtex = `
|
|
|
97
90
|
|
|
98
91
|
### Math Module
|
|
99
92
|
|
|
100
|
-
|
|
93
|
+
Scholar does not ship a math renderer. Plain LaTeX in markdown is compiled to MathML by Uniweb's content pipeline and rendered natively by the browser — see the framework docs for the `$x$`, `$$x$$`, and ```math fence syntax.
|
|
101
94
|
|
|
102
|
-
|
|
95
|
+
`<Equation>` and `<EquationRef>` cover the distinct feature of **numbered cross-referenceable equations**. They consume pre-compiled MathML from `content.math` and are fully SSR-safe.
|
|
103
96
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
97
|
+
Label an equation in markdown with a `:<id>` suffix on the fence:
|
|
98
|
+
|
|
99
|
+
````markdown
|
|
100
|
+
```math:einstein
|
|
101
|
+
E = mc^2
|
|
102
|
+
```
|
|
103
|
+
````
|
|
104
|
+
|
|
105
|
+
Then in a foundation's section component:
|
|
106
|
+
|
|
107
|
+
```jsx
|
|
108
|
+
import { Equation, EquationProvider, EquationRef } from '@uniweb/scholar/math'
|
|
109
|
+
|
|
110
|
+
function MyBlock({ content }) {
|
|
111
|
+
return (
|
|
112
|
+
<EquationProvider>
|
|
113
|
+
{content.math?.filter(m => m.id).map(m => (
|
|
114
|
+
<Equation key={m.id} id={m.id} mathml={m.mathml} />
|
|
115
|
+
))}
|
|
116
|
+
<p>As shown in <EquationRef id="einstein" />, energy equals mc².</p>
|
|
117
|
+
</EquationProvider>
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
```
|
|
109
121
|
|
|
110
122
|
#### `<Equation>`
|
|
111
123
|
|
|
112
|
-
|
|
124
|
+
Numbered display equation rendered from pre-compiled MathML.
|
|
113
125
|
|
|
114
126
|
| Prop | Type | Default | Description |
|
|
115
127
|
|------|------|---------|-------------|
|
|
116
|
-
| `
|
|
117
|
-
| `
|
|
118
|
-
| `label` | `string` | - |
|
|
128
|
+
| `id` | `string` | auto | Identifier for cross-referencing |
|
|
129
|
+
| `mathml` | `string` | - | Pre-compiled MathML from `content.math[i].mathml` |
|
|
130
|
+
| `label` | `string` | - | Override the auto-number with a custom label |
|
|
131
|
+
| `className` | `string` | - | CSS classes on the wrapper |
|
|
119
132
|
|
|
120
133
|
#### `<EquationRef>`
|
|
121
134
|
|
|
122
|
-
|
|
135
|
+
Link to a numbered equation within the same `<EquationProvider>`.
|
|
123
136
|
|
|
124
137
|
| Prop | Type | Default | Description |
|
|
125
138
|
|------|------|---------|-------------|
|
|
@@ -128,7 +141,7 @@ Creates a reference link to a numbered equation.
|
|
|
128
141
|
|
|
129
142
|
#### `<EquationProvider>`
|
|
130
143
|
|
|
131
|
-
Context provider for equation numbering. Wrap content containing equations.
|
|
144
|
+
Context provider for equation numbering. Wrap content containing equations and their references.
|
|
132
145
|
|
|
133
146
|
### Citations Module
|
|
134
147
|
|
|
@@ -280,17 +293,14 @@ import { DoiLink } from '@uniweb/scholar/components'
|
|
|
280
293
|
|
|
281
294
|
## Bundle Size
|
|
282
295
|
|
|
283
|
-
|
|
296
|
+
Scholar ships no math renderer — MathML is pre-compiled upstream and rendered natively by the browser. Approximate subpath sizes:
|
|
284
297
|
|
|
285
298
|
| Module | Size |
|
|
286
299
|
|--------|------|
|
|
287
300
|
| bibliography (formatters, parsers) | ~15KB |
|
|
288
|
-
| math (
|
|
289
|
-
|
|
|
290
|
-
|
|
|
291
|
-
| components | ~8KB |
|
|
292
|
-
|
|
293
|
-
KaTeX CSS is automatically injected when math rendering is first used.
|
|
301
|
+
| math (numbered equations, refs) | ~5KB |
|
|
302
|
+
| citations | ~10KB |
|
|
303
|
+
| components | ~8KB |
|
|
294
304
|
|
|
295
305
|
## License
|
|
296
306
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/scholar",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Academic content utilities for Uniweb -
|
|
3
|
+
"version": "0.2.2",
|
|
4
|
+
"description": "Academic content utilities for Uniweb - numbered equations with cross-references, bibliographic references, and inline citations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": "./src/index.js",
|
|
@@ -18,9 +18,8 @@
|
|
|
18
18
|
"uniweb",
|
|
19
19
|
"scholar",
|
|
20
20
|
"academic",
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"latex",
|
|
21
|
+
"equations",
|
|
22
|
+
"mathml",
|
|
24
23
|
"bibliography",
|
|
25
24
|
"citations",
|
|
26
25
|
"bibtex",
|
|
@@ -44,10 +43,20 @@
|
|
|
44
43
|
"@citestyle/bibtex": "^1.0.0",
|
|
45
44
|
"@citestyle/core": "^1.0.0",
|
|
46
45
|
"@citestyle/registry": "^1.0.0",
|
|
47
|
-
"@citestyle/styles": "^1.0.0"
|
|
48
|
-
"katex": "^0.16.0"
|
|
46
|
+
"@citestyle/styles": "^1.0.0"
|
|
49
47
|
},
|
|
50
48
|
"peerDependencies": {
|
|
51
49
|
"react": "^18.0.0 || ^19.0.0"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@testing-library/react": "^16.0.0",
|
|
53
|
+
"jsdom": "^25.0.0",
|
|
54
|
+
"react": "^19.0.0",
|
|
55
|
+
"react-dom": "^19.0.0",
|
|
56
|
+
"vitest": "^2.0.0"
|
|
57
|
+
},
|
|
58
|
+
"scripts": {
|
|
59
|
+
"test": "vitest run",
|
|
60
|
+
"test:watch": "vitest"
|
|
52
61
|
}
|
|
53
62
|
}
|
package/src/index.js
CHANGED
|
@@ -11,8 +11,12 @@
|
|
|
11
11
|
* @module @uniweb/scholar
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
// Math components
|
|
15
|
-
|
|
14
|
+
// Math components — scholar no longer ships a math renderer. Plain
|
|
15
|
+
// LaTeX in markdown (`$x$`, `$$x$$`, fenced ```math) is compiled to
|
|
16
|
+
// MathML by the content pipeline and rendered natively by the browser.
|
|
17
|
+
// <Equation> / <EquationRef> cover numbered cross-referenceable
|
|
18
|
+
// equations, consuming pre-compiled MathML via `content.math`.
|
|
19
|
+
export { Equation, EquationRef } from './math/index.js'
|
|
16
20
|
|
|
17
21
|
// Bibliography — formatting, parsing, normalization
|
|
18
22
|
export {
|
package/src/math/Equation.jsx
CHANGED
|
@@ -1,31 +1,49 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Equation Component
|
|
3
3
|
*
|
|
4
|
-
* Renders a numbered equation
|
|
5
|
-
*
|
|
4
|
+
* Renders a numbered display equation from pre-compiled MathML.
|
|
5
|
+
*
|
|
6
|
+
* The MathML is produced by Uniweb's content pipeline at build time
|
|
7
|
+
* (see @uniweb/content-reader/math). This component does no math
|
|
8
|
+
* rendering of its own — it wraps the MathML string with numbering and
|
|
9
|
+
* cross-reference machinery, which means it is fully SSR-safe and adds
|
|
10
|
+
* zero runtime math library cost to sites.
|
|
11
|
+
*
|
|
12
|
+
* Authors write labeled display equations in markdown:
|
|
13
|
+
*
|
|
14
|
+
* ```math:einstein
|
|
15
|
+
* E = mc^2
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* A foundation's section component then iterates `content.math` and
|
|
19
|
+
* renders each labeled entry through <Equation>:
|
|
20
|
+
*
|
|
21
|
+
* <EquationProvider>
|
|
22
|
+
* {content.math.map(m => (
|
|
23
|
+
* <Equation key={m.id} id={m.id} mathml={m.mathml} />
|
|
24
|
+
* ))}
|
|
25
|
+
* </EquationProvider>
|
|
6
26
|
*
|
|
7
27
|
* @module @uniweb/scholar/math/Equation
|
|
8
28
|
*/
|
|
9
29
|
|
|
10
|
-
import React, {
|
|
11
|
-
import { loadKatex } from './katex-loader.js'
|
|
30
|
+
import React, { useContext, useEffect, useId } from 'react'
|
|
12
31
|
|
|
13
32
|
/**
|
|
14
|
-
* Context for tracking equation numbers
|
|
15
|
-
* Exported for use by EquationRef
|
|
33
|
+
* Context for tracking equation numbers. Exported for use by EquationRef.
|
|
16
34
|
*/
|
|
17
35
|
export const EquationContext = React.createContext(null)
|
|
18
36
|
|
|
19
37
|
/**
|
|
20
|
-
* Provider for equation numbering
|
|
21
|
-
*
|
|
38
|
+
* Provider for equation numbering. Wrap section content with this to
|
|
39
|
+
* enable automatic sequential numbering across all <Equation> instances.
|
|
22
40
|
*
|
|
23
41
|
* @param {Object} props
|
|
24
42
|
* @param {React.ReactNode} props.children
|
|
25
43
|
* @param {number} [props.startNumber=1] - Starting equation number
|
|
26
44
|
*/
|
|
27
45
|
export function EquationProvider({ children, startNumber = 1 }) {
|
|
28
|
-
const [equations, setEquations] = useState(new Map())
|
|
46
|
+
const [equations, setEquations] = React.useState(new Map())
|
|
29
47
|
const counterRef = React.useRef(startNumber)
|
|
30
48
|
|
|
31
49
|
const register = React.useCallback((id) => {
|
|
@@ -39,7 +57,7 @@ export function EquationProvider({ children, startNumber = 1 }) {
|
|
|
39
57
|
|
|
40
58
|
const getNumber = React.useCallback(
|
|
41
59
|
(id) => equations.get(id),
|
|
42
|
-
[equations]
|
|
60
|
+
[equations],
|
|
43
61
|
)
|
|
44
62
|
|
|
45
63
|
return (
|
|
@@ -50,77 +68,35 @@ export function EquationProvider({ children, startNumber = 1 }) {
|
|
|
50
68
|
}
|
|
51
69
|
|
|
52
70
|
/**
|
|
53
|
-
* Hook
|
|
71
|
+
* Hook for foundation code that needs direct access to the numbering
|
|
72
|
+
* context (e.g., to render a list of all equations on a page).
|
|
54
73
|
*/
|
|
55
74
|
export function useEquations() {
|
|
56
75
|
return useContext(EquationContext)
|
|
57
76
|
}
|
|
58
77
|
|
|
59
78
|
/**
|
|
60
|
-
* Equation
|
|
79
|
+
* Equation — numbered display equation rendered from pre-compiled MathML.
|
|
61
80
|
*
|
|
62
81
|
* @param {Object} props
|
|
63
|
-
* @param {string} props.
|
|
64
|
-
*
|
|
65
|
-
* @param {string}
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* @
|
|
69
|
-
* <EquationProvider>
|
|
70
|
-
* <Equation id="einstein">E = mc^2</Equation>
|
|
71
|
-
* <p>As shown in <EquationRef id="einstein" />, energy equals...</p>
|
|
72
|
-
* </EquationProvider>
|
|
82
|
+
* @param {string} [props.id] - Identifier for cross-referencing. If omitted,
|
|
83
|
+
* a stable React id is generated but will not be discoverable by <EquationRef>.
|
|
84
|
+
* @param {string} props.mathml - Pre-compiled MathML HTML string from the
|
|
85
|
+
* content pipeline (e.g., `content.math[i].mathml`).
|
|
86
|
+
* @param {string} [props.label] - Override the auto-number with a custom label.
|
|
87
|
+
* @param {string} [props.className] - Additional CSS classes on the wrapper.
|
|
73
88
|
*/
|
|
74
|
-
export function Equation({
|
|
75
|
-
const
|
|
89
|
+
export function Equation({ id, mathml, label, className, ...props }) {
|
|
90
|
+
const ctx = useContext(EquationContext)
|
|
76
91
|
const generatedId = useId()
|
|
77
92
|
const equationId = id || generatedId
|
|
78
|
-
const ctx = useContext(EquationContext)
|
|
79
|
-
|
|
80
|
-
// Extract LaTeX string from children
|
|
81
|
-
const latex = typeof children === 'string' ? children.trim() : ''
|
|
82
93
|
|
|
83
|
-
// Register equation for numbering
|
|
84
94
|
useEffect(() => {
|
|
85
|
-
if (ctx)
|
|
86
|
-
ctx.register(equationId)
|
|
87
|
-
}
|
|
95
|
+
if (ctx) ctx.register(equationId)
|
|
88
96
|
}, [ctx, equationId])
|
|
89
97
|
|
|
90
|
-
// Get equation number
|
|
91
98
|
const number = ctx?.getNumber(equationId)
|
|
92
|
-
|
|
93
|
-
// Render LaTeX
|
|
94
|
-
useEffect(() => {
|
|
95
|
-
if (!latex) return
|
|
96
|
-
|
|
97
|
-
let cancelled = false
|
|
98
|
-
|
|
99
|
-
async function render() {
|
|
100
|
-
const katex = await loadKatex()
|
|
101
|
-
if (cancelled || !katex) return
|
|
102
|
-
|
|
103
|
-
try {
|
|
104
|
-
const rendered = katex.renderToString(latex, {
|
|
105
|
-
displayMode: true,
|
|
106
|
-
throwOnError: false,
|
|
107
|
-
})
|
|
108
|
-
if (!cancelled) {
|
|
109
|
-
setHtml(rendered)
|
|
110
|
-
}
|
|
111
|
-
} catch (e) {
|
|
112
|
-
console.warn('[Equation] Render error:', e.message)
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
render()
|
|
117
|
-
|
|
118
|
-
return () => {
|
|
119
|
-
cancelled = true
|
|
120
|
-
}
|
|
121
|
-
}, [latex])
|
|
122
|
-
|
|
123
|
-
const displayLabel = label || (number ? `(${number})` : '')
|
|
99
|
+
const displayLabel = label !== undefined ? label : number ? `(${number})` : ''
|
|
124
100
|
|
|
125
101
|
return (
|
|
126
102
|
<div
|
|
@@ -135,16 +111,10 @@ export function Equation({ children, id, label, className, ...props }) {
|
|
|
135
111
|
}}
|
|
136
112
|
{...props}
|
|
137
113
|
>
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
{
|
|
141
|
-
|
|
142
|
-
) : (
|
|
143
|
-
<code>{latex}</code>
|
|
144
|
-
)}
|
|
145
|
-
</div>
|
|
146
|
-
|
|
147
|
-
{/* Equation number */}
|
|
114
|
+
<div
|
|
115
|
+
style={{ flex: 1, textAlign: 'center' }}
|
|
116
|
+
dangerouslySetInnerHTML={{ __html: mathml || '' }}
|
|
117
|
+
/>
|
|
148
118
|
{displayLabel && (
|
|
149
119
|
<span
|
|
150
120
|
style={{
|
package/src/math/index.js
CHANGED
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Math Module
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Numbered equations and cross-references, rendered from pre-compiled
|
|
5
|
+
* MathML produced by Uniweb's content pipeline.
|
|
6
|
+
*
|
|
7
|
+
* Plain inline and display math (`$x$`, `$$x$$`, fenced ```math) is
|
|
8
|
+
* handled entirely by the content pipeline — authors write LaTeX in
|
|
9
|
+
* markdown and the browser renders real MathML natively. No component
|
|
10
|
+
* wrapper needed. The former `<Math>` component (KaTeX-based) was
|
|
11
|
+
* removed in an earlier release.
|
|
12
|
+
*
|
|
13
|
+
* `<Equation>` and `<EquationRef>` remain for the distinct feature of
|
|
14
|
+
* numbered cross-referenceable equations. They consume `content.math`
|
|
15
|
+
* entries (which carry pre-compiled MathML and an optional id); scholar
|
|
16
|
+
* no longer ships a math renderer.
|
|
5
17
|
*
|
|
6
18
|
* @module @uniweb/scholar/math
|
|
7
19
|
*/
|
|
8
20
|
|
|
9
|
-
export {
|
|
10
|
-
|
|
21
|
+
export {
|
|
22
|
+
Equation,
|
|
23
|
+
EquationProvider,
|
|
24
|
+
EquationContext,
|
|
25
|
+
useEquations,
|
|
26
|
+
} from './Equation.jsx'
|
|
11
27
|
export { EquationRef } from './EquationRef.jsx'
|
|
12
|
-
export { loadKatex, isKatexLoaded, renderLatex } from './katex-loader.js'
|
package/src/math/Math.jsx
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Math Component
|
|
3
|
-
*
|
|
4
|
-
* Renders LaTeX math expressions using KaTeX.
|
|
5
|
-
* Supports both inline and display (block) modes.
|
|
6
|
-
*
|
|
7
|
-
* @module @uniweb/scholar/math/Math
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import React, { useEffect, useState } from 'react'
|
|
11
|
-
import { loadKatex } from './katex-loader.js'
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Math - Render LaTeX math expressions
|
|
15
|
-
*
|
|
16
|
-
* @param {Object} props
|
|
17
|
-
* @param {string} props.children - LaTeX expression to render
|
|
18
|
-
* @param {boolean} [props.display=false] - Display mode (block) vs inline
|
|
19
|
-
* @param {string} [props.className] - Additional CSS classes
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* // Inline math
|
|
23
|
-
* <Math>E = mc^2</Math>
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* // Display (block) math
|
|
27
|
-
* <Math display>
|
|
28
|
-
* \int_0^\infty e^{-x^2} dx = \frac{\sqrt{\pi}}{2}
|
|
29
|
-
* </Math>
|
|
30
|
-
*/
|
|
31
|
-
export function Math({ children, display = false, className, ...props }) {
|
|
32
|
-
const [html, setHtml] = useState(null)
|
|
33
|
-
const [error, setError] = useState(null)
|
|
34
|
-
|
|
35
|
-
// Extract LaTeX string from children
|
|
36
|
-
const latex = typeof children === 'string' ? children.trim() : ''
|
|
37
|
-
|
|
38
|
-
useEffect(() => {
|
|
39
|
-
if (!latex) return
|
|
40
|
-
|
|
41
|
-
let cancelled = false
|
|
42
|
-
|
|
43
|
-
async function render() {
|
|
44
|
-
const katex = await loadKatex()
|
|
45
|
-
if (cancelled || !katex) return
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
const rendered = katex.renderToString(latex, {
|
|
49
|
-
displayMode: display,
|
|
50
|
-
throwOnError: false,
|
|
51
|
-
errorColor: '#cc0000',
|
|
52
|
-
})
|
|
53
|
-
if (!cancelled) {
|
|
54
|
-
setHtml(rendered)
|
|
55
|
-
setError(null)
|
|
56
|
-
}
|
|
57
|
-
} catch (e) {
|
|
58
|
-
if (!cancelled) {
|
|
59
|
-
setError(e.message)
|
|
60
|
-
setHtml(null)
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
render()
|
|
66
|
-
|
|
67
|
-
return () => {
|
|
68
|
-
cancelled = true
|
|
69
|
-
}
|
|
70
|
-
}, [latex, display])
|
|
71
|
-
|
|
72
|
-
// Error state
|
|
73
|
-
if (error) {
|
|
74
|
-
return (
|
|
75
|
-
<span
|
|
76
|
-
className={className}
|
|
77
|
-
style={{ color: '#cc0000', fontFamily: 'monospace' }}
|
|
78
|
-
title={error}
|
|
79
|
-
{...props}
|
|
80
|
-
>
|
|
81
|
-
{latex}
|
|
82
|
-
</span>
|
|
83
|
-
)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Loading state - show raw LaTeX in code format
|
|
87
|
-
if (!html) {
|
|
88
|
-
return (
|
|
89
|
-
<code className={className} {...props}>
|
|
90
|
-
{latex}
|
|
91
|
-
</code>
|
|
92
|
-
)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Rendered math
|
|
96
|
-
if (display) {
|
|
97
|
-
return (
|
|
98
|
-
<div
|
|
99
|
-
className={className}
|
|
100
|
-
dangerouslySetInnerHTML={{ __html: html }}
|
|
101
|
-
{...props}
|
|
102
|
-
/>
|
|
103
|
-
)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return (
|
|
107
|
-
<span
|
|
108
|
-
className={className}
|
|
109
|
-
dangerouslySetInnerHTML={{ __html: html }}
|
|
110
|
-
{...props}
|
|
111
|
-
/>
|
|
112
|
-
)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export default Math
|
package/src/math/katex-loader.js
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* KaTeX Lazy Loader
|
|
3
|
-
*
|
|
4
|
-
* Provides lazy loading of KaTeX with module-level caching.
|
|
5
|
-
* KaTeX is only loaded when math rendering is actually needed.
|
|
6
|
-
*
|
|
7
|
-
* @module @uniweb/scholar/math/katex-loader
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
// Module-level state for singleton pattern
|
|
11
|
-
let katexInstance = null
|
|
12
|
-
let katexLoadPromise = null
|
|
13
|
-
let cssInjected = false
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* CDN URL for KaTeX CSS (matches the katex version in package.json)
|
|
17
|
-
*/
|
|
18
|
-
const KATEX_CSS_URL = 'https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.css'
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Inject KaTeX CSS into the document head
|
|
22
|
-
* Only injects once per page load
|
|
23
|
-
*/
|
|
24
|
-
function injectKatexCSS() {
|
|
25
|
-
if (cssInjected || typeof document === 'undefined') return
|
|
26
|
-
|
|
27
|
-
const styleId = 'uniweb-katex-css'
|
|
28
|
-
|
|
29
|
-
// Check if already injected
|
|
30
|
-
if (document.getElementById(styleId)) {
|
|
31
|
-
cssInjected = true
|
|
32
|
-
return
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const link = document.createElement('link')
|
|
36
|
-
link.id = styleId
|
|
37
|
-
link.rel = 'stylesheet'
|
|
38
|
-
link.href = KATEX_CSS_URL
|
|
39
|
-
link.crossOrigin = 'anonymous'
|
|
40
|
-
document.head.appendChild(link)
|
|
41
|
-
|
|
42
|
-
cssInjected = true
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Load KaTeX lazily
|
|
47
|
-
*
|
|
48
|
-
* Returns cached instance if already loaded, otherwise loads
|
|
49
|
-
* KaTeX dynamically and injects its CSS.
|
|
50
|
-
*
|
|
51
|
-
* @returns {Promise<Object>} The KaTeX module
|
|
52
|
-
*/
|
|
53
|
-
export async function loadKatex() {
|
|
54
|
-
if (katexInstance) return katexInstance
|
|
55
|
-
if (katexLoadPromise) return katexLoadPromise
|
|
56
|
-
|
|
57
|
-
katexLoadPromise = (async () => {
|
|
58
|
-
try {
|
|
59
|
-
const katex = await import('katex')
|
|
60
|
-
katexInstance = katex.default || katex
|
|
61
|
-
|
|
62
|
-
// Inject CSS when KaTeX is loaded
|
|
63
|
-
injectKatexCSS()
|
|
64
|
-
|
|
65
|
-
return katexInstance
|
|
66
|
-
} catch (error) {
|
|
67
|
-
console.warn('[scholar/math] Failed to load KaTeX:', error)
|
|
68
|
-
katexLoadPromise = null
|
|
69
|
-
return null
|
|
70
|
-
}
|
|
71
|
-
})()
|
|
72
|
-
|
|
73
|
-
return katexLoadPromise
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Check if KaTeX is already loaded
|
|
78
|
-
*
|
|
79
|
-
* @returns {boolean} True if KaTeX is loaded and ready
|
|
80
|
-
*/
|
|
81
|
-
export function isKatexLoaded() {
|
|
82
|
-
return katexInstance !== null
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Render LaTeX to HTML string
|
|
87
|
-
*
|
|
88
|
-
* @param {string} latex - LaTeX string to render
|
|
89
|
-
* @param {Object} options - KaTeX options
|
|
90
|
-
* @returns {Promise<string|null>} Rendered HTML or null on error
|
|
91
|
-
*/
|
|
92
|
-
export async function renderLatex(latex, options = {}) {
|
|
93
|
-
const katex = await loadKatex()
|
|
94
|
-
if (!katex) return null
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
return katex.renderToString(latex, {
|
|
98
|
-
throwOnError: false,
|
|
99
|
-
...options,
|
|
100
|
-
})
|
|
101
|
-
} catch (error) {
|
|
102
|
-
console.warn('[scholar/math] LaTeX render error:', error.message)
|
|
103
|
-
return null
|
|
104
|
-
}
|
|
105
|
-
}
|