From 969ada97cb14ae6a59b97bfc4d83ead3e293e916 Mon Sep 17 00:00:00 2001 From: "Davide P. Cervone" Date: Tue, 9 Jun 2026 07:46:40 -0400 Subject: [PATCH 01/11] Fix problem with \limits and \nolimits when used after a script. (mathjax/MathJax#3569) --- ts/input/tex/base/BaseMethods.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ts/input/tex/base/BaseMethods.ts b/ts/input/tex/base/BaseMethods.ts index dc371a34d..f993da940 100644 --- a/ts/input/tex/base/BaseMethods.ts +++ b/ts/input/tex/base/BaseMethods.ts @@ -645,14 +645,14 @@ const BaseMethods: { [key: string]: ParseMethod } = { // @test Limits UnderOver node = parser.create('node', 'msubsup'); NodeUtil.copyChildren(op, node); - op = top.Last = node; + op = top.First = node; } else if (NodeUtil.isType(op, 'msubsup') && limits) { // @test Limits SubSup // node = parser.create('node', 'munderover', NodeUtil.getChildren(op), {}); // Needs to be copied, otherwise we get an error in MmlNode.appendChild! node = parser.create('node', 'munderover'); NodeUtil.copyChildren(op, node); - op = top.Last = node; + op = top.First = node; } NodeUtil.setProperty(op, 'movesupsub', limits ? true : false); NodeUtil.setProperties(NodeUtil.getCoreMO(op), { movablelimits: false }); From e72921f6b9174dc90d272dcd4d06fbebb5ea3bf2 Mon Sep 17 00:00:00 2001 From: "Davide P. Cervone" Date: Sat, 16 May 2026 07:09:50 -0400 Subject: [PATCH 02/11] Add symmetric="true" to \middle results. (mathjax/MathJax#3560) --- ts/input/tex/base/BaseItems.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/input/tex/base/BaseItems.ts b/ts/input/tex/base/BaseItems.ts index 4704a6b57..a2eb4d66b 100644 --- a/ts/input/tex/base/BaseItems.ts +++ b/ts/input/tex/base/BaseItems.ts @@ -432,7 +432,7 @@ export class LeftItem extends BaseItem { // // Add the middle delimiter, with empty open and close elements around it for spacing // - const def = { stretchy: true } as any; + const def = { stretchy: true, symmetric: true } as any; if (item.getProperty('color')) { def.mathcolor = item.getProperty('color'); } From 139fe609e8360425b722b44167a8a0189ad79d53 Mon Sep 17 00:00:00 2001 From: "Davide P. Cervone" Date: Tue, 9 Jun 2026 07:47:27 -0400 Subject: [PATCH 03/11] Don't rerender the output during menu initialization. --- ts/ui/menu/Menu.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ts/ui/menu/Menu.ts b/ts/ui/menu/Menu.ts index ebee11f7b..78b100f31 100644 --- a/ts/ui/menu/Menu.ts +++ b/ts/ui/menu/Menu.ts @@ -250,6 +250,8 @@ export class Menu { */ protected document: MenuMathDocument; + protected initialized: boolean = false; + /** * Instances of the various output jax that we can switch to */ @@ -566,6 +568,7 @@ export class Menu { this.mergeUserSettings(); this.initMenu(); this.applySettings(); + this.initialized = true; } /** @@ -1108,7 +1111,7 @@ export class Menu { */ protected setOverflow(overflow: string) { this.document.outputJax.options.displayOverflow = overflow.toLowerCase(); - if (!Menu.loading) { + if (!Menu.loading && this.initialized) { this.document.rerenderPromise(); } } @@ -1118,7 +1121,7 @@ export class Menu { */ protected setInlineBreaks(breaks: boolean) { this.document.outputJax.options.linebreaks.inline = breaks; - if (!Menu.loading) { + if (!Menu.loading && this.initialized) { this.document.rerenderPromise(); } } @@ -1128,7 +1131,7 @@ export class Menu { */ protected setScale(scale: string) { this.document.outputJax.options.scale = parseFloat(scale); - if (!Menu.loading) { + if (!Menu.loading && this.initialized) { this.document.rerenderPromise(); } } From 7e7fa55694e00daefbde857db8ea78e8746bc6fe Mon Sep 17 00:00:00 2001 From: "Davide P. Cervone" Date: Thu, 11 Jun 2026 17:09:02 -0400 Subject: [PATCH 04/11] Make sure the parsers array is cleared for tex and text parsers. (#1474) --- ts/input/tex.ts | 1 + ts/input/tex/Configuration.ts | 2 +- ts/input/tex/TexParser.ts | 2 +- ts/input/tex/textmacros/TextMacrosConfiguration.ts | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ts/input/tex.ts b/ts/input/tex.ts index 91730585c..205ecd4ff 100644 --- a/ts/input/tex.ts +++ b/ts/input/tex.ts @@ -187,6 +187,7 @@ export class TeX extends AbstractInputJax { * @override */ public reset(tag: number = 0) { + this.parseOptions.clear(); this.parseOptions.tags.reset(tag); } diff --git a/ts/input/tex/Configuration.ts b/ts/input/tex/Configuration.ts index 6306dafe2..038680623 100644 --- a/ts/input/tex/Configuration.ts +++ b/ts/input/tex/Configuration.ts @@ -159,7 +159,7 @@ export class Configuration { } /** - * Creates an unnamed, ephemeral package configuration. It will not added to + * Creates an unnamed, ephemeral package configuration. It is not added to * the configuration handler. * * @param {object} config See `create` method. diff --git a/ts/input/tex/TexParser.ts b/ts/input/tex/TexParser.ts index 8e4b1603b..119d8cced 100644 --- a/ts/input/tex/TexParser.ts +++ b/ts/input/tex/TexParser.ts @@ -248,11 +248,11 @@ export default class TexParser { * @returns {MmlNode} The internal Mathml structure. */ public mml(): MmlNode { + this.configuration.popParser(); if (!this.stack.Top().isKind('mml')) { return null; } const node = this.stack.Top().First; - this.configuration.popParser(); const latex = this.trimTex(this.string); if (latex) { node.attributes.set(TexConstant.Attr.LATEX, latex); diff --git a/ts/input/tex/textmacros/TextMacrosConfiguration.ts b/ts/input/tex/textmacros/TextMacrosConfiguration.ts index 981362af7..462c41609 100644 --- a/ts/input/tex/textmacros/TextMacrosConfiguration.ts +++ b/ts/input/tex/textmacros/TextMacrosConfiguration.ts @@ -160,6 +160,7 @@ export const TextMacrosConfiguration = Configuration.create('textmacros', { // const config = data.data.packageData.get('textmacros'); config.parseOptions.nodeFactory.setMmlFactory(config.jax.mmlFactory); + config.parseOptions.clear(); }, ], [ConfigurationType.OPTIONS]: { From e69ef94ac9106ae1df5903dc6e4279883a147c17 Mon Sep 17 00:00:00 2001 From: "Davide P. Cervone" Date: Thu, 11 Jun 2026 17:23:41 -0400 Subject: [PATCH 05/11] Fix `\bbox` problem with padding values that are decimals. (mathjax/MathJax#3568) --- ts/input/tex/bbox/BboxConfiguration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/input/tex/bbox/BboxConfiguration.ts b/ts/input/tex/bbox/BboxConfiguration.ts index 37838302e..02c7785b6 100644 --- a/ts/input/tex/bbox/BboxConfiguration.ts +++ b/ts/input/tex/bbox/BboxConfiguration.ts @@ -64,7 +64,7 @@ const BboxMethods: { [key: string]: ParseMethod } = { height: '+' + pad, depth: '+' + pad, lspace: pad, - width: '+' + 2 * parseInt(match[1], 10) + match[3], + width: '+' + 2 * parseFloat(match[1]) + match[3], }; } } else if (part.match(/^([a-z0-9]+|#[0-9a-f]{6}|#[0-9a-f]{3})$/i)) { From 68caffca88e7d7fc9b855f04b68315f3a412cca6 Mon Sep 17 00:00:00 2001 From: "Davide P. Cervone" Date: Sat, 13 Jun 2026 16:23:33 -0400 Subject: [PATCH 06/11] Make sure multi-byte UTF8 characters are handled properly in data-latex attributes. (mathjax/MathJax#3575) --- ts/input/tex/TexParser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/input/tex/TexParser.ts b/ts/input/tex/TexParser.ts index 8e4b1603b..67136cba5 100644 --- a/ts/input/tex/TexParser.ts +++ b/ts/input/tex/TexParser.ts @@ -151,7 +151,7 @@ export default class TexParser { // // Back up one to pick up the parsed character (this.i is past it at this point). // - this.saveI = this.i - (kind === 'character' && input[1] !== '&' ? 1 : 0); + this.saveI = this.i - (kind === 'character' && input[1] !== '&' ? input[1].length : 0); const result = this.configuration.handlers.get(kind).parse(input); // // The macro gets processed by the \\ character later From a88c6e769946d4975d30aaa96a2edcea7a17140f Mon Sep 17 00:00:00 2001 From: "Davide P. Cervone" Date: Sat, 13 Jun 2026 19:48:35 -0400 Subject: [PATCH 07/11] Set box-sizing in dialogs to avoid CSS bleed through that affects close and help buttons. --- ts/ui/dialog/DraggableDialog.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ts/ui/dialog/DraggableDialog.ts b/ts/ui/dialog/DraggableDialog.ts index 901b14b72..75310766f 100644 --- a/ts/ui/dialog/DraggableDialog.ts +++ b/ts/ui/dialog/DraggableDialog.ts @@ -210,6 +210,9 @@ export class DraggableDialog { position: 'fixed', top: '-4%', }, + '.mjx-dialog *': { + 'box-sizing': 'content-box', + }, '.mjx-dialog.mjx-moving': { cursor: 'grabbing', }, From ecfb01b8d48275081b158f884c109cbf03cb9fc6 Mon Sep 17 00:00:00 2001 From: "Davide P. Cervone" Date: Sun, 14 Jun 2026 10:34:33 -0400 Subject: [PATCH 08/11] Fix label ids for expressions with forward refs. (mathjax/MathJax#3562) --- ts/input/tex/base/BaseMethods.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ts/input/tex/base/BaseMethods.ts b/ts/input/tex/base/BaseMethods.ts index f993da940..4fcf2badb 100644 --- a/ts/input/tex/base/BaseMethods.ts +++ b/ts/input/tex/base/BaseMethods.ts @@ -2177,13 +2177,13 @@ const BaseMethods: { [key: string]: ParseMethod } = { // @test Label Empty return; } + // @test Label, Ref, Ref Unknown + if (parser.tags.label) { + // @test Double Label Error + throw new TexError('MultipleCommand', 'Multiple %1', parser.currentCS); + } + parser.tags.label = label; if (!parser.tags.refUpdate) { - // @test Label, Ref, Ref Unknown - if (parser.tags.label) { - // @test Double Label Error - throw new TexError('MultipleCommand', 'Multiple %1', parser.currentCS); - } - parser.tags.label = label; if ( (parser.tags.allLabels[label] || parser.tags.labels[label]) && !parser.options['ignoreDuplicateLabels'] From f8a3f94bfbb5723a8311bd93b46a327e682b39c1 Mon Sep 17 00:00:00 2001 From: "Davide P. Cervone" Date: Sun, 14 Jun 2026 11:03:55 -0400 Subject: [PATCH 09/11] Update tests --- .../input/tex/__snapshots__/Base.test.ts.snap | 12 +-- .../tex/__snapshots__/Physics.test.ts.snap | 88 +++++++++---------- 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/testsuite/tests/input/tex/__snapshots__/Base.test.ts.snap b/testsuite/tests/input/tex/__snapshots__/Base.test.ts.snap index bc59e9aa3..314cd73b8 100644 --- a/testsuite/tests/input/tex/__snapshots__/Base.test.ts.snap +++ b/testsuite/tests/input/tex/__snapshots__/Base.test.ts.snap @@ -3552,7 +3552,7 @@ exports[`Fenced Fenced Arrows 3 1`] = ` b - + c @@ -3660,7 +3660,7 @@ exports[`Fenced Middle 1`] = ` ( a - | + | b ) @@ -7931,7 +7931,7 @@ exports[`Spacing Nonscript scriptlevel 1`] = ` a - | + | b @@ -7948,7 +7948,7 @@ exports[`Spacing Nonscript toplevel 1`] = ` a - | + | b @@ -9156,7 +9156,7 @@ exports[`User Defined Macros Middle Color 1`] = ` A - | + | B ) @@ -9170,7 +9170,7 @@ exports[`User Defined Macros Right Color 1`] = ` ( A - | + | B diff --git a/testsuite/tests/input/tex/__snapshots__/Physics.test.ts.snap b/testsuite/tests/input/tex/__snapshots__/Physics.test.ts.snap index 218039580..fb892c287 100644 --- a/testsuite/tests/input/tex/__snapshots__/Physics.test.ts.snap +++ b/testsuite/tests/input/tex/__snapshots__/Physics.test.ts.snap @@ -150,7 +150,7 @@ exports[`Bra-Ket Macros Rest innerproduct arg one 1`] = ` A - | + | A @@ -168,7 +168,7 @@ exports[`Bra-Ket Macros Rest innerproduct arg three 1`] = ` A - | + | B @@ -189,7 +189,7 @@ exports[`Bra-Ket Macros Rest innerproduct arg two 1`] = ` A - | + | B @@ -207,7 +207,7 @@ exports[`Bra-Ket Macros Rest ip arg one 1`] = ` A - | + | A @@ -225,7 +225,7 @@ exports[`Bra-Ket Macros Rest ip arg three 1`] = ` A - | + | B @@ -246,7 +246,7 @@ exports[`Bra-Ket Macros Rest ip arg two 1`] = ` A - | + | B @@ -4956,7 +4956,7 @@ exports[`Physics5_0 Derivatives_Deriv_9 1`] = ` f - / + / d @@ -4993,7 +4993,7 @@ exports[`Physics5_0 Derivatives_Deriv_10 1`] = ` f - / + / d @@ -5129,7 +5129,7 @@ exports[`Physics5_1 Derivatives_Partial_0 1`] = ` x - / + / y @@ -5146,7 +5146,7 @@ exports[`Physics5_1 Derivatives_Partial_1 1`] = ` 2 - / + / y @@ -5294,7 +5294,7 @@ exports[`Physics5_1 Derivatives_Partial_10 1`] = ` f - / + / x @@ -5315,7 +5315,7 @@ exports[`Physics5_1 Derivatives_Partial_11 1`] = ` f - / + / @@ -5393,7 +5393,7 @@ exports[`Physics5_1 Derivatives_Partial_14 1`] = ` f - / + / x @@ -5416,7 +5416,7 @@ exports[`Physics5_1 Derivatives_Partial_15 1`] = ` f - / + / x @@ -5634,7 +5634,7 @@ exports[`Physics5_2 Derivatives_Functional_8 1`] = ` δ F - / + / δ x @@ -5650,7 +5650,7 @@ exports[`Physics5_2 Derivatives_Functional_9 1`] = ` δ F - / + / δ x @@ -5666,7 +5666,7 @@ exports[`Physics5_2 Derivatives_Functional_10 1`] = ` δ F - / + / δ x @@ -5791,7 +5791,7 @@ exports[`Physics5_2 Derivatives_Functional_15 1`] = ` f - / + / δ @@ -5824,7 +5824,7 @@ exports[`Physics5_2 Derivatives_Functional_16 1`] = ` f - / + / δ @@ -6482,7 +6482,7 @@ exports[`Physics6_0 BraKet_Bra_0 1`] = ` ϕ - | + | ψ @@ -6500,7 +6500,7 @@ exports[`Physics6_0 BraKet_Bra_1 1`] = ` A - | + | B @@ -6548,7 +6548,7 @@ exports[`Physics6_0 BraKet_Bra_3 1`] = ` A - | + | @@ -6605,7 +6605,7 @@ exports[`Physics6_0 BraKet_Bra_6 1`] = ` A - | + | B @@ -6623,7 +6623,7 @@ exports[`Physics6_0 BraKet_Bra_7 1`] = ` A - | + | @@ -6639,7 +6639,7 @@ exports[`Physics6_0 BraKet_Bra_8 1`] = ` A - | + | @@ -6655,7 +6655,7 @@ exports[`Physics6_0 BraKet_Bra_9 1`] = ` A - | + | @@ -6675,7 +6675,7 @@ exports[`Physics6_0 BraKet_Bra_10 1`] = ` - | + | @@ -6777,7 +6777,7 @@ exports[`Physics6_1 BraKet_Braket_0 1`] = ` A - | + | A @@ -6798,7 +6798,7 @@ exports[`Physics6_1 BraKet_Braket_1 1`] = ` - | + | @@ -6853,7 +6853,7 @@ exports[`Physics6_1 BraKet_Braket_4 1`] = ` a - | + | a @@ -6885,7 +6885,7 @@ exports[`Physics6_1 BraKet_Braket_6 1`] = ` α - | + | α @@ -6906,7 +6906,7 @@ exports[`Physics6_1 BraKet_Braket_7 1`] = ` - | + | A @@ -6944,7 +6944,7 @@ exports[`Physics6_1 BraKet_Braket_9 1`] = ` - | + | @@ -6990,7 +6990,7 @@ exports[`Physics6_1 BraKet_Braket_11 1`] = ` - | + | @@ -7521,13 +7521,13 @@ exports[`Physics6_3 BraKet_Expect_7 1`] = ` - | + | A - | + | @@ -7632,13 +7632,13 @@ exports[`Physics6_3 BraKet_Expect_12 1`] = ` - | + | A - | + | @@ -7749,7 +7749,7 @@ exports[`Physics6_3 BraKet_Expect_16 1`] = ` - | + | @@ -7758,7 +7758,7 @@ exports[`Physics6_3 BraKet_Expect_16 1`] = ` - | + | @@ -7924,7 +7924,7 @@ exports[`Physics6_4 BraKet_MatrixEl_6 1`] = ` n - | + | @@ -7933,7 +7933,7 @@ exports[`Physics6_4 BraKet_MatrixEl_6 1`] = ` - | + | m @@ -7954,7 +7954,7 @@ exports[`Physics6_4 BraKet_MatrixEl_7 1`] = ` - | + | @@ -7963,7 +7963,7 @@ exports[`Physics6_4 BraKet_MatrixEl_7 1`] = ` - | + | From 6ee75da164170e77bbff2b2ea8b63bfc129eba3b Mon Sep 17 00:00:00 2001 From: "Davide P. Cervone" Date: Thu, 18 Jun 2026 12:14:08 -0400 Subject: [PATCH 10/11] Work around issue with Safari 26 not processing math inside otherwise empty containers with overflow: auto. (mathjax/MathJax#3579) --- ts/ui/lazy/LazyHandler.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ts/ui/lazy/LazyHandler.ts b/ts/ui/lazy/LazyHandler.ts index 6bcf48afb..73c53ea93 100644 --- a/ts/ui/lazy/LazyHandler.ts +++ b/ts/ui/lazy/LazyHandler.ts @@ -32,6 +32,7 @@ import { HTMLHandler } from '../../handlers/html/HTMLHandler.js'; import { SpeechMathItem } from '../../a11y/speech.js'; import { handleRetriesFor } from '../../util/Retries.js'; import { OptionList } from '../../util/Options.js'; +import { StyleJson } from '../../util/StyleJson.js'; /** * Add the needed function to the window object. @@ -388,6 +389,19 @@ export function LazyMathDocumentMixin< */ protected lazySet: LazySet = new Set(); + /** + * Extra styles for lazy nodes. + */ + public static lazyStyles: StyleJson = { + 'mjx-lazy': { + // + // Needed for Safari 26 when the math appears inside an othewise empty + // node with overflow: auto. See issue #3579. + // + display: 'inline-block', + }, + }; + /** * Augment the MathItem class used for this MathDocument, * then create the intersection observer and lazy list, @@ -400,6 +414,7 @@ export function LazyMathDocumentMixin< */ constructor(...args: any[]) { super(...args); + this.addStyles((this.constructor as typeof BaseClass).lazyStyles); // // Use the LazyMathItem for math items // From 935412a29abaf718467b37d814697df4c19865b3 Mon Sep 17 00:00:00 2001 From: Ahmed Hassan Date: Fri, 19 Jun 2026 19:05:18 +0100 Subject: [PATCH 11/11] Expose UEB in the braille menu --- ts/ui/menu/Menu.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ts/ui/menu/Menu.ts b/ts/ui/menu/Menu.ts index 78b100f31..5008175c4 100644 --- a/ts/ui/menu/Menu.ts +++ b/ts/ui/menu/Menu.ts @@ -587,6 +587,8 @@ export class Menu { jax.options.displayOverflow.substring(1).toLowerCase(); } this.settings.breakInline = jax.options.linebreaks?.inline; + this.settings.brailleCode = + this.document.options.sre?.braille || this.settings.brailleCode; this.defaultSettings = Object.assign( {}, this.document.options.a11y, @@ -939,7 +941,6 @@ export class Menu { const menu = this.menu; menu.settings = this.settings; menu.findID('Settings', 'Overflow', 'Elide').disable(); - menu.findID('Braille', 'ueb').hide(); menu.setJax(this.jax); this.checkLoadableItems(); const cache: [string, string][] = []; @@ -1087,6 +1088,8 @@ export class Menu { protected applySettings() { this.setTabOrder(this.settings.inTabOrder); const options = this.document.options; + options.sre ||= {}; + options.sre.braille = this.settings.brailleCode; options.enableAssistiveMml = this.settings.assistiveMml; this.enableAccessibilityItems('Speech', this.settings.speech); this.enableAccessibilityItems('Braille', this.settings.braille); @@ -1325,7 +1328,7 @@ export class Menu { } /** - * @param {string} code The Braille code format (nemeth or euro) + * @param {string} code The Braille code format (nemeth, ueb, or euro) */ protected setBrailleCode(code: string) { this.document.options.sre.braille = code;