F1 scoreboard: size-adaptive rendering, BDF fonts, readability fixes - #213
Conversation
📝 WalkthroughWalkthroughThe F1 scoreboard adds bitmap fonts and adaptive rendering across panel sizes, introduces automatic font configuration, persists latest-round data, expands deterministic 2026 fixtures, and updates the plugin to version 1.5.1. ChangesF1 scoreboard update
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant ConfigSchema
participant F1Renderer
participant FontLoader
participant CardRenderer
ConfigSchema->>F1Renderer: provide font and auto_scale settings
F1Renderer->>FontLoader: select and snap font tiers
F1Renderer->>F1Renderer: configure panel metrics and measure text
F1Renderer->>CardRenderer: render responsive scoreboard cards
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 56 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
|
@coderabbitai review |
✅ Action performedReview finished.
|
…fixes Rework the F1 renderer to adapt to every panel size (64x32 up to 256x64) after reports of tiny fonts/logos and off spacing. - Central _configure_layout() derives all size metrics; font sizing is width-aware so tall-narrow panels (128x64) no longer truncate names. - Crisp per-size bitmap (BDF) fonts chosen by panel size (_font_tier), replacing the blocky PressStart2P default; bundled under assets/fonts/. - Tall panels: rows spread to fill the height (_spread_ys), bigger logos, larger circuit map on the upcoming card. - Constructor cards show full team names (drop to a smaller font before shortening) and no longer overlap the driver-split with the last-race stat. - Bigger fonts on 256x64+. - Offline-deterministic golden/functional test harness (test/) fed by a real ESPN snapshot; _get_latest_round persists via the cache so qualifying/ sprint/practice resolve offline. Bumps manifest to 1.5.1 and syncs plugins.json. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ
e5d6725 to
c8bc127
Compare
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
plugins/f1-scoreboard/f1_renderer.py (1)
1526-1532: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd an explicit
strict=tozip().
rows,ys, androw_heightsare all derived from the samerowslist so they are equal length today; making that a hard invariant (strict=True) protects the row-placement loop against a future divergence silently dropping a row, and clears the Ruff B905 hint.♻️ Proposed change
- for (text, font, fill), ry, rh in zip(rows, ys, row_heights): + for (text, font, fill), ry, rh in zip(rows, ys, row_heights, strict=True):🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/f1-scoreboard/f1_renderer.py` around lines 1526 - 1532, Update the row-placement loop in the scoreboard renderer to call zip() with strict=True for rows, ys, and row_heights, preserving the existing iteration and filtering behavior while enforcing equal-length inputs.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@plugins.json`:
- Around line 215-218: Update the F1 plugin manifest entry so its last_updated
field matches the release date for latest_version 1.5.1: 2026-07-18. Leave the
verified, screenshot, and latest_version fields unchanged.
In `@plugins/f1-scoreboard/config_schema.json`:
- Around line 629-634: Update the f1-scoreboard plugin version in manifest.json
to a MINOR bump for the new customization.auto_scale option and expanded font
enums, changing both the manifest version field and the top versions[] entry
from the reported PATCH version to the corresponding 1.x.0 release.
In `@plugins/f1-scoreboard/f1_data.py`:
- Around line 1117-1123: The persistent latest-round lookup in _get_latest_round
must not use the one-hour "standings" TTL. Update the _get_cached call for
round_key so the seeded last-known round uses a much longer or unbounded cache
age, while preserving the existing in-memory cache update and return behavior.
In `@plugins/f1-scoreboard/test/harness.json`:
- Around line 2-10: The _comment in the harness configuration is inaccurate:
update its freeze-time and race description to reflect Barcelona as round 7 on
2026-06-14 and freeze_time 2026-06-16, without claiming round 9 has completed;
revise the size rationale to enumerate every entry in sizes, including 64x32 and
256x32.
---
Nitpick comments:
In `@plugins/f1-scoreboard/f1_renderer.py`:
- Around line 1526-1532: Update the row-placement loop in the scoreboard
renderer to call zip() with strict=True for rows, ys, and row_heights,
preserving the existing iteration and filtering behavior while enforcing
equal-length inputs.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 3f1148e8-042d-4328-913a-4fda47668738
⛔ Files ignored due to path filters (6)
plugins/f1-scoreboard/test/golden/128x32/f1_upcoming.pngis excluded by!**/*.pngplugins/f1-scoreboard/test/golden/128x64/f1_upcoming.pngis excluded by!**/*.pngplugins/f1-scoreboard/test/golden/192x48/f1_upcoming.pngis excluded by!**/*.pngplugins/f1-scoreboard/test/golden/256x32/f1_upcoming.pngis excluded by!**/*.pngplugins/f1-scoreboard/test/golden/256x64/f1_upcoming.pngis excluded by!**/*.pngplugins/f1-scoreboard/test/golden/64x32/f1_upcoming.pngis excluded by!**/*.png
📒 Files selected for processing (13)
plugins.jsonplugins/f1-scoreboard/assets/fonts/10x20.bdfplugins/f1-scoreboard/assets/fonts/4x6.bdfplugins/f1-scoreboard/assets/fonts/5x8.bdfplugins/f1-scoreboard/assets/fonts/6x10.bdfplugins/f1-scoreboard/assets/fonts/7x13.bdfplugins/f1-scoreboard/assets/fonts/9x15.bdfplugins/f1-scoreboard/config_schema.jsonplugins/f1-scoreboard/f1_data.pyplugins/f1-scoreboard/f1_renderer.pyplugins/f1-scoreboard/manifest.jsonplugins/f1-scoreboard/test/fixtures/mock.jsonplugins/f1-scoreboard/test/harness.json
👮 Files not reviewed due to content moderation or server errors (2)
- plugins/f1-scoreboard/assets/fonts/10x20.bdf
- plugins/f1-scoreboard/assets/fonts/9x15.bdf
| "auto_scale": { | ||
| "type": "boolean", | ||
| "title": "Auto-scale fonts to display size", | ||
| "description": "Leave the font fields below blank to auto-pick a crisp pixel (bitmap) font sized for your panel. This toggle only affects a scalable TTF you choose manually: on, it scales that TTF with panel size and snaps it to its pixel grid; off, it uses the exact size below.", | ||
| "default": true | ||
| }, |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Confirm the plugin version bump matches this new config option. Adding customization.auto_scale (and the expanded font enums) introduces a new, backward-compatible config option. Per the plugin versioning rules that is a MINOR bump, but the PR reports the plugin at 1.5.1 (a PATCH bump). Please verify manifest.json (both the version field and the top versions[] entry) reflects a MINOR bump rather than PATCH.
As per coding guidelines: "Bump MINOR version (1.x.0) for new features added, new config options (backward compatible)".
#!/bin/bash
# Inspect the plugin manifest version fields relative to the config_schema change.
fd -a manifest.json plugins/f1-scoreboard | xargs -I{} sh -c 'echo "== {} =="; cat "{}"'🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@plugins/f1-scoreboard/config_schema.json` around lines 629 - 634, Update the
f1-scoreboard plugin version in manifest.json to a MINOR bump for the new
customization.auto_scale option and expanded font enums, changing both the
manifest version field and the top versions[] entry from the reported PATCH
version to the corresponding 1.x.0 release.
Source: Coding guidelines
The calendar (upcoming-events) mode truncated long event names with an ellipsis and left the lower half of tall panels empty. On panels >=48px tall the event name now renders in a larger font, word-wrapped across multiple rows (no ellipsis) and centered in the space below the header; the session time moves up to the header row so the name gets the full width. 32-px-tall panels keep the compact single-row layout. Adds a reusable _wrap_text() helper. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ
- plugins.json / manifest.json: set last_updated to 2026-07-18 to match the
1.5.1 release date (was 2026-07-17).
- f1_data.py: the persistent last-known-round seed was read with the 1-hour
"standings" TTL, so it expired after an hour and couldn't bridge restarts or
offline gaps as intended. Read it under a new 24h "latest_round" category
(long enough to survive restarts/offline, still bounded so the round advances
to a newly completed race — an unbounded TTL would freeze it, since this read
short-circuits the fresh fetch).
- test/harness.json: correct the inaccurate _comment — Barcelona is round 7
(2026-06-14), freeze_time 2026-06-16 sits two days after it, later rounds are
rendering seeds (not "completed"), and enumerate every size (64x32, 128x32,
256x32, 192x48, 128x64, 256x64).
- f1_renderer.py: zip(rows, ys, row_heights, strict=True) to enforce the
equal-length invariant (_spread_ys always returns one y per row).
Skipped the "bump 1.5.1 -> 1.6.0" finding: this PR already added 1.4.0 and 1.5.0
minor entries, and auto_scale / the expanded font enums are documented under the
1.5.0 minor bump ("Crisp per-size bitmap (BDF) fonts"). 1.5.1 is a legitimate
patch (readability fixes) on top, so the features are already under a minor bump.
Validated: safety harness passes 48/48 (8 modes x 6 sizes), goldens match.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ
# Conflicts: # plugins.json
There was a problem hiding this comment.
🧹 Nitpick comments (4)
plugins/f1-scoreboard/f1_renderer.py (4)
330-362: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
_wrap_textlogic looks correct; one static-analysis nit.Traced the max_lines boundary and single-word-too-wide branches — no data loss or off-by-one found. Ruff flags the list concatenation on line 359.
🧹 Ruff RUF005 fix
- remaining = " ".join([lines[-1]] + words[i:]) + remaining = " ".join([lines[-1], *words[i:]])🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/f1-scoreboard/f1_renderer.py` around lines 330 - 362, Update the remaining-text construction in _wrap_text to avoid list concatenation flagged by Ruff RUF005, using an appropriate iterable-unpacking or equivalent approach while preserving the existing truncation behavior and output.Source: Linters/SAST tools
236-260: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicate size-scale formula between
_font_tierand_load_fonts.Both methods independently recompute
min(self.display_height / 32.0, max(1.0, self.display_width / 128.0))(assandtype_scale). If one is tuned later without the other, font-tier selection and the legacy-TTF scale factor will silently drift out of sync.♻️ Extract a shared scale helper
+ def _size_scale(self) -> float: + """Width-aware size scale used for both bitmap-font tiering and + legacy TTF scaling.""" + return min(self.display_height / 32.0, max(1.0, self.display_width / 128.0)) + def _font_tier(self) -> Tuple[str, str]: ... - s = min(self.display_height / 32.0, max(1.0, self.display_width / 128.0)) + s = self._size_scale() if s >= 1.75: ...def _load_fonts(self) -> Dict[str, Any]: ... - type_scale = min(self.display_height / 32.0, max(1.0, self.display_width / 128.0)) + type_scale = self._size_scale()🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/f1-scoreboard/f1_renderer.py` around lines 236 - 260, Extract the shared width-aware scale calculation into a helper near _font_tier, then reuse it for both the tier selection variable in _font_tier and the legacy-TTF scale factor in _load_fonts. Remove the duplicated inline formula while preserving the existing scaling behavior.
279-291: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win
_load_fontskips the native-BDF-size retry that the rest of the codebase relies on.For BDF fonts already in
_BDF_NATIVE_SIZE,_load_fontspasses the correct native pixel size, so this path is safe today. But for a user-configured BDF font that isn't in that dict (customfontoverride with no matching entry),_load_fontwill try the computed/scaled size, and if that isn't the font's true strike size, FreeType raises for an invalid pixel size — caught here only by(OSError, IOError)— and the code falls straight through to the generic default font instead of retrying at the BDF's real native size.plugins/baseball-scoreboard/sports.pyalready implements this retry-with-native-size fallback; consider reusing that pattern here so custom bitmap fonts degrade to their own crisp bitmap instead of an unrelated default font.As per coding guidelines,
plugins/**/*.py: "Ensure plugins render correctly at all supported panel sizes... without overflow, crashes, or incorrect scaling" — a silently wrong fallback font undermines that on custom BDF configs.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/f1-scoreboard/f1_renderer.py` around lines 279 - 291, Update _load_font to retry BDF font loading at the font’s native size when loading at the requested size fails, reusing the existing native-size fallback pattern and _BDF_NATIVE_SIZE data used by _load_fonts. Preserve the existing path iteration and generic ImageFont.load_default fallback when no native-size retry succeeds.
133-135: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove or use
self.is_wide_short. It’s set here but never read inplugins/f1-scoreboard/f1_renderer.py, so the 256×32 layout path isn’t actually using this size hint.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/f1-scoreboard/f1_renderer.py` around lines 133 - 135, Address the unused self.is_wide_short flag in the renderer initialization by either removing the assignment or wiring this size hint into the 256×32 layout decision path. Ensure the selected approach makes the wide-short display condition effective rather than leaving the attribute unread.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@plugins/f1-scoreboard/f1_renderer.py`:
- Around line 330-362: Update the remaining-text construction in _wrap_text to
avoid list concatenation flagged by Ruff RUF005, using an appropriate
iterable-unpacking or equivalent approach while preserving the existing
truncation behavior and output.
- Around line 236-260: Extract the shared width-aware scale calculation into a
helper near _font_tier, then reuse it for both the tier selection variable in
_font_tier and the legacy-TTF scale factor in _load_fonts. Remove the duplicated
inline formula while preserving the existing scaling behavior.
- Around line 279-291: Update _load_font to retry BDF font loading at the font’s
native size when loading at the requested size fails, reusing the existing
native-size fallback pattern and _BDF_NATIVE_SIZE data used by _load_fonts.
Preserve the existing path iteration and generic ImageFont.load_default fallback
when no native-size retry succeeds.
- Around line 133-135: Address the unused self.is_wide_short flag in the
renderer initialization by either removing the assignment or wiring this size
hint into the 256×32 layout decision path. Ensure the selected approach makes
the wide-short display condition effective rather than leaving the attribute
unread.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: bf29529c-5beb-4985-b706-b7c579880b3d
📒 Files selected for processing (5)
plugins.jsonplugins/f1-scoreboard/f1_data.pyplugins/f1-scoreboard/f1_renderer.pyplugins/f1-scoreboard/manifest.jsonplugins/f1-scoreboard/test/harness.json
🚧 Files skipped from review as they are similar to previous changes (4)
- plugins/f1-scoreboard/test/harness.json
- plugins/f1-scoreboard/manifest.json
- plugins/f1-scoreboard/f1_data.py
- plugins.json
Reworks the F1 scoreboard renderer to adapt to every panel size (64×32 through 256×64) after reports that fonts/logos were too small and spacing looked off, then follows up with readability fixes reviewed live on a 256×64 rig.
What changed
_configure_layout()derives every size-dependent metric (stat zone, header reservations, logo sizes, spacing) from the actual panel dimensions and measured text. Font sizing is width-aware (min(h/32, max(1, w/128))), so tall-but-narrow panels like 128×64 no longer render oversized type that truncates names._font_tier()picks purpose-built pixel fonts by panel size (6x10/4x6 → 7x13/5x8 → 10x20/7x13), replacing the blocky PressStart2P default. Bundled underassets/fonts/. Config fields default to blank = auto-tier; users can still pick a specific bundled font._spread_ys()distributes card rows top-to-bottom (shared ys keep name/stat columns aligned), logos scale up (capped by width so 128×64 isn't crowded), and the upcoming card's circuit map is larger.test/harness.json+ a real ESPNfixtures/mock.jsonsocheck_plugin.pyruns fully offline (it previously hung ~13 min fetching live data). Golden images committed forf1_upcoming(the one mode that renders a full static frame). A small supporting change persists the latest-round lookup through the cache so qualifying/sprint/practice resolve offline.Testing
scripts/check_plugin.py --plugin f1-scoreboardpasses all 48 checks (8 modes × 6 sizes) — no crash, no overflow.Based on
main(includes the 1.3.26 x-advanced schema work). Bumps manifest to 1.5.1 and syncsplugins.json.🤖 Generated with Claude Code
https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ
Summary by CodeRabbit