Compare commits
7 Commits
256d38e140
...
fab8d10f03
| Author | SHA1 | Date | |
|---|---|---|---|
| fab8d10f03 | |||
| dec30e4681 | |||
| a1bd999c6a | |||
| b1d33ce643 | |||
| 293f877017 | |||
| e86d0b0f90 | |||
| 059fd167d6 |
Binary file not shown.
Binary file not shown.
851
app.py
851
app.py
@@ -26,6 +26,7 @@ import sqlalchemy
|
||||
import ssl
|
||||
import certifi
|
||||
import os
|
||||
from sqlalchemy.orm import joinedload
|
||||
|
||||
# Modelle importieren
|
||||
from models import (
|
||||
@@ -277,7 +278,7 @@ def create_default_categories():
|
||||
"icon": "fa-palette",
|
||||
"subcategories": [
|
||||
{"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"}
|
||||
]
|
||||
},
|
||||
@@ -2342,750 +2343,184 @@ if __name__ == '__main__':
|
||||
db.create_all()
|
||||
socketio.run(app, debug=True, host='0.0.0.0')
|
||||
|
||||
@app.route('/api/refresh-mindmap')
|
||||
def refresh_mindmap():
|
||||
"""
|
||||
API-Endpunkt zum Neuladen der Mindmap-Daten,
|
||||
wenn die Datenbank-Verbindung vorübergehend fehlgeschlagen ist
|
||||
"""
|
||||
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()
|
||||
|
||||
# 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
|
||||
# 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'
|
||||
})
|
||||
|
||||
# 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
|
||||
|
||||
# Die Route '/mindmap' wird bereits in Zeile 474 definiert - doppelte Definition entfernt
|
||||
|
||||
# Weiterleitung für Community/Forum-Routen
|
||||
@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':
|
||||
@app.route('/api/mindmap/root')
|
||||
def get_root_mindmap_data():
|
||||
"""Liefert die Daten für die Root-Mindmap."""
|
||||
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
|
||||
# Hauptkategorien mit Unterkategorien in einer Abfrage laden
|
||||
categories = Category.query.filter_by(parent_id=None).options(
|
||||
joinedload(Category.children)
|
||||
).all()
|
||||
|
||||
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:
|
||||
# Fallback-Daten falls Datenbankzugriff fehlschlägt
|
||||
fallback_data = get_fallback_mindmap_data(node_id)
|
||||
|
||||
if node_id == 'root':
|
||||
# Hauptebene der Mindmap - finde den "Wissen"-Knoten und seine Verbindungen
|
||||
wissen_node = MindMapNode.query.filter_by(name="Wissen").first()
|
||||
|
||||
if not wissen_node:
|
||||
app.logger.warning("'Wissen'-Knoten nicht in der Datenbank gefunden, Fallback zu Hardcoded-Daten.")
|
||||
return jsonify(fallback_data)
|
||||
|
||||
# Zentrum der Mindmap ist der "Wissen"-Knoten
|
||||
# Basis-Knoten erstellen
|
||||
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
|
||||
'id': 'root',
|
||||
'name': 'Wissen',
|
||||
'description': 'Zentrale Wissensbasis',
|
||||
'color_code': '#4299E1',
|
||||
'is_center': True,
|
||||
'has_children': bool(categories),
|
||||
'icon': 'fa-solid fa-circle'
|
||||
}]
|
||||
|
||||
# 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
|
||||
# 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'
|
||||
})
|
||||
|
||||
nodes.extend(category_nodes)
|
||||
# Kanten erstellen (vereinheitlichte Schlüssel)
|
||||
edges = [{
|
||||
'source': 'root',
|
||||
'target': f'cat_{category.id}',
|
||||
'strength': 0.8
|
||||
} for category in categories]
|
||||
|
||||
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
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'nodes': nodes,
|
||||
'edges': edges
|
||||
})
|
||||
|
||||
# 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:
|
||||
# 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)
|
||||
|
||||
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
|
||||
return jsonify(fallback_data)
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
# 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():
|
||||
"""
|
||||
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
|
||||
def get_science_mindmap_data():
|
||||
return get_category_mindmap_data('Wissenschaft')
|
||||
|
||||
@app.route('/api/mindmap/technology')
|
||||
def get_technology_mindmap():
|
||||
"""
|
||||
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
|
||||
def get_technology_mindmap_data():
|
||||
return get_category_mindmap_data('Technologie')
|
||||
|
||||
@app.route('/api/mindmap/arts')
|
||||
def get_arts_mindmap():
|
||||
"""
|
||||
Gibt die Künste-Mindmap zurück für Frontend-Kompatibilität.
|
||||
"""
|
||||
app.logger.info("Künste-Mindmap-Daten werden angefordert.")
|
||||
def get_arts_mindmap_data():
|
||||
return get_category_mindmap_data('Künste')
|
||||
|
||||
# Generische Route für spezifische Knoten
|
||||
@app.route('/api/mindmap/<node_id>')
|
||||
def get_mindmap_data(node_id):
|
||||
"""Liefert die Daten für einen spezifischen Mindmap-Knoten."""
|
||||
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}
|
||||
]
|
||||
|
||||
# Prüfen, ob es sich um eine spezielle Route handelt
|
||||
if node_id in ['root', 'philosophy', 'science', 'technology', 'arts']:
|
||||
return jsonify({
|
||||
"nodes": nodes,
|
||||
"edges": edges
|
||||
'success': False,
|
||||
'error': 'Ungültige Knoten-ID',
|
||||
'details': 'Diese ID ist für spezielle Routen reserviert'
|
||||
}), 400
|
||||
|
||||
# Knoten mit Unterknoten in einer Abfrage laden
|
||||
node = MindMapNode.query.options(
|
||||
joinedload(MindMapNode.children)
|
||||
).get_or_404(node_id)
|
||||
|
||||
# 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'
|
||||
})
|
||||
|
||||
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')
|
||||
def get_philosophy_mindmap():
|
||||
"""
|
||||
Gibt die Philosophie-Mindmap zurück für Frontend-Kompatibilität.
|
||||
"""
|
||||
app.logger.info("Philosophie-Mindmap-Daten werden angefordert.")
|
||||
|
||||
try:
|
||||
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}
|
||||
]
|
||||
# Kanten erstellen (vereinheitlichte Schlüssel)
|
||||
edges = [{
|
||||
'source': str(node.id),
|
||||
'target': str(child.id),
|
||||
'strength': 0.8
|
||||
} for child in node.children]
|
||||
|
||||
return jsonify({
|
||||
"nodes": nodes,
|
||||
"edges": edges
|
||||
'success': True,
|
||||
'nodes': nodes,
|
||||
'edges': edges
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
app.logger.error(f"Fehler beim Abrufen der Philosophie-Mindmap-Daten: {str(e)}")
|
||||
return jsonify({"error": "Fehler beim Abrufen der Mindmap-Daten"}), 500
|
||||
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
|
||||
|
||||
|
||||
2526
app.py.bak
Normal file
2526
app.py.bak
Normal file
File diff suppressed because it is too large
Load Diff
BIN
fix_routes.py
Normal file
BIN
fix_routes.py
Normal file
Binary file not shown.
4030
logs/app.log
4030
logs/app.log
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
defaultNodeStyle: {
|
||||
fontSize: 18,
|
||||
fontColor: '#fff',
|
||||
neuronSize: 8,
|
||||
neuronActivity: 0.8
|
||||
},
|
||||
{
|
||||
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)
|
||||
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',
|
||||
centerNodeStyle: {
|
||||
fontSize: 22,
|
||||
fontColor: '#222',
|
||||
neuronSize: 12,
|
||||
neuronActivity: 1.0
|
||||
},
|
||||
{
|
||||
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
|
||||
},
|
||||
{
|
||||
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
|
||||
neuronActivity: 1.0,
|
||||
color: '#f5f5f5',
|
||||
icon: 'fa-solid fa-circle'
|
||||
}
|
||||
],
|
||||
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 }
|
||||
]
|
||||
};
|
||||
|
||||
// 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
|
||||
},
|
||||
center: {
|
||||
'background-color': '#f5f5f5',
|
||||
'color': '#222',
|
||||
'font-size': 20,
|
||||
'border-width': 3,
|
||||
'width': 100,
|
||||
'height': 100
|
||||
},
|
||||
selected: {
|
||||
'border-color': '#f59e42',
|
||||
'border-width': 3,
|
||||
'background-opacity': 1
|
||||
}
|
||||
},
|
||||
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');
|
||||
@@ -480,17 +484,13 @@ function updateMindmap() {
|
||||
group: 'nodes',
|
||||
data: {
|
||||
id: node.id,
|
||||
label: node.label,
|
||||
label: node.name,
|
||||
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
|
||||
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'
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -502,30 +502,13 @@ function updateMindmap() {
|
||||
data: {
|
||||
source: edge.source,
|
||||
target: edge.target,
|
||||
strength: edge.strength
|
||||
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 flashContainer = createFlashContainer();
|
||||
const flash = document.createElement('div');
|
||||
flash.className = `flash-message ${type}`;
|
||||
flash.textContent = message;
|
||||
flashContainer.appendChild(flash);
|
||||
document.body.appendChild(flashContainer);
|
||||
|
||||
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">×</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
flashContainer.appendChild(flashMsg);
|
||||
|
||||
// Nach 5 Sekunden automatisch ausblenden
|
||||
setTimeout(() => {
|
||||
flashMsg.style.opacity = '0';
|
||||
setTimeout(() => flashMsg.remove(), 300);
|
||||
}, 5000);
|
||||
flash.classList.add('show');
|
||||
setTimeout(() => {
|
||||
flash.classList.remove('show');
|
||||
setTimeout(() => {
|
||||
flashContainer.remove();
|
||||
}, 300);
|
||||
}, 3000);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// Close-Button
|
||||
const closeBtn = flashMsg.querySelector('.close-flash');
|
||||
closeBtn.addEventListener('click', () => {
|
||||
flashMsg.style.opacity = '0';
|
||||
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
|
||||
@@ -800,152 +821,6 @@ function createFlashContainer() {
|
||||
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
|
||||
async function loadSubthemes(node) {
|
||||
try {
|
||||
@@ -1003,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
|
||||
@@ -1120,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);
|
||||
|
||||
|
||||
1078
static/js/update_mindmap.js.bak
Normal file
1078
static/js/update_mindmap.js.bak
Normal file
File diff suppressed because it is too large
Load Diff
BIN
static/js/update_mindmap.js.new
Normal file
BIN
static/js/update_mindmap.js.new
Normal file
Binary file not shown.
1078
static/js/update_mindmap.js.original
Normal file
1078
static/js/update_mindmap.js.original
Normal file
File diff suppressed because it is too large
Load Diff
BIN
update_routes.py
Normal file
BIN
update_routes.py
Normal 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.
Reference in New Issue
Block a user