1414 < link rel ="stylesheet " type ="text/css " href ="../_static/autodoc_pydantic.css " />
1515 < link rel ="stylesheet " type ="text/css " href ="../_static/sphinx-design.min.css?v=95c83b7e " />
1616 < link rel ="stylesheet " type ="text/css " href ="../_static/styles/furo-extensions.css?v=8dab3a3b " />
17- < link rel ="stylesheet " type ="text/css " href ="../_static/styles/custom.css?v=98e9b53e " />
17+ < link rel ="stylesheet " type ="text/css " href ="../_static/styles/custom.css?v=45bc7fcc " />
1818
1919
2020
@@ -267,12 +267,175 @@ <h1>All modules for which code is available</h1>
267267 < script src ="../_static/clipboard.min.js?v=a7894cd8 "> </ script >
268268 < script src ="../_static/copybutton.js?v=f281be69 "> </ script >
269269 < script src ="../_static/design-tabs.js?v=f930bc37 "> </ script >
270- < script type ="module " src ="https://cdn.jsdelivr.net/npm/mermaid@11.2.0 /dist/mermaid.esm.min.mjs "> </ script >
270+ < script type ="module " src ="https://cdn.jsdelivr.net/npm/mermaid@11.12.1 /dist/mermaid.esm.min.mjs "> </ script >
271271 < script type ="module " src ="https://cdn.jsdelivr.net/npm/@mermaid-js/layout-elk@0.1.4/dist/mermaid-layout-elk.esm.min.mjs "> </ script >
272- < script type ="module "> import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11.2.0 /dist/mermaid.esm.min.mjs" ; import elkLayouts from "https://cdn.jsdelivr.net/npm/@mermaid-js/layout-elk@0.1.4/dist/mermaid-layout-elk.esm.min.mjs" ; mermaid . registerLayoutLoaders ( elkLayouts ) ; mermaid . initialize ( { startOnLoad :false } ) ; </ script >
272+ < script type ="module "> import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11.12.1 /dist/mermaid.esm.min.mjs" ; import elkLayouts from "https://cdn.jsdelivr.net/npm/@mermaid-js/layout-elk@0.1.4/dist/mermaid-layout-elk.esm.min.mjs" ; mermaid . registerLayoutLoaders ( elkLayouts ) ; mermaid . initialize ( { startOnLoad :false } ) ; </ script >
273273 < script src ="https://cdn.jsdelivr.net/npm/d3@7.9.0/dist/d3.min.js "> </ script >
274- < script type ="module ">
275- import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11.2.0/dist/mermaid.esm.min.mjs" ;
274+ < script type ="module "> import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11.12.1/dist/mermaid.esm.min.mjs" ;
275+
276+ const style = document . createElement ( 'style' ) ;
277+ style . textContent = `.mermaid-container {
278+ position: relative !important;
279+ display: inline-block;
280+ width: 100%;
281+ }
282+
283+ .mermaid-fullscreen-btn {
284+ position: absolute !important;
285+ width: 28px;
286+ height: 28px;
287+ background: rgba(255, 255, 255, 0.95);
288+ border: 1px solid rgba(0, 0, 0, 0.3);
289+ border-radius: 4px;
290+ cursor: pointer;
291+ z-index: 100;
292+ display: flex;
293+ align-items: center;
294+ justify-content: center;
295+ transition: all 0.2s;
296+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
297+ font-size: 14px;
298+ line-height: 1;
299+ padding: 0;
300+ color: #333;
301+ }
302+
303+ .mermaid-fullscreen-btn:hover {
304+ opacity: 100% !important;
305+ background: rgba(255, 255, 255, 1);
306+ box-shadow: 0 3px 10px rgba(0, 0, 0, 0.3);
307+ transform: scale(1.1);
308+ }
309+
310+ .mermaid-fullscreen-btn.dark-theme {
311+ background: rgba(50, 50, 50, 0.95);
312+ border: 1px solid rgba(255, 255, 255, 0.3);
313+ color: #e0e0e0;
314+ }
315+
316+ .mermaid-fullscreen-btn.dark-theme:hover {
317+ background: rgba(60, 60, 60, 1);
318+ box-shadow: 0 3px 10px rgba(255, 255, 255, 0.2);
319+ }
320+
321+ .mermaid-fullscreen-modal {
322+ display: none;
323+ position: fixed !important;
324+ top: 0 !important;
325+ left: 0 !important;
326+ width: 100vw;
327+ height: 100vh;
328+ background: rgba(255, 255, 255, 0.98);
329+ z-index: 9999;
330+ padding: 20px;
331+ overflow: auto;
332+ }
333+
334+ .mermaid-fullscreen-modal.dark-theme {
335+ background: rgba(0, 0, 0, 0.98);
336+ }
337+
338+ .mermaid-fullscreen-modal.active {
339+ display: flex;
340+ align-items: center;
341+ justify-content: center;
342+ }
343+
344+ .mermaid-fullscreen-content {
345+ position: relative;
346+ width: 95vw;
347+ height: 90vh;
348+ max-width: 95vw;
349+ max-height: 90vh;
350+ background: white;
351+ border-radius: 8px;
352+ padding: 20px;
353+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
354+ overflow: auto;
355+ display: flex;
356+ align-items: center;
357+ justify-content: center;
358+ }
359+
360+ .mermaid-fullscreen-content.dark-theme {
361+ background: #1a1a1a;
362+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.8);
363+ }
364+
365+ .mermaid-fullscreen-content .mermaid {
366+ width: 100%;
367+ height: 100%;
368+ display: flex;
369+ align-items: center;
370+ justify-content: center;
371+ }
372+
373+ .mermaid-fullscreen-content .mermaid svg {
374+ width: 100%;
375+ height: auto;
376+ max-width: 100%;
377+ max-height: 85vh;
378+ cursor: grab;
379+ }
380+
381+ .mermaid-fullscreen-close {
382+ position: fixed !important;
383+ top: 20px !important;
384+ right: 20px !important;
385+ width: 40px;
386+ height: 40px;
387+ background: rgba(255, 255, 255, 0.95);
388+ border: 1px solid rgba(0, 0, 0, 0.2);
389+ border-radius: 50%;
390+ cursor: pointer;
391+ z-index: 10000;
392+ display: flex;
393+ align-items: center;
394+ justify-content: center;
395+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
396+ transition: all 0.2s;
397+ font-size: 24px;
398+ line-height: 1;
399+ color: #333;
400+ }
401+
402+ .mermaid-fullscreen-close:hover {
403+ background: white;
404+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);
405+ transform: scale(1.1);
406+ }
407+
408+ .mermaid-fullscreen-close.dark-theme {
409+ background: rgba(50, 50, 50, 0.95);
410+ border: 1px solid rgba(255, 255, 255, 0.2);
411+ color: #e0e0e0;
412+ }
413+
414+ .mermaid-fullscreen-close.dark-theme:hover {
415+ background: rgba(60, 60, 60, 1);
416+ box-shadow: 0 6px 16px rgba(255, 255, 255, 0.2);
417+ }
418+
419+ .mermaid-fullscreen-modal .mermaid-fullscreen-btn {
420+ display: none !important;
421+ }
422+ ` ;
423+ document . head . appendChild ( style ) ;
424+
425+ // Detect if page has dark background
426+ const isDarkTheme = ( ) => {
427+ const bgColor = window . getComputedStyle ( document . body ) . backgroundColor ;
428+ const match = bgColor . match ( / r g b \( ( \d + ) , \s * ( \d + ) , \s * ( \d + ) / ) ;
429+ if ( match ) {
430+ const r = parseInt ( match [ 1 ] ) ;
431+ const g = parseInt ( match [ 2 ] ) ;
432+ const b = parseInt ( match [ 3 ] ) ;
433+ const brightness = ( r * 299 + g * 587 + b * 114 ) / 1000 ;
434+ return brightness < 128 ;
435+ }
436+ return false ;
437+ } ;
438+
276439const load = async ( ) => {
277440 await mermaid . run ( ) ;
278441 const all_mermaids = document . querySelectorAll ( ".mermaid" ) ;
@@ -281,11 +444,9 @@ <h1>All modules for which code is available</h1>
281444 if ( mermaids_to_add_zoom > 0 ) {
282445 var svgs = d3 . selectAll ( ".mermaid svg" ) ;
283446 if ( all_mermaids . length !== mermaids_processed . length ) {
284- // try again in a sec, wait for mermaids to load
285447 setTimeout ( load , 200 ) ;
286448 return ;
287449 } else if ( svgs . size ( ) !== mermaids_to_add_zoom ) {
288- // try again in a sec, wait for mermaids to load
289450 setTimeout ( load , 200 ) ;
290451 return ;
291452 } else {
@@ -300,6 +461,138 @@ <h1>All modules for which code is available</h1>
300461 } ) ;
301462 }
302463 }
464+
465+ const darkTheme = isDarkTheme ( ) ;
466+
467+ const modal = document . createElement ( 'div' ) ;
468+ modal . className = 'mermaid-fullscreen-modal' + ( darkTheme ? ' dark-theme' : '' ) ;
469+ modal . setAttribute ( 'role' , 'dialog' ) ;
470+ modal . setAttribute ( 'aria-modal' , 'true' ) ;
471+ modal . setAttribute ( 'aria-label' , 'Fullscreen diagram viewer' ) ;
472+ modal . innerHTML = `
473+ <button class="mermaid-fullscreen-close${ darkTheme ? ' dark-theme' : '' } " aria-label="Close fullscreen">✕</button>
474+ <div class="mermaid-fullscreen-content${ darkTheme ? ' dark-theme' : '' } "></div>
475+ ` ;
476+ document . body . appendChild ( modal ) ;
477+
478+ const modalContent = modal . querySelector ( '.mermaid-fullscreen-content' ) ;
479+ const closeBtn = modal . querySelector ( '.mermaid-fullscreen-close' ) ;
480+
481+ let previousScrollOffset = [ window . scrollX , window . scrollY ] ;
482+
483+ const closeModal = ( ) => {
484+ modal . classList . remove ( 'active' ) ;
485+ modalContent . innerHTML = '' ;
486+ document . body . style . overflow = ''
487+ window . scrollTo ( { left : previousScrollOffset [ 0 ] , top : previousScrollOffset [ 1 ] , behavior : 'instant' } ) ;
488+ } ;
489+
490+ closeBtn . addEventListener ( 'click' , closeModal ) ;
491+ modal . addEventListener ( 'click' , ( e ) => {
492+ if ( e . target === modal ) closeModal ( ) ;
493+ } ) ;
494+ document . addEventListener ( 'keydown' , ( e ) => {
495+ if ( e . key === 'Escape' && modal . classList . contains ( 'active' ) ) {
496+ closeModal ( ) ;
497+ }
498+ } ) ;
499+
500+ const allButtons = [ ] ;
501+
502+ document . querySelectorAll ( '.mermaid' ) . forEach ( ( mermaidDiv ) => {
503+ if ( mermaidDiv . parentNode . classList . contains ( 'mermaid-container' ) ||
504+ mermaidDiv . closest ( '.mermaid-fullscreen-modal' ) ) {
505+ return ;
506+ }
507+
508+ const container = document . createElement ( 'div' ) ;
509+ container . className = 'mermaid-container' ;
510+ mermaidDiv . parentNode . insertBefore ( container , mermaidDiv ) ;
511+ container . appendChild ( mermaidDiv ) ;
512+
513+ const fullscreenBtn = document . createElement ( 'button' ) ;
514+ fullscreenBtn . className = 'mermaid-fullscreen-btn' + ( darkTheme ? ' dark-theme' : '' ) ;
515+ fullscreenBtn . setAttribute ( 'aria-label' , 'View diagram in fullscreen' ) ;
516+ fullscreenBtn . textContent = '⛶' ;
517+ fullscreenBtn . style . opacity = '50%' ;
518+
519+ // Calculate dynamic position based on diagram's margin and padding
520+ const diagramStyle = window . getComputedStyle ( mermaidDiv ) ;
521+ const marginTop = parseFloat ( diagramStyle . marginTop ) || 0 ;
522+ const marginRight = parseFloat ( diagramStyle . marginRight ) || 0 ;
523+ const paddingTop = parseFloat ( diagramStyle . paddingTop ) || 0 ;
524+ const paddingRight = parseFloat ( diagramStyle . paddingRight ) || 0 ;
525+ fullscreenBtn . style . top = `${ marginTop + paddingTop + 4 } px` ;
526+ fullscreenBtn . style . right = `${ marginRight + paddingRight + 4 } px` ;
527+
528+ fullscreenBtn . addEventListener ( 'click' , ( ) => {
529+ previousScrollOffset = [ window . scroll , window . scrollY ] ;
530+ const clone = mermaidDiv . cloneNode ( true ) ;
531+ modalContent . innerHTML = '' ;
532+ modalContent . appendChild ( clone ) ;
533+
534+ const svg = clone . querySelector ( 'svg' ) ;
535+ if ( svg ) {
536+ svg . removeAttribute ( 'width' ) ;
537+ svg . removeAttribute ( 'height' ) ;
538+ svg . style . width = '100%' ;
539+ svg . style . height = 'auto' ;
540+ svg . style . maxWidth = '100%' ;
541+ svg . style . display = 'block' ;
542+
543+ setTimeout ( ( ) => {
544+ const g = svg . querySelector ( 'g' ) ;
545+ if ( g ) {
546+ var svgD3 = d3 . select ( svg ) ;
547+ svgD3 . html ( "<g class='wrapper'>" + svgD3 . html ( ) + "</g>" ) ;
548+ var inner = svgD3 . select ( "g" ) ;
549+ var zoom = d3 . zoom ( ) . on ( "zoom" , function ( event ) {
550+ inner . attr ( "transform" , event . transform ) ;
551+ } ) ;
552+ svgD3 . call ( zoom ) ;
553+ }
554+ } , 100 ) ;
555+ }
556+
557+ modal . classList . add ( 'active' ) ;
558+ document . body . style . overflow = 'hidden' ;
559+ } ) ;
560+
561+ container . appendChild ( fullscreenBtn ) ;
562+ allButtons . push ( fullscreenBtn ) ;
563+ } ) ;
564+
565+ // Update theme classes when theme changes
566+ const updateTheme = ( ) => {
567+ const dark = isDarkTheme ( ) ;
568+ allButtons . forEach ( btn => {
569+ if ( dark ) {
570+ btn . classList . add ( 'dark-theme' ) ;
571+ } else {
572+ btn . classList . remove ( 'dark-theme' ) ;
573+ }
574+ } ) ;
575+ if ( dark ) {
576+ modal . classList . add ( 'dark-theme' ) ;
577+ modalContent . classList . add ( 'dark-theme' ) ;
578+ closeBtn . classList . add ( 'dark-theme' ) ;
579+ } else {
580+ modal . classList . remove ( 'dark-theme' ) ;
581+ modalContent . classList . remove ( 'dark-theme' ) ;
582+ closeBtn . classList . remove ( 'dark-theme' ) ;
583+ }
584+ } ;
585+
586+ // Watch for theme changes
587+ const observer = new MutationObserver ( updateTheme ) ;
588+ observer . observe ( document . documentElement , {
589+ attributes : true ,
590+ attributeFilter : [ 'class' , 'style' , 'data-theme' ]
591+ } ) ;
592+ observer . observe ( document . body , {
593+ attributes : true ,
594+ attributeFilter : [ 'class' , 'style' ]
595+ } ) ;
303596} ;
304597
305598window . addEventListener ( "load" , load ) ;
0 commit comments