Feature: media in replies — rich replies phase 2 (klonkt-demo-c7f)
Drop, paste or pick images/audio/video in the reply editor; they upload, show
as removable chips, travel as AS2 attachments on the federated Note, and render
in the thread.
- POST /posts/upload-reply-media (requireSiteManager): image/audio/video by
extension AND mimetype, stored as-is under /media/reply-media/ (no transcode;
a reply attachment is not a track), 32MB cap, returns {url, mediaType, name}.
- Editor: paperclip button + hidden file input (the mobile path), paste-files
and drag/drop handlers, busy/error chips, image thumbnails, max 4, hidden
attachments JSON field. Media-only submit allowed (text no longer required
when something is attached).
- deliverReply({attachments}): re-validates server-side — own /media/ paths
only (the upload route is the sole producer, remote URLs rejected),
image|audio|video mimetypes, capped at 4; stored as JSON on ap_outbox
(additive column). Dedup guard now includes attachments so two media-only
replies to the same parent are distinct from each other but double-submits
still dedup.
- buildNote reply branch: attachment array with Image/Audio/Video types and
absolute URLs. getInteractions passes media through; fedi-node renders it
(img/audio/video) for visitors too, loading the stylesheet when the
owner-only editor is not on the page.
3 new tests (foreign-URL and type rejection, typed absolute Note attachments,
media-only allowed, only-invalid rejected); 91 green. Browser-verified end to
end: real upload via the endpoint, paste-event -> chip with thumbnail ->
submit -> ap_outbox row with content+language+attachments -> media rendered in
the thread -> /ap/notes/<id> serves the typed absolute attachment.
Co-Authored-By: Claude Opus 4.8 <noreply@…>