@vocantly/widget 0.1.16 → 0.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- var VocantlyWidget=(()=>{var et=Object.create;var ee=Object.defineProperty;var tt=Object.getOwnPropertyDescriptor;var it=Object.getOwnPropertyNames;var nt=Object.getPrototypeOf,st=Object.prototype.hasOwnProperty;var I=(e=>typeof require!="undefined"?require:typeof Proxy!="undefined"?new Proxy(e,{get:(t,i)=>(typeof require!="undefined"?require:t)[i]}):e)(function(e){if(typeof require!="undefined")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var Ae=(e,t)=>()=>{try{return t||e((t={exports:{}}).exports,t),t.exports}catch(i){throw t=0,i}},rt=(e,t)=>{for(var i in t)ee(e,i,{get:t[i],enumerable:!0})},je=(e,t,i,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of it(t))!st.call(e,s)&&s!==i&&ee(e,s,{get:()=>t[s],enumerable:!(n=tt(t,s))||n.enumerable});return e};var ce=(e,t,i)=>(i=e!=null?et(nt(e)):{},je(t||!e||!e.__esModule?ee(i,"default",{value:e,enumerable:!0}):i,e)),at=e=>je(ee({},"__esModule",{value:!0}),e);var Oe=Ae((zt,le)=>{"use strict";var ot=Object.prototype.hasOwnProperty,T="~";function z(){}Object.create&&(z.prototype=Object.create(null),new z().__proto__||(T=!1));function ct(e,t,i){this.fn=e,this.context=t,this.once=i||!1}function He(e,t,i,n,s){if(typeof i!="function")throw new TypeError("The listener must be a function");var a=new ct(i,n||e,s),r=T?T+t:t;return e._events[r]?e._events[r].fn?e._events[r]=[e._events[r],a]:e._events[r].push(a):(e._events[r]=a,e._eventsCount++),e}function te(e,t){--e._eventsCount===0?e._events=new z:delete e._events[t]}function _(){this._events=new z,this._eventsCount=0}_.prototype.eventNames=function(){var t=[],i,n;if(this._eventsCount===0)return t;for(n in i=this._events)ot.call(i,n)&&t.push(T?n.slice(1):n);return Object.getOwnPropertySymbols?t.concat(Object.getOwnPropertySymbols(i)):t};_.prototype.listeners=function(t){var i=T?T+t:t,n=this._events[i];if(!n)return[];if(n.fn)return[n.fn];for(var s=0,a=n.length,r=new Array(a);s<a;s++)r[s]=n[s].fn;return r};_.prototype.listenerCount=function(t){var i=T?T+t:t,n=this._events[i];return n?n.fn?1:n.length:0};_.prototype.emit=function(t,i,n,s,a,r){var c=T?T+t:t;if(!this._events[c])return!1;var l=this._events[c],p=arguments.length,m,f;if(l.fn){switch(l.once&&this.removeListener(t,l.fn,void 0,!0),p){case 1:return l.fn.call(l.context),!0;case 2:return l.fn.call(l.context,i),!0;case 3:return l.fn.call(l.context,i,n),!0;case 4:return l.fn.call(l.context,i,n,s),!0;case 5:return l.fn.call(l.context,i,n,s,a),!0;case 6:return l.fn.call(l.context,i,n,s,a,r),!0}for(f=1,m=new Array(p-1);f<p;f++)m[f-1]=arguments[f];l.fn.apply(l.context,m)}else{var x=l.length,y;for(f=0;f<x;f++)switch(l[f].once&&this.removeListener(t,l[f].fn,void 0,!0),p){case 1:l[f].fn.call(l[f].context);break;case 2:l[f].fn.call(l[f].context,i);break;case 3:l[f].fn.call(l[f].context,i,n);break;case 4:l[f].fn.call(l[f].context,i,n,s);break;default:if(!m)for(y=1,m=new Array(p-1);y<p;y++)m[y-1]=arguments[y];l[f].fn.apply(l[f].context,m)}}return!0};_.prototype.on=function(t,i,n){return He(this,t,i,n,!1)};_.prototype.once=function(t,i,n){return He(this,t,i,n,!0)};_.prototype.removeListener=function(t,i,n,s){var a=T?T+t:t;if(!this._events[a])return this;if(!i)return te(this,a),this;var r=this._events[a];if(r.fn)r.fn===i&&(!s||r.once)&&(!n||r.context===n)&&te(this,a);else{for(var c=0,l=[],p=r.length;c<p;c++)(r[c].fn!==i||s&&!r[c].once||n&&r[c].context!==n)&&l.push(r[c]);l.length?this._events[a]=l.length===1?l[0]:l:te(this,a)}return this};_.prototype.removeAllListeners=function(t){var i;return t?(i=T?T+t:t,this._events[i]&&te(this,i)):(this._events=new z,this._eventsCount=0),this};_.prototype.off=_.prototype.removeListener;_.prototype.addListener=_.prototype.on;_.prefixed=T;_.EventEmitter=_;typeof le!="undefined"&&(le.exports=_)});var Le=Ae((ei,Ie)=>{"use strict";Ie.exports=function(){throw new Error("ws does not work in the browser. Browser clients must use the native WebSocket object")}});var Vt={};rt(Vt,{close:()=>Ft,destroy:()=>Pt,init:()=>Dt,open:()=>$t});var E=ce(Oe(),1);var k=[];for(ie=0;ie<256;++ie)k.push((ie+256).toString(16).slice(1));var ie;function Ue(e,t=0){return(k[e[t+0]]+k[e[t+1]]+k[e[t+2]]+k[e[t+3]]+"-"+k[e[t+4]]+k[e[t+5]]+"-"+k[e[t+6]]+k[e[t+7]]+"-"+k[e[t+8]]+k[e[t+9]]+"-"+k[e[t+10]]+k[e[t+11]]+k[e[t+12]]+k[e[t+13]]+k[e[t+14]]+k[e[t+15]]).toLowerCase()}var ne,lt=new Uint8Array(16);function de(){if(!ne&&(ne=typeof crypto!="undefined"&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto),!ne))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return ne(lt)}var dt=typeof crypto!="undefined"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto),he={randomUUID:dt};function ht(e,t,i){if(he.randomUUID&&!t&&!e)return he.randomUUID();e=e||{};var n=e.random||(e.rng||de)();if(n[6]=n[6]&15|64,n[8]=n[8]&63|128,t){i=i||0;for(var s=0;s<16;++s)t[i+s]=n[s];return t}return Ue(n)}var se=ht;var jt={};var pt=(e=>typeof I!="undefined"?I:typeof Proxy!="undefined"?new Proxy(e,{get:(t,i)=>(typeof I!="undefined"?I:t)[i]}):e)(function(e){if(typeof I!="undefined")return I.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var H=class{constructor(e=!1){this.enabled=e}setEnabled(e){this.enabled=e}debug(...e){this.enabled&&console.debug("[Vocantly SDK]",...e)}info(...e){this.enabled&&console.info("[Vocantly SDK]",...e)}warn(...e){this.enabled&&console.warn("[Vocantly SDK]",...e)}error(...e){this.enabled&&console.error("[Vocantly SDK]",...e)}},pe=typeof process!="undefined"&&process.versions!=null&&process.versions.node!=null,$=null,W=null;async function ft(){return pe?$||W||(W=(async()=>{try{try{return $=await Promise.resolve().then(()=>ce(Le(),1)),$}catch(e){if(typeof pt!="undefined"){let{createRequire:t}=await import("module");return $=t(jt.url||__filename)("ws"),$}throw e}}catch{return null}finally{W=null}})(),W):null}var gt=class extends E.default{constructor(e,t={}){var i,n,s;super(),this.ws=null,this.token=null,this.reconnectAttempts=0,this.reconnectTimer=null,this.isManuallyDisconnected=!1,this.isTokenExpired=!1,this.url=e,this.autoReconnect=(i=t.autoReconnect)!=null?i:!0,this.maxReconnectAttempts=(n=t.maxReconnectAttempts)!=null?n:5,this.logger=new H((s=t.debug)!=null?s:!1)}connect(e){if(!e||e.length===0){let t=new Error("Token is required for connection");return this.emit("error",t),Promise.reject(t)}return this.token=e,this.isManuallyDisconnected=!1,this.isTokenExpired=!1,this.attemptConnection()}async attemptConnection(){var i;let e=new URL(this.url),t=(i=this.token)!=null?i:"";if(pe){let n=await ft();if(this.logger.debug("ws module loaded:",!!n),n&&n.WebSocket)return this.logger.debug("Using ws package with Authorization header"),new Promise((s,a)=>{try{let r=new n.WebSocket(e.toString(),{headers:{Authorization:`Bearer ${t}`}});this.ws=r;let c=!1,l=p=>{if(c)return;c=!0;let m=String((p==null?void 0:p.message)||p||"").toLowerCase();if(m.includes("401")||m.includes("unauthorized")||p!=null&&p.code&&String(p.code).includes("401")){this.handleTokenExpiration();let f=new Error("Token expired or invalid. Please refresh your token and reconnect.");f.name="TokenExpiredError",this.emit("error",f),a(f);return}};typeof r.addEventListener=="function"?(r.addEventListener("error",l),r.addEventListener("open",()=>{r.removeEventListener("error",l)})):typeof r.once=="function"&&(r.once("error",l),r.once("open",()=>{})),this.setupEventHandlers(s,a)}catch(r){this.logger.error("Failed to create ws.WebSocket:",r),a(r)}});this.logger.warn("ws package not available, falling back to native WebSocket")}return this.logger.debug("Using native WebSocket with query parameter",{url:e.toString(),hasToken:!!t}),new Promise((n,s)=>{try{if(!t||t.length===0){let c=new Error("Token is required for WebSocket connection");this.logger.error(c.message),s(c);return}let a=new URL(e.toString());a.searchParams.set("token",t),this.ws=new globalThis.WebSocket(a.toString()),this.logger.debug("WebSocket instance created",{readyState:this.ws.readyState,url:this.ws.url,protocol:this.ws.protocol});let r=this.ws;this.setupEventHandlers(n,s),setTimeout(()=>{(r.readyState===WebSocket.CLOSED||r.readyState===WebSocket.CLOSING)&&this.logger.warn("WebSocket closed immediately after creation",{readyState:r.readyState,url:r.url})},100)}catch(a){this.logger.error("Failed to create native WebSocket:",a),s(a)}})}setupEventHandlers(e,t){this.ws&&(this.ws.onopen=()=>{this.reconnectAttempts=0,this.isTokenExpired=!1;let i=this.ws;if(!i){this.logger.error("WebSocket is null in onopen handler"),t(new Error("WebSocket instance lost"));return}if(this.logger.debug("WebSocket connected",{readyState:i.readyState,url:i.url,protocol:i.protocol,extensions:i.extensions}),i.readyState!==WebSocket.OPEN){this.logger.error("WebSocket readyState is not OPEN after onopen",{readyState:i.readyState}),t(new Error(`WebSocket not open: readyState=${i.readyState}`));return}this.emit("connected"),e()},this.ws.onmessage=i=>{try{let n=JSON.parse(i.data);this.logger.debug("Received message:",n),this.emit("message",n)}catch(n){let s=n instanceof Error?n:new Error("Failed to parse message");this.logger.error("Failed to parse message:",s),this.emit("error",s)}},this.ws.onerror=i=>{this.logger.error("WebSocket error:",i);let n;if(i instanceof Error?n=i:i&&i.message?n=i instanceof Error?i:new Error(String(i.message)):n=new Error("WebSocket connection error"),pe&&i&&i.message){let s=String(i.message).toLowerCase();if(s.includes("401")||s.includes("unauthorized")){this.handleTokenExpiration();let a=new Error("Token expired or invalid. Please refresh your token and reconnect.");a.name="TokenExpiredError",this.emit("error",a),t(a);return}}this.emit("error",n)},this.ws.onclose=i=>{if(this.logger.debug("WebSocket closed:",{code:i.code,reason:i.reason,wasClean:i.wasClean}),i.code===1006&&this.logger.warn("Abnormal closure (1006) - connection closed without proper handshake. Possible causes: network issue, server closed connection, CORS issue, or protocol mismatch."),i.code===1008||i.code===1002||i.reason&&i.reason.toLowerCase().includes("unauthorized")||i.reason&&i.reason.toLowerCase().includes("401")){this.handleTokenExpiration();let s=new Error("Token expired or invalid. Please refresh your token and reconnect.");s.name="TokenExpiredError",this.emit("error",s),this.emit("disconnected",{code:i.code,reason:i.reason,tokenExpired:!0});return}this.emit("disconnected",{code:i.code,reason:i.reason}),!this.isManuallyDisconnected&&!this.isTokenExpired&&this.autoReconnect?this.reconnectAttempts<this.maxReconnectAttempts?this.scheduleReconnect():(this.logger.warn("Max reconnection attempts reached"),this.emit("error",new Error("Max reconnection attempts reached"))):this.isTokenExpired&&this.logger.warn("Not reconnecting: token is expired")})}handleTokenExpiration(){this.isTokenExpired=!0,this.autoReconnect=!1,this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null)}disconnect(){this.isManuallyDisconnected=!0,this.autoReconnect=!1,this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.ws&&(this.ws.close(),this.ws=null),this.logger.debug("WebSocket disconnected")}send(e){if(!this.ws||this.ws.readyState!==WebSocket.OPEN){let t=new Error("WebSocket is not connected");throw this.logger.error(t.message),this.emit("error",t),t}try{let t=JSON.stringify(e);this.logger.debug("Sending message:",e),this.ws.send(t)}catch(t){let i=t instanceof Error?t:new Error("Failed to send message");throw this.logger.error("Send error:",i),this.emit("error",i),i}}isConnected(){return this.ws!==null&&this.ws.readyState===WebSocket.OPEN}getReconnectAttempts(){return this.reconnectAttempts}scheduleReconnect(){if(this.reconnectTimer||!this.token)return;this.reconnectAttempts++;let e=[1e3,2e3,5e3,1e4],t=e[Math.min(this.reconnectAttempts-1,e.length-1)];this.logger.debug(`Scheduling reconnect attempt ${this.reconnectAttempts} in ${t}ms`),this.emit("reconnecting",{attempt:this.reconnectAttempts,delay:t}),this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.attemptConnection().catch(()=>{})},t)}},ut=class{constructor(){this.token=null}setToken(e){if(!this.isValidFormat(e))throw new Error("Invalid token format");this.token=e}getToken(){return this.token}hasToken(){return this.token!==null&&this.token.length>0}clearToken(){this.token=null}rotateToken(e){if(!this.isValidFormat(e))throw new Error("Invalid token format");this.token=e}isValidFormat(e){return typeof e=="string"&&e.length>0}validateTokenPresence(){if(!this.hasToken())throw new Error("Token is required but not set")}},mt=new Set(["text","system","image","video","file"]);function De(e){return e&&mt.has(e)?e:"text"}var q=class{static createOptimistic(e,t,i,n="text",s,a){return{id:t,conversationId:e,content:i,messageType:n,optimistic:!0,receiptState:"sent",createdAt:new Date,metadata:s,attachments:a}}static fromServerPayload(e,t){var s,a;let i=(s=t.sender_id)!=null?s:t.senderId,n=(a=t.sender_type)!=null?a:t.senderType;return{id:t.message_id,conversationId:e,content:t.content,senderId:i,senderName:t.sender_name,senderType:n,messageType:De(t.message_type),optimistic:!1,receiptState:"sent",createdAt:t.sent_at?new Date(t.sent_at):new Date,attachments:t.attachments}}static confirmOptimistic(e,t){var s,a,r,c;let i=(s=t.sender_id)!=null?s:t.senderId,n=(a=t.sender_type)!=null?a:t.senderType;return{...e,optimistic:!1,senderId:i,senderName:t.sender_name,senderType:n,messageType:De(t.message_type),receiptState:(r=e.receiptState)!=null?r:"sent",createdAt:t.sent_at?new Date(t.sent_at):e.createdAt,attachments:(c=t.attachments)!=null?c:e.attachments}}static updateReceiptState(e,t){return{...e,receiptState:t}}},wt=class extends E.default{constructor(e=!1){super(),this.receiptState=new Map,this.logger=new H(e)}getReceiptState(e){return this.receiptState.get(e)}handleDelivered(e,t){var n;if(!e)return this.logger.warn("Invalid delivered receipt: missing messageId"),!1;let i=(n=this.receiptState.get(e))!=null?n:"sent";return this.compareStates(i,"delivered")<0?(this.receiptState.set(e,"delivered"),this.emit("receipt",{messageId:e,state:"delivered",userId:t}),this.logger.debug("Receipt delivered:",e),!0):(this.logger.debug("Delivered receipt ignored (already delivered or read):",e),!1)}handleRead(e,t){var n;if(!e)return this.logger.warn("Invalid read receipt: missing messageId"),!1;let i=(n=this.receiptState.get(e))!=null?n:"sent";return this.compareStates(i,"read")<0?(this.receiptState.set(e,"read"),this.emit("receipt",{messageId:e,state:"read",userId:t}),this.logger.debug("Receipt read:",e),!0):(this.logger.debug("Read receipt ignored (already read):",e),!1)}compareStates(e,t){let i=["sent","delivered","read"],n=i.indexOf(e),s=i.indexOf(t);return n===-1||s===-1?0:n-s}clearConversation(e,t){let i=0;for(let n of t)this.receiptState.has(n)&&(this.receiptState.delete(n),i++);i>0&&this.logger.debug("Cleared receipt state for conversation:",e,`(${i} messages)`)}clearMessage(e){this.receiptState.delete(e)&&this.logger.debug("Cleared receipt state for message:",e)}clear(){this.receiptState.clear(),this.logger.debug("Cleared all receipt state")}},bt=class extends E.default{constructor(e=!1){super(),this.seenMessageIds=new Map,this.optimisticMessages=new Map,this.confirmedMessages=new Map,this.warnedMissingSenderId=new Set,this.logger=new H(e),this.receiptManager=new wt(e),this.receiptManager.on("receipt",t=>{this.updateMessageReceiptState(t)})}createOptimisticMessage(e,t,i,n="text",s,a){let r=q.createOptimistic(e,t,i,n,s,a);return this.trackOptimisticMessage(e,t,r),this.trackMessageId(e,t),this.emit("message",r),this.logger.debug("Created optimistic message:",t),r}handleIncomingMessage(e,t){let i=t.message_id,n=this.getOptimisticMessage(e,i);if(n){let a=this.reconcileOptimisticMessage(n,t);return a.senderId==null&&!this.warnedMissingSenderId.has(e)&&(this.warnedMissingSenderId.add(e),console.warn('[Vocantly SDK] Message payload missing sender_id (and sender_type). Clients cannot show "You" vs other participant. The realtime backend MUST include sender_id and sender_type in every type:"message" payload. See docs/REALTIME_WEBSOCKET_CONTRACT.md.')),this.removeOptimisticMessage(e,i),this.trackMessageId(e,i),this.trackConfirmedMessage(e,i,a),a}if(this.hasSeenMessage(e,i))return this.logger.debug("Duplicate message ignored:",i),null;this.trackMessageId(e,i);let s=q.fromServerPayload(e,t);return s.senderId==null&&!this.warnedMissingSenderId.has(e)&&(this.warnedMissingSenderId.add(e),console.warn('[Vocantly SDK] Message payload missing sender_id (and sender_type). Clients cannot show "You" vs other participant. The realtime backend MUST include sender_id and sender_type in every type:"message" payload. See docs/REALTIME_WEBSOCKET_CONTRACT.md.')),this.trackConfirmedMessage(e,i,s),this.emit("message",s),this.logger.debug("Handled incoming message:",i),s}handleReceipt(e,t,i,n){i==="delivered"?this.receiptManager.handleDelivered(t,n):i==="read"&&this.receiptManager.handleRead(t,n)}updateMessageReceiptState(e){for(let[,t]of this.confirmedMessages.entries()){let i=t.get(e.messageId);if(i){let n=q.updateReceiptState(i,e.state);t.set(e.messageId,n),this.emit("message",n),this.logger.debug("Updated message receipt state:",e.messageId,e.state);return}}for(let[,t]of this.optimisticMessages.entries()){let i=t.get(e.messageId);if(i){let n=q.updateReceiptState(i,e.state);t.set(e.messageId,n),this.emit("message",n),this.logger.debug("Updated optimistic message receipt state:",e.messageId,e.state);return}}this.logger.warn("Receipt for unknown message:",e.messageId)}reconcileOptimisticMessage(e,t){let i=q.confirmOptimistic(e,t);return this.logger.debug("Reconciled optimistic message (no second emit):",e.id),i}hasSeenMessage(e,t){var n;let i=this.seenMessageIds.get(e);return(n=i==null?void 0:i.has(t))!=null?n:!1}trackMessageIdsFromHistory(e,t){for(let i of t)this.trackMessageId(e,i)}trackMessageId(e,t){let i=this.seenMessageIds.get(e);i||(i=new Set,this.seenMessageIds.set(e,i)),i.add(t)}trackOptimisticMessage(e,t,i){let n=this.optimisticMessages.get(e);n||(n=new Map,this.optimisticMessages.set(e,n)),n.set(t,i)}getOptimisticMessage(e,t){let i=this.optimisticMessages.get(e);return i==null?void 0:i.get(t)}removeOptimisticMessage(e,t){let i=this.optimisticMessages.get(e);i&&(i.delete(t),i.size===0&&this.optimisticMessages.delete(e))}trackConfirmedMessage(e,t,i){let n=this.confirmedMessages.get(e);n||(n=new Map,this.confirmedMessages.set(e,n)),n.set(t,i)}getMessageIds(e){let t=this.confirmedMessages.get(e),i=this.optimisticMessages.get(e),n=new Set;if(t)for(let s of t.keys())n.add(s);if(i)for(let s of i.keys())n.add(s);return Array.from(n)}clearConversation(e){this.seenMessageIds.delete(e),this.optimisticMessages.delete(e);let t=this.getMessageIds(e);this.receiptManager.clearConversation(e,t),this.confirmedMessages.delete(e),this.logger.debug("Cleared message tracking for conversation:",e)}clear(){this.seenMessageIds.clear(),this.optimisticMessages.clear(),this.confirmedMessages.clear(),this.receiptManager.clear(),this.logger.debug("Cleared all message tracking")}},vt=class extends E.default{constructor(e=!1){super(),this.typingTimers=new Map,this.activeTyping=new Map,this.TYPING_TIMEOUT_MS=5e3,this.logger=new H(e)}handleTypingStart(e,t){if(!e||!t){this.logger.warn("Invalid typing_start: missing conversationId or userId");return}let i=this.activeTyping.get(e);i||(i=new Set,this.activeTyping.set(e,i));let n=this.typingTimers.get(e);n||(n=new Map,this.typingTimers.set(e,n));let s=i.has(t),a=n.get(t);a&&clearTimeout(a),i.add(t);let r=setTimeout(()=>{this.handleTypingStop(e,t)},this.TYPING_TIMEOUT_MS);n.set(t,r),s?this.logger.debug("Typing timer restarted:",e,t):(this.emit("typing",{conversationId:e,userId:t,state:"start"}),this.logger.debug("Typing started:",e,t))}handleTypingStop(e,t){if(!e||!t){this.logger.warn("Invalid typing_stop: missing conversationId or userId");return}let i=this.activeTyping.get(e),n=this.typingTimers.get(e);if(!i||!i.has(t)){this.logger.debug("Typing stop ignored (user not typing):",e,t);return}let s=n==null?void 0:n.get(t);s&&(clearTimeout(s),n==null||n.delete(t)),i.delete(t),i.size===0&&this.activeTyping.delete(e),n&&n.size===0&&this.typingTimers.delete(e),this.emit("typing",{conversationId:e,userId:t,state:"stop"}),this.logger.debug("Typing stopped:",e,t)}clearConversation(e){let t=this.activeTyping.get(e),i=this.typingTimers.get(e);if(t){for(let n of t)this.emit("typing",{conversationId:e,userId:n,state:"stop"});this.activeTyping.delete(e)}if(i){for(let n of i.values())clearTimeout(n);this.typingTimers.delete(e)}this.logger.debug("Cleared typing state for conversation:",e)}clear(){for(let e of this.typingTimers.values())for(let t of e.values())clearTimeout(t);this.typingTimers.clear(),this.activeTyping.clear(),this.logger.debug("Cleared all typing state")}getTypingUsers(e){let t=this.activeTyping.get(e);return t?Array.from(t):[]}},yt=[{urls:"stun:stun.l.google.com:19302"},{urls:"stun:stun1.l.google.com:19302"}],Fe,$e=typeof RTCPeerConnection!="undefined"&&typeof navigator!="undefined"&&typeof((Fe=navigator.mediaDevices)==null?void 0:Fe.getUserMedia)=="function",B=class extends E.default{constructor(e={}){var t;super(),this.peers=new Map,this.localStream=null,this.mediaOptions={},this.screenTrack=null,this.cameraTrack=null,this.options=e,this.iceServers=(t=e.iceServers)!=null?t:yt,this.getIceServers=e.getIceServers,this.iceServersResolved=!e.getIceServers}static isAvailable(){return $e}static async enumerateDevices(){var e;if(!((e=navigator.mediaDevices)!=null&&e.enumerateDevices))return[];try{return await navigator.mediaDevices.enumerateDevices()}catch{return[]}}setMediaOptions(e){this.mediaOptions={...this.mediaOptions,...e}}async getMedia(e){if(this.localStream)return this.localStream;let t=this.mediaOptions,i={echoCancellation:!0,noiseSuppression:t.noiseSuppression!==!1};t.audioDeviceId&&(i.deviceId={exact:t.audioDeviceId});let n=!1;if(e==="video"){let a={};t.videoDeviceId&&(a.deviceId={exact:t.videoDeviceId}),t.lowBandwidth&&(a.width={ideal:640},a.height={ideal:360},a.frameRate={ideal:15,max:20}),n=Object.keys(a).length?a:!0}let s=await navigator.mediaDevices.getUserMedia({audio:i,video:n});return t.startMuted&&s.getAudioTracks().forEach(a=>a.enabled=!1),t.startVideoOff&&s.getVideoTracks().forEach(a=>a.enabled=!1),this.localStream=s,s}async createPeerConnection(e,t){if(!$e)return null;if(!this.iceServersResolved&&this.getIceServers)try{this.iceServers=await this.getIceServers(),this.iceServersResolved=!0}catch(s){this.options.debug&&console.warn("Failed to fetch ICE servers, using defaults:",s),this.iceServersResolved=!0}let i=new RTCPeerConnection({iceServers:this.iceServers}),n={pc:i};return this.peers.set(e,n),i.ontrack=s=>{s.streams[0]&&(n.remoteStream=s.streams[0],this.emit("remote_stream",{userId:e,stream:s.streams[0]}))},i.onicecandidate=s=>{s.candidate&&this.emit("ice_candidate",{userId:e,candidate:s.candidate.toJSON()})},i.oniceconnectionstatechange=()=>{if(i.iceConnectionState==="connected"||i.iceConnectionState==="completed")this.mediaOptions.lowBandwidth&&this.setVideoBitrate(35e4),this.emit("peer_connected",{userId:e});else if(i.iceConnectionState==="failed"){let s=this.peers.get(e);s&&!s.hasRestartedIce&&typeof i.restartIce=="function"?(s.hasRestartedIce=!0,i.restartIce()):this.emit("ice_failed",{userId:e})}},i}async createOffer(e,t){let i=await this.getMedia(t),n=await this.createPeerConnection(e,t);if(!n)return null;i.getTracks().forEach(a=>n.addTrack(a,i));let s=await n.createOffer();return await n.setLocalDescription(s),s}async handleOffer(e,t,i){let n=await this.getMedia(i),s=await this.createPeerConnection(e,i);if(!s)return null;n.getTracks().forEach(r=>s.addTrack(r,n)),await s.setRemoteDescription(new RTCSessionDescription(t));let a=await s.createAnswer();return await s.setLocalDescription(a),a}async handleAnswer(e,t){let i=this.peers.get(e);i&&await i.pc.setRemoteDescription(new RTCSessionDescription(t))}async addIceCandidate(e,t){let i=this.peers.get(e);if(i)try{await i.pc.addIceCandidate(new RTCIceCandidate(t))}catch(n){this.options.debug&&console.warn("addIceCandidate failed:",n)}}getLocalStream(){return this.localStream}getRemoteStream(e){var t;return(t=this.peers.get(e))==null?void 0:t.remoteStream}getAllRemoteStreams(){let e=new Map;for(let[t,i]of this.peers)i.remoteStream&&e.set(t,i.remoteStream);return e}async applyVideoConstraints(e){var i;let t=(i=this.localStream)==null?void 0:i.getVideoTracks()[0];if(t)try{await t.applyConstraints(e)}catch{}}async applyAudioConstraints(e){var i;let t=(i=this.localStream)==null?void 0:i.getAudioTracks()[0];if(t)try{await t.applyConstraints(e)}catch{}}async setVideoBitrate(e){for(let[,t]of this.peers){let i=t.pc.getSenders().find(s=>{var a;return((a=s.track)==null?void 0:a.kind)==="video"});if(!i)continue;let n=i.getParameters();(!n.encodings||n.encodings.length===0)&&(n.encodings=[{}]);for(let s of n.encodings)e==null?delete s.maxBitrate:s.maxBitrate=e;try{await i.setParameters(n)}catch{}}}async setLowBandwidth(e){this.mediaOptions.lowBandwidth=e,await this.applyVideoConstraints(e?{width:{max:640},height:{max:360},frameRate:{max:20}}:{width:{ideal:1280},height:{ideal:720},frameRate:{ideal:30}}),await this.setVideoBitrate(e?35e4:null)}async switchDevice(e,t){if(!this.localStream)return;let i=e==="video"?{video:{deviceId:{exact:t}}}:{audio:{deviceId:{exact:t}}},n=await navigator.mediaDevices.getUserMedia(i),s=e==="video"?n.getVideoTracks()[0]:n.getAudioTracks()[0];if(!s)return;let a=e==="video"?this.localStream.getVideoTracks()[0]:this.localStream.getAudioTracks()[0];a&&(s.enabled=a.enabled);for(let[,r]of this.peers){let c=r.pc.getSenders().find(l=>{var p;return((p=l.track)==null?void 0:p.kind)===e});c&&await c.replaceTrack(s)}a&&(this.localStream.removeTrack(a),a.stop()),this.localStream.addTrack(s)}isScreenSharing(){return!!this.screenTrack}async startScreenShare(){var s,a;if(this.screenTrack)return!0;let e=navigator.mediaDevices;if(!e.getDisplayMedia)return!1;let t=await e.getDisplayMedia({video:!0,audio:!1}),i=t.getVideoTracks()[0];if(!i)return!1;this.cameraTrack=(a=(s=this.localStream)==null?void 0:s.getVideoTracks()[0])!=null?a:null;let n=!1;for(let[,r]of this.peers){let c=r.pc.getSenders().find(l=>{var p;return((p=l.track)==null?void 0:p.kind)==="video"});c&&(await c.replaceTrack(i),n=!0)}return n?(this.screenTrack=i,i.onended=()=>{this.stopScreenShare()},this.emit("screen_share_changed",{active:!0,stream:t}),!0):(i.stop(),!1)}async stopScreenShare(){var e;if(this.screenTrack){for(let[,t]of this.peers){let i=t.pc.getSenders().find(n=>{var s;return((s=n.track)==null?void 0:s.kind)==="video"||n.track===this.screenTrack});i&&await i.replaceTrack((e=this.cameraTrack)!=null?e:null)}this.screenTrack.stop(),this.screenTrack=null,this.emit("screen_share_changed",{active:!1})}}closePeer(e){let t=this.peers.get(e);t&&t.pc.signalingState!=="closed"&&t.pc.close(),this.peers.delete(e)}cleanup(){for(let[e]of this.peers)this.closePeer(e);this.localStream&&(this.localStream.getTracks().forEach(e=>e.stop()),this.localStream=null),this.removeAllListeners()}async getCallStats(){let e={};for(let[,t]of this.peers)try{(await t.pc.getStats()).forEach(n=>{var a,r,c,l,p,m,f,x,y,d;let s=n;if(n.type==="inbound-rtp")e.packets_lost_in=((a=e.packets_lost_in)!=null?a:0)+((r=s.packetsLost)!=null?r:0),e.packets_received=((c=e.packets_received)!=null?c:0)+((l=s.packetsReceived)!=null?l:0),e.bytes_received=((p=e.bytes_received)!=null?p:0)+((m=s.bytesReceived)!=null?m:0),typeof s.jitter=="number"&&(e.jitter_ms=s.jitter*1e3);else if(n.type==="outbound-rtp")e.packets_sent=((f=e.packets_sent)!=null?f:0)+((x=s.packetsSent)!=null?x:0),e.bytes_sent=((y=e.bytes_sent)!=null?y:0)+((d=s.bytesSent)!=null?d:0);else if(n.type==="candidate-pair"&&n.state==="succeeded"){let M=n.currentRoundTripTime;typeof M=="number"&&(e.rtt_ms=M*1e3)}})}catch{}return e}setLocalAudioEnabled(e){this.localStream&&this.localStream.getAudioTracks().forEach(t=>t.enabled=e)}setLocalVideoEnabled(e){this.localStream&&this.localStream.getVideoTracks().forEach(t=>t.enabled=e)}},xt=6e4,kt=class extends E.default{constructor(e,t={}){var i,n;super(),this.ringTimeoutId=null,this.webRTC=null,this.callRole=null,this.currentCallType="video",this.participants=new Set,this.webRTCEventsSetup=!1,this.callStartedAt=null,this.callConnectedEmitted=!1,this.conversationId=e,this.logger=new H((i=t.debug)!=null?i:!1),this.ringTimeoutMs=(n=t.ringTimeout)!=null?n:xt,this.submitCallQuality=t.submitCallQuality,B.isAvailable()&&(this.webRTC=new B({debug:t.debug,getIceServers:t.getIceServers}))}emitCallConnected(e,t,i){this.callConnectedEmitted||(this.callConnectedEmitted=!0,this.callStartedAt==null&&(this.callStartedAt=Date.now()),this.emit("call_connected",{localStream:e,remoteStream:t,remoteUserId:i}))}setSendCallHandler(e){this.sendCallHandler=e}setCallOptions(e){var t;(t=this.webRTC)==null||t.setMediaOptions(e)}listDevices(){return B.enumerateDevices()}applyVideoConstraints(e){return this.webRTC?this.webRTC.applyVideoConstraints(e):Promise.resolve()}applyAudioConstraints(e){return this.webRTC?this.webRTC.applyAudioConstraints(e):Promise.resolve()}switchDevice(e,t){return this.webRTC?this.webRTC.switchDevice(e,t):Promise.resolve()}setLowBandwidth(e){return this.webRTC?this.webRTC.setLowBandwidth(e):Promise.resolve()}startScreenShare(){return this.webRTC?this.webRTC.startScreenShare():Promise.resolve(!1)}stopScreenShare(){return this.webRTC?this.webRTC.stopScreenShare():Promise.resolve()}isScreenSharing(){var e,t;return(t=(e=this.webRTC)==null?void 0:e.isScreenSharing())!=null?t:!1}setSubmitCallQuality(e){this.submitCallQuality=e}send(e){if(!this.sendCallHandler){let t=new Error("Call not connected. Join conversation first.");throw this.emit("call_error",{reason:"websocket_lost",message:t.message}),t}this.sendCallHandler(e)}clearRingTimeout(){this.ringTimeoutId&&(clearTimeout(this.ringTimeoutId),this.ringTimeoutId=null)}startCall(e="video"){this.clearRingTimeout(),this.callConnectedEmitted=!1,this.callRole="caller",this.currentCallType=e;let t={type:"call_invite",conversation_id:this.conversationId,payload:{call_type:e}};this.send(t),this.logger.debug("call.invite sent",{conversation_id:this.conversationId,call_type:e}),this.ringTimeoutId=setTimeout(()=>{this.ringTimeoutId=null,this.logger.debug("call ring timeout - no answer"),this.endCall("no_answer"),this.emit("call_timeout",{reason:"no_answer"})},this.ringTimeoutMs)}acceptCall(){this.clearRingTimeout(),this.callConnectedEmitted=!1,this.callRole="callee";let e={type:"call_accept",conversation_id:this.conversationId};this.send(e),this.logger.debug("call.accept sent",{conversation_id:this.conversationId})}joinCall(e="video"){this.callRole="joiner",this.currentCallType=e;let t={type:"call_join",conversation_id:this.conversationId};this.send(t),this.logger.debug("call.join sent",{conversation_id:this.conversationId})}rejectCall(e){this.clearRingTimeout();let t={type:"call_reject",conversation_id:this.conversationId,payload:e?{reason:e}:void 0};this.send(t),this.logger.debug("call.reject sent",{conversation_id:this.conversationId,reason:e})}endCall(e){this.clearRingTimeout();let t={type:"call_hangup",conversation_id:this.conversationId,payload:e?{reason:e}:void 0};this.send(t),this.logger.debug("call.hangup sent",{conversation_id:this.conversationId,reason:e})}handleCallInvite(e){this.currentCallType=e.call_type||"video",this.emit("call_invite",{callType:this.currentCallType,callerId:e.caller_id,callerName:e.caller_name})}async handleCallAccept(e){this.clearRingTimeout();let t=e==null?void 0:e.sender_id;if(this.participants.add(t!=null?t:"unknown"),this.webRTC&&t&&this.callRole==="caller")try{let i=await this.webRTC.createOffer(t,this.currentCallType);i&&(this.sendOffer(t,i),this.setupWebRTCEvents())}catch(i){this.logger.debug("WebRTC createOffer failed:",i),this.emit("call_error",{reason:"permission_denied",message:String(i)})}this.emit("call_accept",{accepterId:t})}async handleCallJoin(e){let t=e.joiner_id;if(t&&this.participants.add(t),this.webRTC&&t&&(this.callRole==="caller"||this.callRole==="callee"))try{let i=await this.webRTC.createOffer(t,this.currentCallType);i&&(this.sendOffer(t,i),this.setupWebRTCEvents())}catch(i){this.logger.debug("WebRTC createOffer for joiner failed:",i)}this.emit("call_join",{joinerId:e.joiner_id,joinerName:e.joiner_name})}handleCallReject(e){this.clearRingTimeout(),this.emit("call_reject",{reason:e==null?void 0:e.reason})}async handleCallHangup(e){var i;this.clearRingTimeout();let t=(i=e==null?void 0:e.reason)!=null?i:"user_hangup";await this.submitQualityAndCleanup(t),this.callRole=null,this.participants.clear(),this.emit("call_hangup",{endedBy:e==null?void 0:e.ended_by,reason:t})}handleCallParticipantLeft(e){var i;let t=e.left_user_id;t&&(this.participants.delete(t),(i=this.webRTC)==null||i.closePeer(t)),this.emit("peer_left",{userId:t})}handleCallError(e){this.clearRingTimeout(),this.emit("call_error",{code:e.code,message:e.message,details:e.details})}async handleCallOffer(e){var n;let t=(n=e.from_user_id)!=null?n:e.sender_id,i=e.sdp;if(!(!t||!i)){if(this.participants.add(t),this.webRTC&&(this.callRole==="callee"||this.callRole==="joiner"))try{this.setupWebRTCEvents();let s=await this.webRTC.handleOffer(t,i,this.currentCallType);if(s){this.sendAnswer(t,s);let a=this.webRTC.getLocalStream(),r=this.webRTC.getRemoteStream(t);a&&r&&this.emitCallConnected(a,r,t)}}catch(s){this.logger.debug("WebRTC handleOffer failed:",s),this.emit("call_error",{reason:"permission_denied",message:String(s)})}this.emit("call_offer",{sdp:i,fromUserId:t})}}async handleCallAnswer(e){var n;let t=(n=e.from_user_id)!=null?n:e.sender_id,i=e.sdp;if(!(!t||!i)){if(this.webRTC){await this.webRTC.handleAnswer(t,i);let s=this.webRTC.getLocalStream(),a=this.webRTC.getRemoteStream(t);s&&a&&this.emitCallConnected(s,a,t)}this.emit("call_answer",{sdp:i,fromUserId:t})}}async handleCallICECandidate(e){var n;let t=(n=e.from_user_id)!=null?n:e.sender_id,i=e.candidate;!t||!i||(this.webRTC&&await this.webRTC.addIceCandidate(t,i),this.emit("call_ice_candidate",{candidate:i,fromUserId:t}))}handleCallMute(e){this.emit("peer_mute",{userId:e==null?void 0:e.user_id})}handleCallUnmute(e){this.emit("peer_unmute",{userId:e==null?void 0:e.user_id})}handleVideoOff(e){this.emit("peer_video_off",{userId:e==null?void 0:e.user_id})}handleVideoOn(e){this.emit("peer_video_on",{userId:e==null?void 0:e.user_id})}setMuted(e){var i;(i=this.webRTC)==null||i.setLocalAudioEnabled(!e);let t=e?{type:"call_mute",conversation_id:this.conversationId}:{type:"call_unmute",conversation_id:this.conversationId};this.send(t),this.emit("local_mute_changed",{muted:e})}setVideoEnabled(e){var i;(i=this.webRTC)==null||i.setLocalVideoEnabled(e);let t=e?{type:"video_on",conversation_id:this.conversationId}:{type:"video_off",conversation_id:this.conversationId};this.send(t),this.emit("local_video_changed",{enabled:e})}getLocalStream(){var e,t;return(t=(e=this.webRTC)==null?void 0:e.getLocalStream())!=null?t:null}getRemoteStream(e){var t;return(t=this.webRTC)==null?void 0:t.getRemoteStream(e)}getAllRemoteStreams(){var e,t;return(t=(e=this.webRTC)==null?void 0:e.getAllRemoteStreams())!=null?t:new Map}sendOffer(e,t){let i={sdp:t,target_user_id:this.participants.size>2?e:void 0};this.send({type:"call_offer",conversation_id:this.conversationId,payload:i})}sendAnswer(e,t){let i={sdp:t,target_user_id:this.participants.size>2?e:void 0};this.send({type:"call_answer",conversation_id:this.conversationId,payload:i})}setupWebRTCEvents(){!this.webRTC||this.webRTCEventsSetup||(this.webRTCEventsSetup=!0,this.webRTC.on("ice_candidate",({userId:e,candidate:t})=>{let i={candidate:t,target_user_id:this.participants.size>2?e:void 0};this.send({type:"call_ice_candidate",conversation_id:this.conversationId,payload:i})}),this.webRTC.on("remote_stream",({userId:e,stream:t})=>{var n;let i=(n=this.webRTC)==null?void 0:n.getLocalStream();i&&this.emitCallConnected(i,t,e)}),this.webRTC.on("ice_failed",async()=>{await this.submitQualityAndCleanup("ice_failed"),this.emit("call_error",{reason:"ice_failed",message:"ICE connection failed"})}),this.webRTC.on("screen_share_changed",e=>this.emit("screen_share_changed",e)))}async submitQualityAndCleanup(e,t=!1){let i=this.webRTC,n={end_reason:e};this.callStartedAt!=null&&(n.duration_seconds=Math.max(0,(Date.now()-this.callStartedAt)/1e3));try{if(i){let s=await i.getCallStats();Object.assign(n,s)}}catch{}if(this.submitCallQuality)try{await this.submitCallQuality(this.conversationId,n,e)}catch(s){this.logger.debug("submitCallQuality failed:",s)}i==null||i.cleanup(),this.webRTC=t?null:B.isAvailable()?new B({}):null,this.webRTCEventsSetup=!1,this.callStartedAt=null,this.callConnectedEmitted=!1}async getCallStats(){let e={};if(this.callStartedAt!=null&&(e.duration_seconds=Math.max(0,(Date.now()-this.callStartedAt)/1e3)),this.webRTC){let t=await this.webRTC.getCallStats();Object.assign(e,t)}return e}cleanup(){this.clearRingTimeout(),this.submitQualityAndCleanup("user_left",!0),this.webRTC=null,this.webRTCEventsSetup=!1,this.callRole=null,this.participants.clear(),this.removeAllListeners()}};function Ct(e,t,i,n){return new Promise((s,a)=>{let r=new XMLHttpRequest;r.open("POST",e);for(let[c,l]of Object.entries(i))r.setRequestHeader(c,l);r.upload.onprogress=c=>{n&&c.lengthComputable&&n(Math.round(c.loaded/c.total*100))},r.onload=()=>{if(r.status>=200&&r.status<300)try{s(r.responseText?JSON.parse(r.responseText):{})}catch{s({})}else{let c=`Upload failed: HTTP ${r.status}`;try{let l=JSON.parse(r.responseText);l!=null&&l.message&&(c=l.message)}catch{}a(new Error(c))}},r.onerror=()=>a(new Error("Upload failed: network error")),r.send(t)})}function St(e){return new Promise((t,i)=>{let n=URL.createObjectURL(e),s=new Image;s.onload=()=>{URL.revokeObjectURL(n),t({width:s.naturalWidth,height:s.naturalHeight})},s.onerror=()=>{URL.revokeObjectURL(n),i(new Error("Failed to load image for dimension probing"))},s.src=n})}function _t(e){return new Promise((t,i)=>{let n=URL.createObjectURL(e),s=document.createElement("video");s.preload="metadata",s.onloadedmetadata=()=>{URL.revokeObjectURL(n),t({width:s.videoWidth,height:s.videoHeight,durationMs:Math.round(s.duration*1e3)})},s.onerror=()=>{URL.revokeObjectURL(n),i(new Error("Failed to load video for metadata probing"))},s.src=n})}var Tt=class extends E.default{constructor(e,t=!1){var n,s,a;super(),this._isJoined=!1;let i=typeof t=="boolean"?{debug:t}:t;this.id=e,this.messageManager=new bt((n=i.debug)!=null?n:!1),this.typingManager=new vt((s=i.debug)!=null?s:!1),this.callManager=new kt(e,{debug:(a=i.debug)!=null?a:!1,getIceServers:i.getIceServers}),this.callManager.on("call_invite",r=>this.emit("call_invite",r)),this.callManager.on("call_accept",r=>this.emit("call_accept",r)),this.callManager.on("call_join",r=>this.emit("call_join",r)),this.callManager.on("call_reject",r=>this.emit("call_reject",r)),this.callManager.on("call_hangup",r=>this.emit("call_hangup",r)),this.callManager.on("peer_left",r=>this.emit("peer_left",r)),this.callManager.on("peer_mute",r=>this.emit("peer_mute",r)),this.callManager.on("peer_unmute",r=>this.emit("peer_unmute",r)),this.callManager.on("peer_video_off",r=>this.emit("peer_video_off",r)),this.callManager.on("peer_video_on",r=>this.emit("peer_video_on",r)),this.callManager.on("local_mute_changed",r=>this.emit("local_mute_changed",r)),this.callManager.on("local_video_changed",r=>this.emit("local_video_changed",r)),this.callManager.on("screen_share_changed",r=>this.emit("screen_share_changed",r)),this.callManager.on("call_connected",r=>this.emit("call_connected",r)),this.callManager.on("call_error",r=>this.emit("call_error",r)),this.callManager.on("call_timeout",r=>this.emit("call_timeout",r)),this.messageManager.on("message",r=>{r.conversationId===this.id&&this.emit("message",r)}),this.messageManager.on("receipt",r=>{this.emit("receipt",r)}),this.typingManager.on("typing",r=>{r.conversationId===this.id&&this.emit("typing",{userId:r.userId,state:r.state})})}get isJoined(){return this._isJoined}setSendMessageHandler(e){this.sendMessageHandler=e}setSendTypingHandler(e){this.sendTypingHandler=e}setSendReadHandler(e){this.sendReadHandler=e}setSendCallHandler(e){this.callManager.setSendCallHandler(e)}setSubmitCallQuality(e){this.callManager.setSubmitCallQuality(e)}setFetchMessagesCallback(e){this.fetchMessagesCallback=e}setUploadAttachmentCallback(e){this.uploadAttachmentCallback=e}async fetchMessages(e){var i;if(!this.fetchMessagesCallback)throw new Error("REST not configured. Set apiUrl in Vocantly config for fetchMessages() (token, API keys, or getRestHeaders).");let t=await this.fetchMessagesCallback({externalUserId:(i=e==null?void 0:e.externalUserId)!=null?i:"",cursor:e==null?void 0:e.cursor,limit:e==null?void 0:e.limit});return this.messageManager.trackMessageIdsFromHistory(this.id,t.messages.map(n=>n.id)),t}markJoined(){this._isJoined||(this._isJoined=!0,this.emit("joined",{conversation_id:this.id}))}markLeft(){this._isJoined&&(this._isJoined=!1,this.callManager.cleanup(),this.emit("left",{conversation_id:this.id}))}sendMessage(e,t="text",i){if(!this.sendMessageHandler){let a=new Error("Conversation is not connected. Call join() first.");throw this.emit("error",a),a}if(!this._isJoined){let a=new Error("Conversation is not joined. Call join() first.");throw this.emit("error",a),a}let n=se();this.messageManager.createOptimisticMessage(this.id,n,e,t,i);let s={type:"message",conversation_id:this.id,payload:{message_id:n,content:e,message_type:t}};try{this.sendMessageHandler(s)}catch(a){let r=a instanceof Error?a:new Error("Failed to send message");throw this.emit("error",r),r}}async sendMedia(e,t){var x,y;if(!this.sendMessageHandler){let d=new Error("Conversation is not connected. Call join() first.");throw this.emit("error",d),d}if(!this._isJoined){let d=new Error("Conversation is not joined. Call join() first.");throw this.emit("error",d),d}if(!this.uploadAttachmentCallback){let d=new Error("Media upload is not configured. Set apiUrl and appId in the Vocantly config to use sendMedia().");throw this.emit("error",d),d}let i=(x=t==null?void 0:t.fileName)!=null?x:e instanceof File?e.name:"upload",n=e.type||"application/octet-stream",s,a,r;if(n.startsWith("image/")){let d=await St(e).catch(()=>{});s=d==null?void 0:d.width,a=d==null?void 0:d.height}else if(n.startsWith("video/")){let d=await _t(e).catch(()=>{});s=d==null?void 0:d.width,a=d==null?void 0:d.height,r=d==null?void 0:d.durationMs}let c;try{c=await this.uploadAttachmentCallback({conversationId:this.id,file:e,fileName:i,onProgress:t==null?void 0:t.onProgress})}catch(d){let M=d instanceof Error?d:new Error("Upload failed");throw this.emit("error",M),M}let l=se(),p=[{bucket:c.bucket,key:c.key,file_name:c.fileName,mime_type:c.mimeType,size_bytes:c.sizeBytes,width:s,height:a,duration_ms:r}],m=(y=t==null?void 0:t.caption)!=null?y:"";this.messageManager.createOptimisticMessage(this.id,l,m,c.category,t==null?void 0:t.metadata,p);let f={type:"message",conversation_id:this.id,payload:{message_id:l,content:m,message_type:c.category,attachments:p}};try{this.sendMessageHandler(f)}catch(d){let M=d instanceof Error?d:new Error("Failed to send media message");throw this.emit("error",M),M}}handleIncomingMessage(e){this.messageManager.handleIncomingMessage(this.id,e)}handleReceipt(e,t,i){this.messageManager.handleReceipt(this.id,e,t,i)}handleTyping(e){e.state==="start"?this.typingManager.handleTypingStart(this.id,e.user_id):e.state==="stop"&&this.typingManager.handleTypingStop(this.id,e.user_id)}emitPresence(e){this.emit("presence",{userId:e.user_id,state:e.state})}clearMessages(){this.messageManager.clearConversation(this.id)}clearTyping(){this.typingManager.clearConversation(this.id)}startCall(e="video"){this.ensureJoinedForCall(),this.callManager.startCall(e)}acceptCall(){this.ensureJoinedForCall(),this.callManager.acceptCall()}joinCall(e){this.ensureJoinedForCall(),this.callManager.joinCall(e!=null?e:"video")}rejectCall(e){this.ensureJoinedForCall(),this.callManager.rejectCall(e)}endCall(e){this.ensureJoinedForCall(),this.callManager.endCall(e)}setMuted(e){this.ensureJoinedForCall(),this.callManager.setMuted(e)}setVideoEnabled(e){this.ensureJoinedForCall(),this.callManager.setVideoEnabled(e)}setCallOptions(e){this.callManager.setCallOptions(e)}listDevices(){return this.callManager.listDevices()}applyVideoConstraints(e){return this.callManager.applyVideoConstraints(e)}applyAudioConstraints(e){return this.callManager.applyAudioConstraints(e)}switchDevice(e,t){return this.callManager.switchDevice(e,t)}setLowBandwidth(e){return this.callManager.setLowBandwidth(e)}startScreenShare(){return this.ensureJoinedForCall(),this.callManager.startScreenShare()}stopScreenShare(){return this.callManager.stopScreenShare()}isScreenSharing(){return this.callManager.isScreenSharing()}getLocalStream(){return this.callManager.getLocalStream()}getRemoteStream(e){return this.callManager.getRemoteStream(e)}getAllRemoteStreams(){return this.callManager.getAllRemoteStreams()}getCallStats(){return this.callManager.getCallStats()}handleIncomingCallMessage(e,t){switch(e){case"call_invite":this.callManager.handleCallInvite(t||{});break;case"call_accept":this.callManager.handleCallAccept(t);break;case"call_join":this.callManager.handleCallJoin(t||{});break;case"call_reject":this.callManager.handleCallReject(t);break;case"call_hangup":this.callManager.handleCallHangup(t);break;case"call_participant_left":this.callManager.handleCallParticipantLeft(t||{});break;case"call_error":this.callManager.handleCallError(t||{});break;case"call_offer":this.callManager.handleCallOffer(t||{});break;case"call_answer":this.callManager.handleCallAnswer(t||{});break;case"call_ice_candidate":this.callManager.handleCallICECandidate(t||{});break;case"call_mute":this.callManager.handleCallMute(t||{});break;case"call_unmute":this.callManager.handleCallUnmute(t||{});break;case"video_off":this.callManager.handleVideoOff(t||{});break;case"video_on":this.callManager.handleVideoOn(t||{});break}}ensureJoinedForCall(){if(!this._isJoined)throw new Error("Conversation is not joined. Call join() first.")}startTyping(){if(!this.sendTypingHandler){let t=new Error("Conversation is not connected. Call join() first.");throw this.emit("error",t),t}if(!this._isJoined){let t=new Error("Conversation is not joined. Call join() first.");throw this.emit("error",t),t}let e={type:"typing_start",conversation_id:this.id};try{this.sendTypingHandler(e)}catch(t){let i=t instanceof Error?t:new Error("Failed to send typing_start");throw this.emit("error",i),i}}stopTyping(){if(!this.sendTypingHandler){let t=new Error("Conversation is not connected. Call join() first.");throw this.emit("error",t),t}if(!this._isJoined){let t=new Error("Conversation is not joined. Call join() first.");throw this.emit("error",t),t}let e={type:"typing_stop",conversation_id:this.id};try{this.sendTypingHandler(e)}catch(t){let i=t instanceof Error?t:new Error("Failed to send typing_stop");throw this.emit("error",i),i}}markRead(e){if(!this.sendReadHandler){let i=new Error("Conversation is not connected. Call join() first.");throw this.emit("error",i),i}if(!this._isJoined){let i=new Error("Conversation is not joined. Call join() first.");throw this.emit("error",i),i}let t={type:"read",conversation_id:this.id,payload:{message_id:e}};try{this.sendReadHandler(t)}catch(i){let n=i instanceof Error?i:new Error("Failed to send read receipt");throw this.emit("error",n),n}}},Mt=class extends E.default{constructor(e,t=!1,i,n,s){super(),this.conversations=new Map,this.restFetcher=null,this.appId=null,this.iceServersCache=null,this.sendMessageHandler=e,this.sendTypingHandler=n,this.sendCallHandler=s,this.supportMessageHandler=i,this.debug=t,this.logger=new H(t)}setRestFetcher(e){if(this.restFetcher=e,e)for(let t of this.conversations.values())t.setSubmitCallQuality((i,n,s)=>this.submitCallQualityToApi(i,n,s))}setAppId(e){this.appId=e&&e.trim()||null,this.iceServersCache=null}getIceServersFetcher(){if(!(!this.restFetcher||!this.appId))return async()=>{var r;if(this.iceServersCache)return this.iceServersCache;let t=`${this.restFetcher.apiUrl.replace(/\/$/,"")}/apps/${encodeURIComponent(this.appId)}/ice-servers`,i=await this.restFetcher.getHeaders(),n=await fetch(t,{method:"GET",headers:i});if(!n.ok)throw new Error(`ICE servers fetch failed: ${n.status}`);let s=await n.json();if(!s.success||!((r=s.data)!=null&&r.iceServers))throw new Error("Invalid ICE servers response");let a=s.data.iceServers;return this.iceServersCache=a,a}}setSupportMessageHandler(e){this.supportMessageHandler=e}setSendTypingHandler(e){this.sendTypingHandler=e;for(let t of this.conversations.values())t.setSendTypingHandler(e)}setSendCallHandler(e){this.sendCallHandler=e;for(let t of this.conversations.values())t.setSendCallHandler(e)}setSendReadHandler(e){this.sendReadHandler=e;for(let t of this.conversations.values())t.setSendReadHandler(e)}async list(e){var y,d,M;if(!this.restFetcher)throw new Error("REST not configured. Set apiUrl in Vocantly config for list() and fetchMessages() (token, API keys, or getRestHeaders).");let t=this.restFetcher.apiUrl.replace(/\/$/,""),i=(y=e==null?void 0:e.externalUserId)==null?void 0:y.trim(),n=Math.min(Math.max(1,(d=e==null?void 0:e.limit)!=null?d:20),100),s=new URLSearchParams({limit:String(n)});i&&s.set("external_user_id",i),e!=null&&e.cursor&&s.set("cursor",e.cursor);let a=s.toString(),r=`${t}/sdk/conversations${a?`?${a}`:""}`,c=await this.restFetcher.getHeaders(),l=await fetch(r,{method:"GET",headers:c});if(!l.ok){let L=await l.text(),O={};try{O=JSON.parse(L)}catch{O={message:L}}throw new Error(O.message||`HTTP ${l.status}: ${L}`)}let p=await l.json();if(!p.success||!p.data)throw new Error(p.message||"Invalid response");let m=p.data,f=Array.isArray(m.conversations)?m.conversations:[],x=(M=m.pagination)!=null?M:{has_next_page:!1,next_cursor:null,limit:n};return{conversations:f,pagination:x}}async getMessages(e,t,i={}){var f;if(!(e!=null&&e.trim()))throw new Error("conversationId is required");if(!this.restFetcher)throw new Error("REST not configured. Set apiUrl in Vocantly config for list() and fetchMessages().");let n=Math.min(Math.max(1,(f=i.limit)!=null?f:20),100),s=new URLSearchParams({limit:String(n)});t!=null&&t.trim()&&s.set("external_user_id",t.trim()),i.cursor&&s.set("cursor",i.cursor);let a=this.restFetcher.apiUrl.replace(/\/$/,""),r=s.toString(),c=`${a}/sdk/conversations/${encodeURIComponent(e.trim())}/messages${r?`?${r}`:""}`,l=await this.restFetcher.getHeaders(),p=await fetch(c,{method:"GET",headers:l});if(!p.ok){let x=await p.text(),y={};try{y=JSON.parse(x)}catch{y={message:x}}throw new Error(y.message||`HTTP ${p.status}: ${x}`)}let m=await p.json();if(!m.success||!m.data)throw new Error(m.message||"Invalid response");return{messages:m.data.messages||[],pagination:m.data.pagination||{has_next_page:!1,next_cursor:null,limit:n}}}async uploadAttachment(e){if(!this.restFetcher)throw new Error("REST not configured. Set apiUrl in Vocantly config for sendMedia().");if(!this.appId)throw new Error("appId is required in Vocantly config for sendMedia().");let i=`${this.restFetcher.apiUrl.replace(/\/$/,"")}/apps/${encodeURIComponent(this.appId)}/uploads`,s={...await this.restFetcher.getHeaders()};delete s["Content-Type"],delete s["content-type"];let a=new FormData;a.append("conversation_id",e.conversationId),a.append("file",e.file,e.fileName);let r=await Ct(i,a,s,e.onProgress);if(!r.success||!r.data)throw new Error(r.message||"Invalid upload response");return{bucket:r.data.bucket,key:r.data.key,category:r.data.category,fileName:r.data.file_name,mimeType:r.data.mime_type,sizeBytes:r.data.size_bytes}}async getAttachmentDownloadUrl(e,t){if(!this.restFetcher)throw new Error("REST not configured. Set apiUrl in Vocantly config for media downloads.");if(!this.appId)throw new Error("appId is required in Vocantly config for media downloads.");let i=this.restFetcher.apiUrl.replace(/\/$/,""),n=new URLSearchParams({bucket:e,key:t}),s=`${i}/apps/${encodeURIComponent(this.appId)}/uploads/url?${n.toString()}`,a=await this.restFetcher.getHeaders(),r=await fetch(s,{method:"GET",headers:a});if(!r.ok){let l=await r.text(),p={};try{p=JSON.parse(l)}catch{p={message:l}}throw new Error(p.message||`HTTP ${r.status}: ${l}`)}let c=await r.json();if(!c.success||!c.data)throw new Error(c.message||"Invalid download URL response");return{url:c.data.url,expiresInSeconds:c.data.expires_in_seconds}}async submitCallQualityToApi(e,t,i){if(!this.restFetcher)return;let s=`${this.restFetcher.apiUrl.replace(/\/$/,"")}/conversations/${encodeURIComponent(e)}/call-quality`,a=await this.restFetcher.getHeaders(),r={end_reason:i};typeof t.duration_seconds=="number"&&(r.duration_seconds=t.duration_seconds),typeof t.packets_lost_in=="number"&&(r.packets_lost_in=t.packets_lost_in),typeof t.packets_lost_out=="number"&&(r.packets_lost_out=t.packets_lost_out),typeof t.packets_received=="number"&&(r.packets_received=t.packets_received),typeof t.packets_sent=="number"&&(r.packets_sent=t.packets_sent),typeof t.bytes_received=="number"&&(r.bytes_received=t.bytes_received),typeof t.bytes_sent=="number"&&(r.bytes_sent=t.bytes_sent),typeof t.jitter_ms=="number"&&(r.jitter_ms=t.jitter_ms),typeof t.rtt_ms=="number"&&(r.rtt_ms=t.rtt_ms);let c=await fetch(s,{method:"POST",headers:{...a,"Content-Type":"application/json"},body:JSON.stringify(r)});if(!c.ok){let l=await c.text();throw this.logger.debug("submitCallQuality failed:",c.status,l),new Error(`Call quality submit failed: ${c.status}`)}}get(e){if(!e||e.length===0)throw new Error("Conversation ID is required");let t=this.conversations.get(e);return t||(t=new Tt(e,{debug:this.debug,getIceServers:this.getIceServersFetcher()}),this.sendMessageHandler&&t.setSendMessageHandler(this.sendMessageHandler),this.sendTypingHandler&&t.setSendTypingHandler(this.sendTypingHandler),this.sendCallHandler&&t.setSendCallHandler(this.sendCallHandler),this.sendReadHandler&&t.setSendReadHandler(this.sendReadHandler),this.conversations.set(e,t),this.logger.debug("Created conversation instance:",e)),this.restFetcher&&(t.setFetchMessagesCallback(i=>this.getMessages(e,i.externalUserId,{cursor:i.cursor,limit:i.limit})),t.setSubmitCallQuality((i,n,s)=>this.submitCallQualityToApi(i,n,s)),this.appId&&t.setUploadAttachmentCallback(i=>this.uploadAttachment(i))),t}has(e){return this.conversations.has(e)}async join(e,t){if(!e||e.length===0)throw new Error("Conversation ID is required");this.get(e),await t(e),this.logger.debug("Join requested for conversation:",e)}async leave(e,t){if(!e||e.length===0)throw new Error("Conversation ID is required");let i=this.conversations.get(e);i?(i.clearTyping(),await t(e),i.markLeft(),this.logger.debug("Left conversation:",e)):(await t(e),this.logger.warn("Attempted to leave unknown conversation:",e))}routeIncomingMessage(e){var i;let t;switch(e.type){case"joined":t=((i=e.payload)==null?void 0:i.conversation_id)||e.conversation_id,t&&this.get(t).markJoined();break;case"message":if(t=e.conversation_id,t){let n=this.conversations.get(t);n?n.isJoined?n.handleIncomingMessage(e.payload):this.logger.warn("Received message for conversation that is not joined:",t):this.logger.warn("Received message for unknown conversation:",t)}break;case"typing":if(t=e.conversation_id,t){let n=this.conversations.get(t);n?n.isJoined?n.handleTyping(e.payload):this.logger.warn("Received typing indicator for conversation that is not joined:",t):this.logger.warn("Received typing indicator for unknown conversation:",t)}break;case"receipt":if(t=e.conversation_id,t){let n=this.conversations.get(t);if(n)if(n.isJoined){let s=e;n.handleReceipt(s.payload.message_id,s.payload.state,s.payload.user_id)}else this.logger.warn("Received receipt for conversation that is not joined:",t);else this.logger.warn("Received receipt for unknown conversation:",t)}break;case"support":break;case"presence":if(t=e.conversation_id,t){let n=this.conversations.get(t);n&&n.isJoined&&n.emitPresence(e.payload)}break;case"error":this.logger.error("Received error message:",e.error);break;case"call_invite":case"call_accept":case"call_join":case"call_reject":case"call_hangup":case"call_participant_left":case"call_error":case"call_offer":case"call_answer":case"call_ice_candidate":case"call_mute":case"call_unmute":case"video_off":case"video_on":if(t=e.conversation_id,t){let n=this.get(t),s="payload"in e?e.payload:void 0;n.handleIncomingCallMessage(e.type,s)}break}}routeSupportMessage(e){var n;if(!this.supportMessageHandler){this.logger.warn("Support message handler not set, ignoring support message");return}let t=e.conversation_id;if(!t){this.logger.warn("Support message missing conversation_id");return}let i=(n=e.payload)==null?void 0:n.event;if(!i){this.logger.warn("Support message missing event type");return}this.supportMessageHandler(t,i,e.payload)}remove(e){let t=this.conversations.get(e);t&&(t.markLeft(),t.clearMessages(),t.clearTyping(),this.conversations.delete(e),this.logger.debug("Removed conversation instance:",e))}getAllIds(){return Array.from(this.conversations.keys())}clear(){for(let e of this.conversations.values())e.markLeft(),e.clearMessages(),e.clearTyping();this.conversations.clear(),this.logger.debug("Cleared all conversations")}},Et=class extends E.default{constructor(e=!1){super(),this.presenceState=new Map,this.logger=new H(e)}handlePresence(e,t){if(!e){this.logger.warn("Invalid presence event: missing userId");return}if(t!=="online"&&t!=="offline"){this.logger.warn("Invalid presence state:",t);return}this.presenceState.get(e)!==t?(this.presenceState.set(e,t),this.emit("presence",{userId:e,state:t}),this.logger.debug("Presence changed:",e,t)):this.logger.debug("Duplicate presence event ignored:",e,t)}getPresence(e){return this.presenceState.get(e)}getPresences(e){let t=new Map;for(let i of e){let n=this.presenceState.get(i);n&&t.set(i,n)}return t}clearPresence(e){let t=this.presenceState.has(e);this.presenceState.delete(e),t&&this.logger.debug("Cleared presence for user:",e)}clear(){this.presenceState.clear(),this.logger.debug("Cleared all presence state")}},Rt=class extends E.default{constructor(e,t,i,n=!1){super(),this.botActive=!1,this.status="queued",this.conversationId=e,this.conversation=t,this.role=i,this.logger=new H(n),this.conversation.on("message",s=>this.emit("message",s)),this.conversation.on("typing",s=>this.emit("typing",s)),this.conversation.on("presence",s=>this.emit("presence",s)),this.conversation.on("joined",s=>this.emit("joined",s)),this.conversation.on("left",s=>this.emit("left",s)),this.conversation.on("error",s=>this.emit("error",s))}getConversation(){return this.conversation}isCustomer(){return this.role==="customer"}isAgent(){return this.role==="agent"}getRole(){return this.role}isBotActive(){return this.botActive}getAssignedAgent(){return this.assignedAgentId}getStatus(){return this.status}handleAssigned(e){if(this.assignedAgentId===e)return;let t=this.assignedAgentId,i=t!==void 0;this.assignedAgentId=e,this.status==="queued"?this.status="assigned":this.status==="assigned"||this.status,i?(this.emit("assigned",{agentId:e,previousAgentId:t}),this.logger.debug("Support session reassigned:",this.conversationId,e,"from",t)):(this.emit("assigned",{agentId:e}),this.logger.debug("Support session assigned:",this.conversationId,e))}handleUnassigned(){if(this.assignedAgentId===void 0)return;let e=this.assignedAgentId;this.assignedAgentId=void 0,(this.status==="assigned"||this.status==="active")&&(this.status="queued"),this.emit("unassigned",{previousAgentId:e}),this.logger.debug("Support session unassigned:",this.conversationId)}handleBotTakeover(){this.botActive||(this.botActive=!0,this.emit("bot_takeover",{}),this.logger.debug("Bot took over support session:",this.conversationId))}handleBotReleased(){this.botActive&&(this.botActive=!1,this.emit("bot_released",{}),this.logger.debug("Bot released support session:",this.conversationId))}handleClosed(){if(this.status==="closed")return;let e=this.status;this.status="closed",this.emit("closed",{previousStatus:e}),this.logger.debug("Support session closed:",this.conversationId)}handleReopened(){this.status==="closed"&&(this.status=this.assignedAgentId?"assigned":"queued",this.emit("reopened",{}),this.logger.debug("Support session reopened:",this.conversationId))}sendMessage(e,t="text",i){this.conversation.sendMessage(e,t,i)}async join(){}async leave(){}},At=class extends E.default{constructor(e,t=!1){super(),this.sessions=new Map,this.getConversationHandler=e,this.debug=t,this.logger=new H(t)}get(e,t="customer"){if(!e||e.length===0)throw new Error("Conversation ID is required");let i=this.sessions.get(e);if(!i){let n=this.getConversationHandler(e);i=new Rt(e,n,t,this.debug),i.on("assigned",s=>this.emit("assigned",{conversationId:e,...s})),i.on("unassigned",s=>this.emit("unassigned",{conversationId:e,...s})),i.on("bot_takeover",()=>this.emit("bot_takeover",{conversationId:e})),i.on("bot_released",()=>this.emit("bot_released",{conversationId:e})),i.on("closed",s=>this.emit("closed",{conversationId:e,...s})),i.on("reopened",()=>this.emit("reopened",{conversationId:e})),this.sessions.set(e,i),this.logger.debug("Created support session:",e,t)}return i}has(e){return this.sessions.has(e)}getSession(e){return this.sessions.get(e)}handleSupportEvent(e,t,i){let n=this.sessions.get(e);if(!n){t==="assigned"&&(i!=null&&i.agent_id)?this.logger.debug("Support event for unknown session, will be handled when session is created:",e):this.logger.warn("Support event for unknown session:",e,t);return}switch(t){case"assigned":i!=null&&i.agent_id&&typeof i.agent_id=="string"&&n.handleAssigned(i.agent_id);break;case"unassigned":n.handleUnassigned();break;case"bot_takeover":n.handleBotTakeover();break;case"bot_released":n.handleBotReleased();break;case"closed":n.handleClosed();break;case"reopened":n.handleReopened();break}}remove(e){this.sessions.get(e)&&(this.sessions.delete(e),this.logger.debug("Removed support session:",e))}clear(){this.sessions.clear(),this.logger.debug("Cleared all support sessions")}},Pe=class extends E.default{constructor(e){var a,r;super(),this.connectionState="disconnected",this.wsClient=null,this.config={autoReconnect:!0,maxReconnectAttempts:5,reconnectDelay:1e3,debug:!1,apiUrl:"",...e},this.tokenManager=new ut,this.tokenManager.setToken(e.token),this.logger=new H(this.config.debug),this.wsClient=new gt(this.config.wsUrl,{autoReconnect:this.config.autoReconnect,maxReconnectAttempts:this.config.maxReconnectAttempts,reconnectDelay:this.config.reconnectDelay,debug:this.config.debug}),this.supportManager=new At(c=>this.conversationManager.get(c),this.config.debug),this.conversationManager=new Mt(c=>{if(!this.wsClient)throw new Error("WebSocket client not initialized");this.wsClient.send(c)},this.config.debug,void 0,c=>{if(!this.wsClient)throw new Error("WebSocket client not initialized");this.wsClient.send(c)},c=>{if(!this.wsClient)throw new Error("WebSocket client not initialized");this.wsClient.send(c)}),this.conversationManager.setSendReadHandler(c=>{if(!this.wsClient)throw new Error("WebSocket client not initialized");this.wsClient.send(c)}),this.presenceManager=new Et(this.config.debug),this.presenceManager.on("presence",c=>{this.emit("presence",c)});let t=(a=this.config.apiUrl)==null?void 0:a.trim(),i=(r=this.config.appId)==null?void 0:r.trim(),n=this.config.apiKey&&this.config.apiSecret,s=typeof this.config.getRestHeaders=="function";if(t){let c=async()=>{if(s&&this.config.getRestHeaders)return this.config.getRestHeaders();if(n)return{"X-Api-Key":this.config.apiKey,"X-Api-Secret":this.config.apiSecret,"Content-Type":"application/json"};let l=this.tokenManager.getToken();if(!l)throw new Error("Token required for list() and fetchMessages() when API keys are not set");return{Authorization:`Bearer ${l}`,"Content-Type":"application/json"}};this.conversationManager.setRestFetcher({apiUrl:t,getHeaders:c}),i&&this.conversationManager.setAppId(i)}this.setupWebSocketHandlers()}setupWebSocketHandlers(){this.wsClient&&(this.wsClient.on("connected",()=>{this.connectionState="connected",this.emit("connected"),this.emit("state",this.connectionState),this.logger.debug("SDK connected")}),this.wsClient.on("disconnected",e=>{this.connectionState="disconnected",this.emit("disconnected",e),this.emit("state",this.connectionState),this.logger.debug("SDK disconnected")}),this.wsClient.on("reconnecting",e=>{this.connectionState="reconnecting",this.emit("reconnecting",e),this.emit("state",this.connectionState),this.logger.debug("SDK reconnecting",e)}),this.wsClient.on("message",e=>{if(this.emit("message",e),this.conversationManager.routeIncomingMessage(e),e.type==="presence"){let t=e;t.payload&&this.presenceManager.handlePresence(t.payload.user_id,t.payload.state)}switch(e.type==="support"&&this.conversationManager.routeSupportMessage(e),e.type){case"joined":this.emit("joined",e);break;case"message":this.emit("message",e);break;case"typing":this.emit("typing",e);break;case"receipt":break;case"support":break;case"presence":break;case"error":this.emit("error",e);break}}),this.wsClient.on("error",e=>{this.connectionState="error";let t=e instanceof Error?e.message:"";(e instanceof Error?e.name:"")==="TokenExpiredError"||t&&t.includes("Token expired")?(this.logger.error("Token expired:",t),this.logger.warn("To reconnect, call sdk.updateToken(newToken) then sdk.connect()")):this.logger.error("SDK error:",e);let n=e instanceof Error?e:new Error("WebSocket connection error");this.emit("error",n),this.emit("state",this.connectionState)}))}getState(){return this.connectionState}getConfig(){return{...this.config}}async connect(){if(this.connectionState==="connected"){this.logger.warn("Already connected");return}if(this.connectionState==="connecting"){this.logger.warn("Connection already in progress");return}this.tokenManager.validateTokenPresence();let e=this.tokenManager.getToken();if(!e)throw new Error("Token is required for connection");this.connectionState="connecting",this.emit("state",this.connectionState),this.logger.debug("Connecting...");try{if(!this.wsClient)throw new Error("WebSocket client not initialized");await this.wsClient.connect(e)}catch(t){this.connectionState="error",this.emit("state",this.connectionState);let i=t instanceof Error?t:new Error("Connection failed");throw this.emit("error",i),i}}async disconnect(){this.connectionState!=="disconnected"&&(this.logger.debug("Disconnecting..."),this.conversationManager.clear(),this.presenceManager.clear(),this.supportManager.clear(),this.wsClient&&this.wsClient.disconnect(),this.connectionState="disconnected",this.emit("disconnected"),this.emit("state",this.connectionState))}isConnected(){var e;return this.connectionState==="connected"&&((e=this.wsClient)==null?void 0:e.isConnected())===!0}updateToken(e){if(!e||e.length===0)throw new Error("Token cannot be empty");this.tokenManager.setToken(e),this.logger.debug("Token updated"),this.isConnected()&&(this.logger.warn("Token updated while connected. Disconnecting to reconnect with new token..."),this.disconnect())}get conversations(){return{get:e=>this.conversationManager.get(e),has:e=>this.conversationManager.has(e),join:e=>this.conversationManager.join(e,t=>this.joinConversation(t)),leave:e=>this.conversationManager.leave(e,t=>this.leaveConversation(t)),list:e=>this.conversationManager.list(e),getAttachmentDownloadUrl:(e,t)=>this.conversationManager.getAttachmentDownloadUrl(e,t)}}get support(){return{get:(e,t="customer")=>this.supportManager.get(e,t),has:e=>this.supportManager.has(e)}}async joinConversation(e){if(!this.isConnected())throw new Error("Must be connected before joining a conversation");if(!e||e.length===0)throw new Error("Conversation ID is required");if(!this.wsClient)throw new Error("WebSocket client not initialized");let t={type:"join",conversation_id:e};try{this.wsClient.send(t),this.logger.debug("Join conversation sent:",e)}catch(i){let n=i instanceof Error?i:new Error("Failed to join conversation");throw this.emit("error",n),n}}async leaveConversation(e){if(!this.isConnected())throw new Error("Must be connected before leaving a conversation");if(!e||e.length===0)throw new Error("Conversation ID is required");if(!this.wsClient)throw new Error("WebSocket client not initialized");let t={type:"leave",conversation_id:e};try{this.wsClient.send(t),this.logger.debug("Leave conversation sent:",e)}catch(i){let n=i instanceof Error?i:new Error("Failed to leave conversation");throw this.emit("error",n),n}}rotateToken(e){this.tokenManager.rotateToken(e),this.logger.debug("Token rotated"),this.isConnected()&&this.logger.warn("Token rotated while connected. Reconnect may be required.")}getTokenManager(){return this.tokenManager}};var Ht={wsUrl:"",token:"",conversationId:"",publicKey:"",title:"Support",subtitle:"We typically reply in a few minutes",greeting:"Hi! \u{1F44B} How can we help you today?",placeholder:"Type your message\u2026",primaryColor:"#195EFF",position:"bottom-right",launcher:"\u{1F4AC}",autoOpen:!1,debug:!1,collectContact:!1,requireEmail:!1,contactPrompt:"Let\u2019s get you connected",csat:!0,csatPrompt:"How would you rate the support you received?",csatThanks:"Thanks for the feedback!",calls:!1,callBundleUrl:"https://cdn.jsdelivr.net/npm/@vocantly/widget@0.1.16/dist/vocantly-widget-call.js"},R=null;function Ne(e){try{return JSON.parse(atob(e.split(".")[1]))}catch{return null}}function Ve(e){let t=Ne(e);return(t==null?void 0:t.user_id)||(t==null?void 0:t.sub)||null}function Ot(e){return typeof e!="number"?"":e<1024?e+" B":e<1024*1024?(e/1024).toFixed(1)+" KB":(e/(1024*1024)).toFixed(1)+" MB"}function A(e){return String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function fe(e){return A(e).replace(/\*\*([^*]+)\*\*/g,"<strong>$1</strong>").replace(/(^|[^*])\*([^*\n]+)\*(?!\*)/g,"$1<em>$2</em>").replace(/`([^`]+)`/g,"<code>$1</code>").replace(/\n/g,"<br>")}var J=null;function Ut(){try{let e=window.AudioContext||window.webkitAudioContext;if(!e)return;J=J||new e,J.resume&&J.resume();let t=J,i=t.currentTime,n=t.createOscillator(),s=t.createGain();n.type="sine",n.frequency.setValueAtTime(880,i),n.frequency.exponentialRampToValueAtTime(1320,i+.08),s.gain.setValueAtTime(1e-4,i),s.gain.exponentialRampToValueAtTime(.16,i+.02),s.gain.exponentialRampToValueAtTime(1e-4,i+.34),n.connect(s).connect(t.destination),n.start(i),n.stop(i+.36)}catch{}}function It(e,t){if(document.getElementById("caf-widget-styles"))return;let a=`
1
+ var VocantlyWidget=(()=>{var gt=Object.create;var ce=Object.defineProperty;var mt=Object.getOwnPropertyDescriptor;var bt=Object.getOwnPropertyNames;var wt=Object.getPrototypeOf,vt=Object.prototype.hasOwnProperty;var F=(e=>typeof require!="undefined"?require:typeof Proxy!="undefined"?new Proxy(e,{get:(t,i)=>(typeof require!="undefined"?require:t)[i]}):e)(function(e){if(typeof require!="undefined")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var Pe=(e,t)=>()=>{try{return t||e((t={exports:{}}).exports,t),t.exports}catch(i){throw t=0,i}},yt=(e,t)=>{for(var i in t)ce(e,i,{get:t[i],enumerable:!0})},Fe=(e,t,i,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of bt(t))!vt.call(e,a)&&a!==i&&ce(e,a,{get:()=>t[a],enumerable:!(n=mt(t,a))||n.enumerable});return e};var be=(e,t,i)=>(i=e!=null?gt(wt(e)):{},Fe(t||!e||!e.__esModule?ce(i,"default",{value:e,enumerable:!0}):i,e)),xt=e=>Fe(ce({},"__esModule",{value:!0}),e);var ze=Pe((ri,we)=>{"use strict";var kt=Object.prototype.hasOwnProperty,A="~";function Q(){}Object.create&&(Q.prototype=Object.create(null),new Q().__proto__||(A=!1));function St(e,t,i){this.fn=e,this.context=t,this.once=i||!1}function Ne(e,t,i,n,a){if(typeof i!="function")throw new TypeError("The listener must be a function");var o=new St(i,n||e,a),r=A?A+t:t;return e._events[r]?e._events[r].fn?e._events[r]=[e._events[r],o]:e._events[r].push(o):(e._events[r]=o,e._eventsCount++),e}function le(e,t){--e._eventsCount===0?e._events=new Q:delete e._events[t]}function E(){this._events=new Q,this._eventsCount=0}E.prototype.eventNames=function(){var t=[],i,n;if(this._eventsCount===0)return t;for(n in i=this._events)kt.call(i,n)&&t.push(A?n.slice(1):n);return Object.getOwnPropertySymbols?t.concat(Object.getOwnPropertySymbols(i)):t};E.prototype.listeners=function(t){var i=A?A+t:t,n=this._events[i];if(!n)return[];if(n.fn)return[n.fn];for(var a=0,o=n.length,r=new Array(o);a<o;a++)r[a]=n[a].fn;return r};E.prototype.listenerCount=function(t){var i=A?A+t:t,n=this._events[i];return n?n.fn?1:n.length:0};E.prototype.emit=function(t,i,n,a,o,r){var l=A?A+t:t;if(!this._events[l])return!1;var d=this._events[l],f=arguments.length,v,b;if(d.fn){switch(d.once&&this.removeListener(t,d.fn,void 0,!0),f){case 1:return d.fn.call(d.context),!0;case 2:return d.fn.call(d.context,i),!0;case 3:return d.fn.call(d.context,i,n),!0;case 4:return d.fn.call(d.context,i,n,a),!0;case 5:return d.fn.call(d.context,i,n,a,o),!0;case 6:return d.fn.call(d.context,i,n,a,o,r),!0}for(b=1,v=new Array(f-1);b<f;b++)v[b-1]=arguments[b];d.fn.apply(d.context,v)}else{var R=d.length,k;for(b=0;b<R;b++)switch(d[b].once&&this.removeListener(t,d[b].fn,void 0,!0),f){case 1:d[b].fn.call(d[b].context);break;case 2:d[b].fn.call(d[b].context,i);break;case 3:d[b].fn.call(d[b].context,i,n);break;case 4:d[b].fn.call(d[b].context,i,n,a);break;default:if(!v)for(k=1,v=new Array(f-1);k<f;k++)v[k-1]=arguments[k];d[b].fn.apply(d[b].context,v)}}return!0};E.prototype.on=function(t,i,n){return Ne(this,t,i,n,!1)};E.prototype.once=function(t,i,n){return Ne(this,t,i,n,!0)};E.prototype.removeListener=function(t,i,n,a){var o=A?A+t:t;if(!this._events[o])return this;if(!i)return le(this,o),this;var r=this._events[o];if(r.fn)r.fn===i&&(!a||r.once)&&(!n||r.context===n)&&le(this,o);else{for(var l=0,d=[],f=r.length;l<f;l++)(r[l].fn!==i||a&&!r[l].once||n&&r[l].context!==n)&&d.push(r[l]);d.length?this._events[o]=d.length===1?d[0]:d:le(this,o)}return this};E.prototype.removeAllListeners=function(t){var i;return t?(i=A?A+t:t,this._events[i]&&le(this,i)):(this._events=new Q,this._eventsCount=0),this};E.prototype.off=E.prototype.removeListener;E.prototype.addListener=E.prototype.on;E.prefixed=A;E.EventEmitter=E;typeof we!="undefined"&&(we.exports=E)});var Be=Pe((mi,We)=>{"use strict";We.exports=function(){throw new Error("ws does not work in the browser. Browser clients must use the native WebSocket object")}});var ni={};yt(ni,{close:()=>ti,destroy:()=>ii,init:()=>Zt,open:()=>ei});var j=be(ze(),1);var M=[];for(de=0;de<256;++de)M.push((de+256).toString(16).slice(1));var de;function Ve(e,t=0){return(M[e[t+0]]+M[e[t+1]]+M[e[t+2]]+M[e[t+3]]+"-"+M[e[t+4]]+M[e[t+5]]+"-"+M[e[t+6]]+M[e[t+7]]+"-"+M[e[t+8]]+M[e[t+9]]+"-"+M[e[t+10]]+M[e[t+11]]+M[e[t+12]]+M[e[t+13]]+M[e[t+14]]+M[e[t+15]]).toLowerCase()}var he,Ct=new Uint8Array(16);function ve(){if(!he&&(he=typeof crypto!="undefined"&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto),!he))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return he(Ct)}var _t=typeof crypto!="undefined"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto),ye={randomUUID:_t};function Tt(e,t,i){if(ye.randomUUID&&!t&&!e)return ye.randomUUID();e=e||{};var n=e.random||(e.rng||ve)();if(n[6]=n[6]&15|64,n[8]=n[8]&63|128,t){i=i||0;for(var a=0;a<16;++a)t[i+a]=n[a];return t}return Ve(n)}var Y=Tt;var Jt={};var Mt=(e=>typeof F!="undefined"?F:typeof Proxy!="undefined"?new Proxy(e,{get:(t,i)=>(typeof F!="undefined"?F:t)[i]}):e)(function(e){if(typeof F!="undefined")return F.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var D=class{constructor(e=!1){this.enabled=e}setEnabled(e){this.enabled=e}debug(...e){this.enabled&&console.debug("[Vocantly SDK]",...e)}info(...e){this.enabled&&console.info("[Vocantly SDK]",...e)}warn(...e){this.enabled&&console.warn("[Vocantly SDK]",...e)}error(...e){this.enabled&&console.error("[Vocantly SDK]",...e)}},xe=typeof process!="undefined"&&process.versions!=null&&process.versions.node!=null,B=null,G=null;async function Et(){return xe?B||G||(G=(async()=>{try{try{return B=await Promise.resolve().then(()=>be(Be(),1)),B}catch(e){if(typeof Mt!="undefined"){let{createRequire:t}=await import("module");return B=t(Jt.url||__filename)("ws"),B}throw e}}catch{return null}finally{G=null}})(),G):null}var Rt=class extends j.default{constructor(e,t={}){var i,n,a;super(),this.ws=null,this.token=null,this.reconnectAttempts=0,this.reconnectTimer=null,this.isManuallyDisconnected=!1,this.isTokenExpired=!1,this.url=e,this.autoReconnect=(i=t.autoReconnect)!=null?i:!0,this.maxReconnectAttempts=(n=t.maxReconnectAttempts)!=null?n:5,this.logger=new D((a=t.debug)!=null?a:!1)}connect(e){if(!e||e.length===0){let t=new Error("Token is required for connection");return this.emit("error",t),Promise.reject(t)}return this.token=e,this.isManuallyDisconnected=!1,this.isTokenExpired=!1,this.attemptConnection()}async attemptConnection(){var i;let e=new URL(this.url),t=(i=this.token)!=null?i:"";if(xe){let n=await Et();if(this.logger.debug("ws module loaded:",!!n),n&&n.WebSocket)return this.logger.debug("Using ws package with Authorization header"),new Promise((a,o)=>{try{let r=new n.WebSocket(e.toString(),{headers:{Authorization:`Bearer ${t}`}});this.ws=r;let l=!1,d=f=>{if(l)return;l=!0;let v=String((f==null?void 0:f.message)||f||"").toLowerCase();if(v.includes("401")||v.includes("unauthorized")||f!=null&&f.code&&String(f.code).includes("401")){this.handleTokenExpiration();let b=new Error("Token expired or invalid. Please refresh your token and reconnect.");b.name="TokenExpiredError",this.emit("error",b),o(b);return}};typeof r.addEventListener=="function"?(r.addEventListener("error",d),r.addEventListener("open",()=>{r.removeEventListener("error",d)})):typeof r.once=="function"&&(r.once("error",d),r.once("open",()=>{})),this.setupEventHandlers(a,o)}catch(r){this.logger.error("Failed to create ws.WebSocket:",r),o(r)}});this.logger.warn("ws package not available, falling back to native WebSocket")}return this.logger.debug("Using native WebSocket with query parameter",{url:e.toString(),hasToken:!!t}),new Promise((n,a)=>{try{if(!t||t.length===0){let l=new Error("Token is required for WebSocket connection");this.logger.error(l.message),a(l);return}let o=new URL(e.toString());o.searchParams.set("token",t),this.ws=new globalThis.WebSocket(o.toString()),this.logger.debug("WebSocket instance created",{readyState:this.ws.readyState,url:this.ws.url,protocol:this.ws.protocol});let r=this.ws;this.setupEventHandlers(n,a),setTimeout(()=>{(r.readyState===WebSocket.CLOSED||r.readyState===WebSocket.CLOSING)&&this.logger.warn("WebSocket closed immediately after creation",{readyState:r.readyState,url:r.url})},100)}catch(o){this.logger.error("Failed to create native WebSocket:",o),a(o)}})}setupEventHandlers(e,t){this.ws&&(this.ws.onopen=()=>{this.reconnectAttempts=0,this.isTokenExpired=!1;let i=this.ws;if(!i){this.logger.error("WebSocket is null in onopen handler"),t(new Error("WebSocket instance lost"));return}if(this.logger.debug("WebSocket connected",{readyState:i.readyState,url:i.url,protocol:i.protocol,extensions:i.extensions}),i.readyState!==WebSocket.OPEN){this.logger.error("WebSocket readyState is not OPEN after onopen",{readyState:i.readyState}),t(new Error(`WebSocket not open: readyState=${i.readyState}`));return}this.emit("connected"),e()},this.ws.onmessage=i=>{try{let n=JSON.parse(i.data);this.logger.debug("Received message:",n),this.emit("message",n)}catch(n){let a=n instanceof Error?n:new Error("Failed to parse message");this.logger.error("Failed to parse message:",a),this.emit("error",a)}},this.ws.onerror=i=>{this.logger.error("WebSocket error:",i);let n;if(i instanceof Error?n=i:i&&i.message?n=i instanceof Error?i:new Error(String(i.message)):n=new Error("WebSocket connection error"),xe&&i&&i.message){let a=String(i.message).toLowerCase();if(a.includes("401")||a.includes("unauthorized")){this.handleTokenExpiration();let o=new Error("Token expired or invalid. Please refresh your token and reconnect.");o.name="TokenExpiredError",this.emit("error",o),t(o);return}}this.emit("error",n)},this.ws.onclose=i=>{if(this.logger.debug("WebSocket closed:",{code:i.code,reason:i.reason,wasClean:i.wasClean}),i.code===1006&&this.logger.warn("Abnormal closure (1006) - connection closed without proper handshake. Possible causes: network issue, server closed connection, CORS issue, or protocol mismatch."),i.code===1008||i.code===1002||i.reason&&i.reason.toLowerCase().includes("unauthorized")||i.reason&&i.reason.toLowerCase().includes("401")){this.handleTokenExpiration();let a=new Error("Token expired or invalid. Please refresh your token and reconnect.");a.name="TokenExpiredError",this.emit("error",a),this.emit("disconnected",{code:i.code,reason:i.reason,tokenExpired:!0});return}this.emit("disconnected",{code:i.code,reason:i.reason}),!this.isManuallyDisconnected&&!this.isTokenExpired&&this.autoReconnect?this.reconnectAttempts<this.maxReconnectAttempts?this.scheduleReconnect():(this.logger.warn("Max reconnection attempts reached"),this.emit("error",new Error("Max reconnection attempts reached"))):this.isTokenExpired&&this.logger.warn("Not reconnecting: token is expired")})}handleTokenExpiration(){this.isTokenExpired=!0,this.autoReconnect=!1,this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null)}disconnect(){this.isManuallyDisconnected=!0,this.autoReconnect=!1,this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.ws&&(this.ws.close(),this.ws=null),this.logger.debug("WebSocket disconnected")}send(e){if(!this.ws||this.ws.readyState!==WebSocket.OPEN){let t=new Error("WebSocket is not connected");throw this.logger.error(t.message),this.emit("error",t),t}try{let t=JSON.stringify(e);this.logger.debug("Sending message:",e),this.ws.send(t)}catch(t){let i=t instanceof Error?t:new Error("Failed to send message");throw this.logger.error("Send error:",i),this.emit("error",i),i}}isConnected(){return this.ws!==null&&this.ws.readyState===WebSocket.OPEN}getReconnectAttempts(){return this.reconnectAttempts}scheduleReconnect(){if(this.reconnectTimer||!this.token)return;this.reconnectAttempts++;let e=[1e3,2e3,5e3,1e4],t=e[Math.min(this.reconnectAttempts-1,e.length-1)];this.logger.debug(`Scheduling reconnect attempt ${this.reconnectAttempts} in ${t}ms`),this.emit("reconnecting",{attempt:this.reconnectAttempts,delay:t}),this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.attemptConnection().catch(()=>{})},t)}},At=class{constructor(){this.token=null}setToken(e){if(!this.isValidFormat(e))throw new Error("Invalid token format");this.token=e}getToken(){return this.token}hasToken(){return this.token!==null&&this.token.length>0}clearToken(){this.token=null}rotateToken(e){if(!this.isValidFormat(e))throw new Error("Invalid token format");this.token=e}isValidFormat(e){return typeof e=="string"&&e.length>0}validateTokenPresence(){if(!this.hasToken())throw new Error("Token is required but not set")}},$t=new Set(["text","system","image","video","file"]);function Je(e){return e&&$t.has(e)?e:"text"}var X=class{static createOptimistic(e,t,i,n="text",a,o){return{id:t,conversationId:e,content:i,messageType:n,optimistic:!0,receiptState:"sent",createdAt:new Date,metadata:a,attachments:o}}static fromServerPayload(e,t){var a,o;let i=(a=t.sender_id)!=null?a:t.senderId,n=(o=t.sender_type)!=null?o:t.senderType;return{id:t.message_id,conversationId:e,content:t.content,senderId:i,senderName:t.sender_name,senderType:n,messageType:Je(t.message_type),optimistic:!1,receiptState:"sent",createdAt:t.sent_at?new Date(t.sent_at):new Date,attachments:t.attachments}}static confirmOptimistic(e,t){var a,o,r,l;let i=(a=t.sender_id)!=null?a:t.senderId,n=(o=t.sender_type)!=null?o:t.senderType;return{...e,optimistic:!1,senderId:i,senderName:t.sender_name,senderType:n,messageType:Je(t.message_type),receiptState:(r=e.receiptState)!=null?r:"sent",createdAt:t.sent_at?new Date(t.sent_at):e.createdAt,attachments:(l=t.attachments)!=null?l:e.attachments}}static updateReceiptState(e,t){return{...e,receiptState:t}}},jt=class extends j.default{constructor(e=!1){super(),this.receiptState=new Map,this.logger=new D(e)}getReceiptState(e){return this.receiptState.get(e)}handleDelivered(e,t){var n;if(!e)return this.logger.warn("Invalid delivered receipt: missing messageId"),!1;let i=(n=this.receiptState.get(e))!=null?n:"sent";return this.compareStates(i,"delivered")<0?(this.receiptState.set(e,"delivered"),this.emit("receipt",{messageId:e,state:"delivered",userId:t}),this.logger.debug("Receipt delivered:",e),!0):(this.logger.debug("Delivered receipt ignored (already delivered or read):",e),!1)}handleRead(e,t){var n;if(!e)return this.logger.warn("Invalid read receipt: missing messageId"),!1;let i=(n=this.receiptState.get(e))!=null?n:"sent";return this.compareStates(i,"read")<0?(this.receiptState.set(e,"read"),this.emit("receipt",{messageId:e,state:"read",userId:t}),this.logger.debug("Receipt read:",e),!0):(this.logger.debug("Read receipt ignored (already read):",e),!1)}compareStates(e,t){let i=["sent","delivered","read"],n=i.indexOf(e),a=i.indexOf(t);return n===-1||a===-1?0:n-a}clearConversation(e,t){let i=0;for(let n of t)this.receiptState.has(n)&&(this.receiptState.delete(n),i++);i>0&&this.logger.debug("Cleared receipt state for conversation:",e,`(${i} messages)`)}clearMessage(e){this.receiptState.delete(e)&&this.logger.debug("Cleared receipt state for message:",e)}clear(){this.receiptState.clear(),this.logger.debug("Cleared all receipt state")}},It=class extends j.default{constructor(e=!1){super(),this.seenMessageIds=new Map,this.optimisticMessages=new Map,this.confirmedMessages=new Map,this.warnedMissingSenderId=new Set,this.logger=new D(e),this.receiptManager=new jt(e),this.receiptManager.on("receipt",t=>{this.updateMessageReceiptState(t)})}createOptimisticMessage(e,t,i,n="text",a,o){let r=X.createOptimistic(e,t,i,n,a,o);return this.trackOptimisticMessage(e,t,r),this.trackMessageId(e,t),this.emit("message",r),this.logger.debug("Created optimistic message:",t),r}handleIncomingMessage(e,t){let i=t.message_id,n=this.getOptimisticMessage(e,i);if(n){let o=this.reconcileOptimisticMessage(n,t);return o.senderId==null&&!this.warnedMissingSenderId.has(e)&&(this.warnedMissingSenderId.add(e),console.warn('[Vocantly SDK] Message payload missing sender_id (and sender_type). Clients cannot show "You" vs other participant. The realtime backend MUST include sender_id and sender_type in every type:"message" payload. See docs/REALTIME_WEBSOCKET_CONTRACT.md.')),this.removeOptimisticMessage(e,i),this.trackMessageId(e,i),this.trackConfirmedMessage(e,i,o),o}if(this.hasSeenMessage(e,i))return this.logger.debug("Duplicate message ignored:",i),null;this.trackMessageId(e,i);let a=X.fromServerPayload(e,t);return a.senderId==null&&!this.warnedMissingSenderId.has(e)&&(this.warnedMissingSenderId.add(e),console.warn('[Vocantly SDK] Message payload missing sender_id (and sender_type). Clients cannot show "You" vs other participant. The realtime backend MUST include sender_id and sender_type in every type:"message" payload. See docs/REALTIME_WEBSOCKET_CONTRACT.md.')),this.trackConfirmedMessage(e,i,a),this.emit("message",a),this.logger.debug("Handled incoming message:",i),a}handleReceipt(e,t,i,n){i==="delivered"?this.receiptManager.handleDelivered(t,n):i==="read"&&this.receiptManager.handleRead(t,n)}updateMessageReceiptState(e){for(let[,t]of this.confirmedMessages.entries()){let i=t.get(e.messageId);if(i){let n=X.updateReceiptState(i,e.state);t.set(e.messageId,n),this.emit("message",n),this.logger.debug("Updated message receipt state:",e.messageId,e.state);return}}for(let[,t]of this.optimisticMessages.entries()){let i=t.get(e.messageId);if(i){let n=X.updateReceiptState(i,e.state);t.set(e.messageId,n),this.emit("message",n),this.logger.debug("Updated optimistic message receipt state:",e.messageId,e.state);return}}this.logger.warn("Receipt for unknown message:",e.messageId)}reconcileOptimisticMessage(e,t){let i=X.confirmOptimistic(e,t);return this.logger.debug("Reconciled optimistic message (no second emit):",e.id),i}hasSeenMessage(e,t){var n;let i=this.seenMessageIds.get(e);return(n=i==null?void 0:i.has(t))!=null?n:!1}trackMessageIdsFromHistory(e,t){for(let i of t)this.trackMessageId(e,i)}trackMessageId(e,t){let i=this.seenMessageIds.get(e);i||(i=new Set,this.seenMessageIds.set(e,i)),i.add(t)}trackOptimisticMessage(e,t,i){let n=this.optimisticMessages.get(e);n||(n=new Map,this.optimisticMessages.set(e,n)),n.set(t,i)}getOptimisticMessage(e,t){let i=this.optimisticMessages.get(e);return i==null?void 0:i.get(t)}removeOptimisticMessage(e,t){let i=this.optimisticMessages.get(e);i&&(i.delete(t),i.size===0&&this.optimisticMessages.delete(e))}trackConfirmedMessage(e,t,i){let n=this.confirmedMessages.get(e);n||(n=new Map,this.confirmedMessages.set(e,n)),n.set(t,i)}getMessageIds(e){let t=this.confirmedMessages.get(e),i=this.optimisticMessages.get(e),n=new Set;if(t)for(let a of t.keys())n.add(a);if(i)for(let a of i.keys())n.add(a);return Array.from(n)}clearConversation(e){this.seenMessageIds.delete(e),this.optimisticMessages.delete(e);let t=this.getMessageIds(e);this.receiptManager.clearConversation(e,t),this.confirmedMessages.delete(e),this.logger.debug("Cleared message tracking for conversation:",e)}clear(){this.seenMessageIds.clear(),this.optimisticMessages.clear(),this.confirmedMessages.clear(),this.receiptManager.clear(),this.logger.debug("Cleared all message tracking")}},Lt=class extends j.default{constructor(e=!1){super(),this.typingTimers=new Map,this.activeTyping=new Map,this.TYPING_TIMEOUT_MS=5e3,this.logger=new D(e)}handleTypingStart(e,t){if(!e||!t){this.logger.warn("Invalid typing_start: missing conversationId or userId");return}let i=this.activeTyping.get(e);i||(i=new Set,this.activeTyping.set(e,i));let n=this.typingTimers.get(e);n||(n=new Map,this.typingTimers.set(e,n));let a=i.has(t),o=n.get(t);o&&clearTimeout(o),i.add(t);let r=setTimeout(()=>{this.handleTypingStop(e,t)},this.TYPING_TIMEOUT_MS);n.set(t,r),a?this.logger.debug("Typing timer restarted:",e,t):(this.emit("typing",{conversationId:e,userId:t,state:"start"}),this.logger.debug("Typing started:",e,t))}handleTypingStop(e,t){if(!e||!t){this.logger.warn("Invalid typing_stop: missing conversationId or userId");return}let i=this.activeTyping.get(e),n=this.typingTimers.get(e);if(!i||!i.has(t)){this.logger.debug("Typing stop ignored (user not typing):",e,t);return}let a=n==null?void 0:n.get(t);a&&(clearTimeout(a),n==null||n.delete(t)),i.delete(t),i.size===0&&this.activeTyping.delete(e),n&&n.size===0&&this.typingTimers.delete(e),this.emit("typing",{conversationId:e,userId:t,state:"stop"}),this.logger.debug("Typing stopped:",e,t)}clearConversation(e){let t=this.activeTyping.get(e),i=this.typingTimers.get(e);if(t){for(let n of t)this.emit("typing",{conversationId:e,userId:n,state:"stop"});this.activeTyping.delete(e)}if(i){for(let n of i.values())clearTimeout(n);this.typingTimers.delete(e)}this.logger.debug("Cleared typing state for conversation:",e)}clear(){for(let e of this.typingTimers.values())for(let t of e.values())clearTimeout(t);this.typingTimers.clear(),this.activeTyping.clear(),this.logger.debug("Cleared all typing state")}getTypingUsers(e){let t=this.activeTyping.get(e);return t?Array.from(t):[]}},Ut=[{urls:"stun:stun.l.google.com:19302"},{urls:"stun:stun1.l.google.com:19302"}],Qe,Ke=typeof RTCPeerConnection!="undefined"&&typeof navigator!="undefined"&&typeof((Qe=navigator.mediaDevices)==null?void 0:Qe.getUserMedia)=="function",Z=class extends j.default{constructor(e={}){var t;super(),this.peers=new Map,this.localStream=null,this.mediaOptions={},this.screenTrack=null,this.cameraTrack=null,this.options=e,this.iceServers=(t=e.iceServers)!=null?t:Ut,this.getIceServers=e.getIceServers,this.iceServersResolved=!e.getIceServers}static isAvailable(){return Ke}static async enumerateDevices(){var e;if(!((e=navigator.mediaDevices)!=null&&e.enumerateDevices))return[];try{return await navigator.mediaDevices.enumerateDevices()}catch{return[]}}setMediaOptions(e){this.mediaOptions={...this.mediaOptions,...e}}async getMedia(e){if(this.localStream)return this.localStream;let t=this.mediaOptions,i={echoCancellation:!0,noiseSuppression:t.noiseSuppression!==!1};t.audioDeviceId&&(i.deviceId={exact:t.audioDeviceId});let n=!1;if(e==="video"){let o={};t.videoDeviceId&&(o.deviceId={exact:t.videoDeviceId}),t.lowBandwidth&&(o.width={ideal:640},o.height={ideal:360},o.frameRate={ideal:15,max:20}),n=Object.keys(o).length?o:!0}let a=await navigator.mediaDevices.getUserMedia({audio:i,video:n});return t.startMuted&&a.getAudioTracks().forEach(o=>o.enabled=!1),t.startVideoOff&&a.getVideoTracks().forEach(o=>o.enabled=!1),this.localStream=a,a}async createPeerConnection(e,t){if(!Ke)return null;if(!this.iceServersResolved&&this.getIceServers)try{this.iceServers=await this.getIceServers(),this.iceServersResolved=!0}catch(a){this.options.debug&&console.warn("Failed to fetch ICE servers, using defaults:",a),this.iceServersResolved=!0}let i=new RTCPeerConnection({iceServers:this.iceServers}),n={pc:i};return this.peers.set(e,n),i.ontrack=a=>{a.streams[0]&&(n.remoteStream=a.streams[0],this.emit("remote_stream",{userId:e,stream:a.streams[0]}))},i.onicecandidate=a=>{a.candidate&&this.emit("ice_candidate",{userId:e,candidate:a.candidate.toJSON()})},i.oniceconnectionstatechange=()=>{if(i.iceConnectionState==="connected"||i.iceConnectionState==="completed")this.mediaOptions.lowBandwidth&&this.setVideoBitrate(35e4),this.emit("peer_connected",{userId:e});else if(i.iceConnectionState==="failed"){let a=this.peers.get(e);a&&!a.hasRestartedIce&&typeof i.restartIce=="function"?(a.hasRestartedIce=!0,i.restartIce()):this.emit("ice_failed",{userId:e})}},i}async createOffer(e,t){let i=await this.getMedia(t),n=await this.createPeerConnection(e,t);if(!n)return null;i.getTracks().forEach(o=>n.addTrack(o,i));let a=await n.createOffer();return await n.setLocalDescription(a),a}async handleOffer(e,t,i){let n=await this.getMedia(i),a=await this.createPeerConnection(e,i);if(!a)return null;n.getTracks().forEach(r=>a.addTrack(r,n)),await a.setRemoteDescription(new RTCSessionDescription(t));let o=await a.createAnswer();return await a.setLocalDescription(o),o}async handleAnswer(e,t){let i=this.peers.get(e);i&&await i.pc.setRemoteDescription(new RTCSessionDescription(t))}async addIceCandidate(e,t){let i=this.peers.get(e);if(i)try{await i.pc.addIceCandidate(new RTCIceCandidate(t))}catch(n){this.options.debug&&console.warn("addIceCandidate failed:",n)}}getLocalStream(){return this.localStream}getRemoteStream(e){var t;return(t=this.peers.get(e))==null?void 0:t.remoteStream}getAllRemoteStreams(){let e=new Map;for(let[t,i]of this.peers)i.remoteStream&&e.set(t,i.remoteStream);return e}async applyVideoConstraints(e){var i;let t=(i=this.localStream)==null?void 0:i.getVideoTracks()[0];if(t)try{await t.applyConstraints(e)}catch{}}async applyAudioConstraints(e){var i;let t=(i=this.localStream)==null?void 0:i.getAudioTracks()[0];if(t)try{await t.applyConstraints(e)}catch{}}async setVideoBitrate(e){for(let[,t]of this.peers){let i=t.pc.getSenders().find(a=>{var o;return((o=a.track)==null?void 0:o.kind)==="video"});if(!i)continue;let n=i.getParameters();(!n.encodings||n.encodings.length===0)&&(n.encodings=[{}]);for(let a of n.encodings)e==null?delete a.maxBitrate:a.maxBitrate=e;try{await i.setParameters(n)}catch{}}}async setLowBandwidth(e){this.mediaOptions.lowBandwidth=e,await this.applyVideoConstraints(e?{width:{max:640},height:{max:360},frameRate:{max:20}}:{width:{ideal:1280},height:{ideal:720},frameRate:{ideal:30}}),await this.setVideoBitrate(e?35e4:null)}async switchDevice(e,t){if(!this.localStream)return;let i=e==="video"?{video:{deviceId:{exact:t}}}:{audio:{deviceId:{exact:t}}},n=await navigator.mediaDevices.getUserMedia(i),a=e==="video"?n.getVideoTracks()[0]:n.getAudioTracks()[0];if(!a)return;let o=e==="video"?this.localStream.getVideoTracks()[0]:this.localStream.getAudioTracks()[0];o&&(a.enabled=o.enabled);for(let[,r]of this.peers){let l=r.pc.getSenders().find(d=>{var f;return((f=d.track)==null?void 0:f.kind)===e});l&&await l.replaceTrack(a)}o&&(this.localStream.removeTrack(o),o.stop()),this.localStream.addTrack(a)}isScreenSharing(){return!!this.screenTrack}async startScreenShare(){var a,o;if(this.screenTrack)return!0;let e=navigator.mediaDevices;if(!e.getDisplayMedia)return!1;let t=await e.getDisplayMedia({video:!0,audio:!1}),i=t.getVideoTracks()[0];if(!i)return!1;this.cameraTrack=(o=(a=this.localStream)==null?void 0:a.getVideoTracks()[0])!=null?o:null;let n=!1;for(let[,r]of this.peers){let l=r.pc.getSenders().find(d=>{var f;return((f=d.track)==null?void 0:f.kind)==="video"});l&&(await l.replaceTrack(i),n=!0)}return n?(this.screenTrack=i,i.onended=()=>{this.stopScreenShare()},this.emit("screen_share_changed",{active:!0,stream:t}),!0):(i.stop(),!1)}async stopScreenShare(){var e;if(this.screenTrack){for(let[,t]of this.peers){let i=t.pc.getSenders().find(n=>{var a;return((a=n.track)==null?void 0:a.kind)==="video"||n.track===this.screenTrack});i&&await i.replaceTrack((e=this.cameraTrack)!=null?e:null)}this.screenTrack.stop(),this.screenTrack=null,this.emit("screen_share_changed",{active:!1})}}closePeer(e){let t=this.peers.get(e);t&&t.pc.signalingState!=="closed"&&t.pc.close(),this.peers.delete(e)}cleanup(){for(let[e]of this.peers)this.closePeer(e);this.localStream&&(this.localStream.getTracks().forEach(e=>e.stop()),this.localStream=null),this.removeAllListeners()}async getCallStats(){let e={};for(let[,t]of this.peers)try{(await t.pc.getStats()).forEach(n=>{var o,r,l,d,f,v,b,R,k,u;let a=n;if(n.type==="inbound-rtp")e.packets_lost_in=((o=e.packets_lost_in)!=null?o:0)+((r=a.packetsLost)!=null?r:0),e.packets_received=((l=e.packets_received)!=null?l:0)+((d=a.packetsReceived)!=null?d:0),e.bytes_received=((f=e.bytes_received)!=null?f:0)+((v=a.bytesReceived)!=null?v:0),typeof a.jitter=="number"&&(e.jitter_ms=a.jitter*1e3);else if(n.type==="outbound-rtp")e.packets_sent=((b=e.packets_sent)!=null?b:0)+((R=a.packetsSent)!=null?R:0),e.bytes_sent=((k=e.bytes_sent)!=null?k:0)+((u=a.bytesSent)!=null?u:0);else if(n.type==="candidate-pair"&&n.state==="succeeded"){let C=n.currentRoundTripTime;typeof C=="number"&&(e.rtt_ms=C*1e3)}})}catch{}return e}setLocalAudioEnabled(e){this.localStream&&this.localStream.getAudioTracks().forEach(t=>t.enabled=e)}setLocalVideoEnabled(e){this.localStream&&this.localStream.getVideoTracks().forEach(t=>t.enabled=e)}},Ot=6e4,Ht=class extends j.default{constructor(e,t={}){var i,n;super(),this.ringTimeoutId=null,this.webRTC=null,this.callRole=null,this.currentCallType="video",this.participants=new Set,this.webRTCEventsSetup=!1,this.callStartedAt=null,this.callConnectedEmitted=!1,this.conversationId=e,this.logger=new D((i=t.debug)!=null?i:!1),this.ringTimeoutMs=(n=t.ringTimeout)!=null?n:Ot,this.submitCallQuality=t.submitCallQuality,Z.isAvailable()&&(this.webRTC=new Z({debug:t.debug,getIceServers:t.getIceServers}))}emitCallConnected(e,t,i){this.callConnectedEmitted||(this.callConnectedEmitted=!0,this.callStartedAt==null&&(this.callStartedAt=Date.now()),this.emit("call_connected",{localStream:e,remoteStream:t,remoteUserId:i}))}setSendCallHandler(e){this.sendCallHandler=e}setCallOptions(e){var t;(t=this.webRTC)==null||t.setMediaOptions(e)}listDevices(){return Z.enumerateDevices()}applyVideoConstraints(e){return this.webRTC?this.webRTC.applyVideoConstraints(e):Promise.resolve()}applyAudioConstraints(e){return this.webRTC?this.webRTC.applyAudioConstraints(e):Promise.resolve()}switchDevice(e,t){return this.webRTC?this.webRTC.switchDevice(e,t):Promise.resolve()}setLowBandwidth(e){return this.webRTC?this.webRTC.setLowBandwidth(e):Promise.resolve()}startScreenShare(){return this.webRTC?this.webRTC.startScreenShare():Promise.resolve(!1)}stopScreenShare(){return this.webRTC?this.webRTC.stopScreenShare():Promise.resolve()}isScreenSharing(){var e,t;return(t=(e=this.webRTC)==null?void 0:e.isScreenSharing())!=null?t:!1}setSubmitCallQuality(e){this.submitCallQuality=e}send(e){if(!this.sendCallHandler){let t=new Error("Call not connected. Join conversation first.");throw this.emit("call_error",{reason:"websocket_lost",message:t.message}),t}this.sendCallHandler(e)}clearRingTimeout(){this.ringTimeoutId&&(clearTimeout(this.ringTimeoutId),this.ringTimeoutId=null)}startCall(e="video"){this.clearRingTimeout(),this.callConnectedEmitted=!1,this.callRole="caller",this.currentCallType=e;let t={type:"call_invite",conversation_id:this.conversationId,payload:{call_type:e}};this.send(t),this.logger.debug("call.invite sent",{conversation_id:this.conversationId,call_type:e}),this.ringTimeoutId=setTimeout(()=>{this.ringTimeoutId=null,this.logger.debug("call ring timeout - no answer"),this.endCall("no_answer"),this.emit("call_timeout",{reason:"no_answer"})},this.ringTimeoutMs)}acceptCall(){this.clearRingTimeout(),this.callConnectedEmitted=!1,this.callRole="callee";let e={type:"call_accept",conversation_id:this.conversationId};this.send(e),this.logger.debug("call.accept sent",{conversation_id:this.conversationId})}joinCall(e="video"){this.callRole="joiner",this.currentCallType=e;let t={type:"call_join",conversation_id:this.conversationId};this.send(t),this.logger.debug("call.join sent",{conversation_id:this.conversationId})}rejectCall(e){this.clearRingTimeout();let t={type:"call_reject",conversation_id:this.conversationId,payload:e?{reason:e}:void 0};this.send(t),this.logger.debug("call.reject sent",{conversation_id:this.conversationId,reason:e})}endCall(e){this.clearRingTimeout();let t={type:"call_hangup",conversation_id:this.conversationId,payload:e?{reason:e}:void 0};this.send(t),this.logger.debug("call.hangup sent",{conversation_id:this.conversationId,reason:e})}handleCallInvite(e){this.currentCallType=e.call_type||"video",this.emit("call_invite",{callType:this.currentCallType,callerId:e.caller_id,callerName:e.caller_name})}async handleCallAccept(e){this.clearRingTimeout();let t=e==null?void 0:e.sender_id;if(this.participants.add(t!=null?t:"unknown"),this.webRTC&&t&&this.callRole==="caller")try{let i=await this.webRTC.createOffer(t,this.currentCallType);i&&(this.sendOffer(t,i),this.setupWebRTCEvents())}catch(i){this.logger.debug("WebRTC createOffer failed:",i),this.emit("call_error",{reason:"permission_denied",message:String(i)})}this.emit("call_accept",{accepterId:t})}async handleCallJoin(e){let t=e.joiner_id;if(t&&this.participants.add(t),this.webRTC&&t&&(this.callRole==="caller"||this.callRole==="callee"))try{let i=await this.webRTC.createOffer(t,this.currentCallType);i&&(this.sendOffer(t,i),this.setupWebRTCEvents())}catch(i){this.logger.debug("WebRTC createOffer for joiner failed:",i)}this.emit("call_join",{joinerId:e.joiner_id,joinerName:e.joiner_name})}handleCallReject(e){this.clearRingTimeout(),this.emit("call_reject",{reason:e==null?void 0:e.reason})}async handleCallHangup(e){var i;this.clearRingTimeout();let t=(i=e==null?void 0:e.reason)!=null?i:"user_hangup";await this.submitQualityAndCleanup(t),this.callRole=null,this.participants.clear(),this.emit("call_hangup",{endedBy:e==null?void 0:e.ended_by,reason:t})}handleCallParticipantLeft(e){var i;let t=e.left_user_id;t&&(this.participants.delete(t),(i=this.webRTC)==null||i.closePeer(t)),this.emit("peer_left",{userId:t})}handleCallError(e){this.clearRingTimeout(),this.emit("call_error",{code:e.code,message:e.message,details:e.details})}async handleCallOffer(e){var n;let t=(n=e.from_user_id)!=null?n:e.sender_id,i=e.sdp;if(!(!t||!i)){if(this.participants.add(t),this.webRTC&&(this.callRole==="callee"||this.callRole==="joiner"))try{this.setupWebRTCEvents();let a=await this.webRTC.handleOffer(t,i,this.currentCallType);if(a){this.sendAnswer(t,a);let o=this.webRTC.getLocalStream(),r=this.webRTC.getRemoteStream(t);o&&r&&this.emitCallConnected(o,r,t)}}catch(a){this.logger.debug("WebRTC handleOffer failed:",a),this.emit("call_error",{reason:"permission_denied",message:String(a)})}this.emit("call_offer",{sdp:i,fromUserId:t})}}async handleCallAnswer(e){var n;let t=(n=e.from_user_id)!=null?n:e.sender_id,i=e.sdp;if(!(!t||!i)){if(this.webRTC){await this.webRTC.handleAnswer(t,i);let a=this.webRTC.getLocalStream(),o=this.webRTC.getRemoteStream(t);a&&o&&this.emitCallConnected(a,o,t)}this.emit("call_answer",{sdp:i,fromUserId:t})}}async handleCallICECandidate(e){var n;let t=(n=e.from_user_id)!=null?n:e.sender_id,i=e.candidate;!t||!i||(this.webRTC&&await this.webRTC.addIceCandidate(t,i),this.emit("call_ice_candidate",{candidate:i,fromUserId:t}))}handleCallMute(e){this.emit("peer_mute",{userId:e==null?void 0:e.user_id})}handleCallUnmute(e){this.emit("peer_unmute",{userId:e==null?void 0:e.user_id})}handleVideoOff(e){this.emit("peer_video_off",{userId:e==null?void 0:e.user_id})}handleVideoOn(e){this.emit("peer_video_on",{userId:e==null?void 0:e.user_id})}setMuted(e){var i;(i=this.webRTC)==null||i.setLocalAudioEnabled(!e);let t=e?{type:"call_mute",conversation_id:this.conversationId}:{type:"call_unmute",conversation_id:this.conversationId};this.send(t),this.emit("local_mute_changed",{muted:e})}setVideoEnabled(e){var i;(i=this.webRTC)==null||i.setLocalVideoEnabled(e);let t=e?{type:"video_on",conversation_id:this.conversationId}:{type:"video_off",conversation_id:this.conversationId};this.send(t),this.emit("local_video_changed",{enabled:e})}getLocalStream(){var e,t;return(t=(e=this.webRTC)==null?void 0:e.getLocalStream())!=null?t:null}getRemoteStream(e){var t;return(t=this.webRTC)==null?void 0:t.getRemoteStream(e)}getAllRemoteStreams(){var e,t;return(t=(e=this.webRTC)==null?void 0:e.getAllRemoteStreams())!=null?t:new Map}sendOffer(e,t){let i={sdp:t,target_user_id:this.participants.size>2?e:void 0};this.send({type:"call_offer",conversation_id:this.conversationId,payload:i})}sendAnswer(e,t){let i={sdp:t,target_user_id:this.participants.size>2?e:void 0};this.send({type:"call_answer",conversation_id:this.conversationId,payload:i})}setupWebRTCEvents(){!this.webRTC||this.webRTCEventsSetup||(this.webRTCEventsSetup=!0,this.webRTC.on("ice_candidate",({userId:e,candidate:t})=>{let i={candidate:t,target_user_id:this.participants.size>2?e:void 0};this.send({type:"call_ice_candidate",conversation_id:this.conversationId,payload:i})}),this.webRTC.on("remote_stream",({userId:e,stream:t})=>{var n;let i=(n=this.webRTC)==null?void 0:n.getLocalStream();i&&this.emitCallConnected(i,t,e)}),this.webRTC.on("ice_failed",async()=>{await this.submitQualityAndCleanup("ice_failed"),this.emit("call_error",{reason:"ice_failed",message:"ICE connection failed"})}),this.webRTC.on("screen_share_changed",e=>this.emit("screen_share_changed",e)))}async submitQualityAndCleanup(e,t=!1){let i=this.webRTC,n={end_reason:e};this.callStartedAt!=null&&(n.duration_seconds=Math.max(0,(Date.now()-this.callStartedAt)/1e3));try{if(i){let a=await i.getCallStats();Object.assign(n,a)}}catch{}if(this.submitCallQuality)try{await this.submitCallQuality(this.conversationId,n,e)}catch(a){this.logger.debug("submitCallQuality failed:",a)}i==null||i.cleanup(),this.webRTC=t?null:Z.isAvailable()?new Z({}):null,this.webRTCEventsSetup=!1,this.callStartedAt=null,this.callConnectedEmitted=!1}async getCallStats(){let e={};if(this.callStartedAt!=null&&(e.duration_seconds=Math.max(0,(Date.now()-this.callStartedAt)/1e3)),this.webRTC){let t=await this.webRTC.getCallStats();Object.assign(e,t)}return e}cleanup(){this.clearRingTimeout(),this.submitQualityAndCleanup("user_left",!0),this.webRTC=null,this.webRTCEventsSetup=!1,this.callRole=null,this.participants.clear(),this.removeAllListeners()}};function Dt(e,t,i,n){return new Promise((a,o)=>{let r=new XMLHttpRequest;r.open("POST",e);for(let[l,d]of Object.entries(i))r.setRequestHeader(l,d);r.upload.onprogress=l=>{n&&l.lengthComputable&&n(Math.round(l.loaded/l.total*100))},r.onload=()=>{if(r.status>=200&&r.status<300)try{a(r.responseText?JSON.parse(r.responseText):{})}catch{a({})}else{let l=`Upload failed: HTTP ${r.status}`;try{let d=JSON.parse(r.responseText);d!=null&&d.message&&(l=d.message)}catch{}o(new Error(l))}},r.onerror=()=>o(new Error("Upload failed: network error")),r.send(t)})}function qt(e){return new Promise((t,i)=>{let n=URL.createObjectURL(e),a=new Image;a.onload=()=>{URL.revokeObjectURL(n),t({width:a.naturalWidth,height:a.naturalHeight})},a.onerror=()=>{URL.revokeObjectURL(n),i(new Error("Failed to load image for dimension probing"))},a.src=n})}function Pt(e){return new Promise((t,i)=>{let n=URL.createObjectURL(e),a=document.createElement("video");a.preload="metadata",a.onloadedmetadata=()=>{URL.revokeObjectURL(n),t({width:a.videoWidth,height:a.videoHeight,durationMs:Math.round(a.duration*1e3)})},a.onerror=()=>{URL.revokeObjectURL(n),i(new Error("Failed to load video for metadata probing"))},a.src=n})}var Ft=class extends j.default{constructor(e,t=!1){var n,a,o;super(),this._isJoined=!1;let i=typeof t=="boolean"?{debug:t}:t;this.id=e,this.messageManager=new It((n=i.debug)!=null?n:!1),this.typingManager=new Lt((a=i.debug)!=null?a:!1),this.callManager=new Ht(e,{debug:(o=i.debug)!=null?o:!1,getIceServers:i.getIceServers}),this.callManager.on("call_invite",r=>this.emit("call_invite",r)),this.callManager.on("call_accept",r=>this.emit("call_accept",r)),this.callManager.on("call_join",r=>this.emit("call_join",r)),this.callManager.on("call_reject",r=>this.emit("call_reject",r)),this.callManager.on("call_hangup",r=>this.emit("call_hangup",r)),this.callManager.on("peer_left",r=>this.emit("peer_left",r)),this.callManager.on("peer_mute",r=>this.emit("peer_mute",r)),this.callManager.on("peer_unmute",r=>this.emit("peer_unmute",r)),this.callManager.on("peer_video_off",r=>this.emit("peer_video_off",r)),this.callManager.on("peer_video_on",r=>this.emit("peer_video_on",r)),this.callManager.on("local_mute_changed",r=>this.emit("local_mute_changed",r)),this.callManager.on("local_video_changed",r=>this.emit("local_video_changed",r)),this.callManager.on("screen_share_changed",r=>this.emit("screen_share_changed",r)),this.callManager.on("call_connected",r=>this.emit("call_connected",r)),this.callManager.on("call_error",r=>this.emit("call_error",r)),this.callManager.on("call_timeout",r=>this.emit("call_timeout",r)),this.messageManager.on("message",r=>{r.conversationId===this.id&&this.emit("message",r)}),this.messageManager.on("receipt",r=>{this.emit("receipt",r)}),this.typingManager.on("typing",r=>{r.conversationId===this.id&&this.emit("typing",{userId:r.userId,state:r.state})})}get isJoined(){return this._isJoined}setSendMessageHandler(e){this.sendMessageHandler=e}setSendTypingHandler(e){this.sendTypingHandler=e}setSendReadHandler(e){this.sendReadHandler=e}setSendCallHandler(e){this.callManager.setSendCallHandler(e)}setSubmitCallQuality(e){this.callManager.setSubmitCallQuality(e)}setFetchMessagesCallback(e){this.fetchMessagesCallback=e}setUploadAttachmentCallback(e){this.uploadAttachmentCallback=e}async fetchMessages(e){var i;if(!this.fetchMessagesCallback)throw new Error("REST not configured. Set apiUrl in Vocantly config for fetchMessages() (token, API keys, or getRestHeaders).");let t=await this.fetchMessagesCallback({externalUserId:(i=e==null?void 0:e.externalUserId)!=null?i:"",cursor:e==null?void 0:e.cursor,limit:e==null?void 0:e.limit});return this.messageManager.trackMessageIdsFromHistory(this.id,t.messages.map(n=>n.id)),t}markJoined(){this._isJoined||(this._isJoined=!0,this.emit("joined",{conversation_id:this.id}))}markLeft(){this._isJoined&&(this._isJoined=!1,this.callManager.cleanup(),this.emit("left",{conversation_id:this.id}))}sendMessage(e,t="text",i){if(!this.sendMessageHandler){let o=new Error("Conversation is not connected. Call join() first.");throw this.emit("error",o),o}if(!this._isJoined){let o=new Error("Conversation is not joined. Call join() first.");throw this.emit("error",o),o}let n=Y();this.messageManager.createOptimisticMessage(this.id,n,e,t,i);let a={type:"message",conversation_id:this.id,payload:{message_id:n,content:e,message_type:t}};try{this.sendMessageHandler(a)}catch(o){let r=o instanceof Error?o:new Error("Failed to send message");throw this.emit("error",r),r}}async sendMedia(e,t){var R,k;if(!this.sendMessageHandler){let u=new Error("Conversation is not connected. Call join() first.");throw this.emit("error",u),u}if(!this._isJoined){let u=new Error("Conversation is not joined. Call join() first.");throw this.emit("error",u),u}if(!this.uploadAttachmentCallback){let u=new Error("Media upload is not configured. Set apiUrl and appId in the Vocantly config to use sendMedia().");throw this.emit("error",u),u}let i=(R=t==null?void 0:t.fileName)!=null?R:e instanceof File?e.name:"upload",n=e.type||"application/octet-stream",a,o,r;if(n.startsWith("image/")){let u=await qt(e).catch(()=>{});a=u==null?void 0:u.width,o=u==null?void 0:u.height}else if(n.startsWith("video/")){let u=await Pt(e).catch(()=>{});a=u==null?void 0:u.width,o=u==null?void 0:u.height,r=u==null?void 0:u.durationMs}let l;try{l=await this.uploadAttachmentCallback({conversationId:this.id,file:e,fileName:i,onProgress:t==null?void 0:t.onProgress})}catch(u){let C=u instanceof Error?u:new Error("Upload failed");throw this.emit("error",C),C}let d=Y(),f=[{bucket:l.bucket,key:l.key,file_name:l.fileName,mime_type:l.mimeType,size_bytes:l.sizeBytes,width:a,height:o,duration_ms:r}],v=(k=t==null?void 0:t.caption)!=null?k:"";this.messageManager.createOptimisticMessage(this.id,d,v,l.category,t==null?void 0:t.metadata,f);let b={type:"message",conversation_id:this.id,payload:{message_id:d,content:v,message_type:l.category,attachments:f}};try{this.sendMessageHandler(b)}catch(u){let C=u instanceof Error?u:new Error("Failed to send media message");throw this.emit("error",C),C}}handleIncomingMessage(e){this.messageManager.handleIncomingMessage(this.id,e)}handleReceipt(e,t,i){this.messageManager.handleReceipt(this.id,e,t,i)}handleTyping(e){e.state==="start"?this.typingManager.handleTypingStart(this.id,e.user_id):e.state==="stop"&&this.typingManager.handleTypingStop(this.id,e.user_id)}emitPresence(e){this.emit("presence",{userId:e.user_id,state:e.state})}clearMessages(){this.messageManager.clearConversation(this.id)}clearTyping(){this.typingManager.clearConversation(this.id)}startCall(e="video"){this.ensureJoinedForCall(),this.callManager.startCall(e)}acceptCall(){this.ensureJoinedForCall(),this.callManager.acceptCall()}joinCall(e){this.ensureJoinedForCall(),this.callManager.joinCall(e!=null?e:"video")}rejectCall(e){this.ensureJoinedForCall(),this.callManager.rejectCall(e)}endCall(e){this.ensureJoinedForCall(),this.callManager.endCall(e)}setMuted(e){this.ensureJoinedForCall(),this.callManager.setMuted(e)}setVideoEnabled(e){this.ensureJoinedForCall(),this.callManager.setVideoEnabled(e)}setCallOptions(e){this.callManager.setCallOptions(e)}listDevices(){return this.callManager.listDevices()}applyVideoConstraints(e){return this.callManager.applyVideoConstraints(e)}applyAudioConstraints(e){return this.callManager.applyAudioConstraints(e)}switchDevice(e,t){return this.callManager.switchDevice(e,t)}setLowBandwidth(e){return this.callManager.setLowBandwidth(e)}startScreenShare(){return this.ensureJoinedForCall(),this.callManager.startScreenShare()}stopScreenShare(){return this.callManager.stopScreenShare()}isScreenSharing(){return this.callManager.isScreenSharing()}getLocalStream(){return this.callManager.getLocalStream()}getRemoteStream(e){return this.callManager.getRemoteStream(e)}getAllRemoteStreams(){return this.callManager.getAllRemoteStreams()}getCallStats(){return this.callManager.getCallStats()}handleIncomingCallMessage(e,t){switch(e){case"call_invite":this.callManager.handleCallInvite(t||{});break;case"call_accept":this.callManager.handleCallAccept(t);break;case"call_join":this.callManager.handleCallJoin(t||{});break;case"call_reject":this.callManager.handleCallReject(t);break;case"call_hangup":this.callManager.handleCallHangup(t);break;case"call_participant_left":this.callManager.handleCallParticipantLeft(t||{});break;case"call_error":this.callManager.handleCallError(t||{});break;case"call_offer":this.callManager.handleCallOffer(t||{});break;case"call_answer":this.callManager.handleCallAnswer(t||{});break;case"call_ice_candidate":this.callManager.handleCallICECandidate(t||{});break;case"call_mute":this.callManager.handleCallMute(t||{});break;case"call_unmute":this.callManager.handleCallUnmute(t||{});break;case"video_off":this.callManager.handleVideoOff(t||{});break;case"video_on":this.callManager.handleVideoOn(t||{});break}}ensureJoinedForCall(){if(!this._isJoined)throw new Error("Conversation is not joined. Call join() first.")}startTyping(){if(!this.sendTypingHandler){let t=new Error("Conversation is not connected. Call join() first.");throw this.emit("error",t),t}if(!this._isJoined){let t=new Error("Conversation is not joined. Call join() first.");throw this.emit("error",t),t}let e={type:"typing_start",conversation_id:this.id};try{this.sendTypingHandler(e)}catch(t){let i=t instanceof Error?t:new Error("Failed to send typing_start");throw this.emit("error",i),i}}stopTyping(){if(!this.sendTypingHandler){let t=new Error("Conversation is not connected. Call join() first.");throw this.emit("error",t),t}if(!this._isJoined){let t=new Error("Conversation is not joined. Call join() first.");throw this.emit("error",t),t}let e={type:"typing_stop",conversation_id:this.id};try{this.sendTypingHandler(e)}catch(t){let i=t instanceof Error?t:new Error("Failed to send typing_stop");throw this.emit("error",i),i}}markRead(e){if(!this.sendReadHandler){let i=new Error("Conversation is not connected. Call join() first.");throw this.emit("error",i),i}if(!this._isJoined){let i=new Error("Conversation is not joined. Call join() first.");throw this.emit("error",i),i}let t={type:"read",conversation_id:this.id,payload:{message_id:e}};try{this.sendReadHandler(t)}catch(i){let n=i instanceof Error?i:new Error("Failed to send read receipt");throw this.emit("error",n),n}}},Nt=class extends j.default{constructor(e,t=!1,i,n,a){super(),this.conversations=new Map,this.restFetcher=null,this.appId=null,this.iceServersCache=null,this.sendMessageHandler=e,this.sendTypingHandler=n,this.sendCallHandler=a,this.supportMessageHandler=i,this.debug=t,this.logger=new D(t)}setRestFetcher(e){if(this.restFetcher=e,e)for(let t of this.conversations.values())t.setSubmitCallQuality((i,n,a)=>this.submitCallQualityToApi(i,n,a))}setAppId(e){this.appId=e&&e.trim()||null,this.iceServersCache=null}getIceServersFetcher(){if(!(!this.restFetcher||!this.appId))return async()=>{var r;if(this.iceServersCache)return this.iceServersCache;let t=`${this.restFetcher.apiUrl.replace(/\/$/,"")}/apps/${encodeURIComponent(this.appId)}/ice-servers`,i=await this.restFetcher.getHeaders(),n=await fetch(t,{method:"GET",headers:i});if(!n.ok)throw new Error(`ICE servers fetch failed: ${n.status}`);let a=await n.json();if(!a.success||!((r=a.data)!=null&&r.iceServers))throw new Error("Invalid ICE servers response");let o=a.data.iceServers;return this.iceServersCache=o,o}}setSupportMessageHandler(e){this.supportMessageHandler=e}setSendTypingHandler(e){this.sendTypingHandler=e;for(let t of this.conversations.values())t.setSendTypingHandler(e)}setSendCallHandler(e){this.sendCallHandler=e;for(let t of this.conversations.values())t.setSendCallHandler(e)}setSendReadHandler(e){this.sendReadHandler=e;for(let t of this.conversations.values())t.setSendReadHandler(e)}async list(e){var k,u,C;if(!this.restFetcher)throw new Error("REST not configured. Set apiUrl in Vocantly config for list() and fetchMessages() (token, API keys, or getRestHeaders).");let t=this.restFetcher.apiUrl.replace(/\/$/,""),i=(k=e==null?void 0:e.externalUserId)==null?void 0:k.trim(),n=Math.min(Math.max(1,(u=e==null?void 0:e.limit)!=null?u:20),100),a=new URLSearchParams({limit:String(n)});i&&a.set("external_user_id",i),e!=null&&e.cursor&&a.set("cursor",e.cursor);let o=a.toString(),r=`${t}/sdk/conversations${o?`?${o}`:""}`,l=await this.restFetcher.getHeaders(),d=await fetch(r,{method:"GET",headers:l});if(!d.ok){let N=await d.text(),_={};try{_=JSON.parse(N)}catch{_={message:N}}throw new Error(_.message||`HTTP ${d.status}: ${N}`)}let f=await d.json();if(!f.success||!f.data)throw new Error(f.message||"Invalid response");let v=f.data,b=Array.isArray(v.conversations)?v.conversations:[],R=(C=v.pagination)!=null?C:{has_next_page:!1,next_cursor:null,limit:n};return{conversations:b,pagination:R}}async getMessages(e,t,i={}){var b;if(!(e!=null&&e.trim()))throw new Error("conversationId is required");if(!this.restFetcher)throw new Error("REST not configured. Set apiUrl in Vocantly config for list() and fetchMessages().");let n=Math.min(Math.max(1,(b=i.limit)!=null?b:20),100),a=new URLSearchParams({limit:String(n)});t!=null&&t.trim()&&a.set("external_user_id",t.trim()),i.cursor&&a.set("cursor",i.cursor);let o=this.restFetcher.apiUrl.replace(/\/$/,""),r=a.toString(),l=`${o}/sdk/conversations/${encodeURIComponent(e.trim())}/messages${r?`?${r}`:""}`,d=await this.restFetcher.getHeaders(),f=await fetch(l,{method:"GET",headers:d});if(!f.ok){let R=await f.text(),k={};try{k=JSON.parse(R)}catch{k={message:R}}throw new Error(k.message||`HTTP ${f.status}: ${R}`)}let v=await f.json();if(!v.success||!v.data)throw new Error(v.message||"Invalid response");return{messages:v.data.messages||[],pagination:v.data.pagination||{has_next_page:!1,next_cursor:null,limit:n}}}async uploadAttachment(e){if(!this.restFetcher)throw new Error("REST not configured. Set apiUrl in Vocantly config for sendMedia().");if(!this.appId)throw new Error("appId is required in Vocantly config for sendMedia().");let i=`${this.restFetcher.apiUrl.replace(/\/$/,"")}/apps/${encodeURIComponent(this.appId)}/uploads`,a={...await this.restFetcher.getHeaders()};delete a["Content-Type"],delete a["content-type"];let o=new FormData;o.append("conversation_id",e.conversationId),o.append("file",e.file,e.fileName);let r=await Dt(i,o,a,e.onProgress);if(!r.success||!r.data)throw new Error(r.message||"Invalid upload response");return{bucket:r.data.bucket,key:r.data.key,category:r.data.category,fileName:r.data.file_name,mimeType:r.data.mime_type,sizeBytes:r.data.size_bytes}}async getAttachmentDownloadUrl(e,t){if(!this.restFetcher)throw new Error("REST not configured. Set apiUrl in Vocantly config for media downloads.");if(!this.appId)throw new Error("appId is required in Vocantly config for media downloads.");let i=this.restFetcher.apiUrl.replace(/\/$/,""),n=new URLSearchParams({bucket:e,key:t}),a=`${i}/apps/${encodeURIComponent(this.appId)}/uploads/url?${n.toString()}`,o=await this.restFetcher.getHeaders(),r=await fetch(a,{method:"GET",headers:o});if(!r.ok){let d=await r.text(),f={};try{f=JSON.parse(d)}catch{f={message:d}}throw new Error(f.message||`HTTP ${r.status}: ${d}`)}let l=await r.json();if(!l.success||!l.data)throw new Error(l.message||"Invalid download URL response");return{url:l.data.url,expiresInSeconds:l.data.expires_in_seconds}}async submitCallQualityToApi(e,t,i){if(!this.restFetcher)return;let a=`${this.restFetcher.apiUrl.replace(/\/$/,"")}/conversations/${encodeURIComponent(e)}/call-quality`,o=await this.restFetcher.getHeaders(),r={end_reason:i};typeof t.duration_seconds=="number"&&(r.duration_seconds=t.duration_seconds),typeof t.packets_lost_in=="number"&&(r.packets_lost_in=t.packets_lost_in),typeof t.packets_lost_out=="number"&&(r.packets_lost_out=t.packets_lost_out),typeof t.packets_received=="number"&&(r.packets_received=t.packets_received),typeof t.packets_sent=="number"&&(r.packets_sent=t.packets_sent),typeof t.bytes_received=="number"&&(r.bytes_received=t.bytes_received),typeof t.bytes_sent=="number"&&(r.bytes_sent=t.bytes_sent),typeof t.jitter_ms=="number"&&(r.jitter_ms=t.jitter_ms),typeof t.rtt_ms=="number"&&(r.rtt_ms=t.rtt_ms);let l=await fetch(a,{method:"POST",headers:{...o,"Content-Type":"application/json"},body:JSON.stringify(r)});if(!l.ok){let d=await l.text();throw this.logger.debug("submitCallQuality failed:",l.status,d),new Error(`Call quality submit failed: ${l.status}`)}}get(e){if(!e||e.length===0)throw new Error("Conversation ID is required");let t=this.conversations.get(e);return t||(t=new Ft(e,{debug:this.debug,getIceServers:this.getIceServersFetcher()}),this.sendMessageHandler&&t.setSendMessageHandler(this.sendMessageHandler),this.sendTypingHandler&&t.setSendTypingHandler(this.sendTypingHandler),this.sendCallHandler&&t.setSendCallHandler(this.sendCallHandler),this.sendReadHandler&&t.setSendReadHandler(this.sendReadHandler),this.conversations.set(e,t),this.logger.debug("Created conversation instance:",e)),this.restFetcher&&(t.setFetchMessagesCallback(i=>this.getMessages(e,i.externalUserId,{cursor:i.cursor,limit:i.limit})),t.setSubmitCallQuality((i,n,a)=>this.submitCallQualityToApi(i,n,a)),this.appId&&t.setUploadAttachmentCallback(i=>this.uploadAttachment(i))),t}has(e){return this.conversations.has(e)}async join(e,t){if(!e||e.length===0)throw new Error("Conversation ID is required");this.get(e),await t(e),this.logger.debug("Join requested for conversation:",e)}async leave(e,t){if(!e||e.length===0)throw new Error("Conversation ID is required");let i=this.conversations.get(e);i?(i.clearTyping(),await t(e),i.markLeft(),this.logger.debug("Left conversation:",e)):(await t(e),this.logger.warn("Attempted to leave unknown conversation:",e))}routeIncomingMessage(e){var i;let t;switch(e.type){case"joined":t=((i=e.payload)==null?void 0:i.conversation_id)||e.conversation_id,t&&this.get(t).markJoined();break;case"message":if(t=e.conversation_id,t){let n=this.conversations.get(t);n?n.isJoined?n.handleIncomingMessage(e.payload):this.logger.warn("Received message for conversation that is not joined:",t):this.logger.warn("Received message for unknown conversation:",t)}break;case"typing":if(t=e.conversation_id,t){let n=this.conversations.get(t);n?n.isJoined?n.handleTyping(e.payload):this.logger.warn("Received typing indicator for conversation that is not joined:",t):this.logger.warn("Received typing indicator for unknown conversation:",t)}break;case"receipt":if(t=e.conversation_id,t){let n=this.conversations.get(t);if(n)if(n.isJoined){let a=e;n.handleReceipt(a.payload.message_id,a.payload.state,a.payload.user_id)}else this.logger.warn("Received receipt for conversation that is not joined:",t);else this.logger.warn("Received receipt for unknown conversation:",t)}break;case"support":break;case"presence":if(t=e.conversation_id,t){let n=this.conversations.get(t);n&&n.isJoined&&n.emitPresence(e.payload)}break;case"error":this.logger.error("Received error message:",e.error);break;case"call_invite":case"call_accept":case"call_join":case"call_reject":case"call_hangup":case"call_participant_left":case"call_error":case"call_offer":case"call_answer":case"call_ice_candidate":case"call_mute":case"call_unmute":case"video_off":case"video_on":if(t=e.conversation_id,t){let n=this.get(t),a="payload"in e?e.payload:void 0;n.handleIncomingCallMessage(e.type,a)}break}}routeSupportMessage(e){var n;if(!this.supportMessageHandler){this.logger.warn("Support message handler not set, ignoring support message");return}let t=e.conversation_id;if(!t){this.logger.warn("Support message missing conversation_id");return}let i=(n=e.payload)==null?void 0:n.event;if(!i){this.logger.warn("Support message missing event type");return}this.supportMessageHandler(t,i,e.payload)}remove(e){let t=this.conversations.get(e);t&&(t.markLeft(),t.clearMessages(),t.clearTyping(),this.conversations.delete(e),this.logger.debug("Removed conversation instance:",e))}getAllIds(){return Array.from(this.conversations.keys())}clear(){for(let e of this.conversations.values())e.markLeft(),e.clearMessages(),e.clearTyping();this.conversations.clear(),this.logger.debug("Cleared all conversations")}},zt=class extends j.default{constructor(e=!1){super(),this.presenceState=new Map,this.logger=new D(e)}handlePresence(e,t){if(!e){this.logger.warn("Invalid presence event: missing userId");return}if(t!=="online"&&t!=="offline"){this.logger.warn("Invalid presence state:",t);return}this.presenceState.get(e)!==t?(this.presenceState.set(e,t),this.emit("presence",{userId:e,state:t}),this.logger.debug("Presence changed:",e,t)):this.logger.debug("Duplicate presence event ignored:",e,t)}getPresence(e){return this.presenceState.get(e)}getPresences(e){let t=new Map;for(let i of e){let n=this.presenceState.get(i);n&&t.set(i,n)}return t}clearPresence(e){let t=this.presenceState.has(e);this.presenceState.delete(e),t&&this.logger.debug("Cleared presence for user:",e)}clear(){this.presenceState.clear(),this.logger.debug("Cleared all presence state")}},Vt=class extends j.default{constructor(e,t,i,n=!1){super(),this.botActive=!1,this.status="queued",this.conversationId=e,this.conversation=t,this.role=i,this.logger=new D(n),this.conversation.on("message",a=>this.emit("message",a)),this.conversation.on("typing",a=>this.emit("typing",a)),this.conversation.on("presence",a=>this.emit("presence",a)),this.conversation.on("joined",a=>this.emit("joined",a)),this.conversation.on("left",a=>this.emit("left",a)),this.conversation.on("error",a=>this.emit("error",a))}getConversation(){return this.conversation}isCustomer(){return this.role==="customer"}isAgent(){return this.role==="agent"}getRole(){return this.role}isBotActive(){return this.botActive}getAssignedAgent(){return this.assignedAgentId}getStatus(){return this.status}handleAssigned(e){if(this.assignedAgentId===e)return;let t=this.assignedAgentId,i=t!==void 0;this.assignedAgentId=e,this.status==="queued"?this.status="assigned":this.status==="assigned"||this.status,i?(this.emit("assigned",{agentId:e,previousAgentId:t}),this.logger.debug("Support session reassigned:",this.conversationId,e,"from",t)):(this.emit("assigned",{agentId:e}),this.logger.debug("Support session assigned:",this.conversationId,e))}handleUnassigned(){if(this.assignedAgentId===void 0)return;let e=this.assignedAgentId;this.assignedAgentId=void 0,(this.status==="assigned"||this.status==="active")&&(this.status="queued"),this.emit("unassigned",{previousAgentId:e}),this.logger.debug("Support session unassigned:",this.conversationId)}handleBotTakeover(){this.botActive||(this.botActive=!0,this.emit("bot_takeover",{}),this.logger.debug("Bot took over support session:",this.conversationId))}handleBotReleased(){this.botActive&&(this.botActive=!1,this.emit("bot_released",{}),this.logger.debug("Bot released support session:",this.conversationId))}handleClosed(){if(this.status==="closed")return;let e=this.status;this.status="closed",this.emit("closed",{previousStatus:e}),this.logger.debug("Support session closed:",this.conversationId)}handleReopened(){this.status==="closed"&&(this.status=this.assignedAgentId?"assigned":"queued",this.emit("reopened",{}),this.logger.debug("Support session reopened:",this.conversationId))}sendMessage(e,t="text",i){this.conversation.sendMessage(e,t,i)}async join(){}async leave(){}},Wt=class extends j.default{constructor(e,t=!1){super(),this.sessions=new Map,this.getConversationHandler=e,this.debug=t,this.logger=new D(t)}get(e,t="customer"){if(!e||e.length===0)throw new Error("Conversation ID is required");let i=this.sessions.get(e);if(!i){let n=this.getConversationHandler(e);i=new Vt(e,n,t,this.debug),i.on("assigned",a=>this.emit("assigned",{conversationId:e,...a})),i.on("unassigned",a=>this.emit("unassigned",{conversationId:e,...a})),i.on("bot_takeover",()=>this.emit("bot_takeover",{conversationId:e})),i.on("bot_released",()=>this.emit("bot_released",{conversationId:e})),i.on("closed",a=>this.emit("closed",{conversationId:e,...a})),i.on("reopened",()=>this.emit("reopened",{conversationId:e})),this.sessions.set(e,i),this.logger.debug("Created support session:",e,t)}return i}has(e){return this.sessions.has(e)}getSession(e){return this.sessions.get(e)}handleSupportEvent(e,t,i){let n=this.sessions.get(e);if(!n){t==="assigned"&&(i!=null&&i.agent_id)?this.logger.debug("Support event for unknown session, will be handled when session is created:",e):this.logger.warn("Support event for unknown session:",e,t);return}switch(t){case"assigned":i!=null&&i.agent_id&&typeof i.agent_id=="string"&&n.handleAssigned(i.agent_id);break;case"unassigned":n.handleUnassigned();break;case"bot_takeover":n.handleBotTakeover();break;case"bot_released":n.handleBotReleased();break;case"closed":n.handleClosed();break;case"reopened":n.handleReopened();break}}remove(e){this.sessions.get(e)&&(this.sessions.delete(e),this.logger.debug("Removed support session:",e))}clear(){this.sessions.clear(),this.logger.debug("Cleared all support sessions")}},Bt=class{constructor(e,t){this.apiUrl=e,this.publicKey=t}resolveForm(e={}){let t=new URLSearchParams;return Object.entries(e).forEach(([i,n])=>n&&t.set(i,n)),this.request(`/tickets/public/form${t.size?`?${t}`:""}`)}create(e){return this.request("/tickets/public",{method:"POST",body:JSON.stringify(e)})}getStatus(e,t){return this.request(`/tickets/public/${encodeURIComponent(e)}?token=${encodeURIComponent(t)}`,{},!1)}async reply(e,t,i,n=Y()){await this.request(`/tickets/public/${encodeURIComponent(e)}/reply?token=${encodeURIComponent(t)}`,{method:"POST",body:JSON.stringify({body:i,idempotency_key:n})},!1)}async request(e,t={},i=!0){var o,r;let n=await fetch(this.apiUrl.replace(/\/$/,"")+e,{...t,headers:{"Content-Type":"application/json",...i?{"X-Api-Key":this.publicKey}:{},...(o=t.headers)!=null?o:{}}}),a=await n.json().catch(()=>({}));if(!n.ok)throw new Error(a.message||`Ticket request failed (${n.status})`);return(r=a.data)!=null?r:a}},Ye=class extends j.default{constructor(e){var o,r;super(),this.connectionState="disconnected",this.wsClient=null,this.config={autoReconnect:!0,maxReconnectAttempts:5,reconnectDelay:1e3,debug:!1,apiUrl:"",...e},this.tokenManager=new At,this.tokenManager.setToken(e.token),this.logger=new D(this.config.debug),this.wsClient=new Rt(this.config.wsUrl,{autoReconnect:this.config.autoReconnect,maxReconnectAttempts:this.config.maxReconnectAttempts,reconnectDelay:this.config.reconnectDelay,debug:this.config.debug}),this.supportManager=new Wt(l=>this.conversationManager.get(l),this.config.debug),this.conversationManager=new Nt(l=>{if(!this.wsClient)throw new Error("WebSocket client not initialized");this.wsClient.send(l)},this.config.debug,void 0,l=>{if(!this.wsClient)throw new Error("WebSocket client not initialized");this.wsClient.send(l)},l=>{if(!this.wsClient)throw new Error("WebSocket client not initialized");this.wsClient.send(l)}),this.conversationManager.setSendReadHandler(l=>{if(!this.wsClient)throw new Error("WebSocket client not initialized");this.wsClient.send(l)}),this.presenceManager=new zt(this.config.debug),this.presenceManager.on("presence",l=>{this.emit("presence",l)});let t=(o=this.config.apiUrl)==null?void 0:o.trim(),i=(r=this.config.appId)==null?void 0:r.trim(),n=this.config.apiKey&&this.config.apiSecret,a=typeof this.config.getRestHeaders=="function";if(t){let l=async()=>{if(a&&this.config.getRestHeaders)return this.config.getRestHeaders();if(n)return{"X-Api-Key":this.config.apiKey,"X-Api-Secret":this.config.apiSecret,"Content-Type":"application/json"};let d=this.tokenManager.getToken();if(!d)throw new Error("Token required for list() and fetchMessages() when API keys are not set");return{Authorization:`Bearer ${d}`,"Content-Type":"application/json"}};this.conversationManager.setRestFetcher({apiUrl:t,getHeaders:l}),i&&this.conversationManager.setAppId(i),this.config.publicKey&&(this.ticketManager=new Bt(t,this.config.publicKey))}this.setupWebSocketHandlers()}setupWebSocketHandlers(){this.wsClient&&(this.wsClient.on("connected",()=>{this.connectionState="connected",this.emit("connected"),this.emit("state",this.connectionState),this.logger.debug("SDK connected")}),this.wsClient.on("disconnected",e=>{this.connectionState="disconnected",this.emit("disconnected",e),this.emit("state",this.connectionState),this.logger.debug("SDK disconnected")}),this.wsClient.on("reconnecting",e=>{this.connectionState="reconnecting",this.emit("reconnecting",e),this.emit("state",this.connectionState),this.logger.debug("SDK reconnecting",e)}),this.wsClient.on("message",e=>{if(this.emit("message",e),this.conversationManager.routeIncomingMessage(e),e.type==="presence"){let t=e;t.payload&&this.presenceManager.handlePresence(t.payload.user_id,t.payload.state)}switch(e.type==="support"&&this.conversationManager.routeSupportMessage(e),e.type){case"joined":this.emit("joined",e);break;case"message":this.emit("message",e);break;case"typing":this.emit("typing",e);break;case"receipt":break;case"support":break;case"presence":break;case"error":this.emit("error",e);break}}),this.wsClient.on("error",e=>{this.connectionState="error";let t=e instanceof Error?e.message:"";(e instanceof Error?e.name:"")==="TokenExpiredError"||t&&t.includes("Token expired")?(this.logger.error("Token expired:",t),this.logger.warn("To reconnect, call sdk.updateToken(newToken) then sdk.connect()")):this.logger.error("SDK error:",e);let n=e instanceof Error?e:new Error("WebSocket connection error");this.emit("error",n),this.emit("state",this.connectionState)}))}getState(){return this.connectionState}getConfig(){return{...this.config}}async connect(){if(this.connectionState==="connected"){this.logger.warn("Already connected");return}if(this.connectionState==="connecting"){this.logger.warn("Connection already in progress");return}this.tokenManager.validateTokenPresence();let e=this.tokenManager.getToken();if(!e)throw new Error("Token is required for connection");this.connectionState="connecting",this.emit("state",this.connectionState),this.logger.debug("Connecting...");try{if(!this.wsClient)throw new Error("WebSocket client not initialized");await this.wsClient.connect(e)}catch(t){this.connectionState="error",this.emit("state",this.connectionState);let i=t instanceof Error?t:new Error("Connection failed");throw this.emit("error",i),i}}async disconnect(){this.connectionState!=="disconnected"&&(this.logger.debug("Disconnecting..."),this.conversationManager.clear(),this.presenceManager.clear(),this.supportManager.clear(),this.wsClient&&this.wsClient.disconnect(),this.connectionState="disconnected",this.emit("disconnected"),this.emit("state",this.connectionState))}isConnected(){var e;return this.connectionState==="connected"&&((e=this.wsClient)==null?void 0:e.isConnected())===!0}updateToken(e){if(!e||e.length===0)throw new Error("Token cannot be empty");this.tokenManager.setToken(e),this.logger.debug("Token updated"),this.isConnected()&&(this.logger.warn("Token updated while connected. Disconnecting to reconnect with new token..."),this.disconnect())}get conversations(){return{get:e=>this.conversationManager.get(e),has:e=>this.conversationManager.has(e),join:e=>this.conversationManager.join(e,t=>this.joinConversation(t)),leave:e=>this.conversationManager.leave(e,t=>this.leaveConversation(t)),list:e=>this.conversationManager.list(e),getAttachmentDownloadUrl:(e,t)=>this.conversationManager.getAttachmentDownloadUrl(e,t)}}get support(){return{get:(e,t="customer")=>this.supportManager.get(e,t),has:e=>this.supportManager.has(e)}}get tickets(){if(!this.ticketManager)throw new Error("tickets requires both apiUrl and publicKey");return this.ticketManager}async joinConversation(e){if(!this.isConnected())throw new Error("Must be connected before joining a conversation");if(!e||e.length===0)throw new Error("Conversation ID is required");if(!this.wsClient)throw new Error("WebSocket client not initialized");let t={type:"join",conversation_id:e};try{this.wsClient.send(t),this.logger.debug("Join conversation sent:",e)}catch(i){let n=i instanceof Error?i:new Error("Failed to join conversation");throw this.emit("error",n),n}}async leaveConversation(e){if(!this.isConnected())throw new Error("Must be connected before leaving a conversation");if(!e||e.length===0)throw new Error("Conversation ID is required");if(!this.wsClient)throw new Error("WebSocket client not initialized");let t={type:"leave",conversation_id:e};try{this.wsClient.send(t),this.logger.debug("Leave conversation sent:",e)}catch(i){let n=i instanceof Error?i:new Error("Failed to leave conversation");throw this.emit("error",n),n}}rotateToken(e){this.tokenManager.rotateToken(e),this.logger.debug("Token rotated"),this.isConnected()&&this.logger.warn("Token rotated while connected. Reconnect may be required.")}getTokenManager(){return this.tokenManager}};var Kt={wsUrl:"",token:"",conversationId:"",publicKey:"",title:"Support",subtitle:"We typically reply in a few minutes",greeting:"Hi! \u{1F44B} How can we help you today?",placeholder:"Type your message\u2026",primaryColor:"#195EFF",position:"bottom-right",launcher:"\u{1F4AC}",autoOpen:!1,debug:!1,collectContact:!1,requireEmail:!1,contactPrompt:"Let\u2019s get you connected",csat:!0,csatPrompt:"How would you rate the support you received?",csatThanks:"Thanks for the feedback!",calls:!1,tickets:!0,ticketLabel:"Raise a ticket",callBundleUrl:"https://cdn.jsdelivr.net/npm/@vocantly/widget@0.1.16/dist/vocantly-widget-call.js"},I=null;function Ze(e){try{return JSON.parse(atob(e.split(".")[1]))}catch{return null}}function Ge(e){let t=Ze(e);return(t==null?void 0:t.user_id)||(t==null?void 0:t.sub)||null}function Qt(e){return typeof e!="number"?"":e<1024?e+" B":e<1024*1024?(e/1024).toFixed(1)+" KB":(e/(1024*1024)).toFixed(1)+" MB"}function y(e){return String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function ke(e){return y(e).replace(/\*\*([^*]+)\*\*/g,"<strong>$1</strong>").replace(/(^|[^*])\*([^*\n]+)\*(?!\*)/g,"$1<em>$2</em>").replace(/`([^`]+)`/g,"<code>$1</code>").replace(/\n/g,"<br>")}var ee=null;function Yt(){try{let e=window.AudioContext||window.webkitAudioContext;if(!e)return;ee=ee||new e,ee.resume&&ee.resume();let t=ee,i=t.currentTime,n=t.createOscillator(),a=t.createGain();n.type="sine",n.frequency.setValueAtTime(880,i),n.frequency.exponentialRampToValueAtTime(1320,i+.08),a.gain.setValueAtTime(1e-4,i),a.gain.exponentialRampToValueAtTime(.16,i+.02),a.gain.exponentialRampToValueAtTime(1e-4,i+.34),n.connect(a).connect(t.destination),n.start(i),n.stop(i+.36)}catch{}}function Xe(e,t){let i=String(e).replace("#",""),n=i.length===3?i.split("").map(l=>l+l).join(""):i,a=parseInt(n.slice(0,2),16)||0,o=parseInt(n.slice(2,4),16)||0,r=parseInt(n.slice(4,6),16)||0;return`rgba(${a},${o},${r},${t})`}function Gt(e,t){if(document.getElementById("caf-widget-styles"))return;let o=`
2
2
  /* Deliberately scoped, high-specificity reset. The widget is injected into arbitrary
3
3
  sites, so host button/input rules must never change its geometry. */
4
4
  .caf-root,.caf-root *{box-sizing:border-box;}
@@ -34,6 +34,9 @@ var VocantlyWidget=(()=>{var et=Object.create;var ee=Object.defineProperty;var t
34
34
  .caf-header .caf-s{margin-top:2px;font-size:12.5px;line-height:1.35;color:rgba(255,255,255,.82);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
35
35
  .caf-dot{position:absolute;right:-2px;bottom:-2px;width:12px;height:12px;border:2px solid ${e};border-radius:50%;background:#a7b0c0;}
36
36
  .caf-dot.on{background:#34d399;} .caf-dot.err{background:#fb7185;}
37
+ .caf-feedback{display:none;margin:0;padding:9px 14px;border-bottom:1px solid #e8ecf2;background:#fff7ed;color:#9a3412;
38
+ font-size:12.5px;line-height:1.4;text-align:center;}
39
+ .caf-feedback.show{display:block}.caf-feedback.success{background:#ecfdf5;color:#166534;border-color:#d1fae5;}
37
40
  .caf-root .caf-callbtn,.caf-root .caf-x{width:36px!important;height:36px!important;padding:0!important;border:1px solid rgba(255,255,255,.16)!important;
38
41
  border-radius:12px!important;background:rgba(255,255,255,.12)!important;color:#fff!important;cursor:pointer;align-items:center;justify-content:center;flex:none;
39
42
  transition:background .15s ease,transform .15s ease;}
@@ -89,6 +92,12 @@ var VocantlyWidget=(()=>{var et=Object.create;var ee=Object.defineProperty;var t
89
92
  .caf-upload-track{flex:1;height:4px;border-radius:999px;background:#e8ecf2;overflow:hidden;}
90
93
  .caf-upload-bar{height:100%;width:0%;background:${e};border-radius:999px;transition:width .12s ease;}
91
94
 
95
+ .caf-human-row{display:flex;justify-content:flex-start;padding:8px 14px 0;background:#fff;}
96
+ .caf-root .caf-human{border:1px solid #dce6ff!important;border-radius:999px!important;background:#f5f8ff!important;color:${e}!important;
97
+ padding:6px 10px!important;font-size:11.5px!important;font-weight:700!important;cursor:pointer;display:inline-flex!important;align-items:center;gap:6px;}
98
+ .caf-root .caf-human:hover{background:#eaf0ff!important;}.caf-root .caf-human:disabled{opacity:.55;cursor:default;}
99
+ .caf-panel.caf-prechat .caf-human-row,.caf-panel.caf-ticketing .caf-human-row{display:none;}
100
+
92
101
  .caf-composer{border-top:1px solid #e8ecf2;padding:12px 14px;display:flex;gap:6px;align-items:flex-end;background:#fff;}
93
102
  .caf-root .caf-attach{width:40px!important;height:44px!important;padding:0!important;flex:none;background:transparent!important;color:#8792a3!important;
94
103
  border:0!important;border-radius:12px!important;display:flex!important;align-items:center;justify-content:center;cursor:pointer;
@@ -96,6 +105,21 @@ var VocantlyWidget=(()=>{var et=Object.create;var ee=Object.defineProperty;var t
96
105
  .caf-attach svg{width:20px;height:20px;}
97
106
  .caf-root .caf-attach:hover{background:#f0f2f6!important;color:${e}!important;}
98
107
  .caf-root .caf-attach:disabled{opacity:.38;cursor:not-allowed;}
108
+ .caf-root .caf-ticket{width:40px!important;height:44px!important;padding:0!important;flex:none;background:transparent!important;color:#8792a3!important;
109
+ border:0!important;border-radius:12px!important;display:flex!important;align-items:center;justify-content:center;cursor:pointer;}
110
+ .caf-ticket svg{width:20px;height:20px}.caf-root .caf-ticket:hover{background:#f0f2f6!important;color:${e}!important;}
111
+ .caf-panel.caf-ticketing .caf-composer{display:none}
112
+ .caf-panel.caf-ticketing .caf-msgs{padding:16px;background:linear-gradient(160deg,#f7f9ff 0%,#f6f8fc 54%,#fff 100%);gap:0;}
113
+ .caf-panel.caf-ticketing .caf-msgs>:not(.caf-ticket-form){display:none;}
114
+ .caf-ticket-actions{display:grid;grid-template-columns:auto minmax(0,1fr);gap:10px;margin-top:6px;align-items:center;}
115
+ .caf-ticket-actions .caf-fbtn{margin-top:0!important;}
116
+ .caf-root .caf-ticket-cancel{height:48px!important;padding:0 15px!important;border:1px solid #dce1e9!important;border-radius:13px!important;background:#fff!important;color:#344054!important;cursor:pointer;display:inline-flex!important;align-items:center;justify-content:center;gap:6px;font-weight:700!important;}
117
+ .caf-root .caf-ticket-cancel:hover{border-color:#c8d0dc!important;background:#f8fafc!important;}
118
+ .caf-ticket-cancel svg{width:16px;height:16px;}
119
+ .caf-ticket-success{text-align:center;align-items:center;padding-top:34px!important;padding-bottom:34px!important;}
120
+ .caf-ticket-success .caf-form-icon{margin:0 auto 14px;width:54px;height:54px;border-radius:18px;background:#ecfdf3;color:#079455;font-size:25px;}
121
+ .caf-ticket-ref{display:inline-block;margin:14px 0;padding:9px 13px;border-radius:10px;background:#eaf0ff;color:${e};font-weight:800;letter-spacing:.04em;}
122
+ .caf-ticket-view{display:flex;align-items:center;justify-content:center;width:100%;height:48px;margin-top:8px;border-radius:13px;background:${e};color:#fff!important;font-weight:800;text-decoration:none;box-shadow:0 10px 24px ${Xe(e,.2)};}
99
123
  .caf-input{flex:1;resize:none;min-height:44px;max-height:100px;border:1px solid transparent!important;border-radius:14px!important;background:#f5f7fa!important;
100
124
  color:#172033!important;padding:11px 13px!important;font-size:13.5px!important;line-height:20px!important;outline:none;box-shadow:inset 0 0 0 1px #e7ebf1;transition:background .15s,border-color .15s,box-shadow .15s;}
101
125
  .caf-input::placeholder{color:#98a2b3;opacity:1;}
@@ -112,21 +136,36 @@ var VocantlyWidget=(()=>{var et=Object.create;var ee=Object.defineProperty;var t
112
136
  .caf-panel.caf-prechat .caf-composer{display:none;}
113
137
  .caf-form{width:100%;background:#fff;border:1px solid #e1e6ee;border-radius:20px;padding:22px;margin:0;display:flex;flex-direction:column;
114
138
  box-shadow:0 14px 36px rgba(24,34,55,.08);}
139
+ .caf-ticket-form{padding:20px;box-shadow:0 12px 30px rgba(24,34,55,.065);}
140
+ .caf-ticket-intro{display:flex;align-items:flex-start;gap:12px;margin-bottom:20px;padding-bottom:18px;border-bottom:1px solid #edf0f4;}
141
+ .caf-ticket-intro .caf-form-icon{width:42px;height:42px;margin:0;flex:none;border-radius:13px;}
142
+ .caf-ticket-intro .caf-form-icon svg{width:21px;height:21px;}
143
+ .caf-ticket-intro-copy{min-width:0;flex:1;}
144
+ .caf-ticket-intro .caf-form-d{margin-bottom:0;}
115
145
  .caf-form-icon{width:46px;height:46px;border-radius:14px;background:#eaf0ff;color:${e};display:flex;align-items:center;justify-content:center;margin-bottom:16px;}
116
146
  .caf-form-icon svg{width:23px;height:23px;}
117
147
  .caf-form-t{font-weight:750;font-size:18px;line-height:1.32;color:#172033;}
118
148
  .caf-form-d{margin:7px 0 18px;color:#667085;font-size:12.5px;line-height:1.5;}
119
- .caf-fl{display:block;margin:0 0 7px;color:#344054;font-size:12px;font-weight:700;}
120
- .caf-fl+.caf-fl{margin-top:14px;}
121
- .caf-root input.caf-fi{display:block!important;width:100%!important;height:48px!important;min-height:48px!important;max-height:48px!important;
122
- margin:0!important;border:1px solid #dce1e9!important;border-radius:13px!important;background:#fff!important;color:#172033!important;
123
- padding:0 14px!important;font-size:13.5px!important;font-weight:500!important;line-height:normal!important;outline:none!important;
149
+ .caf-fl{display:block;margin:0 0 7px;color:#344054;font-size:12px;font-weight:700;line-height:1.4;}
150
+ .caf-ticket-form .caf-fl{margin-top:14px;}
151
+ .caf-ticket-form .caf-ticket-intro+.caf-fl{margin-top:0;}
152
+ .caf-required{color:#d92d20;font-weight:800;}
153
+ .caf-optional{color:#98a2b3;font-size:10.5px;font-weight:600;}
154
+ .caf-root .caf-fi{display:block!important;width:100%!important;margin:6px 0 0!important;border:1px solid #d6dce6!important;border-radius:13px!important;background:#fff!important;color:#172033!important;
155
+ padding:0 14px!important;font-size:13.5px!important;font-weight:500!important;line-height:1.45!important;outline:none!important;
124
156
  appearance:none!important;-webkit-appearance:none!important;caret-color:${e};box-shadow:0 1px 2px rgba(16,24,40,.03);
125
157
  transition:border-color .15s,box-shadow .15s,background-color .15s;}
126
- .caf-root input.caf-fi::placeholder{color:#98a2b3!important;opacity:1!important;font-weight:500!important;}
127
- .caf-root input.caf-fi:hover{border-color:#c8d0dc!important;}
128
- .caf-root input.caf-fi:focus{border-color:${e}!important;background:#fff!important;box-shadow:0 0 0 3px rgba(25,94,255,.1),0 1px 2px rgba(16,24,40,.04)!important;}
129
- .caf-root input.caf-fi[aria-invalid="true"]{border-color:#d92d20!important;box-shadow:0 0 0 3px rgba(217,45,32,.1)!important;}
158
+ .caf-root input.caf-fi,.caf-root select.caf-fi{height:48px!important;min-height:48px!important;max-height:48px!important;}
159
+ .caf-root textarea.caf-fi{min-height:104px!important;max-height:180px!important;padding-top:12px!important;padding-bottom:12px!important;resize:vertical!important;}
160
+ .caf-root select.caf-fi{padding-right:36px!important;background-image:linear-gradient(45deg,transparent 50%,#667085 50%),linear-gradient(135deg,#667085 50%,transparent 50%)!important;background-position:calc(100% - 18px) 21px,calc(100% - 13px) 21px!important;background-size:5px 5px,5px 5px!important;background-repeat:no-repeat!important;}
161
+ .caf-root .caf-fi::placeholder{color:#98a2b3!important;opacity:1!important;font-weight:500!important;}
162
+ .caf-root .caf-fi:hover{border-color:#bfc8d6!important;}
163
+ .caf-root .caf-fi:focus{border-color:${e}!important;background:#fff!important;box-shadow:0 0 0 3px ${Xe(e,.11)},0 1px 2px rgba(16,24,40,.04)!important;}
164
+ .caf-root .caf-fi[aria-invalid="true"]{border-color:#d92d20!important;box-shadow:0 0 0 3px rgba(217,45,32,.1)!important;}
165
+ .caf-field-error{display:none;margin-top:5px;color:#d92d20;font-size:10.5px;font-weight:600;line-height:1.4;}
166
+ .caf-field-error.show{display:block;}
167
+ .caf-check-field{display:flex;align-items:flex-start;gap:9px;margin-top:14px;color:#344054;font-size:12px;font-weight:650;cursor:pointer;}
168
+ .caf-check-field input{width:17px;height:17px;margin:1px 0 0;accent-color:${e};flex:none;}
130
169
  .caf-ferr{color:#d92d20;font-size:12px;line-height:1.4;min-height:17px;margin-top:6px;}
131
170
  .caf-root .caf-fbtn{width:100%!important;height:48px!important;background:${e}!important;color:#fff!important;border:0!important;border-radius:13px!important;
132
171
  padding:0 16px!important;font-size:14px!important;font-weight:750!important;cursor:pointer;display:flex!important;align-items:center;justify-content:center;gap:8px;
@@ -150,14 +189,14 @@ var VocantlyWidget=(()=>{var et=Object.create;var ee=Object.defineProperty;var t
150
189
  }
151
190
  @media (max-height:620px) and (min-width:481px){.caf-panel{height:calc(100vh - 96px);bottom:70px}.caf-header{min-height:82px}.caf-form{padding:18px}.caf-form-d{margin-bottom:13px}}
152
191
  @media (prefers-reduced-motion:reduce){.caf-panel,.caf-launcher,.caf-send,.caf-fbtn{transition:none!important;}.caf-media-skeleton{animation:none!important;}}
153
- `,r=document.createElement("style");r.id="caf-widget-styles",r.textContent=a,document.head.appendChild(r)}function Lt(e){let t=document.createElement("div");return t.className="caf-root",t.innerHTML=`
192
+ `,r=document.createElement("style");r.id="caf-widget-styles",r.textContent=o,document.head.appendChild(r)}function Xt(e){let t=document.createElement("div");return t.className="caf-root",t.innerHTML=`
154
193
  <div class="caf-panel" id="caf-support-panel" role="dialog" aria-labelledby="caf-support-title" aria-hidden="true">
155
194
  <div class="caf-header">
156
195
  <div class="caf-headmark" aria-hidden="true">
157
196
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M21 11.5a8.4 8.4 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.4 8.4 0 0 1-3.8-.9L3 21l1.9-5.7a8.4 8.4 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.4 8.4 0 0 1 3.8-.9h.5a8.5 8.5 0 0 1 8 8v.5Z"/></svg>
158
197
  <span class="caf-dot" data-caf="dot"></span>
159
198
  </div>
160
- <div class="caf-headcopy"><div class="caf-t" id="caf-support-title">${A(e.title)}</div><div class="caf-s">${A(e.subtitle)}</div></div>
199
+ <div class="caf-headcopy"><div class="caf-t" id="caf-support-title">${y(e.title)}</div><div class="caf-s">${y(e.subtitle)}</div></div>
161
200
  <button class="caf-callbtn" data-caf="call" title="Start a voice call" aria-label="Start a voice call">
162
201
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.96.36 1.9.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.9.34 1.85.57 2.81.7A2 2 0 0 1 22 16.92z"/></svg>
163
202
  </button>
@@ -165,6 +204,7 @@ var VocantlyWidget=(()=>{var et=Object.create;var ee=Object.defineProperty;var t
165
204
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="m7 7 10 10M17 7 7 17"/></svg>
166
205
  </button>
167
206
  </div>
207
+ <div class="caf-feedback" data-caf="feedback" role="status" aria-live="polite"></div>
168
208
  <div class="caf-msgs" data-caf="msgs"></div>
169
209
  <div class="caf-pending" data-caf="pending">
170
210
  <div class="caf-pending-thumb" data-caf="pending-thumb"></div>
@@ -175,23 +215,34 @@ var VocantlyWidget=(()=>{var et=Object.create;var ee=Object.defineProperty;var t
175
215
  <span class="caf-upload-label" data-caf="upload-label"></span>
176
216
  <div class="caf-upload-track"><div class="caf-upload-bar" data-caf="upload-bar"></div></div>
177
217
  </div>
218
+ <div class="caf-human-row"><button class="caf-human" data-caf="human" type="button">Talk to a person</button></div>
178
219
  <div class="caf-composer">
179
220
  <input type="file" class="caf-file-input" data-caf="file" hidden accept="image/jpeg,image/png,image/gif,image/webp,video/mp4,video/webm,video/quicktime,application/pdf,text/plain,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/zip" />
180
221
  <button class="caf-attach" data-caf="attach" type="button" aria-label="Attach a file" title="Attach a file"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.44 11.05 12.25 20.24a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66L9.41 17.41a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg></button>
181
- <textarea class="caf-input" data-caf="input" rows="1" placeholder="${A(e.placeholder)}"></textarea>
222
+ <button class="caf-ticket" data-caf="ticket" type="button" aria-label="${y(e.ticketLabel)}" title="${y(e.ticketLabel)}"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 5a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v3a2 2 0 0 0 0 4v7a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-7a2 2 0 0 0 0-4V5Z"/><path d="M13 5v2M13 11v2M13 17v2"/></svg></button>
223
+ <textarea class="caf-input" data-caf="input" rows="1" placeholder="${y(e.placeholder)}"></textarea>
182
224
  <button class="caf-send" data-caf="send" aria-label="Send" title="Send"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 2 11 13"/><path d="M22 2 15 22l-4-9-9-4 20-7z"/></svg></button>
183
225
  </div>
184
226
  <div class="caf-foot">powered by Vocantly</div>
185
227
  </div>
186
228
  <button class="caf-launcher" data-caf="launcher" aria-label="Open support chat" aria-controls="caf-support-panel" aria-expanded="false">
187
- <span data-caf="launcher-icon">${e.launcher==="\u{1F4AC}"?'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/></svg>':A(e.launcher)}</span>
229
+ <span data-caf="launcher-icon">${e.launcher==="\u{1F4AC}"?'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/></svg>':y(e.launcher)}</span>
188
230
  <span class="caf-badge" data-caf="badge" style="display:none">0</span>
189
- </button>`,document.body.appendChild(t),{root:t,$:n=>t.querySelector(`[data-caf="${n}"]`)}}function Dt(e){if(R)return R.open(),R.api;let t={...Ht,...e||{}};if(!t.wsUrl)throw new Error("VocantlyWidget.init requires wsUrl");if(!t.publicKey&&!(t.token&&t.conversationId))throw new Error("VocantlyWidget.init requires either { publicKey } (anonymous) or { token, conversationId }");if(t.publicKey&&!t.apiUrl)throw new Error("VocantlyWidget.init with publicKey also requires apiUrl");It(t.primaryColor,t.position);let{root:i,$:n}=Lt(t),s={panel:i.querySelector(".caf-panel"),msgs:n("msgs"),input:n("input"),send:n("send"),dot:n("dot"),badge:n("badge"),launcher:n("launcher"),call:n("call"),file:n("file"),attach:n("attach"),upload:n("upload"),uploadBar:n("upload-bar"),uploadLabel:n("upload-label"),pending:n("pending"),pendingThumb:n("pending-thumb"),pendingName:n("pending-name"),pendingRemove:n("pending-remove")},a=null,r=!1,c=()=>t.calls===!0||r;function l(){a&&U&&s.call&&(s.call.style.display="flex")}function p(){return new Promise((o,h)=>{if(window.VocantlyWidgetCall)return o();let g=document.createElement("script");g.src=t.callBundleUrl,g.async=!0,g.onload=()=>o(),g.onerror=()=>h(new Error("call bundle load failed")),document.head.appendChild(g)})}async function m(){if(!(a||!j))try{if(await p(),!window.VocantlyWidgetCall)return;let o=document.createElement("div");document.body.appendChild(o),a=window.VocantlyWidgetCall.mount(o,j,{logoText:"CA",sessionTitle:t.title||"Call",sessionSubtitle:t.subtitle||"Secured via Vocantly",primaryColor:t.primaryColor,self:{name:"You",role:"Visitor"},peer:{name:t.title||"Support",role:"Agent"},voiceOnly:!0,simple:!0}),s.call&&(s.call.onclick=()=>a.startCall("voice")),l()}catch(o){t.debug&&console.error("[caf] call setup failed",o)}}let f=t.token||"",x=t.conversationId||"",y=f?Ve(f):null,d=null,M=new Set,L=!1,O=!1,ge=0,ue=!1,U=!t.collectContact,me=!1,D=o=>{s.dot.className="caf-dot"+(o?" "+o:"")},F=()=>{s.msgs.scrollTop=s.msgs.scrollHeight},we=o=>{ge=o,s.badge.textContent=String(o),s.badge.style.display=o>0?"flex":"none"};function K(o,h){let g=document.createElement("div");g.className="caf-m "+o,g.innerHTML=h,s.msgs.appendChild(g),F()}function be(o){var S,N,Te,Me,Ee,Re;if(o.optimistic)return;let h=(S=o.id)!=null?S:o.message_id,g=(Te=(N=o.senderId)!=null?N:o.sender_id)!=null?Te:"",b=(Ee=(Me=o.content)!=null?Me:o.text)!=null?Ee:"",u=(Re=o.messageType)!=null?Re:o.message_type,v=o.attachments,C=h||`${g}:${b}`;if(M.has(C))return;if(M.add(C),u==="system"){K("caf-note",fe(b)),t.csat&&qe();return}let w=y&&g===y;(u==="image"||u==="video"||u==="file")&&v&&v.length?We(w,b,u,v[0]):K(w?"caf-self":"caf-other",fe(b)),!w&&L&&Ut(),!w&&!O&&we(ge+1)}function ze(o,h){return`<div class="caf-media-skeleton" style="${o!=="file"&&h.width&&h.height?`aspect-ratio:${h.width}/${h.height};`:"aspect-ratio:4/3;"}"></div>`}function ve(o,h){let g=`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/></svg>
190
- <span class="caf-file-meta"><span class="caf-file-name">${A(o.file_name)}</span><span class="caf-file-size">${A(Ot(o.size_bytes))}</span></span>`;return h?`<a class="caf-file-chip" href="${h}" target="_blank" rel="noopener noreferrer" download="${A(o.file_name)}">${g}</a>`:`<div class="caf-file-chip">${g}</div>`}function re(o,h){o&&(o.innerHTML=`<div class="caf-media-error">Couldn't load ${A(h)}</div>`)}function We(o,h,g,b){let u=document.createElement("div");if(u.className="caf-m caf-media "+(o?"caf-self":"caf-other"),u.innerHTML=`<div class="caf-media-body" data-media>${g==="file"?ve(b):ze(g,b)}</div>`,h){let v=document.createElement("div");v.className="caf-media-caption",v.innerHTML=fe(h),u.appendChild(v)}s.msgs.appendChild(u),F(),d&&d.conversations.getAttachmentDownloadUrl(b.bucket,b.key).then(v=>{let C=u.querySelector("[data-media]");if(C){if(g==="image"){let w=document.createElement("a");w.href=v.url,w.target="_blank",w.rel="noopener noreferrer";let S=document.createElement("img");S.alt=b.file_name,S.loading="lazy",S.onerror=()=>re(C,b.file_name),S.src=v.url,w.appendChild(S),C.innerHTML="",C.appendChild(w)}else if(g==="video"){let w=document.createElement("video");w.controls=!0,w.preload="metadata",w.onerror=()=>re(C,b.file_name),w.src=v.url,C.innerHTML="",C.appendChild(w)}else C.innerHTML=ve(b,v.url);F()}}).catch(v=>{t.debug&&console.error("[caf] attachment load failed",v),re(u.querySelector("[data-media]"),b.file_name)})}let ye=!1;function qe(){if(ye||!t.apiUrl||!x)return;ye=!0;let o=document.createElement("div");o.className="caf-csat",o.innerHTML=`<div>${A(t.csatPrompt)}</div><div class="caf-csat-row"></div>`;let h=o.querySelector(".caf-csat-row");[["1","\u{1F61E}"],["2","\u{1F641}"],["3","\u{1F610}"],["4","\u{1F642}"],["5","\u{1F600}"]].forEach(([g,b])=>{let u=document.createElement("button");u.className="caf-csat-b",u.type="button",u.textContent=b,u.setAttribute("aria-label",g+" out of 5"),u.onclick=()=>Be(Number(g),o),h.appendChild(u)}),s.msgs.appendChild(o),F()}async function Be(o,h){h.innerHTML=`<div>${A(t.csatThanks)}</div>`,F();try{await fetch(t.apiUrl.replace(/\/$/,"")+"/sdk/conversations/"+x+"/rating",{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer "+f},body:JSON.stringify({rating:o})})}catch(g){t.debug&&console.error("[caf] csat failed",g)}}function Q(o){s.input.disabled=!o,s.send.disabled=!o,s.attach.disabled=!o,s.input.placeholder=o?t.placeholder:"\u2026"}function xe(){!ue&&t.greeting&&(K("caf-sys",A(t.greeting)),ue=!0)}async function Je(o,h){if(t.apiUrl)try{if(await Z(),!f)return;let g=await fetch(t.apiUrl.replace(/\/$/,"")+"/sdk/customers/me",{method:"PATCH",headers:{"Content-Type":"application/json",Authorization:"Bearer "+f},body:JSON.stringify({name:o||void 0,email:h||void 0})});!g.ok&&t.debug&&console.error("[caf] saveContact HTTP "+g.status)}catch(g){t.debug&&console.error("[caf] saveContact failed",g)}}function Ke(){me=!0,s.panel.classList.add("caf-prechat");let o=document.createElement("form");o.className="caf-form",o.noValidate=!0,o.innerHTML=`
231
+ </button>`,document.body.appendChild(t),{root:t,$:n=>t.querySelector(`[data-caf="${n}"]`)}}function Zt(e){if(I)return I.open(),I.api;let t={...Kt,...e||{}};if(!t.wsUrl)throw new Error("VocantlyWidget.init requires wsUrl");if(!t.publicKey&&!(t.token&&t.conversationId))throw new Error("VocantlyWidget.init requires either { publicKey } (anonymous) or { token, conversationId }");if(t.publicKey&&!t.apiUrl)throw new Error("VocantlyWidget.init with publicKey also requires apiUrl");Gt(t.primaryColor,t.position);let{root:i,$:n}=Xt(t),a={panel:i.querySelector(".caf-panel"),msgs:n("msgs"),input:n("input"),send:n("send"),dot:n("dot"),badge:n("badge"),launcher:n("launcher"),call:n("call"),file:n("file"),attach:n("attach"),ticket:n("ticket"),upload:n("upload"),uploadBar:n("upload-bar"),uploadLabel:n("upload-label"),pending:n("pending"),pendingThumb:n("pending-thumb"),pendingName:n("pending-name"),pendingRemove:n("pending-remove"),human:n("human"),feedback:n("feedback")},o=null;function r(s,c="error",h=5e3){a.feedback&&(o&&clearTimeout(o),a.feedback.textContent=s,a.feedback.className=`caf-feedback show${c==="success"?" success":""}`,a.feedback.setAttribute("role",c==="error"?"alert":"status"),h>0&&(o=setTimeout(()=>{a.feedback.className="caf-feedback",a.feedback.textContent=""},h)))}function l(){o&&clearTimeout(o),o=null,a.feedback.className="caf-feedback",a.feedback.textContent=""}let d=null,f=!1,v=()=>t.calls===!0||f;function b(){d&&q&&a.call&&(a.call.style.display="flex")}function R(){return new Promise((s,c)=>{if(window.VocantlyWidgetCall)return s();let h=document.createElement("script");h.src=t.callBundleUrl,h.async=!0,h.onload=()=>s(),h.onerror=()=>c(new Error("call bundle load failed")),document.head.appendChild(h)})}async function k(){if(!(d||!O))try{if(await R(),!window.VocantlyWidgetCall)return;let s=document.createElement("div");document.body.appendChild(s),d=window.VocantlyWidgetCall.mount(s,O,{logoText:"CA",sessionTitle:t.title||"Call",sessionSubtitle:t.subtitle||"Secured via Vocantly",primaryColor:t.primaryColor,self:{name:"You",role:"Visitor"},peer:{name:t.title||"Support",role:"Agent"},voiceOnly:!0,simple:!0}),a.call&&(a.call.onclick=()=>d.startCall("voice")),b()}catch(s){r("Calling is unavailable right now. You can keep chatting with support."),t.debug&&console.error("[caf] call setup failed",s)}}let u=t.token||"",C=t.conversationId||"",N=u?Ge(u):null,_=null,Se=new Set,Ce=!1,te=!1,_e=0,Te=!1,q=!t.collectContact,Me=!1,V=s=>{a.dot.className="caf-dot"+(s?" "+s:"")},W=()=>{a.msgs.scrollTop=a.msgs.scrollHeight},Ee=s=>{_e=s,a.badge.textContent=String(s),a.badge.style.display=s>0?"flex":"none"};function ie(s,c){let h=document.createElement("div");h.className="caf-m "+s,h.innerHTML=c,a.msgs.appendChild(h),W()}function Re(s){var S,L,T,P,U,$;if(s.optimistic)return;let c=(S=s.id)!=null?S:s.message_id,h=(T=(L=s.senderId)!=null?L:s.sender_id)!=null?T:"",g=(U=(P=s.content)!=null?P:s.text)!=null?U:"",p=($=s.messageType)!=null?$:s.message_type,w=s.attachments,x=c||`${h}:${g}`;if(Se.has(x))return;if(Se.add(x),p==="system"){ie("caf-note",ke(g)),t.csat&&it();return}let m=N&&h===N;(p==="image"||p==="video"||p==="file")&&w&&w.length?tt(m,g,p,w[0]):ie(m?"caf-self":"caf-other",ke(g)),!m&&Ce&&Yt(),!m&&!te&&Ee(_e+1)}function et(s,c){return`<div class="caf-media-skeleton" style="${s!=="file"&&c.width&&c.height?`aspect-ratio:${c.width}/${c.height};`:"aspect-ratio:4/3;"}"></div>`}function Ae(s,c){let h=`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/></svg>
232
+ <span class="caf-file-meta"><span class="caf-file-name">${y(s.file_name)}</span><span class="caf-file-size">${y(Qt(s.size_bytes))}</span></span>`;return c?`<a class="caf-file-chip" href="${c}" target="_blank" rel="noopener noreferrer" download="${y(s.file_name)}">${h}</a>`:`<div class="caf-file-chip">${h}</div>`}function pe(s,c){s&&(s.innerHTML=`<div class="caf-media-error">Couldn't load ${y(c)}</div>`)}function tt(s,c,h,g){let p=document.createElement("div");if(p.className="caf-m caf-media "+(s?"caf-self":"caf-other"),p.innerHTML=`<div class="caf-media-body" data-media>${h==="file"?Ae(g):et(h,g)}</div>`,c){let w=document.createElement("div");w.className="caf-media-caption",w.innerHTML=ke(c),p.appendChild(w)}a.msgs.appendChild(p),W(),_&&_.conversations.getAttachmentDownloadUrl(g.bucket,g.key).then(w=>{let x=p.querySelector("[data-media]");if(x){if(h==="image"){let m=document.createElement("a");m.href=w.url,m.target="_blank",m.rel="noopener noreferrer";let S=document.createElement("img");S.alt=g.file_name,S.loading="lazy",S.onerror=()=>pe(x,g.file_name),S.src=w.url,m.appendChild(S),x.innerHTML="",x.appendChild(m)}else if(h==="video"){let m=document.createElement("video");m.controls=!0,m.preload="metadata",m.onerror=()=>pe(x,g.file_name),m.src=w.url,x.innerHTML="",x.appendChild(m)}else x.innerHTML=Ae(g,w.url);W()}}).catch(w=>{t.debug&&console.error("[caf] attachment load failed",w),pe(p.querySelector("[data-media]"),g.file_name)})}let $e=!1;function it(){if($e||!t.apiUrl||!C)return;$e=!0;let s=document.createElement("div");s.className="caf-csat",s.innerHTML=`<div>${y(t.csatPrompt)}</div><div class="caf-csat-row"></div>`;let c=s.querySelector(".caf-csat-row");[["1","\u{1F61E}"],["2","\u{1F641}"],["3","\u{1F610}"],["4","\u{1F642}"],["5","\u{1F600}"]].forEach(([h,g])=>{let p=document.createElement("button");p.className="caf-csat-b",p.type="button",p.textContent=g,p.setAttribute("aria-label",h+" out of 5"),p.onclick=()=>nt(Number(h),s),c.appendChild(p)}),a.msgs.appendChild(s),W()}async function nt(s,c){c.innerHTML=`<div>${y(t.csatThanks)}</div>`,W();try{await fetch(t.apiUrl.replace(/\/$/,"")+"/sdk/conversations/"+C+"/rating",{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer "+u},body:JSON.stringify({rating:s})})}catch(h){t.debug&&console.error("[caf] csat failed",h)}}function ne(s){a.input.disabled=!s,a.send.disabled=!s,a.attach.disabled=!s,a.human.disabled=!s,a.input.placeholder=s?t.placeholder:"\u2026"}function je(){!Te&&t.greeting&&(ie("caf-sys",y(t.greeting)),Te=!0)}function fe(s,c){return!s||typeof s!="object"?"":s[c]||s.en||Object.values(s)[0]||""}function at(){let s=t.publicKey?"caf_vid_"+t.publicKey.slice(-10):"";if(!s)return;let c=null;try{c=localStorage.getItem(s)}catch{}if(!c){c="v_"+Math.random().toString(36).slice(2)+Date.now().toString(36);try{localStorage.setItem(s,c)}catch{}}return c}function rt(s,c){if(s.visibility==="agent_only"||s.type==="hidden")return"";let h=y(fe(s.label,c)||s.key),g=y(fe(s.placeholder,c)),p=s.required?' required aria-required="true"':"",w=(s.options||[]).filter(m=>m.is_active).map(m=>`<option value="${y(m.value)}">${y(fe(m.label,c)||m.value)}</option>`).join(""),x;if(s.type==="long_text")x=`<textarea class="caf-fi" name="${y(s.key)}" placeholder="${g}"${p}></textarea>`;else if(s.type==="select")x=`<select class="caf-fi" name="${y(s.key)}"${p}><option value="">Select\u2026</option>${w}</select>`;else{if(s.type==="checkbox"||s.type==="consent")return`<label class="caf-check-field"><input name="${y(s.key)}" type="checkbox"${p} /><span>${h}${s.required?' <span class="caf-required">*</span>':""}</span></label><span class="caf-field-error">Please confirm this field.</span>`;{let m=s.type==="email"?"email":s.type==="phone"?"tel":s.type==="number"||s.type==="currency"?"number":s.type==="date"?"date":s.type==="url"?"url":"text";x=`<input class="caf-fi" name="${y(s.key)}" type="${m}" placeholder="${g}"${p} />`}}return`<label class="caf-fl">${h}${s.required?' <span class="caf-required">*</span>':' <span class="caf-optional">Optional</span>'}${x}<span class="caf-field-error">Please complete this field.</span></label>`}function Ie(){return`<div class="caf-ticket-intro">
233
+ <div class="caf-form-icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M4 5a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v3a2 2 0 0 0 0 4v7a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-7a2 2 0 0 0 0-4V5Z"/><path d="M13 5v2M13 11v2M13 17v2"/></svg></div>
234
+ <div class="caf-ticket-intro-copy"><div class="caf-form-t">${y(t.ticketLabel)}</div><p class="caf-form-d">Tell us what happened. We\u2019ll create a reference so you can track every update.</p></div>
235
+ </div>`}function st(s){let c=null;return s.querySelectorAll("input, textarea, select").forEach(h=>{let g="";h.required&&(h.type==="checkbox"?!h.checked:!String(h.value||"").trim())?g=h.type==="checkbox"?"Please confirm this field.":"Please complete this field.":h.type==="email"&&h.value&&!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(h.value.trim())&&(g="Enter a valid email address."),h.setAttribute("aria-invalid",g?"true":"false");let p=h.closest(".caf-fl")||h.closest(".caf-check-field"),w=(p==null?void 0:p.querySelector(".caf-field-error"))||(p==null?void 0:p.nextElementSibling);w!=null&&w.classList.contains("caf-field-error")&&(w.textContent=g,w.classList.toggle("show",!!g)),g&&!c&&(c=h)}),c?(c.focus(),c.scrollIntoView({block:"center",behavior:"smooth"}),!1):!0}async function ot(){var h;if(!t.tickets||!t.publicKey||!t.apiUrl)return;a.panel.classList.add("caf-ticketing");let s=(navigator.language||"en").split("-")[0],c=document.createElement("form");c.className="caf-form caf-ticket-form",c.noValidate=!0,c.innerHTML=`${Ie()}<div class="caf-ferr" role="status">Loading your ticket form\u2026</div>`,a.msgs.appendChild(c),W();try{let g=t.apiUrl.replace(/\/$/,""),p=await fetch(g+"/tickets/public/form?language="+encodeURIComponent(s),{headers:{"X-Api-Key":t.publicKey}});if(!p.ok)throw new Error("ticket form HTTP "+p.status);let w=await p.json(),x=w.data||w,m=((h=x.schema)==null?void 0:h.fields)||[],S="caf_ticket_submission_"+t.publicKey.slice(-10),L;try{L=sessionStorage.getItem(S)}catch{}if(!L){L=typeof crypto!="undefined"&&crypto.randomUUID?crypto.randomUUID():"sub_"+Math.random().toString(36).slice(2)+Date.now().toString(36);try{sessionStorage.setItem(S,L)}catch{}}c.innerHTML=`${Ie()}
236
+ <label class="caf-fl">Subject <span class="caf-required">*</span><input class="caf-fi" name="subject" required placeholder="Briefly describe the issue" /><span class="caf-field-error">Please complete this field.</span></label>
237
+ <label class="caf-fl">What happened? <span class="caf-required">*</span><textarea class="caf-fi" name="description" required placeholder="Share what happened, what you expected, and anything you\u2019ve already tried."></textarea><span class="caf-field-error">Please complete this field.</span></label>
238
+ <label class="caf-fl">Name <span class="caf-optional">Optional</span><input class="caf-fi" name="name" autocomplete="name" placeholder="Your name" /><span class="caf-field-error"></span></label>
239
+ <label class="caf-fl">Email <span class="caf-optional">Optional</span><input class="caf-fi" name="email" type="email" inputmode="email" autocomplete="email" autocapitalize="none" spellcheck="false" placeholder="you@company.com" /><span class="caf-field-error"></span></label>
240
+ ${m.map(T=>rt(T,s)).join("")}
241
+ <div class="caf-ferr" data-ticket-error role="alert" aria-live="polite"></div><div class="caf-ticket-actions"><button class="caf-ticket-cancel" type="button"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6"/></svg><span>Back</span></button><button class="caf-fbtn" type="submit"><span>Submit ticket</span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg></button></div>`,c.querySelector(".caf-ticket-cancel").onclick=()=>{c.remove(),a.panel.classList.remove("caf-ticketing")},c.querySelectorAll("input, textarea, select").forEach(T=>{let P=()=>{if(!T.getAttribute("aria-invalid")||T.getAttribute("aria-invalid")==="false")return;T.setAttribute("aria-invalid","false");let U=T.closest(".caf-fl")||T.closest(".caf-check-field"),$=(U==null?void 0:U.querySelector(".caf-field-error"))||(U==null?void 0:U.nextElementSibling);$!=null&&$.classList.contains("caf-field-error")&&$.classList.remove("show")};T.addEventListener(T.type==="checkbox"||T.tagName==="SELECT"?"change":"input",P)}),c.onsubmit=async T=>{T.preventDefault();let P=c.querySelector(".caf-fbtn"),U=c.querySelector("[data-ticket-error]");if(!st(c)){U.textContent="Please review the highlighted fields.";return}P.disabled=!0,U.textContent="",P.querySelector("span").textContent="Creating ticket\u2026";let $=new FormData(c),De={};m.forEach(H=>{H.visibility==="agent_only"||H.type==="hidden"||(De[H.key]=H.type==="checkbox"||H.type==="consent"?$.get(H.key)==="on":$.get(H.key))});try{let H=await fetch(g+"/tickets/public",{method:"POST",headers:{"Content-Type":"application/json","X-Api-Key":t.publicKey},body:JSON.stringify({subject:$.get("subject"),description:$.get("description"),name:$.get("name")||void 0,email:$.get("email")||void 0,visitor_id:at(),external_reference:"widget:"+L,language:s,form_version_id:x.form_version_id,custom_fields:De})}),me=await H.json();if(!H.ok)throw new Error(me.message||"Could not create ticket");let z=me.data||me;try{localStorage.setItem("caf_ticket_"+z.reference,JSON.stringify(z))}catch{}try{sessionStorage.removeItem(S)}catch{}c.classList.add("caf-ticket-success");let qe=z.status_url||(z.secure_token?`${location.origin}/ticket/${encodeURIComponent(z.reference)}?token=${encodeURIComponent(z.secure_token)}`:"");c.innerHTML=`<div class="caf-form-icon" style="margin-left:auto;margin-right:auto">\u2713</div><div class="caf-form-t">Ticket created</div><p class="caf-form-d">Keep this reference for future updates.</p><div class="caf-ticket-ref">${y(z.reference)}</div>${qe?`<a class="caf-ticket-view" href="${y(qe)}" target="_blank" rel="noopener noreferrer">View ticket status</a>`:""}<button class="caf-fbtn" type="button">Return to conversation</button>`,c.querySelector("button").onclick=()=>{c.remove(),a.panel.classList.remove("caf-ticketing")}}catch(H){U.textContent=H.message||"Could not create ticket. Please try again.",P.disabled=!1,P.querySelector("span").textContent="Submit ticket"}}}catch(g){c.querySelector(".caf-ferr").textContent="Ticket forms are unavailable right now.",t.debug&&console.error("[caf] ticket form failed",g),setTimeout(()=>{c.remove(),a.panel.classList.remove("caf-ticketing")},2500)}}async function ct(s,c){if(!t.apiUrl)return;if(await oe(),!u)throw new Error("Support connection is not ready");if(!(await fetch(t.apiUrl.replace(/\/$/,"")+"/sdk/customers/me",{method:"PATCH",headers:{"Content-Type":"application/json",Authorization:"Bearer "+u},body:JSON.stringify({name:s||void 0,email:c||void 0})})).ok)throw new Error("Could not save contact details")}function lt(){Me=!0,a.panel.classList.add("caf-prechat");let s=document.createElement("form");s.className="caf-form",s.noValidate=!0,s.innerHTML=`
191
242
  <div class="caf-form-icon" aria-hidden="true">
192
243
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M20 15a4 4 0 0 1-4 4H8l-5 3v-7a4 4 0 0 1-1-2.65V7a4 4 0 0 1 4-4h10a4 4 0 0 1 4 4v8Z"/><path d="M7 9h10M7 13h6"/></svg>
193
244
  </div>
194
- <div class="caf-form-t">${A(t.contactPrompt)}</div>
245
+ <div class="caf-form-t">${y(t.contactPrompt)}</div>
195
246
  <p class="caf-form-d">Share a few details so our team can give you more personal support.</p>
196
247
  <label class="caf-fl" for="caf-contact-name">Name
197
248
  <input class="caf-fi" id="caf-contact-name" data-caf="f-name" name="name" type="text" autocomplete="name" placeholder="How should we address you?" />
@@ -202,4 +253,4 @@ var VocantlyWidget=(()=>{var et=Object.create;var ee=Object.defineProperty;var t
202
253
  <div class="caf-ferr" id="caf-contact-error" data-caf="f-err" role="alert" aria-live="polite"></div>
203
254
  <button class="caf-fbtn" data-caf="f-go" type="submit"><span>Start conversation</span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg></button>
204
255
  ${t.requireEmail?"":'<button class="caf-fskip" data-caf="f-skip" type="button">Continue without details</button>'}
205
- <p class="caf-form-privacy">Your details are only used to support this conversation.</p>`,s.msgs.appendChild(o);let h=v=>o.querySelector(`[data-caf="${v}"]`),g=/^[^\s@]+@[^\s@]+\.[^\s@]+$/,b=async v=>{let C=h("f-name").value.trim(),w=h("f-email").value.trim(),S=h("f-email");if(h("f-err").textContent="",S.setAttribute("aria-invalid","false"),v){if(t.requireEmail&&!w){h("f-err").textContent="Email is required.",S.setAttribute("aria-invalid","true"),S.focus();return}if(w&&!g.test(w)){h("f-err").textContent="Enter a valid email.",S.setAttribute("aria-invalid","true"),S.focus();return}let N=h("f-go");N.disabled=!0,N.querySelector("span").textContent="Starting\u2026",await Je(C,w)}o.remove(),s.panel.classList.remove("caf-prechat"),U=!0,l(),Q(!0),xe(),setTimeout(()=>s.input.focus(),30)};o.onsubmit=v=>{v.preventDefault(),b(!0)};let u=h("f-skip");u&&(u.onclick=()=>b(!1)),setTimeout(()=>h("f-name").focus(),40)}function Y(){if(O=!0,s.panel.classList.add("open"),s.panel.setAttribute("aria-hidden","false"),s.launcher.setAttribute("aria-expanded","true"),s.launcher.setAttribute("aria-label","Close support chat"),we(0),Z(),!U&&!me){Ke();return}U&&(xe(),setTimeout(()=>s.input.focus(),50))}function G(){O=!1,s.panel.classList.remove("open"),s.panel.setAttribute("aria-hidden","true"),s.launcher.setAttribute("aria-expanded","false"),s.launcher.setAttribute("aria-label","Open support chat"),s.launcher.focus()}s.launcher.onclick=()=>O?G():Y(),n("close").onclick=G;let j=null,X=null,P=null;function ke(){P&&(URL.revokeObjectURL(P),P=null),X=null,s.pending.classList.remove("show"),s.pendingThumb.innerHTML="",s.pendingName.textContent=""}function Qe(o){X=o,s.pendingName.textContent=o.name,o.type&&o.type.startsWith("image/")?(P=URL.createObjectURL(o),s.pendingThumb.innerHTML=`<img src="${P}" alt="" />`):s.pendingThumb.innerHTML='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/></svg>',s.pending.classList.add("show"),setTimeout(()=>s.input.focus(),30)}function ae(){if(!U)return;let o=s.input.value.trim();if(X){let h=X;ke(),s.input.value="",s.input.style.height="auto",Ge(h,o);return}if(!(!o||!j))try{j.sendMessage(o),s.input.value="",s.input.style.height="auto"}catch(h){t.debug&&console.error("[caf] send failed",h)}}function Ce(o,h){s.upload.classList.add("show"),s.uploadBar.style.width=h+"%",s.uploadLabel.textContent=`Sending ${o}\u2026 ${h}%`}function Ye(){s.upload.classList.remove("show")}async function Ge(o,h){if(!(!U||!j)){Q(!1),Ce(o.name,0);try{await j.sendMedia(o,{caption:h||void 0,onProgress:g=>Ce(o.name,g)})}catch(g){t.debug&&console.error("[caf] sendMedia failed",g),K("caf-note","Couldn\u2019t send that file. Please try again.")}finally{Ye(),Q(U)}}}s.attach.onclick=()=>s.file.click(),s.file.addEventListener("change",()=>{let o=s.file.files&&s.file.files[0];s.file.value="",o&&Qe(o)}),s.pendingRemove.onclick=()=>ke(),s.send.onclick=ae,Q(U),s.input.addEventListener("input",()=>{s.input.style.height="auto",s.input.style.height=Math.min(s.input.scrollHeight,96)+"px"}),s.input.addEventListener("keydown",o=>{o.key==="Enter"&&!o.shiftKey&&(o.preventDefault(),ae())});let oe=t.publicKey?"caf_session_"+t.publicKey.slice(-10):"",Xe=t.publicKey?"caf_vid_"+t.publicKey.slice(-10):"";function Se(){if(!oe)return null;try{let o=JSON.parse(localStorage.getItem(oe)||"null");if(o&&o.token&&o.conversationId&&o.exp>Date.now())return o}catch{}return null}async function Ze(){let o=t.apiUrl.replace(/\/$/,""),h=oe,g=Xe,b=Se();if(b)return r=!!b.calls_enabled,{token:b.token,conversationId:b.conversationId};let u=null;try{u=localStorage.getItem(g)}catch{}if(!u){u="v_"+Math.random().toString(36).slice(2)+Date.now().toString(36);try{localStorage.setItem(g,u)}catch{}}let v=await fetch(o+"/sdk/anonymous-session",{method:"POST",headers:{"Content-Type":"application/json","X-Api-Key":t.publicKey},body:JSON.stringify({visitor_id:u})});if(!v.ok)throw new Error("anonymous-session HTTP "+v.status);let C=await v.json(),w=C.data||C;r=!!w.calls_enabled;let S=Date.now()+Math.max(0,(w.expires_in||3600)*1e3-6e4);try{localStorage.setItem(h,JSON.stringify({token:w.token,conversationId:w.conversation_id,exp:S,calls_enabled:r}))}catch{}return{token:w.token,conversationId:w.conversation_id}}let V=null;function Z(){return V||(V=(async()=>{var o,h,g,b;try{if(D(""),t.publicKey){let u=await Ze();f=u.token,x=u.conversationId,y=Ve(f)}if(d=new Pe({wsUrl:t.wsUrl,token:f,apiUrl:t.apiUrl,appId:f?(o=Ne(f))==null?void 0:o.app_id:void 0,autoReconnect:!0,debug:t.debug}),d.on("connected",()=>D("on")),d.on("disconnected",()=>D("")),d.on("reconnecting",()=>D("")),d.on("error",()=>D("err")),await d.connect(),await d.conversations.join(x),j=d.conversations.get(x),j.on("message",be),t.apiUrl&&typeof j.fetchMessages=="function")try{let u=await j.fetchMessages({limit:30});((b=(g=u==null?void 0:u.messages)!=null?g:(h=u==null?void 0:u.data)==null?void 0:h.messages)!=null?b:[]).forEach(be)}catch{}L=!0,c()&&m()}catch(u){D("err"),V=null,t.debug&&console.error("[caf] connect/provision failed",u)}})(),V)}(!t.publicKey||Se())&&Z(),t.autoOpen&&Y();let _e={open:Y,close:G,send:async o=>{await Z(),s.input.value=o,ae()},destroy:()=>{try{d==null||d.disconnect()}catch{}i.remove(),R=null}};return R={open:Y,close:G,api:_e},_e}function $t(){R==null||R.open()}function Ft(){R==null||R.close()}function Pt(){R==null||R.api.destroy()}return at(Vt);})();
256
+ <p class="caf-form-privacy">Your details are only used to support this conversation.</p>`,a.msgs.appendChild(s);let c=w=>s.querySelector(`[data-caf="${w}"]`),h=/^[^\s@]+@[^\s@]+\.[^\s@]+$/,g=async w=>{let x=c("f-name").value.trim(),m=c("f-email").value.trim(),S=c("f-email");if(c("f-err").textContent="",S.setAttribute("aria-invalid","false"),w){if(t.requireEmail&&!m){c("f-err").textContent="Email is required.",S.setAttribute("aria-invalid","true"),S.focus();return}if(m&&!h.test(m)){c("f-err").textContent="Enter a valid email.",S.setAttribute("aria-invalid","true"),S.focus();return}let L=c("f-go");L.disabled=!0,L.querySelector("span").textContent="Starting\u2026";try{await ct(x,m)}catch(T){c("f-err").textContent="We couldn\u2019t start the conversation. Check your connection and try again.",L.disabled=!1,L.querySelector("span").textContent="Start conversation",t.debug&&console.error("[caf] saveContact failed",T);return}}s.remove(),a.panel.classList.remove("caf-prechat"),q=!0,b(),ne(!0),je(),setTimeout(()=>a.input.focus(),30)};s.onsubmit=w=>{w.preventDefault(),g(!0)};let p=c("f-skip");p&&(p.onclick=()=>g(!1)),setTimeout(()=>c("f-name").focus(),40)}function ae(){if(te=!0,a.panel.classList.add("open"),a.panel.setAttribute("aria-hidden","false"),a.launcher.setAttribute("aria-expanded","true"),a.launcher.setAttribute("aria-label","Close support chat"),Ee(0),oe().catch(()=>{}),!q&&!Me){lt();return}q&&(je(),setTimeout(()=>a.input.focus(),50))}function re(){te=!1,a.panel.classList.remove("open"),a.panel.setAttribute("aria-hidden","true"),a.launcher.setAttribute("aria-expanded","false"),a.launcher.setAttribute("aria-label","Open support chat"),a.launcher.focus()}a.launcher.onclick=()=>te?re():ae(),n("close").onclick=re;let O=null,se=null,J=null;function Le(){J&&(URL.revokeObjectURL(J),J=null),se=null,a.pending.classList.remove("show"),a.pendingThumb.innerHTML="",a.pendingName.textContent=""}function dt(s){se=s,a.pendingName.textContent=s.name,s.type&&s.type.startsWith("image/")?(J=URL.createObjectURL(s),a.pendingThumb.innerHTML=`<img src="${J}" alt="" />`):a.pendingThumb.innerHTML='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/></svg>',a.pending.classList.add("show"),setTimeout(()=>a.input.focus(),30)}async function ue(){if(!q)return;let s=a.input.value.trim();if(se){let c=se;Le(),a.input.value="",a.input.style.height="auto",pt(c,s);return}if(!(!s||!O))try{a.send.disabled=!0,await Promise.resolve(O.sendMessage(s)),a.input.value="",a.input.style.height="auto"}catch(c){r("Your message wasn\u2019t sent. Please try again."),t.debug&&console.error("[caf] send failed",c)}finally{a.send.disabled=!1}}function Ue(s,c){a.upload.classList.add("show"),a.uploadBar.style.width=c+"%",a.uploadLabel.textContent=`Sending ${s}\u2026 ${c}%`}function ht(){a.upload.classList.remove("show")}async function pt(s,c){if(!(!q||!O)){ne(!1),Ue(s.name,0);try{await O.sendMedia(s,{caption:c||void 0,onProgress:h=>Ue(s.name,h)})}catch(h){t.debug&&console.error("[caf] sendMedia failed",h),ie("caf-note","Couldn\u2019t send that file. Please try again.")}finally{ht(),ne(q)}}}a.attach.onclick=()=>a.file.click(),!t.tickets||!t.publicKey?a.ticket.style.display="none":a.ticket.onclick=ot,a.human.onclick=async()=>{if(!(!q||!O||a.human.disabled))try{a.human.disabled=!0,await Promise.resolve(O.sendMessage("I'd like to talk to a human agent.")),a.human.disabled=!0,a.human.textContent="Human requested",r("A human support agent has been requested.","success")}catch(s){a.human.disabled=!1,r("We couldn\u2019t request a human agent. Please try again."),t.debug&&console.error("[caf] human handoff request failed",s)}},a.file.addEventListener("change",()=>{let s=a.file.files&&a.file.files[0];a.file.value="",s&&dt(s)}),a.pendingRemove.onclick=()=>Le(),a.send.onclick=ue,ne(q),a.input.addEventListener("input",()=>{a.input.style.height="auto",a.input.style.height=Math.min(a.input.scrollHeight,96)+"px"}),a.input.addEventListener("keydown",s=>{s.key==="Enter"&&!s.shiftKey&&(s.preventDefault(),ue())});let ge=t.publicKey?"caf_session_"+t.publicKey.slice(-10):"",ft=t.publicKey?"caf_vid_"+t.publicKey.slice(-10):"";function Oe(){if(!ge)return null;try{let s=JSON.parse(localStorage.getItem(ge)||"null");if(s&&s.token&&s.conversationId&&s.exp>Date.now())return s}catch{}return null}async function ut(){let s=t.apiUrl.replace(/\/$/,""),c=ge,h=ft,g=Oe();if(g)return f=!!g.calls_enabled,{token:g.token,conversationId:g.conversationId};let p=null;try{p=localStorage.getItem(h)}catch{}if(!p){p="v_"+Math.random().toString(36).slice(2)+Date.now().toString(36);try{localStorage.setItem(h,p)}catch{}}let w=await fetch(s+"/sdk/anonymous-session",{method:"POST",headers:{"Content-Type":"application/json","X-Api-Key":t.publicKey},body:JSON.stringify({visitor_id:p})});if(!w.ok)throw new Error("anonymous-session HTTP "+w.status);let x=await w.json(),m=x.data||x;f=!!m.calls_enabled;let S=Date.now()+Math.max(0,(m.expires_in||3600)*1e3-6e4);try{localStorage.setItem(c,JSON.stringify({token:m.token,conversationId:m.conversation_id,exp:S,calls_enabled:f}))}catch{}return{token:m.token,conversationId:m.conversation_id}}let K=null;function oe(){return K||(K=(async()=>{var s,c,h,g;try{if(V(""),t.publicKey){let p=await ut();u=p.token,C=p.conversationId,N=Ge(u)}if(_=new Ye({wsUrl:t.wsUrl,token:u,apiUrl:t.apiUrl,appId:u?(s=Ze(u))==null?void 0:s.app_id:void 0,autoReconnect:!0,debug:t.debug}),_.on("connected",()=>{V("on"),l()}),_.on("disconnected",()=>V("")),_.on("reconnecting",()=>V("")),_.on("error",()=>{V("err"),r("Connection interrupted. We\u2019re trying to reconnect.","error",0)}),await _.connect(),await _.conversations.join(C),O=_.conversations.get(C),O.on("message",Re),t.apiUrl&&typeof O.fetchMessages=="function")try{let p=await O.fetchMessages({limit:30});((g=(h=p==null?void 0:p.messages)!=null?h:(c=p==null?void 0:p.data)==null?void 0:c.messages)!=null?g:[]).forEach(Re)}catch{}Ce=!0,v()&&k()}catch(p){throw V("err"),K=null,r("We couldn\u2019t connect to support. Please check your connection and try again.","error",0),t.debug&&console.error("[caf] connect/provision failed",p),p}})(),K)}(!t.publicKey||Oe())&&oe().catch(()=>{}),t.autoOpen&&ae();let He={open:ae,close:re,send:async s=>{await oe(),a.input.value=s,await ue()},destroy:()=>{try{_==null||_.disconnect()}catch{}i.remove(),I=null}};return I={open:ae,close:re,api:He},He}function ei(){I==null||I.open()}function ti(){I==null||I.close()}function ii(){I==null||I.api.destroy()}return xt(ni);})();