final edit sys-promt and markdown image

This commit is contained in:
2026-03-04 19:29:58 +00:00
parent fe3a25fa3a
commit 0923de4259
15 changed files with 1060 additions and 267 deletions

459
static/chat.css Normal file
View File

@@ -0,0 +1,459 @@
/* --- Basis Layout für die Chat Seite --- */
.chat-main {
display: flex;
height: calc(100vh - 42px);
margin-top: 45px;
overflow: hidden;
}
/* Kleiner visueller Indikator (zwei Punkte oder Linien) */
.resizer::after {
content: "⋮";
color: var(--text-muted);
font-weight: bold;
}
/* --- Artikel Sidebar --- */
.chat-article {
/* width: 40%; Breite des Artikels
max-width: 800px;*/
height: 100%;
background: var(--bg-section-alt);
color: var(--text-main);
overflow-y: auto; /* Erlaubt vertikales Scrollen */
overflow-x: hidden; /* Verhindert horizontales Wackeln */
/* Positionierung der Scrollbar erzwingen */
direction: rtl; /* Left-to-Right (Standard) setzt Scrollbar nach rechts */
/* Optional: Ein schöneres Design für die Scrollbar (Webkit) */
scrollbar-gutter: stable; /* Verhindert das Springen des Inhalts beim Erscheinen */
}
/* --- Artikel-Formatierung --- */
.article-content {
direction: ltr; /* Left-to-Right: Textfluss wieder normal */
padding: 1rem; /* Hier kommt das eigentliche Padding für den Text hin */
/* 1. Blocksatz */
text-align: justify;
/* Verhindert große Lücken im Blocksatz durch Silbentrennung */
hyphens: auto;
line-height: 1.6; /* Angenehmer Zeilenabstand */
color: var(--text-main);
}
/* 2. Abstand zwischen Absätzen */
.article-content p {
margin-top: 0;
margin-bottom: 1.5rem; /* Etwa 1,5 Zeilen Platz nach jedem Absatz */
}
/* Letzten Absatz ohne Abstand nach unten, falls ein Footer folgt */
.article-content p:last-child {
margin-bottom: 0;
}
/* 3. Überschriften-Abstände (damit sie klar vom Text getrennt sind) */
.article-content h1,
.article-content h2,
.article-content h3 {
margin-top: 2.5rem;
margin-bottom: 1rem;
text-align: left; /* Überschriften bleiben linksbündig */
}
/* 4. Listen-Formatierung */
.article-content ul,
.article-content ol {
margin-bottom: 1.5rem;
padding-left: 2rem;
}
.article-content li {
margin-bottom: 0.5rem;
}
/* --- Chat Container --- */
.chat-container {
flex: 1; /* Nimmt den verfügbaren Platz ein */
display: flex;
flex-direction: column;
height: 100%;
background: var(--card-bg);
border-right: 1px solid var(--border-color);
}
/* --- System-Konfiguration Styling --- */
.system-config {
background-color: var(--bg-section-alt);
border: 1px dashed var(--accent);
border-radius: 12px;
padding: 1.2rem;
margin-bottom: 2rem;
width: 100%;
/*align-self: stretch;*/
box-shadow: 0 4px 12px var(--card-shadow);
overflow: visible;
}
.system-config strong {
display: block;
color: var(--accent);
margin-bottom: 0.5rem;
font-size: 0.9rem;
text-transform: uppercase;
letter-spacing: 0.05rem;
}
.system-config p {
margin-bottom: 0.8rem;
color: var(--text-muted);
}
/* Die Textarea für den Prompt */
.system-textarea {
width: 100%;
min-height: 100px;
background: var(--bg-input);
color: var(--text-main);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 12px;
font-family: inherit;
font-size: 0.95rem;
line-height: 1.5;
resize: vertical;
outline: none;
transition: border-color 0.2s;
}
.system-textarea:focus { border-color: var(--accent); }
.system-config.locked { display: none; }
/* Eingabefeld (Chat) */
#user-input {
flex: 1;
padding: 12px 16px;
border: 1px solid var(--border-input);
border-radius: 8px;
background: var(--bg-input);
color: var(--text-input);
font-size: 1rem;
transition: all 0.2s ease;
}
#user-input:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2);
}
#chat-messages {
flex: 1;
overflow-y: auto;
padding: 20px;
display: flex;
flex-direction: column;
gap: 15px;
}
.message {
max-width: 80%;
padding: 12px 16px;
border-radius: 15px;
line-height: 1.5;
font-size: 0.95rem;
}
.user-message {
align-self: flex-end;
background-color: var(--accent);
color: var(--accent-text);
border-bottom-right-radius: 5px;
}
.ai-message {
align-self: flex-start;
background-color: var(--bg-section-alt);
color: var(--text-main);
border-bottom-left-radius: 5px;
}
.chat-input-area {
padding: 20px;
background-color: var(--card-bg);
border-top: 1px solid var(--border-color);
display: flex;
gap: 10px;
align-items: center;
}
/* View-Klassen */
.view-chat-only .chat-article { display: none; }
.view-chat-only .chat-container { display: flex; width: 100%; }
/* --- View-Logik: Article Only --- */
.view-article-only .chat-container,
.view-article-only .resizer {
display: none !important;
}
.view-article-only .chat-article {
display: block !important;
width: 100% !important; /* Erzwingt volle Breite */
flex: 1 1 100% !important; /* Nimmt den gesamten Flex-Platz ein */
max-width: none !important;
border-right: none;
padding-right: 20px; /* Ein bisschen Platz zum Rand lassen */
}
.view-split .chat-article {
display: block;
flex: 0 0 40%; /* WICHTIG: Verhindert, dass das CSS die Breite erzwingt */
/* width: ; Das ist jetzt nur noch der Startwert */
max-width: 80%; /* Verhindert, dass man den Chat ganz wegdrückt */
min-width: 10%; /* Verhindert, dass der Artikel verschwindet */
}
.view-split .chat-container {
display: flex;
flex: 1 1 0; /* Nimmt sich dynamisch den Rest */
min-width: 0; /* Verhindert das "Rausplatzen" bei langem Text */
}
.resizing {
cursor: col-resize;
user-select: none;
}
.resizer {
width: 6px;
cursor: col-resize;
background-color: var(--border-color);
transition: background 0.3s;
cursor: col-resize;
z-index: 10;
display: flex;
justify-content: center;
align-items: center;
align-self: stretch;
}
.resizer:hover {
background-color: var(--accent); /* Färbt sich beim Drüberfahren ein */
}
/* Button Deaktiviert-Status */
.send-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
filter: grayscale(1);
}
/* Die "KI schreibt..." Blase */
.typing-indicator {
align-self: flex-start;
background-color: var(--bg-section-alt);
padding: 12px 16px;
border-radius: 15px;
border-bottom-left-radius: 5px;
display: flex;
gap: 4px;
align-items: center;
}
.typing-indicator span {
width: 6px;
height: 6px;
background: var(--accent);
border-radius: 50%;
display: inline-block;
animation: bounce 1.4s infinite ease-in-out both;
}
.typing-indicator span:nth-child(1) { animation-delay: -0.32s; }
.typing-indicator span:nth-child(2) { animation-delay: -0.16s; }
@keyframes bounce {
0%, 80%, 100% { transform: scale(0); }
40% { transform: scale(1.0); }
}
/* Bilder im Artikel und Chat begrenzen */
.article-content img,
.ai-message img {
max-width: 100%; /* Nie breiter als der Text */
height: auto; /* Proportional bleiben */
border-radius: 8px; /* Sieht moderner aus */
margin: 1rem 0;
box-shadow: 0 4px 10px var(--card-shadow);
}
/* Links hervorheben */
.article-content a,
.ai-message a {
color: var(--accent);
text-decoration: underline;
transition: opacity 0.2s;
}
.article-content a:hover {
opacity: 0.8;
}
/* Videos responsiv machen */
.article-content video,
.article-content iframe {
width: 100%;
aspect-ratio: 16 / 9;
border-radius: 8px;
}
/*
.nav-links { display: none;}
*/
/* AUẞERHALB des Media Queries (für Desktop) */
.nav-links {
display: flex; /* Auf Desktop immer zeigen */
gap: 2rem;
list-style: none;
}
.close-edit-btn {
display: none !important;
}
/* Mobile Hamburger & Menu */
@media (max-width: 600px) {
.hamburger {
display: flex !important; /* Hamburger nur auf Chat-Mobile zeigen */
}
.nav-links {
display: none;
position: absolute;
top: 40px; /* Abstand nach unten vom Hamburger */
left: 10px; /* Richtet das Menü am linken Rand des Hamburgers aus */
right: auto; /* Verhindert das "Ziehen" nach rechts */
background: var(--card-bg);
border: 1px solid var(--border-color);
/* padding: 1rem;
border-radius: 8px;
box-shadow: 0 10px 20px var(--card-shadow);*/
border-radius: 8px;
min-width: 180px;
box-shadow: 0 10px 25px var(--card-shadow);
z-index: 2000;
/*padding: 0.5rem 0;*/
}
.nav-links.mobile-open {
display: flex;
flex-direction: column;
/*gap: 10px;*/
}
.resizer {
display: none;
}
.chat-main {
/* WICHTIG: Nutzt die volle Höhe abzüglich Header,
aber ohne aus dem Viewport zu ragen */
height: calc(100dvh - 45px); /* dvh = dynamic viewport height */
display: flex;
flex-direction: column;
overflow: hidden;
}
.view-split {
flex-direction: column !important;
display: flex !important;
}
.view-split .chat-article {
width: 100% !important;
max-width: none !important;
min-width: 100% !important;
/* Wir geben dem Artikel eine feste, aber begrenzte Höhe */
height: 35% !important;
flex: 0 0 35% !important;
border-bottom: 2px solid var(--border-color);
}
.view-split .chat-container {
width: 100% !important;
max-width: none !important;
min-width: 100% !important;
/* Der Chat nimmt den restlichen Platz ein (65%) */
height: 65% !important;
flex: 1 1 65% !important;
display: flex;
flex-direction: column;
overflow: hidden; /* Verhindert, dass der Container wächst */
}
#system-prompt-container {
/* Verhindert, dass das Mitwachsen der Textarea den Chat-Scroll triggert */
contain: layout;
}
#chat-messages {
/* flex: 1; Drückt die Eingabezeile nach unten, aber hält sie im Bild */
display: block !important;
overflow-y: auto;
}
.message {
/* Da wir oben display: block nutzen, brauchen wir margin für die Abstände */
margin-bottom: 15px;
clear: both;
display: block;
/*width: fit-content;*/
}
/*
.user-message {
margin-left: auto;
}*/
.user-message { float: right; }
.ai-message { float: left; }
.system-message {
float: none;
width: 100%;
clear: both;
}
.chat-input-area {
/* Sicherstellen, dass die Eingabezeile immer sichtbar bleibt */
flex-shrink: 0;
padding: 10px 15px; /* Etwas kompakter auf Mobile */
background: var(--card-bg);
}
/* Diese Klasse wird per JS gesetzt, wenn du ins Feld klickst */
.mobile-edit-mode {
position: fixed !important;
top: 45px !important;
/*margin-top: 45px;*/
left: 0 !important;
max-width: none !important;
min-width: 100% !important;
width: 100vw !important;
height: 75vh;
z-index: 9999;
background: var(--bg-body); /* Deine Hintergrundfarbe */
padding: 10px;
box-sizing: border-box;
display: flex !important;
flex-direction: column;
}
.mobile-edit-mode textarea {
flex: 1; /* Nutzt den Platz über der Tastatur */
font-size: 16px; /* Wichtig gegen Auto-Zoom */
}
}

278
static/chat.js Normal file
View 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);
});
}

View File

@@ -4,7 +4,7 @@
:root {
/* --- Hell-Modus --- */
--bg-body: #ffffff;
--bg-section-alt: #f8f9fa;
--bg-section-alt: #ddddec;
--text-main: #333333;
--text-muted: #666666;
--header-bg: rgba(255, 255, 255, 0.95);
@@ -15,7 +15,7 @@
--border-color: #eeeeee;
/* Hero & Akzente */
--accent: #667eea;
--accent: #6072c5;
--accent-text: #ffffff;
--accent-dark: #764ba2;
--hero-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
@@ -42,7 +42,7 @@
--border-color: #2d334a;
/* Hero & Akzente (Im Darkmode etwas entspannter) */
--accent: #39348c;
--accent: #6e6abc;
--accent-text: #ffffff;
--accent-dark: #3d4593;
--hero-gradient: linear-gradient(135deg, #1e1b4b 0%, #312e81 100%);
@@ -212,25 +212,13 @@ nav {
}
.hamburger {
display: none;
flex-direction: column;
gap: 5px;
cursor: pointer;
}
.hamburger span {
display: block;
width: 25px;
height: 3px;
background-color: var(--accent);
border-radius: 3px;
}
/* ==========================================
3. HERO SECTION
========================================== */
.hero {
min-height: 100vh;
/*height: calc(100vh - 45px);
margin-top: 45px;*/
display: flex;
align-items: center;
justify-content: center;
@@ -257,6 +245,7 @@ nav {
opacity: 0.9;
}
/* ==========================================
4. BUTTONS & INPUTS (Neu gestylt)
========================================== */
@@ -317,23 +306,7 @@ nav {
margin-top: 2rem;
}
/* Eingabefeld (Chat) */
#user-input {
flex: 1;
padding: 12px 16px;
border: 1px solid var(--border-input);
border-radius: 8px;
background: var(--bg-input);
color: var(--text-input);
font-size: 1rem;
transition: all 0.2s ease;
}
#user-input:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2);
}
/* ==========================================
5. SECTIONS & CARDS
@@ -356,7 +329,7 @@ h2 {
}
.text {
max-width: 800px;
/*max-width: 1200px;*/
margin: 0 auto;
text-align: center;
font-size: 1.1rem;
@@ -364,101 +337,8 @@ h2 {
color: var(--text-muted);
}
/* Hier ist die wichtige Änderung für das Nebeneinanderstehen */
.projects, .tech-features {
display: grid;
/* Erzeugt so viele Spalten wie nebeneinander passen (mind. 300px breit) */
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin-top: 3rem;
}
.project-card, .tech-card {
background-color: var(--card-bg);
border-radius: 15px;
padding: 2.5rem;
box-shadow: 0 10px 30px var(--card-shadow);
border: 1px solid var(--border-color);
text-align: center;
transition: transform 0.3s ease, background-color 0.3s;
/* Flexbox Korrektur */
display: flex;
flex-direction: column;
justify-content: flex-start; /* Schiebt alles nach oben */
height: 100%; /* Sorgt dafür, dass alle Karten in einer Reihe gleich hoch sind */
}
.project-card h3, .tech-card h3 {
color: var(--accent);
margin-bottom: 1.2rem; /* Abstand zwischen Überschrift und Text */
font-size: 1.5rem;
}
.project-card p, .tech-card p {
color: var(--text-muted);
line-height: 1.6;
margin-bottom: 0; /* Verhindert unnötigen Platz nach unten */
}
.project-card:hover, .tech-card:hover {
transform: translateY(-5px);
}
/* Chat-Container Hintergrund korrigiert */
.chat-container {
max-width: 800px;
margin: 100px auto 20px;
display: flex;
flex-direction: column;
height: calc(100vh - 140px);
background: var(--card-bg); /* Nutzt Card Background Variable */
border-radius: 12px;
box-shadow: 0 10px 25px var(--card-shadow);
border: 1px solid var(--border-color);
overflow: hidden;
}
#chat-messages {
flex: 1;
overflow-y: auto;
padding: 20px;
display: flex;
flex-direction: column;
gap: 15px;
}
.message {
max-width: 80%;
padding: 12px 16px;
border-radius: 15px;
line-height: 1.5;
font-size: 0.95rem;
}
.user-message {
align-self: flex-end;
background-color: var(--accent);
color: var(--accent-text);
border-bottom-right-radius: 5px;
}
.ai-message {
align-self: flex-start;
background-color: var(--bg-section-alt);
color: var(--text-main);
border-bottom-left-radius: 5px;
}
.chat-input-area {
padding: 20px;
background-color: var(--card-bg);
border-top: 1px solid var(--border-color);
display: flex;
gap: 10px;
align-items: center;
}
/* ==========================================
6. FOOTER
@@ -486,22 +366,35 @@ footer {
/* ==========================================
7. RESPONSIVENESS
========================================== */
/* --- Standard: Logo kurz ausblenden, lang anzeigen --- */
/* Standard für Desktop: Beide sichtbar */
.hamburger {
display: none;
flex-direction: column;
gap: 5px;
cursor: pointer;
}
.hamburger span {
display: block;
width: 25px;
height: 3px;
background-color: var(--accent);
border-radius: 3px;
}
.logo-short { display: none;}
.logo-full { display: inline;}
@media (max-width: 950px) {
.hamburger { display: flex; }
.nav-links {
position: absolute;
top: 100%; left: 0; width: 100%;
background-color: var(--header-bg);
flex-direction: column;
max-height: 0;
overflow: hidden;
transition: max-height 0.4s ease;
}
.nav-links.mobile-open { max-height: 300px; }
.nav-links li { border-bottom: 1px solid var(--border-color); }
/*.nav-links a { padding: 0.1rem; display: block; }*/
.logo-full {display: none;}
.logo-short {display: inline;}
.hero h1 { font-size: 2.2rem; }
}

View File

@@ -8,27 +8,6 @@ function toggleHamburger() {
hamburger.classList.toggle('active');
}
// Initialisierung
document.addEventListener('DOMContentLoaded', () => {
// ... dein bestehender Code ...
const savedTheme = localStorage.getItem('theme') || 'auto';
applyTheme(savedTheme);
// Hamburger Menü schließen bei Klick auf einen Link
const navLinksContainer = document.getElementById('navLinks');
const navLinks = navLinksContainer.querySelectorAll('a');
const hamburger = document.querySelector('.hamburger');
navLinks.forEach(link => {
link.addEventListener('click', () => {
// Prüfen, ob das Menü gerade offen ist
if (navLinksContainer.classList.contains('mobile-open')) {
navLinksContainer.classList.remove('mobile-open');
hamburger.classList.remove('active');
}
});
});
});
/* ==========================================
DROPDOWN LOGIC (Vereinheitlicht)
@@ -119,44 +98,9 @@ function applyTheme(theme) {
}
}
async function startDemoChat() {
try {
const response = await fetch('/api/v1/chats/new', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
// WICHTIG: Wenn dein Nginx den Token nicht setzt,
// muss er hier hin: 'Authorization': 'Bearer DEIN_TOKEN'
},
body: JSON.stringify({
"title": "Demo Chat",
"chat": { "model": "arcee-ai/trinity-large-preview:free"}
})
});
if (!response.ok) {
const errorData = await response.json();
console.error('API Fehlerdetails:', errorData);
throw new Error(`Fehler: ${response.status}`);
}
const data = await response.json();
const sessionId = data.id;
// --- DER KORREKTE DYNAMISCHE PFAD ---
// 1. Hole den aktuellen Sprachpfad (z.B. "/de/", "/en/")
// split('/') macht aus "/de/about/" -> ["", "de", "about", ""]
const pathSegments = window.location.pathname.split('/');
const lang = pathSegments[1] || 'de'; // Fallback auf "de", falls kein Pfad da ist
// 2. Leite auf den Pfad MIT Sprache und Session-ID um
// Das ergibt dann z.B. /de/chat/?session=...
window.location.href = `/${lang}/chat/?session=${sessionId}`;
//window.location.href = `/de/chat/?session=${sessionId}`;
} catch (error) {
console.error('Fehler beim Starten:', error);
alert('Demo-Chat konnte nicht gestartet werden.');
}
}

73
static/index.css Normal file
View File

@@ -0,0 +1,73 @@
/* Hier ist die wichtige Änderung für das Nebeneinanderstehen */
.projects, .tech-features {
display: grid;
/* Erzeugt so viele Spalten wie nebeneinander passen (mind. 300px breit) */
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin-top: 3rem;
}
.project-card, .tech-card {
background-color: var(--card-bg);
border-radius: 15px;
padding: 2.5rem;
box-shadow: 0 10px 30px var(--card-shadow);
border: 1px solid var(--border-color);
text-align: center;
transition: transform 0.3s ease, background-color 0.3s;
/* Flexbox Korrektur */
display: flex;
flex-direction: column;
justify-content: flex-start; /* Schiebt alles nach oben */
height: 100%; /* Sorgt dafür, dass alle Karten in einer Reihe gleich hoch sind */
}
.project-card h3, .tech-card h3 {
color: var(--accent);
margin-bottom: 1.2rem; /* Abstand zwischen Überschrift und Text */
font-size: 1.5rem;
}
.project-card p, .tech-card p {
color: var(--text-muted);
line-height: 1.6;
margin-bottom: 0; /* Verhindert unnötigen Platz nach unten */
}
.project-card:hover, .tech-card:hover {
transform: translateY(-5px);
}
@media (max-width: 950px) {
.hamburger {
display: flex !important; /* Hamburger nur auf Chat-Mobile zeigen */
}
.nav-links {
position: absolute;
top: 100%; left: 0; width: 100%;
background-color: var(--header-bg);
flex-direction: column;
max-height: 0;
overflow: hidden;
transition: max-height 0.4s ease;
}
.nav-links li { border-bottom: 1px solid var(--border-color); }
.nav-links.mobile-open {
display: flex !important;
position: absolute;
max-height: 300px;
top: 100%;
right: 0;
background: var(--header-bg);
width: 200px;
flex-direction: column;
box-shadow: 0 5px 15px var(--card-shadow);
}
}

72
static/index.js Normal file
View File

@@ -0,0 +1,72 @@
/*
async function startDemoChat() {
try {
const response = await fetch('/api/v1/chats/new', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
// WICHTIG: Wenn dein Nginx den Token nicht setzt,
// muss er hier hin: 'Authorization': 'Bearer DEIN_TOKEN'
},
body: JSON.stringify({
"title": "Demo Chat",
"chat": {
//"title": "Neuer Chat",
//"models": ["trinity-test"], // WICHTIG: Es ist ein Array!
//"history": { "messages": {}, "children": {} }
"model":
//"arcee-ai/trinity-large-preview:free"
"trinity-test"
}
})
});
if (!response.ok) {
const errorData = await response.json();
console.error('API Fehlerdetails:', errorData);
throw new Error(`Fehler: ${response.status}`);
}
const data = await response.json();
const sessionId = data.id;
// --- DER KORREKTE DYNAMISCHE PFAD ---
// 1. Hole den aktuellen Sprachpfad (z.B. "/de/", "/en/")
// split('/') macht aus "/de/about/" -> ["", "de", "about", ""]
const pathSegments = window.location.pathname.split('/');
const lang = pathSegments[1] || 'de'; // Fallback auf "de", falls kein Pfad da ist
// 2. Leite auf den Pfad MIT Sprache und Session-ID um
// Das ergibt dann z.B. /de/chat/?session=...
window.location.href = `/${lang}/chat/?session=${sessionId}`;
//window.location.href = `/de/chat/?session=${sessionId}`;
} catch (error) {
console.error('Fehler beim Starten:', error);
alert('Demo-Chat konnte nicht gestartet werden.');
}
}
*/
// Initialisierung
document.addEventListener('DOMContentLoaded', () => {
// ... dein bestehender Code ...
const savedTheme = localStorage.getItem('theme') || 'auto';
applyTheme(savedTheme);
// Hamburger Menü schließen bei Klick auf einen Link
const navLinksContainer = document.getElementById('navLinks');
const navLinks = navLinksContainer.querySelectorAll('a');
const hamburger = document.querySelector('.hamburger');
navLinks.forEach(link => {
link.addEventListener('click', () => {
// Prüfen, ob das Menü gerade offen ist
if (navLinksContainer.classList.contains('mobile-open')) {
navLinksContainer.classList.remove('mobile-open');
hamburger.classList.remove('active');
}
});
});
});