Add mermaid support - #13741
Conversation
| one --> two | ||
| three --> two | ||
| two --> c2 | ||
| ``` No newline at end of file |
There was a problem hiding this comment.
I added this for demo purposes. It needs to be removed before this PR lands.
There was a problem hiding this comment.
Code Review
This pull request adds support for rendering Mermaid diagrams from Markdown code blocks, introducing a custom Markdown block syntax, a node processor, and a client-side MermaidViewer component that dynamically loads the Mermaid library and handles theme updates. Feedback on the implementation highlights a security risk with using securityLevel: 'loose' (XSS), a bug where the diagram does not re-render when its content changes due to a missing didUpdateComponent implementation, an optimization opportunity to cache the imported module, and a potential CSP violation caused by using eval for dynamic imports.
| Future<MermaidModule> _importMermaid() { | ||
| final importFn = _eval('(url) => import(url)'.toJS) as JSFunction; | ||
| final promise = importFn.callAsFunction( | ||
| null, | ||
| 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs'.toJS, | ||
| ) as JSPromise<MermaidModule>; | ||
| return promise.toDart; | ||
| } |
There was a problem hiding this comment.
Every time a MermaidViewer component renders or the theme changes, _importMermaid() is called, which executes _eval and performs a dynamic import. This is inefficient and redundant when multiple diagrams are rendered on the same page.
Caching the Future<MermaidModule> in a private top-level variable ensures that the module is only imported once.
Future<MermaidModule>? _mermaidModuleCache;
Future<MermaidModule> _importMermaid() {
return _mermaidModuleCache ??= () {
final importFn = _eval('(url) => import(url)'.toJS) as JSFunction;
final promise = importFn.callAsFunction(
null,
'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs'.toJS,
) as JSPromise<MermaidModule>;
return promise.toDart;
}();
}|
I'm pretty far out of my element here using js interop and Jaspr, two things I'm only vaguely familiar with. I'd appreciate a thorough review @parlough ! |
|
Staged preview of the updated docs.flutter.dev site (updated for commit ecd987c): https://flutter-docs-prod--docs-pr13741-add-mermaid-support-ve3ntyr2.web.app |
|
Staged preview of the updated flutter.dev site (updated for commit ecd987c): https://flutter-dev-230821--www-pr13741-add-mermaid-support-fzptfisf.web.app |
| .node path { | ||
| stroke-width: 1.5px; | ||
| fill: var(--site-secondaryContainer-bgColor) !important; | ||
| } |
There was a problem hiding this comment.
I'll update these base styles to match our themes once I know whether this entire approach is reasonable.
|
As an alternative approach instead of using mermaid.js, we could use https://github.com/orestesgaolin/mermaid/tree/main/packages/mermaid_core to render the diagrams to SVGs at serve/build time. This is the demo https://roszkowski.dev/mermaid/ Seems like this is not published yet, but if we were to use it maybe we can convince @orestesgaolin to publish it :) |
I would much rather use this, I'll try it out |
|
Would you like me to publish mermaid to pub.dev? For now I only published katex https://pub.dev/packages/katex With mermaid I found that it sometimes does not well represent elk layouts and there are slight differences in rendering |
I'm not concerned with those small rendering differences. I have noticed one bug, the arrow label renders in the wrong spot when the flowchart is
If you plan on maintaining this library, we'd likely want to use it over the JSInterop solution (and we can find a way to contribute and help out). But if you don't plan on maintaining, thats okay, no pressure. |


Description of what this PR is changing or adding, and why:
Adds Mermaid diagram support. "Yeehaw" for code and version control instead of saving diagrams as images.
Presubmit checklist
of 80 characters or fewer.