@@ -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 ) }
@@ -301,68 +329,72 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
301329
302330 < div className = "flex min-w-0 flex-1 flex-col" >
303331 < div
304- className = "bg-background-secondary border-border relative flex border-b [&>*]:flex-1 "
332+ className = "bg-background-secondary border-border flex items-center border-b px-2 py-2 max-md:gap-2 "
305333 role = "tablist"
306334 aria-label = "Metadata views"
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 (
314- "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" ,
317- "hover:bg-hover hover:border-bg-light" ,
318- "active:scale-95"
342+ "hidden max-md:inline-flex h-7 w-7 items-center justify-center rounded-md" ,
343+ "border border-border-light/80 bg-separator/90 text-xs font-semibold text-foreground" ,
344+ "shadow-[0_1px_2px_rgba(0,0,0,0.2)] transition-all duration-200" ,
345+ "hover:bg-hover hover:border-bg-light active:scale-95"
319346 ) }
320347 >
321- ✕
348+ ×
322349 </ button >
323350
324- < TooltipWrapper inline >
325- < button
326- className = { cn (
327- "w-full py-2.5 px-[15px] border-none border-solid cursor-pointer font-primary text-[13px] font-medium transition-all duration-200" ,
328- selectedTab === "costs"
329- ? "text-white bg-separator border-b-2 border-b-plan-mode"
330- : "bg-transparent text-secondary border-b-2 border-b-transparent hover:bg-background-secondary hover:text-foreground"
331- ) }
332- onClick = { ( ) => setSelectedTab ( "costs" ) }
333- id = { costsTabId }
334- role = "tab"
335- type = "button"
336- aria-selected = { selectedTab === "costs" }
337- aria-controls = { costsPanelId }
338- >
339- Costs
340- </ button >
341- < Tooltip className = "tooltip" position = "bottom" align = "center" >
342- { formatKeybind ( KEYBINDS . COSTS_TAB ) }
343- </ Tooltip >
344- </ TooltipWrapper >
345- < TooltipWrapper inline >
346- < button
347- className = { cn (
348- "w-full py-2.5 px-[15px] border-none border-solid cursor-pointer font-primary text-[13px] font-medium transition-all duration-200" ,
349- selectedTab === "review"
350- ? "text-white bg-separator border-b-2 border-b-plan-mode"
351- : "bg-transparent text-secondary border-b-2 border-b-transparent hover:bg-background-secondary hover:text-foreground"
352- ) }
353- onClick = { ( ) => setSelectedTab ( "review" ) }
354- id = { reviewTabId }
355- role = "tab"
356- type = "button"
357- aria-selected = { selectedTab === "review" }
358- aria-controls = { reviewPanelId }
359- >
360- Review
361- </ button >
362- < Tooltip className = "tooltip" position = "bottom" align = "center" >
363- { formatKeybind ( KEYBINDS . REVIEW_TAB ) }
364- </ Tooltip >
365- </ TooltipWrapper >
351+ < div className = "flex flex-1 gap-1 [&>*]:flex-1" >
352+ < TooltipWrapper inline >
353+ < button
354+ className = { cn (
355+ "w-full py-2 px-[15px] border-none border-solid cursor-pointer font-primary text-[13px] font-medium transition-all duration-200" ,
356+ selectedTab === "costs"
357+ ? "text-white bg-separator border-b-2 border-b-plan-mode"
358+ : "bg-transparent text-secondary border-b-2 border-b-transparent hover:bg-background-secondary hover:text-foreground"
359+ ) }
360+ onClick = { ( ) => setSelectedTab ( "costs" ) }
361+ id = { costsTabId }
362+ role = "tab"
363+ type = "button"
364+ aria-selected = { selectedTab === "costs" }
365+ aria-controls = { costsPanelId }
366+ >
367+ Costs
368+ </ button >
369+ < Tooltip className = "tooltip" position = "bottom" align = "center" >
370+ { formatKeybind ( KEYBINDS . COSTS_TAB ) }
371+ </ Tooltip >
372+ </ TooltipWrapper >
373+ < TooltipWrapper inline >
374+ < button
375+ className = { cn (
376+ "w-full py-2 px-[15px] border-none border-solid cursor-pointer font-primary text-[13px] font-medium transition-all duration-200" ,
377+ selectedTab === "review"
378+ ? "text-white bg-separator border-b-2 border-b-plan-mode"
379+ : "bg-transparent text-secondary border-b-2 border-b-transparent hover:bg-background-secondary hover:text-foreground"
380+ ) }
381+ onClick = { ( ) => {
382+ setSelectedTab ( "review" ) ;
383+ setFocusTrigger ( ( prev ) => prev + 1 ) ;
384+ } }
385+ id = { reviewTabId }
386+ role = "tab"
387+ type = "button"
388+ aria-selected = { selectedTab === "review" }
389+ aria-controls = { reviewPanelId }
390+ >
391+ Review
392+ </ button >
393+ < Tooltip className = "tooltip" position = "bottom" align = "center" >
394+ { formatKeybind ( KEYBINDS . REVIEW_TAB ) }
395+ </ Tooltip >
396+ </ TooltipWrapper >
397+ </ div >
366398 </ div >
367399 < div
368400 className = { cn (
0 commit comments