Mermaid Integration #207
-
Is there an integration with mermaid? Or is there another way of making flowcharts with a language that is markdown like? |
Beta Was this translation helpful? Give feedback.
Replies: 8 comments 21 replies
-
Marp has no built-in integrations for any diagrams like mermaid.js, but you can try to extend Marp engine via compatible markdown-it plugins via if you are using Marp CLI. // engine.js
module.exports = ({ marp }) => marp.use(require('markdown-it-mermaid')) marp --engine ./engine.js your-markdown.md But even if could render, the diagram may not become a reliable one because mermaid.js will misdetect font size in Marp slide (inside SVG
That is still a blocker for mermaid integration for Marp Core. The most reliable way to place a mermaid.js diagram to Marp slide is using Markdown snippet generated by mermaid.live. It puts a diagram as pre-rendered SVG image so there is no broken texts. You also can use another "Diagrams as a Service" like kroki, that covers many diagram libraries including mermaid.js. Related |
Beta Was this translation helpful? Give feedback.
-
I've made the basic plugin example to render various diagrams at Currently we have no plan to integrate as the default feature of Marp to prevent wasting kroki.io instance by free riding. |
Beta Was this translation helpful? Give feedback.
This comment has been minimized.
This comment has been minimized.
-
@yhatt given the basic example that you linked, how would one go about setting a background color for the exported images? |
Beta Was this translation helpful? Give feedback.
-
FYI: I've released markdown-it-plugin on npm, embed kroki image. |
Beta Was this translation helpful? Give feedback.
-
Thank you! |
Beta Was this translation helpful? Give feedback.
-
I wrote my own way to render mermaid diagrams without using kroki.
class PostprocessMarpitEngine extends Marp {
/*
Custom Marp engine with async post-processing
Useful for async rendering
https://github.com/markdown-it/markdown-it/issues/248
*/
constructor(options, postprocess) {
super(options)
this.postprocess = postprocess
}
withPostprocess(postprocess) {
this.postprocess = postprocess
return this
}
async render(markdown, env = {}) {
const { html, css, comments } = super.render(markdown, env)
if (this.postprocess) {
return await this.postprocess(markdown, env, html, css, comments)
}
return { html, css, comments }
}
}
async function marpMermaidPlugin(md) {
/* Render mermaid fenced code block to a placeholder for post-processing */
// super fence block rule
const { fence } = md.renderer.rules
// final fence block rule
md.renderer.rules.fence = (tokens, idx, options, env, self) => {
const info = md.utils.unescapeAll(tokens[idx].info).trim()
if (info) {
const [_, lang, divOptions] = info.match(/(\w+)\s*(?:\{\s*(.+)\s*\})?/)
if (lang == "mermaid") {
const graphDefinition = tokens[idx].content
return `<div class="__mermaid" ${divOptions}>${graphDefinition}</div>`
}
}
return fence.call(self, tokens, idx, options, env, self)
}
}
async function renderSvgMermaid(graphDefinition) {
/*
Render mermaid graph to svg using puppeteer
*/
// don't forget to set PUPPETEER_EXECUTABLE_PATH
const browser = await puppeteer.launch({
headless: "new",
ignoreHTTPSErrors: true,
args: [
"--no-sandbox",
"--disable-setuid-sandbox",
'--disable-gpu',
'--full-memory-crash-report',
'--unlimited-storage',
],
// uncomment for advanced debugging
// dumpio: true,
})
const page = await browser.newPage()
page.on('console', (msg) => {
console.log(msg.text())
})
// add container to page
await page.evaluate(() => {
const graphDiv = document.createElement("div")
graphDiv.id = "graphDiv"
document.body.appendChild(graphDiv)
});
// add mermaid script to page
await page.addScriptTag({
path: path.join(__dirname, "vendor/mermaid.min.js"),
})
// render graph
const svg = await page.evaluate(async (graphDefinition) => {
const { svg } = await mermaid.render("graphDiv", graphDefinition);
return svg
}, graphDefinition)
await browser.close()
return svg;
}
async function processMermaidDiv(div) {
const graphDefinition = div.textContent
const svg = await renderSvgMermaid(graphDefinition)
div.innerHTML = svg
div.children[0].setAttribute("width", "100%")
div.children[0].setAttribute("height", "100%")
}
async function syncProcessMermaidDivs(mermaidDivs) {
/* Process mermaid div, one at a time */
for (let i = 0; i < mermaidDivs.length; i++) {
const div = mermaidDivs[i]
await processMermaidDiv(div)
console.log(`${i + 1}/${mermaidDivs.length}`)
}
}
export default {
engine: async (constructorOptions) =>
new PostprocessMarpitEngine(constructorOptions)
.use(marpMermaidPlugin)
.withPostprocess(async (markdown, env, html, css, comments) => {
// parse html to DOM
const doc = new jsdom.JSDOM(html)
// turn div elements with class "__mermaid" into processed svgs
const mermaidDivs = doc.window.document.querySelectorAll("div.__mermaid")
console.log(`Processing ${mermaidDivs.length} mermaid divs...`)
await syncProcessMermaidDivs(mermaidDivs)
const processedHtml = doc.window.document.documentElement.outerHTML
return { html: processedHtml, css, comments }
}),
} see my whole config here: https://github.com/GuillaumeDesforges/git-teaching-material/tree/5204102acaca9385d6fc0fbf7fb4f859d77e1021/presentation Unfortunately I cannot sandbox this still. It seems like the headless chromium instance crashes when trying to run mermaid without internet. |
Beta Was this translation helpful? Give feedback.
-
Just to add another option to mix, I ended us using this snippet at the top of my markdown file: <script src="https://unpkg.com/[email protected]/dist/pako_deflate.min.js" async></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
setTimeout(function() {
Array.from(document.querySelectorAll('.language-mermaid')).map(function(pre) {
const encoded = new TextEncoder('utf-8').encode(pre.textContent);
const compressed = window.pako.deflate(encoded, { level: 9, to: 'string' });
const urlencoded = btoa(compressed).replace(/\+/g, '-').replace(/\//g, '_');
const url = `https://kroki.io/mermaid/svg/${urlencoded}`;
const img = document.createElement('img');
img.src = url;
pre.replaceWith(img);
});
// Give marp some time to load itself etc...
}, 1000);
});
</script> I can then use code blocks tagged with mermaid to render diagrams (using kroki): ---
# Slide with diagram
This will render a `mermaid` diagram:
```mermaid
classDiagram
class Container {
__contains__()
}
|
Beta Was this translation helpful? Give feedback.
Marp has no built-in integrations for any diagrams like mermaid.js, but you can try to extend Marp engine via compatible markdown-it plugins via if you are using Marp CLI.
But even if could render, the diagram may not become a reliable one because mermaid.js will misdetect font size in Marp slide (inside SVG
<foreignObject>
), as pointed out in:That is still a blocker for mermaid integration for Marp Core.
The most reliable way to place a mermaid.js diagram to Marp slide is using Markdown…