Files
Assistent/templates/chat.html
2025-06-18 14:31:22 +02:00

360 lines
13 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>AI Vertriebsassistent Simon Giehl</title>
<style>
body {
background: #23272f;
font-family: 'Segoe UI', Arial, sans-serif;
margin: 0;
padding: 0;
color: #e3e3e3;
}
#container {
max-width: 700px;
margin: 40px auto 0 auto;
background: #181a20;
border-radius: 12px;
box-shadow: 0 4px 24px rgba(0,0,0,0.18);
padding: 0 0 16px 0;
min-height: 80vh;
display: flex;
flex-direction: column;
}
#header {
padding: 24px 24px 0 24px;
text-align: center;
}
#header h1 {
margin: 0 0 8px 0;
font-size: 2em;
color: #00aaff;
letter-spacing: 1px;
}
#header p {
margin: 0;
color: #b0b6c3;
font-size: 1.1em;
}
#chat {
flex: 1;
padding: 32px 24px 16px 24px;
overflow-y: auto;
}
.msg {
display: flex;
margin-bottom: 18px;
}
.user {
justify-content: flex-end;
}
.ai {
justify-content: flex-start;
}
.bubble {
max-width: 75%;
padding: 14px 18px;
border-radius: 16px;
font-size: 1.05em;
line-height: 1.5;
box-shadow: 0 2px 8px rgba(0,0,0,0.10);
position: relative;
word-break: break-word;
}
.user .bubble {
background: linear-gradient(135deg, #0078fe 60%, #005bb5 100%);
color: #fff;
border-bottom-right-radius: 4px;
}
.ai .bubble {
background: #23272f;
color: #e3e3e3;
border-bottom-left-radius: 4px;
border: 1px solid #2c313a;
}
#input-area {
display: flex;
padding: 0 24px;
margin-top: 8px;
}
#input {
flex: 1;
padding: 12px;
border-radius: 8px;
border: 1px solid #353b48;
font-size: 1em;
outline: none;
margin-right: 8px;
background: #23272f;
color: #e3e3e3;
}
#send-btn {
background: #00aaff;
color: #fff;
border: none;
border-radius: 8px;
padding: 0 24px;
font-size: 1em;
cursor: pointer;
transition: background 0.2s;
}
#send-btn:hover {
background: #0078fe;
}
.think-toggle {
background: none;
border: none;
color: #00aaff;
cursor: pointer;
font-size: 1em;
margin-top: 10px;
margin-bottom: 2px;
padding: 0;
display: flex;
align-items: center;
gap: 6px;
transition: color 0.2s;
}
.think-toggle:hover {
color: #fff;
}
.think-section {
display: none;
background: linear-gradient(90deg, #23272f 80%, #00aaff22 100%);
color: #b0e0ff;
border-left: 4px solid #00aaff;
border-radius: 8px;
margin-top: 8px;
margin-bottom: 2px;
padding: 12px 16px;
font-size: 1em;
white-space: pre-line;
box-shadow: 0 2px 8px #00aaff22;
animation: fadeIn 0.3s;
position: relative;
}
.think-section::before {
content: "💡 Gedanken der KI";
display: block;
font-size: 0.95em;
color: #7fd7ff;
margin-bottom: 6px;
font-weight: bold;
letter-spacing: 0.5px;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-8px);}
to { opacity: 1; transform: translateY(0);}
}
#footer {
text-align: center;
color: #6c7380;
font-size: 0.95em;
margin-top: 18px;
margin-bottom: 6px;
letter-spacing: 0.5px;
}
#clear-btn {
background: #353b48;
color: #fff;
border: none;
border-radius: 8px;
padding: 6px 18px;
font-size: 0.98em;
cursor: pointer;
margin-left: 10px;
margin-bottom: 8px;
transition: background 0.2s;
}
#clear-btn:hover {
background: #ff4d4d;
color: #fff;
}
@media (max-width: 600px) {
#container { max-width: 100%; border-radius: 0; }
#chat { padding: 16px 6px 8px 6px; }
#input-area { padding: 0 6px; }
#header { padding: 16px 6px 0 6px; }
}
</style>
</head>
<body>
<div id="container">
<div id="header">
<h1>AI Vertriebsassistent</h1>
<p>Erstellt von Simon Giehl &ndash; Ihr smarter KI-Partner für Kunden- und Vertriebsinfos</p>
</div>
<div style="display:flex; justify-content:flex-end; padding: 0 24px;">
<button id="clear-btn" onclick="clearChat()">Kontext löschen</button>
</div>
<div id="chat"></div>
<div id="input-area">
<input id="input" type="text" placeholder="Nachricht eingeben..." autocomplete="off">
<button id="send-btn" onclick="send()">Senden</button>
</div>
<div id="footer">
&copy; 2025 Simon Giehl &ndash; AI Vertriebsassistent. Alle Rechte vorbehalten.
</div>
</div>
<script>
// Chatverlauf im Local Storage speichern und laden
function saveChatHistory() {
const chat = document.getElementById('chat');
const history = [];
chat.querySelectorAll('.msg').forEach(msgDiv => {
const role = msgDiv.classList.contains('user') ? 'user' : 'ai';
const bubble = msgDiv.querySelector('.bubble');
let text = '';
let thoughts = '';
if (bubble) {
// Extrahiere Antworttext
const spans = bubble.querySelectorAll('span');
if (spans.length > 0) text = spans[spans.length-1].innerText;
// Extrahiere Gedanken, falls vorhanden
const thinkDiv = bubble.querySelector('.think-section');
if (thinkDiv) thoughts = thinkDiv.innerText;
}
history.push({role, text, thoughts});
});
localStorage.setItem('chatHistory', JSON.stringify(history));
}
function loadChatHistory() {
const history = JSON.parse(localStorage.getItem('chatHistory') || '[]');
history.forEach(msg => addMessage(msg.role, msg.text, msg.thoughts));
}
function clearChat() {
document.getElementById('chat').innerHTML = '';
localStorage.removeItem('chatHistory');
}
function addMessage(role, text, thoughts) {
const chat = document.getElementById('chat');
const msgDiv = document.createElement('div');
msgDiv.className = 'msg ' + role;
let bubble = document.createElement('div');
bubble.className = 'bubble';
if (role === 'ai' && thoughts) {
// Button zum Ein-/Ausblenden der Gedanken
const toggleBtn = document.createElement('button');
toggleBtn.className = 'think-toggle';
toggleBtn.innerHTML = '<span style="font-size:1.2em;">💡</span> Gedanken anzeigen';
toggleBtn.onclick = function() {
thinkDiv.style.display = thinkDiv.style.display === 'block' ? 'none' : 'block';
toggleBtn.innerHTML = thinkDiv.style.display === 'block'
? '<span style="font-size:1.2em;">💡</span> Gedanken ausblenden'
: '<span style="font-size:1.2em;">💡</span> Gedanken anzeigen';
};
bubble.appendChild(toggleBtn);
// Gedankenbereich
const thinkDiv = document.createElement('div');
thinkDiv.className = 'think-section';
thinkDiv.textContent = thoughts; // <--- HIER textContent statt innerText
bubble.appendChild(thinkDiv);
}
// Antworttext
const textSpan = document.createElement('span');
textSpan.textContent = text; // <--- HIER textContent statt innerText
bubble.appendChild(textSpan);
msgDiv.appendChild(bubble);
chat.appendChild(msgDiv);
chat.scrollTop = chat.scrollHeight;
saveChatHistory();
}
function send() {
let input = document.getElementById('input');
let msg = input.value.trim();
if (!msg) return;
addMessage('user', msg);
input.value = '';
input.focus();
// AI-Stream-Message vorbereiten
const aiMsgDiv = document.createElement('div');
aiMsgDiv.className = 'msg ai';
const aiBubble = document.createElement('div');
aiBubble.className = 'bubble';
const aiText = document.createElement('span');
aiText.id = 'ai-stream';
aiBubble.appendChild(aiText);
aiMsgDiv.appendChild(aiBubble);
document.getElementById('chat').appendChild(aiMsgDiv);
document.getElementById('chat').scrollTop = document.getElementById('chat').scrollHeight;
fetch('/ask_stream', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({message: msg})
})
.then(response => {
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
function read() {
reader.read().then(({done, value}) => {
if (done) {
// Nach dem Stream: <think>-Bereich extrahieren und anzeigen
const {answer, thoughts} = splitThoughtsAndAnswer(buffer);
aiText.textContent = answer; // <--- HIER textContent statt innerText
if (thoughts) {
// Gedanken-Button und Bereich einfügen
const toggleBtn = document.createElement('button');
toggleBtn.className = 'think-toggle';
toggleBtn.innerHTML = '<span style="font-size:1.2em;">💡</span> Gedanken anzeigen';
const thinkDiv = document.createElement('div');
thinkDiv.className = 'think-section';
thinkDiv.textContent = thoughts; // <--- HIER textContent statt innerText
toggleBtn.onclick = function() {
thinkDiv.style.display = thinkDiv.style.display === 'block' ? 'none' : 'block';
toggleBtn.innerHTML = thinkDiv.style.display === 'block'
? '<span style="font-size:1.2em;">💡</span> Gedanken ausblenden'
: '<span style="font-size:1.2em;">💡</span> Gedanken anzeigen';
};
aiBubble.insertBefore(toggleBtn, aiText);
aiBubble.insertBefore(thinkDiv, aiText);
}
saveChatHistory();
return;
}
buffer += decoder.decode(value, {stream: true});
aiText.textContent = buffer; // <--- HIER textContent statt innerText
document.getElementById('chat').scrollTop = document.getElementById('chat').scrollHeight;
read();
});
}
read();
});
}
// Extrahiere <think>...</think> Bereich aus dem Text
function splitThoughtsAndAnswer(text) {
const thinkMatch = text.match(/<think>([\s\S]*?)<\/think>/i);
let thoughts = '';
let answer = text;
if (thinkMatch) {
thoughts = thinkMatch[1].trim();
answer = text.replace(thinkMatch[0], '').trim();
}
return {answer, thoughts};
}
// Enter-Taste zum Senden
document.getElementById('input').addEventListener('keydown', function(e) {
if (e.key === 'Enter') send();
});
// Beim Laden: Chatverlauf wiederherstellen
window.onload = loadChatHistory;
</script>
</body>
</html>