120 lines
4.1 KiB
JavaScript
120 lines
4.1 KiB
JavaScript
(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 = '<p>' + content + '</p>';
|
|
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 = '<p class="chat-sidebar-loading">Could not load messages.</p>';
|
|
}
|
|
}
|
|
|
|
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 = '<p><em>Thinking...</em></p>';
|
|
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: ')) {
|
|
const chunk = line.slice(6);
|
|
if (chunk.startsWith('[error] ')) {
|
|
p.innerHTML = '<strong style="color: var(--del-color, #b33)">' + chunk.slice(8) + '</strong>';
|
|
break;
|
|
}
|
|
p.innerHTML += chunk;
|
|
}
|
|
}
|
|
messagesEl.scrollTop = messagesEl.scrollHeight;
|
|
}
|
|
|
|
if (!p.innerHTML.trim()) {
|
|
p.innerHTML = '<em>No response.</em>';
|
|
}
|
|
|
|
setStatus('');
|
|
} catch (err) {
|
|
assistantDiv.querySelector('p').innerHTML = '<em>Error.</em>';
|
|
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();
|
|
const event = new Event('submit', {cancelable: true});
|
|
form.dispatchEvent(event);
|
|
if (!event.defaultPrevented) sendMessage(event);
|
|
}
|
|
});
|
|
}
|
|
|
|
loadMessages();
|
|
})(); |