From 289d3fd20fa387892160bbe430ed57cea786c359 Mon Sep 17 00:00:00 2001 From: twfarley Date: Thu, 1 Oct 2026 07:06:01 -0600 Subject: [PATCH] Align calendar tiles and update Caelus dashboard layout --- docs/user_guide.md | 8 +-- sensorius/__init__.py | 2 +- .../test_integrated_biodynamic_calendar.py | 3 +- .../test_integrated_weather_forecast.py | 13 ++-- ui_static/biodynamic_calendar/app.css | 4 +- ui_static/weather_forecast/app.css | 41 +++++------ ui_static/weather_forecast/app.js | 2 - ui_templates/weather_forecast/index.html | 68 +++++++++---------- 8 files changed, 70 insertions(+), 71 deletions(-) diff --git a/docs/user_guide.md b/docs/user_guide.md index 1ba01a4..40dd61b 100644 --- a/docs/user_guide.md +++ b/docs/user_guide.md @@ -1272,11 +1272,11 @@ Select **Return to Forecast** to restore the saved Caelus theme. The weather display uses the existing Sensorius Astral latitude, longitude, timezone, and optional Community/Location Name. Its top row presents the selected sensor's latest current readings, the canonical Sensorius forecast, and the sunrise/sunset daylight -track. Hourly forecast times use the local 12-hour AM/PM clock. The sunlight tile also shows current North and South Pole daylight, the +track. Hourly forecast times use the local 12-hour AM/PM clock. The sunlight tile also shows the next seasonal event, and up to three solar or lunar eclipses visible from the -configured Astral location during the next twelve months. A full-width Windy -map opens in radar view below that row, followed by -the current Moon and phase cycle at the bottom. The display also provides +configured Astral location during the next twelve months. A two-column row below pairs the Windy +map in radar view with the Lunar Calendar showing the current Moon and phase +cycle. These tiles stack on narrow screens. The display also provides theme-matched six-day details. It reuses the Sensorius forecast and does not maintain a separate settings file or readings database. diff --git a/sensorius/__init__.py b/sensorius/__init__.py index 630118e..3d934b0 100644 --- a/sensorius/__init__.py +++ b/sensorius/__init__.py @@ -4,4 +4,4 @@ release notes, and supporting tooling can report a consistent build identity. """ -__version__ = "v0.26.271.7" +__version__ = "v0.26.274.2" diff --git a/testApparatus/test_integrated_biodynamic_calendar.py b/testApparatus/test_integrated_biodynamic_calendar.py index 393e786..1021730 100644 --- a/testApparatus/test_integrated_biodynamic_calendar.py +++ b/testApparatus/test_integrated_biodynamic_calendar.py @@ -231,7 +231,8 @@ def test_integrated_assets_use_namespaced_routes_and_dashboard_navigation(): assert '

Planetary Aspects

' in template assert 'class="hero-calendar-mark"' in template assert '/ui_static/biodynamic_calendar/bd-calendar-icon-512.svg' in template - assert "grid-template-columns: minmax(320px, 1fr) 214px minmax(320px, 1fr);" in stylesheet + assert "--top-middle-col: 214px;" in stylesheet + assert "grid-template-columns: var(--top-left-col) var(--top-middle-col) var(--top-right-col);" in stylesheet assert "border-radius: 50%;" in stylesheet assert "object-fit: contain;" in stylesheet assert "transform: scale(1.24);" not in stylesheet diff --git a/testApparatus/test_integrated_weather_forecast.py b/testApparatus/test_integrated_weather_forecast.py index 54cf8f2..0649543 100644 --- a/testApparatus/test_integrated_weather_forecast.py +++ b/testApparatus/test_integrated_weather_forecast.py @@ -420,16 +420,15 @@ def test_astronomy_context_flags_an_observer_visible_eclipse_in_the_next_24_hour assert eclipse["ends"].endswith((" AM", " PM")) -def test_sunlight_card_places_times_horizontally_and_shows_poles(): +def test_sunlight_card_places_times_horizontally_without_poles(): template = (ROOT / "ui_templates" / "weather_forecast" / "index.html").read_text() script = (ROOT / "ui_static" / "weather_forecast" / "app.js").read_text() css = (ROOT / "ui_static" / "weather_forecast" / "app.css").read_text() - assert 'id="northPoleDaylight"' in template - assert 'id="southPoleDaylight"' in template + assert 'id="northPoleDaylight"' not in template + assert 'id="southPoleDaylight"' not in template assert 'id="nextSeasonLabel"' in template assert 'id="nextSeasonDate"' in template - assert template.index('id="northPoleDaylight"') < template.index('id="nextSeasonHeading"') assert 'id="nextEclipseHeading"' in template assert 'id="nextEclipseList"' in template assert "No visible eclipses for the next 12 months" in template @@ -437,7 +436,8 @@ def test_sunlight_card_places_times_horizontally_and_shows_poles(): assert "grid-template-columns: repeat(3, minmax(0, 1fr))" in css assert ".daylight-times dd { order: -1;" in css assert "function formatSolarTime(value)" in script - assert 'moon.north_pole_daylight ?? "—"' in script + assert 'document.getElementById("northPoleDaylight")' not in script + assert 'document.getElementById("southPoleDaylight")' not in script assert 'moon.next_season_label ?? "—"' in script assert "moon.next_eclipses" in script @@ -451,7 +451,6 @@ def test_sunlight_card_uses_three_semantic_text_colors(): assert ".daylight-card .eyebrow," in css assert ".daylight-detail-section h3 { color: var(--daylight-title-color); }" in css assert ".daylight-times dd { order: -1; margin: 0; color: var(--daylight-data-color);" in css - assert ".polar-daylight-note strong { display: block; color: var(--daylight-data-color);" in css assert ".eclipse-list time { color: var(--daylight-data-color);" in css assert ".daylight-times dt { color: var(--daylight-status-color);" in css assert ".next-season-note strong { color: var(--daylight-status-color);" in css @@ -608,7 +607,7 @@ async def _fake_forecast(*_args, **_kwargs): assert "Station reporting" in readings_panel assert "Station reporting" not in page.text[:page.text.index('id="conditions"')] assert "Environmental decisions" not in page.text - assert 'class="glass-card map-card full-width-map"' in page.text + assert 'class="glass-card map-card"' in page.text assert page.text.index('id="conditions"') < page.text.index('id="map"') < page.text.index('id="moon"') assert "overlay=radar" in page.text assert forecast.json()["days"][0]["label"] == "Sat Aug 8" diff --git a/ui_static/biodynamic_calendar/app.css b/ui_static/biodynamic_calendar/app.css index dd474a0..eb447a6 100644 --- a/ui_static/biodynamic_calendar/app.css +++ b/ui_static/biodynamic_calendar/app.css @@ -97,7 +97,7 @@ body.biodynamic-theme-garden_tools { .page { --layout-gap: 16px; --top-left-col: minmax(320px, 1fr); - --top-middle-col: minmax(320px, 0.88fr); + --top-middle-col: 214px; --top-right-col: minmax(320px, 1fr); max-width: 1320px; margin: 0 auto; @@ -477,7 +477,7 @@ button:active { .hero-grid { display: grid; - grid-template-columns: minmax(320px, 1fr) 214px minmax(320px, 1fr); + grid-template-columns: var(--top-left-col) var(--top-middle-col) var(--top-right-col); grid-auto-rows: 214px; gap: var(--layout-gap); align-items: stretch; diff --git a/ui_static/weather_forecast/app.css b/ui_static/weather_forecast/app.css index b07e258..79315b9 100644 --- a/ui_static/weather_forecast/app.css +++ b/ui_static/weather_forecast/app.css @@ -461,18 +461,23 @@ body.caelus-theme-preview-mode .dashboard-return { opacity: 0; pointer-events: n .lunar-header { display: grid; - grid-template-columns: 1fr minmax(15rem, 0.72fr) 1fr; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.8rem; align-items: center; - min-height: 17rem; - margin-top: 0.85rem; - padding: 1.25rem 1.5rem 4.8rem; + min-height: 31rem; + min-width: 0; + padding: 1.25rem 1.5rem 5.5rem; background: linear-gradient(105deg, rgba(4, 16, 40, 0.94), rgba(7, 35, 51, 0.78)); } +.lunar-calendar-title { grid-column: 1 / -1; margin: 0; align-self: start; font: 700 1.4rem Georgia, serif; } +.lunar-side-left { grid-column: 1; grid-row: 3; } +.lunar-side-right { grid-column: 2; grid-row: 3; } +.lunar-current { grid-column: 1 / -1; grid-row: 2; } .lunar-arc { position: absolute; left: 5%; right: 5%; - top: 2.1rem; + top: 4.1rem; height: 8rem; border-top: 1px solid rgba(239, 194, 101, 0.7); border-radius: 50% 50% 0 0; @@ -480,16 +485,16 @@ body.caelus-theme-preview-mode .dashboard-return { opacity: 0; pointer-events: n } .lunar-side { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0.6rem; position: relative; z-index: 1; } .lunar-side-title { grid-column: 1 / -1; margin: 0 0 0.1rem; color: rgba(235,247,240,.52); font-size: 0.5rem; font-weight: 750; letter-spacing: 0.14em; text-align: center; text-transform: uppercase; } -.lunar-step { text-align: center; color: var(--muted); } -.lunar-step-disk { display: block; width: clamp(2.2rem, 3.6vw, 3.6rem); height: clamp(2.2rem, 3.6vw, 3.6rem); margin: 0 auto; filter: drop-shadow(0 0 11px rgba(237,229,198,.18)); } -.lunar-step small { display: block; min-height: 1.3rem; margin-top: 0.3rem; font-size: 0.48rem; line-height: 1.25; letter-spacing: 0.08em; text-transform: uppercase; } +.lunar-step { min-width: 0; text-align: center; color: var(--muted); } +.lunar-step-disk { display: block; width: min(100%, clamp(2.2rem, 3.6vw, 3.6rem)); height: auto; aspect-ratio: 1; margin: 0 auto; filter: drop-shadow(0 0 11px rgba(237,229,198,.18)); } +.lunar-step small { display: block; overflow-wrap: anywhere; min-height: 1.3rem; margin-top: 0.3rem; font-size: 0.48rem; line-height: 1.25; letter-spacing: 0.08em; text-transform: uppercase; } .lunar-step time { display: block; margin-top: 0.14rem; color: var(--warm); font-size: 0.52rem; font-weight: 700; letter-spacing: 0.04em; } .lunar-current { position: relative; z-index: 2; text-align: center; align-self: start; margin-top: -0.35rem; } .lunar-current-disk { display: block; width: 6rem; height: 6rem; margin: 0 auto; filter: drop-shadow(0 0 24px rgba(255,235,177,.34)); } .lunar-current h2 { margin: 0.2rem 0 0; font: 500 1.25rem Georgia, serif; } .lunar-current p:last-child { margin: 0.35rem 0 0; color: var(--muted); font-size: 0.65rem; } .observer-view-label { color: var(--accent-2); font-size: 0.52rem; letter-spacing: 0.08em; text-transform: uppercase; } -.lunar-event-footer { position: absolute; left: 1.5rem; right: 1.5rem; bottom: 0.45rem; display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: end; gap: 1rem; color: var(--muted); font-size: 0.58rem; } +.lunar-event-footer { position: absolute; left: 1.5rem; right: 1.5rem; bottom: 0.45rem; display: grid; grid-template-columns: minmax(0, 1fr); align-items: end; gap: 0.3rem; color: var(--muted); font-size: 0.58rem; } .lunar-event-timeline { display: grid; gap: 0.3rem; min-width: 0; } .lunar-event-row { display: grid; grid-template-columns: 2.5rem minmax(0, 1fr); align-items: center; gap: 0.55rem; } .lunar-event-row-title { color: rgba(235,247,240,.58); font-size: 0.48rem; font-weight: 750; letter-spacing: 0.13em; text-align: right; text-transform: uppercase; } @@ -503,7 +508,7 @@ body.caelus-theme-preview-mode .dashboard-return { opacity: 0; pointer-events: n .lunar-event-marker.is-start { justify-items: start; transform: none; } .lunar-event-marker.is-end { justify-items: end; transform: translateX(-100%); } .lunar-event-marker.is-unavailable { display: none; } -.lunar-event-footer > small { align-self: end; color: rgba(235,247,240,.42); white-space: nowrap; } +.lunar-event-footer > small { justify-self: end; align-self: end; color: rgba(235,247,240,.42); white-space: nowrap; } .conditions-row { display: grid; grid-template-columns: 0.82fr 1.48fr 0.82fr; gap: 0.85rem; align-items: stretch; } .readings-panel, .forecast-panel, .daylight-card { min-height: 32rem; padding: 1.25rem; } @@ -593,9 +598,9 @@ body.caelus-theme-preview-mode .dashboard-return { opacity: 0; pointer-events: n .forecast-empty { min-height: 22rem; display: grid; place-content: center; text-align: center; color: var(--muted); } .forecast-empty span { color: var(--accent-2); font-size: 4rem; } .forecast-empty p { max-width: 20rem; font-size: 0.75rem; } -.map-row { display: grid; grid-template-columns: 0.82fr 1.48fr 0.82fr; gap: 0.85rem; padding: 0.85rem 0; } -.full-width-map { grid-column: 1 / 4; width: 100%; min-height: 31rem; } -.full-width-map iframe, .full-width-map .windy-map-interaction { min-height: 26rem; } +.map-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.85rem; padding: 0.85rem 0; } +.map-row > .map-card { grid-column: auto; min-width: 0; min-height: 31rem; } +.map-row iframe, .map-row .windy-map-interaction { min-height: 26rem; } .daylight-card { --daylight-title-color: var(--accent); --daylight-data-color: var(--ink); @@ -623,9 +628,6 @@ body.caelus-theme-preview-mode .dashboard-return { opacity: 0; pointer-events: n .next-season-note { display: flex; justify-content: space-between; gap: 0.5rem; align-items: baseline; margin: 0; color: var(--daylight-status-color); font-size: 0.58rem; } .next-season-note strong { color: var(--daylight-status-color); } .next-season-note time { color: var(--daylight-data-color); white-space: nowrap; } -.polar-daylight-note { display: flex; justify-content: space-between; gap: 1rem; margin: 0.9rem 0 0; color: var(--daylight-status-color); font-size: 0.6rem; line-height: 1.5; } -.polar-daylight-note span:last-child { text-align: right; } -.polar-daylight-note strong { display: block; color: var(--daylight-data-color); font-size: 0.78rem; } .eclipse-list { display: grid; gap: 0.32rem; margin: 0; padding: 0; list-style: none; font-size: 0.56rem; } .eclipse-list li { display: flex; justify-content: space-between; gap: 0.5rem; color: var(--daylight-status-color); } .eclipse-list strong { color: var(--daylight-status-color); font-weight: 650; } @@ -735,9 +737,9 @@ body.caelus-theme-preview-mode .dashboard-return { opacity: 0; pointer-events: n .moon-orbit::before { display: none; } .conditions-row { grid-template-columns: 1fr 1fr; } .daylight-card { min-height: auto; } - .lunar-header { grid-template-columns: 1fr minmax(12rem, .7fr) 1fr; } + .lunar-side { grid-column: 1 / -1; } + .lunar-side-right { grid-row: 4; } .map-row { grid-template-columns: 1fr 1fr; } - .full-width-map { grid-column: 1 / 3; } } @media (max-width: 760px) { @@ -764,7 +766,7 @@ body.caelus-theme-preview-mode .dashboard-return { opacity: 0; pointer-events: n .modal-footer p { display: none; } .modal-footer { justify-content: flex-end; } .lunar-header { grid-template-columns: 1fr; min-height: 28rem; padding: 1rem 0.65rem 5rem; } - .lunar-side { width: 100%; max-width: 34rem; margin: 0 auto; } + .lunar-side { grid-column: 1; grid-row: auto; width: 100%; max-width: 34rem; margin: 0 auto; } .lunar-current { margin-top: 0; } .lunar-current-disk { width: 5.4rem; height: 5.4rem; } .lunar-arc { left: 16%; right: 16%; } @@ -780,7 +782,6 @@ body.caelus-theme-preview-mode .dashboard-return { opacity: 0; pointer-events: n .forecast-detail-grid { grid-template-columns: 1fr; } .reading-hero { grid-template-columns: 7rem 1fr; } .map-row { grid-template-columns: 1fr; } - .full-width-map { grid-column: 1; } .daylight-card { min-height: auto; } } diff --git a/ui_static/weather_forecast/app.js b/ui_static/weather_forecast/app.js index 09ce44d..ceac8c9 100644 --- a/ui_static/weather_forecast/app.js +++ b/ui_static/weather_forecast/app.js @@ -496,8 +496,6 @@ eclipseList.append(item); }); } - document.getElementById("northPoleDaylight").textContent = moon.north_pole_daylight ?? "—"; - document.getElementById("southPoleDaylight").textContent = moon.south_pole_daylight ?? "—"; document.getElementById("sunState").textContent = moon.sun_is_up ? "Sun above horizon" : "Sun below horizon"; updateDaylightTrack(moon.daylight_progress); document.getElementById("lunarUpdated").textContent = `Updated ${moon.updated_at.slice(11, 16)} UTC`; diff --git a/ui_templates/weather_forecast/index.html b/ui_templates/weather_forecast/index.html index 163a091..030a0a7 100644 --- a/ui_templates/weather_forecast/index.html +++ b/ui_templates/weather_forecast/index.html @@ -98,7 +98,6 @@

{{ location.name or 'Your weather, alive.' }}

Sunlight today

{{ moon.get('daylight_duration', '—') }}

{{ 'Sun above horizon' if moon.get('sun_is_up', false) else 'Sun below horizon' }}
Sunrise
{{ moon.get('sunrise_display', moon.sunrise) }}
Solar noon
{{ moon.get('solar_noon_display', moon.get('solar_noon', '—')) }}
Sunset
{{ moon.get('sunset_display', moon.sunset) }}
-

{{ moon.get('north_pole_daylight', '—') }} North Pole{{ moon.get('south_pole_daylight', '—') }} South Pole

Next seasonal event

{{ moon.get('next_season_label', '—') }}

@@ -113,7 +112,7 @@

Next eclipse for your location

-
+

Weather in motion

Regional radar

Windy
@@ -122,40 +121,41 @@

Next eclipse for your location

-
-
- {% set moon_altitude = moon.get('moon_altitude') %} - {% set phase_illuminations = (0, 15, 50, 85, 100, 85, 50, 15) %} - -
-

Previous phases

- {% for phase in moon.get('previous_phases', []) %}
{{ phase.name }}
{% endfor %} -
-
-

Moon now · live

- -

{{ moon.name }}

-

{{ moon.illumination }}% illuminated · lunar day {{ moon.age_days }}
Observer-local orientation{% if moon_altitude is not none %} · {{ moon_altitude }}° altitude{% if moon_altitude < 0 %} (below horizon){% endif %}{% endif %}

-
-
-

Upcoming phases

- {% for phase in moon.get('upcoming_phases', []) %}
{{ phase.name }}
{% endfor %} -
-