diff --git a/example/parsar/package.json b/example/parsar/package.json index f9ec82847..389ba0a3e 100644 --- a/example/parsar/package.json +++ b/example/parsar/package.json @@ -17,6 +17,7 @@ "@modelcontextprotocol/sdk": "^1.32.1", "@oac/agents-client": "workspace:*", "@radix-ui/react-dialog": "^1.1.18", + "@radix-ui/react-popover": "1.1.23", "@radix-ui/react-select": "2.3.2", "@radix-ui/react-slot": "^1.3.0", "@radix-ui/react-tooltip": "^1.2.11", diff --git a/example/parsar/pnpm-lock.yaml b/example/parsar/pnpm-lock.yaml index 72663b304..8ad34cc1e 100644 --- a/example/parsar/pnpm-lock.yaml +++ b/example/parsar/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: '@radix-ui/react-dialog': specifier: ^1.1.18 version: 1.1.23(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-popover': + specifier: 1.1.23 + version: 1.1.23(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@radix-ui/react-select': specifier: 2.3.2 version: 2.3.2(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) @@ -546,6 +549,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-popover@1.1.23': + resolution: {integrity: sha512-mw58MrBlyHWFisTOYignD0vf/3gdcgAR+9of1s9G/38CbFiUwH1nCDkc0AUM9IrXFgN5Ue8n45j9WCgyM1sbiQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-popper@1.3.2': resolution: {integrity: sha512-3QXNeMkdshed1MR3LNoiCirBywRFPkD8ETJa/HlPuLwSajaQixf2ro+isoDNJlGABg9ug41XuZpINZJIle4XWg==} peerDependencies: @@ -2832,6 +2848,29 @@ snapshots: optionalDependencies: '@types/react': 19.3.0 + '@radix-ui/react-popover@1.1.23(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-context': 1.2.2(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-focus-guards': 1.1.6(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-focus-scope': 1.1.16(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-popper': 1.3.7(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-slot': 1.3.3(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.3.0)(react@19.3.0) + aria-hidden: 1.2.6 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + react-remove-scroll: 2.7.2(@types/react@19.3.0)(react@19.3.0) + optionalDependencies: + '@types/react': 19.3.0 + '@types/react-dom': 19.3.0(@types/react@19.3.0) + '@radix-ui/react-popper@1.3.2(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': dependencies: '@floating-ui/react-dom': 2.1.9(react-dom@19.3.0(react@19.3.0))(react@19.3.0) diff --git a/example/parsar/src/Agents.tsx b/example/parsar/src/Agents.tsx index 21b3997ab..6db241f5f 100644 --- a/example/parsar/src/Agents.tsx +++ b/example/parsar/src/Agents.tsx @@ -1,5 +1,6 @@ import { tr } from "./lib/i18n"; import { useState } from "react"; +import * as Popover from "@radix-ui/react-popover"; import { Blocks, Plug, Plus, Search, Send, Server } from "lucide-react"; import { useAgents, @@ -33,7 +34,7 @@ export function Chip({ return ( @@ -140,12 +141,38 @@ function Part({ {label} -
{children}
+
{children}
); } -const shownSkills = 8; +// Shows the first item and folds the rest behind "+N", listed in full on click. +function Folded({ items }: { items: string[] }) { + if (!items.length) return {tr("无")}; + return ( + <> + {items[0]} + {items.length > 1 && ( + + + +{items.length - 1} + + + + {items.map((name) => ( + {name} + ))} + + + + )} + + ); +} function AgentCard({ agent, @@ -163,7 +190,6 @@ function AgentCard({ capabilities: string[]; }) { const disabled = agent.enabled === false; - const packages = [...(runtime?.packages?.npm || []), ...(runtime?.packages?.python || [])]; return (
@@ -201,36 +227,16 @@ function AgentCard({ {runtime ? ( - <> - {runtime.name} - {packages.map((name) => ( - {name} - ))} - + {runtime.name} ) : ( {tr("未选择环境")} )} - {skills.length ? ( - <> - {skills.slice(0, shownSkills).map((name) => ( - {name} - ))} - {skills.length > shownSkills && ( - +{skills.length - shownSkills} - )} - - ) : ( - {tr("无")} - )} + - {capabilities.length ? ( - capabilities.map((name) => {name}) - ) : ( - {tr("无")} - )} +
diff --git a/example/parsar/tests/workflow.spec.ts b/example/parsar/tests/workflow.spec.ts index 8410098f1..922a8ae5a 100644 --- a/example/parsar/tests/workflow.spec.ts +++ b/example/parsar/tests/workflow.spec.ts @@ -974,8 +974,11 @@ test("Agent engine options narrow to the selected Harness and reach new conversa await page.getByRole("button", { name: "会话详情" }).click(); await expect(page.getByRole("dialog")).toContainText("网页搜索实时搜索"); await page.goto("/#/agents"); - for (const part of ["未绑定", "开发沙箱", "code-review", "网页搜索", "文档服务"]) - await expect(page.getByRole("article")).toContainText(part); + const card = page.getByRole("article"); + for (const part of ["未绑定", "开发沙箱", "code-review", "网页搜索"]) + await expect(card).toContainText(part); + await card.getByRole("button", { name: /^\+\d+$/ }).click(); + await expect(page.getByRole("dialog")).toContainText("文档服务"); }); test("chat previews authorization QR and artifact images, downloads files and keeps unsafe images out", async ({ page }) => {