/* WeatherMate — screens */ function uvDesc(uv) { if (uv == null) return "—"; if (uv < 3) return "Low"; if (uv < 6) return "Moderate"; if (uv < 8) return "High"; if (uv < 11) return "Very High"; return "Extreme"; } function dewPoint(t, rh) { const a = 17.27, b = 237.7; const g = (a * t) / (b + t) + Math.log(Math.max(rh, 1) / 100); return (b * g) / (a - g); } function pressureDesc(p) { if (p < 1000) return "Low"; if (p > 1022) return "High"; return "Normal"; } function visDesc(km) { if (km >= 20) return "Perfectly clear"; if (km >= 10) return "Clear"; if (km >= 4) return "Hazy"; if (km >= 1) return "Misty"; return "Very poor"; } function DetailsGrid({ data, unit, animate }) { const c = data.current, today = data.daily[0]; const visKm = WX.conv.vis(c.visibility, unit); const dp = dewPoint(c.temp, c.humidity); const feels = WX.conv.temp(c.feels, unit); const actual = WX.conv.temp(c.temp, unit); return (
{c.uv != null ? Math.round(c.uv) : "—"}
{uvDesc(c.uv)}
{WX.fmtClock(today.sunrise)}
Sunset: {WX.fmtClock(today.sunset)}
{WX.conv.wind(c.wind, unit)} {WX.conv.windUnit(unit)} winds
{WX.conv.precip(c.precip, unit).toFixed(1)} {WX.conv.precipUnit(unit)}
{WX.conv.precip(today.precip, unit).toFixed(1)} {WX.conv.precipUnit(unit)} expected today{today.pop != null ? ` · ${today.pop}% chance` : ""}
{feels}°
{feels === actual ? "Same as the actual temp" : feels > actual ? "Humidity makes it warmer" : "Wind makes it feel cooler"}
{Math.round(c.humidity)}%
Dew point {WX.conv.temp(dp, unit)}° right now
{visKm.toFixed(visKm < 10 ? 1 : 0)} {WX.conv.visUnit(unit)}
{visDesc(visKm)}
{Math.round(c.pressure)} hPa
{pressureDesc(c.pressure)} pressure
); } function summaryText(data, unit) { const c = data.current, d = data.daily; const label = WX.labelOf(c.code); const hi = WX.conv.temp(d[0].hi, unit), lo = WX.conv.temp(d[0].lo, unit); // find next rainy day let rainDay = null; for (let i = 1; i < d.length; i++) { const cond = WX.conditionOf(d[i].code); if (cond === "rain" || cond === "drizzle" || cond === "thunder") { rainDay = d[i]; break; } } let s = `${label} conditions will continue with a high of ${hi}° and a low of ${lo}°.`; if (rainDay) { const day = WX.fmtDay(rainDay.date, d.indexOf(rainDay)); s += ` Rain is expected ${day}.`; } else { s += " Dry conditions are expected all week."; } return s; } function ForecastCard({ data, unit, animate, forecastMode, setForecastMode, selectedHour, setSelectedHour }) { const tempVals = data.daily.map(d => [d.lo, d.hi]).flat(); const range = { min: Math.min(...tempVals), max: Math.max(...tempVals) }; return (
{forecastMode === "hourly" ? (
{data.hourly.map((h, i) => ( setSelectedHour(selectedHour === i ? null : i)} /> ))}
) : (
{data.daily.map((d, i) => ( ))}
)}
); } function HomeScreen({ data, unit, animate, theme, city, style, forecastMode, setForecastMode, ptr }) { const [selectedHour, setSelectedHour] = React.useState(null); const scrollRef = React.useRef(null); // hero reflects selected hour or current const sel = selectedHour != null ? data.hourly[selectedHour] : null; const heroCode = sel ? sel.code : data.current.code; const heroDay = sel ? sel.isDay : data.current.isDay; const heroTemp = sel ? sel.temp : data.current.temp; const heroCond = WX.labelOf(heroCode); const hi = WX.conv.temp(data.daily[0].hi, unit); const lo = WX.conv.temp(data.daily[0].lo, unit); const novel = style === "novel"; return (
6 ? 1 : 0, transform: `rotate(${ptr.height * 4}deg)` }} />
{!novel && (
)}
{city.name}
{WX.conv.temp(heroTemp, unit)}° {novel && (
)}
{sel ? `${heroCond} · ${WX.fmtHour(sel.time)}` : heroCond}
H:{hi}°L:{lo}°
Forecast
{summaryText(data, unit)}
); } window.HomeScreen = HomeScreen;