Companion document to the main README. This is the user's guide to the F1SimHubLive Driver Picker window — every panel, button, column, and colour, plus the gotchas that aren't obvious from looking at it.
Path:
C:\Program Files (x86)\SimHub\F1SimHubLive-Picker.exeWindow size: 860 × 960 (resizable, MinWidth=760, MinHeight=540) Always-on-top: toggle via the 📌 Pin button in the header Privileges: runs asasInvoker— no UAC prompt, ever (v1.3.0+) Settings file:%APPDATA%\F1SimHubLive\F1SimHubLive.Settings.json
Standalone WPF live-timing replica that mirrors MultiViewer's leaderboard for the current F1 session. One click on a driver row writes the new DriverNumber to the plugin's settings.json; SimHub's FileSystemWatcher picks up the change within ~250 ms and your wheel flips to that driver inside ~1 second — no SimHub restart, no MultiViewer re-warm-up. The picker also shows per-driver live speed, RPM (for the selected driver), tyre, gap/interval, pit-stop count, sector segments, and sector times with personal-best (yellow) / session-best (purple) colour coding — matching MV's own UI.
The header is the always-visible strip at the top, dark-themed to match Windows 11 dark mode and MultiViewer's chrome.
┌──────────────────────────────────────────────────────────────────────────────┐
│ F1SimHubLive ▮▮▮▮▮▮▮▮▮░░░░░░ 8,742 RPM [ LEDs ON ] [ 📌 Pin ] │
│ HAM #44 │
└──────────────────────────────────────────────────────────────────────────────┘
| Element | Purpose |
|---|---|
F1SimHubLive label |
App name. Cosmetic only. |
HAM #44 sub-label |
Currently-active driver — TLA + racing number. Updates the instant you click a different row. |
| Horizontal LED bar | 15-LED preview that mirrors what your physical wheel's LEDs are doing right now: bottom 5 = green (low RPM), next 5 = red (mid RPM), top 5 = blue / shift-warning (high RPM, flashes at the shift point). Left→right = low→high RPM. Sourced from MV's CarData channel 0 (RPM) for the active driver. |
| RPM readout | Numeric RPM for the selected driver, updated ~5×/sec. Shows — when telemetry isn't flowing. |
LEDs ON / LEDs OFF toggle |
Master switch for the header LED bar. Turning OFF freezes the bar at all-dark. Does NOT affect the physical wheel — that's controlled by SimHub's own LED profile, not this app. |
📌 Pin toggle |
Always-on-top. When pinned, the window stays above SimHub, MultiViewer, the game, browsers, etc. — useful when running on a second monitor. |
Why it's there: the LED bar is a sanity check. If the picker shows LEDs lighting up but your wheel doesn't, the gap is between SimHub and the wheel (not between MV and SimHub). Conversely, if both are dark, look upstream — MV not connected, or no session loaded.
The header also carries the ⏯ Replay toggle (v1.8.0) which opens the on-demand Replay panel described next.
Click ⏯ Replay in the header to open the Replay panel. It drives the
plugin's F1Replay source: playback of any past F1 session straight from
F1's public archive — no MultiViewer and no F1 TV subscription for the data.
DATA only; the video stays on your own player (Apple TV / F1 TV / MultiViewer).
┌──────────────────────────────────────────────────────────────────────────────┐
│ ON-DEMAND REPLAY · direct from F1 (no MultiViewer) Lap 14/53 · 1× │
│ [2026 ▾] [ Austrian Grand Prix — Practice 1 ▾] [⟳] [Load] [● Go Live]│
│ [▶] [0.5×][1×][2×][4×] ───────●─────────────── 37:12 / 1:01:40 │
│ Sync to video — Lap [14] [Go] [◀ −0.5s] [+0.5s ▶] anchor once, run at 1× │
│ Live video delay (Apple TV) ──●───────────────────────────────────── 0 s │
└──────────────────────────────────────────────────────────────────────────────┘
| Control | What it does |
|---|---|
| Year dropdown | Season to browse. Defaults to the current year, back to 2018 (first season in F1's static archive). |
| Session dropdown | Every meeting + session for that year (Race, Qualifying, Practice 1–3, Sprint…), newest first, loaded from Index.json. |
⟳ |
Reload the session list (e.g. a just-completed session appeared). |
Load |
Start replaying the selected session. The plugin switches to the F1Replay source live — no SimHub restart. |
● Go Live |
Exit replay and return to the live source (F1Live). |
▶ / ⏸ |
Play / pause. |
0.5× / 1× / 2× / 4× |
Playback speed (engine allows up to 16×; the buttons cover the useful range). |
| Scrubber | Drag or click to seek anywhere in the session. |
| Position | elapsed / total as MM:SS / H:MM:SS. |
The video is a separate player the picker can't see, so you align it once:
- Read the lap number on your F1 TV / Apple TV picture, type it into Sync to video — Lap, press Go (or Enter). The data jumps to that lap.
- Fine-tune with ◀ −0.5 s / +0.5 s ▶ until the on-screen speed/gear matches the dash.
After that both run at 1× and stay aligned — re-anchor only if you seek the
video. The anchor (last position + speed) is remembered per session in
F1SimHubLive.ReplayPrefs.json, so reloading resumes where you left off.
Live video delay (Apple TV) is for the live case instead: when you watch
the live session on a delayed feed, slide it (0–30 s) until the near-live data
lines up with the picture. It writes BroadcastDelayMs and hot-reloads.
How it talks to the plugin: the panel writes
F1SimHubLive.ReplayCommand.json(monotonicSeq) and readsF1SimHubLive.ReplayStatus.jsonback for the live scrubber/state — both next toF1SimHubLive.Settings.jsonunder%APPDATA%\F1SimHubLive\, same atomic write pattern as the driver swap.
A single line of grey text that only appears when something is wrong:
| Message | Meaning |
|---|---|
| (no line) | All good. MV reachable, data flowing. |
Waiting for MultiViewer... |
The first call to http://localhost:10101 hasn't returned yet. Normal during startup. |
Live timing offline: <error> |
Three consecutive poll failures. MV is closed, no session is loaded, or networking is broken. Picker keeps retrying every 500 ms. |
Scrollable list of every driver in the current MV session, sorted by position. Each row is 72 px tall. Drivers with position 0 (no position assigned — pre-session, formation lap) are pushed to the bottom so the live order stays clean.
The scrollbar is dark-themed to match.
Note (v1.10.20): the driver block was restyled to match MV Live Timing — the position number now sits on the team colour and the TLA on a black/white inset painted in the team colour (one connected, thinner, rectangular unit). The screenshot above predates the restyle and still shows the older dark-grey-position / solid-team-colour-TLA tiles.
┌──┬────┬───────────────────┬──────┬───────────────┬───────────────┬──────┬───┬──────────────────┐
│ 1│LEC │ Leclerc │ 322 │ LAST 1:15.121 │ INT — │ ⓗ │ 2 │ ▮▮▮▮▮▮▮▮ ▮▮▮ │
│ │ │ Ferrari │ km/h │ BEST 1:14.928 │ LDR — │ L18 │ │ 19.546 35.382 │
│ │ │ │ │ │ │ │ │ 19.533 35.382 19.871│
├──┼────┼───────────────────┼──────┼───────────────┼───────────────┼──────┼───┼──────────────────┤
│ 2│HAM │ Hamilton │ 0 │ LAST IN PIT │ INT +0.182 │ ⓗ │ 2 │ ▮▮▮ ▮▮▮▮▮▮ │
│ │ │ Ferrari │ km/h │ BEST 1:15.110 │ LDR +0.182 │ L16 │ │ 31.975 39.008 │
│ │ │ │ │ │ │ │ │ 19.641 35.375 20.094│
└──┴────┴───────────────────┴──────┴───────────────┴───────────────┴──────┴───┴──────────────────┘
↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
pos TLA name/team speed LAST/BEST INT/LDR tyre pit sectors (3 cols × 3 rows)
| # | Column | Width | What it shows | Source |
|---|---|---|---|---|
| 0 | Position | 26 px | Driver's current race / qualifying position, shown on the team-colour base of the driver block with the number in auto black-or-white for contrast (restyled v1.10.20 — was a dark-grey tile). — when zero. |
TimingData.Lines[*].Position |
| 1 | TLA inset | 52 px | Three-letter abbreviation on a black/white contrast inset — black for light teams (Mercedes), white for dark teams (Ferrari) — with the TLA painted in the team colour, matching MV Live Timing (restyled v1.10.20 — was a solid team-colour tile; both halves now read as one connected, thinner block). | DriverList.Tla + TeamColour |
| 2 | Input cluster | 78 px | Dark circular ring with the current gear letter centered (N / R / 1–8), a blue throttle arc sweeping clockwise around the ring 0–100 %, and the integer RPM in white beneath. In races, a position-change arrow (green ▲ gained / red ▼ lost vs. grid, muted −0 for no change) sits alongside the ring. Broadcast-style — see Per-driver input cluster and Positions gained / lost vs. grid below. Added v1.7.4–v1.7.6; arrow added v1.10.17 (column widened 50→78 to seat it). |
MV CarData channels 0 (RPM), 3 (gear), 4 (throttle) for that car's racing number; arrow from TimingAppData.GridPos − Position |
| 3 | Speed | 72 px | Current car speed in km/h, big bold Consolas number with a tiny km/h under it. Updates ~5×/sec for every car (not just the selected one). 0 when telemetry is paused / driver in pit. |
MV CarData channel 2 for that car's racing number |
| 4 | LAST + BEST lap | 120 px | Two-row stack. LAST row shows the most recent completed lap (or IN PIT when the driver is in the pit lane). BEST row shows the personal best lap of the session. Colour-coded — see Time colour scheme below. |
TimingData.Lines[*].LastLapTime + TimingStats.Lines[*].PersonalBestLapTime |
| 5 | INT + LDR | 100 px | Two-row stack. INT = gap to the car directly ahead. LDR = gap to the race leader (blank for P1, who is the leader; 1 L for lapped cars). Both rendered with leading sign (+x.xxx). |
TimingData.Lines[*].IntervalToPositionAhead.Value + GapToLeader |
| 6 | Tyre badge | 50 px | Coloured circle with a single letter (S/M/H/I/W) representing the current compound, and the lap number of the stint (L18 = 18 laps on this set) under it. See Tyre colour scheme. |
TimingAppData.Lines[*].Stints[<last>] |
| 7 | Pit count | 30 px | Number of pit stops completed. Hidden when zero. | TimingData.NumberOfPitStops (falls back to Stints.Count - 1) |
| 8 | Sector strip | 260 px+ (star) | Three sectors side-by-side. Each sector has three rows (top = segment mini-bars, middle = current lap's sector time, bottom = personal-best sector time). See Sector strip below. | TimingData.Lines[*].Sectors[] + TimingStats.Lines[*].BestSectors[] |
History note: the
Name + teamcolumn that lived between TLA and Speed in v1.2.x–v1.7.3 was retired to free room for the v1.7.4 input cluster + widen theLAST/BESTandINT/LDRpill cells so MV's longer status pills (IN PIT,1 L) no longer truncate. Name + team is still in the underlyingDriverTimingRowmodel and surfaces via the TLA tile's tooltip if you need it.
Compact broadcast-style indicator added in v1.7.4 — v1.7.6, mounted between the TLA tile and the Speed column on every driver row.
╭──────╮
│ ╭──╮ │ ← thin blue arc sweeps clockwise around the gear ring
│ │ 6│ │ 0 % throttle = no arc, 100 % = near-full ring (gap at bottom)
│ ╰──╯ │
╰──────╯
10952 ← integer RPM, white Consolas Bold FS=12, centered
| Layer | Element | Source / formula |
|---|---|---|
| Bottom | Background ring — Ellipse, 34 × 34 px, Fill="#0F0F14", Stroke="#3A3A44" 2 px |
Static; one per row |
| Middle | Throttle arc — Path with one ArcSegment, blue #3399FF, 3 px stroke, round caps |
Throttle / 100 × 260° clockwise sweep starting at ~8 o'clock (StartAngleDegrees=140). Below 0.5 % throttle the arc renders empty so 0 % shows as a clean dark ring. |
| Top overlay | Gear letter — TextBlock, Consolas Black FS=18 white, centered |
Gear == 0 → "N", Gear < 0 → "R", otherwise the digit. |
| Below | RPM — TextBlock, Consolas Bold FS=12 white, centered |
Math.Round(Rpm) as integer; blank string when RPM is 0 so unloaded rows don't render a noisy 0. |
Update rate: every ~200 ms for every visible row (the picker polls MV CarData on a fixed 200 ms loop). Per-row cost is essentially zero — the parser was already walking Cars.*.Channels for the existing per-row speed update; gear / throttle / RPM are now extracted in the same single pass.
Why broadcast layout? Mirrors the small cluster F1 Live Timing renders to the left of each driver row on its own broadcast UI — same arc direction, same gap at the bottom, same gear-letter-in-circle convention. Familiar at-a-glance read for anyone who watches F1 sessions.
Where the focused-driver duplicate lives: the picker header (next to the RPM digit readout and LED preview strip) shows the same data for the currently-selected driver — vertical green throttle bar + big gear letter — so you can see your own driver's inputs at a glance while the row clusters show the field.
Under the hood — see docs/wpf-broadcast-visuals.md for the WPF arc-geometry technique, the IValueConverter math, and the broadcast-layout conventions that drive these choices. That doc is the "how to build this kind of thing" reference for anyone extending the picker (or porting the pattern to another WPF tool).
Added v1.10.17. In races only, each row shows the net positions the driver has gained or lost versus their starting grid slot, mirroring F1 official Live Timing:
| Indicator | Meaning |
|---|---|
| 🟢 ▲ N | Gained N positions since lights-out (current position better than grid) |
| 🔴 ▼ N | Lost N positions since lights-out |
| ⬜ −0 (muted grey) | No net change |
| (hidden) | Practice / qualifying — there is no grid, so nothing renders |
The grid slot comes from MV's TimingAppData.Lines[n].GridPos (a string) and the
change is GridPos − current Position. Plumbed via DriverTimingRow.PositionChange
HasGridPos, rendered byPositionChangeToTextConverter(the ▲/▼/−0 glyph + number) andPositionChangeToBrushConverter(green/red/grey). Verified live: e.g. STR starting 22nd up to P19 renders ▲3.
When a driver retires (TimingData.Lines[*].Retired == true) the picker matches
F1 Live Timing's out-of-race styling:
| Element | Behaviour | Version |
|---|---|---|
| LAST column | Shows a dark-maroon RETIRED pill instead of a lap time or the bright-red IN PIT pill. Retired takes precedence over in-pit — MV reports retired cars as Retired:true and InPit:true/Stopped:true simultaneously, so without the precedence they'd wrongly read IN PIT. |
v1.10.15 |
| Whole row | Dimmed to 42% opacity so out-of-race cars visibly fade back while the live field stays bright. Bound via RetiredToOpacityConverter on the row Button's Opacity; hover/pressed/IsCurrent styling is unaffected. |
v1.10.16 |
| Telemetry | Parked/broken cars whose ECU has stopped have their frozen telemetry suppressed — F1's feed carries a stopped car's last on-track sample forward indefinitely, so the picker blanks speed/RPM/gear once RPM+Speed+Gear stay unchanged across ~5s of feed time (clears instantly if the car genuinely resumes). Keyed on the feed's frame clock, so replay behaves identically. | v1.10.18 |
Lap times and sector times follow MultiViewer's universal convention:
| Colour | Meaning | When it applies |
|---|---|---|
| ⚪ White / off-white | Plain time, no flag | Default — the time is recorded but neither a PB nor SB |
| 🟡 Yellow | Personal Best | The driver's own fastest time of the session for this lap / sector, but not the fastest in the field |
| 🟣 Purple | Session Best (a.k.a. Overall Fastest) | The fastest time in the field for this lap / sector |
| 🔴 Red | In Pit | LAST cell only — shown when the driver is currently in the pit lane (otherwise rendered as a normal time) |
| ⬜ Grey (dim) | Empty / not set | Sector or lap has no recorded time yet |
The picker pulls the authoritative Position field from MultiViewer's TimingStats endpoint — Position == 1 means session best (purple), any non-zero position means at minimum a personal best (yellow). This is the same source MV's own UI uses, so colours match exactly.
Each driver row's right-most column is a three-sector strip mirroring MV's leaderboard:
┌───────────┬───────────┬───────────┐
│ ▮▮▮▮▮ │ ▮▮▮▮▮▮▮▮ │ ▮▮▮▮ │ ← mini-bar segments (top row)
│ 19.546 │ 35.382 │ 19.871 │ ← current sector time (middle row)
│ 19.533 │ 35.382 │ 19.871 │ ← personal-best sector time (bottom row)
└───────────┴───────────┴───────────┘
S1 S2 S3
Top row — segment mini-bars: F1's marshalling system divides each sector into 5–10 "mini-sectors." Each bar represents one mini-sector and is colour-coded by the driver's instantaneous status through it:
| Bar colour | Meaning |
|---|---|
| 🟡 Yellow | Normal, no special flag |
| 🟢 Green | Personal best mini-sector |
| 🟣 Purple | Session-best mini-sector |
| ⬛ Dark grey | Mini-sector not yet completed |
| 🟦 Blue | Pit lane (driver is in the pit during this mini-sector) |
| 🔴 Red | Yellow flag / yellow flag active for this mini-sector (rare; shows briefly) |
Middle row — current sector time: The time the driver just set for this sector on the current lap. Colour-coded per the time scheme.
Bottom row — personal-best sector time: The driver's fastest time ever in this sector during the session. Yellow if it's only their PB; purple if it's also the field's session best. Blank during early laps before any sector has been completed.
Cross-reference: if the bottom row is purple, you'll also see a purple mini-bar in the top row for whichever mini-sector inside that sector belongs to the SB lap.
MV-standard F1 tyre compound colours, used on the badge in column 6:
| Letter | Compound | Colour | Hex |
|---|---|---|---|
| S | Soft (slick) | Red | #E83A3A |
| M | Medium (slick) | Yellow | #F5C518 |
| H | Hard (slick) | White | #F5F5FA |
| I | Intermediate (wet) | Green | #3FD06A |
| W | Wet (full wet) | Blue | #3C9CF0 |
| ? | Unknown / test compound | Grey | #7F7F8A |
The L<n> text under the letter is the stint age in laps (resets to 0 on pit-stop with a tyre change).
The driver currently selected (i.e. the one your wheel is showing) gets a coloured border that runs around the entire row. This is the same driver shown in the header's HAM #44 sub-label. When you click a different row, the border jumps immediately to confirm the click was registered (~250 ms before the wheel itself catches up).
- The row flashes green for ~500 ms (visual confirm).
- The picker writes
DriverNumber= that driver's racing number to%APPDATA%\F1SimHubLive\F1SimHubLive.Settings.json. - SimHub's
FileSystemWatcher(also pointed at the same per-user path) picks up the file change within ~250 ms. - The wheel's LEDs, speed, gear, RPM, gap, sectors swap to the new driver inside ~1 second total.
No SimHub restart, no MultiViewer re-warm-up.
Mouse wheel or click-drag the dark scrollbar on the right. There's no virtualisation — all 20 driver rows are always in memory, so scrolling is instant.
Toggle the 📌 button. State is not persisted across launches (intentional — most users want the default behaviour to depend on the screen they're launching it on).
Drag any edge. The driver list expands; the header stays fixed-height. Minimum size is enforced at 760 × 540 — below that the columns start to collide.
Closing the picker window doesn't stop the plugin. The wheel keeps showing whichever driver was last selected. To switch drivers without the picker open, edit settings.json manually — same hot-reload path.
The picker hits MultiViewer's local REST API at http://localhost:10101. All endpoints under /api/v1/live-timing/<feed> are SignalR feeds proxied as JSON.
| Endpoint | Poll rate | Used for |
|---|---|---|
/api/v1/live-timing/DriverList |
every 30 s | Driver TLA, name, team, team colour |
/api/v1/live-timing/TimingData |
every 500 ms | Position, current sector times, lap times, pit status, gap, interval, segments |
/api/v1/live-timing/TimingAppData |
every 500 ms | Stints / tyre compound / stint age / pit count |
/api/v1/live-timing/TimingStats |
every 500 ms | Authoritative personal-best lap + best-sector times with Position ranking (this is what makes PB / SB colours stay correct even when joining mid-session) |
/api/v2/livetiming/cardata/<lap> (or live equivalent) |
every 200 ms | Per-driver telemetry channels — 0 = RPM, 2 = speed (km/h), 3 = gear (int, 0=N, -1=R, 1–8), 4 = throttle (%). Channels 0 / 2 / 3 / 4 are all harvested per car in a single pass to feed the row's input cluster + speed cell (v1.7.4+). |
If MV is closed or any endpoint 404s, the picker logs the failure to the status line and keeps retrying. TimingStats failures are silently tolerated — older MV builds without that endpoint fall back to a client-side running-min PB tracker (less accurate for mid-session joins, but functional).
The picker doesn't have its own settings file. Behaviour is controlled by two fields in the plugin's settings.json:
| Field | Default | Effect |
|---|---|---|
DriverNumber |
"44" |
The currently-active driver. Picker writes here on click. Plugin watches the file and hot-reloads. |
AutoLaunchPicker |
false |
When true, the plugin spawns the picker every time SimHub starts. As of v1.3.0 this is fully unattended — the picker runs as asInvoker (no UAC), so it just opens silently when SimHub does. Safe to leave on permanently. |
Picker opens but shows "Waiting for MultiViewer..." forever:
- MultiViewer isn't running, OR no session is loaded, OR you haven't clicked "Replay Live Timing" on a recorded session.
- Confirm in a browser:
http://localhost:10101/api/v1/live-timing/DriverListshould return JSON, not 404 / connection refused.
Drivers appear but stay in race-number order, not position order:
- The session hasn't run far enough to assign positions (formation lap, pre-session). Normal — positions fill in once running laps start.
Last lap shows IN PIT but driver is clearly on track:
- MV's
InPitflag has a few-second latency at the pit exit. Self-resolves on the next tick.
Sector mini-bars all yellow, never purple/green:
- Either no PB has been set yet this session, or
TimingStatsisn't being served (older MV build). Bottom row of the sector strip will also be empty in that case. Update MV to the latest.
Click doesn't change the wheel:
- The row flash means the click was registered. If
settings.jsondidn't change, check%APPDATA%\F1SimHubLive\F1SimHubLive.Settings.json— antivirus or Controlled Folder Access can block writes to AppData. - If
settings.jsonDID change but the plugin didn't react, check SimHub's plugin log for[F1SimHubLive]— should showDriver changed: 44 → 12. Both plugin and picker resolve viaSettingsPathResolverto the same per-user path, so a path mismatch should be impossible.
Stale settings after upgrading from v1.2.x:
- v1.3.0 moved the settings file from
C:\Program Files (x86)\SimHub\to%APPDATA%\F1SimHubLive\. The first time either the picker or the plugin runs after upgrade, the legacy file is automatically migrated to the new per-user location (preserving driver number, RPM shift range, MV URL, etc.). The legacy file is left in place — it's no longer used and can be deleted manually if desired.
Times look wrong / different from MultiViewer's leaderboard:
- This shouldn't happen as of v1.2.4, which sources
BestSectorsandPersonalBestLapTimedirectly from MV'sTimingStatsendpoint. If you see a divergence, file an issue with a screenshot of both windows side-by-side.
| Version | UI changes |
|---|---|
| v1.1.0 | Original Driver Picker — small (~320 × 640) team-grouped grid, click-to-switch. |
| v1.2.0 | Live-timing replica view introduced. Vertical RPM LED strip on left, scrollable driver list with TLA tiles + name + team. |
| v1.2.1 | Dark title bar + dark scrollbar (matches Windows 11 + MV). |
| v1.2.2 | LED strip relocated from left vertical column into the header bar (horizontal). LED order corrected (green → red → blue runs left → right). |
| v1.2.3 | Per-driver speed column (km/h) added. Sector strip restructured to three rows (segments / current / best). Row height 64 → 72; window 760 → 860 wide. |
| v1.2.4 | TimingStats endpoint wired up — PB / SB colours on lap and sector times now match MV exactly, even when joining mid-session. |
| v1.3.0 | Picker manifest changed from requireAdministrator to asInvoker — no UAC prompt on launch, ever. Settings file moved from C:\Program Files (x86)\SimHub\F1SimHubLive.Settings.json to %APPDATA%\F1SimHubLive\F1SimHubLive.Settings.json; automatic one-shot migration from the legacy path on first run. AutoLaunchPicker = true is now fully unattended. |
| v1.3.1 | Picker now refreshes driver team info (TLA, last name, team name, team colour) when MultiViewer switches sessions while the picker is running. Previously those four fields on DriverTimingRow were declared { get; init; } (C# init-only), so once a row was created the team paint was locked in — most visible when loading a historical-season replay in MV after the picker was already open: drivers who exist in both eras (Hamilton, Verstappen) kept their first-poll team while era-unique drivers (Räikkönen, Bortoleto) painted correctly. Converted those four fields to mutable INPC properties with a refresh pass in the row-exists branch of ApplySnapshot. |
| v1.3.2 | Picker INT and LDR columns now show real values. The poll loop was reading line["TimeDiffToFastest"] and line["TimeDiffToPositionAhead"] — those names don't exist on MultiViewer's public /api/v1/live-timing/TimingData payload (they're signalr-internal). Both reads returned null → empty strings → the columns rendered as bare labels. Fixed to use the actual payload: GapToLeader (top-level string) and IntervalToPositionAhead.Value (nested object). Also renamed the second column from GAP to LDR to match MultiViewer's terminology. |
| v1.7.4 | Per-driver input cluster introduced — circular gear ring + RPM digit, mounted as a new 50 px column between the TLA tile and Speed on every row. Header gains a focused-driver cluster too (vertical green throttle bar + big gear letter) next to the existing RPM digit + LED preview. Telemetry plumbed via a new OnInputsBatch event on PickerTelemetryClient that walks MV Cars.*.Channels in a single pass and surfaces (Gear, Throttle, Rpm) per racing number. To make room without widening the window, the legacy Name + team column was retired (still accessible via TLA tooltip). |
| v1.7.5 | Blue throttle arc wrapped around the per-row gear ring — sweeps clockwise 0–260° as throttle goes 0–100 %, gap at the bottom, matching the F1 Live Timing broadcast layout. Implemented as a one-way IValueConverter (ThrottleToArcGeometryConverter) that builds a frozen PathGeometry with a single ArcSegment per throttle reading. Below 0.5 % throttle the arc renders empty so 0 % shows as a clean dark ring instead of a stray dot artefact. Path is marked IsHitTestVisible="False" so it doesn't swallow row-click events. See docs/wpf-broadcast-visuals.md for the geometry math. |
| v1.7.6 | RPM legibility tweak — per-row RPM digit changed from red #FF4040 SemiBold FS=10 to white #E8E8EE Bold FS=12. Red-on-dark was hard to read at small sizes against the dark grey row background; white at FS=12 reads at a glance without crowding the 50 px cluster column. No layout shift — the existing stack panel absorbs the 2 px font bump. |
| v1.8.0 | Replay panel added — a header ⏯ Replay button opens an on-demand replay UI driving the plugin's F1Replay source: year + session pickers from F1's public archive, play/pause, 0.5×–4× speed, scrubber, seek, seek-to-lap, one-time Sync to video — Lap + ±0.5 s nudge, and ● Go Live to return to live. Talks to the plugin via ReplayCommand.json / ReplayStatus.json under %APPDATA%\F1SimHubLive\. See Replay panel. |
| v1.10.15 | RETIRED pill in the LAST column for retired drivers (dark maroon), taking precedence over IN PIT — MV reports retired cars as Retired:true and InPit:true/Stopped:true at once. |
| v1.10.16 | Retired rows dimmed to 42% opacity via RetiredToOpacityConverter, matching F1 Live Timing's out-of-race fade. Hover/pressed/IsCurrent styling unaffected. |
| v1.10.17 | Position-change arrow (green ▲ gained / red ▼ lost vs. grid, muted −0 no change) added to each race row beside the gear ring; hidden in practice/qualifying. Change = TimingAppData.GridPos − Position; new PositionChangeToTextConverter + PositionChangeToBrushConverter; gear-cluster column widened 50→78 px to seat it. |
| v1.10.18 | Frozen-telemetry suppression for parked/broken cars — F1's feed carries a stopped car's last sample forward forever; the picker blanks speed/RPM/gear once RPM+Speed+Gear stay unchanged across ~5 s of feed time, so a stopped car reads as stopped (clears instantly on genuine resume). Keyed on the feed frame clock, so replay behaves identically. PickerTelemetryClient.ApplyStalenessFilter. |
Want to contribute an annotated screenshot? Drop a PNG into docs/screenshots/ named picker-<thing>.png and replace the <!-- Screenshot... --> placeholder with a standard markdown image tag.


