@scalemule/chat 0.0.7 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/react-components/ChatInput.d.ts +10 -2
- package/dist/react-components/ChatInput.d.ts.map +1 -1
- package/dist/react-components/ChatMessageItem.d.ts +13 -2
- package/dist/react-components/ChatMessageItem.d.ts.map +1 -1
- package/dist/react-components/ChatMessageList.d.ts +20 -4
- package/dist/react-components/ChatMessageList.d.ts.map +1 -1
- package/dist/react-components/ChatThread.d.ts +8 -1
- package/dist/react-components/ChatThread.d.ts.map +1 -1
- package/dist/react-components/EmojiPicker.d.ts +8 -1
- package/dist/react-components/EmojiPicker.d.ts.map +1 -1
- package/dist/react-components/ReactionBar.d.ts +10 -0
- package/dist/react-components/ReactionBar.d.ts.map +1 -0
- package/dist/react-components/ReportDialog.d.ts +30 -0
- package/dist/react-components/ReportDialog.d.ts.map +1 -0
- package/dist/react-components/index.d.ts +3 -1
- package/dist/react-components/index.d.ts.map +1 -1
- package/dist/react.cjs +1981 -559
- package/dist/react.d.cts +97 -13
- package/dist/react.d.ts +1 -1
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +1913 -494
- package/dist/support-widget.global.js +50 -25
- package/dist/widget/styles.d.ts +1 -1
- package/dist/widget/styles.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1,18 +1,26 @@
|
|
|
1
|
-
"use strict";var ScaleMuleSupportWidget=(()=>{var g=class{constructor(){this.listeners=new Map}on(s,e){return this.listeners.has(s)||this.listeners.set(s,new Set),this.listeners.get(s).add(e),()=>this.off(s,e)}off(s,e){this.listeners.get(s)?.delete(e)}once(s,e){let t=(i=>{this.off(s,t),e(i)});return this.on(s,t)}emit(s,...[e]){let t=this.listeners.get(s);if(t)for(let i of t)try{i(e)}catch(n){console.error(`[ScaleMuleChat] Error in ${String(s)} listener:`,n)}}removeAllListeners(s){s?this.listeners.delete(s):this.listeners.clear()}};var b=class extends g{constructor(e,t){super();this.typingTimers=new Map;this.unsubscribers=[];this.client=e,this.conversationId=t,this.state={conversationId:t,messages:[],readStatuses:[],typingUsers:[],members:[],hasMore:!1,isLoading:!0,error:null}}getState(){return this.state}async init(e={}){let t=e.realtime??!0,i=e.presence??t;this.bindEvents(),t&&this.client.connect();try{await this.client.getConversation(this.conversationId);let[n,r]=await Promise.all([this.client.getMessages(this.conversationId),this.client.getReadStatus(this.conversationId)]);return this.state={...this.state,messages:n.data?.messages??[],readStatuses:r.data?.statuses??[],hasMore:n.data?.has_more??!1,isLoading:!1,error:n.error?.message??r.error?.message??null},t&&this.unsubscribers.push(this.client.subscribeToConversation(this.conversationId)),i&&this.client.joinPresence(this.conversationId),this.emit("state",this.state),this.emit("ready",this.state),this.state}catch(n){let r=n instanceof Error?n.message:"Failed to initialize chat controller";throw this.state={...this.state,isLoading:!1,error:r},this.emit("state",this.state),this.emit("error",{message:r}),n}}async loadMore(){let e=this.state.messages[0]?.id;if(!e)return;let t=await this.client.getMessages(this.conversationId,{before:e});t.data?.messages?.length&&(this.state={...this.state,messages:[...t.data.messages,...this.state.messages],hasMore:t.data.has_more??!1},this.emit("state",this.state))}async sendMessage(e,t=[]){let i=t.length>0?t.every(r=>r.mime_type.startsWith("image/"))&&!e?"image":"file":"text",n=await this.client.sendMessage(this.conversationId,{content:e,attachments:t,message_type:i});if(n.error)throw new Error(n.error.message)}stageOptimisticMessage(e){let t=this.client.stageOptimisticMessage(this.conversationId,e);return this.patchState({messages:[...this.client.getCachedMessages(this.conversationId)]}),t}async uploadAttachment(e,t,i){return this.client.uploadAttachment(e,t,i)}async refreshAttachmentUrl(e,t){return this.client.refreshAttachmentUrl(e,t)}async addReaction(e,t){let i=await this.client.addReaction(e,t);if(i.error)throw new Error(i.error.message)}async removeReaction(e,t){let i=await this.client.removeReaction(e,t);if(i.error)throw new Error(i.error.message)}async reportMessage(e,t,i){return this.client.reportMessage(e,t,i)}async muteConversation(e){return this.client.muteConversation(this.conversationId,e)}async unmuteConversation(){return this.client.unmuteConversation(this.conversationId)}async markRead(){await this.client.markRead(this.conversationId)}async refreshReadStatus(){let e=await this.client.getReadStatus(this.conversationId);if(e.data?.statuses)return this.patchState({readStatuses:e.data.statuses}),e.data.statuses;if(e.error)throw new Error(e.error.message);return this.state.readStatuses}sendTyping(e=!0){this.client.sendTyping(this.conversationId,e)}destroy(){this.client.leavePresence(this.conversationId);for(let e of this.typingTimers.values())clearTimeout(e);this.typingTimers.clear();for(let e of this.unsubscribers)e();this.unsubscribers=[],this.removeAllListeners()}bindEvents(){this.unsubscribers.length||(this.unsubscribers.push(this.client.on("message",({conversationId:e})=>{e===this.conversationId&&this.patchState({messages:[...this.client.getCachedMessages(this.conversationId)]})})),this.unsubscribers.push(this.client.on("message:updated",({conversationId:e})=>{e===this.conversationId&&this.patchState({messages:[...this.client.getCachedMessages(this.conversationId)]})})),this.unsubscribers.push(this.client.on("message:deleted",({conversationId:e})=>{e===this.conversationId&&this.patchState({messages:[...this.client.getCachedMessages(this.conversationId)]})})),this.unsubscribers.push(this.client.on("reaction",({conversationId:e})=>{e===this.conversationId&&this.patchState({messages:[...this.client.getCachedMessages(this.conversationId)]})})),this.unsubscribers.push(this.client.on("typing",({conversationId:e,userId:t})=>{if(e!==this.conversationId)return;this.patchState({typingUsers:this.state.typingUsers.includes(t)?this.state.typingUsers:[...this.state.typingUsers,t]});let i=this.typingTimers.get(t);i&&clearTimeout(i),this.typingTimers.set(t,setTimeout(()=>{this.patchState({typingUsers:this.state.typingUsers.filter(n=>n!==t)}),this.typingTimers.delete(t)},3e3))})),this.unsubscribers.push(this.client.on("typing:stop",({conversationId:e,userId:t})=>{if(e!==this.conversationId)return;let i=this.typingTimers.get(t);i&&(clearTimeout(i),this.typingTimers.delete(t)),this.patchState({typingUsers:this.state.typingUsers.filter(n=>n!==t)})})),this.unsubscribers.push(this.client.on("read",({conversationId:e,userId:t,lastReadAt:i})=>{if(e!==this.conversationId)return;let n=[...this.state.readStatuses],r=n.findIndex(a=>a.user_id===t);r>=0?n[r]={...n[r],last_read_at:i}:n.push({user_id:t,last_read_at:i}),this.patchState({readStatuses:n})})),this.unsubscribers.push(this.client.on("presence:state",({conversationId:e,members:t})=>{e===this.conversationId&&this.patchState({members:t.map(i=>({userId:i.user_id,status:i.status??"online",userData:i.user_data}))})})),this.unsubscribers.push(this.client.on("presence:join",({conversationId:e,userId:t,userData:i})=>{e===this.conversationId&&(this.state.members.some(n=>n.userId===t)||this.patchState({members:[...this.state.members,{userId:t,status:"online",userData:i}]}))})),this.unsubscribers.push(this.client.on("presence:leave",({conversationId:e,userId:t})=>{e===this.conversationId&&this.patchState({members:this.state.members.filter(i=>i.userId!==t)})})),this.unsubscribers.push(this.client.on("presence:update",({conversationId:e,userId:t,status:i,userData:n})=>{e===this.conversationId&&this.patchState({members:this.state.members.map(r=>r.userId===t?{...r,status:i,userData:n}:r)})})))}patchState(e){this.state={...this.state,...e,error:e.error!==void 0?e.error:this.state.error},this.emit("state",this.state)}};var M="https://api.scalemule.com";var y=class{constructor(s,e){this.cache=new Map;this.maxMessages=s??200,this.maxConversations=e??50}getMessages(s){return this.cache.get(s)??[]}getMessage(s,e){return this.getMessages(s).find(t=>t.id===e)}setMessages(s,e){this.cache.set(s,e.slice(0,this.maxMessages)),this.evictOldConversations()}addMessage(s,e){let t=this.cache.get(s)??[];t.some(i=>i.id===e.id)||(t.push(e),t.sort((i,n)=>i.created_at.localeCompare(n.created_at)),t.length>this.maxMessages&&t.splice(0,t.length-this.maxMessages),this.cache.set(s,t))}upsertMessage(s,e){if(this.getMessage(s,e.id)){this.updateMessage(s,e);return}this.addMessage(s,e)}updateMessage(s,e){let t=this.cache.get(s);if(!t)return;let i=t.findIndex(n=>n.id===e.id);i>=0&&(t[i]=e)}reconcileOptimisticMessage(s,e){let t=this.cache.get(s);if(!t)return e;let i=(e.attachments??[]).map(r=>r.file_id).sort(),n=t.findIndex(r=>{if(!r.id.startsWith("pending-")||r.sender_id!==e.sender_id||r.content!==e.content)return!1;let a=(r.attachments??[]).map(o=>o.file_id).sort();return a.length!==i.length?!1:a.every((o,l)=>o===i[l])});return n>=0&&(t[n]=e),e}removeMessage(s,e){let t=this.cache.get(s);if(!t)return;let i=t.findIndex(n=>n.id===e);i>=0&&t.splice(i,1)}clear(s){s?this.cache.delete(s):this.cache.clear()}evictOldConversations(){for(;this.cache.size>this.maxConversations;){let s=this.cache.keys().next().value;s&&this.cache.delete(s)}}};var A="scalemule_chat_offline_queue",_=class{constructor(s=!0){this.queue=[];this.enabled=s,this.enabled&&this.load()}enqueue(s,e,t="text",i){this.enabled&&(this.queue.push({conversationId:s,content:e,message_type:t,attachments:i,timestamp:Date.now()}),this.save())}drain(){let s=[...this.queue];return this.queue=[],this.save(),s}get size(){return this.queue.length}save(){try{typeof localStorage<"u"&&localStorage.setItem(A,JSON.stringify(this.queue))}catch{}}load(){try{if(typeof localStorage<"u"){let s=localStorage.getItem(A);s&&(this.queue=JSON.parse(s))}}catch{this.queue=[]}}};var w=class{constructor(s){this.baseUrl=s.baseUrl.replace(/\/$/,""),this.apiKey=s.apiKey,this.getToken=s.getToken,this.timeout=s.timeout??1e4}async get(s){return this.request("GET",s)}async post(s,e){return this.request("POST",s,e)}async patch(s,e){return this.request("PATCH",s,e)}async del(s){return this.request("DELETE",s)}async request(s,e,t){let i={"Content-Type":"application/json"};if(this.apiKey&&(i["x-api-key"]=this.apiKey),this.getToken){let a=await this.getToken();a&&(i.Authorization=`Bearer ${a}`)}let n=new AbortController,r=setTimeout(()=>n.abort(),this.timeout);try{let a=await fetch(`${this.baseUrl}${e}`,{method:s,headers:i,body:t?JSON.stringify(t):void 0,signal:n.signal});if(clearTimeout(r),a.status===204)return{data:null,error:null};let o=await a.json().catch(()=>null);return a.ok?{data:o?.data!==void 0?o.data:o,error:null}:{data:null,error:{code:o?.error?.code??o?.code??"unknown",message:o?.error?.message??o?.message??a.statusText,status:a.status,details:o?.error?.details??o?.details}}}catch(a){return clearTimeout(r),{data:null,error:{code:"network_error",message:a instanceof Error?a.message:"Network error",status:0}}}}};var x=class extends g{constructor(e){super();this.ws=null;this.status="disconnected";this.subscriptions=new Set;this.presenceChannels=new Map;this.reconnectAttempt=0;this.reconnectTimer=null;this.heartbeatTimer=null;this.config=e,this.maxRetries=e.reconnect?.maxRetries??1/0,this.baseDelay=e.reconnect?.baseDelay??1e3,this.maxDelay=e.reconnect?.maxDelay??3e4;let t=e.baseUrl.replace(/\/$/,"");this.ticketUrl=`${t}/v1/realtime/ws/ticket`;let i=e.wsUrl?e.wsUrl.replace(/\/$/,""):t;this.wsBaseUrl=i.replace(/^http/,"ws")+"/v1/realtime/ws"}getStatus(){return this.status}async connect(){if(!(this.status==="connected"||this.status==="connecting")){this.setStatus("connecting");try{let e=await this.obtainTicket();if(!e){this.setStatus("disconnected"),this.emit("error",{message:"Failed to obtain WS ticket"});return}let t=`${this.wsBaseUrl}?ticket=${encodeURIComponent(e)}`;this.ws=new WebSocket(t),this.ws.onopen=()=>{this.setStatus("connected"),this.reconnectAttempt=0,this.startHeartbeat(),this.resubscribeAll()},this.ws.onmessage=i=>{this.handleMessage(i.data)},this.ws.onclose=()=>{this.stopHeartbeat(),this.status!=="disconnected"&&this.scheduleReconnect()},this.ws.onerror=()=>{}}catch{this.setStatus("disconnected"),this.scheduleReconnect()}}}disconnect(){this.setStatus("disconnected"),this.stopHeartbeat(),this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.ws&&(this.ws.onclose=null,this.ws.close(),this.ws=null)}subscribe(e){return this.subscriptions.add(e),this.status==="connected"?this.send({type:"subscribe",channel:e}):this.status==="disconnected"&&this.connect(),()=>this.unsubscribe(e)}unsubscribe(e){this.subscriptions.delete(e),this.status==="connected"&&this.send({type:"unsubscribe",channel:e})}publish(e,t){this.status==="connected"&&this.send({type:"publish",channel:e,data:t})}joinPresence(e,t){this.presenceChannels.set(e,t),this.status==="connected"&&this.send({type:"presence_join",channel:e,user_data:t??{}})}leavePresence(e){this.presenceChannels.delete(e),this.status==="connected"&&this.send({type:"presence_leave",channel:e})}async obtainTicket(){let e={"Content-Type":"application/json"};if(this.config.apiKey&&(e["x-api-key"]=this.config.apiKey),this.config.getToken){let t=await this.config.getToken();t&&(e.Authorization=`Bearer ${t}`)}try{let t=await fetch(this.ticketUrl,{method:"POST",headers:e});if(!t.ok)return null;let i=await t.json();return i.ticket??i.data?.ticket??null}catch{return null}}send(e){this.ws?.readyState===WebSocket.OPEN&&this.ws.send(JSON.stringify(e))}handleMessage(e){if(e!=="pong")try{let t=JSON.parse(e);switch(t.type){case"auth_success":break;case"subscribed":break;case"message":this.emit("message",{channel:t.channel,data:t.data});break;case"presence_state":this.emit("presence:state",{channel:t.channel,members:t.members??[]});break;case"presence_join":this.emit("presence:join",{channel:t.channel,user:t.user});break;case"presence_leave":this.emit("presence:leave",{channel:t.channel,userId:t.user_id});break;case"presence_update":this.emit("presence:update",{channel:t.channel,userId:t.user_id,status:t.status,userData:t.user_data});break;case"error":this.emit("error",{message:t.message??"Unknown error"});break;case"token_expiring":this.reconnectAttempt=0,this.scheduleReconnect();break}}catch{}}resubscribeAll(){for(let e of this.subscriptions)this.send({type:"subscribe",channel:e});for(let[e,t]of this.presenceChannels)this.send({type:"presence_join",channel:e,user_data:t??{}})}startHeartbeat(){this.heartbeatTimer=setInterval(()=>{this.ws?.readyState===WebSocket.OPEN&&this.ws.send("ping")},3e4)}stopHeartbeat(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null)}scheduleReconnect(){if(this.reconnectAttempt>=this.maxRetries){this.setStatus("disconnected");return}this.setStatus("reconnecting"),this.emit("reconnecting",{attempt:this.reconnectAttempt+1});let e=Math.min(this.baseDelay*Math.pow(2,this.reconnectAttempt)+Math.random()*this.baseDelay*.3,this.maxDelay);this.reconnectTimer=setTimeout(()=>{this.reconnectAttempt++,this.connect()},e)}setStatus(e){this.status!==e&&(this.status=e,this.emit("status",e))}};var Z=[0,1e3,3e3],R=45e3,ee=new Set([0,408,429,500,502,503,504]);function te(c){return new Promise(s=>setTimeout(s,c))}function se(c,s){return s==="aborted"?!1:ee.has(c)||s==="upload_stalled"}function ie(c,s,e,t){return new Promise(i=>{if(typeof XMLHttpRequest>"u"){i({data:null,error:{code:"unsupported_environment",message:"XMLHttpRequest is not available in this environment",status:0}});return}let n=new XMLHttpRequest,r=!1,a=null,o=0,l=s.size,h=p=>{r||(r=!0,a&&(clearTimeout(a),a=null),i(p))},f=()=>{a&&clearTimeout(a),a=setTimeout(()=>{n.abort(),h({data:null,error:{code:"upload_stalled",message:`Upload stalled (no progress for ${R/1e3}s)`,status:0,details:{bytes_sent:o,total_bytes:l}}})},R)};if(t){if(t.aborted){h({data:null,error:{code:"aborted",message:"Upload aborted",status:0}});return}t.addEventListener("abort",()=>{n.abort(),h({data:null,error:{code:"aborted",message:"Upload aborted",status:0}})},{once:!0})}n.upload.addEventListener("progress",p=>{f(),o=p.loaded,l=p.total||l,p.lengthComputable&&e?.(Math.round(p.loaded/p.total*100))}),n.addEventListener("load",()=>{if(n.status>=200&&n.status<300){e?.(100),h({data:null,error:null});return}h({data:null,error:{code:"upload_error",message:`S3 upload failed: ${n.status}`,status:n.status,details:{bytes_sent:o,total_bytes:l}}})}),n.addEventListener("error",()=>{h({data:null,error:{code:"upload_error",message:"S3 upload failed",status:n.status||0,details:{bytes_sent:o,total_bytes:l}}})}),n.addEventListener("abort",()=>{r||h({data:null,error:{code:"aborted",message:"Upload aborted",status:0}})}),n.open("PUT",c,!0),s.type&&n.setRequestHeader("Content-Type",s.type),f(),n.send(s)})}async function I(c,s,e,t){let i=null;for(let[n,r]of Z.entries()){r>0&&await te(r);let a=await ie(c,s,e,t);if(!a.error)return a;if(i={...a.error,details:{...a.error.details,attempt:n+1}},!se(a.error.status,a.error.code))break}return{data:null,error:i??{code:"upload_error",message:"Upload failed",status:0}}}var S=class extends g{constructor(e){super();this.conversationSubs=new Map;this.conversationTypes=new Map;let t=e.apiBaseUrl??M;this.currentUserId=e.userId,this.http=new w({baseUrl:t,apiKey:e.apiKey,getToken:e.getToken??(e.sessionToken?()=>Promise.resolve(e.sessionToken):void 0)}),this.ws=new x({baseUrl:t,wsUrl:e.wsUrl,apiKey:e.apiKey,getToken:e.getToken??(e.sessionToken?()=>Promise.resolve(e.sessionToken):void 0),reconnect:e.reconnect}),this.cache=new y(e.messageCache?.maxMessages,e.messageCache?.maxConversations),this.offlineQueue=new _(e.offlineQueue??!0),this.ws.on("status",i=>{switch(i){case"connected":this.emit("connected"),this.flushOfflineQueue();break;case"disconnected":this.emit("disconnected");break}}),this.ws.on("reconnecting",i=>{this.emit("reconnecting",i)}),this.ws.on("message",({channel:i,data:n})=>{this.handleRealtimeMessage(i,n)}),this.ws.on("presence:state",({channel:i,members:n})=>{let r=i.replace(/^conversation:(?:lr:|bc:|support:)?/,"");this.emit("presence:state",{conversationId:r,members:n})}),this.ws.on("presence:join",({channel:i,user:n})=>{let r=i.replace(/^conversation:(?:lr:|bc:|support:)?/,"");this.emit("presence:join",{userId:n.user_id,conversationId:r,userData:n.user_data})}),this.ws.on("presence:leave",({channel:i,userId:n})=>{let r=i.replace(/^conversation:(?:lr:|bc:|support:)?/,"");this.emit("presence:leave",{userId:n,conversationId:r})}),this.ws.on("presence:update",({channel:i,userId:n,status:r,userData:a})=>{let o=i.replace(/^conversation:(?:lr:|bc:|support:)?/,"");this.emit("presence:update",{userId:n,conversationId:o,status:r,userData:a})}),this.ws.on("error",({message:i})=>{this.emit("error",{code:"ws_error",message:i})})}get status(){return this.ws.getStatus()}get userId(){return this.currentUserId}connect(){this.ws.connect()}disconnect(){for(let e of this.conversationSubs.values())e();this.conversationSubs.clear(),this.ws.disconnect()}async createConversation(e){let t={...e,conversation_type:e.conversation_type??"direct"},i=await this.http.post("/v1/chat/conversations",t);return i.data&&this.trackConversationType(i.data),i}async listConversations(e){let t=new URLSearchParams;e?.page&&t.set("page",String(e.page)),e?.per_page&&t.set("per_page",String(e.per_page)),e?.conversation_type&&t.set("conversation_type",e.conversation_type);let i=t.toString(),n=await this.http.get(`/v1/chat/conversations${i?"?"+i:""}`);return n.data&&n.data.forEach(r=>this.trackConversationType(r)),n}async getConversation(e){let t=await this.http.get(`/v1/chat/conversations/${e}`);return t.data&&this.trackConversationType(t.data),t}trackConversationType(e){e.conversation_type!=="direct"&&e.conversation_type!=="group"&&this.conversationTypes.set(e.id,e.conversation_type)}async sendMessage(e,t){let i=await this.http.post(`/v1/chat/conversations/${e}/messages`,{content:t.content,message_type:t.message_type??"text",attachments:t.attachments});if(i.data){let n=this.cache.reconcileOptimisticMessage(e,i.data);this.cache.upsertMessage(e,n)}else i.error?.status===0&&this.offlineQueue.enqueue(e,t.content,t.message_type??"text",t.attachments);return i}async getMessages(e,t){let i=new URLSearchParams;t?.limit&&i.set("limit",String(t.limit)),t?.before&&i.set("before",t.before),t?.after&&i.set("after",t.after);let n=i.toString(),r=await this.http.get(`/v1/chat/conversations/${e}/messages${n?"?"+n:""}`);return r.data?.messages&&(t?.after||(r.data.messages=r.data.messages.slice().reverse()),!t?.before&&!t?.after&&this.cache.setMessages(e,r.data.messages)),r}async editMessage(e,t){return this.http.patch(`/v1/chat/messages/${e}`,{content:t})}async deleteMessage(e){return this.http.del(`/v1/chat/messages/${e}`)}async uploadAttachment(e,t,i){let n=typeof File<"u"&&e instanceof File?e.name:"attachment",r=e.type||"application/octet-stream",a=await this.http.post("/v1/storage/signed-url/upload",{filename:n,content_type:r,size_bytes:e.size,is_public:!1,metadata:{source:"chat_sdk"}});if(a.error||!a.data)return{data:null,error:a.error};let o=await I(a.data.upload_url,e,t,i);if(o.error)return{data:null,error:o.error};let l=await this.http.post("/v1/storage/signed-url/complete",{file_id:a.data.file_id,completion_token:a.data.completion_token});return l.error||!l.data?{data:null,error:l.error}:{data:{file_id:l.data.file_id,file_name:l.data.filename,file_size:l.data.size_bytes,mime_type:l.data.content_type,presigned_url:l.data.url},error:null}}async refreshAttachmentUrl(e,t){return this.http.get(`/v1/chat/messages/${e}/attachment/${t}/url`)}getCachedMessages(e){return this.cache.getMessages(e)}stageOptimisticMessage(e,t){return this.cache.upsertMessage(e,t),t}async addReaction(e,t){return this.http.post(`/v1/chat/messages/${e}/reactions`,{emoji:t})}async removeReaction(e,t){return this.http.del(`/v1/chat/messages/${e}/reactions/${encodeURIComponent(t)}`)}async reportMessage(e,t,i){return this.http.post(`/v1/chat/messages/${e}/report`,{reason:t,description:i})}async muteConversation(e,t){return this.http.post(`/v1/chat/conversations/${e}/mute`,{muted_until:t})}async unmuteConversation(e){return this.http.del(`/v1/chat/conversations/${e}/mute`)}async getUnreadTotal(){return this.http.get("/v1/chat/conversations/unread-total")}async sendTyping(e,t=!0){await this.http.post(`/v1/chat/conversations/${e}/typing`,{is_typing:t})}async markRead(e){await this.http.post(`/v1/chat/conversations/${e}/read`)}async getReadStatus(e){return this.http.get(`/v1/chat/conversations/${e}/read-status`)}async addParticipant(e,t){return this.http.post(`/v1/chat/conversations/${e}/participants`,{user_id:t})}async removeParticipant(e,t){return this.http.del(`/v1/chat/conversations/${e}/participants/${t}`)}joinPresence(e,t){let i=this.channelName(e);this.ws.joinPresence(i,t)}leavePresence(e){let t=this.channelName(e);this.ws.leavePresence(t)}updatePresence(e,t,i){let n=this.channelName(e);this.ws.send({type:"presence_update",channel:n,status:t,user_data:i})}async getChannelSettings(e){return this.http.get(`/v1/chat/channels/${e}/settings`)}setConversationType(e,t){this.conversationTypes.set(e,t)}async findChannelBySessionId(e){let t=await this.http.get(`/v1/chat/channels/by-session?linked_session_id=${encodeURIComponent(e)}`);return t.error?.status===404?null:(t.data&&this.conversationTypes.set(t.data.id,t.data.channel_type),t.data)}async joinChannel(e){return this.http.post(`/v1/chat/channels/${e}/join`,{})}async createEphemeralChannel(e){let t=await this.http.post("/v1/chat/channels/ephemeral",e);return t.data&&this.conversationTypes.set(t.data.id,"ephemeral"),t}async createLargeRoom(e){let t=await this.http.post("/v1/chat/channels/large-room",e);return t.data&&this.conversationTypes.set(t.data.id,"large_room"),t}async getSubscriberCount(e){return(await this.http.get(`/v1/chat/conversations/${e}/subscriber-count`)).data?.count??0}subscribeToConversation(e){if(this.conversationSubs.has(e))return this.conversationSubs.get(e);let t=this.channelName(e),i=this.ws.subscribe(t);return this.conversationSubs.set(e,i),()=>{this.conversationSubs.delete(e),i()}}channelName(e){switch(this.conversationTypes.get(e)){case"large_room":return`conversation:lr:${e}`;case"broadcast":return`conversation:bc:${e}`;case"support":return`conversation:support:${e}`;default:return`conversation:${e}`}}destroy(){this.disconnect(),this.cache.clear(),this.removeAllListeners()}handleRealtimeMessage(e,t){if(e.startsWith("conversation:")){this.handleConversationMessage(e,t);return}if(e.startsWith("private:")){this.handlePrivateMessage(t);return}}handlePrivateMessage(e){let t=e;if(!t)return;let i=t.event??t.type,n=t.data??t;switch(i){case"new_message":{let r=n.conversation_id,a=n.id??n.message_id,o=n.sender_id,l=n.content??"";r&&this.emit("inbox:update",{conversationId:r,messageId:a,senderId:o,preview:l});break}case"support:new_conversation":{let r=n.conversation_id,a=n.visitor_name;r&&this.emit("support:new",{conversationId:r,visitorName:a});break}case"support:assigned":{let r=n.conversation_id,a=n.visitor_name,o=n.visitor_email;r&&this.emit("support:assigned",{conversationId:r,visitorName:a,visitorEmail:o});break}}}normalizeMessage(e){return{id:e.id??e.message_id??"",content:e.content??"",message_type:e.message_type??"text",sender_id:e.sender_id??e.sender_user_id??"",sender_type:e.sender_type,sender_agent_model:e.sender_agent_model,attachments:e.attachments,reactions:e.reactions,is_edited:!!(e.is_edited??!1),created_at:e.created_at??e.updated_at??e.timestamp??new Date().toISOString()}}buildEditedMessage(e,t){let i=t.message_id??t.id;if(!i)return null;let n=this.cache.getMessage(e,i);return{id:n?.id??i,content:t.content??t.new_content??n?.content??"",message_type:n?.message_type??"text",sender_id:n?.sender_id??"",sender_type:n?.sender_type,sender_agent_model:n?.sender_agent_model,attachments:n?.attachments,reactions:n?.reactions,is_edited:!0,created_at:n?.created_at??t.updated_at??t.timestamp??new Date().toISOString()}}applyReactionEvent(e,t){let i={id:`${t.message_id}:${t.user_id}:${t.emoji}`,message_id:t.message_id,user_id:t.user_id,emoji:t.emoji,action:t.action,timestamp:t.timestamp},n=this.cache.getMessage(e,t.message_id);if(!n)return i;let r=[...n.reactions??[]],a=r.findIndex(o=>o.emoji===t.emoji);if(t.action==="added")if(a>=0){let o=r[a];if(!o.user_ids.includes(t.user_id)){let l=[...o.user_ids,t.user_id];r[a]={...o,user_ids:l,count:l.length}}}else r.push({emoji:t.emoji,count:1,user_ids:[t.user_id]});else if(a>=0){let o=r[a],l=o.user_ids.filter(h=>h!==t.user_id);l.length===0?r.splice(a,1):r[a]={...o,user_ids:l,count:l.length}}return this.cache.updateMessage(e,{...n,reactions:r}),i}handleConversationMessage(e,t){let i=e.replace(/^conversation:(?:lr:|bc:|support:)?/,""),n=t;if(!n)return;let r=n.event??n.type,a=n.data??n;switch(r){case"new_message":{let o=this.normalizeMessage(a),l=this.cache.reconcileOptimisticMessage(i,o);this.cache.upsertMessage(i,l),this.emit("message",{message:l,conversationId:i});break}case"message_edited":{let o=a,l=this.buildEditedMessage(i,o);l&&(this.cache.upsertMessage(i,l),this.emit("message:updated",{message:l,conversationId:i,update:o}));break}case"reaction":{let o=a;if(!o.message_id||!o.user_id||!o.emoji)break;let l=this.applyReactionEvent(i,o);this.emit("reaction",{reaction:l,conversationId:i,action:o.action});break}case"message_deleted":{let o=a.message_id??a.id;o&&(this.cache.removeMessage(i,o),this.emit("message:deleted",{messageId:o,conversationId:i}));break}case"user_typing":{let o=a.user_id;o&&this.emit("typing",{userId:o,conversationId:i});break}case"user_stopped_typing":{let o=a.user_id;o&&this.emit("typing:stop",{userId:o,conversationId:i});break}case"typing_batch":{let o=a.users??[];for(let l of o)this.emit("typing",{userId:l,conversationId:i});break}case"messages_read":{let o=a.user_id,l=a.last_read_at;o&&l&&this.emit("read",{userId:o,conversationId:i,lastReadAt:l});break}case"room_upgraded":{if(a.new_type==="large_room"){this.conversationTypes.set(i,"large_room");let l=this.conversationSubs.get(i);l&&(l(),this.conversationSubs.delete(i),this.subscribeToConversation(i)),this.emit("room_upgraded",{conversationId:i,newType:"large_room"})}break}}}async flushOfflineQueue(){let e=this.offlineQueue.drain();for(let t of e)await this.sendMessage(t.conversationId,{content:t.content,message_type:t.message_type,attachments:t.attachments})}};var ne="sm_support_",C=class{constructor(s){this.chatClient=null;this.refreshToken=null;this.accessToken=null;this.tokenExpiresAt=0;this.userId=null;this.apiKey=s.apiKey,this.apiBaseUrl=s.apiBaseUrl??"https://api.scalemule.com",this.wsUrl=s.wsUrl,this.storageKey=ne+s.apiKey.substring(0,8);let e=this.loadState();e?(this.anonymousId=e.anonymous_id,this.refreshToken=e.refresh_token,this.userId=e.user_id):this.anonymousId=crypto.randomUUID()}async initVisitorSession(s){if(this.visitorName=s?.name,this.visitorEmail=s?.email,this.refreshToken)try{await this.refreshAccessToken(),this.initChatClient();return}catch{this.refreshToken=null}let e=await fetch(`${this.apiBaseUrl}/v1/auth/visitor-session`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":this.apiKey},body:JSON.stringify({anonymous_id:this.anonymousId,name:s?.name,email:s?.email,page_url:typeof location<"u"?location.href:void 0})});if(!e.ok){let n=await e.text();throw new Error(`Visitor session failed: ${e.status} ${n}`)}let i=(await e.json()).data;this.accessToken=i.access_token,this.refreshToken=i.refresh_token,this.userId=i.user_id,this.tokenExpiresAt=Date.now()+i.expires_in*1e3,this.saveState(),this.initChatClient()}async startConversation(s,e){if(!this.accessToken)throw new Error("Call initVisitorSession() first");let t=await fetch(`${this.apiBaseUrl}/v1/chat/support/conversations`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":this.apiKey,Authorization:`Bearer ${this.accessToken}`},body:JSON.stringify({message:s,name:this.visitorName,email:this.visitorEmail,page_url:e?.page_url??(typeof location<"u"?location.href:void 0),user_agent:typeof navigator<"u"?navigator.userAgent:void 0,attachments:e?.attachments,metadata:e?.metadata})});if(!t.ok){let r=await t.text();throw new Error(`Create support conversation failed: ${t.status} ${r}`)}let n=(await t.json()).data;return this.chatClient&&this.chatClient.conversationTypes?.set(n.conversation_id,"support"),n}async getActiveConversation(){if(!this.accessToken)return null;let s=await fetch(`${this.apiBaseUrl}/v1/chat/support/conversations/mine`,{headers:{"x-api-key":this.apiKey,Authorization:`Bearer ${this.accessToken}`}});if(!s.ok)return null;let i=(await s.json()).data.find(n=>n.status==="active"||n.status==="waiting");return i&&this.chatClient&&this.chatClient.conversationTypes?.set(i.conversation_id,"support"),i??null}async getWidgetConfig(){let s=await fetch(`${this.apiBaseUrl}/v1/chat/support/widget/config`,{headers:{"x-api-key":this.apiKey}});if(!s.ok){let t=await s.text();throw new Error(`Get widget config failed: ${s.status} ${t}`)}return(await s.json()).data}get chat(){if(!this.chatClient)throw new Error("Call initVisitorSession() first");return this.chatClient}get isInitialized(){return this.chatClient!==null}connect(){this.chat.connect()}disconnect(){this.chat.disconnect()}get visitorUserId(){return this.userId}destroy(){this.chatClient?.destroy(),this.chatClient=null}initChatClient(){this.chatClient&&this.chatClient.destroy();let s={apiKey:this.apiKey,apiBaseUrl:this.apiBaseUrl,wsUrl:this.wsUrl,getToken:async()=>{if(!this.accessToken)return null;if(Date.now()>this.tokenExpiresAt-6e4)try{await this.refreshAccessToken()}catch{}return this.accessToken}};this.chatClient=new S(s)}async refreshAccessToken(){if(!this.refreshToken)throw new Error("No refresh token");let s=await fetch(`${this.apiBaseUrl}/v1/auth/token/refresh`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":this.apiKey},body:JSON.stringify({refresh_token:this.refreshToken})});if(!s.ok)throw new Error(`Token refresh failed: ${s.status}`);let t=(await s.json()).data;this.accessToken=t.access_token,this.tokenExpiresAt=Date.now()+t.expires_in*1e3}loadState(){try{let s=localStorage.getItem(this.storageKey);return s?JSON.parse(s):null}catch{return null}}saveState(){try{localStorage.setItem(this.storageKey,JSON.stringify({anonymous_id:this.anonymousId,refresh_token:this.refreshToken,user_id:this.userId}))}catch{}}};var P='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="28" height="28"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H5.17L4 17.17V4h16v12z"/><path d="M7 9h2v2H7zm4 0h2v2h-2zm4 0h2v2h-2z"/></svg>',L='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>',U='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>',$='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6z"/></svg>',E='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="18" height="18"><path d="M16.5 6.5v9.25a4.75 4.75 0 1 1-9.5 0V5.75a3.25 3.25 0 1 1 6.5 0V14a1.75 1.75 0 1 1-3.5 0V6.5H8.5V14a3.25 3.25 0 1 0 6.5 0V5.75a4.75 4.75 0 1 0-9.5 0v10a6.25 6.25 0 1 0 12.5 0V6.5h-1.5Z"/></svg>',O='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="18" height="18"><path d="M12 22a10 10 0 1 1 10-10 10.01 10.01 0 0 1-10 10Zm0-18.5a8.5 8.5 0 1 0 8.5 8.5A8.51 8.51 0 0 0 12 3.5Zm-3 7a1.25 1.25 0 1 1 1.25-1.25A1.25 1.25 0 0 1 9 10.5Zm6 0a1.25 1.25 0 1 1 1.25-1.25A1.25 1.25 0 0 1 15 10.5Zm-3 6.25A5.22 5.22 0 0 1 7.58 14h1.71a3.5 3.5 0 0 0 5.42 0h1.71A5.22 5.22 0 0 1 12 16.75Z"/></svg>';var D=`
|
|
1
|
+
"use strict";var ScaleMuleSupportWidget=(()=>{var g=class{constructor(){this.listeners=new Map}on(s,e){return this.listeners.has(s)||this.listeners.set(s,new Set),this.listeners.get(s).add(e),()=>this.off(s,e)}off(s,e){this.listeners.get(s)?.delete(e)}once(s,e){let t=(i=>{this.off(s,t),e(i)});return this.on(s,t)}emit(s,...[e]){let t=this.listeners.get(s);if(t)for(let i of t)try{i(e)}catch(n){console.error(`[ScaleMuleChat] Error in ${String(s)} listener:`,n)}}removeAllListeners(s){s?this.listeners.delete(s):this.listeners.clear()}};var b=class extends g{constructor(e,t){super();this.typingTimers=new Map;this.unsubscribers=[];this.client=e,this.conversationId=t,this.state={conversationId:t,messages:[],readStatuses:[],typingUsers:[],members:[],hasMore:!1,isLoading:!0,error:null}}getState(){return this.state}async init(e={}){let t=e.realtime??!0,i=e.presence??t;this.bindEvents(),t&&this.client.connect();try{await this.client.getConversation(this.conversationId);let[n,r]=await Promise.all([this.client.getMessages(this.conversationId),this.client.getReadStatus(this.conversationId)]);return this.state={...this.state,messages:n.data?.messages??[],readStatuses:r.data?.statuses??[],hasMore:n.data?.has_more??!1,isLoading:!1,error:n.error?.message??r.error?.message??null},t&&this.unsubscribers.push(this.client.subscribeToConversation(this.conversationId)),i&&this.client.joinPresence(this.conversationId),this.emit("state",this.state),this.emit("ready",this.state),this.state}catch(n){let r=n instanceof Error?n.message:"Failed to initialize chat controller";throw this.state={...this.state,isLoading:!1,error:r},this.emit("state",this.state),this.emit("error",{message:r}),n}}async loadMore(){let e=this.state.messages[0]?.id;if(!e)return;let t=await this.client.getMessages(this.conversationId,{before:e});t.data?.messages?.length&&(this.state={...this.state,messages:[...t.data.messages,...this.state.messages],hasMore:t.data.has_more??!1},this.emit("state",this.state))}async sendMessage(e,t=[]){let i=t.length>0?t.every(r=>r.mime_type.startsWith("image/"))&&!e?"image":"file":"text",n=await this.client.sendMessage(this.conversationId,{content:e,attachments:t,message_type:i});if(n.error)throw new Error(n.error.message)}stageOptimisticMessage(e){let t=this.client.stageOptimisticMessage(this.conversationId,e);return this.patchState({messages:[...this.client.getCachedMessages(this.conversationId)]}),t}async uploadAttachment(e,t,i){return this.client.uploadAttachment(e,t,i)}async refreshAttachmentUrl(e,t){return this.client.refreshAttachmentUrl(e,t)}async addReaction(e,t){let i=await this.client.addReaction(e,t);if(i.error)throw new Error(i.error.message)}async removeReaction(e,t){let i=await this.client.removeReaction(e,t);if(i.error)throw new Error(i.error.message)}async reportMessage(e,t,i){return this.client.reportMessage(e,t,i)}async muteConversation(e){return this.client.muteConversation(this.conversationId,e)}async unmuteConversation(){return this.client.unmuteConversation(this.conversationId)}async markRead(){await this.client.markRead(this.conversationId)}async refreshReadStatus(){let e=await this.client.getReadStatus(this.conversationId);if(e.data?.statuses)return this.patchState({readStatuses:e.data.statuses}),e.data.statuses;if(e.error)throw new Error(e.error.message);return this.state.readStatuses}sendTyping(e=!0){this.client.sendTyping(this.conversationId,e)}destroy(){this.client.leavePresence(this.conversationId);for(let e of this.typingTimers.values())clearTimeout(e);this.typingTimers.clear();for(let e of this.unsubscribers)e();this.unsubscribers=[],this.removeAllListeners()}bindEvents(){this.unsubscribers.length||(this.unsubscribers.push(this.client.on("message",({conversationId:e})=>{e===this.conversationId&&this.patchState({messages:[...this.client.getCachedMessages(this.conversationId)]})})),this.unsubscribers.push(this.client.on("message:updated",({conversationId:e})=>{e===this.conversationId&&this.patchState({messages:[...this.client.getCachedMessages(this.conversationId)]})})),this.unsubscribers.push(this.client.on("message:deleted",({conversationId:e})=>{e===this.conversationId&&this.patchState({messages:[...this.client.getCachedMessages(this.conversationId)]})})),this.unsubscribers.push(this.client.on("reaction",({conversationId:e})=>{e===this.conversationId&&this.patchState({messages:[...this.client.getCachedMessages(this.conversationId)]})})),this.unsubscribers.push(this.client.on("typing",({conversationId:e,userId:t})=>{if(e!==this.conversationId)return;this.patchState({typingUsers:this.state.typingUsers.includes(t)?this.state.typingUsers:[...this.state.typingUsers,t]});let i=this.typingTimers.get(t);i&&clearTimeout(i),this.typingTimers.set(t,setTimeout(()=>{this.patchState({typingUsers:this.state.typingUsers.filter(n=>n!==t)}),this.typingTimers.delete(t)},3e3))})),this.unsubscribers.push(this.client.on("typing:stop",({conversationId:e,userId:t})=>{if(e!==this.conversationId)return;let i=this.typingTimers.get(t);i&&(clearTimeout(i),this.typingTimers.delete(t)),this.patchState({typingUsers:this.state.typingUsers.filter(n=>n!==t)})})),this.unsubscribers.push(this.client.on("read",({conversationId:e,userId:t,lastReadAt:i})=>{if(e!==this.conversationId)return;let n=[...this.state.readStatuses],r=n.findIndex(a=>a.user_id===t);r>=0?n[r]={...n[r],last_read_at:i}:n.push({user_id:t,last_read_at:i}),this.patchState({readStatuses:n})})),this.unsubscribers.push(this.client.on("presence:state",({conversationId:e,members:t})=>{e===this.conversationId&&this.patchState({members:t.map(i=>({userId:i.user_id,status:i.status??"online",userData:i.user_data}))})})),this.unsubscribers.push(this.client.on("presence:join",({conversationId:e,userId:t,userData:i})=>{e===this.conversationId&&(this.state.members.some(n=>n.userId===t)||this.patchState({members:[...this.state.members,{userId:t,status:"online",userData:i}]}))})),this.unsubscribers.push(this.client.on("presence:leave",({conversationId:e,userId:t})=>{e===this.conversationId&&this.patchState({members:this.state.members.filter(i=>i.userId!==t)})})),this.unsubscribers.push(this.client.on("presence:update",({conversationId:e,userId:t,status:i,userData:n})=>{e===this.conversationId&&this.patchState({members:this.state.members.map(r=>r.userId===t?{...r,status:i,userData:n}:r)})})))}patchState(e){this.state={...this.state,...e,error:e.error!==void 0?e.error:this.state.error},this.emit("state",this.state)}};var M="https://api.scalemule.com";var y=class{constructor(s,e){this.cache=new Map;this.maxMessages=s??200,this.maxConversations=e??50}getMessages(s){return this.cache.get(s)??[]}getMessage(s,e){return this.getMessages(s).find(t=>t.id===e)}setMessages(s,e){this.cache.set(s,e.slice(0,this.maxMessages)),this.evictOldConversations()}addMessage(s,e){let t=this.cache.get(s)??[];t.some(i=>i.id===e.id)||(t.push(e),t.sort((i,n)=>i.created_at.localeCompare(n.created_at)),t.length>this.maxMessages&&t.splice(0,t.length-this.maxMessages),this.cache.set(s,t))}upsertMessage(s,e){if(this.getMessage(s,e.id)){this.updateMessage(s,e);return}this.addMessage(s,e)}updateMessage(s,e){let t=this.cache.get(s);if(!t)return;let i=t.findIndex(n=>n.id===e.id);i>=0&&(t[i]=e)}reconcileOptimisticMessage(s,e){let t=this.cache.get(s);if(!t)return e;let i=(e.attachments??[]).map(r=>r.file_id).sort(),n=t.findIndex(r=>{if(!r.id.startsWith("pending-")||r.sender_id!==e.sender_id||r.content!==e.content)return!1;let a=(r.attachments??[]).map(o=>o.file_id).sort();return a.length!==i.length?!1:a.every((o,l)=>o===i[l])});return n>=0&&(t[n]=e),e}removeMessage(s,e){let t=this.cache.get(s);if(!t)return;let i=t.findIndex(n=>n.id===e);i>=0&&t.splice(i,1)}clear(s){s?this.cache.delete(s):this.cache.clear()}evictOldConversations(){for(;this.cache.size>this.maxConversations;){let s=this.cache.keys().next().value;s&&this.cache.delete(s)}}};var A="scalemule_chat_offline_queue",_=class{constructor(s=!0){this.queue=[];this.enabled=s,this.enabled&&this.load()}enqueue(s,e,t="text",i){this.enabled&&(this.queue.push({conversationId:s,content:e,message_type:t,attachments:i,timestamp:Date.now()}),this.save())}drain(){let s=[...this.queue];return this.queue=[],this.save(),s}get size(){return this.queue.length}save(){try{typeof localStorage<"u"&&localStorage.setItem(A,JSON.stringify(this.queue))}catch{}}load(){try{if(typeof localStorage<"u"){let s=localStorage.getItem(A);s&&(this.queue=JSON.parse(s))}}catch{this.queue=[]}}};var w=class{constructor(s){this.baseUrl=s.baseUrl.replace(/\/$/,""),this.apiKey=s.apiKey,this.getToken=s.getToken,this.timeout=s.timeout??1e4}async get(s){return this.request("GET",s)}async post(s,e){return this.request("POST",s,e)}async patch(s,e){return this.request("PATCH",s,e)}async del(s){return this.request("DELETE",s)}async request(s,e,t){let i={"Content-Type":"application/json"};if(this.apiKey&&(i["x-api-key"]=this.apiKey),this.getToken){let a=await this.getToken();a&&(i.Authorization=`Bearer ${a}`)}let n=new AbortController,r=setTimeout(()=>n.abort(),this.timeout);try{let a=await fetch(`${this.baseUrl}${e}`,{method:s,headers:i,body:t?JSON.stringify(t):void 0,signal:n.signal});if(clearTimeout(r),a.status===204)return{data:null,error:null};let o=await a.json().catch(()=>null);return a.ok?{data:o?.data!==void 0?o.data:o,error:null}:{data:null,error:{code:o?.error?.code??o?.code??"unknown",message:o?.error?.message??o?.message??a.statusText,status:a.status,details:o?.error?.details??o?.details}}}catch(a){return clearTimeout(r),{data:null,error:{code:"network_error",message:a instanceof Error?a.message:"Network error",status:0}}}}};var x=class extends g{constructor(e){super();this.ws=null;this.status="disconnected";this.subscriptions=new Set;this.presenceChannels=new Map;this.reconnectAttempt=0;this.reconnectTimer=null;this.heartbeatTimer=null;this.config=e,this.maxRetries=e.reconnect?.maxRetries??1/0,this.baseDelay=e.reconnect?.baseDelay??1e3,this.maxDelay=e.reconnect?.maxDelay??3e4;let t=e.baseUrl.replace(/\/$/,"");this.ticketUrl=`${t}/v1/realtime/ws/ticket`;let i=e.wsUrl?e.wsUrl.replace(/\/$/,""):t;this.wsBaseUrl=i.replace(/^http/,"ws")+"/v1/realtime/ws"}getStatus(){return this.status}async connect(){if(!(this.status==="connected"||this.status==="connecting")){this.setStatus("connecting");try{let e=await this.obtainTicket();if(!e){this.setStatus("disconnected"),this.emit("error",{message:"Failed to obtain WS ticket"});return}let t=`${this.wsBaseUrl}?ticket=${encodeURIComponent(e)}`;this.ws=new WebSocket(t),this.ws.onopen=()=>{this.setStatus("connected"),this.reconnectAttempt=0,this.startHeartbeat(),this.resubscribeAll()},this.ws.onmessage=i=>{this.handleMessage(i.data)},this.ws.onclose=()=>{this.stopHeartbeat(),this.status!=="disconnected"&&this.scheduleReconnect()},this.ws.onerror=()=>{}}catch{this.setStatus("disconnected"),this.scheduleReconnect()}}}disconnect(){this.setStatus("disconnected"),this.stopHeartbeat(),this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.ws&&(this.ws.onclose=null,this.ws.close(),this.ws=null)}subscribe(e){return this.subscriptions.add(e),this.status==="connected"?this.send({type:"subscribe",channel:e}):this.status==="disconnected"&&this.connect(),()=>this.unsubscribe(e)}unsubscribe(e){this.subscriptions.delete(e),this.status==="connected"&&this.send({type:"unsubscribe",channel:e})}publish(e,t){this.status==="connected"&&this.send({type:"publish",channel:e,data:t})}joinPresence(e,t){this.presenceChannels.set(e,t),this.status==="connected"&&this.send({type:"presence_join",channel:e,user_data:t??{}})}leavePresence(e){this.presenceChannels.delete(e),this.status==="connected"&&this.send({type:"presence_leave",channel:e})}async obtainTicket(){let e={"Content-Type":"application/json"};if(this.config.apiKey&&(e["x-api-key"]=this.config.apiKey),this.config.getToken){let t=await this.config.getToken();t&&(e.Authorization=`Bearer ${t}`)}try{let t=await fetch(this.ticketUrl,{method:"POST",headers:e});if(!t.ok)return null;let i=await t.json();return i.ticket??i.data?.ticket??null}catch{return null}}send(e){this.ws?.readyState===WebSocket.OPEN&&this.ws.send(JSON.stringify(e))}handleMessage(e){if(e!=="pong")try{let t=JSON.parse(e);switch(t.type){case"auth_success":break;case"subscribed":break;case"message":this.emit("message",{channel:t.channel,data:t.data});break;case"presence_state":this.emit("presence:state",{channel:t.channel,members:t.members??[]});break;case"presence_join":this.emit("presence:join",{channel:t.channel,user:t.user});break;case"presence_leave":this.emit("presence:leave",{channel:t.channel,userId:t.user_id});break;case"presence_update":this.emit("presence:update",{channel:t.channel,userId:t.user_id,status:t.status,userData:t.user_data});break;case"error":this.emit("error",{message:t.message??"Unknown error"});break;case"token_expiring":this.reconnectAttempt=0,this.scheduleReconnect();break}}catch{}}resubscribeAll(){for(let e of this.subscriptions)this.send({type:"subscribe",channel:e});for(let[e,t]of this.presenceChannels)this.send({type:"presence_join",channel:e,user_data:t??{}})}startHeartbeat(){this.heartbeatTimer=setInterval(()=>{this.ws?.readyState===WebSocket.OPEN&&this.ws.send("ping")},3e4)}stopHeartbeat(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null)}scheduleReconnect(){if(this.reconnectAttempt>=this.maxRetries){this.setStatus("disconnected");return}this.setStatus("reconnecting"),this.emit("reconnecting",{attempt:this.reconnectAttempt+1});let e=Math.min(this.baseDelay*Math.pow(2,this.reconnectAttempt)+Math.random()*this.baseDelay*.3,this.maxDelay);this.reconnectTimer=setTimeout(()=>{this.reconnectAttempt++,this.connect()},e)}setStatus(e){this.status!==e&&(this.status=e,this.emit("status",e))}};var Z=[0,1e3,3e3],R=45e3,ee=new Set([0,408,429,500,502,503,504]);function te(c){return new Promise(s=>setTimeout(s,c))}function se(c,s){return s==="aborted"?!1:ee.has(c)||s==="upload_stalled"}function ie(c,s,e,t){return new Promise(i=>{if(typeof XMLHttpRequest>"u"){i({data:null,error:{code:"unsupported_environment",message:"XMLHttpRequest is not available in this environment",status:0}});return}let n=new XMLHttpRequest,r=!1,a=null,o=0,l=s.size,h=u=>{r||(r=!0,a&&(clearTimeout(a),a=null),i(u))},f=()=>{a&&clearTimeout(a),a=setTimeout(()=>{n.abort(),h({data:null,error:{code:"upload_stalled",message:`Upload stalled (no progress for ${R/1e3}s)`,status:0,details:{bytes_sent:o,total_bytes:l}}})},R)};if(t){if(t.aborted){h({data:null,error:{code:"aborted",message:"Upload aborted",status:0}});return}t.addEventListener("abort",()=>{n.abort(),h({data:null,error:{code:"aborted",message:"Upload aborted",status:0}})},{once:!0})}n.upload.addEventListener("progress",u=>{f(),o=u.loaded,l=u.total||l,u.lengthComputable&&e?.(Math.round(u.loaded/u.total*100))}),n.addEventListener("load",()=>{if(n.status>=200&&n.status<300){e?.(100),h({data:null,error:null});return}h({data:null,error:{code:"upload_error",message:`S3 upload failed: ${n.status}`,status:n.status,details:{bytes_sent:o,total_bytes:l}}})}),n.addEventListener("error",()=>{h({data:null,error:{code:"upload_error",message:"S3 upload failed",status:n.status||0,details:{bytes_sent:o,total_bytes:l}}})}),n.addEventListener("abort",()=>{r||h({data:null,error:{code:"aborted",message:"Upload aborted",status:0}})}),n.open("PUT",c,!0),s.type&&n.setRequestHeader("Content-Type",s.type),f(),n.send(s)})}async function I(c,s,e,t){let i=null;for(let[n,r]of Z.entries()){r>0&&await te(r);let a=await ie(c,s,e,t);if(!a.error)return a;if(i={...a.error,details:{...a.error.details,attempt:n+1}},!se(a.error.status,a.error.code))break}return{data:null,error:i??{code:"upload_error",message:"Upload failed",status:0}}}var S=class extends g{constructor(e){super();this.conversationSubs=new Map;this.conversationTypes=new Map;let t=e.apiBaseUrl??M;this.currentUserId=e.userId,this.http=new w({baseUrl:t,apiKey:e.apiKey,getToken:e.getToken??(e.sessionToken?()=>Promise.resolve(e.sessionToken):void 0)}),this.ws=new x({baseUrl:t,wsUrl:e.wsUrl,apiKey:e.apiKey,getToken:e.getToken??(e.sessionToken?()=>Promise.resolve(e.sessionToken):void 0),reconnect:e.reconnect}),this.cache=new y(e.messageCache?.maxMessages,e.messageCache?.maxConversations),this.offlineQueue=new _(e.offlineQueue??!0),this.ws.on("status",i=>{switch(i){case"connected":this.emit("connected"),this.flushOfflineQueue();break;case"disconnected":this.emit("disconnected");break}}),this.ws.on("reconnecting",i=>{this.emit("reconnecting",i)}),this.ws.on("message",({channel:i,data:n})=>{this.handleRealtimeMessage(i,n)}),this.ws.on("presence:state",({channel:i,members:n})=>{let r=i.replace(/^conversation:(?:lr:|bc:|support:)?/,"");this.emit("presence:state",{conversationId:r,members:n})}),this.ws.on("presence:join",({channel:i,user:n})=>{let r=i.replace(/^conversation:(?:lr:|bc:|support:)?/,"");this.emit("presence:join",{userId:n.user_id,conversationId:r,userData:n.user_data})}),this.ws.on("presence:leave",({channel:i,userId:n})=>{let r=i.replace(/^conversation:(?:lr:|bc:|support:)?/,"");this.emit("presence:leave",{userId:n,conversationId:r})}),this.ws.on("presence:update",({channel:i,userId:n,status:r,userData:a})=>{let o=i.replace(/^conversation:(?:lr:|bc:|support:)?/,"");this.emit("presence:update",{userId:n,conversationId:o,status:r,userData:a})}),this.ws.on("error",({message:i})=>{this.emit("error",{code:"ws_error",message:i})})}get status(){return this.ws.getStatus()}get userId(){return this.currentUserId}connect(){this.ws.connect()}disconnect(){for(let e of this.conversationSubs.values())e();this.conversationSubs.clear(),this.ws.disconnect()}async createConversation(e){let t={...e,conversation_type:e.conversation_type??"direct"},i=await this.http.post("/v1/chat/conversations",t);return i.data&&this.trackConversationType(i.data),i}async listConversations(e){let t=new URLSearchParams;e?.page&&t.set("page",String(e.page)),e?.per_page&&t.set("per_page",String(e.per_page)),e?.conversation_type&&t.set("conversation_type",e.conversation_type);let i=t.toString(),n=await this.http.get(`/v1/chat/conversations${i?"?"+i:""}`);return n.data&&n.data.forEach(r=>this.trackConversationType(r)),n}async getConversation(e){let t=await this.http.get(`/v1/chat/conversations/${e}`);return t.data&&this.trackConversationType(t.data),t}trackConversationType(e){e.conversation_type!=="direct"&&e.conversation_type!=="group"&&this.conversationTypes.set(e.id,e.conversation_type)}async sendMessage(e,t){let i=await this.http.post(`/v1/chat/conversations/${e}/messages`,{content:t.content,message_type:t.message_type??"text",attachments:t.attachments});if(i.data){let n=this.cache.reconcileOptimisticMessage(e,i.data);this.cache.upsertMessage(e,n)}else i.error?.status===0&&this.offlineQueue.enqueue(e,t.content,t.message_type??"text",t.attachments);return i}async getMessages(e,t){let i=new URLSearchParams;t?.limit&&i.set("limit",String(t.limit)),t?.before&&i.set("before",t.before),t?.after&&i.set("after",t.after);let n=i.toString(),r=await this.http.get(`/v1/chat/conversations/${e}/messages${n?"?"+n:""}`);return r.data?.messages&&(t?.after||(r.data.messages=r.data.messages.slice().reverse()),!t?.before&&!t?.after&&this.cache.setMessages(e,r.data.messages)),r}async editMessage(e,t){return this.http.patch(`/v1/chat/messages/${e}`,{content:t})}async deleteMessage(e){return this.http.del(`/v1/chat/messages/${e}`)}async uploadAttachment(e,t,i){let n=typeof File<"u"&&e instanceof File?e.name:"attachment",r=e.type||"application/octet-stream",a=await this.http.post("/v1/storage/signed-url/upload",{filename:n,content_type:r,size_bytes:e.size,is_public:!1,metadata:{source:"chat_sdk"}});if(a.error||!a.data)return{data:null,error:a.error};let o=await I(a.data.upload_url,e,t,i);if(o.error)return{data:null,error:o.error};let l=await this.http.post("/v1/storage/signed-url/complete",{file_id:a.data.file_id,completion_token:a.data.completion_token});return l.error||!l.data?{data:null,error:l.error}:{data:{file_id:l.data.file_id,file_name:l.data.filename,file_size:l.data.size_bytes,mime_type:l.data.content_type,presigned_url:l.data.url},error:null}}async refreshAttachmentUrl(e,t){return this.http.get(`/v1/chat/messages/${e}/attachment/${t}/url`)}getCachedMessages(e){return this.cache.getMessages(e)}stageOptimisticMessage(e,t){return this.cache.upsertMessage(e,t),t}async addReaction(e,t){return this.http.post(`/v1/chat/messages/${e}/reactions`,{emoji:t})}async removeReaction(e,t){return this.http.del(`/v1/chat/messages/${e}/reactions/${encodeURIComponent(t)}`)}async reportMessage(e,t,i){return this.http.post(`/v1/chat/messages/${e}/report`,{reason:t,description:i})}async muteConversation(e,t){return this.http.post(`/v1/chat/conversations/${e}/mute`,{muted_until:t})}async unmuteConversation(e){return this.http.del(`/v1/chat/conversations/${e}/mute`)}async getUnreadTotal(){return this.http.get("/v1/chat/conversations/unread-total")}async sendTyping(e,t=!0){await this.http.post(`/v1/chat/conversations/${e}/typing`,{is_typing:t})}async markRead(e){await this.http.post(`/v1/chat/conversations/${e}/read`)}async getReadStatus(e){return this.http.get(`/v1/chat/conversations/${e}/read-status`)}async addParticipant(e,t){return this.http.post(`/v1/chat/conversations/${e}/participants`,{user_id:t})}async removeParticipant(e,t){return this.http.del(`/v1/chat/conversations/${e}/participants/${t}`)}joinPresence(e,t){let i=this.channelName(e);this.ws.joinPresence(i,t)}leavePresence(e){let t=this.channelName(e);this.ws.leavePresence(t)}updatePresence(e,t,i){let n=this.channelName(e);this.ws.send({type:"presence_update",channel:n,status:t,user_data:i})}async getChannelSettings(e){return this.http.get(`/v1/chat/channels/${e}/settings`)}setConversationType(e,t){this.conversationTypes.set(e,t)}async findChannelBySessionId(e){let t=await this.http.get(`/v1/chat/channels/by-session?linked_session_id=${encodeURIComponent(e)}`);return t.error?.status===404?null:(t.data&&this.conversationTypes.set(t.data.id,t.data.channel_type),t.data)}async joinChannel(e){return this.http.post(`/v1/chat/channels/${e}/join`,{})}async createEphemeralChannel(e){let t=await this.http.post("/v1/chat/channels/ephemeral",e);return t.data&&this.conversationTypes.set(t.data.id,"ephemeral"),t}async createLargeRoom(e){let t=await this.http.post("/v1/chat/channels/large-room",e);return t.data&&this.conversationTypes.set(t.data.id,"large_room"),t}async getSubscriberCount(e){return(await this.http.get(`/v1/chat/conversations/${e}/subscriber-count`)).data?.count??0}subscribeToConversation(e){if(this.conversationSubs.has(e))return this.conversationSubs.get(e);let t=this.channelName(e),i=this.ws.subscribe(t);return this.conversationSubs.set(e,i),()=>{this.conversationSubs.delete(e),i()}}channelName(e){switch(this.conversationTypes.get(e)){case"large_room":return`conversation:lr:${e}`;case"broadcast":return`conversation:bc:${e}`;case"support":return`conversation:support:${e}`;default:return`conversation:${e}`}}destroy(){this.disconnect(),this.cache.clear(),this.removeAllListeners()}handleRealtimeMessage(e,t){if(e.startsWith("conversation:")){this.handleConversationMessage(e,t);return}if(e.startsWith("private:")){this.handlePrivateMessage(t);return}}handlePrivateMessage(e){let t=e;if(!t)return;let i=t.event??t.type,n=t.data??t;switch(i){case"new_message":{let r=n.conversation_id,a=n.id??n.message_id,o=n.sender_id,l=n.content??"";r&&this.emit("inbox:update",{conversationId:r,messageId:a,senderId:o,preview:l});break}case"support:new_conversation":{let r=n.conversation_id,a=n.visitor_name;r&&this.emit("support:new",{conversationId:r,visitorName:a});break}case"support:assigned":{let r=n.conversation_id,a=n.visitor_name,o=n.visitor_email;r&&this.emit("support:assigned",{conversationId:r,visitorName:a,visitorEmail:o});break}}}normalizeMessage(e){return{id:e.id??e.message_id??"",content:e.content??"",message_type:e.message_type??"text",sender_id:e.sender_id??e.sender_user_id??"",sender_type:e.sender_type,sender_agent_model:e.sender_agent_model,attachments:e.attachments,reactions:e.reactions,is_edited:!!(e.is_edited??!1),created_at:e.created_at??e.updated_at??e.timestamp??new Date().toISOString()}}buildEditedMessage(e,t){let i=t.message_id??t.id;if(!i)return null;let n=this.cache.getMessage(e,i);return{id:n?.id??i,content:t.content??t.new_content??n?.content??"",message_type:n?.message_type??"text",sender_id:n?.sender_id??"",sender_type:n?.sender_type,sender_agent_model:n?.sender_agent_model,attachments:n?.attachments,reactions:n?.reactions,is_edited:!0,created_at:n?.created_at??t.updated_at??t.timestamp??new Date().toISOString()}}applyReactionEvent(e,t){let i={id:`${t.message_id}:${t.user_id}:${t.emoji}`,message_id:t.message_id,user_id:t.user_id,emoji:t.emoji,action:t.action,timestamp:t.timestamp},n=this.cache.getMessage(e,t.message_id);if(!n)return i;let r=[...n.reactions??[]],a=r.findIndex(o=>o.emoji===t.emoji);if(t.action==="added")if(a>=0){let o=r[a];if(!o.user_ids.includes(t.user_id)){let l=[...o.user_ids,t.user_id];r[a]={...o,user_ids:l,count:l.length}}}else r.push({emoji:t.emoji,count:1,user_ids:[t.user_id]});else if(a>=0){let o=r[a],l=o.user_ids.filter(h=>h!==t.user_id);l.length===0?r.splice(a,1):r[a]={...o,user_ids:l,count:l.length}}return this.cache.updateMessage(e,{...n,reactions:r}),i}handleConversationMessage(e,t){let i=e.replace(/^conversation:(?:lr:|bc:|support:)?/,""),n=t;if(!n)return;let r=n.event??n.type,a=n.data??n;switch(r){case"new_message":{let o=this.normalizeMessage(a),l=this.cache.reconcileOptimisticMessage(i,o);this.cache.upsertMessage(i,l),this.emit("message",{message:l,conversationId:i});break}case"message_edited":{let o=a,l=this.buildEditedMessage(i,o);l&&(this.cache.upsertMessage(i,l),this.emit("message:updated",{message:l,conversationId:i,update:o}));break}case"reaction":{let o=a;if(!o.message_id||!o.user_id||!o.emoji)break;let l=this.applyReactionEvent(i,o);this.emit("reaction",{reaction:l,conversationId:i,action:o.action});break}case"message_deleted":{let o=a.message_id??a.id;o&&(this.cache.removeMessage(i,o),this.emit("message:deleted",{messageId:o,conversationId:i}));break}case"user_typing":{let o=a.user_id;o&&this.emit("typing",{userId:o,conversationId:i});break}case"user_stopped_typing":{let o=a.user_id;o&&this.emit("typing:stop",{userId:o,conversationId:i});break}case"typing_batch":{let o=a.users??[];for(let l of o)this.emit("typing",{userId:l,conversationId:i});break}case"messages_read":{let o=a.user_id,l=a.last_read_at;o&&l&&this.emit("read",{userId:o,conversationId:i,lastReadAt:l});break}case"room_upgraded":{if(a.new_type==="large_room"){this.conversationTypes.set(i,"large_room");let l=this.conversationSubs.get(i);l&&(l(),this.conversationSubs.delete(i),this.subscribeToConversation(i)),this.emit("room_upgraded",{conversationId:i,newType:"large_room"})}break}}}async flushOfflineQueue(){let e=this.offlineQueue.drain();for(let t of e)await this.sendMessage(t.conversationId,{content:t.content,message_type:t.message_type,attachments:t.attachments})}};var ne="sm_support_",C=class{constructor(s){this.chatClient=null;this.refreshToken=null;this.accessToken=null;this.tokenExpiresAt=0;this.userId=null;this.apiKey=s.apiKey,this.apiBaseUrl=s.apiBaseUrl??"https://api.scalemule.com",this.wsUrl=s.wsUrl,this.storageKey=ne+s.apiKey.substring(0,8);let e=this.loadState();e?(this.anonymousId=e.anonymous_id,this.refreshToken=e.refresh_token,this.userId=e.user_id):this.anonymousId=crypto.randomUUID()}async initVisitorSession(s){if(this.visitorName=s?.name,this.visitorEmail=s?.email,this.refreshToken)try{await this.refreshAccessToken(),this.initChatClient();return}catch{this.refreshToken=null}let e=await fetch(`${this.apiBaseUrl}/v1/auth/visitor-session`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":this.apiKey},body:JSON.stringify({anonymous_id:this.anonymousId,name:s?.name,email:s?.email,page_url:typeof location<"u"?location.href:void 0})});if(!e.ok){let n=await e.text();throw new Error(`Visitor session failed: ${e.status} ${n}`)}let i=(await e.json()).data;this.accessToken=i.access_token,this.refreshToken=i.refresh_token,this.userId=i.user_id,this.tokenExpiresAt=Date.now()+i.expires_in*1e3,this.saveState(),this.initChatClient()}async startConversation(s,e){if(!this.accessToken)throw new Error("Call initVisitorSession() first");let t=await fetch(`${this.apiBaseUrl}/v1/chat/support/conversations`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":this.apiKey,Authorization:`Bearer ${this.accessToken}`},body:JSON.stringify({message:s,name:this.visitorName,email:this.visitorEmail,page_url:e?.page_url??(typeof location<"u"?location.href:void 0),user_agent:typeof navigator<"u"?navigator.userAgent:void 0,attachments:e?.attachments,metadata:e?.metadata})});if(!t.ok){let r=await t.text();throw new Error(`Create support conversation failed: ${t.status} ${r}`)}let n=(await t.json()).data;return this.chatClient&&this.chatClient.conversationTypes?.set(n.conversation_id,"support"),n}async getActiveConversation(){if(!this.accessToken)return null;let s=await fetch(`${this.apiBaseUrl}/v1/chat/support/conversations/mine`,{headers:{"x-api-key":this.apiKey,Authorization:`Bearer ${this.accessToken}`}});if(!s.ok)return null;let i=(await s.json()).data.find(n=>n.status==="active"||n.status==="waiting");return i&&this.chatClient&&this.chatClient.conversationTypes?.set(i.conversation_id,"support"),i??null}async getWidgetConfig(){let s=await fetch(`${this.apiBaseUrl}/v1/chat/support/widget/config`,{headers:{"x-api-key":this.apiKey}});if(!s.ok){let t=await s.text();throw new Error(`Get widget config failed: ${s.status} ${t}`)}return(await s.json()).data}get chat(){if(!this.chatClient)throw new Error("Call initVisitorSession() first");return this.chatClient}get isInitialized(){return this.chatClient!==null}connect(){this.chat.connect()}disconnect(){this.chat.disconnect()}get visitorUserId(){return this.userId}destroy(){this.chatClient?.destroy(),this.chatClient=null}initChatClient(){this.chatClient&&this.chatClient.destroy();let s={apiKey:this.apiKey,apiBaseUrl:this.apiBaseUrl,wsUrl:this.wsUrl,getToken:async()=>{if(!this.accessToken)return null;if(Date.now()>this.tokenExpiresAt-6e4)try{await this.refreshAccessToken()}catch{}return this.accessToken}};this.chatClient=new S(s)}async refreshAccessToken(){if(!this.refreshToken)throw new Error("No refresh token");let s=await fetch(`${this.apiBaseUrl}/v1/auth/token/refresh`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":this.apiKey},body:JSON.stringify({refresh_token:this.refreshToken})});if(!s.ok)throw new Error(`Token refresh failed: ${s.status}`);let t=(await s.json()).data;this.accessToken=t.access_token,this.tokenExpiresAt=Date.now()+t.expires_in*1e3}loadState(){try{let s=localStorage.getItem(this.storageKey);return s?JSON.parse(s):null}catch{return null}}saveState(){try{localStorage.setItem(this.storageKey,JSON.stringify({anonymous_id:this.anonymousId,refresh_token:this.refreshToken,user_id:this.userId}))}catch{}}};var P='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="28" height="28"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H5.17L4 17.17V4h16v12z"/><path d="M7 9h2v2H7zm4 0h2v2h-2zm4 0h2v2h-2z"/></svg>',L='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>',U='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>',$='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6z"/></svg>',E='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="18" height="18"><path d="M16.5 6.5v9.25a4.75 4.75 0 1 1-9.5 0V5.75a3.25 3.25 0 1 1 6.5 0V14a1.75 1.75 0 1 1-3.5 0V6.5H8.5V14a3.25 3.25 0 1 0 6.5 0V5.75a4.75 4.75 0 1 0-9.5 0v10a6.25 6.25 0 1 0 12.5 0V6.5h-1.5Z"/></svg>',O='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="18" height="18"><path d="M12 22a10 10 0 1 1 10-10 10.01 10.01 0 0 1-10 10Zm0-18.5a8.5 8.5 0 1 0 8.5 8.5A8.51 8.51 0 0 0 12 3.5Zm-3 7a1.25 1.25 0 1 1 1.25-1.25A1.25 1.25 0 0 1 9 10.5Zm6 0a1.25 1.25 0 1 1 1.25-1.25A1.25 1.25 0 0 1 15 10.5Zm-3 6.25A5.22 5.22 0 0 1 7.58 14h1.71a3.5 3.5 0 0 0 5.42 0h1.71A5.22 5.22 0 0 1 12 16.75Z"/></svg>';var D=`
|
|
2
2
|
:host {
|
|
3
3
|
all: initial;
|
|
4
4
|
--sm-primary: #2563eb;
|
|
5
5
|
--sm-primary-hover: #1d4ed8;
|
|
6
6
|
--sm-primary-disabled: #93c5fd;
|
|
7
7
|
--sm-primary-text: #ffffff;
|
|
8
|
+
--sm-visitor-bubble: var(--sm-primary);
|
|
9
|
+
--sm-visitor-text: var(--sm-primary-text);
|
|
10
|
+
--sm-rep-bubble: #f3f4f6;
|
|
11
|
+
--sm-rep-text: #1f2937;
|
|
12
|
+
--sm-bubble-radius: 16px;
|
|
13
|
+
--sm-panel-width: 380px;
|
|
14
|
+
--sm-panel-height: 560px;
|
|
8
15
|
--sm-badge-bg: #ef4444;
|
|
9
16
|
--sm-bubble-left: auto;
|
|
10
17
|
--sm-bubble-right: 20px;
|
|
11
18
|
--sm-panel-left: auto;
|
|
12
19
|
--sm-panel-right: 20px;
|
|
13
|
-
--sm-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
20
|
+
--sm-font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
21
|
+
--sm-font-size: 14px;
|
|
14
22
|
font-family: var(--sm-font-family);
|
|
15
|
-
font-size:
|
|
23
|
+
font-size: var(--sm-font-size);
|
|
16
24
|
line-height: 1.5;
|
|
17
25
|
color: #111827;
|
|
18
26
|
}
|
|
@@ -70,8 +78,8 @@
|
|
|
70
78
|
bottom: 90px;
|
|
71
79
|
left: var(--sm-panel-left);
|
|
72
80
|
right: var(--sm-panel-right);
|
|
73
|
-
width:
|
|
74
|
-
max-height: min(
|
|
81
|
+
width: var(--sm-panel-width);
|
|
82
|
+
max-height: min(var(--sm-panel-height), calc(100vh - 110px));
|
|
75
83
|
background: #ffffff;
|
|
76
84
|
border-radius: 18px;
|
|
77
85
|
box-shadow: 0 24px 48px rgba(15, 23, 42, 0.18);
|
|
@@ -276,6 +284,7 @@
|
|
|
276
284
|
display: flex;
|
|
277
285
|
flex-direction: column;
|
|
278
286
|
background: #f8fafc;
|
|
287
|
+
position: relative;
|
|
279
288
|
}
|
|
280
289
|
|
|
281
290
|
.sm-chat-shell.sm-dragging {
|
|
@@ -349,11 +358,11 @@
|
|
|
349
358
|
|
|
350
359
|
.sm-msg-bubble {
|
|
351
360
|
padding: 10px 12px;
|
|
352
|
-
border-radius:
|
|
361
|
+
border-radius: var(--sm-bubble-radius);
|
|
353
362
|
line-height: 1.4;
|
|
354
363
|
word-break: break-word;
|
|
355
|
-
background:
|
|
356
|
-
color:
|
|
364
|
+
background: var(--sm-rep-bubble);
|
|
365
|
+
color: var(--sm-rep-text);
|
|
357
366
|
}
|
|
358
367
|
|
|
359
368
|
.sm-msg-content {
|
|
@@ -361,8 +370,8 @@
|
|
|
361
370
|
}
|
|
362
371
|
|
|
363
372
|
.sm-msg-visitor .sm-msg-bubble {
|
|
364
|
-
background: var(--sm-
|
|
365
|
-
color: var(--sm-
|
|
373
|
+
background: var(--sm-visitor-bubble);
|
|
374
|
+
color: var(--sm-visitor-text);
|
|
366
375
|
border-bottom-right-radius: 6px;
|
|
367
376
|
}
|
|
368
377
|
|
|
@@ -620,6 +629,22 @@
|
|
|
620
629
|
text-decoration: underline;
|
|
621
630
|
}
|
|
622
631
|
|
|
632
|
+
.sm-drag-overlay {
|
|
633
|
+
position: absolute;
|
|
634
|
+
inset: 0;
|
|
635
|
+
background: rgba(37, 99, 235, 0.08);
|
|
636
|
+
border: 2px dashed var(--sm-primary);
|
|
637
|
+
border-radius: 12px;
|
|
638
|
+
display: flex;
|
|
639
|
+
align-items: center;
|
|
640
|
+
justify-content: center;
|
|
641
|
+
font-size: 13px;
|
|
642
|
+
font-weight: 600;
|
|
643
|
+
color: var(--sm-primary);
|
|
644
|
+
pointer-events: none;
|
|
645
|
+
z-index: 10;
|
|
646
|
+
}
|
|
647
|
+
|
|
623
648
|
@media (max-width: 440px) {
|
|
624
649
|
.sm-panel {
|
|
625
650
|
left: 0;
|
|
@@ -630,7 +655,7 @@
|
|
|
630
655
|
border-radius: 18px 18px 0 0;
|
|
631
656
|
}
|
|
632
657
|
}
|
|
633
|
-
`;var re="sm_widget_";function N(c,s){try{localStorage.setItem(re+c,s)}catch{}}var oe=["\u{1F44D}","\u2764\uFE0F","\u{1F602}","\u{1F389}","\u{1F62E}","\u{1F440}"],
|
|
658
|
+
`;var re="sm_widget_";function N(c,s){try{localStorage.setItem(re+c,s)}catch{}}var oe=["\u{1F44D}","\u2764\uFE0F","\u{1F602}","\u{1F389}","\u{1F62E}","\u{1F440}"],le={"image/":10*1024*1024,"video/":25*1024*1024,"audio/":5*1024*1024};function ce(c){for(let[s,e]of Object.entries(le))if(c.type.startsWith(s)&&c.size>e){let t=e/1048576;return`${c.name} exceeds ${t}MB limit`}return null}var p={title:"Support",subtitle:"We typically reply within a few minutes",primary_color:"#2563eb",position:"right",pre_chat_fields:[{key:"name",label:"Name",type:"text",required:!0},{key:"email",label:"Email",type:"email",required:!1}],business_hours:{},realtime_enabled:!1,welcome_message:"Hi! How can we help?",offline_message:"We're currently offline. Leave a message!",reps_online:!1,online_count:0};function d(c){return String(c??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function T(c=""){return{conversationId:c,messages:[],readStatuses:[],typingUsers:[],members:[],hasMore:!1,isLoading:!1,error:null}}function de(c){try{return new Date(c).toLocaleTimeString(void 0,{hour:"numeric",minute:"2-digit"})}catch{return""}}function he(c){try{return new Date(c).toLocaleDateString(void 0,{month:"short",day:"numeric",year:"numeric"})}catch{return""}}function pe(c,s){let e=new Date(c),t=new Date(s);return e.getFullYear()===t.getFullYear()&&e.getMonth()===t.getMonth()&&e.getDate()===t.getDate()}function F(c,s){let e=c.replace("#","");if(!/^[0-9a-fA-F]{6}$/.test(e))return c;let t=Number.parseInt(e,16),i=t>>16&255,n=t>>8&255,r=t&255,a=o=>Math.max(0,Math.min(255,Math.round(o+s*255))).toString(16).padStart(2,"0");return`#${a(i)}${a(n)}${a(r)}`}function B(c,s){return s.length?!c&&s.every(e=>e.mime_type.startsWith("image/"))?"image":"file":"text"}function ue(c,s){return s?c.find(e=>e.user_id===s)?.last_read_at??null:null}function z(c,s,e){return{id:`pending-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,sender_id:c,sender_type:"human",content:s,message_type:B(s,e),attachments:e,reactions:[],is_edited:!1,created_at:new Date().toISOString()}}function me(c,s){let e=d(s.file_name),t=d(s.presigned_url),i=d(s.file_id);return s.presigned_url?s.mime_type.startsWith("image/")?`<img class="sm-attachment sm-attachment-image" src="${t}" alt="${e}" loading="lazy" />`:s.mime_type.startsWith("video/")?`<video class="sm-attachment sm-attachment-video" src="${t}" controls preload="metadata"></video>`:s.mime_type.startsWith("audio/")?`<audio class="sm-attachment sm-attachment-audio" src="${t}" controls preload="metadata"></audio>`:`<a class="sm-attachment sm-attachment-link" href="${t}" target="_blank" rel="noreferrer">${e}</a>`:`<div class="sm-attachment sm-attachment-link" data-file-id="${i}" data-message-id="${d(c)}">${e}</div>`}var k=class{constructor(s,e,t={}){this.config=p;this.configLoaded=!1;this.conversation=null;this.controller=null;this.runtimeCleanups=[];this.runtimeState=T();this.panelEl=null;this.bodyEl=null;this.messagesEl=null;this.typingEl=null;this.uploadListEl=null;this.inputEl=null;this.fileInputEl=null;this.chatShellEl=null;this.errorBannerEl=null;this.isOpen=!1;this.unreadCount=0;this.unreadMarkerTimestamp=null;this.shouldScrollToUnread=!1;this.fallbackNotice=null;this.activeErrorMessage=null;this.realtimeFallbackActive=!1;this.pendingAttachments=[];this.openReactionMessageId=null;this.pollInterval=null;this.typingStopTimer=null;this.realtimeStatusCleanups=[];this.client=new C({apiKey:s,apiBaseUrl:e}),this.options=t,this.root=document.createElement("div"),this.root.id="scalemule-support-widget",this.shadow=this.root.attachShadow({mode:"closed"}),this.applyConfigTheme();let i=document.createElement("style");i.textContent=D,this.shadow.appendChild(i),this.renderBubble(),document.body.appendChild(this.root)}applyConfigTheme(){let s=this.options.color??this.config.primary_color??p.primary_color,e=this.options.position??this.config.position??p.position,t=F(s,-.12),i=F(s,.22);this.root.style.setProperty("--sm-primary",s),this.root.style.setProperty("--sm-primary-hover",t),this.root.style.setProperty("--sm-primary-disabled",i),this.root.style.setProperty("--sm-position",e),this.root.style.setProperty("--sm-bubble-left",e==="left"?"20px":"auto"),this.root.style.setProperty("--sm-bubble-right",e==="right"?"20px":"auto"),this.root.style.setProperty("--sm-panel-left",e==="left"?"20px":"auto"),this.root.style.setProperty("--sm-panel-right",e==="right"?"20px":"auto")}async ensureConfigLoaded(){if(!this.configLoaded){try{let s=await this.client.getWidgetConfig();this.config={...p,...s,title:this.options.title??s.title??p.title,primary_color:this.options.color??s.primary_color??p.primary_color,position:this.options.position??s.position??p.position}}catch{this.config={...p,title:this.options.title??p.title,primary_color:this.options.color??p.primary_color,position:this.options.position??p.position}}this.configLoaded=!0,this.applyConfigTheme()}}renderBubble(){let s=document.createElement("button");s.className="sm-bubble",s.innerHTML=P,s.setAttribute("aria-label","Open support chat"),s.addEventListener("click",()=>{this.toggle()}),this.shadow.appendChild(s)}renderPanel(){if(this.panelEl)return;let s=document.createElement("div");s.className="sm-panel sm-hidden",s.innerHTML=`
|
|
634
659
|
<div class="sm-header">
|
|
635
660
|
<div class="sm-header-copy">
|
|
636
661
|
<div class="sm-header-title"></div>
|
|
@@ -648,7 +673,7 @@
|
|
|
648
673
|
<div class="sm-error" hidden></div>
|
|
649
674
|
<div class="sm-body"></div>
|
|
650
675
|
<div class="sm-footer">Powered by <a href="https://scalemule.com" target="_blank" rel="noopener">ScaleMule</a></div>
|
|
651
|
-
`,s.querySelector(".sm-minimize-btn").addEventListener("click",()=>this.minimize()),s.querySelector(".sm-close-btn").addEventListener("click",()=>this.minimize()),this.panelEl=s,this.bodyEl=s.querySelector(".sm-body"),this.errorBannerEl=s.querySelector(".sm-error"),this.shadow.appendChild(s),this.updateHeader()}updateHeader(){if(!this.panelEl)return;let s=this.panelEl.querySelector(".sm-header-title"),e=this.panelEl.querySelector(".sm-header-subtitle"),t=this.panelEl.querySelector(".sm-status-dot"),i=this.panelEl.querySelector(".sm-status-label");if(!s||!e||!t||!i)return;let n=this.client.visitorUserId,a=this.runtimeState.members.filter(h=>h.userId!==n).map(h=>h.status),o=this.config.reps_online,l=this.config.reps_online?"Online":"Away";a.length&&(a.some(h=>h==="online")?(o=!0,l="Online"):(a.some(h=>h==="away"||h==="dnd"),o=!1,l="Away")),s.textContent=this.config.title,e.textContent=o?this.config.subtitle:this.config.offline_message,i.textContent=o?l:"We'll respond soon",t.className=`sm-status-dot ${o?"sm-status-dot-online":"sm-status-dot-away"}`}renderError(s){this.activeErrorMessage=s,this.syncErrorBanner()}setFallbackNotice(s){this.fallbackNotice=s,this.syncErrorBanner()}syncErrorBanner(){if(!this.errorBannerEl)return;let s=this.activeErrorMessage??this.fallbackNotice;if(!s){this.errorBannerEl.hidden=!0,this.errorBannerEl.textContent="";return}this.errorBannerEl.hidden=!1,this.errorBannerEl.textContent=s}getConfiguredPreChatFields(){return this.config.pre_chat_fields?.length?this.config.pre_chat_fields.filter(s=>s.key!=="message"):
|
|
676
|
+
`,s.querySelector(".sm-minimize-btn").addEventListener("click",()=>this.minimize()),s.querySelector(".sm-close-btn").addEventListener("click",()=>this.minimize()),this.panelEl=s,this.bodyEl=s.querySelector(".sm-body"),this.errorBannerEl=s.querySelector(".sm-error"),this.shadow.appendChild(s),this.updateHeader()}updateHeader(){if(!this.panelEl)return;let s=this.panelEl.querySelector(".sm-header-title"),e=this.panelEl.querySelector(".sm-header-subtitle"),t=this.panelEl.querySelector(".sm-status-dot"),i=this.panelEl.querySelector(".sm-status-label");if(!s||!e||!t||!i)return;let n=this.client.visitorUserId,a=this.runtimeState.members.filter(h=>h.userId!==n).map(h=>h.status),o=this.config.reps_online,l=this.config.reps_online?"Online":"Away";a.length&&(a.some(h=>h==="online")?(o=!0,l="Online"):(a.some(h=>h==="away"||h==="dnd"),o=!1,l="Away")),s.textContent=this.config.title,e.textContent=o?this.config.subtitle:this.config.offline_message,i.textContent=o?l:"We'll respond soon",t.className=`sm-status-dot ${o?"sm-status-dot-online":"sm-status-dot-away"}`}renderError(s){this.activeErrorMessage=s,this.syncErrorBanner()}setFallbackNotice(s){this.fallbackNotice=s,this.syncErrorBanner()}syncErrorBanner(){if(!this.errorBannerEl)return;let s=this.activeErrorMessage??this.fallbackNotice;if(!s){this.errorBannerEl.hidden=!0,this.errorBannerEl.textContent="";return}this.errorBannerEl.hidden=!1,this.errorBannerEl.textContent=s}getConfiguredPreChatFields(){return this.config.pre_chat_fields?.length?this.config.pre_chat_fields.filter(s=>s.key!=="message"):p.pre_chat_fields}renderPreChatForm(){if(!this.bodyEl)return;let s=this.getConfiguredPreChatFields().map(e=>{let t=`sm-prechat-${e.key}`,i=e.required?"required":"",n=`${d(e.label)}${e.required?" *":""}`,r=`id="${t}" data-prechat-key="${d(e.key)}" placeholder="${d(e.label)}" ${i}`,a=e.type==="textarea"?`<textarea ${r}></textarea>`:`<input type="${d(e.type||"text")}" ${r} />`;return`
|
|
652
677
|
<div class="sm-field">
|
|
653
678
|
<label for="${t}">${n}</label>
|
|
654
679
|
${a}
|
|
@@ -687,33 +712,33 @@
|
|
|
687
712
|
<span class="sm-upload-progress">${d(s.error??`${s.progress}%`)}</span>
|
|
688
713
|
<button class="sm-upload-remove" type="button" data-pending-id="${d(s.id)}" aria-label="Remove attachment">\xD7</button>
|
|
689
714
|
</div>
|
|
690
|
-
`).join(""),this.uploadListEl.querySelectorAll(".sm-upload-remove").forEach(s=>{s.addEventListener("click",()=>{let e=s.dataset.pendingId;e&&(this.pendingAttachments=this.pendingAttachments.filter(t=>t.id!==e),this.renderPendingAttachments())})})}}renderMessages(){if(!this.messagesEl)return;let s=this.client.visitorUserId,e=this.runtimeState.messages,t=!1;if(this.messagesEl.innerHTML=e.map((i,n)=>{let r=e[n-1],a=!r||!
|
|
715
|
+
`).join(""),this.uploadListEl.querySelectorAll(".sm-upload-remove").forEach(s=>{s.addEventListener("click",()=>{let e=s.dataset.pendingId;e&&(this.pendingAttachments=this.pendingAttachments.filter(t=>t.id!==e),this.renderPendingAttachments())})})}}renderMessages(){if(!this.messagesEl)return;let s=this.client.visitorUserId,e=this.runtimeState.messages,t=!1;if(this.messagesEl.innerHTML=e.map((i,n)=>{let r=e[n-1],a=!r||!pe(r.created_at,i.created_at),o=i.sender_id===s,l=i.sender_type==="system"||i.message_type==="system",h=(i.attachments??[]).map(m=>me(i.id,m)).join(""),f=!t&&!o&&!l&&this.unreadMarkerTimestamp!==null&&new Date(i.created_at).getTime()>new Date(this.unreadMarkerTimestamp).getTime();f&&(t=!0);let u=(i.reactions??[]).map(m=>{let v=!!(s&&m.user_ids.includes(s));return`
|
|
691
716
|
<button
|
|
692
717
|
type="button"
|
|
693
718
|
class="sm-reaction-badge ${v?"sm-reaction-badge-active":""}"
|
|
694
719
|
data-action="set-reaction"
|
|
695
720
|
data-message-id="${d(i.id)}"
|
|
696
|
-
data-emoji="${d(
|
|
721
|
+
data-emoji="${d(m.emoji)}"
|
|
697
722
|
data-reacted="${v?"true":"false"}"
|
|
698
723
|
>
|
|
699
|
-
${d(
|
|
724
|
+
${d(m.emoji)} ${m.count}
|
|
700
725
|
</button>
|
|
701
|
-
`}).join(""),
|
|
726
|
+
`}).join(""),H=this.openReactionMessageId===i.id?`
|
|
702
727
|
<div class="sm-reaction-picker">
|
|
703
|
-
${oe.map(
|
|
728
|
+
${oe.map(m=>`
|
|
704
729
|
<button
|
|
705
730
|
type="button"
|
|
706
731
|
class="sm-reaction-picker-btn"
|
|
707
732
|
data-action="set-reaction"
|
|
708
733
|
data-message-id="${d(i.id)}"
|
|
709
|
-
data-emoji="${d(
|
|
710
|
-
data-reacted="${i.reactions?.some(v=>v.emoji===
|
|
734
|
+
data-emoji="${d(m)}"
|
|
735
|
+
data-reacted="${i.reactions?.some(v=>v.emoji===m&&!!(s&&v.user_ids.includes(s)))?"true":"false"}"
|
|
711
736
|
>
|
|
712
|
-
${d(
|
|
737
|
+
${d(m)}
|
|
713
738
|
</button>
|
|
714
739
|
`).join("")}
|
|
715
740
|
</div>
|
|
716
|
-
`:"",q=a?`<div class="sm-date-divider">${d(
|
|
741
|
+
`:"",q=a?`<div class="sm-date-divider">${d(he(i.created_at))}</div>`:"",W=f?'<div class="sm-unread-divider" id="sm-unread-divider"><span>New messages</span></div>':"",j=!l&&!o?`<button type="button" class="sm-msg-action" data-action="toggle-picker" data-message-id="${d(i.id)}" aria-label="Add reaction">${O}</button>`:"";return`
|
|
717
742
|
${q}
|
|
718
743
|
${W}
|
|
719
744
|
<div class="sm-msg ${l?"sm-msg-system":o?"sm-msg-visitor":"sm-msg-rep"}">
|
|
@@ -724,8 +749,8 @@
|
|
|
724
749
|
</div>
|
|
725
750
|
${j}
|
|
726
751
|
</div>
|
|
727
|
-
${l?"":`<div class="sm-msg-time">${d(
|
|
728
|
-
${
|
|
729
|
-
${
|
|
752
|
+
${l?"":`<div class="sm-msg-time">${d(de(i.created_at))}${i.is_edited?" \xB7 edited":""}</div>`}
|
|
753
|
+
${u?`<div class="sm-reactions">${u}</div>`:""}
|
|
754
|
+
${H}
|
|
730
755
|
</div>
|
|
731
|
-
`}).join(""),this.shouldScrollToUnread){let i=this.messagesEl.querySelector("#sm-unread-divider");i?i.scrollIntoView({block:"center"}):this.messagesEl.scrollTop=this.messagesEl.scrollHeight,this.shouldScrollToUnread=!1}else this.messagesEl.scrollTop=this.messagesEl.scrollHeight}captureUnreadMarkerFromState(){let s=he(this.runtimeState.readStatuses,this.client.visitorUserId);if(!s){this.unreadMarkerTimestamp=null;return}let e=this.runtimeState.messages.some(t=>t.sender_id!==this.client.visitorUserId&&new Date(t.created_at).getTime()>new Date(s).getTime());this.unreadMarkerTimestamp=e?s:null,this.shouldScrollToUnread=e}clearUnreadMarkerIfViewed(){if(!this.unreadMarkerTimestamp||!this.messagesEl)return;let s=this.messagesEl.querySelector("#sm-unread-divider");s&&this.messagesEl.scrollTop>=s.offsetTop-24&&(this.unreadMarkerTimestamp=null,this.renderMessages())}async initializeVisitorSession(){this.client.isInitialized||await this.client.initVisitorSession()}async toggle(){if(this.isOpen){this.minimize();return}await this.ensureConfigLoaded(),this.renderPanel(),this.isOpen=!0,this.unreadCount=0,this.updateBadge(),this.panelEl?.classList.remove("sm-hidden");try{await this.initializeVisitorSession()}catch{this.renderError("Unable to initialize support chat")}let s=this.conversation??await this.client.getActiveConversation();s?(this.conversation=s,await this.ensureConversationRuntime(!0),this.renderChatView(),await this.markConversationRead()):(this.renderPreChatForm(),this.renderError(null))}minimize(){this.isOpen=!1,this.panelEl?.classList.add("sm-hidden")}async ensureConversationRuntime(s){if(this.conversation){if(this.renderError(null),this.config.realtime_enabled){await this.ensureRealtimeRuntime(s);return}this.realtimeFallbackActive=!1,this.cleanupRealtimeStatusWatchers(),this.setFallbackNotice(null),this.cleanupRealtimeRuntime(),await this.loadPollingSnapshot(s),this.startPolling()}}cleanupRealtimeRuntime(){this.controller&&(this.controller.destroy(),this.controller=null);for(let s of this.runtimeCleanups)s();this.runtimeCleanups=[]}cleanupRealtimeStatusWatchers(){for(let s of this.realtimeStatusCleanups)s();this.realtimeStatusCleanups=[]}ensureRealtimeStatusWatchers(){this.realtimeStatusCleanups.length||(this.realtimeStatusCleanups.push(this.client.chat.on("reconnecting",()=>{!this.config.realtime_enabled||!this.conversation||this.realtimeFallbackActive||this.setFallbackNotice("Realtime reconnecting\u2026")})),this.realtimeStatusCleanups.push(this.client.chat.on("disconnected",()=>{!this.config.realtime_enabled||!this.conversation||this.enterRealtimeFallback()})),this.realtimeStatusCleanups.push(this.client.chat.on("connected",()=>{if(!(!this.config.realtime_enabled||!this.conversation)){if(this.realtimeFallbackActive){this.restoreRealtimeRuntime();return}this.setFallbackNotice(null)}})))}async ensureRealtimeRuntime(s){if(this.conversation){if(this.ensureRealtimeStatusWatchers(),!this.controller||this.runtimeState.conversationId!==this.conversation.conversation_id){this.cleanupRealtimeRuntime(),this.stopPolling(),this.runtimeState=T(this.conversation.conversation_id),this.realtimeFallbackActive=!1,this.setFallbackNotice(null),this.controller=new b(this.client.chat,this.conversation.conversation_id),this.runtimeCleanups.push(this.controller.on("state",t=>{this.applyRuntimeState(t,!1)})),this.runtimeCleanups.push(this.controller.on("error",({message:t})=>{this.renderError(t)}));let e=await this.controller.init({realtime:!0,presence:!0});this.applyRuntimeState(e,s);return}s&&this.captureUnreadMarkerFromState()}}async enterRealtimeFallback(s="Realtime connection lost. Falling back to polling."){if(this.conversation){this.realtimeFallbackActive=!0,this.setFallbackNotice(s),this.cleanupRealtimeRuntime();try{await this.loadPollingSnapshot(!1),this.startPolling(),this.isOpen&&await this.markConversationRead()}catch(e){this.renderError(e instanceof Error?e.message:"Failed to refresh chat state")}}}async restoreRealtimeRuntime(){if(!(!this.conversation||!this.config.realtime_enabled))try{this.realtimeFallbackActive=!1,this.setFallbackNotice(null),await this.ensureRealtimeRuntime(!1)}catch{this.realtimeFallbackActive=!0,this.setFallbackNotice("Realtime reconnect failed. Staying on polling fallback."),this.startPolling()}}applyRuntimeState(s,e){let t=new Set(this.runtimeState.messages.map(n=>n.id)),i=s.messages.filter(n=>!t.has(n.id)&&n.sender_id!==this.client.visitorUserId&&n.sender_type!=="system").length;this.runtimeState=s,e&&this.captureUnreadMarkerFromState(),!this.isOpen&&i>0&&(this.unreadCount+=i,this.updateBadge()),this.updateHeader(),this.renderError(s.error),this.conversation&&this.panelEl&&this.bodyEl?.dataset.view==="chat"&&this.renderChatView(),this.isOpen&&i>0&&this.markConversationRead()}async loadPollingSnapshot(s){if(!this.conversation)return;let[e,t]=await Promise.all([this.client.chat.getMessages(this.conversation.conversation_id,{limit:50}),this.client.chat.getReadStatus(this.conversation.conversation_id)]),i={conversationId:this.conversation.conversation_id,messages:e.data?.messages??this.runtimeState.messages,readStatuses:t.data?.statuses??this.runtimeState.readStatuses,typingUsers:[],members:[],hasMore:e.data?.has_more??!1,isLoading:!1,error:e.error?.message??t.error?.message??null};this.applyRuntimeState(i,s)}startPolling(){this.pollInterval||!this.conversation||(this.pollInterval=setInterval(()=>{this.pollForUpdates()},3e4))}stopPolling(){this.pollInterval&&(clearInterval(this.pollInterval),this.pollInterval=null)}async pollForUpdates(){!this.conversation||this.config.realtime_enabled&&!this.realtimeFallbackActive||(await this.loadPollingSnapshot(!1),this.isOpen&&await this.markConversationRead())}getReadyAttachments(){return this.pendingAttachments.filter(s=>s.attachment).map(s=>s.attachment)}async handleFilesSelected(s){if(s.length){try{await this.initializeVisitorSession()}catch{this.renderError("Unable to prepare attachment upload");return}for(let e of s){let t=`${e.name}:${e.size}:${Date.now()}:${Math.random().toString(36).slice(2)}`;this.pendingAttachments=[...this.pendingAttachments,{id:t,fileName:e.name,progress:0}],this.renderPendingAttachments();let i=await this.client.chat.uploadAttachment(e,n=>{this.pendingAttachments=this.pendingAttachments.map(r=>r.id===t?{...r,progress:n}:r),this.renderPendingAttachments()});this.pendingAttachments=this.pendingAttachments.map(n=>n.id!==t?n:i.data?{...n,progress:100,attachment:i.data}:{...n,error:i.error?.message??"Upload failed"}),this.renderPendingAttachments()}}}collectPreChatValues(){let s={},e,t,i=!0;for(let n of this.getConfiguredPreChatFields()){let r=this.bodyEl?.querySelector(`[data-prechat-key="${n.key}"]`),a=r?.value.trim()??"";if(n.required&&!a){i=!1,r?.focus();break}a&&(n.key==="name"?e=a:n.key==="email"?t=a:s[n.key]=a)}return{name:e,email:t,metadata:s,valid:i}}async handleStartChat(){let s=this.bodyEl?.querySelector("#sm-start-btn"),t=this.bodyEl?.querySelector("#sm-prechat-message")?.value.trim()??"",i=this.getReadyAttachments(),{name:n,email:r,metadata:a,valid:o}=this.collectPreChatValues();if(!o||!t&&!i.length||!s){this.renderError("Please complete the required fields before starting the chat.");return}s.disabled=!0,s.textContent="Connecting...";try{await this.client.initVisitorSession({name:n,email:r}),this.conversation=await this.client.startConversation(t,{page_url:typeof location<"u"?location.href:void 0,attachments:i,metadata:a}),N("conversation_id",this.conversation.conversation_id),this.pendingAttachments=[],this.runtimeState={...T(this.conversation.conversation_id),messages:[B(this.client.visitorUserId??"visitor",t,i)]},this.unreadMarkerTimestamp=null,this.shouldScrollToUnread=!1,await this.ensureConversationRuntime(!1),this.renderChatView(),await this.markConversationRead()}catch(l){this.renderError(l instanceof Error?l.message:"Failed to start chat"),s.disabled=!1,s.textContent="Start Chat"}}async handleSendMessage(){if(!this.conversation)return;let s=this.inputEl?.value.trim()??"",e=this.getReadyAttachments();if(!s&&!e.length)return;this.inputEl.value="",this.inputEl.style.height="auto",this.pendingAttachments=[],this.renderPendingAttachments();let t=B(this.client.visitorUserId??"visitor",s,e);this.controller?this.controller.stageOptimisticMessage(t):(this.client.chat.stageOptimisticMessage(this.conversation.conversation_id,t),this.runtimeState={...this.runtimeState,messages:[...this.client.chat.getCachedMessages(this.conversation.conversation_id)]},this.renderChatView());try{if(this.controller)await this.controller.sendMessage(s,e);else{let i=await this.client.chat.sendMessage(this.conversation.conversation_id,{content:s,attachments:e,message_type:H(s,e)});if(i.error)throw new Error(i.error.message);await this.loadPollingSnapshot(!1)}await this.markConversationRead()}catch(i){this.renderError(i instanceof Error?i.message:"Failed to send message")}}async handleReaction(s,e,t){try{t?this.controller?await this.controller.removeReaction(s,e):(await this.client.chat.removeReaction(s,e),await this.loadPollingSnapshot(!1)):this.controller?await this.controller.addReaction(s,e):(await this.client.chat.addReaction(s,e),await this.loadPollingSnapshot(!1))}catch(i){this.renderError(i instanceof Error?i.message:"Failed to update reaction")}}sendTypingPulse(){!this.controller||!this.config.realtime_enabled||(this.controller.sendTyping(!0),this.typingStopTimer&&clearTimeout(this.typingStopTimer),this.typingStopTimer=setTimeout(()=>{this.controller?.sendTyping(!1)},2500))}async markConversationRead(){if(this.conversation){this.unreadCount=0,this.updateBadge();try{this.controller?(await this.controller.markRead(),await this.controller.refreshReadStatus()):await this.client.chat.markRead(this.conversation.conversation_id)}catch{}}}renderChatView(){this.ensureChatView(),this.updateHeader(),this.renderError(this.runtimeState.error)}updateBadge(){let s=this.shadow.querySelector(".sm-bubble");if(!s)return;let e=s.querySelector(".sm-badge");this.unreadCount>0?(e||(e=document.createElement("span"),e.className="sm-badge",s.appendChild(e)),e.textContent=String(this.unreadCount)):e&&e.remove()}destroy(){this.stopPolling(),this.typingStopTimer&&(clearTimeout(this.typingStopTimer),this.typingStopTimer=null),this.cleanupRealtimeStatusWatchers(),this.cleanupRealtimeRuntime(),this.client.destroy(),this.panelEl?.remove(),this.root.remove()}};(function(){let s=document.querySelectorAll("script[data-api-key]"),e=s[s.length-1];if(!e){console.warn("[ScaleMule] Support widget: missing data-api-key on script tag");return}let t=e.getAttribute("data-api-key");if(!t){console.warn("[ScaleMule] Support widget: data-api-key is empty");return}let i=e.getAttribute("data-api-url")||void 0,n=e.getAttribute("data-color")||void 0,r=e.getAttribute("data-position"),a=r==="left"||r==="right"?r:void 0,o=()=>{new k(t,i,{color:n,position:a})};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",o):o()})();})();
|
|
756
|
+
`}).join(""),this.shouldScrollToUnread){let i=this.messagesEl.querySelector("#sm-unread-divider");i?i.scrollIntoView({block:"center"}):this.messagesEl.scrollTop=this.messagesEl.scrollHeight,this.shouldScrollToUnread=!1}else this.messagesEl.scrollTop=this.messagesEl.scrollHeight}captureUnreadMarkerFromState(){let s=ue(this.runtimeState.readStatuses,this.client.visitorUserId);if(!s){this.unreadMarkerTimestamp=null;return}let e=this.runtimeState.messages.some(t=>t.sender_id!==this.client.visitorUserId&&new Date(t.created_at).getTime()>new Date(s).getTime());this.unreadMarkerTimestamp=e?s:null,this.shouldScrollToUnread=e}clearUnreadMarkerIfViewed(){if(!this.unreadMarkerTimestamp||!this.messagesEl)return;let s=this.messagesEl.querySelector("#sm-unread-divider");s&&this.messagesEl.scrollTop>=s.offsetTop-24&&(this.unreadMarkerTimestamp=null,this.renderMessages())}async initializeVisitorSession(){this.client.isInitialized||await this.client.initVisitorSession()}async toggle(){if(this.isOpen){this.minimize();return}await this.ensureConfigLoaded(),this.renderPanel(),this.isOpen=!0,this.unreadCount=0,this.updateBadge(),this.panelEl?.classList.remove("sm-hidden");try{await this.initializeVisitorSession()}catch{this.renderError("Unable to initialize support chat")}let s=this.conversation??await this.client.getActiveConversation();s?(this.conversation=s,await this.ensureConversationRuntime(!0),this.renderChatView(),await this.markConversationRead()):(this.renderPreChatForm(),this.renderError(null))}minimize(){this.isOpen=!1,this.panelEl?.classList.add("sm-hidden")}async ensureConversationRuntime(s){if(this.conversation){if(this.renderError(null),this.config.realtime_enabled){await this.ensureRealtimeRuntime(s);return}this.realtimeFallbackActive=!1,this.cleanupRealtimeStatusWatchers(),this.setFallbackNotice(null),this.cleanupRealtimeRuntime(),await this.loadPollingSnapshot(s),this.startPolling()}}cleanupRealtimeRuntime(){this.controller&&(this.controller.destroy(),this.controller=null);for(let s of this.runtimeCleanups)s();this.runtimeCleanups=[]}cleanupRealtimeStatusWatchers(){for(let s of this.realtimeStatusCleanups)s();this.realtimeStatusCleanups=[]}ensureRealtimeStatusWatchers(){this.realtimeStatusCleanups.length||(this.realtimeStatusCleanups.push(this.client.chat.on("reconnecting",()=>{!this.config.realtime_enabled||!this.conversation||this.realtimeFallbackActive||this.setFallbackNotice("Realtime reconnecting\u2026")})),this.realtimeStatusCleanups.push(this.client.chat.on("disconnected",()=>{!this.config.realtime_enabled||!this.conversation||this.enterRealtimeFallback()})),this.realtimeStatusCleanups.push(this.client.chat.on("connected",()=>{if(!(!this.config.realtime_enabled||!this.conversation)){if(this.realtimeFallbackActive){this.restoreRealtimeRuntime();return}this.setFallbackNotice(null)}})))}async ensureRealtimeRuntime(s){if(this.conversation){if(this.ensureRealtimeStatusWatchers(),!this.controller||this.runtimeState.conversationId!==this.conversation.conversation_id){this.cleanupRealtimeRuntime(),this.stopPolling(),this.runtimeState=T(this.conversation.conversation_id),this.realtimeFallbackActive=!1,this.setFallbackNotice(null),this.controller=new b(this.client.chat,this.conversation.conversation_id),this.runtimeCleanups.push(this.controller.on("state",t=>{this.applyRuntimeState(t,!1)})),this.runtimeCleanups.push(this.controller.on("error",({message:t})=>{this.renderError(t)}));let e=await this.controller.init({realtime:!0,presence:!0});this.applyRuntimeState(e,s);return}s&&this.captureUnreadMarkerFromState()}}async enterRealtimeFallback(s="Realtime connection lost. Falling back to polling."){if(this.conversation){this.realtimeFallbackActive=!0,this.setFallbackNotice(s),this.cleanupRealtimeRuntime();try{await this.loadPollingSnapshot(!1),this.startPolling(),this.isOpen&&await this.markConversationRead()}catch(e){this.renderError(e instanceof Error?e.message:"Failed to refresh chat state")}}}async restoreRealtimeRuntime(){if(!(!this.conversation||!this.config.realtime_enabled))try{this.realtimeFallbackActive=!1,this.setFallbackNotice(null),await this.ensureRealtimeRuntime(!1)}catch{this.realtimeFallbackActive=!0,this.setFallbackNotice("Realtime reconnect failed. Staying on polling fallback."),this.startPolling()}}applyRuntimeState(s,e){let t=new Set(this.runtimeState.messages.map(n=>n.id)),i=s.messages.filter(n=>!t.has(n.id)&&n.sender_id!==this.client.visitorUserId&&n.sender_type!=="system").length;this.runtimeState=s,e&&this.captureUnreadMarkerFromState(),!this.isOpen&&i>0&&(this.unreadCount+=i,this.updateBadge()),this.updateHeader(),this.renderError(s.error),this.conversation&&this.panelEl&&this.bodyEl?.dataset.view==="chat"&&this.renderChatView(),this.isOpen&&i>0&&this.markConversationRead()}async loadPollingSnapshot(s){if(!this.conversation)return;let[e,t]=await Promise.all([this.client.chat.getMessages(this.conversation.conversation_id,{limit:50}),this.client.chat.getReadStatus(this.conversation.conversation_id)]),i={conversationId:this.conversation.conversation_id,messages:e.data?.messages??this.runtimeState.messages,readStatuses:t.data?.statuses??this.runtimeState.readStatuses,typingUsers:[],members:[],hasMore:e.data?.has_more??!1,isLoading:!1,error:e.error?.message??t.error?.message??null};this.applyRuntimeState(i,s)}startPolling(){this.pollInterval||!this.conversation||(this.pollInterval=setInterval(()=>{this.pollForUpdates()},3e4))}stopPolling(){this.pollInterval&&(clearInterval(this.pollInterval),this.pollInterval=null)}async pollForUpdates(){!this.conversation||this.config.realtime_enabled&&!this.realtimeFallbackActive||(await this.loadPollingSnapshot(!1),this.isOpen&&await this.markConversationRead())}getReadyAttachments(){return this.pendingAttachments.filter(s=>s.attachment).map(s=>s.attachment)}async handleFilesSelected(s){if(s.length){try{await this.initializeVisitorSession()}catch{this.renderError("Unable to prepare attachment upload");return}for(let e of s){let t=ce(e);if(t){this.renderError(t);continue}let i=`${e.name}:${e.size}:${Date.now()}:${Math.random().toString(36).slice(2)}`;this.pendingAttachments=[...this.pendingAttachments,{id:i,fileName:e.name,progress:0}],this.renderPendingAttachments();let n=await this.client.chat.uploadAttachment(e,r=>{this.pendingAttachments=this.pendingAttachments.map(a=>a.id===i?{...a,progress:r}:a),this.renderPendingAttachments()});this.pendingAttachments=this.pendingAttachments.map(r=>r.id!==i?r:n.data?{...r,progress:100,attachment:n.data}:{...r,error:n.error?.message??"Upload failed"}),this.renderPendingAttachments()}}}collectPreChatValues(){let s={},e,t,i=!0;for(let n of this.getConfiguredPreChatFields()){let r=this.bodyEl?.querySelector(`[data-prechat-key="${n.key}"]`),a=r?.value.trim()??"";if(n.required&&!a){i=!1,r?.focus();break}a&&(n.key==="name"?e=a:n.key==="email"?t=a:s[n.key]=a)}return{name:e,email:t,metadata:s,valid:i}}async handleStartChat(){let s=this.bodyEl?.querySelector("#sm-start-btn"),t=this.bodyEl?.querySelector("#sm-prechat-message")?.value.trim()??"",i=this.getReadyAttachments(),{name:n,email:r,metadata:a,valid:o}=this.collectPreChatValues();if(!o||!t&&!i.length||!s){this.renderError("Please complete the required fields before starting the chat.");return}s.disabled=!0,s.textContent="Connecting...";try{await this.client.initVisitorSession({name:n,email:r}),this.conversation=await this.client.startConversation(t,{page_url:typeof location<"u"?location.href:void 0,attachments:i,metadata:a}),N("conversation_id",this.conversation.conversation_id),this.pendingAttachments=[],this.runtimeState={...T(this.conversation.conversation_id),messages:[z(this.client.visitorUserId??"visitor",t,i)]},this.unreadMarkerTimestamp=null,this.shouldScrollToUnread=!1,await this.ensureConversationRuntime(!1),this.renderChatView(),await this.markConversationRead()}catch(l){this.renderError(l instanceof Error?l.message:"Failed to start chat"),s.disabled=!1,s.textContent="Start Chat"}}async handleSendMessage(){if(!this.conversation)return;let s=this.inputEl?.value.trim()??"",e=this.getReadyAttachments();if(!s&&!e.length)return;this.inputEl.value="",this.inputEl.style.height="auto",this.pendingAttachments=[],this.renderPendingAttachments();let t=z(this.client.visitorUserId??"visitor",s,e);this.controller?this.controller.stageOptimisticMessage(t):(this.client.chat.stageOptimisticMessage(this.conversation.conversation_id,t),this.runtimeState={...this.runtimeState,messages:[...this.client.chat.getCachedMessages(this.conversation.conversation_id)]},this.renderChatView());try{if(this.controller)await this.controller.sendMessage(s,e);else{let i=await this.client.chat.sendMessage(this.conversation.conversation_id,{content:s,attachments:e,message_type:B(s,e)});if(i.error)throw new Error(i.error.message);await this.loadPollingSnapshot(!1)}await this.markConversationRead()}catch(i){this.renderError(i instanceof Error?i.message:"Failed to send message")}}async handleReaction(s,e,t){try{t?this.controller?await this.controller.removeReaction(s,e):(await this.client.chat.removeReaction(s,e),await this.loadPollingSnapshot(!1)):this.controller?await this.controller.addReaction(s,e):(await this.client.chat.addReaction(s,e),await this.loadPollingSnapshot(!1))}catch(i){this.renderError(i instanceof Error?i.message:"Failed to update reaction")}}sendTypingPulse(){!this.controller||!this.config.realtime_enabled||(this.controller.sendTyping(!0),this.typingStopTimer&&clearTimeout(this.typingStopTimer),this.typingStopTimer=setTimeout(()=>{this.controller?.sendTyping(!1)},2e3))}async markConversationRead(){if(this.conversation){this.unreadCount=0,this.updateBadge();try{this.controller?(await this.controller.markRead(),await this.controller.refreshReadStatus()):await this.client.chat.markRead(this.conversation.conversation_id)}catch{}}}renderChatView(){this.ensureChatView(),this.updateHeader(),this.renderError(this.runtimeState.error)}updateBadge(){let s=this.shadow.querySelector(".sm-bubble");if(!s)return;let e=s.querySelector(".sm-badge");this.unreadCount>0?(e||(e=document.createElement("span"),e.className="sm-badge",s.appendChild(e)),e.textContent=String(this.unreadCount)):e&&e.remove()}destroy(){this.stopPolling(),this.typingStopTimer&&(clearTimeout(this.typingStopTimer),this.typingStopTimer=null),this.cleanupRealtimeStatusWatchers(),this.cleanupRealtimeRuntime(),this.client.destroy(),this.panelEl?.remove(),this.root.remove()}};(function(){let s=document.querySelectorAll("script[data-api-key]"),e=s[s.length-1];if(!e){console.warn("[ScaleMule] Support widget: missing data-api-key on script tag");return}let t=e.getAttribute("data-api-key");if(!t){console.warn("[ScaleMule] Support widget: data-api-key is empty");return}let i=e.getAttribute("data-api-url")||void 0,n=e.getAttribute("data-color")||void 0,r=e.getAttribute("data-position"),a=r==="left"||r==="right"?r:void 0,o=()=>{new k(t,i,{color:n,position:a})};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",o):o()})();})();
|
package/dist/widget/styles.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
/** All CSS for the support widget, injected into Shadow DOM. */
|
|
2
|
-
export declare const WIDGET_STYLES = "\n:host {\n all: initial;\n --sm-primary: #2563eb;\n --sm-primary-hover: #1d4ed8;\n --sm-primary-disabled: #93c5fd;\n --sm-primary-text: #ffffff;\n --sm-badge-bg: #ef4444;\n --sm-bubble-left: auto;\n --sm-bubble-right: 20px;\n --sm-panel-left: auto;\n --sm-panel-right: 20px;\n --sm-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;\n font-family: var(--sm-font-family);\n font-size: 14px;\n line-height: 1.5;\n color: #111827;\n}\n\n* {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n}\n\n.sm-bubble {\n position: fixed;\n bottom: 20px;\n left: var(--sm-bubble-left);\n right: var(--sm-bubble-right);\n width: 60px;\n height: 60px;\n border-radius: 999px;\n background: var(--sm-primary);\n color: var(--sm-primary-text);\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 12px 30px rgba(15, 23, 42, 0.22);\n transition: transform 0.2s ease, box-shadow 0.2s ease;\n z-index: 999999;\n}\n\n.sm-bubble:hover {\n transform: translateY(-1px) scale(1.03);\n box-shadow: 0 18px 36px rgba(15, 23, 42, 0.28);\n}\n\n.sm-bubble .sm-badge {\n position: absolute;\n top: -4px;\n right: -4px;\n min-width: 20px;\n height: 20px;\n border-radius: 999px;\n background: var(--sm-badge-bg);\n color: #fff;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0 6px;\n font-size: 11px;\n font-weight: 700;\n}\n\n.sm-panel {\n position: fixed;\n bottom: 90px;\n left: var(--sm-panel-left);\n right: var(--sm-panel-right);\n width: 380px;\n max-height: min(560px, calc(100vh - 110px));\n background: #ffffff;\n border-radius: 18px;\n box-shadow: 0 24px 48px rgba(15, 23, 42, 0.18);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n z-index: 999998;\n animation: sm-slide-up 0.22s ease;\n}\n\n@keyframes sm-slide-up {\n from { opacity: 0; transform: translateY(14px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.sm-panel.sm-hidden {\n display: none;\n}\n\n.sm-header {\n background: var(--sm-primary);\n color: var(--sm-primary-text);\n padding: 16px 18px;\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 16px;\n}\n\n.sm-header-copy {\n min-width: 0;\n}\n\n.sm-header-title {\n font-size: 16px;\n font-weight: 700;\n}\n\n.sm-header-status {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 4px;\n font-size: 12px;\n opacity: 0.92;\n}\n\n.sm-status-dot {\n width: 10px;\n height: 10px;\n border-radius: 999px;\n background: rgba(255, 255, 255, 0.65);\n}\n\n.sm-status-dot-online {\n background: #22c55e;\n box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.18);\n}\n\n.sm-status-dot-away {\n background: #cbd5e1;\n}\n\n.sm-header-subtitle {\n font-size: 12px;\n opacity: 0.82;\n margin-top: 6px;\n max-width: 250px;\n}\n\n.sm-header-actions {\n display: flex;\n gap: 6px;\n}\n\n.sm-header-btn {\n background: none;\n border: none;\n color: inherit;\n cursor: pointer;\n border-radius: 10px;\n padding: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n opacity: 0.86;\n}\n\n.sm-header-btn:hover {\n opacity: 1;\n background: rgba(255, 255, 255, 0.12);\n}\n\n.sm-error {\n padding: 10px 14px;\n background: #fef2f2;\n border-bottom: 1px solid #fecaca;\n color: #b91c1c;\n font-size: 12px;\n}\n\n.sm-body {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n}\n\n.sm-prechat {\n padding: 20px 18px 18px;\n display: flex;\n flex-direction: column;\n gap: 14px;\n}\n\n.sm-prechat-title {\n font-size: 18px;\n font-weight: 700;\n color: #111827;\n}\n\n.sm-prechat-desc {\n font-size: 13px;\n color: #6b7280;\n}\n\n.sm-field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.sm-field label {\n font-size: 13px;\n font-weight: 600;\n color: #374151;\n}\n\n.sm-field input,\n.sm-field textarea,\n.sm-input {\n border: 1px solid #d1d5db;\n border-radius: 14px;\n padding: 10px 12px;\n font-size: 14px;\n font-family: inherit;\n color: #111827;\n outline: none;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n background: #fff;\n}\n\n.sm-field input:focus,\n.sm-field textarea:focus,\n.sm-input:focus {\n border-color: var(--sm-primary);\n box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.12);\n}\n\n.sm-field textarea {\n resize: vertical;\n min-height: 92px;\n}\n\n.sm-prechat-actions {\n display: flex;\n gap: 10px;\n align-items: center;\n}\n\n.sm-submit-btn,\n.sm-send-btn {\n border: none;\n cursor: pointer;\n background: var(--sm-primary);\n color: var(--sm-primary-text);\n transition: background 0.15s ease, opacity 0.15s ease;\n}\n\n.sm-submit-btn:hover,\n.sm-send-btn:hover {\n background: var(--sm-primary-hover);\n}\n\n.sm-submit-btn:disabled,\n.sm-send-btn:disabled {\n background: var(--sm-primary-disabled);\n cursor: not-allowed;\n}\n\n.sm-submit-btn {\n flex: 1;\n border-radius: 14px;\n padding: 12px;\n font-size: 14px;\n font-weight: 700;\n}\n\n.sm-chat-shell {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n background: #f8fafc;\n}\n\n.sm-chat-shell.sm-dragging {\n background: rgba(37, 99, 235, 0.05);\n}\n\n.sm-messages {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n padding: 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.sm-date-divider {\n align-self: center;\n font-size: 11px;\n font-weight: 600;\n color: #6b7280;\n background: rgba(148, 163, 184, 0.16);\n padding: 4px 10px;\n border-radius: 999px;\n}\n\n.sm-unread-divider {\n display: flex;\n align-items: center;\n gap: 12px;\n font-size: 11px;\n font-weight: 700;\n color: var(--sm-primary);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.sm-unread-divider::before,\n.sm-unread-divider::after {\n content: '';\n flex: 1;\n height: 1px;\n background: rgba(37, 99, 235, 0.24);\n}\n\n.sm-msg {\n display: flex;\n flex-direction: column;\n gap: 6px;\n max-width: 84%;\n}\n\n.sm-msg-visitor {\n align-self: flex-end;\n}\n\n.sm-msg-rep {\n align-self: flex-start;\n}\n\n.sm-msg-system {\n align-self: center;\n max-width: 92%;\n}\n\n.sm-msg-row {\n display: flex;\n align-items: flex-end;\n gap: 8px;\n}\n\n.sm-msg-bubble {\n padding: 10px 12px;\n border-radius: 16px;\n line-height: 1.4;\n word-break: break-word;\n background: #f3f4f6;\n color: #111827;\n}\n\n.sm-msg-content {\n white-space: pre-wrap;\n}\n\n.sm-msg-visitor .sm-msg-bubble {\n background: var(--sm-primary);\n color: var(--sm-primary-text);\n border-bottom-right-radius: 6px;\n}\n\n.sm-msg-rep .sm-msg-bubble {\n border-bottom-left-radius: 6px;\n}\n\n.sm-msg-system .sm-msg-bubble {\n background: transparent;\n color: #94a3b8;\n font-size: 12px;\n text-align: center;\n padding: 2px 8px;\n}\n\n.sm-msg-action {\n border: 1px solid #e5e7eb;\n background: #ffffff;\n color: #6b7280;\n border-radius: 999px;\n width: 28px;\n height: 28px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n flex-shrink: 0;\n}\n\n.sm-msg-action:hover {\n color: var(--sm-primary);\n border-color: rgba(37, 99, 235, 0.24);\n}\n\n.sm-msg-time {\n font-size: 11px;\n color: #94a3b8;\n}\n\n.sm-msg-visitor .sm-msg-time {\n text-align: right;\n}\n\n.sm-reactions {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.sm-reaction-badge {\n border: 1px solid #e5e7eb;\n background: #ffffff;\n color: #374151;\n border-radius: 999px;\n padding: 4px 8px;\n font-size: 12px;\n cursor: pointer;\n}\n\n.sm-reaction-badge-active {\n border-color: rgba(37, 99, 235, 0.32);\n background: rgba(37, 99, 235, 0.08);\n color: var(--sm-primary);\n}\n\n.sm-reaction-picker {\n display: inline-flex;\n gap: 6px;\n padding: 6px;\n border-radius: 999px;\n border: 1px solid #e5e7eb;\n background: #ffffff;\n box-shadow: 0 12px 28px rgba(15, 23, 42, 0.12);\n}\n\n.sm-reaction-picker-btn {\n border: none;\n background: transparent;\n cursor: pointer;\n border-radius: 999px;\n width: 30px;\n height: 30px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-size: 17px;\n}\n\n.sm-attachment {\n display: block;\n margin-top: 8px;\n border-radius: 12px;\n max-width: 100%;\n}\n\n.sm-attachment-image,\n.sm-attachment-video {\n max-width: 260px;\n}\n\n.sm-attachment-audio {\n width: 100%;\n}\n\n.sm-attachment-link {\n color: inherit;\n text-decoration: underline;\n font-size: 13px;\n}\n\n.sm-typing {\n min-height: 22px;\n padding: 0 16px 10px;\n font-size: 12px;\n color: #6b7280;\n}\n\n.sm-typing-indicator {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n\n.sm-typing-indicator span:nth-child(-n+3) {\n width: 6px;\n height: 6px;\n border-radius: 999px;\n background: #94a3b8;\n animation: sm-bounce 1.2s infinite ease-in-out;\n}\n\n.sm-typing-indicator span:nth-child(2) {\n animation-delay: 0.15s;\n}\n\n.sm-typing-indicator span:nth-child(3) {\n animation-delay: 0.3s;\n}\n\n@keyframes sm-bounce {\n 0%, 80%, 100% { transform: scale(0.85); opacity: 0.5; }\n 40% { transform: scale(1); opacity: 1; }\n}\n\n.sm-upload-list {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n padding: 0 16px 10px;\n}\n\n.sm-upload-chip {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n max-width: 100%;\n border-radius: 999px;\n border: 1px solid #e5e7eb;\n background: #ffffff;\n padding: 6px 10px;\n font-size: 12px;\n color: #374151;\n}\n\n.sm-upload-chip-error {\n border-color: #fecaca;\n background: #fef2f2;\n color: #b91c1c;\n}\n\n.sm-upload-name {\n max-width: 140px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.sm-upload-progress {\n color: #6b7280;\n}\n\n.sm-upload-chip-error .sm-upload-progress {\n color: inherit;\n}\n\n.sm-upload-remove {\n border: none;\n background: transparent;\n color: inherit;\n cursor: pointer;\n font-size: 14px;\n line-height: 1;\n}\n\n.sm-input-area {\n display: flex;\n align-items: flex-end;\n gap: 10px;\n padding: 12px 16px 16px;\n border-top: 1px solid #e5e7eb;\n background: #ffffff;\n}\n\n.sm-input {\n flex: 1;\n min-height: 44px;\n max-height: 100px;\n resize: none;\n overflow-y: auto;\n line-height: 1.4;\n}\n\n.sm-attach-btn {\n width: 42px;\n height: 42px;\n border-radius: 14px;\n border: 1px solid #d1d5db;\n background: #ffffff;\n color: #374151;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.sm-attach-btn:hover {\n border-color: rgba(37, 99, 235, 0.28);\n color: var(--sm-primary);\n}\n\n.sm-send-btn {\n width: 42px;\n height: 42px;\n border-radius: 14px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.sm-footer {\n text-align: center;\n padding: 8px;\n font-size: 11px;\n color: #94a3b8;\n}\n\n.sm-footer a {\n color: #6b7280;\n text-decoration: none;\n}\n\n.sm-footer a:hover {\n text-decoration: underline;\n}\n\n@media (max-width: 440px) {\n .sm-panel {\n left: 0;\n right: 0;\n bottom: 0;\n width: 100%;\n max-height: 100vh;\n border-radius: 18px 18px 0 0;\n }\n}\n";
|
|
2
|
+
export declare const WIDGET_STYLES = "\n:host {\n all: initial;\n --sm-primary: #2563eb;\n --sm-primary-hover: #1d4ed8;\n --sm-primary-disabled: #93c5fd;\n --sm-primary-text: #ffffff;\n --sm-visitor-bubble: var(--sm-primary);\n --sm-visitor-text: var(--sm-primary-text);\n --sm-rep-bubble: #f3f4f6;\n --sm-rep-text: #1f2937;\n --sm-bubble-radius: 16px;\n --sm-panel-width: 380px;\n --sm-panel-height: 560px;\n --sm-badge-bg: #ef4444;\n --sm-bubble-left: auto;\n --sm-bubble-right: 20px;\n --sm-panel-left: auto;\n --sm-panel-right: 20px;\n --sm-font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;\n --sm-font-size: 14px;\n font-family: var(--sm-font-family);\n font-size: var(--sm-font-size);\n line-height: 1.5;\n color: #111827;\n}\n\n* {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n}\n\n.sm-bubble {\n position: fixed;\n bottom: 20px;\n left: var(--sm-bubble-left);\n right: var(--sm-bubble-right);\n width: 60px;\n height: 60px;\n border-radius: 999px;\n background: var(--sm-primary);\n color: var(--sm-primary-text);\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 12px 30px rgba(15, 23, 42, 0.22);\n transition: transform 0.2s ease, box-shadow 0.2s ease;\n z-index: 999999;\n}\n\n.sm-bubble:hover {\n transform: translateY(-1px) scale(1.03);\n box-shadow: 0 18px 36px rgba(15, 23, 42, 0.28);\n}\n\n.sm-bubble .sm-badge {\n position: absolute;\n top: -4px;\n right: -4px;\n min-width: 20px;\n height: 20px;\n border-radius: 999px;\n background: var(--sm-badge-bg);\n color: #fff;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0 6px;\n font-size: 11px;\n font-weight: 700;\n}\n\n.sm-panel {\n position: fixed;\n bottom: 90px;\n left: var(--sm-panel-left);\n right: var(--sm-panel-right);\n width: var(--sm-panel-width);\n max-height: min(var(--sm-panel-height), calc(100vh - 110px));\n background: #ffffff;\n border-radius: 18px;\n box-shadow: 0 24px 48px rgba(15, 23, 42, 0.18);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n z-index: 999998;\n animation: sm-slide-up 0.22s ease;\n}\n\n@keyframes sm-slide-up {\n from { opacity: 0; transform: translateY(14px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.sm-panel.sm-hidden {\n display: none;\n}\n\n.sm-header {\n background: var(--sm-primary);\n color: var(--sm-primary-text);\n padding: 16px 18px;\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 16px;\n}\n\n.sm-header-copy {\n min-width: 0;\n}\n\n.sm-header-title {\n font-size: 16px;\n font-weight: 700;\n}\n\n.sm-header-status {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 4px;\n font-size: 12px;\n opacity: 0.92;\n}\n\n.sm-status-dot {\n width: 10px;\n height: 10px;\n border-radius: 999px;\n background: rgba(255, 255, 255, 0.65);\n}\n\n.sm-status-dot-online {\n background: #22c55e;\n box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.18);\n}\n\n.sm-status-dot-away {\n background: #cbd5e1;\n}\n\n.sm-header-subtitle {\n font-size: 12px;\n opacity: 0.82;\n margin-top: 6px;\n max-width: 250px;\n}\n\n.sm-header-actions {\n display: flex;\n gap: 6px;\n}\n\n.sm-header-btn {\n background: none;\n border: none;\n color: inherit;\n cursor: pointer;\n border-radius: 10px;\n padding: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n opacity: 0.86;\n}\n\n.sm-header-btn:hover {\n opacity: 1;\n background: rgba(255, 255, 255, 0.12);\n}\n\n.sm-error {\n padding: 10px 14px;\n background: #fef2f2;\n border-bottom: 1px solid #fecaca;\n color: #b91c1c;\n font-size: 12px;\n}\n\n.sm-body {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n}\n\n.sm-prechat {\n padding: 20px 18px 18px;\n display: flex;\n flex-direction: column;\n gap: 14px;\n}\n\n.sm-prechat-title {\n font-size: 18px;\n font-weight: 700;\n color: #111827;\n}\n\n.sm-prechat-desc {\n font-size: 13px;\n color: #6b7280;\n}\n\n.sm-field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.sm-field label {\n font-size: 13px;\n font-weight: 600;\n color: #374151;\n}\n\n.sm-field input,\n.sm-field textarea,\n.sm-input {\n border: 1px solid #d1d5db;\n border-radius: 14px;\n padding: 10px 12px;\n font-size: 14px;\n font-family: inherit;\n color: #111827;\n outline: none;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n background: #fff;\n}\n\n.sm-field input:focus,\n.sm-field textarea:focus,\n.sm-input:focus {\n border-color: var(--sm-primary);\n box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.12);\n}\n\n.sm-field textarea {\n resize: vertical;\n min-height: 92px;\n}\n\n.sm-prechat-actions {\n display: flex;\n gap: 10px;\n align-items: center;\n}\n\n.sm-submit-btn,\n.sm-send-btn {\n border: none;\n cursor: pointer;\n background: var(--sm-primary);\n color: var(--sm-primary-text);\n transition: background 0.15s ease, opacity 0.15s ease;\n}\n\n.sm-submit-btn:hover,\n.sm-send-btn:hover {\n background: var(--sm-primary-hover);\n}\n\n.sm-submit-btn:disabled,\n.sm-send-btn:disabled {\n background: var(--sm-primary-disabled);\n cursor: not-allowed;\n}\n\n.sm-submit-btn {\n flex: 1;\n border-radius: 14px;\n padding: 12px;\n font-size: 14px;\n font-weight: 700;\n}\n\n.sm-chat-shell {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n background: #f8fafc;\n position: relative;\n}\n\n.sm-chat-shell.sm-dragging {\n background: rgba(37, 99, 235, 0.05);\n}\n\n.sm-messages {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n padding: 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.sm-date-divider {\n align-self: center;\n font-size: 11px;\n font-weight: 600;\n color: #6b7280;\n background: rgba(148, 163, 184, 0.16);\n padding: 4px 10px;\n border-radius: 999px;\n}\n\n.sm-unread-divider {\n display: flex;\n align-items: center;\n gap: 12px;\n font-size: 11px;\n font-weight: 700;\n color: var(--sm-primary);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.sm-unread-divider::before,\n.sm-unread-divider::after {\n content: '';\n flex: 1;\n height: 1px;\n background: rgba(37, 99, 235, 0.24);\n}\n\n.sm-msg {\n display: flex;\n flex-direction: column;\n gap: 6px;\n max-width: 84%;\n}\n\n.sm-msg-visitor {\n align-self: flex-end;\n}\n\n.sm-msg-rep {\n align-self: flex-start;\n}\n\n.sm-msg-system {\n align-self: center;\n max-width: 92%;\n}\n\n.sm-msg-row {\n display: flex;\n align-items: flex-end;\n gap: 8px;\n}\n\n.sm-msg-bubble {\n padding: 10px 12px;\n border-radius: var(--sm-bubble-radius);\n line-height: 1.4;\n word-break: break-word;\n background: var(--sm-rep-bubble);\n color: var(--sm-rep-text);\n}\n\n.sm-msg-content {\n white-space: pre-wrap;\n}\n\n.sm-msg-visitor .sm-msg-bubble {\n background: var(--sm-visitor-bubble);\n color: var(--sm-visitor-text);\n border-bottom-right-radius: 6px;\n}\n\n.sm-msg-rep .sm-msg-bubble {\n border-bottom-left-radius: 6px;\n}\n\n.sm-msg-system .sm-msg-bubble {\n background: transparent;\n color: #94a3b8;\n font-size: 12px;\n text-align: center;\n padding: 2px 8px;\n}\n\n.sm-msg-action {\n border: 1px solid #e5e7eb;\n background: #ffffff;\n color: #6b7280;\n border-radius: 999px;\n width: 28px;\n height: 28px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n flex-shrink: 0;\n}\n\n.sm-msg-action:hover {\n color: var(--sm-primary);\n border-color: rgba(37, 99, 235, 0.24);\n}\n\n.sm-msg-time {\n font-size: 11px;\n color: #94a3b8;\n}\n\n.sm-msg-visitor .sm-msg-time {\n text-align: right;\n}\n\n.sm-reactions {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.sm-reaction-badge {\n border: 1px solid #e5e7eb;\n background: #ffffff;\n color: #374151;\n border-radius: 999px;\n padding: 4px 8px;\n font-size: 12px;\n cursor: pointer;\n}\n\n.sm-reaction-badge-active {\n border-color: rgba(37, 99, 235, 0.32);\n background: rgba(37, 99, 235, 0.08);\n color: var(--sm-primary);\n}\n\n.sm-reaction-picker {\n display: inline-flex;\n gap: 6px;\n padding: 6px;\n border-radius: 999px;\n border: 1px solid #e5e7eb;\n background: #ffffff;\n box-shadow: 0 12px 28px rgba(15, 23, 42, 0.12);\n}\n\n.sm-reaction-picker-btn {\n border: none;\n background: transparent;\n cursor: pointer;\n border-radius: 999px;\n width: 30px;\n height: 30px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-size: 17px;\n}\n\n.sm-attachment {\n display: block;\n margin-top: 8px;\n border-radius: 12px;\n max-width: 100%;\n}\n\n.sm-attachment-image,\n.sm-attachment-video {\n max-width: 260px;\n}\n\n.sm-attachment-audio {\n width: 100%;\n}\n\n.sm-attachment-link {\n color: inherit;\n text-decoration: underline;\n font-size: 13px;\n}\n\n.sm-typing {\n min-height: 22px;\n padding: 0 16px 10px;\n font-size: 12px;\n color: #6b7280;\n}\n\n.sm-typing-indicator {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n\n.sm-typing-indicator span:nth-child(-n+3) {\n width: 6px;\n height: 6px;\n border-radius: 999px;\n background: #94a3b8;\n animation: sm-bounce 1.2s infinite ease-in-out;\n}\n\n.sm-typing-indicator span:nth-child(2) {\n animation-delay: 0.15s;\n}\n\n.sm-typing-indicator span:nth-child(3) {\n animation-delay: 0.3s;\n}\n\n@keyframes sm-bounce {\n 0%, 80%, 100% { transform: scale(0.85); opacity: 0.5; }\n 40% { transform: scale(1); opacity: 1; }\n}\n\n.sm-upload-list {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n padding: 0 16px 10px;\n}\n\n.sm-upload-chip {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n max-width: 100%;\n border-radius: 999px;\n border: 1px solid #e5e7eb;\n background: #ffffff;\n padding: 6px 10px;\n font-size: 12px;\n color: #374151;\n}\n\n.sm-upload-chip-error {\n border-color: #fecaca;\n background: #fef2f2;\n color: #b91c1c;\n}\n\n.sm-upload-name {\n max-width: 140px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.sm-upload-progress {\n color: #6b7280;\n}\n\n.sm-upload-chip-error .sm-upload-progress {\n color: inherit;\n}\n\n.sm-upload-remove {\n border: none;\n background: transparent;\n color: inherit;\n cursor: pointer;\n font-size: 14px;\n line-height: 1;\n}\n\n.sm-input-area {\n display: flex;\n align-items: flex-end;\n gap: 10px;\n padding: 12px 16px 16px;\n border-top: 1px solid #e5e7eb;\n background: #ffffff;\n}\n\n.sm-input {\n flex: 1;\n min-height: 44px;\n max-height: 100px;\n resize: none;\n overflow-y: auto;\n line-height: 1.4;\n}\n\n.sm-attach-btn {\n width: 42px;\n height: 42px;\n border-radius: 14px;\n border: 1px solid #d1d5db;\n background: #ffffff;\n color: #374151;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.sm-attach-btn:hover {\n border-color: rgba(37, 99, 235, 0.28);\n color: var(--sm-primary);\n}\n\n.sm-send-btn {\n width: 42px;\n height: 42px;\n border-radius: 14px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.sm-footer {\n text-align: center;\n padding: 8px;\n font-size: 11px;\n color: #94a3b8;\n}\n\n.sm-footer a {\n color: #6b7280;\n text-decoration: none;\n}\n\n.sm-footer a:hover {\n text-decoration: underline;\n}\n\n.sm-drag-overlay {\n position: absolute;\n inset: 0;\n background: rgba(37, 99, 235, 0.08);\n border: 2px dashed var(--sm-primary);\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 13px;\n font-weight: 600;\n color: var(--sm-primary);\n pointer-events: none;\n z-index: 10;\n}\n\n@media (max-width: 440px) {\n .sm-panel {\n left: 0;\n right: 0;\n bottom: 0;\n width: 100%;\n max-height: 100vh;\n border-radius: 18px 18px 0 0;\n }\n}\n";
|
|
3
3
|
//# sourceMappingURL=styles.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../src/widget/styles.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAEhE,eAAO,MAAM,aAAa,
|
|
1
|
+
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../src/widget/styles.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAEhE,eAAO,MAAM,aAAa,4vXAipBzB,CAAC"}
|