AI_Robot / static /widget.js
Sanjay kumar K
Slow bot eye and antenna animations
00fc62e
Raw
History Blame Contribute Delete
25 kB
/**
* Vibhu Solutions AI Chat Widget
* Embed on any website with:
* <script src="https://sanjay8013-ai-robot.hf.space/static/widget.js"></script>
*/
(function () {
if (document.getElementById('vs-cb-root')) return; // prevent double-load
const API_URL = 'https://sanjay8013-ai-robot.hf.space/chat';
// ── Bot SVG (blinking robot character) ───────────────────────────────────────
const BOT_SVG = (w, h) => `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 56 64" width="${w}" height="${h}"><line x1="28" y1="6" x2="28" y2="14" stroke="rgba(255,255,255,0.85)" stroke-width="3.5" stroke-linecap="round"/><circle cx="28" cy="4" r="4.5" fill="white" class="vs-bot-ant"/><rect x="4" y="12" width="48" height="44" rx="14" fill="rgba(255,255,255,0.18)" stroke="rgba(255,255,255,0.5)" stroke-width="1.5"/><ellipse cx="19" cy="31" rx="7.5" ry="8.5" fill="white" class="vs-bot-eye"/><ellipse cx="37" cy="31" rx="7.5" ry="8.5" fill="white" class="vs-bot-eye vs-bot-eye-r"/><circle cx="19" cy="32" r="4" fill="#0044CC"/><circle cx="37" cy="32" r="4" fill="#0044CC"/><circle cx="21.5" cy="29" r="1.8" fill="rgba(255,255,255,0.75)"/><circle cx="39.5" cy="29" r="1.8" fill="rgba(255,255,255,0.75)"/><path d="M 14 46 Q 28 57 42 46" stroke="white" stroke-width="2.8" fill="none" stroke-linecap="round"/></svg>`;
// ── CSS ──────────────────────────────────────────────────────────────────────
// Inject Manrope font
if (!document.getElementById('vs-cb-manrope')) {
const lnk = document.createElement('link');
lnk.id = 'vs-cb-manrope';
lnk.rel = 'stylesheet';
lnk.href = 'https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700;800&display=swap';
document.head.appendChild(lnk);
}
const style = document.createElement('style');
style.textContent = `
#vs-cb-overlay {
position:fixed; inset:0;
background:rgba(0,4,12,0.82); backdrop-filter:blur(14px);
display:flex; align-items:center; justify-content:center;
z-index:2147483640; font-family:'Manrope','Segoe UI',Tahoma,sans-serif;
}
#vs-cb-card {
background:rgba(4,14,31,0.93);
backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px);
border:1px solid rgba(107,166,255,0.22);
border-radius:24px;
padding:40px 34px 32px; width:100%; max-width:400px; margin:16px;
box-shadow:0 32px 80px rgba(0,0,0,0.8),0 0 60px rgba(0,66,200,0.18);
animation:vsCbSlide 0.32s ease;
}
@keyframes vsCbSlide {
from{opacity:0;transform:translateY(22px);}
to {opacity:1;transform:translateY(0);}
}
.vs-logo {
width:72px;height:72px;
background:linear-gradient(107.8deg, #6BA6FF 3.37%, #0066FF 95.93%);
border-radius:18px;display:flex;align-items:center;justify-content:center;
margin:0 auto 18px;
box-shadow:0 10px 26px rgba(0,102,255,0.45);
}
#vs-cb-card h2 {
text-align:center;color:white;font-size:20px;font-weight:700;
margin-bottom:6px;font-family:'Manrope','Segoe UI',sans-serif;
}
.vs-subtitle {
text-align:center;color:rgba(255,255,255,0.55);font-size:13px;
margin-bottom:22px;line-height:1.6;
}
.vs-field{margin-bottom:13px;}
.vs-field label{
display:block;font-size:12.5px;font-weight:600;
color:rgba(255,255,255,0.78);margin-bottom:6px;font-family:'Manrope','Segoe UI',sans-serif;
}
.vs-field input{
width:100%;border:1.5px solid rgba(107,166,255,0.22);border-radius:9px;
padding:11px 14px;font-size:13.5px;color:white;outline:none;
background:rgba(255,255,255,0.07);box-sizing:border-box;
transition:border-color 0.2s,box-shadow 0.2s,background 0.2s;
font-family:'Manrope','Segoe UI',sans-serif;
}
.vs-field input::placeholder{color:rgba(255,255,255,0.3);}
.vs-field input:focus{
border-color:#6BA6FF;
box-shadow:0 0 0 3px rgba(0,102,255,0.18);
background:rgba(107,166,255,0.1);
}
.vs-field input.vs-err{border-color:#ef4444;}
#vs-cb-login-btn{
width:100%;padding:13px;margin-top:4px;
background:linear-gradient(107.8deg, #6BA6FF 3.37%, #0066FF 95.93%);
color:white;border:none;border-radius:9px;
font-size:14px;font-weight:600;cursor:pointer;
letter-spacing:0.3px;transition:opacity 0.2s;
font-family:'Manrope','Segoe UI',sans-serif;
box-shadow:0 6px 20px rgba(0,102,255,0.45);
}
#vs-cb-login-btn:hover{opacity:0.9;}
.vs-privacy{text-align:center;font-size:11px;color:rgba(255,255,255,0.35);margin-top:12px;}
#vs-cb-toggle{
position:fixed;bottom:20px;right:20px;
width:48px;height:48px;border-radius:50%;overflow:visible;
background:linear-gradient(107.8deg, #6BA6FF 3.37%, #0066FF 95.93%);
border:none;cursor:pointer;color:white;
box-shadow:0 4px 18px rgba(0,102,255,0.55),0 0 14px rgba(0,102,255,0.3);
z-index:2147483638;
display:none;align-items:center;justify-content:center;
}
#vs-cb-widget{
display:none;flex-direction:column;
position:fixed;bottom:78px;right:20px;
width:310px;height:460px;
background:#04111e;
border:1px solid rgba(107,166,255,0.2);
border-radius:16px;
box-shadow:0 12px 48px rgba(0,0,0,0.7),0 0 30px rgba(0,66,200,0.15);
z-index:2147483637;overflow:hidden;
font-family:'Manrope','Segoe UI',Tahoma,sans-serif;
animation:vsCbPop 0.24s ease;
}
@keyframes vsCbPop{
from{opacity:0;transform:scale(0.95) translateY(10px);}
to {opacity:1;transform:scale(1) translateY(0);}
}
#vs-cb-header{
background:linear-gradient(107.8deg, #6BA6FF 3.37%, #0066FF 95.93%);
color:white;padding:10px 12px;
display:flex;align-items:center;gap:8px;flex-shrink:0;
}
.vs-avatar{
width:30px;height:30px;background:rgba(255,255,255,0.22);
border-radius:50%;display:flex;align-items:center;
justify-content:center;flex-shrink:0;
}
.vs-hinfo h3{font-size:12px;font-weight:700;margin:0;}
.vs-hinfo p {font-size:10px;opacity:0.82;margin:1px 0 0;}
#vs-cb-utag{
font-size:11px;background:rgba(255,255,255,0.2);
border-radius:12px;padding:3px 10px;margin-left:auto;
white-space:nowrap;max-width:105px;
overflow:hidden;text-overflow:ellipsis;
}
#vs-cb-dot{
width:9px;height:9px;background:#4ade80;
border-radius:50%;flex-shrink:0;
animation:vsPulse 2s infinite;
}
@keyframes vsPulse{
0% {box-shadow:0 0 0 0 rgba(74,222,128,0.5);}
70% {box-shadow:0 0 0 7px rgba(74,222,128,0);}
100%{box-shadow:0 0 0 0 rgba(74,222,128,0);}
}
#vs-cb-msgs{
flex:1;overflow-y:auto;padding:10px;
display:flex;flex-direction:column;gap:8px;
background:#030c1a;scroll-behavior:smooth;
}
#vs-cb-msgs::-webkit-scrollbar{width:4px;}
#vs-cb-msgs::-webkit-scrollbar-thumb{background:rgba(107,166,255,0.3);border-radius:2px;}
.vs-wrap{display:flex;flex-direction:column;}
.vs-wrap.user{align-items:flex-end;}
.vs-wrap.bot{flex-direction:row;align-items:flex-end;gap:8px;}
.vs-wrap.bot .vs-msg-col{display:flex;flex-direction:column;align-items:flex-start;}
.vs-bot-mini{
width:22px;height:22px;flex-shrink:0;border-radius:50%;
background:linear-gradient(107.8deg,#6BA6FF 3.37%,#0066FF 95.93%);
display:flex;align-items:center;justify-content:center;align-self:flex-end;
box-shadow:0 2px 6px rgba(0,102,255,0.4);
}
.vs-msg{
max-width:86%;padding:8px 11px;border-radius:12px;
font-size:12px;line-height:1.55;
animation:vsFade 0.2s ease;
}
@keyframes vsFade{
from{opacity:0;transform:translateY(5px);}
to {opacity:1;transform:none;}
}
.vs-msg.bot{
background:rgba(255,255,255,0.07);color:#d0e8ff;
border:1px solid rgba(107,166,255,0.12);
border-bottom-left-radius:4px;
}
.vs-msg.bot a{color:#6BA6FF;text-decoration:none;font-weight:500;}
.vs-msg.bot a:hover{text-decoration:underline;}
.vs-msg.user{
background:linear-gradient(107.8deg, #6BA6FF 3.37%, #0066FF 95.93%);
color:white;border-bottom-right-radius:4px;
}
.vs-time{font-size:10.5px;color:rgba(255,255,255,0.3);margin-top:4px;padding:0 4px;}
.vs-msg.typing{
background:rgba(255,255,255,0.07);
border:1px solid rgba(107,166,255,0.12);
border-bottom-left-radius:4px;
padding:13px 16px;
}
.vs-dots{display:flex;gap:5px;align-items:center;}
.vs-dots span{
width:7px;height:7px;background:rgba(107,166,255,0.7);border-radius:50%;
animation:vsDot 1.3s infinite ease-in-out;
}
.vs-dots span:nth-child(2){animation-delay:0.18s;}
.vs-dots span:nth-child(3){animation-delay:0.36s;}
@keyframes vsDot{
0%,60%,100%{transform:translateY(0);}
30% {transform:translateY(-8px);}
}
#vs-cb-quick{
display:flex;flex-wrap:wrap;gap:4px;
padding:0 10px 7px;background:#030c1a;flex-shrink:0;
}
.vs-qbtn{
background:rgba(0,102,255,0.12);border:1px solid rgba(107,166,255,0.35);color:#6BA6FF;
border-radius:14px;padding:3px 10px;font-size:10.5px;
cursor:pointer;transition:all 0.18s;white-space:nowrap;
font-family:'Manrope','Segoe UI',sans-serif;
}
.vs-qbtn:hover{background:#0066FF;color:white;border-color:#0066FF;}
#vs-cb-inputarea{
display:flex;padding:7px 9px;gap:6px;
border-top:1px solid rgba(107,166,255,0.15);background:#04111e;flex-shrink:0;
}
#vs-cb-input{
flex:1;border:1px solid rgba(107,166,255,0.2);border-radius:18px;
padding:7px 12px;font-size:12px;outline:none;color:white;
background:rgba(255,255,255,0.06);
transition:border-color 0.2s,background 0.2s;resize:none;max-height:60px;
font-family:'Manrope','Segoe UI',sans-serif;
}
#vs-cb-input::placeholder{color:rgba(255,255,255,0.3);}
#vs-cb-input:focus{border-color:#6BA6FF;background:rgba(107,166,255,0.08);}
#vs-cb-send{
width:30px;height:30px;flex-shrink:0;
background:linear-gradient(107.8deg, #6BA6FF 3.37%, #0066FF 95.93%);
border:none;border-radius:50%;cursor:pointer;
color:white;font-size:13px;
display:flex;align-items:center;justify-content:center;
transition:opacity 0.2s;
box-shadow:0 3px 10px rgba(0,102,255,0.4);
}
#vs-cb-send:hover{opacity:0.88;}
#vs-cb-send:disabled{opacity:0.4;cursor:not-allowed;}
/* ── Bot Animations ─────────────────────────────────────────────── */
.vs-bot-eye{
transform-box:fill-box;transform-origin:50% 50%;
animation:vsBotBlink 4.8s ease-in-out infinite;
}
.vs-bot-eye-r{animation-delay:0.12s;}
@keyframes vsBotBlink{
0%,82%,100%{transform:scaleY(1);}
84%{transform:scaleY(0.18);}
86%{transform:scaleY(1);}
88%{transform:scaleY(0.08);}
90%{transform:scaleY(1);}
}
.vs-bot-ant{
transform-box:fill-box;transform-origin:50% 50%;
animation:vsAntGlow 2.4s ease-in-out infinite;
}
@keyframes vsAntGlow{
0%,100%{opacity:0.4;transform:scale(0.8);filter:drop-shadow(0 0 2px rgba(255,255,255,0.4));}
45%{opacity:1;transform:scale(1.15);filter:drop-shadow(0 0 5px white);}
70%{opacity:0.65;transform:scale(0.9);filter:drop-shadow(0 0 3px rgba(255,255,255,0.55));}
}
#vs-cb-toggle{animation:vsBotFloat 2s ease-in-out infinite;}
#vs-cb-toggle:hover{animation:none;transform:scale(1.12);}
@keyframes vsBotFloat{
0%,100%{transform:translateY(0);}
50%{transform:translateY(-8px);}
}
#vs-cb-toggle::after{
content:'';position:absolute;inset:-4px;border-radius:50%;
border:2px solid rgba(107,166,255,0.6);
animation:vsRingPulse 1.2s ease-out infinite;
}
#vs-cb-toggle::before{
content:'';position:absolute;inset:-10px;border-radius:50%;
border:1.5px solid rgba(107,166,255,0.25);
animation:vsRingPulse 1.2s ease-out infinite 0.4s;
}
@keyframes vsRingPulse{
0%{transform:scale(1);opacity:0.8;}
100%{transform:scale(1.35);opacity:0;}
}
@media(max-width:480px){
#vs-cb-widget{
bottom:0;right:0;left:0;
width:100%;height:100%;border-radius:0;
}
#vs-cb-toggle{bottom:16px;right:16px;}
}
`;
document.head.appendChild(style);
// ── HTML ─────────────────────────────────────────────────────────────────────
const root = document.createElement('div');
root.id = 'vs-cb-root';
root.innerHTML = `
<div id="vs-cb-overlay">
<div id="vs-cb-card">
<div class="vs-logo">${BOT_SVG(44,50)}</div>
<h2>Vibhu Solutions AI</h2>
<p class="vs-subtitle">Enter your details to chat with our<br>intelligent business AI assistant</p>
<div class="vs-field">
<label>Full Name <span style="color:#ef4444">*</span></label>
<input id="vs-ln" type="text" placeholder="Your full name" autocomplete="name"/>
</div>
<div class="vs-field">
<label>Email Address <span style="color:#ef4444">*</span></label>
<input id="vs-le" type="email" placeholder="your@email.com" autocomplete="email"/>
</div>
<div class="vs-field">
<label>Phone / WhatsApp
<span style="font-weight:400;color:#9ca3af;font-size:11px">(optional)</span>
</label>
<input id="vs-lp" type="tel" placeholder="+91 9XXXXXXXXX" autocomplete="tel"/>
</div>
<button id="vs-cb-login-btn">Start Chatting β†’</button>
<p class="vs-privacy">πŸ”’ Your information is kept private and secure.</p>
</div>
</div>
<button id="vs-cb-toggle" title="Chat with us">${BOT_SVG(26,29)}</button>
<div id="vs-cb-widget">
<div id="vs-cb-header">
<div class="vs-avatar">${BOT_SVG(20,23)}</div>
<div class="vs-hinfo">
<h3>Vibhu Solutions AI</h3>
<p>Ask me about our services</p>
</div>
<span id="vs-cb-utag"></span>
<div id="vs-cb-dot"></div>
</div>
<div id="vs-cb-msgs"></div>
<div id="vs-cb-quick">
<button class="vs-qbtn" data-msg="What services do you offer?">Services</button>
<button class="vs-qbtn" data-msg="How do I start a project with you?">Start Project</button>
<button class="vs-qbtn" data-msg="How can I contact your team?">Contact</button>
<button class="vs-qbtn" data-msg="How long does a project take?">Timeline</button>
</div>
<div id="vs-cb-inputarea">
<textarea id="vs-cb-input" rows="1"
placeholder="Ask anything about our services…"></textarea>
<button id="vs-cb-send">➀</button>
</div>
</div>
`;
document.body.appendChild(root);
// ── State ────────────────────────────────────────────────────────────────────
let cbOpen = false, cbTypingEl = null, cbUser = null;
// ── Format bot message (clickable links / emails / phones) ───────────────────
function formatMsg(text) {
let h = text
.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
h = h.replace(/\*\*([^*\n]+)\*\*/g, '<strong>$1</strong>');
h = h.replace(/(https?:\/\/[^\s&<]+)/g,
'<a href="$1" target="_blank" rel="noopener">$1</a>');
h = h.replace(/([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/g,
'<a href="mailto:$1">$1</a>');
h = h.replace(/(\+91[\s]?[0-9]{10})/g,
'<a href="tel:$1">$1</a>');
h = h.replace(/\n/g,'<br>');
return h;
}
// ── Add message ──────────────────────────────────────────────────────────────
function addMsg(content, type, isHTML) {
const msgs = document.getElementById('vs-cb-msgs');
const wrap = document.createElement('div');
wrap.className = 'vs-wrap ' + type;
if (type === 'bot') {
const mini = document.createElement('div');
mini.className = 'vs-bot-mini';
mini.innerHTML = BOT_SVG(14, 16);
wrap.appendChild(mini);
const col = document.createElement('div');
col.className = 'vs-msg-col';
const div = document.createElement('div');
div.className = 'vs-msg bot';
if (isHTML) div.innerHTML = content;
else div.textContent = content;
col.appendChild(div);
const t = document.createElement('span');
t.className = 'vs-time';
t.textContent = new Date().toLocaleTimeString([], {hour:'2-digit', minute:'2-digit'});
col.appendChild(t);
wrap.appendChild(col);
} else {
const div = document.createElement('div');
div.className = 'vs-msg ' + type;
if (isHTML) div.innerHTML = content;
else div.textContent = content;
wrap.appendChild(div);
const t = document.createElement('span');
t.className = 'vs-time';
t.textContent = new Date().toLocaleTimeString([], {hour:'2-digit', minute:'2-digit'});
wrap.appendChild(t);
}
msgs.appendChild(wrap);
msgs.scrollTop = msgs.scrollHeight;
return wrap;
}
// ── Animated typing dots ─────────────────────────────────────────────────────
function showTyping() {
const msgs = document.getElementById('vs-cb-msgs');
const wrap = document.createElement('div');
wrap.className = 'vs-wrap bot';
const mini = document.createElement('div');
mini.className = 'vs-bot-mini';
mini.innerHTML = BOT_SVG(18, 20);
wrap.appendChild(mini);
const col = document.createElement('div');
col.className = 'vs-msg-col';
const div = document.createElement('div');
div.className = 'vs-msg typing';
div.innerHTML = '<div class="vs-dots"><span></span><span></span><span></span></div>';
col.appendChild(div);
wrap.appendChild(col);
msgs.appendChild(wrap);
msgs.scrollTop = msgs.scrollHeight;
cbTypingEl = wrap;
}
function hideTyping() {
if (cbTypingEl) { cbTypingEl.remove(); cbTypingEl = null; }
}
// ── Login ────────────────────────────────────────────────────────────────────
function doLogin() {
const nameEl = document.getElementById('vs-ln');
const emailEl = document.getElementById('vs-le');
const name = nameEl.value.trim();
const email = emailEl.value.trim();
const phone = document.getElementById('vs-lp').value.trim();
nameEl.classList.remove('vs-err');
emailEl.classList.remove('vs-err');
let ok = true;
if (!name) { nameEl.classList.add('vs-err'); nameEl.focus(); ok = false; }
if (!email || !email.includes('@')) {
emailEl.classList.add('vs-err');
if (ok) emailEl.focus();
ok = false;
}
if (!ok) return;
cbUser = { name, email, phone,
session_id: 'vs_' + Date.now() + '_' + Math.random().toString(36).slice(2,8) };
try { localStorage.setItem('vs_cb_user', JSON.stringify(cbUser)); } catch(_){}
document.getElementById('vs-cb-overlay').style.display = 'none';
document.getElementById('vs-cb-toggle').style.display = 'flex';
cbToggle();
}
// ── Restore session ──────────────────────────────────────────────────────────
try {
const saved = localStorage.getItem('vs_cb_user');
if (saved) {
cbUser = JSON.parse(saved);
document.getElementById('vs-cb-overlay').style.display = 'none';
document.getElementById('vs-cb-toggle').style.display = 'flex';
}
} catch(_) {}
// ── Toggle widget ────────────────────────────────────────────────────────────
function cbToggle() {
cbOpen = !cbOpen;
const w = document.getElementById('vs-cb-widget');
const btn = document.getElementById('vs-cb-toggle');
w.style.display = cbOpen ? 'flex' : 'none';
btn.innerHTML = cbOpen ? 'βœ•' : BOT_SVG(26, 29);
if (cbUser)
document.getElementById('vs-cb-utag').textContent =
'πŸ‘€ ' + cbUser.name.split(' ')[0];
if (cbOpen && document.getElementById('vs-cb-msgs').children.length === 0) {
const fn = cbUser ? cbUser.name.split(' ')[0] : '';
const greeting = fn
? `Hi ${fn}! πŸ‘‹ Welcome to Vibhu Solutions.\n\nI'm your AI assistant. I can help you with:\nβ€’ Our services & technologies\nβ€’ How to start a project\nβ€’ Our process & timelines\nβ€’ How to contact our team\n\nWhat can I help you with today?`
: `Hello! πŸ‘‹ Welcome to Vibhu Solutions.\n\nAsk me about our services or how to get started!`;
addMsg(greeting, 'bot', false);
}
if (cbOpen) document.getElementById('vs-cb-input').focus();
}
// ── API call with timeout ────────────────────────────────────────────────────
async function callAPI(payload, ms) {
const ctrl = new AbortController();
const timer = setTimeout(() => ctrl.abort(), ms);
try {
const r = await fetch(API_URL, {
method:'POST', signal:ctrl.signal,
headers:{'Content-Type':'application/json'},
body: JSON.stringify(payload),
});
return await r.json();
} finally { clearTimeout(timer); }
}
// ── Send message ─────────────────────────────────────────────────────────────
async function cbSend() {
const input = document.getElementById('vs-cb-input');
const send = document.getElementById('vs-cb-send');
const msg = input.value.trim();
if (!msg) return;
addMsg(msg, 'user', false);
input.value = ''; input.style.height = '';
input.disabled = true; send.disabled = true;
showTyping();
document.getElementById('vs-cb-quick').style.display = 'none';
const payload = {
message: msg,
session_id: cbUser ? cbUser.session_id : 'guest_' + Date.now(),
user_name: cbUser ? cbUser.name : '',
user_email: cbUser ? cbUser.email : '',
user_phone: cbUser ? cbUser.phone : '',
};
try {
const data = await callAPI(payload, 30000);
hideTyping();
addMsg(formatMsg(data.reply || 'Sorry, something went wrong.'), 'bot', true);
} catch(_) {
hideTyping();
cbTypingEl = addMsg('Please wait, connecting…', 'bot', false);
await new Promise(r => setTimeout(r, 3000));
hideTyping();
try {
const data = await callAPI(payload, 60000);
hideTyping();
addMsg(formatMsg(data.reply || 'Sorry, something went wrong.'), 'bot', true);
} catch(_) {
hideTyping();
addMsg('Unable to reach assistant.\n\nπŸ“§ contact@vibhusolutions.com\nπŸ“ž +91 9380345108',
'bot', false);
}
} finally {
input.disabled = false; send.disabled = false; input.focus();
}
}
// ── Event listeners ──────────────────────────────────────────────────────────
document.getElementById('vs-cb-login-btn').addEventListener('click', doLogin);
document.getElementById('vs-ln').addEventListener('keypress',
e => { if (e.key==='Enter') doLogin(); });
document.getElementById('vs-cb-toggle').addEventListener('click', cbToggle);
document.getElementById('vs-cb-send').addEventListener('click', cbSend);
document.getElementById('vs-cb-input').addEventListener('keydown',
e => { if (e.key==='Enter' && !e.shiftKey) { e.preventDefault(); cbSend(); } });
document.getElementById('vs-cb-input').addEventListener('input', function() {
this.style.height = '';
this.style.height = Math.min(this.scrollHeight, 72) + 'px';
});
document.querySelectorAll('.vs-qbtn').forEach(btn => {
btn.addEventListener('click', () => {
document.getElementById('vs-cb-input').value = btn.dataset.msg;
cbSend();
});
});
})();