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

98 lines
6.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 system** — `notifications.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 build** — `vite-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 minification** — `minify: 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.