22< html >
33< head >
44 < meta charset ="utf-8 ">
5- < meta name ="viewport " content ="width=device-width, initial-scale=1.0 ">
5+ < meta name ="viewport " content ="width=device-width, shrink-to-fit=0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0 ">
6+
67 < title > Contributor Network</ title >
8+
9+ < meta name ="author " content ="Development Seed ">
10+ < meta name ="description " content ="An interactive visualization of contributors and their connections to repositories ">
11+ < meta name ="theme-color " content ="#cf3f02 ">
12+ < meta property ="og:title " content ="The Development Seed Contributor Network ">
13+ < meta property ="og:url " content ="https://developmentseed.org/contributor-network/ ">
14+ < meta property ="og:type " content ="article ">
15+ < meta property ="og:locale " content ="en_US ">
16+ < meta property ="og:image " content ="https://developmentseed.org/contributor-network/site-image.jpg ">
17+ < meta property ="og:image:type " content ="image/jpeg ">
18+ < meta property ="og:image:width " content ="1200 ">
19+ < meta property ="og:image:height " content ="800 ">
20+
721 < link rel ="shortcut icon " type ="image/png " href ="assets/img/favicon.png " />
822 < link rel ="stylesheet " href ="assets/css/style.css ">
23+ < link rel ="preconnect " href ="https://fonts.googleapis.com ">
24+ < link rel ="preconnect " href ="https://fonts.gstatic.com " crossorigin >
25+ < link href ="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,700;1,400;1,700&family=Roboto+Condensed:wght@400;700&family=Fira+Code:wght@300..700&display=swap " rel ="stylesheet ">
26+
927 < script src ="assets/lib/d3.v7.js "> </ script >
1028 < script src ="assets/lib/d3-delaunay.js "> </ script >
1129 < script src ="assets/lib/d3-bboxCollide.min.js "> </ script >
1533 < div id ="chart-introduction ">
1634 < div id ="chart-title ">
1735 < h1 > The Contributor Network of</ h1 >
18- < h1 > < span id ="title-repo-name " class ="central-repo "> Development Seed </ span > </ h1 >
36+ < h1 > < span id ="title-repo-name " class ="central-repo "> ... </ span > </ h1 >
1937 < p id ="last-commit-date "> </ p >
2038 </ div >
2139 < div id ="chart-intro-text ">
22- < p > Explore the network of contributors and repositories.</ p >
40+ < p id =" chart-description " > Explore the network of contributors and repositories.</ p >
2341 < p > This visualization is based off of the excellent < a href ="https://nbremer.github.io/ORCA/top-contributor-network/ "> Top Contributor Network</ a > from < a href ="https://github.com/nbremer "> Nadieh Bremer</ a > , and is licensed the same (MPL).</ p >
2442 </ div >
2543 </ div >
@@ -51,84 +69,74 @@ <h1><span id="title-repo-name" class="central-repo">Development Seed</span></h1>
5169 </ div >
5270
5371 < script type ="module ">
54- // Load the main visualization module and handle all initialization
5572 import { createContributorNetworkVisual } from './js/chart.js' ;
5673
57- const REPOSITORY = "NASA-IMPACT/veda-config" ;
58- const contributor_padding = 20 ;
74+ // ─── Load configuration ───────────────────────────────────
75+ // In production, config.json is generated by the build command from config.toml.
76+ // For local development, place a config.json in assets/data/ with at minimum:
77+ // { "central_repository": "owner/repo", "contributor_padding": 20, "contributors": {} }
78+ const configResponse = await fetch ( 'assets/data/config.json' ) ;
79+ if ( ! configResponse . ok ) {
80+ document . getElementById ( "chart-container" ) . innerHTML =
81+ '<p style="color: red; padding: 20px;">Error: Could not load config.json. Run the build command or create assets/data/config.json for local development.</p>' ;
82+ throw new Error ( 'Failed to load config.json' ) ;
83+ }
84+ const config = await configResponse . json ( ) ;
5985
60- // Master contributor list - simplified for testing
61- const mainContributors = new Set ( [
62- "Angela Camacho" ,
63- "Alice Rühl" ,
64- "Lane Goodman" ,
65- "Aimee Barciauskas" ,
66- "Anthony Boyd"
67- ] ) ;
86+ const REPOSITORY = config . central_repository ;
87+ const contributor_padding = config . contributor_padding || 20 ;
6888
69- // For compatibility with isValidContributor function
70- const masterContributors = { } ;
89+ // Build contributor lookup maps from config
90+ const masterContributors = config . contributors || { } ;
7191 const displayNameToUsername = { } ;
72- mainContributors . forEach ( name => {
73- masterContributors [ name ] = true ;
74- displayNameToUsername [ name ] = name ;
92+ Object . entries ( masterContributors ) . forEach ( ( [ username , displayName ] ) => {
93+ displayNameToUsername [ displayName ] = username ;
7594 } ) ;
7695
77- // Load data
78- const promises = [
79- d3 . csv ( 'assets/data/top_contributors.csv' ) ,
80- d3 . csv ( 'assets/data/repositories.csv' ) ,
81- d3 . csv ( 'assets/data/links.csv' )
82- ] ;
96+ // ─── Populate page from config ────────────────────────────
97+ const repoDisplayName = REPOSITORY . includes ( '/' ) ? REPOSITORY . split ( '/' ) . pop ( ) : REPOSITORY ;
98+ document . getElementById ( "title-repo-name" ) . textContent = repoDisplayName ;
99+ if ( config . title ) document . title = config . title ;
100+ if ( config . description ) document . getElementById ( "chart-description" ) . textContent = config . description ;
83101
84- let container = document . getElementById ( "chart-container" ) ;
85- let wrapper = document . getElementById ( "chart-wrapper " ) ;
86- let contributorNetworkVisual ;
102+ // ─── Set up sizing ────────────────────────────────────────
103+ const container = document . getElementById ( "chart-container " ) ;
104+ const wrapper = document . getElementById ( "chart-wrapper" ) ;
87105
88- // Get chart dimensions based on available container space
89106 function getChartDimensions ( ) {
90107 let wrapperRect = wrapper . getBoundingClientRect ( ) ;
91108 let availableWidth = wrapperRect . width - 40 ;
92-
93109 if ( availableWidth < 400 ) {
94110 availableWidth = Math . min ( window . innerWidth - 40 , 1400 - 40 ) ;
95111 }
96-
97112 return Math . max ( availableWidth , 600 ) ;
98113 }
99114
100- // Initialize visualization
101- function initVisualization ( ) {
102- let size = getChartDimensions ( ) ;
103- container . style . height = size + 'px' ;
104-
105- contributorNetworkVisual = createContributorNetworkVisual (
106- container ,
107- REPOSITORY ,
108- contributor_padding ,
109- masterContributors ,
110- displayNameToUsername
111- )
112- . width ( size )
113- . height ( size )
114- . repository ( REPOSITORY ) ;
115-
116- return size ;
117- }
118-
119- let size = initVisualization ( ) ;
115+ // ─── Initialize visualization ─────────────────────────────
116+ let size = getChartDimensions ( ) ;
117+ container . style . height = size + 'px' ;
118+
119+ const contributorNetworkVisual = createContributorNetworkVisual (
120+ container ,
121+ REPOSITORY ,
122+ contributor_padding ,
123+ masterContributors ,
124+ displayNameToUsername
125+ )
126+ . width ( size )
127+ . height ( size )
128+ . repository ( REPOSITORY ) ;
129+
130+ // ─── Load CSV data and render ─────────────────────────────
131+ const promises = [
132+ d3 . csv ( 'assets/data/top_contributors.csv' ) ,
133+ d3 . csv ( 'assets/data/repositories.csv' ) ,
134+ d3 . csv ( 'assets/data/links.csv' )
135+ ] ;
120136
121137 document . fonts . ready . then ( ( ) => {
122138 Promise . all ( promises ) . then ( values => {
123- let formatDigit = d3 . format ( ",.2r" ) ;
124-
125- // Deep copy data
126- let data_raw = [ ] ;
127- data_raw . push ( JSON . parse ( JSON . stringify ( values [ 0 ] ) ) ) ;
128- data_raw . push ( JSON . parse ( JSON . stringify ( values [ 1 ] ) ) ) ;
129- data_raw . push ( JSON . parse ( JSON . stringify ( values [ 2 ] ) ) ) ;
130-
131- // Generate organization options
139+ // Generate organization filter options
132140 const uniqueOrgs = new Set ( ) ;
133141 values [ 1 ] . forEach ( repo => {
134142 const owner = repo . repo . substring ( 0 , repo . repo . indexOf ( "/" ) ) ;
@@ -145,7 +153,7 @@ <h1><span id="title-repo-name" class="central-repo">Development Seed</span></h1>
145153 } ) ;
146154
147155 let currentSelectedOrg = null ;
148- orgSelect . addEventListener ( "change" , function ( ) {
156+ orgSelect . addEventListener ( "change" , function ( ) {
149157 const selectedOrg = this . value ;
150158 if ( selectedOrg === "" ) {
151159 if ( currentSelectedOrg ) {
@@ -170,14 +178,12 @@ <h1><span id="title-repo-name" class="central-repo">Development Seed</span></h1>
170178 statsElement . textContent = `Filtered to: ${ currentSelectedOrg } ` ;
171179 }
172180 }
173-
174181 updateFilterStats ( ) ;
175182
176183 // Render the visualization
177184 contributorNetworkVisual ( values ) ;
178185
179- // Get dates
180- let central_repo = values [ 1 ] . find ( d => d . repo === REPOSITORY ) ;
186+ // Display most recent commit date
181187 let formatDateLong = d3 . utcFormat ( "%B %-e, %Y" ) ;
182188 let links_to_central = values [ 2 ] . filter ( d => {
183189 if ( typeof d . target === 'object' && d . target && d . target . data ) {
@@ -187,17 +193,18 @@ <h1><span id="title-repo-name" class="central-repo">Development Seed</span></h1>
187193 } ) ;
188194 let most_recent_commit = d3 . max ( links_to_central , d => + d . commit_sec_max ) ;
189195 if ( most_recent_commit ) {
190- // Convert Unix timestamp (seconds) to Date object (milliseconds)
191196 let commitDate = new Date ( most_recent_commit * 1000 ) ;
192- document . getElementById ( "last-commit-date" ) . innerHTML = `Including commits up to ${ formatDateLong ( commitDate ) } ` ;
197+ document . getElementById ( "last-commit-date" ) . innerHTML =
198+ `Including commits up to ${ formatDateLong ( commitDate ) } ` ;
193199 }
194200 } ) . catch ( err => {
195201 console . error ( 'Error loading data:' , err ) ;
196- document . getElementById ( "chart-container" ) . innerHTML = '<p style="color: red; padding: 20px;">Error loading data files. Make sure CSV files exist.</p>' ;
202+ document . getElementById ( "chart-container" ) . innerHTML =
203+ '<p style="color: red; padding: 20px;">Error loading data files. Make sure CSV files exist in assets/data/.</p>' ;
197204 } ) ;
198205 } ) ;
199206
200- // Handle resize
207+ // ─── Handle resize ────────────────────────────────────────
201208 let resizeTimer = null ;
202209 window . addEventListener ( "resize" , function ( ) {
203210 clearTimeout ( resizeTimer ) ;
0 commit comments