"use client" import { useCallback, useDeferredValue, useEffect, useMemo, useState } from "react" import { ArrowRightLeft, Search, X } from "lucide-react" import { type BenchmarkEvaluationCardData } from "@/components/benchmark-evaluation-card" import { DeveloperTable } from "@/components/developer-table" import { InfiniteScrollSentinel } from "@/components/infinite-scroll" import { ModelCompareDialog } from "@/components/model-compare-dialog" import { ModelTable } from "@/components/model-table" import { Navigation } from "@/components/navigation" import { ParamRangePicker } from "@/components/param-range-picker" import { fetchDevelopers, fetchModelCards, fetchBenchmarkMetadata, type DeveloperListItem } from "@/lib/dashboard-data-client" import type { BenchmarkCard } from "@/lib/benchmark-schema" import { PARAM_RANGE_MAX_INDEX, paramStepToNumeric } from "@/lib/param-range" const PAGE_SIZE = 40 const MAX_COMPARE_MODELS = 4 type ModelSort = "benchmarks" | "results" | "name" | "released" | "params" type DevSort = "coverage" | "evaluated" | "models" | "name" function safeTimestamp(value: string | null | undefined) { if (!value) return 0 const numeric = Number(value) if (!Number.isNaN(numeric) && !value.includes("-")) { return numeric > 1_000_000_000_000 ? numeric : numeric * 1000 } const parsed = new Date(value).getTime() return Number.isNaN(parsed) ? 0 : parsed } export default function ModelsPage() { const [evaluations, setEvaluations] = useState([]) const [developers, setDevelopers] = useState([]) const [benchmarkCards, setBenchmarkCards] = useState>({}) const [loadingModels, setLoadingModels] = useState(true) const [loadingDevelopers, setLoadingDevelopers] = useState(false) const [developersReady, setDevelopersReady] = useState(false) const [groupByDeveloper, setGroupByDeveloper] = useState(false) const [modelSortBy, setModelSortBy] = useState("benchmarks") const [developerSortBy, setDeveloperSortBy] = useState("coverage") const [searchQuery, setSearchQuery] = useState("") const [selectedModelIds, setSelectedModelIds] = useState([]) const [compareOpen, setCompareOpen] = useState(false) const [visibleCount, setVisibleCount] = useState(PAGE_SIZE) const [minParamStep, setMinParamStep] = useState(0) const [maxParamStep, setMaxParamStep] = useState(PARAM_RANGE_MAX_INDEX) const [showUnknownSize, setShowUnknownSize] = useState(true) const deferredSearchQuery = useDeferredValue(searchQuery) const numericMinParams = useMemo(() => paramStepToNumeric(minParamStep, "min"), [minParamStep]) const numericMaxParams = useMemo(() => paramStepToNumeric(maxParamStep, "max"), [maxParamStep]) useEffect(() => { Promise.all([fetchModelCards(), fetchBenchmarkMetadata()]) .then(([cards, metadata]) => { setEvaluations(cards) setBenchmarkCards(metadata) }) .catch((error) => { console.error("Failed to load evaluations:", error) }) .finally(() => setLoadingModels(false)) }, []) useEffect(() => { if (!groupByDeveloper || developersReady || loadingDevelopers) return setLoadingDevelopers(true) fetchDevelopers() .then(setDevelopers) .catch((error) => { console.error("Failed to load developers:", error) }) .finally(() => { setLoadingDevelopers(false) setDevelopersReady(true) }) }, [developersReady, groupByDeveloper, loadingDevelopers]) const totalBenchmarks = useMemo(() => Object.keys(benchmarkCards).length, [benchmarkCards]) // Models — filter + sort const sortedEvaluations = useMemo(() => { const query = deferredSearchQuery.trim().toLowerCase() let filtered = evaluations filtered = filtered.filter((row) => { if (row.params_billions == null) return showUnknownSize if (numericMinParams != null && row.params_billions < numericMinParams) return false if (numericMaxParams != null && row.params_billions > numericMaxParams) return false return true }) if (query) { filtered = filtered.filter((row) => { return ( row.model_name.toLowerCase().includes(query) || row.canonical_model_name.toLowerCase().includes(query) || row.developer.toLowerCase().includes(query) || (row.benchmark_names ?? []).some((b) => b.toLowerCase().includes(query)) ) }) } return filtered.slice().sort((a, b) => { switch (modelSortBy) { case "name": return a.model_name.localeCompare(b.model_name) case "released": return safeTimestamp(b.release_date ?? b.latest_timestamp) - safeTimestamp(a.release_date ?? a.latest_timestamp) case "params": return (b.params_billions ?? 0) - (a.params_billions ?? 0) case "results": return b.evaluations_count - a.evaluations_count case "benchmarks": default: return b.benchmarks_count - a.benchmarks_count } }) }, [evaluations, deferredSearchQuery, modelSortBy, numericMinParams, numericMaxParams, showUnknownSize]) // Developers — filter + sort const sortedDevelopers = useMemo(() => { const query = deferredSearchQuery.trim().toLowerCase() let filtered = developers if (query) { filtered = filtered.filter( (dev) => dev.developer.toLowerCase().includes(query) || dev.popular_evals.some((ev) => ev.benchmark.toLowerCase().includes(query)), ) } return filtered.slice().sort((a, b) => { switch (developerSortBy) { case "evaluated": return b.evaluation_count - a.evaluation_count case "models": return b.model_count - a.model_count case "name": return a.developer.localeCompare(b.developer) case "coverage": default: return b.benchmark_count - a.benchmark_count } }) }, [developers, deferredSearchQuery, developerSortBy]) // Reset visible window when filter/sort changes useEffect(() => { setVisibleCount(PAGE_SIZE) }, [groupByDeveloper, modelSortBy, developerSortBy, deferredSearchQuery, minParamStep, maxParamStep, showUnknownSize]) const totalCount = groupByDeveloper ? sortedDevelopers.length : sortedEvaluations.length const visibleEvaluations = useMemo( () => sortedEvaluations.slice(0, visibleCount), [sortedEvaluations, visibleCount], ) const visibleDevelopers = useMemo( () => sortedDevelopers.slice(0, visibleCount), [sortedDevelopers, visibleCount], ) const hasMore = visibleCount < totalCount const handleLoadMore = useCallback(() => { setVisibleCount((current) => Math.min(current + PAGE_SIZE, totalCount)) }, [totalCount]) const toggleModelSelection = useCallback((id: string) => { setSelectedModelIds((current) => { if (current.includes(id)) return current.filter((existing) => existing !== id) if (current.length >= MAX_COMPARE_MODELS) return current return [...current, id] }) }, []) const selectedModels = useMemo( () => selectedModelIds .map((id) => evaluations.find((evaluation) => evaluation.id === id)) .filter((evaluation): evaluation is BenchmarkEvaluationCardData => Boolean(evaluation)), [evaluations, selectedModelIds], ) const loading = loadingModels || (groupByDeveloper && !developersReady) const totalResults = useMemo( () => sortedEvaluations.reduce((sum, row) => sum + row.evaluations_count, 0), [sortedEvaluations], ) return (
{/* HEADER --------------------------------------------------- */}
Index

{groupByDeveloper ? "Model developers" : "Models"}

{groupByDeveloper ? "Every reporting organisation in the corpus and the breadth of evaluation it ships." : ( <> Every indexed model and the shape of its published evaluation record. Coverage shows the share of the{" "} {totalBenchmarks.toLocaleString()}-benchmark {" "} registry that the developer (or a third party) has reported on. )}

{/* META + FILTER BAR — single row -------------------------- */}
{/* Inline meta — mono kicker stat strip */}
{totalCount.toLocaleString()} {groupByDeveloper ? "developers" : "models"} {!groupByDeveloper && ( · {totalResults.toLocaleString()} results )} {!groupByDeveloper && ( · {selectedModels.length}/{MAX_COMPARE_MODELS} tray )}
setSearchQuery(event.target.value)} placeholder={ groupByDeveloper ? "Search developers…" : "Search model or developer…" } />
{/* PARAM RANGE — its own row so the rail has room to breathe. Sharing the toolbar with stats / search / sort squished it. */} {!groupByDeveloper && (
{ setMinParamStep(0) setMaxParamStep(PARAM_RANGE_MAX_INDEX) }} showUnknownSize={showUnknownSize} onShowUnknownSizeChange={setShowUnknownSize} />
)} {/* TABLE ---------------------------------------------------- */} {loading ? (
Loading…
) : totalCount === 0 ? (

{groupByDeveloper ? "No developers found matching your filters." : "No models found matching your filters."}

) : groupByDeveloper ? ( ) : ( )} {/* COMPARE TRAY (sticky bottom) ---------------------------- */} {!groupByDeveloper && selectedModels.length > 0 && (
Compare tray
{selectedModels.map((model) => ( {model.model_name} ))}
)}
) }