(function () { const chatHtml = `
logo
` function generateUUID() { return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace( /[xy]/g, function (c) { const r = (Math.random() * 16) | 0, v = c === "x" ? r : (r & 0x3) | 0x8; return v.toString(16); } ); } function createBubble(contents, sender) { const bubble = document.createElement("div") const bubbleClasses = `max-w-[80%] py-[10px] px-[14px] rounded-[16px] text-sm leading-[1.4] shadow-sm break-words whitespace-normal ${sender === "user" ? "self-end text-[#333] bg-[#fff]" : "self-start text-[#333] bg-[#e6e6fa]"} ` bubble.className = bubbleClasses bubble.innerHTML = contents return bubble } function onDomContentLoaded() { console.log('Injecting script'); const rootContainer = document.createElement("div") rootContainer.innerHTML = chatHtml document.body.appendChild(rootContainer) let centralChatExpanded = false const chatId = "de91e0ee-b71b-442c-a664-4d96e16025be"; const projectKey = "5e642567-53bd-4c49-964d-a963ad0b94be"; const cahoAssistantSmallButton = document.getElementById("cahoAssistantSmallButton") const chatbotContainer = document.getElementById("chatbotContainer") const centralChatCloseButton = document.getElementById("centralChatCloseButton") const centralChatEnlargeButton = document.getElementById("centralChatEnlargeButton") const sendMessageButton = document.getElementById("sendMessageButton") const sendMessageButtonSvg = document.getElementById("sendMessageButtonSvg") const microphoneButton = document.getElementById("microphoneButton") const textInput = document.getElementById("textInput") const chatMessagesListContainer = document.getElementById("chatMessagesListContainer") const botDescriptionAndSuggestionsContainer = document.getElementById("botDescriptionAndSuggestionsContainer") const topLogosContainer = document.getElementById("topLogosContainer") function removeBotDescriptionAndSuggestionsContainer() { botDescriptionAndSuggestionsContainer.remove() } cahoAssistantSmallButton.addEventListener("click", () => { cahoAssistantSmallButton.classList.add("hidden") chatbotContainer.classList.remove("hidden") }) centralChatCloseButton.addEventListener("click", () => { cahoAssistantSmallButton.classList.remove("hidden") chatbotContainer.classList.add("hidden") }) const contractSvgIcon = document.getElementById("contractSvgIcon") const expandSvgIcon = document.getElementById("expandSvgIcon") function handleChatWidgetEnlargeOrContract() { if (centralChatExpanded) { // action: contract to bottom widget // show relevant svg items contractSvgIcon.classList.add("hidden") expandSvgIcon.classList.remove("hidden") topLogosContainer.style.marginBottom = '0px' // hide the messages list container: do not show messages chatMessagesListContainer.style.display = "none" centralChatExpanded = false } else { // action: expand to bigger window // show relevant svg items contractSvgIcon.classList.remove("hidden") expandSvgIcon.classList.add("hidden") topLogosContainer.style.marginBottom = '20px' // show the messages list container chatMessagesListContainer.style.display = "flex" chatMessagesListContainer.style.minHeight = "60vh" // show suggestions on expand botDescriptionAndSuggestionsContainer.style.display = "flex" centralChatExpanded = true } } centralChatEnlargeButton.addEventListener("click", handleChatWidgetEnlargeOrContract) textInput.addEventListener("focus", () => { sendMessageButton.style.backgroundColor = "#6b7cfc" if (!centralChatExpanded) handleChatWidgetEnlargeOrContract() const paths = sendMessageButtonSvg.getElementsByTagName("path") for (let i = 0; i < paths.length; i++) { paths[i].setAttribute("stroke", "white") } }) const onTextInputOutOfFocus = () => { sendMessageButton.style.backgroundColor = "transparent" const paths = sendMessageButtonSvg.getElementsByTagName("path") for (let i = 0; i < paths.length; i++) { paths[i].setAttribute("stroke", "#6b7cfc") } } textInput.addEventListener("focusout", onTextInputOutOfFocus) textInput.addEventListener("blur", onTextInputOutOfFocus) sendMessageButton.addEventListener("click", () => { sendMessage(textInput.value) }) textInput.addEventListener("keypress", (e) => { if (e.key === "Enter") { e.preventDefault() sendMessage(textInput.value) } }) function scrollToBottom() { chatMessagesListContainer.scrollTop = chatMessagesListContainer.scrollHeight; } async function sendMessage(questionText) { const question = questionText.trim() if (!question || question.length === 0) return removeBotDescriptionAndSuggestionsContainer() chatMessagesListContainer.style.paddingTop = "12px" chatMessagesListContainer.style.paddingBottom = "12px" const userBubble = createBubble(question, "user"); chatMessagesListContainer.appendChild(userBubble); textInput.value = "" // clear input const typingBubble = createBubble("...", "bot") chatMessagesListContainer.appendChild(typingBubble) // Save UUID in session storage let sessionUUID = sessionStorage.getItem("sessionUUID"); if (!sessionUUID) { sessionUUID = generateUUID(); sessionStorage.setItem("sessionUUID", sessionUUID); } const response = await fetch("https://main.tuluhealth.com/v1/web/chat/?instance=upkarhospital", { method: "POST", body: JSON.stringify({ query: question, project_id: projectKey, "id": chatId || sessionUUID }), headers: { "Content-Type": "application/json", Authorization: "Basic " + btoa("ayush:tulu@2025") }, }); if (!response.ok) { typingBubble.innerHTML = "

An error occured in serving your query. please try again

" scrollToBottom(); return } const reader = response.body.getReader(); const decoder = new TextDecoder("utf-8"); let botAnswer = ""; while (true) { const { done, value } = await reader.read(); if (done) break; botAnswer += decoder.decode(value, { stream: true }); typingBubble.innerHTML = marked.parse(botAnswer); scrollToBottom(); } typingBubble.remove(); const botBubble = createBubble(marked.parse(botAnswer), "bot"); chatMessagesListContainer.appendChild(botBubble); scrollToBottom(); } // speech to text (voice input) let recognition; let isListening = false; if ("webkitSpeechRecognition" in window) { recognition = new webkitSpeechRecognition(); recognition.continuous = false; recognition.interimResults = false; recognition.lang = "en-US"; recognition.onstart = () => { isListening = true microphoneButton.classList.add("listening") } recognition.onend = () => { isListening = false; microphoneButton.classList.remove("listening") } recognition.onresult = (e) => { const transcript = e.results[0][0].transcript; textInput.value = transcript } } else { microphoneButton.disabled = true microphoneButton.title = "Speech recognition not supported in this browser" } microphoneButton.addEventListener("click", () => { if (!recognition) return if (!isListening) recognition.start() else recognition.stop() }) document.querySelectorAll(".suggestionsButton").forEach((suggestionBtn) => { suggestionBtn.addEventListener("click", () => { const question = suggestionBtn.innerHTML.trim() textInput.value = question sendMessage(question) }) }) } async function loadLibrary(src) { return new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = src; script.onload = resolve; script.onerror = reject; document.head.appendChild(script); }); } loadLibrary("https://cdn.jsdelivr.net/npm/marked/marked.min.js"); document.addEventListener('DOMContentLoaded', () => { // load the tailwindcss library before injecting the widget to the dom // this is done to mitigate the case where initial UI renders without any styles (bad UX) loadLibrary("https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4").then(onDomContentLoaded) }); })()