import { useState, useCallback, useRef } from 'react' import './ScreenCapture.css' const ScreenCapture = ({ onCapture, onError }) => { const [isCapturing, setIsCapturing] = useState(false) const [permissionState, setPermissionState] = useState('prompt') // 'prompt', 'granted', 'denied' const [errorMessage, setErrorMessage] = useState(null) const [stream, setStream] = useState(null) const videoRef = useRef(null) const canvasRef = useRef(null) const checkBrowserSupport = () => { if (!navigator.mediaDevices || !navigator.mediaDevices.getDisplayMedia) { return { supported: false, message: 'Screen capture is not supported in your browser. Please use Chrome, Edge, or Firefox.' } } return { supported: true } } const handlePermissionError = (error) => { console.error('Screen capture error:', error) let userMessage = '' let developerInfo = '' if (error.name === 'NotAllowedError' || error.name === 'PermissionDeniedError') { userMessage = 'Screen capture permission was denied. Please click "Allow" when prompted to share your screen.' developerInfo = 'User denied permission' setPermissionState('denied') } else if (error.name === 'NotFoundError') { userMessage = 'No screen capture sources available. Please make sure you have a display connected.' developerInfo = 'No capture sources found' } else if (error.name === 'NotReadableError') { userMessage = 'Screen capture source is currently in use by another application. Please close other screen recording applications and try again.' developerInfo = 'Hardware or OS constraint' } else if (error.name === 'OverconstrainedError') { userMessage = 'The requested screen capture settings are not supported. Trying with default settings...' developerInfo = 'Constraint error' } else if (error.name === 'TypeError') { userMessage = 'Screen capture API error. Please refresh the page and try again.' developerInfo = 'API usage error' } else if (error.name === 'AbortError') { userMessage = 'Screen capture was cancelled.' developerInfo = 'User aborted' } else { userMessage = `Screen capture failed: ${error.message || 'Unknown error'}` developerInfo = error.toString() } setErrorMessage(userMessage) if (onError) { onError({ userMessage, technicalDetails: { name: error.name, message: error.message, info: developerInfo } }) } return userMessage } const startCapture = useCallback(async () => { const support = checkBrowserSupport() if (!support.supported) { setErrorMessage(support.message) if (onError) { onError({ userMessage: support.message, technicalDetails: { name: 'BrowserNotSupported' } }) } return } setIsCapturing(true) setErrorMessage(null) try { // Configure capture options with fallbacks const displayMediaOptions = { video: { displaySurface: 'browser', // Prefer browser tab logicalSurface: true, cursor: 'always', width: { ideal: 1920 }, height: { ideal: 1080 } }, audio: false, preferCurrentTab: false, selfBrowserSurface: 'exclude', surfaceSwitching: 'include', systemAudio: 'exclude' } // Try to get display media with full options let mediaStream try { mediaStream = await navigator.mediaDevices.getDisplayMedia(displayMediaOptions) } catch (err) { console.warn('Failed with full options, trying minimal options:', err) // Fallback to minimal options mediaStream = await navigator.mediaDevices.getDisplayMedia({ video: true, audio: false }) } setStream(mediaStream) setPermissionState('granted') // Set up video element to display the stream if (videoRef.current) { videoRef.current.srcObject = mediaStream await videoRef.current.play() } // Listen for stream end (user stops sharing) mediaStream.getVideoTracks()[0].addEventListener('ended', () => { stopCapture() setErrorMessage('Screen sharing was stopped.') }) // Capture a frame after a short delay to ensure video is ready setTimeout(() => captureFrame(mediaStream), 500) } catch (error) { handlePermissionError(error) } finally { setIsCapturing(false) } }, []) const captureFrame = useCallback((mediaStream) => { if (!videoRef.current || !canvasRef.current) { setErrorMessage('Unable to capture frame. Video elements not ready.') return } try { const video = videoRef.current const canvas = canvasRef.current const context = canvas.getContext('2d') // Set canvas size to match video canvas.width = video.videoWidth canvas.height = video.videoHeight // Draw video frame to canvas context.drawImage(video, 0, 0, canvas.width, canvas.height) // Convert to blob canvas.toBlob((blob) => { if (blob && onCapture) { // Convert blob to base64 for sending to backend const reader = new FileReader() reader.onloadend = () => { onCapture({ dataUrl: reader.result, blob: blob, width: canvas.width, height: canvas.height, timestamp: new Date().toISOString() }) } reader.readAsDataURL(blob) } }, 'image/png', 0.9) } catch (error) { console.error('Error capturing frame:', error) setErrorMessage('Failed to capture frame from screen.') } }, [onCapture]) const stopCapture = useCallback(() => { if (stream) { stream.getTracks().forEach(track => track.stop()) setStream(null) } if (videoRef.current) { videoRef.current.srcObject = null } }, [stream]) const retryCapture = useCallback(() => { setErrorMessage(null) setPermissionState('prompt') startCapture() }, [startCapture]) return (