diff --git a/app/public/app-ui.css b/app/public/app-ui.css index d999ac8..1cdcf70 100644 --- a/app/public/app-ui.css +++ b/app/public/app-ui.css @@ -241,3 +241,17 @@ body:not([data-tab="conditions"]) #loading { display: none; } .app { padding-left: calc(var(--gutter) + env(safe-area-inset-left, 0px)); padding-right: calc(var(--gutter) + env(safe-area-inset-right, 0px)); } .topbar { margin-left: calc(-1 * (var(--gutter) + env(safe-area-inset-left, 0px))); margin-right: calc(-1 * (var(--gutter) + env(safe-area-inset-right, 0px))); } .install-dialog { margin-top: max(1rem, env(safe-area-inset-top, 0px)); } + +/* ── Map tab: the map is the page (not a 56vh strip); timeline and details sit below it ── */ +body:not(.map-fs) #tab-unified-map .unified-map-wrap { height: auto; min-height: 0; padding: 0.7rem; gap: 0.6rem; border-radius: 16px; } +body:not(.map-fs) #tab-unified-map .unified-map-container { + height: max(540px, calc(100dvh - var(--topbar-h) - 250px)); border-radius: 12px; +} +@media (max-width: 640px) { + body:not(.map-fs) #tab-unified-map .unified-map-container { + height: max(460px, calc(100dvh - var(--topbar-h) - 58px - max(6px, env(safe-area-inset-bottom, 0px)) - 190px)); + } +} +.map-controls-header { padding-bottom: 0.5rem; gap: 0.5rem; } +.map-recenter { font: inherit; font-size: 0.78rem; font-weight: 700; min-height: 40px; padding: 0 0.9rem; border-radius: 999px; border: 1px solid var(--border); background: var(--paper-dark); color: var(--ink); cursor: pointer; margin-left: auto; white-space: nowrap; } +.map-recenter:hover { border-color: var(--accent); color: var(--accent); } diff --git a/app/public/app-ui.js b/app/public/app-ui.js index f0132f2..2fa23a7 100644 --- a/app/public/app-ui.js +++ b/app/public/app-ui.js @@ -378,6 +378,33 @@ function updateRadar(city, outlook) { if (map.isStyleLoaded()) apply(); else map.once('idle', apply); } + +// ── Map tab: independent, remembered view (not tied to the main-page radar) ── +let mapCenteredFor = null; +const mapCityKey = (c) => `${(+c.latitude).toFixed(2)},${(+c.longitude).toFixed(2)}`; +function centerMap(city, force) { + const m = typeof mapsOverview !== 'undefined' ? mapsOverview?.map : null; + if (!m || city?.latitude == null) return; + const key = mapCityKey(city); + if (!force && mapCenteredFor === key) return; // keep wherever the user panned/zoomed to + let saved = null; + try { saved = JSON.parse(localStorage.getItem('mapView') || 'null'); } catch {} + if (!force && !mapCenteredFor && saved && saved.key === key && Number.isFinite(saved.zoom)) m.jumpTo({ center: [saved.lng, saved.lat], zoom: saved.zoom }); + else mapsOverview.panTo(city.latitude, city.longitude, force ? 7 : 5); // wider regional view than the main-page radar + mapCenteredFor = key; +} +(() => { + const m = typeof mapsOverview !== 'undefined' ? mapsOverview?.map : null; + if (!m) return; + let t = 0; + m.on('moveend', () => { + if (!mapCenteredFor) return; + clearTimeout(t); + t = setTimeout(() => { try { const c = m.getCenter(); localStorage.setItem('mapView', JSON.stringify({ key: mapCenteredFor, lng: c.lng, lat: c.lat, zoom: m.getZoom() })); } catch {} }, 400); + }); + $('map-recenter-btn')?.addEventListener('click', () => { if (state.currentCity) centerMap(state.currentCity, true); }); +})(); + // ── Public hooks ───────────────────────────────────────────────────────────── let lastOutlook = null; window.AppUI = { @@ -399,6 +426,7 @@ window.AppUI = { if (state.currentAir) renderAqi(state.currentAir); if (stationFresh) renderStationContext(); }, + centerMap, onAir(air) { renderAqi(air); }, refreshFx() { const c = $('sky-fx'); if (c && fx.kind) { cancelAnimationFrame(fx.raf); fxResize(c); fxSeed(fx.kind); if (fx.parts.length) fxFrame(performance.now()); } }, onStation() { syncTopbarLocation(); renderStationContext(); }, diff --git a/app/public/app.js b/app/public/app.js index 2cfb03d..5986ece 100644 --- a/app/public/app.js +++ b/app/public/app.js @@ -1281,8 +1281,9 @@ function refreshMapsOverview() { if (!state.currentCity) return; - const zoom = mapsOverview.map.getZoom ? mapsOverview.map.getZoom() : 6; - mapsOverview.panTo(state.currentCity.latitude, state.currentCity.longitude, zoom || 6); + // Keep the user's own pan/zoom; only (re)centre when the location changes + if (window.AppUI && window.AppUI.centerMap) window.AppUI.centerMap(state.currentCity); + else mapsOverview.panTo(state.currentCity.latitude, state.currentCity.longitude, mapsOverview.map.getZoom ? mapsOverview.map.getZoom() : 6); clearLightningMarkers(); clearWindArrows(); @@ -1599,8 +1600,8 @@ function renderWeather(data, city) { radarEmbed.panTo(city.latitude, city.longitude, 6); radarFull.panTo(city.latitude, city.longitude, 6); - const mapZoom = mapsOverview?.map?.getZoom ? mapsOverview.map.getZoom() : 6; - mapsOverview.panTo(city.latitude, city.longitude, mapZoom || 6); + if (window.AppUI && window.AppUI.centerMap) window.AppUI.centerMap(city); + else mapsOverview.panTo(city.latitude, city.longitude, (mapsOverview?.map?.getZoom ? mapsOverview.map.getZoom() : 6) || 6); if (mapsWindEl) mapsWindEl.textContent = `${speedDisplay(c.wind_speed_10m)} ${speedUnit()} ${compassDir(c.wind_direction_10m)}`; updateSourceLocation(city); updateShareUrl(city); diff --git a/app/public/index.html b/app/public/index.html index 504890f..0674d00 100644 --- a/app/public/index.html +++ b/app/public/index.html @@ -259,6 +259,7 @@ + @@ -351,7 +352,7 @@
-
+
Temp -- AQI --