78 lines
2.4 KiB
HTML
78 lines
2.4 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}AI Coach{% endblock %}
|
|
{% block content %}
|
|
<h1>AI Coach</h1>
|
|
|
|
<div class="chat-messages" id="chat-messages">
|
|
{% for m in messages %}
|
|
<div class="chat-message {{ m.role }}">
|
|
<small>{{ m.role }} · {{ m.created_at[:10] if m.created_at else '' }}</small>
|
|
<p>{{ m.content }}</p>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<form class="chat-input" id="chat-form" onsubmit="sendMessage(event)">
|
|
<input type="text" id="chat-input" placeholder="Ask your coach..." required autocomplete="off">
|
|
<button type="submit">Send</button>
|
|
</form>
|
|
|
|
<script>
|
|
function sendMessage(event) {
|
|
event.preventDefault();
|
|
const input = document.getElementById('chat-input');
|
|
const messages = document.getElementById('chat-messages');
|
|
const text = input.value;
|
|
if (!text.trim()) return;
|
|
|
|
const userDiv = document.createElement('div');
|
|
userDiv.className = 'chat-message user';
|
|
userDiv.innerHTML = `<small>user</small><p>${text}</p>`;
|
|
messages.appendChild(userDiv);
|
|
|
|
const assistantDiv = document.createElement('div');
|
|
assistantDiv.className = 'chat-message assistant';
|
|
assistantDiv.innerHTML = '<small>assistant</small><p><em>Thinking...</em></p>';
|
|
messages.appendChild(assistantDiv);
|
|
messages.scrollTop = messages.scrollHeight;
|
|
|
|
input.disabled = true;
|
|
|
|
const formData = new FormData();
|
|
formData.append('message', text);
|
|
|
|
fetch('/chat', {
|
|
method: 'POST',
|
|
body: formData,
|
|
}).then(async response => {
|
|
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);
|
|
}
|
|
}
|
|
messages.scrollTop = messages.scrollHeight;
|
|
}
|
|
|
|
input.disabled = false;
|
|
input.value = '';
|
|
input.focus();
|
|
}).catch(() => {
|
|
assistantDiv.querySelector('p').innerHTML = 'Error connecting to coach.';
|
|
input.disabled = false;
|
|
});
|
|
}
|
|
</script>
|
|
{% endblock %}
|