diff --git a/frontend/src/components/InputAndControlsPanel.tsx b/frontend/src/components/InputAndControlsPanel.tsx index a6cbbcb1..84d096bc 100644 --- a/frontend/src/components/InputAndControlsPanel.tsx +++ b/frontend/src/components/InputAndControlsPanel.tsx @@ -116,6 +116,7 @@ export function InputAndControlsPanel({ // Check if this pipeline supports multiple input modes const isMultiMode = pipelineIsMultiMode(pipelineId); + const promptLabel = pipelineId === "vibevoice" ? "Text input" : "Prompts"; useEffect(() => { if (videoRef.current && localStream) { @@ -288,7 +289,7 @@ export function InputAndControlsPanel({ return (
-

Prompts

+

{promptLabel}

{isEditMode && ( Editing diff --git a/frontend/src/components/VideoOutput.tsx b/frontend/src/components/VideoOutput.tsx index 4ed6bb61..e60564aa 100644 --- a/frontend/src/components/VideoOutput.tsx +++ b/frontend/src/components/VideoOutput.tsx @@ -14,6 +14,7 @@ interface VideoOutputProps { onPlayPauseToggle?: () => void; onStartStream?: () => void; onVideoPlaying?: () => void; + isAudioOnly?: boolean; } export function VideoOutput({ @@ -27,17 +28,60 @@ export function VideoOutput({ onPlayPauseToggle, onStartStream, onVideoPlaying, + isAudioOnly = false, }: VideoOutputProps) { const videoRef = useRef(null); + const audioRef = useRef(null); const [showOverlay, setShowOverlay] = useState(false); const [isFadingOut, setIsFadingOut] = useState(false); + const [isAudioPlaying, setIsAudioPlaying] = useState(false); const overlayTimeoutRef = useRef(null); useEffect(() => { - if (videoRef.current && remoteStream) { + if (!isAudioOnly && videoRef.current && remoteStream) { videoRef.current.srcObject = remoteStream; } - }, [remoteStream]); + }, [remoteStream, isAudioOnly]); + + useEffect(() => { + if (isAudioOnly && audioRef.current && remoteStream) { + audioRef.current.srcObject = remoteStream; + } + }, [remoteStream, isAudioOnly]); + + // Notify when audio starts playing (parity with video) + useEffect(() => { + if (!isAudioOnly) return; + const audio = audioRef.current; + if (!audio || !remoteStream) return; + + const handlePlaying = () => { + setIsAudioPlaying(true); + onVideoPlaying?.(); + }; + + const handlePause = () => { + setIsAudioPlaying(false); + }; + + const handleEnded = () => { + setIsAudioPlaying(false); + }; + + if (!audio.paused && audio.currentTime > 0 && !audio.ended) { + setIsAudioPlaying(true); + setTimeout(() => onVideoPlaying?.(), 0); + } + + audio.addEventListener("playing", handlePlaying); + audio.addEventListener("pause", handlePause); + audio.addEventListener("ended", handleEnded); + return () => { + audio.removeEventListener("playing", handlePlaying); + audio.removeEventListener("pause", handlePause); + audio.removeEventListener("ended", handleEnded); + }; + }, [isAudioOnly, onVideoPlaying, remoteStream]); // Listen for video playing event to notify parent useEffect(() => { @@ -132,7 +176,50 @@ export function VideoOutput({ Video Output - {remoteStream ? ( + {remoteStream && isAudioOnly ? ( +
+ {/* Hidden audio element for actual playback */} +