{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "components-primitives-avatar",
  "title": "Avatar",
  "description": "An animated AI orb avatar with blinking eyes, 12 color presets, 3 sizes, and 3 shapes (circle, square, squircle).",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "path": "registry/components/primitives/avatar/index.tsx",
      "content": "'use client';\n\nimport { useId } from 'react';\nimport { motion } from 'motion/react';\n\nexport type AvatarColor =\n  | 'blue'\n  | 'orange'\n  | 'red'\n  | 'green'\n  | 'purple'\n  | 'yellow'\n  | 'cyan'\n  | 'pink'\n  | 'indigo'\n  | 'lime'\n  | 'turquoise'\n  | 'violet';\nexport type AvatarSize = 'sm' | 'md' | 'lg';\nexport type AvatarShape = 'circle' | 'square' | 'squircle';\n\nexport interface AvatarProps {\n  blinking?: boolean;\n  color?: AvatarColor;\n  size?: AvatarSize;\n  shape?: AvatarShape;\n  className?: string;\n}\n\nconst BLINK_KEYFRAMES = `\n@keyframes av-blink {\n  0%, 88%, 100% { transform: scaleY(1); }\n  93%            { transform: scaleY(0.07); }\n  97%            { transform: scaleY(0.07); }\n}\n`;\nconst PRESETS: Record<\n  AvatarColor,\n  {\n    gradient: string;\n    boxShadow: string;\n    iris: string;\n    shine: string;\n  }\n> = {\n  blue: {\n    gradient:\n      'radial-gradient(circle at 50% 45%, #0d4d9a 0%, #3d7dd8 40%, #6fb3ff 68%, #e0eeff 100%)',\n    boxShadow:\n      '0 0 4px 0px rgba(20,102,216,.35), 0 0 16px 6px rgba(20,102,216,.18), inset 0 0 0 1px rgba(255,255,255,.05)',\n    iris: 'linear-gradient(135deg, #ffffff 0%, #d4ecff 100%)',\n    shine:\n      'radial-gradient(ellipse at 30% 24%, rgba(255,255,255,.75) 0%, rgba(255,255,255,.1) 50%, transparent 70%)',\n  },\n  orange: {\n    gradient:\n      'radial-gradient(circle at 50% 45%, #a63e10 0%, #e27a2a 40%, #ffb46a 68%, #ffe8cc 100%)',\n    boxShadow:\n      '0 0 4px 0px rgba(232,100,0,.35), 0 0 16px 6px rgba(232,100,0,.18), inset 0 0 0 1px rgba(255,255,255,.05)',\n    iris: 'linear-gradient(135deg, #ffffff 0%, #ffd9b8 100%)',\n    shine:\n      'radial-gradient(ellipse at 30% 24%, rgba(255,255,255,.75) 0%, rgba(255,255,255,.1) 50%, transparent 70%)',\n  },\n  red: {\n    gradient:\n      'radial-gradient(circle at 50% 45%, #a60033 0%, #e74668 40%, #ff8aaa 68%, #ffd6e8 100%)',\n    boxShadow:\n      '0 0 4px 0px rgba(223,24,92,.35), 0 0 16px 6px rgba(223,24,92,.18), inset 0 0 0 1px rgba(255,255,255,.05)',\n    iris: 'linear-gradient(135deg, #ffffff 0%, #ffcde4 100%)',\n    shine:\n      'radial-gradient(ellipse at 30% 24%, rgba(255,255,255,.75) 0%, rgba(255,255,255,.1) 50%, transparent 70%)',\n  },\n  green: {\n    gradient:\n      'radial-gradient(circle at 50% 45%, #0d6632 0%, #2a9d5f 40%, #6dd187 68%, #d1fadd 100%)',\n    boxShadow:\n      '0 0 4px 0px rgba(12,168,82,.35), 0 0 16px 6px rgba(12,168,82,.18), inset 0 0 0 1px rgba(255,255,255,.05)',\n    iris: 'linear-gradient(135deg, #ffffff 0%, #c5f5d8 100%)',\n    shine:\n      'radial-gradient(ellipse at 30% 24%, rgba(255,255,255,.75) 0%, rgba(255,255,255,.1) 50%, transparent 70%)',\n  },\n  purple: {\n    gradient:\n      'radial-gradient(circle at 50% 45%, #4a0080 0%, #8b3fd1 40%, #c896ff 68%, #e8d4ff 100%)',\n    boxShadow:\n      '0 0 4px 0px rgba(110,46,224,.35), 0 0 16px 6px rgba(110,46,224,.18), inset 0 0 0 1px rgba(255,255,255,.05)',\n    iris: 'linear-gradient(135deg, #ffffff 0%, #e0c9ff 100%)',\n    shine:\n      'radial-gradient(ellipse at 30% 24%, rgba(255,255,255,.75) 0%, rgba(255,255,255,.1) 50%, transparent 70%)',\n  },\n  yellow: {\n    gradient:\n      'radial-gradient(circle at 50% 45%, #8a5500 0%, #d4a000 40%, #ffc93a 68%, #fff5cc 100%)',\n    boxShadow:\n      '0 0 4px 0px rgba(214,142,0,.35), 0 0 16px 6px rgba(214,142,0,.18), inset 0 0 0 1px rgba(255,255,255,.05)',\n    iris: 'linear-gradient(135deg, #ffffff 0%, #fff0a8 100%)',\n    shine:\n      'radial-gradient(ellipse at 30% 24%, rgba(255,255,255,.75) 0%, rgba(255,255,255,.1) 50%, transparent 70%)',\n  },\n  cyan: {\n    gradient:\n      'radial-gradient(circle at 50% 45%, #003d66 0%, #0a8fb5 40%, #5dd4ff 68%, #cdf5ff 100%)',\n    boxShadow:\n      '0 0 4px 0px rgba(10,143,181,.35), 0 0 16px 6px rgba(10,143,181,.18), inset 0 0 0 1px rgba(255,255,255,.05)',\n    iris: 'linear-gradient(135deg, #ffffff 0%, #d0f0ff 100%)',\n    shine:\n      'radial-gradient(ellipse at 30% 24%, rgba(255,255,255,.75) 0%, rgba(255,255,255,.1) 50%, transparent 70%)',\n  },\n  pink: {\n    gradient:\n      'radial-gradient(circle at 50% 45%, #7a0055 0%, #d63384 40%, #ff6bb3 68%, #ffe5f5 100%)',\n    boxShadow:\n      '0 0 4px 0px rgba(214,51,132,.35), 0 0 16px 6px rgba(214,51,132,.18), inset 0 0 0 1px rgba(255,255,255,.05)',\n    iris: 'linear-gradient(135deg, #ffffff 0%, #ffd6ed 100%)',\n    shine:\n      'radial-gradient(ellipse at 30% 24%, rgba(255,255,255,.75) 0%, rgba(255,255,255,.1) 50%, transparent 70%)',\n  },\n  indigo: {\n    gradient:\n      'radial-gradient(circle at 50% 45%, #2d157a 0%, #4f46e5 40%, #8b7eff 68%, #ddd6ff 100%)',\n    boxShadow:\n      '0 0 4px 0px rgba(79,70,229,.35), 0 0 16px 6px rgba(79,70,229,.18), inset 0 0 0 1px rgba(255,255,255,.05)',\n    iris: 'linear-gradient(135deg, #ffffff 0%, #e0d9ff 100%)',\n    shine:\n      'radial-gradient(ellipse at 30% 24%, rgba(255,255,255,.75) 0%, rgba(255,255,255,.1) 50%, transparent 70%)',\n  },\n  lime: {\n    gradient:\n      'radial-gradient(circle at 50% 45%, #4a5910 0%, #84cc16 40%, #bef264 68%, #ecfccf 100%)',\n    boxShadow:\n      '0 0 4px 0px rgba(132,204,22,.35), 0 0 16px 6px rgba(132,204,22,.18), inset 0 0 0 1px rgba(255,255,255,.05)',\n    iris: 'linear-gradient(135deg, #ffffff 0%, #f7fee8 100%)',\n    shine:\n      'radial-gradient(ellipse at 30% 24%, rgba(255,255,255,.75) 0%, rgba(255,255,255,.1) 50%, transparent 70%)',\n  },\n  turquoise: {\n    gradient:\n      'radial-gradient(circle at 50% 45%, #1a5555 0%, #0d9488 40%, #2dd4bf 68%, #ccfbf1 100%)',\n    boxShadow:\n      '0 0 4px 0px rgba(13,148,136,.35), 0 0 16px 6px rgba(13,148,136,.18), inset 0 0 0 1px rgba(255,255,255,.05)',\n    iris: 'linear-gradient(135deg, #ffffff 0%, #c0fdf5 100%)',\n    shine:\n      'radial-gradient(ellipse at 30% 24%, rgba(255,255,255,.75) 0%, rgba(255,255,255,.1) 50%, transparent 70%)',\n  },\n  violet: {\n    gradient:\n      'radial-gradient(circle at 50% 45%, #4a2a7a 0%, #a855f7 40%, #d8b4fe 68%, #f3e8ff 100%)',\n    boxShadow:\n      '0 0 4px 0px rgba(168,85,247,.35), 0 0 16px 6px rgba(168,85,247,.18), inset 0 0 0 1px rgba(255,255,255,.05)',\n    iris: 'linear-gradient(135deg, #ffffff 0%, #ede9fe 100%)',\n    shine:\n      'radial-gradient(ellipse at 30% 24%, rgba(255,255,255,.75) 0%, rgba(255,255,255,.1) 50%, transparent 70%)',\n  },\n};\n\nconst SIZE: Record<\n  AvatarSize,\n  {\n    orb: string;\n    eye: string;\n    eyeGap: string;\n    eyeY: string;\n  }\n> = {\n  sm: {\n    orb: 'size-8',\n    eye: 'w-1 h-1.5',\n    eyeGap: 'gap-1.5',\n    eyeY: '-translate-y-0.5',\n  },\n  md: {\n    orb: 'size-12',\n    eye: 'w-1.5 h-2.5',\n    eyeGap: 'gap-2.5',\n    eyeY: '-translate-y-0.5',\n  },\n  lg: {\n    orb: 'size-16',\n    eye: 'w-2 h-3',\n    eyeGap: 'gap-3.5',\n    eyeY: '-translate-y-1',\n  },\n};\n\nconst SHAPE_RADIUS: Record<AvatarShape, string> = {\n  circle: 'rounded-full',\n  square: 'rounded-[0%]',\n  squircle: 'rounded-[40%]',\n};\n\ninterface EyeProps {\n  blinking: boolean;\n  delayMs?: number;\n  irisGradient: string;\n  sizeClass: string;\n}\n\nfunction Eye({ blinking, delayMs = 0, irisGradient, sizeClass }: EyeProps) {\n  return (\n    <div\n      className={['rounded-full', sizeClass].filter(Boolean).join(' ')}\n      style={{\n        background: irisGradient,\n        ...(blinking\n          ? { animation: `av-blink 3.6s ease-in-out ${delayMs}ms infinite` }\n          : {}),\n      }}\n    />\n  );\n}\n\nfunction Avatar({\n  blinking = true,\n  color = 'blue',\n  size = 'md',\n  shape = 'circle',\n  className = '',\n}: AvatarProps) {\n  const uid = useId();\n  const noiseId = `av-n${uid.replace(/\\W/g, '')}`;\n  const preset = PRESETS[color] ?? PRESETS.blue;\n  const dims = SIZE[size] ?? SIZE.md;\n\n  return (\n    <>\n      <style>{BLINK_KEYFRAMES}</style>\n      <motion.div\n        aria-label=\"AI Avatar\"\n        role=\"img\"\n        whileTap={{ scaleX: 1.15, scaleY: 1.3 }}\n        transition={{\n          type: 'tween',\n          duration: 0.8,\n          ease: [0.34, 1.56, 0.64, 1],\n        }}\n        className={[\n          'relative flex cursor-pointer items-center justify-center overflow-hidden',\n          dims.orb,\n          SHAPE_RADIUS[shape],\n          className,\n        ]\n          .filter(Boolean)\n          .join(' ')}\n        style={{\n          background: preset.gradient,\n          boxShadow: preset.boxShadow,\n        }}\n      >\n        <svg\n          aria-hidden=\"true\"\n          className=\"pointer-events-none absolute inset-0 opacity-[0.2] mix-blend-overlay\"\n          width=\"100%\"\n          height=\"100%\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n        >\n          <defs>\n            <filter id={noiseId} x=\"0%\" y=\"0%\" width=\"100%\" height=\"100%\">\n              <feTurbulence\n                type=\"fractalNoise\"\n                baseFrequency=\"0.72\"\n                numOctaves=\"4\"\n                stitchTiles=\"stitch\"\n              />\n              <feColorMatrix type=\"saturate\" values=\"0\" />\n            </filter>\n          </defs>\n          <rect width=\"100%\" height=\"100%\" filter={`url(#${noiseId})`} />\n        </svg>\n\n        <svg\n          aria-hidden=\"true\"\n          className=\"pointer-events-none absolute inset-0 opacity-[0.2] mix-blend-overlay\"\n          width=\"100%\"\n          height=\"100%\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n        >\n          <defs>\n            <filter\n              id={`grain-${uid.replace(/\\W/g, '')}`}\n              x=\"0%\"\n              y=\"0%\"\n              width=\"100%\"\n              height=\"100%\"\n            >\n              <feTurbulence\n                type=\"fractalNoise\"\n                baseFrequency=\"3.2\"\n                numOctaves=\"1\"\n                stitchTiles=\"stitch\"\n              />\n              <feColorMatrix type=\"saturate\" values=\"0\" />\n            </filter>\n          </defs>\n          <rect\n            width=\"100%\"\n            height=\"100%\"\n            filter={`url(#grain-${uid.replace(/\\W/g, '')})`}\n          />\n        </svg>\n\n        <div\n          aria-hidden=\"true\"\n          className=\"pointer-events-none absolute inset-0\"\n          style={{ background: preset.shine }}\n        />\n\n        <div\n          aria-hidden=\"true\"\n          className=\"pointer-events-none absolute inset-0 blur-xs\"\n          style={{\n            background:\n              'radial-gradient(circle at 62% 68%, rgba(0,0,0,0.18) 0%, transparent 55%)',\n          }}\n        />\n\n        <div\n          className={[\n            'relative z-10 flex items-center',\n            dims.eyeGap,\n            dims.eyeY,\n          ].join(' ')}\n        >\n          <Eye\n            blinking={blinking}\n            delayMs={0}\n            irisGradient={preset.iris}\n            sizeClass={dims.eye}\n          />\n          <Eye\n            blinking={blinking}\n            delayMs={60}\n            irisGradient={preset.iris}\n            sizeClass={dims.eye}\n          />\n        </div>\n      </motion.div>\n    </>\n  );\n}\n\nexport default Avatar;\n",
      "type": "registry:ui",
      "target": "components/odysseyui/avatar.tsx"
    }
  ],
  "type": "registry:ui"
}