98 lines
3.2 KiB
JavaScript
98 lines
3.2 KiB
JavaScript
export default function setupAIChat(el) {
|
|
const model = el.dataset.model || 'llama3.2';
|
|
const ollamaURL = el.dataset.ollamaUrl || 'http://localhost:11434';
|
|
|
|
const messagesEl = el.querySelector('.ai-chat-messages');
|
|
const inputEl = el.querySelector('.ai-chat-input');
|
|
const sendBtn = el.querySelector('.ai-chat-send');
|
|
|
|
const history = [];
|
|
let busy = false;
|
|
|
|
function appendMessage(role, text) {
|
|
const div = document.createElement('div');
|
|
div.className = `ai-chat-msg ai-chat-msg-${role}`;
|
|
div.textContent = text;
|
|
messagesEl.appendChild(div);
|
|
messagesEl.scrollTop = messagesEl.scrollHeight;
|
|
return div;
|
|
}
|
|
|
|
async function send() {
|
|
const text = inputEl.value.trim();
|
|
if (!text || busy) return;
|
|
|
|
busy = true;
|
|
sendBtn.disabled = true;
|
|
inputEl.value = '';
|
|
inputEl.style.height = 'auto';
|
|
|
|
history.push({ role: 'user', content: text });
|
|
appendMessage('user', text);
|
|
|
|
const assistantEl = appendMessage('assistant', '');
|
|
assistantEl.classList.add('ai-chat-msg-loading');
|
|
|
|
let reply = '';
|
|
|
|
try {
|
|
const res = await fetch('/api/ai-chat', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ ollama_url: ollamaURL, model, messages: history }),
|
|
});
|
|
|
|
if (!res.ok) {
|
|
assistantEl.textContent = `Error: ${res.statusText}`;
|
|
assistantEl.classList.remove('ai-chat-msg-loading');
|
|
history.pop();
|
|
return;
|
|
}
|
|
|
|
const reader = res.body.getReader();
|
|
const decoder = new TextDecoder();
|
|
|
|
while (true) {
|
|
const { done, value } = await reader.read();
|
|
if (done) break;
|
|
|
|
const lines = decoder.decode(value, { stream: true }).split('\n');
|
|
for (const line of lines) {
|
|
if (!line.trim()) continue;
|
|
try {
|
|
const chunk = JSON.parse(line);
|
|
if (chunk.message && chunk.message.content) {
|
|
reply += chunk.message.content;
|
|
assistantEl.textContent = reply;
|
|
messagesEl.scrollTop = messagesEl.scrollHeight;
|
|
}
|
|
} catch (_) {}
|
|
}
|
|
}
|
|
} catch (e) {
|
|
assistantEl.textContent = 'Failed to reach Ollama.';
|
|
} finally {
|
|
assistantEl.classList.remove('ai-chat-msg-loading');
|
|
if (reply) history.push({ role: 'assistant', content: reply });
|
|
busy = false;
|
|
sendBtn.disabled = false;
|
|
inputEl.focus();
|
|
}
|
|
}
|
|
|
|
sendBtn.addEventListener('click', send);
|
|
|
|
inputEl.addEventListener('keydown', e => {
|
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
e.preventDefault();
|
|
send();
|
|
}
|
|
});
|
|
|
|
// Auto-grow textarea
|
|
inputEl.addEventListener('input', () => {
|
|
inputEl.style.height = 'auto';
|
|
inputEl.style.height = Math.min(inputEl.scrollHeight, 120) + 'px';
|
|
});
|
|
}
|