"use client"; import { useMemo, useState } from "react"; import useSWR from "swr"; import { Loader2, Plus, Sparkles } from "lucide-react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { ScrollArea } from "@/components/ui/scroll-area"; import { agentService } from "@/services/agents"; import type { AgentPreset } from "@/types/agent"; import { AgentCategoryTabs } from "@/components/agents/AgentCategoryTabs"; import { AgentPresetCard } from "@/components/agents/AgentPresetCard"; import { AgentPresetDialog } from "@/components/agents/AgentPresetDialog"; interface AgentPresetPanelProps { selectedPresetId?: string | null; onSelectPreset: (presetId: string | null) => void; } export function AgentPresetPanel({ selectedPresetId, onSelectPreset, }: AgentPresetPanelProps) { const [keyword, setKeyword] = useState(""); const [selectedCategoryId, setSelectedCategoryId] = useState(null); const [showCreateDialog, setShowCreateDialog] = useState(false); const [editingPreset, setEditingPreset] = useState(null); const { data, isLoading, mutate } = useSWR( ["agent-presets", selectedCategoryId], async () => { const result = await agentService.getPresets({ scope: "all", sort: "latest", }); if (!result.success || !result.data) { throw new Error(result.error || "获取智能体失败"); } return result.data; } ); const publicCategories = useMemo( () => (data?.categories || []).filter((category) => category.scope !== "private"), [data?.categories] ); const filteredPublicPresets = useMemo(() => { return (data?.public || []).filter((preset) => { const matchedCategory = !selectedCategoryId || preset.category?.id === selectedCategoryId; const matchedKeyword = !keyword.trim() || preset.name.toLowerCase().includes(keyword.trim().toLowerCase()) || String(preset.category?.name || "") .toLowerCase() .includes(keyword.trim().toLowerCase()); return matchedCategory && matchedKeyword; }); }, [data?.public, keyword, selectedCategoryId]); const filteredPrivatePresets = useMemo(() => { return (data?.private || []).filter((preset) => { const matchedKeyword = !keyword.trim() || preset.name.toLowerCase().includes(keyword.trim().toLowerCase()) || String(preset.category?.name || "") .toLowerCase() .includes(keyword.trim().toLowerCase()); return matchedKeyword; }); }, [data?.private, keyword]); 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(); }; return ( <>
智能体
选择智能体后,右侧对话框会按其要求显示上传槽位
setKeyword(event.target.value)} />
公共智能体
{isLoading ? (
) : filteredPublicPresets.length > 0 ? (
{filteredPublicPresets.map((preset) => ( ))}
) : (
暂无公共智能体
)}
我的智能体
{isLoading ? null : filteredPrivatePresets.length > 0 ? (
{filteredPrivatePresets.map((preset) => ( ))}
) : (
还没有私有智能体,先新建一个吧
)}
{ if (!nextOpen) { setShowCreateDialog(false); setEditingPreset(null); return; } setShowCreateDialog(true); }} categories={data?.categories || []} initialPreset={editingPreset} onSaved={async (preset) => { await mutate(); onSelectPreset(preset.id); setEditingPreset(null); setShowCreateDialog(false); }} /> ); }