import { persisted } from 'svelte-persisted-store'; export interface GeoLocation { id: number; name: string; latitude: number; longitude: number; elevation: number; feature_code: string; country_code: string | undefined; admin1_id: number | undefined; admin3_id?: number | undefined; admin4_id?: number | undefined; timezone: string; population: number | undefined; postcodes: string[] | undefined; country_id: number | undefined; country: string | undefined; admin1: string | undefined; admin3?: string | undefined; admin4?: string | undefined; } export const defaultLocation: GeoLocation = { id: 2950159, name: 'Berlin', latitude: 52.52437, longitude: 13.41053, elevation: 74, feature_code: 'PPLC', country_code: 'DE', admin1_id: 2950157, admin3_id: 6547383, admin4_id: 6547539, timezone: 'Europe/Berlin', population: 3426354, postcodes: ['10967', '13347'], country_id: 2921044, country: 'Germany', admin1: 'Land Berlin', admin3: 'Berlin, Stadt', admin4: 'Berlin' }; export const storedLocation = persisted('stored_location', defaultLocation as GeoLocation); export type Theme = 'system' | 'light' | 'dark'; export const storedTheme = persisted('theme', 'system'); /** Selected forecast model, shared across the whole site. */ export const storedModel = persisted('selected_model', 'best_match'); /** Which variables are visible in the hourly table and the meteograms. */ export interface VariablePrefs { table: Record; charts: Record; } export const defaultVariablePrefs: VariablePrefs = { table: { icons: true, temperature: true, feels: true, wind: true, humidity: true, clouds: true, precipitation: true, // extra rows, off by default dew_point: false, gusts: false, pressure: false, uv: false, visibility: false, snowfall: false }, charts: { temperature: true, cloud_cover: true, precipitation: true, precipitation_probability: true, wind: true, humidity: true } }; export const storedVariablePrefs = persisted('variable_prefs', defaultVariablePrefs); /** Order of the hourly-table rows (visibility is the separate toggle above). */ export const defaultTableRowOrder: string[] = [ 'icons', 'temperature', 'feels', 'dew_point', 'wind', 'gusts', 'humidity', 'clouds', 'pressure', 'uv', 'visibility', 'precipitation', 'snowfall' ]; export const storedTableRowOrder = persisted('table_row_order_v1', defaultTableRowOrder); /** * Normalizes a stored row order against the known rows: drops keys that no * longer exist and appends new rows (added after the user last saved) at the * end, so stored orders survive app updates. */ export function mergeTableRowOrder(stored: string[]): string[] { return [ ...stored.filter((k) => defaultTableRowOrder.includes(k)), ...defaultTableRowOrder.filter((k) => !stored.includes(k)) ]; } /** Hourly table interval: 3-hourly (default) or 1-hourly. */ export const storedHourlyInterval = persisted<1 | 3>('hourly_interval', 3); /** * Meteogram layout: an ordered list of chart panels, each holding an ordered * list of variable keys (see the chart variable registry). Users drag * variables between panels to fully customise the meteograms. */ export interface ChartPanel { id: string; variables: string[]; } export const defaultChartLayout: ChartPanel[] = [ { id: 'panel-1', variables: ['temperature', 'weather_icons'] }, { id: 'panel-2', variables: ['precipitation', 'precipitation_probability', 'cloud_cover'] }, { id: 'panel-3', variables: ['wind', 'wind_gusts', 'wind_direction'] } ]; export const storedChartLayout = persisted('chart_layout_v1', defaultChartLayout); /** * Time range the meteograms open on. 'auto' narrows to three days on a phone, * where a full week of hours is too dense to read, and shows everything on * roomier screens. */ export type ChartRangePref = 'auto' | 'today' | '3d' | '5d' | 'all'; export const storedChartRange = persisted('chart_range_v1', 'auto'); /** Selected ensemble model for the 14-day spread forecast. */ export const storedEnsembleModel = persisted('ensemble_model', 'ncep_gefs_seamless'); /** Reanalysis used on the historical page, and the seasonal model. */ export const storedArchiveModel = persisted('archive_model', 'best_match'); export const storedSeasonalModel = persisted('seasonal_model', 'best_match'); /** Measurement units, shared across every forecast page and persisted. */ export interface UnitPrefs { temperature_unit: 'celsius' | 'fahrenheit'; wind_speed_unit: 'kmh' | 'ms' | 'mph' | 'kn'; precipitation_unit: 'mm' | 'inch'; } export const defaultUnits: UnitPrefs = { temperature_unit: 'celsius', wind_speed_unit: 'kmh', precipitation_unit: 'mm' }; export const storedUnits = persisted('units_v1', defaultUnits); /** Recently visited and starred locations, shown in the search dropdown. */ export const storedRecentLocations = persisted('recent_locations_v1', []); export const storedFavoriteLocations = persisted('favorite_locations_v1', []); /** Stable key for de-duping locations (geocoding id, or rounded coordinates). */ export function locationKey(l: GeoLocation): string { return l.id && l.id !== 0 ? `id:${l.id}` : `c:${l.latitude.toFixed(3)},${l.longitude.toFixed(3)}`; }