Mindmap wird nicht initialisiert ! :(
This commit is contained in:
1008
static/js/mindmap.js
1008
static/js/mindmap.js
File diff suppressed because it is too large
Load Diff
@@ -166,8 +166,8 @@ function updateInfoPanel(node) {
|
||||
|
||||
html += `
|
||||
</ul>
|
||||
</div>
|
||||
`;
|
||||
</div>
|
||||
`;
|
||||
|
||||
infoPanel.innerHTML = html;
|
||||
infoPanel.style.display = 'block';
|
||||
@@ -206,9 +206,9 @@ function updateSidebar(node) {
|
||||
html += `
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
</div>
|
||||
`;
|
||||
|
||||
sidebar.innerHTML = html;
|
||||
}
|
||||
|
||||
@@ -236,316 +236,316 @@ function resetSelection(cy) {
|
||||
if (sidebar) {
|
||||
sidebar.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generiert Standarddaten für die Mindmap als Fallback
|
||||
*/
|
||||
function generateDefaultData() {
|
||||
return {
|
||||
nodes: [
|
||||
{ id: 'root', name: 'Wissen', description: 'Zentrale Wissensbasis', category: 'Zentral', color_code: '#4299E1' },
|
||||
{ id: 'philosophy', name: 'Philosophie', description: 'Philosophisches Denken', category: 'Philosophie', color_code: '#9F7AEA', parent_id: 'root' },
|
||||
{ id: 'science', name: 'Wissenschaft', description: 'Wissenschaftliche Erkenntnisse', category: 'Wissenschaft', color_code: '#48BB78', parent_id: 'root' },
|
||||
{ id: 'technology', name: 'Technologie', description: 'Technologische Entwicklungen', category: 'Technologie', color_code: '#ED8936', parent_id: 'root' },
|
||||
{ id: 'arts', name: 'Künste', description: 'Künstlerische Ausdrucksformen', category: 'Künste', color_code: '#ED64A6', parent_id: 'root' },
|
||||
{ id: 'psychology', name: 'Psychologie', description: 'Menschliches Verhalten und Geist', category: 'Psychologie', color_code: '#4299E1', parent_id: 'root' }
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Rendert die Mindmap mit Cytoscape.js
|
||||
*/
|
||||
function renderMindmap(data) {
|
||||
console.log('Rendere Mindmap mit Daten:', data);
|
||||
|
||||
// Konvertiere Backend-Daten in Cytoscape-Format
|
||||
const elements = convertToCytoscapeFormat(data);
|
||||
|
||||
// Leere den Container
|
||||
cyContainer.innerHTML = '';
|
||||
|
||||
// Erstelle Cytoscape-Instanz
|
||||
mindmap = cytoscape({
|
||||
container: cyContainer,
|
||||
elements: elements,
|
||||
style: [
|
||||
{
|
||||
selector: 'node',
|
||||
style: {
|
||||
'background-color': 'data(color)',
|
||||
'label': 'data(name)',
|
||||
'width': 30,
|
||||
'height': 30,
|
||||
'font-size': 12,
|
||||
'text-valign': 'bottom',
|
||||
'text-halign': 'center',
|
||||
'text-margin-y': 8,
|
||||
'color': document.documentElement.classList.contains('dark') ? '#f1f5f9' : '#334155',
|
||||
'text-background-color': document.documentElement.classList.contains('dark') ? 'rgba(30, 41, 59, 0.8)' : 'rgba(241, 245, 249, 0.8)',
|
||||
'text-background-opacity': 0.8,
|
||||
'text-background-padding': '2px',
|
||||
'text-background-shape': 'roundrectangle',
|
||||
'text-wrap': 'ellipsis',
|
||||
'text-max-width': '100px'
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: 'edge',
|
||||
style: {
|
||||
'width': 2,
|
||||
'line-color': document.documentElement.classList.contains('dark') ? 'rgba(255, 255, 255, 0.15)' : 'rgba(30, 41, 59, 0.15)',
|
||||
'target-arrow-color': document.documentElement.classList.contains('dark') ? 'rgba(255, 255, 255, 0.15)' : 'rgba(30, 41, 59, 0.15)',
|
||||
'curve-style': 'bezier'
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: 'node:selected',
|
||||
style: {
|
||||
'background-color': 'data(color)',
|
||||
'border-width': 3,
|
||||
'border-color': '#8b5cf6',
|
||||
'width': 40,
|
||||
'height': 40,
|
||||
'font-size': 14,
|
||||
'font-weight': 'bold',
|
||||
'text-background-color': '#8b5cf6',
|
||||
'text-background-opacity': 0.9
|
||||
}
|
||||
}
|
||||
],
|
||||
layout: {
|
||||
name: 'cose',
|
||||
animate: true,
|
||||
animationDuration: 800,
|
||||
nodeDimensionsIncludeLabels: true,
|
||||
refresh: 30,
|
||||
randomize: true,
|
||||
componentSpacing: 100,
|
||||
nodeRepulsion: 8000,
|
||||
nodeOverlap: 20,
|
||||
idealEdgeLength: 200,
|
||||
edgeElasticity: 100,
|
||||
nestingFactor: 1.2,
|
||||
gravity: 80,
|
||||
fit: true,
|
||||
padding: 30
|
||||
}
|
||||
});
|
||||
|
||||
// Event-Listener für Knoteninteraktionen
|
||||
mindmap.on('tap', 'node', function(evt) {
|
||||
const node = evt.target;
|
||||
const nodeData = node.data();
|
||||
|
||||
// Update Info-Panel
|
||||
updateNodeInfoPanel(nodeData);
|
||||
|
||||
// Lade verbundene Knoten
|
||||
updateConnectedNodes(node);
|
||||
});
|
||||
|
||||
// Toolbar-Buttons aktivieren
|
||||
if (fitButton) {
|
||||
fitButton.addEventListener('click', () => {
|
||||
mindmap.fit();
|
||||
mindmap.center();
|
||||
});
|
||||
}
|
||||
|
||||
if (resetButton) {
|
||||
resetButton.addEventListener('click', () => {
|
||||
mindmap.layout({
|
||||
/**
|
||||
* Generiert Standarddaten für die Mindmap als Fallback
|
||||
*/
|
||||
function generateDefaultData() {
|
||||
return {
|
||||
nodes: [
|
||||
{ id: 'root', name: 'Wissen', description: 'Zentrale Wissensbasis', category: 'Zentral', color_code: '#4299E1' },
|
||||
{ id: 'philosophy', name: 'Philosophie', description: 'Philosophisches Denken', category: 'Philosophie', color_code: '#9F7AEA', parent_id: 'root' },
|
||||
{ id: 'science', name: 'Wissenschaft', description: 'Wissenschaftliche Erkenntnisse', category: 'Wissenschaft', color_code: '#48BB78', parent_id: 'root' },
|
||||
{ id: 'technology', name: 'Technologie', description: 'Technologische Entwicklungen', category: 'Technologie', color_code: '#ED8936', parent_id: 'root' },
|
||||
{ id: 'arts', name: 'Künste', description: 'Künstlerische Ausdrucksformen', category: 'Künste', color_code: '#ED64A6', parent_id: 'root' },
|
||||
{ id: 'psychology', name: 'Psychologie', description: 'Menschliches Verhalten und Geist', category: 'Psychologie', color_code: '#4299E1', parent_id: 'root' }
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Rendert die Mindmap mit Cytoscape.js
|
||||
*/
|
||||
function renderMindmap(data) {
|
||||
console.log('Rendere Mindmap mit Daten:', data);
|
||||
|
||||
// Konvertiere Backend-Daten in Cytoscape-Format
|
||||
const elements = convertToCytoscapeFormat(data);
|
||||
|
||||
// Leere den Container
|
||||
cyContainer.innerHTML = '';
|
||||
|
||||
// Erstelle Cytoscape-Instanz
|
||||
mindmap = cytoscape({
|
||||
container: cyContainer,
|
||||
elements: elements,
|
||||
style: [
|
||||
{
|
||||
selector: 'node',
|
||||
style: {
|
||||
'background-color': 'data(color)',
|
||||
'label': 'data(name)',
|
||||
'width': 30,
|
||||
'height': 30,
|
||||
'font-size': 12,
|
||||
'text-valign': 'bottom',
|
||||
'text-halign': 'center',
|
||||
'text-margin-y': 8,
|
||||
'color': document.documentElement.classList.contains('dark') ? '#f1f5f9' : '#334155',
|
||||
'text-background-color': document.documentElement.classList.contains('dark') ? 'rgba(30, 41, 59, 0.8)' : 'rgba(241, 245, 249, 0.8)',
|
||||
'text-background-opacity': 0.8,
|
||||
'text-background-padding': '2px',
|
||||
'text-background-shape': 'roundrectangle',
|
||||
'text-wrap': 'ellipsis',
|
||||
'text-max-width': '100px'
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: 'edge',
|
||||
style: {
|
||||
'width': 2,
|
||||
'line-color': document.documentElement.classList.contains('dark') ? 'rgba(255, 255, 255, 0.15)' : 'rgba(30, 41, 59, 0.15)',
|
||||
'target-arrow-color': document.documentElement.classList.contains('dark') ? 'rgba(255, 255, 255, 0.15)' : 'rgba(30, 41, 59, 0.15)',
|
||||
'curve-style': 'bezier'
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: 'node:selected',
|
||||
style: {
|
||||
'background-color': 'data(color)',
|
||||
'border-width': 3,
|
||||
'border-color': '#8b5cf6',
|
||||
'width': 40,
|
||||
'height': 40,
|
||||
'font-size': 14,
|
||||
'font-weight': 'bold',
|
||||
'text-background-color': '#8b5cf6',
|
||||
'text-background-opacity': 0.9
|
||||
}
|
||||
}
|
||||
],
|
||||
layout: {
|
||||
name: 'cose',
|
||||
animate: true,
|
||||
animationDuration: 800,
|
||||
nodeDimensionsIncludeLabels: true,
|
||||
refresh: 30,
|
||||
randomize: true,
|
||||
fit: true
|
||||
}).run();
|
||||
});
|
||||
}
|
||||
|
||||
if (toggleLabelsButton) {
|
||||
let labelsVisible = true;
|
||||
toggleLabelsButton.addEventListener('click', () => {
|
||||
labelsVisible = !labelsVisible;
|
||||
|
||||
if (labelsVisible) {
|
||||
mindmap.style()
|
||||
.selector('node')
|
||||
.style('label', 'data(name)')
|
||||
.update();
|
||||
} else {
|
||||
mindmap.style()
|
||||
.selector('node')
|
||||
.style('label', '')
|
||||
.update();
|
||||
componentSpacing: 100,
|
||||
nodeRepulsion: 8000,
|
||||
nodeOverlap: 20,
|
||||
idealEdgeLength: 200,
|
||||
edgeElasticity: 100,
|
||||
nestingFactor: 1.2,
|
||||
gravity: 80,
|
||||
fit: true,
|
||||
padding: 30
|
||||
}
|
||||
});
|
||||
|
||||
// Event-Listener für Knoteninteraktionen
|
||||
mindmap.on('tap', 'node', function(evt) {
|
||||
const node = evt.target;
|
||||
const nodeData = node.data();
|
||||
|
||||
// Update Info-Panel
|
||||
updateNodeInfoPanel(nodeData);
|
||||
|
||||
// Lade verbundene Knoten
|
||||
updateConnectedNodes(node);
|
||||
});
|
||||
|
||||
// Toolbar-Buttons aktivieren
|
||||
if (fitButton) {
|
||||
fitButton.addEventListener('click', () => {
|
||||
mindmap.fit();
|
||||
mindmap.center();
|
||||
});
|
||||
}
|
||||
|
||||
if (resetButton) {
|
||||
resetButton.addEventListener('click', () => {
|
||||
mindmap.layout({
|
||||
name: 'cose',
|
||||
animate: true,
|
||||
randomize: true,
|
||||
fit: true
|
||||
}).run();
|
||||
});
|
||||
}
|
||||
|
||||
if (toggleLabelsButton) {
|
||||
let labelsVisible = true;
|
||||
toggleLabelsButton.addEventListener('click', () => {
|
||||
labelsVisible = !labelsVisible;
|
||||
|
||||
if (labelsVisible) {
|
||||
mindmap.style()
|
||||
.selector('node')
|
||||
.style('label', 'data(name)')
|
||||
.update();
|
||||
} else {
|
||||
mindmap.style()
|
||||
.selector('node')
|
||||
.style('label', '')
|
||||
.update();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Dark Mode-Änderungen überwachen
|
||||
document.addEventListener('darkModeToggled', function(event) {
|
||||
updateDarkModeStyles(event.detail.isDark);
|
||||
});
|
||||
|
||||
// Initial fit und center
|
||||
setTimeout(() => {
|
||||
mindmap.fit();
|
||||
mindmap.center();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// Dark Mode-Änderungen überwachen
|
||||
document.addEventListener('darkModeToggled', function(event) {
|
||||
updateDarkModeStyles(event.detail.isDark);
|
||||
});
|
||||
|
||||
// Initial fit und center
|
||||
setTimeout(() => {
|
||||
mindmap.fit();
|
||||
mindmap.center();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Konvertiert die Backend-Daten ins Cytoscape-Format
|
||||
*/
|
||||
function convertToCytoscapeFormat(data) {
|
||||
const elements = {
|
||||
nodes: [],
|
||||
edges: []
|
||||
};
|
||||
|
||||
// Nodes hinzufügen
|
||||
if (data.nodes && data.nodes.length > 0) {
|
||||
data.nodes.forEach(node => {
|
||||
elements.nodes.push({
|
||||
data: {
|
||||
id: String(node.id),
|
||||
name: node.name,
|
||||
description: node.description || 'Keine Beschreibung verfügbar',
|
||||
category: node.category || 'Allgemein',
|
||||
color: node.color_code || getRandomColor()
|
||||
/**
|
||||
* Konvertiert die Backend-Daten ins Cytoscape-Format
|
||||
*/
|
||||
function convertToCytoscapeFormat(data) {
|
||||
const elements = {
|
||||
nodes: [],
|
||||
edges: []
|
||||
};
|
||||
|
||||
// Nodes hinzufügen
|
||||
if (data.nodes && data.nodes.length > 0) {
|
||||
data.nodes.forEach(node => {
|
||||
elements.nodes.push({
|
||||
data: {
|
||||
id: String(node.id),
|
||||
name: node.name,
|
||||
description: node.description || 'Keine Beschreibung verfügbar',
|
||||
category: node.category || 'Allgemein',
|
||||
color: node.color_code || getRandomColor()
|
||||
}
|
||||
});
|
||||
|
||||
// Kante zum Elternknoten hinzufügen (falls vorhanden)
|
||||
if (node.parent_id) {
|
||||
elements.edges.push({
|
||||
data: {
|
||||
id: `edge-${node.parent_id}-${node.id}`,
|
||||
source: String(node.parent_id),
|
||||
target: String(node.id)
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Kante zum Elternknoten hinzufügen (falls vorhanden)
|
||||
if (node.parent_id) {
|
||||
elements.edges.push({
|
||||
data: {
|
||||
id: `edge-${node.parent_id}-${node.id}`,
|
||||
source: String(node.parent_id),
|
||||
target: String(node.id)
|
||||
}
|
||||
// Zusätzliche Kanten zwischen Knoten hinzufügen (falls in den Daten vorhanden)
|
||||
if (data.edges && data.edges.length > 0) {
|
||||
data.edges.forEach(edge => {
|
||||
elements.edges.push({
|
||||
data: {
|
||||
id: `edge-${edge.source}-${edge.target}`,
|
||||
source: String(edge.source),
|
||||
target: String(edge.target)
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Zusätzliche Kanten zwischen Knoten hinzufügen (falls in den Daten vorhanden)
|
||||
if (data.edges && data.edges.length > 0) {
|
||||
data.edges.forEach(edge => {
|
||||
elements.edges.push({
|
||||
data: {
|
||||
id: `edge-${edge.source}-${edge.target}`,
|
||||
source: String(edge.source),
|
||||
target: String(edge.target)
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualisiert das Informations-Panel mit den Knotendaten
|
||||
*/
|
||||
function updateNodeInfoPanel(nodeData) {
|
||||
if (nodeInfoPanel && nodeDescription) {
|
||||
// Panel anzeigen
|
||||
nodeInfoPanel.style.display = 'block';
|
||||
|
||||
// Titel und Beschreibung aktualisieren
|
||||
const titleElement = nodeInfoPanel.querySelector('.info-panel-title');
|
||||
if (titleElement) {
|
||||
titleElement.textContent = nodeData.name;
|
||||
}
|
||||
|
||||
if (nodeDescription) {
|
||||
nodeDescription.textContent = nodeData.description || 'Keine Beschreibung verfügbar';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualisiert die Liste der verbundenen Knoten
|
||||
*/
|
||||
function updateConnectedNodes(node) {
|
||||
if (connectedNodes) {
|
||||
// Leere den Container
|
||||
connectedNodes.innerHTML = '';
|
||||
|
||||
// Hole verbundene Knoten
|
||||
const connectedEdges = node.connectedEdges();
|
||||
|
||||
if (connectedEdges.length === 0) {
|
||||
connectedNodes.innerHTML = '<div class="text-sm italic">Keine verbundenen Knoten</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Füge alle verbundenen Knoten hinzu
|
||||
connectedEdges.forEach(edge => {
|
||||
const targetNode = edge.target().id() === node.id() ? edge.source() : edge.target();
|
||||
const targetData = targetNode.data();
|
||||
|
||||
const nodeLink = document.createElement('div');
|
||||
nodeLink.className = 'node-link';
|
||||
nodeLink.innerHTML = `
|
||||
<span class="w-2 h-2 rounded-full" style="background-color: ${targetData.color}"></span>
|
||||
${targetData.name}
|
||||
`;
|
||||
|
||||
// Klick-Event zum Fokussieren des Knotens
|
||||
nodeLink.addEventListener('click', () => {
|
||||
mindmap.center(targetNode);
|
||||
targetNode.select();
|
||||
updateNodeInfoPanel(targetData);
|
||||
updateConnectedNodes(targetNode);
|
||||
});
|
||||
|
||||
connectedNodes.appendChild(nodeLink);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualisiert das Informations-Panel mit den Knotendaten
|
||||
*/
|
||||
function updateNodeInfoPanel(nodeData) {
|
||||
if (nodeInfoPanel && nodeDescription) {
|
||||
// Panel anzeigen
|
||||
nodeInfoPanel.style.display = 'block';
|
||||
/**
|
||||
* Aktualisiert die Styles bei Dark Mode-Änderungen
|
||||
*/
|
||||
function updateDarkModeStyles(isDark) {
|
||||
if (!mindmap) return;
|
||||
|
||||
// Titel und Beschreibung aktualisieren
|
||||
const titleElement = nodeInfoPanel.querySelector('.info-panel-title');
|
||||
if (titleElement) {
|
||||
titleElement.textContent = nodeData.name;
|
||||
}
|
||||
const textColor = isDark ? '#f1f5f9' : '#334155';
|
||||
const textBgColor = isDark ? 'rgba(30, 41, 59, 0.8)' : 'rgba(241, 245, 249, 0.8)';
|
||||
const edgeColor = isDark ? 'rgba(255, 255, 255, 0.15)' : 'rgba(30, 41, 59, 0.15)';
|
||||
|
||||
if (nodeDescription) {
|
||||
nodeDescription.textContent = nodeData.description || 'Keine Beschreibung verfügbar';
|
||||
}
|
||||
mindmap.style()
|
||||
.selector('node')
|
||||
.style({
|
||||
'color': textColor,
|
||||
'text-background-color': textBgColor
|
||||
})
|
||||
.selector('edge')
|
||||
.style({
|
||||
'line-color': edgeColor,
|
||||
'target-arrow-color': edgeColor
|
||||
})
|
||||
.update();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualisiert die Liste der verbundenen Knoten
|
||||
*/
|
||||
function updateConnectedNodes(node) {
|
||||
if (connectedNodes) {
|
||||
// Leere den Container
|
||||
connectedNodes.innerHTML = '';
|
||||
|
||||
// Hole verbundene Knoten
|
||||
const connectedEdges = node.connectedEdges();
|
||||
|
||||
if (connectedEdges.length === 0) {
|
||||
connectedNodes.innerHTML = '<div class="text-sm italic">Keine verbundenen Knoten</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Füge alle verbundenen Knoten hinzu
|
||||
connectedEdges.forEach(edge => {
|
||||
const targetNode = edge.target().id() === node.id() ? edge.source() : edge.target();
|
||||
const targetData = targetNode.data();
|
||||
|
||||
const nodeLink = document.createElement('div');
|
||||
nodeLink.className = 'node-link';
|
||||
nodeLink.innerHTML = `
|
||||
<span class="w-2 h-2 rounded-full" style="background-color: ${targetData.color}"></span>
|
||||
${targetData.name}
|
||||
`;
|
||||
|
||||
// Klick-Event zum Fokussieren des Knotens
|
||||
nodeLink.addEventListener('click', () => {
|
||||
mindmap.center(targetNode);
|
||||
targetNode.select();
|
||||
updateNodeInfoPanel(targetData);
|
||||
updateConnectedNodes(targetNode);
|
||||
});
|
||||
|
||||
connectedNodes.appendChild(nodeLink);
|
||||
});
|
||||
|
||||
/**
|
||||
* Generiert eine zufällige Farbe
|
||||
*/
|
||||
function getRandomColor() {
|
||||
const colors = [
|
||||
'#4299E1', // Blau
|
||||
'#9F7AEA', // Lila
|
||||
'#48BB78', // Grün
|
||||
'#ED8936', // Orange
|
||||
'#ED64A6', // Pink
|
||||
'#F56565' // Rot
|
||||
];
|
||||
return colors[Math.floor(Math.random() * colors.length)];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualisiert die Styles bei Dark Mode-Änderungen
|
||||
*/
|
||||
function updateDarkModeStyles(isDark) {
|
||||
if (!mindmap) return;
|
||||
|
||||
const textColor = isDark ? '#f1f5f9' : '#334155';
|
||||
const textBgColor = isDark ? 'rgba(30, 41, 59, 0.8)' : 'rgba(241, 245, 249, 0.8)';
|
||||
const edgeColor = isDark ? 'rgba(255, 255, 255, 0.15)' : 'rgba(30, 41, 59, 0.15)';
|
||||
|
||||
mindmap.style()
|
||||
.selector('node')
|
||||
.style({
|
||||
'color': textColor,
|
||||
'text-background-color': textBgColor
|
||||
})
|
||||
.selector('edge')
|
||||
.style({
|
||||
'line-color': edgeColor,
|
||||
'target-arrow-color': edgeColor
|
||||
})
|
||||
.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generiert eine zufällige Farbe
|
||||
*/
|
||||
function getRandomColor() {
|
||||
const colors = [
|
||||
'#4299E1', // Blau
|
||||
'#9F7AEA', // Lila
|
||||
'#48BB78', // Grün
|
||||
'#ED8936', // Orange
|
||||
'#ED64A6', // Pink
|
||||
'#F56565' // Rot
|
||||
];
|
||||
return colors[Math.floor(Math.random() * colors.length)];
|
||||
}
|
||||
|
||||
// Initialisiere die Mindmap-Seite
|
||||
initMindmapPage();
|
||||
@@ -16,7 +16,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
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!');
|
||||
|
||||
@@ -111,12 +111,6 @@
|
||||
|
||||
<!-- ChatGPT Assistant -->
|
||||
<script src="{{ url_for('static', filename='js/modules/chatgpt-assistant.js') }}"></script>
|
||||
|
||||
<!-- MindMap Visualization Module -->
|
||||
<script src="{{ url_for('static', filename='js/modules/mindmap.js') }}"></script>
|
||||
|
||||
<!-- MindMap Page Module -->
|
||||
<script src="{{ url_for('static', filename='js/modules/mindmap-page.js') }}"></script>
|
||||
|
||||
<!-- Neural Network Background Script -->
|
||||
<script src="{{ url_for('static', filename='neural-network-background.js') }}"></script>
|
||||
@@ -741,204 +735,205 @@
|
||||
<!-- Hilfsscripts -->
|
||||
{% block scripts %}{% endblock %}
|
||||
|
||||
<!-- KI-Chat Initialisierung -->
|
||||
<!-- ChatGPT Initialisierung -->
|
||||
<script>
|
||||
// ChatGPT-Assistent Klasse
|
||||
class ChatGPTAssistant {
|
||||
constructor() {
|
||||
this.chatContainer = null;
|
||||
this.messages = [];
|
||||
this.isOpen = false;
|
||||
}
|
||||
|
||||
init() {
|
||||
// Chat-Container erstellen, falls noch nicht vorhanden
|
||||
if (!document.getElementById('chat-assistant-container')) {
|
||||
this.createChatInterface();
|
||||
// Prüfe, ob ChatGPTAssistant bereits existiert
|
||||
if (typeof ChatGPTAssistant === 'undefined') {
|
||||
class ChatGPTAssistant {
|
||||
constructor() {
|
||||
this.chatContainer = null;
|
||||
this.messages = [];
|
||||
this.isOpen = false;
|
||||
}
|
||||
|
||||
// Event-Listener für Chat-Button
|
||||
const chatButton = document.getElementById('chat-assistant-button');
|
||||
if (chatButton) {
|
||||
chatButton.addEventListener('click', () => this.toggleChat());
|
||||
init() {
|
||||
// Chat-Container erstellen, falls noch nicht vorhanden
|
||||
if (!document.getElementById('chat-assistant-container')) {
|
||||
this.createChatInterface();
|
||||
}
|
||||
|
||||
// Event-Listener für Chat-Button
|
||||
const chatButton = document.getElementById('chat-assistant-button');
|
||||
if (chatButton) {
|
||||
chatButton.addEventListener('click', () => this.toggleChat());
|
||||
}
|
||||
|
||||
// Event-Listener für Senden-Button
|
||||
const sendButton = document.getElementById('chat-send-button');
|
||||
if (sendButton) {
|
||||
sendButton.addEventListener('click', () => this.sendMessage());
|
||||
}
|
||||
|
||||
// Event-Listener für Eingabefeld (Enter-Taste)
|
||||
const inputField = document.getElementById('chat-input');
|
||||
if (inputField) {
|
||||
inputField.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
this.sendMessage();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
console.log('KI-Assistent erfolgreich initialisiert');
|
||||
}
|
||||
|
||||
// Event-Listener für Senden-Button
|
||||
const sendButton = document.getElementById('chat-send-button');
|
||||
if (sendButton) {
|
||||
sendButton.addEventListener('click', () => this.sendMessage());
|
||||
}
|
||||
|
||||
// Event-Listener für Eingabefeld (Enter-Taste)
|
||||
const inputField = document.getElementById('chat-input');
|
||||
if (inputField) {
|
||||
inputField.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
this.sendMessage();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
console.log('KI-Assistent erfolgreich initialisiert');
|
||||
}
|
||||
|
||||
createChatInterface() {
|
||||
// Chat-Button erstellen
|
||||
const chatButton = document.createElement('button');
|
||||
chatButton.id = 'chat-assistant-button';
|
||||
chatButton.className = 'fixed bottom-6 right-6 bg-primary-600 text-white rounded-full p-4 shadow-lg z-50 hover:bg-primary-700 transition-all';
|
||||
chatButton.innerHTML = '<i class="fas fa-robot text-xl"></i>';
|
||||
document.body.appendChild(chatButton);
|
||||
|
||||
// Chat-Container erstellen
|
||||
const chatContainer = document.createElement('div');
|
||||
chatContainer.id = 'chat-assistant-container';
|
||||
chatContainer.className = 'fixed bottom-24 right-6 w-80 md:w-96 bg-white dark:bg-gray-800 rounded-xl shadow-xl z-50 flex flex-col transition-all duration-300 transform scale-0 origin-bottom-right';
|
||||
chatContainer.style.height = '500px';
|
||||
chatContainer.style.maxHeight = '70vh';
|
||||
|
||||
// Chat-Header
|
||||
chatContainer.innerHTML = `
|
||||
<div class="p-4 border-b dark:border-gray-700 flex justify-between items-center">
|
||||
<h3 class="font-bold text-gray-800 dark:text-white">Systades Assistent</h3>
|
||||
<button id="chat-close-button" class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div id="chat-messages" class="flex-1 overflow-y-auto p-4 space-y-4"></div>
|
||||
<div class="p-4 border-t dark:border-gray-700">
|
||||
<div class="flex space-x-2">
|
||||
<input id="chat-input" type="text" placeholder="Frage stellen..." class="flex-1 px-4 py-2 rounded-lg border dark:border-gray-700 dark:bg-gray-700 dark:text-white focus:outline-none focus:ring-2 focus:ring-primary-500">
|
||||
<button id="chat-send-button" class="bg-primary-600 text-white px-4 py-2 rounded-lg hover:bg-primary-700 transition-all">
|
||||
<i class="fas fa-paper-plane"></i>
|
||||
createChatInterface() {
|
||||
// Chat-Button erstellen
|
||||
const chatButton = document.createElement('button');
|
||||
chatButton.id = 'chat-assistant-button';
|
||||
chatButton.className = 'fixed bottom-6 right-6 bg-primary-600 text-white rounded-full p-4 shadow-lg z-50 hover:bg-primary-700 transition-all';
|
||||
chatButton.innerHTML = '<i class="fas fa-robot text-xl"></i>';
|
||||
document.body.appendChild(chatButton);
|
||||
|
||||
// Chat-Container erstellen
|
||||
const chatContainer = document.createElement('div');
|
||||
chatContainer.id = 'chat-assistant-container';
|
||||
chatContainer.className = 'fixed bottom-24 right-6 w-80 md:w-96 bg-white dark:bg-gray-800 rounded-xl shadow-xl z-50 flex flex-col transition-all duration-300 transform scale-0 origin-bottom-right';
|
||||
chatContainer.style.height = '500px';
|
||||
chatContainer.style.maxHeight = '70vh';
|
||||
|
||||
// Chat-Header
|
||||
chatContainer.innerHTML = `
|
||||
<div class="p-4 border-b dark:border-gray-700 flex justify-between items-center">
|
||||
<h3 class="font-bold text-gray-800 dark:text-white">Systades Assistent</h3>
|
||||
<button id="chat-close-button" class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(chatContainer);
|
||||
this.chatContainer = chatContainer;
|
||||
|
||||
// Event-Listener für Schließen-Button
|
||||
const closeButton = document.getElementById('chat-close-button');
|
||||
if (closeButton) {
|
||||
closeButton.addEventListener('click', () => this.toggleChat());
|
||||
}
|
||||
}
|
||||
|
||||
toggleChat() {
|
||||
this.isOpen = !this.isOpen;
|
||||
if (this.isOpen) {
|
||||
this.chatContainer.classList.remove('scale-0');
|
||||
this.chatContainer.classList.add('scale-100');
|
||||
} else {
|
||||
this.chatContainer.classList.remove('scale-100');
|
||||
this.chatContainer.classList.add('scale-0');
|
||||
}
|
||||
}
|
||||
|
||||
async sendMessage() {
|
||||
const inputField = document.getElementById('chat-input');
|
||||
const messageText = inputField.value.trim();
|
||||
|
||||
if (!messageText) return;
|
||||
|
||||
// Benutzer-Nachricht anzeigen
|
||||
this.addMessage('user', messageText);
|
||||
inputField.value = '';
|
||||
|
||||
// Lade-Indikator anzeigen
|
||||
this.addMessage('assistant', '...', 'loading-message');
|
||||
|
||||
try {
|
||||
// API-Anfrage senden
|
||||
const response = await fetch('/api/assistant', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
messages: this.messages.map(msg => ({
|
||||
role: msg.role,
|
||||
content: msg.content
|
||||
}))
|
||||
})
|
||||
});
|
||||
<div id="chat-messages" class="flex-1 overflow-y-auto p-4 space-y-4"></div>
|
||||
<div class="p-4 border-t dark:border-gray-700">
|
||||
<div class="flex space-x-2">
|
||||
<input id="chat-input" type="text" placeholder="Frage stellen..." class="flex-1 px-4 py-2 rounded-lg border dark:border-gray-700 dark:bg-gray-700 dark:text-white focus:outline-none focus:ring-2 focus:ring-primary-500">
|
||||
<button id="chat-send-button" class="bg-primary-600 text-white px-4 py-2 rounded-lg hover:bg-primary-700 transition-all">
|
||||
<i class="fas fa-paper-plane"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const data = await response.json();
|
||||
document.body.appendChild(chatContainer);
|
||||
this.chatContainer = chatContainer;
|
||||
|
||||
// Lade-Nachricht entfernen
|
||||
const loadingMessage = document.getElementById('loading-message');
|
||||
if (loadingMessage) {
|
||||
loadingMessage.remove();
|
||||
// Event-Listener für Schließen-Button
|
||||
const closeButton = document.getElementById('chat-close-button');
|
||||
if (closeButton) {
|
||||
closeButton.addEventListener('click', () => this.toggleChat());
|
||||
}
|
||||
|
||||
if (data.error) {
|
||||
this.addMessage('assistant', 'Entschuldigung, es ist ein Fehler aufgetreten: ' + data.error);
|
||||
}
|
||||
|
||||
toggleChat() {
|
||||
this.isOpen = !this.isOpen;
|
||||
if (this.isOpen) {
|
||||
this.chatContainer.classList.remove('scale-0');
|
||||
this.chatContainer.classList.add('scale-100');
|
||||
} else {
|
||||
this.addMessage('assistant', data.response);
|
||||
this.chatContainer.classList.remove('scale-100');
|
||||
this.chatContainer.classList.add('scale-0');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler bei der API-Anfrage:', error);
|
||||
}
|
||||
|
||||
async sendMessage() {
|
||||
const inputField = document.getElementById('chat-input');
|
||||
const messageText = inputField.value.trim();
|
||||
|
||||
// Lade-Nachricht entfernen
|
||||
const loadingMessage = document.getElementById('loading-message');
|
||||
if (loadingMessage) {
|
||||
loadingMessage.remove();
|
||||
if (!messageText) return;
|
||||
|
||||
// Benutzer-Nachricht anzeigen
|
||||
this.addMessage('user', messageText);
|
||||
inputField.value = '';
|
||||
|
||||
// Lade-Indikator anzeigen
|
||||
this.addMessage('assistant', '...', 'loading-message');
|
||||
|
||||
try {
|
||||
// API-Anfrage senden
|
||||
const response = await fetch('/api/assistant', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
messages: this.messages.map(msg => ({
|
||||
role: msg.role,
|
||||
content: msg.content
|
||||
}))
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Lade-Nachricht entfernen
|
||||
const loadingMessage = document.getElementById('loading-message');
|
||||
if (loadingMessage) {
|
||||
loadingMessage.remove();
|
||||
}
|
||||
|
||||
if (data.error) {
|
||||
this.addMessage('assistant', 'Entschuldigung, es ist ein Fehler aufgetreten: ' + data.error);
|
||||
} else {
|
||||
this.addMessage('assistant', data.response);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler bei der API-Anfrage:', error);
|
||||
|
||||
// Lade-Nachricht entfernen
|
||||
const loadingMessage = document.getElementById('loading-message');
|
||||
if (loadingMessage) {
|
||||
loadingMessage.remove();
|
||||
}
|
||||
|
||||
this.addMessage('assistant', 'Entschuldigung, es ist ein Fehler aufgetreten. Bitte versuche es später erneut.');
|
||||
}
|
||||
}
|
||||
|
||||
addMessage(role, content, id = null) {
|
||||
const messagesContainer = document.getElementById('chat-messages');
|
||||
|
||||
// Nachricht zum Array hinzufügen (außer Lade-Nachrichten)
|
||||
if (id !== 'loading-message') {
|
||||
this.messages.push({ role, content });
|
||||
}
|
||||
|
||||
this.addMessage('assistant', 'Entschuldigung, es ist ein Fehler aufgetreten. Bitte versuche es später erneut.');
|
||||
// Nachricht zum DOM hinzufügen
|
||||
const messageElement = document.createElement('div');
|
||||
messageElement.className = `p-3 rounded-lg ${role === 'user' ? 'bg-primary-100 dark:bg-primary-900/30 ml-6' : 'bg-gray-100 dark:bg-gray-700 mr-6'}`;
|
||||
if (id) {
|
||||
messageElement.id = id;
|
||||
}
|
||||
|
||||
messageElement.innerHTML = `
|
||||
<div class="flex items-start">
|
||||
<div class="w-8 h-8 rounded-full flex items-center justify-center ${role === 'user' ? 'bg-primary-600' : 'bg-gray-600'} text-white mr-2">
|
||||
<i class="fas ${role === 'user' ? 'fa-user' : 'fa-robot'} text-xs"></i>
|
||||
</div>
|
||||
<div class="flex-1 text-sm ${role === 'user' ? 'text-gray-800 dark:text-gray-200' : 'text-gray-700 dark:text-gray-300'}">
|
||||
${content}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
messagesContainer.appendChild(messageElement);
|
||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||
}
|
||||
}
|
||||
|
||||
addMessage(role, content, id = null) {
|
||||
const messagesContainer = document.getElementById('chat-messages');
|
||||
|
||||
// Nachricht zum Array hinzufügen (außer Lade-Nachrichten)
|
||||
if (id !== 'loading-message') {
|
||||
this.messages.push({ role, content });
|
||||
}
|
||||
|
||||
// Nachricht zum DOM hinzufügen
|
||||
const messageElement = document.createElement('div');
|
||||
messageElement.className = `p-3 rounded-lg ${role === 'user' ? 'bg-primary-100 dark:bg-primary-900/30 ml-6' : 'bg-gray-100 dark:bg-gray-700 mr-6'}`;
|
||||
if (id) {
|
||||
messageElement.id = id;
|
||||
}
|
||||
|
||||
messageElement.innerHTML = `
|
||||
<div class="flex items-start">
|
||||
<div class="w-8 h-8 rounded-full flex items-center justify-center ${role === 'user' ? 'bg-primary-600' : 'bg-gray-600'} text-white mr-2">
|
||||
<i class="fas ${role === 'user' ? 'fa-user' : 'fa-robot'} text-xs"></i>
|
||||
</div>
|
||||
<div class="flex-1 text-sm ${role === 'user' ? 'text-gray-800 dark:text-gray-200' : 'text-gray-700 dark:text-gray-300'}">
|
||||
${content}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
messagesContainer.appendChild(messageElement);
|
||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialisiere den ChatGPT-Assistenten direkt, um sicherzustellen,
|
||||
// dass er auf jeder Seite verfügbar ist, selbst wenn MindMap nicht geladen ist
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Prüfen, ob der Assistent bereits durch MindMap initialisiert wurde
|
||||
if (!window.MindMap || !window.MindMap.assistant) {
|
||||
console.log('KI-Assistent wird direkt initialisiert...');
|
||||
const assistant = new ChatGPTAssistant();
|
||||
assistant.init();
|
||||
|
||||
// Speichere in window.MindMap, falls es existiert, oder erstelle es
|
||||
if (!window.MindMap) {
|
||||
window.MindMap = {};
|
||||
// Initialisiere den ChatGPT-Assistenten direkt
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Prüfen, ob der Assistent bereits durch MindMap initialisiert wurde
|
||||
if (!window.MindMap || !window.MindMap.assistant) {
|
||||
console.log('KI-Assistent wird direkt initialisiert...');
|
||||
const assistant = new ChatGPTAssistant();
|
||||
assistant.init();
|
||||
|
||||
// Speichere in window.MindMap, falls es existiert, oder erstelle es
|
||||
if (!window.MindMap) {
|
||||
window.MindMap = {};
|
||||
}
|
||||
window.MindMap.assistant = assistant;
|
||||
}
|
||||
window.MindMap.assistant = assistant;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Dark/Light-Mode vereinheitlicht -->
|
||||
|
||||
@@ -228,7 +228,5 @@
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape-cose-bilkent/4.1.0/cytoscape-cose-bilkent.min.js"></script>
|
||||
|
||||
<!-- Unsere JavaScript-Dateien -->
|
||||
<script src="{{ url_for('static', filename='js/mindmap-interaction.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/mindmap-init.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/update_mindmap.js') }}"></script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user