1051 lines
54 KiB
HTML
1051 lines
54 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de" class="dark">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Systades - {% block title %}{% endblock %}</title>
|
||
|
||
<!-- Favicon -->
|
||
<link rel="icon" href="{{ url_for('static', filename='img/neuron-favicon.svg') }}" type="image/svg+xml">
|
||
|
||
<!-- Meta Tags -->
|
||
<meta name="description" content="Eine interaktive Plattform zum Visualisieren, Erforschen und Teilen von Wissen">
|
||
<meta name="keywords" content="systades, wissen, visualisierung, lernen, gedanken, theorie">
|
||
<meta name="author" content="Systades-Team">
|
||
|
||
<!-- Tailwind CSS - CDN für Entwicklung und Produktion (laut Vorgabe) -->
|
||
<script src="https://cdn.tailwindcss.com"></script>
|
||
<!-- Alternative lokale Version, falls die CDN-Version blockiert wird -->
|
||
<script>
|
||
tailwind = window.tailwind || {};
|
||
tailwind.config = {
|
||
darkMode: 'class',
|
||
theme: {
|
||
extend: {
|
||
fontFamily: {
|
||
sans: ['Inter', 'ui-sans-serif', 'system-ui', '-apple-system', 'sans-serif'],
|
||
mono: ['JetBrains Mono', 'ui-monospace', 'monospace']
|
||
},
|
||
colors: {
|
||
primary: {
|
||
50: '#f5f3ff',
|
||
100: '#ede9fe',
|
||
200: '#ddd6fe',
|
||
300: '#c4b5fd',
|
||
400: '#a78bfa',
|
||
500: '#8b5cf6',
|
||
600: '#7c3aed',
|
||
700: '#6d28d9',
|
||
800: '#5b21b6',
|
||
900: '#4c1d95'
|
||
},
|
||
secondary: {
|
||
50: '#ecfdf5',
|
||
100: '#d1fae5',
|
||
200: '#a7f3d0',
|
||
300: '#6ee7b7',
|
||
400: '#34d399',
|
||
500: '#10b981',
|
||
600: '#059669',
|
||
700: '#047857',
|
||
800: '#065f46',
|
||
900: '#064e3b'
|
||
},
|
||
dark: {
|
||
500: '#374151',
|
||
600: '#1f2937',
|
||
700: '#111827',
|
||
800: '#0e1220',
|
||
900: '#0a0e19'
|
||
}
|
||
},
|
||
keyframes: {
|
||
float: {
|
||
'0%, 100%': { transform: 'translateY(0)' },
|
||
'50%': { transform: 'translateY(-5px)' }
|
||
},
|
||
'bounce-slow': {
|
||
'0%, 100%': { transform: 'translateY(0)' },
|
||
'50%': { transform: 'translateY(-8px)' }
|
||
}
|
||
},
|
||
animation: {
|
||
float: 'float 3s ease-in-out infinite',
|
||
'bounce-slow': 'bounce-slow 2s ease-in-out infinite'
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<!-- Local Font Files -->
|
||
<link href="{{ url_for('static', filename='fonts/inter.css') }}" rel="stylesheet">
|
||
<link href="{{ url_for('static', filename='fonts/jetbrains-mono.css') }}" rel="stylesheet">
|
||
|
||
<!-- Font Awesome vom CDN -->
|
||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" rel="stylesheet">
|
||
|
||
<!-- Assistent CSS -->
|
||
<link href="{{ url_for('static', filename='css/assistant.css') }}" rel="stylesheet">
|
||
|
||
<!-- Basis-Stylesheet -->
|
||
<link href="{{ url_for('static', filename='style.css') }}" rel="stylesheet">
|
||
|
||
<!-- Base-Styles ausgelagert in eigene Datei -->
|
||
<link href="{{ url_for('static', filename='css/base-styles.css') }}" rel="stylesheet">
|
||
|
||
<!-- Alpine.js - CDN Version -->
|
||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.12.3/dist/cdn.min.js"></script>
|
||
|
||
<!-- Neural Network Background CSS -->
|
||
<link href="{{ url_for('static', filename='css/neural-network-background.css') }}" rel="stylesheet">
|
||
|
||
<!-- Mindmap CSS -->
|
||
<link href="{{ url_for('static', filename='css/mindmap.css', v='1.0.1') }}" rel="stylesheet">
|
||
|
||
<!-- D3.js für Visualisierungen -->
|
||
<script src="https://d3js.org/d3.v7.min.js"></script>
|
||
|
||
<!-- Marked.js für Markdown-Parsing -->
|
||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||
|
||
<!-- ChatGPT Assistant -->
|
||
<script src="{{ url_for('static', filename='js/modules/chatgpt-assistant.js') }}"></script>
|
||
|
||
<!-- Neural Network Background Script -->
|
||
<script src="{{ url_for('static', filename='neural-network-background.js') }}"></script>
|
||
|
||
<!-- Hauptmodul laden (als traditionelles Skript) -->
|
||
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
|
||
|
||
<!-- Seitenspezifische Styles -->
|
||
{% block extra_css %}{% endblock %}
|
||
|
||
<!-- Custom dark/light mode styles -->
|
||
<!-- ► ► Farb‑Token strikt getrennt ◄ ◄ -->
|
||
<style>
|
||
/* Light‑Mode */
|
||
:root {
|
||
--bg-primary:#f8fafc;
|
||
--bg-secondary:#f1f5f9;
|
||
--text-primary:#232837;
|
||
--text-secondary:#475569;
|
||
--accent-primary:#7c3aed;
|
||
--accent-secondary:#8b5cf6;
|
||
--glow-effect:0 0 8px rgba(139,92,246,.08);
|
||
background-image: linear-gradient(to bottom right, rgba(248, 250, 252, 0.8), rgba(241, 245, 249, 0.8));
|
||
background-attachment: fixed;
|
||
}
|
||
/* Dark‑Mode */
|
||
.dark {
|
||
--bg-primary:#181c24;
|
||
--bg-secondary:#232837;
|
||
--text-primary:#f9fafb;
|
||
--text-secondary:#e5e7eb;
|
||
--accent-primary:#6d28d9;
|
||
--accent-secondary:#8b5cf6;
|
||
--glow-effect:0 0 8px rgba(124,58,237,.15);
|
||
}
|
||
|
||
body {
|
||
@apply min-h-screen bg-[color:var(--bg-primary)] text-[color:var(--text-primary)];
|
||
transition: background-color 0.5s ease-in-out, color 0.3s ease-in-out, background-image 0.5s ease-in-out;
|
||
}
|
||
|
||
/* Utilities */
|
||
.mystical-glow { text-shadow: var(--glow-effect); }
|
||
.gradient-text {
|
||
background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));
|
||
-webkit-background-clip:text; background-clip:text; color:transparent; text-shadow:none;
|
||
}
|
||
.glass-morphism { backdrop-filter: blur(10px); }
|
||
.glass-navbar { @apply glass-morphism border backdrop-blur-xl; }
|
||
.light .glass-navbar { background-color:rgba(255,255,255,.8); border-color:rgba(0,0,0,.05); }
|
||
.dark .glass-navbar { background-color:rgba(10,14,25,.8); border-color:rgba(255,255,255,.05); }
|
||
|
||
/* Light-Mode spezifische Stile */
|
||
body:not(.dark) {
|
||
background-color: var(--bg-primary);
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.nav-link-light {
|
||
color: var(--text-secondary);
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.nav-link-light:hover {
|
||
color: var(--text-primary);
|
||
background-color: rgba(126, 34, 206, 0.1);
|
||
}
|
||
|
||
.nav-link-light-active {
|
||
color: var(--accent-primary);
|
||
background-color: rgba(126, 34, 206, 0.15);
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* Kartendesign im Light-Mode */
|
||
body:not(.dark) .card {
|
||
background-color: white;
|
||
border: 1px solid #e5e7eb;
|
||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||
}
|
||
|
||
body:not(.dark) .card:hover {
|
||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||
}
|
||
|
||
a:hover {
|
||
color: var(--light-primary-hover);
|
||
}
|
||
|
||
/* Light Mode Buttons */
|
||
body:not(.dark) .btn,
|
||
body:not(.dark) button:not(.toggle) {
|
||
background: linear-gradient(135deg, #7c3aed, #6d28d9);
|
||
color: white !important;
|
||
border: none;
|
||
box-shadow: 0 2px 4px rgba(124, 58, 237, 0.25);
|
||
border-radius: 8px;
|
||
padding: 0.625rem 1.25rem;
|
||
transition: all 0.2s ease;
|
||
font-weight: 600;
|
||
letter-spacing: 0.02em;
|
||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||
}
|
||
|
||
body:not(.dark) .btn:hover,
|
||
body:not(.dark) button:not(.toggle):hover {
|
||
background: linear-gradient(135deg, #8b5cf6, #7c3aed);
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 4px 12px rgba(124, 58, 237, 0.3);
|
||
color: white !important;
|
||
}
|
||
|
||
/* KI-Chat Button im Light-Mode */
|
||
body:not(.dark) [onclick*="MindMap.assistant.toggleAssistant"] {
|
||
background: linear-gradient(135deg, #7c3aed, #4f46e5);
|
||
color: white !important;
|
||
font-weight: 500;
|
||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||
}
|
||
|
||
body:not(.dark) [onclick*="MindMap.assistant.toggleAssistant"]:hover {
|
||
background: linear-gradient(135deg, #8b5cf6, #6366f1);
|
||
box-shadow: 0 4px 12px rgba(79, 70, 229, 0.3);
|
||
}
|
||
|
||
/* Style improvements for the theme toggle button */
|
||
.theme-toggle {
|
||
position: relative;
|
||
width: 48px;
|
||
height: 24px;
|
||
border-radius: 24px;
|
||
padding: 2px;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
overflow: hidden;
|
||
}
|
||
|
||
body.dark .theme-toggle {
|
||
background: linear-gradient(to right, #7c3aed, #3b82f6);
|
||
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3), 0 0 10px rgba(124, 58, 237, 0.3);
|
||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||
}
|
||
|
||
body:not(.dark) .theme-toggle {
|
||
background: linear-gradient(to right, #8b5cf6, #60a5fa);
|
||
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1), 0 0 10px rgba(124, 58, 237, 0.15);
|
||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||
}
|
||
|
||
.theme-toggle::after {
|
||
content: '';
|
||
position: absolute;
|
||
width: 20px;
|
||
height: 20px;
|
||
border-radius: 50%;
|
||
top: 2px;
|
||
transition: all 0.3s ease;
|
||
z-index: 2;
|
||
}
|
||
|
||
body.dark .theme-toggle::after {
|
||
background: #f1f5f9 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%237c3aed' width='14' height='14'%3E%3Cpath d='M21.752 15.002A9.718 9.718 0 0118 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 003 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 009.002-5.998z'%3E%3C/path%3E%3C/svg%3E") no-repeat center center;
|
||
transform: translateX(24px);
|
||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
|
||
}
|
||
|
||
body:not(.dark) .theme-toggle::after {
|
||
background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23f59e0b' width='14' height='14'%3E%3Cpath d='M12 2.25a.75.75 0 01.75.75v2.25a.75.75 0 01-1.5 0V3a.75.75 0 01.75-.75zM7.5 12a4.5 4.5 0 119 0 4.5 4.5 0 01-9 0zM18.894 6.166a.75.75 0 00-1.06-1.06l-1.591 1.59a.75.75 0 101.06 1.061l1.591-1.59zM21.75 12a.75.75 0 01-.75.75h-2.25a.75.75 0 010-1.5H21a.75.75 0 01.75.75zM17.834 18.894a.75.75 0 001.06-1.06l-1.59-1.591a.75.75 0 10-1.061 1.06l1.59 1.591zM12 18a.75.75 0 01.75.75V21a.75.75 0 01-1.5 0v-2.25A.75.75 0 0112 18zM7.758 17.303a.75.75 0 00-1.061-1.06l-1.591 1.59a.75.75 0 001.06 1.061l1.591-1.59zM6 12a.75.75 0 01-.75.75H3a.75.75 0 010-1.5h2.25A.75.75 0 016 12zM6.697 7.757a.75.75 0 001.06-1.06l-1.59-1.591a.75.75 0 00-1.061 1.06l1.59 1.591z'%3E%3C/path%3E%3C/svg%3E") no-repeat center center;
|
||
transform: translateX(2px);
|
||
box-shadow: 0 0 8px rgba(124, 58, 237, 0.2);
|
||
}
|
||
|
||
.theme-toggle:hover::after {
|
||
box-shadow: 0 0 12px rgba(124, 58, 237, 0.4);
|
||
}
|
||
|
||
/* Fixes for light mode button text colors */
|
||
body:not(.dark) .btn-primary {
|
||
color: white !important;
|
||
}
|
||
|
||
/* Fix for KI-Chat container */
|
||
#chatgpt-assistant {
|
||
position: fixed;
|
||
bottom: 1.5rem;
|
||
right: 1.5rem;
|
||
z-index: 100;
|
||
}
|
||
|
||
.chat-assistant {
|
||
max-height: 80vh !important;
|
||
}
|
||
|
||
.chat-assistant .chat-messages {
|
||
max-height: calc(80vh - 160px) !important;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body data-page="{{ request.endpoint }}" class="relative overflow-x-hidden dark bg-gray-900 text-white" x-data="{
|
||
darkMode: true,
|
||
mobileMenuOpen: false,
|
||
userMenuOpen: false,
|
||
showSettingsModal: false,
|
||
|
||
init() {
|
||
this.initDarkMode();
|
||
},
|
||
|
||
initDarkMode() {
|
||
// Lade zuerst den Wert aus dem localStorage (client-seitig)
|
||
const storedMode = localStorage.getItem('colorMode');
|
||
if (storedMode) {
|
||
this.darkMode = storedMode === 'dark';
|
||
}
|
||
|
||
// Dann hole die Server-Einstellung, die Vorrang hat
|
||
this.fetchDarkModeFromSession();
|
||
},
|
||
|
||
fetchDarkModeFromSession() {
|
||
fetch('/api/get_dark_mode')
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
this.darkMode = data.darkMode === 'true';
|
||
this.applyDarkMode();
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Fehler beim Laden der Dark Mode-Einstellung:', error);
|
||
});
|
||
},
|
||
|
||
applyDarkMode() {
|
||
document.querySelector('html').classList.toggle('dark', this.darkMode);
|
||
document.querySelector('body').classList.toggle('dark', this.darkMode);
|
||
localStorage.setItem('colorMode', this.darkMode ? 'dark' : 'light');
|
||
},
|
||
|
||
toggleDarkMode() {
|
||
this.darkMode = !this.darkMode;
|
||
this.applyDarkMode();
|
||
|
||
// Server über Änderung informieren
|
||
fetch('/api/set_dark_mode', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({ darkMode: this.darkMode })
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
// Event auslösen für andere Komponenten
|
||
document.dispatchEvent(new CustomEvent('darkModeToggled', {
|
||
detail: { isDark: this.darkMode }
|
||
}));
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Fehler beim Speichern der Dark Mode-Einstellung:', error);
|
||
});
|
||
}
|
||
}">
|
||
<!-- App-Container -->
|
||
<div id="app-container" class="flex flex-col min-h-screen">
|
||
<!-- Hauptnavigation -->
|
||
<nav class="sticky top-0 left-0 right-0 z-50 transition-all duration-300 py-4 px-5 border-b glass-morphism"
|
||
x-bind:class="darkMode ? 'glass-navbar-dark border-white/10' : 'glass-navbar-light border-gray-200/50'">
|
||
<div class="container mx-auto flex justify-between items-center">
|
||
<!-- Logo -->
|
||
<a href="{{ url_for('index') }}" class="flex items-center group">
|
||
<img src="{{ url_for('static', filename='img/neuron-logo.svg') }}" alt="Systades Logo" class="w-8 h-8 mr-2 transform transition-transform group-hover:scale-110">
|
||
<span class="text-2xl font-bold gradient-text transform transition-transform group-hover:scale-105">Systades</span>
|
||
</a>
|
||
|
||
<!-- Hauptnavigation - Desktop -->
|
||
<div class="hidden md:flex items-center space-x-5">
|
||
<a href="{{ url_for('index') }}"
|
||
class="nav-link flex items-center"
|
||
x-bind:class="darkMode
|
||
? '{{ 'nav-link-active' if request.endpoint == 'index' else '' }}'
|
||
: '{{ 'nav-link-light-active' if request.endpoint == 'index' else 'nav-link-light' }}'">
|
||
<i class="fa-solid fa-home mr-2"></i>Start
|
||
</a>
|
||
<a href="{{ url_for('mindmap') }}"
|
||
class="nav-link flex items-center"
|
||
x-bind:class="darkMode
|
||
? '{{ 'nav-link-active' if request.endpoint == 'mindmap' else '' }}'
|
||
: '{{ 'nav-link-light-active' if request.endpoint == 'mindmap' else 'nav-link-light' }}'">
|
||
<i class="fa-solid fa-diagram-project mr-2"></i>Mindmap
|
||
</a>
|
||
{% if current_user.is_authenticated %}
|
||
<a href="{{ url_for('social_feed') }}"
|
||
class="nav-link flex items-center"
|
||
x-bind:class="darkMode
|
||
? '{{ 'nav-link-active' if request.endpoint == 'social_feed' else '' }}'
|
||
: '{{ 'nav-link-light-active' if request.endpoint == 'social_feed' else 'nav-link-light' }}'">
|
||
<i class="fa-solid fa-home mr-2"></i>Feed
|
||
</a>
|
||
<a href="{{ url_for('discover') }}"
|
||
class="nav-link flex items-center"
|
||
x-bind:class="darkMode
|
||
? '{{ 'nav-link-active' if request.endpoint == 'discover' else '' }}'
|
||
: '{{ 'nav-link-light-active' if request.endpoint == 'discover' else 'nav-link-light' }}'">
|
||
<i class="fa-solid fa-compass mr-2"></i>Entdecken
|
||
</a>
|
||
{% endif %}
|
||
<a href="{{ url_for('search_thoughts_page') }}"
|
||
class="nav-link flex items-center"
|
||
x-bind:class="darkMode
|
||
? '{{ 'nav-link-active' if request.endpoint == 'search_thoughts_page' else '' }}'
|
||
: '{{ 'nav-link-light-active' if request.endpoint == 'search_thoughts_page' else 'nav-link-light' }}'">
|
||
<i class="fa-solid fa-search mr-2"></i>Suche
|
||
</a>
|
||
<!-- KI-Assistent Button -->
|
||
<button onclick="window.MindMap && window.MindMap.assistant && window.MindMap.assistant.toggleAssistant(true)"
|
||
class="nav-link flex items-center"
|
||
x-bind:class="darkMode
|
||
? 'bg-gradient-to-r from-purple-900/90 to-indigo-800/90 text-white font-medium px-4 py-2 rounded-xl hover:shadow-lg hover:shadow-purple-800/30 transition-all duration-300'
|
||
: 'bg-gradient-to-r from-purple-600 to-indigo-500 text-white font-medium px-4 py-2 rounded-xl hover:shadow-md transition-all duration-300'">
|
||
<i class="fa-solid fa-robot mr-2"></i>KI-Chat
|
||
</button>
|
||
{% if current_user.is_authenticated %}
|
||
<a href="{{ url_for('profile') }}"
|
||
class="nav-link flex items-center"
|
||
x-bind:class="darkMode
|
||
? '{{ 'nav-link-active' if request.endpoint == 'profile' else '' }}'
|
||
: '{{ 'nav-link-light-active' if request.endpoint == 'profile' else 'nav-link-light' }}'">
|
||
<i class="fa-solid fa-user mr-2"></i>Profil
|
||
</a>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<!-- Rechte Seite -->
|
||
<div class="flex items-center space-x-4">
|
||
<!-- Dark/Light Mode Schalter -->
|
||
<button
|
||
@click="toggleDarkMode()"
|
||
class="theme-toggle relative w-12 h-6 rounded-full transition-all duration-300 flex items-center overflow-hidden"
|
||
aria-label="Dark Mode umschalten"
|
||
>
|
||
<span class="sr-only" x-text="darkMode ? 'Zum Light Mode wechseln' : 'Zum Dark Mode wechseln'"></span>
|
||
</button>
|
||
<!-- Profil-Link oder Login -->
|
||
{% if current_user.is_authenticated %}
|
||
<div class="relative" x-data="{ open: false }">
|
||
<button @click="open = !open"
|
||
class="flex items-center space-x-2 p-2 rounded-full transition-all duration-300 cursor-pointer"
|
||
x-bind:class="darkMode
|
||
? 'bg-gray-800/80 text-white/90 hover:bg-gray-700/80'
|
||
: 'bg-gray-200/80 text-gray-700 hover:bg-gray-300/80'">
|
||
<div class="w-9 h-9 rounded-full flex items-center justify-center text-white font-medium text-sm overflow-hidden"
|
||
style="background: linear-gradient(135deg, #8b5cf6, #6366f1);">
|
||
{% if current_user.avatar %}
|
||
<img src="{{ current_user.avatar }}" alt="{{ current_user.username }}" class="w-full h-full object-cover">
|
||
{% else %}
|
||
<svg width="100%" height="100%" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<circle cx="100" cy="100" r="98" fill="url(#user-gradient)" stroke="#7C3AED" stroke-width="4"/>
|
||
<circle cx="100" cy="80" r="36" fill="white"/>
|
||
<path d="M100 140C77.9086 140 60 157.909 60 180H140C140 157.909 122.091 140 100 140Z" fill="white"/>
|
||
<defs>
|
||
<linearGradient id="user-gradient" x1="0" y1="0" x2="200" y2="200" gradientUnits="userSpaceOnUse">
|
||
<stop offset="0" stop-color="#8B5CF6"/>
|
||
<stop offset="1" stop-color="#3B82F6"/>
|
||
</linearGradient>
|
||
</defs>
|
||
</svg>
|
||
{% endif %}
|
||
</div>
|
||
<span class="hidden md:block">{{ current_user.username }}</span>
|
||
<i class="fas fa-chevron-down text-xs opacity-60 ml-1.5"></i>
|
||
</button>
|
||
|
||
<!-- Dropdown-Menü -->
|
||
<div x-show="open"
|
||
@click.away="open = false"
|
||
x-transition:enter="transition ease-out duration-200"
|
||
x-transition:enter-start="opacity-0 scale-95"
|
||
x-transition:enter-end="opacity-100 scale-100"
|
||
x-transition:leave="transition ease-in duration-150"
|
||
x-transition:leave-start="opacity-100 scale-100"
|
||
x-transition:leave-end="opacity-0 scale-95"
|
||
class="absolute right-0 mt-2 w-52 rounded-2xl overflow-hidden shadow-lg transform origin-top-right z-50"
|
||
x-bind:class="darkMode
|
||
? 'bg-gray-800/95 backdrop-blur-md border border-white/10'
|
||
: 'bg-white/95 backdrop-blur-md border border-gray-200/50'">
|
||
<a href="{{ url_for('profile') }}"
|
||
class="block px-4 py-3 transition-colors duration-200 flex items-center"
|
||
x-bind:class="darkMode
|
||
? 'text-white/90 hover:bg-purple-500/20'
|
||
: 'text-gray-700 hover:bg-purple-500/10'">
|
||
<i class="fa-solid fa-user mr-2 text-purple-400"></i>Profil
|
||
</a>
|
||
<a href="{{ url_for('my_account') }}"
|
||
class="block px-4 py-3 transition-colors duration-200 flex items-center"
|
||
x-bind:class="darkMode
|
||
? 'text-white/90 hover:bg-purple-500/20'
|
||
: 'text-gray-700 hover:bg-purple-500/10'">
|
||
<i class="fa-solid fa-bookmark mr-2 text-purple-400"></i>Meine Merkliste
|
||
</a>
|
||
<a href="{{ url_for('settings') }}"
|
||
class="block px-4 py-3 transition-colors duration-200 flex items-center"
|
||
x-bind:class="darkMode
|
||
? 'text-white/90 hover:bg-purple-500/20'
|
||
: 'text-gray-700 hover:bg-purple-500/10'">
|
||
<i class="fa-solid fa-gear mr-2 text-purple-400"></i>Einstellungen
|
||
</a>
|
||
<div class="my-2 h-px" x-bind:class="darkMode ? 'bg-white/10' : 'bg-gray-200'"></div>
|
||
<a href="{{ url_for('logout') }}"
|
||
class="block px-4 py-3 transition-colors duration-200 flex items-center"
|
||
x-bind:class="darkMode
|
||
? 'text-white/90 hover:bg-red-500/20'
|
||
: 'text-gray-700 hover:bg-red-500/10'">
|
||
<i class="fa-solid fa-right-from-bracket mr-2 text-red-400"></i>Abmelden
|
||
</a>
|
||
</div>
|
||
</div>
|
||
{% else %}
|
||
<div class="flex items-center space-x-2">
|
||
<a href="{{ url_for('login') }}"
|
||
class="py-2 px-4 rounded-lg transition-all duration-300"
|
||
x-bind:class="darkMode
|
||
? 'text-white/90 hover:bg-dark-700/80'
|
||
: 'text-gray-700 hover:bg-gray-100/80'">
|
||
<i class="fa-solid fa-sign-in-alt mr-2"></i>Login
|
||
</a>
|
||
<a href="{{ url_for('register') }}"
|
||
class="py-2 px-4 rounded-lg transition-all duration-300 font-medium"
|
||
x-bind:class="darkMode
|
||
? 'bg-purple-800/80 text-white hover:bg-purple-700/80'
|
||
: 'bg-purple-600/20 text-gray-700 hover:bg-purple-600/30'">
|
||
Registrieren
|
||
</a>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- Mobilmenü-Button -->
|
||
<button @click="mobileMenuOpen = !mobileMenuOpen"
|
||
class="md:hidden rounded-xl p-2.5 transition-colors duration-200 focus:outline-none"
|
||
x-bind:class="darkMode
|
||
? 'text-white/90 hover:bg-gray-700/50'
|
||
: 'text-gray-700 hover:bg-gray-200/80'">
|
||
<i class="fa-solid" :class="mobileMenuOpen ? 'fa-times' : 'fa-bars'"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
|
||
<!-- Mobile Menü -->
|
||
<div x-show="mobileMenuOpen"
|
||
x-cloak
|
||
x-transition:enter="transition ease-out duration-200"
|
||
x-transition:enter-start="opacity-0 -translate-y-4"
|
||
x-transition:enter-end="opacity-100 translate-y-0"
|
||
x-transition:leave="transition ease-in duration-150"
|
||
x-transition:leave-start="opacity-100 translate-y-0"
|
||
x-transition:leave-end="opacity-0 -translate-y-4"
|
||
class="md:hidden w-full z-40 border-b"
|
||
x-bind:class="darkMode
|
||
? 'bg-gray-900/90 backdrop-blur-lg border-white/10'
|
||
: 'bg-white/90 backdrop-blur-lg border-gray-200'">
|
||
<div class="px-4 py-4 space-y-3">
|
||
<a href="{{ url_for('index') }}"
|
||
class="block py-3.5 px-4 rounded-xl transition-all duration-200 flex items-center"
|
||
x-bind:class="darkMode
|
||
? '{{ 'bg-purple-500/20 text-white' if request.endpoint == 'index' else 'text-white/80 hover:bg-gray-800/80 hover:text-white' }}'
|
||
: '{{ 'bg-purple-500/10 text-gray-900' if request.endpoint == 'index' else 'text-gray-700 hover:bg-gray-100 hover:text-gray-900' }}'">
|
||
<i class="fa-solid fa-home w-5 mr-3"></i>Start
|
||
</a>
|
||
<a href="{{ url_for('mindmap') }}"
|
||
class="block py-3.5 px-4 rounded-xl transition-all duration-200 flex items-center"
|
||
x-bind:class="darkMode
|
||
? '{{ 'bg-purple-500/20 text-white' if request.endpoint == 'mindmap' else 'text-white/80 hover:bg-gray-800/80 hover:text-white' }}'
|
||
: '{{ 'bg-purple-500/10 text-gray-900' if request.endpoint == 'mindmap' else 'text-gray-700 hover:bg-gray-100 hover:text-gray-900' }}'">
|
||
<i class="fa-solid fa-diagram-project w-5 mr-3"></i>Mindmap
|
||
</a>
|
||
{% if current_user.is_authenticated %}
|
||
<a href="{{ url_for('social_feed') }}"
|
||
class="block py-3.5 px-4 rounded-xl transition-all duration-200 flex items-center"
|
||
x-bind:class="darkMode
|
||
? '{{ 'bg-purple-500/20 text-white' if request.endpoint == 'social_feed' else 'text-white/80 hover:bg-gray-800/80 hover:text-white' }}'
|
||
: '{{ 'bg-purple-500/10 text-gray-900' if request.endpoint == 'social_feed' else 'text-gray-700 hover:bg-gray-100 hover:text-gray-900' }}'">
|
||
<i class="fa-solid fa-home w-5 mr-3"></i>Feed
|
||
</a>
|
||
<a href="{{ url_for('discover') }}"
|
||
class="block py-3.5 px-4 rounded-xl transition-all duration-200 flex items-center"
|
||
x-bind:class="darkMode
|
||
? '{{ 'bg-purple-500/20 text-white' if request.endpoint == 'discover' else 'text-white/80 hover:bg-gray-800/80 hover:text-white' }}'
|
||
: '{{ 'bg-purple-500/10 text-gray-900' if request.endpoint == 'discover' else 'text-gray-700 hover:bg-gray-100 hover:text-gray-900' }}'">
|
||
<i class="fa-solid fa-compass w-5 mr-3"></i>Entdecken
|
||
</a>
|
||
{% endif %}
|
||
<a href="{{ url_for('search_thoughts_page') }}"
|
||
class="block py-3.5 px-4 rounded-xl transition-all duration-200 flex items-center"
|
||
x-bind:class="darkMode
|
||
? '{{ 'bg-purple-500/20 text-white' if request.endpoint == 'search_thoughts_page' else 'text-white/80 hover:bg-gray-800/80 hover:text-white' }}'
|
||
: '{{ 'bg-purple-500/10 text-gray-900' if request.endpoint == 'search_thoughts_page' else 'text-gray-700 hover:bg-gray-100 hover:text-gray-900' }}'">
|
||
<i class="fa-solid fa-search w-5 mr-3"></i>Suche
|
||
</a>
|
||
<!-- KI-Button für Mobilmenü -->
|
||
<button onclick="window.MindMap && window.MindMap.assistant && window.MindMap.assistant.toggleAssistant(true); mobileMenuOpen = false;"
|
||
class="block w-full text-left py-3.5 px-4 rounded-xl transition-all duration-200 flex items-center"
|
||
x-bind:class="darkMode
|
||
? 'bg-gradient-to-r from-purple-600/30 to-blue-500/30 text-white hover:from-purple-600/40 hover:to-blue-500/40'
|
||
: 'bg-gradient-to-r from-purple-600 to-blue-500 text-white hover:from-purple-600/90 hover:to-blue-500/90'">
|
||
<i class="fa-solid fa-robot w-5 mr-3"></i>KI-Chat
|
||
</button>
|
||
{% if current_user.is_authenticated %}
|
||
<a href="{{ url_for('profile') }}"
|
||
class="block py-3.5 px-4 rounded-xl transition-all duration-200 flex items-center"
|
||
x-bind:class="darkMode
|
||
? '{{ 'bg-purple-500/20 text-white' if request.endpoint == 'profile' else 'text-white/80 hover:bg-gray-800/80 hover:text-white' }}'
|
||
: '{{ 'bg-purple-500/10 text-gray-900' if request.endpoint == 'profile' else 'text-gray-700 hover:bg-gray-100 hover:text-gray-900' }}'">
|
||
<i class="fa-solid fa-user w-5 mr-3"></i>Profil
|
||
</a>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Hauptinhalt -->
|
||
<main class="flex-grow pt-6">
|
||
{% block content %}{% endblock %}
|
||
</main>
|
||
|
||
<!-- Footer -->
|
||
<footer class="mt-12 py-10 transition-colors duration-300 rounded-t-3xl mx-4 sm:mx-6 md:mx-8"
|
||
:class="darkMode ? 'bg-gray-900/60 backdrop-blur-xl border-t border-white/10' : 'bg-white/60 backdrop-blur-xl border-t border-gray-200/50'">
|
||
<div class="container mx-auto px-4">
|
||
<div class="grid grid-cols-1 md:grid-cols-3 gap-10">
|
||
<!-- Logo und Beschreibung -->
|
||
<div class="text-center md:text-left flex flex-col">
|
||
<a href="{{ url_for('index') }}" class="text-2xl font-bold mb-4 gradient-text inline-block transform transition-transform hover:scale-105">Systades</a>
|
||
<p class="mt-2 text-sm max-w-md"
|
||
:class="darkMode ? 'text-gray-300' : 'text-gray-600'">
|
||
Eine interaktive Plattform zum Visualisieren, Erforschen und Teilen von Wissen und Gedanken in einem strukturierten Format.
|
||
</p>
|
||
<!-- Social Media Icons -->
|
||
<div class="flex items-center space-x-4 mt-6 justify-center md:justify-start">
|
||
<a href="#" class="transition-all duration-200 transform hover:scale-110 hover:-translate-y-1"
|
||
:class="darkMode ? 'text-purple-400 hover:text-purple-300' : 'text-purple-600 hover:text-purple-500'">
|
||
<i class="fab fa-twitter text-xl"></i>
|
||
</a>
|
||
<a href="#" class="transition-all duration-200 transform hover:scale-110 hover:-translate-y-1"
|
||
:class="darkMode ? 'text-purple-400 hover:text-purple-300' : 'text-purple-600 hover:text-purple-500'">
|
||
<i class="fab fa-linkedin text-xl"></i>
|
||
</a>
|
||
<a href="#" class="transition-all duration-200 transform hover:scale-110 hover:-translate-y-1"
|
||
:class="darkMode ? 'text-purple-400 hover:text-purple-300' : 'text-purple-600 hover:text-purple-500'">
|
||
<i class="fab fa-github text-xl"></i>
|
||
</a>
|
||
<a href="#" class="transition-all duration-200 transform hover:scale-110 hover:-translate-y-1"
|
||
:class="darkMode ? 'text-purple-400 hover:text-purple-300' : 'text-purple-600 hover:text-purple-500'">
|
||
<i class="fab fa-discord text-xl"></i>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Links -->
|
||
<div class="grid grid-cols-2 gap-8">
|
||
<div class="flex flex-col space-y-3">
|
||
<h3 class="font-semibold text-lg mb-2"
|
||
:class="darkMode ? 'text-white' : 'text-gray-800'">Navigation</h3>
|
||
<a href="{{ url_for('index') }}" class="text-sm transition-all duration-200"
|
||
:class="darkMode ? 'text-gray-300 hover:text-white' : 'text-gray-600 hover:text-gray-900'">
|
||
Startseite
|
||
</a>
|
||
<a href="{{ url_for('mindmap') }}" class="text-sm transition-all duration-200"
|
||
:class="darkMode ? 'text-gray-300 hover:text-white' : 'text-gray-600 hover:text-gray-900'">
|
||
Mindmap
|
||
</a>
|
||
<a href="{{ url_for('search_thoughts_page') }}" class="text-sm transition-all duration-200"
|
||
:class="darkMode ? 'text-gray-300 hover:text-white' : 'text-gray-600 hover:text-gray-900'">
|
||
Suche
|
||
</a>
|
||
{% if current_user.is_authenticated %}
|
||
<a href="{{ url_for('profile') }}" class="text-sm transition-all duration-200"
|
||
:class="darkMode ? 'text-gray-300 hover:text-white' : 'text-gray-600 hover:text-gray-900'">
|
||
Profil
|
||
</a>
|
||
<a href="{{ url_for('my_account') }}" class="text-sm transition-all duration-200"
|
||
:class="darkMode ? 'text-gray-300 hover:text-white' : 'text-gray-600 hover:text-gray-900'">
|
||
Meine Merkliste
|
||
</a>
|
||
{% else %}
|
||
<a href="{{ url_for('login') }}" class="text-sm transition-all duration-200"
|
||
:class="darkMode ? 'text-gray-300 hover:text-white' : 'text-gray-600 hover:text-gray-900'">
|
||
Anmelden
|
||
</a>
|
||
<a href="{{ url_for('register') }}" class="text-sm transition-all duration-200"
|
||
:class="darkMode ? 'text-gray-300 hover:text-white' : 'text-gray-600 hover:text-gray-900'">
|
||
Registrieren
|
||
</a>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<div class="flex flex-col space-y-3">
|
||
<h3 class="font-semibold text-lg mb-2"
|
||
:class="darkMode ? 'text-white' : 'text-gray-800'">Rechtliches</h3>
|
||
<a href="{{ url_for('impressum') }}" class="text-sm transition-all duration-200"
|
||
:class="darkMode ? 'text-gray-300 hover:text-white' : 'text-gray-600 hover:text-gray-900'">
|
||
Impressum
|
||
</a>
|
||
<a href="{{ url_for('ueber_uns') }}" class="text-sm transition-all duration-200"
|
||
:class="darkMode ? 'text-gray-300 hover:text-white' : 'text-gray-600 hover:text-gray-900'">
|
||
Über uns
|
||
</a>
|
||
<a href="{{ url_for('datenschutz') }}" class="text-sm transition-all duration-200"
|
||
:class="darkMode ? 'text-gray-300 hover:text-white' : 'text-gray-600 hover:text-gray-900'">
|
||
Datenschutz
|
||
</a>
|
||
<a href="{{ url_for('agb') }}" class="text-sm transition-all duration-200"
|
||
:class="darkMode ? 'text-gray-300 hover:text-white' : 'text-gray-600 hover:text-gray-900'">
|
||
AGB
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Newsletter Anmeldung -->
|
||
<div class="flex flex-col">
|
||
<h3 class="font-semibold text-lg mb-4"
|
||
:class="darkMode ? 'text-white' : 'text-gray-800'">Newsletter</h3>
|
||
<p class="text-sm mb-4"
|
||
:class="darkMode ? 'text-gray-300' : 'text-gray-600'">
|
||
Bleibe auf dem Laufenden mit unseren neuesten Funktionen und Updates.
|
||
</p>
|
||
<form class="flex flex-col space-y-3">
|
||
<input type="email" placeholder="Deine E-Mail Adresse"
|
||
class="px-4 py-2.5 rounded-xl transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-purple-500"
|
||
:class="darkMode ? 'bg-gray-800/80 text-white border border-gray-700 focus:bg-gray-800' : 'bg-white/80 text-gray-800 border border-gray-300 focus:bg-white'" />
|
||
<button type="submit"
|
||
class="px-4 py-2.5 rounded-xl font-medium transition-all duration-300 bg-gradient-to-r from-purple-600 to-indigo-600 text-white shadow-md hover:shadow-lg hover:-translate-y-0.5">
|
||
Abonnieren
|
||
</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Untere Linie -->
|
||
<div class="mt-10 pt-6 border-t flex flex-col md:flex-row justify-between items-center"
|
||
:class="darkMode ? 'border-gray-800/50 text-gray-400' : 'border-gray-300/50 text-gray-600'">
|
||
<div class="text-xs md:text-sm mb-3 md:mb-0">
|
||
© {{ current_year }} Systades. Alle Rechte vorbehalten.
|
||
</div>
|
||
<div class="text-xs md:text-sm">
|
||
Designed with <i class="fas fa-heart text-pink-500"></i> in Deutschland
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
</div>
|
||
|
||
<!-- Hilfsscripts -->
|
||
{% block scripts %}{% endblock %}
|
||
{% block extra_js %}{% endblock %}
|
||
|
||
<!-- ChatGPT Initialisierung -->
|
||
<script>
|
||
// Prüfe, ob ChatGPTAssistant bereits existiert
|
||
if (typeof ChatGPTAssistant === 'undefined') {
|
||
class ChatGPTAssistant {
|
||
constructor() {
|
||
this.chatContainer = null;
|
||
this.messages = [];
|
||
this.isOpen = false;
|
||
}
|
||
|
||
init() {
|
||
// Chat-Container erstellen, falls noch nicht vorhanden
|
||
if (!document.getElementById('chat-assistant-container')) {
|
||
this.createChatInterface();
|
||
}
|
||
|
||
// Event-Listener für Chat-Button
|
||
const chatButton = document.getElementById('chat-assistant-button');
|
||
if (chatButton) {
|
||
chatButton.addEventListener('click', () => this.toggleChat());
|
||
}
|
||
|
||
// Event-Listener für Senden-Button
|
||
const sendButton = document.getElementById('chat-send-button');
|
||
if (sendButton) {
|
||
sendButton.addEventListener('click', () => this.sendMessage());
|
||
}
|
||
|
||
// Event-Listener für Eingabefeld (Enter-Taste)
|
||
const inputField = document.getElementById('chat-input');
|
||
if (inputField) {
|
||
inputField.addEventListener('keypress', (e) => {
|
||
if (e.key === 'Enter') {
|
||
e.preventDefault();
|
||
this.sendMessage();
|
||
}
|
||
});
|
||
}
|
||
|
||
console.log('KI-Assistent erfolgreich initialisiert');
|
||
}
|
||
|
||
createChatInterface() {
|
||
// Chat-Button erstellen
|
||
const chatButton = document.createElement('button');
|
||
chatButton.id = 'chat-assistant-button';
|
||
chatButton.className = 'fixed bottom-6 right-6 bg-primary-600 text-white rounded-full p-4 shadow-lg z-50 hover:bg-primary-700 transition-all';
|
||
chatButton.innerHTML = '<i class="fas fa-robot text-xl"></i>';
|
||
document.body.appendChild(chatButton);
|
||
|
||
// Chat-Container erstellen
|
||
const chatContainer = document.createElement('div');
|
||
chatContainer.id = 'chat-assistant-container';
|
||
chatContainer.className = 'fixed bottom-24 right-6 w-80 md:w-96 bg-white dark:bg-gray-800 rounded-xl shadow-xl z-50 flex flex-col transition-all duration-300 transform scale-0 origin-bottom-right';
|
||
chatContainer.style.height = '500px';
|
||
chatContainer.style.maxHeight = '70vh';
|
||
|
||
// Chat-Header
|
||
chatContainer.innerHTML = `
|
||
<div class="p-4 border-b dark:border-gray-700 flex justify-between items-center">
|
||
<h3 class="font-bold text-gray-800 dark:text-white">Systades Assistent</h3>
|
||
<button id="chat-close-button" class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
|
||
<i class="fas fa-times"></i>
|
||
</button>
|
||
</div>
|
||
<div id="chat-messages" class="flex-1 overflow-y-auto p-4 space-y-4"></div>
|
||
<div class="p-4 border-t dark:border-gray-700">
|
||
<div class="flex space-x-2">
|
||
<input id="chat-input" type="text" placeholder="Frage stellen..." class="flex-1 px-4 py-2 rounded-lg border dark:border-gray-700 dark:bg-gray-700 dark:text-white focus:outline-none focus:ring-2 focus:ring-primary-500">
|
||
<button id="chat-send-button" class="bg-primary-600 text-white px-4 py-2 rounded-lg hover:bg-primary-700 transition-all">
|
||
<i class="fas fa-paper-plane"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
document.body.appendChild(chatContainer);
|
||
this.chatContainer = chatContainer;
|
||
|
||
// Event-Listener für Schließen-Button
|
||
const closeButton = document.getElementById('chat-close-button');
|
||
if (closeButton) {
|
||
closeButton.addEventListener('click', () => this.toggleChat());
|
||
}
|
||
}
|
||
|
||
toggleChat() {
|
||
this.isOpen = !this.isOpen;
|
||
if (this.isOpen) {
|
||
this.chatContainer.classList.remove('scale-0');
|
||
this.chatContainer.classList.add('scale-100');
|
||
} else {
|
||
this.chatContainer.classList.remove('scale-100');
|
||
this.chatContainer.classList.add('scale-0');
|
||
}
|
||
}
|
||
|
||
async sendMessage() {
|
||
const inputField = document.getElementById('chat-input');
|
||
const messageText = inputField.value.trim();
|
||
|
||
if (!messageText) return;
|
||
|
||
// Benutzer-Nachricht anzeigen
|
||
this.addMessage('user', messageText);
|
||
inputField.value = '';
|
||
|
||
// Lade-Indikator anzeigen
|
||
this.addMessage('assistant', '...', 'loading-message');
|
||
|
||
try {
|
||
// API-Anfrage senden
|
||
const response = await fetch('/api/assistant', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
messages: this.messages.map(msg => ({
|
||
role: msg.role,
|
||
content: msg.content
|
||
}))
|
||
})
|
||
});
|
||
|
||
const data = await response.json();
|
||
|
||
// Lade-Nachricht entfernen
|
||
const loadingMessage = document.getElementById('loading-message');
|
||
if (loadingMessage) {
|
||
loadingMessage.remove();
|
||
}
|
||
|
||
if (data.error) {
|
||
this.addMessage('assistant', 'Entschuldigung, es ist ein Fehler aufgetreten: ' + data.error);
|
||
} else {
|
||
this.addMessage('assistant', data.response);
|
||
}
|
||
} catch (error) {
|
||
console.error('Fehler bei der API-Anfrage:', error);
|
||
|
||
// Lade-Nachricht entfernen
|
||
const loadingMessage = document.getElementById('loading-message');
|
||
if (loadingMessage) {
|
||
loadingMessage.remove();
|
||
}
|
||
|
||
this.addMessage('assistant', 'Entschuldigung, es ist ein Fehler aufgetreten. Bitte versuche es später erneut.');
|
||
}
|
||
}
|
||
|
||
addMessage(role, content, id = null) {
|
||
const messagesContainer = document.getElementById('chat-messages');
|
||
|
||
// Nachricht zum Array hinzufügen (außer Lade-Nachrichten)
|
||
if (id !== 'loading-message') {
|
||
this.messages.push({ role, content });
|
||
}
|
||
|
||
// Nachricht zum DOM hinzufügen
|
||
const messageElement = document.createElement('div');
|
||
messageElement.className = `p-3 rounded-lg ${role === 'user' ? 'bg-primary-100 dark:bg-primary-900/30 ml-6' : 'bg-gray-100 dark:bg-gray-700 mr-6'}`;
|
||
if (id) {
|
||
messageElement.id = id;
|
||
}
|
||
|
||
messageElement.innerHTML = `
|
||
<div class="flex items-start">
|
||
<div class="w-8 h-8 rounded-full flex items-center justify-center ${role === 'user' ? 'bg-primary-600' : 'bg-gray-600'} text-white mr-2">
|
||
<i class="fas ${role === 'user' ? 'fa-user' : 'fa-robot'} text-xs"></i>
|
||
</div>
|
||
<div class="flex-1 text-sm ${role === 'user' ? 'text-gray-800 dark:text-gray-200' : 'text-gray-700 dark:text-gray-300'}">
|
||
${content}
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
messagesContainer.appendChild(messageElement);
|
||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||
}
|
||
}
|
||
|
||
// Initialisiere den ChatGPT-Assistenten direkt
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// Prüfen, ob der Assistent bereits durch MindMap initialisiert wurde
|
||
if (!window.MindMap || !window.MindMap.assistant) {
|
||
console.log('KI-Assistent wird direkt initialisiert...');
|
||
const assistant = new ChatGPTAssistant();
|
||
assistant.init();
|
||
|
||
// Speichere in window.MindMap, falls es existiert, oder erstelle es
|
||
if (!window.MindMap) {
|
||
window.MindMap = {};
|
||
}
|
||
window.MindMap.assistant = assistant;
|
||
}
|
||
});
|
||
}
|
||
</script>
|
||
|
||
<!-- Dark/Light-Mode vereinheitlicht -->
|
||
<script>
|
||
// Globaler Zugriff für externe Skripte
|
||
window.MindMap = window.MindMap || {};
|
||
|
||
// Funktion zum Anwenden des Dark Mode, strikt getrennt
|
||
function applyDarkModeClasses(isDarkMode) {
|
||
if (isDarkMode) {
|
||
document.documentElement.classList.add('dark');
|
||
document.body.classList.add('dark');
|
||
localStorage.setItem('colorMode', 'dark');
|
||
} else {
|
||
document.documentElement.classList.remove('dark');
|
||
document.body.classList.remove('dark');
|
||
localStorage.setItem('colorMode', 'light');
|
||
}
|
||
|
||
// Alpine.js darkMode-Variable aktualisieren, falls zutreffend
|
||
const appEl = document.querySelector('body');
|
||
if (appEl && appEl.__x) {
|
||
appEl.__x.$data.darkMode = isDarkMode;
|
||
}
|
||
|
||
// Event für andere Komponenten auslösen
|
||
document.dispatchEvent(new CustomEvent('darkModeToggled', {
|
||
detail: { isDark: isDarkMode }
|
||
}));
|
||
}
|
||
|
||
window.MindMap.toggleDarkMode = function() {
|
||
const isDark = document.documentElement.classList.contains('dark');
|
||
const newIsDark = !isDark;
|
||
|
||
// DOM aktualisieren
|
||
applyDarkModeClasses(newIsDark);
|
||
|
||
// Server aktualisieren
|
||
fetch('/api/set_dark_mode', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ darkMode: newIsDark })
|
||
})
|
||
.catch(console.error);
|
||
};
|
||
|
||
// Initialisierung beim Laden
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// Reihenfolge der Prüfungen: Serverseitige Einstellung > Lokale Einstellung > Browser-Präferenz
|
||
|
||
// 1. Zuerst lokale Einstellung prüfen
|
||
const storedMode = localStorage.getItem('colorMode');
|
||
if (storedMode) {
|
||
applyDarkModeClasses(storedMode === 'dark');
|
||
} else {
|
||
// 2. Fallback auf Browser-Präferenz
|
||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||
applyDarkModeClasses(prefersDark);
|
||
}
|
||
|
||
// 3. Serverseitige Einstellung abrufen und anwenden
|
||
fetch('/api/get_dark_mode')
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
const serverDarkMode = data.darkMode === true || data.darkMode === 'true';
|
||
applyDarkModeClasses(serverDarkMode);
|
||
}
|
||
})
|
||
.catch(error => console.error('Fehler beim Abrufen des Dark Mode Status:', error));
|
||
|
||
// Listener für Änderungen der Browser-Präferenz
|
||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
|
||
if (localStorage.getItem('colorMode') === null) {
|
||
applyDarkModeClasses(e.matches);
|
||
}
|
||
});
|
||
});
|
||
</script>
|
||
</body>
|
||
</html> |