diff --git a/js/nodes_stats.js b/js/nodes_stats.js index 43a6b67..0c066e5 100644 --- a/js/nodes_stats.js +++ b/js/nodes_stats.js @@ -92,7 +92,7 @@ async function showStatsDialog() { ${unusedNew.length} unused <1 month -
+
${used.length} used
@@ -144,6 +144,25 @@ async function showStatsDialog() { } }); }); + + // Easter egg: click "used" badge 5 times to show podium + let eggClicks = 0; + let eggTimer = null; + const usedBadge = document.getElementById("nodes-stats-used-badge"); + if (usedBadge) { + usedBadge.addEventListener("click", () => { + eggClicks++; + clearTimeout(eggTimer); + eggTimer = setTimeout(() => (eggClicks = 0), 1500); + if (eggClicks >= 5) { + eggClicks = 0; + const allNodes = custom + .flatMap((p) => p.nodes.map((n) => ({ ...n, pkg: p.package }))) + .sort((a, b) => b.count - a.count); + showPodium(allNodes.slice(0, 3), overlay); + } + }); + } } function sectionHeader(title, subtitle, color) { @@ -211,6 +230,126 @@ function buildTable(packages, status) { return html; } +// Internal: builds celebratory overlay for top contributors +function showPodium(top3, overlay) { + const existing = document.getElementById("nodes-stats-podium"); + if (existing) { existing.remove(); return; } + + const colors = ["#FFD700", "#C0C0C0", "#CD7F32"]; + const heights = [160, 120, 90]; + const order = [1, 0, 2]; + + // SVG characters: champion with cape, cool runner-up, happy bronze + const characters = [ + // Gold: flexing champion with crown and cape + ` + + + + + + + + + + + + + GOAT + `, + // Silver: sunglasses dude, arms crossed + ` + + + + + + + + + cool. + `, + // Bronze: happy little guy waving + ` + + + + + + + + + + + yay! + `, + ]; + + const podium = document.createElement("div"); + podium.id = "nodes-stats-podium"; + podium.style.cssText = + "position:absolute;top:0;left:0;width:100%;height:100%;background:radial-gradient(ellipse at center,#1a1a2e 0%,#0a0a12 100%);display:flex;flex-direction:column;align-items:center;justify-content:center;border-radius:8px;z-index:1;cursor:pointer;overflow:hidden;"; + podium.addEventListener("click", () => podium.remove()); + + // Sparkle particles + let sparkles = ""; + for (let i = 0; i < 20; i++) { + const x = Math.random() * 100; + const y = Math.random() * 60; + const d = (1 + Math.random() * 2).toFixed(1); + const o = (0.3 + Math.random() * 0.7).toFixed(2); + sparkles += `
`; + } + + let html = ``; + html += sparkles; + + // Trophy title + html += `
+ + + + + + + #1 + +
Hall of Fame
+
`; + + // Podium blocks + html += `
`; + + for (const i of order) { + const node = top3[i]; + if (!node) continue; + const isGold = i === 0; + const w = isGold ? 170 : 140; + const floatDelay = [0, 0.3, 0.6][i]; + + html += `
+
${characters[i]}
+
${escapeHtml(node.class_type)}
+
${escapeHtml(node.pkg)}
+
+
${i + 1}${["st","nd","rd"][i]}
+
${node.count.toLocaleString()}x
+
+
`; + } + + html += `
`; + html += `
click to dismiss
`; + + podium.innerHTML = html; + overlay.querySelector("div").style.position = "relative"; + overlay.querySelector("div").appendChild(podium); +} + function escapeHtml(str) { const div = document.createElement("div"); div.textContent = str;