// 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();