Skip to content

Commit 74e539f

Browse files
mrubensmehmetsunkur
authored andcommitted
Handle Mermaid validation errors (RooCodeInc#3112)
* Handle Mermaid validation errors * PR feedback * More PR feedback
1 parent 917db39 commit 74e539f

File tree

17 files changed

+156
-10
lines changed

17 files changed

+156
-10
lines changed

webview-ui/src/components/common/MermaidBlock.tsx

Lines changed: 92 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import mermaid from "mermaid"
33
import { useDebounceEffect } from "@src/utils/useDebounceEffect"
44
import styled from "styled-components"
55
import { vscode } from "@src/utils/vscode"
6+
import { useAppTranslation } from "@src/i18n/TranslationContext"
7+
import { useCopyToClipboard } from "@src/utils/clipboard"
8+
import CodeBlock from "./CodeBlock"
69

710
const MERMAID_THEME = {
811
background: "#1e1e1e", // VS Code dark theme background
@@ -81,10 +84,15 @@ interface MermaidBlockProps {
8184
export default function MermaidBlock({ code }: MermaidBlockProps) {
8285
const containerRef = useRef<HTMLDivElement>(null)
8386
const [isLoading, setIsLoading] = useState(false)
87+
const [error, setError] = useState<string | null>(null)
88+
const [isErrorExpanded, setIsErrorExpanded] = useState(false)
89+
const { showCopyFeedback, copyWithFeedback } = useCopyToClipboard()
90+
const { t } = useAppTranslation()
8491

8592
// 1) Whenever `code` changes, mark that we need to re-render a new chart
8693
useEffect(() => {
8794
setIsLoading(true)
95+
setError(null)
8896
}, [code])
8997

9098
// 2) Debounce the actual parse/render
@@ -93,12 +101,10 @@ export default function MermaidBlock({ code }: MermaidBlockProps) {
93101
if (containerRef.current) {
94102
containerRef.current.innerHTML = ""
95103
}
104+
96105
mermaid
97-
.parse(code, { suppressErrors: true })
98-
.then((isValid) => {
99-
if (!isValid) {
100-
throw new Error("Invalid or incomplete Mermaid code")
101-
}
106+
.parse(code)
107+
.then(() => {
102108
const id = `mermaid-${Math.random().toString(36).substring(2)}`
103109
return mermaid.render(id, code)
104110
})
@@ -109,7 +115,7 @@ export default function MermaidBlock({ code }: MermaidBlockProps) {
109115
})
110116
.catch((err) => {
111117
console.warn("Mermaid parse/render failed:", err)
112-
containerRef.current!.innerHTML = code.replace(/</g, "&lt;").replace(/>/g, "&gt;")
118+
setError(err.message || "Failed to render Mermaid diagram")
113119
})
114120
.finally(() => {
115121
setIsLoading(false)
@@ -139,12 +145,71 @@ export default function MermaidBlock({ code }: MermaidBlockProps) {
139145
}
140146
}
141147

148+
// Copy functionality handled directly through the copyWithFeedback utility
149+
142150
return (
143151
<MermaidBlockContainer>
144-
{isLoading && <LoadingMessage>Generating mermaid diagram...</LoadingMessage>}
145-
146-
{/* The container for the final <svg> or raw code. */}
147-
<SvgContainer onClick={handleClick} ref={containerRef} $isLoading={isLoading} />
152+
{isLoading && <LoadingMessage>{t("common:mermaid.loading")}</LoadingMessage>}
153+
154+
{error ? (
155+
<div style={{ marginTop: "0px", overflow: "hidden", marginBottom: "8px" }}>
156+
<div
157+
style={{
158+
borderBottom: isErrorExpanded ? "1px solid var(--vscode-editorGroup-border)" : "none",
159+
fontWeight: "normal",
160+
fontSize: "var(--vscode-font-size)",
161+
color: "var(--vscode-editor-foreground)",
162+
display: "flex",
163+
alignItems: "center",
164+
justifyContent: "space-between",
165+
cursor: "pointer",
166+
}}
167+
onClick={() => setIsErrorExpanded(!isErrorExpanded)}>
168+
<div
169+
style={{
170+
display: "flex",
171+
alignItems: "center",
172+
gap: "10px",
173+
flexGrow: 1,
174+
}}>
175+
<span
176+
className="codicon codicon-warning"
177+
style={{
178+
color: "var(--vscode-editorWarning-foreground)",
179+
opacity: 0.8,
180+
fontSize: 16,
181+
marginBottom: "-1.5px",
182+
}}></span>
183+
<span style={{ fontWeight: "bold" }}>{t("common:mermaid.render_error")}</span>
184+
</div>
185+
<div style={{ display: "flex", alignItems: "center" }}>
186+
<CopyButton
187+
onClick={(e) => {
188+
e.stopPropagation()
189+
copyWithFeedback(code, e)
190+
}}>
191+
<span className={`codicon codicon-${showCopyFeedback ? "check" : "copy"}`}></span>
192+
</CopyButton>
193+
<span className={`codicon codicon-chevron-${isErrorExpanded ? "up" : "down"}`}></span>
194+
</div>
195+
</div>
196+
{isErrorExpanded && (
197+
<div
198+
style={{
199+
padding: "8px",
200+
backgroundColor: "var(--vscode-editor-background)",
201+
borderTop: "none",
202+
}}>
203+
<div style={{ marginBottom: "8px", color: "var(--vscode-descriptionForeground)" }}>
204+
{error}
205+
</div>
206+
<CodeBlock language="mermaid" source={code} />
207+
</div>
208+
)}
209+
</div>
210+
) : (
211+
<SvgContainer onClick={handleClick} ref={containerRef} $isLoading={isLoading} />
212+
)}
148213
</MermaidBlockContainer>
149214
)
150215
}
@@ -212,6 +277,23 @@ const LoadingMessage = styled.div`
212277
font-size: 0.9em;
213278
`
214279

280+
const CopyButton = styled.button`
281+
padding: 3px;
282+
height: 24px;
283+
margin-right: 4px;
284+
color: var(--vscode-editor-foreground);
285+
display: flex;
286+
align-items: center;
287+
justify-content: center;
288+
background: transparent;
289+
border: none;
290+
cursor: pointer;
291+
292+
&:hover {
293+
opacity: 0.8;
294+
}
295+
`
296+
215297
interface SvgContainerProps {
216298
$isLoading: boolean
217299
}

webview-ui/src/i18n/locales/ca/common.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,9 @@
66
},
77
"ui": {
88
"search_placeholder": "Cerca..."
9+
},
10+
"mermaid": {
11+
"loading": "Generant diagrama mermaid...",
12+
"render_error": "No es pot renderitzar el diagrama"
913
}
1014
}

webview-ui/src/i18n/locales/de/common.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,9 @@
66
},
77
"ui": {
88
"search_placeholder": "Suchen..."
9+
},
10+
"mermaid": {
11+
"loading": "Mermaid-Diagramm wird generiert...",
12+
"render_error": "Diagramm kann nicht gerendert werden"
913
}
1014
}

webview-ui/src/i18n/locales/en/common.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,9 @@
66
},
77
"ui": {
88
"search_placeholder": "Search..."
9+
},
10+
"mermaid": {
11+
"loading": "Generating mermaid diagram...",
12+
"render_error": "Unable to Render Diagram"
913
}
1014
}

webview-ui/src/i18n/locales/es/common.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,9 @@
66
},
77
"ui": {
88
"search_placeholder": "Buscar..."
9+
},
10+
"mermaid": {
11+
"loading": "Generando diagrama mermaid...",
12+
"render_error": "No se puede renderizar el diagrama"
913
}
1014
}

webview-ui/src/i18n/locales/fr/common.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,9 @@
66
},
77
"ui": {
88
"search_placeholder": "Rechercher..."
9+
},
10+
"mermaid": {
11+
"loading": "Génération du diagramme mermaid...",
12+
"render_error": "Impossible de rendre le diagramme"
913
}
1014
}

webview-ui/src/i18n/locales/hi/common.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,9 @@
66
},
77
"ui": {
88
"search_placeholder": "खोजें..."
9+
},
10+
"mermaid": {
11+
"loading": "मरमेड डायग्राम जनरेट हो रहा है...",
12+
"render_error": "डायग्राम रेंडर नहीं किया जा सकता"
913
}
1014
}

webview-ui/src/i18n/locales/it/common.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,9 @@
66
},
77
"ui": {
88
"search_placeholder": "Cerca..."
9+
},
10+
"mermaid": {
11+
"loading": "Generazione del diagramma mermaid...",
12+
"render_error": "Impossibile renderizzare il diagramma"
913
}
1014
}

webview-ui/src/i18n/locales/ja/common.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,9 @@
66
},
77
"ui": {
88
"search_placeholder": "検索..."
9+
},
10+
"mermaid": {
11+
"loading": "Mermaidダイアグラムを生成中...",
12+
"render_error": "ダイアグラムをレンダリングできません"
913
}
1014
}

webview-ui/src/i18n/locales/ko/common.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,9 @@
66
},
77
"ui": {
88
"search_placeholder": "검색..."
9+
},
10+
"mermaid": {
11+
"loading": "머메이드 다이어그램 생성 중...",
12+
"render_error": "다이어그램을 렌더링할 수 없음"
913
}
1014
}

0 commit comments

Comments
 (0)