diff --git a/public/index.html b/public/index.html index 72ca4ee..d4f29b9 100644 --- a/public/index.html +++ b/public/index.html @@ -1455,9 +1455,25 @@ // ============================================ // SOLAR IMAGE COMPONENT // ============================================ - const SolarImage = () => { + // Combined Solar Panel - toggleable between image and indices + const SolarPanel = ({ solarIndices }) => { + const [showIndices, setShowIndices] = useState(() => { + try { + const saved = localStorage.getItem('openhamclock_solarPanelMode'); + return saved === 'indices'; + } catch (e) { return false; } + }); const [imageType, setImageType] = useState('0193'); // AIA 193 (corona) + // Save preference + const toggleMode = () => { + const newMode = !showIndices; + setShowIndices(newMode); + try { + localStorage.setItem('openhamclock_solarPanelMode', newMode ? 'indices' : 'image'); + } catch (e) {} + }; + // SDO/AIA image types const imageTypes = { '0193': { name: 'AIA 193Å', desc: 'Corona' }, @@ -1468,70 +1484,231 @@ }; // SDO images update every ~15 minutes - const timestamp = Math.floor(Date.now() / 900000) * 900000; // Round to 15 min + const timestamp = Math.floor(Date.now() / 900000) * 900000; const imageUrl = `https://sdo.gsfc.nasa.gov/assets/img/latest/latest_256_${imageType}.jpg?t=${timestamp}`; + // Kp color helper + const getKpColor = (value) => { + if (value >= 7) return '#ff0000'; + if (value >= 5) return '#ff6600'; + if (value >= 4) return '#ffcc00'; + if (value >= 3) return '#88cc00'; + return '#00ff88'; + }; + + const getKpLabel = (value) => { + if (value >= 7) return 'SEVERE'; + if (value >= 5) return 'STORM'; + if (value >= 4) return 'ACTIVE'; + if (value >= 3) return 'UNSETTLED'; + return 'QUIET'; + }; + return (
+ {/* Header with toggle */}
- ☀ SOLAR - + + ☀ {showIndices ? 'SOLAR INDICES' : 'SOLAR'} + +
+ {!showIndices && ( + + )} + +
-
- Current Sun + {solarIndices?.data ? ( +
+ {/* SFI Row */} +
+
+
SFI
+
+ {solarIndices.data.sfi?.current || '--'} +
+
+
+ {solarIndices.data.sfi?.history?.length > 0 && ( + + {(() => { + const data = solarIndices.data.sfi.history.slice(-20); + const values = data.map(d => d.value); + const max = Math.max(...values, 1); + const min = Math.min(...values, 0); + const range = max - min || 1; + const points = data.map((d, i) => { + const x = (i / (data.length - 1)) * 100; + const y = 30 - ((d.value - min) / range) * 28; + return `${x},${y}`; + }).join(' '); + return ; + })()} + + )} +
30 day history
+
+
+ + {/* SSN Row */} +
+
+
Sunspots
+
+ {solarIndices.data.ssn?.current || '--'} +
+
+
+ {solarIndices.data.ssn?.history?.length > 0 && ( + + {(() => { + const data = solarIndices.data.ssn.history; + const values = data.map(d => d.value); + const max = Math.max(...values, 1); + const min = Math.min(...values, 0); + const range = max - min || 1; + const points = data.map((d, i) => { + const x = (i / (data.length - 1)) * 100; + const y = 30 - ((d.value - min) / range) * 28; + return `${x},${y}`; + }).join(' '); + return ; + })()} + + )} +
12 month history
+
+
+ + {/* Kp Row */} +
+
+
Kp Index
+
+ {solarIndices.data.kp?.current?.toFixed(1) || '--'} +
+
+ {solarIndices.data.kp?.current !== null ? getKpLabel(solarIndices.data.kp?.current) : ''} +
+
+
+ {solarIndices.data.kp?.history?.length > 0 && ( + + {(() => { + const hist = solarIndices.data.kp.history.slice(-16); + const forecast = (solarIndices.data.kp.forecast || []).slice(0, 8); + const allData = [...hist, ...forecast]; + const barWidth = 100 / allData.length; + return allData.map((d, i) => { + const isForecast = i >= hist.length; + const barHeight = (d.value / 9) * 32; + return ( + + ); + }); + })()} + + )} +
3 day history + forecast
+
+
+
+ ) : ( +
+ Loading solar data... +
+ )} +
+ NOAA/SWPC +
+
+ ) : ( + /* Solar Image View */ +
+
{ - e.target.style.display = 'none'; - }} - /> -
-
- SDO/AIA • Live from NASA -
+ paddingBottom: '100%', + background: '#000', + borderRadius: '50%', + overflow: 'hidden' + }}> + Current Sun { + e.target.style.display = 'none'; + }} + /> +
+
+ SDO/AIA • Live from NASA +
+
+ )} ); }; @@ -2425,198 +2602,6 @@ ); }; - // ============================================ - // SOLAR INDICES PANEL - // ============================================ - const SolarIndicesPanel = ({ data, loading }) => { - if (loading || !data) { - return ( -
-
☀️ Solar Indices
-
- Loading solar data... -
-
- ); - } - - const { sfi, kp, ssn } = data; - - // Get Kp color based on value - const getKpColor = (value) => { - if (value >= 7) return '#ff0000'; // Severe storm - if (value >= 5) return '#ff6600'; // Storm - if (value >= 4) return '#ffcc00'; // Active - if (value >= 3) return '#88cc00'; // Unsettled - return '#00ff88'; // Quiet - }; - - // Get Kp label - const getKpLabel = (value) => { - if (value >= 7) return 'SEVERE'; - if (value >= 5) return 'STORM'; - if (value >= 4) return 'ACTIVE'; - if (value >= 3) return 'UNSETTLED'; - return 'QUIET'; - }; - - // Mini sparkline chart component - const Sparkline = ({ data, color, height = 30, showForecast = false, forecastData = [] }) => { - if (!data || data.length === 0) return null; - - const allData = showForecast ? [...data, ...forecastData] : data; - const values = allData.map(d => d.value); - const max = Math.max(...values, 1); - const min = Math.min(...values, 0); - const range = max - min || 1; - - const width = 100; - const points = allData.map((d, i) => { - const x = (i / (allData.length - 1)) * width; - const y = height - ((d.value - min) / range) * height; - return `${x},${y}`; - }).join(' '); - - // Split point between history and forecast - const historyEndIndex = data.length - 1; - const historyPoints = data.map((d, i) => { - const x = (i / (allData.length - 1)) * width; - const y = height - ((d.value - min) / range) * height; - return `${x},${y}`; - }).join(' '); - - const forecastPoints = showForecast && forecastData.length > 0 ? - [data[data.length - 1], ...forecastData].map((d, i) => { - const x = ((historyEndIndex + i) / (allData.length - 1)) * width; - const y = height - ((d.value - min) / range) * height; - return `${x},${y}`; - }).join(' ') : ''; - - return ( - - {/* History line */} - - {/* Forecast line (dashed) */} - {showForecast && forecastPoints && ( - - )} - - ); - }; - - // Mini bar chart for Kp - const KpBars = ({ history, forecast }) => { - const allData = [...(history || []), ...(forecast || []).slice(0, 8)]; - if (allData.length === 0) return null; - - const barWidth = 100 / allData.length; - const height = 35; - - return ( - - {allData.map((d, i) => { - const isForecast = i >= (history?.length || 0); - const barHeight = (d.value / 9) * height; - return ( - - ); - })} - {/* Divider line between history and forecast */} - {forecast && forecast.length > 0 && history && ( - - )} - - ); - }; - - return ( -
-
- ☀️ Solar Indices - NOAA/SWPC -
- -
- {/* SFI */} -
-
SFI
-
- {sfi.current || '--'} -
-
- -
-
30 day history
-
- - {/* SSN */} -
-
Sunspots
-
- {ssn.current || '--'} -
-
- -
-
12 month history
-
- - {/* Kp Index */} -
-
Kp Index
-
- - {kp.current !== null ? kp.current.toFixed(1) : '--'} - - - {kp.current !== null ? getKpLabel(kp.current) : ''} - -
-
- -
-
- 3 day history + forecast -
-
-
-
- ); - }; - // ============================================ // LEGACY LAYOUT (Classic HamClock Style) // ============================================ @@ -3763,14 +3748,11 @@ - {/* Solar Image */} - + {/* Solar Panel (toggleable between image and indices) */} + {/* VOACAP Propagation - Toggleable */} - - {/* Solar Indices with History */} - {/* CENTER - MAP */}