File size: 7,690 Bytes
55d7c10 c798beb 22aa376 55d7c10 22aa376 eda28cd 55d7c10 a6d1411 b17ac62 eda28cd 9aaca36 eda28cd 9aaca36 eda28cd 9aaca36 eda28cd 5e26aa6 17388ad eda28cd 17388ad eda28cd 17388ad eda28cd 17388ad eda28cd 17388ad 9aaca36 17388ad eda28cd 17388ad 9aaca36 17388ad 633e5e5 17388ad a6d1411 55d7c10 a6d1411 55d7c10 a6d1411 9aaca36 a6d1411 9aaca36 a6d1411 55d7c10 22aa376 17388ad 633e5e5 17388ad 633e5e5 9aaca36 a6d1411 9aaca36 55d7c10 633e5e5 a61e08f a6d1411 9aaca36 a6d1411 9aaca36 a6d1411 a61e08f a6d1411 a61e08f a6d1411 55d7c10 a6d1411 9aaca36 a6d1411 9aaca36 a6d1411 9aaca36 a6d1411 428d2d6 5e26aa6 22aa376 17388ad a6d1411 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
import React, { useState, useEffect, useMemo } from "react";
import { generateCalendarData } from "../utils/calendar";
import {
OpenSourceHeatmapProps,
ProviderInfo,
ModelData,
CalendarData,
} from "../types/heatmap";
import Heatmap from "../components/Heatmap";
import { fetchAllProvidersData, fetchAllAuthorsData } from "../utils/authors";
import UserSearchDialog from "../components/UserSearchDialog";
import ProviderSummary from "../components/ProviderSummary";
import OrganizationCard from "../components/OrganizationCard";
import { getRankingBadge } from "../utils/ranking";
const PROVIDERS: ProviderInfo[] = [
{ color: "#ff7000", authors: ["mistralai"] },
{ color: "#1877F2", authors: ["meta-llama", "facebook", ] },
{ color: "#10A37F", authors: ["openai"] },
{ color: "#cc785c", authors: ["Anthropic"] },
{ color: "#DB4437", authors: ["google"] },
{ color: "#5E35B1", authors: ["allenai"] },
{ color: "#0088cc", authors: ["apple"] },
{ color: "#FEB800", authors: ["microsoft"] },
{ color: "#76B900", authors: ["nvidia"] },
{ color: "#0088cc", authors: ["deepseek-ai"] },
{ color: "#0088cc", authors: ["Qwen"] },
{ color: "#4C6EE6", authors: ["CohereLabs"] },
{ color: "#4C6EE6", authors: ["ibm-granite"] },
{ color: "#A020F0", authors: ["stabilityai"] },
];
export async function getStaticProps() {
try {
const allAuthors = PROVIDERS.flatMap(({ authors }) => authors);
const uniqueAuthors = Array.from(new Set(allAuthors));
const flatData: ModelData[] = await fetchAllAuthorsData(uniqueAuthors);
const updatedProviders = await fetchAllProvidersData(PROVIDERS);
const calendarData = generateCalendarData(flatData, updatedProviders);
return {
props: {
calendarData,
providers: updatedProviders,
},
revalidate: 3600, // regenerate every hour
};
} catch (error) {
console.error("Error fetching data:", error);
return {
props: {
calendarData: {},
providers: PROVIDERS,
},
revalidate: 60, // retry after 1 minute if there was an error
};
}
}
const ProviderHeatmap = React.memo(({ provider, calendarData, rank }: { provider: ProviderInfo, calendarData: CalendarData, rank: number }) => {
const providerName = provider.fullName || provider.authors[0];
const calendarKey = provider.authors[0]; // Use the same key as calendar generation
const totalCount = calendarData[calendarKey]?.reduce((sum, day) => sum + day.count, 0) || 0;
const rankingBadge = getRankingBadge(rank);
return (
<div id={`provider-${calendarKey}`} className={`relative bg-gradient-to-br from-card to-card/95 rounded-2xl border border-border shadow-lg hover:shadow-xl transition-all duration-300 p-6 group ${rank <= 3 ? 'ring-2 ring-yellow-200/50 shadow-yellow-100/20' : ''}`}>
{/* Ranking Badge */}
<div className={rankingBadge.className}>
<span className="flex items-center justify-center">
{rankingBadge.icon || rank}
</span>
</div>
{/* Organization Header */}
<OrganizationCard
provider={provider}
calendarKey={calendarKey}
providerName={providerName}
totalCount={totalCount}
/>
{/* Heatmap Section */}
<div className="flex flex-col items-center bg-muted/30 rounded-xl p-3 group-hover:bg-muted/40 transition-colors duration-300">
<Heatmap
data={calendarData[calendarKey]}
color={provider.color}
providerName={providerName}
fullName={provider.fullName ?? providerName}
avatarUrl={provider.avatarUrl ?? ''}
authorId={calendarKey}
showHeader={false}
/>
</div>
</div>
);
});
const OpenSourceHeatmap: React.FC<OpenSourceHeatmapProps> = ({
calendarData,
providers,
}) => {
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
if (calendarData && Object.keys(calendarData).length > 0) {
setIsLoading(false);
}
}, [calendarData]);
const sortedProviders = useMemo(() =>
providers.sort((a, b) => {
const aData = calendarData[a.authors[0]] || [];
const bData = calendarData[b.authors[0]] || [];
const aCount = aData.reduce((sum, day) => sum + day.count, 0);
const bCount = bData.reduce((sum, day) => sum + day.count, 0);
return bCount - aCount;
}),
[providers, calendarData]
);
return (
<div className="w-full p-4 py-16 relative">
<h1 className="text-3xl lg:text-5xl mt-16 font-bold text-center mb-2 text-foreground">
Hugging Face Heatmap 🤗
</h1>
<div className="text-center text-sm my-8 space-y-4">
<p className="text-muted-foreground">
Open models, datasets, and apps from the top AI labs.
</p>
</div>
{/* Provider Summary List */}
<div className="mb-16 mx-auto">
<div className="overflow-x-auto scrollbar-hide">
<div className="flex gap-6 px-4 py-2 min-w-max justify-center">
{sortedProviders.map((provider, index) => (
<ProviderSummary
key={provider.fullName || provider.authors[0]}
provider={provider}
calendarData={calendarData}
rank={index + 1}
/>
))}
</div>
</div>
</div>
{isLoading ? (
<div className="flex items-center justify-center py-20">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500"></div>
<p className="ml-4 text-muted-foreground">Loading heatmaps...</p>
</div>
) : (
<>
{/* Top 3 Podium */}
<div className="mb-16">
<div className="flex flex-col gap-8 max-w-4xl mx-auto">
{sortedProviders.slice(0, 3).map((provider, index) => (
<ProviderHeatmap
key={provider.fullName || provider.authors[0]}
provider={provider}
calendarData={calendarData}
rank={index + 1}
/>
))}
</div>
</div>
{/* Rest of the providers */}
{sortedProviders.length > 3 && (
<div>
<h2 className="text-xl font-semibold text-center mb-8 text-muted-foreground">All Organizations</h2>
<div className="flex flex-col gap-8 max-w-4xl mx-auto">
{sortedProviders.slice(3).map((provider, index) => (
<ProviderHeatmap
key={provider.fullName || provider.authors[0]}
provider={provider}
calendarData={calendarData}
rank={index + 4}
/>
))}
</div>
</div>
)}
</>
)}
{/* CTA Section */}
<div className="mt-24 mb-16 flex justify-center">
<div className="bg-gradient-to-br from-card to-card/95 rounded-2xl border border-border shadow-lg hover:shadow-xl transition-all duration-300 p-8 max-w-2xl w-full text-center space-y-6">
<div className="space-y-4">
<h2 className="text-2xl lg:text-3xl font-bold text-foreground">
Want Your Own Heatmap?
</h2>
<p className="text-muted-foreground text-lg">
Search for any Hugging Face organization or user to see their model release activity.
</p>
</div>
<div className="flex justify-center">
<UserSearchDialog />
</div>
</div>
</div>
</div>
);
};
export default OpenSourceHeatmap;
|