const theme = "lightWarm"; const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjFjYTIyMjRiLTUxYTAtNGVlZi05MDQ0LTY0NWNmOGU2YTY4NSIsInByb2ZpbGVfbmFtZSI6InJlbmV3ZWQtYXF1YS1wZWFjb2NrIiwiY3JlYXRlZF9hdCI6IjIwMjQtMDUtMTZUMjM6MzI6MTYuOTM0WiIsInVwZGF0ZWRfYXQiOiIyMDI0LTA1LTE2VDIzOjMyOjE2LjkzNFoiLCJib3RfaWQiOiI3ZDk1ZmY1Mi01NzFiLTQ0NGYtYjJmZS00NDdmNDlkODRhZmQiLCJzdWJzY3JpYmVfd2ViX2NoYW5uZWxzIjpbeyJpZCI6ImM4YTYyNzMwLTMxMGMtNDIxNy1hNDcwLWE4MTE5YWI0MTkxNSIsIm1hbnVhbF9tb2RlIjpmYWxzZX1dLCJ3ZWJfY2xpZW50X2luZm8iOnsiYnJvd3NlciI6Ik1vemlsbGEvNS4wIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja287IGNvbXBhdGlibGU7IENsYXVkZUJvdC8xLjA7ICtjbGF1ZGVib3RAYW50aHJvcGljLmNvbSkiLCJvcyI6IiIsImRldmljZSI6IiJ9LCJvcmlnaW4iOiJodHRwczovL3d3dy5waWt6aXQuY29tIiwiaWF0IjoxNzE1OTAyMzM2LCJleHAiOjE3MTU5MDk1MzZ9.JhDmv56foj01JGokZwKJVMKInm18Gpp9nAnClfrJ0a4"; const bubble_message = "¿Necesitas Ayuda?"; const avatar_img = "https://firebasestorage.googleapis.com/v0/b/t-bit-bots.appspot.com/o/documents%2FIcono.png?alt=media&token=db6342db-50b8-4644-af4c-3112ddac6795"; const initial_message = "Hola cómo estás. Cómo te puedo ayudar"; const primary_color = "#4caf50"; const position = "left"; const base_url = "https://conversations.t-bit.io"; const channelId = "c8a62730-310c-4217-a470-a8119ab41915"; const themes = { lightWarm: { background: '#f5f3f2', line: '#e3dfdf', text: '#595757', primary: primary_color, }, lightCold: { background: '#f1f4f4', line: '#dfe2e3', text: '#575859', primary: primary_color, }, darkWarm: { background: '#2e2d2d', text: '#e3dfdf', line: '#575555', primary: primary_color, }, darkCold: { background: '#2d2e2e', text: '#dee2e3', line: '#555657', primary: primary_color, }, }; const floating = !(position === 'fixed'); function createSendIcon() { const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); svg.setAttribute('fill', 'none'); svg.setAttribute('viewBox', '0 0 24 24'); svg.setAttribute('stroke-width', '1.5'); svg.setAttribute('stroke', 'currentColor'); svg.setAttribute('class', 'tbit__sendBtn tbit__sendBtnDisabled'); svg.setAttribute('disabled', true); const path1 = document.createElementNS('http://www.w3.org/2000/svg', 'path'); path1.setAttribute('stroke-linecap', 'round'); path1.setAttribute('stroke-linejoin', 'round'); path1.setAttribute( 'd', 'M6 12L3.269 3.126A59.768 59.768 0 0121.485 12 59.77 59.77 0 013.27 20.876L5.999 12zm0 0h7.5', ); svg.appendChild(path1); return svg; } function createCloseIcon() { const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); svg.setAttribute('id', 'tbit__closeBtn'); svg.setAttribute('fill', 'none'); svg.setAttribute('viewBox', '0 0 24 24'); svg.setAttribute('stroke-width', '1.5'); svg.setAttribute('stroke', 'currentColor'); svg.setAttribute('class', 'tbit__closeBtn'); const path1 = document.createElementNS('http://www.w3.org/2000/svg', 'path'); path1.setAttribute('stroke-linecap', 'round'); path1.setAttribute('stroke-linejoin', 'round'); path1.setAttribute('d', 'M9.75 9.75l4.5 4.5'); svg.appendChild(path1); const path2 = document.createElementNS('http://www.w3.org/2000/svg', 'path'); path2.setAttribute('stroke-linecap', 'round'); path2.setAttribute('stroke-linejoin', 'round'); path2.setAttribute('d', 'm14.25 9.75-4.5 4.5'); svg.appendChild(path2); const path3 = document.createElementNS('http://www.w3.org/2000/svg', 'path'); path3.setAttribute('d', 'M21 12a9 9 0 11-18 0 9 9 0 0118 0z'); svg.appendChild(path3); return svg; } function createUserIcon() { const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); svg.setAttribute('fill', 'none'); svg.setAttribute('viewBox', '0 0 24 24'); svg.setAttribute('stroke-width', '1.5'); svg.setAttribute('stroke', 'currentColor'); svg.setAttribute('class', 'tbit__chatAvatar'); const path1 = document.createElementNS('http://www.w3.org/2000/svg', 'path'); path1.setAttribute('stroke-linecap', 'round'); path1.setAttribute('stroke-linejoin', 'round'); path1.setAttribute( 'd', 'M17.982 18.725A7.488 7.488 0 0012 15.75a7.488 7.488 0 00-5.982 2.975m11.963 0a9 9 0 10-11.963 0m11.963 0A8.966 8.966 0 0112 21a8.966 8.966 0 01-5.982-2.275M15 9.75a3 3 0 11-6 0 3 3 0 016 0z', ); svg.appendChild(path1); return svg; } function createChatMessage(message, position, display_loader = false) { const div = document.createElement('div'); div.classList.add('tbit__chatMessage'); div.classList.add( `${ position === 'right' ? 'tbit__chatMessageRight' : 'tbit__chatMessageLeft' }`, ); const messageText = document.createElement('div'); if (display_loader) { const loader = document.createElement('span'); loader.classList.add('tbit__loader'); loader.setAttribute('id', 'tbit__loader'); div.appendChild(loader); messageText.setAttribute('id', 'tbit__chatMessageTextLoading'); } messageText.classList.add('tbit__chatMessageTextContainer'); messageText.innerHTML = `

${message}

`; div.appendChild(messageText); if (position === 'left') { const avatar = document.createElement('img'); avatar.classList.add('tbit__chatAvatarImg'); avatar.setAttribute('src', avatar_img); div.appendChild(avatar); // add loader to the chat message } else { const userIcon = createUserIcon(); div.appendChild(userIcon); } return div; } async function fetchStreamData(message) { try { const url = new URL(`${base_url}/message`); const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}`, }, body: JSON.stringify({ message, id: channelId, }), }); if (!response.body) { throw new Error('No response body'); } const reader = response.body.getReader(); while (true) { const { done, value } = await reader.read(); if (done) { const chatMessageTextStream = document.getElementById( 'tbit__chatMessageTextStream', ); chatMessageTextStream.removeAttribute('id'); const sendBtn = document.querySelector('.tbit__sendBtn'); const chatInput = document.getElementById('tbit__chatInput'); sendBtn.removeAttribute('disabled'); chatInput.removeAttribute('disabled'); break; } const chunk = new TextDecoder('utf-8').decode(value); const lines = chunk.split('\n'); for (const line of lines) { if (line.startsWith('data:')) { const dataValue = JSON.parse(line.slice(6)).chunk; const chatMessageTextLoading = document.getElementById( 'tbit__chatMessageTextLoading', ); const chatMessageTextStream = document.getElementById( 'tbit__chatMessageTextStream', ); if (chatMessageTextLoading) { const chatText = document.getElementById('tbit__firstStream'); chatText.textContent = dataValue; chatText.setAttribute('id', 'tbit__chatMessageTextStream'); chatMessageTextLoading.setAttribute('id', ''); // remove loader const loader = document.getElementById('tbit__loader'); loader.parentNode.removeChild(loader); } else if (chatMessageTextStream) { if (dataValue.includes('\n\n')) { // create new paragraph const newParagraph = document.createElement('p'); newParagraph.classList.add('tbit__chatMessageText'); newParagraph.setAttribute('id', 'tbit__chatMessageTextStream'); chatMessageTextStream.textContent = chatMessageTextStream.textContent + dataValue; chatMessageTextStream.parentNode.appendChild(newParagraph); // remove old pointer chatMessageTextStream.removeAttribute('id'); } else { chatMessageTextStream.textContent = chatMessageTextStream.textContent + dataValue; } } // scroll to the bottom const chatSection = document.getElementById('tbit__chatSection'); chatSection.scrollTop = chatSection.scrollHeight; } } } } catch (error) { console.error('Error al obtener el stream:', error); } } async function sendMsg() { const sendBtn = document.querySelector('.tbit__sendBtn'); const chatInput = document.getElementById('tbit__chatInput'); // disable textarea and send button sendBtn.setAttribute('disabled', true); chatInput.setAttribute('disabled', true); sendBtn.classList.add('tbit__sendBtnDisabled'); const chatSection = document.getElementById('tbit__chatSection'); const message = chatInput.value; chatSection.appendChild(createChatMessage(message, 'right')); chatSection.appendChild(createChatMessage('Pensando', 'left', true)); chatInput.value = ''; chatInput.style.height = 'auto'; chatInput.style.height = chatInput.scrollHeight + 'px'; // scroll to the bottom chatSection.scrollTop = chatSection.scrollHeight; await fetchStreamData(message); } function createChatWindow() { const div = document.createElement('div'); div.setAttribute('id', 'tbit__chatWindow'); if (floating) div.addEventListener('click', handleDisplayModal); div.innerHTML = `

${bubble_message}

`; div.classList.add('tbit__chatContainer'); // add send button const sendBtn = createSendIcon(); const chatBox = div.querySelector('.tbit__chatBox'); chatBox.appendChild(sendBtn); if (floating) { const closeBtn = createCloseIcon(); const chatHeader = div.querySelector('#tbit__chatHeader'); chatHeader.appendChild(closeBtn); } const chatSection = div.querySelector('#tbit__chatSection'); chatSection.appendChild(createChatMessage(initial_message, 'left')); // resize the textarea when the user types const chatInput = div.querySelector('#tbit__chatInput'); chatInput.addEventListener('input', function () { this.style.height = 'auto'; this.style.height = this.scrollHeight + 'px'; }); // send message when the user press enter chatInput.addEventListener('keydown', function (e) { if (e.key === 'Enter') { e.preventDefault(); if (this.value.length === 0) return; sendMsg(); } }); // send message when the user clicks on the send button sendBtn.addEventListener('click', function (e) { e.preventDefault(); if (chatInput.value.length === 0) return; sendMsg(); }); if (floating) { document.body.insertBefore(div, document.body.firstChild); } else { // select elment by id const chatWrapper = document.getElementById('tbit__chatWrapper'); if (chatWrapper) { chatWrapper.appendChild(div); } } // on window creation, focus on the textarea add a delay setTimeout(() => { if (floating) chatInput.focus(); }, 100); // listen for esc key to close the chat window if (floating) { window.addEventListener('keydown', function (e) { if (e.key === 'Escape') { const el = document.getElementById('tbit__chatWindow'); document.body.removeChild(el); createBubbleWindow(); } }); } // enable send button when the user types and disable it when the textarea is empty chatInput.addEventListener('input', function () { if (this.value.length > 0) { sendBtn.removeAttribute('disabled'); sendBtn.classList.remove('tbit__sendBtnDisabled'); } else { sendBtn.setAttribute('disabled', true); sendBtn.classList.add('tbit__sendBtnDisabled'); } }); } function createBubbleWindow() { const div = document.createElement('div'); div.setAttribute('id', 'tbit__bubbleWindow'); div.innerHTML = `

${bubble_message}

`; div.classList.add(`tbit__bubbleContainer`); div.classList.add( `${ position === 'right' ? 'tbit__bubbleRightPosition' : 'tbit__bubbleLeftPosition' }`, ); div.addEventListener('click', handleDisplayModal); document.body.insertBefore(div, document.body.firstChild); } function handleDisplayModal(e) { e.preventDefault(); const target = e.target.id; if (!target) return; if (target === 'tbit__chatWindow' || target === 'tbit__closeBtn') { const el = document.getElementById('tbit__chatWindow'); document?.body?.removeChild(el); createBubbleWindow(); } if (target === 'tbit__bubbleWindow' || target === 'tbit__bubbleBodyText') { const el = document.getElementById('tbit__bubbleWindow'); document.body.removeChild(el); createChatWindow(); } } function initChat() { const style = document.createElement('style'); if (position === 'fixed') { createChatWindow(); } else { createBubbleWindow(); } style.innerHTML = ` .tbit__bubbleContainer { font-family: sans-serif; padding: 15px 10px; border-radius: 25px; position: fixed; cursor: pointer; bottom: 20px; animation: fadeInAnimation 0.1s ease-in; box-shadow: 0 0 10px rgba(0,0,0,0.2); z-index: 9999; transition: all 0.1s ease-in; border: 1px solid ${primary_color}; color: ${primary_color}; background-color: ${themes[theme].background}; } .tbit__bubbleContainer:hover { transform: scale(1.05); } .tbit__bodyText { margin: 0; } .tbit__bubbleRightPosition { right: 20px; } .tbit__bubbleLeftPosition { left: 20px; } .tbit__chatContainer { font-family: sans-serif; margin: 0; ${floating ? ` background-color: rgba(0,0,0,0.4); position: fixed; z-index: 9999; right: 0; bottom: 0; left: 0; top: 0; backdrop-filter: blur(10px); animation: fadeInAnimation 0.2s ease-in; display: flex; padding-top: 10vh; justify-content: center; ` : ''} } .tbit__chatCard { background-color: ${themes[theme].background}; box-shadow: 0px 5px 5px rgba(0,0,0,0.1); padding: 10px 15px; border-radius: 12px; width: 800px; height: fit-content; } .tbit__chatHeader { display: flex; justify-content: space-between; align-items: center; padding-bottom: 25px; margin-bottom: 20px; border-bottom: 1px solid ${themes[theme].line}; } .tbit__chatTitle { margin: 0; font-size: 1rem; color: ${themes[theme].text}; font-weight: 300; } .tbit__closeBtn, .tbit__sendBtn { width: 20px; height: 20px; cursor: pointer; color: ${themes[theme].primary}; } .tbit__sendBtnDisabled { color: ${themes[theme].line}; } .tbit__chatSection { height: 300px; overflow-y: scroll; padding: 0 10px; } .tbit__chatMessage { display: flex; align-items: start; margin-bottom: 10px; gap: 25px; padding-bottom: 25px; border-bottom: 1px solid ${themes[theme].line}; } .tbit__chatMessageLeft { justify-content: flex-end; flex-direction: row-reverse; } .tbit__chatMessageRight { justify-content: flex-end; text-align: right; } .tbit__chatMessageTextContainer { max-width: 70%; } .tbit__chatMessageText { color: ${themes[theme].text}; font-size: 0.9rem; font-weight: 300; margin: 0 0 12px 0; word-wrap: break-word; line-height: 1.5; } .tbit__chatAvatarImg { widht: 35px; height: 35px; border-radius: 50%; } .tbit__chatAvatar { width: 35px; height: 35px; border-radius: 50%; color: ${themes[theme].primary}; display: flex; justify-content: center; align-items: center; } .tbit__chatBox { display: flex; align-items: center; justify-content: space-between; padding: 0 10px; gap: 10px; background-color: ${themes[theme].background}; overflow: hidden; border-radius: 5px; box-shadow: 0 5px 10px rgba(0,0,0,0.1); } .tbit__chatInput { width: 100%; height: 40px; border: none; outline: none; background: none; font-size: 0.9rem; font-family: sans-serif; font-weight: 300; color: ${themes[theme].text}; resize: none; height: 100%; min-height: 40px; max-height: 100px; } .tbit__footer { display: flex; justify-content: center; align-items: center; } .tbit__link { text-decoration: none; color: ${themes[theme].text}; font-size: 0.8rem; font-weight: 600; margin: 10px 0; } .tbit__loader { width: 32px; height: 32px; position: relative; background-image: linear-gradient(#FFF 8px, transparent 0) , linear-gradient(${primary_color} 8px, transparent 0) , linear-gradient(${primary_color} 8px, transparent 0) , linear-gradient(#FFF 8px, transparent 0); background-repeat: no-repeat; background-size: 8px 8px; background-position: left top , left bottom , right top , right bottom; animation: rotate 1s linear infinite; } @media only screen and (max-width: 768px) { .tbit__chatCard { padding: 5px 10px; border-radius: 10px; width: 100%; margin: 0 15px; } .tbit__link { font-size: 0.9rem; } } @media only screen and (max-width: 960px) { .tbit__chatCard { ${floating ? 'margin: 0 15px;' : 'margin: 0;'} } .tbit__chatSection { height: 450px; padding: 0 5px; } .tbit__chatContainer { padding-top: 5vh; } .tbit__chatMessageTextContainer { max-width: 80%; } } @keyframes fadeInAnimation { 0% { opacity: 0; } 100% { opacity: 1; } } @keyframes rotate { 0% { width: 32px; height: 32px; transform: rotate(0deg) } 50% { width: 16px; height: 16px; transform: rotate(180deg) } 100% { width: 32px; height: 32px; transform: rotate(360deg) } } `; document.body.insertBefore(style, document.body.firstChild); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initChat); } else { initChat(); }