diff --git a/utilities/AI-Resume-Analyzer/AI-Resume-Analyzer.py b/utilities/AI-Resume-Analyzer/AI-Resume-Analyzer.py
deleted file mode 100644
index 145a7b8b..00000000
--- a/utilities/AI-Resume-Analyzer/AI-Resume-Analyzer.py
+++ /dev/null
@@ -1,185 +0,0 @@
-import re
-import nltk
-from nltk.corpus import stopwords
-from nltk.tokenize import word_tokenize
-from nltk.util import ngrams
-
-
-def _ensure_nltk_resources() -> None:
- """Download NLTK resources only if not already present."""
- resources = {
- 'punkt': 'tokenizers/punkt',
- 'punkt_tab': 'tokenizers/punkt_tab',
- 'stopwords': 'corpora/stopwords',
- }
- for name, path in resources.items():
- try:
- nltk.data.find(path)
- except LookupError:
- nltk.download(name, quiet=True)
-
-
-def analyze_resume(resume_text: str) -> dict:
- _ensure_nltk_resources()
-
- resume = resume_text.lower()
-
- # Pre-process to protect C++, C#, and .NET from NLTK tokenization
- PROTECTED = {"c++": "cpplang", "c#": "csharplang", ".net": "dotnetlang"}
- for raw, placeholder in PROTECTED.items():
- resume = resume.replace(raw, placeholder)
-
- # NLP processing
- words = word_tokenize(resume)
-
- # Restore protected tokens
- words = [PROTECTED.get(w, w) for w in words]
-
- stop_words = set(stopwords.words('english'))
- KEEP_AS_IS = {"c++", "c#", ".net"}
-
- clean_words = [
- w for w in words
- if (w.isalnum() or w in KEEP_AS_IS) and w not in stop_words
- ]
-
- # Phrase tokens
- bigrams = [' '.join(bg) for bg in ngrams(clean_words, 2)]
-
- # Skill aliases
- skill_aliases = {
- # Languages
- "py": "python",
- "js": "javascript",
- "ts": "typescript",
- "cpp": "c++",
- # Databases
- "postgres": "postgresql",
- "mongo": "mongodb",
- # AI / ML
- "ml": "machine learning",
- "dl": "deep learning",
- "cv": "computer vision",
- # Frameworks
- "reactjs": "react",
- "react.js": "react",
- "nodejs": "node.js",
- "expressjs": "express",
- # Cloud / DevOps
- "aws": "amazon web services",
- "gcp": "google cloud platform",
- # Tools
- "gitlab": "git",
- "github": "git",
- }
-
- normalized_words = [
- skill_aliases.get(word, word)
- for word in clean_words
- ]
-
- normalized_bigrams = [
- skill_aliases.get(bg, bg)
- for bg in bigrams
- ]
-
- # Skills
- skills = [
- "python", "java", "c++", "c#", ".net", "django", "sql",
- "machine learning", "html", "css",
- "javascript", "communication", "teamwork"
- ]
-
- found_skills = []
- for skill in skills:
- if skill in normalized_words or skill in normalized_bigrams:
- found_skills.append(skill)
-
- # Resume section parsing
- education_pattern = r"(education|academic|qualification)"
- experience_pattern = r"(experience|internship|work experience)"
- project_pattern = r"(projects|project)"
-
- edu_found = bool(re.search(education_pattern, resume))
- exp_found = bool(re.search(experience_pattern, resume))
- project_found = bool(re.search(project_pattern, resume))
-
- # Improved score system
- score = 0
-
- # Skill score
- score += min(len(found_skills) * 8, 40)
-
- # Section score
- if edu_found:
- score += 20
- if exp_found:
- score += 25
- if project_found:
- score += 15
-
- # Resume quality bonus
- if len(found_skills) >= 5:
- score += 10
-
- if score > 100:
- score = 100
-
- missing_skills = [s for s in skills if s not in found_skills]
-
- return {
- "found_skills": found_skills,
- "score": score,
- "edu_found": edu_found,
- "exp_found": exp_found,
- "project_found": project_found,
- "missing_skills": missing_skills,
- "all_skills": skills,
- }
-
-
-def main() -> None:
- print("=== π€ AI Resume Analyzer (Advanced NLP Version) ===")
-
- resume = input("\nPaste your resume text:\n")
- if not resume.strip():
- print("β Error: Resume cannot be empty.")
- return
-
- result = analyze_resume(resume)
-
- # OUTPUT
- print("\nπ === Analysis Result ===")
-
- print("\nβ
Skills detected:")
- for s in result["found_skills"]:
- print("-", s)
-
- score = result["score"]
- print(f"\nπ― ATS Score: {score}/100")
-
- # Strength
- if score >= 80:
- print("β
Resume Strength: Excellent")
- elif score >= 50:
- print("π Resume Strength: Good")
- else:
- print("β οΈ Needs Improvement")
-
- # Suggestions
- print("\nπ‘ Recommendations:")
- for m in result["missing_skills"][:5]:
- print("-", m)
-
- # Section check
- print("\nπ Resume Sections:")
- print("- Education:", "β
Found" if result["edu_found"] else "β Missing")
- print("- Experience:", "β
Found" if result["exp_found"] else "β Missing")
- print("- Projects:", "β
Found" if result["project_found"] else "β Missing")
-
- # Final
- print("\nπ Analysis Completed")
-
-
-if __name__ == "__main__":
- main()
diff --git a/utilities/AI-Resume-Analyzer/README.MD b/utilities/AI-Resume-Analyzer/README.MD
deleted file mode 100644
index cca36ba7..00000000
--- a/utilities/AI-Resume-Analyzer/README.MD
+++ /dev/null
@@ -1,50 +0,0 @@
-# π€ AI Resume Analyzer (NLP Powered)
-
-An intelligent resume analysis tool built using Python and NLTK that extracts skills, evaluates resumes, and provides ATS scoring with improvement suggestions.
-
----
-
-## π Features
-
-- π§ NLP-based text processing using NLTK
-- π― Skill detection from resume text
-- π ATS (Applicant Tracking System) score generation
-- π Resume strength evaluation
-- π‘ Smart improvement suggestions
-- π Education & experience detection
-
----
-
-## π οΈ Tech Stack
-
-- Python π
-- NLTK (Natural Language Toolkit)
-
----
-
-## π Project Structure
-AI-Resume-Analyzer/
-βββ main.py
-βββ requirements.txt
-βββ README.md
-
-
----
-
-## βοΈ Installation & Setup
-
-### 1οΈβ£ Install dependencies
-
-```bash
-pip install -r requirements.txt
-
-run project- python main.py
-
----NLTK Setup (FIRST TIME ONLY)---
-
-Run this once in Python:
-
-import nltk
-nltk.download('punkt_tab')
-nltk.download('punkt')
-nltk.download('stopwords')
\ No newline at end of file
diff --git a/utilities/AI-Resume-Analyzer/requirements.txt b/utilities/AI-Resume-Analyzer/requirements.txt
deleted file mode 100644
index 6fa2de44..00000000
--- a/utilities/AI-Resume-Analyzer/requirements.txt
+++ /dev/null
@@ -1 +0,0 @@
-nltk
\ No newline at end of file
diff --git a/web-app/assets/banners/reverse-hangman.webp b/web-app/assets/banners/reverse-hangman.webp
deleted file mode 100644
index 841df72d..00000000
Binary files a/web-app/assets/banners/reverse-hangman.webp and /dev/null differ
diff --git a/web-app/css/styles.css b/web-app/css/styles.css
index f376d128..a3130834 100644
--- a/web-app/css/styles.css
+++ b/web-app/css/styles.css
@@ -1052,28 +1052,8 @@ main>.hero-section:has(.hero-code-snippets) {
animation: pulseKicker 2s ease-in-out infinite;
}
-@keyframes pulseKicker {
-
- 0%,
- 100% {
- opacity: 1;
- transform: scale(1.1);
- }
-
- 50% {
- opacity: 0.4;
- transform: scale(0.85);
- }
-}
-
-[data-theme="dark"] .hero-kicker {
- background: linear-gradient(135deg,
- rgba(106, 191, 141, 0.18),
- rgba(242, 194, 108, 0.1));
- border-color: rgba(255, 243, 224, 0.1);
- color: #d4b48a;
-}
-
+/* ββ Hero Badge Row (Glassified Kicker & Status Button) ββββββββ */
+.hero-kicker,
.hero-status {
display: inline-flex;
align-items: center;
diff --git a/web-app/generate_banners.py b/web-app/generate_banners.py
index bc6b0aad..b86721fa 100644
--- a/web-app/generate_banners.py
+++ b/web-app/generate_banners.py
@@ -468,18 +468,6 @@ def draw_o(ox, oy):
x = 220 + i * 80
y = 175
v_draw.ellipse([x, y, x + 65, y + 65], fill=col, outline=(255,255,255,100), width=2)
- elif "resume" in n_lower or "analyzer" in n_lower:
- # Resume analyzer dashboard
- v_draw.rounded_rectangle([250, 100, 550, 350], radius=24, fill=(255,255,255,12), outline=color_accent, width=3)
- v_draw.rounded_rectangle([285, 135, 515, 315], radius=18, fill=(255,255,255,8), outline=(255,255,255,50), width=2)
- v_draw.polygon([(332, 135), (515, 135), (515, 190)], fill=(255,255,255,20), outline=color_accent)
- v_draw.ellipse([280, 145, 390, 255], outline=color_accent, width=8)
- v_draw.text((335, 200), "82%", fill=color_accent, anchor="mm")
- for i, w in enumerate([90, 75, 65]):
- y = 275 + i * 18
- v_draw.rounded_rectangle([410, y, 410 + w, y + 10], radius=4, fill=color_accent)
- v_draw.text((430, 165), "AI RESUME", fill=color_accent, anchor="lm")
- v_draw.text((430, 188), "ANALYZER", fill=(255,255,255), anchor="lm")
elif "caesar" in n_lower:
# Cipher wheel and shifting letters
cx, cy = 400, 225
@@ -720,7 +708,6 @@ def draw_o(ox, oy):
("Number Converter", "utilities", "number-converter.webp"),
("Typing Speed Tester", "utilities", "typing-speed-tester.webp"),
("Color Palette Suggestor", "utilities", "color-palette.webp"),
- ("AI Resume Analyzer", "utilities", "resume-analyzer.webp"),
("Caesar Cipher", "utilities", "caesar-cipher.webp"),
("Unit Converter", "utilities", "unit-converter.webp"),
("Budget Tracker", "utilities", "budget-tracker.webp"),
diff --git a/web-app/index.html b/web-app/index.html
index be026e0d..ea257c14 100644
--- a/web-app/index.html
+++ b/web-app/index.html
@@ -508,7 +508,6 @@
Stay Updated
-
@@ -865,13 +864,6 @@ Stay Updated
desc: "Generate color palettes",
tags: "utility,design",
},
- {
- project: "resume-analyzer",
- title: "Resume Analyzer",
- category: "utilities",
- desc: "Analyze and improve resumes",
- tags: "utility,career",
- },
{
project: "calculator",
title: "Scientific Graphing Calculator",
diff --git a/web-app/js/main.js b/web-app/js/main.js
index 80e8ba7d..15e0f7b4 100644
--- a/web-app/js/main.js
+++ b/web-app/js/main.js
@@ -1354,8 +1354,7 @@ document.addEventListener("DOMContentLoaded", function () {
// Info button already exists, skip injection
console.log('βΉοΈ Info button already exists for', name);
} else {
- // Look for any heading element
- var firstHeading = projectContent.querySelector("h2, h3, .resume-analyzer-copy h2, .pet-title");
+ var firstHeading = projectContent.querySelector("h2, h3, .pet-title");
// Special case for Tic Tac Toe - look for the heading inside project-content
if (!firstHeading) {
diff --git a/web-app/js/modules/modal.js b/web-app/js/modules/modal.js
index 54e2e0c9..323583b1 100644
--- a/web-app/js/modules/modal.js
+++ b/web-app/js/modules/modal.js
@@ -143,7 +143,7 @@ export function openProjectSafe(name, trigger) {
const projectContent = modalBody.querySelector(".project-content");
if (projectContent) {
let firstHeading = projectContent.querySelector(
- "h2, h3, .resume-analyzer-copy h2, .pet-title"
+ "h2, h3, .pet-title"
);
if (!firstHeading) {
firstHeading = projectContent.querySelector(
diff --git a/web-app/js/projects.js b/web-app/js/projects.js
index fe6ece08..0bf144bf 100644
--- a/web-app/js/projects.js
+++ b/web-app/js/projects.js
@@ -25,7 +25,6 @@ function getProjectHTML(projectName) {
'matrix-calculator': () => getMatrixCalculatorHTML(),
'sudoku-game': getSudokuGameHTML(),
'unit-converter': getUnitConverterHTML(),
- 'resume-analyzer': getResumeAnalyzerHTML(),
'reverse-hangman': () => getReverseHangmanHTML(),
'budget-tracker': getBudgetTrackerHTML(),
'snake-game': getSnakeGameHTML(),
@@ -465,15 +464,6 @@ const projectInstructions = {
"Copy the secure password"
]
},
- "resume-analyzer": {
- title: "π How to Use AI Resume Analyzer",
- steps: [
- "Upload your resume (PDF, DOC, or TXT)",
- "Click 'Analyze Resume'",
- "View your ATS score and keyword matches",
- "Check suggestions to improve your resume"
- ]
- },
"typing-speed-tester": {
title: "β¨οΈ How to Use Typing Speed Tester",
steps: [
diff --git a/web-app/js/projects/resume-analyzer.js b/web-app/js/projects/resume-analyzer.js
deleted file mode 100644
index a70632eb..00000000
--- a/web-app/js/projects/resume-analyzer.js
+++ /dev/null
@@ -1,355 +0,0 @@
-// ============================================
-// AI RESUME ANALYZER
-// Logic ported from utilities/AI-Resume-Analyzer/AI-Resume-Analyzer.py
-// No API calls β pure client-side text analysis.
-// ============================================
-
-function getResumeAnalyzerHTML() {
- return `
-
-
-
-
GSSoC Utility
-
AI Resume Analyzer
-
Upload a resume and get a quick ATS-style snapshot with keyword, structure, and formatting feedback.
-
-
No backend required
-
-
-
-
-
-
-
-
Drag & drop or click to choose a resume (.pdf, .doc, .docx, .txt)
-
-
-
-
-
-
-
-
-
-
- `;
-}
-
-function initResumeAnalyzer() {
- const analyzeBtn = document.getElementById('analyzeBtn');
- const resumeInput = document.getElementById('resumeInput');
- const fileStatus = document.getElementById('resumeFileStatus');
- const uploadIcon = document.getElementById('resumeUploadIcon');
- const ats = document.getElementById('ats');
- const bottomSection = document.getElementById('bottomSection');
-
- if (!analyzeBtn || !resumeInput) return;
-
- function loadPdfJs() {
- return new Promise((resolve, reject) => {
- if (window.pdfjsLib) { resolve(window.pdfjsLib); return; }
- const script = document.createElement('script');
- script.src = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js';
- script.onload = () => {
- window.pdfjsLib.GlobalWorkerOptions.workerSrc =
- 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
- resolve(window.pdfjsLib);
- };
- script.onerror = () => reject(new Error('Failed to load PDF.js'));
- document.head.appendChild(script);
- });
- }
-
- async function readPdfAsText(file) {
- const pdfjsLib = await loadPdfJs();
- const arrayBuffer = await file.arrayBuffer();
- const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
- const pageTexts = [];
- for (let i = 1; i <= pdf.numPages; i++) {
- const page = await pdf.getPage(i);
- const content = await page.getTextContent();
- pageTexts.push(content.items.map(item => item.str).join(' '));
- }
- return pageTexts.join('\n');
- }
-
- const SKILLS = [
- "python", "java", "c++", "c#", ".net", "django", "sql",
- "machine learning", "html", "css", "javascript",
- "communication", "teamwork"
- ];
-
- const ALIASES = {
- "py": "python",
- "js": "javascript",
- "ts": "typescript",
- "cpp": "c++",
- "postgres": "postgresql",
- "mongo": "mongodb",
- "ml": "machine learning",
- "dl": "deep learning",
- "cv": "computer vision",
- "reactjs": "react",
- "react.js": "react",
- "nodejs": "node.js",
- "expressjs": "express",
- "aws": "amazon web services",
- "gcp": "google cloud platform",
- "gitlab": "git",
- "github": "git",
- };
-
- const EDU_RE = /education|academic|qualification/i;
- const EXP_RE = /experience|internship|work experience/i;
- const PROJ_RE = /projects?/i;
-
- resumeInput.addEventListener('change', () => {
- const file = resumeInput.files[0];
- if (!file) return;
-
- fileStatus.textContent = `β
File selected: ${file.name}`;
- fileStatus.style.color = 'var(--accent)';
- uploadIcon.style.color = 'var(--accent)';
- uploadIcon.className = 'fa-solid fa-circle-check resume-upload-icon';
- });
-
- const dropZone = document.getElementById('resumeDropZone');
- if (dropZone) {
- dropZone.addEventListener('dragover', e => {
- e.preventDefault();
- dropZone.style.borderColor = 'var(--accent)';
- });
- dropZone.addEventListener('dragleave', () => {
- dropZone.style.borderColor = '';
- });
- dropZone.addEventListener('drop', e => {
- e.preventDefault();
- dropZone.style.borderColor = '';
- const file = e.dataTransfer.files[0];
- if (file) {
- const dt = new DataTransfer();
- dt.items.add(file);
- resumeInput.files = dt.files;
- fileStatus.textContent = `β
File selected: ${file.name}`;
- fileStatus.style.color = 'var(--accent)';
- uploadIcon.className = 'fa-solid fa-circle-check resume-upload-icon';
- }
- });
- }
-
- analyzeBtn.addEventListener('click', async () => {
- if (!resumeInput.files.length) {
- fileStatus.textContent = 'β οΈ Please upload a resume file first!';
- fileStatus.style.color = 'var(--danger-color, #ef4444)';
- return;
- }
-
- const file = resumeInput.files[0];
- const ext = file.name.split('.').pop().toLowerCase();
-
- analyzeBtn.textContent = 'Analyzingβ¦ β³';
- analyzeBtn.disabled = true;
-
- try {
- let text = '';
-
- if (ext === 'pdf') {
- fileStatus.textContent = 'π Extracting text from PDFβ¦';
- fileStatus.style.color = 'var(--accent)';
- text = await readPdfAsText(file);
- //console.log(`[ResumeAnalyzer] PDF extracted (${text.length} chars):`, text);
- if (!text || text.trim().length < 30) {
- console.warn('[ResumeAnalyzer] Text too short β likely a scanned/image PDF.');
- fileStatus.textContent =
- `β οΈ "${file.name}" appears to be a scanned/image-only PDF. ` +
- `For best results, export your resume as a text-based PDF or .txt file.`;
- fileStatus.style.color = 'var(--warning-color, #f59e0b)';
- return;
- }
- } else if (ext === 'doc' || ext === 'docx') {
- text = await readFileAsText(file);
- //console.log(`[ResumeAnalyzer] DOCX raw text (${text.length} chars):`, text);
- if (!text || text.trim().length < 30) {
- console.warn('[ResumeAnalyzer] DOCX text too short β binary file.');
- fileStatus.textContent =
- `β οΈ "${file.name}" could not be read as plain text in the browser. ` +
- `For best results, export your resume as a .pdf or .txt file and re-upload.`;
- fileStatus.style.color = 'var(--warning-color, #f59e0b)';
- return;
- }
- } else {
- text = await readFileAsText(file);
- //console.log(`[ResumeAnalyzer] TXT extracted (${text.length} chars):`, text);
- }
-
- //console.log('[ResumeAnalyzer] Running analysis on text:', text);
- runAnalysis(text);
- } catch (err) {
- fileStatus.textContent = `β Could not read file: ${err.message}`;
- fileStatus.style.color = 'var(--danger-color, #ef4444)';
- } finally {
- analyzeBtn.textContent = 'Analyze Resume π';
- analyzeBtn.disabled = false;
- }
- });
-
- function readFileAsText(file) {
- return new Promise((resolve, reject) => {
- const reader = new FileReader();
- reader.onload = e => resolve(e.target.result);
- reader.onerror = () => reject(new Error('Read error'));
- reader.readAsText(file);
- });
- }
-
- function runAnalysis(rawText) {
- const text = rawText.toLowerCase();
-
- const tokenRe = /c\+\+|c#|\.net|[a-z0-9]+/gi;
- const tokens = (text.match(tokenRe) || []).map(t => t.toLowerCase());
-
- const normalised = tokens.map(t => ALIASES[t] ?? t);
-
- const bigrams = [];
- for (let i = 0; i < normalised.length - 1; i++) {
- bigrams.push(normalised[i] + ' ' + normalised[i + 1]);
- }
-
- const foundSkills = SKILLS.filter(
- skill => normalised.includes(skill) || bigrams.includes(skill)
- );
-
- const eduFound = EDU_RE.test(text);
- const expFound = EXP_RE.test(text);
- const projFound = PROJ_RE.test(text);
-
- let score = 0;
- score += Math.min(foundSkills.length * 8, 40); // up to 40
- if (eduFound) score += 20;
- if (expFound) score += 25;
- if (projFound) score += 15;
- if (foundSkills.length >= 5) score += 10; // quality bonus
- score = Math.min(score, 100);
-
- const formattingScore = Math.round(
- ((eduFound ? 1 : 0) + (expFound ? 1 : 0) + (projFound ? 1 : 0)) / 3 * 100
- );
- const keywordScore = Math.min(
- Math.round((foundSkills.length / SKILLS.length) * 100), 100
- );
-
- let strength;
- if (score >= 80) strength = 'β
Resume Strength: Excellent';
- else if (score >= 50) strength = 'π Resume Strength: Good';
- else strength = 'β οΈ Resume Strength: Needs Improvement';
-
- const missingSkills = SKILLS.filter(s => !foundSkills.includes(s));
-
- renderResults({
- score, formattingScore, keywordScore, strength,
- foundSkills, missingSkills,
- eduFound, expFound, projFound
- });
- }
-
- function renderResults({ score, formattingScore, keywordScore, strength,
- foundSkills, missingSkills,
- eduFound, expFound, projFound }) {
- ats.classList.remove('hidden');
- bottomSection.classList.remove('hidden');
-
- document.getElementById('atsScoreDisplay').textContent = score + '%';
- document.getElementById('atsFormattingScore').textContent = `β Formatting Score: ${formattingScore}%`;
- document.getElementById('atsKeywordScore').textContent = `β Keyword Match: ${keywordScore}%`;
- document.getElementById('atsStrength').textContent = strength;
-
- const kwList = document.getElementById('resumeKeywordsList');
- kwList.textContent = '';
-
- const displaySkills = [
- ...foundSkills.map(s => ({ name: s, pct: Math.min(70 + Math.random() * 30 | 0, 100), found: true })),
- ...missingSkills.slice(0, Math.max(0, 5 - foundSkills.length))
- .map(s => ({ name: s, pct: 0, found: false }))
- ].slice(0, 6);
-
- if (displaySkills.length === 0) {
- kwList.innerHTML = 'No matching keywords detected. Try a .txt version of your resume.
';
- } else {
- displaySkills.forEach(({ name, pct, found }) => {
- kwList.innerHTML += `
-
-
${capitalise(name)}${found ? '' : ' β'}
-
-
`;
- });
- }
-
- const suggestionsEl = document.getElementById('resumeSuggestions');
- suggestionsEl.innerHTML = '';
-
- const suggestions = buildSuggestions({ foundSkills, missingSkills, eduFound, expFound, projFound });
- suggestions.forEach(s => {
- suggestionsEl.innerHTML += `
- `;
- });
-
- ats.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
- }
-
- function buildSuggestions({ foundSkills, missingSkills, eduFound, expFound, projFound }) {
- const list = [];
-
- if (eduFound) list.push({ ok: true, text: 'Education section detected' });
- else list.push({ ok: false, text: 'Add an Education section' });
-
- if (expFound) list.push({ ok: true, text: 'Experience section detected' });
- else list.push({ ok: false, text: 'Add a Work Experience / Internship section' });
-
- if (projFound) list.push({ ok: true, text: 'Projects section detected' });
- else list.push({ ok: false, text: 'Add a Projects section' });
-
- if (foundSkills.length >= 5)
- list.push({ ok: true, text: `Strong skill set detected (${foundSkills.length} skills)` });
- else
- list.push({ ok: false, text: 'Add more technical skills' });
-
- if (missingSkills.length > 0)
- list.push({ ok: false, text: `Consider adding: ${missingSkills.slice(0, 3).map(capitalise).join(', ')}` });
-
- list.push({ ok: false, text: 'Use strong action verbs (e.g. "Developed", "Led", "Built")' });
-
- return list.slice(0, 6);
- }
-
- function capitalise(str) {
- return str.charAt(0).toUpperCase() + str.slice(1);
- }
-}
\ No newline at end of file
diff --git a/web-app/projects_registry.json b/web-app/projects_registry.json
index 4c88a0c9..5535901e 100644
--- a/web-app/projects_registry.json
+++ b/web-app/projects_registry.json
@@ -487,21 +487,6 @@
],
"path": "math/Quadratic-Solver/Quadratic-Solver.py"
},
- {
- "name": "AI Resume Analyzer",
- "emoji": "π",
- "category": "utilities",
- "difficulty": "advanced",
- "description": "Analyze resumes using NLTK to extract key skills and suggestions.",
- "keywords": [
- "resume",
- "analyzer",
- "nltk",
- "skills",
- "parser"
- ],
- "path": "utilities/AI-Resume-Analyzer/AI-Resume-Analyzer.py"
- },
{
"name": "Budget Tracker",
"emoji": "π°",
diff --git a/web-app/utilities.html b/web-app/utilities.html
index 2ed4577e..12f05017 100644
--- a/web-app/utilities.html
+++ b/web-app/utilities.html
@@ -370,27 +370,6 @@ Budget Tracker
Track income and expenses, view categories, and persist data locally
-
-

-
-
-
-
-
AI Resume Analyzer
-
- Score resumes with ATS-style keyword and formatting insights!
-
-
-
TSP Visualizer
-