mermaid-js / mermaid

Generation of diagrams like flowcharts or sequence diagrams from text in a similar manner as markdown
https://mermaid.js.org
MIT License
70.13k stars 6.23k forks source link

Support rendering in a web worker #2426

Open bheads opened 2 years ago

bheads commented 2 years ago

Is your feature request related to a problem? Please describe. Our internal app often generates large graphs, it would be nice to run the render in a web work to not lock up the page.

Describe the solution you'd like Support rendering in a web worker

v4dkou commented 2 years ago

@bheads Mermaid's rendering uses the DOM (through the D3.js library)

There's a proof of concept of moving D3-based SVG generation to web workers that might work https://github.com/chrisahardie/d3-svg-chart-in-web-worker

Quick glance through Gantt chart rendering code leaves me thinking there should be little to no errors if you'd try to use in inside jsdom context. Didn't try it though. https://github.com/mermaid-js/mermaid/blob/develop/src/diagrams/gantt/ganttRenderer.js

fabiospampinato commented 2 years ago

@knsv sorry for the mention, are you aware of any successful attempt at running mermaid in a worker? I need to move mermaid out of my main thread for security and performance isolation.

SamMousa commented 1 month ago

What about moving it to an iframe? As far as I can tell you can do your rendering in there using the normal DOM API and then just transfer the resulting svg string back.

I just tested this in a Proof of Concept in a SvelteKit app:

<script lang="ts">
    import { onMount } from "svelte";
    import mermaid from 'mermaid';

    let logContainer: HTMLUListElement;

    function log(message: string) {
        const line = document.createElement('li');
        line.innerText = message;
        logContainer.append(line);
    }
    onMount(() => {
        const channel = new BroadcastChannel('mermaid');
        channel.addEventListener('message', async (ev) => {
            log(ev.data);
            const result = await mermaid.render('test', ev.data);
            channel.postMessage(result.svg);

        })
    })
</script>
This is a worker, we load it in an iframe for now.
<ul bind:this={logContainer}>

</ul>

This is a trivial and ugly implementation. It listens for diagram strings on a broadcast channel and sends back svg strings on the same channel. It will probably hard crash if you open multiple tabs with this code.

But... it works and should even be trivial to scale.