diff --git a/frontend-web/package.json b/frontend-web/package.json index f7eed944..ee266aaa 100644 --- a/frontend-web/package.json +++ b/frontend-web/package.json @@ -63,7 +63,6 @@ "codemirror": "^6.0.2", "date-fns": "^4.1.0", "dayjs": "^1.11.21", - "dnd-kit": "^0.0.2", "docx": "^9.5.1", "dotenv": "^17.2.3", "embla-carousel-react": "^8.6.0", diff --git a/frontend-web/src/components/workspace/agents/agent-card.tsx b/frontend-web/src/components/workspace/agents/agent-card.tsx index a3002755..39f24aad 100644 --- a/frontend-web/src/components/workspace/agents/agent-card.tsx +++ b/frontend-web/src/components/workspace/agents/agent-card.tsx @@ -2,6 +2,8 @@ import { BotIcon, + ChevronDownIcon, + ChevronUpIcon, CopyIcon, GlobeIcon, InfoIcon, @@ -85,6 +87,10 @@ export function AgentCard({ agent }: AgentCardProps) { agent.skills ?? [], ); const [detailSkillName, setDetailSkillName] = useState(null); + const [skillsExpanded, setSkillsExpanded] = useState(false); + + const agentSkills = agent.skills ?? []; + const showSkillsToggle = agentSkills.length >= 3; // The /api/agents list response intentionally omits `soul` (perf optimization // — SOUL.md is only read on demand). Fetch the detail row when the edit @@ -111,6 +117,10 @@ export function AgentCard({ agent }: AgentCardProps) { const canFavorite = !isOwned; const isFavorite = !!agent.is_favorite; + useEffect(() => { + setSkillsExpanded(false); + }, [agent.id, agentSkills.length]); + useEffect(() => { if (!editOpen) { return; @@ -382,11 +392,13 @@ export function AgentCard({ agent }: AgentCardProps) { )} - {agent.description && ( - - {desensitize(agent.description)} - - )} +
+ {agent.description ? ( + + {desensitize(agent.description)} + + ) : null} +
{(agent.tags?.length ?? 0) > 0 && ( @@ -396,36 +408,73 @@ export function AgentCard({ agent }: AgentCardProps) { )} {(agent.tool_groups?.length ?? agent.skills?.length ?? 0) > 0 && ( - -
- {agent.tool_groups?.map((group) => ( - + {(agent.tool_groups?.length ?? 0) > 0 && ( +
+ {agent.tool_groups?.map((group) => ( + + {desensitize(group)} + + ))} +
+ )} + {agentSkills.length > 0 && ( +
+ {showSkillsToggle && ( + + )} +
- {desensitize(group)} - - ))} - {agent.skills?.map((skill) => ( - { - e.stopPropagation(); - setDetailSkillName(skill); - }} - > - {desensitize(skill)} - - ))} -
+ {agentSkills.map((skill) => ( + { + e.stopPropagation(); + setDetailSkillName(skill); + }} + > + {desensitize(skill)} + + ))} +
+
+ )}
)} + {(canEdit || canDelete || isOwned || isAdmin || canFavorite) && (
{canFavorite && ( diff --git a/frontend-web/src/components/workspace/agents/agent-inspect-dialog.tsx b/frontend-web/src/components/workspace/agents/agent-inspect-dialog.tsx new file mode 100644 index 00000000..b616ed1a --- /dev/null +++ b/frontend-web/src/components/workspace/agents/agent-inspect-dialog.tsx @@ -0,0 +1,194 @@ +"use client"; + +import { InfoIcon } from "lucide-react"; +import { useMemo, useState } from "react"; + +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; +import { SkillDetailDialog } from "@/components/workspace/skills/skill-detail-dialog"; +import { useAgent } from "@/core/agents"; +import { useI18n } from "@/core/i18n/hooks"; +import { useSkills } from "@/core/skills/hooks"; +import { desensitize } from "@/lib/desensitize"; + +/** 与 `AgentCard` 编辑弹框同布局,仅查看、不可修改。 */ +export function AgentInspectDialog({ + agentId, + open, + onOpenChange, +}: { + agentId: string | null; + open: boolean; + onOpenChange: (open: boolean) => void; +}) { + const { t } = useI18n(); + const { agent, isLoading } = useAgent(open ? agentId : null); + const { skills, isLoading: isSkillsLoading } = useSkills(); + const [detailSkillName, setDetailSkillName] = useState(null); + + const enabledSkills = useMemo( + () => skills.filter((s) => s.enabled), + [skills], + ); + + const selectedSkills = useMemo( + () => + (agent?.skills ?? []).map((name) => { + const meta = enabledSkills.find((s) => s.name === name); + return { name, description: meta?.description }; + }), + [agent?.skills, enabledSkills], + ); + + return ( + <> + + + + + 查看智能体 + {agent ? `:${desensitize(agent.name)}` : ""} + + + 仅查看名称、描述、技能白名单、SOUL 与发布状态,不可修改。 + + + {isLoading && !agent ? ( +

+ {t.common.loading} +

+ ) : !agent ? ( +

+ 无法加载智能体信息 +

+ ) : ( +
+
+ + +
+
+ +