A Message Sequence Charts extension for odoc
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix MSC extension for SPA navigation

Replace Js_inline loader script with Js_url support file using
MutationObserver pattern. When new MSC diagram elements appear
(via SPA content swap), re-load the mscgenjs-inpage script which
scans and renders script[type="text/x-mscgen"] elements.

mscgenjs-inpage has no public API — it only scans on load — so
re-loading is the only way to trigger rendering for new elements.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+40 -7
+40 -7
src/msc_extension.ml
··· 25 25 (** MscGen.js CDN URL - the inpage version auto-renders on DOMContentLoaded *) 26 26 let mscgen_js_url = "https://unpkg.com/mscgenjs-inpage@4/dist/mscgen-inpage.js" 27 27 28 - (** Script to load mscgenjs with defer-like behavior *) 29 - let loader_script = Printf.sprintf {| 28 + (** Runtime JS for MSC — uses MutationObserver pattern for SPA compat. 29 + Loads mscgenjs-inpage and re-loads it when new MSC elements appear, 30 + since mscgenjs-inpage only scans for elements at load time. *) 31 + let msc_init_js = Printf.sprintf {| 30 32 (function() { 33 + 'use strict'; 34 + var mscgenUrl = %S; 35 + var loading = false; 36 + 31 37 function loadMscgen() { 38 + if (loading) return; 39 + loading = true; 32 40 var script = document.createElement('script'); 33 - script.src = %S; 34 - script.async = false; 41 + script.src = mscgenUrl; 42 + script.onload = function() { loading = false; }; 43 + script.onerror = function() { loading = false; }; 35 44 document.head.appendChild(script); 36 45 } 46 + 47 + function renderAll() { 48 + var pending = document.querySelectorAll('.odoc-msc-diagram:not([data-msc-init])'); 49 + if (pending.length === 0) return; 50 + pending.forEach(function(el) { 51 + el.setAttribute('data-msc-init', '1'); 52 + }); 53 + // Re-load mscgenjs-inpage to trigger its DOM scan on the new elements 54 + loadMscgen(); 55 + } 56 + 37 57 if (document.readyState === 'loading') { 38 - document.addEventListener('DOMContentLoaded', loadMscgen); 58 + document.addEventListener('DOMContentLoaded', function() { 59 + renderAll(); 60 + observe(); 61 + }); 39 62 } else { 40 - loadMscgen(); 63 + renderAll(); 64 + observe(); 65 + } 66 + 67 + function observe() { 68 + new MutationObserver(function() { renderAll(); }) 69 + .observe(document.body, { childList: true, subtree: true }); 41 70 } 42 71 })(); 43 72 |} mscgen_js_url ··· 216 245 Api.content = block; 217 246 overrides = []; 218 247 resources = [ 219 - Api.Js_inline loader_script; 248 + Api.Js_url "extensions/msc-init.js"; 220 249 Api.Css_url "extensions/msc.css"; 221 250 ]; 222 251 assets = []; ··· 328 357 Api.Registry.register_support_file ~prefix:"msc" { 329 358 filename = "extensions/msc.css"; 330 359 content = Inline msc_css; 360 + }; 361 + Api.Registry.register_support_file ~prefix:"msc" { 362 + filename = "extensions/msc-init.js"; 363 + content = Inline msc_init_js; 331 364 }