The native GStreamer screen share path is gone: in a peer-to-peer mesh every
hardware and OS combination behaved differently, and keeping them in step cost
more than it saved. Screen sharing uses Chromium's own capture everywhere.
- Screen share bitrate is a setting: a stepper from 300 to 10,000 Kbps or
unlimited, with press-and-hold repeat and typed entry. Changes apply live,
including to a share going through the voice relay (debounced, so a hold
becomes one update). The default stays 1080p at 30 fps with an 8,000 Kbps cap.
- Black screen share tiles on GPU encoders: the sharer asks for a fresh
keyframe after negotiation, and the watchdog only re-adopts tracks a tile is
not already showing.
- Relay screen publishing is serialized and tied to the share it was started
for, so a stop, a new share or a relay restart can never publish a stale
producer, and a failed first publish retries on its own.
The per-user menu only renders on other people's rows. It was already hidden
until hover, but with opacity, which hides a thing without freeing its space:
it stayed in the flex flow with margin-left:auto and its own padding, so every
other user's stream badge and status icons stopped a button-width short of
where your own row's ended and the column never lined up.
It is positioned absolutely now, with the same slot reserved on every row, so
rows with and without a button share the same geometry. Measured with a real
stylesheet: the badge and icon edges were 31px apart before and are identical
after. pointer-events keeps the invisible button from swallowing clicks in
that corner, and it still reveals on hover or keyboard focus.
Also removes a second, older .voice-user-menu-btn block further down the file
that this was silently fighting with.
.voice-user-item was flex-wrap: wrap, and a wrapping flex line pushes whole
items to the next row rather than shrinking them. So the moment a user
picked up a LIVE, camera or viewer badge, the status icon group wrapped
onto a row of its own -- and since it carries margin-left:auto it sat at
the far right of that row, reading as an empty second line.
nowrap plus min-width:0 keeps the row on one line; the name already
ellipsises, so a long name truncates instead of wrapping. The stream badge
gets flex-shrink:0 so it is not squashed on the way.
- Mute/deafen state broadcast across voice channels
- Deafen-implies-mute with state memory
- Graceful SIGTERM/SIGINT shutdown
- Inline voice indicators with mute/deafen status
- Reconnect state preservation
Refactor: Moved voice styles to dedicated voice.css, since style.css is unmanageably large. Imported cleanly in style.css.
Co-authored by: ancsemi <ancsemi@users.noreply.github.com>