{ "$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 background?: string;\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 \n \n Chat\n \n {messages.map((m, i) => (\n \n {m.text}\n \n ))}\n \n );\n}\n\nfunction DefaultPreview() {\n return (\n \n \n \n \n \n \n \n \n Ship faster.\n \n
\n The fastest way to launch your idea. Built with Remotion.\n
\n \n Get started\n \n \n \n );\n}\n\nexport function ChatToPreviewLayout({\n chat,\n preview,\n startChatRatio = 0.5,\n endChatRatio = 0.25,\n background = \"#0a0a0a\",\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 \n \n \n {chat ?? }\n \n \n \n \n {preview ?? }\n \n \n \n );\n}\n", "type": "registry:component", "target": "components/remocn/chat-to-preview-layout.tsx" } ], "type": "registry:component" }