Commit graph

89 commits

Author SHA1 Message Date
f7370028da KI-Vision-Model, Breed-Scraper, Karte/Routen + Release v1292
Parallele Arbeit (auf Staging mitgetestet): KI-Vision-Model (VISION_MODEL in
ki.py/routes, im KI-Status sichtbar), Breed-Scraper-Anpassungen
(breed_enricher/breed_evaluator, evaluate_enrichment mit user_id),
Karten-/Routen-Änderungen (map.js, routes.js), kleinere UI-Anpassungen
(admin.js, components.css), docker-compose, MARKETING, nav-loop-Test.

Version-Bump auf 1292 (VERSION, sw.js, app.js, index.html, landing.html).
2026-06-14 20:23:21 +02:00
a31d08a2dc Karte: Blickrichtungs-Kegel (Kompass) + ruhigeres Folgen (Rene: 'weiss nicht wo es ist/hinschaut')
- Standort-Punkt zeigt jetzt einen Kompass-KEGEL (wie Google Maps): CSS-Kegel
  im loc-icon (GL + Leaflet), deviceorientation mit webkitCompassHeading (iOS)
  bzw. 360-alpha (Android), Glaettung ueber kuerzesten Winkelweg, rAF-throttled.
  iOS-Permission via User-Geste (Follow-/Standort-Button startet den Kompass).
- Follow pant nur noch bei brauchbarem Fix (accuracy < 75m) — ungenaue
  Positionen liessen die Karte zappeln; GPS-Fixes frischer (maximumAge 2s
  statt 5s). Marker aktualisiert weiterhin jeden Fix.
Bump v1249
2026-06-06 20:30:22 +02:00
0d976b90b7 Karte: Bewegungs-Gate fuer den POI-Scanner — Follow-Mode triggerte ihn laufend
Rene: aktiviertes Crosshair pannt alle paar Sekunden -> jedes moveend
startete den Scan. Scans aus KARTENBEWEGUNG laufen jetzt erst, wenn sich
das Zentrum >= 20% der Viewport-Breite bewegt hat oder der Zoom wechselt
(bei Z16 ~ alle 100-150m Fussweg). Alle anderen Trigger (Marker
gespeichert, Layer-Toggle, Retry, Init, Scan-Queue) ungebremst.
Bump v1247
2026-06-06 20:07:23 +02:00
fc55943e90 Karte: Follow-Crosshair zwischen Zoom-Reglern und Offline-Puls-Icon (Rene)
position:fixed + safe-area (+70px) wie das Offline-Icon (+110px) statt
absolute im Karten-Container — sitzt jetzt exakt in der Luecke; Offline-
Puls bleibt unveraendert an seinem Platz.
Bump v1246
2026-06-06 20:04:49 +02:00
f4f597edf8 Karte: Follow-Button (Crosshair) wie in Routen — ein Tipp folgt dem Standort
Wunsch Rene: sichtbarer Follow-Schalter direkt auf der Karte statt nur ueber
das Speed-Dial. Crosshair links unter den Zoom-Reglern (unterhalb des
Offline-Puls-Icons): Tipp = zentrieren + folgen (Button farbig), Karte
ziehen = pausiert (grau). Synchron mit Standort-Speed-Dial-Button und
Aufzeichnungs-Start (beide aktivieren Follow ebenfalls).
Bump v1245
2026-06-06 19:58:56 +02:00
5330681059 DWD-Regenvorhersage: Pipeline + /radar-Route + Timeline-Integration + Settings-Toggle
PoC BESTANDEN (tools/dwd-radar/poc): Anker (9E,51N) = Pixel-Mitte (470/600),
Ecken decken sich mit der DWD-DE1200-Spec — Georeferenzierung bewiesen.
- tools/dwd-radar: RV-Komposit (25 Frames, 0-120min) -> kolorierte RGBA-
  PMTiles z4-7 je Frame (MapLibre overzoomt darueber) + manifest.json,
  atomarer Swap, KEEP_RUNS-Aufraeumen; 25 Frames in ~14s lokal
- docker-compose.dwd.yml (DSM-Cron alle 5 min, NIE --remove-orphans)
- main.py: /radar/manifest.json (no-store) + /radar/{run}/{file} (Range/206,
  immutable — Run-Id im Pfad); sw.js: /radar/ pass-through
- map.js: Radar-Frames heterogen ({url,time,dwd}) — DWD ersetzt RainViewer-
  Nowcast (0-120min, 5-min-Schritte) wenn Toggle an + GL + Karte in DE +
  Manifest frisch (<30min); sonst RainViewer-Fallback; Label '+X Min - DWD'
- settings.js: Toggle 'DWD-Regenvorhersage' (by_dwd_radar, Default AN)
- pytest 39 passed
Bump v1240
2026-06-06 18:08:57 +02:00
6a06c9be7e POI-Bewertung: Live-Praesenz zaehlt als GPS-Beleg (Rene stand am Ort, wurde abgewiesen)
Vorher zaehlte NUR eine aufgezeichnete Tour der letzten 48h (<=50m am POI,
>=2 Punkte) — die aktuelle Geraeteposition floss gar nicht ein.
- DogFriendlyIn: optional user_lat/user_lon (Frontend schickt _userPos mit)
- Beleg-Weg a): Geraetestandort <= 75m am POI (50m Radius + GPS-Toleranz)
  -> route_id=None markiert Live-Beleg; Weg b) Tour-Beleg unveraendert
- Fehlermeldung nennt jetzt beide Wege (hin gehen ODER Standort aktivieren)
- pytest 39 passed
Bump v1239
2026-06-06 17:26:20 +02:00
abd7447d29 Karte: Follow-Mode + Live-Strecke bei Routen-Aufzeichnung (Wunsch Rene)
- MapGLMini-Polyline hatte KEIN addLatLng (nur setLatLngs) -> TypeError im
  Rec-Overlay: Strecke unsichtbar, Marker fror ein, kein Folgen. Facade
  ergaenzt (Leaflet-kompatibel).
- Rec-Overlay (routes.js): Follow-Mode default AN — Karte wandert mit dem
  Standort, Drag pausiert, Crosshair-Button (unten rechts) reaktiviert;
  erster Fix setzt Zoom 16, danach bleibt der Zoom erhalten
- Zentrale Karte (map.js): Standort-Button aktiviert Follow (+Toast),
  dragstart beendet es (beide Engines); GPS-Tracking folgt sanft (easeTo);
  Aufzeichnung startet im Follow-Mode, _updateRecMap pant nur noch im Follow
Bump v1238
2026-06-06 17:20:38 +02:00
2cdb743ce7 Selektives Loeschen: auch Funkloch-Gebiete bleiben + Keep-Set haertung
Rene: Funkloecher + Routen waren nach 'Alles loeschen' weiter weg.
- Funkloch-Regionen jetzt im Keep-Set (geloescht wird NUR Manuelles);
  Zonen behalten ihren Fuellstatus (Komplett-Wipe setzt weiter zurueck)
- Korridor-Migration beim Loeschen: keepTracks=[{name,track}] schreibt
  Tracks in Alt-Eintraege ohne r.track (Bestand vor v1236) bzw. legt
  fehlende Korridor-Regionen an — kein Warten auf Self-Healing
- clear() liefert Summary; Toast zeigt 'behalten: Standort, X Routen,
  Y Funkloch-Gebiete' — Diagnose-Sichtbarkeit fuer Geraetetests
Bump v1237
2026-06-06 13:55:37 +02:00
ca97613938 Fix: Selektives Loeschen griff auf Geraet nicht — Keep-Set jetzt selbsttragend aus der Meta
Renes Befund: 'Alles loeschen' wischte weiter alles. Ursachen: (a) Bestands-
Gebiete hatten keine standort-Region (ensureHomeArea legt nur bei FEHLENDER
Kachel los), (b) Korridor-Keys waren nur aus API-Tracks ableitbar -> leeres
Keep-Set = Komplett-Wipe.
- downloadCorridor speichert vereinfachten Track (<=60 Pkt) in der Region-Meta;
  clear() baut Korridor-Keep daraus — ohne API/Login/GPS
- Standort-ADOPTION: clear() mit center legt fehlende standort-Region
  synthetisch an (Bestandsdaten vor Runde 6)
- map.js: center-Fallback auf by_last_position wenn GPS noch keinen Fix hat
- Test r7 erweitert (clear ohne Optionen haelt Korridor aus Meta), alle gruen
Bump v1236
2026-06-06 13:44:01 +02:00
29cd489287 Offline-Karten Runde 7: 'Alles loeschen' selektiv — Standort + Routen-Korridore bleiben
Idee Rene (spart Vorladezeit + Daten): statt loeschen-und-neu-laden bleiben
- Standort-Gebiete (Regionen type 'standort')
- Korridore der gespeicherten Routen (clear({keepTracks}) aus preview_track)
- 5-km-Umkreis der aktuellen Position + Basis-Zooms 0-9
- Marker/Warnungen (p/) + Glyphs (f/)
Geloescht: manuelle Gebiete/Ausschnitte + Funkloch-Kacheln (Zonen bleiben
gemerkt, Nahe laden automatisch neu). Ohne Keep-Kandidaten: Komplett-Wipe.
Batch-Delete in einer Transaktion. Tests r7 neu, r6 angepasst, Regression gruen.
Bump v1235
2026-06-06 13:35:18 +02:00
94a6ce49ba Offline-Karten Runde 6: Standort-Grundversorgung — aktuelles Gebiet bleibt immer geladen
Renes Modell Punkt 1 war zu eng interpretiert (nur Funkloecher): das Gebiet
um die aktuelle Position gehoert zur Grundversorgung.
- ensureHomeArea(lat,lon): Zentrums-Kachel-Check -> bei Luecke Budget-Download
  (type 'standort', Cap-gated)
- Start-Check: Standort raw pruefen (ohne GL-Stack) und bei Bedarf laden
- 'Alles loeschen': Standort-Gebiet wird SOFORT neu geladen (+ Toast) —
  vorher war die Offline-Funktionalitaet genau am wichtigsten Ort weg
- Pfote Segment 5: Standort-Kachel da UND Zonen im 50-km-Umkreis gefuellt
  (ferne Zonen zaehlen nicht mehr — sie laden erst vor Ort)
- Tests r6 + Regression r1/r3/r4/r5 gruen
Bump v1234
2026-06-06 13:23:33 +02:00
6c313aca05 Offline-Karten Runde 5: Geraetetest-Feedback (Indikator, Flugmodus-Signal, Ent-Funklochen, Warnungs-Aktualitaet, Routen-Start-Check)
- Indikator links unter die Zoom-Regler (rechts verdeckte Legenden-Chips)
- Flugmodus bei offener App -> Position raw als Funkloch-Zone (offline-Event)
- Ent-Funklochen: Zonen-Liste im Offline-Modal mit X (removeDeadZone)
- Warnungs-Aktualitaet: _mergeStore Bbox-Replace (aufgehobene Giftkoeder/
  gefundene Hunde verschwinden; Fetch-Kreis deckt Bbox via sqrt2 ab;
  fresh=null merged nie) + 24h-Refresh im 50km-Umkreis beim Start
- Routen offline nutzbar halten: ensureRouteCorridors beim Start-Check
  (Stichproben-Verify, Re-Download aus preview_track, Region-Dedupe)
- Stub-Tests ins Repo: tests/js/ (r1/r3/r4/r5, alle gruen)
Bump v1231
2026-06-06 13:00:20 +02:00
53bc27faa3 Offline-Karten Runde 4: Minimal-Speicher-Modell (Modell Rene)
Funkloch-Gedaechtnis = Quelle der Wahrheit, Kacheln = ableitbarer Cache:
- Ephemeres Vorausladen: Prefetch-Kacheln werden bei Aufzeichnungsende
  geloescht, wenn die Runde kein Funkloch hatte (nur neue Keys)
- Netz-Probe bei Aufzeichnung (~2min, 6s-Timeout): erkennt Funkloecher auch
  in bereits gespeicherten Gebieten (dort kein Remote-Miss als Signal)
- clear() behaelt Zonen (filled=false) -> naechster Online-Start laedt
  Funkloch-Gebiete automatisch neu, auch nach 'Alles loeschen'
- Start-Check mit Position: nur Zonen im 50-km-Umkreis, naechste zuerst,
  Coverage-Verify (faengt Eviction ab); Pfote Segment 5 = alle Zonen gefuellt
- Coverage-Layer zweifarbig: Funkloch orange, manuell blau + Modal-Legende
- Stub-Tests Runde 4 (Prune/Probe/clear/Naehe/Verify/Faerbung) bestanden
Bump v1230
2026-06-06 12:46:12 +02:00
763108fa7c Offline-Karten Runde 3: Puls-Icon, rollendes Vorausladen, Ausschnitt-Download, Speicher-Cap
- Offline-Indikator: pulsierendes 32px-Icon oben rechts (unter Kopfzeilen-Hoehe)
  statt Leiste ueber die volle Breite — verdeckte '<- Zurueck' in der
  Routennavigation (Geraetetest Rene)
- Rollendes Vorausladen: setGps laedt alle ~400m still fehlende z14+-2-Kacheln
  um die Position — deckt den Weg schon beim ERSTEN Funkloch-Besuch ab
- Bereichsauswahl light: 'Sichtbaren Ausschnitt speichern' im Offline-Modal
  (downloadBbox, Cap 40 MB, Zu-gross-Schutz)
- Speicher-Cap 250 MB als Soft-Guard fuer automatische Pfade + totalBytes-Zaehler
  + navigator.storage.persist() best-effort; echte LRU vertagt (Refcounting noetig)
- Auto-OSM-Raster-Prefetch entfernt (manueller Leaflet-Pfad bleibt)
- Logik-Tests (Node-Stubs) fuer Bbox/Cap/Throttle/persist bestanden
Bump v1229
2026-06-06 12:34:48 +02:00
a600ca1dec Offline: Giftkoeder + vermisste Hunde offline sichtbar, Korridor auf Detailkarte
Geraetetest-Befunde Runde 2:
- Giftkoeder verschwanden offline: /api/places kam aus dem SW-Cache (feste URL)
  und verhinderte den allFailed-Fallback, waehrend /api/poison?lat=... (Bbox-URL)
  scheiterte -> jetzt faellt jede Quelle EINZELN auf den letzten guten Stand
  zurueck (localStorage) + Merge aus dem Offline-Region-Snapshot
- Region-Download speichert jetzt auch /api/poison + /api/lost der Gegend
  (p/_poison, p/_lost, anonym; MapOffline.alerts(kind,bbox) als Reader) —
  Sicherheitsdaten muessen auch am vorab gespeicherten Urlaubsort da sein
- lost.js Offline-Pfad merged den Region-Snapshot in Cache- und Leer-Fall
- Routen-Korridor war 'unsichtbar' (lag im schon gespeicherten Gebiet):
  nach dem Speichern werden die gespeicherten Bereiche blau auf der
  Detailkarte eingeblendet; Logik per Node-Stub-Test verifiziert
Bump v1227
2026-06-06 12:15:34 +02:00
42a04ec405 Offline-Karten Runde 2: adaptives Modell (Budget, Funkloch-Gedaechtnis, Korridor, Coverage)
Design Rene 2026-06-06:
- Budget-Download: z14-Ringe um den Standort bis 5 MB gespeicherte Bytes
  (Stadt klein, Land gross — passend zur Funknetzdichte); client-seitig,
  Server-Region-Extract entfaellt
- Funkloch-Gedaechtnis: Tile-Miss bei aktivem GPS-Recording -> Zone gemerkt
  (lokal, nie hochgeladen); Auto-Download offener Zonen sobald online
- Routen-Korridor: 'Offline'-Button im Routen-Detail, Kacheln +-1km um den
  Track + Marker (Cap 50 MB) — fuer mehrtaegige Unternehmungen
- Coverage-Layer: gespeicherte Bereiche als blauer Layer; Offline-Button
  oeffnet Verwaltungs-Modal (Stats, speichern, anzeigen, loeschen)
- Flag-Logik zentral in boot.js BY.offlineTiles() (war 3x dupliziert)
Bump v1226
2026-06-06 12:00:43 +02:00
45534aa8ee Sweep: r.ok-Check bei allen direkten fetch('/api/...')-Aufrufen (SW-503-JSON-Falle)
5 Fundstellen nach dem Marker-Bug-Muster (v1224) gefixt:
- landing-init.js: Stats-Zahlen waeren offline NaN geworden
- social.js: Medien-Upload-Fehler wurde verschluckt (kein ok-Check, kein catch)
- routes.js: Unterwegs-POIs — {detail:...}.filter warf statt sauber []
- map.js: Marker-Melden zeigte Erfolgs-Toast obwohl Request fehlschlug
- settings.js: Update-Check meldete offline faelschlich 'ist aktuell'
Rest geprueft: api.js-Wrapper, wiki/uebungen/trainingsplaene-Helper checken ok,
externe Dienste (Nominatim etc.) laufen nicht ueber den SW-/api/-Zweig.
Bump v1225
2026-06-06 11:42:05 +02:00
e6d6a3e697 Fix: Marker verschwanden offline beim Kartenbewegen — SW-503-JSON als Fehler werten
Der SW beantwortet nicht-cachebare API-GETs offline mit 503 + JSON-Body,
r.json() wirft dann NICHT -> Erfolgs-Pfad ersetzte Marker durch {detail:...}.
_fetchPois prueft jetzt r.ok + Array.isArray, dadurch greift der
IndexedDB-Fallback (MapOffline.pois) in Phase 1 und Phase 2 behaelt den Bestand.
Bump v1224
2026-06-06 11:35:03 +02:00
e2c75f04bc Offline-Karten: POI-Marker offlinetauglich + Offline-Banner klappt ein (Geraetetest-Befunde)
- MapOffline.downloadAround speichert zusaetzlich /api/osm/pois je Typ fuer die
  Region-Bbox in IndexedDB (Key-Praefix p/, Merge per id — zweite Region loescht
  die erste nicht); MapOffline.pois(type,bbox) filtert fuer den Ausschnitt
- map.js Phase-1-Fallback: Fetch fehlgeschlagen (offline) -> gespeicherte
  Region-POIs statt leerer Karte; Download-Toast zeigt Marker-Anzahl
- Offline-Banner: nach 5s auf schmale Icon-Leiste eingeklappt (verdeckte die
  Karten-Legende); Inline-Styles nach components.css konsolidiert
- Bump v1223
2026-06-06 11:25:40 +02:00
70a1f5856a Offline-Karten Follow-ups: Staging-Default AN, Karten-Download-Button, Glyph-Persistenz
- by_offline_tiles Default AN auf staging.banyaro.app (localStorage/?tilesoffline=1/0 uebersteuert)
- Speed-Dial 'Karte offline speichern': GL -> MapOffline.downloadAround(Kartenmitte, 5km),
  Leaflet -> alter Raster-Prefetch (_cacheTiles war seit FAB-Redesign verwaist)
- Glyphs in IndexedDB (Key-Praefix f/) + byt://f/-Protokoll: ueberlebt App-Updates
- OSM-Raster-Prefetch im Offline-Tiles-Modus uebersprungen (GL nutzt das Raster nicht)
- Button-Sichtbarkeit gated: GL ohne Offline-Flag (= Production) zeigt ihn nicht
2026-06-06 11:03:46 +02:00
939e48b0c7 Karten: GL als Default auf Production freigeschaltet (Hostname banyaro.app/.de)
MapLibre-GL-Migration ist verifiziert (Staging) → GL jetzt Default auf allen
deployten Hosts statt nur staging.banyaro.app. localhost/LAN bleibt OSM-Raster
(keine lokalen Tiles), by_map_gl=0 erzwingt weiterhin den Leaflet-Fallback.
Tiles (dach.pmtiles, 15 Länder) + Glyphs liegen nun auch im Prod-Data-Volume.
SW-Bump 1219→1220.
2026-06-05 21:12:24 +02:00
ac187dc740 Radar-Timeline: Slider-Scrubbing gefixt + Breite/Höhe an Status-Pill
- Scrub-Bug: _radarPause() setzte slider.value zurück, BEVOR der gezogene Wert
  gelesen wurde → sprang immer auf 'jetzt'. Jetzt Wert zuerst lesen. Scrubben
  stoppt Play + zeigt den Frame der Position (verifiziert: Klick 20%→Frame 2,
  Setter→5→Frame 5).
- Breite per JS an .map-statusbar angeglichen (gleiche linke + rechte Kante),
  Höhe/Optik an die Pill (kleinerer Play-Button, flacher).
2026-06-05 20:39:41 +02:00
bcbf9a9645 Regenradar: abspielbare Zeitleiste (RainViewer ~2h Verlauf + Nowcast)
Bisher nur der neueste Frame; jetzt alle ~13-16 RainViewer-Frames (past+nowcast,
10-Min-Schritte) mit Play/Pause + Slider + Zeitstempel (jetzt / +N / -N Min,
Vorhersage-Frames bläulich) — RainToday-artig. Frame-Wechsel smooth via
raster setTiles (kein Flackern), Loop, _radarNowIdx = letzter Vergangenheits-Frame.
Timeline unten-mittig, wird bei Radar-AUS entfernt. Pro-Feature wie das Radar.
Headless verifiziert: 13 Frames, Play scrubbt (12→0→1→2), keine Fehler.
2026-06-05 20:12:30 +02:00
aefdac87ad Wetter-Pill: Niederschlag = Höchstwert der nächsten 3 Std statt Tages-Max
weather.py get_weather_for_location: precip = max(h_precip[now_h:now_h+3])
(Fallback Tages-Max). map.js Pill zeigt '💧 X% (3h)'. Gilt auch fürs
Welten-Banner (geteiltes precip_prob-Feld) — behebt das 'ganzer Tag'-Problem
überall. Gespeicherte Tagebuch-Snapshots unberührt (historisch).
2026-06-05 20:01:32 +02:00
8f13f4d38d Offline-Karten: Kern implementiert (Region-Download → IndexedDB → Offline-Render)
map-offline.js (window.MapOffline): lädt Vektorkacheln eines Bereichs via pmtiles.getZxy
in IndexedDB + cacht die Glyphs mit (KRITISCH: ohne Glyphs lässt MapLibre offline die
ganze Kachel fallen). byt://-Protokoll bedient MapLibre IndexedDB-first, remote-Fallback.
- map-gl-style.js: build({offline}) nutzt byt-Source statt pmtiles:// (Flag by_offline_tiles,
  Default AUS bis gerätegetestet); glyphs bleiben /fonts (SW-gecacht)
- ui.js + map.js: map-offline.js mitladen + byt-Protokoll registrieren
- getZxy liefert bereits dekomprimierte MVT (kein gunzip) → ~15 MB/5km in IndexedDB

Headless bewiesen: Download 97 Tiles (5km München) → Netz AUS → 1903 Features gerendert,
nicht geladene Gegend (Paris) korrekt leer. Offen: Download-Button/FAB-Segment-5-Verdrahtung,
adaptives Lernen, Bereichsauswahl/Routen-Korridor (siehe docs/OFFLINE_MAPS_PLAN.md).
2026-06-05 19:46:18 +02:00
c7201aa07b Karten-Attribution: standardmäßig eingeklappt (nur ⓘ) + doppelten Hinweis entfernt
Punkt 6: MapLibre rendert die Compact-Attribution offen (maplibregl-compact-show
+ open) → voller Text '© OpenStreetMap contributors' immer sichtbar. Neuer Helper
MapGLStyle.collapseAttribution() entfernt die Klasse/open nach dem Hinzufügen →
nur noch das ⓘ, der Text erscheint erst auf Klick (rechtlich nach ODbL ausreichend).
In map-gl-mini.js (Seitenkarten) + map.js (zentrale Karte) verdrahtet.

Punkt 7: poison.js + lost.js hatten UNTER der Karte zusätzlich ein hartkodiertes
'© OpenStreetMap-Mitwirkende' — doppelt zum Karten-ⓘ. Entfernt (+ ungenutzte
.lost-map-attribution CSS-Klasse). Verifiziert: osmTextLeafCount 2-3 → 1, compactShown true → false.
2026-06-05 15:48:11 +02:00
3523a44a0b MapLibre: GL als Staging-Default + Feinschliff (Cluster-Zahlen, Theme-Robustheit)
- _useGL: Staging default-AN (Prod aus, ?mapgl=0 überschreibt) → Breitentest
- Cluster zeigen ZAHL (point_count) statt Icon (Glyphs vorhanden)
- Theme-Wechsel: Wetter-Raster + Rec-Track nach setStyle neu anlegen; Click-Handler nur einmal binden (keine doppelten Popups)
2026-06-05 11:26:55 +02:00
425f99effb MapLibre-Port 3b+3c: Wetter + GPS-Aufzeichnung für GL entgaten
3b Wetter: engine-neutrale Helfer (_wxAddRaster→raster-source unter den Markern, _mapOnMove,
   _wxAddTempMarker→maplibregl.Marker). _toggleRadar/_loadRadar/_toggleTemp/_loadTempLabels gebrancht.
3c GPS: _recTrackGL (geojson line-source), _updateRecMarker (maplibregl.Marker), _recCleanupMap.
   _updateRecMap/_startRecording-Resume/Cleanup gebrancht, Gates entfernt. panTo [lng,lat] für GL.
2026-06-05 11:19:40 +02:00
9c959dd632 GL-Karte: Ortsnamen-Labels (Glyphs self-hosted) + ScaleControl raus (lag unter der Status-Pill)
- main.py: /fonts-Mount (Glyph-PBFs aus data/tiles/fonts), Open Sans Regular self-hosted
- map-gl-style.js: glyphs-URL + Label-Layer (Ortsnamen/Straßen/Gewässer, name:de)
- map.js _initMapGL: ScaleControl entfernt (überdeckte die Zoom/Wetter/Zecken-Pill)
Ortsnamen für Orientierung (René), auch bei kleinem Zoom.
2026-06-05 11:09:08 +02:00
4d0cd0f460 Karten-Fix: fraktionalen MapLibre-Zoom für Scan-Schwelle runden
MapLibre-Zoom ist kontinuierlich (13.7); Statusleiste rundet (zeigt Z14), Scan verglich roh
→ bei angezeigtem Z14 (echt 13.x) galt zoom<14 → Marker gecleart, erst ab Z15 sichtbar.
Jetzt Math.round(getZoom()) für die 10/14-Schwellen (wie die Anzeige). Leaflet unverändert (ganzzahlig).
2026-06-05 11:01:09 +02:00
980338d7f1 Karten-Fix: Scan-Race bei schnellen Zoom-Folgen (Z16→Z13→Z14 → keine Marker)
_loadOsmLayers verwarf Scan-Anfragen, die während eines laufenden Scans kamen (return).
Bei langsamem Overpass (Handy) ging so der finale Z14-Scan verloren → Marker leer.
Jetzt: _scanQueued merkt die Anfrage vor, finally holt sie nach → letzte Ansicht wird garantiert gescannt.
2026-06-05 10:56:08 +02:00
2d7eca16a7 MapLibre-Port 3a-Fix: Scanner-Endlosschleife (resize im Scan) + Pinch-Page-Zoom + Rotation
- _loadOsmLayers: kein _map.resize() für GL (löste move→moveend→scan-Loop aus, 'pausenlos')
- _initMapGL: touchZoomRotate.disableRotation() + touchPitch.disable() + container touch-action:none
  → Pinch=reines Zoom, bleibt in der Karte (kein iOS-Page-Zoom), keine ungewollte Drehung
2026-06-05 10:26:35 +02:00
542106e77b MapLibre-Port Runde 3a: MapGLMarkers in map.js verdrahtet (flag-gated)
- _loadOsmLayers/_loadAll/_addPlaces/_addPoison/_addBreeders/_applyVisibility/_deleteUserPoi
  engine-neutral (GL: POI-DATEN statt Marker, MapGLMarkers.setLayer/setVisible)
- Standort-Dot, Place-Picker-Temp-Marker, Such-Marker als maplibregl.Marker
- engine-spez. Aufrufe über Facade (_mapFlyTo/_mapSetView/_mapResize)
- Wetter + GPS-Recording für GL vorerst gegated (Port in 3b/3c)
- Flag 'by_map_gl'/?mapgl=1, default AUS
2026-06-05 10:06:54 +02:00
63c9be68c6 MapLibre-Port Runde 1: Engine-Fundament (flag-gated, Default Leaflet)
map.js: _useGL()/loadMapLibre()/_initMapGL() + engine-neutrale Facade
(_mapFlyTo/_mapSetView/_mapGetZoom/_mapResize/_mapGetCenter/_mapPaddedBounds, kapselt
[lat,lon]↔[lng,lat]). init() verzweigt auf GL bei Flag 'by_map_gl'/?mapgl=1. Basemap+
Controls+Dark(setStyle)+Scan-Wiring+Crosshair. POI-Layer/Marker = Runde 2. Flag default AUS.
2026-06-05 09:37:59 +02:00
1d64dc5d70 Fix: zentrale Karte nutzte window.UI (undefined) statt bare UI → immer Raster-Branch
ui.js exponiert 'const UI' = globales lexikalisches Binding (bare UI), NICHT window.UI.
map.js _addBasemap prüfte window.UI → immer falsy → Vektor-Pfad nie genommen.
Jetzt: typeof UI-Check. (window.UI?.toast in boot/api/app sind separat tote No-Ops.)
2026-06-05 09:06:09 +02:00
647aa684db Vektor-Basemap: zentrale Karte (pages/map.js) integrieren — sie umging UI.map.create
- map.js _addBasemap: Vektor-Layer (Flag) mit Raster-Fallback, eigener Basemap-Code
- Theme-Wechsel baut Vektor-Layer mit passendem Flavor neu (kein CSS-Filter bei Vektor)
- ui.js: UI.map.vectorEnabled()/vectorLayer() exponiert für Karten mit eigenem Layer-Mgmt
- APP_VER bump
2026-06-05 08:28:11 +02:00
78866206b4 Feature: Routenaufzeichnung übersteht App-Updates (Guard + Persistenz)
Stufe 1 (Guard): Während aktiver Aufzeichnung wird der SW-/Force-Update-Reload
aufgeschoben (window._byRecording → boot.js/_bySwReload + app.js force-update);
nach Stop/Speichern via window._byReloadIfPending() nachgeholt.

Stufe 2 (Persistenz): Track wird gedrosselt nach localStorage (RecStore) gesichert
und beim nächsten Öffnen der Karten-/Routen-Seite als 'Aufzeichnung fortsetzen?'
angeboten (Resume seedet Track+km+Startzeit). Schützt auch bei Crash/OS-Kill/
manuellem Reload. Greift in map.js UND routes.js. SW v1167
2026-06-04 17:13:23 +02:00
9afbf24535 OSM-Beiträge: "Hund willkommen?" 👍/👎 (dog=yes/no) + Umdrehen
- dog=no zusätzlich zu dog=yes (Pächterwechsel → Ort nicht mehr hundefreundlich).
- Map-Popup: ein "Hund willkommen?"-Block mit Daumen hoch/runter statt zwei
  Buttons. Beide rufen /dog-friendly mit welcome=true|false.
- Backend generisch: tag_value yes|no; vorhandene Markierung mit anderem Wert
  wird umgedreht (Update statt 409); submit_dog_tag(value); Confirm/Revert prüft
  gegen den jeweiligen tag_value; Changeset-Kommentar wertabhängig.
2026-06-03 21:49:44 +02:00
57849515ea OSM-Beiträge: Map-Button (dog=yes), Changeset-Upload, Confirm/Pro-Job
- Map-Popup: "Hund war willkommen"-Button (dog=yes) für Restaurant/Hotel/
  Shop/Tierarzt/Hundesalon → POST /osm-contrib/dog-friendly.
- OSM-Changeset-Upload (write_api): Element holen (node/way) → dog=yes →
  Changeset create/upload/close; idempotent; best-effort beim Tap.
- OSM-Endpunkte konfigurierbar (OSM_OAUTH_BASE/OSM_API_BASE) — Staging gegen
  Dev-Sandbox, KEINE echten Edits auf Produktiv-OSM.
- Scheduler-Job (täglich 03:40): Pending-Retry + Revert-Überleben (7 Tage) →
  confirmed/rejected; Pro-Freischaltung (100 confirmed = 1 Jahr, idempotent via
  osm_pro_grants). HINWEIS: is_premium/subscription direkt gesetzt — vor Prod
  mit Billing abgleichen.
- Native Attestierung/Sensoren: bewusst NICHT (iOS-App-Thema, nicht PWA).
2026-06-03 21:40:50 +02:00
10e39ed135 Karten-Ausbau (OSM), Forum-Erweiterung, UI-Komponenten, Refactor Tagebuch/Gassi (DRY), Landing/SEO — APP_VER 1155 2026-06-03 17:24:47 +02:00
c517c9281d Refactor: 1167 _esc() → UI.escape() in 36 Dateien, SW by-v1113
Bündel 1 aus dem Duplikat-Audit: existierende zentrale Helper nutzen
statt lokale Duplikate.

Pure Migration ohne neuen Code:
- 1167 _esc()-Aufrufe in 36 Page-Modulen migriert auf UI.escape()
- 24 lokale _esc/_escape-Definitionen entfernt
- lost.js hatte _escape() (Variante) — 17 Aufrufe ebenfalls migriert
- jobs.js + breeder.js: tote Alias-Wrapper entfernt

UI.escape() existierte schon — wurde nur überall lokal nochmal
implementiert. Funktional identisch (gleiche 4-replace-chain für
& < > ").

Tests 19/19 grün. Frontend-LOC um ~120 Zeilen reduziert.

Hinweis: _emptyState (7 Stellen) und _icon (8 Stellen) wurden NICHT
migriert — sie haben abweichende Signaturen von UI.emptyState({...})
bzw. UI.icon(name). Eigener Sprint nötig.
2026-05-27 10:15:33 +02:00
459cd425f2 Design-System Sprint A: utilities.css + 948 Inline-Styles → Utility-Klassen, SW by-v1102
PHASE 1 — Sofort-Cleanup ohne Risiko:
- Neue Datei utilities.css mit ~25 Klassen für häufige Kombinationen:
  * text-xs-muted, text-xs-secondary, text-sm-muted, text-sm-secondary
  * flex-gap-2/3, flex-col-gap-2/3/4, flex-center-gap-1/2/3
  * flex-between, flex-1-min, mb-1/3, mt-1/3
  * icon-xs/sm/md/lg, label-block, caption
- index.html bindet utilities.css ein
- mb-3/mt-3 ergänzt (waren in design-system.css unvollständig)

PHASE 2 — .by-tab Modifier für Vereinheitlichung:
- .by-tabs.grid (mit --tab-cols Variable für Admin/Health/etc.)
- .by-tabs.sticky (Desktop vertikale Tabs für Admin)
- .by-tabs.wrap (Zuchthunde, flex-wrap statt scroll)
- .by-tabs.separated (Sitting, mit eigenem Hintergrund + Border)

PHASE 3 — Inline-Style → Klassen-Migration (Python-Script):
- 948 Inline-Styles entfernt (5101 → 4153, -18%)
- 962 Migrationen über 47 Page-Dateien
- Top-Treffer: admin.js (180), health.js (67), dog-profile.js (67),
  litters.js (62), settings.js (61), zuchthunde.js (51)
- Patterns: text-muted, text-secondary, text-danger, text-xs-muted,
  text-sm-muted, grid-2 (Duplikat-Bug behoben!), flex-col-gap-3,
  p-3/4, mb-2/3/4, hidden, w-full, flex-1, ...
- Bewahrt bestehende class-Attribute (mergt korrekt)

Alle 19 Tests grün. Kein visueller Diff erwartet (gleiche Property-Werte).
2026-05-27 07:11:27 +02:00
cc4f030fd0 Feature: Hundesalons in der Karte, SW by-v1098
- OSM-Query 'hundesalon' nutzt shop=pet_grooming + craft=pet_grooming
- map.js: neuer Layer 'hundesalon' mit Schere-Icon (#EC4899 Pink),
  in Layer-Liste, TYPEN, OSM_LAYER_MAP und PIN_TYPES eingetragen
- User können selbst Hundesalons als POI anlegen
  (places.py TYPEN + osm.py ALLOWED_TYPES erweitert)
2026-05-26 21:13:16 +02:00
19640af288 Style: Marker + Cluster Rand Tactical-Olive statt Weiß
rgba(255,255,255,0.7/.8) → rgba(52,68,36,0.55/.65)
Dezentes Dunkeloliv auf allen Marker-Icons und Cluster-Bubbles.
SW by-v1025, APP_VER 1025
2026-05-16 11:35:01 +02:00
2cf0bc0d97 Fix: Karte CSS-Filter statt CartoDB Dark, Social Wrapper 860px
Karte Dark-Mode:
- CSS-Filter statt CartoDB Dark Matter (war zu dunkel)
  invert(93%) hue-rotate(180deg) brightness(0.88) contrast(0.88) saturate(0.85)
  Gleiche OSM-Tiles, Marker bleiben unbeeinflusst (tilePane separater Layer)
- Filter sofort bei Init + bei Theme-Wechsel via MutationObserver

Social:
- Outer Wrapper width:100%;max-width:860px;margin:0 auto
  Header-Karte und Tabs sind jetzt konsistent gleich breit
SW by-v1023, APP_VER 1023
2026-05-16 11:19:37 +02:00
721e630a34 Fix: Dark-Mode Karte + Badge-Farben + --c-bg-secondary
Karte:
- Dark-Mode: CartoDB Dark Matter Tiles statt OSM Standard
- MutationObserver + matchMedia watchee für Live-Theme-Wechsel
- _buildTileLayer() / _applyTileTheme() / _isDarkMode()

Badges litters.js:
- Hardgekodete dunkle Hintergründe → CSS-Klassen (badge-warning/-success/-muted)
- Funktioniert jetzt in Light + Dark Mode korrekt

movies.css:
- .movie-tag-stirbt/.movie-tag-ueberlebt → CSS-Variablen (danger-/success-subtle)
- Kein weißer Hintergrund mehr in Dark Mode

--c-bg-secondary: Zoom-Control 30px bleiben aus dem letzten Commit
SW by-v1022, APP_VER 1022
2026-05-16 11:11:09 +02:00
e44414015a Fix: Karte — Zoom-Ausrichtung, 'Z14' kürzer, Marker-Count ausgeblendet
- leaflet-top padding-top 28→33px (Zoom-Buttons mittig zu 2 Filter-Reihen)
- Zoom-Anzeige: 'Zoom 14 · ab 14: alle Layer' → 'Z14' (Hinweis als title)
- #map-osm-status ausgeblendet (Marker-Anzahl nimmt Platz von Wetter etc.)
- components.css ?v=1012, SW by-v1019, APP_VER 1019
2026-05-16 10:39:12 +02:00
562d64979f Fix: Karte — Filter-Button mit Label, kürzere Chip-Texte
- #map-legend-all: 'Filter' Text neben dem List-Icon
- 'Hundefreundl. Café/Restaurant' → 'Café & Restaurant'
- 'Hundefreundl. Hotel' → 'Hotel'
- .map-legend-all: padding angepasst für Text-Label
- components.css ?v=1011, SW by-v1018, APP_VER 1018
2026-05-16 10:32:11 +02:00
53fcb61933 Offline-Fallbacks für diary, poison, map + SW-Erweiterung
- sw.js: /api/places, /api/breeder/map-markers, /api/gassi-zeiten in _CACHEABLE_GET; /api/lost/report und /api/walks in _QUEUEABLE
- diary.js: localStorage-Cache pro Hund, Fallback bei Offline mit Toast
- poison.js: localStorage-Cache, Fallback bei Offline mit Toast (sicherheitsrelevant)
- map.js: POI-Cache (places/poison/breeders) in localStorage, Offline-Toast + Fallback auf gecachte Daten
2026-05-15 17:02:26 +02:00