Skip to content

fix: wrap vcenter mpadded in mrow for valid MathML#4193

Merged
grigoriy-reshetniak merged 3 commits intoKaTeX:mainfrom
cyphercodes:main
Apr 5, 2026
Merged

fix: wrap vcenter mpadded in mrow for valid MathML#4193
grigoriy-reshetniak merged 3 commits intoKaTeX:mainfrom
cyphercodes:main

Conversation

@cyphercodes
Copy link
Copy Markdown
Contributor

@cyphercodes cyphercodes commented Apr 5, 2026

Fixes #4078

Problem

When \vcenter is used inside \mathrel (and similar commands), the resulting MathML is invalid:

<!-- Before: Invalid MathML -->
<mo><mpadded class="vcenter"\u003e...</mpadded></mo>

The issue is that <mpadded> cannot be a direct child of <mo> per the MathML spec.

Solution

Wrap the <mpadded> element in an <mrow> to produce valid MathML:

<!-- After: Valid MathML -->
<mo><mrow><mpadded class="vcenter">...</mpadded></mrow></mo>

Changes

  • Modified src/functions/vcenter.ts to wrap mpadded in mrow

Testing

  • All 1270 existing Jest tests pass
  • TypeScript compilation passes
  • Linting passes

Fix invalid MathML structure when \vcenter is used inside \mathrel.
Previously, \mathrel{\vcenter{:}} produced <mo><mpadded>...</mpadded></mo>,
which is invalid because <mpadded> cannot be a child of <mo>.

Now wraps the <mpadded> in an <mrow>, producing valid MathML:
<mo><mrow><mpadded>...</mpadded></mrow></mo>

Fixes KaTeX#4078
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 5, 2026

Greptile Summary

This PR fixes invalid MathML output when \vcenter is nested inside \mathrel or similar commands (\mathbin, \mathord, etc.), where <mpadded> as a direct child of <mo> is not permitted by the MathML spec. The one-line fix wraps mpadded in mrow, which is semantically neutral (grouping only) and produces spec-compliant output.

Confidence Score: 5/5

Safe to merge; the fix is minimal, correct, and all 1270 existing tests pass.

The change is a targeted two-liner that wraps mpadded in mrow, directly matching the MathML content-model spec. No existing behavior is altered for standalone vcenter usage since mrow is semantically transparent. The only remaining feedback is a P2 suggestion to add a regression test, which does not block merge.

No files require special attention.

Important Files Changed

Filename Overview
src/functions/vcenter.ts Wraps mpadded in mrow so vcenter produces valid MathML inside mo/mi/mrel elements; fix is correct and minimal.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["\\mathrel{\\vcenter{...}}"] --> B["mclass mathmlBuilder"]
    B --> C["mo node"]
    C -->|"Before fix"| D["mpadded.vcenter ❌ invalid child of mo"]
    C -->|"After fix"| E["mrow"]
    E --> F["mpadded.vcenter ✓ valid per MathML spec"]
Loading

Reviews (1): Last reviewed commit: "fix: wrap vcenter mpadded in mrow for va..." | Re-trigger Greptile

Comment thread src/functions/vcenter.ts
Comment on lines +40 to +42
const mpadded = new MathNode(
"mpadded", [mml.buildGroup(group.body, options)], ["vcenter"]);
return new MathNode("mrow", [mpadded]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Missing regression test for the fixed scenario

No test currently asserts the MathML structure when \vcenter is nested inside \mathrel (or \mathbin, \mathord, etc.) — the exact bug being fixed. Adding a test that renders \mathrel{\vcenter{\frac{a}{b}}} and checks that the output does not contain <mo><mpadded (bare mpadded inside mo) would guard this path against future regressions.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 5, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.32%. Comparing base (314f856) to head (3fafd5a).
⚠️ Report is 119 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #4193      +/-   ##
==========================================
+ Coverage   93.15%   93.32%   +0.16%     
==========================================
  Files          91       91              
  Lines        6795     6812      +17     
  Branches     1582     1585       +3     
==========================================
+ Hits         6330     6357      +27     
- Misses        429      453      +24     
+ Partials       36        2      -34     
Files with missing lines Coverage Δ
src/functions/vcenter.ts 100.00% <100.00%> (ø)

... and 15 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update ed12205...3fafd5a. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Member

@edemaine edemaine left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As Grigoriy has pointed out, this is potentially suboptimal if \vcenter isn't nested inside something, but I think it's fine to take this simpler approach. I do wonder whether other macros need a similar treatment... but can worry about that another time.

@grigoriy-reshetniak grigoriy-reshetniak merged commit ee66b78 into KaTeX:main Apr 5, 2026
9 checks passed
@grigoriy-reshetniak
Copy link
Copy Markdown
Collaborator

Thank you!

@edemaine
Copy link
Copy Markdown
Member

edemaine commented Apr 5, 2026

🎉 This PR is included in version 0.16.45 🎉

The release is available on:

Your semantic-release bot 📦🚀

grigoriy-reshetniak pushed a commit that referenced this pull request Apr 5, 2026
## [0.16.45](v0.16.44...v0.16.45) (2026-04-05)

### Bug Fixes

* wrap vcenter mpadded in mrow for valid MathML ([#4193](#4193)) ([ee66b78](ee66b78)), closes [#4078](#4078)
dadezzz pushed a commit to dadezzz/ice-notes that referenced this pull request Apr 9, 2026
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [katex](https://katex.org) ([source](https://github.com/KaTeX/KaTeX)) | [`0.16.44` → `0.16.45`](https://renovatebot.com/diffs/npm/katex/0.16.44/0.16.45) | ![age](https://developer.mend.io/api/mc/badges/age/npm/katex/0.16.45?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/katex/0.16.44/0.16.45?slim=true) |

---

### Release Notes

<details>
<summary>KaTeX/KaTeX (katex)</summary>

### [`v0.16.45`](https://github.com/KaTeX/KaTeX/blob/HEAD/CHANGELOG.md#01645-2026-04-05)

[Compare Source](KaTeX/KaTeX@v0.16.44...v0.16.45)

##### Bug Fixes

- wrap vcenter mpadded in mrow for valid MathML ([#&#8203;4193](KaTeX/KaTeX#4193)) ([ee66b78](KaTeX/KaTeX@ee66b78)), closes [#&#8203;4078](KaTeX/KaTeX#4078)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMDQuOCIsInVwZGF0ZWRJblZlciI6IjQzLjEwNC44IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: Renovate Bot <[email protected]>
Co-committed-by: Renovate Bot <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

KaTeX produces invalid MathML with \vcenter

3 participants