(function(){ var C=window.chatbotConfig||{}; var B=C.botId,O=C.orgId; var A=C.apiBase||'https://api.codeweft.in'; var K=C.botKey||null; var T='light'; var W=(C.welcome||C.greeting||'Hello! How can I help you?'); var N=C.botName||'Chatbot'; var I=C.icon||''; var POS=C.position||'right'; var AUTO=C.autoOpen||false; var MODE=C.mode||'bubble'; var CONTAINER=C.containerId||'bot-inline'; var ACC=C.buttonColor||C.accent||'#2563eb'; // Contrast auto-adjust helper (prevents light accent on light card in dark mode) try{(function(){function _hexToRgb(h){h=h.replace(/#/,'');if(h.length===3){h=h.split('').map(x=>x+x).join('');}var num=parseInt(h,16);return {r:(num>>16)&255,g:(num>>8)&255,b:num&255};}function _lum(c){var r=c.r/255,g=c.g/255,b=c.b/255;[r,g,b]=[r,g,b].map(v=>{return v<=0.03928? v/12.92: Math.pow((v+0.055)/1.055,2.4);});return 0.2126*r+0.7152*g+0.0722*b;}function _isHex(x){return /^#?[0-9a-f]{3,6}$/i.test(x);}if(T==='dark'){if(_isHex(ACC)){var rgb=_hexToRgb(ACC);if(_lum(rgb)>0.7){ACC='#3b82f6';}}if(_isHex(BG)){var rgbBG=_hexToRgb(BG);if(_lum(rgbBG)>0.2){BG='#0b111a';}}if(_isHex(CARD)){var rgbCARD=_hexToRgb(CARD);var rgbBG2=_hexToRgb(BG.replace('#',''));if(Math.abs(_lum(rgbCARD)-_lum(rgbBG2))<0.04){CARD='#162131';}}}})();}catch(__){} // Dark / Light palette with stronger dark contrasts var BG=C.bg||((T==='dark')?'#0b111a':'#ffffff'); var CARD=C.card||((T==='dark')?'#162131':'#ffffff'); var TEXT=C.text||((T==='dark')?'#f1f5f9':'#0f1724'); var MUTED=C.muted||((T==='dark')?'#7a8694':'#64748b'); var BORDER=C.border||((T==='dark')?'rgba(255,255,255,0.12)':'rgba(16,24,40,0.06)'); var ME=C.bubbleMe||((T==='dark')?'linear-gradient(180deg,#3b82f6,#1e3a8a)':'linear-gradient(180deg,#2563eb,#1e40af)'); var BOT=C.bubbleBot||((T==='dark')?'rgba(255,255,255,0.06)':'#ffffff'); try{ if(T==='dark' && BOT===BG){ BOT='rgba(255,255,255,0.06)'; } }catch(__){} var SHADOW=C.shadow||((T==='dark')?'0 24px 72px rgba(0,0,0,0.65),0 8px 24px rgba(0,0,0,0.45)':'0 10px 30px rgba(0,0,0,0.15)'); var RADIUS=(C.radius!==undefined?C.radius+'px':'12px'); var LSIZE=(C.launcherSize||56); var ISCALE=(C.iconScale||60); var MLSIZE=(C.mobileLauncherSize||LSIZE); var MISCALE=(C.mobileIconScale||ISCALE); var TRANSPARENT=C.transparentBubble||false; var BN='CodeWeft'; var BL='https://codeweft.in'; if(!O){console.warn('Chatbot: OrgId missing');return;} var busy=false; var SHOW_BADGE=(C.showButtonTyping===undefined)?true:!!C.showButtonTyping; // Generate or retrieve session ID from localStorage var SESSION_KEY='chatbot_session_'+O+'_'+B; var SESSION_ID=(function(){ try{ var stored=localStorage.getItem(SESSION_KEY); if(stored){ var parsed=JSON.parse(stored); var age=Date.now()-parsed.created; if(age<24*60*60*1000)return parsed.id; } }catch(__){} var newId='sess_'+Date.now()+'_'+Math.random().toString(36).substr(2,9); try{localStorage.setItem(SESSION_KEY,JSON.stringify({id:newId,created:Date.now()}));}catch(__){} return newId; })(); var BTN_BG=TRANSPARENT?'transparent':'linear-gradient(135deg, '+ACC+', color-mix(in srgb, '+ACC+' 80%, black))'; var BTN_SHADOW=TRANSPARENT?'none':'0 8px 32px rgba(0,0,0,0.12), 0 2px 8px rgba(0,0,0,0.08)'; var BTN_BLUR=TRANSPARENT?'none':'blur(10px)'; var BTN_HOVER_SHADOW=TRANSPARENT?'none':'0 12px 36px rgba(0,0,0,0.16), 0 4px 12px rgba(0,0,0,0.12)'; // --- CSS Injection --- var __cw_css = ` :root { --cb-right: ${POS==='left'?'auto':'20px'}; --cb-left: ${POS==='left'?'20px':'auto'}; --cb-origin: ${POS==='left'?'left bottom':'right bottom'}; --cb-accent: ${ACC}; --cb-bg: ${BG}; --cb-card: ${CARD}; --cb-text: ${TEXT}; --cb-muted: ${MUTED}; --cb-border: ${BORDER}; --cb-bubble-me: ${ME}; --cb-bubble-bot: ${BOT}; --cb-shadow: ${SHADOW}; --cb-radius: ${RADIUS}; --cb-lsize: ${LSIZE}px; --cb-icon-scale: ${ISCALE}%; --cb-icon-fs: ${ISCALE*0.4}px; --cb-btn-bg: ${BTN_BG}; --cb-btn-shadow: ${BTN_SHADOW}; --cb-btn-blur: ${BTN_BLUR}; --cb-btn-hover-shadow: ${BTN_HOVER_SHADOW}; --cb-mlsize: ${MLSIZE}px; --cb-miscale: ${MISCALE}%; --cb-mode: ${T}; } :root[data-cb-theme='dark'] { color-scheme:dark; } .cb-btn { position:fixed; bottom:20px; right:var(--cb-right); left:var(--cb-left); width:var(--cb-lsize); height:var(--cb-lsize); border-radius:var(--cb-radius); border:none; background:var(--cb-btn-bg); display:flex; align-items:center; justify-content:center; cursor:pointer; z-index:99999; box-shadow:var(--cb-btn-shadow); transition:all .3s cubic-bezier(0.4, 0, 0.2, 1); backdrop-filter:var(--cb-btn-blur); -webkit-backdrop-filter:var(--cb-btn-blur); } .cb-btn:hover { transform:translateY(-2px) scale(1.05); box-shadow:var(--cb-btn-hover-shadow); } .cb-btn:active { transform:translateY(0) scale(0.98); } .cb-btn svg { width:var(--cb-icon-scale); height:var(--cb-icon-scale); display:block; fill:#fff; filter:drop-shadow(0 2px 4px rgba(0,0,0,0.1)); } .cb-btn img { width:var(--cb-icon-scale); height:var(--cb-icon-scale); object-fit:cover; filter:none !important; } .cb-emoji { font-size:var(--cb-icon-fs); line-height:1; filter:drop-shadow(0 2px 4px rgba(0,0,0,0.1)); } .cb-badge { position:absolute; top:-4px; right:-4px; min-width:24px; height:18px; padding:0 6px; border-radius:999px; display:none; align-items:center; justify-content:center; background:linear-gradient(135deg, #ef4444, #dc2626); color:#fff; box-shadow:0 4px 12px rgba(239,68,68,0.4); z-index:999999; font-size:10px; font-weight:700; } .cb-badge .dot { width:4px; height:4px; border-radius:50%; background:#fff; display:inline-block; margin:0 1px; opacity:.6; animation:badge-dot 1.2s ease-in-out infinite; } .cb-badge .dot:nth-child(2) { animation-delay:.15s; } .cb-badge .dot:nth-child(3) { animation-delay:.3s; } @keyframes badge-dot { 0%,100%{transform:translateY(0) scale(1);opacity:.6} 50%{transform:translateY(-4px) scale(1.1);opacity:1} } .cb-panel { position:fixed; bottom:85px; right:var(--cb-right); left:var(--cb-left); width:380px; max-width:calc(100vw - 24px); max-height:min(550px, calc(100vh - 120px)); border-radius:var(--cb-radius); overflow:hidden; display:none; flex-direction:column; z-index:99998; box-shadow:0 20px 60px rgba(0,0,0,0.2), 0 8px 24px rgba(0,0,0,0.12); background:var(--cb-bg); border:1px solid var(--cb-border); transform-origin:var(--cb-origin); opacity:0; transform:translateY(16px) scale(0.95); transition:all .3s cubic-bezier(0.4, 0, 0.2, 1); } @media (max-width: 480px) { .cb-panel { width:calc(100vw - 16px); max-height:calc(100vh - 100px); bottom:75px; } .cb-btn { width:var(--cb-mlsize); height:var(--cb-mlsize); bottom:16px; } } .cb-head { display:flex; align-items:center; justify-content:space-between; padding:14px 18px; border-bottom:1px solid var(--cb-border); background:linear-gradient(135deg, var(--cb-card), color-mix(in srgb, var(--cb-card) 97%, black)); backdrop-filter:blur(10px); } .cb-title { font-weight:700; font-size:15px; color:var(--cb-text); display:flex; align-items:center; gap:8px; letter-spacing:-0.01em; } .cb-body { height:min(400px, calc(100vh - 280px)); overflow-y:auto; padding:16px; display:flex; flex-direction:column; gap:12px; background:var(--cb-bg); scroll-behavior:smooth; } @media (max-width: 480px) { .cb-body { height:min(350px, calc(100vh - 220px)); padding:12px; gap:10px; } .cb-head { padding:12px 14px; } .cb-title { font-size:14px; } } .cb-body::-webkit-scrollbar { width:6px; } .cb-body::-webkit-scrollbar-track { background:transparent; } .cb-body::-webkit-scrollbar-thumb { background:var(--cb-border); border-radius:999px; } .cb-body::-webkit-scrollbar-thumb:hover { background:var(--cb-muted); } .cb-input { display:flex; gap:10px; padding:14px 16px; border-top:1px solid var(--cb-border); background:var(--cb-card); backdrop-filter:blur(10px); } .cb-input input { flex:1; padding:10px 14px; border-radius:calc(var(--cb-radius) - 4px); border:1.5px solid var(--cb-border); background:var(--cb-bg); color:var(--cb-text); outline:none; font-size:13px; transition:all .2s ease; } @media (max-width: 480px) { .cb-input { padding:12px 14px; gap:8px; } .cb-input input { padding:9px 12px; font-size:13px; } } .cb-input input:focus { border-color:var(--cb-accent); box-shadow:0 0 0 3px color-mix(in srgb, var(--cb-accent) 10%, transparent); } .cb-input input::placeholder { color:var(--cb-muted); } .cb-send { padding:10px 18px; border-radius:calc(var(--cb-radius) - 4px); border:none; background:linear-gradient(135deg, var(--cb-accent), color-mix(in srgb, var(--cb-accent) 85%, black)); color:#fff; font-weight:600; cursor:pointer; font-size:13px; transition:all .2s ease; box-shadow:0 2px 8px color-mix(in srgb, var(--cb-accent) 30%, transparent); } @media (max-width: 480px) { .cb-send { padding:9px 16px; font-size:13px; } } .cb-send:hover { transform:translateY(-1px); box-shadow:0 4px 12px color-mix(in srgb, var(--cb-accent) 40%, transparent); } .cb-send:active { transform:translateY(0); } .cb-send:disabled { opacity:0.5; cursor:not-allowed; } .cb-footer { padding:8px 14px; font-size:10px; color:var(--cb-muted); text-align:center; background:var(--cb-card); border-top:1px solid var(--cb-border); } @media (max-width: 480px) { .cb-footer { padding:6px 12px; font-size:9px; } } .cb-footer a { color:var(--cb-accent); text-decoration:none; font-weight:600; transition:opacity .2s; } .cb-footer a:hover { opacity:0.8; } .row { display:flex; width:100%; animation:slideUp .3s ease; } @keyframes slideUp { from{opacity:0;transform:translateY(8px)} to{opacity:1;transform:translateY(0)} } .bubble { max-width:82%; padding:11px 14px; border-radius:calc(var(--cb-radius) + 4px); line-height:1.5; font-size:13px; word-break:break-word; box-shadow:0 2px 12px rgba(0,0,0,0.06); position:relative; transition:all .2s ease; } @media (max-width: 480px) { .bubble { max-width:85%; padding:10px 12px; font-size:13px; } } .bubble:hover { box-shadow:0 4px 16px rgba(0,0,0,0.1); } .bubble.me { margin-left:auto; background:var(--cb-bubble-me); color:#fff; border-bottom-right-radius:6px; box-shadow:0 2px 12px color-mix(in srgb, var(--cb-accent) 20%, transparent); } .bubble.bot { margin-right:auto; background:var(--cb-bubble-bot); color:var(--cb-text); border:1.5px solid var(--cb-border); border-bottom-left-radius:6px; } .bubble pre { background:rgba(0,0,0,0.05); padding:10px 12px; border-radius:8px; overflow-x:auto; margin:8px 0; font-family:'Courier New',monospace; font-size:13px; border:1px solid var(--cb-border); } .bubble code { background:rgba(0,0,0,0.06); padding:3px 6px; border-radius:6px; font-size:13px; font-family:'Courier New',monospace; } .bubble.bot pre { background:rgba(0,0,0,0.03); color:var(--cb-text); } .bubble a { color:inherit; text-decoration:underline; font-weight:600; } .bubble img { max-width:100%; height:auto; border-radius:calc(var(--cb-radius) - 2px); display:block; } .bubble.bot img { margin:-11px -14px; } .bubble.transparent { background:transparent; border:none; padding:0; box-shadow:none; } .bubble.transparent.bot { background:transparent; border:none; } .typing { display:inline-flex; align-items:flex-end; gap:5px; padding:8px 10px; } .typing .dot { width:7px; height:7px; border-radius:50%; background:var(--cb-muted); animation:dot 1.4s ease-in-out infinite; } .typing .dot:nth-child(2){animation-delay:.2s} .typing .dot:nth-child(3){animation-delay:.4s} @keyframes dot{0%,100%{transform:translateY(0) scale(1);opacity:.5}50%{transform:translateY(-8px) scale(1.1);opacity:1}} /* Markdown styling */ .bubble h1 { font-size:18px; font-weight:800; color:var(--cb-text); margin:12px 0 8px; } .bubble h2 { font-size:16px; font-weight:700; color:var(--cb-text); margin:11px 0 7px; } .bubble h3 { font-size:14px; font-weight:700; color:var(--cb-text); margin:10px 0 6px; } .bubble h4 { font-size:13px; font-weight:700; color:var(--cb-text); margin:9px 0 5px; } .bubble h5 { font-size:12px; font-weight:700; color:var(--cb-text); margin:8px 0 4px; } .bubble h6 { font-size:11px; font-weight:700; color:var(--cb-text); margin:8px 0 4px; } .bubble p { margin:8px 0; line-height:1.6; } .bubble ul, .bubble ol { margin:8px 0; padding-left:24px; } .bubble li { margin:4px 0; line-height:1.6; } .bubble blockquote { margin:8px 0; padding:8px 0 8px 12px; border-left:3px solid var(--cb-accent); color:var(--cb-muted); font-style:italic; } .bubble strong { font-weight:700; } .bubble em { font-style:italic; } .bubble code { background:rgba(0,0,0,0.06); padding:2px 5px; border-radius:3px; font-family:'Courier New',monospace; font-size:12px; } .bubble pre { background:rgba(0,0,0,0.05); padding:10px 12px; border-radius:6px; overflow-x:auto; margin:8px 0; font-family:'Courier New',monospace; font-size:11px; border:1px solid var(--cb-border); line-height:1.4; } .bubble pre code { background:none; padding:0; border-radius:0; font-size:11px; } .bubble hr { border:none; border-top:1px solid var(--cb-border); margin:10px 0; } `; try{var s=document.createElement('style');s.innerHTML=__cw_css;document.head.appendChild(s);}catch(_){} function applyTheme(){ var root=document.documentElement; try{ var BTN_BG=TRANSPARENT?'transparent':'linear-gradient(135deg, '+ACC+', color-mix(in srgb, '+ACC+' 80%, black))'; var BTN_SHADOW=TRANSPARENT?'none':'0 8px 32px rgba(0,0,0,0.12), 0 2px 8px rgba(0,0,0,0.08)'; var BTN_BLUR=TRANSPARENT?'none':'blur(10px)'; var BTN_HOVER_SHADOW=TRANSPARENT?'none':'0 12px 36px rgba(0,0,0,0.16), 0 4px 12px rgba(0,0,0,0.12)'; root.style.setProperty('--cb-btn-bg', BTN_BG); root.style.setProperty('--cb-btn-shadow', BTN_SHADOW); root.style.setProperty('--cb-btn-blur', BTN_BLUR); root.style.setProperty('--cb-btn-hover-shadow', BTN_HOVER_SHADOW); root.style.setProperty('--cb-right', (POS==='left'?'auto':'20px')); root.style.setProperty('--cb-left', (POS==='left'?'20px':'auto')); root.style.setProperty('--cb-origin', (POS==='left'?'left bottom':'right bottom')); root.style.setProperty('--cb-accent', ACC); root.style.setProperty('--cb-bg', BG); root.style.setProperty('--cb-card', CARD); root.style.setProperty('--cb-text', TEXT); root.style.setProperty('--cb-muted', MUTED); root.style.setProperty('--cb-border', BORDER); root.style.setProperty('--cb-bubble-me', ME); root.style.setProperty('--cb-bubble-bot', BOT); root.style.setProperty('--cb-shadow', SHADOW); root.style.setProperty('--cb-radius', RADIUS); root.style.setProperty('--cb-lsize', LSIZE+'px'); root.style.setProperty('--cb-icon-scale', ISCALE+'%'); root.style.setProperty('--cb-icon-fs', (ISCALE*0.4)+'px'); root.setAttribute('data-cb-theme', T); }catch(__){ } } function refreshConfig(){ var C=window.chatbotConfig||{}; N = (C.title||C.name||C.botName||N); I = (C.icon!==undefined?C.icon:I); POS = (C.position||POS); T = (C.theme||T); ACC=(C.buttonColor||C.accent||ACC); BG=(C.bg||BG); CARD=(C.card||CARD); TEXT=(C.text||TEXT); MUTED=(C.muted||MUTED); BORDER=(C.border||BORDER); ME=(C.bubbleMe||ME); BOT=(C.bubbleBot||BOT); SHADOW=(C.shadow||SHADOW); RADIUS=(C.radius!==undefined?(C.radius+'px'):RADIUS); LSIZE=(C.launcherSize||LSIZE); ISCALE=(C.iconScale||ISCALE); TRANSPARENT=(C.transparentBubble!==undefined?C.transparentBubble:TRANSPARENT); applyTheme(); try{ alignPanel(); }catch(__){ } try{ var t=panel.querySelector('.cb-title'); if(t){ t.textContent=N||'Chatbot'; } }catch(__){ } var BTN_BG=TRANSPARENT?'transparent':'linear-gradient(135deg, '+ACC+', color-mix(in srgb, '+ACC+' 80%, black))'; var BTN_SHADOW=TRANSPARENT?'none':'0 8px 32px rgba(0,0,0,0.12), 0 2px 8px rgba(0,0,0,0.08)'; var BTN_BLUR=TRANSPARENT?'none':'blur(10px)'; var BTN_HOVER_SHADOW=TRANSPARENT?'none':'0 12px 36px rgba(0,0,0,0.16), 0 4px 12px rgba(0,0,0,0.12)'; applyTheme(); if(footer){ footer.style.display='block'; footer.innerHTML='Powered by CodeWeft'; try{ Object.defineProperty(footer, 'innerHTML', { writable: false, configurable: false }); Object.defineProperty(footer.style, 'display', { value: 'block', writable: false, configurable: false }); }catch(__){ } } root.style.setProperty('--cb-btn-bg', BTN_BG); root.style.setProperty('--cb-btn-shadow', BTN_SHADOW); root.style.setProperty('--cb-btn-blur', BTN_BLUR); root.style.setProperty('--cb-btn-hover-shadow', BTN_HOVER_SHADOW); } // --- Simple but effective Markdown Parser --- function md(s){ var html = String(s || ''); // Escape HTML to prevent XSS html = html.replace(/&/g, '&').replace(//g, '>'); // Headers html = html.replace(/^### (.+)$/gm, '

$1

'); html = html.replace(/^## (.+)$/gm, '

$1

'); html = html.replace(/^# (.+)$/gm, '

$1

'); // Code blocks html = html.replace(/```([\s\S]*?)```/g, '
$1
'); // Inline code html = html.replace(/`([^`]+)`/g, '$1'); // Bold html = html.replace(/\*\*(.+?)\*\*/g, '$1'); // Italic html = html.replace(/\*(.+?)\*/g, '$1'); // Links html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1'); // Blockquotes html = html.replace(/^> (.+)$/gm, '
$1
'); // Lists - unordered html = html.replace(/^[\*\-] (.+)$/gm, '
  • $1
  • '); html = html.replace(/(
  • .*<\/li>)/s, ''); // Horizontal rule html = html.replace(/^[-=*]{3,}$/gm, '
    '); // Paragraphs html = html.replace(/\n\n+/g, '

    '); html = '

    ' + html + '

    '; // Line breaks within paragraphs html = html.replace(/([^<])\n([^<])/g, '$1
    $2'); return {html: html, isImageOnly: false}; } function normalizeWords(t){ if(!t) return t; // Only normalize within words, not after punctuation // This preserves spaces after periods, commas, exclamation marks, etc. // Remove spaces within hyphenated words that got split: e.g. 'AI - powered' -> 'AI-powered' t=t.replace(/([a-z])-\s+([a-z])/g,'$1-$2'); return t; } function joinToken(acc,t){ var token=String(t||''); if(!acc) return token; return acc+token; } // --- UI Elements --- var btn = document.createElement('button'); btn.className = 'cb-btn'; btn.setAttribute('aria-label', 'Open chat'); // **UPDATED ICON LOGIC** // 1. Check if Icon is URL (http/data). 2. Check if Icon exists (Emoji/Text). 3. Default SVG. if(I && (I.indexOf('http')===0 || I.indexOf('https')===0 || I.indexOf('data:image')===0 || I.indexOf('/')===0)){ var img = document.createElement('img'); img.src=I; img.style.objectFit='cover'; if(TRANSPARENT){ img.style.width=ISCALE+'%'; img.style.height=ISCALE+'%'; img.style.borderRadius=RADIUS; } else { img.style.width=ISCALE+'%'; img.style.height=ISCALE+'%'; img.style.borderRadius='50%'; } btn.appendChild(img); } else if (I) { var spn = document.createElement('span'); spn.className='cb-emoji'; spn.textContent=I; btn.appendChild(spn); } else { btn.innerHTML = ''; } if(MODE!=='inline'){ document.body.appendChild(btn); } var badge = document.createElement('span'); badge.className='cb-badge'; badge.innerHTML=''; btn.appendChild(badge); var panel = document.createElement('div'); panel.className = 'cb-panel'; panel.innerHTML = `
    `+(I?''+(I.indexOf('http')===0||I.indexOf('https')===0||I.indexOf('/')===0?'':I)+' ':'') + N +`
    `; var mount = (MODE==='inline' ? (document.getElementById(CONTAINER)||document.body) : document.body); mount.appendChild(panel); var body = panel.querySelector('.cb-body'); var input = panel.querySelector('input'); var sendBtn = panel.querySelector('.cb-send'); var closeBtn = panel.querySelector('.cb-close'); var footer = panel.querySelector('.cb-footer'); if(footer){ footer.style.display='block'; footer.innerHTML='Powered by CodeWeft'; try{ Object.defineProperty(footer,'innerHTML',{writable:false,configurable:false}); Object.defineProperty(footer.style,'display',{value:'block',writable:false,configurable:false}); }catch(__){} } var shownWelcome=false; var opened=false; function getW(){ var C=window.chatbotConfig||{}; return (C.welcome||C.greeting||''); } // --- Logic --- function alignPanel(){ var br = btn.getBoundingClientRect(); if(POS==='left'){ btn.style.left='24px'; btn.style.right='auto'; panel.style.left=Math.max(8,br.left)+'px'; panel.style.right='auto'; panel.style.transformOrigin='left bottom'; } else { btn.style.right='24px'; btn.style.left='auto'; panel.style.right=Math.max(8,window.innerWidth-br.right)+'px'; panel.style.left='auto'; panel.style.transformOrigin='right bottom'; } panel.style.bottom = (window.innerHeight - br.top + 12) + 'px'; } setTimeout(alignPanel, 100); window.addEventListener('resize', alignPanel); function open(){ alignPanel(); panel.style.display='flex'; requestAnimationFrame(function(){ panel.style.opacity='1'; panel.style.transform='translateY(0) scale(1)'; }); var W0=getW(); if(body.childNodes.length===0 && W0){ addMsg('bot', W0); shownWelcome=true; } opened=true; input.focus(); } function close(){ opened=false; panel.style.opacity='0'; panel.style.transform='translateY(10px) scale(0.98)'; setTimeout(function(){ panel.style.display='none'; }, 200); } function addMsg(type, text){ var r = document.createElement('div'); r.className='row'; var b = document.createElement('div'); b.className='bubble '+(type==='me'?'me':'bot'); if(text){ var mdResult = md(text); b.innerHTML = mdResult.html; if(mdResult.isImageOnly) b.classList.add('transparent'); } r.appendChild(b); body.appendChild(r); body.scrollTop = body.scrollHeight; return b; } function openPopup(u){ body.style.display='none'; var inpDiv=panel.querySelector('.cb-input'); if(inpDiv)inpDiv.style.display='none'; if(footer)footer.style.display='none'; panel.style.height = '550px'; var frm=document.createElement('div'); frm.className='cb-form-layer'; frm.style.flex='1'; frm.style.display='flex'; frm.style.flexDirection='column'; frm.style.background=BG; var hd=document.createElement('div'); hd.style.padding='8px 12px'; hd.style.borderBottom='1px solid '+BORDER; hd.style.display='flex'; hd.style.alignItems='center'; hd.style.gap='10px'; hd.style.background=CARD; var back=document.createElement('button'); back.innerHTML='← Back'; back.style.background='transparent'; back.style.border='none'; back.style.color=ACC; back.style.fontWeight='600'; back.style.cursor='pointer'; back.style.fontSize='13px'; var title=document.createElement('span'); title.style.fontWeight='600'; title.style.fontSize='14px'; title.textContent=(u.indexOf('reschedule')>-1?'Reschedule':'Booking'); hd.appendChild(back); hd.appendChild(title); frm.appendChild(hd); var fr=document.createElement('iframe'); fr.src=u+(u.indexOf('?')>-1?'&':'?')+'session_id='+encodeURIComponent(SESSION_ID); fr.style.flex='1'; fr.style.border='none'; frm.appendChild(fr); panel.insertBefore(frm, footer); function closeForm(){ frm.remove(); body.style.display='flex'; if(inpDiv)inpDiv.style.display='flex'; if(footer)footer.style.display='block'; panel.style.height = ''; window._cbCloseForm=null; } back.onclick=closeForm; window._cbCloseForm=closeForm; } body.addEventListener('click', function(e){ var a=e.target.closest('a'); if(!a) return; var href=a.getAttribute('href')||''; if(href.indexOf('/api/form/')>-1 || href.indexOf('/api/reschedule/')>-1){ e.preventDefault(); openPopup(href); } }); window.addEventListener('message', function(e){ var d=e.data; if(d && (d.type==='appointment-booked'||d.type==='BOOKING_SUCCESS'||d.type==='RESCHEDULE_SUCCESS'||d.type==='RESCHEDULE_BLOCKED'||d.type==='LEAD_SUBMITTED')){ if(d.message){ addMsg('bot', d.message); }else if(d.type==='RESCHEDULE_SUCCESS'){ addMsg('bot', 'Rescheduled your appointment to '+(d.start||'')+' - '+(d.end||'')+'. ID: '+(d.id||'')); }else if(d.type==='LEAD_SUBMITTED'){ addMsg('bot', 'Thanks! Your enquiry has been submitted. Reference ID: '+(d.id||'')); }else{ addMsg('bot', 'Booked your appointment for '+(d.start||'')+' to '+(d.end||'')+'. ID: '+d.id); } if(window._cbCloseForm){ window._cbCloseForm(); } try{ var ov=document.querySelector('.cb-popup'); if(ov){ ov.remove(); } }catch(__){} } }); function setBadge(on){ if(!SHOW_BADGE) return; badge.style.display = on ? 'inline-flex' : 'none'; } function sendApi(m, onchunk){ var h={'Content-Type':'application/json'}; if(K) h['X-Bot-Key']=K; var payload=JSON.stringify({message:m, org_id:O, session_id:SESSION_ID}); fetch(A+'/api/chat/stream/'+B, {method:'POST',headers:h,body:payload}) .then(function(r){ var rd=r.body.getReader(); var d=new TextDecoder(); var buf=''; function pump(){ rd.read().then(function(x){ if(x.done){ onchunk(null,true); return; } buf += d.decode(x.value); var idx=buf.indexOf('\n\n'); while(idx>-1){ var l=buf.slice(0,idx); buf=buf.slice(idx+2); if(l.indexOf('data: ')===0){ onchunk(l.replace(/^data:\s*/,''), false); } else if(l.indexOf('event: end')===0){ onchunk(null,true); } idx=buf.indexOf('\n\n'); } pump(); }); } pump(); }).catch(function(e){ onchunk('Error connecting.', true); }); } function doSend(){ if(busy) return; var txt = input.value.trim(); if(!txt) return; var m0 = txt.toLowerCase(); var isGreet = (m0==='hi'||m0==='hello'||m0==='hey'||m0==='hola'||m0==='hii'||m0.startsWith('hi ')||m0.startsWith('hello ')||m0.startsWith('hey ')); var W0=getW(); if(isGreet && W0 && !shownWelcome){ input.value=''; addMsg('me', txt); addMsg('bot', W0); shownWelcome=true; input.focus(); return; } busy = true; input.value=''; input.disabled=true; sendBtn.disabled=true; addMsg('me', txt); var botRow = document.createElement('div'); botRow.className='row'; var botBub = document.createElement('div'); botBub.className='bubble bot'; botBub.innerHTML = '
    '; botRow.appendChild(botBub); body.appendChild(botRow); body.scrollTop=body.scrollHeight; setBadge(true); var acc = ''; sendApi(txt, function(token, end){ if(end){ // ChatGPT style: render markdown only when stream ends // Parse JSON-encoded response to get actual newlines try { acc = JSON.parse(acc); } catch(e) { } // Use marked.js for proper markdown rendering (already loaded via CDN) var htmlResult = (typeof marked !== 'undefined' && marked.parse) ? marked.parse(acc) : md(acc); if (typeof htmlResult === 'string') { botBub.innerHTML = htmlResult; } else if (htmlResult && htmlResult.html) { botBub.innerHTML = htmlResult.html; } botBub.classList.remove('transparent'); body.scrollTop = body.scrollHeight; busy=false; input.disabled=false; sendBtn.disabled=false; setBadge(false); input.focus(); return; } // Accumulate tokens without rendering mid-stream if(acc==='') botBub.innerHTML='
    '; acc = joinToken(acc, token); }); } if(MODE!=='inline'){ btn.onclick = function(){ if(opened) close(); else open(); }; } closeBtn.onclick = close; sendBtn.onclick = doSend; input.onkeydown = function(e){ if(e.key==='Enter' || e.keyCode===13){ e.preventDefault(); doSend(); } }; function isUuid(s){return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s);} function init(){ fetch(A+'/api/bots?org_id='+encodeURIComponent(O)) .then(r=>r.json()).then(d=>{ if(d.bots && d.bots.length > 0) { var found=null; if(B && isUuid(B)){ found = d.bots.find(function(x){return x.bot_id===B;}) || d.bots[0]; } else { B=d.bots[0].bot_id; found=d.bots[0]; } var C=window.chatbotConfig||{}; if(!(C.welcome||C.greeting) && found.welcome_message){ W=found.welcome_message; window.chatbotConfig=Object.assign({},C,{welcome:found.welcome_message}); } if(AUTO && !opened){ setTimeout(function(){ open(); }, 10); } } }).catch(e=>{}); } init(); if(MODE==='inline'){ AUTO=true; } if(AUTO){ setTimeout(function(){ open(); }, 100); } })();