Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 53 additions & 20 deletions components/leaderboard-view.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client'

import { useCallback, useMemo, useState } from 'react'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useSearchParams, useRouter, usePathname } from 'next/navigation'
import type { LeaderboardEntry, BenchmarkVersion } from '@/lib/types'
import { PROVIDER_COLORS } from '@/lib/types'
Expand Down Expand Up @@ -52,6 +52,8 @@ export function LeaderboardView({ entries, lastUpdated, versions, currentVersion
const [providerFilter, setProviderFilterState] = useState<string | null>(initialProvider)
const [openWeightsOnly, setOpenWeightsOnlyState] = useState<boolean>(initialOpenWeights)
const [graphSubTab, setGraphSubTabState] = useState<GraphSubTab>(initialGraphTab)
// Hidden providers — managed here so Header counts stay in sync with graph legend toggles
const [hiddenProviders, setHiddenProviders] = useState<Set<string>>(new Set())

// Helper to update URL params without full page reload
const updateUrl = useCallback((updates: Record<string, string | null>) => {
Expand Down Expand Up @@ -100,31 +102,57 @@ export function LeaderboardView({ entries, lastUpdated, versions, currentVersion
updateUrl({ official: v ? null : 'false' })
}, [updateUrl])

const filteredEntries = useMemo(() => {
let result = entries
if (providerFilter) {
result = result.filter(
(entry) => entry.provider.toLowerCase() === providerFilter.toLowerCase()
)
}
if (openWeightsOnly) {
result = result.filter((entry) => entry.weights === 'Open')
}
return result
// Business-level filters only (provider filter, open weights).
// Used for legend provider list and all charts/tables.
const businessFilteredEntries = useMemo(() => {
return entries.filter(entry => {
if (providerFilter && entry.provider.toLowerCase() !== providerFilter.toLowerCase()) return false
if (openWeightsOnly && entry.weights !== 'Open') return false
return true
})
}, [entries, providerFilter, openWeightsOnly])

// Scatter-visible entries: business filters + legend-hidden providers.
// Used only for scatter graph: chart dots and header totalRuns.
const scatterVisibleEntries = useMemo(() => {
return businessFilteredEntries.filter(entry =>
!hiddenProviders.has(entry.provider.toLowerCase())
)
}, [businessFilteredEntries, hiddenProviders])

// When business filters change, prune hiddenProviders to remove providers
// that are no longer present (e.g. after changing provider filter or open-weights).
const prevBusinessFiltersRef = useRef({ providerFilter, openWeightsOnly })
useEffect(() => {
const prev = prevBusinessFiltersRef.current
if (prev.providerFilter !== providerFilter || prev.openWeightsOnly !== openWeightsOnly) {
prevBusinessFiltersRef.current = { providerFilter, openWeightsOnly }
const currentProviders = new Set(businessFilteredEntries.map(e => e.provider.toLowerCase()))
setHiddenProviders(prev => {
const pruned = new Set([...prev].filter(k => currentProviders.has(k)))
return pruned.size === prev.size ? prev : pruned
})
}
}, [providerFilter, openWeightsOnly, businessFilteredEntries])

const providerColor = providerFilter
? PROVIDER_COLORS[providerFilter.toLowerCase()] || '#666'
: undefined

// Which entries to show in the Header stats:
// - In scatter graph view: use scatterVisibleEntries (excludes legend-hidden providers)
// - Otherwise: use businessFilteredEntries (legend-hidden providers don't affect totals)
const headerEntries =
view === 'graphs' && graphSubTab === 'scatter' ? scatterVisibleEntries : businessFilteredEntries

const totalRuns = useMemo(() => {
return filteredEntries.reduce((sum, entry) => sum + (entry.submission_count ?? 0), 0)
}, [filteredEntries])
return headerEntries.reduce((sum, entry) => sum + (entry.submission_count ?? 0), 0)
}, [headerEntries])

return (
<div className="min-h-screen bg-background">
<LeaderboardHeader
filteredEntryCount={filteredEntries.length}
filteredEntryCount={headerEntries.length}
totalRuns={totalRuns}
versions={versions}
currentVersion={currentVersion}
Expand Down Expand Up @@ -167,23 +195,28 @@ export function LeaderboardView({ entries, lastUpdated, versions, currentVersion
</div>

{graphSubTab === 'scatter' && (
<ScatterGraphs entries={filteredEntries} scoreMode={scoreMode} />
<ScatterGraphs
entries={businessFilteredEntries}
scoreMode={scoreMode}
hiddenProviders={hiddenProviders}
onHiddenProvidersChange={setHiddenProviders}
/>
)}
{graphSubTab === 'heatmap' && (
<TaskHeatmap entries={filteredEntries} scoreMode={scoreMode} />
<TaskHeatmap entries={businessFilteredEntries} scoreMode={scoreMode} />
)}
{graphSubTab === 'distribution' && (
<ScoreDistribution entries={filteredEntries} scoreMode={scoreMode} currentVersion={currentVersion} officialOnly={officialOnlyState} />
<ScoreDistribution entries={businessFilteredEntries} scoreMode={scoreMode} currentVersion={currentVersion} officialOnly={officialOnlyState} />
)}
{graphSubTab === 'radar' && (
<ModelRadar entries={filteredEntries} scoreMode={scoreMode} />
<ModelRadar entries={businessFilteredEntries} scoreMode={scoreMode} />
)}

<KiloClawAdCard />
</div>
) : (
<SimpleLeaderboard
entries={filteredEntries}
entries={businessFilteredEntries}
view={view as 'success' | 'speed' | 'cost' | 'value'}
scoreMode={scoreMode}
benchmarkVersion={currentVersion}
Expand Down
10 changes: 6 additions & 4 deletions components/scatter-graphs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ type GraphTab = 'perf-vs-cost' | 'perf-vs-speed'
interface ScatterGraphsProps {
entries: LeaderboardEntry[]
scoreMode: 'best' | 'average'
// Controlled from LeaderboardView so hidden state syncs with Header
hiddenProviders: Set<string>
onHiddenProvidersChange: (next: Set<string> | ((prev: Set<string>) => Set<string>)) => void
}

interface DataPoint {
Expand Down Expand Up @@ -285,16 +288,15 @@ function ProviderLegend({ providers, hiddenProviders, onToggle }: {

// --- Main component ---

export function ScatterGraphs({ entries, scoreMode }: ScatterGraphsProps) {
export function ScatterGraphs({ entries, scoreMode, hiddenProviders, onHiddenProvidersChange }: ScatterGraphsProps) {
const [graphTab, setGraphTab] = useState<GraphTab>('perf-vs-cost')
const [hiddenProviders, setHiddenProviders] = useState<Set<string>>(new Set())

const toggleProvider = (provider: string) => {
if (provider === '__reset__') {
setHiddenProviders(new Set())
onHiddenProvidersChange(new Set())
return
}
setHiddenProviders(prev => {
onHiddenProvidersChange(prev => {
const next = new Set(prev)
if (next.has(provider)) {
next.delete(provider)
Expand Down