HTML 77.2%
TypeScript 10.5%
Python 9.6%
JavaScript 2.5%
1import { ExternalLink } from 'lucide-react';2import Link from 'next/link';3import type { ReactNode } from 'react';4import { ChangeRow } from '@/components/changes/change-row';5import { EntityBadge, ImportanceMark, OpennessBadge } from '@/components/ui/badges';6import { DataTable, EmptyRow, Td, Th } from '@/components/ui/data-table';7import { EntityLink } from '@/components/ui/entity';8import { Note, Section } from '@/components/ui/section';9import { EmptyState } from '@/components/ui/unavailable';10import { fmtDate, fmtDeltaPct, fmtInt, fmtParams, fmtScore, fmtTokens, fmtUsdPerM, hostOf, num } from '@/lib/format';11import { routes } from '@/lib/site';12import type { ChangeEvent, DiffPayload11, EntitySummary } from '@/lib/types';1314/* Diff sections (server). Dense rows with typed deltas; every row keeps its source link and a History link. */1516export function Capped({ shown, total, what }: { shown: number; total: number | null; what: string }) {17 if (total === null || total <= shown) return null;18 return <Note className="mt-2">Showing the first {fmtInt(shown)} of {fmtInt(total)} {what}. Narrow the scope or the dates to see everything.</Note>;19}2021function SectionTitle({ title, n }: { title: string; n: number | null }) {22 return (23 <>24 {title} <span className="tnum text-base font-normal text-ink-3">{fmtInt(n)}</span>25 </>26 );27}2829function Src({ url }: { url: string | null }) {30 if (!url) return <span className="text-ink-3">—</span>;31 return (32 <a href={url} target="_blank" rel="noopener noreferrer" className="inline-flex items-center gap-1 text-xs text-ink-3 hover:text-accent">33 {hostOf(url) ?? 'source'} <ExternalLink className="size-3" aria-hidden />34 </a>35 );36}37function When({ e }: { e: ChangeEvent }) {38 const at = e.occurred_at ?? e.effective_at ?? e.observed_at;39 return (40 <time dateTime={at} title={`observed ${e.observed_at}`} className="tnum text-xs text-ink-2">41 {fmtDate(at)}42 </time>43 );44}4546/** New entities of one type as a dense table. */47export function NewEntitiesSection({ id, title, items, total, empty, type }: { id: string; title: string; items: EntitySummary[]; total: number | null; empty: string; type: 'model' | 'paper' | 'other' }) {48 return (49 <Section id={id} eyebrow={title} title={<SectionTitle title={title} n={total ?? items.length} />}>50 {items.length === 0 ? (51 <EmptyState title={empty} />52 ) : (53 <>54 <DataTable caption={title} compact scroll>55 <thead>56 <tr>57 <Th>{type === 'paper' ? 'Paper' : type === 'model' ? 'Model' : 'Entity'}</Th>58 {type === 'other' && <Th>Type</Th>}59 <Th>Organization</Th>60 {type === 'model' && (61 <>62 <Th num>Params</Th>63 <Th num>Context</Th>64 <Th>Openness</Th>65 <Th>Released</Th>66 </>67 )}68 {type === 'paper' && <Th>Published</Th>}69 <Th>First seen</Th>70 </tr>71 </thead>72 <tbody>73 {items.map((e) => {74 const a = e.attributes ?? {};75 return (76 <tr key={e.id}>77 <Td primary>78 <EntityLink e={e} />79 </Td>80 {type === 'other' && (81 <Td label="Type">82 <EntityBadge type={e.entity_type} small />83 </Td>84 )}85 <Td label="Organization" className="text-ink-2">86 {e.organization ? <Link href={routes.entity({ entity_type: 'company', slug: e.organization.slug })} className="hover:text-accent">{e.organization.name}</Link> : <span className="text-ink-3">—</span>}87 </Td>88 {type === 'model' && (89 <>90 <Td num label="Params" className="tnum">{fmtParams(a.parameter_count)}</Td>91 <Td num label="Context" className="tnum">{num(a.context_length) === null ? '—' : fmtTokens(a.context_length)}</Td>92 <Td label="Openness">{typeof a.openness === 'string' ? <OpennessBadge openness={a.openness} /> : <span className="text-ink-3">—</span>}</Td>93 <Td label="Released" className="tnum text-ink-2">{typeof a.release_date === 'string' ? fmtDate(a.release_date) : '—'}</Td>94 </>95 )}96 {type === 'paper' && <Td label="Published" className="tnum text-ink-2">{typeof a.published_at === 'string' ? fmtDate(a.published_at) : '—'}</Td>}97 <Td label="First seen" className="tnum text-xs text-ink-3">{fmtDate(e.first_seen_at)}</Td>98 </tr>99 );100 })}101 </tbody>102 </DataTable>103 <Capped shown={items.length} total={total} what="entities" />104 </>105 )}106 </Section>107 );108}109110type PriceObj = { input_per_mtok?: unknown; output_per_mtok?: unknown };111function priceOf(v: unknown): PriceObj | null {112 return v && typeof v === 'object' ? (v as PriceObj) : null;113}114function DeltaCell({ from, to, fmt }: { from: unknown; to: unknown; fmt: (v: unknown) => string }) {115 const a = num(from);116 const b = num(to);117 const pct = fmtDeltaPct(a, b);118 return (119 <span className="tnum inline-flex flex-wrap items-baseline gap-1">120 <span className="text-ink-3 line-through decoration-ink-3/60">{a === null ? '—' : fmt(a)}</span>121 <span className="text-ink-3">→</span>122 <span className="font-medium text-ink">{b === null ? '—' : fmt(b)}</span>123 {pct && <span className={`text-[11px] ${b !== null && a !== null && b < a ? 'text-positive' : 'text-accent-2'}`}>{pct}</span>}124 </span>125 );126}127128/** PRICE_CHANGED / PROVIDER_LISTED rows: old/new are `{ input_per_mtok, output_per_mtok }` objects. */129export function PriceChangesSection({ id, title, items, total, empty }: { id: string; title: string; items: ChangeEvent[]; total: number | null; empty: string }) {130 return (131 <Section id={id} eyebrow={title} title={<SectionTitle title={title} n={total ?? items.length} />}>132 {items.length === 0 ? (133 <EmptyState title={empty} />134 ) : (135 <>136 <DataTable caption={title} compact scroll>137 <thead>138 <tr>139 <Th>Date</Th>140 <Th>Model</Th>141 <Th>Provider</Th>142 <Th num>Input / 1M</Th>143 <Th num>Output / 1M</Th>144 <Th>Source</Th>145 </tr>146 </thead>147 <tbody>148 {items.map((e) => {149 const o = priceOf(e.old_value);150 const n = priceOf(e.new_value);151 const provider = typeof e.meta?.provider === 'string' ? e.meta.provider : e.entity?.entity_type === 'provider' ? e.entity.name : null;152 return (153 <tr key={e.id}>154 <Td label="Date">155 <span className="inline-flex items-center gap-2">156 <ImportanceMark importance={e.importance} /> <When e={e} />157 </span>158 </Td>159 <Td primary>{e.entity ? <EntityLink e={e.entity} /> : <span className="text-ink-2">{e.summary}</span>}</Td>160 <Td label="Provider" className="text-ink-2">{provider ?? '—'}</Td>161 <Td num label="Input">{o || n ? <DeltaCell from={o?.input_per_mtok} to={n?.input_per_mtok} fmt={(v) => fmtUsdPerM(v)} /> : '—'}</Td>162 <Td num label="Output">{o || n ? <DeltaCell from={o?.output_per_mtok} to={n?.output_per_mtok} fmt={(v) => fmtUsdPerM(v)} /> : '—'}</Td>163 <Td label="Source">164 <Src url={e.source_url} />165 </Td>166 </tr>167 );168 })}169 </tbody>170 </DataTable>171 <Capped shown={items.length} total={total} what="price events" />172 </>173 )}174 </Section>175 );176}177178/** CONTEXT_CHANGED (and other numeric property events): old → new with %. */179export function ContextChangesSection({ id, title, items, total, empty }: { id: string; title: string; items: ChangeEvent[]; total: number | null; empty: string }) {180 return (181 <Section id={id} eyebrow={title} title={<SectionTitle title={title} n={total ?? items.length} />}>182 {items.length === 0 ? (183 <EmptyState title={empty} />184 ) : (185 <>186 <DataTable caption={title} compact scroll>187 <thead>188 <tr>189 <Th>Date</Th>190 <Th>Model</Th>191 <Th>Organization</Th>192 <Th num>Context window</Th>193 <Th>Source</Th>194 <Th>History</Th>195 </tr>196 </thead>197 <tbody>198 {items.map((e) => (199 <tr key={e.id}>200 <Td label="Date">201 <span className="inline-flex items-center gap-2">202 <ImportanceMark importance={e.importance} /> <When e={e} />203 </span>204 </Td>205 <Td primary>{e.entity ? <EntityLink e={e.entity} /> : <span className="text-ink-2">{e.summary}</span>}</Td>206 <Td label="Organization" className="text-ink-2">{e.entity?.organization?.name ?? '—'}</Td>207 <Td num label="Context">208 <DeltaCell from={e.old_value} to={e.new_value} fmt={(v) => `${fmtTokens(v)} tokens`} />209 </Td>210 <Td label="Source">211 <Src url={e.source_url} />212 </Td>213 <Td label="History">{e.entity ? <Link href={routes.entityHistory(e.entity, e.property ?? undefined)} className="link text-xs">claims →</Link> : '—'}</Td>214 </tr>215 ))}216 </tbody>217 </DataTable>218 <Capped shown={items.length} total={total} what="context changes" />219 </>220 )}221 </Section>222 );223}224225export function LeadersSection({ id, items, a, b }: { id: string; items: NonNullable<DiffPayload11['new_benchmark_leaders']>; a: string; b: string }) {226 return (227 <Section id={id} eyebrow="New benchmark leaders" title={<SectionTitle title="New benchmark leaders" n={items.length} />} lede={`Benchmarks whose primary-group leader (computed from results observed by each date) differs between ${fmtDate(a)} and ${fmtDate(b)}.`}>228 {items.length === 0 ? (229 <EmptyState title="No benchmark changed leader between these dates" />230 ) : (231 <DataTable caption="New benchmark leaders" compact scroll>232 <thead>233 <tr>234 <Th>Benchmark</Th>235 <Th>Leader at {fmtDate(a)}</Th>236 <Th num>Score</Th>237 <Th>Leader at {fmtDate(b)}</Th>238 <Th num>Score</Th>239 <Th>Metric</Th>240 </tr>241 </thead>242 <tbody>243 {items.map((l) => (244 <tr key={l.benchmark.id}>245 <Td primary>246 <Link href={routes.benchmark(l.benchmark.slug)} className="text-ink hover:text-accent hover:underline">247 {l.benchmark.name}248 </Link>249 {l.benchmark.category && <span className="block text-[11px] text-ink-3">{l.benchmark.category}</span>}250 </Td>251 <Td label={`At ${fmtDate(a)}`}>{l.at_a ? <EntityLink e={l.at_a.model} className="text-ink-2" /> : <span className="text-ink-3">no result observed yet</span>}</Td>252 <Td num label="Score" className="tnum text-ink-2">{l.at_a ? fmtScore(l.at_a.score) : '—'}</Td>253 <Td label={`At ${fmtDate(b)}`}>{l.at_b ? <EntityLink e={l.at_b.model} className="font-medium" /> : <span className="text-ink-3">—</span>}</Td>254 <Td num label="Score" className="tnum font-medium">{l.at_b ? fmtScore(l.at_b.score) : '—'}</Td>255 <Td label="Metric" className="text-xs text-ink-2">{l.at_b?.metric ?? l.at_a?.metric ?? '—'}{l.at_b?.trust_level ? <span className="block text-ink-3">{l.at_b.trust_level}</span> : null}</Td>256 </tr>257 ))}258 </tbody>259 </DataTable>260 )}261 </Section>262 );263}264265/** Generic event list section (property / provider / hardware changes). */266export function EventsSection({ id, title, items, total, empty, lede }: { id: string; title: string; items: ChangeEvent[]; total: number | null; empty: string; lede?: ReactNode }) {267 return (268 <Section id={id} eyebrow={title} title={<SectionTitle title={title} n={total ?? items.length} />} lede={lede}>269 {items.length === 0 ? (270 <EmptyState title={empty} />271 ) : (272 <>273 <ul className="border-t border-rule">274 {items.map((e) => (275 <ChangeRow key={e.id} e={e} showDate live={false} />276 ))}277 </ul>278 <Capped shown={items.length} total={total} what="events" />279 </>280 )}281 </Section>282 );283}284285export function EntitiesSection({ id, title, items, total, empty }: { id: string; title: string; items: EntitySummary[]; total: number | null; empty: string }) {286 return (287 <Section id={id} eyebrow={title} title={<SectionTitle title={title} n={total ?? items.length} />}>288 {items.length === 0 ? (289 <EmptyState title={empty} />290 ) : (291 <>292 <ul className="grid gap-x-8 border-t border-rule sm:grid-cols-2">293 {items.map((e) => (294 <li key={e.id} className="flex items-center gap-2 border-b border-rule py-2 text-sm">295 <EntityBadge type={e.entity_type} small />296 <EntityLink e={e} className="truncate" />297 {e.organization && <span className="ml-auto shrink-0 text-xs text-ink-3">{e.organization.name}</span>}298 </li>299 ))}300 </ul>301 <Capped shown={items.length} total={total} what="entities" />302 </>303 )}304 </Section>305 );306}307