V1 Web PWA · Surface walkthrough

Nyx · Celestial Events Calendar

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

walked
8sections5 minread

On this page

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

Last walked. 2026-05-29 automated runtime walk (Playwright headless) — render, /v1 data (2xx), console/page-errors, expected content, screenshot verified; live screen-reader, touch, offline, and telemetry-delivery checks pending a manual AT pass. Evidence: WALKTHROUGH/results/runtime-sweep-2026-05-29.md; content re-verified 2026-06-03 against current source

Purpose#

Canonical Nyx event calendar with cross-domain bridges — lists upcoming sky events with importance filtering, Nisaba/Veritas/Tara/Concept-graph focus panels for the lead event, and per-event save / follow / reminder / ICS calendar-export actions. Page is a 'use client' shell (apps/oshun/web/src/app/domains/nyx/events/page.tsx) that mounts <EventCalendarOverlay onClose={() => router.replace('/domains/nyx?origin=explore')} origin="explore" stack={[]} /> under an sr-only <h1>Celestial Events · Nyx · OSHUN</h1>. The hydrated shell variant at /domains/nyx?origin=...&path=%2Fevents closes by removing only the path query while preserving origin and stack.

Entry points#

  • Direct URL / bookmark — yes (signed-in)
  • In-app navigation — verify the entry points (Nyx hub link, cross-domain explore rail)
  • Event detail back-linkNyxEventDetailWorkspace shows "Return to Nyx events" → /domains/nyx?origin=explore&path=%2Fevents (so a detour through the domain root, not the /domains/nyx/events URL)
  • Close affordance — direct /domains/nyx/events arrivals replace to the canonical /domains/nyx?origin=explore shell; hydrated-shell arrivals remove only path and preserve the incoming shell context

Layout regions#

EventCalendarOverlay (apps/oshun/web/src/components/domains/nyx/EventCalendarOverlay.tsx) renders inside an OverlaySheet with variant="fullscreen", title="Celestial Calendar", description="Upcoming astronomical events and viewing windows."

  • sr-only heading: page-level <h1>Celestial Events · Nyx · OSHUN</h1>
  • OverlaySheet header: title + description + close button (sheet affordance)
  • Timezone hint: Times shown in: <Intl timezone>
  • Importance filter radio group: chips All | Major | Notable | Minor (role="radiogroup", aria-label="Filter by importance")
  • Count line: <N> event(s)
  • SkyTextJourneyRail — cross-domain breadcrumb rail wired to the active stage open-cosmology-and-texts ("Open cosmology and texts"). The rail renders the four canonical stages in order: "Hold the sky moment", "Frame the observation", "Open cosmology and texts", and "Preserve the sky thread"; the active stage's primary action is "Open cosmology passage".
  • Nisaba overlay focus panel ("Nisaba overlay focus", data-testid="nyx-calendar-nisaba-focus", ~190; if focusEvent.nisabaOverlays.length > 0)
  • Veritas evidence focus panel ("Veritas evidence focus", data-testid="nyx-calendar-veritas-focus", ~252; if focusEvent.veritasExplainers.length > 0)
  • Tara perspective focus panel ("Tara perspective focus", data-testid="nyx-calendar-tara-focus", ~310; if focusEvent.taraRecommendations.length > 0)
  • Concept-graph focus panel ("Concept graph focus", data-testid="nyx-calendar-concept-graph-focus", ~372; if focusEvent.conceptGraphThreads.length > 0)
  • Event rows: each row shows time, importance pill, event-type chip, window/countdown/visibility line, summary, badges, action buttons, and a "View event page" link

States#

  • Loading — bundled model from listNyxEventPageModels(); no fetch
  • Populated (All filter) — every model returned by listNyxEventPageModels({ origin, stack })
  • Filtered (Major / Notable / Minor) — filter narrows the list
  • Empty (filter excludes all) — count reads 0 events; verify empty state copy
  • Focus event with Nisaba overlays — Nisaba focus panel renders
  • Focus event with Veritas explainers — Veritas focus panel renders
  • Focus event with Tara recommendations — Tara perspective focus panel renders
  • Focus event with concept-graph threads — Concept-graph focus panel renders
  • Focus event with none of the above — each of the four focus panels is independently omitted when its array is empty
  • Reminder enabledNyxEventReminderControls (compact) expands under the action row
  • Offline — static client computation; reminder/save state lives in useNyxEventActionState (verify persistence)
  • Standalone PWA — overlay sheet covers full viewport

Interactions#

OverlaySheet chrome#

  • Close button (sheet-level) — nyx-full-journey verifies direct-route close lands on /domains/nyx?origin=explore and hydrated-shell close removes path while preserving origin=home&stack=tara

Importance filter#

  • "All" radio chip (role="radio", aria-checked)
    • Function: setFilter('All')
  • "Major" radio chip — colored with var(--l-alert)
  • "Notable" radio chip — colored with var(--l-warn)
  • "Minor" radio chip — colored with L.muted

Focus panels#

  • SkyTextJourneyRail — cross-domain rail with stage labels from OSHUN_SKY_TEXT_STAGE_MODEL: "Hold the sky moment", "Frame the observation", "Open cosmology and texts", and "Preserve the sky thread". Step action targets are produced by apps/oshun/web/src/navigation/sky-text-journey.ts: sky-nyx-nightly-highlights → Nyx nightly highlights, sky-nyx-event-calendar → Nyx events, sky-nyx-sky-map → Nyx sky map, sky-nyx-observation-log → Nyx observations, sky-nisaba-cosmology-passage → Nisaba cosmology passage, sky-nisaba-calendar-source → Nisaba calendar source, sky-nisaba-compare-texts → Nisaba compare, sky-nisaba-notebook → Nisaba notebook, and sky-library-save → Library notebook filter.
  • NisabaFocusLink (each overlay) — link to Nisaba surface
  • VeritasFocusLink (each explainer) — link to a Veritas explainer
  • TaraFocusLink (each taraRecommendations entry) — link into a Tara practice
  • ConceptGraphFocusThread (each conceptGraphThreads entry) — link into the cross-domain concept graph

Per-event row (each event in filteredEvents)#

  • Importance pill — read-only badge with event.importanceLabel
  • Event-type chip — read-only with event.eventTypeLabel
  • Summary lineevent.windowLabel · event.countdownLabel · event.visibilityLabel
  • Save toggle (button, data-testid="nyx-event-save-toggle-<eventId>")
    • Function: toggleSaved — flips saved state
    • Label: "Save" → "Saved"
  • Follow toggle (button, data-testid="nyx-event-follow-toggle-<eventId>")
    • Label: "Follow" → "Following"
  • Reminder toggle (button, data-testid="nyx-event-reminder-toggle-<eventId>")
    • Label: "Remind" → "Reminder on"
  • "Add to Calendar (.ics)" button
    • Function: downloadNyxEventICalFile(event.eventId) — triggers ICS file download
    • Screen reader: aria-label='Add "<event.title>" to calendar'
  • "View event page" link (data-testid="nyx-event-open-<eventId>")
    • Function: navigates to event.domainHref (/domains/nyx/events/<id>)
  • Reminder cadence + channels controls (when reminder enabled)
    • Compact NyxEventReminderControls with setReminderCadence and setReminderChannels

Data & contracts#

  • Reads: listNyxEventPageModels({ origin, stack }) from nyxEventPageModels — bundled event index
  • Writes: ICS file download via downloadNyxEventICalFile(eventId); save / follow / reminder state via useNyxEventActionState (verify persistence: localStorage vs server)
  • Realtime: none
  • Caching: client bundle, static
  • Auth/role check: middleware enforces signed-in for /domains/*

Cross-references#

  • Detail view: domains-nyx-events-eventId.md
  • Domain hub: nyx.md
  • Component source: apps/oshun/web/src/components/domains/nyx/EventCalendarOverlay.tsx
  • Model source: apps/oshun/web/src/components/domains/nyx/nyxEventPageModels.ts
  • Action hook: apps/oshun/web/src/components/domains/nyx/useNyxEventActionState.ts

Open questions / known gaps#

  • Confirm whether save / follow / reminder state persists across reloads (useNyxEventActionState storage strategy)
  • Document the cross-domain bridge — domainHref from the rail (the event domain target) versus /domains/nyx/events/[eventId] page
  • Confirm whether OverlaySheet close emits a route navigation or just hides the sheet — fixed and verified 2026-06-24. Direct route close replaces to /domains/nyx?origin=explore; hydrated shell close removes only path and keeps the rest of the shell context.
  • Verify whether unfiltered list contains a virtualized scroll for large catalogs