import { useState } from "react"; import { Alert, Card, Flex, Group, Loader, ScrollArea, Stack, Text, } from "@mantine/core"; import type { ReadResourceResult, Resource, ResourceTemplate, } from "@modelcontextprotocol/sdk/types.js"; import type { InspectorResourceSubscription } from "../../../../../../core/mcp/types.js"; import { ResourceControls } from "../../groups/ResourceControls/ResourceControls"; import { ResourcePreviewPanel } from "../../groups/ResourcePreviewPanel/ResourcePreviewPanel"; import { ResourceTemplatePanel } from "../../groups/ResourceTemplatePanel/ResourceTemplatePanel"; export interface ReadResourceState { status: "idle" | "pending" | "ok" | "error"; uri?: string; result?: ReadResourceResult; error?: string; lastUpdated?: Date; isSubscribed?: boolean; } export interface ResourcesScreenProps { resources: Resource[]; templates: ResourceTemplate[]; subscriptions: InspectorResourceSubscription[]; readState?: ReadResourceState; listChanged: boolean; onRefreshList: () => void; onReadResource: (uri: string) => void; onSubscribeResource: (uri: string) => void; onUnsubscribeResource: (uri: string) => void; } const ScreenLayout = Flex.withProps({ variant: "screen", h: "calc(100vh - var(--app-shell-header-height, 0px))", gap: "md", p: "xl", }); const Sidebar = Stack.withProps({ w: 340, flex: "0 0 auto", }); const SidebarCard = Card.withProps({ withBorder: true, padding: "lg", }); const DetailCard = Card.withProps({ withBorder: true, padding: "lg", }); const EmptyState = Text.withProps({ c: "dimmed", ta: "center", py: "xl", }); const SCROLL_MAX_HEIGHT = "calc(100vh - var(--app-shell-header-height, 0px) - var(--mantine-spacing-xl) * 2)"; export function ResourcesScreen({ resources, templates, subscriptions, readState, listChanged, onRefreshList, onReadResource, onSubscribeResource, onUnsubscribeResource, }: ResourcesScreenProps) { const [selectedResourceUri, setSelectedResourceUri] = useState< string | undefined >(undefined); const [selectedTemplateUri, setSelectedTemplateUri] = useState< string | undefined >(undefined); const selectedResource = selectedResourceUri ? resources.find((r) => r.uri === selectedResourceUri) : undefined; const selectedTemplate = selectedTemplateUri ? templates.find((t) => t.uriTemplate === selectedTemplateUri) : undefined; // For template-expanded URIs that don't appear in the resources list, // construct a synthetic Resource so the preview panel can render. const readResource: Resource | undefined = selectedResource ?? (readState?.uri && readState.uri === selectedResourceUri ? { name: readState.uri, uri: readState.uri } : undefined); function handleSelectResource(uri: string) { setSelectedTemplateUri(undefined); setSelectedResourceUri(uri); } function handleSelectTemplate(uriTemplate: string) { setSelectedResourceUri(undefined); setSelectedTemplateUri(uriTemplate); } function handleReadResource(uri: string) { setSelectedResourceUri(uri); onReadResource(uri); } function renderReadState() { if (!readState) return null; if (readState.status === "pending") { return ( Reading resource... ); } if (readState.status === "error") { return ( {readState.error ?? "Failed to read resource"} ); } if (readState.result && readResource) { return ( handleReadResource(readResource.uri)} onSubscribe={() => onSubscribeResource(readResource.uri)} onUnsubscribe={() => onUnsubscribeResource(readResource.uri)} /> ); } return null; } return ( {selectedTemplate ? ( {renderReadState() ?? ( Enter a URI and click Read to preview )} ) : selectedResource ? ( {renderReadState() ?? ( Click to read this resource )} ) : ( Select a resource to preview )} ); }