SQL_agent / templates /index.html
WebashalarForML's picture
Update templates/index.html
c2c8c47 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Agent Chat</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.5.4/socket.io.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--primary: #10a37f;
--primary-dark: #0d8a6a;
--bg-color: #343541;
--chat-bg: #444654;
--user-bg: #343541;
--text-color: #ececf1;
--text-secondary: #acacbe;
--border-color: #565869;
--log-bg: #2a2b32;
--error-color: #ef4146;
--warning-color: #f0b72f;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
display: flex;
flex-direction: column;
height: 100vh;
overflow: hidden;
}
nav {
background-color: var(--user-bg);
padding: 12px 20px;
border-bottom: 1px solid var(--border-color);
display: flex;
justify-content: space-between;
align-items: center;
}
.nav-title {
font-size: 1.2rem;
font-weight: 600;
}
.nav-actions {
display: flex;
gap: 10px;
}
.btn {
background-color: var(--primary);
color: white;
border: none;
border-radius: 4px;
padding: 8px 12px;
font-size: 0.9rem;
cursor: pointer;
display: flex;
align-items: center;
gap: 6px;
transition: background-color 0.2s;
}
.btn:hover {
background-color: var(--primary-dark);
}
.btn-sm {
padding: 6px 10px;
font-size: 0.8rem;
}
.btn-icon {
padding: 8px;
border-radius: 4px;
background: transparent;
color: var(--text-color);
}
.btn-icon:hover {
background-color: rgba(255,255,255,0.1);
}
main {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
.chat-container {
flex: 1;
overflow-y: auto;
padding: 20px 0;
}
.message {
padding: 20px;
display: flex;
max-width: 900px;
margin: 0 auto;
gap: 20px;
}
.message-user {
background-color: var(--user-bg);
}
.message-agent {
background-color: var(--chat-bg);
}
.avatar {
width: 36px;
height: 36px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.avatar-user {
background-color: var(--primary);
}
.avatar-agent {
background-color: #5436da;
}
.message-content {
flex: 1;
line-height: 1.5;
padding-top: 4px;
}
.logs-container {
background-color: var(--log-bg);
border-top: 1px solid var(--border-color);
padding: 12px 20px;
max-height: 120px;
overflow-y: auto;
font-size: 0.85rem;
color: var(--text-secondary);
transition: all 0.3s ease;
}
.logs-container.collapsed {
max-height: 0;
padding: 0;
overflow: hidden;
}
.log-entry {
margin-bottom: 6px;
display: flex;
gap: 10px;
}
.log-timestamp {
color: var(--text-secondary);
flex-shrink: 0;
}
.log-message {
flex: 1;
}
.log-error {
color: var(--error-color);
}
.log-warning {
color: var(--warning-color);
}
.input-container {
padding: 20px;
border-top: 1px solid var(--border-color);
background-color: var(--user-bg);
position: relative;
}
.input-wrapper {
max-width: 900px;
margin: 0 auto;
position: relative;
}
.input-actions {
position: absolute;
right: 12px;
bottom: 12px;
display: flex;
gap: 8px;
z-index: 2;
}
textarea {
width: 100%;
min-height: 60px;
max-height: 200px;
padding: 12px 50px 12px 16px;
border-radius: 8px;
border: 1px solid var(--border-color);
background-color: var(--chat-bg);
color: var(--text-color);
resize: none;
font-size: 1rem;
line-height: 1.5;
}
textarea:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 1px var(--primary);
}
.typing-indicator {
display: flex;
gap: 4px;
padding: 0 20px 10px;
color: var(--text-secondary);
font-size: 0.9rem;
}
.typing-dots {
display: flex;
gap: 2px;
align-items: flex-end;
}
.typing-dot {
width: 6px;
height: 6px;
background-color: var(--text-secondary);
border-radius: 50%;
animation: typingAnimation 1.4s infinite ease-in-out;
}
.typing-dot:nth-child(1) {
animation-delay: 0s;
}
.typing-dot:nth-child(2) {
animation-delay: 0.2s;
}
.typing-dot:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes typingAnimation {
0%, 60%, 100% { transform: translateY(0); }
30% { transform: translateY(-4px); }
}
footer {
background-color: var(--user-bg);
padding: 12px 20px;
border-top: 1px solid var(--border-color);
font-size: 0.8rem;
color: var(--text-secondary);
text-align: center;
}
/* Add this new style for log toggle */
.logs-container {
position: relative;
background-color: var(--log-bg);
border-top: 1px solid var(--border-color);
padding: 12px 20px;
max-height: 120px;
overflow-y: auto;
font-size: 0.85rem;
color: var(--text-secondary);
transition: all 0.3s ease;
}
.log-toggle {
position: absolute;
right: 20px;
top: -12px;
background: var(--log-bg);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 2px 8px;
font-size: 0.7rem;
cursor: pointer;
z-index: 1;
color: var(--text-secondary);
}
.log-toggle:hover {
background: var(--border-color);
}
.alert {
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
padding: 12px 20px;
border-radius: 8px;
background-color: var(--error-color);
color: white;
z-index: 1000;
display: flex;
align-items: center;
gap: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
animation: slideIn 0.3s ease-out;
}
.alert-success {
background-color: var(--primary);
}
.alert-warning {
background-color: var(--warning-color);
}
@keyframes slideIn {
from { top: -50px; opacity: 0; }
to { top: 20px; opacity: 1; }
}
.markdown-content pre {
background-color: rgba(0,0,0,0.2);
padding: 12px;
border-radius: 6px;
overflow-x: auto;
margin: 12px 0;
}
.markdown-content code {
font-family: 'Courier New', Courier, monospace;
font-size: 0.9rem;
}
</style>
</head>
<body>
<nav>
<div class="nav-title">Agent Chat</div>
<div class="nav-actions">
<button class="btn">
<i class="fas fa-plus"></i> New Chat
</button>
</div>
</nav>
<main>
<div class="chat-container" id="chat">
<!-- Messages will be inserted here -->
</div>
<div class="typing-indicator" id="typing-indicator" style="display: none;">
<div class="typing-dots">
<div class="typing-dot"></div>
<div class="typing-dot"></div>
<div class="typing-dot"></div>
</div>
<span>Agent is typing...</span>
</div>
<div class="input-container">
<div class="input-wrapper">
<textarea id="prompt" rows="1" placeholder="Message Agent..." autofocus></textarea>
<div class="input-actions">
<button class="btn-icon" id="upload-btn" title="Upload Database">
<i class="fas fa-database"></i>
</button>
<button class="btn-icon" id="send-btn" title="Send message">
<i class="fas fa-paper-plane"></i>
</button>
</div>
</div>
</div>
<div class="logs-container" id="logs-container">
<button class="log-toggle" id="log-toggle">Hide Logs</button>
<div id="logs"></div>
</div>
</main>
<footer>
<div>Agent Chat v1.0 · © 2023</div>
</footer>
<div id="flash-message" class="alert" style="display: none;"></div>
<script>
const socket = io();
const chatContainer = document.getElementById("chat");
const logsContainer = document.getElementById("logs");
const sendButton = document.getElementById("send-btn");
const uploadButton = document.getElementById("upload-btn");
const promptTextarea = document.getElementById("prompt");
const flashMessage = document.getElementById('flash-message');
const typingIndicator = document.getElementById('typing-indicator');
const logToggle = document.getElementById('log-toggle');
const logsContainerElement = document.getElementById('logs-container');
// Auto-resize textarea
promptTextarea.addEventListener('input', function() {
this.style.height = 'auto';
this.style.height = (this.scrollHeight) + 'px';
});
// Handle Enter key (Shift+Enter for new line)
promptTextarea.addEventListener('keydown', function(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
// Send button click handler
sendButton.addEventListener('click', sendMessage);
// Upload button click handler
uploadButton.addEventListener('click', () => {
window.location.href = '/upload';
});
// Log toggle handler
logToggle.addEventListener('click', () => {
logsContainerElement.classList.toggle('collapsed');
logToggle.textContent = logsContainerElement.classList.contains('collapsed') ? 'Show Logs' : 'Hide Logs';
});
function sendMessage() {
const prompt = promptTextarea.value.trim();
if (!prompt) return;
addMessage("user", prompt);
promptTextarea.value = '';
promptTextarea.style.height = 'auto';
typingIndicator.style.display = 'flex';
fetch("/generate", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ prompt: prompt })
});
}
function addMessage(sender, text) {
const messageDiv = document.createElement("div");
messageDiv.classList.add("message", `message-${sender}`);
const avatarDiv = document.createElement("div");
avatarDiv.classList.add("avatar", `avatar-${sender}`);
avatarDiv.innerHTML = sender === 'user' ?
'<i class="fas fa-user"></i>' :
'<i class="fas fa-robot"></i>';
const contentDiv = document.createElement("div");
contentDiv.classList.add("message-content");
contentDiv.innerHTML = `<div class="markdown-content">${text}</div>`;
messageDiv.appendChild(avatarDiv);
messageDiv.appendChild(contentDiv);
chatContainer.appendChild(messageDiv);
chatContainer.scrollTop = chatContainer.scrollHeight;
}
function addLogMessage(text, type = 'info') {
const now = new Date();
const timestamp = now.toLocaleTimeString();
const logDiv = document.createElement("div");
logDiv.classList.add("log-entry");
const timeDiv = document.createElement("div");
timeDiv.classList.add("log-timestamp");
timeDiv.textContent = timestamp;
const messageDiv = document.createElement("div");
messageDiv.classList.add("log-message");
if (type !== 'info') messageDiv.classList.add(`log-${type}`);
messageDiv.textContent = text;
logDiv.appendChild(timeDiv);
logDiv.appendChild(messageDiv);
logsContainer.appendChild(logDiv);
logsContainer.scrollTop = logsContainer.scrollHeight;
}
function showFlashMessage(message, type = 'error') {
flashMessage.textContent = message;
flashMessage.className = `alert alert-${type}`;
flashMessage.style.display = 'flex';
setTimeout(() => {
flashMessage.style.display = 'none';
}, 3000);
}
// Socket.io handlers
let agentMessageDiv = null;
socket.on("final_stream", (data) => {
if (!agentMessageDiv) {
agentMessageDiv = document.createElement("div");
agentMessageDiv.classList.add("message", "message-agent");
const avatarDiv = document.createElement("div");
avatarDiv.classList.add("avatar", "avatar-agent");
avatarDiv.innerHTML = '<i class="fas fa-robot"></i>';
const contentDiv = document.createElement("div");
contentDiv.classList.add("message-content");
contentDiv.id = "agent-message-content";
agentMessageDiv.appendChild(avatarDiv);
agentMessageDiv.appendChild(contentDiv);
chatContainer.appendChild(agentMessageDiv);
}
const contentDiv = document.getElementById("agent-message-content");
contentDiv.innerHTML = `<div class="markdown-content">${data.message}</div>`;
chatContainer.scrollTop = chatContainer.scrollHeight;
});
socket.on("final", (data) => {
typingIndicator.style.display = 'none';
if (agentMessageDiv) {
const contentDiv = document.getElementById("agent-message-content");
contentDiv.innerHTML = `<div class="markdown-content">${data.message}</div>`;
agentMessageDiv = null;
} else {
addMessage("agent", data.message);
}
});
socket.on("log", (data) => {
addLogMessage(data.message, data.type || 'info');
});
socket.on("error", (data) => {
showFlashMessage(data.message, 'error');
typingIndicator.style.display = 'none';
});
// Handle flash messages from server
if (flashMessage) {
setTimeout(() => {
flashMessage.style.display = 'none';
}, 3000);
}
</script>
</body>
</html>