Replaces the scrolling stats-card layout with the full-bleed Map HUD:
UI-04's draggable/resizable HUD widgets float over UI-01's shared
background map (full opacity on this tab), and the old Pause/Stop/Resume/
Discard buttons become full-bleed, icon-only segments matching the
mockup's tertiary-container/error-container colors exactly. The idle
state keeps its prior compact layout deliberately -- HUD widgets only
appear once a ride exists, preserving the existing "a resting screen
must not look like a ride going nowhere" guarantee rather than
reinterpreting it.
State-machine logic (ticker, wakelock, speed subscription, error
handling) is untouched; every pre-existing record-screen test passed
unchanged against the rebuilt screen. Added tests that actually tap
Start/Pause/Stop and verify engine state changes, confirm HUD widgets
render over the map rather than replacing it, and verify the
mounted-mode speed digit's real contrast ratio against GlassPanel's
translucent surface specifically (the ticket's own named risk).
Corrects a UI-04 mistake found while implementing this ticket: the HUD's
default four metrics were ordered Speed/Distance/Elapsed/Max Speed, a
guess made before reading the actual Map HUD mockup HTML closely. The
real fixed row is Speed/Avg Speed/Dist/Time -- reordered HudMetric to
match and updated every test that asserted the old order.
Adds PulsingLocationMarker (UI-03) to RideMap's live usage via a new
showLocationMarker flag, and explicit tertiaryContainer/errorContainer
tokens to ripprColors so the control bar matches the design system's
literal values rather than an auto-derived tonal palette.
Verified end-to-end on a real emulator: Start, Pause, Resume, and Stop
all correctly drive the trip state machine with the full live map behind
everything. A lengthy false alarm during this verification (taps
appearing to do nothing) turned out to be a screenshot-scale
mis-measurement on the verification side, not an app defect -- resolved
by sampling pixel colors directly from the raw screenshot to find the
control bar's true on-screen position.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Xki7YAcc2TiN2PRZJ2tXr
Adds MapConnectivityState, a shared tracker of tile-fetch outcomes (cache
miss + network failure) that flips every map into an animated skeleton
after 3 consecutive failures and recovers on a single success -- either an
ordinary fetch succeeding, or (once TileLayer has been fully unmounted in
skeleton mode) a periodic single-tile probe every 15s. Detected at the
fetch level rather than via an OS connectivity API, since a captive portal
or degraded connection can report "online" while every real fetch times
out.
SkeletonMapLayer reuses the Stitch exports' 40px grid-overlay treatment
with a shimmer sweep, replacing TileLayer entirely (never fetching
underneath its own placeholder) while markers/polylines keep rendering
since they come from local data. RideMap and the route planner's
independent FlutterMap both wire this in via a plain skeletonMode bool.
Moved the tile-source constants into a new tiles/tile_config.dart so the
connectivity probe (in the app-layer composition root) doesn't need to
import from ui/ to build its request URL.
Verified end-to-end on a real emulator: cut network, cleared the tile
cache, confirmed the skeleton renders after real fetch failures, then
confirmed automatic recovery within one probe interval once network
returned -- not just via the widget/unit tests that also cover this.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Xki7YAcc2TiN2PRZJ2tXr
Replaces the push/pop stack rooted at Record with a StatefulShellRoute
(Map/Rides/Plan/Settings), each tab keeping its own navigator so Trip Detail
and the route planner push within their own branch. AppShell/ShellScaffold
host one shared RideMap instance behind every tab -- full opacity and
interactive on Map, dimmed and non-interactive elsewhere -- so the camera
position and live path survive a tab switch instead of being refetched.
Every tab's AppBar/header is removed per the redesign's no-chrome mandate.
Fixes a bug this surfaced: RideMap rendered a structurally different tree
for empty vs. non-empty points, which crashed once the map became a
long-lived shell background instead of a fresh per-screen widget. Unified
the background-map tree shape so it no longer remounts mid-recording.
Verified on Medium_Phone_API_35: all four tabs, live recording surviving
tab switches with the camera preserved, and the real HOME-key lifecycle
tile-drop from V3-04 still firing correctly in the shell context.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Xki7YAcc2TiN2PRZJ2tXr