weather_app/AGENTS.md
2026-07-24 01:53:36 +00:00

6.7 KiB
Raw Blame History

WeatherLens — Agent Context

Project Overview

A single-file, offline-capable weather app built with Svelte 5 (runes-based reactivity). Pulls data from the free Open-Meteo API (no API key required), persists user locations and settings in IndexedDB, and provides configurable weather alerts. The production build is a single dist/index.html with zero network dependencies — it works from file://, USB, or any static server. Weather data is fetched live from the CDN.

Architecture

src/
├── App.svelte                  # Root component — geolocation init, sidebar + main layout
├── main.js                     # Entry point, mounts App + imports CSS
├── components/
│   ├── LocationSidebar.svelte  # Saved locations list, inline geocoding search
│   ├── AddLocationDialog.svelte# Modal: geocoding search → add location
│   ├── CurrentWeather.svelte   # Hero card: current conditions, dynamic gradient bg
│   ├── HourlyForecast.svelte   # Horizontal scroll: next 24h with precip bars
│   ├── DailyForecast.svelte    # 7-day forecast cards with icons + detail
│   ├── WeatherDetail.svelte    # Expanded metrics: wind, atmosphere, sun/UV, temps
│   ├── NotificationBell.svelte  # Bell icon with badge; dropdown lists/dismisses alerts
│   └── SettingsDialog.svelte   # Units (metric/imperial), alert thresholds, data reset
├── lib/
│   ├── autofocus.js            # Svelte action for autofocus on mount
│   ├── api/
│   │   ├── weather.js          # Open-Meteo fetch functions (forecast + geocoding)
│   │   └── weather-codes.js    # WMO code → icon/description/severity mapping
│   ├── storage/
│   │   └── db.js               # IndexedDB layer (idb): locations, settings
│   └── stores/
│       ├── app.svelte.js       # AppStore: $state for locations, forecast, UI, settings
│       └── notifications.svelte.js # Alert generation from forecast data

Key Design Decisions

  • Svelte 5 runes — Use $state, $derived, $effect. Props-based event passing. No legacy Svelte events.
  • $state proxy pitfall — Svelte 5 $state wraps values in JavaScript Proxy objects. Any API that uses the structured clone algorithm (IndexedDB put()/add(), structuredClone(), postMessage(), history.pushState()) will throw "Proxy object could not be cloned" if given a $state value directly. To avoid this:
    • Never pass $state objects directly to IndexedDB. Always spread into a plain object first: db.put('store', { ...proxyObj }).
    • Never use structuredClone() on $state values. Use { ...obj, nested: { ...obj.nested } } or JSON.parse(JSON.stringify(obj)) for deep copies.
    • DB functions should sanitize inputs at the storage layer: JSON.parse(JSON.stringify(settings)) before db.put().
    • Reading from IndexedDB returns plain objects (no proxies), so reads are safe.
  • Open-Meteo API — Free forecast API (no key required). Two endpoints used:
    • api.open-meteo.com/v1/forecast — Current + hourly (24h) + daily (7 days) weather
    • geocoding-api.open-meteo.com/v1/search — City/location search by name
  • Weather state via emoji — All weather icons use Unicode emoji, so no icon fonts or image assets are needed. WMO codes map to emoji via weather-codes.js.
  • Dynamic backgrounds — CurrentWeather uses CSS gradient classes (weather-bg-clear, weather-bg-rain, weather-bg-storm, etc.) determined by weather severity.
  • Alert/notification systemnotifications.svelte.js analyzes forecast data each time it changes and generates alerts for: high precipitation, thunderstorms, extreme temps, high UV, strong winds. Alerts are shown only in the 🔔 bell dropdown. Dismissed alerts persist in IndexedDB and only auto-reset when the forecast date changes (so refreshing the same day won't resurrect cleared alerts).
  • Geolocation on first launch — If no locations are saved, the app requests browser geolocation and adds "Current Location" automatically. Falls back to manual "Add Location" if denied.
  • Locations persist in IndexedDB — Via idb. Two stores: locations and settings. Current location pinned at top of sidebar.
  • Single-file buildvite-plugin-singlefile inlines all JS/CSS; post-build script inlines favicon as base64 data URI.
  • Mobile-first responsive — Slide-over sidebar on mobile (same pattern as password manager). Bottom tab nav on mobile, desktop tab bar on wide screens. Touch-friendly with large tap targets.
  • No minificationminify: false in vite.config.js for readable single-file output.

Weather Alert System

The notification store checks each forecast refresh for:

Alert Type Trigger Severity
Thunderstorm (current) WMO code 65, 75, 82, 86, 95-99 active now danger
Thunderstorm (forecast) WMO code 65, 75, 82, 86, 95-99 in next 3 days danger
Precipitation precipitation_probability_max ≥ threshold (default 70%) warning
High Temperature temperature_2m_max > threshold (default 35°C) warning
Low Temperature temperature_2m_min < threshold (default 0°C) warning
UV Index uv_index_max ≥ threshold (default 6) warning
Wind wind_speed_10m_max ≥ threshold (default 40 km/h) warning

All thresholds are configurable in Settings.

Scripts

Command Description
npm run dev Vite dev server with HMR on :5173
npm run build Production build → dist/index.html (single self-contained file)
npm run preview Preview production build
npm run test Vitest (watch mode)
npm run test:run Vitest (single run)

Testing

  • Framework: Vitest with jsdom environment
  • Test setup: tests/setup.js (fake-indexeddb polyfill, Web Crypto API polyfill, mock matchMedia, mock navigator)
  • Test files:
    • tests/lib/api/weather-codes.test.js — WMO code → icon/desc/severity mapping, isSevereWeather
    • tests/lib/storage/db.test.js — Locations CRUD (add, update, delete, setCurrent, clear), settings CRUD with defaults
    • tests/lib/stores/app.test.js — AppStore initialization and derived selectedLocation
    • tests/lib/stores/notifications.test.js — Alert generation for all alert types, dismissal, reset, day filtering
  • Run with npm run test or npm run test:run

API Notes

  • Open-Meteo is free and requires no API key.
  • Forecast parameters: current (10 metrics), hourly (4 metrics × time), daily (8 metrics × 7 days)
  • Geocoding API returns up to 8 results per query with name, coordinates, country, admin1, timezone.
  • Rate limits are generous (~10,000 requests/day for the free tier).
  • Used only via fetch() in browser — no server-side API calls needed.