@kilnai/widget 0.10.0 → 0.13.0

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.
Files changed (2) hide show
  1. package/dist/widget.js +53 -3
  2. package/package.json +1 -1
package/dist/widget.js CHANGED
@@ -1,4 +1,4 @@
1
- (()=>{var{defineProperty:f,getOwnPropertyNames:w,getOwnPropertyDescriptor:z}=Object,Y=Object.prototype.hasOwnProperty;function $(n){return this[n]}var W=(n)=>{var i=(v??=new WeakMap).get(n),o;if(i)return i;if(i=f({},"__esModule",{value:!0}),n&&typeof n==="object"||typeof n==="function"){for(var r of w(n))if(!Y.call(i,r))f(i,r,{get:$.bind(n,r),enumerable:!(o=z(n,r))||o.enumerable})}return v.set(n,i),i},v;var C=(n)=>n;function M(n,i){this[n]=C.bind(null,i)}var j=(n,i)=>{for(var o in i)f(n,o,{get:i[o],enumerable:!0,configurable:!0,set:M.bind(i,o)})};var K={};j(K,{KilnWidget:()=>p});class u{ws=null;reconnectTimer=null;reconnectDelay=1000;maxReconnectDelay=30000;intentionalClose=!1;messageHandler=null;statusHandler=null;url;userId;constructor(n,i,o){let r=n.startsWith("https")?"wss":"ws",a=n.replace(/^https?:\/\//,"").replace(/^wss?:\/\//,""),l=`kiln_uid_${o}`;this.userId=localStorage.getItem(l)??crypto.randomUUID(),localStorage.setItem(l,this.userId),this.url=`${r}://${a}/apps/${i}/ws?widgetId=${o}&userId=${encodeURIComponent(this.userId)}`}connect(){this.intentionalClose=!1,this.setStatus("connecting");let n=new WebSocket(this.url);this.ws=n,n.onopen=()=>{this.reconnectDelay=1000,this.setStatus("connected")},n.onmessage=(i)=>{try{let o=JSON.parse(i.data);if(o.type==="ping"){try{n.send(JSON.stringify({type:"pong"}))}catch{}return}this.messageHandler?.(o)}catch{}},n.onerror=()=>this.setStatus("error"),n.onclose=()=>{if(this.ws=null,!this.intentionalClose)this.setStatus("disconnected"),this.scheduleReconnect()}}send(n){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)return;let i={type:"message",content:n};this.ws.send(JSON.stringify(i))}identify(n){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)return;let i={type:"identify",visitor:n};this.ws.send(JSON.stringify(i))}onMessage(n){this.messageHandler=n}onStatusChange(n){this.statusHandler=n}disconnect(){if(this.intentionalClose=!0,this.reconnectTimer)clearTimeout(this.reconnectTimer),this.reconnectTimer=null;this.ws?.close(),this.ws=null,this.setStatus("disconnected")}get connected(){return this.ws?.readyState===WebSocket.OPEN}setStatus(n){this.statusHandler?.(n)}scheduleReconnect(){this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.connect()},this.reconnectDelay),this.reconnectDelay=Math.min(this.reconnectDelay*2,this.maxReconnectDelay)}}function y(n){let i=n==="dark";return`
1
+ (()=>{var{defineProperty:f,getOwnPropertyNames:Y,getOwnPropertyDescriptor:$}=Object,W=Object.prototype.hasOwnProperty;function C(n){return this[n]}var L=(n)=>{var i=(y??=new WeakMap).get(n),o;if(i)return i;if(i=f({},"__esModule",{value:!0}),n&&typeof n==="object"||typeof n==="function"){for(var a of Y(n))if(!W.call(i,a))f(i,a,{get:C.bind(n,a),enumerable:!(o=$(n,a))||o.enumerable})}return y.set(n,i),i},y;var j=(n)=>n;function F(n,i){this[n]=j.bind(null,i)}var M=(n,i)=>{for(var o in i)f(n,o,{get:i[o],enumerable:!0,configurable:!0,set:F.bind(i,o)})};var Q={};M(Q,{KilnWidget:()=>u});class g{ws=null;reconnectTimer=null;reconnectDelay=1000;maxReconnectDelay=30000;intentionalClose=!1;messageHandler=null;statusHandler=null;url;userId;constructor(n,i,o){let a=n.startsWith("https")?"wss":"ws",l=n.replace(/^https?:\/\//,"").replace(/^wss?:\/\//,""),r=`kiln_uid_${o}`;this.userId=localStorage.getItem(r)??crypto.randomUUID(),localStorage.setItem(r,this.userId),this.url=`${a}://${l}/apps/${i}/ws?widgetId=${o}&userId=${encodeURIComponent(this.userId)}`}connect(){this.intentionalClose=!1,this.setStatus("connecting");let n=new WebSocket(this.url);this.ws=n,n.onopen=()=>{this.reconnectDelay=1000,this.setStatus("connected")},n.onmessage=(i)=>{try{let o=JSON.parse(i.data);if(o.type==="ping"){try{n.send(JSON.stringify({type:"pong"}))}catch{}return}this.messageHandler?.(o)}catch{}},n.onerror=()=>this.setStatus("error"),n.onclose=()=>{if(this.ws=null,!this.intentionalClose)this.setStatus("disconnected"),this.scheduleReconnect()}}send(n){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)return;let i={type:"message",content:n};this.ws.send(JSON.stringify(i))}identify(n){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)return;let i={type:"identify",visitor:n};this.ws.send(JSON.stringify(i))}onMessage(n){this.messageHandler=n}onStatusChange(n){this.statusHandler=n}disconnect(){if(this.intentionalClose=!0,this.reconnectTimer)clearTimeout(this.reconnectTimer),this.reconnectTimer=null;this.ws?.close(),this.ws=null,this.setStatus("disconnected")}get connected(){return this.ws?.readyState===WebSocket.OPEN}setStatus(n){this.statusHandler?.(n)}scheduleReconnect(){this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.connect()},this.reconnectDelay),this.reconnectDelay=Math.min(this.reconnectDelay*2,this.maxReconnectDelay)}}function m(n){let i=n==="dark";return`
2
2
  :host {
3
3
  --kiln-bg: ${i?"#1a1a2e":"#ffffff"};
4
4
  --kiln-bg-secondary: ${i?"#2a2a3e":"#f0f0f0"};
@@ -287,6 +287,55 @@
287
287
  background: rgba(255, 255, 255, 0.15);
288
288
  }
289
289
 
290
+ .kiln-bubble pre {
291
+ background: rgba(0, 0, 0, 0.06);
292
+ border-radius: 6px;
293
+ padding: 10px 12px;
294
+ margin: 6px 0;
295
+ overflow-x: auto;
296
+ font-size: 13px;
297
+ }
298
+
299
+ .kiln-msg.user .kiln-bubble pre {
300
+ background: rgba(255, 255, 255, 0.1);
301
+ }
302
+
303
+ .kiln-bubble pre code {
304
+ background: none;
305
+ padding: 0;
306
+ font-size: inherit;
307
+ white-space: pre;
308
+ }
309
+
310
+ .kiln-bubble strong {
311
+ font-weight: 600;
312
+ }
313
+
314
+ .kiln-bubble em {
315
+ font-style: italic;
316
+ }
317
+
318
+ .kiln-bubble ul,
319
+ .kiln-bubble ol {
320
+ margin: 4px 0;
321
+ padding-left: 20px;
322
+ }
323
+
324
+ .kiln-bubble li {
325
+ margin: 2px 0;
326
+ line-height: 1.5;
327
+ }
328
+
329
+ .kiln-bubble a {
330
+ color: inherit;
331
+ text-decoration: underline;
332
+ text-underline-offset: 2px;
333
+ }
334
+
335
+ .kiln-bubble a:hover {
336
+ opacity: 0.8;
337
+ }
338
+
290
339
  /* Error message */
291
340
  .kiln-msg.error .kiln-bubble {
292
341
  background: #fee2e2;
@@ -532,5 +581,6 @@
532
581
  .kiln-form-submit:active {
533
582
  transform: scale(0.98);
534
583
  }
535
- `}var L='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>',F='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>',H='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>',m="kiln_visitor_";function P(n){let i=document.createDocumentFragment(),o=/(\*\*[^*]+\*\*|`[^`]+`|\n)/g,r=0,a;while((a=o.exec(n))!==null){if(a.index>r)i.appendChild(document.createTextNode(n.slice(r,a.index)));let l=a[0];if(l===`
536
- `)i.appendChild(document.createElement("br"));else if(l.startsWith("**")){let e=document.createElement("strong");e.textContent=l.slice(2,-2),i.appendChild(e)}else if(l.startsWith("`")){let e=document.createElement("code");e.textContent=l.slice(1,-1),i.appendChild(e)}r=a.index+l.length}if(r<n.length)i.appendChild(document.createTextNode(n.slice(r)));return i}function g(n){try{let i=localStorage.getItem(`${m}${n}`);return i?JSON.parse(i):null}catch{return null}}function J(n,i){try{localStorage.setItem(`${m}${n}`,JSON.stringify(i))}catch{}}class p{config;client;container;shadow;messages=[];isOpen=!1;isLoading=!1;idCounter=0;greetingShown=!1;identified=!1;panelEl;messagesEl;typingEl;inputEl;sendEl;statusDotEl;launcherEl;formEl=null;chatAreaEl;constructor(n){this.config=n,this.client=new u(n.gatewayUrl,n.appName,n.widgetId);let i=g(n.widgetId);if(i)this.identified=!0;this.container=document.createElement("div"),this.container.id="kiln-widget-root",this.shadow=this.container.attachShadow({mode:"closed"}),document.body.appendChild(this.container);let o=document.createElement("style");if(o.textContent=y(this.resolveTheme()),this.shadow.appendChild(o),this.render(),this.client.onMessage((r)=>this.handleFrame(r)),this.client.onStatusChange((r)=>this.updateStatus(r)),this.client.connect(),n.greeting)this.addMessage({id:String(++this.idCounter),role:"assistant",content:n.greeting,timestamp:Date.now()}),this.greetingShown=!0;if(i)this.client.onStatusChange((r)=>{if(r==="connected"&&this.identified){let a=g(n.widgetId);if(a)this.client.identify(a)}this.updateStatus(r)})}resolveTheme(){let n=this.config.theme??"auto";if(n==="auto")return window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light";return n}render(){let n=this.config.position??"bottom-right",i=document.createElement("button");i.id="kiln-launcher",i.className=`position-${n}`,i.setAttribute("aria-label","Open chat"),i.innerHTML=L,i.addEventListener("click",()=>this.toggle()),this.launcherEl=i;let o=document.createElement("div");o.id="kiln-panel",o.className=`position-${n} hidden`,o.setAttribute("role","dialog"),o.setAttribute("aria-label","Chat");let r=document.createElement("div");r.id="kiln-header";let a=document.createElement("span");a.id="kiln-status-dot",a.className="disconnected",this.statusDotEl=a;let l=document.createElement("span");l.id="kiln-title",l.textContent=this.config.appName;let e=document.createElement("button");e.id="kiln-close",e.setAttribute("aria-label","Close chat"),e.innerHTML=F,e.addEventListener("click",()=>this.close()),r.appendChild(a),r.appendChild(l),r.appendChild(e);let s=document.createElement("div");s.id="kiln-chat-area",this.chatAreaEl=s;let t=document.createElement("div");t.id="kiln-messages",this.messagesEl=t;let d=document.createElement("div");d.id="kiln-typing",d.className="hidden";for(let b=0;b<3;b++){let h=document.createElement("span");h.className="kiln-typing-dot",d.appendChild(h)}this.typingEl=d,t.appendChild(d);let x=document.createElement("div");x.id="kiln-input-area";let k=document.createElement("textarea");k.id="kiln-input",k.rows=1,k.placeholder=this.config.placeholder??"Type a message...",k.setAttribute("aria-label","Message input"),k.addEventListener("keydown",(b)=>{if(b.key==="Enter"&&!b.shiftKey)b.preventDefault(),this.sendMessage()}),k.addEventListener("input",()=>this.autoResizeTextarea()),this.inputEl=k;let c=document.createElement("button");c.id="kiln-send",c.setAttribute("aria-label","Send message"),c.innerHTML=H,c.addEventListener("click",()=>this.sendMessage()),this.sendEl=c,x.appendChild(k),x.appendChild(c),s.appendChild(t),s.appendChild(x),o.appendChild(r),o.appendChild(s),this.panelEl=o,this.shadow.appendChild(i),this.shadow.appendChild(o)}renderPreChatForm(n){if(this.formEl)return;let i=document.createElement("div");i.id="kiln-prechat-form";let o=document.createElement("p");o.className="kiln-form-title",o.textContent="Before we start, tell us a bit about yourself",i.appendChild(o);let r=[];for(let l of n.fields){let e=document.createElement("div");e.className="kiln-form-group";let s=document.createElement("label");if(s.className="kiln-form-label",s.textContent=l.label,l.required){let d=document.createElement("span");d.className="kiln-form-required",d.textContent=" *",s.appendChild(d)}let t=document.createElement("input");t.className="kiln-form-input",t.type=l.type==="phone"?"tel":l.type,t.name=l.key,t.required=l.required,t.setAttribute("aria-label",l.label),r.push({key:l.key,input:t,required:l.required}),e.appendChild(s),e.appendChild(t),i.appendChild(e)}let a=document.createElement("button");a.className="kiln-form-submit",a.textContent=n.submitLabel??"Start Chat",a.addEventListener("click",()=>this.submitPreChatForm(r)),i.appendChild(a),i.addEventListener("keydown",(l)=>{if(l.key==="Enter")l.preventDefault(),this.submitPreChatForm(r)}),this.formEl=i,this.chatAreaEl.classList.add("hidden"),this.panelEl.insertBefore(i,this.chatAreaEl)}submitPreChatForm(n){for(let{input:a,required:l}of n){if(l&&!a.value.trim()){a.classList.add("kiln-form-error"),a.focus();return}a.classList.remove("kiln-form-error")}let i={},o={};for(let{key:a,input:l}of n){let e=l.value.trim();if(!e)continue;if(a==="name"||a==="email"||a==="phone")i[a]=e;else o[a]=e}if(Object.keys(o).length>0)i.custom=o;let r=i;J(this.config.widgetId,r),this.identified=!0,this.client.identify(r),this.formEl?.remove(),this.formEl=null,this.chatAreaEl.classList.remove("hidden"),this.inputEl.focus()}autoResizeTextarea(){this.inputEl.style.height="auto",this.inputEl.style.height=`${Math.min(this.inputEl.scrollHeight,120)}px`}open(){if(this.isOpen=!0,this.panelEl.classList.remove("hidden"),this.launcherEl.setAttribute("aria-expanded","true"),!this.formEl)this.inputEl.focus();this.scrollToBottom()}close(){this.isOpen=!1,this.panelEl.classList.add("hidden"),this.launcherEl.setAttribute("aria-expanded","false")}toggle(){if(this.isOpen)this.close();else this.open()}sendMessage(n){let i=n??this.inputEl.value.trim();if(!i||this.isLoading)return;this.removeSuggestions(),this.addMessage({id:String(++this.idCounter),role:"user",content:i,timestamp:Date.now()}),this.inputEl.value="",this.inputEl.style.height="auto",this.setLoading(!0),this.client.send(i)}addMessage(n){this.messages.push(n),this.renderMessage(n),this.scrollToBottom()}renderMessage(n){let i=document.createElement("div");i.className=`kiln-msg ${n.role}`,i.dataset.msgId=n.id;let o=document.createElement("div");if(o.className="kiln-bubble",n.role==="assistant")o.appendChild(P(n.content));else o.textContent=n.content;i.appendChild(o),this.messagesEl.insertBefore(i,this.typingEl)}renderErrorMessage(n){let i=document.createElement("div");i.className="kiln-msg error";let o=document.createElement("div");o.className="kiln-bubble",o.textContent=n,i.appendChild(o),this.messagesEl.insertBefore(i,this.typingEl),this.scrollToBottom()}setLoading(n){if(this.isLoading=n,this.sendEl.disabled=n,this.inputEl.disabled=n,n)this.typingEl.classList.remove("hidden"),this.scrollToBottom();else this.typingEl.classList.add("hidden")}scrollToBottom(){requestAnimationFrame(()=>{this.messagesEl.scrollTop=this.messagesEl.scrollHeight})}handleFrame(n){if(n.type==="done")this.setLoading(!1),this.addMessage({id:String(++this.idCounter),role:"assistant",content:n.content,timestamp:Date.now()});else if(n.type==="error")if(this.setLoading(!1),n.code==="BUDGET_EXHAUSTED")this.renderInfoMessage(n.message);else this.renderErrorMessage(n.message);else if(n.type==="welcome"){if(n.greeting&&!this.greetingShown)this.addMessage({id:String(++this.idCounter),role:"assistant",content:n.greeting,timestamp:Date.now()}),this.greetingShown=!0;if(n.suggestions&&n.suggestions.length>0)this.renderSuggestions([...n.suggestions]);if(n.preChatForm?.enabled&&!this.identified)this.renderPreChatForm(n.preChatForm);if(this.identified){let i=g(this.config.widgetId);if(i)this.client.identify(i)}}else if(n.type==="suggestions")this.renderSuggestions([...n.items])}renderSuggestions(n){this.removeSuggestions();let i=document.createElement("div");i.className="kiln-suggestions";for(let o of n){let r=document.createElement("button");r.className="kiln-chip",r.textContent=o,r.addEventListener("click",()=>{this.sendMessage(o)}),i.appendChild(r)}this.messagesEl.insertBefore(i,this.typingEl),this.scrollToBottom()}removeSuggestions(){let n=this.messagesEl.querySelector(".kiln-suggestions");if(n)n.remove()}renderInfoMessage(n){let i=document.createElement("div");i.className="kiln-msg info";let o=document.createElement("div");o.className="kiln-bubble",o.textContent=n,i.appendChild(o),this.messagesEl.insertBefore(i,this.typingEl),this.scrollToBottom()}updateStatus(n){this.statusDotEl.className=n}destroy(){this.client.disconnect(),this.container.remove()}}(function(){let n=document.currentScript;if(!n)return;let i=n.dataset.gateway,o=n.dataset.app,r=n.dataset.widgetId;if(!i||!o||!r)return;let a={gatewayUrl:i,appName:o,widgetId:r,position:n.dataset.position??"bottom-right",theme:n.dataset.theme??"auto",greeting:n.dataset.greeting,placeholder:n.dataset.placeholder},l=()=>new p(a);if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",l);else l()})();})();
584
+ `}function z(n){let i=document.createDocumentFragment(),o=n.split(/(```[\s\S]*?```)/g);for(let a of o){if(a.startsWith("```")){let e=a.slice(3,-3),t=e.indexOf(`
585
+ `),s=t>=0?e.slice(t+1):e,d=document.createElement("pre"),b=document.createElement("code");b.textContent=s,d.appendChild(b),i.appendChild(d);continue}let l=a.split(`
586
+ `),r=0;while(r<l.length){let e=l[r];if(/^[\s]*[-*]\s+/.test(e)){let t=document.createElement("ul");while(r<l.length){let s=l[r].match(/^[\s]*[-*]\s+(.*)/);if(!s)break;let d=document.createElement("li");d.appendChild(x(s[1])),t.appendChild(d),r++}i.appendChild(t);continue}if(/^[\s]*\d+\.\s+/.test(e)){let t=document.createElement("ol");while(r<l.length){let s=l[r].match(/^[\s]*\d+\.\s+(.*)/);if(!s)break;let d=document.createElement("li");d.appendChild(x(s[1])),t.appendChild(d),r++}i.appendChild(t);continue}if(e.length>0)i.appendChild(x(e));if(r<l.length-1&&e.length>0)i.appendChild(document.createElement("br"));r++}}return i}function x(n){let i=document.createDocumentFragment(),o=/(\*\*[^*]+\*\*|\*[^*]+\*|`[^`]+`|\[[^\]]+\]\([^)]+\))/g,a=0,l;while((l=o.exec(n))!==null){if(l.index>a)i.appendChild(document.createTextNode(n.slice(a,l.index)));let r=l[0];if(r.startsWith("**")){let e=document.createElement("strong");e.appendChild(x(r.slice(2,-2))),i.appendChild(e)}else if(r.startsWith("`")){let e=document.createElement("code");e.textContent=r.slice(1,-1),i.appendChild(e)}else if(r.startsWith("[")){let e=r.match(/^\[([^\]]+)\]\(([^)]+)\)$/);if(e){let t=document.createElement("a");t.textContent=e[1],t.href=e[2],t.target="_blank",t.rel="noopener noreferrer",i.appendChild(t)}}else if(r.startsWith("*")){let e=document.createElement("em");e.appendChild(x(r.slice(1,-1))),i.appendChild(e)}a=l.index+r.length}if(a<n.length)i.appendChild(document.createTextNode(n.slice(a)));return i}var H='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>',P='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>',J='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>',w="kiln_visitor_";function h(n){try{let i=localStorage.getItem(`${w}${n}`);return i?JSON.parse(i):null}catch{return null}}function K(n,i){try{localStorage.setItem(`${w}${n}`,JSON.stringify(i))}catch{}}class u{config;client;container;shadow;messages=[];isOpen=!1;isLoading=!1;idCounter=0;greetingShown=!1;identified=!1;panelEl;messagesEl;typingEl;inputEl;sendEl;statusDotEl;launcherEl;formEl=null;chatAreaEl;constructor(n){this.config=n,this.client=new g(n.gatewayUrl,n.appName,n.widgetId);let i=h(n.widgetId);if(i)this.identified=!0;this.container=document.createElement("div"),this.container.id="kiln-widget-root",this.shadow=this.container.attachShadow({mode:"closed"}),document.body.appendChild(this.container);let o=document.createElement("style");if(o.textContent=m(this.resolveTheme()),this.shadow.appendChild(o),this.render(),this.client.onMessage((a)=>this.handleFrame(a)),this.client.onStatusChange((a)=>this.updateStatus(a)),this.client.connect(),n.greeting)this.addMessage({id:String(++this.idCounter),role:"assistant",content:n.greeting,timestamp:Date.now()}),this.greetingShown=!0;if(i)this.client.onStatusChange((a)=>{if(a==="connected"&&this.identified){let l=h(n.widgetId);if(l)this.client.identify(l)}this.updateStatus(a)})}resolveTheme(){let n=this.config.theme??"auto";if(n==="auto")return window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light";return n}render(){let n=this.config.position??"bottom-right",i=document.createElement("button");i.id="kiln-launcher",i.className=`position-${n}`,i.setAttribute("aria-label","Open chat"),i.innerHTML=H,i.addEventListener("click",()=>this.toggle()),this.launcherEl=i;let o=document.createElement("div");o.id="kiln-panel",o.className=`position-${n} hidden`,o.setAttribute("role","dialog"),o.setAttribute("aria-label","Chat");let a=document.createElement("div");a.id="kiln-header";let l=document.createElement("span");l.id="kiln-status-dot",l.className="disconnected",this.statusDotEl=l;let r=document.createElement("span");r.id="kiln-title",r.textContent=this.config.appName;let e=document.createElement("button");e.id="kiln-close",e.setAttribute("aria-label","Close chat"),e.innerHTML=P,e.addEventListener("click",()=>this.close()),a.appendChild(l),a.appendChild(r),a.appendChild(e);let t=document.createElement("div");t.id="kiln-chat-area",this.chatAreaEl=t;let s=document.createElement("div");s.id="kiln-messages",this.messagesEl=s;let d=document.createElement("div");d.id="kiln-typing",d.className="hidden";for(let p=0;p<3;p++){let v=document.createElement("span");v.className="kiln-typing-dot",d.appendChild(v)}this.typingEl=d,s.appendChild(d);let b=document.createElement("div");b.id="kiln-input-area";let k=document.createElement("textarea");k.id="kiln-input",k.rows=1,k.placeholder=this.config.placeholder??"Type a message...",k.setAttribute("aria-label","Message input"),k.addEventListener("keydown",(p)=>{if(p.key==="Enter"&&!p.shiftKey)p.preventDefault(),this.sendMessage()}),k.addEventListener("input",()=>this.autoResizeTextarea()),this.inputEl=k;let c=document.createElement("button");c.id="kiln-send",c.setAttribute("aria-label","Send message"),c.innerHTML=J,c.addEventListener("click",()=>this.sendMessage()),this.sendEl=c,b.appendChild(k),b.appendChild(c),t.appendChild(s),t.appendChild(b),o.appendChild(a),o.appendChild(t),this.panelEl=o,this.shadow.appendChild(i),this.shadow.appendChild(o)}renderPreChatForm(n){if(this.formEl)return;let i=document.createElement("div");i.id="kiln-prechat-form";let o=document.createElement("p");o.className="kiln-form-title",o.textContent="Before we start, tell us a bit about yourself",i.appendChild(o);let a=[];for(let r of n.fields){let e=document.createElement("div");e.className="kiln-form-group";let t=document.createElement("label");if(t.className="kiln-form-label",t.textContent=r.label,r.required){let d=document.createElement("span");d.className="kiln-form-required",d.textContent=" *",t.appendChild(d)}let s=document.createElement("input");s.className="kiln-form-input",s.type=r.type==="phone"?"tel":r.type,s.name=r.key,s.required=r.required,s.setAttribute("aria-label",r.label),a.push({key:r.key,input:s,required:r.required}),e.appendChild(t),e.appendChild(s),i.appendChild(e)}let l=document.createElement("button");l.className="kiln-form-submit",l.textContent=n.submitLabel??"Start Chat",l.addEventListener("click",()=>this.submitPreChatForm(a)),i.appendChild(l),i.addEventListener("keydown",(r)=>{if(r.key==="Enter")r.preventDefault(),this.submitPreChatForm(a)}),this.formEl=i,this.chatAreaEl.classList.add("hidden"),this.panelEl.insertBefore(i,this.chatAreaEl)}submitPreChatForm(n){for(let{input:l,required:r}of n){if(r&&!l.value.trim()){l.classList.add("kiln-form-error"),l.focus();return}l.classList.remove("kiln-form-error")}let i={},o={};for(let{key:l,input:r}of n){let e=r.value.trim();if(!e)continue;if(l==="name"||l==="email"||l==="phone")i[l]=e;else o[l]=e}if(Object.keys(o).length>0)i.custom=o;let a=i;K(this.config.widgetId,a),this.identified=!0,this.client.identify(a),this.formEl?.remove(),this.formEl=null,this.chatAreaEl.classList.remove("hidden"),this.inputEl.focus()}autoResizeTextarea(){this.inputEl.style.height="auto",this.inputEl.style.height=`${Math.min(this.inputEl.scrollHeight,120)}px`}open(){if(this.isOpen=!0,this.panelEl.classList.remove("hidden"),this.launcherEl.setAttribute("aria-expanded","true"),!this.formEl)this.inputEl.focus();this.scrollToBottom()}close(){this.isOpen=!1,this.panelEl.classList.add("hidden"),this.launcherEl.setAttribute("aria-expanded","false")}toggle(){if(this.isOpen)this.close();else this.open()}sendMessage(n){let i=n??this.inputEl.value.trim();if(!i||this.isLoading)return;this.removeSuggestions(),this.addMessage({id:String(++this.idCounter),role:"user",content:i,timestamp:Date.now()}),this.inputEl.value="",this.inputEl.style.height="auto",this.setLoading(!0),this.client.send(i)}addMessage(n){this.messages.push(n),this.renderMessage(n),this.scrollToBottom()}renderMessage(n){let i=document.createElement("div");i.className=`kiln-msg ${n.role}`,i.dataset.msgId=n.id;let o=document.createElement("div");if(o.className="kiln-bubble",n.role==="assistant")o.appendChild(z(n.content));else o.textContent=n.content;i.appendChild(o),this.messagesEl.insertBefore(i,this.typingEl)}renderErrorMessage(n){let i=document.createElement("div");i.className="kiln-msg error";let o=document.createElement("div");o.className="kiln-bubble",o.textContent=n,i.appendChild(o),this.messagesEl.insertBefore(i,this.typingEl),this.scrollToBottom()}setLoading(n){if(this.isLoading=n,this.sendEl.disabled=n,this.inputEl.disabled=n,n)this.typingEl.classList.remove("hidden"),this.scrollToBottom();else this.typingEl.classList.add("hidden")}scrollToBottom(){requestAnimationFrame(()=>{this.messagesEl.scrollTop=this.messagesEl.scrollHeight})}handleFrame(n){if(n.type==="done")this.setLoading(!1),this.addMessage({id:String(++this.idCounter),role:"assistant",content:n.content,timestamp:Date.now()});else if(n.type==="error")if(this.setLoading(!1),n.code==="BUDGET_EXHAUSTED")this.renderInfoMessage(n.message);else this.renderErrorMessage(n.message);else if(n.type==="welcome"){if(n.greeting&&!this.greetingShown)this.addMessage({id:String(++this.idCounter),role:"assistant",content:n.greeting,timestamp:Date.now()}),this.greetingShown=!0;if(n.suggestions&&n.suggestions.length>0)this.renderSuggestions([...n.suggestions]);if(n.preChatForm?.enabled&&!this.identified)this.renderPreChatForm(n.preChatForm);if(this.identified){let i=h(this.config.widgetId);if(i)this.client.identify(i)}}else if(n.type==="suggestions")this.renderSuggestions([...n.items])}renderSuggestions(n){this.removeSuggestions();let i=document.createElement("div");i.className="kiln-suggestions";for(let o of n){let a=document.createElement("button");a.className="kiln-chip",a.textContent=o,a.addEventListener("click",()=>{this.sendMessage(o)}),i.appendChild(a)}this.messagesEl.insertBefore(i,this.typingEl),this.scrollToBottom()}removeSuggestions(){let n=this.messagesEl.querySelector(".kiln-suggestions");if(n)n.remove()}renderInfoMessage(n){let i=document.createElement("div");i.className="kiln-msg info";let o=document.createElement("div");o.className="kiln-bubble",o.textContent=n,i.appendChild(o),this.messagesEl.insertBefore(i,this.typingEl),this.scrollToBottom()}updateStatus(n){this.statusDotEl.className=n}destroy(){this.client.disconnect(),this.container.remove()}}(function(){let n=document.currentScript;if(!n)return;let i=n.dataset.gateway,o=n.dataset.app,a=n.dataset.widgetId;if(!i||!o||!a)return;let l={gatewayUrl:i,appName:o,widgetId:a,position:n.dataset.position??"bottom-right",theme:n.dataset.theme??"auto",greeting:n.dataset.greeting,placeholder:n.dataset.placeholder},r=()=>new u(l);if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",r);else r()})();})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kilnai/widget",
3
- "version": "0.10.0",
3
+ "version": "0.13.0",
4
4
  "description": "Embeddable chat widget for Kiln AI — add AI chat to any website with one script tag",
5
5
  "type": "module",
6
6
  "main": "dist/widget.js",