@runwayml/avatars-react 0.8.0 → 0.10.0-beta.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/README.md +132 -3
- package/dist/api.cjs +10 -0
- package/dist/api.cjs.map +1 -1
- package/dist/api.d.cts +74 -1
- package/dist/api.d.ts +74 -1
- package/dist/api.js +10 -1
- package/dist/api.js.map +1 -1
- package/dist/index.cjs +147 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +206 -8
- package/dist/index.d.ts +206 -8
- package/dist/index.js +143 -5
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as _livekit_components_react from '@livekit/components-react';
|
|
2
|
-
import { TrackReferenceOrPlaceholder } from '@livekit/components-react';
|
|
3
|
-
export { RoomAudioRenderer as AudioRenderer, VideoTrack } from '@livekit/components-react';
|
|
2
|
+
import { TrackReference, TrackReferenceOrPlaceholder } from '@livekit/components-react';
|
|
3
|
+
export { RoomAudioRenderer as AudioRenderer, VideoTrack, isTrackReference } from '@livekit/components-react';
|
|
4
4
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
5
5
|
import * as livekit_client from 'livekit-client';
|
|
6
6
|
import { ComponentPropsWithoutRef, ReactNode } from 'react';
|
|
@@ -30,7 +30,7 @@ interface SessionCredentials {
|
|
|
30
30
|
/**
|
|
31
31
|
* Props for the AvatarSession component
|
|
32
32
|
*/
|
|
33
|
-
interface AvatarSessionProps {
|
|
33
|
+
interface AvatarSessionProps<E extends ClientEvent = ClientEvent> {
|
|
34
34
|
/** Connection credentials from Runway API */
|
|
35
35
|
credentials: SessionCredentials;
|
|
36
36
|
/** Children to render inside the session */
|
|
@@ -43,6 +43,13 @@ interface AvatarSessionProps {
|
|
|
43
43
|
onEnd?: () => void;
|
|
44
44
|
/** Callback when an error occurs */
|
|
45
45
|
onError?: (error: Error) => void;
|
|
46
|
+
/** Callback when a client event is received from the avatar */
|
|
47
|
+
onClientEvent?: ClientEventHandler<E>;
|
|
48
|
+
/**
|
|
49
|
+
* Pre-captured screen share stream (from getDisplayMedia).
|
|
50
|
+
* When provided, screen sharing activates automatically once the session connects.
|
|
51
|
+
*/
|
|
52
|
+
initialScreenStream?: MediaStream;
|
|
46
53
|
/**
|
|
47
54
|
* Advanced LiveKit room options. Not part of the public API.
|
|
48
55
|
* @internal
|
|
@@ -52,7 +59,7 @@ interface AvatarSessionProps {
|
|
|
52
59
|
/**
|
|
53
60
|
* Props for the AvatarCall component
|
|
54
61
|
*/
|
|
55
|
-
interface AvatarCallProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'onError'> {
|
|
62
|
+
interface AvatarCallProps<E extends ClientEvent = ClientEvent> extends Omit<React.ComponentPropsWithoutRef<'div'>, 'onError'> {
|
|
56
63
|
/** The avatar ID to connect to */
|
|
57
64
|
avatarId: string;
|
|
58
65
|
/** Session ID (use with sessionKey - package will call consumeSession) */
|
|
@@ -77,8 +84,15 @@ interface AvatarCallProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'o
|
|
|
77
84
|
onEnd?: () => void;
|
|
78
85
|
/** Callback when an error occurs */
|
|
79
86
|
onError?: (error: Error) => void;
|
|
87
|
+
/** Callback when a client event is received from the avatar */
|
|
88
|
+
onClientEvent?: ClientEventHandler<E>;
|
|
80
89
|
/** Custom children - defaults to AvatarVideo + ControlBar if not provided */
|
|
81
90
|
children?: React.ReactNode;
|
|
91
|
+
/**
|
|
92
|
+
* Pre-captured screen share stream (from getDisplayMedia).
|
|
93
|
+
* When provided, screen sharing activates automatically once the session connects.
|
|
94
|
+
*/
|
|
95
|
+
initialScreenStream?: MediaStream;
|
|
82
96
|
/**
|
|
83
97
|
* Advanced LiveKit room options. Not part of the public API.
|
|
84
98
|
* @internal
|
|
@@ -119,8 +133,51 @@ interface UseLocalMediaReturn {
|
|
|
119
133
|
/** The local video track reference */
|
|
120
134
|
localVideoTrackRef: _livekit_components_react.TrackReferenceOrPlaceholder | null;
|
|
121
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Client event received from the avatar via the data channel.
|
|
138
|
+
* These are fire-and-forget events triggered by the avatar model.
|
|
139
|
+
*
|
|
140
|
+
* @typeParam T - The tool name (defaults to string for untyped usage)
|
|
141
|
+
* @typeParam A - The args type (defaults to Record<string, unknown>)
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```typescript
|
|
145
|
+
* // Untyped usage
|
|
146
|
+
* const event: ClientEvent = { type: 'client_event', tool: 'show_caption', args: { text: 'Hello' } };
|
|
147
|
+
*
|
|
148
|
+
* // Typed usage with discriminated union
|
|
149
|
+
* type MyEvent = ClientEvent<'show_caption', { text: string }>;
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
interface ClientEvent<T extends string = string, A = Record<string, unknown>> {
|
|
153
|
+
type: 'client_event';
|
|
154
|
+
tool: T;
|
|
155
|
+
args: A;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Handler function for client events
|
|
159
|
+
*/
|
|
160
|
+
type ClientEventHandler<E extends ClientEvent = ClientEvent> = (event: E) => void;
|
|
161
|
+
/**
|
|
162
|
+
* A transcription segment received from the session.
|
|
163
|
+
* SDK-owned type wrapping the underlying transport's transcription data.
|
|
164
|
+
*/
|
|
165
|
+
interface TranscriptionEntry {
|
|
166
|
+
/** Unique segment identifier */
|
|
167
|
+
id: string;
|
|
168
|
+
/** Transcribed text */
|
|
169
|
+
text: string;
|
|
170
|
+
/** Whether this is a final (non-streaming) segment */
|
|
171
|
+
final: boolean;
|
|
172
|
+
/** Identity of the participant who spoke */
|
|
173
|
+
participantIdentity: string;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Handler function for transcription events
|
|
177
|
+
*/
|
|
178
|
+
type TranscriptionHandler = (entry: TranscriptionEntry) => void;
|
|
122
179
|
|
|
123
|
-
declare function AvatarCall({ avatarId, sessionId, sessionKey, credentials: directCredentials, connectUrl, connect, baseUrl, audio, video, avatarImageUrl, onEnd, onError, children, __unstable_roomOptions, ...props }: AvatarCallProps): react_jsx_runtime.JSX.Element;
|
|
180
|
+
declare function AvatarCall<E extends ClientEvent = ClientEvent>({ avatarId, sessionId, sessionKey, credentials: directCredentials, connectUrl, connect, baseUrl, audio, video, avatarImageUrl, onEnd, onError, onClientEvent, children, initialScreenStream, __unstable_roomOptions, ...props }: AvatarCallProps<E>): react_jsx_runtime.JSX.Element;
|
|
124
181
|
|
|
125
182
|
/**
|
|
126
183
|
* AvatarSession component - the main entry point for avatar sessions
|
|
@@ -128,7 +185,7 @@ declare function AvatarCall({ avatarId, sessionId, sessionKey, credentials: dire
|
|
|
128
185
|
* Establishes a WebRTC connection and provides session state to children.
|
|
129
186
|
* This is a headless component that renders minimal DOM.
|
|
130
187
|
*/
|
|
131
|
-
declare function AvatarSession({ credentials, children, audio: requestAudio, video: requestVideo, onEnd, onError, __unstable_roomOptions, }: AvatarSessionProps): react_jsx_runtime.JSX.Element;
|
|
188
|
+
declare function AvatarSession<E extends ClientEvent = ClientEvent>({ credentials, children, audio: requestAudio, video: requestVideo, onEnd, onError, onClientEvent, initialScreenStream, __unstable_roomOptions, }: AvatarSessionProps<E>): react_jsx_runtime.JSX.Element;
|
|
132
189
|
|
|
133
190
|
/**
|
|
134
191
|
* useAvatarStatus Hook
|
|
@@ -166,7 +223,7 @@ type AvatarStatus = {
|
|
|
166
223
|
status: 'waiting';
|
|
167
224
|
} | {
|
|
168
225
|
status: 'ready';
|
|
169
|
-
videoTrackRef:
|
|
226
|
+
videoTrackRef: TrackReference;
|
|
170
227
|
} | {
|
|
171
228
|
status: 'ending';
|
|
172
229
|
} | {
|
|
@@ -282,6 +339,70 @@ type UseAvatarSessionReturn = {
|
|
|
282
339
|
*/
|
|
283
340
|
declare function useAvatarSession(): UseAvatarSessionReturn;
|
|
284
341
|
|
|
342
|
+
type EventArgs<E extends ClientEvent, T extends E['tool']> = Extract<E, {
|
|
343
|
+
tool: T;
|
|
344
|
+
}>['args'];
|
|
345
|
+
/**
|
|
346
|
+
* Subscribe to a single client event type by tool name.
|
|
347
|
+
*
|
|
348
|
+
* Returns the latest args as React state (`null` before the first event),
|
|
349
|
+
* and optionally fires a callback on each event for side effects.
|
|
350
|
+
*
|
|
351
|
+
* Must be used within an AvatarSession or AvatarCall component.
|
|
352
|
+
*
|
|
353
|
+
* @example
|
|
354
|
+
* ```tsx
|
|
355
|
+
* // State only — returns latest args
|
|
356
|
+
* const score = useClientEvent<TriviaEvent, 'update_score'>('update_score');
|
|
357
|
+
* // score: { score: number; streak: number } | null
|
|
358
|
+
*
|
|
359
|
+
* // State + side effect
|
|
360
|
+
* const result = useClientEvent<TriviaEvent, 'reveal_answer'>('reveal_answer', (args) => {
|
|
361
|
+
* if (args.correct) fireConfetti();
|
|
362
|
+
* });
|
|
363
|
+
*
|
|
364
|
+
* // Side effect only — ignore the return value
|
|
365
|
+
* useClientEvent<TriviaEvent, 'play_sound'>('play_sound', (args) => {
|
|
366
|
+
* new Audio(SOUNDS[args.sound]).play();
|
|
367
|
+
* });
|
|
368
|
+
* ```
|
|
369
|
+
*/
|
|
370
|
+
declare function useClientEvent<E extends ClientEvent, T extends E['tool']>(toolName: T, onEvent?: (args: EventArgs<E, T>) => void): EventArgs<E, T> | null;
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Hook to listen for all client events from the avatar.
|
|
374
|
+
*
|
|
375
|
+
* Use this hook in child components to handle client events without prop drilling.
|
|
376
|
+
* Must be used within an AvatarSession or AvatarCall component.
|
|
377
|
+
*
|
|
378
|
+
* @typeParam E - The expected event type (defaults to ClientEvent for untyped usage)
|
|
379
|
+
*
|
|
380
|
+
* @example
|
|
381
|
+
* ```tsx
|
|
382
|
+
* // Untyped usage
|
|
383
|
+
* useClientEvents((event) => {
|
|
384
|
+
* console.log('Received:', event.tool, event.args);
|
|
385
|
+
* });
|
|
386
|
+
*
|
|
387
|
+
* // Type-safe usage with discriminated union
|
|
388
|
+
* type MyEvents =
|
|
389
|
+
* | ClientEvent<'show_caption', { text: string }>
|
|
390
|
+
* | ClientEvent<'play_sound', { url: string }>;
|
|
391
|
+
*
|
|
392
|
+
* useClientEvents<MyEvents>((event) => {
|
|
393
|
+
* switch (event.tool) {
|
|
394
|
+
* case 'show_caption':
|
|
395
|
+
* setCaption(event.args.text); // TypeScript knows this is string
|
|
396
|
+
* break;
|
|
397
|
+
* case 'play_sound':
|
|
398
|
+
* new Audio(event.args.url).play();
|
|
399
|
+
* break;
|
|
400
|
+
* }
|
|
401
|
+
* });
|
|
402
|
+
* ```
|
|
403
|
+
*/
|
|
404
|
+
declare function useClientEvents<E extends ClientEvent = ClientEvent>(handler: ClientEventHandler<E>): void;
|
|
405
|
+
|
|
285
406
|
/**
|
|
286
407
|
* Hook for local media controls (mic, camera, screen share).
|
|
287
408
|
*
|
|
@@ -291,4 +412,81 @@ declare function useAvatarSession(): UseAvatarSessionReturn;
|
|
|
291
412
|
*/
|
|
292
413
|
declare function useLocalMedia(): UseLocalMediaReturn;
|
|
293
414
|
|
|
294
|
-
|
|
415
|
+
/**
|
|
416
|
+
* Hook to listen for transcription events from the session.
|
|
417
|
+
*
|
|
418
|
+
* Fires the handler for each transcription segment received. By default,
|
|
419
|
+
* only final segments are delivered. Pass `{ interim: true }` to also
|
|
420
|
+
* receive partial/streaming segments.
|
|
421
|
+
*
|
|
422
|
+
* Must be used within an AvatarSession or AvatarCall component.
|
|
423
|
+
*
|
|
424
|
+
* @example
|
|
425
|
+
* ```tsx
|
|
426
|
+
* useTranscription((entry) => {
|
|
427
|
+
* console.log(`${entry.participantIdentity}: ${entry.text}`);
|
|
428
|
+
* });
|
|
429
|
+
*
|
|
430
|
+
* // Include interim (non-final) segments
|
|
431
|
+
* useTranscription((entry) => {
|
|
432
|
+
* console.log(entry.final ? 'FINAL' : 'partial', entry.text);
|
|
433
|
+
* }, { interim: true });
|
|
434
|
+
* ```
|
|
435
|
+
*/
|
|
436
|
+
declare function useTranscription(handler: TranscriptionHandler, options?: {
|
|
437
|
+
interim?: boolean;
|
|
438
|
+
}): void;
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* A standalone client tool definition. Composable — combine into arrays
|
|
442
|
+
* and derive event types with `ClientEventsFrom`.
|
|
443
|
+
*
|
|
444
|
+
* At runtime this is just `{ type, name, description }`. The `Args` generic
|
|
445
|
+
* is phantom — it only exists at the TypeScript level for type narrowing.
|
|
446
|
+
*/
|
|
447
|
+
interface ClientToolDef<Name extends string = string, Args = unknown> {
|
|
448
|
+
readonly type: 'client_event';
|
|
449
|
+
readonly name: Name;
|
|
450
|
+
readonly description: string;
|
|
451
|
+
/** @internal phantom field — always `undefined` at runtime */
|
|
452
|
+
readonly _args?: Args;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Derive a discriminated union of ClientEvent types from an array of tools.
|
|
456
|
+
*
|
|
457
|
+
* @example
|
|
458
|
+
* ```typescript
|
|
459
|
+
* const tools = [showQuestion, playSound];
|
|
460
|
+
* type MyEvent = ClientEventsFrom<typeof tools>;
|
|
461
|
+
* ```
|
|
462
|
+
*/
|
|
463
|
+
type ClientEventsFrom<T extends ReadonlyArray<ClientToolDef>> = T[number] extends infer U ? U extends ClientToolDef<infer Name, infer Args> ? ClientEvent<Name, Args> : never : never;
|
|
464
|
+
/**
|
|
465
|
+
* Define a single client tool.
|
|
466
|
+
*
|
|
467
|
+
* Returns a standalone object that can be composed into arrays and passed
|
|
468
|
+
* to `realtimeSessions.create({ tools })`.
|
|
469
|
+
*
|
|
470
|
+
* @example
|
|
471
|
+
* ```typescript
|
|
472
|
+
* const showQuestion = clientTool('show_question', {
|
|
473
|
+
* description: 'Display a trivia question',
|
|
474
|
+
* args: {} as { question: string; options: Array<string> },
|
|
475
|
+
* });
|
|
476
|
+
*
|
|
477
|
+
* const playSound = clientTool('play_sound', {
|
|
478
|
+
* description: 'Play a sound effect',
|
|
479
|
+
* args: {} as { sound: 'correct' | 'incorrect' },
|
|
480
|
+
* });
|
|
481
|
+
*
|
|
482
|
+
* // Combine and derive types
|
|
483
|
+
* const tools = [showQuestion, playSound];
|
|
484
|
+
* type MyEvent = ClientEventsFrom<typeof tools>;
|
|
485
|
+
* ```
|
|
486
|
+
*/
|
|
487
|
+
declare function clientTool<Name extends string, Args>(name: Name, config: {
|
|
488
|
+
description: string;
|
|
489
|
+
args: Args;
|
|
490
|
+
}): ClientToolDef<Name, Args>;
|
|
491
|
+
|
|
492
|
+
export { AvatarCall, type AvatarCallProps, AvatarSession, type AvatarStatus, AvatarVideo, type AvatarVideoStatus, type ClientEvent, type ClientEventHandler, type ClientEventsFrom, type ClientToolDef, ControlBar, ScreenShareVideo, type SessionCredentials, type SessionState, type TranscriptionEntry, type TranscriptionHandler, UserVideo, clientTool, useAvatar, useAvatarSession, useAvatarStatus, useClientEvent, useClientEvents, useLocalMedia, useTranscription };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { LiveKitRoom, RoomAudioRenderer, useRoomContext, useConnectionState, useRemoteParticipants, useTracks, isTrackReference, VideoTrack, useLocalParticipant, useMediaDevices, TrackToggle } from '@livekit/components-react';
|
|
2
|
-
export { RoomAudioRenderer as AudioRenderer, VideoTrack } from '@livekit/components-react';
|
|
3
|
-
import { createContext, useRef, useCallback, useState,
|
|
4
|
-
import { Track,
|
|
2
|
+
export { RoomAudioRenderer as AudioRenderer, VideoTrack, isTrackReference } from '@livekit/components-react';
|
|
3
|
+
import { createContext, useRef, useEffect, useCallback, useState, useContext, useSyncExternalStore } from 'react';
|
|
4
|
+
import { ConnectionState, Track, RoomEvent } from 'livekit-client';
|
|
5
5
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
6
6
|
|
|
7
7
|
// src/api/config.ts
|
|
@@ -153,6 +153,22 @@ function useLatest(value) {
|
|
|
153
153
|
}, [value]);
|
|
154
154
|
return ref;
|
|
155
155
|
}
|
|
156
|
+
|
|
157
|
+
// src/utils/parseClientEvent.ts
|
|
158
|
+
function isAckMessage(args) {
|
|
159
|
+
return "status" in args && args.status === "event_sent";
|
|
160
|
+
}
|
|
161
|
+
function parseClientEvent(payload) {
|
|
162
|
+
try {
|
|
163
|
+
const message = JSON.parse(new TextDecoder().decode(payload));
|
|
164
|
+
if (message?.type === "client_event" && typeof message.tool === "string" && message.args != null && typeof message.args === "object" && !isAckMessage(message.args)) {
|
|
165
|
+
return message;
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
} catch {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
156
172
|
async function hasMediaDevice(kind, timeoutMs = 1e3) {
|
|
157
173
|
try {
|
|
158
174
|
const timeoutPromise = new Promise(
|
|
@@ -228,6 +244,8 @@ function AvatarSession({
|
|
|
228
244
|
video: requestVideo = true,
|
|
229
245
|
onEnd,
|
|
230
246
|
onError,
|
|
247
|
+
onClientEvent,
|
|
248
|
+
initialScreenStream,
|
|
231
249
|
__unstable_roomOptions
|
|
232
250
|
}) {
|
|
233
251
|
const errorRef = useRef(null);
|
|
@@ -262,7 +280,9 @@ function AvatarSession({
|
|
|
262
280
|
{
|
|
263
281
|
sessionId: credentials.sessionId,
|
|
264
282
|
onEnd,
|
|
283
|
+
onClientEvent,
|
|
265
284
|
errorRef,
|
|
285
|
+
initialScreenStream,
|
|
266
286
|
children
|
|
267
287
|
}
|
|
268
288
|
),
|
|
@@ -274,13 +294,52 @@ function AvatarSession({
|
|
|
274
294
|
function AvatarSessionContextInner({
|
|
275
295
|
sessionId,
|
|
276
296
|
onEnd,
|
|
297
|
+
onClientEvent,
|
|
277
298
|
errorRef,
|
|
299
|
+
initialScreenStream,
|
|
278
300
|
children
|
|
279
301
|
}) {
|
|
280
302
|
const room = useRoomContext();
|
|
281
303
|
const connectionState = useConnectionState();
|
|
282
304
|
const onEndRef = useRef(onEnd);
|
|
283
305
|
onEndRef.current = onEnd;
|
|
306
|
+
const onClientEventRef = useRef(onClientEvent);
|
|
307
|
+
onClientEventRef.current = onClientEvent;
|
|
308
|
+
const publishedRef = useRef(false);
|
|
309
|
+
useEffect(() => {
|
|
310
|
+
if (connectionState !== ConnectionState.Connected) return;
|
|
311
|
+
if (!initialScreenStream || publishedRef.current) return;
|
|
312
|
+
publishedRef.current = true;
|
|
313
|
+
const videoTrack = initialScreenStream.getVideoTracks()[0];
|
|
314
|
+
if (videoTrack) {
|
|
315
|
+
room.localParticipant.publishTrack(videoTrack, {
|
|
316
|
+
source: Track.Source.ScreenShare
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
const audioTrack = initialScreenStream.getAudioTracks()[0];
|
|
320
|
+
if (audioTrack) {
|
|
321
|
+
room.localParticipant.publishTrack(audioTrack, {
|
|
322
|
+
source: Track.Source.ScreenShareAudio
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
return () => {
|
|
326
|
+
initialScreenStream.getTracks().forEach((t) => {
|
|
327
|
+
t.stop();
|
|
328
|
+
});
|
|
329
|
+
};
|
|
330
|
+
}, [connectionState, initialScreenStream, room]);
|
|
331
|
+
useEffect(() => {
|
|
332
|
+
function handleDataReceived(payload) {
|
|
333
|
+
const event = parseClientEvent(payload);
|
|
334
|
+
if (event) {
|
|
335
|
+
onClientEventRef.current?.(event);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
room.on(RoomEvent.DataReceived, handleDataReceived);
|
|
339
|
+
return () => {
|
|
340
|
+
room.off(RoomEvent.DataReceived, handleDataReceived);
|
|
341
|
+
};
|
|
342
|
+
}, [room]);
|
|
284
343
|
const end = useCallback(async () => {
|
|
285
344
|
try {
|
|
286
345
|
const encoder = new TextEncoder();
|
|
@@ -343,7 +402,10 @@ function useAvatarStatus() {
|
|
|
343
402
|
return { status: "connecting" };
|
|
344
403
|
case "active":
|
|
345
404
|
if (hasVideo && videoTrackRef) {
|
|
346
|
-
return {
|
|
405
|
+
return {
|
|
406
|
+
status: "ready",
|
|
407
|
+
videoTrackRef
|
|
408
|
+
};
|
|
347
409
|
}
|
|
348
410
|
return { status: "waiting" };
|
|
349
411
|
case "ending":
|
|
@@ -608,7 +670,9 @@ function AvatarCall({
|
|
|
608
670
|
avatarImageUrl,
|
|
609
671
|
onEnd,
|
|
610
672
|
onError,
|
|
673
|
+
onClientEvent,
|
|
611
674
|
children,
|
|
675
|
+
initialScreenStream,
|
|
612
676
|
__unstable_roomOptions,
|
|
613
677
|
...props
|
|
614
678
|
}) {
|
|
@@ -660,6 +724,8 @@ function AvatarCall({
|
|
|
660
724
|
video,
|
|
661
725
|
onEnd,
|
|
662
726
|
onError: handleSessionError,
|
|
727
|
+
onClientEvent,
|
|
728
|
+
initialScreenStream,
|
|
663
729
|
__unstable_roomOptions,
|
|
664
730
|
children: children ?? defaultChildren
|
|
665
731
|
}
|
|
@@ -693,7 +759,79 @@ function ScreenShareVideo({
|
|
|
693
759
|
}
|
|
694
760
|
return /* @__PURE__ */ jsx("div", { ...props, "data-avatar-screen-share": "", "data-avatar-sharing": isSharing, children: screenShareTrackRef && isTrackReference(screenShareTrackRef) && /* @__PURE__ */ jsx(VideoTrack, { trackRef: screenShareTrackRef }) });
|
|
695
761
|
}
|
|
762
|
+
function useClientEvent(toolName, onEvent) {
|
|
763
|
+
const room = useRoomContext();
|
|
764
|
+
const [state, setState] = useState(null);
|
|
765
|
+
const onEventRef = useRef(onEvent);
|
|
766
|
+
onEventRef.current = onEvent;
|
|
767
|
+
useEffect(() => {
|
|
768
|
+
function handleDataReceived(payload) {
|
|
769
|
+
const event = parseClientEvent(payload);
|
|
770
|
+
if (event && event.tool === toolName) {
|
|
771
|
+
const args = event.args;
|
|
772
|
+
setState(args);
|
|
773
|
+
onEventRef.current?.(args);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
room.on(RoomEvent.DataReceived, handleDataReceived);
|
|
777
|
+
return () => {
|
|
778
|
+
room.off(RoomEvent.DataReceived, handleDataReceived);
|
|
779
|
+
};
|
|
780
|
+
}, [room, toolName]);
|
|
781
|
+
return state;
|
|
782
|
+
}
|
|
783
|
+
function useClientEvents(handler) {
|
|
784
|
+
const room = useRoomContext();
|
|
785
|
+
const handlerRef = useRef(handler);
|
|
786
|
+
handlerRef.current = handler;
|
|
787
|
+
useEffect(() => {
|
|
788
|
+
function handleDataReceived(payload) {
|
|
789
|
+
const event = parseClientEvent(payload);
|
|
790
|
+
if (event) {
|
|
791
|
+
handlerRef.current(event);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
room.on(RoomEvent.DataReceived, handleDataReceived);
|
|
795
|
+
return () => {
|
|
796
|
+
room.off(RoomEvent.DataReceived, handleDataReceived);
|
|
797
|
+
};
|
|
798
|
+
}, [room]);
|
|
799
|
+
}
|
|
800
|
+
function useTranscription(handler, options) {
|
|
801
|
+
const room = useRoomContext();
|
|
802
|
+
const handlerRef = useRef(handler);
|
|
803
|
+
handlerRef.current = handler;
|
|
804
|
+
const interimRef = useRef(options?.interim ?? false);
|
|
805
|
+
interimRef.current = options?.interim ?? false;
|
|
806
|
+
useEffect(() => {
|
|
807
|
+
function handleTranscription(segments, participant) {
|
|
808
|
+
const identity = participant?.identity ?? "unknown";
|
|
809
|
+
for (const segment of segments) {
|
|
810
|
+
if (!interimRef.current && !segment.final) continue;
|
|
811
|
+
handlerRef.current({
|
|
812
|
+
id: segment.id,
|
|
813
|
+
text: segment.text,
|
|
814
|
+
final: segment.final,
|
|
815
|
+
participantIdentity: identity
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
room.on(RoomEvent.TranscriptionReceived, handleTranscription);
|
|
820
|
+
return () => {
|
|
821
|
+
room.off(RoomEvent.TranscriptionReceived, handleTranscription);
|
|
822
|
+
};
|
|
823
|
+
}, [room]);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// src/tools.ts
|
|
827
|
+
function clientTool(name, config) {
|
|
828
|
+
return {
|
|
829
|
+
type: "client_event",
|
|
830
|
+
name,
|
|
831
|
+
description: config.description
|
|
832
|
+
};
|
|
833
|
+
}
|
|
696
834
|
|
|
697
|
-
export { AvatarCall, AvatarSession, AvatarVideo, ControlBar, ScreenShareVideo, UserVideo, useAvatar, useAvatarSession, useAvatarStatus, useLocalMedia };
|
|
835
|
+
export { AvatarCall, AvatarSession, AvatarVideo, ControlBar, ScreenShareVideo, UserVideo, clientTool, useAvatar, useAvatarSession, useAvatarStatus, useClientEvent, useClientEvents, useLocalMedia, useTranscription };
|
|
698
836
|
//# sourceMappingURL=index.js.map
|
|
699
837
|
//# sourceMappingURL=index.js.map
|