({
messages,
setInput,
setFiles,
processedToolCallsRef,
sessionId,
onRegenerate,
onEditMessage,
status = "idle",
}: ChatMessageDisplayProps)
| 179 | } |
| 180 | |
| 181 | export function ChatMessageDisplay({ |
| 182 | messages, |
| 183 | setInput, |
| 184 | setFiles, |
| 185 | processedToolCallsRef, |
| 186 | sessionId, |
| 187 | onRegenerate, |
| 188 | onEditMessage, |
| 189 | status = "idle", |
| 190 | }: ChatMessageDisplayProps) { |
| 191 | const { chartXML, loadDiagram: onDisplayChart } = useDiagram() |
| 192 | const messagesEndRef = useRef<HTMLDivElement>(null) |
| 193 | const previousXML = useRef<string>("") |
| 194 | const processedToolCalls = processedToolCallsRef |
| 195 | const [expandedTools, setExpandedTools] = useState<Record<string, boolean>>( |
| 196 | {}, |
| 197 | ) |
| 198 | const [copiedMessageId, setCopiedMessageId] = useState<string | null>(null) |
| 199 | const [copyFailedMessageId, setCopyFailedMessageId] = useState< |
| 200 | string | null |
| 201 | >(null) |
| 202 | const [feedback, setFeedback] = useState<Record<string, "good" | "bad">>({}) |
| 203 | const [editingMessageId, setEditingMessageId] = useState<string | null>( |
| 204 | null, |
| 205 | ) |
| 206 | const editTextareaRef = useRef<HTMLTextAreaElement>(null) |
| 207 | const [editText, setEditText] = useState<string>("") |
| 208 | // Track which PDF sections are expanded (key: messageId-sectionIndex) |
| 209 | const [expandedPdfSections, setExpandedPdfSections] = useState< |
| 210 | Record<string, boolean> |
| 211 | >({}) |
| 212 | |
| 213 | const copyMessageToClipboard = async (messageId: string, text: string) => { |
| 214 | try { |
| 215 | await navigator.clipboard.writeText(text) |
| 216 | |
| 217 | setCopiedMessageId(messageId) |
| 218 | setTimeout(() => setCopiedMessageId(null), 2000) |
| 219 | } catch (err) { |
| 220 | // Fallback for non-secure contexts (HTTP) or permission denied |
| 221 | const textarea = document.createElement("textarea") |
| 222 | textarea.value = text |
| 223 | textarea.style.position = "fixed" |
| 224 | textarea.style.left = "-9999px" |
| 225 | textarea.style.opacity = "0" |
| 226 | document.body.appendChild(textarea) |
| 227 | |
| 228 | try { |
| 229 | textarea.select() |
| 230 | const success = document.execCommand("copy") |
| 231 | if (!success) { |
| 232 | throw new Error("Copy command failed") |
| 233 | } |
| 234 | setCopiedMessageId(messageId) |
| 235 | setTimeout(() => setCopiedMessageId(null), 2000) |
| 236 | } catch (fallbackErr) { |
| 237 | console.error("Failed to copy message:", fallbackErr) |
| 238 | toast.error( |
nothing calls this directly
no test coverage detected