mirror of
https://github.com/screentinker/screentinker.git
synced 2026-08-13 22:03:13 -06:00
The auto-framing fitted the view to whatever warning polygons were active, so a storm a few counties away pulled the frame out to cover it and the configured area shrank to an unreadable corner of a half-state view. On signage that is read at a glance, a map that wanders is worse than one that shows less. Framing is now centred and bounded: - The map never pans. The centre stays on the configured point and only the zoom responds, because the box handed to fitBounds is symmetric about home. - Zoom-out is capped at `max_counties` (default 2) county-widths in every direction, with longitude scaled by cos(lat) so the budget is the same distance on the ground north and south. - Warnings entirely outside that box are not chased at all; the configured view is held. Warnings clearing returns to it rather than staying parked on the last storm. - A floor on the frame keeps one small cell overhead from zooming to street level, and fit padding drops to 24px, which on a PiP-sized overlay was discarding a third of the width per side. The overlay assets are served max-age=14400, so a player that had already loaded them kept the old copy for four hours and silently ignored a redeploy. The page URL and its script tag now carry a version, documented to be bumped together. Tests cover the invariants against the shipped frameFor source rather than a copy of it: centred after reframing, capped at the county budget, small cells floored, distant storms not chased.
60 lines
3.2 KiB
HTML
60 lines
3.2 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Live Weather Radar</title>
|
|
<!-- Vendored Leaflet, served same-origin so the server CSP (script-src 'self') allows it. -->
|
|
<link rel="stylesheet" href="/leaflet.css">
|
|
<style>
|
|
html, body { margin: 0; height: 100%; background: #0b0d10; overflow: hidden; }
|
|
#map { position: absolute; inset: 0; background: #0b0d10; }
|
|
/* darker, calmer Leaflet attribution to match the TV look */
|
|
.leaflet-control-attribution { background: rgba(10,12,16,.6) !important; color: #9aa3ad !important; font-size: 10px; }
|
|
.leaflet-control-attribution a { color: #c9d2db !important; }
|
|
|
|
.hud { position: absolute; left: 0; right: 0; top: 0; z-index: 1000; pointer-events: none;
|
|
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; color: #fff; }
|
|
.bar { display: flex; align-items: center; gap: 14px; padding: 12px 18px;
|
|
background: linear-gradient(180deg, rgba(8,10,13,.92), rgba(8,10,13,0)); }
|
|
.live { display: flex; align-items: center; gap: 9px; font-weight: 800; letter-spacing: .06em;
|
|
text-transform: uppercase; font-size: clamp(15px, 2.4vw, 24px); }
|
|
.dot { width: 13px; height: 13px; border-radius: 50%; background: #FF2D2D;
|
|
box-shadow: 0 0 0 0 rgba(255,45,45,.7); animation: pulse 1.2s ease-out infinite; }
|
|
@keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(255,45,45,.7) } 70% { box-shadow: 0 0 0 12px rgba(255,45,45,0) } 100% { box-shadow: 0 0 0 0 rgba(255,45,45,0) } }
|
|
.area { font-weight: 600; font-size: clamp(13px, 2vw, 20px); opacity: .92; }
|
|
.spacer { flex: 1; }
|
|
.clock { font-variant-numeric: tabular-nums; font-weight: 600; font-size: clamp(12px, 1.8vw, 18px);
|
|
color: #cfe8ff; opacity: .9; }
|
|
.chips { display: flex; flex-wrap: wrap; gap: 8px; padding: 0 18px 10px; }
|
|
.chip { pointer-events: none; font-size: clamp(11px, 1.6vw, 15px); font-weight: 700; color: #0b0d10;
|
|
padding: 4px 10px; border-radius: 999px; box-shadow: 0 2px 8px rgba(0,0,0,.4); }
|
|
.chip.none { background: #2c3340; color: #aeb6c0; font-weight: 600; }
|
|
|
|
.legend { position: absolute; right: 12px; bottom: 26px; z-index: 1000; pointer-events: none;
|
|
background: rgba(10,12,16,.72); border-radius: 10px; padding: 8px 10px; font-family: system-ui, sans-serif;
|
|
color: #dfe6ee; font-size: 11px; line-height: 1.5; }
|
|
.legend .row { display: flex; align-items: center; gap: 7px; }
|
|
.legend .sw { width: 12px; height: 12px; border-radius: 3px; display: inline-block; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="map"></div>
|
|
<div class="hud">
|
|
<div class="bar">
|
|
<span class="live"><span class="dot"></span>Live Radar</span>
|
|
<span class="area" id="area"></span>
|
|
<span class="spacer"></span>
|
|
<span class="clock" id="clock"></span>
|
|
</div>
|
|
<div class="chips" id="chips"></div>
|
|
</div>
|
|
<div class="legend" id="legend"></div>
|
|
<script src="/leaflet.js"></script>
|
|
<!-- ?v= is a cache-buster, NOT decoration. These assets are served max-age=14400, so a
|
|
player that loaded the old file keeps it for four hours and silently ignores a
|
|
redeploy. Bump this AND `ASSET_V` in radar.js together whenever the JS changes. -->
|
|
<script src="/radar-overlay.js?v=2"></script>
|
|
</body>
|
|
</html>
|