id: 3e2fc3a261e44d4988ca97e08b31f5ef
parent_id: 
item_type: 1
item_id: 50a09627d5d347009197b94bcee90411
item_updated_time: 1780927509563
title_diff: "[{\"diffs\":[[1,\"racecraft — Vue.js Web Client Notes\"]],\"start1\":0,\"start2\":0,\"length1\":0,\"length2\":35}]"
body_diff: "[{\"diffs\":[[1,\"# racecraft — Vue.js Web Client Notes\\\n\\\n> **Parent note:** [Sim Racing Telemetry Analysis Platform — Project Plan](joplin://6c0dcb2a567348fd9796f50c790082e4)\\\n> **Repo:** `~/WebstormProjects/racecraft`\\\n> **Companion:** [rusty-telemetry — REST API Reference](joplin://0c837f4e6b7e462a997cbc19e47c864a)\\\n> **Current version:** v0.1.4\\\n\\\n---\\\n\\\n## Tech Stack\\\n\\\n| Component | Technology |\\\n|-----------|-----------|\\\n| Framework | Vue 3.5 (Composition API, `<script setup>`) |\\\n| Language | TypeScript 5.7 |\\\n| Build | Vite 6 (dev server with API proxy to localhost:8080) |\\\n| State | Pinia 3 |\\\n| Routing | Vue Router 4 |\\\n| HTTP | Axios |\\\n| Charts | Chart.js 4 + vue-chartjs 5 |\\\n| Track maps | HTML Canvas 2D |\\\n\\\n---\\\n\\\n## Architecture\\\n\\\n```\\\nsrc/\\\n├── api/\\\n│   └── client.ts              — Axios HTTP client, all API functions\\\n├── components/\\\n│   └── FeedStatus.vue         — Per-feed health display\\\n├── router/\\\n│   └── index.ts               — Vue Router config\\\n├── stores/\\\n│   ├── sessions.ts            — Pinia store for session/run management\\\n│   ├── telemetry.ts           — Pinia store for live telemetry polling\\\n│   └── tracks.ts              — Pinia store for track model CRUD\\\n├── types/\\\n│   └── index.ts               — TypeScript interfaces matching API responses\\\n├── utils/\\\n│   └── analysis.ts            — Type guards for analysis results\\\n├── views/\\\n│   ├── DashboardView.vue      — Main dashboard (live feeds, status cards)\\\n│   ├── LiveView.vue           — Live telemetry gauges\\\n│   ├── SessionsView.vue       — Session list + create\\\n│   ├── SessionDetailView.vue  — Session detail, run management, analysis, track mapping workflow\\\n│   ├── ShiftPointsView.vue    — Shift point analysis charts\\\n│   ├── TrackMapView.vue       — Track map visualization (session analysis)\\\n│   ├── TracksView.vue         — Stored track model listing\\\n│   └── TrackDetailView.vue    — Track model detail with full geometry rendering\\\n├── App.vue\\\n└── main.ts\\\n```\\\n\\\n---\\\n\\\n## Key Pages & Features\\\n\\\n### Dashboard (`/`)\\\n- System status cards (API Server, Sim Connection, Recording) in 3-column grid\\\n- Active recording info\\\n- Quick links to sessions and tracks\\\n\\\n### Live View (`/live`)\\\n- Real-time telemetry gauges (speed, RPM, gear, throttle, brake)\\\n- Per-feed status panels via FeedStatus component\\\n- Polls `/api/live` endpoint\\\n\\\n### Sessions (`/sessions`)\\\n- Session list with status badges\\\n- Create new session form (use case selector including Track Mapping, label)\\\n- Delete sessions\\\n\\\n### Session Detail (`/sessions/:id`)\\\n- Session info card (use case, runs, timestamps, guidance)\\\n- **Run management:**\\\n  - Start run with feed selector and optional label\\\n  - Merge feeds toggle (select additional feeds to merge into the run) — v0.1.3\\\n  - Stop active runs\\\n  - Delete runs with confirmation dialog — v0.1.3\\\n- **Session lifecycle:**\\\n  - Complete session → triggers auto-analysis\\\n  - Reopen completed session (to add more runs) — v0.1.3\\\n  - Re-analyze button\\\n  - View full analysis (navigates to analysis-specific page)\\\n- **Quick stats** for shift points (recommended RPM, crossover count, runs analyzed)\\\n- **Quick stats** for track map (boundaries, total points)\\\n- **Track Mapping workflow** — v0.1.4\\\n  - Build Track Model button on completed track_mapping sessions\\\n  - Validates left + right boundary runs before allowing build\\\n  - POST `/api/tracks/build` with session_id\\\n  - Success message with link to Tracks view\\\n\\\n### Shift Points Analysis (`/analysis/shift-points/:id`)\\\n- Summary stats (recommended shift RPM, crossover points, runs analyzed, frames analyzed)\\\n- **Charts (in order):**\\\n  1. RPM vs. Force — x: RPM, y: acceleration_g (v0.1.2)\\\n  2. Speed vs. Force — x: speed_kmh, y: acceleration_g (v0.1.2)\\\n  3. Speed vs. RPM (Gear Ratios) — x: speed_kmh, y: rpm (v0.1.2)\\\n  4. Crossover Points — grouped bar chart (RPM + speed per gear change)\\\n- Crossover points table\\\n\\\n### Track Map (`/analysis/track-map/:id`)\\\n- Track boundary visualization via Canvas 2D\\\n- Center line overlay\\\n\\\n### Tracks (`/tracks`) — v0.1.4\\\n- List all stored track models from `GET /api/tracks`\\\n- Game label, track name, corner count, creation date\\\n- View and delete actions\\\n\\\n### Track Detail (`/tracks/:game/:trackName`) — v0.1.4\\\n- Full track model geometry rendered on Canvas 2D:\\\n  - Left boundary (red) and right boundary (blue)\\\n  - Center line with curvature heat map (green → red gradient)\\\n  - Corner markers with numbered labels\\\n  - Sector shading (3-color overlay)\\\n- Toggle controls for curvature, corners, sectors visibility\\\n- Stats cards: corners, sectors, center line points, source\\\n- Corners table (entry/apex/exit spline positions, peak curvature)\\\n- Sectors table\\\n- Metadata card (game, track name, created date, boundary point counts)\\\n\\\n---\\\n\\\n## API Client Functions\\\n\\\nDefined in `src/api/client.ts`:\\\n\\\n| Function | Endpoint | Added |\\\n|----------|----------|-------|\\\n| `getLiveTelemetry()` | GET `/api/live` | v0.1.0 |\\\n| `listRecordings()` | GET `/api/recordings` | v0.1.0 |\\\n| `getRecording(id)` | GET `/api/recordings/{id}` | v0.1.0 |\\\n| `startRecording(req?)` | POST `/api/recordings` | v0.1.0 |\\\n| `stopActiveRecording()` | POST `/api/recordings/stop` | v0.1.0 |\\\n| `stopRecording(id)` | POST `/api/recordings/{id}/stop` | v0.1.0 |\\\n| `deleteRecording(id)` | DELETE `/api/recordings/{id}` | v0.1.0 |\\\n| `listSessions()` | GET `/api/sessions` | v0.1.0 |\\\n| `getSession(id)` | GET `/api/sessions/{id}` | v0.1.0 |\\\n| `createSession(req)` | POST `/api/sessions` | v0.1.0 |\\\n| `deleteSession(id)` | DELETE `/api/sessions/{id}` | v0.1.0 |\\\n| `startRun(sessionId, req)` | POST `/api/sessions/{id}/runs` | v0.1.0 |\\\n| `stopRun(sessionId, runId)` | POST `/api/sessions/{id}/runs/{runId}/stop` | v0.1.0 |\\\n| `completeSession(id)` | POST `/api/sessions/{id}/complete` | v0.1.0 |\\\n| `getAnalysis(id)` | GET `/api/sessions/{id}/analysis` | v0.1.0 |\\\n| `reAnalyze(id)` | POST `/api/sessions/{id}/analyze` | v0.1.0 |\\\n| `deleteRun(sessionId, runId)` | DELETE `/api/sessions/{id}/runs/{run_id}` | v0.1.3 |\\\n| `reopenSession(id)` | POST `/api/sessions/{id}/reopen` | v0.1.3 |\\\n| `listTracks()` | GET `/api/tracks` | v0.1.4 |\\\n| `getTrack(game, trackName)` | GET `/api/tracks/{game}/{track_name}` | v0.1.4 |\\\n| `deleteTrack(game, trackName)` | DELETE `/api/tracks/{game}/{track_name}` | v0.1.4 |\\\n| `buildTrackModel(req)` | POST `/api/tracks/build` | v0.1.4 |\\\n\\\n---\\\n\\\n## TypeScript Types\\\n\\\nDefined in `src/types/index.ts`. Key interfaces:\\\n\\\n- `UseCase` — includes `shift_points`, `track_map`, `track_mapping`, `racing_line`, `braking_analysis`, `lap_times`, `sector_analysis`, `general`\\\n- `Game` — `assetto_corsa`, `assetto_corsa_competizione`, `project_cars_1`, `project_cars_2`\\\n- `StartRunRequest` — `{ feed_name, label?, merge_feeds?: string[] }` (merge_feeds added v0.1.3)\\\n- `SessionRun` — `{ id, label, feed_name, status, start_marker, end_marker, frame_count, data_truncated }`\\\n- `UseCaseSession` — `{ id, use_case, label, status, runs, analysis, guidance, recording_id }`\\\n- `ShiftPointAnalysis` — `{ gear_curves, crossover_points, recommended_shift_rpm, runs_analyzed, total_frames_analyzed }`\\\n- `GearCurve` — `{ gear, gear_display, run_label, data_points, min/max rpm, min/max speed, sample_count }`\\\n- `GearDataPoint` — `{ speed_kmh, rpm, acceleration_g }`\\\n- **Track model types (v0.1.4):**\\\n  - `TrackModel` — `{ game, track_name, center_line, left_boundary, right_boundary, track_width, heading, curvature, corners, sectors, source, created_at }`\\\n  - `TrackSummary` — `{ game, track_name, source, created_at, corner_count }`\\\n  - `Corner` — `{ index, number, entry_s, apex_s, exit_s, curvature_peak }`\\\n  - `Sector` — `{ index, start_s, end_s }`\\\n  - `BuildTrackModelRequest` — `{ session_id, game?, track_name? }`\\\n  - `TrackMappingAnalysis` — type alias for `TrackMapAnalysis`\\\n\\\n---\\\n\\\n## Pinia Store Actions\\\n\\\n### Sessions Store (`src/stores/sessions.ts`)\\\n\\\n| Action | Added |\\\n|--------|-------|\\\n| `fetchSessions()` | v0.1.0 |\\\n| `fetchSession(id)` | v0.1.0 |\\\n| `createSession(req)` | v0.1.0 |\\\n| `deleteSession(id)` | v0.1.0 |\\\n| `startRun(sessionId, req)` | v0.1.0 |\\\n| `stopRun(sessionId, runId)` | v0.1.0 |\\\n| `completeSession(id)` | v0.1.0 |\\\n| `reAnalyze(id)` | v0.1.0 |\\\n| `deleteRun(sessionId, runId)` | v0.1.3 |\\\n| `reopenSession(id)` | v0.1.3 |\\\n\\\n### Tracks Store (`src/stores/tracks.ts`) — v0.1.4\\\n\\\n| Action | Description |\\\n|--------|-------------|\\\n| `fetchTracks()` | List all stored track summaries |\\\n| `fetchTrack(game, trackName)` | Load full track model |\\\n| `deleteTrack(game, trackName)` | Delete track model |\\\n\\\n---\\\n\\\n## Change Log\\\n\\\n### v0.1.4 (2026-06-08) — Track Mapping Web Client\\\n\\\n**New Features:**\\\n- Track model types: `TrackModel`, `TrackSummary`, `Corner`, `Sector`, `BuildTrackModelRequest`\\\n- New use case: `track_mapping` added to UseCase type\\\n- Game type values updated to match API: `project_cars_1` / `project_cars_2` (was `project_cars1` / `project_cars2`)\\\n- New API client functions: `listTracks()`, `getTrack()`, `deleteTrack()`, `buildTrackModel()`\\\n- New Pinia store: `tracks` store for track model CRUD\\\n- New view: TracksView — list all stored track models with game labels, corner counts\\\n- New view: TrackDetailView — full geometry rendering with Canvas 2D:\\\n  - Left/right boundaries, center line with curvature heat map\\\n  - Corner markers with numbered labels\\\n  - Sector shading (3-color overlay)\\\n  - Toggle controls for curvature, corners, sectors\\\n  - Corners table, sectors table, metadata card\\\n- SessionDetailView: \\\"Build Track Model\\\" section for track_mapping sessions\\\n  - Validates left + right boundary runs\\\n  - POST `/api/tracks/build` button\\\n  - Success message with link to Tracks view\\\n- Dashboard: added \\\"Tracks\\\" quick action button, `track_mapping` use case label\\\n- SessionsView: added `track_mapping` to use case selector\\\n- App.vue: added \\\"Tracks\\\" nav item\\\n- Router: added `/tracks` and `/tracks/:game/:trackName` routes\\\n\\\n**Code Quality:**\\\n- `encodeURIComponent()` applied consistently to both `game` and `trackName` in track API URLs\\\n- Curvature heat map batched into 16 color groups (reduced from ~2000 individual draw calls)\\\n- `Math.max(...spread)` replaced with `reduce()` to avoid stack overflow on 2000+ elements\\\n- `TrackMappingAnalysis` defined as type alias for `TrackMapAnalysis` (no structural duplication)\\\n\\\n### v0.1.3 (2026-06-06) — Run Management & Feed Merging\\\n\\\n**New Features:**\\\n- Feed merging: toggle + checkbox selector when starting a run (sends `merge_feeds` to API)\\\n- Run deletion: delete button per run with confirmation dialog (`DELETE /api/sessions/{id}/runs/{run_id}`)\\\n- Session reopen: \\\"Reopen Session\\\" button on completed sessions (`POST /api/sessions/{id}/reopen`)\\\n- New API client functions: `deleteRun()`, `reopenSession()`\\\n- New store actions: `deleteRun()`, `reopenSession()`\\\n- `StartRunRequest` type extended with optional `merge_feeds: string[]`\\\n\\\n### v0.1.2 (2026-06-06) — Shifting Analysis Charts\\\n\\\n- Added RPM vs. Force scatter chart (x: RPM, y: acceleration_g)\\\n- Added Speed vs. RPM scatter chart (x: speed_kmh, y: rpm)\\\n- Renamed \\\"RPM vs. Acceleration (Tractive Force)\\\" → \\\"RPM vs. Force\\\"\\\n- Renamed \\\"Gear Acceleration Curves\\\" → \\\"Speed vs. Force\\\"\\\n- Chart order: RPM vs. Force → Speed vs. Force → Speed vs. RPM → Crossover Points\\\n\\\n### v0.1.1 (2026-06-05) — Dashboard Status Card Layout\\\n\\\n- Refactored System Status to 3 individual cards in a row (3-column grid)\\\n- Responsive: collapses to single column below 768px\\\n\\\n### v0.1.0 (2026-06-05) — Initial Release\\\n\\\n- Dashboard with live feeds and recording status\\\n- Session management (create, list, detail, delete)\\\n- Run management (start, stop)\\\n- Shift point analysis with charts\\\n- Track map visualization\\\n- API client with proxy to localhost:8080\\\n\\\n---\\\n\\\n## Cross-References\\\n\\\n- **Main project plan:** [Sim Racing Telemetry Analysis Platform — Project Plan](joplin://6c0dcb2a567348fd9796f50c790082e4)\\\n- **API reference:** [rusty-telemetry — REST API Reference](joplin://0c837f4e6b7e462a997cbc19e47c864a)\\\n- **Architecture note:** [Architecture & Infrastructure](joplin://c1c3a7b2055642268ab230b95551f470)\\\n\\\n---\\\n\\\n*Last updated: 2026-06-08 — v0.1.4: track mapping web client, persistent track model views, track API integration, Canvas 2D geometry rendering*\"]],\"start1\":0,\"start2\":0,\"length1\":0,\"length2\":12075}]"
metadata_diff: {"new":{"id":"50a09627d5d347009197b94bcee90411","parent_id":"0e8e00b432a840628faa4df5bc2068bc","latitude":"0.00000000","longitude":"0.00000000","altitude":"0.0000","author":"","source_url":"","is_todo":0,"todo_due":0,"todo_completed":0,"source":"joplin-desktop","source_application":"net.cozic.joplin-desktop","application_data":"","order":1780733439362,"markup_language":1,"is_shared":0,"share_id":"","conflict_original_id":"","master_key_id":"","user_data":"","deleted_time":0},"deleted":[]}
encryption_cipher_text: 
encryption_applied: 0
updated_time: 2026-06-08T14:13:34.102Z
created_time: 2026-06-08T14:13:34.102Z
type_: 13