diff --git a/components/messages.tsx b/components/messages.tsx index 213fa5af9..53c43f945 100644 --- a/components/messages.tsx +++ b/components/messages.tsx @@ -648,7 +648,31 @@ function PureMessages({ isAutoMode && (isGeneratingImageFlag || hasGeneratingMarker || hasGenerationEnd || hasImageContent); - if (showVideo) return ; + if (showVideo) { + const videoProviderType = + modelType === "ruxa-video" + ? "ruxa-video" + : modelType === "fal-video" + ? "fal-video" + : modelType === "volcano-video" + ? "volcano-video" + : modelType === "qianwen-video" + ? "qianwen-video" + : currentModelProviderType === "doubao-video" + ? "volcano-video" + : currentModelProviderType; + const videoPhase = + imageGenerationPhase === "idle" ? "processing" : imageGenerationPhase; + + return ( + + ); + } if (shouldWaitForAgentReplyBeforeImagePlaceholder) { return ; } diff --git a/components/qianwen-video-loading.tsx b/components/qianwen-video-loading.tsx index 5bf69d980..fe07515f6 100644 --- a/components/qianwen-video-loading.tsx +++ b/components/qianwen-video-loading.tsx @@ -1,137 +1,208 @@ "use client"; import { memo } from "react"; +import { CircleDot } from "lucide-react"; import { SparklesIcon } from "./icons"; +type VideoGenerationPhase = "idle" | "queued" | "processing" | "finalizing" | "failed"; + +type VideoGenerationLoadingProps = { + providerType?: string; + modelName?: string; + phase?: VideoGenerationPhase; + elapsedSeconds?: number; +}; + +const PROVIDER_MODEL_LABEL: Record = { + "qianwen-video": "千问视频", + "fal-video": "FAL 视频", + "ruxa-video": "RUXA 视频", + "volcano-video": "火山视频", +}; + +const PHASE_HINT: Record = { + idle: "任务已创建,正在初始化视频参数。", + queued: "任务已进入队列,请稍候。", + processing: "模型正在生成视频,请耐心等待。", + finalizing: "视频已生成完成,正在同步到对话。", + failed: "视频生成失败,正在尝试故障迁移。", +}; + +const PROVIDER_GRADIENT_CLASS: Record = { + "qianwen-video": "from-blue-400 via-purple-400 to-pink-400", + "fal-video": "from-sky-400 via-cyan-400 to-indigo-400", + "ruxa-video": "from-violet-400 via-fuchsia-400 to-pink-400", + "volcano-video": "from-orange-400 via-amber-400 to-yellow-400", +}; + /** * 千问视频生成加载占位符组件 * 显示一个带有一闪一闪头像效果的动态卡片 */ -export const QianwenVideoLoading = memo(() => { - return ( -
-
- {/* 头像 - 和图片生成一样的一闪一闪效果 */} -
-
-
-
-
- -
-
-
+export const QianwenVideoLoading = memo( + ({ + providerType, + modelName, + phase = "processing", + elapsedSeconds = 0, + }: VideoGenerationLoadingProps) => { + const normalizedProviderType = + providerType === "doubao-video" ? "volcano-video" : providerType || "qianwen-video"; + const resolvedModelName = + modelName?.trim() || + PROVIDER_MODEL_LABEL[normalizedProviderType] || + "AI 视频模型"; + const elapsedLabel = elapsedSeconds > 0 ? `已等待 ${elapsedSeconds}s` : "预计 60~180 秒"; + const phaseHint = PHASE_HINT[phase]; + const isFailed = phase === "failed"; + const palette = isFailed + ? { + ping: "bg-red-500/20", + pulse: "bg-red-500/10", + ring: "ring-red-500/30", + spinner: "text-red-500", + dot: "bg-red-500", + } + : { + ping: "bg-blue-500/20", + pulse: "bg-blue-500/10", + ring: "ring-blue-500/30", + spinner: "text-blue-500", + dot: "bg-blue-500", + }; + const gradientClass = + PROVIDER_GRADIENT_CLASS[normalizedProviderType] || PROVIDER_GRADIENT_CLASS["qianwen-video"]; -
- {/* 文本提示 */} -
- - 正在生成视频 - - - - - - -
- - {/* 视频占位符 - 170x200 像素,带动态效果 */} -
- {/* 底层渐变背景 */} -
- - {/* 动态模糊叠加层 1 */} -
- - {/* 动态模糊叠加层 2 */} -
- - {/* 噪点纹理效果 */} -
- - {/* 模糊叠加 */} -
- - {/* 中心图标 */} -
-
- {/* 外圈光晕 */} -
- {/* 视频图标 */} - - - + return ( +
+
+ {/* 头像 - 和图片生成一样的一闪一闪效果 */} +
+
+
+
+
+
- - {/* 流光效果 */} -
- - {/* 提示文字 */} - - ⏱️ 视频生成需要 30-60 秒,请耐心等待... - + +
+
+ + {resolvedModelName} +
+ + {/* 文本提示 */} +
+ + 正在生成视频 + + + + + + +
+ + {/* 视频占位符 - 170x200 像素,带动态效果 */} +
+ {/* 底层渐变背景 */} +
+ + {/* 动态模糊叠加层 1 */} +
+ + {/* 动态模糊叠加层 2 */} +
+ + {/* 噪点纹理效果 */} +
+ + {/* 模糊叠加 */} +
+ + {/* 中心图标 */} +
+
+ {/* 外圈光晕 */} +
+ {/* 视频图标 */} + + + +
+
+ + {/* 流光效果 */} +
+
+ + {/* 提示文字 */} +
+

{phaseHint}

+

{elapsedLabel}

+
+
+ + {/* 流光动画样式 */} +
- - {/* 流光动画样式 */} - -
- ); -}); + ); + } +); QianwenVideoLoading.displayName = "QianwenVideoLoading"; -