'use client' import { useEffect, useState } from 'react' import { PlayIcon, PauseIcon, EnterFullScreenIcon, ExitFullScreenIcon, } from '@radix-ui/react-icons' /** Formats milliseconds as mm:ss. */ function formatTime(ms: number): string { if (!Number.isFinite(ms) || ms < 0) return '0:00' const s = Math.floor(ms / 1000) const m = Math.floor(s / 60) return `${m}:${String(s % 60).padStart(2, '0')}` } const SPEED_OPTIONS = [1, 2, 4, 8] as const export type ReplayPlayerControlsProps = { isPlaying: boolean onPlayPause: () => void currentTimeMs: number totalTimeMs: number onSeek: (fraction: number) => void speed: number onSpeedChange: (speed: number) => void skipInactive: boolean onSkipInactiveChange: () => void onFullscreenRequest: () => void } /** * Custom session replay player controls with Ciphera branding. * Matches design: brand orange #FD5E0F, Plus Jakarta Sans, rounded-xl, neutral greys. */ export default function ReplayPlayerControls({ isPlaying, onPlayPause, currentTimeMs, totalTimeMs, onSeek, speed, onSpeedChange, skipInactive, onSkipInactiveChange, onFullscreenRequest, }: ReplayPlayerControlsProps) { const [isFullscreen, setIsFullscreen] = useState(false) const [isSeeking, setIsSeeking] = useState(false) const [seekValue, setSeekValue] = useState(0) const totalSec = totalTimeMs / 1000 const currentSec = currentTimeMs / 1000 const fraction = totalSec > 0 ? Math.min(1, Math.max(0, currentSec / totalSec)) : 0 const displayFraction = isSeeking ? seekValue : fraction useEffect(() => { const onFullscreenChange = () => { setIsFullscreen(!!document.fullscreenElement) } document.addEventListener('fullscreenchange', onFullscreenChange) return () => document.removeEventListener('fullscreenchange', onFullscreenChange) }, []) const handleSeekChange = (e: React.ChangeEvent) => { const v = parseFloat(e.target.value) const p = Number.isFinite(v) ? Math.max(0, Math.min(1, v)) : 0 setSeekValue(p) onSeek(p) } const handleSeekPointerDown = () => { setSeekValue(fraction) setIsSeeking(true) } const handleSeekPointerUp = () => setIsSeeking(false) return (
{/* * Progress bar / timeline */}
{formatTime(currentTimeMs)}
{displayFraction > 0 && displayFraction < 1 && (
)}
{formatTime(totalTimeMs)}
{/* * Buttons row */}
{/* * Play / Pause */} {/* * Speed pills */}
{SPEED_OPTIONS.map((s) => ( ))}
{/* * Skip inactive toggle */}
{/* * Fullscreen */}
) }