Skip to content

feat(companion): ride timeline with zones, and the profile above the ledger - #2205

Merged
timohueser merged 13 commits into
developfrom
claude/ride-timeline
Sep 25, 2026
Merged

timohueser merged 13 commits into
developfrom
claude/ride-timeline

Conversation

@timohueser

Copy link
Copy Markdown
Owner

Summary

The ride page gets a ride timeline, and the elevation profile moves up on the route and trip-day pages.

  • The ride page order is: map, title and date, timeline, time in zones, ledger, then Replay ride.
  • The timeline shows elevation, speed, heart rate, power and cadence as strips on one distance axis. It shows a strip only for channels that the ride has. Heart rate and power are coloured zone by zone. A sideways drag moves one cursor through every strip and moves a dot on the map; a vertical drag still scrolls the page. Before the first scrub, each strip shows the ride's average.
  • Tapping a strip label opens a detail sheet with a larger chart, min, avg and max, and that channel's time in zones. For elevation, the line is coloured by gradient. There is no pinch-to-zoom.
  • Zones come from the limits recorded with each ride (Ride.zoneLimits, feat(firmware): record each ride's effort limits, v6 ride footer #2199). If a ride has no limits, one plain line says which limit was not set.
  • The model RideTimeline in OBCDomain builds the channels, downsamples them for drawing, and computes zones, time in zones and min/avg/max from every sample.
  • The route and trip-day pages show the profile above the ledger.
  • Sync: when the device does not answer the receipt, the app sends it again once before it shows a banner. The app logs each failure. The banner copy is plain. The ride page rebuilds when a synced ride returns at a new revision. specs/Ride_Archive_Contract.md describes the resend.
  • Fixtures: effort.json has a ride with all sensors and limits, a ride with sensors but no limits, and a ride with no sensors.

Public docs

Changed. ride-detail.webp and route-imported.webp are captured again for the new order, in their own docs: commit.

Checks

  • swift test in Packages/OBCKit: pass. This includes new tests for the timeline model, zone parity, time in zones and the receipt resend.
  • UI suites RideTimelineUITests (new), SyncTests, RouteDetailTests, ReplayTests, TripTests, MainScreenTests and GalleryLaunchTests: pass.
  • capture-website-screenshots.sh --check: pass.
  • The owner tested Release builds on a real iPhone, with the Kandel sensor ride synced from the development kit.

Requirements: none

🤖 Generated with Claude Code

timohueser and others added 13 commits September 25, 2026 11:38
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A route and a trip day lead with the profile under the title and the
device state, so it is no longer below the ledger.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RideTimeline builds elevation, speed, heart rate, power and cadence
from the tracklog. It downsamples each channel to drawing columns, and
it takes zones, time in zones and the time-weighted mean from every
sample. RideZoneLimits exposes its edge percents for zone bands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The effort set holds a climb with heart rate, power, cadence and zone
limits, a ride with a strap but no limits, and a ride without sensors.
Fixture fixes can carry a time and sensor values, and the ride summary
takes its sensor totals from them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The ride page shows the map, the title, a timeline of stacked channel
strips with one cursor, the time in zones and the ledger. A sideways
drag or a tap on a strip moves the cursor and a dot on the map. A tap
on a strip label opens that channel in a sheet: elevation coloured by
grade, zone bands behind heart rate and power, min, avg and max.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The time-in-zones and grade legends sit side by side and start a new row
only where the next item does not fit, instead of one item per line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The detail chart draws the line in zone colours segment by segment, as
the timeline strips do, and no longer shades zone bands behind it. The
zone edge percents return to private in RideZoneLimits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A ride that the device rewrote at a new revision can keep every sample
and change only its summary, such as its zone limits. The page now keys
on the ride's revision as well, so a sync that replaces the ride shows
the new zones at once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ords

A receipt that gets no answer is sent once more at the end of the sync
run, after a three-second pause, and only a second failure shows the
banner. Each unanswered receipt is logged with its error, which the app
discarded before. The banner says what happened in plain words, and its
action reads Try again instead of Resume when there is no download to
continue.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The legend takes its full width, so placement uses the rows it measured,
and tighter spacing fits all five zones on one row at the default text
size. A wrap spreads the items evenly across the rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The ride page keeps the approved order: map, title and date, timeline,
zones, ledger. The Replay ride button follows the ledger.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The ride page shows the timeline before the ledger, and the imported
route shows its profile above the ledger.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 25, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 785f8d98-758b-4725-8381-b85546259a90


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@timohueser
timohueser merged commit 286dd6b into develop Sep 25, 2026
25 checks passed
@timohueser
timohueser deleted the claude/ride-timeline branch September 25, 2026 11:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant