/** * MessageItem component * Renders individual chat messages with actions for assistant messages */ import { Copy, RotateCcw } from "lucide-react"; import { defaultRehypePlugins, Streamdown } from "streamdown"; import { Action, Actions, } from "@/components/lighthouse-v1/ai-elements/actions"; import { ChainOfThoughtDisplay } from "@/components/lighthouse-v1/chain-of-thought-display"; import { extractChainOfThoughtEvents, extractMessageText, type Message, MESSAGE_ROLES, MESSAGE_STATUS, } from "@/components/lighthouse-v1/chat-utils"; import { Loader } from "@/components/lighthouse-v1/loader"; import { escapeAngleBracketPlaceholders } from "@/lib/markdown"; interface MessageItemProps { message: Message; index: number; isLastMessage: boolean; status: string; onCopy: (text: string) => void; onRegenerate: () => void; } export function MessageItem({ message, index, isLastMessage, status, onCopy, onRegenerate, }: MessageItemProps) { const messageText = extractMessageText(message); // Check if this is the streaming assistant message const isStreamingAssistant = isLastMessage && message.role === MESSAGE_ROLES.ASSISTANT && status === MESSAGE_STATUS.STREAMING; // Use a composite key to ensure uniqueness even if IDs are duplicated temporarily const uniqueKey = `${message.id}-${index}-${message.role}`; // Extract chain-of-thought events from message parts const chainOfThoughtEvents = extractChainOfThoughtEvents(message); return (
{/* Chain of Thought for assistant messages */} {message.role === MESSAGE_ROLES.ASSISTANT && ( )} {/* Show loader only if streaming with no text AND no chain-of-thought events */} {isStreamingAssistant && !messageText && chainOfThoughtEvents.length === 0 ? ( ) : messageText ? (
{message.role === MESSAGE_ROLES.USER ? ( // User messages: render as plain text to preserve HTML-like tags

{messageText}

) : ( // Assistant messages: render with markdown support
, , etc. // This prevents them from being interpreted as HTML elements defaultRehypePlugins.katex, defaultRehypePlugins.harden, ]} isAnimating={isStreamingAssistant} > {escapeAngleBracketPlaceholders(messageText)}
)}
) : null}
{/* Actions for assistant messages */} {message.role === MESSAGE_ROLES.ASSISTANT && isLastMessage && messageText && status !== MESSAGE_STATUS.STREAMING && (
onCopy(messageText)} >
)}
); }