Update requirements and enhance mindmap UI: Comment out Pillow dependency, add network background script, and implement new styles and animations for improved visual effects in the mindmap template.
This commit is contained in:
69
ANLEITUNG.md
Normal file
69
ANLEITUNG.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# Anleitung: Animierter Netzwerk-Hintergrund
|
||||||
|
|
||||||
|
Diese Anleitung erklärt, wie Sie ein Netzwerk-Bild als animierten Hintergrund für die gesamte Website einrichten können.
|
||||||
|
|
||||||
|
## Option 1: Manuelle Installation
|
||||||
|
|
||||||
|
1. Kopieren Sie das gewünschte Netzwerk-Bild (z.B. `d2efd014-1325-471f-b9a7-90d025eb81d6.png`) in die Datei `website/static/network-bg.jpg`.
|
||||||
|
|
||||||
|
Sie können dafür das beiliegende Batch-Skript verwenden:
|
||||||
|
```
|
||||||
|
copy-network-image.bat d2efd014-1325-471f-b9a7-90d025eb81d6.png
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Starten Sie den Flask-Server mit dem folgenden Befehl:
|
||||||
|
```
|
||||||
|
python start-flask-server.py
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Öffnen Sie die Website unter http://127.0.0.1:5000/
|
||||||
|
|
||||||
|
## Anpassung der Animation
|
||||||
|
|
||||||
|
Sie können die Animation des Netzwerk-Hintergrunds anpassen, indem Sie die Datei `website/static/network-background.js` bearbeiten. Hier sind die wichtigsten Parameter:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
let animationSpeed = 0.0005; // Geschwindigkeit der Rotation
|
||||||
|
let scaleSpeed = 0.0002; // Geschwindigkeit der Skalierung
|
||||||
|
let opacitySpeed = 0.0003; // Geschwindigkeit der Transparenzänderung
|
||||||
|
```
|
||||||
|
|
||||||
|
## Animation der Mindmap-Verbindungen
|
||||||
|
|
||||||
|
Die Verbindungen zwischen den Knoten in der Mindmap werden jetzt mit einer fließenden Animation dargestellt. Diese Animationen verbessern die Sichtbarkeit der Zusammenhänge und machen die Interaktion mit der Karte intuitiver.
|
||||||
|
|
||||||
|
### Funktionen:
|
||||||
|
|
||||||
|
1. **Animierte Linien**: Die Verbindungslinien zwischen den Knoten bewegen sich in einem fließenden Muster.
|
||||||
|
2. **Hervorhebung bei Hover**: Beim Überfahren eines Knotens oder einer Verbindung mit der Maus werden diese hervorgehoben.
|
||||||
|
3. **Kategorien-Beziehungen**: Die visuellen Verbindungen zwischen den Kategorien sind jetzt deutlicher erkennbar.
|
||||||
|
|
||||||
|
## Position des Auswahlfelds
|
||||||
|
|
||||||
|
Das Auswahlfeld auf der Karte wurde weiter nach links verschoben, sodass es vollständig sichtbar ist, wenn keine Auswahl getroffen wurde. Die Größe wurde ebenfalls angepasst, um die Lesbarkeit zu verbessern.
|
||||||
|
|
||||||
|
## Wiederherstellung des ursprünglichen Hintergrunds (optional)
|
||||||
|
|
||||||
|
Wenn Sie zum ursprünglichen Sternenhintergrund zurückkehren möchten, müssen Sie folgende Änderungen vornehmen:
|
||||||
|
|
||||||
|
1. Bearbeiten Sie die Datei `website/templates/base.html` und ersetzen Sie:
|
||||||
|
```html
|
||||||
|
<!-- Network Background Script -->
|
||||||
|
<script src="{{ url_for('static', filename='network-background.js') }}"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
mit:
|
||||||
|
```html
|
||||||
|
<!-- Three.js für den Sternenhintergrund -->
|
||||||
|
<script src="{{ url_for('static', filename='three.min.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='background.js') }}"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Bearbeiten Sie die Datei `website/templates/mindmap.html` und entfernen Sie die Zeile:
|
||||||
|
```html
|
||||||
|
<script src="{{ url_for('static', filename='network-animation.js') }}"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Entfernen Sie den CSS-Code für `#mindmap-container::before` und die anderen netzwerkspezifischen Stile aus der Datei `website/templates/mindmap.html`.
|
||||||
|
|
||||||
|
4. Starten Sie den Flask-Server neu, um die Änderungen zu übernehmen.
|
||||||
33
copy-network-image.bat
Normal file
33
copy-network-image.bat
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
@echo off
|
||||||
|
echo Copying network image to website/static/network-bg.jpg...
|
||||||
|
|
||||||
|
if not exist "website\static" (
|
||||||
|
echo Error: website/static directory does not exist.
|
||||||
|
echo Make sure you are running this script from the main project directory.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%~1"=="" (
|
||||||
|
echo Usage: copy-network-image.bat [path_to_image]
|
||||||
|
echo Example: copy-network-image.bat d2efd014-1325-471f-b9a7-90d025eb81d6.png
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
if not exist "%~1" (
|
||||||
|
echo Error: The specified image file "%~1" does not exist.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
copy /Y "%~1" "website\static\network-bg.jpg" > nul
|
||||||
|
|
||||||
|
if %errorlevel% equ 0 (
|
||||||
|
echo Success! The image has been copied to website/static/network-bg.jpg
|
||||||
|
echo Please restart the Flask server to see the changes.
|
||||||
|
) else (
|
||||||
|
echo Error: Failed to copy the image.
|
||||||
|
)
|
||||||
|
|
||||||
|
pause
|
||||||
BIN
website/__pycache__/app.cpython-313.pyc
Normal file
BIN
website/__pycache__/app.cpython-313.pyc
Normal file
Binary file not shown.
BIN
website/__pycache__/init_db.cpython-313.pyc
Normal file
BIN
website/__pycache__/init_db.cpython-313.pyc
Normal file
Binary file not shown.
@@ -9,6 +9,6 @@ openai==1.3.0
|
|||||||
requests==2.31.0
|
requests==2.31.0
|
||||||
flask-cors==4.0.0
|
flask-cors==4.0.0
|
||||||
gunicorn==21.2.0
|
gunicorn==21.2.0
|
||||||
pillow==10.0.1
|
#pillow==10.0.1
|
||||||
pytest==7.4.0
|
pytest==7.4.0
|
||||||
pytest-flask==1.2.0
|
pytest-flask==1.2.0
|
||||||
88
website/static/network-animation.js
Normal file
88
website/static/network-animation.js
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
// Network Animation Effect
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Check if we're on the mindmap page
|
||||||
|
const mindmapContainer = document.getElementById('mindmap-container');
|
||||||
|
if (!mindmapContainer) return;
|
||||||
|
|
||||||
|
// Add enhanced animations for links and nodes
|
||||||
|
setTimeout(function() {
|
||||||
|
// Get all SVG links (connections between nodes)
|
||||||
|
const links = document.querySelectorAll('.link');
|
||||||
|
const nodes = document.querySelectorAll('.node');
|
||||||
|
|
||||||
|
// Add animation to links
|
||||||
|
links.forEach(link => {
|
||||||
|
// Create random animation duration between 15 and 30 seconds
|
||||||
|
const duration = 15 + Math.random() * 15;
|
||||||
|
link.style.animation = `dash ${duration}s linear infinite`;
|
||||||
|
link.style.strokeDasharray = '5, 5';
|
||||||
|
|
||||||
|
// Add pulse effect on hover
|
||||||
|
link.addEventListener('mouseover', function() {
|
||||||
|
this.classList.add('highlighted');
|
||||||
|
this.style.animation = 'dash 5s linear infinite';
|
||||||
|
});
|
||||||
|
|
||||||
|
link.addEventListener('mouseout', function() {
|
||||||
|
this.classList.remove('highlighted');
|
||||||
|
this.style.animation = `dash ${duration}s linear infinite`;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add effects to nodes
|
||||||
|
nodes.forEach(node => {
|
||||||
|
node.addEventListener('mouseover', function() {
|
||||||
|
this.querySelector('circle').style.filter = 'drop-shadow(0 0 15px rgba(179, 143, 255, 0.8))';
|
||||||
|
|
||||||
|
// Highlight connected links
|
||||||
|
const nodeId = this.getAttribute('data-id') || this.id;
|
||||||
|
links.forEach(link => {
|
||||||
|
const source = link.getAttribute('data-source');
|
||||||
|
const target = link.getAttribute('data-target');
|
||||||
|
|
||||||
|
if (source === nodeId || target === nodeId) {
|
||||||
|
link.classList.add('highlighted');
|
||||||
|
link.style.animation = 'dash 5s linear infinite';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
node.addEventListener('mouseout', function() {
|
||||||
|
this.querySelector('circle').style.filter = 'drop-shadow(0 0 8px rgba(179, 143, 255, 0.5))';
|
||||||
|
|
||||||
|
// Remove highlight from connected links
|
||||||
|
const nodeId = this.getAttribute('data-id') || this.id;
|
||||||
|
links.forEach(link => {
|
||||||
|
const source = link.getAttribute('data-source');
|
||||||
|
const target = link.getAttribute('data-target');
|
||||||
|
|
||||||
|
if (source === nodeId || target === nodeId) {
|
||||||
|
link.classList.remove('highlighted');
|
||||||
|
const duration = 15 + Math.random() * 15;
|
||||||
|
link.style.animation = `dash ${duration}s linear infinite`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, 1000); // Wait for the mindmap to be fully loaded
|
||||||
|
|
||||||
|
// Add network background effect
|
||||||
|
const networkBackground = document.createElement('div');
|
||||||
|
networkBackground.className = 'network-background';
|
||||||
|
networkBackground.style.cssText = `
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-image: url('/static/network-bg.jpg');
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
opacity: 0.15;
|
||||||
|
z-index: -1;
|
||||||
|
pointer-events: none;
|
||||||
|
animation: pulse 10s ease-in-out infinite alternate;
|
||||||
|
`;
|
||||||
|
|
||||||
|
mindmapContainer.appendChild(networkBackground);
|
||||||
|
});
|
||||||
118
website/static/network-background.js
Normal file
118
website/static/network-background.js
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
// Animated Network Background
|
||||||
|
let canvas, ctx, networkImage;
|
||||||
|
let isImageLoaded = false;
|
||||||
|
let animationSpeed = 0.0005; // Speed of rotation
|
||||||
|
let scaleSpeed = 0.0002; // Speed of scaling
|
||||||
|
let opacitySpeed = 0.0003; // Speed of opacity change
|
||||||
|
let rotation = 0;
|
||||||
|
let scale = 1;
|
||||||
|
let opacity = 0.6;
|
||||||
|
let scaleDirection = 1;
|
||||||
|
let opacityDirection = 1;
|
||||||
|
|
||||||
|
// Initialize the canvas and load the image
|
||||||
|
function initNetworkBackground() {
|
||||||
|
// Create canvas element if it doesn't exist
|
||||||
|
if (!document.getElementById('network-background')) {
|
||||||
|
canvas = document.createElement('canvas');
|
||||||
|
canvas.id = 'network-background';
|
||||||
|
canvas.style.position = 'fixed';
|
||||||
|
canvas.style.top = '0';
|
||||||
|
canvas.style.left = '0';
|
||||||
|
canvas.style.width = '100%';
|
||||||
|
canvas.style.height = '100%';
|
||||||
|
canvas.style.zIndex = '-1';
|
||||||
|
document.body.appendChild(canvas);
|
||||||
|
} else {
|
||||||
|
canvas = document.getElementById('network-background');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set canvas size to window size
|
||||||
|
resizeCanvas();
|
||||||
|
|
||||||
|
// Get context
|
||||||
|
ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
// Load the network image
|
||||||
|
networkImage = new Image();
|
||||||
|
networkImage.src = '/static/network-bg.jpg';
|
||||||
|
networkImage.onload = function() {
|
||||||
|
isImageLoaded = true;
|
||||||
|
animate();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle window resize
|
||||||
|
window.addEventListener('resize', resizeCanvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize canvas to match window size
|
||||||
|
function resizeCanvas() {
|
||||||
|
if (canvas) {
|
||||||
|
canvas.width = window.innerWidth;
|
||||||
|
canvas.height = window.innerHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animation loop
|
||||||
|
function animate() {
|
||||||
|
if (!isImageLoaded) return;
|
||||||
|
|
||||||
|
// Clear canvas
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
// Update animation parameters
|
||||||
|
rotation += animationSpeed;
|
||||||
|
|
||||||
|
// Update scale with oscillation
|
||||||
|
scale += scaleSpeed * scaleDirection;
|
||||||
|
if (scale > 1.1) {
|
||||||
|
scaleDirection = -1;
|
||||||
|
} else if (scale < 0.9) {
|
||||||
|
scaleDirection = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update opacity with oscillation
|
||||||
|
opacity += opacitySpeed * opacityDirection;
|
||||||
|
if (opacity > 0.8) {
|
||||||
|
opacityDirection = -1;
|
||||||
|
} else if (opacity < 0.5) {
|
||||||
|
opacityDirection = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save context state
|
||||||
|
ctx.save();
|
||||||
|
|
||||||
|
// Move to center of canvas
|
||||||
|
ctx.translate(canvas.width / 2, canvas.height / 2);
|
||||||
|
|
||||||
|
// Rotate
|
||||||
|
ctx.rotate(rotation);
|
||||||
|
|
||||||
|
// Scale
|
||||||
|
ctx.scale(scale, scale);
|
||||||
|
|
||||||
|
// Set global opacity
|
||||||
|
ctx.globalAlpha = opacity;
|
||||||
|
|
||||||
|
// Draw image centered
|
||||||
|
ctx.drawImage(
|
||||||
|
networkImage,
|
||||||
|
-networkImage.width / 2,
|
||||||
|
-networkImage.height / 2,
|
||||||
|
networkImage.width,
|
||||||
|
networkImage.height
|
||||||
|
);
|
||||||
|
|
||||||
|
// Restore context state
|
||||||
|
ctx.restore();
|
||||||
|
|
||||||
|
// Request next frame
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize background when the DOM is loaded
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', initNetworkBackground);
|
||||||
|
} else {
|
||||||
|
initNetworkBackground();
|
||||||
|
}
|
||||||
2
website/static/network-bg.jpg
Normal file
2
website/static/network-bg.jpg
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/* This is a placeholder for the network background image.
|
||||||
|
The actual image (d2efd014-1325-471f-b9a7-90d025eb81d6.png) should be copied here. */
|
||||||
@@ -34,6 +34,9 @@
|
|||||||
<!-- Alpine.js -->
|
<!-- Alpine.js -->
|
||||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.12.3/dist/cdn.min.js"></script>
|
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.12.3/dist/cdn.min.js"></script>
|
||||||
|
|
||||||
|
<!-- Network Background Script -->
|
||||||
|
<script src="{{ url_for('static', filename='network-background.js') }}"></script>
|
||||||
|
|
||||||
<!-- Hauptmodul laden (als ES6 Modul) -->
|
<!-- Hauptmodul laden (als ES6 Modul) -->
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import MindMap from "{{ url_for('static', filename='js/main.js') }}";
|
import MindMap from "{{ url_for('static', filename='js/main.js') }}";
|
||||||
@@ -515,23 +518,23 @@
|
|||||||
<a href="{{ url_for('index') }}"
|
<a href="{{ url_for('index') }}"
|
||||||
style="display: flex; align-items: center; padding: 0.5rem 1rem; border-radius: 0.75rem; font-weight: 500; transition: all 0.25s ease; backdrop-filter: blur(15px); -webkit-backdrop-filter: blur(15px);"
|
style="display: flex; align-items: center; padding: 0.5rem 1rem; border-radius: 0.75rem; font-weight: 500; transition: all 0.25s ease; backdrop-filter: blur(15px); -webkit-backdrop-filter: blur(15px);"
|
||||||
x-bind:style="darkMode
|
x-bind:style="darkMode
|
||||||
? '{{ request.endpoint == 'index' ? 'background: rgba(179, 143, 255, 0.3); color: white; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);' : 'background: transparent; color: rgba(255, 255, 255, 0.9);' }}'
|
? '{{ 'background: rgba(179, 143, 255, 0.3); color: white; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);' if request.endpoint == 'index' else 'background: transparent; color: rgba(255, 255, 255, 0.9);' }}'
|
||||||
: '{{ request.endpoint == 'index' ? 'background: rgba(179, 143, 255, 0.2); color: rgba(26, 32, 44, 1); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);' : 'background: transparent; color: rgba(26, 32, 44, 0.9);' }}'">
|
: '{{ 'background: rgba(179, 143, 255, 0.2); color: rgba(26, 32, 44, 1); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);' if request.endpoint == 'index' else 'background: transparent; color: rgba(26, 32, 44, 0.9);' }}'">
|
||||||
<i class="fa-solid fa-home" style="margin-right: 0.5rem;"></i>Start
|
<i class="fa-solid fa-home" style="margin-right: 0.5rem;"></i>Start
|
||||||
</a>
|
</a>
|
||||||
<a href="{{ url_for('mindmap') }}"
|
<a href="{{ url_for('mindmap') }}"
|
||||||
style="display: flex; align-items: center; padding: 0.5rem 1rem; border-radius: 0.75rem; font-weight: 500; transition: all 0.25s ease; backdrop-filter: blur(15px); -webkit-backdrop-filter: blur(15px);"
|
style="display: flex; align-items: center; padding: 0.5rem 1rem; border-radius: 0.75rem; font-weight: 500; transition: all 0.25s ease; backdrop-filter: blur(15px); -webkit-backdrop-filter: blur(15px);"
|
||||||
x-bind:style="darkMode
|
x-bind:style="darkMode
|
||||||
? '{{ request.endpoint == 'mindmap' ? 'background: rgba(179, 143, 255, 0.3); color: white; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);' : 'background: transparent; color: rgba(255, 255, 255, 0.9);' }}'
|
? '{{ 'background: rgba(179, 143, 255, 0.3); color: white; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);' if request.endpoint == 'mindmap' else 'background: transparent; color: rgba(255, 255, 255, 0.9);' }}'
|
||||||
: '{{ request.endpoint == 'mindmap' ? 'background: rgba(179, 143, 255, 0.2); color: rgba(26, 32, 44, 1); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);' : 'background: transparent; color: rgba(26, 32, 44, 0.9);' }}'">
|
: '{{ 'background: rgba(179, 143, 255, 0.2); color: rgba(26, 32, 44, 1); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);' if request.endpoint == 'mindmap' else 'background: transparent; color: rgba(26, 32, 44, 0.9);' }}'">
|
||||||
<i class="fa-solid fa-diagram-project" style="margin-right: 0.5rem;"></i>Mindmap
|
<i class="fa-solid fa-diagram-project" style="margin-right: 0.5rem;"></i>Mindmap
|
||||||
</a>
|
</a>
|
||||||
{% if current_user.is_authenticated %}
|
{% if current_user.is_authenticated %}
|
||||||
<a href="{{ url_for('profile') }}"
|
<a href="{{ url_for('profile') }}"
|
||||||
style="display: flex; align-items: center; padding: 0.5rem 1rem; border-radius: 0.75rem; font-weight: 500; transition: all 0.25s ease; backdrop-filter: blur(15px); -webkit-backdrop-filter: blur(15px);"
|
style="display: flex; align-items: center; padding: 0.5rem 1rem; border-radius: 0.75rem; font-weight: 500; transition: all 0.25s ease; backdrop-filter: blur(15px); -webkit-backdrop-filter: blur(15px);"
|
||||||
x-bind:style="darkMode
|
x-bind:style="darkMode
|
||||||
? '{{ request.endpoint == 'profile' ? 'background: rgba(179, 143, 255, 0.3); color: white; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);' : 'background: transparent; color: rgba(255, 255, 255, 0.9);' }}'
|
? '{{ 'background: rgba(179, 143, 255, 0.3); color: white; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);' if request.endpoint == 'profile' else 'background: transparent; color: rgba(255, 255, 255, 0.9);' }}'
|
||||||
: '{{ request.endpoint == 'profile' ? 'background: rgba(179, 143, 255, 0.2); color: rgba(26, 32, 44, 1); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);' : 'background: transparent; color: rgba(26, 32, 44, 0.9);' }}'">
|
: '{{ 'background: rgba(179, 143, 255, 0.2); color: rgba(26, 32, 44, 1); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);' if request.endpoint == 'profile' else 'background: transparent; color: rgba(26, 32, 44, 0.9);' }}'">
|
||||||
<i class="fa-solid fa-user" style="margin-right: 0.5rem;"></i>Profil
|
<i class="fa-solid fa-user" style="margin-right: 0.5rem;"></i>Profil
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -647,23 +650,23 @@
|
|||||||
<a href="{{ url_for('index') }}"
|
<a href="{{ url_for('index') }}"
|
||||||
style="display: flex; align-items: center; padding: 0.75rem 1rem; margin-bottom: 0.5rem; border-radius: 0.75rem; font-weight: 500; text-decoration: none; transition: all 0.25s ease;"
|
style="display: flex; align-items: center; padding: 0.75rem 1rem; margin-bottom: 0.5rem; border-radius: 0.75rem; font-weight: 500; text-decoration: none; transition: all 0.25s ease;"
|
||||||
x-bind:style="darkMode
|
x-bind:style="darkMode
|
||||||
? '{{ request.endpoint == 'index' ? 'background: rgba(179, 143, 255, 0.3); color: white;' : 'background: transparent; color: rgba(255, 255, 255, 0.9);' }}'
|
? '{{ 'background: rgba(179, 143, 255, 0.3); color: white;' if request.endpoint == 'index' else 'background: transparent; color: rgba(255, 255, 255, 0.9);' }}'
|
||||||
: '{{ request.endpoint == 'index' ? 'background: rgba(179, 143, 255, 0.2); color: rgba(26, 32, 44, 1);' : 'background: transparent; color: rgba(26, 32, 44, 0.9);' }}'">
|
: '{{ 'background: rgba(179, 143, 255, 0.2); color: rgba(26, 32, 44, 1);' if request.endpoint == 'index' else 'background: transparent; color: rgba(26, 32, 44, 0.9);' }}'">
|
||||||
<i class="fa-solid fa-home" style="margin-right: 0.5rem; width: 1.25rem;"></i>Start
|
<i class="fa-solid fa-home" style="margin-right: 0.5rem; width: 1.25rem;"></i>Start
|
||||||
</a>
|
</a>
|
||||||
<a href="{{ url_for('mindmap') }}"
|
<a href="{{ url_for('mindmap') }}"
|
||||||
style="display: flex; align-items: center; padding: 0.75rem 1rem; margin-bottom: 0.5rem; border-radius: 0.75rem; font-weight: 500; text-decoration: none; transition: all 0.25s ease;"
|
style="display: flex; align-items: center; padding: 0.75rem 1rem; margin-bottom: 0.5rem; border-radius: 0.75rem; font-weight: 500; text-decoration: none; transition: all 0.25s ease;"
|
||||||
x-bind:style="darkMode
|
x-bind:style="darkMode
|
||||||
? '{{ request.endpoint == 'mindmap' ? 'background: rgba(179, 143, 255, 0.3); color: white;' : 'background: transparent; color: rgba(255, 255, 255, 0.9);' }}'
|
? '{{ 'background: rgba(179, 143, 255, 0.3); color: white;' if request.endpoint == 'mindmap' else 'background: transparent; color: rgba(255, 255, 255, 0.9);' }}'
|
||||||
: '{{ request.endpoint == 'mindmap' ? 'background: rgba(179, 143, 255, 0.2); color: rgba(26, 32, 44, 1);' : 'background: transparent; color: rgba(26, 32, 44, 0.9);' }}'">
|
: '{{ 'background: rgba(179, 143, 255, 0.2); color: rgba(26, 32, 44, 1);' if request.endpoint == 'mindmap' else 'background: transparent; color: rgba(26, 32, 44, 0.9);' }}'">
|
||||||
<i class="fa-solid fa-diagram-project" style="margin-right: 0.5rem; width: 1.25rem;"></i>Mindmap
|
<i class="fa-solid fa-diagram-project" style="margin-right: 0.5rem; width: 1.25rem;"></i>Mindmap
|
||||||
</a>
|
</a>
|
||||||
{% if current_user.is_authenticated %}
|
{% if current_user.is_authenticated %}
|
||||||
<a href="{{ url_for('profile') }}"
|
<a href="{{ url_for('profile') }}"
|
||||||
style="display: flex; align-items: center; padding: 0.75rem 1rem; margin-bottom: 0.5rem; border-radius: 0.75rem; font-weight: 500; text-decoration: none; transition: all 0.25s ease;"
|
style="display: flex; align-items: center; padding: 0.75rem 1rem; margin-bottom: 0.5rem; border-radius: 0.75rem; font-weight: 500; text-decoration: none; transition: all 0.25s ease;"
|
||||||
x-bind:style="darkMode
|
x-bind:style="darkMode
|
||||||
? '{{ request.endpoint == 'profile' ? 'background: rgba(179, 143, 255, 0.3); color: white;' : 'background: transparent; color: rgba(255, 255, 255, 0.9);' }}'
|
? '{{ 'background: rgba(179, 143, 255, 0.3); color: white;' if request.endpoint == 'profile' else 'background: transparent; color: rgba(255, 255, 255, 0.9);' }}'
|
||||||
: '{{ request.endpoint == 'profile' ? 'background: rgba(179, 143, 255, 0.2); color: rgba(26, 32, 44, 1);' : 'background: transparent; color: rgba(26, 32, 44, 0.9);' }}'">
|
: '{{ 'background: rgba(179, 143, 255, 0.2); color: rgba(26, 32, 44, 1);' if request.endpoint == 'profile' else 'background: transparent; color: rgba(26, 32, 44, 0.9);' }}'">
|
||||||
<i class="fa-solid fa-user" style="margin-right: 0.5rem; width: 1.25rem;"></i>Profil
|
<i class="fa-solid fa-user" style="margin-right: 0.5rem; width: 1.25rem;"></i>Profil
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -22,6 +22,52 @@
|
|||||||
width: 100%;
|
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 */
|
/* Verbesserte Glasmorphismus-Stile für Karten */
|
||||||
.glass-card, .mindmap-card {
|
.glass-card, .mindmap-card {
|
||||||
background: rgba(24, 28, 45, 0.75);
|
background: rgba(24, 28, 45, 0.75);
|
||||||
@@ -91,6 +137,44 @@
|
|||||||
letter-spacing: 0.3px;
|
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 */
|
||||||
.mindmap-header {
|
.mindmap-header {
|
||||||
background: rgba(20, 24, 42, 0.85);
|
background: rgba(20, 24, 42, 0.85);
|
||||||
@@ -401,50 +485,100 @@
|
|||||||
html.light .mindmap-card-footer {
|
html.light .mindmap-card-footer {
|
||||||
border-color: rgba(0, 0, 0, 0.06);
|
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: url('/static/network-bg.jpg');
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
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>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container mx-auto px-4 py-12">
|
<div class="relative overflow-hidden">
|
||||||
<!-- Feature-Karten-Container -->
|
<!-- Neuronales Netz / Universum Hintergrund -->
|
||||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-8 mb-12">
|
<div class="neural-universe-bg"></div>
|
||||||
<!-- Feature-Karte 1: Visualisiere Wissen -->
|
|
||||||
<div class="feature-card">
|
|
||||||
<div class="feature-card-icon">
|
|
||||||
<i class="fas fa-brain"></i>
|
|
||||||
</div>
|
|
||||||
<h3>Visualisiere Wissen</h3>
|
|
||||||
<p>Sieh Wissen als vernetztes System, entdecke Zusammenhänge und erkenne überraschende Verbindungen zwischen verschiedenen Themengebieten.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Feature-Karte 2: Teile Gedanken -->
|
|
||||||
<div class="feature-card">
|
|
||||||
<div class="feature-card-icon">
|
|
||||||
<i class="fas fa-lightbulb"></i>
|
|
||||||
</div>
|
|
||||||
<h3>Teile Gedanken</h3>
|
|
||||||
<p>Füge deine eigenen Ideen und Perspektiven hinzu. Erstelle Verbindungen zu vorhandenen Gedanken und bereichere die wachsende Wissensbasis.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Feature-Karte 3: Community -->
|
|
||||||
<div class="feature-card">
|
|
||||||
<div class="feature-card-icon">
|
|
||||||
<i class="fas fa-users"></i>
|
|
||||||
</div>
|
|
||||||
<h3>Community</h3>
|
|
||||||
<p>Sei Teil einer Gemeinschaft, die gemeinsam ein verteiltes Wissensarchiv aufbaut und sich in thematisch fokussierten Bereichen austauscht.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<div class="container mx-auto px-4 py-8">
|
||||||
<!-- Mindmap-Visualisierung Header -->
|
<!-- Mindmap-Visualisierung Header -->
|
||||||
<div class="mindmap-header p-6 mb-6">
|
<div class="mindmap-header p-6 mb-6">
|
||||||
<h2 class="text-3xl font-bold mb-2 gradient-text">Wissenslandschaft erkunden</h2>
|
<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>
|
<p class="text-gray-300 mb-0">Interagiere mit der Mindmap, um Verbindungen zu entdecken und neue Ideen hinzuzufügen</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Mindmap-Container -->
|
<!-- Mindmap-Container - Jetzt größer -->
|
||||||
<div class="glass-card overflow-hidden mb-12">
|
<div class="glass-card overflow-hidden mb-12">
|
||||||
<div id="mindmap-container" class="relative" style="height: 70vh; min-height: 500px;">
|
<div id="mindmap-container" class="relative" style="height: 80vh; min-height: 700px;">
|
||||||
<!-- Lade-Overlay -->
|
<!-- Lade-Overlay -->
|
||||||
<div class="mindmap-loading absolute inset-0 flex items-center justify-center z-10" style="background: rgba(14, 18, 32, 0.7); backdrop-filter: blur(5px);">
|
<div class="mindmap-loading absolute inset-0 flex items-center justify-center z-10" style="background: rgba(14, 18, 32, 0.7); backdrop-filter: blur(5px);">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
@@ -481,6 +615,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Visuelle Trennung -->
|
||||||
|
<div class="visual-divider"></div>
|
||||||
|
|
||||||
<!-- Unterer Bereich: KI-Assistenz, Suche und Lernpfade -->
|
<!-- Unterer Bereich: KI-Assistenz, Suche und Lernpfade -->
|
||||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||||
<!-- KI-Assistenz -->
|
<!-- KI-Assistenz -->
|
||||||
@@ -511,6 +648,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block extra_js %}
|
{% block extra_js %}
|
||||||
@@ -523,9 +661,73 @@
|
|||||||
<script src="{{ url_for('static', filename='d3-extensions.js') }}"></script>
|
<script src="{{ url_for('static', filename='d3-extensions.js') }}"></script>
|
||||||
<!-- Mindmap JS -->
|
<!-- Mindmap JS -->
|
||||||
<script src="{{ url_for('static', filename='mindmap.js') }}"></script>
|
<script src="{{ url_for('static', filename='mindmap.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='network-animation.js') }}"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
// Dynamische Neuronen-Netz-Animation im Hintergrund
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Animation des neuronalen Netzwerks hinzufügen
|
||||||
|
const neuralBg = document.querySelector('.neural-universe-bg');
|
||||||
|
|
||||||
|
// Neuronenpunkte erstellen
|
||||||
|
const neuronCount = 100;
|
||||||
|
for (let i = 0; i < neuronCount; i++) {
|
||||||
|
const neuron = document.createElement('div');
|
||||||
|
neuron.className = 'neuron-point';
|
||||||
|
|
||||||
|
// Zufällige Position
|
||||||
|
const posX = Math.random() * 100;
|
||||||
|
const posY = Math.random() * 100;
|
||||||
|
const size = Math.random() * 3 + 1;
|
||||||
|
const animDuration = Math.random() * 50 + 20;
|
||||||
|
|
||||||
|
// Styling mit Glasmorphismus
|
||||||
|
neuron.style.cssText = `
|
||||||
|
position: absolute;
|
||||||
|
left: ${posX}%;
|
||||||
|
top: ${posY}%;
|
||||||
|
width: ${size}px;
|
||||||
|
height: ${size}px;
|
||||||
|
background: rgba(255, 255, 255, ${Math.random() * 0.3 + 0.1});
|
||||||
|
border-radius: 50%;
|
||||||
|
filter: blur(${Math.random() * 1}px);
|
||||||
|
box-shadow: 0 0 ${Math.random() * 10 + 5}px rgba(179, 143, 255, 0.5);
|
||||||
|
animation: pulse ${animDuration}s infinite alternate ease-in-out;
|
||||||
|
`;
|
||||||
|
|
||||||
|
neuralBg.appendChild(neuron);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verbindungen zwischen Neuronen erstellen
|
||||||
|
const connectionCount = 40;
|
||||||
|
for (let i = 0; i < connectionCount; i++) {
|
||||||
|
const connection = document.createElement('div');
|
||||||
|
connection.className = 'neuron-connection';
|
||||||
|
|
||||||
|
// Zufällige Position und Rotation für Verbindungen
|
||||||
|
const posX = Math.random() * 100;
|
||||||
|
const posY = Math.random() * 100;
|
||||||
|
const width = Math.random() * 150 + 50;
|
||||||
|
const height = Math.random() * 1 + 0.5;
|
||||||
|
const rotation = Math.random() * 360;
|
||||||
|
const opacity = Math.random() * 0.2 + 0.05;
|
||||||
|
const animDuration = Math.random() * 20 + 10;
|
||||||
|
|
||||||
|
connection.style.cssText = `
|
||||||
|
position: absolute;
|
||||||
|
left: ${posX}%;
|
||||||
|
top: ${posY}%;
|
||||||
|
width: ${width}px;
|
||||||
|
height: ${height}px;
|
||||||
|
background: linear-gradient(90deg, transparent, rgba(179, 143, 255, ${opacity}), transparent);
|
||||||
|
transform: rotate(${rotation}deg);
|
||||||
|
animation: flash ${animDuration}s infinite alternate ease-in-out;
|
||||||
|
opacity: ${opacity};
|
||||||
|
`;
|
||||||
|
|
||||||
|
neuralBg.appendChild(connection);
|
||||||
|
}
|
||||||
|
|
||||||
// Initialisiere die Mindmap-Visualisierung
|
// Initialisiere die Mindmap-Visualisierung
|
||||||
const mindmapContainer = document.getElementById('mindmap-container');
|
const mindmapContainer = document.getElementById('mindmap-container');
|
||||||
const containerWidth = mindmapContainer.clientWidth;
|
const containerWidth = mindmapContainer.clientWidth;
|
||||||
@@ -658,5 +860,21 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Animationen für die Hintergrundeffekte
|
||||||
|
document.head.insertAdjacentHTML('beforeend', `
|
||||||
|
<style>
|
||||||
|
@keyframes pulse {
|
||||||
|
0% { transform: scale(1); opacity: 0.5; }
|
||||||
|
100% { transform: scale(1.5); opacity: 0.2; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes flash {
|
||||||
|
0% { opacity: 0.02; }
|
||||||
|
50% { opacity: 0.2; }
|
||||||
|
100% { opacity: 0.08; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
`);
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user