Compare commits

...

7 Commits

18 changed files with 9301 additions and 1203 deletions
Binary file not shown.
Binary file not shown.
+150 -715
View File
@@ -26,6 +26,7 @@ import sqlalchemy
import ssl import ssl
import certifi import certifi
import os import os
from sqlalchemy.orm import joinedload
# Modelle importieren # Modelle importieren
from models import ( from models import (
@@ -277,7 +278,7 @@ def create_default_categories():
"icon": "fa-palette", "icon": "fa-palette",
"subcategories": [ "subcategories": [
{"name": "Literatur", "description": "Schriftliche Werke", "icon": "fa-book"}, {"name": "Literatur", "description": "Schriftliche Werke", "icon": "fa-book"},
{"name": "Musik", "description": "Klangkunst", "icon": "fa-music"}, {"name": "Musik", "description": "Klangkunst", "icon": "fa-musik"},
{"name": "Bildende Kunst", "description": "Visuelle Kunstformen", "icon": "fa-paint-brush"} {"name": "Bildende Kunst", "description": "Visuelle Kunstformen", "icon": "fa-paint-brush"}
] ]
}, },
@@ -2342,750 +2343,184 @@ if __name__ == '__main__':
db.create_all() db.create_all()
socketio.run(app, debug=True, host='0.0.0.0') socketio.run(app, debug=True, host='0.0.0.0')
@app.route('/api/refresh-mindmap') def get_category_mindmap_data(category_name):
def refresh_mindmap(): """Generische Funktion zum Abrufen der Mindmap-Daten für eine Kategorie."""
"""
API-Endpunkt zum Neuladen der Mindmap-Daten,
wenn die Datenbank-Verbindung vorübergehend fehlgeschlagen ist
"""
try: try:
# Stelle sicher, dass wir Kategorien haben # Kategorie mit allen Unterkategorien in einer Abfrage laden
if Category.query.count() == 0: category = Category.query.filter_by(name=category_name).options(
create_default_categories() joinedload(Category.children)
).first_or_404()
# Überprüfe, ob wir bereits einen "Wissen"-Knoten haben # Basis-Knoten erstellen
wissen_node = MindMapNode.query.filter_by(name="Wissen").first() 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 # Unterkategorien hinzufügen
if not wissen_node: for subcat in category.children:
wissen_node = MindMapNode( nodes.append({
name="Wissen", 'id': f'cat_{subcat.id}',
description="Zentrale Wissensbasis", 'name': subcat.name,
color_code="#4299E1", 'description': subcat.description or '',
is_public=True 'color_code': subcat.color_code or '#9F7AEA',
) 'category': category_name,
db.session.add(wissen_node) 'has_children': bool(subcat.children),
db.session.commit() 'icon': subcat.icon or 'fa-solid fa-circle'
})
# Hole alle Kategorien und Knoten # Kanten erstellen (vereinheitlichte Schlüssel)
categories = Category.query.filter_by(parent_id=None).all() edges = [{
category_tree = [build_category_tree(cat) for cat in categories] 'source': f'cat_{category.id}',
'target': f'cat_{subcat.id}',
# Hole alle Mindmap-Knoten außer dem "Wissen"-Knoten 'strength': 0.8
nodes = MindMapNode.query.filter(MindMapNode.id != wissen_node.id).all() } for subcat in category.children]
# 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({ return jsonify({
'success': True, 'success': True,
'categories': category_tree, 'nodes': nodes,
'nodes': node_data, 'edges': edges
'edges': edge_data
}) })
except Exception as e: 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({ return jsonify({
'success': False, 'success': False,
'error': 'Datenbankverbindung konnte nicht hergestellt werden' 'error': f'{category_name}-Mindmap konnte nicht geladen werden',
'details': str(e)
}), 500 }), 500
# Die Route '/mindmap' wird bereits in Zeile 474 definiert - doppelte Definition entfernt @app.route('/api/mindmap/root')
def get_root_mindmap_data():
# Weiterleitung für Community/Forum-Routen """Liefert die Daten für die Root-Mindmap."""
@app.route('/community')
@app.route('/Community')
@app.route('/forum')
@app.route('/Forum')
@app.route('/community_forum')
def redirect_community():
"""Leitet alle Community/Forum-URLs zur Startseite um"""
return redirect(url_for('index'))
@app.route('/static/js/mindmap-init.js')
def serve_mindmap_init_js():
"""Bedient die Mindmap-Initialisierungsdatei."""
return app.send_static_file('js/mindmap-init.js'), 200, {'Content-Type': 'application/javascript'}
# Datenbank-Update-Route (admin-geschützt)
@app.route('/admin/update-database', methods=['GET', 'POST'])
@admin_required
def admin_update_database():
"""Admin-Route zum Aktualisieren der Datenbank"""
message = None
success = None
if request.method == 'POST':
try:
import utils.update_db as update_db
update_success = update_db.update_user_table()
if update_success:
message = "Die Datenbank wurde erfolgreich aktualisiert."
success = True
else:
message = "Es gab ein Problem bei der Aktualisierung der Datenbank."
success = False
except Exception as e:
message = f"Fehler: {str(e)}"
success = False
return render_template('admin/update_database.html', message=message, success=success)
@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.
"""
app.logger.info(f"Mindmap-Daten werden für Node '{node_id}' angefordert.")
try: try:
# Fallback-Daten falls Datenbankzugriff fehlschlägt # Hauptkategorien mit Unterkategorien in einer Abfrage laden
fallback_data = get_fallback_mindmap_data(node_id) categories = Category.query.filter_by(parent_id=None).options(
joinedload(Category.children)
).all()
if node_id == 'root': # Basis-Knoten erstellen
# Hauptebene der Mindmap - finde den "Wissen"-Knoten und seine Verbindungen nodes = [{
wissen_node = MindMapNode.query.filter_by(name="Wissen").first() 'id': 'root',
'name': 'Wissen',
if not wissen_node: 'description': 'Zentrale Wissensbasis',
app.logger.warning("'Wissen'-Knoten nicht in der Datenbank gefunden, Fallback zu Hardcoded-Daten.") 'color_code': '#4299E1',
return jsonify(fallback_data) 'is_center': True,
'has_children': bool(categories),
# Zentrum der Mindmap ist der "Wissen"-Knoten 'icon': 'fa-solid fa-circle'
nodes = [{ }]
"id": str(wissen_node.id),
"name": "Wissenskarte", # Frontend-Name für Root-Knoten
"description": wissen_node.description or "Zentrale Wissenskarte mit allen Hauptthemen",
"is_center": True,
"color_code": "#f5f5f5",
"has_children": True
}]
# Hauptkategorien als Knoten
main_categories = Category.query.filter_by(parent_id=None).all()
category_nodes = []
edges = []
for category in main_categories:
category_node = {
"id": f"cat_{category.id}",
"name": category.name,
"description": category.description or f"Kategorie: {category.name}",
"category": category.name,
"has_children": True,
"color_code": category.color_code or "#9F7AEA"
}
category_nodes.append(category_node)
# Verbindung vom Wissen-Knoten zur Kategorie
edges.append({
"source_id": str(wissen_node.id),
"target_id": f"cat_{category.id}",
"strength": 0.9
})
nodes.extend(category_nodes)
response = {
"nodes": nodes,
"edges": edges
}
app.logger.info(f"Mindmap-Daten für 'root' erfolgreich aus Datenbank geladen. {len(nodes)} Knoten gefunden.")
return jsonify(response)
elif node_id.startswith('cat_'):
# Unterkategorien einer Hauptkategorie anzeigen
category_id = node_id.replace('cat_', '')
try:
category_id = int(category_id)
category = Category.query.get(category_id)
if not category:
app.logger.warning(f"Kategorie mit ID {category_id} nicht gefunden.")
return jsonify(fallback_data)
nodes = []
edges = []
# Unterkategorien dieser Kategorie
subcategories = Category.query.filter_by(parent_id=category_id).all()
for subcat in subcategories:
subcat_node = {
"id": f"subcat_{subcat.id}",
"name": subcat.name,
"description": subcat.description or f"Unterkategorie: {subcat.name}",
"category": category.name,
"has_children": True,
"color_code": subcat.color_code or category.color_code
}
nodes.append(subcat_node)
# Verbindung von der Hauptkategorie zur Unterkategorie
edges.append({
"source_id": node_id,
"target_id": f"subcat_{subcat.id}",
"strength": 0.8
})
# Wenn es keine Unterkategorien gibt, zeige verwandte Knoten
if not subcategories:
# Im Fallback-Modus zurückfallen
app.logger.info(f"Keine Unterkategorien für Kategorie {category.name} gefunden, verwende Fallback-Daten.")
return jsonify(fallback_data)
response = {
"nodes": nodes,
"edges": edges
}
return jsonify(response)
except (ValueError, TypeError):
app.logger.error(f"Ungültige Kategorie-ID: {category_id}")
return jsonify(fallback_data)
else: # Kategorien als Knoten hinzufügen
# Versuche, einen MindMapNode mit der gegebenen ID zu finden for category in categories:
try: nodes.append({
node_id_int = int(node_id) 'id': f'cat_{category.id}',
node = MindMapNode.query.get(node_id_int) 'name': category.name,
'description': category.description or '',
if node: 'color_code': category.color_code or '#9F7AEA',
# Zeige Gedanken oder verwandte Knoten 'category': category.name,
# Hier würden wir verwandte Knoten aus der Datenbank laden 'has_children': bool(category.children),
# Für den Moment verwenden wir Fallback-Daten 'icon': category.icon or 'fa-solid fa-circle'
app.logger.info(f"Knoten {node.name} gefunden, aber keine spezifische Verarbeitung implementiert.") })
return jsonify(fallback_data)
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)
# Wenn wir hier ankommen, gibt es keine spezifische Verarbeitung für diese node_id # Kanten erstellen (vereinheitlichte Schlüssel)
return jsonify(fallback_data) 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: except Exception as e:
app.logger.error(f"Fehler beim Abrufen der Mindmap-Daten: {str(e)}") print(f"Fehler beim Abrufen der Root-Mindmap: {str(e)}")
app.logger.error(f"Stack Trace: {traceback.format_exc()}") return jsonify({
return jsonify({"error": "Fehler beim Abrufen der Mindmap-Daten"}), 500 'success': False,
'error': 'Root-Mindmap konnte nicht geladen werden',
'details': str(e)
}), 500
# Spezifische Routen für Kategorien
def get_fallback_mindmap_data(node_id): @app.route('/api/mindmap/philosophy')
""" def get_philosophy_mindmap_data():
Liefert Fallback-Daten für die Mindmap, wenn die Datenbank nicht verfügbar ist. return get_category_mindmap_data('Philosophie')
"""
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
@app.route('/api/mindmap/science') @app.route('/api/mindmap/science')
def get_science_mindmap(): def get_science_mindmap_data():
""" return get_category_mindmap_data('Wissenschaft')
Gibt die Wissenschafts-Mindmap zurück für Frontend-Kompatibilität.
"""
app.logger.info("Wissenschafts-Mindmap-Daten werden angefordert.")
try:
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}
]
return jsonify({
"nodes": nodes,
"edges": edges
})
except Exception as e:
app.logger.error(f"Fehler beim Abrufen der Wissenschafts-Mindmap-Daten: {str(e)}")
return jsonify({"error": "Fehler beim Abrufen der Mindmap-Daten"}), 500
@app.route('/api/mindmap/root')
def get_root_mindmap():
"""
Gibt die Root-Mindmap zurück, um Kompatibilität mit dem Frontend sicherzustellen.
"""
app.logger.info("Root-Mindmap-Daten werden angefordert.")
try:
# Fallback-Daten
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}
]
response = {
"nodes": nodes,
"edges": edges
}
return jsonify(response)
except Exception as e:
app.logger.error(f"Fehler beim Abrufen der Root-Mindmap-Daten: {str(e)}")
app.logger.error(f"Stack Trace: {traceback.format_exc()}")
return jsonify({"error": "Fehler beim Abrufen der Mindmap-Daten"}), 500
@app.route('/api/mindmap/technology') @app.route('/api/mindmap/technology')
def get_technology_mindmap(): def get_technology_mindmap_data():
""" return get_category_mindmap_data('Technologie')
Gibt die Technologie-Mindmap zurück für Frontend-Kompatibilität.
"""
app.logger.info("Technologie-Mindmap-Daten werden angefordert.")
try:
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}
]
return jsonify({
"nodes": nodes,
"edges": edges
})
except Exception as e:
app.logger.error(f"Fehler beim Abrufen der Technologie-Mindmap-Daten: {str(e)}")
return jsonify({"error": "Fehler beim Abrufen der Mindmap-Daten"}), 500
@app.route('/api/mindmap/arts') @app.route('/api/mindmap/arts')
def get_arts_mindmap(): def get_arts_mindmap_data():
""" return get_category_mindmap_data('Künste')
Gibt die Künste-Mindmap zurück für Frontend-Kompatibilität.
"""
app.logger.info("Künste-Mindmap-Daten werden angefordert.")
try:
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}
]
return jsonify({
"nodes": nodes,
"edges": edges
})
except Exception as e:
app.logger.error(f"Fehler beim Abrufen der Künste-Mindmap-Daten: {str(e)}")
return jsonify({"error": "Fehler beim Abrufen der Mindmap-Daten"}), 500
@app.route('/api/mindmap/philosophy') # Generische Route für spezifische Knoten
def get_philosophy_mindmap(): @app.route('/api/mindmap/<node_id>')
""" def get_mindmap_data(node_id):
Gibt die Philosophie-Mindmap zurück für Frontend-Kompatibilität. """Liefert die Daten für einen spezifischen Mindmap-Knoten."""
"""
app.logger.info("Philosophie-Mindmap-Daten werden angefordert.")
try: try:
nodes = [ # Prüfen, ob es sich um eine spezielle Route handelt
{ if node_id in ['root', 'philosophy', 'science', 'technology', 'arts']:
"id": "epistemology", return jsonify({
"name": "Erkenntnistheorie", 'success': False,
"description": "Untersuchung der Natur und Grenzen menschlicher Erkenntnis", 'error': 'Ungültige Knoten-ID',
"category": "Philosophie", 'details': 'Diese ID ist für spezielle Routen reserviert'
"has_children": True, }), 400
"color_code": "#9F7AEA"
}, # Knoten mit Unterknoten in einer Abfrage laden
{ node = MindMapNode.query.options(
"id": "ethics", joinedload(MindMapNode.children)
"name": "Ethik", ).get_or_404(node_id)
"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 = [ # Basis-Knoten erstellen
{"source_id": "philosophy", "target_id": "epistemology", "strength": 0.8}, nodes = [{
{"source_id": "philosophy", "target_id": "ethics", "strength": 0.8}, 'id': str(node.id),
{"source_id": "philosophy", "target_id": "metaphysics", "strength": 0.8} '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({ return jsonify({
"nodes": nodes, 'success': True,
"edges": edges 'nodes': nodes,
'edges': edges
}) })
except Exception as e: except Exception as e:
app.logger.error(f"Fehler beim Abrufen der Philosophie-Mindmap-Daten: {str(e)}") print(f"Fehler beim Abrufen der Mindmap-Daten für Knoten {node_id}: {str(e)}")
return jsonify({"error": "Fehler beim Abrufen der Mindmap-Daten"}), 500 return jsonify({
'success': False,
'error': 'Mindmap-Daten konnten nicht geladen werden',
'details': str(e)
}), 500
+2526
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+4030
View File
File diff suppressed because it is too large Load Diff
+439 -488
View File
@@ -5,199 +5,260 @@
* Implementiert Lazy Loading & Progressive Disclosure * Implementiert Lazy Loading & Progressive Disclosure
*/ */
// Mock-Datenbank für Subthemen (später durch echte DB-Abfragen ersetzen) // Neue zentrale Konfiguration
const subthemesDatabase = { const mindmapConfig = {
'philosophy': [ categories: {
{ 'Philosophie': {
id: 'epistemology', icon: 'fa-solid fa-lightbulb',
label: 'Erkenntnistheorie', color: '#b71c1c',
category: 'Philosophie', description: 'Die Lehre vom Denken und der Erkenntnis'
description: 'Untersuchung der Natur und Grenzen menschlicher Erkenntnis',
hasChildren: true
}, },
{ 'Wissenschaft': {
id: 'ethics', icon: 'fa-solid fa-atom',
label: 'Ethik', color: '#f4b400',
category: 'Philosophie', description: 'Systematische Erforschung der Natur und Gesellschaft'
description: 'Lehre vom moralisch richtigen Handeln',
hasChildren: true
}, },
{ 'Technologie': {
id: 'metaphysics', icon: 'fa-solid fa-microchip',
label: 'Metaphysik', color: '#0d47a1',
category: 'Philosophie', description: 'Anwendung wissenschaftlicher Erkenntnisse'
description: 'Grundfragen des Seins und der Wirklichkeit', },
hasChildren: true 'Künste': {
icon: 'fa-solid fa-palette',
color: '#c2185b',
description: 'Kreativer Ausdruck und künstlerische Gestaltung'
} }
], },
'science': [ defaultNodeStyle: {
{ fontSize: 18,
id: 'physics', fontColor: '#fff',
label: 'Physik', neuronSize: 8,
category: 'Wissenschaft', neuronActivity: 0.8
description: 'Lehre von der Materie und ihren Wechselwirkungen', },
hasChildren: true centerNodeStyle: {
}, fontSize: 22,
{ fontColor: '#222',
id: 'biology', neuronSize: 12,
label: 'Biologie', neuronActivity: 1.0,
category: 'Wissenschaft', color: '#f5f5f5',
description: 'Lehre von den Lebewesen und ihren Lebensprozessen', icon: 'fa-solid fa-circle'
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
}
]
}; };
// Icon-Definitionen für Kategorien (FontAwesome oder SVG-URL) // Zentrale Styling-Konfiguration
const categoryIcons = { const mindmapStyles = {
'Philosophie': 'fa-solid fa-lightbulb', node: {
'Wissenschaft': 'fa-solid fa-atom', base: {
'Technologie': 'fa-solid fa-microchip', 'background-color': 'data(color)',
'Künste': 'fa-solid fa-palette', 'label': 'data(label)',
'Psychologie': 'fa-solid fa-brain' 'color': '#ffffff',
}; 'text-background-color': 'rgba(0, 0, 0, 0.7)',
'text-background-opacity': 0.8,
// Farben für Kategorien (wie im Bild) 'text-background-padding': '4px',
const categoryColors = { 'text-valign': 'center',
'Philosophie': '#b71c1c', // Rot 'text-halign': 'center',
'Wissenschaft': '#f4b400', // Gelb 'font-size': 16,
'Technologie': '#0d47a1', // Blau 'width': 'mapData(neuronSize, 3, 10, 30, 60)',
'Künste': '#c2185b', // Pink 'height': 'mapData(neuronSize, 3, 10, 30, 60)',
'Psychologie': '#009688' // Türkis 'border-width': 2,
}; 'border-color': '#ffffff',
'border-opacity': 0.8,
// Initiale Mindmap-Daten (nur oberste Ebene, mit Icon und Farbe) 'shape': 'ellipse',
const mindmapData = { 'background-opacity': 0.85
nodes: [
{
id: 'center',
label: 'Wissenskarte',
isCenter: true,
color: '#f5f5f5',
icon: 'fa-solid fa-circle',
fontColor: '#222',
fontSize: 22,
neuronSize: 12,
neuronActivity: 1.0
}, },
{ center: {
id: 'philosophy', 'background-color': '#f5f5f5',
label: 'Philosophie', 'color': '#222',
category: 'Philosophie', 'font-size': 20,
description: 'Die Lehre vom Denken und der Erkenntnis', 'border-width': 3,
hasChildren: true, 'width': 100,
expanded: false, 'height': 100
neuronSize: 8,
neuronActivity: 0.8,
color: categoryColors['Philosophie'],
icon: categoryIcons['Philosophie'],
fontColor: '#fff',
fontSize: 18
}, },
{ selected: {
id: 'science', 'border-color': '#f59e42',
label: 'Wissenschaft', 'border-width': 3,
category: 'Wissenschaft', 'background-opacity': 1
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
} }
], },
edges: [ edge: {
{ source: 'center', target: 'philosophy', strength: 0.9 }, base: {
{ source: 'center', target: 'science', strength: 0.9 }, 'width': function(ele) {
{ source: 'center', target: 'technology', strength: 0.9 }, return ele.data('strength') ? ele.data('strength') * 2 : 1;
{ source: 'center', target: 'arts', strength: 0.9 } },
] '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 // Warte bis DOM geladen ist
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
console.log('DOMContentLoaded Event ausgelöst'); console.log('DOMContentLoaded Event ausgelöst');
@@ -257,79 +318,22 @@ document.addEventListener('DOMContentLoaded', function() {
style: [ style: [
{ {
selector: 'node', selector: 'node',
style: { style: mindmapStyles.node.base
'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]', selector: 'node[isCenter]',
style: { style: mindmapStyles.node.center
'background-color': '#f5f5f5',
'color': '#222',
'font-size': 20,
'border-width': 3,
'width': 100,
'height': 100
}
}, },
{ {
selector: 'node:selected', selector: 'node:selected',
style: { style: mindmapStyles.node.selected
'border-color': '#f59e42',
'border-width': 3,
'background-opacity': 1
}
}, },
{ {
selector: 'edge', selector: 'edge',
style: { style: mindmapStyles.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: { layout: mindmapStyles.layout.base
name: 'concentric',
fit: true,
padding: 50,
animate: true,
concentric: function(node) {
return node.data('isCenter') ? 2 : 1;
},
levelWidth: function() { return 1; }
}
}); });
console.log('Cytoscape initialisiert'); console.log('Cytoscape initialisiert');
@@ -469,63 +473,42 @@ function initializeNeuralDesign(cy) {
// Modifiziere die updateMindmap Funktion // Modifiziere die updateMindmap Funktion
function updateMindmap() { function updateMindmap() {
if (!cy) return; if (!cy) return;
// Bestehende Elemente entfernen // Bestehende Elemente entfernen
cy.elements().remove(); cy.elements().remove();
// Neue Knoten hinzufügen // Neue Knoten hinzufügen
mindmapData.nodes.forEach(node => { mindmapData.nodes.forEach(node => {
cy.add({ cy.add({
group: 'nodes', group: 'nodes',
data: { data: {
id: node.id, id: node.id,
label: node.label, label: node.name,
category: node.category, category: node.category,
description: node.description, description: node.description,
hasChildren: node.hasChildren, hasChildren: node.has_children,
expanded: node.expanded, expanded: false,
neuronSize: node.neuronSize, color: node.color_code || mindmapConfig.categories[node.category]?.color || '#60a5fa',
neuronActivity: node.neuronActivity, icon: node.icon || mindmapConfig.categories[node.category]?.icon || 'fa-solid fa-circle'
color: node.color, }
icon: node.icon, });
fontColor: node.fontColor,
fontSize: node.fontSize
}
}); });
});
// Neue Kanten hinzufügen
// Neue Kanten hinzufügen mindmapData.edges.forEach(edge => {
mindmapData.edges.forEach(edge => { cy.add({
cy.add({ group: 'edges',
group: 'edges', data: {
data: { source: edge.source,
source: edge.source, target: edge.target,
target: edge.target, strength: edge.strength || 0.5
strength: edge.strength }
} });
}); });
});
// Layout aktualisieren
// Neuronales Design initialisieren cy.layout(mindmapStyles.layout.base).run();
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();
} }
/** /**
@@ -764,31 +747,69 @@ function startNeuralActivitySimulation(cy) {
// Hilfe-Funktion zum Hinzufügen eines Flash-Hinweises // Hilfe-Funktion zum Hinzufügen eines Flash-Hinweises
function showFlash(message, type = 'info') { function showFlash(message, type = 'info') {
const flashContainer = document.getElementById('flash-messages') || createFlashContainer(); const flashContainer = createFlashContainer();
const flash = document.createElement('div');
const flashMsg = document.createElement('div'); flash.className = `flash-message ${type}`;
flashMsg.className = `flash-message flash-${type} mb-2 p-3 rounded`; flash.textContent = message;
flashMsg.innerHTML = ` flashContainer.appendChild(flash);
<div class="flex items-center justify-between"> document.body.appendChild(flashContainer);
<div>${message}</div>
<button class="close-flash ml-2">&times;</button>
</div>
`;
flashContainer.appendChild(flashMsg);
// Nach 5 Sekunden automatisch ausblenden
setTimeout(() => { setTimeout(() => {
flashMsg.style.opacity = '0'; flash.classList.add('show');
setTimeout(() => flashMsg.remove(), 300); setTimeout(() => {
}, 5000); flash.classList.remove('show');
setTimeout(() => {
// Close-Button flashContainer.remove();
const closeBtn = flashMsg.querySelector('.close-flash'); }, 300);
closeBtn.addEventListener('click', () => { }, 3000);
flashMsg.style.opacity = '0'; }, 100);
setTimeout(() => flashMsg.remove(), 300); }
});
/**
* 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 // Hilfsfunktion zum Erstellen eines Flash-Containers, falls keiner existiert
@@ -798,152 +819,6 @@ function createFlashContainer() {
container.className = 'fixed top-4 right-4 z-50 w-64'; container.className = 'fixed top-4 right-4 z-50 w-64';
document.body.appendChild(container); document.body.appendChild(container);
return container; return container;
}
// Funktion zum Laden der Mindmap-Daten aus der Datenbank
async function loadMindmapData(nodeId = null) {
try {
let url;
// Wir müssen zwischen numerischen IDs und String-IDs unterscheiden
if (nodeId === null || nodeId === undefined) {
// Wenn keine ID angegeben ist, verwende 'root'
url = '/api/mindmap/root';
} else if (isNaN(parseInt(nodeId))) {
// Für String-IDs wie 'root', 'technology', 'arts' - direkte Route
url = `/api/mindmap/${nodeId}`;
} else {
// Für numerische IDs - neue Route mit /id/ Präfix
url = `/api/mindmap/id/${nodeId}`;
}
const response = await fetch(url);
if (!response.ok) throw new Error('Fehler beim Laden der Mindmap-Daten');
return await response.json();
} catch (error) {
console.error('Fehler beim Laden der Mindmap-Daten:', error);
showFlash('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 // Funktion zum Laden der Subthemen
@@ -1003,24 +878,7 @@ async function loadSubthemes(node) {
})) }))
], ],
style: cy.style(), style: cy.style(),
layout: { layout: mindmapStyles.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
}
}); });
// Event-Listener für die neue Mindmap // Event-Listener für die neue Mindmap
@@ -1120,6 +978,99 @@ style.textContent = `
color: #fff; color: #fff;
text-shadow: 0 2px 8px rgba(0,0,0,0.25); 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); document.head.appendChild(style);
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.