// 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 ─────────────────────────────────────────────────────────── 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 updateCopyBtnVisibility() { if (resultArea.value.trim()) { copyBtn.style.display = 'inline-flex'; } else { copyBtn.style.display = 'none'; } } resultArea.addEventListener('input', updateCopyBtnVisibility); function stopRecording() { if (!isRecording) return; isRecording = false; chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { const activeTab = tabs[0]; if (activeTab && activeTab.id) { chrome.tabs.sendMessage(activeTab.id, { type: 'STOP_RECORDING_FROM_POPUP' }, () => { if (chrome.runtime.lastError) { console.warn('Could not send STOP message to tab:', chrome.runtime.lastError.message); } }); } }); micBtn.style.background = 'linear-gradient(135deg, var(--accent), #9b5de5)'; micBtn.innerHTML = '🎤'; updateCopyBtnVisibility(); 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(); updateCopyBtnVisibility(); } 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(); } else if (message.type === 'SPEECH_END') { if (isRecording) { stopRecording(); } } }); micBtn.addEventListener('click', async () => { if (isRecording) { stopRecording(); } 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) => { // Clean up any existing instances in this tab first if (window.__ljaSpeechActiveRecognition) { try { window.__ljaSpeechActiveRecognition.stop(); } catch(e) {} } if (window.__ljaSpeechStopListener) { try { chrome.runtime.onMessage.removeListener(window.__ljaSpeechStopListener); } catch(e) {} } window.__ljaDictationActive = true; window.__ljaSpeechUserStopped = false; window.__ljaSpeechAccumulatedText = ''; window.__ljaSpeechCurrentSessionFinalText = ''; const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; if (!SpeechRecognition) { chrome.runtime.sendMessage({ type: 'SPEECH_ERROR', payload: { error: 'not-supported' } }); window.__ljaDictationActive = false; return; } let recognition; function startRecognition() { if (window.__ljaSpeechUserStopped) return; recognition = new SpeechRecognition(); window.__ljaSpeechActiveRecognition = recognition; 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 sessionFinalText = ''; for (let i = 0; i < event.results.length; i++) { const result = event.results[i]; if (result.isFinal) { sessionFinalText += result[0].transcript + ' '; } else { interimText += result[0].transcript; } } window.__ljaSpeechCurrentSessionFinalText = sessionFinalText; const fullFinalText = window.__ljaSpeechAccumulatedText + sessionFinalText; chrome.runtime.sendMessage({ type: 'SPEECH_RESULT', payload: { interimText: interimText, finalText: fullFinalText } }); }; recognition.onerror = (event) => { console.error('[LJA Dictation] Error:', event.error); if (event.error === 'not-allowed' || event.error === 'service-not-allowed') { chrome.runtime.sendMessage({ type: 'SPEECH_ERROR', payload: { error: event.error } }); window.__ljaDictationActive = false; window.__ljaSpeechUserStopped = true; window.__ljaSpeechActiveRecognition = null; } else { console.warn('[LJA Dictation] Recoverable error:', event.error); } }; recognition.onend = () => { if (window.__ljaSpeechUserStopped) { // User explicitly stopped chrome.runtime.sendMessage({ type: 'SPEECH_END' }); window.__ljaDictationActive = false; window.__ljaSpeechActiveRecognition = null; } else { // Ended due to browser timeout / silence, restart it window.__ljaSpeechAccumulatedText += window.__ljaSpeechCurrentSessionFinalText; window.__ljaSpeechCurrentSessionFinalText = ''; console.log('[LJA Dictation] Silence/timeout. Restarting recognition...'); setTimeout(() => { if (!window.__ljaSpeechUserStopped) { startRecognition(); } }, 150); } }; try { recognition.start(); } catch (e) { chrome.runtime.sendMessage({ type: 'SPEECH_ERROR', payload: { error: e.message } }); window.__ljaDictationActive = false; window.__ljaSpeechUserStopped = true; window.__ljaSpeechActiveRecognition = null; } } // Listen for STOP message from popup window.__ljaSpeechStopListener = (msg, sender, sendResponse) => { if (msg.type === 'STOP_RECORDING_FROM_POPUP') { window.__ljaSpeechUserStopped = true; try { recognition.stop(); } catch(e) {} window.__ljaDictationActive = false; window.__ljaSpeechActiveRecognition = null; if (window.__ljaSpeechStopListener) { try { chrome.runtime.onMessage.removeListener(window.__ljaSpeechStopListener); } catch(e) {} window.__ljaSpeechStopListener = null; } sendResponse({ success: true }); } }; chrome.runtime.onMessage.addListener(window.__ljaSpeechStopListener); startRecognition(); }, 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; resultArea.value = ''; updateCopyBtnVisibility(); }, 1000); }); } // ─── Init ──────────────────────────────────────────────────────────────────── loadSettings(); initDictation();