{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "v0",
  "title": "v0",
  "description": "Animated Vercel v0 composer — types a prompt into the dark textarea and the mic button morphs into a send button. Dark/light themes.",
  "dependencies": ["remotion", "@remotion/google-fonts"],
  "registryDependencies": ["@remocn/caret", "@remocn/remocn-ui"],
  "files": [
    {
      "path": "registry/remocn/v0/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 V0Props {\n  greeting?: string;\n  placeholder?: string;\n  prompt?: string;\n  modelName?: string;\n  projectName?: string;\n  speed?: number;\n}\n\ninterface Theme {\n  page: string;\n  boxBg: string;\n  boxBorder: string;\n  fg: string;\n  fgMuted: string;\n  iconColor: string;\n  btnBg: string;\n  btnFg: string;\n}\n\nexport const THEMES: Record<\"light\" | \"dark\", Theme> = {\n  light: {\n    page: \"#FFFFFF\",\n    boxBg: \"#FFFFFF\",\n    boxBorder: \"#E3E3E3\",\n    fg: \"#0D0D0D\",\n    fgMuted: \"#8A8A8A\",\n    iconColor: \"#5D5D5D\",\n    btnBg: \"#0D0D0D\",\n    btnFg: \"#FFFFFF\",\n  },\n  dark: {\n    page: \"#000000\",\n    boxBg: \"#0A0A0A\",\n    boxBorder: \"#2A2A2A\",\n    fg: \"#EDEDED\",\n    fgMuted: \"#8A8A8A\",\n    iconColor: \"#A0A0A0\",\n    btnBg: \"#FFFFFF\",\n    btnFg: \"#0A0A0A\",\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 V0LogoIcon({ size, color }: { size: number; color: string }) {\n  return (\n    <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\">\n      <title>v0</title>\n      <rect\n        x={2.5}\n        y={2.5}\n        width={19}\n        height={19}\n        rx={5}\n        stroke={color}\n        strokeWidth={1.8}\n      />\n      <rect\n        x={7.5}\n        y={7.5}\n        width={9}\n        height={9}\n        rx={2.5}\n        stroke={color}\n        strokeWidth={1.8}\n      />\n    </svg>\n  );\n}\n\nfunction ChevronDownIcon({ 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 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 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\nexport function V0({\n  greeting = \"What do you want to create?\",\n  placeholder = \"Ask v0 to build…\",\n  prompt = \"a landing page for my SaaS with pricing and testimonials\",\n  modelName = \"v0 Max\",\n  projectName = \"Project\",\n  speed = 1,\n}: V0Props) {\n  const frame = useCurrentFrame();\n  const { width, height, fps } = useVideoConfig();\n  const t = THEMES.dark;\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 boxFade = fadeUpAt(frame * speed, [10, 26]);\n\n  const boxWidth = 880;\n  const boxLeft = (refW - boxWidth) / 2;\n  const boxTop = 270;\n  const boxHeight = 150;\n  const btnSize = 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          translate: \"-50% -50%\",\n          scale: `${stageScale}`,\n        }}\n      >\n        <div\n          style={{\n            position: \"absolute\",\n            left: 0,\n            top: 150,\n            width: refW,\n            textAlign: \"center\",\n            fontFamily: SANS_FAMILY,\n            fontSize: 44,\n            fontWeight: 700,\n            color: t.fg,\n            opacity: headingFade.opacity,\n            translate: `0 ${headingFade.translateY + intro.translateY * 0.4}px`,\n          }}\n        >\n          {greeting}\n        </div>\n\n        <div\n          style={{\n            position: \"absolute\",\n            left: boxLeft,\n            top: boxTop,\n            width: boxWidth,\n            height: boxHeight,\n            background: t.boxBg,\n            border: `1px solid ${t.boxBorder}`,\n            borderRadius: 16,\n            boxShadow: \"0 8px 40px -16px rgba(0,0,0,0.8)\",\n            opacity: boxFade.opacity,\n            translate: `0 ${boxFade.translateY + intro.translateY * 0.6}px`,\n            scale: `${intro.scale}`,\n            transformOrigin: \"center top\",\n            display: \"flex\",\n            flexDirection: \"column\",\n            boxSizing: \"border-box\",\n          }}\n        >\n          <div\n            style={{\n              flex: 1,\n              padding: \"18px 20px\",\n              fontFamily: SANS_FAMILY,\n              fontSize: 18,\n              color: t.fg,\n              display: \"flex\",\n              alignItems: \"flex-start\",\n              overflow: \"hidden\",\n            }}\n          >\n            {showText ? (\n              <span\n                style={{\n                  color: t.fg,\n                  whiteSpace: \"pre-wrap\",\n                }}\n              >\n                {visibleText}\n                <Caret\n                  color={t.fg}\n                  blink={!tw.typing}\n                  speed={speed}\n                  height={20}\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={20}\n                />\n                <span style={{ marginLeft: 6 }}>{placeholder}</span>\n              </span>\n            )}\n          </div>\n\n          <div\n            style={{\n              padding: \"12px 16px\",\n              display: \"flex\",\n              justifyContent: \"space-between\",\n              alignItems: \"center\",\n            }}\n          >\n            <div style={{ display: \"flex\", alignItems: \"center\", gap: 10 }}>\n              <PlusIcon size={20} color={t.iconColor} />\n              <div\n                style={{\n                  display: \"inline-flex\",\n                  alignItems: \"center\",\n                  gap: 7,\n                }}\n              >\n                <V0LogoIcon size={18} color={t.fg} />\n                <span\n                  style={{\n                    fontFamily: SANS_FAMILY,\n                    fontSize: 15,\n                    fontWeight: 500,\n                    color: t.fg,\n                  }}\n                >\n                  {modelName}\n                </span>\n                <ChevronDownIcon size={14} color={t.fgMuted} />\n              </div>\n            </div>\n\n            <div style={{ display: \"flex\", alignItems: \"center\", gap: 12 }}>\n              <div\n                style={{\n                  display: \"inline-flex\",\n                  alignItems: \"center\",\n                  gap: 4,\n                }}\n              >\n                <span\n                  style={{\n                    fontFamily: SANS_FAMILY,\n                    fontSize: 15,\n                    color: t.fgMuted,\n                  }}\n                >\n                  {projectName}\n                </span>\n                <ChevronDownIcon size={14} color={t.fgMuted} />\n              </div>\n\n              <div\n                style={{\n                  position: \"relative\",\n                  width: btnSize,\n                  height: btnSize,\n                  borderRadius: 10,\n                  background: t.btnBg,\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                    opacity: 1 - morph,\n                    scale: `${1 - 0.08 * morph}`,\n                  }}\n                >\n                  <MicIcon size={20} color={t.btnFg} />\n                </div>\n                <div\n                  style={{\n                    position: \"absolute\",\n                    inset: 0,\n                    display: \"flex\",\n                    alignItems: \"center\",\n                    justifyContent: \"center\",\n                    opacity: morph,\n                    scale: `${0.85 + 0.15 * morph}`,\n                  }}\n                >\n                  <ArrowUpIcon size={20} color={t.btnFg} />\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/v0.tsx"
    }
  ],
  "type": "registry:component"
}
