fix: a bracket typed without \left keeps its glyph height

The MathML operator dictionary makes ( [ { | stretchy, so a plain bracket grew
to the tallest thing in its row — next to a sum with a fraction, C(b) came out
in full row height. Only a fence node emits stretchy brackets now.
This commit is contained in:
Steffen Becker committed 2026-09-06 21:19:09 +02:00
1 parent 855cc24371
commit d8be23cf68
10 files changed
+7 -1

No files matched your search

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+5 -1
View File
@@ -13,6 +13,8 @@ const esc = (s: string) =>
const TAG_BY_KIND: Record<TxtKind, string> = { i: 'mi', n: 'mn', o: 'mo', f: 'mi' };
const BRACKETS = /^[([{|‖⟨⌊⌈)\]}⟩⌋⌉]$/;
// A row that MathML needs as a single element (mfrac, msup, … each take exactly one
// child per slot), so a multi-node row is wrapped in <mrow>.
function slot(n: MathNode): string {
@@ -27,7 +29,9 @@ export function astToMathml(n: MathNode): string {
if (n.kind === 'f') return `<mi mathvariant="normal">${esc(n.s)}</mi>`;
// Multi-letter identifiers stay upright in MathML unless split, and a number
// like 3.14 is one token — so emit the run whole and let the class pick the tag.
return `<${TAG_BY_KIND[n.kind]}>${esc(n.s)}</${TAG_BY_KIND[n.kind]}>`;
// A bracket typed without \left stays glyph-height; only a fence stretches.
const attr = n.kind === 'o' && BRACKETS.test(n.s) ? ' stretchy="false"' : '';
return `<${TAG_BY_KIND[n.kind]}${attr}>${esc(n.s)}</${TAG_BY_KIND[n.kind]}>`;
}
case 'frac': return `<mfrac>${slot(n.num)}${slot(n.den)}</mfrac>`;
case 'script': {
+2
View File
@@ -54,6 +54,8 @@ describe('MathML', () => {
expect(astToMathml(parseLatex('\\int_{0}^{1} x'))).toContain('<msubsup>');
// Brackets must be stretchy or they stay glyph-height next to a fraction.
expect(astToMathml(parseLatex('\\left(x\\right)'))).toContain('<mo stretchy="true">(</mo>');
// A plain bracket must not: the dictionary would stretch it to the row's tallest.
expect(astToMathml(parseLatex('C(b) = \\frac{1}{2}'))).toContain('<mo stretchy="false">(</mo>');
});
it('round-trips through the document element', () => {