diff --git a/manifest.json b/manifest.json index ab37d07..e9e7d66 100644 --- a/manifest.json +++ b/manifest.json @@ -44,15 +44,6 @@ "post_feed.css" ], "run_at": "document_idle" - }, - { - "matches": [ - "https://claude.ai/*" - ], - "js": [ - "claude-arabic-voice/content.js" - ], - "run_at": "document_idle" } ], "action": { @@ -63,15 +54,6 @@ "128": "icons/icon128.png" } }, - "web_accessible_resources": [ - { - "resources": [ - "claude-arabic-voice/speech.html", - "claude-arabic-voice/speech.js" - ], - "matches": ["https://claude.ai/*"] - } - ], "background": { "service_worker": "background.js" }, diff --git a/popup.html b/popup.html index af28632..a1d57e2 100644 --- a/popup.html +++ b/popup.html @@ -414,6 +414,23 @@
+ +
+
+ 🎤 إملاء صوتي ذكي + +
+ +
+ +
اضغط للتحدث (بالعربية)
+
+ + +
+
diff --git a/popup.js b/popup.js index f6b719d..7556024 100644 --- a/popup.js +++ b/popup.js @@ -262,7 +262,169 @@ document.getElementById('autofill-btn').addEventListener('click', async () => { } }); +// ─── Voice Dictation ─────────────────────────────────────────────────────────── + +function initDictation() { + const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; + const statusEl = document.getElementById('dictation-status'); + const micBtn = document.getElementById('dictation-mic-btn'); + const resultArea = document.getElementById('dictation-result'); + const copyBtn = document.getElementById('copy-dictation-btn'); + + if (!SpeechRecognition) { + statusEl.textContent = '❌ المتصفح لا يدعم التعرف على الصوت'; + micBtn.disabled = true; + micBtn.style.opacity = '0.5'; + return; + } + + let recognition = new SpeechRecognition(); + recognition.continuous = true; + recognition.interimResults = true; + recognition.lang = 'ar-SA'; + + let isRecording = false; + let finalTranscript = ''; + + recognition.onstart = () => { + isRecording = true; + micBtn.style.background = 'linear-gradient(135deg, #ff4d6d, #c9184a)'; + micBtn.innerHTML = '🔴'; + micBtn.style.animation = 'claude-voice-pulse 1.5s infinite'; // We can add this via inline or just rely on color + statusEl.textContent = 'جارٍ الاستماع... اضغط للإيقاف'; + finalTranscript = ''; + resultArea.value = ''; + copyBtn.style.display = 'none'; + }; + + recognition.onresult = (event) => { + let interimTranscript = ''; + let currentFinal = ''; + + for (let i = event.resultIndex; i < event.results.length; i++) { + const transcript = event.results[i][0].transcript; + if (event.results[i].isFinal) { + currentFinal += transcript + ' '; + } else { + interimTranscript += transcript; + } + } + + finalTranscript += currentFinal; + resultArea.value = finalTranscript + interimTranscript; + }; + + recognition.onerror = (event) => { + console.error('Speech recognition error', event.error); + if (event.error === 'not-allowed') { + statusEl.textContent = '❌ يرجى السماح للميكروفون من إعدادات المتصفح'; + } else if (event.error !== 'no-speech') { + statusEl.textContent = '❌ خطأ: ' + event.error; + } + stopRecording(false); + }; + + recognition.onend = () => { + if (isRecording) { + stopRecording(true); + } + }; + + function stopRecording(process = true) { + if (!isRecording) return; + isRecording = false; + recognition.stop(); + micBtn.style.background = 'linear-gradient(135deg, var(--accent), #9b5de5)'; + micBtn.innerHTML = '🎤'; + + const textToProcess = resultArea.value.trim(); + if (textToProcess && process) { + statusEl.textContent = '✨ جارٍ التحسين بواسطة الذكاء الاصطناعي...'; + refineWithGemini(textToProcess); + } else { + statusEl.textContent = 'اضغط للتحدث (بالعربية)'; + } + } + + micBtn.addEventListener('click', async () => { + if (isRecording) { + stopRecording(true); + } else { + try { + const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); + stream.getTracks().forEach(t => t.stop()); + recognition.start(); + } catch (err) { + statusEl.textContent = '❌ يجب السماح باستخدام الميكروفون'; + } + } + }); + + copyBtn.addEventListener('click', () => { + navigator.clipboard.writeText(resultArea.value); + const originalText = copyBtn.textContent; + copyBtn.textContent = '✅ تم النسخ!'; + setTimeout(() => { copyBtn.textContent = originalText; }, 2000); + }); + + async function refineWithGemini(text) { + const apiKey = document.getElementById('api-key-input').value.trim(); + if (!apiKey) { + statusEl.textContent = '⚠️ الرجاء إدخال مفتاح Gemini لتحسين النص'; + copyBtn.style.display = 'inline-flex'; + return; + } + + try { + const prompt = `You are an expert Arabic text refinement assistant. Your task is to: +1. Correct any speech recognition errors in the following Arabic text +2. Fix punctuation, capitalization, and formatting +3. Keep the original meaning and content intact +4. Do NOT add any new information or commentary +5. Return ONLY the corrected text, nothing else + +TEXT TO REFINE: +${text} + +CORRECTED TEXT:`; + + const response = await fetch('https://cv.intaleqapp.com/cv/server/generate_cv.php', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + action: 'generateText', + apiKey: apiKey, + prompt: prompt + }) + }); + + if (response.ok) { + const rawText = await response.text(); + let data; + try { data = JSON.parse(rawText); } catch(e) { data = rawText; } + + let processedText = text; + if (data && data.candidates && data.candidates[0]) { + processedText = data.candidates[0].content?.parts?.[0]?.text; + } else if (typeof data === 'string') { + processedText = data; + } + + resultArea.value = processedText.trim(); + statusEl.textContent = '✅ تم التحسين بنجاح! يمكنك النسخ الآن.'; + copyBtn.style.display = 'inline-flex'; + } else { + throw new Error('Server error'); + } + } catch (e) { + statusEl.textContent = '⚠️ فشل التحسين الذكي، نعرض النص الأصلي.'; + copyBtn.style.display = 'inline-flex'; + } + } +} + // ─── Init ──────────────────────────────────────────────────────────────────── loadSettings(); +initDictation();