@tavus/cvi-ui 0.0.1-beta.2 → 0.0.1-beta.3

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.
Files changed (59) hide show
  1. package/README.md +44 -33
  2. package/dev-components/components/README.md +62 -1
  3. package/dev-components/components/audio-wave/audio-wave.module.css +33 -0
  4. package/dev-components/components/audio-wave/index.tsx +55 -0
  5. package/dev-components/components/controls/controls.module.css +75 -73
  6. package/dev-components/components/controls/index.tsx +2 -2
  7. package/dev-components/components/conversation-01/conversation.module.css +222 -0
  8. package/dev-components/components/conversation-01/index.tsx +180 -0
  9. package/dev-components/components/{hair-check → hair-check-01}/hair-check.module.css +2 -1
  10. package/dev-components/components/{hair-check → hair-check-01}/index.tsx +1 -1
  11. package/dist/index.js +412 -104
  12. package/dist/types/templates/components.d.ts +32 -2
  13. package/dist/types/templates/jsx/index.d.ts +11 -1
  14. package/dist/types/templates/tsx/index.d.ts +11 -1
  15. package/dist/types/utils/version-utils.d.ts +3 -0
  16. package/package.json +6 -4
  17. package/src/templates/components.ts +18 -0
  18. package/src/templates/index.ts +2 -0
  19. package/src/templates/jsx/components/audio-wave.json +5 -0
  20. package/src/templates/jsx/components/controls.json +10 -0
  21. package/src/templates/jsx/components/conversation-01.json +11 -0
  22. package/src/templates/jsx/components/cvi-provider.json +5 -0
  23. package/src/templates/jsx/components/hair-check-01.json +10 -0
  24. package/src/templates/jsx/hooks/cvi-events-hooks.json +5 -0
  25. package/src/templates/jsx/hooks/use-cvi-call.json +5 -0
  26. package/src/templates/jsx/hooks/use-local-camera.json +5 -0
  27. package/src/templates/jsx/hooks/use-local-microphone.json +5 -0
  28. package/src/templates/jsx/hooks/use-local-screenshare.json +5 -0
  29. package/src/templates/jsx/hooks/use-remote-participant-ids.json +5 -0
  30. package/src/templates/jsx/hooks/use-replica-ids.json +5 -0
  31. package/src/templates/jsx/hooks/use-request-permissions.json +5 -0
  32. package/src/templates/jsx/hooks/use-start-haircheck.json +5 -0
  33. package/src/templates/jsx/index.ts +14 -0
  34. package/src/templates/tsx/components/audio-wave.json +5 -0
  35. package/src/templates/tsx/components/controls.json +10 -0
  36. package/src/templates/tsx/components/conversation-01.json +11 -0
  37. package/src/templates/tsx/components/cvi-provider.json +5 -0
  38. package/src/templates/tsx/components/hair-check-01.json +10 -0
  39. package/src/templates/tsx/hooks/cvi-events-hooks.json +5 -0
  40. package/src/templates/tsx/hooks/use-cvi-call.json +5 -0
  41. package/src/templates/tsx/hooks/use-local-camera.json +5 -0
  42. package/src/templates/tsx/hooks/use-local-microphone.json +5 -0
  43. package/src/templates/tsx/hooks/use-local-screenshare.json +5 -0
  44. package/src/templates/tsx/hooks/use-remote-participant-ids.json +5 -0
  45. package/src/templates/tsx/hooks/use-replica-ids.json +5 -0
  46. package/src/templates/tsx/hooks/use-request-permissions.json +8 -0
  47. package/src/templates/tsx/hooks/use-start-haircheck.json +5 -0
  48. package/src/templates/tsx/index.ts +14 -0
  49. package/src/utils/resolve-components-tree.ts +59 -2
  50. package/src/utils/update-files.ts +38 -10
  51. package/src/utils/version-utils.ts +26 -0
  52. /package/dev-components/hooks/{use-cvi-call.ts → use-cvi-call.tsx} +0 -0
  53. /package/dev-components/hooks/{use-local-camera.ts → use-local-camera.tsx} +0 -0
  54. /package/dev-components/hooks/{use-local-microphone.ts → use-local-microphone.tsx} +0 -0
  55. /package/dev-components/hooks/{use-local-screenshare.ts → use-local-screenshare.tsx} +0 -0
  56. /package/dev-components/hooks/{use-remote-participant-ids.ts → use-remote-participant-ids.tsx} +0 -0
  57. /package/dev-components/hooks/{use-replica-ids.ts → use-replica-ids.tsx} +0 -0
  58. /package/dev-components/hooks/{use-request-permissions.ts → use-request-permissions.tsx} +0 -0
  59. /package/dev-components/hooks/{use-start-haircheck.ts → use-start-haircheck.tsx} +0 -0
package/README.md CHANGED
@@ -6,49 +6,49 @@ A CLI tool for installing and managing CVI (Conversational Video Interface) comp
6
6
 
7
7
  **Initialize the project**:
8
8
 
9
- ```bash
10
- npx @tavus/cvi-ui init
11
- ```
9
+ ```bash
10
+ npx @tavus/cvi-ui@latest init
11
+ ```
12
12
 
13
- This will:
13
+ This will:
14
14
 
15
- - Create a `cvi-components.json` configuration file
16
- - Prompt for TypeScript preference
17
- - Install the necessary dependencies
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
- ```bash
22
- npx @tavus/cvi-ui add conversation
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
- ```tsx
28
- import { CVIProvider } from './components/cvi/components/cvi-provider';
27
+ ```tsx
28
+ import { CVIProvider } from './components/cvi/components/cvi-provider';
29
29
 
30
- function App() {
31
- return (
32
- <CVIProvider>
33
- {/* Your app content */}
34
- </CVIProvider>
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
- ```tsx
42
- import { Conversation } from './components/cvi/components/conversation';
41
+ ```tsx
42
+ import { Conversation } from './components/cvi/components/conversation';
43
43
 
44
- function Call() {
45
- return (
46
- <div>
47
- <Conversation {...conversationProps} />
48
- </div>
49
- );
50
- }
51
- ```
44
+ function Call() {
45
+ return (
46
+ <div>
47
+ <Conversation {...conversationProps} />
48
+ </div>
49
+ );
50
+ }
51
+ ```
52
52
 
53
53
  ## Available Components
54
54
 
@@ -59,15 +59,27 @@ A CLI tool for installing and managing CVI (Conversational Video Interface) comp
59
59
  ### UI Components
60
60
 
61
61
  - **`controls`** - Video chat control buttons with device selection capabilities
62
+
62
63
  - `MicrophoneButton` - Toggle microphone with device dropdown
63
- - `CameraButton` - Toggle camera with device dropdown
64
+ - `CameraButton` - Toggle camera with device dropdown
64
65
  - `ScreenShareButton` - Toggle screen sharing
65
66
 
66
- - **`hair-check`** - Pre-call interface for testing and configuring audio/video devices
67
+ - **`hair-check-01`** - Pre-call interface for testing and configuring audio/video devices
68
+
67
69
  - Live camera preview with mirror effect
68
70
  - Permission management and device controls
69
71
  - Join interface with loading states
70
72
 
73
+ - **`conversation-01`** - Conversation component for video chat
74
+
75
+ - Video chat with audio wave indicator
76
+ - Device selection and permission management
77
+ - Screen sharing support
78
+ - Error handling
79
+
80
+ - **`audio-wave`** - Audio wave indicator for conversation
81
+ - Shows active speaker audio level
82
+
71
83
  ### Hooks
72
84
 
73
85
  #### Core Call Management
@@ -100,4 +112,3 @@ MIT License - see the [LICENSE](LICENSE) file for details.
100
112
 
101
113
  - [CVI Documentation](https://docs.tavus.io/sections/conversational-video-interface/cvi-overview)
102
114
  - [Tavus Examples](https://github.com/Tavus-Engineering/tavus-examples)
103
-
@@ -11,6 +11,7 @@ The `Controls` component provides a set of video chat control buttons with integ
11
11
  - **`MicrophoneButton`**: A button component that toggles microphone on/off with a dropdown for device selection
12
12
  - **`CameraButton`**: A button component that toggles camera on/off with a dropdown for device selection
13
13
  - **`ScreenShareButton`**: A button component that toggles screen sharing on/off
14
+ - **`SelectDevice`**: A reusable dropdown component for selecting camera and microphone devices
14
15
 
15
16
  #### Usage:
16
17
 
@@ -62,7 +63,7 @@ import { MicrophoneButton, CameraButton, ScreenShareButton } from './controls';
62
63
  - Integration with Daily.co device management system
63
64
  - CSS modules for scoped styling
64
65
 
65
- ### Hair Check
66
+ ### Hair Check 01
66
67
 
67
68
  The `HairCheck` component provides a pre-call interface for users to test and configure their audio/video devices before joining a video chat.
68
69
 
@@ -99,3 +100,63 @@ import { HairCheck } from './hair-check';
99
100
  - Fallback user avatar when camera is unavailable
100
101
  - Integrated device controls for easy testing
101
102
  - Clean, intuitive interface for pre-call setup
103
+
104
+ ### Conversation 01
105
+
106
+ 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.
107
+
108
+ #### Features:
109
+
110
+ - **Main Video Display**: Large video area showing the AI replica or screen share
111
+ - **Self-View Preview**: Small preview window showing local camera feed
112
+ - **Screen Sharing Support**: Automatic switching between replica video and screen share
113
+ - **Device Controls**: Integrated microphone, camera, and screen share controls
114
+ - **Error Handling**: Graceful handling of camera/microphone permission errors
115
+ - **Responsive Layout**: Adaptive design for different screen sizes
116
+
117
+ #### Usage:
118
+
119
+ ```tsx
120
+ import { Conversation } from './conversation-01';
121
+
122
+ <Conversation
123
+ conversation={conversationData}
124
+ onLeave={() => handleLeaveCall()}
125
+ />
126
+ ```
127
+
128
+ #### Props:
129
+
130
+ - `conversation` (ConversationData): Object containing conversation details
131
+ - `conversation_id` (string): Unique identifier for the conversation
132
+ - `conversation_name` (string): Display name for the conversation
133
+ - `status` ('active' | 'ended' | 'error'): Current conversation status
134
+ - `conversation_url` (string): Daily.co room URL for joining
135
+ - `replica_id` (string | null): ID of the AI replica participant
136
+ - `persona_id` (string | null): ID of the persona being used
137
+ - `created_at` (string): Timestamp of conversation creation
138
+ - `onLeave` (function): Callback when user leaves the conversation
139
+
140
+ ### Audio Wave
141
+
142
+ The `AudioWave` component provides real-time audio level visualization for video chat participants, displaying animated bars that respond to audio input levels.
143
+
144
+ #### Features:
145
+
146
+ - **Real-time Audio Visualization**: Three animated bars that respond to audio levels
147
+ - **Active Speaker Detection**: Visual distinction between active and inactive speakers
148
+ - **Performance Optimized**: Uses `requestAnimationFrame` for smooth animations
149
+ - **Responsive Design**: Compact circular design that fits well in video previews
150
+ - **Audio Level Scaling**: Intelligent volume scaling for consistent visual feedback
151
+
152
+ #### Usage:
153
+
154
+ ```tsx
155
+ import { AudioWave } from './audio-wave';
156
+
157
+ <AudioWave id={participantId} />
158
+ ```
159
+
160
+ #### Props:
161
+
162
+ - `id` (string): The participant's session ID to monitor audio levels for
@@ -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';
@@ -1,113 +1,115 @@
1
1
  /* SelectDevice styles */
2
2
  .selectDevice {
3
- height: 3rem;
4
- width: 5.5rem;
5
- border-radius: 9999px;
6
- background-color: rgba(255, 255, 255, 0.2);
7
- padding: 0 0.75rem;
8
- border: 1px solid rgba(255, 255, 255, 0.2);
9
- backdrop-filter: blur(10px);
10
- color: transparent;
11
- -webkit-appearance: none;
12
- -moz-appearance: none;
13
- appearance: none;
3
+ height: 3rem;
4
+ width: 5.5rem;
5
+ border-radius: 9999px;
6
+ background-color: rgba(255, 255, 255, 0.2);
7
+ padding: 0 0.75rem;
8
+ border: 1px solid rgba(255, 255, 255, 0.2);
9
+ backdrop-filter: blur(10px);
10
+ color: transparent;
11
+ padding-right: 2rem; /* space for arrow */
12
+ box-sizing: border-box;
13
+ -webkit-appearance: none;
14
+ -moz-appearance: none;
15
+ appearance: none;
16
+ cursor: pointer;
14
17
  }
15
18
 
16
19
  .selectDevice::-ms-expand {
17
- display: none;
20
+ display: none;
18
21
  }
19
22
 
20
23
  .selectDevice option {
21
- color: transparent;
22
- background-color: transparent;
24
+ color: transparent;
25
+ background-color: transparent;
23
26
  }
24
27
 
25
28
  .selectDevice:focus {
26
- outline: none;
29
+ outline: none;
27
30
  }
28
31
 
29
32
  /* Device button container styles */
30
33
  .deviceButtonContainer {
31
- position: relative;
32
- display: flex;
33
- align-items: center;
34
- justify-content: center;
35
- border-radius: 9999px;
36
- backdrop-filter: blur(4px);
34
+ position: relative;
35
+ display: flex;
36
+ align-items: center;
37
+ justify-content: center;
38
+ border-radius: 9999px;
39
+ backdrop-filter: blur(4px);
37
40
  }
38
41
 
39
42
  /* Device button styles */
40
43
  .deviceButton {
41
- position: absolute;
42
- left: 0;
43
- top: 0;
44
- z-index: 10;
45
- height: 3rem;
46
- width: 3rem;
47
- border-radius: 9999px;
48
- background-color: white;
49
- display: flex;
50
- align-items: center;
51
- justify-content: center;
52
- border: 1px solid transparent;
44
+ position: absolute;
45
+ left: 0;
46
+ top: 0;
47
+ z-index: 10;
48
+ height: 3rem;
49
+ width: 3rem;
50
+ border-radius: 9999px;
51
+ background-color: white;
52
+ display: flex;
53
+ align-items: center;
54
+ justify-content: center;
55
+ border: 1px solid transparent;
56
+ cursor: pointer;
53
57
  }
54
58
 
55
59
  .deviceButtonIcon {
56
- display: flex;
60
+ display: flex;
57
61
  }
58
62
 
59
63
  .deviceButton:disabled {
60
- opacity: 0.5;
64
+ opacity: 0.5;
61
65
  }
62
66
 
63
67
  /* Screen reader only text */
64
68
  .srOnly {
65
- position: absolute;
66
- width: 1px;
67
- height: 1px;
68
- padding: 0;
69
- margin: -1px;
70
- overflow: hidden;
71
- clip: rect(0, 0, 0, 0);
72
- white-space: nowrap;
73
- border-width: 0;
69
+ position: absolute;
70
+ width: 1px;
71
+ height: 1px;
72
+ padding: 0;
73
+ margin: -1px;
74
+ overflow: hidden;
75
+ clip: rect(0, 0, 0, 0);
76
+ white-space: nowrap;
77
+ border-width: 0;
74
78
  }
75
79
 
76
80
  /* Controls container */
77
81
  .controlsContainer {
78
- display: flex;
79
- gap: 1rem;
80
- justify-content: center;
81
- align-items: center;
82
- }
83
-
84
- /* End call button */
85
- .endCallButton {
86
- height: 3rem;
87
- width: 3rem;
88
- border-radius: 9999px;
89
- background-color: #EF4444;
90
- display: flex;
91
- align-items: center;
92
- justify-content: center;
82
+ display: flex;
83
+ gap: 1rem;
84
+ justify-content: center;
85
+ align-items: center;
93
86
  }
94
87
 
95
88
  .selectDeviceContainer {
96
- position: relative;
97
- display: flex;
98
- align-items: center;
89
+ position: relative;
90
+ display: flex;
91
+ align-items: center;
99
92
  }
100
93
 
101
- .selectDevice {
102
- padding-right: 2rem; /* space for arrow */
103
- box-sizing: border-box;
94
+ .selectArrow {
95
+ position: absolute;
96
+ right: 1rem;
97
+ pointer-events: none;
98
+ display: flex;
99
+ align-items: center;
100
+ height: 100%;
104
101
  }
105
102
 
106
- .selectArrow {
107
- position: absolute;
108
- right: 1rem;
109
- pointer-events: none;
110
- display: flex;
111
- align-items: center;
112
- height: 100%;
113
- }
103
+ .screenShareButton {
104
+ cursor: pointer;
105
+ position: relative;
106
+ height: 3rem;
107
+ width: 3rem;
108
+ display: flex;
109
+ align-items: center;
110
+ justify-content: center;
111
+ border-radius: 9999px;
112
+ backdrop-filter: blur(4px);
113
+ background-color: rgba(255, 255, 255, 0.2);
114
+ border: 1px solid rgba(255, 255, 255, 0.2);
115
+ }
@@ -144,7 +144,7 @@ export const CameraButton = memo(() => {
144
144
  disabled={!isCamReady || !currentCam}
145
145
  className={styles.deviceButton}
146
146
  >
147
- <span>
147
+ <span className={styles.deviceButtonIcon}>
148
148
  {isCamMuted ? (
149
149
  <svg
150
150
  xmlns="http://www.w3.org/2000/svg"
@@ -218,7 +218,7 @@ export const ScreenShareButton = memo(() => {
218
218
  <button
219
219
  type="button"
220
220
  onClick={onToggleScreenshare}
221
- className={styles.deviceButton}
221
+ className={`${styles.deviceButtonContainer} ${styles.screenShareButton}`}
222
222
  >
223
223
  <span>
224
224
  {isScreenSharing ? (