{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "avatar",
  "title": "Avatar",
  "description": "Pixel-stepped circle avatar with a 1px ring, a dithered drop shadow, and an image or pixel-font initials.",
  "dependencies": [
    "cn",
    "radix-ui"
  ],
  "registryDependencies": [
    "@ui95/theme"
  ],
  "files": [
    {
      "path": "components/ui/avatar.tsx",
      "content": "\"use client\"\n\nimport { cn } from \"cn\"\nimport { Avatar as AvatarPrimitive } from \"radix-ui\"\nimport * as React from \"react\"\n\n\ntype AvatarSize = \"sm\" | \"default\" | \"lg\"\n\ntype RowSpan = readonly [number, number]\n\nconst DISC_CELLS: Record<AvatarSize, number> = { sm: 12, default: 16, lg: 20 }\n\nfunction readDiscRows(cells: number): RowSpan[] {\n  const radius = cells / 2\n  return Array.from({ length: cells }, (_, row) => {\n    const fromCenter = row + 0.5 - radius\n    const half = Math.sqrt(Math.max(radius * radius - fromCenter * fromCenter, 0))\n    const left = Math.round(radius - half)\n    return [left, cells - left] as const\n  })\n}\n\nfunction erodeRows(rows: RowSpan[]): RowSpan[] {\n  return rows.map(([left, right], row) => {\n    const above = rows[row - 1]\n    const below = rows[row + 1]\n    if (!above || !below) {\n      return [0, 0] as const\n    }\n    return [\n      Math.max(left + 1, above[0], below[0]),\n      Math.min(right - 1, above[1], below[1]),\n    ] as const\n  })\n}\n\nfunction toPolygon(rows: RowSpan[], cells: number): string {\n  const percent = (value: number) => `${(value / cells) * 100}%`\n  const filled = rows.flatMap(([left, right], row) => (right > left ? [{ row, left, right }] : []))\n  const rightSide = filled.flatMap(({ row, right }) => [\n    `${percent(right)} ${percent(row)}`,\n    `${percent(right)} ${percent(row + 1)}`,\n  ])\n  const leftSide = [...filled]\n    .reverse()\n    .flatMap(({ row, left }) => [`${percent(left)} ${percent(row + 1)}`, `${percent(left)} ${percent(row)}`])\n  return `polygon(${[...rightSide, ...leftSide].join(\", \")})`\n}\n\nfunction readDiscStyle(size: AvatarSize): React.CSSProperties {\n  const cells = DISC_CELLS[size]\n  const rows = readDiscRows(cells)\n  return {\n    \"--avatar-disc\": toPolygon(rows, cells),\n    \"--avatar-face\": toPolygon(erodeRows(rows), cells),\n  } as React.CSSProperties\n}\n\nconst DISC_STYLES: Record<AvatarSize, React.CSSProperties> = {\n  sm: readDiscStyle(\"sm\"),\n  default: readDiscStyle(\"default\"),\n  lg: readDiscStyle(\"lg\"),\n}\n\nconst discClassName = cn(\n  \"relative isolate flex shrink-0 select-none\",\n  \"after:absolute after:inset-0 after:-z-10 after:bg-border after:content-[''] after:[clip-path:var(--avatar-disc)]\"\n)\n\nconst faceClassName = \"absolute inset-0 size-full [clip-path:var(--avatar-face)]\"\n\nfunction Avatar({\n  className,\n  size = \"default\",\n  style,\n  ...props\n}: React.ComponentProps<typeof AvatarPrimitive.Root> & {\n  size?: AvatarSize\n}) {\n  return (\n    <AvatarPrimitive.Root\n      data-slot=\"avatar\"\n      data-size={size}\n      style={{ ...DISC_STYLES[size], ...style }}\n      className={cn(\n        \"group/avatar size-8 data-[size=lg]:size-10 data-[size=sm]:size-6\",\n        discClassName,\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction AvatarImage({\n  className,\n  ...props\n}: React.ComponentProps<typeof AvatarPrimitive.Image>) {\n  return (\n    <AvatarPrimitive.Image\n      data-slot=\"avatar-image\"\n      className={cn(faceClassName, \"object-cover\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction AvatarFallback({\n  className,\n  ...props\n}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {\n  return (\n    <AvatarPrimitive.Fallback\n      data-slot=\"avatar-fallback\"\n      className={cn(\n        faceClassName,\n        \"flex items-center justify-center bg-muted font-pixel text-sm text-foreground uppercase group-data-[size=sm]/avatar:text-sm\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction AvatarBadge({ className, ...props }: React.ComponentProps<\"span\">) {\n  return (\n    <span\n      data-slot=\"avatar-badge\"\n      className={cn(\n        \"absolute right-0 bottom-0 z-10 inline-flex items-center justify-center pixel-border border-background bg-primary text-primary-foreground select-none\",\n        \"group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden\",\n        \"group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2\",\n        \"group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction AvatarGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"avatar-group\"\n      className={cn(\"group/avatar-group flex -space-x-2\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction AvatarGroupCount({\n  className,\n  size = \"default\",\n  style,\n  children,\n  ...props\n}: React.ComponentProps<\"div\"> & { size?: AvatarSize }) {\n  return (\n    <div\n      data-slot=\"avatar-group-count\"\n      data-size={size}\n      style={{ ...DISC_STYLES[size], ...style }}\n      className={cn(\n        \"size-8 data-[size=lg]:size-10 data-[size=sm]:size-6\",\n        discClassName,\n        className\n      )}\n      {...props}\n    >\n      <span\n        className={cn(\n          faceClassName,\n          \"flex items-center justify-center bg-muted font-pixel text-sm text-foreground [&>svg]:size-4\"\n        )}\n      >\n        {children}\n      </span>\n    </div>\n  )\n}\n\nexport {\n  Avatar,\n  AvatarBadge,\n  AvatarFallback,\n  AvatarGroup,\n  AvatarGroupCount,\n  AvatarImage,\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "ui"
  ],
  "type": "registry:ui"
}