@convai/web-sdk 0.1.1-beta.5 → 0.2.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 +720 -213
- package/dist/core/AudioManager.d.ts.map +1 -1
- package/dist/core/AudioManager.js +10 -0
- package/dist/core/AudioManager.js.map +1 -1
- package/dist/core/ConvaiClient.d.ts +4 -0
- package/dist/core/ConvaiClient.d.ts.map +1 -1
- package/dist/core/ConvaiClient.js +28 -0
- package/dist/core/ConvaiClient.js.map +1 -1
- package/dist/core/MessageHandler.d.ts.map +1 -1
- package/dist/core/MessageHandler.js +1 -0
- package/dist/core/MessageHandler.js.map +1 -1
- package/dist/core/ScreenShareManager.d.ts.map +1 -1
- package/dist/core/ScreenShareManager.js +4 -0
- package/dist/core/ScreenShareManager.js.map +1 -1
- package/dist/core/VideoManager.d.ts.map +1 -1
- package/dist/core/VideoManager.js +2 -0
- package/dist/core/VideoManager.js.map +1 -1
- package/dist/core/types.d.ts +13 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/react/components/ConvaiWidget.d.ts +3 -0
- package/dist/react/components/ConvaiWidget.d.ts.map +1 -1
- package/dist/react/components/ConvaiWidget.js +20 -12
- package/dist/react/components/ConvaiWidget.js.map +1 -1
- package/dist/react/components/index.d.ts +0 -1
- package/dist/react/components/index.d.ts.map +1 -1
- package/dist/react/components/index.js +0 -2
- package/dist/react/components/index.js.map +1 -1
- package/dist/react/hooks/useConvaiClient.d.ts +3 -0
- package/dist/react/hooks/useConvaiClient.d.ts.map +1 -1
- package/dist/react/hooks/useConvaiClient.js +34 -0
- package/dist/react/hooks/useConvaiClient.js.map +1 -1
- package/dist/vanilla/ConvaiWidget.d.ts +1 -1
- package/dist/vanilla/ConvaiWidget.d.ts.map +1 -1
- package/dist/vanilla/ConvaiWidget.js +625 -367
- package/dist/vanilla/ConvaiWidget.js.map +1 -1
- package/dist/vanilla/index.d.ts +0 -2
- package/dist/vanilla/index.d.ts.map +1 -1
- package/dist/vanilla/index.js +0 -2
- package/dist/vanilla/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Vanilla ConvaiWidget - Complete UI widget for Convai conversations
|
|
3
3
|
* Ports the React ConvaiWidget to vanilla TypeScript with DOM manipulation
|
|
4
4
|
*/
|
|
5
|
-
import { AudioRenderer } from
|
|
6
|
-
import { aeroTheme, injectGlobalStyles } from
|
|
7
|
-
import { Icons } from
|
|
5
|
+
import { AudioRenderer } from "./AudioRenderer";
|
|
6
|
+
import { aeroTheme, injectGlobalStyles } from "./styles";
|
|
7
|
+
import { Icons } from "./icons";
|
|
8
8
|
/**
|
|
9
9
|
* Create a Convai chat widget in the specified container
|
|
10
10
|
*
|
|
@@ -33,7 +33,7 @@ import { Icons } from './icons';
|
|
|
33
33
|
* ```
|
|
34
34
|
*/
|
|
35
35
|
export function createConvaiWidget(container, options) {
|
|
36
|
-
const { convaiClient, showVideo = true, showScreenShare = true
|
|
36
|
+
const { convaiClient, showVideo = true, showScreenShare = true } = options;
|
|
37
37
|
// Inject global styles
|
|
38
38
|
injectGlobalStyles();
|
|
39
39
|
// State
|
|
@@ -42,9 +42,9 @@ export function createConvaiWidget(container, options) {
|
|
|
42
42
|
let isVoiceMode = false;
|
|
43
43
|
let isMuted = false;
|
|
44
44
|
let isVideoVisible = false;
|
|
45
|
-
let inputValue =
|
|
46
|
-
let characterName =
|
|
47
|
-
let characterImage =
|
|
45
|
+
let inputValue = "";
|
|
46
|
+
let characterName = "Character";
|
|
47
|
+
let characterImage = "";
|
|
48
48
|
let audioRenderer = null;
|
|
49
49
|
// DOM elements (will be created below)
|
|
50
50
|
let rootElement;
|
|
@@ -65,31 +65,39 @@ export function createConvaiWidget(container, options) {
|
|
|
65
65
|
let dataArray = null;
|
|
66
66
|
let rafId = null;
|
|
67
67
|
let source = null;
|
|
68
|
-
// Fetch character info
|
|
68
|
+
// Fetch character info - matches React useCharacterInfo hook
|
|
69
69
|
const fetchCharacterInfo = async () => {
|
|
70
70
|
if (!convaiClient.apiKey || !convaiClient.characterId)
|
|
71
71
|
return;
|
|
72
72
|
try {
|
|
73
|
-
const response = await fetch(
|
|
73
|
+
const response = await fetch("https://api.convai.com/character/get", {
|
|
74
|
+
method: "POST",
|
|
74
75
|
headers: {
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
"Content-Type": "application/json",
|
|
77
|
+
"CONVAI-API-KEY": convaiClient.apiKey,
|
|
78
|
+
},
|
|
79
|
+
body: JSON.stringify({ charID: convaiClient.characterId }),
|
|
77
80
|
});
|
|
78
81
|
if (response.ok) {
|
|
79
82
|
const data = await response.json();
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
// Extract character name and image with fallbacks - matches React version
|
|
84
|
+
characterName = data.character_name || "Convi";
|
|
85
|
+
characterImage =
|
|
86
|
+
data.model_details?.METAHUMAN?.avatar_image_square ||
|
|
87
|
+
data.model_details?.METAHUMAN?.avatar_image ||
|
|
88
|
+
data.model_details?.modelPlaceholder ||
|
|
89
|
+
"";
|
|
82
90
|
updateHeader();
|
|
83
91
|
}
|
|
84
92
|
}
|
|
85
93
|
catch (error) {
|
|
86
|
-
console.error(
|
|
94
|
+
console.error("Failed to fetch character info:", error);
|
|
87
95
|
}
|
|
88
96
|
};
|
|
89
97
|
// Create root structure
|
|
90
98
|
const createDOM = () => {
|
|
91
|
-
rootElement = document.createElement(
|
|
92
|
-
rootElement.className =
|
|
99
|
+
rootElement = document.createElement("div");
|
|
100
|
+
rootElement.className = "convai-widget";
|
|
93
101
|
rootElement.style.cssText = `
|
|
94
102
|
position: fixed;
|
|
95
103
|
bottom: 1.5rem;
|
|
@@ -98,7 +106,7 @@ export function createConvaiWidget(container, options) {
|
|
|
98
106
|
font-family: ${aeroTheme.typography.fontFamily.primary};
|
|
99
107
|
`;
|
|
100
108
|
// Morphing container
|
|
101
|
-
morphingContainer = document.createElement(
|
|
109
|
+
morphingContainer = document.createElement("div");
|
|
102
110
|
morphingContainer.style.cssText = `
|
|
103
111
|
position: relative;
|
|
104
112
|
width: 4rem;
|
|
@@ -113,9 +121,10 @@ export function createConvaiWidget(container, options) {
|
|
|
113
121
|
display: flex;
|
|
114
122
|
align-items: center;
|
|
115
123
|
justify-content: center;
|
|
124
|
+
cursor: pointer;
|
|
116
125
|
`;
|
|
117
126
|
// Button content (logo)
|
|
118
|
-
buttonContent = document.createElement(
|
|
127
|
+
buttonContent = document.createElement("div");
|
|
119
128
|
buttonContent.style.cssText = `
|
|
120
129
|
position: absolute;
|
|
121
130
|
inset: 0;
|
|
@@ -126,11 +135,11 @@ export function createConvaiWidget(container, options) {
|
|
|
126
135
|
opacity: 1;
|
|
127
136
|
transform: scale(1);
|
|
128
137
|
`;
|
|
129
|
-
const convaiLogo = Icons.ConvaiLogo(
|
|
138
|
+
const convaiLogo = Icons.ConvaiLogo("xl", "idle");
|
|
130
139
|
convaiLogo.style.color = aeroTheme.colors.convai.light;
|
|
131
140
|
buttonContent.appendChild(convaiLogo);
|
|
132
141
|
// Chat content
|
|
133
|
-
chatContent = document.createElement(
|
|
142
|
+
chatContent = document.createElement("div");
|
|
134
143
|
chatContent.style.cssText = `
|
|
135
144
|
position: absolute;
|
|
136
145
|
inset: 0;
|
|
@@ -147,7 +156,7 @@ export function createConvaiWidget(container, options) {
|
|
|
147
156
|
// Header
|
|
148
157
|
headerElement = createHeader();
|
|
149
158
|
// Content area
|
|
150
|
-
contentElement = document.createElement(
|
|
159
|
+
contentElement = document.createElement("div");
|
|
151
160
|
contentElement.style.cssText = `
|
|
152
161
|
flex: 1;
|
|
153
162
|
overflow-y: auto;
|
|
@@ -172,11 +181,11 @@ export function createConvaiWidget(container, options) {
|
|
|
172
181
|
container.appendChild(floatingVideo);
|
|
173
182
|
container.appendChild(rootElement);
|
|
174
183
|
// Event listeners
|
|
175
|
-
morphingContainer.addEventListener(
|
|
184
|
+
morphingContainer.addEventListener("click", handleToggle);
|
|
176
185
|
};
|
|
177
186
|
// Create Voice Mode Overlay
|
|
178
187
|
const createVoiceModeOverlay = () => {
|
|
179
|
-
const overlay = document.createElement(
|
|
188
|
+
const overlay = document.createElement("div");
|
|
180
189
|
overlay.style.cssText = `
|
|
181
190
|
position: absolute;
|
|
182
191
|
top: 50%;
|
|
@@ -192,8 +201,8 @@ export function createConvaiWidget(container, options) {
|
|
|
192
201
|
gap: 1.5rem;
|
|
193
202
|
`;
|
|
194
203
|
// Bars Container
|
|
195
|
-
const barsContainer = document.createElement(
|
|
196
|
-
barsContainer.id =
|
|
204
|
+
const barsContainer = document.createElement("div");
|
|
205
|
+
barsContainer.id = "voice-bars-container";
|
|
197
206
|
barsContainer.style.cssText = `
|
|
198
207
|
display: flex;
|
|
199
208
|
align-items: center;
|
|
@@ -204,8 +213,8 @@ export function createConvaiWidget(container, options) {
|
|
|
204
213
|
`;
|
|
205
214
|
// Create 40 bars
|
|
206
215
|
for (let i = 0; i < 40; i++) {
|
|
207
|
-
const bar = document.createElement(
|
|
208
|
-
bar.className =
|
|
216
|
+
const bar = document.createElement("div");
|
|
217
|
+
bar.className = "voice-bar";
|
|
209
218
|
bar.style.cssText = `
|
|
210
219
|
width: 3px;
|
|
211
220
|
height: 15px;
|
|
@@ -218,23 +227,23 @@ export function createConvaiWidget(container, options) {
|
|
|
218
227
|
}
|
|
219
228
|
overlay.appendChild(barsContainer);
|
|
220
229
|
// Status Text
|
|
221
|
-
const statusContainer = document.createElement(
|
|
222
|
-
const statusTitle = document.createElement(
|
|
223
|
-
statusTitle.id =
|
|
230
|
+
const statusContainer = document.createElement("div");
|
|
231
|
+
const statusTitle = document.createElement("div");
|
|
232
|
+
statusTitle.id = "voice-mode-title";
|
|
224
233
|
statusTitle.style.cssText = `
|
|
225
234
|
font-size: 14px;
|
|
226
235
|
font-weight: 500;
|
|
227
236
|
color: ${aeroTheme.colors.text.primary};
|
|
228
237
|
margin-bottom: 0.5rem;
|
|
229
238
|
`;
|
|
230
|
-
statusTitle.textContent =
|
|
231
|
-
const statusSubtitle = document.createElement(
|
|
232
|
-
statusSubtitle.id =
|
|
239
|
+
statusTitle.textContent = "Voice Only Mode";
|
|
240
|
+
const statusSubtitle = document.createElement("div");
|
|
241
|
+
statusSubtitle.id = "voice-mode-subtitle";
|
|
233
242
|
statusSubtitle.style.cssText = `
|
|
234
243
|
font-size: 12px;
|
|
235
244
|
color: ${aeroTheme.colors.text.secondary};
|
|
236
245
|
`;
|
|
237
|
-
statusSubtitle.textContent =
|
|
246
|
+
statusSubtitle.textContent = "Press and hold the microphone to talk";
|
|
238
247
|
statusContainer.appendChild(statusTitle);
|
|
239
248
|
statusContainer.appendChild(statusSubtitle);
|
|
240
249
|
overlay.appendChild(statusContainer);
|
|
@@ -249,7 +258,7 @@ export function createConvaiWidget(container, options) {
|
|
|
249
258
|
const updateAudioBars = () => {
|
|
250
259
|
if (!voiceModeOverlay)
|
|
251
260
|
return;
|
|
252
|
-
const bars = voiceModeOverlay.querySelectorAll(
|
|
261
|
+
const bars = voiceModeOverlay.querySelectorAll(".voice-bar");
|
|
253
262
|
const isTalking = convaiClient.state.isSpeaking;
|
|
254
263
|
const isListening = !convaiClient.audioControls.isAudioMuted;
|
|
255
264
|
const isAnimating = isListening || isTalking;
|
|
@@ -262,8 +271,8 @@ export function createConvaiWidget(container, options) {
|
|
|
262
271
|
: aeroTheme.colors.neutral[400];
|
|
263
272
|
});
|
|
264
273
|
// Update Text
|
|
265
|
-
const title = document.getElementById(
|
|
266
|
-
const subtitle = document.getElementById(
|
|
274
|
+
const title = document.getElementById("voice-mode-title");
|
|
275
|
+
const subtitle = document.getElementById("voice-mode-subtitle");
|
|
267
276
|
if (title) {
|
|
268
277
|
title.textContent = isTalking
|
|
269
278
|
? "Character Speaking..."
|
|
@@ -272,9 +281,10 @@ export function createConvaiWidget(container, options) {
|
|
|
272
281
|
: "Voice Only Mode";
|
|
273
282
|
}
|
|
274
283
|
if (subtitle) {
|
|
275
|
-
subtitle.textContent =
|
|
276
|
-
|
|
277
|
-
|
|
284
|
+
subtitle.textContent =
|
|
285
|
+
isListening || isTalking
|
|
286
|
+
? "Audio active"
|
|
287
|
+
: "Press and hold the microphone to talk";
|
|
278
288
|
}
|
|
279
289
|
// Animation Logic - Matches React version
|
|
280
290
|
if (isListening && analyzer && dataArray) {
|
|
@@ -307,8 +317,11 @@ export function createConvaiWidget(container, options) {
|
|
|
307
317
|
// Simulate speaking bars with natural speech patterns
|
|
308
318
|
const elapsed = (Date.now() - startTime) / 1000; // seconds
|
|
309
319
|
// Generate new random target levels occasionally (simulating syllables/words)
|
|
310
|
-
if (Math.random() < 0.08) {
|
|
311
|
-
|
|
320
|
+
if (Math.random() < 0.08) {
|
|
321
|
+
// 8% chance per frame = ~5 times per second
|
|
322
|
+
targetLevels = Array(40)
|
|
323
|
+
.fill(0)
|
|
324
|
+
.map((_, i) => {
|
|
312
325
|
// More variation in the middle bars, less on edges for natural spread
|
|
313
326
|
const position = i / 40;
|
|
314
327
|
const centerWeight = 1 - Math.abs(position - 0.5) * 0.5;
|
|
@@ -341,7 +354,7 @@ export function createConvaiWidget(container, options) {
|
|
|
341
354
|
else {
|
|
342
355
|
// Reset to idle state
|
|
343
356
|
bars.forEach((bar) => {
|
|
344
|
-
bar.style.height =
|
|
357
|
+
bar.style.height = "15px";
|
|
345
358
|
});
|
|
346
359
|
}
|
|
347
360
|
rafId = requestAnimationFrame(updateAudioBars);
|
|
@@ -349,9 +362,10 @@ export function createConvaiWidget(container, options) {
|
|
|
349
362
|
const startAudioAnalysis = async () => {
|
|
350
363
|
try {
|
|
351
364
|
if (!audioContext) {
|
|
352
|
-
audioContext = new (window.AudioContext ||
|
|
365
|
+
audioContext = new (window.AudioContext ||
|
|
366
|
+
window.webkitAudioContext)();
|
|
353
367
|
}
|
|
354
|
-
if (audioContext.state ===
|
|
368
|
+
if (audioContext.state === "suspended") {
|
|
355
369
|
await audioContext.resume();
|
|
356
370
|
}
|
|
357
371
|
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
@@ -372,7 +386,7 @@ export function createConvaiWidget(container, options) {
|
|
|
372
386
|
cancelAnimationFrame(rafId);
|
|
373
387
|
if (source) {
|
|
374
388
|
source.disconnect();
|
|
375
|
-
source.mediaStream.getTracks().forEach(t => t.stop());
|
|
389
|
+
source.mediaStream.getTracks().forEach((t) => t.stop());
|
|
376
390
|
source = null;
|
|
377
391
|
}
|
|
378
392
|
// Keep context alive if possible, or close it? React component closes it.
|
|
@@ -380,7 +394,7 @@ export function createConvaiWidget(container, options) {
|
|
|
380
394
|
};
|
|
381
395
|
// Create header
|
|
382
396
|
const createHeader = () => {
|
|
383
|
-
const header = document.createElement(
|
|
397
|
+
const header = document.createElement("div");
|
|
384
398
|
header.style.cssText = `
|
|
385
399
|
display: flex;
|
|
386
400
|
align-items: center;
|
|
@@ -388,16 +402,11 @@ export function createConvaiWidget(container, options) {
|
|
|
388
402
|
padding: 1rem;
|
|
389
403
|
border-bottom: 1px solid ${aeroTheme.colors.neutral[200]};
|
|
390
404
|
background: white;
|
|
405
|
+
position: relative;
|
|
391
406
|
`;
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
align-items: center;
|
|
396
|
-
gap: 0.5rem;
|
|
397
|
-
flex: 1;
|
|
398
|
-
`;
|
|
399
|
-
const closeButton = document.createElement('button');
|
|
400
|
-
const chevronIcon = Icons.ChevronDown('md');
|
|
407
|
+
// Close button on the left
|
|
408
|
+
const closeButton = document.createElement("button");
|
|
409
|
+
const chevronIcon = Icons.ChevronDown("md");
|
|
401
410
|
closeButton.appendChild(chevronIcon);
|
|
402
411
|
closeButton.style.cssText = `
|
|
403
412
|
font-size: 1.25rem;
|
|
@@ -405,24 +414,30 @@ export function createConvaiWidget(container, options) {
|
|
|
405
414
|
cursor: pointer;
|
|
406
415
|
padding: 0.25rem;
|
|
407
416
|
transition: ${aeroTheme.transitions.fast};
|
|
417
|
+
background: transparent;
|
|
418
|
+
border: none;
|
|
408
419
|
`;
|
|
409
|
-
closeButton.addEventListener(
|
|
420
|
+
closeButton.addEventListener("click", (e) => {
|
|
410
421
|
e.stopPropagation();
|
|
411
422
|
handleClose();
|
|
412
423
|
});
|
|
413
|
-
|
|
424
|
+
// Title section centered - matches React version
|
|
425
|
+
const titleSection = document.createElement("div");
|
|
414
426
|
titleSection.style.cssText = `
|
|
427
|
+
position: absolute;
|
|
428
|
+
left: 50%;
|
|
429
|
+
transform: translateX(-50%);
|
|
415
430
|
display: flex;
|
|
416
431
|
align-items: center;
|
|
417
432
|
gap: 0.5rem;
|
|
418
|
-
flex: 1;
|
|
419
433
|
font-size: ${aeroTheme.typography.fontSize.base};
|
|
420
434
|
font-weight: ${aeroTheme.typography.fontWeight.semibold};
|
|
421
435
|
color: ${aeroTheme.colors.text.primary};
|
|
422
436
|
`;
|
|
423
|
-
titleSection.id =
|
|
424
|
-
|
|
425
|
-
const
|
|
437
|
+
titleSection.id = "convai-widget-title";
|
|
438
|
+
// Settings button on the right
|
|
439
|
+
const settingsButton = document.createElement("button");
|
|
440
|
+
const moreIcon = Icons.MoreVertical("md");
|
|
426
441
|
settingsButton.appendChild(moreIcon);
|
|
427
442
|
settingsButton.style.cssText = `
|
|
428
443
|
font-size: 1.5rem;
|
|
@@ -430,25 +445,27 @@ export function createConvaiWidget(container, options) {
|
|
|
430
445
|
cursor: pointer;
|
|
431
446
|
padding: 0.25rem;
|
|
432
447
|
transition: ${aeroTheme.transitions.fast};
|
|
448
|
+
background: transparent;
|
|
449
|
+
border: none;
|
|
450
|
+
margin-left: auto;
|
|
433
451
|
`;
|
|
434
|
-
settingsButton.addEventListener(
|
|
452
|
+
settingsButton.addEventListener("click", (e) => {
|
|
435
453
|
e.stopPropagation();
|
|
436
454
|
handleSettingsToggle();
|
|
437
455
|
});
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
header.appendChild(leftSection);
|
|
456
|
+
header.appendChild(closeButton);
|
|
457
|
+
header.appendChild(titleSection);
|
|
441
458
|
header.appendChild(settingsButton);
|
|
442
459
|
return header;
|
|
443
460
|
};
|
|
444
461
|
// Update header with character info
|
|
445
462
|
const updateHeader = () => {
|
|
446
|
-
const titleSection = document.getElementById(
|
|
463
|
+
const titleSection = document.getElementById("convai-widget-title");
|
|
447
464
|
if (!titleSection)
|
|
448
465
|
return;
|
|
449
|
-
titleSection.innerHTML =
|
|
466
|
+
titleSection.innerHTML = "";
|
|
450
467
|
if (characterImage) {
|
|
451
|
-
const img = document.createElement(
|
|
468
|
+
const img = document.createElement("img");
|
|
452
469
|
img.src = characterImage;
|
|
453
470
|
img.alt = characterName;
|
|
454
471
|
img.style.cssText = `
|
|
@@ -462,14 +479,16 @@ export function createConvaiWidget(container, options) {
|
|
|
462
479
|
`;
|
|
463
480
|
titleSection.appendChild(img);
|
|
464
481
|
}
|
|
465
|
-
const nameSpan = document.createElement(
|
|
482
|
+
const nameSpan = document.createElement("span");
|
|
466
483
|
nameSpan.textContent = characterName;
|
|
467
484
|
titleSection.appendChild(nameSpan);
|
|
468
485
|
// Mute button
|
|
469
|
-
const muteButton = document.createElement(
|
|
470
|
-
muteButton.innerHTML =
|
|
471
|
-
const volumeIcon = isMuted
|
|
472
|
-
|
|
486
|
+
const muteButton = document.createElement("button");
|
|
487
|
+
muteButton.innerHTML = "";
|
|
488
|
+
const volumeIcon = isMuted
|
|
489
|
+
? Icons.VolumeMute("sm")
|
|
490
|
+
: Icons.VolumeHigh("sm");
|
|
491
|
+
volumeIcon.style.color = isMuted ? "#919EABA6" : "#0E7360";
|
|
473
492
|
muteButton.appendChild(volumeIcon);
|
|
474
493
|
muteButton.style.cssText = `
|
|
475
494
|
cursor: pointer;
|
|
@@ -485,21 +504,21 @@ export function createConvaiWidget(container, options) {
|
|
|
485
504
|
outline: none;
|
|
486
505
|
transition: transform 0.1s ease-out;
|
|
487
506
|
`;
|
|
488
|
-
muteButton.addEventListener(
|
|
507
|
+
muteButton.addEventListener("click", (e) => {
|
|
489
508
|
e.stopPropagation();
|
|
490
509
|
handleToggleMute();
|
|
491
510
|
});
|
|
492
|
-
muteButton.addEventListener(
|
|
493
|
-
muteButton.style.transform =
|
|
511
|
+
muteButton.addEventListener("mouseenter", () => {
|
|
512
|
+
muteButton.style.transform = "scale(1.1)";
|
|
494
513
|
});
|
|
495
|
-
muteButton.addEventListener(
|
|
496
|
-
muteButton.style.transform =
|
|
514
|
+
muteButton.addEventListener("mouseleave", () => {
|
|
515
|
+
muteButton.style.transform = "scale(1)";
|
|
497
516
|
});
|
|
498
517
|
titleSection.appendChild(muteButton);
|
|
499
518
|
// Voice Mode Badge
|
|
500
519
|
if (isVoiceMode) {
|
|
501
|
-
const voiceBadge = document.createElement(
|
|
502
|
-
voiceBadge.textContent =
|
|
520
|
+
const voiceBadge = document.createElement("span");
|
|
521
|
+
voiceBadge.textContent = "VOICE";
|
|
503
522
|
voiceBadge.style.cssText = `
|
|
504
523
|
font-size: 10px;
|
|
505
524
|
color: ${aeroTheme.colors.convai.light};
|
|
@@ -514,8 +533,8 @@ export function createConvaiWidget(container, options) {
|
|
|
514
533
|
};
|
|
515
534
|
// Create message list
|
|
516
535
|
const createMessageList = () => {
|
|
517
|
-
const list = document.createElement(
|
|
518
|
-
list.id =
|
|
536
|
+
const list = document.createElement("div");
|
|
537
|
+
list.id = "convai-message-list";
|
|
519
538
|
list.style.cssText = `
|
|
520
539
|
display: flex;
|
|
521
540
|
flex-direction: column;
|
|
@@ -526,7 +545,7 @@ export function createConvaiWidget(container, options) {
|
|
|
526
545
|
};
|
|
527
546
|
// Create footer
|
|
528
547
|
const createFooter = () => {
|
|
529
|
-
const footer = document.createElement(
|
|
548
|
+
const footer = document.createElement("div");
|
|
530
549
|
footer.style.cssText = `
|
|
531
550
|
padding: 1rem;
|
|
532
551
|
border-top: 1px solid ${aeroTheme.colors.neutral[200]};
|
|
@@ -537,9 +556,9 @@ export function createConvaiWidget(container, options) {
|
|
|
537
556
|
position: relative;
|
|
538
557
|
`;
|
|
539
558
|
// Voice Mode Exit Button (Initially hidden)
|
|
540
|
-
const voiceExitButton = document.createElement(
|
|
541
|
-
voiceExitButton.id =
|
|
542
|
-
const exitIcon = Icons.Waveform(
|
|
559
|
+
const voiceExitButton = document.createElement("button");
|
|
560
|
+
voiceExitButton.id = "convai-voice-exit-btn";
|
|
561
|
+
const exitIcon = Icons.Waveform("md");
|
|
543
562
|
voiceExitButton.appendChild(exitIcon);
|
|
544
563
|
voiceExitButton.style.cssText = `
|
|
545
564
|
width: 2.25rem;
|
|
@@ -554,14 +573,16 @@ export function createConvaiWidget(container, options) {
|
|
|
554
573
|
margin: 0 auto;
|
|
555
574
|
border: none;
|
|
556
575
|
`;
|
|
557
|
-
voiceExitButton.addEventListener(
|
|
576
|
+
voiceExitButton.addEventListener("click", async () => {
|
|
577
|
+
convaiClient.sendInterruptMessage();
|
|
578
|
+
await convaiClient.audioControls.muteAudio(); // Mute on exit
|
|
558
579
|
isVoiceMode = false;
|
|
559
580
|
updateVoiceMode();
|
|
560
581
|
});
|
|
561
582
|
footer.appendChild(voiceExitButton);
|
|
562
583
|
// Standard Footer Content (Mic + Input)
|
|
563
|
-
const standardContent = document.createElement(
|
|
564
|
-
standardContent.id =
|
|
584
|
+
const standardContent = document.createElement("div");
|
|
585
|
+
standardContent.id = "convai-footer-standard";
|
|
565
586
|
standardContent.style.cssText = `
|
|
566
587
|
display: flex;
|
|
567
588
|
gap: 0.5rem;
|
|
@@ -569,9 +590,9 @@ export function createConvaiWidget(container, options) {
|
|
|
569
590
|
width: 100%;
|
|
570
591
|
`;
|
|
571
592
|
// Mic button
|
|
572
|
-
const micButton = document.createElement(
|
|
573
|
-
micButton.id =
|
|
574
|
-
const micIcon = Icons.Mic(
|
|
593
|
+
const micButton = document.createElement("button");
|
|
594
|
+
micButton.id = "convai-mic-button";
|
|
595
|
+
const micIcon = Icons.Mic("md");
|
|
575
596
|
micButton.appendChild(micIcon);
|
|
576
597
|
const initialMicBackground = convaiClient.audioControls.isAudioMuted
|
|
577
598
|
? aeroTheme.colors.error[500]
|
|
@@ -586,23 +607,54 @@ export function createConvaiWidget(container, options) {
|
|
|
586
607
|
align-items: center;
|
|
587
608
|
justify-content: center;
|
|
588
609
|
cursor: pointer;
|
|
589
|
-
transition:
|
|
610
|
+
transition: background-color 0.2s ease-out, transform 0.15s ease-out, box-shadow 0.2s ease-out;
|
|
590
611
|
flex-shrink: 0;
|
|
591
612
|
border: none;
|
|
613
|
+
transform: scale(1);
|
|
614
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
592
615
|
`;
|
|
593
|
-
|
|
616
|
+
// Add hover and active effects
|
|
617
|
+
micButton.addEventListener("mouseenter", () => {
|
|
618
|
+
const isMuted = convaiClient.audioControls.isAudioMuted;
|
|
619
|
+
micButton.style.transform = "scale(1.05)";
|
|
620
|
+
if (isMuted) {
|
|
621
|
+
// Red button gets darker on hover
|
|
622
|
+
micButton.style.background = "#dc2626"; // Darker red
|
|
623
|
+
micButton.style.boxShadow = "0 4px 8px rgba(239, 68, 68, 0.3)";
|
|
624
|
+
}
|
|
625
|
+
else {
|
|
626
|
+
// Black button gets slightly lighter on hover
|
|
627
|
+
micButton.style.background = "#374151"; // Lighter dark
|
|
628
|
+
micButton.style.boxShadow = "0 4px 8px rgba(0, 0, 0, 0.2)";
|
|
629
|
+
}
|
|
630
|
+
});
|
|
631
|
+
micButton.addEventListener("mouseleave", () => {
|
|
632
|
+
const isMuted = convaiClient.audioControls.isAudioMuted;
|
|
633
|
+
micButton.style.transform = "scale(1)";
|
|
634
|
+
micButton.style.background = isMuted
|
|
635
|
+
? aeroTheme.colors.error[500]
|
|
636
|
+
: aeroTheme.colors.text.primary;
|
|
637
|
+
micButton.style.boxShadow = "0 2px 4px rgba(0, 0, 0, 0.1)";
|
|
638
|
+
});
|
|
639
|
+
micButton.addEventListener("mousedown", () => {
|
|
640
|
+
micButton.style.transform = "scale(0.95)";
|
|
641
|
+
});
|
|
642
|
+
micButton.addEventListener("mouseup", () => {
|
|
643
|
+
micButton.style.transform = "scale(1.05)";
|
|
644
|
+
});
|
|
645
|
+
micButton.addEventListener("click", handleMicToggle);
|
|
594
646
|
standardContent.appendChild(micButton);
|
|
595
647
|
// Input container
|
|
596
|
-
const inputContainer = document.createElement(
|
|
648
|
+
const inputContainer = document.createElement("div");
|
|
597
649
|
inputContainer.style.cssText = `
|
|
598
650
|
flex: 1;
|
|
599
651
|
position: relative;
|
|
600
652
|
display: flex;
|
|
601
653
|
align-items: center;
|
|
602
654
|
`;
|
|
603
|
-
inputElement = document.createElement(
|
|
604
|
-
inputElement.type =
|
|
605
|
-
inputElement.placeholder =
|
|
655
|
+
inputElement = document.createElement("input");
|
|
656
|
+
inputElement.type = "text";
|
|
657
|
+
inputElement.placeholder = "Conversation";
|
|
606
658
|
inputElement.style.cssText = `
|
|
607
659
|
width: 100%;
|
|
608
660
|
padding: 0.75rem 3rem 0.75rem 1rem;
|
|
@@ -614,18 +666,18 @@ export function createConvaiWidget(container, options) {
|
|
|
614
666
|
transition: ${aeroTheme.transitions.fast};
|
|
615
667
|
outline: none;
|
|
616
668
|
`;
|
|
617
|
-
inputElement.addEventListener(
|
|
669
|
+
inputElement.addEventListener("input", (e) => {
|
|
618
670
|
inputValue = e.target.value;
|
|
619
671
|
updateSendButton();
|
|
620
672
|
});
|
|
621
|
-
inputElement.addEventListener(
|
|
622
|
-
if (e.key ===
|
|
673
|
+
inputElement.addEventListener("keypress", (e) => {
|
|
674
|
+
if (e.key === "Enter")
|
|
623
675
|
handleSend();
|
|
624
676
|
});
|
|
625
677
|
// Send button
|
|
626
|
-
const sendButton = document.createElement(
|
|
627
|
-
sendButton.id =
|
|
628
|
-
const sendIcon = Icons.Send(
|
|
678
|
+
const sendButton = document.createElement("button");
|
|
679
|
+
sendButton.id = "convai-send-button";
|
|
680
|
+
const sendIcon = Icons.Send("md");
|
|
629
681
|
sendButton.appendChild(sendIcon);
|
|
630
682
|
sendButton.style.cssText = `
|
|
631
683
|
position: absolute;
|
|
@@ -642,12 +694,14 @@ export function createConvaiWidget(container, options) {
|
|
|
642
694
|
transition: ${aeroTheme.transitions.fast};
|
|
643
695
|
border: none;
|
|
644
696
|
`;
|
|
645
|
-
sendButton.addEventListener(
|
|
697
|
+
sendButton.addEventListener("click", async () => {
|
|
646
698
|
if (inputValue.length > 0) {
|
|
647
699
|
handleSend();
|
|
648
700
|
}
|
|
649
701
|
else {
|
|
650
702
|
// Toggle Voice Mode
|
|
703
|
+
convaiClient.sendInterruptMessage();
|
|
704
|
+
await convaiClient.audioControls.unmuteAudio(); // Unmute on enter
|
|
651
705
|
isVoiceMode = true;
|
|
652
706
|
updateVoiceMode();
|
|
653
707
|
}
|
|
@@ -660,8 +714,8 @@ export function createConvaiWidget(container, options) {
|
|
|
660
714
|
};
|
|
661
715
|
// Create settings tray - Matches React SettingsTray component exactly
|
|
662
716
|
const createSettingsTray = () => {
|
|
663
|
-
const tray = document.createElement(
|
|
664
|
-
tray.setAttribute(
|
|
717
|
+
const tray = document.createElement("div");
|
|
718
|
+
tray.setAttribute("data-settings-tray", "true");
|
|
665
719
|
tray.style.cssText = `
|
|
666
720
|
position: absolute;
|
|
667
721
|
top: 60px;
|
|
@@ -681,9 +735,9 @@ export function createConvaiWidget(container, options) {
|
|
|
681
735
|
overflow: hidden;
|
|
682
736
|
`;
|
|
683
737
|
// Add animation keyframes if not present
|
|
684
|
-
if (!document.getElementById(
|
|
685
|
-
const style = document.createElement(
|
|
686
|
-
style.id =
|
|
738
|
+
if (!document.getElementById("convai-widget-keyframes")) {
|
|
739
|
+
const style = document.createElement("style");
|
|
740
|
+
style.id = "convai-widget-keyframes";
|
|
687
741
|
style.textContent = `
|
|
688
742
|
@keyframes popIn {
|
|
689
743
|
from { opacity: 0; transform: scale(0.95) translateY(-10px); }
|
|
@@ -693,7 +747,7 @@ export function createConvaiWidget(container, options) {
|
|
|
693
747
|
document.head.appendChild(style);
|
|
694
748
|
}
|
|
695
749
|
// Horizontal Settings Row Container
|
|
696
|
-
const settingsRow = document.createElement(
|
|
750
|
+
const settingsRow = document.createElement("div");
|
|
697
751
|
settingsRow.style.cssText = `
|
|
698
752
|
display: flex;
|
|
699
753
|
flex-direction: row;
|
|
@@ -702,7 +756,7 @@ export function createConvaiWidget(container, options) {
|
|
|
702
756
|
gap: 4px;
|
|
703
757
|
`;
|
|
704
758
|
const createOption = (icon, label, onClick, isActive = false, isDestructive = false) => {
|
|
705
|
-
const btn = document.createElement(
|
|
759
|
+
const btn = document.createElement("div");
|
|
706
760
|
btn.style.cssText = `
|
|
707
761
|
display: flex;
|
|
708
762
|
flex-direction: column;
|
|
@@ -712,85 +766,184 @@ export function createConvaiWidget(container, options) {
|
|
|
712
766
|
cursor: pointer;
|
|
713
767
|
border-radius: ${aeroTheme.borderRadius.md};
|
|
714
768
|
background-color: ${isActive
|
|
715
|
-
?
|
|
769
|
+
? "rgba(16, 185, 129, 0.15)"
|
|
716
770
|
: isDestructive
|
|
717
|
-
?
|
|
718
|
-
:
|
|
771
|
+
? "rgba(239, 68, 68, 0.1)"
|
|
772
|
+
: "transparent"};
|
|
719
773
|
color: ${isActive
|
|
720
|
-
?
|
|
774
|
+
? "#10b981"
|
|
721
775
|
: isDestructive
|
|
722
|
-
?
|
|
776
|
+
? "#ef4444"
|
|
723
777
|
: aeroTheme.colors.text.primary};
|
|
724
778
|
transition: ${aeroTheme.transitions.fast};
|
|
725
779
|
min-width: 50px;
|
|
726
780
|
`;
|
|
727
781
|
// Icon container
|
|
728
|
-
const iconContainer = document.createElement(
|
|
782
|
+
const iconContainer = document.createElement("div");
|
|
729
783
|
iconContainer.appendChild(icon);
|
|
730
784
|
btn.appendChild(iconContainer);
|
|
731
|
-
const span = document.createElement(
|
|
785
|
+
const span = document.createElement("span");
|
|
732
786
|
span.textContent = label;
|
|
733
787
|
span.style.cssText = `
|
|
734
788
|
font-size: 10px;
|
|
735
789
|
font-weight: 500;
|
|
736
790
|
`;
|
|
737
791
|
btn.appendChild(span);
|
|
738
|
-
btn.addEventListener(
|
|
792
|
+
btn.addEventListener("click", (e) => {
|
|
739
793
|
e.stopPropagation();
|
|
740
794
|
onClick();
|
|
741
795
|
});
|
|
742
|
-
btn.addEventListener(
|
|
743
|
-
btn.style.transform =
|
|
796
|
+
btn.addEventListener("mouseover", () => {
|
|
797
|
+
btn.style.transform = "scale(1.02)";
|
|
744
798
|
if (!isActive && !isDestructive) {
|
|
745
799
|
btn.style.backgroundColor = aeroTheme.colors.neutral[100];
|
|
746
800
|
}
|
|
747
801
|
});
|
|
748
|
-
btn.addEventListener(
|
|
749
|
-
btn.style.transform =
|
|
802
|
+
btn.addEventListener("mouseout", () => {
|
|
803
|
+
btn.style.transform = "scale(1)";
|
|
750
804
|
btn.style.backgroundColor = isActive
|
|
751
|
-
?
|
|
805
|
+
? "rgba(16, 185, 129, 0.15)"
|
|
752
806
|
: isDestructive
|
|
753
|
-
?
|
|
754
|
-
:
|
|
807
|
+
? "rgba(239, 68, 68, 0.1)"
|
|
808
|
+
: "transparent";
|
|
755
809
|
});
|
|
756
810
|
return btn;
|
|
757
811
|
};
|
|
758
812
|
// Reset
|
|
759
|
-
const resetIcon = Icons.Redo(
|
|
760
|
-
resetIcon.style.width =
|
|
761
|
-
resetIcon.style.height =
|
|
762
|
-
settingsRow.appendChild(createOption(resetIcon,
|
|
813
|
+
const resetIcon = Icons.Redo("md");
|
|
814
|
+
resetIcon.style.width = "18px";
|
|
815
|
+
resetIcon.style.height = "18px";
|
|
816
|
+
settingsRow.appendChild(createOption(resetIcon, "Reset", handleReset));
|
|
763
817
|
// Video
|
|
764
|
-
if (convaiClient.connectionType ===
|
|
765
|
-
const videoIcon = isVideoVisible
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
818
|
+
if (convaiClient.connectionType === "video" && showVideo) {
|
|
819
|
+
const videoIcon = isVideoVisible
|
|
820
|
+
? Icons.Video("md")
|
|
821
|
+
: Icons.VideoOff("md");
|
|
822
|
+
videoIcon.style.width = "18px";
|
|
823
|
+
videoIcon.style.height = "18px";
|
|
824
|
+
const videoBtn = createOption(videoIcon, "Video", handleToggleVideo, isVideoVisible);
|
|
825
|
+
videoBtn.id = "convai-settings-video-btn";
|
|
770
826
|
settingsRow.appendChild(videoBtn);
|
|
771
827
|
}
|
|
772
828
|
// Screen Share
|
|
773
|
-
if (convaiClient.connectionType ===
|
|
829
|
+
if (convaiClient.connectionType === "video" && showScreenShare) {
|
|
774
830
|
const isSharing = convaiClient.screenShareControls.isScreenShareActive;
|
|
775
|
-
const shareIcon = isSharing
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
831
|
+
const shareIcon = isSharing
|
|
832
|
+
? Icons.StopScreenShare("md")
|
|
833
|
+
: Icons.ScreenShare("md");
|
|
834
|
+
shareIcon.style.width = "18px";
|
|
835
|
+
shareIcon.style.height = "18px";
|
|
836
|
+
const shareBtn = createOption(shareIcon, "Screen", handleToggleScreenShare, isSharing);
|
|
837
|
+
shareBtn.id = "convai-settings-share-btn";
|
|
780
838
|
settingsRow.appendChild(shareBtn);
|
|
781
839
|
}
|
|
782
840
|
// Disconnect
|
|
783
|
-
const disconnectIcon = document.createElement(
|
|
784
|
-
disconnectIcon.innerHTML =
|
|
785
|
-
disconnectIcon.style.fontSize =
|
|
786
|
-
settingsRow.appendChild(createOption(disconnectIcon,
|
|
841
|
+
const disconnectIcon = document.createElement("span");
|
|
842
|
+
disconnectIcon.innerHTML = "⏻";
|
|
843
|
+
disconnectIcon.style.fontSize = "18px";
|
|
844
|
+
settingsRow.appendChild(createOption(disconnectIcon, "Disconnect", handleDisconnect, false, true));
|
|
787
845
|
tray.appendChild(settingsRow);
|
|
788
846
|
return tray;
|
|
789
847
|
};
|
|
848
|
+
// Update settings tray content to reflect current connection state
|
|
849
|
+
const updateSettingsTray = () => {
|
|
850
|
+
const settingsRow = settingsTray.querySelector("div");
|
|
851
|
+
if (!settingsRow)
|
|
852
|
+
return;
|
|
853
|
+
// Clear existing content
|
|
854
|
+
settingsRow.innerHTML = "";
|
|
855
|
+
const createOption = (icon, label, onClick, isActive = false, isDestructive = false) => {
|
|
856
|
+
const btn = document.createElement("div");
|
|
857
|
+
btn.style.cssText = `
|
|
858
|
+
display: flex;
|
|
859
|
+
flex-direction: column;
|
|
860
|
+
align-items: center;
|
|
861
|
+
gap: 4px;
|
|
862
|
+
padding: 8px 12px;
|
|
863
|
+
cursor: pointer;
|
|
864
|
+
border-radius: ${aeroTheme.borderRadius.md};
|
|
865
|
+
background-color: ${isActive
|
|
866
|
+
? "rgba(16, 185, 129, 0.15)"
|
|
867
|
+
: isDestructive
|
|
868
|
+
? "rgba(239, 68, 68, 0.1)"
|
|
869
|
+
: "transparent"};
|
|
870
|
+
color: ${isActive
|
|
871
|
+
? "#10b981"
|
|
872
|
+
: isDestructive
|
|
873
|
+
? "#ef4444"
|
|
874
|
+
: aeroTheme.colors.text.primary};
|
|
875
|
+
transition: ${aeroTheme.transitions.fast};
|
|
876
|
+
min-width: 50px;
|
|
877
|
+
`;
|
|
878
|
+
// Icon container
|
|
879
|
+
const iconContainer = document.createElement("div");
|
|
880
|
+
iconContainer.appendChild(icon);
|
|
881
|
+
btn.appendChild(iconContainer);
|
|
882
|
+
const span = document.createElement("span");
|
|
883
|
+
span.textContent = label;
|
|
884
|
+
span.style.cssText = `
|
|
885
|
+
font-size: 10px;
|
|
886
|
+
font-weight: 500;
|
|
887
|
+
`;
|
|
888
|
+
btn.appendChild(span);
|
|
889
|
+
btn.addEventListener("click", (e) => {
|
|
890
|
+
e.stopPropagation();
|
|
891
|
+
onClick();
|
|
892
|
+
});
|
|
893
|
+
btn.addEventListener("mouseover", () => {
|
|
894
|
+
btn.style.transform = "scale(1.02)";
|
|
895
|
+
if (!isActive && !isDestructive) {
|
|
896
|
+
btn.style.backgroundColor = aeroTheme.colors.neutral[100];
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
btn.addEventListener("mouseout", () => {
|
|
900
|
+
btn.style.transform = "scale(1)";
|
|
901
|
+
btn.style.backgroundColor = isActive
|
|
902
|
+
? "rgba(16, 185, 129, 0.15)"
|
|
903
|
+
: isDestructive
|
|
904
|
+
? "rgba(239, 68, 68, 0.1)"
|
|
905
|
+
: "transparent";
|
|
906
|
+
});
|
|
907
|
+
return btn;
|
|
908
|
+
};
|
|
909
|
+
// Reset
|
|
910
|
+
const resetIcon = Icons.Redo("md");
|
|
911
|
+
resetIcon.style.width = "18px";
|
|
912
|
+
resetIcon.style.height = "18px";
|
|
913
|
+
settingsRow.appendChild(createOption(resetIcon, "Reset", handleReset));
|
|
914
|
+
// Video - Always show if showVideo is true and connection type is video
|
|
915
|
+
if (convaiClient.connectionType === "video" && showVideo) {
|
|
916
|
+
const videoIcon = isVideoVisible
|
|
917
|
+
? Icons.Video("md")
|
|
918
|
+
: Icons.VideoOff("md");
|
|
919
|
+
videoIcon.style.width = "18px";
|
|
920
|
+
videoIcon.style.height = "18px";
|
|
921
|
+
const videoBtn = createOption(videoIcon, "Video", handleToggleVideo, isVideoVisible);
|
|
922
|
+
videoBtn.id = "convai-settings-video-btn";
|
|
923
|
+
settingsRow.appendChild(videoBtn);
|
|
924
|
+
}
|
|
925
|
+
// Screen Share - Always show if showScreenShare is true and connection type is video
|
|
926
|
+
if (convaiClient.connectionType === "video" && showScreenShare) {
|
|
927
|
+
const isSharing = convaiClient.screenShareControls.isScreenShareActive;
|
|
928
|
+
const shareIcon = isSharing
|
|
929
|
+
? Icons.StopScreenShare("md")
|
|
930
|
+
: Icons.ScreenShare("md");
|
|
931
|
+
shareIcon.style.width = "18px";
|
|
932
|
+
shareIcon.style.height = "18px";
|
|
933
|
+
const shareBtn = createOption(shareIcon, "Screen", handleToggleScreenShare, isSharing);
|
|
934
|
+
shareBtn.id = "convai-settings-share-btn";
|
|
935
|
+
settingsRow.appendChild(shareBtn);
|
|
936
|
+
}
|
|
937
|
+
// Disconnect
|
|
938
|
+
const disconnectIcon = document.createElement("span");
|
|
939
|
+
disconnectIcon.innerHTML = "⏻";
|
|
940
|
+
disconnectIcon.style.fontSize = "18px";
|
|
941
|
+
settingsRow.appendChild(createOption(disconnectIcon, "Disconnect", handleDisconnect, false, true));
|
|
942
|
+
};
|
|
790
943
|
// Create floating video - Matches React FloatingVideo component
|
|
791
944
|
const createFloatingVideo = () => {
|
|
792
|
-
const container = document.createElement(
|
|
793
|
-
container.id =
|
|
945
|
+
const container = document.createElement("div");
|
|
946
|
+
container.id = "floating-video-container";
|
|
794
947
|
container.style.cssText = `
|
|
795
948
|
position: fixed;
|
|
796
949
|
left: 20px;
|
|
@@ -804,7 +957,7 @@ export function createConvaiWidget(container, options) {
|
|
|
804
957
|
display: none;
|
|
805
958
|
transition: opacity 0.4s cubic-bezier(0.4, 0, 0.2, 1), transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
806
959
|
`;
|
|
807
|
-
const wrapper = document.createElement(
|
|
960
|
+
const wrapper = document.createElement("div");
|
|
808
961
|
wrapper.style.cssText = `
|
|
809
962
|
background: rgba(15, 23, 42, 0.95);
|
|
810
963
|
backdrop-filter: blur(20px);
|
|
@@ -813,7 +966,7 @@ export function createConvaiWidget(container, options) {
|
|
|
813
966
|
overflow: hidden;
|
|
814
967
|
`;
|
|
815
968
|
// Header
|
|
816
|
-
const header = document.createElement(
|
|
969
|
+
const header = document.createElement("div");
|
|
817
970
|
header.style.cssText = `
|
|
818
971
|
display: flex;
|
|
819
972
|
align-items: center;
|
|
@@ -822,7 +975,7 @@ export function createConvaiWidget(container, options) {
|
|
|
822
975
|
background: rgba(0, 0, 0, 0.3);
|
|
823
976
|
border-bottom: 1px solid ${aeroTheme.colors.neutral[700]};
|
|
824
977
|
`;
|
|
825
|
-
const headerLeft = document.createElement(
|
|
978
|
+
const headerLeft = document.createElement("div");
|
|
826
979
|
headerLeft.style.cssText = `
|
|
827
980
|
display: flex;
|
|
828
981
|
align-items: center;
|
|
@@ -831,18 +984,18 @@ export function createConvaiWidget(container, options) {
|
|
|
831
984
|
font-size: 12px;
|
|
832
985
|
font-weight: 500;
|
|
833
986
|
`;
|
|
834
|
-
const dragIcon = Icons.DragIndicator(
|
|
835
|
-
dragIcon.style.width =
|
|
836
|
-
dragIcon.style.height =
|
|
987
|
+
const dragIcon = Icons.DragIndicator("sm");
|
|
988
|
+
dragIcon.style.width = "16px";
|
|
989
|
+
dragIcon.style.height = "16px";
|
|
837
990
|
headerLeft.appendChild(dragIcon);
|
|
838
|
-
const label = document.createElement(
|
|
839
|
-
label.textContent =
|
|
991
|
+
const label = document.createElement("span");
|
|
992
|
+
label.textContent = "Your Camera";
|
|
840
993
|
headerLeft.appendChild(label);
|
|
841
|
-
const closeButton = document.createElement(
|
|
842
|
-
closeButton.innerHTML =
|
|
843
|
-
const closeIcon = Icons.Close(
|
|
844
|
-
closeIcon.style.width =
|
|
845
|
-
closeIcon.style.height =
|
|
994
|
+
const closeButton = document.createElement("button");
|
|
995
|
+
closeButton.innerHTML = "";
|
|
996
|
+
const closeIcon = Icons.Close("md");
|
|
997
|
+
closeIcon.style.width = "20px";
|
|
998
|
+
closeIcon.style.height = "20px";
|
|
846
999
|
closeButton.appendChild(closeIcon);
|
|
847
1000
|
closeButton.style.cssText = `
|
|
848
1001
|
background: transparent;
|
|
@@ -855,28 +1008,28 @@ export function createConvaiWidget(container, options) {
|
|
|
855
1008
|
padding: 4px;
|
|
856
1009
|
transition: transform 0.1s ease-out;
|
|
857
1010
|
`;
|
|
858
|
-
closeButton.addEventListener(
|
|
1011
|
+
closeButton.addEventListener("click", (e) => {
|
|
859
1012
|
e.stopPropagation();
|
|
860
1013
|
convaiClient.videoControls.disableVideo();
|
|
861
1014
|
});
|
|
862
|
-
closeButton.addEventListener(
|
|
863
|
-
closeButton.style.transform =
|
|
1015
|
+
closeButton.addEventListener("mouseenter", () => {
|
|
1016
|
+
closeButton.style.transform = "scale(1.1)";
|
|
864
1017
|
});
|
|
865
|
-
closeButton.addEventListener(
|
|
866
|
-
closeButton.style.transform =
|
|
1018
|
+
closeButton.addEventListener("mouseleave", () => {
|
|
1019
|
+
closeButton.style.transform = "scale(1)";
|
|
867
1020
|
});
|
|
868
1021
|
header.appendChild(headerLeft);
|
|
869
1022
|
header.appendChild(closeButton);
|
|
870
1023
|
// Video Container
|
|
871
|
-
const videoContainer = document.createElement(
|
|
1024
|
+
const videoContainer = document.createElement("div");
|
|
872
1025
|
videoContainer.style.cssText = `
|
|
873
1026
|
position: relative;
|
|
874
1027
|
width: 100%;
|
|
875
1028
|
padding-bottom: 75%;
|
|
876
1029
|
background: #000;
|
|
877
1030
|
`;
|
|
878
|
-
const videoWrapper = document.createElement(
|
|
879
|
-
videoWrapper.id =
|
|
1031
|
+
const videoWrapper = document.createElement("div");
|
|
1032
|
+
videoWrapper.id = "floating-video-wrapper";
|
|
880
1033
|
videoWrapper.style.cssText = `
|
|
881
1034
|
position: absolute;
|
|
882
1035
|
top: 0;
|
|
@@ -884,8 +1037,8 @@ export function createConvaiWidget(container, options) {
|
|
|
884
1037
|
width: 100%;
|
|
885
1038
|
height: 100%;
|
|
886
1039
|
`;
|
|
887
|
-
const videoElement = document.createElement(
|
|
888
|
-
videoElement.id =
|
|
1040
|
+
const videoElement = document.createElement("video");
|
|
1041
|
+
videoElement.id = "floating-video-element";
|
|
889
1042
|
videoElement.autoplay = true;
|
|
890
1043
|
videoElement.playsInline = true;
|
|
891
1044
|
videoElement.muted = true;
|
|
@@ -896,8 +1049,8 @@ export function createConvaiWidget(container, options) {
|
|
|
896
1049
|
transform: scaleX(-1);
|
|
897
1050
|
`;
|
|
898
1051
|
// Camera off placeholder
|
|
899
|
-
const cameraOffPlaceholder = document.createElement(
|
|
900
|
-
cameraOffPlaceholder.id =
|
|
1052
|
+
const cameraOffPlaceholder = document.createElement("div");
|
|
1053
|
+
cameraOffPlaceholder.id = "camera-off-placeholder";
|
|
901
1054
|
cameraOffPlaceholder.style.cssText = `
|
|
902
1055
|
width: 100%;
|
|
903
1056
|
height: 100%;
|
|
@@ -909,12 +1062,12 @@ export function createConvaiWidget(container, options) {
|
|
|
909
1062
|
gap: 12px;
|
|
910
1063
|
background: #000;
|
|
911
1064
|
`;
|
|
912
|
-
const cameraOffIcon = Icons.VideoOff(
|
|
913
|
-
cameraOffIcon.style.width =
|
|
914
|
-
cameraOffIcon.style.height =
|
|
1065
|
+
const cameraOffIcon = Icons.VideoOff("lg");
|
|
1066
|
+
cameraOffIcon.style.width = "48px";
|
|
1067
|
+
cameraOffIcon.style.height = "48px";
|
|
915
1068
|
cameraOffPlaceholder.appendChild(cameraOffIcon);
|
|
916
|
-
const cameraOffText = document.createElement(
|
|
917
|
-
cameraOffText.textContent =
|
|
1069
|
+
const cameraOffText = document.createElement("span");
|
|
1070
|
+
cameraOffText.textContent = "Camera is off";
|
|
918
1071
|
cameraOffText.style.cssText = `
|
|
919
1072
|
font-size: 14px;
|
|
920
1073
|
text-align: center;
|
|
@@ -933,7 +1086,7 @@ export function createConvaiWidget(container, options) {
|
|
|
933
1086
|
let startLeft = 20;
|
|
934
1087
|
let startTop = 20;
|
|
935
1088
|
const handleMouseDown = (e) => {
|
|
936
|
-
if (e.target.closest(
|
|
1089
|
+
if (e.target.closest("button"))
|
|
937
1090
|
return;
|
|
938
1091
|
isDragging = true;
|
|
939
1092
|
startX = e.clientX;
|
|
@@ -941,7 +1094,7 @@ export function createConvaiWidget(container, options) {
|
|
|
941
1094
|
const style = window.getComputedStyle(container);
|
|
942
1095
|
startLeft = parseInt(style.left);
|
|
943
1096
|
startTop = parseInt(style.top);
|
|
944
|
-
container.style.cursor =
|
|
1097
|
+
container.style.cursor = "grabbing";
|
|
945
1098
|
e.preventDefault();
|
|
946
1099
|
};
|
|
947
1100
|
const handleMouseMove = (e) => {
|
|
@@ -962,12 +1115,12 @@ export function createConvaiWidget(container, options) {
|
|
|
962
1115
|
const handleMouseUp = () => {
|
|
963
1116
|
if (isDragging) {
|
|
964
1117
|
isDragging = false;
|
|
965
|
-
container.style.cursor =
|
|
1118
|
+
container.style.cursor = "grab";
|
|
966
1119
|
}
|
|
967
1120
|
};
|
|
968
|
-
container.addEventListener(
|
|
969
|
-
document.addEventListener(
|
|
970
|
-
document.addEventListener(
|
|
1121
|
+
container.addEventListener("mousedown", handleMouseDown);
|
|
1122
|
+
document.addEventListener("mousemove", handleMouseMove);
|
|
1123
|
+
document.addEventListener("mouseup", handleMouseUp);
|
|
971
1124
|
return container;
|
|
972
1125
|
};
|
|
973
1126
|
// Event handlers
|
|
@@ -983,7 +1136,7 @@ export function createConvaiWidget(container, options) {
|
|
|
983
1136
|
setIsOpen(true);
|
|
984
1137
|
}
|
|
985
1138
|
catch (error) {
|
|
986
|
-
console.error(
|
|
1139
|
+
console.error("Failed to connect:", error);
|
|
987
1140
|
}
|
|
988
1141
|
}
|
|
989
1142
|
else {
|
|
@@ -997,8 +1150,8 @@ export function createConvaiWidget(container, options) {
|
|
|
997
1150
|
const handleSend = () => {
|
|
998
1151
|
if (inputValue.trim() && convaiClient.state.isConnected) {
|
|
999
1152
|
convaiClient.sendUserTextMessage(inputValue);
|
|
1000
|
-
inputValue =
|
|
1001
|
-
inputElement.value =
|
|
1153
|
+
inputValue = "";
|
|
1154
|
+
inputElement.value = "";
|
|
1002
1155
|
updateSendButton();
|
|
1003
1156
|
}
|
|
1004
1157
|
};
|
|
@@ -1033,7 +1186,7 @@ export function createConvaiWidget(container, options) {
|
|
|
1033
1186
|
updateSendButton();
|
|
1034
1187
|
}
|
|
1035
1188
|
catch (error) {
|
|
1036
|
-
console.error(
|
|
1189
|
+
console.error("Failed to reset:", error);
|
|
1037
1190
|
}
|
|
1038
1191
|
};
|
|
1039
1192
|
const handleDisconnect = async () => {
|
|
@@ -1047,163 +1200,137 @@ export function createConvaiWidget(container, options) {
|
|
|
1047
1200
|
updateSendButton();
|
|
1048
1201
|
};
|
|
1049
1202
|
const handleToggleVideo = async () => {
|
|
1050
|
-
if (convaiClient.connectionType !==
|
|
1203
|
+
if (convaiClient.connectionType !== "video")
|
|
1051
1204
|
return;
|
|
1052
1205
|
try {
|
|
1053
|
-
if (
|
|
1206
|
+
if (isVideoVisible) {
|
|
1054
1207
|
await convaiClient.videoControls.disableVideo();
|
|
1055
1208
|
}
|
|
1056
1209
|
else {
|
|
1057
1210
|
await convaiClient.videoControls.enableVideo();
|
|
1058
1211
|
}
|
|
1059
|
-
|
|
1060
|
-
updateVoiceMode(); // Updates UI based on video state
|
|
1061
|
-
// Update tray button state
|
|
1062
|
-
const videoBtn = document.getElementById('convai-settings-video-btn');
|
|
1063
|
-
if (videoBtn) {
|
|
1064
|
-
const isActive = isVideoVisible;
|
|
1065
|
-
videoBtn.style.backgroundColor = isActive ? 'rgba(16, 185, 129, 0.15)' : 'transparent';
|
|
1066
|
-
videoBtn.style.color = isActive ? '#10b981' : aeroTheme.colors.text.primary;
|
|
1067
|
-
// Update Icon - first child is icon container
|
|
1068
|
-
const iconContainer = videoBtn.querySelector('div');
|
|
1069
|
-
if (iconContainer) {
|
|
1070
|
-
iconContainer.innerHTML = '';
|
|
1071
|
-
const icon = isActive ? Icons.Video('md') : Icons.VideoOff('md');
|
|
1072
|
-
icon.style.width = '18px';
|
|
1073
|
-
icon.style.height = '18px';
|
|
1074
|
-
iconContainer.appendChild(icon);
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1212
|
+
// State will be updated via event listener below
|
|
1077
1213
|
}
|
|
1078
1214
|
catch (e) {
|
|
1079
|
-
console.error("
|
|
1215
|
+
console.error("Failed to toggle video:", e);
|
|
1080
1216
|
}
|
|
1081
1217
|
};
|
|
1082
1218
|
const handleToggleScreenShare = async () => {
|
|
1083
1219
|
try {
|
|
1084
1220
|
await convaiClient.screenShareControls.toggleScreenShare();
|
|
1085
|
-
|
|
1086
|
-
const shareBtn = document.getElementById('convai-settings-share-btn');
|
|
1087
|
-
if (shareBtn) {
|
|
1088
|
-
const isActive = isSharing;
|
|
1089
|
-
shareBtn.style.backgroundColor = isActive ? 'rgba(16, 185, 129, 0.15)' : 'transparent';
|
|
1090
|
-
shareBtn.style.color = isActive ? '#10b981' : aeroTheme.colors.text.primary;
|
|
1091
|
-
// Update Icon - first child is icon container
|
|
1092
|
-
const iconContainer = shareBtn.querySelector('div');
|
|
1093
|
-
if (iconContainer) {
|
|
1094
|
-
iconContainer.innerHTML = '';
|
|
1095
|
-
const icon = isActive ? Icons.StopScreenShare('md') : Icons.ScreenShare('md');
|
|
1096
|
-
icon.style.width = '18px';
|
|
1097
|
-
icon.style.height = '18px';
|
|
1098
|
-
iconContainer.appendChild(icon);
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1221
|
+
// State will be updated via event listener above
|
|
1101
1222
|
}
|
|
1102
1223
|
catch (e) {
|
|
1103
|
-
console.error("
|
|
1224
|
+
console.error("Failed to toggle screen share:", e);
|
|
1104
1225
|
}
|
|
1105
1226
|
};
|
|
1106
1227
|
const handleSettingsToggle = () => {
|
|
1228
|
+
// Regenerate settings tray content BEFORE opening to reflect current connection type
|
|
1229
|
+
if (!isSettingsOpen) {
|
|
1230
|
+
updateSettingsTray();
|
|
1231
|
+
}
|
|
1107
1232
|
setIsSettingsOpen(!isSettingsOpen);
|
|
1108
1233
|
};
|
|
1109
1234
|
// Update functions
|
|
1110
1235
|
const setIsOpen = (open) => {
|
|
1111
1236
|
isOpen = open;
|
|
1112
1237
|
if (open) {
|
|
1113
|
-
morphingContainer.style.width =
|
|
1114
|
-
morphingContainer.style.height =
|
|
1238
|
+
morphingContainer.style.width = "400px";
|
|
1239
|
+
morphingContainer.style.height = "600px";
|
|
1115
1240
|
morphingContainer.style.borderRadius = aeroTheme.borderRadius.xl;
|
|
1116
|
-
morphingContainer.style.cursor =
|
|
1117
|
-
buttonContent.style.opacity =
|
|
1118
|
-
buttonContent.style.transform =
|
|
1119
|
-
buttonContent.style.pointerEvents =
|
|
1120
|
-
chatContent.style.opacity =
|
|
1121
|
-
chatContent.style.transform =
|
|
1122
|
-
chatContent.style.pointerEvents =
|
|
1241
|
+
morphingContainer.style.cursor = "default";
|
|
1242
|
+
buttonContent.style.opacity = "0";
|
|
1243
|
+
buttonContent.style.transform = "scale(0.8)";
|
|
1244
|
+
buttonContent.style.pointerEvents = "none";
|
|
1245
|
+
chatContent.style.opacity = "1";
|
|
1246
|
+
chatContent.style.transform = "scale(1)";
|
|
1247
|
+
chatContent.style.pointerEvents = "auto";
|
|
1123
1248
|
}
|
|
1124
1249
|
else {
|
|
1125
|
-
morphingContainer.style.width =
|
|
1126
|
-
morphingContainer.style.height =
|
|
1127
|
-
morphingContainer.style.borderRadius =
|
|
1128
|
-
morphingContainer.style.cursor =
|
|
1129
|
-
buttonContent.style.opacity =
|
|
1130
|
-
buttonContent.style.transform =
|
|
1131
|
-
buttonContent.style.pointerEvents =
|
|
1132
|
-
chatContent.style.opacity =
|
|
1133
|
-
chatContent.style.transform =
|
|
1134
|
-
chatContent.style.pointerEvents =
|
|
1250
|
+
morphingContainer.style.width = "4rem";
|
|
1251
|
+
morphingContainer.style.height = "4rem";
|
|
1252
|
+
morphingContainer.style.borderRadius = "50%";
|
|
1253
|
+
morphingContainer.style.cursor = "pointer";
|
|
1254
|
+
buttonContent.style.opacity = "1";
|
|
1255
|
+
buttonContent.style.transform = "scale(1)";
|
|
1256
|
+
buttonContent.style.pointerEvents = "auto";
|
|
1257
|
+
chatContent.style.opacity = "0";
|
|
1258
|
+
chatContent.style.transform = "scale(0.8)";
|
|
1259
|
+
chatContent.style.pointerEvents = "none";
|
|
1135
1260
|
}
|
|
1136
1261
|
};
|
|
1137
1262
|
const setIsSettingsOpen = (open) => {
|
|
1138
1263
|
isSettingsOpen = open;
|
|
1139
|
-
settingsTray.style.display = open ?
|
|
1264
|
+
settingsTray.style.display = open ? "flex" : "none";
|
|
1140
1265
|
};
|
|
1141
1266
|
const updateSendButton = () => {
|
|
1142
|
-
const sendButton = document.getElementById(
|
|
1267
|
+
const sendButton = document.getElementById("convai-send-button");
|
|
1143
1268
|
if (!sendButton)
|
|
1144
1269
|
return;
|
|
1145
1270
|
// Clear previous content
|
|
1146
|
-
sendButton.innerHTML =
|
|
1271
|
+
sendButton.innerHTML = "";
|
|
1147
1272
|
if (inputValue.length > 0) {
|
|
1148
1273
|
// Show Send Button
|
|
1149
|
-
sendButton.appendChild(Icons.Send(
|
|
1274
|
+
sendButton.appendChild(Icons.Send("md"));
|
|
1150
1275
|
sendButton.style.background = aeroTheme.colors.text.primary;
|
|
1151
|
-
sendButton.style.color =
|
|
1152
|
-
sendButton.style.border =
|
|
1276
|
+
sendButton.style.color = "white";
|
|
1277
|
+
sendButton.style.border = "none";
|
|
1153
1278
|
sendButton.title = "Send";
|
|
1154
1279
|
}
|
|
1155
1280
|
else {
|
|
1156
1281
|
// Show Voice Mode Toggle
|
|
1157
|
-
sendButton.appendChild(Icons.Waveform(
|
|
1158
|
-
sendButton.style.background =
|
|
1282
|
+
sendButton.appendChild(Icons.Waveform("md"));
|
|
1283
|
+
sendButton.style.background = "transparent";
|
|
1159
1284
|
sendButton.style.color = aeroTheme.colors.text.primary;
|
|
1160
1285
|
sendButton.style.border = `1px solid ${aeroTheme.colors.neutral[300]}`;
|
|
1161
1286
|
sendButton.title = "Voice Mode";
|
|
1162
1287
|
}
|
|
1163
1288
|
};
|
|
1164
1289
|
const updateVideoTrack = () => {
|
|
1165
|
-
if (!floatingVideo ||
|
|
1290
|
+
if (!floatingVideo ||
|
|
1291
|
+
!convaiClient.room ||
|
|
1292
|
+
!convaiClient.room.localParticipant)
|
|
1166
1293
|
return;
|
|
1167
|
-
const videoEl = floatingVideo.querySelector(
|
|
1168
|
-
const placeholder = floatingVideo.querySelector(
|
|
1294
|
+
const videoEl = floatingVideo.querySelector("#floating-video-element");
|
|
1295
|
+
const placeholder = floatingVideo.querySelector("#camera-off-placeholder");
|
|
1169
1296
|
if (!videoEl || !placeholder)
|
|
1170
1297
|
return;
|
|
1171
1298
|
if (isVideoVisible && !isVoiceMode) {
|
|
1172
1299
|
const tracks = Array.from(convaiClient.room.localParticipant.videoTrackPublications.values());
|
|
1173
|
-
const videoPub = tracks.find((t) => t.kind ===
|
|
1300
|
+
const videoPub = tracks.find((t) => t.kind === "video");
|
|
1174
1301
|
if (videoPub && videoPub.track) {
|
|
1175
1302
|
videoPub.track.attach(videoEl);
|
|
1176
|
-
videoEl.style.display =
|
|
1177
|
-
placeholder.style.display =
|
|
1303
|
+
videoEl.style.display = "block";
|
|
1304
|
+
placeholder.style.display = "none";
|
|
1178
1305
|
}
|
|
1179
1306
|
else {
|
|
1180
|
-
videoEl.style.display =
|
|
1181
|
-
placeholder.style.display =
|
|
1307
|
+
videoEl.style.display = "none";
|
|
1308
|
+
placeholder.style.display = "flex";
|
|
1182
1309
|
}
|
|
1183
1310
|
}
|
|
1184
1311
|
else {
|
|
1185
|
-
videoEl.style.display =
|
|
1186
|
-
placeholder.style.display =
|
|
1312
|
+
videoEl.style.display = "none";
|
|
1313
|
+
placeholder.style.display = "flex";
|
|
1187
1314
|
}
|
|
1188
1315
|
};
|
|
1189
1316
|
const updateVoiceMode = () => {
|
|
1190
|
-
const standardFooter = document.getElementById(
|
|
1191
|
-
const voiceExitBtn = document.getElementById(
|
|
1317
|
+
const standardFooter = document.getElementById("convai-footer-standard");
|
|
1318
|
+
const voiceExitBtn = document.getElementById("convai-voice-exit-btn");
|
|
1192
1319
|
if (isVoiceMode) {
|
|
1193
1320
|
// Show Voice Overlay
|
|
1194
1321
|
if (voiceModeOverlay)
|
|
1195
|
-
voiceModeOverlay.style.display =
|
|
1322
|
+
voiceModeOverlay.style.display = "flex";
|
|
1196
1323
|
if (messageListElement)
|
|
1197
|
-
messageListElement.style.display =
|
|
1324
|
+
messageListElement.style.display = "none";
|
|
1198
1325
|
if (floatingVideo)
|
|
1199
|
-
floatingVideo.style.display =
|
|
1326
|
+
floatingVideo.style.display = "none";
|
|
1200
1327
|
// Footer: Show Exit Button, Hide Standard
|
|
1201
1328
|
if (standardFooter)
|
|
1202
|
-
standardFooter.style.display =
|
|
1329
|
+
standardFooter.style.display = "none";
|
|
1203
1330
|
if (voiceExitBtn)
|
|
1204
|
-
voiceExitBtn.style.display =
|
|
1331
|
+
voiceExitBtn.style.display = "flex";
|
|
1205
1332
|
if (footerElement)
|
|
1206
|
-
footerElement.style.justifyContent =
|
|
1333
|
+
footerElement.style.justifyContent = "center";
|
|
1207
1334
|
// Update header to show VOICE badge
|
|
1208
1335
|
updateHeader();
|
|
1209
1336
|
// Reset animation state for voice mode
|
|
@@ -1216,28 +1343,28 @@ export function createConvaiWidget(container, options) {
|
|
|
1216
1343
|
else {
|
|
1217
1344
|
// Hide Overlay
|
|
1218
1345
|
if (voiceModeOverlay)
|
|
1219
|
-
voiceModeOverlay.style.display =
|
|
1346
|
+
voiceModeOverlay.style.display = "none";
|
|
1220
1347
|
// Show Video or Message List
|
|
1221
1348
|
if (isVideoVisible) {
|
|
1222
1349
|
if (messageListElement)
|
|
1223
|
-
messageListElement.style.display =
|
|
1350
|
+
messageListElement.style.display = "none";
|
|
1224
1351
|
if (floatingVideo)
|
|
1225
|
-
floatingVideo.style.display =
|
|
1352
|
+
floatingVideo.style.display = "block";
|
|
1226
1353
|
updateVideoTrack();
|
|
1227
1354
|
}
|
|
1228
1355
|
else {
|
|
1229
1356
|
if (messageListElement)
|
|
1230
|
-
messageListElement.style.display =
|
|
1357
|
+
messageListElement.style.display = "flex";
|
|
1231
1358
|
if (floatingVideo)
|
|
1232
|
-
floatingVideo.style.display =
|
|
1359
|
+
floatingVideo.style.display = "none";
|
|
1233
1360
|
}
|
|
1234
1361
|
// Footer: Show Standard, Hide Exit
|
|
1235
1362
|
if (standardFooter)
|
|
1236
|
-
standardFooter.style.display =
|
|
1363
|
+
standardFooter.style.display = "flex";
|
|
1237
1364
|
if (voiceExitBtn)
|
|
1238
|
-
voiceExitBtn.style.display =
|
|
1365
|
+
voiceExitBtn.style.display = "none";
|
|
1239
1366
|
if (footerElement)
|
|
1240
|
-
footerElement.style.justifyContent =
|
|
1367
|
+
footerElement.style.justifyContent = "flex-start";
|
|
1241
1368
|
// Update header to hide VOICE badge
|
|
1242
1369
|
updateHeader();
|
|
1243
1370
|
// Stop Audio Analysis
|
|
@@ -1245,12 +1372,32 @@ export function createConvaiWidget(container, options) {
|
|
|
1245
1372
|
}
|
|
1246
1373
|
};
|
|
1247
1374
|
const updateMicButton = () => {
|
|
1248
|
-
// Update mic button appearance based on mute state
|
|
1249
|
-
const micButton = document.getElementById(
|
|
1375
|
+
// Update mic button appearance based on mute state with visual feedback
|
|
1376
|
+
const micButton = document.getElementById("convai-mic-button");
|
|
1250
1377
|
if (micButton) {
|
|
1251
|
-
|
|
1378
|
+
const isMuted = convaiClient.audioControls.isAudioMuted;
|
|
1379
|
+
const newBackground = isMuted
|
|
1252
1380
|
? aeroTheme.colors.error[500]
|
|
1253
1381
|
: aeroTheme.colors.text.primary;
|
|
1382
|
+
// Add a brief pulse effect to show the state changed
|
|
1383
|
+
micButton.style.transform = "scale(1.15)";
|
|
1384
|
+
micButton.style.background = newBackground;
|
|
1385
|
+
// Add colored glow based on state
|
|
1386
|
+
if (isMuted) {
|
|
1387
|
+
micButton.style.boxShadow =
|
|
1388
|
+
"0 0 12px rgba(239, 68, 68, 0.6), 0 2px 4px rgba(0, 0, 0, 0.1)";
|
|
1389
|
+
}
|
|
1390
|
+
else {
|
|
1391
|
+
micButton.style.boxShadow =
|
|
1392
|
+
"0 0 12px rgba(16, 185, 129, 0.4), 0 2px 4px rgba(0, 0, 0, 0.1)";
|
|
1393
|
+
}
|
|
1394
|
+
// Return to normal size after animation
|
|
1395
|
+
setTimeout(() => {
|
|
1396
|
+
if (micButton) {
|
|
1397
|
+
micButton.style.transform = "scale(1)";
|
|
1398
|
+
micButton.style.boxShadow = "0 2px 4px rgba(0, 0, 0, 0.1)";
|
|
1399
|
+
}
|
|
1400
|
+
}, 200);
|
|
1254
1401
|
}
|
|
1255
1402
|
};
|
|
1256
1403
|
// Helper for Markdown - matches React MarkdownRenderer.tsx
|
|
@@ -1258,8 +1405,8 @@ export function createConvaiWidget(container, options) {
|
|
|
1258
1405
|
if (!text)
|
|
1259
1406
|
return;
|
|
1260
1407
|
// Handle both actual newlines and \n escape sequences
|
|
1261
|
-
const normalizedText = text.replace(/\\n/g,
|
|
1262
|
-
const lines = normalizedText.split(
|
|
1408
|
+
const normalizedText = text.replace(/\\n/g, "\n");
|
|
1409
|
+
const lines = normalizedText.split("\n");
|
|
1263
1410
|
lines.forEach((line, lineIndex) => {
|
|
1264
1411
|
// Process markdown within each line
|
|
1265
1412
|
const processLine = (text) => {
|
|
@@ -1281,21 +1428,31 @@ export function createConvaiWidget(container, options) {
|
|
|
1281
1428
|
}
|
|
1282
1429
|
// Determine if it's bold or italic
|
|
1283
1430
|
if (match[1] && match[2]) {
|
|
1284
|
-
// Bold text (** or __)
|
|
1285
|
-
const strong = document.createElement(
|
|
1431
|
+
// Bold text (** or __) - matches React UserBubble styling
|
|
1432
|
+
const strong = document.createElement("strong");
|
|
1286
1433
|
strong.textContent = match[2];
|
|
1434
|
+
strong.style.cssText = `
|
|
1435
|
+
font-weight: 600;
|
|
1436
|
+
color: ${aeroTheme.colors.text.primary};
|
|
1437
|
+
`;
|
|
1287
1438
|
parts.push(strong);
|
|
1288
1439
|
}
|
|
1289
1440
|
else if (match[3]) {
|
|
1290
|
-
// Italic text (*)
|
|
1291
|
-
const em = document.createElement(
|
|
1441
|
+
// Italic text (*) - matches React UserBubble styling
|
|
1442
|
+
const em = document.createElement("em");
|
|
1292
1443
|
em.textContent = match[3];
|
|
1444
|
+
em.style.cssText = `
|
|
1445
|
+
font-style: italic;
|
|
1446
|
+
`;
|
|
1293
1447
|
parts.push(em);
|
|
1294
1448
|
}
|
|
1295
1449
|
else if (match[4]) {
|
|
1296
|
-
// Italic text (_)
|
|
1297
|
-
const em = document.createElement(
|
|
1450
|
+
// Italic text (_) - matches React UserBubble styling
|
|
1451
|
+
const em = document.createElement("em");
|
|
1298
1452
|
em.textContent = match[4];
|
|
1453
|
+
em.style.cssText = `
|
|
1454
|
+
font-style: italic;
|
|
1455
|
+
`;
|
|
1299
1456
|
parts.push(em);
|
|
1300
1457
|
}
|
|
1301
1458
|
lastIndex = match.index + match[0].length;
|
|
@@ -1312,19 +1469,19 @@ export function createConvaiWidget(container, options) {
|
|
|
1312
1469
|
const processedLine = processLine(line);
|
|
1313
1470
|
// Handle empty lines (creates paragraph spacing)
|
|
1314
1471
|
if (line.trim() === "" && lineIndex > 0) {
|
|
1315
|
-
container.appendChild(document.createElement(
|
|
1472
|
+
container.appendChild(document.createElement("br"));
|
|
1316
1473
|
return;
|
|
1317
1474
|
}
|
|
1318
1475
|
// Append the processed line
|
|
1319
1476
|
if (processedLine.length > 0) {
|
|
1320
|
-
processedLine.forEach(part => container.appendChild(part));
|
|
1477
|
+
processedLine.forEach((part) => container.appendChild(part));
|
|
1321
1478
|
}
|
|
1322
1479
|
else {
|
|
1323
1480
|
container.appendChild(document.createTextNode(line));
|
|
1324
1481
|
}
|
|
1325
1482
|
// Add line break between lines
|
|
1326
1483
|
if (lineIndex < lines.length - 1) {
|
|
1327
|
-
container.appendChild(document.createElement(
|
|
1484
|
+
container.appendChild(document.createElement("br"));
|
|
1328
1485
|
}
|
|
1329
1486
|
});
|
|
1330
1487
|
};
|
|
@@ -1332,9 +1489,9 @@ export function createConvaiWidget(container, options) {
|
|
|
1332
1489
|
if (!messageListElement)
|
|
1333
1490
|
return;
|
|
1334
1491
|
const messages = formatMessages();
|
|
1335
|
-
messageListElement.innerHTML =
|
|
1492
|
+
messageListElement.innerHTML = "";
|
|
1336
1493
|
if (messages.length === 0) {
|
|
1337
|
-
const emptyState = document.createElement(
|
|
1494
|
+
const emptyState = document.createElement("div");
|
|
1338
1495
|
emptyState.style.cssText = `
|
|
1339
1496
|
display: flex;
|
|
1340
1497
|
flex-direction: column;
|
|
@@ -1353,79 +1510,107 @@ export function createConvaiWidget(container, options) {
|
|
|
1353
1510
|
return;
|
|
1354
1511
|
}
|
|
1355
1512
|
messages.forEach((msg) => {
|
|
1356
|
-
|
|
1513
|
+
// MessageWrapper - matches React UserMessageContainer
|
|
1514
|
+
const messageWrapper = document.createElement("div");
|
|
1357
1515
|
messageWrapper.style.cssText = `
|
|
1358
1516
|
display: flex;
|
|
1359
1517
|
flex-direction: column;
|
|
1360
|
-
|
|
1361
|
-
|
|
1518
|
+
max-width: ${msg.isUser ? "70%" : "70%"};
|
|
1519
|
+
margin-left: ${msg.isUser ? "auto" : "0"};
|
|
1362
1520
|
width: 100%;
|
|
1521
|
+
isolation: isolate;
|
|
1522
|
+
contain: layout style paint;
|
|
1363
1523
|
`;
|
|
1364
|
-
const bubble = document.createElement(
|
|
1524
|
+
const bubble = document.createElement("div");
|
|
1525
|
+
// Add data attribute to identify user messages for debugging/styling
|
|
1526
|
+
if (msg.isUser) {
|
|
1527
|
+
bubble.setAttribute("data-message-type", "user");
|
|
1528
|
+
}
|
|
1529
|
+
// Set base styles first - explicitly set background to match React UserBubble exactly
|
|
1530
|
+
// CRITICAL: Use the exact same background for both user and bot messages
|
|
1531
|
+
const backgroundColor = "rgba(252, 252, 253, 0.95)";
|
|
1365
1532
|
bubble.style.cssText = `
|
|
1366
|
-
padding: 12px
|
|
1367
|
-
border-radius:
|
|
1368
|
-
background: ${
|
|
1369
|
-
color: ${
|
|
1370
|
-
|
|
1533
|
+
padding: 12px;
|
|
1534
|
+
border-radius: ${msg.isUser ? "12px 12px 4px 12px" : "12px 12px 12px 4px"};
|
|
1535
|
+
background: ${backgroundColor};
|
|
1536
|
+
background-color: ${backgroundColor};
|
|
1537
|
+
backdrop-filter: blur(20px) saturate(180%);
|
|
1538
|
+
border: 1px solid rgba(0, 0, 0, 0.06);
|
|
1539
|
+
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.8) inset, 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
1540
|
+
color: ${aeroTheme.colors.text.primary};
|
|
1541
|
+
font-family: ${aeroTheme.typography.fontFamily.body};
|
|
1542
|
+
font-size: 14px;
|
|
1371
1543
|
line-height: 1.5;
|
|
1372
1544
|
word-wrap: break-word;
|
|
1373
|
-
|
|
1374
|
-
border-bottom-${msg.isUser ? 'right' : 'left'}-radius: 4px;
|
|
1375
|
-
max-width: 85%;
|
|
1545
|
+
max-width: 100%;
|
|
1376
1546
|
display: flex;
|
|
1377
1547
|
flex-direction: column;
|
|
1378
|
-
|
|
1548
|
+
font-synthesis: none;
|
|
1549
|
+
text-rendering: optimizeLegibility;
|
|
1550
|
+
-webkit-font-smoothing: antialiased;
|
|
1551
|
+
-moz-osx-font-smoothing: grayscale;
|
|
1552
|
+
-webkit-text-size-adjust: 100%;
|
|
1553
|
+
isolation: isolate;
|
|
1554
|
+
contain: layout style paint;
|
|
1379
1555
|
`;
|
|
1380
|
-
//
|
|
1556
|
+
// Set background with !important to override any other styles (this is critical)
|
|
1557
|
+
bubble.style.setProperty("background", backgroundColor, "important");
|
|
1558
|
+
bubble.style.setProperty("background-color", backgroundColor, "important");
|
|
1559
|
+
// Header (Logo/Icon + Name) inside bubble - matches React MessageHeader
|
|
1381
1560
|
if (msg.sender) {
|
|
1382
|
-
const header = document.createElement(
|
|
1561
|
+
const header = document.createElement("div");
|
|
1383
1562
|
header.style.cssText = `
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1563
|
+
display: flex;
|
|
1564
|
+
align-items: center;
|
|
1565
|
+
gap: ${aeroTheme.spacing.xs};
|
|
1566
|
+
margin-bottom: ${aeroTheme.spacing.xs};
|
|
1567
|
+
isolation: isolate;
|
|
1568
|
+
contain: layout style paint;
|
|
1569
|
+
`;
|
|
1570
|
+
// Logo/Icon - matches React LogoWrapper
|
|
1571
|
+
const logoContainer = document.createElement("div");
|
|
1391
1572
|
logoContainer.style.cssText = `
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1573
|
+
width: 1rem;
|
|
1574
|
+
height: 1rem;
|
|
1575
|
+
display: flex;
|
|
1576
|
+
align-items: center;
|
|
1577
|
+
justify-content: center;
|
|
1578
|
+
isolation: isolate;
|
|
1579
|
+
contain: layout style paint;
|
|
1580
|
+
`;
|
|
1399
1581
|
if (msg.isUser) {
|
|
1400
|
-
// User Icon - FaUser from react-icons/fa
|
|
1401
|
-
const userIcon = Icons.User(
|
|
1402
|
-
userIcon.style.width =
|
|
1403
|
-
userIcon.style.height =
|
|
1582
|
+
// User Icon - FaUser from react-icons/fa, matches React exactly
|
|
1583
|
+
const userIcon = Icons.User("sm");
|
|
1584
|
+
userIcon.style.width = "16px";
|
|
1585
|
+
userIcon.style.height = "16px";
|
|
1404
1586
|
userIcon.style.color = aeroTheme.colors.convai.dark;
|
|
1405
1587
|
logoContainer.appendChild(userIcon);
|
|
1406
1588
|
}
|
|
1407
1589
|
else {
|
|
1408
1590
|
// Bot Logo - ConvaiLogo sm size with connected state
|
|
1409
|
-
const botLogo = Icons.ConvaiLogo(
|
|
1410
|
-
botLogo.style.width =
|
|
1411
|
-
botLogo.style.height =
|
|
1412
|
-
botLogo.style.color =
|
|
1591
|
+
const botLogo = Icons.ConvaiLogo("sm", "connected");
|
|
1592
|
+
botLogo.style.width = "16px";
|
|
1593
|
+
botLogo.style.height = "16px";
|
|
1594
|
+
botLogo.style.color = "#10b981";
|
|
1413
1595
|
logoContainer.appendChild(botLogo);
|
|
1414
1596
|
}
|
|
1415
|
-
|
|
1597
|
+
// Name Label - matches React UserLabel exactly
|
|
1598
|
+
const nameLabel = document.createElement("span");
|
|
1416
1599
|
nameLabel.textContent = msg.sender === "User" ? "You" : msg.sender;
|
|
1417
1600
|
nameLabel.style.cssText = `
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1601
|
+
font-size: 0.75rem;
|
|
1602
|
+
color: ${msg.isUser ? aeroTheme.colors.convai.dark : "#10b981"};
|
|
1603
|
+
font-weight: 500;
|
|
1604
|
+
font-family: ${aeroTheme.typography.fontFamily.body};
|
|
1605
|
+
isolation: isolate;
|
|
1606
|
+
contain: layout style paint;
|
|
1607
|
+
`;
|
|
1423
1608
|
header.appendChild(logoContainer);
|
|
1424
1609
|
header.appendChild(nameLabel);
|
|
1425
1610
|
bubble.appendChild(header);
|
|
1426
1611
|
}
|
|
1427
1612
|
// Message Content
|
|
1428
|
-
const contentDiv = document.createElement(
|
|
1613
|
+
const contentDiv = document.createElement("div");
|
|
1429
1614
|
contentDiv.style.cssText = `
|
|
1430
1615
|
white-space: pre-wrap;
|
|
1431
1616
|
word-wrap: break-word;
|
|
@@ -1433,17 +1618,19 @@ export function createConvaiWidget(container, options) {
|
|
|
1433
1618
|
renderMarkdown(msg.text, contentDiv);
|
|
1434
1619
|
bubble.appendChild(contentDiv);
|
|
1435
1620
|
messageWrapper.appendChild(bubble);
|
|
1436
|
-
// Timestamp
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1621
|
+
// Timestamp - matches React exactly
|
|
1622
|
+
if (msg.timestamp) {
|
|
1623
|
+
const timestamp = document.createElement("span");
|
|
1624
|
+
timestamp.textContent = msg.timestamp;
|
|
1625
|
+
timestamp.style.cssText = `
|
|
1626
|
+
font-size: 0.75rem;
|
|
1627
|
+
color: ${aeroTheme.colors.text.secondary};
|
|
1628
|
+
margin-top: 4px;
|
|
1629
|
+
align-self: ${msg.isUser ? "flex-end" : "flex-start"};
|
|
1630
|
+
font-family: ${aeroTheme.typography.fontFamily.body};
|
|
1631
|
+
`;
|
|
1632
|
+
messageWrapper.appendChild(timestamp);
|
|
1633
|
+
}
|
|
1447
1634
|
messageListElement.appendChild(messageWrapper);
|
|
1448
1635
|
});
|
|
1449
1636
|
// Scroll to bottom
|
|
@@ -1479,9 +1666,17 @@ export function createConvaiWidget(container, options) {
|
|
|
1479
1666
|
};
|
|
1480
1667
|
// Setup event listeners for client state changes
|
|
1481
1668
|
const setupClientListeners = () => {
|
|
1482
|
-
convaiClient.on(
|
|
1669
|
+
convaiClient.on("stateChange", () => {
|
|
1483
1670
|
updateHeader();
|
|
1484
1671
|
updateMicButton();
|
|
1672
|
+
// Update pulse animation based on connecting state
|
|
1673
|
+
if (convaiClient.state.isConnecting) {
|
|
1674
|
+
morphingContainer.style.animation =
|
|
1675
|
+
"convai-pulse 2s ease-in-out infinite";
|
|
1676
|
+
}
|
|
1677
|
+
else {
|
|
1678
|
+
morphingContainer.style.animation = "none";
|
|
1679
|
+
}
|
|
1485
1680
|
// Update speaking animation state
|
|
1486
1681
|
if (convaiClient.state.isSpeaking) {
|
|
1487
1682
|
if (!startTime) {
|
|
@@ -1496,11 +1691,6 @@ export function createConvaiWidget(container, options) {
|
|
|
1496
1691
|
targetLevels = Array(40).fill(0.05);
|
|
1497
1692
|
}
|
|
1498
1693
|
}
|
|
1499
|
-
// Sync video visibility with controls state
|
|
1500
|
-
if (isVideoVisible !== convaiClient.videoControls.isVideoEnabled) {
|
|
1501
|
-
isVideoVisible = convaiClient.videoControls.isVideoEnabled;
|
|
1502
|
-
updateVoiceMode();
|
|
1503
|
-
}
|
|
1504
1694
|
// Auto-collapse when disconnected
|
|
1505
1695
|
if (!convaiClient.state.isConnected && !convaiClient.state.isConnecting) {
|
|
1506
1696
|
if (isOpen) {
|
|
@@ -1508,14 +1698,79 @@ export function createConvaiWidget(container, options) {
|
|
|
1508
1698
|
}
|
|
1509
1699
|
isMuted = false;
|
|
1510
1700
|
isVoiceMode = false;
|
|
1701
|
+
isVideoVisible = false;
|
|
1511
1702
|
updateHeader();
|
|
1512
1703
|
updateVoiceMode();
|
|
1513
1704
|
}
|
|
1514
1705
|
});
|
|
1515
|
-
|
|
1706
|
+
// Audio controls state change listener
|
|
1707
|
+
convaiClient.audioControls.on("audioStateChange", (audioState) => {
|
|
1708
|
+
if (audioState.isAudioMuted !== undefined) {
|
|
1709
|
+
updateMicButton();
|
|
1710
|
+
}
|
|
1711
|
+
});
|
|
1712
|
+
// Video state change listener
|
|
1713
|
+
convaiClient.videoControls.on("videoStateChange", (videoState) => {
|
|
1714
|
+
if (videoState.isVideoEnabled !== undefined) {
|
|
1715
|
+
isVideoVisible = videoState.isVideoEnabled;
|
|
1716
|
+
updateVoiceMode();
|
|
1717
|
+
// Update tray button state
|
|
1718
|
+
const videoBtn = document.getElementById("convai-settings-video-btn");
|
|
1719
|
+
if (videoBtn) {
|
|
1720
|
+
const isActive = isVideoVisible;
|
|
1721
|
+
videoBtn.style.backgroundColor = isActive
|
|
1722
|
+
? "rgba(16, 185, 129, 0.15)"
|
|
1723
|
+
: "transparent";
|
|
1724
|
+
videoBtn.style.color = isActive
|
|
1725
|
+
? "#10b981"
|
|
1726
|
+
: aeroTheme.colors.text.primary;
|
|
1727
|
+
// Update Icon - first child is icon container
|
|
1728
|
+
const iconContainer = videoBtn.querySelector("div");
|
|
1729
|
+
if (iconContainer) {
|
|
1730
|
+
iconContainer.innerHTML = "";
|
|
1731
|
+
const icon = isActive ? Icons.Video("md") : Icons.VideoOff("md");
|
|
1732
|
+
icon.style.width = "18px";
|
|
1733
|
+
icon.style.height = "18px";
|
|
1734
|
+
iconContainer.appendChild(icon);
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
});
|
|
1739
|
+
// Screen share state change listener
|
|
1740
|
+
convaiClient.screenShareControls.on("screenShareStateChange", (screenShareState) => {
|
|
1741
|
+
if (screenShareState.isScreenShareActive !== undefined) {
|
|
1742
|
+
const isSharing = screenShareState.isScreenShareActive;
|
|
1743
|
+
const shareBtn = document.getElementById("convai-settings-share-btn");
|
|
1744
|
+
if (shareBtn) {
|
|
1745
|
+
const isActive = isSharing;
|
|
1746
|
+
shareBtn.style.backgroundColor = isActive
|
|
1747
|
+
? "rgba(16, 185, 129, 0.15)"
|
|
1748
|
+
: "transparent";
|
|
1749
|
+
shareBtn.style.color = isActive
|
|
1750
|
+
? "#10b981"
|
|
1751
|
+
: aeroTheme.colors.text.primary;
|
|
1752
|
+
// Update Icon - first child is icon container
|
|
1753
|
+
const iconContainer = shareBtn.querySelector("div");
|
|
1754
|
+
if (iconContainer) {
|
|
1755
|
+
iconContainer.innerHTML = "";
|
|
1756
|
+
const icon = isActive
|
|
1757
|
+
? Icons.StopScreenShare("md")
|
|
1758
|
+
: Icons.ScreenShare("md");
|
|
1759
|
+
icon.style.width = "18px";
|
|
1760
|
+
icon.style.height = "18px";
|
|
1761
|
+
iconContainer.appendChild(icon);
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
});
|
|
1766
|
+
convaiClient.on("messagesChange", () => {
|
|
1516
1767
|
updateMessageList();
|
|
1517
1768
|
});
|
|
1518
|
-
|
|
1769
|
+
// Bot ready listener - updates UI when bot becomes ready
|
|
1770
|
+
convaiClient.on("botReady", () => {
|
|
1771
|
+
updateHeader(); // Update header to show green status
|
|
1772
|
+
});
|
|
1773
|
+
convaiClient.on("connect", () => {
|
|
1519
1774
|
// Initialize audio renderer on connection
|
|
1520
1775
|
if (convaiClient.room && !audioRenderer) {
|
|
1521
1776
|
audioRenderer = new AudioRenderer(convaiClient.room);
|
|
@@ -1523,7 +1778,7 @@ export function createConvaiWidget(container, options) {
|
|
|
1523
1778
|
// Fetch character info
|
|
1524
1779
|
fetchCharacterInfo();
|
|
1525
1780
|
});
|
|
1526
|
-
convaiClient.on(
|
|
1781
|
+
convaiClient.on("disconnect", () => {
|
|
1527
1782
|
// Cleanup audio renderer
|
|
1528
1783
|
if (audioRenderer) {
|
|
1529
1784
|
audioRenderer.destroy();
|
|
@@ -1535,6 +1790,8 @@ export function createConvaiWidget(container, options) {
|
|
|
1535
1790
|
// Initialize
|
|
1536
1791
|
createDOM();
|
|
1537
1792
|
setupClientListeners();
|
|
1793
|
+
// Set initial button state
|
|
1794
|
+
updateSendButton(); // Show voice mode button initially since input is empty
|
|
1538
1795
|
// If already connected, initialize audio renderer and fetch character info
|
|
1539
1796
|
if (convaiClient.state.isConnected && convaiClient.room) {
|
|
1540
1797
|
audioRenderer = new AudioRenderer(convaiClient.room);
|
|
@@ -1550,7 +1807,7 @@ export function createConvaiWidget(container, options) {
|
|
|
1550
1807
|
audioRenderer = null;
|
|
1551
1808
|
}
|
|
1552
1809
|
// Remove event listeners
|
|
1553
|
-
morphingContainer.removeEventListener(
|
|
1810
|
+
morphingContainer.removeEventListener("click", handleToggle);
|
|
1554
1811
|
// Remove DOM elements
|
|
1555
1812
|
if (rootElement.parentElement) {
|
|
1556
1813
|
rootElement.remove();
|
|
@@ -1559,11 +1816,12 @@ export function createConvaiWidget(container, options) {
|
|
|
1559
1816
|
floatingVideo.remove();
|
|
1560
1817
|
}
|
|
1561
1818
|
// Unsubscribe from client events
|
|
1562
|
-
convaiClient.off(
|
|
1563
|
-
convaiClient.off(
|
|
1564
|
-
convaiClient.off(
|
|
1565
|
-
convaiClient.off(
|
|
1566
|
-
|
|
1819
|
+
convaiClient.off("stateChange", () => { });
|
|
1820
|
+
convaiClient.off("messagesChange", () => { });
|
|
1821
|
+
convaiClient.off("botReady", () => { });
|
|
1822
|
+
convaiClient.off("connect", () => { });
|
|
1823
|
+
convaiClient.off("disconnect", () => { });
|
|
1824
|
+
},
|
|
1567
1825
|
};
|
|
1568
1826
|
return widget;
|
|
1569
1827
|
}
|