bhavinmatariya's picture
Initial complete codebase with Git LFS for binary files
8e0dd55
import React from 'react';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import rehypeRaw from 'rehype-raw';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { tomorrow } from 'react-syntax-highlighter/dist/cjs/styles/prism';
import Mermaid from './Mermaid';
interface MarkdownProps {
content: string;
}
const Markdown: React.FC<MarkdownProps> = ({ content }) => {
// Define markdown components
const MarkdownComponents: React.ComponentProps<typeof ReactMarkdown>['components'] = {
p({ children, ...props }: { children?: React.ReactNode }) {
return <p className="mb-3 text-sm leading-relaxed dark:text-white" {...props}>{children}</p>;
},
h1({ children, ...props }: { children?: React.ReactNode }) {
return <h1 className="text-xl font-bold mt-6 mb-3 dark:text-white" {...props}>{children}</h1>;
},
h2({ children, ...props }: { children?: React.ReactNode }) {
// Special styling for ReAct headings
if (children && typeof children === 'string') {
const text = children.toString();
if (text.includes('Thought') || text.includes('Action') || text.includes('Observation') || text.includes('Answer')) {
return (
<h2
className={`text-base font-bold mt-5 mb-3 p-2 rounded ${
text.includes('Thought') ? 'bg-blue-100 dark:bg-blue-900/30 text-blue-800 dark:text-blue-300' :
text.includes('Action') ? 'bg-green-100 dark:bg-green-900/30 text-green-800 dark:text-green-300' :
text.includes('Observation') ? 'bg-amber-100 dark:bg-amber-900/30 text-amber-800 dark:text-amber-300' :
text.includes('Answer') ? 'bg-purple-100 dark:bg-purple-900/30 text-purple-800 dark:text-purple-300' :
'dark:text-white'
}`}
{...props}
>
{children}
</h2>
);
}
}
return <h2 className="text-lg font-bold mt-5 mb-3 dark:text-white" {...props}>{children}</h2>;
},
h3({ children, ...props }: { children?: React.ReactNode }) {
return <h3 className="text-base font-semibold mt-4 mb-2 dark:text-white" {...props}>{children}</h3>;
},
h4({ children, ...props }: { children?: React.ReactNode }) {
return <h4 className="text-sm font-semibold mt-3 mb-2 dark:text-white" {...props}>{children}</h4>;
},
ul({ children, ...props }: { children?: React.ReactNode }) {
return <ul className="list-disc pl-6 mb-4 text-sm dark:text-white space-y-2" {...props}>{children}</ul>;
},
ol({ children, ...props }: { children?: React.ReactNode }) {
return <ol className="list-decimal pl-6 mb-4 text-sm dark:text-white space-y-2" {...props}>{children}</ol>;
},
li({ children, ...props }: { children?: React.ReactNode }) {
return <li className="mb-2 text-sm leading-relaxed dark:text-white" {...props}>{children}</li>;
},
a({ children, href, ...props }: { children?: React.ReactNode; href?: string }) {
return (
<a
href={href}
className="text-purple-600 dark:text-purple-400 hover:underline font-medium"
target="_blank"
rel="noopener noreferrer"
{...props}
>
{children}
</a>
);
},
blockquote({ children, ...props }: { children?: React.ReactNode }) {
return (
<blockquote
className="border-l-4 border-gray-300 dark:border-gray-700 pl-4 py-1 text-gray-700 dark:text-gray-300 italic my-4 text-sm"
{...props}
>
{children}
</blockquote>
);
},
table({ children, ...props }: { children?: React.ReactNode }) {
return (
<div className="overflow-x-auto my-6 rounded-md">
<table className="min-w-full text-sm border-collapse" {...props}>
{children}
</table>
</div>
);
},
thead({ children, ...props }: { children?: React.ReactNode }) {
return <thead className="bg-gray-100 dark:bg-gray-800" {...props}>{children}</thead>;
},
tbody({ children, ...props }: { children?: React.ReactNode }) {
return <tbody className="divide-y divide-gray-200 dark:divide-gray-700" {...props}>{children}</tbody>;
},
tr({ children, ...props }: { children?: React.ReactNode }) {
return <tr className="hover:bg-gray-50 dark:hover:bg-gray-900" {...props}>{children}</tr>;
},
th({ children, ...props }: { children?: React.ReactNode }) {
return (
<th
className="px-4 py-3 text-left font-medium text-gray-700 dark:text-gray-300"
{...props}
>
{children}
</th>
);
},
td({ children, ...props }: { children?: React.ReactNode }) {
return <td className="px-4 py-3 border-t border-gray-200 dark:border-gray-700" {...props}>{children}</td>;
},
code(props: {
inline?: boolean;
className?: string;
children?: React.ReactNode;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[key: string]: any; // Using any here as it's required for ReactMarkdown components
}) {
const { inline, className, children, ...otherProps } = props;
const match = /language-(\w+)/.exec(className || '');
const codeContent = children ? String(children).replace(/\n$/, '') : '';
// Handle Mermaid diagrams
if (!inline && match && match[1] === 'mermaid') {
return (
<div className="my-8 bg-gray-50 dark:bg-gray-800 rounded-md overflow-hidden shadow-sm">
<Mermaid
chart={codeContent}
className="w-full max-w-full"
zoomingEnabled={true}
/>
</div>
);
}
// Handle code blocks
if (!inline && match) {
return (
<div className="my-6 rounded-md overflow-hidden text-sm shadow-sm">
<div className="bg-gray-800 text-gray-200 px-5 py-2 text-sm flex justify-between items-center">
<span>{match[1]}</span>
<button
onClick={() => {
navigator.clipboard.writeText(codeContent);
}}
className="text-gray-400 hover:text-white"
title="Copy code"
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"
/>
</svg>
</button>
</div>
<SyntaxHighlighter
language={match[1]}
style={tomorrow}
className="!text-sm"
customStyle={{ margin: 0, borderRadius: '0 0 0.375rem 0.375rem', padding: '1rem' }}
showLineNumbers={true}
wrapLines={true}
wrapLongLines={true}
{...otherProps}
>
{codeContent}
</SyntaxHighlighter>
</div>
);
}
// Handle inline code
return (
<code
className={`${className} font-mono bg-gray-100 dark:bg-gray-800 px-2 py-0.5 rounded text-pink-500 dark:text-pink-400 text-sm`}
{...otherProps}
>
{children}
</code>
);
},
};
return (
<div className="prose prose-base dark:prose-invert max-w-none px-2 py-4">
<ReactMarkdown
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeRaw]}
components={MarkdownComponents}
>
{content}
</ReactMarkdown>
</div>
);
};
export default Markdown;