import { ExternalLink } from 'lucide-react'; import Link from 'next/link'; import { EntityLink } from '@/components/ui/entity'; import { DataTable, EmptyRow, Td, Th } from '@/components/ui/data-table'; import { Note } from '@/components/ui/section'; import { cn } from '@/lib/cn'; import { fmtDateTime, fmtAgo, fmtUsdPerM, num } from '@/lib/format'; import type { ChangeEvent } from '@/lib/types'; type PriceValue = { input_per_mtok?: unknown; output_per_mtok?: unknown } | null | undefined; function pv(v: unknown): { input: number | null; output: number | null } { const o = (v && typeof v === 'object' ? v : {}) as PriceValue; return { input: num(o?.input_per_mtok), output: num(o?.output_per_mtok) }; } function host(url: string): string { try { return new URL(url).hostname.replace(/^www\./, ''); } catch { return 'source'; } } /** Relative change in %, null when either side is missing or the base is 0. */ export function pctChange(from: number | null, to: number | null): number | null { if (from === null || to === null || from === 0) return null; return ((to - from) / Math.abs(from)) * 100; } /** Old → new price cell: strike-through old, bold new, direction sign. Amber = money. */ function Move({ from, to }: { from: number | null; to: number | null }) { if (from === null && to === null) return —; const dir = from !== null && to !== null ? (to < from ? '↓' : to > from ? '↑' : '=') : ''; return ( {fmtUsdPerM(from)} → {fmtUsdPerM(to)} {dir && dir !== '=' && {dir}} ); } /** ±% chip; green when cheaper, red when dearer. */ export function PctChip({ pct, className }: { pct: number | null; className?: string }) { if (pct === null) return —; const sign = pct > 0 ? '+' : pct < 0 ? '−' : ''; return 0 ? 'text-danger' : 'text-ink-2', className)}>{`${sign}${Math.abs(pct).toFixed(Math.abs(pct) < 10 ? 1 : 0)}%`}; } /** * PRICE_CHANGED events as a dense table: model · provider · input move · output move · % change · when · source. * `providerHref(slug)` optionally turns the provider cell into a filter link (the price terminal). */ export function PriceMovers({ movers, providerHref, limit }: { movers: (ChangeEvent & { percent_change?: unknown })[]; providerHref?: (providerName: string) => string | undefined; limit?: number }) { const rows = movers.filter((e) => e.event_type === 'PRICE_CHANGED' || e.category === 'price').slice(0, limit ?? movers.length); return ( <>