Compare commits

..

69 Commits

Author SHA1 Message Date
a7bb9563b3 hintergrund Fixes 2025-04-29 20:49:43 +01:00
4e0c470663 Update compiled Python files in __pycache__ for utils module 2025-04-29 20:38:00 +01:00
b5300f74bd Update compiled Python files in __pycache__ for models and utils modules 2025-04-29 20:35:23 +01:00
6a53e621ca ''Über Uns'' 2025-04-29 20:31:02 +01:00
a59ce652af Impressum 2025-04-29 19:26:35 +01:00
27cfc95081 Merge branch 'tills-branch' of https://git.clickcandit.com/marwinm/website into tills-branch 2025-04-29 20:24:41 +02:00
c513666391 Refactor nodeCount to config object" Explanation: This commit message follows the Conventional C... 2025-04-29 20:11:43 +02:00
ae30dbce57 Flask Fix JAHAH 2025-04-29 19:08:49 +01:00
817ddd98e9 chore: Änderungen commited 2025-04-29 20:08:12 +02:00
bfce2fc7b7 chore: Änderungen commited 2025-04-29 20:06:29 +02:00
efbcd567ee chore: Änderungen commited 2025-04-29 20:04:32 +02:00
a873765d08 feat: Implement smooth animation with adjustable pulse and flow speeds 2025-04-29 20:02:51 +02:00
efbcadb95a chore: Änderungen commited 2025-04-29 20:00:58 +02:00
da3ccaffe9 Refactor nodeCount configuration in NeuralNetworkBackground.js 2025-04-29 19:59:09 +02:00
f4e04573bd chore: Änderungen commited 2025-04-29 19:57:44 +02:00
aa253f3871 chore: Änderungen commited 2025-04-29 19:27:29 +02:00
cfd6a25b21 📝 Commit Message: "Refactor app module binary files using Conventional Commits (feat)" Explanat... 2025-04-29 19:25:18 +02:00
d307763007 Aktualisiere die Konfiguration der neuronalen Netzwerk-Hintergrundanimation in neural-network-background.js, um die Anzahl der Knoten, Variationen, Geschwindigkeiten und Sichtbarkeiten anzupassen. Füge neue Parameter für aktive Flows und Flussweiterleitung hinzu, um die Animation dynamischer zu gestalten. Optimiere die Logik zur Verwaltung aktiver Flows und verbessere die Sichtbarkeit von Verbindungen für ein flüssigeres Benutzererlebnis. 2025-04-29 15:19:33 +02:00
d7e6912e08 Aktualisiere die Umgebungsvariablen in .env mit neuen Werten für SECRET_KEY und OPENAI_API_KEY. Entferne die alte Instanz von systades.db und aktualisiere die neuronale Netzwerk-Hintergrundanimation in neural-network-background.js mit verbesserten visuellen Effekten, einschließlich einer neuen Zickzack-Bewegung für Blitze und subtileren Glüheffekten. Implementiere eine sanftere Ausblendanimation für das Canvas-Element und verbessere die Funkenanimationen. Optimiere die Cluster-Generierung und -Verteilung für eine flüssigere Benutzererfahrung. 2025-04-29 14:58:54 +02:00
ffe96074f4 Verbessere die Funktionalität des neuronalen Netzwerk-Hintergrunds in neural-network-background.js durch die Einführung einer flexiblen Konfigurationsstruktur, die benutzerdefinierte Optionen unterstützt. Optimiere die Cluster-Generierung und -Verteilung, verbessere die Verbindungslogik zwischen Knoten und implementiere erweiterte Fehlerbehandlung in den API-Funktionen in mindmap.js. Füge Fallback-Knoten hinzu, um die Benutzererfahrung zu verbessern, und implementiere visuelle Rückmeldungen für die Initialisierung der Mindmap. 2025-04-29 12:32:18 +02:00
49ccf3908a Merge remote-tracking branch 'origin/tills-branch' into tills-branch 2025-04-29 10:28:36 +02:00
9514645904 keine ahnung ehrlich 2025-04-29 10:28:15 +02:00
63f45abb3e background, mindmap 2025-04-29 10:27:07 +02:00
7d74b5a7bf Implement Flask-Migrate for database migrations in app.py, disable CSRF protection, and update requirements.txt to include Flask-Migrate. Remove obsolete systades.db file and add migration configuration files for Alembic. 2025-04-28 21:41:38 +02:00
55f2553780 Update ROADMAP.md, remove CORS & flask-cors from app.py, and update requirements.txt: no longer use CORS for Flask-SocketIO. 2025-04-28 21:24:11 +02:00
0852ea070b Add Flask-CORS and SocketIO for real-time updates, refactor database handling to use a temporary Flask app; improve error handling with @app.errorhandler decorators. 2025-04-28 15:21:11 +02:00
7a0533ac09 Verbessere die Funktionalität des Chat-Assistenten in app.py: Aktualisiere die Systemnachricht mit spezifischen Informationen zur Systades-Wissensdatenbank und erweitere die API-Nachrichtenformatierung. Füge Unterstützung für ausgewählte Elemente aus der Datenbank hinzu und erhöhe die maximale Tokenanzahl für detailliertere Antworten. Implementiere eine neue JavaScript-Datei für eine neuronale Netzwerk-Hintergrundanimation und verbessere die CSS-Stile für den Light Mode. Optimiere die Benutzeroberfläche und die Lesbarkeit in beiden Modi. Aktualisiere die Grundstile für eine konsistente Darstellung. 2025-04-28 14:49:02 +02:00
65c44ab371 Refactor node relationship handling in app.py and introduce new routes for thoughts association with nodes. 2025-04-28 13:20:41 +02:00
5399169b11 jo 2025-04-27 21:22:28 +01:00
05f6f149ad Update ROADMAP.md: Mark completion of Phases 1, 2, and 3 with detailed task lists, introduce Phase 4 for user-defined mind maps, and outline future tasks including tagging and source management. Enhance visual design and UX elements, and update current improvements and future tasks for Q3 2024. 2025-04-27 18:19:43 +02:00
12ed413c04 Update README.md: Enhance error handling for robust data processing and node references, improve connection detection between nodes, and update current status to reflect 75% completion of Phase 3. Document critical bug fixes in node visualization and API data format handling. 2025-04-27 18:18:34 +02:00
ccf5a1d678 Update README.md: Add WebGL support for animated background effects, complete Phase 2 design tasks, and enhance MindMap features with improved clustering and visualization. Update current status and next steps to reflect recent progress. 2025-04-27 18:17:52 +02:00
eb23d638e6 Merge remote-tracking branch 'origin/tills-branch' into tills-branch 2025-04-27 18:16:30 +02:00
750dba2d6f Update requirements.txt: Remove specific version for openai package to allow for flexibility in dependency management. 2025-04-27 18:10:41 +01:00
6aa3780a96 Enhance MindMapVisualization functionality: Implement connection count updates for nodes upon data processing and fallback scenarios. Refactor connected node retrieval methods to improve safety and reliability by ensuring valid node IDs. Introduce a new method for checking links between nodes, enhancing overall robustness of the mind map visualization logic. 2025-04-27 18:16:09 +02:00
8890a62026 Merge remote-tracking branch 'origin/tills-branch' into tills-branch 2025-04-27 17:57:40 +02:00
6cf9b2a627 Update compiled Python files in __pycache__: Refresh app.cpython-313.pyc and models.cpython-313.pyc to reflect recent code changes and optimizations. 2025-04-27 17:14:32 +01:00
4f6aea8e20 Update neural network background animation: Enhance color visibility and node dynamics for improved visual effects. Introduce clustering for node positioning and optimize connection rendering with increased opacity and strength. Remove old database file and update systades.db for better data management. 2025-04-27 17:57:04 +02:00
e5f485d9d7 Update COMMON_ERRORS.md: Add a new entry for common error 'D' and maintain clarity in the documentation of frequent issues and their solutions. 2025-04-27 17:15:54 +02:00
cf3fc09a63 Merge remote-tracking branch 'origin/tills-branch' into tills-branch 2025-04-27 17:15:47 +02:00
10747a8336 Add new database file: Create systades.db to support application data storage and management. 2025-04-27 17:00:27 +01:00
7eb958f3c8 Update app.py and COMMON_ERRORS.md for improved clarity and functionality: Correct the comment in app.py from "Kontext-Prozessor" to "Context-Prozessor" for better understanding. Enhance COMMON_ERRORS.md by adding new common errors and solutions related to TypeScript usage, OAuth implementation, and neural network background animation issues. Update mindmap page scripts to ensure proper global availability of functions and improve error handling for user notifications. Adjust template references for Tailwind CSS and Alpine.js to support both CDN and local versions, ensuring better resource loading and compatibility. 2025-04-27 17:14:48 +02:00
4a3092a4d2 Update OpenAI API key and enhance app functionality: Replace the OpenAI API key in the .env file for improved access. Refactor app.py to include error handling for missing API keys and implement dark mode functionality with session management. Update README.md to reflect the use of Tailwind CSS via CDN and document the Content Security Policy (CSP) adjustments. Enhance mindmap data loading with a new API endpoint for refreshing data, ensuring better user experience during database connection issues. Update styles and templates for improved UI consistency and responsiveness. 2025-04-27 16:56:16 +02:00
2d8cdc052f Refactor chat interface in index.html and main.js: Improve user interaction by optimizing chat message rendering and enhancing initialization logic for the ChatGPT assistant. Update styles for better responsiveness and visual appeal, ensuring seamless integration with existing functionalities. 2025-04-27 15:12:52 +02:00
968515ce2b Overhaul website to modernize design, integrate SVG visualizations, and enhance KI functionality; update documentation for MindMapProjekt. 2025-04-27 15:09:29 +02:00
88f8e98df0 Enhance Neural Network Background Animation: Introduce subtle flowing network aesthetics with improved color schemes and animations. Update canvas handling to ensure proper layering and visibility. Implement new flow animations along connections for a more dynamic visual experience. Adjust node and connection properties for a cleaner, more organic look. Ensure compatibility with dark mode and optimize rendering methods for both WebGL and Canvas. Update styles to maintain transparency across themes. 2025-04-27 12:16:57 +01:00
e5409eef68 Remove unused background scripts and assets: Delete background.js, network-animation.js, network-background.js, and associated media files to streamline the project. Update base.html to reflect changes in script references and ensure proper functionality of the dark mode theme. 2025-04-27 11:51:35 +01:00
013bf76446 Merge branch 'tills-branch' of https://git.clickcandit.com/marwinm/website into tills-branch 2025-04-27 11:15:48 +01:00
808a3c7bbe Update compiled Python files and database: Refresh app and init_db bytecode files, and update mindmap database to reflect recent changes and improvements. 2025-04-27 10:14:44 +01:00
d117978005 Update GPT model to 'gpt-4o-mini' for chat functionality in app.py 2025-04-27 08:03:55 +02:00
48d8463481 Enhance chat interface styling in index.html: Add animations for chat messages and typing indicators, implement smooth scrolling for chat messages, and customize scrollbar appearance. Introduce hover effects for quick query buttons to improve user interaction and visual feedback. 2025-04-27 08:00:53 +02:00
08314ec703 Enhance embedded ChatGPT assistant functionality: Integrate a new chat interface within the index.html template, allowing users to interact with the assistant directly. Update main.js to ensure proper initialization and reference management. Improve user experience with quick query buttons and a responsive chat layout, while maintaining existing functionality in the application. 2025-04-27 08:00:16 +02:00
0bb7d8d0dc Add ChatGPT assistant initialization in main.js: Integrate a new ChatGPTAssistant instance during the MindMap application initialization, ensuring a global reference for enhanced user interaction. This update improves the functionality of the chat feature within the application. 2025-04-27 07:52:23 +02:00
4a28c2c453 Refactor chat_with_assistant function to support messages array input: Enhance the chatbot API by allowing an array of messages for context, extracting system messages, and updating the response format. Maintain backward compatibility with the previous prompt structure while improving error handling for empty inputs. 2025-04-27 07:49:40 +02:00
66d987857a Remove deprecated database management scripts and admin user creation functionality: Delete create_admin.py, fix_db.py, rebuild_db.py, and test_db.py to streamline the project structure and eliminate unused code. Update README.md with installation instructions and management tools for improved user guidance. 2025-04-27 07:46:48 +02:00
d58aba26c2 Refactor OpenAI integration and enhance mindmap UI: Update OpenAI client initialization to use a dedicated class, streamline API key management, and improve loading animations in the mindmap template. Add a modal for adding new thoughts with enhanced user feedback and error handling, while updating the loading overlay for better visual appeal and responsiveness. 2025-04-27 07:43:03 +02:00
8f0a6d4372 Update environment configuration and enhance app functionality: Add detailed comments to the .env file for better clarity, implement a route to reload environment variables dynamically, and ensure the .env file is loaded with force in the app. Remove obsolete build and development scripts to streamline the project structure. Update setup script to create a .env file if it doesn't exist, prompting users to configure necessary values. 2025-04-27 07:28:05 +02:00
5372fe220e Add flash message API and enhance mindmap visualization: Implement a new API endpoint for retrieving flash messages, integrate flash message display in the mindmap visualization, and improve user feedback with dynamic notifications. Update UI elements for better responsiveness and visual appeal, while removing obsolete background image references. 2025-04-27 07:18:32 +02:00
11ab15127c Refactor mindmap visualization and enhance user authentication UI: Implement API calls to load mindmap data dynamically, process hierarchical data into nodes and links, and improve error handling. Update login and registration templates for a modern design with enhanced validation and user experience. Remove obsolete network background images. 2025-04-27 07:08:38 +02:00
0705ecce59 Implement database path configuration and enhance category management: Update database URI to use an absolute path, ensure directory creation for the database, and implement default category creation on initialization. Add new routes for searching thoughts and user account management, while improving the UI with navigation updates for better accessibility. 2025-04-27 07:02:54 +02:00
1c59b0b616 node entfernt 2025-04-27 06:49:59 +02:00
d42c43db50 Enhance footer layout and mindmap functionality: Revamp footer structure with improved grid layout, add social media icons, and implement a newsletter subscription form. Update mindmap template to use SVG background, streamline script loading, and enhance visualization initialization with new event handlers for user interactions. 2025-04-27 06:33:01 +02:00
e46264b201 Fix: network background loading and fallback mechanism: Implement a retry logic for loading the network background image with a maximum of two attempts, first trying the SVG version and then falling back to a JPG if necessary. Add a fallback background drawing function to maintain visual continuity when image loading fails. Update placeholder comment in JPG file to reflect the use of an SVG alternative. 2025-04-27 06:26:10 +02:00
74307ba345 Add user account route and bookmark functionality: Implement '/my-account' route for user bookmarks and personal mindmap, enhance mindmap visualization with bookmark management, and update UI elements for better user experience. 2025-04-26 18:51:53 +01:00
14474c4eab Refactor UI and enhance functionality: Update welcome messages and input placeholders in the chat assistant, implement connection count updates in the mindmap visualization, and change branding from "MindMap" to "Systades" across templates for a cohesive user experience. 2025-04-26 18:40:27 +01:00
4797cc3b72 Optimize network background animation and enhance UI styles: Adjust animation speeds for smoother transitions, implement dark mode support, and improve card and navbar styles with glassmorphism effects. Update HTML structure for better responsiveness and visual appeal. 2025-04-25 23:39:41 +02:00
ab280b55af Update requirements and enhance mindmap UI: Comment out Pillow dependency, add network background script, and implement new styles and animations for improved visual effects in the mindmap template. 2025-04-25 21:30:35 +01:00
84b492d8d2 Update README and enhance application functionality: Add detailed installation instructions, integrate OpenAI GPT for the AI assistant, implement error handling for various HTTP errors, and improve the admin interface with user management features. Refactor mindmap visualization and enhance UI with modern design elements. 2025-04-25 00:30:04 +02:00
b0db3398f2 Erweitere die Anwendung um neue Funktionen: Implementiere eine dauerhafte Sitzung für den Dark Mode, füge Benutzer- und Gedankenbewertung hinzu, verbessere die Benutzeroberfläche und aktualisiere die Datenbankinitialisierung mit Beispielbenutzern und Gedanken. Optimiere die Templates für ein modernes Design und verbessere die Suchfunktionalität. 2025-04-24 18:08:04 +02:00
24 changed files with 868 additions and 877 deletions

BIN
.env

Binary file not shown.

View File

@@ -1,31 +0,0 @@
# Dockerfile
FROM python:3.11-slim
# Arbeitsverzeichnis in Container
WORKDIR /app
# Systemabhängigkeiten (falls erforderlich)
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 . .
# Exponiere Port 5000 für Flask
EXPOSE 5000
# Setze Umgebungsvariablen
ENV FLASK_APP=app.py
ENV FLASK_ENV=production
# Wenn eine .env im Arbeitsverzeichnis vorhanden ist, wird sie automatisch von Flask geladen
# Startkommando
CMD ["python", "app.py"]

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,18 +0,0 @@
version: '3.9'
services:
web:
build: .
image: systades_app:latest
container_name: systades_app
restart: always
env_file:
- .env
ports:
- "5000:5000"
volumes:
- .:/app:ro
- db_data:/app/database
volumes:
db_data:

View File

@@ -1,22 +0,0 @@
#!/usr/bin/env bash
set -e
# Alte Container stoppen und entfernen
if [ $(docker ps -aq --filter "name=systades_app" | wc -l) -gt 0 ]; then
docker rm -f systades_app || true
fi
if [ $(docker ps -aq --filter "name=systades_db" | wc -l) -gt 0 ]; then
docker rm -f systades_db || true
fi
# Alte Images löschen
docker rmi -f systades_app:latest || true
# Docker-Compose Setup neu bauen
docker-compose build --no-cache
# Docker-Compose neu starten
docker-compose up -d --force-recreate
# Ausgabe
echo "Systades-Anwendung ist jetzt unter http://localhost:5000 erreichbar."

View File

@@ -40,8 +40,8 @@
--light-bg: #f9fafb; --light-bg: #f9fafb;
--light-text: #1e293b; --light-text: #1e293b;
--light-heading: #0f172a; --light-heading: #0f172a;
--light-primary: #7c3aed; --light-primary: #3b82f6;
--light-primary-hover: #6d28d9; --light-primary-hover: #4f46e5;
--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,231 +524,3 @@ 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;
}

View File

@@ -38,9 +38,8 @@ function initMindmapPage() {
console.log('D3 Bibliothek verfügbar:', typeof d3 !== 'undefined'); console.log('D3 Bibliothek verfügbar:', typeof d3 !== 'undefined');
console.log('MindMapVisualization verfügbar:', typeof MindMapVisualization !== 'undefined'); console.log('MindMapVisualization verfügbar:', typeof MindMapVisualization !== 'undefined');
const mindmapContainer = document.getElementById('cy'); const mindmapContainer = document.getElementById('mindmap-container');
const thoughtsContainer = document.getElementById('thoughts-container'); const thoughtsContainer = document.getElementById('thoughts-container');
const nodeDetailsContainer = document.getElementById('node-details');
if (!mindmapContainer) { if (!mindmapContainer) {
console.error('Mindmap-Container nicht gefunden!'); console.error('Mindmap-Container nicht gefunden!');
@@ -51,40 +50,35 @@ function initMindmapPage() {
// Prüfe, ob D3.js geladen ist // Prüfe, ob D3.js geladen ist
if (typeof d3 === 'undefined') { if (typeof d3 === 'undefined') {
console.error('D3.js ist nicht geladen!'); console.error('D3.js ist nicht geladen!');
if (mindmapContainer) { mindmapContainer.innerHTML = `
mindmapContainer.innerHTML = ` <div class="glass-effect p-6 text-center">
<div class="glass-effect p-6 text-center"> <div class="text-red-500 mb-4">
<div class="text-red-500 mb-4"> <i class="fa-solid fa-triangle-exclamation text-4xl"></i>
<i class="fa-solid fa-triangle-exclamation text-4xl"></i>
</div>
<p class="text-xl">D3.js konnte nicht geladen werden. Bitte laden Sie die Seite neu.</p>
</div> </div>
`; <p class="text-xl">D3.js konnte nicht geladen werden. Bitte laden Sie die Seite neu.</p>
} </div>
`;
return; return;
} }
// Prüfe, ob MindMapVisualization definiert ist // Prüfe, ob MindMapVisualization definiert ist
if (typeof MindMapVisualization === 'undefined') { if (typeof MindMapVisualization === 'undefined') {
console.error('MindMapVisualization-Klasse ist nicht definiert!'); console.error('MindMapVisualization-Klasse ist nicht definiert!');
if (mindmapContainer) { mindmapContainer.innerHTML = `
mindmapContainer.innerHTML = ` <div class="glass-effect p-6 text-center">
<div class="glass-effect p-6 text-center"> <div class="text-red-500 mb-4">
<div class="text-red-500 mb-4"> <i class="fa-solid fa-triangle-exclamation text-4xl"></i>
<i class="fa-solid fa-triangle-exclamation text-4xl"></i>
</div>
<p class="text-xl">MindMap-Visualisierung konnte nicht geladen werden. Bitte laden Sie die Seite neu.</p>
</div> </div>
`; <p class="text-xl">MindMap-Visualisierung konnte nicht geladen werden. Bitte laden Sie die Seite neu.</p>
} </div>
`;
return; return;
} }
// Erstelle die Mindmap-Visualisierung // Erstelle die Mindmap-Visualisierung
let mindmap;
try { try {
console.log('Versuche, MindMapVisualization zu erstellen...'); console.log('Versuche, MindMapVisualization zu erstellen...');
mindmap = new MindMapVisualization('#cy', { const mindmap = new MindMapVisualization('#mindmap-container', {
height: 600, height: 600,
onNodeClick: handleNodeClick onNodeClick: handleNodeClick
}); });
@@ -95,51 +89,38 @@ function initMindmapPage() {
// Lade die Mindmap-Daten // Lade die Mindmap-Daten
mindmap.loadData(); mindmap.loadData();
console.log('MindMapVisualization erfolgreich erstellt und geladen'); console.log('MindMapVisualization erfolgreich erstellt und geladen');
// Dark Mode Listener registrieren
registerDarkModeListener(mindmap);
} catch (error) { } catch (error) {
console.error('Fehler beim Erstellen der MindMapVisualization:', error); console.error('Fehler beim Erstellen der MindMapVisualization:', error);
if (mindmapContainer) { mindmapContainer.innerHTML = `
mindmapContainer.innerHTML = ` <div class="glass-effect p-6 text-center">
<div class="glass-effect p-6 text-center"> <div class="text-red-500 mb-4">
<div class="text-red-500 mb-4"> <i class="fa-solid fa-triangle-exclamation text-4xl"></i>
<i class="fa-solid fa-triangle-exclamation text-4xl"></i>
</div>
<p class="text-xl">Fehler beim Erstellen der Mindmap-Visualisierung:</p>
<p class="text-md mt-2">${error.message}</p>
</div> </div>
`; <p class="text-xl">Fehler beim Erstellen der Mindmap-Visualisierung:</p>
} <p class="text-md mt-2">${error.message}</p>
</div>
`;
return; return;
} }
// Suchfunktion für die Mindmap // Suchfunktion für die Mindmap
const searchInput = document.getElementById('search-mindmap'); const searchInput = document.getElementById('mindmap-search');
if (searchInput) { if (searchInput) {
searchInput.addEventListener('input', function(e) { searchInput.addEventListener('input', function(e) {
mindmap.filterBySearchTerm(e.target.value); mindmap.filterBySearchTerm(e.target.value);
}); });
} }
// UI-Elemente initialisieren
initializeMindmapButtons(mindmap);
/** /**
* Behandelt Klicks auf Mindmap-Knoten * Behandelt Klicks auf Mindmap-Knoten
*/ */
async function handleNodeClick(node) { async function handleNodeClick(node) {
// Details im Detailbereich anzeigen
if (nodeDetailsContainer) {
updateNodeDetails(node);
}
if (!thoughtsContainer) return; if (!thoughtsContainer) return;
// Zeige Lade-Animation // Zeige Lade-Animation
thoughtsContainer.innerHTML = ` thoughtsContainer.innerHTML = `
<div class="flex justify-center items-center p-4"> <div class="flex justify-center items-center p-12">
<div class="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-primary-500"></div> <div class="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-primary-400"></div>
</div> </div>
`; `;
@@ -159,110 +140,37 @@ function initMindmapPage() {
} catch (error) { } catch (error) {
console.error('Fehler beim Laden der Gedanken:', error); console.error('Fehler beim Laden der Gedanken:', error);
thoughtsContainer.innerHTML = ` thoughtsContainer.innerHTML = `
<div class="p-4"> <div class="glass-effect p-6 text-center">
<div class="text-red-500 mb-2"> <div class="text-red-500 mb-4">
<i class="fa-solid fa-triangle-exclamation text-xl"></i> <i class="fa-solid fa-triangle-exclamation text-4xl"></i>
</div> </div>
<p class="text-sm">Fehler beim Laden der Gedanken.</p> <p class="text-xl">Fehler beim Laden der Gedanken.</p>
<p class="text-gray-300">Bitte versuchen Sie es später erneut.</p>
</div> </div>
`; `;
} }
} }
/**
* Aktualisiert den Node-Details-Bereich mit Informationen zum ausgewählten Knoten
*/
function updateNodeDetails(node) {
if (!nodeDetailsContainer) return;
const isDarkMode = document.documentElement.classList.contains('dark');
const textClass = isDarkMode ? 'text-gray-300' : 'text-gray-600';
nodeDetailsContainer.innerHTML = `
<div>
<h3 class="text-lg font-semibold mb-2 ${isDarkMode ? 'text-white' : 'text-gray-800'}">
${node.name}
</h3>
<div class="mb-3">
<span class="px-2 py-1 text-xs rounded-full ${getCategoryBadgeClass(node.category)}">
${node.category || 'Keine Kategorie'}
</span>
</div>
<p class="text-sm ${textClass}">
${node.description || 'Keine Beschreibung verfügbar.'}
</p>
<div class="mt-4 text-sm">
<div class="flex justify-between items-center mb-1">
<span class="${textClass}">Erstellt:</span>
<span class="${textClass}">${formatDate(node.created_at || new Date())}</span>
</div>
<div class="flex justify-between items-center">
<span class="${textClass}">Verbindungen:</span>
<span class="${textClass}">${node.connections || 0}</span>
</div>
</div>
</div>
`;
}
/**
* Formatiert ein Datum in ein lesbares Format
*/
function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleDateString('de-DE', {
year: 'numeric',
month: '2-digit',
day: '2-digit'
});
}
/**
* Liefert eine CSS-Klasse für die Kategorie-Badge basierend auf der Kategorie
*/
function getCategoryBadgeClass(category) {
if (!category) return 'bg-gray-500 text-white';
const categories = {
'Konzept': 'bg-blue-500 text-white',
'Theorie': 'bg-purple-500 text-white',
'Methode': 'bg-green-500 text-white',
'Person': 'bg-yellow-500 text-black',
'Ereignis': 'bg-red-500 text-white',
'Referenz': 'bg-indigo-500 text-white'
};
return categories[category] || 'bg-gray-500 text-white';
}
/** /**
* Rendert die Gedanken in den Container * Rendert die Gedanken in den Container
*/ */
function renderThoughts(thoughts, nodeName) { function renderThoughts(thoughts, nodeName) {
const isDarkMode = document.documentElement.classList.contains('dark');
// Wenn keine Gedanken vorhanden sind // Wenn keine Gedanken vorhanden sind
if (thoughts.length === 0) { if (thoughts.length === 0) {
thoughtsContainer.innerHTML = ` thoughtsContainer.innerHTML = `
<div class="text-center py-4"> <div class="glass-effect p-6 text-center">
<div class="text-blue-400 mb-2"> <div class="text-blue-400 mb-4">
<i class="fa-solid fa-info-circle text-xl"></i> <i class="fa-solid fa-info-circle text-4xl"></i>
</div> </div>
<p class="text-sm ${isDarkMode ? 'text-gray-300' : 'text-gray-600'}"> <p class="text-xl">Keine Gedanken für "${nodeName}" vorhanden.</p>
Keine Gedanken zu "${nodeName}" vorhanden. <button id="add-thought-btn" class="btn-primary mt-4">
</p> <i class="fa-solid fa-plus mr-2"></i> Gedanke hinzufügen
<button id="add-thought-btn" class="mt-3 px-3 py-1.5 text-xs rounded-lg transition-colors duration-200"
${isDarkMode ?
'class="bg-primary-600 hover:bg-primary-700 text-white"' :
'class="bg-primary-500 hover:bg-primary-600 text-white"'}>
<i class="fa-solid fa-plus mr-1"></i> Gedanke hinzufügen
</button> </button>
</div> </div>
`; `;
// Event-Listener für den Button // Event-Listener für den Button
document.getElementById('add-thought-btn')?.addEventListener('click', () => { document.getElementById('add-thought-btn').addEventListener('click', () => {
openAddThoughtModal(nodeName); openAddThoughtModal(nodeName);
}); });
@@ -271,29 +179,24 @@ function initMindmapPage() {
// Gedanken anzeigen // Gedanken anzeigen
thoughtsContainer.innerHTML = ` thoughtsContainer.innerHTML = `
<div class="flex justify-between items-center mb-3"> <div class="flex justify-between items-center mb-6">
<h3 class="text-sm font-semibold ${isDarkMode ? 'text-white' : 'text-gray-800'}"> <h2 class="text-xl font-bold text-white">Gedanken zu "${nodeName}"</h2>
Gedanken zu "${nodeName}" <button id="add-thought-btn" class="btn-primary">
</h3> <i class="fa-solid fa-plus mr-2"></i> Neuer Gedanke
<button id="add-thought-btn" class="px-2 py-1 text-xs rounded-lg transition-colors duration-200"
${isDarkMode ?
'class="bg-primary-600 hover:bg-primary-700 text-white"' :
'class="bg-primary-500 hover:bg-primary-600 text-white"'}>
<i class="fa-solid fa-plus mr-1"></i> Neu
</button> </button>
</div> </div>
<div class="space-y-3 max-h-80 overflow-y-auto pr-1" id="thoughts-grid"></div> <div class="grid grid-cols-1 gap-4" id="thoughts-grid"></div>
`; `;
// Button-Event-Listener // Button-Event-Listener
document.getElementById('add-thought-btn')?.addEventListener('click', () => { document.getElementById('add-thought-btn').addEventListener('click', () => {
openAddThoughtModal(nodeName); openAddThoughtModal(nodeName);
}); });
// Gedanken-Karten rendern // Gedanken-Karten rendern
const thoughtsGrid = document.getElementById('thoughts-grid'); const thoughtsGrid = document.getElementById('thoughts-grid');
thoughts.forEach((thought, index) => { thoughts.forEach((thought, index) => {
const card = createThoughtCard(thought, isDarkMode); const card = createThoughtCard(thought);
// Animation verzögern für gestaffeltes Erscheinen // Animation verzögern für gestaffeltes Erscheinen
setTimeout(() => { setTimeout(() => {
@@ -308,149 +211,567 @@ function initMindmapPage() {
/** /**
* Erstellt eine Gedanken-Karte * Erstellt eine Gedanken-Karte
*/ */
function createThoughtCard(thought, isDarkMode) { function createThoughtCard(thought) {
const card = document.createElement('div'); const card = document.createElement('div');
card.className = `relative transition-all duration-300 opacity-0 translate-y-4 transform rounded-lg overflow-hidden p-3 border-l-4 ${isDarkMode ? 'bg-gray-800/70' : 'bg-white/90'} border-l-[${thought.color_code || '#4080ff'}]`; card.className = 'card transition-all duration-300 opacity-0 translate-y-4 transform hover:shadow-lg border-l-4';
card.style.borderLeftColor = thought.color_code || '#4080ff';
// Karten-Inhalt // Karten-Inhalt
card.innerHTML = ` card.innerHTML = `
<div class="flex justify-between items-start mb-1"> <div class="p-4">
<h4 class="text-sm font-semibold ${isDarkMode ? 'text-white' : 'text-gray-800'}">${thought.title}</h4> <div class="flex justify-between items-start">
<div class="text-xs ${isDarkMode ? 'text-gray-400' : 'text-gray-500'}">${formatDate(thought.timestamp)}</div> <h3 class="text-lg font-bold text-white">${thought.title}</h3>
</div> <div class="text-sm text-gray-400">${thought.timestamp}</div>
<div class="prose dark:prose-invert prose-sm">
<p class="text-xs line-clamp-2 ${isDarkMode ? 'text-gray-300' : 'text-gray-600'}">${thought.content}</p>
</div>
${thought.keywords ? `
<div class="flex flex-wrap gap-1 mt-2">
${thought.keywords.split(',').slice(0, 2).map(keyword =>
`<span class="px-2 py-0.5 text-[10px] rounded-full ${isDarkMode ? 'bg-gray-700 text-gray-300' : 'bg-gray-200 text-gray-700'}">${keyword.trim()}</span>`
).join('')}
${thought.keywords.split(',').length > 2 ?
`<span class="px-2 py-0.5 text-[10px] rounded-full ${isDarkMode ? 'bg-gray-700 text-gray-300' : 'bg-gray-200 text-gray-700'}">+${thought.keywords.split(',').length - 2}</span>` :
''}
</div> </div>
` : ''} <div class="prose dark:prose-invert mt-2">
<div class="mt-2 flex justify-between items-center"> <p>${thought.content}</p>
<div class="text-xs ${isDarkMode ? 'text-gray-400' : 'text-gray-500'}"> </div>
<i class="fa-solid fa-user text-xs mr-1"></i> ${thought.author} ${thought.keywords ? `
<div class="flex flex-wrap gap-1 mt-3">
${thought.keywords.split(',').map(keyword =>
`<span class="px-2 py-1 text-xs rounded-full bg-secondary-700 text-white">${keyword.trim()}</span>`
).join('')}
</div>
` : ''}
<div class="mt-4 flex justify-between items-center">
<div class="text-sm text-gray-400">
<i class="fa-solid fa-user mr-1"></i> ${thought.author}
</div>
<div class="flex space-x-2">
<button class="text-sm px-2 py-1 rounded hover:bg-white/10 transition-colors"
onclick="showComments(${thought.id})">
<i class="fa-solid fa-comments mr-1"></i> Kommentare
</button>
<button class="text-sm px-2 py-1 rounded hover:bg-white/10 transition-colors"
onclick="showRelations(${thought.id})">
<i class="fa-solid fa-diagram-project mr-1"></i> Beziehungen
</button>
</div>
</div> </div>
<button class="viewThought-btn text-xs px-2 py-1 rounded ${isDarkMode ? 'hover:bg-white/10' : 'hover:bg-gray-200'} transition-colors"
data-thought-id="${thought.id}">
<i class="fa-solid fa-eye mr-1"></i> Ansehen
</button>
</div> </div>
`; `;
// Event-Listener für die Detailansicht
card.querySelector('.viewThought-btn').addEventListener('click', function() {
const thoughtId = this.getAttribute('data-thought-id');
showThoughtDetail(thoughtId);
});
return card; return card;
} }
/** /**
* Event-Listener für Dark-Mode-Änderungen registrieren * Öffnet das Modal zum Hinzufügen eines neuen Gedankens
*/ */
function registerDarkModeListener(mindmap) { function openAddThoughtModal(nodeName) {
document.addEventListener('darkModeToggled', function(event) { // Node-Information extrahieren
const isDark = event.detail.isDark; let nodeId, nodeTitle;
console.log('Dark mode changed to:', isDark);
// Mindmap-Styling aktualisieren if (typeof nodeName === 'string') {
if (mindmap && mindmap.updateColorScheme) { // Wenn nur ein String übergeben wurde
mindmap.updateColorScheme(isDark); nodeTitle = nodeName;
// Versuche nodeId aus der Mindmap zu finden
const nodeElement = d3.selectAll('.node-group').filter(d => d.name === nodeName);
if (nodeElement.size() > 0) {
nodeId = nodeElement.datum().id;
} }
} else if (typeof nodeName === 'object') {
// Wenn ein Node-Objekt übergeben wurde
nodeId = nodeName.id;
nodeTitle = nodeName.name;
} else {
console.error('Ungültiger Node-Parameter', nodeName);
return;
}
// UI-Elemente aktualisieren, falls notwendig // Modal-Struktur erstellen
if (nodeDetailsContainer) { const modal = document.createElement('div');
const selectedNode = mindmap.getSelectedNode(); modal.className = 'fixed inset-0 z-50 flex items-center justify-center p-4';
if (selectedNode) { modal.innerHTML = `
updateNodeDetails(selectedNode); <div class="absolute inset-0 bg-black/50 backdrop-blur-sm" id="modal-backdrop"></div>
<div class="glass-effect relative rounded-lg shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto z-10 transform transition-all">
<div class="p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-xl font-bold text-white flex items-center">
<span class="w-3 h-3 rounded-full bg-primary-400 mr-2"></span>
Neuer Gedanke zu "${nodeTitle}"
</h3>
<button id="close-modal-btn" class="text-gray-400 hover:text-white transition-colors">
<i class="fa-solid fa-xmark text-xl"></i>
</button>
</div>
<form id="add-thought-form" class="space-y-4">
<input type="hidden" id="node_id" name="node_id" value="${nodeId || ''}">
<div>
<label for="title" class="block text-sm font-medium text-gray-300">Titel</label>
<input type="text" id="title" name="title" required
class="mt-1 block w-full rounded-md bg-dark-700 border border-dark-500 text-white p-2.5 focus:ring-2 focus:ring-primary-500 focus:border-transparent">
</div>
<div>
<label for="content" class="block text-sm font-medium text-gray-300">Inhalt</label>
<textarea id="content" name="content" rows="5" required
class="mt-1 block w-full rounded-md bg-dark-700 border border-dark-500 text-white p-2.5 focus:ring-2 focus:ring-primary-500 focus:border-transparent"></textarea>
</div>
<div>
<label for="keywords" class="block text-sm font-medium text-gray-300">Schlüsselwörter (kommagetrennt)</label>
<input type="text" id="keywords" name="keywords"
class="mt-1 block w-full rounded-md bg-dark-700 border border-dark-500 text-white p-2.5 focus:ring-2 focus:ring-primary-500 focus:border-transparent">
</div>
<div>
<label for="abstract" class="block text-sm font-medium text-gray-300">Zusammenfassung (optional)</label>
<textarea id="abstract" name="abstract" rows="2"
class="mt-1 block w-full rounded-md bg-dark-700 border border-dark-500 text-white p-2.5 focus:ring-2 focus:ring-primary-500 focus:border-transparent"></textarea>
</div>
<div>
<label for="color_code" class="block text-sm font-medium text-gray-300">Farbcode</label>
<div class="flex space-x-2 mt-1">
<input type="color" id="color_code" name="color_code" value="#4080ff"
class="h-10 w-10 rounded bg-dark-700 border border-dark-500">
<select id="predefined_colors"
class="block flex-grow rounded-md bg-dark-700 border border-dark-500 text-white p-2.5">
<option value="#4080ff">Blau</option>
<option value="#a040ff">Lila</option>
<option value="#40bf80">Grün</option>
<option value="#ff4080">Rot</option>
<option value="#ffaa00">Orange</option>
<option value="#00ccff">Türkis</option>
</select>
</div>
</div>
<div class="flex justify-between pt-4">
<div class="flex items-center">
<div class="relative">
<button type="button" id="open-relation-btn" class="btn-outline text-sm pl-3 pr-9">
<i class="fa-solid fa-diagram-project mr-2"></i> Verbindung
<i class="fa-solid fa-chevron-down absolute right-3 top-1/2 transform -translate-y-1/2"></i>
</button>
<div id="relation-menu" class="absolute left-0 mt-2 w-60 rounded-md shadow-lg bg-dark-800 ring-1 ring-black ring-opacity-5 z-10 hidden">
<div class="py-1">
<div class="px-3 py-2 text-xs font-semibold text-gray-400 border-b border-dark-600">BEZIEHUNGSTYPEN</div>
<div class="max-h-48 overflow-y-auto">
<button type="button" class="relation-type-btn w-full text-left px-4 py-2 text-sm text-white hover:bg-dark-600" data-type="supports">
<i class="fa-solid fa-circle-arrow-up text-green-400 mr-2"></i> Stützt
</button>
<button type="button" class="relation-type-btn w-full text-left px-4 py-2 text-sm text-white hover:bg-dark-600" data-type="contradicts">
<i class="fa-solid fa-circle-arrow-down text-red-400 mr-2"></i> Widerspricht
</button>
<button type="button" class="relation-type-btn w-full text-left px-4 py-2 text-sm text-white hover:bg-dark-600" data-type="builds_upon">
<i class="fa-solid fa-arrow-right text-blue-400 mr-2"></i> Baut auf auf
</button>
<button type="button" class="relation-type-btn w-full text-left px-4 py-2 text-sm text-white hover:bg-dark-600" data-type="generalizes">
<i class="fa-solid fa-arrow-up-wide-short text-purple-400 mr-2"></i> Verallgemeinert
</button>
<button type="button" class="relation-type-btn w-full text-left px-4 py-2 text-sm text-white hover:bg-dark-600" data-type="specifies">
<i class="fa-solid fa-arrow-down-wide-short text-yellow-400 mr-2"></i> Spezifiziert
</button>
<button type="button" class="relation-type-btn w-full text-left px-4 py-2 text-sm text-white hover:bg-dark-600" data-type="inspires">
<i class="fa-solid fa-lightbulb text-amber-400 mr-2"></i> Inspiriert
</button>
</div>
</div>
</div>
</div>
<input type="hidden" id="relation_type" name="relation_type" value="">
<input type="hidden" id="relation_target" name="relation_target" value="">
</div>
<div class="flex space-x-3">
<button type="button" id="cancel-btn" class="btn-outline">Abbrechen</button>
<button type="submit" class="btn-primary">
<i class="fa-solid fa-save mr-2"></i> Speichern
</button>
</div>
</div>
</form>
</div>
</div>
`;
document.body.appendChild(modal);
// Focus auf das erste Feld setzen
setTimeout(() => {
modal.querySelector('#title').focus();
}, 100);
// Event-Listener hinzufügen
modal.querySelector('#modal-backdrop').addEventListener('click', closeModal);
modal.querySelector('#close-modal-btn').addEventListener('click', closeModal);
modal.querySelector('#cancel-btn').addEventListener('click', closeModal);
// Farbauswahl-Event-Listener
const colorInput = modal.querySelector('#color_code');
const predefinedColors = modal.querySelector('#predefined_colors');
predefinedColors.addEventListener('change', function() {
colorInput.value = this.value;
});
// Beziehungsmenü-Funktionalität
const relationBtn = modal.querySelector('#open-relation-btn');
const relationMenu = modal.querySelector('#relation-menu');
relationBtn.addEventListener('click', function() {
relationMenu.classList.toggle('hidden');
});
// Klick außerhalb des Menüs schließt es
document.addEventListener('click', function(event) {
if (!relationBtn.contains(event.target) && !relationMenu.contains(event.target)) {
relationMenu.classList.add('hidden');
}
});
// Beziehungstyp-Auswahl
const relationTypeBtns = modal.querySelectorAll('.relation-type-btn');
const relationTypeInput = modal.querySelector('#relation_type');
relationTypeBtns.forEach(btn => {
btn.addEventListener('click', function() {
const relationType = this.dataset.type;
relationTypeInput.value = relationType;
// Sichtbare Anzeige aktualisieren
relationBtn.innerHTML = `
<i class="fa-solid fa-diagram-project mr-2"></i>
${this.innerText.trim()}
<i class="fa-solid fa-chevron-down absolute right-3 top-1/2 transform -translate-y-1/2"></i>
`;
// Menü schließen
relationMenu.classList.add('hidden');
});
});
// Form-Submit-Handler
const form = modal.querySelector('#add-thought-form');
form.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(form);
const thoughtData = {
node_id: formData.get('node_id'),
title: formData.get('title'),
content: formData.get('content'),
keywords: formData.get('keywords'),
abstract: formData.get('abstract'),
color_code: formData.get('color_code'),
relation_type: formData.get('relation_type'),
relation_target: formData.get('relation_target')
};
try {
const response = await fetch('/api/thoughts', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(thoughtData)
});
if (!response.ok) {
throw new Error('Fehler beim Speichern des Gedankens.');
} }
}
if (thoughtsContainer) { // Modal schließen
const thoughts = thoughtsContainer.querySelector('#thoughts-grid'); closeModal();
if (thoughts) {
// Einfache Aktualisierung - im Produktionscode würde man das komplexer machen // Gedanken neu laden
const selectedNode = mindmap.getSelectedNode(); if (nodeId) {
if (selectedNode) { handleNodeClick({ id: nodeId, name: nodeTitle });
handleNodeClick(selectedNode); }
}
// Erfolgsbenachrichtigung
if (window.MindMap && window.MindMap.showNotification) {
window.MindMap.showNotification('Gedanke erfolgreich gespeichert.', 'success');
}
} catch (error) {
console.error('Fehler beim Speichern:', error);
if (window.MindMap && window.MindMap.showNotification) {
window.MindMap.showNotification('Fehler beim Speichern des Gedankens.', 'error');
} }
} }
}); });
// Modal schließen
function closeModal() {
modal.classList.add('opacity-0');
setTimeout(() => {
modal.remove();
}, 300);
}
} }
/**
* Initialisiert die Buttons für die Mindmap
*/
function initializeMindmapButtons(mindmap) {
// Zoom-Buttons
document.getElementById('zoomIn')?.addEventListener('click', () => mindmap.zoomIn());
document.getElementById('zoomOut')?.addEventListener('click', () => mindmap.zoomOut());
document.getElementById('zoomReset')?.addEventListener('click', () => mindmap.zoomReset());
// Layout-Button
document.getElementById('reLayout')?.addEventListener('click', () => mindmap.reLayout());
// Export-Button
document.getElementById('exportMindmap')?.addEventListener('click', () => mindmap.exportMindmap());
// Bearbeitungs-Buttons
document.getElementById('addNode')?.addEventListener('click', () => openAddNodeModal(mindmap));
document.getElementById('addEdge')?.addEventListener('click', () => mindmap.startAddEdgeMode());
document.getElementById('editNode')?.addEventListener('click', () => {
const selectedNode = mindmap.getSelectedNode();
if (selectedNode) {
openEditNodeModal(selectedNode, mindmap);
} else {
showNotification('Bitte wählen Sie zuerst einen Knoten aus.', 'warning');
}
});
document.getElementById('deleteNode')?.addEventListener('click', () => {
const selectedNode = mindmap.getSelectedNode();
if (selectedNode) {
confirmDeleteNode(selectedNode, mindmap);
} else {
showNotification('Bitte wählen Sie zuerst einen Knoten aus.', 'warning');
}
});
document.getElementById('deleteEdge')?.addEventListener('click', () => {
const selectedEdge = mindmap.getSelectedEdge();
if (selectedEdge) {
confirmDeleteEdge(selectedEdge, mindmap);
} else {
showNotification('Bitte wählen Sie zuerst eine Verbindung aus.', 'warning');
}
});
}
/**
* Zeigt eine Benachrichtigung an
*/
function showNotification(message, type = 'info') {
console.log(`Benachrichtigung (${type}): ${message}`);
// Implementiere eine Toast-Benachrichtigung
// ...
}
// ... Weitere Funktionen wie openAddThoughtModal, openAddNodeModal usw. ...
} }
/** /**
* Öffnet das Modal zum Hinzufügen eines Gedankens * Füge globale Funktionen für das Mindmap-Objekt hinzu
*/ */
function openAddThoughtModal(nodeName) { window.showComments = async function(thoughtId) {
// Modal-Implementierung... try {
// Lade-Animation erstellen
const modal = createModalWithLoading('Kommentare werden geladen...');
document.body.appendChild(modal);
// Kommentare laden
const response = await fetch(`/api/comments/${thoughtId}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const comments = await response.json();
// Modal mit Kommentaren aktualisieren
updateModalWithComments(modal, comments, thoughtId);
} catch (error) {
console.error('Fehler beim Laden der Kommentare:', error);
if (window.MindMap && window.MindMap.showNotification) {
window.MindMap.showNotification('Fehler beim Laden der Kommentare.', 'error');
} else {
alert('Fehler beim Laden der Kommentare.');
}
}
};
/**
* Zeigt die Beziehungen eines Gedankens an
*/
window.showRelations = async function(thoughtId) {
try {
// Lade-Animation erstellen
const modal = createModalWithLoading('Beziehungen werden geladen...');
document.body.appendChild(modal);
// Beziehungen laden
const response = await fetch(`/api/thoughts/${thoughtId}/relations`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const relations = await response.json();
// Modal mit Beziehungen aktualisieren
updateModalWithRelations(modal, relations, thoughtId);
} catch (error) {
console.error('Fehler beim Laden der Beziehungen:', error);
if (window.MindMap && window.MindMap.showNotification) {
window.MindMap.showNotification('Fehler beim Laden der Beziehungen.', 'error');
} else {
alert('Fehler beim Laden der Beziehungen.');
}
}
};
/**
* Erstellt ein Modal mit Lade-Animation
*/
function createModalWithLoading(loadingText) {
const modal = document.createElement('div');
modal.className = 'fixed inset-0 z-50 flex items-center justify-center p-4';
modal.innerHTML = `
<div class="absolute inset-0 bg-black/50" id="modal-backdrop"></div>
<div class="glass-effect relative rounded-lg shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto z-10">
<div class="p-6 text-center">
<div class="flex justify-center mb-4">
<div class="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-primary-400"></div>
</div>
<p class="text-lg text-white">${loadingText}</p>
</div>
</div>
`;
// Event-Listener zum Schließen
modal.querySelector('#modal-backdrop').addEventListener('click', () => {
modal.remove();
});
return modal;
} }
/** /**
* Zeigt die Detailansicht eines Gedankens * Aktualisiert das Modal mit Kommentaren
*/ */
function showThoughtDetail(thoughtId) { function updateModalWithComments(modal, comments, thoughtId) {
// Detailansicht-Implementierung... const modalContent = modal.querySelector('.glass-effect');
modalContent.innerHTML = `
<div class="p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-xl font-bold text-white">Kommentare</h3>
<button id="close-modal-btn" class="text-gray-400 hover:text-white">
<i class="fa-solid fa-xmark text-xl"></i>
</button>
</div>
<div class="comments-list mb-6 space-y-4">
${comments.length === 0 ?
'<div class="text-center text-gray-400 py-4">Keine Kommentare vorhanden.</div>' :
comments.map(comment => `
<div class="glass-effect p-3 rounded">
<div class="flex justify-between items-start">
<div class="font-medium text-white">${comment.author}</div>
<div class="text-xs text-gray-400">${comment.timestamp}</div>
</div>
<p class="mt-2 text-gray-200">${comment.content}</p>
</div>
`).join('')
}
</div>
<form id="comment-form" class="space-y-3">
<div>
<label for="comment-content" class="block text-sm font-medium text-gray-300">Neuer Kommentar</label>
<textarea id="comment-content" name="content" rows="3" required
class="mt-1 block w-full rounded-md bg-dark-700 border-dark-500 text-white"></textarea>
</div>
<div class="flex justify-end pt-2">
<button type="submit" class="btn-primary">
<i class="fa-solid fa-paper-plane mr-2"></i> Senden
</button>
</div>
</form>
</div>
`;
// Event-Listener hinzufügen
modalContent.querySelector('#close-modal-btn').addEventListener('click', () => {
modal.remove();
});
// Kommentar-Formular
const form = modalContent.querySelector('#comment-form');
form.addEventListener('submit', async (e) => {
e.preventDefault();
const content = form.elements.content.value;
try {
const response = await fetch('/api/comments', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
thought_id: thoughtId,
content: content
})
});
if (!response.ok) {
throw new Error('Fehler beim Speichern des Kommentars.');
}
// Modal schließen
modal.remove();
// Erfolgsbenachrichtigung
MindMap.showNotification('Kommentar erfolgreich gespeichert.', 'success');
} catch (error) {
console.error('Fehler beim Speichern des Kommentars:', error);
MindMap.showNotification('Fehler beim Speichern des Kommentars.', 'error');
}
});
}
/**
* Aktualisiert das Modal mit Beziehungen
*/
function updateModalWithRelations(modal, relations, thoughtId) {
const modalContent = modal.querySelector('.glass-effect');
modalContent.innerHTML = `
<div class="p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-xl font-bold text-white">Beziehungen</h3>
<button id="close-modal-btn" class="text-gray-400 hover:text-white">
<i class="fa-solid fa-xmark text-xl"></i>
</button>
</div>
<div class="relations-list mb-6 space-y-4">
${relations.length === 0 ?
'<div class="text-center text-gray-400 py-4">Keine Beziehungen vorhanden.</div>' :
relations.map(relation => `
<div class="glass-effect p-3 rounded">
<div class="flex items-center">
<span class="inline-block px-2 py-1 rounded-full text-xs font-medium bg-primary-600 text-white">
${relation.relation_type}
</span>
<div class="ml-3">
<div class="text-white">Ziel: Gedanke #${relation.target_id}</div>
<div class="text-xs text-gray-400">Erstellt von ${relation.created_by} am ${relation.created_at}</div>
</div>
</div>
</div>
`).join('')
}
</div>
<form id="relation-form" class="space-y-3">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label for="target_id" class="block text-sm font-medium text-gray-300">Ziel-Gedanke ID</label>
<input type="number" id="target_id" name="target_id" required
class="mt-1 block w-full rounded-md bg-dark-700 border-dark-500 text-white">
</div>
<div>
<label for="relation_type" class="block text-sm font-medium text-gray-300">Beziehungstyp</label>
<select id="relation_type" name="relation_type" required
class="mt-1 block w-full rounded-md bg-dark-700 border-dark-500 text-white">
<option value="SUPPORTS">Stützt</option>
<option value="CONTRADICTS">Widerspricht</option>
<option value="BUILDS_UPON">Baut auf auf</option>
<option value="GENERALIZES">Verallgemeinert</option>
<option value="SPECIFIES">Spezifiziert</option>
<option value="INSPIRES">Inspiriert</option>
</select>
</div>
</div>
<div class="flex justify-end pt-2">
<button type="submit" class="btn-primary">
<i class="fa-solid fa-plus mr-2"></i> Beziehung erstellen
</button>
</div>
</form>
</div>
`;
// Event-Listener hinzufügen
modalContent.querySelector('#close-modal-btn').addEventListener('click', () => {
modal.remove();
});
// Beziehungs-Formular
const form = modalContent.querySelector('#relation-form');
form.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = {
source_id: thoughtId,
target_id: parseInt(form.elements.target_id.value),
relation_type: form.elements.relation_type.value
};
try {
const response = await fetch('/api/relations', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formData)
});
if (!response.ok) {
throw new Error('Fehler beim Erstellen der Beziehung.');
}
// Modal schließen
modal.remove();
// Erfolgsbenachrichtigung
MindMap.showNotification('Beziehung erfolgreich erstellt.', 'success');
} catch (error) {
console.error('Fehler beim Erstellen der Beziehung:', error);
MindMap.showNotification('Fehler beim Erstellen der Beziehung.', 'error');
}
});
} }

View File

@@ -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">
<!-- Font Awesome vom CDN --> <!-- Icons - Self-hosted Font Awesome -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" rel="stylesheet"> <link href="{{ url_for('static', filename='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/light mode styles --> <!-- Custom dark mode styles -->
<!-- ► ► FarbToken strikt getrennt ◄ ◄ --> <!-- ► ► FarbToken strikt getrennt ◄ ◄ -->
<style> <style>
/* LightMode */ /* LightMode */
@@ -149,39 +149,6 @@
.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="{
@@ -201,7 +168,6 @@
if (data.success) { if (data.success) {
this.darkMode = data.darkMode === 'true'; this.darkMode = data.darkMode === 'true';
document.querySelector('html').classList.toggle('dark', this.darkMode); document.querySelector('html').classList.toggle('dark', this.darkMode);
document.querySelector('body').classList.toggle('dark', this.darkMode);
} }
}) })
.catch(error => { .catch(error => {
@@ -212,7 +178,6 @@
toggleDarkMode() { toggleDarkMode() {
this.darkMode = !this.darkMode; this.darkMode = !this.darkMode;
document.querySelector('html').classList.toggle('dark', this.darkMode); document.querySelector('html').classList.toggle('dark', this.darkMode);
document.querySelector('body').classList.toggle('dark', this.darkMode);
fetch('/api/set_dark_mode', { fetch('/api/set_dark_mode', {
method: 'POST', method: 'POST',

View File

@@ -1,287 +1,234 @@
{% extends "base.html" %} <!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Interaktive Mindmap</title>
{% block title %}Mindmap{% endblock %} <!-- Cytoscape.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.26.0/cytoscape.min.js"></script>
{% block extra_css %} <!-- Socket.IO -->
<style> <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.7.2/socket.io.min.js"></script>
/* Spezifische Stile für die Mindmap-Seite */
#cy {
width: 100%;
height: 600px;
background-color: var(--bg-secondary);
transition: background-color 0.3s ease;
border-radius: 10px;
overflow: hidden;
position: relative;
}
.mindmap-container { <!-- Feather Icons (optional) -->
position: relative; <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
border-radius: 10px;
overflow: hidden;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.dark .mindmap-container { <style>
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2); * {
} box-sizing: border-box;
margin: 0;
.mindmap-toolbar { padding: 0;
display: flex;
gap: 0.5rem;
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 {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.875rem;
border-radius: 0.375rem;
transition: all 0.3s ease;
}
.category-filters {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
padding: 0.75rem;
background-color: var(--bg-secondary);
transition: background-color 0.3s ease;
}
.category-filter {
border: none;
border-radius: 0.25rem;
padding: 0.25rem 0.75rem;
font-size: 0.75rem;
cursor: pointer;
transition: opacity 0.2s;
color: white;
}
.category-filter:not(.active) {
opacity: 0.6;
}
.category-filter:hover:not(.active) {
opacity: 0.8;
}
/* Kontextmenü */
#context-menu {
position: absolute;
border-radius: 0.375rem;
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 {
padding: 0.5rem 1rem;
cursor: pointer;
transition: background-color 0.2s ease;
}
.dark #context-menu .menu-item:hover {
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>
{% endblock %}
{% block content %} body {
<div class="container mx-auto px-4"> font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
<div class="mb-8"> background-color: #f9fafb;
<h1 class="text-3xl font-bold mb-2 gradient-text">Interaktive Mindmap</h1> color: #111827;
<p class="text-gray-500 dark:text-gray-400">Visualisieren und erkunden Sie Wissensnetze und Gedankenkonstrukte</p> line-height: 1.5;
</div> }
<div class="mindmap-section"> .container {
<!-- Linke Spalte: Mindmap --> display: flex;
<div class="mindmap-container"> flex-direction: column;
<!-- Suchleiste --> height: 100vh;
<div class="mindmap-toolbar"> width: 100%;
<div class="flex-1"> }
<input type="text" id="search-mindmap"
class="w-full max-w-md px-3 py-2 rounded-lg"
x-bind:class="darkMode ? 'bg-gray-800 border border-gray-700 text-white' : 'bg-white border border-gray-300 text-gray-700'"
placeholder="In der Mindmap suchen...">
</div>
<button id="addNode" class="btn" .header {
x-bind:class="darkMode ? 'bg-primary-600 hover:bg-primary-700 text-white' : 'bg-primary-500 hover:bg-primary-600 text-white'"> background-color: #1f2937;
<i class="fa-solid fa-plus"></i> color: white;
<span class="hidden sm:inline">Knoten hinzufügen</span> padding: 1rem;
</button> display: flex;
justify-content: space-between;
align-items: center;
}
<button id="addEdge" class="btn" .header h1 {
x-bind:class="darkMode ? 'bg-primary-600 hover:bg-primary-700 text-white' : 'bg-primary-500 hover:bg-primary-600 text-white'"> font-size: 1.5rem;
<i class="fa-solid fa-link"></i> font-weight: 500;
<span class="hidden sm:inline">Verbindung</span> }
</button>
<button id="reLayout" class="btn" .toolbar {
x-bind:class="darkMode ? 'bg-gray-700 hover:bg-gray-600 text-white' : 'bg-gray-200 hover:bg-gray-300 text-gray-700'"> background-color: #f3f4f6;
<i class="fa-solid fa-compass-drafting"></i> padding: 0.75rem;
<span class="hidden sm:inline">Anordnen</span> display: flex;
</button> gap: 0.5rem;
border-bottom: 1px solid #e5e7eb;
}
<div class="relative" x-data="{ open: false }"> .btn {
<button @click="open = !open" class="btn" background-color: #3b82f6;
x-bind:class="darkMode ? 'bg-gray-700 hover:bg-gray-600 text-white' : 'bg-gray-200 hover:bg-gray-300 text-gray-700'"> color: white;
<i class="fa-solid fa-ellipsis-vertical"></i> border: none;
</button> border-radius: 0.25rem;
padding: 0.5rem 1rem;
font-size: 0.875rem;
cursor: pointer;
transition: background-color 0.2s;
display: flex;
align-items: center;
gap: 0.5rem;
}
<div x-show="open" @click.away="open = false" .btn:hover {
x-transition:enter="transition ease-out duration-200" background-color: #2563eb;
x-transition:enter-start="opacity-0 scale-95" }
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-150" .btn-secondary {
x-transition:leave-start="opacity-100 scale-100" background-color: #6b7280;
x-transition:leave-end="opacity-0 scale-95" }
class="absolute right-0 mt-2 w-48 origin-top-right z-10 rounded-md shadow-lg"
x-bind:class="darkMode ? 'bg-gray-800' : 'bg-white'"> .btn-secondary:hover {
<div class="py-1"> background-color: #4b5563;
<button id="editNode" class="block w-full text-left px-4 py-2 text-sm hover:bg-gray-700" }
x-bind:class="darkMode ? 'text-gray-300 hover:bg-gray-700' : 'text-gray-700 hover:bg-gray-100'">
<i class="fa-solid fa-edit mr-2"></i>Knoten bearbeiten .btn-danger {
</button> background-color: #ef4444;
<button id="deleteNode" class="block w-full text-left px-4 py-2 text-sm hover:bg-red-600/20" }
x-bind:class="darkMode ? 'text-gray-300 hover:bg-red-600/20' : 'text-gray-700 hover:bg-red-100'">
<i class="fa-solid fa-trash-alt mr-2"></i>Knoten löschen .btn-danger:hover {
</button> background-color: #dc2626;
<button id="deleteEdge" class="block w-full text-left px-4 py-2 text-sm hover:bg-red-600/20" }
x-bind:class="darkMode ? 'text-gray-300 hover:bg-red-600/20' : 'text-gray-700 hover:bg-red-100'">
<i class="fa-solid fa-unlink mr-2"></i>Verbindung löschen .search-container {
</button> flex: 1;
<button id="exportMindmap" class="block w-full text-left px-4 py-2 text-sm" display: flex;
x-bind:class="darkMode ? 'text-gray-300 hover:bg-gray-700' : 'text-gray-700 hover:bg-gray-100'"> margin-left: 1rem;
<i class="fa-solid fa-file-export mr-2"></i>Exportieren }
</button>
</div> .search-input {
</div> width: 100%;
</div> max-width: 300px;
padding: 0.5rem;
border: 1px solid #d1d5db;
border-radius: 0.25rem;
font-size: 0.875rem;
}
#cy {
flex: 1;
width: 100%;
position: relative;
}
.category-filters {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
padding: 0.75rem;
background-color: #ffffff;
border-bottom: 1px solid #e5e7eb;
}
.category-filter {
border: none;
border-radius: 0.25rem;
padding: 0.25rem 0.75rem;
font-size: 0.75rem;
cursor: pointer;
transition: opacity 0.2s;
color: white;
}
.category-filter:not(.active) {
opacity: 0.6;
}
.category-filter:hover:not(.active) {
opacity: 0.8;
}
.footer {
background-color: #f3f4f6;
padding: 0.75rem;
text-align: center;
font-size: 0.75rem;
color: #6b7280;
border-top: 1px solid #e5e7eb;
}
/* Kontextmenü Styling */
#context-menu {
position: absolute;
background-color: white;
border: 1px solid #e5e7eb;
border-radius: 0.25rem;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
z-index: 1000;
}
#context-menu .menu-item {
padding: 0.5rem 1rem;
cursor: pointer;
}
#context-menu .menu-item:hover {
background-color: #f3f4f6;
}
</style>
</head>
<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> </div>
</header>
<!-- Kategoriefilter --> <div class="toolbar">
<div id="category-filters" class="category-filters"></div> <button id="addNode" class="btn">
<i data-feather="plus-circle"></i>
<!-- Mindmap-Visualisierung --> Knoten hinzufügen
<div id="cy"></div> </button>
<button id="addEdge" class="btn">
<i data-feather="git-branch"></i>
Verbindung erstellen
</button>
<button id="editNode" class="btn btn-secondary">
<i data-feather="edit-2"></i>
Knoten bearbeiten
</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>
</div> </div>
<!-- Rechte Spalte: Detail-Ansicht und Interaktionen --> <div id="category-filters" class="category-filters">
<div class="space-y-6"> <!-- Wird dynamisch befüllt -->
<!-- Knoteninformationen -->
<div class="card p-5"
x-bind:class="darkMode ? 'bg-gray-800/80 border border-gray-700' : 'bg-white/90 border border-gray-200'">
<h2 class="text-xl font-semibold mb-3"
x-bind:class="darkMode ? 'text-white' : 'text-gray-800'">
<i class="fa-solid fa-circle-info mr-2"></i>Information
</h2>
<div id="node-details">
<p class="text-sm"
x-bind:class="darkMode ? 'text-gray-300' : 'text-gray-600'">
Wählen Sie einen Knoten aus der Mindmap aus, um Details anzuzeigen.
</p>
</div>
</div>
<!-- Verbundene Gedanken -->
<div class="card p-5"
x-bind:class="darkMode ? 'bg-gray-800/80 border border-gray-700' : 'bg-white/90 border border-gray-200'">
<div class="flex justify-between items-center mb-3">
<h2 class="text-xl font-semibold"
x-bind:class="darkMode ? 'text-white' : 'text-gray-800'">
<i class="fa-solid fa-lightbulb mr-2"></i>Gedanken
</h2>
<button class="btn-sm"
x-bind:class="darkMode ? 'bg-primary-600 hover:bg-primary-700 text-white' : 'bg-primary-500 hover:bg-primary-600 text-white'">
<i class="fa-solid fa-plus mr-1"></i>Neu
</button>
</div>
<div id="thoughts-container">
<p class="text-sm"
x-bind:class="darkMode ? 'text-gray-300' : 'text-gray-600'">
Wählen Sie einen Knoten aus, um zugehörige Gedanken zu sehen.
</p>
</div>
</div>
<!-- Schnellhilfe -->
<div class="card p-5"
x-bind:class="darkMode ? 'bg-gray-800/80 border border-gray-700' : 'bg-white/90 border border-gray-200'">
<h2 class="text-xl font-semibold mb-3"
x-bind:class="darkMode ? 'text-white' : 'text-gray-800'">
<i class="fa-solid fa-circle-question mr-2"></i>Schnellhilfe
</h2>
<ul class="text-sm space-y-2"
x-bind:class="darkMode ? 'text-gray-300' : 'text-gray-600'">
<li><i class="fa-solid fa-mouse mr-2"></i>Klicken Sie auf einen Knoten, um ihn auszuwählen</li>
<li><i class="fa-solid fa-arrows-up-down-left-right mr-2"></i>Ziehen Sie die Maus, um die Ansicht zu verschieben</li>
<li><i class="fa-solid fa-magnifying-glass-plus mr-2"></i>Mausrad zum Zoomen</li>
<li><i class="fa-solid fa-right-click mr-2"></i>Rechtsklick für Kontextmenü</li>
</ul>
</div>
</div> </div>
<div id="cy"></div>
<footer class="footer">
Mindmap-Anwendung © 2023
</footer>
</div> </div>
</div>
<!-- Cytoscape.js für die Mindmap-Visualisierung --> <!-- Unsere Mindmap JS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.26.0/cytoscape.min.js"></script> <script src="{{ url_for('static', filename='js/mindmap.js') }}"></script>
<!-- Socket.IO für Echtzeitaktualisierungen --> <!-- Icons initialisieren -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.7.2/socket.io.min.js"></script> <script>
{% endblock %} document.addEventListener('DOMContentLoaded', () => {
if (typeof feather !== 'undefined') {
{% block scripts %} feather.replace();
<script> }
document.addEventListener('DOMContentLoaded', function() {
// Verarbeite Dark-Mode-Umschaltungen für die Mindmap-Visualisierung
document.addEventListener('darkModeToggled', function(event) {
// Hier könnten wir das Mindmap-Styling basierend auf Dark/Light-Mode aktualisieren
// z.B. Hintergrundfarben, Knotenfarben etc.
console.log('Dark Mode für Mindmap aktualisiert:', event.detail.isDark);
}); });
</script>
// Weitere Mindmap-spezifische Initialisierung </body>
// Sollte mit dem vorhandenen mindmap.js-Modul funktionieren </html>
});
</script>
{% endblock %}

58
windows_setup.bat Normal file
View File

@@ -0,0 +1,58 @@
@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