@@ -167,35 +167,63 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
167167 false
168168 ) ;
169169
170+ // Single render point for VerticalTokenMeter
171+ // Shows when: (1) collapsed, OR (2) Review tab is active
172+ const showMeter = showCollapsed || selectedTab === "review" ;
173+ const verticalMeter = showMeter ? < VerticalTokenMeter data = { verticalMeterData } /> : null ;
174+
175+ // Track manual expansion to prevent auto-collapse immediately after user opens sidebar
176+ const manualExpandRef = React . useRef ( false ) ;
177+
178+ const openSidebar = React . useCallback (
179+ ( manual : boolean ) => {
180+ manualExpandRef . current = manual ;
181+ setShowCollapsed ( false ) ;
182+ } ,
183+ [ setShowCollapsed ]
184+ ) ;
185+
186+ const closeSidebar = React . useCallback ( ( ) => {
187+ manualExpandRef . current = false ;
188+ setShowCollapsed ( true ) ;
189+ } , [ setShowCollapsed ] ) ;
190+
191+ // Expose open function to parent (for mobile header button)
192+ React . useEffect ( ( ) => {
193+ if ( onMountOpenCallback ) {
194+ onMountOpenCallback ( ( ) => openSidebar ( true ) ) ;
195+ }
196+ } , [ onMountOpenCallback , openSidebar ] ) ;
197+
198+ const openSidebarAuto = React . useCallback ( ( ) => openSidebar ( false ) , [ openSidebar ] ) ;
199+ const openSidebarManual = React . useCallback ( ( ) => openSidebar ( true ) , [ openSidebar ] ) ;
200+
170201 React . useEffect ( ( ) => {
171202 // Never collapse when Review tab is active - code review needs space
172203 if ( selectedTab === "review" ) {
173204 if ( showCollapsed ) {
174- setShowCollapsed ( false ) ;
205+ openSidebarAuto ( ) ;
175206 }
207+ manualExpandRef . current = false ;
208+ return ;
209+ }
210+
211+ // If user manually expanded on mobile, keep sidebar open until they close it
212+ if ( manualExpandRef . current ) {
176213 return ;
177214 }
178215
179- // Normal hysteresis for Costs/Tools tabs
180216 if ( chatAreaWidth <= COLLAPSE_THRESHOLD ) {
181- setShowCollapsed ( true ) ;
217+ if ( ! showCollapsed ) {
218+ closeSidebar ( ) ;
219+ }
182220 } else if ( chatAreaWidth >= EXPAND_THRESHOLD ) {
183- setShowCollapsed ( false ) ;
221+ if ( showCollapsed ) {
222+ openSidebarAuto ( ) ;
223+ }
184224 }
185225 // Between thresholds: maintain current state (no change)
186- } , [ chatAreaWidth , selectedTab , showCollapsed , setShowCollapsed ] ) ;
187-
188- // Single render point for VerticalTokenMeter
189- // Shows when: (1) collapsed, OR (2) Review tab is active
190- const showMeter = showCollapsed || selectedTab === "review" ;
191- const verticalMeter = showMeter ? < VerticalTokenMeter data = { verticalMeterData } /> : null ;
192-
193- // Expose open function to parent (for mobile header button)
194- React . useEffect ( ( ) => {
195- if ( onMountOpenCallback ) {
196- onMountOpenCallback ( ( ) => setShowCollapsed ( false ) ) ;
197- }
198- } , [ onMountOpenCallback , setShowCollapsed ] ) ;
226+ } , [ chatAreaWidth , selectedTab , showCollapsed , closeSidebar , openSidebarAuto ] ) ;
199227
200228 // Swipe gesture detection for mobile - right-to-left swipe to open sidebar
201229 React . useEffect ( ( ) => {
@@ -243,11 +271,11 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
243271
244272 // Open sidebar on left swipe from right edge when collapsed
245273 if ( isLeftSwipe && isHorizontal && isFastEnough && showCollapsed ) {
246- setShowCollapsed ( false ) ;
274+ openSidebarManual ( ) ;
247275 }
248276 // Close sidebar on right swipe when open (from anywhere on screen)
249277 else if ( isRightSwipe && isHorizontal && isFastEnough && ! showCollapsed ) {
250- setShowCollapsed ( true ) ;
278+ closeSidebar ( ) ;
251279 }
252280 } ;
253281
@@ -258,15 +286,15 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
258286 window . removeEventListener ( "touchstart" , handleTouchStart ) ;
259287 window . removeEventListener ( "touchend" , handleTouchEnd ) ;
260288 } ;
261- } , [ showCollapsed , setShowCollapsed ] ) ;
289+ } , [ showCollapsed , closeSidebar , openSidebarManual ] ) ;
262290
263291 return (
264292 < >
265293 { /* Backdrop overlay - only on mobile when sidebar is expanded */ }
266294 { ! showCollapsed && (
267295 < div
268296 className = "fixed inset-0 z-[998] hidden bg-black/50 backdrop-blur-sm max-md:block"
269- onClick = { ( ) => setShowCollapsed ( true ) }
297+ onClick = { closeSidebar }
270298 aria-hidden = "true"
271299 />
272300 ) }
@@ -307,18 +335,19 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
307335 >
308336 { /* Close button - only visible on mobile */ }
309337 < button
310- onClick = { ( ) => setShowCollapsed ( true ) }
338+ onClick = { closeSidebar }
311339 title = "Close panel"
312340 aria-label = "Close panel"
313341 className = { cn (
314342 "hidden max-md:flex absolute top-2 right-2 z-10" ,
315- "w-8 h-8 bg-separator border border-border-light rounded cursor-pointer" ,
316- "items-center justify-center text-foreground transition-all duration-200" ,
343+ "h-6 w-6 rounded-md bg-separator/90 border border-border-light/80" ,
344+ "items-center justify-center text-[11px] font-semibold text-foreground transition-all duration-200" ,
345+ "shadow-[0_1px_2px_rgba(0,0,0,0.2)]" ,
317346 "hover:bg-hover hover:border-bg-light" ,
318347 "active:scale-95"
319348 ) }
320349 >
321- ✕
350+ ×
322351 </ button >
323352
324353 < TooltipWrapper inline >
0 commit comments