@runwayml/avatars-react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,623 @@
1
+ import { LiveKitRoom, RoomAudioRenderer, useRoomContext, useConnectionState, useRemoteParticipants, useTracks, isTrackReference, VideoTrack, useLocalParticipant, useMediaDevices, TrackToggle } from '@livekit/components-react';
2
+ export { RoomAudioRenderer as AudioRenderer } from '@livekit/components-react';
3
+ import { createContext, useRef, useCallback, useState, useEffect, useContext, useReducer } from 'react';
4
+ import { ParticipantEvent, Track, ConnectionState } from 'livekit-client';
5
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
6
+
7
+ // src/api/config.ts
8
+ var DEFAULT_BASE_URL = "https://api.dev.runwayml.com";
9
+ function getBaseUrl() {
10
+ try {
11
+ const envUrl = process.env.RUNWAYML_BASE_URL;
12
+ if (envUrl) return envUrl;
13
+ } catch {
14
+ }
15
+ return DEFAULT_BASE_URL;
16
+ }
17
+ var config = null;
18
+ function getConfig() {
19
+ if (!config) {
20
+ config = { baseUrl: getBaseUrl() };
21
+ }
22
+ return config;
23
+ }
24
+
25
+ // src/api/consume.ts
26
+ async function consumeSession(options) {
27
+ const { sessionId, sessionKey, baseUrl = getConfig().baseUrl } = options;
28
+ const url = `${baseUrl}/v1/realtime_sessions/${sessionId}/consume`;
29
+ const response = await fetch(url, {
30
+ method: "POST",
31
+ headers: {
32
+ "Content-Type": "application/json",
33
+ Authorization: `Bearer ${sessionKey}`
34
+ }
35
+ });
36
+ if (!response.ok) {
37
+ const errorText = await response.text();
38
+ throw new Error(
39
+ `Failed to consume session: ${response.status} ${errorText}`
40
+ );
41
+ }
42
+ return response.json();
43
+ }
44
+
45
+ // src/hooks/useCredentials.ts
46
+ function credentialsReducer(_state, action) {
47
+ switch (action.type) {
48
+ case "CONNECT":
49
+ return { status: "connecting", credentials: null, error: null };
50
+ case "CONNECTED":
51
+ return {
52
+ status: "connected",
53
+ credentials: action.credentials,
54
+ error: null
55
+ };
56
+ case "ERROR":
57
+ return { status: "error", credentials: null, error: action.error };
58
+ }
59
+ }
60
+ function useCredentials(options) {
61
+ const {
62
+ avatarId,
63
+ sessionId,
64
+ sessionKey,
65
+ credentials: directCredentials,
66
+ connectUrl,
67
+ connect,
68
+ onError
69
+ } = options;
70
+ const [state, dispatch] = useReducer(credentialsReducer, {
71
+ status: "idle",
72
+ credentials: null,
73
+ error: null
74
+ });
75
+ const fetchedForRef = useRef(null);
76
+ const onErrorRef = useRef(onError);
77
+ onErrorRef.current = onError;
78
+ const mode = directCredentials ? "direct" : sessionId && sessionKey ? "session" : connectUrl || connect ? "connect" : null;
79
+ useEffect(() => {
80
+ if (mode !== "direct" || !directCredentials) return;
81
+ dispatch({ type: "CONNECTED", credentials: directCredentials });
82
+ }, [mode, directCredentials]);
83
+ useEffect(() => {
84
+ if (mode !== "session" || !sessionId || !sessionKey) return;
85
+ if (fetchedForRef.current === sessionId) return;
86
+ fetchedForRef.current = sessionId;
87
+ dispatch({ type: "CONNECT" });
88
+ consumeSession({ sessionId, sessionKey }).then(({ url, token, roomName }) => {
89
+ dispatch({
90
+ type: "CONNECTED",
91
+ credentials: { sessionId, serverUrl: url, token, roomName }
92
+ });
93
+ }).catch((err) => {
94
+ const error = err instanceof Error ? err : new Error(String(err));
95
+ dispatch({ type: "ERROR", error });
96
+ onErrorRef.current?.(error);
97
+ });
98
+ }, [mode, sessionId, sessionKey]);
99
+ useEffect(() => {
100
+ if (mode !== "connect") return;
101
+ if (fetchedForRef.current === avatarId) return;
102
+ fetchedForRef.current = avatarId;
103
+ dispatch({ type: "CONNECT" });
104
+ async function fetchCredentials() {
105
+ if (connect) {
106
+ return connect(avatarId);
107
+ }
108
+ if (connectUrl) {
109
+ const response = await fetch(connectUrl, {
110
+ method: "POST",
111
+ headers: { "Content-Type": "application/json" },
112
+ body: JSON.stringify({ avatarId })
113
+ });
114
+ if (!response.ok) {
115
+ const errorText = await response.text();
116
+ throw new Error(`Failed to connect: ${response.status} ${errorText}`);
117
+ }
118
+ return response.json();
119
+ }
120
+ throw new Error("No connect method available");
121
+ }
122
+ fetchCredentials().then((credentials) => {
123
+ dispatch({ type: "CONNECTED", credentials });
124
+ }).catch((err) => {
125
+ const error = err instanceof Error ? err : new Error(String(err));
126
+ dispatch({ type: "ERROR", error });
127
+ onErrorRef.current?.(error);
128
+ });
129
+ }, [mode, avatarId, connectUrl, connect]);
130
+ return state;
131
+ }
132
+ function useLatest(value) {
133
+ const ref = useRef(value);
134
+ useEffect(() => {
135
+ ref.current = value;
136
+ }, [value]);
137
+ return ref;
138
+ }
139
+ function mapConnectionState(connectionState) {
140
+ switch (connectionState) {
141
+ case ConnectionState.Connecting:
142
+ return "connecting";
143
+ case ConnectionState.Connected:
144
+ return "active";
145
+ case ConnectionState.Reconnecting:
146
+ return "connecting";
147
+ case ConnectionState.Disconnected:
148
+ return "ended";
149
+ default:
150
+ return "ended";
151
+ }
152
+ }
153
+ var AvatarSessionContext = createContext(
154
+ null
155
+ );
156
+ function AvatarSession({
157
+ credentials,
158
+ children,
159
+ audio = true,
160
+ video = true,
161
+ onEnd,
162
+ onError
163
+ }) {
164
+ const errorRef = useRef(null);
165
+ const handleError = (error) => {
166
+ errorRef.current = error;
167
+ onError?.(error);
168
+ };
169
+ return /* @__PURE__ */ jsxs(
170
+ LiveKitRoom,
171
+ {
172
+ serverUrl: credentials.serverUrl,
173
+ token: credentials.token,
174
+ connect: true,
175
+ audio,
176
+ video,
177
+ onDisconnected: () => onEnd?.(),
178
+ onError: handleError,
179
+ options: {
180
+ adaptiveStream: true,
181
+ dynacast: true
182
+ },
183
+ children: [
184
+ /* @__PURE__ */ jsx(
185
+ AvatarSessionContextInner,
186
+ {
187
+ sessionId: credentials.sessionId,
188
+ onEnd,
189
+ errorRef,
190
+ children
191
+ }
192
+ ),
193
+ /* @__PURE__ */ jsx(RoomAudioRenderer, {})
194
+ ]
195
+ }
196
+ );
197
+ }
198
+ function AvatarSessionContextInner({
199
+ sessionId,
200
+ onEnd,
201
+ errorRef,
202
+ children
203
+ }) {
204
+ const room = useRoomContext();
205
+ const connectionState = useConnectionState();
206
+ const onEndRef = useRef(onEnd);
207
+ onEndRef.current = onEnd;
208
+ const end = useCallback(async () => {
209
+ try {
210
+ const encoder = new TextEncoder();
211
+ const data = encoder.encode(JSON.stringify({ type: "END_CALL" }));
212
+ await room.localParticipant.publishData(data, { reliable: true });
213
+ } catch {
214
+ }
215
+ await room.disconnect();
216
+ onEndRef.current?.();
217
+ }, [room]);
218
+ const contextValue = {
219
+ state: mapConnectionState(connectionState),
220
+ sessionId,
221
+ error: errorRef.current,
222
+ end
223
+ };
224
+ return /* @__PURE__ */ jsx(AvatarSessionContext.Provider, { value: contextValue, children });
225
+ }
226
+ function useAvatarSessionContext() {
227
+ const context = useContext(AvatarSessionContext);
228
+ if (!context) {
229
+ throw new Error(
230
+ "useAvatarSessionContext must be used within an AvatarSession"
231
+ );
232
+ }
233
+ return context;
234
+ }
235
+ function useAvatar() {
236
+ const remoteParticipants = useRemoteParticipants();
237
+ const avatarParticipant = remoteParticipants[0] ?? null;
238
+ const avatarIdentity = avatarParticipant?.identity ?? null;
239
+ const [isSpeaking, setIsSpeaking] = useState(false);
240
+ useEffect(() => {
241
+ if (!avatarParticipant) {
242
+ setIsSpeaking(false);
243
+ return;
244
+ }
245
+ setIsSpeaking(avatarParticipant.isSpeaking);
246
+ const handleIsSpeakingChanged = (speaking) => {
247
+ setIsSpeaking(speaking);
248
+ };
249
+ avatarParticipant.on(
250
+ ParticipantEvent.IsSpeakingChanged,
251
+ handleIsSpeakingChanged
252
+ );
253
+ return () => {
254
+ avatarParticipant.off(
255
+ ParticipantEvent.IsSpeakingChanged,
256
+ handleIsSpeakingChanged
257
+ );
258
+ };
259
+ }, [avatarParticipant]);
260
+ const tracks = useTracks(
261
+ [
262
+ { source: Track.Source.Camera, withPlaceholder: true },
263
+ { source: Track.Source.Microphone, withPlaceholder: true }
264
+ ],
265
+ { onlySubscribed: true }
266
+ );
267
+ let videoTrackRef = null;
268
+ let audioTrackRef = null;
269
+ for (const trackRef of tracks) {
270
+ if (trackRef.participant.identity !== avatarIdentity) continue;
271
+ if (trackRef.source === Track.Source.Camera && !videoTrackRef) {
272
+ videoTrackRef = trackRef;
273
+ } else if (trackRef.source === Track.Source.Microphone && !audioTrackRef) {
274
+ audioTrackRef = trackRef;
275
+ }
276
+ if (videoTrackRef && audioTrackRef) break;
277
+ }
278
+ const hasVideo = videoTrackRef !== null && isTrackReference(videoTrackRef);
279
+ const hasAudio = audioTrackRef !== null && isTrackReference(audioTrackRef);
280
+ return {
281
+ participant: avatarParticipant,
282
+ videoTrackRef,
283
+ audioTrackRef,
284
+ isSpeaking,
285
+ hasVideo,
286
+ hasAudio
287
+ };
288
+ }
289
+
290
+ // src/hooks/useAvatarSession.ts
291
+ function useAvatarSession() {
292
+ const context = useAvatarSessionContext();
293
+ return context;
294
+ }
295
+ function AvatarVideo({ children, ...props }) {
296
+ const session = useAvatarSession();
297
+ const { videoTrackRef, isSpeaking, hasVideo } = useAvatar();
298
+ const isConnecting = session.state === "connecting";
299
+ const state = {
300
+ hasVideo,
301
+ isConnecting,
302
+ isSpeaking,
303
+ trackRef: videoTrackRef
304
+ };
305
+ if (children) {
306
+ return /* @__PURE__ */ jsx(Fragment, { children: children(state) });
307
+ }
308
+ return /* @__PURE__ */ jsx(
309
+ "div",
310
+ {
311
+ ...props,
312
+ "data-has-video": hasVideo,
313
+ "data-connecting": isConnecting,
314
+ "data-speaking": isSpeaking,
315
+ children: hasVideo && videoTrackRef && isTrackReference(videoTrackRef) && /* @__PURE__ */ jsx(VideoTrack, { trackRef: videoTrackRef })
316
+ }
317
+ );
318
+ }
319
+ function useLocalMedia() {
320
+ const { localParticipant } = useLocalParticipant();
321
+ const audioDevices = useMediaDevices({ kind: "audioinput" });
322
+ const videoDevices = useMediaDevices({ kind: "videoinput" });
323
+ const hasMic = audioDevices.length > 0;
324
+ const hasCamera = videoDevices.length > 0;
325
+ const isMicEnabled = localParticipant?.isMicrophoneEnabled ?? false;
326
+ const isCameraEnabled = localParticipant?.isCameraEnabled ?? false;
327
+ const isScreenShareEnabled = localParticipant?.isScreenShareEnabled ?? false;
328
+ const isMicEnabledRef = useLatest(isMicEnabled);
329
+ const isCameraEnabledRef = useLatest(isCameraEnabled);
330
+ const isScreenShareEnabledRef = useLatest(isScreenShareEnabled);
331
+ const toggleMic = useCallback(() => {
332
+ localParticipant?.setMicrophoneEnabled(!isMicEnabledRef.current);
333
+ }, [localParticipant, isMicEnabledRef]);
334
+ const toggleCamera = useCallback(() => {
335
+ localParticipant?.setCameraEnabled(!isCameraEnabledRef.current);
336
+ }, [localParticipant, isCameraEnabledRef]);
337
+ const toggleScreenShare = useCallback(() => {
338
+ localParticipant?.setScreenShareEnabled(!isScreenShareEnabledRef.current);
339
+ }, [localParticipant, isScreenShareEnabledRef]);
340
+ const tracks = useTracks(
341
+ [{ source: Track.Source.Camera, withPlaceholder: true }],
342
+ {
343
+ onlySubscribed: false
344
+ }
345
+ );
346
+ const localIdentity = localParticipant?.identity;
347
+ const localVideoTrackRef = tracks.find(
348
+ (trackRef) => trackRef.participant.identity === localIdentity && trackRef.source === Track.Source.Camera
349
+ ) ?? null;
350
+ return {
351
+ hasMic,
352
+ hasCamera,
353
+ isMicEnabled,
354
+ isCameraEnabled,
355
+ isScreenShareEnabled,
356
+ toggleMic,
357
+ toggleCamera,
358
+ toggleScreenShare,
359
+ localVideoTrackRef
360
+ };
361
+ }
362
+ function ControlBar({
363
+ children,
364
+ showMicrophone = true,
365
+ showCamera = true,
366
+ showScreenShare = false,
367
+ showEndCall = true,
368
+ ...props
369
+ }) {
370
+ const session = useAvatarSession();
371
+ const { isMicEnabled, isCameraEnabled, toggleMic, toggleCamera } = useLocalMedia();
372
+ const isActive = session.state === "active";
373
+ const state = {
374
+ isMicEnabled,
375
+ isCameraEnabled,
376
+ toggleMic,
377
+ toggleCamera,
378
+ endCall: session.end,
379
+ isActive
380
+ };
381
+ if (children) {
382
+ return /* @__PURE__ */ jsx(Fragment, { children: children(state) });
383
+ }
384
+ if (!isActive) {
385
+ return null;
386
+ }
387
+ return /* @__PURE__ */ jsxs("div", { ...props, "data-active": isActive, children: [
388
+ showMicrophone && /* @__PURE__ */ jsx(
389
+ "button",
390
+ {
391
+ type: "button",
392
+ onClick: toggleMic,
393
+ "data-control": "microphone",
394
+ "data-enabled": isMicEnabled,
395
+ "aria-label": isMicEnabled ? "Mute microphone" : "Unmute microphone",
396
+ children: microphoneIcon
397
+ }
398
+ ),
399
+ showCamera && /* @__PURE__ */ jsx(
400
+ "button",
401
+ {
402
+ type: "button",
403
+ onClick: toggleCamera,
404
+ "data-control": "camera",
405
+ "data-enabled": isCameraEnabled,
406
+ "aria-label": isCameraEnabled ? "Turn off camera" : "Turn on camera",
407
+ children: cameraIcon
408
+ }
409
+ ),
410
+ showScreenShare && /* @__PURE__ */ jsx(
411
+ TrackToggle,
412
+ {
413
+ source: Track.Source.ScreenShare,
414
+ showIcon: false,
415
+ "data-control": "screen-share",
416
+ "aria-label": "Toggle screen share",
417
+ children: screenShareIcon
418
+ }
419
+ ),
420
+ showEndCall && /* @__PURE__ */ jsx(
421
+ "button",
422
+ {
423
+ type: "button",
424
+ onClick: session.end,
425
+ "data-control": "end-call",
426
+ "aria-label": "End call",
427
+ children: phoneIcon
428
+ }
429
+ )
430
+ ] });
431
+ }
432
+ var microphoneIcon = /* @__PURE__ */ jsxs(
433
+ "svg",
434
+ {
435
+ width: "20",
436
+ height: "20",
437
+ viewBox: "0 0 24 24",
438
+ fill: "none",
439
+ stroke: "currentColor",
440
+ strokeWidth: "2",
441
+ strokeLinecap: "round",
442
+ strokeLinejoin: "round",
443
+ "aria-hidden": "true",
444
+ children: [
445
+ /* @__PURE__ */ jsx("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" }),
446
+ /* @__PURE__ */ jsx("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
447
+ /* @__PURE__ */ jsx("line", { x1: "12", x2: "12", y1: "19", y2: "22" })
448
+ ]
449
+ }
450
+ );
451
+ var cameraIcon = /* @__PURE__ */ jsxs(
452
+ "svg",
453
+ {
454
+ width: "20",
455
+ height: "20",
456
+ viewBox: "0 0 24 24",
457
+ fill: "none",
458
+ stroke: "currentColor",
459
+ strokeWidth: "2",
460
+ strokeLinecap: "round",
461
+ strokeLinejoin: "round",
462
+ "aria-hidden": "true",
463
+ children: [
464
+ /* @__PURE__ */ jsx("path", { d: "m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5" }),
465
+ /* @__PURE__ */ jsx("rect", { x: "2", y: "6", width: "14", height: "12", rx: "2" })
466
+ ]
467
+ }
468
+ );
469
+ var screenShareIcon = /* @__PURE__ */ jsxs(
470
+ "svg",
471
+ {
472
+ width: "20",
473
+ height: "20",
474
+ viewBox: "0 0 24 24",
475
+ fill: "none",
476
+ stroke: "currentColor",
477
+ strokeWidth: "2",
478
+ strokeLinecap: "round",
479
+ strokeLinejoin: "round",
480
+ "aria-hidden": "true",
481
+ children: [
482
+ /* @__PURE__ */ jsx("rect", { width: "20", height: "14", x: "2", y: "3", rx: "2" }),
483
+ /* @__PURE__ */ jsx("line", { x1: "8", x2: "16", y1: "21", y2: "21" }),
484
+ /* @__PURE__ */ jsx("line", { x1: "12", x2: "12", y1: "17", y2: "21" })
485
+ ]
486
+ }
487
+ );
488
+ var phoneIcon = /* @__PURE__ */ jsx(
489
+ "svg",
490
+ {
491
+ width: "20",
492
+ height: "20",
493
+ viewBox: "8 14 24 12",
494
+ fill: "currentColor",
495
+ "aria-hidden": "true",
496
+ children: /* @__PURE__ */ jsx("path", { d: "M12.8429 22.5693L11.4018 21.0986C11.2675 20.9626 11.1625 20.7995 11.0935 20.6197C11.0245 20.4399 10.9931 20.2474 11.0013 20.0545C11.0094 19.8616 11.0569 19.6726 11.1408 19.4995C11.2247 19.3265 11.343 19.1732 11.4883 19.0495C13.127 17.7049 15.0519 16.7714 17.1083 16.3239C19.0064 15.892 20.9744 15.892 22.8725 16.3239C24.9374 16.7743 26.8693 17.7147 28.5117 19.0691C28.6565 19.1924 28.7746 19.3451 28.8585 19.5176C28.9423 19.69 28.99 19.8784 28.9986 20.0707C29.0072 20.263 28.9764 20.455 28.9083 20.6345C28.8402 20.814 28.7362 20.9771 28.603 21.1133L27.1619 22.584C26.9311 22.8242 26.6226 22.9706 26.2938 22.9959C25.9651 23.0211 25.6385 22.9235 25.3751 22.7212C24.8531 22.3127 24.2875 21.9657 23.689 21.6869C23.4525 21.5774 23.2517 21.4009 23.1103 21.1785C22.969 20.9561 22.8931 20.697 22.8917 20.4319V19.1867C21.0053 18.6573 19.0139 18.6573 17.1275 19.1867V20.4319C17.1261 20.697 17.0502 20.9561 16.9089 21.1785C16.7676 21.4009 16.5667 21.5774 16.3302 21.6869C15.7317 21.9657 15.1661 22.3127 14.6442 22.7212C14.3779 22.9258 14.0473 23.0233 13.7152 22.9953C13.383 22.9673 13.0726 22.8156 12.8429 22.5693Z" })
497
+ }
498
+ );
499
+ function UserVideo({ children, mirror = true, ...props }) {
500
+ const { localVideoTrackRef, isCameraEnabled } = useLocalMedia();
501
+ const hasVideo = localVideoTrackRef !== null && isTrackReference(localVideoTrackRef);
502
+ const state = {
503
+ hasVideo,
504
+ isCameraEnabled,
505
+ trackRef: localVideoTrackRef
506
+ };
507
+ if (children) {
508
+ return /* @__PURE__ */ jsx(Fragment, { children: children(state) });
509
+ }
510
+ return /* @__PURE__ */ jsx(
511
+ "div",
512
+ {
513
+ ...props,
514
+ "data-has-video": hasVideo,
515
+ "data-camera-enabled": isCameraEnabled,
516
+ "data-mirror": mirror,
517
+ children: hasVideo && localVideoTrackRef && isTrackReference(localVideoTrackRef) && /* @__PURE__ */ jsx(VideoTrack, { trackRef: localVideoTrackRef })
518
+ }
519
+ );
520
+ }
521
+ function AvatarCall({
522
+ avatarId,
523
+ sessionId,
524
+ sessionKey,
525
+ credentials: directCredentials,
526
+ connectUrl,
527
+ connect,
528
+ avatarImageUrl,
529
+ onEnd,
530
+ onError,
531
+ children,
532
+ ...props
533
+ }) {
534
+ const onErrorRef = useLatest(onError);
535
+ const { status, credentials, error } = useCredentials({
536
+ avatarId,
537
+ sessionId,
538
+ sessionKey,
539
+ credentials: directCredentials,
540
+ connectUrl,
541
+ connect,
542
+ onError
543
+ });
544
+ const handleSessionError = (err) => {
545
+ onErrorRef.current?.(err);
546
+ };
547
+ const backgroundStyle = avatarImageUrl ? { "--avatar-image": `url(${avatarImageUrl})` } : void 0;
548
+ if (status === "idle" || status === "connecting") {
549
+ return /* @__PURE__ */ jsx(
550
+ "div",
551
+ {
552
+ ...props,
553
+ "data-avatar-call": "",
554
+ "data-state": "connecting",
555
+ "data-avatar-id": avatarId,
556
+ style: { ...props.style, ...backgroundStyle }
557
+ }
558
+ );
559
+ }
560
+ if (status === "error" || !credentials) {
561
+ return /* @__PURE__ */ jsx(
562
+ "div",
563
+ {
564
+ ...props,
565
+ "data-avatar-call": "",
566
+ "data-state": "error",
567
+ "data-avatar-id": avatarId,
568
+ "data-error": error?.message,
569
+ style: { ...props.style, ...backgroundStyle }
570
+ }
571
+ );
572
+ }
573
+ return /* @__PURE__ */ jsx(
574
+ "div",
575
+ {
576
+ ...props,
577
+ "data-avatar-call": "",
578
+ "data-state": "connected",
579
+ "data-avatar-id": avatarId,
580
+ style: { ...props.style, ...backgroundStyle },
581
+ children: /* @__PURE__ */ jsx(
582
+ AvatarSession,
583
+ {
584
+ credentials,
585
+ onEnd,
586
+ onError: handleSessionError,
587
+ children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
588
+ /* @__PURE__ */ jsx(AvatarVideo, {}),
589
+ /* @__PURE__ */ jsx(UserVideo, {}),
590
+ /* @__PURE__ */ jsx(ControlBar, {})
591
+ ] })
592
+ }
593
+ )
594
+ }
595
+ );
596
+ }
597
+ function ScreenShareVideo({ children, ...props }) {
598
+ const { localParticipant } = useLocalParticipant();
599
+ const tracks = useTracks(
600
+ [{ source: Track.Source.ScreenShare, withPlaceholder: false }],
601
+ { onlySubscribed: false }
602
+ );
603
+ const localIdentity = localParticipant?.identity;
604
+ const screenShareTrackRef = tracks.find(
605
+ (trackRef) => trackRef.participant.identity === localIdentity && trackRef.source === Track.Source.ScreenShare
606
+ ) ?? null;
607
+ const isSharing = screenShareTrackRef !== null && isTrackReference(screenShareTrackRef);
608
+ const state = {
609
+ isSharing,
610
+ trackRef: screenShareTrackRef
611
+ };
612
+ if (children) {
613
+ return /* @__PURE__ */ jsx(Fragment, { children: children(state) });
614
+ }
615
+ if (!isSharing) {
616
+ return null;
617
+ }
618
+ return /* @__PURE__ */ jsx("div", { ...props, "data-sharing": isSharing, children: screenShareTrackRef && isTrackReference(screenShareTrackRef) && /* @__PURE__ */ jsx(VideoTrack, { trackRef: screenShareTrackRef }) });
619
+ }
620
+
621
+ export { AvatarCall, AvatarSession, AvatarVideo, ControlBar, ScreenShareVideo, UserVideo, useAvatar, useAvatarSession, useLocalMedia };
622
+ //# sourceMappingURL=index.js.map
623
+ //# sourceMappingURL=index.js.map