"use client"; import * as React from "react"; import { useInView, useReducedMotion } from "motion/react"; import { BarChart3, Boxes, MessageSquare, RotateCcw, Swords } from "lucide-react"; import { Badge } from "@/components/ui/badge"; import { Segmented } from "@/components/ui/segmented"; import { cn } from "@/lib/utils"; import { Section, SectionHeading } from "./section"; import { Reveal } from "./reveal"; import { DesktopFrame, PhoneFrame } from "./frames"; import { fastestIndex, useFakeStreams, type FakeStream } from "./fake-stream"; import { DesktopArenaMock, DesktopChatMock, DesktopModelsMock, DesktopUsageMock, MobileArenaMock, MobileChatMock, MobileModelsMock, MobileUsageMock } from "./product-mock"; import { ARENA_PROMPT, ARENA_REPLIES, CHAT_PROMPT, CHAT_REPLY, MOCK_MODELS } from "./mock-data"; type Tab = "chat" | "arena" | "models" | "usage"; const CHAT_MODEL = MOCK_MODELS[0]; const CHAT_STREAMS: FakeStream[] = [{ text: CHAT_REPLY, speed: 4, delay: 700 }]; const ARENA_MODELS = [MOCK_MODELS[1], MOCK_MODELS[2], MOCK_MODELS[3]]; const ARENA_TEXTS = ARENA_MODELS.map((m) => ARENA_REPLIES[m.key]); const ARENA_STREAMS: FakeStream[] = [ { text: ARENA_TEXTS[0], speed: 3, delay: 900 }, { text: ARENA_TEXTS[1], speed: 5, delay: 350 }, { text: ARENA_TEXTS[2], speed: 2, delay: 1300 }, ]; const ARENA_FASTEST = fastestIndex(ARENA_STREAMS); const TAB_OPTIONS = [ { value: "chat" as Tab, label: "Chat", icon: }, { value: "arena" as Tab, label: "Arena", icon: }, { value: "models" as Tab, label: "Models", icon: }, { value: "usage" as Tab, label: "Usage", icon: }, ]; const TAB_URL: Record = { chat: "polyllm.io/app/chat", arena: "polyllm.io/app/arena", models: "polyllm.io/app/models", usage: "polyllm.io/app/usage" }; const TAB_NOTE: Record = { chat: "Real streaming with a live metadata line: tokens, time to first token, throughput and cost.", arena: "One prompt, three models in parallel. On phones you swipe between answers; on desktop they sit side by side.", models: "The catalog reads the registry: context, prices, lifecycle and capability badges. Star favorites, select two to compare.", usage: "Usage is computed from real request records and the provider price sheets, with savings suggestions.", }; /** Chat and Arena streams live here so the desktop and phone frames stay in sync. */ function DemoScreens({ tab, active, replay }: { tab: Tab; active: boolean; replay: number }) { const reduce = !!useReducedMotion(); const chat = useFakeStreams(CHAT_STREAMS, active && tab === "chat", replay, reduce); const arena = useFakeStreams(ARENA_STREAMS, active && tab === "arena", replay, reduce); const chatProps = { prompt: CHAT_PROMPT, reply: CHAT_REPLY, progress: chat.progress[0], elapsedMs: chat.elapsedFor(0), ttftMs: chat.ttftFor(0), model: CHAT_MODEL }; const arenaProps = { prompt: ARENA_PROMPT, models: ARENA_MODELS, replies: ARENA_TEXTS, progress: arena.progress, elapsedFor: arena.elapsedFor, ttftFor: arena.ttftFor, fastest: ARENA_FASTEST, allDone: arena.allDone, winner: 1 }; return ( {/* Desktop frame — never squeezed into the phone */} {tab === "chat" ? : tab === "arena" ? : tab === "models" ? : } {/* Phone frame — the real mobile layout */} {tab === "chat" ? : tab === "arena" ? : tab === "models" ? : } ); } export function InteractiveDemo() { const ref = React.useRef(null); const inView = useInView(ref, { once: true, margin: "0px 0px -15% 0px" }); const [tab, setTab] = React.useState("chat"); const [replay, setReplay] = React.useState(0); const select = (t: Tab) => { setTab(t); setReplay((r) => r + 1); }; return ( value={tab} onChange={select} options={TAB_OPTIONS} ariaLabel="Demo view" className="w-full sm:w-auto" fill /> Product mock · scripted output {tab === "chat" || tab === "arena" ? ( setReplay((r) => r + 1)} className="tap inline-flex h-9 items-center gap-1.5 rounded-md border border-border bg-bg-elevated px-3 text-[13px] text-fg-muted transition-colors hover:text-fg"> Replay ) : null} {TAB_NOTE[tab]} ); }
{TAB_NOTE[tab]}