UNPKG

@airsurfer09/web-handsfree

Version:

A React package for integrating Convai's AI-powered voice assistants with LiveKit for real-time audio/video conversations

198 lines 7.65 kB
import { useCallback, useState, useEffect } from "react"; import { logger } from "../utils/logger"; /** * Hook for managing screen sharing controls in LiveKit. * * Provides methods to enable/disable screen sharing, manage screen share state, * and handle screen sharing with audio support. * * Features: * - Includes current tab in screen share options (selfBrowserSurface: "include") * - Supports surface switching during screen share * - Supports system audio capture * * Note: By default, browsers exclude the current tab from screen share to prevent * infinite recursion ("hall of mirrors" effect). This hook enables it by setting * selfBrowserSurface: "include", but be aware this may cause visual feedback loops. * * @param {Room | null} room - LiveKit room instance * @returns {ScreenShareControls} Object containing screen share control methods and state * * @example * ```tsx * function ScreenShareControls() { * const screenShareControls = useScreenShare(room); * * return ( * <div> * <button onClick={screenShareControls.toggleScreenShare}> * {screenShareControls.isScreenShareEnabled ? 'Stop Sharing' : 'Share Screen'} * </button> * </div> * ); * } * ``` */ export const useScreenShare = (room) => { const [isScreenShareEnabled, setIsScreenShareEnabled] = useState(false); const [isScreenShareActive, setIsScreenShareActive] = useState(false); // Update screen share state when room changes useEffect(() => { if (!room) { setIsScreenShareEnabled(false); setIsScreenShareActive(false); return; } const localParticipant = room.localParticipant; // Set initial state setIsScreenShareEnabled(localParticipant.isScreenShareEnabled); setIsScreenShareActive(localParticipant.isScreenShareEnabled); // Listen for screen share state changes const handleTrackPublished = (track) => { if (track.source === 'screen_share') { setIsScreenShareActive(true); logger.log("🖥️ Screen share started"); } }; const handleTrackUnpublished = (track) => { if (track.source === 'screen_share') { setIsScreenShareActive(false); logger.log("🖥️ Screen share stopped"); } }; const handleTrackMuted = (track) => { if (track.source === 'screen_share') { setIsScreenShareActive(false); logger.log("🖥️ Screen share muted"); } }; const handleTrackUnmuted = (track) => { if (track.source === 'screen_share') { setIsScreenShareActive(true); logger.log("🖥️ Screen share unmuted"); } }; // Subscribe to events localParticipant.on('trackPublished', handleTrackPublished); localParticipant.on('trackUnpublished', handleTrackUnpublished); localParticipant.on('trackMuted', handleTrackMuted); localParticipant.on('trackUnmuted', handleTrackUnmuted); return () => { localParticipant.off('trackPublished', handleTrackPublished); localParticipant.off('trackUnpublished', handleTrackUnpublished); localParticipant.off('trackMuted', handleTrackMuted); localParticipant.off('trackUnmuted', handleTrackUnmuted); }; }, [room]); // Enable screen share const enableScreenShare = useCallback(async () => { if (!room) { logger.warn("Cannot enable screen share: no room connection"); return; } try { // Use setScreenShareEnabled with options to include current tab await room.localParticipant.setScreenShareEnabled(true, { // @ts-ignore - selfBrowserSurface is a valid Chrome option but may not be in types selfBrowserSurface: "include", surfaceSwitching: "include", systemAudio: "include", }); setIsScreenShareEnabled(true); logger.log("🖥️ Screen share enabled successfully"); } catch (error) { logger.error("Failed to enable screen share:", error); throw error; } }, [room]); // Disable screen share const disableScreenShare = useCallback(async () => { if (!room) { logger.warn("Cannot disable screen share: no room connection"); return; } try { await room.localParticipant.setScreenShareEnabled(false); setIsScreenShareEnabled(false); logger.log("🖥️ Screen share disabled successfully"); } catch (error) { logger.error("Failed to disable screen share:", error); throw error; } }, [room]); // Toggle screen share const toggleScreenShare = useCallback(async () => { if (isScreenShareEnabled) { await disableScreenShare(); } else { await enableScreenShare(); } }, [isScreenShareEnabled, enableScreenShare, disableScreenShare]); // Enable screen share with audio const enableScreenShareWithAudio = useCallback(async () => { if (!room) { logger.warn("Cannot enable screen share with audio: no room connection"); return; } try { // Create screen tracks with audio enabled and include current tab const tracks = await room.localParticipant.createScreenTracks({ audio: true, // @ts-ignore - selfBrowserSurface is a valid Chrome option but may not be in types selfBrowserSurface: "include", surfaceSwitching: "include", systemAudio: "include", }); // Publish each track tracks.forEach((track) => { room.localParticipant.publishTrack(track); }); setIsScreenShareEnabled(true); logger.log("🖥️ Screen share with audio enabled successfully"); } catch (error) { logger.error("Failed to enable screen share with audio:", error); throw error; } }, [room]); // Get screen share tracks const getScreenShareTracks = useCallback(async () => { if (!room) { logger.warn("Cannot get screen share tracks: no room connection"); return []; } try { const localParticipant = room.localParticipant; const screenShareTracks = []; // Get all published tracks localParticipant.trackPublications.forEach((publication) => { if (publication.source === 'screen_share' && publication.track) { screenShareTracks.push(publication.track); } }); logger.log("🖥️ Screen share tracks:", screenShareTracks); return screenShareTracks; } catch (error) { logger.error("Failed to get screen share tracks:", error); throw error; } }, [room]); return { // Screen share state isScreenShareEnabled, isScreenShareActive, // Screen share controls enableScreenShare, disableScreenShare, toggleScreenShare, // Screen share with audio enableScreenShareWithAudio, // Screen share settings getScreenShareTracks, }; }; //# sourceMappingURL=useScreenShare.js.map