Browse Source

[Feature] Support loading for mermaid. (#6004)

Co-authored-by: 靖谦 <jingqian@kaiwu.cloud>
Jinq Qian 9 months ago
parent
commit
9f16739518
2 changed files with 27 additions and 15 deletions
  1. 1 1
      web/app/components/base/markdown.tsx
  2. 26 14
      web/app/components/base/mermaid/index.tsx

+ 1 - 1
web/app/components/base/markdown.tsx

@@ -103,7 +103,7 @@ const useLazyLoad = (ref: RefObject<Element>): boolean => {
 // visit https://reactjs.org/docs/error-decoder.html?invariant=185 for the full message
 // or use the non-minified dev environment for full errors and additional helpful warnings.
 const CodeBlock: CodeComponent = memo(({ inline, className, children, ...props }) => {
-  const [isSVG, setIsSVG] = useState(false)
+  const [isSVG, setIsSVG] = useState(true)
   const match = /language-(\w+)/.exec(className || '')
   const language = match?.[1]
   const languageShowName = getCorrectCapitalizationLanguageName(language || '')

+ 26 - 14
web/app/components/base/mermaid/index.tsx

@@ -1,6 +1,7 @@
 import React, { useEffect, useRef, useState } from 'react'
 import mermaid from 'mermaid'
 import CryptoJS from 'crypto-js'
+import LoadingAnim from '@/app/components/base/chat/chat/loading-anim'
 
 let mermaidAPI: any
 mermaidAPI = null
@@ -23,12 +24,24 @@ const style = {
   overflow: 'auto',
 }
 
+const svgToBase64 = (svgGraph: string) => {
+  const svgBytes = new TextEncoder().encode(svgGraph)
+  const blob = new Blob([svgBytes], { type: 'image/svg+xml;charset=utf-8' })
+  return new Promise((resolve, reject) => {
+    const reader = new FileReader()
+    reader.onloadend = () => resolve(reader.result)
+    reader.onerror = reject
+    reader.readAsDataURL(blob)
+  })
+}
+
 const Flowchart = React.forwardRef((props: {
   PrimitiveCode: string
 }, ref) => {
   const [svgCode, setSvgCode] = useState(null)
   const chartId = useRef(`flowchart_${CryptoJS.MD5(props.PrimitiveCode).toString()}`)
-  const [isRender, setIsRender] = useState(true)
+  const [isRender, setIsRender] = useState(false)
+  const [isLoading, setIsLoading] = useState(true)
 
   const clearFlowchartCache = () => {
     for (let i = localStorage.length - 1; i >= 0; --i) {
@@ -43,14 +56,19 @@ const Flowchart = React.forwardRef((props: {
       const cachedSvg: any = localStorage.getItem(chartId.current)
       if (cachedSvg) {
         setSvgCode(cachedSvg)
+        setIsLoading(false)
         return
       }
 
       if (typeof window !== 'undefined' && mermaidAPI) {
         const svgGraph = await mermaidAPI.render(chartId.current, PrimitiveCode)
-        // eslint-disable-next-line @typescript-eslint/no-use-before-define
+        const dom = new DOMParser().parseFromString(svgGraph.svg, 'text/xml')
+        if (!dom.querySelector('g.main'))
+          throw new Error('empty svg')
+
         const base64Svg: any = await svgToBase64(svgGraph.svg)
         setSvgCode(base64Svg)
+        setIsLoading(false)
         if (chartId.current && base64Svg)
           localStorage.setItem(chartId.current, base64Svg)
       }
@@ -62,17 +80,6 @@ const Flowchart = React.forwardRef((props: {
     }
   }
 
-  const svgToBase64 = (svgGraph: string) => {
-    const svgBytes = new TextEncoder().encode(svgGraph)
-    const blob = new Blob([svgBytes], { type: 'image/svg+xml;charset=utf-8' })
-    return new Promise((resolve, reject) => {
-      const reader = new FileReader()
-      reader.onloadend = () => resolve(reader.result)
-      reader.onerror = reject
-      reader.readAsDataURL(blob)
-    })
-  }
-
   const handleReRender = () => {
     setIsRender(false)
     setSvgCode(null)
@@ -99,10 +106,15 @@ const Flowchart = React.forwardRef((props: {
     <div ref={ref}>
       {
         isRender
-          && <div id={chartId.current} className="mermaid" style={style}>
+          && <div className="mermaid" style={style}>
             {svgCode && <img src={svgCode} style={{ width: '100%', height: 'auto' }} alt="Mermaid chart" />}
           </div>
       }
+      {isLoading
+        && <div className='py-4 px-[26px]'>
+          <LoadingAnim type='text' />
+        </div>
+      }
     </div>
   )
 })