Skip to content

Commit 20a2dbe

Browse files
authored
Merge pull request modelcontextprotocol#12 from modelcontextprotocol/ashwin/listpane
extract listpane
2 parents 4f26b5a + bc1bc0e commit 20a2dbe

File tree

5 files changed

+94
-91
lines changed

5 files changed

+94
-91
lines changed

client/src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import ResourcesTab, { Resource } from "./components/ResourcesTab";
1919
import NotificationsTab from "./components/NotificationsTab";
2020
import PromptsTab, { Prompt } from "./components/PromptsTab";
2121
import ToolsTab, { Tool as ToolType } from "./components/ToolsTab";
22-
import History from "./components/CommandHistory";
22+
import History from "./components/History";
2323

2424
const App = () => {
2525
const [socket, setSocket] = useState<WebSocket | null>(null);

client/src/components/ListPane.tsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Button } from "./ui/button";
2+
3+
type ListPaneProps<T> = {
4+
items: T[];
5+
listItems: () => void;
6+
setSelectedItem: (item: T) => void;
7+
renderItem: (item: T) => React.ReactNode;
8+
title: string;
9+
buttonText: string;
10+
};
11+
12+
const ListPane = <T extends object>({
13+
items,
14+
listItems,
15+
setSelectedItem,
16+
renderItem,
17+
title,
18+
buttonText,
19+
}: ListPaneProps<T>) => (
20+
<div className="bg-white rounded-lg shadow">
21+
<div className="p-4 border-b border-gray-200">
22+
<h3 className="font-semibold">{title}</h3>
23+
</div>
24+
<div className="p-4">
25+
<Button variant="outline" className="w-full mb-4" onClick={listItems}>
26+
{buttonText}
27+
</Button>
28+
<div className="space-y-2">
29+
{items.map((item, index) => (
30+
<div
31+
key={index}
32+
className="flex items-center p-2 rounded hover:bg-gray-50 cursor-pointer"
33+
onClick={() => setSelectedItem(item)}
34+
>
35+
{renderItem(item)}
36+
</div>
37+
))}
38+
</div>
39+
</div>
40+
</div>
41+
);
42+
43+
export default ListPane;

client/src/components/PromptsTab.tsx

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Input } from "@/components/ui/input";
66
import { Textarea } from "@/components/ui/textarea";
77
import { useState } from "react";
88
import { Label } from "@/components/ui/label";
9+
import ListPane from "./ListPane";
910

1011
export type Prompt = {
1112
name: string;
@@ -48,34 +49,22 @@ const PromptsTab = ({
4849

4950
return (
5051
<TabsContent value="prompts" className="grid grid-cols-2 gap-4">
51-
<div className="bg-white rounded-lg shadow">
52-
<div className="p-4 border-b border-gray-200">
53-
<h3 className="font-semibold">Prompts</h3>
54-
</div>
55-
<div className="p-4">
56-
<Button
57-
variant="outline"
58-
className="w-full mb-4"
59-
onClick={listPrompts}
60-
>
61-
List Prompts
62-
</Button>
63-
<div className="space-y-2">
64-
{prompts.map((prompt) => (
65-
<div
66-
key={prompt.name}
67-
className="flex items-center p-2 rounded hover:bg-gray-50 cursor-pointer"
68-
onClick={() => {
69-
setSelectedPrompt(prompt);
70-
setPromptArgs({});
71-
}}
72-
>
73-
<span className="flex-1">{prompt.name}</span>
74-
</div>
75-
))}
76-
</div>
77-
</div>
78-
</div>
52+
<ListPane
53+
items={prompts}
54+
listItems={listPrompts}
55+
setSelectedItem={(prompt) => {
56+
setSelectedPrompt(prompt);
57+
setPromptArgs({});
58+
}}
59+
renderItem={(prompt) => (
60+
<>
61+
<span className="flex-1">{prompt.name}</span>
62+
<span className="text-sm text-gray-500">{prompt.description}</span>
63+
</>
64+
)}
65+
title="Prompts"
66+
buttonText="List Prompts"
67+
/>
7968

8069
<div className="bg-white rounded-lg shadow">
8170
<div className="p-4 border-b border-gray-200">

client/src/components/ResourcesTab.tsx

Lines changed: 19 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
1-
import {
2-
FileText,
3-
PlayCircle,
4-
ChevronRight,
5-
AlertCircle,
6-
RefreshCw,
7-
} from "lucide-react";
1+
import { FileText, ChevronRight, AlertCircle, RefreshCw } from "lucide-react";
82
import { Button } from "@/components/ui/button";
93
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
104
import { TabsContent } from "@/components/ui/tabs";
5+
import ListPane from "./ListPane";
116

127
export type Resource = {
138
uri: string;
@@ -31,37 +26,23 @@ const ResourcesTab = ({
3126
error: string | null;
3227
}) => (
3328
<TabsContent value="resources" className="grid grid-cols-2 gap-4">
34-
<div className="bg-white rounded-lg shadow">
35-
<div className="p-4 border-b border-gray-200">
36-
<h3 className="font-semibold">Resources</h3>
37-
</div>
38-
<div className="p-4">
39-
<Button
40-
variant="outline"
41-
className="w-full mb-4"
42-
onClick={listResources}
43-
>
44-
<PlayCircle className="w-4 h-4 mr-2" />
45-
List Resources
46-
</Button>
47-
<div className="space-y-2">
48-
{resources.map((resource, index) => (
49-
<div
50-
key={index}
51-
className="flex items-center p-2 rounded hover:bg-gray-50 cursor-pointer"
52-
onClick={() => {
53-
setSelectedResource(resource);
54-
readResource(resource.uri);
55-
}}
56-
>
57-
<FileText className="w-4 h-4 mr-2 text-gray-500" />
58-
<span className="flex-1">{resource.uri}</span>
59-
<ChevronRight className="w-4 h-4 text-gray-400" />
60-
</div>
61-
))}
62-
</div>
63-
</div>
64-
</div>
29+
<ListPane
30+
items={resources}
31+
listItems={listResources}
32+
setSelectedItem={(resource) => {
33+
setSelectedResource(resource);
34+
readResource(resource.uri);
35+
}}
36+
renderItem={(resource) => (
37+
<>
38+
<FileText className="w-4 h-4 mr-2 text-gray-500" />
39+
<span className="flex-1">{resource.uri}</span>
40+
<ChevronRight className="w-4 h-4 text-gray-400" />
41+
</>
42+
)}
43+
title="Resources"
44+
buttonText="List Resources"
45+
/>
6546

6647
<div className="bg-white rounded-lg shadow">
6748
<div className="p-4 border-b border-gray-200 flex justify-between items-center">

client/src/components/ToolsTab.tsx

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Send, AlertCircle } from "lucide-react";
55
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
66
import { useState } from "react";
77
import { Label } from "@/components/ui/label";
8+
import ListPane from "./ListPane";
89

910
export type Tool = {
1011
name: string;
@@ -36,30 +37,19 @@ const ToolsTab = ({
3637

3738
return (
3839
<TabsContent value="tools" className="grid grid-cols-2 gap-4">
39-
<div className="bg-white rounded-lg shadow">
40-
<div className="p-4 border-b border-gray-200">
41-
<h3 className="font-semibold">Tools</h3>
42-
</div>
43-
<div className="p-4">
44-
<Button variant="outline" className="w-full mb-4" onClick={listTools}>
45-
List Tools
46-
</Button>
47-
<div className="space-y-2">
48-
{tools.map((tool, index) => (
49-
<div
50-
key={index}
51-
className="flex items-center p-2 rounded hover:bg-gray-50 cursor-pointer"
52-
onClick={() => setSelectedTool(tool)}
53-
>
54-
<span className="flex-1">{tool.name}</span>
55-
<span className="text-sm text-gray-500">
56-
{tool.description}
57-
</span>
58-
</div>
59-
))}
60-
</div>
61-
</div>
62-
</div>
40+
<ListPane
41+
items={tools}
42+
listItems={listTools}
43+
setSelectedItem={setSelectedTool}
44+
renderItem={(tool) => (
45+
<>
46+
<span className="flex-1">{tool.name}</span>
47+
<span className="text-sm text-gray-500">{tool.description}</span>
48+
</>
49+
)}
50+
title="Tools"
51+
buttonText="List Tools"
52+
/>
6353

6454
<div className="bg-white rounded-lg shadow">
6555
<div className="p-4 border-b border-gray-200">

0 commit comments

Comments
 (0)