@tavus/cvi-ui 0.0.1-beta.2 → 0.0.1-beta.4
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 +42 -75
- package/dev-components/components/README.md +145 -59
- package/dev-components/components/audio-wave/audio-wave.module.css +33 -0
- package/dev-components/components/audio-wave/index.tsx +55 -0
- package/dev-components/components/conversation-01/conversation.module.css +228 -0
- package/dev-components/components/conversation-01/index.tsx +173 -0
- package/dev-components/components/device-select/device-select.module.css +115 -0
- package/dev-components/components/{controls → device-select}/index.tsx +7 -7
- package/dev-components/components/{hair-check → hair-check-01}/hair-check.module.css +3 -2
- package/dev-components/components/{hair-check → hair-check-01}/index.tsx +4 -4
- package/dev-components/components/media-controls/index.tsx +207 -0
- package/dev-components/components/media-controls/media-controls.module.css +49 -0
- package/dev-components/hooks/cvi-events-hooks.tsx +6 -6
- package/dev-components/hooks/{use-cvi-call.ts → use-cvi-call.tsx} +7 -0
- package/dist/index.js +457 -110
- package/dist/types/templates/components.d.ts +37 -4
- package/dist/types/templates/jsx/index.d.ts +13 -2
- package/dist/types/templates/tsx/index.d.ts +13 -2
- package/dist/types/utils/version-utils.d.ts +3 -0
- package/package.json +9 -5
- package/prepare-scripts/create-templates.js +1 -1
- package/src/templates/components.ts +19 -0
- package/src/templates/index.ts +2 -0
- package/src/templates/jsx/components/audio-wave.json +5 -0
- package/src/templates/jsx/components/conversation-01.json +13 -0
- package/src/templates/jsx/components/cvi-provider.json +5 -0
- package/src/templates/jsx/components/device-select.json +10 -0
- package/src/templates/jsx/components/hair-check-01.json +11 -0
- package/src/templates/jsx/components/media-controls.json +10 -0
- package/src/templates/jsx/hooks/cvi-events-hooks.json +5 -0
- package/src/templates/jsx/hooks/use-cvi-call.json +5 -0
- package/src/templates/jsx/hooks/use-local-camera.json +5 -0
- package/src/templates/jsx/hooks/use-local-microphone.json +5 -0
- package/src/templates/jsx/hooks/use-local-screenshare.json +5 -0
- package/src/templates/jsx/hooks/use-remote-participant-ids.json +5 -0
- package/src/templates/jsx/hooks/use-replica-ids.json +5 -0
- package/src/templates/jsx/hooks/use-request-permissions.json +5 -0
- package/src/templates/jsx/hooks/use-start-haircheck.json +5 -0
- package/src/templates/jsx/index.ts +15 -0
- package/src/templates/tsx/components/audio-wave.json +5 -0
- package/src/templates/tsx/components/conversation-01.json +13 -0
- package/src/templates/tsx/components/cvi-provider.json +5 -0
- package/src/templates/tsx/components/device-select.json +10 -0
- package/src/templates/tsx/components/hair-check-01.json +11 -0
- package/src/templates/tsx/components/media-controls.json +10 -0
- package/src/templates/tsx/hooks/cvi-events-hooks.json +5 -0
- package/src/templates/tsx/hooks/use-cvi-call.json +5 -0
- package/src/templates/tsx/hooks/use-local-camera.json +5 -0
- package/src/templates/tsx/hooks/use-local-microphone.json +5 -0
- package/src/templates/tsx/hooks/use-local-screenshare.json +5 -0
- package/src/templates/tsx/hooks/use-remote-participant-ids.json +5 -0
- package/src/templates/tsx/hooks/use-replica-ids.json +5 -0
- package/src/templates/tsx/hooks/use-request-permissions.json +8 -0
- package/src/templates/tsx/hooks/use-start-haircheck.json +5 -0
- package/src/templates/tsx/index.ts +15 -0
- package/src/utils/resolve-components-tree.ts +59 -2
- package/src/utils/update-files.ts +38 -10
- package/src/utils/version-utils.ts +26 -0
- package/dev-components/components/controls/controls.module.css +0 -113
- package/dist/templates/components/controls.tsx +0 -279
- package/dist/templates/components/cvi-provider.tsx +0 -9
- package/dist/templates/controls.tsx +0 -279
- package/dist/templates/cvi-hooks.tsx +0 -38
- package/dist/templates/cvi-provider.tsx +0 -9
- package/dist/templates/hooks/cvi-hooks.tsx +0 -38
- package/dist/templates/tsx/components/controls.tsx +0 -279
- package/dist/templates/tsx/components/cvi-provider.tsx +0 -9
- package/dist/types/constants/components.d.ts +0 -59
- package/dist/types/utils/resolve-import.d.ts +0 -2
- package/dist/typescript-XxXP1Woc.js +0 -14
- /package/dev-components/hooks/{use-local-camera.ts → use-local-camera.tsx} +0 -0
- /package/dev-components/hooks/{use-local-microphone.ts → use-local-microphone.tsx} +0 -0
- /package/dev-components/hooks/{use-local-screenshare.ts → use-local-screenshare.tsx} +0 -0
- /package/dev-components/hooks/{use-remote-participant-ids.ts → use-remote-participant-ids.tsx} +0 -0
- /package/dev-components/hooks/{use-replica-ids.ts → use-replica-ids.tsx} +0 -0
- /package/dev-components/hooks/{use-request-permissions.ts → use-request-permissions.tsx} +0 -0
- /package/dev-components/hooks/{use-start-haircheck.ts → use-start-haircheck.tsx} +0 -0
package/README.md
CHANGED
|
@@ -2,102 +2,69 @@
|
|
|
2
2
|
|
|
3
3
|
A CLI tool for installing and managing CVI (Conversational Video Interface) components for React applications. This library provides pre-built components for creating real-time multimodal video conversations with AI replicas.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Quick Start
|
|
6
6
|
|
|
7
7
|
**Initialize the project**:
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
```bash
|
|
10
|
+
npx @tavus/cvi-ui@latest init
|
|
11
|
+
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
This will:
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
- Create a `cvi-components.json` configuration file
|
|
16
|
+
- Prompt for TypeScript preference
|
|
17
|
+
- Install the necessary dependencies
|
|
18
18
|
|
|
19
19
|
**Add components to your project**:
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
```bash
|
|
22
|
+
npx @tavus/cvi-ui@latest add conversation
|
|
23
|
+
```
|
|
24
24
|
|
|
25
25
|
**Wrap your app with the CVI provider**:
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
```tsx
|
|
28
|
+
import { CVIProvider } from './components/cvi/components/cvi-provider';
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
function App() {
|
|
31
|
+
return (
|
|
32
|
+
<CVIProvider>
|
|
33
|
+
{/* Your app content */}
|
|
34
|
+
</CVIProvider>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
38
|
|
|
39
39
|
**Add conversation components**:
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
```tsx
|
|
42
|
+
import { Conversation } from './components/cvi/components/conversation';
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
function CVI() {
|
|
45
|
+
return (
|
|
46
|
+
<div
|
|
47
|
+
style={{
|
|
48
|
+
width: '100%',
|
|
49
|
+
height: '100%',
|
|
50
|
+
maxWidth: '1200px',
|
|
51
|
+
margin: '0 auto',
|
|
52
|
+
}}
|
|
53
|
+
>
|
|
54
|
+
<Conversation conversationUrl='YOUR_TAVUS_MEETING_URL' onLeave={() => {}} />
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
```
|
|
52
59
|
|
|
53
|
-
##
|
|
60
|
+
## Documentation
|
|
54
61
|
|
|
55
|
-
|
|
62
|
+
- [CVI Documentation](https://docs.tavus.io/sections/conversational-video-interface/component-library/overview)
|
|
56
63
|
|
|
57
|
-
|
|
64
|
+
## Examples
|
|
58
65
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
- **`controls`** - Video chat control buttons with device selection capabilities
|
|
62
|
-
- `MicrophoneButton` - Toggle microphone with device dropdown
|
|
63
|
-
- `CameraButton` - Toggle camera with device dropdown
|
|
64
|
-
- `ScreenShareButton` - Toggle screen sharing
|
|
65
|
-
|
|
66
|
-
- **`hair-check`** - Pre-call interface for testing and configuring audio/video devices
|
|
67
|
-
- Live camera preview with mirror effect
|
|
68
|
-
- Permission management and device controls
|
|
69
|
-
- Join interface with loading states
|
|
70
|
-
|
|
71
|
-
### Hooks
|
|
72
|
-
|
|
73
|
-
#### Core Call Management
|
|
74
|
-
|
|
75
|
-
- **`use-cvi-call`** – Provides basic join and leave functionality for CVI calls.
|
|
76
|
-
- **`use-start-haircheck`** – Manages device-permission checks and initializes the camera to start a haircheck session.
|
|
77
|
-
|
|
78
|
-
#### Media Controls
|
|
79
|
-
|
|
80
|
-
- **`use-local-camera`** – Manages local camera state and toggle functionality.
|
|
81
|
-
- **`use-local-microphone`** – Manages local microphone state and toggle functionality.
|
|
82
|
-
- **`use-local-screenshare`** – Manages screen-share state and toggle functionality.
|
|
83
|
-
- **`use-request-permissions`** – Requests permissions and handles audio processing setup.
|
|
84
|
-
|
|
85
|
-
#### Participant Management
|
|
86
|
-
|
|
87
|
-
- **`use-replica-ids`** – Retrieves Tavus–replica participant IDs.
|
|
88
|
-
- **`use-remote-participant-ids`** – Retrieves remote participant IDs.
|
|
89
|
-
|
|
90
|
-
#### Conversation & Events
|
|
91
|
-
|
|
92
|
-
- **`use-observable-event`** – Listens for CVI events.
|
|
93
|
-
- **`use-send-app-message`** – Sends CVI app messages.
|
|
66
|
+
- [Tavus Examples](https://github.com/Tavus-Engineering/tavus-examples)
|
|
94
67
|
|
|
95
68
|
## License
|
|
96
69
|
|
|
97
70
|
MIT License - see the [LICENSE](LICENSE) file for details.
|
|
98
|
-
|
|
99
|
-
## Related Links
|
|
100
|
-
|
|
101
|
-
- [CVI Documentation](https://docs.tavus.io/sections/conversational-video-interface/cvi-overview)
|
|
102
|
-
- [Tavus Examples](https://github.com/Tavus-Engineering/tavus-examples)
|
|
103
|
-
|
|
@@ -2,100 +2,186 @@
|
|
|
2
2
|
|
|
3
3
|
## Components
|
|
4
4
|
|
|
5
|
-
###
|
|
5
|
+
### Hair Check 01
|
|
6
6
|
|
|
7
|
-
The `
|
|
7
|
+
The `HairCheck` component provides a pre-call interface for users to test and configure their audio/video devices before joining a video chat.
|
|
8
|
+
|
|
9
|
+
#### Features:
|
|
10
|
+
|
|
11
|
+
- **Device Testing**: Live preview of camera feed with mirror effect
|
|
12
|
+
- **Permission Management**: Handles camera and microphone permission requests
|
|
13
|
+
- **Device Controls**: Integrated microphone and camera controls
|
|
14
|
+
- **Join Interface**: Call-to-action button to join the video chat
|
|
15
|
+
- **Responsive Design**: Works on both desktop and mobile devices
|
|
16
|
+
|
|
17
|
+
#### Usage:
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { HairCheck } from './hair-check';
|
|
21
|
+
|
|
22
|
+
<HairCheck
|
|
23
|
+
isJoinBtnLoading={false}
|
|
24
|
+
onJoin={() => handleJoinCall()}
|
|
25
|
+
onCancel={() => handleCancel()}
|
|
26
|
+
/>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
#### Props:
|
|
30
|
+
|
|
31
|
+
- `isJoinBtnLoading` (boolean): Shows loading state on join button
|
|
32
|
+
- `onJoin` (function): Callback when user clicks join
|
|
33
|
+
- `onCancel` (function, optional): Callback when user cancels
|
|
34
|
+
|
|
35
|
+
#### Key Features:
|
|
36
|
+
|
|
37
|
+
- Automatic permission request flow
|
|
38
|
+
- Visual feedback for different permission states
|
|
39
|
+
- Fallback user avatar when camera is unavailable
|
|
40
|
+
- Integrated device controls for easy testing
|
|
41
|
+
- Clean, intuitive interface for pre-call setup
|
|
42
|
+
|
|
43
|
+
### Conversation 01
|
|
44
|
+
|
|
45
|
+
The `Conversation` component provides a complete video chat interface for one-to-one conversations with AI replicas, featuring main video display, self-view preview, and integrated controls.
|
|
46
|
+
|
|
47
|
+
#### Features:
|
|
48
|
+
|
|
49
|
+
- **Main Video Display**: Large video area showing the AI replica or screen share
|
|
50
|
+
- **Self-View Preview**: Small preview window showing local camera feed
|
|
51
|
+
- **Screen Sharing Support**: Automatic switching between replica video and screen share
|
|
52
|
+
- **Device Controls**: Integrated microphone, camera, and screen share controls
|
|
53
|
+
- **Error Handling**: Graceful handling of camera/microphone permission errors
|
|
54
|
+
- **Responsive Layout**: Adaptive design for different screen sizes
|
|
55
|
+
|
|
56
|
+
#### Usage:
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
import { Conversation } from './conversation-01';
|
|
60
|
+
|
|
61
|
+
<Conversation
|
|
62
|
+
conversationUrl={conversationUrl}
|
|
63
|
+
onLeave={() => handleLeaveCall()}
|
|
64
|
+
/>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
#### Props:
|
|
68
|
+
|
|
69
|
+
- `conversationUrl` (string): Daily.co room URL for joining
|
|
70
|
+
- `onLeave` (function): Callback when user leaves the conversation
|
|
71
|
+
|
|
72
|
+
### Audio Wave
|
|
73
|
+
|
|
74
|
+
The `AudioWave` component provides real-time audio level visualization for video chat participants, displaying animated bars that respond to audio input levels.
|
|
75
|
+
|
|
76
|
+
#### Features:
|
|
77
|
+
|
|
78
|
+
- **Real-time Audio Visualization**: Three animated bars that respond to audio levels
|
|
79
|
+
- **Active Speaker Detection**: Visual distinction between active and inactive speakers
|
|
80
|
+
- **Performance Optimized**: Uses `requestAnimationFrame` for smooth animations
|
|
81
|
+
- **Responsive Design**: Compact circular design that fits well in video previews
|
|
82
|
+
- **Audio Level Scaling**: Intelligent volume scaling for consistent visual feedback
|
|
83
|
+
|
|
84
|
+
#### Usage:
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
import { AudioWave } from './audio-wave';
|
|
88
|
+
|
|
89
|
+
<AudioWave id={participantId} />
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
#### Props:
|
|
93
|
+
|
|
94
|
+
- `id` (string): The participant's session ID to monitor audio levels for
|
|
95
|
+
|
|
96
|
+
### Media Controls
|
|
97
|
+
|
|
98
|
+
The `media-controls` module provides simple toggle buttons for microphone, camera, and screen sharing, designed for direct use in video chat interfaces.
|
|
8
99
|
|
|
9
100
|
#### Exported Components:
|
|
10
101
|
|
|
11
|
-
- **`
|
|
12
|
-
- **`
|
|
13
|
-
- **`ScreenShareButton`**:
|
|
102
|
+
- **`MicToggleButton`**: Toggles microphone mute/unmute state
|
|
103
|
+
- **`CameraToggleButton`**: Toggles camera on/off
|
|
104
|
+
- **`ScreenShareButton`**: Toggles screen sharing on/off
|
|
14
105
|
|
|
15
106
|
#### Usage:
|
|
16
107
|
|
|
17
108
|
```tsx
|
|
18
|
-
import {
|
|
109
|
+
import { MicToggleButton, CameraToggleButton, ScreenShareButton } from './media-controls';
|
|
19
110
|
|
|
20
|
-
|
|
21
|
-
<
|
|
22
|
-
<CameraButton />
|
|
111
|
+
<MicToggleButton />
|
|
112
|
+
<CameraToggleButton />
|
|
23
113
|
<ScreenShareButton />
|
|
24
114
|
```
|
|
25
115
|
|
|
26
|
-
####
|
|
116
|
+
#### Key Features:
|
|
27
117
|
|
|
28
|
-
|
|
118
|
+
- Simple, accessible toggle buttons
|
|
119
|
+
- Visual state indicators (muted, unmuted, on/off)
|
|
120
|
+
- Disabled state when device is not ready
|
|
121
|
+
- Uses Daily.co hooks for device state
|
|
122
|
+
- CSS modules for styling
|
|
29
123
|
|
|
30
|
-
|
|
31
|
-
- Shows visual indicators for muted/unmuted states
|
|
32
|
-
- Includes device selection dropdown
|
|
33
|
-
- Disabled when microphone is not ready
|
|
34
|
-
- Uses `useLocalMicrophone` and `useDevices` hooks
|
|
124
|
+
---
|
|
35
125
|
|
|
36
|
-
|
|
126
|
+
### Device Select
|
|
37
127
|
|
|
38
|
-
-
|
|
39
|
-
- Shows visual indicators for camera muted/unmuted states
|
|
40
|
-
- Includes device selection dropdown
|
|
41
|
-
- Disabled when camera is not ready or no camera is available
|
|
42
|
-
- Uses `useLocalCamera` and `useDevices` hooks
|
|
128
|
+
The `device-select` module provides advanced device selection controls, including dropdowns for choosing microphones and cameras, and integrated toggle buttons.
|
|
43
129
|
|
|
44
|
-
|
|
130
|
+
#### Exported Components:
|
|
45
131
|
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
132
|
+
- **`SelectDevice`**: Dropdown for selecting a device from a list
|
|
133
|
+
- **`MicSelectBtn`**: Microphone toggle button with device selection
|
|
134
|
+
- **`CameraSelectBtn`**: Camera toggle button with device selection
|
|
135
|
+
- **`ScreenShareButton`**: Button to toggle screen sharing
|
|
49
136
|
|
|
50
|
-
|
|
137
|
+
#### Usage:
|
|
51
138
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
- Includes custom dropdown arrow styling
|
|
139
|
+
```tsx
|
|
140
|
+
import { SelectDevice, MicSelectBtn, CameraSelectBtn, ScreenShareButton } from './device-select';
|
|
55
141
|
|
|
56
|
-
|
|
142
|
+
<SelectDevice value={currentDeviceId} devices={devices} disabled={false} onChange={setDevice} />
|
|
143
|
+
<MicSelectBtn />
|
|
144
|
+
<CameraSelectBtn />
|
|
145
|
+
<ScreenShareButton />
|
|
146
|
+
```
|
|
57
147
|
|
|
58
|
-
|
|
59
|
-
- Visual state indicators with SVG icons
|
|
60
|
-
- Accessibility support with screen reader labels (`srOnly` class)
|
|
61
|
-
- Responsive design with mobile-friendly controls
|
|
62
|
-
- Integration with Daily.co device management system
|
|
63
|
-
- CSS modules for scoped styling
|
|
148
|
+
#### Props (SelectDevice):
|
|
64
149
|
|
|
65
|
-
|
|
150
|
+
- `value` (string): Currently selected device ID
|
|
151
|
+
- `devices` (array): List of device objects `{ device: MediaDeviceInfo }[]`
|
|
152
|
+
- `disabled` (boolean): Whether the dropdown is disabled
|
|
153
|
+
- `onChange` (function): Callback when device is changed
|
|
66
154
|
|
|
67
|
-
|
|
155
|
+
#### Key Features:
|
|
68
156
|
|
|
69
|
-
|
|
157
|
+
- Integrated device selection and toggling
|
|
158
|
+
- Dropdowns for camera/microphone selection
|
|
159
|
+
- Visual state indicators and accessibility support
|
|
160
|
+
- Uses Daily.co device management hooks
|
|
161
|
+
- CSS modules for styling
|
|
70
162
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
### CVI Provider
|
|
166
|
+
|
|
167
|
+
The `CVIProvider` component wraps your app with the Daily.co provider context, enabling all Daily React hooks and components to function.
|
|
76
168
|
|
|
77
169
|
#### Usage:
|
|
78
170
|
|
|
79
171
|
```tsx
|
|
80
|
-
import {
|
|
172
|
+
import { CVIProvider } from './cvi-provider';
|
|
81
173
|
|
|
82
|
-
<
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
onCancel={() => handleCancel()}
|
|
86
|
-
/>
|
|
174
|
+
<CVIProvider>
|
|
175
|
+
{/* your app components */}
|
|
176
|
+
</CVIProvider>
|
|
87
177
|
```
|
|
88
178
|
|
|
89
179
|
#### Props:
|
|
90
180
|
|
|
91
|
-
- `
|
|
92
|
-
- `onJoin` (function): Callback when user clicks join
|
|
93
|
-
- `onCancel` (function, optional): Callback when user cancels
|
|
181
|
+
- `children` (ReactNode): Components to be wrapped by the provider
|
|
94
182
|
|
|
95
183
|
#### Key Features:
|
|
96
184
|
|
|
97
|
-
-
|
|
98
|
-
-
|
|
99
|
-
-
|
|
100
|
-
- Integrated device controls for easy testing
|
|
101
|
-
- Clean, intuitive interface for pre-call setup
|
|
185
|
+
- Provides Daily.co context to all child components
|
|
186
|
+
- Required for using Daily React hooks and video/audio components
|
|
187
|
+
- Simple wrapper for app-level integration
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
.container {
|
|
2
|
+
overflow: hidden;
|
|
3
|
+
border: 1px solid white;
|
|
4
|
+
width: 1.5rem;
|
|
5
|
+
height: 1.5rem;
|
|
6
|
+
border-radius: 9999px;
|
|
7
|
+
will-change: transform;
|
|
8
|
+
transform: translateZ(0);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.waveContainer {
|
|
12
|
+
display: flex;
|
|
13
|
+
justify-content: center;
|
|
14
|
+
align-items: center;
|
|
15
|
+
gap: 0.125rem;
|
|
16
|
+
width: 100%;
|
|
17
|
+
height: 100%;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.bar {
|
|
21
|
+
width: 0.25rem;
|
|
22
|
+
height: 0.25rem;
|
|
23
|
+
background-color: white;
|
|
24
|
+
border-radius: 9999px;
|
|
25
|
+
transition: height 200ms ease-out;
|
|
26
|
+
will-change: height;
|
|
27
|
+
transform: translateZ(0);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.barInactive {
|
|
31
|
+
width: 0.25rem !important;
|
|
32
|
+
height: 0.25rem !important;
|
|
33
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React, { useCallback, useRef, memo } from "react";
|
|
2
|
+
import { useActiveSpeakerId } from "@daily-co/daily-react";
|
|
3
|
+
import { useAudioLevelObserver } from "@daily-co/daily-react";
|
|
4
|
+
import styles from "./audio-wave.module.css";
|
|
5
|
+
|
|
6
|
+
export const AudioWave = memo(({ id }: { id: string }) => {
|
|
7
|
+
const activeSpeakerId = useActiveSpeakerId();
|
|
8
|
+
const isActiveSpeaker = activeSpeakerId === id;
|
|
9
|
+
|
|
10
|
+
const leftBarRef = useRef<HTMLDivElement>(null);
|
|
11
|
+
const centerBarRef = useRef<HTMLDivElement>(null);
|
|
12
|
+
const rightBarRef = useRef<HTMLDivElement>(null);
|
|
13
|
+
const animationFrameRef = useRef<number | undefined>(undefined);
|
|
14
|
+
|
|
15
|
+
useAudioLevelObserver(
|
|
16
|
+
id,
|
|
17
|
+
useCallback((volume) => {
|
|
18
|
+
// Cancel any pending animation frame to prevent accumulation
|
|
19
|
+
if (animationFrameRef.current) {
|
|
20
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Use requestAnimationFrame to batch DOM updates
|
|
24
|
+
animationFrameRef.current = requestAnimationFrame(() => {
|
|
25
|
+
const scaledVolume = Number(Math.max(0.01, volume).toFixed(2));
|
|
26
|
+
if (leftBarRef.current && centerBarRef.current && rightBarRef.current) {
|
|
27
|
+
leftBarRef.current.style.height = `${20 + scaledVolume * 40}%`;
|
|
28
|
+
centerBarRef.current.style.height = `${20 + scaledVolume * 130}%`;
|
|
29
|
+
rightBarRef.current.style.height = `${20 + scaledVolume * 40}%`;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}, []),
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div className={styles.container}>
|
|
37
|
+
<div className={styles.waveContainer}>
|
|
38
|
+
<div
|
|
39
|
+
ref={leftBarRef}
|
|
40
|
+
className={`${styles.bar} ${!isActiveSpeaker ? styles.barInactive : ''}`}
|
|
41
|
+
/>
|
|
42
|
+
<div
|
|
43
|
+
ref={centerBarRef}
|
|
44
|
+
className={`${styles.bar} ${!isActiveSpeaker ? styles.barInactive : ''}`}
|
|
45
|
+
/>
|
|
46
|
+
<div
|
|
47
|
+
ref={rightBarRef}
|
|
48
|
+
className={`${styles.bar} ${!isActiveSpeaker ? styles.barInactive : ''}`}
|
|
49
|
+
/>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
AudioWave.displayName = 'AudioWave';
|