/* eslint-disable @next/next/no-img-element */ 'use client'; import { cn } from '@/lib/utils'; import { CheckCheck, Copy as CopyIcon, Brain } from 'lucide-react'; import Markdown, { MarkdownToJSX } from 'markdown-to-jsx'; import { useState } from 'react'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { oneDark } from 'react-syntax-highlighter/dist/cjs/styles/prism'; import ThinkBox from './ThinkBox'; // Helper functions for think overlay const extractThinkContent = (content: string): string | null => { const thinkRegex = /([\s\S]*?)<\/think>/g; const matches = content.match(thinkRegex); if (!matches) return null; // Extract content between think tags and join if multiple const extractedContent = matches .map((match) => match.replace(/<\/?think>/g, '')) .join('\n\n'); // Return null if content is empty or only whitespace return extractedContent.trim().length === 0 ? null : extractedContent; }; const removeThinkTags = (content: string): string => { return content.replace(/[\s\S]*?<\/think>/g, '').trim(); }; const ThinkTagProcessor = ({ children, isOverlayMode = false, }: { children: React.ReactNode; isOverlayMode?: boolean; }) => { // In overlay mode, don't render anything (content will be handled by overlay) if (isOverlayMode) { return null; } return ; }; const CodeBlock = ({ className, children, }: { className?: string; children: React.ReactNode; }) => { // Extract language from className (format could be "language-javascript" or "lang-javascript") let language = ''; if (className) { if (className.startsWith('language-')) { language = className.replace('language-', ''); } else if (className.startsWith('lang-')) { language = className.replace('lang-', ''); } } const content = children as string; const [isCopied, setIsCopied] = useState(false); const handleCopyCode = () => { navigator.clipboard.writeText(content); setIsCopied(true); setTimeout(() => setIsCopied(false), 2000); }; return (
{language}
1} useInlineStyles={true} PreTag="div" > {content}
); }; interface MarkdownRendererProps { content: string; className?: string; thinkOverlay?: boolean; } const MarkdownRenderer = ({ content, className, thinkOverlay = false, }: MarkdownRendererProps) => { const [showThinkBox, setShowThinkBox] = useState(false); // Extract think content from the markdown const thinkContent = thinkOverlay ? extractThinkContent(content) : null; const contentWithoutThink = thinkOverlay ? removeThinkTags(content) : content; // Markdown formatting options const markdownOverrides: MarkdownToJSX.Options = { overrides: { think: { component: ({ children }) => ( {children} ), }, code: { component: ({ className, children }) => { // Check if it's an inline code block or a fenced code block if (className) { // This is a fenced code block (```code```) return {children}; } // This is an inline code block (`code`) return ( {children} ); }, }, pre: { component: ({ children }) => children, }, a: { component: (props) => ( ), }, // Prevent rendering of certain HTML elements for security iframe: () => null, // Don't render iframes script: () => null, // Don't render scripts object: () => null, // Don't render objects style: () => null, // Don't render styles }, }; return (
{/* Think box when expanded - shows above markdown */} {thinkOverlay && thinkContent && showThinkBox && (
setShowThinkBox(false)} />
)} {thinkOverlay ? contentWithoutThink : content} {/* Overlay icon when think box is collapsed */} {thinkOverlay && thinkContent && !showThinkBox && ( )}
); }; export default MarkdownRenderer;