Address issue #331: Mermaid rendering issue#333
Merged
Conversation
Add MPMermaidRenderingTests.m with 18 tests covering the Obj-C rendering pipeline for Mermaid diagrams: code block class generation, script inclusion/exclusion logic, preference change detection, multiple diagrams, edge cases, and coexistence with MathJax and Graphviz. These tests are written first (TDD) before implementing the fix for Mermaid diagrams not re-rendering on DOM replacement. Related to #331
- Add MutationObserver in mermaid.init.js to re-render diagrams when body.innerHTML is replaced (the DOM replacement path in MPDocument.m). Previously, Mermaid only rendered on window.load, so diagrams would not appear during live preview updates. - Add rendering guard to prevent re-entrant calls when the observer fires during an active render cycle. - Add early return when no .language-mermaid elements are found to avoid unnecessary work on every DOM mutation. - Add CSS rule in export.css for full-width Mermaid SVG rendering, so diagrams use the full preview pane width instead of being constrained. - Remove out-of-scope testExportCSSIncludedWithStyles test per review. Related to #331
Script inclusion tests need MPAssetFullLink (render method) to check for filenames like mermaid.min.js. The export path uses MPAssetEmbedded which inlines script content without filenames. Related to #331
Contributor
Code Coverage ReportCurrent Coverage: 57.18% Coverage Details (Summary) |
schuyler
added a commit
that referenced
this pull request
Apr 8, 2026
Related to #332 window.load doesn't fire when MPDocument.m replaces body.innerHTML during live editing, so Graphviz diagrams never rendered after the initial page load. This is the same class of bug fixed for Mermaid in #331 / PR #333. Changes: - viz.init.js: add rendering guard flag and MutationObserver that watches for .language-{engine} elements across all 6 Graphviz engines (circo, dot, fdp, neato, osage, twopi); calls init() when any are found. Loop prevention is natural: doGraphviz() replaces the code element with SVG, so the trigger element disappears before the observer can fire again. - MPGraphvizRenderingTests.m: new test file mirroring MPMermaidRenderingTests.m; covers code block rendering, script inclusion/exclusion, preference change detection, multiple diagrams, mixed content, edge cases, and graphviz+mermaid coexistence. - project.pbxproj: register MPGraphvizRenderingTests.m in MacDownTests target.
2 tasks
schuyler
added a commit
that referenced
this pull request
Apr 8, 2026
…ng (#357) ## Summary - `viz.init.js`: adds a `rendering` guard flag and a `MutationObserver` that watches for `.language-{engine}` elements across all 6 Graphviz engines; calls `init()` when any are found after `body.innerHTML` replacement. Loop prevention is structural — `doGraphviz()` replaces the triggering `<code>` element with SVG before the observer can fire again. - `MPGraphvizRenderingTests.m`: new test file mirroring `MPMermaidRenderingTests.m`; 15 tests covering code block rendering, script inclusion/exclusion, preference change detection, style changes, multiple diagrams, mixed content, edge cases, and graphviz+mermaid coexistence. - `project.pbxproj`: registers new test file in the `MacDownTests` target. This is the same class of bug fixed for Mermaid in #331 / PR #333. ## Test plan - [x] All four CI runners passed (macos-14, macos-15, macos-15-intel, macos-26) - [ ] Manual: open a doc with a `dot` fence, edit it — diagram re-renders without full reload Related to #332
This was referenced Apr 8, 2026
Closed
schuyler
added a commit
that referenced
this pull request
Apr 9, 2026
## Summary - Fix `mermaid.init.js`: the rendering loop navigated to the *parent* of each `<pre>` and set its `innerHTML`, replacing the entire parent's content on the first iteration and destroying all subsequent diagram elements. Changed to `pre.outerHTML = result.svg` to replace only the specific `<pre>`. Added null/tagName guard for unexpected DOM structures. - Fix `viz.init.js`: identical root cause (`dom.parentElement.parentElement.innerHTML`). Changed to `dom.parentElement.outerHTML`. Added matching null/tagName guard. - Update `plans/test_coverage_improvement_plan.md`: `MPGraphvizRenderingTests.m` was missing from the tracking document. ## Root Cause PR #333 (v3000.0.5) fixed the MutationObserver path so Mermaid renders at all on DOM replacement, but the rendering loop itself was broken regardless of how it was triggered. `querySelectorAll` returns a static snapshot, so the loop correctly collects all elements — but the first iteration's `innerHTML` assignment on the shared parent element removed all siblings from the DOM, leaving subsequent elements with `parentElement === null` and silently failing. ## Related Issues Related to #331 Related to #332 ## Manual Testing Plan 13 scenarios covering: multiple Mermaid diagrams, multiple Graphviz diagrams, mixed engines, invalid syntax alongside valid diagrams, live-edit re-render, and Mermaid+Graphviz together in one document. Full checklist: - M-1: Two Mermaid diagrams render (primary regression) - M-2: Three Mermaid diagrams render - M-3: Single Mermaid diagram (no regression) - M-4: Invalid Mermaid + valid Mermaid — error in first doesn't block second - M-5: Live-edit re-render with multiple Mermaid diagrams - M-6: Mermaid mixed with tables, code blocks, other content - G-1: Two Graphviz diagrams render (primary regression) - G-2: Three Graphviz diagrams render - G-3: Mixed Graphviz engines (dot, neato, circo) in one document - G-4: Invalid Graphviz + valid Graphviz — error doesn't block second - G-5: Live-edit re-render with multiple Graphviz diagrams - C-1: Mermaid and Graphviz together in one document - C-2: HTML export with multiple diagrams ## Review Notes No Obj-C changes. No automated JS tests possible (no WKWebView test harness exists in this project). The fix is a mechanical change to DOM replacement strategy; the `rendering` guard, MutationObserver, and unique ID scheme are unchanged.
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.
Summary
MutationObserverinmermaid.init.jsthat detects new.language-mermaidelements and triggers rendering automaticallyMPMermaidRenderingTests.mcovering the Obj-C rendering pipelineRelated Issue
Related to #331
Also filed #332 for the same bug in Graphviz (same root cause, separate fix)
Changes
MacDown/Resources/Extensions/mermaid.init.jsMacDown/Resources/Extensions/export.csssvg[id^="mermaid_"]CSS rule for full-width diagramsMacDownTests/MPMermaidRenderingTests.mMacDown 3000.xcodeproj/project.pbxprojplans/test_coverage_improvement_plan.mdplans/xcuitest.mdRoot Cause
The preview has two rendering paths in
MPDocument.m:body.innerHTMLand re-triggers Prism and MathJax, but never re-triggered Mermaid renderingThe
mermaid.init.jsscript only ran onwindow.load, which only fires on full page reload. The MutationObserver now detects when.language-mermaidelements appear in the DOM afterbody.innerHTMLreplacement and triggers rendering automatically. After Mermaid renders, those elements are replaced with SVGs, providing a natural guard against infinite loops.Manual Testing Plan
assets/demo.md— all 3 Mermaid diagrams (flow chart, sequence, Gantt) should renderReview Notes
MutationObserverpattern stays self-contained inside the existing IIFE (no global namespace pollution)