Files
Crumb-Core-v.1/app/templates/pages/chat.html

291 lines
8.5 KiB
HTML

{% extends "base.html" %}
{% block content %}
<article>
<header>
<h1>🌲 Crumbforest Chat</h1>
<p>Wähle deinen Gesprächspartner und stelle deine Fragen!</p>
</header>
<!-- Character Selection -->
<section id="character-selection" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1rem; margin-bottom: 2rem;">
<article class="character-card" data-character="eule" style="cursor: pointer; border: 2px solid transparent; transition: all 0.3s;">
<header style="text-align: center;">
<h2 style="font-size: 3rem; margin: 0;">🦉</h2>
<h3 style="margin: 0.5rem 0;">Krümeleule</h3>
</header>
<p style="text-align: center; color: var(--pico-muted-color);">
Weise und geduldig. Erklärt die Welt mit Ruhe und Respekt.
</p>
</article>
<article class="character-card" data-character="fox" style="cursor: pointer; border: 2px solid transparent; transition: all 0.3s;">
<header style="text-align: center;">
<h2 style="font-size: 3rem; margin: 0;">🦊</h2>
<h3 style="margin: 0.5rem 0;">FunkFox</h3>
</header>
<p style="text-align: center; color: var(--pico-muted-color);">
Der rappende Bit! Erklärt Tech mit Beats und Reimen.
</p>
</article>
<article class="character-card" data-character="bugsy" style="cursor: pointer; border: 2px solid transparent; transition: all 0.3s;">
<header style="text-align: center;">
<h2 style="font-size: 3rem; margin: 0;">🐞</h2>
<h3 style="margin: 0.5rem 0;">Bugsy</h3>
</header>
<p style="text-align: center; color: var(--pico-muted-color);">
Fehler ohne Scham. Macht Debugging zu einer Lernchance.
</p>
</article>
</section>
<!-- Chat Interface -->
<section id="chat-interface" style="display: none;">
<header style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
<div>
<h2 id="current-character-name" style="margin: 0;">Character</h2>
<p id="current-character-emoji" style="font-size: 2rem; margin: 0;">🦉</p>
</div>
<button id="change-character-btn" class="outline secondary">Character wechseln</button>
</header>
<!-- Chat Messages -->
<div id="chat-messages" style="min-height: 300px; max-height: 500px; overflow-y: auto; padding: 1rem; background: var(--pico-card-background-color); border-radius: var(--pico-border-radius); margin-bottom: 1rem;">
<p class="text-muted text-center">Stelle deine erste Frage! 🌱</p>
</div>
<!-- Input Form -->
<form id="chat-form">
<fieldset role="group">
<input
type="text"
id="question-input"
placeholder="Deine Frage..."
required
autocomplete="off"
/>
<button type="submit" id="send-btn">Senden</button>
</fieldset>
</form>
<!-- Loading Indicator -->
<div id="loading" style="display: none; text-align: center; margin-top: 1rem;">
<progress></progress>
<p class="text-muted">Dein Character denkt nach...</p>
</div>
</section>
</article>
<style>
.character-card:hover {
border-color: var(--brand-color) !important;
transform: scale(1.02);
}
.character-card.selected {
border-color: var(--brand-color) !important;
background: rgba(16, 185, 129, 0.05);
}
.message {
margin-bottom: 1.5rem;
padding: 1rem;
border-radius: var(--pico-border-radius);
animation: fadeIn 0.3s ease-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(0.5rem);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.message-user {
background: var(--pico-primary-background);
border-left: 4px solid var(--pico-primary);
}
.message-character {
background: var(--pico-card-background-color);
border-left: 4px solid var(--brand-color);
}
.message-header {
font-weight: 600;
margin-bottom: 0.5rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.message-content {
white-space: pre-wrap;
line-height: 1.6;
}
.sources {
margin-top: 0.75rem;
padding-top: 0.75rem;
border-top: 1px solid var(--pico-muted-border-color);
font-size: 0.875rem;
color: var(--pico-muted-color);
}
.sources ul {
margin: 0.5rem 0 0 0;
padding-left: 1.5rem;
}
.sources li {
margin-bottom: 0.25rem;
}
</style>
<script>
const API_URL = '/api/chat';
let currentCharacter = null;
// Character Selection
document.querySelectorAll('.character-card').forEach(card => {
card.addEventListener('click', () => {
currentCharacter = card.dataset.character;
startChat();
});
});
// Change Character Button
document.getElementById('change-character-btn').addEventListener('click', () => {
document.getElementById('chat-interface').style.display = 'none';
document.getElementById('character-selection').style.display = 'grid';
document.getElementById('chat-messages').innerHTML = '<p class="text-muted text-center">Stelle deine erste Frage! 🌱</p>';
currentCharacter = null;
});
function startChat() {
// Update UI
document.getElementById('character-selection').style.display = 'none';
document.getElementById('chat-interface').style.display = 'block';
// Update character info
const characterInfo = {
eule: { name: 'Krümeleule', emoji: '🦉' },
fox: { name: 'FunkFox', emoji: '🦊' },
bugsy: { name: 'Bugsy', emoji: '🐞' }
};
const info = characterInfo[currentCharacter];
document.getElementById('current-character-name').textContent = info.name;
document.getElementById('current-character-emoji').textContent = info.emoji;
// Focus input
document.getElementById('question-input').focus();
}
// Chat Form Submission
document.getElementById('chat-form').addEventListener('submit', async (e) => {
e.preventDefault();
const input = document.getElementById('question-input');
const question = input.value.trim();
if (!question) return;
// Add user message to chat
addMessage('user', 'Du', question);
// Clear input
input.value = '';
// Show loading
document.getElementById('loading').style.display = 'block';
document.getElementById('send-btn').disabled = true;
try {
// Send request to API
const response = await fetch(API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
character_id: currentCharacter,
question: question,
lang: '{{ lang }}'
})
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const data = await response.json();
// Add character response
const characterInfo = {
eule: { name: 'Krümeleule', emoji: '🦉' },
fox: { name: 'FunkFox', emoji: '🦊' },
bugsy: { name: 'Bugsy', emoji: '🐞' }
};
const info = characterInfo[currentCharacter];
addMessage('character', `${info.emoji} ${info.name}`, data.answer, data.sources);
} catch (error) {
console.error('Chat error:', error);
addMessage('error', '❌ Fehler', 'Entschuldigung, es gab einen Fehler. Bitte versuche es erneut.');
} finally {
// Hide loading
document.getElementById('loading').style.display = 'none';
document.getElementById('send-btn').disabled = false;
input.focus();
}
});
function addMessage(type, sender, content, sources = null) {
const messagesDiv = document.getElementById('chat-messages');
// Remove placeholder if exists
if (messagesDiv.querySelector('.text-muted')) {
messagesDiv.innerHTML = '';
}
const messageDiv = document.createElement('div');
messageDiv.className = `message message-${type}`;
let html = `
<div class="message-header">${sender}</div>
<div class="message-content">${escapeHtml(content)}</div>
`;
if (sources && sources.length > 0) {
html += `
<div class="sources">
📚 Quellen (${sources.length}):
<ul>
${sources.map(s => `<li>${escapeHtml(s.file_path)} (${s.score.toFixed(3)})</li>`).join('')}
</ul>
</div>
`;
}
messageDiv.innerHTML = html;
messagesDiv.appendChild(messageDiv);
// Scroll to bottom
messagesDiv.scrollTop = messagesDiv.scrollHeight;
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
</script>
{% endblock %}