Repository navigation
Unified status/onboarding/error screen - #24
Merged
Merged
Conversation
Adds the design spec for replacing ui.cpp's drawStatusScreen(), net.cpp's showProvisioningScreen(), and display.cpp's showError() with one shared renderer and content-fitting engine, plus the browser-based atlas (docs/superpowers/specs/2026-08-13-status-page- atlas.html) it was validated against across all 21 EE02-EE05 panel variants before this port began. No firmware behavior changes yet -- design only.
Task-by-task port of the validated atlas design into ui.cpp/net.cpp/ display.cpp: font-ladder sizing engine + reduction cascade (pure logic, host-testable), pixelarticons-derived bitmap icons, one shared drawFrameScreen() renderer, forced-portrait via a PortraitScope RAII guard. Verification split per the design spec: real native tests for the pure logic (all boards run it), build check on all 4 board envs, real flash+visual check on EE02 only (the only board physically available). Claude-Session: https://claude.ai/code/session_01TV93uMPitQ8sUgVSw5WmL5
…ation Task 3 of the unified-status-screen plan. Adds src/logic/status_content.h (host-testable, no Arduino deps) on top of Task 2's layout_math.h: - ScreenState/LineKind/StatIcon enums, ContentLine/ScreenContent structs - buildLines(): assembles a screen's content lines (header, stats, caption/scan/url, legend) under a given reduction level's ContentConfig - fitScreen(): top-level orchestration -- walks the reduction cascade, fits title/stat/chrome sizes with icon-column reserve, runs the height-aware shrink pass, and returns a ScreenFit ready for rendering test/test_status_content/main.cpp covers content assembly per state (Normal/Onboarding/Error), legend/detail reduction, and fitScreen termination/QR-floor behavior on both generous and tiny panels. Claude-Session: https://claude.ai/code/session_01TV93uMPitQ8sUgVSw5WmL5
…pper Replaces the old LayoutMetrics/currentLayout()-based status screen with drawFrameScreen(), the shared unified-frame renderer for the status/onboarding/error screens (header/status/action/legend zones sized by fitScreen()'s font-ladder engine). gatherLiveContent() gathers the battery/Wi-Fi/next-fetch fields shared by the status and error screens. drawStatusScreen() now calls epaper.update() itself instead of leaving it to the caller; main.cpp's runStatusMode() is updated to match (no more double update()). net.cpp is still on the old broken API at this point -- pio run -e ee02 fails only for reasons in net.cpp now, not ui.cpp/main.cpp.
Replaces the LayoutMetrics/currentLayout()-based onboarding screen with a thin wrapper around drawFrameScreen(ScreenState::Onboarding, ...): battery read fresh (no NVS metadata yet before a connection), Wi-Fi/ next-fetch left at "--" per the design spec's per-state field table. Also drops the stale "layout.h" include (deleted in the layout_math font-ladder rewrite, Task 2) which was the last thing keeping net.cpp off the removed API and blocking the board build. pio run -e ee02 -e ee03 -e ee04 -e ee05: all 4 SUCCESS -- first fully green board build since Task 2. pio test -e native: 84/84 passed.
Every GFXFF font on this firmware's ladder only covers 0x20-0x7E; U+00B7 (middle dot) falls outside that range and TFT_eSPI's drawString() silently skips it, rendering "98% \xC2\xB7 4.17V" as "98% 4.17V" (dot missing, double space) on real hardware. Replace all 7 occurrences (3 stat-detail lines, 2 each in the Normal/Error combined-legend strings) with " - ", which every font on the ladder -- including classic Font1/GLCD -- can render. Update test_buildLines_stat_detail_dropped_at_level3 to assert against the new " - " separator instead of the old UTF-8 byte sequence.
Finding 5: guard the statItemsRough/statItems array fills with explicit bounds (statCount < 3, i < 3) matching the chromeItems[9] loop's existing chromeCount < 9 guard a few lines below -- defense-in-depth even though buildLines() never currently emits more than 3 stat lines. Finding 7: remove display.h's now-dead #include "logic/wifi_strength.h"; its only consumer (drawWifiIcon()) was removed in an earlier task and nothing else in display.h/display.cpp references WifiStrength (confirmed via grep). battery_curve.h stays -- batteryColorForLevel() still needs BatteryLevel.
…finding 2)
fitScreen() picked each role's size independently via fitSize() based on
width alone, with no cross-role ordering constraint. On 480x800 portrait
panels (EE04/EE05-shaped), a short stat value or short URL could pick a
large ladder rung while the long title string got forced to a small rung
by the narrow row -- producing stat/chrome text visibly larger than the
title. This violated the design spec's Sizing engine intent: "hierarchy
is a rule to hold, not an accident of which role happened to be biggest."
Add a clamp right after the width-only fit computes sizes = RoleSizes{...}
(before the height-aware shrink-pass block): stat is capped to title,
chrome to (the post-clamp) stat. Clamping only ever shrinks a role -- it
can never grow one past what fitSize already proved fits its own width --
so it cannot reintroduce a width overflow.
Clamped via a new clampToLadder() helper (searches the role's own ladder
for the largest rung <= the cap via ladderFor()) rather than a raw
numeric min(): TITLE_SIZES/STAT_SIZES/CHROME_SIZES don't share identical
rungs, and fontFor() in ui.cpp switches on exact rung values (56/42/29/
22/16/8), falling through to its default: case for anything else.
New test: test_fitScreen_hierarchy_holds_on_480x800_panel, parameterized
over all three ScreenStates at 480x800, asserting chrome <= stat <=
title. Verified this test fails without the clamp (temporarily reverted
it to confirm) before restoring the fix.
See the final fix report for the reserve-recompute and ladder-clamp
reasoning: .superpowers/sdd/2026-08-18-unified-status-screen/final-fix-report.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces three visually unrelated screens (KEY1 status page, Wi-Fi onboarding, error page) with one shared renderer and one content-fitting engine, ported from a browser-validated design prototype (
docs/superpowers/specs/2026-08-13-status-page-atlas.html).src/logic/layout_math.hrewrite,src/logic/status_content.hnew) — host-testable, no Arduino dependencysrc/icons.h, new) replacing the old vector-drawn iconsdrawFrameScreen()renderer (src/ui.cpp) driving all three screens as thin wrappers (drawStatusScreen(),showProvisioningScreen(),showError())PortraitScopeguard, restoring the configured orientation afterwardSpec & plan
docs/superpowers/specs/2026-08-18-unified-status-screen-design.mddocs/superpowers/plans/2026-08-18-unified-status-screen.mdVerification
pio test -e native), including 23 new cases covering font-ladder selection, shrink priority, QR scannability floor, and the reduction cascadePortraitScope's force-then-restore was independently verified with rotation set to landscape (status screen stayed portrait; the panel's reported dimensions flipped back to landscape immediately afterward)Process
Built via
superpowers:subagent-driven-development— one implementer + task reviewer per plan task, plus a final whole-branch review that caught and fixed two real issues before merge:Both were fixed and re-reviewed clean.
Claude-Session: https://claude.ai/code/session_01TV93uMPitQ8sUgVSw5WmL5