From a166af2306a7a7f220d66af2e51737a511491ead Mon Sep 17 00:00:00 2001 From: svemagie <869694+svemagie@users.noreply.github.com> Date: Sun, 15 Mar 2026 23:56:56 +0100 Subject: [PATCH] =?UTF-8?q?chore:=20sync=20upstream=20=E2=80=94=20performa?= =?UTF-8?q?nce,=20webmentions=20v2,=20OG=20v3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - _data: switch to cachedFetch wrapper (10s timeout + 4h watch cache) - js/webmentions.js: owner reply threading, platform provenance badges, DOM dedup, Micropub reply support - js/comments.js: owner detection, reply system, Alpine.store integration - _includes/components/webmentions.njk: data-wm-* attrs, provenance badge slots, reply buttons - _includes/components/comments.njk: owner-aware comment form, threaded replies - widgets/toc.njk: Alpine.js tocScanner upgrade (replaces is-land/inline-JS) - lib/og.js + og-cli.js: OG card v3 (light theme, avatar, batched spawn, DESIGN_VERSION=3) - eleventy.config.js: hasOgImage cache, memoized date filters, batched OG/unfurl, post-build GC, YouTube check opt - base.njk: Inter font preloads + toc-scanner.js script - critical.css: font-face declarations (font-display:optional) - tailwind.css: font-display swap→optional - tailwind.config.js: prose link colors -700→-600 - Color design system: accent-700/300 → accent-600/400 across components Co-Authored-By: Claude Sonnet 4.6 --- _data/blogrollStatus.js | 4 +- _data/conversationMentions.js | 4 +- _data/githubRepos.js | 4 +- _data/newsActivity.js | 4 +- _data/podrollStatus.js | 4 +- _data/recentComments.js | 4 +- _data/youtubeChannel.js | 4 +- _includes/components/comments.njk | 104 +++- _includes/components/cv-builder.njk | 10 +- _includes/components/h-card.njk | 42 +- _includes/components/post-navigation.njk | 8 +- .../components/sections/featured-posts.njk | 18 +- .../components/sections/posting-activity.njk | 2 +- _includes/components/webmentions.njk | 28 +- _includes/components/widgets/ai-usage.njk | 6 +- _includes/components/widgets/blogroll.njk | 10 +- .../components/widgets/post-navigation.njk | 4 +- .../components/widgets/recent-comments.njk | 2 +- _includes/components/widgets/toc.njk | 84 +--- _includes/components/widgets/webmentions.njk | 22 +- _includes/layouts/base.njk | 6 + about.njk | 2 +- changelog.njk | 78 +-- css/critical.css | 6 + css/tailwind.css | 16 +- cv.njk | 10 +- digest-index.njk | 2 +- digest.njk | 12 +- eleventy.config.js | 126 ++++- featured.njk | 4 +- images/rick.jpg | Bin 0 -> 39154 bytes js/comments.js | 142 +++++- js/webmentions.js | 474 ++++++++++++++++-- lib/og-cli.js | 19 +- lib/og.js | 273 ++++++++-- news.njk | 20 +- search.njk | 2 +- slashes.njk | 72 +-- tailwind.config.js | 4 +- webmention-debug.njk | 2 +- 40 files changed, 1208 insertions(+), 430 deletions(-) create mode 100644 images/rick.jpg diff --git a/_data/blogrollStatus.js b/_data/blogrollStatus.js index aee1601..d2999c1 100644 --- a/_data/blogrollStatus.js +++ b/_data/blogrollStatus.js @@ -4,7 +4,7 @@ * Used for conditional navigation — the blogroll page itself loads data client-side. */ -import EleventyFetch from "@11ty/eleventy-fetch"; +import { cachedFetch } from "../lib/data-fetch.js"; const INDIEKIT_URL = process.env.SITE_URL || "https://example.com"; @@ -12,7 +12,7 @@ export default async function () { try { const url = `${INDIEKIT_URL}/blogrollapi/api/status`; console.log(`[blogrollStatus] Checking API: ${url}`); - const data = await EleventyFetch(url, { + const data = await cachedFetch(url, { duration: "15m", type: "json", }); diff --git a/_data/conversationMentions.js b/_data/conversationMentions.js index 1652f37..4af7bc9 100644 --- a/_data/conversationMentions.js +++ b/_data/conversationMentions.js @@ -1,8 +1,8 @@ -import EleventyFetch from "@11ty/eleventy-fetch"; +import { cachedFetch } from "../lib/data-fetch.js"; export default async function () { try { - const data = await EleventyFetch( + const data = await cachedFetch( "http://127.0.0.1:8080/conversations/api/mentions?per-page=10000", { duration: "15m", type: "json" } ); diff --git a/_data/githubRepos.js b/_data/githubRepos.js index 1b8fe5f..9b1bc34 100644 --- a/_data/githubRepos.js +++ b/_data/githubRepos.js @@ -3,7 +3,7 @@ * Fetches public repositories from GitHub API */ -import EleventyFetch from "@11ty/eleventy-fetch"; +import { cachedFetch } from "../lib/data-fetch.js"; export default async function () { const username = process.env.GITHUB_USERNAME || ""; @@ -12,7 +12,7 @@ export default async function () { // Fetch public repos, sorted by updated date const url = `https://api.github.com/users/${username}/repos?sort=updated&per_page=10&type=owner`; - const repos = await EleventyFetch(url, { + const repos = await cachedFetch(url, { duration: "1h", // Cache for 1 hour type: "json", fetchOptions: { diff --git a/_data/newsActivity.js b/_data/newsActivity.js index 47499b8..3c63883 100644 --- a/_data/newsActivity.js +++ b/_data/newsActivity.js @@ -3,7 +3,7 @@ * Fetches from Indiekit's endpoint-rss public API */ -import EleventyFetch from "@11ty/eleventy-fetch"; +import { cachedFetch } from "../lib/data-fetch.js"; const INDIEKIT_URL = process.env.SITE_URL || "https://example.com"; @@ -14,7 +14,7 @@ async function fetchFromIndiekit(endpoint) { try { const url = `${INDIEKIT_URL}/rssapi/api/${endpoint}`; console.log(`[newsActivity] Fetching from Indiekit: ${url}`); - const data = await EleventyFetch(url, { + const data = await cachedFetch(url, { duration: "15m", type: "json", }); diff --git a/_data/podrollStatus.js b/_data/podrollStatus.js index cf117e5..9ac88cb 100644 --- a/_data/podrollStatus.js +++ b/_data/podrollStatus.js @@ -4,7 +4,7 @@ * Used for conditional navigation — the podroll page itself loads data client-side. */ -import EleventyFetch from "@11ty/eleventy-fetch"; +import { cachedFetch } from "../lib/data-fetch.js"; const INDIEKIT_URL = process.env.SITE_URL || "https://example.com"; @@ -12,7 +12,7 @@ export default async function () { try { const url = `${INDIEKIT_URL}/podrollapi/api/status`; console.log(`[podrollStatus] Checking API: ${url}`); - const data = await EleventyFetch(url, { + const data = await cachedFetch(url, { duration: "15m", type: "json", }); diff --git a/_data/recentComments.js b/_data/recentComments.js index bdd1ede..0f9edda 100644 --- a/_data/recentComments.js +++ b/_data/recentComments.js @@ -3,7 +3,7 @@ * Fetches the 5 most recent comments at build time for the sidebar widget. */ -import EleventyFetch from "@11ty/eleventy-fetch"; +import { cachedFetch } from "../lib/data-fetch.js"; const INDIEKIT_URL = process.env.SITE_URL || "https://example.com"; @@ -11,7 +11,7 @@ export default async function () { try { const url = `${INDIEKIT_URL}/comments/api/comments?limit=5`; console.log(`[recentComments] Fetching: ${url}`); - const data = await EleventyFetch(url, { + const data = await cachedFetch(url, { duration: "15m", type: "json", }); diff --git a/_data/youtubeChannel.js b/_data/youtubeChannel.js index 7fbf461..81be351 100644 --- a/_data/youtubeChannel.js +++ b/_data/youtubeChannel.js @@ -4,7 +4,7 @@ * Supports single or multiple channels */ -import EleventyFetch from "@11ty/eleventy-fetch"; +import { cachedFetch } from "../lib/data-fetch.js"; const INDIEKIT_URL = process.env.SITE_URL || "https://example.com"; @@ -15,7 +15,7 @@ async function fetchFromIndiekit(endpoint) { try { const url = `${INDIEKIT_URL}/youtubeapi/api/${endpoint}`; console.log(`[youtubeChannel] Fetching from Indiekit: ${url}`); - const data = await EleventyFetch(url, { + const data = await cachedFetch(url, { duration: "5m", type: "json", }); diff --git a/_includes/components/comments.njk b/_includes/components/comments.njk index dacd261..445fe96 100644 --- a/_includes/components/comments.njk +++ b/_includes/components/comments.njk @@ -30,7 +30,7 @@ {# Sign-in form (shown when not authenticated) #} -
+

Sign in with your website to comment:

@@ -46,12 +46,13 @@
- {# Comment form (shown when authenticated) #} -
+ {# Comment form (shown when authenticated via IndieAuth OR as site owner) #} +
Signed in as - - + +
@@ -76,28 +77,83 @@

Loading comments...

-