Three issues fixed:
1. GPX parser used browser DOMParser which doesn't exist in Node/Vite SSR.
Added async parseGpxAsync() using linkedom for server-side parsing.
2. Server-side session initialization stored waypoints in a Yjs doc
instance separate from the Vite WebSocket plugin's doc store.
Moved waypoint initialization to client-side: API returns parsed
waypoints, client adds them to Yjs after sync.
3. GPX was encoded in URL params causing HTTP 431. Now the Journal
creates a Planner session via API (POST body), and only passes
compact waypoint coordinates in URL params.
Verified: Journal route → Edit in Planner → 4 waypoints loaded,
route computed (79.3km), elevation profile, Save to Journal ready.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Waypoint drag:
- Use observeDeep instead of observe to detect Y.Map property changes
Host election:
- Extracted to host-election.ts with 8 unit tests
- Simple rule: lowest real client ID is always host, deterministic
- Always sets role (host or participant), no missing roles
- Filters server docs (empty awareness state) from election
BRouter routing:
- Route segment-by-segment like bikerouter.de (one request per
waypoint pair), guarantees route passes through every waypoint
- Merge GeoJSON segments with accumulated stats
- 8 unit tests for segment merging and waypoint splitting
Awareness cleanup:
- Track client IDs per WebSocket connection
- Immediately remove awareness on WebSocket close
Debug panel:
- Visibility + expanded state persisted in localStorage
- Hotkey (Shift+Cmd+Option+T) toggles visibility
- Labels server docs as (server)
- All logic self-contained in YjsDebugPanel.tsx
User identity:
- Color and name persisted in localStorage across reloads
Tests: 32 total (16 existing + 8 host election + 8 routing)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add Vite plugin implementing y-protocols sync server for dev mode
- Fix useYjs hook: move provider creation into useEffect to survive
React StrictMode remounts
- Fix WebSocket URL: use /sync as base (y-websocket appends room name)
- Verified in browser: Connected status, Host badge, awareness working
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>