{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "claude-chat",
  "title": "Claude Chat",
  "description": "Animated Claude chat input — types a prompt character-by-character and the waveform button morphs into a terracotta send button the moment text appears. Warm light/dark themes.",
  "dependencies": ["remotion", "@remotion/google-fonts"],
  "registryDependencies": ["@remocn/caret", "@remocn/remocn-ui"],
  "files": [
    {
      "path": "registry/remocn/claude-chat/index.tsx",
      "content": "\"use client\";\n\nimport { loadFont as loadBodyFont } 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 } = loadBodyFont();\n\nexport interface ClaudeChatProps {\n  greeting?: string;\n  placeholder?: string;\n  prompt?: string;\n  modelName?: string;\n  modelTier?: string;\n  accentColor?: string;\n  speed?: number;\n}\n\ninterface Theme {\n  page: string;\n  cardBg: string;\n  cardBorder: string;\n  fg: string;\n  fgMuted: string;\n  placeholder: string;\n  iconBtnBorder: string;\n}\n\nexport const THEMES: Record<\"light\" | \"dark\", Theme> = {\n  light: {\n    page: \"#F5F4EF\",\n    cardBg: \"#FFFFFF\",\n    cardBorder: \"#E8E5DD\",\n    fg: \"#1F1E1D\",\n    fgMuted: \"#73726C\",\n    placeholder: \"#A3A097\",\n    iconBtnBorder: \"#E0DDD4\",\n  },\n  dark: {\n    page: \"#262624\",\n    cardBg: \"#1F1E1D\",\n    cardBorder: \"#3A3936\",\n    fg: \"#F0EEE6\",\n    fgMuted: \"#9B9892\",\n    placeholder: \"#73726C\",\n    iconBtnBorder: \"#3A3936\",\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\nexport function 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\nexport function 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 ChevronDown({ size, color }: { size: number; color: string }) {\n  return (\n    <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\">\n      <title>Expand</title>\n      <path\n        d=\"M6 9l6 6 6-6\"\n        stroke={color}\n        strokeWidth={2}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\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 SendIcon({ size }: { size: number }) {\n  return (\n    <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\">\n      <title>Send</title>\n      <path\n        d=\"M12 19V5M12 5l-6 6M12 5l6 6\"\n        stroke=\"#FFFFFF\"\n        strokeWidth={2.2}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nfunction IconButton({\n  size,\n  border,\n  children,\n}: {\n  size: number;\n  border: string;\n  children: React.ReactNode;\n}) {\n  return (\n    <div\n      style={{\n        width: size,\n        height: size,\n        borderRadius: \"100%\",\n        border: `1px solid ${border}`,\n        display: \"flex\",\n        alignItems: \"center\",\n        justifyContent: \"center\",\n        flexShrink: 0,\n      }}\n    >\n      {children}\n    </div>\n  );\n}\n\nexport function ClaudeChat({\n  placeholder = \"Try: draft an email · summarize a doc · plan your week\",\n  prompt = \"Draft a launch tweet for our new release\",\n  modelName = \"Opus 4.8\",\n  modelTier = \"Max\",\n  accentColor = \"#D97757\",\n  speed = 1,\n}: ClaudeChatProps) {\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 cardFade = fadeUpAt(frame * speed, [6, 22]);\n\n  const cardWidth = 860;\n  const cardLeft = (refW - cardWidth) / 2;\n  const iconBtnSize = 36;\n  const morphSize = 40;\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: cardLeft,\n            top: 300,\n            width: cardWidth,\n            background: t.cardBg,\n            border: `1px solid ${t.cardBorder}`,\n            borderRadius: 24,\n            boxShadow: \"0 8px 30px -12px rgba(31,30,29,0.12)\",\n            opacity: cardFade.opacity,\n            transform: `translateY(${cardFade.translateY + intro.translateY}px) scale(${intro.scale})`,\n            transformOrigin: \"center top\",\n          }}\n        >\n          <div\n            style={{\n              padding: \"26px 28px\",\n              minHeight: 58,\n              fontFamily: SANS_FAMILY,\n              fontSize: 21,\n              lineHeight: 1.3,\n              display: \"flex\",\n              alignItems: \"center\",\n            }}\n          >\n            {showText ? (\n              <span style={{ color: t.fg }}>\n                {visibleText}\n                <Caret\n                  color={t.fg}\n                  blink={!tw.typing}\n                  speed={speed}\n                  height={24}\n                  radius={0}\n                  marginLeft={1}\n                  style={{\n                    verticalAlign: \"text-bottom\",\n                    transform: \"translateY(3px)\",\n                  }}\n                />\n              </span>\n            ) : (\n              <span\n                style={{\n                  color: t.placeholder,\n                  display: \"inline-flex\",\n                  alignItems: \"center\",\n                }}\n              >\n                <Caret\n                  color={t.fg}\n                  blink={!tw.typing}\n                  speed={speed}\n                  height={24}\n                  radius={0}\n                  marginLeft={1}\n                  style={{\n                    verticalAlign: \"text-bottom\",\n                    transform: \"translateY(3px)\",\n                  }}\n                />\n                <span style={{ marginLeft: 2 }}>{placeholder}</span>\n              </span>\n            )}\n          </div>\n\n          <div\n            style={{\n              padding: \"14px 18px\",\n              display: \"flex\",\n              justifyContent: \"space-between\",\n              alignItems: \"center\",\n            }}\n          >\n            <IconButton size={iconBtnSize} border={t.iconBtnBorder}>\n              <PlusIcon size={20} color={t.fg} />\n            </IconButton>\n\n            <div\n              style={{\n                display: \"flex\",\n                alignItems: \"center\",\n                gap: 14,\n              }}\n            >\n              <div\n                style={{\n                  display: \"flex\",\n                  alignItems: \"center\",\n                  gap: 7,\n                }}\n              >\n                <span\n                  style={{\n                    fontFamily: SANS_FAMILY,\n                    fontSize: 18,\n                    fontWeight: 500,\n                    color: t.fg,\n                  }}\n                >\n                  {modelName}\n                </span>\n                <span\n                  style={{\n                    fontFamily: SANS_FAMILY,\n                    fontSize: 18,\n                    fontWeight: 400,\n                    color: t.fgMuted,\n                  }}\n                >\n                  {modelTier}\n                </span>\n                <ChevronDown size={16} color={t.fgMuted} />\n              </div>\n\n              <IconButton size={iconBtnSize} border={t.iconBtnBorder}>\n                <MicIcon size={20} color={t.fg} />\n              </IconButton>\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                    border: `1px solid ${t.iconBtnBorder}`,\n                    opacity: 1 - morph,\n                    transform: `scale(${1 - 0.1 * morph})`,\n                  }}\n                >\n                  <WaveformIcon size={22} color={t.fg} />\n                </div>\n                <div\n                  style={{\n                    position: \"absolute\",\n                    inset: 0,\n                    display: \"flex\",\n                    alignItems: \"center\",\n                    justifyContent: \"center\",\n                    borderRadius: 10,\n                    background: accentColor,\n                    opacity: morph,\n                    transform: `scale(${0.8 + 0.2 * morph})`,\n                  }}\n                >\n                  <SendIcon size={22} />\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/claude-chat.tsx"
    }
  ],
  "type": "registry:component"
}
