mirror of
https://github.com/enricoros/big-AGI.git
synced 2026-05-11 14:10:15 -07:00
Mermaid: vast improvement
This commit is contained in:
Generated
-1
@@ -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",
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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={{
|
||||
|
||||
@@ -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.`,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user