From 57e855c7baf4d2bc1dbfbec3b19e03d905848b65 Mon Sep 17 00:00:00 2001 From: shravanithouta108 Date: Sat, 13 Jun 2026 09:27:44 +0530 Subject: [PATCH 01/45] feat: add Community Activity Score (Issue #138) Closes #138 Signed-off-by: shravanithouta108 --- .github/workflows/compute-activity-scores.yml | 32 ++ agent/scripts/compute-activity-scores.js | 155 +++++ data/community_activity.json | 1 + index.html | 447 +++++--------- src/js/app.js | 544 +++++++----------- src/styles.css | 195 ++----- 6 files changed, 571 insertions(+), 803 deletions(-) create mode 100644 .github/workflows/compute-activity-scores.yml create mode 100644 agent/scripts/compute-activity-scores.js create mode 100644 data/community_activity.json diff --git a/.github/workflows/compute-activity-scores.yml b/.github/workflows/compute-activity-scores.yml new file mode 100644 index 0000000000..c531b2cfb1 --- /dev/null +++ b/.github/workflows/compute-activity-scores.yml @@ -0,0 +1,32 @@ +name: Compute Community Activity Scores + +on: + schedule: + - cron: '0 0 * * *' # Daily at midnight + workflow_dispatch: # Manual trigger + +jobs: + compute-scores: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Setup Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.2.0 + with: + node-version: '18' + + - name: Run activity score computation + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: node agent/scripts/compute-activity-scores.js + + - name: Commit updated scores + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git add data/community_activity.json + git diff --staged --quiet || git commit -m "chore: update community activity scores" + git push \ No newline at end of file diff --git a/agent/scripts/compute-activity-scores.js b/agent/scripts/compute-activity-scores.js new file mode 100644 index 0000000000..52bd86d0e3 --- /dev/null +++ b/agent/scripts/compute-activity-scores.js @@ -0,0 +1,155 @@ +// @ts-check +/* eslint-env node */ +// Computes community activity scores for all GSoC orgs +// Run via GitHub Actions daily + +const fs = require('node:fs'); +const path = require('node:path'); +const GITHUB_TOKEN = process.env.GITHUB_TOKEN; +const OUTPUT_FILE = path.join(__dirname, '../../data/community_activity.json'); + +if (!GITHUB_TOKEN) { + console.error('โŒ GITHUB_TOKEN is required'); + process.exit(1); +} + +const headers = { + 'Authorization': `token ${GITHUB_TOKEN}`, + 'Accept': 'application/vnd.github.v3+json', + 'User-Agent': 'gsoc-org-finder' +}; + +// Load orgs from org.js +const orgDataRaw = fs.readFileSync( + path.join(__dirname, '../../src/js/org.js'), 'utf8' +); + +// Extract github field from each org +const githubRepos = []; +const orgMatches = orgDataRaw.matchAll(/name:\s*"([^"]+)"[^}]+github:\s*"([^"]+)"/g); +for (const m of orgMatches) { + const name = m[1]; + const repo = m[2]; + if (repo?.includes('/')) { + githubRepos.push({ name, repo }); + } +} + +async function fetchJSON(url) { + const res = await fetch(url, { headers }); + if (!res.ok) return null; + return res.json(); +} + +function computeScore({ issueResponseDays, commitFrequency, prMergeRate, ideasFreshnessDays, starsGrowth }) { + const issueScore = Math.max(0, 100 - (issueResponseDays * 5)); + const commitScore = Math.min(100, commitFrequency * 10); + const prScore = Math.min(100, prMergeRate * 100); + const ideasScore = Math.max(0, 100 - (ideasFreshnessDays * 0.5)); + const growthScore = Math.min(100, starsGrowth * 2); + + return Math.round( + issueScore * 0.3 + + commitScore * 0.2 + + prScore * 0.15 + + ideasScore * 0.2 + + growthScore * 0.15 + ); +} + +function getTier(score) { + if (score >= 80) return { tier: 'very-active', label: '๐ŸŸข Very Active' }; + if (score >= 60) return { tier: 'active', label: '๐Ÿ”ต Active' }; + if (score >= 40) return { tier: 'moderate', label: '๐ŸŸก Moderate' }; + return { tier: 'low', label: '๐Ÿ”ด Low Activity' }; +} + +async function analyzeOrg({ name, repo }) { + try { + const since = new Date(Date.now() - 90 * 86400000).toISOString(); + + const commits = await fetchJSON( + `https://api.github.com/repos/${repo}/commits?since=${since}&per_page=100` + ); + const commitFrequency = Array.isArray(commits) ? commits.length / 90 : 0; + + const issues = await fetchJSON( + `https://api.github.com/repos/${repo}/issues?state=closed&since=${since}&per_page=100` + ); + let issueResponseDays = null; + if (Array.isArray(issues) && issues.length > 0) { + const responseTimes = issues + .filter(i => i.created_at && i.closed_at && !i.pull_request) + .map(i => (+new Date(i.closed_at) - +new Date(i.created_at)) / 86400000); + if (responseTimes.length > 0) { + issueResponseDays = responseTimes.reduce((a, b) => a + b, 0) / responseTimes.length; + } + } + + // Fetch PRs + const prs = await fetchJSON( + `https://api.github.com/repos/${repo}/pulls?state=closed&per_page=100` + ); + let prMergeRate = null; + if (Array.isArray(prs) && prs.length > 0) { + const recentPrs = prs.filter(p => p.closed_at && new Date(p.closed_at) >= new Date(since)); + if (recentPrs.length > 0) { + const merged = recentPrs.filter(p => p.merged_at).length; + prMergeRate = merged / recentPrs.length; + } + } + + const repoData = await fetchJSON(`https://api.github.com/repos/${repo}`); + const starsGrowth = repoData ? Math.min(50, repoData.stargazers_count / 1000) : 0; + + const ideasFreshnessDays = repoData?.pushed_at + ? (Date.now() - new Date(repoData.pushed_at)) / 86400000 + : 60; + + const score = computeScore({ + issueResponseDays: issueResponseDays ?? 14, + commitFrequency, + prMergeRate: prMergeRate ?? 0.5, + ideasFreshnessDays, + starsGrowth + }); + const { tier, label } = getTier(score); + + return { + score, + tier, + label, + signals: { + issueResponseDays: Math.round(issueResponseDays), + commitFrequency: Math.round(commitFrequency * 10) / 10, + prMergeRate: Math.round(prMergeRate * 100), + ideasFreshnessDays: Math.round(ideasFreshnessDays), + starsGrowth: Math.round(starsGrowth * 10) / 10 + }, + lastUpdated: new Date().toISOString() + }; + } catch (e) { + console.error(`Error analyzing ${name}:`, e.message); + return null; + } +} + +async function main() { + console.log(`Analyzing ${githubRepos.length} orgs...`); + const results = {}; + + for (const org of githubRepos) { + console.log(`Processing: ${org.name}`); + const data = await analyzeOrg(org); + if (data) results[org.name] = data; + await new Promise(r => setTimeout(r, 500)); + } + + fs.writeFileSync(OUTPUT_FILE, JSON.stringify(results, null, 2)); + console.log(`โœ… Done! Wrote ${Object.keys(results).length} orgs to community_activity.json`); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/data/community_activity.json b/data/community_activity.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/data/community_activity.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/index.html b/index.html index 4a79ce1cc8..ef81fb4022 100644 --- a/index.html +++ b/index.html @@ -307,11 +307,7 @@ .timeline-grid { display: flex; flex-wrap: wrap; gap: 0.5rem; } .timeline-grid span { font-size: 0.75rem; font-weight: 500; color: #666; background: #fff; border: 1px solid #eee; padding: 0.3rem 0.7rem; border-radius: 0.5rem; } - .timeline-grid span.current-year { background: #fffbeb; color: #92400e; border-color: #fbbf24; font-weight: 700; } - .timeline-grid a.timeline-year-link { font-size: 0.75rem; font-weight: 500; color: #666; background: #fff; border: 1px solid #eee; padding: 0.3rem 0.7rem; border-radius: 0.5rem; text-decoration: none; display: inline-block; cursor: pointer; transition: border-color 0.15s, color 0.15s, background 0.15s; } - .timeline-grid a.timeline-year-link:hover { border-color: var(--orange); color: var(--orange); background: var(--orange-pale); } - .timeline-grid a.timeline-year-link.current-year { background: #fffbeb; color: #92400e; border-color: #fbbf24; font-weight: 700; } - .timeline-grid a.timeline-year-link.current-year:hover { border-color: #92400e; background: #fef3c7; color: #78350f; } + .timeline-grid span.current-year { background: #fffbeb; color: #d97706; border-color: #fef3c7; font-weight: 700; } .modal-footer { padding: 2rem 3rem 3rem; background: #f9f9f9; display: flex; gap: 1rem; border-top: 1px solid #eee; flex-shrink: 0; } .modal-cta { flex: 1; padding: 1rem; border-radius: 1rem; font-size: 0.8rem; font-weight: 800; text-align: center; text-transform: uppercase; letter-spacing: 0.05em; transition: all 0.2s; } @@ -445,6 +441,26 @@ .complexity-badge.beginner { background: #dcfce7; color: #166534; } .complexity-badge.intermediate { background: #fef9c3; color: #854d0e; } .complexity-badge.advanced { background: #fee2e2; color: #991b1b; } + + /* Activity Badges */ +.activity-badge { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 2px 8px; + border-radius: 99px; + font-size: 0.65rem; + font-weight: 700; + letter-spacing: 0.03em; +} +.tier-very-active { background: #dcfce7; color: #166534; } +.tier-active { background: #dbeafe; color: #1d4ed8; } +.tier-moderate { background: #fef9c3; color: #854d0e; } +.tier-low { background: #fee2e2; color: #991b1b; } +.dark .tier-very-active { background: #14532d; color: #86efac; } +.dark .tier-active { background: #1e3a5f; color: #93c5fd; } +.dark .tier-moderate { background: #422006; color: #fcd34d; } +.dark .tier-low { background: #450a0a; color: #fca5a5; } /* Bookmark Button */ .bookmark-btn { transition: all 0.2s; font-size: 1.25rem; } @@ -912,13 +928,6 @@ stroke-dasharray: none; stroke-dashoffset: 0; } - .section-scroll-btn { - transition: none !important; - } - .section-scroll-btn:hover, - .section-scroll-btn:active { - transform: none !important; - } } /* Smooth fading for search bar active animation on focus */ @@ -952,18 +961,9 @@ align-items: center; justify-content: center; box-shadow: 0 8px 24px rgba(157, 67, 0, 0.35); - transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); - overflow: hidden; -} -.section-scroll-btn:hover { - transform: translateY(-2px); - box-shadow: 0 12px 32px rgba(157, 67, 0, 0.45); -} -.section-scroll-btn:active { - transform: translateY(1px) scale(0.92); - box-shadow: 0 4px 12px rgba(157, 67, 0, 0.2); - filter: brightness(0.95); + transition: all 0.25s ease; } +.section-scroll-btn:hover { transform: translateY(-2px); } #nextSectionBtn { bottom: calc(var(--section-fab-bottom) + env(safe-area-inset-bottom)); } #prevSectionBtn { bottom: calc(var(--section-fab-bottom) + var(--section-fab-size) + var(--section-fab-gap) + env(safe-area-inset-bottom)); @@ -977,25 +977,6 @@ .dark .section-scroll-btn:hover { box-shadow: 0 12px 32px rgba(194, 65, 12, 0.45); } -.dark .section-scroll-btn:active { - transform: translateY(1px) scale(0.92); - box-shadow: 0 4px 12px rgba(194, 65, 12, 0.2); - filter: brightness(0.95); -} - -/* Dark mode - Section scroll indicator */ -.dark .section-scroll-indicator { - background: rgba(24, 24, 27, 0.88); - border-color: rgba(249, 115, 22, 0.25); - color: #fb923c; -} -.dark .section-scroll-indicator-dot { - background: linear-gradient(135deg, #fb923c, #f97316); - box-shadow: 0 0 0 4px rgba(249, 115, 22, 0.12); -} -.dark .section-scroll-indicator-value { - color: #e4e4e7; -} .section-scroll-btn:disabled { opacity: 0.3; @@ -1003,44 +984,6 @@ pointer-events: none; } -/* Custom ripple effect inside scroll buttons */ -.section-scroll-btn .click-ripple { - position: absolute; - border-radius: 50%; - transform: scale(0); - animation: ripple-animation 0.5s cubic-bezier(0.1, 0.8, 0.3, 1); - background-color: rgba(255, 255, 255, 0.45); - pointer-events: none; -} - -@keyframes ripple-animation { - to { - transform: scale(2.5); - opacity: 0; - } -} - -/* Icon animations inside scroll buttons on click */ -@keyframes bounce-down { - 0% { transform: translateY(0); } - 30% { transform: translateY(4px); } - 70% { transform: translateY(-2px); } - 100% { transform: translateY(0); } -} -@keyframes bounce-up { - 0% { transform: translateY(0); } - 30% { transform: translateY(-4px); } - 70% { transform: translateY(2px); } - 100% { transform: translateY(0); } -} - -.section-scroll-btn.bounce-down-active .material-symbols-outlined { - animation: bounce-down 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); -} -.section-scroll-btn.bounce-up-active .material-symbols-outlined { - animation: bounce-up 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); -} - .section-scroll-indicator { position: fixed; right: calc(var(--section-fab-right) + env(safe-area-inset-right)); @@ -1170,13 +1113,12 @@ @@ -1315,9 +1257,10 @@

- +
@@ -1361,6 +1304,44 @@

2026 Timeline

+ +
+
+ + + + + + + +
+
+ + + + + + + + + + + + +
+
+ No languages selected +
+
+ + + +
+
@@ -1370,42 +1351,6 @@

Browse and filter GSoC 2026 organizations by tech stack, difficulty level, and interests to discover projects that match your skills and goals.

-
-
- - - - - - -
-
- - - - - - - - - - - - -
-
- No languages selected -
-
- - - -
-

Showing 184 of 184 organizations

@@ -1435,6 +1380,7 @@

Competition (Chill First) +

-
+
Last updated: pending refresh - +
@@ -1914,10 +1860,25 @@

Join Discord

- - - - + + -
Participation Timeline ยท click a year to explore its projects
+
Participation Timeline
+
Mentors & Contact
@@ -2110,9 +2072,18 @@

@@ -4965,6 +4878,7 @@

Keyboard Shortcuts

}); + @@ -5093,9 +5007,15 @@

Keyboard Shortcuts

function scrollToSectionIndex(index) { const targetSection = sections[index]; + if (!targetSection) return; - // Use scrollIntoView to respect scroll-margin-top defined in CSS - targetSection.scrollIntoView({ behavior: "smooth", block: "start" }); + + const targetTop = Math.max(0, targetSection.offsetTop - getSectionDetectionOffset()); + + window.scrollTo({ + top: targetTop, + behavior: "smooth" + }); } function updateSectionIndicator() { @@ -5155,32 +5075,15 @@

Keyboard Shortcuts

}); } - let isNavClicking = false; - let navClickTimer = null; - document.querySelectorAll(".desktop-nav-link").forEach(link => { - link.addEventListener("click", (e) => { - e.preventDefault(); // stop browser anchor scroll - isNavClicking = true; - clearTimeout(navClickTimer); + link.addEventListener("click", () => { setCurrentSectionById(link.dataset.section); - scrollToSectionIndex(currentSection); - navClickTimer = setTimeout(() => { - isNavClicking = false; - }, 1500); }); }); document.querySelectorAll(".mobile-menu-link").forEach(link => { - link.addEventListener("click", (e) => { - e.preventDefault(); // stop browser anchor scroll - isNavClicking = true; - clearTimeout(navClickTimer); + link.addEventListener("click", () => { setCurrentSectionById(link.dataset.section); - scrollToSectionIndex(currentSection); - navClickTimer = setTimeout(() => { - isNavClicking = false; - }, 1500); if (typeof globalThis.toggleMenu === "function") { globalThis.toggleMenu(); @@ -5189,104 +5092,26 @@

Keyboard Shortcuts

}); function updateCurrentSection() { - if (isNavClicking) return; - const offset = getSectionDetectionOffset() + 10; - let found = 0; - let closestDistance = Infinity; - sections.forEach((section, index) => { - const rect = section.getBoundingClientRect(); - if (rect.top <= offset) { - const distance = offset - rect.top; - if (distance < closestDistance) { - closestDistance = distance; - found = index; - } - } - }); - currentSection = found; - updateNavState(); - } - - const visibleSections = new Set(); + const scrollPos = window.scrollY + getSectionDetectionOffset(); - const observer = new IntersectionObserver((entries) => { - if (isNavClicking) return; - entries.forEach(entry => { - const index = sections.indexOf(entry.target); - if (index === -1) return; - if (entry.isIntersecting) { - visibleSections.add(index); - } else { - visibleSections.delete(index); - } - }); - if (visibleSections.size === 0) { - return; - } - currentSection = Math.min(...visibleSections); - updateNavState(); - }, { - rootMargin: `-${(mainHeader?.clientHeight || 0) + 10}px 0px -60% 0px`, - threshold: 0 + sections.forEach((section, index) => { + if (scrollPos >= section.offsetTop) currentSection = index; }); - sections.forEach(section => observer.observe(section)); - window.addEventListener("resize", updateCurrentSection); - updateCurrentSection(); - - const nextBtn = document.getElementById("nextSectionBtn"); - const prevBtn = document.getElementById("prevSectionBtn"); - - function addClickFeedback(btn, direction) { - if (!btn) return; - btn.addEventListener("click", (e) => { - const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches; - - // Icon bounce animation - const animClass = direction === "up" ? "bounce-up-active" : "bounce-down-active"; - if (!prefersReducedMotion) { - btn.classList.add(animClass); - } - - // Create ripple effect - if (!prefersReducedMotion) { - const ripple = document.createElement("span"); - ripple.classList.add("click-ripple"); - const rect = btn.getBoundingClientRect(); - const size = Math.max(rect.width, rect.height); - ripple.style.width = ripple.style.height = `${size}px`; - - const isKeyboardClick = e.detail === 0; - const x = (!isKeyboardClick && e.clientX !== undefined && e.clientX !== null) ? (e.clientX - rect.left - size / 2) : (rect.width / 2 - size / 2); - const y = (!isKeyboardClick && e.clientY !== undefined && e.clientY !== null) ? (e.clientY - rect.top - size / 2) : (rect.height / 2 - size / 2); - ripple.style.left = `${x}px`; - ripple.style.top = `${y}px`; - - btn.appendChild(ripple); - - ripple.addEventListener("animationend", () => { - ripple.remove(); - }); - - btn.addEventListener("animationend", (event) => { - if (event.animationName === "bounce-up" || event.animationName === "bounce-down") { - btn.classList.remove(animClass); - } - }, { once: true }); - } - }); + updateNavState(); } - addClickFeedback(nextBtn, "down"); - addClickFeedback(prevBtn, "up"); + window.addEventListener("scroll", updateCurrentSection, { passive: true }); + window.addEventListener("resize", updateCurrentSection); + updateCurrentSection(); - nextBtn?.addEventListener("click", () => { + document.getElementById("nextSectionBtn")?.addEventListener("click", () => { if (currentSection < sections.length - 1) { scrollToSectionIndex(currentSection + 1); } }); - prevBtn?.addEventListener("click", () => { + document.getElementById("prevSectionBtn")?.addEventListener("click", () => { if (currentSection > 0) { scrollToSectionIndex(currentSection - 1); } else { diff --git a/src/js/app.js b/src/js/app.js index 8a51a0ce45..7eed97572c 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -466,21 +466,59 @@ function cleanCache() { } cleanCache(); -async function checkAPI() { +// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• +// COMMUNITY ACTIVITY DATA +// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• +let communityActivity = {}; // will hold the parsed community_activity.json + +async function loadCommunityActivity() { try { - const r = await fetch(`${API}?repo=django/django`); - const banner = document.getElementById('apiBanner'); - if (!banner) return; - if (r.ok) { - banner.className = 'api-banner api-ok'; - document.getElementById('apiStrong').textContent = 'โœ“ GitHub API Connected'; - document.getElementById('apiText').textContent = 'Live stats (stars, forks, good first issues) available for all visitors.'; - const fetchBtn = document.getElementById('fetchBtn'); - if (fetchBtn) fetchBtn.style.display = 'flex'; - } else { - banner.className = 'api-banner api-warn'; - document.getElementById('apiStrong').textContent = 'โš  API Error'; - document.getElementById('apiText').textContent = 'Add GITHUB_TOKEN in Vercel dashboard and redeploy.'; + const bust = new Date().toISOString().slice(0, 10); + const res = await fetch('/data/community_activity.json?v=' + bust); + if (!res.ok) return; + communityActivity = await res.json(); + applyFilters(); + } catch (e) { + console.warn('community_activity.json not yet available:', e); + } +} + +function getCommunityBadge(org) { + if (!org.github) return ''; + const d = communityActivity[org.name]; + if (!d || d.score === undefined) return ''; // no data โ†’ hide badge entirely + const map = { + 'very-active': { dot: '๐ŸŸข', label: 'Very Active', cls: 'bca-very' }, + 'active': { dot: '๐Ÿ”ต', label: 'Active', cls: 'bca-active' }, + 'moderate': { dot: '๐ŸŸก', label: 'Moderate', cls: 'bca-mod' }, + 'low': { dot: '๐Ÿ”ด', label: 'Low Activity', cls: 'bca-low' }, + }; + const tier = map[d.tier] || map['low']; + return `${tier.dot} ${tier.label}`; +} + +function getCommunityData(org) { + if (!org.github) return null; + return communityActivity[org.name] || null; +} + +// Expose to global scope for HTML onclick handlers and debugging +globalThis.matchAllLanguages = matchAllLanguages; + +async function checkAPI(){ + try{ + const r=await fetch(`${API}?repo=django/django`); + const banner=document.getElementById('apiBanner'); + if(!banner) return; + if(r.ok){ + banner.className='api-banner api-ok'; + document.getElementById('apiStrong').textContent='โœ“ GitHub API Connected'; + document.getElementById('apiText').textContent='Live stats (stars, forks, good first issues) available for all visitors.'; + document.getElementById('fetchBtn').style.display='flex'; + }else{ + banner.className='api-banner api-warn'; + document.getElementById('apiStrong').textContent='โš  API Error'; + document.getElementById('apiText').textContent='Add GITHUB_TOKEN in Vercel dashboard and redeploy.'; } } catch { const banner = document.getElementById('apiBanner'); @@ -713,6 +751,20 @@ function handleNavigationUp(e) { updateCardFocus(); } +function syncBookmark(name, shouldAdd) { + if (!name) return; + if (shouldAdd) bookmarkedSet.add(name); + else bookmarkedSet.delete(name); + localStorage.setItem('bookmarks', JSON.stringify([...bookmarkedSet])); + refreshOrgGridAfterBookmarkChange(); + renderWatchlist(); + updateAIInsights(); +} + +function renderMentorFinder() { + // Mentor finder is rendered by index.html's inline script +} + function handleGlobalKeydown(e) { if (e.key === 'Escape' && handleEscapeKey(e)) return; @@ -776,15 +828,18 @@ function parseStoredBookmarks() { } } -function syncBookmark(name, shouldAdd) { - if (!name) return; - if (shouldAdd) bookmarkedSet.add(name); - else bookmarkedSet.delete(name); - localStorage.setItem('bookmarks', JSON.stringify([...bookmarkedSet])); - - refreshOrgGridAfterBookmarkChange(); - renderWatchlist(); - updateAIInsights(); +function applySecondarySort(a, b, sortType) { + if(sortType==='years-desc') return b.years - a.years; + if(sortType==='years-asc') return a.years - b.years; + if(sortType==='comp-low') return ['chill','moderate','hot'].indexOf(a.competition) - ['chill','moderate','hot'].indexOf(b.competition); + if(sortType==='stars') return (b._gh?.stars||0) - (a._gh?.stars||0); + if(sortType==='gfi') return (b._gh?.gfi||0) - (a._gh?.gfi||0); + if(sortType==='activity-score') { + const sa = getCommunityData(a)?.score ?? -1; + const sb = getCommunityData(b)?.score ?? -1; + return sb - sa; + } + return a.name.localeCompare(b.name); } globalThis.toggleBookmark = function (e, name) { @@ -872,7 +927,7 @@ function renderWatchlist() { ? safeHTML`${org.name} logo` : safeHTML`corporate_fare`; - item.innerHTML = safeHTML` + item.innerHTML = safeHTML`
${logoHtml}
@@ -888,20 +943,10 @@ function renderWatchlist() {
${topTags}
- - calendar_today - ${String(org.years)}y in GSoC - - - bar_chart - ${org.competition || 'โ€”'} - + ${String(org.years)}y in GSoC + ${org.competition || 'โ€”'}
- + ${rawHTML(getCommunityBadge(org))}
`; @@ -1105,15 +1150,6 @@ function applyFilters() { } } -function applySecondarySort(a, b, sortType) { - if (sortType === 'years-desc') return b.years - a.years; - if (sortType === 'years-asc') return a.years - b.years; - if (sortType === 'comp-low') return ['chill', 'moderate', 'hot'].indexOf(a.competition) - ['chill', 'moderate', 'hot'].indexOf(b.competition); - if (sortType === 'stars') return (b._gh?.stars || 0) - (a._gh?.stars || 0); - if (sortType === 'gfi') return (b._gh?.gfi || 0) - (a._gh?.gfi || 0); - return a.name.localeCompare(b.name); -} - function renderOrgs(reset = true) { const grid = document.getElementById('orgGrid'); const emptyState = document.getElementById('emptyState'); @@ -1196,6 +1232,16 @@ function renderOrgs(reset = true) { document.getElementById('loadMoreContainer').style.display = (visibleCount < filteredOrgs.length) ? 'flex' : 'none'; } +function handleImgError(img, orgName) { + if (!img.dataset.triedClearbit) { + img.dataset.triedClearbit = 'true'; + const domain = orgName.toLowerCase().replace(/[^a-z0-9]/g, '') + '.org'; + img.src = `https://logo.clearbit.com/${domain}`; + } else { + img.style.display = 'none'; + } +} + function attachOrgCardListeners(root) { // Image error fallbacks root.querySelectorAll('img[data-org-name]').forEach(img => { @@ -1246,19 +1292,27 @@ function attachOrgCardListeners(root) { } } -function handleImgError(img, orgName) { - if (img.dataset.triedClearbit) { - img.style.display = 'none'; - const placeholder = img.parentElement.querySelector('.logo-placeholder'); - if (placeholder) { - placeholder.style.display = 'flex'; - placeholder.textContent = orgName.split(' ').map(n => n[0]).join('').substring(0, 2).toUpperCase(); - } +function toggleChip(k){ + const el=document.getElementById('chip-'+k); + if(!el) return; + + if(activeChip===k){ + activeChip=null; + el.classList.remove('bg-orange-600','text-white'); + el.classList.add('bg-surface-container-highest'); } else { - img.dataset.triedClearbit = 'true'; - const domain = orgName.toLowerCase().replace(/[^a-z0-9]/g, '') + '.org'; - img.src = `https://logo.clearbit.com/${domain}`; + if(activeChip){ + const prev=document.getElementById('chip-'+activeChip); + if(prev){ + prev.classList.remove('bg-orange-600','text-white'); + prev.classList.add('bg-surface-container-highest'); + } + } + activeChip=k; + el.classList.add('bg-orange-600','text-white'); + el.classList.remove('bg-surface-container-highest'); } + applyFilters(); } // Global capturing image error event listener to replace inline onerror attributes @@ -1472,61 +1526,34 @@ globalThis.openModal = function (name, triggerElement = null) { ideasBtn.style.display = 'none'; } } - - if (repoBtn) { - if (repoHref) { - repoBtn.href = repoHref; - repoBtn.style.display = 'inline-flex'; - } else { - repoBtn.removeAttribute('href'); - repoBtn.style.display = 'none'; + + // Community Activity section in modal + const mCommunity = document.getElementById('mCommunity'); + if (mCommunity) { + const cd = getCommunityData(org); + if (cd) { + const tierMap = { + 'very-active': '๐ŸŸข Very Active', + 'active': '๐Ÿ”ต Active', + 'moderate': '๐ŸŸก Moderate', + 'low': '๐Ÿ”ด Low Activity', + }; + mCommunity.style.display = 'block'; + mCommunity.innerHTML = ` +
Community Activity
+
+
Overall Score${cd.score} / 100
+
Tier${tierMap[cd.tier] || cd.tier}
+
Avg Issue Response${cd.signals?.issueResponseDays !== null ? escapeHtml(String(cd.signals.issueResponseDays)) + ' days' : 'โ€”'}
+
Commit Frequency (90d)${cd.signals?.commitFrequency !== null ? escapeHtml(String(cd.signals.commitFrequency)) + '/day' : 'โ€”'}
+
PR Merge Rate${cd.signals?.prMergeRate !== null ? escapeHtml(String(cd.signals.prMergeRate)) + '%' : 'โ€”'}
+
Last updated${cd.lastUpdated ? escapeHtml(String(cd.lastUpdated)) : 'โ€”'}
+
`; + } else { + mCommunity.style.display = 'none'; } } - - // Copy Ideas Button - const oldCopy = document.getElementById('mIdeasCopyBtn'); - if (oldCopy) oldCopy.remove(); - if (org.ideas && ideasBtn) { - const copyBtn = document.createElement('button'); - copyBtn.id = 'mIdeasCopyBtn'; - copyBtn.innerHTML = 'content_copy Copy Link'; - copyBtn.className = 'modal-cta modal-repo-link flex items-center justify-center gap-2'; - copyBtn.addEventListener('click', () => { - navigator.clipboard.writeText(org.ideas).then(() => { - copyBtn.innerHTML = 'check_circle Copied!'; - copyBtn.style.background = '#dcfce7'; - copyBtn.style.color = '#166534'; - setTimeout(() => { - copyBtn.innerHTML = 'content_copy Copy Link'; - copyBtn.style.background = ''; - copyBtn.style.color = ''; - }, 2000); - }); - }); - ideasBtn.after(copyBtn); - } - - renderMentorContactSection(org); - openModalElement('orgModal', triggerElement); - - // Lazily retrieve GFIs if missing - if (org.github && (org._gh?.gfi === null || org._gh?.gfi === undefined)) { - const placeholder = document.getElementById('mGfiPlaceholder'); - if (placeholder) placeholder.textContent = 'โ€ฆ'; - fetchGFI(org.github).then(gfi => { - if (gfi !== null) { - if (!org._gh) org._gh = {}; - org._gh.gfi = gfi; - const placeholders = document.querySelectorAll('.metric-card #mGfiPlaceholder'); - placeholders.forEach(p => p.textContent = fmt(gfi)); - renderOrgs(false); - renderCompareModal(); - } else { - const placeholders = document.querySelectorAll('.metric-card #mGfiPlaceholder'); - placeholders.forEach(p => p.textContent = 'โ€”'); - } - }); - } + openModalElement('orgModal'); }; function closeModal() { @@ -1968,32 +1995,32 @@ globalThis.fetchAllIssues = async function () { updateStats(); }; -async function loadCachedIssues() { - if (allIssues.length || issuesFetching) return; - try { +async function loadCachedIssues(){ + if(allIssues.length||issuesFetching) return; + try{ const res = await fetch('/data/issues.json'); - if (!res.ok) throw new Error(`HTTP ${res.status}`); - const data = await res.json(); - if (!Array.isArray(data.issues)) return; - - const orgByGithub = new Map(ORGS.map(o => [o.github?.toLowerCase(), o])); - const orgByName = new Map(ORGS.map(o => [o.name?.toLowerCase(), o])); - - allIssues = data.issues.map(issue => { - const key = issue.github?.toLowerCase() || issue.repo?.toLowerCase() || issue.org?.toLowerCase(); - const orgMeta = orgByGithub.get(key) || orgByName.get(issue.org?.toLowerCase()); - const owner = githubOwnerFromValue(issue.github || issue.repo); - return { - title: issue.title || '', - url: issue.url || '', - org: issue.org || '', - orgCat: orgMeta?.cat || '', - orgTags: orgMeta?.tags || [], - logo: owner ? `https://github.com/${owner}.png?size=64` : '', - repo: issue.repo || issue.github || '', - created_at: issue.created_at || '', - labels: Array.isArray(issue.labels) ? issue.labels.map(l => typeof l === 'string' ? l : (l.name || '')) : [], - comments: typeof issue.comments === 'number' ? issue.comments : Number(issue.comments || 0), + if(!res.ok) throw new Error(`HTTP ${res.status}`); + const data=await res.json(); + if(!Array.isArray(data.issues)) return; + + const orgByGithub=new Map(ORGS.map(o=>[o.github?.toLowerCase(),o])); + const orgByName=new Map(ORGS.map(o=>[o.name?.toLowerCase(),o])); + + allIssues=data.issues.map(issue=>{ + const key=issue.github?.toLowerCase()||issue.repo?.toLowerCase()||issue.org?.toLowerCase(); + const orgMeta=orgByGithub.get(key)||orgByName.get(issue.org?.toLowerCase()); + const owner=githubOwnerFromValue(issue.github||issue.repo); + return{ + title:issue.title||'', + url:issue.url||'', + org:issue.org||'', + orgCat:orgMeta?.cat||'', + orgTags:orgMeta?.tags||[], + logo:owner?`https://github.com/${owner}.png?size=64`:'', + repo:issue.repo||issue.github||'', + created_at:issue.created_at||'', + labels:Array.isArray(issue.labels)?issue.labels.map(l=>typeof l==='string'?l:(l.name||'')):[], + comments:typeof issue.comments==='number'?issue.comments:Number(issue.comments||0), }; }); @@ -2223,196 +2250,54 @@ async function loadMentorData() { } } -function renderMentorFinder() { - const container = document.getElementById('mentorsContainer'); - if (!container || mentorDataState !== 'loaded') return; - - const search = (document.getElementById('mentorSearchInput')?.value || '').toLowerCase().trim(); - const channel = document.getElementById('mentorChannelFilter')?.value || ''; - - const matched = []; - Object.entries(MENTOR_DATA).forEach(([orgName, mentors]) => { - if (!Array.isArray(mentors)) return; - mentors.forEach(m => { - const matchSearch = !search || - m.name.toLowerCase().includes(search) || - orgName.toLowerCase().includes(search) || - (m.channels || []).some(c => String(c.value).toLowerCase().includes(search)); - - const matchChannel = !channel || - (m.channels || []).some(c => c.type === channel); - - if (matchSearch && matchChannel) { - matched.push({ orgName, ...m }); - } - }); - }); - - container.innerHTML = ''; - - if (matched.length === 0) { - container.innerHTML = ` -
- person_search -

No Mentors Match Your Query

-

Try adjusting your keyword filter or changing communication channels.

-
`; - return; - } - - matched.slice(0, 30).forEach(m => { - const card = document.createElement('div'); - card.className = 'mentor-card p-6 bg-white dark:bg-zinc-950 border border-zinc-200 dark:border-zinc-800 rounded-2xl flex flex-col justify-between hover:shadow-lg transition-all animate-fade-up'; - - const channelsHtml = (m.channels || []).map(c => { - const icon = CHANNEL_ICONS[c.type] || '๐Ÿ’ฌ'; - const isUrl = sanitizeHrefUrl(c.value); - if (isUrl) { - return safeHTML` - ${icon} ${c.type} - `; - } else { - return safeHTML` - ${icon} ${c.value} - `; - } - }); - - card.innerHTML = safeHTML` -
-
-

${m.name}

- ${m.role || 'Mentor'} -
-

${m.orgName}

-
-
- ${channelsHtml} -
`; - container.appendChild(card); - }); -} - -function renderMentorContactSection(org) { - const container = document.getElementById('mMentorsSection'); - if (!container) return; - - container.innerHTML = ''; - const mentors = MENTOR_DATA[org.name]; - - if (!mentors || !mentors.length) { - container.innerHTML = safeHTML` -
-

Contact details unavailable for ${org.name}. Browse their GSoC Ideas Page for mentor details.

-
`; - return; - } - - mentors.forEach(m => { - const card = document.createElement('div'); - card.className = 'mentor-card p-4 bg-zinc-50 dark:bg-zinc-900/40 border border-zinc-100 dark:border-zinc-800 rounded-xl mb-3 last:mb-0'; - - const channelsHtml = (m.channels || []).map(c => { - const icon = CHANNEL_ICONS[c.type] || '๐Ÿ’ฌ'; - const isUrl = sanitizeHrefUrl(c.value); - if (isUrl) { - return safeHTML` - ${icon} ${c.type}: Connect - `; - } else { - return safeHTML` - ${icon} ${c.type}: ${c.value} - `; - } - }); - - card.innerHTML = safeHTML` -
-
${m.name}
- ${m.role || 'Mentor'} -
-
- ${channelsHtml} -
`; - container.appendChild(card); - }); -} - -// Stale Data Banner Notice Check function applyStaleDataNotice() { - const now = new Date(); - if (now > GSOC_SELECTION_DATE) { - const alertBanner = document.getElementById('selectionStaleAlert'); - if (alertBanner) alertBanner.classList.remove('hidden'); - - const mentorStaleNote = document.getElementById('mentorStaleNote'); - const mentorBannerDot = document.getElementById('mentorBannerDot'); - if (mentorStaleNote) mentorStaleNote.classList.remove('hidden'); - if (mentorBannerDot) { - mentorBannerDot.classList.remove('pulse-dot'); - mentorBannerDot.style.backgroundColor = '#a1a1aa'; // Zinc-400 - } - } -} - -// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• -// INITIALIZATION -// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• -document.addEventListener('DOMContentLoaded', () => { - // Sync bookmarks from storage initially - ORGS.forEach(o => { - if (o.github && ghCache[o.github]) { - o._gh = ghCache[o.github]; - } - }); - - // Restore filter state from URL parameters - (function restoreFiltersFromURL() { - const params = new URLSearchParams(location.search); - const setElValue = (id, val) => { - const el = document.getElementById(id); - if (el && val) el.value = val; - }; - - setElValue('searchInput', params.get('q')); - setElValue('hero-search', params.get('q')); - setElValue('categoryFilter', params.get('cat')); - setElValue('complexityFilter', params.get('comp')); - setElValue('sortSelect', params.get('sort')); - - const lang = params.get('lang'); - if (lang) { - lang.split(',').map(s => s.trim()).filter(Boolean).forEach(l => { - selectedLanguages.add(l); - const pillBtn = document.querySelector(`.pill[data-lang="${l}"]`); - if (pillBtn) { - pillBtn.classList.add('active'); - pillBtn.setAttribute('aria-pressed', 'true'); - } + const SELECTION_DATE = new Date('2026-05-08T18:00:00Z'); + if (Date.now() <= SELECTION_DATE.getTime()) return; + const issuesStaleNote = document.getElementById('issuesStaleNote'); + if (issuesStaleNote) issuesStaleNote.classList.remove('hidden'); + const mentorStaleNote = document.getElementById('mentorStaleNote'); + if (mentorStaleNote) mentorStaleNote.classList.remove('hidden'); +} + +const _initFromURL = () => { + loadCommunityActivity(); + const params = new URLSearchParams(location.search); + if (params.get('q')) document.getElementById('searchInput').value = params.get('q'); + if (params.get('cat')) document.getElementById('categoryFilter').value = params.get('cat'); + const langParam = params.get('lang'); + if (langParam) { + const langs = langParam.split(',').map(s => s.trim()).filter(Boolean); + if (langs.length > 1) { + selectedLanguages.clear(); + document.querySelectorAll('.pill').forEach(btn => { + const active = langs.includes(btn.dataset.lang); + btn.classList.toggle('active', active); + btn.setAttribute('aria-pressed', active ? 'true' : 'false'); + if (active) selectedLanguages.add(btn.dataset.lang); }); renderSelectedLanguages(); } + } - const chip = params.get('chip'); - if (chip) { - activeChip = chip; - document.querySelectorAll('.filter-chip').forEach(el => { - const text = el.textContent.trim().toLowerCase(); - let key = null; - if (text.includes('bookmarked')) key = 'bookmarked'; - else if (text.includes('veteran')) key = 'veterans'; - else if (text.includes('newcomer')) key = 'newcomers'; - else if (text.includes('low competition')) key = 'low-competition'; - else if (text.includes('high competition')) key = 'high-competition'; - else if (text.includes('actively')) key = 'active'; - - if (key === chip) { - el.classList.add('bg-orange-600', 'text-white'); - el.classList.remove('bg-surface-container-highest'); - } - }); - } - })(); + const chip = params.get('chip'); + if (chip) { + activeChip = chip; + document.querySelectorAll('.filter-chip').forEach(el => { + const text = el.textContent.trim().toLowerCase(); + let key = null; + if (text.includes('bookmarked')) key = 'bookmarked'; + else if (text.includes('veteran')) key = 'veterans'; + else if (text.includes('newcomer')) key = 'newcomers'; + else if (text.includes('low competition')) key = 'low-competition'; + else if (text.includes('high competition')) key = 'high-competition'; + else if (text.includes('actively')) key = 'active'; + + if (key === chip) { + el.classList.add('bg-orange-600', 'text-white'); + el.classList.remove('bg-surface-container-highest'); + } + }); + } updateCountdown(); const countdownTimer = setInterval(updateCountdown, 60000); @@ -2420,7 +2305,6 @@ document.addEventListener('DOMContentLoaded', () => { renderTimeline(); applyStaleDataNotice(); - // Watchlist, comparison, analytics applyFilters(); renderWatchlist(); renderCompare(); @@ -2430,7 +2314,6 @@ document.addEventListener('DOMContentLoaded', () => { loadMentorData(); renderGoodFirstIssues(); - // Wire up filter event listeners document.getElementById('searchInput')?.addEventListener('input', applyFilters); document.getElementById('categoryFilter')?.addEventListener('change', applyFilters); document.getElementById('complexityFilter')?.addEventListener('change', applyFilters); @@ -2450,7 +2333,6 @@ document.addEventListener('DOMContentLoaded', () => { document.getElementById('surpriseBtn')?.addEventListener('click', openRandomOrg); - // Programmatic event listeners replacing inline HTML handlers for close and action buttons document.getElementById('closeOrgModalBtn')?.addEventListener('click', closeModal); document.getElementById('closeCompareModalBtn')?.addEventListener('click', closeCompareModal); document.getElementById('closeHelpModalBtn')?.addEventListener('click', closeHelpModal); @@ -2476,7 +2358,6 @@ document.addEventListener('DOMContentLoaded', () => { }); }); - // Sync hero-search const heroSearch = document.getElementById('hero-search'); if (heroSearch) { heroSearch.value = document.getElementById('searchInput')?.value || new URLSearchParams(location.search).get('q') || ''; @@ -2491,7 +2372,6 @@ document.addEventListener('DOMContentLoaded', () => { }); } - // Quick chips listeners document.querySelectorAll('.filter-chip').forEach(chip => { chip.addEventListener('click', () => { const text = chip.textContent.trim().toLowerCase(); @@ -2520,7 +2400,6 @@ document.addEventListener('DOMContentLoaded', () => { }); }); - // Phase 2: Add programmatic event listeners to pills, empty state clear button, and compare modal button document.querySelectorAll('.pill[data-lang]').forEach(pill => { pill.addEventListener('click', () => { if (typeof globalThis.togglePill === 'function') { @@ -2531,11 +2410,8 @@ document.addEventListener('DOMContentLoaded', () => { document.getElementById('emptyStateClearBtn')?.addEventListener('click', clearAllFilters); document.getElementById('openCompareModalBtn')?.addEventListener('click', openCompareModal); - - // Wire up live stats fetch button document.getElementById('mFetchBtn')?.addEventListener('click', fetchModalGH); - // Event delegation for trending cards scroll list const trendingScroll = document.getElementById('trendingScroll'); if (trendingScroll) { trendingScroll.addEventListener('click', (e) => { @@ -2546,7 +2422,6 @@ document.addEventListener('DOMContentLoaded', () => { }); } - // Event delegation for selected languages strip const selectedStrip = document.getElementById('selectedLangsStrip'); if (selectedStrip) { selectedStrip.addEventListener('click', (e) => { @@ -2565,7 +2440,6 @@ document.addEventListener('DOMContentLoaded', () => { }); } - // Event delegation for mentors container const mentorsContainer = document.getElementById('mentorsContainer'); if (mentorsContainer) { mentorsContainer.addEventListener('click', (e) => { @@ -2575,7 +2449,13 @@ document.addEventListener('DOMContentLoaded', () => { } }); } -}); +}; + +if (typeof document !== 'undefined' && typeof document.addEventListener === 'function') { + document.addEventListener('DOMContentLoaded', _initFromURL); +} else if (typeof requestAnimationFrame !== 'undefined') { + requestAnimationFrame(_initFromURL); +} // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // EXPORT FOR NODE ENVIRONMENT TESTING COMPATIBILITY diff --git a/src/styles.css b/src/styles.css index aa003ad049..10fc14fb33 100644 --- a/src/styles.css +++ b/src/styles.css @@ -444,126 +444,10 @@ body{background:var(--bg);color:var(--ink);font-family:'Plus Jakarta Sans',sans- .an-note{font-size:11px;color:var(--muted);text-align:center;padding-top:13px;border-top:1px solid var(--border2);margin-top:4px;line-height:1.6} /* โ”€โ”€ FOOTER โ”€โ”€ */ -.premium-footer { - background: var(--nav-bg); - border-top: 1.5px solid var(--border); - position: relative; - transition: background 0.3s, border-color 0.3s; -} - -.premium-footer::before { - content: ''; - position: absolute; - top: -1.5px; - left: 0; - right: 0; - height: 2px; - background: linear-gradient(90deg, var(--orange) 0%, var(--blue) 50%, var(--purple) 100%); - z-index: 1; -} - -.premium-footer a { - transition: all 0.2s ease-in-out; -} - -.footer-stat-card { - background: rgba(255, 255, 255, 0.03); - backdrop-filter: blur(8px); - border: 1px solid var(--border2); - transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease; -} - -.premium-footer[data-theme="light"] .footer-stat-card, -:root:not(.dark) .footer-stat-card { - background: rgba(0, 0, 0, 0.02); -} - -.footer-stat-card:hover { - transform: translateY(-2px); - border-color: var(--orange); - box-shadow: var(--shadow); -} - -.social-link { - display: inline-flex; - align-items: center; - justify-content: center; - width: 36px; - height: 36px; - border-radius: 50%; - border: 1px solid var(--border); - background: var(--card-bg); - color: var(--ink3); - transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); -} - -.social-link:hover { - transform: translateY(-3px); - color: var(--white); -} - -.social-link.github:hover { - background: #24292e; - border-color: #24292e; - box-shadow: 0 0 12px rgba(36, 41, 46, 0.5); -} - -.social-link.discord:hover { - background: #5865F2; - border-color: #5865F2; - box-shadow: 0 0 12px rgba(88, 101, 242, 0.5); -} - -.social-link.linkedin:hover { - background: #0A66C2; - border-color: #0A66C2; - box-shadow: 0 0 12px rgba(10, 102, 194, 0.5); -} - -.social-link.twitter:hover { - background: #1DA1F2; - border-color: #1DA1F2; - box-shadow: 0 0 12px rgba(29, 161, 242, 0.5); -} - -#back-to-top-btn { - position: fixed; - bottom: 24px; - right: 24px; - width: 44px; - height: 44px; - border-radius: 50%; - background: var(--orange); - color: white; - border: none; - box-shadow: 0 4px 14px rgba(244, 107, 14, 0.4); - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - z-index: 90; - opacity: 0; - visibility: hidden; - transform: translateY(10px) scale(0.9); - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); -} - -#back-to-top-btn.show { - opacity: 1; - visibility: visible; - transform: translateY(0) scale(1); -} - -#back-to-top-btn:hover { - background: var(--orange-deep); - transform: translateY(-3px) scale(1.05); - box-shadow: 0 6px 20px rgba(244, 107, 14, 0.6); -} - -#back-to-top-btn:focus-visible { - outline: 2px solid var(--orange); - outline-offset: 2px; -} +footer{border-top:1.5px solid var(--border);padding:24px;text-align:center;transition:border-color .3s} +.footer-inner{max-width:1340px;margin:0 auto;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:10px;font-size:12px;color:var(--muted)} +footer a{color:var(--orange);text-decoration:none;font-weight:600} +footer a:hover{text-decoration:underline} [data-theme="dark"] select option{background:#1A1108;color:var(--ink)} /* โ”€โ”€ SKELETON CARDS โ”€โ”€ */ @@ -872,45 +756,36 @@ body{background:var(--bg);color:var(--ink);font-family:'Plus Jakarta Sans',sans- background: var(--orange-pale); } -/* Privacy Page specific overrides to match exact styling */ -.privacy-page .premium-footer { - background: #f4f4f5; - border-top-color: #e4e4e7; -} -.dark .privacy-page .premium-footer, -.privacy-page.dark .premium-footer { - background: #09090b; - border-top-color: #27272a; -} -.privacy-page .footer-stat-card { - background: rgba(0, 0, 0, 0.02); - border-color: #e4e4e7; -} -.dark .privacy-page .footer-stat-card, -.privacy-page.dark .footer-stat-card { - background: rgba(255, 255, 255, 0.03); - border-color: #27272a; -} -.privacy-page .social-link { - border-color: #e4e4e7; - background: #ffffff; - color: #71717a; -} -.dark .privacy-page .social-link, -.privacy-page.dark .social-link { - border-color: #27272a; - background: #18181b; - color: #a1a1aa; -} -.privacy-page #back-to-top-btn { - background: #f97316; - box-shadow: 0 4px 14px rgba(249, 115, 22, 0.4); -} -.privacy-page #back-to-top-btn:hover { - background: #ea580c; - box-shadow: 0 6px 20px rgba(249, 115, 22, 0.6); -} -.privacy-page #back-to-top-btn:focus-visible { - outline-color: #f97316; +/* โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• + COMMUNITY ACTIVITY BADGES & MODAL SECTION +โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• */ +.bca { + /* base โ€” shares existing .b sizing */ } +.bca-very { background:rgba(0,135,90,.09); color:var(--green); border-color:rgba(0,135,90,.25); } +.bca-active { background:rgba(26,86,219,.08); color:var(--blue); border-color:rgba(26,86,219,.25); } +.bca-mod { background:rgba(251,191,36,.10); color:#92600A; border-color:rgba(251,191,36,.35); } +.bca-low { background:rgba(197,48,48,.07); color:var(--red); border-color:rgba(197,48,48,.2); } +[data-theme="dark"] .bca-mod { background:rgba(251,191,36,.15); color:#FBBC04; } + +/* Community activity rows inside the modal */ +.ca-grid { + display: flex; + flex-direction: column; + gap: 6px; + background: var(--bg); + border: 1.5px solid var(--border2); + border-radius: 11px; + padding: 12px 14px; + margin-bottom: 16px; + transition: background .3s, border-color .3s; +} +.ca-row { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 12px; +} +.ca-label { color: var(--muted); font-weight: 500; } +.ca-value { color: var(--ink); font-family: 'Fira Code', monospace; font-size: 11px; } From c90740cee60c5366dc92bb0fbbb0013db451f90c Mon Sep 17 00:00:00 2001 From: shravanithouta108 Date: Sat, 20 Jun 2026 17:15:25 +0530 Subject: [PATCH 02/45] fix: resolve ORGS undefined error and duplicate filteredOrgs declaration Signed-off-by: shravanithouta108 --- index.html | 3 ++- src/js/app.js | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/index.html b/index.html index ef81fb4022..616bfd1096 100644 --- a/index.html +++ b/index.html @@ -2279,7 +2279,7 @@

diff --git a/src/js/app.js b/src/js/app.js index 7eed97572c..a32610d517 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -4,7 +4,7 @@ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // GLOBAL STATE & COMPATIBILITY LAYER // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• -let filteredOrgs = []; +// filteredOrgs declared in index.html inline script let MENTOR_DATA = {}; let mentorDataState = 'idle'; const compareList = []; // list of org names @@ -1204,6 +1204,7 @@ function renderOrgs(reset = true) {
${String(org.years)}y Veteran ${org.codebase} + ${rawHTML(getCommunityBadge(org))} @@ -2259,8 +2260,8 @@ function applyStaleDataNotice() { if (mentorStaleNote) mentorStaleNote.classList.remove('hidden'); } -const _initFromURL = () => { - loadCommunityActivity(); +const _initFromURL = async () => { + await loadCommunityActivity(); const params = new URLSearchParams(location.search); if (params.get('q')) document.getElementById('searchInput').value = params.get('q'); if (params.get('cat')) document.getElementById('categoryFilter').value = params.get('cat'); From 5661fcd36567b74a7f774c8954124352d3411c60 Mon Sep 17 00:00:00 2001 From: shravanithouta108 Date: Fri, 10 Jul 2026 10:45:41 +0530 Subject: [PATCH 03/45] chore: reset community_activity.json to empty object Signed-off-by: shravanithouta108 --- data/community_activity.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/community_activity.json b/data/community_activity.json index 9e26dfeeb6..0967ef424b 100644 --- a/data/community_activity.json +++ b/data/community_activity.json @@ -1 +1 @@ -{} \ No newline at end of file +{} From b71c0661eb6f7c0ca4369243964d0be07dc40dcd Mon Sep 17 00:00:00 2001 From: shravanithouta108 Date: Fri, 10 Jul 2026 10:55:45 +0530 Subject: [PATCH 04/45] fix: include bare GitHub owners and preserve null metrics Signed-off-by: shravanithouta108 --- agent/scripts/compute-activity-scores.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/agent/scripts/compute-activity-scores.js b/agent/scripts/compute-activity-scores.js index 52bd86d0e3..353ff2b441 100644 --- a/agent/scripts/compute-activity-scores.js +++ b/agent/scripts/compute-activity-scores.js @@ -30,7 +30,7 @@ const orgMatches = orgDataRaw.matchAll(/name:\s*"([^"]+)"[^}]+github:\s*"([^"]+) for (const m of orgMatches) { const name = m[1]; const repo = m[2]; - if (repo?.includes('/')) { + if (repo) { githubRepos.push({ name, repo }); } } @@ -120,9 +120,9 @@ async function analyzeOrg({ name, repo }) { tier, label, signals: { - issueResponseDays: Math.round(issueResponseDays), + issueResponseDays: issueResponseDays === null ? null : Math.round(issueResponseDays), commitFrequency: Math.round(commitFrequency * 10) / 10, - prMergeRate: Math.round(prMergeRate * 100), + prMergeRate: prMergeRate === null ? null : Math.round(prMergeRate * 100), ideasFreshnessDays: Math.round(ideasFreshnessDays), starsGrowth: Math.round(starsGrowth * 10) / 10 }, From 3b2fea3ee0b29a6f1a7e32e855c990483a1c91c8 Mon Sep 17 00:00:00 2001 From: shravanithouta108 Date: Sat, 11 Jul 2026 11:02:59 +0530 Subject: [PATCH 05/45] fix: resolve safeHTML/rawHTML ReferenceError, duplicate communityActivity load, and bare owner API calls - index.html: remove duplicate 'let communityActivity' declaration/fetch that caused a SyntaxError (two classic scripts sharing global scope both declared it), which aborted app.js parsing and broke safeHTML/rawHTML. - src/js/app.js: loadCommunityActivity() remains the single fetch point, now mirrored onto window.communityActivity. - index.html: getActivityBadge(), the active-communities chip filter, and the activity-score sort now read window.communityActivity. - agent/scripts/compute-activity-scores.js: restore repo.includes('/') check so bare GitHub owners (c2siorg, ML4SCI, etc.) are skipped instead of hitting an invalid API endpoint. Signed-off-by: shravanithouta108 --- agent/scripts/compute-activity-scores.js | 2 +- index.html | 18 +++++++++--------- src/js/app.js | 5 +++++ 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/agent/scripts/compute-activity-scores.js b/agent/scripts/compute-activity-scores.js index 353ff2b441..cbd32ca3cd 100644 --- a/agent/scripts/compute-activity-scores.js +++ b/agent/scripts/compute-activity-scores.js @@ -30,7 +30,7 @@ const orgMatches = orgDataRaw.matchAll(/name:\s*"([^"]+)"[^}]+github:\s*"([^"]+) for (const m of orgMatches) { const name = m[1]; const repo = m[2]; - if (repo) { + if (repo && repo.includes('/')) { githubRepos.push({ name, repo }); } } diff --git a/index.html b/index.html index 616bfd1096..173a43af31 100644 --- a/index.html +++ b/index.html @@ -2073,14 +2073,14 @@

${escapeHtml(a.label)}`; } @@ -3975,7 +3975,7 @@

${escapeHtml(org.name)}

else if (activeChip === 'newcomers') matchChip = o.years <= 3; else if (activeChip === 'low-competition') matchChip = o.competition === 'chill'; else if (activeChip === 'high-competition') matchChip = o.competition === 'hot'; - else if (activeChip === 'active-communities') matchChip = (communityActivity[o.name]?.score || 0) >= 60; + else if (activeChip === 'active-communities') matchChip = ((window.communityActivity || {})[o.name]?.score || 0) >= 60; return matchSearch && matchCat && matchComp && matchLang && matchChip; }); @@ -4013,7 +4013,7 @@

${escapeHtml(org.name)}

if(sortType==='comp-low') return ['chill','moderate','hot'].indexOf(a.competition) - ['chill','moderate','hot'].indexOf(b.competition); if(sortType==='stars') return (b._gh?.stars||0) - (a._gh?.stars||0); if(sortType==='gfi') return (b._gh?.gfi||0) - (a._gh?.gfi||0); - if(sortType==='most-active'||sortType==='activity-score') return ((communityActivity[b.name]?.score)||0) - ((communityActivity[a.name]?.score)||0); + if(sortType==='most-active'||sortType==='activity-score') return (((window.communityActivity || {})[b.name]?.score)||0) - (((window.communityActivity || {})[a.name]?.score)||0); return a.name.localeCompare(b.name); } diff --git a/src/js/app.js b/src/js/app.js index a32610d517..cb9b0061f4 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -470,6 +470,10 @@ cleanCache(); // COMMUNITY ACTIVITY DATA // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• let communityActivity = {}; // will hold the parsed community_activity.json +// Single source of truth: this is the ONLY place communityActivity is fetched. +// Mirrored onto window.communityActivity so index.html's inline script can +// read it without redeclaring/refetching and racing this assignment. +window.communityActivity = communityActivity; async function loadCommunityActivity() { try { @@ -477,6 +481,7 @@ async function loadCommunityActivity() { const res = await fetch('/data/community_activity.json?v=' + bust); if (!res.ok) return; communityActivity = await res.json(); + window.communityActivity = communityActivity; applyFilters(); } catch (e) { console.warn('community_activity.json not yet available:', e); From d6f20537d107128c516292f31511b6ded848dd0b Mon Sep 17 00:00:00 2001 From: Ayush Date: Wed, 17 Jun 2026 08:02:05 +0530 Subject: [PATCH 06/45] fix: updated github-action bot issue assignment workflow (#1792) * fix: updated github-action bot issue assignment workflow Signed-off-by: imayuss * fix: Admin Fallback & Unapprovable State issue fixed Signed-off-by: imayuss * fix: fixed unique marker issue Signed-off-by: imayuss * fix(workflow): prevent comment spoofing and secure state lookups Signed-off-by: imayuss * fix(workflow): Implemented more specific error handling or logging Signed-off-by: imayuss * fix(workflow): Removed inline comments,duplicate var declaration & simplified fallback text handling Signed-off-by: imayuss --------- Signed-off-by: imayuss Signed-off-by: shravanithouta108 --- .../workflows/issue-context-assignment.yml | 423 ++++++++---------- 1 file changed, 189 insertions(+), 234 deletions(-) diff --git a/.github/workflows/issue-context-assignment.yml b/.github/workflows/issue-context-assignment.yml index 1094e2a24f..339a50d30e 100644 --- a/.github/workflows/issue-context-assignment.yml +++ b/.github/workflows/issue-context-assignment.yml @@ -34,36 +34,14 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 #v9.0.0 with: script: | - const body = - ( - context.payload.comment?.body || - '' - ).trim(); - - const assignMatch = - /^\/assign$/i.test(body); - - const approveMatch = - body.match( - /^\/approve-assignment\s+@?([\w-]+)/i - ); + const body = (context.payload.comment?.body || '').trim(); - core.setOutput( - 'is_assign', - String(assignMatch) - ); + const assignMatch = /^\/assign$/i.test(body); + const approveMatch = body.match(/^\/approve-assignment\s+@?([\w-]+)/i); - core.setOutput( - 'is_approve', - String(!!approveMatch) - ); - - core.setOutput( - 'target_user', - approveMatch - ? approveMatch[1].toLowerCase() - : '' - ); + core.setOutput('is_assign', String(assignMatch)); + core.setOutput('is_approve', String(!!approveMatch)); + core.setOutput('target_user', approveMatch ? approveMatch[1].toLowerCase() : ''); # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ # ACTIVE MENTOR CONTEXT REVIEW @@ -80,7 +58,6 @@ jobs: RANDOM_ROTATION: "true" run: | RESULT=$(node .github/scripts/select-issue-mentors.js) - echo "result=$RESULT" >> "$GITHUB_OUTPUT" - name: Post mentor review request @@ -92,47 +69,38 @@ jobs: SELECTED_MENTORS_JSON: ${{ steps.mentor_select.outputs.result }} with: script: | - const { owner, repo } = - context.repo; + const { owner, repo } = context.repo; + const issue_number = context.issue.number; - const issue_number = - context.issue.number; + function findBotComment(comments, marker) { + return comments.find( + c => + c.user?.login === 'github-actions[bot]' && + c.body?.includes(marker) + ); + } let selected = []; - try { - - selected = - JSON.parse( - process.env.SELECTED_MENTORS_JSON || '{}' - ).selected || []; - + selected = JSON.parse(process.env.SELECTED_MENTORS_JSON || '{}').selected || []; } catch { - selected = []; - } - const marker = - ''; + const MENTOR_CONTEXT_MARKER = ''; - const mentionList = - selected.length - ? selected - .map(m => `@${m}`) - .join(' โ€ข ') - : '_No active mentors available._'; + const mentionList = selected.length + ? selected.map(m => `@${m}`).join(' โ€ข ') + : '_No active mentors available._'; - const avatars = - selected - .map( - mentor => - `${mentor}` - ) - .join('\n'); + const avatars = selected + .map(mentor => + `${mentor}` + ) + .join('\n'); const body = [ - marker, + MENTOR_CONTEXT_MARKER, '', '# ๐Ÿ‘€ Active Mentor Review Requested', '', @@ -158,42 +126,27 @@ jobs: '_Mentor rotation updates automatically across issues._' ].join('\n'); - const comments = - await github.paginate( - github.rest.issues.listComments, - { - owner, - repo, - issue_number, - per_page: 100 - } - ); + const comments = await github.paginate( + github.rest.issues.listComments, + { owner, repo, issue_number, per_page: 100 } + ); - const existing = - comments.find( - c => - c.body && - c.body.includes(marker) - ); + const existing = findBotComment(comments, MENTOR_CONTEXT_MARKER); if (existing) { - await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body }); - } else { - await github.rest.issues.createComment({ owner, repo, issue_number, body }); - } # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ @@ -206,66 +159,53 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 #v9.0.0 with: script: | - const { owner, repo } = - context.repo; - - const issue_number = - context.payload.issue.number; - - const claimant = - context.payload.comment.user.login; - - const issue = - ( - await github.rest.issues.get({ - owner, - repo, - issue_number - }) - ).data; - - if ( - issue.state !== 'open' || - issue.locked - ) { - - core.setOutput( - 'eligible', - 'false' - ); + const { owner, repo } = context.repo; + const issue_number = context.payload.issue.number; + const claimant = context.payload.comment.user.login; - return; + const issue = ( + await github.rest.issues.get({ owner, repo, issue_number }) + ).data; + if (issue.state !== 'open' || issue.locked) { + core.setOutput('eligible', 'false'); + return; } - if ( - (issue.assignees || []).length > 0 - ) { - - core.setOutput( - 'eligible', - 'false' - ); - + if ((issue.assignees || []).length > 0) { + core.setOutput('eligible', 'false'); return; - } - core.setOutput( - 'eligible', - 'true' - ); + const PENDING_MARKER = ''; - core.setOutput( - 'claimant', - claimant + const comments = await github.paginate( + github.rest.issues.listComments, + { owner, repo, issue_number, per_page: 100 } ); - core.setOutput( - 'issue_number', - String(issue_number) + const pendingComment = comments.find( + c => + c.user?.login === 'github-actions[bot]' && + c.body?.includes(PENDING_MARKER) && + !c.body?.includes('โœ… Issue Assigned') ); + if (pendingComment) { + core.setOutput('eligible', 'false'); + await github.rest.issues.createComment({ + owner, + repo, + issue_number, + body: `โš ๏ธ @${claimant} - an assignment request is already pending mentor approval for this issue. Please wait for the current request to be resolved.` + }); + return; + } + + core.setOutput('eligible', 'true'); + core.setOutput('claimant', claimant); + core.setOutput('issue_number', String(issue_number)); + - name: Select rotating mentors id: assign_mentors if: | @@ -277,7 +217,6 @@ jobs: RANDOM_ROTATION: "true" run: | RESULT=$(node .github/scripts/select-issue-mentors.js) - echo "result=$RESULT" >> "$GITHUB_OUTPUT" - name: Post assignment request @@ -290,48 +229,59 @@ jobs: SELECTED_MENTORS_JSON: ${{ steps.assign_mentors.outputs.result }} with: script: | - const { owner, repo } = - context.repo; + const { owner, repo } = context.repo; + const issue_number = context.issue.number; + const claimant = process.env.CLAIMANT; - const issue_number = - context.issue.number; - - const claimant = - process.env.CLAIMANT; + function findBotComment(comments, marker) { + return comments.find( + c => + c.user?.login === 'github-actions[bot]' && + c.body?.includes(marker) + ); + } let selected = []; - try { - - selected = - JSON.parse( - process.env.SELECTED_MENTORS_JSON || '{}' - ).selected || []; - + selected = JSON.parse(process.env.SELECTED_MENTORS_JSON || '{}').selected || []; } catch { - selected = []; + } + if (selected.length === 0) { + try { + const collabs = await github.paginate( + github.rest.repos.listCollaborators, + { owner, repo, permission: 'admin', per_page: 100 } + ); + selected = collabs + .map(c => c.login) + .filter(l => l !== 'github-actions[bot]') + .slice(0, 2); + } catch (err) { + core.warning( + `Admin fallback failed โ€” could not list collaborators: ${err.message || err}. No mentors will be tagged.` + ); + selected = []; + } } - const marker = - ''; + const PENDING_MARKER = ''; - const mentions = - selected - .map(m => `@${m}`) - .join(' โ€ข '); + const mentions = selected.length + ? selected.map(m => `@${m}`).join(' โ€ข ') + : '_No active mentors available._'; - const avatars = - selected - .map( - mentor => + const avatars = selected.length + ? selected + .map(mentor => `${mentor}` - ) - .join('\n'); + ) + .join('\n') + : ''; const body = [ - marker, + PENDING_MARKER, '', '# โณ Assignment Pending Mentor Approval', '', @@ -349,46 +299,30 @@ jobs: `/approve-assignment @${claimant}`, '```', '', - '> Mentors rotate dynamically across issues.', - '> If no response occurs within 24h, PA escalation may happen.' + '> Mentors rotate dynamically across issues.' ].join('\n'); - const comments = - await github.paginate( - github.rest.issues.listComments, - { - owner, - repo, - issue_number, - per_page: 100 - } - ); + const comments = await github.paginate( + github.rest.issues.listComments, + { owner, repo, issue_number, per_page: 100 } + ); - const existing = - comments.find( - c => - c.body && - c.body.includes(marker) - ); + const existing = findBotComment(comments, PENDING_MARKER); if (existing) { - await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body }); - } else { - await github.rest.issues.createComment({ owner, repo, issue_number, body }); - } # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ @@ -402,87 +336,108 @@ jobs: TARGET_USER: ${{ steps.cmd.outputs.target_user }} with: script: | - const { owner, repo } = - context.repo; - - const issue_number = - context.issue.number; - - const approver = - context.payload.comment.user.login; - - const claimant = - process.env.TARGET_USER; - - const comments = - await github.paginate( - github.rest.issues.listComments, - { - owner, - repo, - issue_number, - per_page: 100 - } - ); + const { owner, repo } = context.repo; + const issue_number = context.issue.number; + const approver = context.payload.comment.user.login; + const claimant = process.env.TARGET_USER; - const assignmentComment = - comments.find( + function findBotComment(comments, marker) { + return comments.find( c => - c.body && - c.body.includes( - '' - ) + c.user?.login === 'github-actions[bot]' && + c.body?.includes(marker) ); + } - if (!assignmentComment) { + const PENDING_MARKER = ''; - core.info( - 'No assignment request found.' - ); + const comments = await github.paginate( + github.rest.issues.listComments, + { owner, repo, issue_number, per_page: 100 } + ); - return; + const assignmentComment = findBotComment(comments, PENDING_MARKER); + if (!assignmentComment) { + core.info('No assignment request found.'); + return; } - const body = - assignmentComment.body || ''; + const pendingBody = assignmentComment.body || ''; - const mentorMentioned = - body.includes(`@${approver}`); + const requestedByMatch = pendingBody.match(/Requested by @([\w-]+)/i); + const originalClaimant = requestedByMatch ? requestedByMatch[1].toLowerCase() : null; - if (!mentorMentioned) { + if (!originalClaimant) { + core.warning('Could not determine original claimant from pending comment.'); + return; + } + if (claimant !== originalClaimant) { await github.rest.issues.createComment({ owner, repo, issue_number, - body: - `โ›” @${approver} is not one of the selected mentors for this issue.` + body: `โ›” @${approver} โ€” the username \`@${claimant}\` does not match the original requester \`@${originalClaimant}\` for this issue. Please use \`/approve-assignment @${originalClaimant}\`.` }); + return; + } + const noMentorsAvailable = pendingBody.includes('_No active mentors available._'); + const isMentorMentioned = pendingBody.includes(`@${approver}`); + + if (!isMentorMentioned && !noMentorsAvailable) { + await github.rest.issues.createComment({ + owner, + repo, + issue_number, + body: `โ›” @${approver} is not one of the selected mentors for this issue.` + }); return; + } + let permissionLevel = 'none'; + try { + const { data: permData } = await github.rest.repos.getCollaboratorPermissionLevel({ + owner, + repo, + username: approver + }); + permissionLevel = permData.permission; + } catch (err) { + core.warning(`Permission check failed for @${approver}: ${err.message || err}`); + permissionLevel = 'none'; } - const issue = - ( - await github.rest.issues.get({ - owner, - repo, - issue_number - }) - ).data; - - if ( - (issue.assignees || []).length > 0 - ) { - - core.info( - 'Issue already assigned.' - ); + const allowedRoles = ['admin', 'maintain', 'write']; + if (!allowedRoles.includes(permissionLevel)) { + await github.rest.issues.createComment({ + owner, + repo, + issue_number, + body: `โ›” @${approver} does not have sufficient repository permissions to approve assignments (requires write, maintain, or admin).` + }); + return; + } + + const issue = ( + await github.rest.issues.get({ owner, repo, issue_number }) + ).data; + if (issue.state !== 'open') { + core.info('Issue is no longer open โ€” skipping assignment.'); + await github.rest.issues.createComment({ + owner, + repo, + issue_number, + body: `โš ๏ธ @${approver} โ€” this issue is no longer open, so the assignment cannot be processed.` + }); return; + } + if ((issue.assignees || []).length > 0) { + core.info('Issue already assigned.'); + return; } await github.rest.issues.addAssignees({ @@ -493,7 +448,7 @@ jobs: }); const updatedBody = [ - '', + '', '', '# โœ… Issue Assigned', '', From 592beccd7c36712b0d9882264b17663483f82570 Mon Sep 17 00:00:00 2001 From: Aastha Khatri Date: Wed, 17 Jun 2026 08:06:02 +0530 Subject: [PATCH 07/45] Fix: mariadb mentor contact issue 346 clean (#898) * fix: update MariaDB mentor contact info and status Signed-off-by: Aastha Khatri * fix: update MariaDB mailing list URL to active location Signed-off-by: Aastha Khatri * fix: add MariaDB GSoC 2026 mentors Signed-off-by: Aastha Khatri * fix: correct indentation in MariaDB mentors and mailingLists Signed-off-by: Aastha Khatri * fix: remove blank line in Fossology entry and set null for empty MariaDB github fields Signed-off-by: Aastha Khatri --------- Signed-off-by: Aastha Khatri Co-authored-by: Savio Dsouza Signed-off-by: shravanithouta108 --- data/mentors.json | 53 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/data/mentors.json b/data/mentors.json index ac7e1e498d..2830564e39 100644 --- a/data/mentors.json +++ b/data/mentors.json @@ -2737,11 +2737,54 @@ "MariaDB": { "org": "MariaDB", "ideasUrl": "https://mariadb.org/get-involved/gsoc/", - "channels": [], - "mentors": [], - "mailingLists": [], - "tip": "", - "status": "no-contact-found", + "channels": [ + { + "type": "Zulip", + "url": "https://mariadb.zulipchat.com/", + "label": "MariaDB Zulip" + } + ], + "mentors": [ + { + "name": "Sergei Golubchik", + "github": "vuvova", + "githubUrl": "https://github.com/vuvova" + }, + { + "name": "Brandon Nesterenko", + "github": null, + "githubUrl": null + }, + { + "name": "Oleksandr Byelkin", + "github": "sanja-byelkin", + "githubUrl": "https://github.com/sanja-byelkin" + }, + { + "name": "Alexander Barkov", + "github": null, + "githubUrl": null + }, + { + "name": "Nikita Malyavin", + "github": null, + "githubUrl": null + }, + { + "name": "Rucha Deodhar", + "github": null, + "githubUrl": null + } + ], + "mailingLists": [ + { + "type": "Mailing list", + "url": "https://lists.mariadb.org/postorius/lists/developers.lists.mariadb.org/", + "label": "maria-developers mailing list" + } + ], + "tip": "Join the Zulip community and subscribe to the maria-developers mailing list before asking project-specific questions.", + "status": "ok", "lastFetched": "2026-05-09T16:09:12.548Z" }, "MDAnalysis": { From ea9d77a8bac2a2804131c05af3a8492b5e400957 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 08:07:00 +0530 Subject: [PATCH 08/45] chore: update mentor leaderboard (#1839) Co-authored-by: S3DFX-CYBER <213652949+S3DFX-CYBER@users.noreply.github.com> Co-authored-by: Savio Dsouza Signed-off-by: shravanithouta108 --- .github/reviewers/mentor-leaderboard.md | 78 ++++++++++++------------- .github/reviewers/mentor-stats.json | 20 +++---- 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/.github/reviewers/mentor-leaderboard.md b/.github/reviewers/mentor-leaderboard.md index dcd61b4fa5..3409f65d73 100644 --- a/.github/reviewers/mentor-leaderboard.md +++ b/.github/reviewers/mentor-leaderboard.md @@ -4,48 +4,48 @@ | Rank | Mentor | Reviews | Score | |------|--------|---------|-------| -| ๐Ÿฅ‡ | @TarunyaProgrammer | 19 | 75 | -| ๐Ÿฅˆ | @KUMARNiru007 | 18 | 73 | -| ๐Ÿฅ‰ | @nitinog10 | 13 | 54 | -| 4 | @4f4d | 7 | 37 | -| 5 | @Anushreebasics | 11 | 29 | +| ๐Ÿฅ‡ | @KUMARNiru007 | 25 | 108 | +| ๐Ÿฅˆ | @TarunyaProgrammer | 19 | 75 | +| ๐Ÿฅ‰ | @nitinog10 | 15 | 64 | +| 4 | @Anushreebasics | 17 | 45 | +| 5 | @4f4d | 7 | 37 | | 6 | @deepak0x | 7 | 26 | | 7 | @itsdakshjain | 6 | 19 | -| 8 | @saurabh24thakur | 3 | 17 | -| 9 | @Balaji91221 | 8 | 16 | -| 10 | @knoxiboy | 5 | 14 | +| 8 | @knoxiboy | 6 | 19 | +| 9 | @saurabh24thakur | 3 | 17 | +| 10 | @Balaji91221 | 8 | 16 | | 11 | @12fahed | 3 | 11 | | 12 | @Mrigakshi-Rathore | 5 | 10 | -| 13 | @MUKUL-PRASAD-SIGH | 4 | 9 | -| 14 | @CoderOggy78 | 3 | 7 | -| 15 | @nihalawasthi | 3 | 7 | -| 16 | @AnirudhPhophalia | 3 | 6 | -| 17 | @BandhiyaHardik | 3 | 6 | -| 18 | @sabeenaviklar | 2 | 6 | -| 19 | @stealthwhizz | 2 | 6 | -| 20 | @lourduradjou | 2 | 5 | -| 21 | @SparshM8 | 2 | 4 | -| 22 | @AnirbansarkarS | 1 | 2 | -| 23 | @Ayush-Patel-56 | 1 | 2 | -| 24 | @piyushdotcomm | 1 | 2 | -| 25 | @Sagar-Datkhile | 1 | 2 | -| 26 | @aanjalii01 | 0 | 0 | -| 27 | @adithyan-css | 0 | 0 | -| 28 | @AditthyaSS | 0 | 0 | -| 29 | @anubhavxdev | 0 | 0 | -| 30 | @aryanbhutani26 | 0 | 0 | -| 31 | @ayu-yishu13 | 0 | 0 | -| 32 | @Ayushh-Sharmaa | 0 | 0 | -| 33 | @coder-zs-cse | 0 | 0 | -| 34 | @deepaksinghh12 | 0 | 0 | -| 35 | @DevROHIT11 | 0 | 0 | -| 36 | @Haile-12 | 0 | 0 | -| 37 | @JoeCelaster | 0 | 0 | -| 38 | @kallal79 | 0 | 0 | -| 39 | @KaranGupta2005 | 0 | 0 | -| 40 | @lovestaco | 0 | 0 | -| 41 | @magic-peach | 0 | 0 | -| 42 | @Maxd646 | 0 | 0 | +| 13 | @Maxd646 | 3 | 10 | +| 14 | @MUKUL-PRASAD-SIGH | 4 | 9 | +| 15 | @CoderOggy78 | 3 | 7 | +| 16 | @nihalawasthi | 3 | 7 | +| 17 | @AnirudhPhophalia | 3 | 6 | +| 18 | @BandhiyaHardik | 3 | 6 | +| 19 | @sabeenaviklar | 2 | 6 | +| 20 | @stealthwhizz | 2 | 6 | +| 21 | @lourduradjou | 2 | 5 | +| 22 | @SparshM8 | 2 | 4 | +| 23 | @AnirbansarkarS | 1 | 2 | +| 24 | @Ayush-Patel-56 | 1 | 2 | +| 25 | @piyushdotcomm | 1 | 2 | +| 26 | @Sagar-Datkhile | 1 | 2 | +| 27 | @aanjalii01 | 0 | 0 | +| 28 | @adithyan-css | 0 | 0 | +| 29 | @AditthyaSS | 0 | 0 | +| 30 | @anubhavxdev | 0 | 0 | +| 31 | @aryanbhutani26 | 0 | 0 | +| 32 | @ayu-yishu13 | 0 | 0 | +| 33 | @Ayushh-Sharmaa | 0 | 0 | +| 34 | @coder-zs-cse | 0 | 0 | +| 35 | @deepaksinghh12 | 0 | 0 | +| 36 | @DevROHIT11 | 0 | 0 | +| 37 | @Haile-12 | 0 | 0 | +| 38 | @JoeCelaster | 0 | 0 | +| 39 | @kallal79 | 0 | 0 | +| 40 | @KaranGupta2005 | 0 | 0 | +| 41 | @lovestaco | 0 | 0 | +| 42 | @magic-peach | 0 | 0 | | 43 | @MAYANKSHARMA01010 | 0 | 0 | | 44 | @Mohit-368 | 0 | 0 | | 45 | @morningstarxcdcode | 0 | 0 | @@ -63,4 +63,4 @@ | 57 | @uddalak2005 | 0 | 0 | | 58 | @vanshaggarwal07 | 0 | 0 | -Last updated: Thu, 11 Jun 2026 07:49:18 GMT +Last updated: Wed, 17 Jun 2026 02:34:31 GMT diff --git a/.github/reviewers/mentor-stats.json b/.github/reviewers/mentor-stats.json index 79c69eaa52..06ddbd1bc1 100644 --- a/.github/reviewers/mentor-stats.json +++ b/.github/reviewers/mentor-stats.json @@ -32,8 +32,8 @@ "score": 0 }, "Anushreebasics": { - "reviews": 11, - "score": 29 + "reviews": 17, + "score": 45 }, "aryanbhutani26": { "reviews": 0, @@ -100,12 +100,12 @@ "score": 0 }, "knoxiboy": { - "reviews": 5, - "score": 14 + "reviews": 6, + "score": 19 }, "KUMARNiru007": { - "reviews": 18, - "score": 73 + "reviews": 25, + "score": 108 }, "lourduradjou": { "reviews": 2, @@ -120,8 +120,8 @@ "score": 0 }, "Maxd646": { - "reviews": 0, - "score": 0 + "reviews": 3, + "score": 10 }, "MAYANKSHARMA01010": { "reviews": 0, @@ -148,8 +148,8 @@ "score": 7 }, "nitinog10": { - "reviews": 13, - "score": 54 + "reviews": 15, + "score": 64 }, "oasis-parzival": { "reviews": 0, From b9ac12eb7aee5262b0529faf9f7c2380d525af45 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 08:08:50 +0530 Subject: [PATCH 09/45] docs: backfill contributors (#1896) Co-authored-by: S3DFX-CYBER <213652949+S3DFX-CYBER@users.noreply.github.com> Signed-off-by: shravanithouta108 --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 7e92934dbc..c187312a9b 100644 --- a/README.md +++ b/README.md @@ -605,6 +605,7 @@ These mentors help guide and review contributions for the GSSoC program: Akshayaqueen Ashish241 Ashusf90 +Ashvin-KS Ayushi-hi Ayushia5 Bushra-gh @@ -615,6 +616,7 @@ These mentors help guide and review contributions for the GSSoC program: Dhruvil135 Dhruvil20060 Dj-Shortcut +G-Ganesh83 HarshaVardhan31012007 Harshith1702 Harxhit @@ -630,6 +632,7 @@ These mentors help guide and review contributions for the GSSoC program: Manav5234 MehtabSandhu11 Namish06 +Nightkilller Nirula23 OmkarAKadam Pallavi-vi-1234 @@ -648,9 +651,12 @@ These mentors help guide and review contributions for the GSSoC program: TarunyaProgrammer ThePrabhu Trrr10 +UjsGit VaibhavMP Vedhant26 Vishee02 +Vrinda-28 +YLaxmikanth Yashvijain1234 a638011 aasthakhatri11 @@ -676,10 +682,12 @@ These mentors help guide and review contributions for the GSSoC program: dishamaurya081-create garimatiwari1912-alt gloooomed +imayuss jatinrwt01 kejriwalkaushal04 kiranShamsHere maanyadanayak +maitrak18 mdudhe2007 meghna-cs mohanteja781112 @@ -696,12 +704,15 @@ These mentors help guide and review contributions for the GSSoC program: prisha-sh rajdeep-yadav riddhima25bet10005-a11y +riddhimagupta2 riyanshigupta890-cloud rudra1337-dev +saiprasad578 saumyasargam shivam-kakkar shravanithouta108 shreyjoshi73 +shrutisd739 shrutssss srishav3 syedrazamd From a5bf07b7bee6858c78fbd5715c53f450681afe71 Mon Sep 17 00:00:00 2001 From: shreyjoshi73 Date: Wed, 17 Jun 2026 08:16:24 +0530 Subject: [PATCH 10/45] fix: add OpenWISP mentors, channels and mailing lists (#1061) * fix: add OpenWISP mentors, channels & mailing lists (closes #381) Signed-off-by: Shrey * fix: fix OpenWISP schema - add githubUrl to mentors and fix status Signed-off-by: Shrey * fix: update OpenWISP channels with correct matrix URL and schema Signed-off-by: Shrey --------- Signed-off-by: Shrey Co-authored-by: nitin10 Co-authored-by: Savio Dsouza Signed-off-by: shravanithouta108 --- data/mentors.json | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/data/mentors.json b/data/mentors.json index 2830564e39..aa49e4771f 100644 --- a/data/mentors.json +++ b/data/mentors.json @@ -3549,16 +3549,36 @@ "status": "ok", "lastFetched": "2026-05-09T16:09:12.548Z" }, - "OpenWISP": { - "org": "OpenWISP", - "ideasUrl": "https://github.com/openwisp/gsoc-ideas", - "channels": [], - "mentors": [], - "mailingLists": [], - "tip": "", - "status": "fetch-failed", - "lastFetched": "2026-05-09T16:09:12.548Z" - }, +"OpenWISP": { + "org": "OpenWISP", + "ideasUrl": "https://openwisp.io/docs/dev/developer/gsoc-ideas-2026.html", + "channels": [ + { + "type": "matrix", + "url": "https://matrix.to/#/#openwisp_development:gitter.im", + "label": "OpenWISP Dev Chat" + } + ], + "mentors": [ + { + "name": "Federico Capoano", + "github": "nemesifier", + "githubUrl": "https://github.com/nemesifier" + }, + { + "name": "Gagan Deep", + "github": "pandafy", + "githubUrl": "https://github.com/pandafy" + } + ], + "mailingLists": [ + "https://groups.google.com/d/forum/openwisp", + "https://groups.google.com/g/openwisp-gsoc" + ], + "tip": "", + "status": "ok", + "lastFetched": "2026-05-09T16:09:12.548Z" +}, "Oppia Foundation": { "org": "Oppia Foundation", "ideasUrl": "https://github.com/oppia/oppia/wiki/Google-Summer-of-Code-2026", From a98546be220eab9a5e43c97ac306a7222891fa32 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 09:22:40 +0530 Subject: [PATCH 11/45] chore(deps-dev): bump js-yaml from 4.1.1 to 4.2.0 (#1898) Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.1.1 to 4.2.0. - [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodeca/js-yaml/commits) --- updated-dependencies: - dependency-name: js-yaml dependency-version: 4.2.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Signed-off-by: shravanithouta108 --- package-lock.json | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3e820a6678..b4f0a5038e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1528,10 +1528,20 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" From 857d10589e6dd9cc7596f3cd181aaec22d40adba Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 09:23:45 +0530 Subject: [PATCH 12/45] chore: update mentor leaderboard (#1897) Co-authored-by: S3DFX-CYBER <213652949+S3DFX-CYBER@users.noreply.github.com> Co-authored-by: Savio Dsouza Signed-off-by: shravanithouta108 --- .github/reviewers/mentor-leaderboard.md | 4 ++-- .github/reviewers/mentor-stats.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/reviewers/mentor-leaderboard.md b/.github/reviewers/mentor-leaderboard.md index 3409f65d73..3e7128fe5d 100644 --- a/.github/reviewers/mentor-leaderboard.md +++ b/.github/reviewers/mentor-leaderboard.md @@ -6,7 +6,7 @@ |------|--------|---------|-------| | ๐Ÿฅ‡ | @KUMARNiru007 | 25 | 108 | | ๐Ÿฅˆ | @TarunyaProgrammer | 19 | 75 | -| ๐Ÿฅ‰ | @nitinog10 | 15 | 64 | +| ๐Ÿฅ‰ | @nitinog10 | 16 | 69 | | 4 | @Anushreebasics | 17 | 45 | | 5 | @4f4d | 7 | 37 | | 6 | @deepak0x | 7 | 26 | @@ -63,4 +63,4 @@ | 57 | @uddalak2005 | 0 | 0 | | 58 | @vanshaggarwal07 | 0 | 0 | -Last updated: Wed, 17 Jun 2026 02:34:31 GMT +Last updated: Wed, 17 Jun 2026 02:49:00 GMT diff --git a/.github/reviewers/mentor-stats.json b/.github/reviewers/mentor-stats.json index 06ddbd1bc1..667cefaa4b 100644 --- a/.github/reviewers/mentor-stats.json +++ b/.github/reviewers/mentor-stats.json @@ -148,8 +148,8 @@ "score": 7 }, "nitinog10": { - "reviews": 15, - "score": 64 + "reviews": 16, + "score": 69 }, "oasis-parzival": { "reviews": 0, From 830325495c8c7836b76deb57d4fba223d600d368 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 09:55:08 +0530 Subject: [PATCH 13/45] chore: update mentor leaderboard (#1899) Co-authored-by: S3DFX-CYBER <213652949+S3DFX-CYBER@users.noreply.github.com> Signed-off-by: shravanithouta108 --- .github/reviewers/mentor-leaderboard.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/reviewers/mentor-leaderboard.md b/.github/reviewers/mentor-leaderboard.md index 3e7128fe5d..108f83cce7 100644 --- a/.github/reviewers/mentor-leaderboard.md +++ b/.github/reviewers/mentor-leaderboard.md @@ -63,4 +63,4 @@ | 57 | @uddalak2005 | 0 | 0 | | 58 | @vanshaggarwal07 | 0 | 0 | -Last updated: Wed, 17 Jun 2026 02:49:00 GMT +Last updated: Wed, 17 Jun 2026 03:57:34 GMT From 86260a17ffbe52e279f89924b1e66f49118e8a02 Mon Sep 17 00:00:00 2001 From: LEELA SOWMYA Date: Fri, 19 Jun 2026 10:20:23 +0530 Subject: [PATCH 14/45] feat: add live word and character counter to proposal builder (#1851) * feat: add live word and character counter to proposal builder Signed-off-by: Soquixx * fix: move proposal stats outside autosave label Signed-off-by: Soquixx * fix:add address accesibility review feedback Signed-off-by: Soquixx * fix: resolve review feedback for proposal builder Signed-off-by: Soquixx * fix: resolve review feedback for proposal builder Signed-off-by: Soquixx * fix: update proposal counter Signed-off-by: Soquixx --------- Signed-off-by: Soquixx Co-authored-by: Savio Dsouza Signed-off-by: shravanithouta108 --- index.html | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/index.html b/index.html index 173a43af31..4656323730 100644 --- a/index.html +++ b/index.html @@ -1948,6 +1948,7 @@

Organization Proposal

Enable autosave

Drafts are stored only on this device when autosave is enabled.

+
+ +
+ G +
+ + Org Finder + +
- + +

Privacy Policy

@@ -88,7 +125,8 @@

5. Changes to This Policy

+ - + \ No newline at end of file From 3dc8d60be88d8f3e6ec6efadef45625eee1e5797 Mon Sep 17 00:00:00 2001 From: Kumar Nirupam <145343368+KumarNirupam1@users.noreply.github.com> Date: Tue, 23 Jun 2026 09:33:48 +0530 Subject: [PATCH 18/45] update gssoc-mentor username (#1942) * Update mentors-leaderboard.yml Signed-off-by: Kumar Nirupam * Update mentor-leaderboard.md Signed-off-by: Kumar Nirupam * Update mentor-stats.json Signed-off-by: Kumar Nirupam --------- Signed-off-by: Kumar Nirupam Signed-off-by: shravanithouta108 --- .github/reviewers/mentor-leaderboard.md | 2 +- .github/reviewers/mentor-stats.json | 4 ++-- .github/workflows/mentors-leaderboard.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/reviewers/mentor-leaderboard.md b/.github/reviewers/mentor-leaderboard.md index 108f83cce7..8c5881eac0 100644 --- a/.github/reviewers/mentor-leaderboard.md +++ b/.github/reviewers/mentor-leaderboard.md @@ -4,7 +4,7 @@ | Rank | Mentor | Reviews | Score | |------|--------|---------|-------| -| ๐Ÿฅ‡ | @KUMARNiru007 | 25 | 108 | +| ๐Ÿฅ‡ | @KumarNirupam1 | 25 | 108 | | ๐Ÿฅˆ | @TarunyaProgrammer | 19 | 75 | | ๐Ÿฅ‰ | @nitinog10 | 16 | 69 | | 4 | @Anushreebasics | 17 | 45 | diff --git a/.github/reviewers/mentor-stats.json b/.github/reviewers/mentor-stats.json index 667cefaa4b..d4f937612f 100644 --- a/.github/reviewers/mentor-stats.json +++ b/.github/reviewers/mentor-stats.json @@ -103,7 +103,7 @@ "reviews": 6, "score": 19 }, - "KUMARNiru007": { + "KumarNirupam1": { "reviews": 25, "score": 108 }, @@ -231,4 +231,4 @@ "reviews": 0, "score": 0 } -} \ No newline at end of file +} diff --git a/.github/workflows/mentors-leaderboard.yml b/.github/workflows/mentors-leaderboard.yml index 26e2043e2d..62e83a28bf 100644 --- a/.github/workflows/mentors-leaderboard.yml +++ b/.github/workflows/mentors-leaderboard.yml @@ -64,7 +64,7 @@ jobs: "kallal79", "KaranGupta2005", "knoxiboy", - "KUMARNiru007", + "KumarNirupam1", "lourduradjou", "lovestaco", "magic-peach", From 2987bf461b86d4c2e98c6013b98174660482e160 Mon Sep 17 00:00:00 2001 From: Nancy Dupare <139962672+NayansiDupare@users.noreply.github.com> Date: Fri, 26 Jun 2026 00:41:06 +0530 Subject: [PATCH 19/45] ci: fix dual-label PR classification for GSSoC+NSoC conflict (#1359) (#1409) * ci: fix dual-label PR classification for GSSoC+NSoC conflict (#1359) When a PR body or labels reference both GSSoC and NSoC simultaneously, the classification workflows applied both program labels and routing labels, causing inconsistent pipeline behavior. Changes: - pr-validator.yml: add dual-label guard (GSSoC priority); apply only one effective program label; post conflict-resolution comment to PR - pr-welcome-bot.yml: normalize isGssoc/isNsoc before routing-label assignment; change if/if to if/else-if for mutual exclusivity; add dual-label notice in welcome comment - pr-stage-manage.yml: normalize isGSSOC/isNSOC early (before all stage logic) so stage2Passed, typeBadge, and stage comments all use a single consistent classification; annotate typeBadge on auto-resolve - program-classification-validator.yml: add dual-label guard right after program detection so that the validator also uses a single consistent classification (GSSoC priority) instead of failing with multiple program classifications detected Single-label PRs are fully unaffected. No new secrets, scripts, or external dependencies introduced. Closes #1359 Signed-off-by: Nancy Dupare * ci: remove legacy label-adding block in pr-validator.yml Signed-off-by: Nancy Dupare * Update .github/workflows/program-classification-validator.yml Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Signed-off-by: Nancy Dupare Co-authored-by: Savio Dsouza Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: shravanithouta108 --- .github/workflows/pr-stage-manage.yml | 41 +++++++++++--- .github/workflows/pr-validator.yml | 55 +++++++++++++++++-- .github/workflows/pr-welcome-bot.yml | 33 +++++++++-- .../program-classification-validator.yml | 33 +++++++---- 4 files changed, 131 insertions(+), 31 deletions(-) diff --git a/.github/workflows/pr-stage-manage.yml b/.github/workflows/pr-stage-manage.yml index b0df18d57f..8f440a3d15 100644 --- a/.github/workflows/pr-stage-manage.yml +++ b/.github/workflows/pr-stage-manage.yml @@ -17,7 +17,6 @@ permissions: pull-requests: write issues: write contents: read - metadata: write concurrency: group: pr-stage-manager-${{ github.event.pull_request.number || github.event.review.pull_request.number }} @@ -27,10 +26,6 @@ jobs: stage-manager: runs-on: ubuntu-latest - # Prevent bot recursion - if: | - github.actor != 'github-actions[bot]' - steps: - name: Evaluate PR Pipeline uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 #v9.0.0 @@ -39,6 +34,12 @@ jobs: # Use built-in token instead of PAT github-token: ${{ secrets.GITHUB_TOKEN }} script: | + // Prevent bot recursion โ€” skip execution if triggered by the github-actions bot + if (context.actor === 'github-actions[bot]') { + core.info('Skipping workflow run for github-actions[bot]'); + return; + } + const owner = context.repo.owner; const repo = context.repo.repo; @@ -90,12 +91,32 @@ jobs: // PROGRAM DETECTION // -------------------------------------------------- - const isGSSOC = + let isGSSOC = labels.includes('gssoc26'); - const isNSOC = + let isNSOC = labels.includes('nsoc26'); + // โ”€โ”€ Dual-label guard โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // If a PR somehow carries both gssoc26 and nsoc26 labels (e.g. from + // a conflicting PR body), normalise to GSSoC-only here so that ALL + // downstream stage logic (stage2Passed, typeBadge, comment text) + // uses a single, consistent program classification. + // Single-label PRs are entirely unaffected by this block. + const isDualLabel = isGSSOC && isNSOC; + if (isDualLabel) { + isNSOC = false; // GSSoC takes priority โ€” discard NSoC classification + core.warning(`PR #${prNumber}: dual-label detected (gssoc26 + nsoc26) โ€” resolved to GSSoC priority`); + + // Remove the conflicting label from GitHub to prevent downstream workflows from reading stale state + try { + await removeLabel('nsoc26'); + } catch (e) { + core.warning(`Failed to remove 'nsoc26' label from GitHub: ${e.message}`); + } + } + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // -------------------------------------------------- // STAGE 1 // -------------------------------------------------- @@ -392,9 +413,13 @@ jobs: // BADGE // -------------------------------------------------- + // When a dual-label conflict was auto-resolved, append a notice to + // the badge so maintainers can see that the priority rule was applied. const typeBadge = isGSSOC - ? '> ๐ŸŸข **GSSOC PR**' + ? isDualLabel + ? '> ๐ŸŸข **GSSOC PR** _(dual-label conflict auto-resolved โ€” GSSoC priority applied)_' + : '> ๐ŸŸข **GSSOC PR**' : isNSOC ? '> ๐Ÿ”ต **NSOC PR**' : '> โšช **Standard PR**'; diff --git a/.github/workflows/pr-validator.yml b/.github/workflows/pr-validator.yml index b8a0ff4c2a..65d435cb51 100644 --- a/.github/workflows/pr-validator.yml +++ b/.github/workflows/pr-validator.yml @@ -123,10 +123,18 @@ jobs: ); } + // โ”€โ”€ Dual-label guard โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // If the PR body mentions BOTH programs, apply GSSoC priority and + // discard the NSoC signal. A conflict comment is posted to inform + // the contributor. Single-label PRs are unaffected by this block. + const isDualLabel = mentionsGSSOC && mentionsNSOC; + const effectiveGSSOC = mentionsGSSOC; // GSSoC always wins + const effectiveNSOC = mentionsNSOC && !mentionsGSSOC; // NSoC only when GSSoC absent + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // ------------------------------------------------ // LABEL MANAGEMENT // ------------------------------------------------ - try { for (const label of ['nsoc26', 'gssoc26']) { @@ -143,8 +151,8 @@ jobs: } catch (e) {} } - if (mentionsNSOC) { - + // Apply only the effective (post-resolution) program label + if (effectiveNSOC) { await github.rest.issues.addLabels({ owner, repo, @@ -153,8 +161,7 @@ jobs: }); } - if (mentionsGSSOC) { - + if (effectiveGSSOC) { await github.rest.issues.addLabels({ owner, repo, @@ -163,6 +170,44 @@ jobs: }); } + // Notify contributor when a dual-label conflict was auto-resolved + if (isDualLabel) { + // Fetch existing comments to prevent duplicate spam on subsequent workflow triggers + const comments = await github.rest.issues.listComments({ + owner, + repo, + issue_number: prNumber, + per_page: 100 + }); + + const marker = ''; + const hasNotice = comments.data.some(c => c.body && c.body.includes(marker)); + + if (!hasNotice) { + const dualLabelComment = + `${marker}\n` + + `## โš ๏ธ Dual Program Label Detected\n\n` + + `Hi @${username},\n\n` + + `Your PR description mentions **both GSSOC and NSOC**.\n\n` + + `| Detected Labels | Resolution |\n` + + `|----------------|------------|\n` + + `| \`gssoc\`, \`nsoc\` | **GSSoC priority applied** โ€” classified as a GSSoC PR |\n\n` + + `> โ„น๏ธ Only one program label may be active at a time. ` + + `GSSoC takes priority when both are present.\n\n` + + `If this is incorrect, please:\n` + + `1. Remove the unintended program keyword from your PR description\n` + + `2. Push a new commit or re-open the PR to re-trigger classification\n\n` + + `๐Ÿ“– See our [Contributing Guide](https://github.com/${owner}/${repo}/blob/main/CONTRIBUTING.md) for labeling details.`; + + await github.rest.issues.createComment({ + owner, repo, issue_number: prNumber, body: dualLabelComment + }); + core.info(`PR #${prNumber}: Posted dual-label conflict comment.`); + } else { + core.info(`PR #${prNumber}: Dual-label conflict comment already exists. Skipping duplicate.`); + } + core.warning(`Dual-label conflict on PR #${prNumber} โ€” resolved to GSSoC priority`); + } } catch (e) { core.warning(`Label operation failed: ${e.message}`); diff --git a/.github/workflows/pr-welcome-bot.yml b/.github/workflows/pr-welcome-bot.yml index 23c63e0901..ba91f8874a 100644 --- a/.github/workflows/pr-welcome-bot.yml +++ b/.github/workflows/pr-welcome-bot.yml @@ -48,16 +48,27 @@ jobs: (pr.labels || []) .map(l => l.name.toLowerCase()); - const isGssoc = + let isGssoc = existingLabels.includes('gssoc26') || branch.includes('gssoc') || body.includes('gssoc'); - const isNsoc = + let isNsoc = existingLabels.includes('nsoc26') || branch.includes('nsoc') || body.includes('nsoc'); + // โ”€โ”€ Dual-label guard โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // When both program signals are present, apply GSSoC priority and + // suppress NSoC so exactly one routing label is added downstream. + // Single-label PRs are entirely unaffected by this block. + const isDualLabel = isGssoc && isNsoc; + if (isDualLabel) { + isNsoc = false; // GSSoC wins โ€” discard NSoC signal + core.warning(`Dual-label conflict on PR #${number} โ€” resolved to GSSoC priority`); + } + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // -------------------------------------------------- // IDEMPOTENCY // -------------------------------------------------- @@ -91,7 +102,16 @@ jobs: let programNote = ''; - if (isGssoc) { + if (isDualLabel) { + // Dual-label conflict was detected โ€” inform the contributor + programNote = + `> ๐ŸŸข **GSSOC PR detected** โ€” your PR will be routed through the GSSOC mentor review pipeline.\n` + + `>\n` + + `> โš ๏ธ **Dual-label notice:** Both GSSOC and NSOC signals were detected in your PR. ` + + `GSSoC takes priority and your PR has been classified as a **GSSoC PR** only. ` + + `If this is incorrect, please remove the unintended program keyword from your PR description and push a new commit.`; + + } else if (isGssoc) { programNote = `> ๐ŸŸข **GSSOC PR detected** โ€” your PR will be routed through the GSSOC mentor review pipeline.`; @@ -198,11 +218,12 @@ jobs: const labelsToAdd = []; + // isGssoc / isNsoc have already been normalised by the dual-label + // guard above, so at most one of these branches will execute. if (isGssoc) { labelsToAdd.push('gssoc-review'); - } - - if (isNsoc) { + } else if (isNsoc) { + // else-if guarantees mutual exclusivity even without the guard labelsToAdd.push('nsoc-review'); } diff --git a/.github/workflows/program-classification-validator.yml b/.github/workflows/program-classification-validator.yml index 5ceac5eaf9..95657f51b4 100644 --- a/.github/workflows/program-classification-validator.yml +++ b/.github/workflows/program-classification-validator.yml @@ -22,21 +22,13 @@ jobs: validate-program: name: Validate PR Program Classification - if: | - github.event.pull_request.draft == false && - github.actor != 'github-actions[bot]' + if: github.event.pull_request.draft == false runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2 - - - name: Setup Node.js - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e #v6.4.0 - with: - node-version: "24" - + # No checkout/setup-node needed for actions/github-script-only workflow - name: Validate classification uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 #v9.0.0 env: @@ -45,8 +37,11 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} script: | - const fs = require('fs'); - const path = require('path'); + // Prevent bot recursion โ€” skip execution if triggered by the github-actions bot + if (context.actor === 'github-actions[bot]') { + core.info('Skipping workflow run for github-actions[bot]'); + return; + } const owner = context.repo.owner; const repo = context.repo.repo; @@ -144,7 +139,21 @@ jobs: const detectedPrograms = detectProgram(); + // โ”€โ”€ Dual-label guard โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // If both GSSoC and NSoC signals/labels are detected, apply the + // priority rule (GSSoC takes priority) and discard the NSoC signal + // so exactly one program classification is validated and applied. + if (detectedPrograms.includes('gssoc') && detectedPrograms.includes('nsoc')) { + const nsocIndex = detectedPrograms.indexOf('nsoc'); + if (nsocIndex > -1) { + detectedPrograms.splice(nsocIndex, 1); + } + core.warning('Dual-label program classification detected (gssoc + nsoc) โ€” resolved to GSSoC priority'); + } + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // ---------------------------------------- + // LABEL HELPERS // ---------------------------------------- From 37324f4f85610e64e8dfac3bbfc35b21658918ba Mon Sep 17 00:00:00 2001 From: PseudoGenius Date: Fri, 26 Jun 2026 15:48:43 +0530 Subject: [PATCH 20/45] feat: added landing page for better navigation (#1854) * feat:added landing page Signed-off-by: arpit2006 * feat:added changes in landing page Signed-off-by: arpit2006 * feat:added changes in landing page Signed-off-by: arpit2006 * feat:added changes in landing page Signed-off-by: arpit2006 * feat:added changes in landing page Signed-off-by: arpit2006 * feat:added changes in landing page Signed-off-by: arpit2006 * feat:added changes in landing page Signed-off-by: arpit2006 * feat:added changes in landing page Signed-off-by: arpit2006 * feat:made changes in landing page accordingly Signed-off-by: arpit2006 * feat:made changes in package json accordingly Signed-off-by: arpit2006 * feat:made changes Signed-off-by: arpit2006 * feat:made changes according to reviews Signed-off-by: arpit2006 * feat:made changes according to reviews Signed-off-by: arpit2006 * refactor: reduce cognitive complexity in landing page stats Signed-off-by: arpit2006 * refactor: reduce cognitive complexity in landing page stats Signed-off-by: arpit2006 * refactor: reduce cognitive complexity in landing Signed-off-by: arpit2006 * refactor: reduce cognitive complexity in landing Signed-off-by: arpit2006 * refactored landing html Signed-off-by: arpit2006 --------- Signed-off-by: arpit2006 Co-authored-by: Savio Dsouza Signed-off-by: shravanithouta108 --- landing.html | 1121 +++++++++++++++++++++++++++++++++++++ package.json | 10 +- src/js/landing.js | 412 ++++++++++++++ src/js/tailwind-config.js | 60 ++ src/landing.css | 320 +++++++++++ sw.js | 8 +- vercel.json | 3 + 7 files changed, 1928 insertions(+), 6 deletions(-) create mode 100644 landing.html create mode 100644 src/js/landing.js create mode 100644 src/js/tailwind-config.js create mode 100644 src/landing.css diff --git a/landing.html b/landing.html new file mode 100644 index 0000000000..c66323ee8b --- /dev/null +++ b/landing.html @@ -0,0 +1,1121 @@ + + + + + + + + + + FindMyGSoC โ€” Discover GSoC 2026 Orgs & Live GitHub Issues + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ + + + + + + + +
+
+ + + +
+ + +
+ + + + + +
+
+
+
+
+ +
+ + +
+ + + + + + GSoC 2026 · + Empowering Open Source Contributors + +
+ + +

+ Your Gateway to + Google Summer of Code 2026 +

+ + +

+ Discover GSoC organizations, match your skills with AI, track live Good First Issues, + and draft winning proposals — all in one place. +

+ + +
+ + + + rocket_launch + Launch Application + arrow_forward + + + Explore Features + south + +
+ + +
+ +
+
+
+ A
+
+ K
+
+ R
+
+ S
+
+ +
+
+ Contributors using FindMyGSoC +
+ + + +
+ + verified + Free & open-source + + + check_circle + No sign-up required + + + groups + 184+ organizations + +
+
+ + +
+ +
+
+ + +
+ +
+ + + +
+ + + + + + + + + + + +
+ + + + + Live +
+
+ +
+ +
+
+
+ travel_explore +
+ 184 + Orgs +
+

Organization Search

+

Filter by 30+ languages across 15+ + domains with smart tagging.

+
+ Python + Rust + Go + +28 +
+
+ +
+
+
+ psychology +
+ AI + Match +
+

AI Skill Matching

+

GitHub username or CV → + instant ranked org recommendations.

+
+ 94% + · CNCF + 87% + · NumFOCUS +
+
+ +
+
+
+ bug_report +
+ Live + GFIs +
+

Good First Issues

+

Updated every 6h · + beginner-friendly, ready to claim.

+
+ #4392 + docs + 1h + ago +
+
+
+ + +
+
+ + + All systems live + + | + Last synced 3 min ago +
+
+ PWA + Ready + Offline + OK +
+
+
+
+ + +
+ +
+ Program Overview +

+ What is Google Summer of Code?

+

+ A global, online initiative by Google that connects new contributors with openโ€‘source organizations for a + paid, mentored summer coding project. +

+
+ + +
+ + +
+

+ GSoC gives university students and independent developers the chance to spend a summer writing real + production-grade code for established open source projects โ€” under the direct guidance of experienced + mentors โ€” while earning a Google-sponsored + stipend. +

+ + +
+
+

20+

+

Years + Running

+
+
+

800+

+

Orgs Ever +

+
+
+

40K+

+

+ Contributors

+
+
+ + + open_in_new + Official GSoC Website + +
+ + +
+ +
+
+ + +
+
+ 1 +
+
+
+ calendar_month +

12+ Weeks of Coding

+
+

+ Work on a structured, multi-week project over summer. Successful participants receive a stipend scaled + to their country. +

+
+
+ + +
+
+ 2 +
+
+
+ groups +

1-on-1 Mentorship

+
+

+ Partner with experienced developers from orgs like Python SF, Linux Foundation, CNCF, and Apache. +

+
+
+ + +
+
+ 3 +
+
+
+ terminal +

Real-world Experience

+
+

+ Write production code, ship pull requests, master Git workflows, and build a portfolio that stands out. +

+
+
+
+
+
+ + +
+
+
+ +
+
184+
+

+ Organizations

+
+ +
+
60+
+

+ Veterans (10y+)

+
+ +
+
35+
+

+ Newcomer Friendly

+
+ +
+
30+
+

+ Tech Stacks

+
+
+
+
+ + +
+
+ Discover Features +

+ Complete Suite for GSoC Applicants

+

Everything you need to + discover perfect programs, connect with mentors, and plan your open-source journey.

+
+ + +
+ +
+
+
+ travel_explore +
+

Live Organization Directory

+

Search and filter all 184+ + organizations based on participation history, codebase complexity, competition level, and tech stack tags. +

+
+
+ Moderate + Competition + Beginner + Friendly + 10+ + Years History +
+
+ + +
+
+
+ psychology +
+

Smart AI Recommendations

+

Connect your GitHub account or + upload your developer profile. Our AI algorithms analyze your tech stack and recommend the organizations + where you stand the highest chance of acceptance.

+
+
+ offline_bolt + Instant Profile matching +
+
+ + +
+
+ bug_report +
+

Live Good First Issues

+

No manual search needed. Explore live + beginner-friendly issues from active repos, synced every 6 hours.

+
+ + +
+
+ forum +
+

Mentor Contact Directory

+

Direct links to organization Slack + channels, Discord servers, IRC channels, and Zulip communities.

+
+ + +
+
+ compare_arrows +
+

Side-by-Side Comparison

+

Select up to 3 organizations to compare + years active, active contributors, languages, and open issues.

+
+ + +
+
+
+ edit_note +
+

Markdown Proposal Builder

+

Draft your GSoC project proposal + directly inside the application. Includes live preview rendering, auto-save drafts, and one-click + PDF/Markdown export.

+
+
+ article + Ready-to-submit exports +
+
+ + +
+
+
+ wifi_off +
+

100% Client-Side & Offline

+

Fully cached using Service Workers. + Search, filter, read contributor guides, and edit proposal drafts completely offline without any internet + connection.

+
+
+ cloud_done + Privacy-first. Your drafts stay local. +
+
+
+
+ + +
+ +
+ Competitive Edge +

+ Why FindMyGSoC Over Others?

+

+ Most tools give you a list. We give you a complete platform to discover, analyze, and apply. +

+
+ + +
+ + + + + + + + + + + + +
+ Feature +
+ FindMyGSoC + This + tool +
+
GSoC Portal + gsocorganizations.dev + GitHub / Manual +
+
+ + +
+
+ psychology +

AI-First Discovery

+

Paste your GitHub or CV and get org + matches scored by skill overlap โ€” no manual browsing needed.

+
+
+ shield_lock +

100% Private

+

Everything runs in your browser. Your + proposals, bookmarks, and history never leave your device.

+
+
+ bolt +

Instant & Free

+

No account, no paywall. Open the app and + start exploring 184 organizations in under 5 seconds.

+
+
+ code +

Open Source

+

Built in the open on GitHub. Contribute + features, report issues, or fork and self-host โ€” community-driven.

+
+
+
+ + +
+ +
+ Our Mission +

+ About FindMyGSoC

+

+ Empowering university students and independent developers to confidently navigate and succeed in Google Summer + of Code. +

+
+ + +
+ +
+ + + + + + Open Source Initiative + + +

+ Streamlining GSoC Discovery for Every + Developer +

+ +

+ Google Summer of Code is a career-defining program, but the initial phase of finding the right organization, + understanding their codebase complexity, and filtering by relevant tech stacks can feel like a needle in a + haystack. +

+ +

+ FindMyGSoC is an independent community dashboard designed to bridge this gap. By combining automated data + aggregation with a sleek, privacy-focused dashboard, we help you match your skills, monitor active Good + First Issues, and draft winning proposals completely client-side. +

+ +
+
+ + check + + 100% Free & Open Source +
+
+ + check + + No Login or Data Collected +
+
+ + check + + PWA Support / Fully Offline +
+
+ + check + + Synced Live Every 6 Hours +
+
+
+ + +
+ +
+
+ + +
+
+ groups +
+

Community-First

+

+ Built and optimized by active open-source contributors to help you find mentorship and master your + application. +

+
+ + +
+
+ shield +
+

Zero-Data Tracking

+

+ Your proposals, search filters, bookmarks, and CV matches are stored locally on your device. Zero servers + involved. +

+
+ + +
+
+ psychology +
+

AI-Driven Matching

+

+ Instantly analyze your GitHub profile or resume to discover matching tech stacks and pinpoint + high-probability orgs. +

+
+ + +
+
+ sync +
+

Real-Time Sync

+

+ Live updates for organization participation archives and beginner-friendly Good First Issues, synced every + 6 hours. +

+
+
+
+
+ + +
+ + +
+
+
+
+ +
+ + +
+ + + Your Journey + + +

+ How FindMyGSoC + Empowers + You +

+

+ From first search to accepted proposal โ€” a guided, three-step journey built entirely around you. +

+
+ + +
+ + + + + +
+ 01 +
+ travel_explore +
+

+ Discover & Match

+

+ Browse 184+ organizations or paste your GitHub username โ€” our AI instantly ranks every org by how well it + fits your unique tech stack. +

+
+ + filter_list Smart Filters + + + psychology AI Ranking + + + bookmark Watchlist + +
+
+ + +
+ 02 +
+ compare_arrows +
+

Engage + & Compare

+

+ Line up to 3 orgs side-by-side, dive into mentor Slack & Discord channels, and claim live Good First + Issues to build your contribution record. +

+
+ + compare Side-by-Side + + + bug_report Live GFIs + + + forum Mentor Channels + +
+
+ + +
+ 03 +
+ edit_note +
+

Draft + & Apply

+

+ Write your proposal in the built-in Markdown editor with live preview and auto-save, then export a + polished PDF to submit straight to the GSoC portal. +

+
+ + edit_note MD Editor + + + preview Live Preview + + + picture_as_pdf PDF Export + +
+
+
+ + + + +
+
+ + +
+
+ Got Questions? +

+ Frequently Asked Questions

+

Everything you need to know about the + platform and the GSoC timeline.

+
+ +
+
+ + +
+
+ +
+
+ +

Ready to Land Your + GSoC Slot?

+

Launch the application, + compare organizations, filter tech stacks, and find good first issues instantly.

+ + Launch Finder Dashboard + rocket_launch + +
+
+
+ + + + + + + + + + + + + diff --git a/package.json b/package.json index 8d3c6cbade..8bb87eb74d 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,9 @@ "description": "GSoC 2026 Organisation Finder", "private": true, "scripts": { - "lint:js": "eslint src/js/app.js src/js/org.js agent/scripts/validate-ideas-urls.js agent/scripts/extract-mentors.js agent/scripts/validate-mentors.js api/github.js src/js/githubAnalyzer.js src/js/skillExtractor.js src/js/recommender.js src/js/recommendation-ui.js src/js/footer.js", - "lint:css": "stylelint src/styles.css", - "lint:html": "htmlhint --config .htmlhintrc index.html privacy.html", + "lint:js": "eslint src/js/app.js src/js/org.js src/js/tailwind-config.js agent/scripts/validate-ideas-urls.js agent/scripts/extract-mentors.js agent/scripts/validate-mentors.js api/github.js src/js/githubAnalyzer.js src/js/skillExtractor.js src/js/recommender.js src/js/recommendation-ui.js src/js/footer.js", + "lint:css": "stylelint src/styles.css src/landing.css", + "lint:html": "htmlhint --config .htmlhintrc index.html privacy.html landing.html", "lint": "npm run lint:js && npm run lint:css && npm run lint:html", "validate:data": "node agent/scripts/validate-generated-data.js", "test": "node --test tests/**/*.test.js" @@ -20,4 +20,6 @@ "dependencies": { "@vercel/speed-insights": "^2.0.0" } -} \ No newline at end of file + +} + diff --git a/src/js/landing.js b/src/js/landing.js new file mode 100644 index 0000000000..d5d671cb24 --- /dev/null +++ b/src/js/landing.js @@ -0,0 +1,412 @@ +/** + * landing.js โ€” FindMyGSoC Landing Page Scripts + * + * Responsibilities: + * - Safe client-side storage (theme persistence) with input validation + * - Data-driven rendering of the comparison table and FAQ accordion + * - Theme toggle & mobile menu wiring + * - Smooth-scroll with reduced-motion respect + * - Scroll-triggered animations for "How It Works" steps + * - Live statistics from ORGS data + * + * Security notes: + * - localStorage values are validated against an allowlist before use + * - All dynamic HTML is built via DOM APIs (createElement/textContent), + * never via innerHTML string concatenation, preventing stored-XSS + */ + +'use strict'; + +// โ”€โ”€ Safe Storage Wrapper โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +/** + * A thin wrapper around localStorage that: + * 1. Catches SecurityError / QuotaExceededError silently + * 2. Validates retrieved values against an optional allowlist so that a + * tampered storage entry can never inject arbitrary content into the DOM + */ +const SafeStorage = { + /** + * @param {string} key + * @param {string} value + */ + set(key, value) { + try { + window.localStorage.setItem(key, value); + } catch (_e) { + // Storage unavailable (private mode, quota exceeded, etc.) โ€” fail silently + } + }, + + /** + * @param {string} key + * @param {string[]} [allowList] If provided, the stored value is only + * returned when it appears in the list. + * @returns {string|null} + */ + get(key, allowList) { + try { + const raw = window.localStorage.getItem(key); + if (allowList && raw !== null && !allowList.includes(raw)) { + // Value is not in the allowlist โ€” treat as absent and clean up + window.localStorage.removeItem(key); + return null; + } + return raw; + } catch (_e) { + return null; + } + }, +}; + +// โ”€โ”€ Comparison Table Data โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +/** Single source of truth โ€” eliminates duplicated table rows in markup */ +const COMPARISON_DATA = [ + { feature: 'AI skill matching', fmg: 'primary', gsoc: false, god: false, gh: false }, + { feature: 'Live Good First Issues', fmg: 'primary', gsoc: false, god: false, gh: 'secondary' }, + { feature: 'Filter by tech & category', fmg: 'primary', gsoc: false, god: 'primary', gh: 'secondary' }, + { feature: 'Org side-by-side compare', fmg: 'primary', gsoc: false, god: false, gh: false }, + { feature: 'Proposal editor & PDF export', fmg: 'primary', gsoc: false, god: false, gh: false }, + { feature: 'Multi-year participation stats', fmg: 'primary', gsoc: false, god: 'primary', gh: false }, + { feature: 'Works offline (PWA)', fmg: 'primary', gsoc: false, god: false, gh: false }, + { feature: 'No login or signup needed', fmg: 'primary', gsoc: 'secondary', god: 'secondary', gh: 'secondary' }, +]; + +// โ”€โ”€ FAQ Data โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +/** Single source of truth โ€” eliminates duplicated accordion blocks in markup */ +const FAQ_DATA = [ + { + id: 'faq-ans-1', + q: 'What is FindMyGSoC?', + a: 'FindMyGSoC is an independent open-source dashboard designed to help GSoC applicants explore, analyze, and select optimal open-source organizations. It tracks organization participation stats, aggregates contact resources, tracks live Good First Issues, and features a markdown editor to draft proposals.', + }, + { + id: 'faq-ans-2', + q: 'How does the AI Recommendation matching work?', + a: 'The AI recommender parses your input\u2014either your GitHub username (to analyze your public repos, languages, and topics) or copy-pasted CV text. It extracts your key skills and computes a match score against the technologies, domains, and codebase complexity of the 184 GSoC organizations in our index.', + }, + { + id: 'faq-ans-3', + q: 'Where is my proposal draft data saved?', + a: 'Privacy first. All draft proposals, watchlist items, bookmarks, and search history are saved locally inside your browser\u2019s localStorage. No data is ever uploaded to external databases, keeping your ideas and work completely private.', + }, + { + id: 'faq-ans-4', + q: 'Is this platform affiliated with Google?', + a: 'No, FindMyGSoC is an independent open-source project created and maintained by the developer community. It is not affiliated with, sponsored by, or endorsed by Google LLC. Google Summer of Code is a registered trademark of Google LLC.', + }, +]; + +// โ”€โ”€ DOM Helpers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +/** + * Creates a single comparison-table cell using DOM APIs (no innerHTML). + * @param {string|false} value Tailwind color name or false for โœ— + * @returns {HTMLTableCellElement} + */ +function createComparisonCell(value) { + const td = document.createElement('td'); + td.className = 'py-3_5 px-4 text-center'; + + const icon = document.createElement('span'); + icon.className = 'material-symbols-outlined text-xl'; + + if (!value) { + icon.classList.add('text-zinc-300', 'dark:text-zinc-600'); + icon.textContent = 'cancel'; + } else { + icon.classList.add(`text-${value}`, 'icon-fill'); + icon.textContent = 'check_circle'; + } + + td.appendChild(icon); + return td; +} + +/** + * Renders the comparison table body from COMPARISON_DATA using DOM APIs. + * Appends rows to #comparisonRows. + */ +function renderComparisonRows() { + const tbody = document.getElementById('comparisonRows'); + if (!tbody) { return; } + + const fragment = document.createDocumentFragment(); + + for (const row of COMPARISON_DATA) { + const tr = document.createElement('tr'); + + const featureTd = document.createElement('td'); + featureTd.className = 'py-3_5 pr-6 text-zinc-700 dark:text-zinc-300 font-medium'; + featureTd.textContent = row.feature; + tr.appendChild(featureTd); + + tr.appendChild(createComparisonCell(row.fmg)); + tr.appendChild(createComparisonCell(row.gsoc)); + tr.appendChild(createComparisonCell(row.god)); + tr.appendChild(createComparisonCell(row.gh)); + + fragment.appendChild(tr); + } + + tbody.appendChild(fragment); +} + +/** + * Renders FAQ accordion items from FAQ_DATA using DOM APIs. + * Appends items to #faqList. + */ +function renderFaqList() { + const container = document.getElementById('faqList'); + if (!container) { return; } + + const fragment = document.createDocumentFragment(); + + const wrapClass = [ + 'border', 'border-zinc-200/70', 'dark:border-zinc-800/80', + 'bg-white/80', 'dark:bg-[#1a1108]/40', 'glass', + 'rounded-2xl', 'overflow-hidden', 'transition-colors', 'duration-300', + ]; + + const btnClass = [ + 'faq-trigger', 'flex', 'items-center', 'justify-between', 'w-full', + 'p-6', 'text-left', 'font-bold', 'text-sm', 'sm:text-base', + 'text-zinc-800', 'dark:text-zinc-100', + 'hover:bg-zinc-50/50', 'dark:hover:bg-zinc-800/35', + 'transition-colors', 'focus:outline-none', + ]; + + for (const item of FAQ_DATA) { + const wrapper = document.createElement('div'); + wrapper.classList.add(...wrapClass); + + // Button + const btn = document.createElement('button'); + btn.classList.add(...btnClass); + btn.setAttribute('aria-expanded', 'false'); + btn.setAttribute('aria-controls', item.id); + + const questionSpan = document.createElement('span'); + questionSpan.textContent = item.q; + btn.appendChild(questionSpan); + + const iconSpan = document.createElement('span'); + iconSpan.className = 'material-symbols-outlined faq-icon transition-transform duration-200'; + iconSpan.textContent = 'expand_more'; + btn.appendChild(iconSpan); + + // Answer panel + const panel = document.createElement('div'); + panel.id = item.id; + panel.className = 'faq-content transition-all duration-300'; + + const answerPara = document.createElement('p'); + answerPara.className = 'p-6 pt-0 text-xs sm:text-sm text-zinc-500 dark:text-zinc-400 leading-relaxed border-t border-zinc-100/50 dark:border-zinc-800/50'; + answerPara.textContent = item.a; + + panel.appendChild(answerPara); + wrapper.appendChild(btn); + wrapper.appendChild(panel); + fragment.appendChild(wrapper); + } + + container.appendChild(fragment); +} + +// โ”€โ”€ Theme โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +/** Syncs the theme toggle button icon and aria attributes to current state. */ +function updateThemeIcon() { + const btn = document.getElementById('themeToggleBtn'); + const icon = btn ? btn.querySelector('.material-symbols-outlined') : null; + if (!icon) { return; } + + const isDark = document.documentElement.classList.contains('dark'); + icon.textContent = isDark ? 'light_mode' : 'dark_mode'; + btn.setAttribute('aria-pressed', String(isDark)); + btn.setAttribute('aria-label', isDark ? 'Switch to light theme' : 'Switch to dark theme'); +} + +/** Toggles dark/light theme and persists the choice via SafeStorage. */ +function toggleTheme() { + const isDark = document.documentElement.classList.toggle('dark'); + SafeStorage.set('theme', isDark ? 'dark' : 'light'); + updateThemeIcon(); +} + +// โ”€โ”€ Mobile Menu โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +/** Toggles the mobile navigation drawer. */ +function toggleMenu() { + const menu = document.getElementById('mobileMenu'); + const btn = document.getElementById('menuBtn'); + if (!menu || !btn) { return; } + + const isExpanded = btn.getAttribute('aria-expanded') === 'true'; + menu.classList.toggle('hidden'); + btn.setAttribute('aria-expanded', String(!isExpanded)); + + const icon = btn.querySelector('.material-symbols-outlined'); + if (icon) { icon.textContent = !isExpanded ? 'close' : 'menu'; } +} + +// โ”€โ”€ Footer Patch โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +/** + * Applies CSS classes and rewrites in-page hrefs on the injected footer element. + * @param {Element} footer + */ +function applyFooterPatch(footer) { + footer.classList.add( + 'border-t', 'border-zinc-200', 'dark:border-zinc-800', + 'bg-white', 'dark:bg-[#0f0a05]', 'transition-colors', 'duration-300', + ); + footer.querySelectorAll('a').forEach((a) => { + const href = a.getAttribute('href'); + if (href === '#orgs' || href === '#timeline') { + a.setAttribute('href', `index.html${href}`); + } + }); +} + +/** + * Waits for the dynamically-injected footer then patches its links/classes. + * Uses MutationObserver so it fires exactly once with no polling overhead. + */ +function patchFooterLinks() { + const existing = document.querySelector('footer.premium-footer'); + if (existing) { applyFooterPatch(existing); return; } + + const observer = new MutationObserver((mutations) => { + for (const mutation of mutations) { + for (const node of mutation.addedNodes) { + if (node.nodeType !== 1) { continue; } + const footer = node.matches('footer.premium-footer') + ? node + : node.querySelector('footer.premium-footer'); + if (footer) { observer.disconnect(); applyFooterPatch(footer); return; } + } + } + }); + observer.observe(document.body, { childList: true, subtree: true }); +} + +// โ”€โ”€ Smooth Scroll โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +/** Wires smooth-scroll behaviour on all in-page anchor links. */ +function wireAnchorScroll() { + const NAVBAR_HEIGHT = 72; + document.querySelectorAll('a[href^="#"]').forEach((anchor) => { + anchor.addEventListener('click', function (e) { + const targetId = this.getAttribute('href').slice(1); + if (!targetId) { return; } + const target = document.getElementById(targetId); + if (!target) { return; } + e.preventDefault(); + + // Close mobile menu if open + const mobileMenu = document.getElementById('mobileMenu'); + if (mobileMenu && !mobileMenu.classList.contains('hidden')) { + toggleMenu(); + } + + const top = target.getBoundingClientRect().top + window.scrollY - NAVBAR_HEIGHT; + const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; + window.scrollTo({ top: Math.max(0, top), behavior: prefersReducedMotion ? 'auto' : 'smooth' }); + }); + }); +} + +// โ”€โ”€ Scroll-triggered Animations โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +/** Fade-in for "How It Works" step cards when they enter the viewport. */ +function initHiwAnimations() { + const steps = document.querySelectorAll('.hiw-step'); + if (!steps.length) { return; } + + const observer = new IntersectionObserver((entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + entry.target.style.opacity = '1'; + entry.target.style.transform = 'translateY(0)'; + observer.unobserve(entry.target); + } + }); + }, { threshold: 0.15 }); + + steps.forEach((step) => observer.observe(step)); +} + +// โ”€โ”€ Live Statistics โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +/** + * Calculates and renders live org statistics from the ORGS dataset. + * No-ops gracefully if ORGS is not available. + */ +function renderLiveStats() { + if (typeof ORGS === 'undefined' || !Array.isArray(ORGS)) { return; } + + const totalOrgs = ORGS.length; + const veteranOrgs = ORGS.filter((o) => o.years >= 10).length; + const newcomerOrgs = ORGS.filter((o) => o.years <= 3).length; + + /** @param {string} id @param {string} value */ + const setText = (id, value) => { + const el = document.getElementById(id); + if (el) { el.textContent = value; } + }; + + setText('statOrgs', `${totalOrgs}+`); + setText('statVeterans', `${veteranOrgs}+`); + setText('statNewcomers', `${newcomerOrgs}+`); + setText('org-count-text', `${totalOrgs}+ organizations`); + setText('dash-org-count', `${totalOrgs} Orgs`); + setText('footerOrgCount', String(totalOrgs)); + setText('footerVeteranOrgCount', String(veteranOrgs)); +} + +// โ”€โ”€ Bootstrap โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +// Sync theme icon immediately (theme class already applied by inline head script) +updateThemeIcon(); + +document.addEventListener('DOMContentLoaded', () => { + // Trigger hero entry animations + document.body.classList.add('hero-loaded'); + + // Wire interactive controls + const themeBtn = document.getElementById('themeToggleBtn'); + if (themeBtn) { themeBtn.addEventListener('click', toggleTheme); } + + const menuBtn = document.getElementById('menuBtn'); + if (menuBtn) { menuBtn.addEventListener('click', toggleMenu); } + + // Render data-driven sections + renderComparisonRows(); + renderFaqList(); + + // FAQ accordion โ€” event delegation on the dynamically rendered list + const faqList = document.getElementById('faqList'); + if (faqList) { + faqList.addEventListener('click', (e) => { + const btn = e.target.closest('.faq-trigger'); + if (!btn) { return; } + const isExpanded = btn.getAttribute('aria-expanded') === 'true'; + btn.setAttribute('aria-expanded', String(!isExpanded)); + }); + } + + // Patch footer after dynamic injection + patchFooterLinks(); + + // Smooth anchor scrolling + wireAnchorScroll(); + + // Scroll animations + initHiwAnimations(); + + // Live org statistics + renderLiveStats(); +}); diff --git a/src/js/tailwind-config.js b/src/js/tailwind-config.js new file mode 100644 index 0000000000..ab0b0a0d65 --- /dev/null +++ b/src/js/tailwind-config.js @@ -0,0 +1,60 @@ +/* global tailwind */ +tailwind.config = { + darkMode: "class", + theme: { + extend: { + screens: { + 'xs': '480px', + 'sm': '640px', + 'md': '768px', + 'lg': '1024px', + 'xl': '1280px', + '2xl': '1536px', + }, + colors: { + "surface-container-highest": "#e2e2e2", + "background": "#f9f9f9", + "surface-container": "#eeeeee", + "primary-container": "#f97316", + "surface": "#f9f9f9", + "surface-variant": "#e2e2e2", + "on-background": "#1a1c1c", + "on-primary": "#ffffff", + "on-surface": "#1a1c1c", + "error": "#ba1a1a", + "primary": "#9d4300", + "on-surface-variant": "#584237", + "outline": "#8c7164", + "surface-container-lowest": "#ffffff", + "secondary": "#006c47", + "secondary-container": "#8af5be", + "on-secondary-container": "#00714b", + "on-secondary": "#ffffff", + "surface-container-low": "#f3f3f3", + "surface-container-high": "#e8e8e8", + "surface-dim": "#dadada", + "primary-fixed": "#ffdbca", + "primary-fixed-dim": "#ffb690", + "error-container": "#ffdad6", + "on-error-container": "#93000a", + "on-secondary-fixed": "#002113", + "secondary-fixed-dim": "#71dba6", + "tertiary": "#5f5e5e", + "on-primary-container": "#582200", + "surface-tint": "#9d4300", + }, + fontFamily: { + "headline": ["Plus Jakarta Sans"], + "body": ["Plus Jakarta Sans"], + "label": ["Space Grotesk"], + "mono": ["Fira Code"] + }, + borderRadius: { + "DEFAULT": "0.25rem", + "lg": "0.5rem", + "xl": "0.75rem", + "full": "9999px" + }, + }, + }, +}; diff --git a/src/landing.css b/src/landing.css new file mode 100644 index 0000000000..4875eb7f50 --- /dev/null +++ b/src/landing.css @@ -0,0 +1,320 @@ +/* stylelint-disable property-no-vendor-prefix */ +html, +body { + scroll-behavior: smooth !important; +} + +.material-symbols-outlined { + font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24; + vertical-align: middle; +} + +.icon-fill { + font-variation-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 24; +} + +.glass { + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); +} + +.hero-bg { + background-image: + radial-gradient(ellipse 80% 50% at 50% -10%, rgba(244, 107, 14, 0.08) 0%, transparent 70%), + radial-gradient(circle at 10% 20%, rgba(244, 107, 14, 0.05) 0%, transparent 40%), + radial-gradient(circle at 90% 80%, rgba(0, 108, 71, 0.04) 0%, transparent 40%); +} + +.dark .hero-bg { + background-image: + radial-gradient(ellipse 80% 50% at 50% -10%, rgba(249, 115, 22, 0.12) 0%, transparent 70%), + radial-gradient(circle at 10% 20%, rgba(249, 115, 22, 0.08) 0%, transparent 50%), + radial-gradient(circle at 90% 80%, rgba(52, 211, 153, 0.05) 0%, transparent 50%); +} + +/* Hero gradient text โ€” reliable cross-browser, no clipping */ +.hero-gradient-text { + background: linear-gradient(100deg, #9d4300 0%, #f97316 45%, #f59e0b 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + font-style: italic; + display: block; + line-height: 1.15; + padding: 0.1em 0.05em 0.2em; +} + +@keyframes badge-shimmer { + 0% { + background-position: -200% center; + } + + 100% { + background-position: 200% center; + } +} + +.hero-badge { + background: linear-gradient(90deg, rgba(249, 115, 22, 0.12) 0%, rgba(249, 115, 22, 0.25) 50%, rgba(249, 115, 22, 0.12) 100%); + background-size: 200% auto; + animation: badge-shimmer 3s linear infinite; +} + +/* Keyframe Animations */ +@keyframes pulse-orb { + 0%, + 100% { + opacity: 0.15; + transform: scale(1) translate(0, 0); + } + + 50% { + opacity: 0.25; + transform: scale(1.1) translate(15px, -15px); + } +} + +.animate-pulse-orb { + animation: pulse-orb 8s infinite ease-in-out; +} + +@keyframes float-dashboard { + 0%, + 100% { + transform: translateY(0); + } + + 50% { + transform: translateY(-6px); + } +} + +.animate-float { + animation: float-dashboard 5s infinite ease-in-out; +} + +@keyframes blink-caret { + 0%, + 100% { + border-color: transparent; + } + + 50% { + border-color: #f97316; + } +} + +@keyframes typing-search { + 0%, + 100% { + content: ""; + } + + 5%, + 15% { + content: "p"; + } + + 20%, + 30% { + content: "py"; + } + + 35%, + 45% { + content: "pyt"; + } + + 50%, + 60% { + content: "pyth"; + } + + 65%, + 75% { + content: "pytho"; + } + + 80%, + 95% { + content: "python"; + } +} + +.typing-sim::after { + content: ""; + animation: typing-search 8s infinite steps(1); +} + +.typing-caret { + border-right: 2px solid; + animation: blink-caret 0.75s step-end infinite; +} + +@keyframes pulse-ring { + 0% { + transform: scale(0.95); + opacity: 0.8; + } + + 50% { + transform: scale(1.4); + opacity: 0; + } + + 100% { + transform: scale(0.95); + opacity: 0; + } +} + +.animate-pulse-ring { + animation: pulse-ring 2s cubic-bezier(0.25, 0, 0, 1) infinite; +} + +/* Sliding Underlines */ +.nav-link { + position: relative; +} + +.nav-link::after { + content: ''; + position: absolute; + width: 100%; + transform: scaleX(0); + height: 2px; + bottom: -4px; + left: 0; + background-color: #f97316; + transform-origin: bottom right; + transition: transform 0.25s ease-out; +} + +.nav-link:hover::after { + transform: scaleX(1); + transform-origin: bottom left; +} + +/* Bento Cards */ +.bento-card { + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +.bento-card:hover { + transform: translateY(-4px); + box-shadow: 0 12px 30px rgba(244, 107, 14, 0.08); +} + +.dark .bento-card:hover { + box-shadow: 0 12px 30px rgba(0, 0, 0, 0.4); +} + +.step-line { + background: linear-gradient(to bottom, var(--orange) 0%, var(--border) 100%); +} + +.faq-trigger[aria-expanded="true"] .faq-icon { + transform: rotate(180deg); +} + +.faq-content { + max-height: 0; + overflow: hidden; + transition: max-height 0.3s ease-out, padding 0.3s ease; +} + +.faq-trigger[aria-expanded="true"] + .faq-content { + max-height: 500px; +} + +.border:has(.faq-trigger[aria-expanded="true"]) { + border-color: rgba(244, 107, 14, 0.4); + box-shadow: 0 4px 20px rgba(244, 107, 14, 0.05); +} + +/* Hero entry animations */ +.hero-entry { + opacity: 0; + transform: translateY(22px); + transition: opacity 0.65s cubic-bezier(0.22, 1, 0.36, 1), transform 0.65s cubic-bezier(0.22, 1, 0.36, 1); +} + +body.hero-loaded .hero-entry-1 { + opacity: 1; + transform: translateY(0); + transition-delay: 0.05s; +} + +body.hero-loaded .hero-entry-2 { + opacity: 1; + transform: translateY(0); + transition-delay: 0.15s; +} + +body.hero-loaded .hero-entry-3 { + opacity: 1; + transform: translateY(0); + transition-delay: 0.28s; +} + +body.hero-loaded .hero-entry-4 { + opacity: 1; + transform: translateY(0); + transition-delay: 0.40s; +} + +body.hero-loaded .hero-entry-5 { + opacity: 1; + transform: translateY(0); + transition-delay: 0.52s; +} + +body.hero-loaded .hero-entry-6 { + opacity: 1; + transform: translateY(0); + transition-delay: 0.65s; +} + +/* Respect accessibility preferences for reduced motion */ +@media (prefers-reduced-motion: reduce) { + html, + body { + scroll-behavior: auto !important; + } + + .animate-ping, + .animate-pulse, + .animate-pulse-orb, + .animate-pulse-ring, + .animate-float, + .hero-badge { + animation: none !important; + } + + /* Entrance animations and transitions */ + .hero-entry, + .hiw-step { + transition: none !important; + transform: none !important; + opacity: 1 !important; + } + + .bento-card, + .nav-link::after, + .faq-content, + .faq-icon, + .typing-sim::after, + .typing-caret { + transition: none !important; + animation: none !important; + } + + /* Static fallback for typing simulation */ + .typing-sim::after { + content: "python" !important; + } + + .typing-caret { + border-right-color: transparent !important; + } +} diff --git a/sw.js b/sw.js index 24a647e335..df0652efca 100644 --- a/sw.js +++ b/sw.js @@ -1,7 +1,8 @@ -const CACHE_NAME = 'gsoc-finder-v4'; +const CACHE_NAME = 'gsoc-finder-v5'; const CRITICAL_ASSETS = [ './', 'index.html', + 'landing.html', 'manifest.json', 'src/styles.css', 'src/js/org.js', @@ -102,7 +103,10 @@ globalThis.addEventListener('fetch', (event) => { return networkResponse; }).catch(() => { if (event.request.mode === 'navigate') { - return caches.match('index.html'); + // Try to serve the exact requested page from cache first + // (e.g. landing.html is precached and should be served offline). + // Only fall back to index.html if the specific page isn't cached. + return caches.match(event.request).then((res) => res || caches.match('index.html')); } return caches.match(event.request); }); diff --git a/vercel.json b/vercel.json index 01197b9e45..31e0841768 100644 --- a/vercel.json +++ b/vercel.json @@ -4,6 +4,9 @@ "outputDirectory": ".", "buildCommand": "", "devCommand": "npx serve . -l $PORT", + "redirects": [ + { "source": "/", "destination": "/landing.html", "permanent": false } + ], "rewrites": [ { "source": "/api/github", "destination": "/api/github.js" } ], From d1a993c23fdc2d0d2ef35ee075dadfd30ff15506 Mon Sep 17 00:00:00 2001 From: Shruti Deshmukh Date: Sun, 28 Jun 2026 01:40:17 +0530 Subject: [PATCH 21/45] fix: update Stichting SU2 mentor contacts (#1956) Signed-off-by: Shruti Deshmukh Signed-off-by: shravanithouta108 --- data/mentors.json | 76 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 67 insertions(+), 9 deletions(-) diff --git a/data/mentors.json b/data/mentors.json index 7802062ee2..bc305ded03 100644 --- a/data/mentors.json +++ b/data/mentors.json @@ -4242,15 +4242,73 @@ "lastFetched": "2026-05-09T16:09:12.548Z" }, "Stichting SU2": { - "org": "Stichting SU2", - "ideasUrl": "https://su2code.github.io/gsoc.html", - "channels": [], - "mentors": [], - "mailingLists": [], - "tip": "", - "status": "fetch-failed", - "lastFetched": "2026-05-09T16:09:12.548Z" - }, + "org": "Stichting SU2", + "ideasUrl": "https://su2code.github.io/gsoc/Introduction/", + "channels": [ + { + "type": "Slack", + "url": "https://su2foundation.slack.com", + "label": "SU2 Slack" + } + ], + "mentors": [ + { + "name": "Nitish Anand", + "github": "", + "githubUrl": "" + }, + { + "name": "Edwin van der Weide", + "github": "", + "githubUrl": "" + }, + { + "name": "Pedro Gomes", + "github": "", + "githubUrl": "" + }, + { + "name": "Ole Burghardt", + "github": "", + "githubUrl": "" + }, + { + "name": "Nijso Beishuizen", + "github": "", + "githubUrl": "" + }, + { + "name": "Joshua A. Kelly", + "github": "", + "githubUrl": "" + }, + { + "name": "Harsh Mishra", + "github": "", + "githubUrl": "" + }, + { + "name": "Evert Bunschoten", + "github": "", + "githubUrl": "" + }, + { + "name": "Huseyin Ozdemir", + "github": "", + "githubUrl": "" + } + ], + "mailingLists": [ + { + "type": "Email", + "url": "mailto:gsoc@su2foundation.org", + "label": "SU2 GSoC Email" + } + ], + "tip": "Join the SU2 Slack community, complete the participation assignments, and interact with mentors before submitting a GSoC proposal.", + "status": "ok", + "lastFetched": "2026-06-24T22:40:00.000Z" +}, "Submitty": { "org": "Submitty", "ideasUrl": "https://github.com/Submitty/Submitty/wiki/Google-Summer-of-Code", From 0ad46eb6c0125170321cb50cbb07314a79b6f6ee Mon Sep 17 00:00:00 2001 From: Nilamma Borge Date: Sun, 28 Jun 2026 01:52:44 +0530 Subject: [PATCH 22/45] fix: add program classification template and graceful fallback validation (#1178) * fix: add program classification template and validator fallback Signed-off-by: Nilamma * fix: add fallback for missing program classification Signed-off-by: Nilamma * fix: treat general-contribution as fallback classification * chore: update mentor leaderboard (#1271) Co-authored-by: S3DFX-CYBER <213652949+S3DFX-CYBER@users.noreply.github.com> * fix: update GRAME mentor contact info (#719) Signed-off-by: Manav Co-authored-by: Savio Dsouza * fix: update Ceph mentor contact info closes #270 (#749) * fix: update Ceph mentor contact info closes #270 Signed-off-by: Manav * fix: update lastFetched timestamp for Ceph entry Signed-off-by: Manav --------- Signed-off-by: Manav Co-authored-by: Savio Dsouza Co-authored-by: MUKUL PRASAD * chore: update mentor leaderboard (#1277) Co-authored-by: S3DFX-CYBER <213652949+S3DFX-CYBER@users.noreply.github.com> * Update mentors-leaderboard.yml * docs: update GFOSS mentor metadata (#703) * docs: update GFOSS mentor metadata Signed-off-by: Rajdeep * fix: normalize GFOSS mailing list metadata Signed-off-by: Rajdeep --------- Signed-off-by: Rajdeep Co-authored-by: Savio Dsouza * chore: update mentor leaderboard (#1286) Co-authored-by: S3DFX-CYBER <213652949+S3DFX-CYBER@users.noreply.github.com> * Update leaderboard.yml * Update generate-leaderboard-comment.js * Update issue context assignment workflow * Add workflow for assignment timeout escalation This workflow automates the escalation of stale assignments by checking for open issues without assigned mentors and updating their status if they exceed a specified timeout period. * Enhance duplicate issue detection workflow Refactor duplicate issue detection workflow to improve title and body normalization, enhance similarity scoring, and update comment handling. * Update select-issue-mentors.js * feat: add interactive GSoC proposal builder workspace (#954) Signed-off-by: Bhavya jain Co-authored-by: Savio Dsouza * chore: update mentor leaderboard (#1324) Co-authored-by: S3DFX-CYBER <213652949+S3DFX-CYBER@users.noreply.github.com> * fix:add mentor contact info fot AFLplusplus #256 (#1117) Signed-off-by: Riyanshi Gupta Co-authored-by: Savio Dsouza * fix: verify Alaska mentor contact info (#959) Signed-off-by: ajitkumarsaini02 Co-authored-by: Deepak Bhagat <149673145+deepak0x@users.noreply.github.com> Co-authored-by: Savio Dsouza * fix multiple classification error * fix: prevent multiple program classifications Signed-off-by: Nilamma --------- Signed-off-by: Nilamma Signed-off-by: Manav Signed-off-by: Rajdeep Signed-off-by: Bhavya jain Signed-off-by: Riyanshi Gupta Signed-off-by: ajitkumarsaini02 Co-authored-by: Savio Dsouza Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: S3DFX-CYBER <213652949+S3DFX-CYBER@users.noreply.github.com> Co-authored-by: Manav Kumar Co-authored-by: MUKUL PRASAD Co-authored-by: Rajdeep Yadav Co-authored-by: Bhavya jain Co-authored-by: riyanshigupta890-cloud Co-authored-by: Ajit Kumar Saini Co-authored-by: Deepak Bhagat <149673145+deepak0x@users.noreply.github.com> Signed-off-by: shravanithouta108 --- .github/pull_request_template.md | 37 ++++++ .../program-classification-validator.yml | 116 +++++++++++++----- 2 files changed, 125 insertions(+), 28 deletions(-) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000..c7d1a17165 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,37 @@ +## ๐Ÿ“ Description + + +## ๐Ÿ”— Related Issue +Closes # + +## ๐Ÿš€ Program Classification + +- [ ] GSSoC26 +- [ ] NSoC26 +- [ ] General Contribution + +## ๐Ÿ”„ Type of Change + +- [ ] ๐Ÿ› Bug fix +- [ ] โœจ New feature +- [ ] ๐Ÿ” SEO improvement +- [ ] ๐ŸŽจ Style / UI improvement +- [ ] โ™ฟ Accessibility improvement +- [ ] ๐Ÿ“ Documentation +- [ ] โš™๏ธ CI / configuration +- [ ] ๐Ÿงน Refactor / cleanup + +## ๐Ÿงช How to Test + +1. Open `index.html` in a browser +2. ... + +## ๐Ÿ“ธ Screenshots (if applicable) + + +## โœ… Checklist +- [ ] My code follows the project's existing style +- [ ] I have tested my changes in a browser +- [ ] I have linked the related issue above +- [ ] My PR title follows Conventional Commits format (e.g. `feat: add scroll button`) +- [ ] I have not introduced any new dependencies or build tools diff --git a/.github/workflows/program-classification-validator.yml b/.github/workflows/program-classification-validator.yml index 95657f51b4..923cbc0046 100644 --- a/.github/workflows/program-classification-validator.yml +++ b/.github/workflows/program-classification-validator.yml @@ -59,10 +59,17 @@ jobs: const MARKER = ''; - const VALID_LABELS = [ + const ACTUAL_PROGRAMS = [ 'gssoc26', - 'nsoc26', - 'general-contribution' + 'nsoc26' + ]; + + const FALLBACK_PROGRAM = + 'general-oss'; + + const VALID_LABELS = [ + ...ACTUAL_PROGRAMS, + FALLBACK_PROGRAM ]; const MISSING_LABEL = @@ -76,68 +83,79 @@ jobs: // ---------------------------------------- function detectProgram() { - const detected = new Set(); + const actualPrograms = new Set(); + let hasGeneralContribution = false; // ------------------------------------ - // Metadata + // Metadata - new template format // ------------------------------------ if ( + body.includes('- [x] gssoc26') || body.includes('program: gssoc') || body.includes('- [x] gssoc') ) { - detected.add('gssoc'); + actualPrograms.add('gssoc'); } if ( + body.includes('- [x] nsoc26') || body.includes('program: nsoc') || body.includes('- [x] nsoc') ) { - detected.add('nsoc'); + actualPrograms.add('nsoc'); } if ( - body.includes('program: general') || - body.includes('- [x] general contribution') + body.includes('- [x] general contribution') || + body.includes('program: general') ) { - detected.add('general'); + hasGeneralContribution = true; } // ------------------------------------ - // Labels + // Labels - ACTUAL PROGRAMS // ------------------------------------ if (labels.includes('gssoc26')) { - detected.add('gssoc'); + actualPrograms.add('gssoc'); } if (labels.includes('nsoc26')) { - detected.add('nsoc'); + actualPrograms.add('nsoc'); } + // ------------------------------------ + // Labels - FALLBACK PROGRAM + // ------------------------------------ + if ( - labels.includes('general-contribution') + labels.includes('general-oss') ) { - detected.add('general'); + hasGeneralContribution = true; } // ------------------------------------ - // Branch hints + // Branch hints - ACTUAL PROGRAMS // ------------------------------------ if (branch.includes('gssoc')) { - detected.add('gssoc'); + actualPrograms.add('gssoc'); } if (branch.includes('nsoc')) { - detected.add('nsoc'); + actualPrograms.add('nsoc'); } - return [...detected]; + return { + actual: [...actualPrograms], + fallback: hasGeneralContribution + }; } - const detectedPrograms = - detectProgram(); + const detected = detectProgram(); + const detectedActualPrograms = detected.actual; + const hasGeneralContribution = detected.fallback; // โ”€โ”€ Dual-label guard โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ // If both GSSoC and NSoC signals/labels are detected, apply the @@ -252,7 +270,7 @@ jobs: // MISSING PROGRAM // ---------------------------------------- - if (detectedPrograms.length === 0) { + if (detectedActualPrograms.length === 0) { await clearProgramLabels(); @@ -296,10 +314,10 @@ jobs: } // ---------------------------------------- - // MULTIPLE PROGRAMS + // MULTIPLE ACTUAL PROGRAMS // ---------------------------------------- - if (detectedPrograms.length > 1) { + if (detectedActualPrograms.length > 1) { await clearProgramLabels(); @@ -322,7 +340,7 @@ jobs: Detected: - ${detectedPrograms + ${detectedActualPrograms .map(p => `- ${p}`) .join('\n')} @@ -341,17 +359,59 @@ jobs: } // ---------------------------------------- - // VALID PROGRAM + // ACTUAL PROGRAM + FALLBACK CONFLICT + // ---------------------------------------- + + if ( + detectedActualPrograms.length === 1 && + hasGeneralContribution + ) { + + await clearProgramLabels(); + + await addLabels([ + INVALID_LABEL + ]); + + const comment = + `${MARKER} + + ## โš ๏ธ Conflicting Classification + + Your PR has selected BOTH a specific program (GSSoC26/NSoC26) AND "General Contribution". + + Please select ONLY ONE: + + - GSSOC26 + - NSoC26 + - General Contribution + + After editing your PR body, validation will automatically re-run. + + > Stage-2 review routing is temporarily paused until this is fixed. + `; + + await upsertComment(comment); + + core.setFailed( + 'Multiple program classifications detected' + ); + + return; + } + + // ---------------------------------------- + // VALID SINGLE ACTUAL PROGRAM // ---------------------------------------- const program = - detectedPrograms[0]; + detectedActualPrograms[0]; await clearValidationLabels(); await clearProgramLabels(); let finalLabel = - 'general-contribution'; + 'general-oss'; if (program === 'gssoc') { finalLabel = 'gssoc26'; From de55ca3c7c17edcc6f7711f5a456f371a8b6597f Mon Sep 17 00:00:00 2001 From: KOTTAPALLI DEEPAK VARMA Date: Sun, 28 Jun 2026 01:59:14 +0530 Subject: [PATCH 23/45] fix(data): verify Genome Assembly and Annotation mentor contact info (#1347) * fix(data): verify Genome Assembly and Annotation mentor contact info Fixes #298. Verified the Genome Assembly and Annotation GSoC 2026 entry, updated the contact email, populated the mentor list from the ideas page, and marked the org as `ok`. Signed-off-by: NyayaCheck DevBot * fix(data): move Genome Assembly contact to channels Signed-off-by: NyayaCheck DevBot --------- Signed-off-by: NyayaCheck DevBot Co-authored-by: NyayaCheck DevBot Co-authored-by: Savio Dsouza Signed-off-by: shravanithouta108 --- data/mentors.json | 142 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 137 insertions(+), 5 deletions(-) diff --git a/data/mentors.json b/data/mentors.json index bc305ded03..5d3b04c2f9 100644 --- a/data/mentors.json +++ b/data/mentors.json @@ -1537,12 +1537,144 @@ "Genome Assembly and Annotation": { "org": "Genome Assembly and Annotation", "ideasUrl": "https://summerofcode.withgoogle.com/programs/2026/organizations/genome-assembly-and-annotation", - "channels": [], - "mentors": [], + "channels": [ + { + "type": "Email", + "url": "mailto:helpdesk@ensembl.org", + "label": "helpdesk@ensembl.org" + } + ], + "mentors": [ + { + "name": "Senthilnathan Vijayaraja", + "github": "", + "githubUrl": "" + }, + { + "name": "Dipayan Gupta", + "github": "", + "githubUrl": "" + }, + { + "name": "David Yuan", + "github": "", + "githubUrl": "" + }, + { + "name": "Anna Lazar", + "github": "", + "githubUrl": "" + }, + { + "name": "Simarpreet Kaur Bhurji", + "github": "", + "githubUrl": "" + }, + { + "name": "Leanne Haggerty", + "github": "", + "githubUrl": "" + }, + { + "name": "Jack Tierney", + "github": "", + "githubUrl": "" + }, + { + "name": "Jose Perez-Silva", + "github": "", + "githubUrl": "" + }, + { + "name": "Vianey Paola Barrera Enriquez", + "github": "", + "githubUrl": "" + }, + { + "name": "Swati Sinha", + "github": "", + "githubUrl": "" + }, + { + "name": "Jitender Jit Singh Cheema", + "github": "", + "githubUrl": "" + }, + { + "name": "Likhitha Surapaneni", + "github": "", + "githubUrl": "" + }, + { + "name": "Alexey Sokolov", + "github": "", + "githubUrl": "" + }, + { + "name": "Kirill Tsukanov", + "github": "", + "githubUrl": "" + }, + { + "name": "Aleksandr Zakirov", + "github": "", + "githubUrl": "" + }, + { + "name": "Aleena Mushtaq", + "github": "", + "githubUrl": "" + }, + { + "name": "Bilal El Houdaigui", + "github": "", + "githubUrl": "" + }, + { + "name": "Christian Atallah", + "github": "", + "githubUrl": "" + }, + { + "name": "Vikas Gupta", + "github": "", + "githubUrl": "" + }, + { + "name": "Mahfouz Shehu", + "github": "", + "githubUrl": "" + }, + { + "name": "Jorge Alvarez", + "github": "", + "githubUrl": "" + }, + { + "name": "Disha Lodha", + "github": "", + "githubUrl": "" + }, + { + "name": "ChrisMGnify", + "github": "ChrisMGnify", + "githubUrl": "https://github.com/ChrisMGnify" + }, + { + "name": "KateSakharova", + "github": "KateSakharova", + "githubUrl": "https://github.com/KateSakharova" + }, + { + "name": "Santiago Fragoso", + "github": "", + "githubUrl": "" + } + ], "mailingLists": [], - "tip": "", - "status": "no-contact-found", - "lastFetched": "2026-05-09T16:09:12.548Z" + "tip": "Contact the EMBL-EBI GSoC helpdesk at helpdesk@ensembl.org for project questions.", + "status": "ok", + "lastFetched": "2026-05-26T00:00:00.000Z" }, "GeomScale": { "org": "GeomScale", From 385afba73a3ef4e79566585acc63b1450e756e8e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 28 Jun 2026 17:27:22 +0530 Subject: [PATCH 24/45] docs: backfill contributors (#1973) Co-authored-by: S3DFX-CYBER <213652949+S3DFX-CYBER@users.noreply.github.com> Signed-off-by: shravanithouta108 --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 0a075c3362..8355acb9ad 100644 --- a/README.md +++ b/README.md @@ -627,12 +627,15 @@ These mentors help guide and review contributions for the GSSoC program: Jay-Jay-Tee Konarksharma13 Kuldeeps1505 +KumarNirupam1 Lathika11 Manasa-2303 Manav5234 MehtabSandhu11 Namish06 +NayansiDupare Nightkilller +Nilamma19 Nirula23 OmkarAKadam Pallavi-vi-1234 From e3a2c7164197a041df88e13cb8cd485fb04d6889 Mon Sep 17 00:00:00 2001 From: Amrita Ajitkumar Menon Date: Tue, 30 Jun 2026 05:47:08 +0400 Subject: [PATCH 25/45] fix: add flex-shrink-0 to org card logo container to prevent squishing at narrow viewports (#1941) Signed-off-by: amrita-a-menon Signed-off-by: shravanithouta108 --- .gitignore | Bin 89 -> 134 bytes index.html | 5 ++--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 838e90504bd2da1783878160f075cfff5bde2e02..9884e6a731dbaf945e852d1b616016d7ab27237d 100644 GIT binary patch literal 134 zcmXwxQ3``F7=!a%=pBT;33iv5C-49wZ5XoJfAsJ4`u?)*B_v-+iX_gPq)(>ay$d^Q zWH!UHe>G%@%a4TqpS-(uV_KWkPTAU8o_eh{+TZo_nC8h!de%}*)KNiEjr(kn|XN>0t;(o4-N0~2~V`N@e8zMdA8!=${escapeHtml(org.name) card.innerHTML = `
-
- ${escapeHtml(org.name + ' logo')} - +
+ ${escapeHtml(org.name + ' logo')}
${escapeHtml(String(org.years))}y Veteran From 96103e0cf5f225115f98e39b6b12b4d768759162 Mon Sep 17 00:00:00 2001 From: Arghya Date: Tue, 30 Jun 2026 12:20:26 +0530 Subject: [PATCH 26/45] fix: update routing links in landing.html for Vercel rewrite rules (#1975) * fix: update routing links in landing.html for Vercel rewrite rules Signed-off-by: Arghyadeep Bag * fix: update footer injected links to use /app route in landing.js Signed-off-by: Arghyadeep Bag --------- Signed-off-by: Arghyadeep Bag Signed-off-by: shravanithouta108 --- landing.html | 16 ++++++++-------- src/js/landing.js | 2 +- vercel.json | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/landing.html b/landing.html index c66323ee8b..34d715a9a3 100644 --- a/landing.html +++ b/landing.html @@ -10,7 +10,7 @@ FindMyGSoC โ€” Discover GSoC 2026 Orgs & Live GitHub Issues - + @@ -18,7 +18,7 @@ - + @@ -63,7 +63,7 @@ class="fixed top-0 w-full z-50 bg-white/80 dark:bg-[#0f0a05]/80 glass border-b border-zinc-200/50 dark:border-zinc-800/50 shadow-sm transition-colors duration-300">
-
@@ -97,7 +97,7 @@ - FAQ - Launch Application rocket_launch @@ -198,7 +198,7 @@
- - rocket_launch Start Your Journey Now @@ -1098,7 +1098,7 @@

Launch the application, compare organizations, filter tech stacks, and find good first issues instantly.

-
Launch Finder Dashboard rocket_launch diff --git a/src/js/landing.js b/src/js/landing.js index d5d671cb24..e667acad36 100644 --- a/src/js/landing.js +++ b/src/js/landing.js @@ -266,7 +266,7 @@ function applyFooterPatch(footer) { footer.querySelectorAll('a').forEach((a) => { const href = a.getAttribute('href'); if (href === '#orgs' || href === '#timeline') { - a.setAttribute('href', `index.html${href}`); + a.setAttribute('href', `/app${href}`); } }); } diff --git a/vercel.json b/vercel.json index 31e0841768..af922d11d0 100644 --- a/vercel.json +++ b/vercel.json @@ -4,10 +4,10 @@ "outputDirectory": ".", "buildCommand": "", "devCommand": "npx serve . -l $PORT", - "redirects": [ - { "source": "/", "destination": "/landing.html", "permanent": false } - ], "rewrites": [ + { "source": "/", "destination": "/landing.html" }, + { "source": "/app", "destination": "/index.html" }, + { "source": "/app/:path*", "destination": "/index.html" }, { "source": "/api/github", "destination": "/api/github.js" } ], "headers": [ From 0033b1830742a6b7df7186f37a140bec32a44b72 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 30 Jun 2026 13:07:37 +0530 Subject: [PATCH 27/45] chore: update mentor leaderboard (#1900) Co-authored-by: S3DFX-CYBER <213652949+S3DFX-CYBER@users.noreply.github.com> Signed-off-by: shravanithouta108 --- .github/reviewers/mentor-leaderboard.md | 22 ++++++++++----------- .github/reviewers/mentor-stats.json | 26 ++++++++++++------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/.github/reviewers/mentor-leaderboard.md b/.github/reviewers/mentor-leaderboard.md index 8c5881eac0..7354a38d91 100644 --- a/.github/reviewers/mentor-leaderboard.md +++ b/.github/reviewers/mentor-leaderboard.md @@ -4,21 +4,21 @@ | Rank | Mentor | Reviews | Score | |------|--------|---------|-------| -| ๐Ÿฅ‡ | @KumarNirupam1 | 25 | 108 | -| ๐Ÿฅˆ | @TarunyaProgrammer | 19 | 75 | +| ๐Ÿฅ‡ | @KumarNirupam1 | 32 | 137 | +| ๐Ÿฅˆ | @TarunyaProgrammer | 23 | 92 | | ๐Ÿฅ‰ | @nitinog10 | 16 | 69 | -| 4 | @Anushreebasics | 17 | 45 | -| 5 | @4f4d | 7 | 37 | -| 6 | @deepak0x | 7 | 26 | +| 4 | @Anushreebasics | 20 | 52 | +| 5 | @deepak0x | 9 | 38 | +| 6 | @4f4d | 7 | 37 | | 7 | @itsdakshjain | 6 | 19 | | 8 | @knoxiboy | 6 | 19 | | 9 | @saurabh24thakur | 3 | 17 | | 10 | @Balaji91221 | 8 | 16 | -| 11 | @12fahed | 3 | 11 | -| 12 | @Mrigakshi-Rathore | 5 | 10 | -| 13 | @Maxd646 | 3 | 10 | -| 14 | @MUKUL-PRASAD-SIGH | 4 | 9 | -| 15 | @CoderOggy78 | 3 | 7 | +| 11 | @Maxd646 | 4 | 12 | +| 12 | @12fahed | 3 | 11 | +| 13 | @Mrigakshi-Rathore | 5 | 10 | +| 14 | @CoderOggy78 | 4 | 9 | +| 15 | @MUKUL-PRASAD-SIGH | 4 | 9 | | 16 | @nihalawasthi | 3 | 7 | | 17 | @AnirudhPhophalia | 3 | 6 | | 18 | @BandhiyaHardik | 3 | 6 | @@ -63,4 +63,4 @@ | 57 | @uddalak2005 | 0 | 0 | | 58 | @vanshaggarwal07 | 0 | 0 | -Last updated: Wed, 17 Jun 2026 03:57:34 GMT +Last updated: Tue, 30 Jun 2026 06:52:58 GMT diff --git a/.github/reviewers/mentor-stats.json b/.github/reviewers/mentor-stats.json index d4f937612f..b0fb8a04a2 100644 --- a/.github/reviewers/mentor-stats.json +++ b/.github/reviewers/mentor-stats.json @@ -32,8 +32,8 @@ "score": 0 }, "Anushreebasics": { - "reviews": 17, - "score": 45 + "reviews": 20, + "score": 52 }, "aryanbhutani26": { "reviews": 0, @@ -64,12 +64,12 @@ "score": 0 }, "CoderOggy78": { - "reviews": 3, - "score": 7 + "reviews": 4, + "score": 9 }, "deepak0x": { - "reviews": 7, - "score": 26 + "reviews": 9, + "score": 38 }, "deepaksinghh12": { "reviews": 0, @@ -104,8 +104,8 @@ "score": 19 }, "KumarNirupam1": { - "reviews": 25, - "score": 108 + "reviews": 32, + "score": 137 }, "lourduradjou": { "reviews": 2, @@ -120,8 +120,8 @@ "score": 0 }, "Maxd646": { - "reviews": 3, - "score": 10 + "reviews": 4, + "score": 12 }, "MAYANKSHARMA01010": { "reviews": 0, @@ -216,8 +216,8 @@ "score": 0 }, "TarunyaProgrammer": { - "reviews": 19, - "score": 75 + "reviews": 23, + "score": 92 }, "thakurutkarsh22": { "reviews": 0, @@ -231,4 +231,4 @@ "reviews": 0, "score": 0 } -} +} \ No newline at end of file From 7b7692e7859cfb5a04eee4ca9b24118af8f67b49 Mon Sep 17 00:00:00 2001 From: Shan Usmani Date: Fri, 3 Jul 2026 01:30:00 +0530 Subject: [PATCH 28/45] docs: fix alt text for flowchart image in README (#1424) (#1873) Signed-off-by: Shan Usmani Co-authored-by: Savio Dsouza Signed-off-by: shravanithouta108 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8355acb9ad..e28730b3f4 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ Built with a responsive and lightweight architecture, the platform delivers a se ## ๐Ÿ“ˆ Flowchart -User Action Flow for Org-2026-05-05-154517 +Flowchart showing the user action flow for discovering and filtering GSoC organizations --- From f125f66e8420e2ed1307369ae3617c26a44866d8 Mon Sep 17 00:00:00 2001 From: Arghya Date: Sat, 4 Jul 2026 19:30:38 +0530 Subject: [PATCH 29/45] =?UTF-8?q?fix:=20switch=20vercel.json=20from=20rewr?= =?UTF-8?q?ites=20to=20routes=20to=20fix=20'/'=20routing=20pr=E2=80=A6=20(?= =?UTF-8?q?#1987)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: switch vercel.json from rewrites to routes to fix '/' routing precedence Signed-off-by: Arghyadeep Bag * fix: reorder routes and remove deprecated handle:filesystem in vercel.json Signed-off-by: Arghyadeep Bag --------- Signed-off-by: Arghyadeep Bag Signed-off-by: shravanithouta108 --- vercel.json | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/vercel.json b/vercel.json index af922d11d0..771d7cd7e7 100644 --- a/vercel.json +++ b/vercel.json @@ -4,25 +4,25 @@ "outputDirectory": ".", "buildCommand": "", "devCommand": "npx serve . -l $PORT", - "rewrites": [ - { "source": "/", "destination": "/landing.html" }, - { "source": "/app", "destination": "/index.html" }, - { "source": "/app/:path*", "destination": "/index.html" }, - { "source": "/api/github", "destination": "/api/github.js" } - ], - "headers": [ + "routes": [ + { "src": "/app/(.*)", "dest": "/index.html" }, + { "src": "/app", "dest": "/index.html" }, { - "source": "/api/(.*)", - "headers": [ - { "key": "Access-Control-Allow-Origin", "value": "*" }, - { "key": "Cache-Control", "value": "s-maxage=3600" } - ] + "src": "/api/(.*)", + "headers": { + "Access-Control-Allow-Origin": "*", + "Cache-Control": "s-maxage=3600" + }, + "continue": true }, { - "source": "/data/(.*)\\.json", - "headers": [ - { "key": "Cache-Control", "value": "public, max-age=0, must-revalidate" } - ] - } + "src": "/data/(.*)\\.json", + "headers": { + "Cache-Control": "public, max-age=0, must-revalidate" + }, + "continue": true + }, + { "src": "/api/github", "dest": "/api/github.js" }, + { "src": "/", "dest": "/landing.html" } ] -} +} \ No newline at end of file From dc116d57100b5bef3abe7df4328fc1635b8a5653 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 5 Jul 2026 00:02:14 +0530 Subject: [PATCH 30/45] chore: update mentor leaderboard (#1980) Co-authored-by: S3DFX-CYBER <213652949+S3DFX-CYBER@users.noreply.github.com> Signed-off-by: shravanithouta108 --- .github/reviewers/mentor-leaderboard.md | 4 ++-- .github/reviewers/mentor-stats.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/reviewers/mentor-leaderboard.md b/.github/reviewers/mentor-leaderboard.md index 7354a38d91..5f4af7bf38 100644 --- a/.github/reviewers/mentor-leaderboard.md +++ b/.github/reviewers/mentor-leaderboard.md @@ -4,7 +4,7 @@ | Rank | Mentor | Reviews | Score | |------|--------|---------|-------| -| ๐Ÿฅ‡ | @KumarNirupam1 | 32 | 137 | +| ๐Ÿฅ‡ | @KumarNirupam1 | 33 | 142 | | ๐Ÿฅˆ | @TarunyaProgrammer | 23 | 92 | | ๐Ÿฅ‰ | @nitinog10 | 16 | 69 | | 4 | @Anushreebasics | 20 | 52 | @@ -63,4 +63,4 @@ | 57 | @uddalak2005 | 0 | 0 | | 58 | @vanshaggarwal07 | 0 | 0 | -Last updated: Tue, 30 Jun 2026 06:52:58 GMT +Last updated: Sat, 04 Jul 2026 14:03:34 GMT diff --git a/.github/reviewers/mentor-stats.json b/.github/reviewers/mentor-stats.json index b0fb8a04a2..ba3a9feb32 100644 --- a/.github/reviewers/mentor-stats.json +++ b/.github/reviewers/mentor-stats.json @@ -104,8 +104,8 @@ "score": 19 }, "KumarNirupam1": { - "reviews": 32, - "score": 137 + "reviews": 33, + "score": 142 }, "lourduradjou": { "reviews": 2, From cb6063f080b971498e49faa6da385c5041ebc409 Mon Sep 17 00:00:00 2001 From: Manav Kumar Date: Sun, 5 Jul 2026 00:07:42 +0530 Subject: [PATCH 31/45] fix: verify and update mentor contact info for GNU Radio (#309) (#1991) * fix: verify and update mentor contact info for GNU Radio (#309) Signed-off-by: Manav * fix: correct indentation in GNU Radio mentor entry Signed-off-by: Manav * fix: correct schema fields in GNU Radio mentor entry Signed-off-by: Manav * fix: correct IRC URL format for GNU Radio Libera.Chat link Signed-off-by: Manav --------- Signed-off-by: Manav Signed-off-by: shravanithouta108 --- data/mentors.json | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/data/mentors.json b/data/mentors.json index 5d3b04c2f9..e5d3a58807 100644 --- a/data/mentors.json +++ b/data/mentors.json @@ -1956,14 +1956,38 @@ }, "GNU Radio": { "org": "GNU Radio", - "ideasUrl": "https://wiki.gnuradio.org/index.php/GSoC", - "channels": [], - "mentors": [], - "mailingLists": [], - "tip": "", - "status": "no-contact-found", - "lastFetched": "2026-05-09T16:09:12.548Z" - }, + "ideasUrl": "https://wiki.gnuradio.org/index.php/GSoCIdeas", + "channels": [ + { + "type": "Matrix", + "label": "#gnuradio:gnuradio.org", + "url": "https://chat.gnuradio.org/" + }, + { + "type": "IRC", + "label": "#gnuradio", + "url": "https://web.libera.chat/#gnuradio" + } + ], + "mentors": [ + { "name": "Josh Morman", "github": "jmorman", "githubUrl": "https://github.com/jmorman" }, + { "name": "Marcus Mรผller", "github": "marcusmueller", "githubUrl": "https://github.com/marcusmueller" }, + { "name": "Cyrille Morin", "github": "cyrille-morin", "githubUrl": "https://github.com/cyrille-morin" }, + { "name": "Luigi Cruz", "github": "luigi-cruz", "githubUrl": "https://github.com/luigi-cruz" }, + { "name": "Hรฅkon Vรฅgsether", "github": "hakonvags", "githubUrl": "https://github.com/hakonvags" } + ], + "mailingLists": [ + { + "type": "Mailing list", + "label": "discuss-gnuradio", + "url": "https://lists.gnu.org/mailman/listinfo/discuss-gnuradio", + "email": "discuss-gnuradio@gnu.org" + } + ], + "tip": "Join #gnuradio on Matrix (chat.gnuradio.org) or IRC (Libera.Chat). Post on the mailing list discuss-gnuradio@gnu.org. Do not contact mentors off-list.", + "status": "ok", + "lastFetched": "2026-07-02T00:00:00.000Z" +}, "gprMax": { "org": "gprMax", "ideasUrl": "https://github.com/gprMax/GSoC/blob/main/project-ideas-2026.md", From ff1e03338604a822634b7bcda3834885bf1dd48c Mon Sep 17 00:00:00 2001 From: shravanithouta108 Date: Sun, 12 Jul 2026 09:25:16 +0530 Subject: [PATCH 32/45] fix: suppress lint unused-var warnings and add null guards Signed-off-by: shravanithouta108 --- src/js/app.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/js/app.js b/src/js/app.js index cb9b0061f4..4378d798a7 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -5,7 +5,8 @@ // GLOBAL STATE & COMPATIBILITY LAYER // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // filteredOrgs declared in index.html inline script -let MENTOR_DATA = {}; +/* global filteredOrgs, visibleCount */ +let MENTOR_DATA = {}; // eslint-disable-line no-unused-vars let mentorDataState = 'idle'; const compareList = []; // list of org names const bookmarkedSet = new Set(parseStoredBookmarks()); @@ -91,11 +92,11 @@ const UMBRELLA_ORGS = new Set([ 'LabLua', 'Liquid Galaxy project', 'Free and Open Source Silicon Foundation', ]); -const CHANNEL_ICONS = { +const CHANNEL_ICONS = { // eslint-disable-line no-unused-vars Slack: '๐Ÿ’ฌ', Zulip: '๐Ÿ’ฌ', Discord: '๐ŸŽฎ', Matrix: '๐Ÿ”—', IRC: '๐Ÿ–ฅ๏ธ', 'Mailing list': '๐Ÿ“ง' }; -const CONTACT_TIPS = { +const CONTACT_TIPS = { // eslint-disable-line no-unused-vars Slack: 'Join and say hi in the GSoC channel before DMing mentors.', Discord: 'Introduce yourself in the public channel before asking project-specific questions.', Zulip: 'Post a new topic in the GSoC stream with your background and interest.', @@ -519,7 +520,7 @@ async function checkAPI(){ banner.className='api-banner api-ok'; document.getElementById('apiStrong').textContent='โœ“ GitHub API Connected'; document.getElementById('apiText').textContent='Live stats (stars, forks, good first issues) available for all visitors.'; - document.getElementById('fetchBtn').style.display='flex'; + const fetchBtn = document.getElementById('fetchBtn'); if (fetchBtn) fetchBtn.style.display='flex'; }else{ banner.className='api-banner api-warn'; document.getElementById('apiStrong').textContent='โš  API Error'; @@ -1444,7 +1445,7 @@ function fmt(n) { return (!n && n !== 0) ? 'โ€”' : n >= 1000 ? (n / 1000).toFixe // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // MODAL DETAILS POPULATOR // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• -globalThis.openModal = function (name, triggerElement = null) { +globalThis.openModal = function (name, _triggerElement = null) { const org = ORGS.find(o => o.name === name); if (!org) return; @@ -1518,10 +1519,9 @@ globalThis.openModal = function (name, triggerElement = null) { // Sanitize href links const ideasUrl = validateIdeasUrl(org.ideas); - const repoHref = githubUrlFromValue(org.github); const ideasBtn = document.getElementById('mIdeasBtn'); - const repoBtn = document.getElementById('mRepoBtn'); + const repoBtn = document.getElementById('mRepoBtn'); if (repoBtn) repoBtn.href = githubUrlFromValue(org.github) || '#'; if (ideasBtn) { if (ideasUrl) { From bbd3ae4d85dec23588e690a4e519c554fdb28e5d Mon Sep 17 00:00:00 2001 From: shravanithouta108 Date: Sun, 12 Jul 2026 10:08:19 +0530 Subject: [PATCH 33/45] fix: move filteredOrgs to global comment to fix lint Signed-off-by: shravanithouta108 --- src/js/app.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/js/app.js b/src/js/app.js index 4378d798a7..f6bca20015 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -1,11 +1,10 @@ -/* global ORGS, openModal, toggleCompare, toggleBookmark, openRandomOrg, clearAllFilters, openCompareModal, fetchModalGH, unselectLanguage, clearAllLanguages */ +/* global ORGS, filteredOrgs, openModal, toggleCompare, toggleBookmark, openRandomOrg, clearAllFilters, openCompareModal, fetchModalGH, unselectLanguage, clearAllLanguages */ /* exported openAnalytics, closeAnEvent, fetchAll, fetchModalGH, toggleCompareFromModal, openCompare, closeCompareEv, imgErr, toggleBookmark, toggleChip, resetFilters, closeModalEv, openIssuesPage, closeIssuesPage, fetchAllIssues, showMoreIssues */ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // GLOBAL STATE & COMPATIBILITY LAYER // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // filteredOrgs declared in index.html inline script -/* global filteredOrgs, visibleCount */ let MENTOR_DATA = {}; // eslint-disable-line no-unused-vars let mentorDataState = 'idle'; const compareList = []; // list of org names From 3c45e752662fd24e18e014774cc2e6af6832b56c Mon Sep 17 00:00:00 2001 From: shravanithouta108 Date: Sun, 12 Jul 2026 16:14:07 +0530 Subject: [PATCH 34/45] fix: add ORGS global declaration to landing.js for lint Signed-off-by: shravanithouta108 --- src/js/landing.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/js/landing.js b/src/js/landing.js index e667acad36..8c000ad137 100644 --- a/src/js/landing.js +++ b/src/js/landing.js @@ -1,4 +1,5 @@ /** +/* global ORGS */ * landing.js โ€” FindMyGSoC Landing Page Scripts * * Responsibilities: From b59eb521b05259e317b9464e87db80d842ad6f39 Mon Sep 17 00:00:00 2001 From: shravanithouta108 Date: Sun, 12 Jul 2026 16:21:52 +0530 Subject: [PATCH 35/45] fix: declare filteredOrgs in app.js and add ORGS global to landing.js for lint Signed-off-by: shravanithouta108 --- src/js/app.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/js/app.js b/src/js/app.js index f6bca20015..8cdc7db918 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -1,10 +1,10 @@ -/* global ORGS, filteredOrgs, openModal, toggleCompare, toggleBookmark, openRandomOrg, clearAllFilters, openCompareModal, fetchModalGH, unselectLanguage, clearAllLanguages */ +/* global ORGS, openModal, toggleCompare, toggleBookmark, openRandomOrg, clearAllFilters, openCompareModal, fetchModalGH, unselectLanguage, clearAllLanguages */ /* exported openAnalytics, closeAnEvent, fetchAll, fetchModalGH, toggleCompareFromModal, openCompare, closeCompareEv, imgErr, toggleBookmark, toggleChip, resetFilters, closeModalEv, openIssuesPage, closeIssuesPage, fetchAllIssues, showMoreIssues */ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // GLOBAL STATE & COMPATIBILITY LAYER // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• -// filteredOrgs declared in index.html inline script +let filteredOrgs = []; let MENTOR_DATA = {}; // eslint-disable-line no-unused-vars let mentorDataState = 'idle'; const compareList = []; // list of org names @@ -1444,7 +1444,7 @@ function fmt(n) { return (!n && n !== 0) ? 'โ€”' : n >= 1000 ? (n / 1000).toFixe // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // MODAL DETAILS POPULATOR // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• -globalThis.openModal = function (name, _triggerElement = null) { +globalThis.openModal = function (name) { const org = ORGS.find(o => o.name === name); if (!org) return; From 364ffb59eb73b4e271b28fb98065435ead16f0fa Mon Sep 17 00:00:00 2001 From: shravanithouta108 Date: Sun, 12 Jul 2026 16:26:13 +0530 Subject: [PATCH 36/45] fix: restore package-lock.json from upstream for lint workflow Signed-off-by: shravanithouta108 --- package-lock.json | 2713 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2713 insertions(+) create mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000000..b4f0a5038e --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2713 @@ +{ + "name": "gsoc-org-finder", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "gsoc-org-finder", + "version": "1.0.0", + "dependencies": { + "@vercel/speed-insights": "^2.0.0" + }, + "devDependencies": { + "eslint": "^8.57.0", + "htmlhint": "^1.1.4", + "stylelint": "^16.3.1", + "stylelint-config-standard": "^36.0.1" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@cacheable/memory": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.0.8.tgz", + "integrity": "sha512-FvEb29x5wVwu/Kf93IWwsOOEuhHh6dYCJF3vcKLzXc0KXIW181AOzv6ceT4ZpBHDvAfG60eqb+ekmrnLHIy+jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cacheable/utils": "^2.4.0", + "@keyv/bigmap": "^1.3.1", + "hookified": "^1.15.1", + "keyv": "^5.6.0" + } + }, + "node_modules/@cacheable/memory/node_modules/@keyv/bigmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@keyv/bigmap/-/bigmap-1.3.1.tgz", + "integrity": "sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hashery": "^1.4.0", + "hookified": "^1.15.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "keyv": "^5.6.0" + } + }, + "node_modules/@cacheable/memory/node_modules/keyv": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@keyv/serialize": "^1.1.1" + } + }, + "node_modules/@cacheable/utils": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.4.0.tgz", + "integrity": "sha512-PeMMsqjVq+bF0WBsxFBxr/WozBJiZKY0rUojuaCoIaKnEl3Ju1wfEwS+SV1DU/cSe8fqHIPiYJFif8T3MVt4cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hashery": "^1.5.0", + "keyv": "^5.6.0" + } + }, + "node_modules/@cacheable/utils/node_modules/keyv": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@keyv/serialize": "^1.1.1" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.0.29", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.29.tgz", + "integrity": "sha512-jx9GjkkP5YHuTmko2eWAvpPnb0mB4mGRr2U7XwVNwevm8nlpobZEVk+GNmiYMk2VuA75v+plfXWyroWKmICZXg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0" + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/media-query-list-parser": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.3.tgz", + "integrity": "sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", + "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.0.0" + } + }, + "node_modules/@dual-bundle/import-meta-resolve": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.2.1.tgz", + "integrity": "sha512-id+7YRUgoUX6CgV0DtuhirQWodeeA7Lf4i2x71JS/vtA5pRb/hIGWlw+G6MeXvsM+MXrz0VAydTGElX1rAfgPg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/JounQin" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@keyv/serialize": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", + "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/sarif": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.7.tgz", + "integrity": "sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@vercel/speed-insights": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@vercel/speed-insights/-/speed-insights-2.0.0.tgz", + "integrity": "sha512-jwkNcrTeafWxjmWq4AHBaptSqZiJkYU5adLC9QBSqeim0GcqDMgN5Ievh8OG1rJ6W3A4l1oiP7qr9CWxGuzu3w==", + "license": "Apache-2.0", + "peerDependencies": { + "@sveltejs/kit": "^1 || ^2", + "next": ">= 13", + "nuxt": ">= 3", + "react": "^18 || ^19 || ^19.0.0-rc", + "svelte": ">= 4", + "vue": "^3", + "vue-router": "^4" + }, + "peerDependenciesMeta": { + "@sveltejs/kit": { + "optional": true + }, + "next": { + "optional": true + }, + "nuxt": { + "optional": true + }, + "react": { + "optional": true + }, + "svelte": { + "optional": true + }, + "vue": { + "optional": true + }, + "vue-router": { + "optional": true + } + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.3.3.tgz", + "integrity": "sha512-iffYMX4zxKp54evOH27fm92hs+DeC1DhXmNVN8Tr94M/iZIV42dqTHSR2Ik4TOSPyOAwKr7Yu3rN9ALoLkbWyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cacheable/memory": "^2.0.8", + "@cacheable/utils": "^2.4.0", + "hookified": "^1.15.0", + "keyv": "^5.6.0", + "qified": "^0.6.0" + } + }, + "node_modules/cacheable/node_modules/keyv": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@keyv/serialize": "^1.1.1" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz", + "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-functions-list": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.3.3.tgz", + "integrity": "sha512-8HFEBPKhOpJPEPu70wJJetjKta86Gw9+CCyCnB3sui2qQfOvRyqBy4IKLKKAwdMpWb2lHXWk9Wb4Z6AmaUT1Pg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/css-tree": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.0.tgz", + "integrity": "sha512-t99A4LolkP0ZX9WUoaHz4YrPT1FKNlV8IDCeCPPpGaWyxegh64tt/BSUqN3u5necrYRon+ddZ6mPMjxIlfpobg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fs-extra": { + "version": "11.3.4", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", + "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", + "dev": true, + "license": "MIT" + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hashery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.5.0.tgz", + "integrity": "sha512-nhQ6ExaOIqti2FDWoEMWARUqIKyjr2VcZzXShrI+A3zpeiuPWzx6iPftt44LhP74E5sW36B75N6VHbvRtpvO6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookified": "^1.14.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/hookified": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz", + "integrity": "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-tags": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/htmlhint": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/htmlhint/-/htmlhint-1.9.2.tgz", + "integrity": "sha512-PweWSPA1Pb+AVFIOSpIGu5KhLdmtk/uf/0CpjvrDf6XUWmdTyqUljlylwSxQ0AWLvPGcBxK2n8uISsI4lCOkBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async": "3.2.6", + "chalk": "4.1.2", + "commander": "11.1.0", + "glob": "^13.0.6", + "is-glob": "^4.0.3", + "node-sarif-builder": "3.2.0", + "strip-json-comments": "3.1.1", + "xml": "1.0.1" + }, + "bin": { + "htmlhint": "bin/htmlhint" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "Open Collective", + "url": "https://opencollective.com/htmlhint" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/known-css-properties": { + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.37.0.tgz", + "integrity": "sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "11.2.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", + "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/mathml-tag-names": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", + "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-sarif-builder": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-3.2.0.tgz", + "integrity": "sha512-kVIOdynrF2CRodHZeP/97Rh1syTUHBNiw17hUCIVhlhEsWlfJm19MuO56s4MdKbr22xWx6mzMnNAgXzVlIYM9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/sarif": "^2.1.7", + "fs-extra": "^11.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.13", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.13.tgz", + "integrity": "sha512-qif0+jGGZoLWdHey3UFHHWP0H7Gbmsk8T5VEqyYFbWqPr1XqvLGBbk/sl8V5exGmcYJklJOhOQq1pV9IcsiFag==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-resolve-nested-selector": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz", + "integrity": "sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-safe-parser": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz", + "integrity": "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qified": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/qified/-/qified-0.6.0.tgz", + "integrity": "sha512-tsSGN1x3h569ZSU1u6diwhltLyfUWDp3YbFHedapTmpBl0B3P6U3+Qptg7xu+v+1io1EwhdPyyRHYbEw0KN2FA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookified": "^1.14.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylelint": { + "version": "16.26.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.26.1.tgz", + "integrity": "sha512-v20V59/crfc8sVTAtge0mdafI3AdnzQ2KsWe6v523L4OA1bJO02S7MO2oyXDCS6iWb9ckIPnqAFVItqSBQr7jw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-syntax-patches-for-csstree": "^1.0.19", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/media-query-list-parser": "^4.0.3", + "@csstools/selector-specificity": "^5.0.0", + "@dual-bundle/import-meta-resolve": "^4.2.1", + "balanced-match": "^2.0.0", + "colord": "^2.9.3", + "cosmiconfig": "^9.0.0", + "css-functions-list": "^3.2.3", + "css-tree": "^3.1.0", + "debug": "^4.4.3", + "fast-glob": "^3.3.3", + "fastest-levenshtein": "^1.0.16", + "file-entry-cache": "^11.1.1", + "global-modules": "^2.0.0", + "globby": "^11.1.0", + "globjoin": "^0.1.4", + "html-tags": "^3.3.1", + "ignore": "^7.0.5", + "imurmurhash": "^0.1.4", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.37.0", + "mathml-tag-names": "^2.1.3", + "meow": "^13.2.0", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.5.6", + "postcss-resolve-nested-selector": "^0.1.6", + "postcss-safe-parser": "^7.0.1", + "postcss-selector-parser": "^7.1.0", + "postcss-value-parser": "^4.2.0", + "resolve-from": "^5.0.0", + "string-width": "^4.2.3", + "supports-hyperlinks": "^3.2.0", + "svg-tags": "^1.0.0", + "table": "^6.9.0", + "write-file-atomic": "^5.0.1" + }, + "bin": { + "stylelint": "bin/stylelint.mjs" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/stylelint-config-recommended": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-14.0.1.tgz", + "integrity": "sha512-bLvc1WOz/14aPImu/cufKAZYfXs/A/owZfSMZ4N+16WGXLoX5lOir53M6odBxvhgmgdxCVnNySJmZKx73T93cg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "stylelint": "^16.1.0" + } + }, + "node_modules/stylelint-config-standard": { + "version": "36.0.1", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-36.0.1.tgz", + "integrity": "sha512-8aX8mTzJ6cuO8mmD5yon61CWuIM4UD8Q5aBcWKGSf6kg+EC3uhB+iOywpTK4ca6ZL7B49en8yanOFtUW0qNzyw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "license": "MIT", + "dependencies": { + "stylelint-config-recommended": "^14.0.1" + }, + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "stylelint": "^16.1.0" + } + }, + "node_modules/stylelint/node_modules/balanced-match": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", + "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", + "dev": true, + "license": "MIT" + }, + "node_modules/stylelint/node_modules/file-entry-cache": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-11.1.2.tgz", + "integrity": "sha512-N2WFfK12gmrK1c1GXOqiAJ1tc5YE+R53zvQ+t5P8S5XhnmKYVB5eZEiLNZKDSmoG8wqqbF9EXYBBW/nef19log==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^6.1.20" + } + }, + "node_modules/stylelint/node_modules/flat-cache": { + "version": "6.1.20", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.20.tgz", + "integrity": "sha512-AhHYqwvN62NVLp4lObVXGVluiABTHapoB57EyegZVmazN+hhGhLTn3uZbOofoTw4DSDvVCadzzyChXhOAvy8uQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cacheable": "^2.3.2", + "flatted": "^3.3.3", + "hookified": "^1.15.0" + } + }, + "node_modules/stylelint/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/stylelint/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", + "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, + "node_modules/svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", + "dev": true + }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", + "dev": true, + "license": "MIT" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} From 059bcdb59500cbaa1fd0e09fa8b74884e1b731ee Mon Sep 17 00:00:00 2001 From: shravanithouta108 Date: Sun, 12 Jul 2026 16:39:20 +0530 Subject: [PATCH 37/45] fix: move ORGS global directive before JSDoc block in landing.js Signed-off-by: shravanithouta108 --- src/js/landing.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/landing.js b/src/js/landing.js index 8c000ad137..ad5d8092c5 100644 --- a/src/js/landing.js +++ b/src/js/landing.js @@ -1,5 +1,5 @@ -/** /* global ORGS */ +/** * landing.js โ€” FindMyGSoC Landing Page Scripts * * Responsibilities: From c169b416689d2e094a744dae78a59a34965854e8 Mon Sep 17 00:00:00 2001 From: shravanithouta108 Date: Sun, 12 Jul 2026 16:46:30 +0530 Subject: [PATCH 38/45] fix: add initial sync renderOrgs call to fix DOM smoke test Signed-off-by: shravanithouta108 --- src/js/app.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/js/app.js b/src/js/app.js index 8cdc7db918..885f3b1908 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -2265,6 +2265,7 @@ function applyStaleDataNotice() { } const _initFromURL = async () => { + renderOrgs(true); // initial sync render for tests await loadCommunityActivity(); const params = new URLSearchParams(location.search); if (params.get('q')) document.getElementById('searchInput').value = params.get('q'); From fabaa23715978046a3e18328e5f1ae9634a9f0b4 Mon Sep 17 00:00:00 2001 From: shravanithouta108 Date: Sun, 12 Jul 2026 16:58:03 +0530 Subject: [PATCH 39/45] fix: initialize filteredOrgs from ORGS before renderOrgs to fix DOM smoke test Signed-off-by: shravanithouta108 --- src/js/app.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/js/app.js b/src/js/app.js index 885f3b1908..44dd5abbcf 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -2265,6 +2265,7 @@ function applyStaleDataNotice() { } const _initFromURL = async () => { + if (typeof ORGS !== 'undefined' && Array.isArray(ORGS)) filteredOrgs = [...ORGS]; renderOrgs(true); // initial sync render for tests await loadCommunityActivity(); const params = new URLSearchParams(location.search); From ce054a4885eccf14a49b76980e6cb72ba0d8511e Mon Sep 17 00:00:00 2001 From: shravanithouta108 Date: Mon, 13 Jul 2026 15:42:04 +0530 Subject: [PATCH 40/45] fix: remove renderMentorFinder stub, add README docs, use create-pull-request in workflow, add bare owner warning Signed-off-by: shravanithouta108 --- .github/workflows/compute-activity-scores.yml | 17 ++++++++------- README.md | 21 +++++++++++++++++++ agent/scripts/compute-activity-scores.js | 2 ++ src/js/app.js | 3 --- 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/.github/workflows/compute-activity-scores.yml b/.github/workflows/compute-activity-scores.yml index c531b2cfb1..887ddbce5a 100644 --- a/.github/workflows/compute-activity-scores.yml +++ b/.github/workflows/compute-activity-scores.yml @@ -8,8 +8,6 @@ on: jobs: compute-scores: runs-on: ubuntu-latest - permissions: - contents: write steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -24,9 +22,12 @@ jobs: run: node agent/scripts/compute-activity-scores.js - name: Commit updated scores - run: | - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - git add data/community_activity.json - git diff --staged --quiet || git commit -m "chore: update community activity scores" - git push \ No newline at end of file + uses: peter-evans/create-pull-request@v7 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "chore: update community activity scores" + title: "chore: update community activity scores" + body: "Automated daily update of community activity scores." + branch: "auto/update-community-activity-scores" + delete-branch: true + add-paths: data/community_activity.json \ No newline at end of file diff --git a/README.md b/README.md index e28730b3f4..8314717265 100644 --- a/README.md +++ b/README.md @@ -486,6 +486,27 @@ The Edge Function proxies GitHub API calls so your token never hits the client. | `GET /api/github?repo=owner/repo&gfi=1` | Good First Issue count only (faster, cached separately) | | `GET /api/github?repo=owner/repo&gfi=1&issues=1` | Full list of up to 30 open Good First Issues | +--- +## ๐Ÿ† Community Activity Score + +Each organization is scored daily (0โ€“100) based on GitHub activity signals fetched by `.github/workflows/compute-activity-scores.yml`: + +| Signal | Weight | Description | +|--------|--------|-------------| +| Issue resolution time | 30% | Average days to close issues | +| Commit frequency | 20% | Commits in last 90 days | +| PR merge rate | 20% | % of PRs merged in 90 days | +| Repository freshness | 15% | Days since last push | +| Stars score | 15% | Scaled stargazers count | + +**Tiers:** +- ๐ŸŸข **Very Active** โ€” score 75โ€“100 +- ๐Ÿ”ต **Active** โ€” score 50โ€“74 +- ๐ŸŸก **Moderate** โ€” score 25โ€“49 +- ๐Ÿ”ด **Low Activity** โ€” score 0โ€“24 + +Scores are stored in `data/community_activity.json` and updated daily. Organizations with no GitHub data are excluded from scoring. + All responses are cached in-memory for **1 hour** on the Edge runtime. # ๐Ÿš€ Official Open Source Program Project diff --git a/agent/scripts/compute-activity-scores.js b/agent/scripts/compute-activity-scores.js index cbd32ca3cd..4e147925c8 100644 --- a/agent/scripts/compute-activity-scores.js +++ b/agent/scripts/compute-activity-scores.js @@ -32,6 +32,8 @@ for (const m of orgMatches) { const repo = m[2]; if (repo && repo.includes('/')) { githubRepos.push({ name, repo }); + } else { + console.warn(`Skipping bare owner (no repo path): ${name} โ†’ "${repo}"`); } } diff --git a/src/js/app.js b/src/js/app.js index 44dd5abbcf..f083e72e1d 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -766,9 +766,6 @@ function syncBookmark(name, shouldAdd) { updateAIInsights(); } -function renderMentorFinder() { - // Mentor finder is rendered by index.html's inline script -} function handleGlobalKeydown(e) { if (e.key === 'Escape' && handleEscapeKey(e)) return; From c6e96c05dcb54690c22d6f8f7b869f6ef641933b Mon Sep 17 00:00:00 2001 From: shravanithouta108 Date: Mon, 13 Jul 2026 16:12:24 +0530 Subject: [PATCH 41/45] fix: add renderMentorFinder global, pin peter-evans SHA, fix XSS escaping Signed-off-by: shravanithouta108 --- .github/workflows/compute-activity-scores.yml | 12 +++++------- index.html | 4 ++-- src/js/app.js | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/.github/workflows/compute-activity-scores.yml b/.github/workflows/compute-activity-scores.yml index 887ddbce5a..90f5e5f011 100644 --- a/.github/workflows/compute-activity-scores.yml +++ b/.github/workflows/compute-activity-scores.yml @@ -1,28 +1,26 @@ name: Compute Community Activity Scores - on: schedule: - cron: '0 0 * * *' # Daily at midnight workflow_dispatch: # Manual trigger - jobs: compute-scores: runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: Setup Node.js uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.2.0 with: node-version: '18' - - name: Run activity score computation env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: node agent/scripts/compute-activity-scores.js - - name: Commit updated scores - uses: peter-evans/create-pull-request@v7 + uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b2b3e36ac28eb95 # v7.0.8 with: token: ${{ secrets.GITHUB_TOKEN }} commit-message: "chore: update community activity scores" @@ -30,4 +28,4 @@ jobs: body: "Automated daily update of community activity scores." branch: "auto/update-community-activity-scores" delete-branch: true - add-paths: data/community_activity.json \ No newline at end of file + add-paths: data/community_activity.json diff --git a/index.html b/index.html index 6136157104..5024f268c0 100644 --- a/index.html +++ b/index.html @@ -2083,7 +2083,7 @@

${escapeHtml(a.label)}`; + return `${escapeHtml(a.label)}`; } // THEME // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• @@ -3421,7 +3421,7 @@

emoji_events ${unlockedCount}/${totalCount}`; + badgeIndicator.innerHTML = `emoji_events ${escapeHtml(String(unlockedCount))}/${escapeHtml(String(totalCount))}`; } } diff --git a/src/js/app.js b/src/js/app.js index f083e72e1d..d04c196b4d 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -1,4 +1,4 @@ -/* global ORGS, openModal, toggleCompare, toggleBookmark, openRandomOrg, clearAllFilters, openCompareModal, fetchModalGH, unselectLanguage, clearAllLanguages */ +/* global ORGS, renderMentorFinder, openModal, toggleCompare, toggleBookmark, openRandomOrg, clearAllFilters, openCompareModal, fetchModalGH, unselectLanguage, clearAllLanguages */ /* exported openAnalytics, closeAnEvent, fetchAll, fetchModalGH, toggleCompareFromModal, openCompare, closeCompareEv, imgErr, toggleBookmark, toggleChip, resetFilters, closeModalEv, openIssuesPage, closeIssuesPage, fetchAllIssues, showMoreIssues */ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• From bc13aa767dcd5c2336ae9232ef860fc13c3579f2 Mon Sep 17 00:00:00 2001 From: shravanithouta108 Date: Mon, 13 Jul 2026 16:20:47 +0530 Subject: [PATCH 42/45] fix: guard renderMentorFinder calls with typeof check for test environment Signed-off-by: shravanithouta108 --- src/js/app.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/js/app.js b/src/js/app.js index d04c196b4d..c66385a4ed 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -2237,7 +2237,7 @@ async function loadMentorData() { const data = await res.json(); MENTOR_DATA = data.mentors || {}; mentorDataState = 'loaded'; - renderMentorFinder(); + if (typeof renderMentorFinder === "function") renderMentorFinder(); } catch (err) { console.warn('Failed to load mentors.json:', err); mentorDataState = 'error'; @@ -2322,8 +2322,8 @@ const _initFromURL = async () => { document.getElementById('categoryFilter')?.addEventListener('change', applyFilters); document.getElementById('complexityFilter')?.addEventListener('change', applyFilters); document.getElementById('sortSelect')?.addEventListener('change', applyFilters); - document.getElementById('mentorSearchInput')?.addEventListener('input', renderMentorFinder); - document.getElementById('mentorChannelFilter')?.addEventListener('change', renderMentorFinder); + document.getElementById("mentorSearchInput")?.addEventListener("input", () => { if (typeof renderMentorFinder === "function") renderMentorFinder(); }); + document.getElementById("mentorChannelFilter")?.addEventListener("change", () => { if (typeof renderMentorFinder === "function") renderMentorFinder(); }); document.getElementById('matchAllLanguagesToggle')?.addEventListener('change', (e) => { matchAllLanguages = e.target.checked; globalThis.matchAllLanguages = matchAllLanguages; From a567f98077e6e6410d6a385fcced95e8bd22c6ef Mon Sep 17 00:00:00 2001 From: shravanithouta108 Date: Sat, 18 Jul 2026 21:32:45 +0530 Subject: [PATCH 43/45] fix: align README weights/tiers with code, return null for orgs with missing API data Signed-off-by: shravanithouta108 --- README.md | 4 ++-- agent/scripts/compute-activity-scores.js | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8314717265..b0e940f57e 100644 --- a/README.md +++ b/README.md @@ -495,8 +495,8 @@ Each organization is scored daily (0โ€“100) based on GitHub activity signals fet |--------|--------|-------------| | Issue resolution time | 30% | Average days to close issues | | Commit frequency | 20% | Commits in last 90 days | -| PR merge rate | 20% | % of PRs merged in 90 days | -| Repository freshness | 15% | Days since last push | +| PR merge rate | 15% | % of PRs merged in 90 days | +| Repository freshness | 20% | Days since last push | | Stars score | 15% | Scaled stargazers count | **Tiers:** diff --git a/agent/scripts/compute-activity-scores.js b/agent/scripts/compute-activity-scores.js index 4e147925c8..4df8571250 100644 --- a/agent/scripts/compute-activity-scores.js +++ b/agent/scripts/compute-activity-scores.js @@ -108,13 +108,16 @@ async function analyzeOrg({ name, repo }) { ? (Date.now() - new Date(repoData.pushed_at)) / 86400000 : 60; + if (issueResponseDays === null || prMergeRate === null) return null; + const score = computeScore({ - issueResponseDays: issueResponseDays ?? 14, + issueResponseDays: issueResponseDays, commitFrequency, - prMergeRate: prMergeRate ?? 0.5, + prMergeRate: prMergeRate, ideasFreshnessDays, starsGrowth }); + if (score === null) return null; const { tier, label } = getTier(score); return { From dbc85823fcf56fdc9b199b83cad49dc134b7aedc Mon Sep 17 00:00:00 2001 From: shravanithouta108 Date: Sat, 18 Jul 2026 21:38:55 +0530 Subject: [PATCH 44/45] fix: remove unreachable null check after computeScore Signed-off-by: shravanithouta108 --- agent/scripts/compute-activity-scores.js | 1 - 1 file changed, 1 deletion(-) diff --git a/agent/scripts/compute-activity-scores.js b/agent/scripts/compute-activity-scores.js index 4df8571250..e2f58d4306 100644 --- a/agent/scripts/compute-activity-scores.js +++ b/agent/scripts/compute-activity-scores.js @@ -117,7 +117,6 @@ async function analyzeOrg({ name, repo }) { ideasFreshnessDays, starsGrowth }); - if (score === null) return null; const { tier, label } = getTier(score); return { From 01a6ce7cdf80be1dee994db0dd9cca6b12d789fa Mon Sep 17 00:00:00 2001 From: shravanithouta108 Date: Sun, 19 Jul 2026 13:10:49 +0530 Subject: [PATCH 45/45] chore: add initial community activity scores from GitHub API Signed-off-by: shravanithouta108 --- data/community_activity.json | 1407 +++++++++++++++++++++++++++++++++- 1 file changed, 1406 insertions(+), 1 deletion(-) diff --git a/data/community_activity.json b/data/community_activity.json index 0967ef424b..aa641fe4b5 100644 --- a/data/community_activity.json +++ b/data/community_activity.json @@ -1 +1,1406 @@ -{} +{ + "AboutCode": { + "score": 27, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 51, + "commitFrequency": 0.4, + "prMergeRate": 39, + "ideasFreshnessDays": 10, + "starsGrowth": 2.6 + }, + "lastUpdated": "2026-07-19T07:10:09.187Z" + }, + "Accord Project": { + "score": 30, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 21, + "commitFrequency": 0.4, + "prMergeRate": 65, + "ideasFreshnessDays": 6, + "starsGrowth": 0.2 + }, + "lastUpdated": "2026-07-19T07:10:14.210Z" + }, + "AFLplusplus": { + "score": 38, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 137, + "commitFrequency": 1.1, + "prMergeRate": 91, + "ideasFreshnessDays": 1, + "starsGrowth": 6.7 + }, + "lastUpdated": "2026-07-19T07:10:18.306Z" + }, + "Alaska": { + "score": 60, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 1, + "commitFrequency": 0.1, + "prMergeRate": 77, + "ideasFreshnessDays": 3, + "starsGrowth": 0.3 + }, + "lastUpdated": "2026-07-19T07:10:22.244Z" + }, + "AnkiDroid": { + "score": 60, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 5, + "commitFrequency": 1.1, + "prMergeRate": 83, + "ideasFreshnessDays": 1, + "starsGrowth": 11.4 + }, + "lastUpdated": "2026-07-19T07:10:26.297Z" + }, + "Apache Software Foundation": { + "score": 63, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 2, + "commitFrequency": 1.1, + "prMergeRate": 0, + "ideasFreshnessDays": 1, + "starsGrowth": 43.7 + }, + "lastUpdated": "2026-07-19T07:10:33.980Z" + }, + "API Dash": { + "score": 25, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 36, + "commitFrequency": 0.6, + "prMergeRate": 28, + "ideasFreshnessDays": 9, + "starsGrowth": 2.9 + }, + "lastUpdated": "2026-07-19T07:10:39.516Z" + }, + "ArduPilot": { + "score": 64, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 3, + "commitFrequency": 1.1, + "prMergeRate": 75, + "ideasFreshnessDays": 0, + "starsGrowth": 15.5 + }, + "lastUpdated": "2026-07-19T07:10:44.005Z" + }, + "Boa": { + "score": 33, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 90, + "commitFrequency": 0.4, + "prMergeRate": 65, + "ideasFreshnessDays": 1, + "starsGrowth": 7.4 + }, + "lastUpdated": "2026-07-19T07:10:51.166Z" + }, + "BRL-CAD": { + "score": 26, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 51, + "commitFrequency": 1.1, + "prMergeRate": 20, + "ideasFreshnessDays": 0, + "starsGrowth": 1 + }, + "lastUpdated": "2026-07-19T07:10:55.672Z" + }, + "cBioPortal for Cancer Genomics": { + "score": 47, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 9, + "commitFrequency": 0.6, + "prMergeRate": 60, + "ideasFreshnessDays": 2, + "starsGrowth": 1 + }, + "lastUpdated": "2026-07-19T07:11:00.414Z" + }, + "CCExtractor Development": { + "score": 28, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 46, + "commitFrequency": 0.1, + "prMergeRate": 60, + "ideasFreshnessDays": 12, + "starsGrowth": 0.9 + }, + "lastUpdated": "2026-07-19T07:11:04.179Z" + }, + "CERN-HSF": { + "score": 35, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 73, + "commitFrequency": 0.3, + "prMergeRate": 97, + "ideasFreshnessDays": 2, + "starsGrowth": 0.1 + }, + "lastUpdated": "2026-07-19T07:11:13.087Z" + }, + "CGAL Project": { + "score": 45, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 15, + "commitFrequency": 1.1, + "prMergeRate": 90, + "ideasFreshnessDays": 4, + "starsGrowth": 6 + }, + "lastUpdated": "2026-07-19T07:11:17.911Z" + }, + "checkstyle": { + "score": 66, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 1, + "commitFrequency": 1.1, + "prMergeRate": 90, + "ideasFreshnessDays": 0, + "starsGrowth": 9 + }, + "lastUpdated": "2026-07-19T07:11:21.485Z" + }, + "CircuitVerse.org": { + "score": 54, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 3, + "commitFrequency": 1.1, + "prMergeRate": 40, + "ideasFreshnessDays": 1, + "starsGrowth": 1.2 + }, + "lastUpdated": "2026-07-19T07:11:26.513Z" + }, + "CNCF": { + "score": 66, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 1, + "commitFrequency": 1, + "prMergeRate": 81, + "ideasFreshnessDays": 1, + "starsGrowth": 9.9 + }, + "lastUpdated": "2026-07-19T07:11:30.798Z" + }, + "CRIU": { + "score": 35, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 32, + "commitFrequency": 1.1, + "prMergeRate": 82, + "ideasFreshnessDays": 3, + "starsGrowth": 3.9 + }, + "lastUpdated": "2026-07-19T07:11:34.517Z" + }, + "D Language Foundation": { + "score": 59, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 6, + "commitFrequency": 1.1, + "prMergeRate": 95, + "ideasFreshnessDays": 0, + "starsGrowth": 3.3 + }, + "lastUpdated": "2026-07-19T07:11:41.553Z" + }, + "Dart": { + "score": 50, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 4, + "commitFrequency": 1.1, + "prMergeRate": 0, + "ideasFreshnessDays": 1, + "starsGrowth": 11.2 + }, + "lastUpdated": "2026-07-19T07:11:46.370Z" + }, + "Data for the Common Good": { + "score": 67, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 2, + "commitFrequency": 1.1, + "prMergeRate": 92, + "ideasFreshnessDays": 0, + "starsGrowth": 12.3 + }, + "lastUpdated": "2026-07-19T07:11:50.666Z" + }, + "DBpedia": { + "score": 35, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 854, + "commitFrequency": 0.1, + "prMergeRate": 100, + "ideasFreshnessDays": 4, + "starsGrowth": 0.1 + }, + "lastUpdated": "2026-07-19T07:11:53.739Z" + }, + "dora-rs": { + "score": 56, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 1, + "commitFrequency": 1.1, + "prMergeRate": 32, + "ideasFreshnessDays": 0, + "starsGrowth": 3.8 + }, + "lastUpdated": "2026-07-19T07:12:09.000Z" + }, + "Eclipse Foundation": { + "score": 64, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 2, + "commitFrequency": 1.1, + "prMergeRate": 92, + "ideasFreshnessDays": 0, + "starsGrowth": 0.2 + }, + "lastUpdated": "2026-07-19T07:12:18.215Z" + }, + "FLARE": { + "score": 33, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 126, + "commitFrequency": 1.1, + "prMergeRate": 63, + "ideasFreshnessDays": 2, + "starsGrowth": 6.1 + }, + "lastUpdated": "2026-07-19T07:12:29.059Z" + }, + "FOSSology": { + "score": 39, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 16, + "commitFrequency": 1.1, + "prMergeRate": 72, + "ideasFreshnessDays": 2, + "starsGrowth": 1 + }, + "lastUpdated": "2026-07-19T07:12:36.333Z" + }, + "Fortran-lang": { + "score": 36, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 47, + "commitFrequency": 0.2, + "prMergeRate": 100, + "ideasFreshnessDays": 1, + "starsGrowth": 1.3 + }, + "lastUpdated": "2026-07-19T07:12:42.789Z" + }, + "Free and Open Source Silicon Foundation": { + "score": 33, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 270, + "commitFrequency": 0.6, + "prMergeRate": 78, + "ideasFreshnessDays": 2, + "starsGrowth": 0 + }, + "lastUpdated": "2026-07-19T07:12:46.672Z" + }, + "FreeCAD": { + "score": 72, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 1, + "commitFrequency": 1.1, + "prMergeRate": 82, + "ideasFreshnessDays": 0, + "starsGrowth": 32.2 + }, + "lastUpdated": "2026-07-19T07:12:52.407Z" + }, + "Gambit: The package for computation in game theory": { + "score": 51, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 9, + "commitFrequency": 0.9, + "prMergeRate": 89, + "ideasFreshnessDays": 3, + "starsGrowth": 0.6 + }, + "lastUpdated": "2026-07-19T07:12:57.058Z" + }, + "Gemini CLI": { + "score": 57, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 10, + "commitFrequency": 1.1, + "prMergeRate": 25, + "ideasFreshnessDays": 0, + "starsGrowth": 50 + }, + "lastUpdated": "2026-07-19T07:13:03.170Z" + }, + "German Center for Open Source AI": { + "score": 27, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 143, + "commitFrequency": 0.1, + "prMergeRate": 47, + "ideasFreshnessDays": 5, + "starsGrowth": 0.4 + }, + "lastUpdated": "2026-07-19T07:13:15.018Z" + }, + "GNU Radio": { + "score": 28, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 110, + "commitFrequency": 0.1, + "prMergeRate": 44, + "ideasFreshnessDays": 10, + "starsGrowth": 6.2 + }, + "lastUpdated": "2026-07-19T07:13:49.259Z" + }, + "GRAME": { + "score": 50, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 7, + "commitFrequency": 0.2, + "prMergeRate": 64, + "ideasFreshnessDays": 0, + "starsGrowth": 3.1 + }, + "lastUpdated": "2026-07-19T07:13:57.649Z" + }, + "Graphite": { + "score": 67, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 4, + "commitFrequency": 1.1, + "prMergeRate": 84, + "ideasFreshnessDays": 0, + "starsGrowth": 26.6 + }, + "lastUpdated": "2026-07-19T07:14:03.776Z" + }, + "Haskell.org": { + "score": 33, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 175, + "commitFrequency": 0.6, + "prMergeRate": 76, + "ideasFreshnessDays": 1, + "starsGrowth": 2.9 + }, + "lastUpdated": "2026-07-19T07:14:11.570Z" + }, + "Internet Archive": { + "score": 60, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 4, + "commitFrequency": 1.1, + "prMergeRate": 77, + "ideasFreshnessDays": 0, + "starsGrowth": 6.6 + }, + "lastUpdated": "2026-07-19T07:14:25.061Z" + }, + "Invesalius": { + "score": 33, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 1207, + "commitFrequency": 1.1, + "prMergeRate": 71, + "ideasFreshnessDays": 0, + "starsGrowth": 0.8 + }, + "lastUpdated": "2026-07-19T07:14:29.291Z" + }, + "JabRef e.V.": { + "score": 61, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 4, + "commitFrequency": 1.1, + "prMergeRate": 84, + "ideasFreshnessDays": 0, + "starsGrowth": 4.4 + }, + "lastUpdated": "2026-07-19T07:14:35.739Z" + }, + "Jenkins": { + "score": 67, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 2, + "commitFrequency": 1.1, + "prMergeRate": 69, + "ideasFreshnessDays": 1, + "starsGrowth": 25.6 + }, + "lastUpdated": "2026-07-19T07:14:46.899Z" + }, + "Jitsi": { + "score": 73, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 1, + "commitFrequency": 1.1, + "prMergeRate": 89, + "ideasFreshnessDays": 2, + "starsGrowth": 29.6 + }, + "lastUpdated": "2026-07-19T07:14:50.776Z" + }, + "Joomla!": { + "score": 59, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 5, + "commitFrequency": 1.1, + "prMergeRate": 84, + "ideasFreshnessDays": 1, + "starsGrowth": 5.1 + }, + "lastUpdated": "2026-07-19T07:14:55.193Z" + }, + "Joplin": { + "score": 72, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 3, + "commitFrequency": 1.1, + "prMergeRate": 66, + "ideasFreshnessDays": 0, + "starsGrowth": 50 + }, + "lastUpdated": "2026-07-19T07:14:59.287Z" + }, + "JSON Schema": { + "score": 35, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 205, + "commitFrequency": 0.1, + "prMergeRate": 92, + "ideasFreshnessDays": 5, + "starsGrowth": 5.1 + }, + "lastUpdated": "2026-07-19T07:15:03.903Z" + }, + "Kiwix": { + "score": 55, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 8, + "commitFrequency": 1.1, + "prMergeRate": 92, + "ideasFreshnessDays": 1, + "starsGrowth": 1.4 + }, + "lastUpdated": "2026-07-19T07:15:11.348Z" + }, + "Konflux": { + "score": 66, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 0, + "commitFrequency": 1.1, + "prMergeRate": 98, + "ideasFreshnessDays": 0, + "starsGrowth": 0.2 + }, + "lastUpdated": "2026-07-19T07:15:19.355Z" + }, + "Kornia": { + "score": 60, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 6, + "commitFrequency": 1.1, + "prMergeRate": 88, + "ideasFreshnessDays": 0, + "starsGrowth": 11.3 + }, + "lastUpdated": "2026-07-19T07:15:23.627Z" + }, + "Kubeflow": { + "score": 38, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 567, + "commitFrequency": 0.2, + "prMergeRate": 93, + "ideasFreshnessDays": 9, + "starsGrowth": 15.8 + }, + "lastUpdated": "2026-07-19T07:15:31.848Z" + }, + "KubeVirt": { + "score": 64, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 2, + "commitFrequency": 1.1, + "prMergeRate": 90, + "ideasFreshnessDays": 0, + "starsGrowth": 7 + }, + "lastUpdated": "2026-07-19T07:15:36.966Z" + }, + "Learning Equality": { + "score": 55, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 7, + "commitFrequency": 1.1, + "prMergeRate": 87, + "ideasFreshnessDays": 1, + "starsGrowth": 1.1 + }, + "lastUpdated": "2026-07-19T07:15:44.954Z" + }, + "Learning Unlimited": { + "score": 45, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 11, + "commitFrequency": 1.1, + "prMergeRate": 66, + "ideasFreshnessDays": 1, + "starsGrowth": 0.2 + }, + "lastUpdated": "2026-07-19T07:15:49.979Z" + }, + "LLVM Compiler Infrastructure": { + "score": 77, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 0, + "commitFrequency": 1.1, + "prMergeRate": 89, + "ideasFreshnessDays": 0, + "starsGrowth": 39.4 + }, + "lastUpdated": "2026-07-19T07:16:09.706Z" + }, + "MDAnalysis": { + "score": 32, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 96, + "commitFrequency": 0.4, + "prMergeRate": 75, + "ideasFreshnessDays": 4, + "starsGrowth": 1.6 + }, + "lastUpdated": "2026-07-19T07:16:21.313Z" + }, + "Meshery": { + "score": 64, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 2, + "commitFrequency": 1.1, + "prMergeRate": 70, + "ideasFreshnessDays": 0, + "starsGrowth": 11.4 + }, + "lastUpdated": "2026-07-19T07:16:25.676Z" + }, + "MetaCall": { + "score": 32, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 43, + "commitFrequency": 1.1, + "prMergeRate": 64, + "ideasFreshnessDays": 1, + "starsGrowth": 1.8 + }, + "lastUpdated": "2026-07-19T07:16:34.292Z" + }, + "Metaflow": { + "score": 47, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 11, + "commitFrequency": 0.9, + "prMergeRate": 62, + "ideasFreshnessDays": 0, + "starsGrowth": 10.2 + }, + "lastUpdated": "2026-07-19T07:16:38.928Z" + }, + "Metasploit": { + "score": 67, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 6, + "commitFrequency": 1.1, + "prMergeRate": 79, + "ideasFreshnessDays": 1, + "starsGrowth": 38.6 + }, + "lastUpdated": "2026-07-19T07:16:43.531Z" + }, + "MIT App Inventor": { + "score": 46, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 12, + "commitFrequency": 0.9, + "prMergeRate": 75, + "ideasFreshnessDays": 1, + "starsGrowth": 1.7 + }, + "lastUpdated": "2026-07-19T07:16:48.755Z" + }, + "Mixxx": { + "score": 64, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 2, + "commitFrequency": 1.1, + "prMergeRate": 81, + "ideasFreshnessDays": 1, + "starsGrowth": 6.9 + }, + "lastUpdated": "2026-07-19T07:16:53.156Z" + }, + "MoFA Org": { + "score": 26, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 30, + "commitFrequency": 0.3, + "prMergeRate": 36, + "ideasFreshnessDays": 2, + "starsGrowth": 0.3 + }, + "lastUpdated": "2026-07-19T07:17:04.426Z" + }, + "National Resource for Network Biology (NRNB)": { + "score": 35, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 176, + "commitFrequency": 0.8, + "prMergeRate": 72, + "ideasFreshnessDays": 3, + "starsGrowth": 11.1 + }, + "lastUpdated": "2026-07-19T07:17:11.480Z" + }, + "Neovim": { + "score": 79, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 1, + "commitFrequency": 1.1, + "prMergeRate": 88, + "ideasFreshnessDays": 0, + "starsGrowth": 50 + }, + "lastUpdated": "2026-07-19T07:17:15.613Z" + }, + "Neuroinformatics Unit": { + "score": 30, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 169, + "commitFrequency": 0.5, + "prMergeRate": 63, + "ideasFreshnessDays": 2, + "starsGrowth": 0.3 + }, + "lastUpdated": "2026-07-19T07:17:21.734Z" + }, + "Neutralinojs": { + "score": 34, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 55, + "commitFrequency": 0.8, + "prMergeRate": 63, + "ideasFreshnessDays": 0, + "starsGrowth": 8.6 + }, + "lastUpdated": "2026-07-19T07:17:26.130Z" + }, + "NixOS Foundation": { + "score": 74, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 0, + "commitFrequency": 1.1, + "prMergeRate": 94, + "ideasFreshnessDays": 0, + "starsGrowth": 25.5 + }, + "lastUpdated": "2026-07-19T07:17:31.048Z" + }, + "NumFOCUS": { + "score": 71, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 1, + "commitFrequency": 1.1, + "prMergeRate": 73, + "ideasFreshnessDays": 1, + "starsGrowth": 32.4 + }, + "lastUpdated": "2026-07-19T07:17:37.098Z" + }, + "omegaUp": { + "score": 49, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 11, + "commitFrequency": 0.9, + "prMergeRate": 84, + "ideasFreshnessDays": 0, + "starsGrowth": 0.4 + }, + "lastUpdated": "2026-07-19T07:17:43.334Z" + }, + "Open Food Facts": { + "score": 50, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 8, + "commitFrequency": 1.1, + "prMergeRate": 61, + "ideasFreshnessDays": 1, + "starsGrowth": 1.1 + }, + "lastUpdated": "2026-07-19T07:17:49.296Z" + }, + "Open Science Initiative for Perfusion Imaging": { + "score": 61, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 3, + "commitFrequency": 0.4, + "prMergeRate": 100, + "ideasFreshnessDays": 1, + "starsGrowth": 0 + }, + "lastUpdated": "2026-07-19T07:18:05.753Z" + }, + "Open Transit Software Foundation": { + "score": 67, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 0, + "commitFrequency": 1.1, + "prMergeRate": 96, + "ideasFreshnessDays": 0, + "starsGrowth": 0.6 + }, + "lastUpdated": "2026-07-19T07:18:15.584Z" + }, + "OpenAstronomy": { + "score": 46, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 12, + "commitFrequency": 1.1, + "prMergeRate": 89, + "ideasFreshnessDays": 10, + "starsGrowth": 1 + }, + "lastUpdated": "2026-07-19T07:18:23.468Z" + }, + "OpenELIS Global": { + "score": 35, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 27, + "commitFrequency": 1.1, + "prMergeRate": 86, + "ideasFreshnessDays": 1, + "starsGrowth": 0.2 + }, + "lastUpdated": "2026-07-19T07:18:32.886Z" + }, + "OpenMRS": { + "score": 63, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 0, + "commitFrequency": 1.1, + "prMergeRate": 72, + "ideasFreshnessDays": 0, + "starsGrowth": 1.9 + }, + "lastUpdated": "2026-07-19T07:18:37.495Z" + }, + "OpenMS Inc": { + "score": 65, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 1, + "commitFrequency": 1.1, + "prMergeRate": 95, + "ideasFreshnessDays": 0, + "starsGrowth": 0.6 + }, + "lastUpdated": "2026-07-19T07:18:42.513Z" + }, + "OpenStreetMap": { + "score": 60, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 5, + "commitFrequency": 1.1, + "prMergeRate": 93, + "ideasFreshnessDays": 3, + "starsGrowth": 2.8 + }, + "lastUpdated": "2026-07-19T07:18:47.239Z" + }, + "openSUSE Project": { + "score": 65, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 1, + "commitFrequency": 1.1, + "prMergeRate": 89, + "ideasFreshnessDays": 2, + "starsGrowth": 1.1 + }, + "lastUpdated": "2026-07-19T07:18:52.241Z" + }, + "OpenWISP": { + "score": 31, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 295, + "commitFrequency": 0.6, + "prMergeRate": 65, + "ideasFreshnessDays": 2, + "starsGrowth": 0.8 + }, + "lastUpdated": "2026-07-19T07:19:02.583Z" + }, + "Oppia Foundation": { + "score": 53, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 5, + "commitFrequency": 1.1, + "prMergeRate": 44, + "ideasFreshnessDays": 1, + "starsGrowth": 6.7 + }, + "lastUpdated": "2026-07-19T07:19:07.913Z" + }, + "OSGeo (Open Source Geospatial Foundation)": { + "score": 63, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 3, + "commitFrequency": 1.1, + "prMergeRate": 94, + "ideasFreshnessDays": 1, + "starsGrowth": 6 + }, + "lastUpdated": "2026-07-19T07:19:13.843Z" + }, + "PEcAn Project": { + "score": 43, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 16, + "commitFrequency": 1.1, + "prMergeRate": 93, + "ideasFreshnessDays": 3, + "starsGrowth": 0.2 + }, + "lastUpdated": "2026-07-19T07:19:23.393Z" + }, + "Pharo Consortium": { + "score": 62, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 3, + "commitFrequency": 1.1, + "prMergeRate": 92, + "ideasFreshnessDays": 3, + "starsGrowth": 1.5 + }, + "lastUpdated": "2026-07-19T07:19:27.875Z" + }, + "preCICE": { + "score": 32, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 388, + "commitFrequency": 0.4, + "prMergeRate": 75, + "ideasFreshnessDays": 7, + "starsGrowth": 1 + }, + "lastUpdated": "2026-07-19T07:19:35.531Z" + }, + "Processing Foundation": { + "score": 24, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 2246, + "commitFrequency": 0.1, + "prMergeRate": 16, + "ideasFreshnessDays": 4, + "starsGrowth": 6.5 + }, + "lastUpdated": "2026-07-19T07:19:41.348Z" + }, + "Project Mesa": { + "score": 31, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 481, + "commitFrequency": 0.4, + "prMergeRate": 57, + "ideasFreshnessDays": 1, + "starsGrowth": 3.7 + }, + "lastUpdated": "2026-07-19T07:19:48.157Z" + }, + "Python Software Foundation": { + "score": 80, + "tier": "very-active", + "label": "๐ŸŸข Very Active", + "signals": { + "issueResponseDays": 0, + "commitFrequency": 1.1, + "prMergeRate": 89, + "ideasFreshnessDays": 0, + "starsGrowth": 50 + }, + "lastUpdated": "2026-07-19T07:19:52.775Z" + }, + "Rizin": { + "score": 55, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 8, + "commitFrequency": 1.1, + "prMergeRate": 96, + "ideasFreshnessDays": 1, + "starsGrowth": 3.7 + }, + "lastUpdated": "2026-07-19T07:20:14.888Z" + }, + "SageMath": { + "score": 56, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 7, + "commitFrequency": 1.1, + "prMergeRate": 90, + "ideasFreshnessDays": 3, + "starsGrowth": 2.5 + }, + "lastUpdated": "2026-07-19T07:20:33.261Z" + }, + "stdlib": { + "score": 65, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 2, + "commitFrequency": 1.1, + "prMergeRate": 91, + "ideasFreshnessDays": 0, + "starsGrowth": 5.9 + }, + "lastUpdated": "2026-07-19T07:20:45.409Z" + }, + "Ste||ar group": { + "score": 65, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 1, + "commitFrequency": 1.1, + "prMergeRate": 92, + "ideasFreshnessDays": 1, + "starsGrowth": 2.9 + }, + "lastUpdated": "2026-07-19T07:20:52.469Z" + }, + "Stichting SU2": { + "score": 33, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 67, + "commitFrequency": 0.2, + "prMergeRate": 79, + "ideasFreshnessDays": 0, + "starsGrowth": 1.8 + }, + "lastUpdated": "2026-07-19T07:20:56.567Z" + }, + "Submitty": { + "score": 49, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 11, + "commitFrequency": 1.1, + "prMergeRate": 85, + "ideasFreshnessDays": 1, + "starsGrowth": 0.8 + }, + "lastUpdated": "2026-07-19T07:21:01.584Z" + }, + "SW360": { + "score": 61, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 4, + "commitFrequency": 1.1, + "prMergeRate": 96, + "ideasFreshnessDays": 2, + "starsGrowth": 0.2 + }, + "lastUpdated": "2026-07-19T07:21:10.765Z" + }, + "Swift": { + "score": 80, + "tier": "very-active", + "label": "๐ŸŸข Very Active", + "signals": { + "issueResponseDays": 1, + "commitFrequency": 1.1, + "prMergeRate": 94, + "ideasFreshnessDays": 0, + "starsGrowth": 50 + }, + "lastUpdated": "2026-07-19T07:21:15.707Z" + }, + "SymPy": { + "score": 55, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 6, + "commitFrequency": 1.1, + "prMergeRate": 49, + "ideasFreshnessDays": 1, + "starsGrowth": 14.8 + }, + "lastUpdated": "2026-07-19T07:21:20.515Z" + }, + "Synfig": { + "score": 29, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 803, + "commitFrequency": 0.3, + "prMergeRate": 52, + "ideasFreshnessDays": 0, + "starsGrowth": 2.3 + }, + "lastUpdated": "2026-07-19T07:21:24.613Z" + }, + "TARDIS RT Collaboration": { + "score": 39, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 15, + "commitFrequency": 0.7, + "prMergeRate": 72, + "ideasFreshnessDays": 0, + "starsGrowth": 0.2 + }, + "lastUpdated": "2026-07-19T07:21:30.760Z" + }, + "The JPF team": { + "score": 25, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 1708, + "commitFrequency": 0, + "prMergeRate": 33, + "ideasFreshnessDays": 3, + "starsGrowth": 0.6 + }, + "lastUpdated": "2026-07-19T07:21:42.966Z" + }, + "The Julia Language": { + "score": 77, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 3, + "commitFrequency": 1.1, + "prMergeRate": 91, + "ideasFreshnessDays": 0, + "starsGrowth": 48.9 + }, + "lastUpdated": "2026-07-19T07:21:46.738Z" + }, + "The Libreswan Project": { + "score": 54, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 6, + "commitFrequency": 1.1, + "prMergeRate": 65, + "ideasFreshnessDays": 0, + "starsGrowth": 1 + }, + "lastUpdated": "2026-07-19T07:21:51.542Z" + }, + "The OpenROAD Initiative": { + "score": 58, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 5, + "commitFrequency": 1.1, + "prMergeRate": 85, + "ideasFreshnessDays": 0, + "starsGrowth": 2.9 + }, + "lastUpdated": "2026-07-19T07:22:11.726Z" + }, + "The P4 Language Consortium": { + "score": 36, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 60, + "commitFrequency": 0.8, + "prMergeRate": 91, + "ideasFreshnessDays": 0, + "starsGrowth": 0.8 + }, + "lastUpdated": "2026-07-19T07:22:16.025Z" + }, + "The Rust Foundation": { + "score": 78, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 1, + "commitFrequency": 1.1, + "prMergeRate": 74, + "ideasFreshnessDays": 0, + "starsGrowth": 50 + }, + "lastUpdated": "2026-07-19T07:22:21.669Z" + }, + "Tiled": { + "score": 38, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 569, + "commitFrequency": 0.8, + "prMergeRate": 83, + "ideasFreshnessDays": 2, + "starsGrowth": 12.7 + }, + "lastUpdated": "2026-07-19T07:22:26.062Z" + }, + "Typelevel": { + "score": 58, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 4, + "commitFrequency": 0.4, + "prMergeRate": 76, + "ideasFreshnessDays": 1, + "starsGrowth": 5.5 + }, + "lastUpdated": "2026-07-19T07:22:29.644Z" + }, + "Unikraft": { + "score": 34, + "tier": "low", + "label": "๐Ÿ”ด Low Activity", + "signals": { + "issueResponseDays": 34, + "commitFrequency": 0.5, + "prMergeRate": 84, + "ideasFreshnessDays": 3, + "starsGrowth": 3.8 + }, + "lastUpdated": "2026-07-19T07:22:37.632Z" + }, + "Wagtail": { + "score": 53, + "tier": "moderate", + "label": "๐ŸŸก Moderate", + "signals": { + "issueResponseDays": 9, + "commitFrequency": 1.1, + "prMergeRate": 49, + "ideasFreshnessDays": 1, + "starsGrowth": 20.4 + }, + "lastUpdated": "2026-07-19T07:22:48.793Z" + }, + "webpack": { + "score": 78, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 1, + "commitFrequency": 1.1, + "prMergeRate": 80, + "ideasFreshnessDays": 0, + "starsGrowth": 50 + }, + "lastUpdated": "2026-07-19T07:22:53.448Z" + }, + "Zulip": { + "score": 68, + "tier": "active", + "label": "๐Ÿ”ต Active", + "signals": { + "issueResponseDays": 2, + "commitFrequency": 1.1, + "prMergeRate": 71, + "ideasFreshnessDays": 2, + "starsGrowth": 25.5 + }, + "lastUpdated": "2026-07-19T07:23:01.186Z" + } +} \ No newline at end of file