@pipecat-ai/client-react 0.3.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 +234 -0
- package/dist/index.d.ts +65 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +548 -0
- package/dist/index.js.map +1 -0
- package/dist/index.module.js +531 -0
- package/dist/index.module.js.map +1 -0
- package/package.json +43 -0
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
import {jsx as $h9lXz$jsx, Fragment as $h9lXz$Fragment} from "react/jsx-runtime";
|
|
2
|
+
import {RTVIEvent as $h9lXz$RTVIEvent} from "@pipecat-ai/client-js";
|
|
3
|
+
import $h9lXz$react, {useRef as $h9lXz$useRef, useEffect as $h9lXz$useEffect, useCallback as $h9lXz$useCallback, useContext as $h9lXz$useContext, createContext as $h9lXz$createContext, forwardRef as $h9lXz$forwardRef} from "react";
|
|
4
|
+
import {createStore as $h9lXz$createStore, atom as $h9lXz$atom, useAtomValue as $h9lXz$useAtomValue, useAtom as $h9lXz$useAtom} from "jotai";
|
|
5
|
+
import {Provider as $h9lXz$Provider} from "jotai/react";
|
|
6
|
+
import {atomFamily as $h9lXz$atomFamily, useAtomCallback as $h9lXz$useAtomCallback} from "jotai/utils";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Copyright (c) 2024, Daily.
|
|
10
|
+
*
|
|
11
|
+
* SPDX-License-Identifier: BSD-2-Clause
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Copyright (c) 2024, Daily.
|
|
17
|
+
*
|
|
18
|
+
* SPDX-License-Identifier: BSD-2-Clause
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Copyright (c) 2024, Daily.
|
|
22
|
+
*
|
|
23
|
+
* SPDX-License-Identifier: BSD-2-Clause
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
const $f3f7d4263dc13c6a$var$defaultStore = (0, $h9lXz$createStore)();
|
|
30
|
+
const $f3f7d4263dc13c6a$export$8d2b07cbee622e7c = /*#__PURE__*/ (0, $h9lXz$createContext)({});
|
|
31
|
+
const $f3f7d4263dc13c6a$export$4a4ae2d5dc96782 = ({ children: children, client: client, jotaiStore: jotaiStore = $f3f7d4263dc13c6a$var$defaultStore })=>{
|
|
32
|
+
return (0, $h9lXz$jsx)((0, $h9lXz$Provider), {
|
|
33
|
+
store: jotaiStore,
|
|
34
|
+
children: (0, $h9lXz$jsx)($f3f7d4263dc13c6a$export$8d2b07cbee622e7c.Provider, {
|
|
35
|
+
value: {
|
|
36
|
+
client: client
|
|
37
|
+
},
|
|
38
|
+
children: children
|
|
39
|
+
})
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
$f3f7d4263dc13c6a$export$4a4ae2d5dc96782.displayName = "RTVIClientProvider";
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
const $54a3c9f5bdbf0854$export$31a5f6a22c9b8fba = ()=>{
|
|
46
|
+
const { client: client } = (0, $h9lXz$useContext)((0, $f3f7d4263dc13c6a$export$8d2b07cbee622e7c));
|
|
47
|
+
return client;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
const $824ea64b5f757259$export$33a6ac53b8f02625 = (event, handler)=>{
|
|
52
|
+
const client = (0, $54a3c9f5bdbf0854$export$31a5f6a22c9b8fba)();
|
|
53
|
+
(0, $h9lXz$useEffect)(()=>{
|
|
54
|
+
if (!client) return;
|
|
55
|
+
client.on(event, handler);
|
|
56
|
+
return ()=>{
|
|
57
|
+
client.off(event, handler);
|
|
58
|
+
};
|
|
59
|
+
}, [
|
|
60
|
+
event,
|
|
61
|
+
handler,
|
|
62
|
+
client
|
|
63
|
+
]);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Copyright (c) 2024, Daily.
|
|
69
|
+
*
|
|
70
|
+
* SPDX-License-Identifier: BSD-2-Clause
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
const $194c75143b7a1fa0$var$localAudioTrackAtom = (0, $h9lXz$atom)(null);
|
|
78
|
+
const $194c75143b7a1fa0$var$localVideoTrackAtom = (0, $h9lXz$atom)(null);
|
|
79
|
+
const $194c75143b7a1fa0$var$botAudioTrackAtom = (0, $h9lXz$atom)(null);
|
|
80
|
+
const $194c75143b7a1fa0$var$botVideoTrackAtom = (0, $h9lXz$atom)(null);
|
|
81
|
+
const $194c75143b7a1fa0$var$trackAtom = (0, $h9lXz$atomFamily)(({ local: local, trackType: trackType })=>{
|
|
82
|
+
if (local) return trackType === "audio" ? $194c75143b7a1fa0$var$localAudioTrackAtom : $194c75143b7a1fa0$var$localVideoTrackAtom;
|
|
83
|
+
return trackType === "audio" ? $194c75143b7a1fa0$var$botAudioTrackAtom : $194c75143b7a1fa0$var$botVideoTrackAtom;
|
|
84
|
+
});
|
|
85
|
+
const $194c75143b7a1fa0$export$7c03381e0d26a6c3 = (trackType, participantType)=>{
|
|
86
|
+
const client = (0, $54a3c9f5bdbf0854$export$31a5f6a22c9b8fba)();
|
|
87
|
+
const track = (0, $h9lXz$useAtomValue)($194c75143b7a1fa0$var$trackAtom({
|
|
88
|
+
local: participantType === "local",
|
|
89
|
+
trackType: trackType
|
|
90
|
+
}));
|
|
91
|
+
const updateTrack = (0, $h9lXz$useAtomCallback)((0, $h9lXz$useCallback)((get, set, track, trackType, local)=>{
|
|
92
|
+
const atom = $194c75143b7a1fa0$var$trackAtom({
|
|
93
|
+
local: local,
|
|
94
|
+
trackType: trackType
|
|
95
|
+
});
|
|
96
|
+
const oldTrack = get(atom);
|
|
97
|
+
if (oldTrack?.id === track.id) return;
|
|
98
|
+
set(atom, track);
|
|
99
|
+
}, [
|
|
100
|
+
participantType,
|
|
101
|
+
track,
|
|
102
|
+
trackType
|
|
103
|
+
]));
|
|
104
|
+
(0, $824ea64b5f757259$export$33a6ac53b8f02625)((0, $h9lXz$RTVIEvent).TrackStarted, (0, $h9lXz$useCallback)((track, participant)=>{
|
|
105
|
+
updateTrack(track, track.kind, Boolean(participant?.local));
|
|
106
|
+
}, []));
|
|
107
|
+
(0, $h9lXz$useEffect)(()=>{
|
|
108
|
+
if (!client) return;
|
|
109
|
+
const tracks = client.tracks();
|
|
110
|
+
const track = tracks?.[participantType]?.[trackType];
|
|
111
|
+
if (!track) return;
|
|
112
|
+
updateTrack(track, trackType, participantType === "local");
|
|
113
|
+
}, [
|
|
114
|
+
participantType,
|
|
115
|
+
trackType,
|
|
116
|
+
updateTrack,
|
|
117
|
+
client
|
|
118
|
+
]);
|
|
119
|
+
return track;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
const $f8b885726fc652c0$export$ba1245f7cbf3ae02 = ()=>{
|
|
124
|
+
const botAudioRef = (0, $h9lXz$useRef)(null);
|
|
125
|
+
const botAudioTrack = (0, $194c75143b7a1fa0$export$7c03381e0d26a6c3)("audio", "bot");
|
|
126
|
+
(0, $h9lXz$useEffect)(()=>{
|
|
127
|
+
if (!botAudioRef.current || !botAudioTrack) return;
|
|
128
|
+
if (botAudioRef.current.srcObject) {
|
|
129
|
+
const oldTrack = botAudioRef.current.srcObject.getAudioTracks()[0];
|
|
130
|
+
if (oldTrack.id === botAudioTrack.id) return;
|
|
131
|
+
}
|
|
132
|
+
botAudioRef.current.srcObject = new MediaStream([
|
|
133
|
+
botAudioTrack
|
|
134
|
+
]);
|
|
135
|
+
}, [
|
|
136
|
+
botAudioTrack
|
|
137
|
+
]);
|
|
138
|
+
(0, $824ea64b5f757259$export$33a6ac53b8f02625)((0, $h9lXz$RTVIEvent).SpeakerUpdated, (0, $h9lXz$useCallback)((speaker)=>{
|
|
139
|
+
if (!botAudioRef.current) return;
|
|
140
|
+
if (typeof botAudioRef.current.setSinkId !== "function") return;
|
|
141
|
+
botAudioRef.current.setSinkId(speaker.deviceId);
|
|
142
|
+
}, []));
|
|
143
|
+
return (0, $h9lXz$jsx)((0, $h9lXz$Fragment), {
|
|
144
|
+
children: (0, $h9lXz$jsx)("audio", {
|
|
145
|
+
ref: botAudioRef,
|
|
146
|
+
autoPlay: true
|
|
147
|
+
})
|
|
148
|
+
});
|
|
149
|
+
};
|
|
150
|
+
$f8b885726fc652c0$export$ba1245f7cbf3ae02.displayName = "RTVIClientAudio";
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Copyright (c) 2024, Daily.
|
|
159
|
+
*
|
|
160
|
+
* SPDX-License-Identifier: BSD-2-Clause
|
|
161
|
+
*
|
|
162
|
+
* This file contains code derived from:
|
|
163
|
+
* https://github.com/jaredLunde/react-hook/blob/master/packages/merged-ref/src/index.tsx
|
|
164
|
+
* Original author: Jared Lunde (https://github.com/jaredLunde)
|
|
165
|
+
* Original license: MIT (https://github.com/jaredLunde/react-hook/blob/master/LICENSE)
|
|
166
|
+
*/
|
|
167
|
+
function $9098519210cf34e2$var$useMergedRef(...refs) {
|
|
168
|
+
return (0, $h9lXz$useCallback)((element)=>{
|
|
169
|
+
for(let i = 0; i < refs.length; i++){
|
|
170
|
+
const ref = refs[i];
|
|
171
|
+
if (typeof ref === "function") ref(element);
|
|
172
|
+
else if (ref && typeof ref === "object") ref.current = element;
|
|
173
|
+
}
|
|
174
|
+
}, // eslint-disable-next-line react-hooks/exhaustive-deps
|
|
175
|
+
refs);
|
|
176
|
+
}
|
|
177
|
+
var $9098519210cf34e2$export$2e2bcd8739ae039 = $9098519210cf34e2$var$useMergedRef;
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
const $b76d887910983811$export$d090a384943608eb = /*#__PURE__*/ (0, $h9lXz$forwardRef)(function VoiceClientVideo({ participant: participant = "local", fit: fit = "contain", mirror: mirror, onResize: onResize, style: style = {}, ...props }, ref) {
|
|
181
|
+
const videoTrack = (0, $194c75143b7a1fa0$export$7c03381e0d26a6c3)("video", participant);
|
|
182
|
+
const videoEl = (0, $h9lXz$useRef)(null);
|
|
183
|
+
const videoRef = (0, $9098519210cf34e2$export$2e2bcd8739ae039)(videoEl, ref);
|
|
184
|
+
/**
|
|
185
|
+
* Handle canplay & picture-in-picture events.
|
|
186
|
+
*/ (0, $h9lXz$useEffect)(function setupVideoEvents() {
|
|
187
|
+
const video = videoEl.current;
|
|
188
|
+
if (!video) return;
|
|
189
|
+
const playVideo = ()=>{
|
|
190
|
+
const promise = video.play();
|
|
191
|
+
if (promise !== undefined) promise.then(()=>{
|
|
192
|
+
// All good, playback started.
|
|
193
|
+
video.controls = false;
|
|
194
|
+
}).catch((error)=>{
|
|
195
|
+
// Auto-play was prevented. Show video controls, so user can play video manually.
|
|
196
|
+
video.controls = true;
|
|
197
|
+
console.warn("Failed to play video", error);
|
|
198
|
+
});
|
|
199
|
+
};
|
|
200
|
+
const handleCanPlay = ()=>{
|
|
201
|
+
if (!video.paused) return;
|
|
202
|
+
playVideo();
|
|
203
|
+
};
|
|
204
|
+
const handleEnterPIP = ()=>{
|
|
205
|
+
video.style.transform = "scale(1)";
|
|
206
|
+
};
|
|
207
|
+
const handleLeavePIP = ()=>{
|
|
208
|
+
video.style.transform = "";
|
|
209
|
+
setTimeout(()=>{
|
|
210
|
+
if (video.paused) playVideo();
|
|
211
|
+
}, 100);
|
|
212
|
+
};
|
|
213
|
+
const handleVisibilityChange = ()=>{
|
|
214
|
+
if (document.visibilityState === "hidden") return;
|
|
215
|
+
if (!video.paused) return;
|
|
216
|
+
playVideo();
|
|
217
|
+
};
|
|
218
|
+
video.addEventListener("canplay", handleCanPlay);
|
|
219
|
+
video.addEventListener("enterpictureinpicture", handleEnterPIP);
|
|
220
|
+
video.addEventListener("leavepictureinpicture", handleLeavePIP);
|
|
221
|
+
// Videos can be paused if media was played in another app on iOS.
|
|
222
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
223
|
+
return ()=>{
|
|
224
|
+
video.removeEventListener("canplay", handleCanPlay);
|
|
225
|
+
video.removeEventListener("enterpictureinpicture", handleEnterPIP);
|
|
226
|
+
video.removeEventListener("leavepictureinpicture", handleLeavePIP);
|
|
227
|
+
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
228
|
+
};
|
|
229
|
+
}, []);
|
|
230
|
+
/**
|
|
231
|
+
* Update srcObject.
|
|
232
|
+
*/ (0, $h9lXz$useEffect)(function updateSrcObject() {
|
|
233
|
+
const video = videoEl.current;
|
|
234
|
+
if (!video || !videoTrack) return;
|
|
235
|
+
video.srcObject = new MediaStream([
|
|
236
|
+
videoTrack
|
|
237
|
+
]);
|
|
238
|
+
video.load();
|
|
239
|
+
return ()=>{
|
|
240
|
+
// clean up when unmounted
|
|
241
|
+
video.srcObject = null;
|
|
242
|
+
video.load();
|
|
243
|
+
};
|
|
244
|
+
}, [
|
|
245
|
+
videoTrack,
|
|
246
|
+
videoTrack?.id
|
|
247
|
+
]);
|
|
248
|
+
/**
|
|
249
|
+
* Add optional event listener for resize event so the parent component
|
|
250
|
+
* can know the video's native aspect ratio.
|
|
251
|
+
*/ (0, $h9lXz$useEffect)(function reportVideoDimensions() {
|
|
252
|
+
const video = videoEl.current;
|
|
253
|
+
if (!onResize || !video) return;
|
|
254
|
+
let frame;
|
|
255
|
+
function handleResize() {
|
|
256
|
+
if (frame) cancelAnimationFrame(frame);
|
|
257
|
+
frame = requestAnimationFrame(()=>{
|
|
258
|
+
const video = videoEl.current;
|
|
259
|
+
if (!video || document.hidden) return;
|
|
260
|
+
const videoWidth = video.videoWidth;
|
|
261
|
+
const videoHeight = video.videoHeight;
|
|
262
|
+
if (videoWidth && videoHeight) onResize?.({
|
|
263
|
+
aspectRatio: videoWidth / videoHeight,
|
|
264
|
+
height: videoHeight,
|
|
265
|
+
width: videoWidth
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
handleResize();
|
|
270
|
+
video.addEventListener("loadedmetadata", handleResize);
|
|
271
|
+
video.addEventListener("resize", handleResize);
|
|
272
|
+
return ()=>{
|
|
273
|
+
if (frame) cancelAnimationFrame(frame);
|
|
274
|
+
video.removeEventListener("loadedmetadata", handleResize);
|
|
275
|
+
video.removeEventListener("resize", handleResize);
|
|
276
|
+
};
|
|
277
|
+
}, [
|
|
278
|
+
onResize
|
|
279
|
+
]);
|
|
280
|
+
return (0, $h9lXz$jsx)("video", {
|
|
281
|
+
autoPlay: true,
|
|
282
|
+
muted: true,
|
|
283
|
+
playsInline: true,
|
|
284
|
+
ref: videoRef,
|
|
285
|
+
style: {
|
|
286
|
+
objectFit: fit,
|
|
287
|
+
transform: mirror ? "scale(-1, 1)" : "",
|
|
288
|
+
...style
|
|
289
|
+
},
|
|
290
|
+
...props
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
$b76d887910983811$export$d090a384943608eb.displayName = "RTVIClientVideo";
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
const $c7d06534b21735c2$var$availableMicsAtom = (0, $h9lXz$atom)([]);
|
|
305
|
+
const $c7d06534b21735c2$var$availableCamsAtom = (0, $h9lXz$atom)([]);
|
|
306
|
+
const $c7d06534b21735c2$var$availableSpeakersAtom = (0, $h9lXz$atom)([]);
|
|
307
|
+
const $c7d06534b21735c2$var$selectedMicAtom = (0, $h9lXz$atom)({});
|
|
308
|
+
const $c7d06534b21735c2$var$selectedCamAtom = (0, $h9lXz$atom)({});
|
|
309
|
+
const $c7d06534b21735c2$var$selectedSpeakerAtom = (0, $h9lXz$atom)({});
|
|
310
|
+
const $c7d06534b21735c2$export$652c54907b83a48d = ()=>{
|
|
311
|
+
const client = (0, $54a3c9f5bdbf0854$export$31a5f6a22c9b8fba)();
|
|
312
|
+
const availableCams = (0, $h9lXz$useAtomValue)($c7d06534b21735c2$var$availableCamsAtom);
|
|
313
|
+
const availableMics = (0, $h9lXz$useAtomValue)($c7d06534b21735c2$var$availableMicsAtom);
|
|
314
|
+
const availableSpeakers = (0, $h9lXz$useAtomValue)($c7d06534b21735c2$var$availableSpeakersAtom);
|
|
315
|
+
const selectedCam = (0, $h9lXz$useAtomValue)($c7d06534b21735c2$var$selectedCamAtom);
|
|
316
|
+
const selectedMic = (0, $h9lXz$useAtomValue)($c7d06534b21735c2$var$selectedMicAtom);
|
|
317
|
+
const selectedSpeaker = (0, $h9lXz$useAtomValue)($c7d06534b21735c2$var$selectedSpeakerAtom);
|
|
318
|
+
(0, $824ea64b5f757259$export$33a6ac53b8f02625)((0, $h9lXz$RTVIEvent).AvailableCamsUpdated, (0, $h9lXz$useAtomCallback)((0, $h9lXz$useCallback)((_get, set, cams)=>{
|
|
319
|
+
set($c7d06534b21735c2$var$availableCamsAtom, cams);
|
|
320
|
+
}, [])));
|
|
321
|
+
(0, $824ea64b5f757259$export$33a6ac53b8f02625)((0, $h9lXz$RTVIEvent).AvailableMicsUpdated, (0, $h9lXz$useAtomCallback)((0, $h9lXz$useCallback)((_get, set, mics)=>{
|
|
322
|
+
set($c7d06534b21735c2$var$availableMicsAtom, mics);
|
|
323
|
+
}, [])));
|
|
324
|
+
(0, $824ea64b5f757259$export$33a6ac53b8f02625)((0, $h9lXz$RTVIEvent).AvailableSpeakersUpdated, (0, $h9lXz$useAtomCallback)((0, $h9lXz$useCallback)((_get, set, speakers)=>{
|
|
325
|
+
set($c7d06534b21735c2$var$availableSpeakersAtom, speakers);
|
|
326
|
+
}, [])));
|
|
327
|
+
(0, $824ea64b5f757259$export$33a6ac53b8f02625)((0, $h9lXz$RTVIEvent).CamUpdated, (0, $h9lXz$useAtomCallback)((0, $h9lXz$useCallback)((_get, set, cam)=>{
|
|
328
|
+
set($c7d06534b21735c2$var$selectedCamAtom, cam);
|
|
329
|
+
}, [])));
|
|
330
|
+
(0, $824ea64b5f757259$export$33a6ac53b8f02625)((0, $h9lXz$RTVIEvent).MicUpdated, (0, $h9lXz$useAtomCallback)((0, $h9lXz$useCallback)((_get, set, mic)=>{
|
|
331
|
+
set($c7d06534b21735c2$var$selectedMicAtom, mic);
|
|
332
|
+
}, [])));
|
|
333
|
+
(0, $824ea64b5f757259$export$33a6ac53b8f02625)((0, $h9lXz$RTVIEvent).SpeakerUpdated, (0, $h9lXz$useAtomCallback)((0, $h9lXz$useCallback)((_get, set, speaker)=>{
|
|
334
|
+
set($c7d06534b21735c2$var$selectedSpeakerAtom, speaker);
|
|
335
|
+
}, [])));
|
|
336
|
+
const updateCam = (0, $h9lXz$useCallback)((id)=>{
|
|
337
|
+
client?.updateCam(id);
|
|
338
|
+
}, [
|
|
339
|
+
client
|
|
340
|
+
]);
|
|
341
|
+
const updateMic = (0, $h9lXz$useCallback)((id)=>{
|
|
342
|
+
client?.updateMic(id);
|
|
343
|
+
}, [
|
|
344
|
+
client
|
|
345
|
+
]);
|
|
346
|
+
const updateSpeaker = (0, $h9lXz$useCallback)((id)=>{
|
|
347
|
+
client?.updateSpeaker(id);
|
|
348
|
+
}, [
|
|
349
|
+
client
|
|
350
|
+
]);
|
|
351
|
+
return {
|
|
352
|
+
availableCams: availableCams,
|
|
353
|
+
availableMics: availableMics,
|
|
354
|
+
availableSpeakers: availableSpeakers,
|
|
355
|
+
selectedCam: selectedCam,
|
|
356
|
+
selectedMic: selectedMic,
|
|
357
|
+
selectedSpeaker: selectedSpeaker,
|
|
358
|
+
updateCam: updateCam,
|
|
359
|
+
updateMic: updateMic,
|
|
360
|
+
updateSpeaker: updateSpeaker
|
|
361
|
+
};
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Copyright (c) 2024, Daily.
|
|
368
|
+
*
|
|
369
|
+
* SPDX-License-Identifier: BSD-2-Clause
|
|
370
|
+
*/
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
const $8376ffbc1b1f3c97$var$transportStateAtom = (0, $h9lXz$atom)("disconnected");
|
|
374
|
+
const $8376ffbc1b1f3c97$export$599fa01283bd4ece = ()=>{
|
|
375
|
+
const [transportState, setTransportState] = (0, $h9lXz$useAtom)($8376ffbc1b1f3c97$var$transportStateAtom);
|
|
376
|
+
(0, $824ea64b5f757259$export$33a6ac53b8f02625)((0, $h9lXz$RTVIEvent).TransportStateChanged, setTransportState);
|
|
377
|
+
return transportState;
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
const $993a744193844a95$export$59bf27bd43679db6 = /*#__PURE__*/ (0, $h9lXz$react).memo(({ backgroundColor: backgroundColor = "transparent", barColor: barColor = "black", barWidth: barWidth = 30, barGap: barGap = 12, barMaxHeight: barMaxHeight = 120, participantType: participantType })=>{
|
|
385
|
+
const canvasRef = (0, $h9lXz$useRef)(null);
|
|
386
|
+
const track = (0, $194c75143b7a1fa0$export$7c03381e0d26a6c3)("audio", participantType);
|
|
387
|
+
(0, $h9lXz$useEffect)(()=>{
|
|
388
|
+
if (!canvasRef.current) return;
|
|
389
|
+
const canvasWidth = 5 * barWidth + 4 * barGap;
|
|
390
|
+
const canvasHeight = barMaxHeight;
|
|
391
|
+
const canvas = canvasRef.current;
|
|
392
|
+
const scaleFactor = 2;
|
|
393
|
+
// Make canvas fill the width and height of its container
|
|
394
|
+
const resizeCanvas = ()=>{
|
|
395
|
+
canvas.width = canvasWidth * scaleFactor;
|
|
396
|
+
canvas.height = canvasHeight * scaleFactor;
|
|
397
|
+
canvas.style.width = `${canvasWidth}px`;
|
|
398
|
+
canvas.style.height = `${canvasHeight}px`;
|
|
399
|
+
canvasCtx.lineCap = "round";
|
|
400
|
+
canvasCtx.scale(scaleFactor, scaleFactor);
|
|
401
|
+
};
|
|
402
|
+
const canvasCtx = canvas.getContext("2d");
|
|
403
|
+
resizeCanvas();
|
|
404
|
+
if (!track) return;
|
|
405
|
+
const audioContext = new AudioContext();
|
|
406
|
+
const source = audioContext.createMediaStreamSource(new MediaStream([
|
|
407
|
+
track
|
|
408
|
+
]));
|
|
409
|
+
const analyser = audioContext.createAnalyser();
|
|
410
|
+
analyser.fftSize = 1024;
|
|
411
|
+
source.connect(analyser);
|
|
412
|
+
const frequencyData = new Uint8Array(analyser.frequencyBinCount);
|
|
413
|
+
canvasCtx.lineCap = "round";
|
|
414
|
+
const bands = [
|
|
415
|
+
{
|
|
416
|
+
startFreq: 85,
|
|
417
|
+
endFreq: 255,
|
|
418
|
+
smoothValue: 0
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
startFreq: 255,
|
|
422
|
+
endFreq: 500,
|
|
423
|
+
smoothValue: 0
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
startFreq: 500,
|
|
427
|
+
endFreq: 2000,
|
|
428
|
+
smoothValue: 0
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
startFreq: 2000,
|
|
432
|
+
endFreq: 4000,
|
|
433
|
+
smoothValue: 0
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
startFreq: 4000,
|
|
437
|
+
endFreq: 8000,
|
|
438
|
+
smoothValue: 0
|
|
439
|
+
}
|
|
440
|
+
];
|
|
441
|
+
const getFrequencyBinIndex = (frequency)=>{
|
|
442
|
+
const nyquist = audioContext.sampleRate / 2;
|
|
443
|
+
return Math.round(frequency / nyquist * (analyser.frequencyBinCount - 1));
|
|
444
|
+
};
|
|
445
|
+
function drawSpectrum() {
|
|
446
|
+
analyser.getByteFrequencyData(frequencyData);
|
|
447
|
+
canvasCtx.clearRect(0, 0, canvas.width / scaleFactor, canvas.height / scaleFactor);
|
|
448
|
+
canvasCtx.fillStyle = backgroundColor;
|
|
449
|
+
canvasCtx.fillRect(0, 0, canvas.width / scaleFactor, canvas.height / scaleFactor);
|
|
450
|
+
let isActive = false;
|
|
451
|
+
const totalBarsWidth = bands.length * barWidth + (bands.length - 1) * barGap;
|
|
452
|
+
const startX = (canvas.width / scaleFactor - totalBarsWidth) / 2; // Center bars
|
|
453
|
+
const adjustedCircleRadius = barWidth / 2; // Fixed radius for reset circles
|
|
454
|
+
bands.forEach((band, i)=>{
|
|
455
|
+
const startIndex = getFrequencyBinIndex(band.startFreq);
|
|
456
|
+
const endIndex = getFrequencyBinIndex(band.endFreq);
|
|
457
|
+
const bandData = frequencyData.slice(startIndex, endIndex);
|
|
458
|
+
const bandValue = bandData.reduce((acc, val)=>acc + val, 0) / bandData.length;
|
|
459
|
+
const smoothingFactor = 0.2;
|
|
460
|
+
if (bandValue < 1) band.smoothValue = Math.max(band.smoothValue - smoothingFactor * 5, 0);
|
|
461
|
+
else {
|
|
462
|
+
band.smoothValue = band.smoothValue + (bandValue - band.smoothValue) * smoothingFactor;
|
|
463
|
+
isActive = true;
|
|
464
|
+
}
|
|
465
|
+
const x = startX + i * (barWidth + barGap);
|
|
466
|
+
// Calculate bar height with a maximum cap
|
|
467
|
+
const barHeight = Math.min(band.smoothValue / 255 * barMaxHeight, barMaxHeight);
|
|
468
|
+
const yTop = Math.max(canvas.height / scaleFactor / 2 - barHeight / 2, adjustedCircleRadius);
|
|
469
|
+
const yBottom = Math.min(canvas.height / scaleFactor / 2 + barHeight / 2, canvas.height / scaleFactor - adjustedCircleRadius);
|
|
470
|
+
if (band.smoothValue > 0) {
|
|
471
|
+
canvasCtx.beginPath();
|
|
472
|
+
canvasCtx.moveTo(x + barWidth / 2, yTop);
|
|
473
|
+
canvasCtx.lineTo(x + barWidth / 2, yBottom);
|
|
474
|
+
canvasCtx.lineWidth = barWidth;
|
|
475
|
+
canvasCtx.strokeStyle = barColor;
|
|
476
|
+
canvasCtx.stroke();
|
|
477
|
+
} else {
|
|
478
|
+
canvasCtx.beginPath();
|
|
479
|
+
canvasCtx.arc(x + barWidth / 2, canvas.height / scaleFactor / 2, adjustedCircleRadius, 0, 2 * Math.PI);
|
|
480
|
+
canvasCtx.fillStyle = barColor;
|
|
481
|
+
canvasCtx.fill();
|
|
482
|
+
canvasCtx.closePath();
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
if (!isActive) drawInactiveCircles(adjustedCircleRadius, barColor);
|
|
486
|
+
requestAnimationFrame(drawSpectrum);
|
|
487
|
+
}
|
|
488
|
+
function drawInactiveCircles(circleRadius, color) {
|
|
489
|
+
const totalBarsWidth = bands.length * barWidth + (bands.length - 1) * barGap;
|
|
490
|
+
const startX = (canvas.width / scaleFactor - totalBarsWidth) / 2;
|
|
491
|
+
const y = canvas.height / scaleFactor / 2;
|
|
492
|
+
bands.forEach((_, i)=>{
|
|
493
|
+
const x = startX + i * (barWidth + barGap);
|
|
494
|
+
canvasCtx.beginPath();
|
|
495
|
+
canvasCtx.arc(x + barWidth / 2, y, circleRadius, 0, 2 * Math.PI);
|
|
496
|
+
canvasCtx.fillStyle = color;
|
|
497
|
+
canvasCtx.fill();
|
|
498
|
+
canvasCtx.closePath();
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
drawSpectrum();
|
|
502
|
+
// Handle resizing
|
|
503
|
+
window.addEventListener("resize", resizeCanvas);
|
|
504
|
+
return ()=>{
|
|
505
|
+
audioContext.close();
|
|
506
|
+
window.removeEventListener("resize", resizeCanvas);
|
|
507
|
+
};
|
|
508
|
+
}, [
|
|
509
|
+
backgroundColor,
|
|
510
|
+
barColor,
|
|
511
|
+
barGap,
|
|
512
|
+
barMaxHeight,
|
|
513
|
+
barWidth,
|
|
514
|
+
track
|
|
515
|
+
]);
|
|
516
|
+
return (0, $h9lXz$jsx)("canvas", {
|
|
517
|
+
ref: canvasRef,
|
|
518
|
+
style: {
|
|
519
|
+
display: "block",
|
|
520
|
+
width: "100%",
|
|
521
|
+
height: "100%"
|
|
522
|
+
}
|
|
523
|
+
});
|
|
524
|
+
});
|
|
525
|
+
$993a744193844a95$export$59bf27bd43679db6.displayName = "VoiceVisualizer";
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
export {$f8b885726fc652c0$export$ba1245f7cbf3ae02 as RTVIClientAudio, $f3f7d4263dc13c6a$export$4a4ae2d5dc96782 as RTVIClientProvider, $b76d887910983811$export$d090a384943608eb as RTVIClientVideo, $54a3c9f5bdbf0854$export$31a5f6a22c9b8fba as useRTVIClient, $824ea64b5f757259$export$33a6ac53b8f02625 as useRTVIClientEvent, $c7d06534b21735c2$export$652c54907b83a48d as useRTVIClientMediaDevices, $194c75143b7a1fa0$export$7c03381e0d26a6c3 as useRTVIClientMediaTrack, $8376ffbc1b1f3c97$export$599fa01283bd4ece as useRTVIClientTransportState, $993a744193844a95$export$59bf27bd43679db6 as VoiceVisualizer};
|
|
531
|
+
//# sourceMappingURL=index.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"mappings":"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","sources":["client-react/src/index.ts","client-react/src/RTVIClientAudio.tsx","client-react/src/useRTVIClientEvent.ts","client-react/src/useRTVIClient.ts","client-react/src/RTVIClientProvider.tsx","client-react/src/useRTVIClientMediaTrack.ts","client-react/src/RTVIClientVideo.tsx","client-react/src/useMergedRef.ts","client-react/src/useRTVIClientMediaDevices.ts","client-react/src/useRTVIClientTransportState.ts","client-react/src/VoiceVisualizer.tsx"],"sourcesContent":["/**\n * Copyright (c) 2024, Daily.\n *\n * SPDX-License-Identifier: BSD-2-Clause\n */\n\nimport { RTVIClientAudio } from \"./RTVIClientAudio\";\nimport { RTVIClientProvider } from \"./RTVIClientProvider\";\nimport { RTVIClientVideo } from \"./RTVIClientVideo\";\nimport { useRTVIClient } from \"./useRTVIClient\";\nimport { useRTVIClientEvent } from \"./useRTVIClientEvent\";\nimport { useRTVIClientMediaDevices } from \"./useRTVIClientMediaDevices\";\nimport { useRTVIClientMediaTrack } from \"./useRTVIClientMediaTrack\";\nimport { useRTVIClientTransportState } from \"./useRTVIClientTransportState\";\nimport { VoiceVisualizer } from \"./VoiceVisualizer\";\n\nexport {\n RTVIClientAudio,\n RTVIClientProvider,\n RTVIClientVideo,\n useRTVIClient,\n useRTVIClientEvent,\n useRTVIClientMediaDevices,\n useRTVIClientMediaTrack,\n useRTVIClientTransportState,\n VoiceVisualizer,\n};\n","/**\n * Copyright (c) 2024, Daily.\n *\n * SPDX-License-Identifier: BSD-2-Clause\n */\n\nimport { RTVIEvent } from \"@pipecat-ai/client-js\";\nimport { useCallback, useEffect, useRef } from \"react\";\nimport { useRTVIClientEvent } from \"./useRTVIClientEvent\";\nimport { useRTVIClientMediaTrack } from \"./useRTVIClientMediaTrack\";\n\nexport const RTVIClientAudio = () => {\n const botAudioRef = useRef<HTMLAudioElement>(null);\n const botAudioTrack = useRTVIClientMediaTrack(\"audio\", \"bot\");\n\n useEffect(() => {\n if (!botAudioRef.current || !botAudioTrack) return;\n if (botAudioRef.current.srcObject) {\n const oldTrack = (\n botAudioRef.current.srcObject as MediaStream\n ).getAudioTracks()[0];\n if (oldTrack.id === botAudioTrack.id) return;\n }\n botAudioRef.current.srcObject = new MediaStream([botAudioTrack]);\n }, [botAudioTrack]);\n\n useRTVIClientEvent(\n RTVIEvent.SpeakerUpdated,\n useCallback((speaker: MediaDeviceInfo) => {\n if (!botAudioRef.current) return;\n if (typeof botAudioRef.current.setSinkId !== \"function\") return;\n botAudioRef.current.setSinkId(speaker.deviceId);\n }, [])\n );\n\n return (\n <>\n <audio ref={botAudioRef} autoPlay />\n </>\n );\n};\nRTVIClientAudio.displayName = \"RTVIClientAudio\";\n","/**\n * Copyright (c) 2024, Daily.\n *\n * SPDX-License-Identifier: BSD-2-Clause\n */\n\nimport { RTVIEvent, RTVIEventHandler } from \"@pipecat-ai/client-js\";\nimport { useEffect } from \"react\";\nimport { useRTVIClient } from \"./useRTVIClient\";\n\nexport const useRTVIClientEvent = <E extends RTVIEvent>(\n event: E,\n handler: RTVIEventHandler<E>\n) => {\n const client = useRTVIClient();\n\n useEffect(() => {\n if (!client) return;\n client.on(event, handler);\n return () => {\n client.off(event, handler);\n };\n }, [event, handler, client]);\n};\n","/**\n * Copyright (c) 2024, Daily.\n *\n * SPDX-License-Identifier: BSD-2-Clause\n */\n\nimport { useContext } from \"react\";\nimport { RTVIClientContext } from \"./RTVIClientProvider\";\n\nexport const useRTVIClient = () => {\n const { client } = useContext(RTVIClientContext);\n return client;\n};\n","/**\n * Copyright (c) 2024, Daily.\n *\n * SPDX-License-Identifier: BSD-2-Clause\n */\n\nimport { createContext } from \"react\";\n\nimport { RTVIClient } from \"@pipecat-ai/client-js\";\nimport { createStore } from \"jotai\";\nimport { Provider as JotaiProvider } from \"jotai/react\";\n\nexport interface Props {\n client: RTVIClient;\n jotaiStore?: React.ComponentProps<typeof JotaiProvider>[\"store\"];\n}\n\nconst defaultStore = createStore();\n\nexport const RTVIClientContext = createContext<{ client?: RTVIClient }>({});\n\nexport const RTVIClientProvider: React.FC<React.PropsWithChildren<Props>> = ({\n children,\n client,\n jotaiStore = defaultStore,\n}) => {\n return (\n <JotaiProvider store={jotaiStore}>\n <RTVIClientContext.Provider value={{ client }}>\n {children}\n </RTVIClientContext.Provider>\n </JotaiProvider>\n );\n};\nRTVIClientProvider.displayName = \"RTVIClientProvider\";\n","/**\n * Copyright (c) 2024, Daily.\n *\n * SPDX-License-Identifier: BSD-2-Clause\n */\n\nimport { Participant, RTVIEvent, Tracks } from \"@pipecat-ai/client-js\";\nimport { atom, useAtomValue } from \"jotai\";\nimport { atomFamily, useAtomCallback } from \"jotai/utils\";\nimport { PrimitiveAtom } from \"jotai/vanilla\";\nimport { useCallback, useEffect } from \"react\";\nimport { useRTVIClient } from \"./useRTVIClient\";\nimport { useRTVIClientEvent } from \"./useRTVIClientEvent\";\n\ntype ParticipantType = keyof Tracks;\ntype TrackType = keyof Tracks[\"local\"];\n\nconst localAudioTrackAtom = atom<MediaStreamTrack | null>(null);\nconst localVideoTrackAtom = atom<MediaStreamTrack | null>(null);\nconst botAudioTrackAtom = atom<MediaStreamTrack | null>(null);\nconst botVideoTrackAtom = atom<MediaStreamTrack | null>(null);\n\nconst trackAtom = atomFamily<\n { local: boolean; trackType: TrackType },\n PrimitiveAtom<MediaStreamTrack | null>\n>(({ local, trackType }) => {\n if (local)\n return trackType === \"audio\" ? localAudioTrackAtom : localVideoTrackAtom;\n return trackType === \"audio\" ? botAudioTrackAtom : botVideoTrackAtom;\n});\n\nexport const useRTVIClientMediaTrack = (\n trackType: TrackType,\n participantType: ParticipantType\n) => {\n const client = useRTVIClient();\n const track = useAtomValue(\n trackAtom({ local: participantType === \"local\", trackType })\n );\n\n const updateTrack = useAtomCallback(\n useCallback(\n (\n get,\n set,\n track: MediaStreamTrack,\n trackType: TrackType,\n local: boolean\n ) => {\n const atom = trackAtom({\n local,\n trackType,\n });\n const oldTrack = get(atom);\n if (oldTrack?.id === track.id) return;\n set(atom, track);\n },\n [participantType, track, trackType]\n )\n );\n\n useRTVIClientEvent(\n RTVIEvent.TrackStarted,\n useCallback((track: MediaStreamTrack, participant?: Participant) => {\n updateTrack(track, track.kind as TrackType, Boolean(participant?.local));\n }, [])\n );\n\n useEffect(() => {\n if (!client) return;\n const tracks = client.tracks();\n const track = tracks?.[participantType]?.[trackType];\n if (!track) return;\n updateTrack(track, trackType, participantType === \"local\");\n }, [participantType, trackType, updateTrack, client]);\n\n return track;\n};\n","/**\n * Copyright (c) 2024, Daily.\n *\n * SPDX-License-Identifier: BSD-2-Clause\n */\n\nimport React, { forwardRef, useEffect, useRef } from \"react\";\nimport { useRTVIClientMediaTrack } from \"./useRTVIClientMediaTrack\";\nimport useMergedRef from \"./useMergedRef\";\n\ninterface RTVIClientVideoInterface {\n aspectRatio: number;\n height: number;\n width: number;\n}\n\nexport interface Props\n extends Omit<React.VideoHTMLAttributes<HTMLVideoElement>, \"onResize\"> {\n participant: \"local\" | \"bot\";\n\n /**\n * Defines whether the video should be fully contained or cover the box. Default: 'contain'.\n */\n fit?: \"contain\" | \"cover\";\n /**\n * Forces the video to be mirrored, if set.\n */\n mirror?: boolean;\n\n /**\n * Optional callback, which is triggered whenever the video's rendered width or height changes.\n * Returns the video's native width, height and aspectRatio.\n */\n onResize?(dimensions: RTVIClientVideoInterface): void;\n}\n\nexport const RTVIClientVideo = forwardRef<HTMLVideoElement, Props>(\n function VoiceClientVideo(\n {\n participant = \"local\",\n fit = \"contain\",\n mirror,\n onResize,\n style = {},\n ...props\n },\n ref\n ) {\n const videoTrack: MediaStreamTrack | null = useRTVIClientMediaTrack(\n \"video\",\n participant\n );\n\n const videoEl = useRef<HTMLVideoElement>(null);\n const videoRef = useMergedRef<HTMLVideoElement>(videoEl, ref);\n\n /**\n * Handle canplay & picture-in-picture events.\n */\n useEffect(function setupVideoEvents() {\n const video = videoEl.current;\n if (!video) return;\n\n const playVideo = () => {\n const promise = video.play();\n if (promise !== undefined) {\n promise\n .then(() => {\n // All good, playback started.\n video.controls = false;\n })\n .catch((error) => {\n // Auto-play was prevented. Show video controls, so user can play video manually.\n video.controls = true;\n console.warn(\"Failed to play video\", error);\n });\n }\n };\n\n const handleCanPlay = () => {\n if (!video.paused) return;\n playVideo();\n };\n const handleEnterPIP = () => {\n video.style.transform = \"scale(1)\";\n };\n const handleLeavePIP = () => {\n video.style.transform = \"\";\n setTimeout(() => {\n if (video.paused) playVideo();\n }, 100);\n };\n const handleVisibilityChange = () => {\n if (document.visibilityState === \"hidden\") return;\n if (!video.paused) return;\n playVideo();\n };\n video.addEventListener(\"canplay\", handleCanPlay);\n video.addEventListener(\"enterpictureinpicture\", handleEnterPIP);\n video.addEventListener(\"leavepictureinpicture\", handleLeavePIP);\n\n // Videos can be paused if media was played in another app on iOS.\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\n return () => {\n video.removeEventListener(\"canplay\", handleCanPlay);\n video.removeEventListener(\"enterpictureinpicture\", handleEnterPIP);\n video.removeEventListener(\"leavepictureinpicture\", handleLeavePIP);\n document.removeEventListener(\n \"visibilitychange\",\n handleVisibilityChange\n );\n };\n }, []);\n\n /**\n * Update srcObject.\n */\n useEffect(\n function updateSrcObject() {\n const video = videoEl.current;\n if (!video || !videoTrack) return;\n video.srcObject = new MediaStream([videoTrack]);\n video.load();\n return () => {\n // clean up when unmounted\n video.srcObject = null;\n video.load();\n };\n },\n [videoTrack, videoTrack?.id]\n );\n\n /**\n * Add optional event listener for resize event so the parent component\n * can know the video's native aspect ratio.\n */\n useEffect(\n function reportVideoDimensions() {\n const video = videoEl.current;\n if (!onResize || !video) return;\n\n let frame: ReturnType<typeof requestAnimationFrame>;\n function handleResize() {\n if (frame) cancelAnimationFrame(frame);\n frame = requestAnimationFrame(() => {\n const video = videoEl.current;\n if (!video || document.hidden) return;\n const videoWidth = video.videoWidth;\n const videoHeight = video.videoHeight;\n if (videoWidth && videoHeight) {\n onResize?.({\n aspectRatio: videoWidth / videoHeight,\n height: videoHeight,\n width: videoWidth,\n });\n }\n });\n }\n\n handleResize();\n video.addEventListener(\"loadedmetadata\", handleResize);\n video.addEventListener(\"resize\", handleResize);\n\n return () => {\n if (frame) cancelAnimationFrame(frame);\n video.removeEventListener(\"loadedmetadata\", handleResize);\n video.removeEventListener(\"resize\", handleResize);\n };\n },\n [onResize]\n );\n\n return (\n <video\n autoPlay\n muted\n playsInline\n ref={videoRef}\n style={{\n objectFit: fit,\n transform: mirror ? \"scale(-1, 1)\" : \"\",\n ...style,\n }}\n {...props}\n />\n );\n }\n);\nRTVIClientVideo.displayName = \"RTVIClientVideo\";\n","/**\n * Copyright (c) 2024, Daily.\n *\n * SPDX-License-Identifier: BSD-2-Clause\n *\n * This file contains code derived from:\n * https://github.com/jaredLunde/react-hook/blob/master/packages/merged-ref/src/index.tsx\n * Original author: Jared Lunde (https://github.com/jaredLunde)\n * Original license: MIT (https://github.com/jaredLunde/react-hook/blob/master/LICENSE)\n */\n\nimport React, { useCallback } from \"react\";\n\nfunction useMergedRef<T>(...refs: React.Ref<T>[]): React.RefCallback<T> {\n return useCallback(\n (element: T) => {\n for (let i = 0; i < refs.length; i++) {\n const ref = refs[i];\n if (typeof ref === \"function\") ref(element);\n else if (ref && typeof ref === \"object\")\n (ref as React.MutableRefObject<T>).current = element;\n }\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n refs\n );\n}\n\nexport default useMergedRef;\n","import { RTVIEvent } from \"@pipecat-ai/client-js\";\nimport { atom, useAtomValue } from \"jotai\";\nimport { useAtomCallback } from \"jotai/utils\";\nimport { useCallback } from \"react\";\nimport { useRTVIClient } from \"./useRTVIClient\";\nimport { useRTVIClientEvent } from \"./useRTVIClientEvent\";\n\ntype OptionalMediaDeviceInfo = MediaDeviceInfo | Record<string, never>;\n\nconst availableMicsAtom = atom<MediaDeviceInfo[]>([]);\nconst availableCamsAtom = atom<MediaDeviceInfo[]>([]);\nconst availableSpeakersAtom = atom<MediaDeviceInfo[]>([]);\nconst selectedMicAtom = atom<OptionalMediaDeviceInfo>({});\nconst selectedCamAtom = atom<OptionalMediaDeviceInfo>({});\nconst selectedSpeakerAtom = atom<OptionalMediaDeviceInfo>({});\n\nexport const useRTVIClientMediaDevices = () => {\n const client = useRTVIClient();\n\n const availableCams = useAtomValue(availableCamsAtom);\n const availableMics = useAtomValue(availableMicsAtom);\n const availableSpeakers = useAtomValue(availableSpeakersAtom);\n const selectedCam = useAtomValue(selectedCamAtom);\n const selectedMic = useAtomValue(selectedMicAtom);\n const selectedSpeaker = useAtomValue(selectedSpeakerAtom);\n\n useRTVIClientEvent(\n RTVIEvent.AvailableCamsUpdated,\n useAtomCallback(\n useCallback((_get, set, cams) => {\n set(availableCamsAtom, cams);\n }, [])\n )\n );\n useRTVIClientEvent(\n RTVIEvent.AvailableMicsUpdated,\n useAtomCallback(\n useCallback((_get, set, mics) => {\n set(availableMicsAtom, mics);\n }, [])\n )\n );\n useRTVIClientEvent(\n RTVIEvent.AvailableSpeakersUpdated,\n useAtomCallback(\n useCallback((_get, set, speakers) => {\n set(availableSpeakersAtom, speakers);\n }, [])\n )\n );\n useRTVIClientEvent(\n RTVIEvent.CamUpdated,\n useAtomCallback(\n useCallback((_get, set, cam) => {\n set(selectedCamAtom, cam);\n }, [])\n )\n );\n useRTVIClientEvent(\n RTVIEvent.MicUpdated,\n useAtomCallback(\n useCallback((_get, set, mic) => {\n set(selectedMicAtom, mic);\n }, [])\n )\n );\n useRTVIClientEvent(\n RTVIEvent.SpeakerUpdated,\n useAtomCallback(\n useCallback((_get, set, speaker) => {\n set(selectedSpeakerAtom, speaker);\n }, [])\n )\n );\n\n const updateCam = useCallback(\n (id: string) => {\n client?.updateCam(id);\n },\n [client]\n );\n const updateMic = useCallback(\n (id: string) => {\n client?.updateMic(id);\n },\n [client]\n );\n const updateSpeaker = useCallback(\n (id: string) => {\n client?.updateSpeaker(id);\n },\n [client]\n );\n\n return {\n availableCams,\n availableMics,\n availableSpeakers,\n selectedCam,\n selectedMic,\n selectedSpeaker,\n updateCam,\n updateMic,\n updateSpeaker,\n };\n};\n","/**\n * Copyright (c) 2024, Daily.\n *\n * SPDX-License-Identifier: BSD-2-Clause\n */\n\nimport { RTVIEvent, TransportState } from \"@pipecat-ai/client-js\";\nimport { atom, useAtom } from \"jotai\";\nimport { useRTVIClientEvent } from \"./useRTVIClientEvent\";\n\nconst transportStateAtom = atom<TransportState>(\"disconnected\");\n\nexport const useRTVIClientTransportState = () => {\n const [transportState, setTransportState] = useAtom(transportStateAtom);\n\n useRTVIClientEvent(RTVIEvent.TransportStateChanged, setTransportState);\n\n return transportState;\n};\n","/**\n * Copyright (c) 2024, Daily.\n *\n * SPDX-License-Identifier: BSD-2-Clause\n */\n\nimport React, { useEffect, useRef } from \"react\";\nimport { useRTVIClientMediaTrack } from \"./useRTVIClientMediaTrack\";\n\ntype ParticipantType = Parameters<typeof useRTVIClientMediaTrack>[1];\n\ninterface Props {\n backgroundColor?: string;\n barColor?: string;\n barGap?: number;\n barWidth?: number;\n barMaxHeight?: number;\n participantType: ParticipantType;\n}\n\nexport const VoiceVisualizer: React.FC<Props> = React.memo(\n ({\n backgroundColor = \"transparent\",\n barColor = \"black\",\n barWidth = 30,\n barGap = 12,\n barMaxHeight = 120,\n participantType,\n }) => {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n\n const track: MediaStreamTrack | null = useRTVIClientMediaTrack(\n \"audio\",\n participantType\n );\n\n useEffect(() => {\n if (!canvasRef.current) return;\n\n const canvasWidth = 5 * barWidth + 4 * barGap;\n const canvasHeight = barMaxHeight;\n\n const canvas = canvasRef.current;\n\n const scaleFactor = 2;\n\n // Make canvas fill the width and height of its container\n const resizeCanvas = () => {\n canvas.width = canvasWidth * scaleFactor;\n canvas.height = canvasHeight * scaleFactor;\n\n canvas.style.width = `${canvasWidth}px`;\n canvas.style.height = `${canvasHeight}px`;\n\n canvasCtx.lineCap = \"round\";\n canvasCtx.scale(scaleFactor, scaleFactor);\n };\n\n const canvasCtx = canvas.getContext(\"2d\")!;\n resizeCanvas();\n\n if (!track) return;\n\n const audioContext = new AudioContext();\n const source = audioContext.createMediaStreamSource(\n new MediaStream([track])\n );\n const analyser = audioContext.createAnalyser();\n\n analyser.fftSize = 1024;\n\n source.connect(analyser);\n\n const frequencyData = new Uint8Array(analyser.frequencyBinCount);\n\n canvasCtx.lineCap = \"round\";\n\n const bands = [\n { startFreq: 85, endFreq: 255, smoothValue: 0 }, // Covers fundamental frequencies for male and female voices\n { startFreq: 255, endFreq: 500, smoothValue: 0 }, // Lower formants and some harmonics\n { startFreq: 500, endFreq: 2000, smoothValue: 0 }, // Vowel formants and key consonant frequencies\n { startFreq: 2000, endFreq: 4000, smoothValue: 0 }, // Higher formants, \"clarity\" of speech\n { startFreq: 4000, endFreq: 8000, smoothValue: 0 }, // Sibilance and high-frequency consonants\n ];\n\n const getFrequencyBinIndex = (frequency: number) => {\n const nyquist = audioContext.sampleRate / 2;\n return Math.round(\n (frequency / nyquist) * (analyser.frequencyBinCount - 1)\n );\n };\n\n function drawSpectrum() {\n analyser.getByteFrequencyData(frequencyData);\n canvasCtx.clearRect(\n 0,\n 0,\n canvas.width / scaleFactor,\n canvas.height / scaleFactor\n );\n canvasCtx.fillStyle = backgroundColor;\n canvasCtx.fillRect(\n 0,\n 0,\n canvas.width / scaleFactor,\n canvas.height / scaleFactor\n );\n\n let isActive = false;\n\n const totalBarsWidth =\n bands.length * barWidth + (bands.length - 1) * barGap;\n const startX = (canvas.width / scaleFactor - totalBarsWidth) / 2; // Center bars\n\n const adjustedCircleRadius = barWidth / 2; // Fixed radius for reset circles\n\n bands.forEach((band, i) => {\n const startIndex = getFrequencyBinIndex(band.startFreq);\n const endIndex = getFrequencyBinIndex(band.endFreq);\n const bandData = frequencyData.slice(startIndex, endIndex);\n const bandValue =\n bandData.reduce((acc, val) => acc + val, 0) / bandData.length;\n\n const smoothingFactor = 0.2;\n\n if (bandValue < 1) {\n band.smoothValue = Math.max(\n band.smoothValue - smoothingFactor * 5,\n 0\n );\n } else {\n band.smoothValue =\n band.smoothValue +\n (bandValue - band.smoothValue) * smoothingFactor;\n isActive = true;\n }\n\n const x = startX + i * (barWidth + barGap);\n // Calculate bar height with a maximum cap\n const barHeight = Math.min(\n (band.smoothValue / 255) * barMaxHeight,\n barMaxHeight\n );\n\n const yTop = Math.max(\n canvas.height / scaleFactor / 2 - barHeight / 2,\n adjustedCircleRadius\n );\n const yBottom = Math.min(\n canvas.height / scaleFactor / 2 + barHeight / 2,\n canvas.height / scaleFactor - adjustedCircleRadius\n );\n\n if (band.smoothValue > 0) {\n canvasCtx.beginPath();\n canvasCtx.moveTo(x + barWidth / 2, yTop);\n canvasCtx.lineTo(x + barWidth / 2, yBottom);\n canvasCtx.lineWidth = barWidth;\n canvasCtx.strokeStyle = barColor;\n canvasCtx.stroke();\n } else {\n canvasCtx.beginPath();\n canvasCtx.arc(\n x + barWidth / 2,\n canvas.height / scaleFactor / 2,\n adjustedCircleRadius,\n 0,\n 2 * Math.PI\n );\n canvasCtx.fillStyle = barColor;\n canvasCtx.fill();\n canvasCtx.closePath();\n }\n });\n\n if (!isActive) {\n drawInactiveCircles(adjustedCircleRadius, barColor);\n }\n\n requestAnimationFrame(drawSpectrum);\n }\n\n function drawInactiveCircles(circleRadius: number, color: string) {\n const totalBarsWidth =\n bands.length * barWidth + (bands.length - 1) * barGap;\n const startX = (canvas.width / scaleFactor - totalBarsWidth) / 2;\n const y = canvas.height / scaleFactor / 2;\n\n bands.forEach((_, i) => {\n const x = startX + i * (barWidth + barGap);\n\n canvasCtx.beginPath();\n canvasCtx.arc(x + barWidth / 2, y, circleRadius, 0, 2 * Math.PI);\n canvasCtx.fillStyle = color;\n canvasCtx.fill();\n canvasCtx.closePath();\n });\n }\n\n drawSpectrum();\n\n // Handle resizing\n window.addEventListener(\"resize\", resizeCanvas);\n\n return () => {\n audioContext.close();\n window.removeEventListener(\"resize\", resizeCanvas);\n };\n }, [backgroundColor, barColor, barGap, barMaxHeight, barWidth, track]);\n\n return (\n <canvas\n ref={canvasRef}\n style={{\n display: \"block\",\n width: \"100%\",\n height: \"100%\",\n }}\n />\n );\n }\n);\n\nVoiceVisualizer.displayName = \"VoiceVisualizer\";\n"],"names":[],"version":3,"file":"index.module.js.map"}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pipecat-ai/client-react",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"license": "BSD-2-Clause",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.module.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"source": "src/index.ts",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/pipecat-ai/pipecat-client-web.git"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"package.json",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "parcel build --no-cache",
|
|
20
|
+
"dev": "parcel watch",
|
|
21
|
+
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@pipecat-ai/client-js": "*",
|
|
25
|
+
"@types/react": "^18.3.3",
|
|
26
|
+
"@types/react-dom": "^18.3.0",
|
|
27
|
+
"eslint": "^9.11.1",
|
|
28
|
+
"eslint-config-prettier": "^9.1.0",
|
|
29
|
+
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
30
|
+
"parcel": "^2.12.0",
|
|
31
|
+
"react": "^18.3.1",
|
|
32
|
+
"react-dom": "^18.3.1",
|
|
33
|
+
"typescript": "^5.2.2"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"react": ">=18",
|
|
37
|
+
"react-dom": ">=18",
|
|
38
|
+
"@pipecat-ai/client-js": "*"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"jotai": "^2.9.0"
|
|
42
|
+
}
|
|
43
|
+
}
|