- nws.js: new fetchActiveAdvisories() hits the official NWS alerts feed
(api.weather.gov /alerts/active?point=lat,lon) and normalizes each advisory
into the app's alert shape, tagged source:'nws'. Adds classifyAdvisoryEvent
(maps NWS events -> app buckets: heat/wind/cold/storm/precip, else advisory),
nwsSeverity (Extreme/Severe->danger etc.), advisoryId (stable short id from
the URN), advisoryIcon, and local effectiveDate/expiresDate for dedup.
- notifications store: cached _nwsAdvisories + refreshAdvisories() (best
effort, cached 5 min, non-US/no-alerts safe). analyze() now merges
advisories via new pure mergeAdvisories(): an advisory that is officially
in force on a date supersedes the derived forecast alert of the same type
on that date, so the official NWS warning replaces the forecast guess. Both
are dismissible independently (nws-* vs forecast-* ids).
- New per-type toggle "advisory" (defaults on) across app/default settings and
SettingsDialog, so official advisories are configurable like every other
alert and the bell shows them with their own severity/icon.
- Wired: App.svelte calls refreshAdvisories() alongside analyze() on forecast
load and changes.
- Tests: 12 advisory tests (classifier, severity, id, normalize, dedup incl.
disable toggle) + 3 store integration tests (overlap dedup, non-overlap kept,
toggle-off). 107 total pass.
- Verified end-to-end in headless Chromium against the live NWS feed for
Rogers County, OK: the current Heat Advisory appears in the bell, and the
derived heat alerts for the days it covers are suppressed while a
non-overlapping day's forecast heat alert remains.
- New src/lib/api/moon.js: fetches daily moon_phase from Open-Meteo
(independent of the NWS/Open-Meteo weather source, since NWS has no moon
data), plus pure helpers: moonName (8 phases), illumination fraction, and
nextMoonPhase, which unwraps the ~29.5-day cycle and interpolates the next
quarter boundary (new/first quarter/full/last quarter), returning its name,
date, and days until it.
- New MoonPhaseCard.svelte: renders an SVG moon (two-circle terminator that
correctly shows crescent/quarter/gibbous shapes), the current phase name,
percent illuminated, and the next phase with date. Caches per-location via
an effect; degrades gracefully when unavailable.
- Wired into WeatherDetail between Sun and UV and Temperature Range.
- 11 unit tests for moonName/illumination/interpolation/nextMoonPhase.
- Verified in headless Chromium: card renders, moon shape correct, no errors.
- Lightning layer on PrecipitationRadar: toggleable ⚡ button (persisted via
showLightning setting). When on, fetches recent strikes from the planned CORS
relay and renders them as markers that pan/zoom with the slippy map.
- New src/lib/api/lightning.js: fetchLightningStrikes() reads
https://cors.thecookiejar.me/lightning?lat&lon&radiusKm&limit (relay NOT
deployed yet) with an 8s abort timeout and graceful failure - never blocks or
crashes the map, shows an unavailable banner instead. Exports pure
projectStrikeToLayer() for Mercator tile-layer marker projection.
- showLightning setting added to DEFAULT_SETTINGS + app store; toggle persists.
- Tests: fetch URL/parsing/error handling + layer projection. 71 total pass.