{
  "name": "ai-composer-showcase",
  "type": "registry:block",
  "description": "Showcase reel cutting through five AI composer interfaces with title and end cards",
  "dependencies": [
    "remotion",
    "@remotion/google-fonts",
    "@remotion/transitions"
  ],
  "registryDependencies": [
    "layout",
    "transition-fade",
    "chat-gpt",
    "claude-chat",
    "v0",
    "claude-code",
    "opencode"
  ],
  "composition": {
    "id": "AiComposerShowcase",
    "component": "AiComposerShowcase",
    "durationInFrames": 533,
    "fps": 30,
    "width": 1920,
    "height": 1080,
    "importPath": "@/compositions/ai-composer-showcase/index"
  },
  "files": [
    {
      "path": "registry/bases/default/compositions/ai-composer-showcase/index.tsx",
      "type": "registry:block",
      "content": "import { loadFont } from \"@remotion/google-fonts/Inter\";\nimport {\n  AbsoluteFill,\n  Easing,\n  Img,\n  interpolate,\n  spring,\n  staticFile,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\nimport { TransitionSeries } from \"@remotion/transitions\";\nimport { transitionFade } from \"@/remotion/primitives/transition-fade\";\nimport { getSafeAreaPadding, scaleFont } from \"@/remotion/lib/layout\";\nimport { ChatGpt } from \"@/remotion/scenes/chat-gpt\";\nimport { ClaudeChat } from \"@/remotion/scenes/claude-chat\";\nimport { V0Composer } from \"@/remotion/scenes/v0\";\nimport { ClaudeCode } from \"@/remotion/scenes/claude-code\";\nimport { Opencode } from \"@/remotion/scenes/opencode\";\n\nconst { fontFamily } = loadFont(\"normal\", {\n  weights: [\"400\", \"500\", \"600\", \"700\"],\n  subsets: [\"latin\"],\n});\n\nconst BG = \"#080810\";\nconst PHOSPHOR = \"#e8b86d\";\nconst ENTER_EASE = Easing.bezier(0.16, 1, 0.3, 1);\nconst EXIT_EASE = Easing.in(Easing.cubic);\n\nconst FADE = transitionFade({ durationInFrames: 12 });\n\nconst TITLE_DUR = 50;\nconst SCENE_DUR = 100;\nconst END_DUR = 55;\nconst END_EXIT_DUR = 18;\n\n/* ─── shared pieces ─── */\n\nconst Logo: React.FC<{ size: number }> = ({ size }) => (\n  <Img src={staticFile(\"logo.svg\")} style={{ width: size, height: size }} />\n);\n\nconst GlowDot: React.FC<{ delay: number; size: number }> = ({ delay, size }) => {\n  const frame = useCurrentFrame();\n  const { fps } = useVideoConfig();\n  const progress = spring({ frame, fps, delay, config: { damping: 20 } });\n  return (\n    <div\n      style={{\n        width: size,\n        height: size,\n        borderRadius: \"50%\",\n        background: PHOSPHOR,\n        opacity: progress,\n        boxShadow: `0 0 ${size * 2}px ${size * 0.5}px ${PHOSPHOR}55`,\n      }}\n    />\n  );\n};\n\n/* ─── title card ─── */\n\nconst TitleCard: React.FC = () => {\n  const frame = useCurrentFrame();\n  const { fps, width } = useVideoConfig();\n\n  const logoProgress = spring({ frame, fps, delay: 4, config: { damping: 14 } });\n  const titleProgress = spring({ frame, fps, delay: 10, config: { damping: 16 } });\n  const subProgress = spring({ frame, fps, delay: 18, config: { damping: 18 } });\n  const exitProgress = interpolate(frame, [TITLE_DUR - 14, TITLE_DUR], [1, 0], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n    easing: EXIT_EASE,\n  });\n\n  return (\n    <AbsoluteFill\n      style={{\n        backgroundColor: BG,\n        justifyContent: \"center\",\n        alignItems: \"center\",\n        backgroundImage:\n          \"radial-gradient(ellipse 80% 60% at 50% 40%, rgba(232,184,109,0.06), transparent)\",\n        opacity: exitProgress,\n        fontFamily,\n      }}\n    >\n      <div style={{ textAlign: \"center\" }}>\n        <div\n          style={{\n            display: \"flex\",\n            justifyContent: \"center\",\n            marginBottom: scaleFont(24, width),\n            opacity: logoProgress,\n            transform: `scale(${logoProgress})`,\n          }}\n        >\n          <Logo size={scaleFont(64, width)} />\n        </div>\n        <div\n          style={{\n            fontSize: scaleFont(68, width),\n            fontWeight: 700,\n            color: \"#fff\",\n            letterSpacing: -1,\n            opacity: titleProgress,\n            transform: `translateY(${interpolate(titleProgress, [0, 1], [16, 0])}px)`,\n          }}\n        >\n          AI Composers\n        </div>\n        <div\n          style={{\n            marginTop: scaleFont(14, width),\n            fontSize: scaleFont(26, width),\n            color: \"rgba(255,255,255,0.5)\",\n            fontWeight: 400,\n            opacity: subProgress,\n            transform: `translateY(${interpolate(subProgress, [0, 1], [10, 0])}px)`,\n          }}\n        >\n          Five interfaces. Animated. Source you own.\n        </div>\n      </div>\n    </AbsoluteFill>\n  );\n};\n\n/* ─── scene wrapper with label + feature callout ─── */\n\ntype SceneConfig = {\n  name: string;\n  feature: string;\n};\n\nconst SceneLabel: React.FC<{ config: SceneConfig }> = ({ config }) => {\n  const frame = useCurrentFrame();\n  const { fps, width, height } = useVideoConfig();\n  const safe = getSafeAreaPadding({ width, height });\n\n  const labelProgress = spring({ frame, fps, delay: 6, config: { damping: 16 } });\n  const featureProgress = spring({ frame, fps, delay: 22, config: { damping: 18 } });\n  const exitProgress = interpolate(frame, [SCENE_DUR - 14, SCENE_DUR], [1, 0], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n    easing: EXIT_EASE,\n  });\n\n  return (\n    <div\n      style={{\n        position: \"absolute\",\n        bottom: 0,\n        left: 0,\n        right: 0,\n        paddingTop: scaleFont(48, width),\n        paddingBottom: Math.max(safe.paddingBottom, scaleFont(32, width)),\n        paddingLeft: Math.max(safe.paddingLeft, scaleFont(48, width)),\n        paddingRight: Math.max(safe.paddingRight, scaleFont(48, width)),\n        background: \"linear-gradient(to top, rgba(0,0,0,0.85) 0%, transparent 100%)\",\n        display: \"flex\",\n        alignItems: \"center\",\n        justifyContent: \"space-between\",\n        gap: scaleFont(16, width),\n        opacity: exitProgress,\n        fontFamily,\n      }}\n    >\n      <div style={{ display: \"flex\", alignItems: \"center\", gap: scaleFont(12, width), minWidth: 0 }}>\n        <div\n          style={{\n            opacity: labelProgress,\n            transform: `translateX(${interpolate(labelProgress, [0, 1], [-12, 0])}px)`,\n            flexShrink: 0,\n          }}\n        >\n          <GlowDot delay={14} size={scaleFont(6, width)} />\n        </div>\n        <div\n          style={{\n            fontSize: scaleFont(28, width),\n            fontWeight: 600,\n            color: \"#fff\",\n            letterSpacing: -0.3,\n            opacity: labelProgress,\n            transform: `translateX(${interpolate(labelProgress, [0, 1], [-8, 0])}px)`,\n            whiteSpace: \"nowrap\",\n          }}\n        >\n          {config.name}\n        </div>\n      </div>\n      <div\n        style={{\n          fontSize: scaleFont(18, width),\n          fontWeight: 500,\n          color: PHOSPHOR,\n          opacity: featureProgress,\n          transform: `translateX(${interpolate(featureProgress, [0, 1], [12, 0])}px)`,\n          textAlign: \"right\",\n        }}\n      >\n        {config.feature}\n      </div>\n    </div>\n  );\n};\n\nconst SCENES: Array<{\n  Component: React.FC;\n  config: SceneConfig;\n}> = [\n  { Component: ChatGpt, config: { name: \"ChatGPT\", feature: \"Typed prompts + suggestion chips\" } },\n  { Component: ClaudeChat, config: { name: \"Claude\", feature: \"Artifact split view + streaming\" } },\n  { Component: V0Composer, config: { name: \"v0\", feature: \"Model selectors + quick actions\" } },\n  { Component: ClaudeCode, config: { name: \"Claude Code\", feature: \"Terminal UI + CLI typing\" } },\n  { Component: Opencode, config: { name: \"OpenCode\", feature: \"TUI input + agent status\" } },\n];\n\n/* ─── end card ─── */\n\nconst EndCard: React.FC = () => {\n  const frame = useCurrentFrame();\n  const { fps, width } = useVideoConfig();\n\n  const logoProgress = spring({ frame, fps, delay: 4, config: { damping: 14 } });\n  const titleProgress = spring({ frame, fps, delay: 10, config: { damping: 16 } });\n  const tagProgress = spring({ frame, fps, delay: 18, config: { damping: 18 } });\n  const exitProgress = interpolate(\n    frame,\n    [END_DUR - END_EXIT_DUR, END_DUR],\n    [1, 0],\n    { extrapolateLeft: \"clamp\", extrapolateRight: \"clamp\", easing: EXIT_EASE },\n  );\n\n  return (\n    <AbsoluteFill\n      style={{\n        backgroundColor: BG,\n        justifyContent: \"center\",\n        alignItems: \"center\",\n        backgroundImage:\n          \"radial-gradient(ellipse 70% 50% at 50% 50%, rgba(232,184,109,0.05), transparent)\",\n        opacity: exitProgress,\n        fontFamily,\n      }}\n    >\n      <div style={{ textAlign: \"center\" }}>\n        <div\n          style={{\n            display: \"flex\",\n            justifyContent: \"center\",\n            marginBottom: scaleFont(20, width),\n            opacity: logoProgress,\n            transform: `scale(${logoProgress})`,\n          }}\n        >\n          <Logo size={scaleFont(56, width)} />\n        </div>\n        <div\n          style={{\n            fontSize: scaleFont(48, width),\n            fontWeight: 700,\n            color: \"#fff\",\n            letterSpacing: -0.5,\n            opacity: titleProgress,\n            transform: `translateY(${interpolate(titleProgress, [0, 1], [12, 0])}px)`,\n          }}\n        >\n          remotionui.com\n        </div>\n        <div\n          style={{\n            marginTop: scaleFont(12, width),\n            fontSize: scaleFont(22, width),\n            color: PHOSPHOR,\n            fontWeight: 500,\n            opacity: tagProgress,\n            transform: `translateY(${interpolate(tagProgress, [0, 1], [8, 0])}px)`,\n          }}\n        >\n          Source you own. Ship your story.\n        </div>\n      </div>\n    </AbsoluteFill>\n  );\n};\n\n/* ─── main composition ─── */\n\nexport const AiComposerShowcase: React.FC = () => {\n  return (\n    <AbsoluteFill style={{ backgroundColor: BG }}>\n      <TransitionSeries>\n        <TransitionSeries.Sequence durationInFrames={TITLE_DUR}>\n          <TitleCard />\n        </TransitionSeries.Sequence>\n\n        <TransitionSeries.Transition {...FADE} />\n\n        {SCENES.map(({ Component, config }, i) => (\n          <TransitionSeries.Sequence key={config.name} durationInFrames={SCENE_DUR}>\n            <AbsoluteFill>\n              <Component />\n              <SceneLabel config={config} />\n            </AbsoluteFill>\n          </TransitionSeries.Sequence>\n        )).reduce<React.ReactNode[]>((acc, seq, i) => {\n          if (i > 0) acc.push(<TransitionSeries.Transition key={`t-${i}`} {...FADE} />);\n          acc.push(seq);\n          return acc;\n        }, [])}\n\n        <TransitionSeries.Transition {...FADE} />\n\n        <TransitionSeries.Sequence durationInFrames={END_DUR}>\n          <EndCard />\n        </TransitionSeries.Sequence>\n      </TransitionSeries>\n    </AbsoluteFill>\n  );\n};\n"
    }
  ],
  "atlas": {
    "lane": "reels",
    "drive": "time",
    "tier": "advanced",
    "tags": [
      "ai"
    ]
  }
}