diff --git a/apps/web/app/api/catalog/route.ts b/apps/web/app/api/catalog/route.ts index 00fdee0..8c27acd 100644 --- a/apps/web/app/api/catalog/route.ts +++ b/apps/web/app/api/catalog/route.ts @@ -1,21 +1,31 @@ import { - buildCatalogStats, - filterCatalogStats, + buildFilteredCatalogStats, + type CatalogFilterParams, } from "../../../lib/catalogStats"; import { loadBenchmarkArtifacts, loadRunArtifacts } from "../../../lib/data"; import { catalogStatsToCsv } from "../../../lib/listExport"; +function readCatalogFilters(url: URL): CatalogFilterParams { + return { + q: url.searchParams.get("q") ?? undefined, + model: url.searchParams.get("model") ?? undefined, + preset: url.searchParams.get("preset") ?? undefined, + fast: url.searchParams.get("fast") ?? undefined, + from: url.searchParams.get("from") ?? undefined, + to: url.searchParams.get("to") ?? undefined, + }; +} + export async function GET(request: Request) { const url = new URL(request.url); const format = url.searchParams.get("format") ?? "json"; - const q = url.searchParams.get("q") ?? undefined; + const filters = readCatalogFilters(url); const [runs, benchmarks] = await Promise.all([ loadRunArtifacts(), loadBenchmarkArtifacts(), ]); - const rawStats = buildCatalogStats(runs, benchmarks); - const stats = filterCatalogStats(rawStats, q); + const stats = buildFilteredCatalogStats(runs, benchmarks, filters); if (format === "csv") { const csv = catalogStatsToCsv(stats); @@ -30,9 +40,8 @@ export async function GET(request: Request) { } return Response.json({ - query: q ?? null, + filters, totals: stats.totals, - rawTotals: rawStats.totals, models: stats.models, presets: stats.presets, combos: stats.combos, diff --git a/apps/web/app/catalog/page.tsx b/apps/web/app/catalog/page.tsx index 31e8f46..868cb31 100644 --- a/apps/web/app/catalog/page.tsx +++ b/apps/web/app/catalog/page.tsx @@ -2,8 +2,15 @@ import type { Metadata } from "next"; import Link from "next/link"; import { CollapsibleFilterCard } from "../../components/CollapsibleFilterCard"; import { MetricCard } from "../../components/MetricCard"; +import { ModelFilterSelect } from "../../components/ModelFilterSelect"; +import { PresetFilterSelect } from "../../components/PresetFilterSelect"; import { ResponsiveTable } from "../../components/ResponsiveTable"; -import { buildCatalogStats, filterCatalogStats } from "../../lib/catalogStats"; +import { collectArtifactFacets } from "../../lib/artifactFacets"; +import { + buildCatalogStats, + buildFilteredCatalogStats, + hasActiveCatalogFilters, +} from "../../lib/catalogStats"; import { loadBenchmarkArtifacts, loadRunArtifacts } from "../../lib/data"; import { buildQueryString } from "../../lib/listPagination"; @@ -13,6 +20,11 @@ export const metadata: Metadata = { type CatalogSearchParams = { q?: string; + model?: string; + preset?: string; + fast?: string; + from?: string; + to?: string; }; function filterHref( @@ -33,9 +45,11 @@ export default async function CatalogPage({ loadRunArtifacts(), loadBenchmarkArtifacts(), ]); + const { models, presets } = collectArtifactFacets(runs, benchmarks); const rawStats = buildCatalogStats(runs, benchmarks); - const stats = filterCatalogStats(rawStats, params.q); + const stats = buildFilteredCatalogStats(runs, benchmarks, params); const query = (params.q ?? "").trim(); + const filtersActive = hasActiveCatalogFilters(params); const empty = runs.length === 0 && benchmarks.length === 0; const filteredCount = stats.models.length + stats.presets.length + stats.combos.length; @@ -72,10 +86,10 @@ export default async function CatalogPage({ {filteredCount} of {totalCount} catalog rows match - “{query}” + {query ? <> “{query}” : null} ) : ( <> @@ -93,6 +107,38 @@ export default async function CatalogPage({ defaultValue={params.q ?? ""} className="input" /> + + + + +