feat(math): implement m:nary n-ary operator converter#2752
feat(math): implement m:nary n-ary operator converter#2752caio-pizzol merged 5 commits intosuperdoc-dev:mainfrom
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b02a1238f6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…c-dev#2602) Made-with: Cursor
Made-with: Cursor
… (SD-2381) - m:limLoc (§22.1.2.53): absent element now uses operator-character heuristic — integrals default to subSup, non-integrals to undOvr. <m:limLoc/> with no val attribute defaults to undOvr per spec. - m:sub/m:sup (§22.1.2.70): treat as actually absent when the element is missing, not just when a hide flag is set — indefinite integrals now render as bare <mo> instead of <msubsup> with empty <mrow/> slots. - m:chr (§22.1.2.20): <m:chr/> with no val renders an empty operator instead of silently defaulting to the integral glyph. - m:grow (§22.1.2.72): when explicitly OFF, emit largeop="false" stretchy="false" on the <mo> so the renderer doesn't enlarge. - Helper type is OmmlJsonNode (no more typeof subHide). JSDoc documents all 6 output shapes. Tests: - 10 new unit tests in omml-to-mathml.test.ts covering every ECMA-376 spec path for m:nary (subHide true/bare/OFF, limLoc no-val, chr no-val, operator heuristic, m:grow suppression, etc). - New behavior fixture math-nary-tests.docx (13 scenarios) + 9 Playwright tests in math-equations.spec.ts. Fixture uploaded to the shared R2 corpus as rendering/sd-2381-nary-scenarios.docx for layout/visual.
…(SD-2381) Per ECMA-376 §22.1.2.72, the subHide/supHide flags control whether EMPTY m:sub/m:sup limits are rendered as a placeholder character or hidden. When the limit has content, it must always be rendered regardless of the flag — matching Word's actual behavior. Previous code hid the entire slot whenever the hide flag was ON, which silently dropped content. For example, an integral with sub="0", sup="1", and subHide=true was rendering as ∫¹ instead of ∫₀¹. - hasSub/hasSup now check meaningful content (ignoring m:ctrlPr, the formatting-hint child Word emits inside empty limit elements). - The hide flag only suppresses the slot when the limit is empty/absent. - Updated 3 unit tests that encoded the incorrect semantics; added a new regression test for "content overrides hide" and a test confirming m:ctrlPr-only limits are treated as empty. - Updated the behavior test for the same scenarios.
…ord)
Follow-up to the previous subHide/supHide fix. When m:subHide is ON and
m:sub has content, Word doesn't simply suppress the slot — it promotes
the sub content into the sup slot, prepended to any existing sup content
(symmetric for supHide: sup content appended to sub). This preserves any
author-entered content even when a file is hand-crafted with conflicting
hide+content OMML.
Before: an integral with m:sub="0", m:sup="1", m:subHide="true" rendered
as <msubsup><mo>∫</mo><mrow>0</mrow><mrow>1</mrow></msubsup> (∫ with 0
below and 1 above — stacked).
After: renders as <msup><mo>∫</mo><mrow>01</mrow></msup>, matching
Word's ∫^{01} where 0 appears to the left of 1 in the superscript.
- Compute renderSubChildren / renderSupChildren with promotion, then pass
those arrays to convertChildren.
- Strip m:ctrlPr before the check so Word's "empty with formatting hint"
pattern still resolves to bare <mo>.
- Replaced the "content always wins over hide" unit test with two tests
anchoring the promotion (sub→sup, sup→sub).
- Updated the behavior test for scenarios 8/9 to assert msup with "01".
ea6f827 to
7e02eca
Compare
|
🎉 This PR is included in @superdoc-dev/react v1.2.0-next.7 The release is available on GitHub release |
|
🎉 This PR is included in vscode-ext v2.3.0-next.10 |
|
🎉 This PR is included in esign v2.3.0-next.10 The release is available on GitHub release |
|
🎉 This PR is included in template-builder v1.5.0-next.10 The release is available on GitHub release |
|
🎉 This PR is included in superdoc-cli v0.7.0-next.11 The release is available on GitHub release |
|
🎉 This PR is included in superdoc v1.26.0-next.10 The release is available on GitHub release |
|
🎉 This PR is included in superdoc-cli v0.7.0-next.11 The release is available on GitHub release |
|
🎉 This PR is included in superdoc-sdk v1.6.0-next.8 |
|
🎉 This PR is included in superdoc-cli v0.7.0 The release is available on GitHub release |
|
🎉 This PR is included in superdoc v1.27.0 The release is available on GitHub release |
|
🎉 This PR is included in @superdoc-dev/react v1.2.0 The release is available on GitHub release |
|
🎉 This PR is included in superdoc-sdk v1.6.0 |
Closes #2602
Summary
m:naryOMML-to-MathML converter for n-ary operators (integral, summation, product, etc.)subSup(default) andundOvrlimit locations, mapping to<msubsup>/<munderover>respectivelym:subHideandm:supHideflags, falling back to<msub>/<msup>or bare<mo>as neededm:chris absentMATH_OBJECT_REGISTRYSpec reference
ECMA-376 Section 22.1.2.70
Test plan
vitest runpasses for omml-to-mathml.test.ts