Browse Source

refac: more robust mermaid chart rendering

Timothy J. Baek 8 months ago
parent
commit
9435d2044a

+ 29 - 15
src/lib/components/chat/Messages/CodeBlock.svelte

@@ -3,7 +3,7 @@
 	import { loadPyodide } from 'pyodide';
 	import { loadPyodide } from 'pyodide';
 	import mermaid from 'mermaid';
 	import mermaid from 'mermaid';
 
 
-	import { getContext, getAllContexts } from 'svelte';
+	import { getContext, getAllContexts, onMount } from 'svelte';
 	import { copyToClipboard } from '$lib/utils';
 	import { copyToClipboard } from '$lib/utils';
 
 
 	import 'highlight.js/styles/github-dark.min.css';
 	import 'highlight.js/styles/github-dark.min.css';
@@ -18,6 +18,8 @@
 	export let lang = '';
 	export let lang = '';
 	export let code = '';
 	export let code = '';
 
 
+	let mermaidHtml = null;
+
 	let highlightedCode = null;
 	let highlightedCode = null;
 	let executing = false;
 	let executing = false;
 
 
@@ -213,17 +215,14 @@ __builtins__.input = input`);
 
 
 	$: if (code) {
 	$: if (code) {
 		if (lang === 'mermaid' && (token?.raw ?? '').endsWith('```')) {
 		if (lang === 'mermaid' && (token?.raw ?? '').endsWith('```')) {
-			// Function to perform the code highlighting
-			const renderMermaid = async () => {
-				// mermaid.initialize({ startOnLoad: true });
-				await mermaid.run({
-					querySelector: `.mermaid-${id}`
-				});
-			};
-			// Clear the previous timeout if it exists
-			clearTimeout(debounceTimeout);
-			// Set a new timeout to debounce the code highlighting
-			debounceTimeout = setTimeout(renderMermaid, 50);
+			(async () => {
+				try {
+					const { svg } = await mermaid.render(`mermaid-${id}`, code);
+					mermaidHtml = svg;
+				} catch (error) {
+					console.error('Error:', error);
+				}
+			})();
 		} else {
 		} else {
 			// Function to perform the code highlighting
 			// Function to perform the code highlighting
 			const highlightCode = () => {
 			const highlightCode = () => {
@@ -236,13 +235,28 @@ __builtins__.input = input`);
 			debounceTimeout = setTimeout(highlightCode, 10);
 			debounceTimeout = setTimeout(highlightCode, 10);
 		}
 		}
 	}
 	}
+
+	onMount(async () => {
+		await mermaid.initialize({ startOnLoad: true });
+
+		if (lang === 'mermaid' && (token?.raw ?? '').endsWith('```')) {
+			try {
+				const { svg } = await mermaid.render(`mermaid-${id}`, code);
+				mermaidHtml = svg;
+			} catch (error) {
+				console.error('Error:', error);
+			}
+		}
+	});
 </script>
 </script>
 
 
 <div class="my-2" dir="ltr">
 <div class="my-2" dir="ltr">
 	{#if lang === 'mermaid'}
 	{#if lang === 'mermaid'}
-		{#key code}
-			<pre class="mermaid-{id}">{code}</pre>
-		{/key}
+		{#if mermaidHtml}
+			{@html mermaidHtml}
+		{:else}
+			<pre class=" mermaid-{id}">{code}</pre>
+		{/if}
 	{:else}
 	{:else}
 		<div
 		<div
 			class="flex justify-between bg-[#202123] text-white text-xs px-4 pt-1 pb-0.5 rounded-t-lg overflow-x-auto"
 			class="flex justify-between bg-[#202123] text-white text-xs px-4 pt-1 pb-0.5 rounded-t-lg overflow-x-auto"

+ 1 - 1
src/lib/components/chat/Messages/MarkdownTokens.svelte

@@ -26,7 +26,7 @@
 		</svelte:element>
 		</svelte:element>
 	{:else if token.type === 'code'}
 	{:else if token.type === 'code'}
 		<CodeBlock
 		<CodeBlock
-			{id}
+			id={`${id}-${tokenIdx}`}
 			{token}
 			{token}
 			lang={token?.lang ?? ''}
 			lang={token?.lang ?? ''}
 			code={revertSanitizedResponseContent(token?.text ?? '')}
 			code={revertSanitizedResponseContent(token?.text ?? '')}

+ 3 - 0
src/routes/(app)/+layout.svelte

@@ -3,6 +3,8 @@
 	import { onMount, tick, getContext } from 'svelte';
 	import { onMount, tick, getContext } from 'svelte';
 	import { openDB, deleteDB } from 'idb';
 	import { openDB, deleteDB } from 'idb';
 	import fileSaver from 'file-saver';
 	import fileSaver from 'file-saver';
+	import mermaid from 'mermaid';
+
 	const { saveAs } = fileSaver;
 	const { saveAs } = fileSaver;
 
 
 	import { goto } from '$app/navigation';
 	import { goto } from '$app/navigation';
@@ -178,6 +180,7 @@
 			await tick();
 			await tick();
 		}
 		}
 
 
+		await mermaid.initialize({ startOnLoad: false });
 		loaded = true;
 		loaded = true;
 	});
 	});
 </script>
 </script>