|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; |
|
import { useState, useRef, useEffect } from 'react'; |
|
import { MdSend, MdRefresh } from 'react-icons/md'; |
|
import { FiCpu, FiInfo } from 'react-icons/fi'; |
|
import { v4 as uuidv4 } from 'uuid'; |
|
import { toast } from "react-toastify"; |
|
import classNames from "classnames"; |
|
import ChatMessageComponent from './ChatMessage'; |
|
import { defaultHTML } from "../../utils/consts"; |
|
const ChatInterface = ({ agentId, html, setHtml, isAiWorking, setisAiWorking, onNewPrompt, onScrollToBottom, geminiKey, chatgptKey, hfToken }) => { |
|
const [prompt, setPrompt] = useState(""); |
|
const [messages, setMessages] = useState([]); |
|
const chatEndRef = useRef(null); |
|
const [latestHtml, setLatestHtml] = useState(html); |
|
const [isTyping, setIsTyping] = useState(false); |
|
|
|
useEffect(() => { |
|
chatEndRef.current?.scrollIntoView({ behavior: 'smooth' }); |
|
}, [messages]); |
|
|
|
const send = async () => { |
|
if (!prompt.trim() || isAiWorking) |
|
return; |
|
setisAiWorking(true); |
|
onNewPrompt(prompt); |
|
|
|
const userMessage = { |
|
id: uuidv4(), |
|
type: 'user', |
|
content: prompt, |
|
timestamp: new Date() |
|
}; |
|
setMessages(prev => [...prev, userMessage]); |
|
try { |
|
|
|
const context = { |
|
currentHtml: html !== defaultHTML ? html : "Sem HTML ainda", |
|
previousMessages: messages.slice(-5), |
|
agentId |
|
}; |
|
const resp = await fetch(`/api/agent/${agentId}/message`, { |
|
method: "POST", |
|
headers: { "Content-Type": "application/json" }, |
|
body: JSON.stringify({ |
|
prompt, |
|
html: html === defaultHTML ? undefined : html, |
|
gemini_api_key: geminiKey, |
|
chatgpt_api_key: chatgptKey, |
|
hf_token: hfToken, |
|
provider: "auto", |
|
context: JSON.stringify(context) |
|
}), |
|
}); |
|
const reader = resp.body?.getReader(); |
|
const dec = new TextDecoder("utf-8"); |
|
let partial = ""; |
|
let agentReply = { |
|
id: uuidv4(), |
|
type: 'agent', |
|
content: '', |
|
agentId, |
|
timestamp: new Date() |
|
}; |
|
|
|
setMessages(prev => [...prev, agentReply]); |
|
while (true) { |
|
const { done, value } = await reader.read(); |
|
if (done) |
|
break; |
|
const chunk = dec.decode(value); |
|
|
|
const lines = chunk.split('\n'); |
|
for (const line of lines) { |
|
if (line.trim().startsWith('[LOG]')) { |
|
|
|
const logMessage = { |
|
id: uuidv4(), |
|
type: 'log', |
|
content: line.replace('[LOG] ', ''), |
|
timestamp: new Date() |
|
}; |
|
setMessages(prev => [...prev, logMessage]); |
|
} |
|
else if (line.trim().startsWith('❌')) { |
|
|
|
const errorMessage = { |
|
id: uuidv4(), |
|
type: 'error', |
|
content: line, |
|
timestamp: new Date() |
|
}; |
|
setMessages(prev => [...prev, errorMessage]); |
|
} |
|
else if (line.trim()) { |
|
|
|
partial += line; |
|
setMessages(prev => prev.map(msg => msg.id === agentReply.id |
|
? { ...msg, content: partial } |
|
: msg)); |
|
} |
|
} |
|
if (partial.length > 200) |
|
onScrollToBottom(); |
|
|
|
if (partial.includes("</html>")) { |
|
setLatestHtml(partial); |
|
setHtml(partial); |
|
} |
|
} |
|
} |
|
catch (e) { |
|
toast.error(e.message); |
|
|
|
const errorMessage = { |
|
id: uuidv4(), |
|
type: 'error', |
|
content: e.message, |
|
timestamp: new Date() |
|
}; |
|
setMessages(prev => [...prev, errorMessage]); |
|
} |
|
finally { |
|
setisAiWorking(false); |
|
setPrompt(""); |
|
} |
|
}; |
|
|
|
const clearChat = () => { |
|
if (window.confirm("Tem certeza que deseja limpar todas as mensagens?")) { |
|
setMessages([]); |
|
toast.info("Chat limpo com sucesso"); |
|
} |
|
}; |
|
return (_jsxs("div", { className: "flex flex-col h-full bg-gray-950", children: [_jsxs("div", { className: "bg-gray-900 px-4 py-2 flex justify-between items-center border-b border-gray-800 text-xs text-gray-400", children: [_jsxs("div", { className: "flex items-center", children: [_jsx("span", { className: `w-2 h-2 rounded-full mr-2 ${isAiWorking ? 'bg-green-500 animate-pulse' : 'bg-gray-500'}` }), isAiWorking ? 'Processando...' : 'Pronto'] }), _jsxs("div", { className: "flex space-x-2", children: [_jsx("button", { onClick: clearChat, className: "text-gray-400 hover:text-gray-200 transition-colors", title: "Limpar chat", children: _jsx(MdRefresh, { size: 16 }) }), _jsx("button", { className: "text-gray-400 hover:text-gray-200 transition-colors", title: "Sugest\u00F5es de prompt", children: _jsx(FiInfo, { size: 16 }) })] })] }), _jsxs("div", { className: "flex-1 overflow-y-auto p-3 space-y-3", children: [messages.length === 0 ? (_jsxs("div", { className: "h-full flex flex-col items-center justify-center text-gray-500", children: [_jsx(FiCpu, { size: 32, className: "mb-3" }), _jsxs("p", { className: "text-sm", children: ["Inicie uma conversa com o agente ", agentId] }), _jsxs("div", { className: "mt-4 grid grid-cols-2 gap-2", children: [_jsx(SuggestionButton, { onClick: () => setPrompt("Crie um componente de botão com hover effect"), text: "Criar bot\u00E3o com hover" }), _jsx(SuggestionButton, { onClick: () => setPrompt("Crie um formulário de contato responsivo"), text: "Formul\u00E1rio de contato" }), _jsx(SuggestionButton, { onClick: () => setPrompt("Adicione um cabeçalho com menu de navegação"), text: "Cabe\u00E7alho com menu" }), _jsx(SuggestionButton, { onClick: () => setPrompt("Implemente um dark mode toggle"), text: "Dark mode toggle" })] })] })) : (messages.map(message => (_jsx(ChatMessageComponent, { message: message }, message.id)))), _jsx("div", { ref: chatEndRef })] }), _jsxs("div", { className: "bg-gray-900 p-3 border-t border-gray-800", children: [_jsxs("div", { className: "relative", children: [_jsx("textarea", { className: classNames("w-full px-4 py-3 pr-10 bg-gray-800 border border-gray-700 rounded-md resize-none text-gray-200 placeholder-gray-500 focus:outline-none focus:border-indigo-500 transition-colors", { |
|
"opacity-70": isAiWorking, |
|
}), rows: 2, disabled: isAiWorking, value: prompt, placeholder: `Pergunte algo para ${agentId}...`, onChange: (e) => setPrompt(e.target.value), onKeyDown: (e) => { |
|
if (e.key === "Enter" && !e.shiftKey) { |
|
e.preventDefault(); |
|
send(); |
|
} |
|
} }), _jsx("button", { disabled: isAiWorking || !prompt.trim(), onClick: send, className: classNames("absolute right-3 bottom-3 p-1.5 rounded-full text-white transition-colors", { |
|
"bg-indigo-600 hover:bg-indigo-700": prompt.trim() && !isAiWorking, |
|
"bg-gray-700 cursor-not-allowed": !prompt.trim() || isAiWorking |
|
}), children: _jsx(MdSend, { size: 18 }) })] }), _jsx("div", { className: "text-xs text-gray-500 mt-2 px-2", children: "Pressione Enter para enviar, Shift+Enter para nova linha" })] })] })); |
|
}; |
|
const SuggestionButton = ({ onClick, text }) => (_jsx("button", { onClick: onClick, className: "px-3 py-2 bg-gray-800 hover:bg-gray-700 text-gray-300 rounded-md text-sm text-left transition-colors", children: text })); |
|
export default ChatInterface; |
|
|