{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "github-profile-card",
  "title": "GitHub Profile Card",
  "description": "A comprehensive profile card displaying GitHub user information including avatar, bio, location, company, and follower stats.",
  "dependencies": [],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/blocks/github/github-profile-card/components/elements/github-profile-card.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\ninterface ProfileData {\n  login: string;\n  name: string | null;\n  avatar_url: string;\n  bio: string | null;\n  public_repos: number;\n  followers: number;\n  following: number;\n  company: string | null;\n  location: string | null;\n  blog: string | null;\n  twitter_username: string | null;\n}\n\ninterface GitHubProfileCardProps extends React.HTMLAttributes<HTMLDivElement> {\n  username: string;\n  staticData?: ProfileData;\n  showStats?: boolean;\n}\n\nfunction formatNumber(num: number): string {\n  if (num >= 1000000) {\n    return `${(num / 1000000).toFixed(1)}M`;\n  }\n  if (num >= 1000) {\n    return `${(num / 1000).toFixed(1)}K`;\n  }\n  return num.toString();\n}\n\nfunction LocationIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      className={className}\n      viewBox=\"0 0 16 16\"\n      fill=\"currentColor\"\n      aria-hidden=\"true\"\n    >\n      <path d=\"m12.596 11.596-3.535 3.536a1.5 1.5 0 0 1-2.122 0l-3.535-3.536a6.5 6.5 0 1 1 9.192 0ZM8 14.5l3.535-3.535a5.5 5.5 0 1 0-7.07 0L8 14.5Zm0-6a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm0 1a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5Z\" />\n    </svg>\n  );\n}\n\nfunction LinkIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      className={className}\n      viewBox=\"0 0 16 16\"\n      fill=\"currentColor\"\n      aria-hidden=\"true\"\n    >\n      <path d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\" />\n    </svg>\n  );\n}\n\nfunction CompanyIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      className={className}\n      viewBox=\"0 0 16 16\"\n      fill=\"currentColor\"\n      aria-hidden=\"true\"\n    >\n      <path d=\"M1.75 16A1.75 1.75 0 0 1 0 14.25V1.75C0 .784.784 0 1.75 0h8.5C11.216 0 12 .784 12 1.75v12.5c0 .085-.006.168-.018.25h2.268a.25.25 0 0 0 .25-.25V8.285a.25.25 0 0 0-.111-.208l-1.055-.703a.749.749 0 1 1 .832-1.248l1.055.703c.487.325.779.871.779 1.456v5.965A1.75 1.75 0 0 1 14.25 16h-3.5a.766.766 0 0 1-.197-.026c-.099.017-.2.026-.303.026h-3a.75.75 0 0 1-.75-.75V14h-1v1.25a.75.75 0 0 1-.75.75Zm-.25-1.75c0 .138.112.25.25.25H4v-1.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 .75.75v1.25h2.25a.25.25 0 0 0 .25-.25V1.75a.25.25 0 0 0-.25-.25h-8.5a.25.25 0 0 0-.25.25ZM3.75 6h.5a.75.75 0 0 1 0 1.5h-.5a.75.75 0 0 1 0-1.5ZM3 3.75A.75.75 0 0 1 3.75 3h.5a.75.75 0 0 1 0 1.5h-.5A.75.75 0 0 1 3 3.75Zm4 3A.75.75 0 0 1 7.75 6h.5a.75.75 0 0 1 0 1.5h-.5A.75.75 0 0 1 7 6.75ZM7.75 3h.5a.75.75 0 0 1 0 1.5h-.5a.75.75 0 0 1 0-1.5ZM3 9.75A.75.75 0 0 1 3.75 9h.5a.75.75 0 0 1 0 1.5h-.5A.75.75 0 0 1 3 9.75ZM7.75 9h.5a.75.75 0 0 1 0 1.5h-.5a.75.75 0 0 1 0-1.5Z\" />\n    </svg>\n  );\n}\n\nfunction XIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      className={className}\n      viewBox=\"0 0 24 24\"\n      fill=\"currentColor\"\n      aria-hidden=\"true\"\n    >\n      <path d=\"M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z\" />\n    </svg>\n  );\n}\n\nexport function GitHubProfileCard({\n  username,\n  staticData,\n  showStats = true,\n  className,\n  ...props\n}: GitHubProfileCardProps) {\n  const [data, setData] = useState<ProfileData | null>(staticData || null);\n  const [loading, setLoading] = useState(!staticData);\n  const [error, setError] = useState<string | null>(null);\n\n  useEffect(() => {\n    if (staticData) return;\n\n    async function fetchData() {\n      try {\n        setLoading(true);\n        setError(null);\n\n        const response = await fetch(\n          `https://api.github.com/users/${username}`\n        );\n\n        if (!response.ok) {\n          throw new Error(\"User not found\");\n        }\n\n        const userData = await response.json();\n        setData(userData);\n      } catch (err) {\n        setError(err instanceof Error ? err.message : \"Unknown error\");\n      } finally {\n        setLoading(false);\n      }\n    }\n\n    fetchData();\n  }, [username, staticData]);\n\n  if (loading) {\n    return (\n      <div\n        data-slot=\"github-profile-card\"\n        className={cn(\n          \"flex flex-col gap-4 p-6 border border-border rounded-lg bg-card\",\n          className\n        )}\n        {...props}\n      >\n        <div className=\"flex items-start gap-4\">\n          <div className=\"w-16 h-16 rounded-full bg-muted animate-pulse\" />\n          <div className=\"flex flex-col gap-2 flex-1\">\n            <div className=\"h-5 w-32 bg-muted rounded animate-pulse\" />\n            <div className=\"h-4 w-24 bg-muted rounded animate-pulse\" />\n          </div>\n        </div>\n        <div className=\"h-4 w-full bg-muted rounded animate-pulse\" />\n        <div className=\"flex gap-4\">\n          {[1, 2, 3].map((i) => (\n            <div key={i} className=\"h-4 w-16 bg-muted rounded animate-pulse\" />\n          ))}\n        </div>\n      </div>\n    );\n  }\n\n  if (error || !data) {\n    return (\n      <div\n        data-slot=\"github-profile-card\"\n        className={cn(\n          \"flex flex-col gap-4 p-6 border border-destructive/50 rounded-lg bg-destructive/10\",\n          className\n        )}\n        {...props}\n      >\n        <p className=\"text-sm text-destructive\">{error || \"No data\"}</p>\n      </div>\n    );\n  }\n\n  const blogUrl = data.blog\n    ? data.blog.startsWith(\"http\")\n      ? data.blog\n      : `https://${data.blog}`\n    : null;\n\n  return (\n    <div\n      data-slot=\"github-profile-card\"\n      className={cn(\n        \"flex flex-col gap-4 p-6 border border-border rounded-lg bg-card\",\n        className\n      )}\n      {...props}\n    >\n      <div className=\"flex items-start gap-4\">\n        <a\n          href={`https://github.com/${data.login}`}\n          target=\"_blank\"\n          rel=\"noopener noreferrer\"\n        >\n          <img\n            src={data.avatar_url}\n            alt={`${data.login}'s avatar`}\n            className=\"w-16 h-16 rounded-full border border-border\"\n          />\n        </a>\n        <div className=\"flex flex-col gap-0.5 min-w-0\">\n          <a\n            href={`https://github.com/${data.login}`}\n            target=\"_blank\"\n            rel=\"noopener noreferrer\"\n            className=\"font-semibold text-foreground hover:underline truncate\"\n          >\n            {data.name || data.login}\n          </a>\n          <span className=\"text-sm text-muted-foreground\">@{data.login}</span>\n        </div>\n      </div>\n\n      {data.bio && (\n        <p className=\"text-sm text-muted-foreground\">{data.bio}</p>\n      )}\n\n      <div className=\"flex flex-wrap gap-x-4 gap-y-1 text-sm text-muted-foreground\">\n        {data.company && (\n          <span className=\"flex items-center gap-1.5\">\n            <CompanyIcon className=\"w-4 h-4\" />\n            {data.company}\n          </span>\n        )}\n        {data.location && (\n          <span className=\"flex items-center gap-1.5\">\n            <LocationIcon className=\"w-4 h-4\" />\n            {data.location}\n          </span>\n        )}\n        {blogUrl && (\n          <a\n            href={blogUrl}\n            target=\"_blank\"\n            rel=\"noopener noreferrer\"\n            className=\"flex items-center gap-1.5 hover:text-foreground transition-colors\"\n          >\n            <LinkIcon className=\"w-4 h-4\" />\n            {data.blog?.replace(/^https?:\\/\\//, \"\")}\n          </a>\n        )}\n        {data.twitter_username && (\n          <a\n            href={`https://x.com/${data.twitter_username}`}\n            target=\"_blank\"\n            rel=\"noopener noreferrer\"\n            className=\"flex items-center gap-1.5 hover:text-foreground transition-colors\"\n          >\n            <XIcon className=\"w-4 h-4\" />\n            @{data.twitter_username}\n          </a>\n        )}\n      </div>\n\n      {showStats && (\n        <div className=\"flex gap-4 pt-2 border-t border-border text-sm\">\n          <span className=\"flex items-center gap-1.5\">\n            <span className=\"font-semibold text-foreground\">\n              {formatNumber(data.public_repos)}\n            </span>\n            <span className=\"text-muted-foreground\">repos</span>\n          </span>\n          <span className=\"flex items-center gap-1.5\">\n            <span className=\"font-semibold text-foreground\">\n              {formatNumber(data.followers)}\n            </span>\n            <span className=\"text-muted-foreground\">followers</span>\n          </span>\n          <span className=\"flex items-center gap-1.5\">\n            <span className=\"font-semibold text-foreground\">\n              {formatNumber(data.following)}\n            </span>\n            <span className=\"text-muted-foreground\">following</span>\n          </span>\n        </div>\n      )}\n    </div>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "docs": "Fetches user profile from GitHub API. Shows avatar, name, bio, location, company, website, and Twitter. Toggle stats with showStats prop.",
  "categories": [
    "github"
  ],
  "type": "registry:block"
}