import type { Metadata } from 'next';
import Link from 'next/link';
import { RailFilters } from '@/components/changes/rail-filters';
import { TerminalLayout } from '@/components/layout/terminal';
import { ActiveFilters } from '@/components/listing/filters';
import { BreadcrumbLd } from '@/components/meta/breadcrumb-ld';
import { Chip, EntityBadge } from '@/components/ui/badges';
import { DataTable, EmptyRow, Td, Th } from '@/components/ui/data-table';
import { EntityLink } from '@/components/ui/entity';
import { Hint } from '@/components/ui/hint';
import { Pagination, withParams } from '@/components/ui/pagination';
import { Container, Note, PageHeader } from '@/components/ui/section';
import { EmptyState, Unavailable } from '@/components/ui/unavailable';
import { api, safe } from '@/lib/api';
import { fmtDate, fmtInt } from '@/lib/format';
import { routes, SITE_NAME } from '@/lib/site';
import type { EntityDetail, EntitySummary } from '@/lib/types';
export const metadata: Metadata = { title: 'AI research papers — authors, models introduced, datasets, benchmarks', description: 'Research papers in the atlas with their authors, publication dates, arXiv categories and the models, datasets and benchmarks they are linked to — as stated by model cards and paper metadata.', alternates: { canonical: '/papers' } };
export const revalidate = 300;
type SP = Record;
const LIMIT = 25;
const KEYS = ['q', 'category', 'org', 'since', 'until', 'sort', 'offset'] as const;
const SORTS = [
{ value: 'published', label: 'Recently published' },
{ value: 'updated', label: 'Recently updated' },
{ value: 'name', label: 'Title' },
];
function str(v: unknown): string | null {
return typeof v === 'string' && v.trim() ? v : null;
}
function list(v: unknown): string[] {
return Array.isArray(v) ? v.filter((x) => x !== null && x !== undefined).map(String) : [];
}
type Linked = { models: EntitySummary[]; datasets: EntitySummary[]; benchmarks: EntitySummary[]; code: EntitySummary[] };
function linked(d: EntityDetail | null): Linked {
const out: Linked = { models: [], datasets: [], benchmarks: [], code: [] };
if (!d) return out;
const seen = new Set();
for (const g of d.relations ?? [])
for (const it of g.items) {
if (seen.has(it.id)) continue;
seen.add(it.id);
if (it.entity_type === 'model' || it.entity_type === 'artifact') out.models.push(it);
else if (it.entity_type === 'dataset') out.datasets.push(it);
else if (it.entity_type === 'benchmark') out.benchmarks.push(it);
else if (['repository', 'framework', 'library'].includes(it.entity_type)) out.code.push(it);
}
return out;
}
export default async function PapersPage({ searchParams }: { searchParams: Promise }) {
const sp = await searchParams;
const current: Record = {};
for (const k of KEYS) if (sp[k]) current[k] = sp[k];
const offset = Math.max(0, Number(current.offset) || 0);
const sort = current.sort ?? 'published';
const [page, orgs] = await Promise.all([safe(api.papers({ ...current, sort, limit: LIMIT, offset })), safe(api.companies({ limit: 40, sort: 'models' }))]);
// "Introduces" needs each paper's relations: fetch the page's details in parallel (local API, ISR-cached).
const details = page ? await Promise.all(page.items.map((p) => safe(api.entity(p.slug)))) : [];
const href = (patch: Record) => withParams('/papers', current, patch);
const cats = new Map();
for (const p of page?.items ?? []) for (const c of list(p.attributes?.categories)) cats.set(c, (cats.get(c) ?? 0) + 1);
const activeCount = Object.keys(current).filter((k) => !['sort', 'offset'].includes(k)).length;
const filters = (
o.slug === current.org).map((o) => ({ value: o.slug, label: o.name })), note: 'Stated publisher only — arXiv metadata carries no affiliation.' },
{ kind: 'row', fields: [{ kind: 'date', name: 'since', label: 'Since', value: current.since }, { kind: 'date', name: 'until', label: 'Until', value: current.until }] },
{ kind: 'select', name: 'sort', label: 'Sort', value: sort, any: SORTS[0]!.label, options: SORTS.slice(1) },
]}
/>
);
const inspector = (
Categories on this page
{cats.size === 0 ? (
—
) : (
{[...cats.entries()]
.sort((a, b) => b[1] - a[1])
.slice(0, 16)
.map(([c, n]) => (
{c} {n}
))}
)}
“Introduces” lists the models whose cards cite the paper (described_by ). Author names link to researcher pages when a record exists. Research graph →
);
return (
<>
{fmtInt(page.total)} papers
: undefined} className="pb-3">
href(p)} className="mt-3" />
{!page ? (
) : page.items.length === 0 ? (
Try another title, category or organization.
) : (
<>
Title
Authors
Organization
Published
Introduces
Datasets
Benchmarks
Code
{page.items.length === 0 && No rows. }
{page.items.map((e, i) => {
const a = e.attributes ?? {};
const authors = list(a.authors);
const l = linked(details[i] ?? null);
const code = str(a.code_url);
const primary = str(a.primary_category);
return (
{str(a.arxiv_id) && arXiv:{str(a.arxiv_id)} }
{primary && (
{primary}
)}
{authors.length ? (
<>
{authors.slice(0, 3).join(', ')}
{authors.length > 3 && +{authors.length - 3} }
>
) : (
—
)}
{e.organization ? (
{e.organization.name}
) : str(a.venue) ? (
{str(a.venue)}
) : (
—
)}
{str(a.published_at) ? fmtDate(str(a.published_at)) : — }
{l.models.length ? (
{l.models.slice(0, 3).map((m) => (
))}
{l.models.length > 3 && +{l.models.length - 3} }
) : details[i] === null ? (
unavailable
) : (
—
)}
{l.datasets.length ? l.datasets.slice(0, 2).map((x) => ) : — }
{l.benchmarks.length ? l.benchmarks.slice(0, 2).map((x) => ) : — }
{l.code.length ? (
l.code.slice(0, 2).map((x) => )
) : code ? (
{code.replace(/^https?:\/\/(www\.)?/, '').slice(0, 32)}
) : (
—
)}
);
})}
href({ offset: o || undefined })} className="mt-4" />
Author lists and categories are copied from the paper's own metadata (arXiv, publisher). Linked models, datasets, benchmarks and code come from stated relations only; a dash means no source stated one.
>
)}
>
);
}