89 lines
3.3 KiB
TypeScript
89 lines
3.3 KiB
TypeScript
"use client";
|
|
|
|
import { ImagePlus, MonitorSmartphone, Sparkles, Video } from "lucide-react";
|
|
import type { AgentSlotNodeData, CanvasNode } from "@/types/canvas";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
interface AgentSlotNodeProps {
|
|
node: CanvasNode;
|
|
isSelected: boolean;
|
|
}
|
|
|
|
function getSlotIcon(slotType: AgentSlotNodeData["slotType"]) {
|
|
if (slotType === "video") return Video;
|
|
if (slotType === "ui-screen") return MonitorSmartphone;
|
|
return ImagePlus;
|
|
}
|
|
|
|
function getSlotTypeLabel(slotType: AgentSlotNodeData["slotType"]) {
|
|
if (slotType === "video") return "视频槽位";
|
|
if (slotType === "ui-screen") return "UI 槽位";
|
|
return "图片槽位";
|
|
}
|
|
|
|
function getStatusLabel(status: AgentSlotNodeData["status"]) {
|
|
if (status === "generating") return "生成中";
|
|
if (status === "completed") return "已填充";
|
|
if (status === "failed") return "失败";
|
|
return "等待生成";
|
|
}
|
|
|
|
export function AgentSlotNode({ node, isSelected }: AgentSlotNodeProps) {
|
|
const data = node.data as AgentSlotNodeData;
|
|
const slotType = data.slotType || "image";
|
|
const SlotIcon = getSlotIcon(slotType);
|
|
const filledCount = Array.isArray(data.filledNodeIds)
|
|
? data.filledNodeIds.length
|
|
: 0;
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
"flex h-full w-full flex-col overflow-hidden rounded-none border-2 border-dashed bg-white/92 text-slate-700 shadow-[0_12px_28px_rgba(15,23,42,0.08)] transition-all dark:bg-zinc-950/92 dark:text-slate-100",
|
|
isSelected
|
|
? "border-[#5b76fe] ring-4 ring-[#5b76fe]/12"
|
|
: "border-[#b8c3d8] hover:border-[#8ea2d6] dark:border-zinc-700",
|
|
data.status === "generating" && "border-[#7c9cff] bg-[#f7f9ff]",
|
|
data.status === "failed" && "border-red-300 bg-red-50 text-red-900"
|
|
)}
|
|
data-agent-slot-node="true"
|
|
data-slot-type={slotType}
|
|
>
|
|
<div className="flex min-h-0 flex-1 flex-col items-center justify-center gap-3 px-5 py-4 text-center">
|
|
<div
|
|
className={cn(
|
|
"flex h-12 w-12 items-center justify-center rounded-full border border-[#dbe2ee] bg-white text-[#5b76fe] shadow-sm dark:border-zinc-700 dark:bg-zinc-900",
|
|
data.status === "generating" && "animate-pulse",
|
|
data.status === "failed" && "border-red-200 text-red-600"
|
|
)}
|
|
>
|
|
{data.status === "generating" ? (
|
|
<Sparkles className="h-5 w-5" />
|
|
) : (
|
|
<SlotIcon className="h-5 w-5" />
|
|
)}
|
|
</div>
|
|
<div className="min-w-0 space-y-1">
|
|
<div className="truncate font-semibold text-[15px] leading-5">
|
|
{data.label || getSlotTypeLabel(slotType)}
|
|
</div>
|
|
<div className="text-[12px] leading-5 text-slate-500 dark:text-slate-400">
|
|
{getStatusLabel(data.status)}
|
|
{filledCount > 0 ? ` · ${filledCount} 个结果` : ""}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center justify-between border-[#e6ebf5] border-t px-3 py-2 text-[11px] text-slate-500 dark:border-zinc-800 dark:text-slate-400">
|
|
<span className="truncate">{data.aspectRatio || "1:1"}</span>
|
|
<span className="truncate">
|
|
{data.fillPolicy === "append-version"
|
|
? "追加版本"
|
|
: data.fillPolicy === "replace-existing"
|
|
? "允许替换"
|
|
: "填充空槽"}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|