{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "avatar",
  "type": "registry:ui",
  "title": "Avatar",
  "description": "A person as a small raised disc: initials, or a photo that fades in once loaded; presence as the LED; a group overlaps and spreads apart on the object spring when hovered.",
  "dependencies": [
    "@base-ui/react@^1.8.0"
  ],
  "registryDependencies": [
    "https://metalui.dev/r/tokens.json"
  ],
  "files": [
    {
      "path": "packages/metalui/src/components/avatar/avatar.tsx",
      "type": "registry:ui",
      "target": "components/metalui/avatar/avatar.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\nimport { Avatar as BaseAvatar } from '@base-ui/react/avatar';\nimport { Led, type LedKind } from '../led/led';\n\n/* ─────────────────────────────────────────────────────────\n * AVATAR and AVATAR GROUP, a person as a small raised disc\n *\n *   rest      the initials engraved in ink2 on the raised surface\n *   photo     fades in over the initials on the settle spring once it has loaded; a broken photo\n *             never shows (the initials stay)\n *   presence  the LED part at the lower right, on a ring of the page's ground\n *   group     discs overlap, each ringed in the ground; a +N disc counts the rest\n *   open      hovering the group spreads the discs one grid step apart on the object spring (a\n *             stack opening); letting go settles them back on the release spring\n * Reduce Motion: the photo appears at once; the stack does not spread.\n * An object: it stands for a person. It uses the raised surface and the LED part.\n * ───────────────────────────────────────────────────────── */\n\nexport type AvatarSize = 'small' | 'regular' | 'large';\n\nconst DISC = 'mu-avatar relative inline-grid flex-none place-items-center rounded-full recipe-surface-raise-sm select-none';\nconst SIZE: Record<AvatarSize, string> = {\n  small: 'size-avatar-size-small type-meta',\n  regular: 'size-avatar-size-regular type-ui',\n  large: 'size-avatar-size-large type-title',\n};\nconst INITIALS = 'mu-avatar-initials text-ink2 uppercase';\nconst PHOTO = 'mu-avatar-photo absolute inset-0 size-full rounded-full object-cover avatar-photo';\nconst PRESENCE = 'mu-avatar-presence absolute right-0 bottom-0 inline-grid place-items-center leading-none rounded-full avatar-ring';\n\n/** The first letters of the first and last words: \"Ana Rocha\" → \"AR\". */\nexport function initialsOf(name: string) {\n  const words = name.trim().split(/\\s+/).filter(Boolean);\n  return ((words[0]?.[0] ?? '') + (words.length > 1 ? words[words.length - 1][0] : '')).toUpperCase();\n}\n\nexport interface AvatarProps {\n  /** The person's name: it names the avatar and gives the initials. */\n  name: string;\n  /** Their photo. */\n  src?: string;\n  size?: AvatarSize;\n  /** Their presence, as the LED part: live (here), waiting (away), off. */\n  presence?: Extract<LedKind, 'live' | 'waiting' | 'off'>;\n  className?: string;\n}\n\n/** A person as a small raised disc: a photo, or their initials. */\nexport function Avatar({ name, src, size = 'regular', presence, className }: AvatarProps) {\n  const own = `${DISC} ${SIZE[size]}`;\n  const label = presence ? `${name}, ${presence === 'live' ? 'here' : presence === 'waiting' ? 'away' : 'offline'}` : name;\n  return (\n    <BaseAvatar.Root role=\"img\" aria-label={label} className={className ? `${own} ${className}` : own}>\n      <BaseAvatar.Fallback className={INITIALS}>{initialsOf(name)}</BaseAvatar.Fallback>\n      {src && <BaseAvatar.Image src={src} alt=\"\" className={PHOTO} />}\n      {presence && <span className={PRESENCE}><Led kind={presence} size={size === 'large' ? 'default' : 'small'} /></span>}\n    </BaseAvatar.Root>\n  );\n}\n\nexport interface AvatarGroupProps {\n  /** People, in order. */\n  people: { name: string; src?: string; presence?: AvatarProps['presence'] }[];\n  /** How many discs before a +N disc counts the rest (4). */\n  max?: number;\n  size?: AvatarSize;\n  /** Names the group for assistive tech: \"Shared with\". */\n  'aria-label': string;\n  className?: string;\n}\n\n/** People together: overlapping discs that spread when hovered. */\nexport function AvatarGroup({ people, max = 4, size = 'regular', className, ...aria }: AvatarGroupProps) {\n  const shown = people.length > max ? people.slice(0, max - 1) : people;\n  const rest = people.length - shown.length;\n  const own = 'mu-avatar-group inline-flex items-center avatar-stack';\n  return (\n    <div role=\"group\" aria-label={aria['aria-label']} className={className ? `${own} ${className}` : own}>\n      {shown.map((p, i) => (\n        <span key={p.name} className=\"inline-flex rounded-full avatar-ring\" style={{ '--mu-avatar-i': i } as React.CSSProperties}>\n          <Avatar {...p} size={size} />\n        </span>\n      ))}\n      {rest > 0 && (\n        <span className=\"inline-flex rounded-full avatar-ring\" style={{ '--mu-avatar-i': shown.length } as React.CSSProperties}>\n          <span role=\"img\" aria-label={`${rest} more`} className={`${DISC} ${SIZE[size]} text-ink2 tabular-nums`}>+{rest}</span>\n        </span>\n      )}\n    </div>\n  );\n}\n"
    }
  ],
  "docs": "Agent guide: https://metalui.dev/r/avatar.md. SwiftUI: MetalAvatar in the MetalUI Swift package."
}
