@facepe/widget-mobile-app 1.2.0 → 1.3.1
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 +52 -0
- package/dist/FacePeMobileAvatar.js +109 -12
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/types.d.ts +38 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +2 -0
- package/package.json +61 -61
package/README.md
CHANGED
|
@@ -253,6 +253,7 @@ const styles = StyleSheet.create({
|
|
|
253
253
|
| `objectFit` | `'cover' \| 'contain'` | `'cover'` | Video aspect scaling mode. |
|
|
254
254
|
| `mirror` | `boolean` | `false` | Mirror video orientation. |
|
|
255
255
|
| `autoStart` | `boolean` | `true` | Automatically connect and start stream on mount. |
|
|
256
|
+
| `posterUrl` | `string \| null` | avatar's portrait | Picture shown until the video starts. Defaults to the avatar's portrait from FacePe; pass `null` for none. |
|
|
256
257
|
| `enableMic` | `boolean` | `true` | Automatically enable microphone on connect. |
|
|
257
258
|
| `onTranscript` | `function` | `undefined` | Fires when speech transcription arrives. |
|
|
258
259
|
| `onSpeakStart` | `function` | `undefined` | Fires when avatar starts speaking. |
|
|
@@ -289,5 +290,56 @@ await avatarRef.current?.interrupt();
|
|
|
289
290
|
|
|
290
291
|
---
|
|
291
292
|
|
|
293
|
+
## 🗂️ Conversation data (`onConversationEnd`)
|
|
294
|
+
|
|
295
|
+
When a conversation ends, the SDK hands your app the **whole conversation** in
|
|
296
|
+
one record. It stays in your app — the SDK sends it nowhere; you decide whether
|
|
297
|
+
to show it, save it on your server, or ignore it.
|
|
298
|
+
|
|
299
|
+
```tsx
|
|
300
|
+
<FacePeMobileAvatar
|
|
301
|
+
onConversationEnd={(conversation) => {
|
|
302
|
+
console.log(conversation.durationSec, conversation.transcript.length);
|
|
303
|
+
// e.g. save it on your own server:
|
|
304
|
+
// fetch('https://your-server.example.com/conversations', { method: 'POST', body: JSON.stringify(conversation) });
|
|
305
|
+
}}
|
|
306
|
+
/>
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
It is called once per conversation: after `stop()`, when the stream drops, or
|
|
310
|
+
when the component unmounts.
|
|
311
|
+
|
|
312
|
+
```json
|
|
313
|
+
{
|
|
314
|
+
"conversationId": "cm_usage_123",
|
|
315
|
+
"avatar": { "id": "cm_avatar_456", "name": "Dharun" },
|
|
316
|
+
"language": "en",
|
|
317
|
+
"platform": "android",
|
|
318
|
+
"sdkVersion": "1.3.0",
|
|
319
|
+
"startedAt": "2026-10-12T11:40:02.000Z",
|
|
320
|
+
"endedAt": "2026-10-12T11:43:17.000Z",
|
|
321
|
+
"durationSec": 195,
|
|
322
|
+
"endReason": "ended",
|
|
323
|
+
"transcript": [
|
|
324
|
+
{ "role": "assistant", "text": "Welcome! How can I help you today?", "at": "2026-10-12T11:40:05.000Z" },
|
|
325
|
+
{ "role": "user", "text": "Two cheeseburgers please.", "at": "2026-10-12T11:40:11.000Z" }
|
|
326
|
+
]
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
| Field | Meaning |
|
|
331
|
+
|---|---|
|
|
332
|
+
| `conversationId` | The conversation's FacePe id (its usage record) |
|
|
333
|
+
| `avatar` | `{ id, name }` of the avatar |
|
|
334
|
+
| `language` | The language requested for the conversation |
|
|
335
|
+
| `platform` | `web`, `android` or `ios` |
|
|
336
|
+
| `sdkVersion` | The SDK version that recorded it |
|
|
337
|
+
| `startedAt`, `endedAt` | ISO 8601 times |
|
|
338
|
+
| `durationSec` | Length in seconds |
|
|
339
|
+
| `endReason` | `ended` (the user or your code ended it), `disconnected` (the stream dropped), `unmounted` (the component went away) |
|
|
340
|
+
| `transcript` | Every line in order: `{ role: "user" \| "assistant", text, at }` |
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
292
344
|
## 📄 License
|
|
293
345
|
MIT © FacePe AI
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, useState, useImperativeHandle, forwardRef, useCallback, } from 'react';
|
|
3
|
-
import { View, StyleSheet, ActivityIndicator, Text, TouchableOpacity } from 'react-native';
|
|
3
|
+
import { View, StyleSheet, ActivityIndicator, Text, TouchableOpacity, Platform, Image } from 'react-native';
|
|
4
|
+
import { SDK_VERSION } from './version';
|
|
4
5
|
import { useFacePeMobileContext } from './FacePeMobileProvider';
|
|
5
6
|
// Resolve LiveKit dynamically at runtime so bare/Expo projects can run smoothly.
|
|
6
7
|
// @livekit/react-native brings the native parts (registerGlobals, AudioSession,
|
|
@@ -57,8 +58,36 @@ function ensureLiveKitGlobals(LiveKitRN) {
|
|
|
57
58
|
console.warn('[FacePeMobileAvatar] registerGlobals warning:', err);
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
|
-
export const FacePeMobileAvatar = forwardRef(({ avatarKey, language = 'en', style, videoStyle, objectFit = 'cover', mirror = false, autoStart = true, enableMic = true, onTranscript, onSpeakStart, onSpeakEnd, onUserSpeakStart, onUserSpeakEnd, onOrderUpdate, onCheckout, onStatusChange, onError, }, ref) => {
|
|
61
|
+
export const FacePeMobileAvatar = forwardRef(({ avatarKey, language = 'en', style, videoStyle, objectFit = 'cover', mirror = false, autoStart = true, enableMic = true, onTranscript, onSpeakStart, onSpeakEnd, onUserSpeakStart, onUserSpeakEnd, onOrderUpdate, onCheckout, onStatusChange, onError, onConversationEnd, posterUrl, }, ref) => {
|
|
61
62
|
const { token, loading: tokenLoading, error: tokenError, apiBaseUrl, refreshToken } = useFacePeMobileContext();
|
|
63
|
+
// The avatar's portrait, shown until its video arrives. Looked up from the
|
|
64
|
+
// avatars this token may use, unless the app gave one (or null for none).
|
|
65
|
+
const [autoPoster, setAutoPoster] = useState(null);
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (posterUrl !== undefined || !token)
|
|
68
|
+
return;
|
|
69
|
+
let cancelled = false;
|
|
70
|
+
void (async () => {
|
|
71
|
+
try {
|
|
72
|
+
const res = await fetch(`${apiBaseUrl.replace(/\/+$/, '')}/avatars`, {
|
|
73
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
74
|
+
});
|
|
75
|
+
if (!res.ok)
|
|
76
|
+
return;
|
|
77
|
+
const avatars = (await res.json())?.data?.avatars ?? [];
|
|
78
|
+
const avatar = avatarKey ? avatars.find((a) => a.id === avatarKey) : avatars[0];
|
|
79
|
+
if (!cancelled)
|
|
80
|
+
setAutoPoster(avatar?.previewUrl ?? null);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
/* no picture: the plain background stays */
|
|
84
|
+
}
|
|
85
|
+
})();
|
|
86
|
+
return () => {
|
|
87
|
+
cancelled = true;
|
|
88
|
+
};
|
|
89
|
+
}, [token, apiBaseUrl, avatarKey, posterUrl]);
|
|
90
|
+
const poster = posterUrl !== undefined ? posterUrl : autoPoster;
|
|
62
91
|
const [status, setStatus] = useState('idle');
|
|
63
92
|
const [videoTrack, setVideoTrack] = useState(null);
|
|
64
93
|
const [isMicEnabled, setIsMicEnabled] = useState(enableMic);
|
|
@@ -72,6 +101,50 @@ export const FacePeMobileAvatar = forwardRef(({ avatarKey, language = 'en', styl
|
|
|
72
101
|
const streamClientRef = useRef(null);
|
|
73
102
|
const [streamURL, setStreamURL] = useState(null);
|
|
74
103
|
const usageIdRef = useRef(null);
|
|
104
|
+
// The conversation as it happens, for the onConversationEnd record. Lines
|
|
105
|
+
// that carry an id (a line that grows while spoken) replace their earlier version.
|
|
106
|
+
const conversationRef = useRef(null);
|
|
107
|
+
const onConversationEndRef = useRef(onConversationEnd);
|
|
108
|
+
onConversationEndRef.current = onConversationEnd;
|
|
109
|
+
const onTranscriptRef = useRef(onTranscript);
|
|
110
|
+
onTranscriptRef.current = onTranscript;
|
|
111
|
+
// A transcript line: recorded for the conversation record, and passed on.
|
|
112
|
+
const emitLine = (role, text, id) => {
|
|
113
|
+
const conv = conversationRef.current;
|
|
114
|
+
if (conv && text) {
|
|
115
|
+
const same = id ? conv.lines.find((l) => l.id === id) : undefined;
|
|
116
|
+
if (same)
|
|
117
|
+
same.text = text;
|
|
118
|
+
else
|
|
119
|
+
conv.lines.push({ id, role, text, at: new Date().toISOString() });
|
|
120
|
+
}
|
|
121
|
+
onTranscriptRef.current?.({ payload: { role, text, isFinal: true } });
|
|
122
|
+
};
|
|
123
|
+
// Hands the whole conversation to the app, once.
|
|
124
|
+
const finishConversation = (endReason) => {
|
|
125
|
+
const conv = conversationRef.current;
|
|
126
|
+
conversationRef.current = null;
|
|
127
|
+
if (!conv)
|
|
128
|
+
return;
|
|
129
|
+
const endedAt = new Date();
|
|
130
|
+
try {
|
|
131
|
+
onConversationEndRef.current?.({
|
|
132
|
+
conversationId: conv.conversationId,
|
|
133
|
+
avatar: conv.avatar,
|
|
134
|
+
language: conv.language,
|
|
135
|
+
platform: Platform.OS === 'ios' ? 'ios' : Platform.OS === 'android' ? 'android' : 'web',
|
|
136
|
+
sdkVersion: SDK_VERSION,
|
|
137
|
+
startedAt: conv.startedAt.toISOString(),
|
|
138
|
+
endedAt: endedAt.toISOString(),
|
|
139
|
+
durationSec: Math.max(0, Math.round((endedAt.getTime() - conv.startedAt.getTime()) / 1000)),
|
|
140
|
+
endReason,
|
|
141
|
+
transcript: conv.lines.map(({ role, text, at }) => ({ role, text, at })),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
console.warn('[FacePeMobileAvatar] onConversationEnd error:', err);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
75
148
|
const mountedRef = useRef(true);
|
|
76
149
|
const isConnectingRef = useRef(false);
|
|
77
150
|
const tokenRef = useRef(token);
|
|
@@ -191,6 +264,13 @@ export const FacePeMobileAvatar = forwardRef(({ avatarKey, language = 'en', styl
|
|
|
191
264
|
const sessionPayload = await sessionResp.json();
|
|
192
265
|
const sessionInfo = sessionPayload?.data || sessionPayload;
|
|
193
266
|
usageIdRef.current = sessionInfo?.usageId || null;
|
|
267
|
+
conversationRef.current = {
|
|
268
|
+
conversationId: usageIdRef.current,
|
|
269
|
+
avatar: { id: String(sessionInfo?.avatar?.id ?? targetAvatarId), name: String(sessionInfo?.avatar?.name ?? '') },
|
|
270
|
+
language: language ?? null,
|
|
271
|
+
startedAt: new Date(),
|
|
272
|
+
lines: [],
|
|
273
|
+
};
|
|
194
274
|
const connection = sessionInfo?.connection;
|
|
195
275
|
if (connection?.type === 'stream') {
|
|
196
276
|
// Hosted avatar: the stream SDK gives plain MediaStreams (video +
|
|
@@ -212,7 +292,7 @@ export const FacePeMobileAvatar = forwardRef(({ avatarKey, language = 'en', styl
|
|
|
212
292
|
if (!text || seen.has(key))
|
|
213
293
|
continue;
|
|
214
294
|
seen.add(key);
|
|
215
|
-
|
|
295
|
+
emitLine(String(m.role).includes('user') ? 'user' : 'assistant', text, m?.id ? String(m.id) : undefined);
|
|
216
296
|
}
|
|
217
297
|
});
|
|
218
298
|
client.addListener(ev.USER_SPEECH_STARTED, () => { setIsUserSpeaking(true); onUserSpeakStart?.(); });
|
|
@@ -224,6 +304,7 @@ export const FacePeMobileAvatar = forwardRef(({ avatarKey, language = 'en', styl
|
|
|
224
304
|
setStreamURL(null);
|
|
225
305
|
setIsUserSpeaking(false);
|
|
226
306
|
updateStatus('disconnected');
|
|
307
|
+
finishConversation('disconnected');
|
|
227
308
|
void endUsage();
|
|
228
309
|
});
|
|
229
310
|
const streams = await client.stream();
|
|
@@ -335,13 +416,13 @@ export const FacePeMobileAvatar = forwardRef(({ avatarKey, language = 'en', styl
|
|
|
335
416
|
if (type === 'avatar.transcription' || type === 'response.audio_transcript.done') {
|
|
336
417
|
const str = msg.text || msg.transcript || msg.payload?.text;
|
|
337
418
|
if (str)
|
|
338
|
-
|
|
419
|
+
emitLine('assistant', String(str).trim());
|
|
339
420
|
}
|
|
340
421
|
else if (type === 'user.transcription' ||
|
|
341
422
|
type === 'conversation.item.input_audio_transcription.completed') {
|
|
342
423
|
const str = msg.text || msg.transcript || msg.payload?.text;
|
|
343
424
|
if (str)
|
|
344
|
-
|
|
425
|
+
emitLine('user', String(str).trim());
|
|
345
426
|
}
|
|
346
427
|
// Order & checkout intents
|
|
347
428
|
if (type === 'order.update') {
|
|
@@ -358,12 +439,17 @@ export const FacePeMobileAvatar = forwardRef(({ avatarKey, language = 'en', styl
|
|
|
358
439
|
});
|
|
359
440
|
// 4. Connection state transitions
|
|
360
441
|
room.on(LiveKitRN.RoomEvent.Disconnected, () => {
|
|
361
|
-
if (!mountedRef.current)
|
|
362
|
-
return;
|
|
442
|
+
if (!mountedRef.current || roomRef.current !== room)
|
|
443
|
+
return; // stop() handles its own end
|
|
444
|
+
roomRef.current = null;
|
|
363
445
|
setVideoTrack(null);
|
|
364
446
|
setIsAvatarSpeaking(false);
|
|
365
447
|
setIsUserSpeaking(false);
|
|
366
448
|
updateStatus('disconnected');
|
|
449
|
+
// The stream dropped: close the conversation now, so it is not
|
|
450
|
+
// left open (and billed) until the server's 30-minute limit.
|
|
451
|
+
finishConversation('disconnected');
|
|
452
|
+
void endUsage();
|
|
367
453
|
});
|
|
368
454
|
room.on(LiveKitRN.RoomEvent.Reconnecting, () => {
|
|
369
455
|
updateStatus('reconnecting');
|
|
@@ -423,6 +509,8 @@ export const FacePeMobileAvatar = forwardRef(({ avatarKey, language = 'en', styl
|
|
|
423
509
|
setStreamURL(null);
|
|
424
510
|
if (failedClient)
|
|
425
511
|
await failedClient.stopStreaming().catch(() => undefined);
|
|
512
|
+
// Opened on the server but never got going: still hand over the record.
|
|
513
|
+
finishConversation('disconnected');
|
|
426
514
|
if (!mountedRef.current)
|
|
427
515
|
return;
|
|
428
516
|
const msg = err?.message || 'Connection failed';
|
|
@@ -452,7 +540,8 @@ export const FacePeMobileAvatar = forwardRef(({ avatarKey, language = 'en', styl
|
|
|
452
540
|
onError,
|
|
453
541
|
]);
|
|
454
542
|
// Stop streaming session
|
|
455
|
-
const stopStream = useCallback(async () => {
|
|
543
|
+
const stopStream = useCallback(async (reason = 'ended') => {
|
|
544
|
+
finishConversation(reason);
|
|
456
545
|
const room = roomRef.current;
|
|
457
546
|
roomRef.current = null;
|
|
458
547
|
const streamClient = streamClientRef.current;
|
|
@@ -561,7 +650,7 @@ export const FacePeMobileAvatar = forwardRef(({ avatarKey, language = 'en', styl
|
|
|
561
650
|
}
|
|
562
651
|
return () => {
|
|
563
652
|
mountedRef.current = false;
|
|
564
|
-
void stopStream();
|
|
653
|
+
void stopStream('unmounted');
|
|
565
654
|
};
|
|
566
655
|
}, [autoStart, token, tokenLoading, tokenError]);
|
|
567
656
|
// Handle token loading
|
|
@@ -584,9 +673,9 @@ export const FacePeMobileAvatar = forwardRef(({ avatarKey, language = 'en', styl
|
|
|
584
673
|
// A plain object, not an array: VideoView spreads `style` into its
|
|
585
674
|
// container ({ ...style }), and spreading an array loses every
|
|
586
675
|
// size — the video got no height and stayed invisible.
|
|
587
|
-
style: StyleSheet.flatten([styles.video, videoStyle]), objectFit: objectFit, mirror: mirror })) : (
|
|
588
|
-
|
|
589
|
-
|
|
676
|
+
style: StyleSheet.flatten([styles.video, videoStyle]), objectFit: objectFit, mirror: mirror })) : (_jsxs(View, { style: [styles.container, styles.center, !!poster && (status === 'idle' || status === 'disconnected') && styles.overPoster], children: [poster && _jsx(Image, { source: { uri: poster }, style: StyleSheet.absoluteFill, resizeMode: "cover" }), poster && status !== 'idle' && status !== 'disconnected' && _jsx(View, { style: [StyleSheet.absoluteFill, styles.dim] }), status === 'connecting' || status === 'reconnecting' || status === 'connected' ? (
|
|
677
|
+
// connected: the stream is up and the first video frame is on its way.
|
|
678
|
+
_jsxs(_Fragment, { children: [_jsx(ActivityIndicator, { size: "large", color: "#8B5CF6" }), _jsx(Text, { style: styles.loadingText, children: status === 'reconnecting' ? 'Reconnecting stream…' : 'Connecting to FacePe AI Avatar…' })] })) : status === 'error' ? (_jsxs(_Fragment, { children: [_jsx(Text, { style: styles.errorTitle, children: "Connection Failed" }), _jsx(Text, { style: styles.errorText, children: errorMessage || 'Unable to connect to stream' }), _jsx(TouchableOpacity, { style: styles.retryButton, onPress: () => void startStream(), children: _jsx(Text, { style: styles.retryText, children: "Reconnect" }) })] })) : (_jsx(TouchableOpacity, { style: styles.startButton, onPress: () => void startStream(), children: _jsx(Text, { style: styles.startText, children: "Start Avatar Session" }) }))] })), status === 'connected' && (isAvatarSpeaking || isUserSpeaking) && (_jsxs(View, { style: styles.speakerPill, children: [_jsx(View, { style: [
|
|
590
679
|
styles.speakerDot,
|
|
591
680
|
isAvatarSpeaking ? styles.dotAvatar : styles.dotUser,
|
|
592
681
|
] }), _jsx(Text, { style: styles.speakerText, children: isAvatarSpeaking ? 'Avatar speaking' : 'Listening to you…' })] }))] }));
|
|
@@ -606,6 +695,14 @@ const styles = StyleSheet.create({
|
|
|
606
695
|
width: '100%',
|
|
607
696
|
height: '100%',
|
|
608
697
|
},
|
|
698
|
+
// Over a portrait, keep the face clear: the button sits at the bottom.
|
|
699
|
+
overPoster: {
|
|
700
|
+
justifyContent: 'flex-end',
|
|
701
|
+
paddingBottom: 28,
|
|
702
|
+
},
|
|
703
|
+
dim: {
|
|
704
|
+
backgroundColor: 'rgba(15, 23, 42, 0.45)',
|
|
705
|
+
},
|
|
609
706
|
center: {
|
|
610
707
|
justifyContent: 'center',
|
|
611
708
|
alignItems: 'center',
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/types.d.ts
CHANGED
|
@@ -11,6 +11,37 @@ export interface TranscriptPayload {
|
|
|
11
11
|
text: string;
|
|
12
12
|
isFinal?: boolean;
|
|
13
13
|
}
|
|
14
|
+
/** One line of a finished conversation, with the time it was said (ISO 8601). */
|
|
15
|
+
export interface FacePeConversationLine {
|
|
16
|
+
role: 'user' | 'assistant';
|
|
17
|
+
text: string;
|
|
18
|
+
at: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Everything about one conversation, given once when it ends
|
|
22
|
+
* (`onConversationEnd`). It stays in your app: the SDK sends it nowhere — you
|
|
23
|
+
* decide whether to show, keep or send it.
|
|
24
|
+
*/
|
|
25
|
+
export interface FacePeConversation {
|
|
26
|
+
/** The conversation's id (its FacePe usage record), or null if none was opened. */
|
|
27
|
+
conversationId: string | null;
|
|
28
|
+
avatar: {
|
|
29
|
+
id: string;
|
|
30
|
+
name: string;
|
|
31
|
+
};
|
|
32
|
+
/** The language requested for the conversation (ISO 639-1). */
|
|
33
|
+
language: string | null;
|
|
34
|
+
platform: 'android' | 'ios' | 'web';
|
|
35
|
+
sdkVersion: string;
|
|
36
|
+
/** When it started and ended (ISO 8601). */
|
|
37
|
+
startedAt: string;
|
|
38
|
+
endedAt: string;
|
|
39
|
+
durationSec: number;
|
|
40
|
+
/** `ended`: by `stop()`; `disconnected`: the stream dropped; `unmounted`: the component went away. */
|
|
41
|
+
endReason: 'ended' | 'disconnected' | 'unmounted';
|
|
42
|
+
/** Every line, in order (a line that grew while being spoken appears once, complete). */
|
|
43
|
+
transcript: FacePeConversationLine[];
|
|
44
|
+
}
|
|
14
45
|
export interface OrderItem {
|
|
15
46
|
name: string;
|
|
16
47
|
qty: number;
|
|
@@ -69,6 +100,13 @@ export interface FacePeMobileAvatarProps {
|
|
|
69
100
|
onCheckout?: () => void;
|
|
70
101
|
/** Called when stream connection status changes */
|
|
71
102
|
onStatusChange?: (status: FacePeConnectionStatus) => void;
|
|
103
|
+
/**
|
|
104
|
+
* Picture shown until the video starts (idle, connecting, errors). By default
|
|
105
|
+
* the avatar's own portrait from FacePe; pass your own URL, or null for none.
|
|
106
|
+
*/
|
|
107
|
+
posterUrl?: string | null;
|
|
108
|
+
/** Called once when a conversation ends, with the whole conversation (stays in your app) */
|
|
109
|
+
onConversationEnd?: (conversation: FacePeConversation) => void;
|
|
72
110
|
/** Called when an error occurs */
|
|
73
111
|
onError?: (event: {
|
|
74
112
|
code: string;
|
package/dist/version.js
ADDED
package/package.json
CHANGED
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@facepe/widget-mobile-app",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "FacePe AI 3D Video Avatar Native Live Streaming SDK for React Native (iOS & Android)",
|
|
5
|
-
"main": "./dist/index.js",
|
|
6
|
-
"module": "./dist/index.js",
|
|
7
|
-
"types": "./dist/index.d.ts",
|
|
8
|
-
"files": [
|
|
9
|
-
"dist",
|
|
10
|
-
"README.md"
|
|
11
|
-
],
|
|
12
|
-
"publishConfig": {
|
|
13
|
-
"access": "public"
|
|
14
|
-
},
|
|
15
|
-
"keywords": [
|
|
16
|
-
"facepe",
|
|
17
|
-
"avatar",
|
|
18
|
-
"ai-avatar",
|
|
19
|
-
"livekit",
|
|
20
|
-
"react-native",
|
|
21
|
-
"ios",
|
|
22
|
-
"android",
|
|
23
|
-
"webrtc",
|
|
24
|
-
"streaming",
|
|
25
|
-
"digital-human"
|
|
26
|
-
],
|
|
27
|
-
"author": "FacePe AI",
|
|
28
|
-
"license": "MIT",
|
|
29
|
-
"scripts": {
|
|
30
|
-
"lint": "tsc --noEmit",
|
|
31
|
-
"build": "tsc"
|
|
32
|
-
},
|
|
33
|
-
"peerDependencies": {
|
|
34
|
-
"@livekit/react-native": ">=2.0.0",
|
|
35
|
-
"@livekit/react-native-webrtc": ">=124.0.0",
|
|
36
|
-
"livekit-client": ">=2.0.0",
|
|
37
|
-
"react": ">=18.0.0",
|
|
38
|
-
"react-native": ">=0.70.0",
|
|
39
|
-
"@anam-ai/js-sdk": ">=4.27.0"
|
|
40
|
-
},
|
|
41
|
-
"peerDependenciesMeta": {
|
|
42
|
-
"@livekit/react-native": {
|
|
43
|
-
"optional": true
|
|
44
|
-
},
|
|
45
|
-
"@livekit/react-native-webrtc": {
|
|
46
|
-
"optional": true
|
|
47
|
-
},
|
|
48
|
-
"livekit-client": {
|
|
49
|
-
"optional": true
|
|
50
|
-
},
|
|
51
|
-
"@anam-ai/js-sdk": {
|
|
52
|
-
"optional": true
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
"devDependencies": {
|
|
56
|
-
"@types/react": "^18.3.3",
|
|
57
|
-
"react": "18.3.1",
|
|
58
|
-
"react-native": "0.74.0",
|
|
59
|
-
"typescript": "^5.4.5"
|
|
60
|
-
}
|
|
61
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@facepe/widget-mobile-app",
|
|
3
|
+
"version": "1.3.1",
|
|
4
|
+
"description": "FacePe AI 3D Video Avatar Native Live Streaming SDK for React Native (iOS & Android)",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"facepe",
|
|
17
|
+
"avatar",
|
|
18
|
+
"ai-avatar",
|
|
19
|
+
"livekit",
|
|
20
|
+
"react-native",
|
|
21
|
+
"ios",
|
|
22
|
+
"android",
|
|
23
|
+
"webrtc",
|
|
24
|
+
"streaming",
|
|
25
|
+
"digital-human"
|
|
26
|
+
],
|
|
27
|
+
"author": "FacePe AI",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"scripts": {
|
|
30
|
+
"lint": "tsc --noEmit",
|
|
31
|
+
"build": "tsc"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"@livekit/react-native": ">=2.0.0",
|
|
35
|
+
"@livekit/react-native-webrtc": ">=124.0.0",
|
|
36
|
+
"livekit-client": ">=2.0.0",
|
|
37
|
+
"react": ">=18.0.0",
|
|
38
|
+
"react-native": ">=0.70.0",
|
|
39
|
+
"@anam-ai/js-sdk": ">=4.27.0"
|
|
40
|
+
},
|
|
41
|
+
"peerDependenciesMeta": {
|
|
42
|
+
"@livekit/react-native": {
|
|
43
|
+
"optional": true
|
|
44
|
+
},
|
|
45
|
+
"@livekit/react-native-webrtc": {
|
|
46
|
+
"optional": true
|
|
47
|
+
},
|
|
48
|
+
"livekit-client": {
|
|
49
|
+
"optional": true
|
|
50
|
+
},
|
|
51
|
+
"@anam-ai/js-sdk": {
|
|
52
|
+
"optional": true
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/react": "^18.3.3",
|
|
57
|
+
"react": "18.3.1",
|
|
58
|
+
"react-native": "0.74.0",
|
|
59
|
+
"typescript": "^5.4.5"
|
|
60
|
+
}
|
|
61
|
+
}
|