// Ukrainian in Germany - Chat Widget (styled per provided markup) (function(){ const API_BASE = "https://jaj.digicat.pl"; const path = (window.location.pathname || "/").toLowerCase(); const lang = path.startsWith("/ru/") ? "ru" : path.startsWith("/uk/") ? "uk" : "de"; const sid = (window.crypto && crypto.randomUUID) ? crypto.randomUUID() : ("sid_"+Date.now()); // Build or reuse provided markup let btn = document.getElementById('chat-widget-button'); let box = document.getElementById('chat-widget-container'); if (!btn){ btn = document.createElement('button'); btn.id='chat-widget-button'; document.body.appendChild(btn); } if (!box){ box = document.createElement('div'); box.id='chat-widget-container'; box.innerHTML = [ '
', ' Ukrainian in Germany', ' ', '
', '
', '' ].join('\n'); document.body.appendChild(box); } // Styles (navy theme, fixed size) const css = ` :root{--chat-primary:#0b3d91;--chat-secondary:#163e72;--chat-bg:#fff;--chat-font:#1f2937;--chat-z:10000002} @keyframes chat-dots{0%{opacity:.2}50%{opacity:1}100%{opacity:.2}} #chat-widget-button{position:fixed;right:18px;bottom:90px;z-index:var(--chat-z);background:var(--chat-primary);color:#fff;border:0;border-radius:24px;padding:10px 14px;font:14px/1.2 system-ui,Arial;box-shadow:0 6px 20px rgba(0,0,0,.18);cursor:pointer;display:flex;align-items:center;gap:8px} #chat-widget-button::before{content:"\u{1F4AC}";font-size:16px} #chat-widget-container{position:fixed;right:18px;bottom:140px;z-index:var(--chat-z);width:360px;height:520px;display:none;flex-direction:column;background:var(--chat-bg);border:1px solid #e5e7eb;border-radius:12px;box-shadow:0 12px 28px rgba(0,0,0,.2);overflow:hidden} #chat-widget-header{display:flex;justify-content:space-between;align-items:center;background:var(--chat-secondary);color:#fff;padding:10px 12px;font:600 14px system-ui} #chat-widget-header button{background:transparent;color:#fff;border:0;cursor:pointer;font-size:16px} #chat-widget-body{flex:1;overflow:auto;padding:12px;font:14px/1.4 system-ui;color:var(--chat-font);background:#fff} #chat-widget-footer{display:flex;gap:8px;border-top:1px solid #eee;padding:10px;background:#fafafa} #chat-widget-input{flex:1;border:1px solid #e5e7eb;border-radius:8px;padding:10px 12px;outline:none;font:14px system-ui} #chat-widget-send{background:var(--chat-primary);color:#fff;border:0;border-radius:8px;padding:0 12px;cursor:pointer;display:flex;align-items:center;justify-content:center;width:44px} #chat-widget-send svg{fill:#fff;width:18px;height:18px} .user-message{text-align:right;margin:6px 0} .bot-message{margin:6px 0;background:#f8fafc;border-radius:12px;padding:10px 12px;display:inline-block;max-width:85%} .chat-sources{margin-top:6px;font-size:12px;color:#444} .chat-sources a{color:inherit;text-decoration:underline}`; const style = document.createElement('style'); style.textContent = css; document.head.appendChild(style); // Elements const headerTitle = box.querySelector('#chat-widget-header span'); if (headerTitle) headerTitle.textContent = 'Ukrainian in Germany'; const bodyEl = document.getElementById('chat-widget-body'); const inputEl= document.getElementById('chat-widget-input'); const sendEl = document.getElementById('chat-widget-send'); const closeEl= document.getElementById('chat-close'); // Greeting per language const greetings = { de:"Hallo! Wie kann ich dir helfen?", ru:"Привет! Чем могу помочь?", uk:"Привіт! Чим можу допомогти?" }; const greet = greetings[lang] || greetings.de; if (bodyEl && !bodyEl.dataset.init){ bodyEl.dataset.init='1'; const p=document.createElement('p'); p.className='bot-message'; p.innerHTML = `${greet}`; bodyEl.appendChild(p); } // Adjust above anchor_div function adjustBottom(){ const anchor = document.querySelector('.anchor_div'); let offset = 18; if (anchor){ const cs=getComputedStyle(anchor); if (cs.position==='fixed'||cs.position==='sticky'){ offset = (anchor.offsetHeight||0) + 12; } } document.getElementById('chat-widget-button').style.bottom = offset + 'px'; document.getElementById('chat-widget-container').style.bottom = (offset + 50) + 'px'; } window.addEventListener('load', adjustBottom); window.addEventListener('resize', adjustBottom); setTimeout(adjustBottom, 300); // Markdown renderer (basic) function renderMarkdown(md){ if(!md) return ''; md = md.replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g,'$1<\/a>'); md = md.replace(/\*\*(.+?)\*\*/g,'$1<\/strong>'); const lines=md.split(/\r?\n/); let html='', inList=false; for(const lineRaw of lines){ const line=lineRaw.trim(); if (/^- /.test(line)){ if(!inList){ html+=''; inList=false; } html+=line+'
'; } } if(inList) html+=''; return html; } function addMsg(text, role){ const p=document.createElement('p'); p.className = role==='user'? 'user-message':'bot-message'; p.innerHTML = text; bodyEl.appendChild(p); bodyEl.scrollTop = bodyEl.scrollHeight; } function send(){ const text=(inputEl.value||'').trim(); if(!text) return; addMsg(text,'user'); inputEl.value=''; const typing=document.createElement('div'); typing.className='bot-message'; typing.innerHTML = ''+ ''+ ''+ ''+ ''; bodyEl.appendChild(typing); bodyEl.scrollTop=bodyEl.scrollHeight; fetch(API_BASE+'/api/chat',{method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({message:text, lang:lang, session_id:sid, max_results:5})}) .then(async res=>{ const ct=res.headers.get('content-type')||''; const data=ct.includes('application/json')? await res.json():{}; if(!res.ok) throw new Error(data?.detail||('API '+res.status)); return data; }) .then(data=>{ typing.remove(); const full = renderMarkdown(data.response||""); const limit = 1200; // clamp answer if (full.length <= limit) { addMsg(full,'bot'); } else { const preview = full.slice(0,limit)+" 
Show more"; addMsg(preview,'bot'); const more = bodyEl.querySelector('.chat-more:last-of-type'); if (more) more.addEventListener('click', function(e){ e.preventDefault(); this.closest('.bot-message').innerHTML = full; }); } if (Array.isArray(data.sources) && data.sources.length){ const div=document.createElement('div'); div.className='chat-sources'; div.innerHTML = data.sources.map(u=>`${u}`).join('
'); bodyEl.appendChild(div); bodyEl.scrollTop=bodyEl.scrollHeight; } }) .catch(err=>{ typing.remove(); addMsg('Przepraszamy, wystąpił błąd połączenia. Spróbuj ponownie.','bot'); console.error(err); }); } // Bind events btn.addEventListener('click', ()=>{ box.style.display='flex'; btn.style.display='none'; inputEl && inputEl.focus(); adjustBottom(); }); if (closeEl) closeEl.addEventListener('click', ()=>{ box.style.display='none'; btn.style.display='flex'; }); sendEl.addEventListener('click', send); inputEl.addEventListener('keydown', (e)=>{ if(e.key==='Enter') send(); }); })();