diff --git a/dist/index.html b/dist/index.html index b43ae80..7e0404d 100644 --- a/dist/index.html +++ b/dist/index.html @@ -7773,61 +7773,18 @@ async function fetchLightningStrikes({ lat, lon, radiusKm = 150, limit = 200 } = if (!data || !Array.isArray(data.strikes)) throw new Error("Lightning relay returned an invalid response"); return data.strikes; } -/** -* Project a lat/lon onto the radar tile layer's pixel coordinates. -* -* Mirrors the slippy-map math in PrecipitationRadar: the layer is a grid of -* TILE_SIZE tiles centered on the location's center tile (cx, cy in tile -* space). A strike at world coordinates is converted to floating tile -* coordinates at the given zoom, then to pixels relative to the tile layer's -* origin (the tile two rows/cols above-left of the center tile, since the grid -* is 5x5 and centered). -* -* @param {number} lat -* @param {number} lon -* @param {Object} opts -* @param {number} opts.zoom -* @param {number} opts.cx - center tile X (float) -* @param {number} opts.cy - center tile Y (float) -* @param {number} [opts.tileSize=256] -* @returns {{ x: number, y: number }} Pixel coords within the tile layer. -*/ -function projectStrikeToLayer(lat, lon, { zoom, cx, cy, tileSize = 256 }) { - const fx = tileX(lon, zoom); - const fy = tileY(lat, zoom); - const tcx = Math.floor(cx); - const tcy = Math.floor(cy); - return { - x: (fx - (tcx - 2)) * tileSize, - y: (fy - (tcy - 2)) * tileSize - }; -} -function tileX(lon, z) { - return (lon + 180) / 360 * Math.pow(2, z); -} -function tileY(lat, z) { - const rad = lat * Math.PI / 180; - return (1 - Math.log(Math.tan(rad) + 1 / Math.cos(rad)) / Math.PI) * Math.pow(2, z - 1); -} //#endregion //#region src/components/PrecipitationRadar.svelte -init_db(); -var root$4 = /* @__PURE__ */ from_html(``); -var root_1$4 = /* @__PURE__ */ from_html(`
`); -var root_2$4 = /* @__PURE__ */ from_html(``); -var root_3$3 = /* @__PURE__ */ from_html(` `, 1); -var root_4$3 = /* @__PURE__ */ from_html(``); -var root_5$3 = /* @__PURE__ */ from_html(`
`); -var root_6$2 = /* @__PURE__ */ from_html(`
`, 1); -var root_7$1 = /* @__PURE__ */ from_html(`
⏳ Loading radar...
`); -var root_8 = /* @__PURE__ */ from_html(`
`); -var root_9 = /* @__PURE__ */ from_html(`
⏳ Loading lightning...
`); -var root_10 = /* @__PURE__ */ from_html(`
`); -var root_11 = /* @__PURE__ */ from_html(`
⚡ No recent strikes nearby
`); -var root_12 = /* @__PURE__ */ from_html(`
`); -var root_13 = /* @__PURE__ */ from_html(`

No radar data available.

`); -var root_14 = /* @__PURE__ */ from_html(`
`, 1); -var root_15 = /* @__PURE__ */ from_html(`

Precipitation Radar

`); +var root$4 = /* @__PURE__ */ from_html(`
`); +var root_1$4 = /* @__PURE__ */ from_html(``); +var root_2$4 = /* @__PURE__ */ from_html(` `, 1); +var root_3$3 = /* @__PURE__ */ from_html(`
`, 1); +var root_4$3 = /* @__PURE__ */ from_html(`
⏳ Loading radar...
`); +var root_5$3 = /* @__PURE__ */ from_html(`
`); +var root_6$2 = /* @__PURE__ */ from_html(`
`); +var root_7$1 = /* @__PURE__ */ from_html(`

No radar data available.

`); +var root_8 = /* @__PURE__ */ from_html(`
`, 1); +var root_9 = /* @__PURE__ */ from_html(`

Precipitation Radar

`); function PrecipitationRadar($$anchor, $$props) { push($$props, true); let radarData = /* @__PURE__ */ state(null); @@ -7851,7 +7808,6 @@ function PrecipitationRadar($$anchor, $$props) { const TILE_SIZE = 256; const RADAR_COLOR = 4; const loc = /* @__PURE__ */ user_derived(() => app$1.selectedLocation); - let showLightning = /* @__PURE__ */ state(proxy(app$1.settings.showLightning || false)); let strikes = /* @__PURE__ */ state(proxy([])); let lightningError = /* @__PURE__ */ state(""); let lightningLoading = /* @__PURE__ */ state(false); @@ -7883,7 +7839,7 @@ function PrecipitationRadar($$anchor, $$props) { } } async function loadLightning() { - if (!get(loc) || !get(showLightning)) return; + if (!get(loc)) return; set(lightningLoading, true); set(lightningError, ""); try { @@ -7902,17 +7858,6 @@ function PrecipitationRadar($$anchor, $$props) { set(lightningLoading, false); } } - function toggleLightning() { - set(showLightning, !get(showLightning)); - app$1.settings = { - ...app$1.settings, - showLightning: get(showLightning) - }; - saveSettings({ - ...app$1.settings, - alertThresholds: { ...app$1.settings.alertThresholds } - }).catch(() => {}); - } function startLightningTimer() { stopLightningTimer(); lightningTimer = setInterval(loadLightning, LIGHTNING_REFRESH_MS); @@ -7924,7 +7869,7 @@ function PrecipitationRadar($$anchor, $$props) { } } user_effect(() => { - if (get(showLightning) && get(loc)) { + if (get(loc)) { loadLightning(); startLightningTimer(); return () => { @@ -7936,15 +7881,6 @@ function PrecipitationRadar($$anchor, $$props) { set(lightningError, ""); } }); - const strikeMarkers = /* @__PURE__ */ user_derived(() => get(showLightning) ? get(strikes).map((s) => ({ - ...s, - ...projectStrikeToLayer(s.lat, s.lon, { - zoom: get(zoom), - cx: get(cx), - cy: get(cy), - tileSize: TILE_SIZE - }) - })) : []); const pastFrames = /* @__PURE__ */ user_derived(() => get(radarData)?.radar?.past || []); const nowcastFrames = /* @__PURE__ */ user_derived(() => get(radarData)?.radar?.nowcast || []); const allFrames = /* @__PURE__ */ user_derived(() => [...get(pastFrames), ...get(nowcastFrames)]); @@ -8071,58 +8007,41 @@ function PrecipitationRadar($$anchor, $$props) { } const isPast = /* @__PURE__ */ user_derived(() => get(currentIdx) < get(pastFrames).length); const frameLabel = /* @__PURE__ */ user_derived(() => get(isPast) ? "Past" : "Forecast"); - var div = root_15(); - var div_1 = child(div); - var node = sibling(child(div_1), 2); + var div = root_9(); + var node = sibling(child(div), 2); var consequent = ($$anchor) => { - var button = root$4(); - let classes; - template_effect(() => { - classes = set_class(button, 1, "layer-toggle svelte-tjvfz7", null, classes, { on: get(showLightning) }); - set_attribute(button, "aria-checked", get(showLightning)); - }); - delegated("click", button, toggleLightning); - append($$anchor, button); - }; - if_block(node, ($$render) => { - if (get(loc)) $$render(consequent); - }); - reset(div_1); - var node_1 = sibling(div_1, 2); - var consequent_1 = ($$anchor) => { - var div_2 = root_1$4(); - var span = child(div_2); + var div_1 = root$4(); + var span = child(div_1); var text = child(span); reset(span); - var button_1 = sibling(span, 2); - reset(div_2); + var button = sibling(span, 2); + reset(div_1); template_effect(() => set_text(text, `⚠️ ${get(error) ?? ""}`)); - delegated("click", button_1, loadRadar); - append($$anchor, div_2); + delegated("click", button, loadRadar); + append($$anchor, div_1); }; var alternate = ($$anchor) => { - var fragment = root_14(); - var div_3 = first_child(fragment); - let classes_1; - var node_2 = child(div_3); - var consequent_4 = ($$anchor) => { - var fragment_1 = root_6$2(); - var div_4 = first_child(fragment_1); - var node_3 = child(div_4); - each(node_3, 17, () => get(tiles), (t) => t.col + "_" + t.row, ($$anchor, t) => { - var fragment_2 = root_3$3(); + var fragment = root_8(); + var div_2 = first_child(fragment); + let classes; + var node_1 = child(div_2); + var consequent_2 = ($$anchor) => { + var fragment_1 = root_3$3(); + var div_3 = first_child(fragment_1); + each(div_3, 21, () => get(tiles), (t) => t.col + "_" + t.row, ($$anchor, t) => { + var fragment_2 = root_2$4(); var img = first_child(fragment_2); - var node_4 = sibling(img, 2); - var consequent_2 = ($$anchor) => { - var img_1 = root_2$4(); + var node_2 = sibling(img, 2); + var consequent_1 = ($$anchor) => { + var img_1 = root_1$4(); template_effect(($0) => { set_attribute(img_1, "src", $0); set_style(img_1, `grid-column: ${get(t).col + 3}; grid-row: ${get(t).row + 3}`); }, [() => radarUrl(get(t).tx, get(t).ty)]); append($$anchor, img_1); }; - if_block(node_4, ($$render) => { - if (get(frame)) $$render(consequent_2); + if_block(node_2, ($$render) => { + if (get(frame)) $$render(consequent_1); }); template_effect(($0) => { set_attribute(img, "src", $0); @@ -8130,133 +8049,95 @@ function PrecipitationRadar($$anchor, $$props) { }, [() => osmUrl(get(t).tx, get(t).ty)]); append($$anchor, fragment_2); }); - var node_5 = sibling(node_3, 2); - var consequent_3 = ($$anchor) => { - var div_5 = root_5$3(); - each(div_5, 21, () => get(strikeMarkers), (s) => s.time + "_" + s.lat + "_" + s.lon, ($$anchor, s) => { - var span_1 = root_4$3(); - template_effect(($0) => { - set_attribute(span_1, "title", $0); - set_style(span_1, `left: ${get(s).x ?? ""}px; top: ${get(s).y ?? ""}px`); - }, [() => "Strike " + (/* @__PURE__ */ new Date(get(s).time * 1e3)).toLocaleString()]); - append($$anchor, span_1); - }); - reset(div_5); - append($$anchor, div_5); - }; - if_block(node_5, ($$render) => { - if (get(showLightning)) $$render(consequent_3); - }); - reset(div_4); - var div_6 = sibling(div_4, 2); + reset(div_3); + var div_4 = sibling(div_3, 2); template_effect(() => { - set_style(div_4, `transform: translate(${get(panX) + 128 - get(pxOff)}px, ${get(panY) + 128 - get(pyOff)}px)`); - set_attribute(div_6, "title", get(loc)?.name || ""); - set_style(div_6, `transform: translate(calc(-50% + ${get(panX) ?? ""}px), calc(-50% + ${get(panY) ?? ""}px))`); + set_style(div_3, `transform: translate(${get(panX) + 128 - get(pxOff)}px, ${get(panY) + 128 - get(pyOff)}px)`); + set_attribute(div_4, "title", get(loc)?.name || ""); + set_style(div_4, `transform: translate(calc(-50% + ${get(panX) ?? ""}px), calc(-50% + ${get(panY) ?? ""}px))`); }); append($$anchor, fragment_1); }; - if_block(node_2, ($$render) => { - if (get(loc)) $$render(consequent_4); + if_block(node_1, ($$render) => { + if (get(loc)) $$render(consequent_2); }); - var node_6 = sibling(node_2, 2); - var consequent_5 = ($$anchor) => { - append($$anchor, root_7$1()); + var node_3 = sibling(node_1, 2); + var consequent_3 = ($$anchor) => { + append($$anchor, root_4$3()); }; - if_block(node_6, ($$render) => { - if (get(loading)) $$render(consequent_5); + if_block(node_3, ($$render) => { + if (get(loading)) $$render(consequent_3); }); - var node_7 = sibling(node_6, 2); - var consequent_6 = ($$anchor) => { - var div_8 = root_8(); - var span_2 = child(div_8); - var text_1 = child(span_2); - reset(span_2); - var button_2 = sibling(span_2, 2); - reset(div_8); + var node_4 = sibling(node_3, 2); + var consequent_4 = ($$anchor) => { + var div_6 = root_5$3(); + var span_1 = child(div_6); + var text_1 = child(span_1); + reset(span_1); + var button_1 = sibling(span_1, 2); + reset(div_6); template_effect(() => set_text(text_1, `⚠️ ${get(error) ?? ""}`)); - delegated("click", button_2, loadRadar); - append($$anchor, div_8); + delegated("click", button_1, loadRadar); + append($$anchor, div_6); }; - if_block(node_7, ($$render) => { - if (get(error)) $$render(consequent_6); + if_block(node_4, ($$render) => { + if (get(error)) $$render(consequent_4); }); - var node_8 = sibling(node_7, 2); - var consequent_7 = ($$anchor) => { - append($$anchor, root_9()); - }; - var consequent_8 = ($$anchor) => { - var div_10 = root_10(); - var span_3 = child(div_10); - var text_2 = child(span_3); - reset(span_3); - reset(div_10); - template_effect(() => set_text(text_2, `⚡ ${get(lightningError) ?? ""} (relay not live yet)`)); - append($$anchor, div_10); - }; - var consequent_9 = ($$anchor) => { - append($$anchor, root_11()); - }; - if_block(node_8, ($$render) => { - if (get(showLightning) && get(lightningLoading)) $$render(consequent_7); - else if (get(showLightning) && get(lightningError)) $$render(consequent_8, 1); - else if (get(showLightning) && get(strikes).length === 0 && !get(lightningLoading)) $$render(consequent_9, 2); - }); - var div_12 = sibling(node_8, 2); - var button_3 = child(div_12); - var span_4 = sibling(button_3, 2); - var text_3 = child(span_4, true); - reset(span_4); - var button_4 = sibling(span_4, 2); - reset(div_12); - reset(div_3); - var node_9 = sibling(div_3, 2); - var consequent_10 = ($$anchor) => { - var div_13 = root_12(); - var div_14 = child(div_13); - var button_5 = child(div_14); + var div_7 = sibling(node_4, 2); + var button_2 = child(div_7); + var span_2 = sibling(button_2, 2); + var text_2 = child(span_2, true); + reset(span_2); + var button_3 = sibling(span_2, 2); + reset(div_7); + reset(div_2); + var node_5 = sibling(div_2, 2); + var consequent_5 = ($$anchor) => { + var div_8 = root_6$2(); + var div_9 = child(div_8); + var button_4 = child(div_9); + var button_5 = sibling(button_4, 2); + var text_3 = child(button_5, true); + reset(button_5); var button_6 = sibling(button_5, 2); - var text_4 = child(button_6, true); - reset(button_6); - var button_7 = sibling(button_6, 2); - reset(div_14); - var div_15 = sibling(div_14, 2); - var input = child(div_15); + reset(div_9); + var div_10 = sibling(div_9, 2); + var input = child(div_10); remove_input_defaults(input); - reset(div_15); - var div_16 = sibling(div_15, 2); - var span_5 = child(div_16); - var text_5 = child(span_5, true); + reset(div_10); + var div_11 = sibling(div_10, 2); + var span_3 = child(div_11); + var text_4 = child(span_3, true); + reset(span_3); + var span_4 = sibling(span_3, 2); + var text_5 = child(span_4, true); + reset(span_4); + var span_5 = sibling(span_4, 2); + let classes_1; + var text_6 = child(span_5, true); reset(span_5); var span_6 = sibling(span_5, 2); - var text_6 = child(span_6, true); + var text_7 = child(span_6); reset(span_6); - var span_7 = sibling(span_6, 2); - let classes_2; - var text_7 = child(span_7, true); - reset(span_7); - var span_8 = sibling(span_7, 2); - var text_8 = child(span_8); - reset(span_8); - reset(div_16); - reset(div_13); + reset(div_11); + reset(div_8); template_effect(($0, $1) => { - set_attribute(button_6, "aria-label", get(playing) ? "Pause" : "Play"); - set_text(text_4, get(playing) ? "⏸" : "▶"); + set_attribute(button_5, "aria-label", get(playing) ? "Pause" : "Play"); + set_text(text_3, get(playing) ? "⏸" : "▶"); set_attribute(input, "max", get(totalFrames) - 1); set_value(input, get(currentIdx)); - set_text(text_5, $0); - set_text(text_6, $1); - classes_2 = set_class(span_7, 1, "tl-badge svelte-tjvfz7", null, classes_2, { + set_text(text_4, $0); + set_text(text_5, $1); + classes_1 = set_class(span_5, 1, "tl-badge svelte-tjvfz7", null, classes_1, { "tl-past": get(isPast), "tl-future": !get(isPast) }); - set_text(text_7, get(frameLabel)); - set_text(text_8, `${get(currentIdx) + 1}/${get(totalFrames) ?? ""}`); + set_text(text_6, get(frameLabel)); + set_text(text_7, `${get(currentIdx) + 1}/${get(totalFrames) ?? ""}`); }, [() => fmtTime(get(frame).time), () => fmtDate(get(frame).time)]); - delegated("click", button_5, stepBack); - delegated("click", button_6, togglePlay); - delegated("click", button_7, stepForward); + delegated("click", button_4, stepBack); + delegated("click", button_5, togglePlay); + delegated("click", button_6, stepForward); delegated("input", input, (e) => { set(playing, false); if (animRef) { @@ -8265,35 +8146,35 @@ function PrecipitationRadar($$anchor, $$props) { } set(currentIdx, Number(e.target.value), true); }); - append($$anchor, div_13); + append($$anchor, div_8); }; - var consequent_11 = ($$anchor) => { - var div_17 = root_13(); - var button_8 = sibling(child(div_17), 2); - reset(div_17); - delegated("click", button_8, loadRadar); - append($$anchor, div_17); + var consequent_6 = ($$anchor) => { + var div_12 = root_7$1(); + var button_7 = sibling(child(div_12), 2); + reset(div_12); + delegated("click", button_7, loadRadar); + append($$anchor, div_12); }; - if_block(node_9, ($$render) => { - if (get(frame)) $$render(consequent_10); - else if (!get(loading)) $$render(consequent_11, 1); + if_block(node_5, ($$render) => { + if (get(frame)) $$render(consequent_5); + else if (!get(loading)) $$render(consequent_6, 1); }); template_effect(() => { - classes_1 = set_class(div_3, 1, "radar-viewport svelte-tjvfz7", null, classes_1, { dragging: get(dragging) }); - button_3.disabled = get(zoom) >= 11; - set_text(text_3, get(zoom)); - button_4.disabled = get(zoom) <= 2; + classes = set_class(div_2, 1, "radar-viewport svelte-tjvfz7", null, classes, { dragging: get(dragging) }); + button_2.disabled = get(zoom) >= 11; + set_text(text_2, get(zoom)); + button_3.disabled = get(zoom) <= 2; }); - delegated("pointerdown", div_3, onPointerDown); - delegated("pointermove", div_3, onPointerMove); - delegated("pointerup", div_3, onPointerUp); - event("pointerleave", div_3, onPointerUp); - delegated("click", button_3, zoomIn); - delegated("click", button_4, zoomOut); + delegated("pointerdown", div_2, onPointerDown); + delegated("pointermove", div_2, onPointerMove); + delegated("pointerup", div_2, onPointerUp); + event("pointerleave", div_2, onPointerUp); + delegated("click", button_2, zoomIn); + delegated("click", button_3, zoomOut); append($$anchor, fragment); }; - if_block(node_1, ($$render) => { - if (get(error) && !get(loc)) $$render(consequent_1); + if_block(node, ($$render) => { + if (get(error) && !get(loc)) $$render(consequent); else $$render(alternate, -1); }); reset(div); @@ -9314,7 +9195,7 @@ delegate([ ]); //#endregion //#region src/main.js -console.info({ commit_hash: "97b676093a00eb7dc56bd07219df7b9446bc6e2e" }); +console.info({ commit_hash: "86acc5614316d179e884e15c8c697a00896da03e" }); registerServiceWorker(); mount(App, { target: document.getElementById("app") }); //#endregion @@ -10562,31 +10443,6 @@ input::placeholder { gap: 8px; } - .layer-toggle.svelte-tjvfz7 { - display: inline-flex; - align-items: center; - gap: 5px; - padding: 5px 11px; - font-size: 0.78rem; - font-weight: 600; - border-radius: 16px; - border: 1px solid var(--color-border); - background: var(--color-surface); - color: var(--color-text-muted); - cursor: pointer; - margin-bottom: 12px; - transition: background var(--transition), color var(--transition), border-color var(--transition); - } - .layer-toggle.svelte-tjvfz7:hover { - border-color: var(--color-primary); - color: var(--color-text); - } - .layer-toggle.on.svelte-tjvfz7 { - background: rgba(250, 204, 21, 0.18); - border-color: #facc15; - color: #fde047; - } - .radar-status.svelte-tjvfz7, .radar-status-err.svelte-tjvfz7 { display: flex; @@ -10690,40 +10546,6 @@ input::placeholder { z-index: 1; } - /* ── Lightning strike layer ── */ - .lightning-layer.svelte-tjvfz7 { - position: absolute; - inset: 0; - z-index: 2; - pointer-events: none; - } - - .lightning-strike.svelte-tjvfz7 { - position: absolute; - width: 8px; - height: 8px; - margin-left: -4px; - margin-top: -4px; - border-radius: 50%; - background: #facc15; - box-shadow: 0 0 6px 2px rgba(250, 204, 21, 0.85); - pointer-events: auto; - } - - .lightning-status.svelte-tjvfz7 { - position: absolute; - top: 12px; - right: 12px; - z-index: 10; - padding: 5px 10px; - background: rgba(10, 22, 40, 0.8); - border: 1px solid var(--color-border); - border-radius: var(--radius-sm); - pointer-events: none; - } - .lightning-status-err.svelte-tjvfz7 { - border-color: rgba(239, 68, 68, 0.4); - } /* ── Location marker ── */ .location-marker.svelte-tjvfz7 {