Files
Ricardo 1c2fb321bc feat: image rendering, link preview CSS, lightbox swipe, URL linkification (v2.8.0)
- Gallery photos: 220px → 280px height, 180px on mobile (≤480px)
- Link preview cards: full CSS for horizontal card layout (text left, image right)
- Lightbox: touch/swipe support for mobile (50px threshold)
- URL linkification: bare URLs in content auto-wrapped in <a> tags before AP delivery

Confab-Link: http://localhost:8080/sessions/c5b1471e-b046-44d9-b94f-ab5e68fae7cc
2026-03-06 10:42:39 +01:00

73 lines
4.1 KiB
Plaintext

{# Media attachments partial — included from ap-item-card.njk #}
{# Photo gallery with lightbox #}
{% if item.photo and item.photo.length > 0 %}
{% set displayCount = item.photo.length if item.photo.length < 4 else 4 %}
{% set extraCount = item.photo.length - 4 %}
{% set totalPhotos = item.photo.length %}
<div x-data="{ lightbox: false, idx: 0, touchX: 0 }" class="ap-card__gallery ap-card__gallery--{{ displayCount }}">
{% for photo in item.photo %}
{# Support both old string format and new object format #}
{% set photoSrc = photo.url if photo.url else photo %}
{% set photoAlt = photo.alt if photo.alt else "" %}
{% set photoBlurhash = photo.blurhash if photo.blurhash else "" %}
{# Focus-point cropping: convert -1..1 range to CSS object-position percentages #}
{% set focusStyle = "" %}
{% if photo.focus and photo.focus.x != null and photo.focus.y != null %}
{% set fpX = ((photo.focus.x + 1) / 2 * 100) %}
{% set fpY = ((1 - (photo.focus.y + 1) / 2) * 100) %}
{% set focusStyle = "object-position:" + fpX + "% " + fpY + "%;" %}
{% endif %}
{% if loop.index0 < 4 %}
<div class="ap-card__gallery-item" x-data="{ showAlt: false }">
<button type="button" @click="idx = {{ loop.index0 }}; lightbox = true" class="ap-card__gallery-link{% if loop.index0 == 3 and extraCount > 0 %} ap-card__gallery-link--more{% endif %}">
<img src="{{ photoSrc }}" alt="{{ photoAlt }}" loading="lazy"{% if focusStyle %} style="{{ focusStyle }}"{% endif %}{% if photoBlurhash %} data-blurhash="{{ photoBlurhash }}"{% endif %}>
{% if loop.index0 == 3 and extraCount > 0 %}
<span class="ap-card__gallery-more">+{{ extraCount }}</span>
{% endif %}
</button>
{% if photoAlt %}
<button type="button" class="ap-media__alt-badge" @click.stop="showAlt = !showAlt" :aria-expanded="showAlt">ALT</button>
<div class="ap-media__alt-text" x-show="showAlt" x-cloak @click.stop>{{ photoAlt }}</div>
{% endif %}
</div>
{% endif %}
{% endfor %}
{# Lightbox modal — teleported to body to prevent overflow clipping #}
<template x-teleport="body">
<div x-show="lightbox" x-cloak @keydown.escape.window="lightbox = false" @click.self="lightbox = false" @touchstart="touchX = $event.changedTouches[0].clientX" @touchend="let dx = $event.changedTouches[0].clientX - touchX; if (dx < -50) idx = (idx + 1) % {{ totalPhotos }}; else if (dx > 50) idx = (idx - 1 + {{ totalPhotos }}) % {{ totalPhotos }}" class="ap-lightbox" role="dialog" aria-modal="true">
<button type="button" @click="lightbox = false" class="ap-lightbox__close" aria-label="Close">&times;</button>
{% if totalPhotos > 1 %}
<button type="button" @click="idx = (idx - 1 + {{ totalPhotos }}) % {{ totalPhotos }}" class="ap-lightbox__prev" aria-label="Previous image">&lsaquo;</button>
{% endif %}
<img :src="[{% for photo in item.photo %}'{{ photo.url if photo.url else photo }}'{% if not loop.last %},{% endif %}{% endfor %}][idx]"
:alt="[{% for photo in item.photo %}'{{ (photo.alt if photo.alt else '') | replace(\"'\", \"\\'\") }}'{% if not loop.last %},{% endif %}{% endfor %}][idx]"
class="ap-lightbox__img">
{% if totalPhotos > 1 %}
<button type="button" @click="idx = (idx + 1) % {{ totalPhotos }}" class="ap-lightbox__next" aria-label="Next image">&rsaquo;</button>
<div class="ap-lightbox__counter" x-text="(idx + 1) + ' / ' + {{ totalPhotos }}"></div>
{% endif %}
</div>
</template>
</div>
{% endif %}
{# Video embed #}
{% if item.video and item.video.length > 0 %}
<div class="ap-card__video">
<video controls preload="metadata" src="{{ item.video[0] }}">
{{ __("activitypub.reader.videoNotSupported") }}
</video>
</div>
{% endif %}
{# Audio player #}
{% if item.audio and item.audio.length > 0 %}
<div class="ap-card__audio">
<audio controls preload="metadata" src="{{ item.audio[0] }}">
{{ __("activitypub.reader.audioNotSupported") }}
</audio>
</div>
{% endif %}