From 599d1a5bfd42c9b90ffeb8bab3d09db67df2dc95 Mon Sep 17 00:00:00 2001 From: Muhammad Date: Sat, 15 Aug 2026 20:40:27 +0500 Subject: [PATCH 1/6] Create BadgeSnippet.jsx --- components/BadgeSnippet.jsx | 82 +++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 components/BadgeSnippet.jsx diff --git a/components/BadgeSnippet.jsx b/components/BadgeSnippet.jsx new file mode 100644 index 0000000..c4da4a0 --- /dev/null +++ b/components/BadgeSnippet.jsx @@ -0,0 +1,82 @@ +'use client'; + +import { useState } from 'react'; + +const STAT_OPTIONS = [ + { value: 'globalRank', label: 'Global Rank' }, + { value: 'countryRank', label: 'Country Rank' }, + { value: 'cityRank', label: 'City Rank' }, + { value: 'score', label: 'Score' }, + { value: 'stars', label: 'Stars' }, +]; + +export default function BadgeSnippet({ login, siteUrl, compact = false }) { + const [stat, setStat] = useState('globalRank'); + const [format, setFormat] = useState('markdown'); + const [copied, setCopied] = useState(false); + + const statQuery = stat === 'globalRank' ? '' : `?stat=${stat}`; + const badgeUrl = `${siteUrl}/api/badge/${encodeURIComponent(login)}.svg${statQuery}`; + const profileUrl = `${siteUrl}/share/${encodeURIComponent(login)}`; + + const snippets = { + markdown: `[![devglobe](${badgeUrl})](${profileUrl})`, + html: `devglobe badge`, + }; + + async function handleCopy() { + try { + await navigator.clipboard.writeText(snippets[format]); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } catch { + // Clipboard API can fail silently (permissions, insecure context); + // the snippet is still selectable text, so no further action needed. + } + } + + return ( +
+ Embeddable badge +

Get your badge

+

+ Embed a live-updating rank badge in your GitHub README or personal site. It refreshes automatically as your DevGlobe stats update. +

+ +
+ {STAT_OPTIONS.map(option => ( + + ))} +
+ +
+ {['markdown', 'html'].map(option => ( + + ))} +
+ +
+ {snippets[format]} + +
+
+ ); +} From 5f440b52ff3f99201d51e28d6e7023f9eeb6b28b Mon Sep 17 00:00:00 2001 From: Muhammad Date: Sat, 15 Aug 2026 20:40:53 +0500 Subject: [PATCH 2/6] Enhance DetailPanel with card generation feature --- components/DetailPanel.jsx | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/components/DetailPanel.jsx b/components/DetailPanel.jsx index b366363..7651ee9 100644 --- a/components/DetailPanel.jsx +++ b/components/DetailPanel.jsx @@ -5,12 +5,13 @@ import { track } from '@vercel/analytics'; import * as d3 from 'd3'; import { formatNum, formatRelativeTime, isStaleData } from '../lib/format.js'; import { DIMENSIONS, SCORE_METHODOLOGY } from '../lib/scoring.js'; -import { SOCIAL_PREVIEW_VERSION } from '../lib/site.js'; +import { SOCIAL_PREVIEW_VERSION, getSiteUrl } from '../lib/site.js'; import { classifyAgent } from '../lib/agent-class.js'; import { AI_TOOLS } from '../lib/ai-profile.js'; import SpecialTags from './SpecialTags.jsx'; +import BadgeSnippet from './BadgeSnippet.jsx'; -export default function DetailPanel({ dev, onClose, claimedLogins, openCardOnMount = false, claimSuccess = false }) { +export default function DetailPanel({ dev, onClose, onCardGenerated, claimedLogins, openCardOnMount = false, claimSuccess = false }) { const [fullData, setFullData] = useState(null); const [showCard, setShowCard] = useState(false); const radarRef = useRef(null); @@ -19,6 +20,7 @@ export default function DetailPanel({ dev, onClose, claimedLogins, openCardOnMou const handleGenerateCard = () => { track('card_generated', { login: dev.login }); + onCardGenerated?.(dev.login); setShowCard(true); }; @@ -132,7 +134,7 @@ export default function DetailPanel({ dev, onClose, claimedLogins, openCardOnMou {merged.soUserId && ( StackOverflow ↗ )} - @@ -550,13 +552,14 @@ function CardModal({ dev, claimSuccess, onClose }) { const rankText = dev.globalRank ? `Global #${dev.globalRank} of ${dev.globalTotal}` : 'Ranked on DevGlobe'; const agent = classifyAgent(dev); - const shareText = `I mapped my open-source identity on DevGlobe: ${rankText}. Generate yours and see where you rank.`; const shareHashtags = ['buildinpublic', 'DevGlobe', 'OpenSource', 'DeveloperCommunity', 'GitHub']; const hashtagText = shareHashtags.map(hashtag => `#${hashtag}`).join(' '); + const shareText = `I mapped my open-source identity on DevGlobe: ${rankText}. Generate yours and see where you rank.\n\n${hashtagText}`; const linkedinCaption = `I mapped my open-source contributions on DevGlobe and discovered my developer identity: ${agent.name}. ${rankText}. Build your card and see where your work places you in the global developer community.\n\n${hashtagText}`; const shareLinks = { - twitter: `https://twitter.com/intent/tweet?text=${encodeURIComponent(shareText)}&url=${encodeURIComponent(shareUrl)}&hashtags=${shareHashtags.join(',')}`, + twitter: `https://twitter.com/intent/tweet?text=${encodeURIComponent(shareText)}&url=${encodeURIComponent(shareUrl)}`, + facebook: `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(shareUrl)}`, linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(shareUrl)}`, reddit: `https://reddit.com/submit?url=${encodeURIComponent(shareUrl)}&title=${encodeURIComponent(`My DevGlobe Developer Card - ${name} ${hashtagText}`)}`, }; @@ -654,6 +657,11 @@ function CardModal({ dev, claimSuccess, onClose }) { + + +