UPD: Add Validators & Error Toast for Mermaid & Vega diagrams

### UPD: Feat:  Add Validators & Error Toast for Mermaid & Vega diagrams

Description:
As many time the diagrams generated or entered have syntax errors the diagrams are not rendered due to that errors, but as there isn't any notification is difficult to know what happend.

This PR add validator and toast notification when error on Mermaid and Vega/Vega-Lite diagrams, helping the user to fix its.
This commit is contained in:
_00_ 2025-10-04 14:17:04 +02:00
parent 039358e049
commit 2e08bda19d
2 changed files with 33 additions and 15 deletions

View file

@ -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);
}
};

View file

@ -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 '';
}
};