fix(call): one-row call bar on phones; screenshare-audio mute only when useful (#265, #277)

#265: in the compact layout (phones, narrow windows) the control bar
stacked into four rows (~330px, 40% of a portrait phone). It is now one
row: mic, deafen, camera, chat, soundboard, More and an icon-only End.
Screenshare, its audio mute and fullscreen move into More; Share Screen
is only offered where getDisplayMedia exists. (The self-view comes from
the fork, lotus.27.)

#277: "Mute Screenshare Audio" showed whenever screensharing was allowed,
as a speaker icon that did nothing. It now shows while the fork reports
remote screenshare audio (io.lotus.screenshare_audio_state), or while it
is muted, so a hidden mute cannot silence the next share.

Fixes #265
Fixes #277

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
This commit is contained in:
Lotus CI
2026-10-19 17:24:38 -04:00
committed by jared
co-authored by Claude Opus 5.5
parent 5d2d7ca63c
commit 3418a8865d
4 changed files with 270 additions and 134 deletions
+25
View File
@@ -57,6 +57,15 @@ export function parseForkHotkeyReport(data: unknown): ForkHotkeyReport | null {
export const CALL_ERROR_CHANGED = 'call_error_changed';
export const SCREENSHARE_AUDIO_STATE = 'screenshare_audio_state';
/** Pure: parse the fork's `io.lotus.screenshare_audio_state` (#277). */
export function parseScreenshareAudioState(data: unknown): boolean | undefined {
if (typeof data !== 'object' || data === null) return undefined;
const { present } = data as { present?: unknown };
return typeof present === 'boolean' ? present : undefined;
}
export type AudioOutputOption = { id: string; label: string };
export const AUDIO_OUTPUT_STATE = 'audio_output_state';
@@ -281,6 +290,22 @@ export class CallControl extends EventEmitter implements CallControlState {
this.emit(AUDIO_OUTPUT_STATE);
}
// [Gitea #277] Whether a remote screenshare's audio is being received (the
// fork's `io.lotus.screenshare_audio_state`), so the call bar only offers
// "Mute Screenshare Audio" when there is something to mute.
private _remoteScreenshareAudio = false;
public get remoteScreenshareAudio(): boolean {
return this._remoteScreenshareAudio;
}
public onScreenshareAudioState(data: unknown) {
const present = parseScreenshareAudioState(data);
if (present === undefined || present === this._remoteScreenshareAudio) return;
this._remoteScreenshareAudio = present;
this.emit(SCREENSHARE_AUDIO_STATE);
}
private sendAudioOutput(deviceId: string): void {
if (!this.joined) return;
this.call.transport.send('io.lotus.set_audio_output', { deviceId }).catch(() => undefined);