feat: add unfurl cards to collection views and homepage

Show rich link preview cards in bookmarks, likes, replies, reposts
collection pages and the homepage recent posts section. URLs are
fetched once and cached — the same cache serves all templates.
This commit is contained in:
Ricardo
2026-02-20 12:39:11 +01:00
parent 41c7fae2f1
commit 0f496d624f
5 changed files with 30 additions and 40 deletions

View File

@@ -40,11 +40,10 @@
{{ post.date | dateDisplay }}
</time>
</div>
<p class="mt-1">
<a class="u-like-of text-sm text-primary-600 dark:text-primary-400 hover:underline break-all" href="{{ likedUrl }}">
{{ likedUrl }}
</a>
</p>
{% unfurl likedUrl %}
<a class="u-like-of text-xs text-surface-400 dark:text-surface-500 hover:underline break-all mt-1 inline-block" href="{{ likedUrl }}">
{{ likedUrl }}
</a>
{% if post.templateContent %}
<div class="e-content prose dark:prose-invert prose-sm mt-2 max-w-none line-clamp-3">
{{ post.templateContent | safe }}
@@ -74,11 +73,10 @@
<a class="hover:text-primary-600 dark:hover:text-primary-400" href="{{ post.url }}">{{ post.data.title }}</a>
</h3>
{% endif %}
<p class="mt-1 text-sm">
<a class="u-bookmark-of text-primary-600 dark:text-primary-400 hover:underline break-all" href="{{ bookmarkedUrl }}">
{{ bookmarkedUrl }}
</a>
</p>
{% unfurl bookmarkedUrl %}
<a class="u-bookmark-of text-xs text-surface-400 dark:text-surface-500 hover:underline break-all mt-1 inline-block" href="{{ bookmarkedUrl }}">
{{ bookmarkedUrl }}
</a>
{% if post.templateContent %}
<div class="e-content prose dark:prose-invert prose-sm mt-2 max-w-none line-clamp-3">
{{ post.templateContent | safe }}
@@ -103,11 +101,10 @@
{{ post.date | dateDisplay }}
</time>
</div>
<p class="mt-1 text-sm">
<a class="u-repost-of text-primary-600 dark:text-primary-400 hover:underline break-all" href="{{ repostedUrl }}">
{{ repostedUrl }}
</a>
</p>
{% unfurl repostedUrl %}
<a class="u-repost-of text-xs text-surface-400 dark:text-surface-500 hover:underline break-all mt-1 inline-block" href="{{ repostedUrl }}">
{{ repostedUrl }}
</a>
{% if post.templateContent %}
<div class="e-content prose dark:prose-invert prose-sm mt-2 max-w-none line-clamp-3">
{{ post.templateContent | safe }}
@@ -132,11 +129,10 @@
{{ post.date | dateDisplay }}
</time>
</div>
<p class="mt-1 text-sm">
<a class="u-in-reply-to text-primary-600 dark:text-primary-400 hover:underline break-all" href="{{ replyToUrl }}">
{{ replyToUrl }}
</a>
</p>
{% unfurl replyToUrl %}
<a class="u-in-reply-to text-xs text-surface-400 dark:text-surface-500 hover:underline break-all mt-1 inline-block" href="{{ replyToUrl }}">
{{ replyToUrl }}
</a>
{% if post.templateContent %}
<div class="e-content prose dark:prose-invert prose-sm mt-2 max-w-none line-clamp-3">
{{ post.templateContent | safe }}

View File

@@ -54,14 +54,10 @@ permalink: "bookmarks/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageN
{# Support both camelCase (Indiekit Eleventy preset) and underscore (legacy) property names #}
{% set bookmarkedUrl = post.data.bookmarkOf or post.data.bookmark_of %}
{% if bookmarkedUrl %}
<p class="mt-3 flex items-center gap-2 text-sm">
<svg class="w-4 h-4 text-primary-500 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z"/>
</svg>
<a class="u-bookmark-of text-primary-600 dark:text-primary-400 hover:underline break-all" href="{{ bookmarkedUrl }}">
{{ bookmarkedUrl }}
</a>
</p>
{% unfurl bookmarkedUrl %}
<a class="u-bookmark-of text-xs text-surface-400 dark:text-surface-500 hover:underline break-all mt-1 inline-block" href="{{ bookmarkedUrl }}">
{{ bookmarkedUrl }}
</a>
{% endif %}
{% if post.templateContent %}
<div class="e-content prose dark:prose-invert prose-sm mt-3 max-w-none">

View File

@@ -52,11 +52,10 @@ permalink: "likes/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumbe
{# Support both camelCase (Indiekit Eleventy preset) and underscore (legacy) property names #}
{% set likedUrl = post.data.likeOf or post.data.like_of %}
{% if likedUrl %}
<p class="mt-2">
<a class="u-like-of text-primary-600 dark:text-primary-400 hover:underline break-all" href="{{ likedUrl }}">
{{ likedUrl }}
</a>
</p>
{% unfurl likedUrl %}
<a class="u-like-of text-xs text-surface-400 dark:text-surface-500 hover:underline break-all mt-1 inline-block" href="{{ likedUrl }}">
{{ likedUrl }}
</a>
{% endif %}
{% if post.templateContent %}
<div class="e-content prose dark:prose-invert prose-sm mt-3 max-w-none">

View File

@@ -58,9 +58,10 @@ permalink: "replies/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNum
{% set replyTo = post.data.inReplyTo or post.data.in_reply_to %}
{% if replyTo %}
{% set protocol = replyTo | protocolType %}
{% unfurl replyTo %}
<p class="mt-2 text-sm flex items-center gap-2 flex-wrap">
<span class="text-surface-500">In reply to:</span>
<a class="u-in-reply-to text-primary-600 dark:text-primary-400 hover:underline break-all" href="{{ replyTo }}">
<a class="u-in-reply-to text-xs text-surface-400 dark:text-surface-500 hover:underline break-all" href="{{ replyTo }}">
{{ replyTo | replace("https://", "") | replace("http://", "") | truncate(60) }}
</a>
{% if protocol == "atmosphere" %}

View File

@@ -57,12 +57,10 @@ permalink: "reposts/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNum
{# Support both camelCase (Indiekit Eleventy preset) and underscore (legacy) property names #}
{% set repostedUrl = post.data.repostOf or post.data.repost_of %}
{% if repostedUrl %}
<p class="mt-2 text-sm">
<span class="text-surface-500">Reposted:</span>
<a class="u-repost-of text-primary-600 dark:text-primary-400 hover:underline break-all" href="{{ repostedUrl }}">
{{ repostedUrl }}
</a>
</p>
{% unfurl repostedUrl %}
<a class="u-repost-of text-xs text-surface-400 dark:text-surface-500 hover:underline break-all mt-1 inline-block" href="{{ repostedUrl }}">
{{ repostedUrl }}
</a>
{% endif %}
{% if post.templateContent %}
<div class="e-content prose dark:prose-invert prose-sm mt-3 max-w-none">