Skip to content
This repository was archived by the owner on May 21, 2026. It is now read-only.

Commit fbbd018

Browse files
committed
feat(inspector): update dependencies and enhance UI components
- Add cmdk, next-themes, and sonner dependencies to improve UI functionality - Refactor App component to include ThemeProvider and Toaster for better user experience - Enhance InspectorDashboard with new connection management features and export options - Update Layout component to support command palette and keyboard shortcuts - Improve styling and animations in various components for a more polished look - Add new CSS animations for status indicators in index.css - Implement version injection from package.json in vite.config.ts
1 parent 5c66233 commit fbbd018

21 files changed

Lines changed: 2012 additions & 437 deletions

packages/inspector/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,20 @@
6262
"@types/react-syntax-highlighter": "^15.5.13",
6363
"class-variance-authority": "^0.7.1",
6464
"clsx": "^2.1.1",
65+
"cmdk": "^1.1.1",
6566
"express": "^4.18.0",
6667
"framer-motion": "^12.23.22",
6768
"hono": "^4.0.0",
6869
"lucide-react": "^0.545.0",
6970
"mcp-use": "workspace:*",
7071
"motion": "^12.23.22",
72+
"next-themes": "^0.4.6",
7173
"react": "^19.2.0",
7274
"react-dom": "^19.2.0",
7375
"react-resizable-panels": "^3.0.6",
7476
"react-router-dom": "^7.9.3",
7577
"react-syntax-highlighter": "^15.6.6",
78+
"sonner": "^2.0.7",
7679
"tailwind-merge": "^3.3.1"
7780
},
7881
"publishConfig": {

packages/inspector/src/client/App.tsx

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,25 @@ import { Layout } from './components/Layout'
44
import { ServerDetail } from './components/ServerDetail'
55
import { ServerList } from './components/ServerList'
66
import { McpProvider } from './context/McpContext'
7+
import { ThemeProvider } from './context/ThemeContext'
8+
import { Toaster } from '@/components/ui/sonner'
79

810
function App() {
911
return (
10-
<McpProvider>
11-
<Router basename="/inspector">
12-
<Layout>
13-
<Routes>
14-
<Route path="/" element={<InspectorDashboard />} />
15-
<Route path="/servers" element={<ServerList />} />
16-
<Route path="/servers/:serverId" element={<ServerDetail />} />
17-
</Routes>
18-
</Layout>
19-
</Router>
20-
</McpProvider>
12+
<ThemeProvider>
13+
<McpProvider>
14+
<Router basename="/inspector">
15+
<Layout>
16+
<Routes>
17+
<Route path="/" element={<InspectorDashboard />} />
18+
<Route path="/servers" element={<ServerList />} />
19+
<Route path="/servers/:serverId" element={<ServerDetail />} />
20+
</Routes>
21+
</Layout>
22+
</Router>
23+
<Toaster position="top-center" />
24+
</McpProvider>
25+
</ThemeProvider>
2126
)
2227
}
2328

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
"use client";
2+
3+
import { cn } from "@/lib/utils";
4+
import { Monitor, Moon, SunDim } from "lucide-react";
5+
import { useTheme } from "../context/ThemeContext";
6+
import { useEffect, useRef, useState } from "react";
7+
import { flushSync } from "react-dom";
8+
9+
type props = {
10+
className?: string;
11+
};
12+
13+
export const AnimatedThemeToggler = ({ className }: props) => {
14+
const { theme, setTheme } = useTheme();
15+
const buttonRef = useRef<HTMLButtonElement | null>(null);
16+
const [mounted, setMounted] = useState(false);
17+
18+
// Prevent hydration mismatch by only showing theme-dependent content after mount
19+
useEffect(() => {
20+
setMounted(true);
21+
}, []);
22+
23+
// Cycle through: system -> light -> dark -> system
24+
const getNextTheme = () => {
25+
if (theme === "system") return "light";
26+
if (theme === "light") return "dark";
27+
return "system";
28+
};
29+
30+
const getThemeIcon = () => {
31+
if (theme === "system") return <Monitor className="size-4" />;
32+
if (theme === "light") return <SunDim className="size-4" />;
33+
return <Moon className="size-4" />;
34+
};
35+
36+
const getThemeLabel = () => {
37+
if (theme === "system") return "Switch to light mode";
38+
if (theme === "light") return "Switch to dark mode";
39+
return "Switch to system mode";
40+
};
41+
42+
const changeTheme = async () => {
43+
if (!buttonRef.current) return;
44+
45+
const nextTheme = getNextTheme();
46+
47+
// Check if view transitions are supported
48+
if (typeof document !== 'undefined' && 'startViewTransition' in document) {
49+
await document.startViewTransition(() => {
50+
flushSync(() => {
51+
setTheme(nextTheme);
52+
});
53+
}).ready;
54+
55+
const { top, left, width, height } =
56+
buttonRef.current.getBoundingClientRect();
57+
const y = top + height / 2;
58+
const x = left + width / 2;
59+
60+
const right = window.innerWidth - left;
61+
const bottom = window.innerHeight - top;
62+
const maxRad = Math.hypot(Math.max(left, right), Math.max(top, bottom));
63+
64+
document.documentElement.animate(
65+
{
66+
clipPath: [
67+
`circle(0px at ${x}px ${y}px)`,
68+
`circle(${maxRad}px at ${x}px ${y}px)`,
69+
],
70+
},
71+
{
72+
duration: 700,
73+
easing: "ease-in-out",
74+
pseudoElement: "::view-transition-new(root)",
75+
}
76+
);
77+
} else {
78+
// Fallback for browsers that don't support view transitions
79+
setTheme(nextTheme);
80+
}
81+
};
82+
83+
// Show a placeholder during SSR to prevent hydration mismatch
84+
if (!mounted) {
85+
return (
86+
<button
87+
ref={buttonRef}
88+
className={cn(className)}
89+
aria-label="Toggle theme"
90+
>
91+
<Monitor className="size-4" />
92+
</button>
93+
);
94+
}
95+
96+
return (
97+
<button
98+
ref={buttonRef}
99+
onClick={changeTheme}
100+
className={cn(className)}
101+
aria-label={getThemeLabel()}
102+
title={`Current: ${theme === "system" ? "Auto" : theme === "light" ? "Light" : "Dark"}`}
103+
>
104+
{getThemeIcon()}
105+
</button>
106+
);
107+
};

0 commit comments

Comments
 (0)