use-weather-forecast 0.1.86 → 0.1.88

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # use-weather-forecast
2
2
 
3
+ [![Coverage](https://codecov.io/gh/OpenSourceAGI/qwksearch-research-agent/graph/badge.svg?component=package-react-weather-forecast)](https://codecov.io/gh/OpenSourceAGI/qwksearch-research-agent)
4
+
3
5
  React weather forecast component using Open-Meteo for current, hourly, and daily forecasts and Cloudflare/ipapi.co for IP geolocation.
4
6
 
5
7
  ## Features
package/dist/index.cjs CHANGED
@@ -320,13 +320,6 @@ var WIND_SPEED_UNIT_LABELS = {
320
320
  ms: "m/s",
321
321
  kn: "kn"
322
322
  };
323
- function closestHour(hourly, targetTime) {
324
- return hourly.reduce((closest, hour) => {
325
- const diff = Math.abs(new Date(hour.time).getTime() - targetTime.getTime());
326
- const closestDiff = closest ? Math.abs(new Date(closest.time).getTime() - targetTime.getTime()) : Infinity;
327
- return diff < closestDiff ? hour : closest;
328
- }, void 0);
329
- }
330
323
  function formatInZone(instant, timezone, options) {
331
324
  try {
332
325
  return new Intl.DateTimeFormat([], { ...options, timeZone: timezone || void 0 }).format(instant);
@@ -395,53 +388,62 @@ var styles = {
395
388
  hours: { display: "flex", gap: 12, overflowX: "auto", paddingBottom: 4 },
396
389
  hourCard: { minWidth: 72, textAlign: "center", padding: 8, borderRadius: 8, background: "#f9fafb" },
397
390
  dayRow: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 10, padding: "8px 0", borderBottom: "1px solid #f3f4f6" },
398
- // Card-sized: a fixed maximum width with generous, even padding, so the
399
- // widget reads as a self-contained tile wherever it is dropped in.
391
+ // Fluid: fills whatever container it is dropped into (e.g. the full
392
+ // sidebar/column width on desktop), with generous, even padding. Current
393
+ // conditions sit on the left and the upcoming days on the right; the two
394
+ // halves wrap into a stack when the container gets narrow.
400
395
  compactRoot: {
401
396
  fontFamily: "system-ui, sans-serif",
402
397
  display: "flex",
403
- flexDirection: "column",
404
- gap: 14,
405
- padding: 18,
398
+ flexDirection: "row",
399
+ flexWrap: "wrap",
400
+ alignItems: "stretch",
401
+ gap: 24,
402
+ padding: "20px 24px",
406
403
  borderRadius: 16,
407
404
  width: "100%",
408
- maxWidth: 340,
409
405
  boxSizing: "border-box"
410
406
  },
411
- // Current conditions stack down the card; the upcoming days sit in a row
412
- // along the bottom.
413
- compactBody: { display: "flex", flexDirection: "column", gap: 10 },
407
+ // Current conditions form two spread-out rows down the left side of the
408
+ // card (rather than five stacked lines), so the card stays short and each
409
+ // row uses the full available width.
410
+ compactBody: { display: "flex", flexDirection: "column", justifyContent: "space-between", gap: 20, flex: "1.6 1 240px", minWidth: 0 },
411
+ compactRow: { display: "flex", alignItems: "center", justifyContent: "space-between", flexWrap: "wrap", gap: "12px 16px" },
414
412
  compactCity: { fontSize: 15, fontWeight: 700, lineHeight: 1.2 },
415
- compactHeader: { display: "flex", alignItems: "center", flexWrap: "wrap", gap: 8 },
413
+ compactHeader: { display: "flex", alignItems: "center", flexWrap: "wrap", gap: 12 },
416
414
  compactTemp: { fontSize: 32, fontWeight: 600, lineHeight: 1 },
417
- // The short-term outlook ("59 deg in 6h") trails the current temperature.
418
- compactOutlook: { display: "flex", alignItems: "center", gap: 4, fontSize: 12, opacity: 0.75, whiteSpace: "nowrap" },
419
415
  compactTime: { fontSize: 26, fontWeight: 600, lineHeight: 1.1 },
420
- compactSeconds: { fontSize: 13, fontWeight: 500, opacity: 0.6 },
421
- compactDate: { fontSize: 11, opacity: 0.7, marginTop: 2 },
422
- compactStats: { display: "flex", flexWrap: "wrap", gap: 14, fontSize: 12, opacity: 0.9 },
423
- // Three equal columns along the bottom edge, each stacking weekday / icon /
424
- // high-low so the row stays readable at card width.
416
+ compactSeconds: { fontSize: 13, fontWeight: 500, opacity: 0.6, marginLeft: 2 },
417
+ compactDate: { fontSize: 11, opacity: 0.7, marginTop: 5 },
418
+ compactStats: { display: "flex", flexWrap: "wrap", gap: "8px 18px", fontSize: 12, opacity: 0.9 },
419
+ // Three equal columns on the right side of the card, each stacking
420
+ // weekday / icon / high-low, vertically centered against the current
421
+ // conditions and nudged over behind a divider so the two halves read as
422
+ // separate sections.
425
423
  upcoming: {
426
424
  display: "grid",
427
425
  gridTemplateColumns: "repeat(3, 1fr)",
428
- gap: 8,
429
- paddingTop: 12,
430
- borderTop: "1px solid currentColor",
431
- borderTopColor: "rgba(128,128,128,0.25)",
432
- fontSize: 12
426
+ alignContent: "center",
427
+ gap: 14,
428
+ flex: "1 1 190px",
429
+ minWidth: 0,
430
+ paddingLeft: 22,
431
+ borderLeft: "1px solid currentColor",
432
+ borderLeftColor: "rgba(128,128,128,0.25)",
433
+ fontSize: 11
433
434
  },
434
- upcomingDay: { display: "flex", flexDirection: "column", alignItems: "center", gap: 4, textAlign: "center" },
435
+ upcomingDay: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 5, textAlign: "center" },
435
436
  upcomingDayName: { opacity: 0.7, whiteSpace: "nowrap" },
436
437
  upcomingTemps: { whiteSpace: "nowrap" },
437
- compactStat: { display: "flex", alignItems: "center", gap: 4, whiteSpace: "nowrap" },
438
+ compactStat: { display: "flex", alignItems: "center", gap: 6, whiteSpace: "nowrap" },
438
439
  unitSwitch: { display: "inline-flex", alignItems: "center", cursor: "pointer", userSelect: "none" },
439
440
  rain: { color: "#2563eb", whiteSpace: "nowrap" },
440
441
  tz: { fontSize: 12, opacity: 0.7, marginTop: 2 },
441
442
  list: { display: "flex", flexDirection: "column" }
442
443
  };
444
+ var RAIN_RISK_THRESHOLD = 2;
443
445
  function RainBadge({ probability, compact }) {
444
- if (probability === void 0 || probability === null || probability <= 0) return null;
446
+ if (probability === void 0 || probability === null || probability <= RAIN_RISK_THRESHOLD) return null;
445
447
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: styles.rain, children: compact ? ` \xB7 ${probability}%` : ` \xB7 ${probability}% rain` });
446
448
  }
447
449
  function SingleWeatherForecast(props) {
@@ -476,7 +478,7 @@ function SingleWeatherForecast(props) {
476
478
  style: { ...styles.unitSwitch, fontSize },
477
479
  children: [
478
480
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { fontWeight: unit === "fahrenheit" ? 700 : 400, opacity: unit === "fahrenheit" ? 1 : 0.45 }, children: "\xB0F" }),
479
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.4, margin: "0 3px" }, children: "|" }),
481
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.4, margin: "0 4px" }, children: "|" }),
480
482
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { fontWeight: unit === "celsius" ? 700 : 400, opacity: unit === "celsius" ? 1 : 0.45 }, children: "\xB0C" })
481
483
  ]
482
484
  }
@@ -493,56 +495,52 @@ function SingleWeatherForecast(props) {
493
495
  const currentClock = clockParts(now, timezone);
494
496
  const currentZone = zoneLabel(now, timezone);
495
497
  if (props.compact) {
496
- const forecastBase = new Date(data.current.time);
497
498
  const today = data.daily[0];
498
- const in6Hours = closestHour(data.hourly, new Date(forecastBase.getTime() + 6 * 60 * 60 * 1e3));
499
499
  const windSpeedUnitLabel = WIND_SPEED_UNIT_LABELS[props.windSpeedUnit ?? "mph"];
500
500
  const upcomingDays = data.daily.slice(1, 4);
501
501
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: props.className, style: { ...styles.compactRoot, ...props.style }, children: [
502
502
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: styles.compactBody, children: [
503
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: styles.compactCity, children: [
504
- data.location?.city || "Current location",
505
- data.location?.region ? `, ${data.location.region}` : ""
506
- ] }),
507
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: styles.compactHeader, children: [
508
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(WeatherIcon, { condition: data.current.icon, width: 30, height: 30, title: "Current weather" }),
509
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { style: styles.compactTemp, children: data.current.temperature }),
510
- renderUnitSwitch(13),
511
- in6Hours && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: styles.compactOutlook, children: [
512
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(WeatherIcon, { condition: in6Hours.icon, width: 14, height: 14, title: "In 6 hours" }),
513
- in6Hours.temperature,
514
- "\xB0 6h",
515
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RainBadge, { probability: in6Hours.precipitationProbability, compact: true })
516
- ] })
517
- ] }),
518
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
519
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: styles.compactTime, children: [
520
- currentClock.main,
521
- currentClock.tail && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: styles.compactSeconds, children: currentClock.tail })
503
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: styles.compactRow, children: [
504
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: styles.compactCity, children: [
505
+ data.location?.city || "Current location",
506
+ data.location?.region ? `, ${data.location.region}` : ""
522
507
  ] }),
523
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: styles.compactDate, children: [
524
- currentDate,
525
- currentZone ? ` \xB7 ${currentZone}` : ""
508
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: styles.compactHeader, children: [
509
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(WeatherIcon, { condition: data.current.icon, width: 30, height: 30, title: "Current weather" }),
510
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { style: styles.compactTemp, children: data.current.temperature }),
511
+ renderUnitSwitch(13)
526
512
  ] })
527
513
  ] }),
528
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: styles.compactStats, children: [
529
- today && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: styles.compactStat, children: [
530
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(WeatherIcon, { condition: today.icon, width: 16, height: 16, title: "Today's high and low" }),
531
- today.max,
532
- "\xB0 / ",
533
- today.min,
534
- "\xB0",
535
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RainBadge, { probability: today.precipitationProbabilityMax, compact: true })
514
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: styles.compactRow, children: [
515
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
516
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: styles.compactTime, children: [
517
+ currentClock.main,
518
+ currentClock.tail && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: styles.compactSeconds, children: currentClock.tail })
519
+ ] }),
520
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: styles.compactDate, children: [
521
+ currentDate,
522
+ currentZone ? ` \xB7 ${currentZone}` : ""
523
+ ] })
536
524
  ] }),
537
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: styles.compactStat, children: [
538
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(WindIcon, { width: 16, height: 16, title: "Wind speed" }),
539
- data.current.windSpeed !== void 0 ? `${Math.round(data.current.windSpeed)} ${windSpeedUnitLabel}` : "\u2014"
525
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: styles.compactStats, children: [
526
+ today && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: styles.compactStat, children: [
527
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(WeatherIcon, { condition: today.icon, width: 16, height: 16, title: "Today's high and low" }),
528
+ today.max,
529
+ "\xB0 / ",
530
+ today.min,
531
+ "\xB0",
532
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RainBadge, { probability: today.precipitationProbabilityMax, compact: true })
533
+ ] }),
534
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: styles.compactStat, children: [
535
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(WindIcon, { width: 16, height: 16, title: "Wind speed" }),
536
+ data.current.windSpeed !== void 0 ? `${Math.round(data.current.windSpeed)} ${windSpeedUnitLabel}` : "\u2014"
537
+ ] })
540
538
  ] })
541
539
  ] })
542
540
  ] }),
543
541
  upcomingDays.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: styles.upcoming, children: upcomingDays.map((day) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: styles.upcomingDay, title: upcomingDayDetail(day, windSpeedUnitLabel), children: [
544
542
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: styles.upcomingDayName, children: upcomingDayLabel(day.date) }),
545
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(WeatherIcon, { condition: day.icon, width: 18, height: 18 }),
543
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(WeatherIcon, { condition: day.icon, width: 14, height: 14 }),
546
544
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: styles.upcomingTemps, children: [
547
545
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("strong", { children: [
548
546
  day.max,
@@ -554,7 +552,7 @@ function SingleWeatherForecast(props) {
554
552
  "\xB0"
555
553
  ] })
556
554
  ] }),
557
- day.precipitationProbabilityMax !== void 0 && day.precipitationProbabilityMax > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: { ...styles.rain, fontSize: 11 }, children: [
555
+ day.precipitationProbabilityMax !== void 0 && day.precipitationProbabilityMax > RAIN_RISK_THRESHOLD && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: { ...styles.rain, fontSize: 10 }, children: [
558
556
  day.precipitationProbabilityMax,
559
557
  "%"
560
558
  ] })
@@ -604,7 +602,7 @@ function SingleWeatherForecast(props) {
604
602
  "\xB0 / ",
605
603
  day.max,
606
604
  "\xB0",
607
- day.precipitationProbabilityMax !== void 0 && day.precipitationProbabilityMax > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: styles.rain, children: [
605
+ day.precipitationProbabilityMax !== void 0 && day.precipitationProbabilityMax > RAIN_RISK_THRESHOLD && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: styles.rain, children: [
608
606
  " \xB7 ",
609
607
  day.precipitationProbabilityMax,
610
608
  "%"
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/weatherCodes.ts","../src/lib/cache.ts","../src/api/geolocation.ts","../src/api/forecast.ts","../src/hooks/useWeatherForecast.ts","../src/icons/BaseIcon.tsx","../src/icons/weather-icons.tsx","../src/icons/WeatherIcon.tsx","../src/components/WeatherForecast.tsx"],"sourcesContent":["export * from './types';\nexport * from './weatherCodes';\nexport { clearWeatherForecastCache } from './lib/cache';\nexport * from './api/geolocation';\nexport * from './api/forecast';\nexport * from './hooks/useWeatherForecast';\nexport * from './icons/WeatherIcon';\nexport * from './components/WeatherForecast';\n","import type { WeatherCondition } from './types';\n\nexport function getWeatherIcon(code: number, isDay = 1): WeatherCondition {\n if (code === 0) return 'sun';\n if ([1, 2].includes(code)) return isDay ? 'cloud-sun' : 'clouds';\n if (code === 3) return 'clouds';\n if ([45, 48].includes(code)) return 'cloud-fog';\n if ([51, 53, 55, 56, 57].includes(code)) return 'cloud-drizzle';\n if ([61, 63].includes(code)) return 'cloud-showers';\n if ([65, 66, 67, 80, 81, 82].includes(code)) return 'cloud-showers-heavy';\n if ([71, 73, 75, 77, 85, 86].includes(code)) return 'cloud-snow';\n if (code === 95) return 'cloud-bolt';\n if ([96, 99].includes(code)) return 'cloud-hail';\n return 'clouds';\n}\n","const CACHE_PREFIX = 'weather-forecast-cache:';\nconst CACHE_TTL_MS = 30 * 60 * 1000;\n\ntype CacheEntry<T> = {\n timestamp: number;\n data: T;\n};\n\nfunction getStorage(): Storage | null {\n if (typeof window === 'undefined' || !window.localStorage) return null;\n return window.localStorage;\n}\n\n/** Returns the cached value for `key` if it was written within the last 30 minutes. */\nexport function readCachedForecast<T>(key: string): T | null {\n const storage = getStorage();\n if (!storage) return null;\n\n try {\n const raw = storage.getItem(CACHE_PREFIX + key);\n if (!raw) return null;\n\n const entry = JSON.parse(raw) as CacheEntry<T>;\n if (Date.now() - entry.timestamp > CACHE_TTL_MS) {\n storage.removeItem(CACHE_PREFIX + key);\n return null;\n }\n\n return entry.data;\n } catch {\n return null;\n }\n}\n\nexport function writeCachedForecast<T>(key: string, data: T): void {\n const storage = getStorage();\n if (!storage) return;\n\n try {\n const entry: CacheEntry<T> = { timestamp: Date.now(), data };\n storage.setItem(CACHE_PREFIX + key, JSON.stringify(entry));\n } catch {\n // Storage full or unavailable (e.g. private browsing) — safe to ignore,\n // the next call will simply hit the network again.\n }\n}\n\n/** Clears all cached forecasts. Mainly useful for tests/debugging. */\nexport function clearWeatherForecastCache(): void {\n const storage = getStorage();\n if (!storage) return;\n\n for (let i = storage.length - 1; i >= 0; i--) {\n const key = storage.key(i);\n if (key?.startsWith(CACHE_PREFIX)) storage.removeItem(key);\n }\n}\n","import type { WeatherLocation } from '../types';\n\nconst DEFAULT_IP_API_URL = 'https://ipapi.co';\n\ntype IpApiResponse = {\n city?: string;\n region?: string;\n country_name?: string;\n timezone?: string;\n latitude?: number;\n longitude?: number;\n error?: boolean;\n reason?: string;\n};\n\ntype GeoWorkerResponse = {\n city?: string;\n region?: string;\n country?: string;\n timezone?: string;\n latitude?: number | string;\n longitude?: number | string;\n};\n\nexport async function getClientLocation(geoEndpoint?: string, ip?: string): Promise<WeatherLocation> {\n if (geoEndpoint) {\n const url = ip ? `${geoEndpoint}?ip=${encodeURIComponent(ip)}` : geoEndpoint;\n const res = await fetch(url);\n if (!res.ok) throw new Error(`Geolocation worker lookup failed: ${res.status}`);\n const data: GeoWorkerResponse = await res.json();\n\n return {\n city: data.city,\n region: data.region,\n country: data.country,\n timezone: data.timezone,\n latitude: Number(data.latitude),\n longitude: Number(data.longitude),\n };\n }\n\n const res = await fetch(`${DEFAULT_IP_API_URL}/${ip ? `${encodeURIComponent(ip)}/` : ''}json/`);\n if (!res.ok) throw new Error(`ipapi.co lookup failed: ${res.status}`);\n const data: IpApiResponse = await res.json();\n\n if (data.error) {\n throw new Error(`ipapi.co lookup failed: ${data.reason ?? 'unknown error'}`);\n }\n\n return {\n city: data.city,\n region: data.region,\n country: data.country_name,\n timezone: data.timezone,\n latitude: Number(data.latitude),\n longitude: Number(data.longitude),\n };\n}\n","import type { WeatherForecastData, WeatherForecastOptions, WeatherLocation } from '../types';\nimport { getWeatherIcon } from '../weatherCodes';\nimport { getClientLocation } from './geolocation';\nimport { readCachedForecast, writeCachedForecast } from '../lib/cache';\n\nfunction buildUrl(latitude: number, longitude: number, options: WeatherForecastOptions) {\n const params = new URLSearchParams({\n latitude: String(latitude),\n longitude: String(longitude),\n timezone: options.location?.timezone || 'auto',\n temperature_unit: options.temperatureUnit || 'fahrenheit',\n wind_speed_unit: options.windSpeedUnit || 'mph',\n forecast_days: String(options.forecastDays ?? 5),\n forecast_hours: String(options.forecastHours ?? 24),\n current: [\n 'temperature_2m',\n 'weather_code',\n 'is_day',\n 'rain',\n 'showers',\n 'snowfall',\n 'wind_speed_10m',\n ].join(','),\n hourly: [\n 'temperature_2m',\n 'weather_code',\n 'precipitation_probability',\n 'rain',\n 'showers',\n 'snowfall',\n ].join(','),\n daily: [\n 'temperature_2m_max',\n 'temperature_2m_min',\n 'weather_code',\n 'precipitation_probability_max',\n 'precipitation_sum',\n 'wind_speed_10m_max',\n ].join(','),\n });\n\n return `https://api.open-meteo.com/v1/forecast?${params.toString()}`;\n}\n\nexport async function getWeatherForecast(\n options: WeatherForecastOptions = {}\n): Promise<WeatherForecastData> {\n let location: WeatherLocation;\n\n if (typeof options.latitude === 'number' && typeof options.longitude === 'number') {\n location = {\n ...options.location,\n latitude: options.latitude,\n longitude: options.longitude,\n };\n } else {\n location = await getClientLocation(options.geoEndpoint, options.ip);\n }\n\n const url = buildUrl(location.latitude, location.longitude, { ...options, location });\n\n const cached = readCachedForecast<WeatherForecastData>(url);\n if (cached) return cached;\n\n const response = await fetch(url, { headers: { 'Content-Type': 'application/json' } });\n\n if (!response.ok) {\n throw new Error(`Weather request failed: ${response.status} ${response.statusText}`);\n }\n\n const data = await response.json();\n\n if (!data?.current || !data?.hourly || !data?.daily) {\n throw new Error('Invalid weather response');\n }\n\n const result: WeatherForecastData = {\n location: { ...location, timezone: data.timezone || location.timezone },\n current: {\n time: data.current.time,\n temperature: Math.round(data.current.temperature_2m),\n weatherCode: data.current.weather_code,\n icon: getWeatherIcon(data.current.weather_code, data.current.is_day),\n isDay: data.current.is_day,\n rain: data.current.rain,\n showers: data.current.showers,\n snowfall: data.current.snowfall,\n windSpeed: data.current.wind_speed_10m,\n },\n hourly: data.hourly.time.map((time: string, index: number) => ({\n time,\n temperature: Math.round(data.hourly.temperature_2m[index]),\n weatherCode: data.hourly.weather_code[index],\n precipitationProbability: data.hourly.precipitation_probability?.[index],\n rain: data.hourly.rain?.[index],\n showers: data.hourly.showers?.[index],\n snowfall: data.hourly.snowfall?.[index],\n icon: getWeatherIcon(data.hourly.weather_code[index]),\n })),\n daily: data.daily.time.map((date: string, index: number) => ({\n date,\n min: Math.round(data.daily.temperature_2m_min[index]),\n max: Math.round(data.daily.temperature_2m_max[index]),\n weatherCode: data.daily.weather_code[index],\n precipitationProbabilityMax: data.daily.precipitation_probability_max?.[index],\n precipitationSum: data.daily.precipitation_sum?.[index],\n windSpeedMax: data.daily.wind_speed_10m_max?.[index],\n icon: getWeatherIcon(data.daily.weather_code[index]),\n })),\n };\n\n writeCachedForecast(url, result);\n return result;\n}\n","import { useEffect, useState } from 'react';\nimport type { WeatherForecastData, WeatherForecastOptions } from '../types';\nimport { getWeatherForecast } from '../api/forecast';\n\nexport function useWeatherForecast(options: WeatherForecastOptions = {}) {\n const [data, setData] = useState<WeatherForecastData | null>(null);\n const [error, setError] = useState<Error | null>(null);\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n let active = true;\n setLoading(true);\n setError(null);\n\n getWeatherForecast(options)\n .then((result) => { if (active) setData(result); })\n .catch((err) => { if (active) setError(err instanceof Error ? err : new Error('Unknown error')); })\n .finally(() => { if (active) setLoading(false); });\n\n return () => { active = false; };\n }, [\n options.latitude,\n options.longitude,\n options.geoEndpoint,\n options.ip,\n options.forecastDays,\n options.forecastHours,\n options.temperatureUnit,\n options.windSpeedUnit,\n options.location?.timezone,\n ]);\n\n return { data, error, loading };\n}\n","import React from 'react';\nimport type { IconProps } from '../types';\n\nexport function BaseIcon({ title, children, ...props }: IconProps & { children: React.ReactNode }) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n role={title ? 'img' : 'presentation'}\n aria-hidden={title ? undefined : true}\n aria-label={title}\n {...props}\n >\n {title ? <title>{title}</title> : null}\n {children}\n </svg>\n );\n}\n","import React from 'react';\nimport type { IconProps } from '../types';\nimport { BaseIcon } from './BaseIcon';\n\nexport const CloudBoltIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M7 18a4.815 4.815 0 0 1-3.327-1.318A4.403 4.403 0 0 1 2.295 13.5c0-1.194.496-2.338 1.378-3.182A4.815 4.815 0 0 1 7 9c.295-1.313 1.157-2.466 2.397-3.207a5.971 5.971 0 0 1 2.025-.749 6.224 6.224 0 0 1 2.19.006c.721.131 1.408.39 2.02.76a5.36 5.36 0 0 1 1.543 1.397c.407.552.69 1.172.831 1.823.142.65.14 1.32-.006 1.97h1a3.5 3.5 0 0 1 0 7h-1m-4.75-5L11 17.5h3.375l-2.25 4.5\" /></BaseIcon>\n);\nexport const CloudDrizzleIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M12 18v1m-3-4v1m6-1v1m2 3v1M7 19v1m-3-3.034a4.63 4.63 0 0 1-.327-.284A4.403 4.403 0 0 1 2.295 13.5c0-1.194.496-2.338 1.378-3.182A4.815 4.815 0 0 1 7 9c.295-1.313 1.157-2.466 2.397-3.207a5.971 5.971 0 0 1 2.025-.749 6.224 6.224 0 0 1 2.19.006c.721.131 1.408.39 2.02.76a5.36 5.36 0 0 1 1.543 1.397c.407.552.69 1.172.831 1.823.142.65.14 1.32-.006 1.97h1a3.5 3.5 0 0 1 1.5 6.662\" /></BaseIcon>\n);\nexport const CloudFogIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M4 16.966a4.63 4.63 0 0 1-.327-.284A4.403 4.403 0 0 1 2.295 13.5c0-1.194.496-2.338 1.378-3.182A4.815 4.815 0 0 1 7 9c.295-1.313 1.157-2.466 2.397-3.207a5.971 5.971 0 0 1 2.025-.749 6.224 6.224 0 0 1 2.19.006c.721.131 1.408.39 2.02.76a5.36 5.36 0 0 1 1.543 1.397c.407.552.69 1.172.831 1.823.142.65.14 1.32-.006 1.97h1a3.5 3.5 0 0 1 1.5 6.662M5 21h7m4 0h3M8 17h8\" /></BaseIcon>\n);\nexport const CloudHailIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M8 17.45v.1m-1.5 3.9v.1m10.5-4.1v.1m-1.5 3.9v.1m-3-4.1v.1m-1.5 3.9v.1m10.475-4.575A3.5 3.5 0 0 0 19 11h-1c.146-.65.148-1.32.006-1.97a4.784 4.784 0 0 0-.831-1.823 5.36 5.36 0 0 0-1.544-1.398 5.964 5.964 0 0 0-2.02-.759 6.224 6.224 0 0 0-2.19-.006 5.971 5.971 0 0 0-2.024.749C8.157 6.533 7.295 7.687 7 9a4.815 4.815 0 0 0-3.327 1.318A4.403 4.403 0 0 0 2.295 13.5c0 1.194.496 2.338 1.378 3.182.105.1.214.195.327.284m4.5.534a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Zm-1.5 4a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Zm10.5-4a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Zm-1.5 4a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Zm-3-4a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Zm-1.5 4a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Z\" /></BaseIcon>\n);\nexport const CloudShowersIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M4 16.966a4.63 4.63 0 0 1-.327-.284A4.403 4.403 0 0 1 2.295 13.5c0-1.194.496-2.338 1.378-3.182A4.815 4.815 0 0 1 7 9c.295-1.313 1.157-2.466 2.397-3.207a5.971 5.971 0 0 1 2.025-.749 6.224 6.224 0 0 1 2.19.006c.721.131 1.408.39 2.02.76a5.36 5.36 0 0 1 1.543 1.397c.407.552.69 1.172.831 1.823.142.65.14 1.32-.006 1.97h1a3.5 3.5 0 0 1 1.5 6.662M12 13v2m0 3v2m4-5v2m0 3v2m-8-7v2m0 3v2\" /></BaseIcon>\n);\nexport const CloudShowersHeavyIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M4 16.966a4.63 4.63 0 0 1-.327-.284A4.403 4.403 0 0 1 2.295 13.5c0-1.194.496-2.338 1.378-3.182A4.815 4.815 0 0 1 7 9c.295-1.313 1.157-2.466 2.397-3.207a5.971 5.971 0 0 1 2.025-.749 6.224 6.224 0 0 1 2.19.006c.721.131 1.408.39 2.02.76a5.36 5.36 0 0 1 1.543 1.397c.407.552.69 1.172.831 1.823.142.65.14 1.32-.006 1.97h1a3.5 3.5 0 0 1 1.5 6.662M16 15v6m-8-6v6m4-8v6\" /></BaseIcon>\n);\nexport const CloudSleetIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M13.5 17.45a1 1 0 1 0-2 0h2Zm-2 .1a1 1 0 1 0 2 0h-2Zm.5 3.9a1 1 0 1 0-2 0h2Zm-2 .1a1 1 0 1 0 2 0h-2Zm-6.327-4.868.692-.723-.692.723ZM7 9v1a1 1 0 0 0 .976-.781L7 9Zm2.397-3.207.512.858-.512-.858ZM18 11l-.976-.219A1 1 0 0 0 18 12v-1Zm2.768 5.268a1 1 0 0 0 1.414 1.414l-.707-.707-.707-.707ZM3.379 17.75a1 1 0 0 0 1.242-1.567L4 16.966l-.62.784Zm5.55-.379a1 1 0 0 0-1.857-.742L8 17l.928.371ZM5.071 21.63a1 1 0 0 0 1.856.742L6 22l-.928-.371Zm13.857-4.258a1 1 0 0 0-1.857-.742L18 17l.928.371Zm-3.857 4.258a1 1 0 0 0 1.857.742L16 22l-.928-.371ZM13 17.5h-1a.5.5 0 0 1 .5-.5v2a1.5 1.5 0 0 0 1.5-1.5h-1Zm-.5.5v-1a.5.5 0 0 1 .5.5h-2a1.5 1.5 0 0 0 1.5 1.5v-1Zm-.5-.5h1a.5.5 0 0 1-.5.5v-2a1.5 1.5 0 0 0-1.5 1.5h1Zm.5-.5v1a.5.5 0 0 1-.5-.5h2a1.5 1.5 0 0 0-1.5-1.5v1Zm0 .45h-1v.1h2v-.1h-1Zm-1 4.05h-1a.5.5 0 0 1 .5-.5v2a1.5 1.5 0 0 0 1.5-1.5h-1Zm-.5.5v-1a.5.5 0 0 1 .5.5h-2A1.5 1.5 0 0 0 11 23v-1Zm-.5-.5h1a.5.5 0 0 1-.5.5v-2a1.5 1.5 0 0 0-1.5 1.5h1Zm.5-.5v1a.5.5 0 0 1-.5-.5h2A1.5 1.5 0 0 0 11 20v1Zm0 .45h-1v.1h2v-.1h-1Zm-7.327-4.768.692-.723a3.403 3.403 0 0 1-1.07-2.459h-2c0 1.473.613 2.877 1.687 3.905l.691-.723ZM2.295 13.5h1c0-.914.38-1.8 1.07-2.46l-.692-.722-.69-.723A5.403 5.403 0 0 0 1.294 13.5h1Zm1.378-3.182.692.723A3.815 3.815 0 0 1 7 10V8c-1.499 0-2.945.57-4.018 1.595l.691.723ZM7 9l.976.219c.23-1.024.91-1.956 1.933-2.568l-.512-.858-.513-.859c-1.456.87-2.5 2.245-2.86 3.847L7 9Zm2.397-3.207.512.858a4.972 4.972 0 0 1 1.686-.622l-.173-.985-.174-.985a6.971 6.971 0 0 0-2.364.875l.513.859Zm2.025-.749.173.985a5.223 5.223 0 0 1 1.837.005l.18-.984.178-.984a7.223 7.223 0 0 0-2.542-.007l.174.985Zm2.19.006-.18.984c.606.11 1.177.326 1.681.631l.518-.856.518-.855a6.963 6.963 0 0 0-2.359-.888l-.179.984Zm2.02.76-.519.855c.505.305.93.693 1.257 1.136l.805-.594.805-.594a6.361 6.361 0 0 0-1.831-1.659l-.518.855Zm1.543 1.397-.805.594c.326.442.549.932.66 1.441l.976-.212.978-.213a5.786 5.786 0 0 0-1.005-2.204l-.804.594Zm.831 1.823-.977.212c.111.509.11 1.031-.005 1.539L18 11l.976.219c.178-.792.18-1.609.008-2.402l-.978.213ZM18 11v1h1v-2h-1v1Zm1 0v1a2.5 2.5 0 0 1 1.768.732l.707-.707.707-.707A4.5 4.5 0 0 0 19 10v1Zm2.475 1.025-.707.707A2.5 2.5 0 0 1 21.5 14.5h2a4.5 4.5 0 0 0-1.318-3.182l-.707.707ZM22.5 14.5h-1a2.5 2.5 0 0 1-.732 1.768l.707.707.707.707A4.5 4.5 0 0 0 23.5 14.5h-1ZM4 16.966l.62-.784a3.684 3.684 0 0 1-.255-.223l-.692.723-.69.723c.126.121.259.237.396.345L4 16.966ZM8 17l-.928-.371-2 5L6 22l.928.371 2-5L8 17Zm10 0-.928-.371-2 5L16 22l.928.371 2-5L18 17Z\" /></BaseIcon>\n);\nexport const CloudSnowIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M8 15h2m-1-1v2m5-1h2m-1-1v2M5 21h2m-1-1v2m5-2h2m-1-1v2m5 0h2m-1-1v2M4 16.966a4.63 4.63 0 0 1-.327-.284A4.403 4.403 0 0 1 2.295 13.5c0-1.194.496-2.338 1.378-3.182A4.815 4.815 0 0 1 7 9c.295-1.313 1.157-2.466 2.397-3.207a5.971 5.971 0 0 1 2.025-.749 6.224 6.224 0 0 1 2.19.006c.721.131 1.408.39 2.02.76a5.36 5.36 0 0 1 1.543 1.397c.407.552.69 1.172.831 1.823.142.65.14 1.32-.006 1.97h1a3.5 3.5 0 0 1 1.5 6.662\" /></BaseIcon>\n);\nexport const CloudSunIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M10.201 19.997c-1.977 0-3.58-1.544-3.58-3.448 0-1.903 1.603-3.446 3.58-3.446.303-1.355 1.38-2.461 2.827-2.902 1.445-.44 3.042-.148 4.186.77 1.144.915 1.663 2.312 1.361 3.667h.761c1.472 0 2.664 1.2 2.664 2.68A2.672 2.672 0 0 1 19.336 20h-9.134m2.66-12.934a4.166 4.166 0 0 0-2.677-1.732m2.677 1.732 2.744-1.892m-2.744 1.892c.55.796.802 1.754.723 2.709m-3.4-4.44a4.167 4.167 0 0 0-3.118.667m3.118-.668.602-3.278m-3.72 3.946A4.167 4.167 0 0 0 5.334 8.68m1.733-2.678L5.174 3.26m.16 5.42c-.2 1.088.041 2.21.669 3.12m-.669-3.12-3.278-.601m3.947 3.72-2.744 1.893m2.744-1.893a4.167 4.167 0 0 0 2.174 1.607\" /></BaseIcon>\n);\nexport const CloudsIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M8.958 21.05C6.772 21.05 5 19.344 5 17.24s1.772-3.81 3.958-3.81c.335-1.498 1.525-2.72 3.124-3.207 1.598-.487 3.363-.164 4.628.85 1.264 1.011 1.837 2.556 1.504 4.053h.842A2.953 2.953 0 0 1 22 18.09a2.954 2.954 0 0 1-2.945 2.964H8.958M18 8c-.5-2.5-3.532-3.401-5.5-2.5a3.117 3.117 0 0 0-2.11-2.312c-1.352-.411-2.845-.138-3.915.72-1.07.855-1.554 2.161-1.273 3.428h-.711A2.498 2.498 0 0 0 2 9.843c0 1.094.695 2.023 1.666 2.366\" /></BaseIcon>\n);\nexport const SnowflakeIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"m9.25 4-1 2H6m-2.25 4L5 12m0 0-1.25 2M5 12h3m-3 0H2.5m17.75-2L19 12l1.25 2M6 18h2.25l1 2M14 9h-4m4 0 3-5m-3 5 2 3m-6-3L7 4m3 5-2 3m0 0 2 3m0 0-3 5m3-5h4m2-3-2 3m2-3h5.5M14 15l3 5M14.75 4l1 2H18m0 12h-2.25l-1 2\" /></BaseIcon>\n);\nexport const SunIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M12 4V2m0 20v-2m10-8h-2M4 12H2m15.657-5.657 1.414-1.414M4.93 19.07l1.414-1.414m12.728 1.414-1.414-1.414M6.343 6.343 4.93 4.929M7 12a5 5 0 1 0 10 0 5 5 0 0 0-10 0Z\" /></BaseIcon>\n);\nexport const WindIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M9.59 4.59A2 2 0 1 1 11 8H2m10.59 11.41A2 2 0 1 0 14 16H2m15.73-8.27A2.5 2.5 0 1 1 19.5 12H2\" /></BaseIcon>\n);\n","import React from 'react';\nimport type { IconProps, WeatherCondition } from '../types';\nimport {\n CloudBoltIcon,\n CloudDrizzleIcon,\n CloudFogIcon,\n CloudHailIcon,\n CloudShowersHeavyIcon,\n CloudShowersIcon,\n CloudSleetIcon,\n CloudSnowIcon,\n CloudSunIcon,\n CloudsIcon,\n SnowflakeIcon,\n SunIcon,\n} from './weather-icons';\n\nconst iconMap: Record<WeatherCondition, React.ComponentType<IconProps>> = {\n sun: SunIcon,\n 'cloud-sun': CloudSunIcon,\n clouds: CloudsIcon,\n 'cloud-fog': CloudFogIcon,\n 'cloud-drizzle': CloudDrizzleIcon,\n 'cloud-showers': CloudShowersIcon,\n 'cloud-showers-heavy': CloudShowersHeavyIcon,\n 'cloud-sleet': CloudSleetIcon,\n 'cloud-snow': CloudSnowIcon,\n snowflake: SnowflakeIcon,\n 'cloud-bolt': CloudBoltIcon,\n 'cloud-hail': CloudHailIcon,\n};\n\nexport function WeatherIcon({ condition, ...props }: IconProps & { condition: WeatherCondition }) {\n const Icon = iconMap[condition] || CloudsIcon;\n return <Icon {...props} />;\n}\n","import React, { useEffect, useState } from 'react';\nimport { useWeatherForecast } from '../hooks/useWeatherForecast';\nimport type { DailyWeather, HourlyWeather, TemperatureUnit, WeatherForecastOptions, WindSpeedUnit } from '../types';\nimport { WeatherIcon } from '../icons/WeatherIcon';\nimport { WindIcon } from '../icons/weather-icons';\n\ntype Props = WeatherForecastOptions & {\n className?: string;\n style?: React.CSSProperties;\n compact?: boolean;\n};\n\nconst WIND_SPEED_UNIT_LABELS: Record<WindSpeedUnit, string> = {\n kmh: 'km/h',\n mph: 'mph',\n ms: 'm/s',\n kn: 'kn',\n};\n\nfunction closestHour(hourly: HourlyWeather[], targetTime: Date): HourlyWeather | undefined {\n return hourly.reduce<HourlyWeather | undefined>((closest, hour) => {\n const diff = Math.abs(new Date(hour.time).getTime() - targetTime.getTime());\n const closestDiff = closest ? Math.abs(new Date(closest.time).getTime() - targetTime.getTime()) : Infinity;\n return diff < closestDiff ? hour : closest;\n }, undefined);\n}\n\n// Format a real instant in a specific IANA timezone, falling back to the\n// browser's local zone if the timezone string is missing or invalid.\nfunction formatInZone(instant: Date, timezone: string | undefined, options: Intl.DateTimeFormatOptions): string {\n try {\n return new Intl.DateTimeFormat([], { ...options, timeZone: timezone || undefined }).format(instant);\n } catch {\n return new Intl.DateTimeFormat([], options).format(instant);\n }\n}\n\n// The clock is shown with the hour and minute large and the seconds (plus any\n// AM/PM marker) trailing in a smaller, dimmer size, so split it into those two\n// halves. Falls back to one undivided string if the locale's parts are not\n// where we expect them.\nfunction clockParts(instant: Date, timezone: string | undefined): { main: string; tail: string } {\n const options: Intl.DateTimeFormatOptions = { hour: 'numeric', minute: '2-digit', second: '2-digit' };\n const format = (() => {\n try {\n return new Intl.DateTimeFormat([], { ...options, timeZone: timezone || undefined });\n } catch {\n return new Intl.DateTimeFormat([], options);\n }\n })();\n const parts = format.formatToParts(instant);\n const part = (type: Intl.DateTimeFormatPartTypes) => parts.find((p) => p.type === type)?.value;\n const hour = part('hour');\n const minute = part('minute');\n const second = part('second');\n if (!hour || !minute || !second) return { main: format.format(instant), tail: '' };\n const dayPeriod = part('dayPeriod');\n return { main: `${hour}:${minute}`, tail: `:${second}${dayPeriod ? ` ${dayPeriod}` : ''}` };\n}\n\n// Just the zone abbreviation (e.g. \"GMT+2\"), so it can be shown apart from the\n// clock itself.\nfunction zoneLabel(instant: Date, timezone: string | undefined): string {\n const options: Intl.DateTimeFormatOptions = { hour: 'numeric', timeZoneName: 'short' };\n try {\n const format = (() => {\n try {\n return new Intl.DateTimeFormat([], { ...options, timeZone: timezone || undefined });\n } catch {\n return new Intl.DateTimeFormat([], options);\n }\n })();\n return format.formatToParts(instant).find((part) => part.type === 'timeZoneName')?.value ?? '';\n } catch {\n return '';\n }\n}\n\n// Daily dates arrive as plain \"YYYY-MM-DD\" calendar days; `new Date(...)` would\n// read those as UTC midnight and roll back a day in western timezones, so build\n// the date from its parts and let it stay a local calendar day.\nfunction parseCalendarDate(date: string): Date {\n const [year, month, day] = date.split('-').map(Number);\n if (!year || !month || !day) return new Date(date);\n return new Date(year, month - 1, day);\n}\n\n// The upcoming days sit in narrow columns along the bottom of the card, so\n// they are labelled with the abbreviated weekday (\"Fri\") rather than a full\n// name or \"Tomorrow\".\nfunction upcomingDayLabel(date: string): string {\n return parseCalendarDate(date).toLocaleDateString([], { weekday: 'short' });\n}\n\n// Daily totals that no longer have a column of their own in the card layout are\n// kept as hover text so the detail is still reachable.\nfunction upcomingDayDetail(day: DailyWeather, windSpeedUnitLabel: string): string | undefined {\n const details: string[] = [];\n if (day.precipitationSum !== undefined) details.push(`${day.precipitationSum.toFixed(1)} mm`);\n if (day.windSpeedMax !== undefined) details.push(`${day.windSpeedMax.toFixed(1)} ${windSpeedUnitLabel} wind`);\n return details.length > 0 ? details.join(' · ') : undefined;\n}\n\nconst styles = {\n root: {\n fontFamily: 'system-ui, sans-serif',\n border: '1px solid #e5e7eb',\n borderRadius: 12,\n padding: 16,\n background: '#fff',\n color: '#111827',\n maxWidth: 640,\n } as React.CSSProperties,\n row: { display: 'flex', alignItems: 'center', gap: 8 } as React.CSSProperties,\n hours: { display: 'flex', gap: 12, overflowX: 'auto', paddingBottom: 4 } as React.CSSProperties,\n hourCard: { minWidth: 72, textAlign: 'center' as const, padding: 8, borderRadius: 8, background: '#f9fafb' },\n dayRow: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, padding: '8px 0', borderBottom: '1px solid #f3f4f6' } as React.CSSProperties,\n // Card-sized: a fixed maximum width with generous, even padding, so the\n // widget reads as a self-contained tile wherever it is dropped in.\n compactRoot: {\n fontFamily: 'system-ui, sans-serif',\n display: 'flex',\n flexDirection: 'column',\n gap: 14,\n padding: 18,\n borderRadius: 16,\n width: '100%',\n maxWidth: 340,\n boxSizing: 'border-box',\n } as React.CSSProperties,\n // Current conditions stack down the card; the upcoming days sit in a row\n // along the bottom.\n compactBody: { display: 'flex', flexDirection: 'column', gap: 10 } as React.CSSProperties,\n compactCity: { fontSize: 15, fontWeight: 700, lineHeight: 1.2 } as React.CSSProperties,\n compactHeader: { display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 8 } as React.CSSProperties,\n compactTemp: { fontSize: 32, fontWeight: 600, lineHeight: 1 } as React.CSSProperties,\n // The short-term outlook (\"59 deg in 6h\") trails the current temperature.\n compactOutlook: { display: 'flex', alignItems: 'center', gap: 4, fontSize: 12, opacity: 0.75, whiteSpace: 'nowrap' } as React.CSSProperties,\n compactTime: { fontSize: 26, fontWeight: 600, lineHeight: 1.1 } as React.CSSProperties,\n compactSeconds: { fontSize: 13, fontWeight: 500, opacity: 0.6 } as React.CSSProperties,\n compactDate: { fontSize: 11, opacity: 0.7, marginTop: 2 } as React.CSSProperties,\n compactStats: { display: 'flex', flexWrap: 'wrap', gap: 14, fontSize: 12, opacity: 0.9 } as React.CSSProperties,\n // Three equal columns along the bottom edge, each stacking weekday / icon /\n // high-low so the row stays readable at card width.\n upcoming: {\n display: 'grid',\n gridTemplateColumns: 'repeat(3, 1fr)',\n gap: 8,\n paddingTop: 12,\n borderTop: '1px solid currentColor',\n borderTopColor: 'rgba(128,128,128,0.25)',\n fontSize: 12,\n } as React.CSSProperties,\n upcomingDay: { display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4, textAlign: 'center' } as React.CSSProperties,\n upcomingDayName: { opacity: 0.7, whiteSpace: 'nowrap' } as React.CSSProperties,\n upcomingTemps: { whiteSpace: 'nowrap' } as React.CSSProperties,\n compactStat: { display: 'flex', alignItems: 'center', gap: 4, whiteSpace: 'nowrap' } as React.CSSProperties,\n unitSwitch: { display: 'inline-flex', alignItems: 'center', cursor: 'pointer', userSelect: 'none' } as React.CSSProperties,\n rain: { color: '#2563eb', whiteSpace: 'nowrap' } as React.CSSProperties,\n tz: { fontSize: 12, opacity: 0.7, marginTop: 2 } as React.CSSProperties,\n list: { display: 'flex', flexDirection: 'column' } as React.CSSProperties,\n};\n\n// Precipitation chance is only surfaced when there is a non-zero chance of it.\nfunction RainBadge({ probability, compact }: { probability?: number; compact?: boolean }) {\n if (probability === undefined || probability === null || probability <= 0) return null;\n return <span style={styles.rain}>{compact ? ` · ${probability}%` : ` · ${probability}% rain`}</span>;\n}\n\n/**\n * Renders one location's forecast: a live current time (ticking, in the\n * location's own timezone), a clickable degF / degC unit switch, high/low and\n * precipitation for the next hours and days.\n */\nfunction SingleWeatherForecast(props: Props) {\n // Temperature unit is local state so the degF / degC switch can toggle it;\n // it is seeded from (and re-synced to) the temperatureUnit prop.\n const [unit, setUnit] = useState<TemperatureUnit>(props.temperatureUnit ?? 'fahrenheit');\n useEffect(() => {\n setUnit(props.temperatureUnit ?? 'fahrenheit');\n }, [props.temperatureUnit]);\n\n const { data, loading, error } = useWeatherForecast({ ...props, temperatureUnit: unit });\n\n // A ticking clock so the displayed current time stays accurate to the second.\n const [now, setNow] = useState(() => new Date());\n useEffect(() => {\n const id = setInterval(() => setNow(new Date()), 1000);\n return () => clearInterval(id);\n }, []);\n\n const toggleUnit = () => setUnit((u) => (u === 'celsius' ? 'fahrenheit' : 'celsius'));\n\n const renderUnitSwitch = (fontSize: number) => (\n <span\n role=\"button\"\n tabIndex={0}\n onClick={(e) => {\n e.stopPropagation();\n toggleUnit();\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n toggleUnit();\n }\n }}\n title=\"Switch temperature units\"\n aria-label={`Switch temperature units, currently ${unit === 'celsius' ? 'Celsius' : 'Fahrenheit'}`}\n style={{ ...styles.unitSwitch, fontSize }}\n >\n <span style={{ fontWeight: unit === 'fahrenheit' ? 700 : 400, opacity: unit === 'fahrenheit' ? 1 : 0.45 }}>&deg;F</span>\n <span style={{ opacity: 0.4, margin: '0 3px' }}>|</span>\n <span style={{ fontWeight: unit === 'celsius' ? 700 : 400, opacity: unit === 'celsius' ? 1 : 0.45 }}>&deg;C</span>\n </span>\n );\n\n // Keep showing the last forecast while a unit switch refetches in the\n // background; only suppress output on the very first load.\n if (loading && !data) return null;\n if (error) return <div className={props.className} style={props.style}>Error: {error.message}</div>;\n if (!data) return <div className={props.className} style={props.style}>No forecast available.</div>;\n\n const timezone = data.location?.timezone;\n const currentDate = formatInZone(now, timezone, { weekday: 'short', month: 'short', day: 'numeric' });\n const currentTime = formatInZone(now, timezone, { hour: 'numeric', minute: '2-digit', second: '2-digit', timeZoneName: 'short' });\n // The compact layout shows the clock at a larger size, so the zone label is\n // split off onto the smaller date line to keep the time on one line.\n const currentClock = clockParts(now, timezone);\n const currentZone = zoneLabel(now, timezone);\n\n if (props.compact) {\n const forecastBase = new Date(data.current.time);\n const today = data.daily[0];\n const in6Hours = closestHour(data.hourly, new Date(forecastBase.getTime() + 6 * 60 * 60 * 1000));\n const windSpeedUnitLabel = WIND_SPEED_UNIT_LABELS[props.windSpeedUnit ?? 'mph'];\n // Today is already summarised in the card's own stats line, so the bottom\n // row starts at tomorrow and shows the next three days.\n const upcomingDays = data.daily.slice(1, 4);\n\n return (\n <div className={props.className} style={{ ...styles.compactRoot, ...props.style }}>\n <div style={styles.compactBody}>\n <div style={styles.compactCity}>\n {data.location?.city || 'Current location'}\n {data.location?.region ? `, ${data.location.region}` : ''}\n </div>\n\n <div style={styles.compactHeader}>\n <WeatherIcon condition={data.current.icon} width={30} height={30} title=\"Current weather\" />\n <strong style={styles.compactTemp}>{data.current.temperature}</strong>\n {renderUnitSwitch(13)}\n {in6Hours && (\n <span style={styles.compactOutlook}>\n <WeatherIcon condition={in6Hours.icon} width={14} height={14} title=\"In 6 hours\" />\n {in6Hours.temperature}&deg; 6h\n <RainBadge probability={in6Hours.precipitationProbability} compact />\n </span>\n )}\n </div>\n\n <div>\n <div style={styles.compactTime}>\n {currentClock.main}\n {currentClock.tail && <span style={styles.compactSeconds}>{currentClock.tail}</span>}\n </div>\n <div style={styles.compactDate}>\n {currentDate}\n {currentZone ? ` · ${currentZone}` : ''}\n </div>\n </div>\n\n <div style={styles.compactStats}>\n {today && (\n <span style={styles.compactStat}>\n <WeatherIcon condition={today.icon} width={16} height={16} title=\"Today's high and low\" />\n {today.max}&deg; / {today.min}&deg;\n <RainBadge probability={today.precipitationProbabilityMax} compact />\n </span>\n )}\n <span style={styles.compactStat}>\n <WindIcon width={16} height={16} title=\"Wind speed\" />\n {data.current.windSpeed !== undefined ? `${Math.round(data.current.windSpeed)} ${windSpeedUnitLabel}` : '—'}\n </span>\n </div>\n </div>\n\n {upcomingDays.length > 0 && (\n <div style={styles.upcoming}>\n {upcomingDays.map((day) => (\n <div key={day.date} style={styles.upcomingDay} title={upcomingDayDetail(day, windSpeedUnitLabel)}>\n <span style={styles.upcomingDayName}>{upcomingDayLabel(day.date)}</span>\n <WeatherIcon condition={day.icon} width={18} height={18} />\n <span style={styles.upcomingTemps}>\n <strong>{day.max}&deg;</strong>\n <span style={{ opacity: 0.6 }}> / {day.min}&deg;</span>\n </span>\n {day.precipitationProbabilityMax !== undefined && day.precipitationProbabilityMax > 0 && (\n <span style={{ ...styles.rain, fontSize: 11 }}>{day.precipitationProbabilityMax}%</span>\n )}\n </div>\n ))}\n </div>\n )}\n </div>\n );\n }\n\n return (\n <div className={props.className} style={{ ...styles.root, ...props.style }}>\n <div>\n <h3 style={{ margin: 0, fontSize: 20 }}>\n {data.location?.city || 'Current location'}\n {data.location?.region ? `, ${data.location.region}` : ''}\n </h3>\n <div style={styles.tz}>\n {currentDate} &middot; {currentTime}\n {timezone ? ` · ${timezone}` : ''}\n </div>\n <div style={{ ...styles.row, marginTop: 8 }}>\n <WeatherIcon condition={data.current.icon} width={28} height={28} title=\"Current weather\" />\n <strong style={{ fontSize: 28 }}>{data.current.temperature}</strong>\n {renderUnitSwitch(14)}\n </div>\n </div>\n\n <div style={{ marginTop: 20 }}>\n <h4 style={{ margin: '0 0 10px 0' }}>Next hours</h4>\n <div style={styles.hours}>\n {data.hourly.slice(0, props.forecastHours ?? 12).map((hour) => (\n <div key={hour.time} style={styles.hourCard}>\n <div style={{ fontSize: 12 }}>{new Date(hour.time).toLocaleTimeString([], { hour: 'numeric' })}</div>\n <WeatherIcon condition={hour.icon} width={20} height={20} style={{ margin: '6px auto' }} />\n <div>{hour.temperature}&deg;</div>\n {hour.precipitationProbability !== undefined && hour.precipitationProbability > 0 && (\n <div style={{ ...styles.rain, fontSize: 12 }}>{hour.precipitationProbability}%</div>\n )}\n </div>\n ))}\n </div>\n </div>\n\n <div style={{ marginTop: 20 }}>\n <h4 style={{ margin: '0 0 10px 0' }}>Next days</h4>\n <div>\n {data.daily.map((day) => (\n <div key={day.date} style={styles.dayRow}>\n <div style={{ width: 120 }}>{new Date(day.date).toLocaleDateString([], { weekday: 'short', month: 'short', day: 'numeric' })}</div>\n <WeatherIcon condition={day.icon} width={20} height={20} />\n <div>\n {day.min}&deg; / {day.max}&deg;\n {day.precipitationProbabilityMax !== undefined && day.precipitationProbabilityMax > 0 && (\n <span style={styles.rain}> &middot; {day.precipitationProbabilityMax}%</span>\n )}\n </div>\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n\n/**\n * Weather forecast widget. When `locations` is provided, one forecast is\n * rendered per location; otherwise a single auto-detected forecast is shown.\n */\nexport function WeatherForecast(props: Props) {\n const { locations, ...rest } = props;\n\n if (locations && locations.length > 0) {\n const gap = props.compact ? 8 : 16;\n return (\n <div style={{ ...styles.list, gap }}>\n {locations.map((loc, index) => (\n <SingleWeatherForecast\n key={`${loc.label ?? ''}:${loc.latitude ?? ''}:${loc.longitude ?? ''}:${index}`}\n {...rest}\n latitude={typeof loc.latitude === 'number' ? loc.latitude : undefined}\n longitude={typeof loc.longitude === 'number' ? loc.longitude : undefined}\n location={{ city: loc.label, timezone: loc.timezone }}\n />\n ))}\n </div>\n );\n }\n\n return <SingleWeatherForecast {...rest} />;\n}\n"],"mappings":"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","names":["res","data","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/weatherCodes.ts","../src/lib/cache.ts","../src/api/geolocation.ts","../src/api/forecast.ts","../src/hooks/useWeatherForecast.ts","../src/icons/BaseIcon.tsx","../src/icons/weather-icons.tsx","../src/icons/WeatherIcon.tsx","../src/components/WeatherForecast.tsx"],"sourcesContent":["export * from './types';\nexport * from './weatherCodes';\nexport { clearWeatherForecastCache } from './lib/cache';\nexport * from './api/geolocation';\nexport * from './api/forecast';\nexport * from './hooks/useWeatherForecast';\nexport * from './icons/WeatherIcon';\nexport * from './components/WeatherForecast';\n","import type { WeatherCondition } from './types';\n\nexport function getWeatherIcon(code: number, isDay = 1): WeatherCondition {\n if (code === 0) return 'sun';\n if ([1, 2].includes(code)) return isDay ? 'cloud-sun' : 'clouds';\n if (code === 3) return 'clouds';\n if ([45, 48].includes(code)) return 'cloud-fog';\n if ([51, 53, 55, 56, 57].includes(code)) return 'cloud-drizzle';\n if ([61, 63].includes(code)) return 'cloud-showers';\n if ([65, 66, 67, 80, 81, 82].includes(code)) return 'cloud-showers-heavy';\n if ([71, 73, 75, 77, 85, 86].includes(code)) return 'cloud-snow';\n if (code === 95) return 'cloud-bolt';\n if ([96, 99].includes(code)) return 'cloud-hail';\n return 'clouds';\n}\n","const CACHE_PREFIX = 'weather-forecast-cache:';\nconst CACHE_TTL_MS = 30 * 60 * 1000;\n\ntype CacheEntry<T> = {\n timestamp: number;\n data: T;\n};\n\nfunction getStorage(): Storage | null {\n if (typeof window === 'undefined' || !window.localStorage) return null;\n return window.localStorage;\n}\n\n/** Returns the cached value for `key` if it was written within the last 30 minutes. */\nexport function readCachedForecast<T>(key: string): T | null {\n const storage = getStorage();\n if (!storage) return null;\n\n try {\n const raw = storage.getItem(CACHE_PREFIX + key);\n if (!raw) return null;\n\n const entry = JSON.parse(raw) as CacheEntry<T>;\n if (Date.now() - entry.timestamp > CACHE_TTL_MS) {\n storage.removeItem(CACHE_PREFIX + key);\n return null;\n }\n\n return entry.data;\n } catch {\n return null;\n }\n}\n\nexport function writeCachedForecast<T>(key: string, data: T): void {\n const storage = getStorage();\n if (!storage) return;\n\n try {\n const entry: CacheEntry<T> = { timestamp: Date.now(), data };\n storage.setItem(CACHE_PREFIX + key, JSON.stringify(entry));\n } catch {\n // Storage full or unavailable (e.g. private browsing) — safe to ignore,\n // the next call will simply hit the network again.\n }\n}\n\n/** Clears all cached forecasts. Mainly useful for tests/debugging. */\nexport function clearWeatherForecastCache(): void {\n const storage = getStorage();\n if (!storage) return;\n\n for (let i = storage.length - 1; i >= 0; i--) {\n const key = storage.key(i);\n if (key?.startsWith(CACHE_PREFIX)) storage.removeItem(key);\n }\n}\n","import type { WeatherLocation } from '../types';\n\nconst DEFAULT_IP_API_URL = 'https://ipapi.co';\n\ntype IpApiResponse = {\n city?: string;\n region?: string;\n country_name?: string;\n timezone?: string;\n latitude?: number;\n longitude?: number;\n error?: boolean;\n reason?: string;\n};\n\ntype GeoWorkerResponse = {\n city?: string;\n region?: string;\n country?: string;\n timezone?: string;\n latitude?: number | string;\n longitude?: number | string;\n};\n\nexport async function getClientLocation(geoEndpoint?: string, ip?: string): Promise<WeatherLocation> {\n if (geoEndpoint) {\n const url = ip ? `${geoEndpoint}?ip=${encodeURIComponent(ip)}` : geoEndpoint;\n const res = await fetch(url);\n if (!res.ok) throw new Error(`Geolocation worker lookup failed: ${res.status}`);\n const data: GeoWorkerResponse = await res.json();\n\n return {\n city: data.city,\n region: data.region,\n country: data.country,\n timezone: data.timezone,\n latitude: Number(data.latitude),\n longitude: Number(data.longitude),\n };\n }\n\n const res = await fetch(`${DEFAULT_IP_API_URL}/${ip ? `${encodeURIComponent(ip)}/` : ''}json/`);\n if (!res.ok) throw new Error(`ipapi.co lookup failed: ${res.status}`);\n const data: IpApiResponse = await res.json();\n\n if (data.error) {\n throw new Error(`ipapi.co lookup failed: ${data.reason ?? 'unknown error'}`);\n }\n\n return {\n city: data.city,\n region: data.region,\n country: data.country_name,\n timezone: data.timezone,\n latitude: Number(data.latitude),\n longitude: Number(data.longitude),\n };\n}\n","import type { WeatherForecastData, WeatherForecastOptions, WeatherLocation } from '../types';\nimport { getWeatherIcon } from '../weatherCodes';\nimport { getClientLocation } from './geolocation';\nimport { readCachedForecast, writeCachedForecast } from '../lib/cache';\n\nfunction buildUrl(latitude: number, longitude: number, options: WeatherForecastOptions) {\n const params = new URLSearchParams({\n latitude: String(latitude),\n longitude: String(longitude),\n timezone: options.location?.timezone || 'auto',\n temperature_unit: options.temperatureUnit || 'fahrenheit',\n wind_speed_unit: options.windSpeedUnit || 'mph',\n forecast_days: String(options.forecastDays ?? 5),\n forecast_hours: String(options.forecastHours ?? 24),\n current: [\n 'temperature_2m',\n 'weather_code',\n 'is_day',\n 'rain',\n 'showers',\n 'snowfall',\n 'wind_speed_10m',\n ].join(','),\n hourly: [\n 'temperature_2m',\n 'weather_code',\n 'precipitation_probability',\n 'rain',\n 'showers',\n 'snowfall',\n ].join(','),\n daily: [\n 'temperature_2m_max',\n 'temperature_2m_min',\n 'weather_code',\n 'precipitation_probability_max',\n 'precipitation_sum',\n 'wind_speed_10m_max',\n ].join(','),\n });\n\n return `https://api.open-meteo.com/v1/forecast?${params.toString()}`;\n}\n\nexport async function getWeatherForecast(\n options: WeatherForecastOptions = {}\n): Promise<WeatherForecastData> {\n let location: WeatherLocation;\n\n if (typeof options.latitude === 'number' && typeof options.longitude === 'number') {\n location = {\n ...options.location,\n latitude: options.latitude,\n longitude: options.longitude,\n };\n } else {\n location = await getClientLocation(options.geoEndpoint, options.ip);\n }\n\n const url = buildUrl(location.latitude, location.longitude, { ...options, location });\n\n const cached = readCachedForecast<WeatherForecastData>(url);\n if (cached) return cached;\n\n const response = await fetch(url, { headers: { 'Content-Type': 'application/json' } });\n\n if (!response.ok) {\n throw new Error(`Weather request failed: ${response.status} ${response.statusText}`);\n }\n\n const data = await response.json();\n\n if (!data?.current || !data?.hourly || !data?.daily) {\n throw new Error('Invalid weather response');\n }\n\n const result: WeatherForecastData = {\n location: { ...location, timezone: data.timezone || location.timezone },\n current: {\n time: data.current.time,\n temperature: Math.round(data.current.temperature_2m),\n weatherCode: data.current.weather_code,\n icon: getWeatherIcon(data.current.weather_code, data.current.is_day),\n isDay: data.current.is_day,\n rain: data.current.rain,\n showers: data.current.showers,\n snowfall: data.current.snowfall,\n windSpeed: data.current.wind_speed_10m,\n },\n hourly: data.hourly.time.map((time: string, index: number) => ({\n time,\n temperature: Math.round(data.hourly.temperature_2m[index]),\n weatherCode: data.hourly.weather_code[index],\n precipitationProbability: data.hourly.precipitation_probability?.[index],\n rain: data.hourly.rain?.[index],\n showers: data.hourly.showers?.[index],\n snowfall: data.hourly.snowfall?.[index],\n icon: getWeatherIcon(data.hourly.weather_code[index]),\n })),\n daily: data.daily.time.map((date: string, index: number) => ({\n date,\n min: Math.round(data.daily.temperature_2m_min[index]),\n max: Math.round(data.daily.temperature_2m_max[index]),\n weatherCode: data.daily.weather_code[index],\n precipitationProbabilityMax: data.daily.precipitation_probability_max?.[index],\n precipitationSum: data.daily.precipitation_sum?.[index],\n windSpeedMax: data.daily.wind_speed_10m_max?.[index],\n icon: getWeatherIcon(data.daily.weather_code[index]),\n })),\n };\n\n writeCachedForecast(url, result);\n return result;\n}\n","import { useEffect, useState } from 'react';\nimport type { WeatherForecastData, WeatherForecastOptions } from '../types';\nimport { getWeatherForecast } from '../api/forecast';\n\nexport function useWeatherForecast(options: WeatherForecastOptions = {}) {\n const [data, setData] = useState<WeatherForecastData | null>(null);\n const [error, setError] = useState<Error | null>(null);\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n let active = true;\n setLoading(true);\n setError(null);\n\n getWeatherForecast(options)\n .then((result) => { if (active) setData(result); })\n .catch((err) => { if (active) setError(err instanceof Error ? err : new Error('Unknown error')); })\n .finally(() => { if (active) setLoading(false); });\n\n return () => { active = false; };\n }, [\n options.latitude,\n options.longitude,\n options.geoEndpoint,\n options.ip,\n options.forecastDays,\n options.forecastHours,\n options.temperatureUnit,\n options.windSpeedUnit,\n options.location?.timezone,\n ]);\n\n return { data, error, loading };\n}\n","import React from 'react';\nimport type { IconProps } from '../types';\n\nexport function BaseIcon({ title, children, ...props }: IconProps & { children: React.ReactNode }) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n role={title ? 'img' : 'presentation'}\n aria-hidden={title ? undefined : true}\n aria-label={title}\n {...props}\n >\n {title ? <title>{title}</title> : null}\n {children}\n </svg>\n );\n}\n","import React from 'react';\nimport type { IconProps } from '../types';\nimport { BaseIcon } from './BaseIcon';\n\nexport const CloudBoltIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M7 18a4.815 4.815 0 0 1-3.327-1.318A4.403 4.403 0 0 1 2.295 13.5c0-1.194.496-2.338 1.378-3.182A4.815 4.815 0 0 1 7 9c.295-1.313 1.157-2.466 2.397-3.207a5.971 5.971 0 0 1 2.025-.749 6.224 6.224 0 0 1 2.19.006c.721.131 1.408.39 2.02.76a5.36 5.36 0 0 1 1.543 1.397c.407.552.69 1.172.831 1.823.142.65.14 1.32-.006 1.97h1a3.5 3.5 0 0 1 0 7h-1m-4.75-5L11 17.5h3.375l-2.25 4.5\" /></BaseIcon>\n);\nexport const CloudDrizzleIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M12 18v1m-3-4v1m6-1v1m2 3v1M7 19v1m-3-3.034a4.63 4.63 0 0 1-.327-.284A4.403 4.403 0 0 1 2.295 13.5c0-1.194.496-2.338 1.378-3.182A4.815 4.815 0 0 1 7 9c.295-1.313 1.157-2.466 2.397-3.207a5.971 5.971 0 0 1 2.025-.749 6.224 6.224 0 0 1 2.19.006c.721.131 1.408.39 2.02.76a5.36 5.36 0 0 1 1.543 1.397c.407.552.69 1.172.831 1.823.142.65.14 1.32-.006 1.97h1a3.5 3.5 0 0 1 1.5 6.662\" /></BaseIcon>\n);\nexport const CloudFogIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M4 16.966a4.63 4.63 0 0 1-.327-.284A4.403 4.403 0 0 1 2.295 13.5c0-1.194.496-2.338 1.378-3.182A4.815 4.815 0 0 1 7 9c.295-1.313 1.157-2.466 2.397-3.207a5.971 5.971 0 0 1 2.025-.749 6.224 6.224 0 0 1 2.19.006c.721.131 1.408.39 2.02.76a5.36 5.36 0 0 1 1.543 1.397c.407.552.69 1.172.831 1.823.142.65.14 1.32-.006 1.97h1a3.5 3.5 0 0 1 1.5 6.662M5 21h7m4 0h3M8 17h8\" /></BaseIcon>\n);\nexport const CloudHailIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M8 17.45v.1m-1.5 3.9v.1m10.5-4.1v.1m-1.5 3.9v.1m-3-4.1v.1m-1.5 3.9v.1m10.475-4.575A3.5 3.5 0 0 0 19 11h-1c.146-.65.148-1.32.006-1.97a4.784 4.784 0 0 0-.831-1.823 5.36 5.36 0 0 0-1.544-1.398 5.964 5.964 0 0 0-2.02-.759 6.224 6.224 0 0 0-2.19-.006 5.971 5.971 0 0 0-2.024.749C8.157 6.533 7.295 7.687 7 9a4.815 4.815 0 0 0-3.327 1.318A4.403 4.403 0 0 0 2.295 13.5c0 1.194.496 2.338 1.378 3.182.105.1.214.195.327.284m4.5.534a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Zm-1.5 4a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Zm10.5-4a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Zm-1.5 4a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Zm-3-4a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Zm-1.5 4a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Z\" /></BaseIcon>\n);\nexport const CloudShowersIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M4 16.966a4.63 4.63 0 0 1-.327-.284A4.403 4.403 0 0 1 2.295 13.5c0-1.194.496-2.338 1.378-3.182A4.815 4.815 0 0 1 7 9c.295-1.313 1.157-2.466 2.397-3.207a5.971 5.971 0 0 1 2.025-.749 6.224 6.224 0 0 1 2.19.006c.721.131 1.408.39 2.02.76a5.36 5.36 0 0 1 1.543 1.397c.407.552.69 1.172.831 1.823.142.65.14 1.32-.006 1.97h1a3.5 3.5 0 0 1 1.5 6.662M12 13v2m0 3v2m4-5v2m0 3v2m-8-7v2m0 3v2\" /></BaseIcon>\n);\nexport const CloudShowersHeavyIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M4 16.966a4.63 4.63 0 0 1-.327-.284A4.403 4.403 0 0 1 2.295 13.5c0-1.194.496-2.338 1.378-3.182A4.815 4.815 0 0 1 7 9c.295-1.313 1.157-2.466 2.397-3.207a5.971 5.971 0 0 1 2.025-.749 6.224 6.224 0 0 1 2.19.006c.721.131 1.408.39 2.02.76a5.36 5.36 0 0 1 1.543 1.397c.407.552.69 1.172.831 1.823.142.65.14 1.32-.006 1.97h1a3.5 3.5 0 0 1 1.5 6.662M16 15v6m-8-6v6m4-8v6\" /></BaseIcon>\n);\nexport const CloudSleetIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M13.5 17.45a1 1 0 1 0-2 0h2Zm-2 .1a1 1 0 1 0 2 0h-2Zm.5 3.9a1 1 0 1 0-2 0h2Zm-2 .1a1 1 0 1 0 2 0h-2Zm-6.327-4.868.692-.723-.692.723ZM7 9v1a1 1 0 0 0 .976-.781L7 9Zm2.397-3.207.512.858-.512-.858ZM18 11l-.976-.219A1 1 0 0 0 18 12v-1Zm2.768 5.268a1 1 0 0 0 1.414 1.414l-.707-.707-.707-.707ZM3.379 17.75a1 1 0 0 0 1.242-1.567L4 16.966l-.62.784Zm5.55-.379a1 1 0 0 0-1.857-.742L8 17l.928.371ZM5.071 21.63a1 1 0 0 0 1.856.742L6 22l-.928-.371Zm13.857-4.258a1 1 0 0 0-1.857-.742L18 17l.928.371Zm-3.857 4.258a1 1 0 0 0 1.857.742L16 22l-.928-.371ZM13 17.5h-1a.5.5 0 0 1 .5-.5v2a1.5 1.5 0 0 0 1.5-1.5h-1Zm-.5.5v-1a.5.5 0 0 1 .5.5h-2a1.5 1.5 0 0 0 1.5 1.5v-1Zm-.5-.5h1a.5.5 0 0 1-.5.5v-2a1.5 1.5 0 0 0-1.5 1.5h1Zm.5-.5v1a.5.5 0 0 1-.5-.5h2a1.5 1.5 0 0 0-1.5-1.5v1Zm0 .45h-1v.1h2v-.1h-1Zm-1 4.05h-1a.5.5 0 0 1 .5-.5v2a1.5 1.5 0 0 0 1.5-1.5h-1Zm-.5.5v-1a.5.5 0 0 1 .5.5h-2A1.5 1.5 0 0 0 11 23v-1Zm-.5-.5h1a.5.5 0 0 1-.5.5v-2a1.5 1.5 0 0 0-1.5 1.5h1Zm.5-.5v1a.5.5 0 0 1-.5-.5h2A1.5 1.5 0 0 0 11 20v1Zm0 .45h-1v.1h2v-.1h-1Zm-7.327-4.768.692-.723a3.403 3.403 0 0 1-1.07-2.459h-2c0 1.473.613 2.877 1.687 3.905l.691-.723ZM2.295 13.5h1c0-.914.38-1.8 1.07-2.46l-.692-.722-.69-.723A5.403 5.403 0 0 0 1.294 13.5h1Zm1.378-3.182.692.723A3.815 3.815 0 0 1 7 10V8c-1.499 0-2.945.57-4.018 1.595l.691.723ZM7 9l.976.219c.23-1.024.91-1.956 1.933-2.568l-.512-.858-.513-.859c-1.456.87-2.5 2.245-2.86 3.847L7 9Zm2.397-3.207.512.858a4.972 4.972 0 0 1 1.686-.622l-.173-.985-.174-.985a6.971 6.971 0 0 0-2.364.875l.513.859Zm2.025-.749.173.985a5.223 5.223 0 0 1 1.837.005l.18-.984.178-.984a7.223 7.223 0 0 0-2.542-.007l.174.985Zm2.19.006-.18.984c.606.11 1.177.326 1.681.631l.518-.856.518-.855a6.963 6.963 0 0 0-2.359-.888l-.179.984Zm2.02.76-.519.855c.505.305.93.693 1.257 1.136l.805-.594.805-.594a6.361 6.361 0 0 0-1.831-1.659l-.518.855Zm1.543 1.397-.805.594c.326.442.549.932.66 1.441l.976-.212.978-.213a5.786 5.786 0 0 0-1.005-2.204l-.804.594Zm.831 1.823-.977.212c.111.509.11 1.031-.005 1.539L18 11l.976.219c.178-.792.18-1.609.008-2.402l-.978.213ZM18 11v1h1v-2h-1v1Zm1 0v1a2.5 2.5 0 0 1 1.768.732l.707-.707.707-.707A4.5 4.5 0 0 0 19 10v1Zm2.475 1.025-.707.707A2.5 2.5 0 0 1 21.5 14.5h2a4.5 4.5 0 0 0-1.318-3.182l-.707.707ZM22.5 14.5h-1a2.5 2.5 0 0 1-.732 1.768l.707.707.707.707A4.5 4.5 0 0 0 23.5 14.5h-1ZM4 16.966l.62-.784a3.684 3.684 0 0 1-.255-.223l-.692.723-.69.723c.126.121.259.237.396.345L4 16.966ZM8 17l-.928-.371-2 5L6 22l.928.371 2-5L8 17Zm10 0-.928-.371-2 5L16 22l.928.371 2-5L18 17Z\" /></BaseIcon>\n);\nexport const CloudSnowIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M8 15h2m-1-1v2m5-1h2m-1-1v2M5 21h2m-1-1v2m5-2h2m-1-1v2m5 0h2m-1-1v2M4 16.966a4.63 4.63 0 0 1-.327-.284A4.403 4.403 0 0 1 2.295 13.5c0-1.194.496-2.338 1.378-3.182A4.815 4.815 0 0 1 7 9c.295-1.313 1.157-2.466 2.397-3.207a5.971 5.971 0 0 1 2.025-.749 6.224 6.224 0 0 1 2.19.006c.721.131 1.408.39 2.02.76a5.36 5.36 0 0 1 1.543 1.397c.407.552.69 1.172.831 1.823.142.65.14 1.32-.006 1.97h1a3.5 3.5 0 0 1 1.5 6.662\" /></BaseIcon>\n);\nexport const CloudSunIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M10.201 19.997c-1.977 0-3.58-1.544-3.58-3.448 0-1.903 1.603-3.446 3.58-3.446.303-1.355 1.38-2.461 2.827-2.902 1.445-.44 3.042-.148 4.186.77 1.144.915 1.663 2.312 1.361 3.667h.761c1.472 0 2.664 1.2 2.664 2.68A2.672 2.672 0 0 1 19.336 20h-9.134m2.66-12.934a4.166 4.166 0 0 0-2.677-1.732m2.677 1.732 2.744-1.892m-2.744 1.892c.55.796.802 1.754.723 2.709m-3.4-4.44a4.167 4.167 0 0 0-3.118.667m3.118-.668.602-3.278m-3.72 3.946A4.167 4.167 0 0 0 5.334 8.68m1.733-2.678L5.174 3.26m.16 5.42c-.2 1.088.041 2.21.669 3.12m-.669-3.12-3.278-.601m3.947 3.72-2.744 1.893m2.744-1.893a4.167 4.167 0 0 0 2.174 1.607\" /></BaseIcon>\n);\nexport const CloudsIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M8.958 21.05C6.772 21.05 5 19.344 5 17.24s1.772-3.81 3.958-3.81c.335-1.498 1.525-2.72 3.124-3.207 1.598-.487 3.363-.164 4.628.85 1.264 1.011 1.837 2.556 1.504 4.053h.842A2.953 2.953 0 0 1 22 18.09a2.954 2.954 0 0 1-2.945 2.964H8.958M18 8c-.5-2.5-3.532-3.401-5.5-2.5a3.117 3.117 0 0 0-2.11-2.312c-1.352-.411-2.845-.138-3.915.72-1.07.855-1.554 2.161-1.273 3.428h-.711A2.498 2.498 0 0 0 2 9.843c0 1.094.695 2.023 1.666 2.366\" /></BaseIcon>\n);\nexport const SnowflakeIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"m9.25 4-1 2H6m-2.25 4L5 12m0 0-1.25 2M5 12h3m-3 0H2.5m17.75-2L19 12l1.25 2M6 18h2.25l1 2M14 9h-4m4 0 3-5m-3 5 2 3m-6-3L7 4m3 5-2 3m0 0 2 3m0 0-3 5m3-5h4m2-3-2 3m2-3h5.5M14 15l3 5M14.75 4l1 2H18m0 12h-2.25l-1 2\" /></BaseIcon>\n);\nexport const SunIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M12 4V2m0 20v-2m10-8h-2M4 12H2m15.657-5.657 1.414-1.414M4.93 19.07l1.414-1.414m12.728 1.414-1.414-1.414M6.343 6.343 4.93 4.929M7 12a5 5 0 1 0 10 0 5 5 0 0 0-10 0Z\" /></BaseIcon>\n);\nexport const WindIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M9.59 4.59A2 2 0 1 1 11 8H2m10.59 11.41A2 2 0 1 0 14 16H2m15.73-8.27A2.5 2.5 0 1 1 19.5 12H2\" /></BaseIcon>\n);\n","import React from 'react';\nimport type { IconProps, WeatherCondition } from '../types';\nimport {\n CloudBoltIcon,\n CloudDrizzleIcon,\n CloudFogIcon,\n CloudHailIcon,\n CloudShowersHeavyIcon,\n CloudShowersIcon,\n CloudSleetIcon,\n CloudSnowIcon,\n CloudSunIcon,\n CloudsIcon,\n SnowflakeIcon,\n SunIcon,\n} from './weather-icons';\n\nconst iconMap: Record<WeatherCondition, React.ComponentType<IconProps>> = {\n sun: SunIcon,\n 'cloud-sun': CloudSunIcon,\n clouds: CloudsIcon,\n 'cloud-fog': CloudFogIcon,\n 'cloud-drizzle': CloudDrizzleIcon,\n 'cloud-showers': CloudShowersIcon,\n 'cloud-showers-heavy': CloudShowersHeavyIcon,\n 'cloud-sleet': CloudSleetIcon,\n 'cloud-snow': CloudSnowIcon,\n snowflake: SnowflakeIcon,\n 'cloud-bolt': CloudBoltIcon,\n 'cloud-hail': CloudHailIcon,\n};\n\nexport function WeatherIcon({ condition, ...props }: IconProps & { condition: WeatherCondition }) {\n const Icon = iconMap[condition] || CloudsIcon;\n return <Icon {...props} />;\n}\n","import React, { useEffect, useState } from 'react';\nimport { useWeatherForecast } from '../hooks/useWeatherForecast';\nimport type { DailyWeather, TemperatureUnit, WeatherForecastOptions, WindSpeedUnit } from '../types';\nimport { WeatherIcon } from '../icons/WeatherIcon';\nimport { WindIcon } from '../icons/weather-icons';\n\ntype Props = WeatherForecastOptions & {\n className?: string;\n style?: React.CSSProperties;\n compact?: boolean;\n};\n\nconst WIND_SPEED_UNIT_LABELS: Record<WindSpeedUnit, string> = {\n kmh: 'km/h',\n mph: 'mph',\n ms: 'm/s',\n kn: 'kn',\n};\n\n// Format a real instant in a specific IANA timezone, falling back to the\n// browser's local zone if the timezone string is missing or invalid.\nfunction formatInZone(instant: Date, timezone: string | undefined, options: Intl.DateTimeFormatOptions): string {\n try {\n return new Intl.DateTimeFormat([], { ...options, timeZone: timezone || undefined }).format(instant);\n } catch {\n return new Intl.DateTimeFormat([], options).format(instant);\n }\n}\n\n// The clock is shown with the hour and minute large and the seconds (plus any\n// AM/PM marker) trailing in a smaller, dimmer size, so split it into those two\n// halves. Falls back to one undivided string if the locale's parts are not\n// where we expect them.\nfunction clockParts(instant: Date, timezone: string | undefined): { main: string; tail: string } {\n const options: Intl.DateTimeFormatOptions = { hour: 'numeric', minute: '2-digit', second: '2-digit' };\n const format = (() => {\n try {\n return new Intl.DateTimeFormat([], { ...options, timeZone: timezone || undefined });\n } catch {\n return new Intl.DateTimeFormat([], options);\n }\n })();\n const parts = format.formatToParts(instant);\n const part = (type: Intl.DateTimeFormatPartTypes) => parts.find((p) => p.type === type)?.value;\n const hour = part('hour');\n const minute = part('minute');\n const second = part('second');\n if (!hour || !minute || !second) return { main: format.format(instant), tail: '' };\n const dayPeriod = part('dayPeriod');\n return { main: `${hour}:${minute}`, tail: `:${second}${dayPeriod ? ` ${dayPeriod}` : ''}` };\n}\n\n// Just the zone abbreviation (e.g. \"GMT+2\"), so it can be shown apart from the\n// clock itself.\nfunction zoneLabel(instant: Date, timezone: string | undefined): string {\n const options: Intl.DateTimeFormatOptions = { hour: 'numeric', timeZoneName: 'short' };\n try {\n const format = (() => {\n try {\n return new Intl.DateTimeFormat([], { ...options, timeZone: timezone || undefined });\n } catch {\n return new Intl.DateTimeFormat([], options);\n }\n })();\n return format.formatToParts(instant).find((part) => part.type === 'timeZoneName')?.value ?? '';\n } catch {\n return '';\n }\n}\n\n// Daily dates arrive as plain \"YYYY-MM-DD\" calendar days; `new Date(...)` would\n// read those as UTC midnight and roll back a day in western timezones, so build\n// the date from its parts and let it stay a local calendar day.\nfunction parseCalendarDate(date: string): Date {\n const [year, month, day] = date.split('-').map(Number);\n if (!year || !month || !day) return new Date(date);\n return new Date(year, month - 1, day);\n}\n\n// The upcoming days sit in narrow columns in the card, so they are labelled\n// with the abbreviated weekday (\"Fri\") rather than a full name or \"Tomorrow\".\nfunction upcomingDayLabel(date: string): string {\n return parseCalendarDate(date).toLocaleDateString([], { weekday: 'short' });\n}\n\n// Daily totals that no longer have a column of their own in the card layout are\n// kept as hover text so the detail is still reachable.\nfunction upcomingDayDetail(day: DailyWeather, windSpeedUnitLabel: string): string | undefined {\n const details: string[] = [];\n if (day.precipitationSum !== undefined) details.push(`${day.precipitationSum.toFixed(1)} mm`);\n if (day.windSpeedMax !== undefined) details.push(`${day.windSpeedMax.toFixed(1)} ${windSpeedUnitLabel} wind`);\n return details.length > 0 ? details.join(' · ') : undefined;\n}\n\nconst styles = {\n root: {\n fontFamily: 'system-ui, sans-serif',\n border: '1px solid #e5e7eb',\n borderRadius: 12,\n padding: 16,\n background: '#fff',\n color: '#111827',\n maxWidth: 640,\n } as React.CSSProperties,\n row: { display: 'flex', alignItems: 'center', gap: 8 } as React.CSSProperties,\n hours: { display: 'flex', gap: 12, overflowX: 'auto', paddingBottom: 4 } as React.CSSProperties,\n hourCard: { minWidth: 72, textAlign: 'center' as const, padding: 8, borderRadius: 8, background: '#f9fafb' },\n dayRow: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, padding: '8px 0', borderBottom: '1px solid #f3f4f6' } as React.CSSProperties,\n // Fluid: fills whatever container it is dropped into (e.g. the full\n // sidebar/column width on desktop), with generous, even padding. Current\n // conditions sit on the left and the upcoming days on the right; the two\n // halves wrap into a stack when the container gets narrow.\n compactRoot: {\n fontFamily: 'system-ui, sans-serif',\n display: 'flex',\n flexDirection: 'row',\n flexWrap: 'wrap',\n alignItems: 'stretch',\n gap: 24,\n padding: '20px 24px',\n borderRadius: 16,\n width: '100%',\n boxSizing: 'border-box',\n } as React.CSSProperties,\n // Current conditions form two spread-out rows down the left side of the\n // card (rather than five stacked lines), so the card stays short and each\n // row uses the full available width.\n compactBody: { display: 'flex', flexDirection: 'column', justifyContent: 'space-between', gap: 20, flex: '1.6 1 240px', minWidth: 0 } as React.CSSProperties,\n compactRow: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexWrap: 'wrap', gap: '12px 16px' } as React.CSSProperties,\n compactCity: { fontSize: 15, fontWeight: 700, lineHeight: 1.2 } as React.CSSProperties,\n compactHeader: { display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 12 } as React.CSSProperties,\n compactTemp: { fontSize: 32, fontWeight: 600, lineHeight: 1 } as React.CSSProperties,\n compactTime: { fontSize: 26, fontWeight: 600, lineHeight: 1.1 } as React.CSSProperties,\n compactSeconds: { fontSize: 13, fontWeight: 500, opacity: 0.6, marginLeft: 2 } as React.CSSProperties,\n compactDate: { fontSize: 11, opacity: 0.7, marginTop: 5 } as React.CSSProperties,\n compactStats: { display: 'flex', flexWrap: 'wrap', gap: '8px 18px', fontSize: 12, opacity: 0.9 } as React.CSSProperties,\n // Three equal columns on the right side of the card, each stacking\n // weekday / icon / high-low, vertically centered against the current\n // conditions and nudged over behind a divider so the two halves read as\n // separate sections.\n upcoming: {\n display: 'grid',\n gridTemplateColumns: 'repeat(3, 1fr)',\n alignContent: 'center',\n gap: 14,\n flex: '1 1 190px',\n minWidth: 0,\n paddingLeft: 22,\n borderLeft: '1px solid currentColor',\n borderLeftColor: 'rgba(128,128,128,0.25)',\n fontSize: 11,\n } as React.CSSProperties,\n upcomingDay: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 5, textAlign: 'center' } as React.CSSProperties,\n upcomingDayName: { opacity: 0.7, whiteSpace: 'nowrap' } as React.CSSProperties,\n upcomingTemps: { whiteSpace: 'nowrap' } as React.CSSProperties,\n compactStat: { display: 'flex', alignItems: 'center', gap: 6, whiteSpace: 'nowrap' } as React.CSSProperties,\n unitSwitch: { display: 'inline-flex', alignItems: 'center', cursor: 'pointer', userSelect: 'none' } as React.CSSProperties,\n rain: { color: '#2563eb', whiteSpace: 'nowrap' } as React.CSSProperties,\n tz: { fontSize: 12, opacity: 0.7, marginTop: 2 } as React.CSSProperties,\n list: { display: 'flex', flexDirection: 'column' } as React.CSSProperties,\n};\n\n// Precipitation chance is only surfaced once it represents a meaningful risk.\nconst RAIN_RISK_THRESHOLD = 2;\n\nfunction RainBadge({ probability, compact }: { probability?: number; compact?: boolean }) {\n if (probability === undefined || probability === null || probability <= RAIN_RISK_THRESHOLD) return null;\n return <span style={styles.rain}>{compact ? ` · ${probability}%` : ` · ${probability}% rain`}</span>;\n}\n\n/**\n * Renders one location's forecast: a live current time (ticking, in the\n * location's own timezone), a clickable degF / degC unit switch, high/low and\n * precipitation for the next hours and days.\n */\nfunction SingleWeatherForecast(props: Props) {\n // Temperature unit is local state so the degF / degC switch can toggle it;\n // it is seeded from (and re-synced to) the temperatureUnit prop.\n const [unit, setUnit] = useState<TemperatureUnit>(props.temperatureUnit ?? 'fahrenheit');\n useEffect(() => {\n setUnit(props.temperatureUnit ?? 'fahrenheit');\n }, [props.temperatureUnit]);\n\n const { data, loading, error } = useWeatherForecast({ ...props, temperatureUnit: unit });\n\n // A ticking clock so the displayed current time stays accurate to the second.\n const [now, setNow] = useState(() => new Date());\n useEffect(() => {\n const id = setInterval(() => setNow(new Date()), 1000);\n return () => clearInterval(id);\n }, []);\n\n const toggleUnit = () => setUnit((u) => (u === 'celsius' ? 'fahrenheit' : 'celsius'));\n\n const renderUnitSwitch = (fontSize: number) => (\n <span\n role=\"button\"\n tabIndex={0}\n onClick={(e) => {\n e.stopPropagation();\n toggleUnit();\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n toggleUnit();\n }\n }}\n title=\"Switch temperature units\"\n aria-label={`Switch temperature units, currently ${unit === 'celsius' ? 'Celsius' : 'Fahrenheit'}`}\n style={{ ...styles.unitSwitch, fontSize }}\n >\n <span style={{ fontWeight: unit === 'fahrenheit' ? 700 : 400, opacity: unit === 'fahrenheit' ? 1 : 0.45 }}>&deg;F</span>\n <span style={{ opacity: 0.4, margin: '0 4px' }}>|</span>\n <span style={{ fontWeight: unit === 'celsius' ? 700 : 400, opacity: unit === 'celsius' ? 1 : 0.45 }}>&deg;C</span>\n </span>\n );\n\n // Keep showing the last forecast while a unit switch refetches in the\n // background; only suppress output on the very first load.\n if (loading && !data) return null;\n if (error) return <div className={props.className} style={props.style}>Error: {error.message}</div>;\n if (!data) return <div className={props.className} style={props.style}>No forecast available.</div>;\n\n const timezone = data.location?.timezone;\n const currentDate = formatInZone(now, timezone, { weekday: 'short', month: 'short', day: 'numeric' });\n const currentTime = formatInZone(now, timezone, { hour: 'numeric', minute: '2-digit', second: '2-digit', timeZoneName: 'short' });\n // The compact layout shows the clock at a larger size, so the zone label is\n // split off onto the smaller date line to keep the time on one line.\n const currentClock = clockParts(now, timezone);\n const currentZone = zoneLabel(now, timezone);\n\n if (props.compact) {\n const today = data.daily[0];\n const windSpeedUnitLabel = WIND_SPEED_UNIT_LABELS[props.windSpeedUnit ?? 'mph'];\n // Today is already summarised in the card's own stats line, so the\n // upcoming-days section starts at tomorrow and shows the next three days.\n const upcomingDays = data.daily.slice(1, 4);\n\n return (\n <div className={props.className} style={{ ...styles.compactRoot, ...props.style }}>\n <div style={styles.compactBody}>\n <div style={styles.compactRow}>\n <div style={styles.compactCity}>\n {data.location?.city || 'Current location'}\n {data.location?.region ? `, ${data.location.region}` : ''}\n </div>\n\n <div style={styles.compactHeader}>\n <WeatherIcon condition={data.current.icon} width={30} height={30} title=\"Current weather\" />\n <strong style={styles.compactTemp}>{data.current.temperature}</strong>\n {renderUnitSwitch(13)}\n </div>\n </div>\n\n <div style={styles.compactRow}>\n <div>\n <div style={styles.compactTime}>\n {currentClock.main}\n {currentClock.tail && <span style={styles.compactSeconds}>{currentClock.tail}</span>}\n </div>\n <div style={styles.compactDate}>\n {currentDate}\n {currentZone ? ` · ${currentZone}` : ''}\n </div>\n </div>\n\n <div style={styles.compactStats}>\n {today && (\n <span style={styles.compactStat}>\n <WeatherIcon condition={today.icon} width={16} height={16} title=\"Today's high and low\" />\n {today.max}&deg; / {today.min}&deg;\n <RainBadge probability={today.precipitationProbabilityMax} compact />\n </span>\n )}\n <span style={styles.compactStat}>\n <WindIcon width={16} height={16} title=\"Wind speed\" />\n {data.current.windSpeed !== undefined ? `${Math.round(data.current.windSpeed)} ${windSpeedUnitLabel}` : '—'}\n </span>\n </div>\n </div>\n </div>\n\n {upcomingDays.length > 0 && (\n <div style={styles.upcoming}>\n {upcomingDays.map((day) => (\n <div key={day.date} style={styles.upcomingDay} title={upcomingDayDetail(day, windSpeedUnitLabel)}>\n <span style={styles.upcomingDayName}>{upcomingDayLabel(day.date)}</span>\n <WeatherIcon condition={day.icon} width={14} height={14} />\n <span style={styles.upcomingTemps}>\n <strong>{day.max}&deg;</strong>\n <span style={{ opacity: 0.6 }}> / {day.min}&deg;</span>\n </span>\n {day.precipitationProbabilityMax !== undefined && day.precipitationProbabilityMax > RAIN_RISK_THRESHOLD && (\n <span style={{ ...styles.rain, fontSize: 10 }}>{day.precipitationProbabilityMax}%</span>\n )}\n </div>\n ))}\n </div>\n )}\n </div>\n );\n }\n\n return (\n <div className={props.className} style={{ ...styles.root, ...props.style }}>\n <div>\n <h3 style={{ margin: 0, fontSize: 20 }}>\n {data.location?.city || 'Current location'}\n {data.location?.region ? `, ${data.location.region}` : ''}\n </h3>\n <div style={styles.tz}>\n {currentDate} &middot; {currentTime}\n {timezone ? ` · ${timezone}` : ''}\n </div>\n <div style={{ ...styles.row, marginTop: 8 }}>\n <WeatherIcon condition={data.current.icon} width={28} height={28} title=\"Current weather\" />\n <strong style={{ fontSize: 28 }}>{data.current.temperature}</strong>\n {renderUnitSwitch(14)}\n </div>\n </div>\n\n <div style={{ marginTop: 20 }}>\n <h4 style={{ margin: '0 0 10px 0' }}>Next hours</h4>\n <div style={styles.hours}>\n {data.hourly.slice(0, props.forecastHours ?? 12).map((hour) => (\n <div key={hour.time} style={styles.hourCard}>\n <div style={{ fontSize: 12 }}>{new Date(hour.time).toLocaleTimeString([], { hour: 'numeric' })}</div>\n <WeatherIcon condition={hour.icon} width={20} height={20} style={{ margin: '6px auto' }} />\n <div>{hour.temperature}&deg;</div>\n {hour.precipitationProbability !== undefined && hour.precipitationProbability > 0 && (\n <div style={{ ...styles.rain, fontSize: 12 }}>{hour.precipitationProbability}%</div>\n )}\n </div>\n ))}\n </div>\n </div>\n\n <div style={{ marginTop: 20 }}>\n <h4 style={{ margin: '0 0 10px 0' }}>Next days</h4>\n <div>\n {data.daily.map((day) => (\n <div key={day.date} style={styles.dayRow}>\n <div style={{ width: 120 }}>{new Date(day.date).toLocaleDateString([], { weekday: 'short', month: 'short', day: 'numeric' })}</div>\n <WeatherIcon condition={day.icon} width={20} height={20} />\n <div>\n {day.min}&deg; / {day.max}&deg;\n {day.precipitationProbabilityMax !== undefined && day.precipitationProbabilityMax > RAIN_RISK_THRESHOLD && (\n <span style={styles.rain}> &middot; {day.precipitationProbabilityMax}%</span>\n )}\n </div>\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n\n/**\n * Weather forecast widget. When `locations` is provided, one forecast is\n * rendered per location; otherwise a single auto-detected forecast is shown.\n */\nexport function WeatherForecast(props: Props) {\n const { locations, ...rest } = props;\n\n if (locations && locations.length > 0) {\n const gap = props.compact ? 8 : 16;\n return (\n <div style={{ ...styles.list, gap }}>\n {locations.map((loc, index) => (\n <SingleWeatherForecast\n key={`${loc.label ?? ''}:${loc.latitude ?? ''}:${loc.longitude ?? ''}:${index}`}\n {...rest}\n latitude={typeof loc.latitude === 'number' ? loc.latitude : undefined}\n longitude={typeof loc.longitude === 'number' ? loc.longitude : undefined}\n location={{ city: loc.label, timezone: loc.timezone }}\n />\n ))}\n </div>\n );\n }\n\n return <SingleWeatherForecast {...rest} />;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEO,SAAS,eAAe,MAAc,QAAQ,GAAqB;AACxE,MAAI,SAAS,EAAG,QAAO;AACvB,MAAI,CAAC,GAAG,CAAC,EAAE,SAAS,IAAI,EAAG,QAAO,QAAQ,cAAc;AACxD,MAAI,SAAS,EAAG,QAAO;AACvB,MAAI,CAAC,IAAI,EAAE,EAAE,SAAS,IAAI,EAAG,QAAO;AACpC,MAAI,CAAC,IAAI,IAAI,IAAI,IAAI,EAAE,EAAE,SAAS,IAAI,EAAG,QAAO;AAChD,MAAI,CAAC,IAAI,EAAE,EAAE,SAAS,IAAI,EAAG,QAAO;AACpC,MAAI,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,EAAE,SAAS,IAAI,EAAG,QAAO;AACpD,MAAI,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,EAAE,SAAS,IAAI,EAAG,QAAO;AACpD,MAAI,SAAS,GAAI,QAAO;AACxB,MAAI,CAAC,IAAI,EAAE,EAAE,SAAS,IAAI,EAAG,QAAO;AACpC,SAAO;AACT;;;ACdA,IAAM,eAAe;AACrB,IAAM,eAAe,KAAK,KAAK;AAO/B,SAAS,aAA6B;AACpC,MAAI,OAAO,WAAW,eAAe,CAAC,OAAO,aAAc,QAAO;AAClE,SAAO,OAAO;AAChB;AAGO,SAAS,mBAAsB,KAAuB;AAC3D,QAAM,UAAU,WAAW;AAC3B,MAAI,CAAC,QAAS,QAAO;AAErB,MAAI;AACF,UAAM,MAAM,QAAQ,QAAQ,eAAe,GAAG;AAC9C,QAAI,CAAC,IAAK,QAAO;AAEjB,UAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,QAAI,KAAK,IAAI,IAAI,MAAM,YAAY,cAAc;AAC/C,cAAQ,WAAW,eAAe,GAAG;AACrC,aAAO;AAAA,IACT;AAEA,WAAO,MAAM;AAAA,EACf,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,oBAAuB,KAAa,MAAe;AACjE,QAAM,UAAU,WAAW;AAC3B,MAAI,CAAC,QAAS;AAEd,MAAI;AACF,UAAM,QAAuB,EAAE,WAAW,KAAK,IAAI,GAAG,KAAK;AAC3D,YAAQ,QAAQ,eAAe,KAAK,KAAK,UAAU,KAAK,CAAC;AAAA,EAC3D,QAAQ;AAAA,EAGR;AACF;AAGO,SAAS,4BAAkC;AAChD,QAAM,UAAU,WAAW;AAC3B,MAAI,CAAC,QAAS;AAEd,WAAS,IAAI,QAAQ,SAAS,GAAG,KAAK,GAAG,KAAK;AAC5C,UAAM,MAAM,QAAQ,IAAI,CAAC;AACzB,QAAI,KAAK,WAAW,YAAY,EAAG,SAAQ,WAAW,GAAG;AAAA,EAC3D;AACF;;;ACtDA,IAAM,qBAAqB;AAsB3B,eAAsB,kBAAkB,aAAsB,IAAuC;AACnG,MAAI,aAAa;AACf,UAAM,MAAM,KAAK,GAAG,WAAW,OAAO,mBAAmB,EAAE,CAAC,KAAK;AACjE,UAAMA,OAAM,MAAM,MAAM,GAAG;AAC3B,QAAI,CAACA,KAAI,GAAI,OAAM,IAAI,MAAM,qCAAqCA,KAAI,MAAM,EAAE;AAC9E,UAAMC,QAA0B,MAAMD,KAAI,KAAK;AAE/C,WAAO;AAAA,MACL,MAAMC,MAAK;AAAA,MACX,QAAQA,MAAK;AAAA,MACb,SAASA,MAAK;AAAA,MACd,UAAUA,MAAK;AAAA,MACf,UAAU,OAAOA,MAAK,QAAQ;AAAA,MAC9B,WAAW,OAAOA,MAAK,SAAS;AAAA,IAClC;AAAA,EACF;AAEA,QAAM,MAAM,MAAM,MAAM,GAAG,kBAAkB,IAAI,KAAK,GAAG,mBAAmB,EAAE,CAAC,MAAM,EAAE,OAAO;AAC9F,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,2BAA2B,IAAI,MAAM,EAAE;AACpE,QAAM,OAAsB,MAAM,IAAI,KAAK;AAE3C,MAAI,KAAK,OAAO;AACd,UAAM,IAAI,MAAM,2BAA2B,KAAK,UAAU,eAAe,EAAE;AAAA,EAC7E;AAEA,SAAO;AAAA,IACL,MAAM,KAAK;AAAA,IACX,QAAQ,KAAK;AAAA,IACb,SAAS,KAAK;AAAA,IACd,UAAU,KAAK;AAAA,IACf,UAAU,OAAO,KAAK,QAAQ;AAAA,IAC9B,WAAW,OAAO,KAAK,SAAS;AAAA,EAClC;AACF;;;ACpDA,SAAS,SAAS,UAAkB,WAAmB,SAAiC;AACtF,QAAM,SAAS,IAAI,gBAAgB;AAAA,IACjC,UAAU,OAAO,QAAQ;AAAA,IACzB,WAAW,OAAO,SAAS;AAAA,IAC3B,UAAU,QAAQ,UAAU,YAAY;AAAA,IACxC,kBAAkB,QAAQ,mBAAmB;AAAA,IAC7C,iBAAiB,QAAQ,iBAAiB;AAAA,IAC1C,eAAe,OAAO,QAAQ,gBAAgB,CAAC;AAAA,IAC/C,gBAAgB,OAAO,QAAQ,iBAAiB,EAAE;AAAA,IAClD,SAAS;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,GAAG;AAAA,IACV,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,GAAG;AAAA,IACV,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,GAAG;AAAA,EACZ,CAAC;AAED,SAAO,0CAA0C,OAAO,SAAS,CAAC;AACpE;AAEA,eAAsB,mBACpB,UAAkC,CAAC,GACL;AAC9B,MAAI;AAEJ,MAAI,OAAO,QAAQ,aAAa,YAAY,OAAO,QAAQ,cAAc,UAAU;AACjF,eAAW;AAAA,MACT,GAAG,QAAQ;AAAA,MACX,UAAU,QAAQ;AAAA,MAClB,WAAW,QAAQ;AAAA,IACrB;AAAA,EACF,OAAO;AACL,eAAW,MAAM,kBAAkB,QAAQ,aAAa,QAAQ,EAAE;AAAA,EACpE;AAEA,QAAM,MAAM,SAAS,SAAS,UAAU,SAAS,WAAW,EAAE,GAAG,SAAS,SAAS,CAAC;AAEpF,QAAM,SAAS,mBAAwC,GAAG;AAC1D,MAAI,OAAQ,QAAO;AAEnB,QAAM,WAAW,MAAM,MAAM,KAAK,EAAE,SAAS,EAAE,gBAAgB,mBAAmB,EAAE,CAAC;AAErF,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI,MAAM,2BAA2B,SAAS,MAAM,IAAI,SAAS,UAAU,EAAE;AAAA,EACrF;AAEA,QAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,MAAI,CAAC,MAAM,WAAW,CAAC,MAAM,UAAU,CAAC,MAAM,OAAO;AACnD,UAAM,IAAI,MAAM,0BAA0B;AAAA,EAC5C;AAEA,QAAM,SAA8B;AAAA,IAClC,UAAU,EAAE,GAAG,UAAU,UAAU,KAAK,YAAY,SAAS,SAAS;AAAA,IACtE,SAAS;AAAA,MACP,MAAM,KAAK,QAAQ;AAAA,MACnB,aAAa,KAAK,MAAM,KAAK,QAAQ,cAAc;AAAA,MACnD,aAAa,KAAK,QAAQ;AAAA,MAC1B,MAAM,eAAe,KAAK,QAAQ,cAAc,KAAK,QAAQ,MAAM;AAAA,MACnE,OAAO,KAAK,QAAQ;AAAA,MACpB,MAAM,KAAK,QAAQ;AAAA,MACnB,SAAS,KAAK,QAAQ;AAAA,MACtB,UAAU,KAAK,QAAQ;AAAA,MACvB,WAAW,KAAK,QAAQ;AAAA,IAC1B;AAAA,IACA,QAAQ,KAAK,OAAO,KAAK,IAAI,CAAC,MAAc,WAAmB;AAAA,MAC7D;AAAA,MACA,aAAa,KAAK,MAAM,KAAK,OAAO,eAAe,KAAK,CAAC;AAAA,MACzD,aAAa,KAAK,OAAO,aAAa,KAAK;AAAA,MAC3C,0BAA0B,KAAK,OAAO,4BAA4B,KAAK;AAAA,MACvE,MAAM,KAAK,OAAO,OAAO,KAAK;AAAA,MAC9B,SAAS,KAAK,OAAO,UAAU,KAAK;AAAA,MACpC,UAAU,KAAK,OAAO,WAAW,KAAK;AAAA,MACtC,MAAM,eAAe,KAAK,OAAO,aAAa,KAAK,CAAC;AAAA,IACtD,EAAE;AAAA,IACF,OAAO,KAAK,MAAM,KAAK,IAAI,CAAC,MAAc,WAAmB;AAAA,MAC3D;AAAA,MACA,KAAK,KAAK,MAAM,KAAK,MAAM,mBAAmB,KAAK,CAAC;AAAA,MACpD,KAAK,KAAK,MAAM,KAAK,MAAM,mBAAmB,KAAK,CAAC;AAAA,MACpD,aAAa,KAAK,MAAM,aAAa,KAAK;AAAA,MAC1C,6BAA6B,KAAK,MAAM,gCAAgC,KAAK;AAAA,MAC7E,kBAAkB,KAAK,MAAM,oBAAoB,KAAK;AAAA,MACtD,cAAc,KAAK,MAAM,qBAAqB,KAAK;AAAA,MACnD,MAAM,eAAe,KAAK,MAAM,aAAa,KAAK,CAAC;AAAA,IACrD,EAAE;AAAA,EACJ;AAEA,sBAAoB,KAAK,MAAM;AAC/B,SAAO;AACT;;;ACjHA,mBAAoC;AAI7B,SAAS,mBAAmB,UAAkC,CAAC,GAAG;AACvE,QAAM,CAAC,MAAM,OAAO,QAAI,uBAAqC,IAAI;AACjE,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAuB,IAAI;AACrD,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAS,IAAI;AAE3C,8BAAU,MAAM;AACd,QAAI,SAAS;AACb,eAAW,IAAI;AACf,aAAS,IAAI;AAEb,uBAAmB,OAAO,EACvB,KAAK,CAAC,WAAW;AAAE,UAAI,OAAQ,SAAQ,MAAM;AAAA,IAAG,CAAC,EACjD,MAAM,CAAC,QAAQ;AAAE,UAAI,OAAQ,UAAS,eAAe,QAAQ,MAAM,IAAI,MAAM,eAAe,CAAC;AAAA,IAAG,CAAC,EACjG,QAAQ,MAAM;AAAE,UAAI,OAAQ,YAAW,KAAK;AAAA,IAAG,CAAC;AAEnD,WAAO,MAAM;AAAE,eAAS;AAAA,IAAO;AAAA,EACjC,GAAG;AAAA,IACD,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ,UAAU;AAAA,EACpB,CAAC;AAED,SAAO,EAAE,MAAM,OAAO,QAAQ;AAChC;;;AC5BI;AAFG,SAAS,SAAS,EAAE,OAAO,UAAU,GAAG,MAAM,GAA8C;AACjG,SACE;AAAA,IAAC;AAAA;AAAA,MACC,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,QAAO;AAAA,MACP,eAAc;AAAA,MACd,gBAAe;AAAA,MACf,MAAM,QAAQ,QAAQ;AAAA,MACtB,eAAa,QAAQ,SAAY;AAAA,MACjC,cAAY;AAAA,MACX,GAAG;AAAA,MAEH;AAAA,gBAAQ,4CAAC,WAAO,iBAAM,IAAW;AAAA,QACjC;AAAA;AAAA;AAAA,EACH;AAEJ;;;ACfuB,IAAAC,sBAAA;AADhB,IAAM,gBAAgB,CAAC,UAC5B,6CAAC,YAAU,GAAG,OAAO,uDAAC,UAAK,GAAE,qXAAoX,GAAE;AAE9Y,IAAM,mBAAmB,CAAC,UAC/B,6CAAC,YAAU,GAAG,OAAO,uDAAC,UAAK,GAAE,0XAAyX,GAAE;AAEnZ,IAAM,eAAe,CAAC,UAC3B,6CAAC,YAAU,GAAG,OAAO,uDAAC,UAAK,GAAE,4WAA2W,GAAE;AAErY,IAAM,gBAAgB,CAAC,UAC5B,6CAAC,YAAU,GAAG,OAAO,uDAAC,UAAK,GAAE,moBAAkoB,GAAE;AAE5pB,IAAM,mBAAmB,CAAC,UAC/B,6CAAC,YAAU,GAAG,OAAO,uDAAC,UAAK,GAAE,+XAA8X,GAAE;AAExZ,IAAM,wBAAwB,CAAC,UACpC,6CAAC,YAAU,GAAG,OAAO,uDAAC,UAAK,GAAE,6WAA4W,GAAE;AAEtY,IAAM,iBAAiB,CAAC,UAC7B,6CAAC,YAAU,GAAG,OAAO,uDAAC,UAAK,GAAE,05EAAy5E,GAAE;AAEn7E,IAAM,gBAAgB,CAAC,UAC5B,6CAAC,YAAU,GAAG,OAAO,uDAAC,UAAK,GAAE,2ZAA0Z,GAAE;AAEpb,IAAM,eAAe,CAAC,UAC3B,6CAAC,YAAU,GAAG,OAAO,uDAAC,UAAK,GAAE,wlBAAulB,GAAE;AAEjnB,IAAM,aAAa,CAAC,UACzB,6CAAC,YAAU,GAAG,OAAO,uDAAC,UAAK,GAAE,yaAAwa,GAAE;AAElc,IAAM,gBAAgB,CAAC,UAC5B,6CAAC,YAAU,GAAG,OAAO,uDAAC,UAAK,GAAE,qNAAoN,GAAE;AAE9O,IAAM,UAAU,CAAC,UACtB,6CAAC,YAAU,GAAG,OAAO,uDAAC,UAAK,GAAE,sKAAqK,GAAE;AAE/L,IAAM,WAAW,CAAC,UACvB,6CAAC,YAAU,GAAG,OAAO,uDAAC,UAAK,GAAE,gGAA+F,GAAE;;;ACPvH,IAAAC,sBAAA;AAjBT,IAAM,UAAoE;AAAA,EACxE,KAAK;AAAA,EACL,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,uBAAuB;AAAA,EACvB,eAAe;AAAA,EACf,cAAc;AAAA,EACd,WAAW;AAAA,EACX,cAAc;AAAA,EACd,cAAc;AAChB;AAEO,SAAS,YAAY,EAAE,WAAW,GAAG,MAAM,GAAgD;AAChG,QAAM,OAAO,QAAQ,SAAS,KAAK;AACnC,SAAO,6CAAC,QAAM,GAAG,OAAO;AAC1B;;;ACnCA,IAAAC,gBAA2C;AAuKlC,IAAAC,sBAAA;AA3JT,IAAM,yBAAwD;AAAA,EAC5D,KAAK;AAAA,EACL,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,IAAI;AACN;AAIA,SAAS,aAAa,SAAe,UAA8B,SAA6C;AAC9G,MAAI;AACF,WAAO,IAAI,KAAK,eAAe,CAAC,GAAG,EAAE,GAAG,SAAS,UAAU,YAAY,OAAU,CAAC,EAAE,OAAO,OAAO;AAAA,EACpG,QAAQ;AACN,WAAO,IAAI,KAAK,eAAe,CAAC,GAAG,OAAO,EAAE,OAAO,OAAO;AAAA,EAC5D;AACF;AAMA,SAAS,WAAW,SAAe,UAA8D;AAC/F,QAAM,UAAsC,EAAE,MAAM,WAAW,QAAQ,WAAW,QAAQ,UAAU;AACpG,QAAM,UAAU,MAAM;AACpB,QAAI;AACF,aAAO,IAAI,KAAK,eAAe,CAAC,GAAG,EAAE,GAAG,SAAS,UAAU,YAAY,OAAU,CAAC;AAAA,IACpF,QAAQ;AACN,aAAO,IAAI,KAAK,eAAe,CAAC,GAAG,OAAO;AAAA,IAC5C;AAAA,EACF,GAAG;AACH,QAAM,QAAQ,OAAO,cAAc,OAAO;AAC1C,QAAM,OAAO,CAAC,SAAuC,MAAM,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI,GAAG;AACzF,QAAM,OAAO,KAAK,MAAM;AACxB,QAAM,SAAS,KAAK,QAAQ;AAC5B,QAAM,SAAS,KAAK,QAAQ;AAC5B,MAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAQ,QAAO,EAAE,MAAM,OAAO,OAAO,OAAO,GAAG,MAAM,GAAG;AACjF,QAAM,YAAY,KAAK,WAAW;AAClC,SAAO,EAAE,MAAM,GAAG,IAAI,IAAI,MAAM,IAAI,MAAM,IAAI,MAAM,GAAG,YAAY,IAAI,SAAS,KAAK,EAAE,GAAG;AAC5F;AAIA,SAAS,UAAU,SAAe,UAAsC;AACtE,QAAM,UAAsC,EAAE,MAAM,WAAW,cAAc,QAAQ;AACrF,MAAI;AACF,UAAM,UAAU,MAAM;AACpB,UAAI;AACF,eAAO,IAAI,KAAK,eAAe,CAAC,GAAG,EAAE,GAAG,SAAS,UAAU,YAAY,OAAU,CAAC;AAAA,MACpF,QAAQ;AACN,eAAO,IAAI,KAAK,eAAe,CAAC,GAAG,OAAO;AAAA,MAC5C;AAAA,IACF,GAAG;AACH,WAAO,OAAO,cAAc,OAAO,EAAE,KAAK,CAAC,SAAS,KAAK,SAAS,cAAc,GAAG,SAAS;AAAA,EAC9F,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKA,SAAS,kBAAkB,MAAoB;AAC7C,QAAM,CAAC,MAAM,OAAO,GAAG,IAAI,KAAK,MAAM,GAAG,EAAE,IAAI,MAAM;AACrD,MAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAK,QAAO,IAAI,KAAK,IAAI;AACjD,SAAO,IAAI,KAAK,MAAM,QAAQ,GAAG,GAAG;AACtC;AAIA,SAAS,iBAAiB,MAAsB;AAC9C,SAAO,kBAAkB,IAAI,EAAE,mBAAmB,CAAC,GAAG,EAAE,SAAS,QAAQ,CAAC;AAC5E;AAIA,SAAS,kBAAkB,KAAmB,oBAAgD;AAC5F,QAAM,UAAoB,CAAC;AAC3B,MAAI,IAAI,qBAAqB,OAAW,SAAQ,KAAK,GAAG,IAAI,iBAAiB,QAAQ,CAAC,CAAC,KAAK;AAC5F,MAAI,IAAI,iBAAiB,OAAW,SAAQ,KAAK,GAAG,IAAI,aAAa,QAAQ,CAAC,CAAC,IAAI,kBAAkB,OAAO;AAC5G,SAAO,QAAQ,SAAS,IAAI,QAAQ,KAAK,QAAK,IAAI;AACpD;AAEA,IAAM,SAAS;AAAA,EACb,MAAM;AAAA,IACJ,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,UAAU;AAAA,EACZ;AAAA,EACA,KAAK,EAAE,SAAS,QAAQ,YAAY,UAAU,KAAK,EAAE;AAAA,EACrD,OAAO,EAAE,SAAS,QAAQ,KAAK,IAAI,WAAW,QAAQ,eAAe,EAAE;AAAA,EACvE,UAAU,EAAE,UAAU,IAAI,WAAW,UAAmB,SAAS,GAAG,cAAc,GAAG,YAAY,UAAU;AAAA,EAC3G,QAAQ,EAAE,SAAS,QAAQ,YAAY,UAAU,gBAAgB,iBAAiB,KAAK,IAAI,SAAS,SAAS,cAAc,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA,EAK/I,aAAa;AAAA,IACX,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,eAAe;AAAA,IACf,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,KAAK;AAAA,IACL,SAAS;AAAA,IACT,cAAc;AAAA,IACd,OAAO;AAAA,IACP,WAAW;AAAA,EACb;AAAA;AAAA;AAAA;AAAA,EAIA,aAAa,EAAE,SAAS,QAAQ,eAAe,UAAU,gBAAgB,iBAAiB,KAAK,IAAI,MAAM,eAAe,UAAU,EAAE;AAAA,EACpI,YAAY,EAAE,SAAS,QAAQ,YAAY,UAAU,gBAAgB,iBAAiB,UAAU,QAAQ,KAAK,YAAY;AAAA,EACzH,aAAa,EAAE,UAAU,IAAI,YAAY,KAAK,YAAY,IAAI;AAAA,EAC9D,eAAe,EAAE,SAAS,QAAQ,YAAY,UAAU,UAAU,QAAQ,KAAK,GAAG;AAAA,EAClF,aAAa,EAAE,UAAU,IAAI,YAAY,KAAK,YAAY,EAAE;AAAA,EAC5D,aAAa,EAAE,UAAU,IAAI,YAAY,KAAK,YAAY,IAAI;AAAA,EAC9D,gBAAgB,EAAE,UAAU,IAAI,YAAY,KAAK,SAAS,KAAK,YAAY,EAAE;AAAA,EAC7E,aAAa,EAAE,UAAU,IAAI,SAAS,KAAK,WAAW,EAAE;AAAA,EACxD,cAAc,EAAE,SAAS,QAAQ,UAAU,QAAQ,KAAK,YAAY,UAAU,IAAI,SAAS,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,EAK/F,UAAU;AAAA,IACR,SAAS;AAAA,IACT,qBAAqB;AAAA,IACrB,cAAc;AAAA,IACd,KAAK;AAAA,IACL,MAAM;AAAA,IACN,UAAU;AAAA,IACV,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,iBAAiB;AAAA,IACjB,UAAU;AAAA,EACZ;AAAA,EACA,aAAa,EAAE,SAAS,QAAQ,eAAe,UAAU,YAAY,UAAU,gBAAgB,UAAU,KAAK,GAAG,WAAW,SAAS;AAAA,EACrI,iBAAiB,EAAE,SAAS,KAAK,YAAY,SAAS;AAAA,EACtD,eAAe,EAAE,YAAY,SAAS;AAAA,EACtC,aAAa,EAAE,SAAS,QAAQ,YAAY,UAAU,KAAK,GAAG,YAAY,SAAS;AAAA,EACnF,YAAY,EAAE,SAAS,eAAe,YAAY,UAAU,QAAQ,WAAW,YAAY,OAAO;AAAA,EAClG,MAAM,EAAE,OAAO,WAAW,YAAY,SAAS;AAAA,EAC/C,IAAI,EAAE,UAAU,IAAI,SAAS,KAAK,WAAW,EAAE;AAAA,EAC/C,MAAM,EAAE,SAAS,QAAQ,eAAe,SAAS;AACnD;AAGA,IAAM,sBAAsB;AAE5B,SAAS,UAAU,EAAE,aAAa,QAAQ,GAAgD;AACxF,MAAI,gBAAgB,UAAa,gBAAgB,QAAQ,eAAe,oBAAqB,QAAO;AACpG,SAAO,6CAAC,UAAK,OAAO,OAAO,MAAO,oBAAU,SAAM,WAAW,MAAM,SAAM,WAAW,UAAS;AAC/F;AAOA,SAAS,sBAAsB,OAAc;AAG3C,QAAM,CAAC,MAAM,OAAO,QAAI,wBAA0B,MAAM,mBAAmB,YAAY;AACvF,+BAAU,MAAM;AACd,YAAQ,MAAM,mBAAmB,YAAY;AAAA,EAC/C,GAAG,CAAC,MAAM,eAAe,CAAC;AAE1B,QAAM,EAAE,MAAM,SAAS,MAAM,IAAI,mBAAmB,EAAE,GAAG,OAAO,iBAAiB,KAAK,CAAC;AAGvF,QAAM,CAAC,KAAK,MAAM,QAAI,wBAAS,MAAM,oBAAI,KAAK,CAAC;AAC/C,+BAAU,MAAM;AACd,UAAM,KAAK,YAAY,MAAM,OAAO,oBAAI,KAAK,CAAC,GAAG,GAAI;AACrD,WAAO,MAAM,cAAc,EAAE;AAAA,EAC/B,GAAG,CAAC,CAAC;AAEL,QAAM,aAAa,MAAM,QAAQ,CAAC,MAAO,MAAM,YAAY,eAAe,SAAU;AAEpF,QAAM,mBAAmB,CAAC,aACxB;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,UAAU;AAAA,MACV,SAAS,CAAC,MAAM;AACd,UAAE,gBAAgB;AAClB,mBAAW;AAAA,MACb;AAAA,MACA,WAAW,CAAC,MAAM;AAChB,YAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK;AACtC,YAAE,eAAe;AACjB,qBAAW;AAAA,QACb;AAAA,MACF;AAAA,MACA,OAAM;AAAA,MACN,cAAY,uCAAuC,SAAS,YAAY,YAAY,YAAY;AAAA,MAChG,OAAO,EAAE,GAAG,OAAO,YAAY,SAAS;AAAA,MAExC;AAAA,qDAAC,UAAK,OAAO,EAAE,YAAY,SAAS,eAAe,MAAM,KAAK,SAAS,SAAS,eAAe,IAAI,KAAK,GAAG,mBAAM;AAAA,QACjH,6CAAC,UAAK,OAAO,EAAE,SAAS,KAAK,QAAQ,QAAQ,GAAG,eAAC;AAAA,QACjD,6CAAC,UAAK,OAAO,EAAE,YAAY,SAAS,YAAY,MAAM,KAAK,SAAS,SAAS,YAAY,IAAI,KAAK,GAAG,mBAAM;AAAA;AAAA;AAAA,EAC7G;AAKF,MAAI,WAAW,CAAC,KAAM,QAAO;AAC7B,MAAI,MAAO,QAAO,8CAAC,SAAI,WAAW,MAAM,WAAW,OAAO,MAAM,OAAO;AAAA;AAAA,IAAQ,MAAM;AAAA,KAAQ;AAC7F,MAAI,CAAC,KAAM,QAAO,6CAAC,SAAI,WAAW,MAAM,WAAW,OAAO,MAAM,OAAO,oCAAsB;AAE7F,QAAM,WAAW,KAAK,UAAU;AAChC,QAAM,cAAc,aAAa,KAAK,UAAU,EAAE,SAAS,SAAS,OAAO,SAAS,KAAK,UAAU,CAAC;AACpG,QAAM,cAAc,aAAa,KAAK,UAAU,EAAE,MAAM,WAAW,QAAQ,WAAW,QAAQ,WAAW,cAAc,QAAQ,CAAC;AAGhI,QAAM,eAAe,WAAW,KAAK,QAAQ;AAC7C,QAAM,cAAc,UAAU,KAAK,QAAQ;AAE3C,MAAI,MAAM,SAAS;AACjB,UAAM,QAAQ,KAAK,MAAM,CAAC;AAC1B,UAAM,qBAAqB,uBAAuB,MAAM,iBAAiB,KAAK;AAG9E,UAAM,eAAe,KAAK,MAAM,MAAM,GAAG,CAAC;AAE1C,WACE,8CAAC,SAAI,WAAW,MAAM,WAAW,OAAO,EAAE,GAAG,OAAO,aAAa,GAAG,MAAM,MAAM,GAC9E;AAAA,oDAAC,SAAI,OAAO,OAAO,aACjB;AAAA,sDAAC,SAAI,OAAO,OAAO,YACjB;AAAA,wDAAC,SAAI,OAAO,OAAO,aAChB;AAAA,iBAAK,UAAU,QAAQ;AAAA,YACvB,KAAK,UAAU,SAAS,KAAK,KAAK,SAAS,MAAM,KAAK;AAAA,aACzD;AAAA,UAEA,8CAAC,SAAI,OAAO,OAAO,eACjB;AAAA,yDAAC,eAAY,WAAW,KAAK,QAAQ,MAAM,OAAO,IAAI,QAAQ,IAAI,OAAM,mBAAkB;AAAA,YAC1F,6CAAC,YAAO,OAAO,OAAO,aAAc,eAAK,QAAQ,aAAY;AAAA,YAC5D,iBAAiB,EAAE;AAAA,aACtB;AAAA,WACF;AAAA,QAEA,8CAAC,SAAI,OAAO,OAAO,YACjB;AAAA,wDAAC,SACC;AAAA,0DAAC,SAAI,OAAO,OAAO,aAChB;AAAA,2BAAa;AAAA,cACb,aAAa,QAAQ,6CAAC,UAAK,OAAO,OAAO,gBAAiB,uBAAa,MAAK;AAAA,eAC/E;AAAA,YACA,8CAAC,SAAI,OAAO,OAAO,aAChB;AAAA;AAAA,cACA,cAAc,SAAM,WAAW,KAAK;AAAA,eACvC;AAAA,aACF;AAAA,UAEA,8CAAC,SAAI,OAAO,OAAO,cAChB;AAAA,qBACC,8CAAC,UAAK,OAAO,OAAO,aAClB;AAAA,2DAAC,eAAY,WAAW,MAAM,MAAM,OAAO,IAAI,QAAQ,IAAI,OAAM,wBAAuB;AAAA,cACvF,MAAM;AAAA,cAAI;AAAA,cAAS,MAAM;AAAA,cAAI;AAAA,cAC9B,6CAAC,aAAU,aAAa,MAAM,6BAA6B,SAAO,MAAC;AAAA,eACrE;AAAA,YAEF,8CAAC,UAAK,OAAO,OAAO,aAClB;AAAA,2DAAC,YAAS,OAAO,IAAI,QAAQ,IAAI,OAAM,cAAa;AAAA,cACnD,KAAK,QAAQ,cAAc,SAAY,GAAG,KAAK,MAAM,KAAK,QAAQ,SAAS,CAAC,IAAI,kBAAkB,KAAK;AAAA,eAC1G;AAAA,aACF;AAAA,WACF;AAAA,SACF;AAAA,MAEC,aAAa,SAAS,KACrB,6CAAC,SAAI,OAAO,OAAO,UAChB,uBAAa,IAAI,CAAC,QACjB,8CAAC,SAAmB,OAAO,OAAO,aAAa,OAAO,kBAAkB,KAAK,kBAAkB,GAC7F;AAAA,qDAAC,UAAK,OAAO,OAAO,iBAAkB,2BAAiB,IAAI,IAAI,GAAE;AAAA,QACjE,6CAAC,eAAY,WAAW,IAAI,MAAM,OAAO,IAAI,QAAQ,IAAI;AAAA,QACzD,8CAAC,UAAK,OAAO,OAAO,eAClB;AAAA,wDAAC,YAAQ;AAAA,gBAAI;AAAA,YAAI;AAAA,aAAK;AAAA,UACtB,8CAAC,UAAK,OAAO,EAAE,SAAS,IAAI,GAAG;AAAA;AAAA,YAAI,IAAI;AAAA,YAAI;AAAA,aAAK;AAAA,WAClD;AAAA,QACC,IAAI,gCAAgC,UAAa,IAAI,8BAA8B,uBAClF,8CAAC,UAAK,OAAO,EAAE,GAAG,OAAO,MAAM,UAAU,GAAG,GAAI;AAAA,cAAI;AAAA,UAA4B;AAAA,WAAC;AAAA,WAR3E,IAAI,IAUd,CACD,GACH;AAAA,OAEJ;AAAA,EAEJ;AAEA,SACE,8CAAC,SAAI,WAAW,MAAM,WAAW,OAAO,EAAE,GAAG,OAAO,MAAM,GAAG,MAAM,MAAM,GACvE;AAAA,kDAAC,SACC;AAAA,oDAAC,QAAG,OAAO,EAAE,QAAQ,GAAG,UAAU,GAAG,GAClC;AAAA,aAAK,UAAU,QAAQ;AAAA,QACvB,KAAK,UAAU,SAAS,KAAK,KAAK,SAAS,MAAM,KAAK;AAAA,SACzD;AAAA,MACA,8CAAC,SAAI,OAAO,OAAO,IAChB;AAAA;AAAA,QAAY;AAAA,QAAW;AAAA,QACvB,WAAW,SAAM,QAAQ,KAAK;AAAA,SACjC;AAAA,MACA,8CAAC,SAAI,OAAO,EAAE,GAAG,OAAO,KAAK,WAAW,EAAE,GACxC;AAAA,qDAAC,eAAY,WAAW,KAAK,QAAQ,MAAM,OAAO,IAAI,QAAQ,IAAI,OAAM,mBAAkB;AAAA,QAC1F,6CAAC,YAAO,OAAO,EAAE,UAAU,GAAG,GAAI,eAAK,QAAQ,aAAY;AAAA,QAC1D,iBAAiB,EAAE;AAAA,SACtB;AAAA,OACF;AAAA,IAEA,8CAAC,SAAI,OAAO,EAAE,WAAW,GAAG,GAC1B;AAAA,mDAAC,QAAG,OAAO,EAAE,QAAQ,aAAa,GAAG,wBAAU;AAAA,MAC/C,6CAAC,SAAI,OAAO,OAAO,OAChB,eAAK,OAAO,MAAM,GAAG,MAAM,iBAAiB,EAAE,EAAE,IAAI,CAAC,SACpD,8CAAC,SAAoB,OAAO,OAAO,UACjC;AAAA,qDAAC,SAAI,OAAO,EAAE,UAAU,GAAG,GAAI,cAAI,KAAK,KAAK,IAAI,EAAE,mBAAmB,CAAC,GAAG,EAAE,MAAM,UAAU,CAAC,GAAE;AAAA,QAC/F,6CAAC,eAAY,WAAW,KAAK,MAAM,OAAO,IAAI,QAAQ,IAAI,OAAO,EAAE,QAAQ,WAAW,GAAG;AAAA,QACzF,8CAAC,SAAK;AAAA,eAAK;AAAA,UAAY;AAAA,WAAK;AAAA,QAC3B,KAAK,6BAA6B,UAAa,KAAK,2BAA2B,KAC9E,8CAAC,SAAI,OAAO,EAAE,GAAG,OAAO,MAAM,UAAU,GAAG,GAAI;AAAA,eAAK;AAAA,UAAyB;AAAA,WAAC;AAAA,WALxE,KAAK,IAOf,CACD,GACH;AAAA,OACF;AAAA,IAEA,8CAAC,SAAI,OAAO,EAAE,WAAW,GAAG,GAC1B;AAAA,mDAAC,QAAG,OAAO,EAAE,QAAQ,aAAa,GAAG,uBAAS;AAAA,MAC9C,6CAAC,SACE,eAAK,MAAM,IAAI,CAAC,QACf,8CAAC,SAAmB,OAAO,OAAO,QAChC;AAAA,qDAAC,SAAI,OAAO,EAAE,OAAO,IAAI,GAAI,cAAI,KAAK,IAAI,IAAI,EAAE,mBAAmB,CAAC,GAAG,EAAE,SAAS,SAAS,OAAO,SAAS,KAAK,UAAU,CAAC,GAAE;AAAA,QAC7H,6CAAC,eAAY,WAAW,IAAI,MAAM,OAAO,IAAI,QAAQ,IAAI;AAAA,QACzD,8CAAC,SACE;AAAA,cAAI;AAAA,UAAI;AAAA,UAAS,IAAI;AAAA,UAAI;AAAA,UACzB,IAAI,gCAAgC,UAAa,IAAI,8BAA8B,uBAClF,8CAAC,UAAK,OAAO,OAAO,MAAM;AAAA;AAAA,YAAW,IAAI;AAAA,YAA4B;AAAA,aAAC;AAAA,WAE1E;AAAA,WARQ,IAAI,IASd,CACD,GACH;AAAA,OACF;AAAA,KACF;AAEJ;AAMO,SAAS,gBAAgB,OAAc;AAC5C,QAAM,EAAE,WAAW,GAAG,KAAK,IAAI;AAE/B,MAAI,aAAa,UAAU,SAAS,GAAG;AACrC,UAAM,MAAM,MAAM,UAAU,IAAI;AAChC,WACE,6CAAC,SAAI,OAAO,EAAE,GAAG,OAAO,MAAM,IAAI,GAC/B,oBAAU,IAAI,CAAC,KAAK,UACnB;AAAA,MAAC;AAAA;AAAA,QAEE,GAAG;AAAA,QACJ,UAAU,OAAO,IAAI,aAAa,WAAW,IAAI,WAAW;AAAA,QAC5D,WAAW,OAAO,IAAI,cAAc,WAAW,IAAI,YAAY;AAAA,QAC/D,UAAU,EAAE,MAAM,IAAI,OAAO,UAAU,IAAI,SAAS;AAAA;AAAA,MAJ/C,GAAG,IAAI,SAAS,EAAE,IAAI,IAAI,YAAY,EAAE,IAAI,IAAI,aAAa,EAAE,IAAI,KAAK;AAAA,IAK/E,CACD,GACH;AAAA,EAEJ;AAEA,SAAO,6CAAC,yBAAuB,GAAG,MAAM;AAC1C;","names":["res","data","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime"]}
package/dist/index.js CHANGED
@@ -288,13 +288,6 @@ var WIND_SPEED_UNIT_LABELS = {
288
288
  ms: "m/s",
289
289
  kn: "kn"
290
290
  };
291
- function closestHour(hourly, targetTime) {
292
- return hourly.reduce((closest, hour) => {
293
- const diff = Math.abs(new Date(hour.time).getTime() - targetTime.getTime());
294
- const closestDiff = closest ? Math.abs(new Date(closest.time).getTime() - targetTime.getTime()) : Infinity;
295
- return diff < closestDiff ? hour : closest;
296
- }, void 0);
297
- }
298
291
  function formatInZone(instant, timezone, options) {
299
292
  try {
300
293
  return new Intl.DateTimeFormat([], { ...options, timeZone: timezone || void 0 }).format(instant);
@@ -363,53 +356,62 @@ var styles = {
363
356
  hours: { display: "flex", gap: 12, overflowX: "auto", paddingBottom: 4 },
364
357
  hourCard: { minWidth: 72, textAlign: "center", padding: 8, borderRadius: 8, background: "#f9fafb" },
365
358
  dayRow: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 10, padding: "8px 0", borderBottom: "1px solid #f3f4f6" },
366
- // Card-sized: a fixed maximum width with generous, even padding, so the
367
- // widget reads as a self-contained tile wherever it is dropped in.
359
+ // Fluid: fills whatever container it is dropped into (e.g. the full
360
+ // sidebar/column width on desktop), with generous, even padding. Current
361
+ // conditions sit on the left and the upcoming days on the right; the two
362
+ // halves wrap into a stack when the container gets narrow.
368
363
  compactRoot: {
369
364
  fontFamily: "system-ui, sans-serif",
370
365
  display: "flex",
371
- flexDirection: "column",
372
- gap: 14,
373
- padding: 18,
366
+ flexDirection: "row",
367
+ flexWrap: "wrap",
368
+ alignItems: "stretch",
369
+ gap: 24,
370
+ padding: "20px 24px",
374
371
  borderRadius: 16,
375
372
  width: "100%",
376
- maxWidth: 340,
377
373
  boxSizing: "border-box"
378
374
  },
379
- // Current conditions stack down the card; the upcoming days sit in a row
380
- // along the bottom.
381
- compactBody: { display: "flex", flexDirection: "column", gap: 10 },
375
+ // Current conditions form two spread-out rows down the left side of the
376
+ // card (rather than five stacked lines), so the card stays short and each
377
+ // row uses the full available width.
378
+ compactBody: { display: "flex", flexDirection: "column", justifyContent: "space-between", gap: 20, flex: "1.6 1 240px", minWidth: 0 },
379
+ compactRow: { display: "flex", alignItems: "center", justifyContent: "space-between", flexWrap: "wrap", gap: "12px 16px" },
382
380
  compactCity: { fontSize: 15, fontWeight: 700, lineHeight: 1.2 },
383
- compactHeader: { display: "flex", alignItems: "center", flexWrap: "wrap", gap: 8 },
381
+ compactHeader: { display: "flex", alignItems: "center", flexWrap: "wrap", gap: 12 },
384
382
  compactTemp: { fontSize: 32, fontWeight: 600, lineHeight: 1 },
385
- // The short-term outlook ("59 deg in 6h") trails the current temperature.
386
- compactOutlook: { display: "flex", alignItems: "center", gap: 4, fontSize: 12, opacity: 0.75, whiteSpace: "nowrap" },
387
383
  compactTime: { fontSize: 26, fontWeight: 600, lineHeight: 1.1 },
388
- compactSeconds: { fontSize: 13, fontWeight: 500, opacity: 0.6 },
389
- compactDate: { fontSize: 11, opacity: 0.7, marginTop: 2 },
390
- compactStats: { display: "flex", flexWrap: "wrap", gap: 14, fontSize: 12, opacity: 0.9 },
391
- // Three equal columns along the bottom edge, each stacking weekday / icon /
392
- // high-low so the row stays readable at card width.
384
+ compactSeconds: { fontSize: 13, fontWeight: 500, opacity: 0.6, marginLeft: 2 },
385
+ compactDate: { fontSize: 11, opacity: 0.7, marginTop: 5 },
386
+ compactStats: { display: "flex", flexWrap: "wrap", gap: "8px 18px", fontSize: 12, opacity: 0.9 },
387
+ // Three equal columns on the right side of the card, each stacking
388
+ // weekday / icon / high-low, vertically centered against the current
389
+ // conditions and nudged over behind a divider so the two halves read as
390
+ // separate sections.
393
391
  upcoming: {
394
392
  display: "grid",
395
393
  gridTemplateColumns: "repeat(3, 1fr)",
396
- gap: 8,
397
- paddingTop: 12,
398
- borderTop: "1px solid currentColor",
399
- borderTopColor: "rgba(128,128,128,0.25)",
400
- fontSize: 12
394
+ alignContent: "center",
395
+ gap: 14,
396
+ flex: "1 1 190px",
397
+ minWidth: 0,
398
+ paddingLeft: 22,
399
+ borderLeft: "1px solid currentColor",
400
+ borderLeftColor: "rgba(128,128,128,0.25)",
401
+ fontSize: 11
401
402
  },
402
- upcomingDay: { display: "flex", flexDirection: "column", alignItems: "center", gap: 4, textAlign: "center" },
403
+ upcomingDay: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 5, textAlign: "center" },
403
404
  upcomingDayName: { opacity: 0.7, whiteSpace: "nowrap" },
404
405
  upcomingTemps: { whiteSpace: "nowrap" },
405
- compactStat: { display: "flex", alignItems: "center", gap: 4, whiteSpace: "nowrap" },
406
+ compactStat: { display: "flex", alignItems: "center", gap: 6, whiteSpace: "nowrap" },
406
407
  unitSwitch: { display: "inline-flex", alignItems: "center", cursor: "pointer", userSelect: "none" },
407
408
  rain: { color: "#2563eb", whiteSpace: "nowrap" },
408
409
  tz: { fontSize: 12, opacity: 0.7, marginTop: 2 },
409
410
  list: { display: "flex", flexDirection: "column" }
410
411
  };
412
+ var RAIN_RISK_THRESHOLD = 2;
411
413
  function RainBadge({ probability, compact }) {
412
- if (probability === void 0 || probability === null || probability <= 0) return null;
414
+ if (probability === void 0 || probability === null || probability <= RAIN_RISK_THRESHOLD) return null;
413
415
  return /* @__PURE__ */ jsx4("span", { style: styles.rain, children: compact ? ` \xB7 ${probability}%` : ` \xB7 ${probability}% rain` });
414
416
  }
415
417
  function SingleWeatherForecast(props) {
@@ -444,7 +446,7 @@ function SingleWeatherForecast(props) {
444
446
  style: { ...styles.unitSwitch, fontSize },
445
447
  children: [
446
448
  /* @__PURE__ */ jsx4("span", { style: { fontWeight: unit === "fahrenheit" ? 700 : 400, opacity: unit === "fahrenheit" ? 1 : 0.45 }, children: "\xB0F" }),
447
- /* @__PURE__ */ jsx4("span", { style: { opacity: 0.4, margin: "0 3px" }, children: "|" }),
449
+ /* @__PURE__ */ jsx4("span", { style: { opacity: 0.4, margin: "0 4px" }, children: "|" }),
448
450
  /* @__PURE__ */ jsx4("span", { style: { fontWeight: unit === "celsius" ? 700 : 400, opacity: unit === "celsius" ? 1 : 0.45 }, children: "\xB0C" })
449
451
  ]
450
452
  }
@@ -461,56 +463,52 @@ function SingleWeatherForecast(props) {
461
463
  const currentClock = clockParts(now, timezone);
462
464
  const currentZone = zoneLabel(now, timezone);
463
465
  if (props.compact) {
464
- const forecastBase = new Date(data.current.time);
465
466
  const today = data.daily[0];
466
- const in6Hours = closestHour(data.hourly, new Date(forecastBase.getTime() + 6 * 60 * 60 * 1e3));
467
467
  const windSpeedUnitLabel = WIND_SPEED_UNIT_LABELS[props.windSpeedUnit ?? "mph"];
468
468
  const upcomingDays = data.daily.slice(1, 4);
469
469
  return /* @__PURE__ */ jsxs2("div", { className: props.className, style: { ...styles.compactRoot, ...props.style }, children: [
470
470
  /* @__PURE__ */ jsxs2("div", { style: styles.compactBody, children: [
471
- /* @__PURE__ */ jsxs2("div", { style: styles.compactCity, children: [
472
- data.location?.city || "Current location",
473
- data.location?.region ? `, ${data.location.region}` : ""
474
- ] }),
475
- /* @__PURE__ */ jsxs2("div", { style: styles.compactHeader, children: [
476
- /* @__PURE__ */ jsx4(WeatherIcon, { condition: data.current.icon, width: 30, height: 30, title: "Current weather" }),
477
- /* @__PURE__ */ jsx4("strong", { style: styles.compactTemp, children: data.current.temperature }),
478
- renderUnitSwitch(13),
479
- in6Hours && /* @__PURE__ */ jsxs2("span", { style: styles.compactOutlook, children: [
480
- /* @__PURE__ */ jsx4(WeatherIcon, { condition: in6Hours.icon, width: 14, height: 14, title: "In 6 hours" }),
481
- in6Hours.temperature,
482
- "\xB0 6h",
483
- /* @__PURE__ */ jsx4(RainBadge, { probability: in6Hours.precipitationProbability, compact: true })
484
- ] })
485
- ] }),
486
- /* @__PURE__ */ jsxs2("div", { children: [
487
- /* @__PURE__ */ jsxs2("div", { style: styles.compactTime, children: [
488
- currentClock.main,
489
- currentClock.tail && /* @__PURE__ */ jsx4("span", { style: styles.compactSeconds, children: currentClock.tail })
471
+ /* @__PURE__ */ jsxs2("div", { style: styles.compactRow, children: [
472
+ /* @__PURE__ */ jsxs2("div", { style: styles.compactCity, children: [
473
+ data.location?.city || "Current location",
474
+ data.location?.region ? `, ${data.location.region}` : ""
490
475
  ] }),
491
- /* @__PURE__ */ jsxs2("div", { style: styles.compactDate, children: [
492
- currentDate,
493
- currentZone ? ` \xB7 ${currentZone}` : ""
476
+ /* @__PURE__ */ jsxs2("div", { style: styles.compactHeader, children: [
477
+ /* @__PURE__ */ jsx4(WeatherIcon, { condition: data.current.icon, width: 30, height: 30, title: "Current weather" }),
478
+ /* @__PURE__ */ jsx4("strong", { style: styles.compactTemp, children: data.current.temperature }),
479
+ renderUnitSwitch(13)
494
480
  ] })
495
481
  ] }),
496
- /* @__PURE__ */ jsxs2("div", { style: styles.compactStats, children: [
497
- today && /* @__PURE__ */ jsxs2("span", { style: styles.compactStat, children: [
498
- /* @__PURE__ */ jsx4(WeatherIcon, { condition: today.icon, width: 16, height: 16, title: "Today's high and low" }),
499
- today.max,
500
- "\xB0 / ",
501
- today.min,
502
- "\xB0",
503
- /* @__PURE__ */ jsx4(RainBadge, { probability: today.precipitationProbabilityMax, compact: true })
482
+ /* @__PURE__ */ jsxs2("div", { style: styles.compactRow, children: [
483
+ /* @__PURE__ */ jsxs2("div", { children: [
484
+ /* @__PURE__ */ jsxs2("div", { style: styles.compactTime, children: [
485
+ currentClock.main,
486
+ currentClock.tail && /* @__PURE__ */ jsx4("span", { style: styles.compactSeconds, children: currentClock.tail })
487
+ ] }),
488
+ /* @__PURE__ */ jsxs2("div", { style: styles.compactDate, children: [
489
+ currentDate,
490
+ currentZone ? ` \xB7 ${currentZone}` : ""
491
+ ] })
504
492
  ] }),
505
- /* @__PURE__ */ jsxs2("span", { style: styles.compactStat, children: [
506
- /* @__PURE__ */ jsx4(WindIcon, { width: 16, height: 16, title: "Wind speed" }),
507
- data.current.windSpeed !== void 0 ? `${Math.round(data.current.windSpeed)} ${windSpeedUnitLabel}` : "\u2014"
493
+ /* @__PURE__ */ jsxs2("div", { style: styles.compactStats, children: [
494
+ today && /* @__PURE__ */ jsxs2("span", { style: styles.compactStat, children: [
495
+ /* @__PURE__ */ jsx4(WeatherIcon, { condition: today.icon, width: 16, height: 16, title: "Today's high and low" }),
496
+ today.max,
497
+ "\xB0 / ",
498
+ today.min,
499
+ "\xB0",
500
+ /* @__PURE__ */ jsx4(RainBadge, { probability: today.precipitationProbabilityMax, compact: true })
501
+ ] }),
502
+ /* @__PURE__ */ jsxs2("span", { style: styles.compactStat, children: [
503
+ /* @__PURE__ */ jsx4(WindIcon, { width: 16, height: 16, title: "Wind speed" }),
504
+ data.current.windSpeed !== void 0 ? `${Math.round(data.current.windSpeed)} ${windSpeedUnitLabel}` : "\u2014"
505
+ ] })
508
506
  ] })
509
507
  ] })
510
508
  ] }),
511
509
  upcomingDays.length > 0 && /* @__PURE__ */ jsx4("div", { style: styles.upcoming, children: upcomingDays.map((day) => /* @__PURE__ */ jsxs2("div", { style: styles.upcomingDay, title: upcomingDayDetail(day, windSpeedUnitLabel), children: [
512
510
  /* @__PURE__ */ jsx4("span", { style: styles.upcomingDayName, children: upcomingDayLabel(day.date) }),
513
- /* @__PURE__ */ jsx4(WeatherIcon, { condition: day.icon, width: 18, height: 18 }),
511
+ /* @__PURE__ */ jsx4(WeatherIcon, { condition: day.icon, width: 14, height: 14 }),
514
512
  /* @__PURE__ */ jsxs2("span", { style: styles.upcomingTemps, children: [
515
513
  /* @__PURE__ */ jsxs2("strong", { children: [
516
514
  day.max,
@@ -522,7 +520,7 @@ function SingleWeatherForecast(props) {
522
520
  "\xB0"
523
521
  ] })
524
522
  ] }),
525
- day.precipitationProbabilityMax !== void 0 && day.precipitationProbabilityMax > 0 && /* @__PURE__ */ jsxs2("span", { style: { ...styles.rain, fontSize: 11 }, children: [
523
+ day.precipitationProbabilityMax !== void 0 && day.precipitationProbabilityMax > RAIN_RISK_THRESHOLD && /* @__PURE__ */ jsxs2("span", { style: { ...styles.rain, fontSize: 10 }, children: [
526
524
  day.precipitationProbabilityMax,
527
525
  "%"
528
526
  ] })
@@ -572,7 +570,7 @@ function SingleWeatherForecast(props) {
572
570
  "\xB0 / ",
573
571
  day.max,
574
572
  "\xB0",
575
- day.precipitationProbabilityMax !== void 0 && day.precipitationProbabilityMax > 0 && /* @__PURE__ */ jsxs2("span", { style: styles.rain, children: [
573
+ day.precipitationProbabilityMax !== void 0 && day.precipitationProbabilityMax > RAIN_RISK_THRESHOLD && /* @__PURE__ */ jsxs2("span", { style: styles.rain, children: [
576
574
  " \xB7 ",
577
575
  day.precipitationProbabilityMax,
578
576
  "%"
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/weatherCodes.ts","../src/lib/cache.ts","../src/api/geolocation.ts","../src/api/forecast.ts","../src/hooks/useWeatherForecast.ts","../src/icons/BaseIcon.tsx","../src/icons/weather-icons.tsx","../src/icons/WeatherIcon.tsx","../src/components/WeatherForecast.tsx"],"sourcesContent":["import type { WeatherCondition } from './types';\n\nexport function getWeatherIcon(code: number, isDay = 1): WeatherCondition {\n if (code === 0) return 'sun';\n if ([1, 2].includes(code)) return isDay ? 'cloud-sun' : 'clouds';\n if (code === 3) return 'clouds';\n if ([45, 48].includes(code)) return 'cloud-fog';\n if ([51, 53, 55, 56, 57].includes(code)) return 'cloud-drizzle';\n if ([61, 63].includes(code)) return 'cloud-showers';\n if ([65, 66, 67, 80, 81, 82].includes(code)) return 'cloud-showers-heavy';\n if ([71, 73, 75, 77, 85, 86].includes(code)) return 'cloud-snow';\n if (code === 95) return 'cloud-bolt';\n if ([96, 99].includes(code)) return 'cloud-hail';\n return 'clouds';\n}\n","const CACHE_PREFIX = 'weather-forecast-cache:';\nconst CACHE_TTL_MS = 30 * 60 * 1000;\n\ntype CacheEntry<T> = {\n timestamp: number;\n data: T;\n};\n\nfunction getStorage(): Storage | null {\n if (typeof window === 'undefined' || !window.localStorage) return null;\n return window.localStorage;\n}\n\n/** Returns the cached value for `key` if it was written within the last 30 minutes. */\nexport function readCachedForecast<T>(key: string): T | null {\n const storage = getStorage();\n if (!storage) return null;\n\n try {\n const raw = storage.getItem(CACHE_PREFIX + key);\n if (!raw) return null;\n\n const entry = JSON.parse(raw) as CacheEntry<T>;\n if (Date.now() - entry.timestamp > CACHE_TTL_MS) {\n storage.removeItem(CACHE_PREFIX + key);\n return null;\n }\n\n return entry.data;\n } catch {\n return null;\n }\n}\n\nexport function writeCachedForecast<T>(key: string, data: T): void {\n const storage = getStorage();\n if (!storage) return;\n\n try {\n const entry: CacheEntry<T> = { timestamp: Date.now(), data };\n storage.setItem(CACHE_PREFIX + key, JSON.stringify(entry));\n } catch {\n // Storage full or unavailable (e.g. private browsing) — safe to ignore,\n // the next call will simply hit the network again.\n }\n}\n\n/** Clears all cached forecasts. Mainly useful for tests/debugging. */\nexport function clearWeatherForecastCache(): void {\n const storage = getStorage();\n if (!storage) return;\n\n for (let i = storage.length - 1; i >= 0; i--) {\n const key = storage.key(i);\n if (key?.startsWith(CACHE_PREFIX)) storage.removeItem(key);\n }\n}\n","import type { WeatherLocation } from '../types';\n\nconst DEFAULT_IP_API_URL = 'https://ipapi.co';\n\ntype IpApiResponse = {\n city?: string;\n region?: string;\n country_name?: string;\n timezone?: string;\n latitude?: number;\n longitude?: number;\n error?: boolean;\n reason?: string;\n};\n\ntype GeoWorkerResponse = {\n city?: string;\n region?: string;\n country?: string;\n timezone?: string;\n latitude?: number | string;\n longitude?: number | string;\n};\n\nexport async function getClientLocation(geoEndpoint?: string, ip?: string): Promise<WeatherLocation> {\n if (geoEndpoint) {\n const url = ip ? `${geoEndpoint}?ip=${encodeURIComponent(ip)}` : geoEndpoint;\n const res = await fetch(url);\n if (!res.ok) throw new Error(`Geolocation worker lookup failed: ${res.status}`);\n const data: GeoWorkerResponse = await res.json();\n\n return {\n city: data.city,\n region: data.region,\n country: data.country,\n timezone: data.timezone,\n latitude: Number(data.latitude),\n longitude: Number(data.longitude),\n };\n }\n\n const res = await fetch(`${DEFAULT_IP_API_URL}/${ip ? `${encodeURIComponent(ip)}/` : ''}json/`);\n if (!res.ok) throw new Error(`ipapi.co lookup failed: ${res.status}`);\n const data: IpApiResponse = await res.json();\n\n if (data.error) {\n throw new Error(`ipapi.co lookup failed: ${data.reason ?? 'unknown error'}`);\n }\n\n return {\n city: data.city,\n region: data.region,\n country: data.country_name,\n timezone: data.timezone,\n latitude: Number(data.latitude),\n longitude: Number(data.longitude),\n };\n}\n","import type { WeatherForecastData, WeatherForecastOptions, WeatherLocation } from '../types';\nimport { getWeatherIcon } from '../weatherCodes';\nimport { getClientLocation } from './geolocation';\nimport { readCachedForecast, writeCachedForecast } from '../lib/cache';\n\nfunction buildUrl(latitude: number, longitude: number, options: WeatherForecastOptions) {\n const params = new URLSearchParams({\n latitude: String(latitude),\n longitude: String(longitude),\n timezone: options.location?.timezone || 'auto',\n temperature_unit: options.temperatureUnit || 'fahrenheit',\n wind_speed_unit: options.windSpeedUnit || 'mph',\n forecast_days: String(options.forecastDays ?? 5),\n forecast_hours: String(options.forecastHours ?? 24),\n current: [\n 'temperature_2m',\n 'weather_code',\n 'is_day',\n 'rain',\n 'showers',\n 'snowfall',\n 'wind_speed_10m',\n ].join(','),\n hourly: [\n 'temperature_2m',\n 'weather_code',\n 'precipitation_probability',\n 'rain',\n 'showers',\n 'snowfall',\n ].join(','),\n daily: [\n 'temperature_2m_max',\n 'temperature_2m_min',\n 'weather_code',\n 'precipitation_probability_max',\n 'precipitation_sum',\n 'wind_speed_10m_max',\n ].join(','),\n });\n\n return `https://api.open-meteo.com/v1/forecast?${params.toString()}`;\n}\n\nexport async function getWeatherForecast(\n options: WeatherForecastOptions = {}\n): Promise<WeatherForecastData> {\n let location: WeatherLocation;\n\n if (typeof options.latitude === 'number' && typeof options.longitude === 'number') {\n location = {\n ...options.location,\n latitude: options.latitude,\n longitude: options.longitude,\n };\n } else {\n location = await getClientLocation(options.geoEndpoint, options.ip);\n }\n\n const url = buildUrl(location.latitude, location.longitude, { ...options, location });\n\n const cached = readCachedForecast<WeatherForecastData>(url);\n if (cached) return cached;\n\n const response = await fetch(url, { headers: { 'Content-Type': 'application/json' } });\n\n if (!response.ok) {\n throw new Error(`Weather request failed: ${response.status} ${response.statusText}`);\n }\n\n const data = await response.json();\n\n if (!data?.current || !data?.hourly || !data?.daily) {\n throw new Error('Invalid weather response');\n }\n\n const result: WeatherForecastData = {\n location: { ...location, timezone: data.timezone || location.timezone },\n current: {\n time: data.current.time,\n temperature: Math.round(data.current.temperature_2m),\n weatherCode: data.current.weather_code,\n icon: getWeatherIcon(data.current.weather_code, data.current.is_day),\n isDay: data.current.is_day,\n rain: data.current.rain,\n showers: data.current.showers,\n snowfall: data.current.snowfall,\n windSpeed: data.current.wind_speed_10m,\n },\n hourly: data.hourly.time.map((time: string, index: number) => ({\n time,\n temperature: Math.round(data.hourly.temperature_2m[index]),\n weatherCode: data.hourly.weather_code[index],\n precipitationProbability: data.hourly.precipitation_probability?.[index],\n rain: data.hourly.rain?.[index],\n showers: data.hourly.showers?.[index],\n snowfall: data.hourly.snowfall?.[index],\n icon: getWeatherIcon(data.hourly.weather_code[index]),\n })),\n daily: data.daily.time.map((date: string, index: number) => ({\n date,\n min: Math.round(data.daily.temperature_2m_min[index]),\n max: Math.round(data.daily.temperature_2m_max[index]),\n weatherCode: data.daily.weather_code[index],\n precipitationProbabilityMax: data.daily.precipitation_probability_max?.[index],\n precipitationSum: data.daily.precipitation_sum?.[index],\n windSpeedMax: data.daily.wind_speed_10m_max?.[index],\n icon: getWeatherIcon(data.daily.weather_code[index]),\n })),\n };\n\n writeCachedForecast(url, result);\n return result;\n}\n","import { useEffect, useState } from 'react';\nimport type { WeatherForecastData, WeatherForecastOptions } from '../types';\nimport { getWeatherForecast } from '../api/forecast';\n\nexport function useWeatherForecast(options: WeatherForecastOptions = {}) {\n const [data, setData] = useState<WeatherForecastData | null>(null);\n const [error, setError] = useState<Error | null>(null);\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n let active = true;\n setLoading(true);\n setError(null);\n\n getWeatherForecast(options)\n .then((result) => { if (active) setData(result); })\n .catch((err) => { if (active) setError(err instanceof Error ? err : new Error('Unknown error')); })\n .finally(() => { if (active) setLoading(false); });\n\n return () => { active = false; };\n }, [\n options.latitude,\n options.longitude,\n options.geoEndpoint,\n options.ip,\n options.forecastDays,\n options.forecastHours,\n options.temperatureUnit,\n options.windSpeedUnit,\n options.location?.timezone,\n ]);\n\n return { data, error, loading };\n}\n","import React from 'react';\nimport type { IconProps } from '../types';\n\nexport function BaseIcon({ title, children, ...props }: IconProps & { children: React.ReactNode }) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n role={title ? 'img' : 'presentation'}\n aria-hidden={title ? undefined : true}\n aria-label={title}\n {...props}\n >\n {title ? <title>{title}</title> : null}\n {children}\n </svg>\n );\n}\n","import React from 'react';\nimport type { IconProps } from '../types';\nimport { BaseIcon } from './BaseIcon';\n\nexport const CloudBoltIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M7 18a4.815 4.815 0 0 1-3.327-1.318A4.403 4.403 0 0 1 2.295 13.5c0-1.194.496-2.338 1.378-3.182A4.815 4.815 0 0 1 7 9c.295-1.313 1.157-2.466 2.397-3.207a5.971 5.971 0 0 1 2.025-.749 6.224 6.224 0 0 1 2.19.006c.721.131 1.408.39 2.02.76a5.36 5.36 0 0 1 1.543 1.397c.407.552.69 1.172.831 1.823.142.65.14 1.32-.006 1.97h1a3.5 3.5 0 0 1 0 7h-1m-4.75-5L11 17.5h3.375l-2.25 4.5\" /></BaseIcon>\n);\nexport const CloudDrizzleIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M12 18v1m-3-4v1m6-1v1m2 3v1M7 19v1m-3-3.034a4.63 4.63 0 0 1-.327-.284A4.403 4.403 0 0 1 2.295 13.5c0-1.194.496-2.338 1.378-3.182A4.815 4.815 0 0 1 7 9c.295-1.313 1.157-2.466 2.397-3.207a5.971 5.971 0 0 1 2.025-.749 6.224 6.224 0 0 1 2.19.006c.721.131 1.408.39 2.02.76a5.36 5.36 0 0 1 1.543 1.397c.407.552.69 1.172.831 1.823.142.65.14 1.32-.006 1.97h1a3.5 3.5 0 0 1 1.5 6.662\" /></BaseIcon>\n);\nexport const CloudFogIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M4 16.966a4.63 4.63 0 0 1-.327-.284A4.403 4.403 0 0 1 2.295 13.5c0-1.194.496-2.338 1.378-3.182A4.815 4.815 0 0 1 7 9c.295-1.313 1.157-2.466 2.397-3.207a5.971 5.971 0 0 1 2.025-.749 6.224 6.224 0 0 1 2.19.006c.721.131 1.408.39 2.02.76a5.36 5.36 0 0 1 1.543 1.397c.407.552.69 1.172.831 1.823.142.65.14 1.32-.006 1.97h1a3.5 3.5 0 0 1 1.5 6.662M5 21h7m4 0h3M8 17h8\" /></BaseIcon>\n);\nexport const CloudHailIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M8 17.45v.1m-1.5 3.9v.1m10.5-4.1v.1m-1.5 3.9v.1m-3-4.1v.1m-1.5 3.9v.1m10.475-4.575A3.5 3.5 0 0 0 19 11h-1c.146-.65.148-1.32.006-1.97a4.784 4.784 0 0 0-.831-1.823 5.36 5.36 0 0 0-1.544-1.398 5.964 5.964 0 0 0-2.02-.759 6.224 6.224 0 0 0-2.19-.006 5.971 5.971 0 0 0-2.024.749C8.157 6.533 7.295 7.687 7 9a4.815 4.815 0 0 0-3.327 1.318A4.403 4.403 0 0 0 2.295 13.5c0 1.194.496 2.338 1.378 3.182.105.1.214.195.327.284m4.5.534a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Zm-1.5 4a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Zm10.5-4a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Zm-1.5 4a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Zm-3-4a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Zm-1.5 4a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Z\" /></BaseIcon>\n);\nexport const CloudShowersIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M4 16.966a4.63 4.63 0 0 1-.327-.284A4.403 4.403 0 0 1 2.295 13.5c0-1.194.496-2.338 1.378-3.182A4.815 4.815 0 0 1 7 9c.295-1.313 1.157-2.466 2.397-3.207a5.971 5.971 0 0 1 2.025-.749 6.224 6.224 0 0 1 2.19.006c.721.131 1.408.39 2.02.76a5.36 5.36 0 0 1 1.543 1.397c.407.552.69 1.172.831 1.823.142.65.14 1.32-.006 1.97h1a3.5 3.5 0 0 1 1.5 6.662M12 13v2m0 3v2m4-5v2m0 3v2m-8-7v2m0 3v2\" /></BaseIcon>\n);\nexport const CloudShowersHeavyIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M4 16.966a4.63 4.63 0 0 1-.327-.284A4.403 4.403 0 0 1 2.295 13.5c0-1.194.496-2.338 1.378-3.182A4.815 4.815 0 0 1 7 9c.295-1.313 1.157-2.466 2.397-3.207a5.971 5.971 0 0 1 2.025-.749 6.224 6.224 0 0 1 2.19.006c.721.131 1.408.39 2.02.76a5.36 5.36 0 0 1 1.543 1.397c.407.552.69 1.172.831 1.823.142.65.14 1.32-.006 1.97h1a3.5 3.5 0 0 1 1.5 6.662M16 15v6m-8-6v6m4-8v6\" /></BaseIcon>\n);\nexport const CloudSleetIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M13.5 17.45a1 1 0 1 0-2 0h2Zm-2 .1a1 1 0 1 0 2 0h-2Zm.5 3.9a1 1 0 1 0-2 0h2Zm-2 .1a1 1 0 1 0 2 0h-2Zm-6.327-4.868.692-.723-.692.723ZM7 9v1a1 1 0 0 0 .976-.781L7 9Zm2.397-3.207.512.858-.512-.858ZM18 11l-.976-.219A1 1 0 0 0 18 12v-1Zm2.768 5.268a1 1 0 0 0 1.414 1.414l-.707-.707-.707-.707ZM3.379 17.75a1 1 0 0 0 1.242-1.567L4 16.966l-.62.784Zm5.55-.379a1 1 0 0 0-1.857-.742L8 17l.928.371ZM5.071 21.63a1 1 0 0 0 1.856.742L6 22l-.928-.371Zm13.857-4.258a1 1 0 0 0-1.857-.742L18 17l.928.371Zm-3.857 4.258a1 1 0 0 0 1.857.742L16 22l-.928-.371ZM13 17.5h-1a.5.5 0 0 1 .5-.5v2a1.5 1.5 0 0 0 1.5-1.5h-1Zm-.5.5v-1a.5.5 0 0 1 .5.5h-2a1.5 1.5 0 0 0 1.5 1.5v-1Zm-.5-.5h1a.5.5 0 0 1-.5.5v-2a1.5 1.5 0 0 0-1.5 1.5h1Zm.5-.5v1a.5.5 0 0 1-.5-.5h2a1.5 1.5 0 0 0-1.5-1.5v1Zm0 .45h-1v.1h2v-.1h-1Zm-1 4.05h-1a.5.5 0 0 1 .5-.5v2a1.5 1.5 0 0 0 1.5-1.5h-1Zm-.5.5v-1a.5.5 0 0 1 .5.5h-2A1.5 1.5 0 0 0 11 23v-1Zm-.5-.5h1a.5.5 0 0 1-.5.5v-2a1.5 1.5 0 0 0-1.5 1.5h1Zm.5-.5v1a.5.5 0 0 1-.5-.5h2A1.5 1.5 0 0 0 11 20v1Zm0 .45h-1v.1h2v-.1h-1Zm-7.327-4.768.692-.723a3.403 3.403 0 0 1-1.07-2.459h-2c0 1.473.613 2.877 1.687 3.905l.691-.723ZM2.295 13.5h1c0-.914.38-1.8 1.07-2.46l-.692-.722-.69-.723A5.403 5.403 0 0 0 1.294 13.5h1Zm1.378-3.182.692.723A3.815 3.815 0 0 1 7 10V8c-1.499 0-2.945.57-4.018 1.595l.691.723ZM7 9l.976.219c.23-1.024.91-1.956 1.933-2.568l-.512-.858-.513-.859c-1.456.87-2.5 2.245-2.86 3.847L7 9Zm2.397-3.207.512.858a4.972 4.972 0 0 1 1.686-.622l-.173-.985-.174-.985a6.971 6.971 0 0 0-2.364.875l.513.859Zm2.025-.749.173.985a5.223 5.223 0 0 1 1.837.005l.18-.984.178-.984a7.223 7.223 0 0 0-2.542-.007l.174.985Zm2.19.006-.18.984c.606.11 1.177.326 1.681.631l.518-.856.518-.855a6.963 6.963 0 0 0-2.359-.888l-.179.984Zm2.02.76-.519.855c.505.305.93.693 1.257 1.136l.805-.594.805-.594a6.361 6.361 0 0 0-1.831-1.659l-.518.855Zm1.543 1.397-.805.594c.326.442.549.932.66 1.441l.976-.212.978-.213a5.786 5.786 0 0 0-1.005-2.204l-.804.594Zm.831 1.823-.977.212c.111.509.11 1.031-.005 1.539L18 11l.976.219c.178-.792.18-1.609.008-2.402l-.978.213ZM18 11v1h1v-2h-1v1Zm1 0v1a2.5 2.5 0 0 1 1.768.732l.707-.707.707-.707A4.5 4.5 0 0 0 19 10v1Zm2.475 1.025-.707.707A2.5 2.5 0 0 1 21.5 14.5h2a4.5 4.5 0 0 0-1.318-3.182l-.707.707ZM22.5 14.5h-1a2.5 2.5 0 0 1-.732 1.768l.707.707.707.707A4.5 4.5 0 0 0 23.5 14.5h-1ZM4 16.966l.62-.784a3.684 3.684 0 0 1-.255-.223l-.692.723-.69.723c.126.121.259.237.396.345L4 16.966ZM8 17l-.928-.371-2 5L6 22l.928.371 2-5L8 17Zm10 0-.928-.371-2 5L16 22l.928.371 2-5L18 17Z\" /></BaseIcon>\n);\nexport const CloudSnowIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M8 15h2m-1-1v2m5-1h2m-1-1v2M5 21h2m-1-1v2m5-2h2m-1-1v2m5 0h2m-1-1v2M4 16.966a4.63 4.63 0 0 1-.327-.284A4.403 4.403 0 0 1 2.295 13.5c0-1.194.496-2.338 1.378-3.182A4.815 4.815 0 0 1 7 9c.295-1.313 1.157-2.466 2.397-3.207a5.971 5.971 0 0 1 2.025-.749 6.224 6.224 0 0 1 2.19.006c.721.131 1.408.39 2.02.76a5.36 5.36 0 0 1 1.543 1.397c.407.552.69 1.172.831 1.823.142.65.14 1.32-.006 1.97h1a3.5 3.5 0 0 1 1.5 6.662\" /></BaseIcon>\n);\nexport const CloudSunIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M10.201 19.997c-1.977 0-3.58-1.544-3.58-3.448 0-1.903 1.603-3.446 3.58-3.446.303-1.355 1.38-2.461 2.827-2.902 1.445-.44 3.042-.148 4.186.77 1.144.915 1.663 2.312 1.361 3.667h.761c1.472 0 2.664 1.2 2.664 2.68A2.672 2.672 0 0 1 19.336 20h-9.134m2.66-12.934a4.166 4.166 0 0 0-2.677-1.732m2.677 1.732 2.744-1.892m-2.744 1.892c.55.796.802 1.754.723 2.709m-3.4-4.44a4.167 4.167 0 0 0-3.118.667m3.118-.668.602-3.278m-3.72 3.946A4.167 4.167 0 0 0 5.334 8.68m1.733-2.678L5.174 3.26m.16 5.42c-.2 1.088.041 2.21.669 3.12m-.669-3.12-3.278-.601m3.947 3.72-2.744 1.893m2.744-1.893a4.167 4.167 0 0 0 2.174 1.607\" /></BaseIcon>\n);\nexport const CloudsIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M8.958 21.05C6.772 21.05 5 19.344 5 17.24s1.772-3.81 3.958-3.81c.335-1.498 1.525-2.72 3.124-3.207 1.598-.487 3.363-.164 4.628.85 1.264 1.011 1.837 2.556 1.504 4.053h.842A2.953 2.953 0 0 1 22 18.09a2.954 2.954 0 0 1-2.945 2.964H8.958M18 8c-.5-2.5-3.532-3.401-5.5-2.5a3.117 3.117 0 0 0-2.11-2.312c-1.352-.411-2.845-.138-3.915.72-1.07.855-1.554 2.161-1.273 3.428h-.711A2.498 2.498 0 0 0 2 9.843c0 1.094.695 2.023 1.666 2.366\" /></BaseIcon>\n);\nexport const SnowflakeIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"m9.25 4-1 2H6m-2.25 4L5 12m0 0-1.25 2M5 12h3m-3 0H2.5m17.75-2L19 12l1.25 2M6 18h2.25l1 2M14 9h-4m4 0 3-5m-3 5 2 3m-6-3L7 4m3 5-2 3m0 0 2 3m0 0-3 5m3-5h4m2-3-2 3m2-3h5.5M14 15l3 5M14.75 4l1 2H18m0 12h-2.25l-1 2\" /></BaseIcon>\n);\nexport const SunIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M12 4V2m0 20v-2m10-8h-2M4 12H2m15.657-5.657 1.414-1.414M4.93 19.07l1.414-1.414m12.728 1.414-1.414-1.414M6.343 6.343 4.93 4.929M7 12a5 5 0 1 0 10 0 5 5 0 0 0-10 0Z\" /></BaseIcon>\n);\nexport const WindIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M9.59 4.59A2 2 0 1 1 11 8H2m10.59 11.41A2 2 0 1 0 14 16H2m15.73-8.27A2.5 2.5 0 1 1 19.5 12H2\" /></BaseIcon>\n);\n","import React from 'react';\nimport type { IconProps, WeatherCondition } from '../types';\nimport {\n CloudBoltIcon,\n CloudDrizzleIcon,\n CloudFogIcon,\n CloudHailIcon,\n CloudShowersHeavyIcon,\n CloudShowersIcon,\n CloudSleetIcon,\n CloudSnowIcon,\n CloudSunIcon,\n CloudsIcon,\n SnowflakeIcon,\n SunIcon,\n} from './weather-icons';\n\nconst iconMap: Record<WeatherCondition, React.ComponentType<IconProps>> = {\n sun: SunIcon,\n 'cloud-sun': CloudSunIcon,\n clouds: CloudsIcon,\n 'cloud-fog': CloudFogIcon,\n 'cloud-drizzle': CloudDrizzleIcon,\n 'cloud-showers': CloudShowersIcon,\n 'cloud-showers-heavy': CloudShowersHeavyIcon,\n 'cloud-sleet': CloudSleetIcon,\n 'cloud-snow': CloudSnowIcon,\n snowflake: SnowflakeIcon,\n 'cloud-bolt': CloudBoltIcon,\n 'cloud-hail': CloudHailIcon,\n};\n\nexport function WeatherIcon({ condition, ...props }: IconProps & { condition: WeatherCondition }) {\n const Icon = iconMap[condition] || CloudsIcon;\n return <Icon {...props} />;\n}\n","import React, { useEffect, useState } from 'react';\nimport { useWeatherForecast } from '../hooks/useWeatherForecast';\nimport type { DailyWeather, HourlyWeather, TemperatureUnit, WeatherForecastOptions, WindSpeedUnit } from '../types';\nimport { WeatherIcon } from '../icons/WeatherIcon';\nimport { WindIcon } from '../icons/weather-icons';\n\ntype Props = WeatherForecastOptions & {\n className?: string;\n style?: React.CSSProperties;\n compact?: boolean;\n};\n\nconst WIND_SPEED_UNIT_LABELS: Record<WindSpeedUnit, string> = {\n kmh: 'km/h',\n mph: 'mph',\n ms: 'm/s',\n kn: 'kn',\n};\n\nfunction closestHour(hourly: HourlyWeather[], targetTime: Date): HourlyWeather | undefined {\n return hourly.reduce<HourlyWeather | undefined>((closest, hour) => {\n const diff = Math.abs(new Date(hour.time).getTime() - targetTime.getTime());\n const closestDiff = closest ? Math.abs(new Date(closest.time).getTime() - targetTime.getTime()) : Infinity;\n return diff < closestDiff ? hour : closest;\n }, undefined);\n}\n\n// Format a real instant in a specific IANA timezone, falling back to the\n// browser's local zone if the timezone string is missing or invalid.\nfunction formatInZone(instant: Date, timezone: string | undefined, options: Intl.DateTimeFormatOptions): string {\n try {\n return new Intl.DateTimeFormat([], { ...options, timeZone: timezone || undefined }).format(instant);\n } catch {\n return new Intl.DateTimeFormat([], options).format(instant);\n }\n}\n\n// The clock is shown with the hour and minute large and the seconds (plus any\n// AM/PM marker) trailing in a smaller, dimmer size, so split it into those two\n// halves. Falls back to one undivided string if the locale's parts are not\n// where we expect them.\nfunction clockParts(instant: Date, timezone: string | undefined): { main: string; tail: string } {\n const options: Intl.DateTimeFormatOptions = { hour: 'numeric', minute: '2-digit', second: '2-digit' };\n const format = (() => {\n try {\n return new Intl.DateTimeFormat([], { ...options, timeZone: timezone || undefined });\n } catch {\n return new Intl.DateTimeFormat([], options);\n }\n })();\n const parts = format.formatToParts(instant);\n const part = (type: Intl.DateTimeFormatPartTypes) => parts.find((p) => p.type === type)?.value;\n const hour = part('hour');\n const minute = part('minute');\n const second = part('second');\n if (!hour || !minute || !second) return { main: format.format(instant), tail: '' };\n const dayPeriod = part('dayPeriod');\n return { main: `${hour}:${minute}`, tail: `:${second}${dayPeriod ? ` ${dayPeriod}` : ''}` };\n}\n\n// Just the zone abbreviation (e.g. \"GMT+2\"), so it can be shown apart from the\n// clock itself.\nfunction zoneLabel(instant: Date, timezone: string | undefined): string {\n const options: Intl.DateTimeFormatOptions = { hour: 'numeric', timeZoneName: 'short' };\n try {\n const format = (() => {\n try {\n return new Intl.DateTimeFormat([], { ...options, timeZone: timezone || undefined });\n } catch {\n return new Intl.DateTimeFormat([], options);\n }\n })();\n return format.formatToParts(instant).find((part) => part.type === 'timeZoneName')?.value ?? '';\n } catch {\n return '';\n }\n}\n\n// Daily dates arrive as plain \"YYYY-MM-DD\" calendar days; `new Date(...)` would\n// read those as UTC midnight and roll back a day in western timezones, so build\n// the date from its parts and let it stay a local calendar day.\nfunction parseCalendarDate(date: string): Date {\n const [year, month, day] = date.split('-').map(Number);\n if (!year || !month || !day) return new Date(date);\n return new Date(year, month - 1, day);\n}\n\n// The upcoming days sit in narrow columns along the bottom of the card, so\n// they are labelled with the abbreviated weekday (\"Fri\") rather than a full\n// name or \"Tomorrow\".\nfunction upcomingDayLabel(date: string): string {\n return parseCalendarDate(date).toLocaleDateString([], { weekday: 'short' });\n}\n\n// Daily totals that no longer have a column of their own in the card layout are\n// kept as hover text so the detail is still reachable.\nfunction upcomingDayDetail(day: DailyWeather, windSpeedUnitLabel: string): string | undefined {\n const details: string[] = [];\n if (day.precipitationSum !== undefined) details.push(`${day.precipitationSum.toFixed(1)} mm`);\n if (day.windSpeedMax !== undefined) details.push(`${day.windSpeedMax.toFixed(1)} ${windSpeedUnitLabel} wind`);\n return details.length > 0 ? details.join(' · ') : undefined;\n}\n\nconst styles = {\n root: {\n fontFamily: 'system-ui, sans-serif',\n border: '1px solid #e5e7eb',\n borderRadius: 12,\n padding: 16,\n background: '#fff',\n color: '#111827',\n maxWidth: 640,\n } as React.CSSProperties,\n row: { display: 'flex', alignItems: 'center', gap: 8 } as React.CSSProperties,\n hours: { display: 'flex', gap: 12, overflowX: 'auto', paddingBottom: 4 } as React.CSSProperties,\n hourCard: { minWidth: 72, textAlign: 'center' as const, padding: 8, borderRadius: 8, background: '#f9fafb' },\n dayRow: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, padding: '8px 0', borderBottom: '1px solid #f3f4f6' } as React.CSSProperties,\n // Card-sized: a fixed maximum width with generous, even padding, so the\n // widget reads as a self-contained tile wherever it is dropped in.\n compactRoot: {\n fontFamily: 'system-ui, sans-serif',\n display: 'flex',\n flexDirection: 'column',\n gap: 14,\n padding: 18,\n borderRadius: 16,\n width: '100%',\n maxWidth: 340,\n boxSizing: 'border-box',\n } as React.CSSProperties,\n // Current conditions stack down the card; the upcoming days sit in a row\n // along the bottom.\n compactBody: { display: 'flex', flexDirection: 'column', gap: 10 } as React.CSSProperties,\n compactCity: { fontSize: 15, fontWeight: 700, lineHeight: 1.2 } as React.CSSProperties,\n compactHeader: { display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 8 } as React.CSSProperties,\n compactTemp: { fontSize: 32, fontWeight: 600, lineHeight: 1 } as React.CSSProperties,\n // The short-term outlook (\"59 deg in 6h\") trails the current temperature.\n compactOutlook: { display: 'flex', alignItems: 'center', gap: 4, fontSize: 12, opacity: 0.75, whiteSpace: 'nowrap' } as React.CSSProperties,\n compactTime: { fontSize: 26, fontWeight: 600, lineHeight: 1.1 } as React.CSSProperties,\n compactSeconds: { fontSize: 13, fontWeight: 500, opacity: 0.6 } as React.CSSProperties,\n compactDate: { fontSize: 11, opacity: 0.7, marginTop: 2 } as React.CSSProperties,\n compactStats: { display: 'flex', flexWrap: 'wrap', gap: 14, fontSize: 12, opacity: 0.9 } as React.CSSProperties,\n // Three equal columns along the bottom edge, each stacking weekday / icon /\n // high-low so the row stays readable at card width.\n upcoming: {\n display: 'grid',\n gridTemplateColumns: 'repeat(3, 1fr)',\n gap: 8,\n paddingTop: 12,\n borderTop: '1px solid currentColor',\n borderTopColor: 'rgba(128,128,128,0.25)',\n fontSize: 12,\n } as React.CSSProperties,\n upcomingDay: { display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4, textAlign: 'center' } as React.CSSProperties,\n upcomingDayName: { opacity: 0.7, whiteSpace: 'nowrap' } as React.CSSProperties,\n upcomingTemps: { whiteSpace: 'nowrap' } as React.CSSProperties,\n compactStat: { display: 'flex', alignItems: 'center', gap: 4, whiteSpace: 'nowrap' } as React.CSSProperties,\n unitSwitch: { display: 'inline-flex', alignItems: 'center', cursor: 'pointer', userSelect: 'none' } as React.CSSProperties,\n rain: { color: '#2563eb', whiteSpace: 'nowrap' } as React.CSSProperties,\n tz: { fontSize: 12, opacity: 0.7, marginTop: 2 } as React.CSSProperties,\n list: { display: 'flex', flexDirection: 'column' } as React.CSSProperties,\n};\n\n// Precipitation chance is only surfaced when there is a non-zero chance of it.\nfunction RainBadge({ probability, compact }: { probability?: number; compact?: boolean }) {\n if (probability === undefined || probability === null || probability <= 0) return null;\n return <span style={styles.rain}>{compact ? ` · ${probability}%` : ` · ${probability}% rain`}</span>;\n}\n\n/**\n * Renders one location's forecast: a live current time (ticking, in the\n * location's own timezone), a clickable degF / degC unit switch, high/low and\n * precipitation for the next hours and days.\n */\nfunction SingleWeatherForecast(props: Props) {\n // Temperature unit is local state so the degF / degC switch can toggle it;\n // it is seeded from (and re-synced to) the temperatureUnit prop.\n const [unit, setUnit] = useState<TemperatureUnit>(props.temperatureUnit ?? 'fahrenheit');\n useEffect(() => {\n setUnit(props.temperatureUnit ?? 'fahrenheit');\n }, [props.temperatureUnit]);\n\n const { data, loading, error } = useWeatherForecast({ ...props, temperatureUnit: unit });\n\n // A ticking clock so the displayed current time stays accurate to the second.\n const [now, setNow] = useState(() => new Date());\n useEffect(() => {\n const id = setInterval(() => setNow(new Date()), 1000);\n return () => clearInterval(id);\n }, []);\n\n const toggleUnit = () => setUnit((u) => (u === 'celsius' ? 'fahrenheit' : 'celsius'));\n\n const renderUnitSwitch = (fontSize: number) => (\n <span\n role=\"button\"\n tabIndex={0}\n onClick={(e) => {\n e.stopPropagation();\n toggleUnit();\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n toggleUnit();\n }\n }}\n title=\"Switch temperature units\"\n aria-label={`Switch temperature units, currently ${unit === 'celsius' ? 'Celsius' : 'Fahrenheit'}`}\n style={{ ...styles.unitSwitch, fontSize }}\n >\n <span style={{ fontWeight: unit === 'fahrenheit' ? 700 : 400, opacity: unit === 'fahrenheit' ? 1 : 0.45 }}>&deg;F</span>\n <span style={{ opacity: 0.4, margin: '0 3px' }}>|</span>\n <span style={{ fontWeight: unit === 'celsius' ? 700 : 400, opacity: unit === 'celsius' ? 1 : 0.45 }}>&deg;C</span>\n </span>\n );\n\n // Keep showing the last forecast while a unit switch refetches in the\n // background; only suppress output on the very first load.\n if (loading && !data) return null;\n if (error) return <div className={props.className} style={props.style}>Error: {error.message}</div>;\n if (!data) return <div className={props.className} style={props.style}>No forecast available.</div>;\n\n const timezone = data.location?.timezone;\n const currentDate = formatInZone(now, timezone, { weekday: 'short', month: 'short', day: 'numeric' });\n const currentTime = formatInZone(now, timezone, { hour: 'numeric', minute: '2-digit', second: '2-digit', timeZoneName: 'short' });\n // The compact layout shows the clock at a larger size, so the zone label is\n // split off onto the smaller date line to keep the time on one line.\n const currentClock = clockParts(now, timezone);\n const currentZone = zoneLabel(now, timezone);\n\n if (props.compact) {\n const forecastBase = new Date(data.current.time);\n const today = data.daily[0];\n const in6Hours = closestHour(data.hourly, new Date(forecastBase.getTime() + 6 * 60 * 60 * 1000));\n const windSpeedUnitLabel = WIND_SPEED_UNIT_LABELS[props.windSpeedUnit ?? 'mph'];\n // Today is already summarised in the card's own stats line, so the bottom\n // row starts at tomorrow and shows the next three days.\n const upcomingDays = data.daily.slice(1, 4);\n\n return (\n <div className={props.className} style={{ ...styles.compactRoot, ...props.style }}>\n <div style={styles.compactBody}>\n <div style={styles.compactCity}>\n {data.location?.city || 'Current location'}\n {data.location?.region ? `, ${data.location.region}` : ''}\n </div>\n\n <div style={styles.compactHeader}>\n <WeatherIcon condition={data.current.icon} width={30} height={30} title=\"Current weather\" />\n <strong style={styles.compactTemp}>{data.current.temperature}</strong>\n {renderUnitSwitch(13)}\n {in6Hours && (\n <span style={styles.compactOutlook}>\n <WeatherIcon condition={in6Hours.icon} width={14} height={14} title=\"In 6 hours\" />\n {in6Hours.temperature}&deg; 6h\n <RainBadge probability={in6Hours.precipitationProbability} compact />\n </span>\n )}\n </div>\n\n <div>\n <div style={styles.compactTime}>\n {currentClock.main}\n {currentClock.tail && <span style={styles.compactSeconds}>{currentClock.tail}</span>}\n </div>\n <div style={styles.compactDate}>\n {currentDate}\n {currentZone ? ` · ${currentZone}` : ''}\n </div>\n </div>\n\n <div style={styles.compactStats}>\n {today && (\n <span style={styles.compactStat}>\n <WeatherIcon condition={today.icon} width={16} height={16} title=\"Today's high and low\" />\n {today.max}&deg; / {today.min}&deg;\n <RainBadge probability={today.precipitationProbabilityMax} compact />\n </span>\n )}\n <span style={styles.compactStat}>\n <WindIcon width={16} height={16} title=\"Wind speed\" />\n {data.current.windSpeed !== undefined ? `${Math.round(data.current.windSpeed)} ${windSpeedUnitLabel}` : '—'}\n </span>\n </div>\n </div>\n\n {upcomingDays.length > 0 && (\n <div style={styles.upcoming}>\n {upcomingDays.map((day) => (\n <div key={day.date} style={styles.upcomingDay} title={upcomingDayDetail(day, windSpeedUnitLabel)}>\n <span style={styles.upcomingDayName}>{upcomingDayLabel(day.date)}</span>\n <WeatherIcon condition={day.icon} width={18} height={18} />\n <span style={styles.upcomingTemps}>\n <strong>{day.max}&deg;</strong>\n <span style={{ opacity: 0.6 }}> / {day.min}&deg;</span>\n </span>\n {day.precipitationProbabilityMax !== undefined && day.precipitationProbabilityMax > 0 && (\n <span style={{ ...styles.rain, fontSize: 11 }}>{day.precipitationProbabilityMax}%</span>\n )}\n </div>\n ))}\n </div>\n )}\n </div>\n );\n }\n\n return (\n <div className={props.className} style={{ ...styles.root, ...props.style }}>\n <div>\n <h3 style={{ margin: 0, fontSize: 20 }}>\n {data.location?.city || 'Current location'}\n {data.location?.region ? `, ${data.location.region}` : ''}\n </h3>\n <div style={styles.tz}>\n {currentDate} &middot; {currentTime}\n {timezone ? ` · ${timezone}` : ''}\n </div>\n <div style={{ ...styles.row, marginTop: 8 }}>\n <WeatherIcon condition={data.current.icon} width={28} height={28} title=\"Current weather\" />\n <strong style={{ fontSize: 28 }}>{data.current.temperature}</strong>\n {renderUnitSwitch(14)}\n </div>\n </div>\n\n <div style={{ marginTop: 20 }}>\n <h4 style={{ margin: '0 0 10px 0' }}>Next hours</h4>\n <div style={styles.hours}>\n {data.hourly.slice(0, props.forecastHours ?? 12).map((hour) => (\n <div key={hour.time} style={styles.hourCard}>\n <div style={{ fontSize: 12 }}>{new Date(hour.time).toLocaleTimeString([], { hour: 'numeric' })}</div>\n <WeatherIcon condition={hour.icon} width={20} height={20} style={{ margin: '6px auto' }} />\n <div>{hour.temperature}&deg;</div>\n {hour.precipitationProbability !== undefined && hour.precipitationProbability > 0 && (\n <div style={{ ...styles.rain, fontSize: 12 }}>{hour.precipitationProbability}%</div>\n )}\n </div>\n ))}\n </div>\n </div>\n\n <div style={{ marginTop: 20 }}>\n <h4 style={{ margin: '0 0 10px 0' }}>Next days</h4>\n <div>\n {data.daily.map((day) => (\n <div key={day.date} style={styles.dayRow}>\n <div style={{ width: 120 }}>{new Date(day.date).toLocaleDateString([], { weekday: 'short', month: 'short', day: 'numeric' })}</div>\n <WeatherIcon condition={day.icon} width={20} height={20} />\n <div>\n {day.min}&deg; / {day.max}&deg;\n {day.precipitationProbabilityMax !== undefined && day.precipitationProbabilityMax > 0 && (\n <span style={styles.rain}> &middot; {day.precipitationProbabilityMax}%</span>\n )}\n </div>\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n\n/**\n * Weather forecast widget. When `locations` is provided, one forecast is\n * rendered per location; otherwise a single auto-detected forecast is shown.\n */\nexport function WeatherForecast(props: Props) {\n const { locations, ...rest } = props;\n\n if (locations && locations.length > 0) {\n const gap = props.compact ? 8 : 16;\n return (\n <div style={{ ...styles.list, gap }}>\n {locations.map((loc, index) => (\n <SingleWeatherForecast\n key={`${loc.label ?? ''}:${loc.latitude ?? ''}:${loc.longitude ?? ''}:${index}`}\n {...rest}\n latitude={typeof loc.latitude === 'number' ? loc.latitude : undefined}\n longitude={typeof loc.longitude === 'number' ? loc.longitude : undefined}\n location={{ city: loc.label, timezone: loc.timezone }}\n />\n ))}\n </div>\n );\n }\n\n return <SingleWeatherForecast {...rest} />;\n}\n"],"mappings":"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","names":["res","data","jsx","jsx","useEffect","useState","jsx","jsxs","useState","useEffect"]}
1
+ {"version":3,"sources":["../src/weatherCodes.ts","../src/lib/cache.ts","../src/api/geolocation.ts","../src/api/forecast.ts","../src/hooks/useWeatherForecast.ts","../src/icons/BaseIcon.tsx","../src/icons/weather-icons.tsx","../src/icons/WeatherIcon.tsx","../src/components/WeatherForecast.tsx"],"sourcesContent":["import type { WeatherCondition } from './types';\n\nexport function getWeatherIcon(code: number, isDay = 1): WeatherCondition {\n if (code === 0) return 'sun';\n if ([1, 2].includes(code)) return isDay ? 'cloud-sun' : 'clouds';\n if (code === 3) return 'clouds';\n if ([45, 48].includes(code)) return 'cloud-fog';\n if ([51, 53, 55, 56, 57].includes(code)) return 'cloud-drizzle';\n if ([61, 63].includes(code)) return 'cloud-showers';\n if ([65, 66, 67, 80, 81, 82].includes(code)) return 'cloud-showers-heavy';\n if ([71, 73, 75, 77, 85, 86].includes(code)) return 'cloud-snow';\n if (code === 95) return 'cloud-bolt';\n if ([96, 99].includes(code)) return 'cloud-hail';\n return 'clouds';\n}\n","const CACHE_PREFIX = 'weather-forecast-cache:';\nconst CACHE_TTL_MS = 30 * 60 * 1000;\n\ntype CacheEntry<T> = {\n timestamp: number;\n data: T;\n};\n\nfunction getStorage(): Storage | null {\n if (typeof window === 'undefined' || !window.localStorage) return null;\n return window.localStorage;\n}\n\n/** Returns the cached value for `key` if it was written within the last 30 minutes. */\nexport function readCachedForecast<T>(key: string): T | null {\n const storage = getStorage();\n if (!storage) return null;\n\n try {\n const raw = storage.getItem(CACHE_PREFIX + key);\n if (!raw) return null;\n\n const entry = JSON.parse(raw) as CacheEntry<T>;\n if (Date.now() - entry.timestamp > CACHE_TTL_MS) {\n storage.removeItem(CACHE_PREFIX + key);\n return null;\n }\n\n return entry.data;\n } catch {\n return null;\n }\n}\n\nexport function writeCachedForecast<T>(key: string, data: T): void {\n const storage = getStorage();\n if (!storage) return;\n\n try {\n const entry: CacheEntry<T> = { timestamp: Date.now(), data };\n storage.setItem(CACHE_PREFIX + key, JSON.stringify(entry));\n } catch {\n // Storage full or unavailable (e.g. private browsing) — safe to ignore,\n // the next call will simply hit the network again.\n }\n}\n\n/** Clears all cached forecasts. Mainly useful for tests/debugging. */\nexport function clearWeatherForecastCache(): void {\n const storage = getStorage();\n if (!storage) return;\n\n for (let i = storage.length - 1; i >= 0; i--) {\n const key = storage.key(i);\n if (key?.startsWith(CACHE_PREFIX)) storage.removeItem(key);\n }\n}\n","import type { WeatherLocation } from '../types';\n\nconst DEFAULT_IP_API_URL = 'https://ipapi.co';\n\ntype IpApiResponse = {\n city?: string;\n region?: string;\n country_name?: string;\n timezone?: string;\n latitude?: number;\n longitude?: number;\n error?: boolean;\n reason?: string;\n};\n\ntype GeoWorkerResponse = {\n city?: string;\n region?: string;\n country?: string;\n timezone?: string;\n latitude?: number | string;\n longitude?: number | string;\n};\n\nexport async function getClientLocation(geoEndpoint?: string, ip?: string): Promise<WeatherLocation> {\n if (geoEndpoint) {\n const url = ip ? `${geoEndpoint}?ip=${encodeURIComponent(ip)}` : geoEndpoint;\n const res = await fetch(url);\n if (!res.ok) throw new Error(`Geolocation worker lookup failed: ${res.status}`);\n const data: GeoWorkerResponse = await res.json();\n\n return {\n city: data.city,\n region: data.region,\n country: data.country,\n timezone: data.timezone,\n latitude: Number(data.latitude),\n longitude: Number(data.longitude),\n };\n }\n\n const res = await fetch(`${DEFAULT_IP_API_URL}/${ip ? `${encodeURIComponent(ip)}/` : ''}json/`);\n if (!res.ok) throw new Error(`ipapi.co lookup failed: ${res.status}`);\n const data: IpApiResponse = await res.json();\n\n if (data.error) {\n throw new Error(`ipapi.co lookup failed: ${data.reason ?? 'unknown error'}`);\n }\n\n return {\n city: data.city,\n region: data.region,\n country: data.country_name,\n timezone: data.timezone,\n latitude: Number(data.latitude),\n longitude: Number(data.longitude),\n };\n}\n","import type { WeatherForecastData, WeatherForecastOptions, WeatherLocation } from '../types';\nimport { getWeatherIcon } from '../weatherCodes';\nimport { getClientLocation } from './geolocation';\nimport { readCachedForecast, writeCachedForecast } from '../lib/cache';\n\nfunction buildUrl(latitude: number, longitude: number, options: WeatherForecastOptions) {\n const params = new URLSearchParams({\n latitude: String(latitude),\n longitude: String(longitude),\n timezone: options.location?.timezone || 'auto',\n temperature_unit: options.temperatureUnit || 'fahrenheit',\n wind_speed_unit: options.windSpeedUnit || 'mph',\n forecast_days: String(options.forecastDays ?? 5),\n forecast_hours: String(options.forecastHours ?? 24),\n current: [\n 'temperature_2m',\n 'weather_code',\n 'is_day',\n 'rain',\n 'showers',\n 'snowfall',\n 'wind_speed_10m',\n ].join(','),\n hourly: [\n 'temperature_2m',\n 'weather_code',\n 'precipitation_probability',\n 'rain',\n 'showers',\n 'snowfall',\n ].join(','),\n daily: [\n 'temperature_2m_max',\n 'temperature_2m_min',\n 'weather_code',\n 'precipitation_probability_max',\n 'precipitation_sum',\n 'wind_speed_10m_max',\n ].join(','),\n });\n\n return `https://api.open-meteo.com/v1/forecast?${params.toString()}`;\n}\n\nexport async function getWeatherForecast(\n options: WeatherForecastOptions = {}\n): Promise<WeatherForecastData> {\n let location: WeatherLocation;\n\n if (typeof options.latitude === 'number' && typeof options.longitude === 'number') {\n location = {\n ...options.location,\n latitude: options.latitude,\n longitude: options.longitude,\n };\n } else {\n location = await getClientLocation(options.geoEndpoint, options.ip);\n }\n\n const url = buildUrl(location.latitude, location.longitude, { ...options, location });\n\n const cached = readCachedForecast<WeatherForecastData>(url);\n if (cached) return cached;\n\n const response = await fetch(url, { headers: { 'Content-Type': 'application/json' } });\n\n if (!response.ok) {\n throw new Error(`Weather request failed: ${response.status} ${response.statusText}`);\n }\n\n const data = await response.json();\n\n if (!data?.current || !data?.hourly || !data?.daily) {\n throw new Error('Invalid weather response');\n }\n\n const result: WeatherForecastData = {\n location: { ...location, timezone: data.timezone || location.timezone },\n current: {\n time: data.current.time,\n temperature: Math.round(data.current.temperature_2m),\n weatherCode: data.current.weather_code,\n icon: getWeatherIcon(data.current.weather_code, data.current.is_day),\n isDay: data.current.is_day,\n rain: data.current.rain,\n showers: data.current.showers,\n snowfall: data.current.snowfall,\n windSpeed: data.current.wind_speed_10m,\n },\n hourly: data.hourly.time.map((time: string, index: number) => ({\n time,\n temperature: Math.round(data.hourly.temperature_2m[index]),\n weatherCode: data.hourly.weather_code[index],\n precipitationProbability: data.hourly.precipitation_probability?.[index],\n rain: data.hourly.rain?.[index],\n showers: data.hourly.showers?.[index],\n snowfall: data.hourly.snowfall?.[index],\n icon: getWeatherIcon(data.hourly.weather_code[index]),\n })),\n daily: data.daily.time.map((date: string, index: number) => ({\n date,\n min: Math.round(data.daily.temperature_2m_min[index]),\n max: Math.round(data.daily.temperature_2m_max[index]),\n weatherCode: data.daily.weather_code[index],\n precipitationProbabilityMax: data.daily.precipitation_probability_max?.[index],\n precipitationSum: data.daily.precipitation_sum?.[index],\n windSpeedMax: data.daily.wind_speed_10m_max?.[index],\n icon: getWeatherIcon(data.daily.weather_code[index]),\n })),\n };\n\n writeCachedForecast(url, result);\n return result;\n}\n","import { useEffect, useState } from 'react';\nimport type { WeatherForecastData, WeatherForecastOptions } from '../types';\nimport { getWeatherForecast } from '../api/forecast';\n\nexport function useWeatherForecast(options: WeatherForecastOptions = {}) {\n const [data, setData] = useState<WeatherForecastData | null>(null);\n const [error, setError] = useState<Error | null>(null);\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n let active = true;\n setLoading(true);\n setError(null);\n\n getWeatherForecast(options)\n .then((result) => { if (active) setData(result); })\n .catch((err) => { if (active) setError(err instanceof Error ? err : new Error('Unknown error')); })\n .finally(() => { if (active) setLoading(false); });\n\n return () => { active = false; };\n }, [\n options.latitude,\n options.longitude,\n options.geoEndpoint,\n options.ip,\n options.forecastDays,\n options.forecastHours,\n options.temperatureUnit,\n options.windSpeedUnit,\n options.location?.timezone,\n ]);\n\n return { data, error, loading };\n}\n","import React from 'react';\nimport type { IconProps } from '../types';\n\nexport function BaseIcon({ title, children, ...props }: IconProps & { children: React.ReactNode }) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n role={title ? 'img' : 'presentation'}\n aria-hidden={title ? undefined : true}\n aria-label={title}\n {...props}\n >\n {title ? <title>{title}</title> : null}\n {children}\n </svg>\n );\n}\n","import React from 'react';\nimport type { IconProps } from '../types';\nimport { BaseIcon } from './BaseIcon';\n\nexport const CloudBoltIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M7 18a4.815 4.815 0 0 1-3.327-1.318A4.403 4.403 0 0 1 2.295 13.5c0-1.194.496-2.338 1.378-3.182A4.815 4.815 0 0 1 7 9c.295-1.313 1.157-2.466 2.397-3.207a5.971 5.971 0 0 1 2.025-.749 6.224 6.224 0 0 1 2.19.006c.721.131 1.408.39 2.02.76a5.36 5.36 0 0 1 1.543 1.397c.407.552.69 1.172.831 1.823.142.65.14 1.32-.006 1.97h1a3.5 3.5 0 0 1 0 7h-1m-4.75-5L11 17.5h3.375l-2.25 4.5\" /></BaseIcon>\n);\nexport const CloudDrizzleIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M12 18v1m-3-4v1m6-1v1m2 3v1M7 19v1m-3-3.034a4.63 4.63 0 0 1-.327-.284A4.403 4.403 0 0 1 2.295 13.5c0-1.194.496-2.338 1.378-3.182A4.815 4.815 0 0 1 7 9c.295-1.313 1.157-2.466 2.397-3.207a5.971 5.971 0 0 1 2.025-.749 6.224 6.224 0 0 1 2.19.006c.721.131 1.408.39 2.02.76a5.36 5.36 0 0 1 1.543 1.397c.407.552.69 1.172.831 1.823.142.65.14 1.32-.006 1.97h1a3.5 3.5 0 0 1 1.5 6.662\" /></BaseIcon>\n);\nexport const CloudFogIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M4 16.966a4.63 4.63 0 0 1-.327-.284A4.403 4.403 0 0 1 2.295 13.5c0-1.194.496-2.338 1.378-3.182A4.815 4.815 0 0 1 7 9c.295-1.313 1.157-2.466 2.397-3.207a5.971 5.971 0 0 1 2.025-.749 6.224 6.224 0 0 1 2.19.006c.721.131 1.408.39 2.02.76a5.36 5.36 0 0 1 1.543 1.397c.407.552.69 1.172.831 1.823.142.65.14 1.32-.006 1.97h1a3.5 3.5 0 0 1 1.5 6.662M5 21h7m4 0h3M8 17h8\" /></BaseIcon>\n);\nexport const CloudHailIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M8 17.45v.1m-1.5 3.9v.1m10.5-4.1v.1m-1.5 3.9v.1m-3-4.1v.1m-1.5 3.9v.1m10.475-4.575A3.5 3.5 0 0 0 19 11h-1c.146-.65.148-1.32.006-1.97a4.784 4.784 0 0 0-.831-1.823 5.36 5.36 0 0 0-1.544-1.398 5.964 5.964 0 0 0-2.02-.759 6.224 6.224 0 0 0-2.19-.006 5.971 5.971 0 0 0-2.024.749C8.157 6.533 7.295 7.687 7 9a4.815 4.815 0 0 0-3.327 1.318A4.403 4.403 0 0 0 2.295 13.5c0 1.194.496 2.338 1.378 3.182.105.1.214.195.327.284m4.5.534a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Zm-1.5 4a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Zm10.5-4a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Zm-1.5 4a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Zm-3-4a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Zm-1.5 4a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Z\" /></BaseIcon>\n);\nexport const CloudShowersIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M4 16.966a4.63 4.63 0 0 1-.327-.284A4.403 4.403 0 0 1 2.295 13.5c0-1.194.496-2.338 1.378-3.182A4.815 4.815 0 0 1 7 9c.295-1.313 1.157-2.466 2.397-3.207a5.971 5.971 0 0 1 2.025-.749 6.224 6.224 0 0 1 2.19.006c.721.131 1.408.39 2.02.76a5.36 5.36 0 0 1 1.543 1.397c.407.552.69 1.172.831 1.823.142.65.14 1.32-.006 1.97h1a3.5 3.5 0 0 1 1.5 6.662M12 13v2m0 3v2m4-5v2m0 3v2m-8-7v2m0 3v2\" /></BaseIcon>\n);\nexport const CloudShowersHeavyIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M4 16.966a4.63 4.63 0 0 1-.327-.284A4.403 4.403 0 0 1 2.295 13.5c0-1.194.496-2.338 1.378-3.182A4.815 4.815 0 0 1 7 9c.295-1.313 1.157-2.466 2.397-3.207a5.971 5.971 0 0 1 2.025-.749 6.224 6.224 0 0 1 2.19.006c.721.131 1.408.39 2.02.76a5.36 5.36 0 0 1 1.543 1.397c.407.552.69 1.172.831 1.823.142.65.14 1.32-.006 1.97h1a3.5 3.5 0 0 1 1.5 6.662M16 15v6m-8-6v6m4-8v6\" /></BaseIcon>\n);\nexport const CloudSleetIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M13.5 17.45a1 1 0 1 0-2 0h2Zm-2 .1a1 1 0 1 0 2 0h-2Zm.5 3.9a1 1 0 1 0-2 0h2Zm-2 .1a1 1 0 1 0 2 0h-2Zm-6.327-4.868.692-.723-.692.723ZM7 9v1a1 1 0 0 0 .976-.781L7 9Zm2.397-3.207.512.858-.512-.858ZM18 11l-.976-.219A1 1 0 0 0 18 12v-1Zm2.768 5.268a1 1 0 0 0 1.414 1.414l-.707-.707-.707-.707ZM3.379 17.75a1 1 0 0 0 1.242-1.567L4 16.966l-.62.784Zm5.55-.379a1 1 0 0 0-1.857-.742L8 17l.928.371ZM5.071 21.63a1 1 0 0 0 1.856.742L6 22l-.928-.371Zm13.857-4.258a1 1 0 0 0-1.857-.742L18 17l.928.371Zm-3.857 4.258a1 1 0 0 0 1.857.742L16 22l-.928-.371ZM13 17.5h-1a.5.5 0 0 1 .5-.5v2a1.5 1.5 0 0 0 1.5-1.5h-1Zm-.5.5v-1a.5.5 0 0 1 .5.5h-2a1.5 1.5 0 0 0 1.5 1.5v-1Zm-.5-.5h1a.5.5 0 0 1-.5.5v-2a1.5 1.5 0 0 0-1.5 1.5h1Zm.5-.5v1a.5.5 0 0 1-.5-.5h2a1.5 1.5 0 0 0-1.5-1.5v1Zm0 .45h-1v.1h2v-.1h-1Zm-1 4.05h-1a.5.5 0 0 1 .5-.5v2a1.5 1.5 0 0 0 1.5-1.5h-1Zm-.5.5v-1a.5.5 0 0 1 .5.5h-2A1.5 1.5 0 0 0 11 23v-1Zm-.5-.5h1a.5.5 0 0 1-.5.5v-2a1.5 1.5 0 0 0-1.5 1.5h1Zm.5-.5v1a.5.5 0 0 1-.5-.5h2A1.5 1.5 0 0 0 11 20v1Zm0 .45h-1v.1h2v-.1h-1Zm-7.327-4.768.692-.723a3.403 3.403 0 0 1-1.07-2.459h-2c0 1.473.613 2.877 1.687 3.905l.691-.723ZM2.295 13.5h1c0-.914.38-1.8 1.07-2.46l-.692-.722-.69-.723A5.403 5.403 0 0 0 1.294 13.5h1Zm1.378-3.182.692.723A3.815 3.815 0 0 1 7 10V8c-1.499 0-2.945.57-4.018 1.595l.691.723ZM7 9l.976.219c.23-1.024.91-1.956 1.933-2.568l-.512-.858-.513-.859c-1.456.87-2.5 2.245-2.86 3.847L7 9Zm2.397-3.207.512.858a4.972 4.972 0 0 1 1.686-.622l-.173-.985-.174-.985a6.971 6.971 0 0 0-2.364.875l.513.859Zm2.025-.749.173.985a5.223 5.223 0 0 1 1.837.005l.18-.984.178-.984a7.223 7.223 0 0 0-2.542-.007l.174.985Zm2.19.006-.18.984c.606.11 1.177.326 1.681.631l.518-.856.518-.855a6.963 6.963 0 0 0-2.359-.888l-.179.984Zm2.02.76-.519.855c.505.305.93.693 1.257 1.136l.805-.594.805-.594a6.361 6.361 0 0 0-1.831-1.659l-.518.855Zm1.543 1.397-.805.594c.326.442.549.932.66 1.441l.976-.212.978-.213a5.786 5.786 0 0 0-1.005-2.204l-.804.594Zm.831 1.823-.977.212c.111.509.11 1.031-.005 1.539L18 11l.976.219c.178-.792.18-1.609.008-2.402l-.978.213ZM18 11v1h1v-2h-1v1Zm1 0v1a2.5 2.5 0 0 1 1.768.732l.707-.707.707-.707A4.5 4.5 0 0 0 19 10v1Zm2.475 1.025-.707.707A2.5 2.5 0 0 1 21.5 14.5h2a4.5 4.5 0 0 0-1.318-3.182l-.707.707ZM22.5 14.5h-1a2.5 2.5 0 0 1-.732 1.768l.707.707.707.707A4.5 4.5 0 0 0 23.5 14.5h-1ZM4 16.966l.62-.784a3.684 3.684 0 0 1-.255-.223l-.692.723-.69.723c.126.121.259.237.396.345L4 16.966ZM8 17l-.928-.371-2 5L6 22l.928.371 2-5L8 17Zm10 0-.928-.371-2 5L16 22l.928.371 2-5L18 17Z\" /></BaseIcon>\n);\nexport const CloudSnowIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M8 15h2m-1-1v2m5-1h2m-1-1v2M5 21h2m-1-1v2m5-2h2m-1-1v2m5 0h2m-1-1v2M4 16.966a4.63 4.63 0 0 1-.327-.284A4.403 4.403 0 0 1 2.295 13.5c0-1.194.496-2.338 1.378-3.182A4.815 4.815 0 0 1 7 9c.295-1.313 1.157-2.466 2.397-3.207a5.971 5.971 0 0 1 2.025-.749 6.224 6.224 0 0 1 2.19.006c.721.131 1.408.39 2.02.76a5.36 5.36 0 0 1 1.543 1.397c.407.552.69 1.172.831 1.823.142.65.14 1.32-.006 1.97h1a3.5 3.5 0 0 1 1.5 6.662\" /></BaseIcon>\n);\nexport const CloudSunIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M10.201 19.997c-1.977 0-3.58-1.544-3.58-3.448 0-1.903 1.603-3.446 3.58-3.446.303-1.355 1.38-2.461 2.827-2.902 1.445-.44 3.042-.148 4.186.77 1.144.915 1.663 2.312 1.361 3.667h.761c1.472 0 2.664 1.2 2.664 2.68A2.672 2.672 0 0 1 19.336 20h-9.134m2.66-12.934a4.166 4.166 0 0 0-2.677-1.732m2.677 1.732 2.744-1.892m-2.744 1.892c.55.796.802 1.754.723 2.709m-3.4-4.44a4.167 4.167 0 0 0-3.118.667m3.118-.668.602-3.278m-3.72 3.946A4.167 4.167 0 0 0 5.334 8.68m1.733-2.678L5.174 3.26m.16 5.42c-.2 1.088.041 2.21.669 3.12m-.669-3.12-3.278-.601m3.947 3.72-2.744 1.893m2.744-1.893a4.167 4.167 0 0 0 2.174 1.607\" /></BaseIcon>\n);\nexport const CloudsIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M8.958 21.05C6.772 21.05 5 19.344 5 17.24s1.772-3.81 3.958-3.81c.335-1.498 1.525-2.72 3.124-3.207 1.598-.487 3.363-.164 4.628.85 1.264 1.011 1.837 2.556 1.504 4.053h.842A2.953 2.953 0 0 1 22 18.09a2.954 2.954 0 0 1-2.945 2.964H8.958M18 8c-.5-2.5-3.532-3.401-5.5-2.5a3.117 3.117 0 0 0-2.11-2.312c-1.352-.411-2.845-.138-3.915.72-1.07.855-1.554 2.161-1.273 3.428h-.711A2.498 2.498 0 0 0 2 9.843c0 1.094.695 2.023 1.666 2.366\" /></BaseIcon>\n);\nexport const SnowflakeIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"m9.25 4-1 2H6m-2.25 4L5 12m0 0-1.25 2M5 12h3m-3 0H2.5m17.75-2L19 12l1.25 2M6 18h2.25l1 2M14 9h-4m4 0 3-5m-3 5 2 3m-6-3L7 4m3 5-2 3m0 0 2 3m0 0-3 5m3-5h4m2-3-2 3m2-3h5.5M14 15l3 5M14.75 4l1 2H18m0 12h-2.25l-1 2\" /></BaseIcon>\n);\nexport const SunIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M12 4V2m0 20v-2m10-8h-2M4 12H2m15.657-5.657 1.414-1.414M4.93 19.07l1.414-1.414m12.728 1.414-1.414-1.414M6.343 6.343 4.93 4.929M7 12a5 5 0 1 0 10 0 5 5 0 0 0-10 0Z\" /></BaseIcon>\n);\nexport const WindIcon = (props: IconProps) => (\n <BaseIcon {...props}><path d=\"M9.59 4.59A2 2 0 1 1 11 8H2m10.59 11.41A2 2 0 1 0 14 16H2m15.73-8.27A2.5 2.5 0 1 1 19.5 12H2\" /></BaseIcon>\n);\n","import React from 'react';\nimport type { IconProps, WeatherCondition } from '../types';\nimport {\n CloudBoltIcon,\n CloudDrizzleIcon,\n CloudFogIcon,\n CloudHailIcon,\n CloudShowersHeavyIcon,\n CloudShowersIcon,\n CloudSleetIcon,\n CloudSnowIcon,\n CloudSunIcon,\n CloudsIcon,\n SnowflakeIcon,\n SunIcon,\n} from './weather-icons';\n\nconst iconMap: Record<WeatherCondition, React.ComponentType<IconProps>> = {\n sun: SunIcon,\n 'cloud-sun': CloudSunIcon,\n clouds: CloudsIcon,\n 'cloud-fog': CloudFogIcon,\n 'cloud-drizzle': CloudDrizzleIcon,\n 'cloud-showers': CloudShowersIcon,\n 'cloud-showers-heavy': CloudShowersHeavyIcon,\n 'cloud-sleet': CloudSleetIcon,\n 'cloud-snow': CloudSnowIcon,\n snowflake: SnowflakeIcon,\n 'cloud-bolt': CloudBoltIcon,\n 'cloud-hail': CloudHailIcon,\n};\n\nexport function WeatherIcon({ condition, ...props }: IconProps & { condition: WeatherCondition }) {\n const Icon = iconMap[condition] || CloudsIcon;\n return <Icon {...props} />;\n}\n","import React, { useEffect, useState } from 'react';\nimport { useWeatherForecast } from '../hooks/useWeatherForecast';\nimport type { DailyWeather, TemperatureUnit, WeatherForecastOptions, WindSpeedUnit } from '../types';\nimport { WeatherIcon } from '../icons/WeatherIcon';\nimport { WindIcon } from '../icons/weather-icons';\n\ntype Props = WeatherForecastOptions & {\n className?: string;\n style?: React.CSSProperties;\n compact?: boolean;\n};\n\nconst WIND_SPEED_UNIT_LABELS: Record<WindSpeedUnit, string> = {\n kmh: 'km/h',\n mph: 'mph',\n ms: 'm/s',\n kn: 'kn',\n};\n\n// Format a real instant in a specific IANA timezone, falling back to the\n// browser's local zone if the timezone string is missing or invalid.\nfunction formatInZone(instant: Date, timezone: string | undefined, options: Intl.DateTimeFormatOptions): string {\n try {\n return new Intl.DateTimeFormat([], { ...options, timeZone: timezone || undefined }).format(instant);\n } catch {\n return new Intl.DateTimeFormat([], options).format(instant);\n }\n}\n\n// The clock is shown with the hour and minute large and the seconds (plus any\n// AM/PM marker) trailing in a smaller, dimmer size, so split it into those two\n// halves. Falls back to one undivided string if the locale's parts are not\n// where we expect them.\nfunction clockParts(instant: Date, timezone: string | undefined): { main: string; tail: string } {\n const options: Intl.DateTimeFormatOptions = { hour: 'numeric', minute: '2-digit', second: '2-digit' };\n const format = (() => {\n try {\n return new Intl.DateTimeFormat([], { ...options, timeZone: timezone || undefined });\n } catch {\n return new Intl.DateTimeFormat([], options);\n }\n })();\n const parts = format.formatToParts(instant);\n const part = (type: Intl.DateTimeFormatPartTypes) => parts.find((p) => p.type === type)?.value;\n const hour = part('hour');\n const minute = part('minute');\n const second = part('second');\n if (!hour || !minute || !second) return { main: format.format(instant), tail: '' };\n const dayPeriod = part('dayPeriod');\n return { main: `${hour}:${minute}`, tail: `:${second}${dayPeriod ? ` ${dayPeriod}` : ''}` };\n}\n\n// Just the zone abbreviation (e.g. \"GMT+2\"), so it can be shown apart from the\n// clock itself.\nfunction zoneLabel(instant: Date, timezone: string | undefined): string {\n const options: Intl.DateTimeFormatOptions = { hour: 'numeric', timeZoneName: 'short' };\n try {\n const format = (() => {\n try {\n return new Intl.DateTimeFormat([], { ...options, timeZone: timezone || undefined });\n } catch {\n return new Intl.DateTimeFormat([], options);\n }\n })();\n return format.formatToParts(instant).find((part) => part.type === 'timeZoneName')?.value ?? '';\n } catch {\n return '';\n }\n}\n\n// Daily dates arrive as plain \"YYYY-MM-DD\" calendar days; `new Date(...)` would\n// read those as UTC midnight and roll back a day in western timezones, so build\n// the date from its parts and let it stay a local calendar day.\nfunction parseCalendarDate(date: string): Date {\n const [year, month, day] = date.split('-').map(Number);\n if (!year || !month || !day) return new Date(date);\n return new Date(year, month - 1, day);\n}\n\n// The upcoming days sit in narrow columns in the card, so they are labelled\n// with the abbreviated weekday (\"Fri\") rather than a full name or \"Tomorrow\".\nfunction upcomingDayLabel(date: string): string {\n return parseCalendarDate(date).toLocaleDateString([], { weekday: 'short' });\n}\n\n// Daily totals that no longer have a column of their own in the card layout are\n// kept as hover text so the detail is still reachable.\nfunction upcomingDayDetail(day: DailyWeather, windSpeedUnitLabel: string): string | undefined {\n const details: string[] = [];\n if (day.precipitationSum !== undefined) details.push(`${day.precipitationSum.toFixed(1)} mm`);\n if (day.windSpeedMax !== undefined) details.push(`${day.windSpeedMax.toFixed(1)} ${windSpeedUnitLabel} wind`);\n return details.length > 0 ? details.join(' · ') : undefined;\n}\n\nconst styles = {\n root: {\n fontFamily: 'system-ui, sans-serif',\n border: '1px solid #e5e7eb',\n borderRadius: 12,\n padding: 16,\n background: '#fff',\n color: '#111827',\n maxWidth: 640,\n } as React.CSSProperties,\n row: { display: 'flex', alignItems: 'center', gap: 8 } as React.CSSProperties,\n hours: { display: 'flex', gap: 12, overflowX: 'auto', paddingBottom: 4 } as React.CSSProperties,\n hourCard: { minWidth: 72, textAlign: 'center' as const, padding: 8, borderRadius: 8, background: '#f9fafb' },\n dayRow: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, padding: '8px 0', borderBottom: '1px solid #f3f4f6' } as React.CSSProperties,\n // Fluid: fills whatever container it is dropped into (e.g. the full\n // sidebar/column width on desktop), with generous, even padding. Current\n // conditions sit on the left and the upcoming days on the right; the two\n // halves wrap into a stack when the container gets narrow.\n compactRoot: {\n fontFamily: 'system-ui, sans-serif',\n display: 'flex',\n flexDirection: 'row',\n flexWrap: 'wrap',\n alignItems: 'stretch',\n gap: 24,\n padding: '20px 24px',\n borderRadius: 16,\n width: '100%',\n boxSizing: 'border-box',\n } as React.CSSProperties,\n // Current conditions form two spread-out rows down the left side of the\n // card (rather than five stacked lines), so the card stays short and each\n // row uses the full available width.\n compactBody: { display: 'flex', flexDirection: 'column', justifyContent: 'space-between', gap: 20, flex: '1.6 1 240px', minWidth: 0 } as React.CSSProperties,\n compactRow: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexWrap: 'wrap', gap: '12px 16px' } as React.CSSProperties,\n compactCity: { fontSize: 15, fontWeight: 700, lineHeight: 1.2 } as React.CSSProperties,\n compactHeader: { display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 12 } as React.CSSProperties,\n compactTemp: { fontSize: 32, fontWeight: 600, lineHeight: 1 } as React.CSSProperties,\n compactTime: { fontSize: 26, fontWeight: 600, lineHeight: 1.1 } as React.CSSProperties,\n compactSeconds: { fontSize: 13, fontWeight: 500, opacity: 0.6, marginLeft: 2 } as React.CSSProperties,\n compactDate: { fontSize: 11, opacity: 0.7, marginTop: 5 } as React.CSSProperties,\n compactStats: { display: 'flex', flexWrap: 'wrap', gap: '8px 18px', fontSize: 12, opacity: 0.9 } as React.CSSProperties,\n // Three equal columns on the right side of the card, each stacking\n // weekday / icon / high-low, vertically centered against the current\n // conditions and nudged over behind a divider so the two halves read as\n // separate sections.\n upcoming: {\n display: 'grid',\n gridTemplateColumns: 'repeat(3, 1fr)',\n alignContent: 'center',\n gap: 14,\n flex: '1 1 190px',\n minWidth: 0,\n paddingLeft: 22,\n borderLeft: '1px solid currentColor',\n borderLeftColor: 'rgba(128,128,128,0.25)',\n fontSize: 11,\n } as React.CSSProperties,\n upcomingDay: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 5, textAlign: 'center' } as React.CSSProperties,\n upcomingDayName: { opacity: 0.7, whiteSpace: 'nowrap' } as React.CSSProperties,\n upcomingTemps: { whiteSpace: 'nowrap' } as React.CSSProperties,\n compactStat: { display: 'flex', alignItems: 'center', gap: 6, whiteSpace: 'nowrap' } as React.CSSProperties,\n unitSwitch: { display: 'inline-flex', alignItems: 'center', cursor: 'pointer', userSelect: 'none' } as React.CSSProperties,\n rain: { color: '#2563eb', whiteSpace: 'nowrap' } as React.CSSProperties,\n tz: { fontSize: 12, opacity: 0.7, marginTop: 2 } as React.CSSProperties,\n list: { display: 'flex', flexDirection: 'column' } as React.CSSProperties,\n};\n\n// Precipitation chance is only surfaced once it represents a meaningful risk.\nconst RAIN_RISK_THRESHOLD = 2;\n\nfunction RainBadge({ probability, compact }: { probability?: number; compact?: boolean }) {\n if (probability === undefined || probability === null || probability <= RAIN_RISK_THRESHOLD) return null;\n return <span style={styles.rain}>{compact ? ` · ${probability}%` : ` · ${probability}% rain`}</span>;\n}\n\n/**\n * Renders one location's forecast: a live current time (ticking, in the\n * location's own timezone), a clickable degF / degC unit switch, high/low and\n * precipitation for the next hours and days.\n */\nfunction SingleWeatherForecast(props: Props) {\n // Temperature unit is local state so the degF / degC switch can toggle it;\n // it is seeded from (and re-synced to) the temperatureUnit prop.\n const [unit, setUnit] = useState<TemperatureUnit>(props.temperatureUnit ?? 'fahrenheit');\n useEffect(() => {\n setUnit(props.temperatureUnit ?? 'fahrenheit');\n }, [props.temperatureUnit]);\n\n const { data, loading, error } = useWeatherForecast({ ...props, temperatureUnit: unit });\n\n // A ticking clock so the displayed current time stays accurate to the second.\n const [now, setNow] = useState(() => new Date());\n useEffect(() => {\n const id = setInterval(() => setNow(new Date()), 1000);\n return () => clearInterval(id);\n }, []);\n\n const toggleUnit = () => setUnit((u) => (u === 'celsius' ? 'fahrenheit' : 'celsius'));\n\n const renderUnitSwitch = (fontSize: number) => (\n <span\n role=\"button\"\n tabIndex={0}\n onClick={(e) => {\n e.stopPropagation();\n toggleUnit();\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n toggleUnit();\n }\n }}\n title=\"Switch temperature units\"\n aria-label={`Switch temperature units, currently ${unit === 'celsius' ? 'Celsius' : 'Fahrenheit'}`}\n style={{ ...styles.unitSwitch, fontSize }}\n >\n <span style={{ fontWeight: unit === 'fahrenheit' ? 700 : 400, opacity: unit === 'fahrenheit' ? 1 : 0.45 }}>&deg;F</span>\n <span style={{ opacity: 0.4, margin: '0 4px' }}>|</span>\n <span style={{ fontWeight: unit === 'celsius' ? 700 : 400, opacity: unit === 'celsius' ? 1 : 0.45 }}>&deg;C</span>\n </span>\n );\n\n // Keep showing the last forecast while a unit switch refetches in the\n // background; only suppress output on the very first load.\n if (loading && !data) return null;\n if (error) return <div className={props.className} style={props.style}>Error: {error.message}</div>;\n if (!data) return <div className={props.className} style={props.style}>No forecast available.</div>;\n\n const timezone = data.location?.timezone;\n const currentDate = formatInZone(now, timezone, { weekday: 'short', month: 'short', day: 'numeric' });\n const currentTime = formatInZone(now, timezone, { hour: 'numeric', minute: '2-digit', second: '2-digit', timeZoneName: 'short' });\n // The compact layout shows the clock at a larger size, so the zone label is\n // split off onto the smaller date line to keep the time on one line.\n const currentClock = clockParts(now, timezone);\n const currentZone = zoneLabel(now, timezone);\n\n if (props.compact) {\n const today = data.daily[0];\n const windSpeedUnitLabel = WIND_SPEED_UNIT_LABELS[props.windSpeedUnit ?? 'mph'];\n // Today is already summarised in the card's own stats line, so the\n // upcoming-days section starts at tomorrow and shows the next three days.\n const upcomingDays = data.daily.slice(1, 4);\n\n return (\n <div className={props.className} style={{ ...styles.compactRoot, ...props.style }}>\n <div style={styles.compactBody}>\n <div style={styles.compactRow}>\n <div style={styles.compactCity}>\n {data.location?.city || 'Current location'}\n {data.location?.region ? `, ${data.location.region}` : ''}\n </div>\n\n <div style={styles.compactHeader}>\n <WeatherIcon condition={data.current.icon} width={30} height={30} title=\"Current weather\" />\n <strong style={styles.compactTemp}>{data.current.temperature}</strong>\n {renderUnitSwitch(13)}\n </div>\n </div>\n\n <div style={styles.compactRow}>\n <div>\n <div style={styles.compactTime}>\n {currentClock.main}\n {currentClock.tail && <span style={styles.compactSeconds}>{currentClock.tail}</span>}\n </div>\n <div style={styles.compactDate}>\n {currentDate}\n {currentZone ? ` · ${currentZone}` : ''}\n </div>\n </div>\n\n <div style={styles.compactStats}>\n {today && (\n <span style={styles.compactStat}>\n <WeatherIcon condition={today.icon} width={16} height={16} title=\"Today's high and low\" />\n {today.max}&deg; / {today.min}&deg;\n <RainBadge probability={today.precipitationProbabilityMax} compact />\n </span>\n )}\n <span style={styles.compactStat}>\n <WindIcon width={16} height={16} title=\"Wind speed\" />\n {data.current.windSpeed !== undefined ? `${Math.round(data.current.windSpeed)} ${windSpeedUnitLabel}` : '—'}\n </span>\n </div>\n </div>\n </div>\n\n {upcomingDays.length > 0 && (\n <div style={styles.upcoming}>\n {upcomingDays.map((day) => (\n <div key={day.date} style={styles.upcomingDay} title={upcomingDayDetail(day, windSpeedUnitLabel)}>\n <span style={styles.upcomingDayName}>{upcomingDayLabel(day.date)}</span>\n <WeatherIcon condition={day.icon} width={14} height={14} />\n <span style={styles.upcomingTemps}>\n <strong>{day.max}&deg;</strong>\n <span style={{ opacity: 0.6 }}> / {day.min}&deg;</span>\n </span>\n {day.precipitationProbabilityMax !== undefined && day.precipitationProbabilityMax > RAIN_RISK_THRESHOLD && (\n <span style={{ ...styles.rain, fontSize: 10 }}>{day.precipitationProbabilityMax}%</span>\n )}\n </div>\n ))}\n </div>\n )}\n </div>\n );\n }\n\n return (\n <div className={props.className} style={{ ...styles.root, ...props.style }}>\n <div>\n <h3 style={{ margin: 0, fontSize: 20 }}>\n {data.location?.city || 'Current location'}\n {data.location?.region ? `, ${data.location.region}` : ''}\n </h3>\n <div style={styles.tz}>\n {currentDate} &middot; {currentTime}\n {timezone ? ` · ${timezone}` : ''}\n </div>\n <div style={{ ...styles.row, marginTop: 8 }}>\n <WeatherIcon condition={data.current.icon} width={28} height={28} title=\"Current weather\" />\n <strong style={{ fontSize: 28 }}>{data.current.temperature}</strong>\n {renderUnitSwitch(14)}\n </div>\n </div>\n\n <div style={{ marginTop: 20 }}>\n <h4 style={{ margin: '0 0 10px 0' }}>Next hours</h4>\n <div style={styles.hours}>\n {data.hourly.slice(0, props.forecastHours ?? 12).map((hour) => (\n <div key={hour.time} style={styles.hourCard}>\n <div style={{ fontSize: 12 }}>{new Date(hour.time).toLocaleTimeString([], { hour: 'numeric' })}</div>\n <WeatherIcon condition={hour.icon} width={20} height={20} style={{ margin: '6px auto' }} />\n <div>{hour.temperature}&deg;</div>\n {hour.precipitationProbability !== undefined && hour.precipitationProbability > 0 && (\n <div style={{ ...styles.rain, fontSize: 12 }}>{hour.precipitationProbability}%</div>\n )}\n </div>\n ))}\n </div>\n </div>\n\n <div style={{ marginTop: 20 }}>\n <h4 style={{ margin: '0 0 10px 0' }}>Next days</h4>\n <div>\n {data.daily.map((day) => (\n <div key={day.date} style={styles.dayRow}>\n <div style={{ width: 120 }}>{new Date(day.date).toLocaleDateString([], { weekday: 'short', month: 'short', day: 'numeric' })}</div>\n <WeatherIcon condition={day.icon} width={20} height={20} />\n <div>\n {day.min}&deg; / {day.max}&deg;\n {day.precipitationProbabilityMax !== undefined && day.precipitationProbabilityMax > RAIN_RISK_THRESHOLD && (\n <span style={styles.rain}> &middot; {day.precipitationProbabilityMax}%</span>\n )}\n </div>\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n\n/**\n * Weather forecast widget. When `locations` is provided, one forecast is\n * rendered per location; otherwise a single auto-detected forecast is shown.\n */\nexport function WeatherForecast(props: Props) {\n const { locations, ...rest } = props;\n\n if (locations && locations.length > 0) {\n const gap = props.compact ? 8 : 16;\n return (\n <div style={{ ...styles.list, gap }}>\n {locations.map((loc, index) => (\n <SingleWeatherForecast\n key={`${loc.label ?? ''}:${loc.latitude ?? ''}:${loc.longitude ?? ''}:${index}`}\n {...rest}\n latitude={typeof loc.latitude === 'number' ? loc.latitude : undefined}\n longitude={typeof loc.longitude === 'number' ? loc.longitude : undefined}\n location={{ city: loc.label, timezone: loc.timezone }}\n />\n ))}\n </div>\n );\n }\n\n return <SingleWeatherForecast {...rest} />;\n}\n"],"mappings":"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","names":["res","data","jsx","jsx","useEffect","useState","jsx","jsxs","useState","useEffect"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "use-weather-forecast",
3
- "version": "0.1.86",
3
+ "version": "0.1.88",
4
4
  "description": "React weather forecast component using Open-Meteo and IP geolocation",
5
5
  "keywords": [
6
6
  "react",