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, + }; +}