import { useState, useEffect, useRef } from "react"; import ReactMarkdown from "react-markdown"; import remarkGfm from "remark-gfm"; import { useTranslation } from "react-i18next"; import copy from "copy-to-clipboard"; import { toast } from "sonner"; import { FaCopy, FaPencilAlt } from "react-icons/fa"; import { FaArrowUpLong } from "react-icons/fa6"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; type MessageBubbleProps = { role: "user" | "assistant"; content: string; messageIndex?: number; onEditSubmit?: (messageIndex: number, newContent: string) => void; isComplete?: boolean; }; export function MessageBubble({ role, content, messageIndex = 0, onEditSubmit, isComplete = true, }: MessageBubbleProps) { const { t } = useTranslation(["views/chat", "common"]); const isUser = role === "user"; const [isEditing, setIsEditing] = useState(false); const [draftContent, setDraftContent] = useState(content); const editInputRef = useRef(null); useEffect(() => { setDraftContent(content); }, [content]); useEffect(() => { if (isEditing) { editInputRef.current?.focus(); editInputRef.current?.setSelectionRange( editInputRef.current.value.length, editInputRef.current.value.length, ); } }, [isEditing]); const handleCopy = () => { const text = content?.trim() || ""; if (!text) return; if (copy(text)) { toast.success(t("button.copiedToClipboard", { ns: "common" })); } }; const handleEditClick = () => { setDraftContent(content); setIsEditing(true); }; const handleEditSubmit = () => { const trimmed = draftContent.trim(); if (!trimmed || onEditSubmit == null) return; onEditSubmit(messageIndex, trimmed); setIsEditing(false); }; const handleEditCancel = () => { setDraftContent(content); setIsEditing(false); }; const handleEditKeyDown = (e: React.KeyboardEvent) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); handleEditSubmit(); } if (e.key === "Escape") { handleEditCancel(); } }; if (isUser && isEditing) { return (