From e4eb2421bc8fd6425ea2f08c55db5fa4348ce60d Mon Sep 17 00:00:00 2001 From: Tony Giorgio Date: Tue, 2 Dec 2025 15:52:34 -0600 Subject: [PATCH] feat: add collapsible sidebar on desktop/web views Allow users to collapse/expand the history sidebar on desktop and web views. The toggle button is now visible on all screen sizes. Click-outside-to-close behavior remains mobile-only to preserve desktop UX. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --- frontend/src/components/Sidebar.tsx | 23 ++++---- frontend/src/components/UnifiedChat.tsx | 72 +++++++++++++------------ 2 files changed, 50 insertions(+), 45 deletions(-) diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index 90bb7e105..d0b7e865f 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -23,8 +23,8 @@ export function Sidebar({ const searchInputRef = useRef(null); async function addChat() { - // If sidebar is open, close it - if (isOpen) { + // If sidebar is open on mobile, close it + if (isOpen && isMobile) { onToggle(); } @@ -74,10 +74,14 @@ export function Sidebar({ const sidebarRef = useRef(null); + // Use the centralized hook for mobile detection + const isMobile = useIsMobile(); + // Modified click outside handler to ignore clicks in dropdowns and dialogs + // Only applies on mobile - desktop users use the toggle button const handleClickOutside = useCallback( (event: MouseEvent | TouchEvent) => { - if (isOpen) { + if (isOpen && isMobile) { // Check if the click was inside a dropdown or dialog const target = event.target as HTMLElement; const isInDropdown = target.closest('[role="menu"]'); @@ -89,14 +93,11 @@ export function Sidebar({ } } }, - [isOpen, onToggle] + [isOpen, onToggle, isMobile] ); useClickOutside(sidebarRef, handleClickOutside); - // Use the centralized hook for mobile detection - const isMobile = useIsMobile(); - // Track if component is mounted to prevent state updates after unmount const isMountedRef = useRef(true); useLayoutEffect(() => { @@ -135,20 +136,20 @@ export function Sidebar({ ref={sidebarRef} className={cn([ "fixed md:static z-10 h-full overflow-y-hidden", - isOpen ? "block w-[280px]" : "hidden md:block md:w-[280px]" + isOpen ? "block w-[280px]" : "hidden" ])} >
{/* Header section matching UnifiedChat's h-14 */}
- ); diff --git a/frontend/src/components/UnifiedChat.tsx b/frontend/src/components/UnifiedChat.tsx index 4da81690b..a61e693f1 100644 --- a/frontend/src/components/UnifiedChat.tsx +++ b/frontend/src/components/UnifiedChat.tsx @@ -2255,7 +2255,9 @@ export function UnifiedChat() { }; return ( -
+
{/* Use the existing Sidebar component */} @@ -2271,9 +2273,9 @@ export function UnifiedChat() {
)} - {/* Mobile sidebar toggle */} + {/* Sidebar toggle - visible when sidebar is closed */} {!isSidebarOpen && ( -
+
)} @@ -2289,37 +2291,39 @@ export function UnifiedChat() { > {conversation?.metadata?.title || "Chat"} - {/* Mobile new chat button - positioned on the right */} - + {/* New chat button - visible when sidebar is closed */} + {!isSidebarOpen && ( + + )}
)}