Add a shared sanitizeUrl() utility that validates URLs are well-formed
https: before they reach href attributes, window.open(), or openUrl().
Apply sanitization across all components that render untrusted URLs:
- CalendarEventDetailPage: r-tag links
- ZapstoreAppContent: url and repository tags
- ZapstoreReleaseContent: asset url tags passed to openUrl()
- AppHandlerContent: web handler tags and metadata.website
- NsiteCard: source tag
- GitRepoCard: web tag URLs passed to openUrl()
- FileMetadataContent: url tag used in download href
- ProfilePage: metadata.website (tighten weak startsWith check)
- useUserStatus: r-tag URL
Document sanitizeUrl usage in AGENTS.md for future agent use.
- Add AudioVisualizer component: mirrors VideoPlayer chrome exactly (same overlay controls, progress bar, hide-on-idle, scroll-pause) with a canvas sinewave and author avatar in the centre
- Wire audio into NoteCard (kind 1 notes), FileMetadataContent (kind 1063), and ComposeBox preview
- Extract shared utilities: formatTime, usePlayerControls hook, and mediaUrls (regexes + mimeFromExt) to eliminate duplication across VideoPlayer, AudioVisualizer, NoteCard, NoteContent, ComposeBox, and EmbeddedNote
- Fix ComposeBox: audio URLs now get correct imeta MIME tags and are excluded from link-embed detection
Description now appears in its own rounded bg-secondary/50 card
below the media element, similar to YouTube's description box.
Media renders unwrapped at full width.
Each MIME type now renders inside a rounded border card. The media
is the hero at the top and the description sits in a separated
footer section below. When there's no description, images and
videos skip the card wrapper to stay clean.
Media (webxdc embed, image, video, audio) is now the hero element
rendered first. Descriptions appear below as captions. Audio gets
a styled card with track name, file size, and download button.
Generic files get a polished card with MIME label and size info.
Kind 1063 file metadata events now dispatch to FileMetadataContent
which renders based on MIME type: webxdc apps get WebxdcEmbed,
images get ImageGallery, videos get VideoPlayer, audio gets a
native player, and unknown types show a download link.