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

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 (50) hide show
  1. package/README.md +15 -59
  2. package/dev-components/components/README.md +95 -70
  3. package/dev-components/components/conversation-01/conversation.module.css +7 -1
  4. package/dev-components/components/conversation-01/index.tsx +38 -45
  5. package/dev-components/components/{controls → device-select}/index.tsx +5 -5
  6. package/dev-components/components/hair-check-01/hair-check.module.css +1 -1
  7. package/dev-components/components/hair-check-01/index.tsx +3 -3
  8. package/dev-components/components/media-controls/index.tsx +207 -0
  9. package/dev-components/components/media-controls/media-controls.module.css +49 -0
  10. package/dev-components/hooks/cvi-events-hooks.tsx +6 -6
  11. package/dev-components/hooks/use-cvi-call.tsx +7 -0
  12. package/dist/index.js +195 -145
  13. package/dist/types/constants/frameworks.d.ts +4 -0
  14. package/dist/types/templates/components.d.ts +6 -3
  15. package/dist/types/templates/jsx/index.d.ts +2 -1
  16. package/dist/types/templates/tsx/index.d.ts +2 -1
  17. package/package.json +6 -2
  18. package/prepare-scripts/create-templates.js +1 -1
  19. package/src/constants/frameworks.ts +4 -0
  20. package/src/preflights/preflight-init.ts +1 -1
  21. package/src/templates/components.ts +2 -1
  22. package/src/templates/jsx/components/conversation-01.json +6 -4
  23. package/src/templates/jsx/components/device-select.json +10 -0
  24. package/src/templates/jsx/components/hair-check-01.json +5 -4
  25. package/src/templates/jsx/components/media-controls.json +10 -0
  26. package/src/templates/jsx/hooks/cvi-events-hooks.json +1 -1
  27. package/src/templates/jsx/hooks/use-cvi-call.json +1 -1
  28. package/src/templates/jsx/index.ts +2 -1
  29. package/src/templates/tsx/components/conversation-01.json +6 -4
  30. package/src/templates/tsx/components/device-select.json +10 -0
  31. package/src/templates/tsx/components/hair-check-01.json +5 -4
  32. package/src/templates/tsx/components/media-controls.json +10 -0
  33. package/src/templates/tsx/hooks/cvi-events-hooks.json +1 -1
  34. package/src/templates/tsx/hooks/use-cvi-call.json +1 -1
  35. package/src/templates/tsx/index.ts +2 -1
  36. package/src/utils/get-project-info.ts +118 -124
  37. package/dist/templates/components/controls.tsx +0 -279
  38. package/dist/templates/components/cvi-provider.tsx +0 -9
  39. package/dist/templates/controls.tsx +0 -279
  40. package/dist/templates/cvi-hooks.tsx +0 -38
  41. package/dist/templates/cvi-provider.tsx +0 -9
  42. package/dist/templates/hooks/cvi-hooks.tsx +0 -38
  43. package/dist/templates/tsx/components/controls.tsx +0 -279
  44. package/dist/templates/tsx/components/cvi-provider.tsx +0 -9
  45. package/dist/types/constants/components.d.ts +0 -59
  46. package/dist/types/utils/resolve-import.d.ts +0 -2
  47. package/dist/typescript-XxXP1Woc.js +0 -14
  48. package/src/templates/jsx/components/controls.json +0 -10
  49. package/src/templates/tsx/components/controls.json +0 -10
  50. /package/dev-components/components/{controls/controls.module.css → device-select/device-select.module.css} +0 -0
package/README.md CHANGED
@@ -2,7 +2,7 @@
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
- ## Usage
5
+ ## Quick Start
6
6
 
7
7
  **Initialize the project**:
8
8
 
@@ -41,74 +41,30 @@ function App() {
41
41
  ```tsx
42
42
  import { Conversation } from './components/cvi/components/conversation';
43
43
 
44
- function Call() {
44
+ function CVI() {
45
45
  return (
46
- <div>
47
- <Conversation {...conversationProps} />
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={() => {}} />
48
55
  </div>
49
56
  );
50
57
  }
51
58
  ```
52
59
 
53
- ## Available Components
60
+ ## Documentation
54
61
 
55
- ### Core Components
62
+ - [CVI Documentation](https://docs.tavus.io/sections/conversational-video-interface/component-library/overview)
56
63
 
57
- - **`cvi-provider`** - Main provider component that wraps your app with CVI context
64
+ ## Examples
58
65
 
59
- ### UI Components
60
-
61
- - **`controls`** - Video chat control buttons with device selection capabilities
62
-
63
- - `MicrophoneButton` - Toggle microphone with device dropdown
64
- - `CameraButton` - Toggle camera with device dropdown
65
- - `ScreenShareButton` - Toggle screen sharing
66
-
67
- - **`hair-check-01`** - Pre-call interface for testing and configuring audio/video devices
68
-
69
- - Live camera preview with mirror effect
70
- - Permission management and device controls
71
- - Join interface with loading states
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
-
83
- ### Hooks
84
-
85
- #### Core Call Management
86
-
87
- - **`use-cvi-call`** – Provides basic join and leave functionality for CVI calls.
88
- - **`use-start-haircheck`** – Manages device-permission checks and initializes the camera to start a haircheck session.
89
-
90
- #### Media Controls
91
-
92
- - **`use-local-camera`** – Manages local camera state and toggle functionality.
93
- - **`use-local-microphone`** – Manages local microphone state and toggle functionality.
94
- - **`use-local-screenshare`** – Manages screen-share state and toggle functionality.
95
- - **`use-request-permissions`** – Requests permissions and handles audio processing setup.
96
-
97
- #### Participant Management
98
-
99
- - **`use-replica-ids`** – Retrieves Tavus–replica participant IDs.
100
- - **`use-remote-participant-ids`** – Retrieves remote participant IDs.
101
-
102
- #### Conversation & Events
103
-
104
- - **`use-observable-event`** – Listens for CVI events.
105
- - **`use-send-app-message`** – Sends CVI app messages.
66
+ - [Tavus Examples](https://github.com/Tavus-Engineering/tavus-examples)
106
67
 
107
68
  ## License
108
69
 
109
70
  MIT License - see the [LICENSE](LICENSE) file for details.
110
-
111
- ## Related Links
112
-
113
- - [CVI Documentation](https://docs.tavus.io/sections/conversational-video-interface/cvi-overview)
114
- - [Tavus Examples](https://github.com/Tavus-Engineering/tavus-examples)
@@ -2,67 +2,6 @@
2
2
 
3
3
  ## Components
4
4
 
5
- ### Controls
6
-
7
- The `Controls` component provides a set of video chat control buttons with integrated device selection capabilities.
8
-
9
- #### Exported Components:
10
-
11
- - **`MicrophoneButton`**: A button component that toggles microphone on/off with a dropdown for device selection
12
- - **`CameraButton`**: A button component that toggles camera on/off with a dropdown for device selection
13
- - **`ScreenShareButton`**: A button component that toggles screen sharing on/off
14
- - **`SelectDevice`**: A reusable dropdown component for selecting camera and microphone devices
15
-
16
- #### Usage:
17
-
18
- ```tsx
19
- import { MicrophoneButton, CameraButton, ScreenShareButton } from './controls';
20
-
21
- // Use individual control buttons
22
- <MicrophoneButton />
23
- <CameraButton />
24
- <ScreenShareButton />
25
- ```
26
-
27
- #### Component Details:
28
-
29
- **MicrophoneButton**
30
-
31
- - Toggles microphone mute/unmute state
32
- - Shows visual indicators for muted/unmuted states
33
- - Includes device selection dropdown
34
- - Disabled when microphone is not ready
35
- - Uses `useLocalMicrophone` and `useDevices` hooks
36
-
37
- **CameraButton**
38
-
39
- - Toggles camera on/off
40
- - Shows visual indicators for camera muted/unmuted states
41
- - Includes device selection dropdown
42
- - Disabled when camera is not ready or no camera is available
43
- - Uses `useLocalCamera` and `useDevices` hooks
44
-
45
- **ScreenShareButton**
46
-
47
- - Toggles screen sharing on/off
48
- - Shows visual feedback with different icons for active/inactive states
49
- - Uses `useLocalScreenshare` hook
50
-
51
- **SelectDevice**
52
-
53
- - Reusable dropdown component for device selection
54
- - Accepts devices array, current value, disabled state, and onChange handler
55
- - Includes custom dropdown arrow styling
56
-
57
- #### Key Features:
58
-
59
- - Automatic device detection and switching via Daily.co hooks
60
- - Visual state indicators with SVG icons
61
- - Accessibility support with screen reader labels (`srOnly` class)
62
- - Responsive design with mobile-friendly controls
63
- - Integration with Daily.co device management system
64
- - CSS modules for scoped styling
65
-
66
5
  ### Hair Check 01
67
6
 
68
7
  The `HairCheck` component provides a pre-call interface for users to test and configure their audio/video devices before joining a video chat.
@@ -120,21 +59,14 @@ The `Conversation` component provides a complete video chat interface for one-to
120
59
  import { Conversation } from './conversation-01';
121
60
 
122
61
  <Conversation
123
- conversation={conversationData}
62
+ conversationUrl={conversationUrl}
124
63
  onLeave={() => handleLeaveCall()}
125
64
  />
126
65
  ```
127
66
 
128
67
  #### Props:
129
68
 
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
69
+ - `conversationUrl` (string): Daily.co room URL for joining
138
70
  - `onLeave` (function): Callback when user leaves the conversation
139
71
 
140
72
  ### Audio Wave
@@ -160,3 +92,96 @@ import { AudioWave } from './audio-wave';
160
92
  #### Props:
161
93
 
162
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.
99
+
100
+ #### Exported Components:
101
+
102
+ - **`MicToggleButton`**: Toggles microphone mute/unmute state
103
+ - **`CameraToggleButton`**: Toggles camera on/off
104
+ - **`ScreenShareButton`**: Toggles screen sharing on/off
105
+
106
+ #### Usage:
107
+
108
+ ```tsx
109
+ import { MicToggleButton, CameraToggleButton, ScreenShareButton } from './media-controls';
110
+
111
+ <MicToggleButton />
112
+ <CameraToggleButton />
113
+ <ScreenShareButton />
114
+ ```
115
+
116
+ #### Key Features:
117
+
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
123
+
124
+ ---
125
+
126
+ ### Device Select
127
+
128
+ The `device-select` module provides advanced device selection controls, including dropdowns for choosing microphones and cameras, and integrated toggle buttons.
129
+
130
+ #### Exported Components:
131
+
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
136
+
137
+ #### Usage:
138
+
139
+ ```tsx
140
+ import { SelectDevice, MicSelectBtn, CameraSelectBtn, ScreenShareButton } from './device-select';
141
+
142
+ <SelectDevice value={currentDeviceId} devices={devices} disabled={false} onChange={setDevice} />
143
+ <MicSelectBtn />
144
+ <CameraSelectBtn />
145
+ <ScreenShareButton />
146
+ ```
147
+
148
+ #### Props (SelectDevice):
149
+
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
154
+
155
+ #### Key Features:
156
+
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
162
+
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.
168
+
169
+ #### Usage:
170
+
171
+ ```tsx
172
+ import { CVIProvider } from './cvi-provider';
173
+
174
+ <CVIProvider>
175
+ {/* your app components */}
176
+ </CVIProvider>
177
+ ```
178
+
179
+ #### Props:
180
+
181
+ - `children` (ReactNode): Components to be wrapped by the provider
182
+
183
+ #### Key Features:
184
+
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
@@ -10,7 +10,7 @@
10
10
  overflow: hidden;
11
11
  border-radius: 0.5rem;
12
12
  max-height: 90vh;
13
- background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
13
+ background: linear-gradient(135deg, #4b5563 0%, #1f2937 100%);
14
14
  background-size: 400% 400%;
15
15
  animation: gradient 15s ease infinite;
16
16
  }
@@ -76,6 +76,12 @@
76
76
  opacity: 0.8;
77
77
  }
78
78
 
79
+ .leaveButtonIcon {
80
+ display: flex;
81
+ align-items: center;
82
+ justify-content: center;
83
+ }
84
+
79
85
  /* ReplicaVideo styles */
80
86
  .mainVideoContainer {
81
87
  background: transparent;
@@ -1,34 +1,24 @@
1
- import React, { useEffect } from "react";
1
+ import React, { useEffect, useCallback } from "react";
2
2
  import {
3
3
  DailyAudio,
4
4
  DailyVideo,
5
- useDaily,
6
5
  useDevices,
7
6
  useLocalSessionId,
8
7
  useMeetingState,
9
8
  useScreenVideoTrack,
10
9
  useVideoTrack
11
10
  } from "@daily-co/daily-react";
12
- import { MicrophoneButton, CameraButton, ScreenShareButton } from '../controls'
11
+ import { MicSelectBtn, CameraSelectBtn, ScreenShareButton } from '../device-select'
13
12
  import { useLocalScreenshare } from "../../hooks/use-local-screenshare";
14
13
  import { useReplicaIDs } from "../../hooks/use-replica-ids";
14
+ import { useCVICall } from "../../hooks/use-cvi-call";
15
15
  import { AudioWave } from "../audio-wave";
16
16
 
17
17
  import styles from "./conversation.module.css";
18
18
 
19
- interface ConversationData {
20
- conversation_id: string;
21
- conversation_name: string;
22
- status: 'active' | 'ended' | 'error';
23
- conversation_url: string;
24
- replica_id: string | null;
25
- persona_id: string | null;
26
- created_at: string;
27
- }
28
-
29
19
  interface ConversationProps {
30
20
  onLeave: () => void;
31
- conversation: ConversationData;
21
+ conversationUrl: string;
32
22
  }
33
23
 
34
24
  const VideoPreview = React.memo(({ id }: { id: string }) => {
@@ -76,7 +66,7 @@ const MainVideo = React.memo(() => {
76
66
  const videoState = useVideoTrack(replicaIds[0]);
77
67
  const screenVideoState = useScreenVideoTrack(localId);
78
68
  const isScreenSharing = !screenVideoState.isOff;
79
- // This is one to one call, so we can use the first replica id
69
+ // This is one-to-one call, so we can use the first replica id
80
70
  const replicaId = replicaIds[0];
81
71
 
82
72
  if (!replicaId) {
@@ -96,16 +86,16 @@ const MainVideo = React.memo(() => {
96
86
  automirror
97
87
  sessionId={isScreenSharing ? localId : replicaId}
98
88
  type={isScreenSharing ? "screenVideo" : "video"}
99
- className={`${styles.mainVideo}
100
- ${isScreenSharing ? styles.mainVideoScreenSharing : ''}
89
+ className={`${styles.mainVideo}
90
+ ${isScreenSharing ? styles.mainVideoScreenSharing : ''}
101
91
  ${videoState.isOff ? styles.mainVideoHidden : ''}`}
102
92
  />
103
93
  </div>
104
94
  );
105
95
  });
106
96
 
107
- export const Conversation = React.memo(({ onLeave, conversation }: ConversationProps) => {
108
- const daily = useDaily();
97
+ export const Conversation = React.memo(({ onLeave, conversationUrl }: ConversationProps) => {
98
+ const { joinCall, leaveCall } = useCVICall();
109
99
  const meetingState = useMeetingState();
110
100
  const { hasMicError } = useDevices()
111
101
 
@@ -117,12 +107,13 @@ export const Conversation = React.memo(({ onLeave, conversation }: ConversationP
117
107
 
118
108
  // Initialize call when conversation is available
119
109
  useEffect(() => {
120
- if (conversation?.conversation_url) {
121
- daily?.join({
122
- url: conversation.conversation_url,
123
- });
124
- }
125
- }, [conversation, daily]);
110
+ joinCall({ url: conversationUrl });
111
+ }, []);
112
+
113
+ const handleLeave = useCallback(() => {
114
+ leaveCall();
115
+ onLeave();
116
+ }, [leaveCall, onLeave]);
126
117
 
127
118
  return (
128
119
  <div className={styles.container}>
@@ -149,27 +140,29 @@ export const Conversation = React.memo(({ onLeave, conversation }: ConversationP
149
140
 
150
141
  <div className={styles.footer}>
151
142
  <div className={styles.footerControls}>
152
- <MicrophoneButton />
153
- <CameraButton />
143
+ <MicSelectBtn />
144
+ <CameraSelectBtn />
154
145
  <ScreenShareButton />
155
- <button type="button" className={styles.leaveButton} onClick={onLeave}>
156
- <svg
157
- xmlns="http://www.w3.org/2000/svg"
158
- width="24"
159
- height="24"
160
- viewBox="0 0 24 24"
161
- fill="none"
162
- role="img"
163
- aria-label="Leave Call"
164
- >
165
- <path
166
- d="M18 6L6 18M6 6L18 18"
167
- stroke="currentColor"
168
- strokeWidth="2"
169
- strokeLinecap="round"
170
- strokeLinejoin="round"
171
- />
172
- </svg>
146
+ <button type="button" className={styles.leaveButton} onClick={handleLeave}>
147
+ <span className={styles.leaveButtonIcon}>
148
+ <svg
149
+ xmlns="http://www.w3.org/2000/svg"
150
+ width="24"
151
+ height="24"
152
+ viewBox="0 0 24 24"
153
+ fill="none"
154
+ role="img"
155
+ aria-label="Leave Call"
156
+ >
157
+ <path
158
+ d="M18 6L6 18M6 6L18 18"
159
+ stroke="currentColor"
160
+ strokeWidth="2"
161
+ strokeLinecap="round"
162
+ strokeLinejoin="round"
163
+ />
164
+ </svg>
165
+ </span>
173
166
  </button>
174
167
  </div>
175
168
  </div>
@@ -3,7 +3,7 @@ import { useDevices } from "@daily-co/daily-react";
3
3
  import { useLocalCamera } from "../../hooks/use-local-camera";
4
4
  import { useLocalMicrophone } from "../../hooks/use-local-microphone";
5
5
  import { useLocalScreenshare } from "../../hooks/use-local-screenshare";
6
- import styles from "./controls.module.css";
6
+ import styles from "./device-select.module.css";
7
7
 
8
8
  export const SelectDevice = ({
9
9
  value,
@@ -39,7 +39,7 @@ export const SelectDevice = ({
39
39
  );
40
40
  };
41
41
 
42
- export const MicrophoneButton = memo(() => {
42
+ export const MicSelectBtn = memo(() => {
43
43
  const { onToggleMicrophone, isMicReady, isMicMuted } = useLocalMicrophone();
44
44
  const { microphones, currentMic, setMicrophone } = useDevices();
45
45
 
@@ -129,9 +129,9 @@ export const MicrophoneButton = memo(() => {
129
129
  );
130
130
  });
131
131
 
132
- MicrophoneButton.displayName = "MicrophoneButton";
132
+ MicSelectBtn.displayName = "MicSelectBtn";
133
133
 
134
- export const CameraButton = memo(() => {
134
+ export const CameraSelectBtn = memo(() => {
135
135
  const { onToggleCamera, isCamReady, isCamMuted } = useLocalCamera();
136
136
  const { currentCam, cameras, setCamera } = useDevices();
137
137
 
@@ -209,7 +209,7 @@ export const CameraButton = memo(() => {
209
209
  );
210
210
  });
211
211
 
212
- CameraButton.displayName = "CameraButton";
212
+ CameraSelectBtn.displayName = "CameraSelectBtn";
213
213
 
214
214
  export const ScreenShareButton = memo(() => {
215
215
  const { onToggleScreenshare, isScreenSharing } = useLocalScreenshare();
@@ -115,7 +115,7 @@
115
115
  .haircheckUserPlaceholder {
116
116
  position: absolute;
117
117
  inset: 0;
118
- background-color: #334155;
118
+ background: linear-gradient(135deg, #4b5563 0%, #1f2937 100%);
119
119
  z-index: 5;
120
120
  pointer-events: none;
121
121
  display: flex;
@@ -3,7 +3,7 @@ import {
3
3
  DailyVideo,
4
4
  useDaily,
5
5
  } from "@daily-co/daily-react";
6
- import { CameraButton, MicrophoneButton } from "../controls";
6
+ import { CameraSelectBtn, MicSelectBtn } from "../device-select";
7
7
  import { useStartHaircheck } from "../../hooks/use-start-haircheck";
8
8
  import { useLocalCamera } from "../../hooks/use-local-camera";
9
9
 
@@ -172,8 +172,8 @@ export const HairCheck = memo(({
172
172
  />}
173
173
  </div>
174
174
  <div className={styles.haircheckControls}>
175
- <MicrophoneButton />
176
- <CameraButton />
175
+ <MicSelectBtn />
176
+ <CameraSelectBtn />
177
177
  </div>
178
178
  </div>
179
179
  </div>