Compare commits
2 Commits
till-v2
...
013bf76446
| Author | SHA1 | Date | |
|---|---|---|---|
| 013bf76446 | |||
| 808a3c7bbe |
10
Dockerfile
Normal file
10
Dockerfile
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
FROM python:3.9-slim-buster
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY requirements.txt requirements.txt
|
||||||
|
RUN pip install -r requirements.txt
|
||||||
|
|
||||||
|
COPY website .
|
||||||
|
|
||||||
|
CMD ["python", "app.py"]
|
||||||
180
README.md
180
README.md
@@ -1,134 +1,94 @@
|
|||||||
# MindMapProjekt - Roadmap
|
# MindMap Wissensnetzwerk
|
||||||
|
|
||||||
## Projektübersicht
|
Eine interaktive Plattform zum Visualisieren, Erforschen und Teilen von Wissen mit integriertem ChatGPT-Assistenten.
|
||||||
Das MindMapProjekt ist eine interaktive Plattform zum Visualisieren, Erforschen und Teilen von Wissen. Das Projekt wird umfassend überarbeitet, um ein modernes, benutzerfreundliches Design und erweiterte Funktionalitäten zu bieten.
|
|
||||||
|
|
||||||
## Technischer Stack
|
## Features
|
||||||
- **Backend**: Python/Flask
|
|
||||||
- **Frontend**:
|
|
||||||
- Tailwind CSS für moderne UI
|
|
||||||
- SVG-Bibliotheken für Visualisierungen (D3.js)
|
|
||||||
- JavaScript/Alpine.js für interaktive Komponenten
|
|
||||||
- **Datenbank**: SQLite mit SQLAlchemy
|
|
||||||
- **KI-Integration**: OpenAI API für intelligente Assistenz
|
|
||||||
|
|
||||||
## Installation und Verwendung
|
- Interaktive Mindmap zur Visualisierung von Wissensverbindungen
|
||||||
|
- Gedanken mit verschiedenen Beziehungstypen verknüpfen
|
||||||
|
- Suchfunktion für Gedanken und Verbindungen
|
||||||
|
- Bewertungssystem für Gedanken
|
||||||
|
- Dark/Light Mode
|
||||||
|
- **Integrierter KI-Assistent** mit OpenAI GPT-Integration
|
||||||
|
|
||||||
### Installation
|
## Installation
|
||||||
1. Repository klonen
|
|
||||||
2. Virtuelle Umgebung erstellen: `python -m venv venv`
|
|
||||||
3. Virtuelle Umgebung aktivieren:
|
|
||||||
- Windows: `venv\Scripts\activate`
|
|
||||||
- Unix/MacOS: `source venv/bin/activate`
|
|
||||||
4. Abhängigkeiten installieren: `pip install -r requirements.txt`
|
|
||||||
5. Datenbank initialisieren: `python TOOLS.py db:rebuild`
|
|
||||||
6. Admin-Benutzer erstellen: `python TOOLS.py user:admin`
|
|
||||||
7. Server starten: `python TOOLS.py server:run`
|
|
||||||
|
|
||||||
### Standardbenutzer
|
### Einfache Installation
|
||||||
- **Admin-Benutzer**: Username: `admin` / Passwort: `admin`
|
|
||||||
- **Testbenutzer**: Username: `user` / Passwort: `user`
|
|
||||||
|
|
||||||
### Verwaltungswerkzeuge mit TOOLS.py
|
Führe im übergeordneten Verzeichnis folgendes aus:
|
||||||
Das Projekt enthält ein zentrales Verwaltungsskript `TOOLS.py`, das verschiedene Hilfsfunktionen bietet:
|
|
||||||
|
|
||||||
#### Datenbankverwaltung
|
```
|
||||||
- `python TOOLS.py db:fix` - Reparieren der Datenbankstruktur
|
python setup.py
|
||||||
- `python TOOLS.py db:rebuild` - Datenbank neu aufbauen (löscht alle Daten!)
|
```
|
||||||
- `python TOOLS.py db:test` - Datenbankverbindung und Modelle testen
|
|
||||||
- `python TOOLS.py db:stats` - Datenbankstatistiken anzeigen
|
|
||||||
|
|
||||||
#### Benutzerverwaltung
|
Dies erstellt eine virtuelle Umgebung, installiert alle Abhängigkeiten und erstellt die CSS-Dateien mit Tailwind.
|
||||||
- `python TOOLS.py user:list` - Alle Benutzer anzeigen
|
|
||||||
- `python TOOLS.py user:create -u USERNAME -e EMAIL -p PASSWORD [-a]` - Neuen Benutzer erstellen
|
|
||||||
- `python TOOLS.py user:admin` - Admin-Benutzer erstellen (admin/admin)
|
|
||||||
- `python TOOLS.py user:reset-pw -u USERNAME -p NEWPASSWORD` - Benutzerpasswort zurücksetzen
|
|
||||||
- `python TOOLS.py user:delete -u USERNAME` - Benutzer löschen
|
|
||||||
|
|
||||||
#### Serververwaltung
|
### Manuelle Installation
|
||||||
- `python TOOLS.py server:run [--host HOST] [--port PORT] [--no-debug]` - Entwicklungsserver starten
|
|
||||||
|
|
||||||
Für detaillierte Hilfe: `python TOOLS.py -h`
|
1. Repository klonen:
|
||||||
|
```
|
||||||
|
git clone <repository-url>
|
||||||
|
```
|
||||||
|
|
||||||
## Roadmap der Überarbeitung
|
2. Python-Abhängigkeiten installieren:
|
||||||
|
```
|
||||||
|
cd website
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
### Phase 1: Grundlegende Infrastruktur ✅
|
3. Environment-Variablen konfigurieren:
|
||||||
- [x] Bestandsaufnahme des aktuellen Projekts
|
```
|
||||||
- [x] Erstellung der Roadmap
|
cp example.env .env
|
||||||
- [x] Aktualisierung der Abhängigkeiten
|
```
|
||||||
- [x] Integration von Tailwind CSS
|
Bearbeite die `.env`-Datei und füge deinen OpenAI API-Schlüssel ein.
|
||||||
- [x] Einrichtung der SVG-Bibliotheken (D3.js)
|
|
||||||
- [x] Favicon erstellen
|
|
||||||
- [x] Setup-Skript für einfache Installation
|
|
||||||
|
|
||||||
### Phase 2: Design-Überarbeitung 🔄
|
4. CSS mit Tailwind erstellen:
|
||||||
- [x] Implementierung des Dark Mode
|
```
|
||||||
- [x] Erstellung eines modernen, minimalistischen UI mit Tech-Ästhetik
|
python build_css.py
|
||||||
- [x] Responsive Design für alle Geräte
|
```
|
||||||
- [ ] Gestaltung der Landing Page mit großer Typografie
|
|
||||||
|
|
||||||
### Phase 3: Mindmap-Funktionalitäten 🔄
|
5. Datenbank initialisieren:
|
||||||
- [x] Verbesserte Visualisierung mit SVG und D3.js
|
```
|
||||||
- [x] Implementierung der Mouseover-Funktion
|
python init_db.py
|
||||||
- [x] Entwicklung der Suchfunktion für Knoten
|
```
|
||||||
- [ ] Tagging-System für Inhalte
|
|
||||||
- [ ] Quellenmanagement und -verlinkung
|
|
||||||
- [ ] Upload-Funktionalität an Knotenpunkten
|
|
||||||
|
|
||||||
### Phase 4: Kernseitenentwicklung
|
6. Anwendung starten:
|
||||||
- [ ] Überarbeitung der Startseite mit neuen Features
|
```
|
||||||
- [ ] Entwicklung der "Wer sind wir?"-Seite
|
python run.py
|
||||||
- [ ] Implementierung von Impressum und Datenschutzerklärung
|
```
|
||||||
- [ ] Erstellung der Kontaktseite mit FAQs
|
|
||||||
- [ ] Überarbeitung des Benutzerprofilbereichs
|
|
||||||
|
|
||||||
### Phase 5: Community-Features
|
## Entwicklung
|
||||||
- [ ] Entwicklung des Autorenbereichs
|
|
||||||
- [ ] Implementierung von Community-Bereichen für Themenbereiche
|
|
||||||
- [ ] Verbesserter Kommentarbereich
|
|
||||||
- [ ] Benutzerrechtemanagement
|
|
||||||
|
|
||||||
### Phase 6: KI-Integration
|
Für die Entwicklung mit automatischem CSS-Reload:
|
||||||
- [ ] Implementierung des Frage-Antwort-Systems
|
|
||||||
- [ ] KI-generierte Themeneinleitungen
|
|
||||||
- [ ] Intelligente Suchunterstützung
|
|
||||||
- [ ] Geführte Pfade durch Themenbereiche
|
|
||||||
- [ ] Vorgeschlagene Chat-Möglichkeiten
|
|
||||||
|
|
||||||
### Phase 7: Benutzerprofilfunktionen
|
```
|
||||||
- [ ] Speichern von Thematiken
|
python dev.py
|
||||||
- [ ] Persönliche Mindmap/Pinboard
|
```
|
||||||
- [ ] Beitragsmanagement
|
|
||||||
- [ ] Benutzerstatistiken und -aktivitäten
|
|
||||||
|
|
||||||
### Phase 8: Testing und Optimierung
|
Dieser Befehl startet sowohl den Flask-Server als auch den Tailwind CSS-Watcher, der CSS bei Änderungen automatisch neu generiert.
|
||||||
- [ ] Umfassende Tests aller Funktionen
|
|
||||||
- [ ] Performance-Optimierung
|
|
||||||
- [ ] SEO-Implementierung
|
|
||||||
- [ ] Barrierefreiheit prüfen und verbessern
|
|
||||||
|
|
||||||
### Phase 9: Dokumentation und Einführung
|
## Verwendung des KI-Assistenten
|
||||||
- [ ] Erstellung von Benutzeranleitungen
|
|
||||||
- [ ] Entwicklerdokumentation
|
|
||||||
- [ ] Administratorenhandbuch
|
|
||||||
- [ ] Guided Tour für neue Benutzer
|
|
||||||
|
|
||||||
## Aktueller Status
|
Der KI-Assistent ist über folgende Wege zugänglich:
|
||||||
- **Phase 1**: ✅ Abgeschlossen
|
|
||||||
- **Phase 2**: 🔄 In Bearbeitung (75% abgeschlossen)
|
|
||||||
- **Phase 3**: 🔄 In Bearbeitung (50% abgeschlossen)
|
|
||||||
|
|
||||||
## Aktuelle Fortschritte
|
1. **Schwebende Schaltfläche**: In der unteren rechten Ecke der Webseite ist eine Roboter-Schaltfläche, die den Assistenten öffnet.
|
||||||
- Grundlegende UI modernisiert mit Tailwind CSS und Dark Mode
|
2. **Navigation**: In der Hauptnavigation gibt es ebenfalls eine Schaltfläche mit Roboter-Symbol.
|
||||||
- Neues Favicon für bessere visuelle Identität erstellt
|
3. **Startseite**: Im "KI-Assistent"-Abschnitt auf der Startseite gibt es einen "KI-Chat starten"-Button.
|
||||||
- Setup-Prozess vereinfacht mit einem Shell-Skript
|
|
||||||
- Mindmap-Visualisierung komplett überarbeitet mit D3.js für eine interaktivere Erfahrung
|
|
||||||
- Responsive Design für optimale Darstellung auf allen Geräten
|
|
||||||
|
|
||||||
## Nächste Schritte
|
Der Assistent kann bei folgenden Aufgaben helfen:
|
||||||
- Fertigstellung der Landing Page
|
|
||||||
- Erstellung der "Wer sind wir?"-Seite
|
|
||||||
- Implementierung des Tagging-Systems für Gedanken
|
|
||||||
- Verbesserung der Gedankenansicht im Mindmap-Bereich
|
|
||||||
|
|
||||||
*Zuletzt aktualisiert: 01.06.2024*
|
- Erklärung von Themen und Konzepten
|
||||||
|
- Suche nach Verbindungen zwischen Gedanken
|
||||||
|
- Beantwortung von Fragen zur Plattform
|
||||||
|
- Vorschläge für neue Gedankenverbindungen
|
||||||
|
|
||||||
|
## Technologie-Stack
|
||||||
|
|
||||||
|
- **Backend**: Flask, SQLAlchemy
|
||||||
|
- **Frontend**: HTML, CSS, JavaScript, Tailwind CSS (ohne npm), Alpine.js
|
||||||
|
- **KI**: OpenAI GPT API
|
||||||
|
- **Datenbank**: SQLite (Standard), kann auf andere Datenbanken umgestellt werden
|
||||||
|
|
||||||
|
## Konfiguration
|
||||||
|
|
||||||
|
Die Anwendung kann über Umgebungsvariablen konfiguriert werden. Siehe `example.env` für verfügbare Optionen.
|
||||||
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
|
||||||
86
deploy.py
Executable file
86
deploy.py
Executable file
@@ -0,0 +1,86 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import shutil
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Deploy the website on a server"""
|
||||||
|
print("Deploying the website on the server...")
|
||||||
|
|
||||||
|
# Get the directory where deploy.py is located (project root)
|
||||||
|
project_root = Path(__file__).resolve().parent
|
||||||
|
website_dir = project_root / "website"
|
||||||
|
|
||||||
|
# Check if virtual environment exists, create if not
|
||||||
|
venv_dir = project_root / "venv"
|
||||||
|
if not venv_dir.exists():
|
||||||
|
print("Creating virtual environment...")
|
||||||
|
subprocess.run([sys.executable, "-m", "venv", str(venv_dir)], check=True)
|
||||||
|
|
||||||
|
# Determine Python and pip paths based on OS
|
||||||
|
if os.name == 'nt': # Windows
|
||||||
|
python = venv_dir / "Scripts" / "python"
|
||||||
|
pip = venv_dir / "Scripts" / "pip"
|
||||||
|
else: # Unix-like
|
||||||
|
python = venv_dir / "bin" / "python"
|
||||||
|
pip = venv_dir / "bin" / "pip"
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
print("Installing dependencies...")
|
||||||
|
subprocess.run([str(pip), "install", "-r", str(website_dir / "requirements.txt")], check=True)
|
||||||
|
subprocess.run([str(pip), "install", "gunicorn"], check=True)
|
||||||
|
|
||||||
|
# Build CSS
|
||||||
|
print("Building CSS with Tailwind...")
|
||||||
|
subprocess.run([str(python), str(website_dir / "build_css.py")], check=True)
|
||||||
|
|
||||||
|
# Create a systemd service file
|
||||||
|
service_file = """[Unit]
|
||||||
|
Description=MindMap Wissensnetzwerk
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
User=www-data
|
||||||
|
WorkingDirectory={website_dir}
|
||||||
|
Environment="PATH={venv_bin}"
|
||||||
|
ExecStart={gunicorn} --workers 3 --bind 0.0.0.0:5000 --log-level info 'run:app'
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
""".format(
|
||||||
|
website_dir=website_dir,
|
||||||
|
venv_bin=venv_dir / "bin",
|
||||||
|
gunicorn=venv_dir / "bin" / "gunicorn"
|
||||||
|
)
|
||||||
|
|
||||||
|
service_path = project_root / "mindmap.service"
|
||||||
|
with open(service_path, 'w') as f:
|
||||||
|
f.write(service_file)
|
||||||
|
|
||||||
|
print(f"""
|
||||||
|
Deployment files created!
|
||||||
|
|
||||||
|
To install the service on a Linux server:
|
||||||
|
1. Copy the systemd service file:
|
||||||
|
sudo cp {service_path} /etc/systemd/system/
|
||||||
|
|
||||||
|
2. Reload systemd:
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
|
||||||
|
3. Enable and start the service:
|
||||||
|
sudo systemctl enable mindmap.service
|
||||||
|
sudo systemctl start mindmap.service
|
||||||
|
|
||||||
|
4. Check service status:
|
||||||
|
sudo systemctl status mindmap.service
|
||||||
|
|
||||||
|
Alternatively, you can run the application with gunicorn manually:
|
||||||
|
cd {website_dir}
|
||||||
|
{venv_dir}/bin/gunicorn --workers 3 --bind 0.0.0.0:5000 'run:app'
|
||||||
|
""")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
60
deploy.sh
Executable file
60
deploy.sh
Executable file
@@ -0,0 +1,60 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Farben für Ausgaben
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
echo -e "${GREEN}==== MindMap Projekt Server Deployment ====${NC}"
|
||||||
|
|
||||||
|
# Python-Umgebung erstellen
|
||||||
|
echo -e "${BLUE}Erstelle Python-Umgebung...${NC}"
|
||||||
|
python3 -m venv venv
|
||||||
|
source venv/bin/activate
|
||||||
|
|
||||||
|
# Python-Abhängigkeiten installieren
|
||||||
|
echo -e "${BLUE}Installiere Python-Abhängigkeiten...${NC}"
|
||||||
|
pip install -r website/requirements.txt
|
||||||
|
pip install gunicorn
|
||||||
|
|
||||||
|
# Tailwind CSS kompilieren
|
||||||
|
echo -e "${BLUE}Kompiliere Tailwind CSS...${NC}"
|
||||||
|
cd website
|
||||||
|
python build_css.py
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
# Datenbank initialisieren, falls noch nicht vorhanden
|
||||||
|
echo -e "${BLUE}Initialisiere die Datenbank, falls nötig...${NC}"
|
||||||
|
cd website
|
||||||
|
python init_db.py
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
# Systemd Service erstellen
|
||||||
|
echo -e "${BLUE}Erstelle Systemd Service...${NC}"
|
||||||
|
SERVICE_FILE="[Unit]
|
||||||
|
Description=MindMap Wissensnetzwerk
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
User=$(whoami)
|
||||||
|
WorkingDirectory=$(pwd)/website
|
||||||
|
Environment=\"PATH=$(pwd)/venv/bin\"
|
||||||
|
ExecStart=$(pwd)/venv/bin/gunicorn --workers 3 --bind 0.0.0.0:5000 --log-level info 'run:app'
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target"
|
||||||
|
|
||||||
|
echo "$SERVICE_FILE" > mindmap.service
|
||||||
|
|
||||||
|
echo -e "${GREEN}==== Deployment abgeschlossen ====${NC}"
|
||||||
|
echo -e "${BLUE}Um den Service zu installieren, führe folgende Befehle aus:${NC}"
|
||||||
|
echo -e "sudo cp mindmap.service /etc/systemd/system/"
|
||||||
|
echo -e "sudo systemctl daemon-reload"
|
||||||
|
echo -e "sudo systemctl enable mindmap.service"
|
||||||
|
echo -e "sudo systemctl start mindmap.service"
|
||||||
|
echo -e ""
|
||||||
|
echo -e "${BLUE}Alternativ kannst du den Server manuell starten:${NC}"
|
||||||
|
echo -e "cd website"
|
||||||
|
echo -e "../venv/bin/gunicorn --workers 3 --bind 0.0.0.0:5000 'run:app'"
|
||||||
7
docker-compose.yml
Normal file
7
docker-compose.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
version: "3.9"
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
build: .
|
||||||
|
ports:
|
||||||
|
- "5000:5000"
|
||||||
|
restart: always
|
||||||
53
setup.py
Executable file
53
setup.py
Executable file
@@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Set up the project from the parent directory"""
|
||||||
|
print("Setting up the project...")
|
||||||
|
|
||||||
|
# Get the directory where setup.py is located (project root)
|
||||||
|
project_root = Path(__file__).resolve().parent
|
||||||
|
website_dir = project_root / "website"
|
||||||
|
|
||||||
|
# Check if virtual environment exists, create if not
|
||||||
|
venv_dir = project_root / "venv"
|
||||||
|
if not venv_dir.exists():
|
||||||
|
print("Creating virtual environment...")
|
||||||
|
subprocess.run([sys.executable, "-m", "venv", str(venv_dir)], check=True)
|
||||||
|
|
||||||
|
# Determine pip path based on OS
|
||||||
|
if os.name == 'nt': # Windows
|
||||||
|
pip = venv_dir / "Scripts" / "pip"
|
||||||
|
else: # Unix-like
|
||||||
|
pip = venv_dir / "bin" / "pip"
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
print("Installing dependencies...")
|
||||||
|
subprocess.run([str(pip), "install", "-r", str(website_dir / "requirements.txt")], check=True)
|
||||||
|
|
||||||
|
# Build CSS
|
||||||
|
print("Building CSS with Tailwind...")
|
||||||
|
if os.name == 'nt': # Windows
|
||||||
|
python = venv_dir / "Scripts" / "python"
|
||||||
|
else: # Unix-like
|
||||||
|
python = venv_dir / "bin" / "python"
|
||||||
|
|
||||||
|
subprocess.run([str(python), str(website_dir / "build_css.py")], check=True)
|
||||||
|
|
||||||
|
print("""
|
||||||
|
Setup completed successfully!
|
||||||
|
|
||||||
|
To run the development server:
|
||||||
|
1. Activate the virtual environment:
|
||||||
|
- Windows: .\\venv\\Scripts\\activate
|
||||||
|
- Unix/MacOS: source venv/bin/activate
|
||||||
|
2. Run the Flask application:
|
||||||
|
- cd website
|
||||||
|
- python run.py
|
||||||
|
""")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
/* Cybertechnisches Netzwerk Hintergrund-Overlay */
|
|
||||||
.cyber-network-bg {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: -1;
|
|
||||||
pointer-events: none;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cyber-network-bg::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: linear-gradient(125deg,
|
|
||||||
rgba(14, 14, 22, 0.95) 0%,
|
|
||||||
rgba(30, 30, 46, 0.98) 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.network-grid {
|
|
||||||
position: absolute;
|
|
||||||
width: 200%;
|
|
||||||
height: 200%;
|
|
||||||
top: -50%;
|
|
||||||
left: -50%;
|
|
||||||
background-size: 40px 40px;
|
|
||||||
background-image:
|
|
||||||
linear-gradient(to right, rgba(108, 93, 211, 0.05) 1px, transparent 1px),
|
|
||||||
linear-gradient(to bottom, rgba(108, 93, 211, 0.05) 1px, transparent 1px);
|
|
||||||
transform: perspective(500px) rotateX(60deg);
|
|
||||||
animation: grid-move 20s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.node {
|
|
||||||
position: absolute;
|
|
||||||
width: 4px;
|
|
||||||
height: 4px;
|
|
||||||
background: rgba(76, 223, 255, 0.8);
|
|
||||||
border-radius: 50%;
|
|
||||||
box-shadow: 0 0 10px rgba(76, 223, 255, 0.6);
|
|
||||||
filter: blur(1px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.connection {
|
|
||||||
position: absolute;
|
|
||||||
height: 1px;
|
|
||||||
background: linear-gradient(90deg,
|
|
||||||
rgba(76, 223, 255, 0.2) 0%,
|
|
||||||
rgba(108, 93, 211, 0.3) 50%,
|
|
||||||
rgba(76, 223, 255, 0.2) 100%);
|
|
||||||
transform-origin: left center;
|
|
||||||
animation: pulse 4s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.data-packet {
|
|
||||||
position: absolute;
|
|
||||||
width: 6px;
|
|
||||||
height: 6px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: rgba(118, 69, 217, 0.8);
|
|
||||||
filter: blur(1px);
|
|
||||||
animation: travel var(--travel-time, 6s) linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.glow-overlay {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: radial-gradient(
|
|
||||||
circle at 50% 40%,
|
|
||||||
rgba(76, 223, 255, 0.03) 0%,
|
|
||||||
rgba(108, 93, 211, 0.03) 45%,
|
|
||||||
transparent 70%
|
|
||||||
);
|
|
||||||
opacity: 0.8;
|
|
||||||
animation: pulse-glow 8s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Animations */
|
|
||||||
@keyframes grid-move {
|
|
||||||
0% {
|
|
||||||
transform: perspective(500px) rotateX(60deg) translateY(0);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: perspective(500px) rotateX(60deg) translateY(40px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes pulse {
|
|
||||||
0%, 100% {
|
|
||||||
opacity: 0.2;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
opacity: 0.4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes travel {
|
|
||||||
0% {
|
|
||||||
transform: translateX(0) translateY(0);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
10% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
90% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: translateX(var(--travel-x, 100px)) translateY(var(--travel-y, 100px));
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes pulse-glow {
|
|
||||||
0%, 100% {
|
|
||||||
opacity: 0.4;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
/**
|
|
||||||
* Initialisierungsmodul für den CyberNetwork-Hintergrund
|
|
||||||
* Importiert und startet die Animation
|
|
||||||
*/
|
|
||||||
|
|
||||||
import CyberNetwork from './cyber-network.js';
|
|
||||||
|
|
||||||
// Beim Laden des Dokuments starten
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
console.log('CyberNetwork: Initialisierung gestartet');
|
|
||||||
|
|
||||||
// Prüfen ob das CSS bereits geladen ist, wenn nicht, dann laden
|
|
||||||
if (!document.querySelector('link[href*="cybernetwork-bg.css"]')) {
|
|
||||||
console.log('CyberNetwork: CSS wird geladen');
|
|
||||||
const cyberNetworkCss = document.createElement('link');
|
|
||||||
cyberNetworkCss.rel = 'stylesheet';
|
|
||||||
cyberNetworkCss.href = '/static/css/src/cybernetwork-bg.css';
|
|
||||||
document.head.appendChild(cyberNetworkCss);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Container-Element für das Netzwerk finden
|
|
||||||
const container = document.getElementById('cyber-background-container');
|
|
||||||
|
|
||||||
if (!container) {
|
|
||||||
console.error('CyberNetwork: Container #cyber-background-container nicht gefunden!');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('CyberNetwork: Container gefunden', container);
|
|
||||||
|
|
||||||
// Konfiguration für den Netzwerk-Hintergrund
|
|
||||||
const networkConfig = {
|
|
||||||
container: container,
|
|
||||||
nodeCount: window.innerWidth < 768 ? 15 : 30, // Weniger Nodes auf mobilen Geräten
|
|
||||||
connectionCount: window.innerWidth < 768 ? 25 : 50,
|
|
||||||
packetCount: window.innerWidth < 768 ? 8 : 15,
|
|
||||||
animationSpeed: 1.0
|
|
||||||
};
|
|
||||||
|
|
||||||
// Netzwerk erstellen und initialisieren
|
|
||||||
const cyberNetwork = new CyberNetwork(networkConfig);
|
|
||||||
cyberNetwork.init();
|
|
||||||
console.log('CyberNetwork: Netzwerk initialisiert');
|
|
||||||
|
|
||||||
// Globale Referenz für Debug-Zwecke
|
|
||||||
window.cyberNetwork = cyberNetwork;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Funktion zum manuellen Initialisieren, falls notwendig
|
|
||||||
export function initCyberNetwork(config = {}) {
|
|
||||||
console.log('CyberNetwork: Manuelle Initialisierung gestartet');
|
|
||||||
|
|
||||||
// CSS laden, falls nicht vorhanden
|
|
||||||
if (!document.querySelector('link[href*="cybernetwork-bg.css"]')) {
|
|
||||||
console.log('CyberNetwork: CSS wird geladen (manuell)');
|
|
||||||
const cyberNetworkCss = document.createElement('link');
|
|
||||||
cyberNetworkCss.rel = 'stylesheet';
|
|
||||||
cyberNetworkCss.href = '/static/css/src/cybernetwork-bg.css';
|
|
||||||
document.head.appendChild(cyberNetworkCss);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Container-Element für das Netzwerk finden
|
|
||||||
const container = document.getElementById('cyber-background-container');
|
|
||||||
|
|
||||||
if (!container) {
|
|
||||||
console.error('CyberNetwork: Container #cyber-background-container nicht gefunden!');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bestehende Instanz zurücksetzen, falls vorhanden
|
|
||||||
if (window.cyberNetwork) {
|
|
||||||
console.log('CyberNetwork: Bestehende Instanz wird zurückgesetzt');
|
|
||||||
window.cyberNetwork.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Netzwerk mit benutzerdefinierten Optionen erstellen
|
|
||||||
const networkConfig = {
|
|
||||||
container: container,
|
|
||||||
nodeCount: window.innerWidth < 768 ? 15 : 30,
|
|
||||||
connectionCount: window.innerWidth < 768 ? 25 : 50,
|
|
||||||
packetCount: window.innerWidth < 768 ? 8 : 15,
|
|
||||||
animationSpeed: 1.0,
|
|
||||||
...config
|
|
||||||
};
|
|
||||||
|
|
||||||
// Neue Instanz erstellen und initialisieren
|
|
||||||
const cyberNetwork = new CyberNetwork(networkConfig);
|
|
||||||
cyberNetwork.init();
|
|
||||||
console.log('CyberNetwork: Netzwerk manuell initialisiert');
|
|
||||||
|
|
||||||
// Globale Referenz aktualisieren
|
|
||||||
window.cyberNetwork = cyberNetwork;
|
|
||||||
|
|
||||||
return cyberNetwork;
|
|
||||||
}
|
|
||||||
@@ -1,240 +0,0 @@
|
|||||||
/**
|
|
||||||
* Cyber Network Background Animation
|
|
||||||
* Generiert dynamisch ein animiertes Netzwerk für den Hintergrund
|
|
||||||
*/
|
|
||||||
|
|
||||||
class CyberNetwork {
|
|
||||||
constructor(options = {}) {
|
|
||||||
this.options = {
|
|
||||||
container: options.container || document.body,
|
|
||||||
nodeCount: options.nodeCount || 30,
|
|
||||||
connectionCount: options.connectionCount || 50,
|
|
||||||
packetCount: options.packetCount || 15,
|
|
||||||
animationSpeed: options.animationSpeed || 1.0,
|
|
||||||
...options
|
|
||||||
};
|
|
||||||
|
|
||||||
this.nodes = [];
|
|
||||||
this.connections = [];
|
|
||||||
this.packets = [];
|
|
||||||
this.initialized = false;
|
|
||||||
|
|
||||||
this.containerElement = null;
|
|
||||||
this.networkGridElement = null;
|
|
||||||
this.glowOverlayElement = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
if (this.initialized) return;
|
|
||||||
|
|
||||||
// Container erstellen
|
|
||||||
this.containerElement = document.createElement('div');
|
|
||||||
this.containerElement.className = 'cyber-network-bg';
|
|
||||||
|
|
||||||
// Grid erstellen
|
|
||||||
this.networkGridElement = document.createElement('div');
|
|
||||||
this.networkGridElement.className = 'network-grid';
|
|
||||||
this.containerElement.appendChild(this.networkGridElement);
|
|
||||||
|
|
||||||
// Glow Overlay erstellen
|
|
||||||
this.glowOverlayElement = document.createElement('div');
|
|
||||||
this.glowOverlayElement.className = 'glow-overlay';
|
|
||||||
this.containerElement.appendChild(this.glowOverlayElement);
|
|
||||||
|
|
||||||
// Nodes generieren
|
|
||||||
this.generateNodes();
|
|
||||||
|
|
||||||
// Connections generieren
|
|
||||||
this.generateConnections();
|
|
||||||
|
|
||||||
// Data packets generieren
|
|
||||||
this.generateDataPackets();
|
|
||||||
|
|
||||||
// Container zum DOM hinzufügen
|
|
||||||
if (typeof this.options.container === 'string') {
|
|
||||||
const container = document.querySelector(this.options.container);
|
|
||||||
if (container) {
|
|
||||||
container.appendChild(this.containerElement);
|
|
||||||
} else {
|
|
||||||
document.body.appendChild(this.containerElement);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.options.container.appendChild(this.containerElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.initialized = true;
|
|
||||||
|
|
||||||
// Animation starten
|
|
||||||
window.addEventListener('resize', this.handleResize.bind(this));
|
|
||||||
this.startAnimationCycle();
|
|
||||||
}
|
|
||||||
|
|
||||||
generateNodes() {
|
|
||||||
const containerWidth = window.innerWidth;
|
|
||||||
const containerHeight = window.innerHeight;
|
|
||||||
|
|
||||||
for (let i = 0; i < this.options.nodeCount; i++) {
|
|
||||||
const x = Math.random() * containerWidth;
|
|
||||||
const y = Math.random() * containerHeight;
|
|
||||||
|
|
||||||
const node = document.createElement('div');
|
|
||||||
node.className = 'node';
|
|
||||||
node.style.left = `${x}px`;
|
|
||||||
node.style.top = `${y}px`;
|
|
||||||
|
|
||||||
// Größen-Variation für visuelle Tiefe
|
|
||||||
const size = 2 + Math.random() * 4;
|
|
||||||
node.style.width = `${size}px`;
|
|
||||||
node.style.height = `${size}px`;
|
|
||||||
|
|
||||||
// Speichern der Position für spätere Referenz
|
|
||||||
node._data = { x, y, size };
|
|
||||||
|
|
||||||
this.containerElement.appendChild(node);
|
|
||||||
this.nodes.push(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
generateConnections() {
|
|
||||||
for (let i = 0; i < this.options.connectionCount; i++) {
|
|
||||||
// Zufällige Nodes auswählen
|
|
||||||
const startNodeIndex = Math.floor(Math.random() * this.nodes.length);
|
|
||||||
let endNodeIndex;
|
|
||||||
do {
|
|
||||||
endNodeIndex = Math.floor(Math.random() * this.nodes.length);
|
|
||||||
} while (endNodeIndex === startNodeIndex);
|
|
||||||
|
|
||||||
const startNode = this.nodes[startNodeIndex];
|
|
||||||
const endNode = this.nodes[endNodeIndex];
|
|
||||||
const startData = startNode._data;
|
|
||||||
const endData = endNode._data;
|
|
||||||
|
|
||||||
// Verbindung erstellen
|
|
||||||
const connection = document.createElement('div');
|
|
||||||
connection.className = 'connection';
|
|
||||||
|
|
||||||
// Position und Rotation berechnen
|
|
||||||
const dx = endData.x - startData.x;
|
|
||||||
const dy = endData.y - startData.y;
|
|
||||||
const length = Math.sqrt(dx * dx + dy * dy);
|
|
||||||
const angle = Math.atan2(dy, dx) * (180 / Math.PI);
|
|
||||||
|
|
||||||
connection.style.width = `${length}px`;
|
|
||||||
connection.style.left = `${startData.x}px`;
|
|
||||||
connection.style.top = `${startData.y}px`;
|
|
||||||
connection.style.transform = `rotate(${angle}deg)`;
|
|
||||||
|
|
||||||
// Variation in der Animations-Geschwindigkeit
|
|
||||||
connection.style.animationDuration = `${3 + Math.random() * 4}s`;
|
|
||||||
|
|
||||||
// Speichern der verbundenen Nodes
|
|
||||||
connection._data = {
|
|
||||||
startNode: startNodeIndex,
|
|
||||||
endNode: endNodeIndex,
|
|
||||||
length
|
|
||||||
};
|
|
||||||
|
|
||||||
this.containerElement.appendChild(connection);
|
|
||||||
this.connections.push(connection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
generateDataPackets() {
|
|
||||||
for (let i = 0; i < this.options.packetCount; i++) {
|
|
||||||
this.createNewDataPacket();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
createNewDataPacket() {
|
|
||||||
if (this.connections.length === 0) return;
|
|
||||||
|
|
||||||
// Zufällige Verbindung auswählen
|
|
||||||
const connectionIndex = Math.floor(Math.random() * this.connections.length);
|
|
||||||
const connection = this.connections[connectionIndex];
|
|
||||||
const connectionData = connection._data;
|
|
||||||
|
|
||||||
const startNode = this.nodes[connectionData.startNode];
|
|
||||||
const startData = startNode._data;
|
|
||||||
|
|
||||||
// Data Packet erstellen
|
|
||||||
const packet = document.createElement('div');
|
|
||||||
packet.className = 'data-packet';
|
|
||||||
|
|
||||||
// Position auf dem Startknoten
|
|
||||||
packet.style.left = `${startData.x}px`;
|
|
||||||
packet.style.top = `${startData.y}px`;
|
|
||||||
|
|
||||||
// Zufällige Geschwindigkeit
|
|
||||||
const travelTime = (4 + Math.random() * 4) / this.options.animationSpeed;
|
|
||||||
packet.style.setProperty('--travel-time', `${travelTime}s`);
|
|
||||||
|
|
||||||
// Ziel-Koordinaten berechnen
|
|
||||||
const endNode = this.nodes[connectionData.endNode];
|
|
||||||
const endData = endNode._data;
|
|
||||||
const travelX = endData.x - startData.x;
|
|
||||||
const travelY = endData.y - startData.y;
|
|
||||||
|
|
||||||
packet.style.setProperty('--travel-x', `${travelX}px`);
|
|
||||||
packet.style.setProperty('--travel-y', `${travelY}px`);
|
|
||||||
|
|
||||||
// Farb-Variation
|
|
||||||
if (Math.random() > 0.5) {
|
|
||||||
packet.style.background = 'rgba(76, 223, 255, 0.8)'; // Akzentfarbe
|
|
||||||
}
|
|
||||||
|
|
||||||
this.containerElement.appendChild(packet);
|
|
||||||
this.packets.push(packet);
|
|
||||||
|
|
||||||
// Nach Ende der Animation neues Paket erstellen
|
|
||||||
setTimeout(() => {
|
|
||||||
if (this.containerElement.contains(packet)) {
|
|
||||||
this.containerElement.removeChild(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
const index = this.packets.indexOf(packet);
|
|
||||||
if (index > -1) {
|
|
||||||
this.packets.splice(index, 1);
|
|
||||||
this.createNewDataPacket();
|
|
||||||
}
|
|
||||||
}, travelTime * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleResize() {
|
|
||||||
if (!this.initialized) return;
|
|
||||||
|
|
||||||
// Bei Größenänderung alles neu generieren
|
|
||||||
this.reset();
|
|
||||||
this.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
reset() {
|
|
||||||
if (!this.initialized) return;
|
|
||||||
|
|
||||||
// Alle Elemente entfernen
|
|
||||||
this.nodes.forEach(node => node.remove());
|
|
||||||
this.connections.forEach(connection => connection.remove());
|
|
||||||
this.packets.forEach(packet => packet.remove());
|
|
||||||
|
|
||||||
this.nodes = [];
|
|
||||||
this.connections = [];
|
|
||||||
this.packets = [];
|
|
||||||
|
|
||||||
if (this.containerElement) {
|
|
||||||
this.containerElement.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.initialized = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
startAnimationCycle() {
|
|
||||||
// Regelmäßig neue Pakete erstellen für mehr Dynamik
|
|
||||||
setInterval(() => {
|
|
||||||
if (this.packets.length < this.options.packetCount * 1.5) {
|
|
||||||
this.createNewDataPacket();
|
|
||||||
}
|
|
||||||
}, 1000 / this.options.animationSpeed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exportieren als Modul
|
|
||||||
export default CyberNetwork;
|
|
||||||
134
website/README.md
Normal file
134
website/README.md
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
# MindMapProjekt - Roadmap
|
||||||
|
|
||||||
|
## Projektübersicht
|
||||||
|
Das MindMapProjekt ist eine interaktive Plattform zum Visualisieren, Erforschen und Teilen von Wissen. Das Projekt wird umfassend überarbeitet, um ein modernes, benutzerfreundliches Design und erweiterte Funktionalitäten zu bieten.
|
||||||
|
|
||||||
|
## Technischer Stack
|
||||||
|
- **Backend**: Python/Flask
|
||||||
|
- **Frontend**:
|
||||||
|
- Tailwind CSS für moderne UI
|
||||||
|
- SVG-Bibliotheken für Visualisierungen (D3.js)
|
||||||
|
- JavaScript/Alpine.js für interaktive Komponenten
|
||||||
|
- **Datenbank**: SQLite mit SQLAlchemy
|
||||||
|
- **KI-Integration**: OpenAI API für intelligente Assistenz
|
||||||
|
|
||||||
|
## Installation und Verwendung
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
1. Repository klonen
|
||||||
|
2. Virtuelle Umgebung erstellen: `python -m venv venv`
|
||||||
|
3. Virtuelle Umgebung aktivieren:
|
||||||
|
- Windows: `venv\Scripts\activate`
|
||||||
|
- Unix/MacOS: `source venv/bin/activate`
|
||||||
|
4. Abhängigkeiten installieren: `pip install -r requirements.txt`
|
||||||
|
5. Datenbank initialisieren: `python TOOLS.py db:rebuild`
|
||||||
|
6. Admin-Benutzer erstellen: `python TOOLS.py user:admin`
|
||||||
|
7. Server starten: `python TOOLS.py server:run`
|
||||||
|
|
||||||
|
### Standardbenutzer
|
||||||
|
- **Admin-Benutzer**: Username: `admin` / Passwort: `admin`
|
||||||
|
- **Testbenutzer**: Username: `user` / Passwort: `user`
|
||||||
|
|
||||||
|
### Verwaltungswerkzeuge mit TOOLS.py
|
||||||
|
Das Projekt enthält ein zentrales Verwaltungsskript `TOOLS.py`, das verschiedene Hilfsfunktionen bietet:
|
||||||
|
|
||||||
|
#### Datenbankverwaltung
|
||||||
|
- `python TOOLS.py db:fix` - Reparieren der Datenbankstruktur
|
||||||
|
- `python TOOLS.py db:rebuild` - Datenbank neu aufbauen (löscht alle Daten!)
|
||||||
|
- `python TOOLS.py db:test` - Datenbankverbindung und Modelle testen
|
||||||
|
- `python TOOLS.py db:stats` - Datenbankstatistiken anzeigen
|
||||||
|
|
||||||
|
#### Benutzerverwaltung
|
||||||
|
- `python TOOLS.py user:list` - Alle Benutzer anzeigen
|
||||||
|
- `python TOOLS.py user:create -u USERNAME -e EMAIL -p PASSWORD [-a]` - Neuen Benutzer erstellen
|
||||||
|
- `python TOOLS.py user:admin` - Admin-Benutzer erstellen (admin/admin)
|
||||||
|
- `python TOOLS.py user:reset-pw -u USERNAME -p NEWPASSWORD` - Benutzerpasswort zurücksetzen
|
||||||
|
- `python TOOLS.py user:delete -u USERNAME` - Benutzer löschen
|
||||||
|
|
||||||
|
#### Serververwaltung
|
||||||
|
- `python TOOLS.py server:run [--host HOST] [--port PORT] [--no-debug]` - Entwicklungsserver starten
|
||||||
|
|
||||||
|
Für detaillierte Hilfe: `python TOOLS.py -h`
|
||||||
|
|
||||||
|
## Roadmap der Überarbeitung
|
||||||
|
|
||||||
|
### Phase 1: Grundlegende Infrastruktur ✅
|
||||||
|
- [x] Bestandsaufnahme des aktuellen Projekts
|
||||||
|
- [x] Erstellung der Roadmap
|
||||||
|
- [x] Aktualisierung der Abhängigkeiten
|
||||||
|
- [x] Integration von Tailwind CSS
|
||||||
|
- [x] Einrichtung der SVG-Bibliotheken (D3.js)
|
||||||
|
- [x] Favicon erstellen
|
||||||
|
- [x] Setup-Skript für einfache Installation
|
||||||
|
|
||||||
|
### Phase 2: Design-Überarbeitung 🔄
|
||||||
|
- [x] Implementierung des Dark Mode
|
||||||
|
- [x] Erstellung eines modernen, minimalistischen UI mit Tech-Ästhetik
|
||||||
|
- [x] Responsive Design für alle Geräte
|
||||||
|
- [ ] Gestaltung der Landing Page mit großer Typografie
|
||||||
|
|
||||||
|
### Phase 3: Mindmap-Funktionalitäten 🔄
|
||||||
|
- [x] Verbesserte Visualisierung mit SVG und D3.js
|
||||||
|
- [x] Implementierung der Mouseover-Funktion
|
||||||
|
- [x] Entwicklung der Suchfunktion für Knoten
|
||||||
|
- [ ] Tagging-System für Inhalte
|
||||||
|
- [ ] Quellenmanagement und -verlinkung
|
||||||
|
- [ ] Upload-Funktionalität an Knotenpunkten
|
||||||
|
|
||||||
|
### Phase 4: Kernseitenentwicklung
|
||||||
|
- [ ] Überarbeitung der Startseite mit neuen Features
|
||||||
|
- [ ] Entwicklung der "Wer sind wir?"-Seite
|
||||||
|
- [ ] Implementierung von Impressum und Datenschutzerklärung
|
||||||
|
- [ ] Erstellung der Kontaktseite mit FAQs
|
||||||
|
- [ ] Überarbeitung des Benutzerprofilbereichs
|
||||||
|
|
||||||
|
### Phase 5: Community-Features
|
||||||
|
- [ ] Entwicklung des Autorenbereichs
|
||||||
|
- [ ] Implementierung von Community-Bereichen für Themenbereiche
|
||||||
|
- [ ] Verbesserter Kommentarbereich
|
||||||
|
- [ ] Benutzerrechtemanagement
|
||||||
|
|
||||||
|
### Phase 6: KI-Integration
|
||||||
|
- [ ] Implementierung des Frage-Antwort-Systems
|
||||||
|
- [ ] KI-generierte Themeneinleitungen
|
||||||
|
- [ ] Intelligente Suchunterstützung
|
||||||
|
- [ ] Geführte Pfade durch Themenbereiche
|
||||||
|
- [ ] Vorgeschlagene Chat-Möglichkeiten
|
||||||
|
|
||||||
|
### Phase 7: Benutzerprofilfunktionen
|
||||||
|
- [ ] Speichern von Thematiken
|
||||||
|
- [ ] Persönliche Mindmap/Pinboard
|
||||||
|
- [ ] Beitragsmanagement
|
||||||
|
- [ ] Benutzerstatistiken und -aktivitäten
|
||||||
|
|
||||||
|
### Phase 8: Testing und Optimierung
|
||||||
|
- [ ] Umfassende Tests aller Funktionen
|
||||||
|
- [ ] Performance-Optimierung
|
||||||
|
- [ ] SEO-Implementierung
|
||||||
|
- [ ] Barrierefreiheit prüfen und verbessern
|
||||||
|
|
||||||
|
### Phase 9: Dokumentation und Einführung
|
||||||
|
- [ ] Erstellung von Benutzeranleitungen
|
||||||
|
- [ ] Entwicklerdokumentation
|
||||||
|
- [ ] Administratorenhandbuch
|
||||||
|
- [ ] Guided Tour für neue Benutzer
|
||||||
|
|
||||||
|
## Aktueller Status
|
||||||
|
- **Phase 1**: ✅ Abgeschlossen
|
||||||
|
- **Phase 2**: 🔄 In Bearbeitung (75% abgeschlossen)
|
||||||
|
- **Phase 3**: 🔄 In Bearbeitung (50% abgeschlossen)
|
||||||
|
|
||||||
|
## Aktuelle Fortschritte
|
||||||
|
- Grundlegende UI modernisiert mit Tailwind CSS und Dark Mode
|
||||||
|
- Neues Favicon für bessere visuelle Identität erstellt
|
||||||
|
- Setup-Prozess vereinfacht mit einem Shell-Skript
|
||||||
|
- Mindmap-Visualisierung komplett überarbeitet mit D3.js für eine interaktivere Erfahrung
|
||||||
|
- Responsive Design für optimale Darstellung auf allen Geräten
|
||||||
|
|
||||||
|
## Nächste Schritte
|
||||||
|
- Fertigstellung der Landing Page
|
||||||
|
- Erstellung der "Wer sind wir?"-Seite
|
||||||
|
- Implementierung des Tagging-Systems für Gedanken
|
||||||
|
- Verbesserung der Gedankenansicht im Mindmap-Bereich
|
||||||
|
|
||||||
|
*Zuletzt aktualisiert: 01.06.2024*
|
||||||
Binary file not shown.
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-311.pyc
Normal file
BIN
website/__pycache__/init_db.cpython-311.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.
Binary file not shown.
BIN
website/__pycache__/models.cpython-313.pyc
Normal file
BIN
website/__pycache__/models.cpython-313.pyc
Normal file
Binary file not shown.
@@ -41,7 +41,7 @@ app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
|||||||
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=365) # Langlebige Session für Dark Mode-Einstellung
|
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=365) # Langlebige Session für Dark Mode-Einstellung
|
||||||
|
|
||||||
# OpenAI API-Konfiguration
|
# OpenAI API-Konfiguration
|
||||||
client = OpenAI(api_key="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")
|
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
|
||||||
|
|
||||||
# Context processor für globale Template-Variablen
|
# Context processor für globale Template-Variablen
|
||||||
@app.context_processor
|
@app.context_processor
|
||||||
@@ -1109,7 +1109,7 @@ def create_default_categories():
|
|||||||
child = Category(**child_data, parent_id=category.id)
|
child = Category(**child_data, parent_id=category.id)
|
||||||
db.session.add(child)
|
db.session.add(child)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
print("Standard-Kategorien wurden erstellt!")
|
print("Standard-Kategorien wurden erstellt!")
|
||||||
|
|
||||||
def initialize_database():
|
def initialize_database():
|
||||||
@@ -1145,24 +1145,10 @@ def my_account():
|
|||||||
@app.route('/static/network-bg.jpg')
|
@app.route('/static/network-bg.jpg')
|
||||||
@app.route('/static/network-bg.svg')
|
@app.route('/static/network-bg.svg')
|
||||||
def dummy_network_bg():
|
def dummy_network_bg():
|
||||||
"""Stellt einen Fallback für alte Netzwerk-Hintergrund-Anfragen bereit."""
|
"""Leere Antwort für die nicht mehr verwendeten Netzwerk-Hintergrundbilder."""
|
||||||
return redirect(url_for('static', filename='img/backgrounds/network-bg.jpg'))
|
return '', 200
|
||||||
|
|
||||||
@app.route('/static/css/src/cybernetwork-bg.css')
|
|
||||||
def serve_cybernetwork_css():
|
|
||||||
"""Stellt das CSS für den cybertechnischen Netzwerk-Hintergrund bereit."""
|
|
||||||
return app.send_static_file('css/src/cybernetwork-bg.css')
|
|
||||||
|
|
||||||
@app.route('/static/js/modules/cyber-network.js')
|
|
||||||
def serve_cybernetwork_js():
|
|
||||||
"""Stellt das JavaScript-Modul für den cybertechnischen Netzwerk-Hintergrund bereit."""
|
|
||||||
return app.send_static_file('js/modules/cyber-network.js')
|
|
||||||
|
|
||||||
@app.route('/static/js/modules/cyber-network-init.js')
|
|
||||||
def serve_cybernetwork_init_js():
|
|
||||||
"""Stellt das Initialisierungs-JavaScript für den cybertechnischen Netzwerk-Hintergrund bereit."""
|
|
||||||
return app.send_static_file('js/modules/cyber-network-init.js')
|
|
||||||
|
|
||||||
|
# Route zum expliziten Neu-Laden der Umgebungsvariablen
|
||||||
@app.route('/admin/reload-env', methods=['POST'])
|
@app.route('/admin/reload-env', methods=['POST'])
|
||||||
@admin_required
|
@admin_required
|
||||||
def reload_env():
|
def reload_env():
|
||||||
BIN
website/instance/mindmap.db
Normal file
BIN
website/instance/mindmap.db
Normal file
Binary file not shown.
14
website/requirements.txt
Normal file
14
website/requirements.txt
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
flask==2.2.5
|
||||||
|
flask-login==0.6.2
|
||||||
|
flask-wtf
|
||||||
|
email-validator
|
||||||
|
python-dotenv
|
||||||
|
werkzeug==2.2.3
|
||||||
|
flask-sqlalchemy==3.0.5
|
||||||
|
openai==1.3.0
|
||||||
|
requests==2.31.0
|
||||||
|
flask-cors==4.0.0
|
||||||
|
gunicorn==21.2.0
|
||||||
|
#pillow==10.0.1
|
||||||
|
pytest==7.4.0
|
||||||
|
pytest-flask==1.2.0
|
||||||
33
website/run.py
Executable file
33
website/run.py
Executable file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from init_db import init_database
|
||||||
|
from app import app
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# Lade .env-Datei explizit
|
||||||
|
env_path = Path(__file__).parent / ".env"
|
||||||
|
if env_path.exists():
|
||||||
|
print(f"Lade Umgebungsvariablen aus {env_path}")
|
||||||
|
load_dotenv(dotenv_path=env_path, override=True, force=True)
|
||||||
|
else:
|
||||||
|
print("Warnung: .env-Datei nicht gefunden!")
|
||||||
|
|
||||||
|
# Check if CSS file exists, build it if it doesn't
|
||||||
|
css_file = Path(__file__).parent / "static" / "css" / "main.css"
|
||||||
|
if not css_file.exists():
|
||||||
|
print("CSS file not found. Building with Tailwind...")
|
||||||
|
try:
|
||||||
|
from build_css import build_css
|
||||||
|
build_css()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Warning: Failed to build CSS: {e}")
|
||||||
|
print("You may need to run 'python build_css.py' manually.")
|
||||||
|
|
||||||
|
# Initialize the database first
|
||||||
|
init_database()
|
||||||
|
|
||||||
|
# Run the Flask application
|
||||||
|
app.run(debug=True, host='0.0.0.0', port=5000)
|
||||||
52
website/setup.sh
Executable file
52
website/setup.sh
Executable file
@@ -0,0 +1,52 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Farben für Ausgaben
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
YELLOW='\033[0;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
echo -e "${GREEN}==== MindMap Projekt Setup ====${NC}"
|
||||||
|
|
||||||
|
# Verzeichnis erstellen, wenn nicht vorhanden
|
||||||
|
echo -e "${BLUE}Überprüfe Verzeichnisstruktur...${NC}"
|
||||||
|
mkdir -p static/css/src
|
||||||
|
mkdir -p static/img
|
||||||
|
mkdir -p static/js
|
||||||
|
mkdir -p bin
|
||||||
|
|
||||||
|
# Überprüfe, ob .env-Datei existiert und erstelle sie, wenn nicht
|
||||||
|
echo -e "${BLUE}Überprüfe .env-Datei...${NC}"
|
||||||
|
if [ ! -f ".env" ]; then
|
||||||
|
echo -e "${YELLOW}Keine .env-Datei gefunden. Erstelle neue .env-Datei aus example.env...${NC}"
|
||||||
|
cp example.env .env
|
||||||
|
echo -e "${YELLOW}Bitte bearbeiten Sie die .env-Datei und setzen Sie die erforderlichen Werte.${NC}"
|
||||||
|
echo -e "${YELLOW}Insbesondere müssen Sie einen gültigen API-Schlüssel für OpenAI setzen.${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}.env-Datei existiert bereits.${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Python-Abhängigkeiten installieren
|
||||||
|
echo -e "${BLUE}Installiere Python-Abhängigkeiten...${NC}"
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
# Zusätzliche Abhängigkeiten für Favicon-Erstellung
|
||||||
|
echo -e "${BLUE}Installiere Abhängigkeiten für Favicon-Erstellung...${NC}"
|
||||||
|
pip install pillow cairosvg
|
||||||
|
|
||||||
|
# Tailwind CSS mit Python-Skript kompilieren
|
||||||
|
echo -e "${BLUE}Kompiliere Tailwind CSS...${NC}"
|
||||||
|
python build_css.py
|
||||||
|
|
||||||
|
# Favicon generieren
|
||||||
|
echo -e "${BLUE}Generiere Favicon...${NC}"
|
||||||
|
python static/img/favicon-gen.py
|
||||||
|
|
||||||
|
# Erstelle die Datenbank
|
||||||
|
echo -e "${BLUE}Initialisiere die Datenbank...${NC}"
|
||||||
|
python init_db.py
|
||||||
|
|
||||||
|
echo -e "${GREEN}==== Setup abgeschlossen ====${NC}"
|
||||||
|
echo -e "${GREEN}Starte die Anwendung mit: python run.py${NC}"
|
||||||
|
echo -e "${GREEN}Für Entwicklung mit CSS-Autoreload: python dev.py${NC}"
|
||||||
@@ -1434,16 +1434,11 @@ html, body {
|
|||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
background: linear-gradient(135deg, var(--background-start), var(--background-end));
|
background: linear-gradient(135deg, var(--background-start), var(--background-end));
|
||||||
background-attachment: fixed;
|
background-attachment: fixed;
|
||||||
scroll-behavior: smooth;
|
|
||||||
height: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sticky navbar */
|
/* Sticky navbar */
|
||||||
.navbar.sticky-top {
|
.navbar.sticky-top {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 50;
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Importiere das Cyber-Network CSS */
|
|
||||||
@import url('/static/css/src/cybernetwork-bg.css');
|
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
@@ -73,7 +73,6 @@
|
|||||||
|
|
||||||
<!-- 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">
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/src/cybernetwork-bg.css') }}">
|
|
||||||
|
|
||||||
<!-- Basis-Stylesheet -->
|
<!-- Basis-Stylesheet -->
|
||||||
<link href="{{ url_for('static', filename='style.css') }}" rel="stylesheet">
|
<link href="{{ url_for('static', filename='style.css') }}" rel="stylesheet">
|
||||||
@@ -155,14 +154,8 @@
|
|||||||
|
|
||||||
<!-- Seitenspezifische Styles -->
|
<!-- Seitenspezifische Styles -->
|
||||||
{% block extra_css %}{% endblock %}
|
{% block extra_css %}{% endblock %}
|
||||||
|
|
||||||
<!-- Cybertechnisches Netzwerk-Hintergrund -->
|
|
||||||
<script type="module" src="{{ url_for('static', filename='js/modules/cyber-network-init.js') }}"></script>
|
|
||||||
</head>
|
</head>
|
||||||
<body data-page="{{ request.endpoint }}" class="relative overflow-x-hidden">
|
<body data-page="{{ request.endpoint }}" class="relative overflow-x-hidden">
|
||||||
<!-- Cybertechnisches Netzwerk-Hintergrund Container (wird via JavaScript befüllt) -->
|
|
||||||
<div id="cyber-background-container" style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; pointer-events: none; overflow: hidden;"></div>
|
|
||||||
|
|
||||||
<!-- Globaler Hintergrund -->
|
<!-- Globaler Hintergrund -->
|
||||||
<div class="full-page-bg"></div>
|
<div class="full-page-bg"></div>
|
||||||
<!-- Statischer Fallback-Hintergrund (wird nur angezeigt, wenn JavaScript deaktiviert ist) -->
|
<!-- Statischer Fallback-Hintergrund (wird nur angezeigt, wenn JavaScript deaktiviert ist) -->
|
||||||
@@ -714,7 +714,7 @@
|
|||||||
|
|
||||||
<!-- Initialization Script -->
|
<!-- Initialization Script -->
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
// Mindmap-Container holen
|
// Mindmap-Container holen
|
||||||
const mindmapContainer = document.getElementById('mindmap-container');
|
const mindmapContainer = document.getElementById('mindmap-container');
|
||||||
|
|
||||||
@@ -837,7 +837,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// UI Event-Handler einrichten
|
// UI Event-Handler einrichten
|
||||||
document.getElementById('zoom-in-btn').addEventListener('click', function() {
|
document.getElementById('zoom-in-btn').addEventListener('click', function() {
|
||||||
if (window.mindmap) {
|
if (window.mindmap) {
|
||||||
const transform = d3.zoomTransform(window.mindmap.svg.node());
|
const transform = d3.zoomTransform(window.mindmap.svg.node());
|
||||||
window.mindmap.svg.call(
|
window.mindmap.svg.call(
|
||||||
@@ -845,9 +845,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
d3.zoomIdentity.translate(transform.x, transform.y).scale(transform.k * 1.3)
|
d3.zoomIdentity.translate(transform.x, transform.y).scale(transform.k * 1.3)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('zoom-out-btn').addEventListener('click', function() {
|
document.getElementById('zoom-out-btn').addEventListener('click', function() {
|
||||||
if (window.mindmap) {
|
if (window.mindmap) {
|
||||||
const transform = d3.zoomTransform(window.mindmap.svg.node());
|
const transform = d3.zoomTransform(window.mindmap.svg.node());
|
||||||
window.mindmap.svg.call(
|
window.mindmap.svg.call(
|
||||||
@@ -855,18 +855,18 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
d3.zoomIdentity.translate(transform.x, transform.y).scale(transform.k / 1.3)
|
d3.zoomIdentity.translate(transform.x, transform.y).scale(transform.k / 1.3)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('center-btn').addEventListener('click', function() {
|
document.getElementById('center-btn').addEventListener('click', function() {
|
||||||
if (window.mindmap) {
|
if (window.mindmap) {
|
||||||
window.mindmap.svg.call(
|
window.mindmap.svg.call(
|
||||||
d3.zoom().transform,
|
d3.zoom().transform,
|
||||||
d3.zoomIdentity
|
d3.zoomIdentity
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('add-thought-btn').addEventListener('click', function() {
|
document.getElementById('add-thought-btn').addEventListener('click', function() {
|
||||||
if (window.mindmap && window.mindmap.selectedNode) {
|
if (window.mindmap && window.mindmap.selectedNode) {
|
||||||
const nodeId = window.mindmap.selectedNode.id;
|
const nodeId = window.mindmap.selectedNode.id;
|
||||||
const modal = document.getElementById('add-thought-modal');
|
const modal = document.getElementById('add-thought-modal');
|
||||||
@@ -923,13 +923,13 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
alert('Fehler beim Hinzufügen des Gedankens.');
|
alert('Fehler beim Hinzufügen des Gedankens.');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
alert('Bitte wähle zuerst einen Knoten aus.');
|
alert('Bitte wähle zuerst einen Knoten aus.');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('connect-btn').addEventListener('click', function() {
|
document.getElementById('connect-btn').addEventListener('click', function() {
|
||||||
if (window.mindmap && window.mindmap.selectedNode) {
|
if (window.mindmap && window.mindmap.selectedNode) {
|
||||||
// Speichere den ersten ausgewählten Knoten
|
// Speichere den ersten ausgewählten Knoten
|
||||||
window.mindmap.sourceNode = window.mindmap.selectedNode;
|
window.mindmap.sourceNode = window.mindmap.selectedNode;
|
||||||
@@ -962,7 +962,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
|
|
||||||
// Cursor-Stil ändern, um den Verbindungsmodus anzuzeigen
|
// Cursor-Stil ändern, um den Verbindungsmodus anzuzeigen
|
||||||
document.getElementById('mindmap-container').style.cursor = 'crosshair';
|
document.getElementById('mindmap-container').style.cursor = 'crosshair';
|
||||||
} else {
|
} else {
|
||||||
alert('Bitte wähle zuerst einen Knoten aus.');
|
alert('Bitte wähle zuerst einen Knoten aus.');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1019,8 +1019,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('Fehler beim Speichern des Gedankens:', error);
|
console.error('Fehler beim Speichern des Gedankens:', error);
|
||||||
alert('Fehler beim Speichern des Gedankens. Bitte versuche es erneut.');
|
alert('Fehler beim Speichern des Gedankens. Bitte versuche es erneut.');
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fenstergrößen-Änderung behandeln
|
// Fenstergrößen-Änderung behandeln
|
||||||
Reference in New Issue
Block a user