Files
librechat.ai/components/Video.tsx
Danny Avila a0a74501c9 chore: bump dev packages, linting, logos (#521)
* chore: upgrade eslint to v9

* chore: update package dependencies in package.json and pnpm-lock.yaml

- Added `minimatch` and `serialize-javascript` dependencies with updated versions.
- Upgraded `ajv` to version 6.14.0.
- Removed outdated dependencies from pnpm-lock.yaml for better package management.

* feat: add Stripe logos to Companies section

- Introduced new company entry for Stripe in the Companies component, including both light and dark logo variants.
- Updated the Companies array to display 10 logos instead of 8.
- Adjusted TypeScript environment reference to point to the development types directory.
2026-03-02 18:18:50 -05:00

90 lines
2.9 KiB
TypeScript

import 'vidstack/styles/base.css'
import { cn } from '@/lib/utils'
import { MediaPlayer, MediaOutlet, useMediaRemote, useMediaStore } from '@vidstack/react'
import { Play } from 'lucide-react'
import { useState, useRef } from 'react'
/** Video embed component using vidstack. Supports poster overlays, gif-style autoplay, and lazy loading. */
export const Video = ({
src,
poster,
aspectRatio,
className,
gifStyle = false,
title,
}: {
src: string
poster?: string
aspectRatio?: number
gifStyle?: boolean
className?: string
title?: string
}) => {
const [panelDismissed, setPanelDismissed] = useState(false)
const mediaPlayerRef = useRef(null)
const remote = useMediaRemote(mediaPlayerRef)
const { duration } = useMediaStore(mediaPlayerRef)
const durationString = duration
? `${Math.floor(duration / 60)}:${Math.floor(duration % 60)} min`
: null
return (
<MediaPlayer
ref={mediaPlayerRef}
src={src}
controls={!gifStyle && panelDismissed}
autoplay={gifStyle}
muted={gifStyle}
loop={gifStyle}
load={gifStyle ? 'eager' : 'custom'}
playsinline={gifStyle}
aspectRatio={aspectRatio}
className={cn(
'my-4 overflow-hidden shadow-lg ring-1 ring-slate-700 bg-cover object-cover',
className,
)}
>
{gifStyle ? (
// Capture mouse events, they broke scrolling on iOS
<div className="absolute inset-0 z-10" />
) : panelDismissed ? null : (
// Overlay with play button and poster image
<div
role="button"
tabIndex={0}
aria-label={title ? `Play ${title}` : 'Play video'}
className="group cursor-pointer absolute inset-0 z-10 flex flex-col justify-center items-center bg-cover"
style={{
backgroundImage: poster ? `url(${poster})` : undefined,
}}
onMouseEnter={() => {
remote.startLoading()
}}
onClick={() => {
remote.play()
setPanelDismissed(true)
}}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault()
remote.play()
setPanelDismissed(true)
}
}}
>
<div className="p-3 md:p-6 rounded-full bg-black/75 group-hover:ring-8 ring-black/20 hover:bg-black/90 transition flex">
<Play className="size-6 text-white" />
</div>
<div className="mt-3 md:mt-6 md:opacity-0 group-hover:opacity-100 transition-opacity duration-300">
<span className="flex gap-2 text-xs md:text-sm font-semibold bg-black/90 text-white py-1 px-3 rounded-full">
{title && <span>{title}</span>}
{durationString && <span className="text-white/70">{durationString}</span>}
</span>
</div>
</div>
)}
<MediaOutlet />
</MediaPlayer>
)
}