291 lines
8.5 KiB
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 %}
|