Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Service Layer (Business Logic)
heartRateBluetoothService (HR Monitor)
routeService (Route Management)
workoutService (Workout Tracking)
rownativeService (Route Discovery / Import)
workoutGeneratorService (Structured Workouts)

External APIs & Hardware
Web Bluetooth API (PM5/HR Devices)
Expand Down Expand Up @@ -47,8 +47,10 @@ src/
BluetoothDevice.tsx # PM5 connection UI
HeartRateMonitor.tsx # HR monitor connection
HeartRateChart.tsx # HR visualization
HeartRateZonesChart.tsx # HR zones display
PM5Simulator.tsx # PM5 hardware simulator
RownativeRouteImport.tsx # rownative.icu search/import UI
WorkoutGenerator.tsx # Structured workout UI
WorkoutProgressDisplay.tsx
Comment on lines 49 to +53
MiniMetrics.tsx # Compact metrics overlay
PerformanceChart.tsx # Performance graphs
Canvas3DErrorBoundary.tsx
Expand All @@ -60,9 +62,9 @@ src/
bluetoothService.ts # PM5 Bluetooth communication
heartRateBluetoothService.ts # HR monitor service
pm5SimulatorService.ts # PM5 simulation
routeService.ts # Demo route data + imported routes
rownativeService.ts # rownative.icu course catalogue
routeService.ts # Route data management
workoutService.ts # Workout session tracking
workoutGeneratorService.ts # Structured workouts
Comment on lines 63 to +67
utils/
geoUtils.ts # Geographic calculations
gpuUtils.ts # WebGL/GPU detection
Expand All @@ -86,11 +88,11 @@ playwright/

`App.tsx` owns all application state. There is no global state library; all data flows via props and callbacks:

- `currentView`: `'routes' | 'workout'`
- `currentView`: `'routes' | 'workouts' | 'workout' | 'history'`
- `sessionState`: `'idle' | 'active' | 'paused'`
- `selectedRoute`, `currentSession` — active session data
- `selectedRoute`, `selectedWorkout`, `currentSession` — active session data
- `pm5Data`, `heartRateSamples` — live device streams
- `workoutHistory` — persisted sessions used for local personal-best tracking
- `workoutHistory`, `workoutProgress` — persistence + workout tracking
Comment on lines +91 to +95

A session auto-ends when `pm5Data.distance ≥ 99.5% of route.distance`. This auto-end is suppressed under the `window.__PLAYWRIGHT_TESTING` flag used by E2E tests.

Expand All @@ -112,6 +114,7 @@ The main 3D visualization component (~2000 lines) using React Three Fiber:
Fullscreen HUD rendered on top of the 3D canvas during an active workout:

- Live metrics: pace (s/500m), power (W), distance (m), elapsed time, cadence (SPM), heart rate
- Workout progress when a structured workout is active (segment type, time remaining, compliance)
- Pause/Resume control

### Services
Expand Down
18 changes: 8 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
# VirtualRow — Willowbrook Demo + rownative.icu Routes
# VirtualRow — Row Anywhere on Real Water Routes

[![Deploy Landing Page](https://github.com/MaximumTrainer/virtualrow/actions/workflows/pages.yml/badge.svg)](https://github.com/MaximumTrainer/virtualrow/actions/workflows/pages.yml)
[![Playwright E2E Tests](https://github.com/MaximumTrainer/virtualrow/actions/workflows/playwright-e2e-clean.yml/badge.svg)](https://github.com/MaximumTrainer/virtualrow/actions/workflows/playwright-e2e-clean.yml)

**[🌐 Website](https://maximumtrainer.github.io/virtualrow/) · [📦 GitHub](https://github.com/MaximumTrainer/virtualrow)**

VirtualRow is a web-based fitness application that lets you row on a bundled Willowbrook River demo route or imported rownative.icu courses while connected to your Concept2 PM5 indoor rower via Bluetooth. Experience immersive 3D rowing visualization from your home.
VirtualRow is a web-based fitness application that lets you row on virtual water routes while connected to your Concept2 PM5 indoor rower via Bluetooth. Experience immersive 3D rowing visualization from your home.

## Features

### Water Routes

One built-in demo route ships with the app, and additional routes are imported from rownative.icu:
VirtualRow now ships with one bundled demo route:

| Route | Location | Distance | Difficulty |
|---|---|---|---|
| Willowbrook River | Willowbrook Valley | 5.0 km | Easy |

Use Willowbrook as the demo/example route, then search and import additional courses from rownative.icu directly in the app.
To row real-world courses, search and import them directly from **rownative.icu** in the route selector.

### Immersive 3D Visualization

Expand All @@ -34,7 +34,7 @@ Use Willowbrook as the demo/example route, then search and import additional cou

- Connect directly to your Concept2 PM5 monitor via Web Bluetooth API (Concept2 CSAFE BLE profile)
- Real-time performance metrics: pace, distance, time, power, stroke rate, calories
- Connection persists across route selection and activity views
- Connection persists across route changes and UI navigation
- Built-in PM5 Simulator for testing and demo without physical hardware

### Heart Rate Monitoring
Expand Down Expand Up @@ -72,7 +72,6 @@ src/
BluetoothDevice.tsx # PM5 connection UI
HeartRateMonitor.tsx # HR monitor connection
PM5Simulator.tsx # PM5 hardware simulator
RownativeRouteImport.tsx # rownative.icu route search + import UI
MiniMetrics.tsx # Compact metrics overlay
PerformanceChart.tsx # Post-session performance graphs
Comment on lines 72 to 76
HeartRateChart.tsx # Live HR chart
Expand All @@ -85,9 +84,8 @@ src/
bluetoothService.ts # PM5 BLE communication
heartRateBluetoothService.ts # HR monitor BLE service
pm5SimulatorService.ts # PM5 simulation
routeService.ts # Demo route data + rownative route import
workoutService.ts # Session tracking + localStorage persistence
rownativeService.ts # rownative.icu catalogue + import
routeService.ts # Route data + rownative route import
workoutService.ts # Session tracking
Comment on lines 84 to +88
utils/
geoUtils.ts # Geographic calculations
gpuUtils.ts # WebGL/GPU detection
Expand All @@ -112,7 +110,7 @@ docs/

```bash
# Run unit tests (Vitest)
npm run test -- --run
npm test

# Run unit tests in watch mode
npm test -- --watch
Comment on lines 112 to 116
Expand Down
30 changes: 7 additions & 23 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="VirtualRow — Row the Willowbrook demo route or imported rownative.icu courses in real-time 3D, powered by your Concept2 PM5 via Bluetooth." />
<title>VirtualRow — Willowbrook Demo + rownative.icu Routes</title>
<meta name="description" content="VirtualRow — Row scenic water routes in real-time 3D, powered by your Concept2 PM5 via Bluetooth." />
<title>VirtualRow — Virtual Rowing on Water Routes</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet" />
Expand Down Expand Up @@ -559,7 +559,7 @@
Open Source · Web Bluetooth · No Account Required
</span>
<h1 id="hero-heading" class="fade-up delay-1">
Row the <span class="highlight">Willowbrook demo</span><br/>or import rownative.icu courses
Row on <span class="highlight">scenic water routes</span><br/>in your living room
</h1>
<p class="hero-sub fade-up delay-2">
VirtualRow connects your Concept2 PM5 to immersive 3D river environments
Expand Down Expand Up @@ -611,7 +611,7 @@ <h2 id="features-heading" class="section-title">
</h2>
<p class="section-body">
From real Bluetooth hardware integration to rownative.icu route imports,
VirtualRow delivers training-grade rowing sessions directly in your browser.
VirtualRow focuses on route selection and live on-water activity.
</p>
</div>
<div class="features-grid" role="list">
Expand Down Expand Up @@ -640,8 +640,8 @@ <h3>Immersive 3D Visualization</h3>
<path d="M12 8a8 8 0 0 0-8 8"/>
</svg>
</div>
<h3>Demo + Imported Routes</h3>
<p>Start on the Willowbrook River demo (~5 km), then search and import additional courses from rownative.icu. Progress syncs with your PM5 distance in real-time.</p>
<h3>Scenic Water Routes</h3>
<p>Row the bundled Willowbrook River (~5 km) or search rownative.icu and import real-world routes instantly.</p>
<div class="feature-tags">
<span class="feature-tag">Willowbrook demo</span>
<span class="feature-tag">rownative.icu import</span>
Expand All @@ -665,22 +665,6 @@ <h3>Concept2 PM5 Integration</h3>
</div>
</article>

<!-- Route Discovery -->
<article class="feature-card" role="listitem">
<div class="feature-icon" aria-hidden="true">
<svg viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18M9 21V9"/>
</svg>
</div>
<h3>Route Discovery from rownative.icu</h3>
<p>Search the mirrored rownative.icu course catalogue, import a route with one click, and keep the imported course alongside the Willowbrook demo in your route library.</p>
<div class="feature-tags">
<span class="feature-tag">Search</span>
<span class="feature-tag">One-click import</span>
<span class="feature-tag">Route library</span>
</div>
</article>

<!-- Heart Rate -->
<article class="feature-card" role="listitem">
<div class="feature-icon" aria-hidden="true">
Expand Down Expand Up @@ -733,7 +717,7 @@ <h2 id="screenshots-heading" class="section-title">See the core flow</h2>
<a class="screenshot-thumb" href="screenshot-route-selection.png" data-full-image>
<img
src="screenshot-route-selection.png"
alt="VirtualRow route selection screen — choose Willowbrook River or search rownative.icu routes"
alt="VirtualRow route selection screen — choose Willowbrook River or import a rownative.icu route"
width="640"
height="360"
loading="lazy"
Expand Down
Loading
Loading