{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "github-activity-calendar",
  "title": "GitHub Activity Calendar",
  "description": "The iconic GitHub contribution calendar showing activity levels throughout the year with customizable color schemes.",
  "dependencies": [],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/blocks/github/github-activity-calendar/components/elements/github-activity-calendar.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useMemo, useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\ninterface ContributionDay {\n  date: string;\n  count: number;\n  level: 0 | 1 | 2 | 3 | 4;\n}\n\ninterface GitHubActivityCalendarProps\n  extends React.HTMLAttributes<HTMLDivElement> {\n  username: string;\n  staticData?: ContributionDay[];\n  year?: number;\n  colorScheme?: \"green\" | \"blue\" | \"purple\" | \"orange\";\n}\n\nconst LEVEL_COLORS = {\n  green: [\n    \"bg-neutral-100 dark:bg-neutral-800\",\n    \"bg-green-200 dark:bg-green-900\",\n    \"bg-green-400 dark:bg-green-700\",\n    \"bg-green-500 dark:bg-green-500\",\n    \"bg-green-600 dark:bg-green-400\",\n  ],\n  blue: [\n    \"bg-neutral-100 dark:bg-neutral-800\",\n    \"bg-blue-200 dark:bg-blue-900\",\n    \"bg-blue-400 dark:bg-blue-700\",\n    \"bg-blue-500 dark:bg-blue-500\",\n    \"bg-blue-600 dark:bg-blue-400\",\n  ],\n  purple: [\n    \"bg-neutral-100 dark:bg-neutral-800\",\n    \"bg-purple-200 dark:bg-purple-900\",\n    \"bg-purple-400 dark:bg-purple-700\",\n    \"bg-purple-500 dark:bg-purple-500\",\n    \"bg-purple-600 dark:bg-purple-400\",\n  ],\n  orange: [\n    \"bg-neutral-100 dark:bg-neutral-800\",\n    \"bg-orange-200 dark:bg-orange-900\",\n    \"bg-orange-400 dark:bg-orange-700\",\n    \"bg-orange-500 dark:bg-orange-500\",\n    \"bg-orange-600 dark:bg-orange-400\",\n  ],\n};\n\nconst DAYS = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\nconst MONTHS = [\n  \"Jan\",\n  \"Feb\",\n  \"Mar\",\n  \"Apr\",\n  \"May\",\n  \"Jun\",\n  \"Jul\",\n  \"Aug\",\n  \"Sep\",\n  \"Oct\",\n  \"Nov\",\n  \"Dec\",\n];\n\nfunction generateCalendarData(year: number): ContributionDay[] {\n  const days: ContributionDay[] = [];\n  const startDate = new Date(year, 0, 1);\n  const endDate = new Date(year, 11, 31);\n\n  const current = new Date(startDate);\n  while (current <= endDate) {\n    const random = Math.random();\n    let level: 0 | 1 | 2 | 3 | 4 = 0;\n    if (random > 0.7) level = 1;\n    if (random > 0.8) level = 2;\n    if (random > 0.9) level = 3;\n    if (random > 0.95) level = 4;\n\n    days.push({\n      date: current.toISOString().split(\"T\")[0],\n      count: level * Math.floor(Math.random() * 5 + 1),\n      level,\n    });\n\n    current.setDate(current.getDate() + 1);\n  }\n\n  return days;\n}\n\nfunction getWeeksWithMonths(\n  data: ContributionDay[]\n): { weeks: ContributionDay[][]; monthLabels: { month: string; col: number }[] } {\n  const weeks: ContributionDay[][] = [];\n  const monthLabels: { month: string; col: number }[] = [];\n  let currentWeek: ContributionDay[] = [];\n  let lastMonth = -1;\n\n  const firstDate = data[0] ? new Date(data[0].date) : new Date();\n  const startDay = firstDate.getDay();\n\n  for (let i = 0; i < startDay; i++) {\n    currentWeek.push({ date: \"\", count: 0, level: 0 });\n  }\n\n  for (const day of data) {\n    const date = new Date(day.date);\n    const dayOfWeek = date.getDay();\n    const month = date.getMonth();\n\n    if (dayOfWeek === 0 && currentWeek.length > 0) {\n      weeks.push(currentWeek);\n      currentWeek = [];\n    }\n\n    if (month !== lastMonth && dayOfWeek === 0) {\n      monthLabels.push({ month: MONTHS[month], col: weeks.length });\n      lastMonth = month;\n    }\n\n    currentWeek.push(day);\n  }\n\n  if (currentWeek.length > 0) {\n    while (currentWeek.length < 7) {\n      currentWeek.push({ date: \"\", count: 0, level: 0 });\n    }\n    weeks.push(currentWeek);\n  }\n\n  return { weeks, monthLabels };\n}\n\nexport function GitHubActivityCalendar({\n  username,\n  staticData,\n  year = new Date().getFullYear(),\n  colorScheme = \"green\",\n  className,\n  ...props\n}: GitHubActivityCalendarProps) {\n  const [data, setData] = useState<ContributionDay[]>(staticData || []);\n  const [loading, setLoading] = useState(!staticData);\n  const [totalContributions, setTotalContributions] = useState(0);\n\n  useEffect(() => {\n    if (staticData) {\n      setTotalContributions(staticData.reduce((sum, d) => sum + d.count, 0));\n      return;\n    }\n\n    setLoading(true);\n    const generated = generateCalendarData(year);\n    setData(generated);\n    setTotalContributions(generated.reduce((sum, d) => sum + d.count, 0));\n    setLoading(false);\n  }, [username, year, staticData]);\n\n  const { weeks, monthLabels } = useMemo(\n    () => getWeeksWithMonths(data),\n    [data]\n  );\n\n  const colors = LEVEL_COLORS[colorScheme];\n\n  if (loading) {\n    return (\n      <div\n        data-slot=\"github-activity-calendar\"\n        className={cn(\"flex flex-col gap-2\", className)}\n        {...props}\n      >\n        <div className=\"h-[108px] w-full bg-muted rounded animate-pulse\" />\n      </div>\n    );\n  }\n\n  return (\n    <div\n      data-slot=\"github-activity-calendar\"\n      className={cn(\"flex flex-col gap-2\", className)}\n      {...props}\n    >\n      <div className=\"text-sm text-muted-foreground\">\n        <span className=\"font-medium text-foreground\">\n          {totalContributions.toLocaleString()}\n        </span>{\" \"}\n        contributions in {year}\n      </div>\n\n      <div className=\"overflow-x-auto\">\n        <div className=\"inline-flex flex-col gap-1\">\n          <div className=\"flex gap-1 text-[10px] text-muted-foreground ml-8\">\n            {monthLabels.map(({ month, col }) => (\n              <span\n                key={`${month}-${col}`}\n                className=\"absolute\"\n                style={{ marginLeft: col * 13 }}\n              >\n                {month}\n              </span>\n            ))}\n          </div>\n\n          <div className=\"flex gap-1 mt-4\">\n            <div className=\"flex flex-col gap-1 text-[10px] text-muted-foreground pr-1\">\n              {DAYS.filter((_, i) => i % 2 === 1).map((day) => (\n                <span key={day} className=\"h-[10px] leading-[10px]\">\n                  {day}\n                </span>\n              ))}\n            </div>\n\n            <div className=\"flex gap-[3px]\">\n              {weeks.map((week, weekIndex) => (\n                <div key={weekIndex} className=\"flex flex-col gap-[3px]\">\n                  {week.map((day, dayIndex) => (\n                    <div\n                      key={`${weekIndex}-${dayIndex}`}\n                      className={cn(\n                        \"w-[10px] h-[10px] rounded-sm\",\n                        day.date ? colors[day.level] : \"bg-transparent\"\n                      )}\n                      title={\n                        day.date\n                          ? `${day.count} contributions on ${day.date}`\n                          : undefined\n                      }\n                    />\n                  ))}\n                </div>\n              ))}\n            </div>\n          </div>\n\n          <div className=\"flex items-center gap-1 mt-2 text-[10px] text-muted-foreground justify-end\">\n            <span>Less</span>\n            {colors.map((color, i) => (\n              <div\n                key={i}\n                className={cn(\"w-[10px] h-[10px] rounded-sm\", color)}\n              />\n            ))}\n            <span>More</span>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "docs": "Displays a year's worth of contribution activity. Supports green, blue, purple, and orange color schemes. Pass year prop to show different years.",
  "categories": [
    "github"
  ],
  "type": "registry:block"
}