Commit Graph
2 Commits
Author SHA1 Message Date
Lotus CIandClaude Opus 5 ea579cb998 fix(lotus): screenshare-audio mute survives a re-share — mute via the renderer, not setVolume
"Mute Screenshare Audio" (io.lotus.set_deafen screenshareAudioMuted) used
RemoteParticipant.setVolume(0, ScreenShareAudio). EC's own createVolumeControls
writes volume 1 through the same setter the moment a new screenshare media item
resolves, so when the sharer stopped and re-shared (or a late joiner shared)
the audio came back at full volume while the host button still said
"Unmute Screenshare Audio". Reproduced on the local calls stack with two
headless clients: after a re-share the screen_share_audio element read vol=1.

Now the flag is a global behavior (muteScreenshareAudio$) that
LivekitRoomAudioRenderer turns into the `muted` prop of every
Track.Source.ScreenShareAudio element — the exact mechanism deafen already uses
(pub.setEnabled(false): the server stops sending). Verified via the
RemoteTrackPublication behind each <audio>: the re-published track (new sid)
mounts with enabled=false while muted and re-enables on unmute; deafen +
undeafen leaves it muted; teardown resets the flag so the next call starts
clean. Unit tests updated; renderer test asserts only ScreenShareAudio
elements get muted by the new prop.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-18 23:54:25 -04:00
Lotus CIandClaude Opus 5 f1cfcc7377 fix(lotus): implement deafen via EC's global output mute, not setVolume
Deafen wrote RemoteParticipant.setVolume(0, Microphone), which EC's own
createVolumeControls overwrote with the tile volume every time sink$
re-emitted (every join / re-resolution) — so anyone joining while you were
deafened was audible — and undeafen blanket-wrote 1 to every participant,
clobbering per-tile volume/mute. The ParticipantConnected listeners also
hung off livekitRoomItems$, which is empty while alone, so the first joiner
could be heard briefly.

Deafen now drives setAudioEnabled$ -> muteAllAudio$, the `muted` prop
InCallView already passes to every audio renderer, which also silences
Track.Source.Unknown soundboard audio (the known P6-2 gap). Undeafen
restores the user's own output state and never touches the mute-all
setting. Re-applying the same state is a no-op so resendForkState() is
safe. Screenshare-audio-only mute keeps setVolume(ScreenShareAudio) but
only restores participants it muted itself. Rooms come from
allConnections$ like the sibling modules. Unit-tested.

Fixes #1

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-12 11:34:08 -04:00