/* 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 (
setForecastMode("hourly")}>Hourly Forecast
setForecastMode("weekly")}>Weekly Forecast
{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;