From 462898f4b442a25d39c14329cb7db2f921d1a6d3 Mon Sep 17 00:00:00 2001
From: Peolite1
Date: Mon, 27 Jul 2026 20:59:29 +0100
Subject: [PATCH 1/4] feat: add opt-in contributor leaderboard
---
scripts/gen-contributors.mjs | 49 +++++++++++++++++++
src/App.tsx | 9 ++++
src/data/contributors-optout.json | 4 ++
src/data/contributors.json | 9 ++++
src/pages/Contributors.tsx | 79 +++++++++++++++++++++++++++++++
5 files changed, 150 insertions(+)
create mode 100644 scripts/gen-contributors.mjs
create mode 100644 src/data/contributors-optout.json
create mode 100644 src/data/contributors.json
create mode 100644 src/pages/Contributors.tsx
diff --git a/scripts/gen-contributors.mjs b/scripts/gen-contributors.mjs
new file mode 100644
index 0000000..f2abdec
--- /dev/null
+++ b/scripts/gen-contributors.mjs
@@ -0,0 +1,49 @@
+import fs from 'node:fs/promises';
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const OPTOUT_FILE = path.join(__dirname, '../src/data/contributors-optout.json');
+const OUTPUT_FILE = path.join(__dirname, '../src/data/contributors.json');
+
+async function run() {
+ try {
+ const optOutData = await fs.readFile(OPTOUT_FILE, 'utf-8');
+ const optOutList = JSON.parse(optOutData);
+
+ console.log('Fetching contributors from GitHub API...');
+
+ // In a real run without token, rate limits apply, so we handle fetch gracefully
+ const res = await fetch('https://api.github.com/repos/wraith-protocol/www/contributors', {
+ headers: {
+ 'User-Agent': 'Node-Fetch',
+ ...(process.env.GITHUB_TOKEN ? { Authorization: `Bearer ${process.env.GITHUB_TOKEN}` } : {})
+ }
+ });
+
+ if (!res.ok) {
+ throw new Error(`GitHub API returned ${res.status} ${res.statusText}`);
+ }
+
+ const contributors = await res.json();
+
+ const leaderboard = contributors
+ .filter(c => !optOutList.includes(c.login))
+ .map(c => ({
+ username: c.login,
+ avatar: c.avatar_url,
+ profile: c.html_url,
+ prCount: c.contributions,
+ // Mock wave participation as requested, or derive if data existed.
+ waves: ['Wave 1']
+ }));
+
+ await fs.writeFile(OUTPUT_FILE, JSON.stringify(leaderboard, null, 2), 'utf-8');
+ console.log(`Successfully generated contributors.json with ${leaderboard.length} contributors.`);
+ } catch (error) {
+ console.error('Error generating contributors:', error);
+ process.exit(1);
+ }
+}
+
+run();
diff --git a/src/App.tsx b/src/App.tsx
index aab59e5..43779a7 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -27,6 +27,7 @@ const Roadmap = lazy(() => import('./pages/Roadmap'));
const CaseStudies = lazy(() => import('./pages/CaseStudies'));
const Careers = lazy(() => import('./pages/Careers'));
const NotFound = lazy(() => import('./pages/NotFound'));
+const Contributors = lazy(() => import('./pages/Contributors'));
function Home() {
return (
@@ -87,6 +88,14 @@ export default function App() {
}
/>
+
+
+
+ }
+ />
} />
diff --git a/src/data/contributors-optout.json b/src/data/contributors-optout.json
new file mode 100644
index 0000000..33d816a
--- /dev/null
+++ b/src/data/contributors-optout.json
@@ -0,0 +1,4 @@
+[
+ "dependabot[bot]",
+ "github-actions[bot]"
+]
diff --git a/src/data/contributors.json b/src/data/contributors.json
new file mode 100644
index 0000000..bf92b79
--- /dev/null
+++ b/src/data/contributors.json
@@ -0,0 +1,9 @@
+[
+ {
+ "username": "ghost",
+ "avatar": "https://github.com/ghost.png",
+ "profile": "https://github.com/ghost",
+ "prCount": 10,
+ "waves": ["Wave 1"]
+ }
+]
diff --git a/src/pages/Contributors.tsx b/src/pages/Contributors.tsx
new file mode 100644
index 0000000..b50af0e
--- /dev/null
+++ b/src/pages/Contributors.tsx
@@ -0,0 +1,79 @@
+import React, { useEffect, useState } from 'react';
+import contributorsData from '../data/contributors.json';
+
+interface Contributor {
+ username: string;
+ avatar: string;
+ profile: string;
+ prCount: number;
+ waves: string[];
+}
+
+export default function Contributors() {
+ const [contributors, setContributors] = useState([]);
+
+ useEffect(() => {
+ // Data is static for the client and refreshed weekly by a GitHub Action/script.
+ // We import it directly here.
+ setContributors(contributorsData as Contributor[]);
+ }, []);
+
+ return (
+
+
+
+ Contributor Leaderboard
+
+
+ Celebrating the amazing people who build and improve Wraith Protocol.
+
+
+
+ Note: Data is refreshed weekly via an automated script.
+ If you wish to opt-out of this leaderboard, please submit a PR to add your username to our opt-out list at src/data/contributors-optout.json.
+
+
+
+
+
+
+ );
+}
From 945318aae7dc432f84884ff123f4b199d0495b21 Mon Sep 17 00:00:00 2001
From: Peolite001
Date: Fri, 31 Jul 2026 02:09:37 +0100
Subject: [PATCH 2/4] style: format files
---
scripts/gen-contributors.mjs | 24 ++++++++++++++----------
src/data/contributors-optout.json | 5 +----
src/pages/Contributors.tsx | 23 ++++++++++++++---------
3 files changed, 29 insertions(+), 23 deletions(-)
diff --git a/scripts/gen-contributors.mjs b/scripts/gen-contributors.mjs
index f2abdec..199f745 100644
--- a/scripts/gen-contributors.mjs
+++ b/scripts/gen-contributors.mjs
@@ -10,36 +10,40 @@ async function run() {
try {
const optOutData = await fs.readFile(OPTOUT_FILE, 'utf-8');
const optOutList = JSON.parse(optOutData);
-
+
console.log('Fetching contributors from GitHub API...');
-
+
// In a real run without token, rate limits apply, so we handle fetch gracefully
const res = await fetch('https://api.github.com/repos/wraith-protocol/www/contributors', {
headers: {
'User-Agent': 'Node-Fetch',
- ...(process.env.GITHUB_TOKEN ? { Authorization: `Bearer ${process.env.GITHUB_TOKEN}` } : {})
- }
+ ...(process.env.GITHUB_TOKEN
+ ? { Authorization: `Bearer ${process.env.GITHUB_TOKEN}` }
+ : {}),
+ },
});
if (!res.ok) {
throw new Error(`GitHub API returned ${res.status} ${res.statusText}`);
}
-
+
const contributors = await res.json();
-
+
const leaderboard = contributors
- .filter(c => !optOutList.includes(c.login))
- .map(c => ({
+ .filter((c) => !optOutList.includes(c.login))
+ .map((c) => ({
username: c.login,
avatar: c.avatar_url,
profile: c.html_url,
prCount: c.contributions,
// Mock wave participation as requested, or derive if data existed.
- waves: ['Wave 1']
+ waves: ['Wave 1'],
}));
await fs.writeFile(OUTPUT_FILE, JSON.stringify(leaderboard, null, 2), 'utf-8');
- console.log(`Successfully generated contributors.json with ${leaderboard.length} contributors.`);
+ console.log(
+ `Successfully generated contributors.json with ${leaderboard.length} contributors.`,
+ );
} catch (error) {
console.error('Error generating contributors:', error);
process.exit(1);
diff --git a/src/data/contributors-optout.json b/src/data/contributors-optout.json
index 33d816a..ae4062b 100644
--- a/src/data/contributors-optout.json
+++ b/src/data/contributors-optout.json
@@ -1,4 +1 @@
-[
- "dependabot[bot]",
- "github-actions[bot]"
-]
+["dependabot[bot]", "github-actions[bot]"]
diff --git a/src/pages/Contributors.tsx b/src/pages/Contributors.tsx
index b50af0e..ab4bb83 100644
--- a/src/pages/Contributors.tsx
+++ b/src/pages/Contributors.tsx
@@ -29,12 +29,13 @@ export default function Contributors() {
- Note: Data is refreshed weekly via an automated script.
- If you wish to opt-out of this leaderboard, please submit a PR to add your username to our opt-out list at src/data/contributors-optout.json.
+ Note: Data is refreshed weekly via an automated script. If you wish to
+ opt-out of this leaderboard, please submit a PR to add your username to our opt-out list
+ at src/data/contributors-optout.json.
-
+
{contributors.map((contributor) => (
-
@@ -54,12 +55,16 @@ export default function Contributors() {
{contributor.username}
- {contributor.prCount} Contributions
+ {contributor.prCount}{' '}
+ Contributions
{contributor.waves && contributor.waves.length > 0 && (
- {contributor.waves.map(wave => (
-
+ {contributor.waves.map((wave) => (
+
{wave}
))}
From 4dc763bfd56f74f89307c178e924eb577afd4dcd Mon Sep 17 00:00:00 2001
From: Peolite001
Date: Fri, 31 Jul 2026 02:15:22 +0100
Subject: [PATCH 3/4] fix: resolve unused import typescript errors
---
src/App.tsx | 8 ++++++++
src/pages/Contributors.tsx | 2 +-
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/src/App.tsx b/src/App.tsx
index 4f721ef..dc01097 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -92,6 +92,14 @@ export default function App() {
}
/>
+
+
+
+ }
+ />
} />
diff --git a/src/pages/Contributors.tsx b/src/pages/Contributors.tsx
index ab4bb83..8c39f35 100644
--- a/src/pages/Contributors.tsx
+++ b/src/pages/Contributors.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useState } from 'react';
+import { useEffect, useState } from 'react';
import contributorsData from '../data/contributors.json';
interface Contributor {
From 09a6803d25331e4f7af7ccb614e8b2fa2c960d22 Mon Sep 17 00:00:00 2001
From: Peolite001
Date: Fri, 31 Jul 2026 02:21:56 +0100
Subject: [PATCH 4/4] fix: restore missing contributors route
---
src/App.tsx | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/src/App.tsx b/src/App.tsx
index 981c99a..5ffe66b 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -112,6 +112,14 @@ export default function App() {
}
/>
+
+
+
+ }
+ />
} />