From bcad3e9f3cdf8354d76e6fb29dc59d9ab19a34b7 Mon Sep 17 00:00:00 2001 From: claude-dev Date: Sun, 8 Mar 2026 13:18:04 +0100 Subject: [PATCH] Theme-Toggle als Schalter + Karten-Vollbild + Zoom-Begrenzung MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Theme-Toggle: Button durch Sun/Moon-Slider ersetzt (Dark Mode Standard) - Karte: Fullscreen-Overlay mit Expand-Icon, Escape zum Schließen - Karte: Zoom-Limits (minZoom:2, maxBounds, noWrap) - Karte: Button 'Orte erkennen' -> 'Orte einlesen', rechts ausgerichtet Co-Authored-By: Claude Opus 4.6 --- src/static/css/style.css | 154 ++++++++++++++++++++++++++++++++++-- src/static/dashboard.html | 27 ++++++- src/static/js/app.js | 7 +- src/static/js/components.js | 59 +++++++++++++- 4 files changed, 234 insertions(+), 13 deletions(-) diff --git a/src/static/css/style.css b/src/static/css/style.css index 2c99c00..5f4367a 100644 --- a/src/static/css/style.css +++ b/src/static/css/style.css @@ -3620,17 +3620,58 @@ a:hover { } /* === Theme Toggle Button === */ -.theme-toggle-btn { - font-size: 18px; - width: 36px; - height: 36px; - padding: 0; +.theme-switch { display: flex; align-items: center; - justify-content: center; - border-radius: 50%; + gap: 6px; cursor: pointer; - transition: background 0.2s, border-color 0.2s; + user-select: none; + -webkit-user-select: none; +} +.theme-switch-icon { + font-size: 14px; + line-height: 1; + opacity: 0.4; + transition: opacity 0.3s; +} +.theme-switch.dark .theme-switch-moon, +.theme-switch.light .theme-switch-sun { + opacity: 1; +} +.theme-switch-track { + position: relative; + width: 40px; + height: 22px; + border-radius: 11px; + background: var(--bg-tertiary, #1A2440); + border: 1px solid var(--border, #1E2D45); + transition: background 0.3s, border-color 0.3s; + flex-shrink: 0; +} +.theme-switch-knob { + position: absolute; + top: 2px; + left: 2px; + width: 16px; + height: 16px; + border-radius: 50%; + background: var(--accent, #C8A851); + box-shadow: 0 0 8px rgba(200, 168, 81, 0.3); + transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.3s; +} +/* Dark mode: knob right */ +.theme-switch.dark .theme-switch-knob { + transform: translateX(18px); +} +/* Light mode: knob left */ +.theme-switch.light .theme-switch-knob { + transform: translateX(0); +} +.theme-switch:hover .theme-switch-track { + border-color: var(--accent, #C8A851); +} +.theme-switch:hover .theme-switch-knob { + box-shadow: 0 0 12px rgba(200, 168, 81, 0.5); } /* === Light Theme Sonderregeln === */ @@ -4234,6 +4275,16 @@ select:focus-visible, textarea:focus-visible, } .map-card .card-header { flex-shrink: 0; + display: flex; + align-items: center; + gap: 8px; +} +.card-header-actions { + margin-left: auto; + display: flex; + align-items: center; + gap: 6px; + flex-shrink: 0; } .map-stats { font-size: 12px; @@ -4405,3 +4456,90 @@ a.map-popup-article:hover { [data-theme="light"] .map-cluster span { color: #fff; } + +/* Karten-Legende */ +.map-legend-ctrl { + background: var(--bg-card); + padding: 10px 14px; + border-radius: var(--radius-md); + box-shadow: var(--shadow-md); + font-size: 12px; + font-family: var(--font-body); + color: var(--text-primary); + border: 1px solid var(--border); + line-height: 1.6; +} +.map-legend-ctrl strong { + font-family: var(--font-title); + font-size: 13px; +} +[data-theme="light"] .map-legend-ctrl { + background: #fff; + border-color: #ddd; + color: #333; +} + +/* SVG-Marker: kein Default-divIcon-Styling */ +.map-marker-svg { + background: none !important; + border: none !important; +} +.map-marker-svg svg { + filter: drop-shadow(1px 2px 3px rgba(0,0,0,0.35)); +} + +/* Map Expand Button */ +.map-expand-btn { + margin-left: auto; + width: 32px; + min-height: 32px; + padding: 0; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} +.map-expand-btn:hover { + color: var(--accent); + border-color: var(--accent); +} + +/* Map Fullscreen Overlay */ +.map-fullscreen-overlay { + display: none; + position: fixed; + inset: 0; + z-index: 10000; + background: var(--bg-primary); + flex-direction: column; +} +.map-fullscreen-overlay.active { + display: flex; +} +.map-fullscreen-header { + display: flex; + align-items: center; + gap: 12px; + padding: 12px 20px; + background: var(--bg-card); + border-bottom: 1px solid var(--border); + flex-shrink: 0; +} +.map-fullscreen-title { + font-family: var(--font-title); + font-size: 16px; + font-weight: 600; + color: var(--text-primary); +} +.map-fullscreen-stats { + flex: 1; +} +.map-fullscreen-container { + flex: 1; + position: relative; +} +.map-fullscreen-container .leaflet-container { + width: 100% !important; + height: 100% !important; +} + diff --git a/src/static/dashboard.html b/src/static/dashboard.html index a2dd319..8a2b399 100644 --- a/src/static/dashboard.html +++ b/src/static/dashboard.html @@ -28,7 +28,13 @@

AegisSight Monitor Dashboard

- +
+ ☀︎ +
+
+
+ +
Keine Orte erkannt
@@ -576,5 +587,17 @@ + + +
+
+
Geografische Verteilung
+ + +
+
+
diff --git a/src/static/js/app.js b/src/static/js/app.js index 7de4f5f..5daa075 100644 --- a/src/static/js/app.js +++ b/src/static/js/app.js @@ -36,8 +36,11 @@ const ThemeManager = { UI.updateMapTheme(); }, _updateIcon(theme) { - const btn = document.getElementById('theme-toggle'); - if (btn) btn.textContent = theme === 'dark' ? '\u2600' : '\u263D'; + const el = document.getElementById('theme-toggle'); + if (!el) return; + el.classList.remove('dark', 'light'); + el.classList.add(theme); + el.setAttribute('aria-checked', theme === 'dark' ? 'true' : 'false'); } }; diff --git a/src/static/js/components.js b/src/static/js/components.js index cb59454..88a3211 100644 --- a/src/static/js/components.js +++ b/src/static/js/components.js @@ -717,6 +717,9 @@ const UI = { this._map = L.map(container, { zoomControl: true, attributionControl: true, + minZoom: 2, + maxBounds: [[-85, -180], [85, 180]], + maxBoundsViscosity: 1.0, }).setView([51.1657, 10.4515], 5); // Deutschland-Zentrum this._applyMapTiles(); @@ -839,7 +842,7 @@ const UI = { const tileUrl = 'https://tile.openstreetmap.de/{z}/{x}/{y}.png'; const attribution = '© OpenStreetMap'; - L.tileLayer(tileUrl, { attribution, maxZoom: 18 }).addTo(this._map); + L.tileLayer(tileUrl, { attribution, maxZoom: 18, noWrap: true }).addTo(this._map); }, updateMapTheme() { @@ -858,6 +861,60 @@ const UI = { } }, + _mapFullscreen: false, + _mapOriginalParent: null, + + toggleMapFullscreen() { + const overlay = document.getElementById('map-fullscreen-overlay'); + const fsContainer = document.getElementById('map-fullscreen-container'); + const mapContainer = document.getElementById('map-container'); + const statsEl = document.getElementById('map-stats'); + const fsStatsEl = document.getElementById('map-fullscreen-stats'); + + if (!this._mapFullscreen) { + // Save original parent and height + this._mapOriginalParent = mapContainer.parentElement; + this._savedMapHeight = mapContainer.style.height || mapContainer.offsetHeight + 'px'; + + // Move entire map-container into fullscreen overlay + fsContainer.appendChild(mapContainer); + mapContainer.style.height = '100%'; + + if (statsEl && fsStatsEl) { + fsStatsEl.textContent = statsEl.textContent; + } + overlay.classList.add('active'); + this._mapFullscreen = true; + + // Escape key to close + this._mapFsKeyHandler = (e) => { if (e.key === 'Escape') this.toggleMapFullscreen(); }; + document.addEventListener('keydown', this._mapFsKeyHandler); + + setTimeout(() => { if (this._map) this._map.invalidateSize(); }, 100); + } else { + // Exit fullscreen: move map-container back to original parent + overlay.classList.remove('active'); + if (this._mapOriginalParent) { + this._mapOriginalParent.appendChild(mapContainer); + } + // Restore saved height + mapContainer.style.height = this._savedMapHeight || ''; + + this._mapFullscreen = false; + if (this._mapFsKeyHandler) { + document.removeEventListener('keydown', this._mapFsKeyHandler); + this._mapFsKeyHandler = null; + } + + const self = this; + [100, 300, 600].forEach(delay => { + setTimeout(() => { if (self._map) self._map.invalidateSize(); }, delay); + }); + } + }, + + _mapFsKeyHandler: null, + /** * HTML escapen. */