UNPKG

voicebot-react-native-expo

Version:

This is a voicebot-react-native package of Kipps AI voice bot for React Native Expo

1 lines 131 kB
{"version":3,"file":"hooks-C6T19zG6.mjs","sources":["../src/hooks/internal/useResizeObserver.ts","../src/hooks/internal/useObservableState.ts","../src/hooks/internal/useMediaQuery.ts","../src/hooks/useAudioPlayback.ts","../src/hooks/useClearPinButton.ts","../src/hooks/useConnectionQualityIndicator.ts","../src/hooks/useConnectionStatus.ts","../src/hooks/useDataChannel.ts","../src/hooks/useDisconnectButton.ts","../src/hooks/useFacingMode.ts","../src/hooks/useFocusToggle.ts","../src/hooks/useGridLayout.ts","../src/hooks/useIsMuted.ts","../src/hooks/useIsSpeaking.ts","../src/hooks/useLocalParticipant.ts","../src/hooks/useLocalParticipantPermissions.ts","../src/hooks/useMediaDeviceSelect.ts","../src/hooks/useMediaDevices.ts","../src/hooks/useVisualStableUpdate.ts","../src/hooks/usePagination.ts","../src/hooks/useParticipantInfo.ts","../src/hooks/useParticipantPermissions.ts","../src/hooks/useParticipantTile.ts","../src/hooks/useRemoteParticipants.ts","../src/hooks/useParticipants.ts","../src/hooks/usePinnedTracks.ts","../src/hooks/useRemoteParticipant.ts","../src/hooks/useRoomInfo.ts","../src/hooks/useSpeakingParticipants.ts","../src/hooks/useSortedParticipants.ts","../src/hooks/useStartAudio.ts","../src/hooks/useStartVideo.ts","../src/hooks/useSwipe.ts","../src/hooks/useChatToggle.ts","../src/hooks/useToken.ts","../src/hooks/useTrackMutedIndicator.ts","../src/hooks/useTrackToggle.ts","../src/hooks/useTracks.ts","../src/hooks/useTrackRefBySourceOrName.ts","../src/hooks/useTrackByName.ts","../src/hooks/useChat.ts","../src/hooks/usePersistentUserChoices.ts","../src/hooks/useIsEncrypted.ts","../src/hooks/useTrackVolume.ts","../src/hooks/useParticipantTracks.ts","../src/hooks/useTrackSyncTime.ts","../src/hooks/useTrackTranscription.ts","../src/hooks/useParticipantAttributes.ts","../src/hooks/useVoiceAssistant.ts","../src/hooks/useIsRecording.ts"],"sourcesContent":["/* eslint-disable no-return-assign */\n/* eslint-disable no-underscore-dangle */\nimport * as React from 'react';\n\nconst useLatest = <T>(current: T) => {\n const storedValue = React.useRef(current);\n React.useEffect(() => {\n storedValue.current = current;\n });\n return storedValue;\n};\n\n/**\n * A React hook that fires a callback whenever ResizeObserver detects a change to its size\n * code extracted from https://github.com/jaredLunde/react-hook/blob/master/packages/resize-observer/src/index.tsx in order to not include the polyfill for resize-observer\n *\n * @internal\n */\nexport function useResizeObserver<T extends HTMLElement>(\n target: React.RefObject<T>,\n callback: UseResizeObserverCallback,\n) {\n const resizeObserver = getResizeObserver();\n const storedCallback = useLatest(callback);\n\n React.useLayoutEffect(() => {\n let didUnsubscribe = false;\n\n const targetEl = target.current;\n if (!targetEl) return;\n\n function cb(entry: ResizeObserverEntry, observer: ResizeObserver) {\n if (didUnsubscribe) return;\n storedCallback.current(entry, observer);\n }\n\n resizeObserver?.subscribe(targetEl as HTMLElement, cb);\n\n return () => {\n didUnsubscribe = true;\n resizeObserver?.unsubscribe(targetEl as HTMLElement, cb);\n };\n }, [target.current, resizeObserver, storedCallback]);\n\n return resizeObserver?.observer;\n}\n\nfunction createResizeObserver() {\n let ticking = false;\n let allEntries: ResizeObserverEntry[] = [];\n\n const callbacks: Map<unknown, Array<UseResizeObserverCallback>> = new Map();\n\n if (typeof window === 'undefined') {\n return;\n }\n\n const observer = new ResizeObserver((entries: ResizeObserverEntry[], obs: ResizeObserver) => {\n allEntries = allEntries.concat(entries);\n if (!ticking) {\n window.requestAnimationFrame(() => {\n const triggered = new Set<Element>();\n for (let i = 0; i < allEntries.length; i++) {\n if (triggered.has(allEntries[i].target)) continue;\n triggered.add(allEntries[i].target);\n const cbs = callbacks.get(allEntries[i].target);\n cbs?.forEach((cb) => cb(allEntries[i], obs));\n }\n allEntries = [];\n ticking = false;\n });\n }\n ticking = true;\n });\n\n return {\n observer,\n subscribe(target: HTMLElement, callback: UseResizeObserverCallback) {\n observer.observe(target);\n const cbs = callbacks.get(target) ?? [];\n cbs.push(callback);\n callbacks.set(target, cbs);\n },\n unsubscribe(target: HTMLElement, callback: UseResizeObserverCallback) {\n const cbs = callbacks.get(target) ?? [];\n if (cbs.length === 1) {\n observer.unobserve(target);\n callbacks.delete(target);\n return;\n }\n const cbIndex = cbs.indexOf(callback);\n if (cbIndex !== -1) cbs.splice(cbIndex, 1);\n callbacks.set(target, cbs);\n },\n };\n}\n\nlet _resizeObserver: ReturnType<typeof createResizeObserver>;\n\nconst getResizeObserver = () =>\n !_resizeObserver ? (_resizeObserver = createResizeObserver()) : _resizeObserver;\n\nexport type UseResizeObserverCallback = (\n entry: ResizeObserverEntry,\n observer: ResizeObserver,\n) => unknown;\n\nexport const useSize = (target: React.RefObject<HTMLDivElement>) => {\n const [size, setSize] = React.useState({ width: 0, height: 0 });\n React.useLayoutEffect(() => {\n if (target.current) {\n const { width, height } = target.current.getBoundingClientRect();\n setSize({ width, height });\n }\n }, [target.current]);\n\n const resizeCallback = React.useCallback(\n (entry: ResizeObserverEntry) => setSize(entry.contentRect),\n [],\n );\n // Where the magic happens\n useResizeObserver(target, resizeCallback);\n return size;\n};\n","import * as React from 'react';\n// @ts-ignore\nimport type { Observable } from 'rxjs';\n\n/**\n * @internal\n */\nexport function useObservableState<T>(\n observable: Observable<T> | undefined,\n startWith: T,\n resetWhenObservableChanges = true,\n) {\n const [state, setState] = React.useState<T>(startWith);\n React.useEffect(() => {\n if (resetWhenObservableChanges) {\n setState(startWith);\n }\n // observable state doesn't run in SSR\n if (typeof window === 'undefined' || !observable) return;\n const subscription = observable.subscribe(setState);\n return () => subscription.unsubscribe();\n }, [observable, resetWhenObservableChanges]);\n return state;\n}\n","import * as React from 'react';\n/**\n * Implementation used from https://github.com/juliencrn/usehooks-ts\n *\n * @internal\n */\nexport function useMediaQuery(query: string): boolean {\n const getMatches = (query: string): boolean => {\n // Prevents SSR issues\n if (typeof window !== 'undefined') {\n return window.matchMedia(query).matches;\n }\n return false;\n };\n\n const [matches, setMatches] = React.useState<boolean>(getMatches(query));\n\n function handleChange() {\n setMatches(getMatches(query));\n }\n\n React.useEffect(() => {\n const matchMedia = window.matchMedia(query);\n\n // Triggered at the first client-side load and if query changes\n handleChange();\n\n // Listen matchMedia\n if (matchMedia.addListener) {\n matchMedia.addListener(handleChange);\n } else {\n matchMedia.addEventListener('change', handleChange);\n }\n\n return () => {\n if (matchMedia.removeListener) {\n matchMedia.removeListener(handleChange);\n } else {\n matchMedia.removeEventListener('change', handleChange);\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [query]);\n\n return matches;\n}\n","import type { Room } from 'livekit-client';\nimport * as React from 'react';\nimport { useObservableState } from './internal';\nimport { roomAudioPlaybackAllowedObservable } from '@livekit/components-core';\nimport { useEnsureRoom } from '../context';\n\n/**\n * In many browsers to start audio playback, the user must perform a user-initiated event such as clicking a button.\n * The `useAudioPlayback` hook returns an object with a boolean `canPlayAudio` flag that indicates whether audio\n * playback is allowed in the current context, as well as a `startAudio` function that can be called in a button\n * `onClick` callback to start audio playback in the current context.\n *\n * @see Autoplay policy on MDN web docs for more info: {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Best_practices#autoplay_policy}\n * @alpha\n */\nexport function useAudioPlayback(room?: Room): {\n canPlayAudio: boolean;\n startAudio: () => Promise<void>;\n} {\n const roomEnsured = useEnsureRoom(room);\n const startAudio = React.useCallback(async () => {\n await roomEnsured.startAudio();\n }, [roomEnsured]);\n\n const observable = React.useMemo(\n () => roomAudioPlaybackAllowedObservable(roomEnsured),\n [roomEnsured],\n );\n const { canPlayAudio } = useObservableState(observable, {\n canPlayAudio: roomEnsured.canPlaybackAudio,\n });\n\n return { canPlayAudio, startAudio };\n}\n","import { setupClearPinButton } from '@livekit/components-core';\nimport * as React from 'react';\nimport { useLayoutContext } from '../context';\nimport { mergeProps } from '../mergeProps';\nimport type { ClearPinButtonProps } from '../components';\n\n/**\n * The `useClearPinButton` hook provides props for the {@link ClearPinButton}\n * or your custom implementation of it component. It adds the `onClick` handler\n * to signal the `LayoutContext` that the tile in focus should be cleared.\n * @public\n */\nexport function useClearPinButton(props: ClearPinButtonProps) {\n const { state, dispatch } = useLayoutContext().pin;\n\n const buttonProps = React.useMemo(() => {\n const { className } = setupClearPinButton();\n const mergedProps = mergeProps(props, {\n className,\n disabled: !state?.length,\n onClick: () => {\n if (dispatch) dispatch({ msg: 'clear_pin' });\n },\n });\n return mergedProps;\n }, [props, dispatch, state]);\n\n return { buttonProps };\n}\n","import { setupConnectionQualityIndicator } from '@livekit/components-core';\nimport type { Participant } from 'livekit-client';\nimport { ConnectionQuality } from 'livekit-client';\nimport * as React from 'react';\nimport { useEnsureParticipant } from '../context';\nimport { useObservableState } from './internal';\n\n/** @public */\nexport interface ConnectionQualityIndicatorOptions {\n participant?: Participant;\n}\n\n/**\n * The `useConnectionQualityIndicator` hook provides props for the `ConnectionQualityIndicator` or your custom implementation of it component.\n * @example\n * ```tsx\n * const { quality } = useConnectionQualityIndicator();\n * // or\n * const { quality } = useConnectionQualityIndicator({ participant });\n * ```\n * @public\n */\nexport function useConnectionQualityIndicator(options: ConnectionQualityIndicatorOptions = {}) {\n const p = useEnsureParticipant(options.participant);\n\n const { className, connectionQualityObserver } = React.useMemo(\n () => setupConnectionQualityIndicator(p),\n [p],\n );\n\n const quality = useObservableState(connectionQualityObserver, ConnectionQuality.Unknown);\n\n return { className, quality };\n}\n","import { connectionStateObserver } from '@livekit/components-core';\nimport type { Room } from 'livekit-client';\nimport * as React from 'react';\nimport { useEnsureRoom } from '../context';\nimport { useObservableState } from './internal';\n\n/**\n * The `useConnectionState` hook allows you to simply implement your own `ConnectionState` component.\n *\n * @example\n * ```tsx\n * const connectionState = useConnectionState(room);\n * ```\n * @public\n */\nexport function useConnectionState(room?: Room) {\n // passed room takes precedence, if not supplied get current room context\n const r = useEnsureRoom(room);\n const observable = React.useMemo(() => connectionStateObserver(r), [r]);\n const connectionState = useObservableState(observable, r.state);\n return connectionState;\n}\n","import type { ReceivedDataMessage } from '@livekit/components-core';\nimport { setupDataMessageHandler } from '@livekit/components-core';\nimport * as React from 'react';\nimport type { DataPublishOptions } from 'livekit-client';\nimport { useRoomContext } from '../context';\nimport { useObservableState } from './internal';\n\ntype UseDataChannelReturnType<T extends string | undefined = undefined> = {\n isSending: boolean;\n send: (payload: Uint8Array, options: DataPublishOptions) => void;\n message: ReceivedDataMessage<T> | undefined;\n};\n\n/**\n * The `useDataChannel` hook returns the ability to send and receive messages.\n * By optionally passing a `topic`, you can narrow down which messages are returned in the messages array.\n * @remarks\n * There is only one data channel. Passing a `topic` does not open a new data channel.\n * It is only used to filter out messages with no or a different `topic`.\n *\n * @example\n * ```tsx\n * // Send messages to all participants via the 'chat' topic.\n * const { message: latestMessage, send } = useDataChannel('chat', (msg) => console.log(\"message received\", msg));\n * ```\n * @public\n */\nexport function useDataChannel<T extends string>(\n topic: T,\n onMessage?: (msg: ReceivedDataMessage<T>) => void,\n): UseDataChannelReturnType<T>;\n/**\n * The `useDataChannel` hook returns the ability to send and receive messages.\n * @remarks\n * There is only one data channel. Passing a `topic` does not open a new data channel.\n * It is only used to filter out messages with no or a different `topic`.\n *\n * @example\n * ```tsx\n * // Send messages to all participants\n * const { message: latestMessage, send } = useDataChannel('chat', (msg) => console.log(\"message received\", msg));\n * ```\n * @public\n */\nexport function useDataChannel(\n onMessage?: (msg: ReceivedDataMessage) => void,\n): UseDataChannelReturnType;\n/**\n * @internal\n */\nexport function useDataChannel<T extends string>(\n topicOrCallback?: T | ((msg: ReceivedDataMessage) => void),\n callback?: (msg: ReceivedDataMessage<T>) => void,\n) {\n const onMessage = typeof topicOrCallback === 'function' ? topicOrCallback : callback;\n\n const topic = typeof topicOrCallback === 'string' ? topicOrCallback : undefined;\n const room = useRoomContext();\n const { send, messageObservable, isSendingObservable } = React.useMemo(\n () => setupDataMessageHandler(room, topic, onMessage),\n [room, topic, onMessage],\n );\n\n const message = useObservableState(messageObservable, undefined);\n const isSending = useObservableState(isSendingObservable, false);\n\n return {\n message,\n send,\n isSending,\n };\n}\n","import { setupDisconnectButton } from '@livekit/components-core';\nimport { ConnectionState } from 'livekit-client';\nimport * as React from 'react';\nimport type { DisconnectButtonProps } from '../components';\nimport { useRoomContext } from '../context';\nimport { mergeProps } from '../mergeProps';\nimport { useConnectionState } from './useConnectionStatus';\n\n/**\n * The `useDisconnectButton` hook is used to implement the `DisconnectButton` or your\n * custom implementation of it. It adds onClick handler to the button to disconnect\n * from the room.\n *\n * @example\n * ```tsx\n * const { buttonProps } = useDisconnectButton(buttonProps);\n * return <button {...buttonProps}>Disconnect</button>;\n * ```\n * @public\n */\nexport function useDisconnectButton(props: DisconnectButtonProps) {\n const room = useRoomContext();\n const connectionState = useConnectionState(room);\n\n const buttonProps = React.useMemo(() => {\n const { className, disconnect } = setupDisconnectButton(room);\n const mergedProps = mergeProps(props, {\n className,\n onClick: () => disconnect(props.stopTracks ?? true),\n disabled: connectionState === ConnectionState.Disconnected,\n });\n return mergedProps;\n }, [room, props, connectionState]);\n\n return { buttonProps };\n}\n","import type { TrackReferenceOrPlaceholder } from '@livekit/components-core';\nimport { LocalTrackPublication, facingModeFromLocalTrack } from 'livekit-client';\n\n/**\n * Try to determine the `facingMode` of a local participant video track.\n * @remarks\n * Works only on local video tracks.\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/facingMode | MDN docs on facingMode}\n * @alpha\n */\nexport function useFacingMode(\n trackReference: TrackReferenceOrPlaceholder,\n): 'user' | 'environment' | 'left' | 'right' | 'undefined' {\n if (trackReference.publication instanceof LocalTrackPublication) {\n const localTrack = trackReference.publication.track;\n if (localTrack) {\n const { facingMode } = facingModeFromLocalTrack(localTrack);\n return facingMode;\n }\n }\n return 'undefined';\n}\n","import type { TrackReferenceOrPlaceholder } from '@livekit/components-core';\nimport { setupFocusToggle, isTrackReferencePinned } from '@livekit/components-core';\nimport { useEnsureTrackRef, useMaybeLayoutContext } from '../context';\nimport { mergeProps } from '../mergeProps';\nimport * as React from 'react';\n\n/** @public */\nexport interface UseFocusToggleProps {\n trackRef?: TrackReferenceOrPlaceholder;\n props: React.ButtonHTMLAttributes<HTMLButtonElement>;\n}\n\n/**\n * The `useFocusToggle` hook is used to implement the `FocusToggle` or your custom implementation of it.\n * The `TrackReferenceOrPlaceholder` is used to register a onClick handler and to identify the track to focus on.\n *\n * @example\n * ```tsx\n * const { mergedProps, inFocus } = useFocusToggle({ trackRef, props: yourButtonProps });\n * return <button {...mergedProps}>{inFocus ? 'Unfocus' : 'Focus'}</button>;\n * ```\n * @public\n */\nexport function useFocusToggle({ trackRef, props }: UseFocusToggleProps) {\n const trackReference = useEnsureTrackRef(trackRef);\n\n const layoutContext = useMaybeLayoutContext();\n const { className } = React.useMemo(() => setupFocusToggle(), []);\n\n const inFocus: boolean = React.useMemo(() => {\n return isTrackReferencePinned(trackReference, layoutContext?.pin.state);\n }, [trackReference, layoutContext?.pin.state]);\n\n const mergedProps = React.useMemo(\n () =>\n mergeProps(props, {\n className,\n onClick: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {\n // Call user defined on click callbacks.\n props.onClick?.(event);\n\n // Set or clear focus based on current focus state.\n if (inFocus) {\n layoutContext?.pin.dispatch?.({\n msg: 'clear_pin',\n });\n } else {\n layoutContext?.pin.dispatch?.({\n msg: 'set_pin',\n trackReference,\n });\n }\n },\n }),\n [props, className, trackReference, inFocus, layoutContext?.pin],\n );\n\n return { mergedProps, inFocus };\n}\n","import { GRID_LAYOUTS, selectGridLayout } from '@livekit/components-core';\nimport type { GridLayoutDefinition, GridLayoutInfo } from '@livekit/components-core';\nimport * as React from 'react';\nimport { useSize } from './internal';\n\n/**\n * The `useGridLayout` hook tries to select the best layout to fit all tiles.\n * If the available screen space is not enough, it will reduce the number of maximum visible\n * tiles and select a layout that still works visually within the given limitations.\n * As the order of tiles changes over time, the hook tries to keep visual updates to a minimum\n * while trying to display important tiles such as speaking participants or screen shares.\n *\n * @example\n * ```tsx\n * const { layout } = useGridLayout(gridElement, trackCount);\n * ```\n * @public\n */\nexport function useGridLayout(\n /** HTML element that contains the grid. */\n gridElement: React.RefObject<HTMLDivElement>,\n /** Count of tracks that should get layed out */\n trackCount: number,\n options: {\n gridLayouts?: GridLayoutDefinition[];\n } = {},\n): { layout: GridLayoutInfo; containerWidth: number; containerHeight: number } {\n const gridLayouts = options.gridLayouts ?? GRID_LAYOUTS;\n const { width, height } = useSize(gridElement);\n const layout = selectGridLayout(gridLayouts, trackCount, width, height);\n\n React.useEffect(() => {\n if (gridElement.current && layout) {\n gridElement.current.style.setProperty('--lk-col-count', layout?.columns.toString());\n gridElement.current.style.setProperty('--lk-row-count', layout?.rows.toString());\n }\n }, [gridElement, layout]);\n\n return {\n layout,\n containerWidth: width,\n containerHeight: height,\n };\n}\n","import {\n type TrackReferenceOrPlaceholder,\n getTrackReferenceId,\n mutedObserver,\n} from '@livekit/components-core';\nimport type { Participant, Track } from 'livekit-client';\nimport * as React from 'react';\nimport { useEnsureParticipant } from '../context';\n\n/** @public */\nexport interface UseIsMutedOptions {\n participant?: Participant;\n}\n\n/**\n * The `useIsMuted` hook is used to implement the `TrackMutedIndicator` or your custom implementation of it.\n * It returns a `boolean` that indicates if the track is muted or not.\n *\n * @example\n * ```tsx\n * const isMuted = useIsMuted(track);\n * ```\n * @public\n */\nexport function useIsMuted(trackRef: TrackReferenceOrPlaceholder): boolean;\nexport function useIsMuted(\n sourceOrTrackRef: TrackReferenceOrPlaceholder | Track.Source,\n options: UseIsMutedOptions = {},\n) {\n const passedParticipant =\n typeof sourceOrTrackRef === 'string' ? options.participant : sourceOrTrackRef.participant;\n const p = useEnsureParticipant(passedParticipant);\n const ref =\n typeof sourceOrTrackRef === 'string'\n ? { participant: p, source: sourceOrTrackRef }\n : sourceOrTrackRef;\n const [isMuted, setIsMuted] = React.useState(\n !!(ref.publication?.isMuted || p.getTrackPublication(ref.source)?.isMuted),\n );\n\n React.useEffect(() => {\n const listener = mutedObserver(ref).subscribe(setIsMuted);\n return () => listener.unsubscribe();\n }, [getTrackReferenceId(ref)]);\n\n return isMuted;\n}\n","import { createIsSpeakingObserver } from '@livekit/components-core';\nimport type { Participant } from 'livekit-client';\nimport * as React from 'react';\nimport { useEnsureParticipant } from '../context';\nimport { useObservableState } from './internal';\n\n/**\n * The `useIsSpeaking` hook returns a `boolean` that indicates if the participant is speaking or not.\n * @example\n * ```tsx\n * const isSpeaking = useIsSpeaking(participant);\n * ```\n * @public\n */\nexport function useIsSpeaking(participant?: Participant) {\n const p = useEnsureParticipant(participant);\n const observable = React.useMemo(() => createIsSpeakingObserver(p), [p]);\n const isSpeaking = useObservableState(observable, p.isSpeaking);\n\n return isSpeaking;\n}\n","import type { ParticipantMedia } from '@livekit/components-core';\nimport { observeParticipantMedia } from '@livekit/components-core';\nimport type { TrackPublication, LocalParticipant, Room } from 'livekit-client';\nimport * as React from 'react';\nimport { useEnsureRoom } from '../context';\n\n/** @public */\nexport interface UseLocalParticipantOptions {\n /**\n * The room to use. If not provided, the hook will use the room from the context.\n */\n room?: Room;\n}\n\n/**\n * The `useLocalParticipant` hook returns the local participant and the associated state\n * around the participant.\n *\n * @example\n * ```tsx\n * const { localParticipant } = useLocalParticipant();\n * ```\n * @public\n */\nexport function useLocalParticipant(options: UseLocalParticipantOptions = {}) {\n const room = useEnsureRoom(options.room);\n const [localParticipant, setLocalParticipant] = React.useState(room.localParticipant);\n const [isMicrophoneEnabled, setIsMicrophoneEnabled] = React.useState(\n localParticipant.isMicrophoneEnabled,\n );\n const [isCameraEnabled, setIsCameraEnabled] = React.useState(\n localParticipant.isMicrophoneEnabled,\n );\n const [lastMicrophoneError, setLastMicrophoneError] = React.useState(\n localParticipant.lastMicrophoneError,\n );\n const [lastCameraError, setLastCameraError] = React.useState(localParticipant.lastCameraError);\n const [isScreenShareEnabled, setIsScreenShareEnabled] = React.useState(\n localParticipant.isMicrophoneEnabled,\n );\n const [microphoneTrack, setMicrophoneTrack] = React.useState<TrackPublication | undefined>(\n undefined,\n );\n const [cameraTrack, setCameraTrack] = React.useState<TrackPublication | undefined>(undefined);\n\n const handleUpdate = (media: ParticipantMedia<LocalParticipant>) => {\n setIsCameraEnabled(media.isCameraEnabled);\n setIsMicrophoneEnabled(media.isMicrophoneEnabled);\n setIsScreenShareEnabled(media.isScreenShareEnabled);\n setCameraTrack(media.cameraTrack);\n setMicrophoneTrack(media.microphoneTrack);\n setLastMicrophoneError(media.participant.lastMicrophoneError);\n setLastCameraError(media.participant.lastCameraError);\n setLocalParticipant(media.participant);\n };\n React.useEffect(() => {\n const listener = observeParticipantMedia(room.localParticipant).subscribe(handleUpdate);\n // TODO also listen to permission and metadata etc. events\n return () => listener.unsubscribe();\n }, [room]);\n\n return {\n isMicrophoneEnabled,\n isScreenShareEnabled,\n isCameraEnabled,\n microphoneTrack,\n cameraTrack,\n lastMicrophoneError,\n lastCameraError,\n localParticipant,\n };\n}\n","import { participantPermissionObserver } from '@livekit/components-core';\nimport type { ParticipantPermission } from '@livekit/protocol';\nimport * as React from 'react';\nimport { useRoomContext } from '../context';\nimport { useObservableState } from './internal';\n\n/**\n * The `useLocalParticipantPermissions` hook returns the local participant's permissions.\n *\n * @example\n * ```tsx\n * const { canPublish, canPublishData } = useLocalParticipantPermissions();\n * ```\n * @public\n */\nexport function useLocalParticipantPermissions(): ParticipantPermission | undefined {\n const room = useRoomContext();\n const permissionObserver = React.useMemo(\n () => participantPermissionObserver(room.localParticipant),\n [room],\n );\n const permissions = useObservableState(permissionObserver, room.localParticipant.permissions);\n return permissions;\n}\n","import { createMediaDeviceObserver, setupDeviceSelector, log } from '@livekit/components-core';\nimport type { LocalAudioTrack, LocalVideoTrack, Room } from 'livekit-client';\nimport * as React from 'react';\nimport { useMaybeRoomContext } from '../context';\nimport { useObservableState } from './internal';\n\n/** @public */\nexport interface UseMediaDeviceSelectProps {\n kind: MediaDeviceKind;\n room?: Room;\n track?: LocalAudioTrack | LocalVideoTrack;\n /**\n * this will call getUserMedia if the permissions are not yet given to enumerate the devices with device labels.\n * in some browsers multiple calls to getUserMedia result in multiple permission prompts.\n * It's generally advised only flip this to true, once a (preview) track has been acquired successfully with the\n * appropriate permissions.\n *\n * @see {@link MediaDeviceMenu}\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/enumerateDevices | MDN enumerateDevices}\n */\n requestPermissions?: boolean;\n /**\n * this callback gets called if an error is thrown when failing to select a device and also if a user\n * denied permissions, eventhough the `requestPermissions` option is set to `true`.\n * Most commonly this will emit a MediaDeviceError\n */\n onError?: (e: Error) => void;\n}\n\n/**\n * The `useMediaDeviceSelect` hook is used to implement the `MediaDeviceSelect` component and\n * returns o.a. the list of devices of a given kind (audioinput or videoinput), the currently active device\n * and a function to set the the active device.\n *\n * @example\n * ```tsx\n * const { devices, activeDeviceId, setActiveMediaDevice } = useMediaDeviceSelect({kind: 'audioinput'});\n * ```\n * @public\n */\nexport function useMediaDeviceSelect({\n kind,\n room,\n track,\n requestPermissions,\n onError,\n}: UseMediaDeviceSelectProps) {\n const roomContext = useMaybeRoomContext();\n // List of all devices.\n const deviceObserver = React.useMemo(\n () => createMediaDeviceObserver(kind, onError, requestPermissions),\n [kind, requestPermissions, onError],\n );\n const devices = useObservableState(deviceObserver, [] as MediaDeviceInfo[]);\n // Active device management.\n const [currentDeviceId, setCurrentDeviceId] = React.useState<string>('');\n const { className, activeDeviceObservable, setActiveMediaDevice } = React.useMemo(\n () => setupDeviceSelector(kind, room ?? roomContext, track),\n [kind, room, roomContext, track],\n );\n\n React.useEffect(() => {\n const listener = activeDeviceObservable.subscribe((deviceId) => {\n log.info('setCurrentDeviceId', deviceId);\n if (deviceId) setCurrentDeviceId(deviceId);\n });\n return () => {\n listener?.unsubscribe();\n };\n }, [activeDeviceObservable]);\n\n return { devices, className, activeDeviceId: currentDeviceId, setActiveMediaDevice };\n}\n","import * as React from 'react';\nimport { useObservableState } from './internal';\nimport { createMediaDeviceObserver } from '@livekit/components-core';\n\n/**\n * The `useMediaDevices` hook returns the list of media devices of a given kind.\n *\n * @example\n * ```tsx\n * const videoDevices = useMediaDevices({ kind: 'videoinput' });\n * const audioDevices = useMediaDevices({ kind: 'audioinput' });\n * ```\n * @public\n */\nexport function useMediaDevices({\n kind,\n onError,\n}: {\n kind: MediaDeviceKind;\n onError?: (e: Error) => void;\n}) {\n const deviceObserver = React.useMemo(\n () => createMediaDeviceObserver(kind, onError),\n [kind, onError],\n );\n const devices = useObservableState(deviceObserver, [] as MediaDeviceInfo[]);\n return devices;\n}\n","import type { TrackReferenceOrPlaceholder } from '@livekit/components-core';\nimport { log, sortTrackReferences, updatePages } from '@livekit/components-core';\nimport * as React from 'react';\n\n/** @public */\nexport interface UseVisualStableUpdateOptions {\n /** Overwrites the default sort function. */\n customSortFunction?: (\n trackReferences: TrackReferenceOrPlaceholder[],\n ) => TrackReferenceOrPlaceholder[];\n}\n\n/**\n * The `useVisualStableUpdate` hook is used to prevent visually jarring jumps and shifts of elements\n * in an array. The algorithm only starts to update when there are more items than visually fit\n * on a page. If this is the case, it will make sure that speaking participants move to the first\n * page and are always visible.\n * @remarks\n * Updating the array can occur because attendees leave or join a room, or because they mute/unmute\n * or start speaking.\n * The hook is used for the `GridLayout` and `CarouselLayout` components.\n *\n * @example\n * ```tsx\n * const trackRefs = useTracks();\n * const updatedTrackRefs = useVisualStableUpdate(trackRefs, itemPerPage);\n * ```\n * @public\n */\nexport function useVisualStableUpdate(\n /** `TrackReference`s to display in the grid. */\n trackReferences: TrackReferenceOrPlaceholder[],\n maxItemsOnPage: number,\n options: UseVisualStableUpdateOptions = {},\n): TrackReferenceOrPlaceholder[] {\n const lastTrackRefs = React.useRef<TrackReferenceOrPlaceholder[]>([]);\n const lastMaxItemsOnPage = React.useRef<number>(-1);\n const layoutChanged = maxItemsOnPage !== lastMaxItemsOnPage.current;\n\n const sortedTrackRefs =\n typeof options.customSortFunction === 'function'\n ? options.customSortFunction(trackReferences)\n : sortTrackReferences(trackReferences);\n\n let updatedTrackRefs: TrackReferenceOrPlaceholder[] = [...sortedTrackRefs];\n if (layoutChanged === false) {\n try {\n updatedTrackRefs = updatePages(lastTrackRefs.current, sortedTrackRefs, maxItemsOnPage);\n } catch (error) {\n log.error('Error while running updatePages(): ', error);\n }\n }\n\n // Save info for to compare against in the next update cycle.\n if (layoutChanged) {\n lastTrackRefs.current = sortedTrackRefs;\n } else {\n lastTrackRefs.current = updatedTrackRefs;\n }\n lastMaxItemsOnPage.current = maxItemsOnPage;\n\n return updatedTrackRefs;\n}\n","import type { TrackReferenceOrPlaceholder } from '@livekit/components-core';\nimport * as React from 'react';\nimport { useVisualStableUpdate } from './useVisualStableUpdate';\n\n/**\n * The `usePagination` hook implements simple pagination logic for use with arrays.\n * @example\n * ```tsx\n * const tracks = useTracks();\n * const pagination = usePagination(4, tracks);\n *\n * <TrackLoop tracks={pagination.tracks} />\n * ```\n * @alpha\n */\nexport function usePagination(itemPerPage: number, trackReferences: TrackReferenceOrPlaceholder[]) {\n const [currentPage, setCurrentPage] = React.useState(1);\n const totalPageCount = Math.max(Math.ceil(trackReferences.length / itemPerPage), 1);\n if (currentPage > totalPageCount) {\n setCurrentPage(totalPageCount);\n }\n const lastItemIndex = currentPage * itemPerPage;\n const firstItemIndex = lastItemIndex - itemPerPage;\n\n const changePage = (direction: 'next' | 'previous') => {\n setCurrentPage((state) => {\n if (direction === 'next') {\n if (state === totalPageCount) {\n return state;\n }\n return state + 1;\n } else {\n if (state === 1) {\n return state;\n }\n return state - 1;\n }\n });\n };\n\n const goToPage = (num: number) => {\n if (num > totalPageCount) {\n setCurrentPage(totalPageCount);\n } else if (num < 1) {\n setCurrentPage(1);\n } else {\n setCurrentPage(num);\n }\n };\n\n const updatedTrackReferences = useVisualStableUpdate(trackReferences, itemPerPage);\n\n const tracksOnPage = updatedTrackReferences.slice(firstItemIndex, lastItemIndex);\n\n return {\n totalPageCount,\n nextPage: () => changePage('next'),\n prevPage: () => changePage('previous'),\n setPage: goToPage,\n firstItemIndex,\n lastItemIndex,\n tracks: tracksOnPage,\n currentPage,\n };\n}\n\nexport default usePagination;\n","import { participantInfoObserver } from '@livekit/components-core';\nimport type { Participant } from 'livekit-client';\nimport * as React from 'react';\nimport { useMaybeParticipantContext } from '../context';\nimport { useObservableState } from './internal';\n\n/**\n * The `useParticipantInfo` hook returns the identity, name, and metadata of a given participant.\n * It requires a `Participant` object passed as property or via the `ParticipantContext`.\n *\n * @example\n * ```tsx\n * const { identity, name, metadata } = useParticipantInfo({ participant });\n * ```\n * @public\n */\nexport interface UseParticipantInfoOptions {\n participant?: Participant;\n}\n\n/** @public */\nexport function useParticipantInfo(props: UseParticipantInfoOptions = {}) {\n let p = useMaybeParticipantContext();\n if (props.participant) {\n p = props.participant;\n }\n const infoObserver = React.useMemo(() => participantInfoObserver(p), [p]);\n const { identity, name, metadata } = useObservableState(infoObserver, {\n name: p?.name,\n identity: p?.identity,\n metadata: p?.metadata,\n });\n\n return { identity, name, metadata };\n}\n","import { participantPermissionObserver } from '@livekit/components-core';\nimport type { ParticipantPermission } from '@livekit/protocol';\nimport type { Participant } from 'livekit-client';\nimport * as React from 'react';\nimport { useEnsureParticipant } from '../context';\nimport { useObservableState } from './internal/useObservableState';\n\n/**\n * The `useParticipantPermissions` hook returns the permissions of a given participant.\n *\n * @example\n * ```tsx\n * const permissions = useParticipantPermissions({ participant });\n * ```\n * @public\n */\nexport interface UseParticipantPermissionsOptions {\n participant?: Participant;\n}\n\n/** @public */\nexport function useParticipantPermissions(\n options: UseParticipantPermissionsOptions = {},\n): ParticipantPermission | undefined {\n const p = useEnsureParticipant(options.participant);\n const permissionObserver = React.useMemo(() => participantPermissionObserver(p), [p]);\n const permissions = useObservableState(permissionObserver, p.permissions);\n return permissions;\n}\n","import type { ParticipantClickEvent, TrackReferenceOrPlaceholder } from '@livekit/components-core';\nimport { setupParticipantTile } from '@livekit/components-core';\nimport * as React from 'react';\nimport { useEnsureTrackRef } from '../context';\nimport { mergeProps } from '../mergeProps';\nimport { useFacingMode } from './useFacingMode';\nimport { useIsMuted } from './useIsMuted';\nimport { useIsSpeaking } from './useIsSpeaking';\nimport { Track } from 'livekit-client';\n\n/** @public */\nexport interface UseParticipantTileProps<T extends HTMLElement> extends React.HTMLAttributes<T> {\n /** The track reference to display. */\n trackRef?: TrackReferenceOrPlaceholder;\n disableSpeakingIndicator?: boolean;\n onParticipantClick?: (event: ParticipantClickEvent) => void;\n htmlProps: React.HTMLAttributes<T>;\n}\n\n/**\n * The `useParticipantTile` hook is used to implement the `ParticipantTile` and returns the props needed to render the tile.\n * @remarks\n * The returned props include many data attributes that are useful for CSS styling purposes because they\n * indicate the state of the participant and the track.\n * For example: `data-lk-audio-muted`, `data-lk-video-muted`, `data-lk-speaking`, `data-lk-local-participant`, `data-lk-source`, `data-lk-facing-mode`.\n * @public\n */\nexport function useParticipantTile<T extends HTMLElement>({\n trackRef,\n onParticipantClick,\n disableSpeakingIndicator,\n htmlProps,\n}: UseParticipantTileProps<T>) {\n const trackReference = useEnsureTrackRef(trackRef);\n\n const mergedProps = React.useMemo(() => {\n const { className } = setupParticipantTile();\n return mergeProps(htmlProps, {\n className,\n onClick: (event: React.MouseEvent<T, MouseEvent>) => {\n htmlProps.onClick?.(event);\n if (typeof onParticipantClick === 'function') {\n const track =\n trackReference.publication ??\n trackReference.participant.getTrackPublication(trackReference.source);\n onParticipantClick({ participant: trackReference.participant, track });\n }\n },\n });\n }, [\n htmlProps,\n onParticipantClick,\n trackReference.publication,\n trackReference.source,\n trackReference.participant,\n ]);\n\n const micTrack = trackReference.participant.getTrackPublication(Track.Source.Microphone);\n const micRef = React.useMemo(() => {\n return {\n participant: trackReference.participant,\n source: Track.Source.Microphone,\n publication: micTrack,\n };\n }, [micTrack, trackReference.participant]);\n const isVideoMuted = useIsMuted(trackReference);\n const isAudioMuted = useIsMuted(micRef);\n const isSpeaking = useIsSpeaking(trackReference.participant);\n const facingMode = useFacingMode(trackReference);\n return {\n elementProps: {\n 'data-lk-audio-muted': isAudioMuted,\n 'data-lk-video-muted': isVideoMuted,\n 'data-lk-speaking': disableSpeakingIndicator === true ? false : isSpeaking,\n 'data-lk-local-participant': trackReference.participant.isLocal,\n 'data-lk-source': trackReference.source,\n 'data-lk-facing-mode': facingMode,\n ...mergedProps,\n } as React.HTMLAttributes<T>,\n };\n}\n","import { connectedParticipantsObserver } from '@livekit/components-core';\nimport type { RoomEvent, RemoteParticipant, Room } from 'livekit-client';\nimport * as React from 'react';\nimport { useEnsureRoom } from '../context';\n\n/** @public */\nexport interface UseRemoteParticipantsOptions {\n /**\n * To optimize performance, you can use the `updateOnlyOn` property to decide on what RoomEvents the hook updates.\n * By default it updates on all relevant RoomEvents to keep the returned participants array up to date.\n * The minimal set of non-overwriteable `RoomEvents` is: `[RoomEvent.ParticipantConnected, RoomEvent.ParticipantDisconnected, RoomEvent.ConnectionStateChanged]`\n */\n updateOnlyOn?: RoomEvent[];\n /**\n * The room to use. If not provided, the hook will use the room from the context.\n */\n room?: Room;\n}\n\n/**\n * The `useRemoteParticipants` hook returns all remote participants (without the local) of the current room.\n * @remarks\n * To optimize performance, you can use the `updateOnlyOn` property to decide on what `RoomEvents` the hook updates.\n *\n * @example\n * ```tsx\n * const participants = useRemoteParticipants();\n * <ParticipantLoop participants={participants}>\n * <ParticipantName />\n * </ParticipantLoop>\n * ```\n * @public\n */\nexport function useRemoteParticipants(options: UseRemoteParticipantsOptions = {}) {\n const room = useEnsureRoom(options.room);\n const [participants, setParticipants] = React.useState<RemoteParticipant[]>([]);\n\n React.useEffect(() => {\n const listener = connectedParticipantsObserver(room, {\n additionalRoomEvents: options.updateOnlyOn,\n }).subscribe(setParticipants);\n return () => listener.unsubscribe();\n }, [room, JSON.stringify(options.updateOnlyOn)]);\n return participants;\n}\n","import type { Room, RoomEvent } from 'livekit-client';\nimport { useLocalParticipant } from './useLocalParticipant';\nimport { useRemoteParticipants } from './useRemoteParticipants';\nimport * as React from 'react';\n\n/** @public */\nexport interface UseParticipantsOptions {\n /**\n * To optimize performance, you can use the `updateOnlyOn` property to decide on what RoomEvents the hook updates.\n * By default it updates on all relevant RoomEvents to keep the returned participants array up to date.\n * The minimal set of non-overwriteable `RoomEvents` is: `[RoomEvent.ParticipantConnected, RoomEvent.ParticipantDisconnected, RoomEvent.ConnectionStateChanged]`\n */\n updateOnlyOn?: RoomEvent[];\n /**\n * The room to use. If not provided, the hook will use the room from the context.\n */\n room?: Room;\n}\n\n/**\n * The `useParticipants` hook returns all participants (local and remote) of the current room.\n * @remarks\n * To optimize performance, you can use the `updateOnlyOn` property to decide on what `RoomEvents` the hook updates.\n *\n * @example\n * ```tsx\n * const participants = useParticipants();\n * <ParticipantLoop participants={participants}>\n * <ParticipantName />\n * </ParticipantLoop>\n * ```\n * @public\n */\nexport function useParticipants(options: UseParticipantsOptions = {}) {\n const remoteParticipants = useRemoteParticipants(options);\n const { localParticipant } = useLocalParticipant(options);\n\n return React.useMemo(\n () => [localParticipant, ...remoteParticipants],\n [localParticipant, remoteParticipants],\n );\n}\n","import type { TrackReferenceOrPlaceholder } from '@livekit/components-core';\nimport * as React from 'react';\nimport type { LayoutContextType } from '../context';\nimport { useEnsureLayoutContext } from '../context';\n\n/**\n * The `usePinnedTracks` hook returns a array of the pinned tracks of the current room.\n * @remarks\n * To function properly, this hook must be called within a `LayoutContext`.\n * @example\n * ```tsx\n * const pinnedTracks = usePinnedTracks();\n * ```\n * @public\n */\nexport function usePinnedTracks(layoutContext?: LayoutContextType): TrackReferenceOrPlaceholder[] {\n layoutContext = useEnsureLayoutContext(layoutContext);\n return React.useMemo(() => {\n if (layoutContext?.pin.state !== undefined && layoutContext.pin.state.length >= 1) {\n return layoutContext.pin.state;\n }\n return [];\n }, [layoutContext.pin.state]);\n}\n","import {\n type ParticipantIdentifier,\n connectedParticipantObserver,\n participantByIdentifierObserver,\n} from '@livekit/components-core';\nimport type { ParticipantEvent, RemoteParticipant } from 'livekit-client';\nimport * as React from 'react';\nimport { useRoomContext } from '../context';\n\n/** @public */\nexport interface UseRemoteParticipantOptions {\n /**\n * To optimize performance, you can use the `updateOnlyOn` property to decide on what `ParticipantEvents` the hook updates.\n * By default it updates on all relevant ParticipantEvents to keep the returned participant up to date.\n */\n updateOnlyOn?: ParticipantEvent[];\n}\n\n/**\n * The `useRemoteParticipant` hook returns the first RemoteParticipant by either identity and/or based on the participant kind.\n * @remarks\n * To optimize performance, you can use the `updateOnlyOn` property to decide on what `ParticipantEvents` the hook updates.\n *\n * @example\n * ```tsx\n * const participant = useRemoteParticipant({kind: ParticipantKind.Agent, identity: 'myAgent'});\n * ```\n * @public\n */\nexport function useRemoteParticipant(\n identifier: ParticipantIdentifier,\n options?: UseRemoteParticipantOptions,\n): RemoteParticipant | undefined;\n/**\n * The `useRemoteParticipant` hook returns the first RemoteParticipant by either identity or based on the participant kind.\n * @remarks\n * To optimize performance, you can use the `updateOnlyOn` property to decide on what `ParticipantEvents` the hook updates.\n *\n * @example\n * ```tsx\n * const participant = useRemoteParticipant('Russ');\n * ```\n * @public\n */\nexport function useRemoteParticipant(\n identity: string,\n options?: UseRemoteParticipantOptions,\n): RemoteParticipant | undefined;\nexport function useRemoteParticipant(\n identityOrIdentifier: string | ParticipantIdentifier,\n options: UseRemoteParticipantOptions = {},\n): RemoteParticipant | undefined {\n const room = useRoomContext();\n const [updateOnlyOn] = React.useState(options.updateOnlyOn);\n\n const observable = React.useMemo(() => {\n if (typeof identityOrIdentifier === 'string') {\n return connectedParticipantObserver(room, identityOrIdentifier, {\n additionalEvents: updateOnlyOn,\n });\n } else {\n return participantByIdentifierObserver(room, identityOrIdentifier, {\n additionalEvents: updateOnlyOn,\n });\n }\n }, [room, JSON.stringify(identityOrIdentifier), updateOnlyOn]);\n\n // Using `wrapperParticipant` to ensure a new object reference,\n // triggering a re-render when the participant events fire.\n const [participantWrapper, setParticipantWrapper] = React.useState({\n p: undefined as RemoteParticipant | undefined,\n });\n React.useEffect(() => {\n const listener = observable.subscribe((p) => setParticipantWrapper({ p }));\n return () => listener.unsubscribe();\n }, [observable]);\n\n return participantWrapper.p;\n}\n","import { roomInfoObserver } from '@livekit/components-core';\nimport type { Room } from 'livekit-client';\nimport * as React from 'react';\nimport { useEnsureRoom } from '../context';\nimport { useObservableState } from './internal';\n\n/**\n * The `useRoomInfo` hook returns the name and metadata of the given `Room`.\n * @remarks\n * Needs to be called inside a `RoomContext` or by passing a `Room` instance.\n *\n * @example\n * ```tsx\n * const { name, metadata } = useRoomInfo();\n * ```\n * @public\n */\nexport interface UseRoomInfoOptions {\n room?: Room;\n}\n\n/** @public */\nexport function useRoomInfo(options: UseRoomInfoOptions = {}) {\n const room = useEnsureRoom(options.room);\n const infoObserver = React.useMemo(() => roomInfoObserver(room), [room]);\n const { name, metadata } = useObservableState(infoObserver, {\n name: room.name,\n metadata: room.metadata,\n });\n\n return { name, metadata };\n}\n","import { activeSpeakerObserver } from '@livekit/components-core';\nimport * as React from 'react';\nimport { useRoomContext } from '../context';\nimport { useObservableState } from './internal';\n\n/**\n * The `useSpeakingParticipants` hook returns the only the active speakers of all participants.\n *\n * @example\n * ```tsx\n * const activeSpeakers = useSpeakingParticipants();\n * ```\n * @public\n */\nexport function useSpeakingParticipants() {\n const room = useRoomContext();\n const speakerObserver = React.useMemo(() => activeSpeakerObserver(room), [room]);\n const activeSpeakers = useObservableState(speakerObserver, room.activeSpeakers);\n return activeSpeakers;\n}\n","import { sortParticipants } from '@livekit/components-core';\nimport type { Participant } from 'livekit-client';\nimport * as React from 'react';\nimport { useSpeakingParticipants } from './useSpeakingParticipants';\n\n/**\n * The `useSortedParticipants` hook returns the participants sorted by importance.\n * @public\n */\nexport function useSortedParticipants(participants: Array<Participant>) {\n const [sortedParticipants, setSortedParticipants] = React.useState(\n sortParticipants(participants),\n );\n const activeSpeakers = useSpeakingParticipants();\n\n React.useEffect(() => {\n setSortedParticipants(sortParticipants(participants));\n }, [activeSpeakers, participants]);\n return sortedParticipants;\n}\n","import { setupStartAudio } from '@livekit/components-core';\nimport type { Room } from 'livekit-client';\nimport * as React from 'react';\nimport { useEnsureRoom } from '../context';\nimport { mergeProps } from '../mergeProps';\nimport { useObservableState } from './internal';\n\n/** @alpha */\nexport interface UseStartAudioProps {\n room?: Room;\n props: React.ButtonHTMLAttributes<HTMLButtonElement>;\n}\n\n/**\n * In many browsers to start audi