From b910f48da09ded3c3a012df69edac904619294ba Mon Sep 17 00:00:00 2001
From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
Date: Mon, 20 Jul 2026 17:32:56 -0700
Subject: [PATCH] feat(status-bar): replace usage footer toggle with
Compact/Detailed segmented control
Swap the bottom-of-popover on/off switch for a SettingsSegmentedControl at
the top of the Usage popover (view-switcher pattern), so both modes are
named and discoverable on first open. Reuses the repo's canonical
Compact/Detailed vocabulary from the Workspace card-layout control.
---
.../status-bar/UsageRosterPanel.test.tsx | 99 +++++++++++++++----
.../status-bar/UsageRosterPanel.tsx | 50 +++++-----
src/renderer/src/i18n/locales/en.json | 4 +-
src/renderer/src/i18n/locales/es.json | 4 +-
src/renderer/src/i18n/locales/ja.json | 4 +-
src/renderer/src/i18n/locales/ko.json | 4 +-
src/renderer/src/i18n/locales/zh.json | 4 +-
7 files changed, 122 insertions(+), 47 deletions(-)
diff --git a/src/renderer/src/components/status-bar/UsageRosterPanel.test.tsx b/src/renderer/src/components/status-bar/UsageRosterPanel.test.tsx
index 2285e0c3737..c61faf88367 100644
--- a/src/renderer/src/components/status-bar/UsageRosterPanel.test.tsx
+++ b/src/renderer/src/components/status-bar/UsageRosterPanel.test.tsx
@@ -1,5 +1,9 @@
+// @vitest-environment happy-dom
+
+import { act } from 'react'
+import { createRoot, type Root } from 'react-dom/client'
import { renderToStaticMarkup } from 'react-dom/server'
-import { beforeEach, describe, expect, it, vi } from 'vitest'
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type { ProviderRateLimits } from '../../../../shared/rate-limit-types'
const mocks = vi.hoisted(() => ({
@@ -17,21 +21,6 @@ vi.mock('@/hooks/useResetCountdownClock', () => ({
useResetCountdownClock: mocks.useResetCountdownClock
}))
vi.mock('@/components/ui/dropdown-menu', () => ({
- DropdownMenuCheckboxItem: ({
- children,
- checked,
- onCheckedChange: _onCheckedChange,
- onSelect: _onSelect,
- ...props
- }: React.PropsWithChildren<{
- checked?: boolean
- onCheckedChange?: (checked: boolean) => void
- onSelect?: () => void
- }>) => (
-
- {children}
-
- ),
DropdownMenuItem: ({
children,
onSelect: _onSelect,
@@ -137,7 +126,81 @@ describe('UsageRow', () => {
expect(mocks.useResetCountdownClock).toHaveBeenCalledOnce()
expect(mocks.useResetCountdownClock).toHaveBeenCalledWith([sessionReset, weeklyReset])
expect(markup).toContain('Resets in 2m')
- expect(markup).toContain('Verbose footer')
- expect(markup).toContain('data-checked="true"')
+ })
+})
+
+describe('UsageRosterPanel density picker', () => {
+ let container: HTMLDivElement
+ let root: Root
+
+ beforeEach(() => {
+ mocks.useResetCountdownClock.mockClear()
+ container = document.createElement('div')
+ document.body.appendChild(container)
+ root = createRoot(container)
+ })
+
+ afterEach(() => {
+ act(() => {
+ root.unmount()
+ })
+ container.remove()
+ })
+
+ function renderPanel(
+ statusBarUsageMode: 'verbose' | 'compact',
+ onStatusBarUsageModeChange: (mode: 'verbose' | 'compact') => void
+ ): void {
+ act(() => {
+ root.render(
+ {}}
+ onOpenProvider={() => {}}
+ onSignIn={() => {}}
+ canSignIn={() => true}
+ onManageAccounts={() => {}}
+ onUsageDetails={() => {}}
+ />
+ )
+ })
+ }
+
+ function segmentButton(label: string): HTMLButtonElement {
+ const button = [...container.querySelectorAll('button')].find(
+ (node) => node.textContent === label
+ )
+ if (!button) {
+ throw new Error(`missing "${label}" segment`)
+ }
+ return button as HTMLButtonElement
+ }
+
+ it('offers named Detailed/Compact segments and marks the active one', () => {
+ renderPanel('compact', () => {})
+
+ expect(container.textContent).toContain('Detailed')
+ expect(container.textContent).toContain('Compact')
+ expect(segmentButton('Compact').getAttribute('aria-checked')).toBe('true')
+ expect(segmentButton('Detailed').getAttribute('aria-checked')).toBe('false')
+ })
+
+ it('switches mode when a segment is chosen', () => {
+ const onStatusBarUsageModeChange = vi.fn()
+ renderPanel('compact', onStatusBarUsageModeChange)
+
+ act(() => {
+ segmentButton('Detailed').click()
+ })
+ expect(onStatusBarUsageModeChange).toHaveBeenLastCalledWith('verbose')
+
+ act(() => {
+ segmentButton('Compact').click()
+ })
+ expect(onStatusBarUsageModeChange).toHaveBeenLastCalledWith('compact')
})
})
diff --git a/src/renderer/src/components/status-bar/UsageRosterPanel.tsx b/src/renderer/src/components/status-bar/UsageRosterPanel.tsx
index 5788e67969e..fa11825d140 100644
--- a/src/renderer/src/components/status-bar/UsageRosterPanel.tsx
+++ b/src/renderer/src/components/status-bar/UsageRosterPanel.tsx
@@ -1,6 +1,7 @@
import React from 'react'
import { ChevronRight, RefreshCw } from 'lucide-react'
-import { DropdownMenuCheckboxItem, DropdownMenuItem } from '@/components/ui/dropdown-menu'
+import { DropdownMenuItem } from '@/components/ui/dropdown-menu'
+import { SettingsSegmentedControl } from '@/components/settings/SettingsFormControls'
import { useResetCountdownClock } from '@/hooks/useResetCountdownClock'
import { translate } from '@/i18n/i18n'
import { formatRateLimitWindowChipLabel, formatWindowLabel } from '@/lib/window-label-formatter'
@@ -220,6 +221,30 @@ export function UsageRosterPanel({
+ {/* Density picker lives at the top of the popover it controls (view-switcher
+ pattern) so both modes are named and discoverable on first open. */}
+
+
+ value={statusBarUsageMode}
+ onChange={onStatusBarUsageModeChange}
+ ariaLabel={translate(
+ 'auto.components.status.bar.UsageRosterPanel.footerDetailAria',
+ 'Usage footer detail'
+ )}
+ size="sm"
+ equalWidth
+ options={[
+ {
+ value: 'verbose',
+ label: translate('auto.components.status.bar.UsageRosterPanel.detailed', 'Detailed')
+ },
+ {
+ value: 'compact',
+ label: translate('auto.components.status.bar.UsageRosterPanel.compact', 'Compact')
+ }
+ ]}
+ />
+
{sorted.map((p) => {
const state = getUsageRosterRowState(p, usedSections(p).length > 0)
@@ -259,29 +284,6 @@ export function UsageRosterPanel({
)
})}
- {
- onStatusBarUsageModeChange(checked ? 'verbose' : 'compact')
- }}
- onSelect={(event) => event.preventDefault()}
- className="w-full cursor-pointer justify-between rounded-none px-3.5 py-2.5 text-[13px] text-foreground [&>span:first-child]:hidden"
- >
- {translate('auto.components.status.bar.UsageRosterPanel.verboseFooter', 'Verbose footer')}
- {/* Why: one menu control avoids nesting the SettingsSwitch button inside an interactive item. */}
-
-
-
-