Typeset block $$…$$ math in display style, not text style - #153
Open
manemajef wants to merge 1 commit into
Open
Conversation
manemajef
marked this pull request as ready for review
August 12, 2026 23:41
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
SwiftMathBridgehardcodeslabelMode = .text, so$$ … $$blocks are typeset intext style — the style meant for math sitting inside a line of prose. Block math
should be typeset in display style. The visible difference:
Limits sit beside
\sumand\intinstead of above and below them, fractionsrender at script size, and large operators don't scale up.
The engine already knows which is which —
MarkdownToken.kinddistinguishes.inlineLatexfrom.blockLatex, and block LaTeX is already laid out as acentered standalone block. The information just stopped at the
LatexRendererboundary: both styling paths call
render(latex:fontSize:theme:)with thedelimiter-free content, so
$E=mc^2$and$$E=mc^2$$produce byte-identicalcalls into the renderer.
The change
LatexRenderMode(.inline/.display), passed to a new mode-awareLatexRenderer.renderoverload.MarkdownStyler+Latex.swiftsends.displayfor block and.inlinefor bodymath,
MarkdownStyler+Tables.swiftsends.inlinefor math in table cells.SwiftMathBridgemaps the mode ontolabelMode, and folds it into both thein-memory cache key and the SHA-256 disk-cache filename — without that, a
warmed inline entry would be served back for the same formula in display mode.
Compatibility
Both overloads have default implementations, so a conformer implements whichever
one it cares about and existing renderers keep compiling untouched. The
mode-aware default forwards to the mode-less method; the mode-less default
returns
nil, which bottoms out the chain and is already the documented"cannot produce an image" result. The engine only ever calls the mode-aware
overload.
Embedders using the bundled
SwiftMathBridgewill see their block math reflow onupgrade — that's the fix, but it is a visible change: for
$$\sum_{i=1}^{n} x_i$$at 40pt the image goes from 130×51 to 97×102. Direct
render(latex:fontSize:theme:)callers are unaffected and still get text style. Happy to gate this behind a
BlockLatexStyleflag if you'd rather it be opt-in.Tests
New
MarkdownEngineLatexTeststarget for the tests that need SwiftMath, so thecore
MarkdownEngineTeststarget stays free of that transitive dependency asPackage.swiftdescribes. Mode routing itself is tested inMarkdownEngineTestsagainst a stub renderer, no SwiftMath needed.
swift buildswift test --parallel— 348 tests in 60 suites, plus 3 inMarkdownEngineLatexTestsRebased onto f6137df.