Colour effort by zone, and read distance in the rider's units
A watt is a fact; a zone is what it costs you. The biggest number on the ride screen was the same shade of white at 90 W and at 400 W, which is a thing no training app has done in fifteen years. Zones, with two rules: - **No reference, no zone.** An unset FTP draws the plain number. A zone measured against a guessed threshold would paint every ride with a confident lie. - **Colour never carries it alone.** "Z4" renders beside the swatch, so the meaning survives a colour-blind rider, a phone in direct sun and a black-and-white screenshot. Read off the rolling average, not the instantaneous watts: at 4 Hz the raw figure crosses two boundaries every pedal stroke, and a colour that strobes is worse than no colour. Not pedalling is not zone 1. Units are a display preference applied at the last step before the glass. Everything computed, stored and recorded stays SI, so a FIT file never depends on what the screen was set to. `format.ts` takes the unit system as an argument rather than reading a module-level setting — pure functions are what let every readout redraw the moment it changes. The `km` helper is gone rather than left beside `dist`, so there is no second way to format a distance that ignores the preference. Rust's block labels lose their baked-in kilometres. The block already carries start_x and end_x and the frontend renders that span in the rider's units; a kilometre in the text sat inside a sentence saying miles everywhere else. Also on the ride screen: - The gradient gets a wedge beside the number. A signed decimal has to be read; a slope is seen. Exaggerated and clamped, because a true-scale 6% is indistinguishable from 3% at 40 px wide. - What is coming, from the profile's own block list — "2.1 km at 12% in 460 m". The chart says where the rider is; what is about to happen is what decides whether to shift now. The data was already computed Rust-side and thrown away here. Close in, the small unit reads better than a fraction of the big one. - Mode and target merge into one chip. They are a single fact, and splitting them spent a chip of header width repeating the word "target". - The pod chip no longer reports a missing `+` pod while the `−` pod is connected. The `−` pod relays its twin, so that is the intended configuration — the ride screen was calling it a fault, contradicting the device screen two keystrokes away. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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 @@
|
||||
<span class="label">Loaded</span>
|
||||
<strong>{loaded.name}</strong>
|
||||
<span class="sample-sub">
|
||||
{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' : ''}
|
||||
</span>
|
||||
<button class="btn ghost danger" onclick={() => app.run(() => api.clearProfile())}>
|
||||
@@ -174,7 +176,7 @@
|
||||
<em>{b.kind}</em>
|
||||
{b.label}
|
||||
<span class="quiet">
|
||||
{axisValue(b.unit, b.endX - b.startX)}
|
||||
{axisValue(b.unit, b.endX - b.startX, app.units)}
|
||||
</span>
|
||||
</span>
|
||||
{/each}
|
||||
|
||||
Reference in New Issue
Block a user