From bff09e04095b4b4ca9dc59eff79b617e99faf682 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Tue, 27 May 2025 17:28:04 -0700 Subject: [PATCH] Resizable left sidebar Make left sidebar resizable so long URLs can be viewed in their entirety. Fixes https://github.com/modelcontextprotocol/inspector/issues/457 Vibe-coded with Cursor --- client/src/App.tsx | 67 ++++++++++++++---------- client/src/components/Sidebar.tsx | 2 +- client/src/lib/hooks/useDraggablePane.ts | 49 +++++++++++++++++ 3 files changed, 90 insertions(+), 28 deletions(-) diff --git a/client/src/App.tsx b/client/src/App.tsx index 68065e24..b57107d3 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -28,7 +28,7 @@ import React, { useState, } from "react"; import { useConnection } from "./lib/hooks/useConnection"; -import { useDraggablePane } from "./lib/hooks/useDraggablePane"; +import { useDraggablePane, useDraggableSidebar } from "./lib/hooks/useDraggablePane"; import { StdErrNotification } from "./lib/notificationTypes"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; @@ -163,6 +163,7 @@ const App = () => { const progressTokenRef = useRef(0); const { height: historyPaneHeight, handleDragStart } = useDraggablePane(300); + const { width: sidebarWidth, isDragging: isSidebarDragging, handleDragStart: handleSidebarDragStart } = useDraggableSidebar(320); const { connectionStatus, @@ -562,32 +563,44 @@ const App = () => { return (
- +
+ + {/* Drag handle for resizing sidebar */} +
+
{mcpClient ? ( diff --git a/client/src/components/Sidebar.tsx b/client/src/components/Sidebar.tsx index 516f48d5..2ae3f6b0 100644 --- a/client/src/components/Sidebar.tsx +++ b/client/src/components/Sidebar.tsx @@ -214,7 +214,7 @@ const Sidebar = ({ }, [generateMCPServerFile, toast, reportError]); return ( -
+

diff --git a/client/src/lib/hooks/useDraggablePane.ts b/client/src/lib/hooks/useDraggablePane.ts index 5c0e3573..77e19b8d 100644 --- a/client/src/lib/hooks/useDraggablePane.ts +++ b/client/src/lib/hooks/useDraggablePane.ts @@ -51,3 +51,52 @@ export function useDraggablePane(initialHeight: number) { handleDragStart, }; } + +export function useDraggableSidebar(initialWidth: number) { + const [width, setWidth] = useState(initialWidth); + const [isDragging, setIsDragging] = useState(false); + const dragStartX = useRef(0); + const dragStartWidth = useRef(0); + + const handleDragStart = useCallback( + (e: React.MouseEvent) => { + setIsDragging(true); + dragStartX.current = e.clientX; + dragStartWidth.current = width; + document.body.style.userSelect = "none"; + }, + [width], + ); + + const handleDragMove = useCallback( + (e: MouseEvent) => { + if (!isDragging) return; + const deltaX = e.clientX - dragStartX.current; + const newWidth = Math.max(200, Math.min(600, dragStartWidth.current + deltaX)); + setWidth(newWidth); + }, + [isDragging], + ); + + const handleDragEnd = useCallback(() => { + setIsDragging(false); + document.body.style.userSelect = ""; + }, []); + + useEffect(() => { + if (isDragging) { + window.addEventListener("mousemove", handleDragMove); + window.addEventListener("mouseup", handleDragEnd); + return () => { + window.removeEventListener("mousemove", handleDragMove); + window.removeEventListener("mouseup", handleDragEnd); + }; + } + }, [isDragging, handleDragMove, handleDragEnd]); + + return { + width, + isDragging, + handleDragStart, + }; +}