// speech.js - Handles webkitSpeechRecognition inside the injected iframe let recognition = null; let isRecording = false; let localStream = null; function stopMediaTracks() { if (localStream) { localStream.getTracks().forEach(track => track.stop()); localStream = null; } } function initRecognition(language) { if (recognition) return recognition; const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; if (!SpeechRecognition) { throw new Error('Speech recognition not supported'); } recognition = new SpeechRecognition(); recognition.continuous = true; recognition.interimResults = true; recognition.lang = language || 'ar-SA'; recognition.maxAlternatives = 1; recognition.onresult = (event) => { let interimText = ''; let finalText = ''; for (let i = event.resultIndex; i < event.results.length; i++) { const result = event.results[i]; if (result.isFinal) { finalText += result[0].transcript + ' '; } else { interimText += result[0].transcript; } } chrome.runtime.sendMessage({ type: 'OFFSCREEN_RECORDING_RESULT', payload: { interimText, finalText } }); }; recognition.onerror = (event) => { console.error('[Speech Iframe] Recognition error:', event.error); if (event.error !== 'no-speech') { isRecording = false; stopMediaTracks(); } chrome.runtime.sendMessage({ type: 'OFFSCREEN_RECORDING_ERROR', payload: { error: event.error } }); }; recognition.onend = () => { if (isRecording) { try { recognition.start(); } catch (e) { console.warn('[Speech Iframe] Restart failed:', e); } } else { stopMediaTracks(); chrome.runtime.sendMessage({ type: 'OFFSCREEN_RECORDING_END' }); } }; return recognition; } // Listen for messages broadcasted across the extension chrome.runtime.onMessage.addListener((message) => { if (message.type === 'START_RECORDING_FROM_POPUP') { const lang = message.payload?.language || 'ar-SA'; navigator.mediaDevices.getUserMedia({ audio: true }) .then((stream) => { localStream = stream; try { if (!recognition || recognition.lang !== lang) { recognition = initRecognition(lang); } if (!isRecording) { recognition.start(); isRecording = true; // Tell popup it started successfully chrome.runtime.sendMessage({ type: 'OFFSCREEN_RECORDING_START_SUCCESS' }); } } catch (e) { console.error('[Speech Iframe] Failed to start:', e); chrome.runtime.sendMessage({ type: 'OFFSCREEN_RECORDING_ERROR', payload: { error: e.message } }); } }) .catch((err) => { console.error('[Speech Iframe] getUserMedia failed:', err); chrome.runtime.sendMessage({ type: 'OFFSCREEN_RECORDING_ERROR', payload: { error: 'not-allowed' } }); }); } if (message.type === 'STOP_RECORDING_FROM_POPUP') { if (isRecording && recognition) { isRecording = false; try { recognition.stop(); } catch (e) { console.warn('[Speech Iframe] Stop failed:', e); } } stopMediaTracks(); } });