Files
indiekit-blog/blog.njk
svemagie 01a36d4163 feat: show garden badge in all post list views and overviews
- garden-badge.njk now resolves stage from post.data.gardenStage in
  list contexts (no set required), or gardenStage in post page context
- Badge added after categories in: blog, articles, notes, photos,
  bookmarks, likes, replies, reposts, recent-posts section
- Badge is clickable, links to /garden/#<stage>
2026-03-15 09:04:29 +01:00

396 lines
21 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
layout: layouts/base.njk
title: Blog
withSidebar: true
pagination:
data: collections.listedPosts
size: 20
alias: paginatedPosts
permalink: "blog/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumber + 1 }}/{% endif %}"
---
<div class="h-feed">
<div class="flex flex-wrap items-center gap-4 mb-2">
<h1 class="text-2xl sm:text-3xl font-bold text-surface-900 dark:text-surface-100">Blog</h1>
{% set sparklineSvg = collections.listedPosts | postingFrequency %}
{% if sparklineSvg %}
<div class="flex-1 min-w-0 text-amber-700 dark:text-amber-300">{{ sparklineSvg | safe }}</div>
{% endif %}
</div>
<p class="text-surface-600 dark:text-surface-400 mb-6 sm:mb-8">
All posts including articles and notes.
<span class="text-sm">({{ collections.listedPosts.length }} total)</span>
</p>
{% if paginatedPosts.length > 0 %}
<nav class="flex flex-wrap gap-2 mb-6" aria-label="Filter by post type">
<a href="/blog/" class="px-3 py-1.5 text-sm font-medium rounded-full bg-accent-600 text-white dark:bg-accent-700">All Posts <span class="opacity-75">({{ collections.listedPosts.length }})</span></a>
{% for pt in enabledPostTypes %}
{% set collName = pt.label | lower %}
<a href="{{ pt.path }}" class="px-3 py-1.5 text-sm font-medium rounded-full bg-surface-100 dark:bg-surface-800 text-surface-600 dark:text-surface-300 hover:bg-surface-200 dark:hover:bg-surface-700 border border-surface-200 dark:border-surface-700 transition-colors">{{ pt.label }}{% if collections[collName] %} <span class="text-surface-600 dark:text-surface-400">({{ collections[collName].length }})</span>{% endif %}</a>
{% endfor %}
</nav>
<ul class="post-list">
{% for post in paginatedPosts %}
{# Detect post type from frontmatter properties #}
{% set likedUrl = post.data.likeOf or post.data.like_of %}
{% set bookmarkedUrl = post.data.bookmarkOf or post.data.bookmark_of %}
{% set repostedUrl = post.data.repostOf or post.data.repost_of %}
{% set replyToUrl = post.data.inReplyTo or post.data.in_reply_to %}
{% set hasPhotos = post.data.photo and post.data.photo.length %}
{% set borderClass = "" %}
{% if likedUrl %}
{% set borderClass = "border-l-[3px] border-l-red-400 dark:border-l-red-500" %}
{% elif bookmarkedUrl %}
{% set borderClass = "border-l-[3px] border-l-amber-400 dark:border-l-amber-500" %}
{% elif repostedUrl %}
{% set borderClass = "border-l-[3px] border-l-green-400 dark:border-l-green-500" %}
{% elif replyToUrl %}
{% set borderClass = "border-l-[3px] border-l-sky-400 dark:border-l-sky-500" %}
{% elif hasPhotos %}
{% set borderClass = "border-l-[3px] border-l-purple-400 dark:border-l-purple-500" %}
{% elif post.data.title %}
{% set borderClass = "border-l-[3px] border-l-indigo-400 dark:border-l-indigo-500" %}
{% else %}
{% set borderClass = "border-l-[3px] border-l-teal-400 dark:border-l-teal-500" %}
{% endif %}
<li class="h-entry post-card {{ borderClass }}">
{% if likedUrl %}
{# ── Like card ── #}
<div class="post-header flex items-start gap-3">
<div class="flex-shrink-0 mt-1">
<svg class="w-5 h-5 text-red-500" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>
</svg>
</div>
<div class="flex-1 min-w-0">
<div class="post-meta">
<span class="font-medium text-red-700 dark:text-red-300">Liked</span>
<time-difference><time class="dt-published font-mono text-sm" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }}
</time></time-difference>
{% if post.data.category %}
<span class="post-categories">
{% if post.data.category is string %}
<a href="/categories/{{ post.data.category | slugify }}/" class="p-category">{{ post.data.category }}</a>
{% else %}
{% for cat in post.data.category %}
<a href="/categories/{{ cat | slugify }}/" class="p-category">{{ cat }}</a>
{% endfor %}
{% endif %}
</span>
{% endif %}
{% include "components/garden-badge.njk" %}
</div>
{% unfurl likedUrl %}
<a class="u-like-of text-xs text-surface-600 dark:text-surface-400 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-3 max-w-none">
{{ post.templateContent | safe }}
</div>
{% endif %}
<a class="u-url text-sm text-red-700 dark:text-red-300 hover:underline mt-3 inline-block" href="{{ post.url }}" aria-label="Permalink: {{ post.data.title or ('Like from ' + (post.date | dateDisplay)) }}">Permalink</a>
</div>
</div>
{% elif bookmarkedUrl %}
{# ── Bookmark card ── #}
<div class="post-header flex items-start gap-3">
<div class="flex-shrink-0 mt-1">
<svg class="w-5 h-5 text-amber-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<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>
</div>
<div class="flex-1 min-w-0">
<div class="post-meta">
<span class="font-medium text-amber-700 dark:text-amber-300">Bookmarked</span>
<time-difference><time class="dt-published font-mono text-sm" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }}
</time></time-difference>
{% if post.data.category %}
<span class="post-categories">
{% if post.data.category is string %}
<a href="/categories/{{ post.data.category | slugify }}/" class="p-category">{{ post.data.category }}</a>
{% else %}
{% for cat in post.data.category %}
<a href="/categories/{{ cat | slugify }}/" class="p-category">{{ cat }}</a>
{% endfor %}
{% endif %}
</span>
{% endif %}
{% include "components/garden-badge.njk" %}
</div>
{% if post.data.title %}
<h2 class="p-name text-lg font-semibold text-surface-900 dark:text-surface-100 mt-2">
<a class="hover:text-amber-600 dark:hover:text-amber-400" href="{{ post.url }}">{{ post.data.title }}</a>
</h2>
{% endif %}
{% unfurl bookmarkedUrl %}
<a class="u-bookmark-of text-xs text-surface-600 dark:text-surface-400 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-3 max-w-none">
{{ post.templateContent | safe }}
</div>
{% endif %}
<a class="u-url text-sm text-amber-700 dark:text-amber-300 hover:underline mt-3 inline-block" href="{{ post.url }}" aria-label="Permalink: {{ post.data.title or ('Bookmark from ' + (post.date | dateDisplay)) }}">Permalink</a>
</div>
</div>
{% elif repostedUrl %}
{# ── Repost card ── #}
<div class="post-header flex items-start gap-3">
<div class="flex-shrink-0 mt-1">
<svg class="w-5 h-5 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
</svg>
</div>
<div class="flex-1 min-w-0">
<div class="post-meta">
<span class="font-medium text-green-700 dark:text-green-300">Reposted</span>
<time-difference><time class="dt-published font-mono text-sm" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }}
</time></time-difference>
{% if post.data.category %}
<span class="post-categories">
{% if post.data.category is string %}
<a href="/categories/{{ post.data.category | slugify }}/" class="p-category">{{ post.data.category }}</a>
{% else %}
{% for cat in post.data.category %}
<a href="/categories/{{ cat | slugify }}/" class="p-category">{{ cat }}</a>
{% endfor %}
{% endif %}
</span>
{% endif %}
{% include "components/garden-badge.njk" %}
</div>
{% unfurl repostedUrl %}
<a class="u-repost-of text-xs text-surface-600 dark:text-surface-400 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-3 max-w-none">
{{ post.templateContent | safe }}
</div>
{% endif %}
<a class="u-url text-sm text-green-700 dark:text-green-300 hover:underline mt-3 inline-block" href="{{ post.url }}" aria-label="Permalink: {{ post.data.title or ('Repost from ' + (post.date | dateDisplay)) }}">Permalink</a>
</div>
</div>
{% elif replyToUrl %}
{# ── Reply card ── #}
<div class="post-header flex items-start gap-3">
<div class="flex-shrink-0 mt-1">
<svg class="w-5 h-5 text-sky-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6"/>
</svg>
</div>
<div class="flex-1 min-w-0">
<div class="post-meta">
<span class="font-medium text-sky-700 dark:text-sky-300">In reply to</span>
<time-difference><time class="dt-published font-mono text-sm" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }}
</time></time-difference>
{% if post.data.category %}
<span class="post-categories">
{% if post.data.category is string %}
<a href="/categories/{{ post.data.category | slugify }}/" class="p-category">{{ post.data.category }}</a>
{% else %}
{% for cat in post.data.category %}
<a href="/categories/{{ cat | slugify }}/" class="p-category">{{ cat }}</a>
{% endfor %}
{% endif %}
</span>
{% endif %}
{% include "components/garden-badge.njk" %}
</div>
{% unfurl replyToUrl %}
<a class="u-in-reply-to text-xs text-surface-600 dark:text-surface-400 hover:underline break-all mt-1 inline-block" href="{{ replyToUrl }}">
{{ replyToUrl }}
</a>
<div class="e-content prose dark:prose-invert prose-sm mt-3 max-w-none">
{{ post.templateContent | safe }}
</div>
<a class="u-url text-sm text-sky-700 dark:text-sky-300 hover:underline mt-3 inline-block" href="{{ post.url }}" aria-label="Permalink: {{ post.data.title or ('Reply from ' + (post.date | dateDisplay)) }}">Permalink</a>
</div>
</div>
{% elif hasPhotos %}
{# ── Photo card ── #}
<div class="post-header flex items-start gap-3">
<div class="flex-shrink-0 mt-1">
<svg class="w-5 h-5 text-purple-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 13a3 3 0 11-6 0 3 3 0 016 0z"/>
</svg>
</div>
<div class="flex-1 min-w-0">
<div class="post-meta">
<span class="font-medium text-purple-700 dark:text-purple-300">Photo</span>
<time-difference><time class="dt-published font-mono text-sm" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }}
</time></time-difference>
{% if post.data.category %}
<span class="post-categories">
{% if post.data.category is string %}
<a href="/categories/{{ post.data.category | slugify }}/" class="p-category">{{ post.data.category }}</a>
{% else %}
{% for cat in post.data.category %}
<a href="/categories/{{ cat | slugify }}/" class="p-category">{{ cat }}</a>
{% endfor %}
{% endif %}
</span>
{% endif %}
{% include "components/garden-badge.njk" %}
</div>
<div class="photo-gallery mt-3">
{% for img in post.data.photo %}
{% set photoUrl = img.url %}
{% if photoUrl and photoUrl[0] != '/' and 'http' not in photoUrl %}
{% set photoUrl = '/' + photoUrl %}
{% endif %}
<a href="{{ post.url }}" class="photo-link">
<img src="{{ photoUrl }}" alt="{{ img.alt | default('Photo') }}" class="u-photo" loading="lazy" eleventy:ignore>
</a>
{% endfor %}
</div>
{% if post.templateContent %}
<div class="e-content photo-caption prose dark:prose-invert prose-sm mt-3 max-w-none">
{{ post.templateContent | safe }}
</div>
{% endif %}
<a class="u-url text-sm text-purple-700 dark:text-purple-300 hover:underline mt-3 inline-block" href="{{ post.url }}" aria-label="Permalink: {{ post.data.title or ('Photo from ' + (post.date | dateDisplay)) }}">Permalink</a>
</div>
</div>
{% elif post.data.title %}
{# ── Article card (unchanged) ── #}
<div class="post-header">
<h2 class="text-xl font-semibold mb-1">
<a class="p-name u-url text-surface-900 dark:text-surface-100 hover:text-indigo-700 dark:hover:text-indigo-300" href="{{ post.url }}">
{{ post.data.title }}
</a>
</h2>
<div class="post-meta">
<time class="dt-published font-mono text-sm" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }}
</time>
{% if post.data.category %}
<span class="post-categories">
{% if post.data.category is string %}
<a href="/categories/{{ post.data.category | slugify }}/" class="p-category">{{ post.data.category }}</a>
{% else %}
{% for cat in post.data.category %}
<a href="/categories/{{ cat | slugify }}/" class="p-category">{{ cat }}</a>
{% endfor %}
{% endif %}
</span>
{% endif %}
{% include "components/garden-badge.njk" %}
</div>
</div>
<p class="p-summary text-surface-700 dark:text-surface-300 mt-3">
{{ post.templateContent | striptags | truncate(250) }}
</p>
<a href="{{ post.url }}" class="text-sm text-indigo-700 dark:text-indigo-300 hover:underline mt-3 inline-block">
Read more &rarr;
</a>
{% else %}
{# ── Note card (unchanged) ── #}
<div class="post-header">
<a class="u-url" href="{{ post.url }}">
<time-difference><time class="dt-published text-sm text-surface-600 dark:text-surface-400 font-medium font-mono" datetime="{{ post.date | isoDate }}">
{{ post.date | dateDisplay }}
</time></time-difference>
</a>
{% if post.data.category %}
<span class="post-categories ml-2">
{% if post.data.category is string %}
<a href="/categories/{{ post.data.category | slugify }}/" class="p-category">{{ post.data.category }}</a>
{% else %}
{% for cat in post.data.category %}
<a href="/categories/{{ cat | slugify }}/" class="p-category">{{ cat }}</a>
{% endfor %}
{% endif %}
</span>
{% endif %}
{% include "components/garden-badge.njk" %}
</div>
<div class="e-content prose dark:prose-invert prose-sm mt-3 max-w-none">
{{ post.templateContent | safe }}
</div>
<div class="post-footer mt-3">
<a href="{{ post.url }}" class="text-sm text-teal-700 dark:text-teal-300 hover:underline" aria-label="Permalink: {{ post.data.title or ('Note from ' + (post.date | dateDisplay)) }}">
Permalink
</a>
</div>
{% endif %}
{# AI usage badge — only show when AI was actually used (level > 0) #}
{% set postAi = post.data.ai or {} %}
{% set postAiText = post.data.aiTextLevel or post.data.ai_text_level or postAi.textLevel or postAi.aiTextLevel or "0" %}
{% set postAiCode = post.data.aiCodeLevel or post.data.ai_code_level or postAi.codeLevel or postAi.aiCodeLevel or "0" %}
{% if (postAiText and postAiText !== "0") or (postAiCode and postAiCode !== "0") %}
<span class="inline-flex items-center gap-1 mt-2 px-1.5 py-0.5 rounded text-[10px] font-medium bg-surface-100 dark:bg-surface-700 text-surface-600 dark:text-surface-400" title="AI usage: Text level {{ postAiText or '' }}, Code level {{ postAiCode or '' }}" data-ai-text-level="{{ postAiText }}" data-ai-code-level="{{ postAiCode or '0' }}" data-ai-used="true">
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 3.104v5.714a2.25 2.25 0 01-.659 1.591L5 14.5M9.75 3.104c-.251.023-.501.05-.75.082m.75-.082a24.301 24.301 0 014.5 0m0 0v5.714a2.25 2.25 0 00.659 1.591L19 14.5M14.25 3.104c.251.023.501.05.75.082M19 14.5l-2.47 2.47a2.25 2.25 0 01-1.59.659H9.06a2.25 2.25 0 01-1.591-.659L5 14.5m14 0V17a2 2 0 01-2 2H7a2 2 0 01-2-2v-2.5"/></svg>
AI{% if postAiText %}: T{{ postAiText }}{% endif %}{% if postAiCode %}/C{{ postAiCode }}{% endif %}
<span class="p-ai-text-level hidden">{{ postAiText }}</span>
<span class="p-ai-code-level hidden">{{ postAiCode or "0" }}</span>
</span>
{% endif %}
{% include "components/garden-badge.njk" %}
</li>
{% endfor %}
</ul>
{# Pagination controls #}
{% if pagination.pages.length > 1 %}
<nav class="pagination" aria-label="Blog pagination">
<div class="pagination-info">
Page {{ pagination.pageNumber + 1 }} of {{ pagination.pages.length }}
</div>
<div class="pagination-links">
{% if pagination.href.previous %}
<a href="{{ pagination.href.previous }}" class="pagination-link" aria-label="Previous page">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg>
Previous
</a>
{% else %}
<span class="pagination-link disabled" aria-disabled="true">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg>
Previous
</span>
{% endif %}
{% include "components/garden-badge.njk" %}
{% if pagination.href.next %}
<a href="{{ pagination.href.next }}" class="pagination-link" aria-label="Next page">
Next
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path></svg>
</a>
{% else %}
<span class="pagination-link disabled" aria-disabled="true">
Next
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path></svg>
</span>
{% endif %}
{% include "components/garden-badge.njk" %}
</div>
</nav>
{% endif %}
{% else %}
<p class="text-surface-600 dark:text-surface-400">No posts yet. Create your first post using a Micropub client!</p>
<p class="mt-4 text-surface-600 dark:text-surface-400">Some popular Micropub clients:</p>
<ul class="list-disc list-inside mt-2 text-surface-700 dark:text-surface-300 space-y-1">
<li><a href="https://quill.p3k.io" class="text-accent-700 dark:text-accent-300 hover:underline">Quill</a> - Web-based</li>
<li><a href="https://indiepass.app" class="text-accent-700 dark:text-accent-300 hover:underline">IndiePass</a> - Mobile app</li>
<li><a href="https://micropublish.net" class="text-accent-700 dark:text-accent-300 hover:underline">Micropublish</a> - Web-based</li>
</ul>
{% endif %}
</div>