Files
website/templates/base.html

817 lines
41 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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">
<!-- 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>
<!-- MindMap Visualization Module -->
<script src="{{ url_for('static', filename='js/modules/mindmap.js') }}"></script>
<!-- MindMap Page Module -->
<script src="{{ url_for('static', filename='js/modules/mindmap-page.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 -->
<!-- ► ► FarbToken strikt getrennt ◄ ◄ -->
<style>
/* LightMode */
: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(135deg, rgba(248, 250, 252, 0.9), rgba(241, 245, 249, 0.9));
background-attachment: fixed;
}
/* DarkMode */
.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(12px);
-webkit-backdrop-filter: blur(12px);
transition: all 0.3s ease;
}
.glass-navbar {
@apply glass-morphism border backdrop-blur-xl;
}
body:not(.dark) .glass-navbar {
background-color:rgba(255,255,255,.85);
border-color:rgba(0,0,0,.05);
box-shadow: 0 2px 10px 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);
background-image: linear-gradient(135deg, rgba(249, 250, 251, 0.92), rgba(243, 244, 246, 0.92));
}
body:not(.dark) .nav-link {
color: var(--text-secondary);
transition: all 0.3s ease;
border-radius: 0.5rem;
font-weight: 500;
padding: 0.5rem 0.75rem;
}
body:not(.dark) .nav-link:hover {
color: var(--text-primary);
background-color: rgba(124, 58, 237, 0.08);
transform: translateY(-1px);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.03);
}
body:not(.dark) .nav-link-active,
body:not(.dark) .nav-link-light-active {
color: var(--accent-primary);
background-color: rgba(124, 58, 237, 0.12);
font-weight: 600;
}
/* Kartendesign im Light-Mode */
body:not(.dark) .card {
background-color: rgba(255, 255, 255, 0.9);
border: 1px solid rgba(229, 231, 235, 0.6);
box-shadow: 0 4px 15px -1px rgba(0, 0, 0, 0.05), 0 2px 8px -1px rgba(0, 0, 0, 0.03);
border-radius: 12px;
transition: all 0.3s ease;
}
body:not(.dark) .card:hover {
box-shadow: 0 10px 20px -3px rgba(0, 0, 0, 0.08), 0 4px 10px -2px rgba(0, 0, 0, 0.05);
transform: translateY(-3px);
border-color: rgba(124, 58, 237, 0.2);
}
body:not(.dark) .glass-card {
background-color: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.7);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
border-radius: 16px;
transition: all 0.3s ease;
}
body:not(.dark) .glass-card:hover {
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.08);
transform: translateY(-3px);
border-color: rgba(124, 58, 237, 0.25);
}
/* Verbesserte Buttons im Light Mode */
body:not(.dark) .btn {
background: linear-gradient(135deg, #8b5cf6, #6d28d9);
color: white;
border: none;
box-shadow: 0 3px 6px rgba(91, 33, 182, 0.25);
border-radius: 10px;
padding: 0.625rem 1.25rem;
font-weight: 600;
transition: all 0.2s ease;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}
body:not(.dark) .btn:hover {
background: linear-gradient(135deg, #9f7afa, #7c3aed);
transform: translateY(-2px);
box-shadow: 0 6px 15px rgba(109, 40, 217, 0.3);
}
/* Light Mode Netzwerk-Hintergrund */
body:not(.dark) .network-background {
opacity: 0.3;
}
body:not(.dark) .network-background .node {
fill: rgba(124, 58, 237, 0.5);
}
body:not(.dark) .network-background .link {
stroke: rgba(124, 58, 237, 0.2);
stroke-width: 1.5;
}
</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();
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) {
document.dispatchEvent(new CustomEvent('darkModeToggled', {
detail: { isDark: this.darkMode }
}));
} else {
console.error('Fehler beim Speichern der Dark Mode-Einstellung:', data.error);
}
})
.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>
<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/30 to-indigo-500/30 text-gray-800 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="darkMode = !darkMode; document.documentElement.classList.toggle('dark', darkMode); localStorage.setItem('darkMode', darkMode ? 'dark' : 'light');"
class="p-2 ml-3 rounded-full flex items-center justify-center transition-all duration-300 group focus:outline-none focus:ring-2 focus:ring-indigo-400/50"
:class="darkMode ? 'bg-gray-800 hover:bg-gray-700' : 'bg-white/90 hover:bg-white shadow-md'"
aria-label="Themen-Modus wechseln">
<!-- Verbesserte Icon-Container für Light/Dark Mode Toggle -->
<div class="relative w-10 h-6 flex items-center justify-center transition-all duration-500">
<!-- Sonne (Light Mode) -->
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5 absolute transform transition-all duration-500"
:class="darkMode ? 'opacity-0 rotate-90 scale-0' : 'opacity-100 rotate-0 scale-100'"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
:style="darkMode ? '' : 'color: #6d28d9;'">
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
</svg>
<!-- Mond (Dark Mode) -->
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5 absolute transform transition-all duration-500"
:class="darkMode ? 'opacity-100 rotate-0 scale-100' : 'opacity-0 -rotate-90 scale-0'"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
style="color: #c4b5fd;">
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
</svg>
<!-- Hintergrund-Glow-Effekt -->
<div
class="absolute inset-0 rounded-full transition-all duration-300"
:class="darkMode ? 'shadow-none' : 'shadow-[0_0_10px_rgba(124,58,237,0.3)]'">
</div>
</div>
</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 %}
{{ current_user.username[0].upper() }}
{% endif %}
</div>
<span class="text-sm hidden lg:block">{{ current_user.username }}</span>
<i class="fa-solid fa-chevron-down text-xs hidden lg:block transition-transform duration-200"
x-bind:class="open ? 'transform rotate-180' : ''"></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>
<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-500/10 to-blue-400/10 text-gray-900 hover:from-purple-500/20 hover:to-blue-400/20'">
<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>
{% 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">
&copy; {{ 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 %}
<!-- KI-Chat Initialisierung -->
<script>
// Initialisiere den ChatGPT-Assistenten direkt, um sicherzustellen,
// dass er auf jeder Seite verfügbar ist, selbst wenn MindMap nicht geladen ist
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;
}
}
window.MindMap.toggleDarkMode = function() {
const isDark = document.body.classList.contains('dark');
applyDarkModeClasses(!isDark);
// Server aktualisieren
fetch('/api/set_dark_mode', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ darkMode: !isDark })
}).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 => {
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>