document.addEventListener("DOMContentLoaded", () => { let isInitialized = false; let botId = null; let connectionDetails; let isConversationActive = false; let timerInterval = null; let callSeconds = 0; let showIntroCard= true const style = document.createElement("style"); style.textContent = ` @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap'); .voxgauge-container { position: fixed; bottom: 24px; right: 24px; z-index: 10000; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; } .voxgauge-backdrop { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px); z-index: 9999; display: none; } .voxgauge-backdrop.show { display: block; } .voxgauge-intro-card{ width: 360px; height: 340px; position: absolute; bottom: 100%; right: 100%; display: flex; flex-direction: column; align-items: start; justify-content: center; gap: 10px; background-color: #ffffff; padding: 20px; border-radius: 20px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); z-index: 10001; sefont-family: u } .voxgauge-intro-card-close-btn{ position: absolute; top: 20px; right: 20px; cursor: pointer; width: 24px; height: 24px; padding: 5px; border-radius: 5px; transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); } .voxgauge-intro-card-close-btn:hover svg{ stroke : #5125ef; } .voxgauge-intro-card-h1{ display: flex; align-items: center; justify-content: center; gap: 10px; } .voxgauge-intro-card-h1-span-text{ font-size: 1.63rem; margin-top: 0; margin-bottom: 5px; font-weight: 600; display: flex; align-items: center; justify-content: center; gap: 10px; } .voxgauge-intro-card-h1-span{ display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; background-color: #5125ef; border-radius: 20px; padding: 10px; } .voxgauge-intro-card-h1-span svg{ width: 32px; height: 32px; stroke: #FFFFFF; } .voxgauge-intro-card-p{ font-size: 16px; margin-top: 0; margin-bottom: 4px; font-weight: 400; } .voxgauge-intro-card-ul{ margin-top: 8px; margin-bottom: 14px; font-size: 0.96rem; font-weight: 400; } .voxgauge-intro-card-li{ font-size: 0.96rem; font-weight: 400; margin-bottom: 4px; color: #333; } .voxgauge-intro-card-li::marker{ color: #5125ef; } .voxgauge-intro-card-button{ background-color: #5125ef; width:100%; height: 40px; border-radius: 8px; color: #FFFFFF; border: none; cursor: pointer; transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); font-size: 18px; font-weight: 500; padding: 8px 16px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); display: flex; align-items: center; justify-content: center; } .voxgauge-button { width: 60px; height: 60px; background: #5125ef; border-radius: 32px; display: flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); position: relative; transform-origin: center; overflow: hidden; } .voxgauge-button:hover { transform: scale(1.08); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25); } .voxgauge-button.voxgauge-inactive { opacity: 0.7; } .voxgauge-button.voxgauge-expanded { width: 360px; height: 64px; border-radius: 32px; padding: 0 20px; box-sizing: border-box; transition: all 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94); overflow: visible; } .voxgauge-button.voxgauge-connecting, .voxgauge-button.voxgauge-active-call { width: 160px; height: 64px; border-radius: 32px; transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); } .voxgauge-call-icon { width: 28px; height: 28px; color: #FFFFFF; transition: all 0.3s ease; flex-shrink: 0; } .voxgauge-button.voxgauge-expanded .voxgauge-call-icon, .voxgauge-button.voxgauge-connecting .voxgauge-call-icon, .voxgauge-button.voxgauge-active-call .voxgauge-call-icon { opacity: 0; width: 0; height: 0; margin: 0; } .voxgauge-input-container { display: none; align-items: center; gap: 8px; width: 100%; padding: 0 8px; height: 100%; overflow: visible; transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); } .voxgauge-button.voxgauge-expanded .voxgauge-input-container { display: flex !important; } .voxgauge-country-select { position: relative; min-width: 80px; z-index: 10002; } .voxgauge-country-dropdown { background: #5125ef; border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 8px; color: #FFFFFF; padding: 8px 10px; font-size: 12px; font-weight: 500; cursor: pointer; transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 70px; } .voxgauge-country-dropdown:hover { background: rgba(255, 255, 255, 0.15); border-color: rgba(255, 255, 255, 0.4); transform: translateY(-1px); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); } .voxgauge-dropdown-list { position: absolute; bottom: 100%; left: 0; background: #5125ef; border-radius: 8px; max-height: 200px; overflow-y: auto; width: 120px; display: none; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); z-index: 10003; border: 1px solid rgba(255, 255, 255, 0.1); margin-bottom: 8px; } .voxgauge-dropdown-list.show { display: block; } .voxgauge-dropdown-item { padding: 8px 12px; font-size: 13px; color: #FFFFFF; cursor: pointer; transition: all 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94); } .voxgauge-dropdown-item:hover { background: rgba(255, 255, 255, 0.15); } .voxgauge-dropdown-list::-webkit-scrollbar { width: 4px; } .voxgauge-dropdown-list::-webkit-scrollbar-track { background: rgba(255, 255, 255, 0.1); } .voxgauge-dropdown-list::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.3); border-radius: 2px; } .voxgauge-phone-input { flex: 1; background: transparent; border: none; border-bottom: 2px solid rgba(255, 255, 255, 0.3); color: #FFFFFF; font-size: 14px; padding: 8px 0; outline: none; transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); min-width: 0; max-width: 120px; overflow: visible; text-overflow: ellipsis; font-weight: 500; } .voxgauge-phone-input:focus { border-bottom: 2px solid #FFFFFF; background: rgba(255, 255, 255, 0.1); } .voxgauge-phone-input::placeholder { color: rgba(255, 255, 255, 0.7); font-weight: 400; } .voxgauge-button-group { display: none; gap: 6px; align-items: center; flex-shrink: 0; } .voxgauge-button.voxgauge-expanded .voxgauge-button-group { display: flex; } .voxgauge-action-btn { background: #FFFFFF; color: #5125ef; border: none; border-radius: 16px; padding: 6px 12px; font-size: 12px; font-weight: 500; cursor: pointer; transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); white-space: nowrap; flex-shrink: 0; transform-origin: center; } .voxgauge-action-btn:hover { background: rgba(255, 255, 255, 0.95); transform: translateY(-2px) scale(1.02); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); } .voxgauge-tooltip { position: absolute; top: -45px; right: 0; background: #5125ef; color: #FFFFFF; padding: 8px 12px; border-radius: 8px; font-size: 12px; font-weight: 500; opacity: 0; transform: translateY(8px); transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); pointer-events: none; white-space: nowrap; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); z-index: 10004; } .voxgauge-tooltip::after { content: ''; position: absolute; bottom: -4px; right: 12px; width: 8px; height: 8px; background: #5125ef; transform: rotate(45deg); } .voxgauge-tooltip.show { opacity: 1; transform: translateY(0); } .voxgauge-call-status { display: none; align-items: center; gap: 12px; padding: 0 16px; color: #FFFFFF; font-size: 14px; font-weight: 500; height: 100%; width: 100%; justify-content: center; } .voxgauge-button.voxgauge-connecting .voxgauge-call-status, .voxgauge-button.voxgauge-active-call .voxgauge-call-status { display: flex; } .voxgauge-end-call-btn { background: transparent; border: none; width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); border-radius: 50%; transform-origin: center; margin-left: 8px; } .voxgauge-end-call-btn:hover { background: #FF4D4F; transform: scale(1.15); box-shadow: 0 2px 8px rgba(255, 77, 79, 0.3); } .voxgauge-end-icon { width: 20px; height: 20px; color: #FF4D4F; transition: color 0.2s ease; } .voxgauge-end-call-btn:hover .voxgauge-end-icon { color: #FFFFFF; } @media (max-width: 480px) { .voxgauge-button.voxgauge-expanded { width: 320px; padding: 0 16px; } .voxgauge-button.voxgauge-connecting, .voxgauge-button.voxgauge-active-call { width: 180px; } .voxgauge-end-call-btn { width: 32px; height: 32px; } .voxgauge-end-icon { width: 18px; height: 18px; } .voxgauge-country-dropdown { max-width: 60px; font-size: 11px; padding: 8px 8px; } .voxgauge-phone-input { max-width: 80px; font-size: 13px; } .voxgauge-action-btn { padding: 6px 10px; font-size: 11px; } } `; document.head.appendChild(style); // Create backdrop for blur effect const backdrop = document.createElement("div"); backdrop.className = "voxgauge-backdrop"; const container = document.createElement("div"); container.className = "voxgauge-container"; //intro card const introCard = document.createElement("div"); introCard.className = "voxgauge-intro-card"; introCard.innerHTML = `

Voice AI Assistant

Need help? Click here to call our AI agent for instant assistance and onboarding support.

`; container.appendChild(introCard); // Check localStorage to determine if intro card should be shown const showIntroCardStorage = localStorage.getItem('showIntroCard'); if (showIntroCardStorage === 'false') { backdrop.classList.remove('show'); } else { backdrop.classList.add('show'); } const button = document.createElement("div"); button.className = "voxgauge-button"; const tooltip = document.createElement("div"); const callIcon = document.createElementNS("http://www.w3.org/2000/svg", "svg"); callIcon.className = "voxgauge-call-icon"; callIcon.setAttribute("viewBox", "0 0 24 24"); callIcon.style.color = `#FFFFFF`; callIcon.style.width="28px"; callIcon.style.height="28px"; callIcon.innerHTML = ` `; const inputContainer = document.createElement("div"); inputContainer.className = "voxgauge-input-container"; const countrySelect = document.createElement("div"); countrySelect.className = "voxgauge-country-select"; const countryDisplay = document.createElement("div"); countryDisplay.className = "voxgauge-country-dropdown"; countryDisplay.textContent = "+1"; const dropdownList = document.createElement("div"); dropdownList.className = "voxgauge-dropdown-list"; const countryCodes = [ { code: "+1", country: "US/CA" }, { code: "+44", country: "UK" }, { code: "+91", country: "IN" }, { code: "+86", country: "CN" }, { code: "+81", country: "JP" }, { code: "+49", country: "DE" }, { code: "+33", country: "FR" }, { code: "+39", country: "IT" }, { code: "+34", country: "ES" }, { code: "+61", country: "AU" }, { code: "+65", country: "SG" }, { code: "+91", country: "IND" } // Add more country codes as needed ]; countryCodes.forEach(country => { const item = document.createElement("div"); item.className = "voxgauge-dropdown-item"; item.textContent = `${country.code}`; item.dataset.value = country.code; dropdownList.appendChild(item); }); let selectedCountryCode = "+1"; countrySelect.appendChild(countryDisplay); countrySelect.appendChild(dropdownList); const phoneInput = document.createElement("input"); phoneInput.type = "tel"; phoneInput.className = "voxgauge-phone-input"; phoneInput.placeholder = "Phone number"; const buttonGroup = document.createElement("div"); buttonGroup.className = "voxgauge-button-group"; const callButton = document.createElement("button"); callButton.className = "voxgauge-action-btn"; callButton.textContent = "Call"; const closeButton = document.createElement("button"); closeButton.className = "voxgauge-action-btn"; closeButton.textContent = "Close"; const callStatus = document.createElement("div"); callStatus.className = "voxgauge-call-status"; const connectingText = document.createElement("span"); connectingText.textContent = "Connecting..."; connectingText.style.fontWeight = "600"; const callTimer = document.createElement("span"); callTimer.textContent = "00:00"; callTimer.style.display = "none"; callTimer.style.fontWeight = "600"; callTimer.style.fontSize = "16px"; const endCallButton = document.createElement("button"); endCallButton.className = "voxgauge-end-call-btn"; endCallButton.style.display = "none"; endCallButton.innerHTML = ` `; inputContainer.appendChild(countrySelect); inputContainer.appendChild(phoneInput); buttonGroup.appendChild(callButton); buttonGroup.appendChild(closeButton); callStatus.appendChild(connectingText); callStatus.appendChild(callTimer); callStatus.appendChild(endCallButton); button.appendChild(callIcon); button.appendChild(inputContainer); button.appendChild(buttonGroup); button.appendChild(callStatus); container.appendChild(tooltip); container.appendChild(button); document.body.appendChild(backdrop); document.body.appendChild(container); // introcard const introCardCloseBtn = introCard.querySelector(".voxgauge-intro-card-close-btn"); const introCardGotItBtn = introCard.querySelector(".voxgauge-intro-card-button"); // Only attach event listeners if the intro card is visible (not dismissed) if(showIntroCard !== 'false'){ if (introCardCloseBtn) { introCardCloseBtn.addEventListener("click", () => { introCard.style.display = "none"; backdrop.classList.remove('show'); localStorage.setItem('showIntroCard', 'false'); }); } if (introCardGotItBtn) { introCardGotItBtn.addEventListener("click", () => { introCard.style.display = "none"; backdrop.classList.remove('show'); localStorage.setItem('showIntroCard', 'false'); }); } } if(localStorage.getItem('showIntroCard')==='false'){ introCard.style.display = "none"; backdrop.classList.remove('show'); } else if(localStorage.getItem('showIntroCard')==='true'){ backdrop.classList.add('show'); } // Event Listeners button.addEventListener("click", () => { if (!button.classList.contains("voxgauge-expanded") && !isConversationActive) { button.classList.remove("voxgauge-inactive"); button.classList.add("voxgauge-expanded"); inputContainer.style.display = "flex"; buttonGroup.style.display = "flex"; callIcon.style.opacity = "0"; callIcon.style.width = "0"; callIcon.style.height = "0"; setTimeout(() => phoneInput.focus(), 300); introCard.style.display = "none"; backdrop.classList.remove('show'); localStorage.setItem('showIntroCard', 'false'); } }); countrySelect.addEventListener("click", (e) => { e.stopPropagation(); dropdownList.classList.toggle("show"); }); dropdownList.addEventListener("click", (e) => { e.stopPropagation(); if (e.target.classList.contains("voxgauge-dropdown-item")) { selectedCountryCode = e.target.dataset.value; countryDisplay.textContent = e.target.textContent; dropdownList.classList.remove("show"); phoneInput.focus(); } }); document.addEventListener("click", (e) => { if (!countrySelect.contains(e.target)) { dropdownList.classList.remove("show"); } }); phoneInput.addEventListener("click", (e) => e.stopPropagation()); closeButton.addEventListener("click", (e) => { e.stopPropagation(); resetUI(); }); callButton.addEventListener("click", async (e) => { e.stopPropagation(); const fullPhoneNumber = selectedCountryCode + phoneInput.value.trim(); if (!phoneInput.value.trim()) { showTooltip("Please enter a phone number"); return; } if (!validatePhoneNumber(fullPhoneNumber)) { showTooltip("Invalid phone number format", 4000); return; } // Save phone number to session storage sessionStorage.setItem("phoneNumber", fullPhoneNumber); initializeCallUI(); try { await initializeLiveKit(); await getBotId(); await getConnectionDetails(fullPhoneNumber); const room = new window.Room({ adaptiveStream: true, dynacast: true, }); await room.connect(connectionDetails.serverUrl, connectionDetails.participantToken, { autoSubscribe: true, }); await room.localParticipant.setMicrophoneEnabled(true); window.storytellerRoom = room; isConversationActive = true; room.on(window.RoomEvent.TrackSubscribed, (track, publication, participant) => { if (participant.identity !== room.localParticipant.identity) { const audioElement = track.attach(); document.body.appendChild(audioElement); } }); room.on(window.RoomEvent.ParticipantConnected, () => { button.classList.remove("voxgauge-connecting"); button.classList.add("voxgauge-active-call"); connectingText.style.display = "none"; callTimer.style.display = "inline"; endCallButton.style.display = "flex"; startCallTimer(); }); room.on(window.RoomEvent.Disconnected, () => { resetCallUI(); window.storytellerRoom = null; isConversationActive = false; }); } catch (error) { console.error("Call initialization failed:", error); showTooltip("Failed to start call. Please try again.", 4000); resetCallUI(); } }); endCallButton.addEventListener("click", async (e) => { e.stopPropagation(); if (window.storytellerRoom) { await window.storytellerRoom.disconnect(); window.storytellerRoom = null; } resetCallUI(); isConversationActive = false; }); // Helper Functions function showTooltip(message, duration = 3000) { tooltip.textContent = message; tooltip.classList.add("show"); setTimeout(() => tooltip.classList.remove("show"), duration); } function validatePhoneNumber(phoneNumber) { return /^[+]?[0-9]{1,3}[-s]?[0-9]{7,}$/.test(phoneNumber); } function startCallTimer() { stopCallTimer(); callSeconds = 0; timerInterval = setInterval(() => { callSeconds++; const mins = Math.floor(callSeconds / 60).toString().padStart(2, "0"); const secs = (callSeconds % 60).toString().padStart(2, "0"); callTimer.textContent = `${mins}:${secs}`; }, 1000); } function stopCallTimer() { if (timerInterval) { clearInterval(timerInterval); timerInterval = null; } callSeconds = 0; callTimer.textContent = "00:00"; } function initializeCallUI() { button.classList.remove("voxgauge-expanded"); button.classList.add("voxgauge-connecting"); inputContainer.style.display = "none"; buttonGroup.style.display = "none"; callStatus.style.display = "flex"; connectingText.style.display = "inline"; callTimer.style.display = "none"; endCallButton.style.display = "none"; } function resetCallUI() { button.classList.remove("voxgauge-expanded", "voxgauge-connecting", "voxgauge-active-call"); inputContainer.style.display = "none"; buttonGroup.style.display = "none"; callStatus.style.display = "none"; connectingText.style.display = "none"; callTimer.style.display = "none"; endCallButton.style.display = "none"; callIcon.style.opacity = "1"; callIcon.style.width = "28px"; callIcon.style.height = "28px"; callIcon.style.color = `#FFFFFF`; phoneInput.value = ""; stopCallTimer(); // Remove phone number from session storage sessionStorage.removeItem("phoneNumber"); setTimeout(() => button.classList.add("voxgauge-inactive"), 1000); } function resetUI() { button.classList.remove("voxgauge-expanded"); inputContainer.style.display = "none"; buttonGroup.style.display = "none"; callIcon.style.opacity = "1"; callIcon.style.width = "28px"; callIcon.style.height = "28px"; callIcon.style.color =`#FFFFFF`; phoneInput.value = ""; // Remove phone number from session storage sessionStorage.removeItem("phoneNumber"); showTooltipOnce(); } async function initializeLiveKit() { if (isInitialized) return; try { const script = document.createElement('script'); script.src = 'https://unpkg.com/livekit-client@2.4.0/dist/livekit-client.umd.js'; document.head.appendChild(script); await new Promise((resolve, reject) => { script.onload = resolve; script.onerror = () => reject(new Error('Failed to load LiveKit SDK')); }); window.Room = window.LivekitClient.Room; window.RoomEvent = window.LivekitClient.RoomEvent; isInitialized = true; } catch (error) { console.error('LiveKit initialization failed:', error); throw error; } } async function getBotId() { try { const response = await fetch(`https://strangely-saved-parrot.ngrok.app/account/de7695d7-4ef6-4a5c-b97b-6687b74f62dd/bot/configuration`, { method: 'GET', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiOTQwYTZiZDItM2U4YS00MDY5LTgzODUtZGNiZGI5NzZiMTJmIn0.2uAoaCFTKAiB7Iwfe9Qvy4yUyXcBG-5yD3EqBtHSOM0`, }, }); if (!response.ok) { showTooltip("Failed to fetch bot configuration.", 4000); throw new Error(`HTTP ${response.status}`); } const botData = await response.json(); const activeBots = botData.filter(bot => bot.status === "Active"); if (activeBots.length === 0) { showTooltip("No active bots available.", 4000); throw new Error('No active bots'); } botId = activeBots[0].bot_id; } catch (error) { console.error('Bot ID fetch failed:', error); throw error; } } async function getConnectionDetails(phoneNumber) { try { const response = await fetch(`https://strangely-saved-parrot.ngrok.app/livekit/account/de7695d7-4ef6-4a5c-b97b-6687b74f62dd/trigger/call`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiOTQwYTZiZDItM2U4YS00MDY5LTgzODUtZGNiZGI5NzZiMTJmIn0.2uAoaCFTKAiB7Iwfe9Qvy4yUyXcBG-5yD3EqBtHSOM0`, }, body: JSON.stringify({ bot_id: botId, phone_number: phoneNumber, }), }); if (!response.ok) { showTooltip("Failed to initialize call.", 4000); throw new Error(`HTTP ${response.status}`); } const callData = await response.json(); connectionDetails = { serverUrl: 'wss://voxgauge-test-1zmhxopp.livekit.cloud', roomName: callData.room_name, participantToken: callData.participant_token, participantName: callData.participant_identity }; } catch (error) { console.error('Connection details fetch failed:', error); throw error; } } function showTooltipOnce() { const lastShown = localStorage.getItem("voxgaugeTooltipLastShown"); const now = Date.now(); if (!lastShown || now - parseInt(lastShown) > 30000) { tooltip.classList.add("show"); setTimeout(() => tooltip.classList.remove("show"), 4000); localStorage.setItem("voxgaugeTooltipLastShown", now.toString()); } } // Show tooltip initially after 2 seconds setTimeout(showTooltipOnce, 2000); // Then check every 30 seconds to show tooltip again setInterval(showTooltipOnce, 30000); })