diff --git a/content.js b/content.js index 771ef44..cf046a6 100644 --- a/content.js +++ b/content.js @@ -567,6 +567,13 @@ analyzeBtn.disabled = true; try { + if (tab === 'qa') { + const freshData = extractApplicationQuestions(); + if (freshData.length > 0) { + jobData.questions = freshData; + } + } + const prompt = buildPromptV2(tab, jobData, settings.userProfile, settings.language); const response = await chrome.runtime.sendMessage({ type: 'GEMINI_REQUEST', @@ -749,6 +756,25 @@ Brief honest assessment of this opportunity for my profile` // ─── Markdown Renderer ─────────────────────────────────────────────────── function renderMarkdown(text) { + try { + const startIdx = text.indexOf('{'); + const endIdx = text.lastIndexOf('}'); + if (startIdx !== -1 && endIdx !== -1) { + const parsed = JSON.parse(text.substring(startIdx, endIdx + 1)); + let html = '
'; + for (const [q, a] of Object.entries(parsed)) { + html += `
+
❓ ${q}
+
💡 ${a}
+
`; + } + html += '
'; + return html; + } + } catch(e) { + // Not JSON, continue to normal markdown rendering + } + return text .replace(/&/g, '&').replace(//g, '>') .replace(/^## (.+)$/gm, '

$1

') @@ -766,25 +792,77 @@ Brief honest assessment of this opportunity for my profile` function autoFillAnswers(qaText, root) { showPanelToast(root, '🔄 Attempting to fill answers...'); - // Parse numbered answers from AI response - const answerLines = qaText.split('\n'); - const inputs = document.querySelectorAll('.jobs-easy-apply-content input[type="text"], .jobs-easy-apply-content textarea'); + let answers = {}; + try { + const startIndex = qaText.indexOf('{'); + const endIndex = qaText.lastIndexOf('}'); + if (startIndex !== -1 && endIndex !== -1) { + answers = JSON.parse(qaText.substring(startIndex, endIndex + 1)); + } + } catch (e) { + console.error('Failed to parse AI answers JSON:', e); + } + + if (Object.keys(answers).length === 0) { + showPanelToast(root, '⚠️ Could not parse answers. Please copy manually.'); + return; + } let filled = 0; - inputs.forEach((input, idx) => { - const answerLine = answerLines.find(l => l.match(new RegExp(`^${idx + 1}\\.`))); - if (answerLine) { - const answer = answerLine.replace(/^\d+\.\s*/, '').trim(); - if (answer) { - input.value = answer; + const labels = document.querySelectorAll('.jobs-easy-apply-content label, .jobs-easy-apply-content legend, .jobs-easy-apply-content .fb-dash-form-element__label'); + + labels.forEach(label => { + const qText = label.textContent.trim().toLowerCase(); + if (!qText) return; + + const matchedKey = Object.keys(answers).find(k => { + const kLower = k.toLowerCase(); + return kLower.includes(qText) || qText.includes(kLower); + }); + + if (matchedKey && answers[matchedKey]) { + const parent = label.closest('.fb-dash-form-element') || label.parentElement; + const answerVal = String(answers[matchedKey]); + + // 1. Fill Text inputs / Textareas + const input = parent.querySelector('input[type="text"], textarea'); + if (input) { + input.value = answerVal; input.dispatchEvent(new Event('input', { bubbles: true })); input.dispatchEvent(new Event('change', { bubbles: true })); filled++; } + + // 2. Select dropdowns + const select = parent.querySelector('select'); + if (select) { + const options = Array.from(select.options); + const matchedOpt = options.find(o => o.text.toLowerCase() === answerVal.toLowerCase() || o.value.toLowerCase() === answerVal.toLowerCase() || o.text.toLowerCase().includes(answerVal.toLowerCase())); + if (matchedOpt) { + select.value = matchedOpt.value; + select.dispatchEvent(new Event('change', { bubbles: true })); + filled++; + } + } + + // 3. Radio Buttons (Yes/No) + const radios = parent.querySelectorAll('input[type="radio"]'); + if (radios.length > 0) { + let radioClicked = false; + radios.forEach(radio => { + const radioLabel = radio.parentElement.textContent.trim().toLowerCase(); + const aLower = answerVal.toLowerCase(); + if (radioLabel === aLower || (aLower === 'yes' && radioLabel.includes('yes')) || (aLower === 'no' && radioLabel.includes('no'))) { + radio.click(); + radioClicked = true; + } + }); + if (radioClicked) filled++; + } } }); - showPanelToast(root, filled > 0 ? `✅ Filled ${filled} fields` : '⚠️ Could not auto-fill. Copy answers manually.'); + showPanelToast(root, filled > 0 ? `✅ Filled ${filled} fields` : '⚠️ No matching fields found to auto-fill.'); } // ─── Utilities ─────────────────────────────────────────────────────────── diff --git a/prompts.js b/prompts.js index 97167ba..c78dafc 100644 --- a/prompts.js +++ b/prompts.js @@ -112,47 +112,28 @@ Respond EXACTLY: ## NEW BULLET POINTS TO ADD - [2-3 new achievement bullets ready to paste into CV — in English]`; - P.qa = `You are a career coach. Generate ready-to-paste application answers. -IMPORTANT: ALL answers MUST be in English regardless of the job posting language. + const dynamicQuestions = job.questions && job.questions.length > 0 + ? 'Answer THESE specific application questions:\n' + job.questions.map((q, i) => `${i + 1}. ${q.question}`).join('\n') + : 'Answer these common application questions:\n1. Why are you interested in this role?\n2. What is your relevant experience?\n3. What are your salary expectations?\n4. When can you start?\n5. Do you require visa sponsorship?'; + + P.qa = `You are a career coach. +IMPORTANT: ALL answers MUST be in English. Be highly concise (1 sentence max for text inputs, Yes/No for radio inputs). ${prof} JOB: ${ctx} -Generate answers for ALL: +${dynamicQuestions} -## 1. Why are you interested in this role? -[3-4 sentences specific to ${co} and this role — in English] - -## 2. Why ${co}? -[2-3 sentences referencing something specific about the company] - -## 3. Relevant experience -[4-5 sentences — most relevant achievements for this role] - -## 4. Expected salary -[Competitive range for ${loc} market — USD and local currency] - -## 5. When can you start? -Available immediately or within 2 weeks. - -## 6. Visa sponsorship needed? -Based in Jordan. Open to relocation. Willing to process visa requirements. - -## 7. Notice period -Available immediately — currently seeking new opportunities. - -## 8. Willing to relocate? -Yes, open to ${loc} and broader Middle East. - -## 9. Why are you the best candidate? -[3-4 sentences — unique differentiators for THIS role] - -## 10. Key technology experience -[Identify top 2-3 technologies from the job description and write specific answers about my experience] - -RULES: ALL answers in English. Ready to paste. No brackets. Concise. Use numbers.`; +RETURN EXACTLY A RAW JSON OBJECT where keys are the exact questions above and values are your concise answers. +Do NOT use markdown code blocks like \`\`\`json. Just return the raw JSON. +Example: +{ + "Have you completed the following level of education: Bachelor's Degree?": "Yes", + "How many years of work experience do you have with Infrastructure?": "6", + "Why are you interested in this role?": "My background in mapping perfectly aligns with your needs." +}`; P.benefits = `You are a career analyst specializing in tech compensation in MENA. IMPORTANT: Respond ENTIRELY in English regardless of the job posting language.