Redesign main app: slim top bar, dynamic sky hero, nowcast, scrub chart, unified palette
- One slate/blue palette across the app, dashboard, account/login pages (data colours stay orange = warm, blue = cool on the dashboard) - Slim sticky top bar with location chip, gear (settings) and ? (learn); 4-tab nav (Now, Station, Map, Alerts) with a thumb-reach bottom bar on phones - Hero sky gradient by condition/time with rain, storm, snow and star effects (respects reduced motion); keeps the searched city and badges when temps come from your station - One-line summary, 'Next 2 hours' bars and rain countdown from 15-minute precipitation, rain/cold-aware 'Bring' chips; yesterday + 'vs usual' context from the station - Scrubbable hourly chart (temp, rain chance, wind), 7-day range bars, AQI gauge, details tiles instead of drawers, taller radar with location marker + alert polygons - Skeleton loading instead of a full-screen spinner, sticky section headers, safe areas - Server: minutely_15 precipitation, public /api/station/summary Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1830fb2e2b
commit
fd61e35b56
16 files changed
+846
-221
No files matched your search
+22
-16
@@ -746,7 +746,7 @@ function renderSavedCities() {
|
||||
// ── Tab switching ─────────────────────────────────────────────────────────────
|
||||
|
||||
function activateTab(tabName) {
|
||||
document.querySelectorAll('.tab, .bottom-tab').forEach(t => t.classList.remove('active'));
|
||||
document.querySelectorAll('.tab, .bottom-tab, .icon-btn[data-tab]').forEach(t => t.classList.remove('active'));
|
||||
document.querySelectorAll('.tab-panel').forEach(p => hide(p));
|
||||
document.querySelectorAll(`[data-tab="${tabName}"]`).forEach(t => t.classList.add('active'));
|
||||
show(document.getElementById('tab-' + tabName));
|
||||
@@ -768,7 +768,7 @@ function activateTab(tabName) {
|
||||
refreshSavedCityWeather();
|
||||
}
|
||||
}
|
||||
document.querySelectorAll('.tab, .bottom-tab').forEach(btn => {
|
||||
document.querySelectorAll('.tab, .bottom-tab, .icon-btn[data-tab]').forEach(btn => {
|
||||
btn.addEventListener('click', () => activateTab(btn.dataset.tab));
|
||||
});
|
||||
|
||||
@@ -1609,6 +1609,8 @@ function renderWeather(data, city) {
|
||||
|
||||
// Apply station overlay if we have fresh station data
|
||||
if (state.ambientDevice) applyStationOverlay(state.ambientDevice);
|
||||
|
||||
if (window.AppUI) window.AppUI.onWeather(data, city);
|
||||
}
|
||||
|
||||
function renderDailyForecast(daily) {
|
||||
@@ -1619,16 +1621,22 @@ function renderDailyForecast(daily) {
|
||||
const maxDays = state.showExtendedForecast ? Math.min(16, totalDays) : Math.min(7, totalDays);
|
||||
|
||||
list.innerHTML = '';
|
||||
const hiAll = daily.temperature_2m_max.slice(0, maxDays), loAll = daily.temperature_2m_min.slice(0, maxDays);
|
||||
const wkMin = Math.min(...loAll), wkMax = Math.max(...hiAll), span = Math.max(wkMax - wkMin, 1);
|
||||
for (let i = 0; i < maxDays; i++) {
|
||||
const dw = wmo(daily.weather_code[i]);
|
||||
const lo = daily.temperature_2m_min[i], hi = daily.temperature_2m_max[i];
|
||||
const left = ((lo - wkMin) / span) * 100, width = Math.max(((hi - lo) / span) * 100, 6);
|
||||
const pop = daily.precipitation_probability_max[i];
|
||||
const row = document.createElement('div');
|
||||
row.className = 'day-row' + (i === 0 ? ' today' : '');
|
||||
row.innerHTML = `
|
||||
<div class="day-name">${formatDayShort(daily.time[i])}</div>
|
||||
<div class="day-icon">${dw.icon}</div>
|
||||
<div class="day-desc">${dw.desc}</div>
|
||||
<div class="day-temps">${tempDisplay(daily.temperature_2m_max[i])}°<span class="day-lo"> / ${tempDisplay(daily.temperature_2m_min[i])}°</span></div>
|
||||
<div class="day-meta">${daily.precipitation_probability_max[i] > 0 ? daily.precipitation_probability_max[i] + '% rain<br>' : ''}${speedDisplay(daily.wind_speed_10m_max[i])} ${speedUnit()}</div>
|
||||
<div class="day-icon" title="${dw.desc}">${dw.icon}</div>
|
||||
<div class="day-pop">${pop > 0 ? `<span class="pop-drop">💧</span>${pop}%` : ''}</div>
|
||||
<div class="day-lo-t">${tempDisplay(lo)}°</div>
|
||||
<div class="day-range" title="${dw.desc} · wind ${speedDisplay(daily.wind_speed_10m_max[i])} ${speedUnit()}"><span class="day-range-bar" style="left:${left.toFixed(1)}%;width:${width.toFixed(1)}%"></span></div>
|
||||
<div class="day-hi-t">${tempDisplay(hi)}°</div>
|
||||
`;
|
||||
list.appendChild(row);
|
||||
}
|
||||
@@ -1661,6 +1669,7 @@ function renderAirQuality(data) {
|
||||
if (panelAqi) panelAqi.textContent = cur.us_aqi ?? '--';
|
||||
|
||||
setMapOverlayReadouts({ aqi: cur.us_aqi ?? '--' });
|
||||
if (window.AppUI) window.AppUI.onAir(data);
|
||||
}
|
||||
|
||||
function normalizeSeverity(severity) {
|
||||
@@ -2658,7 +2667,7 @@ syncOfflineState();
|
||||
|
||||
// ── Swipe gestures for tab switching ──────────────────────────────────────────
|
||||
(function initSwipe() {
|
||||
const TABS = ['conditions', 'station', 'unified-map', 'alerts', 'learn', 'settings'];
|
||||
const TABS = ['conditions', 'station', 'unified-map', 'alerts'];
|
||||
let touchX = 0;
|
||||
let touchY = 0;
|
||||
let blocked = false;
|
||||
@@ -3085,16 +3094,13 @@ function applyStationOverlay(device) {
|
||||
peekEl.textContent = `· ${fS(data.windspeedmph)} ${speedUnit()} ${compassDir(data.winddir || 0)} · ${data.humidity ?? '--'}% humidity`;
|
||||
}
|
||||
|
||||
// Replace location name with station name
|
||||
const stationName = device.info?.name || device.macAddress || 'My Station';
|
||||
const locNameEl = document.getElementById('loc-name');
|
||||
const locSubEl = document.getElementById('loc-sub');
|
||||
if (locNameEl) locNameEl.textContent = stationName;
|
||||
if (locSubEl) locSubEl.textContent = 'Ambient Weather Station';
|
||||
|
||||
// Hide the hero badge (redundant — location header now identifies the station)
|
||||
// Keep the searched location's name; mark that temperatures now come from your own station
|
||||
const stationName = (state.stationName || device.info?.name || 'My Station');
|
||||
const badge = document.getElementById('station-badge');
|
||||
if (badge) hide(badge);
|
||||
const badgeName = document.getElementById('station-badge-name');
|
||||
if (badgeName) badgeName.textContent = `Your station · ${stationName}`;
|
||||
if (badge) show(badge);
|
||||
if (window.AppUI) window.AppUI.onStation(device, data);
|
||||
}
|
||||
|
||||
function renderAmbientStation(device) {
|
||||
|
||||
Reference in new issue
Block a user