Spaces:
Sleeping
Sleeping
File size: 4,253 Bytes
5e0532d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
'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>
);
}
|