Changeset 6dd26e5 in Klonkt for src/views


Ignore:
Timestamp:
07/30/2026 11:48:53 AM (6 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
1f640ff
Parents:
67f7150
Message:

De audio-tegel opgevrolijkt: golfvorm als thumbnail en een echte titel

Een audiopost uit Shaer was op Klonkt een kale gradient met (untitled)
(Robins schermafdruk, 30-7). Twee dingen tegelijk gefixt.

De thumbnail: de upload-leg tekent bij audio nu een golfvorm met de
gebundelde ffmpeg (showwavespic, wit op transparant) naar
<naam>.poster.png, precies zoals video zijn stilstaand beeld krijgt. Die
golfvorm rijdt mee als data-poster op de audio-tag (voor de tegels), in
c2s_attachments en als AS2-icon op het gefedereerde Audio-attachment. De
tegel toont hem OVER de eigen hue-gradient, dus elke audiopost houdt
zijn kleur en krijgt zijn vorm.

De titel: C2S-posts hebben geen titel, dus elke Shaer-post toonde
(untitled). Tegels en kaarten leiden de titel nu af: eerst de eigen
woorden van de post (afgekapt op 64), anders een vriendelijk typelabel
(muzieknoot audio, pijl video), en pas als er echt niets is (untitled).
Een tegel die zijn beeld al toont forceert geen label meer.

Changed files:
src/routes/activitypub.js

  • uploadMedia: audio/* -> showwavespic-golfvorm naar .poster.png (best-effort, zelfde patroon als de videoposter)

src/services/ActivityPubService.js

  • c2sCreatePost: posterExt per type (video .jpg, audio .png); de audio-tag krijgt data-poster

src/views/partials/post-tile.ejs

  • audio-detectie + golfvorm over de gradient; titelcascade (titel -> tekst -> typelabel)

src/views/partials/post-card.ejs

  • zelfde spiegel: golfvorm als cover met eigen accent-gradient, zelfde titelcascade

src/assets/css/style.css

  • .grid-tile-wave: contain, padding, transparant op de gradient

test/c2s-compose.test.js

  • audio-golfvormketen: data-poster op de tag, poster in de store, icon op het gefedereerde attachment

remarks: bestaande audioposts kregen hun upload voor deze code en hebben
dus nog geen golfvorm-bestand; die tonen nu wel het muzieknoot-label. Een
verse audiopost laat het hele effect zien. ffmpeg-commando en beide
templates hier lokaal gesmoke-test (RGBA-png, render ok), 345 tests
groen.

-robo
Co-Authored-By: Claude Opus 4.8 <noreply@…>

Location:
src/views/partials
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • src/views/partials/post-card.ejs

    r67f7150 r6dd26e5  
    1919// Content-derived fallback (see post-tile.ejs): a C2S post's media lives in
    2020// its content, and the card fronts it from there.
    21 let _cVideo = null, _cPoster = null, _cImg = null;
     21let _cVideo = null, _cPoster = null, _cImg = null, _cAudio = false, _cWave = null;
    2222if (!_realCover && post.content) {
    2323  const vm = String(post.content).match(/<video[^>]*\ssrc="([^"]+)"[^>]*>/i);
    2424  if (vm) { _cVideo = vm[1]; const pm = vm[0].match(/poster="([^"]+)"/i); if (pm) _cPoster = pm[1]; }
    25   else { const im = String(post.content).match(/<img[^>]*\ssrc="([^"]+)"/i); if (im) _cImg = im[1]; }
    26 }
    27 const _hasCover  = _realCover || !!_cVideo || !!_cImg;
     25  else {
     26    const im = String(post.content).match(/<img[^>]*\ssrc="([^"]+)"/i);
     27    if (im) _cImg = im[1];
     28    else {
     29      // An audio post (Robins vraag, 30-7): the upload leg drew a waveform
     30      // (data-poster on the tag); the card fronts it like a cover.
     31      const am = String(post.content).match(/<audio[^>]*>/i);
     32      if (am) { _cAudio = true; const wm = am[0].match(/data-poster="([^"]+)"/i); if (wm) _cWave = wm[1]; }
     33    }
     34  }
     35}
     36const _hasCover  = _realCover || !!_cVideo || !!_cImg || !!_cWave;
     37// A C2S post has no title: fall back to the post's own words, then to a
     38// friendly type label instead of "(zonder titel)".
     39let _title = post.title || '';
     40if (!_title && post.content) {
     41  const _txt = String(post.content).replace(/<[^>]*>/g, ' ').replace(/&[a-zA-Z#0-9]+;/g, ' ').replace(/\s+/g, ' ').trim();
     42  if (_txt) _title = _txt.length > 64 ? _txt.slice(0, 63).replace(/\s+\S*$/, '') + '…' : _txt;
     43}
     44if (!_title) _title = _cAudio ? '♪ audio' : _cVideo ? '▶ video' : '(zonder titel)';
    2845const _typeLabel = (post.type && post.type !== 'overig' && post.type !== 'post') ? post.type : '';
    2946const _isPinned  = !!post.pinned;
     
    5774    <a class="post-list-cover<%= post.nsfw ? ' nsfw-media' : '' %>" href="<%= _href %>"
    5875       <% 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"<% } %>
    59        aria-label="<%= post.title || '(zonder titel)' %>" tabindex="-1">
     76       aria-label="<%= _title %>" tabindex="-1">
    6077      <% if (post.cover_image_url) { %><img src="<%= thumb(post.cover_image_url, 480) %>"
    6178           srcset="<%= thumb(post.cover_image_url, 320) %> 320w, <%= thumb(post.cover_image_url, 640) %> 640w, <%= thumb(post.cover_image_url, 1280) %> 1280w"
    6279           sizes="(min-width: 768px) 120px, 100vw"
    6380           alt="" loading="lazy" decoding="async"<% if (post.cover_video_url) { %> data-ios-mp4="<%= post.cover_video_url %>"<% } %>>
    64       <% } else if (post.cover_video_url) { %><video src="<%= post.cover_video_url %>" poster="<%= thumb(post.cover_video_url, 480) %>" autoplay loop muted playsinline></video><% } else if (_cVideo) { %><video src="<%= _cVideo %>"<% if (_cPoster) { %> poster="<%= _cPoster %>"<% } %> autoplay loop muted playsinline preload="metadata"></video><% } else if (_cImg) { %><img src="<%= _cImg %>" alt="" loading="lazy" decoding="async"><% } %>
     81      <% } else if (post.cover_video_url) { %><video src="<%= post.cover_video_url %>" poster="<%= thumb(post.cover_video_url, 480) %>" autoplay loop muted playsinline></video><% } else if (_cVideo) { %><video src="<%= _cVideo %>"<% if (_cPoster) { %> poster="<%= _cPoster %>"<% } %> autoplay loop muted playsinline preload="metadata"></video><% } else if (_cImg) { %><img src="<%= _cImg %>" alt="" loading="lazy" decoding="async"><% } else if (_cWave) { %><img class="post-cover-wave" src="<%= _cWave %>" alt="" loading="lazy" decoding="async"><% } %>
    6582      <% if (post.nsfw) { %><span class="nsfw-veil"><%- include('nsfw-veil', { cw: post.content_warning }) %></span><% } %>
    6683    </a>
     
    6885    <a class="post-list-cover nsfw-media" href="<%= _href %>"
    6986       <% 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"<% } %>
    70        aria-label="<%= post.title || '(zonder titel)' %>" tabindex="-1">
     87       aria-label="<%= _title %>" tabindex="-1">
    7188      <span class="nsfw-veil"><%- include('nsfw-veil', { cw: post.content_warning }) %></span>
    7289    </a>
     
    89106      <a href="<%= _href %>"
    90107         <% 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"<% } %>>
    91         <%= post.title || '(zonder titel)' %>
     108        <%= _title %>
    92109      </a>
    93110    </h3>
     
    189206  }
    190207
     208  /* Audio cover (Shaer): white-on-transparent waveform on its own accent
     209     gradient, contained so the shape reads at 120px and full-bleed alike. */
     210  /* (see also .grid-tile-wave in style.css for the grid tiles) */
    191211  /* Pinned indicator */
    192212  .post-list-item.is-pinned .post-list-title a {
     
    233253
    234254/* ============================================================
     255   AUDIO COVER (shared) — Shaer audio post (Robins vraag, 30-7):
     256   the waveform PNG is white on transparent; give it its own accent
     257   gradient and contain it so the shape reads at 120px and full-bleed.
     258   ============================================================ */
     259.post-list-cover .post-cover-wave {
     260  width: 100%; height: 100%;
     261  object-fit: contain !important;
     262  padding: 14% 8%;
     263  background-image: linear-gradient(135deg,
     264      color-mix(in srgb, var(--accent, #888) 26%, var(--paper-2, var(--paper))) 0%,
     265      color-mix(in srgb, var(--accent, #888) 8%, var(--paper-2, var(--paper))) 100%);
     266}
     267
     268/* ============================================================
    235269   PIN + DRAFT INDICATORS (shared)
    236270   ============================================================ */
  • src/views/partials/post-tile.ejs

    r67f7150 r6dd26e5  
    1111// content instead, like the Cirkel card: first <video> (with its poster) or
    1212// first <img>.
    13 let _cVideo = null, _cPoster = null, _cImg = null;
     13let _cVideo = null, _cPoster = null, _cImg = null, _cAudio = false, _cWave = null;
    1414if (!_hasCover && post.content) {
    1515  const vm = String(post.content).match(/<video[^>]*\ssrc="([^"]+)"[^>]*>/i);
    1616  if (vm) { _cVideo = vm[1]; const pm = vm[0].match(/poster="([^"]+)"/i); if (pm) _cPoster = pm[1]; }
    17   else { const im = String(post.content).match(/<img[^>]*\ssrc="([^"]+)"/i); if (im) _cImg = im[1]; }
     17  else {
     18    const im = String(post.content).match(/<img[^>]*\ssrc="([^"]+)"/i);
     19    if (im) _cImg = im[1];
     20    else {
     21      // An audio post (Robins vraag, 30-7): the waveform the upload leg drew
     22      // (data-poster) goes OVER the gradient, so the tile keeps its own hue.
     23      const am = String(post.content).match(/<audio[^>]*>/i);
     24      if (am) { _cAudio = true; const wm = am[0].match(/data-poster="([^"]+)"/i); if (wm) _cWave = wm[1]; }
     25    }
     26  }
    1827}
    19 const _showCover = _hasCover || !!_cVideo || !!_cImg;
     28const _showCover = _hasCover || !!_cVideo || !!_cImg;   // the wave keeps the gradient
     29// A C2S post has no title: fall back to the post's own words, and for a
     30// media-only post to a friendly type label instead of "(untitled)". A tile
     31// that already shows its picture needs no forced label at all.
     32let _title = post.title || '';
     33if (!_title && post.content) {
     34  const _txt = String(post.content).replace(/<[^>]*>/g, ' ').replace(/&[a-zA-Z#0-9]+;/g, ' ').replace(/\s+/g, ' ').trim();
     35  if (_txt) _title = _txt.length > 64 ? _txt.slice(0, 63).replace(/\s+\S*$/, '') + '…' : _txt;
     36}
     37if (!_title) _title = _cAudio ? '♪ audio' : (_cVideo || _cImg) ? '' : '(untitled)';
    2038const _typeLabel = (post.type && post.type !== 'post') ? post.type : '';
    2139const _src = post.source_name || '';   // bron-site (cirkel-feed: van welke site komt deze post)
     
    3048   <% 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"<% } %>>
    3149
    32   <% if (post.cover_image_url) { %><img class="grid-tile-img" src="<%= thumb(post.cover_image_url, 480) %>" alt="" loading="lazy" decoding="async"<% if (post.cover_video_url) { %> data-ios-mp4="<%= post.cover_video_url %>"<% } %>><% } else if (post.cover_video_url) { %><video class="grid-tile-img" src="<%= post.cover_video_url %>" poster="<%= thumb(post.cover_video_url, 480) %>" autoplay loop muted playsinline></video><% } else if (_cVideo) { %><video class="grid-tile-img" src="<%= _cVideo %>"<% if (_cPoster) { %> poster="<%= _cPoster %>"<% } %> autoplay loop muted playsinline preload="metadata"></video><% } else if (_cImg) { %><img class="grid-tile-img" src="<%= _cImg %>" alt="" loading="lazy" decoding="async"><% } %>
     50  <% if (post.cover_image_url) { %><img class="grid-tile-img" src="<%= thumb(post.cover_image_url, 480) %>" alt="" loading="lazy" decoding="async"<% if (post.cover_video_url) { %> data-ios-mp4="<%= post.cover_video_url %>"<% } %>><% } else if (post.cover_video_url) { %><video class="grid-tile-img" src="<%= post.cover_video_url %>" poster="<%= thumb(post.cover_video_url, 480) %>" autoplay loop muted playsinline></video><% } else if (_cVideo) { %><video class="grid-tile-img" src="<%= _cVideo %>"<% if (_cPoster) { %> poster="<%= _cPoster %>"<% } %> autoplay loop muted playsinline preload="metadata"></video><% } else if (_cImg) { %><img class="grid-tile-img" src="<%= _cImg %>" alt="" loading="lazy" decoding="async"><% } else if (_cWave) { %><img class="grid-tile-img grid-tile-wave" src="<%= _cWave %>" alt="" loading="lazy" decoding="async"><% } %>
    3351
    3452  <% if (_typeLabel) { %>
     
    4361  <% if (_hasCover) { %>
    4462    <div class="grid-tile-overlay">
    45       <strong><%= post.title || '(untitled)' %></strong>
     63      <strong><%= _title %></strong>
    4664      <% if (_src) { %><span class="grid-tile-overlay-src">van <%= _src %></span><% } %>
    4765    </div>
    4866  <% } else { %>
    49     <span class="grid-tile-title"><%= post.title || '(untitled)' %></span>
     67    <span class="grid-tile-title"><%= _title %></span>
    5068    <% if (_src) { %><span class="grid-tile-gradient-src">van <%= _src %></span><% } %>
    5169  <% } %>
Note: See TracChangeset for help on using the changeset viewer.