feat(live): add web track map (#32)

Subscribe to SignalR Position.z and CarData.z, decode DEFLATE payloads, stream throttled positions over SSE, and render live car dots against cached track outline bounds with tap telemetry.

Alignment spike: no checked-in or locally cached real Position.z samples were available in this isolated worktree; verified both streams expose the official raw F1 X/Y/Z coordinate contract and implemented shared-bounds normalization against prefetched OpenF1 outlines. Fallback build-outline-from-stream was not taken.

Playwright remains out of scope for live rendering because BOXBOX_DISABLE_LIVE is used there; coverage is via parser, web handler/SSE, pure transform, and seeded component tests.
This commit is contained in:
Aman Tahiliani
2026-07-03 19:34:09 -04:00
committed by GitHub
parent 5408a45bbd
commit 7263949260
15 changed files with 1076 additions and 18 deletions

View File

@@ -2,10 +2,12 @@ import type {
ArticleContent,
ChampionshipHub,
LiveStateResponse,
LiveSessionMeta,
Meeting,
NewsItem,
RaceHub,
Session,
TrackOutline,
Weekend,
} from './types'
@@ -80,6 +82,20 @@ export async function fetchLiveState(): Promise<LiveStateResponse> {
return res.json()
}
export async function fetchLiveTrackOutline(
session: LiveSessionMeta,
year = new Date().getFullYear(),
): Promise<TrackOutline> {
const params = new URLSearchParams({ year: year.toString() })
if (session.MeetingName) params.set('meeting_name', session.MeetingName)
if (session.CircuitName) params.set('circuit_name', session.CircuitName)
const res = await fetch(`/api/v1/track-outline?${params.toString()}`)
if (!res.ok) {
throw new Error(`API ${res.status}: ${res.statusText}`)
}
return res.json()
}
export async function fetchNews(limit?: number, source?: string): Promise<NewsItem[]> {
const params = new URLSearchParams()
if (limit) params.set('limit', limit.toString())