From bb3fefc72b27de44d82a8b0b4bedb496929e7241 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 19 Feb 2026 19:34:26 +0000 Subject: [PATCH] feat: add live GitHub stars badge to homepage hero section Fetches the star count from the GitHub API client-side and displays it as a badge next to the "Get Started" button. Results are cached in localStorage for 1 hour to avoid rate limits. Falls back to the cached value on API errors. https://claude.ai/code/session_01EyaWacx4KxMtxPMbNdJjwa --- .../src/components/GitHubStars/index.js | 82 +++++++++++++++++++ .../components/GitHubStars/styles.module.css | 45 ++++++++++ docusaurus/src/pages/index.js | 2 + 3 files changed, 129 insertions(+) create mode 100644 docusaurus/src/components/GitHubStars/index.js create mode 100644 docusaurus/src/components/GitHubStars/styles.module.css diff --git a/docusaurus/src/components/GitHubStars/index.js b/docusaurus/src/components/GitHubStars/index.js new file mode 100644 index 00000000..9843476a --- /dev/null +++ b/docusaurus/src/components/GitHubStars/index.js @@ -0,0 +1,82 @@ +import React, {useState, useEffect} from 'react'; +import styles from './styles.module.css'; + +const REPO = 'devoxx/DevoxxGenieIDEAPlugin'; +const CACHE_KEY = 'devoxxgenie_github_stars'; +const CACHE_TTL = 3600000; // 1 hour in milliseconds + +function formatStars(count) { + if (count >= 1000) { + return (count / 1000).toFixed(1).replace(/\.0$/, '') + 'k'; + } + return count.toLocaleString(); +} + +export default function GitHubStars() { + const [stars, setStars] = useState(null); + + useEffect(() => { + // Check localStorage cache first + try { + const cached = JSON.parse(localStorage.getItem(CACHE_KEY)); + if (cached && Date.now() - cached.timestamp < CACHE_TTL) { + setStars(cached.count); + return; + } + } catch { + // ignore parse errors + } + + fetch(`https://api.github.com/repos/${REPO}`) + .then((res) => { + if (!res.ok) throw new Error('GitHub API error'); + return res.json(); + }) + .then((data) => { + const count = data.stargazers_count; + setStars(count); + try { + localStorage.setItem( + CACHE_KEY, + JSON.stringify({count, timestamp: Date.now()}) + ); + } catch { + // ignore storage errors + } + }) + .catch(() => { + // On error, show cached value if available (even if expired) + try { + const cached = JSON.parse(localStorage.getItem(CACHE_KEY)); + if (cached) setStars(cached.count); + } catch { + // ignore + } + }); + }, []); + + if (stars === null) return null; + + return ( + + + {formatStars(stars)} + Stars + + ); +} diff --git a/docusaurus/src/components/GitHubStars/styles.module.css b/docusaurus/src/components/GitHubStars/styles.module.css new file mode 100644 index 00000000..4cd4a0ce --- /dev/null +++ b/docusaurus/src/components/GitHubStars/styles.module.css @@ -0,0 +1,45 @@ +.starBadge { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 8px 16px; + border-radius: 8px; + background: rgba(255, 255, 255, 0.15); + backdrop-filter: blur(4px); + border: 1px solid rgba(255, 255, 255, 0.3); + color: #fff; + font-size: 0.95rem; + font-weight: 600; + text-decoration: none; + transition: background 0.2s ease, transform 0.2s ease; + cursor: pointer; + margin-left: 12px; +} + +.starBadge:hover { + background: rgba(255, 255, 255, 0.25); + transform: translateY(-1px); + color: #fff; + text-decoration: none; +} + +.starIcon { + color: #ffd700; + flex-shrink: 0; +} + +.starCount { + font-variant-numeric: tabular-nums; +} + +.starLabel { + opacity: 0.85; + font-weight: 400; +} + +@media (max-width: 600px) { + .starBadge { + margin-left: 0; + margin-top: 10px; + } +} diff --git a/docusaurus/src/pages/index.js b/docusaurus/src/pages/index.js index 9830fd6a..acb1dabd 100644 --- a/docusaurus/src/pages/index.js +++ b/docusaurus/src/pages/index.js @@ -6,6 +6,7 @@ import useBaseUrl from '@docusaurus/useBaseUrl'; import Layout from '@theme/Layout'; import Head from '@docusaurus/Head'; import HomepageFeatures from '@site/src/components/HomepageFeatures'; +import GitHubStars from '@site/src/components/GitHubStars'; const softwareSchema = { '@context': 'https://schema.org', @@ -55,6 +56,7 @@ function HomepageHeader() { to={useBaseUrl('/docs/getting-started/introduction')}> Get Started with DevoxxGenie +