diff --git a/ui/desktop/src/components/Layout/AppLayout.tsx b/ui/desktop/src/components/Layout/AppLayout.tsx index 9d7be7017f87..da5527d06148 100644 --- a/ui/desktop/src/components/Layout/AppLayout.tsx +++ b/ui/desktop/src/components/Layout/AppLayout.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { IpcRendererEvent } from 'electron'; import { Outlet, useLocation } from 'react-router-dom'; import { motion } from 'framer-motion'; @@ -9,7 +9,7 @@ import ChatSessionsContainer from '../ChatSessionsContainer'; import { useChatContext } from '../../contexts/ChatContext'; import { NavigationProvider, useNavigationContext } from './NavigationContext'; import { Navigation } from './NavigationPanel'; -import { NAV_DIMENSIONS, Z_INDEX } from './constants'; +import { Z_INDEX } from './constants'; import { cn } from '../../utils'; import { UserInput } from '../../types/message'; @@ -54,7 +54,41 @@ const AppLayoutContent: React.FC = ({ activeSessions }) = return () => window.electron.off('fullscreen-change', handler); }, [safeIsMacOS]); - const { isNavExpanded, setIsNavExpanded } = useNavigationContext(); + const { isNavExpanded, setIsNavExpanded, navWidth, setNavWidth } = useNavigationContext(); + const [isDragging, setIsDragging] = useState(false); + const isResizing = useRef(false); + const startX = useRef(0); + const startWidth = useRef(0); + + const handleResizeMouseDown = useCallback( + (e: React.MouseEvent) => { + isResizing.current = true; + startX.current = e.clientX; + startWidth.current = navWidth; + setIsDragging(true); + e.preventDefault(); + }, + [navWidth] + ); + + useEffect(() => { + const handleMouseMove = (e: MouseEvent) => { + if (!isResizing.current) return; + setNavWidth(startWidth.current + (e.clientX - startX.current)); + }; + const handleMouseUp = () => { + if (isResizing.current) { + isResizing.current = false; + setIsDragging(false); + } + }; + window.addEventListener('mousemove', handleMouseMove); + window.addEventListener('mouseup', handleMouseUp); + return () => { + window.removeEventListener('mousemove', handleMouseMove); + window.removeEventListener('mouseup', handleMouseUp); + }; + }, [setNavWidth]); if (!chatContext) { throw new Error('AppLayoutContent must be used within ChatProvider'); @@ -93,14 +127,22 @@ const AppLayoutContent: React.FC = ({ activeSessions }) =
+ {isNavExpanded && ( +
+ )} {/* Main content — no border / no card; just flows on the canvas. */} diff --git a/ui/desktop/src/components/Layout/NavigationContext.tsx b/ui/desktop/src/components/Layout/NavigationContext.tsx index 0a14d7d63c38..a92dffb19279 100644 --- a/ui/desktop/src/components/Layout/NavigationContext.tsx +++ b/ui/desktop/src/components/Layout/NavigationContext.tsx @@ -7,6 +7,7 @@ import React, { useRef, useState, } from 'react'; +import { NAV_DIMENSIONS } from './constants'; /** * When the window is narrower than this many CSS pixels, we auto-collapse @@ -15,9 +16,14 @@ import React, { */ const NARROW_WINDOW_THRESHOLD = 700; +export const MIN_NAV_WIDTH = 160; +export const MAX_NAV_WIDTH = 400; + interface NavigationContextValue { isNavExpanded: boolean; setIsNavExpanded: (expanded: boolean) => void; + navWidth: number; + setNavWidth: (width: number) => void; } const NavigationContext = createContext(null); @@ -49,6 +55,23 @@ export const NavigationProvider: React.FC = ({ children localStorage.setItem('navigation_expanded', String(expanded)); }, []); + const [navWidth, setNavWidthState] = useState(() => { + const stored = localStorage.getItem('navigation_width'); + if (stored) { + const parsed = parseInt(stored, 10); + if (!isNaN(parsed) && parsed >= MIN_NAV_WIDTH && parsed <= MAX_NAV_WIDTH) { + return parsed; + } + } + return NAV_DIMENSIONS.NAV_WIDTH; + }); + + const setNavWidth = useCallback((width: number) => { + const clamped = Math.min(MAX_NAV_WIDTH, Math.max(MIN_NAV_WIDTH, width)); + setNavWidthState(clamped); + localStorage.setItem('navigation_width', String(clamped)); + }, []); + const isNavExpandedRef = useRef(isNavExpanded); useEffect(() => { isNavExpandedRef.current = isNavExpanded; @@ -90,6 +113,8 @@ export const NavigationProvider: React.FC = ({ children const value: NavigationContextValue = { isNavExpanded, setIsNavExpanded, + navWidth, + setNavWidth, }; return {children};