Files
BRETT/shared/utils/attachments.ts
T
FliesentischKingandDevin fe9e723c72 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>
2026-10-04 03:06:11 +02:00

46 lines
1.4 KiB
TypeScript

export interface AttachmentAudioMeta {
title: string | null
artist: string | null
album: string | null
voice?: boolean
}
export interface CommentAttachment {
id: number
name: string
url: string
mimeType: string | null
size: number | null
cover?: string | null
meta?: AttachmentAudioMeta | null
}
export const MAX_COMMENT_ATTACHMENTS = 8
export const MAX_ATTACHMENT_SIZE = 200 * 1024 * 1024 // 200 MB
export function isImageAttachment(a: { mimeType?: string | null, name?: string }): boolean {
return !!a.mimeType?.startsWith('image/')
|| (!!a.name && /\.(avif|bmp|gif|jpe?g|png|svg|webp)$/i.test(a.name))
}
export function isAudioAttachment(a: { mimeType?: string | null, name?: string }): boolean {
return !!a.mimeType?.startsWith('audio/')
|| (!!a.name && /\.(mp3|flac|ogg|wav|m4a|aac|opus)$/i.test(a.name))
}
export function isVideoAttachment(a: { mimeType?: string | null, name?: string }): boolean {
return !!a.mimeType?.startsWith('video/')
|| (!!a.name && /\.(mp4|webm|mov|mkv|avi|m4v|mpg|mpeg|ts|mts)$/i.test(a.name))
}
export function isVoiceAttachment(a: { meta?: { voice?: boolean } | null }): boolean {
return a.meta?.voice === true
}
export function formatFileSize(bytes: number | null | undefined): string {
if (!bytes || bytes <= 0) return ''
return bytes >= 1024 * 1024
? `${(bytes / 1024 / 1024).toFixed(1)} MB`
: `${Math.max(1, Math.round(bytes / 1024))} KB`
}