diff --git a/src/lib/components/chat/Messages/CodeBlock.svelte b/src/lib/components/chat/Messages/CodeBlock.svelte index 13bc7ec2a1..d2e3a6323d 100644 --- a/src/lib/components/chat/Messages/CodeBlock.svelte +++ b/src/lib/components/chat/Messages/CodeBlock.svelte @@ -326,12 +326,12 @@ const render = async () => { onUpdate(token); if (lang === 'mermaid' && (token?.raw ?? '').slice(-4).includes('```')) { - mermaidHtml = await renderMermaidDiagram(code); + mermaidHtml = await renderMermaidDiagram(code, $i18n); } else if ( (lang === 'vega' || lang === 'vega-lite') && (token?.raw ?? '').slice(-4).includes('```') ) { - vegaHtml = await renderVegaVisualization(code); + vegaHtml = await renderVegaVisualization(code, $i18n); } }; diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index fb239e87b3..7d05c34135 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -22,6 +22,8 @@ import markedExtension from '$lib/utils/marked/extension'; import markedKatexExtension from '$lib/utils/marked/katex-extension'; import hljs from 'highlight.js'; +import { toast } from 'svelte-sonner'; + ////////////////////////// // Helper functions ////////////////////////// @@ -1579,40 +1581,56 @@ export const decodeString = (str: string) => { } }; -export const renderMermaidDiagram = async (code: string) => { +export const renderMermaidDiagram = async (code: string, i18n?: any) => { try { const { default: mermaid } = await import('mermaid'); mermaid.initialize({ - startOnLoad: true, + startOnLoad: false, // Should be false when using render API theme: document.documentElement.classList.contains('dark') ? 'dark' : 'default', securityLevel: 'loose' }); - if (await mermaid.parse(code)) { + const parseResult = await mermaid.parse(code, { suppressErrors: false }); + if (parseResult) { const { svg } = await mermaid.render(`mermaid-${uuidv4()}`, code); return svg; } + return ''; } catch (error) { - console.log('Failed to render mermaid diagram:', error); + console.error('Failed to render mermaid diagram:', error); + const errorMsg = error instanceof Error ? error.message : String(error); + toast.error(i18n.t('Failed to render diagram') + `: ${errorMsg}`); return ''; } }; -export const renderVegaVisualization = async (spec: string) => { +export const renderVegaVisualization = async (spec: string, i18n?: any) => { try { const vega = await import('vega'); const parsedSpec = JSON.parse(spec); + if (!parsedSpec.$schema) { + const errorMsg = 'Specification missing $schema property'; + toast.error(i18n.t('Failed to render diagram') + `: ${errorMsg}`); + throw new Error(errorMsg); + } let vegaSpec; - if (parsedSpec.$schema && parsedSpec.$schema.includes('vega-lite')) { - const vegaLite = await import('vega-lite'); - vegaSpec = vegaLite.compile(parsedSpec).spec; - } else { - vegaSpec = parsedSpec; - } - const view = new vega.View(vega.parse(vegaSpec), {renderer: 'none'}); + if (parsedSpec.$schema.includes('vega-lite')) { + const vegaLite = await import('vega-lite'); + vegaSpec = vegaLite.compile(parsedSpec).spec; + } else if (parsedSpec.$schema.includes('vega')) { + vegaSpec = parsedSpec; + } else { + const errorMsg = 'Unknown schema format: ' + parsedSpec.$schema; + toast.error(i18n.t('Failed to render diagram') + `: ${errorMsg}`); + throw new Error(errorMsg); + } + const runtime = vega.parse(vegaSpec); + const view = new vega.View(runtime, {renderer: 'none'}); const svg = await view.toSVG(); return svg; } catch (error) { - console.log('Failed to render Vega visualization:', error); + console.error('Failed to render Vega visualization:', error); + const errorMsg = error instanceof Error ? error.message : String(error); + toast.error(i18n.t('Failed to render diagram') + `: ${errorMsg}`); return ''; } };