feat: Mermaid diagram rendering in chat bubbles & improved streaming reliability#30
Conversation
Co-authored-by: OpenSource03 <29690431+OpenSource03@users.noreply.github.com>
Co-authored-by: OpenSource03 <29690431+OpenSource03@users.noreply.github.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ee2c60eee2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
src/components/MermaidDiagram.tsx
Outdated
| }, []); | ||
|
|
||
| useEffect(() => { | ||
| if (!containerRef.current) return; |
There was a problem hiding this comment.
Re-render diagrams after an initial parse failure
After mermaid.render() throws once, the component switches to the error branch, which does not render the element attached to containerRef; on later code updates (for example while assistant output is still streaming), this early return prevents setError(null) and mermaid.render() from running again, so the diagram stays stuck in the error fallback even when the Mermaid source becomes valid.
Useful? React with 👍 / 👎.
src/components/MermaidDiagram.tsx
Outdated
| mermaid.initialize({ | ||
| startOnLoad: false, | ||
| theme: "dark", | ||
| securityLevel: "loose", |
There was a problem hiding this comment.
Use strict Mermaid security for untrusted chat content
This initializes Mermaid with securityLevel: "loose" even though diagram text comes from model/user-generated chat content and is later injected as raw SVG; in this mode Mermaid permits less-restricted markup/link behavior, which can allow malicious diagram payloads to inject active content into the renderer context where privileged IPC bridges are exposed.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
Adds native Mermaid diagram rendering inside chat bubbles by detecting ```mermaid fenced code blocks and rendering them as SVG instead of syntax-highlighted text.
Changes:
- Detect
language-mermaidcode fences inMessageBubbleand render via a dedicatedMermaidDiagramcomponent. - Introduce
MermaidDiagramcomponent with header + copy button and an error fallback to raw code. - Add Mermaid dependency and basic component tests.
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/components/MessageBubble.tsx | Routes mermaid fenced code blocks to the new Mermaid renderer instead of syntax highlighting. |
| src/components/MermaidDiagram.tsx | New component that initializes Mermaid and injects rendered SVG with an error fallback UI. |
| src/components/MermaidDiagram.test.tsx | New tests asserting basic structure for the Mermaid component. |
| package.json | Adds mermaid dependency. |
| pnpm-lock.yaml | Locks Mermaid and its transitive dependencies. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/components/MermaidDiagram.tsx
Outdated
| async function renderDiagram() { | ||
| try { | ||
| setError(null); | ||
| const { svg: renderedSvg } = await mermaid.render(id, code); | ||
| setSvg(renderedSvg); |
|
|
||
| // Generate unique ID for this diagram | ||
| const id = `mermaid-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`; | ||
|
|
||
| async function renderDiagram() { | ||
| try { | ||
| setError(null); | ||
| const { svg: renderedSvg } = await mermaid.render(id, code); | ||
| setSvg(renderedSvg); | ||
| } catch (err) { | ||
| console.error("Mermaid rendering error:", err); | ||
| setError(err instanceof Error ? err.message : "Failed to render diagram"); | ||
| } | ||
| } | ||
|
|
||
| void renderDiagram(); |
| import { ThinkingBlock } from "./ThinkingBlock"; | ||
| import { CopyButton } from "./CopyButton"; | ||
| import { ImageLightbox } from "./ImageLightbox"; | ||
| import { MermaidDiagram } from "./MermaidDiagram"; |
src/components/MermaidDiagram.tsx
Outdated
| mermaid.initialize({ | ||
| startOnLoad: false, | ||
| theme: "dark", | ||
| securityLevel: "loose", | ||
| }); | ||
| mermaidInitialized = true; |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 14a0be555d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
src/components/MermaidDiagram.tsx
Outdated
| if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) { | ||
| return "dark"; |
There was a problem hiding this comment.
Respect explicit light mode when resolving Mermaid theme
This theme resolver treats any non-dark root class as a signal to fall back to OS preference, so when a user explicitly selects light mode on a dark OS, Mermaid diagrams still render with the dark theme and can keep following OS theme changes. In this app, explicit theme choice is represented by the <html> class (dark present/absent), so using matchMedia here overrides user intent for light mode.
Useful? React with 👍 / 👎.
Four targeted fixes for intermittent markdown rendering failures: 1. streaming-buffer: Replace mergeStreamingChunk overlap detection with simple concatenation for text deltas — the SDK sends pure incremental chunks, and the overlap heuristics produced false positives that ate markdown characters (|, `, \n, -) at token boundaries 2. useClaude: Capture buffer.current.messageId before queueing the assistant event's setMessages updater — message_stop could clear it via resetStreaming() before React processed the updater, silently dropping the authoritative final content snapshot 3. useStreamingTextReveal: Extend DOM surgery guards from CODE/PRE to include TABLE, UL, OL, BLOCKQUOTE, DL and descendants — prevents text-node splitting inside structurally volatile elements that can desync React's reconciler 4. MessageBubble: Remove IntersectionObserver lazy rendering gate — messages loaded from persistence or background store mounted with isVisible=false and the observer didn't fire reliably inside Radix ScrollArea, leaving content as plain text with literal # and * Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0c0d31bebe
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const language = match[1]; | ||
|
|
||
| // Render mermaid diagrams with MermaidDiagram component | ||
| if (language === "mermaid") { |
There was a problem hiding this comment.
Normalize Mermaid language tag before renderer dispatch
The Mermaid branch only runs when language === "mermaid", but markdown info strings are case-preserving, so fences like Mermaid or MERMAID produce language-Mermaid and fall back to plain syntax highlighting instead of diagram rendering. This makes Mermaid support inconsistent for equivalent tags and is easy to hit with model-generated markdown that capitalizes language names.
Useful? React with 👍 / 👎.
Summary
language-mermaidType of Change
Related Issues
Closes #178
How to Test
graph TD\nA[-->)Example Mermaid code block:
Screenshots / Screen Recording
Will be added by reviewer
Checklist
pnpm build)anytypes introducedms-*/me-*instead ofml-*/mr-*)wrap-break-wordshared/types/, notsrc/types/Original prompt