diff --git a/popup.js b/popup.js index d663dad..923b1a7 100644 --- a/popup.js +++ b/popup.js @@ -296,7 +296,12 @@ function initDictation() { // Listen to messages from the injected script chrome.runtime.onMessage.addListener((message) => { - if (message.type === 'SPEECH_RESULT') { + if (message.type === 'SPEECH_START_SUCCESS') { + isRecording = true; + micBtn.style.background = 'linear-gradient(135deg, #ff4d6d, #c9184a)'; + micBtn.innerHTML = '🔴'; + statusEl.textContent = 'جارٍ الاستماع... اضغط للإيقاف'; + } else if (message.type === 'SPEECH_RESULT') { interimTranscript = message.payload.interimText || ''; finalTranscript = message.payload.finalText || ''; resultArea.value = (finalTranscript + interimTranscript).trim(); @@ -330,67 +335,95 @@ function initDictation() { return; } - isRecording = true; - micBtn.style.background = 'linear-gradient(135deg, #ff4d6d, #c9184a)'; - micBtn.innerHTML = '🔴'; - statusEl.textContent = 'جارٍ الاستماع... اضغط للإيقاف'; + // Show a loading state until SPEECH_START_SUCCESS is received + micBtn.style.background = 'linear-gradient(135deg, #ffb3c1, #ff758f)'; + micBtn.innerHTML = '⏳'; + statusEl.textContent = 'جاري تهيئة الميكروفون...'; finalTranscript = ''; interimTranscript = ''; resultArea.value = ''; copyBtn.style.display = 'none'; - const extensionUrl = chrome.runtime.getURL('speech.html'); - chrome.scripting.executeScript({ target: { tabId: activeTab.id }, - func: (url) => { - let iframe = document.getElementById('lja-dictation-frame'); - if (!iframe) { - iframe = document.createElement('iframe'); - iframe.id = 'lja-dictation-frame'; - iframe.src = url; - // Avoid display:none because Chrome blocks SpeechRecognition in hidden cross-origin iframes - iframe.style.position = 'fixed'; - iframe.style.top = '0'; - iframe.style.left = '0'; - iframe.style.width = '1px'; - iframe.style.height = '1px'; - iframe.style.opacity = '0.01'; - iframe.style.pointerEvents = 'none'; - iframe.style.zIndex = '-9999'; - iframe.style.border = 'none'; - iframe.setAttribute('allow', 'microphone'); - document.body.appendChild(iframe); + func: (lang) => { + // Prevent multiple instances + if (window.__ljaDictationActive) { + return; + } + window.__ljaDictationActive = true; - // Relay messages from iframe to popup - window.addEventListener('message', (event) => { - if (event.data && event.data.type && event.data.type.startsWith('SPEECH_')) { - chrome.runtime.sendMessage(event.data); - } - }); + const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; + if (!SpeechRecognition) { + chrome.runtime.sendMessage({ type: 'SPEECH_ERROR', payload: { error: 'not-supported' } }); + window.__ljaDictationActive = false; + return; + } - // Listen for messages from popup to iframe - chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { - if (msg.type === 'START_RECORDING_FROM_POPUP' || msg.type === 'STOP_RECORDING_FROM_POPUP') { - iframe.contentWindow.postMessage(msg, '*'); - sendResponse({ success: true }); + const recognition = new SpeechRecognition(); + recognition.lang = lang; + recognition.continuous = true; + recognition.interimResults = true; + recognition.maxAlternatives = 1; + + recognition.onstart = () => { + chrome.runtime.sendMessage({ type: 'SPEECH_START_SUCCESS' }); + }; + + recognition.onresult = (event) => { + let interimText = ''; + let finalText = ''; + + for (let i = 0; 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: 'SPEECH_RESULT', + payload: { interimText, finalText } }); + }; + + recognition.onerror = (event) => { + console.error('[LJA Dictation] Error:', event.error); + chrome.runtime.sendMessage({ type: 'SPEECH_ERROR', payload: { error: event.error } }); + window.__ljaDictationActive = false; + }; + + recognition.onend = () => { + chrome.runtime.sendMessage({ type: 'SPEECH_END' }); + window.__ljaDictationActive = false; + }; + + // Listen for STOP message from popup + const stopListener = (msg, sender, sendResponse) => { + if (msg.type === 'STOP_RECORDING_FROM_POPUP') { + try { recognition.stop(); } catch(e) {} + window.__ljaDictationActive = false; + chrome.runtime.onMessage.removeListener(stopListener); + sendResponse({ success: true }); + } + }; + chrome.runtime.onMessage.addListener(stopListener); + + try { + recognition.start(); + } catch (e) { + chrome.runtime.sendMessage({ type: 'SPEECH_ERROR', payload: { error: e.message } }); + window.__ljaDictationActive = false; } }, - args: [extensionUrl] - }).then(() => { - // Small delay to ensure iframe has loaded and registered listener - setTimeout(() => { - chrome.tabs.sendMessage(activeTab.id, { - type: 'START_RECORDING_FROM_POPUP', - payload: { language: 'ar-SA' } - }); - }, 500); + args: ['ar-SA'] }).catch(err => { - console.error('Failed to inject iframe:', err); + console.error('Failed to inject speech recognition:', err); statusEl.textContent = '❌ فشل الاتصال بالصفحة الحالية'; - stopRecording(false); + micBtn.style.background = 'linear-gradient(135deg, var(--accent), #9b5de5)'; + micBtn.innerHTML = '🎤'; }); }); }