Thanks for your interest in improving Dove's DataViewer! This is an offline-first, open-source motorsport telemetry viewer, and contributions of all kinds are welcome — new file-format parsers, bug fixes, overlays, docs, and reusability rewrites especially.
By participating, you agree to abide by our Code of Conduct (CODE_OF_CONDUCT.md).
These are the rules the project lives by — please keep them in mind:
- Offline-first. 99% of features must work with no network. Only weather, satellite map tiles, and the optional admin backend are allowed to require connectivity.
- Never do on the server what you can do on the client. Telemetry parsing, lap detection, and visualization all happen in the browser. Data never leaves the user's device.
- Modular & reusable. Prefer small, composable modules over monoliths. Rewrites that make code more reusable are always welcome — line count is not a concern.
- Keep docs in sync. Update
README.mdwhen you add a parser, change an env var, or modify build params. Update the Credits list when you add a FOSS dependency. UpdateCLAUDE.mdwith new files/architecture notes.
- Node.js 18+ (or Bun)
git clone https://github.com/TheAngryRaven/DovesDataViewer.git
cd DovesDataViewer
npm install
npm run dev # dev server on http://localhost:8080The core app needs no environment variables — it runs fully offline. Env vars are only required for the optional admin backend (see the README).
| Command | Description |
|---|---|
npm run dev |
Dev server on port 8080 |
npm run build |
Production build to dist/ |
npm run preview |
Preview the production build |
npm run lint |
ESLint |
npm run typecheck |
Type-check via tsc -b |
npm test |
Vitest (watch) |
npm run test:run |
Vitest (single pass, CI-style) |
Note on typechecking: always use
npm run typecheck(tsc -b), not a baretsc --noEmit. The roottsconfig.jsonuses project references; plaintscfrom the root silently checks nothing and exits 0.
Run the same checks CI runs, and make sure they pass:
npm run lint
npm run typecheck
npm run test:run
npm run build
npm run test:coverage # enforces the coverage gateCI runs these as separate workflows on every PR. A PR won't be merged until they're all green.
Coverage is fully self-hosted — no third-party service ever receives our data.
- The gate is enforced by Vitest's line-coverage threshold in
vitest.config.ts. It currently sits at 1% (intentionally low so it passes today) — actual line coverage is much higher. Raise thethresholds.linesvalue as coverage grows to ratchet the floor up; that one number is the gate. - The badge in the README is generated by the
Coverageworkflow on push tomain: it writes a shields.io endpoint JSON and publishes it to an orphanbadgesbranch. The README badge reads that JSON directly fromraw.githubusercontent.com. - Per-PR summary: the workflow posts/updates a sticky coverage comment on each PR (overall totals + a per-file table), using only first-party GitHub Actions.
Locally: npm run test:coverage (gate), npm run coverage:badge and
npm run coverage:summary (generate the badge JSON / Markdown report into
coverage/).
The parser system is the most common contribution. Each parser is self-contained and auto-detected on import.
- Create
src/lib/xxxParser.tsexporting two functions:isXxxFormat(input: string | ArrayBuffer): boolean— format detectionparseXxxFile(input: string | ArrayBuffer): ParsedData— full parse
- Register it in
src/lib/datalogParser.ts— add the import and a detection check in bothparseDatalogFile()andparseDatalogContent(). - Respect detection order: binary formats first (MoTeC LD → UBX), then text formats from most-specific to least (VBO → MoTeC CSV → Dovex → Dove → Alfano → AiM → NMEA fallback).
- Add tests with a representative sample.
- Update the Supported File Formats table in
README.mdand the parser list inCLAUDE.md.
See src/types/racing.ts for the ParsedData / GpsSample shapes your parser
must produce, and src/lib/parserUtils.ts for shared helpers (haversine, speed
calculation, etc.).
- TypeScript + React 18. Function components and hooks only.
- Hooks are composable — each hook does one thing;
Index.tsxorchestrates. - Styling: Tailwind semantic tokens from
index.css. Never hardcode colors in components. - Comments: write them only when the why is non-obvious. Well-named code documents the what.
- Admin code is fully optional and gated behind env vars — the core app must have zero admin dependencies.
- Keep the initial bundle small: respect the
React.lazyboundaries andmanualChunksvendor splits described inCLAUDE.md.
CLAUDE.md at the repo root is the detailed architecture map — it's the best
single reference for how everything fits together.
- Fork the repo and create a topic branch (
feat/...,fix/...,chore/...). - Make focused commits with clear messages.
- Ensure lint, typecheck, tests, and build all pass locally.
- Update relevant docs (
README.md,CLAUDE.md, Credits) as noted above. - Open a PR against
mainand fill out the PR template.
Use the GitHub issue templates. For bugs, include the file format involved (parsers are format-specific) and, where possible, a sample file or the steps to reproduce.
Security issues: please do not open a public issue — follow the
disclosure process in SECURITY.md.