Spaces:
Sleeping
Sleeping
| // 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> | |
| ) | |
| } | |