code.gitea.io/gitea@v1.21.7/web_src/js/markup/mermaid.js (about) 1 import {isDarkTheme} from '../utils.js'; 2 import {makeCodeCopyButton} from './codecopy.js'; 3 import {displayError} from './common.js'; 4 5 const {mermaidMaxSourceCharacters} = window.config; 6 7 // margin removal is for https://github.com/mermaid-js/mermaid/issues/4907 8 const iframeCss = `:root {color-scheme: normal} 9 body {margin: 0; padding: 0; overflow: hidden} 10 #mermaid {display: block; margin: 0 auto} 11 blockquote, dd, dl, figure, h1, h2, h3, h4, h5, h6, hr, p, pre {margin: 0}`; 12 13 export async function renderMermaid() { 14 const els = document.querySelectorAll('.markup code.language-mermaid'); 15 if (!els.length) return; 16 17 const {default: mermaid} = await import(/* webpackChunkName: "mermaid" */'mermaid'); 18 19 mermaid.initialize({ 20 startOnLoad: false, 21 theme: isDarkTheme() ? 'dark' : 'neutral', 22 securityLevel: 'strict', 23 }); 24 25 for (const el of els) { 26 const pre = el.closest('pre'); 27 if (pre.hasAttribute('data-render-done')) continue; 28 29 const source = el.textContent; 30 if (mermaidMaxSourceCharacters >= 0 && source.length > mermaidMaxSourceCharacters) { 31 displayError(pre, new Error(`Mermaid source of ${source.length} characters exceeds the maximum allowed length of ${mermaidMaxSourceCharacters}.`)); 32 continue; 33 } 34 35 try { 36 await mermaid.parse(source); 37 } catch (err) { 38 displayError(pre, err); 39 continue; 40 } 41 42 try { 43 // can't use bindFunctions here because we can't cross the iframe boundary. This 44 // means js-based interactions won't work but they aren't intended to work either 45 const {svg} = await mermaid.render('mermaid', source); 46 47 const iframe = document.createElement('iframe'); 48 iframe.classList.add('markup-render', 'gt-invisible'); 49 iframe.srcdoc = `<html><head><style>${iframeCss}</style></head><body>${svg}</body></html>`; 50 51 const mermaidBlock = document.createElement('div'); 52 mermaidBlock.classList.add('mermaid-block', 'is-loading', 'gt-hidden'); 53 mermaidBlock.append(iframe); 54 55 const btn = makeCodeCopyButton(); 56 btn.setAttribute('data-clipboard-text', source); 57 mermaidBlock.append(btn); 58 59 iframe.addEventListener('load', () => { 60 pre.replaceWith(mermaidBlock); 61 mermaidBlock.classList.remove('gt-hidden'); 62 iframe.style.height = `${iframe.contentWindow.document.body.clientHeight}px`; 63 setTimeout(() => { // avoid flash of iframe background 64 mermaidBlock.classList.remove('is-loading'); 65 iframe.classList.remove('gt-invisible'); 66 }, 0); 67 }); 68 69 document.body.append(mermaidBlock); 70 } catch (err) { 71 displayError(pre, err); 72 } 73 } 74 }