1051 lines
34 KiB
HTML
1051 lines
34 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Mindmap{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tippy.js@6.3.7/dist/tippy.css">
|
|
<style>
|
|
/* Globaler Stil - Hintergrund über die gesamte Seite */
|
|
html, body {
|
|
background-color: var(--dark-bg) !important;
|
|
min-height: 100vh;
|
|
width: 100%;
|
|
color: #ffffff;
|
|
margin: 0;
|
|
padding: 0;
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
/* Sicherstellen, dass der Hintergrund die gesamte Seite abdeckt */
|
|
#app-container, .container, main, .mx-auto, .py-12, body > div, #content-wrapper, #mindmap-container {
|
|
background-color: var(--dark-bg) !important;
|
|
width: 100%;
|
|
}
|
|
|
|
/* Neuronales Netz / Universum Hintergrund */
|
|
.neural-universe-bg {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: radial-gradient(ellipse at center, rgba(30, 20, 60, 0.7) 0%, rgba(10, 10, 25, 0.9) 100%);
|
|
z-index: -1;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.neural-universe-bg::before {
|
|
content: "";
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background-image:
|
|
radial-gradient(circle at 20% 30%, rgba(179, 143, 255, 0.15) 0%, transparent 25%),
|
|
radial-gradient(circle at 80% 20%, rgba(88, 169, 255, 0.1) 0%, transparent 20%),
|
|
radial-gradient(circle at 40% 80%, rgba(20, 184, 166, 0.12) 0%, transparent 30%),
|
|
radial-gradient(circle at 70% 65%, rgba(139, 92, 246, 0.08) 0%, transparent 25%);
|
|
z-index: -1;
|
|
}
|
|
|
|
.neural-universe-bg::after {
|
|
content: "";
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><circle cx="50" cy="50" r="0.5" fill="rgba(255,255,255,0.3)"/></svg>');
|
|
background-size: 200px 200px;
|
|
opacity: 0.4;
|
|
z-index: -1;
|
|
animation: twinkling 8s infinite linear;
|
|
}
|
|
|
|
@keyframes twinkling {
|
|
from { transform: rotate(0deg) scale(1); }
|
|
to { transform: rotate(360deg) scale(1.2); }
|
|
}
|
|
|
|
/* Verbesserte Glasmorphismus-Stile für Karten */
|
|
.glass-card, .mindmap-card {
|
|
background: rgba(24, 28, 45, 0.75);
|
|
backdrop-filter: blur(20px);
|
|
-webkit-backdrop-filter: blur(20px);
|
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
border-radius: 24px;
|
|
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.35);
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.glass-card:hover, .mindmap-card:hover {
|
|
transform: translateY(-5px);
|
|
box-shadow: 0 20px 48px rgba(0, 0, 0, 0.45);
|
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
}
|
|
|
|
/* Feature-Cards-Stil mit besserem Glasmorphismus */
|
|
.feature-card {
|
|
background: rgba(24, 28, 45, 0.75);
|
|
border-radius: 24px;
|
|
overflow: hidden;
|
|
backdrop-filter: blur(20px);
|
|
-webkit-backdrop-filter: blur(20px);
|
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
box-shadow: 0 12px 36px rgba(0, 0, 0, 0.35);
|
|
transition: all 0.3s ease;
|
|
height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding: 1.75rem;
|
|
}
|
|
|
|
.feature-card:hover {
|
|
transform: translateY(-5px);
|
|
border: 1px solid rgba(255, 255, 255, 0.25);
|
|
box-shadow: 0 20px 48px rgba(0, 0, 0, 0.45);
|
|
background: rgba(28, 32, 52, 0.9);
|
|
}
|
|
|
|
.feature-card-icon {
|
|
font-size: 2.75rem;
|
|
margin-bottom: 1.25rem;
|
|
background: linear-gradient(135deg, #b38fff, #14b8a6);
|
|
-webkit-background-clip: text;
|
|
background-clip: text;
|
|
color: transparent;
|
|
filter: drop-shadow(0 0 10px rgba(179, 143, 255, 0.6));
|
|
}
|
|
|
|
/* Feature-Card-Text besser lesbar machen */
|
|
.feature-card h3 {
|
|
font-size: 1.75rem;
|
|
font-weight: 700;
|
|
margin-bottom: 0.9rem;
|
|
color: #ffffff;
|
|
text-shadow: 0 2px 3px rgba(0, 0, 0, 0.3);
|
|
letter-spacing: 0.2px;
|
|
}
|
|
|
|
.feature-card p {
|
|
color: rgba(255, 255, 255, 0.95);
|
|
font-size: 1.1rem;
|
|
line-height: 1.6;
|
|
font-weight: 400;
|
|
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
|
|
letter-spacing: 0.3px;
|
|
}
|
|
|
|
/* Visuelle Trennung für den unteren Bereich */
|
|
.visual-divider {
|
|
position: relative;
|
|
height: 6px;
|
|
width: 100%;
|
|
background: linear-gradient(90deg,
|
|
transparent 0%,
|
|
rgba(179, 143, 255, 0.8) 20%,
|
|
rgba(88, 169, 255, 0.8) 50%,
|
|
rgba(179, 143, 255, 0.8) 80%,
|
|
transparent 100%);
|
|
margin: 2rem 0;
|
|
box-shadow: 0 0 15px rgba(179, 143, 255, 0.5);
|
|
backdrop-filter: blur(10px);
|
|
-webkit-backdrop-filter: blur(10px);
|
|
}
|
|
|
|
.visual-divider::before, .visual-divider::after {
|
|
content: "";
|
|
position: absolute;
|
|
height: 1px;
|
|
width: 100%;
|
|
background: linear-gradient(90deg,
|
|
transparent 0%,
|
|
rgba(255, 255, 255, 0.3) 20%,
|
|
rgba(255, 255, 255, 0.5) 50%,
|
|
rgba(255, 255, 255, 0.3) 80%,
|
|
transparent 100%);
|
|
}
|
|
|
|
.visual-divider::before {
|
|
top: -10px;
|
|
}
|
|
|
|
.visual-divider::after {
|
|
bottom: -10px;
|
|
}
|
|
|
|
/* Mindmap-Header */
|
|
.mindmap-header {
|
|
background: rgba(20, 24, 42, 0.85);
|
|
border-radius: 24px;
|
|
backdrop-filter: blur(20px);
|
|
-webkit-backdrop-filter: blur(20px);
|
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
box-shadow: 0 12px 36px rgba(0, 0, 0, 0.35);
|
|
}
|
|
|
|
.gradient-text {
|
|
background: linear-gradient(to right, #b38fff, #58a9ff);
|
|
-webkit-background-clip: text;
|
|
background-clip: text;
|
|
color: transparent;
|
|
text-shadow: 0 0 25px rgba(179, 143, 255, 0.25);
|
|
font-weight: 800;
|
|
}
|
|
|
|
/* D3.js Mindmap spezifische Stile */
|
|
.mindmap-svg {
|
|
background: rgba(14, 18, 32, 0.3);
|
|
width: 100%;
|
|
height: 100%;
|
|
border-radius: 24px;
|
|
}
|
|
|
|
/* Verbesserte Mindmap-Knoten-Stile */
|
|
.node {
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.node circle {
|
|
stroke: rgba(255, 255, 255, 0.12);
|
|
stroke-width: 2px;
|
|
fill: rgba(24, 28, 45, 0.85);
|
|
filter: url(#glass-effect);
|
|
}
|
|
|
|
.node:hover circle {
|
|
filter: url(#hover-glow);
|
|
stroke: rgba(255, 255, 255, 0.25);
|
|
}
|
|
|
|
.node.selected circle {
|
|
filter: url(#selected-glow);
|
|
stroke: rgba(179, 143, 255, 0.6);
|
|
stroke-width: 3px;
|
|
}
|
|
|
|
.node-label {
|
|
font-family: 'Inter', 'SF Pro Display', system-ui, sans-serif;
|
|
font-weight: 600;
|
|
pointer-events: none;
|
|
user-select: none;
|
|
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.7);
|
|
font-size: 16px;
|
|
letter-spacing: 0.3px;
|
|
fill: #ffffff;
|
|
}
|
|
|
|
.link {
|
|
transition: stroke 0.3s ease, opacity 0.3s ease;
|
|
stroke: rgba(255, 255, 255, 0.3);
|
|
stroke-width: 2;
|
|
opacity: 0.7;
|
|
}
|
|
|
|
.link:hover, .link.highlighted {
|
|
stroke: rgba(179, 143, 255, 0.7);
|
|
opacity: 0.9;
|
|
stroke-width: 3;
|
|
}
|
|
|
|
/* Control Bar mit verbesserten Glasmorphismus und Lesbarkeit */
|
|
.controls-bar {
|
|
background: rgba(24, 28, 45, 0.85);
|
|
backdrop-filter: blur(20px);
|
|
-webkit-backdrop-filter: blur(20px);
|
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
border-radius: 20px 20px 0 0;
|
|
}
|
|
|
|
/* Tooltip Stile */
|
|
.tippy-box[data-theme~='mindmap'] {
|
|
background-color: rgba(24, 28, 45, 0.95);
|
|
color: #ffffff;
|
|
border: 1px solid rgba(179, 143, 255, 0.25);
|
|
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.5), 0 0 15px rgba(179, 143, 255, 0.25);
|
|
backdrop-filter: blur(20px);
|
|
-webkit-backdrop-filter: blur(20px);
|
|
border-radius: 16px;
|
|
}
|
|
|
|
.tippy-box[data-theme~='mindmap'] .tippy-arrow {
|
|
color: rgba(24, 28, 45, 0.95);
|
|
}
|
|
|
|
.node-tooltip {
|
|
font-family: 'Inter', 'SF Pro Display', system-ui, sans-serif;
|
|
font-size: 14px;
|
|
line-height: 1.6;
|
|
padding: 12px 16px;
|
|
letter-spacing: 0.3px;
|
|
}
|
|
|
|
.node-tooltip strong {
|
|
font-weight: 600;
|
|
color: #b38fff;
|
|
}
|
|
|
|
/* Gedanken-Container */
|
|
.thought-container {
|
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
border-radius: 24px;
|
|
backdrop-filter: blur(20px);
|
|
-webkit-backdrop-filter: blur(20px);
|
|
background: rgba(24, 28, 45, 0.85);
|
|
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.3);
|
|
}
|
|
|
|
/* Angepasste Scrollbar für den Gedanken-Container */
|
|
.custom-scrollbar::-webkit-scrollbar {
|
|
width: 6px;
|
|
}
|
|
|
|
.custom-scrollbar::-webkit-scrollbar-track {
|
|
background: rgba(255, 255, 255, 0.08);
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.custom-scrollbar::-webkit-scrollbar-thumb {
|
|
background: rgba(179, 143, 255, 0.5);
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
|
background: rgba(179, 143, 255, 0.7);
|
|
}
|
|
|
|
/* Pulse-Animation für leere Gedanken */
|
|
.pulse-animation {
|
|
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
}
|
|
|
|
@keyframes pulse {
|
|
0%, 100% {
|
|
opacity: 1;
|
|
}
|
|
50% {
|
|
opacity: 0.5;
|
|
}
|
|
}
|
|
|
|
/* Button-Effekte mit verbesserter Lesbarkeit */
|
|
.control-btn {
|
|
background: rgba(32, 36, 55, 0.85);
|
|
color: #ffffff;
|
|
border: 1px solid rgba(255, 255, 255, 0.15);
|
|
border-radius: 16px;
|
|
padding: 0.75rem 1.5rem;
|
|
font-weight: 600;
|
|
transition: all 0.3s ease;
|
|
backdrop-filter: blur(15px);
|
|
-webkit-backdrop-filter: blur(15px);
|
|
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.25);
|
|
font-size: 1rem;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 0.5rem;
|
|
letter-spacing: 0.3px;
|
|
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
|
}
|
|
|
|
.control-btn:hover {
|
|
background: rgba(179, 143, 255, 0.35);
|
|
transform: translateY(-3px);
|
|
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.35), 0 0 15px rgba(179, 143, 255, 0.25);
|
|
border: 1px solid rgba(255, 255, 255, 0.25);
|
|
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
|
|
}
|
|
|
|
.control-btn:active {
|
|
transform: translateY(1px);
|
|
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.2);
|
|
}
|
|
|
|
.control-btn.active {
|
|
background: rgba(179, 143, 255, 0.4);
|
|
border: 1px solid rgba(179, 143, 255, 0.5);
|
|
box-shadow: 0 0 15px rgba(179, 143, 255, 0.3);
|
|
}
|
|
|
|
/* Glow Effect für Buttons */
|
|
.btn-glow:hover {
|
|
box-shadow: 0 0 15px rgba(179, 143, 255, 0.5);
|
|
}
|
|
|
|
/* Light Mode Anpassungen */
|
|
html.light .mindmap-svg {
|
|
background: rgba(240, 244, 248, 0.3);
|
|
}
|
|
|
|
html.light .node circle {
|
|
fill: rgba(255, 255, 255, 0.9);
|
|
stroke: rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
html.light .node-label {
|
|
fill: #1a202c;
|
|
text-shadow: 0 1px 2px rgba(255, 255, 255, 0.7);
|
|
}
|
|
|
|
html.light .link {
|
|
stroke: rgba(0, 0, 0, 0.2);
|
|
}
|
|
|
|
html.light .glass-card,
|
|
html.light .mindmap-card,
|
|
html.light .feature-card,
|
|
html.light .thought-container,
|
|
html.light .mindmap-header,
|
|
html.light .controls-bar {
|
|
background: rgba(255, 255, 255, 0.85);
|
|
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
html.light .control-btn {
|
|
background: rgba(255, 255, 255, 0.9);
|
|
color: #1a202c;
|
|
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
text-shadow: none;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
|
font-weight: 600;
|
|
}
|
|
|
|
html.light .control-btn:hover {
|
|
background: rgba(179, 143, 255, 0.15);
|
|
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.12);
|
|
color: #7e3ff2;
|
|
font-weight: 700;
|
|
}
|
|
|
|
html.light .control-btn.active {
|
|
background: rgba(179, 143, 255, 0.2);
|
|
border: 1px solid rgba(126, 63, 242, 0.3);
|
|
color: #7e3ff2;
|
|
font-weight: 700;
|
|
}
|
|
|
|
html.light .feature-card h3 {
|
|
color: #1a202c;
|
|
text-shadow: none;
|
|
}
|
|
|
|
html.light .feature-card p {
|
|
color: #4a5568;
|
|
text-shadow: none;
|
|
}
|
|
|
|
html.light .node-tooltip strong {
|
|
color: #7e3ff2;
|
|
}
|
|
|
|
/* Karten in der Mindmap mit verbesserten Styles */
|
|
.mindmap-card {
|
|
background: rgba(24, 28, 45, 0.75);
|
|
border-radius: 24px;
|
|
overflow: hidden;
|
|
backdrop-filter: blur(20px);
|
|
-webkit-backdrop-filter: blur(20px);
|
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
box-shadow: 0 12px 36px rgba(0, 0, 0, 0.35);
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.mindmap-card:hover {
|
|
transform: translateY(-5px);
|
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.45);
|
|
background: rgba(28, 32, 52, 0.8);
|
|
}
|
|
|
|
.mindmap-card-header {
|
|
padding: 1.25rem 1.5rem;
|
|
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.mindmap-card-body {
|
|
padding: 1.5rem;
|
|
}
|
|
|
|
.mindmap-card-footer {
|
|
padding: 1.25rem 1.5rem;
|
|
border-top: 1px solid rgba(255, 255, 255, 0.06);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
html.light .mindmap-card-header,
|
|
html.light .mindmap-card-footer {
|
|
border-color: rgba(0, 0, 0, 0.06);
|
|
}
|
|
|
|
/* Animation für die Verbindungen im Mindmap */
|
|
.link {
|
|
stroke-dasharray: 5;
|
|
animation: dash 30s linear infinite;
|
|
stroke-width: 2.5px;
|
|
}
|
|
|
|
@keyframes dash {
|
|
to {
|
|
stroke-dashoffset: 1000;
|
|
}
|
|
}
|
|
|
|
/* Move the selection panel more to the left */
|
|
.thoughts-panel {
|
|
left: 20px !important; /* Changed from right: 20px */
|
|
width: 350px !important; /* Increased from 300px for better visibility */
|
|
background: rgba(24, 28, 45, 0.85) !important; /* More visible background */
|
|
border: 1px solid rgba(179, 143, 255, 0.3) !important; /* Accent colored border */
|
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5) !important; /* Stronger shadow */
|
|
}
|
|
|
|
/* Mindmap container with network background */
|
|
#mindmap-container::before {
|
|
content: "";
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background-image: none;
|
|
opacity: 0.2;
|
|
z-index: -1;
|
|
animation: pulse 10s ease-in-out infinite alternate;
|
|
pointer-events: none;
|
|
}
|
|
|
|
@keyframes pulse {
|
|
0% { opacity: 0.1; transform: scale(1); }
|
|
100% { opacity: 0.3; transform: scale(1.05); }
|
|
}
|
|
|
|
/* Enhance node visibility */
|
|
.node circle {
|
|
stroke-width: 3px !important;
|
|
filter: drop-shadow(0 0 8px rgba(179, 143, 255, 0.5)) !important;
|
|
}
|
|
|
|
.node.selected circle {
|
|
stroke: rgba(179, 143, 255, 0.9) !important;
|
|
stroke-width: 4px !important;
|
|
filter: drop-shadow(0 0 15px rgba(179, 143, 255, 0.8)) !important;
|
|
}
|
|
|
|
/* Improve visibility of node labels */
|
|
.node-label {
|
|
font-weight: 700 !important;
|
|
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.9) !important;
|
|
filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.8)) !important;
|
|
}
|
|
|
|
/* Make connections more visible */
|
|
.link {
|
|
stroke: rgba(179, 143, 255, 0.6) !important;
|
|
stroke-width: 2.5px !important;
|
|
filter: drop-shadow(0 0 5px rgba(179, 143, 255, 0.4)) !important;
|
|
}
|
|
|
|
.link.highlighted {
|
|
stroke: rgba(88, 169, 255, 0.8) !important;
|
|
stroke-width: 4px !important;
|
|
filter: drop-shadow(0 0 8px rgba(88, 169, 255, 0.6)) !important;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="relative overflow-hidden">
|
|
<!-- Neuronales Netz / Universum Hintergrund -->
|
|
<div class="neural-universe-bg"></div>
|
|
|
|
<div class="container mx-auto px-4 py-8">
|
|
<!-- Mindmap-Visualisierung Header -->
|
|
<div class="mindmap-header p-6 mb-6">
|
|
<h2 class="text-3xl font-bold gradient-text">Wissenslandschaft erkunden</h2>
|
|
<p class="text-gray-300 mb-0">Interagiere mit der Mindmap, um Verbindungen zu entdecken und neue Ideen hinzuzufügen</p>
|
|
</div>
|
|
|
|
<!-- Mindmap-Container - Jetzt größer -->
|
|
<div class="glass-card overflow-hidden mb-12">
|
|
<div id="mindmap-container" class="relative" style="height: 80vh; min-height: 700px;">
|
|
<!-- SVG Filters for node effects -->
|
|
<svg width="0" height="0" style="position: absolute;">
|
|
<defs>
|
|
<!-- Glasmorphismus-Effekt für Knoten -->
|
|
<filter id="glass-effect" x="-50%" y="-50%" width="200%" height="200%">
|
|
<feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur" />
|
|
<feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7" result="glow" />
|
|
<feBlend in="SourceGraphic" in2="glow" mode="normal" />
|
|
</filter>
|
|
<!-- Hover-Glow-Effekt -->
|
|
<filter id="hover-glow" x="-50%" y="-50%" width="200%" height="200%">
|
|
<feGaussianBlur in="SourceAlpha" stdDeviation="5" result="blur" />
|
|
<feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0.5 0 1 0 0 0.5 0 0 1 0 1 0 0 0 18 -7" result="glow" />
|
|
<feBlend in="SourceGraphic" in2="glow" mode="normal" />
|
|
</filter>
|
|
<!-- Ausgewählter-Knoten-Glow-Effekt -->
|
|
<filter id="selected-glow" x="-50%" y="-50%" width="200%" height="200%">
|
|
<feGaussianBlur in="SourceAlpha" stdDeviation="8" result="blur" />
|
|
<feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0.7 0 1 0 0 0.2 0 0 1 0 1 0 0 0 18 -7" result="glow" />
|
|
<feBlend in="SourceGraphic" in2="glow" mode="normal" />
|
|
</filter>
|
|
</defs>
|
|
</svg>
|
|
|
|
<!-- Lade-Overlay mit verbesserter Animation und Transition -->
|
|
<div class="mindmap-loading absolute inset-0 flex items-center justify-center z-10" style="background: rgba(14, 18, 32, 0.8); backdrop-filter: blur(10px); transition: opacity 0.5s ease-in-out;">
|
|
<div class="text-center">
|
|
<div class="inline-block animate-spin rounded-full h-16 w-16 border-t-3 border-b-3 border-purple-500 mb-6"></div>
|
|
<p class="text-white text-xl font-semibold mb-3">Wissenslandschaft wird geladen...</p>
|
|
<p class="text-gray-300 text-sm mb-4">Daten werden aus der Datenbank abgerufen</p>
|
|
<div class="w-80 h-3 bg-gray-800 rounded-full mt-2 overflow-hidden">
|
|
<div class="loading-progress h-full bg-gradient-to-r from-purple-500 via-blue-500 to-purple-500 rounded-full" style="width: 0%; transition: width 0.3s ease-in-out;"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Steuerungsleiste -->
|
|
<div class="controls-bar p-4 flex flex-wrap items-center justify-between gap-3">
|
|
<div class="flex flex-wrap gap-2">
|
|
<button class="control-btn" id="zoom-in-btn">
|
|
<i class="fas fa-search-plus mr-1"></i> Vergrößern
|
|
</button>
|
|
<button class="control-btn" id="zoom-out-btn">
|
|
<i class="fas fa-search-minus mr-1"></i> Verkleinern
|
|
</button>
|
|
<button class="control-btn" id="center-btn">
|
|
<i class="fas fa-bullseye mr-1"></i> Zentrieren
|
|
</button>
|
|
</div>
|
|
<div class="flex flex-wrap gap-2">
|
|
<button class="control-btn" id="add-thought-btn">
|
|
<i class="fas fa-plus-circle mr-1"></i> Gedanke hinzufügen
|
|
</button>
|
|
<button class="control-btn" id="connect-btn">
|
|
<i class="fas fa-link mr-1"></i> Verbinden
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Visuelle Trennung -->
|
|
<div class="visual-divider"></div>
|
|
|
|
<!-- Unterer Bereich: KI-Assistenz, Suche und Lernpfade -->
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
|
|
<!-- KI-Assistenz -->
|
|
<div class="feature-card">
|
|
<div class="feature-card-icon">
|
|
<i class="fas fa-robot"></i>
|
|
</div>
|
|
<h3>KI-Assistenz</h3>
|
|
<p>Lass dir von künstlicher Intelligenz helfen, neue Zusammenhänge zu entdecken, Inhalte zusammenzufassen und Fragen zu beantworten.</p>
|
|
</div>
|
|
|
|
<!-- Intelligente Suche -->
|
|
<div class="feature-card">
|
|
<div class="feature-card-icon">
|
|
<i class="fas fa-search"></i>
|
|
</div>
|
|
<h3>Intelligente Suche</h3>
|
|
<p>Finde genau die Informationen, die du suchst, mit fortschrittlichen Such- und Filterfunktionen für eine präzise Navigation durch das Wissen.</p>
|
|
</div>
|
|
|
|
<!-- Geführte Pfade -->
|
|
<div class="feature-card">
|
|
<div class="feature-card-icon">
|
|
<i class="fas fa-map-signs"></i>
|
|
</div>
|
|
<h3>Geführte Pfade</h3>
|
|
<p>Folge kuratierten Lernpfaden durch komplexe Themen oder erschaffe selbst Routen für andere, die deinen Gedankengängen folgen möchten.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal zum Hinzufügen eines neuen Gedanken -->
|
|
<div id="add-thought-modal" class="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50 hidden" style="backdrop-filter: blur(5px);">
|
|
<div class="glass-card w-full max-w-md p-6">
|
|
<div class="flex justify-between items-center mb-4">
|
|
<h3 class="text-xl font-bold text-white">Neuen Gedanken hinzufügen</h3>
|
|
<button class="text-gray-400 hover:text-white" onclick="document.getElementById('add-thought-modal').classList.add('hidden')">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
<form id="add-thought-form" method="POST">
|
|
<input type="hidden" id="thought-node-id" name="node_id">
|
|
<div class="mb-4">
|
|
<label class="block text-gray-300 mb-2" for="thought-title">Titel</label>
|
|
<input class="w-full bg-gray-800 text-white border border-gray-700 rounded-lg py-2 px-3" id="thought-title" name="title" placeholder="Titel des Gedankens" required>
|
|
</div>
|
|
<div class="mb-4">
|
|
<label class="block text-gray-300 mb-2" for="thought-content">Inhalt</label>
|
|
<textarea class="w-full bg-gray-800 text-white border border-gray-700 rounded-lg py-2 px-3 h-32" id="thought-content" name="content" placeholder="Beschreibe deinen Gedanken..." required></textarea>
|
|
</div>
|
|
<div class="flex justify-end space-x-3">
|
|
<button type="button" class="py-2 px-4 bg-gray-700 text-white rounded-lg" onclick="document.getElementById('add-thought-modal').classList.add('hidden')">Abbrechen</button>
|
|
<button type="submit" class="py-2 px-4 bg-gradient-to-r from-purple-600 to-blue-500 text-white rounded-lg">Speichern</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<!-- D3.js Library -->
|
|
<script src="https://d3js.org/d3.v7.min.js"></script>
|
|
|
|
<!-- Tippy.js für Tooltips -->
|
|
<script src="https://unpkg.com/@popperjs/core@2"></script>
|
|
<script src="https://unpkg.com/tippy.js@6"></script>
|
|
|
|
<!-- Mindmap scripts -->
|
|
<script src="{{ url_for('static', filename='js/modules/mindmap.js') }}"></script>
|
|
|
|
<!-- Initialization Script -->
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Mindmap-Container holen
|
|
const mindmapContainer = document.getElementById('mindmap-container');
|
|
|
|
// Options für die Visualisierung
|
|
const options = {
|
|
width: mindmapContainer.clientWidth,
|
|
height: mindmapContainer.clientHeight,
|
|
nodeRadius: 22,
|
|
selectedNodeRadius: 28,
|
|
linkDistance: 150,
|
|
chargeStrength: -1000,
|
|
centerForce: 0.15,
|
|
tooltipEnabled: true,
|
|
onNodeClick: function(node) {
|
|
console.log('Node clicked:', node);
|
|
|
|
// Gedanken zu diesem Knoten laden
|
|
fetch(`/api/nodes/${node.id}/thoughts`)
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
throw new Error('Netzwerkantwort war nicht ok');
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
console.log('Gedanken zu diesem Knoten:', data);
|
|
|
|
// Gedanken im Seitenbereich anzeigen
|
|
const thoughtsContainer = document.getElementById('thoughts-container');
|
|
if (thoughtsContainer) {
|
|
thoughtsContainer.innerHTML = '';
|
|
|
|
if (data.thoughts && data.thoughts.length > 0) {
|
|
data.thoughts.forEach(thought => {
|
|
const thoughtElement = document.createElement('div');
|
|
thoughtElement.className = 'thought-item bg-gray-800 rounded-lg p-4 mb-3';
|
|
thoughtElement.innerHTML = `
|
|
<h3 class="text-lg font-semibold text-white">${thought.title}</h3>
|
|
<p class="text-gray-300 mt-2">${thought.content}</p>
|
|
<div class="flex justify-between mt-3">
|
|
<span class="text-sm text-gray-400">${new Date(thought.created_at).toLocaleDateString('de-DE')}</span>
|
|
<button class="text-blue-400 hover:text-blue-300" data-thought-id="${thought.id}">
|
|
<i class="fas fa-bookmark"></i>
|
|
</button>
|
|
</div>
|
|
`;
|
|
thoughtsContainer.appendChild(thoughtElement);
|
|
});
|
|
} else {
|
|
thoughtsContainer.innerHTML = '<p class="text-gray-400">Keine Gedanken für diesen Knoten vorhanden.</p>';
|
|
}
|
|
}
|
|
|
|
// Aktualisiere das Formular zum Hinzufügen von Gedanken
|
|
document.getElementById('thought-node-id').value = node.id;
|
|
})
|
|
.catch(error => {
|
|
console.error('Fehler beim Laden der Gedanken:', error);
|
|
// Benutzer über den Fehler informieren
|
|
if (window.mindmap && window.mindmap.showFlash) {
|
|
window.mindmap.showFlash('Fehler beim Laden der Gedanken', 'error');
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
// Ladebalken-Animation starten
|
|
const progressBar = document.querySelector('.loading-progress');
|
|
let progress = 0;
|
|
const loadingInterval = setInterval(() => {
|
|
progress += 5;
|
|
if (progress > 100) progress = 100;
|
|
progressBar.style.width = `${progress}%`;
|
|
if (progress === 100) {
|
|
clearInterval(loadingInterval);
|
|
}
|
|
}, 150);
|
|
|
|
// Mindmap erstellen und initialisieren
|
|
window.mindmap = new MindMapVisualization('#mindmap-container', options);
|
|
|
|
// API-Aufruf, um echte Daten zu laden
|
|
fetch('/api/mindmap')
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
throw new Error('Netzwerkantwort war nicht ok');
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
// Ladebalken auf 100% setzen
|
|
progressBar.style.width = '100%';
|
|
|
|
// Lade-Overlay nach kurzer Verzögerung ausblenden
|
|
setTimeout(() => {
|
|
const loadingOverlay = document.querySelector('.mindmap-loading');
|
|
if (loadingOverlay) {
|
|
loadingOverlay.style.opacity = '0';
|
|
setTimeout(() => {
|
|
loadingOverlay.style.display = 'none';
|
|
}, 500);
|
|
}
|
|
}, 500);
|
|
|
|
console.log('Mindmap-Daten geladen:', data);
|
|
})
|
|
.catch(error => {
|
|
console.error('Fehler beim Laden der Mindmap-Daten:', error);
|
|
// Fehlerbehandlung: Zeige trotzdem die Standard-Mindmap
|
|
progressBar.style.width = '100%';
|
|
setTimeout(() => {
|
|
const loadingOverlay = document.querySelector('.mindmap-loading');
|
|
if (loadingOverlay) {
|
|
loadingOverlay.style.opacity = '0';
|
|
setTimeout(() => {
|
|
loadingOverlay.style.display = 'none';
|
|
}, 500);
|
|
}
|
|
}, 500);
|
|
});
|
|
|
|
// UI Event-Handler einrichten
|
|
document.getElementById('zoom-in-btn').addEventListener('click', function() {
|
|
if (window.mindmap) {
|
|
const transform = d3.zoomTransform(window.mindmap.svg.node());
|
|
window.mindmap.svg.call(
|
|
d3.zoom().transform,
|
|
d3.zoomIdentity.translate(transform.x, transform.y).scale(transform.k * 1.3)
|
|
);
|
|
}
|
|
});
|
|
|
|
document.getElementById('zoom-out-btn').addEventListener('click', function() {
|
|
if (window.mindmap) {
|
|
const transform = d3.zoomTransform(window.mindmap.svg.node());
|
|
window.mindmap.svg.call(
|
|
d3.zoom().transform,
|
|
d3.zoomIdentity.translate(transform.x, transform.y).scale(transform.k / 1.3)
|
|
);
|
|
}
|
|
});
|
|
|
|
document.getElementById('center-btn').addEventListener('click', function() {
|
|
if (window.mindmap) {
|
|
window.mindmap.svg.call(
|
|
d3.zoom().transform,
|
|
d3.zoomIdentity
|
|
);
|
|
}
|
|
});
|
|
|
|
document.getElementById('add-thought-btn').addEventListener('click', function() {
|
|
if (window.mindmap && window.mindmap.selectedNode) {
|
|
const nodeId = window.mindmap.selectedNode.id;
|
|
const modal = document.getElementById('add-thought-modal');
|
|
if (modal) {
|
|
// Modal öffnen, wenn vorhanden
|
|
modal.classList.remove('hidden');
|
|
// Node-ID in ein verstecktes Feld setzen
|
|
const nodeIdField = document.getElementById('thought-node-id');
|
|
if (nodeIdField) nodeIdField.value = nodeId;
|
|
} else {
|
|
// Simpler Dialog, wenn kein Modal existiert
|
|
const thoughtText = prompt('Neuen Gedanken eingeben:');
|
|
if (thoughtText) {
|
|
// Gedanken über API hinzufügen
|
|
fetch(`/api/nodes/${nodeId}/thoughts`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
title: thoughtText,
|
|
content: thoughtText
|
|
})
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
// Erfolgsmeldung anzeigen
|
|
const notification = document.createElement('div');
|
|
notification.className = 'fixed top-4 right-4 bg-green-600 text-white p-4 rounded-lg shadow-lg z-50 animate-fade-in';
|
|
notification.innerHTML = `
|
|
<div class="flex items-center">
|
|
<i class="fas fa-check-circle mr-2"></i>
|
|
<p>Gedanke wurde erfolgreich hinzugefügt!</p>
|
|
</div>
|
|
`;
|
|
document.body.appendChild(notification);
|
|
|
|
// Notification nach 3 Sekunden ausblenden
|
|
setTimeout(() => {
|
|
notification.classList.add('animate-fade-out');
|
|
setTimeout(() => document.body.removeChild(notification), 500);
|
|
}, 3000);
|
|
|
|
// Aktualisiere den Gedankenzähler am Knoten, falls vorhanden
|
|
const nodeElement = document.getElementById(`node-${window.mindmap.selectedNode.id}`);
|
|
const countElement = nodeElement.querySelector('.thought-count');
|
|
if (countElement) {
|
|
const currentCount = parseInt(countElement.textContent);
|
|
countElement.textContent = (currentCount + 1).toString();
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Fehler beim Hinzufügen des Gedankens:', error);
|
|
alert('Fehler beim Hinzufügen des Gedankens.');
|
|
});
|
|
}
|
|
}
|
|
} else {
|
|
alert('Bitte wähle zuerst einen Knoten aus.');
|
|
}
|
|
});
|
|
|
|
document.getElementById('connect-btn').addEventListener('click', function() {
|
|
if (window.mindmap && window.mindmap.selectedNode) {
|
|
// Speichere den ersten ausgewählten Knoten
|
|
window.mindmap.sourceNode = window.mindmap.selectedNode;
|
|
|
|
// Visuelles Feedback für den Benutzer
|
|
const selectedCircle = d3.select(`#node-${window.mindmap.selectedNode.id} circle`);
|
|
selectedCircle.classed('connection-source', true);
|
|
|
|
// Benutzerfreundlichere Benachrichtigung mit Statusanzeige
|
|
const notification = document.createElement('div');
|
|
notification.id = 'connection-notification';
|
|
notification.className = 'fixed top-4 right-4 bg-purple-600 text-white p-4 rounded-lg shadow-lg z-50';
|
|
notification.innerHTML = `
|
|
<p class="font-bold mb-1">Verbindungsmodus aktiv</p>
|
|
<p class="text-sm">Wähle einen zweiten Knoten aus, um eine Verbindung herzustellen</p>
|
|
<button id="cancel-connection" class="mt-2 px-3 py-1 bg-purple-800 rounded hover:bg-purple-900 text-sm">Abbrechen</button>
|
|
`;
|
|
document.body.appendChild(notification);
|
|
|
|
// Abbrechen-Button-Funktionalität
|
|
document.getElementById('cancel-connection').addEventListener('click', function() {
|
|
window.mindmap.connectMode = false;
|
|
window.mindmap.sourceNode = null;
|
|
selectedCircle.classed('connection-source', false);
|
|
document.body.removeChild(notification);
|
|
});
|
|
|
|
// Aktiviere den Verbindungsmodus
|
|
window.mindmap.connectMode = true;
|
|
|
|
// Cursor-Stil ändern, um den Verbindungsmodus anzuzeigen
|
|
document.getElementById('mindmap-container').style.cursor = 'crosshair';
|
|
} else {
|
|
alert('Bitte wähle zuerst einen Knoten aus.');
|
|
}
|
|
});
|
|
|
|
// Formular zum Hinzufügen neuer Gedanken behandeln
|
|
const thoughtForm = document.getElementById('add-thought-form');
|
|
if (thoughtForm) {
|
|
thoughtForm.addEventListener('submit', function(event) {
|
|
event.preventDefault();
|
|
|
|
const nodeId = document.getElementById('thought-node-id').value;
|
|
const title = document.getElementById('thought-title').value;
|
|
const content = document.getElementById('thought-content').value;
|
|
|
|
if (!nodeId || !title || !content) {
|
|
alert('Bitte fülle alle Felder aus.');
|
|
return;
|
|
}
|
|
|
|
// Gedanken über API hinzufügen
|
|
fetch(`/api/nodes/${nodeId}/thoughts`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
title: title,
|
|
content: content,
|
|
branch: 'main' // Default-Zweig
|
|
})
|
|
})
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
throw new Error('Fehler beim Hinzufügen des Gedankens');
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
// Modal schließen
|
|
document.getElementById('add-thought-modal').classList.add('hidden');
|
|
|
|
// Formular zurücksetzen
|
|
thoughtForm.reset();
|
|
|
|
// Erfolgsmeldung anzeigen
|
|
alert('Gedanke wurde erfolgreich hinzugefügt!');
|
|
|
|
// Optional: Knoten in der Mindmap aktualisieren (z.B. Zähler erhöhen)
|
|
if (window.mindmap && window.mindmap.selectedNode) {
|
|
window.mindmap.selectedNode.thought_count += 1;
|
|
window.mindmap.updateNodeLabels();
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Fehler beim Speichern des Gedankens:', error);
|
|
alert('Fehler beim Speichern des Gedankens. Bitte versuche es erneut.');
|
|
});
|
|
});
|
|
}
|
|
|
|
// Fenstergrößen-Änderung behandeln
|
|
let resizeTimeout;
|
|
window.addEventListener('resize', function() {
|
|
clearTimeout(resizeTimeout);
|
|
resizeTimeout = setTimeout(function() {
|
|
if (window.mindmap) {
|
|
window.mindmap.width = mindmapContainer.clientWidth;
|
|
window.mindmap.height = mindmapContainer.clientHeight;
|
|
window.mindmap.svg
|
|
.attr('width', '100%')
|
|
.attr('height', window.mindmap.height)
|
|
.attr('viewBox', `0 0 ${window.mindmap.width} ${window.mindmap.height}`);
|
|
|
|
// Mittelpunkt-Kraft aktualisieren
|
|
window.mindmap.simulation.force('center',
|
|
d3.forceCenter(window.mindmap.width / 2, window.mindmap.height / 2)
|
|
);
|
|
|
|
// Simulation neu starten
|
|
window.mindmap.simulation.alpha(0.3).restart();
|
|
}
|
|
}, 250);
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %} |