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:
Jason
2025-11-16 11:31:27 +08:00
parent 9d431cc7ae
commit 031ea3a58f
2 changed files with 43 additions and 41 deletions

View File

@@ -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">
<h3 className="font-medium text-gray-900 dark:text-gray-100">
{name} {name}
</h3> </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"

View File

@@ -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}