48b6d920c4
Key changes merged from svemagie/blog-eleventy-indiekit: - feat: /updated.xml feed for recently edited posts - feat: sitemap.xml generation in eleventy.after hook - feat: excludePostTypes filter for homepage section config - feat: view mode toggle (repo/type) for changelog page - feat: replyTargets config for platform-to-syndicator mapping - feat: syndication badge + linked timestamp on owner replies - perf: memoize aiPosts/aiStats/hash filters; batch unfurl pre-fetch - perf: clear eleventy-img in-memory cache between builds (OOM fix) - perf: memory profiler (logMemory) at build phases - perf: OG batch tracking (totalGenerated/batch counters) - fix: h-entry u-url absolute for IndieNews compatibility - fix: webmention platform detection in build-time templates - fix: deduplicate interactions via interactionKey - fix: reply form syndication via replyTargets (not hardcoded platforms) - fix: remove skeleton loader CSS (CLS fix) - fix: avatar dimensions 96→128 to match CSS classes - css: remove unused skeleton loader rules Local customisations preserved: - Gitea-based data files (githubActivity, githubRepos, githubStarred) - Funkwhale cover image cache copy in eleventy.after - URL fallback arrays in funkwhale/lastfm data fetchers - CONFIGURABLE cache durations (FUNKWHALE_FETCH_CACHE_DURATION etc.) - OG_CACHE_DIR naming (not cacheDir) - Our ogSlug format (plain slug, not date-prefixed) - Gruvbox design tokens (link colours, selection colours) - Unfurl manifest optimisation (skip re-fetching known URLs) - CLAUDE.md, README.md, .github/workflows (ours) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
78 lines
3.3 KiB
Plaintext
78 lines
3.3 KiB
Plaintext
{#
|
|
Hero Section - author intro with avatar, name, title, bio
|
|
Rendered by homepage-builder when hero is enabled
|
|
#}
|
|
|
|
{% set heroConfig = homepageConfig.hero or {} %}
|
|
{% set id = homepageConfig.identity if (homepageConfig and homepageConfig.identity) else {} %}
|
|
{% set authorName = id.name if (id.name is defined) else site.author.name %}
|
|
{% set authorAvatar = id.avatar if (id.avatar is defined) else site.author.avatar %}
|
|
{% set authorTitle = id.title if (id.title is defined) else site.author.title %}
|
|
{% set authorBio = id.bio if (id.bio is defined) else site.author.bio %}
|
|
{% set siteDescription = id.description if (id.description is defined) else site.description %}
|
|
{% set socialLinks = id.social if (id.social is defined) else site.social %}
|
|
{% set authorUrl = id.url if (id.url is defined and id.url) else (site.author.url or site.url) %}
|
|
|
|
<section class="h-card mb-8 sm:mb-12">
|
|
{# Hidden u-photo for reliable microformat parsing (some parsers struggle with img inside links) #}
|
|
{% if authorAvatar %}
|
|
<data class="u-photo hidden" value="{{ authorAvatar }}"></data>
|
|
{% endif %}
|
|
<div class="flex flex-col sm:flex-row gap-6 sm:gap-8 items-start">
|
|
{# Avatar #}
|
|
{% if heroConfig.showAvatar != false %}
|
|
<a href="{{ authorUrl }}" class="flex-shrink-0" rel="me" tabindex="-1" aria-hidden="true">
|
|
<img
|
|
src="{{ authorAvatar }}"
|
|
alt="{{ authorName }}"
|
|
width="128"
|
|
height="128"
|
|
class="w-24 h-24 sm:w-32 sm:h-32 rounded-full object-cover shadow-lg"
|
|
loading="eager"
|
|
>
|
|
</a>
|
|
{% endif %}
|
|
|
|
{# Introduction #}
|
|
<div class="flex-1 min-w-0">
|
|
<h1 class="text-2xl sm:text-3xl md:text-4xl font-bold text-surface-900 dark:text-surface-100 mb-2">
|
|
<a href="{{ authorUrl }}" class="p-name u-url u-uid hover:text-accent-700 dark:hover:text-accent-300" rel="me">{{ authorName }}</a>
|
|
</h1>
|
|
{% if authorTitle %}
|
|
<p class="p-job-title text-lg sm:text-xl text-accent-700 dark:text-accent-300 mb-3 sm:mb-4">
|
|
{{ authorTitle }}
|
|
</p>
|
|
{% endif %}
|
|
{% if authorBio %}
|
|
<p class="p-note text-base sm:text-lg text-surface-700 dark:text-surface-300 mb-4">
|
|
{{ authorBio }}
|
|
</p>
|
|
{% endif %}
|
|
{% if siteDescription %}
|
|
<p class="text-base sm:text-lg text-surface-700 dark:text-surface-300 mb-4 sm:mb-6">
|
|
{{ siteDescription }}
|
|
<a href="/about/" class="text-accent-700 dark:text-accent-300 hover:underline font-medium">Read more →</a>
|
|
</p>
|
|
{% endif %}
|
|
|
|
{# Social Links #}
|
|
{% from "components/social-icon.njk" import socialIcon, socialIconColorClass %}
|
|
{% if heroConfig.showSocial != false and socialLinks %}
|
|
<div class="flex flex-wrap gap-3">
|
|
{% for link in socialLinks %}
|
|
<a
|
|
href="{{ link.url }}"
|
|
rel="{{ link.rel }} noopener"
|
|
class="inline-flex items-center gap-2 px-3 py-2 text-sm text-surface-700 dark:text-surface-300 bg-surface-100 dark:bg-surface-800 rounded-lg hover:bg-surface-200 dark:hover:bg-surface-700 transition-colors"
|
|
target="_blank"
|
|
>
|
|
<span class="{{ socialIconColorClass(link.icon) }}">{{ socialIcon(link.icon, "w-5 h-5") }}</span>
|
|
<span class="text-sm font-medium">{{ link.name }}</span>
|
|
</a>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</section>
|