From fd707e9a253337218c74b1a712d7627792ef1bcf Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 15 Feb 2026 00:11:10 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=94=BB=E5=B8=83=E4=B8=89?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E5=BC=B9=E7=AA=97=E5=B9=B6=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/canvas/CanvasChatPanel.tsx | 175 +++++++++++++++++++------- 1 file changed, 129 insertions(+), 46 deletions(-) diff --git a/components/canvas/CanvasChatPanel.tsx b/components/canvas/CanvasChatPanel.tsx index 31829deeb..ee04e9df7 100644 --- a/components/canvas/CanvasChatPanel.tsx +++ b/components/canvas/CanvasChatPanel.tsx @@ -5,11 +5,17 @@ import { DefaultChatTransport } from "ai"; import { useEffect, useRef, useState, useCallback, useMemo } from "react"; import { createPortal } from "react-dom"; import useSWR from "swr"; -import { Send, Loader2, ImagePlus, Paperclip, X, User, Bot, Plus, History, LayoutGrid, Maximize2, RefreshCw, Globe, Box, ArrowUp, Video } from "lucide-react"; +import { Send, Loader2, ImagePlus, Paperclip, X, User, Bot, Plus, History, LayoutGrid, Maximize2, RefreshCw, Globe, Box, ArrowUp, Video, Sparkles, Check } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; import { Select, SelectContent, @@ -323,7 +329,7 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF }); const [videoParams, setVideoParams] = useState(null); const [videoSlotUploading, setVideoSlotUploading] = useState(null); - const [lastImageModelId, setLastImageModelId] = useState("auto"); + const [lastImageModelId, setLastImageModelId] = useState(null); const [lastVideoModelId, setLastVideoModelId] = useState(null); // 🆕 保存图片参数到 localStorage @@ -875,10 +881,28 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF return [] as string[]; }, [isVideoModel, videoParams]); + const firstImageModelId = useMemo( + () => + allModels.find( + (model) => model.id !== "auto" && !VIDEO_PROVIDER_TYPES.has(model.providerType) + )?.id || null, + [allModels] + ); const firstVideoModelId = useMemo( () => allModels.find((model) => VIDEO_PROVIDER_TYPES.has(model.providerType))?.id || null, [allModels] ); + const activeComposerMode = useMemo<"agent" | "image" | "video">(() => { + if (selectedModelId === "auto") return "agent"; + if (isVideoModel) return "video"; + return "image"; + }, [isVideoModel, selectedModelId]); + const activeModeLabel = + activeComposerMode === "video" + ? "视频" + : activeComposerMode === "image" + ? "生图" + : "Agent"; // 默认选择模型(根据系统设置) useEffect(() => { @@ -933,36 +957,48 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF setLastVideoModelId(currentModel.id); return; } - setLastImageModelId(currentModel.id); + if (currentModel.id !== "auto") { + setLastImageModelId(currentModel.id); + } }, [currentModel, isVideoModel]); - const handleToggleGenerateMode = useCallback(() => { - if (isVideoModel) { - const preferredImageModelId = + const handleSelectGenerationMode = useCallback( + (mode: "agent" | "image" | "video") => { + if (mode === "agent") { + setSelectedModelId("auto"); + return; + } + + if (mode === "image") { + const preferredImageModelId = + allModels.find( + (model) => + model.id === lastImageModelId && + model.id !== "auto" && + !VIDEO_PROVIDER_TYPES.has(model.providerType) + )?.id || firstImageModelId; + + if (!preferredImageModelId) { + toast.error("当前没有可用的图像模型"); + return; + } + setSelectedModelId(preferredImageModelId); + return; + } + + const preferredVideoModelId = allModels.find( - (model) => model.id === lastImageModelId && !VIDEO_PROVIDER_TYPES.has(model.providerType) - )?.id || "auto"; - setSelectedModelId(preferredImageModelId); - return; - } + (model) => model.id === lastVideoModelId && VIDEO_PROVIDER_TYPES.has(model.providerType) + )?.id || firstVideoModelId; - const preferredVideoModelId = - allModels.find( - (model) => model.id === lastVideoModelId && VIDEO_PROVIDER_TYPES.has(model.providerType) - )?.id || firstVideoModelId; - - if (!preferredVideoModelId) { - toast.error("当前没有可用的视频模型"); - return; - } - setSelectedModelId(preferredVideoModelId); - }, [ - allModels, - firstVideoModelId, - isVideoModel, - lastImageModelId, - lastVideoModelId, - ]); + if (!preferredVideoModelId) { + toast.error("当前没有可用的视频模型"); + return; + } + setSelectedModelId(preferredVideoModelId); + }, + [allModels, firstImageModelId, firstVideoModelId, lastImageModelId, lastVideoModelId] + ); useEffect(() => { if (videoFrameSlots.length === 0) return; @@ -2540,24 +2576,71 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF }} /> ))} - + + + + + + handleSelectGenerationMode("agent")} + className={cn( + "h-11 cursor-pointer rounded-xl px-3 text-[16px] font-medium text-[#2e3238]", + activeComposerMode === "agent" ? "bg-white/80" : "hover:bg-white/70" + )} + > + + Agent + {activeComposerMode === "agent" ? : null} + + handleSelectGenerationMode("image")} + className={cn( + "h-11 cursor-pointer rounded-xl px-3 text-[16px] font-medium text-[#2e3238]", + activeComposerMode === "image" ? "bg-white/80" : "hover:bg-white/70" + )} + > + + 图像生成器 + {activeComposerMode === "image" ? : null} + + handleSelectGenerationMode("video")} + className={cn( + "h-11 cursor-pointer rounded-xl px-3 text-[16px] font-medium text-[#2e3238]", + activeComposerMode === "video" ? "bg-white/80" : "hover:bg-white/70" + )} + > + + + {(!isVideoModel || showVideoAttachmentButton) && (