@tips-web/webchat
Version:
Widget de chat embeddable pour WebChatApp avec persistance des conversations
1 lines • 58.3 kB
JavaScript
!function(e){"function"==typeof define&&define.amd?define(e):e()}((function(){"use strict";var e=Object.defineProperty,t=Object.defineProperties,n=Object.getOwnPropertyDescriptors,i=Object.getOwnPropertySymbols,s=Object.prototype.hasOwnProperty,o=Object.prototype.propertyIsEnumerable,a=(t,n,i)=>n in t?e(t,n,{enumerable:!0,configurable:!0,writable:!0,value:i}):t[n]=i,r=(e,t)=>{for(var n in t||(t={}))s.call(t,n)&&a(e,n,t[n]);if(i)for(var n of i(t))o.call(t,n)&&a(e,n,t[n]);return e},A=(e,t,n)=>new Promise(((i,s)=>{var o=e=>{try{r(n.next(e))}catch(t){s(t)}},a=e=>{try{r(n.throw(e))}catch(t){s(t)}},r=e=>e.done?i(e.value):Promise.resolve(e.value).then(o,a);r((n=n.apply(e,t)).next())}));class l{constructor(){this.messageAudio=new Audio("data:audio/mp3;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4Ljc2LjEwMAAAAAAAAAAAAAAA/+M4wAAAAAAAAAAAAEluZm8AAAAPAAAAAwAAAbAAkJCQkJCQkJCQkJCQkJCQkJCQwMDAwMDAwMDAwMDAwMDAwMDAwODg4ODg4ODg4ODg4ODg4ODg4ODg////////////////AAAAOkxhdmM1OC4xMwAAAAAAAAAAAAAAACQCgAAAAAAAAAGwxf/K4AAAAAAAAAAAAAAAAAAAAP/jOMAAAAAAAAAAAABJbmZvAAAADwAAAAMAAAGwAJCQkJCQkJCQkJCQkJCQkJCQkMDAwMDAwMDAwMDAwMDAwMDAwMDg4ODg4ODg4ODg4ODg4ODg4ODg4P///////////////wAAADpMYXZjNTguMTMAAAAAAAAAAAAAAACQAgAAAAAAAAABsMX/yuAAAAAAAAAAAAAAAAAAAAD/4zjAAAAAAAAAAAAASW5mbwAAAA8AAAADAAABsACQkJCQkJCQkJCQkJCQkJCQkJDAwMDAwMDAwMDAwMDAwMDAwMDA4ODg4ODg4ODg4ODg4ODg4ODg4OD////////////////AAAAOkxhdmM1OC4xMwAAAAAAAAAAAAAAACQCgAAAAAAAAAGwxf/K4AAAAAAAAAAAAAAAAAAAAP/jOMAAAAAAAAAAAABJbmZvAAAADwAAAAMAAAGwAJCQkJCQkJCQkJCQkJCQkJCQkMDAwMDAwMDAwMDAwMDAwMDAwMDg4ODg4ODg4ODg4ODg4ODg4ODg4P///////////////wAAADpMYXZjNTguMTMAAAAAAAAAAAAAAACQAgAAAAAAAAABsMX/yuAAAAAAAAAAAAAAAAAAAAD/4zjAAAAAAAAAAAAASW5mbwAAAA8AAAADAAABsACQkJCQkJCQkJCQkJCQkJCQkJDAwMDAwMDAwMDAwMDAwMDAwMDA4ODg4ODg4ODg4ODg4ODg4ODg4OD////////////////AAAAOkxhdmM1OC4xMwAAAAAAAAAAAAAAACQCgAAAAAAAAAGwxf/K4AAAAAAAAAAAAAAAAAAAAA=="),this.connectionAudio=new Audio("data:audio/mp3;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4Ljc2LjEwMAAAAAAAAAAAAAAA/+M4wAAAAAAAAAAAAEluZm8AAAAPAAAAAwAAAbAAkJCQkJCQkJCQkJCQkJCQkJCQwMDAwMDAwMDAwMDAwMDAwMDAwODg4ODg4ODg4ODg4ODg4ODg4ODg////////////////AAAAOkxhdmM1OC4xMwAAAAAAAAAAAAAAACQCgAAAAAAAAAGwxf/K4AAAAAAAAAAAAAAAAAAAAP/jOMAAAAAAAAAAAABJbmZvAAAADwAAAAMAAAGwAJCQkJCQkJCQkJCQkJCQkJCQkMDAwMDAwMDAwMDAwMDAwMDAwMDg4ODg4ODg4ODg4ODg4ODg4ODg4P///////////////wAAADpMYXZjNTguMTMAAAAAAAAAAAAAAACQAgAAAAAAAAABsMX/yuAAAAAAAAAAAAAAAAAAAAD/4zjAAAAAAAAAAAAASW5mbwAAAA8AAAADAAABsACQkJCQkJCQkJCQkJCQkJCQkJDAwMDAwMDAwMDAwMDAwMDAwMDA4ODg4ODg4ODg4ODg4ODg4ODg4OD////////////////AAAAOkxhdmM1OC4xMwAAAAAAAAAAAAAAACQCgAAAAAAAAAGwxf/K4AAAAAAAAAAAAAAAAAAAAA=="),this.transitionAudio=new Audio("data:audio/mp3;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4Ljc2LjEwMAAAAAAAAAAAAAAA/+M4wAAAAAAAAAAAAEluZm8AAAAPAAAAAwAAAbAAkJCQkJCQkJCQkJCQkJCQkJCQwMDAwMDAwMDAwMDAwMDAwMDAwODg4ODg4ODg4ODg4ODg4ODg4ODg////////////////AAAAOkxhdmM1OC4xMwAAAAAAAAAAAAAAACQCgAAAAAAAAAGwxf/K4AAAAAAAAAAAAAAAAAAAAP/jOMAAAAAAAAAAAABJbmZvAAAADwAAAAMAAAGwAJCQkJCQkJCQkJCQkJCQkJCQkMDAwMDAwMDAwMDAwMDAwMDAwMDg4ODg4ODg4ODg4ODg4ODg4ODg4P///////////////wAAADpMYXZjNTguMTMAAAAAAAAAAAAAAACQAgAAAAAAAAABsMX/yuAAAAAAAAAAAAAAAAAAAAD/4zjAAAAAAAAAAAAASW5mbwAAAA8AAAADAAABsACQkJCQkJCQkJCQkJCQkJCQkJDAwMDAwMDAwMDAwMDAwMDAwMDA4ODg4ODg4ODg4ODg4ODg4ODg4OD////////////////AAAAOkxhdmM1OC4xMwAAAAAAAAAAAAAAACQCgAAAAAAAAAGwxf/K4AAAAAAAAAAAAAAAAAAAAA=="),this.userInteracted=!1,this.audioEnabled=!1;"true"===localStorage.getItem("webchat_audio_enabled")&&(this.audioEnabled=!0,this.userInteracted=!0),this.preloadSounds()}preloadSounds(){[this.messageAudio,this.connectionAudio,this.transitionAudio].forEach((e=>{e.load(),e.volume=.3}))}playMessageSound(){this.audioEnabled&&this.playSound(this.messageAudio,"message")}playConnectionSound(){this.audioEnabled&&this.playSound(this.connectionAudio,"connection")}playTransitionSound(){this.audioEnabled&&this.playSound(this.transitionAudio,"transition")}playSound(e,t){try{e.currentTime=0;const t=e.play();void 0!==t&&t.catch((e=>{"NotAllowedError"!==e.name||this.userInteracted||this.requestAudioPermission()}))}catch(n){}}requestAudioPermission(){const e=document.createElement("button");e.textContent="🔊 Activer les sons",e.style.cssText="\n position: fixed;\n top: 20px;\n right: 20px;\n background: #007bff;\n color: white;\n border: none;\n padding: 10px 15px;\n border-radius: 5px;\n cursor: pointer;\n z-index: 10000;\n font-size: 14px;\n box-shadow: 0 2px 10px rgba(0,0,0,0.2);\n ",e.onclick=()=>{this.testAudioAfterInteraction(),document.body.removeChild(e)},setTimeout((()=>{document.body.contains(e)&&document.body.removeChild(e)}),1e4),document.body.appendChild(e)}testAudioAfterInteraction(){try{const e=new Audio("data:audio/wav;base64,UklGRnoGAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoGAACBhYqFbF1fdJivrJBhNjVgodDbq2EcBj+a2/LDciUFLIHO8tiJNwgZaLvt559NEAxQp+PwtmMcBjiR1/LMeSwFJHfH8N2QQAoUXrTp66hVFApGn+DyvmwhBSuBzvLZiTYIG2m98OScTgwOUarm7blmGgU7k9n1unEiBC13yO/eizEIHWq+8+OWT");e.volume=.01,e.play().then((()=>{this.userInteracted=!0,this.audioEnabled=!0,localStorage.setItem("webchat_audio_enabled","true"),this.showAudioNotification("Sons activés !","success")})).catch((e=>{this.audioEnabled=!1,this.showAudioNotification("Sons désactivés","info")}))}catch(e){}}showAudioNotification(e,t="info"){const n=document.createElement("div");n.textContent=e,n.style.cssText=`\n position: fixed;\n top: 20px;\n right: 20px;\n background: ${"success"===t?"#28a745":"#17a2b8"};\n color: white;\n padding: 10px 15px;\n border-radius: 5px;\n z-index: 10001;\n font-size: 14px;\n box-shadow: 0 2px 10px rgba(0,0,0,0.2);\n animation: slideInRight 0.3s ease-out;\n `;const i=document.createElement("style");i.textContent="\n @keyframes slideInRight {\n from { transform: translateX(100%); opacity: 0; }\n to { transform: translateX(0); opacity: 1; }\n }\n ",document.head.appendChild(i),document.body.appendChild(n),setTimeout((()=>{document.body.contains(n)&&document.body.removeChild(n)}),3e3)}setVolume(e){const t=Math.max(0,Math.min(1,e));[this.messageAudio,this.connectionAudio,this.transitionAudio].forEach((e=>{e.volume=t}))}mute(){this.setVolume(0)}unmute(){this.setVolume(.3)}enableAudio(){this.audioEnabled=!0,this.userInteracted=!0,localStorage.setItem("webchat_audio_enabled","true"),this.showAudioNotification("Sons activés !","success")}disableAudio(){this.audioEnabled=!1,localStorage.setItem("webchat_audio_enabled","false"),this.showAudioNotification("Sons désactivés","info")}isAudioEnabled(){return this.audioEnabled}getAudioStatus(){return{enabled:this.audioEnabled,userInteracted:this.userInteracted,volume:this.messageAudio.volume}}}const c={fr:{online:"En ligne",offline:"Hors ligne",typing:"En train d'écrire...",placeholder:"Écrivez votre message...",fileSelected:"Fichier sélectionné - Cliquez sur Envoyer",filesSelected:"Fichiers sélectionnés - Cliquez sur Envoyer",characterCounter:"/1000",conversationClosed:"Cette conversation a été fermée.",conversationClosedPlaceholder:"Conversation fermée - Rechargez pour redémarrer",conversationClosedButton:"Fermé",canRestartConversation:"Vous pouvez maintenant démarrer une nouvelle conversation.",sendMessage:"Tapez votre message...",send:"Envoyer",fileTypeError:"Type de fichier non supporté. Types autorisés: images, PDF, documents Word, texte, audio MP3/WAV, vidéo MP4.",fileSizeError:"Le fichier est trop volumineux. Taille maximum: 10MB.",tooManyFiles:"Trop de fichiers sélectionnés. Maximum 5 fichiers autorisés.",defaultWelcome:"Besoin d'aide ?",phantomAgentName:"Chattez avec nous !",waitingForAgent:"Nous sommes en ligne."},en:{online:"Online",offline:"Offline",typing:"Typing...",placeholder:"Type your message...",fileSelected:"File selected - Click Send",filesSelected:"Files selected - Click Send",characterCounter:"/1000",conversationClosed:"This conversation has been closed.",conversationClosedPlaceholder:"Conversation closed - Reload to restart",conversationClosedButton:"Closed",canRestartConversation:"You can now start a new conversation.",sendMessage:"Type your message...",send:"Send",fileTypeError:"Unsupported file type. Allowed types: images, PDF, Word documents, text, MP3/WAV audio, MP4 video.",fileSizeError:"File is too large. Maximum size: 10MB.",tooManyFiles:"Too many files selected. Maximum 5 files allowed.",defaultWelcome:"Need help?",phantomAgentName:"Chat with us!",waitingForAgent:"We are online."},es:{online:"En línea",offline:"Desconectado",typing:"Escribiendo...",placeholder:"Escribe tu mensaje...",fileSelected:"Archivo seleccionado - Haz clic en Enviar",filesSelected:"Archivos seleccionados - Haz clic en Enviar",characterCounter:"/1000",conversationClosed:"Esta conversación ha sido cerrada.",conversationClosedPlaceholder:"Conversación cerrada - Recarga para reiniciar",conversationClosedButton:"Cerrada",canRestartConversation:"Ahora puedes iniciar una nueva conversación.",sendMessage:"Escribe tu mensaje...",send:"Enviar",fileTypeError:"Tipo de archivo no soportado. Tipos permitidos: imágenes, PDF, documentos Word, texto, audio MP3/WAV, video MP4.",fileSizeError:"El archivo es demasiado grande. Tamaño máximo: 10MB.",tooManyFiles:"Demasiados archivos seleccionados. Máximo 5 archivos permitidos.",defaultWelcome:"¿Necesitas ayuda?",phantomAgentName:"¡Chatea con nosotros!",waitingForAgent:"Estamos en línea."}},d="phantom-agent-system",h="system-agent@001-t.com",g="Assistant Virtuel";function u(e){return!!e&&(e.agentId===d||e.agentEmail===h||e.agentName===g||e.agentEmail&&e.agentEmail.startsWith("system-agent@"))}try{let e=function(){try{window.chatWidget=new s(window.webchatConfig),i()}catch(e){}},i=function(){window.chatWidget&&(window.debugWebChat=()=>window.chatWidget.debug(),window.restoreWebChatConversation=e=>window.chatWidget.forceRestoreConversation(e),window.clearWebChatStorage=()=>window.chatWidget.clearStorage())};if(!window.webchatConfig)throw new Error("Configuration manquante");class s{constructor(e){const t=navigator.language.slice(0,2).toLowerCase();this.currentLang=c[t]?t:"fr";this.config=r({apiUrl:e.apiUrl||"http://localhost:8080",siteId:e.siteId,agentName:e.agentName||"Support",welcomeMessage:(t=>{var n,i;if(!t||!Array.isArray(t))return e.welcomeMessage||this.t("defaultWelcome");const s=t.find((e=>e.lang===this.currentLang));return(null==s?void 0:s.message)||(null==(n=t.find((e=>"fr"===e.lang)))?void 0:n.message)||(null==(i=t[0])?void 0:i.message)||this.t("defaultWelcome")})(e.welcomeMessages),primaryColor:e.primaryColor||"#2196f3",widgetPosition:e.widgetPosition||"right"},e),this.isOpen=!1,this.socket=null,this.conversationId=null,this.visitorToken=null,this.messages=[],this.selectedFiles=[],this.typingTimeout=null,this.connected=!1,this.agentsOnline=!1,this.notificationAudio=new Audio("data:audio/mp3;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4Ljc2LjEwMAAAAAAAAAAAAAAA/+M4wAAAAAAAAAAAAEluZm8AAAAPAAAAAwAAAbAAkJCQkJCQkJCQkJCQkJCQkJCQwMDAwMDAwMDAwMDAwMDAwMDAwODg4ODg4ODg4ODg4ODg4ODg4ODg////////////////AAAAOkxhdmM1OC4xMwAAAAAAAAAAAAAAACQCgAAAAAAAAAGwxf/K4AAAAAAAAAAAAAAAAAAAAP/jOMAAAAAAAAAAAABJbmZvAAAADwAAAAMAAAGwAJCQkJCQkJCQkJCQkJCQkJCQkMDAwMDAwMDAwMDAwMDAwMDAwMDg4ODg4ODg4ODg4ODg4ODg4ODg4P///////////////wAAADpMYXZjNTguMTMAAAAAAAAAAAAAAACQAgAAAAAAAAABsMX/yuAAAAAAAAAAAAAAAAAAAAD/4zjAAAAAAAAAAAAASW5mbwAAAA8AAAADAAABsACQkJCQkJCQkJCQkJCQkJCQkJDAwMDAwMDAwMDAwMDAwMDAwMDA4ODg4ODg4ODg4ODg4ODg4ODg4OD////////////////AAAAOkxhdmM1OC4xMwAAAAAAAAAAAAAAACQCgAAAAAAAAAGwxf/K4AAAAAAAAAAAAAAAAAAAAA=="),this.soundManager=new l,this.isPhantomAgent=!1,this.currentAgentId=null,this.storageKey=`webchat_conversation_${this.config.siteId}`,this.emergencyRecovery(),this.init()}emergencyRecovery(){try{localStorage.getItem(this.storageKey)}catch(e){}}debugLocalStorage(){try{const e=[];for(let n=0;n<localStorage.length;n++){const t=localStorage.key(n);t&&t.includes("webchat")&&e.push({key:t,value:localStorage.getItem(t)})}const t=localStorage.getItem(this.storageKey);t&&this.conversationId?this.conversationId:t&&!this.conversationId||!t&&this.conversationId}catch(e){}}t(e){return c[this.currentLang][e]||c.fr[e]||e}init(){return A(this,null,(function*(){this.debugLocalStorage();try{if(yield this.authenticate(),yield this.loadExistingConversation(),this.createUI(),this.updateAgentHeader({agentName:this.t("phantomAgentName"),agentAvatar:null,agentId:d,agentEmail:h}),this.conversationId&&(yield this.loadConversationHistory()),yield this.connectWebSocket(),this.conversationId)try{yield this.joinExistingConversation()}catch(e){}this.initNavigationTracking()}catch(e){try{this.messagesContainer||this.createUI()}catch(t){}}}))}authenticate(){return A(this,null,(function*(){try{const e=navigator.language||navigator.userLanguage||"fr-FR",t=yield fetch(`${this.config.apiUrl}/api/webchat/auth/${this.config.siteId}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({userAgent:navigator.userAgent,referrer:document.referrer,url:window.location.href,language:e})});if(!t.ok)throw new Error(`Auth failed: ${t.status}`);const n=yield t.json();this.visitorToken=n.token}catch(e){throw e}}))}connectWebSocket(){return A(this,null,(function*(){return new Promise(((e,t)=>{if(window.io)return this.initSocket(),void e();const n=document.createElement("script");n.src="https://cdn.jsdelivr.net/npm/socket.io-client@4/dist/socket.io.min.js",n.onload=()=>{this.initSocket(),e()},n.onerror=()=>t(new Error("Socket.IO load failed")),document.head.appendChild(n)}))}))}initSocket(){this.socket&&this.socket.disconnect(),this.socket=io(this.config.apiUrl,{auth:{token:this.visitorToken},transports:["websocket","polling"],withCredentials:!0,reconnection:!0,reconnectionAttempts:5,reconnectionDelay:1e3}),this.agentsOnline=!1,this.socket.on("connect",(()=>{this.updateConnectionStatus(!0),this.socket.emit("getOnlineUsers"),this.conversationId&&this.joinExistingConversation()})),this.socket.on("disconnect",(()=>{this.updateConnectionStatus(!1),this.agentsOnline=!1,this.updateStatus()})),this.socket.on("agentChanged",(e=>A(this,null,(function*(){e.previousAgentId===d&&e.newAgentId,yield this.forceAgentUpdate()})))),this.socket.on("agentProfileUpdated",(e=>A(this,null,(function*(){yield this.forceAgentUpdate()})))),this.socket.on("newMessage",(e=>A(this,null,(function*(){this.hideTypingIndicator(),"agent"===e.sender&&(yield this.forceAgentUpdate(),this.playNotificationSound(),this.isOpen||this.openChat()),this.displayMessage({id:e.id,content:e.message,sender:"visitor"===e.sender||"client"===e.sender?"user":"agent",timestamp:new Date(e.timestamp),senderName:e.senderName,file:e.file||null})})))),this.socket.on("typing",(e=>A(this,null,(function*(){yield this.forceAgentUpdate(),this.showTypingIndicator(),this.typingTimeout&&clearTimeout(this.typingTimeout),this.typingTimeout=setTimeout((()=>{this.hideTypingIndicator(),this.typingTimeout=null}),3e3)})))),this.socket.on("conversationClosed",(e=>{this.handleConversationClosed(e)})),this.socket.on("conversationDeleted",(e=>{var i,s;this.handleConversationClosed((i=r({},e),s={message:this.t("canRestartConversation"),timestamp:new Date},t(i,n(s))))})),this.socket.on("error",(e=>{})),this.socket.on("userOnline",(e=>{this.agentsOnline=!0,this.updateStatus()})),this.socket.on("userOffline",(e=>{this.socket.emit("getOnlineUsers")})),this.socket.on("onlineUsers",(e=>{const t=e.filter((e=>"AGENT"===e.role||"ADMIN"===e.role));this.agentsOnline=t.length>0,this.updateStatus()})),this.socket.on("onlineUsersUpdate",(e=>{const t=e.filter((e=>"AGENT"===e.role||"ADMIN"===e.role));this.agentsOnline;this.agentsOnline=t.length>0,this.updateStatus()}))}createUI(){if(!document.querySelector('link[href*="fontawesome"]')){const e=document.createElement("link");e.rel="stylesheet",e.href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css",document.head.appendChild(e)}const e=document.createElement("div");e.id="chat-widget",e.innerHTML=`\n <style>\n #chat-widget {\n position: fixed;\n bottom: 20px;\n ${"left"===this.config.widgetPosition?"left: 20px;":"right: 20px;"}\n z-index: 999999;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n \n .chat-button {\n width: 60px;\n height: 60px;\n border-radius: 50%;\n background: ${this.config.primaryColor};\n color: white;\n border: none;\n font-size: 24px;\n cursor: pointer;\n box-shadow: 0 4px 12px rgba(0,0,0,0.3);\n transition: all 0.3s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n position: relative;\n }\n \n .chat-button:hover {\n transform: scale(1.1);\n }\n \n .chat-window {\n position: absolute;\n bottom: 80px;\n ${"left"===this.config.widgetPosition?"left: 0;":"right: 0;"}\n width: 350px;\n height: 500px;\n background: white;\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0,0,0,0.2);\n display: none;\n flex-direction: column;\n overflow: hidden;\n }\n \n .chat-window.open {\n display: flex;\n }\n \n .chat-header {\n background: ${this.config.primaryColor};\n color: white;\n padding: 16px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n border-radius: 12px 12px 0 0;\n box-shadow: 0 2px 8px rgba(0,0,0,0.1);\n }\n \n .logo-container {\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(255,255,255,0.1);\n border-radius: 8px;\n padding: 4px;\n backdrop-filter: blur(10px);\n }\n \n .chat-messages {\n flex: 1;\n padding: 16px;\n overflow-y: auto;\n background: #f8f9fa;\n }\n \n .message {\n margin-bottom: 12px;\n display: flex;\n gap: 8px;\n align-items: flex-start;\n }\n \n .message.user {\n flex-direction: row-reverse;\n }\n \n /* Styles pour les noms d'expéditeur supprimés */\n \n .message-content {\n max-width: 70%;\n padding: 8px 12px;\n border-radius: 12px;\n font-size: 14px;\n line-height: 1.4;\n word-wrap: break-word;\n word-break: break-word;\n overflow-wrap: break-word;\n hyphens: auto;\n min-width: 0;\n }\n \n .message.agent .message-content {\n background: white;\n border: 1px solid #e9ecef;\n }\n \n .message.user .message-content {\n background: ${this.config.primaryColor};\n color: white;\n }\n \n .message.system .message-content {\n background: #fff3cd;\n color: #856404;\n border: 1px solid #ffeaa7;\n font-style: italic;\n text-align: center;\n margin: 0 auto;\n }\n \n .message.file .message-content {\n cursor: pointer;\n transition: background-color 0.2s;\n }\n \n .message.file.agent .message-content:hover {\n background-color: #f8f9fa;\n }\n \n .message.file.user .message-content:hover {\n background-color: rgba(255,255,255,0.1);\n }\n \n .file-attachment {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px;\n }\n \n .file-icon {\n width: 32px;\n height: 32px;\n background: #e9ecef;\n border-radius: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n color: #6c757d;\n }\n \n .file-info {\n flex: 1;\n min-width: 0;\n }\n \n .file-name {\n font-weight: 500;\n font-size: 13px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n \n .file-size {\n font-size: 11px;\n opacity: 0.7;\n }\n \n .file-preview {\n max-width: 200px;\n max-height: 150px;\n border-radius: 8px;\n cursor: pointer;\n }\n \n .chat-input-container {\n padding: 16px;\n background: white;\n border-top: 1px solid #e9ecef;\n display: flex;\n flex-direction: column;\n gap: 8px;\n }\n\n .character-counter {\n font-size: 12px;\n color: #6c757d;\n text-align: right;\n margin-top: 4px;\n }\n\n .character-counter.limit-reached {\n color: #dc3545;\n }\n \n .character-counter {\n font-size: 12px;\n color: #6c757d;\n text-align: right;\n margin-top: 4px;\n }\n \n .character-counter.limit-reached {\n color: #dc3545;\n }\n \n .emoji-button {\n background: none;\n border: none;\n font-size: 20px;\n cursor: pointer;\n padding: 4px 8px;\n transition: transform 0.2s;\n }\n\n .emoji-button:hover {\n transform: scale(1.1);\n }\n\n .emoji-picker {\n display: none;\n position: absolute;\n bottom: 100%;\n left: 0;\n background: white;\n border: 1px solid #e9ecef;\n border-radius: 8px;\n padding: 8px;\n box-shadow: 0 -2px 10px rgba(0,0,0,0.1);\n width: 100%;\n max-height: 200px;\n overflow-y: auto;\n grid-template-columns: repeat(8, 1fr);\n gap: 4px;\n z-index: 999998;\n }\n\n .emoji-picker.open {\n display: grid;\n }\n\n .emoji-item {\n font-size: 20px;\n padding: 4px;\n cursor: pointer;\n text-align: center;\n border-radius: 4px;\n transition: background-color 0.2s;\n }\n\n .emoji-item:hover {\n background-color: #f8f9fa;\n }\n \n .input-row {\n display: flex;\n align-items: center;\n gap: 8px;\n position: relative;\n }\n \n .attachment-button {\n background: none;\n border: none;\n font-size: 20px;\n cursor: pointer;\n padding: 4px 8px;\n color: #6c757d;\n transition: transform 0.2s;\n }\n\n .attachment-button:hover {\n transform: scale(1.1);\n }\n \n .chat-input {\n flex: 1;\n padding: 8px 12px;\n border: 1px solid #e9ecef;\n border-radius: 20px;\n font-size: 14px;\n resize: none;\n max-height: 100px;\n min-height: 40px;\n }\n \n .chat-input:focus {\n outline: none;\n border-color: ${this.config.primaryColor};\n }\n \n .send-button {\n background: none;\n border: none;\n color: ${this.config.primaryColor};\n font-size: 20px;\n cursor: pointer;\n padding: 8px;\n transition: all 0.3s ease;\n }\n \n .send-button:hover {\n transform: scale(1.1);\n }\n \n .send-button.has-file {\n background: ${this.config.primaryColor};\n color: white;\n border-radius: 50%;\n animation: pulse 1.5s infinite;\n }\n \n\n \n @keyframes pulse {\n 0% { transform: scale(1); }\n 50% { transform: scale(1.05); }\n 100% { transform: scale(1); }\n }\n \n .typing-indicator {\n display: none;\n padding: 8px;\n font-size: 12px;\n color: #6c757d;\n font-style: italic;\n }\n \n .typing-indicator.visible {\n display: block;\n }\n \n .status-indicator {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n margin-right: 8px;\n }\n \n .status-indicator.online {\n background: #28a745;\n }\n \n .status-indicator.offline {\n background: #dc3545;\n }\n \n .file-input {\n display: none;\n }\n \n .file-preview-container {\n display: none;\n padding: 8px;\n background: #fff3cd;\n border: 1px solid #ffeaa7;\n border-radius: 8px;\n margin-bottom: 8px;\n position: relative;\n }\n \n .file-preview-container.visible {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n \n .file-preview-container::before {\n content: "📎 ${1===this.selectedFiles.length?this.t("fileSelected"):this.t("filesSelected")}";\n position: absolute;\n top: -20px;\n left: 0;\n font-size: 11px;\n color: #856404;\n font-weight: 500;\n }\n \n .file-preview-item {\n display: flex;\n align-items: center;\n padding: 8px;\n background: #f7fafc;\n border-radius: 8px;\n margin-bottom: 8px;\n position: relative;\n }\n \n .file-preview-item .file-icon {\n margin-right: 12px;\n font-size: 20px;\n color: #4a5568;\n }\n \n .file-preview-item .file-info {\n flex: 1;\n }\n \n .file-preview-item .file-name {\n font-weight: 500;\n color: #2d3748;\n margin-bottom: 2px;\n word-break: break-word;\n }\n \n .file-preview-item .file-size {\n font-size: 12px;\n color: #718096;\n }\n \n .remove-file-btn {\n background: #e53e3e;\n color: white;\n border: none;\n border-radius: 50%;\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n font-size: 12px;\n margin-left: 8px;\n transition: background-color 0.2s ease;\n }\n \n .remove-file-btn:hover {\n background: #c53030;\n }\n \n .clear-file {\n color: #dc3545;\n cursor: pointer;\n font-size: 16px;\n }\n \n .upload-progress {\n display: none;\n height: 2px;\n background: #e9ecef;\n border-radius: 2px;\n overflow: hidden;\n }\n \n .upload-progress.visible {\n display: block;\n }\n \n .upload-progress-bar {\n height: 100%;\n background: ${this.config.primaryColor};\n width: 0;\n transition: width 0.3s ease;\n }\n \n .file-preview-modal {\n display: none;\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.8);\n z-index: 1000000;\n justify-content: center;\n align-items: center;\n cursor: pointer;\n }\n\n .file-preview-modal.visible {\n display: flex;\n }\n\n .file-preview-modal img {\n max-width: 90%;\n max-height: 90vh;\n object-fit: contain;\n border-radius: 8px;\n }\n\n .file-preview-modal iframe {\n width: 90%;\n height: 90vh;\n border: none;\n background: white;\n border-radius: 8px;\n }\n\n .file-preview-modal .close-button {\n position: absolute;\n top: 20px;\n right: 20px;\n color: white;\n font-size: 24px;\n cursor: pointer;\n background: none;\n border: none;\n padding: 8px;\n }\n \n @media (max-width: 480px) {\n .chat-window {\n width: calc(100vw - 40px);\n height: calc(100vh - 100px);\n bottom: 70px;\n }\n }\n </style>\n \n <button class="chat-button">\n <i class="fas fa-comments"></i>\n </button>\n \n <div class="chat-window">\n <div class="chat-header">\n <div style="display: flex; align-items: center; gap: 12px;">\n \x3c!-- Logo 001 TRANSLATIONS --\x3e\n <div class="logo-container">\n <svg width="40" height="40" viewBox="0 0 100 100" style="border-radius: 8px;">\n \x3c!-- Fond avec la couleur primaire --\x3e\n <rect width="100" height="100" fill="${this.config.primaryColor}" rx="8"/>\n \n \x3c!-- Cercle gauche (00) --\x3e\n <circle cx="35" cy="35" r="15" fill="rgba(255,255,255,0.9)"/>\n <circle cx="35" cy="35" r="12" fill="rgba(255,255,255,0.7)"/>\n \n \x3c!-- Cercle droit (00) --\x3e\n <circle cx="55" cy="35" r="15" fill="rgba(255,255,255,0.9)"/>\n <circle cx="55" cy="35" r="12" fill="rgba(255,255,255,0.7)"/>\n \n \x3c!-- Chiffre 1 --\x3e\n <rect x="70" y="20" width="8" height="30" fill="rgba(255,255,255,0.9)" rx="4"/>\n <rect x="68" y="18" width="4" height="8" fill="rgba(255,255,255,0.9)" rx="2"/>\n \n \x3c!-- Texte TRANSLATIONS --\x3e\n <text x="50" y="75" text-anchor="middle" fill="white" font-family="Arial, sans-serif" font-size="8" font-weight="bold">TRANSLATIONS</text>\n </svg>\n </div>\n \n <div>\n <div style="font-weight: 600;" class="agent-name">${this.config.agentName}</div>\n </div>\n </div>\n <i class="fas fa-times" style="cursor: pointer;"></i>\n </div>\n \n <div class="chat-messages"></div>\n \n <div class="typing-indicator">\n <i class="fas fa-ellipsis-h"></i> ${this.t("typing")}\n </div>\n \n <div class="chat-input-container">\n <div class="file-preview-container">\n <div class="file-preview-content"></div>\n <i class="fas fa-times clear-file"></i>\n </div>\n \n <div class="upload-progress">\n <div class="upload-progress-bar"></div>\n </div>\n \n <div class="input-row">\n <button class="emoji-button">😊</button>\n <button class="attachment-button">\n <i class="fas fa-paperclip"></i>\n </button>\n <div class="emoji-picker"></div>\n <textarea class="chat-input" placeholder="${this.t("placeholder")}" rows="1" maxlength="1000"></textarea>\n <button class="send-button">\n <i class="fas fa-paper-plane"></i>\n </button>\n <input type="file" class="file-input" accept="image/*,application/pdf,.doc,.docx,.txt,.mp3,.wav,.mp4" multiple />\n </div>\n <div class="character-counter">0${this.t("characterCounter")}</div>\n </div>\n </div>\n `,document.body.appendChild(e),this.chatButton=e.querySelector(".chat-button"),this.chatWindow=e.querySelector(".chat-window"),this.closeButton=e.querySelector(".fa-times"),this.messagesContainer=e.querySelector(".chat-messages"),this.inputField=e.querySelector(".chat-input"),this.sendButton=e.querySelector(".send-button"),this.fileInput=e.querySelector(".file-input"),this.filePreviewContainer=e.querySelector(".file-preview-container"),this.filePreviewContent=e.querySelector(".file-preview-content"),this.clearFileButton=e.querySelector(".clear-file"),this.uploadProgress=e.querySelector(".upload-progress"),this.uploadProgressBar=e.querySelector(".upload-progress-bar"),this.typingIndicator=e.querySelector(".typing-indicator"),this.statusIndicator=e.querySelector(".status-indicator"),this.agentNameElement=e.querySelector(".agent-name"),this.characterCounter=e.querySelector(".character-counter"),this.emojiButton=e.querySelector(".emoji-button"),this.emojiPicker=e.querySelector(".emoji-picker"),this.attachmentButton=e.querySelector(".attachment-button"),this.initEmojiPicker(),this.chatButton.addEventListener("click",(()=>this.toggleChat())),this.closeButton.addEventListener("click",(()=>this.closeChat())),this.sendButton.addEventListener("click",(()=>this.sendMessage())),this.attachmentButton.addEventListener("click",(()=>this.selectFile())),this.fileInput.addEventListener("change",(e=>this.handleFileSelect(e))),this.clearFileButton.addEventListener("click",(()=>this.clearSelectedFiles())),this.inputField.addEventListener("keypress",(e=>{"Enter"!==e.key||e.shiftKey||(e.preventDefault(),this.sendMessage())})),this.inputField.addEventListener("input",(()=>{this.inputField.style.height="auto",this.inputField.style.height=this.inputField.scrollHeight+"px";const e=this.inputField.value.length;this.characterCounter.textContent=`${e}${this.t("characterCounter")}`,this.characterCounter.classList.toggle("limit-reached",e>=1e3),this.sendTypingEvent()})),this.emojiButton.addEventListener("click",(()=>this.toggleEmojiPicker())),document.addEventListener("click",(e=>{this.emojiPicker.contains(e.target)||this.emojiButton.contains(e.target)||this.emojiPicker.classList.remove("open")})),this.conversationId||this.displayMessage({content:this.config.welcomeMessage,sender:"agent",timestamp:new Date,senderName:this.config.agentName}),this.updateStatus()}initEmojiPicker(){["😊","😂","🤣","❤️","👍","👋","🙌","👏","🎉","✨","🌟","💡","📝","💬","🤔","👀","😅","😉","😍","🥰","😎","🤩","😇","🤗","😋","😄","😃","😀","🙂","😌","😏","😮","😱","😳","🤭","😬","😅","😓","😥","😰","👌","🤝","🙏","💪","🤟","✌️","🤞","🤙","💯","⭐","🔥","💫","💨","💦","👉","👈"].forEach((e=>{const t=document.createElement("div");t.className="emoji-item",t.textContent=e,t.addEventListener("click",(()=>this.insertEmoji(e))),this.emojiPicker.appendChild(t)}))}toggleEmojiPicker(){this.emojiPicker.classList.toggle("open")}insertEmoji(e){const t=this.inputField.selectionStart,n=this.inputField.value.substring(0,t),i=this.inputField.value.substring(t);if(n.length+e.length+i.length>1e3)return;this.inputField.value=n+e+i,this.inputField.focus();const s=t+e.length;this.inputField.setSelectionRange(s,s);const o=this.inputField.value.length;this.characterCounter.textContent=`${o}${this.t("characterCounter")}`,this.characterCounter.classList.toggle("limit-reached",o>=1e3),this.emojiPicker.classList.remove("open")}openChat(){if(!this.isOpen){this.isOpen=!0,this.chatWindow.classList.add("open");try{this.inputField.focus()}catch(e){}setTimeout((()=>{this.messagesContainer.scrollTop=this.messagesContainer.scrollHeight}),100)}}toggleChat(){this.isOpen?this.closeChat():this.openChat()}closeChat(){this.isOpen=!1,this.chatWindow.classList.remove("open")}sendMessage(){return A(this,null,(function*(){const e=this.inputField.value.trim(),t=this.selectedFiles;if(e||0!==t.length){this.inputField.value="",this.inputField.style.height="auto",this.characterCounter.textContent=`0${this.t("characterCounter")}`,this.characterCounter.classList.remove("limit-reached");try{this.conversationId||(this.clearConversationId(),yield this.createConversation()),t.length>0?(yield this.sendMessageWithFiles(e,t),this.clearSelectedFiles()):(this.displayMessage({id:"temp-"+Date.now(),content:e,sender:"user",timestamp:new Date}),this.socket&&this.socket.connected&&this.socket.emit("sendMessage",{conversationId:this.conversationId,message:e},(e=>{e&&e.error})))}catch(n){}}}))}selectFile(){this.fileInput.click()}handleFileSelect(e){const t=Array.from(e.target.files);if(0===t.length)return;if(this.selectedFiles.length+t.length>5)return alert(this.t("tooManyFiles")),void(e.target.value="");const n=[];for(const i of t)this.validateFile(i)&&n.push(i);this.selectedFiles.push(...n),this.showFilesPreview(),e.target.value=""}validateFile(e){return["image/jpeg","image/png","image/gif","image/webp","application/pdf","application/msword","application/vnd.openxmlformats-officedocument.wordprocessingml.document","text/plain","audio/mpeg","audio/wav","video/mp4"].includes(e.type)?!(e.size>10485760)||(alert(this.t("fileSizeError")),!1):(alert(this.t("fileTypeError")),!1)}showFilesPreview(){if(0===this.selectedFiles.length)return void this.filePreviewContainer.classList.remove("visible");const e=this.selectedFiles.map(((e,t)=>{const n=this.getFileIcon(e.type),i=this.formatFileSize(e.size),s=this.normalizeFilenameForDisplay(e.name);return`\n <div class="file-preview-item" data-index="${t}">\n <div class="file-icon">${n}</div>\n <div class="file-info">\n <div class="file-name" title="${s}">${s}</div>\n <div class="file-size">${i}</div>\n </div>\n <button class="remove-file-btn" data-index="${t}">\n <i class="fas fa-times"></i>\n </button>\n </div>\n `})).join("");this.filePreviewContent.innerHTML=e,this.filePreviewContainer.classList.add("visible"),this.sendButton.classList.add("has-file"),this.sendButton.title=1===this.selectedFiles.length?"Cliquer pour envoyer le fichier":`Cliquer pour envoyer ${this.selectedFiles.length} fichiers`;const t=1===this.selectedFiles.length?"Ajoutez un message avec ce fichier (optionnel)...":`Ajoutez un message avec ces ${this.selectedFiles.length} fichiers (optionnel)...`;this.inputField.placeholder=t,this.filePreviewContent.querySelectorAll(".remove-file-btn").forEach((e=>{e.addEventListener("click",(t=>{t.stopPropagation();const n=parseInt(e.getAttribute("data-index"));this.removeFileByIndex(n)}))}))}removeFileByIndex(e){this.selectedFiles.splice(e,1),this.showFilesPreview()}clearSelectedFiles(){this.selectedFiles=[],this.filePreviewContainer.classList.remove("visible"),this.filePreviewContent.innerHTML="",this.sendButton.classList.remove("has-file"),this.sendButton.title="Envoyer le message",this.inputField.placeholder=this.t("placeholder")}sendMessageWithFiles(e,t){return A(this,null,(function*(){try{this.setUploading(!0);const n=new FormData;t.forEach(((e,t)=>{n.append("files",e)})),e&&n.append("content",e);const i=yield fetch(`${this.config.apiUrl}/api/webchat/conversation/${this.conversationId}/message-with-files`,{method:"POST",headers:{Authorization:`Bearer ${this.visitorToken}`},body:n});if(!i.ok)throw new Error(`Upload failed: ${i.status}`);this.inputField.value="",this.inputField.style.height="auto",this.characterCounter.textContent=`0${this.t("characterCounter")}`,this.characterCounter.classList.remove("limit-reached")}catch(n){alert("Erreur lors de l'envoi des fichiers. Veuillez réessayer.")}finally{this.setUploading(!1)}}))}setUploading(e){e?this.uploadProgress.classList.add("visible"):(this.uploadProgress.classList.remove("visible"),this.uploadProgressBar.style.width="0%")}getFileIcon(e){return e?e.startsWith("image/")?'<i class="fas fa-image"></i>':"application/pdf"===e?'<i class="fas fa-file-pdf"></i>':e.includes("word")?'<i class="fas fa-file-word"></i>':e.startsWith("audio/")?'<i class="fas fa-file-audio"></i>':e.startsWith("video/")?'<i class="fas fa-file-video"></i>':e.includes("text")||"text/plain"===e?'<i class="fas fa-file-alt"></i>':e.includes("excel")||e.includes("spreadsheet")?'<i class="fas fa-file-excel"></i>':e.includes("powerpoint")||e.includes("presentation")?'<i class="fas fa-file-powerpoint"></i>':e.includes("zip")||e.includes("rar")||e.includes("7z")?'<i class="fas fa-file-archive"></i>':'<i class="fas fa-file"></i>':'<i class="fas fa-paperclip"></i>'}formatFileSize(e){if(0===e)return"0 B";const t=Math.floor(Math.log(e)/Math.log(1024));return parseFloat((e/Math.pow(1024,t)).toFixed(2))+" "+["B","KB","MB","GB"][t]}createConversation(){return A(this,null,(function*(){try{const e=yield fetch(`${this.config.apiUrl}/api/webchat/conversation/${this.config.siteId}`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.visitorToken}`},body:JSON.stringify({visitorName:"Visiteur",initialMessage:null})});if(!e.ok)throw new Error(`Conversation failed: ${e.status}`);const t=yield e.json();this.conversationId=t.conversationId;this.saveConversationId(this.conversationId),localStorage.getItem(this.storageKey);this.socket&&this.socket.connected&&(this.socket.emit("joinConversation",this.conversationId),this.soundManager.playConnectionSound()),this.updateAgentHeader({agentName:this.t("phantomAgentName"),agentAvatar:null,agentId:d,agentEmail:h}),setTimeout((()=>A(this,null,(function*(){yield this.getAssignedAgent()}))),1e3)}catch(e){throw e}}))}displayMessage(e){const t=this.messagesContainer,n=document.createElement("div");n.className=`message ${e.sender}`;let i="";if(e.file){const t=e.file.type||e.file.mimeType||"",s=(e.file.name||e.file.filename,e.file.size||0),o=e.file.url||"#",a=this.getFileIcon(t),r=this.formatFileSize(s);i=t.startsWith("image/")?`\n ${e.content?`<div style="margin-bottom: 8px;">${e.content}</div>`:""}\n <div class="file-attachment" data-file-url="${o}" data-file-type="${t}" onclick="window.chatWidget.previewFile(event, '${o}', '${t}')">\n <img src="${o}" alt="Image" class="file-preview" \n onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';" />\n <div class="file-attachment" style="display: none;">\n <div class="file-icon">${a}</div>\n <div class="file-info">\n <div class="file-size">${r}</div>\n </div>\n </div>\n </div>\n `:`\n ${e.content?`<div style="margin-bottom: 8px;">${e.content}</div>`:""}\n <div class="file-attachment" data-file-url="${o}" data-file-type="${t}" onclick="window.chatWidget.previewFile(event, '${o}', '${t}')">\n <div class="file-icon">${a}</div>\n <div class="file-info">\n <div class="file-size">${r}</div>\n </div>\n </div>\n `,n.classList.add("file")}else i=e.content;n.innerHTML=`\n \n <div class="message-content">\n ${i}\n </div>\n `,t.appendChild(n),t.scrollTop=t.scrollHeight,this.messages.push(e)}updateConnectionStatus(e){this.connected=e,this.updateStatus()}updateStatus(){this.statusIndicator&&(this.statusIndicator.classList.toggle("online",this.agentsOnline),this.statusIndicator.classList.toggle("offline",!this.agentsOnline));const e=this.chatWindow.querySelector(".status-text");e&&(e.textContent=this.agentsOnline?this.t("online"):this.t("offline"))}showTypingIndicator(){this.typingIndicator&&this.typingIndicator.classList.add("visible")}hideTypingIndicator(){this.typingIndicator&&this.typingIndicator.classList.remove("visible")}sendTypingEvent(){this.socket&&this.socket.connected&&this.conversationId&&this.socket.emit("typing",{conversationId:this.conversationId})}handleConversationClosed(e){this.messagesContainer&&(this.messagesContainer.innerHTML=""),this.conversationId=null,this.messages=[],this.clearConversationId();const t=this.inputField,n=this.sendButton;t&&(t.disabled=!0,t.placeholder=this.t("conversationClosedPlaceholder")),n&&(n.disabled=!0,n.innerHTML='<i class="fas fa-times"></i>'),setTimeout((()=>{t&&(t.disabled=!1,t.placeholder=this.t("sendMessage"),t.focus()),n&&(n.disabled=!1,n.innerHTML='<i class="fas fa-paper-plane"></i>'),this.messagesContainer&&this.displayMessage({id:"system-restart-"+Date.now(),content:this.t("canRestartConversation"),sender:"system",timestamp:new Date})}),1e3)}updateAgentName(e){if(!e||e===this.config.agentName)return;this.config.agentName=e;const t=this.agentNameElement;t&&(t.textContent=e)}getAssignedAgent(){return A(this,null,(function*(){try{if(!this.conversationId||!this.visitorToken)return null;const e=yield fetch(`${this.config.apiUrl}/api/webchat/conversation/${this.conversationId}/agent`,{method:"GET",headers:{Authorization:`Bearer ${this.visitorToken}`,"Content-Type":"application/json"}});if(e.ok){const t=yield e.json(),n=u(t);return this.currentAgentId=t.agentId,n||(this.updateAgentHeader({agentName:t.agentName,agentAvatar:t.agentAvatar,agentId:t.agentId,agentEmail:t.agentEmail}),this.isPhantomAgent&&this.showAgentTransitionNotification(t.agentName)),t}return null}catch(e){return null}}))}forceAgentUpdate(){return A(this,null,(function*(){try{if(!this.conversationId||!this.visitorToken)return;let t=0;const n=3;for(;t<n;)try{const e=yield fetch(`${this.config.apiUrl}/api/webchat/conversation/${this.conversationId}/agent`,{method:"GET",headers:{Authorization:`Bearer ${this.visitorToken}`,"Content-Type":"application/json"},cache:"no-cache"});if(404===e.status)return this.resetWidget(),null;if(!e.ok)throw new Error(`Erreur HTTP: ${e.status}`);const t=yield e.json(),n=this.isPhantomAgent,i=u(t);return n&&!i&&this.showAgentTransitionNotification(t.agentName),this.updateAgentHeader({agentName:t.agentName,agentAvatar:t.agentAvatar,agentId:t.agentId,agentEmail:t.agentEmail}),t}catch(e){if(t++,e.message.includes("404"))return this.resetWidget(),null;if(t===n)throw e;yield new Promise((e=>setTimeout(e,1e3*t)))}}catch(e){return this.resetWidget(),null}}))}updateAgentHeader(e){if(!e)return;const t=this.chatWindow.querySelector(".chat-header > div");if(!t)return;t.innerHTML="";const n=u(e);let i,s=e.agentName||"Support",o=e.agentAvatar;if(n){s=this.t("phantomAgentName");const e=`\n <div style="display: flex; align-items: center; gap: 12px;">\n \x3c!-- Logo 001 TRANSLATIONS pour l'agent phantom --\x3e\n <div class="logo-container">\n <svg width="40" height="40" viewBox="0 0 100 100" style="border-radius: 8px;">\n \x3c!-- Fond avec la couleur primaire --\x3e\n <rect width="100" height="100" fill="${this.config.primaryColor}" rx="8"/>\n \n \x3c!-- Cercle gauche (00) --\x3e\n <circle cx="35" cy="35" r="15" fill="rgba(255,255,255,0.9)"/>\n <circle cx="35" cy="35" r="12" fill="rgba(255,255,255,0.7)"/>\n \n \x3c!-- Cercle droit (00) --\x3e\n <circle cx="55" cy="35" r="15" fill="rgba(255,255,255,0.9)"/>\n <circle cx="55" cy="35" r="12" fill="rgba(255,255,255,0.7)"/>\n \n \x3c!-- Chiffre 1 --\x3e\n <rect x="70" y="20" width="8" height="30" fill="rgba(255,255,255,0.9)" rx="4"/>\n <rect x="68" y="18" width="4" height="8" fill="rgba(255,255,255,0.9)" rx="2"/>\n \n \x3c!-- Texte TRANSLATIONS --\x3e\n <text x="50" y="75" text-anchor="middle" fill="white" font-family="Arial, sans-serif" font-size="8" font-weight="bold">TRANSLATIONS</text>\n </svg>\n </div>\n <div>\n <div style="font-weight: 600; color: white; display: flex; align-items: center; gap: 8px;" class="agent-name">\n <div style="width: 8px; height: 8px; background-color: #4CAF50; border: 1px solid white; border-radius: 50%; flex-shrink: 0;"></div>\n ${s}\n </div>\n <div style="font-size: 12px; color: rgba(255,255,255,0.8); white-space: pre-line;" class="agent-status">${this.t("waitingForAgent")}</div>\n </div>\n </div>\n `;return t.innerHTML=e,this.agentNameElement=t.querySelector(".agent-name"),this.config.agentName=s,void(this.isPhantomAgent=!0)}i=o?`\n <div style="display: flex; align-items: center; gap: 8px;">\n <img src="${o}?t=${Date.now()}" alt="Avatar agent" style="width: 32px; height: 32px; border-radius: 50%; object-fit: cover;">\n <div>\n <div style="font-weight: 600;" class="agent-name">${s}</div>\n </div>\n </div>\n `:`\n <div>\n <div style="font-weight: 600; display: flex; align-items: center; gap: 8px;" class="agent-