source: Klonkt/src/views/partials/post-tile.ejs@ 3487567

main
Last change on this file since 3487567 was f79a471, checked in by Robin Genis <roboburr@…>, 2 months ago

feat(images): on-demand lanczos cover thumbnails for crisp grid/list

High-res covers (esp. line-art) looked jagged because the browser downscaled them to
the grid/list size. Downscale server-side with ffmpeg lanczos to a small cached WebP
instead. No re-upload/backfill: reads the existing original lazily on first request.

  • services/ThumbnailService.js — ffmpeg lanczos downscale -> WebP, disk-cached
  • server.js — GET /media/thumb/:w/* route (whitelist 320/480/640) before the /media static
  • middleware/render.js — thumb(url,w) helper (rewrites local /media covers)
  • views/partials/post-tile.ejs (grid 480) + post-card.ejs (list 320)
  • Property mode set to 100644
File size: 2.9 KB
RevLine 
[7bc636b]1<%
2// Square Instagram-style tile for the .feed-grid view (v9 pattern).
3// - With cover → background-image, dark gradient overlay reveals title on hover/touch
4// - No cover → .grid-tile-gradient with the title centered (random hue per tile)
5const _tileHue = ((post.id || post.slug || '0').charCodeAt(0) * 7) % 360;
6const _isPinned = !!post.pinned;
[c7c7f02]7const _isBoost = !!post.isBoost;
[7bc636b]8const _hasCover = !!post.cover_image_url;
9const _typeLabel = (post.type && post.type !== 'post') ? post.type : '';
[1f0c85b]10const _src = post.source_name || ''; // bron-site (cirkel-feed: van welke site komt deze post)
[7963a6e]11const _base = (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : '';
[43dbf9c]12const _external = post.external_url || null;
13const _href = _external || (_base + '/' + post.slug);
14const _ext = !!_external;
[7bc636b]15%>
[837fc9c]16<a class="grid-tile<%= _hasCover ? '' : ' grid-tile-gradient' %><%= (_isPinned || _isBoost) ? ' is-pinned' : '' %><%= post.nsfw ? ' nsfw-media' : '' %>"
[7963a6e]17 href="<%= _href %>"
[2eca083]18 style="--tile-hue: <%= _tileHue %>;"
[43dbf9c]19 <% if (_ext) { %>target="_blank" rel="noopener"<% } else { %>hx-get="<%= _href %>?partial=1" hx-target="#pcms-main" hx-swap="innerHTML" hx-push-url="<%= _href %>" hx-indicator="#pcms-loading"<% } %>>
[7bc636b]20
[f79a471]21 <% if (_hasCover) { %><img class="grid-tile-img" src="<%= thumb(post.cover_image_url, 480) %>" alt="" loading="lazy" decoding="async"><% } %>
[2eca083]22
[7bc636b]23 <% if (_typeLabel) { %>
24 <span class="grid-tile-type"><%= _typeLabel %></span>
25 <% } %>
[c7c7f02]26 <% if (_isBoost) { %>
27 <span class="grid-tile-pin grid-tile-boost"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m17 2 4 4-4 4"/><path d="M3 11v-1a4 4 0 0 1 4-4h14"/><path d="m7 22-4-4 4-4"/><path d="M21 13v1a4 4 0 0 1-4 4H3"/></svg>Boost</span>
28 <% } else if (_isPinned) { %>
[6bee3ec]29 <span class="grid-tile-pin"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 17v5"/><path d="M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z"/></svg>Pinned</span>
[7bc636b]30 <% } %>
31
32 <% if (_hasCover) { %>
33 <div class="grid-tile-overlay">
34 <strong><%= post.title || '(untitled)' %></strong>
[1f0c85b]35 <% if (_src) { %><span class="grid-tile-overlay-src">van <%= _src %></span><% } %>
[7bc636b]36 </div>
37 <% } else { %>
38 <span class="grid-tile-title"><%= post.title || '(untitled)' %></span>
[1f0c85b]39 <% if (_src) { %><span class="grid-tile-gradient-src">van <%= _src %></span><% } %>
[7bc636b]40 <% } %>
[b7d4458]41 <% if (post.nsfw) { %><div class="nsfw-veil"><span class="nsfw-veil-label">🔞 <%= post.content_warning || t('post.nsfw_warning') %></span><span class="nsfw-veil-btn"><%= t('post.nsfw_show') %></span></div><% } %>
[7bc636b]42</a>
Note: See TracBrowser for help on using the repository browser.