sql_agent / templates /dynamicDB_index_test2.html
prthm11's picture
Upload dynamicDB_index_test2.html
fa831e3 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>
<link rel="icon" href="data:," />
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.5.4/socket.io.js"></script>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/emoji-picker-element"></script>
<emoji-picker id="emoji-picker" style="position: absolute; bottom: 60px; right: 20px; display: none;"></emoji-picker>
<style>
* {
box-sizing: border-box;
}
body {
background: linear-gradient(135deg, rgb(44, 65, 112), #151f35);
color: #f0f0f0;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
padding: 0;
overflow-x: hidden;
}
.container {
max-width: 900px;
margin: 40px auto;
padding: 20px;
border-radius: 20px;
backdrop-filter: blur(12px);
background: rgba(255, 255, 255, 0.04);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2);
transition: all 0.3s ease-in-out;
width:90%;
}
h1 {
text-align: center;
font-size: 2.2rem;
margin-bottom: 30px;
color: #7ee8fa;
letter-spacing: 1px;
text-shadow: 0 2px 4px #000;
}
.chat-container {
height: 450px;
overflow-y: auto;
padding: 20px;
background: rgba(255, 255, 255, 0.05);
border-radius: 15px;
border: 1px solid rgba(255, 255, 255, 0.05);
box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.25);
backdrop-filter: blur(8px);
animation: fadeInUp 0.6s ease;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(15px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Custom scroll bar styling */
.chat-container::-webkit-scrollbar {
width: 8px;
}
.chat-container::-webkit-scrollbar-track {
background: #0f1b33;
border-radius: 4px;
}
.chat-container::-webkit-scrollbar-thumb {
background: #444;
border-radius: 4px;
}
.chat-container::-webkit-scrollbar-thumb:hover {
background: #555;
}
.chat-container {
scrollbar-width: thin;
scrollbar-color: #444 #0f1b33;
}
.message {
margin-bottom: 15px;
display: flex;
align-items: flex-start;
opacity: 0;
animation: fadeIn 0.5s ease forwards;
}
.message.user {
justify-content: flex-end;
}
.message.agent {
justify-content: flex-start;
}
.bubble {
padding: 8px 14px;
font-size: 14px;
line-height: 1.2;
border-radius: 14px;
max-width: 70%;
word-wrap: break-word;
position: relative;
line-height: 1.5;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
transition: all 0.3s ease;
}
.bubble.user {
background: linear-gradient(145deg, #007bff, #0056b3);
color: white;
border-bottom-right-radius: 0;
}
.bubble.agent {
background: linear-gradient(145deg, #2d3a55, #1b2a3a);
color: #f3f3f3;
border-bottom-left-radius: 0;
}
.bubble.agent.thought-bubble {
background: rgba(16, 52, 74, 0.8);
font-style: italic;
border-left: 4px solid #00e0ff;
color: #7ee8fa;
}
.thought-toggle {
color: #ff8800;
font-size: 0.85rem;
cursor: pointer;
display: inline-block;
margin-top: 5px;
}
.input-area {
display: flex;
gap: 10px;
margin-top: 15px;
align-items: center;
}
.input-area textarea {
flex: 1;
padding: 12px;
border-radius: 10px;
background: rgba(255, 255, 255, 0.08);
color: #eaeaea;
border: 1px solid rgba(255, 255, 255, 0.1);
resize: none;
transition: box-shadow 0.3s ease;
}
.input-area textarea:focus {
outline: none;
box-shadow: 0 0 8px #00e0ff;
}
.input-area button {
padding: 12px 20px;
border-radius: 10px;
border: none;
background: #00a8ff;
color: #fff;
cursor: pointer;
transition: background 0.3s;
}
#upload-db {
background: none;
border: none;
cursor: pointer;
/* margin-left: 5px; */
}
#upload-db img {
width: 28px;
height: 28px;
}
.input-area button:disabled {
background-color: #555;
cursor: not-allowed;
}
.input-area button:hover:not(:disabled) {
background: #0077c2;
}
.toast {
position: fixed;
bottom: 25px;
right: 25px;
background: #2c3e50;
padding: 12px 22px;
border-radius: 12px;
font-size: 0.95rem;
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3);
opacity: 0.95;
display: none;
z-index: 1000;
transition: all 0.3s ease-in-out;
}
/* Typing animation dots */
.dot {
animation: blink 1.2s infinite;
display: inline-block;
margin-left: 2px;
}
.dot:nth-child(2) {
animation-delay: 0.2s;
}
.dot:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes blink {
0% {
opacity: 0.2;
}
20% {
opacity: 1;
}
100% {
opacity: 0.2;
}
}
@keyframes fadeIn {
to {
opacity: 1;
}
}
.thought-toggle {
margin-top: 4px;
font-size: 0.8rem;
color: #ffbb00;
cursor: pointer;
transition: color 0.3s;
}
.thought-toggle:hover {
color: #fff000;
}
</style>
</head>
<body>
<div class="container">
<h1>Agent Chat</h1>
<div class="chat-container" id="chat"></div>
<div class="input-area">
<textarea id="prompt" rows="2" placeholder="Type your message…"></textarea>
<button id="upload-db" title="Upload Database"><img src="/static/db_icon.svg" alt="DB" /></button>
<button id="send">Send</button>
<!--<button id="emoji-button">😊</button>-->
<input type="file" id="file-input" accept=".db" style="display:none;" />
</div>
</div>
<div id="toast" class="toast"></div>
<script>
const socket = io()
const chat = document.getElementById('chat')
const toast = document.getElementById('toast')
const sendBtn = document.getElementById('send')
const promptTA = document.getElementById('prompt')
const uploadBtn = document.getElementById('upload-db')
const fileInput = document.getElementById('file-input')
let typingBubble = null
let agentBubble = null
function addMessage(sender, html) {
const msg = document.createElement('div')
msg.className = `message ${sender}`
const bubble = document.createElement('div')
bubble.className = `bubble ${sender}`
bubble.innerHTML = marked.parse(html)
msg.appendChild(bubble)
chat.appendChild(msg)
chat.scrollTop = chat.scrollHeight
}
function showToast(text, duration = 3000) {
toast.textContent = text
toast.style.display = 'block'
setTimeout(() => (toast.style.display = 'none'), duration)
}
// Upload DB
uploadBtn.addEventListener('click', () => fileInput.click())
fileInput.addEventListener('change', e => handleFile(e.target.files[0]));
function handleFile(file) {
if (!file) return // Get the uploaded file name
showToast('Uploading database…')
const fd = new FormData()
fd.append('file', file)
fetch('/upload_db', { method: 'POST', body: fd })
.then((r) => r.json())
.then((data) => {
if (data.success || data.status === 'ok' || data.success === true) {
showToast('Database initialized!', 2000)
addMessage('agent', 'File uploaded and processed successfully!')
} else {
addMessage('agent', `Error: ${data.message}`)
}
})
.catch((err) => addMessage('agent', `Upload error: ${err}`))
}
// Send Prompt
function sendPrompt() {
const text = promptTA.value.trim()
if (!text || sendBtn.disabled) return
addMessage('user', text)
promptTA.value = ''
sendBtn.disabled = true
// typing indicator
typingBubble = document.createElement('div')
typingBubble.className = 'message agent'
typingBubble.innerHTML = `<div class="bubble agent">Thinking<span class="dot">.</span><span class="dot">.</span><span class="dot">.</span></div>`
chat.appendChild(typingBubble)
chat.scrollTop = chat.scrollHeight
fetch('/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ prompt: text })
}).catch((err) => {
addMessage('agent', `Error: ${err}`)
sendBtn.disabled = false
if (typingBubble) chat.removeChild(typingBubble)
})
}
// Send message
sendBtn.addEventListener('click', () => {
const text = promptTA.value.trim()
if (!text) return
addMessage('user', text)
socket.emit('prompt', text)
promptTA.value = ''
})
// Handle agent reply
socket.on('response', (text) => {
addMessage('agent', text)
})
// Handle agent thoughts
socket.on('thought', (text) => {
if (agentBubble) agentBubble.remove()
const msg = document.createElement('div')
msg.className = 'message agent'
agentBubble = document.createElement('div')
agentBubble.className = 'bubble agent thought-bubble'
agentBubble.textContent = text
msg.appendChild(agentBubble)
chat.appendChild(msg)
chat.scrollTop = chat.scrollHeight
})
// Enter to send when not processing
promptTA.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
sendPrompt()
}
})
// Socket.IO handlers
socket.on('final_stream', (data) => {
if (!agentBubble) {
agentBubble = document.createElement('div')
agentBubble.className = 'message agent'
const b = document.createElement('div')
b.className = 'bubble agent'
agentBubble.appendChild(b)
chat.appendChild(agentBubble)
}
const b = agentBubble.querySelector('.bubble')
b.textContent += data.message
chat.scrollTop = chat.scrollHeight
})
socket.on('final', (data) => {
if (typingBubble) {
chat.removeChild(typingBubble)
typingBubble = null
}
agentBubble = null
sendBtn.disabled = false
// Typing animation
const msg = document.createElement('div')
msg.className = 'message agent'
const bubble = document.createElement('div')
bubble.className = 'bubble agent'
msg.appendChild(bubble)
chat.appendChild(msg)
chat.scrollTop = chat.scrollHeight
const text = data.message
let index = 0
const typingInterval = setInterval(() => {
if (index < text.length) {
bubble.textContent += text.charAt(index)
index++
chat.scrollTop = chat.scrollHeight
} else {
clearInterval(typingInterval)
}
}, 50) // Adjust typing speed here
})
function scrollToBottom() {
chat.scrollTop = chat.scrollHeight
// Alternatively, for smooth scrolling:
// chat.lastElementChild.scrollIntoView({ behavior: 'smooth' });
}
/*const emojiPicker = document.getElementById('emoji-picker');
const emojiButton = document.getElementById('emoji-button');
// Toggle emoji picker
emojiButton.addEventListener('click', () => {
emojiPicker.style.display = emojiPicker.style.display === 'none' ? 'block' : 'none';
});
// Insert emoji into textarea when selected
emojiPicker.addEventListener('emoji-click', event => {
const emoji = event.detail.unicode;
const cursorPos = promptTA.selectionStart;
const text = promptTA.value;
promptTA.value = text.slice(0, cursorPos) + emoji + text.slice(cursorPos);
promptTA.focus();
}); */
</script>
</body>
</html>