"feat: Update error handling documentation in frontend files"
This commit is contained in:
parent
2ed13acf21
commit
6bbae62b21
@ -196,3 +196,127 @@ Komplettes Live-Druckererkennungs-System mit Session-Caching und automatischer S
|
|||||||
---
|
---
|
||||||
|
|
||||||
## [2024-12-29] Template-Ladeproblem behoben ✅
|
## [2024-12-29] Template-Ladeproblem behoben ✅
|
||||||
|
|
||||||
|
## 2025-05-29 20:45 - Live-Drucker-Status-Integration behoben
|
||||||
|
|
||||||
|
### Problem
|
||||||
|
Obwohl das Live-Drucker-Erkennungssystem vollständig implementiert war, wurden die Drucker nicht als online angezeigt. Das Frontend nutzte noch die alten API-Endpunkte ohne Live-Status-Updates.
|
||||||
|
|
||||||
|
### Ursache
|
||||||
|
1. **Fehlende Frontend-Integration**: Das `printer_monitor.js` System war implementiert, aber nicht in die HTML-Templates eingebunden
|
||||||
|
2. **Veraltete API-Aufrufe**: Die PrinterManager-Klasse nutzte noch `/api/printers` statt der neuen Live-Monitor-Endpunkte
|
||||||
|
3. **Fehlende Status-Kategorien**: Die neuen Status-Kategorien (standby, unreachable, unconfigured) waren nicht im Frontend implementiert
|
||||||
|
|
||||||
|
### Lösung
|
||||||
|
1. **JavaScript-Einbindung in base.html**:
|
||||||
|
```html
|
||||||
|
<script src="{{ url_for('static', filename='js/printer_monitor.js') }}"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **PrinterManager-Integration erweitert**:
|
||||||
|
```javascript
|
||||||
|
// Nutze das neue PrinterMonitor-System für Live-Status
|
||||||
|
if (window.printerMonitor) {
|
||||||
|
window.printerMonitor.onUpdate((data) => {
|
||||||
|
if (data.type === 'update') {
|
||||||
|
allPrinters = Array.from(data.printers.values());
|
||||||
|
// Update UI mit Live-Daten
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await window.printerMonitor.forceUpdate();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Live-Status-Indikator hinzugefügt**:
|
||||||
|
```html
|
||||||
|
<div id="live-status-indicator" class="w-2 h-2 bg-green-500 rounded-full mr-2 animate-pulse"></div>
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Erweiterte Status-Kategorien**:
|
||||||
|
- **standby**: Gelb - Drucker bereit aber inaktiv
|
||||||
|
- **unreachable**: Grau - Netzwerk nicht erreichbar
|
||||||
|
- **unconfigured**: Indigo - Nicht konfiguriert
|
||||||
|
|
||||||
|
5. **Status-Filter erweitert**:
|
||||||
|
```html
|
||||||
|
<option value="standby">Standby</option>
|
||||||
|
<option value="unreachable">Unerreichbar</option>
|
||||||
|
<option value="unconfigured">Nicht konfiguriert</option>
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **CSS-Styling für neue Status**:
|
||||||
|
```css
|
||||||
|
.status-standby { border-left: 4px solid #f59e0b; }
|
||||||
|
.status-unreachable { border-left: 4px solid #6b7280; }
|
||||||
|
.status-unconfigured { border-left: 4px solid #6366f1; }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Funktionen nach der Behebung
|
||||||
|
- ✅ Live-Status-Updates alle 30 Sekunden
|
||||||
|
- ✅ Session-Caching für bessere Performance
|
||||||
|
- ✅ Automatische Steckdosen-Initialisierung beim Start
|
||||||
|
- ✅ Visuelle Live-Status-Indikatoren
|
||||||
|
- ✅ Erweiterte Status-Kategorien
|
||||||
|
- ✅ Fallback zu Standard-API bei Fehlern
|
||||||
|
- ✅ Detaillierte Status-Logging in der Konsole
|
||||||
|
|
||||||
|
### API-Endpunkte
|
||||||
|
- `GET /api/printers/monitor/live-status` - Live-Status mit Caching
|
||||||
|
- `GET /api/printers/monitor/summary` - Schnelle Übersicht
|
||||||
|
- `POST /api/printers/monitor/clear-cache` - Cache-Management
|
||||||
|
- `POST /api/printers/monitor/initialize-outlets` - Steckdosen-Init
|
||||||
|
|
||||||
|
### Verhalten
|
||||||
|
- **Automatischer Start**: PrinterMonitor startet automatisch auf der Drucker-Seite
|
||||||
|
- **Adaptive Intervalle**: 30s normal, 60s wenn Tab versteckt, 5s bei kritischen Operationen
|
||||||
|
- **Fehlerbehandlung**: Automatischer Fallback zu Standard-API bei Problemen
|
||||||
|
- **Performance**: Multi-Level-Caching (Session 30s, DB 5min)
|
||||||
|
|
||||||
|
### Test-Ergebnisse
|
||||||
|
- Live-Status-Updates funktionieren korrekt
|
||||||
|
- Drucker werden mit korrekten Status-Kategorien angezeigt
|
||||||
|
- Performance-Optimierungen greifen
|
||||||
|
- Fallback-Mechanismen funktionieren
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2025-05-29 18:30 - Rate Limiting Decorator Fehler behoben
|
||||||
|
|
||||||
|
### Problem
|
||||||
|
```
|
||||||
|
TypeError: limit_requests() takes 1 positional argument but 3 were given
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ursache
|
||||||
|
Falsche Verwendung des Rate-Limiting-Decorators:
|
||||||
|
```python
|
||||||
|
# Falsch:
|
||||||
|
@limit_requests("printer_monitor_live", 60, 5)
|
||||||
|
|
||||||
|
# Richtig:
|
||||||
|
@limit_requests("printer_monitor_live")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lösung
|
||||||
|
1. **Rate Limits in Konfiguration definiert**:
|
||||||
|
```python
|
||||||
|
RATE_LIMITS = {
|
||||||
|
"printer_monitor_live": {"requests": 5, "window": 60},
|
||||||
|
"printer_monitor_summary": {"requests": 10, "window": 30},
|
||||||
|
"printer_monitor_cache": {"requests": 3, "window": 120},
|
||||||
|
"printer_monitor_init": {"requests": 2, "window": 300}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Decorator-Syntax korrigiert**:
|
||||||
|
```python
|
||||||
|
@limit_requests("printer_monitor_live")
|
||||||
|
def get_live_printer_status():
|
||||||
|
```
|
||||||
|
|
||||||
|
### Betroffene Dateien
|
||||||
|
- `utils/rate_limiter.py` - Rate Limits hinzugefügt
|
||||||
|
- `blueprints/printer_monitor.py` - Decorator-Syntax korrigiert
|
||||||
|
- `docs/live_drucker_system.md` - Dokumentation aktualisiert
|
||||||
|
|
||||||
|
---
|
@ -1386,19 +1386,29 @@ def check_printer_status(ip_address: str, timeout: int = 7) -> Tuple[str, bool]:
|
|||||||
@measure_execution_time(logger=printers_logger, task_name="Mehrere-Drucker-Status-Prüfung")
|
@measure_execution_time(logger=printers_logger, task_name="Mehrere-Drucker-Status-Prüfung")
|
||||||
def check_multiple_printers_status(printers: List[Dict], timeout: int = 7) -> Dict[int, Tuple[str, bool]]:
|
def check_multiple_printers_status(printers: List[Dict], timeout: int = 7) -> Dict[int, Tuple[str, bool]]:
|
||||||
"""
|
"""
|
||||||
Überprüft den Status mehrerer Drucker parallel mit Timeout.
|
Überprüft den Status mehrerer Drucker parallel.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
printers: Liste von Drucker-Dictionaries mit 'id' und 'ip_address'
|
printers: Liste der zu prüfenden Drucker
|
||||||
timeout: Timeout in Sekunden pro Drucker (Standard: 7)
|
timeout: Timeout für jeden einzelnen Drucker
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Dict[int, Tuple[str, bool]]: Dictionary mit Drucker-ID als Key und (Status, Aktiv) als Value
|
Dict[int, Tuple[str, bool]]: Dictionary mit Drucker-ID als Key und (Status, Aktiv) als Value
|
||||||
"""
|
"""
|
||||||
results = {}
|
results = {}
|
||||||
|
|
||||||
|
# Wenn keine Drucker vorhanden sind, gebe leeres Dict zurück
|
||||||
|
if not printers:
|
||||||
|
printers_logger.info("ℹ️ Keine Drucker zum Status-Check gefunden")
|
||||||
|
return results
|
||||||
|
|
||||||
|
printers_logger.info(f"🔍 Prüfe Status von {len(printers)} Druckern parallel...")
|
||||||
|
|
||||||
# Parallel-Ausführung mit ThreadPoolExecutor
|
# Parallel-Ausführung mit ThreadPoolExecutor
|
||||||
with ThreadPoolExecutor(max_workers=min(len(printers), 10)) as executor:
|
# Sicherstellen, dass max_workers mindestens 1 ist
|
||||||
|
max_workers = min(max(len(printers), 1), 10)
|
||||||
|
|
||||||
|
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
||||||
# Futures für alle Drucker erstellen
|
# Futures für alle Drucker erstellen
|
||||||
future_to_printer = {
|
future_to_printer = {
|
||||||
executor.submit(check_printer_status, printer.get('ip_address'), timeout): printer
|
executor.submit(check_printer_status, printer.get('ip_address'), timeout): printer
|
||||||
@ -1416,6 +1426,8 @@ def check_multiple_printers_status(printers: List[Dict], timeout: int = 7) -> Di
|
|||||||
printers_logger.error(f"Fehler bei Status-Check für Drucker {printer['name']}: {str(e)}")
|
printers_logger.error(f"Fehler bei Status-Check für Drucker {printer['name']}: {str(e)}")
|
||||||
results[printer['id']] = ("offline", False)
|
results[printer['id']] = ("offline", False)
|
||||||
|
|
||||||
|
printers_logger.info(f"✅ Status-Check abgeschlossen für {len(results)} Drucker")
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
# ===== UI-ROUTEN =====
|
# ===== UI-ROUTEN =====
|
||||||
|
Binary file not shown.
Binary file not shown.
@ -789,25 +789,84 @@
|
|||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
left: 0 !important;
|
left: 0 !important;
|
||||||
right: 0 !important;
|
right: 0 !important;
|
||||||
@apply bg-white/60 dark:bg-black/70;
|
/* Verstärktes Glassmorphism Design */
|
||||||
backdrop-filter: blur(24px) saturate(200%);
|
background: rgba(255, 255, 255, 0.15) !important;
|
||||||
-webkit-backdrop-filter: blur(24px) saturate(200%);
|
backdrop-filter: blur(40px) saturate(200%) brightness(110%) contrast(105%) !important;
|
||||||
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.08);
|
-webkit-backdrop-filter: blur(40px) saturate(200%) brightness(110%) contrast(105%) !important;
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, 0.15);
|
box-shadow:
|
||||||
|
0 8px 32px rgba(0, 0, 0, 0.12),
|
||||||
|
0 2px 8px rgba(0, 0, 0, 0.08),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.3),
|
||||||
|
0 0 0 1px rgba(255, 255, 255, 0.15) !important;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.2) !important;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .navbar {
|
.dark .navbar {
|
||||||
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.3), 0 1px 2px rgba(0, 0, 0, 0.2);
|
background: rgba(0, 0, 0, 0.25) !important;
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
backdrop-filter: blur(45px) saturate(180%) brightness(120%) contrast(115%) !important;
|
||||||
|
-webkit-backdrop-filter: blur(45px) saturate(180%) brightness(120%) contrast(115%) !important;
|
||||||
|
box-shadow:
|
||||||
|
0 8px 32px rgba(0, 0, 0, 0.4),
|
||||||
|
0 2px 8px rgba(0, 0, 0, 0.3),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.15),
|
||||||
|
0 0 0 1px rgba(255, 255, 255, 0.08) !important;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Neue Navbar-Komponenten */
|
/* Navbar Scroll-Effekt */
|
||||||
|
.navbar.scrolled {
|
||||||
|
background: rgba(255, 255, 255, 0.25) !important;
|
||||||
|
backdrop-filter: blur(50px) saturate(220%) brightness(115%) contrast(110%) !important;
|
||||||
|
-webkit-backdrop-filter: blur(50px) saturate(220%) brightness(115%) contrast(110%) !important;
|
||||||
|
box-shadow:
|
||||||
|
0 12px 40px rgba(0, 0, 0, 0.15),
|
||||||
|
0 4px 12px rgba(0, 0, 0, 0.1),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.4),
|
||||||
|
0 0 0 1px rgba(255, 255, 255, 0.2) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .navbar.scrolled {
|
||||||
|
background: rgba(0, 0, 0, 0.35) !important;
|
||||||
|
backdrop-filter: blur(55px) saturate(200%) brightness(125%) contrast(120%) !important;
|
||||||
|
-webkit-backdrop-filter: blur(55px) saturate(200%) brightness(125%) contrast(120%) !important;
|
||||||
|
box-shadow:
|
||||||
|
0 12px 40px rgba(0, 0, 0, 0.5),
|
||||||
|
0 4px 12px rgba(0, 0, 0, 0.4),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.2),
|
||||||
|
0 0 0 1px rgba(255, 255, 255, 0.1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Neue Navbar-Komponenten mit verbessertem Glassmorphism */
|
||||||
.navbar-menu-new {
|
.navbar-menu-new {
|
||||||
@apply flex items-center justify-center space-x-0.5 md:space-x-1;
|
@apply flex items-center justify-center space-x-0.5 md:space-x-1;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
-ms-overflow-style: none;
|
-ms-overflow-style: none;
|
||||||
|
/* Glassmorphism für das Menü */
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
backdrop-filter: blur(25px) saturate(170%) brightness(108%);
|
||||||
|
-webkit-backdrop-filter: blur(25px) saturate(170%) brightness(108%);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 8px;
|
||||||
|
margin: 0 16px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||||
|
box-shadow:
|
||||||
|
0 6px 20px rgba(0, 0, 0, 0.1),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.2),
|
||||||
|
0 0 0 1px rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .navbar-menu-new {
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
backdrop-filter: blur(30px) saturate(150%) brightness(115%);
|
||||||
|
-webkit-backdrop-filter: blur(30px) saturate(150%) brightness(115%);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
box-shadow:
|
||||||
|
0 6px 20px rgba(0, 0, 0, 0.3),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.1),
|
||||||
|
0 0 0 1px rgba(255, 255, 255, 0.03);
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-menu-new::-webkit-scrollbar {
|
.navbar-menu-new::-webkit-scrollbar {
|
||||||
@ -815,60 +874,95 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.nav-item {
|
.nav-item {
|
||||||
@apply flex items-center space-x-1.5 px-1.5 py-1.5 rounded-md text-xs font-medium transition-all duration-300;
|
@apply flex items-center space-x-1.5 px-3 py-2.5 rounded-xl text-sm font-medium transition-all duration-300;
|
||||||
color: rgba(15, 23, 42, 0.8);
|
color: rgba(15, 23, 42, 0.85);
|
||||||
background: rgba(255, 255, 255, 0.04);
|
/* Gläserner Nav-Item */
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
backdrop-filter: blur(15px) saturate(140%);
|
||||||
|
-webkit-backdrop-filter: blur(15px) saturate(140%);
|
||||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
white-space: nowrap;
|
box-shadow:
|
||||||
min-width: fit-content;
|
0 4px 12px rgba(0, 0, 0, 0.05),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.15);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Glassmorphism Hover-Effekt */
|
||||||
|
.nav-item::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -100%;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||||
|
transition: left 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item:hover::before {
|
||||||
|
left: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .nav-item {
|
.dark .nav-item {
|
||||||
color: rgba(255, 255, 255, 0.8);
|
color: rgba(255, 255, 255, 0.85);
|
||||||
background: rgba(0, 0, 0, 0.2);
|
background: rgba(0, 0, 0, 0.15);
|
||||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
backdrop-filter: blur(20px) saturate(130%);
|
||||||
|
-webkit-backdrop-filter: blur(20px) saturate(130%);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
box-shadow:
|
||||||
|
0 4px 12px rgba(0, 0, 0, 0.2),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item:hover {
|
.nav-item:hover {
|
||||||
@apply transform -translate-y-0.5;
|
|
||||||
color: rgba(15, 23, 42, 1);
|
color: rgba(15, 23, 42, 1);
|
||||||
background: rgba(255, 255, 255, 0.15);
|
background: rgba(255, 255, 255, 0.2);
|
||||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
backdrop-filter: blur(25px) saturate(160%) brightness(110%);
|
||||||
|
-webkit-backdrop-filter: blur(25px) saturate(160%) brightness(110%);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.25);
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 8px 16px rgba(0, 0, 0, 0.05),
|
0 8px 20px rgba(0, 0, 0, 0.12),
|
||||||
0 2px 4px rgba(0, 0, 0, 0.04),
|
inset 0 1px 0 rgba(255, 255, 255, 0.3),
|
||||||
0 0 1px rgba(0, 0, 0, 0.1);
|
0 0 0 1px rgba(255, 255, 255, 0.1);
|
||||||
|
transform: translateY(-2px) scale(1.02);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .nav-item:hover {
|
.dark .nav-item:hover {
|
||||||
color: rgba(255, 255, 255, 1);
|
color: rgba(255, 255, 255, 1);
|
||||||
background: rgba(255, 255, 255, 0.05);
|
background: rgba(0, 0, 0, 0.25);
|
||||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
backdrop-filter: blur(30px) saturate(150%) brightness(120%);
|
||||||
|
-webkit-backdrop-filter: blur(30px) saturate(150%) brightness(120%);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 8px 16px rgba(0, 0, 0, 0.2),
|
0 8px 20px rgba(0, 0, 0, 0.3),
|
||||||
0 2px 4px rgba(0, 0, 0, 0.15),
|
inset 0 1px 0 rgba(255, 255, 255, 0.15),
|
||||||
0 0 1px rgba(255, 255, 255, 0.05);
|
0 0 0 1px rgba(255, 255, 255, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item.active {
|
.nav-item.active {
|
||||||
color: rgba(15, 23, 42, 1);
|
color: rgba(15, 23, 42, 1);
|
||||||
background: rgba(255, 255, 255, 0.9);
|
background: rgba(255, 255, 255, 0.35);
|
||||||
border: 1px solid rgba(255, 255, 255, 0.25);
|
backdrop-filter: blur(35px) saturate(180%) brightness(115%);
|
||||||
|
-webkit-backdrop-filter: blur(35px) saturate(180%) brightness(115%);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.4);
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 8px 20px rgba(0, 0, 0, 0.08),
|
0 12px 24px rgba(0, 0, 0, 0.15),
|
||||||
0 3px 6px rgba(0, 0, 0, 0.06),
|
inset 0 1px 0 rgba(255, 255, 255, 0.5),
|
||||||
0 1px 2px rgba(0, 0, 0, 0.04);
|
0 0 0 1px rgba(59, 130, 246, 0.3);
|
||||||
font-weight: 600;
|
transform: translateY(-1px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .nav-item.active {
|
.dark .nav-item.active {
|
||||||
color: rgba(255, 255, 255, 1);
|
color: rgba(255, 255, 255, 1);
|
||||||
background: rgba(0, 0, 0, 0.7);
|
background: rgba(0, 0, 0, 0.4);
|
||||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
backdrop-filter: blur(40px) saturate(160%) brightness(125%);
|
||||||
|
-webkit-backdrop-filter: blur(40px) saturate(160%) brightness(125%);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 8px 20px rgba(0, 0, 0, 0.3),
|
0 12px 24px rgba(0, 0, 0, 0.4),
|
||||||
0 3px 6px rgba(0, 0, 0, 0.2),
|
inset 0 1px 0 rgba(255, 255, 255, 0.2),
|
||||||
0 0 0 1px rgba(255, 255, 255, 0.06);
|
0 0 0 1px rgba(59, 130, 246, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dark Mode Toggle - Kompakteres Design */
|
/* Dark Mode Toggle - Kompakteres Design */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user