@glossarist/concept-browser 0.2.12 → 0.3.1
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/package.json +1 -1
- package/scripts/generate-data.mjs +12 -5
- package/scripts/math-prerender.mjs +80 -0
- package/src/__tests__/math.test.ts +26 -58
- package/src/style.css +2 -4
- package/src/utils/math.ts +8 -97
- package/vite.config.ts +0 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glossarist/concept-browser",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Vue SPA for browsing Glossarist terminology datasets with cross-reference resolution, graph visualization, and multi-language support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -3,6 +3,7 @@ import path from 'path';
|
|
|
3
3
|
import yaml from 'js-yaml';
|
|
4
4
|
import { naturalSort } from 'glossarist';
|
|
5
5
|
import { loadSiteConfig } from './load-site-config.mjs';
|
|
6
|
+
import { preRenderMath } from './math-prerender.mjs';
|
|
6
7
|
|
|
7
8
|
const __dirname = path.dirname(new URL(import.meta.url).pathname);
|
|
8
9
|
const ROOT = process.cwd();
|
|
@@ -55,7 +56,7 @@ function termToDesignation(term) {
|
|
|
55
56
|
: term.type === 'abbreviation' ? 'gl:Abbreviation'
|
|
56
57
|
: 'gl:Designation',
|
|
57
58
|
'gl:normativeStatus': term.normative_status || 'preferred',
|
|
58
|
-
'gl:term': term.designation,
|
|
59
|
+
'gl:term': preRenderMath(term.designation),
|
|
59
60
|
};
|
|
60
61
|
if (term.gender) doc['gl:gender'] = term.gender;
|
|
61
62
|
if (term.plurality) doc['gl:plurality'] = term.plurality;
|
|
@@ -68,7 +69,7 @@ function defsToJsonLd(defs) {
|
|
|
68
69
|
return defs
|
|
69
70
|
.map(d => ({
|
|
70
71
|
'@type': 'gl:DetailedDefinition',
|
|
71
|
-
'gl:content': d.content || '',
|
|
72
|
+
'gl:content': preRenderMath(d.content || ''),
|
|
72
73
|
}))
|
|
73
74
|
.filter(d => d['gl:content']);
|
|
74
75
|
}
|
|
@@ -255,7 +256,7 @@ function getPrimaryDesignation(conceptYaml) {
|
|
|
255
256
|
if (lc && lc.terms && lc.terms.length > 0) {
|
|
256
257
|
const preferredExpr = lc.terms.find(t => t.normative_status === 'preferred' && t.type === 'expression');
|
|
257
258
|
const preferred = preferredExpr || lc.terms.find(t => t.normative_status === 'preferred') || lc.terms[0];
|
|
258
|
-
descs[lang] = preferred.designation;
|
|
259
|
+
descs[lang] = preRenderMath(preferred.designation);
|
|
259
260
|
}
|
|
260
261
|
}
|
|
261
262
|
return descs;
|
|
@@ -531,11 +532,17 @@ function processDataset(dir, register, opts) {
|
|
|
531
532
|
status: c.status,
|
|
532
533
|
}));
|
|
533
534
|
|
|
535
|
+
// Strip HTML from index summary for text display
|
|
536
|
+
const plainSummary = summary.map(c => ({
|
|
537
|
+
...c,
|
|
538
|
+
eng: c.eng.replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim(),
|
|
539
|
+
}));
|
|
540
|
+
|
|
534
541
|
const graphNodeEntries = concepts.map(c => {
|
|
535
542
|
let term = '', lang = '';
|
|
536
543
|
if (c.designations.eng) { term = c.designations.eng; lang = 'eng'; }
|
|
537
544
|
else { for (const [l, t] of Object.entries(c.designations)) { if (t) { term = t; lang = l; break; } } }
|
|
538
|
-
return [c.id, term, lang, c.status];
|
|
545
|
+
return [c.id, term.replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim(), lang, c.status];
|
|
539
546
|
});
|
|
540
547
|
fs.mkdirSync(path.join(DATA, register), { recursive: true });
|
|
541
548
|
fs.writeFileSync(
|
|
@@ -553,7 +560,7 @@ function processDataset(dir, register, opts) {
|
|
|
553
560
|
conceptCount: concepts.length,
|
|
554
561
|
chunkSize: CHUNK_SIZE,
|
|
555
562
|
chunks,
|
|
556
|
-
concepts:
|
|
563
|
+
concepts: plainSummary,
|
|
557
564
|
});
|
|
558
565
|
|
|
559
566
|
writeJson(path.join(DATA, register, 'index-meta.json'), {
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import Plurimath from '@plurimath/plurimath';
|
|
2
|
+
|
|
3
|
+
function renderToMathML(math, format) {
|
|
4
|
+
try {
|
|
5
|
+
const p = new Plurimath(math, format);
|
|
6
|
+
return p.toMathml().replace('display="block"', 'display="inline"').trim();
|
|
7
|
+
} catch {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function escapeHtml(text) {
|
|
13
|
+
return text
|
|
14
|
+
.replace(/&/g, '&')
|
|
15
|
+
.replace(/</g, '<')
|
|
16
|
+
.replace(/>/g, '>');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function replaceBracketed(text, prefix, render) {
|
|
20
|
+
let result = '';
|
|
21
|
+
let i = 0;
|
|
22
|
+
const boldPrefix = '*' + prefix;
|
|
23
|
+
while (i < text.length) {
|
|
24
|
+
if (text.startsWith(boldPrefix + '[', i)) {
|
|
25
|
+
i += boldPrefix.length + 1;
|
|
26
|
+
let j = i;
|
|
27
|
+
let d = 1;
|
|
28
|
+
while (j < text.length && d > 0) {
|
|
29
|
+
if (text[j] === '[') d++;
|
|
30
|
+
else if (text[j] === ']') d--;
|
|
31
|
+
j++;
|
|
32
|
+
}
|
|
33
|
+
const content = text.slice(i, j - 1);
|
|
34
|
+
let end = j;
|
|
35
|
+
if (end < text.length && text[end] === '*') end++;
|
|
36
|
+
result += render(content, true);
|
|
37
|
+
i = end;
|
|
38
|
+
} else if (text.startsWith(prefix + '[', i)) {
|
|
39
|
+
i += prefix.length + 1;
|
|
40
|
+
let j = i;
|
|
41
|
+
let d = 1;
|
|
42
|
+
while (j < text.length && d > 0) {
|
|
43
|
+
if (text[j] === '[') d++;
|
|
44
|
+
else if (text[j] === ']') d--;
|
|
45
|
+
j++;
|
|
46
|
+
}
|
|
47
|
+
const content = text.slice(i, j - 1);
|
|
48
|
+
result += render(content, false);
|
|
49
|
+
i = j;
|
|
50
|
+
} else {
|
|
51
|
+
result += text[i];
|
|
52
|
+
i++;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function renderStem(math, bold) {
|
|
59
|
+
const mathml = renderToMathML(math, 'asciimath');
|
|
60
|
+
if (mathml) {
|
|
61
|
+
return `<span class="math-inline${bold ? ' math-bold' : ''}">${mathml}</span>`;
|
|
62
|
+
}
|
|
63
|
+
return `<code class="math-fallback">${escapeHtml(math)}</code>`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function renderLatexmath(math, bold) {
|
|
67
|
+
const mathml = renderToMathML(math, 'latex');
|
|
68
|
+
if (mathml) {
|
|
69
|
+
return `<span class="math-inline${bold ? ' math-bold' : ''}">${mathml}</span>`;
|
|
70
|
+
}
|
|
71
|
+
return `<code class="math-fallback">${escapeHtml(math)}</code>`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function preRenderMath(text) {
|
|
75
|
+
if (!text) return '';
|
|
76
|
+
let result = text;
|
|
77
|
+
result = replaceBracketed(result, 'stem:', renderStem);
|
|
78
|
+
result = replaceBracketed(result, 'latexmath:', renderLatexmath);
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
@@ -1,22 +1,4 @@
|
|
|
1
|
-
import { describe, it, expect
|
|
2
|
-
|
|
3
|
-
// Mock @plurimath/plurimath for test environment
|
|
4
|
-
vi.mock('@plurimath/plurimath', () => {
|
|
5
|
-
return {
|
|
6
|
-
default: class MockPlurimath {
|
|
7
|
-
private data: string;
|
|
8
|
-
private format: string;
|
|
9
|
-
constructor(data: string, format: string) {
|
|
10
|
-
this.data = data;
|
|
11
|
-
this.format = format;
|
|
12
|
-
}
|
|
13
|
-
toMathml() {
|
|
14
|
-
return `<math xmlns="http://www.w3.org/1998/Math/MathML"><mi>${this.data}</mi></math>`;
|
|
15
|
-
}
|
|
16
|
-
},
|
|
17
|
-
};
|
|
18
|
-
});
|
|
19
|
-
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
20
2
|
import { renderMath, cleanContent } from '../utils/math';
|
|
21
3
|
|
|
22
4
|
describe('renderMath', () => {
|
|
@@ -24,25 +6,24 @@ describe('renderMath', () => {
|
|
|
24
6
|
expect(renderMath('hello world')).toBe('hello world');
|
|
25
7
|
});
|
|
26
8
|
|
|
27
|
-
it('
|
|
28
|
-
const
|
|
29
|
-
expect(
|
|
30
|
-
expect(result).toContain('<math');
|
|
31
|
-
expect(result).not.toContain('math-bold');
|
|
9
|
+
it('passes through pre-rendered MathML unchanged', () => {
|
|
10
|
+
const preRendered = 'value <span class="math-inline"><math><mi>x</mi></math></span> here';
|
|
11
|
+
expect(renderMath(preRendered)).toBe(preRendered);
|
|
32
12
|
});
|
|
33
13
|
|
|
34
|
-
it('
|
|
35
|
-
const
|
|
36
|
-
expect(
|
|
37
|
-
|
|
14
|
+
it('still converts italic in mixed pre-rendered MathML content', () => {
|
|
15
|
+
const preRendered = '<span class="math-inline"><math><mi>x</mi></math></span> and *italic*';
|
|
16
|
+
expect(renderMath(preRendered)).toBe(
|
|
17
|
+
'<span class="math-inline"><math><mi>x</mi></math></span> and <em>italic</em>',
|
|
18
|
+
);
|
|
38
19
|
});
|
|
39
20
|
|
|
40
|
-
it('
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
expect(
|
|
21
|
+
it('converts *text* to <em> (italic) for non-pre-rendered content', () => {
|
|
22
|
+
expect(renderMath('some *italic* text')).toBe('some <em>italic</em> text');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('converts ~text~ to <sub> (subscript)', () => {
|
|
26
|
+
expect(renderMath('H~2~O')).toBe('H<sub>2</sub>O');
|
|
46
27
|
});
|
|
47
28
|
|
|
48
29
|
it('converts bullet lines to <ul><li>', () => {
|
|
@@ -52,20 +33,6 @@ describe('renderMath', () => {
|
|
|
52
33
|
expect(result).toContain('<li>second item</li>');
|
|
53
34
|
});
|
|
54
35
|
|
|
55
|
-
it('does NOT convert *stem:[...] lines to list items', () => {
|
|
56
|
-
const result = renderMath('*stem:[x]*');
|
|
57
|
-
expect(result).not.toContain('<ul');
|
|
58
|
-
expect(result).toContain('math-bold');
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('converts *text* to <em> (italic)', () => {
|
|
62
|
-
expect(renderMath('some *italic* text')).toBe('some <em>italic</em> text');
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('converts ~text~ to <sub> (subscript)', () => {
|
|
66
|
-
expect(renderMath('H~2~O')).toBe('H<sub>2</sub>O');
|
|
67
|
-
});
|
|
68
|
-
|
|
69
36
|
it('resolves URN inline refs via xrefResolver', () => {
|
|
70
37
|
const resolver = (uri: string, term: string) => `[${term}→${uri}]`;
|
|
71
38
|
const result = renderMath(
|
|
@@ -117,6 +84,14 @@ describe('renderMath', () => {
|
|
|
117
84
|
expect(result).toBe('see some term');
|
|
118
85
|
});
|
|
119
86
|
|
|
87
|
+
it('resolves cross-refs even in pre-rendered content', () => {
|
|
88
|
+
const resolver = (uri: string, term: string) => `[${term}→${uri}]`;
|
|
89
|
+
const preRendered = '<span class="math-inline"><math><mi>x</mi></math></span> and {{urn:iso:std:iso:14812:3.1.1.1,entity}}';
|
|
90
|
+
expect(renderMath(preRendered, resolver)).toBe(
|
|
91
|
+
'<span class="math-inline"><math><mi>x</mi></math></span> and [entity→urn:iso:std:iso:14812:3.1.1.1]',
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
120
95
|
it('handles empty input', () => {
|
|
121
96
|
expect(renderMath('')).toBe('');
|
|
122
97
|
});
|
|
@@ -128,16 +103,9 @@ describe('renderMath', () => {
|
|
|
128
103
|
});
|
|
129
104
|
|
|
130
105
|
describe('cleanContent', () => {
|
|
131
|
-
it('strips
|
|
132
|
-
expect(cleanContent('value
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
it('strips bold stem', () => {
|
|
136
|
-
expect(cleanContent('value *stem:[x]* here')).toBe('value x here');
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it('strips latexmath:[...] with nested brackets', () => {
|
|
140
|
-
expect(cleanContent('coords latexmath:[[u_0, u_1] \\leq 1.0] end')).toBe('coords [u_0, u_1] \\leq 1.0 end');
|
|
106
|
+
it('strips pre-rendered HTML/MathML tags', () => {
|
|
107
|
+
expect(cleanContent('value <span class="math-inline"><math><mi>x</mi></math></span> here'))
|
|
108
|
+
.toBe('value x here');
|
|
141
109
|
});
|
|
142
110
|
|
|
143
111
|
it('strips *text* to plain text', () => {
|
package/src/style.css
CHANGED
|
@@ -158,16 +158,14 @@
|
|
|
158
158
|
font-family: var(--font-body);
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
/* Math
|
|
161
|
+
/* Math */
|
|
162
162
|
.math-inline {
|
|
163
163
|
display: inline;
|
|
164
164
|
}
|
|
165
165
|
.math-inline math {
|
|
166
166
|
font-size: 1.05em;
|
|
167
167
|
}
|
|
168
|
-
.math-bold math
|
|
169
|
-
.math-bold math mn,
|
|
170
|
-
.math-bold math mo {
|
|
168
|
+
.math-bold .math-inline math {
|
|
171
169
|
font-weight: bold;
|
|
172
170
|
}
|
|
173
171
|
.math-fallback {
|
package/src/utils/math.ts
CHANGED
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
import Plurimath from '@plurimath/plurimath';
|
|
2
|
-
|
|
3
|
-
type MathFormat = 'asciimath' | 'latex' | 'mathml' | 'html' | 'mahtml' | 'omml';
|
|
4
|
-
|
|
5
|
-
function renderMathSpan(math: string, format: MathFormat, bold: boolean): string {
|
|
6
|
-
try {
|
|
7
|
-
const p = new Plurimath(math, format);
|
|
8
|
-
const mathml = p.toMathml();
|
|
9
|
-
return `<span class="math-inline${bold ? ' math-bold' : ''}">${mathml}</span>`;
|
|
10
|
-
} catch {
|
|
11
|
-
return `<code class="math-fallback">${escapeHtml(math)}</code>`;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
1
|
export type XrefResolver = (uri: string, term: string) => string;
|
|
16
2
|
export type BibResolver = (refId: string, title: string) => string;
|
|
17
3
|
export type FigResolver = (figId: string) => string;
|
|
@@ -22,12 +8,7 @@ export interface RenderOptions {
|
|
|
22
8
|
figResolver?: FigResolver;
|
|
23
9
|
}
|
|
24
10
|
|
|
25
|
-
/**
|
|
26
|
-
* Convert `* item` lines into <ul><li> blocks.
|
|
27
|
-
* Also handles `1)` and `1.` numbered items into ordered lists.
|
|
28
|
-
*/
|
|
29
11
|
function convertLists(text: string): string {
|
|
30
|
-
// Unordered: * item (separated by \n or \n\n)
|
|
31
12
|
let result = text.replace(/(?:^|\n)((?:[ \t]*\* [^\n]+)(?:\n[ \t]*\* [^\n]+)*)/g, (_, block) => {
|
|
32
13
|
if (/^\*stem:\[/.test(block.trimStart())) return _;
|
|
33
14
|
const items: string[] = [];
|
|
@@ -41,7 +22,6 @@ function convertLists(text: string): string {
|
|
|
41
22
|
return `\n<ul class="concept-list">${lis}</ul>`;
|
|
42
23
|
});
|
|
43
24
|
|
|
44
|
-
// Ordered: 1) item or 1. item (numbered items)
|
|
45
25
|
result = result.replace(/(?:^|\n)((?:[ \t]*\d+[).][ \t]+[^\n]+)(?:\n[ \t]*\d+[).][ \t]+[^\n]+)*)/g, (_, block) => {
|
|
46
26
|
const items: string[] = [];
|
|
47
27
|
const re = /[ \t]*\d+[).][ \t]+([^\n]+)/g;
|
|
@@ -57,63 +37,6 @@ function convertLists(text: string): string {
|
|
|
57
37
|
return result;
|
|
58
38
|
}
|
|
59
39
|
|
|
60
|
-
/**
|
|
61
|
-
* Replace `prefix:[content]` where content may contain nested brackets.
|
|
62
|
-
* Handles `*prefix:[content]*` (bold) too.
|
|
63
|
-
*/
|
|
64
|
-
function replaceBracketed(
|
|
65
|
-
text: string,
|
|
66
|
-
prefix: string,
|
|
67
|
-
render: (math: string, bold: boolean) => string,
|
|
68
|
-
): string {
|
|
69
|
-
let result = '';
|
|
70
|
-
let i = 0;
|
|
71
|
-
const boldPrefix = '*' + prefix;
|
|
72
|
-
while (i < text.length) {
|
|
73
|
-
// Check for bold variant: *prefix:[...]
|
|
74
|
-
if (text.startsWith(boldPrefix + '[', i)) {
|
|
75
|
-
const start = i;
|
|
76
|
-
i += boldPrefix.length + 1; // skip *prefix:[
|
|
77
|
-
const depth = 1;
|
|
78
|
-
let j = i;
|
|
79
|
-
let d = 1;
|
|
80
|
-
while (j < text.length && d > 0) {
|
|
81
|
-
if (text[j] === '[') d++;
|
|
82
|
-
else if (text[j] === ']') d--;
|
|
83
|
-
j++;
|
|
84
|
-
}
|
|
85
|
-
const content = text.slice(i, j - 1);
|
|
86
|
-
// Check for closing *
|
|
87
|
-
let end = j;
|
|
88
|
-
if (end < text.length && text[end] === '*') end++;
|
|
89
|
-
result += render(content, true);
|
|
90
|
-
i = end;
|
|
91
|
-
}
|
|
92
|
-
// Check for normal variant: prefix:[...]
|
|
93
|
-
else if (text.startsWith(prefix + '[', i)) {
|
|
94
|
-
i += prefix.length + 1;
|
|
95
|
-
let j = i;
|
|
96
|
-
let d = 1;
|
|
97
|
-
while (j < text.length && d > 0) {
|
|
98
|
-
if (text[j] === '[') d++;
|
|
99
|
-
else if (text[j] === ']') d--;
|
|
100
|
-
j++;
|
|
101
|
-
}
|
|
102
|
-
const content = text.slice(i, j - 1);
|
|
103
|
-
result += render(content, false);
|
|
104
|
-
i = j;
|
|
105
|
-
} else {
|
|
106
|
-
result += text[i];
|
|
107
|
-
i++;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return result;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Render stem:[...] math notation to KaTeX HTML.
|
|
115
|
-
* Also handles cross-reference inline patterns (URN refs, bibliography, figures).
|
|
116
|
-
*/
|
|
117
40
|
export function renderMath(text: string, xrefResolverOrOpts?: XrefResolver | RenderOptions): string {
|
|
118
41
|
if (!text) return '';
|
|
119
42
|
let result = text;
|
|
@@ -122,15 +45,11 @@ export function renderMath(text: string, xrefResolverOrOpts?: XrefResolver | Ren
|
|
|
122
45
|
? { xrefResolver: xrefResolverOrOpts }
|
|
123
46
|
: (xrefResolverOrOpts ?? {});
|
|
124
47
|
|
|
125
|
-
|
|
126
|
-
result = replaceBracketed(result, 'latexmath:', (math, bold) => renderMathSpan(math, 'latex', bold));
|
|
127
|
-
|
|
48
|
+
// Math (stem/latexmath) is pre-rendered at build time. Only process text formatting.
|
|
128
49
|
result = convertLists(result);
|
|
129
|
-
|
|
130
50
|
result = result.replace(/\*([^*]+)\*/g, '<em>$1</em>');
|
|
131
51
|
result = result.replace(/~([^~]+)~/g, '<sub>$1</sub>');
|
|
132
52
|
|
|
133
|
-
// Handle AsciiDoc bibliography xrefs: <<ref_XX,title>>
|
|
134
53
|
result = result.replace(/<<([^,>]+),([^>]+)>>/g, (_, refId, title) => {
|
|
135
54
|
if (opts.bibResolver) {
|
|
136
55
|
return opts.bibResolver(refId.trim(), title.trim());
|
|
@@ -138,7 +57,6 @@ export function renderMath(text: string, xrefResolverOrOpts?: XrefResolver | Ren
|
|
|
138
57
|
return `<span class="bib-ref">${escapeHtml(title.trim())}</span>`;
|
|
139
58
|
});
|
|
140
59
|
|
|
141
|
-
// Handle AsciiDoc figure xrefs: <<fig_XX>>
|
|
142
60
|
result = result.replace(/<<(fig_[^>]+)>>/g, (_, figId) => {
|
|
143
61
|
if (opts.figResolver) {
|
|
144
62
|
return opts.figResolver(figId.trim());
|
|
@@ -146,25 +64,22 @@ export function renderMath(text: string, xrefResolverOrOpts?: XrefResolver | Ren
|
|
|
146
64
|
return `<span class="fig-ref">${escapeHtml(figId.trim())}</span>`;
|
|
147
65
|
});
|
|
148
66
|
|
|
149
|
-
// Handle URN inline refs: {{urn:...,term[,displayText]}} (double-braced)
|
|
150
67
|
result = result.replace(/\{\{(urn:[^,}]+),([^,}]+)(?:,([^}]+))?\}\}/g, (_, uri, term, display) => {
|
|
151
|
-
const
|
|
68
|
+
const t = (display || term).trim();
|
|
152
69
|
if (opts.xrefResolver) {
|
|
153
|
-
return opts.xrefResolver(uri,
|
|
70
|
+
return opts.xrefResolver(uri, t);
|
|
154
71
|
}
|
|
155
|
-
return
|
|
72
|
+
return t;
|
|
156
73
|
});
|
|
157
74
|
|
|
158
|
-
// Handle URN inline refs: {urn:...,term[,displayText]} (single-braced)
|
|
159
75
|
result = result.replace(/\{(urn:[^,}]+),([^,}]+)(?:,([^}]+))?\}/g, (_, uri, term, display) => {
|
|
160
|
-
const
|
|
76
|
+
const t = (display || term).trim();
|
|
161
77
|
if (opts.xrefResolver) {
|
|
162
|
-
return opts.xrefResolver(uri,
|
|
78
|
+
return opts.xrefResolver(uri, t);
|
|
163
79
|
}
|
|
164
|
-
return
|
|
80
|
+
return t;
|
|
165
81
|
});
|
|
166
82
|
|
|
167
|
-
// Handle any remaining {{...}} refs (fallback: show term before comma)
|
|
168
83
|
result = result.replace(/\{\{([^,}]+)(?:,\s*[^}]+)?\}\}/g, '$1');
|
|
169
84
|
|
|
170
85
|
return result;
|
|
@@ -177,12 +92,10 @@ function escapeHtml(text: string): string {
|
|
|
177
92
|
.replace(/>/g, '>');
|
|
178
93
|
}
|
|
179
94
|
|
|
180
|
-
/**
|
|
181
|
-
* Clean content for plain text display (no math rendering).
|
|
182
|
-
*/
|
|
183
95
|
export function cleanContent(text: string): string {
|
|
184
96
|
if (!text) return '';
|
|
185
97
|
let result = text
|
|
98
|
+
.replace(/<[^>]+>/g, '') // strip pre-rendered HTML/MathML
|
|
186
99
|
.replace(/\*([^*]+)\*/g, '$1')
|
|
187
100
|
.replace(/~([^~]+)~/g, '_$1')
|
|
188
101
|
.replace(/\n[ \t]*\* /g, '; ')
|
|
@@ -191,7 +104,5 @@ export function cleanContent(text: string): string {
|
|
|
191
104
|
.replace(/\{\{urn:[^,}]+,([^,}]+)(?:,[^}]+)?\}\}/g, '$1')
|
|
192
105
|
.replace(/\{urn:[^,}]+,([^,}]+)(?:,[^}]+)?\}/g, '$1')
|
|
193
106
|
.replace(/\{\{([^,}]+)(?:,\s*[^}]+)?\}\}/g, '$1');
|
|
194
|
-
result = replaceBracketed(result, 'stem:', (math) => math);
|
|
195
|
-
result = replaceBracketed(result, 'latexmath:', (math) => math);
|
|
196
107
|
return result;
|
|
197
108
|
}
|