Atom feed for our EEG site

sync

Changed files
+112 -125
+112 -125
index.html
···
justify-content: space-between;
align-items: center;
width: 100%;
-
max-width: 1000px;
margin: 0 auto;
}
···
.content {
width: 100%;
-
max-width: 850px;
}
.feed-item {
background-color: var(--card-bg);
border: 1px solid var(--border-color);
-
border-radius: 6px;
-
margin-bottom: 15px;
overflow: hidden;
-
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
-
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.feed-item:hover {
-
transform: translateY(-2px);
-
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
-
.feed-item-header {
-
padding: 10px 15px;
-
border-bottom: 1px solid var(--border-color);
-
background-color: rgba(77, 250, 123, 0.05);
}
.feed-item-title {
-
font-size: 1.15rem;
-
font-weight: 500;
-
margin-bottom: 3px;
-
line-height: 1.3;
}
.feed-item-title a {
···
color: var(--accent-color);
}
-
.feed-item-meta {
-
display: flex;
-
justify-content: space-between;
-
align-items: center;
-
font-size: 0.8rem;
-
color: var(--text-muted);
-
}
-
-
.feed-item-source {
-
font-family: 'JetBrains Mono', monospace;
-
color: var(--accent-alt);
-
}
-
.feed-item-preview {
-
padding: 10px 15px;
-
line-height: 1.5;
-
border-bottom: 1px solid var(--border-color);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.feed-item-content {
padding: 15px;
line-height: 1.6;
display: none;
}
.feed-item-content img {
···
color: var(--text-muted);
}
-
.feed-item-actions {
-
padding: 6px 15px;
-
display: flex;
-
justify-content: space-between;
-
align-items: center;
-
}
-
-
.read-more {
background-color: transparent;
-
border: 1px solid var(--accent-color);
color: var(--accent-color);
-
padding: 5px 10px;
-
border-radius: 4px;
cursor: pointer;
font-family: 'JetBrains Mono', monospace;
font-size: 0.8rem;
transition: all 0.2s ease;
display: inline-flex;
align-items: center;
}
-
.read-more:hover {
-
background-color: var(--accent-color);
-
color: var(--bg-color);
}
-
.read-more::after {
-
content: '⌄';
-
margin-left: 5px;
-
font-size: 1.2em;
-
line-height: 0.8;
-
transition: transform 0.2s ease;
}
-
.read-more.active::after {
-
transform: rotate(180deg);
}
#loading {
···
font-family: 'JetBrains Mono', monospace;
}
-
.timestamp {
-
font-family: 'JetBrains Mono', monospace;
-
font-size: 0.75rem;
-
}
-
-
.external-link {
-
display: inline-flex;
-
align-items: center;
-
color: var(--text-muted);
-
text-decoration: none;
-
font-size: 0.8rem;
-
transition: color 0.2s ease;
-
}
-
-
.external-link:hover {
-
color: var(--accent-alt);
-
}
-
-
.external-link::after {
-
content: '↗';
-
margin-left: 3px;
-
font-size: 0.9em;
}
@media (max-width: 600px) {
-
.feed-item-title {
-
font-size: 1rem;
}
-
.feed-item-header, .feed-item-preview, .feed-item-content, .feed-item-actions {
-
padding: 8px 12px;
-
}
-
-
.read-more {
-
padding: 4px 8px;
-
font-size: 0.75rem;
}
}
</style>
···
const entryCountElement = document.getElementById('entry-count');
const sourceCountElement = document.getElementById('source-count');
-
// Function to format date
function formatDate(dateString) {
const date = new Date(dateString);
-
return date.toLocaleString('en-US', {
year: 'numeric',
month: 'short',
-
day: 'numeric',
-
hour: '2-digit',
-
minute: '2-digit'
});
}
···
} else if (firstNewline !== -1) {
endIndex = firstNewline;
} else {
-
// If no period or newline, take first 100 chars
-
endIndex = Math.min(text.length, 100);
}
return text.substring(0, endIndex + 1).trim();
}
-
// Function to attach toggle handlers
-
function setupToggleHandlers() {
-
document.querySelectorAll('.read-more').forEach(button => {
-
button.addEventListener('click', function() {
-
const article = this.closest('article');
-
const content = article.querySelector('.feed-item-content');
-
-
if (content.style.display === 'block') {
-
content.style.display = 'none';
-
this.textContent = 'Read More';
-
this.classList.remove('active');
-
} else {
-
content.style.display = 'block';
-
this.textContent = 'Show Less';
-
this.classList.add('active');
-
}
-
});
-
});
}
try {
···
for (let i = 0; i < entries.length; i++) {
const entry = entries[i];
// Extract entry data
const title = entry.getElementsByTagName('title')[0]?.textContent || 'No Title';
···
const contentType = contentElement?.getAttribute('type') || 'text';
const published = entry.getElementsByTagName('published')[0]?.textContent ||
entry.getElementsByTagName('updated')[0]?.textContent || '';
const categories = entry.getElementsByTagName('category');
// Extract source from category (we're using category to store source name)
···
// Get the first line for preview
const firstLine = getFirstLine(contentHtml);
-
// Format the entry HTML
entriesHTML += `
-
<article class="feed-item">
-
<div class="feed-item-header">
-
<h2 class="feed-item-title"><a href="${link}" target="_blank">${title}</a></h2>
-
<div class="feed-item-meta">
-
<span class="feed-item-source">${source}</span>
-
<span class="timestamp">${formatDate(published)}</span>
</div>
</div>
-
<div class="feed-item-preview">${firstLine}</div>
-
<div class="feed-item-actions">
-
<button class="read-more">Read More</button>
-
<a href="${link}" target="_blank" class="external-link">View Original</a>
-
</div>
<div class="feed-item-content">${contentHtml}</div>
</article>
`;
···
// Update sources count
sourceCountElement.textContent = sources.size;
// Hide loading, show content
loadingContainer.style.display = 'none';
feedItemsContainer.innerHTML = entriesHTML;
-
-
// Setup toggle handlers
-
setupToggleHandlers();
} catch (error) {
console.error('Error loading feed:', error);
···
justify-content: space-between;
align-items: center;
width: 100%;
+
max-width: 1200px;
margin: 0 auto;
}
···
.content {
width: 100%;
+
max-width: 1200px;
}
.feed-item {
background-color: var(--card-bg);
border: 1px solid var(--border-color);
+
border-radius: 4px;
+
margin-bottom: 8px;
overflow: hidden;
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+
transition: background-color 0.2s ease;
}
.feed-item:hover {
+
background-color: #202930;
}
+
.feed-item-row {
+
display: flex;
+
align-items: center;
+
padding: 8px 15px;
+
width: 100%;
+
overflow: hidden;
+
}
+
+
.feed-item-date {
+
font-family: 'JetBrains Mono', monospace;
+
font-size: 0.75rem;
+
color: var(--text-muted);
+
min-width: 80px;
+
margin-right: 10px;
+
}
+
+
.feed-item-author {
+
font-family: 'JetBrains Mono', monospace;
+
color: var(--accent-alt);
+
font-size: 0.85rem;
+
min-width: 70px;
+
margin-right: 15px;
+
white-space: nowrap;
}
.feed-item-title {
+
flex: 1;
+
font-size: 0.95rem;
+
font-weight: 400;
+
white-space: nowrap;
+
overflow: hidden;
+
text-overflow: ellipsis;
+
margin-right: 15px;
}
.feed-item-title a {
···
color: var(--accent-color);
}
.feed-item-preview {
+
flex: 2;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
+
color: var(--text-muted);
+
font-size: 0.85rem;
+
margin-right: 15px;
+
}
+
+
.feed-item-actions {
+
display: flex;
+
align-items: center;
+
gap: 10px;
+
margin-left: auto;
}
.feed-item-content {
padding: 15px;
line-height: 1.6;
display: none;
+
border-top: 1px solid var(--border-color);
+
background-color: #1d252c;
}
.feed-item-content img {
···
color: var(--text-muted);
}
+
.read-more-btn {
background-color: transparent;
+
border: none;
color: var(--accent-color);
cursor: pointer;
font-family: 'JetBrains Mono', monospace;
font-size: 0.8rem;
+
padding: 2px 5px;
+
border-radius: 3px;
transition: all 0.2s ease;
display: inline-flex;
align-items: center;
}
+
.read-more-btn:hover {
+
background-color: rgba(77, 250, 123, 0.1);
+
}
+
+
.external-link {
+
color: var(--text-muted);
+
font-size: 0.8rem;
+
display: inline-flex;
+
align-items: center;
+
text-decoration: none;
}
+
.external-link:hover {
+
color: var(--accent-alt);
}
+
.icon {
+
font-size: 0.9rem;
+
margin-left: 3px;
}
#loading {
···
font-family: 'JetBrains Mono', monospace;
}
+
@media (max-width: 900px) {
+
.feed-item-preview {
+
display: none;
+
}
}
@media (max-width: 600px) {
+
.feed-item-author {
+
min-width: 50px;
+
margin-right: 10px;
}
+
.feed-item-date {
+
min-width: 60px;
+
margin-right: 10px;
}
}
</style>
···
const entryCountElement = document.getElementById('entry-count');
const sourceCountElement = document.getElementById('source-count');
+
// Function to format date (only date, no time)
function formatDate(dateString) {
const date = new Date(dateString);
+
return date.toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
+
day: 'numeric'
});
}
···
} else if (firstNewline !== -1) {
endIndex = firstNewline;
} else {
+
// If no period or newline, take first 80 chars
+
endIndex = Math.min(text.length, 80);
}
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');
+
+
if (content.style.display === 'block') {
+
content.style.display = 'none';
+
button.innerHTML = 'More <span class="icon">↓</span>';
+
} else {
+
content.style.display = 'block';
+
button.innerHTML = 'Less <span class="icon">↑</span>';
+
}
}
try {
···
for (let i = 0; i < entries.length; i++) {
const entry = entries[i];
+
const articleId = `article-${i}`;
// Extract entry data
const title = entry.getElementsByTagName('title')[0]?.textContent || 'No Title';
···
const contentType = contentElement?.getAttribute('type') || 'text';
const published = entry.getElementsByTagName('published')[0]?.textContent ||
entry.getElementsByTagName('updated')[0]?.textContent || '';
+
const author = entry.getElementsByTagName('author')[0]?.getElementsByTagName('name')[0]?.textContent || 'Unknown';
const categories = entry.getElementsByTagName('category');
// Extract source from category (we're using category to store source name)
···
// Get the first line for preview
const firstLine = getFirstLine(contentHtml);
+
// Format the entry HTML - single line layout
entriesHTML += `
+
<article id="${articleId}" class="feed-item">
+
<div class="feed-item-row">
+
<div class="feed-item-date">${formatDate(published)}</div>
+
<div class="feed-item-author">${author}</div>
+
<div class="feed-item-title"><a href="${link}" target="_blank">${title}</a></div>
+
<div class="feed-item-preview">${firstLine}</div>
+
<div class="feed-item-actions">
+
<button class="read-more-btn" onclick="toggleContent('${articleId}')">More <span class="icon">↓</span></button>
+
<a href="${link}" target="_blank" class="external-link">Link <span class="icon">↗</span></a>
</div>
</div>
<div class="feed-item-content">${contentHtml}</div>
</article>
`;
···
// Update sources count
sourceCountElement.textContent = sources.size;
+
// Add the toggleContent function to the global scope
+
window.toggleContent = toggleContent;
+
// Hide loading, show content
loadingContainer.style.display = 'none';
feedItemsContainer.innerHTML = entriesHTML;
} catch (error) {
console.error('Error loading feed:', error);