···
983
+
/* Styles for filter container */
984
+
.filter-container {
986
+
align-items: center;
987
+
margin-bottom: 10px;
989
+
background-color: var(--card-bg);
990
+
border: 1px solid var(--border-color);
991
+
border-radius: 4px;
992
+
align-self: flex-end;
998
+
font-family: 'JetBrains Mono', monospace;
999
+
color: var(--accent-color);
1000
+
font-size: 0.8rem;
1001
+
margin-right: 10px;
1010
+
position: relative;
1012
+
align-items: center;
1014
+
user-select: none;
1017
+
.filter-checkbox {
1018
+
position: absolute;
1024
+
.checkbox-custom {
1025
+
position: relative;
1026
+
display: inline-block;
1029
+
background-color: rgba(77, 250, 123, 0.05);
1030
+
border: 1px solid var(--accent-alt);
1031
+
border-radius: 3px;
1032
+
margin-right: 6px;
1033
+
transition: all 0.2s ease;
1036
+
.filter-checkbox:checked + .checkbox-custom::after {
1038
+
position: absolute;
1043
+
border: solid var(--accent-color);
1044
+
border-width: 0 2px 2px 0;
1045
+
transform: rotate(45deg);
1048
+
.filter-checkbox:checked + .checkbox-custom {
1049
+
background-color: rgba(77, 250, 123, 0.15);
1050
+
border-color: var(--accent-color);
1054
+
font-size: 0.8rem;
1055
+
color: var(--text-color);
1058
+
.filter-option:hover .checkbox-custom {
1059
+
background-color: rgba(77, 250, 123, 0.1);
1060
+
border-color: var(--accent-color);
1065
+
justify-content: flex-end;
1066
+
margin-bottom: 10px;
@media (max-width: 600px) {
···
padding-left: 50px; /* Match the sidebar width on small screens */
1119
+
/* Hide green dot indicators on mobile to prevent text overlap */
1120
+
.timeline-year::after,
1121
+
.timeline-month::after {
1125
+
/* Also hide the connecting line on mobile */
1126
+
.timeline-year::before,
1127
+
.timeline-month::before {
···
<p class="loading-text">Growing Content...</p>
<div id="feed-items" class="tab-content active feed-container" data-tab="posts"></div>
1059
-
<div id="link-items" class="tab-content feed-container" data-tab="links"></div>
1158
+
<div class="tab-content" data-tab="links">
1159
+
<div class="links-header">
1160
+
<div class="filter-container">
1161
+
<div class="filter-title">Filter:</div>
1162
+
<div class="filter-options">
1163
+
<label class="filter-option">
1164
+
<input type="checkbox" id="filter-papers" class="filter-checkbox" data-filter="academic">
1165
+
<span class="checkbox-custom"></span>
1166
+
<span class="filter-label">Papers</span>
1168
+
<label class="filter-option">
1169
+
<input type="checkbox" id="filter-videos" class="filter-checkbox" data-filter="youtube">
1170
+
<span class="checkbox-custom"></span>
1171
+
<span class="filter-label">Videos</span>
1176
+
<div id="link-items" class="feed-container"></div>
<div id="people-items" class="tab-content" data-tab="people">
<h2 class="people-header">EEG Sources</h2>
<div class="people-container"></div>
···
2046
+
// Academic paper detection - PDF files and academic domains
2048
+
link.url.toLowerCase().endsWith('.pdf') ||
2049
+
url.hostname.includes('arxiv.org') ||
2050
+
url.hostname.includes('nature.com') ||
2051
+
url.hostname.includes('science.org') ||
2052
+
url.hostname.includes('mdpi.com') ||
2053
+
url.hostname.includes('doi.org')
2055
+
// Set display text based on source
2056
+
if (url.hostname.includes('arxiv.org')) {
2057
+
// Try to extract arXiv ID
2058
+
const arxivIdMatch = url.pathname.match(/\d+\.\d+/);
2059
+
if (arxivIdMatch) {
2060
+
displayText = `<img src="solid-book-open.svg" width="14" height="14" style="vertical-align: middle; margin-right: 4px;"> ${arxivIdMatch[0]}`;
2062
+
displayText = `<img src="solid-book-open.svg" width="14" height="14" style="vertical-align: middle; margin-right: 4px;"> Paper`;
2064
+
} else if (url.hostname.includes('nature.com')) {
2065
+
displayText = `<img src="solid-book-open.svg" width="14" height="14" style="vertical-align: middle; margin-right: 4px;"> Paper`;
2066
+
} else if (url.hostname.includes('science.org')) {
2067
+
displayText = `<img src="solid-book-open.svg" width="14" height="14" style="vertical-align: middle; margin-right: 4px;"> Paper`;
2068
+
} else if (url.hostname.includes('mdpi.com')) {
2069
+
displayText = `<img src="solid-book-open.svg" width="14" height="14" style="vertical-align: middle; margin-right: 4px;"> Paper`;
2070
+
} else if (link.url.toLowerCase().endsWith('.pdf')) {
2071
+
// For direct PDF links, try to get a meaningful filename
2072
+
const pathParts = url.pathname.split('/');
2073
+
const filename = pathParts[pathParts.length - 1];
2075
+
displayText = `<img src="solid-book-open.svg" width="13https://www.blogger.com/feeds/19062127/posts/default" height="14" style="vertical-align: middle; margin-right: 4px;"> ${decodeURIComponent(filename)}`;
2077
+
displayText = `<img src="solid-book-open.svg" width="14" height="14" style="vertical-align: middle; margin-right: 4px;"> Document`;
// Determine link type for styling and future reference
if (url.hostname.includes('github')) linkType = 'github';
else if (url.hostname.includes('wikipedia')) linkType = 'wikipedia';
else if (url.hostname === 'twitter.com' || url.hostname === 'x.com') linkType = 'twitter';
else if (url.hostname.includes('linkedin.com')) linkType = 'linkedin';
1934
-
else if (url.hostname.includes('youtube.com') || url.hostname === 'youtu.be') linkType = 'youtube';
2089
+
url.hostname.includes('youtube.com') ||
2090
+
url.hostname === 'youtu.be' ||
2091
+
url.hostname === 'watch.eeg.cl.cam.ac.uk' ||
2092
+
url.hostname === 'crank.recoil.org' ||
2093
+
url.hostname === 'watch.ocaml.org'
2094
+
) linkType = 'youtube';
else if (url.hostname.includes('medium.com')) linkType = 'medium';
else if (url.hostname.includes('stackoverflow.com')) linkType = 'stackoverflow';
else if (url.hostname === 'dev.to') linkType = 'dev';
···
else if (url.hostname.includes('news.ycombinator.com')) linkType = 'hackernews';
else if (url.hostname === 'bsky.app' || url.hostname === 'bsky.social') linkType = 'bluesky';
else if (url.hostname === 'ocaml.org' && url.pathname.startsWith('/p/')) linkType = 'ocaml';
2103
+
link.url.toLowerCase().endsWith('.pdf') ||
2104
+
url.hostname.includes('arxiv.org') ||
2105
+
url.hostname.includes('nature.com') ||
2106
+
url.hostname.includes('science.org') ||
2107
+
url.hostname.includes('mdpi.com')
2108
+
) linkType = 'academic';
return `<a href="${link.url}" target="_blank" class="external-link-item" title="${link.url}" data-link-type="${linkType}">${displayText}</a>`;
···
if (parts.length >= 2 && parts[0] === 'in') {
2230
-
} else if (url.hostname.includes('youtube.com') || url.hostname === 'youtu.be') {
2398
+
url.hostname.includes('youtube.com') ||
2399
+
url.hostname === 'youtu.be' ||
2400
+
url.hostname === 'watch.eeg.cl.cam.ac.uk' ||
2401
+
url.hostname === 'crank.recoil.org' ||
2402
+
url.hostname === 'watch.ocaml.org'
iconPath = 'brands-youtube.svg';
2232
-
displayText = 'YouTube Video';
2406
+
// Custom display text for specific video platforms
2407
+
if (url.hostname.includes('youtube.com') || url.hostname === 'youtu.be') {
2408
+
displayText = 'YouTube Video';
2409
+
} else if (url.hostname === 'watch.eeg.cl.cam.ac.uk') {
2410
+
displayText = 'EEG Video';
2411
+
} else if (url.hostname === 'crank.recoil.org') {
2412
+
displayText = 'Crank Video';
2413
+
} else if (url.hostname === 'watch.ocaml.org') {
2414
+
displayText = 'OCaml Video';
2416
+
displayText = 'Video';
} else if (url.hostname === 'ocaml.org' && url.pathname.startsWith('/p/')) {
const parts = url.pathname.substring(1).split('/').filter(part => part);
···
displayText = `@${parts[0]}`;
2482
+
// Academic paper detection - PDF files and academic domains
2484
+
link.url.toLowerCase().endsWith('.pdf') ||
2485
+
url.hostname.includes('arxiv.org') ||
2486
+
url.hostname.includes('nature.com') ||
2487
+
url.hostname.includes('science.org') ||
2488
+
url.hostname.includes('mdpi.com') ||
2489
+
url.hostname.includes('doi.org')
2491
+
iconPath = 'solid-book-open.svg';
2493
+
// Set display text based on source
2494
+
if (url.hostname.includes('arxiv.org')) {
2495
+
// Try to extract arXiv ID
2496
+
const arxivIdMatch = url.pathname.match(/\d+\.\d+/);
2497
+
if (arxivIdMatch) {
2498
+
displayText = arxivIdMatch[0];
2500
+
displayText = 'Paper';
2502
+
} else if (url.hostname.includes('nature.com')) {
2503
+
displayText = 'Paper';
2504
+
} else if (url.hostname.includes('science.org')) {
2505
+
displayText = 'Paper';
2506
+
} else if (url.hostname.includes('mdpi.com')) {
2507
+
displayText = 'Paper';
2508
+
} else if (link.url.toLowerCase().endsWith('.pdf')) {
2509
+
// For direct PDF links, try to get a meaningful filename
2510
+
const pathParts = url.pathname.split('/');
2511
+
const filename = pathParts[pathParts.length - 1];
2513
+
displayText = decodeURIComponent(filename);
2515
+
displayText = 'Document';
···
2673
+
// Setup link filtering based on checkboxes
2674
+
setupLinkFilters();
2676
+
// Function to handle link filtering
2677
+
function setupLinkFilters() {
2678
+
const filterCheckboxes = document.querySelectorAll('.filter-checkbox');
2680
+
filterCheckboxes.forEach(checkbox => {
2681
+
checkbox.addEventListener('change', updateFilter);
2684
+
function updateFilter() {
2685
+
// Get all checked filters
2686
+
const activeFilters = Array.from(document.querySelectorAll('.filter-checkbox:checked'))
2687
+
.map(checkbox => checkbox.getAttribute('data-filter'));
2689
+
// Get all link items
2690
+
const allLinkItems = document.querySelectorAll('.link-item');
2691
+
const monthYearHeaders = document.querySelectorAll('.month-year-header');
2693
+
// Show all items if no filters are selected
2694
+
if (activeFilters.length === 0) {
2695
+
allLinkItems.forEach(item => {
2696
+
item.style.display = '';
2698
+
monthYearHeaders.forEach(header => {
2699
+
header.style.display = '';
2704
+
// Track visible items per month/year
2705
+
const visibleByMonthYear = new Map();
2707
+
// Process all items
2708
+
allLinkItems.forEach(item => {
2709
+
const linkUrl = item.querySelector('.link-item-url');
2710
+
let shouldShow = false;
2712
+
// Papers filter - check for PDF icon
2713
+
if (activeFilters.includes('academic')) {
2714
+
const hasPdfIcon = linkUrl && (
2715
+
linkUrl.innerHTML.includes('solid-book-open.svg') ||
2716
+
(linkUrl.getAttribute('data-link-type') === 'academic')
2718
+
if (hasPdfIcon) shouldShow = true;
2721
+
// Videos filter - check for YouTube icon or video domains
2722
+
if (activeFilters.includes('youtube')) {
2723
+
const hasYoutubeIcon = linkUrl && (
2724
+
linkUrl.innerHTML.includes('brands-youtube.svg') ||
2725
+
(linkUrl.getAttribute('data-link-type') === 'youtube')
2728
+
// Also check for specific video site URLs
2729
+
const url = linkUrl?.getAttribute('href');
2730
+
const isVideoSite = url && (
2731
+
url.includes('youtube.com') ||
2732
+
url.includes('youtu.be') ||
2733
+
url.includes('watch.eeg.cl.cam.ac.uk') ||
2734
+
url.includes('crank.recoil.org') ||
2735
+
url.includes('watch.ocaml.org')
2738
+
if (hasYoutubeIcon || isVideoSite) shouldShow = true;
2742
+
item.style.display = shouldShow ? '' : 'none';
2744
+
// Track visible items by month/year
2746
+
const year = item.getAttribute('data-year');
2747
+
const month = item.getAttribute('data-month');
2748
+
const key = `${year}-${month}`;
2749
+
visibleByMonthYear.set(key, (visibleByMonthYear.get(key) || 0) + 1);
2753
+
// Hide month-year headers with no visible items
2754
+
monthYearHeaders.forEach(header => {
2755
+
const year = header.getAttribute('data-year');
2756
+
const month = header.getAttribute('data-month');
2757
+
const key = `${year}-${month}`;
2759
+
if (visibleByMonthYear.has(key)) {
2760
+
header.style.display = ''; // Show if has visible items
2762
+
header.style.display = 'none'; // Hide if no visible items
2766
+
// Update timeline sidebar to match visible items
2767
+
const timelineYears = document.querySelectorAll('.timeline-year');
2768
+
const timelineMonths = document.querySelectorAll('.timeline-month');
2770
+
// First get all years that have visible items
2771
+
const visibleYears = new Set();
2772
+
visibleByMonthYear.forEach((count, key) => {
2773
+
const [year] = key.split('-');
2774
+
visibleYears.add(year);
2777
+
// Hide years without visible items
2778
+
if (activeFilters.length > 0) {
2779
+
timelineYears.forEach(yearEl => {
2780
+
const year = yearEl.getAttribute('data-year');
2781
+
yearEl.style.display = visibleYears.has(year) ? '' : 'none';
2784
+
// Hide months without visible items
2785
+
timelineMonths.forEach(monthEl => {
2786
+
const year = monthEl.getAttribute('data-year');
2787
+
const month = monthEl.getAttribute('data-month');
2788
+
const key = `${year}-${month}`;
2789
+
monthEl.style.display = visibleByMonthYear.has(key) ? '' : 'none';
2792
+
// Show all timeline elements when no filters
2793
+
timelineYears.forEach(yearEl => yearEl.style.display = '');
2794
+
timelineMonths.forEach(monthEl => monthEl.style.display = '');
// Make timeline items clickable to scroll to relevant posts or links
document.querySelectorAll('.timeline-year, .timeline-month').forEach(item => {