Media attachment cards with popover players and shared volume

- Audio/video attachments render as media cards (cover or icon,
  title/artist/size) instead of plain file chips; image attachments
  use 3:2 thumbnails matching the audio card height
- Players open in a Popover (autoplay on open) with a dimming
  overlay; AudioPlayerDialog/VideoPlayerDialog removed
- New useMediaVolume composable shares volume/mute between all
  audio players and Plyr via the 4brett-player storage key
- Player buttons and Plyr controls follow the theme border radius
- Portrait videos: aspect is applied even when metadata loaded
  before hydration, and the raw video is height-capped pre-JS

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
2026-10-04 03:06:11 +02:00
co-authored by Devin
parent cd04135463
commit fe9e723c72
6 changed files with 154 additions and 167 deletions
+12 -1
View File
@@ -4,6 +4,7 @@ import 'plyr/dist/plyr.css'
defineProps<{
src: string
autoplay?: boolean
}>()
const videoEl = ref<HTMLVideoElement | null>(null)
@@ -12,6 +13,11 @@ let player: Plyr | null = null
onMounted(async () => {
if (!videoEl.value) return
// SSR: Metadaten können vor der Hydration schon geladen sein —
// das loadedmetadata-Event feuert dann nicht mehr
if (videoEl.value.readyState >= HTMLMediaElement.HAVE_METADATA) {
onLoadedMetadata()
}
const { default: PlyrClass } = await import('plyr')
player = new PlyrClass(videoEl.value, {
disableContextMenu: false,
@@ -40,9 +46,10 @@ function onLoadedMetadata() {
<video
ref="videoEl"
:src="src"
:autoplay="autoplay"
playsinline
controls
class="rounded-md"
class="mx-auto max-h-[80vh] w-auto max-w-full rounded-md"
@loadedmetadata="onLoadedMetadata"
/>
</div>
@@ -53,4 +60,8 @@ function onLoadedMetadata() {
--plyr-color-main: var(--accent);
--plyr-border-radius: var(--radius-md);
}
.video-player :deep(.plyr__control--overlaid),
.video-player :deep(.plyr__control) {
border-radius: var(--radius-md);
}
</style>