mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-08 21:29:12 +00:00
Add the mermaid package and apply front-end parsing
- Add the mermaid package and apply front-end parsing for interpreting the diagrams. Retain processing of the excalidraw type for backwards compatibility
This commit is contained in:
@@ -19,7 +19,7 @@ export interface MessageMetadata {
|
|||||||
|
|
||||||
export interface GeneratedAssetsData {
|
export interface GeneratedAssetsData {
|
||||||
images: string[];
|
images: string[];
|
||||||
excalidrawDiagram: string;
|
mermaidjsDiagram: string;
|
||||||
files: AttachedFileText[];
|
files: AttachedFileText[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,8 +114,8 @@ export function processMessageChunk(
|
|||||||
currentMessage.generatedImages = generatedAssets.images;
|
currentMessage.generatedImages = generatedAssets.images;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (generatedAssets.excalidrawDiagram) {
|
if (generatedAssets.mermaidjsDiagram) {
|
||||||
currentMessage.generatedExcalidrawDiagram = generatedAssets.excalidrawDiagram;
|
currentMessage.generatedMermaidjsDiagram = generatedAssets.mermaidjsDiagram;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (generatedAssets.files) {
|
if (generatedAssets.files) {
|
||||||
|
|||||||
@@ -418,7 +418,7 @@ export default function ChatHistory(props: ChatHistoryProps) {
|
|||||||
conversationId: props.conversationId,
|
conversationId: props.conversationId,
|
||||||
images: message.generatedImages,
|
images: message.generatedImages,
|
||||||
queryFiles: message.generatedFiles,
|
queryFiles: message.generatedFiles,
|
||||||
excalidrawDiagram: message.generatedExcalidrawDiagram,
|
mermaidjsDiagram: message.generatedMermaidjsDiagram,
|
||||||
turnId: messageTurnId,
|
turnId: messageTurnId,
|
||||||
}}
|
}}
|
||||||
conversationId={props.conversationId}
|
conversationId={props.conversationId}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ import { DialogTitle } from "@radix-ui/react-dialog";
|
|||||||
import { convertBytesToText } from "@/app/common/utils";
|
import { convertBytesToText } from "@/app/common/utils";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
import { getIconFromFilename } from "@/app/common/iconUtils";
|
import { getIconFromFilename } from "@/app/common/iconUtils";
|
||||||
|
import Mermaid from "../mermaid/mermaid";
|
||||||
|
|
||||||
const md = new markdownIt({
|
const md = new markdownIt({
|
||||||
html: true,
|
html: true,
|
||||||
@@ -164,6 +165,7 @@ export interface SingleChatMessage {
|
|||||||
turnId?: string;
|
turnId?: string;
|
||||||
queryFiles?: AttachedFileText[];
|
queryFiles?: AttachedFileText[];
|
||||||
excalidrawDiagram?: string;
|
excalidrawDiagram?: string;
|
||||||
|
mermaidjsDiagram?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StreamMessage {
|
export interface StreamMessage {
|
||||||
@@ -182,9 +184,11 @@ export interface StreamMessage {
|
|||||||
turnId?: string;
|
turnId?: string;
|
||||||
queryFiles?: AttachedFileText[];
|
queryFiles?: AttachedFileText[];
|
||||||
excalidrawDiagram?: string;
|
excalidrawDiagram?: string;
|
||||||
|
mermaidjsDiagram?: string;
|
||||||
generatedFiles?: AttachedFileText[];
|
generatedFiles?: AttachedFileText[];
|
||||||
generatedImages?: string[];
|
generatedImages?: string[];
|
||||||
generatedExcalidrawDiagram?: string;
|
generatedExcalidrawDiagram?: string;
|
||||||
|
generatedMermaidjsDiagram?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChatHistoryData {
|
export interface ChatHistoryData {
|
||||||
@@ -271,6 +275,7 @@ interface ChatMessageProps {
|
|||||||
turnId?: string;
|
turnId?: string;
|
||||||
generatedImage?: string;
|
generatedImage?: string;
|
||||||
excalidrawDiagram?: string;
|
excalidrawDiagram?: string;
|
||||||
|
mermaidjsDiagram?: string;
|
||||||
generatedFiles?: AttachedFileText[];
|
generatedFiles?: AttachedFileText[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,6 +363,7 @@ const ChatMessage = forwardRef<HTMLDivElement, ChatMessageProps>((props, ref) =>
|
|||||||
const [isPlaying, setIsPlaying] = useState<boolean>(false);
|
const [isPlaying, setIsPlaying] = useState<boolean>(false);
|
||||||
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 interruptedRef = useRef<boolean>(false);
|
const interruptedRef = useRef<boolean>(false);
|
||||||
const messageRef = useRef<HTMLDivElement>(null);
|
const messageRef = useRef<HTMLDivElement>(null);
|
||||||
@@ -401,6 +407,10 @@ const ChatMessage = forwardRef<HTMLDivElement, ChatMessageProps>((props, ref) =>
|
|||||||
setExcalidrawData(props.chatMessage.excalidrawDiagram);
|
setExcalidrawData(props.chatMessage.excalidrawDiagram);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (props.chatMessage.mermaidjsDiagram) {
|
||||||
|
setMermaidjsData(props.chatMessage.mermaidjsDiagram);
|
||||||
|
}
|
||||||
|
|
||||||
// Replace LaTeX delimiters with placeholders
|
// Replace LaTeX delimiters with placeholders
|
||||||
message = message
|
message = message
|
||||||
.replace(/\\\(/g, "LEFTPAREN")
|
.replace(/\\\(/g, "LEFTPAREN")
|
||||||
@@ -718,6 +728,7 @@ const ChatMessage = forwardRef<HTMLDivElement, ChatMessageProps>((props, ref) =>
|
|||||||
dangerouslySetInnerHTML={{ __html: markdownRendered }}
|
dangerouslySetInnerHTML={{ __html: markdownRendered }}
|
||||||
/>
|
/>
|
||||||
{excalidrawData && <ExcalidrawComponent data={excalidrawData} />}
|
{excalidrawData && <ExcalidrawComponent data={excalidrawData} />}
|
||||||
|
{mermaidjsData && <Mermaid chart={mermaidjsData} />}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.teaserReferencesContainer}>
|
<div className={styles.teaserReferencesContainer}>
|
||||||
<TeaserReferencesSection
|
<TeaserReferencesSection
|
||||||
|
|||||||
68
src/interface/web/app/components/mermaid/mermaid.tsx
Normal file
68
src/interface/web/app/components/mermaid/mermaid.tsx
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import React, { useEffect, useState, useRef } from "react";
|
||||||
|
import mermaid from "mermaid";
|
||||||
|
|
||||||
|
interface MermaidProps {
|
||||||
|
chart: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Mermaid: React.FC<MermaidProps> = ({ chart }) => {
|
||||||
|
const [mermaidError, setMermaidError] = useState<string | null>(null);
|
||||||
|
const [mermaidId] = useState(`mermaid-chart-${Math.random().toString(12).substring(7)}`);
|
||||||
|
const elementRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
mermaid.initialize({
|
||||||
|
startOnLoad: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
mermaid.parseError = (error) => {
|
||||||
|
console.error("Mermaid errors:", error);
|
||||||
|
// Extract error message from error object
|
||||||
|
const errorMessage = JSON.stringify(error);
|
||||||
|
setMermaidError(errorMessage);
|
||||||
|
};
|
||||||
|
|
||||||
|
mermaid.contentLoaded();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log("Rendering mermaid chart:", chart);
|
||||||
|
if (elementRef.current) {
|
||||||
|
elementRef.current.removeAttribute("data-processed");
|
||||||
|
|
||||||
|
mermaid
|
||||||
|
.run({
|
||||||
|
nodes: [elementRef.current],
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
setMermaidError(null);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("Mermaid error:", error);
|
||||||
|
// Extract error message from error object
|
||||||
|
const errorMessage = error?.str || error?.message || JSON.stringify(error);
|
||||||
|
console.log("Mermaid error message:", errorMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [chart]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
id={mermaidId}
|
||||||
|
ref={elementRef}
|
||||||
|
className="mermaid"
|
||||||
|
style={{
|
||||||
|
width: "auto",
|
||||||
|
height: "auto",
|
||||||
|
boxSizing: "border-box",
|
||||||
|
overflow: "auto",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{chart}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Mermaid;
|
||||||
@@ -58,6 +58,7 @@
|
|||||||
"lucide-react": "^0.468.0",
|
"lucide-react": "^0.468.0",
|
||||||
"markdown-it": "^14.1.0",
|
"markdown-it": "^14.1.0",
|
||||||
"markdown-it-highlightjs": "^4.1.0",
|
"markdown-it-highlightjs": "^4.1.0",
|
||||||
|
"mermaid": "^11.4.1",
|
||||||
"next": "14.2.15",
|
"next": "14.2.15",
|
||||||
"nodemon": "^3.1.3",
|
"nodemon": "^3.1.3",
|
||||||
"postcss": "^8.4.38",
|
"postcss": "^8.4.38",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user