I hope this works

This commit is contained in:
2026-07-12 00:06:22 +02:00
parent c9e5269708
commit 9d7a3f6660
2 changed files with 49 additions and 139 deletions
+25 -98
View File
@@ -1,4 +1,4 @@
// blog.js Loads and renders blog posts with automatic collapse for long entries
// blog.js Loads and renders blog posts with simple "Read more" toggle for long entries
const BLOG_DIR = 'blog/';
const MANIFEST = BLOG_DIR + 'manifest.json';
@@ -14,7 +14,6 @@ function parseIndexHtmlList(html) {
const name = m[1];
// accept typical text file extensions (and no extension)
if (name.match(/\.(txt|md|text)$/i) || !name.includes('.')) {
// avoid directory links (ending with /)
if (!name.endsWith('/')) hrefs.push(name);
}
}
@@ -47,44 +46,12 @@ async function fetchPost(filename) {
return await res.text();
}
// generate preview: up to 3 sentences, but not exceeding ~300 chars
function getPreview(text, maxLen = 300) {
if (text.length <= maxLen) return text;
// try to split on sentence boundaries (. ! ?) followed by space or newline
const sentences = text.match(/[^.!?]+[.!?]+/g) || [];
let preview = '';
let count = 0;
for (const s of sentences) {
if (preview.length + s.length <= maxLen) {
preview += s;
count++;
if (count >= 3) break;
} else {
// if we haven't reached 3 sentences, break and use what we have
break;
}
}
// If we have at least one sentence, use that; otherwise fallback to wordbreak
if (preview.trim().length > 0) {
return preview.trim();
}
// fallback: take first maxLen chars and cut at last space
let truncated = text.slice(0, maxLen);
const lastSpace = truncated.lastIndexOf(' ');
if (lastSpace > 0) truncated = truncated.slice(0, lastSpace);
return truncated + '…';
}
// ---------- rendering ----------
function renderPost(filename, content) {
const lines = content.split('\n');
if (lines.length < 3) {
// malformed: skip or treat as titleonly
return null;
return null; // malformed
}
const dateTime = lines[0].trim();
const title = lines[1].trim();
@@ -94,13 +61,13 @@ function renderPost(filename, content) {
const postDiv = document.createElement('div');
postDiv.className = 'blog-post';
// title
// Title
const titleEl = document.createElement('h3');
titleEl.className = 'blog-title';
titleEl.textContent = title;
postDiv.appendChild(titleEl);
// meta
// Date
const metaEl = document.createElement('p');
metaEl.className = 'blog-meta';
metaEl.textContent = dateTime;
@@ -108,66 +75,31 @@ function renderPost(filename, content) {
const isLong = body.length > 300;
// Body container (always created, but hidden for long posts)
const bodyDiv = document.createElement('div');
bodyDiv.className = 'blog-body';
bodyDiv.style.whiteSpace = 'pre-line';
bodyDiv.textContent = body;
if (!isLong) {
// short post display full body directly
const bodyDiv = document.createElement('div');
bodyDiv.className = 'blog-body';
bodyDiv.style.whiteSpace = 'pre-line';
bodyDiv.textContent = body;
// Short post show body directly
postDiv.appendChild(bodyDiv);
} else {
// long post show preview with toggle
const container = document.createElement('div');
container.className = 'blog-collapsible';
// Long post hide body initially, add toggle button
bodyDiv.style.display = 'none';
postDiv.appendChild(bodyDiv);
// preview
const previewDiv = document.createElement('div');
previewDiv.className = 'blog-preview';
previewDiv.style.whiteSpace = 'pre-line';
let previewText = getPreview(body);
// ensure ellipsis if preview is truncated
if (previewText.length < body.length && !previewText.endsWith('…')) {
previewText += '…';
}
previewDiv.textContent = previewText;
container.appendChild(previewDiv);
// Toggle button
const toggleBtn = document.createElement('button');
toggleBtn.className = 'blog-toggle';
toggleBtn.textContent = 'Read more';
postDiv.appendChild(toggleBtn);
// Read more button
const readMoreBtn = document.createElement('button');
readMoreBtn.className = 'blog-toggle read-more';
readMoreBtn.textContent = 'Read more';
container.appendChild(readMoreBtn);
// Full content (hidden initially)
const fullDiv = document.createElement('div');
fullDiv.className = 'blog-full';
fullDiv.style.whiteSpace = 'pre-line';
fullDiv.textContent = body;
fullDiv.style.display = 'none';
container.appendChild(fullDiv);
// Show less button (hidden initially) appears at the bottom
const showLessBtn = document.createElement('button');
showLessBtn.className = 'blog-toggle show-less';
showLessBtn.textContent = '✕ Show less';
showLessBtn.style.display = 'none';
container.appendChild(showLessBtn);
// Toggle logic
function expand() {
fullDiv.style.display = 'block';
readMoreBtn.style.display = 'none';
showLessBtn.style.display = 'inline-block';
}
function collapse() {
fullDiv.style.display = 'none';
readMoreBtn.style.display = 'inline-block';
showLessBtn.style.display = 'none';
}
readMoreBtn.addEventListener('click', expand);
showLessBtn.addEventListener('click', collapse);
postDiv.appendChild(container);
toggleBtn.addEventListener('click', function() {
const isHidden = bodyDiv.style.display === 'none';
bodyDiv.style.display = isHidden ? 'block' : 'none';
this.textContent = isHidden ? 'Show less' : 'Read more';
});
}
return postDiv;
@@ -188,11 +120,8 @@ async function initBlog() {
return;
}
// sort files by name (oldest first? we'll sort by date if embedded, but we'll just alphabetical)
// Typically filenames like 2025-01-01.txt, so alphabetical = chronological
files.sort();
files.sort(); // alphabetical -> chronological
// clear feed
feed.innerHTML = '';
for (const file of files) {
@@ -202,7 +131,6 @@ async function initBlog() {
if (el) feed.appendChild(el);
} catch (err) {
console.warn(`Failed to load ${file}:`, err);
// optionally show error placeholder
const errDiv = document.createElement('div');
errDiv.className = 'blog-post';
errDiv.style.color = '#b91c1c';
@@ -211,7 +139,6 @@ async function initBlog() {
}
}
// if nothing was added, show message
if (feed.children.length === 0) {
feed.textContent = 'No valid blog posts found.';
}