fix(calls): the call-bar output menu uses the call frame's devices (#310)
deviceIds are hashed per frame/origin, so the ids the menu got from the chat page's enumerateDevices() were unknown inside the call iframe: the fork fell back to the default output while the menu showed a tick on the chosen device. Handle io.lotus.audio_output_state (also silencing the fork's "host did not ack" log) and build the menu from the fork's list and effective selection; older forks fall back to the old list. Fixes #310 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
3878a85339
commit
ef7690135d
@@ -54,6 +54,27 @@ export function parseForkHotkeyReport(data: unknown): ForkHotkeyReport | null {
|
||||
};
|
||||
}
|
||||
|
||||
export type AudioOutputOption = { id: string; label: string };
|
||||
export const AUDIO_OUTPUT_STATE = 'audio_output_state';
|
||||
|
||||
/** Pure: parse the fork's `io.lotus.audio_output_state` (#310). */
|
||||
export function parseAudioOutputState(
|
||||
data: unknown,
|
||||
): { deviceId?: string; available?: AudioOutputOption[] } | undefined {
|
||||
if (typeof data !== 'object' || data === null) return undefined;
|
||||
const { deviceId, available } = data as { deviceId?: unknown; available?: unknown };
|
||||
const list = Array.isArray(available)
|
||||
? available.filter(
|
||||
(o): o is AudioOutputOption =>
|
||||
typeof o === 'object' &&
|
||||
o !== null &&
|
||||
typeof (o as AudioOutputOption).id === 'string' &&
|
||||
typeof (o as AudioOutputOption).label === 'string',
|
||||
)
|
||||
: undefined;
|
||||
return { deviceId: typeof deviceId === 'string' ? deviceId : undefined, available: list };
|
||||
}
|
||||
|
||||
export class CallControl extends EventEmitter implements CallControlState {
|
||||
private state: CallControlState;
|
||||
|
||||
@@ -210,6 +231,25 @@ export class CallControl extends EventEmitter implements CallControlState {
|
||||
this.sendAudioOutput(deviceId);
|
||||
}
|
||||
|
||||
// [Gitea #310] deviceIds are hashed per frame/origin, so ids from the
|
||||
// host's own enumerateDevices() mean nothing inside the call iframe. The
|
||||
// fork reports its outputs (ids valid in the frame) and its effective
|
||||
// selection; the call bar's menu uses those when present.
|
||||
private _audioOutputs: AudioOutputOption[] | undefined;
|
||||
|
||||
public get audioOutputs(): AudioOutputOption[] | undefined {
|
||||
return this._audioOutputs;
|
||||
}
|
||||
|
||||
/** The fork's `io.lotus.audio_output_state` report. */
|
||||
public onAudioOutputState(data: unknown) {
|
||||
const report = parseAudioOutputState(data);
|
||||
if (!report) return;
|
||||
this._audioOutputs = report.available;
|
||||
if (report.deviceId !== undefined) this._audioOutputId = report.deviceId;
|
||||
this.emit(AUDIO_OUTPUT_STATE);
|
||||
}
|
||||
|
||||
private sendAudioOutput(deviceId: string): void {
|
||||
if (!this.joined) return;
|
||||
this.call.transport.send('io.lotus.set_audio_output', { deviceId }).catch(() => undefined);
|
||||
|
||||
Reference in New Issue
Block a user