278 lines
9.5 KiB
JavaScript
278 lines
9.5 KiB
JavaScript
|
|
const chatMessages = document.getElementById('chat-messages');
|
|
const userInput = document.getElementById('user-input');
|
|
|
|
let messageHistory = [];
|
|
let isFirstMessage = true;
|
|
const MAX_CHAR_LIMIT = 24000; // Dein Volumen-Limit in Zeichen (ca. 2000-3000 Token)
|
|
|
|
// Hilfsfunktion: Berechnet das aktuelle Volumen der History
|
|
function getHistoryVolume() {
|
|
return messageHistory.reduce((sum, msg) => sum + msg.content.length, 0);
|
|
}
|
|
|
|
function safeScrollToBottom() {
|
|
if (isEditingSystemPrompt) return; // Wenn wir editieren, tun wir GAR NICHTS
|
|
|
|
const chatMessages = document.getElementById('chat-messages');
|
|
if (chatMessages) {
|
|
chatMessages.scrollTo({
|
|
top: chatMessages.scrollHeight,
|
|
behavior: 'smooth'
|
|
});
|
|
}
|
|
}
|
|
|
|
async function sendMessage() {
|
|
const userInput = document.getElementById('user-input');
|
|
const sendBtn = document.querySelector('.send-btn');
|
|
const systemInput = document.getElementById('system-prompt-input');
|
|
const systemContainer = document.getElementById('system-prompt-container');
|
|
const chatMessages = document.getElementById('chat-messages');
|
|
|
|
const text = userInput.value.trim();
|
|
if (!text) return;
|
|
|
|
// --- 1. VOLUMEN-CHECK VOR DEM SENDEN ---
|
|
// Wir prüfen das aktuelle Volumen + die neue Nachricht
|
|
const currentVolume = getHistoryVolume();
|
|
if (currentVolume + text.length > MAX_CHAR_LIMIT) {
|
|
appendMessage('system', "⚠️ Kontext-Limit erreicht. Das Volumen der Konversation ist zu groß für die Demo.");
|
|
userInput.disabled = true;
|
|
sendBtn.disabled = true;
|
|
return;
|
|
}
|
|
|
|
// --- 1. UI SPERREN (Warte-Modus) ---
|
|
userInput.disabled = true;
|
|
sendBtn.disabled = true;
|
|
const originalBtnText = sendBtn.innerText;
|
|
sendBtn.innerText = '...';
|
|
|
|
// --- 2. SYSTEM-PROMPT LOGIK (Nur beim ersten Mal) ---
|
|
if (isFirstMessage) {
|
|
const systemText = systemInput.value.trim();
|
|
|
|
// 1. In die API-History pushen
|
|
messageHistory.push({ "role": "system", "content": systemText });
|
|
|
|
// 2. Optisch in den Chat einfügen (als System-Nachricht)
|
|
appendMessage('system', "⚙️ System-Prompt\n" + systemText);
|
|
|
|
// Editor ausblenden
|
|
if (systemContainer) systemContainer.classList.add('locked');
|
|
isFirstMessage = false;
|
|
}
|
|
|
|
// --- 3. USER-NACHRICHT ---
|
|
messageHistory.push({ "role": "user", "content": text });
|
|
appendMessage('user', text);
|
|
userInput.value = '';
|
|
|
|
// --- 4. LADE-ANIMATION ANZEIGEN ---
|
|
const typingDiv = document.createElement('div');
|
|
typingDiv.className = 'message typing-indicator';
|
|
typingDiv.id = 'temp-typing';
|
|
typingDiv.innerHTML = '<span></span><span></span><span></span>';
|
|
chatMessages.appendChild(typingDiv);
|
|
safeScrollToBottom();
|
|
|
|
try {
|
|
const response = await fetch('/api/chat/completions', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
"model": "trinity-test",
|
|
"messages": messageHistory,
|
|
"stream": false
|
|
})
|
|
});
|
|
|
|
if (!response.ok) throw new Error('Server antwortet nicht');
|
|
|
|
const data = await response.json();
|
|
|
|
// Animation entfernen
|
|
const temp = document.getElementById('temp-typing');
|
|
if (temp) temp.remove();
|
|
|
|
// Antwort verarbeiten
|
|
const aiMessage = data.choices[0].message;
|
|
messageHistory.push(aiMessage);
|
|
appendMessage('ai', aiMessage.content);
|
|
|
|
// --- 3. STATUS-UPDATE FÜR DEN NUTZER ---
|
|
const newTotalVolume = getHistoryVolume();
|
|
const fillPercentage = Math.round((newTotalVolume / MAX_CHAR_LIMIT) * 100);
|
|
console.log(`Kontext-Auslastung: ${fillPercentage}%`);
|
|
|
|
if (newTotalVolume > MAX_CHAR_LIMIT * 0.9) {
|
|
appendMessage('system', `Achtung: Der Speicher ist zu ${fillPercentage}% voll.`);
|
|
}
|
|
|
|
} catch (err) {
|
|
const temp = document.getElementById('temp-typing');
|
|
if (temp) temp.remove();
|
|
appendMessage('ai', 'Fehler: ' + err.message);
|
|
console.error(err);
|
|
} finally {
|
|
// UI nur freigeben, wenn noch Platz ist
|
|
if (getHistoryVolume() < MAX_CHAR_LIMIT) {
|
|
userInput.disabled = false;
|
|
sendBtn.disabled = false;
|
|
sendBtn.innerText = originalBtnText;
|
|
userInput.focus();
|
|
} else {
|
|
userInput.placeholder = "Kontext voll.";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Erweitere deine appendMessage Funktion um den 'system' Typ
|
|
function appendMessage(role, text) {
|
|
const chatMessages = document.getElementById('chat-messages');
|
|
const msgDiv = document.createElement('div');
|
|
|
|
// role kann 'user', 'ai' oder 'system' sein
|
|
msgDiv.className = `message ${role}-message`;
|
|
msgDiv.innerText = text;
|
|
|
|
chatMessages.appendChild(msgDiv);
|
|
|
|
safeScrollToBottom();
|
|
}
|
|
|
|
// ======================================================
|
|
|
|
// Navigations-Logik für Chat
|
|
function setView(viewMode) {
|
|
const main = document.getElementById('chatMain'); // Sicherstellen, dass <main id="chatMain"> existiert!
|
|
const nav = document.getElementById('navLinks'); // Konsistent mit common.js
|
|
|
|
if (!main) return;
|
|
|
|
main.classList.remove('view-chat-only', 'view-article-only', 'view-split');
|
|
main.classList.add('view-' + viewMode);
|
|
|
|
if (nav) nav.classList.remove('mobile-open');
|
|
}
|
|
|
|
// Auch die toggle-Funktion für den Chat-Hamburger:
|
|
function toggleViewMenu() {
|
|
const nav = document.getElementById('navLinks');
|
|
if (nav) nav.classList.toggle('mobile-open');
|
|
}
|
|
|
|
|
|
// Initialisierung
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
// Erzwingt Split-View beim Start
|
|
//setView('split');
|
|
// ... dein bestehender Code ...
|
|
const savedTheme = localStorage.getItem('theme') || 'auto';
|
|
applyTheme(savedTheme);
|
|
|
|
const navLinksContainer = document.getElementById('navLinks');
|
|
const hamburger = document.querySelector('.hamburger');
|
|
|
|
if (navLinksContainer && hamburger) {
|
|
const navLinks = navLinksContainer.querySelectorAll('a');
|
|
|
|
navLinks.forEach(link => {
|
|
link.addEventListener('click', () => {
|
|
if (navLinksContainer.classList.contains('mobile-open')) {
|
|
navLinksContainer.classList.remove('mobile-open');
|
|
hamburger.classList.remove('active');
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
const resizer = document.getElementById('dragMe');
|
|
const leftSide = document.getElementById('resizableArticle');
|
|
//const leftSide = document.querySelector('.chat-article');
|
|
const container = document.getElementById('chatMain');
|
|
|
|
if (resizer && leftSide && container) {
|
|
resizer.addEventListener('pointerdown', function(e) {
|
|
document.body.classList.add('resizing');
|
|
e.preventDefault();
|
|
|
|
const startX = e.clientX;
|
|
const startWidth = leftSide.getBoundingClientRect().width;
|
|
const containerWidth = container.getBoundingClientRect().width;
|
|
|
|
//document.body.style.cursor = 'col-resize';
|
|
//leftSide.style.userSelect = 'none';
|
|
document.body.classList.add('resizing');
|
|
|
|
function onPointerMove(e) {
|
|
const deltaX = e.clientX - startX;
|
|
let newWidthPercent = ((startWidth + deltaX) / containerWidth) * 100;
|
|
|
|
newWidthPercent = Math.max(15, Math.min(75, newWidthPercent));
|
|
|
|
leftSide.style.flexBasis = newWidthPercent + '%';
|
|
}
|
|
|
|
function onPointerUp() {
|
|
document.removeEventListener('pointermove', onPointerMove);
|
|
document.removeEventListener('pointerup', onPointerUp);
|
|
//document.body.style.removeProperty('cursor');
|
|
document.body.classList.remove('resizing');
|
|
leftSide.style.removeProperty('user-select');
|
|
}
|
|
|
|
document.addEventListener('pointermove', onPointerMove);
|
|
document.addEventListener('pointerup', onPointerUp);
|
|
});
|
|
}
|
|
|
|
const systemInput = document.getElementById('system-prompt-input');
|
|
|
|
if (systemInput) {
|
|
// Funktion zur Höhenanpassung
|
|
const autoGrow = (el) => {
|
|
el.style.height = "auto";
|
|
el.style.height = el.scrollHeight + "px";
|
|
};
|
|
|
|
// Beim Tippen mitwachsen
|
|
systemInput.addEventListener('input', () => autoGrow(systemInput));
|
|
|
|
// Initial beim Laden anpassen (falls schon Text drin steht)
|
|
autoGrow(systemInput);
|
|
}
|
|
});
|
|
|
|
|
|
let isEditingSystemPrompt = false;
|
|
const systemInput = document.getElementById('system-prompt-input');
|
|
const systemContainer = document.getElementById('system-prompt-container');
|
|
|
|
|
|
|
|
if (systemInput) {
|
|
systemInput.addEventListener('focus', () => {
|
|
if (window.innerWidth < 600) {
|
|
isEditingSystemPrompt = true;
|
|
systemContainer.classList.add('mobile-edit-mode');
|
|
document.body.style.overflow = 'hidden';
|
|
}
|
|
});
|
|
|
|
const exitEditMode = () => {
|
|
isEditingSystemPrompt = false;
|
|
systemContainer.classList.remove('mobile-edit-mode');
|
|
document.body.style.overflow = '';
|
|
|
|
// Einmaliges Auto-Grow nach dem Schließen
|
|
systemInput.style.height = "auto";
|
|
systemInput.style.height = systemInput.scrollHeight + "px";
|
|
};
|
|
|
|
systemInput.addEventListener('blur', () => {
|
|
// Delay, um dem Klick auf den Button Vorrang zu geben
|
|
setTimeout(exitEditMode, 200);
|
|
});
|
|
} |