Upgrade maps to MapLibre GL JS; compact header; move search to settings

- Replace Leaflet with MapLibre GL JS v4.7.1 across all map instances
- CARTO dark/light and Esri satellite raster basemaps (no API key)
- All overlays (hurricane, wildfire, SPC, wind arrows) ported to GeoJSON sources/layers
- AQI, alert, temp, sun/moon markers via maplibregl.Marker with custom HTML
- Radar animation pre-loads all frames on load for smoother playback
- Compact loc-header: tighter padding and font sizes, all info preserved
- Search bar moved from header to Settings tab under Location section
- Service worker bumped to weather-v7

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Nate EmmertandClaude Sonnet 4.6 committed 2026-08-14 11:04:32 -04:00
1 parent 1863f6784b
commit a25864d9ed
6 files changed
+1732 -343

No files matched your search

+170 -47
View File
@@ -12,10 +12,12 @@
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="NeCloud Weather">
<meta name="mobile-web-app-capable" content="yes">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<link rel="stylesheet" href="https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="offline-banner" class="offline-banner hidden">No internet connection — showing cached data</div>
<div id="ptr-indicator" class="ptr-indicator hidden">↓ Refreshing…</div>
<div class="app">
<header class="masthead">
@@ -23,25 +25,13 @@
<nav class="tabs">
<button class="tab active" data-tab="conditions">Conditions</button>
<button class="tab" data-tab="unified-map">Map</button>
<button class="tab" data-tab="alerts">Alerts</button>
<button class="tab" data-tab="alerts">Alerts<span class="alert-badge hidden" id="alert-badge-nav"></span></button>
<button class="tab" data-tab="learn">Learn</button>
<button class="tab" data-tab="settings">Settings</button>
</nav>
<span class="tagline">Open-Meteo · RainViewer</span>
</header>
<!-- Search bar (shared, always visible) -->
<div class="search-wrap">
<div class="search-row">
<input id="search-input" type="text" placeholder="City, region, or country…" autocomplete="off" spellcheck="false">
<button id="search-btn">Search</button>
<button id="locate-btn" title="Use my location">⌖</button>
<button id="units-btn" title="Toggle units">US</button>
</div>
<ul id="suggestions" class="suggestions hidden"></ul>
<p id="error-msg" class="error hidden"></p>
</div>
<!-- Loading -->
<div id="loading" class="loading hidden">
<div class="loading-container">
@@ -123,38 +113,44 @@
</div>
</div>
<!-- ── WIND & ATMOSPHERE ── -->
<div class="section-head">
<span class="section-label">Wind &amp; Atmosphere</span>
</div>
<div class="card-group">
<div class="stat-card">
<div class="stat-label">Wind</div>
<div class="stat-val"><span id="cur-wind">--</span> <span class="stat-unit wind-unit">mph</span></div>
<div class="stat-sub" id="cur-wind-dir">--</div>
<!-- ── WIND & ATMOSPHERE (collapsed by default on mobile) ── -->
<details class="details-drawer wind-drawer" id="wind-drawer">
<summary class="drawer-summary">
Wind &amp; Atmosphere
<span class="wind-drawer-peek" id="wind-drawer-peek"></span>
<span class="drawer-chevron">›</span>
</summary>
<div class="drawer-body">
<div class="card-group drawer-card-group">
<div class="stat-card">
<div class="stat-label">Wind</div>
<div class="stat-val"><span id="cur-wind">--</span> <span class="stat-unit wind-unit">mph</span></div>
<div class="stat-sub" id="cur-wind-dir">--</div>
</div>
<div class="stat-card">
<div class="stat-label">Gusts</div>
<div class="stat-val"><span id="cur-gust">--</span> <span class="stat-unit gust-unit">mph</span></div>
</div>
<div class="stat-card">
<div class="stat-label">Humidity</div>
<div class="stat-val"><span id="cur-hum">--</span><span class="stat-unit">%</span></div>
<div class="stat-sub" id="hum-feel"></div>
</div>
<div class="stat-card">
<div class="stat-label">Dewpoint</div>
<div class="stat-val"><span id="cur-dew">--</span>°</div>
</div>
<div class="stat-card">
<div class="stat-label">Pressure</div>
<div class="stat-val"><span id="cur-pres">--</span> <span class="stat-unit">hPa</span></div>
</div>
<div class="stat-card">
<div class="stat-label">Visibility</div>
<div class="stat-val"><span id="cur-vis">--</span> <span class="stat-unit vis-unit">mi</span></div>
</div>
</div>
</div>
<div class="stat-card">
<div class="stat-label">Gusts</div>
<div class="stat-val"><span id="cur-gust">--</span> <span class="stat-unit gust-unit">mph</span></div>
</div>
<div class="stat-card">
<div class="stat-label">Humidity</div>
<div class="stat-val"><span id="cur-hum">--</span><span class="stat-unit">%</span></div>
<div class="stat-sub" id="hum-feel"></div>
</div>
<div class="stat-card">
<div class="stat-label">Dewpoint</div>
<div class="stat-val"><span id="cur-dew">--</span>°</div>
</div>
<div class="stat-card">
<div class="stat-label">Pressure</div>
<div class="stat-val"><span id="cur-pres">--</span> <span class="stat-unit">hPa</span></div>
</div>
<div class="stat-card">
<div class="stat-label">Visibility</div>
<div class="stat-val"><span id="cur-vis">--</span> <span class="stat-unit vis-unit">mi</span></div>
</div>
</div>
</details>
<!-- ── MORE DETAILS DRAWER ── -->
<details class="details-drawer">
@@ -211,11 +207,15 @@
<!-- Hourly -->
<div class="section-head" style="margin-top:1.5rem">
<span class="section-label">Hourly Forecast</span>
<span class="section-note">next 24 hours</span>
<span class="section-note">
<button id="hourly-feels-toggle" class="link-btn">Feels-like</button>
<button id="hourly-extend-btn" class="link-btn">48h</button>
</span>
</div>
<div id="hourly-scroll" class="hourly-scroll">
<div id="hourly-track" class="hourly-track"></div>
</div>
<div id="precip-chart-wrap" class="precip-chart-wrap"></div>
<!-- 7-day -->
<div class="section-head">
@@ -224,6 +224,18 @@
</div>
<div id="daily-list" class="daily-list"></div>
<details class="details-drawer discussion-drawer" id="discussion-drawer">
<summary class="drawer-summary">
Forecaster's Discussion
<span class="discussion-office" id="discussion-office"></span>
<span class="drawer-chevron">›</span>
</summary>
<div class="drawer-body">
<p class="discussion-meta" id="discussion-meta"></p>
<pre class="discussion-text" id="discussion-text">Loading…</pre>
</div>
</details>
<div class="footer-note">
Weather: <a href="https://open-meteo.com" target="_blank">Open-Meteo</a> ·
Radar: <a href="https://www.rainviewer.com" target="_blank">RainViewer</a> ·
@@ -307,6 +319,27 @@
<span class="toggle-slider"></span>
</span>
</label>
<label class="toggle-control">
<span class="toggle-label">🌀 Hurricanes</span>
<span class="switch-toggle">
<input type="checkbox" id="toggle-hurricane" data-layer="hurricane">
<span class="toggle-slider"></span>
</span>
</label>
<label class="toggle-control">
<span class="toggle-label">🔥 Wildfires</span>
<span class="switch-toggle">
<input type="checkbox" id="toggle-wildfire" data-layer="wildfire">
<span class="toggle-slider"></span>
</span>
</label>
<label class="toggle-control">
<span class="toggle-label">🌪️ SPC Reports</span>
<span class="switch-toggle">
<input type="checkbox" id="toggle-spc" data-layer="spc">
<span class="toggle-slider"></span>
</span>
</label>
</div>
</div>
</div>
@@ -369,6 +402,7 @@
<!-- ── TAB: ALERTS ── -->
<div id="tab-alerts" class="tab-panel hidden">
<div id="hurricane-panel" class="hurricane-panel"></div>
<div id="alerts-wrap" class="alerts-wrap hidden">
<div class="section-head">
<span class="section-label">Weather Alerts</span>
@@ -386,6 +420,20 @@
<!-- ── TAB: SETTINGS ── -->
<div id="tab-settings" class="tab-panel hidden">
<div class="settings-wrap">
<!-- Location search — moved here from header -->
<div class="settings-section-label">Location</div>
<div class="search-wrap settings-search">
<div class="search-row">
<input id="search-input" type="text" placeholder="City, region, or country…" autocomplete="off" spellcheck="false">
<button id="search-btn">Search</button>
<button id="locate-btn" title="Use my location">⌖</button>
<button id="units-btn" title="Toggle units">US</button>
</div>
<ul id="suggestions" class="suggestions hidden"></ul>
<p id="error-msg" class="error hidden"></p>
</div>
<div class="saved-wrap">
<div class="saved-head">
<span>Saved locations</span>
@@ -411,6 +459,80 @@
<span class="settings-label">Units</span>
<button id="units-btn-settings" class="settings-btn" type="button">US</button>
</div>
<div class="settings-row">
<span class="settings-label">Theme</span>
<div class="theme-toggle-group">
<button class="theme-btn" data-theme="system">System</button>
<button class="theme-btn" data-theme="light">Light</button>
<button class="theme-btn" data-theme="dark">Dark</button>
</div>
</div>
<div class="settings-section-title">Push Notifications (ntfy)</div>
<div class="settings-row settings-row--block">
<div class="settings-row-head">
<span class="settings-label">ntfy Topic URL</span>
<button id="ntfy-test-btn" class="settings-btn" type="button">Test</button>
</div>
<input id="ntfy-url-input" class="settings-input" type="url"
placeholder="https://ntfy.sh/my-weather-alerts"
autocomplete="off" spellcheck="false">
<p class="settings-help">
Receive alerts on any device via the <a href="https://ntfy.sh" target="_blank" rel="noopener">ntfy app</a>.
Enter your topic URL — alerts will be pushed here whenever new weather warnings are found.
</p>
<p id="ntfy-status" class="settings-help hidden"></p>
</div>
<div class="settings-section-title">Web Push (Browser Notifications)</div>
<div class="settings-row settings-row--block">
<div class="settings-row-head">
<span class="settings-label">Push alerts for saved location</span>
<button id="push-subscribe-btn" class="settings-btn" type="button">Enable</button>
</div>
<p id="push-status" class="settings-help hidden"></p>
</div>
<div class="settings-section-title">Alert Thresholds</div>
<div class="settings-row settings-row--block">
<div class="settings-row-head">
<label class="settings-label" for="threshold-wind">Wind gust alert (mph)</label>
<input id="threshold-wind" class="settings-input settings-input--short" type="number" min="0" max="200" placeholder="e.g. 40">
</div>
<div class="settings-row-head" style="margin-top:0.5rem">
<label class="settings-label" for="threshold-temp-low">Freeze alert below (°F)</label>
<input id="threshold-temp-low" class="settings-input settings-input--short" type="number" min="-60" max="60" placeholder="e.g. 32">
</div>
<div class="settings-row-head" style="margin-top:0.5rem">
<label class="settings-label" for="threshold-aqi">AQI alert above</label>
<input id="threshold-aqi" class="settings-input settings-input--short" type="number" min="0" max="500" placeholder="e.g. 100">
</div>
</div>
<div class="settings-section-title">Daily Digest</div>
<div class="settings-row">
<label class="settings-label" for="digest-hour">Send forecast at hour (local, 0–23)</label>
<input id="digest-hour" class="settings-input settings-input--short" type="number" min="0" max="23" value="7">
</div>
<div class="settings-section-title">Personal Weather Station</div>
<div class="settings-row settings-row--block">
<div class="settings-row-head">
<span class="settings-label">PWS JSON URL</span>
<button id="pws-save-btn" class="settings-btn" type="button">Save</button>
</div>
<input id="pws-url-input" class="settings-input" type="url"
placeholder="http://192.168.1.x/weather.json"
autocomplete="off" spellcheck="false">
<p class="settings-help">Point to a local device that serves a JSON weather payload.</p>
<p id="pws-status" class="settings-help hidden"></p>
</div>
<div class="settings-row" id="pwa-install-row" style="display:none">
<span class="settings-label">Install App</span>
<button id="pwa-install-btn" class="settings-btn" type="button">Add to Home Screen</button>
</div>
</div>
</div>
@@ -1118,6 +1240,7 @@
<button class="bottom-tab" data-tab="alerts" type="button">
<span class="bottom-icon">⚠️</span>
<span class="bottom-label">Alerts</span>
<span class="alert-badge hidden" id="alert-badge-bottom"></span>
</button>
<button class="bottom-tab" data-tab="learn" type="button">
<span class="bottom-icon">📘</span>
@@ -1129,7 +1252,7 @@
</button>
</nav>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.js"></script>
<script src="app.js"></script>
<script>
if ('serviceWorker' in navigator) {