feat: update app logic and improve mindmap functionality

This commit is contained in:
2025-05-12 19:27:18 +01:00
parent a1bd999c6a
commit dec30e4681
4 changed files with 2142 additions and 1214 deletions

Binary file not shown.

876
app.py
View File

@@ -26,6 +26,7 @@ import sqlalchemy
import ssl
import certifi
import os
from sqlalchemy.orm import joinedload
# Modelle importieren
from models import (
@@ -2342,749 +2343,184 @@ if __name__ == '__main__':
db.create_all()
socketio.run(app, debug=True, host='0.0.0.0')
@app.route('/api/mindmap/science')
def get_science_mindmap():
"""
API-Endpunkt für die Wissenschaft-Mindmap
"""
def get_category_mindmap_data(category_name):
"""Generische Funktion zum Abrufen der Mindmap-Daten für eine Kategorie."""
try:
# Stelle sicher, dass wir Kategorien haben
if Category.query.count() == 0:
create_default_categories()
# Kategorie mit allen Unterkategorien in einer Abfrage laden
category = Category.query.filter_by(name=category_name).options(
joinedload(Category.children)
).first_or_404()
# Überprüfe, ob wir bereits einen "Wissen"-Knoten haben
wissen_node = MindMapNode.query.filter_by(name="Wissen").first()
# Basis-Knoten erstellen
nodes = [{
'id': f'cat_{category.id}',
'name': category.name,
'description': category.description or '',
'color_code': category.color_code or '#9F7AEA',
'is_center': True,
'has_children': bool(category.children),
'icon': category.icon or 'fa-solid fa-circle'
}]
# Wenn kein "Wissen"-Knoten existiert, erstelle ihn
if not wissen_node:
wissen_node = MindMapNode(
name="Wissen",
description="Zentrale Wissensbasis",
color_code="#4299E1",
is_public=True
)
db.session.add(wissen_node)
db.session.commit()
# Unterkategorien hinzufügen
for subcat in category.children:
nodes.append({
'id': f'cat_{subcat.id}',
'name': subcat.name,
'description': subcat.description or '',
'color_code': subcat.color_code or '#9F7AEA',
'category': category_name,
'has_children': bool(subcat.children),
'icon': subcat.icon or 'fa-solid fa-circle'
})
# Hole alle Kategorien und Knoten
categories = Category.query.filter_by(parent_id=None).all()
category_tree = [build_category_tree(cat) for cat in categories]
# Hole alle Mindmap-Knoten außer dem "Wissen"-Knoten
nodes = MindMapNode.query.filter(MindMapNode.id != wissen_node.id).all()
# Vorbereiten der Node- und Edge-Arrays für die Antwort
node_data = []
edge_data = []
# Zuerst den "Wissen"-Knoten hinzufügen
node_data.append({
'id': wissen_node.id,
'name': wissen_node.name,
'description': wissen_node.description or '',
'color_code': wissen_node.color_code or '#4299E1',
'thought_count': len(wissen_node.thoughts),
'category_id': wissen_node.category_id
})
# Dann die anderen Knoten
for node in nodes:
node_obj = {
'id': node.id,
'name': node.name,
'description': node.description or '',
'color_code': node.color_code or '#9F7AEA',
'thought_count': len(node.thoughts),
'category_id': node.category_id
}
# Verbinde alle Top-Level-Knoten mit dem Wissen-Knoten
if not node.parents.all():
edge_data.append({
'source': wissen_node.id,
'target': node.id
})
# Verbindungen zwischen vorhandenen Knoten hinzufügen
node_children = node.children.all()
for child in node_children:
edge_data.append({
'source': node.id,
'target': child.id
})
node_data.append(node_obj)
# Kanten erstellen (vereinheitlichte Schlüssel)
edges = [{
'source': f'cat_{category.id}',
'target': f'cat_{subcat.id}',
'strength': 0.8
} for subcat in category.children]
return jsonify({
'success': True,
'categories': category_tree,
'nodes': node_data,
'edges': edge_data
'nodes': nodes,
'edges': edges
})
except Exception as e:
print(f"Fehler beim Neuladen der Mindmap: {str(e)}")
print(f"Fehler beim Abrufen der {category_name}-Mindmap: {str(e)}")
return jsonify({
'success': False,
'error': 'Datenbankverbindung konnte nicht hergestellt werden'
'error': f'{category_name}-Mindmap konnte nicht geladen werden',
'details': str(e)
}), 500
@app.route('/api/mindmap/root')
def get_root_mindmap_data():
"""Liefert die Daten für die Root-Mindmap."""
try:
# Hauptkategorien mit Unterkategorien in einer Abfrage laden
categories = Category.query.filter_by(parent_id=None).options(
joinedload(Category.children)
).all()
# Basis-Knoten erstellen
nodes = [{
'id': 'root',
'name': 'Wissen',
'description': 'Zentrale Wissensbasis',
'color_code': '#4299E1',
'is_center': True,
'has_children': bool(categories),
'icon': 'fa-solid fa-circle'
}]
# Kategorien als Knoten hinzufügen
for category in categories:
nodes.append({
'id': f'cat_{category.id}',
'name': category.name,
'description': category.description or '',
'color_code': category.color_code or '#9F7AEA',
'category': category.name,
'has_children': bool(category.children),
'icon': category.icon or 'fa-solid fa-circle'
})
# Kanten erstellen (vereinheitlichte Schlüssel)
edges = [{
'source': 'root',
'target': f'cat_{category.id}',
'strength': 0.8
} for category in categories]
return jsonify({
'success': True,
'nodes': nodes,
'edges': edges
})
except Exception as e:
print(f"Fehler beim Abrufen der Root-Mindmap: {str(e)}")
return jsonify({
'success': False,
'error': 'Root-Mindmap konnte nicht geladen werden',
'details': str(e)
}), 500
# Spezifische Routen für Kategorien
@app.route('/api/mindmap/philosophy')
def get_philosophy_mindmap_data():
return get_category_mindmap_data('Philosophie')
@app.route('/api/mindmap/science')
def get_science_mindmap_data():
return get_category_mindmap_data('Wissenschaft')
@app.route('/api/mindmap/technology')
def get_technology_mindmap():
"""
API-Endpunkt für die Technologie-Mindmap
"""
try:
# Stelle sicher, dass wir Kategorien haben
if Category.query.count() == 0:
create_default_categories()
# Überprüfe, ob wir bereits einen "Wissen"-Knoten haben
wissen_node = MindMapNode.query.filter_by(name="Wissen").first()
# Wenn kein "Wissen"-Knoten existiert, erstelle ihn
if not wissen_node:
wissen_node = MindMapNode(
name="Wissen",
description="Zentrale Wissensbasis",
color_code="#4299E1",
is_public=True
)
db.session.add(wissen_node)
db.session.commit()
# Hole alle Kategorien und Knoten
categories = Category.query.filter_by(parent_id=None).all()
category_tree = [build_category_tree(cat) for cat in categories]
# Hole alle Mindmap-Knoten außer dem "Wissen"-Knoten
nodes = MindMapNode.query.filter(MindMapNode.id != wissen_node.id).all()
# Vorbereiten der Node- und Edge-Arrays für die Antwort
node_data = []
edge_data = []
# Zuerst den "Wissen"-Knoten hinzufügen
node_data.append({
'id': wissen_node.id,
'name': wissen_node.name,
'description': wissen_node.description or '',
'color_code': wissen_node.color_code or '#4299E1',
'thought_count': len(wissen_node.thoughts),
'category_id': wissen_node.category_id
})
# Dann die anderen Knoten
for node in nodes:
node_obj = {
'id': node.id,
'name': node.name,
'description': node.description or '',
'color_code': node.color_code or '#9F7AEA',
'thought_count': len(node.thoughts),
'category_id': node.category_id
}
# Verbinde alle Top-Level-Knoten mit dem Wissen-Knoten
if not node.parents.all():
edge_data.append({
'source': wissen_node.id,
'target': node.id
})
# Verbindungen zwischen vorhandenen Knoten hinzufügen
node_children = node.children.all()
for child in node_children:
edge_data.append({
'source': node.id,
'target': child.id
})
node_data.append(node_obj)
return jsonify({
'success': True,
'categories': category_tree,
'nodes': node_data,
'edges': edge_data
})
except Exception as e:
print(f"Fehler beim Neuladen der Mindmap: {str(e)}")
return jsonify({
'success': False,
'error': 'Datenbankverbindung konnte nicht hergestellt werden'
}), 500
def get_technology_mindmap_data():
return get_category_mindmap_data('Technologie')
@app.route('/api/mindmap/arts')
def get_arts_mindmap():
"""
API-Endpunkt für die Künste-Mindmap
"""
try:
# Stelle sicher, dass wir Kategorien haben
if Category.query.count() == 0:
create_default_categories()
# Überprüfe, ob wir bereits einen "Wissen"-Knoten haben
wissen_node = MindMapNode.query.filter_by(name="Wissen").first()
# Wenn kein "Wissen"-Knoten existiert, erstelle ihn
if not wissen_node:
wissen_node = MindMapNode(
name="Wissen",
description="Zentrale Wissensbasis",
color_code="#4299E1",
is_public=True
)
db.session.add(wissen_node)
db.session.commit()
# Hole alle Kategorien und Knoten
categories = Category.query.filter_by(parent_id=None).all()
category_tree = [build_category_tree(cat) for cat in categories]
# Hole alle Mindmap-Knoten außer dem "Wissen"-Knoten
nodes = MindMapNode.query.filter(MindMapNode.id != wissen_node.id).all()
# Vorbereiten der Node- und Edge-Arrays für die Antwort
node_data = []
edge_data = []
# Zuerst den "Wissen"-Knoten hinzufügen
node_data.append({
'id': wissen_node.id,
'name': wissen_node.name,
'description': wissen_node.description or '',
'color_code': wissen_node.color_code or '#4299E1',
'thought_count': len(wissen_node.thoughts),
'category_id': wissen_node.category_id
})
# Dann die anderen Knoten
for node in nodes:
node_obj = {
'id': node.id,
'name': node.name,
'description': node.description or '',
'color_code': node.color_code or '#9F7AEA',
'thought_count': len(node.thoughts),
'category_id': node.category_id
}
# Verbinde alle Top-Level-Knoten mit dem Wissen-Knoten
if not node.parents.all():
edge_data.append({
'source': wissen_node.id,
'target': node.id
})
# Verbindungen zwischen vorhandenen Knoten hinzufügen
node_children = node.children.all()
for child in node_children:
edge_data.append({
'source': node.id,
'target': child.id
})
node_data.append(node_obj)
return jsonify({
'success': True,
'categories': category_tree,
'nodes': node_data,
'edges': edge_data
})
except Exception as e:
print(f"Fehler beim Neuladen der Mindmap: {str(e)}")
return jsonify({
'success': False,
'error': 'Datenbankverbindung konnte nicht hergestellt werden'
}), 500
def get_arts_mindmap_data():
return get_category_mindmap_data('Künste')
@app.route('/api/mindmap/philosophy')
def get_philosophy_mindmap():
"""
API-Endpunkt für die Philosophie-Mindmap
"""
try:
# Stelle sicher, dass wir Kategorien haben
if Category.query.count() == 0:
create_default_categories()
# Überprüfe, ob wir bereits einen "Wissen"-Knoten haben
wissen_node = MindMapNode.query.filter_by(name="Wissen").first()
# Wenn kein "Wissen"-Knoten existiert, erstelle ihn
if not wissen_node:
wissen_node = MindMapNode(
name="Wissen",
description="Zentrale Wissensbasis",
color_code="#4299E1",
is_public=True
)
db.session.add(wissen_node)
db.session.commit()
# Hole alle Kategorien und Knoten
categories = Category.query.filter_by(parent_id=None).all()
category_tree = [build_category_tree(cat) for cat in categories]
# Hole alle Mindmap-Knoten außer dem "Wissen"-Knoten
nodes = MindMapNode.query.filter(MindMapNode.id != wissen_node.id).all()
# Vorbereiten der Node- und Edge-Arrays für die Antwort
node_data = []
edge_data = []
# Zuerst den "Wissen"-Knoten hinzufügen
node_data.append({
'id': wissen_node.id,
'name': wissen_node.name,
'description': wissen_node.description or '',
'color_code': wissen_node.color_code or '#4299E1',
'thought_count': len(wissen_node.thoughts),
'category_id': wissen_node.category_id
})
# Dann die anderen Knoten
for node in nodes:
node_obj = {
'id': node.id,
'name': node.name,
'description': node.description or '',
'color_code': node.color_code or '#9F7AEA',
'thought_count': len(node.thoughts),
'category_id': node.category_id
}
# Verbinde alle Top-Level-Knoten mit dem Wissen-Knoten
if not node.parents.all():
edge_data.append({
'source': wissen_node.id,
'target': node.id
})
# Verbindungen zwischen vorhandenen Knoten hinzufügen
node_children = node.children.all()
for child in node_children:
edge_data.append({
'source': node.id,
'target': child.id
})
node_data.append(node_obj)
return jsonify({
'success': True,
'categories': category_tree,
'nodes': node_data,
'edges': edge_data
})
except Exception as e:
print(f"Fehler beim Neuladen der Mindmap: {str(e)}")
return jsonify({
'success': False,
'error': 'Datenbankverbindung konnte nicht hergestellt werden'
}), 500
# Generische Route für andere Mindmap-Knoten (muss nach den spezifischen Routen kommen)
# Generische Route für spezifische Knoten
@app.route('/api/mindmap/<node_id>')
def get_mindmap_data(node_id):
"""
Stellt Mindmap-Daten für das Frontend bereit.
Liefert für 'root' die Hauptebene und für andere Node-IDs die entsprechenden Unterknoten.
Daten werden aus der Datenbank abgerufen.
"""
if node_id == 'root':
try:
# Stelle sicher, dass wir Kategorien haben
if Category.query.count() == 0:
create_default_categories()
# Überprüfe, ob wir bereits einen "Wissen"-Knoten haben
wissen_node = MindMapNode.query.filter_by(name="Wissen").first()
# Wenn kein "Wissen"-Knoten existiert, erstelle ihn
if not wissen_node:
wissen_node = MindMapNode(
name="Wissen",
description="Zentrale Wissensbasis",
color_code="#4299E1",
is_public=True
)
db.session.add(wissen_node)
db.session.commit()
# Hole alle Kategorien und Knoten
categories = Category.query.filter_by(parent_id=None).all()
category_tree = [build_category_tree(cat) for cat in categories]
# Hole alle Mindmap-Knoten außer dem "Wissen"-Knoten
nodes = MindMapNode.query.filter(MindMapNode.id != wissen_node.id).all()
# Vorbereiten der Node- und Edge-Arrays für die Antwort
node_data = []
edge_data = []
# Zuerst den "Wissen"-Knoten hinzufügen
node_data.append({
'id': wissen_node.id,
'name': wissen_node.name,
'description': wissen_node.description or '',
'color_code': wissen_node.color_code or '#4299E1',
'thought_count': len(wissen_node.thoughts),
'category_id': wissen_node.category_id
})
# Dann die anderen Knoten
for node in nodes:
node_obj = {
'id': node.id,
'name': node.name,
'description': node.description or '',
'color_code': node.color_code or '#9F7AEA',
'thought_count': len(node.thoughts),
'category_id': node.category_id
}
# Verbinde alle Top-Level-Knoten mit dem Wissen-Knoten
if not node.parents.all():
edge_data.append({
'source': wissen_node.id,
'target': node.id
})
# Verbindungen zwischen vorhandenen Knoten hinzufügen
node_children = node.children.all()
for child in node_children:
edge_data.append({
'source': node.id,
'target': child.id
})
node_data.append(node_obj)
return jsonify({
'success': True,
'categories': category_tree,
'nodes': node_data,
'edges': edge_data
})
except Exception as e:
app.logger.error(f"Fehler beim Laden der Root-Mindmap: {str(e)}")
"""Liefert die Daten für einen spezifischen Mindmap-Knoten."""
try:
# Prüfen, ob es sich um eine spezielle Route handelt
if node_id in ['root', 'philosophy', 'science', 'technology', 'arts']:
return jsonify({
'success': False,
'error': 'Datenbankverbindung konnte nicht hergestellt werden'
}), 500
app.logger.info(f"Mindmap-Daten werden für Node '{node_id}' angefordert.")
try:
# Fallback-Daten falls Datenbankzugriff fehlschlägt
fallback_data = get_fallback_mindmap_data(node_id)
if node_id.startswith('cat_'):
# Unterkategorien einer Hauptkategorie anzeigen
category_id = node_id.replace('cat_', '')
try:
category_id_int = int(category_id)
category = Category.query.get(category_id_int)
if category:
# Hole alle Unterkategorien
subcategories = Category.query.filter_by(parent_id=category_id_int).all()
# Hole alle Knoten dieser Kategorie
nodes = MindMapNode.query.filter_by(category_id=category_id_int).all()
# Vorbereiten der Antwort
node_data = []
edge_data = []
# Kategorie-Knoten hinzufügen
node_data.append({
'id': f"cat_{category.id}",
'name': category.name,
'description': category.description or '',
'color_code': category.color_code or '#9F7AEA',
'is_category': True
})
# Unterkategorien als Knoten hinzufügen
for subcat in subcategories:
node_data.append({
'id': f"cat_{subcat.id}",
'name': subcat.name,
'description': subcat.description or '',
'color_code': subcat.color_code or '#9F7AEA',
'is_category': True
})
# Verbindung zur Hauptkategorie
edge_data.append({
'source': f"cat_{category.id}",
'target': f"cat_{subcat.id}"
})
# Mindmap-Knoten hinzufügen
for node in nodes:
node_data.append({
'id': node.id,
'name': node.name,
'description': node.description or '',
'color_code': node.color_code or '#9F7AEA',
'thought_count': len(node.thoughts)
})
# Verbindung zur Kategorie
edge_data.append({
'source': f"cat_{category.id}",
'target': node.id
})
return jsonify({
'success': True,
'nodes': node_data,
'edges': edge_data
})
except (ValueError, TypeError):
app.logger.error(f"Ungültige Kategorie-ID: {category_id}")
return jsonify(fallback_data)
else:
# Versuche, einen MindMapNode mit der gegebenen ID zu finden
try:
node_id_int = int(node_id)
node = MindMapNode.query.get(node_id_int)
if node:
# Zeige Gedanken oder verwandte Knoten
# Hier würden wir verwandte Knoten aus der Datenbank laden
# Für den Moment verwenden wir Fallback-Daten
app.logger.info(f"Knoten {node.name} gefunden, aber keine spezifische Verarbeitung implementiert.")
return jsonify(fallback_data)
'error': 'Ungültige Knoten-ID',
'details': 'Diese ID ist für spezielle Routen reserviert'
}), 400
except (ValueError, TypeError):
# Wenn die ID kein Integer ist, verwende das bisherige Verhalten
app.logger.info(f"Falle auf bisheriges Verhalten für Node ID '{node_id}' zurück.")
return jsonify(fallback_data)
# Knoten mit Unterknoten in einer Abfrage laden
node = MindMapNode.query.options(
joinedload(MindMapNode.children)
).get_or_404(node_id)
# Wenn wir hier ankommen, gibt es keine spezifische Verarbeitung für diese node_id
return jsonify(fallback_data)
# Basis-Knoten erstellen
nodes = [{
'id': str(node.id),
'name': node.name,
'description': node.description or '',
'color_code': node.color_code or '#9F7AEA',
'is_center': True,
'has_children': bool(node.children),
'icon': node.icon or 'fa-solid fa-circle'
}]
# Unterknoten hinzufügen
for child in node.children:
nodes.append({
'id': str(child.id),
'name': child.name,
'description': child.description or '',
'color_code': child.color_code or '#9F7AEA',
'category': node.name,
'has_children': bool(child.children),
'icon': child.icon or 'fa-solid fa-circle'
})
# Kanten erstellen (vereinheitlichte Schlüssel)
edges = [{
'source': str(node.id),
'target': str(child.id),
'strength': 0.8
} for child in node.children]
return jsonify({
'success': True,
'nodes': nodes,
'edges': edges
})
except Exception as e:
app.logger.error(f"Fehler beim Abrufen der Mindmap-Daten: {str(e)}")
app.logger.error(f"Stack Trace: {traceback.format_exc()}")
return jsonify({"error": "Fehler beim Abrufen der Mindmap-Daten"}), 500
def get_fallback_mindmap_data(node_id):
"""
Liefert Fallback-Daten für die Mindmap, wenn die Datenbank nicht verfügbar ist.
"""
if node_id == 'root':
# Hauptebene der Mindmap
nodes = [
{
"id": "center",
"name": "Wissenskarte",
"description": "Zentrale Wissenskarte mit allen Hauptthemen",
"is_center": True,
"color_code": "#f5f5f5",
"has_children": True
},
{
"id": "philosophy",
"name": "Philosophie",
"description": "Die Lehre vom Denken und der Erkenntnis",
"category": "Philosophie",
"has_children": True,
"color_code": "#9F7AEA"
},
{
"id": "science",
"name": "Wissenschaft",
"description": "Systematische Erforschung der Natur und Gesellschaft",
"category": "Wissenschaft",
"has_children": True,
"color_code": "#f4b400"
},
{
"id": "technology",
"name": "Technologie",
"description": "Anwendung wissenschaftlicher Erkenntnisse",
"category": "Technologie",
"has_children": True,
"color_code": "#0d47a1"
},
{
"id": "arts",
"name": "Künste",
"description": "Kreativer Ausdruck und künstlerische Gestaltung",
"category": "Künste",
"has_children": True,
"color_code": "#c2185b"
}
]
edges = [
{"source_id": "center", "target_id": "philosophy", "strength": 0.9},
{"source_id": "center", "target_id": "science", "strength": 0.9},
{"source_id": "center", "target_id": "technology", "strength": 0.9},
{"source_id": "center", "target_id": "arts", "strength": 0.9}
]
elif node_id == 'philosophy':
nodes = [
{
"id": "epistemology",
"name": "Erkenntnistheorie",
"description": "Untersuchung der Natur und Grenzen menschlicher Erkenntnis",
"category": "Philosophie",
"has_children": True,
"color_code": "#9F7AEA"
},
{
"id": "ethics",
"name": "Ethik",
"description": "Lehre vom moralisch richtigen Handeln",
"category": "Philosophie",
"has_children": True,
"color_code": "#9F7AEA"
},
{
"id": "metaphysics",
"name": "Metaphysik",
"description": "Grundfragen des Seins und der Wirklichkeit",
"category": "Philosophie",
"has_children": True,
"color_code": "#9F7AEA"
}
]
edges = [
{"source_id": "philosophy", "target_id": "epistemology", "strength": 0.8},
{"source_id": "philosophy", "target_id": "ethics", "strength": 0.8},
{"source_id": "philosophy", "target_id": "metaphysics", "strength": 0.8}
]
elif node_id == 'science':
nodes = [
{
"id": "physics",
"name": "Physik",
"description": "Lehre von der Materie und ihren Wechselwirkungen",
"category": "Wissenschaft",
"has_children": True,
"color_code": "#f4b400"
},
{
"id": "biology",
"name": "Biologie",
"description": "Lehre von den Lebewesen und ihren Lebensprozessen",
"category": "Wissenschaft",
"has_children": True,
"color_code": "#f4b400"
},
{
"id": "chemistry",
"name": "Chemie",
"description": "Wissenschaft von den Stoffen und ihren Reaktionen",
"category": "Wissenschaft",
"has_children": True,
"color_code": "#f4b400"
}
]
edges = [
{"source_id": "science", "target_id": "physics", "strength": 0.8},
{"source_id": "science", "target_id": "biology", "strength": 0.8},
{"source_id": "science", "target_id": "chemistry", "strength": 0.8}
]
elif node_id == 'technology':
nodes = [
{
"id": "ai",
"name": "Künstliche Intelligenz",
"description": "Maschinelles Lernen und intelligente Systeme",
"category": "Technologie",
"has_children": True,
"color_code": "#0d47a1"
},
{
"id": "robotics",
"name": "Robotik",
"description": "Entwicklung und Steuerung von Robotern",
"category": "Technologie",
"has_children": True,
"color_code": "#0d47a1"
},
{
"id": "quantum_computing",
"name": "Quantencomputing",
"description": "Computer basierend auf Quantenmechanik",
"category": "Technologie",
"has_children": True,
"color_code": "#0d47a1"
}
]
edges = [
{"source_id": "technology", "target_id": "ai", "strength": 0.8},
{"source_id": "technology", "target_id": "robotics", "strength": 0.8},
{"source_id": "technology", "target_id": "quantum_computing", "strength": 0.8}
]
elif node_id == 'arts':
nodes = [
{
"id": "visual_arts",
"name": "Bildende Kunst",
"description": "Malerei, Bildhauerei und andere visuelle Kunstformen",
"category": "Künste",
"has_children": True,
"color_code": "#c2185b"
},
{
"id": "music",
"name": "Musik",
"description": "Tonkunst und musikalische Komposition",
"category": "Künste",
"has_children": True,
"color_code": "#c2185b"
},
{
"id": "literature",
"name": "Literatur",
"description": "Schriftliche Kunstwerke und Poesie",
"category": "Künste",
"has_children": True,
"color_code": "#c2185b"
}
]
edges = [
{"source_id": "arts", "target_id": "visual_arts", "strength": 0.8},
{"source_id": "arts", "target_id": "music", "strength": 0.8},
{"source_id": "arts", "target_id": "literature", "strength": 0.8}
]
else:
# Für jede andere Node-ID geben wir eine leere Struktur zurück
nodes = []
edges = []
# Antwort zusammenstellen
return {
"nodes": nodes,
"edges": edges
}
# Diese Routen wurden an den Anfang der Datei verschoben und werden nicht mehr benötigt
print(f"Fehler beim Abrufen der Mindmap-Daten für Knoten {node_id}: {str(e)}")
return jsonify({
'success': False,
'error': 'Mindmap-Daten konnten nicht geladen werden',
'details': str(e)
}), 500

File diff suppressed because it is too large Load Diff

View File

@@ -5,199 +5,260 @@
* Implementiert Lazy Loading & Progressive Disclosure
*/
// Mock-Datenbank für Subthemen (später durch echte DB-Abfragen ersetzen)
const subthemesDatabase = {
'philosophy': [
{
id: 'epistemology',
label: 'Erkenntnistheorie',
category: 'Philosophie',
description: 'Untersuchung der Natur und Grenzen menschlicher Erkenntnis',
hasChildren: true
// Neue zentrale Konfiguration
const mindmapConfig = {
categories: {
'Philosophie': {
icon: 'fa-solid fa-lightbulb',
color: '#b71c1c',
description: 'Die Lehre vom Denken und der Erkenntnis'
},
{
id: 'ethics',
label: 'Ethik',
category: 'Philosophie',
description: 'Lehre vom moralisch richtigen Handeln',
hasChildren: true
'Wissenschaft': {
icon: 'fa-solid fa-atom',
color: '#f4b400',
description: 'Systematische Erforschung der Natur und Gesellschaft'
},
{
id: 'metaphysics',
label: 'Metaphysik',
category: 'Philosophie',
description: 'Grundfragen des Seins und der Wirklichkeit',
hasChildren: true
'Technologie': {
icon: 'fa-solid fa-microchip',
color: '#0d47a1',
description: 'Anwendung wissenschaftlicher Erkenntnisse'
},
'Künste': {
icon: 'fa-solid fa-palette',
color: '#c2185b',
description: 'Kreativer Ausdruck und künstlerische Gestaltung'
}
],
'science': [
{
id: 'physics',
label: 'Physik',
category: 'Wissenschaft',
description: 'Lehre von der Materie und ihren Wechselwirkungen',
hasChildren: true
},
{
id: 'biology',
label: 'Biologie',
category: 'Wissenschaft',
description: 'Lehre von den Lebewesen und ihren Lebensprozessen',
hasChildren: true
},
{
id: 'chemistry',
label: 'Chemie',
category: 'Wissenschaft',
description: 'Wissenschaft von den Stoffen und ihren Reaktionen',
hasChildren: true
}
],
'technology': [
{
id: 'ai',
label: 'Künstliche Intelligenz',
category: 'Technologie',
description: 'Maschinelles Lernen und intelligente Systeme',
hasChildren: true
},
{
id: 'robotics',
label: 'Robotik',
category: 'Technologie',
description: 'Entwicklung und Steuerung von Robotern',
hasChildren: true
},
{
id: 'quantum_computing',
label: 'Quantencomputing',
category: 'Technologie',
description: 'Computer basierend auf Quantenmechanik',
hasChildren: true
}
],
'arts': [
{
id: 'visual_arts',
label: 'Bildende Kunst',
category: 'Künste',
description: 'Malerei, Bildhauerei und andere visuelle Kunstformen',
hasChildren: true
},
{
id: 'music',
label: 'Musik',
category: 'Künste',
description: 'Tonkunst und musikalische Komposition',
hasChildren: true
},
{
id: 'literature',
label: 'Literatur',
category: 'Künste',
description: 'Schriftliche Kunstwerke und Poesie',
hasChildren: true
}
]
},
defaultNodeStyle: {
fontSize: 18,
fontColor: '#fff',
neuronSize: 8,
neuronActivity: 0.8
},
centerNodeStyle: {
fontSize: 22,
fontColor: '#222',
neuronSize: 12,
neuronActivity: 1.0,
color: '#f5f5f5',
icon: 'fa-solid fa-circle'
}
};
// Icon-Definitionen für Kategorien (FontAwesome oder SVG-URL)
const categoryIcons = {
'Philosophie': 'fa-solid fa-lightbulb',
'Wissenschaft': 'fa-solid fa-atom',
'Technologie': 'fa-solid fa-microchip',
'Künste': 'fa-solid fa-palette',
'Psychologie': 'fa-solid fa-brain'
};
// Farben für Kategorien (wie im Bild)
const categoryColors = {
'Philosophie': '#b71c1c', // Rot
'Wissenschaft': '#f4b400', // Gelb
'Technologie': '#0d47a1', // Blau
'Künste': '#c2185b', // Pink
'Psychologie': '#009688' // Türkis
};
// Initiale Mindmap-Daten (nur oberste Ebene, mit Icon und Farbe)
const mindmapData = {
nodes: [
{
id: 'center',
label: 'Wissenskarte',
isCenter: true,
color: '#f5f5f5',
icon: 'fa-solid fa-circle',
fontColor: '#222',
fontSize: 22,
neuronSize: 12,
neuronActivity: 1.0
// Zentrale Styling-Konfiguration
const mindmapStyles = {
node: {
base: {
'background-color': 'data(color)',
'label': 'data(label)',
'color': '#ffffff',
'text-background-color': 'rgba(0, 0, 0, 0.7)',
'text-background-opacity': 0.8,
'text-background-padding': '4px',
'text-valign': 'center',
'text-halign': 'center',
'font-size': 16,
'width': 'mapData(neuronSize, 3, 10, 30, 60)',
'height': 'mapData(neuronSize, 3, 10, 30, 60)',
'border-width': 2,
'border-color': '#ffffff',
'border-opacity': 0.8,
'shape': 'ellipse',
'background-opacity': 0.85
},
{
id: 'philosophy',
label: 'Philosophie',
category: 'Philosophie',
description: 'Die Lehre vom Denken und der Erkenntnis',
hasChildren: true,
expanded: false,
neuronSize: 8,
neuronActivity: 0.8,
color: categoryColors['Philosophie'],
icon: categoryIcons['Philosophie'],
fontColor: '#fff',
fontSize: 18
center: {
'background-color': '#f5f5f5',
'color': '#222',
'font-size': 20,
'border-width': 3,
'width': 100,
'height': 100
},
{
id: 'science',
label: 'Wissenschaft',
category: 'Wissenschaft',
description: 'Systematische Erforschung der Natur und Gesellschaft',
hasChildren: true,
expanded: false,
neuronSize: 8,
neuronActivity: 0.8,
color: categoryColors['Wissenschaft'],
icon: categoryIcons['Wissenschaft'],
fontColor: '#fff',
fontSize: 18
},
{
id: 'technology',
label: 'Technologie',
category: 'Technologie',
description: 'Anwendung wissenschaftlicher Erkenntnisse',
hasChildren: true,
expanded: false,
neuronSize: 8,
neuronActivity: 0.8,
color: categoryColors['Technologie'],
icon: categoryIcons['Technologie'],
fontColor: '#fff',
fontSize: 18
},
{
id: 'arts',
label: 'Künste',
category: 'Künste',
description: 'Kreativer Ausdruck und künstlerische Gestaltung',
hasChildren: true,
expanded: false,
neuronSize: 8,
neuronActivity: 0.8,
color: categoryColors['Künste'],
icon: categoryIcons['Künste'],
fontColor: '#fff',
fontSize: 18
selected: {
'border-color': '#f59e42',
'border-width': 3,
'background-opacity': 1
}
],
edges: [
{ source: 'center', target: 'philosophy', strength: 0.9 },
{ source: 'center', target: 'science', strength: 0.9 },
{ source: 'center', target: 'technology', strength: 0.9 },
{ source: 'center', target: 'arts', strength: 0.9 }
]
},
edge: {
base: {
'width': function(ele) {
return ele.data('strength') ? ele.data('strength') * 2 : 1;
},
'line-color': function(ele) {
const sourceColor = ele.source().data('color');
return sourceColor || '#8a8aaa';
},
'line-opacity': function(ele) {
return ele.data('strength') ? ele.data('strength') * 0.6 : 0.4;
},
'curve-style': 'bezier',
'target-arrow-shape': 'none',
'control-point-distances': [30, -30],
'control-point-weights': [0.5, 0.5]
}
},
layout: {
base: {
name: 'cose',
animate: true,
animationDuration: 500,
refresh: 20,
fit: true,
padding: 30,
nodeRepulsion: 4500,
idealEdgeLength: 50,
edgeElasticity: 0.45,
randomize: true,
componentSpacing: 100,
nodeOverlap: 20,
gravity: 0.25,
initialTemp: 1000,
coolingFactor: 0.95,
minTemp: 1
}
}
};
// Globale Variable für die Mindmap-Daten
let mindmapData = null;
// Funktion zum Laden der Mindmap-Daten aus der Datenbank
async function loadMindmapData(nodeId = null) {
try {
const apiUrl = nodeId ? `/api/mindmap/${nodeId}` : '/api/mindmap/root';
console.log('Lade Mindmap-Daten von:', apiUrl);
const response = await fetch(apiUrl);
console.log('API-Antwort Status:', response.status);
if (!response.ok) {
let errorData;
try {
errorData = await response.json();
console.log('API-Fehler Details:', {
status: response.status,
statusText: response.statusText,
errorData
});
} catch (e) {
console.error('Fehler beim Parsen der Fehlerantwort:', e);
errorData = {
error: `HTTP-Fehler ${response.status}: ${response.statusText}`
};
}
// Fehlerobjekt für die Benachrichtigung erstellen
const errorMessage = {
error: errorData.error || errorData.message || 'Unbekannter Fehler',
details: errorData.details || null
};
showUINotification(errorMessage, 'error');
throw new Error(errorMessage.error);
}
const data = await response.json();
console.log('Geladene Mindmap-Daten:', data);
if (!data.success) {
const errorMessage = {
error: data.error || 'Mindmap-Daten konnten nicht geladen werden',
details: data.details || null
};
showUINotification(errorMessage, 'error');
throw new Error(errorMessage.error);
}
// Erfolgreiche Antwort
mindmapData = data; // Speichere die Daten in der globalen Variable
showUINotification('Mindmap-Daten erfolgreich geladen', 'success');
return data;
} catch (error) {
console.error('Fehler beim Laden der Mindmap-Daten:', {
message: error.message,
stack: error.stack,
nodeId
});
// Stelle sicher, dass wir eine aussagekräftige Fehlermeldung haben
const errorMessage = {
error: error.message || 'Unbekannter Fehler beim Laden der Mindmap-Daten',
details: error.stack
};
showUINotification(errorMessage, 'error');
throw error;
}
}
// Funktion zum Initialisieren der Mindmap
async function initializeMindmap() {
try {
const data = await loadMindmapData();
if (!data || !data.nodes || !data.edges) {
throw new Error('Ungültiges Datenformat: Mindmap-Daten fehlen oder sind unvollständig');
}
const elements = [
// Knoten
...data.nodes.map(node => ({
data: {
id: node.id,
label: node.name,
category: node.category,
description: node.description,
hasChildren: node.has_children,
expanded: false,
color: node.color_code,
fontColor: '#ffffff',
fontSize: node.is_center ? 20 : 16
}
})),
// Kanten
...data.edges.map(edge => ({
data: {
source: edge.source,
target: edge.target,
strength: edge.strength || 0.5
}
}))
];
// Bestehende Cytoscape-Instanz entfernen, falls vorhanden
if (window.cy) {
window.cy.destroy();
}
window.cy = cytoscape({
container: document.getElementById('cy'),
elements: elements,
style: mindmapStyles,
layout: mindmapStyles.layout.base
});
// Event-Listener für Knoten-Klicks
cy.on('tap', 'node', async function(evt) {
const node = evt.target;
if (node.data('hasChildren') && !node.data('expanded')) {
await loadSubthemes(node);
}
});
// Layout ausführen
cy.layout(mindmapStyles.layout.base).run();
return true;
} catch (error) {
console.error('Fehler bei der Mindmap-Initialisierung:', error);
showUINotification({
error: 'Mindmap konnte nicht initialisiert werden',
details: error.message
}, 'error');
return false;
}
}
// Warte bis DOM geladen ist
document.addEventListener('DOMContentLoaded', function() {
console.log('DOMContentLoaded Event ausgelöst');
@@ -257,79 +318,22 @@ document.addEventListener('DOMContentLoaded', function() {
style: [
{
selector: 'node',
style: {
'background-color': 'data(color)',
'label': 'data(label)',
'color': 'data(fontColor)',
'text-valign': 'center',
'text-halign': 'center',
'font-size': 'data(fontSize)',
'width': function(ele) {
return ele.data('isCenter') ? 100 : 80;
},
'height': function(ele) {
return ele.data('isCenter') ? 100 : 80;
},
'border-width': 2,
'border-color': '#ffffff',
'border-opacity': 0.8,
'shape': 'ellipse',
'background-opacity': 0.9,
'text-wrap': 'wrap',
'text-max-width': 80,
'transition-property': 'background-color, border-width',
'transition-duration': '0.2s'
}
style: mindmapStyles.node.base
},
{
selector: 'node[isCenter]',
style: {
'background-color': '#f5f5f5',
'color': '#222',
'font-size': 20,
'border-width': 3,
'width': 100,
'height': 100
}
style: mindmapStyles.node.center
},
{
selector: 'node:selected',
style: {
'border-color': '#f59e42',
'border-width': 3,
'background-opacity': 1
}
style: mindmapStyles.node.selected
},
{
selector: 'edge',
style: {
'width': function(ele) {
return ele.data('strength') ? ele.data('strength') * 2 : 1;
},
'line-color': function(ele) {
const sourceColor = ele.source().data('color');
return sourceColor || '#8a8aaa';
},
'line-opacity': function(ele) {
return ele.data('strength') ? ele.data('strength') * 0.6 : 0.4;
},
'curve-style': 'bezier',
'target-arrow-shape': 'none',
'control-point-distances': [30, -30],
'control-point-weights': [0.5, 0.5]
}
style: mindmapStyles.edge.base
}
],
layout: {
name: 'concentric',
fit: true,
padding: 50,
animate: true,
concentric: function(node) {
return node.data('isCenter') ? 2 : 1;
},
levelWidth: function() { return 1; }
}
layout: mindmapStyles.layout.base
});
console.log('Cytoscape initialisiert');
@@ -469,63 +473,42 @@ function initializeNeuralDesign(cy) {
// Modifiziere die updateMindmap Funktion
function updateMindmap() {
if (!cy) return;
// Bestehende Elemente entfernen
cy.elements().remove();
// Neue Knoten hinzufügen
mindmapData.nodes.forEach(node => {
cy.add({
group: 'nodes',
data: {
id: node.id,
label: node.label,
category: node.category,
description: node.description,
hasChildren: node.hasChildren,
expanded: node.expanded,
neuronSize: node.neuronSize,
neuronActivity: node.neuronActivity,
color: node.color,
icon: node.icon,
fontColor: node.fontColor,
fontSize: node.fontSize
}
if (!cy) return;
// Bestehende Elemente entfernen
cy.elements().remove();
// Neue Knoten hinzufügen
mindmapData.nodes.forEach(node => {
cy.add({
group: 'nodes',
data: {
id: node.id,
label: node.name,
category: node.category,
description: node.description,
hasChildren: node.has_children,
expanded: false,
color: node.color_code || mindmapConfig.categories[node.category]?.color || '#60a5fa',
icon: node.icon || mindmapConfig.categories[node.category]?.icon || 'fa-solid fa-circle'
}
});
});
});
// Neue Kanten hinzufügen
mindmapData.edges.forEach(edge => {
cy.add({
group: 'edges',
data: {
source: edge.source,
target: edge.target,
strength: edge.strength
}
// Neue Kanten hinzufügen
mindmapData.edges.forEach(edge => {
cy.add({
group: 'edges',
data: {
source: edge.source,
target: edge.target,
strength: edge.strength || 0.5
}
});
});
});
// Neuronales Design initialisieren
initializeNeuralDesign(cy);
// Layout anwenden
cy.layout({
name: 'cose',
animate: true,
animationDuration: 1000,
nodeDimensionsIncludeLabels: true,
padding: 100,
spacingFactor: 1.8,
randomize: false,
fit: true,
componentSpacing: 100,
nodeRepulsion: 8000,
edgeElasticity: 100,
nestingFactor: 1.2,
gravity: 80
}).run();
// Layout aktualisieren
cy.layout(mindmapStyles.layout.base).run();
}
/**
@@ -764,31 +747,69 @@ function startNeuralActivitySimulation(cy) {
// Hilfe-Funktion zum Hinzufügen eines Flash-Hinweises
function showFlash(message, type = 'info') {
const flashContainer = document.getElementById('flash-messages') || createFlashContainer();
const flashMsg = document.createElement('div');
flashMsg.className = `flash-message flash-${type} mb-2 p-3 rounded`;
flashMsg.innerHTML = `
<div class="flex items-center justify-between">
<div>${message}</div>
<button class="close-flash ml-2">&times;</button>
</div>
`;
flashContainer.appendChild(flashMsg);
// Nach 5 Sekunden automatisch ausblenden
const flashContainer = createFlashContainer();
const flash = document.createElement('div');
flash.className = `flash-message ${type}`;
flash.textContent = message;
flashContainer.appendChild(flash);
document.body.appendChild(flashContainer);
setTimeout(() => {
flashMsg.style.opacity = '0';
setTimeout(() => flashMsg.remove(), 300);
}, 5000);
// Close-Button
const closeBtn = flashMsg.querySelector('.close-flash');
closeBtn.addEventListener('click', () => {
flashMsg.style.opacity = '0';
setTimeout(() => flashMsg.remove(), 300);
});
flash.classList.add('show');
setTimeout(() => {
flash.classList.remove('show');
setTimeout(() => {
flashContainer.remove();
}, 300);
}, 3000);
}, 100);
}
/**
* Zeigt eine Benachrichtigung in der UI an
* @param {string|object} message - Die anzuzeigende Nachricht oder ein Fehlerobjekt
* @param {string} type - Der Typ der Benachrichtigung ('info', 'success', 'warning', 'error')
* @param {number} duration - Die Anzeigedauer in Millisekunden (Standard: 3000)
*/
function showUINotification(message, type = 'info', duration = 3000) {
// Überprüfe und formatiere die Nachricht
let displayMessage;
if (typeof message === 'object') {
if (message.message) {
displayMessage = message.message;
} else if (message.error) {
displayMessage = message.error;
} else if (message.details) {
displayMessage = message.details;
} else {
console.error('Ungültiges Nachrichtenobjekt:', message);
displayMessage = 'Ein unbekannter Fehler ist aufgetreten';
}
} else if (typeof message === 'string') {
displayMessage = message;
} else {
console.error('Ungültige Nachricht für UI-Benachrichtigung:', message);
displayMessage = 'Ein unbekannter Fehler ist aufgetreten';
}
// Validiere den Typ
const validTypes = ['info', 'success', 'warning', 'error'];
if (!validTypes.includes(type)) {
console.warn(`Ungültiger Benachrichtigungstyp: ${type}. Verwende 'info' als Fallback.`);
type = 'info';
}
// Validiere die Dauer
if (typeof duration !== 'number' || duration < 1000 || duration > 10000) {
console.warn(`Ungültige Dauer: ${duration}ms. Verwende 3000ms als Fallback.`);
duration = 3000;
}
// Zeige die Benachrichtigung an
showFlash(displayMessage, type);
// Logging für Debugging-Zwecke
console.log(`UI-Benachrichtigung [${type}]:`, displayMessage);
}
// Hilfsfunktion zum Erstellen eines Flash-Containers, falls keiner existiert
@@ -798,158 +819,6 @@ function createFlashContainer() {
container.className = 'fixed top-4 right-4 z-50 w-64';
document.body.appendChild(container);
return container;
}
// Funktion zum Laden der Mindmap-Daten aus der Datenbank
async function loadMindmapData(nodeId = null) {
try {
let url;
// Wir verwenden die generische Route für alle Anfragen
if (nodeId === null || nodeId === undefined || nodeId === 'root') {
url = '/api/mindmap/root';
} else {
// Für spezielle Kategorien wie 'philosophy', 'science' etc.
// verwenden wir die generische Route mit der Kategorie-ID
url = `/api/mindmap/${nodeId}`;
}
const response = await fetch(url);
if (!response.ok) {
const errorData = await response.json().catch(() => ({ error: 'Unbekannter Fehler' }));
throw new Error(errorData.error || `HTTP-Fehler ${response.status}: ${response.statusText}`);
}
const data = await response.json();
if (!data.success) {
throw new Error(data.error || 'Fehler beim Laden der Mindmap-Daten');
}
return data;
} catch (error) {
console.error('Fehler beim Laden der Mindmap-Daten:', error);
showFlash(error.message || 'Fehler beim Laden der Mindmap-Daten', 'error');
return null;
}
}
// Funktion zum Initialisieren der Mindmap
async function initializeMindmap() {
const mindmapData = await loadMindmapData();
if (!mindmapData) return;
const elements = [
// Knoten
...mindmapData.nodes.map(node => ({
data: {
id: node.id,
label: node.name,
category: node.category,
description: node.description,
hasChildren: node.has_children,
expanded: false,
color: node.color_code,
fontColor: '#ffffff',
fontSize: node.is_center ? 20 : 16
}
})),
// Kanten
...mindmapData.edges.map(edge => ({
data: {
source: edge.source_id,
target: edge.target_id,
strength: edge.strength || 0.5
}
}))
];
window.cy = cytoscape({
container: document.getElementById('cy'),
elements: elements,
style: [
{
selector: 'node',
style: {
'background-color': 'data(color)',
'label': 'data(label)',
'color': 'data(fontColor)',
'text-valign': 'center',
'text-halign': 'center',
'font-size': 'data(fontSize)',
'width': function(ele) {
return ele.data('isCenter') ? 100 : 80;
},
'height': function(ele) {
return ele.data('isCenter') ? 100 : 80;
},
'border-width': 2,
'border-color': '#ffffff',
'border-opacity': 0.8,
'shape': 'ellipse',
'background-opacity': 0.9,
'text-wrap': 'wrap',
'text-max-width': 80,
'transition-property': 'background-color, border-width',
'transition-duration': '0.2s'
}
},
{
selector: 'node[isCenter]',
style: {
'background-color': '#f5f5f5',
'color': '#222',
'font-size': 20,
'border-width': 3,
'width': 100,
'height': 100
}
},
{
selector: 'node:selected',
style: {
'border-color': '#f59e42',
'border-width': 3,
'background-opacity': 1
}
},
{
selector: 'edge',
style: {
'width': function(ele) {
return ele.data('strength') ? ele.data('strength') * 2 : 1;
},
'line-color': function(ele) {
const sourceColor = ele.source().data('color');
return sourceColor || '#8a8aaa';
},
'line-opacity': function(ele) {
return ele.data('strength') ? ele.data('strength') * 0.6 : 0.4;
},
'curve-style': 'bezier',
'target-arrow-shape': 'none',
'control-point-distances': [30, -30],
'control-point-weights': [0.5, 0.5]
}
}
],
layout: {
name: 'concentric',
fit: true,
padding: 50,
animate: true,
concentric: function(node) {
return node.data('isCenter') ? 2 : 1;
},
levelWidth: function() { return 1; }
}
});
// Event-Listener für Knoten-Klicks
cy.on('tap', 'node', async function(evt) {
const node = evt.target;
if (node.data('hasChildren') && !node.data('expanded')) {
await loadSubthemes(node);
}
});
}
// Funktion zum Laden der Subthemen
@@ -1009,24 +878,7 @@ async function loadSubthemes(node) {
}))
],
style: cy.style(),
layout: {
name: 'cose',
animate: true,
animationDuration: 500,
refresh: 20,
fit: true,
padding: 30,
nodeRepulsion: 4500,
idealEdgeLength: 50,
edgeElasticity: 0.45,
randomize: true,
componentSpacing: 100,
nodeOverlap: 20,
gravity: 0.25,
initialTemp: 1000,
coolingFactor: 0.95,
minTemp: 1
}
layout: mindmapStyles.layout.base
});
// Event-Listener für die neue Mindmap
@@ -1126,6 +978,99 @@ style.textContent = `
color: #fff;
text-shadow: 0 2px 8px rgba(0,0,0,0.25);
}
/* Verbesserte Flash-Benachrichtigungen */
#flash-messages {
position: fixed;
top: 1rem;
right: 1rem;
z-index: 9999;
display: flex;
flex-direction: column;
gap: 0.5rem;
max-width: 24rem;
}
.flash-message {
padding: 1rem 1.25rem;
border-radius: 0.5rem;
background: rgba(17, 24, 39, 0.95);
color: #fff;
font-size: 0.875rem;
line-height: 1.25rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
0 2px 4px -1px rgba(0, 0, 0, 0.06);
transform: translateX(120%);
opacity: 0;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
backdrop-filter: blur(8px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.flash-message.show {
transform: translateX(0);
opacity: 1;
}
.flash-message.info {
border-left: 4px solid #3b82f6;
background: linear-gradient(to right, rgba(59, 130, 246, 0.1), rgba(17, 24, 39, 0.95));
}
.flash-message.success {
border-left: 4px solid #10b981;
background: linear-gradient(to right, rgba(16, 185, 129, 0.1), rgba(17, 24, 39, 0.95));
}
.flash-message.warning {
border-left: 4px solid #f59e0b;
background: linear-gradient(to right, rgba(245, 158, 11, 0.1), rgba(17, 24, 39, 0.95));
}
.flash-message.error {
border-left: 4px solid #ef4444;
background: linear-gradient(to right, rgba(239, 68, 68, 0.1), rgba(17, 24, 39, 0.95));
}
.flash-message::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(45deg,
rgba(255, 255, 255, 0.1) 0%,
rgba(255, 255, 255, 0) 100%);
pointer-events: none;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.02); }
100% { transform: scale(1); }
}
.flash-message.show {
animation: pulse 0.3s ease-in-out;
}
/* Neuronale Effekte für Benachrichtigungen */
.flash-message.info:hover {
box-shadow: 0 0 15px rgba(59, 130, 246, 0.3);
}
.flash-message.success:hover {
box-shadow: 0 0 15px rgba(16, 185, 129, 0.3);
}
.flash-message.warning:hover {
box-shadow: 0 0 15px rgba(245, 158, 11, 0.3);
}
.flash-message.error:hover {
box-shadow: 0 0 15px rgba(239, 68, 68, 0.3);
}
`;
document.head.appendChild(style);