A graphviz extension for odoc
1
fork

Configure Feed

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

Site review fixes: SPA navigation, broken demos, merlin config

Dot extension: Replace per-diagram Js_inline scripts with a single
Js_url support file (dot-init.js) using MutationObserver pattern.
Store DOT source in <script type="text/dot"> elements to avoid
escaping issues. Fixes SPA navigation rendering.

Merlin config: Add stdlib path to build_path so dynamically loaded
packages (e.g. cmdliner via #require) resolve in merlin's type
checker. Previously only stdlib was set, causing red squiggly
errors on library references despite code compiling fine.

Widget_leaflet: Re-export Leaflet_map module from Widget_leaflet
so it's part of the public API. Add leaflet widget demo page.

Interactive extension demos: Move broken demos to .notyet:
- demo2_v2/v3: fake v1/v2 distinction (same cmdliner version)
- demo3_oxcaml: comprehensions extension disabled in compiler
- demo4_crossorigin: requires jon.ludl.am infrastructure
- demo5_multiverse: requires localhost:9090
- demo6/demo7: porting workshops need work

Fix demo_map and demo_widgets: add @x-ocaml.universe and
@x-ocaml.worker tags pointing to /_opam.

Simplify build-site.sh: remove redundant v2/v3 universe builds.

Sidebar: Add odoc-md to 'odoc Core' package group.

Scrollycode: Remove theme references from docs, align titles.

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

+49 -28
+49 -28
src/dot_extension.ml
··· 115 115 Error (Buffer.contents error_output) 116 116 ) 117 117 118 - (** JavaScript code to render a single diagram (for client-side rendering) *) 119 - let render_script id layout content = 120 - Printf.sprintf {| 118 + (** Runtime JS for dot — follows the MutationObserver pattern so diagrams 119 + render on both direct load and SPA navigation. DOT source and layout 120 + engine are stored in data attributes on the container div. *) 121 + let dot_init_js = {| 121 122 (function() { 122 - function renderDot() { 123 - var container = document.getElementById('%s'); 124 - if (!container) return; 123 + 'use strict'; 125 124 126 - if (typeof Viz === 'undefined') { 127 - container.innerHTML = '<pre style="color: red;">Viz.js not loaded</pre>'; 128 - return; 129 - } 130 - 131 - var viz = new Viz(); 132 - viz.renderSVGElement(%S, { engine: %S }) 133 - .then(function(svg) { 134 - container.innerHTML = ''; 135 - container.appendChild(svg); 136 - }) 137 - .catch(function(error) { 138 - container.innerHTML = '<pre style="color: red;">' + error + '</pre>'; 139 - }); 125 + function renderAll() { 126 + if (typeof Viz === 'undefined') return; 127 + var pending = document.querySelectorAll('.odoc-dot-diagram:not([data-dot-init])'); 128 + if (pending.length === 0) return; 129 + pending.forEach(function(el) { 130 + var srcEl = el.querySelector('script[type="text/dot"]'); 131 + if (!srcEl) return; 132 + el.setAttribute('data-dot-init', '1'); 133 + var src = srcEl.textContent; 134 + var engine = el.getAttribute('data-dot-engine') || 'dot'; 135 + var viz = new Viz(); 136 + viz.renderSVGElement(src, { engine: engine }) 137 + .then(function(svg) { 138 + var pre = el.querySelector('pre'); 139 + if (pre) pre.remove(); 140 + el.appendChild(svg); 141 + }) 142 + .catch(function(error) { 143 + el.innerHTML = '<pre style="color: red;">' + error + '</pre>'; 144 + }); 145 + }); 140 146 } 141 147 142 148 if (document.readyState === 'loading') { 143 - document.addEventListener('DOMContentLoaded', renderDot); 149 + document.addEventListener('DOMContentLoaded', function() { 150 + renderAll(); 151 + observe(); 152 + }); 144 153 } else { 145 - renderDot(); 154 + renderAll(); 155 + observe(); 156 + } 157 + 158 + function observe() { 159 + new MutationObserver(function() { renderAll(); }) 160 + .observe(document.body, { childList: true, subtree: true }); 146 161 } 147 162 })(); 148 - |} id content layout 163 + |} 149 164 150 165 module Dot_handler : Api.Code_Block_Extension = struct 151 166 let prefix = "dot" ··· 221 236 } 222 237 223 238 | None -> 224 - (* Default: client-side JavaScript rendering *) 239 + (* Default: client-side JavaScript rendering. 240 + Store DOT source and engine in data attributes; the 241 + dot-init.js support file renders them via MutationObserver. *) 225 242 let html = Printf.sprintf 226 - {|<div id="%s" class="odoc-dot-diagram"%s><pre>%s</pre></div>|} 227 - id style_attr content 243 + {|<div id="%s" class="odoc-dot-diagram"%s data-dot-engine="%s"><script type="text/dot">%s</script><pre>%s</pre></div>|} 244 + id style_attr layout dot_content content 228 245 in 229 - let script = render_script id layout dot_content in 230 246 let block = Block.[{ 231 247 attr = ["odoc-dot"]; 232 248 desc = Raw_markup ("html", html) ··· 237 253 resources = [ 238 254 Api.Js_url viz_js_url; 239 255 Api.Js_url viz_full_js_url; 240 - Api.Js_inline script; 256 + Api.Js_url "extensions/dot-init.js"; 257 + Api.Css_url "extensions/dot.css"; 241 258 ]; 242 259 assets = []; 243 260 } ··· 289 306 Api.Registry.register_support_file ~prefix:"dot" { 290 307 filename = "extensions/dot.css"; 291 308 content = Inline dot_css; 309 + }; 310 + Api.Registry.register_support_file ~prefix:"dot" { 311 + filename = "extensions/dot-init.js"; 312 + content = Inline dot_init_js; 292 313 }