2022-02-16 04:28:29 +01:00
import { isDarkTheme } from '../utils.js' ;
2022-12-25 18:17:48 +01:00
import { makeCodeCopyButton } from './codecopy.js' ;
2023-04-17 12:10:22 +02:00
import { displayError } from './common.js' ;
2022-12-25 18:17:48 +01:00
2021-10-21 15:37:43 +08:00
const { mermaidMaxSourceCharacters } = window . config ;
2020-08-04 21:56:37 +02:00
2023-10-08 05:20:12 +02:00
// margin removal is for https://github.com/mermaid-js/mermaid/issues/4907
2023-04-17 12:10:22 +02:00
const iframeCss = ` :root {color-scheme: normal}
body { margin : 0 ; padding : 0 ; overflow : hidden }
2023-10-08 05:20:12 +02:00
# mermaid { display : block ; margin : 0 auto }
blockquote , dd , dl , figure , h1 , h2 , h3 , h4 , h5 , h6 , hr , p , pre { margin : 0 } ` ;
2020-07-27 08:24:09 +02:00
2021-11-16 09:16:05 +01:00
export async function renderMermaid ( ) {
const els = document . querySelectorAll ( '.markup code.language-mermaid' ) ;
if ( ! els . length ) return ;
2020-07-27 08:24:09 +02:00
2021-10-19 09:23:58 +02:00
const { default : mermaid } = await import ( /* webpackChunkName: "mermaid" */ 'mermaid' ) ;
2020-07-27 08:24:09 +02:00
2020-08-04 21:56:37 +02:00
mermaid . initialize ( {
2022-02-16 04:28:29 +01:00
startOnLoad : false ,
theme : isDarkTheme ( ) ? 'dark' : 'neutral' ,
2020-07-27 08:24:09 +02:00
securityLevel : 'strict' ,
} ) ;
for ( const el of els ) {
2023-04-17 12:10:22 +02:00
const pre = el . closest ( 'pre' ) ;
if ( pre . hasAttribute ( 'data-render-done' ) ) continue ;
2022-02-16 04:28:29 +01:00
2023-04-17 12:10:22 +02:00
const source = el . textContent ;
2022-02-16 04:28:29 +01:00
if ( mermaidMaxSourceCharacters >= 0 && source . length > mermaidMaxSourceCharacters ) {
2023-04-17 12:10:22 +02:00
displayError ( pre , new Error ( ` Mermaid source of ${ source . length } characters exceeds the maximum allowed length of ${ mermaidMaxSourceCharacters } . ` ) ) ;
2020-08-04 21:56:37 +02:00
continue ;
}
try {
2023-03-04 00:39:07 -05:00
await mermaid . parse ( source ) ;
2020-08-04 21:56:37 +02:00
} catch ( err ) {
2023-04-17 12:10:22 +02:00
displayError ( pre , err ) ;
2020-08-04 21:56:37 +02:00
continue ;
}
try {
2022-02-16 04:28:29 +01:00
// can't use bindFunctions here because we can't cross the iframe boundary. This
// means js-based interactions won't work but they aren't intended to work either
2023-03-04 00:39:07 -05:00
const { svg } = await mermaid . render ( 'mermaid' , source ) ;
const iframe = document . createElement ( 'iframe' ) ;
2024-03-05 06:29:32 +01:00
iframe . classList . add ( 'markup-render' , 'tw-invisible' ) ;
2023-03-04 00:39:07 -05:00
iframe . srcdoc = ` <html><head><style> ${ iframeCss } </style></head><body> ${ svg } </body></html> ` ;
const mermaidBlock = document . createElement ( 'div' ) ;
2024-03-24 19:23:38 +01:00
mermaidBlock . classList . add ( 'mermaid-block' , 'is-loading' , 'tw-hidden' ) ;
2023-03-04 00:39:07 -05:00
mermaidBlock . append ( iframe ) ;
const btn = makeCodeCopyButton ( ) ;
btn . setAttribute ( 'data-clipboard-text' , source ) ;
mermaidBlock . append ( btn ) ;
2023-04-17 12:10:22 +02:00
iframe . addEventListener ( 'load' , ( ) => {
pre . replaceWith ( mermaidBlock ) ;
2024-03-24 19:23:38 +01:00
mermaidBlock . classList . remove ( 'tw-hidden' ) ;
2023-04-17 12:10:22 +02:00
iframe . style . height = ` ${ iframe . contentWindow . document . body . clientHeight } px ` ;
setTimeout ( ( ) => { // avoid flash of iframe background
mermaidBlock . classList . remove ( 'is-loading' ) ;
2024-03-05 06:29:32 +01:00
iframe . classList . remove ( 'tw-invisible' ) ;
2023-04-17 12:10:22 +02:00
} , 0 ) ;
} ) ;
document . body . append ( mermaidBlock ) ;
2020-08-04 21:56:37 +02:00
} catch ( err ) {
2023-04-17 12:10:22 +02:00
displayError ( pre , err ) ;
2020-08-04 21:56:37 +02:00
}
2020-07-27 08:24:09 +02:00
}
}