diff --git a/VERSION b/VERSION
index 02be51a..847dd18 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1211
\ No newline at end of file
+1213
\ No newline at end of file
diff --git a/backend/static/index.html b/backend/static/index.html
index 5987a83..77b269b 100644
--- a/backend/static/index.html
+++ b/backend/static/index.html
@@ -86,14 +86,14 @@
Ban Yaro
-
+
-
-
-
-
-
+
+
+
+
+
@@ -617,11 +617,11 @@
-
-
-
-
-
+
+
+
+
+
@@ -631,7 +631,7 @@
-
+
diff --git a/backend/static/js/app.js b/backend/static/js/app.js
index 55a367c..00af0f7 100644
--- a/backend/static/js/app.js
+++ b/backend/static/js/app.js
@@ -3,7 +3,7 @@
Router, State-Management, Navigation, Initialisierung.
============================================================ */
-const APP_VER = '1211'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
+const APP_VER = '1213'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const APP_VERSION = '1.6.0'; // ← semantische Version, wird bei make release gesetzt
window.APP_VER = APP_VER; // global verfügbar für andere Module (z.B. offline-indicator)
window.APP_VERSION = APP_VERSION;
diff --git a/backend/static/js/offline-indicator.js b/backend/static/js/offline-indicator.js
index 234fca0..55e93b1 100644
--- a/backend/static/js/offline-indicator.js
+++ b/backend/static/js/offline-indicator.js
@@ -20,6 +20,34 @@ window.OfflineIndicator = (() => {
return found ? await caches.open(found) : null;
}
+ // GL-Offline-Tiles-Modus (byt://-Vektorkacheln in IndexedDB) statt OSM-Raster.
+ function _offlineTilesMode() {
+ try { return localStorage.getItem('by_offline_tiles') === '1'; } catch (e) { return false; }
+ }
+ // Ist eine Offline-Region (Vektorkacheln) in IndexedDB gespeichert? (ohne MapOffline zu laden)
+ // WICHTIG: dasselbe Schema/Version wie map-offline.js anlegen — sonst legt ein versionsloses open()
+ // die DB leer an und MapOffline kann seine Stores nicht mehr erstellen.
+ function _offlineRegionStored() {
+ return new Promise(res => {
+ try {
+ const r = indexedDB.open('by-offline-tiles', 1);
+ r.onupgradeneeded = () => {
+ const d = r.result;
+ if (!d.objectStoreNames.contains('tiles')) d.createObjectStore('tiles');
+ if (!d.objectStoreNames.contains('meta')) d.createObjectStore('meta');
+ };
+ r.onsuccess = () => {
+ const db = r.result;
+ if (!db.objectStoreNames.contains('tiles')) { db.close(); return res(false); }
+ const cnt = db.transaction('tiles', 'readonly').objectStore('tiles').count();
+ cnt.onsuccess = () => { res(cnt.result > 0); db.close(); };
+ cnt.onerror = () => { res(false); db.close(); };
+ };
+ r.onerror = () => res(false);
+ } catch (e) { res(false); }
+ });
+ }
+
const CHECKS = [
{ step: 1, title: 'App-Grundgerüst',
detail: 'CSS, Layout und Hauptmodule — die Basis',
@@ -69,8 +97,10 @@ window.OfflineIndicator = (() => {
} },
{ step: 5, title: 'Karten-Kacheln',
- detail: `Mindestens ${TILE_MIN} OSM-Tiles im Umkreis`,
+ detail: 'Karten für deine Gegend offline verfügbar',
probe: async () => {
+ // GL-Modus: gespeicherte Vektor-Region in IndexedDB (das alte OSM-Raster nutzt die GL-Karte nicht).
+ if (_offlineTilesMode()) return _offlineRegionStored();
const c = await caches.open(CACHE_TILES).catch(() => null);
if (!c) return false;
return (await c.keys()).length >= TILE_MIN;
@@ -169,12 +199,30 @@ window.OfflineIndicator = (() => {
tasks.push(fetch('/api/routes').catch(() => {}));
tasks.push(fetch('/api/notes').catch(() => {}));
} else if (m.step === 5) {
- await _prefetchTiles();
+ if (_offlineTilesMode()) await _downloadOfflineRegion();
+ else await _prefetchTiles();
}
}
await Promise.all(tasks);
}
+ // GL-Offline: Vektor-Region (~5 km) um den aktuellen Standort in IndexedDB laden.
+ async function _downloadOfflineRegion() {
+ let pos = null;
+ try { pos = await API.getLocation(); } catch (e) {}
+ if (!pos) {
+ try {
+ const raw = localStorage.getItem(LS_LAST_POS);
+ if (raw) { const p = JSON.parse(raw); pos = { lat: p.lat, lon: p.lon }; }
+ } catch (e) {}
+ }
+ if (!pos) { UI.toast.warning('Standort nötig, um die Gegend offline zu speichern.'); return; }
+ try {
+ await UI.loadMapLibreUI();
+ if (window.MapOffline) await MapOffline.downloadAround(pos.lat, pos.lon, 5);
+ } catch (e) { console.warn('Offline-Region-Download fehlgeschlagen:', e); }
+ }
+
// ----------------------------------------------------------
// Tile-URL-Berechnung (OSM, Subdomain 'a')
// ----------------------------------------------------------
diff --git a/backend/static/js/ui.js b/backend/static/js/ui.js
index 23a71d5..b3d66a8 100644
--- a/backend/static/js/ui.js
+++ b/backend/static/js/ui.js
@@ -1800,6 +1800,7 @@ const UI = (() => {
escape, escHtml, help, pageInfo,
saveToAlbum,
loadLeaflet,
+ loadMapLibreUI,
leafletMarker,
locationPicker,
map,
diff --git a/backend/static/landing.html b/backend/static/landing.html
index 2cbc2a4..5f3d168 100644
--- a/backend/static/landing.html
+++ b/backend/static/landing.html
@@ -4,7 +4,7 @@
-
+
Ban Yaro — Die Hunde-App für Deutschland, Österreich & Schweiz
diff --git a/backend/static/sw.js b/backend/static/sw.js
index b9fa861..8a9b471 100644
--- a/backend/static/sw.js
+++ b/backend/static/sw.js
@@ -4,7 +4,7 @@
============================================================ */
// ← EINZIGE Stelle für die Version — STATIC_ASSETS und CACHE_VERSION leiten sich ab
-const VER = '1211';
+const VER = '1213';
const CACHE_VERSION = `by-v${VER}`;
const CACHE_STATIC = `${CACHE_VERSION}-static`;
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
diff --git a/docs/OFFLINE_MAPS_PLAN.md b/docs/OFFLINE_MAPS_PLAN.md
index c4277b2..1e6ca0d 100644
--- a/docs/OFFLINE_MAPS_PLAN.md
+++ b/docs/OFFLINE_MAPS_PLAN.md
@@ -1,8 +1,29 @@
# Offline-Karten (GL/Vektor) — Feature-Plan
-**Status:** geplant (Umsetzung nach Tile-Build/Produktions-Rollout der GL-Karten).
+**Status:** KERN UMGESETZT + headless verifiziert (2026-06-05, v1213), **flag-gated `by_offline_tiles` (Default AUS)** bis Gerätetest.
**Stand:** 2026-06-05. Autor: René + Claude (Design).
+## Umsetzungsstand (2026-06-05)
+**✅ Fertig + headless bewiesen:**
+- `map-offline.js` (`window.MapOffline`): Region-Download (`downloadAround(lat,lon,radiusKm)`) → Vektorkacheln
+ z0–14 via `pmtiles.getZxy` (liefert bereits dekomprimierte MVT) + Glyphs in **IndexedDB** (`by-offline-tiles`).
+ `byt://`-MapLibre-Protokoll (IndexedDB-first, remote-Fallback). ~15 MB / 5 km (dekomprimiert).
+- `map-gl-style.js` `build({offline})`: `byt`-Source statt `pmtiles://`. Flag `by_offline_tiles` (Default AUS).
+- ui.js/map.js laden map-offline + registrieren `byt`. `UI.loadMapLibreUI` exportiert.
+- Welten-FAB Segment 5: prüft im GL-Modus gespeicherte Region (nicht mehr OSM-Raster); „Fehlende nachladen"
+ stößt `MapOffline.downloadAround(GPS, 5km)` an.
+- **Beweis:** Download 97 Tiles (5 km München) → Netz AUS → **1903 Features gerendert**, nicht geladene
+ Gegend (Paris) leer; Glyphs nötig (sonst lässt MapLibre offline die ganze Kachel fallen).
+
+**🔲 Offen (Follow-ups):**
+- **Gerätetest (iOS-PWA offline/IndexedDB)** → dann Flag-Default auf Staging-AN (analog `by_map_gl`).
+- Download-Button auf der **Karte** (`map-offline-btn`) im GL-Modus auf `downloadAround(Karten-Center)` umbiegen
+ (bisher OSM-Raster-Prefetch).
+- **Adaptives Lernen** (rollendes Vorausladen beim Aufzeichnen + Funkloch-Gedächtnis).
+- **Bereichsauswahl / Routen-Korridor** (inkl. „Route offline speichern" aus routes.js `_openDetail`).
+- **Glyph-Persistenz** über App-Updates (aktuell SW-Cache, wird bei Update gepurged) → in IndexedDB ablegen + via `byt://f/` servieren.
+- Alten OSM-Raster-Prefetch (`offline-indicator.js _prefetchTiles`) entfernen, wenn Flag dauerhaft AN.
+
## Ziel
GL-Vektorkarten offline-tauglich machen — Kernszenario **Gassi/Wandern im Funkloch**.
Selbst-zielend (cacht wo nötig, nicht überall), speichersparsam, ohne Nutzeraufwand.