diff --git a/client/src/App.tsx b/client/src/App.tsx index b2132c3ed..c881869a4 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -29,7 +29,7 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { Notification, StdErrNotification, - StdErrNotificationSchema + StdErrNotificationSchema, } from "./lib/notificationTypes"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; @@ -54,7 +54,6 @@ import RootsTab from "./components/RootsTab"; import SamplingTab, { PendingRequest } from "./components/SamplingTab"; import Sidebar from "./components/Sidebar"; import ToolsTab from "./components/ToolsTab"; -import useDarkModeSync from "./lib/useDarkModeSync"; const DEFAULT_REQUEST_TIMEOUT_MSEC = 10000; @@ -144,8 +143,6 @@ const App = () => { const dragStartY = useRef(0); const dragStartHeight = useRef(0); - useDarkModeSync(); - const handleDragStart = useCallback( (e: React.MouseEvent) => { setIsDragging(true); diff --git a/client/src/components/Sidebar.tsx b/client/src/components/Sidebar.tsx index 7d8a781be..acbb7d8bf 100644 --- a/client/src/components/Sidebar.tsx +++ b/client/src/components/Sidebar.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; -import { Play, ChevronDown, ChevronRight, Settings } from "lucide-react"; +import { Play, ChevronDown, ChevronRight } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { @@ -12,6 +12,8 @@ import { } from "@/components/ui/select"; import { StdErrNotification } from "@/lib/notificationTypes"; +import useTheme from "../lib/useTheme"; + interface SidebarProps { connectionStatus: "disconnected" | "connected" | "error"; transportType: "stdio" | "sse"; @@ -43,13 +45,15 @@ const Sidebar = ({ onConnect, stdErrNotifications, }: SidebarProps) => { + const [theme, setTheme] = useTheme(); const [showEnvVars, setShowEnvVars] = useState(false); return (
-
- -

MCP Inspector

+
+
+

MCP Inspector

+
@@ -212,6 +216,25 @@ const Sidebar = ({
+
+
+ +
+
); }; diff --git a/client/src/lib/useDarkModeSync.ts b/client/src/lib/useDarkModeSync.ts deleted file mode 100644 index 8d612b5e7..000000000 --- a/client/src/lib/useDarkModeSync.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { useEffect } from "react"; - -// Listen for changes to the user's preferred color scheme -const useDarkModeSync = () => { - useEffect(() => { - const darkModeMediaQuery = window.matchMedia( - "(prefers-color-scheme: dark)", - ); - const handleDarkModeChange = (e: MediaQueryListEvent) => { - if (e.matches) { - document.documentElement.classList.add("dark"); - } else { - document.documentElement.classList.remove("dark"); - } - }; - - if (darkModeMediaQuery.matches) { - document.documentElement.classList.add("dark"); - } - - darkModeMediaQuery.addEventListener("change", handleDarkModeChange); - - return () => { - darkModeMediaQuery.removeEventListener("change", handleDarkModeChange); - }; - }, []); -}; - -export default useDarkModeSync; diff --git a/client/src/lib/useTheme.ts b/client/src/lib/useTheme.ts new file mode 100644 index 000000000..c73159b62 --- /dev/null +++ b/client/src/lib/useTheme.ts @@ -0,0 +1,51 @@ +import { useCallback, useEffect, useState } from "react"; + +type Theme = "light" | "dark" | "system"; + +const useTheme = (): [Theme, (mode: Theme) => void] => { + const [theme, setTheme] = useState(() => { + const savedTheme = localStorage.getItem("theme") as Theme; + return savedTheme || "system"; + }); + + useEffect(() => { + const darkModeMediaQuery = window.matchMedia( + "(prefers-color-scheme: dark)", + ); + const handleDarkModeChange = (e: MediaQueryListEvent) => { + if (theme === "system") { + updateDocumentTheme(e.matches ? "dark" : "light"); + } + }; + + const updateDocumentTheme = (newTheme: "light" | "dark") => { + document.documentElement.classList.toggle("dark", newTheme === "dark"); + }; + + // Set initial theme based on current mode + if (theme === "system") { + updateDocumentTheme(darkModeMediaQuery.matches ? "dark" : "light"); + } else { + updateDocumentTheme(theme); + } + + darkModeMediaQuery.addEventListener("change", handleDarkModeChange); + + return () => { + darkModeMediaQuery.removeEventListener("change", handleDarkModeChange); + }; + }, [theme]); + + return [ + theme, + useCallback((newTheme: Theme) => { + setTheme(newTheme); + localStorage.setItem("theme", newTheme); + if (newTheme !== "system") { + document.documentElement.classList.toggle("dark", newTheme === "dark"); + } + }, []), + ]; +}; + +export default useTheme;