mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-02 05:29:12 +00:00
Render mermaid diagram wrapped in markdown codeblocks on web app
This commit is contained in:
@@ -399,6 +399,43 @@ export function TrainOfThought(props: TrainOfThoughtProps) {
|
||||
);
|
||||
}
|
||||
|
||||
// Clean mermaid chart by removing/fixing invalid syntax patterns
|
||||
function cleanMermaidChart(chart: string): string {
|
||||
return chart
|
||||
.split("\n")
|
||||
.filter((line) => !line.trim().match(/^title\s*\[.*\]\s*$/i)) // Remove invalid title[...] lines
|
||||
.map((line) => {
|
||||
// Fix parentheses inside square bracket node labels: [Text (with parens)]
|
||||
// Mermaid interprets () as special syntax, so we need to quote the content
|
||||
// Replace [Label (text)] with ["Label (text)"]
|
||||
return line.replace(/\[([^\]]*\([^\]]*\)[^\]]*)\]/g, '["$1"]');
|
||||
})
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
// Extract mermaid code blocks from markdown content
|
||||
function extractMermaidBlocks(content: string): { cleanedContent: string; mermaidBlocks: string[] } {
|
||||
const mermaidBlocks: string[] = [];
|
||||
// Match ```mermaid ... ``` code blocks
|
||||
// Allow optional whitespace before/after delimiters and handle various line endings
|
||||
const mermaidRegex = /```\s*mermaid\s*\r?\n([\s\S]*?)```/gi;
|
||||
|
||||
const cleanedContent = content.replace(mermaidRegex, (match, mermaidCode) => {
|
||||
const trimmedCode = mermaidCode.trim();
|
||||
if (trimmedCode) {
|
||||
// Clean the mermaid chart before adding
|
||||
const cleanedChart = cleanMermaidChart(trimmedCode);
|
||||
if (cleanedChart.trim()) {
|
||||
mermaidBlocks.push(cleanedChart);
|
||||
}
|
||||
}
|
||||
// Replace with empty string to remove from markdown
|
||||
return "";
|
||||
});
|
||||
|
||||
return { cleanedContent, mermaidBlocks };
|
||||
}
|
||||
|
||||
const ChatMessage = forwardRef<HTMLDivElement, ChatMessageProps>((props, ref) => {
|
||||
const [copySuccess, setCopySuccess] = useState<boolean>(false);
|
||||
const [isHovering, setIsHovering] = useState<boolean>(false);
|
||||
@@ -408,6 +445,7 @@ const ChatMessage = forwardRef<HTMLDivElement, ChatMessageProps>((props, ref) =>
|
||||
const [interrupted, setInterrupted] = useState<boolean>(false);
|
||||
const [excalidrawData, setExcalidrawData] = useState<string>("");
|
||||
const [mermaidjsData, setMermaidjsData] = useState<string>("");
|
||||
const [inlineMermaidBlocks, setInlineMermaidBlocks] = useState<string[]>([]);
|
||||
|
||||
// State for file content preview on file link click, hover
|
||||
const [previewOpen, setPreviewOpen] = useState<boolean>(false);
|
||||
@@ -472,6 +510,11 @@ const ChatMessage = forwardRef<HTMLDivElement, ChatMessageProps>((props, ref) =>
|
||||
setMermaidjsData(props.chatMessage.mermaidjsDiagram);
|
||||
}
|
||||
|
||||
// Extract mermaid blocks from the message content
|
||||
const { cleanedContent, mermaidBlocks } = extractMermaidBlocks(message);
|
||||
message = cleanedContent;
|
||||
setInlineMermaidBlocks(mermaidBlocks);
|
||||
|
||||
// Replace file links with base64 data
|
||||
message = renderCodeGenImageInline(message, props.chatMessage.codeContext);
|
||||
|
||||
@@ -1065,6 +1108,9 @@ const ChatMessage = forwardRef<HTMLDivElement, ChatMessageProps>((props, ref) =>
|
||||
</Dialog>
|
||||
{excalidrawData && <ExcalidrawComponent data={excalidrawData} />}
|
||||
{mermaidjsData && <Mermaid chart={mermaidjsData} />}
|
||||
{inlineMermaidBlocks.map((chart, index) => (
|
||||
<Mermaid key={`inline-mermaid-${index}`} chart={chart} />
|
||||
))}
|
||||
</div>
|
||||
<div className={styles.teaserReferencesContainer}>
|
||||
<TeaserReferencesSection
|
||||
|
||||
Reference in New Issue
Block a user