Compare commits
42 Commits
tills-bran
...
cb3bfe0e6a
| Author | SHA1 | Date | |
|---|---|---|---|
| cb3bfe0e6a | |||
| fd63810845 | |||
| 883973fe7b | |||
| 027e632856 | |||
| 406289e54f | |||
| 71b33e6cec | |||
| c74d3164bb | |||
| 4982cddeef | |||
| 631619ccb4 | |||
| f9881b678d | |||
| 259ce3cf69 | |||
| 9f4743eaea | |||
| de0f837cfd | |||
| 1c49ddfb19 | |||
| 46c16e5f01 | |||
| 84667bca00 | |||
| 779449559d | |||
| 721a10e861 | |||
| a431873ca2 | |||
| e4ab1e1bb5 | |||
| f69356473b | |||
| 38ac13e87c | |||
| 0afb8cb6e2 | |||
| 5d282d2108 | |||
| 4aba72efa2 | |||
| 89476d5353 | |||
| 0f7a33340a | |||
| 73501e7cda | |||
| 9f8eba6736 | |||
| b6bf9f387d | |||
| d9fe1f8efc | |||
| fd7bc59851 | |||
| 55f1f87509 | |||
| 03f8761312 | |||
| 506748fda7 | |||
| 6d069f68cd | |||
| 4310239a7a | |||
| e9fe907af0 | |||
| 0c69d9aba3 | |||
| 6da85cdece | |||
| a073b09115 | |||
| f1f4870989 |
33
Dockerfile
Normal file
33
Dockerfile
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# Dockerfile
|
||||||
|
FROM python:3.11-slim
|
||||||
|
|
||||||
|
# Arbeitsverzeichnis in Container
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Systemabhängigkeiten installieren und Verzeichnisse anlegen
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y --no-install-recommends gcc && \
|
||||||
|
rm -rf /var/lib/apt/lists/* && \
|
||||||
|
mkdir -p /app/database
|
||||||
|
|
||||||
|
# pip auf den neuesten Stand bringen und Requirements installieren
|
||||||
|
COPY requirements.txt ./
|
||||||
|
RUN pip install --upgrade pip && \
|
||||||
|
pip install --no-cache-dir -U -r requirements.txt
|
||||||
|
|
||||||
|
# Anwendungscode kopieren
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Berechtigungen für database-Ordner
|
||||||
|
RUN chmod -R 777 /app/database
|
||||||
|
|
||||||
|
# Exponiere Port 5000 für Flask
|
||||||
|
EXPOSE 5000
|
||||||
|
|
||||||
|
# Setze Umgebungsvariablen
|
||||||
|
ENV FLASK_APP=app.py
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE=1
|
||||||
|
|
||||||
|
# Startkommando mit spezifischen Flags für Produktion
|
||||||
|
CMD ["python", "app.py"]
|
||||||
Binary file not shown.
Binary file not shown.
BIN
backup/archiv_0.1.zip
Normal file
BIN
backup/archiv_0.1.zip
Normal file
Binary file not shown.
17
docker-compose.yml
Normal file
17
docker-compose.yml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
version: '3.9'
|
||||||
|
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
build: .
|
||||||
|
image: systades_app:latest
|
||||||
|
container_name: systades_app
|
||||||
|
restart: always
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
ports:
|
||||||
|
- "5000:5000"
|
||||||
|
volumes:
|
||||||
|
- ./database:/app/database
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
db_data:
|
||||||
@@ -2,10 +2,12 @@
|
|||||||
# Kopiere diese Datei zu .env und passe die Werte an
|
# Kopiere diese Datei zu .env und passe die Werte an
|
||||||
|
|
||||||
# Flask
|
# Flask
|
||||||
SECRET_KEY=dein-geheimer-schluessel-hier
|
FLASK_APP=app.py
|
||||||
|
FLASK_ENV=development
|
||||||
|
SECRET_KEY=your-secret-key-replace-in-production
|
||||||
|
|
||||||
# OpenAI API
|
# OpenAI API
|
||||||
OPENAI_API_KEY=sk-dein-openai-api-schluessel-hier
|
OPENAI_API_KEY=your-openai-api-key
|
||||||
|
|
||||||
# Datenbank
|
# Datenbank
|
||||||
# Bei Bedarf kann hier eine andere Datenbank-URL angegeben werden
|
# Bei Bedarf kann hier eine andere Datenbank-URL angegeben werden
|
||||||
|
|||||||
53
start.sh
Normal file
53
start.sh
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/env powershell
|
||||||
|
# Windows PowerShell-Version des Start-Skripts
|
||||||
|
# Datum: 01.05.2025
|
||||||
|
|
||||||
|
# Docker-Status prüfen
|
||||||
|
Write-Host "Prüfe Docker-Status..." -ForegroundColor Cyan
|
||||||
|
try {
|
||||||
|
$status = docker ps -q
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Host "Docker ist nicht gestartet. Bitte starten Sie Docker Desktop." -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Write-Host "Docker ist nicht verfügbar. Bitte installieren Sie Docker Desktop und starten Sie es." -ForegroundColor Red
|
||||||
|
Write-Host $_.Exception.Message
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Alte Container stoppen und entfernen
|
||||||
|
$containerExists = docker ps -a --filter "name=systades_app" -q
|
||||||
|
if ($containerExists) {
|
||||||
|
Write-Host "Stoppe und entferne alten Container..." -ForegroundColor Yellow
|
||||||
|
docker rm -f systades_app
|
||||||
|
}
|
||||||
|
|
||||||
|
# Alte Images löschen
|
||||||
|
Write-Host "Entferne altes Image..." -ForegroundColor Yellow
|
||||||
|
docker rmi -f systades_app:latest
|
||||||
|
|
||||||
|
# Stelle sicher, dass das Datenbankverzeichnis existiert
|
||||||
|
if (-not (Test-Path "database")) {
|
||||||
|
New-Item -Path "database" -ItemType Directory -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
# Docker-Compose Setup neu bauen
|
||||||
|
Write-Host "Baue Container neu..." -ForegroundColor Green
|
||||||
|
docker-compose build --no-cache
|
||||||
|
|
||||||
|
# Docker-Compose neu starten
|
||||||
|
Write-Host "Starte Container..." -ForegroundColor Green
|
||||||
|
docker-compose up -d --force-recreate
|
||||||
|
|
||||||
|
# Warte kurz und prüfe, ob der Container läuft
|
||||||
|
Write-Host "Prüfe Container-Status..." -ForegroundColor Cyan
|
||||||
|
Start-Sleep -Seconds 3
|
||||||
|
docker ps | Select-String "systades_app"
|
||||||
|
|
||||||
|
# Ausgabe
|
||||||
|
Write-Host "`nSystemstatus:" -ForegroundColor Cyan
|
||||||
|
Write-Host "----------------------------------------"
|
||||||
|
Write-Host "Systades-Anwendung ist jetzt unter http://localhost:5000 erreichbar." -ForegroundColor Green
|
||||||
|
Write-Host "Container-Logs können mit 'docker logs -f systades_app' angezeigt werden." -ForegroundColor Green
|
||||||
|
Write-Host "----------------------------------------"
|
||||||
1
static/541AABD7-4E37-44ED-B491-1459C8C19699.PNG
Normal file
1
static/541AABD7-4E37-44ED-B491-1459C8C19699.PNG
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -40,8 +40,8 @@
|
|||||||
--light-bg: #f9fafb;
|
--light-bg: #f9fafb;
|
||||||
--light-text: #1e293b;
|
--light-text: #1e293b;
|
||||||
--light-heading: #0f172a;
|
--light-heading: #0f172a;
|
||||||
--light-primary: #3b82f6;
|
--light-primary: #7c3aed;
|
||||||
--light-primary-hover: #4f46e5;
|
--light-primary-hover: #6d28d9;
|
||||||
--light-secondary: #6b7280;
|
--light-secondary: #6b7280;
|
||||||
--light-border: #e5e7eb;
|
--light-border: #e5e7eb;
|
||||||
--light-card-bg: rgba(255, 255, 255, 0.92);
|
--light-card-bg: rgba(255, 255, 255, 0.92);
|
||||||
@@ -524,3 +524,231 @@ body:not(.dark) .navbar {
|
|||||||
box-shadow: var(--light-shadow);
|
box-shadow: var(--light-shadow);
|
||||||
border-bottom: 1px solid var(--light-border);
|
border-bottom: 1px solid var(--light-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Erweiterte Light-Mode-spezifische Stile */
|
||||||
|
body:not(.dark) .glass-effect {
|
||||||
|
background-color: rgba(255, 255, 255, 0.7);
|
||||||
|
backdrop-filter: blur(12px);
|
||||||
|
-webkit-backdrop-filter: blur(12px);
|
||||||
|
border: 1px solid rgba(209, 213, 219, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .card {
|
||||||
|
background-color: rgba(255, 255, 255, 0.85);
|
||||||
|
border: 1px solid var(--light-border);
|
||||||
|
box-shadow: var(--light-shadow);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .card:hover {
|
||||||
|
box-shadow: 0 8px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light Mode Buttons */
|
||||||
|
body:not(.dark) .btn-primary {
|
||||||
|
background-color: var(--light-primary);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .btn-primary:hover {
|
||||||
|
background-color: var(--light-primary-hover);
|
||||||
|
box-shadow: 0 4px 12px rgba(124, 58, 237, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .btn-secondary {
|
||||||
|
background-color: #f3f4f6;
|
||||||
|
color: var(--light-text);
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .btn-secondary:hover {
|
||||||
|
background-color: #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .btn-outline {
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--light-primary);
|
||||||
|
border: 1px solid var(--light-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .btn-outline:hover {
|
||||||
|
background-color: rgba(124, 58, 237, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light Mode Formulare */
|
||||||
|
body:not(.dark) input,
|
||||||
|
body:not(.dark) select,
|
||||||
|
body:not(.dark) textarea {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #d1d5db;
|
||||||
|
color: #1f2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) input:focus,
|
||||||
|
body:not(.dark) select:focus,
|
||||||
|
body:not(.dark) textarea:focus {
|
||||||
|
border-color: var(--light-primary);
|
||||||
|
box-shadow: 0 0 0 2px rgba(124, 58, 237, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light Mode Navigation */
|
||||||
|
body:not(.dark) .sidebar {
|
||||||
|
background-color: white;
|
||||||
|
border-right: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .sidebar-link {
|
||||||
|
color: #4b5563;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .sidebar-link:hover {
|
||||||
|
background-color: #f3f4f6;
|
||||||
|
color: var(--light-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .sidebar-link.active {
|
||||||
|
background-color: rgba(124, 58, 237, 0.08);
|
||||||
|
color: var(--light-primary);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light Mode Tabellen */
|
||||||
|
body:not(.dark) table {
|
||||||
|
border-color: #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) th {
|
||||||
|
background-color: #f9fafb;
|
||||||
|
color: #111827;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) tr:nth-child(even) {
|
||||||
|
background-color: #f9fafb;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) tr:hover {
|
||||||
|
background-color: #f3f4f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light Mode Icons */
|
||||||
|
body:not(.dark) .icon {
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .icon-primary {
|
||||||
|
color: var(--light-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light Mode Alerts/Benachrichtigungen */
|
||||||
|
body:not(.dark) .alert-info {
|
||||||
|
background-color: #eff6ff;
|
||||||
|
border-left: 4px solid #3b82f6;
|
||||||
|
color: #1e40af;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .alert-success {
|
||||||
|
background-color: #ecfdf5;
|
||||||
|
border-left: 4px solid #10b981;
|
||||||
|
color: #065f46;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .alert-warning {
|
||||||
|
background-color: #fffbeb;
|
||||||
|
border-left: 4px solid #f59e0b;
|
||||||
|
color: #92400e;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .alert-error {
|
||||||
|
background-color: #fef2f2;
|
||||||
|
border-left: 4px solid #ef4444;
|
||||||
|
color: #b91c1c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light Mode Badge */
|
||||||
|
body:not(.dark) .badge {
|
||||||
|
background-color: #e5e7eb;
|
||||||
|
color: #374151;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .badge-primary {
|
||||||
|
background-color: rgba(124, 58, 237, 0.1);
|
||||||
|
color: var(--light-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light Mode Mindmap spezifisch */
|
||||||
|
body:not(.dark) #cy {
|
||||||
|
background-color: rgba(255, 255, 255, 0.7);
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .node {
|
||||||
|
border: 2px solid white;
|
||||||
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .node:hover,
|
||||||
|
body:not(.dark) .node.selected {
|
||||||
|
box-shadow: 0 0 0 2px rgba(124, 58, 237, 0.5), 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .edge {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .edge:hover,
|
||||||
|
body:not(.dark) .edge.selected {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer im Light Mode */
|
||||||
|
body:not(.dark) footer {
|
||||||
|
background-color: rgba(249, 250, 251, 0.7);
|
||||||
|
border-top: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Alpine.js Transitions im Light Mode */
|
||||||
|
body:not(.dark) [x-cloak] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Suchfeldstyling im Light Mode */
|
||||||
|
body:not(.dark) .search-container input {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #d1d5db;
|
||||||
|
color: #1f2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .search-container input:focus {
|
||||||
|
border-color: var(--light-primary);
|
||||||
|
box-shadow: 0 0 0 2px rgba(124, 58, 237, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .search-results {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .search-result-item:hover {
|
||||||
|
background-color: #f3f4f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Profile und Benutzermenü im Light Mode */
|
||||||
|
body:not(.dark) .avatar {
|
||||||
|
border: 2px solid white;
|
||||||
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .user-dropdown {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .user-dropdown-item:hover {
|
||||||
|
background-color: #f3f4f6;
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -69,8 +69,8 @@
|
|||||||
<link href="{{ url_for('static', filename='fonts/inter.css') }}" rel="stylesheet">
|
<link href="{{ url_for('static', filename='fonts/inter.css') }}" rel="stylesheet">
|
||||||
<link href="{{ url_for('static', filename='fonts/jetbrains-mono.css') }}" rel="stylesheet">
|
<link href="{{ url_for('static', filename='fonts/jetbrains-mono.css') }}" rel="stylesheet">
|
||||||
|
|
||||||
<!-- Icons - Self-hosted Font Awesome -->
|
<!-- Font Awesome vom CDN -->
|
||||||
<link href="{{ url_for('static', filename='css/all.min.css') }}" rel="stylesheet">
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" rel="stylesheet">
|
||||||
|
|
||||||
<!-- Assistent CSS -->
|
<!-- Assistent CSS -->
|
||||||
<link href="{{ url_for('static', filename='css/assistant.css') }}" rel="stylesheet">
|
<link href="{{ url_for('static', filename='css/assistant.css') }}" rel="stylesheet">
|
||||||
@@ -111,7 +111,7 @@
|
|||||||
<!-- Seitenspezifische Styles -->
|
<!-- Seitenspezifische Styles -->
|
||||||
{% block extra_css %}{% endblock %}
|
{% block extra_css %}{% endblock %}
|
||||||
|
|
||||||
<!-- Custom dark mode styles -->
|
<!-- Custom dark/light mode styles -->
|
||||||
<!-- ► ► Farb‑Token strikt getrennt ◄ ◄ -->
|
<!-- ► ► Farb‑Token strikt getrennt ◄ ◄ -->
|
||||||
<style>
|
<style>
|
||||||
/* Light‑Mode */
|
/* Light‑Mode */
|
||||||
@@ -149,6 +149,39 @@
|
|||||||
.glass-navbar { @apply glass-morphism border backdrop-blur-xl; }
|
.glass-navbar { @apply glass-morphism border backdrop-blur-xl; }
|
||||||
.light .glass-navbar { background-color:rgba(255,255,255,.8); border-color:rgba(0,0,0,.05); }
|
.light .glass-navbar { background-color:rgba(255,255,255,.8); border-color:rgba(0,0,0,.05); }
|
||||||
.dark .glass-navbar { background-color:rgba(10,14,25,.8); border-color:rgba(255,255,255,.05); }
|
.dark .glass-navbar { background-color:rgba(10,14,25,.8); border-color:rgba(255,255,255,.05); }
|
||||||
|
|
||||||
|
/* Light-Mode spezifische Stile */
|
||||||
|
body:not(.dark) {
|
||||||
|
background-color: var(--bg-primary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link-light {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link-light:hover {
|
||||||
|
color: var(--text-primary);
|
||||||
|
background-color: rgba(126, 34, 206, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link-light-active {
|
||||||
|
color: var(--accent-primary);
|
||||||
|
background-color: rgba(126, 34, 206, 0.15);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Kartendesign im Light-Mode */
|
||||||
|
body:not(.dark) .card {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .card:hover {
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body data-page="{{ request.endpoint }}" class="relative overflow-x-hidden dark bg-gray-900 text-white" x-data="{
|
<body data-page="{{ request.endpoint }}" class="relative overflow-x-hidden dark bg-gray-900 text-white" x-data="{
|
||||||
@@ -158,6 +191,17 @@
|
|||||||
showSettingsModal: false,
|
showSettingsModal: false,
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
this.initDarkMode();
|
||||||
|
},
|
||||||
|
|
||||||
|
initDarkMode() {
|
||||||
|
// Lade zuerst den Wert aus dem localStorage (client-seitig)
|
||||||
|
const storedMode = localStorage.getItem('colorMode');
|
||||||
|
if (storedMode) {
|
||||||
|
this.darkMode = storedMode === 'dark';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dann hole die Server-Einstellung, die Vorrang hat
|
||||||
this.fetchDarkModeFromSession();
|
this.fetchDarkModeFromSession();
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -167,7 +211,7 @@
|
|||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
this.darkMode = data.darkMode === 'true';
|
this.darkMode = data.darkMode === 'true';
|
||||||
document.querySelector('html').classList.toggle('dark', this.darkMode);
|
this.applyDarkMode();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
@@ -175,9 +219,15 @@
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
applyDarkMode() {
|
||||||
|
document.querySelector('html').classList.toggle('dark', this.darkMode);
|
||||||
|
document.querySelector('body').classList.toggle('dark', this.darkMode);
|
||||||
|
localStorage.setItem('colorMode', this.darkMode ? 'dark' : 'light');
|
||||||
|
},
|
||||||
|
|
||||||
toggleDarkMode() {
|
toggleDarkMode() {
|
||||||
this.darkMode = !this.darkMode;
|
this.darkMode = !this.darkMode;
|
||||||
document.querySelector('html').classList.toggle('dark', this.darkMode);
|
this.applyDarkMode();
|
||||||
|
|
||||||
fetch('/api/set_dark_mode', {
|
fetch('/api/set_dark_mode', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -189,7 +239,6 @@
|
|||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
localStorage.setItem('darkMode', this.darkMode ? 'dark' : 'light');
|
|
||||||
document.dispatchEvent(new CustomEvent('darkModeToggled', {
|
document.dispatchEvent(new CustomEvent('darkModeToggled', {
|
||||||
detail: { isDark: this.darkMode }
|
detail: { isDark: this.darkMode }
|
||||||
}));
|
}));
|
||||||
@@ -578,37 +627,42 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Dark/Light-Mode persistent und robust -->
|
<!-- Dark/Light-Mode vereinheitlicht -->
|
||||||
<script>
|
<script>
|
||||||
(function() {
|
// Globaler Zugriff für externe Skripte
|
||||||
function applyMode(mode) {
|
window.MindMap = window.MindMap || {};
|
||||||
if (mode === 'dark') {
|
|
||||||
document.documentElement.classList.add('dark');
|
window.MindMap.toggleDarkMode = function() {
|
||||||
localStorage.setItem('colorMode', 'dark');
|
// Alpine.js-Instanz benutzen, wenn verfügbar
|
||||||
|
const appEl = document.querySelector('body');
|
||||||
|
if (appEl && appEl.__x) {
|
||||||
|
appEl.__x.$data.toggleDarkMode();
|
||||||
} else {
|
} else {
|
||||||
document.documentElement.classList.remove('dark');
|
// Fallback: Nur classList und localStorage
|
||||||
localStorage.setItem('colorMode', 'light');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Beim Laden: Präferenz aus localStorage oder System übernehmen
|
|
||||||
const stored = localStorage.getItem('colorMode');
|
|
||||||
if (stored === 'dark' || stored === 'light') {
|
|
||||||
applyMode(stored);
|
|
||||||
} else {
|
|
||||||
// Systempräferenz als Fallback
|
|
||||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
||||||
applyMode(prefersDark ? 'dark' : 'light');
|
|
||||||
}
|
|
||||||
// Umschalter für alle Mode-Toggles
|
|
||||||
window.toggleColorMode = function() {
|
|
||||||
const isDark = document.documentElement.classList.contains('dark');
|
const isDark = document.documentElement.classList.contains('dark');
|
||||||
applyMode(isDark ? 'light' : 'dark');
|
document.documentElement.classList.toggle('dark', !isDark);
|
||||||
|
document.body.classList.toggle('dark', !isDark);
|
||||||
|
localStorage.setItem('colorMode', !isDark ? 'dark' : 'light');
|
||||||
|
|
||||||
|
// Server aktualisieren
|
||||||
|
fetch('/api/set_dark_mode', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ darkMode: !isDark })
|
||||||
|
}).catch(console.error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
// Optional: globales Event für andere Skripte
|
|
||||||
window.addEventListener('storage', function(e) {
|
// Fallback für Browser-Präferenz, falls keine Einstellung geladen werden konnte
|
||||||
if (e.key === 'colorMode') applyMode(e.newValue);
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
if (!document.body.classList.contains('dark') && !document.documentElement.classList.contains('dark')) {
|
||||||
|
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
|
if (prefersDark) {
|
||||||
|
document.documentElement.classList.add('dark');
|
||||||
|
document.body.classList.add('dark');
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
})();
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -1,115 +1,53 @@
|
|||||||
<!DOCTYPE html>
|
{% extends "base.html" %}
|
||||||
<html lang="de">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Interaktive Mindmap</title>
|
|
||||||
|
|
||||||
<!-- Cytoscape.js -->
|
{% block title %}Mindmap{% endblock %}
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.26.0/cytoscape.min.js"></script>
|
|
||||||
|
|
||||||
<!-- Socket.IO -->
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.7.2/socket.io.min.js"></script>
|
|
||||||
|
|
||||||
<!-- Feather Icons (optional) -->
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
|
|
||||||
|
|
||||||
|
{% block extra_css %}
|
||||||
<style>
|
<style>
|
||||||
* {
|
/* Spezifische Stile für die Mindmap-Seite */
|
||||||
box-sizing: border-box;
|
#cy {
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
||||||
background-color: #f9fafb;
|
|
||||||
color: #111827;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100vh;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 600px;
|
||||||
|
background-color: var(--bg-secondary);
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.mindmap-container {
|
||||||
background-color: #1f2937;
|
position: relative;
|
||||||
color: white;
|
border-radius: 10px;
|
||||||
padding: 1rem;
|
overflow: hidden;
|
||||||
display: flex;
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
justify-content: space-between;
|
transition: all 0.3s ease;
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.header h1 {
|
.dark .mindmap-container {
|
||||||
font-size: 1.5rem;
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
|
||||||
font-weight: 500;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar {
|
.mindmap-toolbar {
|
||||||
background-color: #f3f4f6;
|
|
||||||
padding: 0.75rem;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
border-bottom: 1px solid #e5e7eb;
|
flex-wrap: wrap;
|
||||||
|
padding: 0.75rem;
|
||||||
|
background-color: var(--bg-secondary);
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) .mindmap-toolbar {
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
background-color: #3b82f6;
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background-color 0.2s;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
|
||||||
|
|
||||||
.btn:hover {
|
|
||||||
background-color: #2563eb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-secondary {
|
|
||||||
background-color: #6b7280;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-secondary:hover {
|
|
||||||
background-color: #4b5563;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-danger {
|
|
||||||
background-color: #ef4444;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-danger:hover {
|
|
||||||
background-color: #dc2626;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-container {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
margin-left: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-input {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 300px;
|
|
||||||
padding: 0.5rem;
|
|
||||||
border: 1px solid #d1d5db;
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
}
|
border-radius: 0.375rem;
|
||||||
|
transition: all 0.3s ease;
|
||||||
#cy {
|
|
||||||
flex: 1;
|
|
||||||
width: 100%;
|
|
||||||
position: relative;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.category-filters {
|
.category-filters {
|
||||||
@@ -117,8 +55,8 @@
|
|||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
padding: 0.75rem;
|
padding: 0.75rem;
|
||||||
background-color: #ffffff;
|
background-color: var(--bg-secondary);
|
||||||
border-bottom: 1px solid #e5e7eb;
|
transition: background-color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.category-filter {
|
.category-filter {
|
||||||
@@ -139,96 +77,189 @@
|
|||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
/* Kontextmenü */
|
||||||
background-color: #f3f4f6;
|
|
||||||
padding: 0.75rem;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
color: #6b7280;
|
|
||||||
border-top: 1px solid #e5e7eb;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Kontextmenü Styling */
|
|
||||||
#context-menu {
|
#context-menu {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background-color: white;
|
border-radius: 0.375rem;
|
||||||
border: 1px solid #e5e7eb;
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark #context-menu {
|
||||||
|
background-color: #232837;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) #context-menu {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#context-menu .menu-item {
|
#context-menu .menu-item {
|
||||||
padding: 0.5rem 1rem;
|
padding: 0.5rem 1rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
#context-menu .menu-item:hover {
|
.dark #context-menu .menu-item:hover {
|
||||||
background-color: #f3f4f6;
|
background-color: rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.dark) #context-menu .menu-item:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Zusätzliches Layout */
|
||||||
|
.mindmap-section {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.mindmap-section {
|
||||||
|
grid-template-columns: 2fr 1fr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
{% endblock %}
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<header class="header">
|
|
||||||
<h1>Interaktive Mindmap</h1>
|
|
||||||
<div class="search-container">
|
|
||||||
<input type="text" id="search-mindmap" class="search-input" placeholder="Suchen...">
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="toolbar">
|
{% block content %}
|
||||||
<button id="addNode" class="btn">
|
<div class="container mx-auto px-4 py-8">
|
||||||
<i data-feather="plus-circle"></i>
|
<div class="flex flex-col lg:flex-row gap-8">
|
||||||
Knoten hinzufügen
|
<!-- Hauptinhalt -->
|
||||||
|
<div class="w-full lg:w-3/4">
|
||||||
|
<!-- Mindmap-Titelbereich -->
|
||||||
|
<div class="mb-6">
|
||||||
|
<h1 class="text-3xl font-bold mb-2 mystical-glow"
|
||||||
|
x-bind:class="darkMode ? 'text-white' : 'text-gray-800'">
|
||||||
|
Wissenslandkarte
|
||||||
|
</h1>
|
||||||
|
<p class="opacity-80 text-lg"
|
||||||
|
x-bind:class="darkMode ? 'text-gray-300' : 'text-gray-600'">
|
||||||
|
Visualisiere die Verbindungen zwischen Gedanken und Konzepten
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Mindmap-Container -->
|
||||||
|
<div class="mindmap-container">
|
||||||
|
<!-- Toolbar -->
|
||||||
|
<div class="mindmap-toolbar">
|
||||||
|
<button id="fit-btn" class="mindmap-action-btn">
|
||||||
|
<i class="fa-solid fa-expand"></i>
|
||||||
|
<span>Ansicht anpassen</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="addEdge" class="btn">
|
<button id="reset-btn" class="mindmap-action-btn">
|
||||||
<i data-feather="git-branch"></i>
|
<i class="fa-solid fa-undo"></i>
|
||||||
Verbindung erstellen
|
<span>Zurücksetzen</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="editNode" class="btn btn-secondary">
|
<button id="toggle-labels-btn" class="mindmap-action-btn">
|
||||||
<i data-feather="edit-2"></i>
|
<i class="fa-solid fa-tags"></i>
|
||||||
Knoten bearbeiten
|
<span>Labels ein/aus</span>
|
||||||
</button>
|
|
||||||
<button id="deleteNode" class="btn btn-danger">
|
|
||||||
<i data-feather="trash-2"></i>
|
|
||||||
Knoten löschen
|
|
||||||
</button>
|
|
||||||
<button id="deleteEdge" class="btn btn-danger">
|
|
||||||
<i data-feather="scissors"></i>
|
|
||||||
Verbindung löschen
|
|
||||||
</button>
|
|
||||||
<button id="reLayout" class="btn btn-secondary">
|
|
||||||
<i data-feather="refresh-cw"></i>
|
|
||||||
Layout neu anordnen
|
|
||||||
</button>
|
|
||||||
<button id="exportMindmap" class="btn btn-secondary">
|
|
||||||
<i data-feather="download"></i>
|
|
||||||
Exportieren
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="category-filters" class="category-filters">
|
<!-- Hauptvisualisierung -->
|
||||||
<!-- Wird dynamisch befüllt -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="cy"></div>
|
<div id="cy"></div>
|
||||||
|
|
||||||
<footer class="footer">
|
<!-- Info-Panel -->
|
||||||
Mindmap-Anwendung © 2023
|
<div id="node-info-panel" class="mindmap-info-panel">
|
||||||
</footer>
|
<h4 class="info-panel-title">Knoteninfo</h4>
|
||||||
|
<p id="node-description" class="info-panel-description">Wählen Sie einen Knoten aus...</p>
|
||||||
|
|
||||||
|
<div class="node-navigation">
|
||||||
|
<h5 class="node-navigation-title">Verknüpfte Knoten</h5>
|
||||||
|
<div id="connected-nodes" class="node-links">
|
||||||
|
<!-- Wird dynamisch befüllt -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Unsere Mindmap JS -->
|
<!-- Seitenleiste -->
|
||||||
<script src="{{ url_for('static', filename='js/mindmap.js') }}"></script>
|
<div class="w-full lg:w-1/4 space-y-6">
|
||||||
|
<!-- Nutzlänge -->
|
||||||
|
<div class="p-5 rounded-lg overflow-hidden border transition-colors duration-300"
|
||||||
|
x-bind:class="darkMode ? 'bg-slate-800/40 border-slate-700/50' : 'bg-white border-slate-200'">
|
||||||
|
<h3 class="text-xl font-semibold mb-3"
|
||||||
|
x-bind:class="darkMode ? 'text-white' : 'text-gray-800'">
|
||||||
|
<i class="fa-solid fa-circle-info text-purple-400 mr-2"></i>Über die Mindmap
|
||||||
|
</h3>
|
||||||
|
<div x-bind:class="darkMode ? 'text-gray-300' : 'text-gray-600'">
|
||||||
|
<p class="mb-2">Die interaktive Wissenslandkarte zeigt Verbindungen zwischen verschiedenen Gedanken und Konzepten.</p>
|
||||||
|
<p class="mb-2">Sie können:</p>
|
||||||
|
<ul class="list-disc pl-5 space-y-1 text-sm">
|
||||||
|
<li>Knoten auswählen, um Details zu sehen</li>
|
||||||
|
<li>Zoomen (Mausrad oder Pinch-Geste)</li>
|
||||||
|
<li>Die Karte verschieben (Drag & Drop)</li>
|
||||||
|
<li>Die Toolbar nutzen für weitere Aktionen</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Icons initialisieren -->
|
<!-- Kategorienlegende -->
|
||||||
|
<div class="p-5 rounded-lg overflow-hidden border transition-colors duration-300"
|
||||||
|
x-bind:class="darkMode ? 'bg-slate-800/40 border-slate-700/50' : 'bg-white border-slate-200'">
|
||||||
|
<h3 class="text-xl font-semibold mb-3"
|
||||||
|
x-bind:class="darkMode ? 'text-white' : 'text-gray-800'">
|
||||||
|
<i class="fa-solid fa-palette text-purple-400 mr-2"></i>Kategorien
|
||||||
|
</h3>
|
||||||
|
<div id="category-legend" class="space-y-2 text-sm"
|
||||||
|
x-bind:class="darkMode ? 'text-gray-300' : 'text-gray-600'">
|
||||||
|
<!-- Wird dynamisch befüllt -->
|
||||||
|
<div class="flex items-center"><span class="w-3 h-3 rounded-full bg-purple-500 mr-2"></span> Philosophie</div>
|
||||||
|
<div class="flex items-center"><span class="w-3 h-3 rounded-full bg-green-500 mr-2"></span> Wissenschaft</div>
|
||||||
|
<div class="flex items-center"><span class="w-3 h-3 rounded-full bg-orange-500 mr-2"></span> Technologie</div>
|
||||||
|
<div class="flex items-center"><span class="w-3 h-3 rounded-full bg-pink-500 mr-2"></span> Künste</div>
|
||||||
|
<div class="flex items-center"><span class="w-3 h-3 rounded-full bg-blue-500 mr-2"></span> Psychologie</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Meine Mindmaps -->
|
||||||
|
{% if current_user.is_authenticated %}
|
||||||
|
<div class="p-5 rounded-lg overflow-hidden border transition-colors duration-300"
|
||||||
|
x-bind:class="darkMode ? 'bg-slate-800/40 border-slate-700/50' : 'bg-white border-slate-200'">
|
||||||
|
<h3 class="text-xl font-semibold mb-3"
|
||||||
|
x-bind:class="darkMode ? 'text-white' : 'text-gray-800'">
|
||||||
|
<i class="fa-solid fa-map text-purple-400 mr-2"></i>Meine Mindmaps
|
||||||
|
</h3>
|
||||||
|
<div class="mb-3">
|
||||||
|
<a href="{{ url_for('create_mindmap') }}" class="w-full inline-block py-2 px-4 bg-purple-600 hover:bg-purple-700 text-white rounded-lg text-center text-sm font-medium transition-colors">
|
||||||
|
<i class="fa-solid fa-plus mr-1"></i> Neue Mindmap erstellen
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-2 max-h-60 overflow-y-auto"
|
||||||
|
x-bind:class="darkMode ? 'text-gray-300' : 'text-gray-600'">
|
||||||
|
{% if user_mindmaps %}
|
||||||
|
{% for mindmap in user_mindmaps %}
|
||||||
|
<a href="{{ url_for('user_mindmap', mindmap_id=mindmap.id) }}" class="block p-2 hover:bg-purple-500/20 rounded-lg transition-colors">
|
||||||
|
<div class="text-sm font-medium">{{ mindmap.name }}</div>
|
||||||
|
<div class="text-xs opacity-70">{{ mindmap.nodes|length }} Knoten</div>
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
<p class="text-sm italic">Sie haben noch keine eigenen Mindmaps erstellt.</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_js %}
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
// Sobald die Seite und die Scripte geladen sind, initialisiere die Mindmap
|
||||||
if (typeof feather !== 'undefined') {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
feather.replace();
|
if (window.MindMap && window.MindMap.pageInitializers && window.MindMap.pageInitializers.mindmap) {
|
||||||
|
window.MindMap.pageInitializers.mindmap();
|
||||||
|
} else {
|
||||||
|
console.error('Mindmap-Initialisierung konnte nicht gefunden werden!');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
{% endblock %}
|
||||||
</html>
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,58 +0,0 @@
|
|||||||
@echo off
|
|
||||||
echo Mindmap Projekt - Windows Setup
|
|
||||||
echo ==============================
|
|
||||||
echo.
|
|
||||||
|
|
||||||
REM Prüfen, ob Python installiert ist
|
|
||||||
python --version >nul 2>&1
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo Python ist nicht installiert oder nicht im PATH.
|
|
||||||
echo Bitte installiere Python 3.11 von https://www.python.org/downloads/
|
|
||||||
echo und stelle sicher, dass "Add Python to PATH" während der Installation aktiviert ist.
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
echo Erstelle virtuelle Umgebung...
|
|
||||||
python -m venv venv
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo Fehler beim Erstellen der virtuellen Umgebung.
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
echo Aktiviere virtuelle Umgebung...
|
|
||||||
call venv\Scripts\activate.bat
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo Fehler beim Aktivieren der virtuellen Umgebung.
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
echo Aktualisiere pip...
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo Warnung: Pip konnte nicht aktualisiert werden. Fahre trotzdem fort.
|
|
||||||
)
|
|
||||||
|
|
||||||
echo Installiere Abhängigkeiten...
|
|
||||||
pip install -r requirements.txt
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo Fehler beim Installieren der Abhängigkeiten.
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo Setup abgeschlossen!
|
|
||||||
echo.
|
|
||||||
echo Zum Starten des Servers:
|
|
||||||
echo 1. Führe "venv\Scripts\activate.bat" aus
|
|
||||||
echo 2. Führe "python TOOLS.py db:rebuild" aus (Nur beim ersten Mal oder zum Zurücksetzen der Datenbank)
|
|
||||||
echo 3. Führe "python TOOLS.py user:admin" aus (Erstellt einen Admin-Benutzer: admin/admin)
|
|
||||||
echo 4. Führe "python TOOLS.py server:run" aus
|
|
||||||
echo.
|
|
||||||
echo Die Anwendung ist dann unter http://localhost:5000 erreichbar.
|
|
||||||
echo.
|
|
||||||
|
|
||||||
pause
|
|
||||||
Reference in New Issue
Block a user