From 4e99e6393ff10b6743acf14659bb16287edd49fa Mon Sep 17 00:00:00 2001 From: cliffhall Date: Sat, 25 Apr 2026 18:34:07 -0400 Subject: [PATCH] Make ViewHeader and ServerStatusIndicator responsive Header now adapts cleanly across widths instead of wrapping or clipping: - LeftSection/RightSection are symmetric (`flex: 1, miw: 0, wrap: nowrap`), so the natural-width CenterSection sits visually centered between them. Server name truncates with ellipsis when the left slice is too narrow. - Below `md` (992px) the SegmentedControl swaps for a Select dropdown so the tab labels never have to shrink or wrap. - Below `sm` (768px) the "Disconnect" button collapses to an MdLinkOff ActionIcon (with `aria-label` + `title` for the tooltip). - Below `lg` (1200px) ServerStatusIndicator hides its text label and shows only the colored dot (full label still available as a tooltip on hover), freeing space so the Disconnect button stops getting truncated at typical desktop widths. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../ServerStatusIndicator.tsx | 6 +- .../groups/ViewHeader/ViewHeader.tsx | 63 ++++++++++++++----- 2 files changed, 52 insertions(+), 17 deletions(-) diff --git a/clients/web/src/components/elements/ServerStatusIndicator/ServerStatusIndicator.tsx b/clients/web/src/components/elements/ServerStatusIndicator/ServerStatusIndicator.tsx index 58f65cfc..cb6afb45 100644 --- a/clients/web/src/components/elements/ServerStatusIndicator/ServerStatusIndicator.tsx +++ b/clients/web/src/components/elements/ServerStatusIndicator/ServerStatusIndicator.tsx @@ -1,4 +1,5 @@ import { Group, Paper, Text } from "@mantine/core"; +import { useMediaQuery } from "@mantine/hooks"; import type { ConnectionStatus } from "@inspector/core/mcp/types.js"; export interface ServerStatusIndicatorProps { @@ -42,13 +43,16 @@ export function ServerStatusIndicator({ latencyMs, retryCount, }: ServerStatusIndicatorProps) { + const showLabel = useMediaQuery("(min-width: 1200px)"); + const label = getLabel(status, latencyMs, retryCount); return ( - {getLabel(status, latencyMs, retryCount)} + {showLabel && {label}} ); } diff --git a/clients/web/src/components/groups/ViewHeader/ViewHeader.tsx b/clients/web/src/components/groups/ViewHeader/ViewHeader.tsx index 4de55a89..14d7deb0 100644 --- a/clients/web/src/components/groups/ViewHeader/ViewHeader.tsx +++ b/clients/web/src/components/groups/ViewHeader/ViewHeader.tsx @@ -5,12 +5,14 @@ import { Group, Image, SegmentedControl, + Select, Text, Title, useComputedColorScheme, } from "@mantine/core"; +import { useMediaQuery } from "@mantine/hooks"; import type { Implementation } from "@modelcontextprotocol/sdk/types.js"; -import { MdLightMode, MdDarkMode } from "react-icons/md"; +import { MdLightMode, MdDarkMode, MdLinkOff } from "react-icons/md"; import type { ConnectionStatus } from "@inspector/core/mcp/types.js"; import { ServerStatusIndicator } from "../../elements/ServerStatusIndicator/ServerStatusIndicator"; import mcpLogo from "../../../theme/assets/MCP.svg"; @@ -39,13 +41,13 @@ const HeaderBar = Group.withProps({ h: "100%", px: "md", wrap: "nowrap", - gap: 0, + gap: "md", }); const LeftSection = Group.withProps({ gap: "md", wrap: "nowrap", - w: "33.33%", + flex: 1, miw: 0, }); @@ -66,17 +68,20 @@ const ServerName = Text.withProps({ fw: 600, size: "lg", truncate: "end", - maw: "calc(100% - 40px)", + miw: 0, + flex: 1, }); const CenterSection = Group.withProps({ - w: "33.33%", - justify: "center", + wrap: "nowrap", + flex: "0 0 auto", }); const RightSection = Group.withProps({ gap: "sm", - w: "33.33%", + wrap: "nowrap", + flex: 1, + miw: 0, justify: "flex-end", }); @@ -86,6 +91,13 @@ const DisconnectButton = Button.withProps({ size: "sm", }); +const DisconnectIcon = ActionIcon.withProps({ + variant: "subtle", + c: "red", + size: 36, + "aria-label": "Disconnect", +}); + const ThemeToggle = ActionIcon.withProps({ variant: "subtle", size: 36, @@ -101,6 +113,8 @@ const UnconnectedBar = Group.withProps({ export function ViewHeader(props: ViewHeaderProps) { const colorScheme = useComputedColorScheme(); const ThemeIcon = colorScheme === "dark" ? MdLightMode : MdDarkMode; + const showSegmented = useMediaQuery("(min-width: 992px)"); + const showDisconnectLabel = useMediaQuery("(min-width: 768px)"); if (!props.connected) { return ( @@ -126,12 +140,23 @@ export function ViewHeader(props: ViewHeaderProps) { - + {showSegmented ? ( + + ) : ( +