chore: Änderungen commited
This commit is contained in:
@@ -1,165 +0,0 @@
|
||||
/**
|
||||
* Mindmap Hauptkomponenten Styling
|
||||
*/
|
||||
|
||||
/* Toast-Container und Benachrichtigungen */
|
||||
#toast-container {
|
||||
position: fixed;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
max-width: 350px;
|
||||
}
|
||||
|
||||
.toast-message {
|
||||
transition: all 0.3s ease;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.toast-message.translate-x-full {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
/* Flash-Nachrichten */
|
||||
.flash-message {
|
||||
position: fixed;
|
||||
top: 1rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) translateY(-100%);
|
||||
padding: 0.75rem 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
background-color: rgba(30, 41, 59, 0.95);
|
||||
color: white;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
z-index: 9999;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.flash-message.visible {
|
||||
transform: translateX(-50%) translateY(0);
|
||||
}
|
||||
|
||||
.flash-message.info {
|
||||
background-color: rgba(59, 130, 246, 0.95);
|
||||
}
|
||||
|
||||
.flash-message.success {
|
||||
background-color: rgba(16, 185, 129, 0.95);
|
||||
}
|
||||
|
||||
.flash-message.warning {
|
||||
background-color: rgba(245, 158, 11, 0.95);
|
||||
}
|
||||
|
||||
.flash-message.error {
|
||||
background-color: rgba(239, 68, 68, 0.95);
|
||||
}
|
||||
|
||||
/* Zoom-Steuerungen */
|
||||
.control-panel {
|
||||
position: absolute;
|
||||
right: 2rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: rgba(15, 23, 42, 0.9);
|
||||
border-radius: 1rem;
|
||||
padding: 1.5rem;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
||||
z-index: 10;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.control-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.75rem 1rem;
|
||||
margin: 0.5rem 0;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: none;
|
||||
border-radius: 0.5rem;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.control-button:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
transform: translateX(-5px);
|
||||
}
|
||||
|
||||
.control-button i {
|
||||
margin-right: 0.75rem;
|
||||
}
|
||||
|
||||
/* Info-Panel */
|
||||
.info-panel {
|
||||
position: absolute;
|
||||
left: 2rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%) translateX(-20px);
|
||||
opacity: 0;
|
||||
background: rgba(15, 23, 42, 0.9);
|
||||
border-radius: 1rem;
|
||||
padding: 1.5rem;
|
||||
width: 300px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
||||
z-index: 10;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
transition: all 0.3s ease;
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.info-panel.visible {
|
||||
opacity: 1;
|
||||
transform: translateY(-50%) translateX(0);
|
||||
}
|
||||
|
||||
.info-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.info-content {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Kategorie-Legende */
|
||||
.category-legend {
|
||||
position: absolute;
|
||||
bottom: 2rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: rgba(15, 23, 42, 0.9);
|
||||
border-radius: 1rem;
|
||||
padding: 1rem 2rem;
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
||||
z-index: 10;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.category-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.category-color {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
@@ -182,117 +182,6 @@ async function loadMindmapData(nodeId = null) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementiert Zoomfunktionalität für die Mindmap
|
||||
* @param {Object} cy - Cytoscape-Instanz
|
||||
*/
|
||||
function implementZoomFunctions(cy) {
|
||||
if (!cy) {
|
||||
console.error('Cytoscape-Instanz nicht gefunden!');
|
||||
return;
|
||||
}
|
||||
|
||||
// Vergrößern-Button
|
||||
const zoomInButton = document.getElementById('zoomIn');
|
||||
if (zoomInButton) {
|
||||
zoomInButton.addEventListener('click', function() {
|
||||
cy.zoom(cy.zoom() * 1.2);
|
||||
cy.center();
|
||||
showUINotification('Ansicht vergrößert', 'info', 1500);
|
||||
});
|
||||
}
|
||||
|
||||
// Verkleinern-Button
|
||||
const zoomOutButton = document.getElementById('zoomOut');
|
||||
if (zoomOutButton) {
|
||||
zoomOutButton.addEventListener('click', function() {
|
||||
cy.zoom(cy.zoom() * 0.8);
|
||||
cy.center();
|
||||
showUINotification('Ansicht verkleinert', 'info', 1500);
|
||||
});
|
||||
}
|
||||
|
||||
// Zurücksetzen-Button
|
||||
const resetViewButton = document.getElementById('resetView');
|
||||
if (resetViewButton) {
|
||||
resetViewButton.addEventListener('click', function() {
|
||||
cy.fit();
|
||||
cy.center();
|
||||
showUINotification('Ansicht zurückgesetzt', 'info', 1500);
|
||||
});
|
||||
}
|
||||
|
||||
// Legende-Button
|
||||
const toggleLegendButton = document.getElementById('toggleLegend');
|
||||
const categoryLegend = document.getElementById('categoryLegend');
|
||||
|
||||
if (toggleLegendButton && categoryLegend) {
|
||||
toggleLegendButton.addEventListener('click', function() {
|
||||
if (categoryLegend.style.display === 'none') {
|
||||
categoryLegend.style.display = 'flex';
|
||||
showUINotification('Legende angezeigt', 'info', 1500);
|
||||
} else {
|
||||
categoryLegend.style.display = 'none';
|
||||
showUINotification('Legende ausgeblendet', 'info', 1500);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Funktion zum Starten der Mindmap-Anwendung
|
||||
*/
|
||||
async function startMindmapApp() {
|
||||
try {
|
||||
const loader = document.getElementById('loader');
|
||||
const statusMessage = document.getElementById('statusMessage');
|
||||
const cyContainer = document.getElementById('cy');
|
||||
|
||||
if (!cyContainer) {
|
||||
throw new Error('Cytoscape-Container nicht gefunden');
|
||||
}
|
||||
|
||||
// Anzeigen der Ladeanzeige
|
||||
if (loader) loader.style.display = 'block';
|
||||
if (statusMessage) {
|
||||
statusMessage.textContent = 'Lade Mindmap...';
|
||||
statusMessage.style.display = 'block';
|
||||
}
|
||||
|
||||
// Initialisieren der Mindmap
|
||||
const cy = await initializeMindmap();
|
||||
window.cy = cy; // Speichern für globalen Zugriff
|
||||
|
||||
// Zoom-Funktionen und Legendensteuerung implementieren
|
||||
implementZoomFunctions(cy);
|
||||
|
||||
// Verstecken der Ladeanzeige
|
||||
if (loader) loader.style.display = 'none';
|
||||
if (statusMessage) statusMessage.style.display = 'none';
|
||||
|
||||
return cy;
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Starten der Mindmap-Anwendung:', error);
|
||||
const statusMessage = document.getElementById('statusMessage');
|
||||
|
||||
if (statusMessage) {
|
||||
statusMessage.textContent = 'Fehler beim Laden der Mindmap: ' + error.message;
|
||||
statusMessage.style.display = 'block';
|
||||
}
|
||||
|
||||
const loader = document.getElementById('loader');
|
||||
if (loader) loader.style.display = 'none';
|
||||
|
||||
showUINotification('Fehler beim Laden der Mindmap: ' + error.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Eventlistener für DOM-Ready
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log('DOM vollständig geladen, starte Mindmap-Anwendung');
|
||||
startMindmapApp();
|
||||
});
|
||||
|
||||
// Funktion zum Initialisieren der Mindmap
|
||||
async function initializeMindmap() {
|
||||
try {
|
||||
@@ -309,138 +198,179 @@ async function initializeMindmap() {
|
||||
label: node.name,
|
||||
category: node.category,
|
||||
description: node.description,
|
||||
color: getCategoryColor(node.category)
|
||||
hasChildren: node.has_children,
|
||||
expanded: false,
|
||||
color: node.color_code,
|
||||
fontColor: '#ffffff',
|
||||
fontSize: node.is_center ? 20 : 16
|
||||
}
|
||||
})),
|
||||
// Kanten
|
||||
...data.edges.map(edge => ({
|
||||
data: {
|
||||
id: edge.id,
|
||||
source: edge.source,
|
||||
target: edge.target,
|
||||
strength: edge.weight || 1.0
|
||||
strength: edge.strength || 0.5
|
||||
}
|
||||
}))
|
||||
];
|
||||
|
||||
// Cytoscape-Container
|
||||
const cyContainer = document.getElementById('cy');
|
||||
if (!cyContainer) {
|
||||
throw new Error('Cytoscape-Container nicht gefunden');
|
||||
// Bestehende Cytoscape-Instanz entfernen, falls vorhanden
|
||||
if (window.cy && typeof window.cy.destroy === 'function') {
|
||||
window.cy.destroy();
|
||||
}
|
||||
|
||||
// Cytoscape initialisieren
|
||||
const cy = cytoscape({
|
||||
const cyContainer = document.getElementById('cy');
|
||||
if (!cyContainer) {
|
||||
throw new Error('Mindmap-Container #cy nicht gefunden!');
|
||||
}
|
||||
|
||||
window.cy = cytoscape({
|
||||
container: cyContainer,
|
||||
elements: elements,
|
||||
style: [
|
||||
// Knoten-Styling
|
||||
{
|
||||
selector: 'node',
|
||||
style: mindmapStyles.node.base
|
||||
},
|
||||
// Kanten-Styling
|
||||
{
|
||||
selector: 'edge',
|
||||
style: mindmapStyles.edge.base
|
||||
selector: 'node[isCenter]',
|
||||
style: mindmapStyles.node.center
|
||||
},
|
||||
// Ausgewählte Knoten
|
||||
{
|
||||
selector: 'node:selected',
|
||||
style: mindmapStyles.node.selected
|
||||
},
|
||||
{
|
||||
selector: 'edge',
|
||||
style: mindmapStyles.edge.base
|
||||
}
|
||||
],
|
||||
layout: mindmapStyles.layout.base
|
||||
});
|
||||
|
||||
// Event-Listener für Knoten-Interaktionen
|
||||
cy.on('tap', 'node', function(evt) {
|
||||
const node = evt.target;
|
||||
showNodeInfo(node);
|
||||
// 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
|
||||
});
|
||||
});
|
||||
|
||||
// UI aktualisieren
|
||||
document.getElementById('loader').style.display = 'none';
|
||||
document.getElementById('statusMessage').style.display = 'none';
|
||||
// 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
|
||||
});
|
||||
});
|
||||
|
||||
// Neurales Design anwenden
|
||||
initializeNeuralDesign(cy);
|
||||
// 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);
|
||||
}
|
||||
});
|
||||
|
||||
// Gebe die Cytoscape-Instanz zurück
|
||||
return cy;
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Initialisieren der Mindmap:', error);
|
||||
document.getElementById('statusMessage').textContent = 'Fehler: ' + error.message;
|
||||
document.getElementById('statusMessage').style.display = 'block';
|
||||
document.getElementById('loader').style.display = 'none';
|
||||
// Layout ausführen
|
||||
cy.layout(mindmapStyles.layout.base).run();
|
||||
|
||||
showUINotification('Fehler beim Initialisieren der Mindmap: ' + error.message, 'error');
|
||||
throw error;
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die Farbe für eine Kategorie zurück
|
||||
* @param {string} category - Die Kategorie
|
||||
* @returns {string} - Die Farbe als Hex-Code
|
||||
*/
|
||||
function getCategoryColor(category) {
|
||||
const categoryMap = {
|
||||
'Philosophie': '#9F7AEA', // Violett
|
||||
'Wissenschaft': '#60A5FA', // Blau
|
||||
'Technologie': '#10B981', // Grün
|
||||
'Künste': '#F59E0B', // Orange
|
||||
'Psychologie': '#EF4444' // Rot
|
||||
};
|
||||
// Warte bis DOM geladen ist
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log('DOMContentLoaded Event ausgelöst');
|
||||
|
||||
return categoryMap[category] || '#8B5CF6'; // Standardfarbe, wenn Kategorie nicht gefunden
|
||||
}
|
||||
// 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');
|
||||
|
||||
/**
|
||||
* Zeigt Informationen zu einem Knoten an
|
||||
* @param {Object} node - Der Knoten, dessen Informationen angezeigt werden sollen
|
||||
*/
|
||||
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');
|
||||
|
||||
if (!infoPanel || !infoTitle || !infoContent) return;
|
||||
|
||||
const data = node.data();
|
||||
|
||||
infoTitle.textContent = data.label || 'Knotendetails';
|
||||
|
||||
let contentHTML = `
|
||||
<p><strong>Kategorie:</strong> ${data.category || 'Nicht kategorisiert'}</p>
|
||||
<p><strong>Beschreibung:</strong> ${data.description || 'Keine Beschreibung verfügbar'}</p>
|
||||
`;
|
||||
|
||||
infoContent.innerHTML = contentHTML;
|
||||
infoPanel.classList.add('visible');
|
||||
}
|
||||
// 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) {
|
||||
if (!cy) return;
|
||||
|
||||
console.log('Initialisiere neurales Design für Mindmap');
|
||||
|
||||
// Füge neurale Eigenschaften zu allen Knoten hinzu
|
||||
// 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
|
||||
lastFired: 0,
|
||||
color: data.color || categoryColor
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Füge synaptische Eigenschaften zu allen Kanten hinzu
|
||||
cy.edges().forEach(edge => {
|
||||
const data = edge.data();
|
||||
@@ -451,172 +381,66 @@ function initializeNeuralDesign(cy) {
|
||||
latency: Math.random() * 100 + 50
|
||||
});
|
||||
});
|
||||
|
||||
// Wende neurales Netzwerk-Styling an
|
||||
applyNeuralNetworkStyle(cy);
|
||||
|
||||
// Starte Simulation der neuronalen Aktivität
|
||||
startNeuralActivitySimulation(cy);
|
||||
|
||||
return cy;
|
||||
}
|
||||
|
||||
// Funktionen für das neurale Netzwerk-Design
|
||||
function applyNeuralNetworkStyle(cy) {
|
||||
if (!cy) return;
|
||||
|
||||
// Animation für pulsierende Knoten
|
||||
const pulseKeyframes = [
|
||||
{ scale: 1.0, opacity: 0.8, borderOpacity: 0.6 },
|
||||
{ scale: 1.05, opacity: 1.0, borderOpacity: 0.9 },
|
||||
{ scale: 1.0, opacity: 0.8, borderOpacity: 0.6 }
|
||||
];
|
||||
|
||||
// Anwenden auf alle Knoten
|
||||
cy.nodes().forEach(node => {
|
||||
const randomDelay = Math.random() * 3000;
|
||||
|
||||
// CSS-Animationen auf die Knoten anwenden
|
||||
node.style({
|
||||
'border-width': 3,
|
||||
'border-opacity': 0.6,
|
||||
'background-opacity': 0.8,
|
||||
'transition-property': 'background-opacity, border-opacity, width, height',
|
||||
'transition-duration': '300ms'
|
||||
});
|
||||
|
||||
// Pulseffekt mit zufälligem Delay starten
|
||||
setTimeout(() => {
|
||||
node.animate({
|
||||
style: pulseKeyframes,
|
||||
duration: 2000 + Math.random() * 1000,
|
||||
complete: function() {
|
||||
// Animation wiederholen
|
||||
this.loopAnimation = true;
|
||||
if (this.loopAnimation) {
|
||||
setTimeout(() => {
|
||||
applyNeuralNetworkStyle(cy);
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, randomDelay);
|
||||
});
|
||||
}
|
||||
|
||||
// Simulation der neuronalen Aktivität
|
||||
function startNeuralActivitySimulation(cy) {
|
||||
if (!cy) return;
|
||||
|
||||
console.log('Starte Simulation der neuronalen Aktivität');
|
||||
let isSimulationRunning = true;
|
||||
|
||||
// Funktion zur Simulation der neuronalen Aktivität
|
||||
function simulateNeuralActivity() {
|
||||
if (!isSimulationRunning) return;
|
||||
|
||||
const currentTime = Date.now();
|
||||
|
||||
// Wähle zufällige Knoten zum Feuern aus
|
||||
const randomNode = cy.nodes()[Math.floor(Math.random() * cy.nodes().length)];
|
||||
if (randomNode) {
|
||||
fireNeuron(randomNode, { isPrimary: true }, currentTime);
|
||||
}
|
||||
|
||||
// Wiederholung der Simulation
|
||||
setTimeout(simulateNeuralActivity, 2000 + Math.random() * 3000);
|
||||
}
|
||||
|
||||
// Funktion zum "Feuern" eines Neurons
|
||||
function fireNeuron(node, state, currentTime) {
|
||||
if (!node) return;
|
||||
|
||||
const data = node.data();
|
||||
const lastFired = data.lastFired || 0;
|
||||
const refractionPeriod = data.refractionPeriod || 1000;
|
||||
|
||||
// Prüfe, ob das Neuron feuerbereit ist (Refraktärzeit vorbei)
|
||||
if (currentTime - lastFired < refractionPeriod && !state.isPrimary) {
|
||||
return; // Neuron ist noch in Refraktärphase
|
||||
}
|
||||
|
||||
// Aktualisiere "Letztes Feuern"-Zeitstempel
|
||||
node.data('lastFired', currentTime);
|
||||
|
||||
// Visuellen Effekt des Feuerns anzeigen
|
||||
node.animate({
|
||||
style: {
|
||||
'background-opacity': 1,
|
||||
'border-opacity': 1,
|
||||
'border-color': '#f59e42',
|
||||
'border-width': 4
|
||||
// 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;
|
||||
},
|
||||
duration: 300,
|
||||
complete: function() {
|
||||
// Zurück zum Normalzustand
|
||||
node.animate({
|
||||
style: {
|
||||
'background-opacity': 0.8,
|
||||
'border-opacity': 0.6,
|
||||
'border-color': '#ffffff',
|
||||
'border-width': 3
|
||||
},
|
||||
duration: 500
|
||||
});
|
||||
|
||||
// Signal an verbundene Knoten weitergeben
|
||||
propagateSignal(node, currentTime);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Funktion zur Signalausbreitung zu verbundenen Knoten
|
||||
function propagateSignal(sourceNode, currentTime) {
|
||||
// Verbundene Kanten finden
|
||||
const connectedEdges = sourceNode.outgoers('edge');
|
||||
|
||||
connectedEdges.forEach(edge => {
|
||||
const targetNode = edge.target();
|
||||
const data = edge.data();
|
||||
const strength = data.strength || 0.5;
|
||||
const conductionVelocity = data.conductionVelocity || 0.5;
|
||||
const latency = data.latency || 100;
|
||||
|
||||
// Kante hervorheben (Signal fließt durch die Kante)
|
||||
edge.animate({
|
||||
style: {
|
||||
'line-opacity': 1,
|
||||
'width': 3
|
||||
},
|
||||
duration: 200,
|
||||
complete: function() {
|
||||
// Nach einer Verzögerung (Latenz), das Ziel-Neuron feuern lassen
|
||||
setTimeout(() => {
|
||||
edge.animate({
|
||||
style: {
|
||||
'line-opacity': data.strength ? data.strength * 0.6 : 0.4,
|
||||
'width': data.strength ? data.strength * 2 : 1
|
||||
},
|
||||
duration: 300
|
||||
});
|
||||
|
||||
// Ziel-Neuron feuern lassen mit Wahrscheinlichkeit basierend auf Stärke
|
||||
if (Math.random() < strength) {
|
||||
fireNeuron(targetNode, { isPrimary: false }, currentTime + latency);
|
||||
}
|
||||
}, latency / conductionVelocity);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Starte die Simulation
|
||||
simulateNeuralActivity();
|
||||
|
||||
// Funktion zum Stoppen der Simulation
|
||||
cy.stopNeuralSimulation = function() {
|
||||
isSimulationRunning = false;
|
||||
};
|
||||
'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
|
||||
@@ -743,122 +567,289 @@ function enhanceMindmap() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}, latency);
|
||||
});
|
||||
}
|
||||
|
||||
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.innerHTML = `
|
||||
<div class="flex items-center">
|
||||
<i class="fas ${type === 'error' ? 'fa-exclamation-triangle' : 'fa-info-circle'} mr-2"></i>
|
||||
<span>${message}</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(flash);
|
||||
|
||||
// Animation zum Einblenden
|
||||
flash.textContent = message;
|
||||
flashContainer.appendChild(flash);
|
||||
document.body.appendChild(flashContainer);
|
||||
|
||||
setTimeout(() => {
|
||||
flash.classList.add('visible');
|
||||
}, 10);
|
||||
|
||||
// Automatisches Ausblenden nach 3 Sekunden
|
||||
setTimeout(() => {
|
||||
flash.classList.remove('visible');
|
||||
flash.classList.add('show');
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(flash);
|
||||
}, 300);
|
||||
}, 3000);
|
||||
flash.classList.remove('show');
|
||||
setTimeout(() => {
|
||||
flashContainer.remove();
|
||||
}, 300);
|
||||
}, 3000);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zeigt eine UI-Benachrichtigung
|
||||
* @param {string} message - Die Nachricht
|
||||
* @param {string} type - Der Typ der Benachrichtigung (info, success, warning, error)
|
||||
* @param {number} duration - Die Anzeigedauer in Millisekunden
|
||||
* Zeigt eine Benachrichtigung in der UI an
|
||||
* @param {string|object} message - Die anzuzeigende Nachricht oder ein Fehlerobjekt
|
||||
* @param {string} type - Der Typ der Benachrichtigung ('info', 'success', 'warning', 'error')
|
||||
* @param {number} duration - Die Anzeigedauer in Millisekunden (Standard: 3000)
|
||||
*/
|
||||
function showUINotification(message, type = 'info', duration = 3000) {
|
||||
// Prüfe, ob Toast-Container existiert, sonst erstellen
|
||||
let toastContainer = document.getElementById('toast-container');
|
||||
|
||||
if (!toastContainer) {
|
||||
toastContainer = document.createElement('div');
|
||||
toastContainer.id = 'toast-container';
|
||||
toastContainer.className = 'fixed top-4 right-4 z-50 flex flex-col gap-2';
|
||||
document.body.appendChild(toastContainer);
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Erstelle Toast-Element
|
||||
const toast = document.createElement('div');
|
||||
toast.className = 'toast-message transform transition-all duration-300 ease-out translate-x-full';
|
||||
// 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';
|
||||
|
||||
// Setze Styling basierend auf Typ
|
||||
let iconClass = 'fa-info-circle';
|
||||
let bgColorClass = 'bg-blue-500';
|
||||
|
||||
switch (type) {
|
||||
case 'success':
|
||||
iconClass = 'fa-check-circle';
|
||||
bgColorClass = 'bg-green-500';
|
||||
break;
|
||||
case 'warning':
|
||||
iconClass = 'fa-exclamation-circle';
|
||||
bgColorClass = 'bg-yellow-500';
|
||||
break;
|
||||
case 'error':
|
||||
iconClass = 'fa-exclamation-triangle';
|
||||
bgColorClass = 'bg-red-500';
|
||||
break;
|
||||
// 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';
|
||||
}
|
||||
|
||||
// Inhalt des Toasts
|
||||
toast.innerHTML = `
|
||||
<div class="flex items-center p-3 rounded-lg shadow-lg ${bgColorClass} text-white">
|
||||
<i class="fas ${iconClass} mr-2"></i>
|
||||
<span>${message}</span>
|
||||
<button class="ml-auto pl-3 focus:outline-none hover:opacity-75">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
// Nachrichteninhalt formatieren
|
||||
let content = '';
|
||||
|
||||
// Füge Toast zum Container hinzu
|
||||
toastContainer.appendChild(toast);
|
||||
if (typeof message === 'object' && message !== null) {
|
||||
// Wenn es ein Fehler-Objekt ist
|
||||
if (message.error) {
|
||||
content = message.error;
|
||||
if (message.details) {
|
||||
content += `<br><small>${message.details}</small>`;
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
|
||||
// Animation zum Einblenden
|
||||
setTimeout(() => {
|
||||
toast.classList.remove('translate-x-full');
|
||||
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);
|
||||
|
||||
// Schließen-Button-Funktionalität
|
||||
const closeButton = toast.querySelector('button');
|
||||
closeButton.addEventListener('click', () => {
|
||||
removeToast(toast);
|
||||
});
|
||||
|
||||
// Automatisches Ausblenden nach der angegebenen Dauer
|
||||
const timeoutId = setTimeout(() => {
|
||||
removeToast(toast);
|
||||
}, duration);
|
||||
|
||||
// Funktion zum Entfernen des Toasts
|
||||
function removeToast(toastElement) {
|
||||
// Animation zum Ausblenden
|
||||
toastElement.classList.add('translate-x-full');
|
||||
|
||||
// Entfernen nach Abschluss der Animation
|
||||
setTimeout(() => {
|
||||
if (toastElement.parentNode === toastContainer) {
|
||||
toastContainer.removeChild(toastElement);
|
||||
}
|
||||
|
||||
// Entferne Container, wenn keine Toasts mehr vorhanden sind
|
||||
if (toastContainer.children.length === 0) {
|
||||
document.body.removeChild(toastContainer);
|
||||
}
|
||||
}, 300);
|
||||
|
||||
// Timeout löschen
|
||||
clearTimeout(timeoutId);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user