// // Copyright 2026 DXOS.org // import React, { useState } from 'react'; import { mx } from '@dxos/ui-theme'; import { type ThemedClassName } from '../../util'; export type MediaKind = 'video' | 'audio'; export type MediaFit = 'cover' | 'contain' | 'fill' | 'none' | 'scale-down'; /** Static map to keep Tailwind's class scanner happy (no dynamic `object-${fit}`). */ const FIT_CLASS: Record = { 'cover': 'object-cover', 'contain': 'object-contain', 'fill': 'object-fill', 'none': 'object-none', 'scale-down': 'object-scale-down', }; const VIDEO_EXTENSIONS = ['.mp4', '.webm', '.ogv', '.mov', '.m4v']; const AUDIO_EXTENSIONS = ['.mp3', '.wav', '.ogg', '.m4a', '.aac', '.flac']; /** iframe sandbox flags compatible with typical oEmbed-style players. */ const DEFAULT_IFRAME_SANDBOX = 'allow-scripts allow-same-origin allow-presentation'; /** * Best-effort detection of `video` vs `audio` from a media URL. * Inspects the pathname's extension (ignoring query/hash). Returns `undefined` * when the URL doesn't look like a recognised media file — callers should * default to 'video' or render a fallback (e.g. iframe / img). */ export const detectMediaKind = (src: string): MediaKind | undefined => { // Strip query and hash, then take the last path segment's extension. const pathname = src.split(/[?#]/, 1)[0]!; const lower = pathname.toLowerCase(); if (VIDEO_EXTENSIONS.some((extension) => lower.endsWith(extension))) { return 'video'; } if (AUDIO_EXTENSIONS.some((extension) => lower.endsWith(extension))) { return 'audio'; } return undefined; }; /** * Heuristic match for URLs that should render as native `