@@ -281,179 +281,13 @@ <h1>All modules for which code is available</h1>
281281import elkLayouts from "https://cdn.jsdelivr.net/npm/@mermaid-js/layout-elk@0.2.0/dist/mermaid-layout-elk.esm.min.mjs"
282282
283283
284-
285- import zenumlLayouts from "https://cdn.jsdelivr.net/npm/@mermaid-js/mermaid-zenuml@0.2.2/dist/mermaid-zenuml.esm.min.mjs"
286-
287-
288284const initStyles = ( ) => {
289285 const defaultStyle = document . createElement ( 'style' ) ;
290- defaultStyle . textContent = `pre.mermaid {
291- /* Same as .mermaid-container > pre */
292- display: block;
293- width: 100%;
294- }
295-
296- pre.mermaid > svg {
297- /* Same as .mermaid-container > pre > svg */
298- height: 500px;
299- width: 100%;
300- max-width: 100% !important;
301- }` ;
286+ defaultStyle . textContent = "pre.mermaid {\n /* Same as .mermaid-container > pre */\n display: block;\n width: 100%;\n}\n\npre.mermaid > svg {\n /* Same as .mermaid-container > pre > svg */\n height: 500px;\n width: 100%;\n max-width: 100% !important;\n}" ;
302287 document . head . appendChild ( defaultStyle ) ;
303288
304289 const fullscreenStyle = document . createElement ( 'style' ) ;
305- fullscreenStyle . textContent = `.mermaid-container {
306- display: flex;
307- flex-direction: row;
308- width: 100%;
309- }
310-
311- .mermaid-container > pre {
312- display: block;
313- width: 100%;
314- }
315-
316- .mermaid-container > pre > svg {
317- height: 500px;
318- width: 100%;
319- max-width: 100% !important;
320- }
321-
322- .mermaid-fullscreen-btn {
323- width: 28px;
324- height: 28px;
325- background: rgba(255, 255, 255, 0.95);
326- border: 1px solid rgba(0, 0, 0, 0.3);
327- border-radius: 4px;
328- cursor: pointer;
329- display: flex;
330- align-items: center;
331- justify-content: center;
332- transition: all 0.2s;
333- box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
334- font-size: 14px;
335- line-height: 1;
336- padding: 0;
337- color: #333;
338- }
339-
340- .mermaid-fullscreen-btn:hover {
341- opacity: 100% !important;
342- background: rgba(255, 255, 255, 1);
343- box-shadow: 0 3px 10px rgba(0, 0, 0, 0.3);
344- transform: scale(1.1);
345- }
346-
347- .mermaid-fullscreen-btn.dark-theme {
348- background: rgba(50, 50, 50, 0.95);
349- border: 1px solid rgba(255, 255, 255, 0.3);
350- color: #e0e0e0;
351- }
352-
353- .mermaid-fullscreen-btn.dark-theme:hover {
354- background: rgba(60, 60, 60, 1);
355- box-shadow: 0 3px 10px rgba(255, 255, 255, 0.2);
356- }
357-
358- .mermaid-fullscreen-modal {
359- display: none;
360- position: fixed !important;
361- top: 0 !important;
362- left: 0 !important;
363- width: 95vw;
364- height: 100vh;
365- background: rgba(255, 255, 255, 0.98);
366- z-index: 9999;
367- padding: 20px;
368- overflow: auto;
369- }
370-
371- .mermaid-fullscreen-modal.dark-theme {
372- background: rgba(0, 0, 0, 0.98);
373- }
374-
375- .mermaid-fullscreen-modal.active {
376- display: flex;
377- align-items: center;
378- justify-content: center;
379- }
380-
381- .mermaid-container-fullscreen {
382- position: relative;
383- width: 95vw;
384- height: 90vh;
385- max-width: 95vw;
386- max-height: 90vh;
387- background: white;
388- border-radius: 8px;
389- padding: 20px;
390- box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
391- overflow: auto;
392- display: flex;
393- align-items: center;
394- justify-content: center;
395- }
396-
397- .mermaid-container-fullscreen.dark-theme {
398- background: #1a1a1a;
399- box-shadow: 0 10px 40px rgba(0, 0, 0, 0.8);
400- }
401-
402- .mermaid-container-fullscreen pre.mermaid {
403- width: 100%;
404- height: 100%;
405- display: flex;
406- align-items: center;
407- justify-content: center;
408- }
409-
410- .mermaid-container-fullscreen .mermaid svg {
411- height: 100% !important;
412- width: 100% !important;
413- cursor: grab;
414- }
415-
416- .mermaid-fullscreen-close {
417- position: fixed !important;
418- top: 20px !important;
419- right: 20px !important;
420- width: 40px;
421- height: 40px;
422- background: rgba(255, 255, 255, 0.95);
423- border: 1px solid rgba(0, 0, 0, 0.2);
424- border-radius: 50%;
425- cursor: pointer;
426- z-index: 10000;
427- display: flex;
428- align-items: center;
429- justify-content: center;
430- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
431- transition: all 0.2s;
432- font-size: 24px;
433- line-height: 1;
434- color: #333;
435- }
436-
437- .mermaid-fullscreen-close:hover {
438- background: white;
439- box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);
440- transform: scale(1.1);
441- }
442-
443- .mermaid-fullscreen-close.dark-theme {
444- background: rgba(50, 50, 50, 0.95);
445- border: 1px solid rgba(255, 255, 255, 0.2);
446- color: #e0e0e0;
447- }
448-
449- .mermaid-fullscreen-close.dark-theme:hover {
450- background: rgba(60, 60, 60, 1);
451- box-shadow: 0 6px 16px rgba(255, 255, 255, 0.2);
452- }
453-
454- .mermaid-fullscreen-modal .mermaid-fullscreen-btn {
455- display: none !important;
456- }` ;
290+ fullscreenStyle . textContent = ".mermaid-container {\n position: relative;\n display: flex;\n flex-direction: row;\n width: 100%;\n}\n\n.mermaid-container > pre {\n display: block;\n width: 100%;\n}\n\n.mermaid-container > pre > svg {\n height: 500px;\n width: 100%;\n max-width: 100% !important;\n}\n\n.mermaid-fullscreen-btn {\n position: absolute;\n width: 28px;\n height: 28px;\n background: rgba(255, 255, 255, 0.95);\n border: 1px solid rgba(0, 0, 0, 0.3);\n border-radius: 4px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s;\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);\n font-size: 14px;\n line-height: 1;\n padding: 0;\n color: #333;\n}\n\n.mermaid-fullscreen-btn:hover {\n opacity: 100% !important;\n background: rgba(255, 255, 255, 1);\n box-shadow: 0 3px 10px rgba(0, 0, 0, 0.3);\n transform: scale(1.1);\n}\n\n.mermaid-fullscreen-btn.dark-theme {\n background: rgba(50, 50, 50, 0.95);\n border: 1px solid rgba(255, 255, 255, 0.3);\n color: #e0e0e0;\n}\n\n.mermaid-fullscreen-btn.dark-theme:hover {\n background: rgba(60, 60, 60, 1);\n box-shadow: 0 3px 10px rgba(255, 255, 255, 0.2);\n}\n\n.mermaid-fullscreen-modal {\n display: none;\n position: fixed !important;\n top: 0 !important;\n left: 0 !important;\n width: 95vw;\n height: 100vh;\n background: rgba(255, 255, 255, 0.98);\n z-index: 9999;\n padding: 20px;\n overflow: auto;\n}\n\n.mermaid-fullscreen-modal.dark-theme {\n background: rgba(0, 0, 0, 0.98);\n}\n\n.mermaid-fullscreen-modal.active {\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.mermaid-container-fullscreen {\n position: relative;\n width: 95vw;\n height: 90vh;\n max-width: 95vw;\n max-height: 90vh;\n background: white;\n border-radius: 8px;\n padding: 20px;\n box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);\n overflow: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.mermaid-container-fullscreen.dark-theme {\n background: #1a1a1a;\n box-shadow: 0 10px 40px rgba(0, 0, 0, 0.8);\n}\n\n.mermaid-container-fullscreen pre.mermaid {\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.mermaid-container-fullscreen .mermaid svg {\n height: 100% !important;\n width: 100% !important;\n cursor: grab;\n}\n\n.mermaid-fullscreen-close {\n position: fixed !important;\n top: 20px !important;\n right: 20px !important;\n width: 40px;\n height: 40px;\n background: rgba(255, 255, 255, 0.95);\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 50%;\n cursor: pointer;\n z-index: 10000;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);\n transition: all 0.2s;\n font-size: 24px;\n line-height: 1;\n color: #333;\n}\n\n.mermaid-fullscreen-close:hover {\n background: white;\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);\n transform: scale(1.1);\n}\n\n.mermaid-fullscreen-close.dark-theme {\n background: rgba(50, 50, 50, 0.95);\n border: 1px solid rgba(255, 255, 255, 0.2);\n color: #e0e0e0;\n}\n\n.mermaid-fullscreen-close.dark-theme:hover {\n background: rgba(60, 60, 60, 1);\n box-shadow: 0 6px 16px rgba(255, 255, 255, 0.2);\n}\n\n.mermaid-fullscreen-modal .mermaid-fullscreen-btn {\n display: none !important;\n}" ;
457291 document . head . appendChild ( fullscreenStyle ) ;
458292}
459293
@@ -500,6 +334,13 @@ <h1>All modules for which code is available</h1>
500334let previousScrollOffset = [ window . scrollX , window . scrollY ] ;
501335let _lazyObserver = null ;
502336let _renderQueue = Promise . resolve ( ) ;
337+ let closeModal = ( ) => { } ;
338+
339+ document . addEventListener ( 'keydown' , ( e ) => {
340+ if ( e . key === 'Escape' && modal ?. classList . contains ( 'active' ) ) {
341+ closeModal ( ) ;
342+ }
343+ } ) ;
503344
504345// Apply d3 zoom to each SVG. Idempotent: an SVG already wrapped is skipped,
505346// so this is safe to call both for the initial batch and for diagrams that
@@ -596,8 +437,10 @@ <h1>All modules for which code is available</h1>
596437 el . removeAttribute ( 'data-mermaid-deferred' ) ;
597438 // Apply zoom to the now-rendered diagram, matching
598439 // the decoration visible diagrams receive.
599- if ( "True" === "True" ) {
600- addZoomToSvgs ( d3 . selectAll ( ".mermaid svg" ) ) ;
440+ if ( true ) {
441+ addZoomToSvgs ( d3 . selectAll ( ".mermaid" ) . select ( function ( ) {
442+ return this . querySelector ( "svg" ) ;
443+ } ) ) ;
601444 }
602445 } catch ( e ) {
603446 console . error ( "Mermaid deferred rendering failed:" , e ) ;
@@ -618,10 +461,12 @@ <h1>All modules for which code is available</h1>
618461 const mermaids_active = document . querySelectorAll ( ".mermaid:not([data-mermaid-deferred]):not([data-mermaid-render-failed])" ) ;
619462 const mermaids_processed = document . querySelectorAll ( ".mermaid:not([data-mermaid-deferred]):not([data-mermaid-render-failed])[data-processed='true']" ) ;
620463
621- if ( "True" === "True" ) {
464+ if ( true ) {
622465 const mermaids_to_add_zoom = - 1 === - 1 ? mermaids_active . length : - 1 ;
623466 if ( mermaids_to_add_zoom > 0 ) {
624- var svgs = d3 . selectAll ( ".mermaid svg" ) ;
467+ var svgs = d3 . selectAll ( ".mermaid" ) . select ( function ( ) {
468+ return this . querySelector ( "svg" ) ;
469+ } ) ;
625470 // Wait until every active (visible) diagram has rendered. Deferred
626471 // diagrams are excluded, so a fixed d3_node_count that counts a
627472 // hidden diagram no longer loops forever waiting for its SVG.
@@ -638,7 +483,7 @@ <h1>All modules for which code is available</h1>
638483 }
639484
640485 // Stop here if not adding fullscreen capability
641- if ( "True" !== "True" ) return ;
486+ if ( ! true ) return ;
642487
643488 if ( modal !== null ) {
644489 // Destroy existing modal
@@ -661,7 +506,7 @@ <h1>All modules for which code is available</h1>
661506 modalContent = modal . querySelector ( '.mermaid-container-fullscreen' ) ;
662507 const closeBtn = modal . querySelector ( '.mermaid-fullscreen-close' ) ;
663508
664- const closeModal = ( ) => {
509+ closeModal = ( ) => {
665510 modal . classList . remove ( 'active' ) ;
666511 modalContent . innerHTML = '' ;
667512 document . body . style . overflow = ''
@@ -672,12 +517,6 @@ <h1>All modules for which code is available</h1>
672517 modal . addEventListener ( 'click' , ( e ) => {
673518 if ( e . target === modal ) closeModal ( ) ;
674519 } ) ;
675- document . addEventListener ( 'keydown' , ( e ) => {
676- if ( e . key === 'Escape' && modal . classList . contains ( 'active' ) ) {
677- closeModal ( ) ;
678- }
679- } ) ;
680-
681520 document . querySelectorAll ( '.mermaid' ) . forEach ( ( mermaidDiv ) => {
682521 if ( mermaidDiv . parentNode . classList . contains ( 'mermaid-container' ) ||
683522 mermaidDiv . closest ( '.mermaid-fullscreen-modal' ) ) {
@@ -697,8 +536,8 @@ <h1>All modules for which code is available</h1>
697536 const fullscreenBtn = document . createElement ( 'button' ) ;
698537 fullscreenBtn . className = 'mermaid-fullscreen-btn' + ( darkTheme ? ' dark-theme' : '' ) ;
699538 fullscreenBtn . setAttribute ( 'aria-label' , 'View diagram in fullscreen' ) ;
700- fullscreenBtn . textContent = '⛶' ;
701- fullscreenBtn . style . opacity = ' 50%' ;
539+ fullscreenBtn . textContent = "\u26f6" ;
540+ fullscreenBtn . style . opacity = " 50%" ;
702541
703542 // Calculate dynamic position based on diagram's margin and padding
704543 const diagramStyle = window . getComputedStyle ( mermaidDiv ) ;
@@ -710,7 +549,7 @@ <h1>All modules for which code is available</h1>
710549 fullscreenBtn . style . right = `${ marginRight + paddingRight + 4 } px` ;
711550
712551 fullscreenBtn . addEventListener ( 'click' , ( ) => {
713- previousScrollOffset = [ window . scroll , window . scrollY ] ;
552+ previousScrollOffset = [ window . scrollX , window . scrollY ] ;
714553 const clone = mermaidDiv . cloneNode ( true ) ;
715554 modalContent . innerHTML = '' ;
716555 modalContent . appendChild ( clone ) ;
@@ -722,9 +561,9 @@ <h1>All modules for which code is available</h1>
722561 svg . style . width = '100%' ;
723562 svg . style . height = 'auto' ;
724563 svg . style . maxWidth = '100%' ;
725- svg . style . sdisplay = 'block' ;
564+ svg . style . display = 'block' ;
726565
727- if ( "True" === "True" ) {
566+ if ( true ) {
728567 setTimeout ( ( ) => {
729568 const g = svg . querySelector ( 'g' ) ;
730569 if ( g ) {
@@ -747,9 +586,27 @@ <h1>All modules for which code is available</h1>
747586 } ) ;
748587} ;
749588
589+
590+ // Resolves once the lazily-loaded zenuml plugin has registered (or immediately
591+ // when the page has no zenuml diagram). `load` awaits this before rendering.
592+ let zenumlReady = Promise . resolve ( ) ;
593+ const pageHasZenuml = ( ) => [ ...document . querySelectorAll ( ".mermaid" ) ] . some ( ( el ) => {
594+ const code = el . getAttribute ( "data-original-code" ) || el . textContent || "" ;
595+ const diagram = code
596+ . replace ( / ^ \s * - - - \s * \n [ ^ ] * ?\n - - - \s * / , "" )
597+ . replace ( / ^ \s * % % \{ [ ^ ] * ?\} % % \s * / , "" ) ;
598+ return / ^ \s * z e n u m l \b / i. test ( diagram ) ;
599+ } ) ;
600+
601+
750602const load = async ( ) => {
751603 initStyles ( ) ;
752604
605+
606+ // Wait for the zenuml plugin (loaded lazily below) to finish registering
607+ // before rendering, so zenuml diagrams are recognised.
608+ await zenumlReady ;
609+
753610 await runMermaid ( true ) ;
754611
755612 const reRunIfThemeChanges = async ( ) => {
@@ -758,10 +615,8 @@ <h1>All modules for which code is available</h1>
758615 darkTheme = newDarkTheme ;
759616 console . log ( "Theme change detected, re-running mermaid with" , darkTheme ? "dark" : "default" , "theme" ) ;
760617 await mermaid . initialize (
761- { ...JSON . parse (
762- `{"startOnLoad": false}`
763- ) ,
764- ...{ darkMode : darkTheme , theme : darkTheme ? 'dark' : 'default' } ,
618+ { ...{ "startOnLoad" : false } ,
619+ ...{ darkMode : darkTheme , theme : darkTheme ? "dark" : "default" } ,
765620 }
766621 ) ;
767622 await runMermaid ( true ) ;
@@ -785,15 +640,40 @@ <h1>All modules for which code is available</h1>
785640
786641
787642
788- mermaid . registerExternalDiagrams ( [ zenumlLayouts ] ) ;
643+ // Only load and register the zenuml plugin when the page actually contains a
644+ // zenuml diagram. This avoids fetching the (large) zenuml bundle on every page,
645+ // and avoids its side effects where they are not needed. The import is kept off
646+ // the module's top level (no top-level await) so the "load" listener below is
647+ // still registered synchronously; `load` awaits `zenumlReady` before rendering.
648+ if ( pageHasZenuml ( ) ) {
649+ // The zenuml bundle injects a stylesheet that declares generic design tokens
650+ // (--background, --border, --white, ...) on :root, clobbering identically-named
651+ // tokens in the host theme (sphinxawesome, shibuya, ...) and causing a flash of
652+ // restyled page content. Confine those declarations to the zenuml diagram
653+ // subtree so they no longer leak onto the host page.
654+ const descopeZenumlStyle = ( node ) => {
655+ if ( ! node || node . tagName !== "STYLE" || node . hasAttribute ( "data-zenuml-descoped" ) ) return ;
656+ const css = node . textContent || "" ;
657+ if ( ! css . includes ( "--occurance-border" ) ) return ; // zenuml stylesheet signature
658+ node . setAttribute ( "data-zenuml-descoped" , "true" ) ;
659+ node . textContent = css . replace ( / : r o o t \b / g, ".zenuml" ) ;
660+ } ;
661+ new MutationObserver ( ( records ) => {
662+ for ( const rec of records ) {
663+ for ( const node of rec . addedNodes ) descopeZenumlStyle ( node ) ;
664+ }
665+ } ) . observe ( document . head , { childList : true } ) ;
666+
667+ zenumlReady = import ( "https://cdn.jsdelivr.net/npm/@mermaid-js/mermaid-zenuml@0.2.2/dist/mermaid-zenuml.esm.min.mjs" ) . then ( ( { default : zenumlLayouts } ) => {
668+ mermaid . registerExternalDiagrams ( [ zenumlLayouts ] ) ;
669+ } ) ;
670+ }
789671
790672
791673console . log ( "Initializing mermaid with" , darkTheme ? "dark" : "default" , "theme" ) ;
792674mermaid . initialize (
793- { ...JSON . parse (
794- `{"startOnLoad": false}`
795- ) ,
796- ...{ darkMode : darkTheme , theme : darkTheme ? 'dark' : 'default' } ,
675+ { ...{ "startOnLoad" : false } ,
676+ ...{ darkMode : darkTheme , theme : darkTheme ? "dark" : "default" } ,
797677 }
798678) ;
799679
0 commit comments