diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 040bc1b6e..03af37067 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -29,9 +29,9 @@ jobs: name: Generate Code Docs steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - - uses: pnpm/action-setup@v4 + - uses: pnpm/action-setup@v5 name: Install pnpm with: version: 10 @@ -44,11 +44,11 @@ jobs: run: pnpm typedoc - name: Upload artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@v5 with: # Upload docs folder path: './docs' - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v5 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8c8d6a72e..bd4092528 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,16 +14,16 @@ jobs: name: Compile and test MathJax steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - - uses: pnpm/action-setup@v4 + - uses: pnpm/action-setup@v5 name: Install pnpm with: version: 10 run_install: false - name: Install Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v5 with: node-version: 24 cache: 'pnpm' @@ -49,7 +49,7 @@ jobs: run: pnpm -s test:gh - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@v6 with: token: ${{ secrets.CODECOV_TOKEN }} slug: mathjax/MathJax-src diff --git a/package.json b/package.json index 1c39dd754..bf29dbdfc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mathjax/src", - "version": "4.1.2", + "version": "4.1.3", "description": "Beautiful and accessible math in all browsers. MathJax is an open-source JavaScript display engine for LaTeX, MathML, and AsciiMath notation that works in all browsers and in server-side node applications. This package includes the source code as well as the packaged components.", "keywords": [ "MathJax", @@ -168,9 +168,9 @@ ] }, "dependencies": { - "@mathjax/mathjax-newcm-font": "4.1.2", + "@mathjax/mathjax-newcm-font": "4.1.3", "mhchemparser": "^4.2.1", "mj-context-menu": "^1.0.0", - "speech-rule-engine": "5.0.0-rc.1" + "speech-rule-engine": "5.0.0-rc.4" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 99de33ac7..029dda439 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@mathjax/mathjax-newcm-font': - specifier: 4.1.2 - version: 4.1.2 + specifier: 4.1.3 + version: 4.1.3 mhchemparser: specifier: ^4.2.1 version: 4.2.1 @@ -18,8 +18,8 @@ importers: specifier: ^1.0.0 version: 1.0.0 speech-rule-engine: - specifier: 5.0.0-rc.1 - version: 5.0.0-rc.1 + specifier: 5.0.0-rc.4 + version: 5.0.0-rc.4 devDependencies: '@eslint/js': specifier: ^9.39.2 @@ -91,8 +91,8 @@ importers: specifier: ^29.7.0 version: 29.7.0 '@mathjax/mathjax-bbm-font-extension': - specifier: ^4.1.2 - version: 4.1.2 + specifier: ^4.1.3 + version: 4.1.3 '@types/jest': specifier: ^29.5.14 version: 29.5.14 @@ -462,11 +462,11 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@mathjax/mathjax-bbm-font-extension@4.1.2': - resolution: {integrity: sha512-UJdm+Cueut0+FjYZYoBmjZp/Rci17OMyUGsaHoSpZ//1NJ7n4LsJm9/jqAM38UGIuZEcQYbksqIfSJLFu10ncw==} + '@mathjax/mathjax-bbm-font-extension@4.1.3': + resolution: {integrity: sha512-dhL8qBIA7COFAeB+ypxbmNtb85+E8Xgis1TaUP5SKpYMMhU0wLcbDwgpriZcpca4Jneo7r6Ty4qEN+0yu6FQyw==} - '@mathjax/mathjax-newcm-font@4.1.2': - resolution: {integrity: sha512-lZHMjNP2XbABHA3kVn40rbse5ERUeMEmrGH03qLkCwxq4/5Z/eNLr0akw1MmQcqTwCbvkx1BFcmJ7RCfbRlw3Q==} + '@mathjax/mathjax-newcm-font@4.1.3': + resolution: {integrity: sha512-gzAB3dFHilHX1l5x2xUqRL+1jDQt3Fyza1DkEMVXWC4E8SvsGdlgEza47HYi2WhVcgfkvf4zgUGzuhbq3Pjlew==} '@pkgr/core@0.2.9': resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} @@ -633,6 +633,7 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + deprecated: Potential CWE-502 - Update to 1.3.1 or higher '@webassemblyjs/ast@1.14.1': resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} @@ -2154,8 +2155,8 @@ packages: spdx-license-ids@3.0.22: resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} - speech-rule-engine@5.0.0-rc.1: - resolution: {integrity: sha512-WgxL1K0MXNXW7+4fn11B6rtJ+jZTnCQMkJ44yV3XmFDcpGBqFJrDEHouGuFryqB5hoG1ksE2zlss/m6KOWnbPA==} + speech-rule-engine@5.0.0-rc.4: + resolution: {integrity: sha512-3dFcH2QtQNhtvUUc09TxoaEK4WG03B9Z57krFG9jocrKykKGu35BhIkWr0vgEAxZZomEWkeluff69y/EbYzP4Q==} hasBin: true sprintf-js@1.0.3: @@ -3040,9 +3041,9 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@mathjax/mathjax-bbm-font-extension@4.1.2': {} + '@mathjax/mathjax-bbm-font-extension@4.1.3': {} - '@mathjax/mathjax-newcm-font@4.1.2': {} + '@mathjax/mathjax-newcm-font@4.1.3': {} '@pkgr/core@0.2.9': {} @@ -4952,7 +4953,7 @@ snapshots: spdx-license-ids@3.0.22: {} - speech-rule-engine@5.0.0-rc.1: + speech-rule-engine@5.0.0-rc.4: dependencies: '@xmldom/xmldom': 0.9.10 commander: 14.0.3 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 3f7e54bfd..b35810fab 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,2 +1,4 @@ packages: - 'testsuite' +minimumReleaseAgeExclude: + - speech-rule-engine@5.0.0-rc.3 diff --git a/testsuite/package.json b/testsuite/package.json index 99c79921f..9c0d3269a 100644 --- a/testsuite/package.json +++ b/testsuite/package.json @@ -18,7 +18,7 @@ }, "dependencies": { "@jest/globals": "^29.7.0", - "@mathjax/mathjax-bbm-font-extension": "^4.1.2", + "@mathjax/mathjax-bbm-font-extension": "^4.1.3", "@types/jest": "^29.5.14", "jest": "^29.7.0", "ts-jest": "^29.4.6", diff --git a/testsuite/tests/input/tex/Base.test.ts b/testsuite/tests/input/tex/Base.test.ts index 6e06df5d7..f31feae31 100644 --- a/testsuite/tests/input/tex/Base.test.ts +++ b/testsuite/tests/input/tex/Base.test.ts @@ -4002,6 +4002,36 @@ describe('Complete Array', () => { expect(tex2mml('\\begin{array}{> {x} c} X \\end{array}')).toMatchSnapshot(); }); + it('column {cc}', () => { + expect(tex2mml('\\begin{array}{{cc}} X Y \\end{array}')).toMatchSnapshot(); + }); + + it('column {c}', () => { + expect(tex2mml('\\begin{array}{c{c}} X Y \\end{array}')).toMatchSnapshot(); + }); + + it('column {{c}}', () => { + expect(tex2mml('\\begin{array}{{{c}}} X \\end{array}')).toMatchSnapshot(); + }); + + it('column {r}c{l}', () => { + expect(tex2mml('\\begin{array}{{r}c{l}} X & Y & Z \\end{array}')).toMatchSnapshot(); + }); + + it('column newline', () => { + expect(tex2mml('\\begin{array}{c\nc} X & Y\\end{array}')).toMatchSnapshot(); + }); + + it('column > > c < <', () => { + expect(tex2mml('\\begin{array}{>{a}>{b}c<{x}<{y}} X \\end{array}')).toMatchSnapshot(); + }); + + it('column brace errors', () => { + expectTexError(tex2mml('\\begin{array}{c{xx}} X Y \\end{array}')).toBe('Illegal pream-token (xx)'); + expectTexError(tex2mml('\\begin{array}{c{{c}}} X Y \\end{array}')).toBe('Illegal pream-token ({c})'); + expectTexError(tex2mml('\\begin{array}{c{c{c}}} X Y \\end{array}')).toBe('Illegal pream-token (c{c})'); + }); + it('BadPreamToken', () => { expectTexError('\\begin{array}a').toBe('Illegal pream-token (a)'); }); diff --git a/testsuite/tests/input/tex/__snapshots__/Base.test.ts.snap b/testsuite/tests/input/tex/__snapshots__/Base.test.ts.snap index bc59e9aa3..23cf581c2 100644 --- a/testsuite/tests/input/tex/__snapshots__/Base.test.ts.snap +++ b/testsuite/tests/input/tex/__snapshots__/Base.test.ts.snap @@ -1820,6 +1820,22 @@ exports[`Complete Array column ! | @ 1`] = ` " `; +exports[`Complete Array column > > c < < 1`] = ` +" + + + + b + a + X + y + x + + + +" +`; + exports[`Complete Array column > space 1`] = ` " @@ -1872,6 +1888,62 @@ exports[`Complete Array column @ p m 1`] = ` " `; +exports[`Complete Array column {{c}} 1`] = ` +" + + + + X + + + +" +`; + +exports[`Complete Array column {c} 1`] = ` +" + + + + X + Y + + + +" +`; + +exports[`Complete Array column {cc} 1`] = ` +" + + + + X + Y + + + +" +`; + +exports[`Complete Array column {r}c{l} 1`] = ` +" + + + + X + + + Y + + + Z + + + +" +`; + exports[`Complete Array column b 1`] = ` " @@ -2179,6 +2251,24 @@ exports[`Complete Array column m 1`] = ` " `; +exports[`Complete Array column newline 1`] = ` +" + + + + X + + + Y + + + +" +`; + exports[`Complete Array column p 1`] = ` " @@ -3552,7 +3642,7 @@ exports[`Fenced Fenced Arrows 3 1`] = ` b - + c @@ -3660,7 +3750,7 @@ exports[`Fenced Middle 1`] = ` ( a - | + | b ) @@ -7931,7 +8021,7 @@ exports[`Spacing Nonscript scriptlevel 1`] = ` a - | + | b @@ -7948,7 +8038,7 @@ exports[`Spacing Nonscript toplevel 1`] = ` a - | + | b @@ -9156,7 +9246,7 @@ exports[`User Defined Macros Middle Color 1`] = ` A - | + | B ) @@ -9170,7 +9260,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`] = ` - | + | diff --git a/ts/components/version.ts b/ts/components/version.ts index ee0f23014..7f1411ee8 100644 --- a/ts/components/version.ts +++ b/ts/components/version.ts @@ -22,4 +22,4 @@ * @author dpvc@mathjax.org (Davide Cervone) */ -export const VERSION = '4.1.2'; +export const VERSION = '4.1.3'; 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/ColumnParser.ts b/ts/input/tex/ColumnParser.ts index a0beec563..5452bf0c8 100644 --- a/ts/input/tex/ColumnParser.ts +++ b/ts/input/tex/ColumnParser.ts @@ -77,13 +77,14 @@ export class ColumnParser { ':': (state) => this.addRule(state, 'dashed'), '>': (state) => (state.cstart[state.j] = - (state.cstart[state.j] || '') + this.getBraces(state)), + this.getBraces(state) + (state.cstart[state.j] || '')), '<': (state) => (state.cend[state.j - 1] = - (state.cend[state.j - 1] || '') + this.getBraces(state)), + this.getBraces(state) + (state.cend[state.j - 1] || '')), '@': (state) => this.addAt(state, this.getBraces(state)), '!': (state) => this.addBang(state, this.getBraces(state)), '*': (state) => this.repeat(state), + '{': (state) => this.brace(state), // // Non-standard for math-mode versions // @@ -94,6 +95,7 @@ export class ColumnParser { // Ignored // ' ': (_state) => {}, + '\n': (_state_) => {}, }; /** @@ -128,6 +130,16 @@ export class ColumnParser { cextra: array.cextra, }; // + // Remove one pair of braces, if there are any + // + if (template.charAt(0) === '{' && template.slice(-1) === '}') { + const braced = this.getBraces(state); + if (braced.length === template.length - 2) { + state.template = braced; + } + state.i = 0; + } + // // Loop through the template to process the column specifiers // let n = 0; @@ -141,10 +153,7 @@ export class ColumnParser { const code = state.template.codePointAt(state.i); const c = (state.c = String.fromCodePoint(code)); state.i += c.length; - if (!Object.hasOwn(this.columnHandler, c)) { - throw new TexError('BadPreamToken', 'Illegal pream-token (%1)', c); - } - this.columnHandler[c](state); + this.processColumn(state, c); } // // Set array definition values @@ -156,6 +165,19 @@ export class ColumnParser { this.setPadding(state, array); } + /** + * Process a column specifier, or throw an error if not defined. + * + * @param {ColumnState} state The current state of the parser + * @param {string} c The column type to process + */ + protected processColumn(state: ColumnState, c: string) { + if (!Object.hasOwn(this.columnHandler, c)) { + throw new TexError('BadPreamToken', 'Illegal pream-token (%1)', c); + } + this.columnHandler[c](state); + } + /** * @param {ColumnState} state The current state of the parser * @param {ArrayItem} array The array stack item to adjust @@ -420,4 +442,14 @@ export class ColumnParser { new Array(n).fill(cols).join('') + state.template.substring(state.i); state.i = 0; } + + /** + * Process a braced column type + * + * @param {ColumnState} state The current state of the parser + */ + public brace(state: ColumnState) { + state.i--; + this.processColumn(state, this.getBraces(state)); + } } 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..48f27addc 100644 --- a/ts/input/tex/TexParser.ts +++ b/ts/input/tex/TexParser.ts @@ -151,7 +151,8 @@ 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 @@ -248,11 +249,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/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'); } diff --git a/ts/input/tex/base/BaseMethods.ts b/ts/input/tex/base/BaseMethods.ts index dc371a34d..4fcf2badb 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 }); @@ -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'] 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)) { 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]: { diff --git a/ts/output/common/FontData.ts b/ts/output/common/FontData.ts index a74e341c3..4509595c5 100644 --- a/ts/output/common/FontData.ts +++ b/ts/output/common/FontData.ts @@ -1330,6 +1330,10 @@ export class FontData< const delim = this.delimiters[n]; if (delim && !('dir' in delim)) { this.delimiters[n] = null; + if (mathjax.asyncIsSynchronous) { + this.loadDynamicFileSync(delim); + return this.getDelimiter(n); + } retryAfter(this.loadDynamicFile(delim)); return null; } @@ -1378,6 +1382,10 @@ export class FontData< const variant = this.variant[name]; delete variant.chars[n]; variant.linked.forEach((link) => delete link[n]); + if (mathjax.asyncIsSynchronous) { + this.loadDynamicFileSync(char); + return this.getChar(name, n); + } retryAfter(this.loadDynamicFile(char)); return null; } diff --git a/ts/output/svg/Wrappers/mrow.ts b/ts/output/svg/Wrappers/mrow.ts index 3be3fba9f..33e6ee3e4 100644 --- a/ts/output/svg/Wrappers/mrow.ts +++ b/ts/output/svg/Wrappers/mrow.ts @@ -233,8 +233,9 @@ export const SvgMrow = (function (): SvgMrowClass { public addChildren(parents: N[]) { let x = 0; let i = 0; + const isEmbellished = this.node.isEmbellished; for (const child of this.childNodes) { - const n = child.breakCount; + const n = isEmbellished ? 0 : child.breakCount; child.toSVG(parents.slice(i, i + n + 1)); if (child.dom) { let k = 0; 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', }, 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 // 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(); } } diff --git a/ts/ui/safe/safe.ts b/ts/ui/safe/safe.ts index 228b77575..77c4db190 100644 --- a/ts/ui/safe/safe.ts +++ b/ts/ui/safe/safe.ts @@ -82,7 +82,7 @@ export class Safe { // // Pattern for allowed ids // - idPattern: /^mjx-[-a-zA-Z0-9_.]+$/, + idPattern: /^mjx-(?:eqn:.+|[-a-zA-Z0-9_.]+)$/, // // Pattern for data attributes // diff --git a/ts/util/context.ts b/ts/util/context.ts index a18295ba3..8179b590b 100644 --- a/ts/util/context.ts +++ b/ts/util/context.ts @@ -22,12 +22,18 @@ */ declare const process: { platform: string }; +declare const exports: object; /** * True if there is a window object */ export const hasWindow = typeof window !== 'undefined'; +/** + * True if used from the cjs directory, false for mjs directory + */ +export const isCJS = typeof exports !== 'undefined'; + /** * The browsers window and document objects, if any */ @@ -75,6 +81,6 @@ export const context = { if (context.os === 'Windows') { context.path = (file: string) => file.match(/^[/\\]?[a-zA-Z]:[/\\]/) - ? 'file://' + file.replace(/\\/g, '/').replace(/^\//, '') + ? (isCJS ? '' : 'file://') + file.replace(/\\/g, '/').replace(/^\//, '') : file.replace(/^\//, 'file:///'); }