openspec: propose journal route/activity detail catch-up changes
Three OpenSpec proposals to bring the journal's route/activity detail and feed closer to best-in-class outdoor apps, in the order we'll implement them: - activity-sport-type: nullable sport enum on activities (hike/walk/run/ ride/gravel/mtb/ski/other), set on create or normalized on import, shown as a badge + feed verb, federated as a Note PropertyValue. - activity-stats: one reusable StatRow + canonical metric set, sport-aware avg pace/speed, moving-vs-elapsed time. Derive-only, no schema change. - journal-elevation-profile: elevation profile chart + chart<->map "active distance" hover/click sync on the read-only detail pages. All three validate with `openspec validate --strict`. Specs only; no code. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
e3e01a34ae
commit
26b59b9c95
15 changed files with 602 additions and 0 deletions
60
openspec/changes/journal-elevation-profile/design.md
Normal file
60
openspec/changes/journal-elevation-profile/design.md
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
## Context
|
||||
|
||||
`@trails-cool/gpx` already parses trackpoints with elevation; the detail loaders
|
||||
already build GeoJSON for the map. The Planner's `elevation-map-interaction`
|
||||
spec defines the hover/click/drag behavior in an editing context (Yjs-backed).
|
||||
The Journal detail pages are read-only React Router routes that render a Leaflet
|
||||
map (`ClientMap` / `RouteMapThumbnail.client`) but no chart.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals**
|
||||
- A self-contained, read-only elevation profile on both detail pages.
|
||||
- A shared "active distance" so the map and chart highlight the same point.
|
||||
|
||||
**Non-Goals**
|
||||
- No editing (the Planner owns reshape/drag-select-to-zoom); clicking the chart
|
||||
centers the map but changes no data.
|
||||
- No multi-series overlay (HR/pace/cadence) — we don't store per-point fitness
|
||||
data, and it's out of scope/ethos.
|
||||
- No schema or API changes; the series is derived from existing `gpx`.
|
||||
|
||||
## Decisions
|
||||
|
||||
### D1: Series derivation
|
||||
A helper returns an array of `{ distance, elevation, lat, lng }` samples from
|
||||
the GPX (reusing the gpx package's parse), downsampled to a sensible cap (e.g.
|
||||
~500 points) for chart performance. Computed in the loader (server) so the chart
|
||||
component stays presentational.
|
||||
|
||||
### D2: Chart rendering
|
||||
An SVG area chart (no charting dependency): x = cumulative distance, y =
|
||||
elevation, gradient fill keyed to gradient steepness using the existing
|
||||
`map-core` elevation/gradient color scale. A summary strip beneath shows ascent,
|
||||
descent, highest, and lowest — formatted via the shared stat helpers from
|
||||
`activity-stats` when present, else local formatting.
|
||||
|
||||
### D3: Shared "active distance" state
|
||||
The detail page lifts an `activeDistance | null` state above both the map and
|
||||
the chart:
|
||||
- Hovering the chart sets `activeDistance`; the map shows a marker at the
|
||||
interpolated lat/lng for that distance.
|
||||
- Hovering the route polyline on the map sets `activeDistance` (nearest sample);
|
||||
the chart draws a crosshair/marker at that x.
|
||||
- Clicking the chart pans/centers the map on that point.
|
||||
This mirrors the Planner's interaction conceptually but is read-only and
|
||||
implemented independently for the Journal surface.
|
||||
|
||||
### D4: Graceful absence
|
||||
If the derived series has no elevation variation or no elevation data (e.g. a
|
||||
flat import or a planned route without elevation), the chart is not rendered and
|
||||
the existing gain/loss numbers stand alone.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **Map↔chart coupling**: the shared state must avoid render thrash on hover;
|
||||
throttle pointer updates and memoize the interpolation.
|
||||
- **Reuse vs. duplication with the Planner**: we deliberately implement a
|
||||
separate read-only component rather than lifting the Planner's editing
|
||||
component, to keep the Journal free of Yjs/editing concerns. The shared piece
|
||||
is the gpx series helper and the map-core color scale, not the component.
|
||||
Loading…
Add table
Add a link
Reference in a new issue