Files
Crumb-Core-v.1/app/templates/home/crew.html

202 lines
6.7 KiB
HTML

{% extends "home/base_home.html" %}
{% block title %}Crew - {{ deployment.home.hero.title }}{% endblock %}
{% block content %}
<main class="container">
<hgroup>
<h1>{{ t.crew.title }}</h1>
<p>{{ t.crew.subtitle }}</p>
</hgroup>
<div class="crew-grid">
{% for character in characters %}
<div class="character-card" onclick="showCharacter('{{ character.id }}')">
<div class="icon">{{ character.icon }}</div>
<h3>{{ character.name }}</h3>
<p>{{ character.short }}</p>
</div>
<!-- Dialog for this character -->
<dialog id="dialog-{{ character.id }}">
<article>
<header>
<button aria-label="Close" rel="prev" onclick="closeCharacter('{{ character.id }}')"></button>
<h2>{{ character.icon }} {{ character.name }}</h2>
</header>
<p>{{ character.description }}</p>
{% if character.id in ['eule', 'fox', 'bugsy'] %}
<!-- Chat Interface for Eule, FunkFox and Bugsy -->
<div class="chat-container" style="margin-top: 1rem;">
<div id="chat-history-{{ character.id }}" class="chat-history" style="max-height: 300px; overflow-y: auto; padding: 1rem; background: rgba(0,0,0,0.1); border-radius: 0.5rem; margin-bottom: 1rem;">
<p style="text-align: center; color: #888;"><small>Stelle mir eine Frage...</small></p>
</div>
<form id="chat-form-{{ character.id }}" onsubmit="sendMessage(event, '{{ character.id }}'); return false;">
<input
type="text"
id="chat-input-{{ character.id }}"
placeholder="{% if character.id == 'eule' %}Deine Frage an die Krümeleule...{% elif character.id == 'fox' %}Deine Frage an FunkFox...{% elif character.id == 'bugsy' %}Deine Frage an Bugsy...{% else %}Deine Frage...{% endif %}"
style="width: 100%; color: #fff; background: rgba(255,255,255,0.1); padding: 0.75rem; border: 1px solid rgba(255,255,255,0.2); border-radius: 0.25rem; margin-bottom: 0.5rem;"
autocomplete="off"
required
/>
<div style="text-align: right;">
<button
type="submit"
id="chat-submit-{{ character.id }}"
style="padding: 0.5rem 1.5rem; font-size: 0.875rem;"
>
Senden
</button>
</div>
</form>
<div id="chat-status-{{ character.id }}" style="margin-top: 0.5rem; text-align: center; min-height: 1.5rem;">
<!-- Status messages appear here -->
</div>
</div>
{% endif %}
<footer>
<small>{{ t.crew.tags_label }}
{% for tag in character.tags %}
<code>#{{ tag }}</code>{% if not loop.last %}, {% endif %}
{% endfor %}
</small>
</footer>
</article>
</dialog>
{% endfor %}
</div>
</main>
{% endblock %}
{% block extra_js %}
<script>
function showCharacter(id) {
const dialog = document.getElementById('dialog-' + id);
dialog.showModal();
// Focus input field for chat-enabled characters
if (id === 'eule' || id === 'fox' || id === 'bugsy') {
setTimeout(() => {
const input = document.getElementById('chat-input-' + id);
if (input) {
input.focus();
}
}, 100);
}
}
function closeCharacter(id) {
document.getElementById('dialog-' + id).close();
}
async function sendMessage(event, characterId) {
event.preventDefault();
const inputEl = document.getElementById('chat-input-' + characterId);
const historyEl = document.getElementById('chat-history-' + characterId);
const statusEl = document.getElementById('chat-status-' + characterId);
const submitBtn = document.getElementById('chat-submit-' + characterId);
const question = inputEl.value.trim();
if (!question) return;
// Disable input while processing
inputEl.disabled = true;
submitBtn.disabled = true;
const statusMessages = {
'eule': '🦉 Die Eule lauscht...',
'fox': '🦊 FunkFox droppt den Beat...',
'bugsy': '🐞 Bugsy analysiert...'
};
statusEl.innerHTML = '<small style="color: #888;">' + (statusMessages[characterId] || 'Denkt nach...') + '</small>';
// Add user message to history
const userMsg = document.createElement('div');
userMsg.style.cssText = 'margin-bottom: 0.5rem; padding: 0.5rem; background: rgba(16,185,129,0.1); border-radius: 0.25rem; text-align: right;';
userMsg.innerHTML = '<strong>Du:</strong> ' + escapeHtml(question);
historyEl.appendChild(userMsg);
// Clear welcome message if present
if (historyEl.querySelector('p')) {
const welcomeMsg = historyEl.querySelector('p');
if (welcomeMsg.textContent.includes('Stelle mir eine Frage')) {
welcomeMsg.remove();
}
}
// Scroll to bottom
historyEl.scrollTop = historyEl.scrollHeight;
// Clear input
inputEl.value = '';
try {
// Call API
const response = await fetch('/api/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
character_id: characterId,
question: question,
lang: '{{ lang }}'
})
});
if (!response.ok) {
throw new Error('API request failed: ' + response.status);
}
const data = await response.json();
// Add assistant message to history
const characterNames = {
'eule': '🦉 Krümeleule',
'fox': '🦊 FunkFox',
'bugsy': '🐞 Bugsy'
};
const assistantMsg = document.createElement('div');
assistantMsg.style.cssText = 'margin-bottom: 0.5rem; padding: 0.5rem; background: rgba(255,255,255,0.05); border-radius: 0.25rem;';
assistantMsg.innerHTML = '<strong>' + (characterNames[characterId] || characterId) + ':</strong><br>' + escapeHtml(data.answer);
// Add sources if available
if (data.sources && data.sources.length > 0) {
const sourcesText = document.createElement('small');
sourcesText.style.cssText = 'color: #888; display: block; margin-top: 0.5rem;';
sourcesText.innerHTML = '📚 Quellen: ' + data.sources.length + ' Dokumente';
assistantMsg.appendChild(sourcesText);
}
historyEl.appendChild(assistantMsg);
// Scroll to bottom
historyEl.scrollTop = historyEl.scrollHeight;
// Clear status
statusEl.innerHTML = '';
} catch (error) {
console.error('Chat error:', error);
statusEl.innerHTML = '<small style="color: #f87171;">❌ Fehler: ' + escapeHtml(error.message) + '</small>';
} finally {
// Re-enable input
inputEl.disabled = false;
submitBtn.disabled = false;
inputEl.focus();
}
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
</script>
{% endblock %}