Browse Source

Merge pull request #10167 from weseek/fix/169030-mermaid-diagrams-are-not-displayed-when-svg-ids-are-duplicated

fix: Mermaid diagrams are not displayed when svg ids are duplicated
Yuki Takei 8 months ago
parent
commit
ea7836c2e3
1 changed files with 27 additions and 8 deletions
  1. 27 8
      apps/app/src/features/mermaid/components/MermaidViewer.tsx

+ 27 - 8
apps/app/src/features/mermaid/components/MermaidViewer.tsx

@@ -1,8 +1,12 @@
 import React, { useRef, useEffect, type JSX } from 'react';
 
 import mermaid from 'mermaid';
+import { v7 as uuidV7 } from 'uuid';
 
 import { useNextThemes } from '~/stores-universal/use-next-themes';
+import loggerFactory from '~/utils/logger';
+
+const logger = loggerFactory('growi:features:mermaid:MermaidViewer');
 
 type MermaidViewerProps = {
   value: string
@@ -16,22 +20,37 @@ export const MermaidViewer = React.memo((props: MermaidViewerProps): JSX.Element
   const ref = useRef<HTMLDivElement>(null);
 
   useEffect(() => {
-    if (ref.current != null && value != null) {
-      mermaid.initialize({
-        theme: isDarkMode ? 'dark' : undefined,
-      });
-      mermaid.run({ nodes: [ref.current] });
-    }
+    (async() => {
+      if (ref.current != null && value != null) {
+        mermaid.initialize({
+          theme: isDarkMode ? 'dark' : undefined,
+        });
+        try {
+          // Attempting to render multiple Mermaid diagrams using `mermaid.run` can cause duplicate SVG IDs.
+          // This is because it uses `Date.now()` for ID generation.
+          // ID generation logic: https://github.com/mermaid-js/mermaid/blob/5b241bbb97f81d37df8a84da523dfa53ac13bfd1/packages/mermaid/src/utils.ts#L755-L764
+          // Related issue: https://github.com/mermaid-js/mermaid/issues/4650
+          // Instead of `mermaid.run`, we use `mermaid.render` which allows us to assign a unique ID.
+          const id = `mermaid-${uuidV7()}`;
+          const { svg } = await mermaid.render(id, value, ref.current);
+          ref.current.innerHTML = svg;
+        }
+        catch (err) {
+          logger.error(err);
+        }
+      }
+    })();
   }, [isDarkMode, value]);
 
   return (
     value
       ? (
-        <div ref={ref} key={value as string}>
+        <div ref={ref} key={value}>
           {value}
         </div>
       )
-      : <div key={value as string}></div>
+      : <div key={value}></div>
   );
 });
+
 MermaidViewer.displayName = 'MermaidViewer';