import React, { useMemo } from 'react'; import { Track } from 'livekit-client'; import { AnimatePresence, motion } from 'motion/react'; import { type TrackReference, VideoTrack, useLocalParticipant, useTracks, useVoiceAssistant, } from '@livekit/components-react'; import { AgentAudioVisualizerBar } from '@/components/agents-ui/agent-audio-visualizer-bar'; import { cn } from '@/lib/shadcn/utils'; const MotionContainer = motion.create('div'); const ANIMATION_TRANSITION = { type: 'spring', stiffness: 675, damping: 75, mass: 1, }; const classNames = { // GRID // 2 Columns x 3 Rows grid: [ 'h-full w-full', 'grid gap-x-2 place-content-center', 'grid-cols-[1fr_1fr] grid-rows-[90px_1fr_90px]', ], // Agent // chatOpen: true, // hasSecondTile: true // layout: Column 1 / Row 1 // align: x-end y-center agentChatOpenWithSecondTile: ['col-start-1 row-start-1', 'self-center justify-self-end'], // Agent // chatOpen: true, // hasSecondTile: false // layout: Column 1 / Row 1 / Column-Span 2 // align: x-center y-center agentChatOpenWithoutSecondTile: ['col-start-1 row-start-1', 'col-span-2', 'place-content-center'], // Agent // chatOpen: false // layout: Column 1 / Row 1 / Column-Span 2 / Row-Span 3 // align: x-center y-center agentChatClosed: ['col-start-1 row-start-1', 'col-span-2 row-span-3', 'place-content-center'], // Second tile // chatOpen: true, // hasSecondTile: true // layout: Column 2 / Row 1 // align: x-start y-center secondTileChatOpen: ['col-start-2 row-start-1', 'self-center justify-self-start'], // Second tile // chatOpen: false, // hasSecondTile: false // layout: Column 2 / Row 2 // align: x-end y-end secondTileChatClosed: ['col-start-2 row-start-3', 'place-content-end'], }; export function useLocalTrackRef(source: Track.Source) { const { localParticipant } = useLocalParticipant(); const publication = localParticipant.getTrackPublication(source); const trackRef = useMemo( () => (publication ? { source, participant: localParticipant, publication } : undefined), [source, publication, localParticipant] ); return trackRef; } interface TileLayoutProps { chatOpen: boolean; } export function TileLayout({ chatOpen }: TileLayoutProps) { const { state: agentState, audioTrack: agentAudioTrack, videoTrack: agentVideoTrack, } = useVoiceAssistant(); const [screenShareTrack] = useTracks([Track.Source.ScreenShare]); const cameraTrack: TrackReference | undefined = useLocalTrackRef(Track.Source.Camera); const isCameraEnabled = cameraTrack && !cameraTrack.publication.isMuted; const isScreenShareEnabled = screenShareTrack && !screenShareTrack.publication.isMuted; const hasSecondTile = isCameraEnabled || isScreenShareEnabled; const animationDelay = chatOpen ? 0 : 0.15; const isAvatar = agentVideoTrack !== undefined; const videoWidth = agentVideoTrack?.publication.dimensions?.width ?? 0; const videoHeight = agentVideoTrack?.publication.dimensions?.height ?? 0; return (
{/* Agent */}
{!isAvatar && ( // Audio Agent )} {isAvatar && ( // Avatar Agent )}
{/* Camera & Screen Share */} {((cameraTrack && isCameraEnabled) || (screenShareTrack && isScreenShareEnabled)) && ( )}
); }