{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "chat-to-preview-layout",
  "title": "Chat to Preview Layout",
  "description": "Two-column layout where chat shrinks and preview expands.",
  "dependencies": ["remotion"],
  "files": [
    {
      "path": "registry/remocn/chat-to-preview-layout/index.tsx",
      "content": "\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { Easing, interpolate, useCurrentFrame, useVideoConfig } from \"remotion\";\n\nexport interface ChatToPreviewLayoutProps {\n  chat?: ReactNode;\n  preview?: ReactNode;\n  startChatRatio?: number;\n  endChatRatio?: number;\n  speed?: number;\n  className?: string;\n}\n\nconst FONT_FAMILY =\n  \"var(--font-geist-sans), -apple-system, BlinkMacSystemFont, sans-serif\";\n\nfunction DefaultChat() {\n  const messages = [\n    { from: \"user\", text: \"Build me a landing page\" },\n    { from: \"ai\", text: \"Sure — what should it feature?\" },\n    { from: \"user\", text: \"Hero, pricing, footer.\" },\n    { from: \"ai\", text: \"On it. Generating now...\" },\n  ];\n  return (\n    <div\n      style={{\n        position: \"absolute\",\n        inset: 0,\n        padding: 24,\n        display: \"flex\",\n        flexDirection: \"column\",\n        gap: 12,\n        fontFamily: FONT_FAMILY,\n        background: \"#111\",\n      }}\n    >\n      <div\n        style={{\n          color: \"#888\",\n          fontSize: 12,\n          fontWeight: 600,\n          letterSpacing: \"0.08em\",\n          textTransform: \"uppercase\",\n          marginBottom: 8,\n        }}\n      >\n        Chat\n      </div>\n      {messages.map((m, i) => (\n        <div\n          key={i}\n          style={{\n            alignSelf: m.from === \"user\" ? \"flex-end\" : \"flex-start\",\n            background: m.from === \"user\" ? \"#0ea5e9\" : \"#262626\",\n            color: \"white\",\n            padding: \"10px 14px\",\n            borderRadius: 14,\n            fontSize: 14,\n            maxWidth: \"85%\",\n          }}\n        >\n          {m.text}\n        </div>\n      ))}\n    </div>\n  );\n}\n\nfunction DefaultPreview() {\n  return (\n    <div\n      style={{\n        position: \"absolute\",\n        inset: 0,\n        background: \"white\",\n        display: \"flex\",\n        flexDirection: \"column\",\n        fontFamily: FONT_FAMILY,\n      }}\n    >\n      <div\n        style={{\n          height: 36,\n          background: \"#f4f4f5\",\n          borderBottom: \"1px solid #e4e4e7\",\n          display: \"flex\",\n          alignItems: \"center\",\n          gap: 6,\n          padding: \"0 12px\",\n        }}\n      >\n        <div\n          style={{\n            width: 10,\n            height: 10,\n            borderRadius: 999,\n            background: \"#ef4444\",\n          }}\n        />\n        <div\n          style={{\n            width: 10,\n            height: 10,\n            borderRadius: 999,\n            background: \"#f59e0b\",\n          }}\n        />\n        <div\n          style={{\n            width: 10,\n            height: 10,\n            borderRadius: 999,\n            background: \"#22c55e\",\n          }}\n        />\n      </div>\n      <div\n        style={{\n          flex: 1,\n          padding: 32,\n          display: \"flex\",\n          flexDirection: \"column\",\n          gap: 16,\n        }}\n      >\n        <div\n          style={{\n            fontSize: 36,\n            fontWeight: 700,\n            letterSpacing: \"-0.03em\",\n            color: \"#0a0a0a\",\n          }}\n        >\n          Ship faster.\n        </div>\n        <div style={{ fontSize: 16, color: \"#71717a\", maxWidth: 360 }}>\n          The fastest way to launch your idea. Built with Remotion.\n        </div>\n        <div\n          style={{\n            marginTop: 12,\n            display: \"inline-flex\",\n            background: \"#0a0a0a\",\n            color: \"white\",\n            padding: \"10px 18px\",\n            borderRadius: 10,\n            alignSelf: \"flex-start\",\n            fontSize: 14,\n            fontWeight: 600,\n          }}\n        >\n          Get started\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport function ChatToPreviewLayout({\n  chat,\n  preview,\n  startChatRatio = 0.5,\n  endChatRatio = 0.25,\n  speed = 1,\n  className,\n}: ChatToPreviewLayoutProps) {\n  const frame = useCurrentFrame() * speed;\n  const { durationInFrames } = useVideoConfig();\n\n  // Apple's signature ease — slow start, dramatic settle. Replaces the older\n  // symmetric bezier so the morph feels deliberate instead of mechanical.\n  const APPLE_EASE = Easing.bezier(0.16, 1, 0.3, 1);\n\n  const morphStart = durationInFrames * 0.1;\n  const morphEnd = durationInFrames * 0.7;\n\n  const ratio = interpolate(\n    frame,\n    [morphStart, morphEnd],\n    [startChatRatio, endChatRatio],\n    {\n      extrapolateLeft: \"clamp\",\n      extrapolateRight: \"clamp\",\n      easing: APPLE_EASE,\n    },\n  );\n\n  // Right (preview) panel reveal: fade in + slide left into its growing slot.\n  // Anchored to the same morph window so it feels like the chat is physically\n  // dragging the preview into existence.\n  const previewOpacity = interpolate(frame, [morphStart, morphEnd], [0, 1], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n    easing: APPLE_EASE,\n  });\n  const previewTx = interpolate(frame, [morphStart, morphEnd], [40, 0], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n    easing: APPLE_EASE,\n  });\n\n  const chatBasis = `${ratio * 100}%`;\n  const previewBasis = `${(1 - ratio) * 100}%`;\n\n  // Inner content min-widths. These are the lever that prevents text from\n  // re-flowing inside the columns as the parent flex-basis morphs — content\n  // is rendered at a fixed virtual width, then clipped by `overflow: hidden`\n  // on the outer column. No reflow → no jumping characters.\n  const CHAT_INNER_MIN = 520;\n  const PREVIEW_INNER_MIN = 720;\n\n  return (\n    <div\n      className={className}\n      style={{\n        position: \"absolute\",\n        inset: 0,\n        padding: 32,\n        display: \"flex\",\n        gap: 16,\n      }}\n    >\n      <div\n        style={{\n          flexBasis: chatBasis,\n          flexGrow: 0,\n          flexShrink: 0,\n          position: \"relative\",\n          overflow: \"hidden\",\n          borderRadius: 16,\n          border: \"1px solid rgba(255,255,255,0.08)\",\n        }}\n      >\n        <div\n          style={{\n            position: \"absolute\",\n            inset: 0,\n            minWidth: CHAT_INNER_MIN,\n          }}\n        >\n          {chat ?? <DefaultChat />}\n        </div>\n      </div>\n      <div\n        style={{\n          flexBasis: previewBasis,\n          flexGrow: 0,\n          flexShrink: 0,\n          position: \"relative\",\n          overflow: \"hidden\",\n          borderRadius: 16,\n          border: \"1px solid rgba(255,255,255,0.08)\",\n          opacity: previewOpacity,\n          transform: `translateX(${previewTx}px)`,\n        }}\n      >\n        <div\n          style={{\n            position: \"absolute\",\n            inset: 0,\n            minWidth: PREVIEW_INNER_MIN,\n          }}\n        >\n          {preview ?? <DefaultPreview />}\n        </div>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/chat-to-preview-layout.tsx"
    }
  ],
  "type": "registry:component"
}
