import { Clock3, Hourglass } from 'lucide-react'; import { motion } from 'motion/react'; import { useEffect, useId, useRef } from 'react'; import generationHeroVideo from '../../media/create_bg_video.mp4'; const GENERATION_PROGRESS_RING_GAP_DEGREES = 90; const GENERATION_PROGRESS_RING_BOTTOM_DEGREES = 90; // 中文注释:SVG 圆从 3 点钟方向起笔;起点放在 135deg,可让 90deg 开口居中落在正下方。 const GENERATION_PROGRESS_RING_START_DEGREES = GENERATION_PROGRESS_RING_BOTTOM_DEGREES + GENERATION_PROGRESS_RING_GAP_DEGREES / 2; const GENERATION_PROGRESS_RING_FILL_START_DEGREES = GENERATION_PROGRESS_RING_START_DEGREES; const GENERATION_PROGRESS_RING_SWEEP_DEGREES = 360 - GENERATION_PROGRESS_RING_GAP_DEGREES; const GENERATION_PROGRESS_RING_VIEWBOX = 400; const GENERATION_PROGRESS_RING_CENTER = GENERATION_PROGRESS_RING_VIEWBOX / 2; const GENERATION_PROGRESS_RING_RADIUS = 166; const GENERATION_PROGRESS_RING_STROKE_WIDTH = 18; const GENERATION_PROGRESS_RING_SWEEP_RATIO = GENERATION_PROGRESS_RING_SWEEP_DEGREES / 360; type GenerationProgressHeroProps = { title: string; phaseLabel: string; progressValue: number; estimatedWaitText: string; elapsedText: string; }; type GenerationCurrentStepCardProps = { label: string; statusLabel: string; progressValue: number; }; function clampGenerationProgress(value: number) { return Math.max(0, Math.min(100, Math.round(value))); } function buildGenerationRingMetrics(progressValue: number) { const circumference = 2 * Math.PI * GENERATION_PROGRESS_RING_RADIUS; const sweepLength = circumference * GENERATION_PROGRESS_RING_SWEEP_RATIO; const progressLength = sweepLength * (progressValue / 100); return { circumference, progressLength, sweepLength, }; } export function GenerationPageBackdrop() { const videoRef = useRef(null); useEffect(() => { const video = videoRef.current; if (!video) { return undefined; } video.defaultMuted = true; video.muted = true; video.volume = 0; const isJsdom = window.navigator.userAgent.toLowerCase().includes('jsdom'); const tryPlay = () => { if (isJsdom) { return; } try { const playPromise = video.play(); if (playPromise && typeof playPromise.then === 'function') { void playPromise.catch(() => {}); } } catch { // 中文注释:测试环境和某些内核可能同步拒绝 play;失败时保留静音背景层,不阻断页面渲染。 } }; tryPlay(); video.addEventListener('loadeddata', tryPlay); video.addEventListener('canplay', tryPlay); video.addEventListener('playing', tryPlay); window.addEventListener('focus', tryPlay); document.addEventListener('visibilitychange', tryPlay); return () => { video.removeEventListener('loadeddata', tryPlay); video.removeEventListener('canplay', tryPlay); video.removeEventListener('playing', tryPlay); window.removeEventListener('focus', tryPlay); document.removeEventListener('visibilitychange', tryPlay); }; }, []); return (
); } export function GenerationProgressHero({ title, phaseLabel, progressValue, estimatedWaitText, elapsedText, }: GenerationProgressHeroProps) { const safeProgress = clampGenerationProgress(progressValue); const ringGradientId = useId().replace(/:/g, ''); const ringMetrics = buildGenerationRingMetrics(safeProgress); const ringDegrees = Math.round( (safeProgress / 100) * GENERATION_PROGRESS_RING_SWEEP_DEGREES, ); const ringTrackDasharray = `${ringMetrics.sweepLength.toFixed(2)} ${ringMetrics.circumference.toFixed(2)}`; const ringFillDasharray = `${ringMetrics.progressLength.toFixed(2)} ${ringMetrics.circumference.toFixed(2)}`; return (
{title} {phaseLabel ? ` ${phaseLabel}` : ''}
总进度
{safeProgress}%
预计等待
{estimatedWaitText}
已耗时
{elapsedText}
); } export function GenerationCurrentStepCard({ label, statusLabel, progressValue, }: GenerationCurrentStepCardProps) { const safeProgress = clampGenerationProgress(progressValue); const isActive = statusLabel === '进行中'; return (
当前步骤
{label}
{statusLabel} {safeProgress}%
{isActive ? (
); }