"use client"; import { useEffect, useMemo, useState } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { useSession } from "next-auth/react"; import { Loader2, Plus, Search, Sparkles, Grid3X3, Wand2, LayoutGrid, FolderOpen, Menu } from "lucide-react"; import useSWR from "swr"; import { toast } from "sonner"; import { cn } from "@/lib/utils"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { agentService } from "@/services/agents"; import type { AgentPreset, AgentPresetLibraryCategoryNode, AgentPresetLibraryConfig } from "@/types/agent"; import { normalizeAgentWorkflowShowcaseConfig } from "@/lib/agents/workflow-showcase-config"; import { AGENT_PRESET_LIBRARY_DEFAULT_SECTION_ID, AGENT_PRESET_LIBRARY_MY_WORKS_ID, getEnabledAgentPresetLibraryNodes, getEnabledAgentPresetLibrarySections, normalizeAgentPresetLibraryConfig, resolveAgentPresetCategoryPath, } from "@/lib/agents/preset-library-config"; import { AgentWorkflowShowcaseBanner } from "./AgentWorkflowShowcaseBanner"; import { AgentPresetCardDark } from "./AgentPresetCardDark"; import { AgentPresetDialog } from "./AgentPresetDialog"; interface AgentPresetPanelDarkV2Props { selectedPresetId?: string | null; onSelectPreset: (presetId: string | null) => void; onCreateClick?: () => void; embedded?: boolean; embeddedEntityLabel?: string; } const AGENT_PRESET_LIBRARY_ALL_MODES_ID = "__all_modes__"; export function AgentPresetPanelDarkV2({ selectedPresetId, onSelectPreset, onCreateClick, embedded = false, embeddedEntityLabel = "风格化", }: AgentPresetPanelDarkV2Props) { const { data: session } = useSession(); const [activeTab, setActiveTab] = useState(AGENT_PRESET_LIBRARY_ALL_MODES_ID); const [keyword, setKeyword] = useState(""); const [showCreateDialog, setShowCreateDialog] = useState(false); const [editingPreset, setEditingPreset] = useState(null); const [selectedLevel1Id, setSelectedLevel1Id] = useState(""); const [selectedLevel2Id, setSelectedLevel2Id] = useState(""); const [selectedLevel3Id, setSelectedLevel3Id] = useState(""); const { data: siteSettingsData } = useSWR<{ agentWorkflowShowcaseConfig?: unknown; agentPresetLibraryConfig?: AgentPresetLibraryConfig; }>( "/api/site-settings?scope=agent-presets", (url: string) => fetch(url, { cache: "no-store" }).then((res) => res.json()) ); const { data, isLoading, mutate } = useSWR( ["agent-presets"], async () => { const result = await agentService.getPresets({ scope: "all", sort: "latest", }); if (!result.success || !result.data) { throw new Error(result.error || "获取风格化失败"); } return result.data; } ); const presetLibraryConfig = useMemo( () => normalizeAgentPresetLibraryConfig(siteSettingsData?.agentPresetLibraryConfig), [siteSettingsData?.agentPresetLibraryConfig] ); const presetLibrarySections = useMemo( () => getEnabledAgentPresetLibrarySections(presetLibraryConfig), [presetLibraryConfig] ); const navTabs = useMemo(() => { const allModesTab = { id: AGENT_PRESET_LIBRARY_ALL_MODES_ID, label: "所有模式", icon: Grid3X3, isMyWorks: false, isAllModes: true, }; const dynamicTabs = presetLibrarySections.map((section) => ({ id: section.id, label: section.label, icon: LayoutGrid, isMyWorks: false, isAllModes: false, })); const myWorksTab = { id: AGENT_PRESET_LIBRARY_MY_WORKS_ID, label: "我的模式", icon: FolderOpen, isMyWorks: true, isAllModes: false, }; if (dynamicTabs.length === 0) { return [allModesTab, myWorksTab]; } return [allModesTab, ...dynamicTabs, myWorksTab]; }, [presetLibrarySections]); const activeSection = useMemo( () => presetLibrarySections.find((section) => section.id === activeTab) || null, [activeTab, presetLibrarySections] ); const level1Options = useMemo( () => getEnabledAgentPresetLibraryNodes(activeSection?.categories || []), [activeSection] ); const selectedLevel1 = useMemo( () => level1Options.find((node) => node.id === selectedLevel1Id) || null, [level1Options, selectedLevel1Id] ); const level2Options = useMemo( () => getEnabledAgentPresetLibraryNodes(selectedLevel1?.children || []), [selectedLevel1] ); const selectedLevel2 = useMemo( () => level2Options.find((node) => node.id === selectedLevel2Id) || null, [level2Options, selectedLevel2Id] ); const level3Options = useMemo( () => getEnabledAgentPresetLibraryNodes(selectedLevel2?.children || []), [selectedLevel2] ); const isAllModesTab = activeTab === AGENT_PRESET_LIBRARY_ALL_MODES_ID; const isMyWorksTab = activeTab === AGENT_PRESET_LIBRARY_MY_WORKS_ID; const canManagePublicPresets = session?.user?.type === "admin" || session?.user?.type === "agent_admin"; useEffect(() => { if (navTabs.some((tab) => tab.id === activeTab)) return; const nextTabId = navTabs[0]?.id || AGENT_PRESET_LIBRARY_MY_WORKS_ID; setActiveTab(nextTabId); }, [activeTab, navTabs]); useEffect(() => { setSelectedLevel1Id(""); setSelectedLevel2Id(""); setSelectedLevel3Id(""); }, [activeTab]); useEffect(() => { if (!selectedLevel1Id || level1Options.some((node) => node.id === selectedLevel1Id)) return; setSelectedLevel1Id(""); setSelectedLevel2Id(""); setSelectedLevel3Id(""); }, [level1Options, selectedLevel1Id]); useEffect(() => { if (!selectedLevel2Id || level2Options.some((node) => node.id === selectedLevel2Id)) return; setSelectedLevel2Id(""); setSelectedLevel3Id(""); }, [level2Options, selectedLevel2Id]); useEffect(() => { if (!selectedLevel3Id || level3Options.some((node) => node.id === selectedLevel3Id)) return; setSelectedLevel3Id(""); }, [level3Options, selectedLevel3Id]); // 根据当前标签过滤数据 const filteredPresets = useMemo(() => { let presets: AgentPreset[] = isMyWorksTab ? data?.private || [] : data?.public || []; if (!isMyWorksTab && !isAllModesTab) { const fallbackSectionId = presetLibrarySections.find((section) => section.id === AGENT_PRESET_LIBRARY_DEFAULT_SECTION_ID)?.id || presetLibrarySections[0]?.id || null; presets = presets.filter((preset) => { const resolvedPath = resolveAgentPresetCategoryPath({ categoryPath: preset.categoryPath || null, config: presetLibraryConfig, }); const presetSectionId = resolvedPath.sectionId || fallbackSectionId; if (!presetSectionId || presetSectionId !== activeTab) { return false; } if (selectedLevel1Id && resolvedPath.level1Id !== selectedLevel1Id) { return false; } if (selectedLevel2Id && resolvedPath.level2Id !== selectedLevel2Id) { return false; } if (selectedLevel3Id && resolvedPath.level3Id !== selectedLevel3Id) { return false; } return true; }); } // 搜索过滤 if (keyword.trim()) { const normalizedKeyword = keyword.trim().toLowerCase(); presets = presets.filter((preset) => [preset.name, preset.description || "", preset.type] .filter(Boolean) .some((value) => String(value).toLowerCase().includes(normalizedKeyword)) ); } return presets; }, [ activeTab, data, isAllModesTab, isMyWorksTab, keyword, presetLibraryConfig, presetLibrarySections, selectedLevel1Id, selectedLevel2Id, selectedLevel3Id, ]); const handleForkPreset = async (preset: AgentPreset) => { const result = await agentService.forkPreset(preset.id, `${preset.name}(副本)`); if (!result.success || !result.data) { toast.error(result.error || "复制风格化失败"); return; } toast.success("已另存为我的风格化"); await mutate(); onSelectPreset(result.data.id); }; const handleDeletePreset = async (preset: AgentPreset) => { if (!window.confirm(`确定删除风格化「${preset.name}」吗?`)) { return; } const result = await agentService.deletePreset(preset.id); if (!result.success) { toast.error(result.error || "删除风格化失败"); return; } toast.success("风格化已删除"); if (selectedPresetId === preset.id) { onSelectPreset(null); } await mutate(); }; // 获取当前标签标题 const getCurrentTitle = () => { if (isAllModesTab) return "所有模式"; if (isMyWorksTab) return "我的模式"; return activeSection?.label || "生图模式"; }; // 嵌入模式只渲染卡片网格 if (embedded) { return ( <> {/* 搜索栏 */}
setKeyword(e.target.value)} className={cn( "h-9 rounded-lg border-0 pl-9 pr-3 text-sm", "bg-white/5 text-white placeholder:text-white/30", "focus:bg-white/10 focus:ring-1 focus:ring-white/20" )} />
{/* 导航标签(简化版) */}
{navTabs.map((tab) => ( ))}
{!isMyWorksTab && !isAllModesTab ? (
{ setSelectedLevel1Id(value); setSelectedLevel2Id(""); setSelectedLevel3Id(""); }} /> {selectedLevel1 ? ( { setSelectedLevel2Id(value); setSelectedLevel3Id(""); }} /> ) : null} {selectedLevel2 ? ( ) : null}
) : null} {/* 卡片网格 */}
{isLoading ? (
) : filteredPresets.length > 0 ? ( {filteredPresets.map((preset, index) => ( ))} ) : (
暂无{embeddedEntityLabel}
)}
{ if (!nextOpen) { setShowCreateDialog(false); setEditingPreset(null); return; } setShowCreateDialog(true); }} initialPreset={editingPreset} entityLabel={embeddedEntityLabel} theme="dark" onSaved={async (preset) => { await mutate(); onSelectPreset(preset.id); setEditingPreset(null); setShowCreateDialog(false); }} /> ); } return (
{/* 顶部固定导航栏 */}
{/* Logo & Site Name */}
AI创作平台
{/* 导航标签 */} {/* 右侧操作 */}
{/* Hero Section - 视频/功能展示区 */}
{ const target = document.getElementById("agent-preset-grid"); if (!target) return; target.scrollIntoView({ behavior: "smooth", block: "start" }); }} />
{/* 风格化卡片网格区域 */}
{/* 搜索栏 */}
setKeyword(e.target.value)} className={cn( "h-11 rounded-full border-0 pl-11 pr-4", "bg-white/5 text-white placeholder:text-white/30", "focus:bg-white/10 focus:ring-1 focus:ring-white/20", "transition-all duration-300" )} />
共 {filteredPresets.length} 个
{!isMyWorksTab && !isAllModesTab ? ( { setSelectedLevel1Id(value); setSelectedLevel2Id(""); setSelectedLevel3Id(""); }} /> {selectedLevel1 ? ( { setSelectedLevel2Id(value); setSelectedLevel3Id(""); }} /> ) : null} {selectedLevel2 ? ( ) : null} ) : null} {/* 区域标题 */}

{getCurrentTitle()}

{filteredPresets.length}
{/* 卡片网格 */}
{isLoading ? (
) : filteredPresets.length > 0 ? ( filteredPresets.map((preset, index) => ( )) ) : ( )}
{/* 页脚 */} { if (!nextOpen) { setShowCreateDialog(false); setEditingPreset(null); return; } setShowCreateDialog(true); }} initialPreset={editingPreset} entityLabel={embeddedEntityLabel} theme="dark" onSaved={async (preset) => { await mutate(); onSelectPreset(preset.id); setEditingPreset(null); setShowCreateDialog(false); }} />
); } function EmptyState({ message }: { message: string }) { return (

{message}

); } function CategoryFilterRow({ label, options, selectedId, onSelect, dark = true, }: { label: string; options: AgentPresetLibraryCategoryNode[]; selectedId?: string; onSelect: (value: string) => void; dark?: boolean; }) { if (!options.length) return null; return (
{label} {options.map((option) => ( ))}
); }