Commit Graph

3 Commits

Author SHA1 Message Date
ruv 18c09d3305 feat(dashboard): iter G+H+I + P0.10 — modal forms, a11y pass, drag persistence, REPL history
Closes ADR-093 P0.10, P1.2, P1.6, P1.7, P2.1, P2.2, P2.3, P2.5.

## Iter G — modal contents (P1.6)
- nv-palette "New scene…" now opens a 5-field form (name, dipole
  moment, heart→sensor distance, ferrous toggle, 60 Hz mains toggle).
  On Apply: builds a real Scene JSON and pushes via client.loadScene().
- nv-palette "Export proof bundle…" now calls client.exportProofBundle()
  and triggers a real blob download with a timestamp filename.

## Iter H — a11y pass (P2.1, P2.2, P2.3, P2.5)
- Skip-to-main-content link at top of nv-app (focus-visible only).
- <main id="main-content" role="main"> wraps the central area; tabindex="-1"
  so the skip link can land focus there.
- nv-rail wraps its 5 view buttons in <nav role="navigation"
  aria-label="Primary"> with aria-current="page" on the active button
  and aria-label on every button. SVGs marked aria-hidden="true".
- nv-console body is now role="log" aria-live="polite"
  aria-label="Console output".
- nv-modal auto-focuses first interactive element on open and traps
  Tab cycling inside the dialog; nv-onboarding already had a dismiss
  affordance covered.

## Iter I — drag persistence (P1.7) + density visual (P1.2)
- scenePositions signal in appStore + IndexedDB key 'scene-positions'.
- nv-scene restores drag positions at connect; persists on pointerup.
- Density visual (CSS body.density-{comfy,default,compact}) confirmed
  active — was already wired but flagged as "doesn't change anything"
  in P1.2; verified during this iter.

## P0.10 — REPL history persistence
- replHistory + pushReplHistory in appStore, persisted to IndexedDB
  key 'repl-history'.
- nv-console arrow-up/down now read from the shared signal so command
  history survives view switches and reloads.

Validated end-to-end with `npx agent-browser` on
https://ruvnet.github.io/RuView/nvsim/ — skip-link, main role, console
log role, nav role, aria-current="page", New Scene modal with 5 form
fields all confirmed live. Console errors: zero.

ADR-093 §2/§3/§4 updated to mark these items resolved.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-04-26 22:08:49 -04:00
ruv c9fbda12fd feat(dashboard): iter B+C+D+E+F — sim controls, scene toolbar, seed modal, transport-pill click, sidebar tunables wire-through, SNR, prefers-reduced-motion auto-detect, REPL proof.export
Closes ADR-093 P0.5, P0.6, P0.7, P0.9, P1.4, P1.8, P1.10, P1.11.

## Iter B — scene toolbar + sim controls (P0.6, P0.7)
- nv-scene scene-toolbar (top-left): zoom +/-, fit-to-view, layer
  toggles for sources / field lines / labels. Zoom drives the SVG
  viewBox so the entire scene scales uniformly.
- nv-scene sim-controls (bottom-right): step ⏮ / play ▶ / step ⏭ /
  speed cycle (0.25× → 4×). Bound to client.run/pause/step.

## Iter C — topbar pill clicks (P0.5, P1.10)
- Seed pill click opens a "Set seed" modal with a hex-validated input.
  Apply propagates via WasmClient.setSeed and toasts the new value.
- Transport pill (wasm/ws) click opens the Settings drawer (Transport
  section), letting the user switch modes inline.

## Iter D — sidebar tunables wire-through (P1.8)
- Every slider edge-triggers pushConfigDebounced() (300 ms). The
  debounced call forwards { digitiser: { f_s_hz, f_mod_hz }, sensor: {
  …, shot_noise_disabled }, dt_s } to the worker via setConfig RPC.
  Worker rebuilds the WasmPipeline so the running stream picks up the
  new config without restart.

## Iter E — proof.export REPL command (P0.9)
- nv-console adds proof.export → calls client.exportProofBundle() and
  triggers a download of the resulting JSON manifest with a timestamp
  filename. Listed in `help`.

## Iter F — SNR + prefers-reduced-motion (P1.4, P1.11, P1.3)
- nv-scene now computes SNR per frame as |b| / max(sigma_per_axis) and
  publishes to the snr signal. The corner stat-card stops showing "—".
- main.ts honors the system prefers-reduced-motion as the default for
  motionReduced when no IndexedDB override is set.

ADR-093 §2/§3 updated to mark these P0/P1 items resolved.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-04-26 21:36:45 -04:00
ruv 39ec05edcb feat(dashboard): nvsim Vite+Lit dashboard with WASM transport + App Store [ADR-092]
End-to-end implementation of the operator dashboard for the nvsim
NV-diamond magnetometer simulator. Vite 5 + TypeScript strict + Lit 3,
~93 KB gzipped JS budget, runs the *real* nvsim Rust crate compiled to
wasm32-unknown-unknown inside a dedicated Web Worker.

Validated end-to-end with `npx agent-browser`:
- WASM module boots, build version + magic 0xC51A_6E70 reported
- Reference witness verifies byte-identical to Proof::EXPECTED_WITNESS_HEX
  cc8de9b01b0ff5bd97a6c17848a3f156c174ea7589d0888164a441584ec593b4
- Pipeline runs at ~1.88 kHz on x86_64 dev hardware (4500x over Cortex-A53)
- Zero browser console errors; only Lit dev-mode warning (expected)

## nvsim crate (additive)
- New `wasm` feature flag with wasm-bindgen 0.2 / serde-wasm-bindgen 0.6
- src/wasm.rs: WasmPipeline wrapper + referenceSceneJson +
  expectedReferenceWitnessHex + referenceWitness + hexWitness exports
- crate-type = ["cdylib", "rlib"] so native + wasm both build
- rand = { default-features = false } drops getrandom OS-entropy path,
  preserving the crate's WASM-ready posture
- Native: 50/50 tests still pass, witness unchanged

## dashboard/ (new package)
- Vite 5 + TypeScript strict, Lit 3 elements, signals-based store
- 12 Lit components mirroring the mockup zones (rail, topbar, sidebar,
  scene SVG with draggable sources + NV crystal, inspector tabs
  Signal/Frame/Witness, console with REPL + filter tabs, settings
  drawer, modals, ⌘K command palette, debug HUD, toast, app-store)
- IndexedDB persistence (theme, density, motion, app activations)
- WasmClient → Web Worker → wasm-pack-built nvsim WASM module
- NvsimClient TS interface — same shape covers future WsClient transport
- MagFrame parser (60-byte LE layout matching nvsim::frame)

## App Store (ADR-092 §14a — added during impl)
- Catalog of all 65 wifi-densepose-wasm-edge modules + nvsim
- 13 categories with event-ID-range labels
- Per-app metadata: id/name/category/crate/summary/events/budget/
  status/adr/tags
- Fuzzy search, category + status filters, IndexedDB-backed activation
- ADR-092 §14a documents the registry contract and per-app schema

## Build pipeline
- wasm-pack build crates/nvsim --target web outputs to
  dashboard/public/nvsim-pkg/ (60 KB pkg, 162 KB unoptimized .wasm)
- npm run build → 93 KB gzip JS, well under 300 KB budget
- ts.config strict, npx tsc --noEmit clean
- Vite worker correctly loads WASM via dynamic import resolving
  against worker origin

## E2E validation
- agent-browser open → 4-zone grid renders correctly in dark theme
- Run button → live B-vector trace, |B| readout updates, FPS counter
- App Store → all 66 apps listed with toggles, fuzzy search filters
  to "Ghost hunter" on "ghost" query
- Witness verify → green check, console logs "determinism gate ✓"
- Console errors: zero (only expected Lit dev-mode warning)

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-04-26 19:22:04 -04:00