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
43 changes: 0 additions & 43 deletions answers/which-prediction-market-data-api-is-the-freshest.yml

This file was deleted.

1 change: 0 additions & 1 deletion src/app/prediction-markets/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import { SITE } from "@/data/site";
const ANSWERS = [
{ slug: "how-long-does-polymarket-take-to-resolve", question: "How long does Polymarket take to resolve a market?" },
{ slug: "polymarket-vs-kalshi-resolution-speed", question: "Polymarket vs Kalshi, which resolves prediction markets faster?" },
{ slug: "which-prediction-market-data-api-is-the-freshest", question: "Which prediction market data API publishes the freshest Polymarket data?" },
{ slug: "which-prediction-market-has-the-strictest-rate-limits", question: "Which prediction market API has the strictest rate limits?" },
{ slug: "polymarket-fees-explained", question: "What fees does Polymarket charge?" },
{ slug: "polymarket-vs-kalshi-fees", question: "Polymarket vs Kalshi fees, which is cheaper to trade?" },
Expand Down
2 changes: 1 addition & 1 deletion src/components/time-series-chart/chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
const W = 1000;
const H = 360;
const padL = 60;
const padR = 96;
const padR = 130;
const padT = 16;
const padB = 36;
const innerW = W - padL - padR;
Expand Down Expand Up @@ -293,7 +293,7 @@
// line draws a break but the dot keeps rendering at 0 / NaN.
return { ...l, color, pts, linePath, fillPath, lastX, lastY, last, isGap };
});
}, [slicedLines, padL, padT, innerW, innerH, lo, yRange, expectedPoints]);

Check warning on line 296 in src/components/time-series-chart/chart.tsx

View workflow job for this annotation

GitHub Actions / check

React Hook useMemo has a missing dependency: 'zoom'. Either include it or remove the dependency array

// Hover line position: mirror the right-anchored placement used by drawn().
// hover.idx is in [0, numPoints-1]; we map it to the visible chart via the
Expand Down
4 changes: 2 additions & 2 deletions src/components/time-series-chart/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@
return higherIsBetter ? bv - av : av - bv;
});
return built;
}, [benchmark, range, region, colors, excluded, seriesOverride, seriesOverride7d, seriesOverride30d, higherIsBetterOverride, lazySeries7d, lazySeries30d, isLongRange, longRangeSeries, panelLazy90d, panelLazy1y]);

Check warning on line 436 in src/components/time-series-chart/index.tsx

View workflow job for this annotation

GitHub Actions / check

React Hook useMemo has missing dependencies: 'panelLazy30d' and 'panelLazy7d'. Either include them or remove the dependency array

// Top-N selector — sized off the post-filter line count via the
// shared `useTopN` hook so the option set agrees across every
Expand Down Expand Up @@ -478,7 +478,7 @@

return (
<figure className="my-2" ref={figureRef}>
<div className="mb-3 flex items-center justify-between gap-3 min-h-7">
<div className="mb-3 flex items-center justify-between gap-3 min-h-7" data-chart-export-omit="true">
<p className="inline-flex items-center gap-2 text-[10px] font-medium uppercase tracking-[0.18em] text-ink-muted">
<LiveDot />
<span>
Expand Down Expand Up @@ -508,7 +508,7 @@
{headerActions}
</div>
</div>
<div className="mb-4 flex flex-wrap items-center justify-between gap-3">
<div className="mb-4 flex flex-wrap items-center justify-between gap-3" data-chart-export-omit="true">
<div className="flex flex-wrap items-center gap-1">
{RANGES.map((r) => {
const active = r === range;
Expand Down
150 changes: 87 additions & 63 deletions src/components/time-series-chart/series.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,75 +21,99 @@ type SeriesPathsProps = {
// now speak for themselves — SeriesPaths renders a natural break wherever
// the underlying values are null.

// Max number of end-of-line labels rendered. The legend below shows all
// providers regardless — this cap prevents right-side label clutter when
// dozens of lines share the same Y range.
const MAX_INLINE_LABELS = 10;
// Minimum vertical gap (SVG units) between consecutive placed labels so
// name + value lines don't overlap.
const LABEL_GAP = 27;

export function SeriesPaths({ drawn, unit }: SeriesPathsProps) {
// Limit labels to top-N non-excluded lines (drawn is pre-sorted by value).
const labeledSlugs = new Set(
drawn.filter((d) => !d.excluded).slice(0, MAX_INLINE_LABELS).map((d) => d.slug),
);

// Collision deflection: sort labeled lines by their natural Y position,
// then push each label down until it clears the previous one.
const labelY: Record<string, number> = {};
const toPlace = drawn
.filter((d) => labeledSlugs.has(d.slug))
.map((d) => ({ slug: d.slug, natural: d.lastY }))
.sort((a, b) => a.natural - b.natural);
let prevBottom = -Infinity;
for (const item of toPlace) {
const y = Math.max(item.natural, prevBottom);
labelY[item.slug] = y;
prevBottom = y + LABEL_GAP;
}

return (
<>
{drawn.map((d) => (
<g
key={d.slug}
className="ts-line"
style={{
opacity: d.excluded ? 0 : 1,
pointerEvents: d.excluded ? "none" : undefined,
transition: "opacity 0.3s ease-out",
}}
>
<path d={d.fillPath} fill={`url(#fill-${d.slug})`} />
<path
d={d.linePath}
fill="none"
stroke={d.color}
strokeWidth={1.4}
strokeLinecap="round"
strokeLinejoin="round"
pathLength={1}
strokeDasharray="1"
{drawn.map((d) => {
const showLabel = labeledSlugs.has(d.slug);
const ly = labelY[d.slug] ?? d.lastY;
return (
<g
key={d.slug}
className="ts-line"
style={{
// Trigger draw-in via CSS animation
animation: "ts-draw 0.7s ease-out forwards",
opacity: d.excluded ? 0 : 1,
pointerEvents: d.excluded ? "none" : undefined,
transition: "opacity 0.3s ease-out",
}}
/>
{/* Live pulse halo. animated outward */}
<circle cx={d.lastX} cy={d.lastY} r={3} fill={d.color} opacity={0.4}>
<animate
attributeName="r"
values="3;10;3"
dur="1.8s"
repeatCount="indefinite"
/>
<animate
attributeName="opacity"
values="0.45;0;0.45"
dur="1.8s"
repeatCount="indefinite"
/>
</circle>
{/* Trailing tail dot */}
<circle cx={d.lastX} cy={d.lastY} r={2.8} fill={d.color} />
{/* End-of-line label */}
<text
x={d.lastX + 8}
y={d.lastY}
dominantBaseline="middle"
fontFamily="var(--font-sans)"
fontSize="11"
fontWeight="500"
fill={d.color}
>
{d.name}
</text>
<text
x={d.lastX + 8}
y={d.lastY + 12}
dominantBaseline="middle"
fontFamily="var(--font-mono)"
fontSize="10"
fill="var(--color-ink-muted)"
>
{fmtUnit(d.last, unit)}
</text>
</g>
))}
<path d={d.fillPath} fill={`url(#fill-${d.slug})`} />
<path
d={d.linePath}
fill="none"
stroke={d.color}
strokeWidth={1.4}
strokeLinecap="round"
strokeLinejoin="round"
pathLength={1}
strokeDasharray="1"
style={{
animation: "ts-draw 0.7s ease-out forwards",
}}
/>
{/* Live pulse halo */}
<circle cx={d.lastX} cy={d.lastY} r={3} fill={d.color} opacity={0.4}>
<animate attributeName="r" values="3;10;3" dur="1.8s" repeatCount="indefinite" />
<animate attributeName="opacity" values="0.45;0;0.45" dur="1.8s" repeatCount="indefinite" />
</circle>
{/* Trailing tail dot */}
<circle cx={d.lastX} cy={d.lastY} r={2.8} fill={d.color} />
{/* End-of-line label — only for top-N, collision-deflected */}
{showLabel && (
<>
<text
x={d.lastX + 8}
y={ly}
dominantBaseline="middle"
fontFamily="var(--font-sans)"
fontSize="11"
fontWeight="500"
fill={d.color}
>
{d.name}
</text>
<text
x={d.lastX + 8}
y={ly + 13}
dominantBaseline="middle"
fontFamily="var(--font-mono)"
fontSize="10"
fill="var(--color-ink-muted)"
>
{fmtUnit(d.last, unit)}
</text>
</>
)}
</g>
);
})}
</>
);
}
Expand Down
8 changes: 8 additions & 0 deletions src/lib/removed-benches.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ export const REMOVED_ANSWER_SLUGS = new Set([
// References solana-dex-quote-latency (staging-only, in
// REMOVED_BENCH_SLUGS). Same pattern.
"which-solana-dex-aggregator-is-the-fastest",
// References pm-data-freshness (bench 113, retired 2026-07). Bench
// removed; answer returns 404 without this guard.
"which-prediction-market-data-api-is-the-freshest",
]);

export const REMOVED_BENCH_SLUGS = new Set([
Expand Down Expand Up @@ -113,6 +116,11 @@ export const REMOVED_BENCH_SLUGS = new Set([
"rpc-keyed-latency",
"explorer-chain-coverage",
"portfolio-chain-coverage",
// pm-data-freshness (bench 113) retired 2026-07: Predexon (the only
// measured data relay) discontinued. Spec + harness removed; kept in
// the 410 list so any indexed URL returns Gone instead of 404.
// Per-venue WS freshness now lives in pm-ws-latency (bench 114).
"pm-data-freshness",
]);

/**
Expand Down
Loading