Merge branch 'main' into fix-reconnect
This commit is contained in:
@@ -12,6 +12,7 @@ interface JsonViewProps {
|
|||||||
initialExpandDepth?: number;
|
initialExpandDepth?: number;
|
||||||
className?: string;
|
className?: string;
|
||||||
withCopyButton?: boolean;
|
withCopyButton?: boolean;
|
||||||
|
isError?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const JsonView = memo(
|
const JsonView = memo(
|
||||||
@@ -21,6 +22,7 @@ const JsonView = memo(
|
|||||||
initialExpandDepth = 3,
|
initialExpandDepth = 3,
|
||||||
className,
|
className,
|
||||||
withCopyButton = true,
|
withCopyButton = true,
|
||||||
|
isError = false,
|
||||||
}: JsonViewProps) => {
|
}: JsonViewProps) => {
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
@@ -86,6 +88,7 @@ const JsonView = memo(
|
|||||||
name={name}
|
name={name}
|
||||||
depth={0}
|
depth={0}
|
||||||
initialExpandDepth={initialExpandDepth}
|
initialExpandDepth={initialExpandDepth}
|
||||||
|
isError={isError}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -100,17 +103,25 @@ interface JsonNodeProps {
|
|||||||
name?: string;
|
name?: string;
|
||||||
depth: number;
|
depth: number;
|
||||||
initialExpandDepth: number;
|
initialExpandDepth: number;
|
||||||
|
isError?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const JsonNode = memo(
|
const JsonNode = memo(
|
||||||
({ data, name, depth = 0, initialExpandDepth }: JsonNodeProps) => {
|
({
|
||||||
|
data,
|
||||||
|
name,
|
||||||
|
depth = 0,
|
||||||
|
initialExpandDepth,
|
||||||
|
isError = false,
|
||||||
|
}: JsonNodeProps) => {
|
||||||
const [isExpanded, setIsExpanded] = useState(depth < initialExpandDepth);
|
const [isExpanded, setIsExpanded] = useState(depth < initialExpandDepth);
|
||||||
const [typeStyleMap] = useState<Record<string, string>>({
|
const [typeStyleMap] = useState<Record<string, string>>({
|
||||||
number: "text-blue-600",
|
number: "text-blue-600",
|
||||||
boolean: "text-amber-600",
|
boolean: "text-amber-600",
|
||||||
null: "text-purple-600",
|
null: "text-purple-600",
|
||||||
undefined: "text-gray-600",
|
undefined: "text-gray-600",
|
||||||
string: "text-green-600 break-all whitespace-pre-wrap",
|
string: "text-green-600 group-hover:text-green-500",
|
||||||
|
error: "text-red-600 group-hover:text-red-500",
|
||||||
default: "text-gray-700",
|
default: "text-gray-700",
|
||||||
});
|
});
|
||||||
const dataType = getDataType(data);
|
const dataType = getDataType(data);
|
||||||
@@ -214,7 +225,14 @@ const JsonNode = memo(
|
|||||||
{name}:
|
{name}:
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<pre className={typeStyleMap.string}>"{value}"</pre>
|
<pre
|
||||||
|
className={clsx(
|
||||||
|
typeStyleMap.string,
|
||||||
|
"break-all whitespace-pre-wrap",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
"{value}"
|
||||||
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -228,8 +246,8 @@ const JsonNode = memo(
|
|||||||
)}
|
)}
|
||||||
<pre
|
<pre
|
||||||
className={clsx(
|
className={clsx(
|
||||||
typeStyleMap.string,
|
isError ? typeStyleMap.error : typeStyleMap.string,
|
||||||
"cursor-pointer group-hover:text-green-500",
|
"cursor-pointer break-all whitespace-pre-wrap",
|
||||||
)}
|
)}
|
||||||
onClick={() => setIsExpanded(!isExpanded)}
|
onClick={() => setIsExpanded(!isExpanded)}
|
||||||
title={isExpanded ? "Click to collapse" : "Click to expand"}
|
title={isExpanded ? "Click to collapse" : "Click to expand"}
|
||||||
|
|||||||
@@ -103,14 +103,19 @@ const Sidebar = ({
|
|||||||
<div className="p-4 flex-1 overflow-auto">
|
<div className="p-4 flex-1 overflow-auto">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium">Transport Type</label>
|
<label
|
||||||
|
className="text-sm font-medium"
|
||||||
|
htmlFor="transport-type-select"
|
||||||
|
>
|
||||||
|
Transport Type
|
||||||
|
</label>
|
||||||
<Select
|
<Select
|
||||||
value={transportType}
|
value={transportType}
|
||||||
onValueChange={(value: "stdio" | "sse") =>
|
onValueChange={(value: "stdio" | "sse") =>
|
||||||
setTransportType(value)
|
setTransportType(value)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger id="transport-type-select">
|
||||||
<SelectValue placeholder="Select transport type" />
|
<SelectValue placeholder="Select transport type" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
@@ -123,8 +128,11 @@ const Sidebar = ({
|
|||||||
{transportType === "stdio" ? (
|
{transportType === "stdio" ? (
|
||||||
<>
|
<>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium">Command</label>
|
<label className="text-sm font-medium" htmlFor="command-input">
|
||||||
|
Command
|
||||||
|
</label>
|
||||||
<Input
|
<Input
|
||||||
|
id="command-input"
|
||||||
placeholder="Command"
|
placeholder="Command"
|
||||||
value={command}
|
value={command}
|
||||||
onChange={(e) => setCommand(e.target.value)}
|
onChange={(e) => setCommand(e.target.value)}
|
||||||
@@ -132,8 +140,14 @@ const Sidebar = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium">Arguments</label>
|
<label
|
||||||
|
className="text-sm font-medium"
|
||||||
|
htmlFor="arguments-input"
|
||||||
|
>
|
||||||
|
Arguments
|
||||||
|
</label>
|
||||||
<Input
|
<Input
|
||||||
|
id="arguments-input"
|
||||||
placeholder="Arguments (space-separated)"
|
placeholder="Arguments (space-separated)"
|
||||||
value={args}
|
value={args}
|
||||||
onChange={(e) => setArgs(e.target.value)}
|
onChange={(e) => setArgs(e.target.value)}
|
||||||
@@ -144,8 +158,11 @@ const Sidebar = ({
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium">URL</label>
|
<label className="text-sm font-medium" htmlFor="sse-url-input">
|
||||||
|
URL
|
||||||
|
</label>
|
||||||
<Input
|
<Input
|
||||||
|
id="sse-url-input"
|
||||||
placeholder="URL"
|
placeholder="URL"
|
||||||
value={sseUrl}
|
value={sseUrl}
|
||||||
onChange={(e) => setSseUrl(e.target.value)}
|
onChange={(e) => setSseUrl(e.target.value)}
|
||||||
@@ -157,6 +174,7 @@ const Sidebar = ({
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => setShowBearerToken(!showBearerToken)}
|
onClick={() => setShowBearerToken(!showBearerToken)}
|
||||||
className="flex items-center w-full"
|
className="flex items-center w-full"
|
||||||
|
aria-expanded={showBearerToken}
|
||||||
>
|
>
|
||||||
{showBearerToken ? (
|
{showBearerToken ? (
|
||||||
<ChevronDown className="w-4 h-4 mr-2" />
|
<ChevronDown className="w-4 h-4 mr-2" />
|
||||||
@@ -167,8 +185,14 @@ const Sidebar = ({
|
|||||||
</Button>
|
</Button>
|
||||||
{showBearerToken && (
|
{showBearerToken && (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium">Bearer Token</label>
|
<label
|
||||||
|
className="text-sm font-medium"
|
||||||
|
htmlFor="bearer-token-input"
|
||||||
|
>
|
||||||
|
Bearer Token
|
||||||
|
</label>
|
||||||
<Input
|
<Input
|
||||||
|
id="bearer-token-input"
|
||||||
placeholder="Bearer Token"
|
placeholder="Bearer Token"
|
||||||
value={bearerToken}
|
value={bearerToken}
|
||||||
onChange={(e) => setBearerToken(e.target.value)}
|
onChange={(e) => setBearerToken(e.target.value)}
|
||||||
@@ -187,6 +211,7 @@ const Sidebar = ({
|
|||||||
onClick={() => setShowEnvVars(!showEnvVars)}
|
onClick={() => setShowEnvVars(!showEnvVars)}
|
||||||
className="flex items-center w-full"
|
className="flex items-center w-full"
|
||||||
data-testid="env-vars-button"
|
data-testid="env-vars-button"
|
||||||
|
aria-expanded={showEnvVars}
|
||||||
>
|
>
|
||||||
{showEnvVars ? (
|
{showEnvVars ? (
|
||||||
<ChevronDown className="w-4 h-4 mr-2" />
|
<ChevronDown className="w-4 h-4 mr-2" />
|
||||||
@@ -201,6 +226,7 @@ const Sidebar = ({
|
|||||||
<div key={idx} className="space-y-2 pb-4">
|
<div key={idx} className="space-y-2 pb-4">
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Input
|
<Input
|
||||||
|
aria-label={`Environment variable key ${idx + 1}`}
|
||||||
placeholder="Key"
|
placeholder="Key"
|
||||||
value={key}
|
value={key}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
@@ -243,6 +269,7 @@ const Sidebar = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Input
|
<Input
|
||||||
|
aria-label={`Environment variable value ${idx + 1}`}
|
||||||
type={shownEnvVars.has(key) ? "text" : "password"}
|
type={shownEnvVars.has(key) ? "text" : "password"}
|
||||||
placeholder="Value"
|
placeholder="Value"
|
||||||
value={value}
|
value={value}
|
||||||
@@ -309,6 +336,7 @@ const Sidebar = ({
|
|||||||
onClick={() => setShowConfig(!showConfig)}
|
onClick={() => setShowConfig(!showConfig)}
|
||||||
className="flex items-center w-full"
|
className="flex items-center w-full"
|
||||||
data-testid="config-button"
|
data-testid="config-button"
|
||||||
|
aria-expanded={showConfig}
|
||||||
>
|
>
|
||||||
{showConfig ? (
|
{showConfig ? (
|
||||||
<ChevronDown className="w-4 h-4 mr-2" />
|
<ChevronDown className="w-4 h-4 mr-2" />
|
||||||
@@ -325,7 +353,10 @@ const Sidebar = ({
|
|||||||
return (
|
return (
|
||||||
<div key={key} className="space-y-2">
|
<div key={key} className="space-y-2">
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<label className="text-sm font-medium text-green-600 break-all">
|
<label
|
||||||
|
className="text-sm font-medium text-green-600 break-all"
|
||||||
|
htmlFor={`${configKey}-input`}
|
||||||
|
>
|
||||||
{configItem.label}
|
{configItem.label}
|
||||||
</label>
|
</label>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
@@ -339,6 +370,7 @@ const Sidebar = ({
|
|||||||
</div>
|
</div>
|
||||||
{typeof configItem.value === "number" ? (
|
{typeof configItem.value === "number" ? (
|
||||||
<Input
|
<Input
|
||||||
|
id={`${configKey}-input`}
|
||||||
type="number"
|
type="number"
|
||||||
data-testid={`${configKey}-input`}
|
data-testid={`${configKey}-input`}
|
||||||
value={configItem.value}
|
value={configItem.value}
|
||||||
@@ -365,7 +397,7 @@ const Sidebar = ({
|
|||||||
setConfig(newConfig);
|
setConfig(newConfig);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger id={`${configKey}-input`}>
|
||||||
<SelectValue />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
@@ -375,6 +407,7 @@ const Sidebar = ({
|
|||||||
</Select>
|
</Select>
|
||||||
) : (
|
) : (
|
||||||
<Input
|
<Input
|
||||||
|
id={`${configKey}-input`}
|
||||||
data-testid={`${configKey}-input`}
|
data-testid={`${configKey}-input`}
|
||||||
value={configItem.value}
|
value={configItem.value}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
@@ -454,14 +487,19 @@ const Sidebar = ({
|
|||||||
|
|
||||||
{loggingSupported && connectionStatus === "connected" && (
|
{loggingSupported && connectionStatus === "connected" && (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium">Logging Level</label>
|
<label
|
||||||
|
className="text-sm font-medium"
|
||||||
|
htmlFor="logging-level-select"
|
||||||
|
>
|
||||||
|
Logging Level
|
||||||
|
</label>
|
||||||
<Select
|
<Select
|
||||||
value={logLevel}
|
value={logLevel}
|
||||||
onValueChange={(value: LoggingLevel) =>
|
onValueChange={(value: LoggingLevel) =>
|
||||||
sendLogLevelRequest(value)
|
sendLogLevelRequest(value)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger id="logging-level-select">
|
||||||
<SelectValue placeholder="Select logging level" />
|
<SelectValue placeholder="Select logging level" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
|
|||||||
@@ -69,11 +69,18 @@ const ToolsTab = ({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h4 className="font-semibold mb-2">
|
<h4 className="font-semibold mb-2">
|
||||||
Tool Result: {isError ? "Error" : "Success"}
|
Tool Result:{" "}
|
||||||
|
{isError ? (
|
||||||
|
<span className="text-red-600 font-semibold">Error</span>
|
||||||
|
) : (
|
||||||
|
<span className="text-green-600 font-semibold">Success</span>
|
||||||
|
)}
|
||||||
</h4>
|
</h4>
|
||||||
{structuredResult.content.map((item, index) => (
|
{structuredResult.content.map((item, index) => (
|
||||||
<div key={index} className="mb-2">
|
<div key={index} className="mb-2">
|
||||||
{item.type === "text" && <JsonView data={item.text} />}
|
{item.type === "text" && (
|
||||||
|
<JsonView data={item.text} isError={isError} />
|
||||||
|
)}
|
||||||
{item.type === "image" && (
|
{item.type === "image" && (
|
||||||
<img
|
<img
|
||||||
src={`data:${item.mimeType};base64,${item.data}`}
|
src={`data:${item.mimeType};base64,${item.data}`}
|
||||||
|
|||||||
Reference in New Issue
Block a user