Skip to content

Commit 97d4699

Browse files
authored
Merge pull request #73 from modelcontextprotocol/ashwin/darkmodetoggle
make theme selectable in UI, store setting in localstorage
2 parents 5139e72 + 11b891c commit 97d4699

File tree

4 files changed

+79
-37
lines changed

4 files changed

+79
-37
lines changed

client/src/App.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import { useCallback, useEffect, useRef, useState } from "react";
2929
import {
3030
Notification,
3131
StdErrNotification,
32-
StdErrNotificationSchema
32+
StdErrNotificationSchema,
3333
} from "./lib/notificationTypes";
3434

3535
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
@@ -54,7 +54,6 @@ import RootsTab from "./components/RootsTab";
5454
import SamplingTab, { PendingRequest } from "./components/SamplingTab";
5555
import Sidebar from "./components/Sidebar";
5656
import ToolsTab from "./components/ToolsTab";
57-
import useDarkModeSync from "./lib/useDarkModeSync";
5857

5958
const DEFAULT_REQUEST_TIMEOUT_MSEC = 10000;
6059

@@ -144,8 +143,6 @@ const App = () => {
144143
const dragStartY = useRef<number>(0);
145144
const dragStartHeight = useRef<number>(0);
146145

147-
useDarkModeSync();
148-
149146
const handleDragStart = useCallback(
150147
(e: React.MouseEvent) => {
151148
setIsDragging(true);

client/src/components/Sidebar.tsx

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useState } from "react";
22

3-
import { Play, ChevronDown, ChevronRight, Settings } from "lucide-react";
3+
import { Play, ChevronDown, ChevronRight } from "lucide-react";
44
import { Button } from "@/components/ui/button";
55
import { Input } from "@/components/ui/input";
66
import {
@@ -12,6 +12,8 @@ import {
1212
} from "@/components/ui/select";
1313
import { StdErrNotification } from "@/lib/notificationTypes";
1414

15+
import useTheme from "../lib/useTheme";
16+
1517
interface SidebarProps {
1618
connectionStatus: "disconnected" | "connected" | "error";
1719
transportType: "stdio" | "sse";
@@ -43,13 +45,15 @@ const Sidebar = ({
4345
onConnect,
4446
stdErrNotifications,
4547
}: SidebarProps) => {
48+
const [theme, setTheme] = useTheme();
4649
const [showEnvVars, setShowEnvVars] = useState(false);
4750

4851
return (
4952
<div className="w-80 bg-card border-r border-border flex flex-col h-full">
50-
<div className="flex items-center p-4 border-b border-gray-200">
51-
<Settings className="w-6 h-6 text-gray-500" />
52-
<h1 className="ml-2 text-lg font-semibold">MCP Inspector</h1>
53+
<div className="flex items-center justify-between p-4 border-b border-gray-200">
54+
<div className="flex items-center">
55+
<h1 className="ml-2 text-lg font-semibold">MCP Inspector</h1>
56+
</div>
5357
</div>
5458

5559
<div className="p-4 flex-1 overflow-auto">
@@ -212,6 +216,25 @@ const Sidebar = ({
212216
</div>
213217
</div>
214218
</div>
219+
<div className="p-4 border-t">
220+
<div className="flex items-center space-x-2">
221+
<Select
222+
value={theme}
223+
onValueChange={(value: string) =>
224+
setTheme(value as "system" | "light" | "dark")
225+
}
226+
>
227+
<SelectTrigger className="w-[120px]" id="theme-select">
228+
<SelectValue />
229+
</SelectTrigger>
230+
<SelectContent>
231+
<SelectItem value="system">System</SelectItem>
232+
<SelectItem value="light">Light</SelectItem>
233+
<SelectItem value="dark">Dark</SelectItem>
234+
</SelectContent>
235+
</Select>
236+
</div>
237+
</div>
215238
</div>
216239
);
217240
};

client/src/lib/useDarkModeSync.ts

Lines changed: 0 additions & 29 deletions
This file was deleted.

client/src/lib/useTheme.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { useCallback, useEffect, useState } from "react";
2+
3+
type Theme = "light" | "dark" | "system";
4+
5+
const useTheme = (): [Theme, (mode: Theme) => void] => {
6+
const [theme, setTheme] = useState<Theme>(() => {
7+
const savedTheme = localStorage.getItem("theme") as Theme;
8+
return savedTheme || "system";
9+
});
10+
11+
useEffect(() => {
12+
const darkModeMediaQuery = window.matchMedia(
13+
"(prefers-color-scheme: dark)",
14+
);
15+
const handleDarkModeChange = (e: MediaQueryListEvent) => {
16+
if (theme === "system") {
17+
updateDocumentTheme(e.matches ? "dark" : "light");
18+
}
19+
};
20+
21+
const updateDocumentTheme = (newTheme: "light" | "dark") => {
22+
document.documentElement.classList.toggle("dark", newTheme === "dark");
23+
};
24+
25+
// Set initial theme based on current mode
26+
if (theme === "system") {
27+
updateDocumentTheme(darkModeMediaQuery.matches ? "dark" : "light");
28+
} else {
29+
updateDocumentTheme(theme);
30+
}
31+
32+
darkModeMediaQuery.addEventListener("change", handleDarkModeChange);
33+
34+
return () => {
35+
darkModeMediaQuery.removeEventListener("change", handleDarkModeChange);
36+
};
37+
}, [theme]);
38+
39+
return [
40+
theme,
41+
useCallback((newTheme: Theme) => {
42+
setTheme(newTheme);
43+
localStorage.setItem("theme", newTheme);
44+
if (newTheme !== "system") {
45+
document.documentElement.classList.toggle("dark", newTheme === "dark");
46+
}
47+
}, []),
48+
];
49+
};
50+
51+
export default useTheme;

0 commit comments

Comments
 (0)