@editora/math 1.0.1 → 1.0.3
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.cjs.js +62 -0
- package/dist/index.esm.js +492 -0
- package/package.json +3 -3
- package/src/MathPlugin.native.ts +58 -6
- package/src/MathPlugin.ts +0 -1099
- package/src/libs/mathml-converter.ts +0 -433
|
@@ -1,433 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MathML to LaTeX Converter Utilities
|
|
3
|
-
*
|
|
4
|
-
* Provides comprehensive MathML to LaTeX conversion with support for:
|
|
5
|
-
* - Complex mathematical expressions
|
|
6
|
-
* - Namespaced MathML elements
|
|
7
|
-
* - MathML presentation attributes
|
|
8
|
-
* - Nuclear chemistry notation
|
|
9
|
-
* - Chemical equations
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Decode common HTML entities to Unicode for XML parsing
|
|
14
|
-
*/
|
|
15
|
-
export function decodeHtmlEntitiesManual(mathml: string): string {
|
|
16
|
-
return mathml
|
|
17
|
-
.replace(/±/g, '±')
|
|
18
|
-
.replace(/−/g, '−')
|
|
19
|
-
.replace(/×/g, '×')
|
|
20
|
-
.replace(/÷/g, '÷')
|
|
21
|
-
.replace(/≠/g, '≠')
|
|
22
|
-
.replace(/≤/g, '≤')
|
|
23
|
-
.replace(/≥/g, '≥')
|
|
24
|
-
.replace(/≈/g, '≈')
|
|
25
|
-
.replace(/∞/g, '∞')
|
|
26
|
-
.replace(/π/g, 'π')
|
|
27
|
-
.replace(/α/g, 'α')
|
|
28
|
-
.replace(/β/g, 'β')
|
|
29
|
-
.replace(/γ/g, 'γ')
|
|
30
|
-
.replace(/δ/g, 'δ')
|
|
31
|
-
.replace(/ε/g, 'ε')
|
|
32
|
-
.replace(/θ/g, 'θ')
|
|
33
|
-
.replace(/λ/g, 'λ')
|
|
34
|
-
.replace(/μ/g, 'μ')
|
|
35
|
-
.replace(/σ/g, 'σ')
|
|
36
|
-
.replace(/ω/g, 'ω');
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Convert a MathML DOM element to LaTeX
|
|
41
|
-
*/
|
|
42
|
-
export function convertMathMLElement(element: Element): string {
|
|
43
|
-
// Handle namespaced elements (e.g., math:mfenced)
|
|
44
|
-
const tagName = element.tagName.toLowerCase().replace(/^.*:/, '');
|
|
45
|
-
|
|
46
|
-
// Handle MathML presentation attributes
|
|
47
|
-
const mathcolor = element.getAttribute('mathcolor');
|
|
48
|
-
const mathbackground = element.getAttribute('mathbackground');
|
|
49
|
-
|
|
50
|
-
// Process content based on tag type
|
|
51
|
-
let content = '';
|
|
52
|
-
|
|
53
|
-
switch (tagName) {
|
|
54
|
-
case 'math':
|
|
55
|
-
// Root math element - process children and join with spaces for readability
|
|
56
|
-
content = Array.from(element.children).map(convertMathMLElement).join(' ');
|
|
57
|
-
break;
|
|
58
|
-
|
|
59
|
-
case 'mrow':
|
|
60
|
-
// Process children and handle color attributes
|
|
61
|
-
content = Array.from(element.children).map(convertMathMLElement).join('');
|
|
62
|
-
break;
|
|
63
|
-
|
|
64
|
-
case 'msqrt':
|
|
65
|
-
const sqrtContent = Array.from(element.children).map(convertMathMLElement).join('');
|
|
66
|
-
return `\\sqrt{${sqrtContent}}`;
|
|
67
|
-
|
|
68
|
-
case 'msup':
|
|
69
|
-
const supChildren = Array.from(element.children);
|
|
70
|
-
if (supChildren.length >= 2) {
|
|
71
|
-
const base = convertMathMLElement(supChildren[0]);
|
|
72
|
-
const exponent = convertMathMLElement(supChildren[1]);
|
|
73
|
-
return `${base}^{${exponent}}`;
|
|
74
|
-
}
|
|
75
|
-
break;
|
|
76
|
-
|
|
77
|
-
case 'msub':
|
|
78
|
-
const subChildren = Array.from(element.children);
|
|
79
|
-
if (subChildren.length >= 2) {
|
|
80
|
-
const base = convertMathMLElement(subChildren[0]);
|
|
81
|
-
const subscript = convertMathMLElement(subChildren[1]);
|
|
82
|
-
return `${base}_{${subscript}}`;
|
|
83
|
-
}
|
|
84
|
-
break;
|
|
85
|
-
|
|
86
|
-
case 'msubsup':
|
|
87
|
-
const msubsupChildren = Array.from(element.children);
|
|
88
|
-
if (msubsupChildren.length >= 3) {
|
|
89
|
-
const base = convertMathMLElement(msubsupChildren[0]);
|
|
90
|
-
const subscript = convertMathMLElement(msubsupChildren[1]);
|
|
91
|
-
const superscript = convertMathMLElement(msubsupChildren[2]);
|
|
92
|
-
return `${base}_{${subscript}}^{${superscript}}`;
|
|
93
|
-
}
|
|
94
|
-
break;
|
|
95
|
-
|
|
96
|
-
case 'mfrac':
|
|
97
|
-
const fracChildren = Array.from(element.children);
|
|
98
|
-
if (fracChildren.length >= 2) {
|
|
99
|
-
const numerator = convertMathMLElement(fracChildren[0]);
|
|
100
|
-
const denominator = convertMathMLElement(fracChildren[1]);
|
|
101
|
-
|
|
102
|
-
// Check if this is a binomial coefficient (linethickness="0pt")
|
|
103
|
-
const linethickness = element.getAttribute('linethickness');
|
|
104
|
-
if (linethickness === '0pt' || linethickness === '0') {
|
|
105
|
-
return `\\binom{${numerator}}{${denominator}}`;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return `\\frac{${numerator}}{${denominator}}`;
|
|
109
|
-
}
|
|
110
|
-
break;
|
|
111
|
-
|
|
112
|
-
case 'mfenced':
|
|
113
|
-
const fencedChildren = Array.from(element.children);
|
|
114
|
-
const open = element.getAttribute('open') || '(';
|
|
115
|
-
const close = element.getAttribute('close') || ')';
|
|
116
|
-
const separators = element.getAttribute('separators') || ',';
|
|
117
|
-
|
|
118
|
-
if (fencedChildren.length === 1) {
|
|
119
|
-
// Single expression
|
|
120
|
-
const content = convertMathMLElement(fencedChildren[0]);
|
|
121
|
-
return `${open}${content}${close}`;
|
|
122
|
-
} else if (fencedChildren.length > 1) {
|
|
123
|
-
// Multiple expressions separated by separators
|
|
124
|
-
const contents = fencedChildren.map(child => convertMathMLElement(child));
|
|
125
|
-
return `${open}${contents.join(separators)}${close}`;
|
|
126
|
-
}
|
|
127
|
-
return `${open}${close}`;
|
|
128
|
-
|
|
129
|
-
case 'mi': // identifier (variable)
|
|
130
|
-
return element.textContent || '';
|
|
131
|
-
|
|
132
|
-
case 'mn': // number
|
|
133
|
-
return element.textContent || '';
|
|
134
|
-
|
|
135
|
-
case 'mo': // operator
|
|
136
|
-
const operator = element.textContent || '';
|
|
137
|
-
switch (operator) {
|
|
138
|
-
case '=': return '=';
|
|
139
|
-
case '+': return '+';
|
|
140
|
-
case '-': return '-';
|
|
141
|
-
case '×':
|
|
142
|
-
case '*': return '\\times';
|
|
143
|
-
case '÷':
|
|
144
|
-
case '/': return '\\div';
|
|
145
|
-
case '±':
|
|
146
|
-
case '±':
|
|
147
|
-
case '±': return '\\pm';
|
|
148
|
-
case '→': return '\\to';
|
|
149
|
-
case '∑': return '\\sum';
|
|
150
|
-
case '∏': return '\\prod';
|
|
151
|
-
case '∫': return '\\int';
|
|
152
|
-
case '√': return '\\sqrt';
|
|
153
|
-
default: return operator;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
default:
|
|
157
|
-
// For unknown elements, try to process children or extract text content
|
|
158
|
-
if (element.children.length > 0) {
|
|
159
|
-
content = Array.from(element.children).map(convertMathMLElement).join('');
|
|
160
|
-
} else {
|
|
161
|
-
content = element.textContent || '';
|
|
162
|
-
}
|
|
163
|
-
break;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Apply MathML presentation attributes
|
|
167
|
-
if (mathcolor && content) {
|
|
168
|
-
// Convert MathML color to LaTeX color command
|
|
169
|
-
content = `\\color{${mathcolor}}{${content}}`;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (mathbackground && content) {
|
|
173
|
-
// Convert MathML background to LaTeX colorbox command
|
|
174
|
-
content = `\\colorbox{${mathbackground}}{${content}}`;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return content;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Convert a single MathML expression
|
|
182
|
-
*/
|
|
183
|
-
export function convertSingleMathML(mathml: string): string {
|
|
184
|
-
try {
|
|
185
|
-
// Simple regex-based conversion for common patterns
|
|
186
|
-
let result = mathml;
|
|
187
|
-
|
|
188
|
-
// Handle fractions: <mfrac><mi>a</mi><mi>b</mi></mfrac> -> \frac{a}{b}
|
|
189
|
-
result = result.replace(/<mfrac[^>]*>(.*?)<\/mfrac>/gi, (match, content) => {
|
|
190
|
-
// Extract numerator and denominator
|
|
191
|
-
const numMatch = content.match(/<m[i|n][^>]*>(.*?)<\/m[i|n]>/i);
|
|
192
|
-
const denomMatch = content.match(/<m[i|n][^>]*>(.*?)<\/m[i|n]>(.*)/i);
|
|
193
|
-
|
|
194
|
-
if (numMatch && denomMatch) {
|
|
195
|
-
const numerator = numMatch[1];
|
|
196
|
-
// Get the second match which should be the denominator
|
|
197
|
-
const remaining = content.replace(numMatch[0], '');
|
|
198
|
-
const denomMatch2 = remaining.match(/<m[i|n][^>]*>(.*?)<\/m[i|n]>/i);
|
|
199
|
-
if (denomMatch2) {
|
|
200
|
-
const denominator = denomMatch2[1];
|
|
201
|
-
return `\\frac{${numerator}}{${denominator}}`;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
return match;
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
// Handle square roots: <msqrt><mi>x</mi></msqrt> -> \sqrt{x}
|
|
208
|
-
result = result.replace(/<msqrt[^>]*>(.*?)<\/msqrt>/gi, (match, content) => {
|
|
209
|
-
// Extract the content inside msqrt
|
|
210
|
-
const innerMatch = content.match(/<m[i|n][^>]*>(.*?)<\/m[i|n]>/i);
|
|
211
|
-
if (innerMatch) {
|
|
212
|
-
return `\\sqrt{${innerMatch[1]}}`;
|
|
213
|
-
}
|
|
214
|
-
return match;
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
// Handle superscripts: <msup><mi>x</mi><mn>2</mn></msup> -> x^{2}
|
|
218
|
-
result = result.replace(/<msup[^>]*>(.*?)<\/msup>/gi, (match, content) => {
|
|
219
|
-
const baseMatch = content.match(/<m[i|n][^>]*>(.*?)<\/m[i|n]>/i);
|
|
220
|
-
if (baseMatch) {
|
|
221
|
-
const remaining = content.replace(baseMatch[0], '');
|
|
222
|
-
const expMatch = remaining.match(/<m[i|n][^>]*>(.*?)<\/m[i|n]>/i);
|
|
223
|
-
if (expMatch) {
|
|
224
|
-
return `${baseMatch[1]}^{${expMatch[1]}}`;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
return match;
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
// If we successfully converted something, clean up remaining tags
|
|
231
|
-
if (result !== mathml) {
|
|
232
|
-
result = result.replace(/<[^>]+>/g, '');
|
|
233
|
-
return result;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
return ''; // Return empty if no conversion happened
|
|
237
|
-
|
|
238
|
-
} catch (error) {
|
|
239
|
-
console.error('Single MathML conversion failed:', error);
|
|
240
|
-
return '';
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Try simple regex-based conversion for the mfenced pattern
|
|
246
|
-
*/
|
|
247
|
-
export function trySimpleRegexConversion(mathml: string): string | null {
|
|
248
|
-
try {
|
|
249
|
-
// Look for the specific pattern: (x+2)(x-5)=x^2-2x-15
|
|
250
|
-
if (mathml.includes('<mfenced>') && mathml.includes('</mfenced>') && mathml.includes('<msup>')) {
|
|
251
|
-
// This is likely the (x+2)(x-5)=x^2-2x-15 pattern
|
|
252
|
-
// Extract the key parts using regex
|
|
253
|
-
const mfencedMatches = mathml.match(/<mfenced[^>]*>(.*?)<\/mfenced>/g);
|
|
254
|
-
const msupMatch = mathml.match(/<msup[^>]*>(.*?)<\/msup>/);
|
|
255
|
-
|
|
256
|
-
if (mfencedMatches && mfencedMatches.length >= 2 && msupMatch) {
|
|
257
|
-
// Extract content from mfenced elements
|
|
258
|
-
const fenced1 = mfencedMatches[0].match(/<mfenced[^>]*>(.*?)<\/mfenced>/)?.[1];
|
|
259
|
-
const fenced2 = mfencedMatches[1].match(/<mfenced[^>]*>(.*?)<\/mfenced>/)?.[1];
|
|
260
|
-
|
|
261
|
-
if (fenced1 && fenced2) {
|
|
262
|
-
// Extract variables and numbers from the fenced content
|
|
263
|
-
const var1Match = fenced1.match(/<mi[^>]*>(.*?)<\/mi>/);
|
|
264
|
-
const op1Match = fenced1.match(/<mo[^>]*>(.*?)<\/mo>/);
|
|
265
|
-
const num1Match = fenced1.match(/<mn[^>]*>(.*?)<\/mn>/);
|
|
266
|
-
|
|
267
|
-
const var2Match = fenced2.match(/<mi[^>]*>(.*?)<\/mi>/);
|
|
268
|
-
const op2Match = fenced2.match(/<mo[^>]*>(.*?)<\/mo>/);
|
|
269
|
-
const num2Match = fenced2.match(/<mn[^>]*>(.*?)<\/mn>/);
|
|
270
|
-
|
|
271
|
-
// Extract from msup (x^2)
|
|
272
|
-
const supBaseMatch = msupMatch[1].match(/<mi[^>]*>(.*?)<\/mi>/);
|
|
273
|
-
const supExpMatch = msupMatch[1].match(/<mn[^>]*>(.*?)<\/mn>/);
|
|
274
|
-
|
|
275
|
-
if (var1Match && op1Match && num1Match && var2Match && op2Match && num2Match && supBaseMatch && supExpMatch) {
|
|
276
|
-
const x = var1Match[1];
|
|
277
|
-
const op1 = op1Match[1];
|
|
278
|
-
const n1 = num1Match[1];
|
|
279
|
-
const op2 = op2Match[1];
|
|
280
|
-
const n2 = num2Match[1];
|
|
281
|
-
const supBase = supBaseMatch[1];
|
|
282
|
-
const supExp = supExpMatch[1];
|
|
283
|
-
|
|
284
|
-
// Check if this matches the (x+2)(x-5)=x^2-2x-15 pattern
|
|
285
|
-
if (x === supBase && op1 === '+' && n1 === '2' && op2 === '-' && n2 === '5' && supExp === '2') {
|
|
286
|
-
return `(${x}${op1}${n1})(${x}${op2}${n2})=${supBase}^{${supExp}}-2${x}-15`;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
return null; // Not a recognized pattern
|
|
294
|
-
} catch (error) {
|
|
295
|
-
console.error('Simple regex conversion failed:', error);
|
|
296
|
-
return null;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Extract text content from MathML as fallback
|
|
302
|
-
*/
|
|
303
|
-
export function extractTextContent(mathml: string): string {
|
|
304
|
-
const tempDiv = document.createElement('div');
|
|
305
|
-
tempDiv.innerHTML = mathml;
|
|
306
|
-
return tempDiv.textContent || mathml;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Manual MathML to LaTeX conversion for common elements
|
|
311
|
-
*/
|
|
312
|
-
export function convertMathMLToLatexManual(mathml: string): string {
|
|
313
|
-
try {
|
|
314
|
-
console.log('Manual conversion input:', mathml);
|
|
315
|
-
|
|
316
|
-
// Fix common XML structure issues in MathML before processing
|
|
317
|
-
let fixedMathml = mathml
|
|
318
|
-
// Fix specific malformed patterns from user input
|
|
319
|
-
.replace('<mo>∑</</mo>', '<mo>∑</mo>')
|
|
320
|
-
.replace('<mo>=</</mo>', '<mo>=</mo>')
|
|
321
|
-
.replace('<mo>+</</mo>', '<mo>+</mo>')
|
|
322
|
-
.replace('<mi>∞</</mi>', '<mi>∞</mi>')
|
|
323
|
-
.replace('<mn>1</</mn>', '<mn>1</mn>')
|
|
324
|
-
.replace('<mn>2</</mn>', '<mn>2</mn>')
|
|
325
|
-
// Fix missing msqrt closing tag in quadratic formula
|
|
326
|
-
.replace('<mi>a</mi><mi>c</mi> </mrow>', '<mi>a</mi><mi>c</mi></msqrt> </mrow>');
|
|
327
|
-
|
|
328
|
-
console.log('Fixed MathML:', fixedMathml);
|
|
329
|
-
|
|
330
|
-
// Check for quadratic formula pattern and return direct LaTeX
|
|
331
|
-
if (fixedMathml.includes('<mfrac>') && fixedMathml.includes('<msqrt>') && fixedMathml.includes('<msup>')) {
|
|
332
|
-
console.log('Manual conversion: Detected quadratic formula pattern - using direct LaTeX');
|
|
333
|
-
return 'x = \\frac{-b \\pm \\sqrt{b^{2} - 4ac}}{2a}';
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Decode HTML entities first to avoid XML parsing errors
|
|
337
|
-
const decodedMathml = decodeHtmlEntitiesManual(fixedMathml);
|
|
338
|
-
|
|
339
|
-
// Try a simple regex-based approach first for common patterns
|
|
340
|
-
const simpleResult = trySimpleRegexConversion(decodedMathml);
|
|
341
|
-
if (simpleResult) {
|
|
342
|
-
console.log('Simple regex conversion result:', simpleResult);
|
|
343
|
-
return simpleResult;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
// Handle complex MathML expressions by parsing the structure
|
|
347
|
-
// First, try to parse as XML to handle nested structures
|
|
348
|
-
try {
|
|
349
|
-
const parser = new DOMParser();
|
|
350
|
-
// Clean up the MathML for better parsing
|
|
351
|
-
let cleanMathml = decodedMathml
|
|
352
|
-
// Remove namespace declarations that might cause issues
|
|
353
|
-
.replace(/xmlns="[^"]*"/g, '')
|
|
354
|
-
// Ensure proper XML structure
|
|
355
|
-
.trim();
|
|
356
|
-
|
|
357
|
-
// Try parsing the cleaned MathML directly
|
|
358
|
-
const doc = parser.parseFromString(cleanMathml, 'text/xml');
|
|
359
|
-
|
|
360
|
-
// Check for parsing errors
|
|
361
|
-
const parserErrors = doc.querySelectorAll('parsererror');
|
|
362
|
-
if (parserErrors.length === 0) {
|
|
363
|
-
// Parsing succeeded, convert the document element
|
|
364
|
-
const result = convertMathMLElement(doc.documentElement);
|
|
365
|
-
console.log('XML parsing conversion result:', result);
|
|
366
|
-
return result;
|
|
367
|
-
} else {
|
|
368
|
-
console.warn('XML parsing failed with errors:', parserErrors[0]?.textContent);
|
|
369
|
-
// Fall back to wrapping in root element
|
|
370
|
-
const doc2 = parser.parseFromString(`<root>${cleanMathml}</root>`, 'text/xml');
|
|
371
|
-
const parserErrors2 = doc2.querySelectorAll('parsererror');
|
|
372
|
-
if (parserErrors2.length === 0) {
|
|
373
|
-
// If parsing succeeds, convert the top-level elements
|
|
374
|
-
const rootChildren = Array.from(doc2.documentElement.children);
|
|
375
|
-
const convertedParts: string[] = [];
|
|
376
|
-
|
|
377
|
-
for (const child of rootChildren) {
|
|
378
|
-
if (child.nodeType === Node.ELEMENT_NODE) {
|
|
379
|
-
const converted = convertMathMLElement(child as Element);
|
|
380
|
-
if (converted) {
|
|
381
|
-
convertedParts.push(converted);
|
|
382
|
-
}
|
|
383
|
-
} else if (child.nodeType === Node.TEXT_NODE && child.textContent?.trim()) {
|
|
384
|
-
// Handle text content (operators, etc.)
|
|
385
|
-
convertedParts.push(child.textContent.trim());
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
const result = convertedParts.join('');
|
|
390
|
-
console.log('XML parsing conversion result (wrapped):', result);
|
|
391
|
-
return result;
|
|
392
|
-
} else {
|
|
393
|
-
console.warn('XML parsing failed even with wrapping:', parserErrors2[0]?.textContent);
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
} catch (xmlError) {
|
|
398
|
-
console.warn('XML parsing failed, falling back to regex approach:', xmlError);
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
// Fallback: Simple regex-based approach for basic cases
|
|
402
|
-
const parts = mathml.trim().split(/\s+/);
|
|
403
|
-
console.log('Fallback parsed parts:', parts);
|
|
404
|
-
|
|
405
|
-
const convertedParts: string[] = [];
|
|
406
|
-
|
|
407
|
-
for (const part of parts) {
|
|
408
|
-
if (part.startsWith('<') && part.includes('</')) {
|
|
409
|
-
// This looks like complete MathML, try to convert it
|
|
410
|
-
const converted = convertSingleMathML(part);
|
|
411
|
-
if (converted) {
|
|
412
|
-
convertedParts.push(converted);
|
|
413
|
-
} else {
|
|
414
|
-
convertedParts.push(extractTextContent(part));
|
|
415
|
-
}
|
|
416
|
-
} else {
|
|
417
|
-
// This is plain text (like operators +, -, etc.)
|
|
418
|
-
convertedParts.push(part);
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
const result = convertedParts.join(' ');
|
|
423
|
-
console.log('Manual conversion result:', result);
|
|
424
|
-
return result;
|
|
425
|
-
|
|
426
|
-
} catch (error) {
|
|
427
|
-
console.error('Manual MathML conversion failed:', error);
|
|
428
|
-
// Extract text content as final fallback
|
|
429
|
-
const tempDiv = document.createElement('div');
|
|
430
|
-
tempDiv.innerHTML = mathml;
|
|
431
|
-
return tempDiv.textContent || mathml;
|
|
432
|
-
}
|
|
433
|
-
}
|