ORA / frontend /components /chat /MarkdownMessage.tsx
Abdalkaderdev's picture
Initial ORA deployment
5e0532d
'use client';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import type { Components } from 'react-markdown';
interface MarkdownMessageProps {
content: string;
variant?: 'user' | 'assistant';
}
export default function MarkdownMessage({ content, variant = 'assistant' }: MarkdownMessageProps) {
const isUser = variant === 'user';
const components: Components = {
// Headings
h1: ({ children }) => (
<h1 className="text-lg font-bold mb-2 mt-3 first:mt-0 text-white">{children}</h1>
),
h2: ({ children }) => (
<h2 className="text-base font-semibold mb-2 mt-3 first:mt-0 text-white">{children}</h2>
),
h3: ({ children }) => (
<h3 className="text-sm font-semibold mb-1.5 mt-2 first:mt-0 text-white">{children}</h3>
),
// Paragraphs
p: ({ children }) => (
<p className="mb-2 last:mb-0 leading-relaxed">{children}</p>
),
// Strong/Bold - for scripture references
strong: ({ children }) => (
<strong className={`font-semibold ${isUser ? 'text-amber-200' : 'text-purple-300'}`}>
{children}
</strong>
),
// Emphasis/Italic
em: ({ children }) => (
<em className="italic text-neutral-300">{children}</em>
),
// Links
a: ({ href, children }) => (
<a
href={href}
target="_blank"
rel="noopener noreferrer"
className={`underline underline-offset-2 hover:no-underline transition-colors ${
isUser ? 'text-amber-400 hover:text-amber-300' : 'text-purple-400 hover:text-purple-300'
}`}
>
{children}
</a>
),
// Lists
ul: ({ children }) => (
<ul className="list-none space-y-1.5 my-2 ml-1">{children}</ul>
),
ol: ({ children }) => (
<ol className="list-decimal list-inside space-y-1.5 my-2 ml-1">{children}</ol>
),
li: ({ children }) => (
<li className="flex items-start gap-2">
<span className={`mt-2 w-1.5 h-1.5 rounded-full shrink-0 ${isUser ? 'bg-amber-400/60' : 'bg-purple-400/60'}`} />
<span className="flex-1">{children}</span>
</li>
),
// Blockquotes - for scripture
blockquote: ({ children }) => (
<blockquote className={`my-3 pl-4 border-l-2 ${
isUser ? 'border-amber-500/40 bg-amber-500/5' : 'border-purple-500/40 bg-purple-500/5'
} rounded-r-lg py-2 pr-3 italic`}>
{children}
</blockquote>
),
// Code blocks
code: ({ className, children }) => {
const isInline = !className;
if (isInline) {
return (
<code className={`px-1.5 py-0.5 rounded text-xs font-mono ${
isUser ? 'bg-amber-500/20 text-amber-200' : 'bg-purple-500/20 text-purple-200'
}`}>
{children}
</code>
);
}
return (
<code className="block p-3 my-2 rounded-lg bg-black/30 border border-white/10 text-xs font-mono overflow-x-auto text-neutral-200">
{children}
</code>
);
},
// Pre for code blocks
pre: ({ children }) => (
<pre className="my-2 rounded-lg overflow-hidden">{children}</pre>
),
// Horizontal rule
hr: () => (
<hr className={`my-4 border-t ${isUser ? 'border-amber-500/20' : 'border-purple-500/20'}`} />
),
// Tables
table: ({ children }) => (
<div className="my-3 overflow-x-auto">
<table className="w-full text-xs border-collapse">{children}</table>
</div>
),
thead: ({ children }) => (
<thead className={`${isUser ? 'bg-amber-500/10' : 'bg-purple-500/10'}`}>{children}</thead>
),
tbody: ({ children }) => <tbody>{children}</tbody>,
tr: ({ children }) => (
<tr className="border-b border-white/5">{children}</tr>
),
th: ({ children }) => (
<th className="px-3 py-2 text-left font-semibold text-white">{children}</th>
),
td: ({ children }) => (
<td className="px-3 py-2 text-neutral-300">{children}</td>
),
};
return (
<div className={`prose prose-sm max-w-none ${isUser ? 'text-amber-50' : 'text-purple-50'}`}>
<ReactMarkdown remarkPlugins={[remarkGfm]} components={components}>
{content}
</ReactMarkdown>
</div>
);
}