import React, { useState } from "react"; import { Copy, Check } from "lucide-react"; import { toast } from "sonner"; import EmoEyes from "./EmoEyes"; import ToolCallCard from "./ToolCallCard"; import LiveHtmlPreview from "./LiveHtmlPreview"; import { cleanDisplayText } from "../lib/messageClean"; import { buildImagePreviewSrc, buildImagePreviewPair } from "../lib/resolveToolPreview"; const MOOD_LABELS = { neutre: "Neutre", amusee: "Amusée", concentree: "Concentrée", sarcastique: "Sarcastique", ironique: "Ironique", enthousiaste: "Enthousiaste", agacee: "Agacée", curieuse: "Curieuse", pensive: "Pensive", }; const RichContent = ({ text, images, showCopyCode = false }) => { if (images?.length) { return (
{images.map((img, i) => ( ))}
{text ? : null}
); } return ; }; const CodeBlock = ({ inner, lang, showCopyCode }) => { const [copied, setCopied] = useState(false); const isHtml = ["html", "htm"].includes((lang || "").toLowerCase()); const handleCopy = async () => { try { await navigator.clipboard.writeText(inner); setCopied(true); toast.success("Code copié"); setTimeout(() => setCopied(false), 2000); } catch { toast.error("Impossible de copier"); } }; return (
{showCopyCode && ( )}
          {lang && (
            
{lang}
)} {inner}
{showCopyCode && isHtml && inner.trim() && ( )}
); }; const RichText = ({ text, showCopyCode = false }) => { if (!text) return null; const parts = text.split(/(```[\s\S]*?```|!\[[^\]]*\]\([^)]+\))/g); return (
{parts.map((part, i) => { if (part.startsWith("```")) { const inner = part.replace(/^```(\w*)\n?/, "").replace(/```$/, ""); const lang = part.match(/^```(\w+)/)?.[1] || ""; return ( ); } const imgMatch = part.match(/^!\[([^\]]*)\]\(([^)]+)\)$/); if (imgMatch) { const [, alt, src] = imgMatch; const resolved = src.startsWith("data:") || src.startsWith("http") ? src : `data:image/jpeg;base64,${src}`; return ( {alt ); } const segments = part.split(/(`[^`]+`)/g); return (

{segments.map((seg, j) => { if (seg.startsWith("`") && seg.endsWith("`")) { return ( {seg.slice(1, -1)} ); } return {seg}; })}

); })}
); }; function normalizeToolEvent(t, i) { const args = t.arguments || t.args || {}; const tool = t.tool || t.name; if (t.tool && t.state && (t.args || t.arguments) && t.inlinePreview) { return { ...t, tool, args }; } const result = { ok: true, path: args.path, url: args.url, content: args.content, title: args.title, }; let inlinePreview = t.inlinePreview || null; if (!inlinePreview && tool === "web_search" && t.result?.results?.length) { inlinePreview = { type: "browser", action: "search", query: t.result.query || args.query || "", results: (t.result.results || []).slice(0, 8), }; } if ( !inlinePreview && ["browser_visit", "browser_open", "web_fetch", "browser_click", "browser_snapshot", "browser_scroll", "browser_press", "browser_type", "browser_fill"].includes(tool) && (t.result?.url || args.url || t.result?.screenshot_base64) ) { inlinePreview = { type: "browser", action: tool === "browser_open" ? "control" : "visit", url: t.result?.url || args.url, title: t.result?.title, preview: t.result?.preview || t.result?.text, screenshot_base64: t.result?.screenshot_base64, elements: t.result?.elements || [], session_id: t.result?.session_id || "default", }; } if (!inlinePreview && tool === "edit_file" && args.path && t.result?.content) { inlinePreview = { type: "file", path: args.path, preview: String(t.result.content).slice(0, 50000), language: (args.path.split(".").pop() || "").toLowerCase(), }; } if (!inlinePreview && tool === "write_file" && args.path && args.content) { inlinePreview = { type: "file", path: args.path, preview: String(args.content).slice(0, 50000), language: (args.path.split(".").pop() || "").toLowerCase(), }; } if (!inlinePreview && tool === "generate_image") { const res = t.result; if (res?.ok !== false && (res?.image_url || res?.image_base64 || res?.has_image)) { inlinePreview = { type: "image", image_url: res.image_url, image_base64: res.image_base64, mime: res.mime || "image/png", title: args.prompt || res.prompt || res.subject || "Image générée", has_image: res.has_image, }; } } return { id: t.id || `hist-${i}`, tool, args, state: t.state || "done", result: t.result || result, inlinePreview, }; } export const ChatMessage = ({ message, isStreaming, liveHtmlByPath = {}, showCopyCode = false }) => { const isUser = message.role === "user"; if (isUser) { return (
); } const mood = message.mood; return (
Émo {mood && mood !== "neutre" && ( {MOOD_LABELS[mood] || mood} )} {message.verified === "true" && ( Vérifié )} {message.verified === "partial" && ( Partiel )} {isStreaming && ( )}
{((message.tool_calls_live && message.tool_calls_live.length > 0) || (message.tool_calls && message.tool_calls.length > 0)) && (
{(message.tool_calls_live || message.tool_calls).map((t, i) => ( ))}
)} {(message.content || message.images?.length) && (
)}
); }; export default ChatMessage;