Skip to content

Commit 9a175e1

Browse files
fix(api): plot of the day shows light preview image in dark mode (#10308)
## Summary - Fixes a user bug report (on `/roc-curve/python/altair`, in German): the "plot of the day" on the landing page was not displayed correctly in dark mode — the terminal card rendered the light-background preview image on the dark page. - Root cause: `GET /insights/plot-of-the-day` only returned the legacy `preview_url`, which is an ORM synonym for `preview_url_light`. The frontend has been theme-aware since Phase C (`selectPreviewUrl` in `app/src/utils/themedPreview.ts`, and `PlotOfTheDayData` already declares the themed fields), but with no `preview_url_dark` in the payload it always fell back to the light image. - Fix: `PlotOfTheDayResponse` now carries `preview_url_light` / `preview_url_dark`, and `_build_potd` threads the dark URL through the candidate selection. No frontend change needed — `PlotOfTheDayTerminal` picks the dark variant up automatically. ## Test plan - [x] `uv run pytest tests/unit/api/test_routers.py` — 137 passed, including the extended `test_potd_with_db` which now asserts both themed URLs flow through to the response - [x] `uv run ruff check` / `ruff format --check` / `uv run mypy api/routers/insights.py` — clean ## Checklist - [x] `CHANGELOG.md` updated under `[Unreleased]` (follow-up commit on this branch referencing this PR number) - [x] Related documentation updated if behavior changed — none needed (no analytics events, workflows, or user-facing docs touched) --- _Generated by [Claude Code](https://claude.ai/code/session_0162HVVDfD1xUDwP3Gs2b3ns)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 68babbc commit 9a175e1

3 files changed

Lines changed: 27 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@ aggregate instead: an italic *Catalog* line at the end of the version section an
138138

139139
### Fixed
140140

141+
- **Plot of the day now respects dark mode** — the landing-page terminal card always showed the
142+
light preview image because `GET /insights/plot-of-the-day` only returned the legacy
143+
`preview_url` (a synonym for the light variant); the response now carries
144+
`preview_url_light` / `preview_url_dark`, so the already theme-aware frontend picks the dark
145+
render automatically (user bug report, in German, on `/roc-curve/python/altair`) (#10308).
141146
- **A correctly rejected plot was reported as a crashed review, deadlocking the PR**
142147
`impl-review.yml` used quality score `0` as its sentinel for "the AI review produced no
143148
output", but `0` is also a score the review prompt *mandates*: the Stage 1 auto-reject gates

api/routers/insights.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ class PlotOfTheDayResponse(BaseModel):
145145
language: str
146146
quality_score: float
147147
preview_url: str | None = None
148+
preview_url_light: str | None = None
149+
preview_url_dark: str | None = None
148150
image_description: str | None = None
149151
code: str | None = None
150152
library_version: str | None = None
@@ -422,7 +424,7 @@ async def _build_potd(spec_repo: SpecRepository, impl_repo: ImplRepository) -> P
422424
today = date.today().isoformat()
423425

424426
# Collect candidates: implementations with quality_score >= 90 (lightweight, no code loaded)
425-
candidates: list[tuple[str, str, str, str, str, float, str]] = []
427+
candidates: list[tuple[str, str, str, str, str, float, str, str | None]] = []
426428
for spec in all_specs:
427429
for impl in spec.impls:
428430
if impl.quality_score is not None and impl.quality_score >= 90 and impl.preview_url:
@@ -436,6 +438,7 @@ async def _build_potd(spec_repo: SpecRepository, impl_repo: ImplRepository) -> P
436438
language,
437439
impl.quality_score,
438440
impl.preview_url,
441+
impl.preview_url_dark,
439442
)
440443
)
441444

@@ -445,11 +448,19 @@ async def _build_potd(spec_repo: SpecRepository, impl_repo: ImplRepository) -> P
445448
# Deterministic selection based on date
446449
seed = int(hashlib.md5(today.encode()).hexdigest(), 16) # noqa: S324
447450
idx = seed % len(candidates)
448-
spec_id, spec_title, description, library_id, language, quality_score, preview_url = candidates[idx]
451+
spec_id, spec_title, description, library_id, language, quality_score, preview_url, preview_url_dark = candidates[
452+
idx
453+
]
449454

450455
# Load deferred fields (code, image_description) for just this one impl
451456
full_impl = await impl_repo.get_by_spec_and_library(spec_id, library_id)
452457

458+
# Prefer preview URLs from the fresh per-impl load so they can't drift from
459+
# the code/metadata below; the candidate-snapshot values are the fallback.
460+
if full_impl:
461+
preview_url = full_impl.preview_url_light or preview_url
462+
preview_url_dark = full_impl.preview_url_dark or preview_url_dark
463+
453464
return PlotOfTheDayResponse(
454465
spec_id=spec_id,
455466
spec_title=spec_title,
@@ -459,6 +470,8 @@ async def _build_potd(spec_repo: SpecRepository, impl_repo: ImplRepository) -> P
459470
language=language,
460471
quality_score=quality_score,
461472
preview_url=preview_url,
473+
preview_url_light=preview_url,
474+
preview_url_dark=preview_url_dark,
462475
image_description=full_impl.review_image_description if full_impl else None,
463476
code=strip_noqa_comments(full_impl.code) if full_impl and full_impl.code else None,
464477
library_version=full_impl.library_version if full_impl else None,

tests/unit/api/test_routers.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1775,7 +1775,9 @@ def test_potd_without_db(self, client: TestClient) -> None:
17751775
assert response.status_code == 503
17761776

17771777
def test_potd_with_db(self, client: TestClient, mock_spec) -> None:
1778-
"""Plot of the day should return a featured implementation."""
1778+
"""Plot of the day should return a featured implementation with themed preview URLs."""
1779+
dark_url = "https://example.com/plot-dark.png"
1780+
mock_spec.impls[0].preview_url_dark = dark_url
17791781
mock_spec_repo = MagicMock()
17801782
mock_spec_repo.get_all = AsyncMock(return_value=[mock_spec])
17811783
mock_impl = MagicMock()
@@ -1784,6 +1786,8 @@ def test_potd_with_db(self, client: TestClient, mock_spec) -> None:
17841786
mock_impl.library_version = "3.10.0"
17851787
mock_impl.python_version = "3.13.11"
17861788
mock_impl.language_version = "3.13.11"
1789+
mock_impl.preview_url_light = TEST_IMAGE_URL
1790+
mock_impl.preview_url_dark = dark_url
17871791
mock_impl_repo = MagicMock()
17881792
mock_impl_repo.get_by_spec_and_library = AsyncMock(return_value=mock_impl)
17891793

@@ -1799,6 +1803,8 @@ def test_potd_with_db(self, client: TestClient, mock_spec) -> None:
17991803
assert data["spec_id"] == "scatter-basic"
18001804
assert data["library_id"] == "matplotlib"
18011805
assert data["quality_score"] == 92.5
1806+
assert data["preview_url_light"] == TEST_IMAGE_URL
1807+
assert data["preview_url_dark"] == dark_url
18021808

18031809
def test_potd_no_candidates(self, client: TestClient) -> None:
18041810
"""Plot of the day should return null when no high-quality implementations."""

0 commit comments

Comments
 (0)