Skip to content

Commit d60e8eb

Browse files
committed
Add dynamic GitHub stats loading for open-source projects
1 parent c317bdc commit d60e8eb

3 files changed

Lines changed: 224 additions & 40 deletions

File tree

content/opensource/_index.md

Lines changed: 35 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,18 @@ Our research group develops and maintains several open-source software projects.
1818
Systematic CXL Memory Characterization and Performance Analysis at Scale (ASPLOS'25)
1919
</div>
2020
<div class="repo-meta">
21-
<span class="repo-language">
22-
<span class="language-dot"></span>
23-
HTML
24-
</span>
2521
<span class="repo-stats" id="melody-stats">
2622
<span class="repo-stars">
2723
<svg width="16" height="16" viewBox="0 0 24 24" fill="#fbbf24">
2824
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
2925
</svg>
30-
19
26+
<span class="loading">...</span>
3127
</span>
3228
<span class="repo-forks">
3329
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
3430
<path d="M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z"/>
3531
</svg>
36-
0
32+
<span class="loading">...</span>
3733
</span>
3834
</span>
3935
</div>
@@ -51,22 +47,18 @@ Our research group develops and maintains several open-source software projects.
5147
Tiered Memory Management Beyond Hotness (OSDI'25)
5248
</div>
5349
<div class="repo-meta">
54-
<span class="repo-language">
55-
<span class="language-dot"></span>
56-
C
57-
</span>
5850
<span class="repo-stats" id="soaralto-stats">
5951
<span class="repo-stars">
6052
<svg width="16" height="16" viewBox="0 0 24 24" fill="#fbbf24">
6153
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
6254
</svg>
63-
13
55+
<span class="loading">...</span>
6456
</span>
6557
<span class="repo-forks">
6658
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
6759
<path d="M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z"/>
6860
</svg>
69-
3
61+
<span class="loading">...</span>
7062
</span>
7163
</span>
7264
</div>
@@ -84,22 +76,18 @@ Our research group develops and maintains several open-source software projects.
8476
Accurate, Scalable and Extensible NVMe SSD Emulator (FAST'18)
8577
</div>
8678
<div class="repo-meta">
87-
<span class="repo-language">
88-
<span class="language-dot"></span>
89-
C
90-
</span>
9179
<span class="repo-stats" id="femu-stats">
9280
<span class="repo-stars">
9381
<svg width="16" height="16" viewBox="0 0 24 24" fill="#fbbf24">
9482
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
9583
</svg>
96-
486
84+
<span class="loading">...</span>
9785
</span>
9886
<span class="repo-forks">
9987
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
10088
<path d="M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z"/>
10189
</svg>
102-
219
90+
<span class="loading">...</span>
10391
</span>
10492
</span>
10593
</div>
@@ -117,22 +105,18 @@ Our research group develops and maintains several open-source software projects.
117105
CXL-Based Memory Pooling Systems for Cloud Platforms (ASPLOS'23)
118106
</div>
119107
<div class="repo-meta">
120-
<span class="repo-language">
121-
<span class="language-dot"></span>
122-
HTML
123-
</span>
124108
<span class="repo-stats" id="pond-stats">
125109
<span class="repo-stars">
126110
<svg width="16" height="16" viewBox="0 0 24 24" fill="#fbbf24">
127111
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
128112
</svg>
129-
205
113+
<span class="loading">...</span>
130114
</span>
131115
<span class="repo-forks">
132116
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
133117
<path d="M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z"/>
134118
</svg>
135-
43
119+
<span class="loading">...</span>
136120
</span>
137121
</span>
138122
</div>
@@ -150,22 +134,18 @@ Our research group develops and maintains several open-source software projects.
150134
Efficient and Portable Virtual NVMe Storage on ARM SoCs (ASPLOS'20)
151135
</div>
152136
<div class="repo-meta">
153-
<span class="repo-language">
154-
<span class="language-dot"></span>
155-
C
156-
</span>
157137
<span class="repo-stats" id="leapio-stats">
158138
<span class="repo-stars">
159139
<svg width="16" height="16" viewBox="0 0 24 24" fill="#fbbf24">
160140
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
161141
</svg>
162-
28
142+
<span class="loading">...</span>
163143
</span>
164144
<span class="repo-forks">
165145
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
166146
<path d="M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z"/>
167147
</svg>
168-
7
148+
<span class="loading">...</span>
169149
</span>
170150
</span>
171151
</div>
@@ -183,43 +163,58 @@ Our research group develops and maintains several open-source software projects.
183163
A Host/Device Co-Design for Strong Predictability Contract on Modern Flash Storage (SOSP'21)
184164
</div>
185165
<div class="repo-meta">
186-
<span class="repo-language">
187-
<span class="language-dot"></span>
188-
C
189-
</span>
190166
<span class="repo-stats" id="ioda-stats">
191167
<span class="repo-stars">
192168
<svg width="16" height="16" viewBox="0 0 24 24" fill="#fbbf24">
193169
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
194170
</svg>
195-
11
171+
<span class="loading">...</span>
196172
</span>
197173
<span class="repo-forks">
198174
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
199175
<path d="M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z"/>
200176
</svg>
201-
4
177+
<span class="loading">...</span>
202178
</span>
203179
</span>
204180
</div>
205181
</div>
206182

207183
</div>
208184

185+
<style>
186+
.repo-stats {
187+
opacity: 0;
188+
transition: opacity 0.5s ease-in-out;
189+
}
190+
191+
.repo-stats.loaded {
192+
opacity: 1;
193+
}
194+
195+
.repo-stats span {
196+
transition: all 0.3s ease;
197+
}
198+
</style>
199+
200+
<!-- Preload GitHub API endpoints for faster loading -->
201+
<link rel="preconnect" href="https://api.github.com">
202+
<link rel="dns-prefetch" href="https://api.github.com">
203+
209204
<script>
210-
// Dynamically load GitHub stats JavaScript when this page is accessed
211-
document.addEventListener('DOMContentLoaded', function() {
212-
// Load the GitHub stats script dynamically
205+
// Load GitHub stats immediately - no waiting for DOM
206+
(function() {
213207
const script = document.createElement('script');
214208
script.src = '/js/github-stats.js';
209+
script.async = true;
215210
script.onload = function() {
216211
console.log('GitHub stats script loaded successfully');
217212
};
218213
script.onerror = function() {
219214
console.error('Failed to load GitHub stats script');
220215
};
221216
document.head.appendChild(script);
222-
});
217+
})();
223218
</script>
224219

225220

layouts/_default/baseof.html

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,44 @@
424424
text-decoration: underline;
425425
}
426426

427+
/* Navigation Mobile Responsiveness */
428+
@media (max-width: 768px) {
429+
.navbar {
430+
flex-direction: column;
431+
gap: 1rem;
432+
padding: 1rem;
433+
}
434+
435+
.navbar-nav {
436+
gap: 1rem;
437+
flex-wrap: wrap;
438+
justify-content: center;
439+
}
440+
441+
.nav-link {
442+
font-size: 0.9rem;
443+
}
444+
}
445+
446+
@media (max-width: 480px) {
447+
.navbar {
448+
padding: 0.75rem;
449+
}
450+
451+
.navbar-brand {
452+
font-size: 1.3rem;
453+
}
454+
455+
.navbar-nav {
456+
gap: 0.75rem;
457+
}
458+
459+
.nav-link {
460+
font-size: 0.85rem;
461+
padding: 0.25rem;
462+
}
463+
}
464+
427465
/* Responsive adjustments */
428466
@media (max-width: 768px) {
429467
.people-grid.vertical {

static/js/github-stats.js

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
// GitHub Repository Statistics Fetcher
2+
// Fetches real-time star and fork counts from GitHub API
3+
4+
const repositories = [
5+
{ id: 'melody-stats', repo: 'MoatLab/Melody' },
6+
{ id: 'soaralto-stats', repo: 'MoatLab/SoarAlto' },
7+
{ id: 'femu-stats', repo: 'MoatLab/FEMU' },
8+
{ id: 'pond-stats', repo: 'MoatLab/Pond' },
9+
{ id: 'leapio-stats', repo: 'MoatLab/LeapIO' },
10+
{ id: 'ioda-stats', repo: 'MoatLab/IODA-SOSP21-AE' }
11+
];
12+
13+
async function fetchGitHubStats(owner, repo) {
14+
try {
15+
// Use AbortController for timeout control
16+
const controller = new AbortController();
17+
const timeoutId = setTimeout(() => controller.abort(), 3000); // 3 second timeout
18+
19+
const response = await fetch(`https://api.github.com/repos/${owner}/${repo}`, {
20+
headers: {
21+
'Accept': 'application/vnd.github.v3+json',
22+
'X-GitHub-Api-Version': '2022-11-28'
23+
},
24+
signal: controller.signal,
25+
cache: 'force-cache' // Use browser cache when available
26+
});
27+
28+
clearTimeout(timeoutId);
29+
30+
if (!response.ok) {
31+
throw new Error(`HTTP error! status: ${response.status}`);
32+
}
33+
34+
const data = await response.json();
35+
return {
36+
stars: data.stargazers_count,
37+
forks: data.forks_count
38+
};
39+
} catch (error) {
40+
if (error.name === 'AbortError') {
41+
console.warn(`Timeout fetching stats for ${owner}/${repo}`);
42+
} else {
43+
console.warn(`Failed to fetch stats for ${owner}/${repo}:`, error);
44+
}
45+
return null;
46+
}
47+
}
48+
49+
function updateRepoStats(elementId, stats) {
50+
const element = document.getElementById(elementId);
51+
if (!element || !stats) return;
52+
53+
const starsElement = element.querySelector('.repo-stars');
54+
const forksElement = element.querySelector('.repo-forks');
55+
56+
if (starsElement) {
57+
// Remove loading span and set actual number
58+
const loadingSpan = starsElement.querySelector('.loading');
59+
if (loadingSpan) {
60+
loadingSpan.remove();
61+
}
62+
starsElement.appendChild(document.createTextNode(stats.stars.toString()));
63+
}
64+
65+
if (forksElement) {
66+
// Remove loading span and set actual number
67+
const loadingSpan = forksElement.querySelector('.loading');
68+
if (loadingSpan) {
69+
loadingSpan.remove();
70+
}
71+
forksElement.appendChild(document.createTextNode(stats.forks.toString()));
72+
}
73+
74+
// Show the stats with fade-in animation
75+
element.classList.add('loaded');
76+
}
77+
78+
// Cache management
79+
const CACHE_KEY = 'moatlab-github-stats';
80+
const CACHE_DURATION = 10 * 60 * 1000; // 10 minutes
81+
82+
function getCachedStats() {
83+
try {
84+
const cached = localStorage.getItem(CACHE_KEY);
85+
if (!cached) return null;
86+
87+
const { data, timestamp } = JSON.parse(cached);
88+
const isExpired = Date.now() - timestamp > CACHE_DURATION;
89+
90+
return isExpired ? null : data;
91+
} catch (error) {
92+
return null;
93+
}
94+
}
95+
96+
function setCachedStats(stats) {
97+
try {
98+
localStorage.setItem(CACHE_KEY, JSON.stringify({
99+
data: stats,
100+
timestamp: Date.now()
101+
}));
102+
} catch (error) {
103+
console.warn('Failed to cache GitHub stats:', error);
104+
}
105+
}
106+
107+
async function loadAllGitHubStats() {
108+
console.log('Loading GitHub repository statistics...');
109+
110+
// Try to load from cache first for instant display
111+
const cachedStats = getCachedStats();
112+
if (cachedStats) {
113+
console.log('Using cached GitHub stats for instant loading');
114+
Object.entries(cachedStats).forEach(([repo, stats]) => {
115+
const repoConfig = repositories.find(r => r.repo === repo);
116+
if (repoConfig && stats) {
117+
updateRepoStats(repoConfig.id, stats);
118+
}
119+
});
120+
return;
121+
}
122+
123+
// Fetch all repository stats in parallel for faster loading
124+
const promises = repositories.map(async ({ id, repo }) => {
125+
const [owner, repoName] = repo.split('/');
126+
const stats = await fetchGitHubStats(owner, repoName);
127+
128+
if (stats) {
129+
updateRepoStats(id, stats);
130+
console.log(`Updated ${repo}: ${stats.stars} stars, ${stats.forks} forks`);
131+
}
132+
133+
return { repo, stats };
134+
});
135+
136+
const results = await Promise.all(promises);
137+
138+
// Cache the results for faster subsequent loads
139+
const statsToCache = {};
140+
results.forEach(({ repo, stats }) => {
141+
if (stats) {
142+
statsToCache[repo] = stats;
143+
}
144+
});
145+
setCachedStats(statsToCache);
146+
147+
console.log('GitHub stats update completed and cached');
148+
}
149+
150+
// Initialize immediately when script loads
151+
loadAllGitHubStats();

0 commit comments

Comments
 (0)