Merge branch 'main' into feature/audio-rendering

This commit is contained in:
Jerome
2025-01-28 02:15:55 -05:00
committed by GitHub
14 changed files with 688 additions and 358 deletions

View File

@@ -1,5 +1,14 @@
import { useState } from "react";
import { Play, ChevronDown, ChevronRight } from "lucide-react";
import {
Play,
ChevronDown,
ChevronRight,
CircleHelp,
Bug,
Github,
Eye,
EyeOff,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
@@ -47,6 +56,7 @@ const Sidebar = ({
}: SidebarProps) => {
const [theme, setTheme] = useTheme();
const [showEnvVars, setShowEnvVars] = useState(false);
const [shownEnvVars, setShownEnvVars] = useState<Set<string>>(new Set());
return (
<div className="w-80 bg-card border-r border-border flex flex-col h-full">
@@ -86,6 +96,7 @@ const Sidebar = ({
placeholder="Command"
value={command}
onChange={(e) => setCommand(e.target.value)}
className="font-mono"
/>
</div>
<div className="space-y-2">
@@ -94,6 +105,7 @@ const Sidebar = ({
placeholder="Arguments (space-separated)"
value={args}
onChange={(e) => setArgs(e.target.value)}
className="font-mono"
/>
</div>
</>
@@ -104,6 +116,7 @@ const Sidebar = ({
placeholder="URL"
value={sseUrl}
onChange={(e) => setSseUrl(e.target.value)}
className="font-mono"
/>
</div>
)}
@@ -124,19 +137,44 @@ const Sidebar = ({
{showEnvVars && (
<div className="space-y-2">
{Object.entries(env).map(([key, value], idx) => (
<div key={idx} className="grid grid-cols-[1fr,auto] gap-2">
<div className="space-y-1">
<div key={idx} className="space-y-2 pb-4">
<div className="flex gap-2">
<Input
placeholder="Key"
value={key}
onChange={(e) => {
const newKey = e.target.value;
const newEnv = { ...env };
delete newEnv[key];
newEnv[e.target.value] = value;
newEnv[newKey] = value;
setEnv(newEnv);
setShownEnvVars((prev) => {
const next = new Set(prev);
if (next.has(key)) {
next.delete(key);
next.add(newKey);
}
return next;
});
}}
className="font-mono"
/>
<Button
variant="destructive"
size="icon"
className="h-9 w-9 p-0 shrink-0"
onClick={() => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { [key]: _removed, ...rest } = env;
setEnv(rest);
}}
>
×
</Button>
</div>
<div className="flex gap-2">
<Input
type={shownEnvVars.has(key) ? "text" : "password"}
placeholder="Value"
value={value}
onChange={(e) => {
@@ -144,25 +182,47 @@ const Sidebar = ({
newEnv[key] = e.target.value;
setEnv(newEnv);
}}
className="font-mono"
/>
<Button
variant="outline"
size="icon"
className="h-9 w-9 p-0 shrink-0"
onClick={() => {
setShownEnvVars((prev) => {
const next = new Set(prev);
if (next.has(key)) {
next.delete(key);
} else {
next.add(key);
}
return next;
});
}}
aria-label={
shownEnvVars.has(key) ? "Hide value" : "Show value"
}
aria-pressed={shownEnvVars.has(key)}
title={
shownEnvVars.has(key) ? "Hide value" : "Show value"
}
>
{shownEnvVars.has(key) ? (
<Eye className="h-4 w-4" aria-hidden="true" />
) : (
<EyeOff className="h-4 w-4" aria-hidden="true" />
)}
</Button>
</div>
<Button
variant="destructive"
onClick={() => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { [key]: removed, ...rest } = env;
setEnv(rest);
}}
>
Remove
</Button>
</div>
))}
<Button
variant="outline"
className="w-full mt-2"
onClick={() => {
const key = "";
const newEnv = { ...env };
newEnv[""] = "";
newEnv[key] = "";
setEnv(newEnv);
}}
>
@@ -220,14 +280,14 @@ const Sidebar = ({
</div>
</div>
<div className="p-4 border-t">
<div className="flex items-center space-x-2">
<div className="flex items-center justify-between">
<Select
value={theme}
onValueChange={(value: string) =>
setTheme(value as "system" | "light" | "dark")
}
>
<SelectTrigger className="w-[120px]" id="theme-select">
<SelectTrigger className="w-[100px]" id="theme-select">
<SelectValue />
</SelectTrigger>
<SelectContent>
@@ -236,6 +296,39 @@ const Sidebar = ({
<SelectItem value="dark">Dark</SelectItem>
</SelectContent>
</Select>
<div className="flex items-center space-x-2">
<a
href="https://modelcontextprotocol.io/docs/tools/inspector"
target="_blank"
rel="noopener noreferrer"
>
<Button variant="ghost" title="Inspector Documentation">
<CircleHelp className="w-4 h-4 text-gray-800" />
</Button>
</a>
<a
href="https://modelcontextprotocol.io/docs/tools/debugging"
target="_blank"
rel="noopener noreferrer"
>
<Button variant="ghost" title="Debugging Guide">
<Bug className="w-4 h-4 text-gray-800" />
</Button>
</a>
<a
href="https://github.com/modelcontextprotocol/inspector"
target="_blank"
rel="noopener noreferrer"
>
<Button
variant="ghost"
title="Report bugs or contribute on GitHub"
>
<Github className="w-4 h-4 text-gray-800" />
</Button>
</a>
</div>
</div>
</div>
</div>

View File

@@ -10,7 +10,7 @@ import {
CallToolResultSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { AlertCircle, Send } from "lucide-react";
import { useState } from "react";
import { useEffect, useState } from "react";
import ListPane from "./ListPane";
import { CompatibilityCallToolResult } from "@modelcontextprotocol/sdk/types.js";
@@ -31,12 +31,15 @@ const ToolsTab = ({
clearTools: () => void;
callTool: (name: string, params: Record<string, unknown>) => void;
selectedTool: Tool | null;
setSelectedTool: (tool: Tool) => void;
setSelectedTool: (tool: Tool | null) => void;
toolResult: CompatibilityCallToolResult | null;
nextCursor: ListToolsResult["nextCursor"];
error: string | null;
}) => {
const [params, setParams] = useState<Record<string, unknown>>({});
useEffect(() => {
setParams({});
}, [selectedTool]);
const renderToolResult = () => {
if (!toolResult) return null;
@@ -107,7 +110,7 @@ const ToolsTab = ({
return (
<>
<h4 className="font-semibold mb-2">Tool Result (Legacy):</h4>
<pre className="bg-gray-50 p-4 rounded text-sm overflow-auto max-h-64">
<pre className="bg-gray-50 dark:bg-gray-800 dark:text-gray-100 p-4 rounded text-sm overflow-auto max-h-64">
{JSON.stringify(toolResult.toolResult, null, 2)}
</pre>
</>
@@ -120,7 +123,10 @@ const ToolsTab = ({
<ListPane
items={tools}
listItems={listTools}
clearItems={clearTools}
clearItems={() => {
clearTools();
setSelectedTool(null);
}}
setSelectedItem={setSelectedTool}
renderItem={(tool) => (
<>
@@ -178,6 +184,30 @@ const ToolsTab = ({
}
className="mt-1"
/>
) : /* @ts-expect-error value type is currently unknown */
value.type === "object" ? (
<Textarea
id={key}
name={key}
// @ts-expect-error value type is currently unknown
placeholder={value.description}
onChange={(e) => {
try {
const parsed = JSON.parse(e.target.value);
setParams({
...params,
[key]: parsed,
});
} catch (err) {
// If invalid JSON, store as string - will be validated on submit
setParams({
...params,
[key]: e.target.value,
});
}
}}
className="mt-1"
/>
) : (
<Input
// @ts-expect-error value type is currently unknown