final edit sys-promt and markdown image
This commit is contained in:
278
static/chat.js
Normal file
278
static/chat.js
Normal file
@@ -0,0 +1,278 @@
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user