Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion src/app/benchmarks/[slug]/share-card/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -798,7 +798,14 @@ async function renderLeaderboard(
colors: Map<string, string>,
chainLabel?: string | null
) {
const sorted = sortByP50(benchmark);
const allSorted = sortByP50(benchmark);
// Hard row cap so the last row can't collide with the CardFooter
// border in the 630 px canvas (observed on wormhole-vaa-latency with
// 14 chains: Moonbeam overprinted the "OPENCHAINBENCH.COM · No 101"
// divider). If truncated, an "and N more" line is appended below.
const MAX_ROWS = 10;
const sorted = allSorted.slice(0, MAX_ROWS);
const truncatedCount = Math.max(0, allSorted.length - sorted.length);
const maxP50 = Math.max(...sorted.map((r) => r.ms.p50)) || 1;
const subtitleLB = `Ranked by p50 · ${benchmark.metric}.`;
// Scale down type + spacing when the roster is dense OR the title is
Expand Down Expand Up @@ -958,6 +965,21 @@ async function renderLeaderboard(
</div>
);
})}
{truncatedCount > 0 && (
<div
style={{
display: "flex",
paddingLeft: 52,
paddingTop: 2,
fontSize: 14,
color: INK_MUTED,
letterSpacing: "0.06em",
fontStyle: "italic",
}}
>
and {truncatedCount} more on openchainbench.com
</div>
)}
</div>
</div>
</CardShell>
Expand Down
31 changes: 18 additions & 13 deletions src/components/benchmark-body.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -603,25 +603,18 @@ export function BenchmarkBody({
!hlArchiveCache[longRangeKey]
);

if (!benchmark || !viewBenchmark) return null;

const pendingCls = variantPending
? " opacity-40 animate-pulse pointer-events-none"
: "";
const pendingLabel = [effectiveChain, effectiveRegion, effectiveKind]
.filter((v): v is string => !!v && v !== "all")
.join(" · ");
const isDraft = viewBenchmark.status === "draft";
const { fieldMin, fieldMedian, fieldMax, tailMin, tailMax, tailSpread } =
computeFieldStats(viewBenchmark.results);
const activePanel =
benchmark.metricPanels?.find((p) => p.id === activePanelId) ?? null;
// Value views (ranked bars) swap each provider's headline p50 for the
// active panel's scalar so the size tabs work on the default chart,
// not only on the timeseries view. Providers the panel has no value
// for (book could not fill the tier) drop out of the ranking, which
// is the skipped-not-extrapolated rule made visible.
//
// Kept ABOVE the `if (!benchmark || !viewBenchmark) return null` early
// return so this useMemo is called on every render (rules-of-hooks).
const activePanel =
benchmark?.metricPanels?.find((p) => p.id === activePanelId) ?? null;
const panelViewBenchmark = useMemo(() => {
if (!viewBenchmark) return null;
if (!activePanel) return viewBenchmark;
const vals = activePanel.values ?? {};
return {
Expand All @@ -634,6 +627,18 @@ export function BenchmarkBody({
};
}, [viewBenchmark, activePanel]);

if (!benchmark || !viewBenchmark || !panelViewBenchmark) return null;

const pendingCls = variantPending
? " opacity-40 animate-pulse pointer-events-none"
: "";
const pendingLabel = [effectiveChain, effectiveRegion, effectiveKind]
.filter((v): v is string => !!v && v !== "all")
.join(" · ");
const isDraft = viewBenchmark.status === "draft";
const { fieldMin, fieldMedian, fieldMax, tailMin, tailMax, tailSpread } =
computeFieldStats(viewBenchmark.results);

const sharedHeaderActions = (
<>
<CsvButton benchmark={viewBenchmark ?? benchmark} range={chartRange} />
Expand Down
9 changes: 7 additions & 2 deletions src/components/chart-export-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,15 @@ export function ChartExportButton({
cacheBust: true,
skipFonts: false,
// Drop the export button itself from the capture — no point
// baking the "Copy" pill into every screenshot.
// baking the "Copy" pill into every screenshot. Also drop any
// element marked with data-chart-export-omit (e.g. the chart's
// header metadata row and the Top-N selector) so the exported
// PNG focuses on the plot itself, not the interactive UI chrome.
filter: (node) => {
if (!(node instanceof HTMLElement)) return true;
return !node.dataset.chartExportButton;
if (node.dataset.chartExportButton) return false;
if (node.dataset.chartExportOmit) return false;
return true;
},
style: { boxShadow: "none" },
});
Expand Down
25 changes: 17 additions & 8 deletions src/components/live/live-number.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,21 +55,30 @@ export function LiveNumber({
const prevRef = useRef<{ value: number; ts: number } | null>(null);
const rafRef = useRef<number | null>(null);

// Render-time reset when the incoming value exceeds the ceiling AND
// the displayed value is already poisoned. React allows setState during
// render on the same component, and moving this out of the effect
// avoids the react-hooks/set-state-in-effect rule while keeping the
// same "flush poison before a healthy push seeds fresh" behavior.
if (
value != null &&
maxValue != null &&
value > maxValue &&
display != null &&
display > maxValue
) {
setDisplay(undefined);
}

useEffect(() => {
if (value == null || !Number.isFinite(value)) return;
if (maxValue != null && value > maxValue) {
// Flush a poisoned lastRef AND the displayed value so a healthy
// push can seed the ticker fresh on the next tick. Without
// resetting `display`, the monotonic guard below would keep
// rejecting the healthy value as smaller than the garbage still
// shown to the user.
// Flush a poisoned lastRef so a healthy push can seed the ticker
// fresh on the next tick.
if (lastRef.current && lastRef.current.value > maxValue) {
lastRef.current = null;
prevRef.current = null;
}
setDisplay((d) =>
d != null && maxValue != null && d > maxValue ? undefined : d,
);
return;
}
const now = performance.now();
Expand Down
10 changes: 8 additions & 2 deletions src/components/ranked-bar-chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,10 @@ export function RankedBarChart({

return (
<figure className="relative my-2" ref={figureRef}>
<div className="mb-3 flex flex-wrap items-center justify-between gap-3 min-h-7">
<div
data-chart-export-omit="true"
className="mb-3 flex flex-wrap items-center justify-between gap-3 min-h-7"
>
<p className="inline-flex items-center gap-2 text-[10px] font-medium uppercase tracking-[0.18em] text-ink-muted">
<LiveDot />
<span>{benchmark.metric} · last 24 hours</span>
Expand All @@ -152,7 +155,10 @@ export function RankedBarChart({
{headerActions}
</div>
</div>
<div className="mb-4 flex flex-wrap items-center justify-end gap-1">
<div
data-chart-export-omit="true"
className="mb-4 flex flex-wrap items-center justify-end gap-1"
>
<TopNSelector value={topN} options={topNOptions} onChange={setTopN} />
</div>
<ul className="space-y-2">
Expand Down
6 changes: 6 additions & 0 deletions src/lib/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ export function fmtUnit(value: number, unit: string) {
if (abs < 1000) return `$${value.toLocaleString("en-US", { maximumFractionDigits: 2 })}`;
return `$${formatCompactCount(value)}`;
}
// Auto-flip to minutes at 60 s and to seconds at 1 s, mirroring the
// suffix returned by unitSuffix(). Without the min flip, wormhole-
// vaa-latency ranking cards rendered "858.79 min" for what was really
// 14.3 min (858.79 s), because fmtValue stripped the trailing " s" that
// fmtUnit produced but unitSuffix independently returned " min".
if (value >= 60000) return `${(value / 60000).toFixed(1)} min`;
if (value >= 1000) return `${(value / 1000).toFixed(2)} s`;
// Sub-millisecond values keep one decimal: pm-data-freshness's 0.5 ms
// anchor rendered "1 ms", contradicting the 0.5 published by the
Expand Down
Loading