diff --git a/static/js/update_mindmap.js b/static/js/update_mindmap.js index 2c4476a..2ea7019 100644 --- a/static/js/update_mindmap.js +++ b/static/js/update_mindmap.js @@ -286,13 +286,23 @@ 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'), 'expanded:', node.data('expanded')); + console.log('Node clicked:', node.id(), 'hasChildren:', node.data('hasChildren') || node.data('has_children'), 'expanded:', node.data('expanded')); - if (node.data('hasChildren') && !node.data('expanded')) { + if ((node.data('hasChildren') || node.data('has_children')) && !node.data('expanded')) { await loadSubthemes(node); } }); + // Event-Listener für Hover über Knoten (für Info-Panel) + cy.on('mouseover', 'node', function(evt) { + const node = evt.target; + showNodeInfo(node); + }); + + cy.on('mouseout', 'node', function() { + hideNodeInfo(); + }); + // Layout ausführen cy.layout(mindmapStyles.layout.base).run(); @@ -311,309 +321,6 @@ async function initializeMindmap() { cy.center(); }, 300); - return true; - } catch (error) { - console.error('Fehler bei der Mindmap-Initialisierung:', error); -/** - * 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 - */ - -// 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' - } -}; - -// 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 -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 && typeof window.cy.destroy === 'function') { - window.cy.destroy(); - } - - const cyContainer = document.getElementById('cy'); - if (!cyContainer) { - throw new Error('Mindmap-Container #cy nicht gefunden!'); - } - - window.cy = cytoscape({ - container: cyContainer, - elements: elements, - style: [ - { - selector: 'node', - style: mindmapStyles.node.base - }, - { - selector: 'node[isCenter]', - style: mindmapStyles.node.center - }, - { - selector: 'node:selected', - style: mindmapStyles.node.selected - }, - { - 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); @@ -775,7 +482,8 @@ function updateMindmap() { label: node.name, category: node.category, description: node.description, - hasChildren: node.has_children, + has_children: node.has_children, + hasChildren: node.has_children, // Doppelte Eigenschaft für Kompatibilität expanded: false, color: node.color_code || mindmapConfig.categories[node.category]?.color || '#60a5fa', icon: node.icon || mindmapConfig.categories[node.category]?.icon || 'fa-solid fa-circle' @@ -2384,7 +2092,10 @@ async function loadSubthemes(node) { coolingFactor: 0.95, minTemp: 1 }, - wheelSensitivity: 0.3 + // Mausrad-Zooming deaktivieren + wheelSensitivity: 0, + minZoom: 0.2, + maxZoom: 2.5 }); // Speichere die Instanz @@ -2451,24 +2162,31 @@ async function loadSubthemes(node) { // Zoom-Controls für die Unterkategorien document.getElementById(`zoomIn-${node.id()}`)?.addEventListener('click', () => { newCy.zoom({ - level: newCy.zoom() * 1.2, + 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.2, + level: newCy.zoom() / 1.3, renderedPosition: { x: newCy.width() / 2, y: newCy.height() / 2 } }); }); document.getElementById(`resetView-${node.id()}`)?.addEventListener('click', () => { - newCy.fit(); + // 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 @@ -2486,6 +2204,15 @@ async function loadSubthemes(node) { // 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'); @@ -2551,4 +2278,88 @@ function goBack() { } // Funktion global verfügbar machen -window.goBack = goBack; \ No newline at end of file +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 } + }); + }); + + 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 } + }); + }); + + document.getElementById('resetView')?.addEventListener('click', function() { + if (!window.cy) return; + + // Auswahl zurücksetzen + cy.nodes().forEach(node => { + node.removeStyle(); + node.connectedEdges().removeStyle(); + }); + + // Zoom auf eine angenehme Stufe setzen und zentrieren + cy.zoom({ + level: 0.7, + renderedPosition: { x: cy.width() / 2, y: cy.height() / 2 } + }); + cy.center(); + }); + }); +}); + +// Funktionen für Knoteninfo-Panel +function showNodeInfo(node) { + if (!node || !node.isNode()) return; + + const infoPanel = document.getElementById('infoPanel'); + if (!infoPanel) return; + + // Stelle sicher, dass das Info-Panel die notwendigen Elemente enthält + if (!infoPanel.querySelector('.info-title')) { + infoPanel.innerHTML = ` +

+
+ `; + } + + const infoTitle = infoPanel.querySelector('.info-title'); + const infoContent = infoPanel.querySelector('.info-content'); + + const data = node.data(); + + infoTitle.textContent = data.label || 'Knotendetails'; + + let contentHTML = ` +

Kategorie: ${data.category || 'Nicht kategorisiert'}

+

Beschreibung: ${data.description || 'Keine Beschreibung verfügbar'}

+ `; + + if (data.hasChildren || data.has_children) { + contentHTML += `

Hat Unterknoten

`; + } + + infoContent.innerHTML = contentHTML; + infoPanel.classList.add('visible'); +} + +function hideNodeInfo() { + const infoPanel = document.getElementById('infoPanel'); + if (infoPanel) { + infoPanel.classList.remove('visible'); + } +} \ No newline at end of file diff --git a/templates/mindmap.html b/templates/mindmap.html index 7919a8b..f289718 100644 --- a/templates/mindmap.html +++ b/templates/mindmap.html @@ -436,7 +436,12 @@ -
+ +
+

Knotendetails

+
+
+
{% endblock %} @@ -651,7 +656,7 @@ document.addEventListener('DOMContentLoaded', function() { } }); - // Zoom-Funktionen + // Funktionen für Zoom-Buttons und Reset document.getElementById('zoomIn').addEventListener('click', function() { if (window.cy) window.cy.zoom(window.cy.zoom() * 1.2); }); @@ -663,36 +668,6 @@ document.addEventListener('DOMContentLoaded', function() { document.getElementById('resetView').addEventListener('click', function() { if (window.cy) window.cy.fit(); }); - - // Funktionen für Knoteninfo-Panel - function showNodeInfo(node) { - if (!node || !node.isNode()) return; - - const infoPanel = document.getElementById('infoPanel'); - const infoTitle = infoPanel.querySelector('.info-title'); - const infoContent = infoPanel.querySelector('.info-content'); - - const data = node.data(); - - infoTitle.textContent = data.label || 'Knotendetails'; - - let contentHTML = ` -

Kategorie: ${data.category || 'Nicht kategorisiert'}

-

Beschreibung: ${data.description || 'Keine Beschreibung verfügbar'}

- `; - - if (data.hasChildren) { - contentHTML += `

Hat Unterknoten

`; - } - - infoContent.innerHTML = contentHTML; - infoPanel.classList.add('visible'); - } - - function hideNodeInfo() { - const infoPanel = document.getElementById('infoPanel'); - infoPanel.classList.remove('visible'); - } }); {% endblock %} \ No newline at end of file