Atom feed for our EEG site

more

Changed files
+149 -225
+149 -225
index.html
···
overflow: hidden;
}
+
.feed-item-left {
+
display: flex;
+
align-items: center;
+
margin-right: 10px;
+
}
+
.feed-item-date {
font-family: 'JetBrains Mono', monospace;
font-size: 0.75rem;
···
}
.feed-item-title {
-
flex: 1;
font-size: 0.95rem;
font-weight: 400;
-
white-space: nowrap;
-
overflow: hidden;
-
text-overflow: ellipsis;
-
margin-right: 15px;
+
margin-bottom: 3px;
}
.feed-item-title a {
···
color: var(--accent-color);
}
-
.feed-item-preview {
-
flex: 2;
-
white-space: nowrap;
+
.feed-item-content-wrapper {
+
flex: 1;
overflow: hidden;
-
text-overflow: ellipsis;
+
}
+
+
.feed-item-preview {
color: var(--text-muted);
font-size: 0.85rem;
-
margin-right: 15px;
+
overflow: hidden;
+
text-overflow: ellipsis;
+
white-space: nowrap;
+
transition: all 0.3s ease;
}
.feed-item-actions {
···
transition: all 0.3s ease;
}
-
.feed-item:hover,
-
.feed-item.content-open {
+
.feed-item:hover {
border-left-color: var(--accent-color);
background-color: rgba(77, 250, 123, 0.03);
}
···
}
-
.feed-item-external-links {
+
.feed-item:hover .feed-item-preview {
+
white-space: normal;
+
line-height: 1.4;
+
max-height: none;
+
}
+
+
.preview-links,
+
.preview-references {
font-size: 0.8rem;
-
padding: 5px 15px;
-
border-top: 1px dashed var(--border-color);
display: none;
flex-wrap: wrap;
align-items: center;
-
gap: 5px;
-
background-color: rgba(77, 250, 123, 0.02);
+
gap: 8px;
+
margin-top: 5px;
+
padding-top: 5px;
+
border-top: 1px dotted var(--border-color);
+
}
+
+
.external-link-item[title*="github.com"] {
+
background-color: rgba(77, 180, 128, 0.08);
+
color: var(--accent-alt);
+
}
+
+
.feed-item:hover .preview-links,
+
.feed-item:hover .preview-references {
+
display: flex;
}
.reference-header {
···
item.addEventListener('mouseenter', () => {
// Close all sections in previously hovered item
if (currentHoveredItem && currentHoveredItem !== item) {
-
// Don't close manually opened content (with the button)
-
if (!currentHoveredItem.classList.contains('content-open')) {
-
const prevContent = currentHoveredItem.querySelector('.feed-item-content');
-
if (prevContent) {
-
prevContent.style.display = 'none';
-
-
// Update button state
-
const prevButton = currentHoveredItem.querySelector('.read-more-btn');
-
if (prevButton) {
-
prevButton.textContent = '📄';
-
prevButton.title = 'Show content';
-
}
-
}
-
}
+
// Remove this section - we no longer show the full content
-
// Don't close manually opened external links
-
if (!currentHoveredItem.classList.contains('links-open')) {
-
const prevLinks = currentHoveredItem.querySelector('.feed-item-external-links');
-
if (prevLinks) {
-
prevLinks.style.display = 'none';
-
-
// Update button state
-
const prevButton = currentHoveredItem.querySelector('.external-links-toggle');
-
if (prevButton) {
-
prevButton.textContent = '🌐';
-
prevButton.title = 'Show external links';
-
}
-
}
-
}
+
// No need to close preview content now since it's controlled by CSS hover
-
// Don't close manually opened references
-
if (!currentHoveredItem.classList.contains('refs-to-open')) {
-
const prevRefsTo = currentHoveredItem.querySelector('.references-to');
-
if (prevRefsTo) {
-
prevRefsTo.style.display = 'none';
-
-
// Update button state
-
const prevButton = currentHoveredItem.querySelector('.references-to-toggle');
-
if (prevButton) {
-
prevButton.textContent = '➡️';
-
prevButton.title = 'Show references to other posts';
-
}
-
}
-
}
-
-
if (!currentHoveredItem.classList.contains('refs-by-open')) {
-
const prevRefsBy = currentHoveredItem.querySelector('.references-by');
-
if (prevRefsBy) {
-
prevRefsBy.style.display = 'none';
-
-
// Update button state
-
const prevButton = currentHoveredItem.querySelector('.references-by-toggle');
-
if (prevButton) {
-
prevButton.textContent = '⬅️';
-
prevButton.title = 'Show posts referencing this one';
-
}
-
}
-
}
+
// References are now controlled by CSS hover
}
// Set this as current hovered item
currentHoveredItem = item;
-
// Show content on hover (unless manually closed)
-
const content = item.querySelector('.feed-item-content');
-
if (content) {
-
content.style.display = 'block';
-
-
// Update button state
-
const button = item.querySelector('.read-more-btn');
-
if (button) {
-
button.textContent = '📕';
-
button.title = 'Hide content';
-
}
-
}
+
// Remove this section - we no longer show the full content
-
// Show external links if available
-
const externalLinks = item.querySelector('.feed-item-external-links');
-
if (externalLinks) {
-
externalLinks.style.display = 'flex';
-
-
// Update button state
-
const externalButton = item.querySelector('.external-links-toggle');
-
if (externalButton) {
-
externalButton.textContent = '🌐';
-
externalButton.title = 'Hide external links';
-
}
-
}
+
// Preview content is shown automatically by CSS on hover
});
});
}
···
});
}
-
// Function to get first line of text
-
function getFirstLine(html) {
+
// Function to get a paragraph preview of text
+
function getTextPreview(html, maxLength = 300) {
// Create a temporary div to parse HTML
const tempDiv = document.createElement('div');
tempDiv.innerHTML = html;
-
// Extract text content
+
// Extract text content and remove extra whitespace
const text = tempDiv.textContent || '';
+
const cleanText = text.replace(/\s+/g, ' ').trim();
-
// Get first line (up to first period or newline, whichever comes first)
-
const firstPeriod = text.indexOf('.');
-
const firstNewline = text.indexOf('\n');
-
-
let endIndex;
-
if (firstPeriod !== -1 && firstNewline !== -1) {
-
endIndex = Math.min(firstPeriod, firstNewline);
-
} else if (firstPeriod !== -1) {
-
endIndex = firstPeriod;
-
} else if (firstNewline !== -1) {
-
endIndex = firstNewline;
-
} else {
-
// If no period or newline, take first 80 chars
-
endIndex = Math.min(text.length, 80);
+
// Get a reasonable preview length (about a paragraph)
+
if (cleanText.length <= maxLength) {
+
return cleanText;
}
-
return text.substring(0, endIndex + 1).trim();
-
}
-
-
// Function to toggle content visibility
-
function toggleContent(articleId) {
-
const article = document.getElementById(articleId);
-
const content = article.querySelector('.feed-item-content');
-
const button = article.querySelector('.read-more-btn');
+
// Try to find a good break point
+
let endIndex = maxLength;
-
if (content.style.display === 'block') {
-
content.style.display = 'none';
-
button.textContent = '📄';
-
button.title = 'Show content';
-
-
// Remove forced hover state
-
article.classList.remove('content-open');
+
// Look for the last sentence break within our limit
+
const lastPeriod = cleanText.lastIndexOf('.', maxLength);
+
if (lastPeriod > maxLength / 2) {
+
endIndex = lastPeriod + 1;
} else {
-
content.style.display = 'block';
-
button.textContent = '📕';
-
button.title = 'Hide content';
-
-
// Add forced hover state
-
article.classList.add('content-open');
-
}
-
}
-
-
// Function to toggle external links visibility
-
function toggleExternalLinks(articleId) {
-
const article = document.getElementById(articleId);
-
const linksContainer = article.querySelector('.feed-item-external-links');
-
const button = article.querySelector('.external-links-toggle');
-
-
if (!linksContainer || !button) {
-
console.error(`External links container or button for ${articleId} not found`);
-
return;
+
// Look for the last space to avoid cutting words
+
const lastSpace = cleanText.lastIndexOf(' ', maxLength);
+
if (lastSpace > 0) {
+
endIndex = lastSpace;
+
}
}
-
if (linksContainer.style.display === 'flex') {
-
linksContainer.style.display = 'none';
-
button.textContent = '🌐';
-
button.title = 'Show external links';
-
article.classList.remove('links-open');
-
} else {
-
linksContainer.style.display = 'flex';
-
button.textContent = '🌐';
-
button.title = 'Hide external links';
-
article.classList.add('links-open');
-
}
+
return cleanText.substring(0, endIndex) + '...';
}
-
// Function to toggle references visibility
-
function toggleReferences(articleId, type) {
-
const article = document.getElementById(articleId);
-
const referencesContainer = article.querySelector(`.references-${type}`);
-
const button = article.querySelector(`.references-${type}-toggle`);
+
// Function to get first line for preview in post listing
+
function getFirstLine(html) {
+
// Create a temporary div to parse HTML
+
const tempDiv = document.createElement('div');
+
tempDiv.innerHTML = html;
-
if (!referencesContainer || !button) {
-
console.error(`References container or button for ${articleId} not found`);
-
return;
-
}
+
// Extract text content
+
const text = tempDiv.textContent || '';
+
const cleanText = text.replace(/\s+/g, ' ').trim();
-
if (referencesContainer.style.display === 'block') {
-
referencesContainer.style.display = 'none';
-
button.textContent = type === 'to' ? '➡️' : '⬅️';
-
button.title = type === 'to' ? 'Show references to other posts' : 'Show posts referencing this one';
-
article.classList.remove(`refs-${type}-open`);
+
// Get first sentence, or about 80 chars
+
const firstPeriod = cleanText.indexOf('.');
+
+
let endIndex;
+
if (firstPeriod !== -1 && firstPeriod < 100) {
+
endIndex = firstPeriod;
} else {
-
referencesContainer.style.display = 'block';
-
button.textContent = type === 'to' ? '➡️' : '⬅️';
-
button.title = type === 'to' ? 'Hide references to other posts' : 'Hide posts referencing this one';
-
article.classList.add(`refs-${type}-open`);
+
// If no suitable period, take first 80 chars
+
endIndex = Math.min(cleanText.length, 80);
+
// Look for the last space to avoid cutting words
+
const lastSpace = cleanText.lastIndexOf(' ', endIndex);
+
if (lastSpace > endIndex / 2) {
+
endIndex = lastSpace;
+
}
}
+
+
return cleanText.substring(0, endIndex + 1).trim();
}
+
+
// Function removed - we no longer toggle full content
+
+
// Removed the external links toggle function as it's no longer needed
+
+
// Reference toggle function removed - references are now shown with CSS on hover
try {
// Fetch the Atom feed and threads data in parallel
···
.replace(/\n/g, '<br>');
}
-
// Get the first line for preview
+
// Get the first line and paragraph preview
const firstLine = getFirstLine(contentHtml);
+
const textPreview = getTextPreview(contentHtml);
// Store the entry data
entriesById[id] = {
···
link,
contentHtml,
firstLine,
+
textPreview,
published,
author,
source,
···
entriesHTML += `
<article id="${entry.articleId}" class="feed-item" ${dateAttr}>
<div class="feed-item-row">
+
<div class="feed-item-left">
+
<a href="${entry.link}" target="_blank" class="external-link" title="Open original post">🔗</a>
+
</div>
<div class="feed-item-date">${formatDate(entry.published)}</div>
<div class="feed-item-author">${entry.author}</div>
-
<div class="feed-item-title"><a href="${entry.link}" target="_blank">${entry.title}</a></div>
-
<div class="feed-item-preview">${entry.firstLine}</div>
-
<div class="feed-item-actions">
-
<button class="read-more-btn" onclick="toggleContent('${entry.articleId}')" title="Show/hide content">📄</button>
-
<a href="${entry.link}" target="_blank" class="external-link" title="Open original post">🔗</a>
-
${entry.externalLinks && entry.externalLinks.length > 0 ?
-
`<button class="external-links-toggle" onclick="toggleExternalLinks('${entry.articleId}')" title="Show/hide external links">🌐</button>` : ''}
-
${entry.referencesTo && entry.referencesTo.length > 0 ?
-
`<button class="references-to-toggle references-toggle" onclick="toggleReferences('${entry.articleId}', 'to')" title="Show/hide references to other posts">➡️</button>` : ''}
-
${entry.referencedBy && entry.referencedBy.length > 0 ?
-
`<button class="references-by-toggle references-toggle" onclick="toggleReferences('${entry.articleId}', 'by')" title="Show/hide posts referencing this one">⬅️</button>` : ''}
-
</div>
-
</div>
-
<div class="feed-item-content">${entry.contentHtml}</div>
-
-
${entry.externalLinks && entry.externalLinks.length > 0 ? `
-
<div class="feed-item-external-links">
-
<span class="external-links-label">External links:</span>
-
${entry.externalLinks.map(link => `<a href="${link.url}" target="_blank" class="external-link-item" title="${link.url}">${new URL(link.url).hostname}</a>`).join(', ')}
-
</div>
-
` : ''}
-
-
${entry.referencesTo && entry.referencesTo.length > 0 ? `
-
<div class="references-container references-to" style="display: none;">
-
<div class="reference-header">References to other posts:</div>
-
${entry.referencesTo.map(ref => `
-
<div class="reference-item">
-
<span class="reference-indicator">→</span>
-
<a href="${ref.link}" target="_blank" class="reference-link">${ref.title}</a>
-
<span class="reference-author"> by ${ref.author}</span>
+
<div class="feed-item-content-wrapper">
+
<div class="feed-item-title"><a href="${entry.link}" target="_blank">${entry.title}</a></div>
+
<div class="feed-item-preview">${entry.textPreview}</div>
+
+
${entry.externalLinks && entry.externalLinks.length > 0 ? `
+
<div class="preview-links">
+
<span class="external-links-label">External links:</span>
+
${entry.externalLinks.map(link => {
+
const url = new URL(link.url);
+
let displayText = url.hostname.replace('www.', '');
+
+
// Special handling for GitHub links
+
if (url.hostname === 'github.com' || url.hostname === 'gist.github.com') {
+
// Extract the parts from pathname (remove leading slash)
+
const parts = url.pathname.substring(1).split('/').filter(part => part);
+
if (parts.length >= 2) {
+
displayText = `github:${parts[0]}/${parts[1]}`;
+
}
+
}
+
+
// Special handling for Wikipedia links
+
if (url.hostname === 'en.wikipedia.org' || url.hostname === 'wikipedia.org' || url.hostname.endsWith('.wikipedia.org')) {
+
const titlePart = url.pathname.split('/').pop();
+
if (titlePart) {
+
const title = decodeURIComponent(titlePart).replace(/_/g, ' ');
+
displayText = `wikipedia:${title}`;
+
}
+
}
+
+
return `<a href="${link.url}" target="_blank" class="external-link-item" title="${link.url}">${displayText}</a>`;
+
}).join(', ')}
+
</div>
+
` : ''}
+
+
${entry.referencesTo && entry.referencesTo.length > 0 ? `
+
<div class="preview-references">
+
<span class="external-links-label">References:</span>
+
${entry.referencesTo.map(ref => `
+
<a href="${ref.link}" target="_blank" class="external-link-item" title="${ref.title} by ${ref.author}">→ ${ref.title}</a>
+
`).join(', ')}
</div>
-
`).join('')}
-
</div>
-
` : ''}
-
-
${entry.referencedBy && entry.referencedBy.length > 0 ? `
-
<div class="references-container references-by" style="display: none;">
-
<div class="reference-header">Posts referencing this one:</div>
-
${entry.referencedBy.map(ref => `
-
<div class="reference-item">
-
<span class="reference-indicator">←</span>
-
<a href="${ref.link}" target="_blank" class="reference-link">${ref.title}</a>
-
<span class="reference-author"> by ${ref.author}</span>
+
` : ''}
+
+
${entry.referencedBy && entry.referencedBy.length > 0 ? `
+
<div class="preview-references">
+
<span class="external-links-label">Referenced by:</span>
+
${entry.referencedBy.map(ref => `
+
<a href="${ref.link}" target="_blank" class="external-link-item" title="${ref.title} by ${ref.author}">← ${ref.title}</a>
+
`).join(', ')}
</div>
-
`).join('')}
+
` : ''}
+
</div>
</div>
-
` : ''}
</article>
`;
···
// Update sources count
sourceCountElement.textContent = sources.size;
-
// Add the toggle functions to the global scope
-
window.toggleContent = toggleContent;
-
window.toggleExternalLinks = toggleExternalLinks;
-
window.toggleReferences = toggleReferences;
+
// No toggle functions needed anymore
// Build timeline sidebar
const timelineSidebar = document.getElementById('timeline-sidebar');