@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.
Files changed (77) hide show
  1. package/README.md +42 -75
  2. package/dev-components/components/README.md +145 -59
  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/conversation-01/conversation.module.css +228 -0
  6. package/dev-components/components/conversation-01/index.tsx +173 -0
  7. package/dev-components/components/device-select/device-select.module.css +115 -0
  8. package/dev-components/components/{controls → device-select}/index.tsx +7 -7
  9. package/dev-components/components/{hair-check → hair-check-01}/hair-check.module.css +3 -2
  10. package/dev-components/components/{hair-check → hair-check-01}/index.tsx +4 -4
  11. package/dev-components/components/media-controls/index.tsx +207 -0
  12. package/dev-components/components/media-controls/media-controls.module.css +49 -0
  13. package/dev-components/hooks/cvi-events-hooks.tsx +6 -6
  14. package/dev-components/hooks/{use-cvi-call.ts → use-cvi-call.tsx} +7 -0
  15. package/dist/index.js +457 -110
  16. package/dist/types/templates/components.d.ts +37 -4
  17. package/dist/types/templates/jsx/index.d.ts +13 -2
  18. package/dist/types/templates/tsx/index.d.ts +13 -2
  19. package/dist/types/utils/version-utils.d.ts +3 -0
  20. package/package.json +9 -5
  21. package/prepare-scripts/create-templates.js +1 -1
  22. package/src/templates/components.ts +19 -0
  23. package/src/templates/index.ts +2 -0
  24. package/src/templates/jsx/components/audio-wave.json +5 -0
  25. package/src/templates/jsx/components/conversation-01.json +13 -0
  26. package/src/templates/jsx/components/cvi-provider.json +5 -0
  27. package/src/templates/jsx/components/device-select.json +10 -0
  28. package/src/templates/jsx/components/hair-check-01.json +11 -0
  29. package/src/templates/jsx/components/media-controls.json +10 -0
  30. package/src/templates/jsx/hooks/cvi-events-hooks.json +5 -0
  31. package/src/templates/jsx/hooks/use-cvi-call.json +5 -0
  32. package/src/templates/jsx/hooks/use-local-camera.json +5 -0
  33. package/src/templates/jsx/hooks/use-local-microphone.json +5 -0
  34. package/src/templates/jsx/hooks/use-local-screenshare.json +5 -0
  35. package/src/templates/jsx/hooks/use-remote-participant-ids.json +5 -0
  36. package/src/templates/jsx/hooks/use-replica-ids.json +5 -0
  37. package/src/templates/jsx/hooks/use-request-permissions.json +5 -0
  38. package/src/templates/jsx/hooks/use-start-haircheck.json +5 -0
  39. package/src/templates/jsx/index.ts +15 -0
  40. package/src/templates/tsx/components/audio-wave.json +5 -0
  41. package/src/templates/tsx/components/conversation-01.json +13 -0
  42. package/src/templates/tsx/components/cvi-provider.json +5 -0
  43. package/src/templates/tsx/components/device-select.json +10 -0
  44. package/src/templates/tsx/components/hair-check-01.json +11 -0
  45. package/src/templates/tsx/components/media-controls.json +10 -0
  46. package/src/templates/tsx/hooks/cvi-events-hooks.json +5 -0
  47. package/src/templates/tsx/hooks/use-cvi-call.json +5 -0
  48. package/src/templates/tsx/hooks/use-local-camera.json +5 -0
  49. package/src/templates/tsx/hooks/use-local-microphone.json +5 -0
  50. package/src/templates/tsx/hooks/use-local-screenshare.json +5 -0
  51. package/src/templates/tsx/hooks/use-remote-participant-ids.json +5 -0
  52. package/src/templates/tsx/hooks/use-replica-ids.json +5 -0
  53. package/src/templates/tsx/hooks/use-request-permissions.json +8 -0
  54. package/src/templates/tsx/hooks/use-start-haircheck.json +5 -0
  55. package/src/templates/tsx/index.ts +15 -0
  56. package/src/utils/resolve-components-tree.ts +59 -2
  57. package/src/utils/update-files.ts +38 -10
  58. package/src/utils/version-utils.ts +26 -0
  59. package/dev-components/components/controls/controls.module.css +0 -113
  60. package/dist/templates/components/controls.tsx +0 -279
  61. package/dist/templates/components/cvi-provider.tsx +0 -9
  62. package/dist/templates/controls.tsx +0 -279
  63. package/dist/templates/cvi-hooks.tsx +0 -38
  64. package/dist/templates/cvi-provider.tsx +0 -9
  65. package/dist/templates/hooks/cvi-hooks.tsx +0 -38
  66. package/dist/templates/tsx/components/controls.tsx +0 -279
  67. package/dist/templates/tsx/components/cvi-provider.tsx +0 -9
  68. package/dist/types/constants/components.d.ts +0 -59
  69. package/dist/types/utils/resolve-import.d.ts +0 -2
  70. package/dist/typescript-XxXP1Woc.js +0 -14
  71. /package/dev-components/hooks/{use-local-camera.ts → use-local-camera.tsx} +0 -0
  72. /package/dev-components/hooks/{use-local-microphone.ts → use-local-microphone.tsx} +0 -0
  73. /package/dev-components/hooks/{use-local-screenshare.ts → use-local-screenshare.tsx} +0 -0
  74. /package/dev-components/hooks/{use-remote-participant-ids.ts → use-remote-participant-ids.tsx} +0 -0
  75. /package/dev-components/hooks/{use-replica-ids.ts → use-replica-ids.tsx} +0 -0
  76. /package/dev-components/hooks/{use-request-permissions.ts → use-request-permissions.tsx} +0 -0
  77. /package/dev-components/hooks/{use-start-haircheck.ts → use-start-haircheck.tsx} +0 -0
@@ -0,0 +1,228 @@
1
+ .container {
2
+ position: relative;
3
+ width: 100%;
4
+ text-align: center;
5
+ display: flex;
6
+ flex-direction: column;
7
+ align-items: center;
8
+ justify-content: center;
9
+ aspect-ratio: 9/16;
10
+ overflow: hidden;
11
+ border-radius: 0.5rem;
12
+ max-height: 90vh;
13
+ background: linear-gradient(135deg, #4b5563 0%, #1f2937 100%);
14
+ background-size: 400% 400%;
15
+ animation: gradient 15s ease infinite;
16
+ }
17
+
18
+ @media (min-width: 768px) {
19
+ .container {
20
+ aspect-ratio: 16/9;
21
+ }
22
+ }
23
+
24
+ .errorContainer {
25
+ position: relative;
26
+ display: flex;
27
+ align-items: center;
28
+ justify-content: center;
29
+ background: rgba(248, 250, 252, 0.08);
30
+ color: white;
31
+ height: 100%;
32
+ font-size: 1.5rem;
33
+ font-weight: 600;
34
+ text-align: center;
35
+ }
36
+
37
+ .videoContainer {
38
+ position: relative;
39
+ z-index: 5;
40
+ width: 100%;
41
+ height: 100%;
42
+ }
43
+
44
+ .footer {
45
+ position: absolute;
46
+ bottom: 1.5rem;
47
+ left: 0;
48
+ right: 0;
49
+ z-index: 20;
50
+ }
51
+
52
+ .footerControls {
53
+ display: flex;
54
+ justify-content: center;
55
+ align-items: center;
56
+ gap: 16px;
57
+ }
58
+
59
+ .leaveButton {
60
+ background: linear-gradient(135deg, #ff6b6b 0%, #ee5a52 100%);
61
+ color: white;
62
+ border: none;
63
+ font-size: 16px;
64
+ cursor: pointer;
65
+ transition: all 0.3s ease;
66
+ height: 3rem;
67
+ width: 3rem;
68
+ border-radius: 9999px;
69
+ background-color: #ef4444;
70
+ display: flex;
71
+ align-items: center;
72
+ justify-content: center;
73
+ }
74
+
75
+ .leaveButton:hover {
76
+ opacity: 0.8;
77
+ }
78
+
79
+ .leaveButtonIcon {
80
+ display: flex;
81
+ align-items: center;
82
+ justify-content: center;
83
+ }
84
+
85
+ /* ReplicaVideo styles */
86
+ .mainVideoContainer {
87
+ background: transparent;
88
+ width: 100%;
89
+ height: 100%;
90
+ position: relative;
91
+ }
92
+
93
+ .mainVideoContainerScreenSharing {
94
+ display: flex;
95
+ align-items: center;
96
+ justify-content: center;
97
+ }
98
+
99
+ .mainVideo {
100
+ position: absolute;
101
+ inset: 0;
102
+ object-position: center;
103
+ object-fit: cover !important;
104
+ height: 100%;
105
+ width: 100%;
106
+ transition: all 0.3s ease;
107
+ }
108
+
109
+ .mainVideoScreenSharing {
110
+ object-fit: contain !important;
111
+ }
112
+
113
+ .mainVideoHidden {
114
+ display: none;
115
+ }
116
+
117
+ /* PreviewVideo styles */
118
+ .previewVideoContainer {
119
+ position: relative;
120
+ background: rgba(2, 6, 23, 0.3);
121
+ aspect-ratio: 16/9;
122
+ width: 11rem;
123
+ border-radius: 1rem;
124
+ overflow: hidden;
125
+ max-height: 120px;
126
+ z-index: 10;
127
+ }
128
+
129
+ @media (min-width: 768px) {
130
+ .previewVideoContainer {
131
+ max-height: 100%;
132
+ }
133
+ }
134
+
135
+ @media (min-width: 1024px) {
136
+ .previewVideoContainer {
137
+ width: 17.875rem;
138
+ }
139
+ }
140
+
141
+ .previewVideoContainerVertical {
142
+ height: 40.5rem;
143
+ width: 6rem;
144
+ }
145
+
146
+ .previewVideoContainerHidden {
147
+ background: transparent;
148
+ display: none;
149
+ }
150
+
151
+ .previewVideo {
152
+ width: 100%;
153
+ height: auto;
154
+ max-height: 120px;
155
+ }
156
+
157
+ @media (min-width: 768px) {
158
+ .previewVideo {
159
+ max-height: 100%;
160
+ }
161
+ }
162
+
163
+ .previewVideoVertical {
164
+ height: 40.5rem;
165
+ width: 6rem;
166
+ object-fit: cover;
167
+ }
168
+
169
+ .previewVideoHidden {
170
+ display: none;
171
+ }
172
+
173
+ /* Main video container */
174
+ .mainVideoContainer {
175
+ width: 100%;
176
+ height: 100%;
177
+ }
178
+
179
+ /* Self view container */
180
+ .selfViewContainer {
181
+ position: absolute;
182
+ bottom: 5rem;
183
+ left: 1rem;
184
+ display: flex;
185
+ align-items: center;
186
+ justify-content: center;
187
+ flex-direction: column;
188
+ gap: 0.75rem;
189
+ }
190
+
191
+ @media (min-width: 768px) {
192
+ .selfViewContainer {
193
+ bottom: 1rem;
194
+ }
195
+ }
196
+
197
+ /* Waiting message container */
198
+ /* Start of Selection */
199
+ .waitingContainer {
200
+ position: relative;
201
+ display: flex;
202
+ align-items: center;
203
+ justify-content: center;
204
+ background: transparent;
205
+ color: white;
206
+ height: 100%;
207
+ font-size: 1.5rem;
208
+ font-weight: 600;
209
+ }
210
+
211
+ @keyframes gradient {
212
+ 0% {
213
+ background-position: 0% 50%;
214
+ }
215
+ 50% {
216
+ background-position: 100% 50%;
217
+ }
218
+ 100% {
219
+ background-position: 0% 50%;
220
+ }
221
+ }
222
+ /* End of Selection */
223
+
224
+ .audioWaveContainer {
225
+ position: absolute;
226
+ bottom: 0.5rem;
227
+ right: 0.5rem;
228
+ }
@@ -0,0 +1,173 @@
1
+ import React, { useEffect, useCallback } from "react";
2
+ import {
3
+ DailyAudio,
4
+ DailyVideo,
5
+ useDevices,
6
+ useLocalSessionId,
7
+ useMeetingState,
8
+ useScreenVideoTrack,
9
+ useVideoTrack
10
+ } from "@daily-co/daily-react";
11
+ import { MicSelectBtn, CameraSelectBtn, ScreenShareButton } from '../device-select'
12
+ import { useLocalScreenshare } from "../../hooks/use-local-screenshare";
13
+ import { useReplicaIDs } from "../../hooks/use-replica-ids";
14
+ import { useCVICall } from "../../hooks/use-cvi-call";
15
+ import { AudioWave } from "../audio-wave";
16
+
17
+ import styles from "./conversation.module.css";
18
+
19
+ interface ConversationProps {
20
+ onLeave: () => void;
21
+ conversationUrl: string;
22
+ }
23
+
24
+ const VideoPreview = React.memo(({ id }: { id: string }) => {
25
+ const videoState = useVideoTrack(id);
26
+ const widthVideo = videoState.track?.getSettings()?.width;
27
+ const heightVideo = videoState.track?.getSettings()?.height;
28
+ const isVertical = widthVideo && heightVideo ? widthVideo < heightVideo : false;
29
+
30
+ return (
31
+ <div
32
+ className={`${styles.previewVideoContainer} ${isVertical ? styles.previewVideoContainerVertical : ''} ${videoState.isOff ? styles.previewVideoContainerHidden : ''}`}
33
+ >
34
+ <DailyVideo
35
+ automirror
36
+ sessionId={id}
37
+ type="video"
38
+ className={`${styles.previewVideo} ${isVertical ? styles.previewVideoVertical : ''} ${videoState.isOff ? styles.previewVideoHidden : ''}`}
39
+ />
40
+ <div className={styles.audioWaveContainer}>
41
+ <AudioWave id={id} />
42
+ </div>
43
+ </div>
44
+ );
45
+ });
46
+
47
+ const PreviewVideos = React.memo(() => {
48
+ const localId = useLocalSessionId();
49
+ const { isScreenSharing } = useLocalScreenshare();
50
+ const replicaIds = useReplicaIDs();
51
+ const replicaId = replicaIds[0];
52
+
53
+ return (
54
+ <>
55
+ {isScreenSharing && (
56
+ <VideoPreview id={replicaId} />
57
+ )}
58
+ <VideoPreview id={localId} />
59
+ </>
60
+ );
61
+ });
62
+
63
+ const MainVideo = React.memo(() => {
64
+ const replicaIds = useReplicaIDs();
65
+ const localId = useLocalSessionId();
66
+ const videoState = useVideoTrack(replicaIds[0]);
67
+ const screenVideoState = useScreenVideoTrack(localId);
68
+ const isScreenSharing = !screenVideoState.isOff;
69
+ // This is one-to-one call, so we can use the first replica id
70
+ const replicaId = replicaIds[0];
71
+
72
+ if (!replicaId) {
73
+ return (
74
+ <div className={styles.waitingContainer}>
75
+ <p>Connecting...</p>
76
+ </div>
77
+ );
78
+ }
79
+
80
+ // Switching between replica video and screen sharing video
81
+ return (
82
+ <div
83
+ className={`${styles.mainVideoContainer} ${isScreenSharing ? styles.mainVideoContainerScreenSharing : ''}`}
84
+ >
85
+ <DailyVideo
86
+ automirror
87
+ sessionId={isScreenSharing ? localId : replicaId}
88
+ type={isScreenSharing ? "screenVideo" : "video"}
89
+ className={`${styles.mainVideo}
90
+ ${isScreenSharing ? styles.mainVideoScreenSharing : ''}
91
+ ${videoState.isOff ? styles.mainVideoHidden : ''}`}
92
+ />
93
+ </div>
94
+ );
95
+ });
96
+
97
+ export const Conversation = React.memo(({ onLeave, conversationUrl }: ConversationProps) => {
98
+ const { joinCall, leaveCall } = useCVICall();
99
+ const meetingState = useMeetingState();
100
+ const { hasMicError } = useDevices()
101
+
102
+ useEffect(() => {
103
+ if (meetingState === 'error') {
104
+ onLeave();
105
+ }
106
+ }, [meetingState, onLeave]);
107
+
108
+ // Initialize call when conversation is available
109
+ useEffect(() => {
110
+ joinCall({ url: conversationUrl });
111
+ }, []);
112
+
113
+ const handleLeave = useCallback(() => {
114
+ leaveCall();
115
+ onLeave();
116
+ }, [leaveCall, onLeave]);
117
+
118
+ return (
119
+ <div className={styles.container}>
120
+ <div className={styles.videoContainer}>
121
+ {
122
+ hasMicError && (
123
+ <div className={styles.errorContainer}>
124
+ <p>
125
+ Camera or microphone access denied. Please check your settings and try again.
126
+ </p>
127
+ </div>
128
+ )}
129
+
130
+ {/* Main video */}
131
+ <div className={styles.mainVideoContainer}>
132
+ <MainVideo />
133
+ </div>
134
+
135
+ {/* Self view */}
136
+ <div className={styles.selfViewContainer}>
137
+ <PreviewVideos />
138
+ </div>
139
+ </div>
140
+
141
+ <div className={styles.footer}>
142
+ <div className={styles.footerControls}>
143
+ <MicSelectBtn />
144
+ <CameraSelectBtn />
145
+ <ScreenShareButton />
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>
166
+ </button>
167
+ </div>
168
+ </div>
169
+
170
+ <DailyAudio />
171
+ </div>
172
+ );
173
+ });
@@ -0,0 +1,115 @@
1
+ /* SelectDevice styles */
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
+ 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;
17
+ }
18
+
19
+ .selectDevice::-ms-expand {
20
+ display: none;
21
+ }
22
+
23
+ .selectDevice option {
24
+ color: transparent;
25
+ background-color: transparent;
26
+ }
27
+
28
+ .selectDevice:focus {
29
+ outline: none;
30
+ }
31
+
32
+ /* Device button container styles */
33
+ .deviceButtonContainer {
34
+ position: relative;
35
+ display: flex;
36
+ align-items: center;
37
+ justify-content: center;
38
+ border-radius: 9999px;
39
+ backdrop-filter: blur(4px);
40
+ }
41
+
42
+ /* Device button styles */
43
+ .deviceButton {
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;
57
+ }
58
+
59
+ .deviceButtonIcon {
60
+ display: flex;
61
+ }
62
+
63
+ .deviceButton:disabled {
64
+ opacity: 0.5;
65
+ }
66
+
67
+ /* Screen reader only text */
68
+ .srOnly {
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;
78
+ }
79
+
80
+ /* Controls container */
81
+ .controlsContainer {
82
+ display: flex;
83
+ gap: 1rem;
84
+ justify-content: center;
85
+ align-items: center;
86
+ }
87
+
88
+ .selectDeviceContainer {
89
+ position: relative;
90
+ display: flex;
91
+ align-items: center;
92
+ }
93
+
94
+ .selectArrow {
95
+ position: absolute;
96
+ right: 1rem;
97
+ pointer-events: none;
98
+ display: flex;
99
+ align-items: center;
100
+ height: 100%;
101
+ }
102
+
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
+ }
@@ -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
 
@@ -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"
@@ -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();
@@ -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 ? (
@@ -46,6 +46,7 @@
46
46
  height: 3rem;
47
47
  transition: background-color 0.2s;
48
48
  font-weight: 500;
49
+ min-width: 8.5rem;
49
50
  }
50
51
 
51
52
  .buttonJoin:hover .buttonJoinInner {
@@ -114,7 +115,7 @@
114
115
  .haircheckUserPlaceholder {
115
116
  position: absolute;
116
117
  inset: 0;
117
- background-color: #334155;
118
+ background: linear-gradient(135deg, #4b5563 0%, #1f2937 100%);
118
119
  z-index: 5;
119
120
  pointer-events: none;
120
121
  display: flex;
@@ -207,5 +208,5 @@
207
208
  display: flex;
208
209
  align-items: flex-end;
209
210
  justify-content: space-between;
210
- gap: 0.5rem;
211
+ gap: 1rem;
211
212
  }
@@ -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
 
@@ -25,7 +25,7 @@ const JoinBtn = ({
25
25
  className={`${styles.buttonJoin} ${className || ''}`}
26
26
  type="button"
27
27
  onClick={onClick}
28
- disabled={disabled}
28
+ disabled={disabled || loading}
29
29
  >
30
30
  <div className={styles.buttonJoinInner}>
31
31
  {loading ? "Joining..." : "Join Video Chat"}
@@ -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>