Render and play InstantPageBlock.audio in the InstantPage V2 renderer
(rich-data message bubbles + the rich send preview), where audio was
previously an inert grey placeholder.
- New InstantPageV2AudioContentNode replicates the standard music message
bubble (ChatMessageInteractiveFileNode's music layout): a SemanticStatusNode
control (album art via playerAlbumArt + play/pause) with a download/progress
overlay, title + "duration · performer" lines, exact fonts/colors from
theme.chat.message.{incoming|outgoing}. Tap is driven by a
UITapGestureRecognizer (ASControl .touchUpInside is cancelled by the chat
ListView's gesture system). V1's InstantPageAudioNode is unchanged.
- Playback runs on InstantPageMediaPlaylist with a discriminated, message-scoped
InstantPageMediaPlaylistId (.instantPage / .richMessage) so concurrent
rich-message audio bubbles don't collide; the big control's play/pause comes
from filteredPlaylistState, the overlay's download/progress from
messageMediaFileStatus, and fetch goes through the fetch manager.
- Rich-message audio fetches via a MessageReference (threaded through the V2
render context) instead of the synthesized webpage; FetchedMediaResource's
.message revalidation arm now also searches RichTextMessageAttribute instant
pages, so a stale instant-page audio/image reference can recover. Corrected a
dormant inverted InstantPagePlaylistLocation.isEqual.
- New .mediaAudio laid-out item + layout/reveal-cost arms; the audio block lays
out full-width at the file node's music normHeight.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port V1's collage and slideshow InstantPage blocks into the V2 renderer
(previously grey-box placeholders).
- Collage: layoutCollage computes the mosaic (MosaicLayout, the grouped-message
engine) over inner image/video sizes and flattens it into existing top-level
.mediaImage/.mediaVideo items + a caption, so gallery / reveal-cost / registry
/ hidden-media all handle the cells with no collage-specific code. Right-edge
cells bleed 4pt for the bubble's rounded clip.
- Slideshow: a new .slideshow laid-out item + InstantPageV2SlideshowView, an
eager paged carousel (UIScrollView + PageControlNode) of InstantPageImageNode
pages, wired through frame/offsetBy/collectMedias/stableId/reuse/makeItemView
and the reveal-cost non-text list.
- Gallery transitions generalized onto InstantPageItemView via
instantPageTransitionNode(for:)/instantPageUpdateHiddenMedia(_:) (default
nil/no-op; explicit per-class witnesses on the 4 static media views, the
slideshow forwards to its live pages) so the multi-media slideshow can
participate alongside single-media views.
Docs: document both in docs/instantpage-richtext.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Design and plan, then the implementation: extract a shared
openInstantPageMedia helper for V1+V2 reuse, add typed media item
cases + allMedias() helper, introduce InstantPageV2RenderContext and
thread it through the view tree, add four media wrapper views, route
layout dispatch through typed media items, let the chat bubble build
the render context so real media renders, wire gallery tap routing,
cache V2View by message id + stableVersion, add stable-id identity +
per-class update(item:theme:), reuse item views on re-layouts via
stable-id harvest, preserve TextView clipping inset on reuse, and let
the use site control media-reference construction.