Files
ruvnet--RuView/ui
Dragan Spiridonov 9b9754778f fix(ui): service worker cached /oauth/status, freezing browser sign-in
Browser sign-in worked, but the settings panel kept offering "Sign in with
Cognitum" afterwards; only a hard reload showed the true state. The server was
correct throughout.

Root cause: `ui/sw.js` routed cache-first as its CATCH-ALL for every path
outside `/api/` and `/health/`. `/oauth/status` therefore had its first
(signed-out) response stored in the Cache API and replayed to the page forever.
The Cache API is not the HTTP cache and ignores `Cache-Control` entirely, so the
`no-store, no-cache, must-revalidate` the server already sends could not prevent
it. A hard reload bypasses the service worker, which is why that alone appeared
to fix it, and why signing out re-poisoned the entry.

Fixes, in order of blast radius:

- `/oauth/` is never handled by the worker. Not network-first — that still
  writes a copy, which would be replayed the moment the server is briefly
  unreachable, silently reinstating a stale sign-in state.
- Cache-first is now an ALLOWLIST (navigations and static asset extensions)
  rather than the catch-all. This is the underlying defect: any endpoint added
  outside `/api/` was frozen on its first response. Unrecognised paths now go
  to the network untouched.
- `CACHE_NAME` bumped to `ruview-v2`, so `activate` evicts the poisoned
  `ruview-v1` from browsers that already ran the old worker. Verified: the
  cache list went from `[ruview-v1]` to `[ruview-v2]` on update.
- Shell lookups use `ignoreSearch` and store a search-less key, so the
  `?signed_in=<ms>` the callback redirects to does not mint a fresh, never-hit
  cache entry per sign-in.

Also: re-check sign-in state on `pageshow` (bfcache restore, where no script
re-runs) and on `visibilitychange` (signed in or out in another tab). Opening
the panel alone is not sufficient — it may already be open.

Verification, in a real browser driven end-to-end:
- Reproduced with a valid session cookie: server returned `signed_in: true` to
  curl while the page's own fetch got a cached `signed_in: false`, and the
  request never appeared in the server log at all.
- Confirmed the cached entry existed: `caches.open('ruview-v1').match(
  '/oauth/status')` returned the signed-out body.
- After the fix, on a NORMAL (not hard) reload the panel reads
  "Signed in as <account> - sensing:read" with Sign out shown.

Tests: `ui/sw.test.mjs` loads the real `sw.js` with stubbed worker globals and
asserts the routing decision per path. 4 of its 10 tests fail against the
pre-fix worker and pass after; the other 6 pin pre-existing guards (non-GET,
websocket upgrade, cross-origin, API paths, static assets, navigation) and pass
in both, so they track behaviour rather than the rewrite.

CI: adds a `ui-tests` job. Nothing ran the UI JavaScript before, so both this
suite and the ADR-272 ws-ticket suite would have rotted unexecuted — which is
the same blind spot that let this defect ship.

Removes the temporary `/oauth/status` cookie logging and the panel console
diagnostic added while tracking this down.

Co-Authored-By: Ruflo & AQE
2026-07-23 11:20:13 +02:00
..

WiFi DensePose UI

A modular, modern web interface for the WiFi DensePose human tracking system. Provides real-time monitoring, WiFi sensing visualization, and pose estimation from CSI (Channel State Information).

Architecture

The UI follows a modular architecture with clear separation of concerns:

ui/
├── app.js                    # Main application entry point
├── index.html                # HTML shell with tab structure
├── style.css                 # Complete CSS design system
├── config/
│   └── api.config.js         # API endpoints and configuration
├── services/
│   ├── api.service.js        # HTTP API client
│   ├── websocket.service.js  # WebSocket connection manager
│   ├── websocket-client.js   # Low-level WebSocket client
│   ├── pose.service.js       # Pose estimation API wrapper
│   ├── sensing.service.js    # WiFi sensing data service (live + simulation fallback)
│   ├── health.service.js     # Health monitoring API wrapper
│   ├── stream.service.js     # Streaming API wrapper
│   └── data-processor.js     # Signal data processing utilities
├── components/
│   ├── TabManager.js         # Tab navigation component
│   ├── DashboardTab.js       # Dashboard with live system metrics
│   ├── SensingTab.js         # WiFi sensing visualization (3D signal field, metrics)
│   ├── LiveDemoTab.js        # Live pose detection with setup guide
│   ├── HardwareTab.js        # Hardware configuration
│   ├── SettingsPanel.js      # Settings panel
│   ├── PoseDetectionCanvas.js # Canvas-based pose skeleton renderer
│   ├── gaussian-splats.js    # 3D Gaussian splat signal field renderer (Three.js)
│   ├── body-model.js         # 3D body model
│   ├── scene.js              # Three.js scene management
│   ├── signal-viz.js         # Signal visualization utilities
│   ├── environment.js        # Environment/room visualization
│   └── dashboard-hud.js      # Dashboard heads-up display
├── utils/
│   ├── backend-detector.js   # Auto-detect backend availability
│   ├── mock-server.js        # Mock server for testing
│   └── pose-renderer.js      # Pose rendering utilities
└── tests/
    ├── test-runner.html       # Test runner UI
    ├── test-runner.js         # Test framework and cases
    └── integration-test.html  # Integration testing page

Features

WiFi Sensing Tab

  • 3D Gaussian-splat signal field visualization (Three.js)
  • Real-time RSSI, variance, motion band, breathing band metrics
  • Presence/motion classification with confidence scores
  • Data source banner: green "LIVE - ESP32", yellow "RECONNECTING...", or red "SIMULATED DATA"
  • Sparkline RSSI history graph
  • "About This Data" card explaining CSI capabilities per sensor count

Live Demo Tab

  • WebSocket-based real-time pose skeleton rendering
  • Estimation Mode badge: green "Signal-Derived" or blue "Model Inference"
  • Setup Guide panel showing what each ESP32 count provides:
    • 1 ESP32: presence, breathing, gross motion
    • 2-3 ESP32s: body localization, motion direction
    • 4+ ESP32s + trained model: individual limb tracking, full pose
  • Debug mode with log export
  • Zone selection and force-reconnect controls
  • Performance metrics sidebar (frames, uptime, errors)

Dashboard

  • Live system health monitoring
  • Real-time pose detection statistics
  • Zone occupancy tracking
  • System metrics (CPU, memory, disk)
  • API status indicators

Hardware Configuration

  • Interactive antenna array visualization
  • Real-time CSI data display
  • Configuration panels
  • Hardware status monitoring

Data Sources

The sensing service (sensing.service.js) supports three connection states:

State Banner Color Description
LIVE - ESP32 Green Connected to the Rust sensing server receiving real CSI data
RECONNECTING Yellow (pulsing) WebSocket disconnected, retrying (up to 20 attempts)
SIMULATED DATA Red Fallback to client-side simulation after 5+ failed reconnects

Simulated frames include a _simulated: true marker so code can detect synthetic data.

Backends

Rust Sensing Server (primary)

The Rust-based wifi-densepose-sensing-server serves the UI and provides:

  • GET /health — server health
  • GET /api/v1/sensing/latest — latest sensing features
  • GET /api/v1/vital-signs — vital sign estimates (HR/RR)
  • GET /api/v1/model/info — RVF model container info
  • WS /ws/sensing — real-time sensing data stream
  • WS /api/v1/stream/pose — real-time pose keypoint stream

Python FastAPI (legacy)

The original Python backend on port 8000 is still supported. The UI auto-detects which backend is available via backend-detector.js.

Quick Start

cd docker/

# Default: auto-detects ESP32 on UDP 5005, falls back to simulation
docker-compose up

# Force real ESP32 data
CSI_SOURCE=esp32 docker-compose up

# Force simulation (no hardware needed)
CSI_SOURCE=simulated docker-compose up

Open http://localhost:3000/ui/index.html

With local Rust binary

cd v2
cargo build -p wifi-densepose-sensing-server --no-default-features

# Run with simulated data
../../target/debug/sensing-server --source simulated --tick-ms 100 --ui-path ../../ui --http-port 3000

# Run with real ESP32
../../target/debug/sensing-server --source esp32 --tick-ms 100 --ui-path ../../ui --http-port 3000

Open http://localhost:3000/ui/index.html

With Python HTTP server (legacy)

# Start FastAPI backend on port 8000
wifi-densepose start

# Serve the UI on port 3000
cd ui/
python -m http.server 3000

Open http://localhost:3000

Pose Estimation Modes

Mode Badge Requirements Accuracy
Signal-Derived Green 1+ ESP32, no model needed Presence, breathing, gross motion
Model Inference Blue 4+ ESP32s + trained .rvf model Full 17-keypoint COCO pose

To use model inference, start the server with a trained model:

sensing-server --source esp32 --model path/to/model.rvf --ui-path ./ui

Configuration

API Configuration

Edit config/api.config.js:

export const API_CONFIG = {
  BASE_URL: window.location.origin,
  API_VERSION: '/api/v1',
  WS_CONFIG: {
    RECONNECT_DELAY: 5000,
    MAX_RECONNECT_ATTEMPTS: 20,
    PING_INTERVAL: 30000
  }
};

Testing

Open tests/test-runner.html to run the test suite:

cd ui/
python -m http.server 3000
# Open http://localhost:3000/tests/test-runner.html

Test categories: API configuration, API service, WebSocket, pose service, health service, UI components, integration.

Styling

Uses a CSS design system with custom properties, dark/light mode, responsive layout, and component-based styling. Key variables in :root of style.css.

License

Part of the WiFi-DensePose system. See the main project LICENSE file.