From e706be2d22c2a8c5364fe8279e9649718ec554cc Mon Sep 17 00:00:00 2001 From: Aryan-Agarwal-creator Date: Wed, 3 Jun 2026 02:38:44 +0530 Subject: [PATCH 1/2] enhancements done --- app/page.tsx | 560 +++++++++++++++++++++-------- components/AnimatedStatsBanner.tsx | 290 +++++++++++++++ components/HowItWorks.tsx | 239 ++++++++++++ components/UsernameInput.tsx | 294 +++++++++++++++ 4 files changed, 1236 insertions(+), 147 deletions(-) create mode 100644 components/AnimatedStatsBanner.tsx create mode 100644 components/HowItWorks.tsx create mode 100644 components/UsernameInput.tsx diff --git a/app/page.tsx b/app/page.tsx index c1ef63b3d..59a3ce505 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -6,22 +6,30 @@ import { useRef, useState, useEffect } from 'react'; import { AnimatePresence, motion } from 'framer-motion'; import { gsap } from 'gsap'; import { useGSAP } from '@gsap/react'; -import { X } from 'lucide-react'; import { CommitPulseLogo } from '@/components/commitpulse-logo'; import { CustomizeCTA } from './components/CustomizeCTA'; import { useRecentSearches } from '@/hooks/useRecentSearches'; import { useDebounce } from '@/hooks/useDebounce'; import { Footer } from '@/app/components/Footer'; +import { UsernameInput } from '@/components/UsernameInput'; +import { HowItWorks } from '@/components/HowItWorks'; +import { AnimatedStatsBanner } from '@/components/AnimatedStatsBanner'; import { FeatureCard, FeatureCardsSection } from '@/components/FeatureCards'; import { DiscordButton } from '@/components/DiscordButton'; import { WallOfLove } from '@/components/WallOfLove'; +/** Well-known GitHub accounts used as sample demo chips */ +const DEMO_USERNAMES = ['torvalds', 'gaearon', 'vercel', 'sindresorhus']; + +/** The fallback sample account shown before first search */ +const SAMPLE_USERNAME = 'torvalds'; + const Icons = { Github: () => ( - + ), @@ -36,6 +44,7 @@ const Icons = { strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" + aria-hidden="true" > @@ -52,6 +61,7 @@ const Icons = { strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" + aria-hidden="true" > @@ -68,10 +78,29 @@ const Icons = { strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" + aria-hidden="true" > ), + BadgeIcon: () => ( + + ), }; export default function LandingPage() { @@ -88,11 +117,29 @@ export default function LandingPage() { const { searches, addSearch, clearSearches, removeSearch } = useRecentSearches(); const [mounted, setMounted] = useState(false); + // Recent search avatar cache + const [recentAvatars, setRecentAvatars] = useState>({}); + useEffect(() => { // eslint-disable-next-line react-hooks/set-state-in-effect setMounted(true); }, []); + // Prefetch avatars for recent searches + useEffect(() => { + searches.forEach((s) => { + if (!recentAvatars[s]) { + const url = `https://avatars.githubusercontent.com/${s}?size=40`; + const img = new Image(); + img.src = url; + img.onload = () => { + setRecentAvatars((prev) => ({ ...prev, [s]: url })); + }; + } + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [searches]); + useGSAP( () => { if (!heroRef.current) return; @@ -121,14 +168,23 @@ export default function LandingPage() { const debouncedUsername = useDebounce(trimmedUsername, 500); const hasUsername = debouncedUsername.length > 0; - const badgeUrl = `/api/streak?user=${debouncedUsername}`; + // Whether to show sample (torvalds) or user's actual badge + const showSample = !hasUsername; + + const activeBadgeUser = showSample ? SAMPLE_USERNAME : debouncedUsername; + const badgeUrl = `/api/streak?user=${activeBadgeUser}`; const siteUrl = process.env.NEXT_PUBLIC_SITE_URL ?? 'https://commitpulse.vercel.app'; const markdown = `![CommitPulse](${siteUrl}/api/streak?user=${trimmedUsername})`; // Derived — automatically false when debouncedUsername changes - const badgeLoaded = - badgeResult?.username === debouncedUsername && badgeResult?.status === 'loaded'; - const badgeError = badgeResult?.username === debouncedUsername && badgeResult?.status === 'error'; + const badgeLoaded = badgeResult?.username === activeBadgeUser && badgeResult?.status === 'loaded'; + const badgeError = badgeResult?.username === activeBadgeUser && badgeResult?.status === 'error'; + + // When switching from sample to user, reset badge result + useEffect(() => { + // eslint-disable-next-line react-hooks/set-state-in-effect + setBadgeResult(null); + }, [activeBadgeUser]); const copyToClipboard = async () => { if (trimmedUsername.length === 0) return; @@ -151,12 +207,13 @@ export default function LandingPage() { return (
-
+