@spilki/widget 1.0.30 → 1.0.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- (function(x){typeof define=="function"&&define.amd?define(x):x()})(function(){"use strict";const x=`
1
+ (function(E){typeof define=="function"&&define.amd?define(E):E()})(function(){"use strict";const E=`
2
2
  <style>
3
3
  :host {
4
4
  all: initial;
@@ -70,8 +70,8 @@
70
70
  </span>
71
71
  <span class="badge" hidden aria-hidden="true">0</span>
72
72
  </button>
73
- `;function F(r){const e=document.createElement("div");e.setAttribute("part","bubble-root"),e.setAttribute("data-position",r.position),e.style.setProperty("--spilki-accent",r.color);const t=e.attachShadow({mode:"open"});t.innerHTML=x;const s=t.querySelector("button"),i=t.querySelector(".badge");return s.addEventListener("click",()=>r.onClick()),{element:e,mount(){document.body.appendChild(e)},destroy(){e.remove()},setOpen(a){s.setAttribute("aria-expanded",String(a)),s.setAttribute("aria-label",a?"Close chat":"Open chat")},setBadge(a){const n=Math.max(0,a),l=s.getAttribute("aria-expanded")==="true";if(n===0){i.toggleAttribute("hidden",!0),i.textContent="0",l||s.setAttribute("aria-label","Open chat");return}i.toggleAttribute("hidden",!1),i.textContent=n>99?"99+":String(n),l||s.setAttribute("aria-label",`Open chat, ${n} unread`)}}}const R="https://api.spilki.app",S={welcome:"Hi! I'm your assistant.",placeholder:"Type a message…",sendLabel:"Send",typing:"Assistant is typing…",offline:"Unable to connect. Please try again later.",title:"Spilki Assistant"},k={apiBase:R,position:"bottom-right",theme:"auto",color:"#6366f1",welcome:S.welcome,persist:!0,sound:!0,i18n:S};function K(r){var t,s,i,o,a,n,l,g;const e={...S,...(t=r.i18n)!=null?t:{}};return{...k,...r,apiBase:(s=r.apiBase)!=null?s:k.apiBase,i18n:e,welcome:(i=r.welcome)!=null?i:e.welcome,position:(o=r.position)!=null?o:k.position,theme:(a=r.theme)!=null?a:k.theme,color:(n=r.color)!=null?n:k.color,persist:(l=r.persist)!=null?l:k.persist,sound:(g=r.sound)!=null?g:k.sound}}function N(r="msg"){return typeof crypto!="undefined"&&crypto.randomUUID?crypto.randomUUID():`${r}-${Math.random().toString(16).slice(2)}`}function H(){var r,e;return(e=(r=window.matchMedia)==null?void 0:r.call(window,"(prefers-color-scheme: dark)").matches)!=null?e:!1}function O(r){return r==="light"||r==="dark"?r:H()?"dark":"light"}function E(r,e=30){return r.slice(-e)}function z(r){if(!r||r.length===0)return;const e=window.location.origin;r.includes(e)||console.warn(`SpilkiWidget: current origin ${e} not in allowedOriginsHint: ${r.join(", ")}`)}const q=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function _(r){const e=new Date(r),t=new Date,s=l=>String(l).padStart(2,"0"),i=`${s(e.getHours())}:${s(e.getMinutes())}`,o=new Date(t.getFullYear(),t.getMonth(),t.getDate()).getTime(),a=o-864e5,n=new Date(e.getFullYear(),e.getMonth(),e.getDate()).getTime();return n===o?`Today at ${i}`:n===a?`Yesterday at ${i}`:`${q[e.getMonth()]} ${e.getDate()}, ${i}`}const J=':host{--spilki-bg-light: #ffffff;--spilki-bg-dark: #0f172a;--spilki-text-light: #0f172a;--spilki-text-dark: #f8fafc;--spilki-border-light: rgba(15, 23, 42, .1);--spilki-border-dark: rgba(148, 163, 184, .25);--spilki-muted-light: #64748b;--spilki-muted-dark: #94a3b8;--spilki-shadow: 0 10px 40px rgba(15, 23, 42, .2);--spilki-radius: 16px;--spilki-font: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;color:inherit;font-family:var(--spilki-font)}.wrapper{position:relative;width:100%;height:100%;display:flex;flex-direction:column;background:var(--spilki-surface);color:var(--spilki-text);border-radius:var(--spilki-radius);box-shadow:var(--spilki-shadow);border:1px solid var(--spilki-border);overflow:hidden}header{display:flex;align-items:center;justify-content:space-between;padding:.75rem 1rem;background:var(--spilki-surface);border-bottom:1px solid var(--spilki-border)}header h1{font-size:1rem;margin:0;display:flex;align-items:center;gap:.5rem}header button.close{border:none;background:transparent;color:inherit;font-size:1.25rem;cursor:pointer}header button.close:focus-visible{outline:2px solid var(--spilki-accent);outline-offset:2px;border-radius:4px}header .status-dot{width:10px;height:10px;border-radius:999px;background:var(--spilki-accent)}.messages{flex:1;overflow-y:auto;padding:1rem;display:flex;flex-direction:column;gap:.5rem;scrollbar-width:thin;scrollbar-color:rgba(148,163,184,.3) transparent;scroll-behavior:smooth}.message{display:flex;flex-direction:column;gap:.15rem;max-width:85%;width:fit-content;line-height:1.4;word-wrap:break-word;overflow-wrap:anywhere;white-space:pre-wrap}.message.user{align-self:flex-end;align-items:flex-end}.message .bubble{padding:.6rem .8rem;border-radius:1rem;background:#6366f126}.message.user .bubble{background:var(--spilki-accent);color:#fff}.message.bot .bubble{background:#94a3b826}.msg-time{font-size:.7rem;color:var(--spilki-muted);margin-top:2px}.date-separator{display:flex;align-items:center;gap:.5rem;padding:.4rem 0;font-size:.72rem;color:var(--spilki-muted);white-space:nowrap}.date-separator:before,.date-separator:after{content:"";flex:1;height:1px;background:var(--spilki-border)}.scroll-bottom{position:absolute;right:1rem;bottom:88px;width:38px;height:38px;border:none;border-radius:999px;background:var(--spilki-accent);color:#fff;box-shadow:0 8px 20px #0f172a40;cursor:pointer;opacity:0;pointer-events:none;transform:translateY(4px);transition:opacity .18s ease,transform .18s ease;z-index:3}.scroll-bottom.visible{opacity:1;pointer-events:auto;transform:translateY(0)}.scroll-arrow{font-size:.8rem;line-height:1}.scroll-count{position:absolute;top:-5px;right:-5px;min-width:16px;height:16px;border-radius:999px;background:#ef4444;color:#fff;font-size:10px;line-height:16px;text-align:center;padding:0 4px}.suggested-replies{display:flex;flex-wrap:wrap;gap:.4rem;padding:.5rem 1rem;border-top:1px solid var(--spilki-border)}.suggested-chip{border:1px solid var(--spilki-accent);background:transparent;color:var(--spilki-accent);border-radius:999px;padding:.35rem .75rem;font-size:.8rem;font-family:inherit;cursor:pointer;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;transition:background .15s,color .15s}.suggested-chip:hover{background:var(--spilki-accent);color:#fff}.suggested-chip:focus-visible{outline:2px solid var(--spilki-accent);outline-offset:2px}.input-area{padding:.5rem .75rem}.input-wrap{display:flex;align-items:center;border:1px solid var(--spilki-border);border-radius:1.5rem;background:transparent;padding-left:0}.input-area textarea{flex:1;resize:none;min-height:2.4rem;max-height:6rem;border:0;border-radius:0;outline:0;background:transparent;box-shadow:none;-webkit-box-shadow:none;-webkit-appearance:none;appearance:none;padding:.55rem 0 .55rem 1rem;font-family:inherit;font-size:.95rem;color:var(--spilki-text);scrollbar-width:thin;scrollbar-color:rgba(148,163,184,.3) transparent}.input-actions{display:flex;align-items:center;gap:.25rem;padding:.25rem .35rem;flex-shrink:0}.input-actions button{width:32px;height:32px;display:grid;place-items:center;border:none;border-radius:50%;cursor:pointer;padding:0;transition:opacity .15s}.emoji-btn{background:#6366f126;color:var(--spilki-accent)}.emoji-btn:hover{background:#6366f140}.emoji-btn:focus-visible{outline:2px solid var(--spilki-accent);outline-offset:1px}.send-btn{background:var(--spilki-accent);color:#fff}.send-btn:hover{opacity:.85}.send-btn:focus-visible{outline:2px solid var(--spilki-accent);outline-offset:2px}.emoji-picker{position:absolute;right:1rem;bottom:80px;width:240px;max-height:180px;overflow-y:auto;display:none;grid-template-columns:repeat(10,minmax(0,1fr));gap:.25rem;padding:.5rem;background:var(--spilki-surface);border:1px solid var(--spilki-border);border-radius:12px;box-shadow:0 14px 30px #0f172a40;z-index:4}.emoji-option{width:100%;aspect-ratio:1;border:none;border-radius:8px;background:transparent;font-size:1rem;cursor:pointer}.emoji-option:hover,.emoji-option:focus-visible{background:#94a3b833}.typing{font-size:.75rem;color:var(--spilki-muted);padding:0 1rem .75rem}.offline{font-size:.8rem;padding:0 1rem;color:#f97316}:host([data-theme="dark"]){--spilki-surface: var(--spilki-bg-dark);--spilki-text: var(--spilki-text-dark);--spilki-border: var(--spilki-border-dark);--spilki-muted: var(--spilki-muted-dark)}:host([data-theme="dark"]) .messages,:host([data-theme="dark"]) .input-area textarea,:host([data-theme="dark"]) .emoji-picker{scrollbar-color:rgba(255,255,255,.2) transparent}:host([data-theme="light"]){--spilki-surface: var(--spilki-bg-light);--spilki-text: var(--spilki-text-light);--spilki-border: var(--spilki-border-light);--spilki-muted: var(--spilki-muted-light)}:host([data-theme="dark"]) .message .bubble{background:#94a3b81f}:host([data-theme="dark"]) .message.bot .bubble{background:#6366f126}:host([data-theme="dark"]) .input-area textarea{background:transparent}.messages::-webkit-scrollbar,.input-area textarea::-webkit-scrollbar,.emoji-picker::-webkit-scrollbar{width:6px}.messages::-webkit-scrollbar-track,.input-area textarea::-webkit-scrollbar-track,.emoji-picker::-webkit-scrollbar-track{background:transparent}.messages::-webkit-scrollbar-thumb,.input-area textarea::-webkit-scrollbar-thumb,.emoji-picker::-webkit-scrollbar-thumb{background:#94a3b84d;border-radius:999px}.messages::-webkit-scrollbar-thumb:hover,.input-area textarea::-webkit-scrollbar-thumb:hover,.emoji-picker::-webkit-scrollbar-thumb:hover{background:#94a3b880}:host([data-theme="dark"]) .messages::-webkit-scrollbar-thumb,:host([data-theme="dark"]) .input-area textarea::-webkit-scrollbar-thumb,:host([data-theme="dark"]) .emoji-picker::-webkit-scrollbar-thumb{background:#fff3}:host([data-theme="dark"]) .messages::-webkit-scrollbar-thumb:hover,:host([data-theme="dark"]) .input-area textarea::-webkit-scrollbar-thumb:hover,:host([data-theme="dark"]) .emoji-picker::-webkit-scrollbar-thumb:hover{background:#ffffff59}.conversation-separator{display:flex;align-items:center;gap:.5rem;padding:.5rem 0;cursor:pointer;user-select:none;font-size:.7rem;color:var(--spilki-muted);white-space:nowrap}.conversation-separator:before,.conversation-separator:after{content:"";flex:1;height:1px;background:var(--spilki-border)}.conversation-separator:hover{color:var(--spilki-text)}.conversation-separator:focus-visible{outline:2px solid var(--spilki-accent);outline-offset:2px;border-radius:4px}.conversation-history{display:none;flex-direction:column;gap:.5rem}.conversation-history.expanded{display:flex}',M=24*60*60*1e3,V=2*60*1e3,Y=200,G=["😀","😂","🤣","😊","😍","🥰","😘","😉","🤔","😏","😎","🤗","😢","😭","😡","🤯","😱","🥳","😴","🤮","👍","👎","👋","🤝","🙏","💪","✌️","🤞","👏","🫶","❤️","🔥","⭐","💯","✅","❌","⚡","🎉","💰","📦","📅","📍","📞","💬","🔔","🔗","📸","🎵","☕","🍕"];function v(r){return r.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}class X{constructor(e){this.options=e,this.relativeTimeFormat=new Intl.RelativeTimeFormat(void 0,{numeric:"auto"}),this.absoluteTimeFormat=new Intl.DateTimeFormat(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit",hour12:!1}),this.dateSeparatorFormat=new Intl.DateTimeFormat(void 0,{month:"short",day:"numeric",year:"numeric"}),this.renderedMessages=[],this.focusable=[],this.seenIds=new Set,this.lastTimelineMessage=null,this.scrollUnreadCount=0,this.emojiPickerOpen=!1,this.open=!1,this.host=document.createElement("div"),this.host.setAttribute("part","panel-root"),this.host.style.position="fixed",this.host.style.bottom="96px",this.host.style[e.position==="bottom-right"?"right":"left"]="24px",this.host.style.width="360px",this.host.style.maxWidth="calc(100vw - 32px)",this.host.style.height="520px",this.host.style.display="none",this.host.style.zIndex="2147483001",this.shadow=this.host.attachShadow({mode:"open"});const t=v(e.i18n.title),s=v(e.i18n.typing),i=v(e.i18n.offline),o=v(e.i18n.placeholder),a=v(e.i18n.sendLabel);this.shadow.innerHTML=`
74
- <style>${J}</style>
73
+ `;function W(r){const e=document.createElement("div");e.setAttribute("part","bubble-root"),e.setAttribute("data-position",r.position),e.style.setProperty("--spilki-accent",r.color);const t=e.attachShadow({mode:"open"});t.innerHTML=E;const s=t.querySelector("button"),i=t.querySelector(".badge");return s.addEventListener("click",()=>r.onClick()),{element:e,mount(){document.body.appendChild(e)},destroy(){e.remove()},setOpen(a){s.setAttribute("aria-expanded",String(a)),s.setAttribute("aria-label",a?"Close chat":"Open chat")},setBadge(a){const n=Math.max(0,a),l=s.getAttribute("aria-expanded")==="true";if(n===0){i.toggleAttribute("hidden",!0),i.textContent="0",l||s.setAttribute("aria-label","Open chat");return}i.toggleAttribute("hidden",!1),i.textContent=n>99?"99+":String(n),l||s.setAttribute("aria-label",`Open chat, ${n} unread`)}}}const F="https://api.spilki.app",S={welcome:"Hi! I'm your assistant.",placeholder:"Type a message…",sendLabel:"Send",typing:"Assistant is typing…",thinking:"Assistant is thinking…",searching:"Searching…",executingTool:"Working on it…",offline:"Unable to connect. Please try again later.",title:"Spilki Assistant"},k={apiBase:F,position:"bottom-right",theme:"auto",color:"#6366f1",welcome:S.welcome,persist:!0,sound:!0,i18n:S};function R(r){var t,s,i,o,a,n,l,g;const e={...S,...(t=r.i18n)!=null?t:{}};return{...k,...r,apiBase:(s=r.apiBase)!=null?s:k.apiBase,i18n:e,welcome:(i=r.welcome)!=null?i:e.welcome,position:(o=r.position)!=null?o:k.position,theme:(a=r.theme)!=null?a:k.theme,color:(n=r.color)!=null?n:k.color,persist:(l=r.persist)!=null?l:k.persist,sound:(g=r.sound)!=null?g:k.sound}}function K(r="msg"){return typeof crypto!="undefined"&&crypto.randomUUID?crypto.randomUUID():`${r}-${Math.random().toString(16).slice(2)}`}function H(){var r,e;return(e=(r=window.matchMedia)==null?void 0:r.call(window,"(prefers-color-scheme: dark)").matches)!=null?e:!1}function A(r){return r==="light"||r==="dark"?r:H()?"dark":"light"}function x(r,e=30){return r.slice(-e)}function z(r){if(!r||r.length===0)return;const e=window.location.origin;r.includes(e)||console.warn(`SpilkiWidget: current origin ${e} not in allowedOriginsHint: ${r.join(", ")}`)}const _=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function G(r){const e=new Date(r),t=new Date,s=l=>String(l).padStart(2,"0"),i=`${s(e.getHours())}:${s(e.getMinutes())}`,o=new Date(t.getFullYear(),t.getMonth(),t.getDate()).getTime(),a=o-864e5,n=new Date(e.getFullYear(),e.getMonth(),e.getDate()).getTime();return n===o?`Today at ${i}`:n===a?`Yesterday at ${i}`:`${_[e.getMonth()]} ${e.getDate()}, ${i}`}const q=':host{--spilki-bg-light: #ffffff;--spilki-bg-dark: #0f172a;--spilki-text-light: #0f172a;--spilki-text-dark: #f8fafc;--spilki-border-light: rgba(15, 23, 42, .1);--spilki-border-dark: rgba(148, 163, 184, .25);--spilki-muted-light: #64748b;--spilki-muted-dark: #94a3b8;--spilki-shadow: 0 10px 40px rgba(15, 23, 42, .2);--spilki-radius: 16px;--spilki-font: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;color:inherit;font-family:var(--spilki-font)}.wrapper{position:relative;width:100%;height:100%;display:flex;flex-direction:column;background:var(--spilki-surface);color:var(--spilki-text);border-radius:var(--spilki-radius);box-shadow:var(--spilki-shadow);border:1px solid var(--spilki-border);overflow:hidden}header{display:flex;align-items:center;justify-content:space-between;padding:.75rem 1rem;background:var(--spilki-surface);border-bottom:1px solid var(--spilki-border)}header h1{font-size:1rem;margin:0;display:flex;align-items:center;gap:.5rem}header button.close{border:none;background:transparent;color:inherit;font-size:1.25rem;cursor:pointer}header button.close:focus-visible{outline:2px solid var(--spilki-accent);outline-offset:2px;border-radius:4px}header .status-dot{width:10px;height:10px;border-radius:999px;background:var(--spilki-accent)}.messages{flex:1;overflow-y:auto;padding:1rem;display:flex;flex-direction:column;gap:.5rem;scrollbar-width:thin;scrollbar-color:rgba(148,163,184,.3) transparent;scroll-behavior:smooth}.message{display:flex;flex-direction:column;gap:.15rem;max-width:85%;width:fit-content;line-height:1.4;word-wrap:break-word;overflow-wrap:anywhere;white-space:pre-wrap}.message.user{align-self:flex-end;align-items:flex-end}.message .bubble{padding:.6rem .8rem;border-radius:1rem;background:#6366f126}.message.user .bubble{background:var(--spilki-accent);color:#fff}.message.bot .bubble{background:#94a3b826}.msg-time{font-size:.7rem;color:var(--spilki-muted);margin-top:2px}.date-separator{display:flex;align-items:center;gap:.5rem;padding:.4rem 0;font-size:.72rem;color:var(--spilki-muted);white-space:nowrap}.date-separator:before,.date-separator:after{content:"";flex:1;height:1px;background:var(--spilki-border)}.scroll-bottom{position:absolute;right:1rem;bottom:88px;width:38px;height:38px;border:none;border-radius:999px;background:var(--spilki-accent);color:#fff;box-shadow:0 8px 20px #0f172a40;cursor:pointer;opacity:0;pointer-events:none;transform:translateY(4px);transition:opacity .18s ease,transform .18s ease;z-index:3}.scroll-bottom.visible{opacity:1;pointer-events:auto;transform:translateY(0)}.scroll-arrow{font-size:.8rem;line-height:1}.scroll-count{position:absolute;top:-5px;right:-5px;min-width:16px;height:16px;border-radius:999px;background:#ef4444;color:#fff;font-size:10px;line-height:16px;text-align:center;padding:0 4px}.suggested-replies{display:flex;flex-wrap:wrap;gap:.4rem;padding:.5rem 1rem;border-top:1px solid var(--spilki-border)}.suggested-chip{border:1px solid var(--spilki-accent);background:transparent;color:var(--spilki-accent);border-radius:999px;padding:.35rem .75rem;font-size:.8rem;font-family:inherit;cursor:pointer;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;transition:background .15s,color .15s}.suggested-chip:hover{background:var(--spilki-accent);color:#fff}.suggested-chip:focus-visible{outline:2px solid var(--spilki-accent);outline-offset:2px}.input-area{padding:.5rem .75rem}.input-wrap{display:flex;align-items:center;border:1px solid var(--spilki-border);border-radius:1.5rem;background:transparent;padding-left:0}.input-area textarea{flex:1;resize:none;min-height:2.4rem;max-height:6rem;border:0;border-radius:0;outline:0;background:transparent;box-shadow:none;-webkit-box-shadow:none;-webkit-appearance:none;appearance:none;padding:.55rem 0 .55rem 1rem;font-family:inherit;font-size:.95rem;color:var(--spilki-text);scrollbar-width:thin;scrollbar-color:rgba(148,163,184,.3) transparent}.input-actions{display:flex;align-items:center;gap:.25rem;padding:.25rem .35rem;flex-shrink:0}.input-actions button{width:32px;height:32px;display:grid;place-items:center;border:none;border-radius:50%;cursor:pointer;padding:0;transition:opacity .15s}.emoji-btn{background:#6366f126;color:var(--spilki-accent)}.emoji-btn:hover{background:#6366f140}.emoji-btn:focus-visible{outline:2px solid var(--spilki-accent);outline-offset:1px}.send-btn{background:var(--spilki-accent);color:#fff}.send-btn:hover{opacity:.85}.send-btn:focus-visible{outline:2px solid var(--spilki-accent);outline-offset:2px}.emoji-picker{position:absolute;right:1rem;bottom:80px;width:240px;max-height:180px;overflow-y:auto;display:none;grid-template-columns:repeat(10,minmax(0,1fr));gap:.25rem;padding:.5rem;background:var(--spilki-surface);border:1px solid var(--spilki-border);border-radius:12px;box-shadow:0 14px 30px #0f172a40;z-index:4}.emoji-option{width:100%;aspect-ratio:1;border:none;border-radius:8px;background:transparent;font-size:1rem;cursor:pointer}.emoji-option:hover,.emoji-option:focus-visible{background:#94a3b833}.typing{font-size:.75rem;color:var(--spilki-muted);padding:0 1rem .75rem}.offline{font-size:.8rem;padding:0 1rem;color:#f97316}:host([data-theme="dark"]){--spilki-surface: var(--spilki-bg-dark);--spilki-text: var(--spilki-text-dark);--spilki-border: var(--spilki-border-dark);--spilki-muted: var(--spilki-muted-dark)}:host([data-theme="dark"]) .messages,:host([data-theme="dark"]) .input-area textarea,:host([data-theme="dark"]) .emoji-picker{scrollbar-color:rgba(255,255,255,.2) transparent}:host([data-theme="light"]){--spilki-surface: var(--spilki-bg-light);--spilki-text: var(--spilki-text-light);--spilki-border: var(--spilki-border-light);--spilki-muted: var(--spilki-muted-light)}:host([data-theme="dark"]) .message .bubble{background:#94a3b81f}:host([data-theme="dark"]) .message.bot .bubble{background:#6366f126}:host([data-theme="dark"]) .input-area textarea{background:transparent}.messages::-webkit-scrollbar,.input-area textarea::-webkit-scrollbar,.emoji-picker::-webkit-scrollbar{width:6px}.messages::-webkit-scrollbar-track,.input-area textarea::-webkit-scrollbar-track,.emoji-picker::-webkit-scrollbar-track{background:transparent}.messages::-webkit-scrollbar-thumb,.input-area textarea::-webkit-scrollbar-thumb,.emoji-picker::-webkit-scrollbar-thumb{background:#94a3b84d;border-radius:999px}.messages::-webkit-scrollbar-thumb:hover,.input-area textarea::-webkit-scrollbar-thumb:hover,.emoji-picker::-webkit-scrollbar-thumb:hover{background:#94a3b880}:host([data-theme="dark"]) .messages::-webkit-scrollbar-thumb,:host([data-theme="dark"]) .input-area textarea::-webkit-scrollbar-thumb,:host([data-theme="dark"]) .emoji-picker::-webkit-scrollbar-thumb{background:#fff3}:host([data-theme="dark"]) .messages::-webkit-scrollbar-thumb:hover,:host([data-theme="dark"]) .input-area textarea::-webkit-scrollbar-thumb:hover,:host([data-theme="dark"]) .emoji-picker::-webkit-scrollbar-thumb:hover{background:#ffffff59}.conversation-separator{display:flex;align-items:center;gap:.5rem;padding:.5rem 0;cursor:pointer;user-select:none;font-size:.7rem;color:var(--spilki-muted);white-space:nowrap}.conversation-separator:before,.conversation-separator:after{content:"";flex:1;height:1px;background:var(--spilki-border)}.conversation-separator:hover{color:var(--spilki-text)}.conversation-separator:focus-visible{outline:2px solid var(--spilki-accent);outline-offset:2px;border-radius:4px}.conversation-history{display:none;flex-direction:column;gap:.5rem}.conversation-history.expanded{display:flex}',I=24*60*60*1e3,Y=2*60*1e3,V=200,J=["😀","😂","🤣","😊","😍","🥰","😘","😉","🤔","😏","😎","🤗","😢","😭","😡","🤯","😱","🥳","😴","🤮","👍","👎","👋","🤝","🙏","💪","✌️","🤞","👏","🫶","❤️","🔥","⭐","💯","✅","❌","⚡","🎉","💰","📦","📅","📍","📞","💬","🔔","🔗","📸","🎵","☕","🍕"];function v(r){return r.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}class X{constructor(e){this.options=e,this.relativeTimeFormat=new Intl.RelativeTimeFormat(void 0,{numeric:"auto"}),this.absoluteTimeFormat=new Intl.DateTimeFormat(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit",hour12:!1}),this.dateSeparatorFormat=new Intl.DateTimeFormat(void 0,{month:"short",day:"numeric",year:"numeric"}),this.renderedMessages=[],this.focusable=[],this.seenIds=new Set,this.lastTimelineMessage=null,this.scrollUnreadCount=0,this.emojiPickerOpen=!1,this.open=!1,this.host=document.createElement("div"),this.host.setAttribute("part","panel-root"),this.host.style.position="fixed",this.host.style.bottom="96px",this.host.style[e.position==="bottom-right"?"right":"left"]="24px",this.host.style.width="360px",this.host.style.maxWidth="calc(100vw - 32px)",this.host.style.height="520px",this.host.style.display="none",this.host.style.zIndex="2147483001",this.shadow=this.host.attachShadow({mode:"open"});const t=v(e.i18n.title),s=v(e.i18n.typing),i=v(e.i18n.offline),o=v(e.i18n.placeholder),a=v(e.i18n.sendLabel);this.shadow.innerHTML=`
74
+ <style>${q}</style>
75
75
  <div class="wrapper" role="dialog" aria-modal="true" aria-label="${t}">
76
76
  <header>
77
77
  <h1><span class="status-dot" aria-hidden="true"></span>${t}</h1>
@@ -107,8 +107,8 @@
107
107
  </div>
108
108
  </div>
109
109
  </div>
110
- `,this.host.dataset.theme=e.theme,this.host.style.setProperty("--spilki-accent",e.color),this.messagesEl=this.shadow.querySelector(".messages"),this.typingEl=this.shadow.querySelector(".typing"),this.input=this.shadow.querySelector("textarea"),this.offlineEl=this.shadow.querySelector(".offline"),this.sendButton=this.shadow.querySelector(".send-btn"),this.emojiButton=this.shadow.querySelector(".emoji-btn"),this.emojiPickerEl=this.shadow.querySelector(".emoji-picker"),this.scrollBottomButton=this.shadow.querySelector(".scroll-bottom"),this.scrollBottomCountEl=this.shadow.querySelector(".scroll-count"),this.closeButton=this.shadow.querySelector("header .close"),this.suggestedRepliesEl=this.shadow.querySelector(".suggested-replies"),this.handleCloseClick=()=>this.options.onClose(),this.handleSendClick=()=>this.send(),this.handleInputKeydown=n=>{if(n.key==="Enter"&&!n.shiftKey)n.preventDefault(),this.send();else if(n.key==="Escape"){if(this.emojiPickerOpen){n.stopPropagation(),this.closeEmojiPicker();return}this.options.onClose()}},this.handleShadowKeydown=n=>{const l=n;if(l.key==="Escape"){if(this.emojiPickerOpen){l.preventDefault(),this.closeEmojiPicker();return}this.options.onClose()}l.key==="Tab"&&this.trapFocus(l)},this.handleFocusin=()=>this.collectFocusable(),this.handleMessagesScroll=()=>this.updateScrollBottomState(),this.handleScrollBottomClick=()=>{this.scrollToBottom(),this.resetScrollUnreadCount()},this.handleEmojiClick=()=>{this.toggleEmojiPicker()},this.handleDocumentPointerDown=n=>{if(!this.emojiPickerOpen)return;const l=n.composedPath();l.includes(this.emojiPickerEl)||l.includes(this.emojiButton)||this.closeEmojiPicker()},this.closeButton.addEventListener("click",this.handleCloseClick),this.sendButton.addEventListener("click",this.handleSendClick),this.emojiButton.addEventListener("click",this.handleEmojiClick),this.scrollBottomButton.addEventListener("click",this.handleScrollBottomClick),this.input.addEventListener("keydown",this.handleInputKeydown),this.messagesEl.addEventListener("scroll",this.handleMessagesScroll),this.shadow.addEventListener("keydown",this.handleShadowKeydown),this.shadow.addEventListener("focusin",this.handleFocusin),document.addEventListener("pointerdown",this.handleDocumentPointerDown,!0),this.renderEmojiPicker(),this.updateScrollBottomState(),this.collectFocusable()}mount(){document.body.appendChild(this.host)}destroy(){this.closeButton.removeEventListener("click",this.handleCloseClick),this.sendButton.removeEventListener("click",this.handleSendClick),this.emojiButton.removeEventListener("click",this.handleEmojiClick),this.scrollBottomButton.removeEventListener("click",this.handleScrollBottomClick),this.input.removeEventListener("keydown",this.handleInputKeydown),this.messagesEl.removeEventListener("scroll",this.handleMessagesScroll),this.shadow.removeEventListener("keydown",this.handleShadowKeydown),this.shadow.removeEventListener("focusin",this.handleFocusin),document.removeEventListener("pointerdown",this.handleDocumentPointerDown,!0),this.host.remove()}show(){this.open||(this.open=!0,this.host.style.display="block",this.focusInput())}hide(){this.open&&(this.open=!1,this.closeEmojiPicker(),this.host.style.display="none")}focusInput(){queueMicrotask(()=>{this.input.focus()})}updateTheme(e){this.host.dataset.theme=e}updateMessages(e){this.messagesEl.innerHTML="",this.seenIds.clear(),this.renderedMessages.length=0,this.lastTimelineMessage=null,e.forEach(t=>{this.seenIds.add(t.id),this.appendTimelineMessage(t)}),this.updateTimestampVisibility(),this.scrollToBottom(),this.resetScrollUnreadCount(),this.collectFocusable()}renderWithConversations(e,t){this.messagesEl.innerHTML="",this.seenIds.clear(),this.renderedMessages.length=0,this.lastTimelineMessage=null;for(const s of e){s.messages.forEach(n=>this.seenIds.add(n.id));const i=this.createHistoryContainer(s.messages),o=s.messages[s.messages.length-1].ts,a=this.createSeparatorElement(o,i);this.messagesEl.appendChild(i),this.messagesEl.appendChild(a)}t.forEach(s=>{this.seenIds.add(s.id),this.appendTimelineMessage(s)}),this.updateTimestampVisibility(),this.scrollToBottom(),this.resetScrollUnreadCount(),this.collectFocusable()}appendMessage(e){if(this.seenIds.has(e.id))return;const t=this.isScrolledUp();this.seenIds.add(e.id),this.appendTimelineMessage(e),this.updateTimestampVisibility(),t?(this.scrollUnreadCount+=1,this.updateScrollBottomState()):(this.scrollToBottom(),this.resetScrollUnreadCount()),this.collectFocusable()}setSuggestedReplies(e){if(e.length===0){this.hideSuggestedReplies();return}this.suggestedRepliesEl.replaceChildren(),e.forEach(t=>{const s=document.createElement("button");s.className="suggested-chip",s.type="button",s.textContent=t.text,s.addEventListener("click",()=>{var i;this.options.onSend((i=t.payload)!=null?i:t.text),this.hideSuggestedReplies()}),this.suggestedRepliesEl.appendChild(s)}),this.suggestedRepliesEl.toggleAttribute("hidden",!1),this.collectFocusable()}hideSuggestedReplies(){this.suggestedRepliesEl.toggleAttribute("hidden",!0),this.suggestedRepliesEl.replaceChildren(),this.collectFocusable(),this.focusInput()}setTyping(e){this.typingEl.toggleAttribute("hidden",!e)}setOffline(e){this.offlineEl.toggleAttribute("hidden",!e)}clearInput(){this.input.value="",this.input.dispatchEvent(new Event("input"))}send(){const e=this.input.value.trim();e&&(this.options.onSend(e),this.clearInput(),this.hideSuggestedReplies())}createMessageElement(e){const t=document.createElement("div");t.className=`message ${e.author}`,t.setAttribute("data-author",e.author),t.setAttribute("role","article"),t.setAttribute("aria-label",e.author==="user"?"You":"Assistant");const s=document.createElement("div");s.className="bubble",s.textContent=e.text;const i=document.createElement("span");return i.className="msg-time",i.textContent=this.formatMessageTimestamp(e.ts),t.appendChild(s),t.appendChild(i),{item:t,timeEl:i}}createSeparatorElement(e,t){const s=document.createElement("div");s.className="conversation-separator",s.setAttribute("role","button"),s.setAttribute("tabindex","0"),s.setAttribute("aria-expanded","false"),s.setAttribute("aria-label","Show previous conversation"),s.textContent=_(e);const i=()=>{const o=t.classList.toggle("expanded");s.setAttribute("aria-expanded",String(o)),s.setAttribute("aria-label",o?"Hide previous conversation":"Show previous conversation")};return s.addEventListener("click",i),s.addEventListener("keydown",o=>{const a=o;(a.key==="Enter"||a.key===" ")&&(a.preventDefault(),i())}),s}createHistoryContainer(e){const t=document.createElement("div");t.className="conversation-history";let s=null;return e.forEach(i=>{this.appendDateSeparatorIfNeeded(s,i,t);const{item:o}=this.createMessageElement(i);t.appendChild(o),s=i}),t}appendTimelineMessage(e){this.appendDateSeparatorIfNeeded(this.lastTimelineMessage,e,this.messagesEl);const t=this.createMessageElement(e);this.messagesEl.appendChild(t.item),this.renderedMessages.push({message:e,timeEl:t.timeEl}),this.lastTimelineMessage=e}appendDateSeparatorIfNeeded(e,t,s){if(!e||this.isSameDay(e.ts,t.ts))return;const i=document.createElement("div");i.className="date-separator",i.textContent=this.formatDateSeparator(t.ts),s.appendChild(i)}updateTimestampVisibility(){for(let e=0;e<this.renderedMessages.length;e+=1){const t=this.renderedMessages[e],s=this.renderedMessages[e+1],i=!!s&&s.message.author===t.message.author&&s.message.ts-t.message.ts<=V&&s.message.ts>=t.message.ts;t.timeEl.toggleAttribute("hidden",i),t.timeEl.textContent=this.formatMessageTimestamp(t.message.ts)}}formatMessageTimestamp(e){const s=Date.now()-e;if(s<M){const i=Math.max(1,Math.round(s/6e4));if(i<60)return this.relativeTimeFormat.format(-i,"minute");const o=Math.max(1,Math.round(i/60));return this.relativeTimeFormat.format(-o,"hour")}return this.absoluteTimeFormat.format(new Date(e))}formatDateSeparator(e){const t=new Date(e),s=this.startOfDay(Date.now()),i=this.startOfDay(e);return i===s?"Today":i===s-M?"Yesterday":this.dateSeparatorFormat.format(t)}isSameDay(e,t){return this.startOfDay(e)===this.startOfDay(t)}startOfDay(e){const t=new Date(e);return new Date(t.getFullYear(),t.getMonth(),t.getDate()).getTime()}isScrolledUp(){return this.distanceFromBottom()>Y}distanceFromBottom(){return this.messagesEl.scrollHeight-this.messagesEl.scrollTop-this.messagesEl.clientHeight}scrollToFirstUnread(e){if(e<=0){this.scrollToBottom();return}const t=this.renderedMessages.find(i=>i.message.author==="bot"&&i.message.ts>e);if(!t){this.scrollToBottom();return}const s=t.timeEl.closest(".message");s&&s.scrollIntoView({behavior:"smooth",block:"start"})}scrollToBottom(){if(typeof this.messagesEl.scrollTo=="function"){this.messagesEl.scrollTo({top:this.messagesEl.scrollHeight,behavior:"smooth"});return}this.messagesEl.scrollTop=this.messagesEl.scrollHeight}updateScrollBottomState(){const e=this.isScrolledUp();this.scrollBottomButton.classList.toggle("visible",e),this.scrollBottomButton.setAttribute("aria-hidden",String(!e)),this.scrollBottomButton.tabIndex=e?0:-1,e||this.resetScrollUnreadCount();const t=this.scrollUnreadCount>0;this.scrollBottomCountEl.toggleAttribute("hidden",!t),this.scrollBottomCountEl.textContent=t?this.scrollUnreadCount>99?"99+":String(this.scrollUnreadCount):"0"}resetScrollUnreadCount(){this.scrollUnreadCount!==0&&(this.scrollUnreadCount=0,this.updateScrollBottomState())}toggleEmojiPicker(){if(this.emojiPickerOpen){this.closeEmojiPicker();return}this.emojiPickerOpen=!0,this.emojiPickerEl.style.display="grid",this.emojiButton.setAttribute("aria-expanded","true"),this.collectFocusable()}closeEmojiPicker(){this.emojiPickerOpen&&(this.emojiPickerOpen=!1,this.emojiPickerEl.style.display="none",this.emojiButton.setAttribute("aria-expanded","false"),this.collectFocusable())}renderEmojiPicker(){this.emojiPickerEl.replaceChildren(),G.forEach(e=>{const t=document.createElement("button");t.type="button",t.className="emoji-option",t.textContent=e,t.setAttribute("aria-label",`Insert ${e}`),t.addEventListener("click",()=>{this.insertEmojiAtCursor(e),this.closeEmojiPicker()}),this.emojiPickerEl.appendChild(t)})}insertEmojiAtCursor(e){var a,n;const t=this.input.value,s=(a=this.input.selectionStart)!=null?a:t.length,i=(n=this.input.selectionEnd)!=null?n:t.length;this.input.value=`${t.slice(0,s)}${e}${t.slice(i)}`;const o=s+e.length;this.input.setSelectionRange(o,o),this.input.dispatchEvent(new Event("input",{bubbles:!0})),this.input.focus()}collectFocusable(){const e=this.shadow.querySelectorAll('button, textarea, [href], [tabindex]:not([tabindex="-1"])');this.focusable.length=0,e.forEach(t=>{!t.hasAttribute("disabled")&&!t.hasAttribute("hidden")&&t.tabIndex>=0&&this.focusable.push(t)})}trapFocus(e){if(this.focusable.length===0)return;const t=this.focusable[0],s=this.focusable[this.focusable.length-1],i=this.shadow.activeElement;e.shiftKey&&i===t?(e.preventDefault(),s.focus()):!e.shiftKey&&i===s&&(e.preventDefault(),t.focus())}}const w=1500,I=8e3;class Z{constructor(e,t){this.sessionId=null,this.currentKind=null,this.stopped=!1,this.backoff=w,this.connectPromise=null,this.options=e,this.handlers=t}setAccessToken(e){this.options.accessToken=e}get kind(){return this.currentKind}get activeSession(){var e,t;return(t=(e=this.sessionId)!=null?e:this.options.sessionId)!=null?t:null}async connect(){if(this.connectPromise)return this.connectPromise;this.connectPromise=this.doConnect();try{return await this.connectPromise}finally{this.connectPromise=null}}async doConnect(){var a,n;this.stopped=!1;const e=`${this.options.apiBase.replace(/\/$/,"")}/widget/session`,t=(n=(a=this.sessionId)!=null?a:this.options.sessionId)!=null?n:void 0,s={organisationId:this.options.org,sessionId:t,userAgent:typeof navigator!="undefined"?navigator.userAgent:"",referrer:typeof document!="undefined"?document.referrer:"",origin:typeof window!="undefined"?window.location.origin:""},i=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json",...this.options.accessToken?{"X-Authorization":`Bearer ${this.options.accessToken}`}:{}},body:JSON.stringify(s)});if(!i.ok)throw new Error(`SpilkiWidget: connect failed (${i.status})`);const o=await i.json();return this.sessionId=o.sessionId,this.options.sessionId=o.sessionId,this.backoff=w,await this.startTransport(o),o}async send(e,t){var a,n;const s={sessionId:(n=(a=this.sessionId)!=null?a:this.options.sessionId)!=null?n:"",text:e,...t?{messageId:t}:{}};if(!s.sessionId)throw new Error("SpilkiWidget: missing session id");if(this.currentKind==="ws"&&this.ws&&this.ws.readyState===WebSocket.OPEN){this.ws.send(JSON.stringify({type:"message",payload:s}));return}const i=`${this.options.apiBase.replace(/\/$/,"")}/widget/message`,o=await fetch(i,{method:"POST",headers:{"Content-Type":"application/json","X-Authorization":`Bearer ${this.options.accessToken}`},body:JSON.stringify(s)});if(!o.ok)throw new Error(`SpilkiWidget: send failed (${o.status})`)}stop(e=!0){this.stopped=!0,e&&(this.backoff=w),this.retryTimer&&(clearTimeout(this.retryTimer),this.retryTimer=void 0),this.ws&&(this.wsOnOpen&&this.ws.removeEventListener("open",this.wsOnOpen),this.wsOnMessage&&this.ws.removeEventListener("message",this.wsOnMessage),this.wsOnClose&&this.ws.removeEventListener("close",this.wsOnClose),this.wsOnError&&this.ws.removeEventListener("error",this.wsOnError),this.ws.close(),this.ws=void 0),this.wsOnOpen=this.wsOnMessage=this.wsOnClose=this.wsOnError=void 0,this.sseAbort&&(this.sseAbort.abort(),this.sseAbort=void 0),this.pollTimer&&(clearTimeout(this.pollTimer),this.pollTimer=void 0),this.currentKind=null}async startTransport(e){if(this.stopped)return;const t=[];e.wsUrl&&t.push(()=>this.startWs(e.wsUrl)),e.sseUrl&&t.push(()=>this.startSse(e.sseUrl)),e.pollUrl&&t.push(()=>this.startPoll(e.pollUrl));for(const s of t)try{await s();return}catch(i){this.handlers.onError(i)}throw new Error("SpilkiWidget: unable to establish transport")}startWs(e){return new Promise((t,s)=>{try{const i=new WebSocket(e);this.ws=i,this.wsOnOpen=()=>{if(this.stopped){i.close();return}this.currentKind="ws",this.handlers.onOpen("ws"),this.backoff=w,t()},this.wsOnMessage=o=>this.handleIncoming(o.data),this.wsOnClose=()=>{this.stopped||this.retryFallback("ws")},this.wsOnError=()=>{this.handlers.onError(new Error("SpilkiWidget: websocket error")),i.readyState!==WebSocket.OPEN&&s(new Error("WebSocket failed"))},i.addEventListener("open",this.wsOnOpen),i.addEventListener("message",this.wsOnMessage),i.addEventListener("close",this.wsOnClose),i.addEventListener("error",this.wsOnError)}catch(i){s(i)}})}startSse(e){return new Promise((t,s)=>{const i=new AbortController;this.sseAbort=i,fetch(e,{headers:{Accept:"text/event-stream","X-Authorization":`Bearer ${this.options.accessToken}`},signal:i.signal}).then(o=>{if(!o.ok||!o.body){s(new Error("SSE failed"));return}if(this.stopped){i.abort();return}this.currentKind="sse",this.handlers.onOpen("sse"),this.backoff=w,t();const a=o.body.getReader(),n=new TextDecoder;let l="";const g=()=>{a.read().then(({done:m,value:d})=>{var f;if(m||this.stopped){this.stopped||(this.handlers.onError(new Error("SpilkiWidget: SSE stream ended")),this.retryFallback("sse"));return}l+=n.decode(d,{stream:!0});const h=l.split(`
110
+ `,this.host.dataset.theme=e.theme,this.host.style.setProperty("--spilki-accent",e.color),this.messagesEl=this.shadow.querySelector(".messages"),this.typingEl=this.shadow.querySelector(".typing"),this.input=this.shadow.querySelector("textarea"),this.offlineEl=this.shadow.querySelector(".offline"),this.sendButton=this.shadow.querySelector(".send-btn"),this.emojiButton=this.shadow.querySelector(".emoji-btn"),this.emojiPickerEl=this.shadow.querySelector(".emoji-picker"),this.scrollBottomButton=this.shadow.querySelector(".scroll-bottom"),this.scrollBottomCountEl=this.shadow.querySelector(".scroll-count"),this.closeButton=this.shadow.querySelector("header .close"),this.suggestedRepliesEl=this.shadow.querySelector(".suggested-replies"),this.handleCloseClick=()=>this.options.onClose(),this.handleSendClick=()=>this.send(),this.handleInputKeydown=n=>{if(n.key==="Enter"&&!n.shiftKey)n.preventDefault(),this.send();else if(n.key==="Escape"){if(this.emojiPickerOpen){n.stopPropagation(),this.closeEmojiPicker();return}this.options.onClose()}},this.handleShadowKeydown=n=>{const l=n;if(l.key==="Escape"){if(this.emojiPickerOpen){l.preventDefault(),this.closeEmojiPicker();return}this.options.onClose()}l.key==="Tab"&&this.trapFocus(l)},this.handleFocusin=()=>this.collectFocusable(),this.handleMessagesScroll=()=>this.updateScrollBottomState(),this.handleScrollBottomClick=()=>{this.scrollToBottom(),this.resetScrollUnreadCount()},this.handleEmojiClick=()=>{this.toggleEmojiPicker()},this.handleDocumentPointerDown=n=>{if(!this.emojiPickerOpen)return;const l=n.composedPath();l.includes(this.emojiPickerEl)||l.includes(this.emojiButton)||this.closeEmojiPicker()},this.closeButton.addEventListener("click",this.handleCloseClick),this.sendButton.addEventListener("click",this.handleSendClick),this.emojiButton.addEventListener("click",this.handleEmojiClick),this.scrollBottomButton.addEventListener("click",this.handleScrollBottomClick),this.input.addEventListener("keydown",this.handleInputKeydown),this.messagesEl.addEventListener("scroll",this.handleMessagesScroll),this.shadow.addEventListener("keydown",this.handleShadowKeydown),this.shadow.addEventListener("focusin",this.handleFocusin),document.addEventListener("pointerdown",this.handleDocumentPointerDown,!0),this.renderEmojiPicker(),this.updateScrollBottomState(),this.collectFocusable()}mount(){document.body.appendChild(this.host)}destroy(){this.closeButton.removeEventListener("click",this.handleCloseClick),this.sendButton.removeEventListener("click",this.handleSendClick),this.emojiButton.removeEventListener("click",this.handleEmojiClick),this.scrollBottomButton.removeEventListener("click",this.handleScrollBottomClick),this.input.removeEventListener("keydown",this.handleInputKeydown),this.messagesEl.removeEventListener("scroll",this.handleMessagesScroll),this.shadow.removeEventListener("keydown",this.handleShadowKeydown),this.shadow.removeEventListener("focusin",this.handleFocusin),document.removeEventListener("pointerdown",this.handleDocumentPointerDown,!0),this.host.remove()}show(){this.open||(this.open=!0,this.host.style.display="block",this.focusInput())}hide(){this.open&&(this.open=!1,this.closeEmojiPicker(),this.host.style.display="none")}focusInput(){queueMicrotask(()=>{this.input.focus()})}updateTheme(e){this.host.dataset.theme=e}updateMessages(e){this.messagesEl.innerHTML="",this.seenIds.clear(),this.renderedMessages.length=0,this.lastTimelineMessage=null,e.forEach(t=>{this.seenIds.add(t.id),this.appendTimelineMessage(t)}),this.updateTimestampVisibility(),this.scrollToBottom(),this.resetScrollUnreadCount(),this.collectFocusable()}renderWithConversations(e,t){this.messagesEl.innerHTML="",this.seenIds.clear(),this.renderedMessages.length=0,this.lastTimelineMessage=null;for(const s of e){s.messages.forEach(n=>this.seenIds.add(n.id));const i=this.createHistoryContainer(s.messages),o=s.messages[s.messages.length-1].ts,a=this.createSeparatorElement(o,i);this.messagesEl.appendChild(i),this.messagesEl.appendChild(a)}t.forEach(s=>{this.seenIds.add(s.id),this.appendTimelineMessage(s)}),this.updateTimestampVisibility(),this.scrollToBottom(),this.resetScrollUnreadCount(),this.collectFocusable()}appendMessage(e){if(this.seenIds.has(e.id))return;const t=this.isScrolledUp();this.seenIds.add(e.id),this.appendTimelineMessage(e),this.updateTimestampVisibility(),t?(this.scrollUnreadCount+=1,this.updateScrollBottomState()):(this.scrollToBottom(),this.resetScrollUnreadCount()),this.collectFocusable()}setSuggestedReplies(e){if(e.length===0){this.hideSuggestedReplies();return}this.suggestedRepliesEl.replaceChildren(),e.forEach(t=>{const s=document.createElement("button");s.className="suggested-chip",s.type="button",s.textContent=t.text,s.addEventListener("click",()=>{var i;this.options.onSend((i=t.payload)!=null?i:t.text),this.hideSuggestedReplies()}),this.suggestedRepliesEl.appendChild(s)}),this.suggestedRepliesEl.toggleAttribute("hidden",!1),this.collectFocusable()}hideSuggestedReplies(){this.suggestedRepliesEl.toggleAttribute("hidden",!0),this.suggestedRepliesEl.replaceChildren(),this.collectFocusable(),this.focusInput()}setAgentActivity(e,t){var i;if(e===null){this.typingEl.toggleAttribute("hidden",!0);return}const s={TYPING:t.typing,THINKING:t.thinking,SEARCHING:t.searching,EXECUTING_TOOL:t.executingTool};this.typingEl.textContent=(i=s[e])!=null?i:t.typing,this.typingEl.toggleAttribute("hidden",!1)}setTyping(e){this.typingEl.toggleAttribute("hidden",!e)}setOffline(e){this.offlineEl.toggleAttribute("hidden",!e)}clearInput(){this.input.value="",this.input.dispatchEvent(new Event("input"))}send(){const e=this.input.value.trim();e&&(this.options.onSend(e),this.clearInput(),this.hideSuggestedReplies())}createMessageElement(e){const t=document.createElement("div");t.className=`message ${e.author}`,t.setAttribute("data-author",e.author),t.setAttribute("role","article"),t.setAttribute("aria-label",e.author==="user"?"You":"Assistant");const s=document.createElement("div");s.className="bubble",s.textContent=e.text;const i=document.createElement("span");return i.className="msg-time",i.textContent=this.formatMessageTimestamp(e.ts),t.appendChild(s),t.appendChild(i),{item:t,timeEl:i}}createSeparatorElement(e,t){const s=document.createElement("div");s.className="conversation-separator",s.setAttribute("role","button"),s.setAttribute("tabindex","0"),s.setAttribute("aria-expanded","false"),s.setAttribute("aria-label","Show previous conversation"),s.textContent=G(e);const i=()=>{const o=t.classList.toggle("expanded");s.setAttribute("aria-expanded",String(o)),s.setAttribute("aria-label",o?"Hide previous conversation":"Show previous conversation")};return s.addEventListener("click",i),s.addEventListener("keydown",o=>{const a=o;(a.key==="Enter"||a.key===" ")&&(a.preventDefault(),i())}),s}createHistoryContainer(e){const t=document.createElement("div");t.className="conversation-history";let s=null;return e.forEach(i=>{this.appendDateSeparatorIfNeeded(s,i,t);const{item:o}=this.createMessageElement(i);t.appendChild(o),s=i}),t}appendTimelineMessage(e){this.appendDateSeparatorIfNeeded(this.lastTimelineMessage,e,this.messagesEl);const t=this.createMessageElement(e);this.messagesEl.appendChild(t.item),this.renderedMessages.push({message:e,timeEl:t.timeEl}),this.lastTimelineMessage=e}appendDateSeparatorIfNeeded(e,t,s){if(!e||this.isSameDay(e.ts,t.ts))return;const i=document.createElement("div");i.className="date-separator",i.textContent=this.formatDateSeparator(t.ts),s.appendChild(i)}updateTimestampVisibility(){for(let e=0;e<this.renderedMessages.length;e+=1){const t=this.renderedMessages[e],s=this.renderedMessages[e+1],i=!!s&&s.message.author===t.message.author&&s.message.ts-t.message.ts<=Y&&s.message.ts>=t.message.ts;t.timeEl.toggleAttribute("hidden",i),t.timeEl.textContent=this.formatMessageTimestamp(t.message.ts)}}formatMessageTimestamp(e){const s=Date.now()-e;if(s<I){const i=Math.max(1,Math.round(s/6e4));if(i<60)return this.relativeTimeFormat.format(-i,"minute");const o=Math.max(1,Math.round(i/60));return this.relativeTimeFormat.format(-o,"hour")}return this.absoluteTimeFormat.format(new Date(e))}formatDateSeparator(e){const t=new Date(e),s=this.startOfDay(Date.now()),i=this.startOfDay(e);return i===s?"Today":i===s-I?"Yesterday":this.dateSeparatorFormat.format(t)}isSameDay(e,t){return this.startOfDay(e)===this.startOfDay(t)}startOfDay(e){const t=new Date(e);return new Date(t.getFullYear(),t.getMonth(),t.getDate()).getTime()}isScrolledUp(){return this.distanceFromBottom()>V}distanceFromBottom(){return this.messagesEl.scrollHeight-this.messagesEl.scrollTop-this.messagesEl.clientHeight}scrollToFirstUnread(e){if(e<=0){this.scrollToBottom();return}const t=this.renderedMessages.find(i=>i.message.author==="bot"&&i.message.ts>e);if(!t){this.scrollToBottom();return}const s=t.timeEl.closest(".message");s&&s.scrollIntoView({behavior:"smooth",block:"start"})}scrollToBottom(){if(typeof this.messagesEl.scrollTo=="function"){this.messagesEl.scrollTo({top:this.messagesEl.scrollHeight,behavior:"smooth"});return}this.messagesEl.scrollTop=this.messagesEl.scrollHeight}updateScrollBottomState(){const e=this.isScrolledUp();this.scrollBottomButton.classList.toggle("visible",e),this.scrollBottomButton.setAttribute("aria-hidden",String(!e)),this.scrollBottomButton.tabIndex=e?0:-1,e||this.resetScrollUnreadCount();const t=this.scrollUnreadCount>0;this.scrollBottomCountEl.toggleAttribute("hidden",!t),this.scrollBottomCountEl.textContent=t?this.scrollUnreadCount>99?"99+":String(this.scrollUnreadCount):"0"}resetScrollUnreadCount(){this.scrollUnreadCount!==0&&(this.scrollUnreadCount=0,this.updateScrollBottomState())}toggleEmojiPicker(){if(this.emojiPickerOpen){this.closeEmojiPicker();return}this.emojiPickerOpen=!0,this.emojiPickerEl.style.display="grid",this.emojiButton.setAttribute("aria-expanded","true"),this.collectFocusable()}closeEmojiPicker(){this.emojiPickerOpen&&(this.emojiPickerOpen=!1,this.emojiPickerEl.style.display="none",this.emojiButton.setAttribute("aria-expanded","false"),this.collectFocusable())}renderEmojiPicker(){this.emojiPickerEl.replaceChildren(),J.forEach(e=>{const t=document.createElement("button");t.type="button",t.className="emoji-option",t.textContent=e,t.setAttribute("aria-label",`Insert ${e}`),t.addEventListener("click",()=>{this.insertEmojiAtCursor(e),this.closeEmojiPicker()}),this.emojiPickerEl.appendChild(t)})}insertEmojiAtCursor(e){var a,n;const t=this.input.value,s=(a=this.input.selectionStart)!=null?a:t.length,i=(n=this.input.selectionEnd)!=null?n:t.length;this.input.value=`${t.slice(0,s)}${e}${t.slice(i)}`;const o=s+e.length;this.input.setSelectionRange(o,o),this.input.dispatchEvent(new Event("input",{bubbles:!0})),this.input.focus()}collectFocusable(){const e=this.shadow.querySelectorAll('button, textarea, [href], [tabindex]:not([tabindex="-1"])');this.focusable.length=0,e.forEach(t=>{!t.hasAttribute("disabled")&&!t.hasAttribute("hidden")&&t.tabIndex>=0&&this.focusable.push(t)})}trapFocus(e){if(this.focusable.length===0)return;const t=this.focusable[0],s=this.focusable[this.focusable.length-1],i=this.shadow.activeElement;e.shiftKey&&i===t?(e.preventDefault(),s.focus()):!e.shiftKey&&i===s&&(e.preventDefault(),t.focus())}}const y=1500,O=8e3;class Z{constructor(e,t){this.sessionId=null,this.currentKind=null,this.stopped=!1,this.backoff=y,this.connectPromise=null,this.options=e,this.handlers=t}setAccessToken(e){this.options.accessToken=e}get kind(){return this.currentKind}get activeSession(){var e,t;return(t=(e=this.sessionId)!=null?e:this.options.sessionId)!=null?t:null}async connect(){if(this.connectPromise)return this.connectPromise;this.connectPromise=this.doConnect();try{return await this.connectPromise}finally{this.connectPromise=null}}async doConnect(){var a,n;this.stopped=!1;const e=`${this.options.apiBase.replace(/\/$/,"")}/widget/session`,t=(n=(a=this.sessionId)!=null?a:this.options.sessionId)!=null?n:void 0,s={organisationId:this.options.org,sessionId:t,userAgent:typeof navigator!="undefined"?navigator.userAgent:"",referrer:typeof document!="undefined"?document.referrer:"",origin:typeof window!="undefined"?window.location.origin:""},i=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json",...this.options.accessToken?{"X-Authorization":`Bearer ${this.options.accessToken}`}:{}},body:JSON.stringify(s)});if(!i.ok)throw new Error(`SpilkiWidget: connect failed (${i.status})`);const o=await i.json();return this.sessionId=o.sessionId,this.options.sessionId=o.sessionId,this.backoff=y,await this.startTransport(o),o}async send(e,t){var a,n;const s={sessionId:(n=(a=this.sessionId)!=null?a:this.options.sessionId)!=null?n:"",text:e,...t?{messageId:t}:{}};if(!s.sessionId)throw new Error("SpilkiWidget: missing session id");if(this.currentKind==="ws"&&this.ws&&this.ws.readyState===WebSocket.OPEN){this.ws.send(JSON.stringify({type:"message",payload:s}));return}const i=`${this.options.apiBase.replace(/\/$/,"")}/widget/message`,o=await fetch(i,{method:"POST",headers:{"Content-Type":"application/json","X-Authorization":`Bearer ${this.options.accessToken}`},body:JSON.stringify(s)});if(!o.ok)throw new Error(`SpilkiWidget: send failed (${o.status})`)}stop(e=!0){this.stopped=!0,e&&(this.backoff=y),this.retryTimer&&(clearTimeout(this.retryTimer),this.retryTimer=void 0),this.ws&&(this.wsOnOpen&&this.ws.removeEventListener("open",this.wsOnOpen),this.wsOnMessage&&this.ws.removeEventListener("message",this.wsOnMessage),this.wsOnClose&&this.ws.removeEventListener("close",this.wsOnClose),this.wsOnError&&this.ws.removeEventListener("error",this.wsOnError),this.ws.close(),this.ws=void 0),this.wsOnOpen=this.wsOnMessage=this.wsOnClose=this.wsOnError=void 0,this.sseAbort&&(this.sseAbort.abort(),this.sseAbort=void 0),this.pollTimer&&(clearTimeout(this.pollTimer),this.pollTimer=void 0),this.currentKind=null}async startTransport(e){if(this.stopped)return;const t=[];e.wsUrl&&t.push(()=>this.startWs(e.wsUrl)),e.sseUrl&&t.push(()=>this.startSse(e.sseUrl)),e.pollUrl&&t.push(()=>this.startPoll(e.pollUrl));for(const s of t)try{await s();return}catch(i){this.handlers.onError(i)}throw new Error("SpilkiWidget: unable to establish transport")}startWs(e){return new Promise((t,s)=>{try{const i=new WebSocket(e);this.ws=i,this.wsOnOpen=()=>{if(this.stopped){i.close();return}this.currentKind="ws",this.handlers.onOpen("ws"),this.backoff=y,t()},this.wsOnMessage=o=>this.handleIncoming(o.data),this.wsOnClose=()=>{this.stopped||this.retryFallback("ws")},this.wsOnError=()=>{this.handlers.onError(new Error("SpilkiWidget: websocket error")),i.readyState!==WebSocket.OPEN&&s(new Error("WebSocket failed"))},i.addEventListener("open",this.wsOnOpen),i.addEventListener("message",this.wsOnMessage),i.addEventListener("close",this.wsOnClose),i.addEventListener("error",this.wsOnError)}catch(i){s(i)}})}startSse(e){return new Promise((t,s)=>{const i=new AbortController;this.sseAbort=i,fetch(e,{headers:{Accept:"text/event-stream","X-Authorization":`Bearer ${this.options.accessToken}`},signal:i.signal}).then(o=>{if(!o.ok||!o.body){s(new Error("SSE failed"));return}if(this.stopped){i.abort();return}this.currentKind="sse",this.handlers.onOpen("sse"),this.backoff=y,t();const a=o.body.getReader(),n=new TextDecoder;let l="";const g=()=>{a.read().then(({done:m,value:h})=>{var f;if(m||this.stopped){this.stopped||(this.handlers.onError(new Error("SpilkiWidget: SSE stream ended")),this.retryFallback("sse"));return}l+=n.decode(h,{stream:!0});const d=l.split(`
111
111
 
112
- `);l=(f=h.pop())!=null?f:"";for(const b of h)for(const p of b.split(`
113
- `))p.startsWith("data:")&&this.handleIncoming(p.slice(5).trim());g()}).catch(m=>{i.signal.aborted||(this.handlers.onError(m),this.stopped||this.retryFallback("sse"))})};g()}).catch(o=>{i.signal.aborted||s(o)})})}async startPoll(e){this.currentKind="poll",this.handlers.onOpen("poll"),this.backoff=w;const t=async()=>{if(!this.stopped)try{const s=await fetch(e,{headers:{"X-Authorization":`Bearer ${this.options.accessToken}`}});if(!s.ok)throw new Error(`Poll failed ${s.status}`);const i=await s.json();E(i).forEach(o=>this.handlers.onMessage(o)),this.backoff=w}catch(s){this.handlers.onError(s),this.backoff=Math.min(this.backoff*1.5,I)}finally{this.stopped||(this.pollTimer=window.setTimeout(t,this.backoff))}};await t()}retryFallback(e){this.stopped||(this.stop(!1),this.backoff=Math.min(this.backoff*1.5,I),this.retryTimer=window.setTimeout(()=>{this.stopped||(this.handlers.onError(new Error(`SpilkiWidget: retrying after ${e}`)),this.connect().catch(t=>this.handlers.onError(t)))},this.backoff))}handleIncoming(e){try{const t=JSON.parse(e);if(Array.isArray(t)){t.forEach(s=>this.dispatchIncoming(s));return}this.dispatchIncoming(t)}catch{console.error("SpilkiWidget: failed to parse incoming message",e)}}dispatchIncoming(e){var t;if("type"in e){e.type==="message"?this.handlers.onMessage(e.payload):e.type==="typing"&&this.handlers.onTyping(!!((t=e.payload)!=null&&t.active));return}this.handlers.onMessage(e)}}const A=30*60*1e3,T=30;class Q{constructor(e,t){this.org=e,this.listeners=new Set,this.state={isOpen:!1,isTyping:!1,isConnected:!1,messages:[]},this.historyKey=`spilki-history:${e}`,this.sessionKey=`spilki-session:${e}`,this.tokenKey=`spilki-token:${e}`,this.activityKey=`spilki-activity:${e}`,this.lastReadKey=`spilki-lastread:${e}`,this.persist=t.persist,this.state.messages=this.loadMessages()}get snapshot(){return{isOpen:this.state.isOpen,isTyping:this.state.isTyping,isConnected:this.state.isConnected,messages:this.state.messages}}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}open(){this.state.isOpen||(this.state.isOpen=!0,this.emit())}close(){this.state.isOpen&&(this.state.isOpen=!1,this.emit())}setTyping(e){this.state.isTyping!==e&&(this.state.isTyping=e,this.emit())}setConnected(e){this.state.isConnected!==e&&(this.state.isConnected=e,this.emit())}addMessage(e){var s,i;const t={id:(s=e.id)!=null?s:N("msg"),ts:(i=e.ts)!=null?i:Date.now(),author:e.author,text:e.text};return this.state.messages.some(o=>o.id===t.id)?null:(this.state.messages=E([...this.state.messages,t],T),this.persistMessages(),this.touchActivity(),this.emit(),t)}setMessages(e){this.state.messages=E(e,T),this.persistMessages(),this.emit()}clearMessages(){this.state.messages=[],this.persistMessages(),this.emit()}get sessionId(){if(!this.persist)return null;try{return localStorage.getItem(this.sessionKey)}catch(e){return console.error("SpilkiWidget: unable to get item",e),null}}persistSession(e){if(this.persist)try{localStorage.setItem(this.sessionKey,e)}catch(t){console.error("SpilkiWidget: unable to set item",t)}}clearSession(){if(this.persist)try{localStorage.removeItem(this.sessionKey)}catch(e){console.error("SpilkiWidget: unable to remove item",e)}}get accessToken(){if(!this.persist)return null;try{return localStorage.getItem(this.tokenKey)}catch(e){return console.error("SpilkiWidget: unable to get item",e),null}}persistAccessToken(e){if(this.persist)try{localStorage.setItem(this.tokenKey,e)}catch(t){console.error("SpilkiWidget: unable to set item",t)}}clearAccessToken(){if(this.persist)try{localStorage.removeItem(this.tokenKey)}catch(e){console.error("SpilkiWidget: unable to remove item",e)}}get lastActivityTs(){if(!this.persist)return 0;try{const e=localStorage.getItem(this.activityKey);return e?Number(e):0}catch{return 0}}touchActivity(){if(this.persist)try{localStorage.setItem(this.activityKey,String(Date.now()))}catch(e){console.error("SpilkiWidget: unable to set item",e)}}get lastReadTs(){if(!this.persist)return 0;try{const e=localStorage.getItem(this.lastReadKey);return e?Number(e):0}catch{return 0}}markRead(){if(this.persist)try{localStorage.setItem(this.lastReadKey,String(Date.now()))}catch{}}countUnread(){const e=this.lastReadTs;return e===0?0:this.state.messages.filter(t=>t.author==="bot"&&t.ts>e).length}isSessionExpired(){const e=this.lastActivityTs;return e===0?!1:Date.now()-e>=A}getConversationGroups(){const e=this.state.messages;if(e.length===0)return[];const t=[];let s={startTs:e[0].ts,messages:[e[0]]};for(let i=1;i<e.length;i++)e[i].ts-e[i-1].ts>=A?(t.push(s),s={startTs:e[i].ts,messages:[e[i]]}):s.messages.push(e[i]);return t.push(s),t}emit(){this.listeners.forEach(e=>e())}persistMessages(){if(this.persist)try{localStorage.setItem(this.historyKey,JSON.stringify(this.state.messages))}catch(e){console.error("SpilkiWidget: unable to set item",e)}}loadMessages(){if(!this.persist)return[];try{const e=localStorage.getItem(this.historyKey);if(!e)return[];const t=JSON.parse(e);return Array.isArray(t)?E(t,T):[]}catch(e){return console.error("SpilkiWidget: unable to load messages",e),[]}}}function ee(r){const e=r.replace(/-/g,"+").replace(/_/g,"/"),t=e.padEnd(e.length+(4-e.length%4)%4,"=");if(typeof atob=="function")return decodeURIComponent(Array.prototype.map.call(atob(t),i=>`%${`00${i.charCodeAt(0).toString(16)}`.slice(-2)}`).join(""));const s=globalThis.Buffer;if(s)return s.from(t,"base64").toString("utf8");throw new Error("SpilkiWidget: no base64 decoder available")}function te(r){if(!r)return null;const e=r.split(".");if(e.length<2)return null;try{const t=ee(e[1]);return JSON.parse(t)}catch(t){return console.error("SpilkiWidget: unable to parse JWT",t),null}}function se(r){const e=te(r);if(!(e!=null&&e.exp)||typeof e.exp!="number")return!0;const t=Math.floor(Date.now()/1e3);return e.exp<t+60}const ie={onOpen(){},onClose(){},onMessage(){},onError(){},onTransportChange(){}};async function re(r,e,t){const s=`${r.replace(/\/$/,"")}/widget/install`,i=await fetch(s,{method:"POST",headers:{"Content-Type":"application/json",Origin:window.location.origin},body:JSON.stringify({token:e,organisationId:t})});if(!i.ok)throw new Error(`SpilkiWidget: install failed (${i.status})`);return(await i.json()).accessToken}async function oe(r,e){const t=`${r.replace(/\/$/,"")}/widget/refresh`,s=await fetch(t,{method:"POST",headers:{"X-Authorization":`Bearer ${e}`,Origin:window.location.origin}});if(!s.ok)throw new Error(`SpilkiWidget: refresh failed (${s.status})`);return(await s.json()).accessToken}function ne(r){let e=!1,t=null;const s=[],i=()=>{var n;if(!r)return null;if(t)return t;try{const l=(n=window.AudioContext)!=null?n:window.webkitAudioContext;if(!l)return null;t=new l}catch{return null}return t},o=()=>{e=!0;try{const n=i();(n==null?void 0:n.state)==="suspended"&&n.resume().catch(()=>{})}catch{}},a=n=>{const l=()=>{o(),window.removeEventListener(n,l,!0)};s.push({type:n,listener:l}),window.addEventListener(n,l,{capture:!0,passive:!0})};return a("pointerdown"),a("keydown"),{markUserInteraction:o,play(){if(!r||!e)return;const n=i();if(!n||n.state!=="running")return;const l=n.createGain();l.gain.value=.15,l.connect(n.destination);const g=(d,h,f)=>{const b=n.createOscillator(),p=n.createGain();b.type="sine",b.frequency.setValueAtTime(d,h),p.gain.setValueAtTime(1e-4,h),p.gain.exponentialRampToValueAtTime(1,h+.012),p.gain.exponentialRampToValueAtTime(1e-4,h+f),b.connect(p),p.connect(l),b.start(h),b.stop(h+f+.02)},m=n.currentTime;g(880,m,.08),g(1175,m+.09,.08)},destroy(){s.forEach(({type:n,listener:l})=>{window.removeEventListener(n,l,!0)}),s.length=0,t&&t.close().catch(()=>{}),t=null}}}function B(r){var D,L,U,$;if(!r.org)throw new Error("SpilkiWidget: org is required");const e=K(r);z(e.allowedOriginsHint);const t={...ie,...(D=e.hooks)!=null?D:{}},s=new Q(e.org,{persist:e.persist}),i=ne(e.sound);let o=(L=s.accessToken)!=null?L:void 0,a=null;const n=()=>{m.setBadge(s.countUnread())},l=()=>{s.markRead(),m.setBadge(0)},g=async()=>a||(a=(async()=>{try{if(o&&se(o))try{o=await oe(e.apiBase,o),s.persistAccessToken(o),h.setAccessToken(o);return}catch{o=void 0,s.clearAccessToken()}if(!o){if(!r.installationToken)throw new Error("SpilkiWidget: missing installationToken");if(!r.org)throw new Error("SpilkiWidget: missing org");o=await re(e.apiBase,r.installationToken,r.org),s.persistAccessToken(o),h.setAccessToken(o)}}finally{a=null}})(),a),m=F({color:e.color,position:(U=e.position)!=null?U:"bottom-right",onClick:()=>{s.snapshot.isOpen?(s.close(),t.onClose()):(i.markUserInteraction(),s.open(),l(),t.onOpen())}}),d=new X({color:e.color,theme:O(e.theme),position:($=e.position)!=null?$:"bottom-right",i18n:e.i18n,onClose:()=>{s.close(),t.onClose()},onSend:c=>{const u=s.addMessage({author:"user",text:c});u&&(d.appendMessage(u),g().then(()=>h.send(c,u.id)).catch(y=>{t.onError(y),s.setConnected(!1),d.setOffline(!0)}))}}),h=new Z({apiBase:e.apiBase,accessToken:o,org:e.org,sessionId:s.sessionId},{onOpen(c){P.transport=c,t.onTransportChange(c),s.setConnected(!0),d.setOffline(!1)},onMessage(c){const u=s.addMessage(c);u&&(d.appendMessage(u),!s.snapshot.isOpen&&u.author==="bot"&&(n(),i.play()),t.onMessage(u))},onTyping(c){s.setTyping(c)},onError(c){t.onError(c),s.setConnected(!1),s.snapshot.isOpen&&d.setOffline(!0)}});m.mount(),d.mount();const f=s.snapshot.messages,b=s.isSessionExpired(),p=s.getConversationGroups();if(f.length===0&&e.welcome){const c={id:"welcome",author:"bot",text:e.welcome,ts:Date.now()};s.addMessage(c),d.appendMessage(c)}else b&&f.length>0?d.renderWithConversations(p,[]):p.length>1?d.renderWithConversations(p.slice(0,-1),p[p.length-1].messages):d.updateMessages(f);let C=!1;const ae=s.subscribe(()=>{const c=s.snapshot;if(m.setOpen(c.isOpen),d.setTyping(c.isTyping),d.setOffline(!c.isConnected),d.updateTheme(O(e.theme)),c.isOpen){if(!C){const u=s.countUnread()>0,y=s.lastReadTs;l(),u&&d.scrollToFirstUnread(y)}C=!0,d.show()}else C=!1,d.hide()}),le=f.length===0||b;n(),g().then(()=>h.connect().then(c=>{var y,W;e.persist&&s.persistSession(c.sessionId),s.setConnected(!0),t.onTransportChange((y=h.kind)!=null?y:"ws");const u=(W=c.suggestedReplies)!=null?W:[];u.length>0&&le&&d.setSuggestedReplies(u)})).catch(c=>{t.onError(c),s.setConnected(!1),d.setOffline(!0)});const P={transport:null,open(){i.markUserInteraction(),s.open(),l(),t.onOpen()},close(){s.close(),t.onClose()},destroy(){ae(),h.stop(),i.destroy(),m.destroy(),d.destroy()}};return P}function j(){var s,i;if(typeof document=="undefined")return;const r=document.currentScript;if(!r)return;const e=r.dataset;if(e.autoinit==="false")return;const t=e.org;if(!t){console.error("SpilkiWidget: data-org and is required for auto init");return}B({org:t,installationToken:e.installationToken,apiBase:e.apiBase,position:(s=e.position)!=null?s:void 0,theme:(i=e.theme)!=null?i:void 0})}typeof window!="undefined"&&(window.SpilkiWidget=window.SpilkiWidget||{},window.SpilkiWidget.init=r=>B(r)),j(),j()});
112
+ `);l=(f=d.pop())!=null?f:"";for(const b of d)for(const p of b.split(`
113
+ `))p.startsWith("data:")&&this.handleIncoming(p.slice(5).trim());g()}).catch(m=>{i.signal.aborted||(this.handlers.onError(m),this.stopped||this.retryFallback("sse"))})};g()}).catch(o=>{i.signal.aborted||s(o)})})}async startPoll(e){this.currentKind="poll",this.handlers.onOpen("poll"),this.backoff=y;const t=async()=>{if(!this.stopped)try{const s=await fetch(e,{headers:{"X-Authorization":`Bearer ${this.options.accessToken}`}});if(!s.ok)throw new Error(`Poll failed ${s.status}`);const i=await s.json();x(i).forEach(o=>this.handlers.onMessage(o)),this.backoff=y}catch(s){this.handlers.onError(s),this.backoff=Math.min(this.backoff*1.5,O)}finally{this.stopped||(this.pollTimer=window.setTimeout(t,this.backoff))}};await t()}retryFallback(e){this.stopped||(this.stop(!1),this.backoff=Math.min(this.backoff*1.5,O),this.retryTimer=window.setTimeout(()=>{this.stopped||(this.handlers.onError(new Error(`SpilkiWidget: retrying after ${e}`)),this.connect().catch(t=>this.handlers.onError(t)))},this.backoff))}handleIncoming(e){try{const t=JSON.parse(e);if(Array.isArray(t)){t.forEach(s=>this.dispatchIncoming(s));return}this.dispatchIncoming(t)}catch{console.error("SpilkiWidget: failed to parse incoming message",e)}}dispatchIncoming(e){var t;if("type"in e){e.type==="message"?this.handlers.onMessage(e.payload):e.type==="typing"?this.handlers.onTyping(!!((t=e.payload)!=null&&t.active)):e.type==="AGENT_EVENT"&&ee(e.payload)&&this.handlers.onAgentEvent(e.payload);return}this.handlers.onMessage(e)}}const Q=new Set(["TYPING","THINKING","SEARCHING","EXECUTING_TOOL"]);function ee(r){if(typeof r!="object"||r===null)return!1;const e=r;return Q.has(e.eventType)&&typeof e.active=="boolean"}const M=30*60*1e3,te=3e4,T=30;class se{constructor(e,t){this.org=e,this.listeners=new Set,this.activityTimer=null,this.activeEvents=new Set,this.state={isOpen:!1,agentActivity:null,isConnected:!1,messages:[]},this.historyKey=`spilki-history:${e}`,this.sessionKey=`spilki-session:${e}`,this.tokenKey=`spilki-token:${e}`,this.activityKey=`spilki-activity:${e}`,this.lastReadKey=`spilki-lastread:${e}`,this.persist=t.persist,this.state.messages=this.loadMessages()}get snapshot(){return{isOpen:this.state.isOpen,agentActivity:this.state.agentActivity,isConnected:this.state.isConnected,messages:this.state.messages}}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}open(){this.state.isOpen||(this.state.isOpen=!0,this.emit())}close(){this.state.isOpen&&(this.state.isOpen=!1,this.emit())}handleAgentEvent(e,t){t?this.activeEvents.add(e):this.activeEvents.delete(e),this.updateDisplayedActivity()}setTyping(e){this.handleAgentEvent("TYPING",e)}updateDisplayedActivity(){this.activityTimer&&(clearTimeout(this.activityTimer),this.activityTimer=null),this.activeEvents.size>0&&(this.activityTimer=setTimeout(()=>{this.activeEvents.clear(),this.state.agentActivity=null,this.activityTimer=null,this.emit()},te));const e=this.resolveTopActivity();this.state.agentActivity!==e&&(this.state.agentActivity=e,this.emit())}resolveTopActivity(){var t;return this.activeEvents.size===0?null:(t=["EXECUTING_TOOL","SEARCHING","THINKING","TYPING"].find(s=>this.activeEvents.has(s)))!=null?t:null}setConnected(e){this.state.isConnected!==e&&(this.state.isConnected=e,this.emit())}addMessage(e){var s,i;const t={id:(s=e.id)!=null?s:K("msg"),ts:(i=e.ts)!=null?i:Date.now(),author:e.author,text:e.text};return this.state.messages.some(o=>o.id===t.id)?null:(this.state.messages=x([...this.state.messages,t],T),this.persistMessages(),this.touchActivity(),this.emit(),t)}setMessages(e){this.state.messages=x(e,T),this.persistMessages(),this.emit()}clearMessages(){this.state.messages=[],this.persistMessages(),this.emit()}get sessionId(){if(!this.persist)return null;try{return localStorage.getItem(this.sessionKey)}catch(e){return console.error("SpilkiWidget: unable to get item",e),null}}persistSession(e){if(this.persist)try{localStorage.setItem(this.sessionKey,e)}catch(t){console.error("SpilkiWidget: unable to set item",t)}}clearSession(){if(this.persist)try{localStorage.removeItem(this.sessionKey)}catch(e){console.error("SpilkiWidget: unable to remove item",e)}}get accessToken(){if(!this.persist)return null;try{return localStorage.getItem(this.tokenKey)}catch(e){return console.error("SpilkiWidget: unable to get item",e),null}}persistAccessToken(e){if(this.persist)try{localStorage.setItem(this.tokenKey,e)}catch(t){console.error("SpilkiWidget: unable to set item",t)}}clearAccessToken(){if(this.persist)try{localStorage.removeItem(this.tokenKey)}catch(e){console.error("SpilkiWidget: unable to remove item",e)}}get lastActivityTs(){if(!this.persist)return 0;try{const e=localStorage.getItem(this.activityKey);return e?Number(e):0}catch{return 0}}touchActivity(){if(this.persist)try{localStorage.setItem(this.activityKey,String(Date.now()))}catch(e){console.error("SpilkiWidget: unable to set item",e)}}get lastReadTs(){if(!this.persist)return 0;try{const e=localStorage.getItem(this.lastReadKey);return e?Number(e):0}catch{return 0}}markRead(){if(this.persist)try{localStorage.setItem(this.lastReadKey,String(Date.now()))}catch{}}countUnread(){const e=this.lastReadTs;return e===0?0:this.state.messages.filter(t=>t.author==="bot"&&t.ts>e).length}isSessionExpired(){const e=this.lastActivityTs;return e===0?!1:Date.now()-e>=M}getConversationGroups(){const e=this.state.messages;if(e.length===0)return[];const t=[];let s={startTs:e[0].ts,messages:[e[0]]};for(let i=1;i<e.length;i++)e[i].ts-e[i-1].ts>=M?(t.push(s),s={startTs:e[i].ts,messages:[e[i]]}):s.messages.push(e[i]);return t.push(s),t}emit(){this.listeners.forEach(e=>e())}persistMessages(){if(this.persist)try{localStorage.setItem(this.historyKey,JSON.stringify(this.state.messages))}catch(e){console.error("SpilkiWidget: unable to set item",e)}}loadMessages(){if(!this.persist)return[];try{const e=localStorage.getItem(this.historyKey);if(!e)return[];const t=JSON.parse(e);return Array.isArray(t)?x(t,T):[]}catch(e){return console.error("SpilkiWidget: unable to load messages",e),[]}}}function ie(r){const e=r.replace(/-/g,"+").replace(/_/g,"/"),t=e.padEnd(e.length+(4-e.length%4)%4,"=");if(typeof atob=="function")return decodeURIComponent(Array.prototype.map.call(atob(t),i=>`%${`00${i.charCodeAt(0).toString(16)}`.slice(-2)}`).join(""));const s=globalThis.Buffer;if(s)return s.from(t,"base64").toString("utf8");throw new Error("SpilkiWidget: no base64 decoder available")}function re(r){if(!r)return null;const e=r.split(".");if(e.length<2)return null;try{const t=ie(e[1]);return JSON.parse(t)}catch(t){return console.error("SpilkiWidget: unable to parse JWT",t),null}}function oe(r){const e=re(r);if(!(e!=null&&e.exp)||typeof e.exp!="number")return!0;const t=Math.floor(Date.now()/1e3);return e.exp<t+60}const ne={onOpen(){},onClose(){},onMessage(){},onError(){},onTransportChange(){}};async function ae(r,e,t){const s=`${r.replace(/\/$/,"")}/widget/install`,i=await fetch(s,{method:"POST",headers:{"Content-Type":"application/json",Origin:window.location.origin},body:JSON.stringify({token:e,organisationId:t})});if(!i.ok)throw new Error(`SpilkiWidget: install failed (${i.status})`);return(await i.json()).accessToken}async function le(r,e){const t=`${r.replace(/\/$/,"")}/widget/refresh`,s=await fetch(t,{method:"POST",headers:{"X-Authorization":`Bearer ${e}`,Origin:window.location.origin}});if(!s.ok)throw new Error(`SpilkiWidget: refresh failed (${s.status})`);return(await s.json()).accessToken}function ce(r){let e=!1,t=null;const s=[],i=()=>{var n;if(!r)return null;if(t)return t;try{const l=(n=window.AudioContext)!=null?n:window.webkitAudioContext;if(!l)return null;t=new l}catch{return null}return t},o=()=>{e=!0;try{const n=i();(n==null?void 0:n.state)==="suspended"&&n.resume().catch(()=>{})}catch{}},a=n=>{const l=()=>{o(),window.removeEventListener(n,l,!0)};s.push({type:n,listener:l}),window.addEventListener(n,l,{capture:!0,passive:!0})};return a("pointerdown"),a("keydown"),{markUserInteraction:o,play(){if(!r||!e)return;const n=i();if(!n||n.state!=="running")return;const l=n.createGain();l.gain.value=.15,l.connect(n.destination);const g=(h,d,f)=>{const b=n.createOscillator(),p=n.createGain();b.type="sine",b.frequency.setValueAtTime(h,d),p.gain.setValueAtTime(1e-4,d),p.gain.exponentialRampToValueAtTime(1,d+.012),p.gain.exponentialRampToValueAtTime(1e-4,d+f),b.connect(p),p.connect(l),b.start(d),b.stop(d+f+.02)},m=n.currentTime;g(880,m,.08),g(1175,m+.09,.08)},destroy(){s.forEach(({type:n,listener:l})=>{window.removeEventListener(n,l,!0)}),s.length=0,t&&t.close().catch(()=>{}),t=null}}}function B(r){var D,L,U,N;if(!r.org)throw new Error("SpilkiWidget: org is required");const e=R(r);z(e.allowedOriginsHint);const t={...ne,...(D=e.hooks)!=null?D:{}},s=new se(e.org,{persist:e.persist}),i=ce(e.sound);let o=(L=s.accessToken)!=null?L:void 0,a=null;const n=()=>{m.setBadge(s.countUnread())},l=()=>{s.markRead(),m.setBadge(0)},g=async()=>a||(a=(async()=>{try{if(o&&oe(o))try{o=await le(e.apiBase,o),s.persistAccessToken(o),d.setAccessToken(o);return}catch{o=void 0,s.clearAccessToken()}if(!o){if(!r.installationToken)throw new Error("SpilkiWidget: missing installationToken");if(!r.org)throw new Error("SpilkiWidget: missing org");o=await ae(e.apiBase,r.installationToken,r.org),s.persistAccessToken(o),d.setAccessToken(o)}}finally{a=null}})(),a),m=W({color:e.color,position:(U=e.position)!=null?U:"bottom-right",onClick:()=>{s.snapshot.isOpen?(s.close(),t.onClose()):(i.markUserInteraction(),s.open(),l(),t.onOpen())}}),h=new X({color:e.color,theme:A(e.theme),position:(N=e.position)!=null?N:"bottom-right",i18n:e.i18n,onClose:()=>{s.close(),t.onClose()},onSend:c=>{const u=s.addMessage({author:"user",text:c});u&&(h.appendMessage(u),g().then(()=>d.send(c,u.id)).catch(w=>{t.onError(w),s.setConnected(!1),h.setOffline(!0)}))}}),d=new Z({apiBase:e.apiBase,accessToken:o,org:e.org,sessionId:s.sessionId},{onOpen(c){j.transport=c,t.onTransportChange(c),s.setConnected(!0),h.setOffline(!1)},onMessage(c){const u=s.addMessage(c);u&&(h.appendMessage(u),!s.snapshot.isOpen&&u.author==="bot"&&(n(),i.play()),t.onMessage(u))},onTyping(c){s.setTyping(c)},onAgentEvent(c){s.handleAgentEvent(c.eventType,c.active)},onError(c){t.onError(c),s.setConnected(!1),s.snapshot.isOpen&&h.setOffline(!0)}});m.mount(),h.mount();const f=s.snapshot.messages,b=s.isSessionExpired(),p=s.getConversationGroups();if(f.length===0&&e.welcome){const c={id:"welcome",author:"bot",text:e.welcome,ts:Date.now()};s.addMessage(c),h.appendMessage(c)}else b&&f.length>0?h.renderWithConversations(p,[]):p.length>1?h.renderWithConversations(p.slice(0,-1),p[p.length-1].messages):h.updateMessages(f);let C=!1;const he=s.subscribe(()=>{const c=s.snapshot;if(m.setOpen(c.isOpen),h.setAgentActivity(c.agentActivity,e.i18n),h.setOffline(!c.isConnected),h.updateTheme(A(e.theme)),c.isOpen){if(!C){const u=s.countUnread()>0,w=s.lastReadTs;l(),u&&h.scrollToFirstUnread(w)}C=!0,h.show()}else C=!1,h.hide()}),de=f.length===0||b;n(),g().then(()=>d.connect().then(c=>{var w,$;e.persist&&s.persistSession(c.sessionId),s.setConnected(!0),t.onTransportChange((w=d.kind)!=null?w:"ws");const u=($=c.suggestedReplies)!=null?$:[];u.length>0&&de&&h.setSuggestedReplies(u)})).catch(c=>{t.onError(c),s.setConnected(!1),h.setOffline(!0)});const j={transport:null,open(){i.markUserInteraction(),s.open(),l(),t.onOpen()},close(){s.close(),t.onClose()},destroy(){he(),d.stop(),i.destroy(),m.destroy(),h.destroy()}};return j}function P(){var s,i;if(typeof document=="undefined")return;const r=document.currentScript;if(!r)return;const e=r.dataset;if(e.autoinit==="false")return;const t=e.org;if(!t){console.error("SpilkiWidget: data-org and is required for auto init");return}B({org:t,installationToken:e.installationToken,apiBase:e.apiBase,position:(s=e.position)!=null?s:void 0,theme:(i=e.theme)!=null?i:void 0})}typeof window!="undefined"&&(window.SpilkiWidget=window.SpilkiWidget||{},window.SpilkiWidget.init=r=>B(r)),P(),P()});
114
114
  //# sourceMappingURL=bootstrap.umd.js.map