新增画布三模式弹窗并优化模式样式
This commit is contained in:
parent
03a424b26d
commit
fd707e9a25
@ -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<VideoGenerationParams | null>(null);
|
||||
const [videoSlotUploading, setVideoSlotUploading] = useState<number | null>(null);
|
||||
const [lastImageModelId, setLastImageModelId] = useState<string>("auto");
|
||||
const [lastImageModelId, setLastImageModelId] = useState<string | null>(null);
|
||||
const [lastVideoModelId, setLastVideoModelId] = useState<string | null>(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
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
onClick={handleToggleGenerateMode}
|
||||
className={cn(
|
||||
"h-8 cursor-pointer rounded-full px-3 text-sm font-medium",
|
||||
isVideoModel
|
||||
? "border border-blue-500 bg-blue-50 text-blue-600 hover:bg-blue-100"
|
||||
: "border border-purple-500 bg-purple-50 text-purple-700 hover:bg-purple-100"
|
||||
)}
|
||||
>
|
||||
{isVideoModel ? (
|
||||
<Video className="mr-1 h-4 w-4" />
|
||||
) : (
|
||||
<ImagePlus className="mr-1 h-4 w-4" />
|
||||
)}
|
||||
{isVideoModel ? "视频" : "生图"}
|
||||
</Button>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
className={cn(
|
||||
"h-8 cursor-pointer rounded-full px-3 text-sm font-medium",
|
||||
activeComposerMode === "video"
|
||||
? "border border-blue-500 bg-blue-50 text-blue-600 hover:bg-blue-100"
|
||||
: activeComposerMode === "image"
|
||||
? "border border-purple-500 bg-purple-50 text-purple-700 hover:bg-purple-100"
|
||||
: "border border-zinc-300 bg-zinc-50 text-zinc-700 hover:bg-zinc-100"
|
||||
)}
|
||||
>
|
||||
{activeComposerMode === "video" ? (
|
||||
<Video className="mr-1 h-4 w-4" />
|
||||
) : activeComposerMode === "image" ? (
|
||||
<ImagePlus className="mr-1 h-4 w-4" />
|
||||
) : (
|
||||
<Sparkles className="mr-1 h-4 w-4" />
|
||||
)}
|
||||
{activeModeLabel}
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
align="start"
|
||||
side="top"
|
||||
sideOffset={10}
|
||||
className="w-[220px] rounded-2xl border border-[#d6d9df] bg-[#f4f5f7] p-2 shadow-[0_10px_24px_rgba(15,23,42,0.14)]"
|
||||
>
|
||||
<DropdownMenuItem
|
||||
onClick={() => 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"
|
||||
)}
|
||||
>
|
||||
<Sparkles className="h-5 w-5" />
|
||||
<span className="flex-1">Agent</span>
|
||||
{activeComposerMode === "agent" ? <Check className="h-5 w-5 text-[#3b82f6]" /> : null}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => 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"
|
||||
)}
|
||||
>
|
||||
<ImagePlus className="h-5 w-5" />
|
||||
<span className="flex-1">图像生成器</span>
|
||||
{activeComposerMode === "image" ? <Check className="h-5 w-5 text-[#3b82f6]" /> : null}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => 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"
|
||||
)}
|
||||
>
|
||||
<Video className="h-5 w-5" />
|
||||
<span className="flex-1">视频生成器</span>
|
||||
{activeComposerMode === "video" ? <Check className="h-5 w-5 text-[#3b82f6]" /> : null}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
||||
{(!isVideoModel || showVideoAttachmentButton) && (
|
||||
<Button
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user