{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "chat-gpt",
  "title": "ChatGPT",
  "description": "Animated ChatGPT composer — types a prompt into the pill input, the blue voice button morphs into a send button, and the suggestion chips fade out. Light/dark themes.",
  "dependencies": ["remotion", "@remotion/google-fonts"],
  "registryDependencies": ["@remocn/caret", "@remocn/remocn-ui"],
  "files": [
    {
      "path": "registry/remocn/chat-gpt/index.tsx",
      "content": "\"use client\";\n\nimport { loadFont } from \"@remotion/google-fonts/Inter\";\nimport {\n  AbsoluteFill,\n  interpolate,\n  spring,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\nimport { Caret } from \"@/components/remocn/caret\";\nimport { useTypewriter } from \"@/lib/remocn-ui\";\n\nconst { fontFamily: SANS_FAMILY } = loadFont();\n\nexport interface ChatGptProps {\n  greeting?: string;\n  placeholder?: string;\n  prompt?: string;\n  accentColor?: string;\n  speed?: number;\n}\n\ninterface Theme {\n  page: string;\n  inputBg: string;\n  inputBorder: string;\n  fg: string;\n  fgMuted: string;\n  chipBorder: string;\n  chipFg: string;\n  sendBg: string;\n  sendArrow: string;\n  iconColor: string;\n}\n\nexport const THEMES: Record<\"light\" | \"dark\", Theme> = {\n  light: {\n    page: \"#FFFFFF\",\n    inputBg: \"#FFFFFF\",\n    inputBorder: \"#E3E3E3\",\n    fg: \"#0D0D0D\",\n    fgMuted: \"#9B9B9B\",\n    chipBorder: \"#E3E3E3\",\n    chipFg: \"#5D5D5D\",\n    sendBg: \"#0D0D0D\",\n    sendArrow: \"#FFFFFF\",\n    iconColor: \"#5D5D5D\",\n  },\n  dark: {\n    page: \"#212121\",\n    inputBg: \"#303030\",\n    inputBorder: \"#454545\",\n    fg: \"#ECECEC\",\n    fgMuted: \"#9B9B9B\",\n    chipBorder: \"#454545\",\n    chipFg: \"#C5C5C5\",\n    sendBg: \"#FFFFFF\",\n    sendArrow: \"#0D0D0D\",\n    iconColor: \"#C5C5C5\",\n  },\n};\n\nexport const TYPING_START_FRAME = 42;\n\nexport const TYPING_CPS = 22;\n\nexport function morphProgressAt(\n  frame: number,\n  opts: { startFrame?: number; fps: number; speed: number },\n): number {\n  const startFrame = opts.startFrame ?? TYPING_START_FRAME;\n  const value = spring({\n    fps: opts.fps,\n    frame: frame * opts.speed - startFrame,\n    config: { damping: 14, stiffness: 200, mass: 0.6 },\n  });\n  return Math.max(0, Math.min(value, 1));\n}\n\nfunction introBounceIn(\n  frame: number,\n  fps: number,\n): { translateY: number; scale: number } {\n  const s = spring({\n    fps,\n    frame,\n    config: { damping: 14, stiffness: 110, mass: 0.7 },\n  });\n  const translateY = interpolate(s, [0, 1], [28, 0]);\n  const scale = interpolate(s, [0, 1], [0.97, 1]);\n  return { translateY, scale };\n}\n\nfunction fadeUpAt(\n  frame: number,\n  range: [number, number],\n): { opacity: number; translateY: number } {\n  const opts = {\n    extrapolateLeft: \"clamp\" as const,\n    extrapolateRight: \"clamp\" as const,\n  };\n  return {\n    opacity: interpolate(frame, range, [0, 1], opts),\n    translateY: interpolate(frame, range, [12, 0], opts),\n  };\n}\n\nfunction PlusIcon({ size, color }: { size: number; color: string }) {\n  return (\n    <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\">\n      <title>Add</title>\n      <path\n        d=\"M12 5v14M5 12h14\"\n        stroke={color}\n        strokeWidth={2}\n        strokeLinecap=\"round\"\n      />\n    </svg>\n  );\n}\n\nfunction MicIcon({ size, color }: { size: number; color: string }) {\n  return (\n    <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\">\n      <title>Voice input</title>\n      <rect\n        x={9}\n        y={3}\n        width={6}\n        height={11}\n        rx={3}\n        stroke={color}\n        strokeWidth={1.8}\n      />\n      <path\n        d=\"M5.5 11a6.5 6.5 0 0013 0M12 17.5V21M9 21h6\"\n        stroke={color}\n        strokeWidth={1.8}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nfunction WaveformIcon({ size, color }: { size: number; color: string }) {\n  const bars = [\n    { x: 4, h: 8 },\n    { x: 9, h: 16 },\n    { x: 14, h: 12 },\n    { x: 19, h: 20 },\n  ];\n  return (\n    <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\">\n      <title>Voice</title>\n      {bars.map((bar) => (\n        <rect\n          key={bar.x}\n          x={bar.x - 1}\n          y={(24 - bar.h) / 2}\n          width={2.4}\n          height={bar.h}\n          rx={1.2}\n          fill={color}\n        />\n      ))}\n    </svg>\n  );\n}\n\nfunction ArrowUpIcon({ size, color }: { size: number; color: string }) {\n  return (\n    <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\">\n      <title>Send</title>\n      <path\n        d=\"M12 19V6M12 6l-6 6M12 6l6 6\"\n        stroke={color}\n        strokeWidth={2.2}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nfunction ImageIcon({ size, color }: { size: number; color: string }) {\n  return (\n    <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\">\n      <title>Create an image</title>\n      <rect\n        x={3}\n        y={4}\n        width={18}\n        height={16}\n        rx={3}\n        stroke={color}\n        strokeWidth={1.7}\n      />\n      <circle cx={8.5} cy={9} r={1.6} stroke={color} strokeWidth={1.7} />\n      <path\n        d=\"M5 18l5-5 4 4 2-2 3 3\"\n        stroke={color}\n        strokeWidth={1.7}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nfunction PencilIcon({ size, color }: { size: number; color: string }) {\n  return (\n    <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\">\n      <title>Write or edit</title>\n      <path\n        d=\"M4 20h4L19 9a2.1 2.1 0 00-3-3L5 17v3z\"\n        stroke={color}\n        strokeWidth={1.7}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n      <path\n        d=\"M14 7l3 3\"\n        stroke={color}\n        strokeWidth={1.7}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nfunction GlobeIcon({ size, color }: { size: number; color: string }) {\n  return (\n    <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\">\n      <title>Look something up</title>\n      <circle cx={12} cy={12} r={9} stroke={color} strokeWidth={1.7} />\n      <path\n        d=\"M3 12h18M12 3c2.5 2.5 2.5 15 0 18M12 3c-2.5 2.5-2.5 15 0 18\"\n        stroke={color}\n        strokeWidth={1.7}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nfunction SuggestionChip({\n  label,\n  border,\n  color,\n  icon,\n}: {\n  label: string;\n  border: string;\n  color: string;\n  icon: React.ReactNode;\n}) {\n  return (\n    <div\n      style={{\n        display: \"inline-flex\",\n        alignItems: \"center\",\n        gap: 8,\n        padding: \"10px 16px\",\n        borderRadius: 20,\n        border: `1px solid ${border}`,\n        color,\n        fontFamily: SANS_FAMILY,\n        fontSize: 15,\n      }}\n    >\n      {icon}\n      <span>{label}</span>\n    </div>\n  );\n}\n\nexport function ChatGpt({\n  greeting = \"What's on your mind today?\",\n  placeholder = \"Ask anything\",\n  prompt = \"Make a sunset over a calm ocean\",\n  accentColor = \"#2F6FED\",\n  speed = 1,\n}: ChatGptProps) {\n  const frame = useCurrentFrame();\n  const { width, height, fps } = useVideoConfig();\n  const t = THEMES.light;\n\n  const refW = 1280;\n  const refH = 720;\n  const stageScale = Math.min(width / refW, height / refH);\n\n  const tw = useTypewriter(prompt, {\n    cps: TYPING_CPS,\n    speed,\n    startFrame: TYPING_START_FRAME,\n  });\n  const visibleText = tw.text;\n  const showText = tw.count > 0;\n  const morph = morphProgressAt(frame, { fps, speed });\n\n  const intro = introBounceIn(frame * speed, fps);\n  const headingFade = fadeUpAt(frame * speed, [4, 20]);\n  const pillFade = fadeUpAt(frame * speed, [10, 26]);\n  const chipsFade = fadeUpAt(frame * speed, [16, 32]);\n\n  const pillWidth = 820;\n  const pillLeft = (refW - pillWidth) / 2;\n  const pillTop = 300;\n  const pillHeight = 64;\n  const morphSize = 44;\n\n  const chipsOpacity = chipsFade.opacity * (1 - morph);\n  const chipsShift = chipsFade.translateY + 8 * morph;\n\n  return (\n    <AbsoluteFill style={{ background: \"transparent\" }}>\n      <div\n        style={{\n          position: \"absolute\",\n          left: \"50%\",\n          top: \"50%\",\n          width: refW,\n          height: refH,\n          transform: `translate(-50%, -50%) scale(${stageScale})`,\n        }}\n      >\n        <div\n          style={{\n            position: \"absolute\",\n            left: 0,\n            top: 196,\n            width: refW,\n            textAlign: \"center\",\n            fontFamily: SANS_FAMILY,\n            fontSize: 40,\n            fontWeight: 700,\n            color: t.fg,\n            opacity: headingFade.opacity,\n            transform: `translateY(${headingFade.translateY + intro.translateY * 0.4}px)`,\n          }}\n        >\n          {greeting}\n        </div>\n\n        <div\n          style={{\n            position: \"absolute\",\n            left: pillLeft,\n            top: pillTop,\n            width: pillWidth,\n            height: pillHeight,\n            background: t.inputBg,\n            border: `1px solid ${t.inputBorder}`,\n            borderRadius: 32,\n            boxShadow: \"0 8px 30px -14px rgba(13,13,13,0.14)\",\n            opacity: pillFade.opacity,\n            transform: `translateY(${pillFade.translateY + intro.translateY * 0.6}px) scale(${intro.scale})`,\n            transformOrigin: \"center top\",\n            display: \"flex\",\n            alignItems: \"center\",\n            paddingLeft: 20,\n            paddingRight: 12,\n            boxSizing: \"border-box\",\n          }}\n        >\n          <PlusIcon size={24} color={t.fg} />\n\n          <div\n            style={{\n              flex: 1,\n              display: \"flex\",\n              alignItems: \"center\",\n              marginLeft: 14,\n              fontFamily: SANS_FAMILY,\n              fontSize: 19,\n              overflow: \"hidden\",\n              whiteSpace: \"nowrap\",\n            }}\n          >\n            {showText ? (\n              <span\n                style={{\n                  color: t.fg,\n                  display: \"inline-flex\",\n                  alignItems: \"center\",\n                }}\n              >\n                {visibleText}\n                <Caret\n                  color={t.fg}\n                  blink={!tw.typing}\n                  speed={speed}\n                  height={22}\n                  marginLeft={2}\n                />\n              </span>\n            ) : (\n              <span\n                style={{\n                  color: t.fgMuted,\n                  display: \"inline-flex\",\n                  alignItems: \"center\",\n                }}\n              >\n                <Caret\n                  color={t.fg}\n                  blink={!tw.typing}\n                  speed={speed}\n                  height={22}\n                />\n                <span style={{ marginLeft: 6 }}>{placeholder}</span>\n              </span>\n            )}\n          </div>\n\n          <div\n            style={{\n              display: \"flex\",\n              alignItems: \"center\",\n              gap: 8,\n              flexShrink: 0,\n            }}\n          >\n            <div\n              style={{\n                width: 40,\n                height: 40,\n                display: \"flex\",\n                alignItems: \"center\",\n                justifyContent: \"center\",\n              }}\n            >\n              <MicIcon size={22} color={t.iconColor} />\n            </div>\n\n            <div\n              style={{\n                position: \"relative\",\n                width: morphSize,\n                height: morphSize,\n                flexShrink: 0,\n              }}\n            >\n              <div\n                style={{\n                  position: \"absolute\",\n                  inset: 0,\n                  display: \"flex\",\n                  alignItems: \"center\",\n                  justifyContent: \"center\",\n                  borderRadius: \"100%\",\n                  background: accentColor,\n                  opacity: 1 - morph,\n                  transform: `scale(${1 - 0.1 * morph})`,\n                }}\n              >\n                <WaveformIcon size={22} color=\"#FFFFFF\" />\n              </div>\n              <div\n                style={{\n                  position: \"absolute\",\n                  inset: 0,\n                  display: \"flex\",\n                  alignItems: \"center\",\n                  justifyContent: \"center\",\n                  borderRadius: \"100%\",\n                  background: t.sendBg,\n                  opacity: morph,\n                  transform: `scale(${0.8 + 0.2 * morph})`,\n                }}\n              >\n                <ArrowUpIcon size={22} color={t.sendArrow} />\n              </div>\n            </div>\n          </div>\n        </div>\n\n        <div\n          style={{\n            position: \"absolute\",\n            left: 0,\n            top: pillTop + pillHeight + 24,\n            width: refW,\n            display: \"flex\",\n            justifyContent: \"center\",\n            gap: 12,\n            opacity: chipsOpacity,\n            transform: `translateY(${chipsShift}px)`,\n          }}\n        >\n          <SuggestionChip\n            label=\"Create an image\"\n            border={t.chipBorder}\n            color={t.chipFg}\n            icon={<ImageIcon size={18} color={t.chipFg} />}\n          />\n          <SuggestionChip\n            label=\"Write or edit\"\n            border={t.chipBorder}\n            color={t.chipFg}\n            icon={<PencilIcon size={18} color={t.chipFg} />}\n          />\n          <SuggestionChip\n            label=\"Look something up\"\n            border={t.chipBorder}\n            color={t.chipFg}\n            icon={<GlobeIcon size={18} color={t.chipFg} />}\n          />\n        </div>\n      </div>\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/chat-gpt.tsx"
    }
  ],
  "type": "registry:component"
}
