diff --git a/static/js/mindmap_controls.js b/static/js/mindmap_controls.js
new file mode 100644
index 0000000..bf3271a
--- /dev/null
+++ b/static/js/mindmap_controls.js
@@ -0,0 +1,220 @@
+/**
+ * Systades Mindmap Steuerung
+ * Implementierung der Funktionalitäten für die Mindmap-Kontrollbuttons
+ */
+
+document.addEventListener('DOMContentLoaded', function() {
+ console.log('Mindmap-Steuerung wird initialisiert...');
+
+ // Vergrößern-Funktion (Kontrollpanel)
+ const zoomInBtn = document.querySelector('#zoomIn');
+ if (zoomInBtn) {
+ console.log('Zoom-In Button gefunden');
+ zoomInBtn.addEventListener('click', function() {
+ vergrößern();
+ });
+ }
+
+ // Verkleinern-Funktion (Kontrollpanel)
+ const zoomOutBtn = document.querySelector('#zoomOut');
+ if (zoomOutBtn) {
+ console.log('Zoom-Out Button gefunden');
+ zoomOutBtn.addEventListener('click', function() {
+ verkleinern();
+ });
+ }
+
+ // Zurücksetzen-Funktion (Kontrollpanel)
+ const resetViewBtn = document.querySelector('#resetView');
+ if (resetViewBtn) {
+ console.log('Reset-View Button gefunden');
+ resetViewBtn.addEventListener('click', function() {
+ zurücksetzen();
+ });
+ }
+
+ // Legende-Funktion (Kontrollpanel)
+ const toggleLegendBtn = document.querySelector('#toggleLegend');
+ if (toggleLegendBtn) {
+ console.log('Toggle-Legend Button gefunden');
+ toggleLegendBtn.addEventListener('click', function() {
+ legendeUmschalten();
+ });
+ }
+
+ // Seitliche Buttons
+ const vergrößernBtn = document.querySelector('#vergrößernBtn');
+ if (vergrößernBtn) {
+ console.log('Vergrößern Button (Seitenleiste) gefunden');
+ vergrößernBtn.addEventListener('click', function() {
+ vergrößern();
+ animateButtonClick(this);
+ });
+ } else {
+ console.warn('Vergrößern Button nicht gefunden');
+ }
+
+ const verkleinernBtn = document.querySelector('#verkleinernBtn');
+ if (verkleinernBtn) {
+ console.log('Verkleinern Button (Seitenleiste) gefunden');
+ verkleinernBtn.addEventListener('click', function() {
+ verkleinern();
+ animateButtonClick(this);
+ });
+ } else {
+ console.warn('Verkleinern Button nicht gefunden');
+ }
+
+ const zurücksetzenBtn = document.querySelector('#zurücksetzenBtn');
+ if (zurücksetzenBtn) {
+ console.log('Zurücksetzen Button (Seitenleiste) gefunden');
+ zurücksetzenBtn.addEventListener('click', function() {
+ zurücksetzen();
+ animateButtonClick(this);
+ });
+ } else {
+ console.warn('Zurücksetzen Button nicht gefunden');
+ }
+});
+
+/**
+ * Vergrößert die aktuelle Mindmap-Ansicht
+ */
+function vergrößern() {
+ console.log('Vergrößern-Funktion aufgerufen');
+ if (window.cy) {
+ const aktuellerZoom = window.cy.zoom();
+ window.cy.zoom({
+ level: aktuellerZoom * 1.2,
+ renderedPosition: { x: window.innerWidth / 2, y: window.innerHeight / 2 }
+ });
+ console.log('Vergrößerung: Neuer Zoom-Level:', window.cy.zoom());
+ } else {
+ console.error('Cytoscape-Instanz nicht verfügbar');
+ }
+}
+
+/**
+ * Verkleinert die aktuelle Mindmap-Ansicht
+ */
+function verkleinern() {
+ console.log('Verkleinern-Funktion aufgerufen');
+ if (window.cy) {
+ const aktuellerZoom = window.cy.zoom();
+ window.cy.zoom({
+ level: aktuellerZoom * 0.8,
+ renderedPosition: { x: window.innerWidth / 2, y: window.innerHeight / 2 }
+ });
+ console.log('Verkleinerung: Neuer Zoom-Level:', window.cy.zoom());
+ } else {
+ console.error('Cytoscape-Instanz nicht verfügbar');
+ }
+}
+
+/**
+ * Setzt die Mindmap-Ansicht zurück, sodass alle Elemente sichtbar sind
+ */
+function zurücksetzen() {
+ console.log('Zurücksetzen-Funktion aufgerufen');
+ if (window.cy) {
+ window.cy.fit();
+ window.cy.center();
+ console.log('Ansicht zurückgesetzt');
+
+ // Zeige kurze Bestätigung an
+ zeigeFlashNachricht('Ansicht zurückgesetzt', 'info');
+ } else {
+ console.error('Cytoscape-Instanz nicht verfügbar');
+ }
+}
+
+/**
+ * Schaltet die Anzeige der Kategorie-Legende um
+ */
+function legendeUmschalten() {
+ console.log('Legende-Umschalten-Funktion aufgerufen');
+ const legende = document.getElementById('categoryLegend');
+ if (legende) {
+ const neuerZustand = legende.style.display === 'none' || legende.style.display === '' ? 'flex' : 'none';
+ legende.style.display = neuerZustand;
+ console.log('Legende ist jetzt:', neuerZustand === 'flex' ? 'sichtbar' : 'ausgeblendet');
+
+ // Zeige kurze Bestätigung an
+ zeigeFlashNachricht(
+ neuerZustand === 'flex' ? 'Legende wird angezeigt' : 'Legende ausgeblendet',
+ 'info'
+ );
+ } else {
+ console.error('Legende-Element nicht gefunden');
+ }
+}
+
+/**
+ * Zeigt eine kurze Flash-Nachricht auf dem Bildschirm an
+ * @param {string} nachricht - Die anzuzeigende Nachricht
+ * @param {string} typ - Der Typ der Nachricht (info, success, warning, error)
+ */
+function zeigeFlashNachricht(nachricht, typ = 'info') {
+ console.log(`Flash-Nachricht: ${nachricht} (${typ})`);
+
+ // Prüfe, ob bereits eine Flash-Nachricht existiert
+ let flashElement = document.getElementById('flash-nachricht');
+
+ // Falls nicht, erstelle sie
+ if (!flashElement) {
+ flashElement = document.createElement('div');
+ flashElement.id = 'flash-nachricht';
+ document.body.appendChild(flashElement);
+
+ // Styles für das Flash-Element
+ Object.assign(flashElement.style, {
+ position: 'fixed',
+ bottom: '20px',
+ left: '50%',
+ transform: 'translateX(-50%)',
+ padding: '10px 20px',
+ borderRadius: '5px',
+ color: 'white',
+ fontWeight: 'bold',
+ zIndex: '9999',
+ opacity: '0',
+ transition: 'opacity 0.3s ease-in-out'
+ });
+ }
+
+ // Setze Hintergrundfarbe je nach Typ
+ const hintergrundFarben = {
+ info: 'rgba(59, 130, 246, 0.9)',
+ success: 'rgba(16, 185, 129, 0.9)',
+ warning: 'rgba(245, 158, 11, 0.9)',
+ error: 'rgba(220, 38, 38, 0.9)'
+ };
+
+ flashElement.style.backgroundColor = hintergrundFarben[typ] || hintergrundFarben.info;
+ flashElement.textContent = nachricht;
+
+ // Animiere das Einblenden
+ flashElement.style.opacity = '1';
+
+ // Ausblenden nach 2 Sekunden
+ setTimeout(() => {
+ flashElement.style.opacity = '0';
+ }, 2000);
+}
+
+/**
+ * Animiert einen Button beim Klicken
+ * @param {HTMLElement} button - Das Button-Element, das animiert werden soll
+ */
+function animateButtonClick(button) {
+ // Aktuelle Transformation speichern
+ const originalTransform = button.style.transform;
+
+ // Animation anwenden
+ button.style.transform = 'scale(0.95)';
+
+ // Nach kurzer Zeit zurücksetzen
+ setTimeout(() => {
+ button.style.transform = originalTransform;
+ }, 150);
+}
\ No newline at end of file
diff --git a/static/js/update_mindmap.js b/static/js/update_mindmap.js
index 2541013..26c88a5 100644
--- a/static/js/update_mindmap.js
+++ b/static/js/update_mindmap.js
@@ -1,195 +1,58 @@
/**
- * Update Mindmap
- * Dieses Skript fügt Knoten zur Mindmap hinzu und stellt sicher,
- * dass sie im neuronalen Netzwerk-Design angezeigt werden.
- * Implementiert Lazy Loading & Progressive Disclosure
+ * Mindmap-Funktionalitäten
+ * Diese Datei enthält die grundlegenden Funktionen für die Mindmap-Visualisierung
*/
-// Neue zentrale Konfiguration
-const mindmapConfig = {
- categories: {
- 'Philosophie': {
- icon: 'fa-solid fa-lightbulb',
- color: '#b71c1c',
- description: 'Die Lehre vom Denken und der Erkenntnis'
- },
- 'Wissenschaft': {
- icon: 'fa-solid fa-atom',
- color: '#f4b400',
- description: 'Systematische Erforschung der Natur und Gesellschaft'
- },
- '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'
- }
- },
- defaultNodeStyle: {
- fontSize: 18,
- fontColor: '#fff',
- neuronSize: 8,
- neuronActivity: 0.8
- },
- centerNodeStyle: {
- fontSize: 22,
- fontColor: '#222',
- neuronSize: 12,
- neuronActivity: 1.0,
- color: '#f5f5f5',
- icon: 'fa-solid fa-circle'
- }
-};
+// Globale Variable für das Cytoscape-Objekt
+window.cy = null;
-// 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': 40,
- 'height': 40,
- '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:', 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 = errorData.error || 'Unbekannter Fehler';
-
- showUINotification(errorMessage, 'error');
- throw new Error(errorMessage);
- }
-
- const data = await response.json();
- console.log('Geladene Mindmap-Daten:', data);
-
- if (!data.success) {
- const errorMessage = data.error || 'Mindmap-Daten konnten nicht geladen werden';
- showUINotification(errorMessage, 'error');
- throw new Error(errorMessage);
- }
-
- // Überprüfen, ob Nodes und Edges existieren
- if (!data.nodes || !data.edges) {
- const errorMessage = 'Ungültiges Datenformat: Nodes oder Edges fehlen';
- showUINotification(errorMessage, 'error');
- throw new Error(errorMessage);
- }
-
- // 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:', error);
-
- // Stelle sicher, dass wir eine aussagekräftige Fehlermeldung haben
- const errorMessage = error.message || 'Unbekannter Fehler beim Laden der Mindmap-Daten';
-
- showUINotification(errorMessage, 'error');
- throw error;
- }
-}
-
-// Funktion zum Initialisieren der Mindmap
+/**
+ * Initialisiert die Mindmap mit Standarddaten
+ */
async function initializeMindmap() {
+ console.log('Initialisiere Mindmap...');
+
+ // Beispieldaten für die Mindmap (falls keine API-Daten verfügbar sind)
+ const fallbackData = {
+ nodes: [
+ { id: 'wissen', name: 'Wissen', category: 'Zentral', description: 'Zentrum der Wissensdatenbank', has_children: true, is_center: true, color_code: '#f5f5f5' },
+ { id: 'philosophie', name: 'Philosophie', category: 'Philosophie', description: 'Philosophisches Denken', has_children: true, is_center: false, color_code: '#9F7AEA' },
+ { id: 'wissenschaft', name: 'Wissenschaft', category: 'Wissenschaft', description: 'Wissenschaftliche Erkenntnisse', has_children: true, is_center: false, color_code: '#60A5FA' },
+ { id: 'technologie', name: 'Technologie', category: 'Technologie', description: 'Technologische Entwicklungen', has_children: true, is_center: false, color_code: '#10B981' },
+ { id: 'kunst', name: 'Künste', category: 'Künste', description: 'Kreative Ausdrucksformen', has_children: true, is_center: false, color_code: '#F59E0B' },
+ { id: 'psychologie', name: 'Psychologie', category: 'Psychologie', description: 'Menschliches Verhalten und Geist', has_children: true, is_center: false, color_code: '#EF4444' }
+ ],
+ edges: [
+ { source: 'wissen', target: 'philosophie', strength: 0.8 },
+ { source: 'wissen', target: 'wissenschaft', strength: 0.8 },
+ { source: 'wissen', target: 'technologie', strength: 0.7 },
+ { source: 'wissen', target: 'kunst', strength: 0.6 },
+ { source: 'wissen', target: 'psychologie', strength: 0.7 },
+ { source: 'wissenschaft', target: 'technologie', strength: 0.9 },
+ { source: 'wissenschaft', target: 'philosophie', strength: 0.5 },
+ { source: 'philosophie', target: 'psychologie', strength: 0.6 }
+ ]
+ };
+
try {
- const data = await loadMindmapData();
- if (!data || !data.nodes || !data.edges) {
- throw new Error('Ungültiges Datenformat: Mindmap-Daten fehlen oder sind unvollständig');
+ // Versuche, Daten von der API zu laden
+ const response = await fetch('/api/mindmap/root');
+ let data;
+
+ if (response.ok) {
+ data = await response.json();
+
+ // Prüfe, ob die API korrekte Daten zurückgegeben hat
+ if (!data.nodes || !data.edges) {
+ console.warn('API-Antwort hat ungültiges Format, verwende Fallback-Daten');
+ data = fallbackData;
+ }
+ } else {
+ console.warn('API-Anfrage fehlgeschlagen, verwende Fallback-Daten');
+ data = fallbackData;
}
-
+
+ // Cytoscape-Elemente erstellen
const elements = [
// Knoten
...data.nodes.map(node => ({
@@ -201,8 +64,7 @@ async function initializeMindmap() {
hasChildren: node.has_children,
expanded: false,
color: node.color_code,
- fontColor: '#ffffff',
- fontSize: node.is_center ? 20 : 16
+ isCenter: node.is_center || false
}
})),
// Kanten
@@ -214,1279 +76,134 @@ async function initializeMindmap() {
}
}))
];
-
- // Bestehende Cytoscape-Instanz entfernen, falls vorhanden
- if (window.cy && typeof window.cy.destroy === 'function') {
+
+ // Cytoscape initialisieren
+ const cyContainer = document.getElementById('cy');
+
+ if (!cyContainer) {
+ throw new Error('Container #cy nicht gefunden');
+ }
+
+ // Bestehende Cytoscape-Instanz zerstören, wenn vorhanden
+ if (window.cy) {
window.cy.destroy();
}
-
- const cyContainer = document.getElementById('cy');
- if (!cyContainer) {
- throw new Error('Mindmap-Container #cy nicht gefunden!');
- }
-
+
+ // Neue Cytoscape-Instanz erstellen
window.cy = cytoscape({
container: cyContainer,
elements: elements,
style: [
{
selector: 'node',
- style: mindmapStyles.node.base
+ style: {
+ 'background-color': 'data(color)',
+ 'label': 'data(label)',
+ 'color': '#ffffff',
+ 'text-outline-color': 'black',
+ 'text-outline-width': 1,
+ 'text-valign': 'center',
+ 'text-halign': 'center',
+ 'font-size': 18,
+ 'width': 50,
+ 'height': 50,
+ 'border-width': 2,
+ 'border-color': '#ffffff',
+ 'border-opacity': 0.6
+ }
},
{
selector: 'node[isCenter]',
- style: mindmapStyles.node.center
- },
- {
- selector: 'node:selected',
- style: mindmapStyles.node.selected
+ style: {
+ 'background-color': '#f5f5f5',
+ 'color': '#222222',
+ 'font-size': 24,
+ 'width': 80,
+ 'height': 80
+ }
},
{
selector: 'edge',
- style: mindmapStyles.edge.base
- }
- ],
- layout: mindmapStyles.layout.base
- });
-
- // Füge neuronale Eigenschaften zu allen Knoten hinzu
- 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]
- ? mindmapConfig.categories[data.category].color
- : '#60a5fa';
-
- node.data({
- ...data,
- neuronSize: data.neuronSize || 8,
- neuronActivity: data.neuronActivity || 0.8,
- refractionPeriod: Math.random() * 300 + 700,
- threshold: Math.random() * 0.3 + 0.6,
- lastFired: 0,
- color: data.color || categoryColor
- });
- });
-
- // Füge synaptische Eigenschaften zu allen Kanten hinzu
- cy.edges().forEach(edge => {
- const data = edge.data();
- edge.data({
- ...data,
- strength: data.strength || 0.5,
- conductionVelocity: Math.random() * 0.5 + 0.3,
- latency: Math.random() * 100 + 50
- });
- });
-
- // 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'), 'expanded:', node.data('expanded'));
-
- if (node.data('hasChildren') && !node.data('expanded')) {
- await loadSubthemes(node);
- }
- });
-
- // Layout ausführen
- cy.layout(mindmapStyles.layout.base).run();
-
- // Starte neuronale Aktivitätssimulation
- startNeuralActivitySimulation(cy);
-
- // Mindmap mit echten Daten befüllen (Styles, Farben etc.)
- updateMindmap();
-
- 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');
-
- // Prüfe, ob der Container existiert
- const cyContainer = document.getElementById('cy');
- console.log('Container gefunden:', cyContainer);
-
- if (!cyContainer) {
- console.error('Mindmap-Container #cy nicht gefunden!');
- return;
- }
-
- // Prüfe, ob Cytoscape verfügbar ist
- if (typeof cytoscape === 'undefined') {
- console.error('Cytoscape ist nicht definiert!');
- return;
- }
- console.log('Cytoscape ist verfügbar');
-
- // Initialisiere die Mindmap
- initializeMindmap()
- .then(success => {
- if (success) {
- console.log('Mindmap wurde erfolgreich initialisiert');
- // Event auslösen, damit andere Scripte reagieren können
- document.dispatchEvent(new Event('mindmap-loaded'));
- console.log('mindmap-loaded Event ausgelöst');
- } else {
- console.error('Mindmap-Initialisierung fehlgeschlagen');
- }
- })
- .catch(error => {
- console.error('Fehler bei der Mindmap-Initialisierung:', error);
- showUINotification({
- error: 'Mindmap konnte nicht initialisiert werden',
- details: error.message
- }, 'error');
- });
-});
-
-// Funktion zum Initialisieren des neuronalen Designs
-function initializeNeuralDesign(cy) {
- // Füge neuronale Eigenschaften zu allen Knoten hinzu
- 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]
- ? mindmapConfig.categories[data.category].color
- : '#60a5fa';
-
- node.data({
- ...data,
- neuronSize: data.neuronSize || 8,
- neuronActivity: data.neuronActivity || 0.8,
- refractionPeriod: Math.random() * 300 + 700,
- threshold: Math.random() * 0.3 + 0.6,
- lastFired: 0,
- color: data.color || categoryColor
- });
- });
-
- // Füge synaptische Eigenschaften zu allen Kanten hinzu
- cy.edges().forEach(edge => {
- const data = edge.data();
- edge.data({
- ...data,
- strength: data.strength || 0.5,
- conductionVelocity: Math.random() * 0.5 + 0.3,
- latency: Math.random() * 100 + 50
- });
- });
-
- // Wende neuronales Styling an
- cy.style()
- .selector('node')
- .style({
- 'background-color': 'data(color)',
- 'label': 'data(label)',
- 'color': '#fff',
- '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': 40,
- 'height': 40,
- 'border-width': 2,
- 'border-color': '#fff',
- 'border-opacity': 0.8,
- 'overlay-padding': 4,
- 'z-index': 10,
- 'shape': 'ellipse',
- 'background-opacity': 0.85,
- 'shadow-blur': 15,
- 'shadow-color': 'data(color)',
- 'shadow-opacity': 0.6,
- 'shadow-offset-x': 0,
- 'shadow-offset-y': 0
- })
- .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'
- })
- .update();
-
- // Starte neuronale Aktivitätssimulation
- startNeuralActivitySimulation(cy);
-}
-
-// Modifiziere die updateMindmap Funktion
-function updateMindmap() {
- if (!cy) return;
-
- // Bestehende Elemente entfernen
- cy.elements().remove();
-
- // Neue Knoten hinzufügen
- mindmapData.nodes.forEach(node => {
- cy.add({
- group: 'nodes',
- data: {
- id: node.id,
- label: node.name,
- category: node.category,
- description: node.description,
- hasChildren: node.has_children,
- expanded: false,
- color: node.color_code || mindmapConfig.categories[node.category]?.color || '#60a5fa',
- icon: node.icon || mindmapConfig.categories[node.category]?.icon || 'fa-solid fa-circle'
- }
- });
- });
-
- // Neue Kanten hinzufügen
- mindmapData.edges.forEach(edge => {
- cy.add({
- group: 'edges',
- data: {
- source: edge.source,
- target: edge.target,
- strength: edge.strength || 0.5
- }
- });
- });
-
- // Layout aktualisieren
- cy.layout(mindmapStyles.layout.base).run();
-}
-
-/**
- * Erweitert die Mindmap mit dem neuronalen Netzwerk-Design
- */
-function enhanceMindmap() {
- // Auf die bestehende Cytoscape-Instanz zugreifen
- const cy = window.cy;
-
- if (!cy) {
- console.error('Keine Cytoscape-Instanz gefunden.');
- return;
- }
-
- // Aktualisiere das Layout für eine bessere Verteilung
- cy.layout({
- name: 'cose',
- animate: true,
- animationDuration: 2000,
- nodeDimensionsIncludeLabels: true,
- padding: 100,
- spacingFactor: 2,
- randomize: true,
- fit: true,
- componentSpacing: 150,
- nodeRepulsion: 10000,
- edgeElasticity: 150,
- nestingFactor: 1.5,
- gravity: 100,
- initialTemp: 1000,
- coolingFactor: 0.95,
- minTemp: 1
- }).run();
-
- // Neuronen-Namen mit besserer Lesbarkeit umgestalten
- cy.style()
- .selector('node')
- .style({
- 'text-background-color': 'rgba(10, 14, 25, 0.7)',
- 'text-background-opacity': 0.7,
- 'text-background-padding': '2px',
- 'text-border-opacity': 0.2,
- 'text-border-width': 1,
- 'text-border-color': '#8b5cf6'
- })
- .update();
-
- // Sicherstellen, dass alle Knoten Neuronen-Eigenschaften haben
- cy.nodes().forEach(node => {
- if (!node.data('neuronSize')) {
- const neuronSize = Math.floor(Math.random() * 8) + 3;
- node.data('neuronSize', neuronSize);
- }
-
- if (!node.data('neuronActivity')) {
- const neuronActivity = Math.random() * 0.7 + 0.3;
- node.data('neuronActivity', neuronActivity);
- }
-
- // Zusätzliche Neuronale Eigenschaften
- node.data('pulseFrequency', Math.random() * 4 + 2); // Pulsfrequenz (2-6 Hz)
- node.data('refractionPeriod', Math.random() * 300 + 700); // Refraktionszeit (700-1000ms)
- node.data('threshold', Math.random() * 0.3 + 0.6); // Aktivierungsschwelle (0.6-0.9)
- });
-
- // Sicherstellen, dass alle Kanten Synapse-Eigenschaften haben
- cy.edges().forEach(edge => {
- if (!edge.data('strength')) {
- const strength = Math.random() * 0.6 + 0.2;
- edge.data('strength', strength);
- }
-
- // Zusätzliche synaptische Eigenschaften
- edge.data('conductionVelocity', Math.random() * 0.5 + 0.3); // Leitungsgeschwindigkeit (0.3-0.8)
- edge.data('latency', Math.random() * 100 + 50); // Signalverzögerung (50-150ms)
- });
-
- // Neuronales Netzwerk-Stil anwenden
- applyNeuralNetworkStyle(cy);
-
- console.log('Mindmap wurde erfolgreich im neuronalen Netzwerk-Stil aktualisiert');
-
- // Spezielle Effekte für das neuronale Netzwerk hinzufügen
- startNeuralActivitySimulation(cy);
-}
-
-/**
- * Wendet detaillierte neuronale Netzwerkstile auf die Mindmap an
- * @param {Object} cy - Cytoscape-Instanz
- */
-function applyNeuralNetworkStyle(cy) {
- cy.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',
- 'transition-property': 'background-color, background-opacity, border-width',
- 'transition-duration': '0.3s',
- 'transition-timing-function': 'ease-in-out'
- })
- .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'
- })
- .update();
-}
-
-// Vereinfachte neuronale Aktivitätssimulation
-function startNeuralActivitySimulation(cy) {
- if (window.neuralInterval) clearInterval(window.neuralInterval);
-
- const nodes = cy.nodes();
- let currentTime = Date.now();
-
- 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);
- }
- }
- });
- }
-
- 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);
- }
- }
-
- function propagateSignal(sourceNode, currentTime) {
- const outgoingEdges = sourceNode.connectedEdges();
-
- outgoingEdges.forEach(edge => {
- const targetNode = edge.target();
- const edgeData = edge.data();
- const latency = edgeData.latency;
-
- edge.style({
- 'line-opacity': 0.8,
- 'width': edgeData.strength * 3
- });
-
- 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');
-
- if (signalStrength > targetData.threshold) {
- fireNeuron(targetNode, true, currentTime + latency);
+ style: {
+ 'width': function(ele) {
+ return ele.data('strength') * 5;
+ },
+ 'line-color': 'rgba(255, 255, 255, 0.5)',
+ 'opacity': 0.7,
+ 'curve-style': 'bezier'
}
}
- }, latency);
+ ],
+ layout: {
+ name: 'cose',
+ idealEdgeLength: 100,
+ nodeOverlap: 20,
+ refresh: 20,
+ fit: true,
+ padding: 30,
+ randomize: false,
+ componentSpacing: 100,
+ nodeRepulsion: 400000,
+ edgeElasticity: 100,
+ nestingFactor: 5,
+ gravity: 80,
+ numIter: 1000,
+ initialTemp: 200,
+ coolingFactor: 0.95,
+ minTemp: 1.0
+ }
});
+
+ // Knoten sperren, damit sie nicht verschoben werden können
+ window.cy.nodes().lock();
+
+ console.log('Mindmap erfolgreich initialisiert');
+ return window.cy;
+ } catch (error) {
+ console.error('Fehler bei der Initialisierung der Mindmap:', error);
+ throw error;
}
-
- window.neuralInterval = setInterval(simulateNeuralActivity, 100);
-}
-
-// Hilfe-Funktion zum Hinzufügen eines Flash-Hinweises
-function showFlash(message, type = 'info') {
- const flashContainer = createFlashContainer();
- const flash = document.createElement('div');
- flash.className = `flash-message ${type}`;
- flash.textContent = message;
- flashContainer.appendChild(flash);
- document.body.appendChild(flashContainer);
-
- setTimeout(() => {
- flash.classList.add('show');
- setTimeout(() => {
- flash.classList.remove('show');
- setTimeout(() => {
- flashContainer.remove();
- }, 300);
- }, 3000);
- }, 100);
}
/**
- * Zeigt eine Benachrichtigung in der UI an
- * @param {string|object} message - Die anzuzeigende Nachricht oder ein Fehlerobjekt
- * @param {string} type - Der Typ der Benachrichtigung ('info', 'success', 'warning', 'error')
- * @param {number} duration - Die Anzeigedauer in Millisekunden (Standard: 3000)
+ * Speichert Änderungen an der Mindmap
+ * @param {Object} cy - Die Cytoscape-Instanz
*/
-function showUINotification(message, type = 'info', duration = 3000) {
- // Container erstellen, falls er nicht existiert
- let container = document.getElementById('notification-container');
- if (!container) {
- container = document.createElement('div');
- container.id = 'notification-container';
- container.style.position = 'fixed';
- container.style.top = '1rem';
- container.style.right = '1rem';
- container.style.zIndex = '1000';
- container.style.maxWidth = '400px';
- document.body.appendChild(container);
- }
-
- // Benachrichtigung erstellen
- const notification = document.createElement('div');
- notification.className = `notification notification-${type}`;
- notification.style.padding = '1rem';
- notification.style.marginBottom = '0.5rem';
- notification.style.borderRadius = '0.25rem';
- notification.style.boxShadow = '0 2px 5px rgba(0, 0, 0, 0.2)';
- notification.style.position = 'relative';
- notification.style.opacity = '0';
- notification.style.transform = 'translateY(-20px)';
- notification.style.transition = 'all 0.3s ease-in-out';
-
- // Farben nach Typ
- if (type === 'success') {
- notification.style.backgroundColor = '#059669';
- notification.style.color = '#ffffff';
- } else if (type === 'error') {
- notification.style.backgroundColor = '#DC2626';
- notification.style.color = '#ffffff';
- } else if (type === 'warning') {
- notification.style.backgroundColor = '#F59E0B';
- notification.style.color = '#ffffff';
- } else {
- notification.style.backgroundColor = '#3B82F6';
- notification.style.color = '#ffffff';
- }
-
- // Nachrichteninhalt formatieren
- let content = '';
-
- if (typeof message === 'object' && message !== null) {
- // Wenn es ein Fehler-Objekt ist
- if (message.error) {
- content = message.error;
- if (message.details) {
- content += `
${message.details}`;
- }
- } else {
- // Versuche, das Objekt zu stringifizieren
- try {
- content = JSON.stringify(message);
- } catch (e) {
- content = 'Objekt konnte nicht angezeigt werden';
- }
- }
- } else {
- // String oder andere primitive Typen
- content = message;
- }
-
- notification.innerHTML = content;
-
- // Schließen-Button
- const closeButton = document.createElement('span');
- closeButton.innerHTML = '×';
- closeButton.style.position = 'absolute';
- closeButton.style.top = '0.25rem';
- closeButton.style.right = '0.5rem';
- closeButton.style.fontSize = '1.25rem';
- closeButton.style.cursor = 'pointer';
- closeButton.onclick = () => {
- notification.style.opacity = '0';
- notification.style.transform = 'translateY(-20px)';
- setTimeout(() => {
- if (notification.parentNode === container) {
- container.removeChild(notification);
- }
- }, 300);
- };
- notification.appendChild(closeButton);
-
- // Zur Seite hinzufügen
- container.appendChild(notification);
-
- // Animation starten
- setTimeout(() => {
- notification.style.opacity = '1';
- notification.style.transform = 'translateY(0)';
- }, 10);
-
- // Automatisch ausblenden, wenn keine Dauer von 0 übergeben wurde
- if (duration > 0) {
- setTimeout(() => {
- if (notification.parentNode === container) {
- notification.style.opacity = '0';
- notification.style.transform = 'translateY(-20px)';
- setTimeout(() => {
- if (notification.parentNode === container) {
- container.removeChild(notification);
- }
- }, 300);
- }
- }, duration);
- }
-}
-
-// Funktion zum Anzeigen der Bearbeitungssteuerungen
-function showEditingControls(nodeId) {
- const container = nodeId ?
- document.getElementById(`cy-${nodeId}`).parentElement :
- document.getElementById('cy').parentElement;
-
- if (!container) return;
-
- // Erstelle Bearbeitungswerkzeuge, wenn sie noch nicht existieren
- let editingControls = container.querySelector('.editing-controls');
- if (!editingControls) {
- editingControls = document.createElement('div');
- editingControls.className = 'editing-controls';
- editingControls.innerHTML = `
-