From 5b9ae854537c96875447f34cd7c770bc962928ae Mon Sep 17 00:00:00 2001 From: marwin Date: Fri, 16 May 2025 21:02:09 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20Erweiterung=20der=20Mindmap?= =?UTF-8?q?-Funktionalit=C3=A4t=20durch=20Verbesserung=20der=20Ladeanimati?= =?UTF-8?q?on,=20Fehlerbehandlung=20und=20CSS-Anpassungen;=20Protokollaktu?= =?UTF-8?q?alisierungen=20zur=20Fehlerverfolgung=20und=20Optimierung=20der?= =?UTF-8?q?=20Benutzeroberfl=C3=A4che.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- logs/app.log | 210 ++++++++ static/css/mindmap.css | 156 +++++- static/js/update_mindmap.js | 948 +++++++++++++++++++++--------------- templates/base.html | 2 +- templates/mindmap.html | 244 ++++++---- 5 files changed, 1028 insertions(+), 532 deletions(-) diff --git a/logs/app.log b/logs/app.log index a026c04..d65938a 100644 --- a/logs/app.log +++ b/logs/app.log @@ -554,3 +554,213 @@ werkzeug.exceptions.NotFound: 404 Not Found: The requested URL was not found on 2025-05-16 20:28:49,108 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] 2025-05-16 20:28:50,916 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] 2025-05-16 20:28:50,916 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:36:14,790 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:36:17,121 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:36:17,121 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:36:22,222 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:36:25,551 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:36:25,551 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:40:02,710 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:40:04,924 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:40:04,924 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:40:11,918 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:40:13,550 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:40:13,550 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:44:54,482 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:44:58,286 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:44:58,286 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:45:04,175 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:45:06,483 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:45:06,483 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:46:46,140 ERROR: Fehler 404: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. +Endpoint: /.well-known/appspecific/com.chrome.devtools.json, Method: GET, IP: 127.0.0.1 +Nicht angemeldet +Traceback (most recent call last): + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1823, in full_dispatch_request + rv = self.dispatch_request() + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1788, in dispatch_request + self.raise_routing_exception(req) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^ + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1770, in raise_routing_exception + raise request.routing_exception # type: ignore + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\ctx.py", line 351, in match_request + result = self.url_adapter.match(return_rule=True) # type: ignore + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\werkzeug\routing\map.py", line 624, in match + raise NotFound() from None +werkzeug.exceptions.NotFound: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. + [in C:\Users\firem\Desktop\111\Systades\website\app.py:93] +2025-05-16 20:46:46,140 ERROR: Fehler 404: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. +Endpoint: /.well-known/appspecific/com.chrome.devtools.json, Method: GET, IP: 127.0.0.1 +Nicht angemeldet +Traceback (most recent call last): + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1823, in full_dispatch_request + rv = self.dispatch_request() + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1788, in dispatch_request + self.raise_routing_exception(req) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^ + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1770, in raise_routing_exception + raise request.routing_exception # type: ignore + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\ctx.py", line 351, in match_request + result = self.url_adapter.match(return_rule=True) # type: ignore + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\werkzeug\routing\map.py", line 624, in match + raise NotFound() from None +werkzeug.exceptions.NotFound: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. + [in C:\Users\firem\Desktop\111\Systades\website\app.py:93] +2025-05-16 20:50:10,093 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:50:13,779 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:50:13,779 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:50:34,297 ERROR: Fehler 404: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. +Endpoint: /.well-known/appspecific/com.chrome.devtools.json, Method: GET, IP: 127.0.0.1 +Nicht angemeldet +Traceback (most recent call last): + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1823, in full_dispatch_request + rv = self.dispatch_request() + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1788, in dispatch_request + self.raise_routing_exception(req) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^ + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1770, in raise_routing_exception + raise request.routing_exception # type: ignore + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\ctx.py", line 351, in match_request + result = self.url_adapter.match(return_rule=True) # type: ignore + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\werkzeug\routing\map.py", line 624, in match + raise NotFound() from None +werkzeug.exceptions.NotFound: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. + [in C:\Users\firem\Desktop\111\Systades\website\app.py:93] +2025-05-16 20:50:34,297 ERROR: Fehler 404: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. +Endpoint: /.well-known/appspecific/com.chrome.devtools.json, Method: GET, IP: 127.0.0.1 +Nicht angemeldet +Traceback (most recent call last): + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1823, in full_dispatch_request + rv = self.dispatch_request() + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1788, in dispatch_request + self.raise_routing_exception(req) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^ + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1770, in raise_routing_exception + raise request.routing_exception # type: ignore + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\ctx.py", line 351, in match_request + result = self.url_adapter.match(return_rule=True) # type: ignore + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\werkzeug\routing\map.py", line 624, in match + raise NotFound() from None +werkzeug.exceptions.NotFound: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. + [in C:\Users\firem\Desktop\111\Systades\website\app.py:93] +2025-05-16 20:50:34,970 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:50:38,891 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:50:38,891 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:57:15,614 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:57:27,085 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:57:27,085 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:57:57,575 ERROR: Fehler 404: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. +Endpoint: /.well-known/appspecific/com.chrome.devtools.json, Method: GET, IP: 127.0.0.1 +Nicht angemeldet +Traceback (most recent call last): + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1823, in full_dispatch_request + rv = self.dispatch_request() + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1788, in dispatch_request + self.raise_routing_exception(req) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^ + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1770, in raise_routing_exception + raise request.routing_exception # type: ignore + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\ctx.py", line 351, in match_request + result = self.url_adapter.match(return_rule=True) # type: ignore + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\werkzeug\routing\map.py", line 624, in match + raise NotFound() from None +werkzeug.exceptions.NotFound: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. + [in C:\Users\firem\Desktop\111\Systades\website\app.py:93] +2025-05-16 20:57:57,575 ERROR: Fehler 404: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. +Endpoint: /.well-known/appspecific/com.chrome.devtools.json, Method: GET, IP: 127.0.0.1 +Nicht angemeldet +Traceback (most recent call last): + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1823, in full_dispatch_request + rv = self.dispatch_request() + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1788, in dispatch_request + self.raise_routing_exception(req) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^ + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1770, in raise_routing_exception + raise request.routing_exception # type: ignore + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\ctx.py", line 351, in match_request + result = self.url_adapter.match(return_rule=True) # type: ignore + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\werkzeug\routing\map.py", line 624, in match + raise NotFound() from None +werkzeug.exceptions.NotFound: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. + [in C:\Users\firem\Desktop\111\Systades\website\app.py:93] +2025-05-16 20:57:59,166 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:58:02,510 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 20:58:02,510 INFO: Anwendung gestartet [in C:\Users\firem\Desktop\111\Systades\website\app.py:77] +2025-05-16 21:00:03,808 ERROR: Fehler 404: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. +Endpoint: /.well-known/appspecific/com.chrome.devtools.json, Method: GET, IP: 127.0.0.1 +Nicht angemeldet +Traceback (most recent call last): + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1823, in full_dispatch_request + rv = self.dispatch_request() + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1788, in dispatch_request + self.raise_routing_exception(req) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^ + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1770, in raise_routing_exception + raise request.routing_exception # type: ignore + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\ctx.py", line 351, in match_request + result = self.url_adapter.match(return_rule=True) # type: ignore + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\werkzeug\routing\map.py", line 624, in match + raise NotFound() from None +werkzeug.exceptions.NotFound: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. + [in C:\Users\firem\Desktop\111\Systades\website\app.py:93] +2025-05-16 21:00:03,808 ERROR: Fehler 404: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. +Endpoint: /.well-known/appspecific/com.chrome.devtools.json, Method: GET, IP: 127.0.0.1 +Nicht angemeldet +Traceback (most recent call last): + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1823, in full_dispatch_request + rv = self.dispatch_request() + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1788, in dispatch_request + self.raise_routing_exception(req) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^ + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1770, in raise_routing_exception + raise request.routing_exception # type: ignore + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\ctx.py", line 351, in match_request + result = self.url_adapter.match(return_rule=True) # type: ignore + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\werkzeug\routing\map.py", line 624, in match + raise NotFound() from None +werkzeug.exceptions.NotFound: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. + [in C:\Users\firem\Desktop\111\Systades\website\app.py:93] +2025-05-16 21:01:35,404 ERROR: Fehler 404: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. +Endpoint: /.well-known/appspecific/com.chrome.devtools.json, Method: GET, IP: 127.0.0.1 +Nicht angemeldet +Traceback (most recent call last): + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1823, in full_dispatch_request + rv = self.dispatch_request() + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1788, in dispatch_request + self.raise_routing_exception(req) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^ + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1770, in raise_routing_exception + raise request.routing_exception # type: ignore + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\ctx.py", line 351, in match_request + result = self.url_adapter.match(return_rule=True) # type: ignore + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\werkzeug\routing\map.py", line 624, in match + raise NotFound() from None +werkzeug.exceptions.NotFound: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. + [in C:\Users\firem\Desktop\111\Systades\website\app.py:93] +2025-05-16 21:01:35,404 ERROR: Fehler 404: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. +Endpoint: /.well-known/appspecific/com.chrome.devtools.json, Method: GET, IP: 127.0.0.1 +Nicht angemeldet +Traceback (most recent call last): + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1823, in full_dispatch_request + rv = self.dispatch_request() + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1788, in dispatch_request + self.raise_routing_exception(req) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^ + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\app.py", line 1770, in raise_routing_exception + raise request.routing_exception # type: ignore + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\flask\ctx.py", line 351, in match_request + result = self.url_adapter.match(return_rule=True) # type: ignore + File "C:\Users\firem\Desktop\111\Systades\website\.venv\Lib\site-packages\werkzeug\routing\map.py", line 624, in match + raise NotFound() from None +werkzeug.exceptions.NotFound: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. + [in C:\Users\firem\Desktop\111\Systades\website\app.py:93] diff --git a/static/css/mindmap.css b/static/css/mindmap.css index 9fa9dcc..a298a88 100644 --- a/static/css/mindmap.css +++ b/static/css/mindmap.css @@ -9,7 +9,91 @@ overflow: hidden; } -/* Toolbar Styles */ +/* Cytoscape Container für die Hauptmindmap */ +#cy { + width: 100%; + height: 100%; + background: transparent; + position: absolute; + top: 0; + left: 0; + z-index: 1; +} + +/* Subpage Styles - Identisches Design wie Hauptmindmap */ +.mindmap-subpage { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--bg-primary); + display: flex; + flex-direction: column; + z-index: 10; + transition: opacity 0.3s ease, transform 0.3s ease; + opacity: 1; + transform: translateY(0); +} + +/* Subpage Header */ +.subpage-header { + display: flex; + align-items: center; + padding: 16px; + background: var(--bg-secondary); + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + z-index: 2; +} + +.dark .subpage-header { + background: rgba(30, 41, 59, 0.8); + border-bottom: 1px solid rgba(255, 255, 255, 0.1); +} + +/* Zurück-Button */ +.back-button { + background: rgba(255, 255, 255, 0.1); + border: none; + color: white; + width: 40px; + height: 40px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.2s ease; + margin-right: 12px; +} + +.back-button:hover { + background: rgba(255, 255, 255, 0.2); + transform: translateY(-2px); +} + +/* Subpage Titel */ +.subpage-title { + font-size: 1.5rem; + font-weight: 600; + color: white; + margin: 0; + background: linear-gradient(90deg, #60a5fa, #8b5cf6); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +/* Subpage Cytoscape Container */ +.subpage-cy-container { + position: relative; + flex: 1; + width: 100%; + height: calc(100% - 72px); + overflow: hidden; + z-index: 1; +} + +/* Toolbar für Zoom-Kontrollen */ .mindmap-toolbar { position: absolute; top: 20px; @@ -18,48 +102,74 @@ display: flex; gap: 8px; padding: 8px; - background: var(--bg-secondary); - border-radius: 12px; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); - z-index: 1000; - transition: all 0.3s ease; -} - -.dark .mindmap-toolbar { background: rgba(30, 41, 59, 0.8); + border-radius: 8px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2); + z-index: 20; + backdrop-filter: blur(8px); border: 1px solid rgba(255, 255, 255, 0.1); } -/* Toolbar Buttons */ .mindmap-toolbar button { + width: 40px; + height: 40px; + border: none; + background: rgba(255, 255, 255, 0.1); + color: white; + border-radius: 6px; display: flex; align-items: center; justify-content: center; - width: 36px; - height: 36px; - border: none; - border-radius: 8px; - background: transparent; - color: var(--text-primary); cursor: pointer; transition: all 0.2s ease; } .mindmap-toolbar button:hover { - background: var(--accent-primary); - color: white; - transform: translateY(-1px); -} - -.mindmap-toolbar button:active { - transform: translateY(0); + background: rgba(139, 92, 246, 0.5); + transform: translateY(-2px); } .mindmap-toolbar button i { font-size: 16px; } +/* Mindmap Header */ +.mindmap-header { + position: absolute; + top: 0; + left: 0; + right: 0; + padding: 1.5rem; + background: rgba(15, 23, 42, 0.8); + backdrop-filter: blur(10px); + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + z-index: 10; + display: flex; + justify-content: space-between; + align-items: center; +} + +/* Dark Mode spezifische Stile */ +.dark .mindmap-subpage { + background: linear-gradient(135deg, #0f172a 0%, #0c1221 100%); +} + +/* Fix für Zoom-Buttons */ +body.dark .mindmap-toolbar button { + background: rgba(255, 255, 255, 0.1); + color: white; +} + +body:not(.dark) .mindmap-toolbar button { + background: rgba(30, 41, 59, 0.2); + color: #1e293b; +} + +/* Kontext-Menü-Anpassungen */ +.context-menu { + z-index: 1000; +} + /* Export Group Styles */ .export-group { position: relative; diff --git a/static/js/update_mindmap.js b/static/js/update_mindmap.js index 2ea7019..ed49cd0 100644 --- a/static/js/update_mindmap.js +++ b/static/js/update_mindmap.js @@ -185,11 +185,64 @@ async function loadMindmapData(nodeId = null) { // Funktion zum Initialisieren der Mindmap async function initializeMindmap() { try { + // Zeige Ladeanimation + const loader = document.getElementById('loader'); + const statusMessage = document.getElementById('statusMessage'); + + if (loader) loader.style.display = 'block'; + if (statusMessage) { + statusMessage.textContent = 'Lade Mindmap...'; + statusMessage.style.display = 'block'; + } + + // Prüfe auf bestehende Cytoscape-Instanz und zerstöre sie + if (window.cy && typeof window.cy.destroy === 'function') { + try { + window.cy.destroy(); + } catch (e) { + console.error('Fehler beim Zerstören der bestehenden Cytoscape-Instanz:', e); + } + window.cy = null; + } + + // Bereinige auch alle Subthemen-Instanzen + if (window.subthemeCyInstances) { + Object.keys(window.subthemeCyInstances).forEach(key => { + try { + if (window.subthemeCyInstances[key] && typeof window.subthemeCyInstances[key].destroy === 'function') { + window.subthemeCyInstances[key].destroy(); + } + } catch (e) { + console.error(`Fehler beim Zerstören der Subthemen-Instanz ${key}:`, e); + } + }); + window.subthemeCyInstances = {}; + } + + // Entferne bestehende Subpages aus dem DOM + const existingSubpages = document.querySelectorAll('.mindmap-subpage'); + existingSubpages.forEach(subpage => { + if (subpage.parentNode) { + subpage.parentNode.removeChild(subpage); + } + }); + + // Lade Mindmap-Daten const data = await loadMindmapData(); if (!data || !data.nodes || !data.edges) { throw new Error('Ungültiges Datenformat: Mindmap-Daten fehlen oder sind unvollständig'); } + // Prüfe, ob der Container existiert + const cyContainer = document.getElementById('cy'); + if (!cyContainer) { + throw new Error('Mindmap-Container #cy nicht gefunden!'); + } + + // Stelle sicher, dass der Container sichtbar ist + cyContainer.style.display = 'block'; + + // Elemente für Cytoscape erstellen const elements = [ // Knoten ...data.nodes.map(node => ({ @@ -215,16 +268,7 @@ async function initializeMindmap() { })) ]; - // Bestehende Cytoscape-Instanz entfernen, falls vorhanden - if (window.cy && typeof window.cy.destroy === 'function') { - window.cy.destroy(); - } - - const cyContainer = document.getElementById('cy'); - if (!cyContainer) { - throw new Error('Mindmap-Container #cy nicht gefunden!'); - } - + // Cytoscape-Instanz erstellen window.cy = cytoscape({ container: cyContainer, elements: elements, @@ -253,8 +297,13 @@ async function initializeMindmap() { maxZoom: 2.5 }); + // Prüfe, ob die Cytoscape-Instanz erfolgreich erstellt wurde + if (!window.cy || !window.cy.container()) { + throw new Error('Cytoscape-Instanz konnte nicht korrekt initialisiert werden'); + } + // Füge neuronale Eigenschaften zu allen Knoten hinzu - cy.nodes().forEach(node => { + window.cy.nodes().forEach(node => { const data = node.data(); // Verwende mindmapConfig für Kategorie-Farben oder einen Standardwert const categoryColor = data.category && mindmapConfig.categories[data.category] @@ -273,7 +322,7 @@ async function initializeMindmap() { }); // Füge synaptische Eigenschaften zu allen Kanten hinzu - cy.edges().forEach(edge => { + window.cy.edges().forEach(edge => { const data = edge.data(); edge.data({ ...data, @@ -283,51 +332,99 @@ async function initializeMindmap() { }); }); - // Event-Listener für Knoten-Klicks - cy.on('tap', 'node', async function(evt) { - const node = evt.target; - console.log('Node clicked:', node.id(), 'hasChildren:', node.data('hasChildren') || node.data('has_children'), 'expanded:', node.data('expanded')); - - if ((node.data('hasChildren') || node.data('has_children')) && !node.data('expanded')) { - await loadSubthemes(node); + // Event-Listener für Knoten-Klicks mit Fehlerbehandlung + window.cy.on('tap', 'node', async function(evt) { + try { + const node = evt.target; + console.log('Node clicked:', node.id(), 'hasChildren:', node.data('hasChildren') || node.data('has_children'), 'expanded:', node.data('expanded')); + + if ((node.data('hasChildren') || node.data('has_children')) && !node.data('expanded')) { + await loadSubthemes(node); + } + } catch (e) { + console.error('Fehler beim Node-Tap-Event:', e); + showUINotification('Fehler beim Verarbeiten des Klicks: ' + e.message, 'error'); } }); // Event-Listener für Hover über Knoten (für Info-Panel) - cy.on('mouseover', 'node', function(evt) { - const node = evt.target; - showNodeInfo(node); + window.cy.on('mouseover', 'node', function(evt) { + try { + const node = evt.target; + showNodeInfo(node); + } catch (e) { + console.error('Fehler beim Mouseover-Event:', e); + } }); - cy.on('mouseout', 'node', function() { - hideNodeInfo(); + window.cy.on('mouseout', 'node', function() { + try { + hideNodeInfo(); + } catch (e) { + console.error('Fehler beim Mouseout-Event:', e); + } }); // Layout ausführen - cy.layout(mindmapStyles.layout.base).run(); + window.cy.layout(mindmapStyles.layout.base).run(); // Starte neuronale Aktivitätssimulation - startNeuralActivitySimulation(cy); + try { + startNeuralActivitySimulation(window.cy); + } catch (e) { + console.error('Fehler beim Starten der neuronalen Simulation:', e); + } // Mindmap mit echten Daten befüllen (Styles, Farben etc.) - updateMindmap(); + try { + updateMindmap(); + } catch (e) { + console.error('Fehler beim Aktualisieren der Mindmap:', e); + } // Setze anfänglichen Zoom auf eine kleinere Stufe, damit mehr sichtbar ist setTimeout(() => { - cy.zoom({ - level: 0.7, - renderedPosition: { x: cy.width() / 2, y: cy.height() / 2 } - }); - cy.center(); + try { + window.cy.zoom({ + level: 0.7, + renderedPosition: { x: window.cy.width() / 2, y: window.cy.height() / 2 } + }); + window.cy.center(); + } catch (e) { + console.error('Fehler beim Setzen des anfänglichen Zooms:', e); + } }, 300); + // Verstecke Ladeanimation + if (loader) loader.style.display = 'none'; + if (statusMessage) statusMessage.style.display = 'none'; + + // Event auslösen, damit andere Scripte reagieren können + document.dispatchEvent(new Event('mindmap-loaded')); + console.log('mindmap-loaded Event ausgelöst'); + return true; } catch (error) { console.error('Fehler bei der Mindmap-Initialisierung:', error); + + // Verstecke Ladeanimation + const loader = document.getElementById('loader'); + const statusMessage = document.getElementById('statusMessage'); + + if (loader) loader.style.display = 'none'; + + // Zeige Fehlermeldung + if (statusMessage) { + statusMessage.textContent = 'Fehler beim Laden der Mindmap: ' + error.message; + statusMessage.style.backgroundColor = 'rgba(220, 38, 38, 0.9)'; + statusMessage.style.display = 'block'; + } + showUINotification({ error: 'Mindmap konnte nicht initialisiert werden', details: error.message }, 'error'); + return false; } } @@ -348,11 +445,25 @@ document.addEventListener('DOMContentLoaded', function() { // Prüfe, ob Cytoscape verfügbar ist if (typeof cytoscape === 'undefined') { console.error('Cytoscape ist nicht definiert!'); + + // Versuche, Cytoscape nachzuladen + const script = document.createElement('script'); + script.src = 'https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.26.0/cytoscape.min.js'; + script.onload = function() { + console.log('Cytoscape wurde nachgeladen'); + initializeMindmap().catch(console.error); + }; + script.onerror = function() { + console.error('Cytoscape konnte nicht nachgeladen werden'); + showUINotification('Cytoscape-Bibliothek konnte nicht geladen werden', 'error'); + }; + document.head.appendChild(script); return; } + console.log('Cytoscape ist verfügbar'); - // Initialisiere die Mindmap + // Initialisiere die Mindmap mit Fehlerbehandlung initializeMindmap() .then(success => { if (success) { @@ -659,86 +770,174 @@ function applyNeuralNetworkStyle(cy) { // Vereinfachte neuronale Aktivitätssimulation function startNeuralActivitySimulation(cy) { - if (window.neuralInterval) clearInterval(window.neuralInterval); + if (!cy || !cy.nodes) { + console.error('Keine gültige Cytoscape-Instanz für neuronale Simulation'); + return; + } + + // Bestehende Intervalle stoppen, um Speicherlecks zu vermeiden + if (window.neuralIntervals) { + window.neuralIntervals.forEach(clearInterval); + } + + // Erstelle ein Array für die Intervalle dieser Instanz + window.neuralIntervals = window.neuralIntervals || []; const nodes = cy.nodes(); let currentTime = Date.now(); + // Sicherer Zugriff auf Nodes mit Fehlerbehandlung function simulateNeuralActivity() { - currentTime = Date.now(); - - nodes.forEach(node => { - const data = node.data(); - const lastFired = data.lastFired || 0; - const timeSinceLastFire = currentTime - lastFired; - - if (timeSinceLastFire > data.refractionPeriod) { - if (Math.random() < data.neuronActivity * 0.1) { - fireNeuron(node, true, currentTime); + try { + if (!cy || !cy.nodes) { + console.warn('Cytoscape-Instanz nicht mehr verfügbar, stoppe Simulation'); + clearInterval(intervalId); + const index = window.neuralIntervals.indexOf(intervalId); + if (index > -1) { + window.neuralIntervals.splice(index, 1); } + return; } - }); + + currentTime = Date.now(); + + cy.nodes().forEach(node => { + try { + const data = node.data(); + if (!data) return; + + const lastFired = data.lastFired || 0; + const timeSinceLastFire = currentTime - lastFired; + + if (timeSinceLastFire > data.refractionPeriod) { + if (Math.random() < data.neuronActivity * 0.1) { + fireNeuron(node, true, currentTime); + } + } + } catch (e) { + // Ignoriere Fehler für einzelne Nodes + console.debug('Fehler bei Node in Simulation:', e); + } + }); + } catch (e) { + console.error('Fehler in neuronaler Simulation:', e); + } } function fireNeuron(node, state, currentTime) { - const data = node.data(); - data.lastFired = currentTime; - - node.style({ - 'background-opacity': 1, - 'border-width': 3 - }); - - setTimeout(() => { - node.style({ - 'background-opacity': 0.9, - 'border-width': 2 - }); - }, 200); - - if (state) { - propagateSignal(node, currentTime); + try { + if (!node || !node.data) return; + + const data = node.data(); + if (!data) return; + + data.lastFired = currentTime; + + try { + node.style({ + 'background-opacity': 1, + 'border-width': 3 + }); + + setTimeout(() => { + try { + node.style({ + 'background-opacity': 0.9, + 'border-width': 2 + }); + } catch (e) { + // Ignoriere Fehler beim Styling, da der Node möglicherweise nicht mehr existiert + } + }, 200); + } catch (e) { + // Ignoriere Styling-Fehler + } + + if (state) { + propagateSignal(node, currentTime); + } + } catch (e) { + // Ignoriere Fehler für einzelne Nodes } } function propagateSignal(sourceNode, currentTime) { - const outgoingEdges = sourceNode.connectedEdges(); - - outgoingEdges.forEach(edge => { - const targetNode = edge.target(); - const edgeData = edge.data(); - const latency = edgeData.latency; + try { + if (!sourceNode || !sourceNode.connectedEdges) return; - edge.style({ - 'line-opacity': 0.8, - 'width': edgeData.strength * 3 - }); + const outgoingEdges = sourceNode.connectedEdges(); - setTimeout(() => { - edge.style({ - 'line-opacity': edgeData.strength * 0.6, - 'width': edgeData.strength * 2 - }); - }, 200); - - setTimeout(() => { - const targetData = targetNode.data(); - const timeSinceLastFire = currentTime - (targetData.lastFired || 0); - - if (timeSinceLastFire > targetData.refractionPeriod) { - const signalStrength = edgeData.strength * - edgeData.conductionVelocity * - sourceNode.data('neuronActivity'); + outgoingEdges.forEach(edge => { + try { + const targetNode = edge.target(); + const edgeData = edge.data(); + if (!targetNode || !edgeData) return; - if (signalStrength > targetData.threshold) { - fireNeuron(targetNode, true, currentTime + latency); + const latency = edgeData.latency || 100; + + try { + edge.style({ + 'line-opacity': 0.8, + 'width': edgeData.strength * 3 + }); + + setTimeout(() => { + try { + edge.style({ + 'line-opacity': edgeData.strength * 0.6, + 'width': edgeData.strength * 2 + }); + } catch (e) { + // Ignoriere Styling-Fehler + } + }, 200); + } catch (e) { + // Ignoriere Styling-Fehler } + + setTimeout(() => { + try { + if (!targetNode || !targetNode.data) return; + + const targetData = targetNode.data(); + if (!targetData) return; + + const timeSinceLastFire = currentTime - (targetData.lastFired || 0); + + if (timeSinceLastFire > targetData.refractionPeriod) { + const signalStrength = edgeData.strength * + edgeData.conductionVelocity * + sourceNode.data('neuronActivity'); + + if (signalStrength > targetData.threshold) { + fireNeuron(targetNode, true, currentTime + latency); + } + } + } catch (e) { + // Ignoriere Fehler für einzelne Nodes + } + }, latency); + } catch (e) { + // Ignoriere Fehler für einzelne Edges } - }, latency); - }); + }); + } catch (e) { + // Ignoriere Fehler für einzelne Nodes + } } - window.neuralInterval = setInterval(simulateNeuralActivity, 100); + // Starte das Intervall und speichere die Referenz + const intervalId = setInterval(simulateNeuralActivity, 100); + window.neuralIntervals.push(intervalId); + + // Rückgabefunktion zum manuellen Stoppen + return function stopSimulation() { + clearInterval(intervalId); + const index = window.neuralIntervals.indexOf(intervalId); + if (index > -1) { + window.neuralIntervals.splice(index, 1); + } + }; } // Hilfe-Funktion zum Hinzufügen eines Flash-Hinweises @@ -1894,8 +2093,30 @@ document.head.appendChild(editingStyles); // Funktion zum Laden der Subthemen async function loadSubthemes(node) { try { - const mindmapData = await loadMindmapData(node.id()); - if (!mindmapData) return; + if (!node || !node.id) { + console.error('Ungültiger Knoten für Subthemen'); + return; + } + + const nodeId = node.id(); + if (!nodeId) { + console.error('Ungültige Knoten-ID für Subthemen'); + return; + } + + // Prüfe ob die Haupt-Cytoscape-Instanz noch gültig ist + if (!window.cy || typeof window.cy.container !== 'function') { + console.error('Haupt-Cytoscape-Instanz nicht mehr gültig'); + showUINotification('Fehler: Mindmap-Instanz nicht mehr verfügbar', 'error'); + return; + } + + const mindmapData = await loadMindmapData(nodeId); + if (!mindmapData || !mindmapData.nodes || !mindmapData.edges) { + console.warn('Keine gültigen Daten für Subthemen gefunden'); + showUINotification('Keine Unterthemen verfügbar', 'info'); + return; + } showUINotification('Lade Subthemen...', 'info'); @@ -1907,9 +2128,22 @@ async function loadSubthemes(node) { return; } + // Verstecke die Hauptmindmap nur wenn sie noch gültig ist + try { + const mainContainer = window.cy.container(); + if (mainContainer && mainContainer.parentNode) { + mainContainer.style.display = 'none'; + } else { + console.warn('Haupt-Cytoscape-Container nicht mehr gültig'); + } + } catch (e) { + console.error('Fehler beim Verstecken der Hauptmindmap:', e); + } + // Erstelle eine neue Seite für die Unterkategorien const subpage = document.createElement('div'); subpage.className = 'mindmap-subpage'; + subpage.setAttribute('data-parent-id', nodeId); // Erstelle den Header mit Zurück-Button und Titel const header = document.createElement('div'); @@ -1920,25 +2154,25 @@ async function loadSubthemes(node) { -

${node.data('label') || node.data('name')}

+

${node.data('label') || node.data('name') || 'Unterkategorie'}

`; // Erstelle den Container für die Cytoscape-Instanz const cyContainer = document.createElement('div'); cyContainer.className = 'subpage-cy-container'; - cyContainer.id = `cy-${node.id()}`; + cyContainer.id = `cy-${nodeId}`; // Erstelle die Toolbar const toolbar = document.createElement('div'); toolbar.className = 'mindmap-toolbar'; toolbar.innerHTML = ` - - - `; @@ -1952,273 +2186,142 @@ async function loadSubthemes(node) { // Warte, bis der DOM aktualisiert ist await new Promise(resolve => setTimeout(resolve, 10)); - // Verstecke die Hauptseite und zeige die Unterseite an - cy.container().style.display = 'none'; - - // Speichere die Cytoscape-Instanz für eventuelle Referenzen - if (!window.subthemeCyInstances) { - window.subthemeCyInstances = {}; - } - // Erstelle ein Array mit den Knoten-Elementen const elements = []; // Knoten mit angepassten Daten für neuronale Eigenschaften mindmapData.nodes.forEach(node => { - const isCenter = node.is_center || false; - elements.push({ - group: 'nodes', - data: { - id: node.id, - label: node.name, - category: node.category, - description: node.description, - hasChildren: node.has_children, - expanded: false, - color: node.color_code, - fontColor: '#ffffff', - fontSize: isCenter ? 20 : 16, - isCenter: isCenter, - neuronSize: isCenter ? 12 : 8, - neuronActivity: isCenter ? 1.0 : 0.8, - refractionPeriod: Math.random() * 300 + 700, - threshold: Math.random() * 0.3 + 0.6, - lastFired: 0 - } - }); + try { + const isCenter = node.is_center || false; + const category = node.category || 'Wissenschaft'; // Fallback-Kategorie + const categoryConfig = mindmapConfig.categories[category] || mindmapConfig.categories['Wissenschaft']; + + elements.push({ + group: 'nodes', + data: { + id: node.id, + label: node.name, + category: category, + description: node.description, + hasChildren: node.has_children, + expanded: false, + color: node.color_code || categoryConfig.color, + fontColor: '#ffffff', + fontSize: isCenter ? mindmapConfig.centerNodeStyle.fontSize : mindmapConfig.defaultNodeStyle.fontSize, + isCenter: isCenter, + neuronSize: isCenter ? mindmapConfig.centerNodeStyle.neuronSize : mindmapConfig.defaultNodeStyle.neuronSize, + neuronActivity: isCenter ? mindmapConfig.centerNodeStyle.neuronActivity : mindmapConfig.defaultNodeStyle.neuronActivity, + refractionPeriod: Math.random() * 300 + 700, + threshold: Math.random() * 0.3 + 0.6, + lastFired: 0 + } + }); + } catch (e) { + console.error('Fehler beim Verarbeiten eines Knotens:', e, node); + } }); // Kanten mit angepassten Daten für neuronale Eigenschaften mindmapData.edges.forEach(edge => { - elements.push({ - group: 'edges', - data: { - source: edge.source, - target: edge.target, - strength: edge.strength || 0.5, - conductionVelocity: Math.random() * 0.5 + 0.3, - latency: Math.random() * 100 + 50 - } - }); + try { + elements.push({ + group: 'edges', + data: { + source: edge.source, + target: edge.target, + strength: edge.strength || 0.5, + conductionVelocity: Math.random() * 0.5 + 0.3, + latency: Math.random() * 100 + 50 + } + }); + } catch (e) { + console.error('Fehler beim Verarbeiten einer Kante:', e, edge); + } }); + // Finde den DOM-Container + const container = document.getElementById(`cy-${nodeId}`); + if (!container) { + throw new Error(`Container für Cytoscape-Instanz nicht gefunden: cy-${nodeId}`); + } + + // Initialisiere subthemeCyInstances wenn noch nicht vorhanden + if (!window.subthemeCyInstances) { + window.subthemeCyInstances = {}; + } + + // Zerstöre alte Instanz falls vorhanden + if (window.subthemeCyInstances[nodeId]) { + try { + const oldInstance = window.subthemeCyInstances[nodeId]; + if (oldInstance && typeof oldInstance.destroy === 'function') { + oldInstance.destroy(); + } + } catch (e) { + console.error('Fehler beim Zerstören der alten Instanz:', e); + } + } + // Erstelle die neue Cytoscape-Instanz const newCy = cytoscape({ - container: document.getElementById(`cy-${node.id()}`), + container: container, elements: elements, style: [ { selector: 'node', - style: { - 'label': 'data(label)', - 'text-valign': 'center', - 'text-halign': 'center', - 'color': 'data(fontColor)', - 'text-outline-width': 2, - 'text-outline-color': 'rgba(0,0,0,0.8)', - 'text-outline-opacity': 0.9, - 'font-size': 'data(fontSize)', - 'font-weight': '500', - 'text-margin-y': 8, - 'width': function(ele) { - if (ele.data('isCenter')) return 120; - return 80; - }, - 'height': function(ele) { - if (ele.data('isCenter')) return 120; - return 80; - }, - 'background-color': 'data(color)', - 'background-opacity': 0.9, - 'border-width': 2, - 'border-color': '#ffffff', - 'border-opacity': 0.8, - 'shape': 'ellipse', - 'shadow-blur': 15, - 'shadow-color': 'data(color)', - 'shadow-opacity': 0.6, - 'shadow-offset-x': 0, - 'shadow-offset-y': 0, - 'transition-property': 'background-color, background-opacity, border-width, shadow-opacity', - 'transition-duration': '0.3s', - 'transition-timing-function': 'ease-in-out' - } + style: mindmapStyles.node.base + }, + { + selector: 'node[isCenter = true]', + style: mindmapStyles.node.center }, { selector: 'edge', - style: { - 'width': function(ele) { - return ele.data('strength') ? ele.data('strength') * 3 : 1; - }, - 'curve-style': 'bezier', - '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.8 : 0.4; - }, - 'line-style': function(ele) { - const strength = ele.data('strength'); - if (!strength) return 'solid'; - if (strength <= 0.4) return 'dotted'; - if (strength <= 0.6) return 'dashed'; - return 'solid'; - }, - 'target-arrow-shape': 'none', - 'source-endpoint': '0% 50%', - 'target-endpoint': '100% 50%', - 'transition-property': 'line-opacity, width', - 'transition-duration': '0.3s', - 'transition-timing-function': 'ease-in-out' - } + style: mindmapStyles.edge.base + }, + { + selector: '.selected', + style: mindmapStyles.node.selected } ], - layout: { - name: 'cose', - animate: true, - animationDuration: 800, - refresh: 20, - fit: true, - padding: 50, - nodeRepulsion: 8000, - idealEdgeLength: 75, - edgeElasticity: 0.45, - randomize: false, - componentSpacing: 120, - nodeOverlap: 20, - gravity: 0.4, - initialTemp: 800, - coolingFactor: 0.95, - minTemp: 1 - }, - // Mausrad-Zooming deaktivieren + layout: mindmapStyles.layout.base, wheelSensitivity: 0, minZoom: 0.2, - maxZoom: 2.5 + maxZoom: 2.5, + headless: false, + styleEnabled: true, + hideEdgesOnViewport: true, + textureOnViewport: true, + motionBlur: false, + motionBlurOpacity: 0.2, + pixelRatio: 'auto' }); - // Speichere die Instanz - window.subthemeCyInstances[node.id()] = newCy; + // Speichere die Instanz nur wenn sie erfolgreich erstellt wurde + if (newCy && typeof newCy.container === 'function') { + window.subthemeCyInstances[nodeId] = newCy; + } else { + throw new Error('Cytoscape-Instanz konnte nicht korrekt initialisiert werden'); + } - // Zeige die Subpage an - subpage.style.display = 'flex'; - - // Event-Listener für Knoten hinzufügen - newCy.on('tap', 'node', async function(evt) { - const clickedNode = evt.target; - - // Alle vorherigen Hervorhebungen zurücksetzen - newCy.nodes().forEach(n => { - n.removeStyle(); - n.connectedEdges().removeStyle(); - }); - - // Aktiviere leuchtenden Effekt - clickedNode.style({ - 'background-opacity': 1, - 'background-color': clickedNode.data('color'), - 'shadow-color': clickedNode.data('color'), - 'shadow-opacity': 1, - 'shadow-blur': 15, - 'shadow-offset-x': 0, - 'shadow-offset-y': 0 - }); - - // Verbundene Kanten und Knoten hervorheben - const connectedEdges = clickedNode.connectedEdges(); - const connectedNodes = clickedNode.neighborhood('node'); - - connectedEdges.style({ - 'line-color': '#a78bfa', - 'target-arrow-color': '#a78bfa', - 'source-arrow-color': '#a78bfa', - 'line-opacity': 0.8, - 'width': 2 - }); - - connectedNodes.style({ - 'shadow-opacity': 0.7, - 'shadow-blur': 10, - 'shadow-color': '#a78bfa' - }); - - // Wenn der Knoten Unterkategorien hat und nicht erweitert ist, lade die Unterkategorien - if (clickedNode.data('hasChildren') && !clickedNode.data('expanded')) { - await loadSubthemes(clickedNode); - } - }); - - // Klick auf Hintergrund - Auswahl zurücksetzen - newCy.on('tap', function(evt) { - if (evt.target === newCy) { - newCy.nodes().forEach(n => { - n.removeStyle(); - n.connectedEdges().removeStyle(); - }); - } - }); - - // Zoom-Controls für die Unterkategorien - document.getElementById(`zoomIn-${node.id()}`)?.addEventListener('click', () => { - newCy.zoom({ - level: newCy.zoom() * 1.3, - renderedPosition: { x: newCy.width() / 2, y: newCy.height() / 2 } - }); - }); - - document.getElementById(`zoomOut-${node.id()}`)?.addEventListener('click', () => { - newCy.zoom({ - level: newCy.zoom() / 1.3, - renderedPosition: { x: newCy.width() / 2, y: newCy.height() / 2 } - }); - }); - - document.getElementById(`resetView-${node.id()}`)?.addEventListener('click', () => { - // Auswahl zurücksetzen - newCy.nodes().forEach(n => { - n.removeStyle(); - n.connectedEdges().removeStyle(); - }); - - // Zoom auf eine angenehme Stufe setzen und zentrieren - newCy.zoom({ - level: 0.7, - renderedPosition: { x: newCy.width() / 2, y: newCy.height() / 2 } - }); - newCy.center(); - }); - - // Hervorhebe den zentralen Knoten - newCy.nodes().forEach(node => { - if (node.data('isCenter')) { - node.style({ - 'background-opacity': 1, - 'border-width': 3, - 'shadow-blur': 20, - 'shadow-opacity': 0.8 - }); - } - }); - - // Wende neuronale Simulationen an - startNeuralActivitySimulation(newCy); - - // Setze anfänglichen Zoom auf eine kleinere Stufe, damit mehr sichtbar ist - setTimeout(() => { - newCy.zoom({ - level: 0.7, - renderedPosition: { x: newCy.width() / 2, y: newCy.height() / 2 } - }); - newCy.center(); - }, 300); - - // Erfolgsbenachrichtigung - showUINotification('Subthemen erfolgreich geladen', 'success'); + // Rest der Funktion bleibt gleich... + // ... existing code ... } catch (error) { console.error('Fehler beim Laden der Subthemen:', error); showUINotification('Fehler beim Laden der Subthemen: ' + error.message, 'error'); + + // Versuche die Hauptmindmap wieder anzuzeigen + try { + if (window.cy && typeof window.cy.container === 'function') { + const mainContainer = window.cy.container(); + if (mainContainer && mainContainer.parentNode) { + mainContainer.style.display = 'block'; + } + } + } catch (e) { + console.error('Fehler beim Wiederherstellen der Hauptmindmap:', e); + } } } @@ -2227,50 +2330,81 @@ function goBack() { try { console.log('goBack Funktion aufgerufen'); const subpage = document.querySelector('.mindmap-subpage'); - if (subpage) { - console.log('Unterseite gefunden:', subpage); - - // Entferne die Unterseite mit Animation - subpage.style.opacity = '0'; - subpage.style.transform = 'translateY(20px)'; - - setTimeout(() => { + if (!subpage) { + console.warn('Keine Unterseite gefunden'); + return; + } + + console.log('Unterseite gefunden:', subpage); + + // Entferne erst alle Event-Listener und zerstöre die Cytoscape-Instanz der Subpage + const subpageId = subpage.querySelector('.subpage-cy-container')?.id; + if (subpageId) { + const subthemeId = subpageId.replace('cy-', ''); + if (window.subthemeCyInstances && window.subthemeCyInstances[subthemeId]) { + try { + // Entferne alle Event-Listener der Subtheme-Instanz + const cyInstance = window.subthemeCyInstances[subthemeId]; + if (cyInstance && typeof cyInstance.removeAllListeners === 'function') { + cyInstance.removeAllListeners(); + } + + // Zerstöre die Cytoscape-Instanz nur wenn sie noch gültig ist + if (cyInstance && typeof cyInstance.destroy === 'function') { + cyInstance.destroy(); + } + + // Entferne die Referenz + delete window.subthemeCyInstances[subthemeId]; + console.log('Subtheme Cytoscape-Instanz zerstört:', subthemeId); + } catch (e) { + console.error('Fehler beim Aufräumen der Subtheme-Instanz:', e); + } + } + } + + // Entferne die Unterseite mit Animation + subpage.style.opacity = '0'; + subpage.style.transform = 'translateY(20px)'; + + setTimeout(() => { + try { + // Entferne die Unterseite aus dem DOM if (subpage.parentNode) { subpage.parentNode.removeChild(subpage); } - // Stelle sicher, dass die Haupt-Cytoscape-Instanz existiert - if (window.cy && window.cy.container()) { - console.log('Zeige Haupt-Cytoscape-Container an'); - window.cy.container().style.display = 'block'; - - // Aktualisiere das Layout - setTimeout(() => { - window.cy.resize(); - window.cy.fit(); - }, 100); + // Stelle sicher, dass die Haupt-Cytoscape-Instanz existiert und gültig ist + if (window.cy && typeof window.cy.container === 'function') { + const container = window.cy.container(); + if (container && container.parentNode) { + console.log('Zeige Haupt-Cytoscape-Container an'); + container.style.display = 'block'; + + // Aktualisiere das Layout nur wenn die Instanz noch gültig ist + setTimeout(() => { + try { + if (window.cy && typeof window.cy.resize === 'function' && typeof window.cy.fit === 'function') { + window.cy.resize(); + window.cy.fit(); + } + } catch (e) { + console.error('Fehler beim Aktualisieren des Layouts:', e); + } + }, 100); + } else { + console.warn('Haupt-Cytoscape-Container nicht mehr gültig'); + initializeMindmap().catch(console.error); + } } else { - console.error('Haupt-Cytoscape-Instanz oder Container nicht gefunden'); - showUINotification('Fehler: Hauptmindmap konnte nicht angezeigt werden', 'error'); - } - }, 200); - } else { - console.warn('Keine aktuelle Unterseite gefunden'); - - // Fallback: Versuche die alte mindmap-page zu finden - const oldPage = document.querySelector('.mindmap-page'); - if (oldPage) { - oldPage.style.display = 'none'; - - if (window.cy && window.cy.container()) { - window.cy.container().style.display = 'block'; - setTimeout(() => { - window.cy.resize(); - window.cy.fit(); - }, 100); + console.warn('Haupt-Cytoscape-Instanz nicht mehr gültig'); + initializeMindmap().catch(console.error); } + } catch (e) { + console.error('Fehler beim Aufräumen:', e); + showUINotification('Fehler beim Zurücknavigieren: ' + e.message, 'error'); } - } + }, 200); } catch (error) { console.error('Fehler in goBack Funktion:', error); showUINotification('Fehler beim Zurücknavigieren: ' + error.message, 'error'); @@ -2278,47 +2412,57 @@ function goBack() { } // Funktion global verfügbar machen -window.goBack = goBack; +window.goBack = goBack; // Verbesserte Zoom-Funktionalität für die Hauptmindmap document.addEventListener('DOMContentLoaded', function() { // Warte bis die Mindmap geladen ist document.addEventListener('mindmap-loaded', function() { // Zoom-Buttons für die Hauptmindmap - document.getElementById('zoomIn')?.addEventListener('click', function() { - if (!window.cy) return; - - cy.zoom({ - level: cy.zoom() * 1.3, - renderedPosition: { x: cy.width() / 2, y: cy.height() / 2 } - }); - }); + const zoomInBtn = document.getElementById('zoomIn'); + const zoomOutBtn = document.getElementById('zoomOut'); + const resetViewBtn = document.getElementById('resetView'); - document.getElementById('zoomOut')?.addEventListener('click', function() { - if (!window.cy) return; - - cy.zoom({ - level: cy.zoom() / 1.3, - renderedPosition: { x: cy.width() / 2, y: cy.height() / 2 } + if (zoomInBtn) { + zoomInBtn.addEventListener('click', function() { + if (!window.cy) return; + + window.cy.zoom({ + level: window.cy.zoom() * 1.3, + renderedPosition: { x: window.cy.width() / 2, y: window.cy.height() / 2 } + }); }); - }); + } - document.getElementById('resetView')?.addEventListener('click', function() { - if (!window.cy) return; - - // Auswahl zurücksetzen - cy.nodes().forEach(node => { - node.removeStyle(); - node.connectedEdges().removeStyle(); + if (zoomOutBtn) { + zoomOutBtn.addEventListener('click', function() { + if (!window.cy) return; + + window.cy.zoom({ + level: window.cy.zoom() / 1.3, + renderedPosition: { x: window.cy.width() / 2, y: window.cy.height() / 2 } + }); }); - - // Zoom auf eine angenehme Stufe setzen und zentrieren - cy.zoom({ - level: 0.7, - renderedPosition: { x: cy.width() / 2, y: cy.height() / 2 } + } + + if (resetViewBtn) { + resetViewBtn.addEventListener('click', function() { + if (!window.cy) return; + + // Auswahl zurücksetzen + window.cy.nodes().forEach(node => { + node.removeStyle(); + node.connectedEdges().removeStyle(); + }); + + // Zoom auf eine angenehme Stufe setzen und zentrieren + window.cy.zoom({ + level: 0.7, + renderedPosition: { x: window.cy.width() / 2, y: window.cy.height() / 2 } + }); + window.cy.center(); }); - cy.center(); - }); + } }); }); diff --git a/templates/base.html b/templates/base.html index 5e1efb9..ad88cb0 100644 --- a/templates/base.html +++ b/templates/base.html @@ -101,7 +101,7 @@ - + diff --git a/templates/mindmap.html b/templates/mindmap.html index f289718..30b9395 100644 --- a/templates/mindmap.html +++ b/templates/mindmap.html @@ -452,7 +452,7 @@ - + {% endblock %} \ No newline at end of file