From 7ff86f4458dd7547cab4392686896cbe5fb649a1 Mon Sep 17 00:00:00 2001
From: teknium1 <127238744+teknium1@users.noreply.github.com>
Date: Tue, 7 Jul 2026 02:13:58 -0700
Subject: [PATCH] refactor(desktop): route preview-pane mermaid fences through
shared embeds registry
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Drop the duplicate mermaid-block.tsx (own mermaid.initialize + render path,
theme frozen at first load) and wire preview-file.tsx's MarkdownCode through
the existing RichCodeBlock registry from #52935 instead. One mermaid init
path, theme-flip re-init, Zoomable + copy-as-PNG, RichBoundary error
fallback — and the preview pane gets svg fences for free. Shiki block stays
as the fallback for all other languages.
---
.../src/app/chat/right-rail/preview-file.tsx | 25 ++----
.../src/components/chat/mermaid-block.tsx | 84 -------------------
2 files changed, 9 insertions(+), 100 deletions(-)
delete mode 100644 apps/desktop/src/components/chat/mermaid-block.tsx
diff --git a/apps/desktop/src/app/chat/right-rail/preview-file.tsx b/apps/desktop/src/app/chat/right-rail/preview-file.tsx
index 48e0649647a..ca4c65f2e99 100644
--- a/apps/desktop/src/app/chat/right-rail/preview-file.tsx
+++ b/apps/desktop/src/app/chat/right-rail/preview-file.tsx
@@ -6,7 +6,7 @@ import type {
MouseEvent as ReactMouseEvent,
ReactNode
} from 'react'
-import { Fragment, lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from 'react'
+import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import ShikiHighlighter from 'react-shiki'
import { Streamdown } from 'streamdown'
@@ -14,6 +14,7 @@ import { requestComposerFocus, requestComposerInsertRefs } from '@/app/chat/comp
import { droppedFileInlineRef } from '@/app/chat/composer/inline-refs'
import { HERMES_PATHS_MIME } from '@/app/chat/hooks/use-composer-actions'
import { isAddSelectionShortcut } from '@/app/right-sidebar/terminal/selection'
+import { RichCodeBlock } from '@/components/assistant-ui/embeds'
import { CodeEditor } from '@/components/chat/code-editor'
import { FileDiffPanel } from '@/components/chat/diff-lines'
import { chunkTextLines, useFixedRowWindow } from '@/components/chat/fixed-row-window'
@@ -34,10 +35,6 @@ import { setPreviewDirty } from '@/store/preview-edit'
import { $currentCwd } from '@/store/session'
import { notifyWorkspaceChanged } from '@/store/workspace-events'
-const MermaidDiagram = lazy(() =>
- import('@/components/chat/mermaid-block').then(m => ({ default: m.MermaidBlock }))
-)
-
const SHIKI_THEME = { dark: 'github-dark-default', light: 'github-light-default' } as const
const TEXT_PREVIEW_MAX_BYTES = 512 * 1024
const SOURCE_CHUNK_LINES = 200
@@ -295,17 +292,9 @@ function MarkdownCode({ className, children, ...props }: ComponentProps<'code'>)
)
}
- if (language === 'mermaid') {
- return (
- Loading diagram…}
- >
-
-
- )
- }
+ const code = String(children).replace(/\n$/, '')
- return (
+ const highlighted = (
)
showLanguage={false}
theme={SHIKI_THEME}
>
- {String(children).replace(/\n$/, '')}
+ {code}
)
+
+ // ```mermaid / ```svg fences route to the shared lazy renderers (same
+ // registry the chat transcript uses); everything else stays on Shiki.
+ return
}
const MARKDOWN_COMPONENTS = {
diff --git a/apps/desktop/src/components/chat/mermaid-block.tsx b/apps/desktop/src/components/chat/mermaid-block.tsx
deleted file mode 100644
index 157dc0d2e03..00000000000
--- a/apps/desktop/src/components/chat/mermaid-block.tsx
+++ /dev/null
@@ -1,84 +0,0 @@
-import { useEffect, useRef, useState } from 'react'
-
-let mermaidPromise: Promise | null = null
-
-function loadMermaid() {
- if (!mermaidPromise) {
- mermaidPromise = import('mermaid').then(async mod => {
- const m = mod.default
-
- m.initialize({
- securityLevel: 'strict',
- startOnLoad: false,
- theme: document.documentElement.classList.contains('dark') ? 'dark' : 'default'
- })
-
- return mod
- })
- }
-
- return mermaidPromise
-}
-
-interface MermaidBlockProps {
- chart: string
-}
-
-export function MermaidBlock({ chart }: MermaidBlockProps) {
- const containerRef = useRef(null)
- const [error, setError] = useState(null)
- const [svg, setSvg] = useState(null)
-
- useEffect(() => {
- let cancelled = false
-
- async function render() {
- try {
- const { default: mermaid } = await loadMermaid()
- const id = `mermaid-${Math.random().toString(36).slice(2, 10)}`
- const { svg: rendered } = await mermaid.render(id, chart)
-
- if (!cancelled) {
- setSvg(rendered)
- setError(null)
- }
- } catch (err) {
- if (!cancelled) {
- setError(err instanceof Error ? err.message : String(err))
- setSvg(null)
- }
- }
- }
-
- void render()
-
- return () => {
- cancelled = true
- }
- }, [chart])
-
- if (error) {
- return (
-
-
Mermaid diagram error
-
{error}
-
- View source
- {chart}
-
-
- )
- }
-
- if (!svg) {
- return Rendering diagram…
- }
-
- return (
-
- )
-}