From 5fd6969ff896173f33ab82db0c18935980f12701 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Sun, 17 May 2026 22:45:01 +0300 Subject: [PATCH] Auto-deploy: 2026-05-17 22:45:01 --- content.js | 149 +++++++++++++++++++++++++++++++++++++++- cv_template.html | 14 ++-- prompts.js | 15 ++++ server/cv_template.html | 14 ++-- server/generate_cv.php | 4 +- 5 files changed, 177 insertions(+), 19 deletions(-) diff --git a/content.js b/content.js index c6854a4..970d79f 100644 --- a/content.js +++ b/content.js @@ -955,6 +955,149 @@ Brief honest assessment of this opportunity for my profile` setTimeout(() => toast.classList.remove('show'), 2500); } + // ─── List Scanner ──────────────────────────────────────────────────────── + + function injectListScanner() { + if (document.getElementById('lja-scan-btn')) return; + + const btn = document.createElement('button'); + btn.id = 'lja-scan-btn'; + btn.innerHTML = '🔍 Scan List'; + btn.style.cssText = ` + position: fixed; + bottom: 20px; + left: 20px; + z-index: 999999; + background: linear-gradient(135deg, #00d67e, #00a65e); + color: white; + border: none; + border-radius: 50px; + padding: 12px 24px; + font-size: 14px; + font-weight: bold; + cursor: pointer; + box-shadow: 0 4px 15px rgba(0, 214, 126, 0.4); + transition: transform 0.2s; + font-family: Arial, sans-serif; + `; + btn.onmouseover = () => btn.style.transform = 'scale(1.05)'; + btn.onmouseout = () => btn.style.transform = 'scale(1)'; + + btn.onclick = async () => { + btn.innerHTML = '⏳ Scanning...'; + try { + await scanJobList(); + } catch (e) { + console.error(e); + alert('Scan failed: ' + e.message); + } + btn.innerHTML = '🔍 Scan List'; + }; + + document.body.appendChild(btn); + } + + async function scanJobList() { + const listItems = document.querySelectorAll('.jobs-search-results__list-item, .job-card-container'); + if (!listItems.length) { + alert('No job items found on this page. Scroll down to load them.'); + return; + } + + const jobsToScan = []; + listItems.forEach((item, index) => { + const titleEl = item.querySelector('.job-card-list__title, .artdeco-entity-lockup__title'); + const companyEl = item.querySelector('.job-card-container__primary-description, .artdeco-entity-lockup__subtitle'); + if (titleEl && companyEl) { + jobsToScan.push({ + index: index, + title: titleEl.textContent.trim().replace(/\n/g, ''), + company: companyEl.textContent.trim().replace(/\n/g, ''), + element: item + }); + } + }); + + if (!jobsToScan.length) { + alert('Could not parse job titles.'); + return; + } + + document.querySelectorAll('.lja-badge').forEach(b => b.remove()); + + const listDataStr = JSON.stringify(jobsToScan.map(j => ({ index: j.index, title: j.title, company: j.company }))); + + const settings = await getSettings(); + if (!settings.apiKey) { + alert('Please set your API key in the extension popup first.'); + return; + } + + if (typeof buildPromptV2 !== 'function') { + alert('buildPromptV2 not found. Extension error.'); + return; + } + + const promptStr = buildPromptV2('list_analysis', { skills: [], listData: listDataStr }, settings.userProfile, settings.language); + + const response = await fetch( + \`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=\${settings.apiKey}\`, + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + contents: [{ parts: [{ text: promptStr }] }], + generationConfig: { maxOutputTokens: 2048, temperature: 0.1 } + }) + } + ); + + if (!response.ok) throw new Error('API Request failed'); + const data = await response.json(); + let resultText = data.candidates[0].content.parts[0].text; + resultText = resultText.replace(/\`\`\`json/g, '').replace(/\`\`\`/g, '').trim(); + + let results; + try { + results = JSON.parse(resultText); + } catch(e) { + console.error(resultText); + throw new Error('Failed to parse AI response'); + } + + results.forEach(res => { + const jobItem = jobsToScan.find(j => j.index === res.index); + if (jobItem) { + const badge = document.createElement('div'); + badge.className = 'lja-badge'; + badge.style.cssText = \` + display: inline-block; + padding: 4px 8px; + border-radius: 6px; + font-size: 11px; + font-weight: bold; + margin-top: 6px; + color: white; + width: 100%; + box-sizing: border-box; + \`; + if (res.verdict === 'YES') { + badge.style.background = 'linear-gradient(135deg, #00d67e, #00a65e)'; + badge.innerHTML = \`✅ MATCH: \${res.reason}\`; + } else if (res.verdict === 'NO') { + badge.style.background = 'linear-gradient(135deg, #ff4d6d, #d90429)'; + badge.innerHTML = \`❌ SKIP: \${res.reason}\`; + } else { + badge.style.background = 'linear-gradient(135deg, #ffb347, #ff9200)'; + badge.innerHTML = \`⚠️ MAYBE: \${res.reason}\`; + } + + const contentContainer = jobItem.element.querySelector('.artdeco-entity-lockup__content'); + if (contentContainer) contentContainer.appendChild(badge); + } + }); + } + // ─── SPA Navigation Observer ───────────────────────────────────────────── let lastUrl = location.href; @@ -970,7 +1113,7 @@ Brief honest assessment of this opportunity for my profile` const old = document.getElementById('lja-root'); if (old) old.remove(); window.__linkedinAnalyzerLoaded = false; - setTimeout(injectOverlay, 1200); + setTimeout(() => { injectOverlay(); injectListScanner(); }, 1200); } // Watch for URL changes (SPA navigation + job switches) @@ -1006,9 +1149,9 @@ Brief honest assessment of this opportunity for my profile` function init() { if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', () => setTimeout(injectOverlay, 1000)); + document.addEventListener('DOMContentLoaded', () => setTimeout(() => { injectOverlay(); injectListScanner(); }, 1000)); } else { - setTimeout(injectOverlay, 1000); + setTimeout(() => { injectOverlay(); injectListScanner(); }, 1000); } } diff --git a/cv_template.html b/cv_template.html index 91572c2..55e5a36 100644 --- a/cv_template.html +++ b/cv_template.html @@ -114,7 +114,7 @@
@@ -123,11 +123,11 @@
Co-Founder & Lead Developer — Tripz Egypt
-
Jan 2024 – Present | Cairo / Remote
+
Jan 2023 – Present | Cairo / Remote
@@ -139,7 +139,7 @@
@@ -168,10 +168,10 @@
Languages
diff --git a/prompts.js b/prompts.js index b5dfb55..6902669 100644 --- a/prompts.js +++ b/prompts.js @@ -26,6 +26,7 @@ function buildPromptV2(tab, job, userProfile, language) { ${analysisLang} Evaluate this job against my profile with brutal honesty and EXTREME brevity. DO NOT recount my history, military background, or summarize my profile. Keep it actionable and short. +CRITICAL RULE: The user is actively seeking to step down from Architect/CTO roles to "Senior Software Engineer" (Flutter, Backend, Full Stack) to gain corporate Agile experience. DO NOT reject jobs for being a "downgrade" if they are Senior level and match his tech stack (Flutter, Python, PHP, Node). ${prof} @@ -177,6 +178,20 @@ Respond EXACTLY: ## OVERALL RATING: X/10 **Worth applying?** [YES / MAYBE / NO] [2-3 sentence honest assessment]`; + P.list_analysis = `You are an AI pre-screening jobs. +I will give you a JSON array of jobs (Title, Company). +My stack: Flutter, Python (FastAPI), PHP, Node.js, GIS, Technical Architect. +I am actively seeking Senior Engineer, Tech Lead, or Architect roles. +Reject Java, C#, C++, .NET, or pure Product Management roles. + +JOBS LIST: +${job.listData} + +Respond ONLY with a raw JSON array of objects, one for each job, containing: +[ + { "index": number, "verdict": "YES" | "NO" | "MAYBE", "reason": "Short reason" } +] +Do not wrap in markdown \`\`\`json blocks.`; return P[tab] || P.analysis; } diff --git a/server/cv_template.html b/server/cv_template.html index 91572c2..55e5a36 100644 --- a/server/cv_template.html +++ b/server/cv_template.html @@ -114,7 +114,7 @@
@@ -123,11 +123,11 @@
Co-Founder & Lead Developer — Tripz Egypt
-
Jan 2024 – Present | Cairo / Remote
+
Jan 2023 – Present | Cairo / Remote
@@ -139,7 +139,7 @@
@@ -168,10 +168,10 @@
Languages
diff --git a/server/generate_cv.php b/server/generate_cv.php index 1713a90..5c50bed 100644 --- a/server/generate_cv.php +++ b/server/generate_cv.php @@ -61,8 +61,8 @@ if ($action === 'generatePdf') { $prompt = "You are an expert ATS CV tailor. Read the following job description and generate tailored content for my CV to maximize my chances of getting an interview. Return ONLY a valid JSON object with EXACTLY three keys: 'headline', 'summary', and 'skills'. -The 'headline' should be a 5-6 word professional title relevant to the job. -The 'summary' should be a 3-sentence powerful paragraph highlighting skills relevant to the job. +The 'headline' should be a clean, confident title like 'Senior Solutions Architect', 'Enterprise Architect', or 'Senior Software Engineer' (do NOT use clunky, stuffed titles like 'Senior Enterprise Solution Architect Leader'). +The 'summary' MUST open with a powerful hook: 'Built two production ride-hailing platforms from zero to thousands of users, on proprietary infrastructure, in sanctioned markets.' Then use the next 2 sentences to seamlessly tie my relevant skills and achievements to the job description requirements. Avoid generic boilerplate. The 'skills' should be a comma-separated list of 10 highly relevant ATS keywords. Do NOT use markdown blocks like ```json, just return raw JSON text.