mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-02 13:18:18 +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 ChatMessage = forwardRef<HTMLDivElement, ChatMessageProps>((props, ref) => {
|
||||||
const [copySuccess, setCopySuccess] = useState<boolean>(false);
|
const [copySuccess, setCopySuccess] = useState<boolean>(false);
|
||||||
const [isHovering, setIsHovering] = 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 [interrupted, setInterrupted] = useState<boolean>(false);
|
||||||
const [excalidrawData, setExcalidrawData] = useState<string>("");
|
const [excalidrawData, setExcalidrawData] = useState<string>("");
|
||||||
const [mermaidjsData, setMermaidjsData] = useState<string>("");
|
const [mermaidjsData, setMermaidjsData] = useState<string>("");
|
||||||
|
const [inlineMermaidBlocks, setInlineMermaidBlocks] = useState<string[]>([]);
|
||||||
|
|
||||||
// State for file content preview on file link click, hover
|
// State for file content preview on file link click, hover
|
||||||
const [previewOpen, setPreviewOpen] = useState<boolean>(false);
|
const [previewOpen, setPreviewOpen] = useState<boolean>(false);
|
||||||
@@ -472,6 +510,11 @@ const ChatMessage = forwardRef<HTMLDivElement, ChatMessageProps>((props, ref) =>
|
|||||||
setMermaidjsData(props.chatMessage.mermaidjsDiagram);
|
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
|
// Replace file links with base64 data
|
||||||
message = renderCodeGenImageInline(message, props.chatMessage.codeContext);
|
message = renderCodeGenImageInline(message, props.chatMessage.codeContext);
|
||||||
|
|
||||||
@@ -1065,6 +1108,9 @@ const ChatMessage = forwardRef<HTMLDivElement, ChatMessageProps>((props, ref) =>
|
|||||||
</Dialog>
|
</Dialog>
|
||||||
{excalidrawData && <ExcalidrawComponent data={excalidrawData} />}
|
{excalidrawData && <ExcalidrawComponent data={excalidrawData} />}
|
||||||
{mermaidjsData && <Mermaid chart={mermaidjsData} />}
|
{mermaidjsData && <Mermaid chart={mermaidjsData} />}
|
||||||
|
{inlineMermaidBlocks.map((chart, index) => (
|
||||||
|
<Mermaid key={`inline-mermaid-${index}`} chart={chart} />
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.teaserReferencesContainer}>
|
<div className={styles.teaserReferencesContainer}>
|
||||||
<TeaserReferencesSection
|
<TeaserReferencesSection
|
||||||
|
|||||||
Reference in New Issue
Block a user