Aria_Bot / frontend /widget.html
AbhishekPathak01's picture
first commit
2a86195
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Aria β€” Abhishek's AI Assistant</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Syne:wght@600;700&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body { background: #0a0a0f; min-height: 100vh; display: flex; align-items: center; justify-content: center; font-family: 'Inter', sans-serif; color: #e2e8f0; }
.demo { text-align: center; padding: 40px 24px; }
.demo h1 { font-family: 'Syne', sans-serif; font-size: 3rem; background: linear-gradient(135deg, #fff 0%, #818cf8 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-bottom: 10px; }
.demo p { color: #64748b; font-size: 15px; margin-bottom: 10px; }
.demo-pills { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin-top: 14px; }
.demo-pill { background: rgba(129,140,248,0.1); border: 1px solid rgba(129,140,248,0.2); border-radius: 20px; padding: 5px 14px; font-size: 11px; color: #818cf8; }
:root {
--bg: #0d0d14; --s1: #12121c; --s2: #181824; --s3: #1e1e2e;
--bdr: rgba(129,140,248,0.12); --bdr-f: rgba(129,140,248,0.4);
--p: #818cf8; --pd: #6366f1; --pglow: rgba(99,102,241,0.35);
--tx: #e2e8f0; --tm: #64748b; --td: #94a3b8;
--ub: #6366f1; --bb: #181824;
--green: #22d3a0; --amber: #f59e0b;
--font: 'Inter', system-ui, sans-serif;
}
#aria-btn {
position: fixed; bottom: 28px; right: 28px; width: 58px; height: 58px;
border-radius: 50%; background: linear-gradient(135deg, #6366f1, #818cf8);
border: none; cursor: pointer; display: flex; align-items: center; justify-content: center;
z-index: 9999; outline: none;
box-shadow: 0 8px 32px var(--pglow), 0 0 0 1px rgba(129,140,248,0.2);
transition: all 0.3s cubic-bezier(0.34,1.56,0.64,1);
}
#aria-btn:hover { transform: scale(1.08) translateY(-2px); box-shadow: 0 16px 48px var(--pglow); }
#aria-btn:active { transform: scale(0.95); }
.btn-ring { position: absolute; width: 100%; height: 100%; border-radius: 50%; border: 1.5px solid rgba(129,140,248,0.5); animation: rp 2.5s ease-out infinite; pointer-events: none; }
@keyframes rp { 0%{transform:scale(1);opacity:.6} 100%{transform:scale(1.75);opacity:0} }
.ico-open, .ico-close { position: absolute; transition: opacity .2s, transform .3s cubic-bezier(0.34,1.56,0.64,1); }
#aria-btn.open .ico-open { opacity:0; transform:rotate(90deg) scale(.5); }
#aria-btn:not(.open) .ico-close { opacity:0; transform:rotate(-90deg) scale(.5); }
#aria-win {
position: fixed; bottom: 100px; right: 28px; width: 400px; height: 590px;
max-height: calc(100vh - 120px); background: var(--bg); border-radius: 22px;
border: 1px solid var(--bdr); box-shadow: 0 40px 100px rgba(0,0,0,.7), 0 0 0 1px rgba(129,140,248,.06);
display: flex; flex-direction: column; z-index: 9998; font-family: var(--font); overflow: hidden;
opacity: 0; transform: translateY(16px) scale(.96); pointer-events: none;
transition: opacity .28s ease, transform .32s cubic-bezier(0.34,1.56,0.64,1);
transform-origin: bottom right;
}
#aria-win.open { opacity:1; transform:translateY(0) scale(1); pointer-events:all; }
/* Header */
.a-hdr { display:flex; align-items:center; gap:12px; padding:15px 18px; background:linear-gradient(135deg,rgba(99,102,241,.1),rgba(129,140,248,.04)); border-bottom:1px solid var(--bdr); flex-shrink:0; }
.a-av { width:40px; height:40px; border-radius:12px; background:linear-gradient(135deg,#6366f1,#818cf8); display:flex; align-items:center; justify-content:center; font-size:18px; flex-shrink:0; box-shadow:0 4px 12px rgba(99,102,241,.3); position:relative; }
.a-dot { position:absolute; bottom:-2px; right:-2px; width:10px; height:10px; background:var(--green); border-radius:50%; border:2px solid var(--bg); }
.a-info h3 { font-size:14px; font-weight:600; color:var(--tx); margin-bottom:2px; }
.a-status { font-size:11px; color:var(--green); display:flex; align-items:center; gap:4px; }
.a-status::before { content:''; width:5px; height:5px; background:var(--green); border-radius:50%; display:inline-block; }
.a-acts { margin-left:auto; }
.a-ibtn { background:none; border:none; cursor:pointer; width:30px; height:30px; border-radius:8px; display:flex; align-items:center; justify-content:center; color:var(--tm); transition:background .2s,color .2s; }
.a-ibtn:hover { background:rgba(255,255,255,.05); color:var(--td); }
/* Messages */
.a-msgs { flex:1; overflow-y:auto; padding:14px 13px; display:flex; flex-direction:column; gap:11px; scroll-behavior:smooth; }
.a-msgs::-webkit-scrollbar { width:3px; }
.a-msgs::-webkit-scrollbar-thumb { background:rgba(129,140,248,.2); border-radius:3px; }
/* Welcome */
.a-welcome { text-align:center; padding:10px 6px; animation:mi .5s ease forwards; }
.w-icon { width:54px; height:54px; border-radius:16px; background:linear-gradient(135deg,#6366f1,#818cf8); display:flex; align-items:center; justify-content:center; font-size:24px; margin:0 auto 13px; box-shadow:0 8px 24px rgba(99,102,241,.3); }
.a-welcome h4 { font-family:'Syne',sans-serif; font-size:16px; font-weight:700; color:var(--tx); margin-bottom:5px; }
.a-welcome p { font-size:12.5px; color:var(--tm); line-height:1.5; max-width:270px; margin:0 auto; }
.a-chips { display:flex; flex-wrap:wrap; gap:6px; justify-content:center; margin-top:13px; }
.a-chip { background:var(--s2); border:1px solid var(--bdr); border-radius:20px; padding:5px 12px; font-size:11.5px; font-weight:500; color:var(--tm); cursor:pointer; font-family:var(--font); transition:all .2s ease; }
.a-chip:hover { background:rgba(99,102,241,.12); border-color:rgba(129,140,248,.4); color:var(--p); transform:translateY(-1px); }
/* Message bubbles */
.a-msg { display:flex; gap:8px; animation:mi .28s cubic-bezier(0.34,1.56,0.64,1) forwards; }
@keyframes mi { from{opacity:0;transform:translateY(8px) scale(.98)} to{opacity:1;transform:translateY(0) scale(1)} }
.a-msg.user { flex-direction:row-reverse; }
.m-av { width:26px; height:26px; border-radius:8px; flex-shrink:0; display:flex; align-items:center; justify-content:center; font-size:12px; margin-top:3px; }
.a-msg.bot .m-av { background:linear-gradient(135deg,#6366f1,#818cf8); }
.a-msg.user .m-av { background:var(--s3); color:var(--tm); font-size:14px; }
.m-wrap { display:flex; flex-direction:column; gap:4px; max-width:calc(100% - 42px); }
.m-bbl { padding:10px 14px; border-radius:16px; font-size:13.5px; line-height:1.65; word-wrap:break-word; }
.a-msg.bot .m-bbl { background:var(--bb); color:var(--tx); border:1px solid var(--bdr); border-bottom-left-radius:5px; }
.a-msg.user .m-bbl { background:var(--ub); color:white; border-bottom-right-radius:5px; }
.m-bbl strong { color:#a5b4fc; font-weight:600; }
.m-bbl code { background:rgba(99,102,241,.15); border:1px solid rgba(99,102,241,.2); border-radius:4px; padding:1px 5px; font-size:12px; color:#a5b4fc; }
.m-bbl a { color:var(--p); text-decoration:none; }
.m-bbl a:hover { text-decoration:underline; }
.m-bbl ul,.m-bbl ol { margin:6px 0; padding-left:18px; }
.m-bbl li { margin-bottom:3px; }
/* Source badge */
.m-badge { display:inline-flex; align-items:center; gap:4px; font-size:10px; font-weight:500; padding:2px 8px; border-radius:10px; width:fit-content; }
.m-badge.portfolio { background:rgba(99,102,241,.15); color:#818cf8; border:1px solid rgba(99,102,241,.2); }
.m-badge.general { background:rgba(245,158,11,.12); color:#f59e0b; border:1px solid rgba(245,158,11,.2); }
/* Typing */
.a-typing { display:flex; gap:8px; align-items:flex-end; }
.t-dots { background:var(--bb); border:1px solid var(--bdr); border-radius:16px; border-bottom-left-radius:5px; padding:13px 16px; display:flex; gap:4px; align-items:center; }
.t-d { width:6px; height:6px; background:var(--p); border-radius:50%; animation:tb 1.3s ease-in-out infinite; }
.t-d:nth-child(2){animation-delay:.18s} .t-d:nth-child(3){animation-delay:.36s}
@keyframes tb { 0%,60%,100%{transform:translateY(0);opacity:.4} 30%{transform:translateY(-7px);opacity:1} }
/* Input */
.a-inp-wrap { padding:11px 13px 15px; border-top:1px solid var(--bdr); background:var(--s1); flex-shrink:0; }
.a-inp-row { display:flex; gap:8px; align-items:flex-end; background:var(--s2); border:1px solid var(--bdr); border-radius:14px; padding:8px 10px; transition:border-color .2s,box-shadow .2s; }
.a-inp-row:focus-within { border-color:var(--bdr-f); box-shadow:0 0 0 3px rgba(99,102,241,.08); }
#a-input { flex:1; background:none; border:none; outline:none; color:var(--tx); font-family:var(--font); font-size:13.5px; resize:none; max-height:100px; line-height:1.5; padding:1px 0; }
#a-input::placeholder { color:var(--tm); }
#a-send { background:linear-gradient(135deg,#6366f1,#818cf8); border:none; cursor:pointer; width:32px; height:32px; border-radius:9px; display:flex; align-items:center; justify-content:center; flex-shrink:0; margin-bottom:1px; transition:opacity .2s,transform .15s; box-shadow:0 4px 12px rgba(99,102,241,.3); }
#a-send:hover { opacity:.9; transform:scale(1.05); }
#a-send:active { transform:scale(.93); }
#a-send:disabled { opacity:.3; cursor:not-allowed; transform:none; }
.a-footer { display:flex; justify-content:center; margin-top:7px; }
.a-brand { font-size:10px; color:var(--tm); opacity:.55; display:flex; align-items:center; gap:4px; }
.a-brand b { color:var(--p); font-weight:600; }
.a-err { background:rgba(239,68,68,.08); border:1px solid rgba(239,68,68,.2); color:#fca5a5; border-radius:10px; padding:9px 13px; font-size:12.5px; text-align:center; }
.a-cur { animation:blink .65s step-end infinite; }
@keyframes blink { 50%{opacity:0} }
@media(max-width:480px) {
#aria-win { width:calc(100vw - 20px); right:10px; bottom:86px; height:calc(100vh - 106px); max-height:none; }
#aria-btn { right:18px; bottom:18px; }
}
</style>
</head>
<body>
<div class="demo">
<h1>Aria ✦</h1>
<p>Abhishek Kumar's Personal AI Assistant</p>
<div class="demo-pills">
<span class="demo-pill">🧠 Portfolio β†’ Search</span>
<span class="demo-pill">🌐 General β†’ Web Search</span>
<span class="demo-pill">⚑ Groq LLM</span>
</div>
</div>
<!-- Trigger Button -->
<button id="aria-btn" aria-label="Open Aria">
<div class="btn-ring"></div>
<svg class="ico-open" width="22" height="22" viewBox="0 0 24 24" fill="white"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H5.17L4 17.17V4h16v12z"/></svg>
<svg class="ico-close" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round"><path d="M18 6L6 18M6 6l12 12"/></svg>
</button>
<!-- Chat Window -->
<div id="aria-win" role="dialog" aria-label="Aria AI Assistant">
<div class="a-hdr">
<div class="a-av">✦<div class="a-dot"></div></div>
<div class="a-info">
<h3>Aria</h3>
<div class="a-status">Online</div>
</div>
<div class="a-acts">
<button class="a-ibtn" id="a-clear" title="New chat">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><polyline points="1 4 1 10 7 10"/><path d="M3.51 15a9 9 0 1 0 .49-3.1"/></svg>
</button>
</div>
</div>
<div class="a-msgs" id="a-msgs">
<div class="a-welcome">
<div class="w-icon">✦</div>
<h4>Hi, I'm Aria!</h4>
<p>Abhishek's personal AI. Ask me about him β€” or anything else. I'll search the web for general questions!</p>
<div class="a-chips">
<button class="a-chip" data-q="Who is Abhishek Kumar?">πŸ‘¨β€πŸ’» About Abhishek</button>
<button class="a-chip" data-q="What are Abhishek's skills?">πŸ›  His Skills</button>
<button class="a-chip" data-q="What projects has Abhishek built?">πŸš€ Projects</button>
<button class="a-chip" data-q="Tell me about Delhi city">πŸŒ† Delhi</button>
<button class="a-chip" data-q="What is machine learning?">πŸ€– What is ML?</button>
<button class="a-chip" data-q="Is Abhishek available for hire?">🀝 Hire Him</button>
</div>
</div>
</div>
<div class="a-inp-wrap">
<div class="a-inp-row">
<textarea id="a-input" placeholder="Ask anything..." rows="1" aria-label="Message"></textarea>
<button id="a-send" aria-label="Send">
<svg width="14" height="14" viewBox="0 0 24 24" fill="white"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>
</button>
</div>
<div class="a-footer">
<span class="a-brand">Built by <b>Abhishek Kumar</b> with Love</span>
</div>
</div>
</div>
<script>
(function() {
const API = 'http://127.0.0.1:8080';
let isOpen=false, loading=false, sessionId=null, typingEl=null;
const btn=document.getElementById('aria-btn'), win=document.getElementById('aria-win');
const msgs=document.getElementById('a-msgs'), inp=document.getElementById('a-input');
const send=document.getElementById('a-send'), clr=document.getElementById('a-clear');
btn.addEventListener('click', toggle);
send.addEventListener('click', submit);
clr.addEventListener('click', clearChat);
inp.addEventListener('keydown', e=>{ if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();submit();} });
inp.addEventListener('input', ()=>{ inp.style.height='auto'; inp.style.height=Math.min(inp.scrollHeight,100)+'px'; });
msgs.addEventListener('click', e=>{ const c=e.target.closest('.a-chip'); if(c){inp.value=c.dataset.q;submit();} });
document.addEventListener('click', e=>{ if(isOpen&&!win.contains(e.target)&&e.target!==btn&&!btn.contains(e.target))toggle(); });
function toggle(){
isOpen=!isOpen;
btn.classList.toggle('open',isOpen);
win.classList.toggle('open',isOpen);
if(isOpen) setTimeout(()=>inp.focus(),320);
}
async function submit(){
const q=inp.value.trim();
if(!q||loading) return;
addMsg('user',q);
inp.value=''; inp.style.height='auto';
setLoad(true);
await doStream(q);
}
async function doStream(q){
showTyping();
let qtype='general';
try{
const res=await fetch(`${API}/chat/stream`,{
method:'POST', headers:{'Content-Type':'application/json'},
body:JSON.stringify({query:q,session_id:sessionId,top_k:5})
});
if(!res.ok) throw new Error(`HTTP ${res.status}`);
hideTyping();
const el=addMsg('bot','',qtype);
const bbl=el.querySelector('.m-bbl'), bdg=el.querySelector('.m-badge');
let full='';
const reader=res.body.getReader(), dec=new TextDecoder();
while(true){
const{done,value}=await reader.read();
if(done) break;
for(const line of dec.decode(value).split('\n')){
if(!line.startsWith('data: ')) continue;
try{
const d=JSON.parse(line.slice(6));
if(d.type==='session') sessionId=d.session_id;
else if(d.type==='meta'){
qtype=d.query_type;
if(bdg){ bdg.className=`m-badge ${qtype}`; bdg.textContent=qtype==='portfolio'?'🧠 Portfolio RAG':'🌐 Web Search'; }
}
else if(d.type==='chunk'){ full+=d.content; bbl.innerHTML=md(full)+'<span class="a-cur">β–‹</span>'; scroll(); }
else if(d.type==='done'){ bbl.innerHTML=md(full); scroll(); }
else if(d.type==='error') bbl.innerHTML=`<div class="a-err">⚠️ ${d.message}</div>`;
}catch(_){}
}
}
}catch(e){
hideTyping();
addMsg('bot',null,null,e.message.includes('fetch')?`⚠️ Cannot connect to <code>${API}</code>. Make sure server is running.`:`⚠️ ${e.message}`);
}finally{ setLoad(false); }
}
function addMsg(role,text,type,err){
const d=document.createElement('div'); d.className=`a-msg ${role}`;
const av=document.createElement('div'); av.className='m-av'; av.textContent=role==='bot'?'✦':'πŸ‘€';
const wrap=document.createElement('div'); wrap.className='m-wrap';
if(role==='bot'&&!err){
const bdg=document.createElement('div');
bdg.className=`m-badge ${type||'general'}`;
bdg.textContent=type==='portfolio'?'🧠 Portfolio RAG':'🌐 Web Search';
wrap.appendChild(bdg);
}
const b=document.createElement('div'); b.className='m-bbl';
if(err) b.innerHTML=`<div class="a-err">${err}</div>`;
else if(role==='bot') b.innerHTML=md(text||'');
else b.textContent=text;
wrap.appendChild(b); d.appendChild(av); d.appendChild(wrap);
msgs.appendChild(d); scroll(); return d;
}
function showTyping(){
typingEl=document.createElement('div'); typingEl.className='a-typing';
typingEl.innerHTML=`<div class="m-av" style="background:linear-gradient(135deg,#6366f1,#818cf8);border-radius:8px;width:26px;height:26px;display:flex;align-items:center;justify-content:center;font-size:12px;flex-shrink:0;margin-top:3px">✦</div><div class="t-dots"><div class="t-d"></div><div class="t-d"></div><div class="t-d"></div></div>`;
msgs.appendChild(typingEl); scroll();
}
function hideTyping(){ if(typingEl){typingEl.remove();typingEl=null;} }
function setLoad(s){ loading=s; send.disabled=s; inp.disabled=s; }
function scroll(){ setTimeout(()=>{msgs.scrollTop=msgs.scrollHeight;},40); }
function clearChat(){
if(sessionId) fetch(`${API}/chat/${sessionId}`,{method:'DELETE'}).catch(()=>{});
sessionId=null;
msgs.innerHTML=`<div class="a-welcome" style="animation:mi .4s ease forwards"><div class="w-icon">✦</div><h4>Fresh start!</h4><p>Ask me anything!</p><div class="a-chips"><button class="a-chip" data-q="Who is Abhishek Kumar?">πŸ‘¨β€πŸ’» About Abhishek</button><button class="a-chip" data-q="What are Abhishek's skills?">πŸ›  His Skills</button><button class="a-chip" data-q="Tell me about Delhi city">πŸŒ† Delhi</button><button class="a-chip" data-q="What is machine learning?">πŸ€– What is ML?</button></div></div>`;
}
function md(t){
return t.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
.replace(/\*\*(.*?)\*\*/g,'<strong>$1</strong>').replace(/\*(.*?)\*/g,'<em>$1</em>')
.replace(/`(.*?)`/g,'<code>$1</code>').replace(/\[([^\]]+)\]\(([^)]+)\)/g,'<a href="$2" target="_blank" rel="noopener">$1</a>')
.replace(/^#{1,3} (.+)$/gm,'<strong>$1</strong>').replace(/^[-β€’] (.+)$/gm,'β€’ $1').replace(/\n/g,'<br>');
}
})();
</script>
</body>
</html>