V1 Web PWA · Surface walkthrough

Nyx · Star Chart

A per-surface walkthrough of the V1 Web PWA customer surface: layout, states, interactions, data, and cross-references.

walked
8sections4 minread

On this page

Context. surface customer · domain nyx · route /domains/nyx/star-chart · auth signed-in · source apps/oshun/web/src/app/domains/nyx/star-chart/page.tsx

Last walked. 2026-05-29 automated runtime walk (Playwright) — hydration fix verified: 0 page/console errors, render+data OK; live SR/touch/offline/telemetry pending manual AT pass. Evidence: WALKTHROUGH/results/runtime-sweep-2026-05-29.md

Purpose#

A full-screen interactive horizon planisphere — rendered as a circular sky map with stars, constellation lines, and planets at the user's default observer location. Magnitude limit, time offset, and layer toggles let the user explore what's currently above the local horizon. Wired via apps/oshun/web/src/app/domains/nyx/star-chart/page.tsx ('use client') which mounts <NyxStarChart onClose={() => router.back()} />.

Entry points#

  • Direct URL / bookmark — yes (signed-in)
  • In-app navigation — verify whether the Nyx domain hub or EventCalendarOverlay link routes here (cross-route: focused sky chart links from NyxEventDetailWorkspace use event.skyChartHref)
  • Browser back affordance — the page mounts onClose={router.back()} so the in-component back button leaves this surface

Layout regions#

NyxStarChart (apps/oshun/web/src/components/domains/nyx/NyxStarChart.tsx) renders a top-to-bottom flow inside one container.

  • Header: back button (aria-label="Go back"), Lucide Star icon, <h1> Star Chart</h1>, right-aligned location chip (DEFAULT_OBSERVER.name from nyx-simulation-data)
  • Controls strip: magnitude limit range slider (Mag limit: <value>), time offset range slider (Time: Now / +Nh), four toggle buttons (Constellations, Planets, Grid, Labels)
  • Chart container: SVG planisphere with radial skyGrad background, horizon circle, optional altitude grid (30°/60°), azimuth radials, and generated stars / constellation lines / planets
  • Selected-body popup: appears when a star or planet is tapped — shows RA / Dec, Magnitude, Spectral Class (stars) or Magnitude / Distance / Elongation / Illumination (planets), with close button

States#

  • Loading — no async data; client component hydrates with generateStars / generateConstellations / generatePlanets synchronously
  • Populated (default) — magnitude limit 6.0, time offset 0, Constellations + Planets + Grid + Labels all on
  • Faint-stars hidden — slider at 0.5 limits visible stars to the brightest
  • Time-shifted — slider at +12h / −12h re-projects the sky for that offset
  • Below horizon — bodies with altitude ≤ 0 are filtered out; confirm chart is not empty for DEFAULT_OBSERVER
  • Star selected — tapping a star surfaces the popup; tapping the SVG background clears it (onClick={() => setSelected(null)})
  • Planet selected — same with planet-specific fields
  • Error (recoverable) — N/A — pure client computation; verify no throw paths in equatorialToHorizontal / computeGMST
  • Offline — view is static client code with no fetch; renders offline once cached by SW
  • Reduced motion — no animations declared; cursor is crosshair on the SVG

Interactions#

  • "Go back" button (icon button, ArrowLeft)
    • Function: calls onCloserouter.back()
    • Keyboard: Enter activates; focusable
    • Screen reader: announces "Go back"

Controls strip#

  • Magnitude limit slider (range input, min 0.5 max 6 step 0.5)
    • Function: filters stars by magnitude <= maxMagnitude
    • Screen reader: aria-label="Magnitude limit, currently <value>"
  • Time offset slider (range input, min −12 max 12 step 0.5)
    • Function: shifts computeGMST reference time; chart re-projects
    • Screen reader: aria-label="Time offset, <now|±Nh hours>"
  • Constellations toggle (button, Layers icon)
    • Function: toggles showConstellations; constellation lines on/off
  • Planets toggle (button, Eye/EyeOff icon)
    • Function: toggles showPlanets; planet markers on/off
  • Grid toggle (button)
    • Function: toggles showGrid; altitude rings + azimuth radials on/off
  • Labels toggle (button)
    • Function: toggles showLabels; star names / planet names on/off

Chart#

  • SVG sky (cursor: 'crosshair')
    • Function: tap empty area clears selected body
    • Touch target: full chart area
  • Star marker tap — sets selected to the star; popup appears with its data
  • Planet marker tap — sets selected to the planet; popup appears

Selected-body popup#

  • Close button (icon button at top of popup)
    • Function: clears selected
  • RA / Dec row — formatted via formatRA / formatDec
  • Magnitude row
  • Spectral Class row (stars only) — color-coded via SPECTRAL_COLORS
  • Distance row (stars: parsec; planets: au)
  • Temperature row (stars only)
  • Constellation row (stars only)
  • Elongation row (planets only)
  • Illumination row (planets only)

Data & contracts#

  • Reads: none from network — generateStars, generateConstellations, generatePlanets, DEFAULT_OBSERVER from apps/oshun/web/src/lib/nyx/nyx-simulation-data; types from apps/oshun/web/src/lib/nyx/nyx-types (StarData, PlanetData)
  • Writes: none
  • Realtime: none
  • Caching: static client bundle; SW caches the JS and renders offline
  • Auth/role check: middleware enforces signed-in for /domains/*

Cross-references#

Open questions / known gaps#

  • Runtime walk (2026-05-29) — defect FIXED & verified: React 418 hydration mismatch resolved (time-derived values now deferred to a post-mount effect via useClientNow/useIsMounted from @/hooks/useClientTime; for /coordinates the missed RiseSetCalculator site was gated). Re-walked against a fresh build: 0 page errors, 0 console errors, HTTP 200. React error #418; visit https://react.dev/errors/418?a. React error 418 is a hydration mismatch — server-rendered HTML differs from the client (this view renders time/position-dependent content, e.g. new Date()/toLocale*, without suppressHydrationWarning). Fix: compute time-dependent values in an effect or gate with suppressHydrationWarning. Status kept drafted until fixed.
  • Confirm whether the magnitude/time sliders expose discrete step keyboard arrows correctly (default browser range behavior)
  • Document where DEFAULT_OBSERVER is overridden for per-user lat/lng — page never passes a custom observer prop
  • Confirm whether the chart supports pinch-zoom on touch or only the decorative crosshair cursor
  • Verify whether the event.skyChartHref from event detail pages targets /domains/nyx/star-chart with query params, and how those params are consumed (the component takes no props beyond onClose)