Skip to content
Open
7 changes: 3 additions & 4 deletions core/include/internal/gpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,14 +189,13 @@ class GPU {
cleanup();
return;
}
if (wgpu::AdapterInfo info;
adapter.GetInfo(&info) != wgpu::Status::Success ||
info.backendType == wgpu::BackendType::Null) {
if (wgpu::AdapterInfo info; adapter.GetInfo(&info) != wgpu::Status::Success ||
info.backendType == wgpu::BackendType::Null) {
IMG2NUM_LOG_INFO("No usable WebGPU adapter found. Falling back to CPU.");
cleanup();
return;
}

// ---------------------------------------------------------
// 2. Get Device
// ---------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions docs/docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ const config = {
},
],
"./plugins/github-stats/index.js",
"./plugins/hallOfFame/index.js",
[
"docusaurus-plugin-copy-page-button",
{
Expand Down
109 changes: 109 additions & 0 deletions docs/lib/hallOfFame.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
*IMPORTANT: GitHub rate limits unauthenticated requests, so
*this may break during development. To fix it, authenticate manually
*/

import hallOfFameInput from "../src/data/hall-of-fame.json";

const REPO = "Ryan-Millard/Img2Num";
const API = "https://api.github.com";
const PER_PAGE = 50;
const MAX_PAGES = 10;

const isBot = (user) => user?.type === "Bot" || user?.login?.endsWith("[bot]");

const withAvatarSize = (url) => (url ? `${url}${url.includes("?") ? "&" : "?"}s=96` : null);

async function fetchAllContributors(headers) {
const all = [];

try {
for (let page = 1; page <= MAX_PAGES; page++) {
const res = await fetch(`${API}/repos/${REPO}/contributors?per_page=${PER_PAGE}&page=${page}`, { headers });
if (!res.ok) break;

const batch = await res.json();
if (!Array.isArray(batch) || batch.length === 0) break;

all.push(...batch);
if (batch.length < PER_PAGE) break;
}
} catch {}

return all
.filter((c) => !isBot(c))
.map((c) => ({
username: c.login,
avatarUrl: withAvatarSize(c.avatar_url),
profileUrl: c.html_url,
contributions: c.contributions,
}));
}

async function fetchFeaturedMember(entry, headers) {
const fallback = {
username: entry.username,
name: entry.username,
avatarUrl: null,
profileUrl: `https://github.com/${entry.username}`,
year: entry.year,
blurb: entry.blurb || "",
mergedPRCount: null,
recentPRs: [],
};

try {
const userRes = await fetch(`${API}/users/${entry.username}`, { headers });
if (!userRes.ok) return fallback;

const userData = await userRes.json();
if (isBot(userData)) return null;

const user = {
...fallback,
username: userData.login || entry.username,
name: userData.name || userData.login || entry.username,
avatarUrl: withAvatarSize(userData.avatar_url),
profileUrl: userData.html_url || fallback.profileUrl,
};

const searchUrl = `${API}/search/issues?q=repo:${REPO}+type:pr+author:${entry.username}+is:merged&sort=created&order=desc&per_page=5`;
const prRes = await fetch(searchUrl, { headers });

if (prRes.ok) {
const prData = await prRes.json();
user.mergedPRCount = prData.total_count ?? 0;
user.recentPRs = (prData.items || []).map((pr) => ({
title: pr.title,
url: pr.html_url,
number: pr.number,
}));
}

return user;
} catch {
return fallback;
}
}

export async function fetchHallOfFameData({ token } = {}) {
const headers = {
"User-Agent": "Img2Num-Docs",
Accept: "application/vnd.github+json",
...(token ? { Authorization: `Bearer ${token}` } : {}),
};

const [contributors, featuredRaw] = await Promise.all([fetchAllContributors(headers), Promise.all(hallOfFameInput.map((e) => fetchFeaturedMember(e, headers)))]);

// Group featured members by year, newest first
const grouped = featuredRaw.filter(Boolean).reduce((acc, item) => {
(acc[item.year] ||= []).push(item);
return acc;
}, {});

const featured = Object.keys(grouped)
.sort((a, b) => Number(b) - Number(a))
.map((year) => ({ year: Number(year), members: grouped[year] }));

return { featured, contributors };
}
17 changes: 17 additions & 0 deletions docs/plugins/hallOfFame/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { fetchHallOfFameData } from "../../lib/hallOfFame.js";

export default function hallOfFamePlugin() {
return {
name: "hall-of-fame",

async loadContent() {
const token = process.env.GITHUB_TOKEN;

return await fetchHallOfFameData({ token });
},

async contentLoaded({ content, actions }) {
actions.setGlobalData(content);
},
};
}
42 changes: 42 additions & 0 deletions docs/src/data/hall-of-fame.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
[
{
"username": "08abhinav",
"year": 2026,
"blurb": "Built this page"
},
{
"username": "siddharth1407",
"year": 2026,
"blurb": "Built a reusable WebGPU helper layer, ported both kernels to it, and documented gpu_utils"
},
{
"username": "hjmillard",
"year": 2025,
"blurb": "Wrote the original K-means and thresholding utilities and later split K-means into its own module"
},
{
"username": "fransafu",
"year": 2025,
"blurb": "Collaborated with @Krasner on the first implementation of the bilateral filter"
},
{
"username": "krasner",
"year": 2025,
"blurb": "Turned Img2Num into what it is today"
},
{
"username": "Prachi-Gupta2808",
"year": 2026,
"blurb": "Did a lot...★★★"
},
{
"username": "dougwithseismic",
"year": "2025",
"blurb": "Set up the Vitest unit-testing infrastructure and co-authored the Docker/CMake cross-platform build"
},
{
"username": "Ryan-Millard",
"year": 2025,
"blurb": "Did nothing"
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Comment thread
Ryan-Millard marked this conversation as resolved.
]
Loading
Loading