From f1d6fbd704d4cbe7d41e6e7d0a3358ce3edf252c Mon Sep 17 00:00:00 2001 From: Cliff Hall Date: Mon, 27 Apr 2026 17:23:32 -0400 Subject: [PATCH] feat(resources): render annotations on ResourceListItem (#1249) * feat(resources): render annotations on ResourceListItem (#1238) Half-finished Phase 5 spec target: ResourceListItem already used title ?? name but ignored resource.annotations entirely. Now renders audience and priority via the existing AnnotationBadge element, matching how ResourcePreviewPanel surfaces the same fields. Layout: a wrapping Group keeps the title + badges on a single line when the row is wide, and lets badges drop below when narrow. When neither annotation is present, the row renders the bare Text (no Group, no leaked spacing). Adds stories for audience-only, priority-only, both, and a ResourceTemplate with annotations. Co-Authored-By: Claude Opus 4.7 (1M context) * Address PR #1249 review feedback - Inline the audience/priority narrowing checks instead of routing through aliased booleans, matching ResourcePreviewPanel's pattern and avoiding reliance on TS aliased-condition narrowing for the inner badges. - Use a distinct resource name (settings.json) for WithAudience so the Storybook canvas reads more clearly across the four annotation stories. Co-Authored-By: Claude Opus 4.7 (1M context) --------- Co-authored-by: Claude Opus 4.7 (1M context) --- .../ResourceListItem.stories.tsx | 45 +++++++++++++++++++ .../ResourceListItem/ResourceListItem.tsx | 36 ++++++++++++++- 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/clients/web/src/components/groups/ResourceListItem/ResourceListItem.stories.tsx b/clients/web/src/components/groups/ResourceListItem/ResourceListItem.stories.tsx index f68dce0a..e1541e40 100644 --- a/clients/web/src/components/groups/ResourceListItem/ResourceListItem.stories.tsx +++ b/clients/web/src/components/groups/ResourceListItem/ResourceListItem.stories.tsx @@ -53,3 +53,48 @@ export const Template: Story = { selected: false, }, }; + +export const WithAudience: Story = { + args: { + resource: { + name: "settings.json", + uri: "file:///settings.json", + annotations: { audience: ["user"] }, + }, + selected: false, + }, +}; + +export const WithPriority: Story = { + args: { + resource: { + name: "schema.sql", + uri: "file:///schema.sql", + annotations: { priority: 0.8 }, + }, + selected: false, + }, +}; + +export const WithAudienceAndPriority: Story = { + args: { + resource: { + name: "config.json", + title: "Configuration File", + uri: "file:///config.json", + annotations: { audience: ["user", "assistant"], priority: 0.5 }, + }, + selected: false, + }, +}; + +export const TemplateWithAnnotations: Story = { + args: { + resource: { + name: "User Profile", + uriTemplate: "file:///users/{userId}/profile", + annotations: { audience: ["assistant"], priority: 0.2 }, + }, + selected: false, + }, +}; diff --git a/clients/web/src/components/groups/ResourceListItem/ResourceListItem.tsx b/clients/web/src/components/groups/ResourceListItem/ResourceListItem.tsx index 3dbdf99b..7059b5b6 100644 --- a/clients/web/src/components/groups/ResourceListItem/ResourceListItem.tsx +++ b/clients/web/src/components/groups/ResourceListItem/ResourceListItem.tsx @@ -1,8 +1,9 @@ -import { Text, UnstyledButton } from "@mantine/core"; +import { Group, Text, UnstyledButton } from "@mantine/core"; import type { Resource, ResourceTemplate, } from "@modelcontextprotocol/sdk/types.js"; +import { AnnotationBadge } from "../../elements/AnnotationBadge/AnnotationBadge"; export interface ResourceListItemProps { resource: Resource | ResourceTemplate; @@ -10,11 +11,28 @@ export interface ResourceListItemProps { onClick: () => void; } +const RowGroup = Group.withProps({ + gap: "xs", + wrap: "wrap", + justify: "space-between", +}); + +const BadgeGroup = Group.withProps({ + gap: "xs", + wrap: "wrap", +}); + export function ResourceListItem({ resource, selected, onClick, }: ResourceListItemProps) { + const audience = resource.annotations?.audience; + const priority = resource.annotations?.priority; + const showBadges = + (audience !== undefined && audience.length > 0) || priority !== undefined; + const label = {resource.title ?? resource.name}; + return ( - {resource.title ?? resource.name} + {showBadges ? ( + + {label} + + {audience !== undefined && audience.length > 0 && ( + + )} + {priority !== undefined && ( + + )} + + + ) : ( + label + )} ); }