@spilki/widget 1.0.33 โ 1.0.34
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.
- package/dist/bootstrap.es.js +240 -206
- package/dist/bootstrap.es.js.map +1 -1
- package/dist/bootstrap.umd.js +7 -7
- package/dist/bootstrap.umd.js.map +1 -1
- package/dist/core/transport.d.ts +4 -1
- package/dist/core/transport.d.ts.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/widget.es.js +243 -209
- package/dist/widget.es.js.map +1 -1
- package/dist/widget.umd.js +7 -7
- package/dist/widget.umd.js.map +1 -1
- package/package.json +1 -1
package/dist/widget.umd.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
(function(w,
|
|
1
|
+
(function(w,S){typeof exports=="object"&&typeof module!="undefined"?S(exports):typeof define=="function"&&define.amd?define(["exports"],S):(w=typeof globalThis!="undefined"?globalThis:w||self,S(w.SpilkiWidget={}))})(this,function(w){"use strict";const S=`
|
|
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(o){const e=document.createElement("div");e.setAttribute("part","bubble-root"),e.setAttribute("data-position",o.position),e.style.setProperty("--spilki-accent",o.color);const t=e.attachShadow({mode:"open"});t.innerHTML=x;const s=t.querySelector("button"),i=t.querySelector(".badge");return s.addEventListener("click",()=>o.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",C={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"},v={apiBase:R,position:"bottom-right",theme:"auto",color:"#6366f1",welcome:C.welcome,persist:!0,sound:!0,i18n:C};function K(o){var t,s,i,r,a,n,l,g;const e={...C,...(t=o.i18n)!=null?t:{}};return{...v,...o,apiBase:(s=o.apiBase)!=null?s:v.apiBase,i18n:e,welcome:(i=o.welcome)!=null?i:e.welcome,position:(r=o.position)!=null?r:v.position,theme:(a=o.theme)!=null?a:v.theme,color:(n=o.color)!=null?n:v.color,persist:(l=o.persist)!=null?l:v.persist,sound:(g=o.sound)!=null?g:v.sound}}function H(o="msg"){return typeof crypto!="undefined"&&crypto.randomUUID?crypto.randomUUID():`${o}-${Math.random().toString(16).slice(2)}`}function z(){var o,e;return(e=(o=window.matchMedia)==null?void 0:o.call(window,"(prefers-color-scheme: dark)").matches)!=null?e:!1}function M(o){return o==="light"||o==="dark"?o:z()?"dark":"light"}function T(o,e=30){return o.slice(-e)}function _(o){if(!o||o.length===0)return;const e=window.location.origin;o.includes(e)||console.warn(`SpilkiWidget: current origin ${e} not in allowedOriginsHint: ${o.join(", ")}`)}const G=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function q(o){const e=new Date(o),t=new Date,s=l=>String(l).padStart(2,"0"),i=`${s(e.getHours())}:${s(e.getMinutes())}`,r=new Date(t.getFullYear(),t.getMonth(),t.getDate()).getTime(),a=r-864e5,n=new Date(e.getFullYear(),e.getMonth(),e.getDate()).getTime();return n===r?`Today at ${i}`:n===a?`Yesterday at ${i}`:`${G[e.getMonth()]} ${e.getDate()}, ${i}`}const Y=':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:.75rem;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}.typing:after{content:"";animation:dots 1.2s steps(4,end) infinite}@keyframes dots{0%{content:""}25%{content:"."}50%{content:".."}75%{content:"..."}}.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}',B=24*60*60*1e3,V=2*60*1e3,J=200,X=["๐","๐","๐คฃ","๐","๐","๐ฅฐ","๐","๐","๐ค","๐","๐","๐ค","๐ข","๐ญ","๐ก","๐คฏ","๐ฑ","๐ฅณ","๐ด","๐คฎ","๐","๐","๐","๐ค","๐","๐ช","โ๏ธ","๐ค","๐","๐ซถ","โค๏ธ","๐ฅ","โญ","๐ฏ","โ
","โ","โก","๐","๐ฐ","๐ฆ","๐
","๐","๐","๐ฌ","๐","๐","๐ธ","๐ต","โ","๐"];function S(o){return o.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}class Z{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=S(e.i18n.title),s=S(e.i18n.typing),i=S(e.i18n.offline),r=S(e.i18n.placeholder),a=S(e.i18n.sendLabel);this.shadow.innerHTML=`
|
|
74
|
-
<style>${
|
|
73
|
+
`;function R(o){const e=document.createElement("div");e.setAttribute("part","bubble-root"),e.setAttribute("data-position",o.position),e.style.setProperty("--spilki-accent",o.color);const t=e.attachShadow({mode:"open"});t.innerHTML=S;const s=t.querySelector("button"),i=t.querySelector(".badge");return s.addEventListener("click",()=>o.onClick()),{element:e,mount(){document.body.appendChild(e)},destroy(){e.remove()},setOpen(l){s.setAttribute("aria-expanded",String(l)),s.setAttribute("aria-label",l?"Close chat":"Open chat")},setBadge(l){const n=Math.max(0,l),a=s.getAttribute("aria-expanded")==="true";if(n===0){i.toggleAttribute("hidden",!0),i.textContent="0",a||s.setAttribute("aria-label","Open chat");return}i.toggleAttribute("hidden",!1),i.textContent=n>99?"99+":String(n),a||s.setAttribute("aria-label",`Open chat, ${n} unread`)}}}const K="https://api.spilki.app",A={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"},v={apiBase:K,position:"bottom-right",theme:"auto",color:"#6366f1",welcome:A.welcome,persist:!0,sound:!0,i18n:A};function H(o){var t,s,i,r,l,n,a,g;const e={...A,...(t=o.i18n)!=null?t:{}};return{...v,...o,apiBase:(s=o.apiBase)!=null?s:v.apiBase,i18n:e,welcome:(i=o.welcome)!=null?i:e.welcome,position:(r=o.position)!=null?r:v.position,theme:(l=o.theme)!=null?l:v.theme,color:(n=o.color)!=null?n:v.color,persist:(a=o.persist)!=null?a:v.persist,sound:(g=o.sound)!=null?g:v.sound}}function z(o="msg"){return typeof crypto!="undefined"&&crypto.randomUUID?crypto.randomUUID():`${o}-${Math.random().toString(16).slice(2)}`}function _(){var o,e;return(e=(o=window.matchMedia)==null?void 0:o.call(window,"(prefers-color-scheme: dark)").matches)!=null?e:!1}function j(o){return o==="light"||o==="dark"?o:_()?"dark":"light"}function C(o,e=30){return o.slice(-e)}function G(o){if(!o||o.length===0)return;const e=window.location.origin;o.includes(e)||console.warn(`SpilkiWidget: current origin ${e} not in allowedOriginsHint: ${o.join(", ")}`)}const q=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function Y(o){const e=new Date(o),t=new Date,s=a=>String(a).padStart(2,"0"),i=`${s(e.getHours())}:${s(e.getMinutes())}`,r=new Date(t.getFullYear(),t.getMonth(),t.getDate()).getTime(),l=r-864e5,n=new Date(e.getFullYear(),e.getMonth(),e.getDate()).getTime();return n===r?`Today at ${i}`:n===l?`Yesterday at ${i}`:`${q[e.getMonth()]} ${e.getDate()}, ${i}`}const V=':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:.75rem;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}.typing:after{content:"";animation:dots 1.2s steps(4,end) infinite}@keyframes dots{0%{content:""}25%{content:"."}50%{content:".."}75%{content:"..."}}.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}',P=24*60*60*1e3,J=2*60*1e3,X=200,Z=["๐","๐","๐คฃ","๐","๐","๐ฅฐ","๐","๐","๐ค","๐","๐","๐ค","๐ข","๐ญ","๐ก","๐คฏ","๐ฑ","๐ฅณ","๐ด","๐คฎ","๐","๐","๐","๐ค","๐","๐ช","โ๏ธ","๐ค","๐","๐ซถ","โค๏ธ","๐ฅ","โญ","๐ฏ","โ
","โ","โก","๐","๐ฐ","๐ฆ","๐
","๐","๐","๐ฌ","๐","๐","๐ธ","๐ต","โ","๐"];function T(o){return o.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}class Q{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=T(e.i18n.title),s=T(e.i18n.typing),i=T(e.i18n.offline),r=T(e.i18n.placeholder),l=T(e.i18n.sendLabel);this.shadow.innerHTML=`
|
|
74
|
+
<style>${V}</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>
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
<circle cx="15" cy="10" r="1" fill="currentColor"/>
|
|
99
99
|
</svg>
|
|
100
100
|
</button>
|
|
101
|
-
<button type="button" class="send-btn" aria-label="${
|
|
101
|
+
<button type="button" class="send-btn" aria-label="${l}">
|
|
102
102
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
103
103
|
<path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" fill="currentColor"/>
|
|
104
104
|
</svg>
|
|
@@ -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),r=s.messages[s.messages.length-1].ts,a=this.createSeparatorElement(r,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=q(e);const i=()=>{const r=t.classList.toggle("expanded");s.setAttribute("aria-expanded",String(r)),s.setAttribute("aria-label",r?"Hide previous conversation":"Show previous conversation")};return s.addEventListener("click",i),s.addEventListener("keydown",r=>{const a=r;(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:r}=this.createMessageElement(i);t.appendChild(r),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<B){const i=Math.max(1,Math.round(s/6e4));if(i<60)return this.relativeTimeFormat.format(-i,"minute");const r=Math.max(1,Math.round(i/60));return this.relativeTimeFormat.format(-r,"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-B?"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()>J}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(),X.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 r=s+e.length;this.input.setSelectionRange(r,r),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 E=1500,j=8e3;class Q{constructor(e,t){this.sessionId=null,this.currentKind=null,this.stopped=!1,this.backoff=E,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 r=await i.json();return this.sessionId=r.sessionId,this.options.sessionId=r.sessionId,this.backoff=E,await this.startTransport(r),r}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`,r=await fetch(i,{method:"POST",headers:{"Content-Type":"application/json","X-Authorization":`Bearer ${this.options.accessToken}`},body:JSON.stringify(s)});if(!r.ok)throw new Error(`SpilkiWidget: send failed (${r.status})`)}stop(e=!0){this.stopped=!0,e&&(this.backoff=E),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=E,t()},this.wsOnMessage=r=>this.handleIncoming(r.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(r=>{if(!r.ok||!r.body){s(new Error("SSE failed"));return}if(this.stopped){i.abort();return}this.currentKind="sse",this.handlers.onOpen("sse"),this.backoff=E,t();const a=r.body.getReader(),n=new TextDecoder;let l="";const g=()=>{a.read().then(({done:u,value:d})=>{var m;if(u||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 a=n;if(a.key==="Escape"){if(this.emojiPickerOpen){a.preventDefault(),this.closeEmojiPicker();return}this.options.onClose()}a.key==="Tab"&&this.trapFocus(a)},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 a=n.composedPath();a.includes(this.emojiPickerEl)||a.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),r=s.messages[s.messages.length-1].ts,l=this.createSeparatorElement(r,i);this.messagesEl.appendChild(i),this.messagesEl.appendChild(l)}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=Y(e);const i=()=>{const r=t.classList.toggle("expanded");s.setAttribute("aria-expanded",String(r)),s.setAttribute("aria-label",r?"Hide previous conversation":"Show previous conversation")};return s.addEventListener("click",i),s.addEventListener("keydown",r=>{const l=r;(l.key==="Enter"||l.key===" ")&&(l.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:r}=this.createMessageElement(i);t.appendChild(r),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<=J&&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<P){const i=Math.max(1,Math.round(s/6e4));if(i<60)return this.relativeTimeFormat.format(-i,"minute");const r=Math.max(1,Math.round(i/60));return this.relativeTimeFormat.format(-r,"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-P?"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()>X}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(),Z.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 l,n;const t=this.input.value,s=(l=this.input.selectionStart)!=null?l:t.length,i=(n=this.input.selectionEnd)!=null?n:t.length;this.input.value=`${t.slice(0,s)}${e}${t.slice(i)}`;const r=s+e.length;this.input.setSelectionRange(r,r),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 E=1500,D=8e3;class ee{constructor(e,t){this.sessionId=null,this.currentKind=null,this.stopped=!1,this.backoff=E,this.connectPromise=null,this.options=e,this.handlers=t}setAccessToken(e){this.options.accessToken=e}setUser(e){const t={};for(const[s,i]of Object.entries(e))if(typeof i=="string"){const r=i.trim();r&&(t[s]=r)}this.user=t,this.sessionId&&this.sendIdentify().catch(s=>this.handlers.onError(s))}async sendIdentify(){const e=`${this.options.apiBase.replace(/\/$/,"")}/widget/identify`,t=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json",...this.options.accessToken?{"X-Authorization":`Bearer ${this.options.accessToken}`}:{}},body:JSON.stringify({sessionId:this.sessionId,user:this.user})});if(!t.ok)throw new Error(`SpilkiWidget: identify failed (${t.status})`)}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 l,n;this.stopped=!1;const e=`${this.options.apiBase.replace(/\/$/,"")}/widget/session`,t=(n=(l=this.sessionId)!=null?l: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:"",...this.user?{user:this.user}:{}},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 r=await i.json();return this.sessionId=r.sessionId,this.options.sessionId=r.sessionId,this.backoff=E,await this.startTransport(r),r}async send(e,t){var l,n,a;const s={sessionId:(n=(l=this.sessionId)!=null?l:this.options.sessionId)!=null?n:"",text:e,...t?{messageId:t}:{},...(a=this.user)!=null&&a.userId?{userId:this.user.userId}:{}};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`,r=await fetch(i,{method:"POST",headers:{"Content-Type":"application/json","X-Authorization":`Bearer ${this.options.accessToken}`},body:JSON.stringify(s)});if(!r.ok)throw new Error(`SpilkiWidget: send failed (${r.status})`)}stop(e=!0){this.stopped=!0,e&&(this.backoff=E),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=E,t()},this.wsOnMessage=r=>this.handleIncoming(r.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(r=>{if(!r.ok||!r.body){s(new Error("SSE failed"));return}if(this.stopped){i.abort();return}this.currentKind="sse",this.handlers.onOpen("sse"),this.backoff=E,t();const l=r.body.getReader(),n=new TextDecoder;let a="";const g=()=>{l.read().then(({done:u,value:d})=>{var m;if(u||this.stopped){this.stopped||(this.handlers.onError(new Error("SpilkiWidget: SSE stream ended")),this.retryFallback("sse"));return}a+=n.decode(d,{stream:!0});const h=a.split(`
|
|
111
111
|
|
|
112
|
-
`);
|
|
113
|
-
`))p.startsWith("data:")&&this.handleIncoming(p.slice(5).trim());g()}).catch(u=>{i.signal.aborted||(this.handlers.onError(u),this.stopped||this.retryFallback("sse"))})};g()}).catch(r=>{i.signal.aborted||s(r)})})}async startPoll(e){this.currentKind="poll",this.handlers.onOpen("poll"),this.backoff=E;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();T(i).forEach(r=>this.handlers.onMessage(r)),this.backoff=E}catch(s){this.handlers.onError(s),this.backoff=Math.min(this.backoff*1.5,j)}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,j),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"&&te(e.payload)&&this.handlers.onAgentEvent(e.payload);return}this.handlers.onMessage(e)}}const ee=new Set(["TYPING","THINKING","SEARCHING","EXECUTING_TOOL"]);function te(o){if(typeof o!="object"||o===null)return!1;const e=o;return ee.has(e.eventType)&&typeof e.active=="boolean"}const P=30*60*1e3,se=3e4,A=30;class ie{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()},se));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:H("msg"),ts:(i=e.ts)!=null?i:Date.now(),author:e.author,text:e.text};return this.state.messages.some(r=>r.id===t.id)?null:(this.state.messages=T([...this.state.messages,t],A),this.persistMessages(),this.touchActivity(),this.emit(),t)}setMessages(e){this.state.messages=T(e,A),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>=P}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>=P?(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)?T(t,A):[]}catch(e){return console.error("SpilkiWidget: unable to load messages",e),[]}}}function oe(o){const e=o.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(o){if(!o)return null;const e=o.split(".");if(e.length<2)return null;try{const t=oe(e[1]);return JSON.parse(t)}catch(t){return console.error("SpilkiWidget: unable to parse JWT",t),null}}function ne(o){const e=re(o);if(!(e!=null&&e.exp)||typeof e.exp!="number")return!0;const t=Math.floor(Date.now()/1e3);return e.exp<t+60}const ae={onOpen(){},onClose(){},onMessage(){},onError(){},onTransportChange(){}};async function le(o,e,t){const s=`${o.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 ce(o,e){const t=`${o.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 de(o){let e=!1,t=null;const s=[],i=()=>{var n;if(!o)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},r=()=>{e=!0;try{const n=i();(n==null?void 0:n.state)==="suspended"&&n.resume().catch(()=>{})}catch{}},a=n=>{const l=()=>{r(),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:r,play(){if(!o||!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,m)=>{const f=n.createOscillator(),p=n.createGain();f.type="sine",f.frequency.setValueAtTime(d,h),p.gain.setValueAtTime(1e-4,h),p.gain.exponentialRampToValueAtTime(1,h+.012),p.gain.exponentialRampToValueAtTime(1e-4,h+m),f.connect(p),p.connect(l),f.start(h),f.stop(h+m+.02)},u=n.currentTime;g(880,u,.08),g(1175,u+.09,.08)},destroy(){s.forEach(({type:n,listener:l})=>{window.removeEventListener(n,l,!0)}),s.length=0,t&&t.close().catch(()=>{}),t=null}}}function I(o){var U,N,$,W;if(!o.org)throw new Error("SpilkiWidget: org is required");const e=K(o);_(e.allowedOriginsHint);const t={...ae,...(U=e.hooks)!=null?U:{}},s=new ie(e.org,{persist:e.persist}),i=de(e.sound);let r=(N=s.accessToken)!=null?N:void 0,a=null;const n=()=>{u.setBadge(s.countUnread())},l=()=>{s.markRead(),u.setBadge(0)},g=async()=>a||(a=(async()=>{try{if(r&&ne(r))try{r=await ce(e.apiBase,r),s.persistAccessToken(r),h.setAccessToken(r);return}catch{r=void 0,s.clearAccessToken()}if(!r){if(!o.installationToken)throw new Error("SpilkiWidget: missing installationToken");if(!o.org)throw new Error("SpilkiWidget: missing org");r=await le(e.apiBase,o.installationToken,o.org),s.persistAccessToken(r),h.setAccessToken(r)}}finally{a=null}})(),a),u=F({color:e.color,position:($=e.position)!=null?$:"bottom-right",onClick:()=>{s.snapshot.isOpen?(s.close(),t.onClose()):(i.markUserInteraction(),s.open(),l(),t.onOpen())}}),d=new Z({color:e.color,theme:M(e.theme),position:(W=e.position)!=null?W:"bottom-right",i18n:e.i18n,onClose:()=>{s.close(),t.onClose()},onSend:c=>{const k=s.addMessage({author:"user",text:c});k&&(d.appendMessage(k),g().then(()=>h.send(c,k.id)).catch(b=>{t.onError(b),s.setConnected(!1),d.setOffline(!0)}))}}),h=new Q({apiBase:e.apiBase,accessToken:r,org:e.org,sessionId:s.sessionId},{onOpen(c){L.transport=c,t.onTransportChange(c),s.setConnected(!0),d.setOffline(!1)},onMessage(c){const b=c.suggestedReplies,y=s.addMessage(c);y&&(d.appendMessage(y),y.author==="bot"&&b&&b.length>0&&d.setSuggestedReplies(b),!s.snapshot.isOpen&&y.author==="bot"&&(n(),i.play()),t.onMessage(y))},onTyping(c){s.setTyping(c)},onAgentEvent(c){s.handleAgentEvent(c.eventType,c.active)},onError(c){t.onError(c),s.setConnected(!1),s.snapshot.isOpen&&d.setOffline(!0)}});u.mount(),d.mount();const m=s.snapshot.messages,f=s.isSessionExpired(),p=s.getConversationGroups();if(m.length===0&&e.welcome){const c={id:"welcome",author:"bot",text:e.welcome,ts:Date.now()};s.addMessage(c),d.appendMessage(c)}else f&&m.length>0?d.renderWithConversations(p,[]):p.length>1?d.renderWithConversations(p.slice(0,-1),p[p.length-1].messages):d.updateMessages(m);let O=!1;const he=s.subscribe(()=>{const c=s.snapshot;if(u.setOpen(c.isOpen),d.setAgentActivity(c.agentActivity,e.i18n),d.setOffline(!c.isConnected),d.updateTheme(M(e.theme)),c.isOpen){if(!O){const k=s.countUnread()>0,b=s.lastReadTs;l(),k&&d.scrollToFirstUnread(b)}O=!0,d.show()}else O=!1,d.hide()}),pe=m.length===0||f;n(),g().then(()=>h.connect().then(c=>{var b,y;e.persist&&s.persistSession(c.sessionId),s.setConnected(!0),t.onTransportChange((b=h.kind)!=null?b:"ws");const k=(y=c.suggestedReplies)!=null?y:[];k.length>0&&pe&&d.setSuggestedReplies(k)})).catch(c=>{t.onError(c),s.setConnected(!1),d.setOffline(!0)});const L={transport:null,open(){i.markUserInteraction(),s.open(),l(),t.onOpen()},close(){s.close(),t.onClose()},destroy(){he(),h.stop(),i.destroy(),u.destroy(),d.destroy()}};return L}function D(){var s,i;if(typeof document=="undefined")return;const o=document.currentScript;if(!o)return;const e=o.dataset;if(e.autoinit==="false")return;const t=e.org;if(!t){console.error("SpilkiWidget: data-org and is required for auto init");return}I({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=o=>I(o)),D(),w.autoInit=D,w.initSpilkiWidget=I,Object.defineProperty(w,Symbol.toStringTag,{value:"Module"})});
|
|
112
|
+
`);a=(m=h.pop())!=null?m:"";for(const f of h)for(const p of f.split(`
|
|
113
|
+
`))p.startsWith("data:")&&this.handleIncoming(p.slice(5).trim());g()}).catch(u=>{i.signal.aborted||(this.handlers.onError(u),this.stopped||this.retryFallback("sse"))})};g()}).catch(r=>{i.signal.aborted||s(r)})})}async startPoll(e){this.currentKind="poll",this.handlers.onOpen("poll"),this.backoff=E;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();C(i).forEach(r=>this.handlers.onMessage(r)),this.backoff=E}catch(s){this.handlers.onError(s),this.backoff=Math.min(this.backoff*1.5,D)}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,D),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"&&se(e.payload)&&this.handlers.onAgentEvent(e.payload);return}this.handlers.onMessage(e)}}const te=new Set(["TYPING","THINKING","SEARCHING","EXECUTING_TOOL"]);function se(o){if(typeof o!="object"||o===null)return!1;const e=o;return te.has(e.eventType)&&typeof e.active=="boolean"}const L=30*60*1e3,ie=3e4,I=30;class oe{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()},ie));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:z("msg"),ts:(i=e.ts)!=null?i:Date.now(),author:e.author,text:e.text};return this.state.messages.some(r=>r.id===t.id)?null:(this.state.messages=C([...this.state.messages,t],I),this.persistMessages(),this.touchActivity(),this.emit(),t)}setMessages(e){this.state.messages=C(e,I),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>=L}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>=L?(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)?C(t,I):[]}catch(e){return console.error("SpilkiWidget: unable to load messages",e),[]}}}function re(o){const e=o.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 ne(o){if(!o)return null;const e=o.split(".");if(e.length<2)return null;try{const t=re(e[1]);return JSON.parse(t)}catch(t){return console.error("SpilkiWidget: unable to parse JWT",t),null}}function ae(o){const e=ne(o);if(!(e!=null&&e.exp)||typeof e.exp!="number")return!0;const t=Math.floor(Date.now()/1e3);return e.exp<t+60}const le={onOpen(){},onClose(){},onMessage(){},onError(){},onTransportChange(){}};async function ce(o,e,t){const s=`${o.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 de(o,e){const t=`${o.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 he(o){let e=!1,t=null;const s=[],i=()=>{var n;if(!o)return null;if(t)return t;try{const a=(n=window.AudioContext)!=null?n:window.webkitAudioContext;if(!a)return null;t=new a}catch{return null}return t},r=()=>{e=!0;try{const n=i();(n==null?void 0:n.state)==="suspended"&&n.resume().catch(()=>{})}catch{}},l=n=>{const a=()=>{r(),window.removeEventListener(n,a,!0)};s.push({type:n,listener:a}),window.addEventListener(n,a,{capture:!0,passive:!0})};return l("pointerdown"),l("keydown"),{markUserInteraction:r,play(){if(!o||!e)return;const n=i();if(!n||n.state!=="running")return;const a=n.createGain();a.gain.value=.15,a.connect(n.destination);const g=(d,h,m)=>{const f=n.createOscillator(),p=n.createGain();f.type="sine",f.frequency.setValueAtTime(d,h),p.gain.setValueAtTime(1e-4,h),p.gain.exponentialRampToValueAtTime(1,h+.012),p.gain.exponentialRampToValueAtTime(1e-4,h+m),f.connect(p),p.connect(a),f.start(h),f.stop(h+m+.02)},u=n.currentTime;g(880,u,.08),g(1175,u+.09,.08)},destroy(){s.forEach(({type:n,listener:a})=>{window.removeEventListener(n,a,!0)}),s.length=0,t&&t.close().catch(()=>{}),t=null}}}function O(o){var $,N,W,F;if(!o.org)throw new Error("SpilkiWidget: org is required");const e=H(o);G(e.allowedOriginsHint);const t={...le,...($=e.hooks)!=null?$:{}},s=new oe(e.org,{persist:e.persist}),i=he(e.sound);let r=(N=s.accessToken)!=null?N:void 0,l=null;const n=()=>{u.setBadge(s.countUnread())},a=()=>{s.markRead(),u.setBadge(0)},g=async()=>l||(l=(async()=>{try{if(r&&ae(r))try{r=await de(e.apiBase,r),s.persistAccessToken(r),h.setAccessToken(r);return}catch{r=void 0,s.clearAccessToken()}if(!r){if(!o.installationToken)throw new Error("SpilkiWidget: missing installationToken");if(!o.org)throw new Error("SpilkiWidget: missing org");r=await ce(e.apiBase,o.installationToken,o.org),s.persistAccessToken(r),h.setAccessToken(r)}}finally{l=null}})(),l),u=R({color:e.color,position:(W=e.position)!=null?W:"bottom-right",onClick:()=>{s.snapshot.isOpen?(s.close(),t.onClose()):(i.markUserInteraction(),s.open(),a(),t.onOpen())}}),d=new Q({color:e.color,theme:j(e.theme),position:(F=e.position)!=null?F:"bottom-right",i18n:e.i18n,onClose:()=>{s.close(),t.onClose()},onSend:c=>{const k=s.addMessage({author:"user",text:c});k&&(d.appendMessage(k),g().then(()=>h.send(c,k.id)).catch(b=>{t.onError(b),s.setConnected(!1),d.setOffline(!0)}))}}),h=new ee({apiBase:e.apiBase,accessToken:r,org:e.org,sessionId:s.sessionId},{onOpen(c){B.transport=c,t.onTransportChange(c),s.setConnected(!0),d.setOffline(!1)},onMessage(c){const b=c.suggestedReplies,y=s.addMessage(c);y&&(d.appendMessage(y),y.author==="bot"&&b&&b.length>0&&d.setSuggestedReplies(b),!s.snapshot.isOpen&&y.author==="bot"&&(n(),i.play()),t.onMessage(y))},onTyping(c){s.setTyping(c)},onAgentEvent(c){s.handleAgentEvent(c.eventType,c.active)},onError(c){t.onError(c),s.setConnected(!1),s.snapshot.isOpen&&d.setOffline(!0)}});u.mount(),d.mount();const m=s.snapshot.messages,f=s.isSessionExpired(),p=s.getConversationGroups();if(m.length===0&&e.welcome){const c={id:"welcome",author:"bot",text:e.welcome,ts:Date.now()};s.addMessage(c),d.appendMessage(c)}else f&&m.length>0?d.renderWithConversations(p,[]):p.length>1?d.renderWithConversations(p.slice(0,-1),p[p.length-1].messages):d.updateMessages(m);let M=!1;const pe=s.subscribe(()=>{const c=s.snapshot;if(u.setOpen(c.isOpen),d.setAgentActivity(c.agentActivity,e.i18n),d.setOffline(!c.isConnected),d.updateTheme(j(e.theme)),c.isOpen){if(!M){const k=s.countUnread()>0,b=s.lastReadTs;a(),k&&d.scrollToFirstUnread(b)}M=!0,d.show()}else M=!1,d.hide()}),ue=m.length===0||f;n(),g().then(()=>h.connect().then(c=>{var b,y;e.persist&&s.persistSession(c.sessionId),s.setConnected(!0),t.onTransportChange((b=h.kind)!=null?b:"ws");const k=(y=c.suggestedReplies)!=null?y:[];k.length>0&&ue&&d.setSuggestedReplies(k)})).catch(c=>{t.onError(c),s.setConnected(!1),d.setOffline(!0)});const B={transport:null,open(){i.markUserInteraction(),s.open(),a(),t.onOpen()},close(){s.close(),t.onClose()},identify(c){h.setUser(c)},destroy(){pe(),h.stop(),i.destroy(),u.destroy(),d.destroy(),x===B&&(x=null)}};return B}function U(){var s,i;if(typeof document=="undefined")return;const o=document.currentScript;if(!o)return;const e=o.dataset;if(e.autoinit==="false")return;const t=e.org;if(!t){console.error("SpilkiWidget: data-org and is required for auto init");return}x=O({org:t,installationToken:e.installationToken,apiBase:e.apiBase,position:(s=e.position)!=null?s:void 0,theme:(i=e.theme)!=null?i:void 0})}let x=null;typeof window!="undefined"&&(window.SpilkiWidget=window.SpilkiWidget||{},window.SpilkiWidget.init=o=>(x=O(o),x),window.SpilkiWidget.identify=o=>{x?x.identify(o):console.warn("SpilkiWidget: call init() before identify()")}),U(),w.autoInit=U,w.initSpilkiWidget=O,Object.defineProperty(w,Symbol.toStringTag,{value:"Module"})});
|
|
114
114
|
//# sourceMappingURL=widget.umd.js.map
|