|
<!DOCTYPE html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Amside AI - Interfaz con IA Real</title>
|
|
<style>
|
|
:root {
|
|
--primary-color: #22272b;
|
|
--secondary-color: #37404a;
|
|
--accent-color: #00bcd4;
|
|
--text-color-primary: #eee;
|
|
--text-color-secondary: #ccc;
|
|
--error-color: #f44336;
|
|
--border-radius: 8px;
|
|
--box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
|
|
--transition: all 0.3s ease-in-out;
|
|
--sidebar-width: 300px;
|
|
}
|
|
|
|
body {
|
|
font-family: 'Roboto Mono', monospace;
|
|
background-color: var(--primary-color);
|
|
color: var(--text-color-primary);
|
|
margin: 0;
|
|
display: flex;
|
|
min-height: 100vh;
|
|
padding: 0;
|
|
}
|
|
|
|
.sidebar {
|
|
background-color: var(--secondary-color);
|
|
width: var(--sidebar-width);
|
|
border-right: 1px solid #444d57;
|
|
padding: 25px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.sidebar-header {
|
|
color: var(--text-color-primary);
|
|
margin-bottom: 30px;
|
|
text-align: left;
|
|
width: 100%;
|
|
}
|
|
|
|
.sidebar-header h2 {
|
|
margin: 0;
|
|
font-size: 1.8em;
|
|
font-weight: 400;
|
|
}
|
|
|
|
.new-chat-button {
|
|
background-color: var(--accent-color);
|
|
color: var(--text-color-primary);
|
|
border: none;
|
|
padding: 12px 18px;
|
|
border-radius: var(--border-radius);
|
|
cursor: pointer;
|
|
font-size: 1em;
|
|
margin-bottom: 20px;
|
|
transition: background-color var(--transition);
|
|
width: 100%;
|
|
text-align: left;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: flex-start;
|
|
}
|
|
|
|
.new-chat-button svg {
|
|
margin-right: 10px;
|
|
}
|
|
|
|
.chat-list-title {
|
|
font-size: 0.9em;
|
|
color: var(--text-color-secondary);
|
|
margin-bottom: 10px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.8px;
|
|
}
|
|
|
|
.chat-list {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
width: 100%;
|
|
}
|
|
|
|
.chat-list li {
|
|
padding: 10px 15px;
|
|
border-radius: var(--border-radius);
|
|
margin-bottom: 6px;
|
|
cursor: pointer;
|
|
background-color: #2c343c;
|
|
color: var(--text-color-secondary);
|
|
transition: background-color var(--transition), color var(--transition);
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.chat-list li:hover {
|
|
background-color: var(--accent-color);
|
|
color: var(--text-color-primary);
|
|
}
|
|
|
|
.chat-container {
|
|
background-color: var(--primary-color);
|
|
border-radius: var(--border-radius);
|
|
box-shadow: var(--box-shadow);
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
flex-grow: 1;
|
|
margin: 20px;
|
|
border: 1px solid #333a40;
|
|
}
|
|
|
|
.chat-header {
|
|
background-color: var(--secondary-color);
|
|
color: var(--text-color-primary);
|
|
padding: 15px 20px;
|
|
text-align: left;
|
|
border-bottom: 1px solid #444d57;
|
|
border-top-left-radius: var(--border-radius);
|
|
border-top-right-radius: var(--border-radius);
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.chat-header button {
|
|
background: none;
|
|
border: none;
|
|
color: var(--text-color-primary);
|
|
font-size: 1em;
|
|
cursor: pointer;
|
|
margin-right: 20px;
|
|
padding: 8px 12px;
|
|
border-radius: var(--border-radius);
|
|
transition: background-color var(--transition);
|
|
}
|
|
|
|
.chat-header button:hover {
|
|
background-color: #444d57;
|
|
}
|
|
|
|
.chat-header h2 {
|
|
margin: 0;
|
|
font-size: 1.4em;
|
|
font-weight: 400;
|
|
}
|
|
|
|
.chat-body {
|
|
padding: 20px;
|
|
overflow-y: auto;
|
|
flex-grow: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
}
|
|
|
|
.message {
|
|
background-color: #2c343c;
|
|
color: var(--text-color-secondary);
|
|
border-radius: var(--border-radius);
|
|
padding: 10px 14px;
|
|
word-break: break-word;
|
|
align-self: flex-start;
|
|
max-width: 80%;
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
font-size: 0.95em;
|
|
}
|
|
|
|
.error-message {
|
|
background-color: #422b2b;
|
|
color: var(--error-color);
|
|
border-radius: var(--border-radius);
|
|
padding: 10px 14px;
|
|
word-break: break-word;
|
|
align-self: flex-start;
|
|
max-width: 80%;
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
font-size: 0.95em;
|
|
border: 1px solid #5a3232;
|
|
}
|
|
|
|
.user-message-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: flex-end;
|
|
max-width: 80%;
|
|
align-self: flex-end;
|
|
}
|
|
|
|
.user-message {
|
|
background-color: var(--accent-color);
|
|
color: var(--text-color-primary);
|
|
border-radius: var(--border-radius);
|
|
padding: 10px 14px;
|
|
word-break: break-word;
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
font-size: 0.95em;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.loading-indicator {
|
|
display: flex;
|
|
align-self: flex-end;
|
|
margin-top: 5px;
|
|
align-items: center;
|
|
font-size: 0.8em;
|
|
color: var(--text-color-secondary);
|
|
}
|
|
|
|
.loading-spinner {
|
|
border: 2px solid rgba(255, 255, 255, 0.1);
|
|
border-top: 2px solid var(--accent-color);
|
|
border-radius: 50%;
|
|
width: 14px;
|
|
height: 14px;
|
|
animation: spin 1s linear infinite;
|
|
margin-left: 6px;
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
|
|
.input-area {
|
|
padding: 15px;
|
|
border-top: 1px solid #444d57;
|
|
display: flex;
|
|
align-items: center;
|
|
background-color: var(--secondary-color);
|
|
border-bottom-left-radius: var(--border-radius);
|
|
border-bottom-right-radius: var(--border-radius);
|
|
}
|
|
|
|
.input-field {
|
|
flex-grow: 1;
|
|
padding: 10px 12px;
|
|
border: 1px solid #555e68;
|
|
border-radius: var(--border-radius);
|
|
margin-right: 10px;
|
|
font-size: 0.95em;
|
|
color: var(--text-color-primary);
|
|
background-color: #2c343c;
|
|
}
|
|
|
|
.input-field:focus {
|
|
outline: none;
|
|
border-color: var(--accent-color);
|
|
box-shadow: 0 1px 3px rgba(var(--accent-color-rgb), 0.2);
|
|
}
|
|
|
|
.send-button {
|
|
background-color: var(--accent-color);
|
|
color: var(--text-color-primary);
|
|
border: none;
|
|
padding: 10px 15px;
|
|
border-radius: var(--border-radius);
|
|
cursor: pointer;
|
|
font-size: 0.95em;
|
|
transition: background-color var(--transition);
|
|
}
|
|
|
|
.send-button:hover {
|
|
background-color: #00838f;
|
|
}
|
|
</style>
|
|
<link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
</head>
|
|
<body>
|
|
<div class="sidebar">
|
|
<div class="sidebar-header">
|
|
<h2>Amside AI</h2>
|
|
</div>
|
|
<button class="new-chat-button" id="new-chat-button">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-square-fill" viewBox="0 0 16 16">
|
|
<path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z"/>
|
|
</svg>
|
|
Nueva Conversación
|
|
</button>
|
|
<h3 class="chat-list-title">Historial</h3>
|
|
<ul class="chat-list" id="chat-list">
|
|
</ul>
|
|
</div>
|
|
<div class="chat-container">
|
|
<div class="chat-header">
|
|
<button id="new-conversation-header-button">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-square-fill" viewBox="0 0 16 16">
|
|
<path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z"/>
|
|
</svg>
|
|
Nuevo
|
|
</button>
|
|
<h2>Chat</h2>
|
|
</div>
|
|
<div class="chat-body" id="chat-messages">
|
|
</div>
|
|
<div class="input-area">
|
|
<input type="text" class="input-field" placeholder="Escribe tu mensaje...">
|
|
<button class="send-button" id="send-button">Enviar</button>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const chatMessages = document.getElementById('chat-messages');
|
|
const inputField = document.querySelector('.input-field');
|
|
const sendButton = document.getElementById('send-button');
|
|
const chatList = document.getElementById('chat-list');
|
|
const newChatButtonSidebar = document.getElementById('new-chat-button');
|
|
const newChatButtonHeader = document.getElementById('new-conversation-header-button');
|
|
|
|
let chatHistory = [];
|
|
let currentChat = [];
|
|
|
|
|
|
|
|
const BACKEND_URL = 'http://127.0.0.1:5000/chat';
|
|
|
|
|
|
function addMessageToChat(msg) {
|
|
const messageContainer = document.createElement('div');
|
|
if (msg.sender === 'user') {
|
|
messageContainer.classList.add('user-message-container');
|
|
const userMessageDiv = document.createElement('div');
|
|
userMessageDiv.classList.add('user-message');
|
|
userMessageDiv.textContent = msg.text;
|
|
messageContainer.appendChild(userMessageDiv);
|
|
} else if (msg.sender === 'ai') {
|
|
const aiMessageDiv = document.createElement('div');
|
|
aiMessageDiv.classList.add('message');
|
|
aiMessageDiv.textContent = msg.text;
|
|
messageContainer.appendChild(aiMessageDiv);
|
|
} else if (msg.sender === 'error') {
|
|
const errorMessageDiv = document.createElement('div');
|
|
errorMessageDiv.classList.add('error-message');
|
|
errorMessageDiv.textContent = msg.text;
|
|
messageContainer.appendChild(errorMessageDiv);
|
|
}
|
|
chatMessages.appendChild(messageContainer);
|
|
scrollToBottom();
|
|
}
|
|
|
|
|
|
function displayChat(messages) {
|
|
chatMessages.innerHTML = '';
|
|
messages.forEach(msg => {
|
|
const messageContainer = document.createElement('div');
|
|
if (msg.sender === 'user') {
|
|
messageContainer.classList.add('user-message-container');
|
|
const userMessageDiv = document.createElement('div');
|
|
userMessageDiv.classList.add('user-message');
|
|
userMessageDiv.textContent = msg.text;
|
|
messageContainer.appendChild(userMessageDiv);
|
|
} else if (msg.sender === 'ai') {
|
|
const aiMessageDiv = document.createElement('div');
|
|
aiMessageDiv.classList.add('message');
|
|
aiMessageDiv.textContent = msg.text;
|
|
messageContainer.appendChild(aiMessageDiv);
|
|
} else if (msg.sender === 'error') {
|
|
const errorMessageDiv = document.createElement('div');
|
|
errorMessageDiv.classList.add('error-message');
|
|
errorMessageDiv.textContent = msg.text;
|
|
messageContainer.appendChild(errorMessageDiv);
|
|
}
|
|
chatMessages.appendChild(messageContainer);
|
|
});
|
|
scrollToBottom();
|
|
}
|
|
|
|
function updateChatList() {
|
|
chatList.innerHTML = '';
|
|
chatHistory.forEach((chat, index) => {
|
|
const listItem = document.createElement('li');
|
|
listItem.textContent = chat.name.substring(0, 22) + (chat.name.length > 22 ? '...' : '');
|
|
listItem.addEventListener('click', () => {
|
|
currentChat = chat.messages;
|
|
displayChat(currentChat);
|
|
});
|
|
chatList.appendChild(listItem);
|
|
});
|
|
}
|
|
|
|
function saveChat(firstUserMessage) {
|
|
if (currentChat.length > 0 && firstUserMessage) {
|
|
const chatName = firstUserMessage.substring(0, 25);
|
|
if (!chatHistory.some(chat => chat.messages === currentChat) && currentChat.length > 1) {
|
|
chatHistory.unshift({ name: chatName, messages: [...currentChat] });
|
|
updateChatList();
|
|
}
|
|
}
|
|
}
|
|
|
|
function startNewChat() {
|
|
if (currentChat.length > 1 && currentChat[0].sender === 'ai' && currentChat[1].sender === 'user') {
|
|
saveChat(currentChat[1].text);
|
|
}
|
|
currentChat = [{ sender: 'ai', text: 'Bienvenido a Amside AI. Estoy aquí para ayudarte. ¿Qué te gustaría saber?' }];
|
|
displayChat(currentChat);
|
|
}
|
|
|
|
sendButton.addEventListener('click', async () => {
|
|
const messageText = inputField.value.trim();
|
|
if (messageText === '') {
|
|
return;
|
|
}
|
|
|
|
const isFirstUserMessageInChat = currentChat.length === 1 && currentChat[0].sender === 'ai';
|
|
|
|
const userMessage = { sender: 'user', text: messageText };
|
|
currentChat.push(userMessage);
|
|
|
|
const userMessageContainer = document.createElement('div');
|
|
userMessageContainer.classList.add('user-message-container');
|
|
const userMessageDiv = document.createElement('div');
|
|
userMessageDiv.classList.add('user-message');
|
|
userMessageDiv.textContent = userMessage.text;
|
|
userMessageContainer.appendChild(userMessageDiv);
|
|
|
|
const loadingIndicator = document.createElement('div');
|
|
loadingIndicator.classList.add('loading-indicator');
|
|
loadingIndicator.innerHTML = '<div class="loading-spinner"></div> Procesando...';
|
|
userMessageContainer.appendChild(loadingIndicator);
|
|
|
|
chatMessages.appendChild(userMessageContainer);
|
|
scrollToBottom();
|
|
|
|
inputField.value = '';
|
|
|
|
try {
|
|
|
|
|
|
const messagesForBackend = currentChat.map(msg => ({
|
|
role: msg.sender === 'user' ? 'user' : 'assistant',
|
|
content: msg.text
|
|
}));
|
|
|
|
const response = await fetch(BACKEND_URL, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({ messages: messagesForBackend }),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json();
|
|
throw new Error(errorData.error || `HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
const aiResponseText = data.response;
|
|
|
|
const aiResponse = { sender: 'ai', text: aiResponseText };
|
|
currentChat.push(aiResponse);
|
|
|
|
if (loadingIndicator && userMessageContainer.contains(loadingIndicator)) {
|
|
loadingIndicator.remove();
|
|
}
|
|
|
|
addMessageToChat(aiResponse);
|
|
|
|
if (isFirstUserMessageInChat) {
|
|
saveChat(messageText);
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error("Error al obtener respuesta de la IA (backend):", error);
|
|
if (loadingIndicator && userMessageContainer.contains(loadingIndicator)) {
|
|
loadingIndicator.remove();
|
|
}
|
|
const errorMessage = { sender: 'error', text: `Lo siento, hubo un error al conectar con la IA. Asegúrate de que el servidor está corriendo. Detalle: ${error.message}` };
|
|
currentChat.push(errorMessage);
|
|
addMessageToChat(errorMessage);
|
|
}
|
|
});
|
|
|
|
inputField.addEventListener('keypress', (event) => {
|
|
if (event.key === 'Enter') {
|
|
sendButton.click();
|
|
}
|
|
});
|
|
|
|
newChatButtonSidebar.addEventListener('click', startNewChat);
|
|
newChatButtonHeader.addEventListener('click', startNewChat);
|
|
|
|
function scrollToBottom() {
|
|
chatMessages.scrollTop = chatMessages.scrollHeight;
|
|
}
|
|
|
|
|
|
startNewChat();
|
|
updateChatList();
|
|
</script>
|
|
</body>
|
|
</html> |