|
1 | | -import { cn } from "@/lib/utils"; |
2 | | -import type { MainResultsRow } from "@/data/mainResultsTable"; |
| 1 | +import type { MainResultsRow } from "@/data/mainResultsTable"; |
| 2 | +import { cn } from "@/lib/utils"; |
3 | 3 |
|
4 | 4 | type MainResultsTableProps = { |
5 | 5 | rows: MainResultsRow[]; |
6 | 6 | }; |
7 | 7 |
|
8 | | -export function MainResultsTable({ rows }: MainResultsTableProps) { |
| 8 | +type ScoreTriplet = { ex: number; in: number; ae: number }; |
| 9 | + |
| 10 | +function collectScores(rows: MainResultsRow[]) { |
| 11 | + const values: number[] = []; |
| 12 | + for (const row of rows) { |
| 13 | + const groups: ScoreTriplet[] = [row.generation, row.editing, row.repair, row.average]; |
| 14 | + for (const g of groups) values.push(g.ex, g.in, g.ae); |
| 15 | + } |
| 16 | + return values; |
| 17 | +} |
| 18 | + |
| 19 | +function scoreStyle(value: number, min: number, max: number) { |
| 20 | + const ratio = max === min ? 0 : (value - min) / (max - min); |
| 21 | + const alpha = 0.08 + ratio * 0.26; |
| 22 | + const text = ratio > 0.72 ? "rgb(22 101 52)" : ratio > 0.5 ? "rgb(21 94 117)" : "rgb(51 65 85)"; |
| 23 | + return { |
| 24 | + backgroundColor: `rgba(16, 185, 129, ${alpha.toFixed(3)})`, |
| 25 | + color: text |
| 26 | + }; |
| 27 | +} |
| 28 | + |
| 29 | +function SectionHeader({ label, tone }: { label: string; tone: string }) { |
9 | 30 | return ( |
10 | | - <div className="overflow-x-auto rounded-xl border border-border/70"> |
11 | | - <table className="min-w-[1300px] w-full text-sm"> |
12 | | - <thead> |
13 | | - <tr className="bg-muted/60"> |
14 | | - <th rowSpan={2} className="sticky left-0 bg-muted/60 px-4 py-3 text-left font-semibold"> |
15 | | - Model |
16 | | - </th> |
17 | | - <th colSpan={3} className="px-4 py-3 text-center font-semibold"> |
18 | | - Generation |
19 | | - </th> |
20 | | - <th colSpan={3} className="px-4 py-3 text-center font-semibold"> |
21 | | - Editing |
22 | | - </th> |
23 | | - <th colSpan={3} className="px-4 py-3 text-center font-semibold"> |
24 | | - Repair |
25 | | - </th> |
26 | | - <th colSpan={3} className="px-4 py-3 text-center font-semibold"> |
27 | | - Average |
28 | | - </th> |
29 | | - </tr> |
30 | | - <tr className="bg-muted/40"> |
31 | | - <th className="px-4 py-2 text-center font-medium">EX</th> |
32 | | - <th className="px-4 py-2 text-center font-medium">IN</th> |
33 | | - <th className="px-4 py-2 text-center font-medium">AE</th> |
34 | | - <th className="px-4 py-2 text-center font-medium">EX</th> |
35 | | - <th className="px-4 py-2 text-center font-medium">IN</th> |
36 | | - <th className="px-4 py-2 text-center font-medium">AE</th> |
37 | | - <th className="px-4 py-2 text-center font-medium">EX</th> |
38 | | - <th className="px-4 py-2 text-center font-medium">IN</th> |
39 | | - <th className="px-4 py-2 text-center font-medium">AE</th> |
40 | | - <th className="px-4 py-2 text-center font-medium">EX</th> |
41 | | - <th className="px-4 py-2 text-center font-medium">IN</th> |
42 | | - <th className="px-4 py-2 text-center font-medium">AE</th> |
43 | | - </tr> |
44 | | - </thead> |
45 | | - <tbody> |
46 | | - <tr> |
47 | | - <td colSpan={13} className="bg-sky-50/70 px-4 py-2 text-xs font-semibold uppercase tracking-wide text-slate-700"> |
48 | | - Closed-Source Large Language Models |
49 | | - </td> |
50 | | - </tr> |
51 | | - {rows |
52 | | - .filter((row) => row.group === "Closed-Source") |
53 | | - .map((row) => ( |
54 | | - <ResultRow key={row.model} row={row} /> |
55 | | - ))} |
56 | | - <tr> |
57 | | - <td colSpan={13} className="bg-emerald-50/70 px-4 py-2 text-xs font-semibold uppercase tracking-wide text-slate-700"> |
58 | | - Open-Source Large Language Models |
59 | | - </td> |
60 | | - </tr> |
61 | | - {rows |
62 | | - .filter((row) => row.group === "Open-Source") |
63 | | - .map((row) => ( |
64 | | - <ResultRow key={row.model} row={row} /> |
65 | | - ))} |
66 | | - </tbody> |
67 | | - </table> |
68 | | - </div> |
| 31 | + <tr> |
| 32 | + <td colSpan={13} className={cn("px-3 py-2 text-xs font-semibold uppercase tracking-wide", tone)}> |
| 33 | + {label} |
| 34 | + </td> |
| 35 | + </tr> |
| 36 | + ); |
| 37 | +} |
| 38 | + |
| 39 | +function MetricCell({ value, min, max }: { value: number; min: number; max: number }) { |
| 40 | + return ( |
| 41 | + <td className="px-1.5 py-2 text-center text-[11px] font-medium md:text-xs" style={scoreStyle(value, min, max)}> |
| 42 | + {value.toFixed(2)} |
| 43 | + </td> |
69 | 44 | ); |
70 | 45 | } |
71 | 46 |
|
72 | | -function ResultRow({ row }: { row: MainResultsRow }) { |
| 47 | +function DesktopRow({ row, min, max }: { row: MainResultsRow; min: number; max: number }) { |
73 | 48 | return ( |
74 | 49 | <tr className="border-t border-border/60"> |
75 | | - <td className={cn("sticky left-0 bg-background px-4 py-3 font-medium")}>{row.model}</td> |
76 | | - <Cell value={row.generation.ex} /> |
77 | | - <Cell value={row.generation.in} /> |
78 | | - <Cell value={row.generation.ae} /> |
79 | | - <Cell value={row.editing.ex} /> |
80 | | - <Cell value={row.editing.in} /> |
81 | | - <Cell value={row.editing.ae} /> |
82 | | - <Cell value={row.repair.ex} /> |
83 | | - <Cell value={row.repair.in} /> |
84 | | - <Cell value={row.repair.ae} /> |
85 | | - <Cell value={row.average.ex} /> |
86 | | - <Cell value={row.average.in} /> |
87 | | - <Cell value={row.average.ae} /> |
| 50 | + <td className="px-3 py-2 text-left text-[11px] font-semibold text-slate-700 md:text-xs lg:text-sm"> |
| 51 | + <span className="line-clamp-2">{row.model}</span> |
| 52 | + </td> |
| 53 | + <MetricCell value={row.generation.ex} min={min} max={max} /> |
| 54 | + <MetricCell value={row.generation.in} min={min} max={max} /> |
| 55 | + <MetricCell value={row.generation.ae} min={min} max={max} /> |
| 56 | + <MetricCell value={row.editing.ex} min={min} max={max} /> |
| 57 | + <MetricCell value={row.editing.in} min={min} max={max} /> |
| 58 | + <MetricCell value={row.editing.ae} min={min} max={max} /> |
| 59 | + <MetricCell value={row.repair.ex} min={min} max={max} /> |
| 60 | + <MetricCell value={row.repair.in} min={min} max={max} /> |
| 61 | + <MetricCell value={row.repair.ae} min={min} max={max} /> |
| 62 | + <MetricCell value={row.average.ex} min={min} max={max} /> |
| 63 | + <MetricCell value={row.average.in} min={min} max={max} /> |
| 64 | + <MetricCell value={row.average.ae} min={min} max={max} /> |
88 | 65 | </tr> |
89 | 66 | ); |
90 | 67 | } |
91 | 68 |
|
92 | | -function Cell({ value }: { value: number }) { |
93 | | - return <td className="px-4 py-3 text-center text-muted-foreground">{value.toFixed(2)}</td>; |
| 69 | +function MobileMetricBlock({ title, values, min, max }: { title: string; values: ScoreTriplet; min: number; max: number }) { |
| 70 | + return ( |
| 71 | + <div className="rounded-lg border border-border/60 bg-white/90 p-2"> |
| 72 | + <div className="mb-1 text-[11px] font-semibold text-slate-600">{title}</div> |
| 73 | + <div className="grid grid-cols-3 gap-1 text-[11px]"> |
| 74 | + <div className="text-center text-muted-foreground">EX</div> |
| 75 | + <div className="text-center text-muted-foreground">IN</div> |
| 76 | + <div className="text-center text-muted-foreground">AE</div> |
| 77 | + <div className="rounded px-1 py-1 text-center" style={scoreStyle(values.ex, min, max)}>{values.ex.toFixed(2)}</div> |
| 78 | + <div className="rounded px-1 py-1 text-center" style={scoreStyle(values.in, min, max)}>{values.in.toFixed(2)}</div> |
| 79 | + <div className="rounded px-1 py-1 text-center" style={scoreStyle(values.ae, min, max)}>{values.ae.toFixed(2)}</div> |
| 80 | + </div> |
| 81 | + </div> |
| 82 | + ); |
| 83 | +} |
| 84 | + |
| 85 | +function MobileRow({ row, min, max }: { row: MainResultsRow; min: number; max: number }) { |
| 86 | + return ( |
| 87 | + <article className="rounded-xl border border-border/70 bg-white/95 p-3"> |
| 88 | + <h4 className="text-sm font-semibold text-slate-800">{row.model}</h4> |
| 89 | + <div className="mt-3 grid grid-cols-2 gap-2"> |
| 90 | + <MobileMetricBlock title="Generation" values={row.generation} min={min} max={max} /> |
| 91 | + <MobileMetricBlock title="Editing" values={row.editing} min={min} max={max} /> |
| 92 | + <MobileMetricBlock title="Repair" values={row.repair} min={min} max={max} /> |
| 93 | + <MobileMetricBlock title="Average" values={row.average} min={min} max={max} /> |
| 94 | + </div> |
| 95 | + </article> |
| 96 | + ); |
| 97 | +} |
| 98 | + |
| 99 | +export function MainResultsTable({ rows }: MainResultsTableProps) { |
| 100 | + const values = collectScores(rows); |
| 101 | + const min = Math.min(...values); |
| 102 | + const max = Math.max(...values); |
| 103 | + |
| 104 | + const closed = rows.filter((row) => row.group === "Closed-Source"); |
| 105 | + const open = rows.filter((row) => row.group === "Open-Source"); |
| 106 | + |
| 107 | + return ( |
| 108 | + <div className="space-y-4"> |
| 109 | + <div className="hidden rounded-2xl border border-border/70 bg-white/95 p-3 shadow-sm lg:block"> |
| 110 | + <table className="w-full table-fixed border-separate border-spacing-0 text-xs"> |
| 111 | + <colgroup> |
| 112 | + <col style={{ width: "18%" }} /> |
| 113 | + {Array.from({ length: 12 }).map((_, i) => ( |
| 114 | + <col key={i} style={{ width: "6.833%" }} /> |
| 115 | + ))} |
| 116 | + </colgroup> |
| 117 | + <thead> |
| 118 | + <tr className="bg-slate-100/90 text-slate-700"> |
| 119 | + <th rowSpan={2} className="rounded-l-lg px-3 py-2 text-left text-xs font-semibold">Model</th> |
| 120 | + <th colSpan={3} className="px-2 py-2 text-center text-xs font-semibold">Generation</th> |
| 121 | + <th colSpan={3} className="px-2 py-2 text-center text-xs font-semibold">Editing</th> |
| 122 | + <th colSpan={3} className="px-2 py-2 text-center text-xs font-semibold">Repair</th> |
| 123 | + <th colSpan={3} className="rounded-r-lg px-2 py-2 text-center text-xs font-semibold">Average</th> |
| 124 | + </tr> |
| 125 | + <tr className="bg-slate-50/90 text-slate-600"> |
| 126 | + {Array.from({ length: 4 }).flatMap((_, i) => ( |
| 127 | + ["EX", "IN", "AE"].map((m) => ( |
| 128 | + <th key={`${i}-${m}`} className="px-1 py-1 text-center text-[11px] font-medium">{m}</th> |
| 129 | + )) |
| 130 | + ))} |
| 131 | + </tr> |
| 132 | + </thead> |
| 133 | + <tbody> |
| 134 | + <SectionHeader label="Closed-Source Large Language Models" tone="bg-sky-50 text-sky-900" /> |
| 135 | + {closed.map((row) => ( |
| 136 | + <DesktopRow key={row.model} row={row} min={min} max={max} /> |
| 137 | + ))} |
| 138 | + <SectionHeader label="Open-Source Large Language Models" tone="bg-emerald-50 text-emerald-900" /> |
| 139 | + {open.map((row) => ( |
| 140 | + <DesktopRow key={row.model} row={row} min={min} max={max} /> |
| 141 | + ))} |
| 142 | + </tbody> |
| 143 | + </table> |
| 144 | + </div> |
| 145 | + |
| 146 | + <div className="space-y-3 lg:hidden"> |
| 147 | + <div className="text-xs font-semibold uppercase tracking-wide text-sky-800">Closed-Source Large Language Models</div> |
| 148 | + {closed.map((row) => ( |
| 149 | + <MobileRow key={row.model} row={row} min={min} max={max} /> |
| 150 | + ))} |
| 151 | + |
| 152 | + <div className="pt-2 text-xs font-semibold uppercase tracking-wide text-emerald-800">Open-Source Large Language Models</div> |
| 153 | + {open.map((row) => ( |
| 154 | + <MobileRow key={row.model} row={row} min={min} max={max} /> |
| 155 | + ))} |
| 156 | + </div> |
| 157 | + </div> |
| 158 | + ); |
94 | 159 | } |
0 commit comments