import { ArrowRightOutlined, DashboardOutlined, FileSearchOutlined, PlayCircleOutlined, PlusOutlined, ShoppingOutlined, ThunderboltOutlined, } from "@ant-design/icons"; import { Fragment, useCallback, useEffect, useMemo, useRef, useState, type CSSProperties } from "react"; import type { WebViewKey, WebImageWorkbenchTool } from "../../types"; import { useScrollEntrance } from "../../hooks/useScrollEntrance"; import WelcomeSplash from "./WelcomeSplash"; import ToolboxSection from "./ToolboxSection"; import ScriptReviewShowcase from "./ScriptReviewShowcase"; import ModelGenerationShowcase from "./ModelGenerationShowcase"; function ScrollEntrance({ children, className, ...rest }: { children: React.ReactNode; className?: string } & React.HTMLAttributes) { const { ref, isVisible } = useScrollEntrance(); return (
{children}
); } const OSS_MUBAN = "https://stringtest.oss-cn-hangzhou.aliyuncs.com/muban"; const heroImage1 = `${OSS_MUBAN}/hero-1.png`; const heroImage2 = `${OSS_MUBAN}/hero-2.png`; const heroImage3 = `${OSS_MUBAN}/hero-3.png`; const featureEcommerceImage = `${OSS_MUBAN}/feature-ecommerce.jpg`; const featureScriptImage = `${OSS_MUBAN}/feature-script.jpg`; const featureTokenImage = `${OSS_MUBAN}/feature-token.jpg`; interface HomePageProps { onOpenGenerate: () => void; onOpenCanvas?: () => void; onOpenEcommerce: () => void; onOpenScriptReview?: () => void; onOpenTokenMonitor?: () => void; onSelectView: (view: WebViewKey) => void; onOpenImageTool?: (tool: WebImageWorkbenchTool) => void; } const HOME_BACKGROUND_VIDEO = "https://stringtest.oss-cn-hangzhou.aliyuncs.com/muban/hero-bg.mp4"; const HOME_CAROUSEL_IMAGES = [ { imageUrl: heroImage1, title: "灵感生成" }, { imageUrl: heroImage2, title: "画布创作" }, { imageUrl: heroImage3, title: "商业素材" }, ]; const HOME_FEATURES = [ { key: "model", eyebrow: "AI Generation", title: "模型生成", description: "通过AI模型生成文本、图片、视频,三种模式覆盖全内容类型,Agent对话式交互智能产出。", imageUrl: featureTokenImage, actionLabel: "开始生成", icon: , stats: ["文本生成", "图片生成", "视频生成"], }, { key: "ecommerce", eyebrow: "AI Commerce", title: "AI 电商生成", description: "上传产品图后自动生成主图、场景图、详情素材和短视频方案,快速覆盖多平台商品视觉。", imageUrl: featureEcommerceImage, actionLabel: "开始生成", icon: , stats: ["多场景", "多角度", "批量输出"], }, { key: "script", eyebrow: "Script Review", title: "剧本智能测评", description: "用六维雷达评分拆解剧本质量,从结构、节奏、人物到商业潜力给出可执行的优化路径。", imageUrl: featureScriptImage, actionLabel: "开始测评", icon: , stats: ["六维评分", "质量量化", "逐项优化"], }, ]; const HOME_EXPERIENCE_POINTS = [ { label: "生成", meta: "图像 / 视频", tone: "green" }, { label: "测评", meta: "剧本质量", tone: "cyan" }, { label: "成本", meta: "Token 用量", tone: "violet" }, { label: "电商", meta: "商品视觉", tone: "amber" }, ]; const ECOMMERCE_MATRIX_FEATURES = [ { icon: "⚡", title: "高效工作流", description: "自动化处理,一键触发" }, { icon: "⊞", title: "矩阵式产出", description: "多场景、多尺寸批量生成" }, { icon: "◈", title: "一致性保证", description: "智能保持商品特征与风格统一" }, ]; const ECOMMERCE_MATRIX_PROCESS = [ { icon: "📤", label: "上传原图", subLabel: "Upload" }, { icon: "🔍", label: "AI识别", subLabel: "Recognition" }, { icon: "⚙️", label: "生成处理", subLabel: "Processing" }, { icon: "📦", label: "矩阵产出", subLabel: "Output" }, ]; const ECOMMERCE_MATRIX_AI_STEPS = ["智能识别主体", "3D虚拟模特", "场景生成", "详情图生成", "批量导出"]; type EcommerceMatrixModelCard = { kind: "model"; color: "brown" | "green" | "blue"; tag: string; tagTone: string; resolution: string; square?: false; }; type EcommerceMatrixSceneCard = { kind: "scene"; color: "p1" | "p2" | "p3"; tag: string; tagTone: string; resolution: string; square: true; variant?: "greenery" | "blue"; }; type EcommerceMatrixLayoutCard = { kind: "layout"; color: "c1" | "c2" | "c3"; tag: string; tagTone: string; resolution: string; square: true; badge: string; badgeTone?: "purple"; }; type EcommerceMatrixCard = EcommerceMatrixModelCard | EcommerceMatrixSceneCard | EcommerceMatrixLayoutCard; const ECOMMERCE_MATRIX_OUTPUTS: Array<{ title: string; subtitle: string; cards: EcommerceMatrixCard[]; }> = [ { title: "3D 虚拟模特", subtitle: "Virtual Model", cards: [ { kind: "model", color: "brown", tag: "3D", tagTone: "tag-3d", resolution: "1024×1536" }, { kind: "model", color: "green", tag: "3D", tagTone: "tag-3d", resolution: "1024×1536" }, { kind: "model", color: "blue", tag: "3D", tagTone: "tag-3d", resolution: "1024×1536" }, ], }, { title: "场景图", subtitle: "Scene Image", cards: [ { kind: "scene", color: "p1", tag: "场景", tagTone: "tag-scene", resolution: "1024×1024", square: true }, { kind: "scene", color: "p2", tag: "场景", tagTone: "tag-scene", resolution: "1024×1024", square: true, variant: "greenery" }, { kind: "scene", color: "p3", tag: "场景", tagTone: "tag-scene", resolution: "1024×1024", square: true, variant: "blue" }, ], }, { title: "详情图", subtitle: "Detail Image", cards: [ { kind: "layout", color: "c1", tag: "详情", tagTone: "tag-layout", resolution: "1080×1080", square: true, badge: "优雅随行" }, { kind: "layout", color: "c2", tag: "详情", tagTone: "tag-layout", resolution: "1080×1080", square: true, badge: "限时特惠", badgeTone: "purple" }, { kind: "layout", color: "c3", tag: "详情", tagTone: "tag-layout", resolution: "1080×1080", square: true, badge: "新品首发" }, ], }, ]; const HOME_CAROUSEL_SLOTS = [-4, -3, -2, -1, 0, 1, 2, 3, 4]; const HOME_CAROUSEL_TRANSITION_MS = 860; type EcommerceFlowLine = { d: string; x: number; y: number; }; interface HomeCarouselMotion { direction: number; progress: 0 | 1; } function getPositiveModulo(value: number, length: number) { return ((value % length) + length) % length; } function getHomeCarouselCardStyle(offset: number): CSSProperties { const depth = Math.abs(offset); const direction = Math.sign(offset); const isActive = depth === 0; const xByDepth = [0, 190, 320, 430, 520, 590]; const yByDepth = [8, -2, -8, -13, -18, -24]; const scaleByDepth = [1, 1, 1, 1, 1, 1]; const x = direction * (xByDepth[depth] ?? xByDepth[xByDepth.length - 1]!); const y = yByDepth[depth] ?? yByDepth[yByDepth.length - 1]!; const z = isActive ? 90 : 28 - depth; const scale = scaleByDepth[depth] ?? scaleByDepth[scaleByDepth.length - 1]!; return { "--apple-card-offset": offset, "--apple-card-depth": depth, "--apple-card-z": 80 - depth, "--apple-card-x": `${x}px`, "--apple-card-y": `${y}px`, "--apple-card-z-offset": `${z}px`, "--apple-card-rotate-y": "0deg", "--apple-card-rotate-z": "0deg", "--apple-card-scale": String(scale), "--apple-card-opacity": String(depth > 4 ? 0 : 1), } as CSSProperties; } function EcommerceMatrixCardVisual({ card }: { card: EcommerceMatrixCard }) { if (card.kind === "model") { return (
); } if (card.kind === "scene") { return (
{card.variant === "greenery" ?
:
}
); } return (
{card.badge}
); } function EcommerceFeatureShowcase() { const rootRef = useRef(null); const inputCardRef = useRef(null); const outputGroupRefs = useRef>([]); const [flowLines, setFlowLines] = useState(() => ECOMMERCE_MATRIX_OUTPUTS.map(() => ({ d: "", x: 0, y: 0 })), ); useEffect(() => { let frameId: number | null = null; const updateFlowLines = () => { const root = rootRef.current; const inputCard = inputCardRef.current; if (!root || !inputCard) return; const rootRect = root.getBoundingClientRect(); const inputRect = inputCard.getBoundingClientRect(); const sx = inputRect.right - rootRect.left; const sy = inputRect.top - rootRect.top + inputRect.height / 2; const cornerRadius = 24; const nextLines = outputGroupRefs.current.slice(0, ECOMMERCE_MATRIX_OUTPUTS.length).map((group) => { if (!group) return { d: "", x: 0, y: 0 }; const groupRect = group.getBoundingClientRect(); const tx = groupRect.left - rootRect.left; const ty = groupRect.top - rootRect.top + groupRect.height / 2; const totalDistance = tx - sx; const splitX = sx + totalDistance * 0.3; const direction = ty > sy ? 1 : ty < sy ? -1 : 0; const verticalDistance = Math.abs(ty - sy); const resolvedRadius = Math.min(cornerRadius, verticalDistance / 2); const d = direction === 0 ? `M ${sx} ${sy} L ${tx} ${ty}` : `M ${sx} ${sy} L ${splitX} ${sy} Q ${splitX + resolvedRadius} ${sy}, ${splitX + resolvedRadius} ${ sy + direction * resolvedRadius } L ${splitX + resolvedRadius} ${ty - direction * resolvedRadius} Q ${splitX + resolvedRadius} ${ty}, ${ splitX + resolvedRadius * 2 } ${ty} L ${tx} ${ty}`; return { d, x: tx, y: ty }; }); setFlowLines(nextLines); }; const scheduleUpdate = () => { if (frameId !== null) { window.cancelAnimationFrame(frameId); } frameId = window.requestAnimationFrame(updateFlowLines); }; scheduleUpdate(); window.addEventListener("resize", scheduleUpdate); const resizeObserver = new ResizeObserver(scheduleUpdate); if (rootRef.current) { resizeObserver.observe(rootRef.current); } return () => { if (frameId !== null) { window.cancelAnimationFrame(frameId); } window.removeEventListener("resize", scheduleUpdate); resizeObserver.disconnect(); }; }, []); return (

一张原图
矩阵生产全场景图文

从商品原图到3D虚拟模特、场景图、详情图
AI工作流自动化,批量生成,高效出图

{ECOMMERCE_MATRIX_FEATURES.map((item) => (
{item.icon}

{item.title}

{item.description}

))}
{ECOMMERCE_MATRIX_PROCESS.map((item, index) => ( {index > 0 ? : null}
{item.icon} {item.label} {item.subLabel}
))}
商品原图 Input 3000×3000
DRINK MORE
DRINK MORE
DRINK MORE
AI 工作流
{ECOMMERCE_MATRIX_AI_STEPS.map((item) => (
{item}
))}
{ECOMMERCE_MATRIX_OUTPUTS.map((group, groupIndex) => (
{ outputGroupRefs.current[groupIndex] = node; }} className="output-group" >

{group.title}

{group.subtitle}

{group.cards.map((card, cardIndex) => (
{card.tag} {card.resolution}
))}
))}
); } function HomePage({ onOpenGenerate, onOpenCanvas, onOpenEcommerce, onOpenScriptReview, onOpenTokenMonitor, onSelectView, onOpenImageTool }: HomePageProps) { const [splashDismissed, setSplashDismissed] = useState(() => sessionStorage.getItem("omniai:splash-seen") === "1"); const [activeSlideIndex, setActiveSlideIndex] = useState(0); const [carouselMotion, setCarouselMotion] = useState(null); const [carouselIsResetting, setCarouselIsResetting] = useState(false); const carouselFrameRef = useRef(null); const carouselResetFrameRef = useRef(null); const carouselTimerRef = useRef(null); const carouselSlotOffsets = useMemo(() => { const direction = carouselMotion?.direction ?? 0; const minSlot = HOME_CAROUSEL_SLOTS[0]! + Math.min(direction, 0); const maxSlot = HOME_CAROUSEL_SLOTS[HOME_CAROUSEL_SLOTS.length - 1]! + Math.max(direction, 0); return Array.from({ length: maxSlot - minSlot + 1 }, (_, index) => minSlot + index); }, [carouselMotion?.direction]); const startCarouselShift = useCallback( (rawDirection: number) => { const direction = Math.sign(rawDirection); if (!direction || HOME_CAROUSEL_IMAGES.length <= 1 || carouselMotion) return; if (carouselFrameRef.current !== null) { window.cancelAnimationFrame(carouselFrameRef.current); } if (carouselTimerRef.current !== null) { window.clearTimeout(carouselTimerRef.current); } if (carouselResetFrameRef.current !== null) { window.cancelAnimationFrame(carouselResetFrameRef.current); } setCarouselIsResetting(false); setCarouselMotion({ direction, progress: 0 }); carouselFrameRef.current = window.requestAnimationFrame(() => { carouselFrameRef.current = window.requestAnimationFrame(() => { setCarouselMotion((current) => (current?.direction === direction ? { direction, progress: 1 } : current)); }); }); carouselTimerRef.current = window.setTimeout(() => { setCarouselIsResetting(true); setActiveSlideIndex((current) => getPositiveModulo(current + direction, HOME_CAROUSEL_IMAGES.length)); setCarouselMotion(null); carouselResetFrameRef.current = window.requestAnimationFrame(() => { carouselResetFrameRef.current = window.requestAnimationFrame(() => { setCarouselIsResetting(false); }); }); }, HOME_CAROUSEL_TRANSITION_MS); }, [carouselMotion], ); useEffect(() => { const timerId = window.setInterval(() => { startCarouselShift(-1); }, 2600); return () => window.clearInterval(timerId); }, [startCarouselShift]); useEffect( () => () => { if (carouselFrameRef.current !== null) { window.cancelAnimationFrame(carouselFrameRef.current); } if (carouselTimerRef.current !== null) { window.clearTimeout(carouselTimerRef.current); } if (carouselResetFrameRef.current !== null) { window.cancelAnimationFrame(carouselResetFrameRef.current); } }, [], ); const handleFeatureOpen = (featureKey: string) => { if (featureKey === "script") { (onOpenScriptReview ?? onOpenGenerate)(); return; } if (featureKey === "model") { onOpenGenerate(); return; } if (featureKey === "ecommerce") { onOpenEcommerce(); return; } onOpenGenerate(); }; return ( <> {!splashDismissed && ( { sessionStorage.setItem("omniai:splash-seen", "1"); setSplashDismissed(true); }} /> )}
{splashDismissed && (
); } export default HomePage;