Skip to content

Measure Mermaid diagrams at CSS zoom 1 - #206

Closed
marc-medley wants to merge 1 commit into
MarkEdit-app:mainfrom
marc-medley:main
Closed

marc-medley wants to merge 1 commit into
MarkEdit-app:mainfrom
marc-medley:main

Conversation

@marc-medley

Copy link
Copy Markdown

Candidate fix to close #205

Caveat: This a Grok 4.7 (high) generated source code solution. As such, you may wish to review the code relative to the MarkEdit-preview project coding practices. That said, it is working locally for me to resolve the issue of Mermaid labels being clipped after zooming the preview pane.

Summary

Mermaid 11.16.0 sizes HTML label <foreignObject> boxes from getBoundingClientRect(). WKWebView includes the preview pane's CSS zoom in that rect, and Mermaid writes it into the SVG as user units. At zoom 0.8 every node label is stored at 0.8× its zoom-1 size (preview is 45.5125 × 19.2 instead of 56.890625 × 24), so the last glyph and the ascenders clip. Command-0 does not rebuild the SVG, so the clip remains after zoom is back at 1.

handlePostRender now clears ancestor style.zoom to 1 before mermaid.run(), forces layout, and restores the previous zoom before the post-render callback. setPageZoom invalidates an in-flight restore, so a zoom key pressed while a diagram is rendering keeps the new value. Quick Look is covered when its content element is the zoomed ancestor. Syntax-hidden diagrams render through mermaid.render() on document.body, outside the preview zoom, and are unchanged.

Nested flowchart.htmlLabels: false is not this fix. In 11.16.0 node labels still use a <foreignObject> unless root htmlLabels is false.

Test plan

  • yarn test — tests/mermaidZoom.test.ts checks the suspend/restore, a zoom write during mermaid.run(), and a preview with no diagram
  • Side-by-side preview, Command-minus twice (zoom 0.8), type a space outside a ```mermaid fence. preview in the Web Inspector is about 56.89 × 24, and the word is whole
  • Command-0, type another space. The same width, at zoom 1
  • Open a note whose diagram was built at zoom 0.8 (foreignObject width about 45.5). Edit once. The label box becomes 56.89 × 24
  • Command-minus while a diagram render is in flight, then Command-0. The pane stays at the zoom from the key, and the label is not clipped
  • Zoom in past 1 (the zoomed-in rule). The diagram stays at its natural width and the labels stay whole
  • Quick Look pinch-zoom, then a preview refresh. Labels stay whole
  • Lite build: no Mermaid run, and Command-minus still changes pane zoom

Fix for a Mermaid 11.16 measurement bug that shows up in
MarkEdit-preview’s WKWebView when the preview pane is zoomed.
Mermaid node labels can end up clipped when the preview pane is zoomed.

Mermaid stores getBoundingClientRect() as foreignObject width and height.
WKWebView includes the preview pane's CSS zoom in that rect, and the SVG
then uses those numbers as user units. At zoom 0.8 the label box is 0.8×
the glyphs, so the last letter and the ascenders clip. The bad box stays
until the next preview rebuild, which is why a window can stay clipped
after zoom is already back at 1.

Run mermaid.run() with ancestor zoom forced to 1, then restore the user's
zoom. A zoom change during that run cancels the restore.
@marc-medley marc-medley changed the title Title: Measure Mermaid diagrams at CSS zoom 1 Measure Mermaid diagrams at CSS zoom 1 Oct 7, 2026
@cyanzhong

Copy link
Copy Markdown
Contributor

Thanks for the detailed report and proposed fix. I’ve confirmed the underlying issue, but this approach still has problems with zoom changes during rendering and stylesheet-defined zoom in Quick Look. I’m closing this PR and will follow up with a fix that avoids temporarily changing the visible pane’s zoom. Keeping #205 open to track that work.

@cyanzhong cyanzhong closed this Oct 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Mermaid node labels clip when the preview pane is zoomed

2 participants