Skip to content

Commit d5e00c0

Browse files
feat(registry): week in merges leaderboard block (#4264)
* feat(registry): add a week-in-merges leaderboard block Authors race across the row at a speed set by their merge count, bouncing at both edges. * chore(registry): cover week-in-merges in the catalog search index * style(registry): format the week-in-merges block Wrap the lines oxfmt rejects so the format check passes. * fix(registry): regenerate the week-in-merges catalog payload The format pass left the committed payload off the generator output. * feat(registry): cap week-in-merges at twelve rows The block races at most rows_max authors, highest merge count first, and writes one line for everyone left out. The default is 12, so a long list cannot shrink the rows. * fix(registry): make week-in-merges speed follow merge count A row now travels the leader's pace times its merges over the leader's count. Only a zero stays on the floor, so a near-tie no longer looks like last place.
1 parent a80d733 commit d5e00c0

11 files changed

Lines changed: 1319 additions & 5 deletions

File tree

‎docs/catalog/blocks/week-in-merges.mdx‎

Lines changed: 582 additions & 0 deletions
Large diffs are not rendered by default.

‎docs/docs.json‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -510,12 +510,12 @@
510510
},
511511
{
512512
"group": "Data & charts",
513-
"tag": "17",
513+
"tag": "18",
514514
"expanded": false,
515515
"pages": [
516516
{
517517
"group": "Data",
518-
"tag": "17",
518+
"tag": "18",
519519
"expanded": false,
520520
"pages": [
521521
"catalog/components/animated-bar-chart",
@@ -534,6 +534,7 @@
534534
"catalog/blocks/us-map-bubble",
535535
"catalog/blocks/us-map-flow",
536536
"catalog/blocks/us-map-hex",
537+
"catalog/blocks/week-in-merges",
537538
"catalog/blocks/world-map"
538539
]
539540
}

‎docs/public/catalog-index.json‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5699,6 +5699,20 @@
56995699
"href": "/catalog/blocks/vpn-youtube-spot",
57005700
"preview": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/vpn-youtube-spot.png"
57015701
},
5702+
{
5703+
"name": "week-in-merges",
5704+
"type": "block",
5705+
"title": "Week in Merges",
5706+
"description": "Each author's avatar bounces across their row at a speed set by how many pull requests they merged",
5707+
"tags": [
5708+
"data",
5709+
"chart",
5710+
"ranking",
5711+
"leaderboard"
5712+
],
5713+
"href": "/catalog/blocks/week-in-merges",
5714+
"preview": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/week-in-merges.png"
5715+
},
57025716
{
57035717
"name": "weight-wave",
57045718
"type": "block",

‎docs/public/catalog/blocks/week-in-merges.json‎

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

‎docs/snippets/catalog-gallery-data.mdx‎

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/**
2+
* The block says speed is proportional to merges. Bind a near-tie and a
3+
* one-merge row, seek one second (before anyone can bounce), and check the
4+
* distances.
5+
*/
6+
import { readFileSync } from "node:fs";
7+
import { join } from "node:path";
8+
import { runInNewContext } from "node:vm";
9+
import { parseHTML } from "linkedom";
10+
import { describe, expect, it } from "vitest";
11+
12+
const htmlPath = join(
13+
import.meta.dirname,
14+
"../../../../registry/blocks/week-in-merges/week-in-merges.html",
15+
);
16+
17+
function distances(
18+
rows: Array<{ name: string; merges: number }>,
19+
extra: Record<string, unknown> = {},
20+
): Map<number, number> {
21+
const { document, window } = parseHTML(readFileSync(htmlPath, "utf8"));
22+
const proto = Object.getPrototypeOf(document.createElement("div"));
23+
Object.defineProperty(proto, "clientWidth", {
24+
configurable: true,
25+
get(this: { id?: string }) {
26+
return String(this.id || "").startsWith("wim-lane") ? 800 : 1080;
27+
},
28+
});
29+
Object.defineProperty(proto, "clientHeight", {
30+
configurable: true,
31+
get(this: { id?: string }) {
32+
return String(this.id || "").startsWith("wim-row") ? 80 : 40;
33+
},
34+
});
35+
36+
let driver: { t: number } | undefined;
37+
const gsap = {
38+
timeline() {
39+
return {
40+
paused: true,
41+
to(target: { t: number }) {
42+
driver = target;
43+
return this;
44+
},
45+
};
46+
},
47+
};
48+
const variables = {
49+
rows: JSON.stringify(rows.map((row) => ({ ...row, avatar: "" }))),
50+
...extra,
51+
};
52+
const sandbox = { document, window, console, gsap };
53+
(window as unknown as { __hfVariables: typeof variables }).__hfVariables = variables;
54+
const source = [...document.querySelectorAll("script")]
55+
.map((node) => node.textContent || "")
56+
.find((text) => text.includes("function speedFor"));
57+
if (!source) throw new Error("speedFor script missing");
58+
runInNewContext(source, sandbox);
59+
if (!driver) throw new Error("timeline driver missing");
60+
driver.t = 1;
61+
62+
const out = new Map<number, number>();
63+
for (const row of document.querySelectorAll(".wim-row")) {
64+
const merges = Number.parseInt(row.querySelector(".wim-count")?.textContent || "", 10);
65+
const transform =
66+
(row.querySelector(".wim-avatar") as HTMLElement | null)?.style.transform || "";
67+
const match = /translateX\(([-\d.]+)px\)/.exec(transform);
68+
out.set(merges, match ? Number(match[1]) : Number.NaN);
69+
}
70+
return out;
71+
}
72+
73+
describe("week-in-merges speed", () => {
74+
it("binds 30, 29, and 1 so distance stays within 5% of the merge ratio", () => {
75+
const rows = [
76+
{ name: "Ada", merges: 30 },
77+
{ name: "Bao", merges: 29 },
78+
{ name: "Cam", merges: 1 },
79+
];
80+
const traveled = distances(rows);
81+
const pairs: Array<[number, number]> = [
82+
[29, 30],
83+
[1, 30],
84+
[1, 29],
85+
];
86+
for (const [left, right] of pairs) {
87+
const got = traveled.get(left)! / traveled.get(right)!;
88+
const want = left / right;
89+
expect(
90+
Math.abs(got - want) / want,
91+
`${left}/${right} traveled ${got}, wanted ${want}`,
92+
).toBeLessThanOrEqual(0.05);
93+
}
94+
95+
// The same counts with the quiet row past the cap. A visible-range rescale
96+
// then pins 29 to the floor; proportionality keeps 29/30.
97+
const capped = distances(rows, { rows_max: 2 });
98+
expect(capped.has(1)).toBe(false);
99+
const cappedRatio = capped.get(29)! / capped.get(30)!;
100+
expect(Math.abs(cappedRatio - 29 / 30) / (29 / 30)).toBeLessThanOrEqual(0.05);
101+
});
102+
});
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
{
2+
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
3+
"name": "week-in-merges",
4+
"type": "hyperframes:block",
5+
"title": "Week in Merges",
6+
"description": "Each author's avatar bounces across their row at a speed set by how many pull requests they merged",
7+
"tags": ["data", "chart", "ranking", "leaderboard"],
8+
"dimensions": {
9+
"width": 1080,
10+
"height": 1350
11+
},
12+
"duration": 16,
13+
"variables": [
14+
{
15+
"id": "eyebrow",
16+
"type": "string",
17+
"label": "Eyebrow",
18+
"default": "ACME / WEEK IN MERGES"
19+
},
20+
{
21+
"id": "title",
22+
"type": "string",
23+
"label": "Title",
24+
"default": "Pull requests merged this week"
25+
},
26+
{
27+
"id": "period",
28+
"type": "string",
29+
"label": "Period",
30+
"default": "1 September - 7 September 2026 · America/Los_Angeles"
31+
},
32+
{
33+
"id": "total",
34+
"type": "string",
35+
"label": "Total",
36+
"default": "12 PRs"
37+
},
38+
{
39+
"id": "footer",
40+
"type": "string",
41+
"label": "Footer",
42+
"default": "Speed proportional to merges · By PR author"
43+
},
44+
{
45+
"id": "source",
46+
"type": "string",
47+
"label": "Source line",
48+
"default": "Example org · 1 repo · Bots · 0"
49+
},
50+
{
51+
"id": "rows",
52+
"type": "string",
53+
"label": "Rows (JSON array of name, merges, avatar)",
54+
"default": "[{\"name\":\"Ada\",\"merges\":12,\"avatar\":\"\"},{\"name\":\"Bao\",\"merges\":7,\"avatar\":\"\"},{\"name\":\"Cam\",\"merges\":4,\"avatar\":\"\"},{\"name\":\"Dee\",\"merges\":2,\"avatar\":\"\"},{\"name\":\"Eli\",\"merges\":1,\"avatar\":\"\"}]"
55+
},
56+
{
57+
"id": "rows_max",
58+
"type": "number",
59+
"label": "Maximum rows",
60+
"default": 12,
61+
"min": 1,
62+
"max": 12,
63+
"step": 1,
64+
"description": "Authors drawn, highest merge count first."
65+
},
66+
{
67+
"id": "overflow",
68+
"type": "string",
69+
"label": "Overflow line",
70+
"default": "",
71+
"description": "Line for everyone past the cap. Empty writes it from the rows left out."
72+
}
73+
],
74+
"files": [
75+
{
76+
"path": "week-in-merges.html",
77+
"target": "compositions/week-in-merges.html",
78+
"type": "hyperframes:composition"
79+
}
80+
],
81+
"params": [
82+
{
83+
"key": "eyebrow",
84+
"label": "Eyebrow",
85+
"type": "text",
86+
"default": "ACME / WEEK IN MERGES"
87+
},
88+
{
89+
"key": "title",
90+
"label": "Title",
91+
"type": "text",
92+
"default": "Pull requests merged this week"
93+
},
94+
{
95+
"key": "period",
96+
"label": "Period",
97+
"type": "text",
98+
"default": "1 September - 7 September 2026 · America/Los_Angeles"
99+
},
100+
{
101+
"key": "total",
102+
"label": "Total",
103+
"type": "text",
104+
"default": "12 PRs"
105+
},
106+
{
107+
"key": "footer",
108+
"label": "Footer",
109+
"type": "text",
110+
"default": "Speed proportional to merges · By PR author"
111+
},
112+
{
113+
"key": "source",
114+
"label": "Source line",
115+
"type": "text",
116+
"default": "Example org · 1 repo · Bots · 0"
117+
},
118+
{
119+
"key": "rows",
120+
"label": "Rows (JSON array of name, merges, avatar)",
121+
"type": "text",
122+
"default": "[{\"name\":\"Ada\",\"merges\":12,\"avatar\":\"\"},{\"name\":\"Bao\",\"merges\":7,\"avatar\":\"\"},{\"name\":\"Cam\",\"merges\":4,\"avatar\":\"\"},{\"name\":\"Dee\",\"merges\":2,\"avatar\":\"\"},{\"name\":\"Eli\",\"merges\":1,\"avatar\":\"\"}]"
123+
},
124+
{
125+
"key": "rows_max",
126+
"label": "Maximum rows",
127+
"type": "number",
128+
"default": "12",
129+
"min": 1,
130+
"max": 12,
131+
"step": 1
132+
},
133+
{
134+
"key": "overflow",
135+
"label": "Overflow line",
136+
"type": "text",
137+
"default": ""
138+
}
139+
]
140+
}

0 commit comments

Comments
 (0)