diff --git a/src-tauri/src/profile_view.rs b/src-tauri/src/profile_view.rs index 8b7cdee..afa5b40 100644 --- a/src-tauri/src/profile_view.rs +++ b/src-tauri/src/profile_view.rs @@ -333,13 +333,17 @@ fn block_label(block: &Block) -> String { amplitude, repeats ), + // No distance in the label. The block already carries `start_x` and + // `end_x`, and the frontend renders that span in the rider's own units + // (FR-7.5a) — a kilometre baked into the text here would sit inside a + // sentence that says miles everywhere else. Block::Segments { segments } => { - let d: f64 = segments.iter().map(|s| s.distance_m).sum(); - format!("{} segments · {:.1} km", segments.len(), d / 1000.0) - } - Block::Terrain { points } => { - let d = points.last().map(|p| p.distance_m).unwrap_or(0.0); - format!("terrain · {:.1} km", d / 1000.0) + format!( + "{} segment{}", + segments.len(), + if segments.len() == 1 { "" } else { "s" } + ) } + Block::Terrain { .. } => "terrain".to_string(), } } diff --git a/ui/src/app.css b/ui/src/app.css index df9741d..537ad31 100644 --- a/ui/src/app.css +++ b/ui/src/app.css @@ -39,6 +39,25 @@ --power: #dfe8f3; --power-raw: #3f4d5d; + /* + * Effort zones (see `powerZone` in lib/format.ts). An ordered ramp, not a + * categorical palette: cool and quiet at the bottom, hot and loud at the top, + * so intensity reads from the colour before the number is even focused on. + * Four of the seven are the tones this stylesheet already uses for ok / warn + * / climb / bad, which keeps one vocabulary on the screen rather than two. + * + * Colour never carries the meaning alone — every zoned readout renders "Z4" + * beside the swatch. That is what makes this safe for the ~8% of male riders + * with a colour vision deficiency, and for a phone in direct sun. + */ + --zone-1: #7b8b9c; + --zone-2: #4aa8ff; + --zone-3: #35d9a0; + --zone-4: #ffcf4a; + --zone-5: #ff9a3c; + --zone-6: #ff5a52; + --zone-7: #c07cff; + /* Fallbacks; viewport.svelte.ts overwrites all of these on . */ --gap: 24px; --edge: 44px; diff --git a/ui/src/components/ProfileDrawer.svelte b/ui/src/components/ProfileDrawer.svelte index a6f06ba..504bada 100644 --- a/ui/src/components/ProfileDrawer.svelte +++ b/ui/src/components/ProfileDrawer.svelte @@ -10,7 +10,7 @@ import { open } from '@tauri-apps/plugin-dialog'; import { app } from '../lib/app.svelte'; import { api } from '../lib/bridge'; - import { axisValue } from '../lib/format'; + import { axisValue, dist, distUnit, elev, elevUnit } from '../lib/format'; import type { ProfileView } from '../lib/types'; let yaml = $state(''); @@ -136,9 +136,11 @@ Loaded {loaded.name} - {loaded.totalMetres ? `${(loaded.totalMetres / 1000).toFixed(1)} km` : ''} + {loaded.totalMetres ? `${dist(loaded.totalMetres, app.units, 1)} ${distUnit(app.units)}` : ''} {loaded.totalSeconds ? `${Math.round(loaded.totalSeconds / 60)} min` : ''} - {loaded.totalAscentM != null ? `· ${loaded.totalAscentM.toFixed(0)} m up` : ''} + {loaded.totalAscentM != null + ? `· ${elev(loaded.totalAscentM, app.units)} ${elevUnit(app.units)} up` + : ''} {loaded.looping ? '· loops' : ''} + + {#if show.routeChart}
- + +
+ +
+ {#if nextUp} + Next{nextUp.label} in {nextUp.away} + {/if}
{/if} @@ -314,27 +403,35 @@ - +
+ + + +
{#if !compact} - - + + {/if} @@ -380,8 +485,9 @@ value={num(d?.rollingPowerW ?? 0, 0)} unit="W" size="mid" - colour="var(--power)" - sub={`now ${num(snap?.telemetry.power_w ?? 0, 0)} W`} + colour={pZone?.colour ?? 'var(--power)'} + badge={pZone ? { label: pZone.short, colour: pZone.colour } : null} + sub={`${pZone ? pZone.name + ' · ' : ''}now ${num(snap?.telemetry.power_w ?? 0, 0)} W`} />