From 2539070cc399d2c02d826a574b29295084ce62b4 Mon Sep 17 00:00:00 2001 From: Anathar <45571916+AnatharEridan@users.noreply.github.com> Date: Thu, 30 Apr 2026 16:58:31 +0300 Subject: [PATCH 1/2] feat: add audio sharing toggle for video call screen share Made-with: Cursor --- src/ipc/channels.ts | 10 ++++- .../ScreenSharingRequestTracker.ts | 25 +++++++++-- src/screenSharing/screenSharePicker.tsx | 43 +++++++++++++++++-- src/videoCallWindow/ipc.ts | 11 ++++- src/videoCallWindow/preload/index.ts | 16 ++++++- src/videoCallWindow/preload/jitsiBridge.ts | 20 ++++++++- 6 files changed, 112 insertions(+), 13 deletions(-) diff --git a/src/ipc/channels.ts b/src/ipc/channels.ts index 11991140ae..2108cbcbd1 100644 --- a/src/ipc/channels.ts +++ b/src/ipc/channels.ts @@ -34,7 +34,15 @@ type ChannelToArgsMap = { 'video-call-window/open-url': (url: string) => void; 'video-call-window/web-contents-id': (webContentsId: number) => void; 'video-call-window/open-screen-picker': () => { success: boolean }; - 'video-call-window/screen-sharing-source-responded': (source: string) => void; + 'video-call-window/screen-sharing-source-responded': ( + source: + | string + | null + | { + sourceId: string | null; + shareAudio?: boolean; + } + ) => void; 'video-call-window/screen-recording-is-permission-granted': () => boolean; 'video-call-window/close-requested': () => { success: boolean }; 'video-call-window/open-webview-dev-tools': () => boolean; diff --git a/src/screenSharing/ScreenSharingRequestTracker.ts b/src/screenSharing/ScreenSharingRequestTracker.ts index ba2a7d4dd3..1373a53154 100644 --- a/src/screenSharing/ScreenSharingRequestTracker.ts +++ b/src/screenSharing/ScreenSharingRequestTracker.ts @@ -4,10 +4,17 @@ import { desktopCapturer, ipcMain } from 'electron'; import type { DisplayMediaCallback } from './screenPicker/types'; const DEFAULT_TIMEOUT = 60000; +type ScreenSharingSelectionPayload = { + sourceId: string | null; + shareAudio?: boolean; +}; export class ScreenSharingRequestTracker { private activeListener: - | ((event: Event, sourceId: string | null) => void) + | (( + event: Event, + payload: string | null | ScreenSharingSelectionPayload + ) => void) | null = null; private activeRequestId: string | null = null; @@ -73,7 +80,10 @@ export class ScreenSharingRequestTracker { let callbackInvoked = false; - const listener = async (_event: Event, sourceId: string | null) => { + const listener = async ( + _event: Event, + payload: string | null | ScreenSharingSelectionPayload + ) => { if (this.activeRequestId !== requestId) { return; } @@ -86,6 +96,15 @@ export class ScreenSharingRequestTracker { this.removeListenerOnly(); this.markComplete(); + const sourceId = + typeof payload === 'object' && payload !== null + ? payload.sourceId + : payload; + const shareAudio = + typeof payload === 'object' && payload !== null + ? payload.shareAudio === true + : false; + if (!sourceId) { cb({ video: false } as any); return; @@ -107,7 +126,7 @@ export class ScreenSharingRequestTracker { return; } - cb({ video: selectedSource }); + cb(shareAudio ? ({ video: selectedSource, audio: 'loopback' } as any) : { video: selectedSource }); } catch (error) { console.error(`${this.label}: error validating source:`, error); cb({ video: false } as any); diff --git a/src/screenSharing/screenSharePicker.tsx b/src/screenSharing/screenSharePicker.tsx index 8cc8b357cf..2e73c15c11 100644 --- a/src/screenSharing/screenSharePicker.tsx +++ b/src/screenSharing/screenSharePicker.tsx @@ -2,6 +2,7 @@ import { Box, Button, Callout, + CheckBox, Label, Tabs, Scrollable, @@ -13,6 +14,7 @@ import type { SourcesOptions, } from 'electron'; import { ipcRenderer } from 'electron'; +import type { ChangeEvent } from 'react'; import { useCallback, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -31,6 +33,11 @@ interface IScreenSharePickerProps { includeTheme?: boolean; } +type ScreenSharingSelectionPayload = { + sourceId: string | null; + shareAudio?: boolean; +}; + export function ScreenSharePicker({ onMounted, responseChannel = 'video-call-window/screen-sharing-source-responded', @@ -43,6 +50,7 @@ export function ScreenSharePicker({ const [sources, setSources] = useState([]); const [currentTab, setCurrentTab] = useState<'screen' | 'window'>('screen'); const [selectedSourceId, setSelectedSourceId] = useState(null); + const [shareAudio, setShareAudio] = useState(false); const [ isScreenRecordingPermissionGranted, setIsScreenRecordingPermissionGranted, @@ -120,7 +128,11 @@ export function ScreenSharePicker({ wasVisibleRef.current = false; if (!responseSentRef.current) { responseSentRef.current = true; - ipcRenderer.send(responseChannel, null); + const payload: ScreenSharingSelectionPayload = { + sourceId: null, + shareAudio: false, + }; + ipcRenderer.send(responseChannel, payload); } } }, [visible, responseChannel]); @@ -175,7 +187,11 @@ export function ScreenSharePicker({ responseSentRef.current = true; setVisible(false); - ipcRenderer.send(responseChannel, selectedSourceId); + const payload: ScreenSharingSelectionPayload = { + sourceId: selectedSourceId, + shareAudio, + }; + ipcRenderer.send(responseChannel, payload); } }; @@ -186,7 +202,11 @@ export function ScreenSharePicker({ } responseSentRef.current = true; setVisible(false); - ipcRenderer.send(responseChannel, null); + const payload: ScreenSharingSelectionPayload = { + sourceId: null, + shareAudio: false, + }; + ipcRenderer.send(responseChannel, payload); }; // Filter sources based on the current tab @@ -383,8 +403,25 @@ export function ScreenSharePicker({ + + ) => + setShareAudio(event.currentTarget.checked) + } + /> + +