Skip to content

Commit 1ed1e4e

Browse files
authored
🤖 refactor: restyle RightSidebar tabs with dynamic data (#1125)
_Generated with `mux`_ ## Changes - **Tab styling**: Restyled from heavy border-bottom accent to subtle pill/toggle style matching app aesthetic - Using `bg-hover` for active state, consistent with `ToggleGroup` pattern - Smaller `text-xs` font, compact padding - **Dynamic tab data**: - Costs tab shows session cost (e.g. "Costs $0.56") - Review tab shows read/total hunks (e.g. "Review 34/56") - Dynamic portions use smaller text (`text-[10px]`) and muted color for visual distinction - Review counter dims when all hunks are read - **Resize handle**: - Moved to left of VerticalTokenMeter - 2px width with subtle border color, highlights on hover/active
1 parent 2c3e68a commit 1ed1e4e

File tree

2 files changed

+77
-18
lines changed

2 files changed

+77
-18
lines changed

src/browser/components/RightSidebar.tsx

Lines changed: 63 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,18 @@ import { CostsTab } from "./RightSidebar/CostsTab";
99
import { VerticalTokenMeter } from "./RightSidebar/VerticalTokenMeter";
1010
import { ReviewPanel } from "./RightSidebar/CodeReview/ReviewPanel";
1111
import { calculateTokenMeterData } from "@/common/utils/tokens/tokenMeterUtils";
12+
import { sumUsageHistory, type ChatUsageDisplay } from "@/common/utils/tokens/usageAggregator";
1213
import { matchesKeybind, KEYBINDS, formatKeybind } from "@/browser/utils/ui/keybinds";
1314
import { Tooltip, TooltipTrigger, TooltipContent } from "./ui/tooltip";
1415
import { cn } from "@/common/lib/utils";
1516
import type { ReviewNoteData } from "@/common/types/review";
1617

18+
/** Stats reported by ReviewPanel for tab display */
19+
export interface ReviewStats {
20+
total: number;
21+
read: number;
22+
}
23+
1724
interface SidebarContainerProps {
1825
collapsed: boolean;
1926
wide?: boolean;
@@ -107,6 +114,9 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
107114
// Trigger for focusing Review panel (preserves hunk selection)
108115
const [focusTrigger, setFocusTrigger] = React.useState(0);
109116

117+
// Review stats reported by ReviewPanel
118+
const [reviewStats, setReviewStats] = React.useState<ReviewStats | null>(null);
119+
110120
// Notify parent (AIView) of tab changes so it can enable/disable resize functionality
111121
React.useEffect(() => {
112122
onTabChange?.(selectedTab);
@@ -144,6 +154,26 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
144154
const lastUsage = usage?.liveUsage ?? usage?.lastContextUsage;
145155
const model = lastUsage?.model ?? null;
146156

157+
// Calculate session cost for tab display
158+
const sessionCost = React.useMemo(() => {
159+
const parts: ChatUsageDisplay[] = [];
160+
if (usage.sessionTotal) parts.push(usage.sessionTotal);
161+
if (usage.liveCostUsage) parts.push(usage.liveCostUsage);
162+
if (parts.length === 0) return null;
163+
164+
const aggregated = sumUsageHistory(parts);
165+
if (!aggregated) return null;
166+
167+
// Sum all cost components
168+
const total =
169+
(aggregated.input.cost_usd ?? 0) +
170+
(aggregated.cached.cost_usd ?? 0) +
171+
(aggregated.cacheCreate.cost_usd ?? 0) +
172+
(aggregated.output.cost_usd ?? 0) +
173+
(aggregated.reasoning.cost_usd ?? 0);
174+
return total > 0 ? total : null;
175+
}, [usage.sessionTotal, usage.liveCostUsage]);
176+
147177
// Auto-compaction settings: threshold per-model
148178
const { threshold: autoCompactThreshold, setThreshold: setAutoCompactThreshold } =
149179
useAutoCompactionSettings(workspaceId, model);
@@ -215,39 +245,36 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
215245
>
216246
{/* Full view when not collapsed */}
217247
<div className={cn("flex-row h-full", !showCollapsed ? "flex" : "hidden")}>
218-
{/* Render meter when Review tab is active */}
219-
{selectedTab === "review" && (
220-
<div className="bg-sidebar border-border-light flex w-5 shrink-0 flex-col border-r">
221-
{verticalMeter}
222-
</div>
223-
)}
224-
225-
{/* Render resize handle to right of meter when Review tab is active */}
248+
{/* Resize handle (left edge) when Review tab is active */}
226249
{selectedTab === "review" && onStartResize && (
227250
<div
228251
className={cn(
229-
"w-1 flex-shrink-0 z-10 transition-[background] duration-150",
230-
"bg-border-light cursor-col-resize hover:bg-accent",
231-
isResizing && "bg-accent"
252+
"w-0.5 flex-shrink-0 z-10 transition-[background] duration-150 cursor-col-resize",
253+
isResizing ? "bg-accent" : "bg-border-light hover:bg-accent"
232254
)}
233255
onMouseDown={(e) => onStartResize(e as unknown as React.MouseEvent)}
234256
/>
235257
)}
236258

259+
{/* Render meter when Review tab is active */}
260+
{selectedTab === "review" && (
261+
<div className="bg-sidebar flex w-5 shrink-0 flex-col">{verticalMeter}</div>
262+
)}
263+
237264
<div className="flex min-w-0 flex-1 flex-col">
238265
<div
239-
className="bg-background-secondary border-border flex border-b [&>*]:flex-1"
266+
className="border-border-light flex gap-1 border-b px-2 py-1.5"
240267
role="tablist"
241268
aria-label="Metadata views"
242269
>
243270
<Tooltip>
244271
<TooltipTrigger asChild>
245272
<button
246273
className={cn(
247-
"w-full py-2.5 px-[15px] border-none border-solid cursor-pointer font-primary text-[13px] font-medium transition-all duration-200",
274+
"rounded-md px-3 py-1 text-xs font-medium transition-all duration-150 flex items-center gap-1.5",
248275
selectedTab === "costs"
249-
? "bg-separator border-b-2 border-b-plan-mode text-[var(--color-sidebar-tab-active)]"
250-
: "bg-transparent text-secondary border-b-2 border-b-transparent hover:bg-background-secondary hover:text-foreground"
276+
? "bg-hover text-foreground"
277+
: "bg-transparent text-muted hover:bg-hover/50 hover:text-foreground"
251278
)}
252279
onClick={() => setSelectedTab("costs")}
253280
id={costsTabId}
@@ -257,6 +284,11 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
257284
aria-controls={costsPanelId}
258285
>
259286
Costs
287+
{sessionCost !== null && (
288+
<span className="text-muted text-[10px]">
289+
${sessionCost < 0.01 ? "<0.01" : sessionCost.toFixed(2)}
290+
</span>
291+
)}
260292
</button>
261293
</TooltipTrigger>
262294
<TooltipContent side="bottom" align="center">
@@ -267,10 +299,10 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
267299
<TooltipTrigger asChild>
268300
<button
269301
className={cn(
270-
"w-full py-2.5 px-[15px] border-none border-solid cursor-pointer font-primary text-[13px] font-medium transition-all duration-200",
302+
"rounded-md px-3 py-1 text-xs font-medium transition-all duration-150 flex items-center gap-1.5",
271303
selectedTab === "review"
272-
? "bg-separator border-b-2 border-b-plan-mode text-[var(--color-sidebar-tab-active)]"
273-
: "bg-transparent text-secondary border-b-2 border-b-transparent hover:bg-background-secondary hover:text-foreground"
304+
? "bg-hover text-foreground"
305+
: "bg-transparent text-muted hover:bg-hover/50 hover:text-foreground"
274306
)}
275307
onClick={() => setSelectedTab("review")}
276308
id={reviewTabId}
@@ -280,6 +312,18 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
280312
aria-controls={reviewPanelId}
281313
>
282314
Review
315+
{reviewStats !== null && reviewStats.total > 0 && (
316+
<span
317+
className={cn(
318+
"text-[10px]",
319+
reviewStats.read === reviewStats.total
320+
? "text-muted" // All read - dimmed
321+
: "text-muted"
322+
)}
323+
>
324+
{reviewStats.read}/{reviewStats.total}
325+
</span>
326+
)}
283327
</button>
284328
</TooltipTrigger>
285329
<TooltipContent side="bottom" align="center">
@@ -309,6 +353,7 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
309353
onReviewNote={onReviewNote}
310354
focusTrigger={focusTrigger}
311355
isCreating={isCreating}
356+
onStatsChange={setReviewStats}
312357
/>
313358
</div>
314359
)}

src/browser/components/RightSidebar/CodeReview/ReviewPanel.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ import { applyFrontendFilters } from "@/browser/utils/review/filterHunks";
4343
import { cn } from "@/common/lib/utils";
4444
import { useAPI } from "@/browser/contexts/API";
4545

46+
/** Stats reported to parent for tab display */
47+
interface ReviewPanelStats {
48+
total: number;
49+
read: number;
50+
}
51+
4652
interface ReviewPanelProps {
4753
workspaceId: string;
4854
workspacePath: string;
@@ -51,6 +57,8 @@ interface ReviewPanelProps {
5157
focusTrigger?: number;
5258
/** Workspace is still being created (git operations in progress) */
5359
isCreating?: boolean;
60+
/** Callback to report stats changes (for tab badge) */
61+
onStatsChange?: (stats: ReviewPanelStats) => void;
5462
}
5563

5664
interface ReviewSearchState {
@@ -85,6 +93,7 @@ export const ReviewPanel: React.FC<ReviewPanelProps> = ({
8593
onReviewNote,
8694
focusTrigger,
8795
isCreating = false,
96+
onStatsChange,
8897
}) => {
8998
const { api } = useAPI();
9099
const panelRef = useRef<HTMLDivElement>(null);
@@ -507,6 +516,11 @@ export const ReviewPanel: React.FC<ReviewPanelProps> = ({
507516
};
508517
}, [hunks, isRead]);
509518

519+
// Report stats to parent for tab badge
520+
useEffect(() => {
521+
onStatsChange?.({ total: stats.total, read: stats.read });
522+
}, [stats.total, stats.read, onStatsChange]);
523+
510524
// Scroll selected hunk into view
511525
useEffect(() => {
512526
if (!selectedHunkId) return;

0 commit comments

Comments
 (0)