11import React from "react" ;
2- import { RIGHT_SIDEBAR_TAB_KEY , RIGHT_SIDEBAR_COLLAPSED_KEY } from "@/common/constants/storage" ;
2+ import { RIGHT_SIDEBAR_TAB_KEY } from "@/common/constants/storage" ;
33import { usePersistedState } from "@/browser/hooks/usePersistedState" ;
44import { useWorkspaceUsage , useWorkspaceStatsSnapshot } from "@/browser/stores/WorkspaceStore" ;
5- import { useProviderOptions } from "@/browser/hooks/useProviderOptions" ;
6- import { useResizeObserver } from "@/browser/hooks/useResizeObserver" ;
75import { useFeatureFlags } from "@/browser/contexts/FeatureFlagsContext" ;
8- import { useAutoCompactionSettings } from "@/browser/hooks/useAutoCompactionSettings" ;
96import { ErrorBoundary } from "./ErrorBoundary" ;
107import { CostsTab } from "./RightSidebar/CostsTab" ;
118import { StatsTab } from "./RightSidebar/StatsTab" ;
12- import { VerticalTokenMeter } from "./RightSidebar/VerticalTokenMeter" ;
139import { ReviewPanel } from "./RightSidebar/CodeReview/ReviewPanel" ;
14- import { calculateTokenMeterData } from "@/common/utils/tokens/tokenMeterUtils" ;
1510import { sumUsageHistory , type ChatUsageDisplay } from "@/common/utils/tokens/usageAggregator" ;
1611import { matchesKeybind , KEYBINDS , formatKeybind } from "@/browser/utils/ui/keybinds" ;
1712import { Tooltip , TooltipTrigger , TooltipContent } from "./ui/tooltip" ;
@@ -34,7 +29,6 @@ function formatTabDuration(ms: number): string {
3429}
3530
3631interface SidebarContainerProps {
37- collapsed : boolean ;
3832 wide ?: boolean ;
3933 /** Custom width from drag-resize (persisted per-tab by AIView) */
4034 customWidth ?: number ;
@@ -49,37 +43,31 @@ interface SidebarContainerProps {
4943 * SidebarContainer - Main sidebar wrapper with dynamic width
5044 *
5145 * Width priority (first match wins):
52- * 1. collapsed (20px) - Shows vertical token meter only
53- * 2. customWidth - From drag-resize (persisted per-tab)
54- * 3. wide - Auto-calculated max width for Review tab (when not drag-resizing)
55- * 4. default (300px) - Costs tab when no customWidth saved
46+ * 1. customWidth - From drag-resize (persisted per-tab)
47+ * 2. wide - Auto-calculated max width for Review tab (when not drag-resizing)
48+ * 3. default (300px) - Costs tab when no customWidth saved
5649 */
5750const SidebarContainer : React . FC < SidebarContainerProps > = ( {
58- collapsed,
5951 wide,
6052 customWidth,
6153 isResizing,
6254 children,
6355 role,
6456 "aria-label" : ariaLabel ,
6557} ) => {
66- const width = collapsed
67- ? "20px"
68- : customWidth
69- ? `${ customWidth } px`
70- : wide
71- ? "min(1200px, calc(100vw - 400px))"
72- : "300px" ;
58+ const width = customWidth
59+ ? `${ customWidth } px`
60+ : wide
61+ ? "min(1200px, calc(100vw - 400px))"
62+ : "300px" ;
7363
7464 return (
7565 < div
7666 className = { cn (
7767 "bg-sidebar border-l border-border-light flex flex-col overflow-hidden flex-shrink-0" ,
7868 ! isResizing && "transition-[width] duration-200" ,
79- collapsed && "sticky right-0 z-10 shadow-[-2px_0_4px_rgba(0,0,0,0.2)]" ,
80- // Mobile: Show vertical meter when collapsed (20px), full width when expanded
8169 "max-md:border-l-0 max-md:border-t max-md:border-border-light" ,
82- ! collapsed && "max-md:w-full max-md:relative max-md:max-h-[50vh]"
70+ "max-md:w-full max-md:relative max-md:max-h-[50vh]"
8371 ) }
8472 style = { { width } }
8573 role = { role }
@@ -97,7 +85,6 @@ export type { TabType };
9785interface RightSidebarProps {
9886 workspaceId : string ;
9987 workspacePath : string ;
100- chatAreaRef : React . RefObject < HTMLDivElement > ;
10188 /** Custom width in pixels (persisted per-tab, provided by AIView) */
10289 width ?: number ;
10390 /** Drag start handler for resize */
@@ -113,7 +100,6 @@ interface RightSidebarProps {
113100const RightSidebarComponent : React . FC < RightSidebarProps > = ( {
114101 workspaceId,
115102 workspacePath,
116- chatAreaRef,
117103 width,
118104 onStartResize,
119105 isResizing = false ,
@@ -160,10 +146,6 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
160146
161147 const usage = useWorkspaceUsage ( workspaceId ) ;
162148
163- const { options } = useProviderOptions ( ) ;
164- const use1M = options . anthropic ?. use1MContext ?? false ;
165- const chatAreaSize = useResizeObserver ( chatAreaRef ) ;
166-
167149 const baseId = `right-sidebar-${ workspaceId } ` ;
168150 const costsTabId = `${ baseId } -tab-costs` ;
169151 const statsTabId = `${ baseId } -tab-stats` ;
@@ -172,10 +154,6 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
172154 const statsPanelId = `${ baseId } -panel-stats` ;
173155 const reviewPanelId = `${ baseId } -panel-review` ;
174156
175- // Use lastContextUsage for context window display (last step = actual context size)
176- const lastUsage = usage ?. liveUsage ?? usage ?. lastContextUsage ;
177- const model = lastUsage ?. model ?? null ;
178-
179157 // Calculate session cost for tab display
180158 const sessionCost = React . useMemo ( ( ) => {
181159 const parts : ChatUsageDisplay [ ] = [ ] ;
@@ -206,90 +184,15 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
206184 return total > 0 ? total : null ;
207185 } ) ( ) ;
208186
209- // Auto-compaction settings: threshold per-model
210- const { threshold : autoCompactThreshold , setThreshold : setAutoCompactThreshold } =
211- useAutoCompactionSettings ( workspaceId , model ) ;
212-
213- // Memoize vertical meter data calculation to prevent unnecessary re-renders
214- const verticalMeterData = React . useMemo ( ( ) => {
215- return lastUsage
216- ? calculateTokenMeterData ( lastUsage , model ?? "unknown" , use1M , true )
217- : { segments : [ ] , totalTokens : 0 , totalPercentage : 0 } ;
218- } , [ lastUsage , model , use1M ] ) ;
219-
220- // Calculate if we should show collapsed view with hysteresis
221- // Strategy: Observe ChatArea width directly (independent of sidebar width)
222- // - ChatArea has min-width: 750px and flex: 1
223- // - Use hysteresis to prevent oscillation:
224- // * Collapse when chatAreaWidth <= 800px (tight space)
225- // * Expand when chatAreaWidth >= 1100px (lots of space)
226- // * Between 800-1100: maintain current state (dead zone)
227- const COLLAPSE_THRESHOLD = 800 ; // Collapse below this
228- const EXPAND_THRESHOLD = 1100 ; // Expand above this
229- const chatAreaWidth = chatAreaSize ?. width ?? 1000 ; // Default to large to avoid flash
230-
231- // Persist collapsed state globally (not per-workspace) since chat area width is shared
232- // This prevents animation flash when switching workspaces - sidebar maintains its state
233- const [ showCollapsed , setShowCollapsed ] = usePersistedState < boolean > (
234- RIGHT_SIDEBAR_COLLAPSED_KEY ,
235- false
236- ) ;
237-
238- React . useEffect ( ( ) => {
239- // Never collapse when Review tab is active - code review needs space
240- if ( selectedTab === "review" ) {
241- if ( showCollapsed ) {
242- setShowCollapsed ( false ) ;
243- }
244- return ;
245- }
246-
247- // If the sidebar is custom-resized (wider than the default Costs width),
248- // auto-collapse based on chatAreaWidth can oscillate between expanded and
249- // collapsed states (because collapsed is 20px but expanded can be much wider),
250- // which looks like a constant flash. In that case, keep it expanded and let
251- // the user resize manually.
252- if ( width !== undefined && width > 300 ) {
253- if ( showCollapsed ) {
254- setShowCollapsed ( false ) ;
255- }
256- return ;
257- }
258-
259- // Normal hysteresis for Costs/Tools tabs
260- if ( chatAreaWidth <= COLLAPSE_THRESHOLD ) {
261- setShowCollapsed ( true ) ;
262- } else if ( chatAreaWidth >= EXPAND_THRESHOLD ) {
263- setShowCollapsed ( false ) ;
264- }
265- // Between thresholds: maintain current state (no change)
266- } , [ chatAreaWidth , selectedTab , showCollapsed , setShowCollapsed , width ] ) ;
267-
268- // Single render point for VerticalTokenMeter
269- // Shows when: (1) collapsed, OR (2) Review tab is active
270- const showMeter = showCollapsed || selectedTab === "review" ;
271- const autoCompactionProps = React . useMemo (
272- ( ) => ( {
273- threshold : autoCompactThreshold ,
274- setThreshold : setAutoCompactThreshold ,
275- } ) ,
276- [ autoCompactThreshold , setAutoCompactThreshold ]
277- ) ;
278- const verticalMeter = showMeter ? (
279- < VerticalTokenMeter data = { verticalMeterData } autoCompaction = { autoCompactionProps } />
280- ) : null ;
281-
282187 return (
283188 < SidebarContainer
284- collapsed = { showCollapsed }
285189 wide = { selectedTab === "review" && ! width } // Auto-wide only if not drag-resizing
286190 customWidth = { width } // Per-tab resized width from AIView
287191 isResizing = { isResizing }
288192 role = "complementary"
289193 aria-label = "Workspace insights"
290194 >
291- { /* Full view when not collapsed */ }
292- < div className = { cn ( "flex-row h-full" , ! showCollapsed ? "flex" : "hidden" ) } >
195+ < div className = "flex h-full flex-row" >
293196 { /* Resize handle (left edge) */ }
294197 { onStartResize && (
295198 < div
@@ -301,11 +204,6 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
301204 />
302205 ) }
303206
304- { /* Render meter when Review tab is active */ }
305- { selectedTab === "review" && (
306- < div className = "bg-sidebar flex w-5 shrink-0 flex-col" > { verticalMeter } </ div >
307- ) }
308-
309207 < div className = "flex min-w-0 flex-1 flex-col" >
310208 < div
311209 className = "border-border-light flex gap-1 border-b px-2 py-1.5"
@@ -442,8 +340,6 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
442340 </ div >
443341 </ div >
444342 </ div >
445- { /* Render meter in collapsed view when sidebar is collapsed */ }
446- < div className = { cn ( "h-full" , showCollapsed ? "flex" : "hidden" ) } > { verticalMeter } </ div >
447343 </ SidebarContainer >
448344 ) ;
449345} ;
0 commit comments