{
  "name": "chat-gpt",
  "type": "registry:block",
  "description": "ChatGPT composer pill with greeting, suggestion chips, and voice-to-send morph",
  "dependencies": [
    "remotion",
    "@remotion/google-fonts"
  ],
  "registryDependencies": [
    "ai-composer-utils"
  ],
  "files": [
    {
      "path": "registry/bases/default/scenes/chat-gpt/index.tsx",
      "type": "registry:block",
      "content": "import { loadFont } from \"@remotion/google-fonts/Inter\";\nimport { AbsoluteFill, useCurrentFrame, useVideoConfig } from \"remotion\";\nimport {\n  AI_TYPING_CPS,\n  AI_TYPING_START,\n  Caret,\n  fadeUpAt,\n  introBounceIn,\n  morphProgressAt,\n  stageScale,\n  useTypewriter,\n} from \"@/remotion/lib/ai-composer-utils\";\n\nconst { fontFamily } = loadFont(\"normal\", {\n  weights: [\"400\", \"500\", \"600\", \"700\"],\n  subsets: [\"latin\"],\n});\n\nexport type ChatGptProps = {\n  greeting?: string;\n  placeholder?: string;\n  prompt?: string;\n  accentColor?: string;\n  theme?: \"light\" | \"dark\";\n  speed?: number;\n};\n\ntype 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\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        fontSize: 15,\n      }}\n    >\n      {icon}\n      <span>{label}</span>\n    </div>\n  );\n}\n\nexport const ChatGpt: React.FC<ChatGptProps> = ({\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  theme = \"light\",\n  speed = 1,\n}) => {\n  const frame = useCurrentFrame();\n  const { width, height, fps } = useVideoConfig();\n  const t = THEMES[theme];\n\n  const refW = 1280;\n  const scale = stageScale(width, height);\n\n  const tw = useTypewriter(prompt, {\n    cps: AI_TYPING_CPS,\n    speed,\n    startFrame: AI_TYPING_START,\n  });\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: t.page, fontFamily }}>\n      <div\n        style={{\n          position: \"absolute\",\n          left: \"50%\",\n          top: \"50%\",\n          width: refW,\n          height: 720,\n          transform: `translate(-50%, -50%) scale(${scale})`,\n        }}\n      >\n        <div\n          style={{\n            position: \"absolute\",\n            left: 0,\n            top: 196,\n            width: refW,\n            textAlign: \"center\",\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              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                {tw.text}\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"
    }
  ],
  "atlas": {
    "lane": "blocks",
    "drive": "time",
    "tier": "core",
    "tags": [
      "ai"
    ]
  }
}