mirror of
https://github.com/AmanTahiliani/box-box.git
synced 2026-08-08 04:06:18 -04:00
Update CLAUDE.md and plan.md to document the web layer
CLAUDE.md previously described only the TUI; it now covers the web server (internal/web), React frontend, ingestion CLI, and the vitest/ playwright test commands. plan.md Phase 3 framing updated and the championship hub recorded. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
87
CLAUDE.md
87
CLAUDE.md
@@ -3,18 +3,31 @@
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
go build -o box-box ./cmd/main.go # Build binary
|
||||
go run cmd/main.go # Run directly
|
||||
go test ./... # All tests
|
||||
go build -o box-box ./cmd/main.go # Build binary
|
||||
go run cmd/main.go # Run TUI
|
||||
go run cmd/main.go --web --port 8080 # Run web server (default port 8080)
|
||||
go run cmd/main.go --ingest-year 2025 # Ingest a season into the domain DB (also: --ingest-meeting, --ingest-session, --ingest-news, --coverage)
|
||||
go test ./... # All Go tests
|
||||
go test -v ./internal/api # API integration tests (requires internet, rate-limit aware)
|
||||
OPENF1_API_KEY=key go run cmd/main.go # Run with paid tier (enables live session access)
|
||||
|
||||
# Frontend (run inside frontend/)
|
||||
npm run dev # Vite dev server on :5173, proxies /api to :8080 (override with BOXBOX_API_PORT)
|
||||
npm run build # tsc --noEmit && vite build -> frontend/dist
|
||||
npm run test # Vitest unit tests (frontend/src/test/); npm run test:watch for watch mode
|
||||
|
||||
# E2E / visual (run at repo root; Playwright auto-starts a seeded Go server + Vite dev server)
|
||||
npm run test:e2e # Playwright e2e (tests/*.spec.ts, playwright.config.ts)
|
||||
npm run test:visual # Visual snapshots (tests/visual/, playwright.visual.config.ts)
|
||||
npm run test:visual:update # Regenerate visual snapshots
|
||||
# :prod variants (test:e2e:prod, test:visual:prod) run against the *.prod.config.ts configs
|
||||
```
|
||||
|
||||
## Project Overview
|
||||
|
||||
**box-box** is an F1 Terminal UI (TUI) dashboard built in Go with Bubble Tea. It shows live timing, standings, race calendar, driver telemetry, track maps, and race replay — all sourced from the OpenF1 API.
|
||||
**box-box** is an F1 dashboard in Go with two frontends sharing the same data layer: a Bubble Tea TUI and a web app (`--web` flag) — a Go HTTP server in `internal/web/` serving a React SPA plus a REST/SSE API. It shows live timing, standings, race calendar, driver telemetry, track maps, and race replay — sourced from the OpenF1 API and a local domain SQLite DB filled by the ingestion CLI.
|
||||
|
||||
**Status**: Pre-beta, actively developed. All layers (API, models, UI) are fully implemented.
|
||||
**Status**: Pre-beta, actively developed.
|
||||
|
||||
## Tech Stack
|
||||
|
||||
@@ -23,12 +36,15 @@ OPENF1_API_KEY=key go run cmd/main.go # Run with paid tier (enables live sessio
|
||||
- **Bubbles** — TUI components (spinner, viewport, table)
|
||||
- **OpenF1 API** — F1 data at `https://api.openf1.org`
|
||||
- **gorilla/websocket** — Official F1 SignalR live feed
|
||||
- **modernc.org/sqlite** — HTTP response caching with TTL
|
||||
- **modernc.org/sqlite** — HTTP response cache + domain DB
|
||||
- **React 18 + Vite + TypeScript** — Web frontend (`frontend/`), TanStack Router + TanStack Query
|
||||
- **Vitest + Testing Library** — Frontend unit tests; **Playwright** — e2e and visual tests (repo root)
|
||||
|
||||
## File Map
|
||||
|
||||
```
|
||||
cmd/main.go Entry point (package main). Inits client, launches TUI.
|
||||
cmd/main.go Entry point. Flags: --web/--port (web server), --ingest-* /
|
||||
--backfill-season/--coverage (ingestion CLI), --db. Default: TUI.
|
||||
|
||||
internal/api/
|
||||
client.go OpenF1Client: HTTP client, 15s timeout, optional Bearer auth
|
||||
@@ -56,6 +72,40 @@ internal/ui/
|
||||
battles.go Sub-view: Auto-detected on-track battles with gap sparkline
|
||||
pitwindow.go Sub-view: Pit stop rejoin position calculator
|
||||
replay.go Sub-view: Lap-by-lap race replay scrubber
|
||||
|
||||
internal/store/ Domain SQLite DB (~/.local/share/box-box/boxbox.db), season/session data
|
||||
internal/ingest/ OpenF1 -> domain DB ingestion (driven by cmd/main.go --ingest-* flags)
|
||||
internal/query/ Read models over the domain DB, used by web handlers
|
||||
internal/news/ RSS/Atom paddock briefing feed ingestion (--ingest-news)
|
||||
internal/live/ Shared F1 SignalR live feed client + LiveStreamData types
|
||||
|
||||
internal/web/
|
||||
server.go HTTP server: route table, CORS/logging middleware, SPA file server.
|
||||
Serves frontend/dist if found (walks up from cwd), else embedded assets/
|
||||
api.go REST handlers under /api/v1/ (results, laps, telemetry, championship
|
||||
hub aggregation, news + readability article extraction, ...)
|
||||
live.go SSEHub + SignalR bridge: relays official F1 feed to SSE subscribers
|
||||
racehub.go /api/v1/race-hub: per-session payload assembled from the domain DB
|
||||
navigation.go Local-first navigation: /api/v1/seasons, meetings, sessions, weekend
|
||||
source.go ?source=openf1|local|auto data-source resolution
|
||||
assets/ Embedded fallback SPA (legacy vanilla JS; used when no frontend/dist)
|
||||
|
||||
frontend/ React + Vite + TypeScript SPA
|
||||
src/main.tsx Entry: QueryClientProvider + RouterProvider
|
||||
src/router.tsx TanStack Router: / (command center), /race-hub, /live, /championship,
|
||||
/briefing, /data-library (also /admin alias)
|
||||
src/api.ts Typed fetch wrappers for /api/v1/ endpoints
|
||||
src/types.ts TypeScript mirrors of API payloads
|
||||
src/pages/ CommandCenterPage, RaceHubPage, LiveTimingPage, ChampionshipPage,
|
||||
BriefingPage, DataLibraryPage
|
||||
src/components/ Shared components (Nav, TabBar, race hub views, live/ timing tower)
|
||||
src/lib/ Client helpers: live SSE parsing, schedule, coverage, GP identity
|
||||
src/test/ Vitest + Testing Library unit tests
|
||||
|
||||
tests/ Playwright e2e specs; tests/visual/ visual snapshot specs
|
||||
playwright*.config.ts Dev/prod e2e + visual configs (webServer blocks seed a temp domain DB
|
||||
and start Go API + Vite automatically)
|
||||
scripts/seed-e2e-db/ Seeds the throwaway domain DB used by Playwright runs
|
||||
```
|
||||
|
||||
## Architecture
|
||||
@@ -66,6 +116,17 @@ Each tab is a sub-model with `Init()`, `Update(msg)`, `View()`. The root `AppMod
|
||||
|
||||
Async work (API calls, WebSocket) returns `tea.Cmd` that emits typed messages back to Update. Use `tea.Batch()` for parallel fetches.
|
||||
|
||||
### Web Layer
|
||||
|
||||
`box-box --web` starts an HTTP server (default `:8080`) with three surfaces:
|
||||
|
||||
- **REST API at `/api/v1/...`** — Handlers in `internal/web/api.go` wrap `OpenF1Client`; navigation/race-hub endpoints read the domain DB via `internal/query` (empty responses if the DB is missing). `?source=openf1|local|auto` picks the data source where supported. Register routes in `routes()` in `server.go` — Go ServeMux longest-prefix matching means more specific paths (e.g. `/api/v1/laps/comparison`) must be registered before their prefixes.
|
||||
- **SSE live stream** — `internal/web/live.go` runs a background SignalR connection to the official F1 feed (exponential-backoff reconnect; disabled with `BOXBOX_DISABLE_LIVE=1`). An `SSEHub` broadcasts snapshots to browsers on `/api/v1/live/stream`, with `/api/v1/live/state` for the initial snapshot and a 20s heartbeat. `LiveTimingPage.tsx` consumes it; parsing helpers live in `frontend/src/lib/live.ts`.
|
||||
- **Embedded SPA** — Static file server with SPA fallback to `index.html`. Prefers a `frontend/dist` directory found by walking up from cwd (so `npm run build` output is served without rebuilding Go); otherwise serves the legacy assets embedded via `//go:embed assets`.
|
||||
- **Championship hub** — `/api/v1/championship/hub` aggregates official standings with derived stats (wins, podiums, poles, last-5 form, teammate head-to-head) and per-round cumulative points, computed from all season race results.
|
||||
|
||||
Frontend dev loop: run `go run cmd/main.go --web` and `npm run dev` in `frontend/` — Vite proxies `/api` to the Go server.
|
||||
|
||||
### Key Patterns
|
||||
|
||||
- **Two-phase standings load**: `GetLatestDriverChampionship()` -> extract SessionKey -> `GetDriversForSession(sessionKey)` -> join by DriverNumber for names/colors
|
||||
@@ -104,13 +165,19 @@ Replay: `h`/`l` or arrows scrub laps
|
||||
- **New message type**: Define in `messages.go`, handle in relevant model's `Update()`
|
||||
- **New keybinding**: Define in `keys.go`, handle in relevant model's `Update()`
|
||||
- **New styles**: Add to `styles.go`, reference F1 palette constants
|
||||
- **New web page/route**: Create page in `frontend/src/pages/`, register route in `frontend/src/router.tsx`, add nav link in `frontend/src/components/Nav.tsx`, add fetchers to `src/api.ts` and payload types to `src/types.ts`, add a test in `frontend/src/test/`
|
||||
- **New web API endpoint**: Add handler in `internal/web/api.go` (or a new file in `internal/web/`), register it in `routes()` in `server.go` (mind prefix ordering), add a handler test alongside (see `championship_hub_test.go`)
|
||||
|
||||
## Testing
|
||||
|
||||
Tests in `openf1_test.go` hit the real OpenF1 API. They use `skipOnRateLimit(t, err)` to gracefully skip on HTTP 429. Require internet.
|
||||
- Go: tests in `openf1_test.go` hit the real OpenF1 API and use `skipOnRateLimit(t, err)` to skip on HTTP 429 (require internet). `internal/web` handler tests run offline.
|
||||
- Frontend: Vitest + Testing Library in `frontend/src/test/` (`npm run test` inside `frontend/`).
|
||||
- E2E/visual: Playwright at repo root (`npm run test:e2e`, `npm run test:visual`). Configs seed a temp domain DB and start the Go server with `BOXBOX_DISABLE_LIVE=1` plus a Vite dev server — no manual setup needed.
|
||||
|
||||
## Environment
|
||||
|
||||
- `OPENF1_API_KEY` — Optional Bearer token for paid tier (live session WebSocket access)
|
||||
- Logs go to `box-box.log` in project root (prevents TUI pollution)
|
||||
- Cache at `~/.cache/box-box/cache.db` (SQLite WAL mode, auto-created)
|
||||
- `BOXBOX_DISABLE_LIVE=1` — Skip the background SignalR live feed in web mode (used by e2e)
|
||||
- `BOXBOX_API_PORT` — Go API port that the Vite dev proxy targets (default 8080)
|
||||
- Logs: TUI writes `box-box.log` in project root; web/ingest modes log to stderr
|
||||
- HTTP cache at `~/.cache/box-box/cache.db`; domain DB at `~/.local/share/box-box/boxbox.db` (override with `--db`)
|
||||
|
||||
3
plan.md
3
plan.md
@@ -28,12 +28,13 @@ New views that reconstruct the race narrative and make box-box indispensable dur
|
||||
|
||||
## Phase 3 — "Engineering Room" (Companion Web View)
|
||||
|
||||
A lightweight local web UI for visualizations that need a proper canvas.
|
||||
Originally scoped as a lightweight canvas for visualizations, this has grown into a full companion web app: a React + Vite SPA (command center, race hub, live timing, championship, briefing, data library) served by the Go server in `internal/web/`, with a `/api/v1` REST surface, an SSE relay of the F1 live feed, and Playwright e2e/visual coverage.
|
||||
|
||||
- [x] **`box-box --web` server** — Spawn a localhost SPA from Go embedded assets, sharing the same SQLite cache
|
||||
- [x] **SVG Track Map** — Animated car positions on a real circuit layout with team colors
|
||||
- [x] **Telemetry Overlay** — Interactive throttle/brake/speed graph through a lap (D3.js or Canvas)
|
||||
- [x] **Strategy Timeline** — Visual pit stop and stint timeline for the full field
|
||||
- [x] **Championship Hub** — `/championship` page backed by `/api/v1/championship/hub`: official standings aggregated with derived stats (wins, podiums, poles, last-5 form, teammate head-to-head) and per-round cumulative points progression
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user