chore: sync upstream — performance, webmentions v2, OG v3

- _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 <noreply@anthropic.com>
This commit is contained in:
svemagie
2026-03-15 23:56:56 +01:00
parent f2b05746d7
commit a166af2306
40 changed files with 1208 additions and 430 deletions

View File

@@ -14,14 +14,14 @@
<div class="flex border-b border-surface-200 dark:border-surface-700 mb-3">
<button
@click="tab = 'inbound'"
:class="tab === 'inbound' ? 'border-accent-500 text-accent-700 dark:text-accent-300' : 'border-transparent text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300'"
:class="tab === 'inbound' ? 'border-accent-500 text-accent-600 dark:text-accent-400' : 'border-transparent text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300'"
class="px-2 py-1.5 text-xs font-medium border-b-2 -mb-px transition-colors">
Received
<span x-show="mentions.length" x-text="mentions.length" class="ml-0.5 text-xs opacity-75"></span>
</button>
<button
@click="tab = 'outbound'"
:class="tab === 'outbound' ? 'border-accent-500 text-accent-700 dark:text-accent-300' : 'border-transparent text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300'"
:class="tab === 'outbound' ? 'border-accent-500 text-accent-600 dark:text-accent-400' : 'border-transparent text-surface-600 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-300'"
class="px-2 py-1.5 text-xs font-medium border-b-2 -mb-px transition-colors">
Sent
</button>
@@ -66,7 +66,7 @@
{# Link to full interactions page #}
<div x-show="mentions.length > 0" class="mt-2 pt-2 border-t border-surface-200 dark:border-surface-700">
<a href="/interactions/" class="text-xs text-accent-700 dark:text-accent-300 hover:underline">
<a href="/interactions/" class="text-xs text-accent-600 dark:text-accent-400 hover:underline">
View all &rarr;
</a>
</div>
@@ -111,7 +111,7 @@
{% endfor %}
</div>
<div class="mt-2 pt-2 border-t border-surface-200 dark:border-surface-700">
<a href="/interactions/" class="text-xs text-accent-700 dark:text-accent-300 hover:underline">
<a href="/interactions/" class="text-xs text-accent-600 dark:text-accent-400 hover:underline">
View all &rarr;
</a>
</div>
@@ -150,19 +150,7 @@ function webmentionsWidget() {
merged.push(item);
}
// Use same self-Bluesky filter as post-interactions/interactions.njk
const isSelfBsky = (item) => {
const u = (item.url || '').toLowerCase();
const a = ((item.author && item.author.url) || '').toLowerCase();
return u.includes('did:plc:g4utqyolpyb5zpwwodmm3hht') ||
u.includes('bsky.app/profile/svemagie.bsky.social') ||
a.includes('did:plc:g4utqyolpyb5zpwwodmm3hht') ||
a.includes('bsky.app/profile/svemagie.bsky.social');
};
const filtered = merged.filter((item) => !isSelfBsky(item));
this.mentions = filtered.sort((a, b) => {
this.mentions = merged.sort((a, b) => {
return new Date(b.published || b['wm-received'] || 0) - new Date(a.published || a['wm-received'] || 0);
});
} catch (e) {