(function() { const sidebar = document.getElementById('chat-sidebar'); if (!sidebar) return; const messagesEl = document.getElementById('chat-sidebar-messages'); const form = document.getElementById('chat-sidebar-form'); const input = document.getElementById('chat-sidebar-input'); const status = document.getElementById('chat-sidebar-status'); function setStatus(msg, isError) { if (status) { status.textContent = msg; status.style.color = isError ? 'var(--del-color, #b33)' : 'var(--muted-color, #888)'; } } function appendMessage(role, content) { const div = document.createElement('div'); div.className = 'chat-message ' + role; div.innerHTML = '

' + content + '

'; messagesEl.appendChild(div); messagesEl.scrollTop = messagesEl.scrollHeight; } function removeLoading() { const ld = messagesEl.querySelector('.chat-sidebar-loading'); if (ld) ld.remove(); } async function loadMessages() { try { const resp = await fetch('/api/chat/messages'); if (!resp.ok) return; const data = await resp.json(); removeLoading(); for (const m of data) { appendMessage(m.role, m.content); } } catch (e) { removeLoading(); messagesEl.innerHTML = '

Could not load messages.

'; } } async function sendMessage(event) { event.preventDefault(); const text = input.value.trim(); if (!text) return; removeLoading(); appendMessage('user', text); input.value = ''; input.disabled = true; setStatus('Waiting...'); const assistantDiv = document.createElement('div'); assistantDiv.className = 'chat-message assistant'; assistantDiv.innerHTML = '

Thinking...

'; messagesEl.appendChild(assistantDiv); try { const formData = new FormData(); formData.append('message', text); const response = await fetch('/chat', { method: 'POST', body: formData }); if (!response.ok) throw new Error('Server returned ' + response.status); const reader = response.body.getReader(); const decoder = new TextDecoder(); const p = assistantDiv.querySelector('p'); p.innerHTML = ''; let buffer = ''; while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const lines = buffer.split('\n'); buffer = lines.pop() || ''; for (const line of lines) { if (line.startsWith('data: ')) { p.innerHTML += line.slice(6); } } messagesEl.scrollTop = messagesEl.scrollHeight; } if (!p.innerHTML.trim()) { p.innerHTML = 'No response.'; } setStatus(''); } catch (err) { assistantDiv.querySelector('p').innerHTML = 'Error.'; setStatus('Connection error. Is opencode serve running?', true); } input.disabled = false; input.focus(); } if (form) form.addEventListener('submit', sendMessage); if (input) { input.addEventListener('keydown', function(e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); form.dispatchEvent(new Event('submit')); } }); } loadMessages(); })();