Skip to content

Commit 8545a6f

Browse files
committed
add starchart
1 parent e839245 commit 8545a6f

2 files changed

Lines changed: 78 additions & 0 deletions

File tree

.github/workflows/deploy.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ on:
44
push:
55
branches: ['main']
66
workflow_dispatch:
7+
schedule:
8+
- cron: '0 19 * * *'
79

810
permissions:
911
contents: read
@@ -29,6 +31,11 @@ jobs:
2931
- name: 📦 Install dependencies
3032
run: bun install
3133

34+
- name: 📊 Generate star history SVG
35+
run: node scripts/gen-star-chart.mjs
36+
env:
37+
STAR_TOKEN: ${{ secrets.STAR_TOKEN }}
38+
3239
- name: 🛠️ Build project
3340
run: bun run build
3441

scripts/gen-star-chart.mjs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { writeFileSync, mkdirSync } from 'fs';
2+
3+
const repo = 'LotusInputMethod/fcitx5-lotus';
4+
const token = process.env.STAR_TOKEN;
5+
6+
if (!repo || !token) {
7+
throw new Error('Missing GITHUB_REPOSITORY or STAR_TOKEN');
8+
}
9+
10+
const stars = [];
11+
let page = 1;
12+
while (true) {
13+
const res = await fetch(
14+
`https://api.github.com/repos/${repo}/stargazers?per_page=100&page=${page}`,
15+
{
16+
headers: {
17+
Authorization: `Bearer ${token}`,
18+
Accept: 'application/vnd.github.star+json',
19+
'X-GitHub-Api-Version': '2022-11-28',
20+
},
21+
},
22+
);
23+
if (!res.ok) throw new Error(`GitHub API ${res.status}: ${await res.text()}`);
24+
const data = await res.json();
25+
if (data.length === 0) break;
26+
for (const s of data) stars.push(new Date(s.starred_at).getTime());
27+
if (data.length < 100) break;
28+
page++;
29+
}
30+
31+
stars.sort((a, b) => a - b);
32+
33+
const byDay = new Map();
34+
stars.forEach((t, i) => {
35+
byDay.set(new Date(t).toISOString().slice(0, 10), i + 1);
36+
});
37+
const points = [...byDay.entries()].map(([day, count]) => ({
38+
day,
39+
count,
40+
}));
41+
42+
const W = 800,
43+
H = 400,
44+
PAD = 50;
45+
const maxCount = Math.max(...points.map((p) => p.count), 1);
46+
const n = points.length;
47+
const xy = points.map((p, i) => {
48+
const x = PAD + (i / Math.max(n - 1, 1)) * (W - 2 * PAD);
49+
const y = H - PAD - (p.count / maxCount) * (H - 2 * PAD);
50+
return `${x.toFixed(1)},${y.toFixed(1)}`;
51+
});
52+
53+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${W} ${H}" font-family="sans-serif">
54+
<rect width="${W}" height="${H}" fill="#0d1117" rx="8"/>
55+
<text x="${W / 2}" y="32" fill="#e6edf3" font-size="18" text-anchor="middle" font-weight="bold">
56+
${repo} — Star History (${maxCount} ⭐)
57+
</text>
58+
<line x1="${PAD}" y1="${H - PAD}" x2="${W - PAD}" y2="${H - PAD}" stroke="#30363d"/>
59+
<line x1="${PAD}" y1="${PAD}" x2="${PAD}" y2="${H - PAD}" stroke="#30363d"/>
60+
<text x="${PAD - 8}" y="${PAD + 5}" fill="#8b949e" font-size="12" text-anchor="end">${maxCount}</text>
61+
<text x="${PAD - 8}" y="${H - PAD + 5}" fill="#8b949e" font-size="12" text-anchor="end">0</text>
62+
<text x="${PAD}" y="${H - PAD + 22}" fill="#8b949e" font-size="12">${points[0]?.day ?? ''}</text>
63+
<text x="${W - PAD}" y="${H - PAD + 22}" fill="#8b949e" font-size="12" text-anchor="end">${points[n - 1]?.day ?? ''}</text>
64+
<polyline points="${xy.join(' ')}" fill="none" stroke="#f0b429" stroke-width="2.5" stroke-linejoin="round"/>
65+
</svg>`;
66+
67+
mkdirSync('public', { recursive: true });
68+
writeFileSync('public/starcharts.svg', svg);
69+
console.log(
70+
`Generated public/starcharts.svg with ${maxCount} stars, ${n} points`,
71+
);

0 commit comments

Comments
 (0)