feat: add contributors list and update settings page

- Introduced a new contributors.json file containing a list of contributors with their GitHub usernames.
- Updated AboutSettings component to display the list of contributors dynamically.
- Modified tsconfig.json to enable JSON module resolution for better imports.
- Enhanced Cargo.toml to include JSON feature for reqwest.
- Added a new Rust module to fetch and update the contributors list from GitHub.
This commit is contained in:
Jamie Pine
2026-02-06 01:46:39 -08:00
parent 77ff1d77fc
commit 98a55b83af
6 changed files with 641 additions and 2 deletions

View File

@@ -3,6 +3,7 @@ import { Ball } from "@sd/assets/images";
import Orb from "../../components/Orb";
import { TopBarButton } from "@sd/ui";
import { GlobeHemisphereWest, GithubLogo, DiscordLogo } from "@phosphor-icons/react";
import contributors from "../../contributors.json";
export function AboutSettings() {
@@ -96,11 +97,36 @@ export function AboutSettings() {
</a>
</motion.div>
{/* Contributors */}
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.45 }}
className="max-w-lg text-center mb-8 px-4"
>
<p className="text-[11px] leading-relaxed text-white/30">
{contributors.map((c, i) => (
<span key={c.github}>
{i > 0 && " · "}
<a
href={`https://github.com/${c.github}`}
target="_blank"
rel="noopener noreferrer"
title={`@${c.github}`}
className="hover:text-white/50 transition-colors"
>
{c.name}
</a>
</span>
))}
</p>
</motion.div>
{/* License */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5, delay: 0.5 }}
transition={{ duration: 0.5, delay: 0.55 }}
className="text-center"
>
<a

View File

@@ -0,0 +1,450 @@
[
{
"name": "Jamie Pine",
"github": "jamiepine"
},
{
"name": "Brendan Allan",
"github": "Brendonovich"
},
{
"name": "Oscar Beaumont",
"github": "oscartbeaumont"
},
{
"name": "Ameer Al Ashhab",
"github": "ameer2468"
},
{
"name": "Arnab Chakraborty",
"github": "Rocky43007"
},
{
"name": "Ericson \"Fogo\" Soares",
"github": "fogodev"
},
{
"name": "nikec",
"github": "niikeec"
},
{
"name": "Vítor Vasconcellos",
"github": "HeavenVolkoff"
},
{
"name": "maxichrome",
"github": "maxichrome"
},
{
"name": "jake",
"github": "brxken128"
},
{
"name": "Utku",
"github": "utkubakir"
},
{
"name": "Lynx",
"github": "iLynxcat"
},
{
"name": "Matthew Yung",
"github": "myung03"
},
{
"name": "Julian Braha",
"github": "julianbraha"
},
{
"name": "Gedeon",
"github": "gedeondoescode"
},
{
"name": "William Stoneham",
"github": "RockBacon9922"
},
{
"name": "Haris",
"github": "xPolar"
},
{
"name": "Artsiom Voitas",
"github": "artsiom-voitas"
},
{
"name": "Benjamin",
"github": "benja"
},
{
"name": "Consoli",
"github": "matheus-consoli"
},
{
"name": "Andre",
"github": "CreatingBytes"
},
{
"name": "Aditya",
"github": "Raghav-45"
},
{
"name": "pr",
"github": "PineappleRind"
},
{
"name": "Stella",
"github": "KodingDev"
},
{
"name": "voletro",
"github": "voletro"
},
{
"name": "Lars Gyrup Brink Nielsen",
"github": "LayZeeDK"
},
{
"name": "Olen Latham",
"github": "PyRo1121"
},
{
"name": "Starbird",
"github": "StarbirdTech"
},
{
"name": "Harry Hopkinson",
"github": "Harry-Hopkinson"
},
{
"name": "Abhinav A",
"github": "abhnva"
},
{
"name": "wany-oh",
"github": "wany-oh"
},
{
"name": "Zakher Masri",
"github": "zaaakher"
},
{
"name": "Yukeey",
"github": "Yukeey07"
},
{
"name": "Md. Fahim Bin Amin",
"github": "FahimFBA"
},
{
"name": "Baran Mordoğan",
"github": "okunamayanad"
},
{
"name": "Na Risong",
"github": "HeavySnowJakarta"
},
{
"name": "Sreecharan",
"github": "sr2echa"
},
{
"name": "matt morris",
"github": "mmattbtw"
},
{
"name": "Jules Guesnon",
"github": "JulesGuesnon"
},
{
"name": "Madison Konig",
"github": "MadisonKonig"
},
{
"name": "Omar Hamad",
"github": "etahamad"
},
{
"name": "he1d1",
"github": "he1d1"
},
{
"name": "Ned Park",
"github": "ned-park"
},
{
"name": "Carter",
"github": "berbaroovez"
},
{
"name": "Tom Heaton",
"github": "tomheaton"
},
{
"name": "Tim Havlicek",
"github": "luckydye"
},
{
"name": "Tilo",
"github": "Tilo-K"
},
{
"name": "Sinan Gençoğlu",
"github": "SinanGncgl"
},
{
"name": "Michelangelo Guerra",
"github": "XSPGMike"
},
{
"name": "Marques Scripps",
"github": "MarquesCoding"
},
{
"name": "Abe",
"github": "FastestMolasses"
},
{
"name": "Carter",
"github": "Carterpersall"
},
{
"name": "Vikram Srinivas",
"github": "vikram2009"
},
{
"name": "Twan L",
"github": "TwanLuttik"
},
{
"name": "Syntax",
"github": "TheUltDev"
},
{
"name": "Takshil Mistry",
"github": "daUnknownCoder"
},
{
"name": "Sarah Bobbe",
"github": "SBobbe"
},
{
"name": "Ramprakash",
"github": "CodePurble"
},
{
"name": "Johan Sandgren",
"github": "Qrutz"
},
{
"name": "Phedona",
"github": "Phedona"
},
{
"name": "Percy Ma",
"github": "kecrily"
},
{
"name": "Param Birje",
"github": "ParamBirje"
},
{
"name": "Niklas Wojtkowiak",
"github": "0xnim"
},
{
"name": "Nicolás Fishman",
"github": "nicofishman"
},
{
"name": "Mykola",
"github": "handicraftsman"
},
{
"name": "Nicholas",
"github": "alsonick"
},
{
"name": "0xBA5E64",
"github": "0xBA5E64"
},
{
"name": "Whisht",
"github": "Whisht"
},
{
"name": "Yousef Abu-Salah",
"github": "ykabusalah"
},
{
"name": "andriizaiets",
"github": "andriizaiets"
},
{
"name": "devqore",
"github": "devqore"
},
{
"name": "erikpodusel",
"github": "erikpodusel"
},
{
"name": "David",
"github": "fivestones"
},
{
"name": "Christopher",
"github": "itschip"
},
{
"name": "jenniferdewan",
"github": "jenniferdewan"
},
{
"name": "leo",
"github": "greendoescode"
},
{
"name": "lzt1008",
"github": "lzt1008"
},
{
"name": "mark-strudwick",
"github": "mark-strudwick"
},
{
"name": "markrieder",
"github": "markrieder"
},
{
"name": "mooy",
"github": "mooyg"
},
{
"name": "S.L",
"github": "slvnlrt"
},
{
"name": "AhmedKaram",
"github": "Adamkaram"
},
{
"name": "Alex",
"github": "misxki"
},
{
"name": "Allie",
"github": "ChildishGiant"
},
{
"name": "Anthony Morris",
"github": "amorriscode"
},
{
"name": "BI3TKL",
"github": "Nightingale0504"
},
{
"name": "briamoe",
"github": "briamoe"
},
{
"name": "Bryan",
"github": "CodeWithBryan"
},
{
"name": "Cavell Blood",
"github": "cavellblood"
},
{
"name": "Cedric ",
"github": "ceddy4395"
},
{
"name": "Christo Todorov",
"github": "chroxify"
},
{
"name": "Colin Griffin",
"github": "krumware"
},
{
"name": "Conrad Crawford",
"github": "cnrad"
},
{
"name": "Eric Wyne",
"github": "ecwyne"
},
{
"name": "HardikBandhiya",
"github": "BandhiyaHardik"
},
{
"name": "Ilkka Poutanen",
"github": "ilkka"
},
{
"name": "Hesham Abourgheba",
"github": "IllusionMan1212"
},
{
"name": "Jeremy Möglich",
"github": "JeremyMoeglich"
},
{
"name": "Jesse Rodrigo",
"github": "JSSRDRG"
},
{
"name": "John Xu",
"github": "dyxushuai"
},
{
"name": "Jx",
"github": "JxJxxJxJ"
},
{
"name": "erian",
"github": "oopserian"
},
{
"name": "Leora",
"github": "Kuuchuu"
},
{
"name": "Liam Brewer",
"github": "liambrewer"
},
{
"name": "Lkhsss",
"github": "Lkhsss"
},
{
"name": "Majal",
"github": "majal"
},
{
"name": "Marc Espin",
"github": "marc2332"
},
{
"name": "Matteo Galiazzo",
"github": "gekoxyz"
},
{
"name": "Matthias Berchtold",
"github": "iammatthi"
},
{
"name": "Mohammed Bajuaifer",
"github": "MohammedBajuaifer"
},
{
"name": "Naman Garg",
"github": "namanlp"
},
{
"name": "Nebhay",
"github": "Nebhay"
}
]

View File

@@ -8,6 +8,7 @@
"module": "ESNext",
"target": "ES2022",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"baseUrl": ".",
"paths": {
"~/*": ["./src/*"]

View File

@@ -9,7 +9,7 @@ anyhow = "1"
flate2 = "1.0"
mustache = "0.9"
owo-colors = "4"
reqwest = { version = "0.12", features = ["blocking", "rustls-tls"], default-features = false }
reqwest = { version = "0.12", features = ["blocking", "json", "rustls-tls"], default-features = false }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tar = "0.4"

156
xtask/src/contributors.rs Normal file
View File

@@ -0,0 +1,156 @@
use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use std::path::Path;
const REPO: &str = "spacedriveapp/spacedrive";
const OUTPUT_PATH: &str = "packages/interface/src/contributors.json";
const EXCLUDED_LOGINS: &[&str] = &["cursoragent"];
#[derive(Deserialize)]
struct GitHubContributor {
login: String,
#[serde(rename = "type")]
account_type: String,
}
#[derive(Deserialize)]
struct GitHubUser {
name: Option<String>,
}
#[derive(Serialize)]
struct Contributor {
name: String,
github: String,
}
/// Try to get a GitHub token from the environment or `gh` CLI
fn get_github_token() -> Option<String> {
if let Ok(token) = std::env::var("GITHUB_TOKEN") {
return Some(token);
}
std::process::Command::new("gh")
.args(["auth", "token"])
.output()
.ok()
.and_then(|o| {
if o.status.success() {
String::from_utf8(o.stdout).ok().map(|s| s.trim().to_string())
} else {
None
}
})
}
fn github_get(
client: &reqwest::blocking::Client,
url: &str,
token: Option<&str>,
) -> reqwest::blocking::RequestBuilder {
let mut req = client.get(url);
if let Some(token) = token {
req = req.bearer_auth(token);
}
req
}
pub fn update(project_root: &Path) -> Result<()> {
println!("Fetching contributors from GitHub...");
let token = get_github_token();
if token.is_some() {
println!(" using authenticated requests");
} else {
println!(" no token found, using unauthenticated requests (may hit rate limits)");
println!(" tip: install `gh` CLI and run `gh auth login` for higher limits");
}
let client = reqwest::blocking::Client::builder()
.user_agent("spacedrive-xtask")
.build()
.context("Failed to build HTTP client")?;
// Paginate through all contributors
let mut all_contributors = Vec::new();
let mut page = 1u32;
loop {
let url = format!(
"https://api.github.com/repos/{}/contributors?per_page=100&page={}",
REPO, page
);
let resp: Vec<GitHubContributor> = github_get(&client, &url, token.as_deref())
.send()
.context("Failed to fetch contributors")?
.json()
.context("Failed to parse contributors response")?;
if resp.is_empty() {
break;
}
all_contributors.extend(resp);
page += 1;
}
// Filter out bots and excluded accounts
let humans: Vec<_> = all_contributors
.iter()
.filter(|c| c.account_type == "User" && !EXCLUDED_LOGINS.contains(&c.login.as_str()))
.collect();
println!("Found {} contributors, resolving names...", humans.len());
let mut contributors = Vec::new();
for (i, contributor) in humans.iter().enumerate() {
let name = match resolve_name(&client, &contributor.login, token.as_deref()) {
Ok(Some(n)) => n,
_ => contributor.login.clone(),
};
contributors.push(Contributor {
name,
github: contributor.login.clone(),
});
// Progress indicator every 25 users
if (i + 1) % 25 == 0 {
println!(" resolved {}/{}", i + 1, humans.len());
}
}
println!(" resolved {}/{}", contributors.len(), humans.len());
let output_path = project_root.join(OUTPUT_PATH);
let json = serde_json::to_string_pretty(&contributors)
.context("Failed to serialize contributors")?;
std::fs::write(&output_path, format!("{}\n", json))
.context("Failed to write contributors.json")?;
println!(
"Wrote {} contributors to {}",
contributors.len(),
OUTPUT_PATH
);
Ok(())
}
fn resolve_name(
client: &reqwest::blocking::Client,
login: &str,
token: Option<&str>,
) -> Result<Option<String>> {
let url = format!("https://api.github.com/users/{}", login);
let user: GitHubUser = github_get(client, &url, token)
.send()
.context("Failed to fetch user")?
.json()
.context("Failed to parse user response")?;
Ok(user.name.filter(|n: &String| !n.is_empty()))
}

View File

@@ -25,6 +25,7 @@
//! - No external tools required (except cargo/rustup)
mod config;
mod contributors;
mod native_deps;
mod system;
mod test_core;
@@ -67,6 +68,7 @@ fn main() -> Result<()> {
eprintln!(" build-ios Build sd-ios-core XCFramework for iOS devices and simulator");
eprintln!(" build-mobile Build sd-mobile-core for React Native iOS/Android");
eprintln!(" test-core Run all core integration tests with progress tracking");
eprintln!(" update-contributors Fetch contributors from GitHub and update contributors.json");
eprintln!();
eprintln!("Examples:");
eprintln!(" cargo xtask setup # First time setup");
@@ -88,6 +90,10 @@ fn main() -> Result<()> {
.unwrap_or(false);
test_core_command(verbose)?;
}
"update-contributors" => {
let project_root = find_workspace_root()?;
contributors::update(&project_root)?;
}
_ => {
eprintln!("Unknown command: {}", args[1]);
eprintln!("Run 'cargo xtask' for usage information.");