import { usePreferencesStore } from '../stores/preferences-store' import type { AppSettings } from '../api/types' /** * Builds styling for vidstack's renderer based on the user's * subtitle preferences. Font size is returned as an inline CSS custom * property because Tailwind can't generate classes for arbitrary pixel * values that only exist at runtime. The other properties use Tailwind * arbitrary-variant selectors (`[&_[data-cue]]:...`) to target the cue * elements that vidstack injects. * * The same builder powers the live preview in Settings so what users see * there matches what they'll see in the player. */ export type SubtitleStyle = { className: string; style: React.CSSProperties } type S = Pick< AppSettings, 'subtitleFontSize' | 'subtitleFontFamily' | 'subtitleBackground' | 'subtitleEdge' | 'subtitlePosition' | 'subtitleColor' > const FAMILY_CLASS: Record = { sans: '[&_[data-cue]]:font-sans', serif: '[&_[data-cue]]:[font-family:var(--font-display)]', mono: '[&_[data-cue]]:font-mono', } const BG_CLASS: Record = { none: '', subtle: '[&_[data-cue]]:bg-black/55 [&_[data-cue]]:rounded-md [&_[data-cue]]:px-3 [&_[data-cue]]:py-1', solid: '[&_[data-cue]]:bg-black [&_[data-cue]]:px-3 [&_[data-cue]]:py-1', } const EDGE_CLASS: Record = { none: '', shadow: '[&_[data-cue]]:[text-shadow:0_2px_6px_rgba(0,0,0,0.85)]', outline: '[&_[data-cue]]:[text-shadow:-1px_-1px_0_#000,1px_-1px_0_#000,-1px_1px_0_#000,1px_1px_0_#000,0_2px_4px_rgba(0,0,0,0.7)]', } const COLOR_CLASS: Record = { white: '[&_[data-cue]]:text-white', yellow: '[&_[data-cue]]:text-yellow-300', cyan: '[&_[data-cue]]:text-cyan-200', } const POSITION_CLASS: Record = { bottom: 'bottom-32', top: 'top-24', } /** Compose the className + inline style for the Captions container. */ export function subtitleClasses(s: S): SubtitleStyle { const className = [ 'absolute inset-x-0 px-7 pointer-events-none text-center font-semibold tracking-tight', '[&_[data-cue]]:inline-block [&_[data-cue]]:leading-tight', POSITION_CLASS[s.subtitlePosition], FAMILY_CLASS[s.subtitleFontFamily], BG_CLASS[s.subtitleBackground], EDGE_CLASS[s.subtitleEdge], COLOR_CLASS[s.subtitleColor], ] .filter(Boolean) .join(' ') const style: React.CSSProperties = { '--cue-font-size': `${s.subtitleFontSize}px`, '--cue-font-size-md': `${Math.round(s.subtitleFontSize * 1.36)}px`, } as React.CSSProperties return { className, style } } /** Hook: composes subtitle styling for live use, subscribed to preference changes. */ export function useSubtitleStyles(): SubtitleStyle { const subtitleFontSize = usePreferencesStore(s => s.subtitleFontSize) const subtitleFontFamily = usePreferencesStore(s => s.subtitleFontFamily) const subtitleBackground = usePreferencesStore(s => s.subtitleBackground) const subtitleEdge = usePreferencesStore(s => s.subtitleEdge) const subtitlePosition = usePreferencesStore(s => s.subtitlePosition) const subtitleColor = usePreferencesStore(s => s.subtitleColor) return subtitleClasses({ subtitleFontSize, subtitleFontFamily, subtitleBackground, subtitleEdge, subtitlePosition, subtitleColor, }) }