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 = ''; 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
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); }); }