This commit is contained in:
2026-07-11 23:15:44 +02:00
parent cd382e2ff2
commit 23a0f16744
+101 -7
View File
@@ -105,6 +105,56 @@ function formatDate(date) {
}).format(date); }).format(date);
} }
// ---------- new preview logic ----------
function getPreviewText(body) {
const maxLen = 300;
if (body.length <= maxLen) {
return { preview: body, isTruncated: false };
}
// Try to use first 3 nonempty lines
const lines = body.split('\n').filter(line => line.trim() !== '');
if (lines.length >= 3) {
const firstThree = lines.slice(0, 3);
const combined = firstThree.join('\n');
if (combined.length < maxLen) {
return { preview: combined, isTruncated: true };
}
}
// Try to use first 3 sentences
const sentenceEnd = /[.!?]+\s*/g;
const sentences = [];
let match;
let lastIndex = 0;
while ((match = sentenceEnd.exec(body)) !== null && sentences.length < 3) {
const end = match.index + match[0].length;
const sentence = body.slice(lastIndex, end).trim();
if (sentence) sentences.push(sentence);
lastIndex = end;
}
if (sentences.length >= 3) {
const combined = sentences.join(' ');
if (combined.length < maxLen) {
return { preview: combined, isTruncated: true };
}
}
// Fallback: take first maxLen chars, cut at last sentence boundary if possible
let cut = maxLen;
const firstPart = body.slice(0, maxLen);
const lastPunct = firstPart.search(/[.!?](?=\s|$)/g);
if (lastPunct !== -1 && lastPunct + 1 < maxLen) {
cut = lastPunct + 1;
} else {
// cut at last space
const lastSpace = firstPart.lastIndexOf(' ');
if (lastSpace > 0) cut = lastSpace;
}
return { preview: body.slice(0, cut) + '…', isTruncated: true };
}
// ---------------------------------------
function createParagraphs(body) { function createParagraphs(body) {
return body return body
.split(/\n{2,}/) .split(/\n{2,}/)
@@ -122,6 +172,13 @@ function createParagraphs(body) {
}); });
} }
function renderPlainText(text) {
// Used for preview shows text with line breaks, no attachment links
const escaped = escapeHtml(text);
const lines = escaped.split('\n');
return lines.join('<br>');
}
function renderBlogEntries(entries) { function renderBlogEntries(entries) {
const container = document.querySelector('.blog-feed'); const container = document.querySelector('.blog-feed');
container.innerHTML = ''; container.innerHTML = '';
@@ -143,17 +200,57 @@ function renderBlogEntries(entries) {
meta.className = 'blog-meta'; meta.className = 'blog-meta';
meta.textContent = entry.date ? formatDate(entry.date) : entry.dateLabel || 'Date not provided'; meta.textContent = entry.date ? formatDate(entry.date) : entry.dateLabel || 'Date not provided';
const body = document.createElement('div'); // ----- content area with preview & full -----
body.className = 'blog-body'; const contentWrapper = document.createElement('div');
contentWrapper.className = 'blog-content';
const { preview, isTruncated } = getPreviewText(entry.body);
// Preview (only shown when truncated)
const previewDiv = document.createElement('div');
previewDiv.className = 'blog-preview';
if (isTruncated) {
previewDiv.innerHTML = renderPlainText(preview);
contentWrapper.appendChild(previewDiv);
}
// Full body (always created, hidden initially if truncated)
const fullDiv = document.createElement('div');
fullDiv.className = 'blog-full';
if (isTruncated) {
fullDiv.style.display = 'none';
}
const paragraphs = createParagraphs(entry.body); const paragraphs = createParagraphs(entry.body);
if (!paragraphs.length) { if (!paragraphs.length) {
const empty = document.createElement('p'); const empty = document.createElement('p');
empty.textContent = 'No content yet.'; empty.textContent = 'No content yet.';
body.appendChild(empty); fullDiv.appendChild(empty);
} else { } else {
paragraphs.forEach((p) => body.appendChild(p)); paragraphs.forEach((p) => fullDiv.appendChild(p));
}
contentWrapper.appendChild(fullDiv);
// Toggle link (only if truncated)
if (isTruncated) {
const toggle = document.createElement('a');
toggle.href = '#';
toggle.className = 'blog-toggle';
toggle.textContent = 'Read more';
toggle.addEventListener('click', (e) => {
e.preventDefault();
const isExpanded = fullDiv.style.display !== 'none';
fullDiv.style.display = isExpanded ? 'none' : 'block';
previewDiv.style.display = isExpanded ? 'block' : 'none';
toggle.textContent = isExpanded ? 'Read more' : 'Collapse';
});
contentWrapper.appendChild(toggle);
} }
article.appendChild(title);
article.appendChild(meta);
article.appendChild(contentWrapper);
// Attachments (always visible)
if (entry.attachments && entry.attachments.length) { if (entry.attachments && entry.attachments.length) {
const attachTitle = document.createElement('div'); const attachTitle = document.createElement('div');
attachTitle.className = 'blog-meta'; attachTitle.className = 'blog-meta';
@@ -174,9 +271,6 @@ function renderBlogEntries(entries) {
article.appendChild(list); article.appendChild(list);
} }
article.appendChild(title);
article.appendChild(meta);
article.appendChild(body);
container.appendChild(article); container.appendChild(article);
}); });
} }