Mermaid: vast improvement

This commit is contained in:
Enrico Ros
2023-11-14 14:30:19 -08:00
parent cafcafb582
commit 48af71d5f1
6 changed files with 67 additions and 44 deletions
-1
View File
@@ -27,7 +27,6 @@
"@trpc/server": "^10.43.3",
"@vercel/analytics": "^1.1.1",
"browser-fs-access": "^0.35.0",
"dompurify": "^3.0.6",
"eventsource-parser": "^1.1.1",
"idb-keyval": "^6.2.1",
"mermaid": "^10.6.1",
-1
View File
@@ -31,7 +31,6 @@
"@trpc/server": "^10.43.3",
"@vercel/analytics": "^1.1.1",
"browser-fs-access": "^0.35.0",
"dompurify": "^3.0.6",
"eventsource-parser": "^1.1.1",
"idb-keyval": "^6.2.1",
"mermaid": "^10.6.1",
@@ -197,7 +197,7 @@ export function ChatMessage(props: {
message: DMessage,
showDate?: boolean, diffPreviousText?: string,
hideAvatars?: boolean, codeBackground?: string,
noMarkdown?: boolean, filterOnlyCode?: boolean,
noMarkdown?: boolean, diagramMode?: boolean,
isBottom?: boolean, noBottomBorder?: boolean,
isImagining?: boolean, isSpeaking?: boolean,
onMessageDelete?: () => void,
@@ -467,7 +467,10 @@ export function ChatMessage(props: {
...blockSx,
flexGrow: 0,
overflowX: 'auto',
...(!!props.filterOnlyCode && { boxShadow: 'md' }),
...(!!props.diagramMode && {
// width: '100%',
boxShadow: 'md',
}),
}}>
{props.showDate === true && (
@@ -489,13 +492,13 @@ export function ChatMessage(props: {
{/* sequence of render components, for each Block */}
{!errorMessage && parseBlocks(collapsedText, fromSystem, textDiffs)
.filter(block => block.type === 'code' || !props.filterOnlyCode)
.filter(block => !props.diagramMode || block.type === 'code')
.map(
(block, index) =>
block.type === 'html'
? <RenderHtml key={'html-' + index} htmlBlock={block} sx={codeSx} />
: block.type === 'code'
? <RenderCode key={'code-' + index} codeBlock={block} sx={codeSx} noCopyButton={props.filterOnlyCode} />
? <RenderCode key={'code-' + index} codeBlock={block} sx={codeSx} noCopyButton={props.diagramMode} />
: block.type === 'image'
? <RenderImage key={'image-' + index} imageBlock={block} allowRunAgain={props.isBottom === true} onRunAgain={handleOpsRunAgain} />
: block.type === 'latex'
@@ -1,71 +1,93 @@
import * as React from 'react';
import type { MermaidConfig } from 'mermaid';
import { Box } from '@mui/joy';
import { SxProps } from '@mui/system';
import { appTheme } from '~/common/app.theme';
const RenderMermaidDynamic = React.lazy(async () => {
// dynamic import
const { default: mermaidAPI } = await import('mermaid');
const { default: DOMPurify } = await import('dompurify');
const mermaidConfig: MermaidConfig = {
mermaidAPI.initialize({
startOnLoad: false,
theme: 'default',
// ... any other Mermaid configuration options
};
mermaidAPI.initialize(mermaidConfig);
// gfx options
fontFamily: appTheme.fontFamily.code,
altFontFamily: appTheme.fontFamily.body,
// style configuration
fontSize: 8,
htmlLabels: true,
securityLevel: 'loose',
theme: 'forest',
// per-chart configuration
mindmap: { useMaxWidth: false },
flowchart: { useMaxWidth: false },
sequence: { useMaxWidth: false },
timeline: { useMaxWidth: false },
class: { useMaxWidth: false },
state: { useMaxWidth: false },
pie: { useMaxWidth: false },
er: { useMaxWidth: false },
gantt: { useMaxWidth: false },
gitGraph: { useMaxWidth: false },
});
const MermaidDiagram = React.memo((props: { mermaidCode: string }) => {
// state
const isMounted = React.useRef(false);
const [svgCode, setSvgCode] = React.useState<string | null>(null);
const [error, setError] = React.useState<string | null>(null);
const mermaidContainerRef = React.useRef<HTMLDivElement>(null);
// [effect] re-render on code changes
React.useEffect(() => {
let isMounted = true;
// Generate a unique ID for each diagram
const id = `mermaid-${Math.random().toString(36).substring(2, 9)}`;
// Render the diagram
mermaidAPI
.render(id, props.mermaidCode)
.then(renderResult => {
if (!isMounted) return;
const svg = DOMPurify.sanitize(renderResult.svg);
setSvgCode(svg);
// if (mermaidContainerRef.current)
// renderResult.bindFunctions?.(mermaidContainerRef.current);
})
.catch(error => {
console.error('Mermaid rendering failed:', error);
setError('Mermaid rendering issue: ' + JSON.stringify(error));
})
.finally(() => {
// ...
});
const updateSvgCode = () =>
mermaidAPI
.render(
`mermaid-${Math.random().toString(36).substring(2, 9)}`,
props.mermaidCode,
mermaidContainerRef.current!,
)
.then(({ svg /*, bindFunctions*/ }) => {
if (mermaidContainerRef.current && isMounted.current) {
setSvgCode(svg);
// bindFunctions?.(mermaidContainerRef.current);
}
})
.catch((error) =>
console.error('Mermaid rendering failed:', error),
);
// mounting state and 'strict mode' debounce
isMounted.current = true;
const timeout = setTimeout(updateSvgCode, 0);
return () => {
isMounted = false;
isMounted.current = false;
clearTimeout(timeout);
};
}, [props.mermaidCode]);
if (error)
return <div>Error: {error}</div>;
return (
<Box component='div'
ref={mermaidContainerRef}
dangerouslySetInnerHTML={{ __html: svgCode || 'Loading diagram...' }}
<Box
component='div'
ref={mermaidContainerRef}
dangerouslySetInnerHTML={{ __html: svgCode || 'Loading Diagram...' }}
/>
);
});
// Assign a displayName to the component for better debugging
MermaidDiagram.displayName = 'MermaidDiagram';
return { default: MermaidDiagram };
+1 -1
View File
@@ -176,7 +176,7 @@ export function DiagramsModal(props: { config: DiagramConfig, onClose: () => voi
{!!message && (!abortController || showOptions) && (
<ChatMessage
message={message} hideAvatars noBottomBorder noMarkdown filterOnlyCode
message={message} hideAvatars noBottomBorder noMarkdown diagramMode
codeBackground='background.surface'
onMessageEdit={(text) => setMessage({ ...message, text })}
sx={{
+2 -2
View File
@@ -37,10 +37,10 @@ mindmap
function mermaidDiagramPrompt(diagramType: DiagramType): { sys: string, usr: string } {
let promptDetails = diagramType === 'auto'
? 'You create a valid Mermaid diagram markdown (```mermaid\\n...), ready to be rendered into a diagram or mindmap. Ensure the code contains no external references, and all names are properly enclosed in double quotes and escaped if necessary. Choose the most suitable diagram type from the following supported types: flowchart, sequence, class, state, erd, gantt, pie, git, or mindmap.'
? 'You create a valid Mermaid diagram markdown (```mermaid\\n...), ready to be rendered into a diagram. Ensure the code contains no external references, and all names are properly enclosed in double quotes and escaped if necessary. Choose the most suitable diagram type from the following supported types: flowchart, sequence, class, state, erd, gantt, pie, git.'
: 'You create a valid Mermaid mindmap markdown (```mermaid\\n...), ready to be rendered into a mind map. Ensure the code contains no external references, and all names are properly enclosed in double quotes and escaped if necessary. For example:\n' + mermaidMindmapExample + '\n';
return {
sys: `You are an AI that generates Mermaid code based on provided text. ${promptDetails}`,
sys: `You are an AI that generates correct Mermaid code based on provided text. ${promptDetails}`,
usr: `Generate the Mermaid code for a ${diagramType === 'auto' ? 'suitable diagram' : 'mind map'} that represents the preceding assistant message.`,
};
}