Skip to content

Commit 571e48d

Browse files
authored
Merge pull request #459 from neural-loop/updates
Various cleanup and updates
2 parents 4ec56e6 + bd77c63 commit 571e48d

36 files changed

Lines changed: 868 additions & 1352 deletions

.github/workflows/main.yml

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,11 @@ on:
44
push:
55
branches: ["main", "staging"]
66

7-
# Standard permissions required for GitHub Pages deployment.
87
permissions:
98
contents: read
109
pages: write
1110
id-token: write
1211

13-
# Environment variables accessible to all jobs and steps.
1412
env:
1513
HUGO_ENV: production
1614
HUGO_VERSION: "0.147.7"
@@ -21,12 +19,11 @@ jobs:
2119
build:
2220
runs-on: ubuntu-24.04
2321
steps:
24-
# 1. Prepare the environment by checking out code and setting up tools.
2522
- name: Checkout repository
2623
uses: actions/checkout@v4
2724
with:
2825
submodules: recursive
29-
fetch-depth: 0 # Needed for Hugo's .Lastmod feature
26+
fetch-depth: 0
3027

3128
- name: Setup Hugo
3229
uses: peaceiris/actions-hugo@v3
@@ -38,23 +35,21 @@ jobs:
3835
uses: actions/setup-go@v5
3936
with:
4037
go-version: ${{ env.GO_VERSION }}
41-
cache: true # Enable Go module caching
38+
cache: false
4239

4340
- name: Setup Node.js
4441
uses: actions/setup-node@v4
4542
with:
4643
node-version: ${{ env.NODE_VERSION }}
47-
cache: 'npm' # Enable caching for npm dependencies
44+
cache: 'npm'
4845

4946
- name: Install npm dependencies
5047
run: npm ci
5148

52-
# 2. Configure GitHub Pages deployment target.
5349
- name: Setup Pages
5450
id: pages
5551
uses: actions/configure-pages@v5
5652

57-
# 3. Cache and run pre-build scripts to generate dynamic content.
5853
- name: Cache and Restore OG Images
5954
uses: actions/cache@v4
6055
with:
@@ -78,9 +73,7 @@ jobs:
7873
env:
7974
PAT_FOR_ISSUES: ${{ secrets.PAT_FOR_ISSUES }}
8075
run: npm run update-mission-board
81-
continue-on-error: true
8276

83-
# 4. Dynamically configure the site's baseURL based on the deployment branch.
8477
- name: Determine Base URL
8578
id: base_url
8679
run: |
@@ -89,27 +82,15 @@ jobs:
8982
elif [[ "${{ github.repository }}" == "open-neuromorphic/open-neuromorphic.github.io" && "${{ github.ref_name }}" == "staging" ]]; then
9083
BASE_URL="https://open-neuromorphic.github.io/staging-preview/"
9184
else
92-
# Fallback for forks or other branches to prevent broken links.
9385
REPO_NAME=$(echo "${{ github.repository }}" | cut -d '/' -f 2)
9486
BASE_URL="https://${{ github.repository_owner }}.github.io/${REPO_NAME}/"
9587
fi
9688
echo "BASE_URL=${BASE_URL}" >> $GITHUB_ENV
9789
echo "Determined baseURL: ${BASE_URL}"
9890
99-
- name: Modify hugo.toml for deployment
100-
run: |
101-
echo "Setting baseURL to ${{ env.BASE_URL }} in hugo.toml"
102-
# Use '|' as a sed delimiter to avoid conflicts with slashes in the URL.
103-
sed -i "s|^baseURL = .*|baseURL = \"${{ env.BASE_URL }}\"|" hugo.toml
104-
echo "--- hugo.toml after modification ---"
105-
cat hugo.toml
106-
echo "------------------------------------"
107-
108-
# 5. Build the static site.
10991
- name: Build Hugo Site
110-
run: npm run hugo-build
92+
run: npm run hugo-build -- --baseURL="${{ env.BASE_URL }}"
11193

112-
# 6. Prepare deployment artifacts, including SEO configurations for non-production sites.
11394
- name: Create .htaccess from template
11495
run: |
11596
cat .github/config/htaccess.template > public/.htaccess
@@ -119,26 +100,21 @@ jobs:
119100
if: env.BASE_URL != 'https://open-neuromorphic.org/'
120101
run: |
121102
echo "Applying noindex rules for baseURL: ${{ env.BASE_URL }}"
122-
# Modify .htaccess to add a noindex header
123103
echo "" >> public/.htaccess
124104
echo "# Rules to prevent indexing on non-production deployments" >> public/.htaccess
125105
echo "Header set X-Robots-Tag \"noindex, nofollow\"" >> public/.htaccess
126-
# Create robots.txt to disallow all crawlers
127106
echo "User-agent: *" > public/robots.txt
128107
echo "Disallow: /" >> public/robots.txt
129108
echo "--- .htaccess final content ---"
130109
cat public/.htaccess
131110
echo "--- robots.txt content ---"
132111
cat public/robots.txt
133112
134-
# 7. Upload the prepared static site as an artifact for the deploy job.
135113
- name: Upload artifact
136114
uses: actions/upload-pages-artifact@v3
137115
with:
138116
path: ./public
139117

140-
# --- DEPLOYMENT JOB ---
141-
# This job runs after the build job is successful.
142118
deploy:
143119
environment:
144120
name: github-pages

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ resources
1212
.hugo_build.lock
1313
jsconfig.json
1414
hugo_stats.json
15-
go.sum
1615
yarn.lock
1716
/content/english/.idea/
1817
.idea

assets/js/main.js

Lines changed: 136 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1-
// assets/js/main.js
2-
// main script
31
(function () {
42
"use strict";
53

6-
// --- Mobile Nav Toggle ---
74
const body = document.body;
85
const navToggler = document.getElementById("nav-toggler");
96
const navOverlay = document.getElementById("mobile-nav-overlay");
@@ -23,14 +20,12 @@
2320
navCloseBtn.addEventListener("click", toggleNav);
2421
}
2522

26-
// Close on Escape key
2723
document.addEventListener('keydown', (e) => {
2824
if (e.key === 'Escape' && body.classList.contains('is-mobile-nav-open')) {
2925
toggleNav();
3026
}
3127
});
3228

33-
// --- Contribution Bubbles for Community Partners ---
3429
const initContributionBubbles = () => {
3530
const section = document.querySelector('.community-partners-section');
3631
const bubbleContainer = document.querySelector('.bubbles-container');
@@ -39,7 +34,6 @@
3934
const partnersWithContributions = Array.from(section.querySelectorAll('.partner-logo-link[data-contributions]'));
4035
if (partnersWithContributions.length === 0) return;
4136

42-
// --- Logic for randomly appearing bubbles ---
4337
const MAX_BUBBLES = 3;
4438
const BUBBLE_LIFETIME = 4000;
4539
const BUBBLE_INTERVAL = 2000;
@@ -102,7 +96,6 @@
10296
setTimeout(() => { recentlyHidden.delete(partner); }, COOLDOWN_PERIOD);
10397
};
10498

105-
// --- NEW: Hover-triggered bubble logic ---
10699
let hoverBubble = document.createElement('div');
107100
hoverBubble.className = 'contribution-bubble';
108101
bubbleContainer.appendChild(hoverBubble);
@@ -111,12 +104,10 @@
111104
partnersWithContributions.forEach(partner => {
112105
partner.addEventListener('mouseenter', () => {
113106
isHovering = true;
114-
// Hide any random bubbles that might be showing
115107
activeBubbles.forEach((bubble, p) => hideBubble(p, bubble));
116108

117109
hoverBubble.innerHTML = setBubbleContent(partner);
118110

119-
// Position calculation must happen *after* content is set
120111
const partnerRect = partner.getBoundingClientRect();
121112
const containerRect = bubbleContainer.getBoundingClientRect();
122113
const left = partnerRect.left - containerRect.left + (partnerRect.width / 2) - (hoverBubble.offsetWidth / 2);
@@ -132,9 +123,7 @@
132123
});
133124
});
134125

135-
// --- Modified interval for random bubbles ---
136126
setInterval(() => {
137-
// Pause random bubbles if user is hovering or max bubbles are already shown
138127
if (isHovering || activeBubbles.size >= MAX_BUBBLES) return;
139128

140129
const rect = section.getBoundingClientRect();
@@ -155,8 +144,6 @@
155144
}, BUBBLE_INTERVAL);
156145
};
157146

158-
159-
// --- Copy Code Block ---
160147
const initCopyCodeButtons = () => {
161148
const codeBlocks = document.querySelectorAll('.code-block-wrapper');
162149
codeBlocks.forEach(wrapper => {
@@ -174,7 +161,7 @@
174161
setTimeout(() => {
175162
button.classList.remove('copied');
176163
button.querySelector('.copy-text').classList.remove('hidden');
177-
button.querySelector('.copied-text').add('hidden');
164+
button.querySelector('.copied-text').classList.add('hidden');
178165
}, 2000);
179166
}).catch(err => {
180167
console.error('Failed to copy text: ', err);
@@ -185,7 +172,6 @@
185172
});
186173
};
187174

188-
// --- OG Image Assets Modal ---
189175
const initOgImageModal = () => {
190176
const modal = document.getElementById('og-image-modal');
191177
const modalContent = document.getElementById('og-modal-content');
@@ -215,8 +201,7 @@
215201
});
216202

217203
window.openOgModal = (imageUrls) => {
218-
modalContent.innerHTML = ''; // Clear previous content
219-
204+
modalContent.innerHTML = '';
220205
if (!imageUrls || imageUrls.length === 0) {
221206
modalContent.innerHTML = '<p class="text-center col-span-full">No social media assets found for this page.</p>';
222207
} else {
@@ -255,21 +240,146 @@
255240
});
256241
};
257242

258-
// Wait for the DOM to be fully loaded to initialize
259-
if (document.readyState === 'loading') {
260-
document.addEventListener('DOMContentLoaded', () => {
261-
initCopyCodeButtons();
262-
initOgImageModal();
263-
if (window.innerWidth > 768) { // Only run on larger screens
264-
initContributionBubbles();
243+
const initMissionBoardFilters = () => {
244+
const filterTags = document.querySelectorAll('.filter-tag');
245+
const clearButton = document.getElementById('clear-filter-btn');
246+
const projectContainers = document.querySelectorAll('.project-container');
247+
const pastContributionsSection = document.getElementById('past-contributions-section');
248+
const projectLinks = document.querySelectorAll('.project-filter-link');
249+
const stickyFilterPanel = document.querySelector('.filter-panel-glow');
250+
const toc = document.getElementById('TableOfContents');
251+
252+
if (filterTags.length === 0 || projectContainers.length === 0) return;
253+
let activeTag = null;
254+
255+
function scrollToElementWithOffset(element) {
256+
if (!element || !stickyFilterPanel) return;
257+
const stickyHeaderBottom = stickyFilterPanel.getBoundingClientRect().bottom;
258+
const elementPosition = element.getBoundingClientRect().top;
259+
const offsetPosition = elementPosition + window.scrollY - stickyHeaderBottom - 20;
260+
261+
window.scrollTo({
262+
top: offsetPosition,
263+
behavior: 'smooth'
264+
});
265+
}
266+
267+
function handleAnchorClick(e) {
268+
const link = e.currentTarget;
269+
const targetId = link.getAttribute('href');
270+
if (targetId && targetId.startsWith('#')) {
271+
e.preventDefault();
272+
const targetElement = document.querySelector(targetId);
273+
if (targetElement) {
274+
scrollToElementWithOffset(targetElement);
275+
history.pushState(null, null, targetId);
276+
}
265277
}
278+
}
279+
280+
projectLinks.forEach(link => {
281+
link.addEventListener('click', (e) => {
282+
if (activeTag) clearFilter();
283+
handleAnchorClick(e);
284+
});
266285
});
267-
} else {
286+
287+
if (toc) {
288+
toc.addEventListener('click', (e) => {
289+
const link = e.target.closest('a');
290+
if (link) handleAnchorClick({ currentTarget: link, preventDefault: () => e.preventDefault() });
291+
});
292+
}
293+
294+
function applyFilter(selectedTag) {
295+
let firstVisibleProject = null;
296+
if (pastContributionsSection) pastContributionsSection.style.display = 'none';
297+
298+
projectContainers.forEach(project => {
299+
const issues = project.querySelectorAll('.issue-item');
300+
let projectHasVisibleIssue = false;
301+
issues.forEach(issue => {
302+
const hasTag = issue.querySelector(`[data-issue-tag="${selectedTag}"]`);
303+
issue.style.display = hasTag ? 'flex' : 'none';
304+
if (hasTag) {
305+
projectHasVisibleIssue = true;
306+
}
307+
});
308+
309+
project.style.display = projectHasVisibleIssue ? 'block' : 'none';
310+
311+
if (projectHasVisibleIssue && !firstVisibleProject) {
312+
firstVisibleProject = project;
313+
}
314+
});
315+
316+
return firstVisibleProject;
317+
}
318+
319+
function clearFilter() {
320+
activeTag = null;
321+
updateTagStyles(null);
322+
if(clearButton) clearButton.classList.add('hidden');
323+
if (pastContributionsSection) pastContributionsSection.style.display = 'block';
324+
projectContainers.forEach(project => {
325+
project.style.display = 'block';
326+
project.querySelectorAll('.issue-item').forEach(issue => {
327+
issue.style.display = 'flex';
328+
});
329+
});
330+
}
331+
332+
function updateTagStyles(selectedTag) {
333+
filterTags.forEach(tag => {
334+
tag.classList.toggle('is-active', tag.dataset.tag === selectedTag);
335+
});
336+
}
337+
338+
filterTags.forEach(tag => {
339+
tag.addEventListener('click', () => {
340+
const selectedTag = tag.dataset.tag;
341+
if (activeTag === selectedTag) {
342+
clearFilter();
343+
return;
344+
}
345+
activeTag = selectedTag;
346+
updateTagStyles(selectedTag);
347+
if(clearButton) clearButton.classList.remove('hidden');
348+
const firstVisibleElement = applyFilter(selectedTag);
349+
if (firstVisibleElement) {
350+
scrollToElementWithOffset(firstVisibleElement);
351+
}
352+
});
353+
});
354+
355+
if(clearButton) {
356+
clearButton.addEventListener('click', clearFilter);
357+
}
358+
359+
const initialHash = window.location.hash;
360+
if (initialHash) {
361+
setTimeout(() => {
362+
const targetElement = document.querySelector(initialHash);
363+
if (targetElement) {
364+
scrollToElementWithOffset(targetElement);
365+
}
366+
}, 100);
367+
}
368+
};
369+
370+
const initAll = () => {
268371
initCopyCodeButtons();
269372
initOgImageModal();
270-
if (window.innerWidth > 768) { // Only run on larger screens
373+
initMissionBoardFilters();
374+
if (window.innerWidth > 768) {
271375
initContributionBubbles();
272376
}
377+
};
378+
379+
if (document.readyState === 'loading') {
380+
document.addEventListener('DOMContentLoaded', initAll);
381+
} else {
382+
initAll();
273383
}
274384

275385
})();

0 commit comments

Comments
 (0)