Skip to content

Commit d0c7872

Browse files
committed
🤖 refactor: remove VerticalTokenMeter component
Now that we have the context usage indicator in the chat area, the VerticalTokenMeter in the right sidebar is no longer needed. Removed: - VerticalTokenMeter.tsx component - ChatMetaSidebar.tsx (only existed to show the meter when collapsed) - Related imports and usage in RightSidebar.tsx -229 lines
1 parent 8cdf42d commit d0c7872

File tree

8 files changed

+87
-326
lines changed

8 files changed

+87
-326
lines changed

src/browser/components/AIView.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,6 @@ const AIViewInner: React.FC<AIViewProps> = ({
797797
key={workspaceId}
798798
workspaceId={workspaceId}
799799
workspacePath={namedWorkspacePath}
800-
chatAreaRef={chatAreaRef}
801800
width={sidebarWidth}
802801
onStartResize={startResize}
803802
isResizing={isResizing}

src/browser/components/ChatMetaSidebar.tsx

Lines changed: 0 additions & 84 deletions
This file was deleted.

src/browser/components/LeftSidebar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export function LeftSidebar(props: LeftSidebarProps) {
5151
className={cn(
5252
"h-screen bg-sidebar border-r border-border flex flex-col shrink-0",
5353
"transition-all duration-200 overflow-hidden relative z-20",
54-
collapsed ? "w-8" : "w-72",
54+
collapsed ? "w-5" : "w-72",
5555
"mobile-sidebar",
5656
collapsed && "mobile-sidebar-collapsed"
5757
)}

src/browser/components/ProjectSidebar.tsx

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
AGE_THRESHOLDS_DAYS,
2020
} from "@/browser/utils/ui/workspaceFiltering";
2121
import { Tooltip, TooltipTrigger, TooltipContent } from "./ui/tooltip";
22+
import { SidebarCollapseButton } from "./ui/SidebarCollapseButton";
2223
import SecretsModal from "./SecretsModal";
2324
import type { Secret } from "@/common/types/secrets";
2425
import { ForceDeleteModal } from "./ForceDeleteModal";
@@ -714,21 +715,12 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
714715
</div>
715716
</>
716717
)}
717-
<Tooltip>
718-
<TooltipTrigger asChild>
719-
<button
720-
onClick={onToggleCollapsed}
721-
aria-label={collapsed ? "Expand sidebar" : "Collapse sidebar"}
722-
className="text-muted border-dark hover:bg-hover hover:text-foreground mt-auto flex h-9 w-full cursor-pointer items-center justify-center border-t border-none bg-transparent p-0 text-sm transition-all duration-200"
723-
>
724-
{collapsed ? "»" : "«"}
725-
</button>
726-
</TooltipTrigger>
727-
<TooltipContent align="center">
728-
{collapsed ? "Expand sidebar" : "Collapse sidebar"} (
729-
{formatKeybind(KEYBINDS.TOGGLE_SIDEBAR)})
730-
</TooltipContent>
731-
</Tooltip>
718+
<SidebarCollapseButton
719+
collapsed={collapsed}
720+
onToggle={onToggleCollapsed}
721+
side="left"
722+
shortcut={formatKeybind(KEYBINDS.TOGGLE_SIDEBAR)}
723+
/>
732724
{secretsModalState && (
733725
<SecretsModal
734726
isOpen={secretsModalState.isOpen}

src/browser/components/RightSidebar.tsx

Lines changed: 30 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,15 @@ import React from "react";
22
import { RIGHT_SIDEBAR_TAB_KEY, RIGHT_SIDEBAR_COLLAPSED_KEY } from "@/common/constants/storage";
33
import { usePersistedState } from "@/browser/hooks/usePersistedState";
44
import { useWorkspaceUsage, useWorkspaceStatsSnapshot } from "@/browser/stores/WorkspaceStore";
5-
import { useProviderOptions } from "@/browser/hooks/useProviderOptions";
6-
import { useResizeObserver } from "@/browser/hooks/useResizeObserver";
75
import { useFeatureFlags } from "@/browser/contexts/FeatureFlagsContext";
8-
import { useAutoCompactionSettings } from "@/browser/hooks/useAutoCompactionSettings";
96
import { ErrorBoundary } from "./ErrorBoundary";
107
import { CostsTab } from "./RightSidebar/CostsTab";
118
import { StatsTab } from "./RightSidebar/StatsTab";
12-
import { VerticalTokenMeter } from "./RightSidebar/VerticalTokenMeter";
139
import { ReviewPanel } from "./RightSidebar/CodeReview/ReviewPanel";
14-
import { calculateTokenMeterData } from "@/common/utils/tokens/tokenMeterUtils";
1510
import { sumUsageHistory, type ChatUsageDisplay } from "@/common/utils/tokens/usageAggregator";
1611
import { matchesKeybind, KEYBINDS, formatKeybind } from "@/browser/utils/ui/keybinds";
1712
import { Tooltip, TooltipTrigger, TooltipContent } from "./ui/tooltip";
13+
import { SidebarCollapseButton } from "./ui/SidebarCollapseButton";
1814
import { cn } from "@/common/lib/utils";
1915
import type { ReviewNoteData } from "@/common/types/review";
2016

@@ -34,7 +30,7 @@ function formatTabDuration(ms: number): string {
3430
}
3531

3632
interface SidebarContainerProps {
37-
collapsed: boolean;
33+
collapsed?: boolean;
3834
wide?: boolean;
3935
/** Custom width from drag-resize (persisted per-tab by AIView) */
4036
customWidth?: number;
@@ -49,7 +45,7 @@ interface SidebarContainerProps {
4945
* SidebarContainer - Main sidebar wrapper with dynamic width
5046
*
5147
* Width priority (first match wins):
52-
* 1. collapsed (20px) - Shows vertical token meter only
48+
* 1. collapsed (20px) - Manual collapse via toggle
5349
* 2. customWidth - From drag-resize (persisted per-tab)
5450
* 3. wide - Auto-calculated max width for Review tab (when not drag-resizing)
5551
* 4. default (300px) - Costs tab when no customWidth saved
@@ -64,7 +60,7 @@ const SidebarContainer: React.FC<SidebarContainerProps> = ({
6460
"aria-label": ariaLabel,
6561
}) => {
6662
const width = collapsed
67-
? "20px"
63+
? "20px" // Match left sidebar collapsed width (w-5 = 20px)
6864
: customWidth
6965
? `${customWidth}px`
7066
: wide
@@ -74,10 +70,8 @@ const SidebarContainer: React.FC<SidebarContainerProps> = ({
7470
return (
7571
<div
7672
className={cn(
77-
"bg-sidebar border-l border-border-light flex flex-col overflow-hidden flex-shrink-0",
73+
"bg-sidebar border-l border-border-light relative flex flex-col overflow-hidden flex-shrink-0",
7874
!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
8175
"max-md:border-l-0 max-md:border-t max-md:border-border-light",
8276
!collapsed && "max-md:w-full max-md:relative max-md:max-h-[50vh]"
8377
)}
@@ -97,7 +91,6 @@ export type { TabType };
9791
interface RightSidebarProps {
9892
workspaceId: string;
9993
workspacePath: string;
100-
chatAreaRef: React.RefObject<HTMLDivElement>;
10194
/** Custom width in pixels (persisted per-tab, provided by AIView) */
10295
width?: number;
10396
/** Drag start handler for resize */
@@ -113,7 +106,6 @@ interface RightSidebarProps {
113106
const RightSidebarComponent: React.FC<RightSidebarProps> = ({
114107
workspaceId,
115108
workspacePath,
116-
chatAreaRef,
117109
width,
118110
onStartResize,
119111
isResizing = false,
@@ -123,6 +115,9 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
123115
// Global tab preference (not per-workspace)
124116
const [selectedTab, setSelectedTab] = usePersistedState<TabType>(RIGHT_SIDEBAR_TAB_KEY, "costs");
125117

118+
// Manual collapse state (persisted globally)
119+
const [collapsed, setCollapsed] = usePersistedState<boolean>(RIGHT_SIDEBAR_COLLAPSED_KEY, false);
120+
126121
const { statsTabState } = useFeatureFlags();
127122
const statsTabEnabled = Boolean(statsTabState?.enabled);
128123

@@ -160,10 +155,6 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
160155

161156
const usage = useWorkspaceUsage(workspaceId);
162157

163-
const { options } = useProviderOptions();
164-
const use1M = options.anthropic?.use1MContext ?? false;
165-
const chatAreaSize = useResizeObserver(chatAreaRef);
166-
167158
const baseId = `right-sidebar-${workspaceId}`;
168159
const costsTabId = `${baseId}-tab-costs`;
169160
const statsTabId = `${baseId}-tab-stats`;
@@ -172,10 +163,6 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
172163
const statsPanelId = `${baseId}-panel-stats`;
173164
const reviewPanelId = `${baseId}-panel-review`;
174165

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-
179166
// Calculate session cost for tab display
180167
const sessionCost = React.useMemo(() => {
181168
const parts: ChatUsageDisplay[] = [];
@@ -206,109 +193,30 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
206193
return total > 0 ? total : null;
207194
})();
208195

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-
282196
return (
283197
<SidebarContainer
284-
collapsed={showCollapsed}
198+
collapsed={collapsed}
285199
wide={selectedTab === "review" && !width} // Auto-wide only if not drag-resizing
286200
customWidth={width} // Per-tab resized width from AIView
287201
isResizing={isResizing}
288202
role="complementary"
289203
aria-label="Workspace insights"
290204
>
291-
{/* Full view when not collapsed */}
292-
<div className={cn("flex-row h-full", !showCollapsed ? "flex" : "hidden")}>
293-
{/* Resize handle (left edge) */}
294-
{onStartResize && (
295-
<div
296-
className={cn(
297-
"w-0.5 flex-shrink-0 z-10 transition-[background] duration-150 cursor-col-resize",
298-
isResizing ? "bg-accent" : "bg-border-light hover:bg-accent"
299-
)}
300-
onMouseDown={(e) => onStartResize(e as unknown as React.MouseEvent)}
301-
/>
302-
)}
205+
{!collapsed && (
206+
<>
207+
{/* Resize handle (left edge) */}
208+
{onStartResize && (
209+
<div
210+
className={cn(
211+
"absolute left-0 top-0 bottom-0 w-0.5 z-10 transition-[background] duration-150 cursor-col-resize",
212+
isResizing ? "bg-accent" : "bg-border-light hover:bg-accent"
213+
)}
214+
onMouseDown={(e) => onStartResize(e as unknown as React.MouseEvent)}
215+
/>
216+
)}
303217

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-
309-
<div className="flex min-w-0 flex-1 flex-col">
310218
<div
311-
className="border-border-light flex gap-1 border-b px-2 py-1.5"
219+
className="border-border-light flex h-8 shrink-0 items-center gap-1 border-b px-2"
312220
role="tablist"
313221
aria-label="Metadata views"
314222
>
@@ -440,10 +348,14 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
440348
</div>
441349
)}
442350
</div>
443-
</div>
444-
</div>
445-
{/* Render meter in collapsed view when sidebar is collapsed */}
446-
<div className={cn("h-full", showCollapsed ? "flex" : "hidden")}>{verticalMeter}</div>
351+
</>
352+
)}
353+
354+
<SidebarCollapseButton
355+
collapsed={collapsed}
356+
onToggle={() => setCollapsed(!collapsed)}
357+
side="right"
358+
/>
447359
</SidebarContainer>
448360
);
449361
};

0 commit comments

Comments
 (0)