Browsers hash deviceId per frame/origin, so the ids the host gets from its own enumerateDevices() are unknown inside the call iframe. mediaDevices.audioOutput.select() then falls back to the default output, so picking a headset in Lotus's call bar did nothing.
Change:io.lotus.audio_output_state now also carries available: [{ id, label }]. These come from the frame's own audioOutput.available$, so the ids are valid in the frame. It is re-sent whenever the list or the selection changes. The host builds its menu from this list. Hosts that ignore the field are unaffected.
Verified with a local build in Lotus (Chromium fake devices):
The menu lists the frame's outputs.
Picking "Fake Audio Output 2" saves a real frame device in matrix-setting-audio-output.
The host's ✓ follows the selection.
No more "host did not ack io.lotus.audio_output_state" warnings once the host handles the action.
Fork half of cinny #310.
Browsers hash `deviceId` per frame/origin, so the ids the host gets from its own `enumerateDevices()` are unknown inside the call iframe. `mediaDevices.audioOutput.select()` then falls back to the default output, so picking a headset in Lotus's call bar did nothing.
**Change:** `io.lotus.audio_output_state` now also carries `available: [{ id, label }]`. These come from the frame's own `audioOutput.available$`, so the ids are valid in the frame. It is re-sent whenever the list or the selection changes. The host builds its menu from this list. Hosts that ignore the field are unaffected.
**Verified** with a local build in Lotus (Chromium fake devices):
- The menu lists the frame's outputs.
- Picking "Fake Audio Output 2" saves a real frame device in `matrix-setting-audio-output`.
- The host's ✓ follows the selection.
- No more "host did not ack io.lotus.audio_output_state" warnings once the host handles the action.
Unit test for the labels · `tsc` · oxlint clean.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
Browsers hash deviceIds per frame/origin, so ids from the host's own
enumerateDevices() are unknown in here and select() fell back to the
default. io.lotus.audio_output_state now also carries
available: [{id,label}] (frame-valid ids), so the host's call-bar menu
can offer real choices.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
jared
merged commit e3ac9a96d6 into lotus2026-10-17 20:45:10 -04:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Fork half of cinny #310.
Browsers hash
deviceIdper frame/origin, so the ids the host gets from its ownenumerateDevices()are unknown inside the call iframe.mediaDevices.audioOutput.select()then falls back to the default output, so picking a headset in Lotus's call bar did nothing.Change:
io.lotus.audio_output_statenow also carriesavailable: [{ id, label }]. These come from the frame's ownaudioOutput.available$, so the ids are valid in the frame. It is re-sent whenever the list or the selection changes. The host builds its menu from this list. Hosts that ignore the field are unaffected.Verified with a local build in Lotus (Chromium fake devices):
matrix-setting-audio-output.Unit test for the labels ·
tsc· oxlint clean.🤖 Generated with Claude Code
https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
Browsers hash deviceIds per frame/origin, so ids from the host's own enumerateDevices() are unknown in here and select() fell back to the default. io.lotus.audio_output_state now also carries available: [{id,label}] (frame-valid ids), so the host's call-bar menu can offer real choices. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5