klawdyoss's picture
salvando
8a0e39e
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);
// Efeito para rolar para o final quando novas mensagens chegarem
useEffect(() => {
chatEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
// Função principal para enviar mensagem
const send = async () => {
if (!prompt.trim() || isAiWorking)
return;
setisAiWorking(true);
onNewPrompt(prompt);
// Adiciona mensagem do usuário ao histórico
const userMessage = {
id: uuidv4(),
type: 'user',
content: prompt,
timestamp: new Date()
};
setMessages(prev => [...prev, userMessage]);
try {
// Registra o contexto atual para o agente orquestrador (opcional)
const context = {
currentHtml: html !== defaultHTML ? html : "Sem HTML ainda",
previousMessages: messages.slice(-5), // Últimas 5 mensagens
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) // Enviando contexto para o servidor
}),
});
const reader = resp.body?.getReader();
const dec = new TextDecoder("utf-8");
let partial = "";
let agentReply = {
id: uuidv4(),
type: 'agent',
content: '',
agentId,
timestamp: new Date()
};
// Adiciona a mensagem vazia inicial do agente
setMessages(prev => [...prev, agentReply]);
while (true) {
const { done, value } = await reader.read();
if (done)
break;
const chunk = dec.decode(value);
// Processa os logs separadamente
const lines = chunk.split('\n');
for (const line of lines) {
if (line.trim().startsWith('[LOG]')) {
// Adiciona logs como mensagens de tipo log
const logMessage = {
id: uuidv4(),
type: 'log',
content: line.replace('[LOG] ', ''),
timestamp: new Date()
};
setMessages(prev => [...prev, logMessage]);
}
else if (line.trim().startsWith('❌')) {
// Adiciona erros como mensagens de tipo error
const errorMessage = {
id: uuidv4(),
type: 'error',
content: line,
timestamp: new Date()
};
setMessages(prev => [...prev, errorMessage]);
}
else if (line.trim()) {
// Atualiza o conteúdo da resposta do agente
partial += line;
setMessages(prev => prev.map(msg => msg.id === agentReply.id
? { ...msg, content: partial }
: msg));
}
}
if (partial.length > 200)
onScrollToBottom();
// Se é um HTML válido, atualiza o editor
if (partial.includes("</html>")) {
setLatestHtml(partial);
setHtml(partial);
}
}
}
catch (e) {
toast.error(e.message);
// Adiciona erro como mensagem
const errorMessage = {
id: uuidv4(),
type: 'error',
content: e.message,
timestamp: new Date()
};
setMessages(prev => [...prev, errorMessage]);
}
finally {
setisAiWorking(false);
setPrompt("");
}
};
// Função para limpar o chat
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;