@pipecat-ai/client-react 0.3.4 → 0.4.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 CHANGED
@@ -93,17 +93,58 @@ Creates a new `<video>` element that renders either the bot or local participant
93
93
  />
94
94
  ```
95
95
 
96
+ ### RTVIClientCamToggle
97
+
98
+ This is a stateful headless component and exposes the user's camEnabled state and an `onClick` handler to toggle the state.
99
+
100
+ #### Props
101
+
102
+ - `onCamEnabledChanged(enabled: boolean)` (function, optional): Triggered when the user's camEnabled state changes
103
+ - `disabled` (boolean, optional): Disables the cam toggle
104
+
105
+ ```jsx
106
+ <RTVIClientCamToggle>
107
+ {({ disabled, isCamEnabled, onClick }) => (
108
+ <button disabled={disabled} onClick={onClick}>
109
+ {isCamEnabled ? "Turn off" : "Turn on"} camera
110
+ </button>
111
+ )}
112
+ </RTVIClientCamToggle>
113
+ ```
114
+
115
+ ### RTVIClientMicToggle
116
+
117
+ This is a stateful headless component and exposes the user's micEnabled state and an `onClick` handler to toggle the state.
118
+
119
+ #### Props
120
+
121
+ - `onMicEnabledChanged(enabled: boolean)` (function, optional): Triggered when the user's micEnabled state changes
122
+ - `disabled` (boolean, optional): Disables the mic toggle
123
+
124
+ ```jsx
125
+ <RTVIClientMicToggle>
126
+ {({ disabled, isMicEnabled, onClick }) => (
127
+ <button disabled={disabled} onClick={onClick}>
128
+ {isMicEnabled ? "Mute" : "Unmute"} microphone
129
+ </button>
130
+ )}
131
+ </RTVIClientMicToggle>
132
+ ```
133
+
96
134
  ### VoiceVisualizer
97
135
 
98
136
  Renders a visual representation of audio input levels on a `<canvas>` element.
99
- The visualization consists of five vertical bars.
137
+ The visualization consists of vertical bars.
100
138
 
101
139
  #### Props
102
140
 
103
141
  - `participantType` (string, required): The participant type to visualize audio for.
104
142
  - `backgroundColor` (string, optional): The background color of the canvas. Default: 'transparent'.
105
143
  - `barColor` (string, optional): The color of the audio level bars. Default: 'black'.
144
+ - `barCount` (number, optional): The amount of bars to render. Default: 5
106
145
  - `barGap` (number, optional): The gap between bars in pixels. Default: 12.
146
+ - `barLineCap` ('round' | 'square', optional): The line cap for each bar. Default: 'round'
147
+ - `barOrigin` ('bottom' | 'center' | 'top', optional): The origin from where the bars grow to full height. Default: 'center'
107
148
  - `barWidth` (number, optional): The width of each bar in pixels. Default: 30.
108
149
  - `barMaxHeight` (number, optional): The maximum height at full volume of each bar in pixels. Default: 120.
109
150
 
@@ -157,6 +198,30 @@ function EventListener() {
157
198
  }
158
199
  ```
159
200
 
201
+ ### useRTVIClientCamControl
202
+
203
+ Allows to control the user's camera state.
204
+
205
+ ```jsx
206
+ import { useRTVIClientCamControl } from "@pipecat-ai/client-react";
207
+
208
+ function CustomCamToggle() {
209
+ const { enableCam, isCamEnabled } = useRTVIClientCamControl();
210
+ }
211
+ ```
212
+
213
+ ### useRTVIClientMicControl
214
+
215
+ Allows to control the user's microphone state.
216
+
217
+ ```jsx
218
+ import { useRTVIClientMicControl } from "@pipecat-ai/client-react";
219
+
220
+ function CustomMicToggle() {
221
+ const { enableMic, isMicEnabled } = useRTVIClientMicControl();
222
+ }
223
+ ```
224
+
160
225
  ### useRTVIClientMediaDevices
161
226
 
162
227
  Manage and list available media devices.
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { RTVIClient, RTVIEvent, RTVIEventHandler, Tracks, TransportState } from "@pipecat-ai/client-js";
2
2
  import { Provider } from "jotai/react";
3
- import { JSX } from "react/jsx-runtime";
4
3
  import React from "react";
4
+ import { JSX } from "react/jsx-runtime";
5
5
  interface Props {
6
6
  client: RTVIClient;
7
7
  jotaiStore?: React.ComponentProps<typeof Provider>["store"];
@@ -16,6 +16,68 @@ export const RTVIClientAudio: {
16
16
  (): JSX.Element;
17
17
  displayName: string;
18
18
  };
19
+ /**
20
+ * Hook to control camera state
21
+ */
22
+ export const useRTVIClientCamControl: () => {
23
+ enableCam: (enabled: boolean) => void;
24
+ isCamEnabled: boolean;
25
+ };
26
+ interface RTVIClientCamToggleProps {
27
+ /**
28
+ * Callback fired when camera state changes
29
+ */
30
+ onCamEnabledChanged?: (enabled: boolean) => void;
31
+ /**
32
+ * Optional prop to disable the cam toggle.
33
+ * When disabled, changes are not applied to the client.
34
+ * @default false
35
+ */
36
+ disabled?: boolean;
37
+ /**
38
+ * Render prop that provides state and handlers to the children
39
+ */
40
+ children: (props: {
41
+ disabled?: boolean;
42
+ isCamEnabled: boolean;
43
+ onClick: () => void;
44
+ }) => React.ReactNode;
45
+ }
46
+ /**
47
+ * Headless component for controlling camera state
48
+ */
49
+ export const RTVIClientCamToggle: React.FC<RTVIClientCamToggleProps>;
50
+ /**
51
+ * Hook to control microphone state
52
+ */
53
+ export const useRTVIClientMicControl: () => {
54
+ enableMic: (enabled: boolean) => void;
55
+ isMicEnabled: boolean;
56
+ };
57
+ interface RTVIClientMicToggleProps {
58
+ /**
59
+ * Callback fired when microphone state changes
60
+ */
61
+ onMicEnabledChanged?: (enabled: boolean) => void;
62
+ /**
63
+ * Optional prop to disable the mic toggle.
64
+ * When disabled, changes are not applied to the client.
65
+ * @default false
66
+ */
67
+ disabled?: boolean;
68
+ /**
69
+ * Render prop that provides state and handlers to the children
70
+ */
71
+ children: (props: {
72
+ disabled?: boolean;
73
+ isMicEnabled: boolean;
74
+ onClick: () => void;
75
+ }) => React.ReactNode;
76
+ }
77
+ /**
78
+ * Headless component for controlling microphone state
79
+ */
80
+ export const RTVIClientMicToggle: React.FC<RTVIClientMicToggleProps>;
19
81
  interface RTVIClientVideoInterface {
20
82
  aspectRatio: number;
21
83
  height: number;
@@ -59,9 +121,12 @@ type _ParticipantType1 = Parameters<typeof useRTVIClientMediaTrack>[1];
59
121
  interface _Props2 {
60
122
  backgroundColor?: string;
61
123
  barColor?: string;
124
+ barCount?: number;
62
125
  barGap?: number;
63
- barWidth?: number;
126
+ barLineCap?: "round" | "square";
64
127
  barMaxHeight?: number;
128
+ barOrigin?: "top" | "bottom" | "center";
129
+ barWidth?: number;
65
130
  participantType: _ParticipantType1;
66
131
  }
67
132
  export const VoiceVisualizer: React.FC<_Props2>;
@@ -1 +1 @@
1
- {"mappings":";;;;AAYA;IACE,MAAM,EAAE,UAAU,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,cAAc,CAAC,eAAoB,CAAC,CAAC,OAAO,CAAC,CAAC;CAClE;AAMD,OAAO,MAAM,oBAAoB,MAAM,EAAE,CAAC,MAAM,iBAAiB,CAAC,KAAK,CAAC,CAYvE,CAAC;ACxBF,OAAO,MAAM,2CAGZ,CAAC;ACFF,OAAO,MAAM,qBAAsB,CAAC,SAAS,SAAS,SAC7C,CAAC,WACC,iBAAiB,CAAC,CAAC,SAW7B,CAAC;ACTF,uBAAuB,MAAM,MAAM,CAAC;AACpC,iBAAiB,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC;AA4BvC,OAAO,MAAM,qCACA,SAAS,mBACH,eAAe,4BAoDjC,CAAC;ACtFF,OAAO,MAAM;;;CA6BZ,CAAC;AE9BF;IACE,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,iBACE,SAAQ,IAAI,CAAC,MAAM,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,UAAU,CAAC;IACrE,WAAW,EAAE,OAAO,GAAG,KAAK,CAAC;IAE7B;;OAEG;IACH,SAAS,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC;IAEpC;;OAEG;IACH,GAAG,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;IAC1B;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,QAAQ,CAAC,CAAC,UAAU,EAAE,wBAAwB,GAAG,IAAI,CAAC;CACvD;AAED,OAAO,MAAM,iGAwJZ,CAAC;AC1LF,+BAA+B,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AASvE,OAAO,MAAM;;;;;;;oBA4DJ,MAAM;oBAMN,MAAM;wBAMN,MAAM;CAiBd,CAAC;AC7FF,OAAO,MAAM,iDAMZ,CAAC;ACTF,yBAAuB,UAAU,CAAC,8BAA8B,CAAC,CAAC,CAAC,CAAC,CAAC;AAErE;IACE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,iBAAe,CAAC;CAClC;AAED,OAAO,MAAM,iBAAiB,MAAM,EAAE,CAAC,OAAK,CAyM3C,CAAC","sources":["client-react/src/src/RTVIClientProvider.tsx","client-react/src/src/useRTVIClient.ts","client-react/src/src/useRTVIClientEvent.ts","client-react/src/src/useRTVIClientMediaTrack.ts","client-react/src/src/RTVIClientAudio.tsx","client-react/src/src/useMergedRef.ts","client-react/src/src/RTVIClientVideo.tsx","client-react/src/src/useRTVIClientMediaDevices.ts","client-react/src/src/useRTVIClientTransportState.ts","client-react/src/src/VoiceVisualizer.tsx","client-react/src/src/index.ts","client-react/src/index.ts"],"sourcesContent":[null,null,null,null,null,null,null,null,null,null,null,"/**\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"],"names":[],"version":3,"file":"index.d.ts.map"}
1
+ {"mappings":"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","sources":["client-react/src/src/RTVIClientProvider.tsx","client-react/src/src/useRTVIClient.ts","client-react/src/src/useRTVIClientEvent.ts","client-react/src/src/useRTVIClientMediaTrack.ts","client-react/src/src/RTVIClientAudio.tsx","client-react/src/src/useRTVIClientCamControl.ts","client-react/src/src/RTVIClientCamToggle.tsx","client-react/src/src/useRTVIClientMicControl.ts","client-react/src/src/RTVIClientMicToggle.tsx","client-react/src/src/useMergedRef.ts","client-react/src/src/RTVIClientVideo.tsx","client-react/src/src/useRTVIClientMediaDevices.ts","client-react/src/src/useRTVIClientTransportState.ts","client-react/src/src/VoiceVisualizer.tsx","client-react/src/src/index.ts","client-react/src/index.ts"],"sourcesContent":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,"/**\n * Copyright (c) 2024, Daily.\n *\n * SPDX-License-Identifier: BSD-2-Clause\n */\n\nimport { RTVIClientAudio } from \"./RTVIClientAudio\";\nimport { RTVIClientCamToggle } from \"./RTVIClientCamToggle\";\nimport { RTVIClientMicToggle } from \"./RTVIClientMicToggle\";\nimport { RTVIClientProvider } from \"./RTVIClientProvider\";\nimport { RTVIClientVideo } from \"./RTVIClientVideo\";\nimport { useRTVIClient } from \"./useRTVIClient\";\nimport { useRTVIClientCamControl } from \"./useRTVIClientCamControl\";\nimport { useRTVIClientEvent } from \"./useRTVIClientEvent\";\nimport { useRTVIClientMediaDevices } from \"./useRTVIClientMediaDevices\";\nimport { useRTVIClientMediaTrack } from \"./useRTVIClientMediaTrack\";\nimport { useRTVIClientMicControl } from \"./useRTVIClientMicControl\";\nimport { useRTVIClientTransportState } from \"./useRTVIClientTransportState\";\nimport { VoiceVisualizer } from \"./VoiceVisualizer\";\n\nexport {\n RTVIClientAudio,\n RTVIClientCamToggle,\n RTVIClientMicToggle,\n RTVIClientProvider,\n RTVIClientVideo,\n useRTVIClient,\n useRTVIClientCamControl,\n useRTVIClientEvent,\n useRTVIClientMediaDevices,\n useRTVIClientMediaTrack,\n useRTVIClientMicControl,\n useRTVIClientTransportState,\n VoiceVisualizer,\n};\n"],"names":[],"version":3,"file":"index.d.ts.map"}
package/dist/index.js CHANGED
@@ -15,12 +15,16 @@ function $parcel$interopDefault(a) {
15
15
  }
16
16
 
17
17
  $parcel$export(module.exports, "RTVIClientAudio", () => $e6a9aa6ceb34e585$export$ba1245f7cbf3ae02);
18
+ $parcel$export(module.exports, "RTVIClientCamToggle", () => $e7a27a30474b270e$export$156c26e16246ae3e);
19
+ $parcel$export(module.exports, "RTVIClientMicToggle", () => $8a175691955798a2$export$bf1c2c9ca877628d);
18
20
  $parcel$export(module.exports, "RTVIClientProvider", () => $0befa35d025c789a$export$4a4ae2d5dc96782);
19
21
  $parcel$export(module.exports, "RTVIClientVideo", () => $c1952f9ddec789e5$export$d090a384943608eb);
20
22
  $parcel$export(module.exports, "useRTVIClient", () => $8ee07494475a7fa7$export$31a5f6a22c9b8fba);
23
+ $parcel$export(module.exports, "useRTVIClientCamControl", () => $affd351738e2f16c$export$46099a7ec2fe8ac5);
21
24
  $parcel$export(module.exports, "useRTVIClientEvent", () => $8a6b68ebf0332682$export$33a6ac53b8f02625);
22
25
  $parcel$export(module.exports, "useRTVIClientMediaDevices", () => $01bcacdf0a25e20c$export$652c54907b83a48d);
23
26
  $parcel$export(module.exports, "useRTVIClientMediaTrack", () => $e5be552b22401c66$export$7c03381e0d26a6c3);
27
+ $parcel$export(module.exports, "useRTVIClientMicControl", () => $b5946020fafb75c5$export$805915964ad7bc25);
24
28
  $parcel$export(module.exports, "useRTVIClientTransportState", () => $6b6c1a9f794236bd$export$599fa01283bd4ece);
25
29
  $parcel$export(module.exports, "VoiceVisualizer", () => $a1dfa75b13e6bb9b$export$59bf27bd43679db6);
26
30
  /**
@@ -125,18 +129,18 @@ const $e5be552b22401c66$export$7c03381e0d26a6c3 = (trackType, participantType)=>
125
129
  const oldTrack = get(atom);
126
130
  if (oldTrack?.id === track.id) return;
127
131
  set(atom, track);
128
- }, [
129
- participantType,
130
- track,
131
- trackType
132
- ]));
132
+ }, []));
133
133
  (0, $8a6b68ebf0332682$export$33a6ac53b8f02625)((0, $5Zyvw$pipecataiclientjs.RTVIEvent).TrackStarted, (0, $5Zyvw$react.useCallback)((track, participant)=>{
134
134
  updateTrack(track, track.kind, Boolean(participant?.local));
135
- }, []));
135
+ }, [
136
+ updateTrack
137
+ ]));
136
138
  (0, $8a6b68ebf0332682$export$33a6ac53b8f02625)((0, $5Zyvw$pipecataiclientjs.RTVIEvent).ScreenTrackStarted, (0, $5Zyvw$react.useCallback)((track, participant)=>{
137
139
  const trackType = track.kind === "audio" ? "screenAudio" : "screenVideo";
138
140
  updateTrack(track, trackType, Boolean(participant?.local));
139
- }, []));
141
+ }, [
142
+ updateTrack
143
+ ]));
140
144
  (0, $5Zyvw$react.useEffect)(()=>{
141
145
  if (!client) return;
142
146
  const tracks = client.tracks();
@@ -186,6 +190,108 @@ $e6a9aa6ceb34e585$export$ba1245f7cbf3ae02.displayName = "RTVIClientAudio";
186
190
 
187
191
 
188
192
 
193
+
194
+ const $affd351738e2f16c$export$46099a7ec2fe8ac5 = ()=>{
195
+ const client = (0, $8ee07494475a7fa7$export$31a5f6a22c9b8fba)();
196
+ const [isCamEnabled, setIsCamEnabled] = (0, $5Zyvw$react.useState)(client?.isCamEnabled ?? false);
197
+ // Sync component state with client state initially
198
+ (0, $5Zyvw$react.useEffect)(()=>{
199
+ if (!client) return;
200
+ setIsCamEnabled(client.isCamEnabled);
201
+ }, [
202
+ client
203
+ ]);
204
+ const enableCam = (0, $5Zyvw$react.useCallback)((enabled)=>{
205
+ setIsCamEnabled(enabled);
206
+ client?.enableCam?.(enabled);
207
+ }, [
208
+ client
209
+ ]);
210
+ return {
211
+ enableCam: enableCam,
212
+ isCamEnabled: isCamEnabled
213
+ };
214
+ };
215
+
216
+
217
+ const $e7a27a30474b270e$export$156c26e16246ae3e = ({ onCamEnabledChanged: onCamEnabledChanged, disabled: disabled = false, children: children })=>{
218
+ const { isCamEnabled: isCamEnabled, enableCam: enableCam } = (0, $affd351738e2f16c$export$46099a7ec2fe8ac5)();
219
+ const handleToggleCam = (0, $5Zyvw$react.useCallback)(()=>{
220
+ if (disabled) return;
221
+ const newEnabledState = !isCamEnabled;
222
+ enableCam(newEnabledState);
223
+ onCamEnabledChanged?.(newEnabledState);
224
+ }, [
225
+ disabled,
226
+ enableCam,
227
+ isCamEnabled,
228
+ onCamEnabledChanged
229
+ ]);
230
+ return (0, $5Zyvw$reactjsxruntime.jsx)((0, $5Zyvw$reactjsxruntime.Fragment), {
231
+ children: children({
232
+ isCamEnabled: isCamEnabled,
233
+ onClick: handleToggleCam,
234
+ disabled: disabled
235
+ })
236
+ });
237
+ };
238
+ var $e7a27a30474b270e$export$2e2bcd8739ae039 = $e7a27a30474b270e$export$156c26e16246ae3e;
239
+
240
+
241
+
242
+
243
+
244
+
245
+ const $b5946020fafb75c5$export$805915964ad7bc25 = ()=>{
246
+ const client = (0, $8ee07494475a7fa7$export$31a5f6a22c9b8fba)();
247
+ const [isMicEnabled, setIsMicEnabled] = (0, $5Zyvw$react.useState)(client?.isMicEnabled ?? false);
248
+ // Sync component state with client state initially
249
+ (0, $5Zyvw$react.useEffect)(()=>{
250
+ if (!client) return;
251
+ setIsMicEnabled(client.isMicEnabled);
252
+ }, [
253
+ client
254
+ ]);
255
+ const enableMic = (0, $5Zyvw$react.useCallback)((enabled)=>{
256
+ setIsMicEnabled(enabled);
257
+ client?.enableMic?.(enabled);
258
+ }, [
259
+ client
260
+ ]);
261
+ return {
262
+ enableMic: enableMic,
263
+ isMicEnabled: isMicEnabled
264
+ };
265
+ };
266
+
267
+
268
+ const $8a175691955798a2$export$bf1c2c9ca877628d = ({ onMicEnabledChanged: onMicEnabledChanged, disabled: disabled = false, children: children })=>{
269
+ const { enableMic: enableMic, isMicEnabled: isMicEnabled } = (0, $b5946020fafb75c5$export$805915964ad7bc25)();
270
+ const handleToggleMic = (0, $5Zyvw$react.useCallback)(()=>{
271
+ if (disabled) return;
272
+ const newEnabledState = !isMicEnabled;
273
+ enableMic(newEnabledState);
274
+ onMicEnabledChanged?.(newEnabledState);
275
+ }, [
276
+ disabled,
277
+ enableMic,
278
+ isMicEnabled,
279
+ onMicEnabledChanged
280
+ ]);
281
+ return (0, $5Zyvw$reactjsxruntime.jsx)((0, $5Zyvw$reactjsxruntime.Fragment), {
282
+ children: children({
283
+ isMicEnabled: isMicEnabled,
284
+ onClick: handleToggleMic,
285
+ disabled: disabled
286
+ })
287
+ });
288
+ };
289
+ var $8a175691955798a2$export$2e2bcd8739ae039 = $8a175691955798a2$export$bf1c2c9ca877628d;
290
+
291
+
292
+
293
+
294
+
189
295
  /**
190
296
  * Copyright (c) 2024, Daily.
191
297
  *
@@ -334,6 +440,7 @@ $c1952f9ddec789e5$export$d090a384943608eb.displayName = "RTVIClientVideo";
334
440
 
335
441
 
336
442
 
443
+
337
444
  const $01bcacdf0a25e20c$var$availableMicsAtom = (0, $5Zyvw$jotai.atom)([]);
338
445
  const $01bcacdf0a25e20c$var$availableCamsAtom = (0, $5Zyvw$jotai.atom)([]);
339
446
  const $01bcacdf0a25e20c$var$availableSpeakersAtom = (0, $5Zyvw$jotai.atom)([]);
@@ -348,6 +455,25 @@ const $01bcacdf0a25e20c$export$652c54907b83a48d = ()=>{
348
455
  const selectedCam = (0, $5Zyvw$jotai.useAtomValue)($01bcacdf0a25e20c$var$selectedCamAtom);
349
456
  const selectedMic = (0, $5Zyvw$jotai.useAtomValue)($01bcacdf0a25e20c$var$selectedMicAtom);
350
457
  const selectedSpeaker = (0, $5Zyvw$jotai.useAtomValue)($01bcacdf0a25e20c$var$selectedSpeakerAtom);
458
+ const initDevices = (0, $5Zyvw$jotaiutils.useAtomCallback)((0, $5Zyvw$react.useCallback)(async (_get, set)=>{
459
+ if (!client) return;
460
+ const availableCams = await client.getAllCams();
461
+ const availableMics = await client.getAllMics();
462
+ const availableSpeakers = await client.getAllSpeakers();
463
+ set($01bcacdf0a25e20c$var$availableCamsAtom, availableCams);
464
+ set($01bcacdf0a25e20c$var$availableMicsAtom, availableMics);
465
+ set($01bcacdf0a25e20c$var$availableSpeakersAtom, availableSpeakers);
466
+ set($01bcacdf0a25e20c$var$selectedCamAtom, client.selectedCam);
467
+ set($01bcacdf0a25e20c$var$selectedMicAtom, client.selectedMic);
468
+ set($01bcacdf0a25e20c$var$selectedSpeakerAtom, client.selectedSpeaker);
469
+ }, [
470
+ client
471
+ ]));
472
+ (0, $5Zyvw$react.useEffect)(()=>{
473
+ initDevices();
474
+ }, [
475
+ initDevices
476
+ ]);
351
477
  (0, $8a6b68ebf0332682$export$33a6ac53b8f02625)((0, $5Zyvw$pipecataiclientjs.RTVIEvent).AvailableCamsUpdated, (0, $5Zyvw$jotaiutils.useAtomCallback)((0, $5Zyvw$react.useCallback)((_get, set, cams)=>{
352
478
  set($01bcacdf0a25e20c$var$availableCamsAtom, cams);
353
479
  }, [])));
@@ -396,6 +522,7 @@ const $01bcacdf0a25e20c$export$652c54907b83a48d = ()=>{
396
522
 
397
523
 
398
524
 
525
+
399
526
  /**
400
527
  * Copyright (c) 2024, Daily.
401
528
  *
@@ -414,12 +541,12 @@ const $6b6c1a9f794236bd$export$599fa01283bd4ece = ()=>{
414
541
 
415
542
 
416
543
 
417
- const $a1dfa75b13e6bb9b$export$59bf27bd43679db6 = /*#__PURE__*/ (0, ($parcel$interopDefault($5Zyvw$react))).memo(({ backgroundColor: backgroundColor = "transparent", barColor: barColor = "black", barWidth: barWidth = 30, barGap: barGap = 12, barMaxHeight: barMaxHeight = 120, participantType: participantType })=>{
544
+ const $a1dfa75b13e6bb9b$export$59bf27bd43679db6 = /*#__PURE__*/ (0, ($parcel$interopDefault($5Zyvw$react))).memo(({ backgroundColor: backgroundColor = "transparent", barColor: barColor = "black", barCount: barCount = 5, barGap: barGap = 12, barLineCap: barLineCap = "round", barMaxHeight: barMaxHeight = 120, barOrigin: barOrigin = "center", barWidth: barWidth = 30, participantType: participantType })=>{
418
545
  const canvasRef = (0, $5Zyvw$react.useRef)(null);
419
546
  const track = (0, $e5be552b22401c66$export$7c03381e0d26a6c3)("audio", participantType);
420
547
  (0, $5Zyvw$react.useEffect)(()=>{
421
548
  if (!canvasRef.current) return;
422
- const canvasWidth = 5 * barWidth + 4 * barGap;
549
+ const canvasWidth = barCount * barWidth + (barCount - 1) * barGap;
423
550
  const canvasHeight = barMaxHeight;
424
551
  const canvas = canvasRef.current;
425
552
  const scaleFactor = 2;
@@ -429,7 +556,7 @@ const $a1dfa75b13e6bb9b$export$59bf27bd43679db6 = /*#__PURE__*/ (0, ($parcel$int
429
556
  canvas.height = canvasHeight * scaleFactor;
430
557
  canvas.style.width = `${canvasWidth}px`;
431
558
  canvas.style.height = `${canvasHeight}px`;
432
- canvasCtx.lineCap = "round";
559
+ canvasCtx.lineCap = barLineCap;
433
560
  canvasCtx.scale(scaleFactor, scaleFactor);
434
561
  };
435
562
  const canvasCtx = canvas.getContext("2d");
@@ -443,34 +570,29 @@ const $a1dfa75b13e6bb9b$export$59bf27bd43679db6 = /*#__PURE__*/ (0, ($parcel$int
443
570
  analyser.fftSize = 1024;
444
571
  source.connect(analyser);
445
572
  const frequencyData = new Uint8Array(analyser.frequencyBinCount);
446
- canvasCtx.lineCap = "round";
447
- const bands = [
448
- {
449
- startFreq: 85,
450
- endFreq: 255,
451
- smoothValue: 0
452
- },
453
- {
454
- startFreq: 255,
455
- endFreq: 500,
456
- smoothValue: 0
457
- },
458
- {
459
- startFreq: 500,
460
- endFreq: 2000,
573
+ canvasCtx.lineCap = barLineCap;
574
+ // Create frequency bands based on barCount
575
+ const bands = Array.from({
576
+ length: barCount
577
+ }, (_, i)=>{
578
+ // Use improved logarithmic scale for better frequency distribution
579
+ const minFreq = barCount > 20 ? 200 : 80; // Adjust min frequency based on bar count
580
+ const maxFreq = 10000; // Cover most important audio frequencies
581
+ // Use Mel scale inspired approach for more perceptually uniform distribution
582
+ // This helps with a large number of bars by placing fewer in the very low range
583
+ // https://en.wikipedia.org/wiki/Mel_scale
584
+ const melMin = 2595 * Math.log10(1 + minFreq / 700);
585
+ const melMax = 2595 * Math.log10(1 + maxFreq / 700);
586
+ const melStep = (melMax - melMin) / barCount;
587
+ const melValue = melMin + i * melStep;
588
+ const startFreq = 700 * (Math.pow(10, melValue / 2595) - 1);
589
+ const endFreq = 700 * (Math.pow(10, (melValue + melStep) / 2595) - 1);
590
+ return {
591
+ startFreq: startFreq,
592
+ endFreq: endFreq,
461
593
  smoothValue: 0
462
- },
463
- {
464
- startFreq: 2000,
465
- endFreq: 4000,
466
- smoothValue: 0
467
- },
468
- {
469
- startFreq: 4000,
470
- endFreq: 8000,
471
- smoothValue: 0
472
- }
473
- ];
594
+ };
595
+ });
474
596
  const getFrequencyBinIndex = (frequency)=>{
475
597
  const nyquist = audioContext.sampleRate / 2;
476
598
  return Math.round(frequency / nyquist * (analyser.frequencyBinCount - 1));
@@ -497,9 +619,25 @@ const $a1dfa75b13e6bb9b$export$59bf27bd43679db6 = /*#__PURE__*/ (0, ($parcel$int
497
619
  }
498
620
  const x = startX + i * (barWidth + barGap);
499
621
  // Calculate bar height with a maximum cap
500
- const barHeight = Math.min(band.smoothValue / 255 * barMaxHeight, barMaxHeight);
501
- const yTop = Math.max(canvas.height / scaleFactor / 2 - barHeight / 2, adjustedCircleRadius);
502
- const yBottom = Math.min(canvas.height / scaleFactor / 2 + barHeight / 2, canvas.height / scaleFactor - adjustedCircleRadius);
622
+ const minHeight = 0;
623
+ const barHeight = Math.max(minHeight, Math.min(band.smoothValue / 255 * barMaxHeight, barMaxHeight));
624
+ let yTop, yBottom;
625
+ const canvasHeight = canvas.height / scaleFactor;
626
+ switch(barOrigin){
627
+ case "top":
628
+ yTop = adjustedCircleRadius;
629
+ yBottom = Math.min(adjustedCircleRadius + barHeight, canvasHeight - adjustedCircleRadius);
630
+ break;
631
+ case "bottom":
632
+ yBottom = canvasHeight - adjustedCircleRadius;
633
+ yTop = Math.max(yBottom - barHeight, adjustedCircleRadius);
634
+ break;
635
+ case "center":
636
+ default:
637
+ yTop = Math.max(canvasHeight / 2 - barHeight / 2, adjustedCircleRadius);
638
+ yBottom = Math.min(canvasHeight / 2 + barHeight / 2, canvasHeight - adjustedCircleRadius);
639
+ break;
640
+ }
503
641
  if (band.smoothValue > 0) {
504
642
  canvasCtx.beginPath();
505
643
  canvasCtx.moveTo(x + barWidth / 2, yTop);
@@ -507,28 +645,47 @@ const $a1dfa75b13e6bb9b$export$59bf27bd43679db6 = /*#__PURE__*/ (0, ($parcel$int
507
645
  canvasCtx.lineWidth = barWidth;
508
646
  canvasCtx.strokeStyle = barColor;
509
647
  canvasCtx.stroke();
510
- } else {
511
- canvasCtx.beginPath();
512
- canvasCtx.arc(x + barWidth / 2, canvas.height / scaleFactor / 2, adjustedCircleRadius, 0, 2 * Math.PI);
513
- canvasCtx.fillStyle = barColor;
514
- canvasCtx.fill();
515
- canvasCtx.closePath();
516
- }
648
+ } else drawInactiveCircle(adjustedCircleRadius, barColor, x, yTop);
517
649
  });
518
650
  if (!isActive) drawInactiveCircles(adjustedCircleRadius, barColor);
519
651
  requestAnimationFrame(drawSpectrum);
520
652
  }
653
+ function drawInactiveCircle(circleRadius, color, x, y) {
654
+ switch(barLineCap){
655
+ case "square":
656
+ canvasCtx.fillStyle = color;
657
+ canvasCtx.fillRect(x + barWidth / 2 - circleRadius, y - circleRadius, circleRadius * 2, circleRadius * 2);
658
+ break;
659
+ case "round":
660
+ default:
661
+ canvasCtx.beginPath();
662
+ canvasCtx.arc(x + barWidth / 2, y, circleRadius, 0, 2 * Math.PI);
663
+ canvasCtx.fillStyle = color;
664
+ canvasCtx.fill();
665
+ canvasCtx.closePath();
666
+ break;
667
+ }
668
+ }
521
669
  function drawInactiveCircles(circleRadius, color) {
522
670
  const totalBarsWidth = bands.length * barWidth + (bands.length - 1) * barGap;
523
671
  const startX = (canvas.width / scaleFactor - totalBarsWidth) / 2;
524
- const y = canvas.height / scaleFactor / 2;
672
+ const canvasHeight = canvas.height / scaleFactor;
673
+ let y;
674
+ switch(barOrigin){
675
+ case "top":
676
+ y = circleRadius;
677
+ break;
678
+ case "bottom":
679
+ y = canvasHeight - circleRadius;
680
+ break;
681
+ case "center":
682
+ default:
683
+ y = canvasHeight / 2;
684
+ break;
685
+ }
525
686
  bands.forEach((_, i)=>{
526
687
  const x = startX + i * (barWidth + barGap);
527
- canvasCtx.beginPath();
528
- canvasCtx.arc(x + barWidth / 2, y, circleRadius, 0, 2 * Math.PI);
529
- canvasCtx.fillStyle = color;
530
- canvasCtx.fill();
531
- canvasCtx.closePath();
688
+ drawInactiveCircle(circleRadius, color, x, y);
532
689
  });
533
690
  }
534
691
  drawSpectrum();
@@ -541,8 +698,11 @@ const $a1dfa75b13e6bb9b$export$59bf27bd43679db6 = /*#__PURE__*/ (0, ($parcel$int
541
698
  }, [
542
699
  backgroundColor,
543
700
  barColor,
701
+ barCount,
544
702
  barGap,
703
+ barLineCap,
545
704
  barMaxHeight,
705
+ barOrigin,
546
706
  barWidth,
547
707
  track
548
708
  ]);