import { useState, useEffect } from 'react'; import { MessageSquare, UserCircle, Bot, Star, User, Image as ImageIcon } from 'lucide-react'; import { cn } from '@/lib/utils'; import { Persona } from '@/types/persona'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { getPersonaAvatarSrc } from '@/utils/avatarUtils'; import { parseMentions, formatMentionsForDisplay } from '@/utils/mentionUtils'; import { focusGroupsApi } from '@/lib/api'; import { Message } from '@/components/focus-group-session/types'; interface ChatMessageProps { message: Message; persona: Persona | null; toggleHighlight: () => void; participants?: Persona[]; // For parsing @mentions in message text focusGroupId?: string; // For loading creative assets } const ChatMessage = ({ message, persona, toggleHighlight, participants = [], focusGroupId }: ChatMessageProps) => { const [isHovered, setIsHovered] = useState(false); const isModerator = message.senderId === 'moderator'; const isFacilitator = message.senderId === 'facilitator'; // Parse and format mentions in the message text const parsedMentions = parseMentions(message.text, participants); const formattedText = formatMentionsForDisplay(message.text, parsedMentions.mentions); // Check for visual asset using metadata (new system) or fallback to legacy parsing const hasCreativeAsset = (isModerator || isFacilitator) && (message.visualAsset || extractLegacyAssetFilename(message.text)) && focusGroupId; // Get asset info from metadata or fallback to legacy extraction const getAssetInfo = () => { if (message.visualAsset) { // New metadata-driven approach return { filename: message.visualAsset.filename, displayReference: message.visualAsset.displayReference }; } else { // Legacy fallback for existing messages const legacyFilename = extractLegacyAssetFilename(message.text); return legacyFilename ? { filename: legacyFilename, displayReference: legacyFilename } : null; } }; const assetInfo = getAssetInfo(); // Legacy filename extraction for backward compatibility function extractLegacyAssetFilename(text: string): string | null { const filenamePatterns = [ /titled\s+['"]([^'"]+\.(jpg|jpeg|png))['\"]/i, // "titled 'filename.jpg'" /asset\s+['"]([^'"]+\.(jpg|jpeg|png))['\"]/i, // "asset 'filename.jpg'" /(fg-[a-f0-9]+-[a-f0-9]{32}\.(jpg|jpeg|png))/i, // fg-{id}-{uuid}.{ext} ]; for (const pattern of filenamePatterns) { const match = text.match(pattern); if (match) { return match[1]; } } return null; } const handleToggleHighlight = () => { toggleHighlight(); }; return (
{!message.text || message.text.trim() === '' || message.text === '...' ? ( [No response content - AI generation may have failed] ) : ( formattedText )}
{/* Display creative asset if this is a moderator/facilitator message with an asset */} {hasCreativeAsset && assetInfo && (