rapier-math 1.0.1 → 1.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 +187 -85
- package/adoption-check.mjs +149 -0
- package/check.mjs +21 -0
- package/classic.d.ts +4 -0
- package/delimiters.d.ts +8 -0
- package/delimiters.mjs +39 -0
- package/examples/README.md +25 -0
- package/examples/browser.html +10 -0
- package/examples/browser.mjs +24 -0
- package/examples/formula.mjs +12 -0
- package/examples/markdown.mjs +9 -0
- package/examples/react.tsx +19 -0
- package/examples/stream.mjs +19 -0
- package/examples/unified.mjs +26 -0
- package/examples/worker.mjs +7 -0
- package/index.d.ts +43 -0
- package/index.mjs +205 -108
- package/integration.d.ts +9 -0
- package/integration.mjs +28 -0
- package/markdown-it.d.ts +6 -0
- package/markdown-it.mjs +45 -0
- package/markdown-rules.mjs +65 -0
- package/math-components.json +6 -6
- package/package.json +78 -5
- package/rapier-math-1.1.0.js +2980 -0
- package/rehype.d.ts +6 -0
- package/rehype.mjs +37 -0
- package/src/math/FONT-SOURCE.md +32 -6
- package/src/math/README.md +210 -0
- package/src/math/adoption-verify.mjs +149 -0
- package/src/math/api.js +37 -14
- package/src/math/browser.js +67 -30
- package/src/math/build-font.py +237 -95
- package/src/math/classic.d.ts +4 -0
- package/src/math/delimiters.d.ts +8 -0
- package/src/math/delimiters.mjs +39 -0
- package/src/math/examples/README.md +25 -0
- package/src/math/examples/browser.html +10 -0
- package/src/math/examples/browser.mjs +24 -0
- package/src/math/examples/formula.mjs +12 -0
- package/src/math/examples/markdown.mjs +9 -0
- package/src/math/examples/react.tsx +19 -0
- package/src/math/examples/stream.mjs +19 -0
- package/src/math/examples/unified.mjs +26 -0
- package/src/math/examples/worker.mjs +7 -0
- package/src/math/fallbacks.js +5 -5
- package/src/math/font.js +59 -26
- package/src/math/index.d.ts +43 -0
- package/src/math/integration.d.ts +9 -0
- package/src/math/integration.mjs +28 -0
- package/src/math/layout.js +37 -33
- package/src/math/markdown-it.d.ts +6 -0
- package/src/math/markdown-it.mjs +45 -0
- package/src/math/markdown-rules.mjs +65 -0
- package/src/math/package-build.mjs +4 -4
- package/src/math/package-check.mjs +118 -0
- package/src/math/package-readme.mjs +8 -0
- package/src/math/package-verify.mjs +7 -2
- package/src/math/rehype.d.ts +6 -0
- package/src/math/rehype.mjs +37 -0
- package/src/math/stream.d.ts +14 -0
- package/src/math/stream.mjs +46 -0
- package/src/package.json +1 -1
- package/src/shell/math-resources.json +184 -25
- package/src/tools/build-math.mjs +23 -4
- package/stream.d.ts +14 -0
- package/stream.mjs +46 -0
- package/verify.mjs +7 -2
- package/rapier-math-1.0.1.js +0 -2883
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
import {render, observe} from '../index.mjs';
|
|
3
|
+
const container = document.querySelector('#equation');
|
|
4
|
+
const source = document.querySelector('#source');
|
|
5
|
+
let stop = () => {};
|
|
6
|
+
function update() {
|
|
7
|
+
stop();
|
|
8
|
+
try {
|
|
9
|
+
const result = render(source.value, {displayMode: true});
|
|
10
|
+
document.querySelector('#errors').textContent = JSON.stringify(result.errors);
|
|
11
|
+
if (result.errors.length) container.replaceChildren(document.createTextNode(source.value));
|
|
12
|
+
else {
|
|
13
|
+
// Only this renderer's freshly returned SVG reaches the HTML sink.
|
|
14
|
+
container.innerHTML = result.svg;
|
|
15
|
+
stop = observe(container);
|
|
16
|
+
}
|
|
17
|
+
} catch (error) {
|
|
18
|
+
container.replaceChildren(document.createTextNode(source.value));
|
|
19
|
+
document.querySelector('#errors').textContent = error.message;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
document.querySelector('#render').addEventListener('click', update);
|
|
23
|
+
window.addEventListener('pagehide', () => stop());
|
|
24
|
+
update();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
import {render, renderToString} from 'rapier-math';
|
|
3
|
+
|
|
4
|
+
// Works in Node/SSR: the result needs no browser JavaScript or remote font.
|
|
5
|
+
const source = String.raw`\frac{-b\pm\sqrt{b^2-4ac}}{2a}`;
|
|
6
|
+
const result = render(source, {displayMode: true});
|
|
7
|
+
if (result.errors.length) throw new Error(JSON.stringify(result.errors));
|
|
8
|
+
console.log(result.svg);
|
|
9
|
+
console.log(renderToString('E=mc^2'));
|
|
10
|
+
const failure = render(String.raw`\unsupported{x}`);
|
|
11
|
+
console.error({source: String.raw`\unsupported{x}`, errors: failure.errors});
|
|
12
|
+
// Retain the original source for editing/copying, even if admission throws.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
import MarkdownIt from 'markdown-it';
|
|
3
|
+
import rapierMath from 'rapier-math/markdown-it';
|
|
4
|
+
|
|
5
|
+
export const md = new MarkdownIt({html: false}).use(rapierMath);
|
|
6
|
+
const source = 'It costs $5 and $10. Code: `$$x$$`. Math: \\(x^2\\).\n\n$$\\frac{1}{2}$$\n';
|
|
7
|
+
const env = {};
|
|
8
|
+
console.log(md.render(source, env));
|
|
9
|
+
console.error(env.rapierMathErrors || []);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
import {useEffect, useMemo, useRef} from 'react';
|
|
3
|
+
import {render, observe} from 'rapier-math';
|
|
4
|
+
|
|
5
|
+
export function Equation({source, displayMode = false}: {source: string; displayMode?: boolean}) {
|
|
6
|
+
const ref = useRef<HTMLSpanElement>(null);
|
|
7
|
+
const result = useMemo(() => {
|
|
8
|
+
try { return render(source, {displayMode}); }
|
|
9
|
+
catch { return null; }
|
|
10
|
+
}, [source, displayMode]);
|
|
11
|
+
const svg = result && result.errors.length === 0 ? result.svg : null;
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (ref.current && svg) return observe(ref.current);
|
|
14
|
+
}, [svg]); // Cleanup runs before replacement setup and on unmount.
|
|
15
|
+
if (!svg) return <code>{source}</code>;
|
|
16
|
+
// The server and initial client render use identical source/options. Fit only
|
|
17
|
+
// after hydration; never pass authored HTML to dangerouslySetInnerHTML.
|
|
18
|
+
return <span ref={ref} dangerouslySetInnerHTML={{__html: svg}} />;
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
import MarkdownIt from 'markdown-it';
|
|
3
|
+
import rapierMath from 'rapier-math/markdown-it';
|
|
4
|
+
import {createMarkdownStream} from 'rapier-math/stream';
|
|
5
|
+
|
|
6
|
+
const md = new MarkdownIt({html: false}).use(rapierMath);
|
|
7
|
+
const stream = createMarkdownStream(source => md.render(source, {}), {previewEvery: 16});
|
|
8
|
+
for (const chunk of ['The answer is \\(', '\\frac{1}', '{2}', '\\)', '. Code: `$$x$$`.']) {
|
|
9
|
+
const preview = stream.write(chunk);
|
|
10
|
+
if (preview) console.log({phase: 'preview', ...preview});
|
|
11
|
+
}
|
|
12
|
+
const final = stream.finish();
|
|
13
|
+
console.log({phase: 'complete', ...final});
|
|
14
|
+
if (final.html !== md.render(final.source, {})) throw new Error('Batch mismatch');
|
|
15
|
+
|
|
16
|
+
// One stream per message. On replacement: cancel its transport, reset, write.
|
|
17
|
+
stream.reset();
|
|
18
|
+
stream.write('Replacement \\(y\\).');
|
|
19
|
+
console.log(stream.finish());
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
import {unified} from 'unified';
|
|
3
|
+
import remarkParse from 'remark-parse';
|
|
4
|
+
import remarkMath from 'remark-math';
|
|
5
|
+
import remarkRehype from 'remark-rehype';
|
|
6
|
+
import rehypeSanitize, {defaultSchema} from 'rehype-sanitize';
|
|
7
|
+
import rehypeRapierMath from 'rapier-math/rehype';
|
|
8
|
+
import rehypeStringify from 'rehype-stringify';
|
|
9
|
+
import {pathToFileURL} from 'node:url';
|
|
10
|
+
|
|
11
|
+
export const processor = unified().use(remarkParse)
|
|
12
|
+
.use(remarkMath, {singleDollarTextMath: false}).use(remarkRehype)
|
|
13
|
+
.use(rehypeSanitize, {
|
|
14
|
+
...defaultSchema,
|
|
15
|
+
attributes: {...defaultSchema.attributes, code: [
|
|
16
|
+
...(defaultSchema.attributes.code || []).filter(rule => !(Array.isArray(rule) && rule[0] === 'className')),
|
|
17
|
+
['className', 'language-math', 'math-inline', 'math-display'],
|
|
18
|
+
]},
|
|
19
|
+
})
|
|
20
|
+
.use(rehypeRapierMath).use(rehypeStringify);
|
|
21
|
+
|
|
22
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
23
|
+
const file = await processor.process('Price $5. Code `$$x$$`. Math $$x^2$$.\n\n$$\n\\frac{1}{2}\n$$');
|
|
24
|
+
console.log(String(file));
|
|
25
|
+
console.error(file.messages);
|
|
26
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
import {render} from '../index.mjs';
|
|
3
|
+
self.onmessage = ({data: {id, source}}) => {
|
|
4
|
+
try { self.postMessage({id, source, result: render(source)}); }
|
|
5
|
+
catch (error) { self.postMessage({id, source, error: {code: error.code, message: error.message}}); }
|
|
6
|
+
};
|
|
7
|
+
// Keep a request id per message; install a reply only if it is still current.
|
package/src/math/fallbacks.js
CHANGED
|
@@ -14,7 +14,7 @@ function mathFallbackGlyph(value,c,extra={}) {
|
|
|
14
14
|
{left:-radius,right:width*unit+radius,inkTop:-high*unit-radius,inkBottom:-low*unit+radius,...extra});
|
|
15
15
|
};
|
|
16
16
|
if(value==='ϝ') {
|
|
17
|
-
const b=draw('𝑓'),g=MATH_FONT.
|
|
17
|
+
const b=draw('𝑓'),g=MATH_FONT.glyph(b.glyph),path=g[7],bars=[...path.matchAll(/H(\d+)/g)],
|
|
18
18
|
start=bars[1],end=bars[2],cut=Number(path.slice(0,start.index).match(/-?\d+/g).at(-1)),
|
|
19
19
|
left=Number(start[1]),right=Number(path.slice(start.index,end.index).match(/-?\d+/g).at(-2)),
|
|
20
20
|
body='M'+left+' '+cut+path.slice(start.index+start[0].length,end.index)+'Z',unit=b.unitScale,
|
|
@@ -39,7 +39,7 @@ function mathFallbackGlyph(value,c,extra={}) {
|
|
|
39
39
|
return centered(draw('⋄',factor));
|
|
40
40
|
}
|
|
41
41
|
if(value==='◆') {
|
|
42
|
-
const b=draw('◊'),path=MATH_FONT.
|
|
42
|
+
const b=draw('◊'),path=MATH_FONT.glyph(b.glyph)[7],contours=path.match(/M[^M]+/g);
|
|
43
43
|
const range=part=>{const numbers=part.match(/-?\d+(?:\.\d+)?/g).map(Number);return Math.max(...numbers)-Math.min(...numbers);};
|
|
44
44
|
const outer=contours.reduce((a,b)=>range(a)>range(b)?a:b);
|
|
45
45
|
return {...b,svg:b.svg.replace('d="'+path+'"','d="'+outer+'"'),glyph:undefined,...extra};
|
|
@@ -51,7 +51,7 @@ function mathFallbackGlyph(value,c,extra={}) {
|
|
|
51
51
|
return contour(800,-75-t,575,'M'+points.map(point=>point.map(mathNumber).join(' ')).join('L')+'Z');
|
|
52
52
|
}
|
|
53
53
|
if(value==='⋔') {
|
|
54
|
-
const b=draw('∩',.86),g=MATH_FONT.
|
|
54
|
+
const b=draw('∩',.86),g=MATH_FONT.glyph(b.glyph),x=(g[1]+g[3])/2,t=MATH_FONT.constants.FractionRuleThickness,
|
|
55
55
|
stem=contour(g[0],g[2],g[4]+100,'M'+mathNumber(x-t/2)+' '+g[2]+'V'+(g[4]+100)+'H'+mathNumber(x+t/2)+'V'+g[2]+'Z',.86);
|
|
56
56
|
return centered(mathCompose([[b,0,0],[stem,0,0]],b.w));
|
|
57
57
|
}
|
|
@@ -72,7 +72,7 @@ function mathFallbackGlyph(value,c,extra={}) {
|
|
|
72
72
|
return mathCompose(items,width,extra);
|
|
73
73
|
}
|
|
74
74
|
if(value==='⇠'||value==='⇢') {
|
|
75
|
-
const right=value==='⇢',b=draw(right?'→':'←'),g=MATH_FONT.
|
|
75
|
+
const right=value==='⇢',b=draw(right?'→':'←'),g=MATH_FONT.glyph(b.glyph),unit=b.unitScale,
|
|
76
76
|
width=Math.max(b.w,extra.stretchAxis==='horizontal'?extra.stretchTarget || 0:0),added=width-b.w,
|
|
77
77
|
split=g[0]*(right?.65:.35);let command='',coordinate=0;
|
|
78
78
|
const path=g[7].replace(/[MLHVCZ]|-?\d+(?:\.\d+)?/g,token=>{
|
|
@@ -100,7 +100,7 @@ function mathFallbackGlyph(value,c,extra={}) {
|
|
|
100
100
|
total=Math.max(target,top.h+bottom.h),gap=Math.max(0,total-top.h-bottom.h),width=top.w,
|
|
101
101
|
ty=-axis-total/2-top.inkTop,by=ty+top.inkBottom+gap-bottom.inkTop,items=[[top,0,ty],[bottom,0,by]];
|
|
102
102
|
if(gap>0) {
|
|
103
|
-
const part=MATH_FONT.
|
|
103
|
+
const part=MATH_FONT.glyph(MATH_FONT.chars['⎪'.codePointAt(0)]),unit=s*factor/MATH_FONT.units,
|
|
104
104
|
overlap=Math.max(MATH_FONT.minConnectorOverlap,40),height=gap/unit+2*overlap,
|
|
105
105
|
stem=contour(part[0],0,height,'M'+part[1]+' 0V'+mathNumber(height)+'H'+part[3]+'V0Z',factor);
|
|
106
106
|
items.push([stem,0,ty+top.inkBottom+gap+overlap*unit]);
|