Files
amethyst/commons
Claude 5cb2351cdb feat(audio-rooms): RoomMember + ParticipantGrid pure projection (T2 #1)
Data model + projection function for the participant grid:

  RoomMember — one row in the grid. Combines the static `p`-tag
  role (kind-30312) with the dynamic presence flags (kind-10312
  aggregator). Carries an `absent` Boolean for "member never
  joined" — nostrnests greys them out and we keep parity by
  surfacing the flag for the UI to decide.

  ParticipantGrid — onStage / audience split.

  buildParticipantGrid(participants, presences) — pure projection.
  Stage placement is `canSpeak() && onstage != false`, so a speaker
  who explicitly emits `onstage=0` (Tier 1 Step 1's "step off the
  stage" tag) drops to audience without losing their speaker role.
  Pure-audience members (present in the ledger but not p-tagged)
  show up in audience with `role = null`.

Tests:
  * Host + speaker on stage with matching presence
  * Speaker with onstage=0 drops to audience
  * Pure listener (no p-tag) lands in audience with role=null
  * P-tagged speaker without presence is on stage with absent=true
  * Empty inputs produce an empty grid (no crash)

The actual `LazyVerticalGrid` rendering is a follow-up — current
`StagePeopleRow`s already cover the on-stage/audience split
visually; the data model is the substantive piece. Wiring the
VM's `participantGrid: StateFlow<ParticipantGrid>` and switching
the room screen to a grid layout can ship later without changing
the wire contract or tests.
2026-04-26 22:52:38 +00:00
..