diff --git a/lightrag_webui/src/api/lightrag.ts b/lightrag_webui/src/api/lightrag.ts index 641b8486..0528d11d 100644 --- a/lightrag_webui/src/api/lightrag.ts +++ b/lightrag_webui/src/api/lightrag.ts @@ -333,7 +333,6 @@ export const queryTextStream = async ( try { const parsed = JSON.parse(line); if (parsed.response) { - console.log('Received chunk:', parsed.response); // Log for debugging onChunk(parsed.response); } else if (parsed.error && onError) { onError(parsed.error); diff --git a/lightrag_webui/src/components/retrieval/ChatMessage.tsx b/lightrag_webui/src/components/retrieval/ChatMessage.tsx index 53f1d2bc..5bb657ca 100644 --- a/lightrag_webui/src/components/retrieval/ChatMessage.tsx +++ b/lightrag_webui/src/components/retrieval/ChatMessage.tsx @@ -1,4 +1,4 @@ -import { ReactNode, useCallback } from 'react' +import { ReactNode, useCallback, useEffect, useRef } from 'react' import { Message } from '@/api/lightrag' import useTheme from '@/hooks/useTheme' import Button from '@/components/ui/Button' @@ -8,6 +8,7 @@ import ReactMarkdown from 'react-markdown' import remarkGfm from 'remark-gfm' import rehypeReact from 'rehype-react' import remarkMath from 'remark-math' +import mermaid from 'mermaid' import type { Element } from 'hast' @@ -23,6 +24,8 @@ export type MessageWithError = Message & { export const ChatMessage = ({ message }: { message: MessageWithError }) => { const { t } = useTranslation() + // Remove extra spaces around bold text + message.content = message.content.replace(/\*\ {3}/g, '').replace(/\ {4}\*\*/g, '**') const handleCopyMarkdown = useCallback(async () => { if (message.content) { @@ -32,7 +35,7 @@ export const ChatMessage = ({ message }: { message: MessageWithError }) => { console.error(t('chat.copyError'), err) } } - }, [message]) + }, [message, t]) // Added t to dependency array return (
Waiting for complete diagram...
'; + return; // Don't attempt to render potentially incomplete content + } + + const processedContent = rawContent + .split('\n') + .map(line => { + const trimmedLine = line.trim(); + // Keep subgraph processing + if (trimmedLine.startsWith('subgraph')) { + const parts = trimmedLine.split(' '); + if (parts.length > 1) { + const title = parts.slice(1).join(' ').replace(/["']/g, ''); + return `subgraph "${title}"`; + } + } + return trimmedLine; + }) + .filter(line => !line.trim().startsWith('linkStyle')) // Keep filtering linkStyle + .join('\n'); + + const mermaidId = `mermaid-${Date.now()}`; + mermaid.render(mermaidId, processedContent) + .then(({ svg, bindFunctions }) => { + // Check ref again inside async callback + // Ensure the container is still the one we intended to update + if (mermaidRef.current === container) { + container.innerHTML = svg; + if (bindFunctions) { + try { // Add try-catch around bindFunctions as it can also throw + bindFunctions(container); + } catch (bindError) { + console.error('Mermaid bindFunctions error:', bindError); + // Optionally display a message in the container + container.innerHTML += `Diagram interactions might be limited.
`; + } + } + } else { + console.log("Mermaid container changed before rendering completed."); + } + }) + .catch(error => { + console.error('Mermaid rendering promise error (debounced):', error); + console.error('Failed content (debounced):', processedContent); + if (mermaidRef.current === container) { + const errorMessage = error instanceof Error ? error.message : String(error); + // Make error display more robust + const errorPre = document.createElement('pre'); + errorPre.className = 'text-red-500 text-xs whitespace-pre-wrap break-words'; + errorPre.textContent = `Mermaid diagram error: ${errorMessage}\n\nContent:\n${processedContent}`; + container.innerHTML = ''; // Clear previous content + container.appendChild(errorPre); + } + }); + + } catch (error) { + console.error('Mermaid synchronous error (debounced):', error); + console.error('Failed content (debounced):', String(children)); + if (mermaidRef.current === container) { + const errorMessage = error instanceof Error ? error.message : String(error); + const errorPre = document.createElement('pre'); + errorPre.className = 'text-red-500 text-xs whitespace-pre-wrap break-words'; + errorPre.textContent = `Mermaid diagram setup error: ${errorMessage}`; + container.innerHTML = ''; // Clear previous content + container.appendChild(errorPre); + } + } + }, 300); // 300ms debounce delay + } + + // Cleanup function to clear the timer + return () => { + if (debounceTimerRef.current) { + clearTimeout(debounceTimerRef.current); + } + }; + }, [language, children, theme]); // Dependencies + + // Render based on language type + if (language === 'mermaid') { + // Container for Mermaid diagram + return ; + } + + // Handle non-Mermaid code blocks return !inline ? (
{children}
- )
-}
+ );
+};