// popup.js — Settings popup logic // ─── Helpers ──────────────────────────────────────────────────────────────── function showToast(msg, type = '') { const toast = document.getElementById('toast'); toast.textContent = msg; toast.className = 'toast show ' + type; setTimeout(() => { toast.className = 'toast'; }, 2500); } function setKeyStatus(state) { const badge = document.getElementById('key-status'); badge.style.display = 'inline-flex'; if (state === 'ok') { badge.className = 'status-badge success'; badge.innerHTML = ' Valid'; } else if (state === 'error') { badge.className = 'status-badge error'; badge.innerHTML = ' Invalid'; } else if (state === 'checking') { badge.className = 'status-badge checking'; badge.innerHTML = ' Testing...'; } } function updateUsageUI(count) { const max = 1000; const pct = Math.min((count / max) * 100, 100).toFixed(1); document.getElementById('usage-count').textContent = `${count} / 1,000 requests`; document.getElementById('usage-fill').style.width = pct + '%'; } // ─── Load saved settings ──────────────────────────────────────────────────── function loadSettings() { chrome.storage.sync.get(['apiKey', 'userProfile', 'language'], (data) => { if (data.apiKey) { document.getElementById('api-key-input').value = data.apiKey; setKeyStatus('ok'); } let profile = data.userProfile || DEFAULT_PROFILE; if (profile.includes('hamzaayed@intaleqapp.com') || profile.includes('hamzaayedflutter@gmail.com') || profile.includes('hamzaayed@tripz-egypt.com')) { profile = profile .replace(/hamzaayed@intaleqapp\.com/g, 'hamzaayed.dev@gmail.com') .replace(/hamzaayedflutter@gmail\.com/g, 'hamzaayed.dev@gmail.com') .replace(/hamzaayed@tripz-egypt\.com/g, 'hamzaayed.dev@gmail.com'); chrome.storage.sync.set({ userProfile: profile }); } document.getElementById('profile-textarea').value = profile; document.getElementById('lang-select').value = data.language || 'auto'; }); // Usage counter from local storage const today = new Date().toDateString(); chrome.storage.local.get(['usageDate', 'usageCount'], (data) => { if (data.usageDate === today) { updateUsageUI(data.usageCount || 0); } else { updateUsageUI(0); } }); } // ─── Save settings ────────────────────────────────────────────────────────── document.getElementById('save-btn').addEventListener('click', () => { const apiKey = document.getElementById('api-key-input').value.trim(); const userProfile = document.getElementById('profile-textarea').value.trim(); const language = document.getElementById('lang-select').value; if (!apiKey) { showToast('⚠️ Please enter your API Key', 'error'); return; } if (!userProfile) { showToast('⚠️ Profile cannot be empty', 'error'); return; } chrome.storage.sync.set({ apiKey, userProfile, language }, () => { showToast('✅ Settings saved!', 'success'); }); }); // ─── Toggle API key visibility ─────────────────────────────────────────────── document.getElementById('toggle-key').addEventListener('click', () => { const input = document.getElementById('api-key-input'); const btn = document.getElementById('toggle-key'); if (input.type === 'password') { input.type = 'text'; btn.textContent = '🙈'; } else { input.type = 'password'; btn.textContent = '👁'; } }); // ─── Test API Key ─────────────────────────────────────────────────────────── document.getElementById('test-key-btn').addEventListener('click', async () => { const apiKey = document.getElementById('api-key-input').value.trim(); if (!apiKey) { showToast('⚠️ Enter your API key first', 'error'); return; } setKeyStatus('checking'); try { const response = await fetch( `https://generativelanguage.googleapis.com/v1beta/models/gemini-flash-lite-latest:generateContent?key=${apiKey}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ contents: [{ parts: [{ text: 'Say "OK" in one word.' }] }], generationConfig: { maxOutputTokens: 5 } }) } ); if (response.ok) { setKeyStatus('ok'); showToast('✅ API Key is valid!', 'success'); } else { const err = await response.json(); setKeyStatus('error'); showToast('❌ Invalid key: ' + (err.error?.message || response.status), 'error'); } } catch (e) { setKeyStatus('error'); showToast('❌ Network error: ' + e.message, 'error'); } }); // ─── Reset profile ────────────────────────────────────────────────────────── document.getElementById('reset-profile-btn').addEventListener('click', () => { if (confirm('Reset profile to default?')) { document.getElementById('profile-textarea').value = DEFAULT_PROFILE; showToast('🔄 Profile reset to default'); } }); // ─── Clear cache ───────────────────────────────────────────────────────────── document.getElementById('clear-cache-btn').addEventListener('click', () => { chrome.storage.local.remove(['analysisCache'], () => { showToast('🗑️ Analysis cache cleared'); }); }); // ─── Clear all data ────────────────────────────────────────────────────────── document.getElementById('clear-all-btn').addEventListener('click', () => { if (confirm('Clear ALL data including API key and profile?')) { chrome.storage.sync.clear(() => { chrome.storage.local.clear(() => { document.getElementById('api-key-input').value = ''; document.getElementById('profile-textarea').value = DEFAULT_PROFILE; document.getElementById('lang-select').value = 'auto'; document.getElementById('key-status').style.display = 'none'; updateUsageUI(0); showToast('🗑️ All data cleared'); }); }); } }); // ─── Autofill Functionality ────────────────────────────────────────────────── function parseProfileText(text) { const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/; const phoneRegex = /\+?[0-9]{1,4}[ \t.-]?[0-9]{3,4}[ \t.-]?[0-9]{3,4}/; const linkedinRegex = /(https?:\/\/)?(www\.)?linkedin\.com\/in\/[a-zA-Z0-9_-]+/; const githubRegex = /(https?:\/\/)?(www\.)?github\.com\/[a-zA-Z0-9_-]+/; const emailMatch = text.match(emailRegex); const phoneMatch = text.match(phoneRegex); const linkedinMatch = text.match(linkedinRegex); const githubMatch = text.match(githubRegex); // Extract Name (first line usually) const lines = text.split('\n').map(l => l.trim()).filter(Boolean); let fullName = "Hamza Ayed"; if (lines.length > 0) { const firstLine = lines[0].replace(/—.*/, '').replace(/:.*/, '').trim(); fullName = firstLine; } const nameParts = fullName.split(' '); const firstName = nameParts[0] || ""; const lastName = nameParts.slice(1).join(' ') || ""; // Extract Summary let summary = ""; const summaryIdx = text.toLowerCase().indexOf("summary:"); if (summaryIdx !== -1) { const skillsIdx = text.toLowerCase().indexOf("core skills:", summaryIdx); if (skillsIdx !== -1) { summary = text.substring(summaryIdx + 8, skillsIdx).trim(); } else { summary = text.substring(summaryIdx + 8, summaryIdx + 500).trim(); } } return { fullName, firstName, lastName, email: emailMatch ? emailMatch[0] : "", phone: phoneMatch ? phoneMatch[0] : "", linkedin: linkedinMatch ? linkedinMatch[0] : "", github: githubMatch ? githubMatch[0] : "", summary, cvText: text }; } document.getElementById('autofill-btn').addEventListener('click', async () => { const userProfile = document.getElementById('profile-textarea').value.trim(); if (!userProfile) { showToast('⚠️ Please enter profile info first', 'error'); return; } const profileData = parseProfileText(userProfile); try { const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); if (!tab) { showToast('❌ No active tab found', 'error'); return; } // Set the profile data on the tab window context await chrome.scripting.executeScript({ target: { tabId: tab.id }, func: (data) => { window.__autofillProfileData = data; }, args: [profileData] }); // Execute the filler script const results = await chrome.scripting.executeScript({ target: { tabId: tab.id }, files: ['filler.js'] }); if (results && results[0]) { showToast('✨ ' + results[0].result, 'success'); } else { showToast('✨ Autofill completed!', 'success'); } } catch (e) { console.error(e); showToast('❌ Error: ' + e.message, 'error'); } }); // ─── Voice Dictation ─────────────────────────────────────────────────────────── // ─── Voice Dictation ─────────────────────────────────────────────────────────── function initDictation() { 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'); let isRecording = false; let finalTranscript = ''; let interimTranscript = ''; function stopRecording(process = true) { if (!isRecording) return; isRecording = false; chrome.runtime.sendMessage({ type: 'STOP_RECORDING_FROM_POPUP' }); micBtn.style.background = 'linear-gradient(135deg, var(--accent), #9b5de5)'; micBtn.innerHTML = '🎤'; const textToProcess = resultArea.value.trim(); if (textToProcess && process) { statusEl.textContent = '✨ جارٍ التحسين بواسطة الذكاء الاصطناعي...'; refineWithGemini(textToProcess); } else { if (!statusEl.textContent.includes('❌')) { statusEl.textContent = 'اضغط للتحدث (بالعربية)'; } } } // Listen to messages from the injected script chrome.runtime.onMessage.addListener((message) => { 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(); } else if (message.type === 'SPEECH_ERROR') { console.error('Speech recognition error', message.payload.error); if (message.payload.error === 'not-allowed') { statusEl.textContent = '❌ يرجى السماح للميكروفون من إعدادات المتصفح'; chrome.tabs.create({ url: chrome.runtime.getURL('permission.html') }); } else if (message.payload.error !== 'no-speech') { statusEl.textContent = '❌ خطأ: ' + message.payload.error; } stopRecording(false); } else if (message.type === 'SPEECH_END') { if (isRecording) { stopRecording(true); } } }); micBtn.addEventListener('click', async () => { if (isRecording) { stopRecording(true); } else { // First, get the active tab chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { const activeTab = tabs[0]; // Cannot inject into chrome:// or chrome-extension:// pages if (!activeTab || !activeTab.url || activeTab.url.startsWith('chrome://') || activeTab.url.startsWith('chrome-extension://') || activeTab.url.startsWith('edge://')) { statusEl.textContent = '❌ يرجى فتح موقع عادي أولاً (مثل جوجل أو لينكدإن)'; return; } // 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'; chrome.scripting.executeScript({ target: { tabId: activeTab.id }, func: (lang) => { // Prevent multiple instances if (window.__ljaDictationActive) { return; } window.__ljaDictationActive = true; const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; if (!SpeechRecognition) { chrome.runtime.sendMessage({ type: 'SPEECH_ERROR', payload: { error: 'not-supported' } }); window.__ljaDictationActive = false; return; } 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: ['ar-SA'] }).catch(err => { console.error('Failed to inject speech recognition:', err); statusEl.textContent = '❌ فشل الاتصال بالصفحة الحالية'; micBtn.style.background = 'linear-gradient(135deg, var(--accent), #9b5de5)'; micBtn.innerHTML = '🎤'; }); }); } }); 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();