"use client"; import * as React from "react"; import { useInView, useReducedMotion } from "motion/react"; import { cn } from "@/lib/utils"; import { DesktopFrame, PhoneFrame } from "./frames"; import { ArenaMiniCard, DesktopChatMock, MobileChatMock } from "./product-mock"; import { fastestIndex, useFakeStreams, type FakeStream } from "./fake-stream"; import { ARENA_REPLIES, HERO_PROMPT, HERO_REPLY, MOCK_MODELS } from "./mock-data"; const HERO_MODEL = MOCK_MODELS[0]; const CHAT_STREAMS: FakeStream[] = [{ text: HERO_REPLY, speed: 5, delay: 800 }]; const ARENA_MODELS = [MOCK_MODELS[1], MOCK_MODELS[2], MOCK_MODELS[3]]; const ARENA_TEXTS = ARENA_MODELS.map((m) => ARENA_REPLIES[m.key]); /** The mini Arena starts a beat after the chat answer so the eye follows one thing at a time. */ const ARENA_STREAMS: FakeStream[] = [ { text: ARENA_TEXTS[0], speed: 3, delay: 3200 }, { text: ARENA_TEXTS[1], speed: 5, delay: 2700 }, { text: ARENA_TEXTS[2], speed: 2, delay: 3600 }, ]; const ARENA_FASTEST = fastestIndex(ARENA_STREAMS); /** * Hero visual: the real product, animated. Desktop (≥ md) shows the app window with the sidebar, * model pill, a streaming answer with cursor and its metadata line, plus a mini Arena card; phones * get a phone-shaped frame with the real mobile layout (header, messages, composer, bottom nav). * Both share one stream so switching breakpoints never desyncs. */ export function HeroVisual({ className }: { className?: string }) { const ref = React.useRef(null); const inView = useInView(ref, { once: true, margin: "0px 0px -10% 0px" }); const reduce = !!useReducedMotion(); const chat = useFakeStreams(CHAT_STREAMS, inView, 0, reduce); const arena = useFakeStreams(ARENA_STREAMS, inView, 0, reduce); const arenaStarted = arena.progress.some((p) => p > 0); const chatProps = { prompt: HERO_PROMPT, reply: HERO_REPLY, progress: chat.progress[0], elapsedMs: chat.elapsedFor(0), ttftMs: chat.ttftFor(0), model: HERO_MODEL }; return (
{/* Desktop */}
{/* Phone */}
); }