1.44 - New Filters, profiles & Mobile fixes

This commit is contained in:
fraxle
2026-05-17 16:01:34 +01:00
parent bc4fd3e6f1
commit 1cd2cd0ab4
11 changed files with 640 additions and 298 deletions
+55 -26
View File
@@ -193,7 +193,7 @@ const COSMIC_CALENDAR = [
// ── 2027 METEOR SHOWERS ─────────────────────────────────────────────
{
id: 'quadrantids-2027',
emoji: String.fromCodePoint(0x2604, 0xFE0F),
emoji: '☄️',
title: 'Quadrantid Meteor Shower',
message: 'The Quadrantid meteor shower is active — up to 120 meteors/hour at peak. Best in the hours before dawn on 4 Jan from a dark site.',
color: '#2a1a5a', textColor: '#e8d8ff',
@@ -202,7 +202,7 @@ const COSMIC_CALENDAR = [
},
{
id: 'lyrids-2027',
emoji: String.fromCodePoint(0x2604, 0xFE0F),
emoji: '☄️',
title: 'Lyrid Meteor Shower',
message: 'The Lyrid meteor shower peaks — up to 20 meteors/hour after midnight. Note: bright waning gibbous moon may reduce visibility this year.',
color: '#2a1a5a', textColor: '#e8d8ff',
@@ -211,7 +211,7 @@ const COSMIC_CALENDAR = [
},
{
id: 'eta-aquariids-2027',
emoji: String.fromCodePoint(0x2604, 0xFE0F),
emoji: '☄️',
title: 'Eta Aquariid Meteor Shower',
message: 'The Eta Aquariids peak — fragments of Halley\'s Comet, up to 50/hour before dawn. New moon on 6 May means excellent dark skies this year.',
color: '#2a1a5a', textColor: '#e8d8ff',
@@ -220,7 +220,7 @@ const COSMIC_CALENDAR = [
},
{
id: 'perseids-2027',
emoji: String.fromCodePoint(0x2604, 0xFE0F),
emoji: '☄️',
title: 'Perseid Meteor Shower',
message: 'The Perseids are active — one of the best showers of the year, up to 100 meteors/hour at peak around 13 Aug.',
color: '#3a1a00', textColor: '#ffe8c0',
@@ -229,7 +229,7 @@ const COSMIC_CALENDAR = [
},
{
id: 'orionids-2027',
emoji: String.fromCodePoint(0x2604, 0xFE0F),
emoji: '☄️',
title: 'Orionid Meteor Shower',
message: 'The Orionid shower peaks — fast, bright meteors from Halley\'s Comet debris. Up to 25/hour.',
color: '#2a1a5a', textColor: '#e8d8ff',
@@ -238,7 +238,7 @@ const COSMIC_CALENDAR = [
},
{
id: 'leonids-2027',
emoji: String.fromCodePoint(0x2604, 0xFE0F),
emoji: '☄️',
title: 'Leonid Meteor Shower',
message: 'The Leonid shower peaks — fast meteors from comet Tempel-Tuttle, up to 15/hour.',
color: '#2a1a5a', textColor: '#e8d8ff',
@@ -247,7 +247,7 @@ const COSMIC_CALENDAR = [
},
{
id: 'geminids-2027',
emoji: String.fromCodePoint(0x2604, 0xFE0F),
emoji: '☄️',
title: 'Geminid Meteor Shower',
message: 'The Geminids peak — the finest shower of the year, up to 150 meteors/hour, visible even before midnight.',
color: '#3a1a00', textColor: '#ffe8c0',
@@ -256,7 +256,7 @@ const COSMIC_CALENDAR = [
},
{
id: 'ursids-2027',
emoji: String.fromCodePoint(0x2604, 0xFE0F),
emoji: '☄️',
title: 'Ursid Meteor Shower',
message: 'The Ursid shower peaks near the winter solstice — circumpolar, best from northern latitudes.',
color: '#2a1a5a', textColor: '#e8d8ff',
@@ -267,7 +267,7 @@ const COSMIC_CALENDAR = [
// ── 2027 ECLIPSES ───────────────────────────────────────────────────
{
id: 'annular-solar-eclipse-2027-feb',
emoji: String.fromCodePoint(0x1F311),
emoji: '🌑',
title: 'Annular Solar Eclipse',
message: 'An annular solar eclipse creates a "ring of fire" effect — visible across parts of South America, Africa and the Indian Ocean.',
color: '#1a0a2a', textColor: '#d8c8f8',
@@ -276,7 +276,7 @@ const COSMIC_CALENDAR = [
},
{
id: 'total-solar-eclipse-2027-aug',
emoji: String.fromCodePoint(0x1F311),
emoji: '🌑',
title: 'Total Solar Eclipse',
message: 'A spectacular total solar eclipse — the path of totality crosses Morocco, Spain, Algeria, Libya, Egypt and Saudi Arabia. One of the longest totalities of the century.',
color: '#1a0a2a', textColor: '#d8c8f8',
@@ -287,7 +287,7 @@ const COSMIC_CALENDAR = [
// ── 2027 PLANETARY EVENTS ───────────────────────────────────────────
{
id: 'mars-opposition-2027',
emoji: String.fromCodePoint(0x1F534),
emoji: '🔴',
title: 'Mars at Opposition',
message: 'Mars is at its closest and brightest — a vivid red beacon visible all night long. Good binoculars will reveal its surface colour.',
color: '#2a1520', textColor: '#ffc8d8',
@@ -296,21 +296,23 @@ const COSMIC_CALENDAR = [
},
{
id: 'venus-jupiter-conjunction-2027',
emoji: String.fromCodePoint(0x1FA90),
emoji: '🪐',
title: 'Venus & Jupiter Conjunction',
message: 'Venus and Jupiter appear strikingly close together in the evening sky — a dazzling naked-eye pairing of the two brightest planets.',
color: '#1a2a3a', textColor: '#c8e8ff',
type: 'cosmic', nightOnly: true,
start: '2027-08-23', end: '2027-08-27', peak: '2027-08-25',
},
];
// ─── WEATHER-DERIVED EVENTS ───────────────────────────────────────────────
// These are computed from the actual forecast data.
// Each checker receives (rows, location) and returns an event object or null.
// rows = today's hourlyRows array. location = { lat, lon, name, country }.
// Computed in real-time from the forecast data.
// Each checker function receives (rows [, location]) and returns an event
// object or null. rows = today's hourlyRows array.
// location = { lat, lon, name, country }.
//
// To add a new weather event: write a checkXxx(rows, location) function
// below and add it to the checks[] array inside getActiveEvents().
function checkStargazing(rows) {
// Great stargazing: mostly clear night hours with low cloud
@@ -748,6 +750,33 @@ export function getCellTagEvents(events, row) {
});
}
// ─── DYNAMIC MESSAGE GENERATOR ───────────────────────────────────────────
// Rewrites a cosmic event's message based on where today sits vs. the peak.
// Before peak : "is active and building — peak on <date>. <detail>"
// On peak ±1d : "peaks tonight — <detail>"
// After peak : "is past its peak (<date>) but still possibly visible — <detail>"
// Single-day events (start === end) keep their static message unchanged.
function dynamicCosmicMessage(ev, dateStr) {
if (!ev.peak || ev.start === ev.end) return ev.message;
var today = new Date(dateStr + 'T00:00Z');
var peak = new Date(ev.peak + 'T00:00Z');
var diffDays = Math.round((today - peak) / 86400000);
var peakFmt = peak.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', timeZone: 'UTC' });
var base = ev.message
.replace(/^.*?(?:peaks tonight|peak tonight|is active tonight|is active|peaks)\s*—\s*/i, '')
.trim();
var baseCapd = base.charAt(0).toUpperCase() + base.slice(1);
if (diffDays < -1) {
return ev.title + ' is active and building — peak on ' + peakFmt + '. ' + baseCapd;
} else if (diffDays <= 1) {
return ev.title + ' peaks tonight — ' + base;
} else {
return ev.title + ' is past its peak (' + peakFmt + ') but still possibly visible — ' + base;
}
}
// ─── MAIN EXPORT ─────────────────────────────────────────────────────────
// Returns ALL active events for the given rows/date as an array.
// Empty array = nothing active.
@@ -765,7 +794,9 @@ export function getActiveEvents(rows, location) {
const dateStr = (rows && rows.length > 0)
? rows[0].iso.slice(0, 10)
: new Date().toISOString().slice(0, 10);
const cosmicHits = COSMIC_CALENDAR.filter(ev => dateStr >= ev.start && dateStr <= ev.end);
const cosmicHits = COSMIC_CALENDAR
.filter(ev => dateStr >= ev.start && dateStr <= ev.end)
.map(ev => Object.assign({}, ev, { message: dynamicCosmicMessage(ev, dateStr) }));
// 3. Weather-derived events (all that match, not just first)
const weatherEvents = [];
@@ -802,16 +833,14 @@ export function getLensEvent(events) {
}
// ─── LENS OVERLAY RENDERER ───────────────────────────────────────────────
// Returns SVG group content for the event overlay in the big ScopeReticle.
// Rendered INSIDE the lens clip and BELOW the glass shine/shade layers.
// cx, cy = lens centre; lensR = lens radius.
// Returns an SVG string for the event overlay inside the big ScopeReticle.
// Rendered INSIDE the lens clip path, BELOW the glass shine/shade layers.
// cx, cy = lens centre coords; lensR = lens radius.
export function getLensOverlaySVG(event, cx, cy, lensR) {
if (!event) return null;
const id = event.id;
if (event.emoji === String.fromCodePoint(0x2604, 0xFE0F)) {
if (event.emoji === '☄️') {
return [
'<g opacity="0.55" clip-path="url(#scope-lens-clip)">',
'<line x1="' + (cx-60) + '" y1="' + (cy-50) + '" x2="' + (cx-20) + '" y2="' + (cy-20) + '" stroke="#ffe8a0" stroke-width="1.5" stroke-linecap="round" opacity="0.9"/>',
@@ -845,13 +874,13 @@ export function getLensOverlaySVG(event, cx, cy, lensR) {
+ '<circle cx="' + cx_ + '" cy="' + cy_ + '" r="16" fill="none" stroke="rgba(220,60,20,0.5)" stroke-width="1.5" /></g>';
}
if (event.emoji === String.fromCodePoint(0x1FA90)) {
if (event.emoji === '🪐' && event.id.includes('conjunction')) {
return '<g clip-path="url(#scope-lens-clip)" opacity="0.65">'
+ '<circle cx="' + (cx+20) + '" cy="' + (cy-35) + '" r="10" fill="rgba(210,185,130,0.7)" />'
+ '<ellipse cx="' + (cx+20) + '" cy="' + (cy-35) + '" rx="18" ry="4.5" fill="none" stroke="rgba(200,170,100,0.65)" stroke-width="2.5" /></g>';
}
if (event.emoji === String.fromCodePoint(0x1F534)) {
if (event.emoji === '🔴') {
return '<g clip-path="url(#scope-lens-clip)" opacity="0.6">'
+ '<circle cx="' + (cx+30) + '" cy="' + (cy-30) + '" r="7" fill="rgba(200,80,40,0.75)" />'
+ '<circle cx="' + (cx-20) + '" cy="' + (cy-45) + '" r="5" fill="rgba(220,160,80,0.7)" /></g>';