import * as React from 'react'; import { useRoomContext } from '@livekit/components-react'; import { setLogLevel, LogLevel, RemoteTrackPublication, setLogExtension } from 'livekit-client'; import { tinykeys } from 'tinykeys'; import { datadogLogs } from '@datadog/browser-logs'; import styles from '../styles/Debug.module.css'; export const useDebugMode = ({ logLevel }: { logLevel?: LogLevel }) => { const room = useRoomContext(); React.useEffect(() => { setLogLevel(logLevel ?? 'debug'); if (process.env.NEXT_PUBLIC_DATADOG_CLIENT_TOKEN && process.env.NEXT_PUBLIC_DATADOG_SITE) { console.log('setting up datadog logs'); datadogLogs.init({ clientToken: process.env.NEXT_PUBLIC_DATADOG_CLIENT_TOKEN, site: process.env.NEXT_PUBLIC_DATADOG_SITE, forwardErrorsToLogs: true, sessionSampleRate: 100, }); setLogExtension((level, msg, context) => { switch (level) { case LogLevel.debug: datadogLogs.logger.debug(msg, context); break; case LogLevel.info: datadogLogs.logger.info(msg, context); break; case LogLevel.warn: datadogLogs.logger.warn(msg, context); break; case LogLevel.error: datadogLogs.logger.error(msg, context); break; default: break; } }); } // @ts-expect-error window.__lk_room = room; return () => { // @ts-expect-error window.__lk_room = undefined; }; }, [room, logLevel]); }; export const DebugMode = ({ logLevel }: { logLevel?: LogLevel }) => { const room = useRoomContext(); const [isOpen, setIsOpen] = React.useState(false); const [, setRender] = React.useState({}); const [roomSid, setRoomSid] = React.useState(''); React.useEffect(() => { room.getSid().then(setRoomSid); }, [room]); useDebugMode({ logLevel }); React.useEffect(() => { if (window) { const unsubscribe = tinykeys(window, { 'Shift+D': () => { console.log('setting open'); setIsOpen((open) => !open); }, }); // timer to re-render const interval = setInterval(() => { setRender({}); }, 1000); return () => { unsubscribe(); clearInterval(interval); }; } }, [isOpen]); if (typeof window === 'undefined' || !isOpen) { return null; } const handleSimulate = (event: React.ChangeEvent) => { const { value } = event.target; if (value == '') { return; } event.target.value = ''; let isReconnect = false; switch (value) { case 'signal-reconnect': isReconnect = true; // fall through default: // @ts-expect-error room.simulateScenario(value); } }; const lp = room.localParticipant; if (!isOpen) { return <>; } else { return (

Room Info {room.name}: {roomSid}

Local Participant: {lp.identity}
Published tracks
{Array.from(lp.trackPublications.values()).map((t) => ( <>
{t.source.toString()}  {t.trackSid}
Kind {t.kind}  {t.kind === 'video' && ( {t.track?.dimensions?.width}x{t.track?.dimensions?.height} )}
Bitrate {Math.ceil(t.track!.currentBitrate / 1000)} kbps
))}
Permissions
{lp.permissions && Object.entries(lp.permissions).map(([key, val]) => ( <> {key !== 'canPublishSources' ? ( ) : ( )} ))}
{key}{val.toString()} {val.join(', ')}
Remote Participants {Array.from(room.remoteParticipants.values()).map((p) => (
{p.identity}
{Array.from(p.trackPublications.values()).map((t) => ( <>
{t.source.toString()}  {t.trackSid}
{t.track && ( )}
Kind {t.kind}  {t.kind === 'video' && ( {t.dimensions?.width}x{t.dimensions?.height} )}
Status {trackStatus(t)}
Bitrate {Math.ceil(t.track.currentBitrate / 1000)} kbps
))}
))}
); } }; function trackStatus(t: RemoteTrackPublication): string { if (t.isSubscribed) { return t.isEnabled ? 'enabled' : 'disabled'; } else { return 'unsubscribed'; } }