style(mcp): refine panel layout for better visual hierarchy and compactness
- Replace checkboxes with toggle switches for app selection (more intuitive for enable/disable actions) - Change switch color from blue to emerald to match MCP button theme - Stack app options vertically with labels on left to save horizontal space - Reduce panel width from max-w-4xl to max-w-3xl for more compact design - Move docs button next to server name for better information grouping
This commit is contained in:
@@ -9,7 +9,7 @@ import {
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { useAllMcpServers, useToggleMcpApp } from "@/hooks/useMcp";
|
import { useAllMcpServers, useToggleMcpApp } from "@/hooks/useMcp";
|
||||||
import type { McpServer } from "@/types";
|
import type { McpServer } from "@/types";
|
||||||
import type { AppId } from "@/lib/api/types";
|
import type { AppId } from "@/lib/api/types";
|
||||||
@@ -117,7 +117,7 @@ const UnifiedMcpPanel: React.FC<UnifiedMcpPanelProps> = ({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent className="max-w-4xl max-h-[85vh] min-h-[600px] flex flex-col">
|
<DialogContent className="max-w-3xl max-h-[85vh] min-h-[600px] flex flex-col">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<div className="flex items-center justify-between pr-8">
|
<div className="flex items-center justify-between pr-8">
|
||||||
<DialogTitle>{t("mcp.unifiedPanel.title")}</DialogTitle>
|
<DialogTitle>{t("mcp.unifiedPanel.title")}</DialogTitle>
|
||||||
@@ -264,9 +264,22 @@ const UnifiedMcpListItem: React.FC<UnifiedMcpListItemProps> = ({
|
|||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
{/* 左侧:服务器信息 */}
|
{/* 左侧:服务器信息 */}
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<h3 className="font-medium text-gray-900 dark:text-gray-100 mb-1">
|
<div className="flex items-center gap-2 mb-1">
|
||||||
{name}
|
<h3 className="font-medium text-gray-900 dark:text-gray-100">
|
||||||
</h3>
|
{name}
|
||||||
|
</h3>
|
||||||
|
{docsUrl && (
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={openDocs}
|
||||||
|
title={t("mcp.presets.docs")}
|
||||||
|
>
|
||||||
|
{t("mcp.presets.docs")}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
{description && (
|
{description && (
|
||||||
<p className="text-sm text-gray-500 dark:text-gray-400 line-clamp-2">
|
<p className="text-sm text-gray-500 dark:text-gray-400 line-clamp-2">
|
||||||
{description}
|
{description}
|
||||||
@@ -279,70 +292,59 @@ const UnifiedMcpListItem: React.FC<UnifiedMcpListItemProps> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 中间:应用复选框 */}
|
{/* 中间:应用开关 */}
|
||||||
<div className="flex items-center gap-4 flex-shrink-0">
|
<div className="flex flex-col gap-2 flex-shrink-0 min-w-[120px]">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center justify-between gap-3">
|
||||||
<Checkbox
|
|
||||||
id={`${id}-claude`}
|
|
||||||
checked={server.apps.claude}
|
|
||||||
onCheckedChange={(checked: boolean) =>
|
|
||||||
onToggleApp(id, "claude", checked === true)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<label
|
<label
|
||||||
htmlFor={`${id}-claude`}
|
htmlFor={`${id}-claude`}
|
||||||
className="text-sm text-gray-700 dark:text-gray-300 cursor-pointer"
|
className="text-sm text-gray-700 dark:text-gray-300 cursor-pointer"
|
||||||
>
|
>
|
||||||
{t("mcp.unifiedPanel.apps.claude")}
|
{t("mcp.unifiedPanel.apps.claude")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
<Switch
|
||||||
|
id={`${id}-claude`}
|
||||||
<div className="flex items-center gap-2">
|
checked={server.apps.claude}
|
||||||
<Checkbox
|
|
||||||
id={`${id}-codex`}
|
|
||||||
checked={server.apps.codex}
|
|
||||||
onCheckedChange={(checked: boolean) =>
|
onCheckedChange={(checked: boolean) =>
|
||||||
onToggleApp(id, "codex", checked === true)
|
onToggleApp(id, "claude", checked)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between gap-3">
|
||||||
<label
|
<label
|
||||||
htmlFor={`${id}-codex`}
|
htmlFor={`${id}-codex`}
|
||||||
className="text-sm text-gray-700 dark:text-gray-300 cursor-pointer"
|
className="text-sm text-gray-700 dark:text-gray-300 cursor-pointer"
|
||||||
>
|
>
|
||||||
{t("mcp.unifiedPanel.apps.codex")}
|
{t("mcp.unifiedPanel.apps.codex")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
<Switch
|
||||||
|
id={`${id}-codex`}
|
||||||
<div className="flex items-center gap-2">
|
checked={server.apps.codex}
|
||||||
<Checkbox
|
|
||||||
id={`${id}-gemini`}
|
|
||||||
checked={server.apps.gemini}
|
|
||||||
onCheckedChange={(checked: boolean) =>
|
onCheckedChange={(checked: boolean) =>
|
||||||
onToggleApp(id, "gemini", checked === true)
|
onToggleApp(id, "codex", checked)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between gap-3">
|
||||||
<label
|
<label
|
||||||
htmlFor={`${id}-gemini`}
|
htmlFor={`${id}-gemini`}
|
||||||
className="text-sm text-gray-700 dark:text-gray-300 cursor-pointer"
|
className="text-sm text-gray-700 dark:text-gray-300 cursor-pointer"
|
||||||
>
|
>
|
||||||
{t("mcp.unifiedPanel.apps.gemini")}
|
{t("mcp.unifiedPanel.apps.gemini")}
|
||||||
</label>
|
</label>
|
||||||
|
<Switch
|
||||||
|
id={`${id}-gemini`}
|
||||||
|
checked={server.apps.gemini}
|
||||||
|
onCheckedChange={(checked: boolean) =>
|
||||||
|
onToggleApp(id, "gemini", checked)
|
||||||
|
}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 右侧:操作按钮 */}
|
{/* 右侧:操作按钮 */}
|
||||||
<div className="flex items-center gap-2 flex-shrink-0">
|
<div className="flex items-center gap-2 flex-shrink-0">
|
||||||
{docsUrl && (
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
onClick={openDocs}
|
|
||||||
title={t("mcp.presets.docs")}
|
|
||||||
>
|
|
||||||
{t("mcp.presets.docs")}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const Switch = React.forwardRef<
|
|||||||
<SwitchPrimitives.Root
|
<SwitchPrimitives.Root
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-blue-500 dark:data-[state=checked]:bg-blue-600 data-[state=unchecked]:bg-gray-300 dark:data-[state=unchecked]:bg-gray-700",
|
"peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-emerald-500 dark:data-[state=checked]:bg-emerald-600 data-[state=unchecked]:bg-gray-300 dark:data-[state=unchecked]:bg-gray-700",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
Reference in New Issue
Block a user