Skip to content

feat(math): implement m:bar overbar/underbar converter#2619

Merged
caio-pizzol merged 4 commits intosuperdoc-dev:mainfrom
Muhammad-Nur-Alamsyah-Anwar:feat/math-m-bar-converter
Mar 28, 2026
Merged

feat(math): implement m:bar overbar/underbar converter#2619
caio-pizzol merged 4 commits intosuperdoc-dev:mainfrom
Muhammad-Nur-Alamsyah-Anwar:feat/math-m-bar-converter

Conversation

@Muhammad-Nur-Alamsyah-Anwar
Copy link
Copy Markdown
Contributor

Closes #2610

Implemented the m:bar (overbar/underbar) OMML → MathML converter.

OMML: m:barm:barPr (pos: top/bot) + m:e
MathML: <mover> (top) or <munder> (bottom) with stretchy <mo>

  • Added converters/bar.ts with proper Unicode (U+203E overline, U+0332 low line)
  • Registered in MATH_OBJECT_REGISTRY
  • Added 3 unit tests in omml-to-mathml.test.ts (overbar, underbar, default)
  • Follows existing patterns (see fraction.ts)

Fixes #2610.

Closes superdoc-dev#2610

- Add convertBar() in converters/bar.ts:
  - Reads m:barPr/m:pos@m:val to determine position
  - 'top' (default) → <mover> with U+203E (overline)
  - 'bot' → <munder> with U+0332 (combining low line)
  - stretchy='true' so the bar stretches over the base expression
- Register 'm:bar': convertBar in MATH_OBJECT_REGISTRY
- Export convertBar from converters/index.ts
- Add 3 unit tests: overbar, underbar, missing barPr fallback
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Implements support for converting OMML m:bar (overbar/underbar) nodes into MathML, extending the OMML→MathML converter’s registry-based coverage.

Changes:

  • Added a new convertBar converter (m:bar<mover>/<munder> with a stretchy <mo>).
  • Registered m:bar in the math object registry and re-exported the converter.
  • Added unit tests covering overbar, underbar, and default behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.

File Description
packages/layout-engine/painters/dom/src/features/math/omml-to-mathml.ts Registers m:bar converter in the OMML→MathML registry.
packages/layout-engine/painters/dom/src/features/math/omml-to-mathml.test.ts Adds unit tests for m:bar conversion behavior.
packages/layout-engine/painters/dom/src/features/math/converters/index.ts Exports the new convertBar converter.
packages/layout-engine/painters/dom/src/features/math/converters/bar.ts Implements m:bar → MathML conversion logic.

Comment on lines +20 to +24
const barPr = elements.find((e) => e.name === 'm:barPr');
const pos = barPr?.elements?.find((e) => e.name === 'm:pos');
const posVal = pos?.attributes?.['m:val'];
const isUnder = posVal === 'bot';

Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

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

m:bar default position appears to be inverted. The implementation defaults to <mover> when m:barPr/m:pos is missing (since isUnder is only true for m:val === 'bot'), but the spec/issue description indicates 'bot' is the default. Consider treating missing/unknown m:pos@m:val as underbar and only using overbar when m:val === 'top', and update the doc comment accordingly.

Copilot uses AI. Check for mistakes.
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.

@Muhammad-Nur-Alamsyah-Anwar this is right — we tested in Word, default is underbar.

const base = elements.find((e) => e.name === 'm:e');

const wrapper = doc.createElementNS(MATHML_NS, isUnder ? 'munder' : 'mover');
wrapper.appendChild(convertChildren(base?.elements ?? []));
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

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

wrapper.appendChild(convertChildren(base?.elements ?? [])) can expand to multiple nodes (e.g., x+1 becomes <mi/><mo/><mn/>), which would make <mover>/<munder> have more than two children and produce invalid MathML. Consider converting the m:e container itself (so it can wrap multiple children into a single <mrow>), or otherwise ensure the base is a single node before appending the accent.

Suggested change
wrapper.appendChild(convertChildren(base?.elements ?? []));
// Convert the m:e container itself so that the base becomes a single MathML node.
wrapper.appendChild(convertChildren(base ? [base] : []));

Copilot uses AI. Check for mistakes.
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.

@Muhammad-Nur-Alamsyah-Anwar correct — bar breaks on multi-letter expressions. see our review comment.

Comment on lines +247 to +261
it('defaults to overbar when m:barPr is missing', () => {
const omml = {
name: 'm:oMath',
elements: [{
name: 'm:bar',
elements: [
{ name: 'm:e', elements: [{ name: 'm:r', elements: [{ name: 'm:t', elements: [{ type: 'text', text: 'z' }] }] }] },
],
}],
};
const result = convertOmmlToMathml(omml, doc);
expect(result).not.toBeNull();
const mover = result!.querySelector('mover');
expect(mover).not.toBeNull();
});
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

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

This test asserts the default behavior is an overbar (<mover>) when m:barPr is missing, but the issue/PR description states 'bot' is the default (i.e., <munder>). Update the expectation (and the converter) so the default matches the intended OMML behavior.

Copilot uses AI. Check for mistakes.
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.

@Muhammad-Nur-Alamsyah-Anwar yes, test expectation needs to flip to underbar.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 983df1346b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const base = elements.find((e) => e.name === 'm:e');

const wrapper = doc.createElementNS(MATHML_NS, isUnder ? 'munder' : 'mover');
wrapper.appendChild(convertChildren(base?.elements ?? []));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Wrap m:bar base in one node before adding accent

mover/munder are 2-argument MathML elements (base + script), but this line appends the converted children of m:e as a fragment, so expressions like x+1 under a bar become multiple base siblings plus the accent. In that case the bar is applied incorrectly (typically only to the first child) or the MathML tree is invalid for consumers. Wrap the converted base in a single container (e.g., <mrow>) before appending the accent so the bar applies to the full expression.

Useful? React with 👍 / 👎.

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.

same as #2 — covered in our review.

@caio-pizzol caio-pizzol self-assigned this Mar 28, 2026
Copy link
Copy Markdown
Contributor

@caio-pizzol caio-pizzol left a comment

Choose a reason for hiding this comment

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

@Muhammad-Nur-Alamsyah-Anwar nice contribution! two things to fix: the default position should be underbar (verified against Word), and bars over multi-part expressions (like x + y) need the base grouped so the bar stretches across. left inline comments.

- Default to munder (underbar) when no position is specified,
  matching Word's rendering behavior (posVal !== 'top')
- Wrap base content in <mrow> to correctly group multi-token
  expressions like 'x + y' as a single MathML child
- Move m:bar entry from 'Not yet implemented' to 'Implemented'
  section in the registry
- Strengthen tests: assert base content text and <mo> character
  for all three cases (top, bot, default)
Copy link
Copy Markdown
Contributor

@caio-pizzol caio-pizzol left a comment

Choose a reason for hiding this comment

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

@Muhammad-Nur-Alamsyah-Anwar all four points from last round are fixed — default position, mrow wrapping, registry section, and test assertions. lgtm, approving.

@caio-pizzol caio-pizzol enabled auto-merge (squash) March 28, 2026 11:46
@caio-pizzol caio-pizzol merged commit db0853d into superdoc-dev:main Mar 28, 2026
38 checks passed
@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot bot commented Mar 28, 2026

🎉 This PR is included in vscode-ext v1.1.0-next.36

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot bot commented Mar 28, 2026

🎉 This PR is included in superdoc v1.24.0-next.35

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot bot commented Mar 28, 2026

🎉 This PR is included in superdoc-cli v0.5.0-next.35

The release is available on GitHub release

@superdoc-bot
Copy link
Copy Markdown
Contributor

superdoc-bot bot commented Mar 28, 2026

🎉 This PR is included in superdoc-sdk v1.3.0-next.35

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Math: implement m:bar overbar/underbar converter (community)

3 participants