Spaces:
Sleeping
Sleeping
| // web/src/components/Message.tsx | |
| import React from "react"; | |
| import ReactMarkdown from "react-markdown"; | |
| import remarkGfm from "remark-gfm"; | |
| import type { Message as MessageType, LearningMode } from "../App"; | |
| interface MessageProps { | |
| message: MessageType; | |
| // existing props you are already passing in ChatArea | |
| showSenderInfo?: boolean; | |
| userId?: string; | |
| isLoggedIn: boolean; | |
| learningMode: LearningMode; | |
| docType?: string; | |
| lastUserText?: string; | |
| } | |
| export function Message({ | |
| message, | |
| showSenderInfo, | |
| userId, | |
| isLoggedIn, | |
| learningMode, | |
| docType, | |
| lastUserText, | |
| }: MessageProps) { | |
| const isUser = message.role === "user"; | |
| // If you already have avatar / sender rendering logic, keep it. | |
| // The only critical change is content rendering below. | |
| return ( | |
| <div className={`flex gap-3 ${isUser ? "justify-end" : "justify-start"}`}> | |
| {!isUser && ( | |
| <div className="w-8 h-8 rounded-full bg-gradient-to-br from-purple-500 to-blue-500 flex items-center justify-center flex-shrink-0"> | |
| <span className="text-white text-sm">C</span> | |
| </div> | |
| )} | |
| <div className={`max-w-[85%] ${isUser ? "text-right" : "text-left"}`}> | |
| {/* Optional sender info (group mode) */} | |
| {showSenderInfo && message.sender && !isUser && ( | |
| <div className="text-xs text-muted-foreground mb-1"> | |
| {message.sender.name} | |
| </div> | |
| )} | |
| <div | |
| className={[ | |
| "rounded-2xl px-4 py-3 border", | |
| isUser | |
| ? "bg-primary text-primary-foreground border-primary/20" | |
| : "bg-muted text-foreground border-border", | |
| ].join(" ")} | |
| > | |
| {/* ✅ THE FIX: render markdown instead of plain text */} | |
| <ReactMarkdown | |
| remarkPlugins={[remarkGfm]} | |
| className={[ | |
| // prose improves markdown typography; max-w-none prevents narrow column | |
| "prose prose-sm max-w-none", | |
| // keep readable in chat bubble | |
| "prose-p:my-2 prose-li:my-1 prose-ul:my-2 prose-ol:my-2", | |
| // make headings not too large inside bubble | |
| "prose-h1:text-base prose-h2:text-base prose-h3:text-sm", | |
| // avoid code blocks overflowing | |
| "prose-pre:overflow-x-auto", | |
| // inherit bubble colors | |
| isUser ? "prose-invert" : "", | |
| ].join(" ")} | |
| > | |
| {message.content || ""} | |
| </ReactMarkdown> | |
| {/* If you already render references, keep your original block here */} | |
| {message.references && message.references.length > 0 && ( | |
| <div className="mt-3 pt-3 border-t border-border/50 text-xs text-muted-foreground space-y-1"> | |
| <div className="font-medium">References</div> | |
| {message.references.map((r, idx) => ( | |
| <div key={idx} className="truncate"> | |
| {r} | |
| </div> | |
| ))} | |
| </div> | |
| )} | |
| {/* If you already have feedback buttons inside Message, keep them here. | |
| Do not change logic—only keep UI. */} | |
| </div> | |
| </div> | |
| {isUser && ( | |
| <div className="w-8 h-8 rounded-full bg-primary flex items-center justify-center flex-shrink-0"> | |
| <span className="text-primary-foreground text-sm">U</span> | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| } | |