Remove unused background scripts and assets: Delete background.js, network-animation.js, network-background.js, and associated media files to streamline the project. Update base.html to reflect changes in script references and ensure proper functionality of the dark mode theme.
This commit is contained in:
Binary file not shown.
@@ -1,108 +0,0 @@
|
|||||||
// Background animation with Three.js
|
|
||||||
let scene, camera, renderer, stars = [];
|
|
||||||
|
|
||||||
function initBackground() {
|
|
||||||
// Setup scene
|
|
||||||
scene = new THREE.Scene();
|
|
||||||
|
|
||||||
// Setup camera
|
|
||||||
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
|
|
||||||
camera.position.z = 100;
|
|
||||||
|
|
||||||
// Setup renderer
|
|
||||||
renderer = new THREE.WebGLRenderer({ alpha: true });
|
|
||||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
||||||
renderer.setClearColor(0x000000, 0); // Transparent background
|
|
||||||
|
|
||||||
// Append renderer to DOM
|
|
||||||
const backgroundContainer = document.getElementById('background-container');
|
|
||||||
if (backgroundContainer) {
|
|
||||||
backgroundContainer.appendChild(renderer.domElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add stars
|
|
||||||
for (let i = 0; i < 1000; i++) {
|
|
||||||
const geometry = new THREE.SphereGeometry(0.2, 8, 8);
|
|
||||||
const material = new THREE.MeshBasicMaterial({ color: 0xffffff, transparent: true, opacity: Math.random() * 0.5 + 0.1 });
|
|
||||||
const star = new THREE.Mesh(geometry, material);
|
|
||||||
|
|
||||||
// Random position
|
|
||||||
star.position.x = Math.random() * 600 - 300;
|
|
||||||
star.position.y = Math.random() * 600 - 300;
|
|
||||||
star.position.z = Math.random() * 600 - 300;
|
|
||||||
|
|
||||||
// Store reference to move in animation
|
|
||||||
star.velocity = Math.random() * 0.02 + 0.005;
|
|
||||||
stars.push(star);
|
|
||||||
|
|
||||||
scene.add(star);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add large glowing particles
|
|
||||||
for (let i = 0; i < 15; i++) {
|
|
||||||
const size = Math.random() * 5 + 2;
|
|
||||||
const geometry = new THREE.SphereGeometry(size, 16, 16);
|
|
||||||
|
|
||||||
// Create a glowing material
|
|
||||||
const color = new THREE.Color();
|
|
||||||
color.setHSL(Math.random(), 0.7, 0.5); // Random hue
|
|
||||||
|
|
||||||
const material = new THREE.MeshBasicMaterial({
|
|
||||||
color: color,
|
|
||||||
transparent: true,
|
|
||||||
opacity: 0.2
|
|
||||||
});
|
|
||||||
|
|
||||||
const particle = new THREE.Mesh(geometry, material);
|
|
||||||
|
|
||||||
// Random position but further away
|
|
||||||
particle.position.x = Math.random() * 1000 - 500;
|
|
||||||
particle.position.y = Math.random() * 1000 - 500;
|
|
||||||
particle.position.z = Math.random() * 200 - 400;
|
|
||||||
|
|
||||||
// Store reference to move in animation
|
|
||||||
particle.velocity = Math.random() * 0.01 + 0.002;
|
|
||||||
stars.push(particle);
|
|
||||||
|
|
||||||
scene.add(particle);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle window resize
|
|
||||||
window.addEventListener('resize', onWindowResize);
|
|
||||||
|
|
||||||
// Start animation
|
|
||||||
animate();
|
|
||||||
}
|
|
||||||
|
|
||||||
function animate() {
|
|
||||||
requestAnimationFrame(animate);
|
|
||||||
|
|
||||||
// Move stars
|
|
||||||
stars.forEach(star => {
|
|
||||||
star.position.z += star.velocity;
|
|
||||||
|
|
||||||
// Reset position if star moves too close
|
|
||||||
if (star.position.z > 100) {
|
|
||||||
star.position.z = -300;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Rotate the entire scene slightly for a dreamy effect
|
|
||||||
scene.rotation.y += 0.0003;
|
|
||||||
scene.rotation.x += 0.0001;
|
|
||||||
|
|
||||||
renderer.render(scene, camera);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onWindowResize() {
|
|
||||||
camera.aspect = window.innerWidth / window.innerHeight;
|
|
||||||
camera.updateProjectionMatrix();
|
|
||||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize background when the DOM is loaded
|
|
||||||
if (document.readyState === 'loading') {
|
|
||||||
document.addEventListener('DOMContentLoaded', initBackground);
|
|
||||||
} else {
|
|
||||||
initBackground();
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
C:\Users\firem\Downloads\background.mp4
|
|
||||||
@@ -1,426 +1,421 @@
|
|||||||
/* Globale Variablen */
|
/* Base Styles - Dark, Mystical Theme */
|
||||||
:root {
|
|
||||||
--dark-bg: #0e1220;
|
|
||||||
--dark-card-bg: rgba(24, 28, 45, 0.8);
|
|
||||||
--dark-element-bg: rgba(24, 28, 45, 0.8);
|
|
||||||
--light-bg: #f0f4f8;
|
|
||||||
--light-card-bg: rgba(255, 255, 255, 0.85);
|
|
||||||
--accent-color: #b38fff;
|
|
||||||
--accent-gradient: linear-gradient(135deg, #b38fff, #58a9ff);
|
|
||||||
--accent-gradient-hover: linear-gradient(135deg, #c7a8ff, #70b5ff);
|
|
||||||
--blur-amount: 20px;
|
|
||||||
--border-radius: 28px;
|
|
||||||
--card-border-radius: 24px;
|
|
||||||
--button-radius: 18px;
|
|
||||||
--nav-item-radius: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dark Mode Einstellungen */
|
/* Global Variables */
|
||||||
html.dark {
|
:root {
|
||||||
color-scheme: dark;
|
--font-sans: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||||
|
--font-mono: 'JetBrains Mono', SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||||
|
|
||||||
|
/* Light Theme */
|
||||||
|
--bg-primary-light: #f8fafc;
|
||||||
|
--bg-secondary-light: #f1f5f9;
|
||||||
|
--bg-tertiary-light: #e2e8f0;
|
||||||
|
--text-primary-light: #1e293b;
|
||||||
|
--text-secondary-light: #475569;
|
||||||
|
--accent-primary-light: #7c3aed;
|
||||||
|
--accent-secondary-light: #8b5cf6;
|
||||||
|
--accent-tertiary-light: #a78bfa;
|
||||||
|
--border-light: #e2e8f0;
|
||||||
|
--shadow-light: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
--glow-light: 0 0 15px rgba(139, 92, 246, 0.3);
|
||||||
|
|
||||||
|
/* Dark Theme */
|
||||||
|
--bg-primary-dark: #0a0e19;
|
||||||
|
--bg-secondary-dark: #111827;
|
||||||
|
--bg-tertiary-dark: #1f2937;
|
||||||
|
--text-primary-dark: #f9fafb;
|
||||||
|
--text-secondary-dark: #e5e7eb;
|
||||||
|
--accent-primary-dark: #6d28d9;
|
||||||
|
--accent-secondary-dark: #8b5cf6;
|
||||||
|
--accent-tertiary-dark: #a78bfa;
|
||||||
|
--border-dark: #1f2937;
|
||||||
|
--shadow-dark: 0 4px 6px -1px rgba(0, 0, 0, 0.5), 0 2px 4px -1px rgba(0, 0, 0, 0.3);
|
||||||
|
--glow-dark: 0 0 15px rgba(124, 58, 237, 0.5);
|
||||||
|
|
||||||
|
/* Transitions */
|
||||||
|
--transition-fast: 150ms ease-in-out;
|
||||||
|
--transition-normal: 300ms ease-in-out;
|
||||||
|
--transition-slow: 500ms ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Base Styles */
|
/* Base Styles */
|
||||||
html, body {
|
html {
|
||||||
background-color: var(--dark-bg) !important;
|
font-family: var(--font-sans);
|
||||||
min-height: 100vh;
|
-webkit-font-smoothing: antialiased;
|
||||||
width: 100%;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
color: #ffffff;
|
scroll-behavior: smooth;
|
||||||
margin: 0;
|
}
|
||||||
padding: 0;
|
|
||||||
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
body {
|
||||||
|
transition: background-color var(--transition-normal), color var(--transition-normal);
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
transition: background-color 0.5s ease, color 0.5s ease;
|
min-height: 100vh;
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sicherstellen, dass der dunkle Hintergrund die gesamte Seite abdeckt */
|
/* Dark Mode */
|
||||||
#app-container, .container, main, .mx-auto, .py-12, #content-wrapper {
|
html.dark body {
|
||||||
background-color: transparent !important;
|
background-color: var(--bg-primary-dark);
|
||||||
width: 100%;
|
color: var(--text-primary-dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Light Mode Einstellungen */
|
/* Light Mode */
|
||||||
html.light, html.light body {
|
body {
|
||||||
background-color: var(--light-bg) !important;
|
background-color: var(--bg-primary-light);
|
||||||
color: #1a202c;
|
color: var(--text-primary-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Große Headings mit verbesserten Stilen */
|
/* Typography */
|
||||||
h1.hero-heading {
|
h1, h2, h3, h4, h5, h6 {
|
||||||
font-size: clamp(2.5rem, 8vw, 5rem);
|
font-weight: 600;
|
||||||
line-height: 1.1;
|
|
||||||
font-weight: 800;
|
|
||||||
letter-spacing: -0.03em;
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2.section-heading {
|
|
||||||
font-size: clamp(1.75rem, 5vw, 3rem);
|
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-heading {
|
||||||
|
font-size: 2.75rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
letter-spacing: -0.02em;
|
letter-spacing: -0.02em;
|
||||||
margin-bottom: 1.25rem;
|
line-height: 1.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Verbesserte Glasmorphismus-Stile */
|
.section-heading {
|
||||||
.glass-morphism {
|
font-size: 2rem;
|
||||||
background: var(--dark-card-bg);
|
font-weight: 700;
|
||||||
backdrop-filter: blur(var(--blur-amount));
|
letter-spacing: -0.01em;
|
||||||
-webkit-backdrop-filter: blur(var(--blur-amount));
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
||||||
border-radius: var(--card-border-radius);
|
|
||||||
box-shadow: 0 12px 36px rgba(0, 0, 0, 0.35);
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.glass-morphism:hover {
|
.gradient-text {
|
||||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
background-clip: text;
|
||||||
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.45);
|
-webkit-background-clip: text;
|
||||||
transform: translateY(-2px);
|
color: transparent;
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.glass-morphism-light {
|
html.dark .gradient-text {
|
||||||
background: var(--light-card-bg);
|
background-image: linear-gradient(135deg, var(--accent-primary-dark), var(--accent-secondary-dark));
|
||||||
backdrop-filter: blur(var(--blur-amount));
|
text-shadow: var(--glow-dark);
|
||||||
-webkit-backdrop-filter: blur(var(--blur-amount));
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
border-radius: var(--card-border-radius);
|
|
||||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.12);
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.glass-morphism-light:hover {
|
.gradient-text {
|
||||||
border: 1px solid rgba(0, 0, 0, 0.15);
|
background-image: linear-gradient(135deg, var(--accent-primary-light), var(--accent-secondary-light));
|
||||||
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.18);
|
text-shadow: var(--glow-light);
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Verbesserte Navbar-Styles */
|
/* Mystical elements */
|
||||||
.glass-navbar-dark {
|
.mystical-border {
|
||||||
background: rgba(14, 18, 32, 0.85);
|
|
||||||
backdrop-filter: blur(15px);
|
|
||||||
-webkit-backdrop-filter: blur(15px);
|
|
||||||
border-color: rgba(255, 255, 255, 0.1);
|
|
||||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.25);
|
|
||||||
border-radius: 0 0 20px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.glass-navbar-light {
|
|
||||||
background: rgba(255, 255, 255, 0.85);
|
|
||||||
backdrop-filter: blur(15px);
|
|
||||||
-webkit-backdrop-filter: blur(15px);
|
|
||||||
border-color: rgba(0, 0, 0, 0.05);
|
|
||||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08);
|
|
||||||
border-radius: 0 0 20px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Verbesserte Button-Stile mit besserer Lesbarkeit und stärkeren Farbverläufen */
|
|
||||||
.btn, button, .button, [type="button"], [type="submit"] {
|
|
||||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
border-radius: var(--button-radius);
|
|
||||||
padding: 0.75rem 1.5rem;
|
|
||||||
font-weight: 600;
|
|
||||||
letter-spacing: 0.4px;
|
|
||||||
color: rgba(255, 255, 255, 1);
|
|
||||||
background: linear-gradient(135deg, rgba(99, 102, 241, 0.8), rgba(168, 85, 247, 0.8));
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
|
||||||
box-shadow: 0 6px 14px rgba(0, 0, 0, 0.2);
|
|
||||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
|
||||||
backdrop-filter: blur(15px);
|
|
||||||
-webkit-backdrop-filter: blur(15px);
|
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
|
||||||
outline: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn:hover, button:hover, .button:hover, [type="button"]:hover, [type="submit"]:hover {
|
.mystical-border::before {
|
||||||
background: linear-gradient(135deg, rgba(129, 140, 248, 0.9), rgba(192, 132, 252, 0.9));
|
content: '';
|
||||||
transform: translateY(-3px);
|
position: absolute;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
top: 0;
|
||||||
box-shadow: 0 8px 22px rgba(0, 0, 0, 0.25), 0 0 12px rgba(179, 143, 255, 0.35);
|
left: 0;
|
||||||
color: white;
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
border: 1px solid;
|
||||||
|
border-radius: inherit;
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn:active, button:active, .button:active, [type="button"]:active, [type="submit"]:active {
|
html.dark .mystical-border::before {
|
||||||
transform: translateY(1px);
|
border-color: var(--accent-primary-dark);
|
||||||
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.2);
|
box-shadow: var(--glow-dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Navigation Stile mit verbesserten Farbverläufen */
|
.mystical-border::before {
|
||||||
|
border-color: var(--accent-primary-light);
|
||||||
|
box-shadow: var(--glow-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Navigation Links */
|
||||||
.nav-link {
|
.nav-link {
|
||||||
transition: all 0.25s ease;
|
|
||||||
border-radius: var(--nav-item-radius);
|
|
||||||
padding: 0.625rem 1rem;
|
|
||||||
font-weight: 500;
|
|
||||||
letter-spacing: 0.3px;
|
|
||||||
color: rgba(255, 255, 255, 0.9);
|
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: visible;
|
padding: 0.5rem 0.75rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
transition: var(--transition-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark .nav-link {
|
||||||
|
color: var(--text-secondary-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link {
|
||||||
|
color: var(--text-secondary-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark .nav-link:hover {
|
||||||
|
color: var(--text-primary-dark);
|
||||||
|
background-color: rgba(31, 41, 55, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link:hover {
|
.nav-link:hover {
|
||||||
background: rgba(179, 143, 255, 0.2);
|
color: var(--text-primary-light);
|
||||||
color: white;
|
background-color: rgba(241, 245, 249, 0.5);
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link-active {
|
html.dark .nav-link-active {
|
||||||
background: linear-gradient(135deg, rgba(124, 58, 237, 0.3), rgba(139, 92, 246, 0.3));
|
color: var(--accent-tertiary-dark);
|
||||||
color: white;
|
background-color: rgba(109, 40, 217, 0.15);
|
||||||
font-weight: 600;
|
|
||||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.18);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-link-active::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 10%;
|
|
||||||
width: 80%;
|
|
||||||
height: 2px;
|
|
||||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.7), transparent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Light-Mode Navigation Stile */
|
|
||||||
.nav-link-light {
|
|
||||||
color: rgba(26, 32, 44, 0.85);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-link-light:hover {
|
|
||||||
background: rgba(179, 143, 255, 0.15);
|
|
||||||
color: rgba(26, 32, 44, 1);
|
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link-light-active {
|
.nav-link-light-active {
|
||||||
background: linear-gradient(135deg, rgba(124, 58, 237, 0.2), rgba(139, 92, 246, 0.2));
|
color: var(--accent-primary-light);
|
||||||
color: rgba(26, 32, 44, 1);
|
background-color: rgba(139, 92, 246, 0.1);
|
||||||
font-weight: 600;
|
|
||||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link-light-active::after {
|
/* Glass Morphism Effects */
|
||||||
background: linear-gradient(90deg, transparent, rgba(26, 32, 44, 0.5), transparent);
|
.glass-navbar-dark {
|
||||||
|
background-color: rgba(10, 14, 25, 0.8);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
border-color: rgba(255, 255, 255, 0.1);
|
||||||
|
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Entfernung von Gradient-Hintergrund überall */
|
.glass-navbar-light {
|
||||||
.gradient-bg, .purple-gradient, .gradient-purple-bg {
|
background-color: rgba(255, 255, 255, 0.8);
|
||||||
background: var(--dark-bg) !important;
|
backdrop-filter: blur(10px);
|
||||||
background-image: none !important;
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
border-color: rgba(226, 232, 240, 0.5);
|
||||||
|
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Verbesserte Light-Mode-Stile für Buttons */
|
.glass-morphism {
|
||||||
html.light .btn, html.light button, html.light .button,
|
transition: background-color var(--transition-normal), backdrop-filter var(--transition-normal);
|
||||||
html.light [type="button"], html.light [type="submit"] {
|
|
||||||
background: linear-gradient(135deg, rgba(124, 58, 237, 0.7), rgba(139, 92, 246, 0.7));
|
|
||||||
color: #ffffff;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
|
||||||
text-shadow: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
html.light .btn:hover, html.light button:hover, html.light .button:hover,
|
/* Cards */
|
||||||
html.light [type="button"]:hover, html.light [type="submit"]:hover {
|
.mystical-card {
|
||||||
background: linear-gradient(135deg, rgba(139, 92, 246, 0.85), rgba(168, 85, 247, 0.85));
|
border-radius: 0.75rem;
|
||||||
color: #ffffff;
|
overflow: hidden;
|
||||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
transition: var(--transition-normal);
|
||||||
box-shadow: 0 8px 22px rgba(0, 0, 0, 0.12), 0 0 12px rgba(179, 143, 255, 0.2);
|
|
||||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Verbesserte Buttons mit Glasmorphismus */
|
html.dark .mystical-card {
|
||||||
.btn-primary {
|
background-color: var(--bg-secondary-dark);
|
||||||
background: linear-gradient(135deg, rgba(179, 143, 255, 0.8), rgba(88, 169, 255, 0.8));
|
border: 1px solid var(--border-dark);
|
||||||
backdrop-filter: blur(var(--blur-amount));
|
box-shadow: var(--shadow-dark);
|
||||||
-webkit-backdrop-filter: blur(var(--blur-amount));
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
||||||
border-radius: var(--button-radius);
|
|
||||||
color: white !important;
|
|
||||||
font-weight: 600;
|
|
||||||
padding: 0.75rem 1.5rem;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-primary:hover {
|
.mystical-card {
|
||||||
transform: translateY(-3px);
|
background-color: var(--bg-secondary-light);
|
||||||
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35);
|
border: 1px solid var(--border-light);
|
||||||
background: linear-gradient(135deg, rgba(190, 160, 255, 0.9), rgba(100, 180, 255, 0.9));
|
box-shadow: var(--shadow-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-secondary {
|
html.dark .mystical-card:hover {
|
||||||
background: rgba(32, 36, 55, 0.8);
|
box-shadow: var(--glow-dark), var(--shadow-dark);
|
||||||
backdrop-filter: blur(var(--blur-amount));
|
border-color: var(--accent-primary-dark);
|
||||||
-webkit-backdrop-filter: blur(var(--blur-amount));
|
}
|
||||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
||||||
border-radius: var(--button-radius);
|
.mystical-card:hover {
|
||||||
color: white;
|
box-shadow: var(--glow-light), var(--shadow-light);
|
||||||
|
border-color: var(--accent-primary-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buttons */
|
||||||
|
.mystical-button {
|
||||||
|
padding: 0.625rem 1.25rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
padding: 0.75rem 1.5rem;
|
transition: var(--transition-normal);
|
||||||
transition: all 0.3s ease;
|
position: relative;
|
||||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-secondary:hover {
|
.mystical-button::before {
|
||||||
transform: translateY(-3px);
|
content: '';
|
||||||
box-shadow: 0 10px 28px rgba(0, 0, 0, 0.3);
|
position: absolute;
|
||||||
background: rgba(38, 42, 65, 0.9);
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Steuerungsbutton-Stil */
|
|
||||||
.control-btn {
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
background: rgba(32, 36, 55, 0.8);
|
|
||||||
color: rgba(255, 255, 255, 0.9);
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
||||||
border-radius: 14px;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
font-weight: 500;
|
|
||||||
transition: all 0.25s ease;
|
|
||||||
backdrop-filter: blur(15px);
|
|
||||||
-webkit-backdrop-filter: blur(15px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.control-btn:hover {
|
|
||||||
background: rgba(38, 42, 65, 0.9);
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.25);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Verbesserter Farbverlauf-Text */
|
|
||||||
.gradient-text {
|
|
||||||
background: linear-gradient(135deg, rgba(200, 170, 255, 1), rgba(100, 180, 255, 1));
|
|
||||||
-webkit-background-clip: text;
|
|
||||||
background-clip: text;
|
|
||||||
color: transparent;
|
|
||||||
font-weight: 700;
|
|
||||||
letter-spacing: -0.02em;
|
|
||||||
text-shadow: 0 2px 10px rgba(179, 143, 255, 0.3);
|
|
||||||
filter: drop-shadow(0 2px 6px rgba(179, 143, 255, 0.3));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Globaler Hintergrund */
|
|
||||||
.full-page-bg {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100vw;
|
width: 100%;
|
||||||
height: 100vh;
|
height: 100%;
|
||||||
background-color: var(--dark-bg);
|
background: linear-gradient(135deg, transparent, rgba(255, 255, 255, 0.05), transparent);
|
||||||
z-index: -10;
|
transform: translateX(-100%);
|
||||||
|
transition: transform 0.8s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
html.light .full-page-bg {
|
.mystical-button:hover::before {
|
||||||
background-color: var(--light-bg);
|
transform: translateX(100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Animationen für Hintergrundeffekte */
|
html.dark .mystical-button-primary {
|
||||||
@keyframes float {
|
background-color: var(--accent-primary-dark);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mystical-button-primary {
|
||||||
|
background-color: var(--accent-primary-light);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark .mystical-button-primary:hover {
|
||||||
|
background-color: var(--accent-secondary-dark);
|
||||||
|
box-shadow: var(--glow-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mystical-button-primary:hover {
|
||||||
|
background-color: var(--accent-secondary-light);
|
||||||
|
box-shadow: var(--glow-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark .mystical-button-secondary {
|
||||||
|
background-color: var(--bg-tertiary-dark);
|
||||||
|
color: var(--text-primary-dark);
|
||||||
|
border: 1px solid var(--border-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mystical-button-secondary {
|
||||||
|
background-color: var(--bg-tertiary-light);
|
||||||
|
color: var(--text-primary-light);
|
||||||
|
border: 1px solid var(--border-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark .mystical-button-secondary:hover {
|
||||||
|
background-color: var(--bg-secondary-dark);
|
||||||
|
border-color: var(--accent-tertiary-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mystical-button-secondary:hover {
|
||||||
|
background-color: var(--bg-secondary-light);
|
||||||
|
border-color: var(--accent-tertiary-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inputs */
|
||||||
|
.mystical-input {
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
transition: var(--transition-normal);
|
||||||
|
width: 100%;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark .mystical-input {
|
||||||
|
background-color: var(--bg-tertiary-dark);
|
||||||
|
border: 1px solid var(--border-dark);
|
||||||
|
color: var(--text-primary-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mystical-input {
|
||||||
|
background-color: var(--bg-tertiary-light);
|
||||||
|
border: 1px solid var(--border-light);
|
||||||
|
color: var(--text-primary-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark .mystical-input:focus {
|
||||||
|
border-color: var(--accent-tertiary-dark);
|
||||||
|
box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mystical-input:focus {
|
||||||
|
border-color: var(--accent-tertiary-light);
|
||||||
|
box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animations */
|
||||||
|
@keyframes floatAnimation {
|
||||||
0% { transform: translateY(0); }
|
0% { transform: translateY(0); }
|
||||||
50% { transform: translateY(-12px); }
|
50% { transform: translateY(-5px); }
|
||||||
100% { transform: translateY(0); }
|
100% { transform: translateY(0); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes pulse {
|
|
||||||
0% { opacity: 0.7; transform: scale(1); }
|
|
||||||
50% { opacity: 1; transform: scale(1.05); }
|
|
||||||
100% { opacity: 0.7; transform: scale(1); }
|
|
||||||
}
|
|
||||||
|
|
||||||
.animate-float {
|
.animate-float {
|
||||||
animation: float 6s ease-in-out infinite;
|
animation: floatAnimation 3s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.animate-pulse {
|
@keyframes fadeIn {
|
||||||
animation: pulse 3s ease-in-out infinite;
|
from { opacity: 0; }
|
||||||
|
to { opacity: 1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Verbesserter Container für konsistente Layouts */
|
.animate-fade-in {
|
||||||
.page-container {
|
animation: fadeIn 0.5s ease-out forwards;
|
||||||
max-width: 1200px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 2rem 1rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dark Mode Toggle Stile */
|
/* Scroll Bars */
|
||||||
.dot {
|
::-webkit-scrollbar {
|
||||||
transform: translateX(0);
|
width: 0.5rem;
|
||||||
transition: transform 0.3s ease-in-out, background-color 0.3s ease;
|
height: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
input:checked ~ .dot {
|
html.dark ::-webkit-scrollbar-track {
|
||||||
transform: translateX(100%);
|
background: var(--bg-secondary-dark);
|
||||||
background-color: #58a9ff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input:checked ~ .block {
|
::-webkit-scrollbar-track {
|
||||||
background-color: rgba(88, 169, 255, 0.4);
|
background: var(--bg-secondary-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Feature Cards mit Glasmorphismus und Farbverlauf */
|
html.dark ::-webkit-scrollbar-thumb {
|
||||||
.feature-card {
|
background: var(--accent-primary-dark);
|
||||||
border-radius: var(--card-border-radius);
|
border-radius: 0.25rem;
|
||||||
padding: 2rem;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
background: linear-gradient(145deg, rgba(32, 36, 55, 0.7), rgba(24, 28, 45, 0.9));
|
|
||||||
backdrop-filter: blur(var(--blur-amount));
|
|
||||||
-webkit-backdrop-filter: blur(var(--blur-amount));
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
||||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.feature-card:hover {
|
::-webkit-scrollbar-thumb {
|
||||||
transform: translateY(-5px);
|
background: var(--accent-primary-light);
|
||||||
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3);
|
border-radius: 0.25rem;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
|
||||||
background: linear-gradient(145deg, rgba(40, 44, 65, 0.8), rgba(28, 32, 50, 0.95));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
html.light .feature-card {
|
html.dark ::-webkit-scrollbar-thumb:hover {
|
||||||
background: linear-gradient(145deg, rgba(255, 255, 255, 0.8), rgba(240, 240, 250, 0.9));
|
background: var(--accent-secondary-dark);
|
||||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
|
||||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
html.light .feature-card:hover {
|
::-webkit-scrollbar-thumb:hover {
|
||||||
background: linear-gradient(145deg, rgba(255, 255, 255, 0.9), rgba(245, 245, 255, 0.95));
|
background: var(--accent-secondary-light);
|
||||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.12);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.feature-card .icon {
|
/* Responsive Utilities */
|
||||||
width: 60px;
|
@media (max-width: 640px) {
|
||||||
height: 60px;
|
.hero-heading {
|
||||||
border-radius: 18px;
|
font-size: 2rem;
|
||||||
display: flex;
|
}
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
.section-heading {
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
margin-bottom: 1.25rem;
|
}
|
||||||
background: linear-gradient(135deg, rgba(124, 58, 237, 0.8), rgba(139, 92, 246, 0.6));
|
|
||||||
color: white;
|
|
||||||
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.feature-card h3 {
|
/* Additional background elements */
|
||||||
font-size: 1.25rem;
|
.mystical-dot {
|
||||||
font-weight: 600;
|
position: absolute;
|
||||||
margin-bottom: 0.75rem;
|
border-radius: 50%;
|
||||||
color: rgba(255, 255, 255, 0.95);
|
opacity: 0.15;
|
||||||
|
filter: blur(3px);
|
||||||
|
z-index: -1;
|
||||||
|
transition: opacity var(--transition-normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
html.light .feature-card h3 {
|
html.dark .mystical-dot {
|
||||||
color: rgba(26, 32, 44, 0.95);
|
background-color: var(--accent-primary-dark);
|
||||||
|
box-shadow: 0 0 15px var(--accent-primary-dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
.feature-card p {
|
.mystical-dot {
|
||||||
color: rgba(255, 255, 255, 0.75);
|
background-color: var(--accent-primary-light);
|
||||||
line-height: 1.6;
|
box-shadow: 0 0 15px var(--accent-primary-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
html.light .feature-card p {
|
/* Accessibility */
|
||||||
color: rgba(26, 32, 44, 0.75);
|
.sr-only {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
padding: 0;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
white-space: nowrap;
|
||||||
|
border-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Focus styles for keyboard navigation */
|
||||||
|
:focus-visible {
|
||||||
|
outline: 2px solid var(--accent-primary-light);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark :focus-visible {
|
||||||
|
outline-color: var(--accent-primary-dark);
|
||||||
}
|
}
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
// Network Animation Effect
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
// Check if we're on the mindmap page
|
|
||||||
const mindmapContainer = document.getElementById('mindmap-container');
|
|
||||||
if (!mindmapContainer) return;
|
|
||||||
|
|
||||||
// Add enhanced animations for links and nodes
|
|
||||||
setTimeout(function() {
|
|
||||||
// Get all SVG links (connections between nodes)
|
|
||||||
const links = document.querySelectorAll('.link');
|
|
||||||
const nodes = document.querySelectorAll('.node');
|
|
||||||
|
|
||||||
// Add animation to links
|
|
||||||
links.forEach(link => {
|
|
||||||
// Create random animation duration between 15 and 30 seconds
|
|
||||||
const duration = 15 + Math.random() * 15;
|
|
||||||
link.style.animation = `dash ${duration}s linear infinite`;
|
|
||||||
link.style.strokeDasharray = '5, 5';
|
|
||||||
|
|
||||||
// Add pulse effect on hover
|
|
||||||
link.addEventListener('mouseover', function() {
|
|
||||||
this.classList.add('highlighted');
|
|
||||||
this.style.animation = 'dash 5s linear infinite';
|
|
||||||
});
|
|
||||||
|
|
||||||
link.addEventListener('mouseout', function() {
|
|
||||||
this.classList.remove('highlighted');
|
|
||||||
this.style.animation = `dash ${duration}s linear infinite`;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add effects to nodes
|
|
||||||
nodes.forEach(node => {
|
|
||||||
node.addEventListener('mouseover', function() {
|
|
||||||
this.querySelector('circle').style.filter = 'drop-shadow(0 0 15px rgba(179, 143, 255, 0.8))';
|
|
||||||
|
|
||||||
// Highlight connected links
|
|
||||||
const nodeId = this.getAttribute('data-id') || this.id;
|
|
||||||
links.forEach(link => {
|
|
||||||
const source = link.getAttribute('data-source');
|
|
||||||
const target = link.getAttribute('data-target');
|
|
||||||
|
|
||||||
if (source === nodeId || target === nodeId) {
|
|
||||||
link.classList.add('highlighted');
|
|
||||||
link.style.animation = 'dash 5s linear infinite';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
node.addEventListener('mouseout', function() {
|
|
||||||
this.querySelector('circle').style.filter = 'drop-shadow(0 0 8px rgba(179, 143, 255, 0.5))';
|
|
||||||
|
|
||||||
// Remove highlight from connected links
|
|
||||||
const nodeId = this.getAttribute('data-id') || this.id;
|
|
||||||
links.forEach(link => {
|
|
||||||
const source = link.getAttribute('data-source');
|
|
||||||
const target = link.getAttribute('data-target');
|
|
||||||
|
|
||||||
if (source === nodeId || target === nodeId) {
|
|
||||||
link.classList.remove('highlighted');
|
|
||||||
const duration = 15 + Math.random() * 15;
|
|
||||||
link.style.animation = `dash ${duration}s linear infinite`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}, 1000); // Wait for the mindmap to be fully loaded
|
|
||||||
|
|
||||||
// Add network background effect
|
|
||||||
const networkBackground = document.createElement('div');
|
|
||||||
networkBackground.className = 'network-background';
|
|
||||||
networkBackground.style.cssText = `
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: rgba(179, 143, 255, 0.05);
|
|
||||||
background-size: cover;
|
|
||||||
background-position: center;
|
|
||||||
opacity: 0.15;
|
|
||||||
z-index: -1;
|
|
||||||
pointer-events: none;
|
|
||||||
animation: pulse 10s ease-in-out infinite alternate;
|
|
||||||
`;
|
|
||||||
|
|
||||||
mindmapContainer.appendChild(networkBackground);
|
|
||||||
});
|
|
||||||
@@ -1,232 +0,0 @@
|
|||||||
// Animated Network Background
|
|
||||||
let canvas, ctx, networkImage;
|
|
||||||
let isImageLoaded = false;
|
|
||||||
let animationSpeed = 0.0003; // Reduzierte Geschwindigkeit für sanftere Rotation
|
|
||||||
let scaleSpeed = 0.0001; // Reduzierte Geschwindigkeit für sanftere Skalierung
|
|
||||||
let opacitySpeed = 0.0002; // Reduzierte Geschwindigkeit für sanftere Opazitätsänderung
|
|
||||||
let rotation = 0;
|
|
||||||
let scale = 1;
|
|
||||||
let opacity = 0.7; // Höhere Basisopazität für bessere Sichtbarkeit
|
|
||||||
let scaleDirection = 1;
|
|
||||||
let opacityDirection = 1;
|
|
||||||
let animationFrameId = null;
|
|
||||||
let isDarkMode = document.documentElement.classList.contains('dark');
|
|
||||||
let loadAttempts = 0;
|
|
||||||
const MAX_LOAD_ATTEMPTS = 2;
|
|
||||||
|
|
||||||
// Initialize the canvas and load the image
|
|
||||||
function initNetworkBackground() {
|
|
||||||
// Create canvas element if it doesn't exist
|
|
||||||
if (!document.getElementById('network-background')) {
|
|
||||||
canvas = document.createElement('canvas');
|
|
||||||
canvas.id = 'network-background';
|
|
||||||
canvas.style.position = 'fixed';
|
|
||||||
canvas.style.top = '0';
|
|
||||||
canvas.style.left = '0';
|
|
||||||
canvas.style.width = '100%';
|
|
||||||
canvas.style.height = '100%';
|
|
||||||
canvas.style.zIndex = '-5'; // Höher als -10 für den full-page-bg
|
|
||||||
canvas.style.pointerEvents = 'none'; // Stellt sicher, dass der Canvas keine Mausinteraktionen blockiert
|
|
||||||
document.body.appendChild(canvas);
|
|
||||||
} else {
|
|
||||||
canvas = document.getElementById('network-background');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set canvas size to window size with pixel ratio consideration
|
|
||||||
resizeCanvas();
|
|
||||||
|
|
||||||
// Get context with alpha enabled
|
|
||||||
ctx = canvas.getContext('2d', { alpha: true });
|
|
||||||
|
|
||||||
// Load the network image - versuche zuerst die SVG-Version
|
|
||||||
networkImage = new Image();
|
|
||||||
networkImage.crossOrigin = "anonymous"; // Vermeidet CORS-Probleme
|
|
||||||
|
|
||||||
// Keine Bilder laden, direkt Fallback-Hintergrund verwenden
|
|
||||||
console.log("Verwende einfachen Hintergrund ohne Bilddateien");
|
|
||||||
isImageLoaded = true; // Animation ohne Hintergrundbild starten
|
|
||||||
startAnimation();
|
|
||||||
|
|
||||||
// Handle window resize
|
|
||||||
window.addEventListener('resize', debounce(resizeCanvas, 250));
|
|
||||||
|
|
||||||
// Überwache Dark Mode-Änderungen
|
|
||||||
document.addEventListener('darkModeToggled', function(event) {
|
|
||||||
isDarkMode = event.detail.isDark;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hilfsfunktion zur Reduzierung der Resize-Event-Aufrufe
|
|
||||||
function debounce(func, wait) {
|
|
||||||
let timeout;
|
|
||||||
return function() {
|
|
||||||
const context = this;
|
|
||||||
const args = arguments;
|
|
||||||
clearTimeout(timeout);
|
|
||||||
timeout = setTimeout(function() {
|
|
||||||
func.apply(context, args);
|
|
||||||
}, wait);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resize canvas to match window size with proper pixel ratio
|
|
||||||
function resizeCanvas() {
|
|
||||||
if (!canvas) return;
|
|
||||||
|
|
||||||
const pixelRatio = window.devicePixelRatio || 1;
|
|
||||||
const width = window.innerWidth;
|
|
||||||
const height = window.innerHeight;
|
|
||||||
|
|
||||||
// Set display size (css pixels)
|
|
||||||
canvas.style.width = width + 'px';
|
|
||||||
canvas.style.height = height + 'px';
|
|
||||||
|
|
||||||
// Set actual size in memory (scaled for pixel ratio)
|
|
||||||
canvas.width = width * pixelRatio;
|
|
||||||
canvas.height = height * pixelRatio;
|
|
||||||
|
|
||||||
// Scale context to match pixel ratio
|
|
||||||
if (ctx) {
|
|
||||||
ctx.scale(pixelRatio, pixelRatio);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wenn Animation läuft und Bild geladen, zeichne erneut
|
|
||||||
if (isImageLoaded && animationFrameId) {
|
|
||||||
drawNetworkImage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start animation
|
|
||||||
function startAnimation() {
|
|
||||||
if (animationFrameId) {
|
|
||||||
cancelAnimationFrame(animationFrameId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start animation loop
|
|
||||||
animate();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw network image
|
|
||||||
function drawNetworkImage() {
|
|
||||||
if (!ctx) return;
|
|
||||||
|
|
||||||
// Clear canvas with proper clear method
|
|
||||||
ctx.clearRect(0, 0, canvas.width / (window.devicePixelRatio || 1), canvas.height / (window.devicePixelRatio || 1));
|
|
||||||
|
|
||||||
// Save context state
|
|
||||||
ctx.save();
|
|
||||||
|
|
||||||
// Move to center of canvas
|
|
||||||
ctx.translate(canvas.width / (2 * (window.devicePixelRatio || 1)), canvas.height / (2 * (window.devicePixelRatio || 1)));
|
|
||||||
|
|
||||||
// Rotate
|
|
||||||
ctx.rotate(rotation);
|
|
||||||
|
|
||||||
// Scale
|
|
||||||
ctx.scale(scale, scale);
|
|
||||||
|
|
||||||
// Set global opacity, angepasst für Dark Mode
|
|
||||||
ctx.globalAlpha = isDarkMode ? opacity : opacity * 0.8;
|
|
||||||
|
|
||||||
if (isImageLoaded && networkImage.complete) {
|
|
||||||
// Bildgröße berechnen, um den Bildschirm abzudecken
|
|
||||||
const imgAspect = networkImage.width / networkImage.height;
|
|
||||||
const canvasAspect = canvas.width / canvas.height;
|
|
||||||
|
|
||||||
let drawWidth, drawHeight;
|
|
||||||
|
|
||||||
if (canvasAspect > imgAspect) {
|
|
||||||
drawWidth = canvas.width / (window.devicePixelRatio || 1);
|
|
||||||
drawHeight = drawWidth / imgAspect;
|
|
||||||
} else {
|
|
||||||
drawHeight = canvas.height / (window.devicePixelRatio || 1);
|
|
||||||
drawWidth = drawHeight * imgAspect;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw image centered
|
|
||||||
ctx.drawImage(
|
|
||||||
networkImage,
|
|
||||||
-drawWidth / 2,
|
|
||||||
-drawHeight / 2,
|
|
||||||
drawWidth,
|
|
||||||
drawHeight
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Fallback: Zeichne einen einfachen Hintergrund mit Punkten
|
|
||||||
drawFallbackBackground();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore context state
|
|
||||||
ctx.restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback-Hintergrund mit Punkten und Linien
|
|
||||||
function drawFallbackBackground() {
|
|
||||||
const width = canvas.width / (window.devicePixelRatio || 1);
|
|
||||||
const height = canvas.height / (window.devicePixelRatio || 1);
|
|
||||||
|
|
||||||
// Zeichne einige zufällige Punkte
|
|
||||||
ctx.fillStyle = isDarkMode ? 'rgba(139, 92, 246, 0.2)' : 'rgba(139, 92, 246, 0.1)';
|
|
||||||
|
|
||||||
for (let i = 0; i < 50; i++) {
|
|
||||||
const x = Math.random() * width;
|
|
||||||
const y = Math.random() * height;
|
|
||||||
const radius = Math.random() * 3 + 1;
|
|
||||||
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(x - width/2, y - height/2, radius, 0, Math.PI * 2);
|
|
||||||
ctx.fill();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Animation loop
|
|
||||||
function animate() {
|
|
||||||
// Update animation parameters
|
|
||||||
rotation += animationSpeed;
|
|
||||||
|
|
||||||
// Update scale with oscillation
|
|
||||||
scale += scaleSpeed * scaleDirection;
|
|
||||||
if (scale > 1.05) { // Kleinerer Skalierungsbereich für weniger starke Größenänderung
|
|
||||||
scaleDirection = -1;
|
|
||||||
} else if (scale < 0.95) {
|
|
||||||
scaleDirection = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update opacity with oscillation
|
|
||||||
opacity += opacitySpeed * opacityDirection;
|
|
||||||
if (opacity > 0.75) { // Kleinerer Opazitätsbereich für subtilere Änderungen
|
|
||||||
opacityDirection = -1;
|
|
||||||
} else if (opacity < 0.65) {
|
|
||||||
opacityDirection = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw the image
|
|
||||||
drawNetworkImage();
|
|
||||||
|
|
||||||
// Request next frame
|
|
||||||
animationFrameId = requestAnimationFrame(animate);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup Funktion für Speicherbereinigung
|
|
||||||
function cleanupNetworkBackground() {
|
|
||||||
if (animationFrameId) {
|
|
||||||
cancelAnimationFrame(animationFrameId);
|
|
||||||
animationFrameId = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canvas && canvas.parentNode) {
|
|
||||||
canvas.parentNode.removeChild(canvas);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.removeEventListener('resize', resizeCanvas);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Führe Initialisierung aus, wenn DOM geladen ist
|
|
||||||
if (document.readyState === 'loading') {
|
|
||||||
document.addEventListener('DOMContentLoaded', initNetworkBackground);
|
|
||||||
} else {
|
|
||||||
initNetworkBackground();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Führe Cleanup durch, wenn das Fenster geschlossen wird
|
|
||||||
window.addEventListener('beforeunload', cleanupNetworkBackground);
|
|
||||||
566
website/static/neural-network-background.js
Normal file
566
website/static/neural-network-background.js
Normal file
@@ -0,0 +1,566 @@
|
|||||||
|
/**
|
||||||
|
* Neural Network Background Animation
|
||||||
|
* Modern, darker, mystical theme using WebGL
|
||||||
|
*/
|
||||||
|
|
||||||
|
class NeuralNetworkBackground {
|
||||||
|
constructor() {
|
||||||
|
// Canvas setup
|
||||||
|
this.canvas = document.createElement('canvas');
|
||||||
|
this.canvas.id = 'neural-network-background';
|
||||||
|
this.canvas.style.position = 'fixed';
|
||||||
|
this.canvas.style.top = '0';
|
||||||
|
this.canvas.style.left = '0';
|
||||||
|
this.canvas.style.width = '100%';
|
||||||
|
this.canvas.style.height = '100%';
|
||||||
|
this.canvas.style.zIndex = '-5';
|
||||||
|
this.canvas.style.pointerEvents = 'none';
|
||||||
|
|
||||||
|
// Append to body
|
||||||
|
document.body.appendChild(this.canvas);
|
||||||
|
|
||||||
|
// WebGL context
|
||||||
|
this.gl = this.canvas.getContext('webgl') || this.canvas.getContext('experimental-webgl');
|
||||||
|
if (!this.gl) {
|
||||||
|
console.warn('WebGL not supported, falling back to canvas rendering');
|
||||||
|
this.gl = null;
|
||||||
|
this.ctx = this.canvas.getContext('2d');
|
||||||
|
this.useWebGL = false;
|
||||||
|
} else {
|
||||||
|
this.useWebGL = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animation properties
|
||||||
|
this.nodes = [];
|
||||||
|
this.connections = [];
|
||||||
|
this.animationFrameId = null;
|
||||||
|
this.isDarkMode = document.documentElement.classList.contains('dark');
|
||||||
|
|
||||||
|
// Colors - using hex values for better control
|
||||||
|
this.darkModeColors = {
|
||||||
|
background: '#0a0e19',
|
||||||
|
nodeColor: '#6d28d9',
|
||||||
|
nodePulse: '#8b5cf6',
|
||||||
|
connectionColor: '#4c1d95',
|
||||||
|
glowColor: '#7c3aed'
|
||||||
|
};
|
||||||
|
|
||||||
|
this.lightModeColors = {
|
||||||
|
background: '#f9fafb',
|
||||||
|
nodeColor: '#7c3aed',
|
||||||
|
nodePulse: '#8b5cf6',
|
||||||
|
connectionColor: '#a78bfa',
|
||||||
|
glowColor: '#c4b5fd'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Config
|
||||||
|
this.config = {
|
||||||
|
nodeCount: 100,
|
||||||
|
nodeSize: 2,
|
||||||
|
nodeVariation: 1.5,
|
||||||
|
connectionDistance: 150,
|
||||||
|
connectionOpacity: 0.2,
|
||||||
|
animationSpeed: 0.3,
|
||||||
|
pulseSpeed: 0.02
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
this.init();
|
||||||
|
|
||||||
|
// Event listeners
|
||||||
|
window.addEventListener('resize', this.resizeCanvas.bind(this));
|
||||||
|
document.addEventListener('darkModeToggled', (event) => {
|
||||||
|
this.isDarkMode = event.detail.isDark;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.resizeCanvas();
|
||||||
|
|
||||||
|
if (this.useWebGL) {
|
||||||
|
this.initWebGL();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.createNodes();
|
||||||
|
this.createConnections();
|
||||||
|
this.startAnimation();
|
||||||
|
}
|
||||||
|
|
||||||
|
resizeCanvas() {
|
||||||
|
const pixelRatio = window.devicePixelRatio || 1;
|
||||||
|
const width = window.innerWidth;
|
||||||
|
const height = window.innerHeight;
|
||||||
|
|
||||||
|
this.canvas.style.width = width + 'px';
|
||||||
|
this.canvas.style.height = height + 'px';
|
||||||
|
this.canvas.width = width * pixelRatio;
|
||||||
|
this.canvas.height = height * pixelRatio;
|
||||||
|
|
||||||
|
if (this.useWebGL) {
|
||||||
|
this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);
|
||||||
|
} else if (this.ctx) {
|
||||||
|
this.ctx.scale(pixelRatio, pixelRatio);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recalculate node positions after resize
|
||||||
|
if (this.nodes.length) {
|
||||||
|
this.createNodes();
|
||||||
|
this.createConnections();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initWebGL() {
|
||||||
|
// Vertex shader
|
||||||
|
const vsSource = `
|
||||||
|
attribute vec2 aVertexPosition;
|
||||||
|
attribute float aPointSize;
|
||||||
|
uniform vec2 uResolution;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// Convert from pixel to clip space
|
||||||
|
vec2 position = (aVertexPosition / uResolution) * 2.0 - 1.0;
|
||||||
|
// Flip Y coordinate
|
||||||
|
position.y = -position.y;
|
||||||
|
|
||||||
|
gl_Position = vec4(position, 0, 1);
|
||||||
|
gl_PointSize = aPointSize;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Fragment shader
|
||||||
|
const fsSource = `
|
||||||
|
precision mediump float;
|
||||||
|
uniform vec4 uColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float distance = length(gl_PointCoord - vec2(0.5, 0.5));
|
||||||
|
|
||||||
|
// Soft circle with glow
|
||||||
|
float alpha = 1.0 - smoothstep(0.3, 0.5, distance);
|
||||||
|
|
||||||
|
// Add glow
|
||||||
|
if (distance > 0.3) {
|
||||||
|
alpha *= 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
gl_FragColor = vec4(uColor.rgb, uColor.a * alpha);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Initialize shaders
|
||||||
|
const vertexShader = this.loadShader(this.gl.VERTEX_SHADER, vsSource);
|
||||||
|
const fragmentShader = this.loadShader(this.gl.FRAGMENT_SHADER, fsSource);
|
||||||
|
|
||||||
|
// Create shader program
|
||||||
|
this.shaderProgram = this.gl.createProgram();
|
||||||
|
this.gl.attachShader(this.shaderProgram, vertexShader);
|
||||||
|
this.gl.attachShader(this.shaderProgram, fragmentShader);
|
||||||
|
this.gl.linkProgram(this.shaderProgram);
|
||||||
|
|
||||||
|
if (!this.gl.getProgramParameter(this.shaderProgram, this.gl.LINK_STATUS)) {
|
||||||
|
console.error('Unable to initialize the shader program: ' +
|
||||||
|
this.gl.getProgramInfoLog(this.shaderProgram));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get attribute and uniform locations
|
||||||
|
this.programInfo = {
|
||||||
|
program: this.shaderProgram,
|
||||||
|
attribLocations: {
|
||||||
|
vertexPosition: this.gl.getAttribLocation(this.shaderProgram, 'aVertexPosition'),
|
||||||
|
pointSize: this.gl.getAttribLocation(this.shaderProgram, 'aPointSize')
|
||||||
|
},
|
||||||
|
uniformLocations: {
|
||||||
|
resolution: this.gl.getUniformLocation(this.shaderProgram, 'uResolution'),
|
||||||
|
color: this.gl.getUniformLocation(this.shaderProgram, 'uColor')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create buffers
|
||||||
|
this.positionBuffer = this.gl.createBuffer();
|
||||||
|
this.sizeBuffer = this.gl.createBuffer();
|
||||||
|
|
||||||
|
// Set clear color for WebGL context
|
||||||
|
const bgColor = this.isDarkMode
|
||||||
|
? this.hexToRgb(this.darkModeColors.background)
|
||||||
|
: this.hexToRgb(this.lightModeColors.background);
|
||||||
|
|
||||||
|
this.gl.clearColor(bgColor.r/255, bgColor.g/255, bgColor.b/255, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadShader(type, source) {
|
||||||
|
const shader = this.gl.createShader(type);
|
||||||
|
this.gl.shaderSource(shader, source);
|
||||||
|
this.gl.compileShader(shader);
|
||||||
|
|
||||||
|
if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
|
||||||
|
console.error('An error occurred compiling the shaders: ' +
|
||||||
|
this.gl.getShaderInfoLog(shader));
|
||||||
|
this.gl.deleteShader(shader);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
createNodes() {
|
||||||
|
this.nodes = [];
|
||||||
|
const width = this.canvas.width / (window.devicePixelRatio || 1);
|
||||||
|
const height = this.canvas.height / (window.devicePixelRatio || 1);
|
||||||
|
|
||||||
|
// Create nodes with random positions and properties
|
||||||
|
for (let i = 0; i < this.config.nodeCount; i++) {
|
||||||
|
const node = {
|
||||||
|
x: Math.random() * width,
|
||||||
|
y: Math.random() * height,
|
||||||
|
size: this.config.nodeSize + Math.random() * this.config.nodeVariation,
|
||||||
|
speed: {
|
||||||
|
x: (Math.random() - 0.5) * this.config.animationSpeed,
|
||||||
|
y: (Math.random() - 0.5) * this.config.animationSpeed
|
||||||
|
},
|
||||||
|
pulsePhase: Math.random() * Math.PI * 2, // Random starting phase
|
||||||
|
connections: []
|
||||||
|
};
|
||||||
|
|
||||||
|
this.nodes.push(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createConnections() {
|
||||||
|
this.connections = [];
|
||||||
|
|
||||||
|
// Create connections between nearby nodes
|
||||||
|
for (let i = 0; i < this.nodes.length; i++) {
|
||||||
|
const nodeA = this.nodes[i];
|
||||||
|
nodeA.connections = [];
|
||||||
|
|
||||||
|
for (let j = i + 1; j < this.nodes.length; j++) {
|
||||||
|
const nodeB = this.nodes[j];
|
||||||
|
const dx = nodeB.x - nodeA.x;
|
||||||
|
const dy = nodeB.y - nodeA.y;
|
||||||
|
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||||
|
|
||||||
|
if (distance < this.config.connectionDistance) {
|
||||||
|
// Create connection
|
||||||
|
const connection = {
|
||||||
|
from: i,
|
||||||
|
to: j,
|
||||||
|
distance: distance,
|
||||||
|
opacity: Math.max(0, 1 - distance / this.config.connectionDistance) * this.config.connectionOpacity
|
||||||
|
};
|
||||||
|
|
||||||
|
this.connections.push(connection);
|
||||||
|
nodeA.connections.push(j);
|
||||||
|
nodeB.connections.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
startAnimation() {
|
||||||
|
this.animationFrameId = requestAnimationFrame(this.animate.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
animate() {
|
||||||
|
// Update nodes
|
||||||
|
const width = this.canvas.width / (window.devicePixelRatio || 1);
|
||||||
|
const height = this.canvas.height / (window.devicePixelRatio || 1);
|
||||||
|
|
||||||
|
// Update node positions
|
||||||
|
for (let i = 0; i < this.nodes.length; i++) {
|
||||||
|
const node = this.nodes[i];
|
||||||
|
|
||||||
|
// Move node
|
||||||
|
node.x += node.speed.x;
|
||||||
|
node.y += node.speed.y;
|
||||||
|
|
||||||
|
// Boundary check with bounce
|
||||||
|
if (node.x < 0 || node.x > width) {
|
||||||
|
node.speed.x *= -1;
|
||||||
|
node.x = Math.max(0, Math.min(node.x, width));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.y < 0 || node.y > height) {
|
||||||
|
node.speed.y *= -1;
|
||||||
|
node.y = Math.max(0, Math.min(node.y, height));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update pulse phase
|
||||||
|
node.pulsePhase += this.config.pulseSpeed;
|
||||||
|
if (node.pulsePhase > Math.PI * 2) {
|
||||||
|
node.pulsePhase -= Math.PI * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recalculate connections dynamically
|
||||||
|
if (Math.random() < 0.05) { // Only recalculate 5% of the time for performance
|
||||||
|
this.createConnections();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render
|
||||||
|
if (this.useWebGL) {
|
||||||
|
this.renderWebGL();
|
||||||
|
} else {
|
||||||
|
this.renderCanvas();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue animation
|
||||||
|
this.animationFrameId = requestAnimationFrame(this.animate.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
renderWebGL() {
|
||||||
|
this.gl.clear(this.gl.COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
const width = this.canvas.width / (window.devicePixelRatio || 1);
|
||||||
|
const height = this.canvas.height / (window.devicePixelRatio || 1);
|
||||||
|
|
||||||
|
// Select shader program
|
||||||
|
this.gl.useProgram(this.programInfo.program);
|
||||||
|
|
||||||
|
// Set resolution uniform
|
||||||
|
this.gl.uniform2f(this.programInfo.uniformLocations.resolution, width, height);
|
||||||
|
|
||||||
|
// Draw connections
|
||||||
|
this.renderConnectionsWebGL();
|
||||||
|
|
||||||
|
// Draw nodes
|
||||||
|
this.renderNodesWebGL();
|
||||||
|
}
|
||||||
|
|
||||||
|
renderNodesWebGL() {
|
||||||
|
// Prepare node positions for WebGL
|
||||||
|
const positions = new Float32Array(this.nodes.length * 2);
|
||||||
|
const sizes = new Float32Array(this.nodes.length);
|
||||||
|
|
||||||
|
for (let i = 0; i < this.nodes.length; i++) {
|
||||||
|
const node = this.nodes[i];
|
||||||
|
positions[i * 2] = node.x;
|
||||||
|
positions[i * 2 + 1] = node.y;
|
||||||
|
|
||||||
|
// Size with pulse effect
|
||||||
|
const pulse = Math.sin(node.pulsePhase) * 0.3 + 1;
|
||||||
|
sizes[i] = node.size * pulse * (node.connections.length > 3 ? 1.5 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind position buffer
|
||||||
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer);
|
||||||
|
this.gl.bufferData(this.gl.ARRAY_BUFFER, positions, this.gl.STATIC_DRAW);
|
||||||
|
this.gl.vertexAttribPointer(
|
||||||
|
this.programInfo.attribLocations.vertexPosition,
|
||||||
|
2, // components per vertex
|
||||||
|
this.gl.FLOAT, // data type
|
||||||
|
false, // normalize
|
||||||
|
0, // stride
|
||||||
|
0 // offset
|
||||||
|
);
|
||||||
|
this.gl.enableVertexAttribArray(this.programInfo.attribLocations.vertexPosition);
|
||||||
|
|
||||||
|
// Bind size buffer
|
||||||
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.sizeBuffer);
|
||||||
|
this.gl.bufferData(this.gl.ARRAY_BUFFER, sizes, this.gl.STATIC_DRAW);
|
||||||
|
this.gl.vertexAttribPointer(
|
||||||
|
this.programInfo.attribLocations.pointSize,
|
||||||
|
1, // components per vertex
|
||||||
|
this.gl.FLOAT, // data type
|
||||||
|
false, // normalize
|
||||||
|
0, // stride
|
||||||
|
0 // offset
|
||||||
|
);
|
||||||
|
this.gl.enableVertexAttribArray(this.programInfo.attribLocations.pointSize);
|
||||||
|
|
||||||
|
// Set node color
|
||||||
|
const colorObj = this.isDarkMode ? this.darkModeColors : this.lightModeColors;
|
||||||
|
const nodeColor = this.hexToRgb(colorObj.nodeColor);
|
||||||
|
this.gl.uniform4f(
|
||||||
|
this.programInfo.uniformLocations.color,
|
||||||
|
nodeColor.r / 255,
|
||||||
|
nodeColor.g / 255,
|
||||||
|
nodeColor.b / 255,
|
||||||
|
0.8 // Alpha
|
||||||
|
);
|
||||||
|
|
||||||
|
// Draw nodes
|
||||||
|
this.gl.enable(this.gl.BLEND);
|
||||||
|
this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
|
||||||
|
this.gl.drawArrays(this.gl.POINTS, 0, this.nodes.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderConnectionsWebGL() {
|
||||||
|
// For each connection, draw a line
|
||||||
|
for (const connection of this.connections) {
|
||||||
|
const fromNode = this.nodes[connection.from];
|
||||||
|
const toNode = this.nodes[connection.to];
|
||||||
|
|
||||||
|
// Line positions
|
||||||
|
const positions = new Float32Array([
|
||||||
|
fromNode.x, fromNode.y,
|
||||||
|
toNode.x, toNode.y
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Bind position buffer
|
||||||
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer);
|
||||||
|
this.gl.bufferData(this.gl.ARRAY_BUFFER, positions, this.gl.STATIC_DRAW);
|
||||||
|
this.gl.vertexAttribPointer(
|
||||||
|
this.programInfo.attribLocations.vertexPosition,
|
||||||
|
2, // components per vertex
|
||||||
|
this.gl.FLOAT, // data type
|
||||||
|
false, // normalize
|
||||||
|
0, // stride
|
||||||
|
0 // offset
|
||||||
|
);
|
||||||
|
this.gl.enableVertexAttribArray(this.programInfo.attribLocations.vertexPosition);
|
||||||
|
|
||||||
|
// Disable point size attribute for lines
|
||||||
|
this.gl.disableVertexAttribArray(this.programInfo.attribLocations.pointSize);
|
||||||
|
|
||||||
|
// Set line color with connection opacity
|
||||||
|
const colorObj = this.isDarkMode ? this.darkModeColors : this.lightModeColors;
|
||||||
|
const lineColor = this.hexToRgb(colorObj.connectionColor);
|
||||||
|
this.gl.uniform4f(
|
||||||
|
this.programInfo.uniformLocations.color,
|
||||||
|
lineColor.r / 255,
|
||||||
|
lineColor.g / 255,
|
||||||
|
lineColor.b / 255,
|
||||||
|
connection.opacity
|
||||||
|
);
|
||||||
|
|
||||||
|
// Data pulse animation along connection
|
||||||
|
if (Math.random() < 0.01 && fromNode.connections.length > 2) {
|
||||||
|
// Draw data pulse (slightly different color)
|
||||||
|
const pulseColor = this.hexToRgb(colorObj.nodePulse);
|
||||||
|
this.gl.uniform4f(
|
||||||
|
this.programInfo.uniformLocations.color,
|
||||||
|
pulseColor.r / 255,
|
||||||
|
pulseColor.g / 255,
|
||||||
|
pulseColor.b / 255,
|
||||||
|
0.8
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the line
|
||||||
|
this.gl.enable(this.gl.BLEND);
|
||||||
|
this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE);
|
||||||
|
this.gl.lineWidth(1);
|
||||||
|
this.gl.drawArrays(this.gl.LINES, 0, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderCanvas() {
|
||||||
|
// Clear canvas
|
||||||
|
const width = this.canvas.width / (window.devicePixelRatio || 1);
|
||||||
|
const height = this.canvas.height / (window.devicePixelRatio || 1);
|
||||||
|
|
||||||
|
this.ctx.clearRect(0, 0, width, height);
|
||||||
|
|
||||||
|
// Set background
|
||||||
|
const backgroundColor = this.isDarkMode
|
||||||
|
? this.darkModeColors.background
|
||||||
|
: this.lightModeColors.background;
|
||||||
|
|
||||||
|
this.ctx.fillStyle = backgroundColor;
|
||||||
|
this.ctx.fillRect(0, 0, width, height);
|
||||||
|
|
||||||
|
// Draw connections
|
||||||
|
const connectionColor = this.isDarkMode
|
||||||
|
? this.darkModeColors.connectionColor
|
||||||
|
: this.lightModeColors.connectionColor;
|
||||||
|
|
||||||
|
for (const connection of this.connections) {
|
||||||
|
const fromNode = this.nodes[connection.from];
|
||||||
|
const toNode = this.nodes[connection.to];
|
||||||
|
|
||||||
|
this.ctx.beginPath();
|
||||||
|
this.ctx.moveTo(fromNode.x, fromNode.y);
|
||||||
|
this.ctx.lineTo(toNode.x, toNode.y);
|
||||||
|
|
||||||
|
const rgbColor = this.hexToRgb(connectionColor);
|
||||||
|
this.ctx.strokeStyle = `rgba(${rgbColor.r}, ${rgbColor.g}, ${rgbColor.b}, ${connection.opacity})`;
|
||||||
|
this.ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw nodes
|
||||||
|
const nodeColor = this.isDarkMode
|
||||||
|
? this.darkModeColors.nodeColor
|
||||||
|
: this.lightModeColors.nodeColor;
|
||||||
|
|
||||||
|
const nodePulse = this.isDarkMode
|
||||||
|
? this.darkModeColors.nodePulse
|
||||||
|
: this.lightModeColors.nodePulse;
|
||||||
|
|
||||||
|
for (const node of this.nodes) {
|
||||||
|
// Node with glow effect
|
||||||
|
const pulse = Math.sin(node.pulsePhase) * 0.3 + 1;
|
||||||
|
const nodeSize = node.size * pulse * (node.connections.length > 3 ? 1.5 : 1);
|
||||||
|
|
||||||
|
// Glow effect
|
||||||
|
const glow = this.ctx.createRadialGradient(
|
||||||
|
node.x, node.y, 0,
|
||||||
|
node.x, node.y, nodeSize * 2
|
||||||
|
);
|
||||||
|
|
||||||
|
const rgbNodeColor = this.hexToRgb(nodeColor);
|
||||||
|
const rgbPulseColor = this.hexToRgb(nodePulse);
|
||||||
|
|
||||||
|
glow.addColorStop(0, `rgba(${rgbPulseColor.r}, ${rgbPulseColor.g}, ${rgbPulseColor.b}, 0.8)`);
|
||||||
|
glow.addColorStop(0.5, `rgba(${rgbNodeColor.r}, ${rgbNodeColor.g}, ${rgbNodeColor.b}, 0.2)`);
|
||||||
|
glow.addColorStop(1, `rgba(${rgbNodeColor.r}, ${rgbNodeColor.g}, ${rgbNodeColor.b}, 0)`);
|
||||||
|
|
||||||
|
this.ctx.beginPath();
|
||||||
|
this.ctx.arc(node.x, node.y, nodeSize * 2, 0, Math.PI * 2);
|
||||||
|
this.ctx.fillStyle = glow;
|
||||||
|
this.ctx.fill();
|
||||||
|
|
||||||
|
// Main node
|
||||||
|
this.ctx.beginPath();
|
||||||
|
this.ctx.arc(node.x, node.y, nodeSize, 0, Math.PI * 2);
|
||||||
|
this.ctx.fillStyle = nodeColor;
|
||||||
|
this.ctx.fill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method to convert hex to RGB
|
||||||
|
hexToRgb(hex) {
|
||||||
|
// Remove # if present
|
||||||
|
hex = hex.replace(/^#/, '');
|
||||||
|
|
||||||
|
// Parse hex values
|
||||||
|
const bigint = parseInt(hex, 16);
|
||||||
|
const r = (bigint >> 16) & 255;
|
||||||
|
const g = (bigint >> 8) & 255;
|
||||||
|
const b = bigint & 255;
|
||||||
|
|
||||||
|
return { r, g, b };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup method
|
||||||
|
destroy() {
|
||||||
|
if (this.animationFrameId) {
|
||||||
|
cancelAnimationFrame(this.animationFrameId);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.removeEventListener('resize', this.resizeCanvas.bind(this));
|
||||||
|
|
||||||
|
if (this.canvas && this.canvas.parentNode) {
|
||||||
|
this.canvas.parentNode.removeChild(this.canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.gl) {
|
||||||
|
// Clean up WebGL resources
|
||||||
|
this.gl.deleteBuffer(this.positionBuffer);
|
||||||
|
this.gl.deleteBuffer(this.sizeBuffer);
|
||||||
|
this.gl.deleteProgram(this.shaderProgram);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize when DOM is loaded
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
window.neuralNetworkBackground = new NeuralNetworkBackground();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Clean up when window is closed
|
||||||
|
window.addEventListener('beforeunload', () => {
|
||||||
|
if (window.neuralNetworkBackground) {
|
||||||
|
window.neuralNetworkBackground.destroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
484
website/static/style.css
Normal file
484
website/static/style.css
Normal file
@@ -0,0 +1,484 @@
|
|||||||
|
/* Main Systades Styles - Dark Mystical Theme */
|
||||||
|
|
||||||
|
/* Import Fonts */
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap');
|
||||||
|
|
||||||
|
/* Root Variables */
|
||||||
|
:root {
|
||||||
|
/* Light Theme Colors */
|
||||||
|
--light-bg-primary: #f8fafc;
|
||||||
|
--light-bg-secondary: #f1f5f9;
|
||||||
|
--light-text-primary: #1e293b;
|
||||||
|
--light-text-secondary: #475569;
|
||||||
|
--light-accent-primary: #7c3aed;
|
||||||
|
--light-accent-secondary: #8b5cf6;
|
||||||
|
--light-border: #e2e8f0;
|
||||||
|
|
||||||
|
/* Dark Theme Colors */
|
||||||
|
--dark-bg-primary: #0a0e19;
|
||||||
|
--dark-bg-secondary: #111827;
|
||||||
|
--dark-text-primary: #f9fafb;
|
||||||
|
--dark-text-secondary: #e5e7eb;
|
||||||
|
--dark-accent-primary: #6d28d9;
|
||||||
|
--dark-accent-secondary: #8b5cf6;
|
||||||
|
--dark-border: #1f2937;
|
||||||
|
|
||||||
|
/* Common */
|
||||||
|
--font-sans: 'Inter', system-ui, -apple-system, sans-serif;
|
||||||
|
--font-mono: 'JetBrains Mono', monospace;
|
||||||
|
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||||
|
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
--shadow-md: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
--shadow-lg: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
|
|
||||||
|
/* Transitions */
|
||||||
|
--transition-fast: 150ms ease-in-out;
|
||||||
|
--transition-normal: 300ms ease-in-out;
|
||||||
|
--transition-slow: 500ms ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Base Elements */
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
transition: background-color var(--transition-normal), color var(--transition-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Theme Specific */
|
||||||
|
body {
|
||||||
|
background-color: var(--light-bg-primary);
|
||||||
|
color: var(--light-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark {
|
||||||
|
background-color: var(--dark-bg-primary);
|
||||||
|
color: var(--dark-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Typography */
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gradient-text {
|
||||||
|
background-clip: text;
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
color: transparent;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
body .gradient-text {
|
||||||
|
background-image: linear-gradient(135deg, var(--light-accent-primary), var(--light-accent-secondary));
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark .gradient-text {
|
||||||
|
background-image: linear-gradient(135deg, var(--dark-accent-primary), var(--dark-accent-secondary));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Subtle glow for dark mode gradient text */
|
||||||
|
body.dark .gradient-text::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
filter: blur(8px);
|
||||||
|
opacity: 0.3;
|
||||||
|
background-image: inherit;
|
||||||
|
z-index: -1;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Containers and Layout */
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
padding-left: 1rem;
|
||||||
|
padding-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 640px) {
|
||||||
|
.container {
|
||||||
|
max-width: 640px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.container {
|
||||||
|
max-width: 768px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.container {
|
||||||
|
max-width: 1024px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1280px) {
|
||||||
|
.container {
|
||||||
|
max-width: 1280px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Glass Morphism */
|
||||||
|
.glass-morphism {
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
body .glass-navbar-light {
|
||||||
|
background-color: rgba(255, 255, 255, 0.8);
|
||||||
|
border-color: rgba(226, 232, 240, 0.5);
|
||||||
|
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark .glass-navbar-dark {
|
||||||
|
background-color: rgba(10, 14, 25, 0.8);
|
||||||
|
border-color: rgba(31, 41, 55, 0.5);
|
||||||
|
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Navigation */
|
||||||
|
.nav-link {
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
transition: all var(--transition-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
body .nav-link {
|
||||||
|
color: var(--light-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark .nav-link {
|
||||||
|
color: var(--dark-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
body .nav-link:hover {
|
||||||
|
color: var(--light-text-primary);
|
||||||
|
background-color: rgba(241, 245, 249, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark .nav-link:hover {
|
||||||
|
color: var(--dark-text-primary);
|
||||||
|
background-color: rgba(31, 41, 55, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
body .nav-link-light-active {
|
||||||
|
color: var(--light-accent-primary);
|
||||||
|
background-color: rgba(139, 92, 246, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark .nav-link-active {
|
||||||
|
color: var(--dark-accent-secondary);
|
||||||
|
background-color: rgba(109, 40, 217, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buttons */
|
||||||
|
.btn {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all var(--transition-normal);
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
body .btn-primary {
|
||||||
|
background-color: var(--light-accent-primary);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark .btn-primary {
|
||||||
|
background-color: var(--dark-accent-primary);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
body .btn-primary:hover {
|
||||||
|
background-color: var(--light-accent-secondary);
|
||||||
|
box-shadow: 0 0 15px rgba(124, 58, 237, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark .btn-primary:hover {
|
||||||
|
background-color: var(--dark-accent-secondary);
|
||||||
|
box-shadow: 0 0 15px rgba(109, 40, 217, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
body .btn-secondary {
|
||||||
|
background-color: transparent;
|
||||||
|
border: 1px solid var(--light-border);
|
||||||
|
color: var(--light-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark .btn-secondary {
|
||||||
|
background-color: transparent;
|
||||||
|
border: 1px solid var(--dark-border);
|
||||||
|
color: var(--dark-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
body .btn-secondary:hover {
|
||||||
|
background-color: var(--light-bg-secondary);
|
||||||
|
border-color: var(--light-accent-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark .btn-secondary:hover {
|
||||||
|
background-color: var(--dark-bg-secondary);
|
||||||
|
border-color: var(--dark-accent-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cards */
|
||||||
|
.card {
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all var(--transition-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
body .card {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid var(--light-border);
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark .card {
|
||||||
|
background-color: var(--dark-bg-secondary);
|
||||||
|
border: 1px solid var(--dark-border);
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
body .card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark .card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -2px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Form Elements */
|
||||||
|
.form-input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
transition: all var(--transition-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
body .form-input {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid var(--light-border);
|
||||||
|
color: var(--light-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark .form-input {
|
||||||
|
background-color: var(--dark-bg-secondary);
|
||||||
|
border: 1px solid var(--dark-border);
|
||||||
|
color: var(--dark-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
body .form-input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--light-accent-secondary);
|
||||||
|
box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark .form-input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--dark-accent-secondary);
|
||||||
|
box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animations */
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from { opacity: 0; }
|
||||||
|
to { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-fade-in {
|
||||||
|
animation: fadeIn 0.5s ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes float {
|
||||||
|
0%, 100% { transform: translateY(0); }
|
||||||
|
50% { transform: translateY(-10px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-float {
|
||||||
|
animation: float 5s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0%, 100% { opacity: 0.7; }
|
||||||
|
50% { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-pulse {
|
||||||
|
animation: pulse 3s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Utilities */
|
||||||
|
.sr-only {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
padding: 0;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
white-space: nowrap;
|
||||||
|
border-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadow-elevation {
|
||||||
|
transition: box-shadow var(--transition-normal), transform var(--transition-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
body .shadow-elevation {
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark .shadow-elevation {
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -1px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
body .shadow-elevation:hover {
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark .shadow-elevation:hover {
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.4), 0 4px 6px -2px rgba(0, 0, 0, 0.3);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tooltips */
|
||||||
|
.tooltip {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip:hover::before {
|
||||||
|
content: attr(data-tooltip);
|
||||||
|
position: absolute;
|
||||||
|
bottom: 100%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
z-index: 10;
|
||||||
|
opacity: 0;
|
||||||
|
animation: fadeIn 0.3s ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
body .tooltip:hover::before {
|
||||||
|
background-color: var(--light-text-primary);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark .tooltip:hover::before {
|
||||||
|
background-color: var(--dark-text-primary);
|
||||||
|
color: var(--dark-bg-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mystical elements */
|
||||||
|
.mystical-border {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mystical-border::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
border: 1px solid;
|
||||||
|
border-radius: inherit;
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.3;
|
||||||
|
transition: opacity var(--transition-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
body .mystical-border::after {
|
||||||
|
border-color: var(--light-accent-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark .mystical-border::after {
|
||||||
|
border-color: var(--dark-accent-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mystical-border:hover::after {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design Helpers */
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.container {
|
||||||
|
padding-left: 1rem;
|
||||||
|
padding-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-heading {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-heading {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Accessibility */
|
||||||
|
:focus-visible {
|
||||||
|
outline: 2px solid;
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body :focus-visible {
|
||||||
|
outline-color: var(--light-accent-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark :focus-visible {
|
||||||
|
outline-color: var(--dark-accent-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scrollbar styling */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 0.5rem;
|
||||||
|
height: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
body ::-webkit-scrollbar-track {
|
||||||
|
background: var(--light-bg-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark ::-webkit-scrollbar-track {
|
||||||
|
background: var(--dark-bg-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
body ::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--light-accent-primary);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark ::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--dark-accent-primary);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
body ::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--light-accent-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark ::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--dark-accent-secondary);
|
||||||
|
}
|
||||||
@@ -83,8 +83,8 @@
|
|||||||
<!-- Alpine.js -->
|
<!-- Alpine.js -->
|
||||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.12.3/dist/cdn.min.js"></script>
|
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.12.3/dist/cdn.min.js"></script>
|
||||||
|
|
||||||
<!-- Network Background Script -->
|
<!-- Neural Network Background Script -->
|
||||||
<script src="{{ url_for('static', filename='network-background.js') }}"></script>
|
<script src="{{ url_for('static', filename='neural-network-background.js') }}"></script>
|
||||||
|
|
||||||
<!-- Hauptmodul laden (als ES6 Modul) -->
|
<!-- Hauptmodul laden (als ES6 Modul) -->
|
||||||
<script type="module">
|
<script type="module">
|
||||||
@@ -92,7 +92,7 @@
|
|||||||
// Alpine.js-Integration
|
// Alpine.js-Integration
|
||||||
document.addEventListener('alpine:init', () => {
|
document.addEventListener('alpine:init', () => {
|
||||||
Alpine.data('layout', () => ({
|
Alpine.data('layout', () => ({
|
||||||
darkMode: false,
|
darkMode: true, // Default to dark mode
|
||||||
mobileMenuOpen: false,
|
mobileMenuOpen: false,
|
||||||
userMenuOpen: false,
|
userMenuOpen: false,
|
||||||
showSettingsModal: false,
|
showSettingsModal: false,
|
||||||
@@ -103,7 +103,7 @@
|
|||||||
|
|
||||||
fetchDarkModeFromSession() {
|
fetchDarkModeFromSession() {
|
||||||
// Lade den Dark Mode-Status vom Server
|
// Lade den Dark Mode-Status vom Server
|
||||||
fetch('/get_dark_mode')
|
fetch('/api/get_dark_mode')
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
@@ -121,7 +121,7 @@
|
|||||||
document.querySelector('html').classList.toggle('dark', this.darkMode);
|
document.querySelector('html').classList.toggle('dark', this.darkMode);
|
||||||
|
|
||||||
// Speichere den Dark Mode-Status auf dem Server
|
// Speichere den Dark Mode-Status auf dem Server
|
||||||
fetch('/set_dark_mode', {
|
fetch('/api/set_dark_mode', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
@@ -154,13 +154,66 @@
|
|||||||
|
|
||||||
<!-- Seitenspezifische Styles -->
|
<!-- Seitenspezifische Styles -->
|
||||||
{% block extra_css %}{% endblock %}
|
{% block extra_css %}{% endblock %}
|
||||||
</head>
|
|
||||||
<body data-page="{{ request.endpoint }}" class="relative overflow-x-hidden">
|
|
||||||
<!-- Globaler Hintergrund -->
|
|
||||||
<div class="full-page-bg"></div>
|
|
||||||
<!-- Statischer Fallback-Hintergrund (wird nur angezeigt, wenn JavaScript deaktiviert ist) -->
|
|
||||||
<div class="fixed inset-0 z-[-9] bg-cover bg-center opacity-50"></div>
|
|
||||||
|
|
||||||
|
<!-- Custom dark mode styles -->
|
||||||
|
<style>
|
||||||
|
/* Dark mystical theme */
|
||||||
|
.dark {
|
||||||
|
--bg-primary: #0a0e19;
|
||||||
|
--bg-secondary: #111827;
|
||||||
|
--text-primary: #f9fafb;
|
||||||
|
--text-secondary: #e5e7eb;
|
||||||
|
--accent-primary: #6d28d9;
|
||||||
|
--accent-secondary: #8b5cf6;
|
||||||
|
--glow-effect: 0 0 15px rgba(124, 58, 237, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light theme with mystical tones */
|
||||||
|
:root {
|
||||||
|
--bg-primary: #f8fafc;
|
||||||
|
--bg-secondary: #f1f5f9;
|
||||||
|
--text-primary: #1e293b;
|
||||||
|
--text-secondary: #475569;
|
||||||
|
--accent-primary: #7c3aed;
|
||||||
|
--accent-secondary: #8b5cf6;
|
||||||
|
--glow-effect: 0 0 15px rgba(139, 92, 246, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark {
|
||||||
|
background-color: var(--bg-primary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mystical glowing effects */
|
||||||
|
.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 effects */
|
||||||
|
.glass-morphism {
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .glass-navbar-dark {
|
||||||
|
background-color: rgba(10, 14, 25, 0.8);
|
||||||
|
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-navbar-light {
|
||||||
|
background-color: rgba(255, 255, 255, 0.8);
|
||||||
|
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body data-page="{{ request.endpoint }}" class="relative overflow-x-hidden dark">
|
||||||
<!-- App-Container -->
|
<!-- App-Container -->
|
||||||
<div id="app-container" class="flex flex-col min-h-screen" x-data="layout">
|
<div id="app-container" class="flex flex-col min-h-screen" x-data="layout">
|
||||||
<!-- Hauptnavigation -->
|
<!-- Hauptnavigation -->
|
||||||
@@ -199,8 +252,8 @@
|
|||||||
<button onclick="window.MindMap && window.MindMap.assistant && window.MindMap.assistant.toggleAssistant(true)"
|
<button onclick="window.MindMap && window.MindMap.assistant && window.MindMap.assistant.toggleAssistant(true)"
|
||||||
class="nav-link flex items-center"
|
class="nav-link flex items-center"
|
||||||
x-bind:class="darkMode
|
x-bind:class="darkMode
|
||||||
? 'bg-gradient-to-r from-purple-600/80 to-blue-500/80 text-white font-medium px-4 py-2 rounded-xl hover:shadow-lg transition-all duration-300 hover:-translate-y-0.5'
|
? '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-500/20 to-blue-400/20 text-gray-800 font-medium px-4 py-2 rounded-xl hover:shadow-md transition-all duration-300 hover:-translate-y-0.5'">
|
: '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
|
<i class="fa-solid fa-robot mr-2"></i>KI-Chat
|
||||||
</button>
|
</button>
|
||||||
{% if current_user.is_authenticated %}
|
{% if current_user.is_authenticated %}
|
||||||
@@ -221,9 +274,9 @@
|
|||||||
<div class="relative w-12 h-6">
|
<div class="relative w-12 h-6">
|
||||||
<input type="checkbox" id="darkModeToggle" class="sr-only" x-model="darkMode">
|
<input type="checkbox" id="darkModeToggle" class="sr-only" x-model="darkMode">
|
||||||
<div class="block w-12 h-6 rounded-full transition-colors duration-300"
|
<div class="block w-12 h-6 rounded-full transition-colors duration-300"
|
||||||
x-bind:class="darkMode ? 'bg-blue-400/50' : 'bg-gray-400/50'"></div>
|
x-bind:class="darkMode ? 'bg-purple-800/50' : 'bg-gray-400/50'"></div>
|
||||||
<div class="dot absolute left-1 top-1 w-4 h-4 rounded-full transition-transform duration-300 shadow-md"
|
<div class="dot absolute left-1 top-1 w-4 h-4 rounded-full transition-transform duration-300 shadow-md"
|
||||||
x-bind:class="darkMode ? 'bg-blue-500 transform translate-x-6' : 'bg-white'"></div>
|
x-bind:class="darkMode ? 'bg-purple-600 transform translate-x-6' : 'bg-white'"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-3 hidden sm:block"
|
<div class="ml-3 hidden sm:block"
|
||||||
x-bind:class="darkMode ? 'text-white/90' : 'text-gray-700'">
|
x-bind:class="darkMode ? 'text-white/90' : 'text-gray-700'">
|
||||||
@@ -301,13 +354,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{{ url_for('login') }}"
|
<div class="flex items-center space-x-2">
|
||||||
class="flex items-center px-4 py-2.5 rounded-xl font-medium transition-all duration-300"
|
<a href="{{ url_for('login') }}"
|
||||||
x-bind:class="darkMode
|
class="py-2 px-4 rounded-lg transition-all duration-300"
|
||||||
? 'bg-gray-800/80 text-white hover:bg-gray-700/80 shadow-md hover:shadow-lg hover:-translate-y-0.5'
|
x-bind:class="darkMode
|
||||||
: 'bg-gray-200/80 text-gray-800 hover:bg-gray-300/80 shadow-sm hover:shadow-md hover:-translate-y-0.5'">
|
? 'text-white/90 hover:bg-dark-700/80'
|
||||||
<i class="fa-solid fa-user mr-2"></i>Mein Konto
|
: 'text-gray-700 hover:bg-gray-100/80'">
|
||||||
</a>
|
<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 %}
|
{% endif %}
|
||||||
|
|
||||||
<!-- Mobilmenü-Button -->
|
<!-- Mobilmenü-Button -->
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
{% block extra_css %}
|
{% block extra_css %}
|
||||||
<style>
|
<style>
|
||||||
/* Hintergrund über die gesamte Seite erstrecken */
|
/* Full height and width for the page */
|
||||||
html, body {
|
html, body {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -13,152 +13,146 @@
|
|||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Entferne den Gradient-Hintergrund vollständig */
|
/* Remove gradient backgrounds */
|
||||||
.hero-gradient, .bg-fade {
|
.hero-gradient, .bg-fade {
|
||||||
background: none !important;
|
background: none !important;
|
||||||
clip-path: none !important;
|
clip-path: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tech-line {
|
/* Style elements */
|
||||||
|
.mystical-line {
|
||||||
height: 1px;
|
height: 1px;
|
||||||
background: linear-gradient(to right, transparent, rgba(100, 100, 100, 0.1), transparent);
|
background: linear-gradient(to right, transparent, rgba(109, 40, 217, 0.2), transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tech-dot {
|
.dark .mystical-line {
|
||||||
width: 4px;
|
background: linear-gradient(to right, transparent, rgba(139, 92, 246, 0.2), transparent);
|
||||||
height: 4px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: rgba(100, 100, 100, 0.2);
|
|
||||||
position: absolute;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .tech-line {
|
/* Text reveal animation */
|
||||||
background: linear-gradient(to right, transparent, rgba(255, 255, 255, 0.1), transparent);
|
@keyframes textReveal {
|
||||||
|
0% { clip-path: polygon(0 0, 0 0, 0 100%, 0 100%); }
|
||||||
|
100% { clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); }
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .tech-dot {
|
.text-reveal {
|
||||||
background-color: rgba(255, 255, 255, 0.3);
|
animation: textReveal 1s cubic-bezier(0.77, 0, 0.18, 1) forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes pulse {
|
.delay-1 { animation-delay: 0.2s; }
|
||||||
0% { r: 10; opacity: 0.7; }
|
.delay-2 { animation-delay: 0.4s; }
|
||||||
50% { r: 12; opacity: 1; }
|
.delay-3 { animation-delay: 0.6s; }
|
||||||
100% { r: 10; opacity: 0.7; }
|
|
||||||
|
/* Home page specific styles */
|
||||||
|
.featured-card {
|
||||||
|
transition: transform 0.5s ease, box-shadow 0.5s ease;
|
||||||
|
border: 1px solid;
|
||||||
|
border-color: rgba(139, 92, 246, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.animate-pulse {
|
.dark .featured-card {
|
||||||
animation: pulse 3s ease-in-out infinite;
|
border-color: rgba(109, 40, 217, 0.2);
|
||||||
|
background-color: rgba(17, 24, 39, 0.7);
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes iconPulse {
|
.featured-card:hover {
|
||||||
0% { transform: scale(1); }
|
transform: translateY(-5px);
|
||||||
50% { transform: scale(1.1); }
|
|
||||||
100% { transform: scale(1); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-pulse {
|
.dark .featured-card:hover {
|
||||||
animation: iconPulse 3s ease-in-out infinite;
|
box-shadow: 0 5px 15px rgba(109, 40, 217, 0.2);
|
||||||
display: inline-block;
|
border-color: rgba(109, 40, 217, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Volle Seitenbreite für Container */
|
.featured-card:hover {
|
||||||
#app-container, .container, main, .mx-auto, .py-12 {
|
box-shadow: 0 5px 15px rgba(139, 92, 246, 0.1);
|
||||||
width: 100%;
|
border-color: rgba(139, 92, 246, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sicherstellen dass der Hintergrund die ganze Seite abdeckt */
|
/* Chat section styles */
|
||||||
.full-page-bg {
|
.embedded-chat {
|
||||||
position: fixed;
|
height: 350px;
|
||||||
top: 0;
|
border-radius: 1rem;
|
||||||
left: 0;
|
overflow: hidden;
|
||||||
width: 100vw;
|
transition: all 0.3s ease;
|
||||||
height: 100vh;
|
border: 1px solid;
|
||||||
z-index: -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Chat-Animationen */
|
.dark .embedded-chat {
|
||||||
.typing-dots span {
|
background-color: rgba(17, 24, 39, 0.7);
|
||||||
animation-duration: 1.2s;
|
border-color: rgba(109, 40, 217, 0.2);
|
||||||
animation-iteration-count: infinite;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Chat-Nachrichten-Animation */
|
.embedded-chat {
|
||||||
@keyframes fadeInUp {
|
background-color: rgba(255, 255, 255, 0.7);
|
||||||
from {
|
border-color: rgba(139, 92, 246, 0.1);
|
||||||
opacity: 0;
|
|
||||||
transform: translate3d(0, 10px, 0);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translate3d(0, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#embedded-chat-messages > div {
|
|
||||||
animation: fadeInUp 0.3s ease-out forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sanftes Scrollen im Chat */
|
|
||||||
#embedded-chat-messages {
|
#embedded-chat-messages {
|
||||||
scroll-behavior: smooth;
|
height: 250px;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Benutzerdefinierter Scrollbar für den Chat */
|
/* Chat typing indicator */
|
||||||
#embedded-chat-messages::-webkit-scrollbar {
|
.typing-dots span {
|
||||||
width: 6px;
|
display: inline-block;
|
||||||
|
width: 5px;
|
||||||
|
height: 5px;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 3px;
|
||||||
|
background-color: currentColor;
|
||||||
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
#embedded-chat-messages::-webkit-scrollbar-track {
|
.typing-dots span:nth-child(1) {
|
||||||
background-color: rgba(0, 0, 0, 0.05);
|
animation: dot-pulse 1.2s infinite ease-in-out;
|
||||||
border-radius: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#embedded-chat-messages::-webkit-scrollbar-thumb {
|
.typing-dots span:nth-child(2) {
|
||||||
background-color: rgba(139, 92, 246, 0.3);
|
animation: dot-pulse 1.2s infinite ease-in-out 0.2s;
|
||||||
border-radius: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark #embedded-chat-messages::-webkit-scrollbar-thumb {
|
.typing-dots span:nth-child(3) {
|
||||||
background-color: rgba(139, 92, 246, 0.5);
|
animation: dot-pulse 1.2s infinite ease-in-out 0.4s;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hover-Effekt für Quick-Query-Buttons */
|
@keyframes dot-pulse {
|
||||||
.quick-query-btn:hover {
|
0%, 100% { transform: scale(1); opacity: 0.5; }
|
||||||
cursor: pointer;
|
50% { transform: scale(1.3); opacity: 1; }
|
||||||
background: linear-gradient(to right, rgba(139, 92, 246, 0.1), rgba(96, 165, 250, 0.1));
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark .quick-query-btn:hover {
|
|
||||||
background: linear-gradient(to right, rgba(139, 92, 246, 0.2), rgba(96, 165, 250, 0.2));
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<!-- Hintergrund für die gesamte Seite -->
|
|
||||||
<div class="full-page-bg gradient-background"></div>
|
|
||||||
|
|
||||||
<!-- Hero Section -->
|
<!-- Hero Section -->
|
||||||
<section class="relative pt-20 pb-32">
|
<section class="relative pt-20 pb-24">
|
||||||
<!-- Hero Content -->
|
<!-- Hero Content -->
|
||||||
<div class="max-w-screen-xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
|
<div class="max-w-screen-xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
|
||||||
<div class="text-center mb-16">
|
<div class="text-center mb-16">
|
||||||
<h1 class="hero-heading mb-8 text-gray-900 dark:text-white">
|
<h1 class="hero-heading mb-8 text-gray-900 dark:text-white">
|
||||||
<span class="gradient-text inline-block transform transition-all duration-700 hover:scale-105">Wissen</span> neu
|
<div class="overflow-hidden">
|
||||||
<div class="mt-2 relative">
|
<span class="gradient-text inline-block text-reveal">Wissen</span>
|
||||||
<span class="relative inline-block">vernetzen
|
</div>
|
||||||
|
<div class="overflow-hidden mt-2">
|
||||||
|
<span class="inline-block text-reveal delay-1">neu</span>
|
||||||
|
</div>
|
||||||
|
<div class="mt-2 relative overflow-hidden">
|
||||||
|
<span class="relative inline-block text-reveal delay-2">vernetzen
|
||||||
<div class="absolute -bottom-2 left-0 right-0 h-1 bg-gradient-to-r from-purple-500/0 via-purple-500/70 to-purple-500/0 rounded-full"></div>
|
<div class="absolute -bottom-2 left-0 right-0 h-1 bg-gradient-to-r from-purple-500/0 via-purple-500/70 to-purple-500/0 rounded-full"></div>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-xl md:text-2xl text-gray-700 dark:text-gray-300 max-w-3xl mx-auto mb-12">
|
<div class="overflow-hidden">
|
||||||
Erkunde komplexe Ideen visuell, schaffe Verbindungen und teile deine Gedanken
|
<p class="text-xl md:text-2xl text-gray-700 dark:text-gray-300 max-w-3xl mx-auto mb-12 text-reveal delay-3">
|
||||||
in einem interaktiven Wissensnetzwerk.
|
Erkunde komplexe Ideen visuell, schaffe Verbindungen und teile deine Gedanken
|
||||||
</p>
|
in einem interaktiven Wissensnetzwerk.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
<div class="flex flex-col sm:flex-row gap-5 justify-center">
|
<div class="flex flex-col sm:flex-row gap-5 justify-center">
|
||||||
<a href="{{ url_for('mindmap') }}" class="group transition-all duration-300 bg-gradient-to-r from-purple-600 to-indigo-600 hover:from-purple-700 hover:to-indigo-700 text-white font-medium text-lg px-8 py-4 rounded-2xl shadow-lg hover:shadow-xl hover:shadow-purple-500/20 transform hover:-translate-y-1">
|
<a href="{{ url_for('mindmap') }}" class="mystical-button mystical-button-primary group transition-all duration-300">
|
||||||
<span class="flex items-center justify-center">
|
<span class="flex items-center justify-center">
|
||||||
<i class="fa-solid fa-diagram-project mr-3 text-purple-200 group-hover:text-white transition-all duration-300 animate-pulse"></i>
|
<i class="fa-solid fa-diagram-project mr-3 text-purple-200 group-hover:text-white transition-all duration-300"></i>
|
||||||
<span class="relative">
|
<span class="relative">
|
||||||
Mindmap erkunden
|
Mindmap erkunden
|
||||||
<span class="absolute -bottom-1 left-0 w-0 h-0.5 bg-white group-hover:w-full transition-all duration-300"></span>
|
<span class="absolute -bottom-1 left-0 w-0 h-0.5 bg-white group-hover:w-full transition-all duration-300"></span>
|
||||||
@@ -166,12 +160,12 @@
|
|||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
{% if not current_user.is_authenticated %}
|
{% if not current_user.is_authenticated %}
|
||||||
<a href="{{ url_for('register') }}" class="group transition-all duration-300 bg-gradient-to-r from-blue-500 to-cyan-500 hover:from-blue-600 hover:to-cyan-600 text-white font-medium text-lg px-8 py-4 rounded-2xl shadow-lg hover:shadow-xl hover:shadow-blue-500/20 transform hover:-translate-y-1">
|
<a href="{{ url_for('register') }}" class="mystical-button mystical-button-secondary group transition-all duration-300">
|
||||||
<span class="flex items-center justify-center">
|
<span class="flex items-center justify-center">
|
||||||
<i class="fa-solid fa-user-plus mr-3 text-blue-200 group-hover:text-white transition-all duration-300 icon-pulse"></i>
|
<i class="fa-solid fa-user-plus mr-3 group-hover:text-accent-secondary-dark dark:group-hover:text-accent-secondary-light transition-all duration-300"></i>
|
||||||
<span class="relative">
|
<span class="relative">
|
||||||
Konto erstellen
|
Konto erstellen
|
||||||
<span class="absolute -bottom-1 left-0 w-0 h-0.5 bg-white group-hover:w-full transition-all duration-300"></span>
|
<span class="absolute -bottom-1 left-0 w-0 h-0.5 bg-accent-primary-light dark:bg-accent-primary-dark group-hover:w-full transition-all duration-300"></span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
@@ -179,61 +173,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Tech illustration -->
|
<!-- Central logo and name -->
|
||||||
<div class="relative w-full max-w-4xl mx-auto h-80 sm:h-96">
|
<div class="relative w-full max-w-4xl mx-auto h-40 sm:h-60 mb-16">
|
||||||
<div class="absolute inset-0 flex items-center justify-center">
|
<div class="absolute inset-0 flex items-center justify-center">
|
||||||
<div class="hidden md:block text-center">
|
<div class="text-center">
|
||||||
<div class="text-3xl font-bold gradient-text mb-2 animate-float">Systades</div>
|
<div class="text-3xl font-bold gradient-text mb-2 animate-float">Systades</div>
|
||||||
<div class="text-lg text-gray-700 dark:text-gray-300">WISSEN VERNETZEN</div>
|
<div class="text-lg text-gray-700 dark:text-gray-300">WISSEN VERNETZEN</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Network Visualization with SVG -->
|
|
||||||
<svg class="absolute inset-0 w-full h-full" viewBox="0 0 800 600" preserveAspectRatio="xMidYMid meet">
|
|
||||||
<!-- Glossy Nodes and Lines -->
|
|
||||||
<defs>
|
|
||||||
<radialGradient id="nodeGradient" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
|
|
||||||
<stop offset="0%" stop-color="rgba(139, 92, 246, 0.9)" />
|
|
||||||
<stop offset="100%" stop-color="rgba(79, 70, 229, 0.5)" />
|
|
||||||
</radialGradient>
|
|
||||||
<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
|
|
||||||
<feGaussianBlur stdDeviation="5" result="blur" />
|
|
||||||
<feComposite in="SourceGraphic" in2="blur" operator="over" />
|
|
||||||
</filter>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<!-- Network Lines -->
|
|
||||||
<g class="lines">
|
|
||||||
<!-- Connection network -->
|
|
||||||
<line x1="200" y1="250" x2="400" y2="150" stroke="rgba(0,0,0,0.1)" stroke-width="1" class="dark:hidden" />
|
|
||||||
<line x1="400" y1="150" x2="600" y2="250" stroke="rgba(0,0,0,0.1)" stroke-width="1" class="dark:hidden" />
|
|
||||||
<line x1="600" y1="250" x2="400" y2="350" stroke="rgba(0,0,0,0.1)" stroke-width="1" class="dark:hidden" />
|
|
||||||
<line x1="400" y1="350" x2="200" y2="250" stroke="rgba(0,0,0,0.1)" stroke-width="1" class="dark:hidden" />
|
|
||||||
<line x1="400" y1="150" x2="400" y2="350" stroke="rgba(0,0,0,0.1)" stroke-width="1" class="dark:hidden" />
|
|
||||||
<line x1="200" y1="250" x2="600" y2="250" stroke="rgba(0,0,0,0.1)" stroke-width="1" class="dark:hidden" />
|
|
||||||
|
|
||||||
<!-- Dark mode connections -->
|
|
||||||
<line x1="200" y1="250" x2="400" y2="150" stroke="rgba(255,255,255,0.1)" stroke-width="1" class="hidden dark:inline" />
|
|
||||||
<line x1="400" y1="150" x2="600" y2="250" stroke="rgba(255,255,255,0.1)" stroke-width="1" class="hidden dark:inline" />
|
|
||||||
<line x1="600" y1="250" x2="400" y2="350" stroke="rgba(255,255,255,0.1)" stroke-width="1" class="hidden dark:inline" />
|
|
||||||
<line x1="400" y1="350" x2="200" y2="250" stroke="rgba(255,255,255,0.1)" stroke-width="1" class="hidden dark:inline" />
|
|
||||||
<line x1="400" y1="150" x2="400" y2="350" stroke="rgba(255,255,255,0.1)" stroke-width="1" class="hidden dark:inline" />
|
|
||||||
<line x1="200" y1="250" x2="600" y2="250" stroke="rgba(255,255,255,0.1)" stroke-width="1" class="hidden dark:inline" />
|
|
||||||
|
|
||||||
<!-- Pulse animation for some lines -->
|
|
||||||
<line class="animate-pulse" x1="400" y1="150" x2="300" y2="200" stroke="rgba(139, 92, 246, 0.5)" stroke-width="2" />
|
|
||||||
<line class="animate-pulse" x1="400" y1="350" x2="500" y2="300" stroke="rgba(168, 85, 247, 0.5)" stroke-width="2" />
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Network Nodes -->
|
|
||||||
<g class="nodes">
|
|
||||||
<circle cx="400" cy="150" r="15" fill="url(#nodeGradient)" filter="url(#glow)" class="animate-pulse float-animation" />
|
|
||||||
<circle cx="200" cy="250" r="10" fill="url(#nodeGradient)" class="float-animation" />
|
|
||||||
<circle cx="600" cy="250" r="10" fill="url(#nodeGradient)" class="float-animation" />
|
|
||||||
<circle cx="400" cy="350" r="15" fill="url(#nodeGradient)" filter="url(#glow)" class="animate-pulse float-animation" />
|
|
||||||
<circle cx="300" cy="200" r="8" fill="url(#nodeGradient)" class="float-animation" />
|
|
||||||
<circle cx="500" cy="300" r="8" fill="url(#nodeGradient)" class="float-animation" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -241,363 +187,278 @@
|
|||||||
|
|
||||||
<!-- Features Section -->
|
<!-- Features Section -->
|
||||||
<section class="py-20 relative">
|
<section class="py-20 relative">
|
||||||
<div class="tech-line absolute top-0 left-1/2 transform -translate-x-1/2 w-1/3"></div>
|
<div class="mystical-line absolute top-0 left-1/2 transform -translate-x-1/2 w-1/3"></div>
|
||||||
|
|
||||||
<div class="max-w-screen-xl mx-auto px-4 sm:px-6 lg:px-8">
|
<div class="max-w-screen-xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
<div class="text-center mb-16">
|
<div class="text-center mb-16">
|
||||||
<h2 class="section-heading mb-4 text-gray-900 dark:text-white">Was ist <span class="gradient-text">Systades?</span></h2>
|
<h2 class="section-heading mb-4 text-gray-900 dark:text-white">Was ist <span class="gradient-text">Systades?</span></h2>
|
||||||
<p class="text-lg text-gray-700 dark:text-gray-300 max-w-3xl mx-auto">
|
<p class="text-lg text-gray-700 dark:text-gray-300 max-w-3xl mx-auto">
|
||||||
Ein modernes Werkzeug zum Visualisieren, Erforschen und Teilen von Wissen
|
Ein modernes Werkzeug zum Visualisieren, Erforschen und Teilen von Wissen in einem interaktiven
|
||||||
in einer intuitiven, interaktiven Umgebung.
|
Netzwerk, das dir hilft, Verbindungen zu entdecken und dein Wissen zu organisieren.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
<!-- Feature Cards -->
|
||||||
<!-- Feature Card 1 -->
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-8 mb-12">
|
||||||
<div class="feature-card p-8 rounded-3xl hover:-translate-y-3 transform transition-all duration-300">
|
<!-- Feature 1: Visualize -->
|
||||||
<div class="icon mb-6 rounded-2xl shadow-lg">
|
<div class="featured-card rounded-2xl p-6 bg-white/80 dark:bg-gray-800/30 backdrop-blur-sm">
|
||||||
<i class="fa-solid fa-brain"></i>
|
<div class="mb-4 text-purple-600 dark:text-purple-400">
|
||||||
|
<i class="fa-solid fa-diagram-project text-3xl"></i>
|
||||||
</div>
|
</div>
|
||||||
<h3 class="text-xl font-semibold mb-3">Visualisiere Wissen</h3>
|
<h3 class="text-xl font-semibold mb-2 text-gray-900 dark:text-white">Visualisiere Wissen</h3>
|
||||||
<p>
|
<p class="text-gray-700 dark:text-gray-300">
|
||||||
Sieh Wissen als vernetztes System, entdecke Zusammenhänge und erkenne überraschende
|
Organisiere Gedanken und Ideen in einem interaktiven Netzwerk, das komplexe Beziehungen
|
||||||
Verbindungen zwischen verschiedenen Themengebieten.
|
visuell darstellt und neue Verbindungen offenbart.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Feature Card 2 -->
|
<!-- Feature 2: Connect -->
|
||||||
<div class="feature-card p-8 rounded-3xl hover:-translate-y-3 transform transition-all duration-300">
|
<div class="featured-card rounded-2xl p-6 bg-white/80 dark:bg-gray-800/30 backdrop-blur-sm">
|
||||||
<div class="icon mb-6 rounded-2xl shadow-lg">
|
<div class="mb-4 text-indigo-600 dark:text-indigo-400">
|
||||||
<i class="fa-solid fa-lightbulb"></i>
|
<i class="fa-solid fa-network-wired text-3xl"></i>
|
||||||
</div>
|
</div>
|
||||||
<h3 class="text-xl font-semibold mb-3">Teile Gedanken</h3>
|
<h3 class="text-xl font-semibold mb-2 text-gray-900 dark:text-white">Verknüpfe Gedanken</h3>
|
||||||
<p>
|
<p class="text-gray-700 dark:text-gray-300">
|
||||||
Füge deine eigenen Ideen und Perspektiven hinzu. Erstelle Verbindungen zu
|
Entdecke Zusammenhänge zwischen scheinbar unzusammenhängenden Ideen und schaffe dein
|
||||||
vorhandenen Gedanken und bereichere die wachsende Wissensbasis.
|
persönliches Wissensnetzwerk über verschiedene Bereiche hinweg.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Feature Card 3 -->
|
<!-- Feature 3: Share -->
|
||||||
<div class="feature-card p-8 rounded-3xl hover:-translate-y-3 transform transition-all duration-300">
|
<div class="featured-card rounded-2xl p-6 bg-white/80 dark:bg-gray-800/30 backdrop-blur-sm">
|
||||||
<div class="icon mb-6 rounded-2xl shadow-lg">
|
<div class="mb-4 text-purple-600 dark:text-purple-400">
|
||||||
<i class="fa-solid fa-users"></i>
|
<i class="fa-solid fa-share-nodes text-3xl"></i>
|
||||||
</div>
|
</div>
|
||||||
<h3 class="text-xl font-semibold mb-3">Community</h3>
|
<h3 class="text-xl font-semibold mb-2 text-gray-900 dark:text-white">Teile und Entdecke</h3>
|
||||||
<p>
|
<p class="text-gray-700 dark:text-gray-300">
|
||||||
Sei Teil einer Gemeinschaft, die gemeinsam ein verteiltes Wissensarchiv aufbaut
|
Tausche Erkenntnisse mit anderen aus und erweitere dein Wissen durch die
|
||||||
und sich in thematisch fokussierten Bereichen austauscht.
|
Perspektiven und Gedanken der Community.
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Feature Card 4 -->
|
|
||||||
<div class="feature-card p-8 rounded-3xl hover:-translate-y-3 transform transition-all duration-300">
|
|
||||||
<div class="icon mb-6 rounded-2xl shadow-lg">
|
|
||||||
<i class="fa-solid fa-robot"></i>
|
|
||||||
</div>
|
|
||||||
<h3 class="text-xl font-semibold mb-3">KI-Assistenz</h3>
|
|
||||||
<p>
|
|
||||||
Lass dir von künstlicher Intelligenz helfen, neue Zusammenhänge zu entdecken,
|
|
||||||
Inhalte zusammenzufassen und Fragen zu beantworten.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Feature Card 5 -->
|
|
||||||
<div class="feature-card p-8 rounded-3xl hover:-translate-y-3 transform transition-all duration-300">
|
|
||||||
<div class="icon mb-6 rounded-2xl shadow-lg">
|
|
||||||
<i class="fa-solid fa-search"></i>
|
|
||||||
</div>
|
|
||||||
<h3 class="text-xl font-semibold mb-3">Intelligente Suche</h3>
|
|
||||||
<p>
|
|
||||||
Finde genau die Informationen, die du suchst, mit fortschrittlichen Such- und
|
|
||||||
Filterfunktionen für eine präzise Navigation durch das Wissen.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Feature Card 6 -->
|
|
||||||
<div class="feature-card p-8 rounded-3xl hover:-translate-y-3 transform transition-all duration-300">
|
|
||||||
<div class="icon mb-6 rounded-2xl shadow-lg">
|
|
||||||
<i class="fa-solid fa-route"></i>
|
|
||||||
</div>
|
|
||||||
<h3 class="text-xl font-semibold mb-3">Geführte Pfade</h3>
|
|
||||||
<p>
|
|
||||||
Folge kuratierten Lernpfaden durch komplexe Themen oder erschaffe selbst
|
|
||||||
Routen für andere, die deinen Gedankengängen folgen möchten.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- Call to Action Section -->
|
<!-- AI Assistant Preview -->
|
||||||
<section class="py-16 sm:py-20 md:py-24 relative overflow-hidden">
|
<section class="py-20 relative">
|
||||||
<div class="max-w-screen-xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
|
<div class="mystical-line absolute top-0 left-1/2 transform -translate-x-1/2 w-1/3"></div>
|
||||||
<div class="glass-effect p-6 sm:p-8 md:p-12 rounded-3xl transform transition-all duration-500 hover:-translate-y-2 hover:shadow-2xl bg-gradient-to-br from-purple-500/15 to-blue-500/15 backdrop-blur-xl border border-white/10 shadow-lg">
|
|
||||||
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-6">
|
|
||||||
<div class="md:w-2/3">
|
|
||||||
<h2 class="text-2xl sm:text-3xl lg:text-4xl font-bold mb-3 text-gray-900 dark:text-white leading-tight">
|
|
||||||
Bereit, <span class="gradient-text bg-clip-text text-transparent bg-gradient-to-r from-purple-500 to-blue-500">Wissen</span> neu zu entdecken?
|
|
||||||
</h2>
|
|
||||||
<p class="text-gray-700 dark:text-gray-300 text-base sm:text-lg mb-6 md:mb-0 max-w-2xl">
|
|
||||||
Starte jetzt deine Reise durch das Wissensnetzwerk und erschließe neue Perspektiven.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="md:w-1/3 text-center md:text-right">
|
|
||||||
<a href="{{ url_for('mindmap') }}" class="inline-flex items-center justify-center w-full md:w-auto btn-primary font-bold py-3 sm:py-3.5 px-6 sm:px-8 rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1 hover:scale-105 bg-gradient-to-r from-purple-600 to-blue-600 text-white">
|
|
||||||
<span class="flex items-center justify-center">
|
|
||||||
<i class="fa-solid fa-arrow-right mr-2"></i>
|
|
||||||
<span>Zur Mindmap</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Quick Access Section -->
|
|
||||||
<section class="py-16 sm:py-20">
|
|
||||||
<div class="max-w-screen-xl mx-auto px-4 sm:px-6 lg:px-8">
|
<div class="max-w-screen-xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 lg:gap-8">
|
<div class="text-center mb-12">
|
||||||
<!-- Themen-Übersicht -->
|
<h2 class="section-heading mb-4 text-gray-900 dark:text-white">Dein <span class="gradient-text">KI-Assistent</span></h2>
|
||||||
<div class="glass-morphism p-6 sm:p-8 rounded-3xl transition-all duration-500 hover:-translate-y-3 hover:shadow-xl border border-white/10 backdrop-blur-md">
|
<p class="text-lg text-gray-700 dark:text-gray-300 max-w-3xl mx-auto">
|
||||||
<h3 class="text-xl font-bold mb-4 flex items-center text-gray-800 dark:text-white">
|
Unser integrierter KI-Assistent hilft dir, Wissen zu organisieren, Verbindungen zu finden und
|
||||||
<div class="w-10 h-10 sm:w-12 sm:h-12 rounded-2xl bg-gradient-to-r from-violet-500 to-fuchsia-500 flex items-center justify-center mr-3 sm:mr-4 shadow-md transform transition-transform duration-300 hover:scale-110">
|
Einsichten zu gewinnen.
|
||||||
<i class="fa-solid fa-fire text-white text-base sm:text-lg"></i>
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Chat Interface Preview -->
|
||||||
|
<div class="max-w-3xl mx-auto embedded-chat">
|
||||||
|
<!-- Chat Header -->
|
||||||
|
<div class="p-4 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="w-8 h-8 rounded-full bg-gradient-to-r from-purple-600 to-indigo-600 flex items-center justify-center text-white mr-3">
|
||||||
|
<i class="fa-solid fa-robot text-sm"></i>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-lg sm:text-xl md:text-2xl">Themen-Übersicht</span>
|
<span class="font-medium text-gray-800 dark:text-gray-200">Systades Assistent</span>
|
||||||
</h3>
|
|
||||||
<div class="space-y-3 sm:space-y-4 mb-6">
|
|
||||||
<a href="{{ url_for('mindmap') }}" class="flex items-center p-3 sm:p-3.5 rounded-xl hover:bg-gray-100/50 dark:hover:bg-white/5 transition-all duration-200 group">
|
|
||||||
<div class="w-3 h-3 rounded-full bg-purple-400 mr-3 group-hover:scale-125 transition-transform"></div>
|
|
||||||
<div class="flex-grow">
|
|
||||||
<p class="font-medium text-gray-800 dark:text-gray-200">Wissensbereiche <span class="text-xs text-gray-500">(12)</span></p>
|
|
||||||
<p class="text-xs sm:text-sm text-gray-500 dark:text-gray-400">Überblick über Themenbereiche</p>
|
|
||||||
</div>
|
|
||||||
<i class="fa-solid fa-chevron-right text-gray-500 group-hover:translate-x-1 transition-transform"></i>
|
|
||||||
</a>
|
|
||||||
<a href="{{ url_for('search_thoughts_page') }}" class="flex items-center p-3 sm:p-3.5 rounded-xl hover:bg-gray-100/50 dark:hover:bg-white/5 transition-all duration-200 group">
|
|
||||||
<div class="w-3 h-3 rounded-full bg-blue-400 mr-3 group-hover:scale-125 transition-transform"></div>
|
|
||||||
<div class="flex-grow">
|
|
||||||
<p class="font-medium text-gray-800 dark:text-gray-200">Gedanken <span class="text-xs text-gray-500">(87)</span></p>
|
|
||||||
<p class="text-xs sm:text-sm text-gray-500 dark:text-gray-400">Konkrete Einträge durchsuchen</p>
|
|
||||||
</div>
|
|
||||||
<i class="fa-solid fa-chevron-right text-gray-500 group-hover:translate-x-1 transition-transform"></i>
|
|
||||||
</a>
|
|
||||||
<a href="#" class="flex items-center p-3 sm:p-3.5 rounded-xl hover:bg-gray-100/50 dark:hover:bg-white/5 transition-all duration-200 group">
|
|
||||||
<div class="w-3 h-3 rounded-full bg-green-400 mr-3 group-hover:scale-125 transition-transform"></div>
|
|
||||||
<div class="flex-grow">
|
|
||||||
<p class="font-medium text-gray-800 dark:text-gray-200">Verbindungen <span class="text-xs text-gray-500">(34)</span></p>
|
|
||||||
<p class="text-xs sm:text-sm text-gray-500 dark:text-gray-400">Beziehungen zwischen Gedanken</p>
|
|
||||||
</div>
|
|
||||||
<i class="fa-solid fa-chevron-right text-gray-500 group-hover:translate-x-1 transition-transform"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
<a href="{{ url_for('search_thoughts_page') }}" class="btn-primary w-full text-center rounded-xl py-3 sm:py-3.5 transform transition-all duration-300 hover:-translate-y-1 hover:shadow-lg flex items-center justify-center">
|
<div>
|
||||||
<span>Alle Themen entdecken</span>
|
<button class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
|
||||||
<i class="fa-solid fa-arrow-right ml-2"></i>
|
<i class="fa-solid fa-expand"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Chat Messages -->
|
||||||
|
<div id="embedded-chat-messages" class="border-b border-gray-200 dark:border-gray-700">
|
||||||
|
<!-- Assistant Message -->
|
||||||
|
<div class="mb-4 flex">
|
||||||
|
<div class="w-8 h-8 rounded-full bg-gradient-to-r from-purple-600 to-indigo-600 flex items-center justify-center text-white mr-2 flex-shrink-0">
|
||||||
|
<i class="fa-solid fa-robot text-sm"></i>
|
||||||
|
</div>
|
||||||
|
<div class="bg-gray-100 dark:bg-gray-800 rounded-lg p-3 max-w-[80%]">
|
||||||
|
<p class="text-gray-700 dark:text-gray-300">
|
||||||
|
Hallo! Ich bin dein Systades-Assistent. Wie kann ich dir heute helfen? Du kannst mir Fragen zu deinen Gedanken stellen,
|
||||||
|
Verbindungen zwischen Konzepten finden oder Informationen zusammenfassen lassen.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- User Message -->
|
||||||
|
<div class="mb-4 flex justify-end">
|
||||||
|
<div class="bg-purple-100 dark:bg-purple-900/30 rounded-lg p-3 max-w-[80%]">
|
||||||
|
<p class="text-gray-800 dark:text-gray-200">
|
||||||
|
Kann ich mit deiner Hilfe eine Mindmap zum Thema Künstliche Intelligenz erstellen?
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="w-8 h-8 rounded-full bg-gray-300 dark:bg-gray-600 flex items-center justify-center text-gray-700 dark:text-gray-300 ml-2 flex-shrink-0">
|
||||||
|
<i class="fa-solid fa-user text-sm"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Assistant Typing -->
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="w-8 h-8 rounded-full bg-gradient-to-r from-purple-600 to-indigo-600 flex items-center justify-center text-white mr-2 flex-shrink-0">
|
||||||
|
<i class="fa-solid fa-robot text-sm"></i>
|
||||||
|
</div>
|
||||||
|
<div class="bg-gray-100 dark:bg-gray-800 rounded-lg p-3">
|
||||||
|
<div class="typing-dots">
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Chat Input -->
|
||||||
|
<div class="p-4">
|
||||||
|
<div class="flex">
|
||||||
|
<input type="text" placeholder="Stelle eine Frage..." class="mystical-input flex-grow" disabled>
|
||||||
|
<button class="ml-2 bg-gradient-to-r from-purple-600 to-indigo-600 text-white p-2 rounded-lg disabled:opacity-50" disabled>
|
||||||
|
<i class="fa-solid fa-paper-plane"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<!-- Quick Queries -->
|
||||||
|
<div class="mt-3 flex flex-wrap gap-2">
|
||||||
|
<span class="text-xs text-gray-500 dark:text-gray-400 mr-1">Beispiele:</span>
|
||||||
|
<button class="quick-query-btn text-xs px-2 py-1 rounded-full bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300" disabled>Verbindungen finden</button>
|
||||||
|
<button class="quick-query-btn text-xs px-2 py-1 rounded-full bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300" disabled>Zusammenfassen</button>
|
||||||
|
<button class="quick-query-btn text-xs px-2 py-1 rounded-full bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300" disabled>Mindmap erstellen</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Try it Button -->
|
||||||
|
<div class="text-center mt-10">
|
||||||
|
<button onclick="window.MindMap && window.MindMap.assistant && window.MindMap.assistant.toggleAssistant(true)"
|
||||||
|
class="mystical-button mystical-button-primary inline-flex items-center">
|
||||||
|
<i class="fa-solid fa-robot mr-2"></i>
|
||||||
|
KI-Assistenten ausprobieren
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Getting Started Section -->
|
||||||
|
<section class="py-20 relative">
|
||||||
|
<div class="mystical-line absolute top-0 left-1/2 transform -translate-x-1/2 w-1/3"></div>
|
||||||
|
|
||||||
|
<div class="max-w-screen-xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
|
<div class="text-center mb-12">
|
||||||
|
<h2 class="section-heading mb-4 text-gray-900 dark:text-white">So <span class="gradient-text">funktioniert's</span></h2>
|
||||||
|
<p class="text-lg text-gray-700 dark:text-gray-300 max-w-3xl mx-auto">
|
||||||
|
In wenigen einfachen Schritten kannst du mit Systades beginnen, dein Wissen zu organisieren.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Step Cards -->
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||||
|
<!-- Step 1 -->
|
||||||
|
<div class="featured-card rounded-2xl p-6 bg-white/80 dark:bg-gray-800/30 backdrop-blur-sm relative overflow-hidden">
|
||||||
|
<div class="absolute top-4 right-4 w-10 h-10 rounded-full bg-purple-100 dark:bg-purple-900/30 flex items-center justify-center text-purple-600 dark:text-purple-400 font-bold">
|
||||||
|
1
|
||||||
|
</div>
|
||||||
|
<h3 class="text-xl font-semibold mb-4 text-gray-900 dark:text-white">Konto erstellen</h3>
|
||||||
|
<p class="text-gray-700 dark:text-gray-300 mb-4">
|
||||||
|
Registriere dich für ein kostenloses Konto, um deine persönliche Wissenslandschaft zu erstellen.
|
||||||
|
</p>
|
||||||
|
{% if not current_user.is_authenticated %}
|
||||||
|
<a href="{{ url_for('register') }}" class="text-purple-600 dark:text-purple-400 hover:underline">
|
||||||
|
Jetzt registrieren <i class="fa-solid fa-arrow-right ml-1"></i>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Step 2 -->
|
||||||
|
<div class="featured-card rounded-2xl p-6 bg-white/80 dark:bg-gray-800/30 backdrop-blur-sm relative overflow-hidden">
|
||||||
|
<div class="absolute top-4 right-4 w-10 h-10 rounded-full bg-purple-100 dark:bg-purple-900/30 flex items-center justify-center text-purple-600 dark:text-purple-400 font-bold">
|
||||||
|
2
|
||||||
|
</div>
|
||||||
|
<h3 class="text-xl font-semibold mb-4 text-gray-900 dark:text-white">Mindmap erkunden</h3>
|
||||||
|
<p class="text-gray-700 dark:text-gray-300 mb-4">
|
||||||
|
Entdecke die öffentliche Wissensmindmap und füge Knoten zu deiner persönlichen Landschaft hinzu.
|
||||||
|
</p>
|
||||||
|
<a href="{{ url_for('mindmap') }}" class="text-purple-600 dark:text-purple-400 hover:underline">
|
||||||
|
Zur Mindmap <i class="fa-solid fa-arrow-right ml-1"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- KI-Assistent mit eingebettetem Chat -->
|
<!-- Step 3 -->
|
||||||
<div class="glass-morphism p-6 sm:p-8 rounded-3xl transition-all duration-500 hover:-translate-y-1 hover:shadow-xl backdrop-blur-md border border-white/10">
|
<div class="featured-card rounded-2xl p-6 bg-white/80 dark:bg-gray-800/30 backdrop-blur-sm relative overflow-hidden">
|
||||||
<h3 class="text-xl md:text-2xl font-bold mb-4 flex flex-wrap sm:flex-nowrap items-center text-gray-800 dark:text-white">
|
<div class="absolute top-4 right-4 w-10 h-10 rounded-full bg-purple-100 dark:bg-purple-900/30 flex items-center justify-center text-purple-600 dark:text-purple-400 font-bold">
|
||||||
<div class="w-10 h-10 sm:w-12 sm:h-12 rounded-2xl bg-gradient-to-r from-purple-600 to-blue-600 flex items-center justify-center mr-3 sm:mr-4 shadow-lg transform transition-transform duration-300 hover:scale-110">
|
3
|
||||||
<i class="fa-solid fa-robot text-white text-base sm:text-lg"></i>
|
|
||||||
</div>
|
|
||||||
<span class="mt-1 sm:mt-0">KI-Assistent</span>
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<!-- Eingebettetes Chat-Interface -->
|
|
||||||
<div id="embedded-assistant" class="rounded-xl border border-gray-200/50 dark:border-gray-700/50 overflow-hidden flex flex-col h-[300px]">
|
|
||||||
<!-- Chat Verlauf -->
|
|
||||||
<div id="embedded-chat-messages" class="flex-grow p-4 overflow-y-auto space-y-3 bg-white/70 dark:bg-gray-800/70">
|
|
||||||
<!-- Begrüßungsnachricht -->
|
|
||||||
<div class="flex items-start space-x-2">
|
|
||||||
<div class="w-8 h-8 rounded-full bg-gradient-to-r from-purple-600 to-blue-600 flex items-center justify-center flex-shrink-0">
|
|
||||||
<i class="fa-solid fa-robot text-white text-xs"></i>
|
|
||||||
</div>
|
|
||||||
<div class="max-w-[85%] bg-purple-100 dark:bg-gray-700 p-3 rounded-xl rounded-tl-none shadow-sm">
|
|
||||||
<p class="text-sm text-gray-700 dark:text-gray-200">Hallo! Ich bin dein KI-Assistent. Wie kann ich dir helfen?</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Chat Eingabe -->
|
|
||||||
<div class="p-3 border-t border-gray-200/70 dark:border-gray-700/70 bg-gray-50/90 dark:bg-gray-800/90">
|
|
||||||
<form id="embedded-chat-form" class="flex items-center space-x-2">
|
|
||||||
<input type="text" id="embedded-chat-input"
|
|
||||||
placeholder="Stelle eine Frage..."
|
|
||||||
class="flex-grow px-4 py-2 rounded-xl border bg-white/90 dark:bg-gray-700/90 border-gray-300 dark:border-gray-600 shadow-sm focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent transition-all duration-200 placeholder-gray-400 dark:placeholder-gray-500 text-gray-700 dark:text-gray-200">
|
|
||||||
<button type="submit"
|
|
||||||
class="p-2 rounded-xl bg-gradient-to-r from-purple-600 to-blue-600 text-white shadow-md hover:shadow-lg transition-all duration-200 hover:-translate-y-0.5">
|
|
||||||
<i class="fa-solid fa-paper-plane"></i>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<h3 class="text-xl font-semibold mb-4 text-gray-900 dark:text-white">Gedanken teilen</h3>
|
||||||
<!-- Schnelllinks unter dem Chat -->
|
<p class="text-gray-700 dark:text-gray-300 mb-4">
|
||||||
<div class="mt-4 flex flex-wrap gap-2">
|
Teile deine eigenen Gedanken, verbinde sie mit vorhandenen Knoten und baue das kollektive Wissen aus.
|
||||||
<button class="quick-query-btn px-2 sm:px-3 py-1 sm:py-1.5 bg-gray-100 hover:bg-gray-200 dark:bg-gray-800/70 dark:hover:bg-gray-700/80 rounded-lg sm:rounded-xl text-xs text-gray-700 dark:text-gray-300 transition-all duration-200 hover:-translate-y-0.5 shadow-sm hover:shadow">
|
</p>
|
||||||
Was ist Systades?
|
{% if current_user.is_authenticated %}
|
||||||
</button>
|
<a href="{{ url_for('profile') }}" class="text-purple-600 dark:text-purple-400 hover:underline">
|
||||||
<button class="quick-query-btn px-2 sm:px-3 py-1 sm:py-1.5 bg-gray-100 hover:bg-gray-200 dark:bg-gray-800/70 dark:hover:bg-gray-700/80 rounded-lg sm:rounded-xl text-xs text-gray-700 dark:text-gray-300 transition-all duration-200 hover:-translate-y-0.5 shadow-sm hover:shadow">
|
Zum Profil <i class="fa-solid fa-arrow-right ml-1"></i>
|
||||||
Wie erstelle ich eine Mindmap?
|
</a>
|
||||||
</button>
|
{% else %}
|
||||||
<button class="quick-query-btn px-2 sm:px-3 py-1 sm:py-1.5 bg-gray-100 hover:bg-gray-200 dark:bg-gray-800/70 dark:hover:bg-gray-700/80 rounded-lg sm:rounded-xl text-xs text-gray-700 dark:text-gray-300 transition-all duration-200 hover:-translate-y-0.5 shadow-sm hover:shadow">
|
<a href="{{ url_for('login') }}" class="text-purple-600 dark:text-purple-400 hover:underline">
|
||||||
Zeige neueste Gedanken
|
Jetzt anmelden <i class="fa-solid fa-arrow-right ml-1"></i>
|
||||||
</button>
|
</a>
|
||||||
</div>
|
{% endif %}
|
||||||
|
|
||||||
<!-- Vollständigen KI-Chat öffnen -->
|
|
||||||
<button onclick="window.MindMap.assistant.toggleAssistant(true)" class="mt-4 btn-primary w-full text-center rounded-xl py-2 sm:py-2.5 shadow-md hover:shadow-lg transition-all duration-300 hover:-translate-y-1 flex items-center justify-center">
|
|
||||||
<i class="fa-solid fa-expand mr-2"></i>
|
|
||||||
<span>Chat in Vollansicht öffnen</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- Call to Action -->
|
||||||
|
<section class="py-20 relative">
|
||||||
|
<div class="mystical-line absolute top-0 left-1/2 transform -translate-x-1/2 w-1/3"></div>
|
||||||
|
|
||||||
|
<div class="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
|
||||||
|
<h2 class="section-heading mb-6 text-gray-900 dark:text-white">Bereit, dein <span class="gradient-text">Wissen zu vernetzen</span>?</h2>
|
||||||
|
<p class="text-lg text-gray-700 dark:text-gray-300 mb-8">
|
||||||
|
Tritt unserer wachsenden Community bei und entdecke eine neue Art, Wissen zu organisieren und zu teilen.
|
||||||
|
</p>
|
||||||
|
<div class="flex flex-col sm:flex-row gap-4 justify-center">
|
||||||
|
<a href="{{ url_for('mindmap') }}" class="mystical-button mystical-button-primary">
|
||||||
|
<i class="fa-solid fa-diagram-project mr-2"></i> Mindmap erkunden
|
||||||
|
</a>
|
||||||
|
{% if not current_user.is_authenticated %}
|
||||||
|
<a href="{{ url_for('register') }}" class="mystical-button mystical-button-secondary">
|
||||||
|
<i class="fa-solid fa-user-plus mr-2"></i> Konto erstellen
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
<!-- JavaScript für eingebetteten Chat -->
|
{% block extra_js %}
|
||||||
{% block scripts %}
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
// Simulate assistant typing and response
|
||||||
// Warten bis MindMap und der Assistent initialisiert sind
|
setTimeout(() => {
|
||||||
const waitForAssistant = setInterval(() => {
|
const chatMessages = document.getElementById('embedded-chat-messages');
|
||||||
if (window.MindMap && window.MindMap.assistant) {
|
const typingIndicator = chatMessages.querySelector('.flex:last-child');
|
||||||
clearInterval(waitForAssistant);
|
|
||||||
initEmbeddedChat();
|
|
||||||
}
|
|
||||||
}, 200);
|
|
||||||
|
|
||||||
function initEmbeddedChat() {
|
if (typingIndicator) {
|
||||||
const chatForm = document.getElementById('embedded-chat-form');
|
// Create assistant response
|
||||||
const chatInput = document.getElementById('embedded-chat-input');
|
const response = document.createElement('div');
|
||||||
const messagesContainer = document.getElementById('embedded-chat-messages');
|
response.className = 'mb-4 flex';
|
||||||
const quickQueryBtns = document.querySelectorAll('.quick-query-btn');
|
response.innerHTML = `
|
||||||
|
<div class="w-8 h-8 rounded-full bg-gradient-to-r from-purple-600 to-indigo-600 flex items-center justify-center text-white mr-2 flex-shrink-0">
|
||||||
|
<i class="fa-solid fa-robot text-sm"></i>
|
||||||
|
</div>
|
||||||
|
<div class="bg-gray-100 dark:bg-gray-800 rounded-lg p-3 max-w-[80%]">
|
||||||
|
<p class="text-gray-700 dark:text-gray-300">
|
||||||
|
Natürlich! Ich kann dir dabei helfen, eine Mindmap zum Thema KI zu erstellen. Beginnen wir mit zentralen Konzepten wie Machine Learning, Neural Networks und Natural Language Processing. Möchtest du einen bestimmten Aspekt der KI genauer betrachten?
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
// Event-Listener für das Chat-Formular
|
// Remove typing indicator and add response
|
||||||
chatForm.addEventListener('submit', function(e) {
|
typingIndicator.remove();
|
||||||
e.preventDefault();
|
chatMessages.appendChild(response);
|
||||||
const userMessage = chatInput.value.trim();
|
|
||||||
if (!userMessage) return;
|
|
||||||
|
|
||||||
// Nachricht des Benutzers anzeigen
|
// Scroll to bottom
|
||||||
appendMessage('user', userMessage);
|
chatMessages.scrollTop = chatMessages.scrollHeight;
|
||||||
chatInput.value = '';
|
|
||||||
|
|
||||||
// Anzeigen, dass der Assistent antwortet
|
|
||||||
const typingIndicator = appendTypingIndicator();
|
|
||||||
|
|
||||||
// API-Anfrage an den Assistenten senden
|
|
||||||
sendToAssistant(userMessage)
|
|
||||||
.then(response => {
|
|
||||||
// Entferne Tipp-Indikator
|
|
||||||
typingIndicator.remove();
|
|
||||||
// Zeige Antwort des Assistenten an
|
|
||||||
appendMessage('assistant', response);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
typingIndicator.remove();
|
|
||||||
appendMessage('assistant', 'Es tut mir leid, ich konnte deine Nachricht nicht verarbeiten. Bitte versuche es später noch einmal.');
|
|
||||||
console.error('Fehler bei der Kommunikation mit dem Assistenten:', error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Schnellabfragen-Buttons
|
|
||||||
quickQueryBtns.forEach(btn => {
|
|
||||||
btn.addEventListener('click', function() {
|
|
||||||
const query = this.textContent.trim();
|
|
||||||
chatInput.value = query;
|
|
||||||
chatForm.dispatchEvent(new Event('submit'));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Funktion zum Hinzufügen einer Nachricht zum Chat
|
|
||||||
function appendMessage(sender, message) {
|
|
||||||
const messageElement = document.createElement('div');
|
|
||||||
messageElement.className = 'flex items-start space-x-2';
|
|
||||||
|
|
||||||
if (sender === 'user') {
|
|
||||||
messageElement.innerHTML = `
|
|
||||||
<div class="flex-grow"></div>
|
|
||||||
<div class="max-w-[85%] bg-blue-100 dark:bg-blue-900/40 p-3 rounded-xl rounded-tr-none shadow-sm">
|
|
||||||
<p class="text-sm text-gray-700 dark:text-gray-200">${message}</p>
|
|
||||||
</div>
|
|
||||||
<div class="w-8 h-8 rounded-full bg-gradient-to-r from-blue-500 to-indigo-500 flex items-center justify-center flex-shrink-0">
|
|
||||||
<i class="fa-solid fa-user text-white text-xs"></i>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
} else {
|
|
||||||
messageElement.innerHTML = `
|
|
||||||
<div class="w-8 h-8 rounded-full bg-gradient-to-r from-purple-600 to-blue-600 flex items-center justify-center flex-shrink-0">
|
|
||||||
<i class="fa-solid fa-robot text-white text-xs"></i>
|
|
||||||
</div>
|
|
||||||
<div class="max-w-[85%] bg-purple-100 dark:bg-gray-700 p-3 rounded-xl rounded-tl-none shadow-sm">
|
|
||||||
<p class="text-sm text-gray-700 dark:text-gray-200">${message}</p>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
messagesContainer.appendChild(messageElement);
|
|
||||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tipp-Indikator für "Assistent schreibt..."
|
|
||||||
function appendTypingIndicator() {
|
|
||||||
const indicatorElement = document.createElement('div');
|
|
||||||
indicatorElement.className = 'flex items-start space-x-2 typing-indicator';
|
|
||||||
indicatorElement.innerHTML = `
|
|
||||||
<div class="w-8 h-8 rounded-full bg-gradient-to-r from-purple-600 to-blue-600 flex items-center justify-center flex-shrink-0">
|
|
||||||
<i class="fa-solid fa-robot text-white text-xs"></i>
|
|
||||||
</div>
|
|
||||||
<div class="max-w-[85%] bg-purple-100 dark:bg-gray-700 p-3 rounded-xl rounded-tl-none shadow-sm">
|
|
||||||
<p class="text-sm text-gray-500 dark:text-gray-400 flex items-center">
|
|
||||||
<span class="mr-1">Tipp</span>
|
|
||||||
<span class="typing-dots flex space-x-1">
|
|
||||||
<span class="w-1.5 h-1.5 bg-gray-500 dark:bg-gray-400 rounded-full animate-bounce" style="animation-delay: 0ms;"></span>
|
|
||||||
<span class="w-1.5 h-1.5 bg-gray-500 dark:bg-gray-400 rounded-full animate-bounce" style="animation-delay: 150ms;"></span>
|
|
||||||
<span class="w-1.5 h-1.5 bg-gray-500 dark:bg-gray-400 rounded-full animate-bounce" style="animation-delay: 300ms;"></span>
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
messagesContainer.appendChild(indicatorElement);
|
|
||||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
|
||||||
return indicatorElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sende Nachricht an den Assistenten und erhalte Antwort
|
|
||||||
async function sendToAssistant(message) {
|
|
||||||
try {
|
|
||||||
const response = await fetch('/api/assistant', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
messages: [
|
|
||||||
{ role: "system", content: "Du bist ein hilfreicher Assistent für das Wissensnetzwerk Systades." },
|
|
||||||
{ role: "user", content: message }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(data.error || 'Unbekannter Fehler');
|
|
||||||
}
|
|
||||||
|
|
||||||
return data.response || data.answer || 'Ich habe keine Antwort erhalten.';
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Fehler bei der API-Anfrage:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}, 3000);
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user