feat(web): render mermaid fences as diagrams in chat - #8533
feat(web): render mermaid fences as diagrams in chat#8533rishi-chauhan wants to merge 9 commits into
Conversation
Agents often emit mermaid flowcharts as fenced code. Those blocks were only syntax-highlighted, so users had to copy them out of T3 to see a diagram. Render mermaid and mmd fences as SVG after a message finishes streaming, keep copy on the source, and offer a toggle back to the highlighted fence. Load mermaid only when a fence appears so ordinary chats do not pay for the library. Collapsed plan previews keep a started fence intact through its closing marker so a truncated mermaid block is not parsed as a broken diagram.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
There was a problem hiding this comment.
Reviewed the mermaid chat-diagram changes against the shared chat-markdown chrome. Three concrete issues on the new rendering surface: a dead max-width override that defeats the diagram's own scroll container, prose margins leaking into the code-block chrome from the new <p> placeholders, and an unstyled horizontal scrollbar next to the styled pre scrollbar. Everything else (primitive reuse for the toggle, clipboard contract, streaming gate) looks consistent.
Posted via Macroscope — UI Consistency
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This is a substantial user-facing feature that adds Mermaid rendering and changes completed fences, clipboard behavior, and collapsed plan previews across shared markdown surfaces. Rendering is enabled by default for existing You can add or adjust custom eligibility rules. Learn more. |
Review found truncated quoted/indented fences, empty copy from inside the SVG, prose margins on placeholder paragraphs, an unstyled horizontal scrollbar, and mermaid's inline max-width beating our CSS. Complete fences after blockquote and list prefixes, drop a fence that never closes, restore mermaid source from the diagram wrapper on copy, and match the existing chat-markdown scrollbar and diagram sizing.
There was a problem hiding this comment.
One finding: the new mermaid clipboard-restore branches in markdown-clipboard.ts are unreachable from the only production caller, so selecting inside a rendered diagram still copies SVG label text. The earlier findings (prose-margin leak on the placeholder/error rows, missing .chat-markdown scrollbar treatment on the diagram scroll container, and the max-width override) all look addressed.
Posted via Macroscope — UI Consistency
chatMarkdownClipboardPayload serializes a detached cloneContents() tree, so closest() cannot reach the diagram wrapper. Resolve data-markdown-copy from the live common ancestor instead.
There was a problem hiding this comment.
One finding on the mermaid clipboard path. The earlier live-ancestor fix looks right for text/plain; the text/html flavor for the same selection now ends up empty.
Posted via Macroscope — UI Consistency
Selecting inside a rendered diagram strips the svg from text/html, so rich-paste targets insert nothing. Put the mermaid fence in a pre/code block instead, matching how those fences copied before diagrams.
There was a problem hiding this comment.
Two consistency/regression findings in the new mermaid rendering path. Everything flagged in earlier runs (prose-margin leaks, scrollbar treatment, dead clipboard branches, empty HTML flavor for diagram-only selections) looks addressed.
Posted via Macroscope — UI Consistency
A whole-message copy skipped the diagram-only clipboard branch and stripped the svg, so rich-paste dropped the fence. Replace mermaid hosts with a pre/code block before sanitizing. Drop the svg max-width !important override so types that still use mermaid's default useMaxWidth stay fit-to-width instead of stretching.
There was a problem hiding this comment.
One finding on the new mermaid renderer: failed renders leave mermaid's temporary error-diagram element attached to document.body.
Posted via Macroscope — UI Consistency
Failed mermaid.render() draws a syntax-error diagram in a unique temp node and throws before cleanup. suppressErrorRendering lets mermaid remove that node and reject, which MermaidDiagram already surfaces as an error.
There was a problem hiding this comment.
One finding: rendered mermaid SVGs are not cached across remounts, so every scroll-back in the virtualized message feed replays the async render (placeholder flash + height jump). Details inline.
Posted via Macroscope — UI Consistency
Virtualized chat remounts replay mermaid.render and flash the loading placeholder. Cache successful svgs like highlighted code, and clear a rejected dynamic import so a later render can load the chunk.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 1c53982. Configure here.
There was a problem hiding this comment.
Reviewed the mermaid chat-diagram changes for UI consistency. The earlier findings (render cache, suppressErrorRendering, useMaxWidth handling instead of a max-width: none !important override, mermaid-aware clipboard HTML, non-<p> placeholders, shared code-block scrollbar treatment) are addressed. One residual layout-stability issue remains on theme switching.
Posted via Macroscope — UI Consistency
Cached svg markup reused mermaid's t3mermaid ids, so a second visible copy of the same fence could steal markers via url(#…). Clone those ids on each cache read. Keep the last svg while a theme re-render is in flight, and remount on fence content change so the placeholder is only for new diagrams.
|
this would be a crazy good feature, commenting for support ! |
|
+1 on this. Mermaid diagrams show up constantly in agent output (Cursor, Claude Code, Codex all emit them), and rendering them inline instead of as raw source is a big readability win for the chat view. @t3dotgg @juliusmarminge any chance this can get a look? Would love to see mermaid rendering land. |
|
Note: GPT-5.6 on behalf of shivam (@shivamhwp). Thanks for the work on this, including the caching, source toggle, and follow-up fixes. We are not planning to add Mermaid rendering to T3 Code right now. Even with lazy loading and caching, Mermaid adds a large frontend dependency and moves diagram parsing, graph layout, SVG generation, and DOM work onto the client. That creates performance and memory risks in long, virtualized conversations. The benefit does not justify that cost for us right now, especially while mobile would still render the same content differently. We are closing this based on product direction. This is not a request to revise the implementation. |

What Changed
This change is to support Cursor cli. Cursor cli and its models generate mermaid flowcharts frequently. Now that t3code is planning to support Cursor cli, it PR would help.
Chat markdown now renders mermaid and
mmdfences as diagrams instead of highlighted source.Desktop inherits this through the web UI. Mobile is unchanged.
Why
Agents already emit mermaid flowcharts in replies and plans. T3 only syntax-highlighted that text, so users had to copy it into another tool to see the picture. Rendering in place is the obvious chat behavior.
The collapsed-plan path used a line budget that could split an opening mermaid fence from its closing marker. Mermaid then parsed a fragment and threw (for example
got '1'). Completing the fence avoids that without hiding a plan that starts with a diagram.UI Changes
Collapsed plan view:

In chat:

Test plan
Checklist
Note
Render mermaid fences as diagrams in chat messages
MermaidDiagramcomponent andmermaidRendererutility that asynchronously renders mermaid/mmd code fences into cached, theme-aware SVGs with unique ids per insertionMarkdownCodeBlockin ChatMarkdown.tsx shows a diagram/source toggle (EyeIcon/Code2Icon) for mermaid fences when the message is not streaming; during streaming, fences stay as source code. Render failures fall back to source view with an inline error alertbuildCollapsedProposedPlanPreviewMarkdownin proposedPlan.ts tracks open fenced blocks so previews no longer truncate mid-fence; unclosed fences are dropped with an overflow markermermaid(^11.17.2) as a new dependency toapps/web; dynamically imported on first render so bundle size impact is deferred to runtimeMacroscope summarized f2ab071.
Note
Medium Risk
Rendered SVG is injected via
dangerouslySetInnerHTML; risk is mitigated by mermaid strict mode but this is still untrusted assistant content in the DOM. Adds a sizable client dependency loaded on first mermaid fence.Overview
Chat markdown now renders
mermaid/mmdcode fences as inline SVG diagrams once a message finishes streaming, instead of only Shiki-highlighted source. While streaming, fences stay as normal code blocks so partial diagrams are not parsed.ChatMarkdownwires this through existing code-block chrome: a Show source / Show diagram toggle, copy still uses the raw fence text, and parse failures show an alert and fall back to highlighted source. Rendering is delegated to a newMermaidDiagramcomponent backed byrenderMermaidSvg(dynamicimport("mermaid"), per-theme init withsecurityLevel: "strict", LRU-cached SVGs with fresh DOM ids on reuse).Copy/paste is updated so selecting a rendered diagram copies the original fenced mermaid source (plain and HTML), including when the selection is inside the SVG or spans surrounding prose.
Collapsed plan previews (
buildCollapsedProposedPlanPreviewMarkdown) now track open markdown fences so truncation does not split a mermaid/code block mid-fence; unclosed fences are dropped rather than swallowing the rest of the plan.Reviewed by Cursor Bugbot for commit f2ab071. Bugbot is set up for automated code reviews on this repo. Configure here.