Spaces:
Running
Running
// src/components/ChatMessage.tsx | |
import React, { useState, useMemo } from 'react' | |
import ReactMarkdown from 'react-markdown' | |
import remarkGfm from 'remark-gfm' | |
import rehypeRaw from 'rehype-raw' | |
import { cn } from '@/lib/utils' | |
import { toast } from '../ui/sonner' | |
interface ChatMessageProps { | |
content: string | |
className?: string | |
} | |
export const ChatMessage: React.FC<ChatMessageProps> = ({ | |
content, | |
className, | |
}) => { | |
// Process thinking tags | |
const processedContent = useMemo(() => { | |
// Replace <think>...</think> tags with a special format | |
const contentWithProcessedThinking = content.replace( | |
/<think>([\s\S]*?)<\/think>/g, | |
(_, thinkContent) => { | |
return `<div class="think-block">${thinkContent}</div>`; | |
} | |
); | |
// Continue processing source tags as before | |
return contentWithProcessedThinking.replace( | |
/<source\s+path=["'](.+?)["']\s*\/>/g, | |
(_match, path) => { | |
const filename = path | |
.split('/') | |
.pop()! | |
.replace(/\.[^/.]+$/, '') | |
// embed your ExternalLink SVG inline so you get the icon | |
return `<a href="${path}" target="_blank" class="inline-flex items-center text-xs font-medium mx-0.5 rounded-sm px-1 bg-financial-accent/10 text-financial-accent border border-financial-accent/20 hover:bg-financial-accent/20 transition-colors"> | |
${filename} | |
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 ml-1" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"> | |
<path stroke-linecap="round" stroke-linejoin="round" d="M18 13v6a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6M15 3h6m0 0v6m0-6L10 14"/> | |
</svg> | |
</a>` | |
} | |
); | |
}, [content]); | |
return ( | |
<div | |
className={cn( | |
'group relative w-full rounded-md p-2 hover:bg-muted/30 transition-colors', | |
className | |
)} | |
> | |
<ReactMarkdown | |
remarkPlugins={[remarkGfm]} | |
rehypePlugins={[rehypeRaw]} | |
components={{ | |
// style your normal links if you like | |
a: ({ href, children, node, ...props }) => | |
href && href.endsWith('.md') ? ( | |
<a | |
href={href} | |
target="_blank" | |
rel="noopener noreferrer" | |
{...props} | |
> | |
{children} | |
</a> | |
) : ( | |
<a href={href} {...props}> | |
{children} | |
</a> | |
), | |
}} | |
> | |
{processedContent} | |
</ReactMarkdown> | |
</div> | |
) | |
} | |