refine UI layout, theming, and component consistency across screens
- Add MCP logo (light/dark variants) to ConnectedLayout header - Replace Tabs with SegmentedControl in header, split into 1/3 sections - Wrap all screens in Container for consistent margins - Use Card instead of Paper for panel backgrounds across all screens - Add SubscribeButton atom, CopyButton for resource URI - Move annotations from ResourceListItem to ResourcePreviewPanel - Improve AnnotationBadge colors for dark mode visibility - Add Tools, Resources, and Prompts headers to list panels - Add ConnectedLayout stories for Tools, Resources, and Prompts tabs - Remove unused onCopy prop and copy button from PromptMessagesDisplay Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
08c42a2e62
commit
f674ce0c22
22 changed files with 357 additions and 157 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { Badge } from "@mantine/core";
|
||||
import { Badge, useComputedColorScheme } from "@mantine/core";
|
||||
|
||||
export type AnnotationVariant =
|
||||
| "audience"
|
||||
|
|
@ -17,11 +17,11 @@ const variantMap: Record<
|
|||
AnnotationVariant,
|
||||
{ color: string; variant: string }
|
||||
> = {
|
||||
audience: { color: "gray", variant: "outline" },
|
||||
readOnly: { color: "dark", variant: "filled" },
|
||||
destructive: { color: "red", variant: "outline" },
|
||||
longRun: { color: "yellow", variant: "outline" },
|
||||
priority: { color: "orange", variant: "light" },
|
||||
audience: { color: "blue", variant: "outline" },
|
||||
readOnly: { color: "green", variant: "filled" },
|
||||
destructive: { color: "red", variant: "filled" },
|
||||
longRun: { color: "yellow", variant: "filled" },
|
||||
priority: { color: "orange", variant: "filled" },
|
||||
default: { color: "gray", variant: "light" },
|
||||
};
|
||||
|
||||
|
|
@ -29,10 +29,13 @@ export function AnnotationBadge({
|
|||
label,
|
||||
variant = "default",
|
||||
}: AnnotationBadgeProps) {
|
||||
const colorScheme = useComputedColorScheme();
|
||||
const { color, variant: badgeVariant } = variantMap[variant];
|
||||
const textColor =
|
||||
(variant === "priority" || variant === "destructive") && colorScheme === "dark" ? "black" : undefined;
|
||||
|
||||
return (
|
||||
<Badge color={color} variant={badgeVariant}>
|
||||
<Badge color={color} variant={badgeVariant} c={textColor}>
|
||||
{label}
|
||||
</Badge>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { fn } from "storybook/test";
|
||||
import { SubscribeButton } from "./SubscribeButton";
|
||||
|
||||
const meta: Meta<typeof SubscribeButton> = {
|
||||
title: "Atoms/SubscribeButton",
|
||||
component: SubscribeButton,
|
||||
args: {
|
||||
onToggle: fn(),
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof SubscribeButton>;
|
||||
|
||||
export const Subscribed: Story = {
|
||||
args: {
|
||||
subscribed: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const Unsubscribed: Story = {
|
||||
args: {
|
||||
subscribed: false,
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
import { Button } from "@mantine/core";
|
||||
|
||||
export interface SubscribeButtonProps {
|
||||
subscribed: boolean;
|
||||
onToggle: () => void;
|
||||
}
|
||||
|
||||
export function SubscribeButton({
|
||||
subscribed,
|
||||
onToggle,
|
||||
}: SubscribeButtonProps) {
|
||||
return (
|
||||
<Button variant="outline" size="sm" onClick={onToggle}>
|
||||
{subscribed ? "Unsubscribe" : "Subscribe"}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
|
@ -2,6 +2,9 @@ import type { Meta, StoryObj } from "@storybook/react-vite";
|
|||
import { fn } from "storybook/test";
|
||||
import { Text } from "@mantine/core";
|
||||
import { ConnectedLayout } from "./ConnectedLayout";
|
||||
import { ToolsScreen } from "../../organisms/ToolsScreen/ToolsScreen";
|
||||
import { ResourcesScreen } from "../../organisms/ResourcesScreen/ResourcesScreen";
|
||||
import { PromptsScreen } from "../../organisms/PromptsScreen/PromptsScreen";
|
||||
|
||||
const allTabs = ["Tools", "Resources", "Prompts", "Logs", "Tasks", "History"];
|
||||
|
||||
|
|
@ -27,14 +30,153 @@ type Story = StoryObj<typeof ConnectedLayout>;
|
|||
export const ToolsActive: Story = {
|
||||
args: {
|
||||
activeTab: "Tools",
|
||||
children: <Text>Tools screen content</Text>,
|
||||
children: (
|
||||
<ToolsScreen
|
||||
tools={[
|
||||
{ name: "send_message", title: "Send Message", selected: false, onClick: fn() },
|
||||
{ name: "create_record", title: "Create Record", selected: true, onClick: fn() },
|
||||
{ name: "delete_records", selected: false, onClick: fn() },
|
||||
{ name: "list_users", selected: false, onClick: fn() },
|
||||
{ name: "batch_process", selected: false, onClick: fn() },
|
||||
]}
|
||||
selectedTool={{
|
||||
name: "create_record",
|
||||
title: "Create Record",
|
||||
description: "Creates a new record with the given parameters",
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
title: { type: "string", description: "Record title" },
|
||||
count: { type: "number", description: "Number of items" },
|
||||
enabled: { type: "boolean", description: "Whether the record is active" },
|
||||
},
|
||||
required: ["title"],
|
||||
},
|
||||
formValues: {},
|
||||
isExecuting: false,
|
||||
onFormChange: fn(),
|
||||
onExecute: fn(),
|
||||
onCancel: fn(),
|
||||
}}
|
||||
result={{
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify(
|
||||
{ id: 42, title: "New Record", count: 5, enabled: true, createdAt: "2026-03-17T12:00:00Z" },
|
||||
null,
|
||||
2,
|
||||
),
|
||||
},
|
||||
],
|
||||
onClear: fn(),
|
||||
}}
|
||||
searchText=""
|
||||
listChanged={false}
|
||||
onSearchChange={fn()}
|
||||
onRefreshList={fn()}
|
||||
onSelectTool={fn()}
|
||||
/>
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
export const ResourcesActive: Story = {
|
||||
args: {
|
||||
activeTab: "Resources",
|
||||
children: <Text>Resources screen content</Text>,
|
||||
children: (
|
||||
<ResourcesScreen
|
||||
resources={[
|
||||
{
|
||||
name: "config.json",
|
||||
uri: "file:///config.json",
|
||||
annotations: { audience: "developer", priority: 0.8 },
|
||||
selected: true,
|
||||
onClick: fn(),
|
||||
},
|
||||
{
|
||||
name: "README.md",
|
||||
uri: "file:///README.md",
|
||||
selected: false,
|
||||
onClick: fn(),
|
||||
},
|
||||
{
|
||||
name: "schema.sql",
|
||||
uri: "file:///schema.sql",
|
||||
annotations: { priority: 0.5 },
|
||||
selected: false,
|
||||
onClick: fn(),
|
||||
},
|
||||
]}
|
||||
templates={[
|
||||
{
|
||||
template: "file:///users/{userId}/profile",
|
||||
variables: { userId: "" },
|
||||
onVariableChange: fn(),
|
||||
onSubmit: fn(),
|
||||
},
|
||||
]}
|
||||
subscriptions={[
|
||||
{ name: "config.json", lastUpdated: "2026-03-17T10:30:00Z" },
|
||||
]}
|
||||
selectedResource={{
|
||||
uri: "file:///config.json",
|
||||
mimeType: "application/json",
|
||||
annotations: { audience: "developer", priority: 0.8 },
|
||||
content: JSON.stringify(
|
||||
{ name: "my-project", version: "1.0.0", settings: { debug: true, logLevel: "info" } },
|
||||
null,
|
||||
2,
|
||||
),
|
||||
lastUpdated: "2026-03-17T10:30:00Z",
|
||||
isSubscribed: true,
|
||||
onSubscribe: fn(),
|
||||
onUnsubscribe: fn(),
|
||||
}}
|
||||
listChanged={false}
|
||||
searchText=""
|
||||
onSearchChange={fn()}
|
||||
onRefreshList={fn()}
|
||||
onSelectResource={fn()}
|
||||
/>
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
export const PromptsActive: Story = {
|
||||
args: {
|
||||
activeTab: "Prompts",
|
||||
children: (
|
||||
<PromptsScreen
|
||||
promptForm={{
|
||||
prompts: [
|
||||
{ name: "summarize", description: "Summarize a document" },
|
||||
{ name: "translate", description: "Translate text to another language" },
|
||||
{ name: "code-review", description: "Review code for issues" },
|
||||
],
|
||||
selectedPrompt: "translate",
|
||||
arguments: [
|
||||
{ name: "text", required: true, description: "The text to translate" },
|
||||
{ name: "targetLanguage", required: true, description: "Target language code" },
|
||||
],
|
||||
argumentValues: {
|
||||
text: "Hello, how are you?",
|
||||
targetLanguage: "es",
|
||||
},
|
||||
onSelectPrompt: fn(),
|
||||
onArgumentChange: fn(),
|
||||
onGetPrompt: fn(),
|
||||
}}
|
||||
messages={{
|
||||
messages: [
|
||||
{ role: "user", content: 'Translate the following text to Spanish: "Hello, how are you?"' },
|
||||
{ role: "assistant", content: "Hola, como estas?" },
|
||||
],
|
||||
}}
|
||||
listChanged={false}
|
||||
onRefreshList={fn()}
|
||||
/>
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -47,7 +189,7 @@ export const LimitedTabs: Story = {
|
|||
|
||||
export const LongServerName: Story = {
|
||||
args: {
|
||||
serverName: "my-very-long-server-name-that-might-overflow",
|
||||
serverName: "my-very-long-server-name-that-might-overflow-is",
|
||||
children: <Text>Tools screen content</Text>,
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import type { ReactNode } from "react";
|
||||
import { ActionIcon, AppShell, Button, Group, Tabs, Text } from "@mantine/core";
|
||||
import { ActionIcon, AppShell, Button, Group, Image, SegmentedControl, Text } from "@mantine/core";
|
||||
import { useComputedColorScheme } from "@mantine/core";
|
||||
import { StatusIndicator } from "../../atoms/StatusIndicator/StatusIndicator";
|
||||
import mcpLogo from "../../../theme/assets/MCP.svg";
|
||||
import mcpLogoDark from "../../../theme/assets/MCP-dark.svg";
|
||||
|
||||
export interface ConnectedLayoutProps {
|
||||
serverName: string;
|
||||
|
|
@ -18,6 +20,8 @@ export interface ConnectedLayoutProps {
|
|||
const ServerName = Text.withProps({
|
||||
fw: 600,
|
||||
size: "lg",
|
||||
truncate: "end",
|
||||
maw: "calc(100% - 40px)",
|
||||
});
|
||||
|
||||
export function ConnectedLayout({
|
||||
|
|
@ -36,35 +40,23 @@ export function ConnectedLayout({
|
|||
return (
|
||||
<AppShell header={{ height: 60 }} padding="md">
|
||||
<AppShell.Header>
|
||||
<Group h="100%" px="md" justify="space-between" wrap="nowrap">
|
||||
<Group gap="md" wrap="nowrap">
|
||||
<Group h="100%" px="md" wrap="nowrap" gap={0}>
|
||||
<Group gap="md" wrap="nowrap" w="33.33%" miw={0}>
|
||||
<Image src={colorScheme === "dark" ? mcpLogoDark : mcpLogo} alt="MCP" w={28} h={28} fit="contain" />
|
||||
<ServerName>{serverName}</ServerName>
|
||||
<StatusIndicator status={status} latencyMs={latencyMs} />
|
||||
</Group>
|
||||
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
onChange={(value) => value && onTabChange(value)}
|
||||
variant="default"
|
||||
>
|
||||
<Tabs.List>
|
||||
{availableTabs.map((tab) => (
|
||||
<Tabs.Tab key={tab} value={tab}>
|
||||
{tab}
|
||||
</Tabs.Tab>
|
||||
))}
|
||||
</Tabs.List>
|
||||
</Tabs>
|
||||
<Group w="33.33%" justify="center">
|
||||
<SegmentedControl
|
||||
value={activeTab}
|
||||
onChange={onTabChange}
|
||||
data={availableTabs}
|
||||
size="sm"
|
||||
/>
|
||||
</Group>
|
||||
|
||||
<Group gap="sm">
|
||||
<ActionIcon
|
||||
variant="default"
|
||||
size="lg"
|
||||
aria-label="Toggle color scheme"
|
||||
onClick={onToggleTheme}
|
||||
>
|
||||
{colorScheme === "dark" ? "\u2600" : "\u263E"}
|
||||
</ActionIcon>
|
||||
<Group gap="sm" w="33.33%" justify="flex-end">
|
||||
<StatusIndicator status={status} latencyMs={latencyMs} />
|
||||
<Button
|
||||
variant="outline"
|
||||
color="red"
|
||||
|
|
@ -73,6 +65,14 @@ export function ConnectedLayout({
|
|||
>
|
||||
Disconnect
|
||||
</Button>
|
||||
<ActionIcon
|
||||
variant="default"
|
||||
size="lg"
|
||||
aria-label="Toggle color scheme"
|
||||
onClick={onToggleTheme}
|
||||
>
|
||||
{colorScheme === "dark" ? "\u2600" : "\u263E"}
|
||||
</ActionIcon>
|
||||
</Group>
|
||||
</Group>
|
||||
</AppShell.Header>
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export function PromptArgumentsForm({
|
|||
|
||||
return (
|
||||
<Stack gap="md">
|
||||
<Title order={4}>Select Prompt</Title>
|
||||
<Title order={4}>Prompts</Title>
|
||||
<Select
|
||||
data={prompts.map((p) => ({ value: p.name, label: p.name }))}
|
||||
value={selectedPrompt ?? null}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,7 @@ import { PromptMessagesDisplay } from "./PromptMessagesDisplay";
|
|||
const meta: Meta<typeof PromptMessagesDisplay> = {
|
||||
title: "Molecules/PromptMessagesDisplay",
|
||||
component: PromptMessagesDisplay,
|
||||
args: {
|
||||
onCopy: fn(),
|
||||
},
|
||||
args: {},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Button, Stack, Text, Title } from "@mantine/core";
|
||||
import { Stack, Text, Title } from "@mantine/core";
|
||||
import { MessageBubble } from "../../atoms/MessageBubble/MessageBubble";
|
||||
|
||||
export interface PromptMessage {
|
||||
|
|
@ -10,12 +10,10 @@ export interface PromptMessage {
|
|||
|
||||
export interface PromptMessagesDisplayProps {
|
||||
messages: PromptMessage[];
|
||||
onCopy: () => void;
|
||||
}
|
||||
|
||||
export function PromptMessagesDisplay({
|
||||
messages,
|
||||
onCopy,
|
||||
}: PromptMessagesDisplayProps) {
|
||||
return (
|
||||
<Stack gap="md">
|
||||
|
|
@ -34,9 +32,6 @@ export function PromptMessagesDisplay({
|
|||
/>
|
||||
))
|
||||
)}
|
||||
<Button variant="light" size="sm" onClick={onCopy}>
|
||||
Copy
|
||||
</Button>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { Group, Stack, Text, UnstyledButton } from "@mantine/core";
|
||||
import { AnnotationBadge } from "../../atoms/AnnotationBadge/AnnotationBadge";
|
||||
import { Text, UnstyledButton } from "@mantine/core";
|
||||
|
||||
export interface ResourceAnnotations {
|
||||
audience?: string;
|
||||
|
|
@ -14,15 +13,8 @@ export interface ResourceListItemProps {
|
|||
onClick: () => void;
|
||||
}
|
||||
|
||||
function priorityLabel(priority: number): string {
|
||||
if (priority >= 0.7) return "priority: high";
|
||||
if (priority >= 0.4) return "priority: medium";
|
||||
return "priority: low";
|
||||
}
|
||||
|
||||
export function ResourceListItem({
|
||||
name,
|
||||
annotations,
|
||||
selected,
|
||||
onClick,
|
||||
}: ResourceListItemProps) {
|
||||
|
|
@ -34,25 +26,7 @@ export function ResourceListItem({
|
|||
style={{ borderRadius: "var(--mantine-radius-md)" }}
|
||||
onClick={onClick}
|
||||
>
|
||||
<Stack gap="xs">
|
||||
<Text fw={500}>{name}</Text>
|
||||
{annotations && (
|
||||
<Group gap="xs">
|
||||
{annotations.audience && (
|
||||
<AnnotationBadge
|
||||
label={annotations.audience}
|
||||
variant="audience"
|
||||
/>
|
||||
)}
|
||||
{annotations.priority !== undefined && (
|
||||
<AnnotationBadge
|
||||
label={priorityLabel(annotations.priority)}
|
||||
variant="priority"
|
||||
/>
|
||||
)}
|
||||
</Group>
|
||||
)}
|
||||
</Stack>
|
||||
<Text fw={500}>{name}</Text>
|
||||
</UnstyledButton>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { Button, Group, Stack, Text, Title } from "@mantine/core";
|
||||
import { Group, Stack, Text, Title } from "@mantine/core";
|
||||
import { AnnotationBadge } from "../../atoms/AnnotationBadge/AnnotationBadge";
|
||||
import { ContentViewer } from "../../atoms/ContentViewer/ContentViewer";
|
||||
import { CopyButton } from "../../atoms/CopyButton/CopyButton";
|
||||
import { SubscribeButton } from "../../atoms/SubscribeButton/SubscribeButton";
|
||||
|
||||
export interface ResourcePreviewPanelProps {
|
||||
uri: string;
|
||||
|
|
@ -12,6 +15,12 @@ export interface ResourcePreviewPanelProps {
|
|||
onUnsubscribe: () => void;
|
||||
}
|
||||
|
||||
function priorityLabel(priority: number): string {
|
||||
if (priority >= 0.7) return "priority: high";
|
||||
if (priority >= 0.4) return "priority: medium";
|
||||
return "priority: low";
|
||||
}
|
||||
|
||||
function resolveContentType(mimeType: string): "json" | "image" | "text" {
|
||||
if (mimeType === "application/json") return "json";
|
||||
if (mimeType.startsWith("image/")) return "image";
|
||||
|
|
@ -30,51 +39,53 @@ export function ResourcePreviewPanel({
|
|||
}: ResourcePreviewPanelProps) {
|
||||
return (
|
||||
<Stack gap="md">
|
||||
<Title order={4}>Content Preview</Title>
|
||||
<Text size="sm">
|
||||
URI:{" "}
|
||||
<Text span c="blue">
|
||||
{uri}
|
||||
</Text>
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed">
|
||||
MIME: {mimeType}
|
||||
</Text>
|
||||
{annotations && (
|
||||
<Stack gap="xs">
|
||||
<Text size="sm">Annotations:</Text>
|
||||
{annotations.audience && (
|
||||
<Text size="sm" c="dimmed">
|
||||
Audience: {annotations.audience}
|
||||
</Text>
|
||||
)}
|
||||
{annotations.priority !== undefined && (
|
||||
<Text size="sm" c="dimmed">
|
||||
Priority: {annotations.priority}
|
||||
</Text>
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
<Group justify="space-between" wrap="nowrap">
|
||||
<Title order={4}>Resource</Title>
|
||||
<Group gap="xs" wrap="nowrap">
|
||||
<Text size="sm" c="blue" truncate="end">
|
||||
{uri}
|
||||
</Text>
|
||||
<CopyButton value={uri} />
|
||||
</Group>
|
||||
</Group>
|
||||
<ContentViewer
|
||||
type={resolveContentType(mimeType)}
|
||||
content={content}
|
||||
mimeType={mimeType}
|
||||
copyable
|
||||
/>
|
||||
<Group>
|
||||
<Button
|
||||
variant="light"
|
||||
size="sm"
|
||||
onClick={isSubscribed ? onUnsubscribe : onSubscribe}
|
||||
>
|
||||
{isSubscribed ? "Unsubscribe" : "Subscribe"}
|
||||
</Button>
|
||||
</Group>
|
||||
{lastUpdated && (
|
||||
<Text size="xs" c="dimmed" ta="right">
|
||||
Last updated: {lastUpdated}
|
||||
<Group justify="space-between" wrap="nowrap">
|
||||
{lastUpdated ? (
|
||||
<Text size="xs" c="dimmed">
|
||||
Last updated: {lastUpdated}
|
||||
</Text>
|
||||
) : (
|
||||
<span />
|
||||
)}
|
||||
<Text size="sm" c="dimmed">
|
||||
{mimeType}
|
||||
</Text>
|
||||
)}
|
||||
</Group>
|
||||
<Group justify="space-between">
|
||||
<Group gap="xs">
|
||||
{annotations?.audience && (
|
||||
<AnnotationBadge
|
||||
label={annotations.audience}
|
||||
variant="audience"
|
||||
/>
|
||||
)}
|
||||
{annotations?.priority !== undefined && (
|
||||
<AnnotationBadge
|
||||
label={priorityLabel(annotations.priority)}
|
||||
variant="priority"
|
||||
/>
|
||||
)}
|
||||
</Group>
|
||||
<SubscribeButton
|
||||
subscribed={isSubscribed}
|
||||
onToggle={isSubscribed ? onUnsubscribe : onSubscribe}
|
||||
/>
|
||||
</Group>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,6 @@ export function ResourceTemplateInput({
|
|||
<TextInput
|
||||
key={part.value}
|
||||
size="sm"
|
||||
label={part.value}
|
||||
placeholder={part.value}
|
||||
value={variables[part.value] ?? ""}
|
||||
onChange={(e) =>
|
||||
|
|
|
|||
|
|
@ -52,11 +52,6 @@ export function ToolDetailPanel({
|
|||
<Text fw={700} size="lg" truncate="end">
|
||||
{title ?? name}
|
||||
</Text>
|
||||
{title && (
|
||||
<Text size="sm" c="dimmed" truncate="end">
|
||||
{name}
|
||||
</Text>
|
||||
)}
|
||||
{hasAnnotations && (
|
||||
<Group gap="xs">
|
||||
{annotations.audience && (
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Container,
|
||||
Grid,
|
||||
Group,
|
||||
Paper,
|
||||
|
|
@ -30,6 +31,7 @@ export function LoggingScreen({
|
|||
onCopyAll,
|
||||
}: LoggingScreenProps) {
|
||||
return (
|
||||
<Container size="xl" py="xl">
|
||||
<Grid align="stretch">
|
||||
<Grid.Col span={3}>
|
||||
<Paper withBorder p="md" h="100%">
|
||||
|
|
@ -69,5 +71,6 @@ export function LoggingScreen({
|
|||
</Paper>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,7 +76,6 @@ export const WithResult: Story = {
|
|||
content: "Hola, como estas?",
|
||||
},
|
||||
],
|
||||
onCopy: fn(),
|
||||
},
|
||||
listChanged: false,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Grid, Paper, Stack, Text } from "@mantine/core";
|
||||
import { Card, Container, Grid, Stack, Text } from "@mantine/core";
|
||||
import { PromptArgumentsForm } from "../../molecules/PromptArgumentsForm/PromptArgumentsForm";
|
||||
import { PromptMessagesDisplay } from "../../molecules/PromptMessagesDisplay/PromptMessagesDisplay";
|
||||
import { ListChangedIndicator } from "../../atoms/ListChangedIndicator/ListChangedIndicator";
|
||||
|
|
@ -19,9 +19,10 @@ export function PromptsScreen({
|
|||
onRefreshList,
|
||||
}: PromptsScreenProps) {
|
||||
return (
|
||||
<Grid>
|
||||
<Container size="xl" py="xl">
|
||||
<Grid align="stretch">
|
||||
<Grid.Col span={5}>
|
||||
<Paper withBorder p="md">
|
||||
<Card withBorder padding="lg" h="100%">
|
||||
<Stack gap="md">
|
||||
<ListChangedIndicator
|
||||
visible={listChanged}
|
||||
|
|
@ -29,10 +30,10 @@ export function PromptsScreen({
|
|||
/>
|
||||
<PromptArgumentsForm {...promptForm} />
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Card>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={7}>
|
||||
<Paper withBorder p="md">
|
||||
<Card withBorder padding="lg" h="100%">
|
||||
{messages ? (
|
||||
<PromptMessagesDisplay {...messages} />
|
||||
) : (
|
||||
|
|
@ -40,8 +41,9 @@ export function PromptsScreen({
|
|||
Select a prompt and click Get Prompt to see messages
|
||||
</Text>
|
||||
)}
|
||||
</Paper>
|
||||
</Card>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,7 +75,6 @@ const selectedResourceProps: ResourcePreviewPanelProps = {
|
|||
),
|
||||
lastUpdated: "2026-03-17T10:30:00Z",
|
||||
isSubscribed: true,
|
||||
onCopy: fn(),
|
||||
onSubscribe: fn(),
|
||||
onUnsubscribe: fn(),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Accordion, Grid, Paper, Stack, Text, TextInput } from "@mantine/core";
|
||||
import { Accordion, Card, Container, Grid, Stack, Text, TextInput, Title } from "@mantine/core";
|
||||
import { ListChangedIndicator } from "../../atoms/ListChangedIndicator/ListChangedIndicator";
|
||||
import { ResourceListItem } from "../../molecules/ResourceListItem/ResourceListItem";
|
||||
import { ResourcePreviewPanel } from "../../molecules/ResourcePreviewPanel/ResourcePreviewPanel";
|
||||
|
|
@ -38,11 +38,19 @@ export function ResourcesScreen({
|
|||
r.name.toLowerCase().includes(searchText.toLowerCase()),
|
||||
);
|
||||
|
||||
const openSections = [
|
||||
...(filteredResources.length > 0 ? ["resources"] : []),
|
||||
...(templates.length > 0 ? ["templates"] : []),
|
||||
...(subscriptions.length > 0 ? ["subscriptions"] : []),
|
||||
];
|
||||
|
||||
return (
|
||||
<Grid>
|
||||
<Container size="xl" py="xl">
|
||||
<Grid align="stretch">
|
||||
<Grid.Col span={4}>
|
||||
<Paper withBorder p="md">
|
||||
<Card withBorder padding="lg" h="100%">
|
||||
<Stack gap="sm">
|
||||
<Title order={4}>Resources</Title>
|
||||
<ListChangedIndicator
|
||||
visible={listChanged}
|
||||
onRefresh={onRefreshList}
|
||||
|
|
@ -54,11 +62,11 @@ export function ResourcesScreen({
|
|||
/>
|
||||
<Accordion
|
||||
multiple
|
||||
defaultValue={["resources", "templates", "subscriptions"]}
|
||||
defaultValue={openSections}
|
||||
>
|
||||
<Accordion.Item value="resources">
|
||||
<Accordion.Control>
|
||||
Resources ({filteredResources.length})
|
||||
<Accordion.Control disabled={filteredResources.length === 0}>
|
||||
URIs ({filteredResources.length})
|
||||
</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<Stack gap="xs">
|
||||
|
|
@ -70,7 +78,7 @@ export function ResourcesScreen({
|
|||
</Accordion.Item>
|
||||
|
||||
<Accordion.Item value="templates">
|
||||
<Accordion.Control>
|
||||
<Accordion.Control disabled={templates.length === 0}>
|
||||
Templates ({templates.length})
|
||||
</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
|
|
@ -86,7 +94,7 @@ export function ResourcesScreen({
|
|||
</Accordion.Item>
|
||||
|
||||
<Accordion.Item value="subscriptions">
|
||||
<Accordion.Control>
|
||||
<Accordion.Control disabled={subscriptions.length === 0}>
|
||||
Subscriptions ({subscriptions.length})
|
||||
</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
|
|
@ -107,11 +115,11 @@ export function ResourcesScreen({
|
|||
</Accordion.Item>
|
||||
</Accordion>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Card>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={8}>
|
||||
<Paper withBorder p="md">
|
||||
<Card withBorder padding="lg">
|
||||
{selectedResource ? (
|
||||
<ResourcePreviewPanel {...selectedResource} />
|
||||
) : (
|
||||
|
|
@ -119,8 +127,9 @@ export function ResourcesScreen({
|
|||
Select a resource to preview
|
||||
</Text>
|
||||
)}
|
||||
</Paper>
|
||||
</Card>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import {
|
|||
SimpleGrid,
|
||||
Stack,
|
||||
Text,
|
||||
Title,
|
||||
} from "@mantine/core";
|
||||
import { ServerCard } from "../../molecules/ServerCard/ServerCard";
|
||||
import { AddServerMenu } from "../../molecules/AddServerMenu/AddServerMenu";
|
||||
|
|
@ -26,8 +25,7 @@ export function ServerListScreen({
|
|||
return (
|
||||
<Container size="xl" py="xl">
|
||||
<Stack gap="lg">
|
||||
<Group justify="space-between">
|
||||
<Title order={1}>MCP Inspector</Title>
|
||||
<Group justify="flex-end">
|
||||
<AddServerMenu
|
||||
onAddManually={onAddManually}
|
||||
onImportConfig={onImportConfig}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Button, Group, Stack, Text, Title } from "@mantine/core";
|
||||
import { Button, Container, Group, Stack, Text, Title } from "@mantine/core";
|
||||
import { TaskCard } from "../../molecules/TaskCard/TaskCard";
|
||||
import type { TaskCardProps } from "../../molecules/TaskCard/TaskCard";
|
||||
|
||||
|
|
@ -16,11 +16,11 @@ export function TasksScreen({
|
|||
onClearHistory,
|
||||
}: TasksScreenProps) {
|
||||
return (
|
||||
<Container size="xl" py="xl">
|
||||
<Stack gap="lg">
|
||||
<Group justify="space-between">
|
||||
<Title order={2}>Tasks</Title>
|
||||
<Button variant="light" onClick={onRefresh}>
|
||||
Refresh
|
||||
<Group justify="flex-end">
|
||||
<Button variant="dark" onClick={onRefresh}>
|
||||
Refresh Tasks
|
||||
</Button>
|
||||
</Group>
|
||||
|
||||
|
|
@ -37,9 +37,11 @@ export function TasksScreen({
|
|||
|
||||
<Group justify="space-between">
|
||||
<Title order={4}>Completed Tasks ({completedTasks.length})</Title>
|
||||
<Button variant="light" size="xs" onClick={onClearHistory}>
|
||||
Clear History
|
||||
</Button>
|
||||
{completedTasks.length > 0 && (
|
||||
<Button variant="light" size="xs" onClick={onClearHistory}>
|
||||
Clear History
|
||||
</Button>
|
||||
)}
|
||||
</Group>
|
||||
{completedTasks.length === 0 ? (
|
||||
<Text c="dimmed">No completed tasks</Text>
|
||||
|
|
@ -51,5 +53,6 @@ export function TasksScreen({
|
|||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Grid, Paper, Stack, Text, TextInput } from "@mantine/core";
|
||||
import { Card, Container, Grid, Stack, Text, TextInput, Title } from "@mantine/core";
|
||||
import { ListChangedIndicator } from "../../atoms/ListChangedIndicator/ListChangedIndicator";
|
||||
import { ToolListItem } from "../../molecules/ToolListItem/ToolListItem";
|
||||
import { ToolDetailPanel } from "../../molecules/ToolDetailPanel/ToolDetailPanel";
|
||||
|
|
@ -35,10 +35,12 @@ export function ToolsScreen({
|
|||
: tools;
|
||||
|
||||
return (
|
||||
<Grid>
|
||||
<Container size="xl" py="xl">
|
||||
<Grid align="stretch">
|
||||
<Grid.Col span={3}>
|
||||
<Paper withBorder p="md">
|
||||
<Card withBorder padding="lg" h="100%">
|
||||
<Stack gap="sm">
|
||||
<Title order={4}>Tools</Title>
|
||||
<ListChangedIndicator
|
||||
visible={listChanged}
|
||||
onRefresh={onRefreshList}
|
||||
|
|
@ -56,11 +58,11 @@ export function ToolsScreen({
|
|||
/>
|
||||
))}
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Card>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={5}>
|
||||
<Paper withBorder p="md" variant="contained">
|
||||
<Card withBorder padding="lg" h="100%">
|
||||
{selectedTool ? (
|
||||
<ToolDetailPanel {...selectedTool} />
|
||||
) : (
|
||||
|
|
@ -68,11 +70,11 @@ export function ToolsScreen({
|
|||
Select a tool to view details
|
||||
</Text>
|
||||
)}
|
||||
</Paper>
|
||||
</Card>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={4}>
|
||||
<Paper withBorder p="md">
|
||||
<Card withBorder padding="lg" h="100%">
|
||||
{result ? (
|
||||
<ResultPanel {...result} />
|
||||
) : (
|
||||
|
|
@ -80,8 +82,9 @@ export function ToolsScreen({
|
|||
Results will appear here
|
||||
</Text>
|
||||
)}
|
||||
</Paper>
|
||||
</Card>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue