diff --git a/client/src/App.tsx b/client/src/App.tsx index 396f50514..d3d4a87bb 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -29,7 +29,10 @@ 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"; @@ -164,6 +167,11 @@ const App = () => { const progressTokenRef = useRef(0); const { height: historyPaneHeight, handleDragStart } = useDraggablePane(300); + const { + width: sidebarWidth, + isDragging: isSidebarDragging, + handleDragStart: handleSidebarDragStart, + } = useDraggableSidebar(320); const { connectionStatus, @@ -565,32 +573,58 @@ const App = () => { return (
- +
+ + {/* Drag handle for resizing sidebar */} +
+
{mcpClient ? ( diff --git a/client/src/components/Sidebar.tsx b/client/src/components/Sidebar.tsx index eee8eeaa6..ce667241b 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 5c0e35737..4ee5af541 100644 --- a/client/src/lib/hooks/useDraggablePane.ts +++ b/client/src/lib/hooks/useDraggablePane.ts @@ -51,3 +51,55 @@ 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, + }; +}