// static/chat.js function generateUUID() { if (typeof crypto !== 'undefined' && crypto.randomUUID) return crypto.randomUUID(); return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { const r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); } window.currentThreadId = null; window.addEventListener('DOMContentLoaded', async () => { await loadThreads(); const threads = await fetch(ROUTES.GET_ALL_THREADS, { headers: AUTH_HEADERS() }).then(r => r.json()); if (threads.threads && threads.threads.length > 0) { selectThread(threads.threads[0]); } else { createNewThread(); } }); async function loadThreads() { const res = await fetch(ROUTES.GET_ALL_THREADS, { headers: AUTH_HEADERS() }); const data = await res.json(); const list = document.getElementById('threadsList'); if (!list) return; list.innerHTML = ''; (data.threads || []).forEach(id => { const item = document.createElement('div'); item.className = 'thread-item' + (window.currentThreadId === id ? ' active' : ''); item.onclick = () => selectThread(id); item.innerHTML = `
💬 Thread ${id.substring(0, 8)}
`; list.appendChild(item); }); } async function deleteThread(id) { if (!confirm('Are you sure you want to delete this thread?')) return; try { const res = await fetch(ROUTES.DELETE_THREAD(id), { method: 'DELETE', headers: AUTH_HEADERS() }); if (res.ok) { if (window.currentThreadId === id) { window.currentThreadId = null; document.getElementById('messagesContainer').innerHTML = '
Thread deleted. Select another or create new.
'; document.getElementById('sessionIdDisplay').textContent = 'Thread ID: ---'; } await loadThreads(); } else { alert('Failed to delete thread'); } } catch (err) { console.error('Delete error:', err); alert('Error deleting thread'); } } async function selectThread(id) { window.currentThreadId = id; document.getElementById('sessionIdDisplay').textContent = `Session: ${id.substring(0, 12)}...`; const container = document.getElementById('messagesContainer'); container.innerHTML = '
Loading conversation...
'; try { const res = await fetch(ROUTES.LOAD_CONVERSATION(id), { headers: AUTH_HEADERS() }); const data = await res.json(); container.innerHTML = ''; if (data.messages && data.messages.length > 0) { data.messages.forEach(msg => { const role = msg.type === 'human' ? 'user' : 'assistant'; appendMessage(role, msg.content || (typeof msg === 'string' ? msg : '')); }); } else { appendMessage('assistant', 'Conversation empty. How can I help?'); } } catch (err) { container.innerHTML = '
Error loading thread.
'; } loadThreads(); } function createNewThread() { const id = generateUUID(); window.currentThreadId = id; document.getElementById('messagesContainer').innerHTML = ''; document.getElementById('sessionIdDisplay').textContent = `Session: ${id.substring(0, 12)}...`; appendMessage('assistant', 'New thread started. I am ready to help!'); loadThreads(); } function appendMessage(role, text) { const container = document.getElementById('messagesContainer'); const msgDiv = document.createElement('div'); msgDiv.className = `message ${role}`; const avatar = document.createElement('div'); avatar.className = 'message-avatar'; avatar.textContent = role === 'user' ? 'U' : 'AI'; const content = document.createElement('div'); content.className = 'message-content'; content.innerHTML = (role === 'assistant' && typeof marked !== 'undefined') ? marked.parse(text) : text; msgDiv.appendChild(avatar); msgDiv.appendChild(content); // Create a wrapper to center it const wrapper = document.createElement('div'); wrapper.className = 'message-container'; wrapper.appendChild(msgDiv); container.appendChild(wrapper); container.scrollTop = container.scrollHeight; } async function sendMessage() { const input = document.getElementById('messageInput'); const text = input.value.trim(); if (!text) return; input.value = ''; input.style.height = 'auto'; appendMessage('user', text); const res = await fetch(ROUTES.CHAT_MESSAGE(text), { method: 'POST', headers: AUTH_HEADERS() }); const data = await res.json(); if (res.ok) { appendMessage('assistant', data.data || 'No response'); loadThreads(); } } function handleKeyDown(event) { if (event.key === 'Enter' && !event.shiftKey) { event.preventDefault(); sendMessage(); } } function autoResize(el) { el.style.height = 'auto'; el.style.height = Math.min(el.scrollHeight, 200) + 'px'; } // Modal functions function showUrlModal() { document.getElementById('urlModal').style.display = 'flex'; } function hideUrlModal() { document.getElementById('urlModal').style.display = 'none'; } async function handleUrlUpload() { const url = document.getElementById('urlInput').value.trim(); if (!url) return; const status = document.getElementById('urlUploadStatus'); status.textContent = 'Processing...'; const res = await fetch(ROUTES.UPLOAD_URL(url), { method: 'POST', headers: AUTH_HEADERS() }); if (res.ok) { status.textContent = '✓ Success'; appendMessage('assistant', `Website **${url}** integrated.`); setTimeout(hideUrlModal, 1000); } else { status.textContent = '✗ Error'; } } async function handleFileUpload(event) { const file = event.target.files[0]; if (!file) return; const formData = new FormData(); formData.append('file', file); const res = await fetch(ROUTES.UPLOAD_FILE, { method: 'POST', headers: { 'user_id': getUserId(), 'thread_id': window.currentThreadId }, body: formData }); if (res.ok) { appendMessage('assistant', `File **${file.name}** uploaded.`); } }