From 55066ee1852af2fa146d4950f1b08c2bbb8dfb3d Mon Sep 17 00:00:00 2001
From: brennanb2025
Date: Fri, 12 Jun 2026 11:47:39 -0700
Subject: [PATCH] Remove unrelated UI merge artifacts
Co-authored-by: Orca
---
.../components/mobile/MobileHeroPaired.tsx | 78 --------
.../ThemeStepGhosttyDiscoveryRow.tsx | 92 ---------
.../HostedReviewDeleteWorkspaceButton.tsx | 36 ----
.../HostedReviewReopenButton.tsx | 41 ----
.../components/settings/McpConfigSection.tsx | 39 ++--
.../settings/mcp-config-inspection-state.ts | 34 ----
.../src/components/stats/ClaudeUsagePane.tsx | 167 ++++++++++++----
.../src/components/stats/CodexUsagePane.tsx | 170 ++++++++++++----
.../components/stats/OpenCodeUsagePane.tsx | 184 ++++++++++++++----
.../stats/UsagePaneFilterControls.tsx | 124 ------------
.../stats/UsageTrackingDisabledCard.tsx | 34 ----
.../stats/usage-display-formatting.ts | 36 ----
...lder-workspace-path-status-cache-expiry.ts | 4 +-
13 files changed, 429 insertions(+), 610 deletions(-)
delete mode 100644 src/renderer/src/components/mobile/MobileHeroPaired.tsx
delete mode 100644 src/renderer/src/components/onboarding/ThemeStepGhosttyDiscoveryRow.tsx
delete mode 100644 src/renderer/src/components/right-sidebar/HostedReviewDeleteWorkspaceButton.tsx
delete mode 100644 src/renderer/src/components/right-sidebar/HostedReviewReopenButton.tsx
delete mode 100644 src/renderer/src/components/settings/mcp-config-inspection-state.ts
delete mode 100644 src/renderer/src/components/stats/UsagePaneFilterControls.tsx
delete mode 100644 src/renderer/src/components/stats/UsageTrackingDisabledCard.tsx
delete mode 100644 src/renderer/src/components/stats/usage-display-formatting.ts
diff --git a/src/renderer/src/components/mobile/MobileHeroPaired.tsx b/src/renderer/src/components/mobile/MobileHeroPaired.tsx
deleted file mode 100644
index 807c34bb0c1..00000000000
--- a/src/renderer/src/components/mobile/MobileHeroPaired.tsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import { Smartphone, Trash2 } from 'lucide-react'
-import type { PairedDevice } from './MobileHero'
-import { translate } from '@/i18n/i18n'
-
-type HeroPairedProps = {
- devices: readonly PairedDevice[]
- onPairAnother: () => void
- onRevoke: (deviceId: string) => void
- revokingDeviceIds: readonly string[]
-}
-
-export function HeroPaired({
- devices,
- onPairAnother,
- onRevoke,
- revokingDeviceIds
-}: HeroPairedProps): React.JSX.Element {
- return (
-
-
-
- {translate('auto.components.mobile.MobileHero.5410d55d79', 'Orca Mobile')}
-
-
-
- {devices.length === 1
- ? translate('auto.components.mobile.MobileHero.051978a785', 'Your phone is paired.')
- : translate('auto.components.mobile.MobileHero.d0b52871ce', 'Your phones are paired.')}
-
-
- {translate(
- 'auto.components.mobile.MobileHero.266c18c105',
- 'Open Orca Mobile to pick up where you left off, or pair another device.'
- )}
-
-
- {devices.map((device) => {
- const revoking = revokingDeviceIds.includes(device.deviceId)
- return (
-
-
-
-
-
-
{device.name}
-
- {translate('auto.components.mobile.MobileHero.94829abdb1', 'Paired')}{' '}
- {new Date(device.pairedAt).toLocaleDateString()}
-
-
- onRevoke(device.deviceId)}
- disabled={revoking}
- aria-label={translate(
- 'auto.components.mobile.MobileHero.34f878d04f',
- 'Revoke {{value0}}',
- { value0: device.name }
- )}
- title={translate('auto.components.mobile.MobileHero.f9cbf4bb53', 'Revoke device')}
- >
-
-
-
- )
- })}
-
-
-
-
- {translate('auto.components.mobile.MobileHero.ff48d9d520', 'Pair another device')}
-
-
-
-
- )
-}
diff --git a/src/renderer/src/components/onboarding/ThemeStepGhosttyDiscoveryRow.tsx b/src/renderer/src/components/onboarding/ThemeStepGhosttyDiscoveryRow.tsx
deleted file mode 100644
index 7020c52155a..00000000000
--- a/src/renderer/src/components/onboarding/ThemeStepGhosttyDiscoveryRow.tsx
+++ /dev/null
@@ -1,92 +0,0 @@
-import { Check } from 'lucide-react'
-import type { GhosttyImportPreview } from '../../../../shared/types'
-import ghosttyIcon from '../../../../../resources/ghostty.svg'
-import type { DiscoveryState } from './ThemeStep'
-import { translate } from '@/i18n/i18n'
-
-type GhosttyDiscoveryRowProps = {
- discovery: DiscoveryState
- importing: boolean
- disabled: boolean
- onImport: (preview: GhosttyImportPreview) => void
-}
-
-export function GhosttyDiscoveryRow({
- discovery,
- importing,
- disabled,
- onImport
-}: GhosttyDiscoveryRowProps): React.JSX.Element | null {
- if (discovery.status === 'absent' || discovery.status === 'idle') {
- return null
- }
-
- if (discovery.status === 'detecting') {
- return (
-
-
- {translate(
- 'auto.components.onboarding.ThemeStep.2c3aa538f8',
- 'Looking for a Ghostty config…'
- )}
-
- )
- }
-
- if (discovery.status === 'imported') {
- return (
-
-
-
-
- {translate('auto.components.onboarding.ThemeStep.78b6386140', 'Imported from Ghostty.')}
-
- {discovery.fields.length > 0 && (
- {discovery.fields.join(' · ')}
- )}
-
-
- )
- }
-
- const { preview, fields } = discovery
- return (
-
-
-
-
-
- {translate(
- 'auto.components.onboarding.ThemeStep.7ee9234e54',
- 'Ghostty config detected.'
- )}
- {' '}
-
- {translate('auto.components.onboarding.ThemeStep.248c812283', 'Import')}{' '}
- {fields.length > 0
- ? fields.map((f) => f.toLowerCase()).join(', ')
- : translate('auto.components.onboarding.ThemeStep.906c4373fe', 'settings')}
- ?
-
-
- {preview.configPath && (
-
- {preview.configPath}
-
- )}
-
-
onImport(preview)}
- >
- {importing
- ? translate('auto.components.onboarding.ThemeStep.ad19e5c916', 'Importing…')
- : translate('auto.components.onboarding.ThemeStep.248c812283', 'Import')}
-
-
- )
-}
diff --git a/src/renderer/src/components/right-sidebar/HostedReviewDeleteWorkspaceButton.tsx b/src/renderer/src/components/right-sidebar/HostedReviewDeleteWorkspaceButton.tsx
deleted file mode 100644
index f0aaf4dfbfa..00000000000
--- a/src/renderer/src/components/right-sidebar/HostedReviewDeleteWorkspaceButton.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import { LoaderCircle, Trash2 } from 'lucide-react'
-import { Button } from '@/components/ui/button'
-import { translate } from '@/i18n/i18n'
-
-type HostedReviewDeleteWorkspaceButtonProps = {
- deleting: boolean
- onDelete: () => void
-}
-
-export function HostedReviewDeleteWorkspaceButton({
- deleting,
- onDelete
-}: HostedReviewDeleteWorkspaceButtonProps): React.JSX.Element {
- return (
-
- {deleting ? (
-
- ) : (
-
- )}
- {deleting
- ? translate('auto.components.right.sidebar.HostedReviewActions.eefd50457e', 'Deleting...')
- : translate(
- 'auto.components.right.sidebar.HostedReviewActions.e4aca40024',
- 'Delete Workspace'
- )}
-
- )
-}
diff --git a/src/renderer/src/components/right-sidebar/HostedReviewReopenButton.tsx b/src/renderer/src/components/right-sidebar/HostedReviewReopenButton.tsx
deleted file mode 100644
index 35411fb9aba..00000000000
--- a/src/renderer/src/components/right-sidebar/HostedReviewReopenButton.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import { CircleDot, LoaderCircle } from 'lucide-react'
-import { Button } from '@/components/ui/button'
-import { translate } from '@/i18n/i18n'
-
-type HostedReviewReopenButtonProps = {
- label: string
- reopening: boolean
- disabled: boolean
- onReopen: () => void
-}
-
-export function HostedReviewReopenButton({
- label,
- reopening,
- disabled,
- onReopen
-}: HostedReviewReopenButtonProps): React.JSX.Element {
- return (
-
- {reopening ? (
-
- ) : (
-
- )}
- {reopening
- ? translate('auto.components.right.sidebar.HostedReviewActions.6645ac7dd1', 'Reopening...')
- : translate(
- 'auto.components.right.sidebar.HostedReviewActions.3ce211ece6',
- 'Reopen {{value0}}',
- { value0: label }
- )}
-
- )
-}
diff --git a/src/renderer/src/components/settings/McpConfigSection.tsx b/src/renderer/src/components/settings/McpConfigSection.tsx
index 2b02cca478a..8e775a5c69b 100644
--- a/src/renderer/src/components/settings/McpConfigSection.tsx
+++ b/src/renderer/src/components/settings/McpConfigSection.tsx
@@ -2,7 +2,8 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { AlertCircle, FileCode2, LoaderCircle, Plus, RefreshCw } from 'lucide-react'
import { toast } from 'sonner'
import { useMountedRef } from '@/hooks/useMountedRef'
-import type { Repo } from '../../../../shared/types'
+import type { Repo, Worktree } from '../../../../shared/types'
+import { getRepoIdFromWorktreeId } from '../../../../shared/worktree-id'
import {
canInspectLocalMcpConfigRoot,
inspectMcpConfigContent,
@@ -15,11 +16,6 @@ import { extractIpcErrorMessage } from '../../lib/ipc-error'
import { Button } from '../ui/button'
import { isWindowsUserAgent } from '../terminal-pane/pane-helpers'
import { McpConfigFileRow, type LoadedMcpConfigInspection } from './McpConfigFileRow'
-import {
- EMPTY_MCP_WORKTREES,
- countMcpConfigServers,
- selectMcpTargetWorktree
-} from './mcp-config-inspection-state'
import { McpMissingConfigList } from './McpMissingConfigList'
import { loadMcpConfigInspections } from './mcp-config-inspection'
import { translate } from '@/i18n/i18n'
@@ -28,15 +24,19 @@ type McpConfigSectionProps = {
repo: Repo
}
+const EMPTY_WORKTREES: Worktree[] = []
+
+function countServers(configs: LoadedMcpConfigInspection[]): number {
+ return configs.reduce((sum, config) => sum + config.servers.length, 0)
+}
+
export function McpConfigSection({ repo }: McpConfigSectionProps): React.JSX.Element {
const openFile = useAppStore((state) => state.openFile)
const setActiveView = useAppStore((state) => state.setActiveView)
const setActiveWorktree = useAppStore((state) => state.setActiveWorktree)
const ensureWorktreeRootGroup = useAppStore((state) => state.ensureWorktreeRootGroup)
const activeWorktreeId = useAppStore((state) => state.activeWorktreeId)
- const worktreesForRepo = useAppStore(
- (state) => state.worktreesByRepo[repo.id] ?? EMPTY_MCP_WORKTREES
- )
+ const worktreesForRepo = useAppStore((state) => state.worktreesByRepo[repo.id] ?? EMPTY_WORKTREES)
const sshConnectionStatus = useAppStore((state) =>
repo.connectionId ? state.sshConnectionStates.get(repo.connectionId)?.status : null
)
@@ -51,10 +51,21 @@ export function McpConfigSection({ repo }: McpConfigSectionProps): React.JSX.Ele
const connectionId = repo.connectionId ?? undefined
const isWindows = isWindowsUserAgent()
- const targetWorktree = useMemo(
- () => selectMcpTargetWorktree(repo, worktreesForRepo, activeWorktreeId),
- [activeWorktreeId, repo, worktreesForRepo]
- )
+ const targetWorktree = useMemo(() => {
+ if (activeWorktreeId && getRepoIdFromWorktreeId(activeWorktreeId) === repo.id) {
+ return (
+ worktreesForRepo.find((worktree) => worktree.id === activeWorktreeId) ?? {
+ id: activeWorktreeId,
+ path: repo.path
+ }
+ )
+ }
+ return (
+ worktreesForRepo.find((worktree) => worktree.isMainWorktree) ??
+ worktreesForRepo.find((worktree) => worktree.path === repo.path) ??
+ worktreesForRepo[0] ?? { id: `${repo.id}::${repo.path}`, path: repo.path }
+ )
+ }, [activeWorktreeId, repo.id, repo.path, worktreesForRepo])
const targetWorktreeId = targetWorktree.id
const targetRootPath = targetWorktree.path
const detectedCount = useMemo(() => configs.filter((config) => config.exists).length, [configs])
@@ -85,7 +96,7 @@ export function McpConfigSection({ repo }: McpConfigSectionProps): React.JSX.Ele
),
[targetRootPath]
)
- const serverCount = useMemo(() => countMcpConfigServers(configs), [configs])
+ const serverCount = useMemo(() => countServers(configs), [configs])
const canCreateStarter = detectedCount === 0 && !inspectionUnavailable
const loadConfigs = useCallback(async (): Promise => {
diff --git a/src/renderer/src/components/settings/mcp-config-inspection-state.ts b/src/renderer/src/components/settings/mcp-config-inspection-state.ts
deleted file mode 100644
index 0abb25b62b5..00000000000
--- a/src/renderer/src/components/settings/mcp-config-inspection-state.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import type { Repo, Worktree } from '../../../../shared/types'
-import { getRepoIdFromWorktreeId } from '../../../../shared/worktree-id'
-import type { LoadedMcpConfigInspection } from './McpConfigFileRow'
-
-export const EMPTY_MCP_WORKTREES: Worktree[] = []
-
-export function isMissingMcpConfigFileError(error: unknown): boolean {
- const message = error instanceof Error ? error.message : String(error)
- return /ENOENT|no such file|not found/i.test(message)
-}
-
-export function countMcpConfigServers(configs: LoadedMcpConfigInspection[]): number {
- return configs.reduce((sum, config) => sum + config.servers.length, 0)
-}
-
-export function selectMcpTargetWorktree(
- repo: Repo,
- worktreesForRepo: readonly Worktree[],
- activeWorktreeId: string | null
-): Pick {
- if (activeWorktreeId && getRepoIdFromWorktreeId(activeWorktreeId) === repo.id) {
- return (
- worktreesForRepo.find((worktree) => worktree.id === activeWorktreeId) ?? {
- id: activeWorktreeId,
- path: repo.path
- }
- )
- }
- return (
- worktreesForRepo.find((worktree) => worktree.isMainWorktree) ??
- worktreesForRepo.find((worktree) => worktree.path === repo.path) ??
- worktreesForRepo[0] ?? { id: `${repo.id}::${repo.path}`, path: repo.path }
- )
-}
diff --git a/src/renderer/src/components/stats/ClaudeUsagePane.tsx b/src/renderer/src/components/stats/ClaudeUsagePane.tsx
index 946104bbadd..32536628f8d 100644
--- a/src/renderer/src/components/stats/ClaudeUsagePane.tsx
+++ b/src/renderer/src/components/stats/ClaudeUsagePane.tsx
@@ -5,18 +5,29 @@ import {
DatabaseZap,
FolderKanban,
Gauge,
+ RefreshCw,
+ SlidersHorizontal,
Sparkles,
Waypoints
} from 'lucide-react'
import type { ClaudeUsageRange, ClaudeUsageScope } from '../../../../shared/claude-usage-types'
import { useAppStore } from '../../store'
+import { Button } from '../ui/button'
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuLabel,
+ DropdownMenuRadioGroup,
+ DropdownMenuRadioItem,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger
+} from '../ui/dropdown-menu'
+import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/tooltip'
import { ClaudeUsageDetails } from './ClaudeUsageDetails'
import { ClaudeUsageLoadingState } from './ClaudeUsageLoadingState'
import { ShareUsageButton } from './ShareUsageButton'
import { StatCard } from './StatCard'
-import { UsagePaneFilterControls } from './UsagePaneFilterControls'
-import { UsageTrackingDisabledCard } from './UsageTrackingDisabledCard'
-import { formatCost, formatTokens, formatUpdatedAt } from './usage-display-formatting'
+import { formatCost, formatTokens, formatUpdatedAt } from './usage-formatters'
import { translate } from '@/i18n/i18n'
const RANGE_OPTIONS: ClaudeUsageRange[] = ['7d', '30d', '90d', 'all']
@@ -76,21 +87,37 @@ export function ClaudeUsagePane(): React.JSX.Element {
if (!scanState?.enabled) {
return (
- handleSetEnabled(true)}
- />
+
+
+
+
+ {translate(
+ 'auto.components.stats.ClaudeUsagePane.6afacbee37',
+ 'Claude Usage Tracking'
+ )}
+
+
+ {translate(
+ 'auto.components.stats.ClaudeUsagePane.0cb1a36d7d',
+ 'Reads local Claude usage logs to show token, model, and session stats.'
+ )}
+
+
+
handleSetEnabled(true)}
+ className="relative inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border border-transparent bg-muted-foreground/30 transition-colors"
+ >
+
+
+
+
)
}
@@ -122,30 +149,92 @@ export function ClaudeUsagePane(): React.JSX.Element {
{summary && daily.length > 0 && (
)}
-
+
+
+
+
+
+
+
+
+
+
+ {translate('auto.components.stats.ClaudeUsagePane.dd29209b21', 'Filters')}
+
+
+
+
+
+ {translate('auto.components.stats.ClaudeUsagePane.f61cffb9c8', 'Scope')}
+
+ void setClaudeUsageScope(value as ClaudeUsageScope)}
+ >
+ {SCOPE_OPTIONS.map((option) => (
+
+ {option.label}
+
+ ))}
+
+
+
+ {translate('auto.components.stats.ClaudeUsagePane.505be9aac4', 'Range')}
+
+ void setClaudeUsageRange(value as ClaudeUsageRange)}
+ >
+ {RANGE_OPTIONS.map((option) => (
+
+ {RANGE_LABELS[option]}
+
+ ))}
+
+
+
+
+
+
+ void refreshClaudeUsage()}
+ disabled={scanState.isScanning}
+ aria-label={translate(
+ 'auto.components.stats.ClaudeUsagePane.c5b9b344d0',
+ 'Refresh Claude usage'
+ )}
+ >
+
+
+
+
+ {translate('auto.components.stats.ClaudeUsagePane.8d18bbb771', 'Refresh')}
+
+
+
+ void setClaudeUsageScope(value)}
- onRangeChange={(value) => void setClaudeUsageRange(value)}
- onRefresh={() => void refreshClaudeUsage()}
- onDisable={() => handleSetEnabled(false)}
- />
+ onClick={() => handleSetEnabled(false)}
+ className="relative inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border border-transparent bg-foreground transition-colors"
+ >
+
+
diff --git a/src/renderer/src/components/stats/CodexUsagePane.tsx b/src/renderer/src/components/stats/CodexUsagePane.tsx
index 255d9334030..2ebd6913d44 100644
--- a/src/renderer/src/components/stats/CodexUsagePane.tsx
+++ b/src/renderer/src/components/stats/CodexUsagePane.tsx
@@ -1,14 +1,32 @@
import { useEffect } from 'react'
-import { Activity, Brain, Coins, DatabaseZap, FolderKanban, Sparkles } from 'lucide-react'
+import {
+ Activity,
+ Brain,
+ Coins,
+ DatabaseZap,
+ FolderKanban,
+ RefreshCw,
+ SlidersHorizontal,
+ Sparkles
+} from 'lucide-react'
import type { CodexUsageRange, CodexUsageScope } from '../../../../shared/codex-usage-types'
import { useAppStore } from '../../store'
+import { Button } from '../ui/button'
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuLabel,
+ DropdownMenuRadioGroup,
+ DropdownMenuRadioItem,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger
+} from '../ui/dropdown-menu'
+import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/tooltip'
import { ClaudeUsageLoadingState } from './ClaudeUsageLoadingState'
import { CodexUsageDetails } from './CodexUsageDetails'
import { ShareUsageButton } from './ShareUsageButton'
import { StatCard } from './StatCard'
-import { UsagePaneFilterControls } from './UsagePaneFilterControls'
-import { UsageTrackingDisabledCard } from './UsageTrackingDisabledCard'
-import { formatCost, formatTokens, formatUpdatedAt } from './usage-display-formatting'
+import { formatCost, formatTokens, formatUpdatedAt } from './usage-formatters'
import { translate } from '@/i18n/i18n'
const RANGE_OPTIONS: CodexUsageRange[] = ['7d', '30d', '90d', 'all']
@@ -68,18 +86,34 @@ export function CodexUsagePane(): React.JSX.Element {
if (!scanState?.enabled) {
return (
- handleSetEnabled(true)}
- />
+
+
+
+
+ {translate('auto.components.stats.CodexUsagePane.408210470c', 'Codex Usage Tracking')}
+
+
+ {translate(
+ 'auto.components.stats.CodexUsagePane.13badcd8f2',
+ 'Reads local Codex usage logs to show token, model, and session stats.'
+ )}
+
+
+
handleSetEnabled(true)}
+ className="relative inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border border-transparent bg-muted-foreground/30 transition-colors"
+ >
+
+
+
+
)
}
@@ -117,30 +151,92 @@ export function CodexUsagePane(): React.JSX.Element {
{summary && daily.length > 0 && (
)}
-
+
+
+
+
+
+
+
+
+
+
+ {translate('auto.components.stats.CodexUsagePane.1af1a39b2f', 'Filters')}
+
+
+
+
+
+ {translate('auto.components.stats.CodexUsagePane.6d68e8399a', 'Scope')}
+
+ void setCodexUsageScope(value as CodexUsageScope)}
+ >
+ {SCOPE_OPTIONS.map((option) => (
+
+ {option.label}
+
+ ))}
+
+
+
+ {translate('auto.components.stats.CodexUsagePane.89162e019b', 'Range')}
+
+ void setCodexUsageRange(value as CodexUsageRange)}
+ >
+ {RANGE_OPTIONS.map((option) => (
+
+ {RANGE_LABELS[option]}
+
+ ))}
+
+
+
+
+
+
+ void refreshCodexUsage()}
+ disabled={scanState.isScanning}
+ aria-label={translate(
+ 'auto.components.stats.CodexUsagePane.ec4d270e2c',
+ 'Refresh Codex usage'
+ )}
+ >
+
+
+
+
+ {translate('auto.components.stats.CodexUsagePane.3022cda443', 'Refresh')}
+
+
+
+ void setCodexUsageScope(value)}
- onRangeChange={(value) => void setCodexUsageRange(value)}
- onRefresh={() => void refreshCodexUsage()}
- onDisable={() => handleSetEnabled(false)}
- />
+ onClick={() => handleSetEnabled(false)}
+ className="relative inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border border-transparent bg-foreground transition-colors"
+ >
+
+
diff --git a/src/renderer/src/components/stats/OpenCodeUsagePane.tsx b/src/renderer/src/components/stats/OpenCodeUsagePane.tsx
index b23689fea03..c09f2a5a179 100644
--- a/src/renderer/src/components/stats/OpenCodeUsagePane.tsx
+++ b/src/renderer/src/components/stats/OpenCodeUsagePane.tsx
@@ -1,16 +1,34 @@
import { useEffect } from 'react'
-import { Activity, Brain, Coins, DatabaseZap, FolderKanban, Sparkles } from 'lucide-react'
+import {
+ Activity,
+ Brain,
+ Coins,
+ DatabaseZap,
+ FolderKanban,
+ RefreshCw,
+ SlidersHorizontal,
+ Sparkles
+} from 'lucide-react'
import type {
OpenCodeUsageRange,
OpenCodeUsageScope
} from '../../../../shared/opencode-usage-types'
import { useAppStore } from '../../store'
+import { Button } from '../ui/button'
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuLabel,
+ DropdownMenuRadioGroup,
+ DropdownMenuRadioItem,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger
+} from '../ui/dropdown-menu'
+import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/tooltip'
import { ClaudeUsageLoadingState } from './ClaudeUsageLoadingState'
import { OpenCodeUsageDetails } from './OpenCodeUsageDetails'
import { StatCard } from './StatCard'
-import { UsagePaneFilterControls } from './UsagePaneFilterControls'
-import { UsageTrackingDisabledCard } from './UsageTrackingDisabledCard'
-import { formatCost, formatTokens, formatUpdatedAt } from './usage-display-formatting'
+import { formatCost, formatTokens, formatUpdatedAt } from './usage-formatters'
import { translate } from '@/i18n/i18n'
const RANGE_OPTIONS: OpenCodeUsageRange[] = ['7d', '30d', '90d', 'all']
@@ -73,21 +91,37 @@ export function OpenCodeUsagePane(): React.JSX.Element {
if (!scanState?.enabled) {
return (
- handleSetEnabled(true)}
- />
+
+
+
+
+ {translate(
+ 'auto.components.stats.OpenCodeUsagePane.bea80ceae0',
+ 'OpenCode Usage Tracking'
+ )}
+
+
+ {translate(
+ 'auto.components.stats.OpenCodeUsagePane.b8b3522436',
+ 'Reads local OpenCode usage logs to show token, model, and session stats.'
+ )}
+
+
+
handleSetEnabled(true)}
+ className="relative inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border border-transparent bg-muted-foreground/30 transition-colors"
+ >
+
+
+
+
)
}
@@ -127,30 +161,94 @@ export function OpenCodeUsagePane(): React.JSX.Element {
: ''}
- void setOpenCodeUsageScope(value)}
- onRangeChange={(value) => void setOpenCodeUsageRange(value)}
- onRefresh={() => void refreshOpenCodeUsage()}
- onDisable={() => handleSetEnabled(false)}
- />
+
+
+
+
+
+
+
+
+
+
+
+
+ {translate('auto.components.stats.OpenCodeUsagePane.01583b30aa', 'Filters')}
+
+
+
+
+
+ {translate('auto.components.stats.OpenCodeUsagePane.40d283c837', 'Scope')}
+
+ void setOpenCodeUsageScope(value as OpenCodeUsageScope)}
+ >
+ {SCOPE_OPTIONS.map((option) => (
+
+ {option.label}
+
+ ))}
+
+
+
+ {translate('auto.components.stats.OpenCodeUsagePane.b5ed5c9fd0', 'Range')}
+
+ void setOpenCodeUsageRange(value as OpenCodeUsageRange)}
+ >
+ {RANGE_OPTIONS.map((option) => (
+
+ {RANGE_LABELS[option]}
+
+ ))}
+
+
+
+
+
+
+ void refreshOpenCodeUsage()}
+ disabled={scanState.isScanning}
+ aria-label={translate(
+ 'auto.components.stats.OpenCodeUsagePane.bed558df0b',
+ 'Refresh OpenCode usage'
+ )}
+ >
+
+
+
+
+ {translate('auto.components.stats.OpenCodeUsagePane.603cd138dc', 'Refresh')}
+
+
+
+ handleSetEnabled(false)}
+ className="relative inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border border-transparent bg-foreground transition-colors"
+ >
+
+
+
diff --git a/src/renderer/src/components/stats/UsagePaneFilterControls.tsx b/src/renderer/src/components/stats/UsagePaneFilterControls.tsx
deleted file mode 100644
index 5b64e9b0802..00000000000
--- a/src/renderer/src/components/stats/UsagePaneFilterControls.tsx
+++ /dev/null
@@ -1,124 +0,0 @@
-import { RefreshCw, SlidersHorizontal } from 'lucide-react'
-import { Button } from '../ui/button'
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuLabel,
- DropdownMenuRadioGroup,
- DropdownMenuRadioItem,
- DropdownMenuSeparator,
- DropdownMenuTrigger
-} from '../ui/dropdown-menu'
-import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/tooltip'
-
-type UsagePaneFilterControlsProps
= {
- scope: Scope
- range: Range
- scopeOptions: { value: Scope; label: string }[]
- rangeOptions: Range[]
- rangeLabels: Record
- isScanning: boolean
- optionsLabel: string
- filtersLabel: string
- scopeLabel: string
- rangeLabel: string
- refreshLabel: string
- enableLabel: string
- onScopeChange: (value: Scope) => void
- onRangeChange: (value: Range) => void
- onRefresh: () => void
- onDisable: () => void
-}
-
-export function UsagePaneFilterControls({
- scope,
- range,
- scopeOptions,
- rangeOptions,
- rangeLabels,
- isScanning,
- optionsLabel,
- filtersLabel,
- scopeLabel,
- rangeLabel,
- refreshLabel,
- enableLabel,
- onScopeChange,
- onRangeChange,
- onRefresh,
- onDisable
-}: UsagePaneFilterControlsProps): React.JSX.Element {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
- {filtersLabel}
-
-
-
-
- {scopeLabel}
- onScopeChange(value as Scope)}
- >
- {scopeOptions.map((option) => (
-
- {option.label}
-
- ))}
-
-
- {rangeLabel}
- onRangeChange(value as Range)}
- >
- {rangeOptions.map((option) => (
-
- {rangeLabels[option]}
-
- ))}
-
-
-
-
-
-
-
-
-
-
-
- {refreshLabel}
-
-
-
-
-
-
-
- )
-}
diff --git a/src/renderer/src/components/stats/UsageTrackingDisabledCard.tsx b/src/renderer/src/components/stats/UsageTrackingDisabledCard.tsx
deleted file mode 100644
index 1bed4177439..00000000000
--- a/src/renderer/src/components/stats/UsageTrackingDisabledCard.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-type UsageTrackingDisabledCardProps = {
- title: string
- description: string
- enableLabel: string
- onEnable: () => void
-}
-
-export function UsageTrackingDisabledCard({
- title,
- description,
- enableLabel,
- onEnable
-}: UsageTrackingDisabledCardProps): React.JSX.Element {
- return (
-
-
-
-
{title}
-
{description}
-
-
-
-
-
-
- )
-}
diff --git a/src/renderer/src/components/stats/usage-display-formatting.ts b/src/renderer/src/components/stats/usage-display-formatting.ts
deleted file mode 100644
index ae3345a1e60..00000000000
--- a/src/renderer/src/components/stats/usage-display-formatting.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-export function formatTokens(value: number): string {
- if (value >= 1_000_000) {
- return `${(value / 1_000_000).toFixed(1)}M`
- }
- if (value >= 1_000) {
- return `${(value / 1_000).toFixed(1)}k`
- }
- return value.toLocaleString()
-}
-
-export function formatCost(value: number | null): string {
- if (value === null) {
- return 'n/a'
- }
- return value < 0.01 ? `$${value.toFixed(4)}` : `$${value.toFixed(2)}`
-}
-
-export function formatUpdatedAt(timestamp: number | null): string {
- if (!timestamp) {
- return 'Not scanned yet'
- }
- return `Updated ${new Date(timestamp).toLocaleString()}`
-}
-
-export function formatSessionTime(timestamp: string): string {
- const parsed = new Date(timestamp)
- if (Number.isNaN(parsed.getTime())) {
- return timestamp
- }
- return parsed.toLocaleString(undefined, {
- month: 'short',
- day: 'numeric',
- hour: 'numeric',
- minute: '2-digit'
- })
-}
diff --git a/src/renderer/src/lib/folder-workspace-path-status-cache-expiry.ts b/src/renderer/src/lib/folder-workspace-path-status-cache-expiry.ts
index 563daae5bb9..513a44d80d7 100644
--- a/src/renderer/src/lib/folder-workspace-path-status-cache-expiry.ts
+++ b/src/renderer/src/lib/folder-workspace-path-status-cache-expiry.ts
@@ -6,14 +6,14 @@ type FolderWorkspacePathStatusCacheClockEntry = {
}
export function useFolderWorkspacePathStatusCacheExpiryTick(
- entries: Record | null | undefined
+ entries: Record
): number {
const [tick, setTick] = useState(0)
useEffect(() => {
const now = Date.now()
let nextDelayMs = Number.POSITIVE_INFINITY
- for (const entry of Object.values(entries ?? {})) {
+ for (const entry of Object.values(entries)) {
const delayMs = entry.checkedAt + FOLDER_WORKSPACE_PATH_STATUS_TTL_MS - now
if (delayMs > 0) {
nextDelayMs = Math.min(nextDelayMs, delayMs)