From aae9836b997effd6bcae2f02cfdd385a2836c2f3 Mon Sep 17 00:00:00 2001 From: Labyricorn Date: Wed, 12 Aug 2026 00:09:20 -0700 Subject: [PATCH] Batch recent activity six at a time --- AGENTS.md | 4 ++++ README.md | 5 ++++- assets/static/activity.js | 34 ++++++++++++++++++++++++++++++++++ assets/static/style.css | 18 ++++++++++++++++++ templates/base.html | 3 ++- templates/home.html | 8 ++++++-- 6 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 assets/static/activity.js diff --git a/AGENTS.md b/AGENTS.md index 7945cfb..176d0ca 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -63,6 +63,10 @@ making changes; it is the authoritative operations and deployment runbook. mustard labels and are excluded from discovery. - Keep project devlog entries in the homepage Recent Activity feed alongside projects, blog entries, and articles, ordered by publication date. +- Keep Recent Activity batching as a progressive enhancement: render the full + semantic feed, show six items initially with JavaScript, reveal six more per + activation, announce the visible count, and leave all items available when + JavaScript is disabled. - When `README.md` changes, keep the README-derived example blog entry aligned with the runbook content it demonstrates. diff --git a/README.md b/README.md index 2cba691..b00952e 100644 --- a/README.md +++ b/README.md @@ -199,7 +199,10 @@ The public routes are `/projects/thinkloom/`, native Lektor records and approved images. This site owns their models, templates, layout, repository-information card, and import policy. Imported devlog entries also join the homepage Recent Activity feed, ordered with -projects, articles, and blog entries by publication date. +projects, articles, and blog entries by publication date. The feed initially +shows six items and reveals up to six more per button activation. This is a +progressive enhancement implemented by `assets/static/activity.js`; without +JavaScript, the complete semantic feed remains visible. `configs/project-sources.ini` is the allowlist. Each source declares a stable project ID, credential-free HTTPS Git URL, public web/API URL, and branch. Add diff --git a/assets/static/activity.js b/assets/static/activity.js new file mode 100644 index 0000000..b9f3d27 --- /dev/null +++ b/assets/static/activity.js @@ -0,0 +1,34 @@ +(() => { + for (const feed of document.querySelectorAll("[data-activity-feed]")) { + const items = [...feed.querySelectorAll("[data-activity-item]")]; + const batchSize = Number.parseInt(feed.dataset.batchSize || "6", 10); + const controls = feed.parentElement.querySelector("[data-activity-controls]"); + const status = controls?.querySelector("[data-activity-status]"); + const button = controls?.querySelector("[data-activity-more]"); + + if (!controls || !status || !button || items.length <= batchSize) continue; + + let visibleCount = Math.min(batchSize, items.length); + + const render = () => { + for (const [index, item] of items.entries()) { + item.hidden = index >= visibleCount; + } + + const remaining = items.length - visibleCount; + status.textContent = `Showing ${visibleCount} of ${items.length} recent activity items.`; + button.disabled = remaining === 0; + button.textContent = remaining + ? `Show ${Math.min(batchSize, remaining)} more` + : "All activity shown"; + controls.hidden = false; + }; + + button.addEventListener("click", () => { + visibleCount = Math.min(visibleCount + batchSize, items.length); + render(); + }); + + render(); + } +})(); diff --git a/assets/static/style.css b/assets/static/style.css index ab1db66..379a41f 100644 --- a/assets/static/style.css +++ b/assets/static/style.css @@ -57,6 +57,23 @@ h1 { max-width: 650px; margin: 0; font-size: clamp(30px, 4vw, 45px); font-style: .timeline-entry h2, .entry-card h2 { margin: 10px 0 5px; font-size: 21px; line-height: 1.3; } .timeline-entry p, .entry-card p { max-width: 680px; margin: 0 0 10px; color: var(--muted); } .read-more { color: var(--magenta); font-size: 10px; } +.activity-controls { display: flex; justify-content: space-between; align-items: center; gap: 18px; padding: 24px 0 0 37px; } +.activity-controls[hidden] { display: none; } +.activity-status { margin: 0; color: var(--muted); font: 500 9px var(--mono); letter-spacing: .08em; text-transform: uppercase; } +.activity-more-button { + border: 1px solid var(--rule); + background: transparent; + padding: 7px 10px; + color: var(--muted); + cursor: pointer; + font: 500 9px var(--mono); + letter-spacing: .08em; + white-space: nowrap; + text-transform: uppercase; +} +.activity-more-button:hover, +.activity-more-button:focus-visible { border-color: var(--teal); color: var(--teal); } +.activity-more-button:disabled { cursor: default; opacity: .55; } .card-grid { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 1px; background: var(--rule); border: 1px solid var(--rule); } .entry-card { min-height: 245px; padding: 28px; background: var(--bg); } @@ -286,6 +303,7 @@ h1 { max-width: 650px; margin: 0; font-size: clamp(30px, 4vw, 45px); font-style: .card-grid { grid-template-columns: 1fr; } .tag-directory { grid-template-columns: 1fr; } .tag-filter { padding-block: 18px; } + .activity-controls { align-items: flex-start; padding-left: 30px; } .tag-directory-card { min-height: 0; } .project-hero { padding-block: 44px; } .project-hero-inner, diff --git a/templates/base.html b/templates/base.html index 7720de6..d4d8428 100644 --- a/templates/base.html +++ b/templates/base.html @@ -9,8 +9,9 @@ - + +