2022-02-16 06:28:29 +03:00
import { isDarkTheme } from '../utils.js' ;
2022-12-25 20:17:48 +03:00
import { makeCodeCopyButton } from './codecopy.js' ;
2021-10-21 10:37:43 +03:00
const { mermaidMaxSourceCharacters } = window . config ;
2020-08-04 22:56:37 +03:00
2022-02-16 06:28:29 +03:00
const iframeCss = `
2022-10-31 23:57:31 +03:00
: root { color - scheme : normal }
2022-11-24 23:56:57 +03:00
body { margin : 0 ; padding : 0 ; overflow : hidden }
2022-02-16 06:28:29 +03:00
# mermaid { display : block ; margin : 0 auto }
` ;
2020-08-04 22:56:37 +03:00
function displayError ( el , err ) {
el . closest ( 'pre' ) . classList . remove ( 'is-loading' ) ;
const errorNode = document . createElement ( 'div' ) ;
2023-02-13 20:59:59 +03:00
errorNode . setAttribute ( 'class' , 'ui message error markup-block-error gt-mono' ) ;
2020-08-04 22:56:37 +03:00
errorNode . textContent = err . str || err . message || String ( err ) ;
el . closest ( 'pre' ) . before ( errorNode ) ;
}
2020-07-27 09:24:09 +03:00
2021-11-16 11:16:05 +03:00
export async function renderMermaid ( ) {
const els = document . querySelectorAll ( '.markup code.language-mermaid' ) ;
if ( ! els . length ) return ;
2020-07-27 09:24:09 +03:00
2021-10-19 10:23:58 +03:00
const { default : mermaid } = await import ( /* webpackChunkName: "mermaid" */ 'mermaid' ) ;
2020-07-27 09:24:09 +03:00
2020-08-04 22:56:37 +03:00
mermaid . initialize ( {
2022-02-16 06:28:29 +03:00
startOnLoad : false ,
theme : isDarkTheme ( ) ? 'dark' : 'neutral' ,
2020-07-27 09:24:09 +03:00
securityLevel : 'strict' ,
} ) ;
for ( const el of els ) {
2022-02-16 06:28:29 +03:00
const source = el . textContent ;
if ( mermaidMaxSourceCharacters >= 0 && source . length > mermaidMaxSourceCharacters ) {
displayError ( el , new Error ( ` Mermaid source of ${ source . length } characters exceeds the maximum allowed length of ${ mermaidMaxSourceCharacters } . ` ) ) ;
2020-08-04 22:56:37 +03:00
continue ;
}
try {
2023-03-04 08:39:07 +03:00
await mermaid . parse ( source ) ;
2020-08-04 22:56:37 +03:00
} catch ( err ) {
displayError ( el , err ) ;
el . closest ( 'pre' ) . classList . remove ( 'is-loading' ) ;
continue ;
}
try {
2022-02-16 06:28:29 +03: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 08:39:07 +03:00
const { svg } = await mermaid . render ( 'mermaid' , source ) ;
const heightStr = ( svg . match ( /viewBox="(.+?)"/ ) || [ '' , '' ] ) [ 1 ] . split ( /\s+/ ) [ 3 ] ;
if ( ! heightStr ) return displayError ( el , new Error ( 'Could not determine chart height' ) ) ;
const iframe = document . createElement ( 'iframe' ) ;
iframe . classList . add ( 'markup-render' ) ;
iframe . sandbox = 'allow-scripts' ;
iframe . style . height = ` ${ Math . ceil ( parseFloat ( heightStr ) ) } px ` ;
iframe . srcdoc = ` <html><head><style> ${ iframeCss } </style></head><body> ${ svg } </body></html> ` ;
const mermaidBlock = document . createElement ( 'div' ) ;
mermaidBlock . classList . add ( 'mermaid-block' ) ;
mermaidBlock . append ( iframe ) ;
const btn = makeCodeCopyButton ( ) ;
btn . setAttribute ( 'data-clipboard-text' , source ) ;
mermaidBlock . append ( btn ) ;
el . closest ( 'pre' ) . replaceWith ( mermaidBlock ) ;
2020-08-04 22:56:37 +03:00
} catch ( err ) {
displayError ( el , err ) ;
}
2020-07-27 09:24:09 +03:00
}
}