From 85c415d1f54e914dd62ebb6828b1ecd98833ddab Mon Sep 17 00:00:00 2001 From: fraxle Date: Mon, 18 May 2026 23:02:41 +0100 Subject: [PATCH] 1.5.7 Profile memory Comment cleanup --- assets/js/app.js | 161 +++++------------- assets/js/components.js | 166 +++++++++--------- assets/js/compute.js | 94 +++++------ assets/js/config.js | 42 ++--- assets/js/events.js | 24 +-- assets/js/events/almanac-calendar.js | 14 +- assets/js/events/cosmic-calendar.js | 18 +- assets/js/events/dynamic-message.js | 12 +- assets/js/events/lens-overlay.js | 8 +- assets/js/events/weather-checks.js | 12 +- assets/js/hooks/useColumnPopup.js | 30 ++-- assets/js/hooks/useForecast.js | 18 +- assets/js/hooks/useTableScroll.js | 38 ++--- assets/js/main.js | 6 +- assets/js/physics.js | 242 +++++++++++++-------------- assets/js/utils.js | 200 +++++++++++----------- 16 files changed, 504 insertions(+), 581 deletions(-) diff --git a/assets/js/app.js b/assets/js/app.js index 6aa7595..495497f 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -261,13 +261,20 @@ export function UTCIForecast() { // (FREE_DAYS, FILTER_PROFILES and OUTDOORS_VARIANTS now live in ./config.js.) - // Current active filter profile - const [activeProfile, setActiveProfile] = useState('basic'); + // Current active filter profile - persisted in localStorage + const [activeProfile, setActiveProfile] = useState(() => { + try { return localStorage.getItem('sunscope_profile') || 'basic'; } catch (e) { return 'basic'; } + }); // Which columns appear in the hourly table by default. // true = visible on first load (and the only ones free users see) // false = hidden by default (Pro users can toggle these on) - const [visibleCols, setVisibleCols] = useState({ ...FILTER_PROFILES.basic.cols }); + const [visibleCols, setVisibleCols] = useState(() => { + try { + const saved = localStorage.getItem('sunscope_profile') || 'basic'; + return { ...(FILTER_PROFILES[saved]?.cols ?? FILTER_PROFILES.basic.cols) }; + } catch (e) { return { ...FILTER_PROFILES.basic.cols }; } + }); const toggleCol = (col) => setVisibleCols(prev => ({ ...prev, [col]: !prev[col] })); // Skin type for the sunburn-time column. Fitzpatrick II is typical UK fair. @@ -276,8 +283,14 @@ export function UTCIForecast() { // Vehicle type for the cabin heat column. 'car' is the default preset. const [vehicleType, setVehicleType] = useState('car'); - // Places profile sub-variant (urban / beach / events / festival / wintersports / naturist) - const [outdoorsVariant, setOutdoorsVariant] = useState('urban'); + // Places profile sub-variant - persisted in localStorage + const [outdoorsVariant, setOutdoorsVariant] = useState(() => { + try { return localStorage.getItem('sunscope_outdoors_variant') || 'urban'; } catch (e) { return 'urban'; } + }); + const setOutdoorsVariantAndSave = (v) => { + try { localStorage.setItem('sunscope_outdoors_variant', v); } catch (e) { /* ignore */ } + setOutdoorsVariant(v); + }; // Vehicle ventilation — true = windows open (high convective loss). const [vehicleVent, setVehicleVent] = useState(false); @@ -287,7 +300,13 @@ export function UTCIForecast() { // 'off' = hidden, 'on' = indoorT shown (managed or not depending on indoorManaged). const [buildingType, setBuildingType] = useState('brick'); const [indoorManaged, setIndoorManaged] = useState(false); - const [indoorMode, setIndoorMode] = useState('off'); + const [indoorMode, setIndoorMode] = useState(() => { + try { + const saved = localStorage.getItem('sunscope_profile') || 'basic'; + const cols = FILTER_PROFILES[saved]?.cols ?? FILTER_PROFILES.basic.cols; + return (cols['indoorT'] || cols['managedT']) ? 'on' : 'off'; + } catch (e) { return 'off'; } + }); // Pollen type for the pollen column. Persisted in localStorage. const [pollenType, setPollenType] = useState(() => { @@ -304,6 +323,7 @@ export function UTCIForecast() { const activateProfile = (key) => { const profile = FILTER_PROFILES[key]; + try { localStorage.setItem('sunscope_profile', key); } catch (e) { /* ignore */ } setActiveProfile(key); if (key !== 'custom') { setVisibleCols({ ...profile.cols }); @@ -544,7 +564,7 @@ export function UTCIForecast() {
- +
${activeEvents.length > 0 && (() => { const ev = activeEvents[bannerIndex] || activeEvents[0]; @@ -626,24 +646,7 @@ export function UTCIForecast() { glob=${currentRow?.glob ?? 0} activeEvent=${lensEvent} /> - {/* ── FUTURE FEATURE v2: Historical Context Line ───────────────── - Show a subtle single line directly below the dial readout: - e.g. "3.2°C above the May average for this location" - "Near normal for late August" - - Design notes: - • Must be visually elegant — same Fraunces/Manrope type pairing - as the rest of the dial area, small and muted - • Positive delta: warm amber tone; negative delta: cool blue tone - • Source: Open-Meteo has a free /climate endpoint that returns - monthly climate normals (ERA5 reanalysis) for any lat/lon. - Fetch once on location change, cache in a ref. Compare today's - peak air temp (or UTCI) to that month's normal. - • Could also show min/max historical context for the week: - "Warmest day forecast this week" / "Coolest night since March" - • The fetch is separate from the main forecast — handle its own - loading/error state independently so it doesn't block the UI. - ─────────────────────────────────────────────────────────────── */} +
@@ -694,12 +697,7 @@ export function UTCIForecast() { ${forecast && days.length > 0 && html` <${Fragment}> - +
- + ${proPromptDay !== null && days[proPromptDay] && (() => { const promptDate = new Date(days[proPromptDay].key + 'T00:00Z'); const dayName = promptDate.toLocaleDateString('en-GB', { weekday: 'long', timeZone: 'UTC' }); @@ -1001,13 +993,7 @@ export function UTCIForecast() {
`; })()} - +
Profile: ${profileButtonOrder.slice(0, 3).map((key) => { @@ -1048,8 +1034,9 @@ export function UTCIForecast() { setProPromptDay(0); return; } + try { localStorage.setItem('sunscope_profile', 'outdoors'); } catch (e) { /* ignore */ } setActiveProfile('outdoors'); - setOutdoorsVariant(v); + setOutdoorsVariantAndSave(v); setVisibleCols({ ...OUTDOORS_VARIANTS[v].cols }); setIndoorMode('off'); setIndoorManaged(false); @@ -1077,8 +1064,9 @@ export function UTCIForecast() { setProPromptDay(0); return; } + try { localStorage.setItem('sunscope_profile', 'outdoors'); } catch (e) { /* ignore */ } setActiveProfile('outdoors'); - setOutdoorsVariant(v); + setOutdoorsVariantAndSave(v); setVisibleCols({ ...OUTDOORS_VARIANTS[v].cols }); setIndoorMode('off'); setIndoorManaged(false); @@ -1108,12 +1096,7 @@ export function UTCIForecast() {
- + ${(isPro || activeCols['burn'] || activeCols['vehicleT'] || activeCols['indoorT'] || activeCols['managedT'] || activeCols['pollen']) && html`
Columns: @@ -1244,68 +1227,13 @@ ${isPro && (activeProfile === 'custom' || activeCols['air']) && html`