"use client"; import type React from "react"; import { useRef, useEffect, useState } from "react"; import { FaGithub } from "react-icons/fa"; import { Card, CardContent, CardFooter, CardHeader, CardTitle, } from "@/components/ui/card"; import { useChat } from "@ai-sdk/react"; import { DefaultChatTransport } from "ai"; import { ChatInput } from "@/components/chat-input"; import { ChatMessageDisplay } from "./chat-message-display"; import { useDiagram } from "@/contexts/diagram-context"; import { replaceNodes } from "@/lib/utils"; export default function ChatPanel() { const { loadDiagram: onDisplayChart, handleExport: onExport, resolverRef, chartXML, clearDiagram, } = useDiagram(); const onFetchChart = () => { return new Promise((resolve) => { if (resolverRef && "current" in resolverRef) { resolverRef.current = resolve; // Store the resolver } onExport(); // Trigger the export }); }; // Add a step counter to track updates // Add state for file attachments const [files, setFiles] = useState([]); // Add state for showing the history dialog const [showHistory, setShowHistory] = useState(false); // Convert File[] to FileList for experimental_attachments const createFileList = (files: File[]): FileList => { const dt = new DataTransfer(); files.forEach((file) => dt.items.add(file)); return dt.files; }; // Add state for input management const [input, setInput] = useState(""); // Remove the currentXmlRef and related useEffect const { messages, sendMessage, addToolResult, status, error, setMessages } = useChat({ transport: new DefaultChatTransport({ api: "/api/chat", }), async onToolCall({ toolCall }) { if (toolCall.toolName === "display_diagram") { const { xml } = toolCall.input as { xml: string }; // do nothing because we will handle this streamingly in the ChatMessageDisplay component // onDisplayChart(replaceNodes(chartXML, xml)); // Use addToolResult instead of returning a value addToolResult({ tool: "display_diagram", toolCallId: toolCall.toolCallId, output: "Successfully displayed the flowchart.", }); } }, onError: (error) => { console.error("Chat error:", error); }, }); const messagesEndRef = useRef(null); // Scroll to bottom when messages change useEffect(() => { if (messagesEndRef.current) { messagesEndRef.current.scrollIntoView({ behavior: "smooth" }); } }, [messages]); console.log(JSON.stringify(messages, null, 2)); const onFormSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (input.trim() && status !== "streaming") { try { // Fetch chart data before sending message const chartXml = await onFetchChart(); // Create message parts const parts: any[] = [{ type: "text", text: input }]; // Add file parts if files exist if (files.length > 0) { for (const file of files) { const reader = new FileReader(); const dataUrl = await new Promise((resolve) => { reader.onload = () => resolve(reader.result as string); reader.readAsDataURL(file); }); parts.push({ type: "file", url: dataUrl, mediaType: file.type, }); } } sendMessage( { parts }, { body: { xml: chartXml, }, } ); // Clear input and files after submission setInput(""); setFiles([]); } catch (error) { console.error("Error fetching chart data:", error); } } }; // Handle input change const handleInputChange = ( e: React.ChangeEvent ) => { setInput(e.target.value); }; // Helper function to handle file changes const handleFileChange = (newFiles: File[]) => { setFiles(newFiles); }; return ( Next-AI-Drawio { setMessages([]); clearDiagram(); }} files={files} onFileChange={handleFileChange} showHistory={showHistory} onToggleHistory={setShowHistory} /> ); }