@wabbit-dashboard/embed 1.1.1 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Wabbit={})}(this,function(e){"use strict";function t(e){if(!e.apiKey)throw new Error("[Wabbit] API key is required");if("string"!=typeof e.apiKey)throw new Error("[Wabbit] API key must be a string");if(e.chat?.enabled){if(!e.chat.collectionId)throw new Error("[Wabbit] collectionId is required when chat is enabled");if("inline"===e.chat.mode&&!e.chat.container)throw new Error('[Wabbit] container is required when chat mode is "inline"')}if(e.forms?.enabled&&!e.forms.formId)throw new Error("[Wabbit] formId is required when forms is enabled")}function n(e){const t=function(e){if(e)return e;if("undefined"!=typeof window&&window.WABBIT_API_URL)return window.WABBIT_API_URL;if("undefined"!=typeof window){const e=window.location.hostname;if("localhost"===e||"127.0.0.1"===e||"0.0.0.0"===e)return"http://localhost:3000"}return"https://platform.insourcedata.ai"}(e.apiUrl),n=function(e,t){return e||("undefined"!=typeof window&&window.WABBIT_WS_URL?window.WABBIT_WS_URL:t&&(t.includes("localhost")||t.includes("127.0.0.1"))?"ws://localhost:8003/ws/chat":"wss://chat.insourcedata.ai/ws/chat")}(e.wsUrl,t);return{...e,apiUrl:t,wsUrl:n,chat:e.chat?{...e.chat,mode:e.chat.mode||"widget",position:e.chat.position||"bottom-right",triggerType:e.chat.triggerType||"button",theme:e.chat.theme||"auto",primaryColor:e.chat.primaryColor||"#6366f1",placeholder:e.chat.placeholder||"Type your message...",showHeader:e.chat.showHeader??!0,headerTitle:e.chat.headerTitle||"AI Assistant"}:void 0,forms:e.forms?{...e.forms,theme:e.forms.theme||"auto"}:void 0,emailCapture:e.emailCapture?{...e.emailCapture,triggerAfterMessages:e.emailCapture.triggerAfterMessages||3,fields:e.emailCapture.fields||["email"]}:void 0}}class i{constructor(){this.store=new Map}getItem(e){return this.store.get(e)??null}setItem(e,t){this.store.set(e,t)}removeItem(e){this.store.delete(e)}clear(){this.store.clear()}get length(){return this.store.size}key(e){return Array.from(this.store.keys())[e]??null}}class s{constructor(e=localStorage,t="wabbit_"){this.storage=e,this.prefix=t}get(e){try{const t=this.storage.getItem(this.prefix+e);return null===t?null:JSON.parse(t)}catch(t){return console.error(`[Wabbit] Failed to get storage item "${e}":`,t),null}}set(e,t){try{return this.storage.setItem(this.prefix+e,JSON.stringify(t)),!0}catch(t){return console.error(`[Wabbit] Failed to set storage item "${e}":`,t),!1}}remove(e){this.storage.removeItem(this.prefix+e)}clear(){if(void 0!==this.storage.length&&this.storage.key){const e=[];for(let t=0;t<this.storage.length;t++){const n=this.storage.key(t);n&&n.startsWith(this.prefix)&&e.push(n)}e.forEach(e=>this.storage.removeItem(e))}else{["session_id","email_capture_dismissed"].forEach(e=>this.remove(e))}}}const a=new s;function r(e){try{return localStorage.getItem("wabbit_"+e)}catch{return null}}function o(e,t){try{localStorage.setItem("wabbit_"+e,t)}catch(t){console.error(`[Wabbit] Failed to set storage item "${e}":`,t)}}class l{constructor(e){this.chatWidget=null,this.formsWidget=null,this.emailCaptureWidget=null,this.config=e,this.storage=function(e=!0){let t;return t=e?"undefined"!=typeof window?window.localStorage:new i:"undefined"!=typeof window?window.sessionStorage:new i,new s(t)}(e.persistSession??!0)}static init(e){if(l.instance)return console.warn("[Wabbit] SDK already initialized. Returning existing instance."),l.instance;t(e);const i=n(e);l.instance=new l(i);let s=null;if(i.chat?.enabled&&i.chat){const t=i.chat,n=t.collectionId,a=t.onChatReady||e.onChatReady,r=(i.wsUrl||t.wsUrl||"wss://chat.insourcedata.ai/ws/chat").replace(/^wss:\/\//,"https://").replace(/^ws:\/\//,"http://").replace(/\/ws\/chat.*$/,"");Promise.resolve().then(function(){return v}).then(({ChatWidget:e})=>{if(!l.instance)return void console.warn("[Wabbit] Instance was destroyed before chat widget could initialize");const n={enabled:t.enabled,collectionId:t.collectionId,apiKey:t.apiKey||i.apiKey,apiUrl:t.apiUrl||i.apiUrl,wsUrl:i.wsUrl||t.wsUrl,mode:t.mode,container:t.container,position:t.position,triggerType:t.triggerType,triggerDelay:t.triggerDelay,theme:t.theme,primaryColor:t.primaryColor,welcomeMessage:t.welcomeMessage,placeholder:t.placeholder,showHeader:t.showHeader,headerTitle:t.headerTitle,onChatReady:()=>{if(s&&l.instance?.chatWidget){const e=l.instance.chatWidget;e.wsClient&&s.setWebSocketClient(e.wsClient)}a&&a()},onUserMessage:()=>{s&&s.handleMessage()}},r=new e(n,l.instance.storage);r.init(),l.instance.chatWidget=r}),fetch(`${r}/api/email-capture/config/${n}`).then(e=>e.json()).then(async e=>{if(!e.success||!e.config?.enabled)return;if(!l.instance)return;const t=e.config,{EmailCaptureWidget:n}=await Promise.resolve().then(function(){return W});if(!l.instance)return;const a="message_count"===t.trigger?.type?t.trigger.messageCount:3,r=["email"];t.fields?.name?.enabled&&r.push("name"),t.fields?.company?.enabled&&r.push("company");const o=new n({enabled:!0,triggerAfterMessages:a,title:t.modal?.title,description:t.modal?.description,fields:r,onCapture:i.emailCapture?.onCapture});if(o.init(),l.instance.emailCaptureWidget=o,s=o,l.instance.chatWidget){const e=l.instance.chatWidget;e.wsClient&&o.setWebSocketClient(e.wsClient)}}).catch(e=>{console.warn("[Wabbit] Failed to fetch email capture config:",e)})}return i.forms?.enabled&&i.forms&&Promise.resolve().then(function(){return k}).then(({FormWidget:e})=>{if(!l.instance)return void console.warn("[Wabbit] Instance was destroyed before form widget could initialize");const t=i.forms,n=new e({enabled:t.enabled,containerId:t.containerId,formId:t.formId,apiKey:t.apiKey||i.apiKey,apiUrl:t.apiUrl||i.apiUrl,theme:t.theme,primaryColor:t.primaryColor,onSubmit:t.onSubmit,onError:t.onError,onLoad:t.onLoad});n.init(),l.instance.formsWidget=n}),l.instance&&l.instance.initLegacyForms(e),e.onReady&&("loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{e.onReady?.()}):e.onReady()),l.instance}static getInstance(){return l.instance}static destroy(){if(l.instance){l.instance.chatWidget&&l.instance.chatWidget.destroy(),l.instance.formsWidget&&l.instance.formsWidget.destroy();document.querySelectorAll("[data-wabbit-form-id]").forEach(e=>{const t=e.__wabbitFormWidget;t&&"function"==typeof t.destroy&&t.destroy()}),l.instance.legacyFormObserver&&(l.instance.legacyFormObserver.disconnect(),l.instance.legacyFormObserver=null),l.instance.emailCaptureWidget&&l.instance.emailCaptureWidget.destroy(),l.instance=null}}static getChatPageUrl(e,t){const n=t?.baseUrl||"undefined"!=typeof window&&window.WABBIT_BASE_URL||("undefined"==typeof window||"localhost"!==window.location.hostname&&"127.0.0.1"!==window.location.hostname?"https://platform.insourcedata.ai":"http://localhost:3000"),i=new URL(`/c/${e}`,n);return t?.initialMessage&&i.searchParams.set("q",t.initialMessage),t?.theme&&"auto"!==t.theme&&i.searchParams.set("theme",t.theme),t?.primaryColor&&i.searchParams.set("color",t.primaryColor.replace("#","")),i.toString()}static openChatPage(e,t){const n=l.getChatPageUrl(e,t);window.open(n,"_blank","noopener,noreferrer")}getConfig(){return{...this.config}}get chat(){return this.chatWidget}get forms(){return this.formsWidget}get emailCapture(){return this.emailCaptureWidget}initLegacyForms(e){e.apiKey&&("loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{this.initLegacyFormsOnReady(e)}):this.initLegacyFormsOnReady(e))}initLegacyFormsOnReady(e){const t=document.querySelectorAll("[data-wabbit-form-id]");0!==t.length&&(console.log("[Wabbit] Found",t.length,"legacy form container(s)"),t.forEach(t=>{const n=t.getAttribute("data-wabbit-form-id");n&&(t.querySelector('[data-wabbit-form-instance="true"]')?console.warn(`[Wabbit] Form ${n} already initialized`):Promise.resolve().then(function(){return k}).then(({FormWidget:i})=>{const s=new i({enabled:!0,containerId:void 0,formId:n,apiKey:e.apiKey,apiUrl:e.apiUrl,theme:e.forms?.theme||"auto",primaryColor:e.forms?.primaryColor,onSubmit:e.forms?.onSubmit,onError:e.forms?.onError,onLoad:e.forms?.onLoad});s.findContainer=()=>t,s.init(),t.__wabbitFormWidget=s}))}),this.setupLegacyFormObserver(e))}setupLegacyFormObserver(e){if(this.legacyFormObserver)return;const t=new MutationObserver(t=>{for(const n of t)if(n.addedNodes.length){if(Array.from(n.addedNodes).some(e=>{if(1!==e.nodeType)return!1;const t=e;return t.hasAttribute?.("data-wabbit-form-id")||t.querySelector?.("[data-wabbit-form-id]")})){console.log("[Wabbit] Detected dynamically added legacy form container"),this.initLegacyFormsOnReady(e);break}}});t.observe(document.body,{childList:!0,subtree:!0}),this.legacyFormObserver=t}}l.instance=null;class c{constructor(e,t,n=null,i){this.ws=null,this.status="disconnected",this.reconnectAttempts=0,this.maxReconnectAttempts=3,this.reconnectDelay=1e3,this.messageQueue=[],this.MAX_QUEUE_SIZE=10,this.onStatusChange=null,this.onWelcome=null,this.onMessage=null,this.onMessageHistory=null,this.onError=null,this.onDisconnect=null,this.onQueueOverflow=null,this.onMessageStart=null,this.onMessageChunk=null,this.onMessageEnd=null,this.apiKey=e,this.wsUrl=t,this.storage=i,this.sessionId=n||this.getStoredSessionId()}setStatus(e){this.status=e,this.onStatusChange&&this.onStatusChange(e)}getStatus(){return this.status}async connect(){if(this.ws&&(this.ws.readyState===WebSocket.OPEN||this.ws.readyState===WebSocket.CONNECTING))return;this.setStatus("connecting");const e=new URLSearchParams({api_key:this.apiKey});this.sessionId&&e.append("session_id",this.sessionId);const t=`${this.wsUrl}?${e.toString()}`;try{this.ws=new WebSocket(t),this.ws.onopen=()=>{this.setStatus("connected"),this.reconnectAttempts=0,console.log("[Wabbit] WebSocket connected"),this.flushMessageQueue()},this.ws.onmessage=e=>{try{const t=JSON.parse(e.data);this.handleMessage(t)}catch(e){console.error("[Wabbit] Failed to parse message:",e)}},this.ws.onerror=e=>{console.error("[Wabbit] WebSocket error:",e),this.onError&&this.onError("Connection error occurred")},this.ws.onclose=()=>{console.log("[Wabbit] WebSocket closed"),this.setStatus("disconnected"),this.onDisconnect&&this.onDisconnect(),this.attemptReconnect()}}catch(e){console.error("[Wabbit] Failed to connect:",e),this.onError&&this.onError("Failed to establish connection"),this.setStatus("disconnected")}}handleMessage(e){switch(e.type){case"welcome":this.sessionId=e.session_id,this.sessionId&&this.storage.set("session_id",this.sessionId),this.onWelcome&&this.onWelcome(e.session_id,e.collection_id,e.message||"Connected");break;case"message_history":if(this.onMessageHistory&&e.messages&&Array.isArray(e.messages)){const t=e.messages.map(e=>({id:e.id||crypto.randomUUID(),role:e.role,content:e.content,timestamp:new Date(e.timestamp),metadata:e.metadata}));this.onMessageHistory(t)}break;case"assistant_message_start":this.onMessageStart&&this.onMessageStart(e.message_id);break;case"assistant_message_chunk":this.onMessageChunk&&this.onMessageChunk(e.message_id,e.content);break;case"assistant_message_end":this.onMessageEnd&&this.onMessageEnd(e.message_id,e.metadata);break;case"assistant_message":if(this.onMessage){const t={id:e.message_id||crypto.randomUUID(),role:"assistant",content:e.content,timestamp:new Date,metadata:e.metadata};this.onMessage(t)}break;case"error":this.onError&&this.onError(e.message||"An error occurred");break;default:console.log("[Wabbit] Unknown message type:",e.type)}}sendMessage(e,t){const n={type:"message",content:e,metadata:t||{}};if(this.ws&&this.ws.readyState===WebSocket.OPEN)this.ws.send(JSON.stringify(n));else{if(this.messageQueue.length>=this.MAX_QUEUE_SIZE)return console.warn("[Wabbit] Message queue full, cannot queue message"),void(this.onQueueOverflow&&this.onQueueOverflow("Message queue full. Please wait for reconnection."));this.messageQueue.push({content:e,metadata:t}),console.log(`[Wabbit] Message queued (${this.messageQueue.length}/${this.MAX_QUEUE_SIZE})`)}}disconnect(){this.ws&&(this.ws.close(),this.ws=null),this.setStatus("disconnected")}attemptReconnect(){if(this.reconnectAttempts>=this.maxReconnectAttempts)return void console.log("[Wabbit] Max reconnect attempts reached");this.reconnectAttempts++,this.setStatus("reconnecting");const e=this.reconnectDelay*Math.pow(2,this.reconnectAttempts-1);console.log(`[Wabbit] Attempting to reconnect in ${e}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`),setTimeout(()=>{this.connect()},e)}flushMessageQueue(){if(0===this.messageQueue.length)return;console.log(`[Wabbit] Flushing ${this.messageQueue.length} queued message(s)`);const e=[...this.messageQueue];this.messageQueue=[],e.forEach(({content:e,metadata:t})=>{if(this.ws&&this.ws.readyState===WebSocket.OPEN){const n={type:"message",content:e,metadata:t||{}};this.ws.send(JSON.stringify(n))}})}clearSession(){this.sessionId=null,this.storage.remove("session_id"),this.messageQueue=[]}clearMessageQueue(){this.messageQueue=[]}getStoredSessionId(){return this.storage.get("session_id")||null}getQueueSize(){return this.messageQueue.length}}function d(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}function h(e){"loading"===document.readyState?document.addEventListener("DOMContentLoaded",e):e()}function b(e,t,n){const i=document.createElement(e);return t&&Object.entries(t).forEach(([e,t])=>{i.setAttribute(e,t)}),n&&n.forEach(e=>{"string"==typeof e?i.appendChild(document.createTextNode(e)):i.appendChild(e)}),i}class m{constructor(e){this.element=null,this.options=e}render(){return this.element||(this.element=b("button",{class:`wabbit-chat-bubble ${this.options.position}`,"aria-label":"Open chat",type:"button"}),this.element.innerHTML='\n <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">\n <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>\n </svg>\n ',this.element.addEventListener("click",this.options.onClick),document.body.appendChild(this.element)),this.element}show(){this.element&&(this.element.style.display="flex")}hide(){this.element&&(this.element.style.display="none")}destroy(){this.element&&(this.element.removeEventListener("click",this.options.onClick),this.element.remove(),this.element=null)}}class u{constructor(e){this.element=null,this.messagesContainer=null,this.inputElement=null,this.sendButton=null,this.messages=[],this.isWaitingForResponse=!1,this.closeButton=null,this.eventCleanup=[],this.streamingMessages=new Map,this.streamingCleanupInterval=null,this.STREAMING_TIMEOUT_MS=3e4,this.options=e}render(){if(this.element)return this.element;const e="inline"===this.options.mode;if(this.element=b("div",{class:e?"wabbit-chat-panel wabbit-chat-panel-inline":`wabbit-chat-panel ${this.options.position}`}),!1!==this.options.showHeader){const t=this.createHeader(e);this.element.appendChild(t)}this.messagesContainer=b("div",{class:"wabbit-chat-messages"});const t=this.createInputArea();return this.element.appendChild(this.messagesContainer),this.element.appendChild(t),e&&this.options.container?(this.options.container.appendChild(this.element),this.element.style.display="flex"):(document.body.appendChild(this.element),this.element.style.display="none"),this.options.welcomeMessage&&this.addSystemMessage(this.options.welcomeMessage),this.scrollToBottom(),this.element}createHeader(e){const t=b("div",{class:"wabbit-chat-panel-header"}),n=b("div",{class:"wabbit-chat-panel-header-title"}),i=b("div",{class:"wabbit-chat-panel-header-icon"});i.textContent="AI";const s=b("div",{class:"wabbit-chat-panel-header-text"}),a=b("h3");a.textContent=this.options.headerTitle||"AI Assistant";const r=b("p");if(r.textContent="Powered by Wabbit",s.appendChild(a),s.appendChild(r),n.appendChild(i),n.appendChild(s),t.appendChild(n),!e){this.closeButton=b("button",{class:"wabbit-chat-panel-close","aria-label":"Close chat",type:"button"}),this.closeButton.innerHTML="×";const e=this.options.onClose;this.closeButton.addEventListener("click",e),this.eventCleanup.push(()=>{this.closeButton?.removeEventListener("click",e)}),t.appendChild(this.closeButton)}return t}createInputArea(){const e=b("div",{class:"wabbit-chat-input-area"}),t=b("div",{class:"wabbit-chat-input-wrapper"});this.inputElement=document.createElement("textarea"),this.inputElement.className="wabbit-chat-input",this.inputElement.placeholder=this.options.placeholder||"Type your message...",this.inputElement.rows=1,this.inputElement.disabled=this.options.disabled||!1;const n=()=>{this.inputElement.style.height="auto",this.inputElement.style.height=`${Math.min(this.inputElement.scrollHeight,120)}px`,this.updateSendButtonState()};this.inputElement.addEventListener("input",n),this.eventCleanup.push(()=>{this.inputElement?.removeEventListener("input",n)});const i=e=>{"Enter"!==e.key||e.shiftKey||(e.preventDefault(),this.handleSend())};this.inputElement.addEventListener("keydown",i),this.eventCleanup.push(()=>{this.inputElement?.removeEventListener("keydown",i)}),this.sendButton=b("button",{class:"wabbit-chat-send-button",type:"button","aria-label":"Send message"}),this.sendButton.innerHTML='\n <svg class="wabbit-chat-send-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">\n <line x1="22" y1="2" x2="11" y2="13"></line>\n <polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>\n </svg>\n ';const s=()=>this.handleSend();return this.sendButton.addEventListener("click",s),this.eventCleanup.push(()=>{this.sendButton?.removeEventListener("click",s)}),this.updateSendButtonState(),t.appendChild(this.inputElement),t.appendChild(this.sendButton),e.appendChild(t),e}addMessage(e){this.messages.push(e),this.renderMessage(e),this.scrollToBottom()}setMessages(e){this.messages=e,this.messagesContainer&&(this.messagesContainer.innerHTML="",e.forEach(e=>this.renderMessage(e)),this.scrollToBottom())}addSystemMessage(e){const t={id:`system-${Date.now()}`,role:"system",content:e,timestamp:new Date};this.addMessage(t)}setWaitingForResponse(e){if(this.isWaitingForResponse=e,this.messagesContainer){const t=this.messagesContainer.querySelector(".wabbit-chat-typing");if(t&&t.remove(),e){const e=b("div",{class:"wabbit-chat-typing"});e.innerHTML='\n <div class="wabbit-chat-typing-dot"></div>\n <div class="wabbit-chat-typing-dot"></div>\n <div class="wabbit-chat-typing-dot"></div>\n ',this.messagesContainer.appendChild(e)}this.scrollToBottom()}this.updateSendButtonState()}setDisabled(e){this.options.disabled=e,this.inputElement&&(this.inputElement.disabled=e,this.updateSendButtonState())}renderMessage(e){if(!this.messagesContainer)return;const t=b("div",{class:`wabbit-chat-message wabbit-chat-message-${e.role}`}),n=b("div",{class:"wabbit-chat-message-bubble"}),i=b("div",{class:"wabbit-chat-message-content"});"assistant"===e.role?i.innerHTML=this.formatMessage(e.content):i.textContent=e.content,n.appendChild(i),t.appendChild(n),this.messagesContainer.appendChild(t)}formatMessage(e){let t=d(e);return t=t.replace(/\[([^\]]+)\]\(([^)]+)\)/g,'<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>'),t=t.replace(/\*\*(.+?)\*\*/g,"<strong>$1</strong>"),t=t.replace(/\*(.+?)\*/g,"<em>$1</em>"),t=t.replace(/`(.+?)`/g,'<code style="background: rgba(0,0,0,0.1); padding: 2px 4px; border-radius: 3px;">$1</code>'),t=t.replace(/(?<!href=")(https?:\/\/[^\s<]+)/g,'<a href="$1" target="_blank" rel="noopener noreferrer">$1</a>'),t=t.replace(/(?<!["\/])([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/g,'<a href="mailto:$1">$1</a>'),t=t.replace(/(?<!["\/])(\+\d[\d\s-]{7,})/g,(e,t)=>`<a href="tel:${t.replace(/[\s-]/g,"")}">${t}</a>`),t=t.replace(/\n/g,"<br>"),t}startStreamingMessage(e){if(!this.messagesContainer)return;const t=b("div",{class:"wabbit-chat-message wabbit-chat-message-assistant wabbit-chat-message-streaming","data-message-id":e}),n=b("div",{class:"wabbit-chat-message-bubble"}),i=b("div",{class:"wabbit-chat-message-content"});i.innerHTML='<span class="wabbit-chat-cursor">▋</span>',n.appendChild(i),t.appendChild(n),this.messagesContainer.appendChild(t),this.streamingMessages.set(e,{element:t,content:"",startTime:Date.now()}),this.streamingCleanupInterval||(this.streamingCleanupInterval=window.setInterval(()=>{this.cleanupStaleStreamingMessages()},5e3)),this.scrollToBottom()}appendToStreamingMessage(e,t){const n=this.streamingMessages.get(e);if(!n)return void console.warn("[ChatPanel] No streaming message found for ID:",e);n.content+=t;const i=n.element.querySelector(".wabbit-chat-message-content");i&&(i.innerHTML=this.formatMessage(n.content)+'<span class="wabbit-chat-cursor">▋</span>'),this.scrollToBottom()}finishStreamingMessage(e,t){const n=this.streamingMessages.get(e);if(!n)return;n.element.classList.remove("wabbit-chat-message-streaming");const i=n.element.querySelector(".wabbit-chat-message-content");i&&(i.innerHTML=this.formatMessage(n.content));const s={id:e,role:"assistant",content:n.content,timestamp:new Date,metadata:t};this.messages.push(s),this.streamingMessages.delete(e),this.scrollToBottom()}cancelStreamingMessage(e){const t=this.streamingMessages.get(e);t&&(t.element.remove(),this.streamingMessages.delete(e))}cancelAllStreamingMessages(){this.streamingMessages.forEach(e=>{e.element.remove()}),this.streamingMessages.clear()}cleanupStaleStreamingMessages(){const e=Date.now(),t=[];this.streamingMessages.forEach((n,i)=>{e-n.startTime>this.STREAMING_TIMEOUT_MS&&(console.warn("[ChatPanel] Cleaning up stale streaming message:",i),n.element.remove(),t.push(i))}),t.forEach(e=>this.streamingMessages.delete(e)),0===this.streamingMessages.size&&this.streamingCleanupInterval&&(clearInterval(this.streamingCleanupInterval),this.streamingCleanupInterval=null)}handleSend(){if(!this.inputElement||this.isWaitingForResponse)return;const e=this.inputElement.value.trim();e&&(this.inputElement.value="",this.inputElement.style.height="auto",this.options.onSendMessage(e))}updateSendButtonState(){if(!this.sendButton||!this.inputElement)return;const e=this.inputElement.value.trim().length>0,t=this.options.disabled||this.isWaitingForResponse||!e;t?this.sendButton.setAttribute("disabled","true"):this.sendButton.removeAttribute("disabled"),this.sendButton instanceof HTMLButtonElement&&(this.sendButton.disabled=t)}scrollToBottom(){if(!this.messagesContainer)return;const e=this.messagesContainer,t=()=>{const t=e.scrollHeight,n=e.clientHeight;e.scrollTop=t>n?t:999999};t(),requestAnimationFrame(()=>{t(),requestAnimationFrame(t)}),setTimeout(t,10),setTimeout(t,50),setTimeout(t,100),setTimeout(t,200),setTimeout(t,500)}show(){this.element&&(this.element.style.display="flex",this.scrollToBottom(),this.inputElement&&this.inputElement.focus())}hide(){this.element&&(this.element.style.display="none"),this.streamingCleanupInterval&&(clearInterval(this.streamingCleanupInterval),this.streamingCleanupInterval=null)}destroy(){this.eventCleanup.forEach(e=>e()),this.eventCleanup=[],this.streamingCleanupInterval&&(clearInterval(this.streamingCleanupInterval),this.streamingCleanupInterval=null),this.element&&(this.element.remove(),this.element=null,this.messagesContainer=null,this.inputElement=null,this.sendButton=null,this.closeButton=null)}}function p(){const e=document.documentElement,t=e.getAttribute("data-theme");return"light"===t||"dark"===t?t:e.classList.contains("dark")?"dark":"light"}function g(e){const t=new MutationObserver(()=>{e(p())});t.observe(document.documentElement,{attributes:!0,attributeFilter:["data-theme","class"]});const n=window.matchMedia("(prefers-color-scheme: dark)"),i=()=>{e(p())};return n.addEventListener("change",i),e(p()),()=>{t.disconnect(),n.removeEventListener("change",i)}}function w(e="#6366f1",t){const n="wabbit-chat-styles",i=document.getElementById(n);i&&i.remove();const s="dark"===("auto"!==t&&t?t:p())?{"--wabbit-bg-base":"#1A1816","--wabbit-bg-elevated":"#2D2826","--wabbit-bg-hover":"#3D3935","--wabbit-text-primary":"#F5F1ED","--wabbit-text-secondary":"#A39C94","--wabbit-text-muted":"#6B6560","--wabbit-border-subtle":"#3D3935","--wabbit-primary":e}:{"--wabbit-bg-base":"#FAF8F6","--wabbit-bg-elevated":"#FFFFFF","--wabbit-bg-hover":"#F2F0EE","--wabbit-text-primary":"#2D2826","--wabbit-text-secondary":"#5A5450","--wabbit-text-muted":"#8A847E","--wabbit-border-subtle":"#E6E3E0","--wabbit-primary":e},a=`\n :root {\n ${Object.entries(s).map(([e,t])=>`${e}: ${t};`).join("\n ")}\n }\n\n /* Chat Bubble */\n .wabbit-chat-bubble {\n position: fixed;\n z-index: 9999;\n width: 60px;\n height: 60px;\n border-radius: 50%;\n background: linear-gradient(135deg, var(--wabbit-primary), ${f(e,.2)});\n border: none;\n cursor: pointer;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15), 0 2px 4px rgba(0, 0, 0, 0.1);\n display: flex;\n align-items: center;\n justify-content: center;\n transition: transform 0.2s ease, box-shadow 0.2s ease;\n color: white;\n font-size: 24px;\n }\n\n .wabbit-chat-bubble:hover {\n transform: scale(1.1);\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2), 0 4px 8px rgba(0, 0, 0, 0.15);\n }\n\n .wabbit-chat-bubble:active {\n transform: scale(0.95);\n }\n\n .wabbit-chat-bubble.bottom-right {\n bottom: 20px;\n right: 20px;\n }\n\n .wabbit-chat-bubble.bottom-left {\n bottom: 20px;\n left: 20px;\n }\n\n /* Chat Panel */\n .wabbit-chat-panel {\n position: fixed;\n z-index: 9998;\n width: 380px;\n max-width: calc(100vw - 40px);\n height: 600px;\n max-height: calc(100vh - 100px);\n background: var(--wabbit-bg-elevated);\n border: 1px solid var(--wabbit-border-subtle);\n border-radius: 16px;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif;\n font-size: 14px;\n line-height: 1.5;\n color: var(--wabbit-text-primary);\n }\n\n .wabbit-chat-panel.bottom-right {\n bottom: 90px;\n right: 20px;\n }\n\n .wabbit-chat-panel.bottom-left {\n bottom: 90px;\n left: 20px;\n }\n\n @media (max-width: 480px) {\n .wabbit-chat-panel {\n width: calc(100vw - 20px);\n height: calc(100vh - 20px);\n max-height: calc(100vh - 20px);\n bottom: 10px !important;\n left: 10px !important;\n right: 10px !important;\n border-radius: 12px;\n }\n }\n\n /* Panel Header */\n .wabbit-chat-panel-header {\n background: rgba(var(--wabbit-primary-rgb, 99, 102, 241), 0.1);\n border-bottom: 1px solid var(--wabbit-border-subtle);\n padding: 16px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n\n .wabbit-chat-panel-header-title {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .wabbit-chat-panel-header-icon {\n width: 32px;\n height: 32px;\n border-radius: 50%;\n background: linear-gradient(135deg, var(--wabbit-primary), ${f(e,.2)});\n display: flex;\n align-items: center;\n justify-content: center;\n color: white;\n font-size: 16px;\n font-weight: 600;\n }\n\n .wabbit-chat-panel-header-text h3 {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--wabbit-text-primary);\n }\n\n .wabbit-chat-panel-header-text p {\n margin: 0;\n font-size: 12px;\n color: var(--wabbit-text-muted);\n }\n\n .wabbit-chat-panel-close {\n background: none;\n border: none;\n cursor: pointer;\n color: var(--wabbit-text-secondary);\n font-size: 24px;\n line-height: 1;\n padding: 4px;\n transition: color 0.2s;\n }\n\n .wabbit-chat-panel-close:hover {\n color: var(--wabbit-text-primary);\n }\n\n /* Messages Area */\n .wabbit-chat-messages {\n flex: 1;\n overflow-y: auto;\n padding: 16px;\n display: flex;\n flex-direction: column;\n gap: 16px;\n }\n\n .wabbit-chat-message {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n .wabbit-chat-message-user {\n align-items: flex-end;\n }\n\n .wabbit-chat-message-assistant {\n align-items: flex-start;\n }\n\n .wabbit-chat-message-system {\n align-items: center;\n }\n\n .wabbit-chat-message-bubble {\n max-width: 85%;\n padding: 12px 16px;\n border-radius: 16px;\n word-wrap: break-word;\n white-space: pre-wrap;\n }\n\n .wabbit-chat-message-user .wabbit-chat-message-bubble {\n background: linear-gradient(135deg, var(--wabbit-primary), ${f(e,.2)});\n color: white;\n border-bottom-right-radius: 4px;\n }\n\n .wabbit-chat-message-assistant .wabbit-chat-message-bubble {\n background: var(--wabbit-bg-hover);\n color: var(--wabbit-text-primary);\n border: 1px solid var(--wabbit-border-subtle);\n border-bottom-left-radius: 4px;\n }\n\n .wabbit-chat-message-system .wabbit-chat-message-bubble {\n background: rgba(var(--wabbit-primary-rgb, 99, 102, 241), 0.1);\n border: 1px solid rgba(var(--wabbit-primary-rgb, 99, 102, 241), 0.3);\n color: var(--wabbit-primary);\n text-align: center;\n font-size: 12px;\n padding: 8px 12px;\n }\n\n .wabbit-chat-message-content {\n font-size: 14px;\n line-height: 1.5;\n }\n\n .wabbit-chat-message-content a {\n color: var(--wabbit-primary);\n text-decoration: underline;\n text-underline-offset: 2px;\n }\n\n .wabbit-chat-message-content a:hover {\n opacity: 0.8;\n }\n\n .wabbit-chat-message-user .wabbit-chat-message-content a {\n color: inherit;\n }\n\n /* Typing Indicator */\n .wabbit-chat-typing {\n display: flex;\n gap: 4px;\n padding: 12px 16px;\n }\n\n .wabbit-chat-typing-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--wabbit-text-muted);\n animation: wabbit-typing 1.4s infinite;\n }\n\n .wabbit-chat-typing-dot:nth-child(2) {\n animation-delay: 0.2s;\n }\n\n .wabbit-chat-typing-dot:nth-child(3) {\n animation-delay: 0.4s;\n }\n\n @keyframes wabbit-typing {\n 0%, 60%, 100% {\n transform: translateY(0);\n opacity: 0.7;\n }\n 30% {\n transform: translateY(-10px);\n opacity: 1;\n }\n }\n\n /* Input Area */\n .wabbit-chat-input-area {\n border-top: 1px solid var(--wabbit-border-subtle);\n padding: 12px;\n background: var(--wabbit-bg-elevated);\n }\n\n .wabbit-chat-input-wrapper {\n display: flex;\n gap: 8px;\n align-items: flex-end;\n }\n\n .wabbit-chat-input {\n flex: 1;\n resize: none;\n background: var(--wabbit-bg-hover);\n border: 1px solid var(--wabbit-border-subtle);\n border-radius: 12px;\n padding: 10px 12px;\n color: var(--wabbit-text-primary);\n font-size: 14px;\n font-family: inherit;\n line-height: 1.5;\n min-height: 44px;\n max-height: 120px;\n transition: border-color 0.2s, box-shadow 0.2s;\n }\n\n .wabbit-chat-input:focus {\n outline: none;\n border-color: var(--wabbit-primary);\n box-shadow: 0 0 0 3px rgba(var(--wabbit-primary-rgb, 99, 102, 241), 0.1);\n }\n\n .wabbit-chat-input::placeholder {\n color: var(--wabbit-text-muted);\n }\n\n .wabbit-chat-input:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .wabbit-chat-send-button {\n width: 44px;\n height: 44px;\n border-radius: 12px;\n background: linear-gradient(135deg, var(--wabbit-primary), ${f(e,.2)});\n border: none;\n color: white;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: opacity 0.2s, transform 0.2s;\n flex-shrink: 0;\n }\n\n .wabbit-chat-send-button:hover:not(:disabled) {\n opacity: 0.9;\n transform: scale(1.05);\n }\n\n .wabbit-chat-send-button:active:not(:disabled) {\n transform: scale(0.95);\n }\n\n .wabbit-chat-send-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .wabbit-chat-send-icon {\n width: 20px;\n height: 20px;\n }\n\n /* Scrollbar */\n .wabbit-chat-messages::-webkit-scrollbar {\n width: 6px;\n }\n\n .wabbit-chat-messages::-webkit-scrollbar-track {\n background: transparent;\n }\n\n .wabbit-chat-messages::-webkit-scrollbar-thumb {\n background: var(--wabbit-border-subtle);\n border-radius: 3px;\n }\n\n .wabbit-chat-messages::-webkit-scrollbar-thumb:hover {\n background: var(--wabbit-text-muted);\n }\n\n /* Animations */\n @keyframes wabbit-fade-in {\n from {\n opacity: 0;\n transform: translateY(10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n /* Streaming Cursor */\n .wabbit-chat-cursor {\n display: inline-block;\n color: var(--wabbit-primary);\n font-weight: bold;\n animation: wabbit-cursor-blink 1s infinite;\n margin-left: 2px;\n }\n\n @keyframes wabbit-cursor-blink {\n 0%, 50% {\n opacity: 1;\n }\n 51%, 100% {\n opacity: 0;\n }\n }\n\n .wabbit-chat-panel {\n animation: wabbit-fade-in 0.3s ease-out;\n }\n\n .wabbit-chat-message {\n animation: wabbit-fade-in 0.2s ease-out;\n }\n\n /* ========================================\n * INLINE MODE STYLES\n * ======================================== */\n\n /* Inline Chat Panel - renders inside container instead of fixed position */\n .wabbit-chat-panel.wabbit-chat-panel-inline {\n position: absolute !important;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n width: 100%;\n height: 100%;\n max-height: none;\n max-width: none;\n border-radius: 0;\n box-shadow: none;\n animation: none; /* Disable slide-in animation for inline */\n }\n\n /* Inline mode messages area fills available space */\n .wabbit-chat-panel-inline .wabbit-chat-messages {\n flex: 1 1 0; /* flex-grow, flex-shrink, flex-basis: 0 for proper sizing */\n min-height: 0; /* Critical for nested flex scroll to work */\n overflow-y: auto !important;\n }\n\n /* Full-height variant for inline mode (used in standalone chat pages) */\n .wabbit-chat-panel-inline.wabbit-chat-panel-fullheight {\n height: 100vh;\n max-height: 100vh;\n border-radius: 0;\n }\n\n /* Mobile responsive for inline mode */\n @media (max-width: 480px) {\n .wabbit-chat-panel.wabbit-chat-panel-inline {\n border-radius: 0;\n min-height: 300px;\n }\n }\n `,r=document.createElement("style");r.id=n,r.setAttribute("data-wabbit","true"),r.textContent=a,document.head.appendChild(r)}function f(e,t){const n=e.replace("#",""),i=parseInt(n.substr(0,2),16),s=parseInt(n.substr(2,2),16),a=parseInt(n.substr(4,2),16),r=Math.max(0,Math.min(255,Math.round(i+(255-i)*t))),o=Math.max(0,Math.min(255,Math.round(s+(255-s)*t))),l=Math.max(0,Math.min(255,Math.round(a+(255-a)*t)));return`#${r.toString(16).padStart(2,"0")}${o.toString(16).padStart(2,"0")}${l.toString(16).padStart(2,"0")}`}class y{constructor(e,t){this.wsClient=null,this.bubble=null,this.panel=null,this.isOpen=!1,this.cleanup=[],this.themeCleanup=null,this.onChatReadyCallback=null,this.chatReadyFired=!1,this.config=e,this.storage=t,this.onChatReadyCallback=e.onChatReady||null}async init(){await new Promise(e=>{h(()=>{e()})});const e="inline"===this.config.mode;let t=null;if(e&&(t=this.resolveContainer(),!t))return void console.error(`[Wabbit] Container not found: ${this.config.container}`);w(this.config.primaryColor||"#6366f1",this.config.theme),this.setupThemeWatcher();const n=this.getWebSocketUrl();this.wsClient=new c(this.config.apiKey||"",n,null,this.storage),this.setupWebSocketHandlers(),e||(this.bubble=new m({position:this.config.position||"bottom-right",onClick:()=>this.toggle()})),this.panel=new u({position:this.config.position||"bottom-right",welcomeMessage:this.config.welcomeMessage,placeholder:this.config.placeholder,onSendMessage:e=>this.handleSendMessage(e),onClose:()=>this.close(),disabled:!0,mode:this.config.mode||"widget",container:t||void 0,showHeader:this.config.showHeader,headerTitle:this.config.headerTitle}),this.bubble&&this.bubble.render(),this.panel.render(),e?this.isOpen=!0:(this.panel&&this.panel.hide(),this.bubble&&this.bubble.show(),this.isOpen=!1,this.handleTriggerType()),await this.wsClient.connect()}resolveContainer(){if(!this.config.container)return null;const e=this.config.container,t=e.startsWith("#")?e.slice(1):e;return document.getElementById(t)||document.querySelector(e)}setupWebSocketHandlers(){this.wsClient&&(this.wsClient.onWelcome=(e,t,n)=>{console.log("[Wabbit] Chat connected:",e),this.panel&&(this.panel.setDisabled(!1),n&&this.panel.addSystemMessage(n)),!this.chatReadyFired&&this.onChatReadyCallback&&(this.chatReadyFired=!0,requestAnimationFrame(()=>{console.log("[Wabbit] Chat ready, firing onChatReady callback"),this.onChatReadyCallback&&this.onChatReadyCallback()}))},this.wsClient.onMessageHistory=e=>{console.log("[Wabbit] Loaded message history:",e.length),this.panel&&this.panel.setMessages(e)},this.wsClient.onMessageStart=e=>{console.log("[Wabbit] Streaming message started:",e),this.panel&&this.panel.startStreamingMessage(e)},this.wsClient.onMessageChunk=(e,t)=>{this.panel&&this.panel.appendToStreamingMessage(e,t)},this.wsClient.onMessageEnd=(e,t)=>{console.log("[Wabbit] Streaming message ended:",e),this.panel&&(this.panel.finishStreamingMessage(e,t),this.panel.setWaitingForResponse(!1))},this.wsClient.onMessage=e=>{this.panel&&(this.panel.addMessage(e),this.panel.setWaitingForResponse(!1))},this.wsClient.onError=e=>{console.error("[Wabbit] Chat error:",e),this.panel&&(this.panel.cancelAllStreamingMessages(),this.panel.addSystemMessage(`Error: ${e}`),this.panel.setWaitingForResponse(!1))},this.wsClient.onStatusChange=e=>{this.panel&&this.panel.setDisabled("connected"!==e)},this.wsClient.onDisconnect=()=>{if(this.panel){this.panel.cancelAllStreamingMessages();const e=this.wsClient.getQueueSize();e>0?this.panel.addSystemMessage(`Disconnected from chat service. ${e} message(s) will be sent when reconnected.`):this.panel.addSystemMessage("Disconnected from chat service"),this.panel.setDisabled(!0)}},this.wsClient.onQueueOverflow=e=>{this.panel&&this.panel.addSystemMessage(e)})}handleTriggerType(){switch(this.config.triggerType||"button"){case"auto":const e=this.config.triggerDelay||5e3;setTimeout(()=>{this.open()},e);break;case"scroll":let t=!1;const n=()=>{t||(t=!0,this.open(),window.removeEventListener("scroll",n))};window.addEventListener("scroll",n),this.cleanup.push(()=>{window.removeEventListener("scroll",n)})}}getWebSocketUrl(){if(this.config.wsUrl)return this.config.wsUrl;const e=this.config.apiUrl||"https://platform.insourcedata.ai";return`${e.startsWith("https")?"wss":"ws"}://${e.replace(/^https?:\/\//,"").replace(/\/$/,"")}/ws/chat`}handleSendMessage(e){if(!this.wsClient)return;const t={id:`user-${Date.now()}`,role:"user",content:e,timestamp:new Date};this.panel&&(this.panel.addMessage(t),this.panel.setWaitingForResponse(!0)),this.wsClient.sendMessage(e),this.config.onUserMessage&&this.config.onUserMessage(e)}open(){"inline"!==this.config.mode&&(this.isOpen||(this.isOpen=!0,this.panel&&this.panel.show(),this.bubble&&this.bubble.hide()))}close(){"inline"!==this.config.mode&&this.isOpen&&(this.isOpen=!1,this.panel&&this.panel.hide(),this.bubble&&this.bubble.show())}toggle(){"inline"!==this.config.mode&&(this.isOpen?this.close():this.open())}sendMessage(e){this.handleSendMessage(e)}setupThemeWatcher(){"auto"===(this.config.theme||"auto")&&(this.themeCleanup=g(()=>{w(this.config.primaryColor||"#6366f1","auto")}))}destroy(){this.cleanup.forEach(e=>e()),this.cleanup=[],this.themeCleanup&&(this.themeCleanup(),this.themeCleanup=null),this.wsClient&&(this.wsClient.disconnect(),this.wsClient=null),this.bubble&&(this.bubble.destroy(),this.bubble=null),this.panel&&(this.panel.destroy(),this.panel=null)}}var v=Object.freeze({__proto__:null,ChatWidget:y});class C{constructor(e){this.styles=e}render(e,t){const n=this.styles.getColors(t),i=e.fields;let s='<form class="wabbit-form" data-wabbit-form-instance="true">';return i.forEach(e=>{s+=this.renderField(e,n)}),s+='<button\n type="submit"\n class="wabbit-submit"\n >Submit</button>',s+='<div class="wabbit-message" style="display: none;"></div>',s+="</form>",s}renderField(e,t){let n='<div class="wabbit-field">';const i=["text","email","number","textarea","select"].includes(e.type)?`wabbit-input-${e.id}`:void 0;switch(n+=`<label${i?` for="${d(i)}"`:""} style="display: block; font-weight: 600; margin-bottom: 0.5rem; color: ${t.labelColor} !important; font-size: 0.875rem;">${d(e.label)}${e.required?' <span style="color: #E07A5F;">*</span>':""}</label>`,e.type){case"text":case"email":case"number":i&&(n+=this.renderTextInput(e,t,i));break;case"textarea":i&&(n+=this.renderTextarea(e,t,i));break;case"select":i&&(n+=this.renderSelect(e,t,i));break;case"radio":n+=this.renderRadio(e,t);break;case"checkbox":n+=this.renderCheckbox(e,t);break;case"rating":n+=this.renderRating(e,t)}return n+="</div>",n}renderTextInput(e,t,n){return`<input\n type="${e.type}"\n id="${d(n)}"\n name="${d(e.id)}"\n placeholder="${e.placeholder?d(e.placeholder):""}"\n ${e.required?"required":""}\n />`}renderTextarea(e,t,n){return`<textarea\n id="${d(n)}"\n name="${d(e.id)}"\n placeholder="${e.placeholder?d(e.placeholder):""}"\n ${e.required?"required":""}\n rows="4"\n ></textarea>`}renderSelect(e,t,n){if(!e.options||0===e.options.length)return"";let i=`<select id="${d(n)}" name="${d(e.id)}" ${e.required?"required":""}>`;return i+='<option value="">Select...</option>',e.options.forEach(e=>{i+=`<option value="${d(e)}">${d(e)}</option>`}),i+="</select>",i}renderRadio(e,t){if(!e.options||0===e.options.length)return"";let n='<div class="wabbit-radio-group">';return e.options.forEach((t,i)=>{const s=`${e.id}_${i}`;n+=`<div>\n <label>\n <input\n type="radio"\n name="${d(e.id)}"\n value="${d(t)}"\n id="${d(s)}"\n ${e.required&&0===i?"required":""}\n />\n <span>${d(t)}</span>\n </label>\n </div>`}),n+="</div>",n}renderCheckbox(e,t){if(!e.options||0===e.options.length)return"";let n='<div class="wabbit-checkbox-group">';return e.options.forEach((t,i)=>{const s=`${e.id}_${i}`;n+=`<div>\n <label>\n <input\n type="checkbox"\n name="${d(e.id)}[]"\n value="${d(t)}"\n id="${d(s)}"\n />\n <span>${d(t)}</span>\n </label>\n </div>`}),n+="</div>",n}renderRating(e,t){const n=e.max||5;let i='<div class="wabbit-rating">';for(let t=1;t<=n;t++){const n=`${e.id}_${t}`;i+=`<input\n type="radio"\n name="${d(e.id)}"\n value="${t}"\n id="${d(n)}"\n ${e.required&&1===t?"required":""}\n />`,i+=`<label for="${d(n)}">★</label>`}return i+="</div>",i}}class x{constructor(e,t){if(this.styleElement=null,this.currentTheme="light",this._primaryColor="#C15F3C",this.themes={dark:{formBg:"#1A1816",labelColor:"#F5F1ED",inputBg:"#2D2826",inputText:"#F5F1ED",inputBorder:"#3D3935",inputBorderFocus:"#C15F3C",placeholderColor:"#6B6560",radioColor:"#A39C94",ratingInactive:"#3D3935",errorBg:"rgba(224, 122, 95, 0.1)",errorColor:"#E07A5F",errorBorder:"#E07A5F",successBg:"rgba(132, 169, 140, 0.1)",successColor:"#84A98C",successBorder:"#84A98C",buttonBg:"#C15F3C",buttonBgHover:"#AB4E2F"},light:{formBg:"#FAF8F6",labelColor:"#2D2826",inputBg:"#FFFFFF",inputText:"#2D2826",inputBorder:"#D4CFCA",inputBorderFocus:"#C15F3C",placeholderColor:"#8A847E",radioColor:"#5A5450",ratingInactive:"#D4CFCA",errorBg:"rgba(193, 84, 56, 0.1)",errorColor:"#C15438",errorBorder:"#C15438",successBg:"rgba(92, 122, 99, 0.1)",successColor:"#5C7A63",successBorder:"#5C7A63",buttonBg:"#C15F3C",buttonBgHover:"#AB4E2F"}},this.styleId=t?`wabbit-form-styles-${t.replace(/[^a-zA-Z0-9]/g,"-")}`:`wabbit-form-styles-${Math.random().toString(36).substr(2,9)}`,e){this._primaryColor=e,this.themes.dark.inputBorderFocus=e,this.themes.light.inputBorderFocus=e,this.themes.dark.buttonBg=e,this.themes.light.buttonBg=e;const t=this.darkenColor(e,10);this.themes.dark.buttonBgHover=t,this.themes.light.buttonBgHover=t}this.injectStyles()}getColors(e){const t=e||this.currentTheme;return this.themes[t]}updateTheme(e){this.currentTheme=e,this.injectStyles()}updatePrimaryColor(e){this._primaryColor=e,this.themes.dark.inputBorderFocus=e,this.themes.light.inputBorderFocus=e,this.themes.dark.buttonBg=e,this.themes.light.buttonBg=e;const t=this.darkenColor(e,10);this.themes.dark.buttonBgHover=t,this.themes.light.buttonBgHover=t,this.injectStyles()}getPrimaryColor(){return this._primaryColor}darkenColor(e,t){if(e.startsWith("#")){const n=parseInt(e.slice(1),16);return`#${(Math.max(0,Math.floor((n>>16)*(1-t/100)))<<16|Math.max(0,Math.floor((n>>8&255)*(1-t/100)))<<8|Math.max(0,Math.floor((255&n)*(1-t/100)))).toString(16).padStart(6,"0")}`}return e}injectStyles(){this.styleElement=document.getElementById(this.styleId),this.styleElement||(this.styleElement=document.createElement("style"),this.styleElement.id=this.styleId,this.styleElement.setAttribute("data-wabbit","form-styles"),document.head.appendChild(this.styleElement));const e=this.getColors(),t=this.generateCSS(e);this.styleElement.textContent=t}generateCSS(e){return`\n /* Wabbit Form Styles */\n .wabbit-form {\n max-width: 600px;\n margin: 0 auto;\n font-family: Inter, system-ui, sans-serif;\n background-color: ${e.formBg};\n padding: 1.5rem;\n border-radius: 0.5rem;\n }\n\n .wabbit-field {\n margin-bottom: 1.5rem;\n }\n\n /* Label color is set via inline style in FormRenderer for theme-aware rendering */\n /* This ensures the correct theme color is applied at render time */\n\n .wabbit-field input[type="text"],\n .wabbit-field input[type="email"],\n .wabbit-field input[type="number"],\n .wabbit-field textarea,\n .wabbit-field select {\n width: 100%;\n padding: 0.75rem 1rem;\n border: 1px solid ${e.inputBorder};\n border-radius: 0.5rem;\n font-size: 1rem;\n background-color: ${e.inputBg};\n color: ${e.inputText};\n transition: border-color 0.2s, box-shadow 0.2s;\n box-sizing: border-box;\n }\n\n .wabbit-field input[type="text"]:focus,\n .wabbit-field input[type="email"]:focus,\n .wabbit-field input[type="number"]:focus,\n .wabbit-field textarea:focus,\n .wabbit-field select:focus {\n outline: none;\n border-color: var(--wabbit-primary-color);\n box-shadow: 0 0 0 3px rgba(193, 95, 60, 0.1);\n }\n\n .wabbit-field input::placeholder,\n .wabbit-field textarea::placeholder {\n color: ${e.placeholderColor};\n opacity: 1;\n }\n\n .wabbit-field textarea {\n resize: vertical;\n }\n\n .wabbit-field .wabbit-radio-group,\n .wabbit-field .wabbit-checkbox-group {\n margin-bottom: 0.5rem;\n }\n\n .wabbit-field .wabbit-radio-group label,\n .wabbit-field .wabbit-checkbox-group label {\n display: flex;\n align-items: center;\n cursor: pointer;\n color: ${e.radioColor};\n margin-bottom: 0.5rem;\n }\n\n .wabbit-field input[type="radio"],\n .wabbit-field input[type="checkbox"] {\n margin-right: 0.5rem;\n accent-color: var(--wabbit-primary-color);\n }\n\n .wabbit-rating {\n display: flex;\n gap: 0.25rem;\n }\n\n .wabbit-rating input[type="radio"] {\n display: none;\n }\n\n .wabbit-rating label {\n cursor: pointer;\n font-size: 1.5rem;\n color: ${e.ratingInactive};\n transition: color 0.2s;\n }\n\n .wabbit-rating label:hover {\n color: var(--wabbit-primary-color);\n }\n\n .wabbit-rating input[type="radio"]:checked ~ label,\n .wabbit-rating label:has(+ input[type="radio"]:checked) {\n color: var(--wabbit-primary-color);\n }\n\n .wabbit-submit {\n width: 100%;\n padding: 0.75rem 1.5rem;\n background-color: var(--wabbit-primary-color);\n color: white;\n border: none;\n border-radius: 0.5rem;\n font-size: 1rem;\n font-weight: 600;\n cursor: pointer;\n transition: background-color 0.2s, transform 0.15s;\n }\n\n .wabbit-submit:hover:not(:disabled) {\n background-color: var(--wabbit-primary-color-hover);\n transform: translateY(-1px);\n }\n\n .wabbit-submit:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .wabbit-message {\n display: none;\n margin-top: 1rem;\n padding: 0.75rem;\n border-radius: 0.5rem;\n }\n\n .wabbit-message.wabbit-message-success {\n background-color: ${e.successBg};\n color: ${e.successColor};\n border: 1px solid ${e.successBorder};\n }\n\n .wabbit-message.wabbit-message-error {\n background-color: ${e.errorBg};\n color: ${e.errorColor};\n border: 1px solid ${e.errorBorder};\n }\n `}destroy(){this.styleElement&&(this.styleElement.remove(),this.styleElement=null)}}class E{constructor(e){this.baseUrl=e.baseUrl.replace(/\/$/,""),this.apiKey=e.apiKey||"",this.timeout=e.timeout||3e4}async get(e,t){return this.request("GET",e,void 0,t)}async post(e,t,n){return this.request("POST",e,t,n)}async request(e,t,n,i){const s=`${this.baseUrl}${t}`,a={"Content-Type":"application/json"};!1!==i?.requireAuth&&this.apiKey&&(a["X-API-Key"]=this.apiKey);const r=new AbortController,o=setTimeout(()=>r.abort(),this.timeout),l={method:e,headers:a,mode:"cors",signal:r.signal};n&&"GET"!==e&&(l.body=JSON.stringify(n));try{const e=await fetch(s,l);if(clearTimeout(o),!e.ok){let t;try{const n=await e.json();t=n.detail?n.detail:n.error?n.error:n.message?n.message:JSON.stringify(n)}catch{const n=await e.text();t=n||(e.statusText||"An error occurred")}return{success:!1,error:t}}return{success:!0,data:await e.json()}}catch(e){return clearTimeout(o),e instanceof Error&&"AbortError"===e.name?{success:!1,error:`Request timeout after ${this.timeout}ms`}:e instanceof TypeError&&e.message.includes("CORS")?{success:!1,error:"CORS error: Please check server configuration"}:{success:!1,error:e instanceof Error?e.message:"Unknown error"}}}}class S{constructor(e){this.container=null,this.formElement=null,this.currentTheme="light",this.themeCleanup=null,this.formObserver=null,this.config=e;const t=e.apiUrl||this.getApiUrlFromScript();this.apiClient=new E({baseUrl:t,apiKey:e.apiKey}),this.styles=new x(e.primaryColor,e.containerId),this.renderer=new C(this.styles)}init(){h(()=>{const e=this.config.theme||"auto";this.currentTheme="auto"===e?p():e,this.styles.updateTheme(this.currentTheme),this.setupThemeWatcher(),this.loadForm()})}async loadForm(){try{if(this.container=this.findContainer(),!this.container)throw new Error(`Container not found: ${this.config.containerId||"data-wabbit-form-id"}`);const e=await this.apiClient.get(`/api/forms/${this.config.formId}/definition`,{requireAuth:!1});if(!e.success||!e.data)throw new Error(e.error||"Failed to load form definition");const t=e.data,n=this.renderer.render(t,this.currentTheme);this.container.innerHTML=n;const i=this.config.primaryColor||"#C15F3C";this.container.style.setProperty("--wabbit-primary-color",i);const s=this.calculateHoverColor(i);if(this.container.style.setProperty("--wabbit-primary-color-hover",s),console.log(`[Wabbit] Form in container "${this.config.containerId}" using primary color: ${i}`),this.formElement=this.container.querySelector("form"),!this.formElement)throw new Error("Form element not found after rendering");this.attachSubmitHandler(t.settings.successMessage),this.setupRatingInteractions(),this.config.onLoad&&this.config.onLoad()}catch(e){console.error("[Wabbit] Form load error:",e),this.showError(e instanceof Error?e.message:"Failed to load form"),this.config.onError&&this.config.onError(e instanceof Error?e:new Error(String(e)))}}findContainer(){if(this.config.containerId){const e=document.getElementById(this.config.containerId)||document.querySelector(this.config.containerId);if(e)return e}const e=document.querySelector(`[data-wabbit-form-id="${this.config.formId}"]`);return e||null}attachSubmitHandler(e){this.formElement&&this.formElement.addEventListener("submit",async t=>{t.preventDefault(),await this.handleSubmit(e)})}setupRatingInteractions(){if(!this.formElement)return;this.formElement.querySelectorAll('.wabbit-rating input[type="radio"]').forEach(e=>{const t=this.formElement.querySelector(`label[for="${e.id}"]`);if(!t)return;const n=e;n.addEventListener("change",()=>{this.updateRatingStars(n)}),t.addEventListener("mouseenter",()=>{const e=this.styles.getColors(this.currentTheme);t.style.color=e.inputBorderFocus}),t.addEventListener("mouseleave",()=>{this.updateRatingStars(n)})})}updateRatingStars(e){if(!this.formElement)return;const t=e.name,n=this.styles.getColors(this.currentTheme),i=this.formElement.querySelectorAll(`input[name="${t}"]`),s=this.formElement.querySelectorAll(`label[for^="${t}_"]`);i.forEach(e=>{const t=Array.from(s).find(t=>t.getAttribute("for")===e.id);t&&(e.checked?t.style.color=n.inputBorderFocus:t.style.color=n.ratingInactive)})}async handleSubmit(e){if(!this.formElement)return;const t=this.formElement.querySelector(".wabbit-submit"),n=this.formElement.querySelector(".wabbit-message");if(t&&n){t.disabled=!0,t.textContent="Submitting...",n.style.display="none",n.className="wabbit-message";try{const i=new FormData(this.formElement),s={};for(const[e,t]of i.entries())if(e.endsWith("[]")){const n=e.replace("[]","");s[n]||(s[n]=[]),s[n].push(t)}else s[e]=t;const a=await this.apiClient.post(`/api/forms/${this.config.formId}/submit`,{responses:s,metadata:{referrer:document.referrer,userAgent:navigator.userAgent}},{requireAuth:!1});if(a.success&&a.data?.success){const i=a.data.message||e||"Thank you! Your submission has been received.";this.showSuccess(i,n),this.formElement.reset(),this.config.onSubmit&&this.config.onSubmit(s,a.data.submissionId),setTimeout(()=>{t.disabled=!1,t.textContent="Submit"},2e3)}else{const e=a.data?.error||a.error||"Failed to submit form",i=this.formatErrorMessage(e);this.showError(i,n),t.disabled=!1,t.textContent="Submit",this.config.onError&&this.config.onError(new Error(e))}}catch(e){console.error("[Wabbit] Submission error:",e);const i=e instanceof Error?e.message:"Network error. Please try again.",s=this.formatErrorMessage(i);this.showError(s,n),t.disabled=!1,t.textContent="Submit",this.config.onError&&this.config.onError(e instanceof Error?e:new Error(String(e)))}}}formatErrorMessage(e){if(!e)return"An error occurred. Please try again.";let t=e.replace(/^HTTP\s+\d+\s*/i,"").replace(/^\d+\s+[A-Z\s]+:\s*/i,"").replace(/\b\d{3}\s+[A-Z\s]+\b/gi,"").trim();return t&&0!==t.length?(t=t.replace(/^(Error|Failed|Error:)\s*/i,""),t):"An error occurred. Please try again."}showSuccess(e,t){const n=t||this.formElement?.querySelector(".wabbit-message");n&&(n.textContent=e,n.className="wabbit-message wabbit-message-success",n.style.display="block")}showError(e,t){if(this.container&&!t){const t=this.styles.getColors(this.currentTheme);return void(this.container.innerHTML=`<p style="color: ${t.errorColor}; padding: 1rem; border: 1px solid ${t.errorBorder}; border-radius: 0.5rem; background-color: ${t.errorBg};">${d(e)}</p>`)}const n=t||this.formElement?.querySelector(".wabbit-message");n&&(n.textContent=e,n.className="wabbit-message wabbit-message-error",n.style.display="block")}setupThemeWatcher(){const e=this.config.theme||"auto";"auto"===e?this.themeCleanup=g(e=>{this.currentTheme=e,this.updateFormTheme()}):(this.currentTheme=e,this.styles.updateTheme(e))}updateFormTheme(){if(this.styles.updateTheme(this.currentTheme),this.formElement){this.formElement.querySelectorAll('.wabbit-rating input[type="radio"]').forEach(e=>{e.checked&&this.updateRatingStars(e)})}}calculateHoverColor(e){if(e.startsWith("#")){const t=parseInt(e.slice(1),16);return`#${(Math.max(0,Math.floor(.9*(t>>16)))<<16|Math.max(0,Math.floor(.9*(t>>8&255)))<<8|Math.max(0,Math.floor(.9*(255&t)))).toString(16).padStart(6,"0")}`}return e}getApiUrlFromScript(){const e=document.currentScript||document.querySelector('script[src*="embed"]');if(e&&e instanceof HTMLScriptElement&&e.src)try{return new URL(e.src).origin}catch{}return"https://platform.insourcedata.ai"}destroy(){this.themeCleanup&&(this.themeCleanup(),this.themeCleanup=null),this.formObserver&&(this.formObserver.disconnect(),this.formObserver=null),this.styles&&this.styles.destroy(),this.container&&(this.container.innerHTML=""),this.container=null,this.formElement=null}}var k=Object.freeze({__proto__:null,FormWidget:S});class M{constructor(e){this.overlay=null,this.modal=null,this.isOpen=!1,this.config=e}show(e,t){this.isOpen||(this.onSubmitCallback=e,this.onDismissCallback=t,this.isOpen=!0,this.render())}hide(){this.isOpen&&(this.isOpen=!1,this.destroy())}render(){const e="dark"===p();this.overlay=document.createElement("div"),this.overlay.className="wabbit-email-capture-overlay",this.overlay.addEventListener("click",e=>{e.target===this.overlay&&this.handleDismiss()}),this.modal=document.createElement("div"),this.modal.className="wabbit-email-capture-modal "+(e?"dark":"");const t=(this.config.fields||["email"]).map(e=>this.renderField(e)).join("");this.modal.innerHTML=`\n <button class="wabbit-email-capture-close ${e?"dark":""}" aria-label="Close">\n <svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">\n <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />\n </svg>\n </button>\n <div class="wabbit-email-capture-content">\n <h2 class="wabbit-email-capture-title">\n ${this.config.title||"Save this conversation?"}\n </h2>\n <p class="wabbit-email-capture-description">\n ${this.config.description||"Enter your email to continue this chat later"}\n </p>\n <form class="wabbit-email-capture-form">\n ${t}\n <div class="wabbit-email-capture-checkbox">\n <input type="checkbox" id="wabbit-dont-show-again" />\n <label for="wabbit-dont-show-again">Don't show this again</label>\n </div>\n <div class="wabbit-email-capture-buttons">\n <button type="submit" class="wabbit-email-capture-button wabbit-email-capture-button-primary">\n Save Conversation\n </button>\n <button type="button" class="wabbit-email-capture-button wabbit-email-capture-button-secondary">\n Maybe Later\n </button>\n </div>\n </form>\n </div>\n `,this.attachEventListeners(),this.overlay.appendChild(this.modal),document.body.appendChild(this.overlay)}renderField(e){const t={email:{label:"Email",type:"email",placeholder:"your@email.com",required:!0},name:{label:"Name",type:"text",placeholder:"Your name",required:!1},company:{label:"Company",type:"text",placeholder:"Your company",required:!1}}[e];return`\n <div class="wabbit-email-capture-field">\n <label class="wabbit-email-capture-label" for="wabbit-field-${e}">\n ${t.label}\n </label>\n <input\n type="${t.type}"\n id="wabbit-field-${e}"\n name="${e}"\n placeholder="${t.placeholder}"\n class="wabbit-email-capture-input"\n ${t.required?"required":""}\n />\n <div class="wabbit-email-capture-error" id="wabbit-error-${e}" style="display: none;"></div>\n </div>\n `}attachEventListeners(){if(!this.modal)return;const e=this.modal.querySelector(".wabbit-email-capture-close");e&&e.addEventListener("click",()=>this.handleDismiss());const t=this.modal.querySelector("form");t&&t.addEventListener("submit",e=>this.handleSubmit(e));const n=this.modal.querySelector(".wabbit-email-capture-button-secondary");n&&n.addEventListener("click",()=>this.handleDismiss())}async handleSubmit(e){if(e.preventDefault(),!this.modal||!this.onSubmitCallback)return;const t=this.modal.querySelector("form");if(!t)return;const n=new FormData(t),i={email:n.get("email")||"",name:n.get("name")||void 0,company:n.get("company")||void 0};if(!i.email||!this.isValidEmail(i.email))return void this.showError("email","Please enter a valid email address");const s=t.querySelector("#wabbit-dont-show-again")?.checked||!1;this.setFormDisabled(!0);try{await this.onSubmitCallback(i,s),this.hide()}catch(e){this.showError("email",e instanceof Error?e.message:"Failed to save email"),this.setFormDisabled(!1)}}handleDismiss(){this.onDismissCallback&&this.onDismissCallback(),this.hide()}setFormDisabled(e){if(!this.modal)return;this.modal.querySelectorAll("input, button").forEach(t=>{t.style.opacity=e?"0.5":"1",t.style.pointerEvents=e?"none":"auto"});const t=this.modal.querySelector(".wabbit-email-capture-button-primary");t&&(t.disabled=e,t.textContent=e?"Saving...":"Save Conversation")}showError(e,t){if(!this.modal)return;const n=this.modal.querySelector(`#wabbit-error-${e}`);n&&(n.textContent=t,n.style.display="block")}isValidEmail(e){return/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e)}destroy(){this.overlay&&(this.overlay.remove(),this.overlay=null),this.modal=null,this.isOpen=!1}}class ${constructor(e){this.messageCount=0,this.emailCaptured=!1,this.wsClient=null,this.config=e,this.modal=new M(e),this.onCaptureCallback=e.onCapture}init(){!function(e="#6366f1"){const t="wabbit-email-capture-styles",n=document.getElementById(t);n&&n.remove();const i=document.createElement("style");i.id=t,i.setAttribute("data-wabbit","email-capture-styles"),i.textContent=`\n /* Email Capture Modal Styles */\n .wabbit-email-capture-overlay {\n position: fixed;\n inset: 0;\n z-index: 9999;\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n animation: wabbit-fade-in 0.2s ease-out;\n padding: 1rem;\n box-sizing: border-box;\n overflow-y: auto;\n }\n\n .wabbit-email-capture-modal {\n position: relative;\n background: white;\n border-radius: 0.5rem;\n box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);\n padding: 1.5rem;\n width: calc(100% - 2rem);\n max-width: 28rem;\n margin: 1rem;\n animation: wabbit-slide-up 0.3s ease-out;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .wabbit-email-capture-modal.dark {\n background: #1f2937;\n color: #f9fafb;\n }\n\n .wabbit-email-capture-close {\n position: absolute;\n top: 1rem;\n right: 1rem;\n background: none;\n border: none;\n color: #6b7280;\n cursor: pointer;\n padding: 0.25rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 0.25rem;\n transition: all 0.2s;\n }\n\n .wabbit-email-capture-close:hover {\n color: #374151;\n background-color: #f3f4f6;\n }\n\n .wabbit-email-capture-close.dark:hover {\n color: #d1d5db;\n background-color: #374151;\n }\n\n .wabbit-email-capture-close:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .wabbit-email-capture-title {\n font-size: 1.25rem;\n font-weight: 600;\n color: #111827;\n margin-bottom: 0.5rem;\n }\n\n .wabbit-email-capture-modal.dark .wabbit-email-capture-title {\n color: #f9fafb;\n }\n\n .wabbit-email-capture-description {\n font-size: 0.875rem;\n color: #6b7280;\n margin-bottom: 1.5rem;\n }\n\n .wabbit-email-capture-modal.dark .wabbit-email-capture-description {\n color: #d1d5db;\n }\n\n .wabbit-email-capture-form {\n display: flex;\n flex-direction: column;\n gap: 1rem;\n width: 100%;\n box-sizing: border-box;\n }\n\n .wabbit-email-capture-field {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n width: 100%;\n box-sizing: border-box;\n }\n\n .wabbit-email-capture-label {\n font-size: 0.875rem;\n font-weight: 500;\n color: #374151;\n }\n\n .wabbit-email-capture-modal.dark .wabbit-email-capture-label {\n color: #d1d5db;\n }\n\n .wabbit-email-capture-input {\n width: 100%;\n padding: 0.75rem;\n border: 1px solid #d1d5db;\n border-radius: 0.375rem;\n font-size: 0.875rem;\n transition: all 0.2s;\n background: white;\n color: #111827;\n box-sizing: border-box;\n }\n\n .wabbit-email-capture-modal.dark .wabbit-email-capture-input {\n background: #374151;\n border-color: #4b5563;\n color: #f9fafb;\n }\n\n .wabbit-email-capture-input:focus {\n outline: none;\n border-color: ${e};\n box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);\n }\n\n .wabbit-email-capture-input:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .wabbit-email-capture-error {\n font-size: 0.875rem;\n color: #dc2626;\n margin-top: 0.25rem;\n }\n\n .wabbit-email-capture-checkbox {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n }\n\n .wabbit-email-capture-checkbox input[type="checkbox"] {\n width: 1rem;\n height: 1rem;\n accent-color: ${e};\n cursor: pointer;\n }\n\n .wabbit-email-capture-checkbox label {\n font-size: 0.875rem;\n color: #6b7280;\n cursor: pointer;\n }\n\n .wabbit-email-capture-modal.dark .wabbit-email-capture-checkbox label {\n color: #d1d5db;\n }\n\n .wabbit-email-capture-buttons {\n display: flex;\n gap: 0.75rem;\n margin-top: 0.5rem;\n width: 100%;\n box-sizing: border-box;\n }\n\n .wabbit-email-capture-button {\n flex: 1;\n padding: 0.75rem 1.5rem;\n border: none;\n border-radius: 0.375rem;\n font-size: 0.875rem;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s;\n box-sizing: border-box;\n min-width: 0;\n }\n\n .wabbit-email-capture-button-primary {\n background-color: ${e};\n color: white;\n }\n\n .wabbit-email-capture-button-primary:hover:not(:disabled) {\n opacity: 0.9;\n transform: translateY(-1px);\n }\n\n .wabbit-email-capture-button-primary:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .wabbit-email-capture-button-secondary {\n background-color: white;\n color: #374151;\n border: 1px solid #d1d5db;\n }\n\n .wabbit-email-capture-modal.dark .wabbit-email-capture-button-secondary {\n background-color: #374151;\n color: #d1d5db;\n border-color: #4b5563;\n }\n\n .wabbit-email-capture-button-secondary:hover:not(:disabled) {\n background-color: #f9fafb;\n }\n\n .wabbit-email-capture-modal.dark .wabbit-email-capture-button-secondary:hover:not(:disabled) {\n background-color: #4b5563;\n }\n\n @keyframes wabbit-fade-in {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n }\n\n @keyframes wabbit-slide-up {\n from {\n opacity: 0;\n transform: translateY(1rem);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n `,document.head.appendChild(i)}("#6366f1");if("true"===r("wabbit_email_capture_dismissed"))return void console.log("[Wabbit] Email capture dismissed by user");return"true"===r("wabbit_email_captured")?(this.emailCaptured=!0,void console.log("[Wabbit] Email already captured")):void 0}setWebSocketClient(e){this.wsClient=e}handleMessage(){if(this.emailCaptured)return;this.messageCount++;const e=this.config.triggerAfterMessages||3;this.messageCount>=e&&this.showModal()}showModal(){this.emailCaptured||this.modal.show((e,t)=>this.handleEmailSubmit(e,t),()=>this.handleDismiss())}async handleEmailSubmit(e,t){if(t&&o("wabbit_email_capture_dismissed","true"),this.wsClient&&this.wsClient.ws&&this.wsClient.ws.readyState===WebSocket.OPEN)return new Promise((t,n)=>{const i=setTimeout(()=>{n(new Error("Email capture timeout"))},5e3),s=a=>{try{const r=JSON.parse(a.data);"email_confirmed"===r.type&&(clearTimeout(i),this.wsClient.ws.removeEventListener("message",s),r.success?(this.emailCaptured=!0,o("wabbit_email_captured","true"),this.onCaptureCallback&&this.onCaptureCallback({email:e.email,name:e.name||"",company:e.company||""}),t()):n(new Error(r.message||"Failed to save email")))}catch(e){}};this.wsClient.ws.addEventListener("message",s),this.wsClient.ws.send(JSON.stringify({type:"provide_email",email:e.email,name:e.name,company:e.company}))});this.emailCaptured=!0,o("wabbit_email_captured","true"),this.onCaptureCallback&&this.onCaptureCallback({email:e.email,name:e.name||"",company:e.company||""})}handleDismiss(){}destroy(){this.modal&&this.modal.destroy(),this.wsClient=null}}var W=Object.freeze({__proto__:null,EmailCaptureWidget:$});function F(e){return l.init(e)}function T(){return l.getInstance()}function I(){return l.destroy()}function A(e,t){return l.getChatPageUrl(e,t)}function B(e,t){return l.openChatPage(e,t)}const _={init:F,getInstance:T,destroy:I,getChatPageUrl:A,openChatPage:B};function L(){if(l.getInstance())return;const e=document.querySelectorAll("[data-wabbit-form-id]");if(0===e.length)return;console.log("[Wabbit] Auto-initializing",e.length,"form(s)");const t=(()=>{const e=document.currentScript||document.querySelector('script[src*="wabbit"]')||document.querySelector('script[src*="embed"]');if(e&&e instanceof HTMLScriptElement&&e.src)try{return new URL(e.src).origin}catch{}return"https://platform.insourcedata.ai"})();e.forEach(e=>{const n=e.getAttribute("data-wabbit-form-id");n&&(e.__wabbitFormWidget||Promise.resolve().then(function(){return k}).then(({FormWidget:i})=>{try{const s=new i({enabled:!0,formId:n,apiUrl:t,theme:"auto"});s.findContainer=()=>e,s.init(),e.__wabbitFormWidget=s,console.log("[Wabbit] Form auto-initialized:",n)}catch(e){console.error("[Wabbit] Failed to auto-initialize form:",n,e)}}))})}if("undefined"!=typeof document){"loading"===document.readyState?document.addEventListener("DOMContentLoaded",L):L();const e=new MutationObserver(e=>{for(const t of e)if(t.addedNodes.length){if(Array.from(t.addedNodes).some(e=>{if(1!==e.nodeType)return!1;const t=e;return t.hasAttribute?.("data-wabbit-form-id")||t.querySelector?.("[data-wabbit-form-id]")})){console.log("[Wabbit] Detected dynamically added form container"),L();break}}});"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{e.observe(document.body,{childList:!0,subtree:!0})}):e.observe(document.body,{childList:!0,subtree:!0})}e.ApiClient=E,e.ChatBubble=m,e.ChatPanel=u,e.ChatWebSocketClient=c,e.ChatWidget=y,e.EmailCaptureModal=M,e.EmailCaptureWidget=$,e.EventEmitter=class{constructor(){this.events=new Map}on(e,t){this.events.has(e)||this.events.set(e,[]),this.events.get(e).push(t)}off(e,t){const n=this.events.get(e);if(n){const e=n.indexOf(t);e>-1&&n.splice(e,1)}}emit(e,...t){const n=this.events.get(e);n&&n.forEach(n=>{try{n(...t)}catch(t){console.error(`[Wabbit] Error in event handler for "${e}":`,t)}})}removeAllListeners(e){e?this.events.delete(e):this.events.clear()}listenerCount(e){return this.events.get(e)?.length||0}},e.FormRenderer=C,e.FormStyles=x,e.FormWidget=S,e.SafeStorage=s,e.Wabbit=l,e.createElement=b,e.default=_,e.destroy=I,e.detectTheme=p,e.escapeHtml=d,e.getChatPageUrl=A,e.getInstance=T,e.init=F,e.mergeConfig=n,e.onDOMReady=h,e.openChatPage=B,e.storage=a,e.validateConfig=t,e.watchTheme=g,Object.defineProperty(e,"__esModule",{value:!0})});
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Wabbit={})}(this,function(e){"use strict";function t(e){if(!e.apiKey)throw new Error("[Wabbit] API key is required");if("string"!=typeof e.apiKey)throw new Error("[Wabbit] API key must be a string");if(e.chat?.enabled){if(!e.chat.collectionId)throw new Error("[Wabbit] collectionId is required when chat is enabled");if("inline"===e.chat.mode&&!e.chat.container)throw new Error('[Wabbit] container is required when chat mode is "inline"')}if(e.forms?.enabled&&!e.forms.formId)throw new Error("[Wabbit] formId is required when forms is enabled")}function n(e){const t=function(e){if(e)return e;if("undefined"!=typeof window&&window.WABBIT_API_URL)return window.WABBIT_API_URL;if("undefined"!=typeof window){const e=window.location.hostname;if("localhost"===e||"127.0.0.1"===e||"0.0.0.0"===e)return"http://localhost:3000"}return"https://platform.insourcedata.ai"}(e.apiUrl),n=function(e,t){return e||("undefined"!=typeof window&&window.WABBIT_WS_URL?window.WABBIT_WS_URL:t&&(t.includes("localhost")||t.includes("127.0.0.1"))?"ws://localhost:8003/ws/chat":"wss://chat.insourcedata.ai/ws/chat")}(e.wsUrl,t);return{...e,apiUrl:t,wsUrl:n,chat:e.chat?{...e.chat,mode:e.chat.mode||"widget",position:e.chat.position||"bottom-right",triggerType:e.chat.triggerType||"button",theme:e.chat.theme||"auto",primaryColor:e.chat.primaryColor||"#6366f1",placeholder:e.chat.placeholder||"Type your message...",showHeader:e.chat.showHeader??!0,headerTitle:e.chat.headerTitle||"AI Assistant"}:void 0,forms:e.forms?{...e.forms,theme:e.forms.theme||"auto"}:void 0,emailCapture:e.emailCapture?{...e.emailCapture,triggerAfterMessages:e.emailCapture.triggerAfterMessages||3,fields:e.emailCapture.fields||["email"]}:void 0}}class i{constructor(){this.store=new Map}getItem(e){return this.store.get(e)??null}setItem(e,t){this.store.set(e,t)}removeItem(e){this.store.delete(e)}clear(){this.store.clear()}get length(){return this.store.size}key(e){return Array.from(this.store.keys())[e]??null}}class a{constructor(e=localStorage,t="wabbit_"){this.storage=e,this.prefix=t}get(e){try{const t=this.storage.getItem(this.prefix+e);return null===t?null:JSON.parse(t)}catch(t){return console.error(`[Wabbit] Failed to get storage item "${e}":`,t),null}}set(e,t){try{return this.storage.setItem(this.prefix+e,JSON.stringify(t)),!0}catch(t){return console.error(`[Wabbit] Failed to set storage item "${e}":`,t),!1}}remove(e){this.storage.removeItem(this.prefix+e)}clear(){if(void 0!==this.storage.length&&this.storage.key){const e=[];for(let t=0;t<this.storage.length;t++){const n=this.storage.key(t);n&&n.startsWith(this.prefix)&&e.push(n)}e.forEach(e=>this.storage.removeItem(e))}else{["session_id","email_capture_dismissed"].forEach(e=>this.remove(e))}}}const s=new a;function r(e){try{return localStorage.getItem("wabbit_"+e)}catch{return null}}function o(e,t){try{localStorage.setItem("wabbit_"+e,t)}catch(t){console.error(`[Wabbit] Failed to set storage item "${e}":`,t)}}class l{constructor(e){this.chatWidget=null,this.formsWidget=null,this.emailCaptureWidget=null,this.config=e,this.storage=function(e=!0){let t;return t=e?"undefined"!=typeof window?window.localStorage:new i:"undefined"!=typeof window?window.sessionStorage:new i,new a(t)}(e.persistSession??!0)}static init(e){if(l.instance)return console.warn("[Wabbit] SDK already initialized. Returning existing instance."),l.instance;t(e);const i=n(e);l.instance=new l(i);let a=null;if(i.chat?.enabled&&i.chat){const t=i.chat,n=t.collectionId,s=t.onChatReady||e.onChatReady,r=(i.wsUrl||t.wsUrl||"wss://chat.insourcedata.ai/ws/chat").replace(/^wss:\/\//,"https://").replace(/^ws:\/\//,"http://").replace(/\/ws\/chat.*$/,"");Promise.resolve().then(function(){return C}).then(({ChatWidget:e})=>{if(!l.instance)return void console.warn("[Wabbit] Instance was destroyed before chat widget could initialize");const n={enabled:t.enabled,collectionId:t.collectionId,apiKey:t.apiKey||i.apiKey,apiUrl:t.apiUrl||i.apiUrl,wsUrl:i.wsUrl||t.wsUrl,mode:t.mode,container:t.container,position:t.position,triggerType:t.triggerType,triggerDelay:t.triggerDelay,theme:t.theme,primaryColor:t.primaryColor,welcomeMessage:t.welcomeMessage,placeholder:t.placeholder,showHeader:t.showHeader,headerTitle:t.headerTitle,onChatReady:()=>{if(a&&l.instance?.chatWidget){const e=l.instance.chatWidget;e.wsClient&&a.setWebSocketClient(e.wsClient)}s&&s()},onUserMessage:()=>{a&&a.handleMessage()}},r=new e(n,l.instance.storage);r.init(),l.instance.chatWidget=r}),fetch(`${r}/api/email-capture/config/${n}`).then(e=>e.json()).then(async e=>{if(!e.success||!e.config?.enabled)return;if(!l.instance)return;const t=e.config,{EmailCaptureWidget:n}=await Promise.resolve().then(function(){return I});if(!l.instance)return;const s="message_count"===t.trigger?.type?t.trigger.messageCount:3,r=["email"];t.fields?.name?.enabled&&r.push("name"),t.fields?.company?.enabled&&r.push("company");const o=new n({enabled:!0,triggerAfterMessages:s,title:t.modal?.title,description:t.modal?.description,fields:r,onCapture:i.emailCapture?.onCapture});if(o.init(),l.instance.emailCaptureWidget=o,a=o,l.instance.chatWidget){const e=l.instance.chatWidget;e.wsClient&&o.setWebSocketClient(e.wsClient)}}).catch(e=>{console.warn("[Wabbit] Failed to fetch email capture config:",e)})}return i.forms?.enabled&&i.forms&&Promise.resolve().then(function(){return k}).then(({FormWidget:e})=>{if(!l.instance)return void console.warn("[Wabbit] Instance was destroyed before form widget could initialize");const t=i.forms,n=new e({enabled:t.enabled,containerId:t.containerId,formId:t.formId,apiKey:t.apiKey||i.apiKey,apiUrl:t.apiUrl||i.apiUrl,theme:t.theme,primaryColor:t.primaryColor,onSubmit:t.onSubmit,onError:t.onError,onLoad:t.onLoad});n.init(),l.instance.formsWidget=n}),l.instance&&l.instance.initLegacyForms(e),e.onReady&&("loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{e.onReady?.()}):e.onReady()),l.instance}static getInstance(){return l.instance}static destroy(){if(l.instance){l.instance.chatWidget&&l.instance.chatWidget.destroy(),l.instance.formsWidget&&l.instance.formsWidget.destroy();document.querySelectorAll("[data-wabbit-form-id]").forEach(e=>{const t=e.__wabbitFormWidget;t&&"function"==typeof t.destroy&&t.destroy()}),l.instance.legacyFormObserver&&(l.instance.legacyFormObserver.disconnect(),l.instance.legacyFormObserver=null),l.instance.emailCaptureWidget&&l.instance.emailCaptureWidget.destroy(),l.instance=null}}static getChatPageUrl(e,t){const n=t?.baseUrl||"undefined"!=typeof window&&window.WABBIT_BASE_URL||("undefined"==typeof window||"localhost"!==window.location.hostname&&"127.0.0.1"!==window.location.hostname?"https://platform.insourcedata.ai":"http://localhost:3000"),i=new URL(`/c/${e}`,n);return t?.initialMessage&&i.searchParams.set("q",t.initialMessage),t?.theme&&"auto"!==t.theme&&i.searchParams.set("theme",t.theme),t?.primaryColor&&i.searchParams.set("color",t.primaryColor.replace("#","")),i.toString()}static openChatPage(e,t){const n=l.getChatPageUrl(e,t);window.open(n,"_blank","noopener,noreferrer")}getConfig(){return{...this.config}}get chat(){return this.chatWidget}get forms(){return this.formsWidget}get emailCapture(){return this.emailCaptureWidget}initLegacyForms(e){e.apiKey&&("loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{this.initLegacyFormsOnReady(e)}):this.initLegacyFormsOnReady(e))}initLegacyFormsOnReady(e){const t=document.querySelectorAll("[data-wabbit-form-id]");0!==t.length&&(console.log("[Wabbit] Found",t.length,"legacy form container(s)"),t.forEach(t=>{const n=t.getAttribute("data-wabbit-form-id");n&&(t.querySelector('[data-wabbit-form-instance="true"]')?console.warn(`[Wabbit] Form ${n} already initialized`):Promise.resolve().then(function(){return k}).then(({FormWidget:i})=>{const a=new i({enabled:!0,containerId:void 0,formId:n,apiKey:e.apiKey,apiUrl:e.apiUrl,theme:e.forms?.theme||"auto",primaryColor:e.forms?.primaryColor,onSubmit:e.forms?.onSubmit,onError:e.forms?.onError,onLoad:e.forms?.onLoad});a.findContainer=()=>t,a.init(),t.__wabbitFormWidget=a}))}),this.setupLegacyFormObserver(e))}setupLegacyFormObserver(e){if(this.legacyFormObserver)return;const t=new MutationObserver(t=>{for(const n of t)if(n.addedNodes.length){if(Array.from(n.addedNodes).some(e=>{if(1!==e.nodeType)return!1;const t=e;return t.hasAttribute?.("data-wabbit-form-id")||t.querySelector?.("[data-wabbit-form-id]")})){console.log("[Wabbit] Detected dynamically added legacy form container"),this.initLegacyFormsOnReady(e);break}}});t.observe(document.body,{childList:!0,subtree:!0}),this.legacyFormObserver=t}}l.instance=null;class c{constructor(e,t,n=null,i){this.ws=null,this.status="disconnected",this.reconnectAttempts=0,this.maxReconnectAttempts=3,this.reconnectDelay=1e3,this.messageQueue=[],this.MAX_QUEUE_SIZE=10,this.onStatusChange=null,this.onWelcome=null,this.onMessage=null,this.onMessageHistory=null,this.onError=null,this.onDisconnect=null,this.onQueueOverflow=null,this.onMessageStart=null,this.onMessageChunk=null,this.onMessageEnd=null,this.apiKey=e,this.wsUrl=t,this.storage=i,this.sessionId=n||this.getStoredSessionId()}setStatus(e){this.status=e,this.onStatusChange&&this.onStatusChange(e)}getStatus(){return this.status}async connect(){if(this.ws&&(this.ws.readyState===WebSocket.OPEN||this.ws.readyState===WebSocket.CONNECTING))return;this.setStatus("connecting");const e=new URLSearchParams({api_key:this.apiKey});this.sessionId&&e.append("session_id",this.sessionId);const t=`${this.wsUrl}?${e.toString()}`;try{this.ws=new WebSocket(t),this.ws.onopen=()=>{this.setStatus("connected"),this.reconnectAttempts=0,console.log("[Wabbit] WebSocket connected"),this.flushMessageQueue()},this.ws.onmessage=e=>{try{const t=JSON.parse(e.data);this.handleMessage(t)}catch(e){console.error("[Wabbit] Failed to parse message:",e)}},this.ws.onerror=e=>{console.error("[Wabbit] WebSocket error:",e),this.onError&&this.onError("Connection error occurred")},this.ws.onclose=()=>{console.log("[Wabbit] WebSocket closed"),this.setStatus("disconnected"),this.onDisconnect&&this.onDisconnect(),this.attemptReconnect()}}catch(e){console.error("[Wabbit] Failed to connect:",e),this.onError&&this.onError("Failed to establish connection"),this.setStatus("disconnected")}}handleMessage(e){switch(e.type){case"welcome":this.sessionId=e.session_id,this.sessionId&&this.storage.set("session_id",this.sessionId),this.onWelcome&&this.onWelcome(e.session_id,e.collection_id,e.message||"Connected");break;case"message_history":if(this.onMessageHistory&&e.messages&&Array.isArray(e.messages)){const t=e.messages.map(e=>({id:e.id||crypto.randomUUID(),role:e.role,content:e.content,timestamp:new Date(e.timestamp),metadata:e.metadata}));this.onMessageHistory(t)}break;case"assistant_message_start":this.onMessageStart&&this.onMessageStart(e.message_id);break;case"assistant_message_chunk":this.onMessageChunk&&this.onMessageChunk(e.message_id,e.content);break;case"assistant_message_end":this.onMessageEnd&&this.onMessageEnd(e.message_id,e.metadata);break;case"assistant_message":if(this.onMessage){const t={id:e.message_id||crypto.randomUUID(),role:"assistant",content:e.content,timestamp:new Date,metadata:e.metadata};this.onMessage(t)}break;case"error":this.onError&&this.onError(e.message||"An error occurred");break;default:console.log("[Wabbit] Unknown message type:",e.type)}}sendMessage(e,t){const n={type:"message",content:e,metadata:t||{}};if(this.ws&&this.ws.readyState===WebSocket.OPEN)this.ws.send(JSON.stringify(n));else{if(this.messageQueue.length>=this.MAX_QUEUE_SIZE)return console.warn("[Wabbit] Message queue full, cannot queue message"),void(this.onQueueOverflow&&this.onQueueOverflow("Message queue full. Please wait for reconnection."));this.messageQueue.push({content:e,metadata:t}),console.log(`[Wabbit] Message queued (${this.messageQueue.length}/${this.MAX_QUEUE_SIZE})`)}}disconnect(){this.ws&&(this.ws.close(),this.ws=null),this.setStatus("disconnected")}attemptReconnect(){if(this.reconnectAttempts>=this.maxReconnectAttempts)return void console.log("[Wabbit] Max reconnect attempts reached");this.reconnectAttempts++,this.setStatus("reconnecting");const e=this.reconnectDelay*Math.pow(2,this.reconnectAttempts-1);console.log(`[Wabbit] Attempting to reconnect in ${e}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`),setTimeout(()=>{this.connect()},e)}flushMessageQueue(){if(0===this.messageQueue.length)return;console.log(`[Wabbit] Flushing ${this.messageQueue.length} queued message(s)`);const e=[...this.messageQueue];this.messageQueue=[],e.forEach(({content:e,metadata:t})=>{if(this.ws&&this.ws.readyState===WebSocket.OPEN){const n={type:"message",content:e,metadata:t||{}};this.ws.send(JSON.stringify(n))}})}clearSession(){this.sessionId=null,this.storage.remove("session_id"),this.messageQueue=[]}clearMessageQueue(){this.messageQueue=[]}getStoredSessionId(){return this.storage.get("session_id")||null}getQueueSize(){return this.messageQueue.length}}function d(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}function h(e){"loading"===document.readyState?document.addEventListener("DOMContentLoaded",e):e()}function b(e,t,n){const i=document.createElement(e);return t&&Object.entries(t).forEach(([e,t])=>{i.setAttribute(e,t)}),n&&n.forEach(e=>{"string"==typeof e?i.appendChild(document.createTextNode(e)):i.appendChild(e)}),i}class m{constructor(e){this.element=null,this.options=e}render(){return this.element||(this.element=b("button",{class:`wabbit-chat-bubble ${this.options.position}`,"aria-label":"Open chat",type:"button"}),this.element.innerHTML='\n <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">\n <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>\n </svg>\n ',this.element.addEventListener("click",this.options.onClick),document.body.appendChild(this.element)),this.element}show(){this.element&&(this.element.style.display="flex")}hide(){this.element&&(this.element.style.display="none")}destroy(){this.element&&(this.element.removeEventListener("click",this.options.onClick),this.element.remove(),this.element=null)}}class u{constructor(e){this.element=null,this.messagesContainer=null,this.inputElement=null,this.sendButton=null,this.messages=[],this.isWaitingForResponse=!1,this.closeButton=null,this.statusIndicator=null,this.eventCleanup=[],this.streamingMessages=new Map,this.streamingCleanupInterval=null,this.STREAMING_TIMEOUT_MS=3e4,this.options=e}render(){if(this.element)return this.element;const e="inline"===this.options.mode;if(this.element=b("div",{class:e?"wabbit-chat-panel wabbit-chat-panel-inline":`wabbit-chat-panel ${this.options.position}`}),!1!==this.options.showHeader){const t=this.createHeader(e);this.element.appendChild(t)}this.messagesContainer=b("div",{class:"wabbit-chat-messages"});const t=this.createInputArea();return this.element.appendChild(this.messagesContainer),this.element.appendChild(t),e&&this.options.container?(this.options.container.appendChild(this.element),this.element.style.display="flex"):(document.body.appendChild(this.element),this.element.style.display="none"),this.options.welcomeMessage&&this.addSystemMessage(this.options.welcomeMessage),this.scrollToBottom(),this.element}createHeader(e){const t=b("div",{class:"wabbit-chat-panel-header"}),n=b("div",{class:"wabbit-chat-panel-header-title"}),i=b("div",{class:"wabbit-chat-panel-header-icon"});i.textContent="AI";const a=b("div",{class:"wabbit-chat-panel-header-text"}),s=b("h3");s.textContent=this.options.headerTitle||"AI Assistant";const r=b("p");if(r.textContent="Powered by Wabbit",a.appendChild(s),a.appendChild(r),n.appendChild(i),n.appendChild(a),t.appendChild(n),!e){this.closeButton=b("button",{class:"wabbit-chat-panel-close","aria-label":"Close chat",type:"button"}),this.closeButton.innerHTML="×";const e=this.options.onClose;this.closeButton.addEventListener("click",e),this.eventCleanup.push(()=>{this.closeButton?.removeEventListener("click",e)}),t.appendChild(this.closeButton)}return t}createInputArea(){const e=b("div",{class:"wabbit-chat-input-area"}),t=b("div",{class:"wabbit-chat-input-wrapper"});this.inputElement=document.createElement("textarea"),this.inputElement.className="wabbit-chat-input",this.inputElement.placeholder=this.options.placeholder||"Type your message...",this.inputElement.rows=1,this.inputElement.disabled=this.options.disabled||!1;const n=()=>{this.inputElement.style.height="auto",this.inputElement.style.height=`${Math.min(this.inputElement.scrollHeight,120)}px`,this.updateSendButtonState()};this.inputElement.addEventListener("input",n),this.eventCleanup.push(()=>{this.inputElement?.removeEventListener("input",n)});const i=e=>{"Enter"!==e.key||e.shiftKey||(e.preventDefault(),this.handleSend())};this.inputElement.addEventListener("keydown",i),this.eventCleanup.push(()=>{this.inputElement?.removeEventListener("keydown",i)}),this.sendButton=b("button",{class:"wabbit-chat-send-button",type:"button","aria-label":"Send message"}),this.sendButton.innerHTML='\n <svg class="wabbit-chat-send-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">\n <line x1="22" y1="2" x2="11" y2="13"></line>\n <polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>\n </svg>\n ';const a=()=>this.handleSend();return this.sendButton.addEventListener("click",a),this.eventCleanup.push(()=>{this.sendButton?.removeEventListener("click",a)}),this.updateSendButtonState(),this.statusIndicator=b("div",{class:"wabbit-chat-status-indicator","data-status":"connecting",title:"Connecting...","aria-label":"Connection status: connecting",role:"status"}),t.appendChild(this.statusIndicator),t.appendChild(this.inputElement),t.appendChild(this.sendButton),e.appendChild(t),e}addMessage(e){this.messages.push(e),this.renderMessage(e),this.scrollToBottom()}setMessages(e){this.messages=e,this.messagesContainer&&(this.messagesContainer.innerHTML="",e.forEach(e=>this.renderMessage(e)),this.scrollToBottom())}addSystemMessage(e,t){const n={id:`system-${Date.now()}`,role:"system",content:e,timestamp:new Date};this.messages.push(n),this.renderMessage(n,t),this.scrollToBottom()}setWaitingForResponse(e){if(this.isWaitingForResponse=e,this.messagesContainer){const t=this.messagesContainer.querySelector(".wabbit-chat-typing");if(t&&t.remove(),e){const e=b("div",{class:"wabbit-chat-typing"});e.innerHTML='\n <div class="wabbit-chat-typing-dot"></div>\n <div class="wabbit-chat-typing-dot"></div>\n <div class="wabbit-chat-typing-dot"></div>\n ',this.messagesContainer.appendChild(e)}this.scrollToBottom()}this.updateSendButtonState()}setDisabled(e){this.options.disabled=e,this.inputElement&&(this.inputElement.disabled=e,this.updateSendButtonState())}setConnectionStatus(e){if(!this.statusIndicator)return;this.statusIndicator.setAttribute("data-status",e);const t={connected:"Connected",disconnected:"Disconnected",connecting:"Connecting...",reconnecting:"Reconnecting..."};this.statusIndicator.setAttribute("title",t[e]),this.statusIndicator.setAttribute("aria-label",`Connection status: ${t[e].toLowerCase()}`)}renderMessage(e,t){if(!this.messagesContainer)return;const n=b("div",{class:`wabbit-chat-message wabbit-chat-message-${e.role}`+("system"===e.role&&t?" wabbit-chat-message-system-error":"")}),i=b("div",{class:"wabbit-chat-message-bubble"}),a=b("div",{class:"wabbit-chat-message-content"});"assistant"===e.role?a.innerHTML=this.formatMessage(e.content):a.textContent=e.content,i.appendChild(a),n.appendChild(i),this.messagesContainer.appendChild(n)}formatMessage(e){let t=d(e);return t=t.replace(/\[([^\]]+)\]\(([^)]+)\)/g,'<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>'),t=t.replace(/\*\*(.+?)\*\*/g,"<strong>$1</strong>"),t=t.replace(/\*(.+?)\*/g,"<em>$1</em>"),t=t.replace(/`(.+?)`/g,'<code style="background: rgba(0,0,0,0.1); padding: 2px 4px; border-radius: 3px;">$1</code>'),t=t.replace(/(?<!href=")(https?:\/\/[^\s<]+)/g,'<a href="$1" target="_blank" rel="noopener noreferrer">$1</a>'),t=t.replace(/(?<!["\/])([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/g,'<a href="mailto:$1">$1</a>'),t=t.replace(/(?<!["\/])(\+\d[\d\s-]{7,})/g,(e,t)=>`<a href="tel:${t.replace(/[\s-]/g,"")}">${t}</a>`),t=t.replace(/\n/g,"<br>"),t}startStreamingMessage(e){if(!this.messagesContainer)return;const t=b("div",{class:"wabbit-chat-message wabbit-chat-message-assistant wabbit-chat-message-streaming","data-message-id":e}),n=b("div",{class:"wabbit-chat-message-bubble"}),i=b("div",{class:"wabbit-chat-message-content"});i.innerHTML='<span class="wabbit-chat-cursor">▋</span>',n.appendChild(i),t.appendChild(n),this.messagesContainer.appendChild(t),this.streamingMessages.set(e,{element:t,content:"",startTime:Date.now()}),this.streamingCleanupInterval||(this.streamingCleanupInterval=window.setInterval(()=>{this.cleanupStaleStreamingMessages()},5e3)),this.scrollToBottom()}appendToStreamingMessage(e,t){const n=this.streamingMessages.get(e);if(!n)return void console.warn("[ChatPanel] No streaming message found for ID:",e);n.content+=t;const i=n.element.querySelector(".wabbit-chat-message-content");i&&(i.innerHTML=this.formatMessage(n.content)+'<span class="wabbit-chat-cursor">▋</span>'),this.scrollToBottom()}finishStreamingMessage(e,t){const n=this.streamingMessages.get(e);if(!n)return;n.element.classList.remove("wabbit-chat-message-streaming");const i=n.element.querySelector(".wabbit-chat-message-content");i&&(i.innerHTML=this.formatMessage(n.content));const a={id:e,role:"assistant",content:n.content,timestamp:new Date,metadata:t};this.messages.push(a),this.streamingMessages.delete(e),this.scrollToBottom()}cancelStreamingMessage(e){const t=this.streamingMessages.get(e);t&&(t.element.remove(),this.streamingMessages.delete(e))}cancelAllStreamingMessages(){this.streamingMessages.forEach(e=>{e.element.remove()}),this.streamingMessages.clear()}cleanupStaleStreamingMessages(){const e=Date.now(),t=[];this.streamingMessages.forEach((n,i)=>{e-n.startTime>this.STREAMING_TIMEOUT_MS&&(console.warn("[ChatPanel] Cleaning up stale streaming message:",i),n.element.remove(),t.push(i))}),t.forEach(e=>this.streamingMessages.delete(e)),0===this.streamingMessages.size&&this.streamingCleanupInterval&&(clearInterval(this.streamingCleanupInterval),this.streamingCleanupInterval=null)}handleSend(){if(!this.inputElement||this.isWaitingForResponse)return;const e=this.inputElement.value.trim();e&&(this.inputElement.value="",this.inputElement.style.height="auto",this.options.onSendMessage(e))}updateSendButtonState(){if(!this.sendButton||!this.inputElement)return;const e=this.inputElement.value.trim().length>0,t=this.options.disabled||this.isWaitingForResponse||!e;t?this.sendButton.setAttribute("disabled","true"):this.sendButton.removeAttribute("disabled"),this.sendButton instanceof HTMLButtonElement&&(this.sendButton.disabled=t)}scrollToBottom(){if(!this.messagesContainer)return;const e=this.messagesContainer,t=()=>{const t=e.scrollHeight,n=e.clientHeight;e.scrollTop=t>n?t:999999};t(),requestAnimationFrame(()=>{t(),requestAnimationFrame(t)}),setTimeout(t,10),setTimeout(t,50),setTimeout(t,100),setTimeout(t,200),setTimeout(t,500)}show(){this.element&&(this.element.style.display="flex",this.scrollToBottom(),this.inputElement&&this.inputElement.focus())}hide(){this.element&&(this.element.style.display="none"),this.streamingCleanupInterval&&(clearInterval(this.streamingCleanupInterval),this.streamingCleanupInterval=null)}destroy(){this.eventCleanup.forEach(e=>e()),this.eventCleanup=[],this.streamingCleanupInterval&&(clearInterval(this.streamingCleanupInterval),this.streamingCleanupInterval=null),this.element&&(this.element.remove(),this.element=null,this.messagesContainer=null,this.inputElement=null,this.sendButton=null,this.closeButton=null,this.statusIndicator=null)}}function p(){const e=document.documentElement,t=e.getAttribute("data-theme");return"light"===t||"dark"===t?t:e.classList.contains("dark")?"dark":"light"}function g(e){const t=new MutationObserver(()=>{e(p())});t.observe(document.documentElement,{attributes:!0,attributeFilter:["data-theme","class"]});const n=window.matchMedia("(prefers-color-scheme: dark)"),i=()=>{e(p())};return n.addEventListener("change",i),e(p()),()=>{t.disconnect(),n.removeEventListener("change",i)}}function w(e="#6366f1",t){const n="wabbit-chat-styles",i=document.getElementById(n);i&&i.remove();const a="dark"===("auto"!==t&&t?t:p())?{"--wabbit-bg-base":"#1A1816","--wabbit-bg-elevated":"#2D2826","--wabbit-bg-hover":"#3D3935","--wabbit-text-primary":"#F5F1ED","--wabbit-text-secondary":"#A39C94","--wabbit-text-muted":"#6B6560","--wabbit-border-subtle":"#3D3935","--wabbit-primary":e}:{"--wabbit-bg-base":"#FAF8F6","--wabbit-bg-elevated":"#FFFFFF","--wabbit-bg-hover":"#F2F0EE","--wabbit-text-primary":"#2D2826","--wabbit-text-secondary":"#5A5450","--wabbit-text-muted":"#8A847E","--wabbit-border-subtle":"#E6E3E0","--wabbit-primary":e},s=`\n :root {\n ${Object.entries(a).map(([e,t])=>`${e}: ${t};`).join("\n ")}\n }\n\n /* Chat Bubble */\n .wabbit-chat-bubble {\n position: fixed;\n z-index: 9999;\n width: 60px;\n height: 60px;\n border-radius: 50%;\n background: linear-gradient(135deg, var(--wabbit-primary), ${f(e,.2)});\n border: none;\n cursor: pointer;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15), 0 2px 4px rgba(0, 0, 0, 0.1);\n display: flex;\n align-items: center;\n justify-content: center;\n transition: transform 0.2s ease, box-shadow 0.2s ease;\n color: white;\n font-size: 24px;\n }\n\n .wabbit-chat-bubble:hover {\n transform: scale(1.1);\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2), 0 4px 8px rgba(0, 0, 0, 0.15);\n }\n\n .wabbit-chat-bubble:active {\n transform: scale(0.95);\n }\n\n .wabbit-chat-bubble.bottom-right {\n bottom: 20px;\n right: 20px;\n }\n\n .wabbit-chat-bubble.bottom-left {\n bottom: 20px;\n left: 20px;\n }\n\n /* Chat Panel */\n .wabbit-chat-panel {\n position: fixed;\n z-index: 9998;\n width: 380px;\n max-width: calc(100vw - 40px);\n height: 600px;\n max-height: calc(100vh - 100px);\n background: var(--wabbit-bg-elevated);\n border: 1px solid var(--wabbit-border-subtle);\n border-radius: 16px;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif;\n font-size: 14px;\n line-height: 1.5;\n color: var(--wabbit-text-primary);\n }\n\n .wabbit-chat-panel.bottom-right {\n bottom: 90px;\n right: 20px;\n }\n\n .wabbit-chat-panel.bottom-left {\n bottom: 90px;\n left: 20px;\n }\n\n @media (max-width: 480px) {\n .wabbit-chat-panel {\n width: calc(100vw - 20px);\n height: calc(100vh - 20px);\n max-height: calc(100vh - 20px);\n bottom: 10px !important;\n left: 10px !important;\n right: 10px !important;\n border-radius: 12px;\n }\n }\n\n /* Panel Header */\n .wabbit-chat-panel-header {\n background: rgba(var(--wabbit-primary-rgb, 99, 102, 241), 0.1);\n border-bottom: 1px solid var(--wabbit-border-subtle);\n padding: 16px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n\n .wabbit-chat-panel-header-title {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .wabbit-chat-panel-header-icon {\n width: 32px;\n height: 32px;\n border-radius: 50%;\n background: linear-gradient(135deg, var(--wabbit-primary), ${f(e,.2)});\n display: flex;\n align-items: center;\n justify-content: center;\n color: white;\n font-size: 16px;\n font-weight: 600;\n }\n\n .wabbit-chat-panel-header-text h3 {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--wabbit-text-primary);\n }\n\n .wabbit-chat-panel-header-text p {\n margin: 0;\n font-size: 12px;\n color: var(--wabbit-text-muted);\n }\n\n .wabbit-chat-panel-close {\n background: none;\n border: none;\n cursor: pointer;\n color: var(--wabbit-text-secondary);\n font-size: 24px;\n line-height: 1;\n padding: 4px;\n transition: color 0.2s;\n }\n\n .wabbit-chat-panel-close:hover {\n color: var(--wabbit-text-primary);\n }\n\n /* Messages Area */\n .wabbit-chat-messages {\n flex: 1;\n overflow-y: auto;\n padding: 16px;\n display: flex;\n flex-direction: column;\n gap: 16px;\n }\n\n .wabbit-chat-message {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n .wabbit-chat-message-user {\n align-items: flex-end;\n }\n\n .wabbit-chat-message-assistant {\n align-items: flex-start;\n }\n\n .wabbit-chat-message-system {\n align-items: center;\n }\n\n .wabbit-chat-message-bubble {\n max-width: 85%;\n padding: 12px 16px;\n border-radius: 16px;\n word-wrap: break-word;\n white-space: pre-wrap;\n }\n\n .wabbit-chat-message-user .wabbit-chat-message-bubble {\n background: linear-gradient(135deg, var(--wabbit-primary), ${f(e,.2)});\n color: white;\n border-bottom-right-radius: 4px;\n }\n\n .wabbit-chat-message-assistant .wabbit-chat-message-bubble {\n background: var(--wabbit-bg-hover);\n color: var(--wabbit-text-primary);\n border: 1px solid var(--wabbit-border-subtle);\n border-bottom-left-radius: 4px;\n }\n\n .wabbit-chat-message-system .wabbit-chat-message-bubble {\n background: rgba(var(--wabbit-primary-rgb, 99, 102, 241), 0.1);\n border: 1px solid rgba(var(--wabbit-primary-rgb, 99, 102, 241), 0.3);\n color: var(--wabbit-primary);\n text-align: center;\n font-size: 12px;\n padding: 8px 12px;\n }\n\n .wabbit-chat-message-system-error .wabbit-chat-message-bubble {\n background: rgba(220, 38, 38, 0.1);\n border: 1px solid rgba(220, 38, 38, 0.4);\n color: #b91c1c;\n text-align: left;\n font-size: 13px;\n padding: 10px 14px;\n }\n\n .wabbit-chat-message-content {\n font-size: 14px;\n line-height: 1.5;\n }\n\n .wabbit-chat-message-content a {\n color: var(--wabbit-primary);\n text-decoration: underline;\n text-underline-offset: 2px;\n }\n\n .wabbit-chat-message-content a:hover {\n opacity: 0.8;\n }\n\n .wabbit-chat-message-user .wabbit-chat-message-content a {\n color: inherit;\n }\n\n /* Typing Indicator */\n .wabbit-chat-typing {\n display: flex;\n gap: 4px;\n padding: 12px 16px;\n }\n\n .wabbit-chat-typing-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--wabbit-text-muted);\n animation: wabbit-typing 1.4s infinite;\n }\n\n .wabbit-chat-typing-dot:nth-child(2) {\n animation-delay: 0.2s;\n }\n\n .wabbit-chat-typing-dot:nth-child(3) {\n animation-delay: 0.4s;\n }\n\n @keyframes wabbit-typing {\n 0%, 60%, 100% {\n transform: translateY(0);\n opacity: 0.7;\n }\n 30% {\n transform: translateY(-10px);\n opacity: 1;\n }\n }\n\n /* Input Area */\n .wabbit-chat-input-area {\n border-top: 1px solid var(--wabbit-border-subtle);\n padding: 12px;\n background: var(--wabbit-bg-elevated);\n }\n\n .wabbit-chat-input-wrapper {\n display: flex;\n gap: 8px;\n align-items: flex-end;\n }\n\n .wabbit-chat-input {\n flex: 1;\n resize: none;\n background: var(--wabbit-bg-hover);\n border: 1px solid var(--wabbit-border-subtle);\n border-radius: 12px;\n padding: 10px 12px;\n color: var(--wabbit-text-primary);\n font-size: 14px;\n font-family: inherit;\n line-height: 1.5;\n min-height: 44px;\n max-height: 120px;\n transition: border-color 0.2s, box-shadow 0.2s;\n }\n\n .wabbit-chat-input:focus {\n outline: none;\n border-color: var(--wabbit-primary);\n box-shadow: 0 0 0 3px rgba(var(--wabbit-primary-rgb, 99, 102, 241), 0.1);\n }\n\n .wabbit-chat-input::placeholder {\n color: var(--wabbit-text-muted);\n }\n\n .wabbit-chat-input:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .wabbit-chat-send-button {\n width: 44px;\n height: 44px;\n border-radius: 12px;\n background: linear-gradient(135deg, var(--wabbit-primary), ${f(e,.2)});\n border: none;\n color: white;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: opacity 0.2s, transform 0.2s;\n flex-shrink: 0;\n }\n\n .wabbit-chat-send-button:hover:not(:disabled) {\n opacity: 0.9;\n transform: scale(1.05);\n }\n\n .wabbit-chat-send-button:active:not(:disabled) {\n transform: scale(0.95);\n }\n\n .wabbit-chat-send-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .wabbit-chat-send-icon {\n width: 20px;\n height: 20px;\n }\n\n /* Connection Status Indicator */\n .wabbit-chat-status-indicator {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex-shrink: 0;\n align-self: center;\n transition: background-color 0.3s ease;\n }\n\n .wabbit-chat-status-indicator[data-status="connected"] {\n background-color: #22c55e;\n }\n\n .wabbit-chat-status-indicator[data-status="disconnected"] {\n background-color: #ef4444;\n }\n\n .wabbit-chat-status-indicator[data-status="connecting"],\n .wabbit-chat-status-indicator[data-status="reconnecting"] {\n background-color: #f59e0b;\n animation: wabbit-status-pulse 1.5s ease-in-out infinite;\n }\n\n @keyframes wabbit-status-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.3; }\n }\n\n /* Scrollbar */\n .wabbit-chat-messages::-webkit-scrollbar {\n width: 6px;\n }\n\n .wabbit-chat-messages::-webkit-scrollbar-track {\n background: transparent;\n }\n\n .wabbit-chat-messages::-webkit-scrollbar-thumb {\n background: var(--wabbit-border-subtle);\n border-radius: 3px;\n }\n\n .wabbit-chat-messages::-webkit-scrollbar-thumb:hover {\n background: var(--wabbit-text-muted);\n }\n\n /* Animations */\n @keyframes wabbit-fade-in {\n from {\n opacity: 0;\n transform: translateY(10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n /* Streaming Cursor */\n .wabbit-chat-cursor {\n display: inline-block;\n color: var(--wabbit-primary);\n font-weight: bold;\n animation: wabbit-cursor-blink 1s infinite;\n margin-left: 2px;\n }\n\n @keyframes wabbit-cursor-blink {\n 0%, 50% {\n opacity: 1;\n }\n 51%, 100% {\n opacity: 0;\n }\n }\n\n .wabbit-chat-panel {\n animation: wabbit-fade-in 0.3s ease-out;\n }\n\n .wabbit-chat-message {\n animation: wabbit-fade-in 0.2s ease-out;\n }\n\n /* ========================================\n * INLINE MODE STYLES\n * ======================================== */\n\n /* Inline Chat Panel - renders inside container instead of fixed position */\n .wabbit-chat-panel.wabbit-chat-panel-inline {\n position: absolute !important;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n width: 100%;\n height: 100%;\n max-height: none;\n max-width: none;\n border-radius: 0;\n box-shadow: none;\n animation: none; /* Disable slide-in animation for inline */\n }\n\n /* Inline mode messages area fills available space */\n .wabbit-chat-panel-inline .wabbit-chat-messages {\n flex: 1 1 0; /* flex-grow, flex-shrink, flex-basis: 0 for proper sizing */\n min-height: 0; /* Critical for nested flex scroll to work */\n overflow-y: auto !important;\n }\n\n /* Full-height variant for inline mode (used in standalone chat pages) */\n .wabbit-chat-panel-inline.wabbit-chat-panel-fullheight {\n height: 100vh;\n max-height: 100vh;\n border-radius: 0;\n }\n\n /* Mobile responsive for inline mode */\n @media (max-width: 480px) {\n .wabbit-chat-panel.wabbit-chat-panel-inline {\n border-radius: 0;\n min-height: 300px;\n }\n }\n `,r=document.createElement("style");r.id=n,r.setAttribute("data-wabbit","true"),r.textContent=s,document.head.appendChild(r)}function f(e,t){const n=e.replace("#",""),i=parseInt(n.substr(0,2),16),a=parseInt(n.substr(2,2),16),s=parseInt(n.substr(4,2),16),r=Math.max(0,Math.min(255,Math.round(i+(255-i)*t))),o=Math.max(0,Math.min(255,Math.round(a+(255-a)*t))),l=Math.max(0,Math.min(255,Math.round(s+(255-s)*t)));return`#${r.toString(16).padStart(2,"0")}${o.toString(16).padStart(2,"0")}${l.toString(16).padStart(2,"0")}`}class y{constructor(e,t){this.wsClient=null,this.bubble=null,this.panel=null,this.isOpen=!1,this.cleanup=[],this.themeCleanup=null,this.onChatReadyCallback=null,this.chatReadyFired=!1,this.config=e,this.storage=t,this.onChatReadyCallback=e.onChatReady||null}async init(){await new Promise(e=>{h(()=>{e()})});const e="inline"===this.config.mode;let t=null;if(e&&(t=this.resolveContainer(),!t))return void console.error(`[Wabbit] Container not found: ${this.config.container}`);w(this.config.primaryColor||"#6366f1",this.config.theme),this.setupThemeWatcher();const n=this.getWebSocketUrl();this.wsClient=new c(this.config.apiKey||"",n,null,this.storage),this.setupWebSocketHandlers(),e||(this.bubble=new m({position:this.config.position||"bottom-right",onClick:()=>this.toggle()})),this.panel=new u({position:this.config.position||"bottom-right",welcomeMessage:this.config.welcomeMessage,placeholder:this.config.placeholder,onSendMessage:e=>this.handleSendMessage(e),onClose:()=>this.close(),disabled:!0,mode:this.config.mode||"widget",container:t||void 0,showHeader:this.config.showHeader,headerTitle:this.config.headerTitle}),this.bubble&&this.bubble.render(),this.panel.render(),e?this.isOpen=!0:(this.panel&&this.panel.hide(),this.bubble&&this.bubble.show(),this.isOpen=!1,this.handleTriggerType()),await this.wsClient.connect()}resolveContainer(){if(!this.config.container)return null;const e=this.config.container,t=e.startsWith("#")?e.slice(1):e;return document.getElementById(t)||document.querySelector(e)}setupWebSocketHandlers(){this.wsClient&&(this.wsClient.onWelcome=(e,t,n)=>{console.log("[Wabbit] Chat connected:",e),this.panel&&(this.panel.setDisabled(!1),n&&this.panel.addSystemMessage(n)),!this.chatReadyFired&&this.onChatReadyCallback&&(this.chatReadyFired=!0,requestAnimationFrame(()=>{console.log("[Wabbit] Chat ready, firing onChatReady callback"),this.onChatReadyCallback&&this.onChatReadyCallback()}))},this.wsClient.onMessageHistory=e=>{console.log("[Wabbit] Loaded message history:",e.length),this.panel&&this.panel.setMessages(e)},this.wsClient.onMessageStart=e=>{console.log("[Wabbit] Streaming message started:",e),this.panel&&this.panel.startStreamingMessage(e)},this.wsClient.onMessageChunk=(e,t)=>{this.panel&&this.panel.appendToStreamingMessage(e,t)},this.wsClient.onMessageEnd=(e,t)=>{console.log("[Wabbit] Streaming message ended:",e),this.panel&&(this.panel.finishStreamingMessage(e,t),this.panel.setWaitingForResponse(!1))},this.wsClient.onMessage=e=>{this.panel&&(this.panel.addMessage(e),this.panel.setWaitingForResponse(!1))},this.wsClient.onError=e=>{const t="string"==typeof e?e:e?.message??"An error occurred";console.error("[Wabbit] Chat error:",t),this.panel&&(this.panel.cancelAllStreamingMessages(),this.panel.setWaitingForResponse(!1))},this.wsClient.onStatusChange=e=>{this.panel&&(this.panel.setDisabled("connected"!==e),this.panel.setConnectionStatus(e))},this.wsClient.onDisconnect=()=>{this.panel&&(this.panel.cancelAllStreamingMessages(),this.panel.setDisabled(!0))},this.wsClient.onQueueOverflow=e=>{console.warn("[Wabbit] Queue overflow:",e)})}handleTriggerType(){switch(this.config.triggerType||"button"){case"auto":const e=this.config.triggerDelay||5e3;setTimeout(()=>{this.open()},e);break;case"scroll":let t=!1;const n=()=>{t||(t=!0,this.open(),window.removeEventListener("scroll",n))};window.addEventListener("scroll",n),this.cleanup.push(()=>{window.removeEventListener("scroll",n)})}}getWebSocketUrl(){if(this.config.wsUrl)return this.config.wsUrl;const e=this.config.apiUrl||"https://platform.insourcedata.ai";return`${e.startsWith("https")?"wss":"ws"}://${e.replace(/^https?:\/\//,"").replace(/\/$/,"")}/ws/chat`}handleSendMessage(e){if(!this.wsClient)return;const t={id:`user-${Date.now()}`,role:"user",content:e,timestamp:new Date};this.panel&&(this.panel.addMessage(t),this.panel.setWaitingForResponse(!0)),this.wsClient.sendMessage(e),this.config.onUserMessage&&this.config.onUserMessage(e)}open(){"inline"!==this.config.mode&&(this.isOpen||(this.isOpen=!0,this.panel&&this.panel.show(),this.bubble&&this.bubble.hide()))}close(){"inline"!==this.config.mode&&this.isOpen&&(this.isOpen=!1,this.panel&&this.panel.hide(),this.bubble&&this.bubble.show())}toggle(){"inline"!==this.config.mode&&(this.isOpen?this.close():this.open())}sendMessage(e){this.handleSendMessage(e)}setupThemeWatcher(){"auto"===(this.config.theme||"auto")&&(this.themeCleanup=g(()=>{w(this.config.primaryColor||"#6366f1","auto")}))}destroy(){this.cleanup.forEach(e=>e()),this.cleanup=[],this.themeCleanup&&(this.themeCleanup(),this.themeCleanup=null),this.wsClient&&(this.wsClient.disconnect(),this.wsClient=null),this.bubble&&(this.bubble.destroy(),this.bubble=null),this.panel&&(this.panel.destroy(),this.panel=null)}}var C=Object.freeze({__proto__:null,ChatWidget:y});class v{constructor(e){this.styles=e}render(e,t){const n=this.styles.getColors(t),i=e.fields;let a='<form class="wabbit-form" data-wabbit-form-instance="true">';return i.forEach(e=>{a+=this.renderField(e,n)}),a+='<button\n type="submit"\n class="wabbit-submit"\n >Submit</button>',a+='<div class="wabbit-message" style="display: none;"></div>',a+="</form>",a}renderField(e,t){let n='<div class="wabbit-field">';const i=["text","email","number","textarea","select"].includes(e.type)?`wabbit-input-${e.id}`:void 0;switch(n+=`<label${i?` for="${d(i)}"`:""} style="display: block; font-weight: 600; margin-bottom: 0.5rem; color: ${t.labelColor} !important; font-size: 0.875rem;">${d(e.label)}${e.required?' <span style="color: #E07A5F;">*</span>':""}</label>`,e.type){case"text":case"email":case"number":i&&(n+=this.renderTextInput(e,t,i));break;case"textarea":i&&(n+=this.renderTextarea(e,t,i));break;case"select":i&&(n+=this.renderSelect(e,t,i));break;case"radio":n+=this.renderRadio(e,t);break;case"checkbox":n+=this.renderCheckbox(e,t);break;case"rating":n+=this.renderRating(e,t)}return n+="</div>",n}renderTextInput(e,t,n){return`<input\n type="${e.type}"\n id="${d(n)}"\n name="${d(e.id)}"\n placeholder="${e.placeholder?d(e.placeholder):""}"\n ${e.required?"required":""}\n />`}renderTextarea(e,t,n){return`<textarea\n id="${d(n)}"\n name="${d(e.id)}"\n placeholder="${e.placeholder?d(e.placeholder):""}"\n ${e.required?"required":""}\n rows="4"\n ></textarea>`}renderSelect(e,t,n){if(!e.options||0===e.options.length)return"";let i=`<select id="${d(n)}" name="${d(e.id)}" ${e.required?"required":""}>`;return i+='<option value="">Select...</option>',e.options.forEach(e=>{i+=`<option value="${d(e)}">${d(e)}</option>`}),i+="</select>",i}renderRadio(e,t){if(!e.options||0===e.options.length)return"";let n='<div class="wabbit-radio-group">';return e.options.forEach((t,i)=>{const a=`${e.id}_${i}`;n+=`<div>\n <label>\n <input\n type="radio"\n name="${d(e.id)}"\n value="${d(t)}"\n id="${d(a)}"\n ${e.required&&0===i?"required":""}\n />\n <span>${d(t)}</span>\n </label>\n </div>`}),n+="</div>",n}renderCheckbox(e,t){if(!e.options||0===e.options.length)return"";let n='<div class="wabbit-checkbox-group">';return e.options.forEach((t,i)=>{const a=`${e.id}_${i}`;n+=`<div>\n <label>\n <input\n type="checkbox"\n name="${d(e.id)}[]"\n value="${d(t)}"\n id="${d(a)}"\n />\n <span>${d(t)}</span>\n </label>\n </div>`}),n+="</div>",n}renderRating(e,t){const n=e.max||5;let i='<div class="wabbit-rating">';for(let t=1;t<=n;t++){const n=`${e.id}_${t}`;i+=`<input\n type="radio"\n name="${d(e.id)}"\n value="${t}"\n id="${d(n)}"\n ${e.required&&1===t?"required":""}\n />`,i+=`<label for="${d(n)}">★</label>`}return i+="</div>",i}}class x{constructor(e,t){if(this.styleElement=null,this.currentTheme="light",this._primaryColor="#C15F3C",this.themes={dark:{formBg:"#1A1816",labelColor:"#F5F1ED",inputBg:"#2D2826",inputText:"#F5F1ED",inputBorder:"#3D3935",inputBorderFocus:"#C15F3C",placeholderColor:"#6B6560",radioColor:"#A39C94",ratingInactive:"#3D3935",errorBg:"rgba(224, 122, 95, 0.1)",errorColor:"#E07A5F",errorBorder:"#E07A5F",successBg:"rgba(132, 169, 140, 0.1)",successColor:"#84A98C",successBorder:"#84A98C",buttonBg:"#C15F3C",buttonBgHover:"#AB4E2F"},light:{formBg:"#FAF8F6",labelColor:"#2D2826",inputBg:"#FFFFFF",inputText:"#2D2826",inputBorder:"#D4CFCA",inputBorderFocus:"#C15F3C",placeholderColor:"#8A847E",radioColor:"#5A5450",ratingInactive:"#D4CFCA",errorBg:"rgba(193, 84, 56, 0.1)",errorColor:"#C15438",errorBorder:"#C15438",successBg:"rgba(92, 122, 99, 0.1)",successColor:"#5C7A63",successBorder:"#5C7A63",buttonBg:"#C15F3C",buttonBgHover:"#AB4E2F"}},this.styleId=t?`wabbit-form-styles-${t.replace(/[^a-zA-Z0-9]/g,"-")}`:`wabbit-form-styles-${Math.random().toString(36).substr(2,9)}`,e){this._primaryColor=e,this.themes.dark.inputBorderFocus=e,this.themes.light.inputBorderFocus=e,this.themes.dark.buttonBg=e,this.themes.light.buttonBg=e;const t=this.darkenColor(e,10);this.themes.dark.buttonBgHover=t,this.themes.light.buttonBgHover=t}this.injectStyles()}getColors(e){const t=e||this.currentTheme;return this.themes[t]}updateTheme(e){this.currentTheme=e,this.injectStyles()}updatePrimaryColor(e){this._primaryColor=e,this.themes.dark.inputBorderFocus=e,this.themes.light.inputBorderFocus=e,this.themes.dark.buttonBg=e,this.themes.light.buttonBg=e;const t=this.darkenColor(e,10);this.themes.dark.buttonBgHover=t,this.themes.light.buttonBgHover=t,this.injectStyles()}getPrimaryColor(){return this._primaryColor}darkenColor(e,t){if(e.startsWith("#")){const n=parseInt(e.slice(1),16);return`#${(Math.max(0,Math.floor((n>>16)*(1-t/100)))<<16|Math.max(0,Math.floor((n>>8&255)*(1-t/100)))<<8|Math.max(0,Math.floor((255&n)*(1-t/100)))).toString(16).padStart(6,"0")}`}return e}injectStyles(){this.styleElement=document.getElementById(this.styleId),this.styleElement||(this.styleElement=document.createElement("style"),this.styleElement.id=this.styleId,this.styleElement.setAttribute("data-wabbit","form-styles"),document.head.appendChild(this.styleElement));const e=this.getColors(),t=this.generateCSS(e);this.styleElement.textContent=t}generateCSS(e){return`\n /* Wabbit Form Styles */\n .wabbit-form {\n max-width: 600px;\n margin: 0 auto;\n font-family: Inter, system-ui, sans-serif;\n background-color: ${e.formBg};\n padding: 1.5rem;\n border-radius: 0.5rem;\n }\n\n .wabbit-field {\n margin-bottom: 1.5rem;\n }\n\n /* Label color is set via inline style in FormRenderer for theme-aware rendering */\n /* This ensures the correct theme color is applied at render time */\n\n .wabbit-field input[type="text"],\n .wabbit-field input[type="email"],\n .wabbit-field input[type="number"],\n .wabbit-field textarea,\n .wabbit-field select {\n width: 100%;\n padding: 0.75rem 1rem;\n border: 1px solid ${e.inputBorder};\n border-radius: 0.5rem;\n font-size: 1rem;\n background-color: ${e.inputBg};\n color: ${e.inputText};\n transition: border-color 0.2s, box-shadow 0.2s;\n box-sizing: border-box;\n }\n\n .wabbit-field input[type="text"]:focus,\n .wabbit-field input[type="email"]:focus,\n .wabbit-field input[type="number"]:focus,\n .wabbit-field textarea:focus,\n .wabbit-field select:focus {\n outline: none;\n border-color: var(--wabbit-primary-color);\n box-shadow: 0 0 0 3px rgba(193, 95, 60, 0.1);\n }\n\n .wabbit-field input::placeholder,\n .wabbit-field textarea::placeholder {\n color: ${e.placeholderColor};\n opacity: 1;\n }\n\n .wabbit-field textarea {\n resize: vertical;\n }\n\n .wabbit-field .wabbit-radio-group,\n .wabbit-field .wabbit-checkbox-group {\n margin-bottom: 0.5rem;\n }\n\n .wabbit-field .wabbit-radio-group label,\n .wabbit-field .wabbit-checkbox-group label {\n display: flex;\n align-items: center;\n cursor: pointer;\n color: ${e.radioColor};\n margin-bottom: 0.5rem;\n }\n\n .wabbit-field input[type="radio"],\n .wabbit-field input[type="checkbox"] {\n margin-right: 0.5rem;\n accent-color: var(--wabbit-primary-color);\n }\n\n .wabbit-rating {\n display: flex;\n gap: 0.25rem;\n }\n\n .wabbit-rating input[type="radio"] {\n display: none;\n }\n\n .wabbit-rating label {\n cursor: pointer;\n font-size: 1.5rem;\n color: ${e.ratingInactive};\n transition: color 0.2s;\n }\n\n .wabbit-rating label:hover {\n color: var(--wabbit-primary-color);\n }\n\n .wabbit-rating input[type="radio"]:checked ~ label,\n .wabbit-rating label:has(+ input[type="radio"]:checked) {\n color: var(--wabbit-primary-color);\n }\n\n .wabbit-submit {\n width: 100%;\n padding: 0.75rem 1.5rem;\n background-color: var(--wabbit-primary-color);\n color: white;\n border: none;\n border-radius: 0.5rem;\n font-size: 1rem;\n font-weight: 600;\n cursor: pointer;\n transition: background-color 0.2s, transform 0.15s;\n }\n\n .wabbit-submit:hover:not(:disabled) {\n background-color: var(--wabbit-primary-color-hover);\n transform: translateY(-1px);\n }\n\n .wabbit-submit:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .wabbit-message {\n display: none;\n margin-top: 1rem;\n padding: 0.75rem;\n border-radius: 0.5rem;\n }\n\n .wabbit-message.wabbit-message-success {\n background-color: ${e.successBg};\n color: ${e.successColor};\n border: 1px solid ${e.successBorder};\n }\n\n .wabbit-message.wabbit-message-error {\n background-color: ${e.errorBg};\n color: ${e.errorColor};\n border: 1px solid ${e.errorBorder};\n }\n `}destroy(){this.styleElement&&(this.styleElement.remove(),this.styleElement=null)}}class E{constructor(e){this.baseUrl=e.baseUrl.replace(/\/$/,""),this.apiKey=e.apiKey||"",this.timeout=e.timeout||3e4}async get(e,t){return this.request("GET",e,void 0,t)}async post(e,t,n){return this.request("POST",e,t,n)}async request(e,t,n,i){const a=`${this.baseUrl}${t}`,s={"Content-Type":"application/json"};!1!==i?.requireAuth&&this.apiKey&&(s["X-API-Key"]=this.apiKey);const r=new AbortController,o=setTimeout(()=>r.abort(),this.timeout),l={method:e,headers:s,mode:"cors",signal:r.signal};n&&"GET"!==e&&(l.body=JSON.stringify(n));try{const e=await fetch(a,l);if(clearTimeout(o),!e.ok){let t;try{const n=await e.json();t=n.detail?n.detail:n.error?n.error:n.message?n.message:JSON.stringify(n)}catch{const n=await e.text();t=n||(e.statusText||"An error occurred")}return{success:!1,error:t}}return{success:!0,data:await e.json()}}catch(e){return clearTimeout(o),e instanceof Error&&"AbortError"===e.name?{success:!1,error:`Request timeout after ${this.timeout}ms`}:e instanceof TypeError&&e.message.includes("CORS")?{success:!1,error:"CORS error: Please check server configuration"}:{success:!1,error:e instanceof Error?e.message:"Unknown error"}}}}class S{constructor(e){this.container=null,this.formElement=null,this.currentTheme="light",this.themeCleanup=null,this.formObserver=null,this.config=e;const t=e.apiUrl||this.getApiUrlFromScript();this.apiClient=new E({baseUrl:t,apiKey:e.apiKey}),this.styles=new x(e.primaryColor,e.containerId),this.renderer=new v(this.styles)}init(){h(()=>{const e=this.config.theme||"auto";this.currentTheme="auto"===e?p():e,this.styles.updateTheme(this.currentTheme),this.setupThemeWatcher(),this.loadForm()})}async loadForm(){try{if(this.container=this.findContainer(),!this.container)throw new Error(`Container not found: ${this.config.containerId||"data-wabbit-form-id"}`);const e=await this.apiClient.get(`/api/forms/${this.config.formId}/definition`,{requireAuth:!1});if(!e.success||!e.data)throw new Error(e.error||"Failed to load form definition");const t=e.data,n=this.renderer.render(t,this.currentTheme);this.container.innerHTML=n;const i=this.config.primaryColor||"#C15F3C";this.container.style.setProperty("--wabbit-primary-color",i);const a=this.calculateHoverColor(i);if(this.container.style.setProperty("--wabbit-primary-color-hover",a),console.log(`[Wabbit] Form in container "${this.config.containerId}" using primary color: ${i}`),this.formElement=this.container.querySelector("form"),!this.formElement)throw new Error("Form element not found after rendering");this.attachSubmitHandler(t.settings.successMessage),this.setupRatingInteractions(),this.config.onLoad&&this.config.onLoad()}catch(e){console.error("[Wabbit] Form load error:",e),this.showError(e instanceof Error?e.message:"Failed to load form"),this.config.onError&&this.config.onError(e instanceof Error?e:new Error(String(e)))}}findContainer(){if(this.config.containerId){const e=document.getElementById(this.config.containerId)||document.querySelector(this.config.containerId);if(e)return e}const e=document.querySelector(`[data-wabbit-form-id="${this.config.formId}"]`);return e||null}attachSubmitHandler(e){this.formElement&&this.formElement.addEventListener("submit",async t=>{t.preventDefault(),await this.handleSubmit(e)})}setupRatingInteractions(){if(!this.formElement)return;this.formElement.querySelectorAll('.wabbit-rating input[type="radio"]').forEach(e=>{const t=this.formElement.querySelector(`label[for="${e.id}"]`);if(!t)return;const n=e;n.addEventListener("change",()=>{this.updateRatingStars(n)}),t.addEventListener("mouseenter",()=>{const e=this.styles.getColors(this.currentTheme);t.style.color=e.inputBorderFocus}),t.addEventListener("mouseleave",()=>{this.updateRatingStars(n)})})}updateRatingStars(e){if(!this.formElement)return;const t=e.name,n=this.styles.getColors(this.currentTheme),i=this.formElement.querySelectorAll(`input[name="${t}"]`),a=this.formElement.querySelectorAll(`label[for^="${t}_"]`);i.forEach(e=>{const t=Array.from(a).find(t=>t.getAttribute("for")===e.id);t&&(e.checked?t.style.color=n.inputBorderFocus:t.style.color=n.ratingInactive)})}async handleSubmit(e){if(!this.formElement)return;const t=this.formElement.querySelector(".wabbit-submit"),n=this.formElement.querySelector(".wabbit-message");if(t&&n){t.disabled=!0,t.textContent="Submitting...",n.style.display="none",n.className="wabbit-message";try{const i=new FormData(this.formElement),a={};for(const[e,t]of i.entries())if(e.endsWith("[]")){const n=e.replace("[]","");a[n]||(a[n]=[]),a[n].push(t)}else a[e]=t;const s=await this.apiClient.post(`/api/forms/${this.config.formId}/submit`,{responses:a,metadata:{referrer:document.referrer,userAgent:navigator.userAgent}},{requireAuth:!1});if(s.success&&s.data?.success){const i=s.data.message||e||"Thank you! Your submission has been received.";this.showSuccess(i,n),this.formElement.reset(),this.config.onSubmit&&this.config.onSubmit(a,s.data.submissionId),setTimeout(()=>{t.disabled=!1,t.textContent="Submit"},2e3)}else{const e=s.data?.error||s.error||"Failed to submit form",i=this.formatErrorMessage(e);this.showError(i,n),t.disabled=!1,t.textContent="Submit",this.config.onError&&this.config.onError(new Error(e))}}catch(e){console.error("[Wabbit] Submission error:",e);const i=e instanceof Error?e.message:"Network error. Please try again.",a=this.formatErrorMessage(i);this.showError(a,n),t.disabled=!1,t.textContent="Submit",this.config.onError&&this.config.onError(e instanceof Error?e:new Error(String(e)))}}}formatErrorMessage(e){if(!e)return"An error occurred. Please try again.";let t=e.replace(/^HTTP\s+\d+\s*/i,"").replace(/^\d+\s+[A-Z\s]+:\s*/i,"").replace(/\b\d{3}\s+[A-Z\s]+\b/gi,"").trim();return t&&0!==t.length?(t=t.replace(/^(Error|Failed|Error:)\s*/i,""),t):"An error occurred. Please try again."}showSuccess(e,t){const n=t||this.formElement?.querySelector(".wabbit-message");n&&(n.textContent=e,n.className="wabbit-message wabbit-message-success",n.style.display="block")}showError(e,t){if(this.container&&!t){const t=this.styles.getColors(this.currentTheme);return void(this.container.innerHTML=`<p style="color: ${t.errorColor}; padding: 1rem; border: 1px solid ${t.errorBorder}; border-radius: 0.5rem; background-color: ${t.errorBg};">${d(e)}</p>`)}const n=t||this.formElement?.querySelector(".wabbit-message");n&&(n.textContent=e,n.className="wabbit-message wabbit-message-error",n.style.display="block")}setupThemeWatcher(){const e=this.config.theme||"auto";"auto"===e?this.themeCleanup=g(e=>{this.currentTheme=e,this.updateFormTheme()}):(this.currentTheme=e,this.styles.updateTheme(e))}updateFormTheme(){if(this.styles.updateTheme(this.currentTheme),this.formElement){this.formElement.querySelectorAll('.wabbit-rating input[type="radio"]').forEach(e=>{e.checked&&this.updateRatingStars(e)})}}calculateHoverColor(e){if(e.startsWith("#")){const t=parseInt(e.slice(1),16);return`#${(Math.max(0,Math.floor(.9*(t>>16)))<<16|Math.max(0,Math.floor(.9*(t>>8&255)))<<8|Math.max(0,Math.floor(.9*(255&t)))).toString(16).padStart(6,"0")}`}return e}getApiUrlFromScript(){const e=document.currentScript||document.querySelector('script[src*="embed"]');if(e&&e instanceof HTMLScriptElement&&e.src)try{return new URL(e.src).origin}catch{}return"https://platform.insourcedata.ai"}destroy(){this.themeCleanup&&(this.themeCleanup(),this.themeCleanup=null),this.formObserver&&(this.formObserver.disconnect(),this.formObserver=null),this.styles&&this.styles.destroy(),this.container&&(this.container.innerHTML=""),this.container=null,this.formElement=null}}var k=Object.freeze({__proto__:null,FormWidget:S});class M{constructor(e){this.overlay=null,this.modal=null,this.isOpen=!1,this.config=e}show(e,t){this.isOpen||(this.onSubmitCallback=e,this.onDismissCallback=t,this.isOpen=!0,this.render())}hide(){this.isOpen&&(this.isOpen=!1,this.destroy())}render(){const e="dark"===p();this.overlay=document.createElement("div"),this.overlay.className="wabbit-email-capture-overlay",this.overlay.addEventListener("click",e=>{e.target===this.overlay&&this.handleDismiss()}),this.modal=document.createElement("div"),this.modal.className="wabbit-email-capture-modal "+(e?"dark":"");const t=(this.config.fields||["email"]).map(e=>this.renderField(e)).join("");this.modal.innerHTML=`\n <button class="wabbit-email-capture-close ${e?"dark":""}" aria-label="Close">\n <svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">\n <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />\n </svg>\n </button>\n <div class="wabbit-email-capture-content">\n <h2 class="wabbit-email-capture-title">\n ${this.config.title||"Save this conversation?"}\n </h2>\n <p class="wabbit-email-capture-description">\n ${this.config.description||"Enter your email to continue this chat later"}\n </p>\n <form class="wabbit-email-capture-form">\n ${t}\n <div class="wabbit-email-capture-checkbox">\n <input type="checkbox" id="wabbit-dont-show-again" />\n <label for="wabbit-dont-show-again">Don't show this again</label>\n </div>\n <div class="wabbit-email-capture-buttons">\n <button type="submit" class="wabbit-email-capture-button wabbit-email-capture-button-primary">\n Save Conversation\n </button>\n <button type="button" class="wabbit-email-capture-button wabbit-email-capture-button-secondary">\n Maybe Later\n </button>\n </div>\n </form>\n </div>\n `,this.attachEventListeners(),this.overlay.appendChild(this.modal),document.body.appendChild(this.overlay)}renderField(e){const t={email:{label:"Email",type:"email",placeholder:"your@email.com",required:!0},name:{label:"Name",type:"text",placeholder:"Your name",required:!1},company:{label:"Company",type:"text",placeholder:"Your company",required:!1}}[e];return`\n <div class="wabbit-email-capture-field">\n <label class="wabbit-email-capture-label" for="wabbit-field-${e}">\n ${t.label}\n </label>\n <input\n type="${t.type}"\n id="wabbit-field-${e}"\n name="${e}"\n placeholder="${t.placeholder}"\n class="wabbit-email-capture-input"\n ${t.required?"required":""}\n />\n <div class="wabbit-email-capture-error" id="wabbit-error-${e}" style="display: none;"></div>\n </div>\n `}attachEventListeners(){if(!this.modal)return;const e=this.modal.querySelector(".wabbit-email-capture-close");e&&e.addEventListener("click",()=>this.handleDismiss());const t=this.modal.querySelector("form");t&&t.addEventListener("submit",e=>this.handleSubmit(e));const n=this.modal.querySelector(".wabbit-email-capture-button-secondary");n&&n.addEventListener("click",()=>this.handleDismiss())}async handleSubmit(e){if(e.preventDefault(),!this.modal||!this.onSubmitCallback)return;const t=this.modal.querySelector("form");if(!t)return;const n=new FormData(t),i={email:n.get("email")||"",name:n.get("name")||void 0,company:n.get("company")||void 0};if(!i.email||!this.isValidEmail(i.email))return void this.showError("email","Please enter a valid email address");const a=t.querySelector("#wabbit-dont-show-again")?.checked||!1;this.setFormDisabled(!0);try{await this.onSubmitCallback(i,a),this.hide()}catch(e){this.showError("email",e instanceof Error?e.message:"Failed to save email"),this.setFormDisabled(!1)}}handleDismiss(){this.onDismissCallback&&this.onDismissCallback(),this.hide()}setFormDisabled(e){if(!this.modal)return;this.modal.querySelectorAll("input, button").forEach(t=>{t.style.opacity=e?"0.5":"1",t.style.pointerEvents=e?"none":"auto"});const t=this.modal.querySelector(".wabbit-email-capture-button-primary");t&&(t.disabled=e,t.textContent=e?"Saving...":"Save Conversation")}showError(e,t){if(!this.modal)return;const n=this.modal.querySelector(`#wabbit-error-${e}`);n&&(n.textContent=t,n.style.display="block")}isValidEmail(e){return/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e)}destroy(){this.overlay&&(this.overlay.remove(),this.overlay=null),this.modal=null,this.isOpen=!1}}class ${constructor(e){this.messageCount=0,this.emailCaptured=!1,this.wsClient=null,this.config=e,this.modal=new M(e),this.onCaptureCallback=e.onCapture}init(){!function(e="#6366f1"){const t="wabbit-email-capture-styles",n=document.getElementById(t);n&&n.remove();const i=document.createElement("style");i.id=t,i.setAttribute("data-wabbit","email-capture-styles"),i.textContent=`\n /* Email Capture Modal Styles */\n .wabbit-email-capture-overlay {\n position: fixed;\n inset: 0;\n z-index: 9999;\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n animation: wabbit-fade-in 0.2s ease-out;\n padding: 1rem;\n box-sizing: border-box;\n overflow-y: auto;\n }\n\n .wabbit-email-capture-modal {\n position: relative;\n background: white;\n border-radius: 0.5rem;\n box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);\n padding: 1.5rem;\n width: calc(100% - 2rem);\n max-width: 28rem;\n margin: 1rem;\n animation: wabbit-slide-up 0.3s ease-out;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .wabbit-email-capture-modal.dark {\n background: #1f2937;\n color: #f9fafb;\n }\n\n .wabbit-email-capture-close {\n position: absolute;\n top: 1rem;\n right: 1rem;\n background: none;\n border: none;\n color: #6b7280;\n cursor: pointer;\n padding: 0.25rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 0.25rem;\n transition: all 0.2s;\n }\n\n .wabbit-email-capture-close:hover {\n color: #374151;\n background-color: #f3f4f6;\n }\n\n .wabbit-email-capture-close.dark:hover {\n color: #d1d5db;\n background-color: #374151;\n }\n\n .wabbit-email-capture-close:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .wabbit-email-capture-title {\n font-size: 1.25rem;\n font-weight: 600;\n color: #111827;\n margin-bottom: 0.5rem;\n }\n\n .wabbit-email-capture-modal.dark .wabbit-email-capture-title {\n color: #f9fafb;\n }\n\n .wabbit-email-capture-description {\n font-size: 0.875rem;\n color: #6b7280;\n margin-bottom: 1.5rem;\n }\n\n .wabbit-email-capture-modal.dark .wabbit-email-capture-description {\n color: #d1d5db;\n }\n\n .wabbit-email-capture-form {\n display: flex;\n flex-direction: column;\n gap: 1rem;\n width: 100%;\n box-sizing: border-box;\n }\n\n .wabbit-email-capture-field {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n width: 100%;\n box-sizing: border-box;\n }\n\n .wabbit-email-capture-label {\n font-size: 0.875rem;\n font-weight: 500;\n color: #374151;\n }\n\n .wabbit-email-capture-modal.dark .wabbit-email-capture-label {\n color: #d1d5db;\n }\n\n .wabbit-email-capture-input {\n width: 100%;\n padding: 0.75rem;\n border: 1px solid #d1d5db;\n border-radius: 0.375rem;\n font-size: 0.875rem;\n transition: all 0.2s;\n background: white;\n color: #111827;\n box-sizing: border-box;\n }\n\n .wabbit-email-capture-modal.dark .wabbit-email-capture-input {\n background: #374151;\n border-color: #4b5563;\n color: #f9fafb;\n }\n\n .wabbit-email-capture-input:focus {\n outline: none;\n border-color: ${e};\n box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);\n }\n\n .wabbit-email-capture-input:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .wabbit-email-capture-error {\n font-size: 0.875rem;\n color: #dc2626;\n margin-top: 0.25rem;\n }\n\n .wabbit-email-capture-checkbox {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n }\n\n .wabbit-email-capture-checkbox input[type="checkbox"] {\n width: 1rem;\n height: 1rem;\n accent-color: ${e};\n cursor: pointer;\n }\n\n .wabbit-email-capture-checkbox label {\n font-size: 0.875rem;\n color: #6b7280;\n cursor: pointer;\n }\n\n .wabbit-email-capture-modal.dark .wabbit-email-capture-checkbox label {\n color: #d1d5db;\n }\n\n .wabbit-email-capture-buttons {\n display: flex;\n gap: 0.75rem;\n margin-top: 0.5rem;\n width: 100%;\n box-sizing: border-box;\n }\n\n .wabbit-email-capture-button {\n flex: 1;\n padding: 0.75rem 1.5rem;\n border: none;\n border-radius: 0.375rem;\n font-size: 0.875rem;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s;\n box-sizing: border-box;\n min-width: 0;\n }\n\n .wabbit-email-capture-button-primary {\n background-color: ${e};\n color: white;\n }\n\n .wabbit-email-capture-button-primary:hover:not(:disabled) {\n opacity: 0.9;\n transform: translateY(-1px);\n }\n\n .wabbit-email-capture-button-primary:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .wabbit-email-capture-button-secondary {\n background-color: white;\n color: #374151;\n border: 1px solid #d1d5db;\n }\n\n .wabbit-email-capture-modal.dark .wabbit-email-capture-button-secondary {\n background-color: #374151;\n color: #d1d5db;\n border-color: #4b5563;\n }\n\n .wabbit-email-capture-button-secondary:hover:not(:disabled) {\n background-color: #f9fafb;\n }\n\n .wabbit-email-capture-modal.dark .wabbit-email-capture-button-secondary:hover:not(:disabled) {\n background-color: #4b5563;\n }\n\n @keyframes wabbit-fade-in {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n }\n\n @keyframes wabbit-slide-up {\n from {\n opacity: 0;\n transform: translateY(1rem);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n `,document.head.appendChild(i)}("#6366f1");if("true"===r("wabbit_email_capture_dismissed"))return void console.log("[Wabbit] Email capture dismissed by user");return"true"===r("wabbit_email_captured")?(this.emailCaptured=!0,void console.log("[Wabbit] Email already captured")):void 0}setWebSocketClient(e){this.wsClient=e}handleMessage(){if(this.emailCaptured)return;this.messageCount++;const e=this.config.triggerAfterMessages||3;this.messageCount>=e&&this.showModal()}showModal(){this.emailCaptured||this.modal.show((e,t)=>this.handleEmailSubmit(e,t),()=>this.handleDismiss())}async handleEmailSubmit(e,t){if(t&&o("wabbit_email_capture_dismissed","true"),this.wsClient&&this.wsClient.ws&&this.wsClient.ws.readyState===WebSocket.OPEN)return new Promise((t,n)=>{const i=setTimeout(()=>{n(new Error("Email capture timeout"))},5e3),a=s=>{try{const r=JSON.parse(s.data);"email_confirmed"===r.type&&(clearTimeout(i),this.wsClient.ws.removeEventListener("message",a),r.success?(this.emailCaptured=!0,o("wabbit_email_captured","true"),this.onCaptureCallback&&this.onCaptureCallback({email:e.email,name:e.name||"",company:e.company||""}),t()):n(new Error(r.message||"Failed to save email")))}catch(e){}};this.wsClient.ws.addEventListener("message",a),this.wsClient.ws.send(JSON.stringify({type:"provide_email",email:e.email,name:e.name,company:e.company}))});this.emailCaptured=!0,o("wabbit_email_captured","true"),this.onCaptureCallback&&this.onCaptureCallback({email:e.email,name:e.name||"",company:e.company||""})}handleDismiss(){}destroy(){this.modal&&this.modal.destroy(),this.wsClient=null}}var I=Object.freeze({__proto__:null,EmailCaptureWidget:$});function W(e){return l.init(e)}function F(){return l.getInstance()}function T(){return l.destroy()}function A(e,t){return l.getChatPageUrl(e,t)}function B(e,t){return l.openChatPage(e,t)}const _={init:W,getInstance:F,destroy:T,getChatPageUrl:A,openChatPage:B};function L(){if(l.getInstance())return;const e=document.querySelectorAll("[data-wabbit-form-id]");if(0===e.length)return;console.log("[Wabbit] Auto-initializing",e.length,"form(s)");const t=(()=>{const e=document.currentScript||document.querySelector('script[src*="wabbit"]')||document.querySelector('script[src*="embed"]');if(e&&e instanceof HTMLScriptElement&&e.src)try{return new URL(e.src).origin}catch{}return"https://platform.insourcedata.ai"})();e.forEach(e=>{const n=e.getAttribute("data-wabbit-form-id");n&&(e.__wabbitFormWidget||Promise.resolve().then(function(){return k}).then(({FormWidget:i})=>{try{const a=new i({enabled:!0,formId:n,apiUrl:t,theme:"auto"});a.findContainer=()=>e,a.init(),e.__wabbitFormWidget=a,console.log("[Wabbit] Form auto-initialized:",n)}catch(e){console.error("[Wabbit] Failed to auto-initialize form:",n,e)}}))})}if("undefined"!=typeof document){"loading"===document.readyState?document.addEventListener("DOMContentLoaded",L):L();const e=new MutationObserver(e=>{for(const t of e)if(t.addedNodes.length){if(Array.from(t.addedNodes).some(e=>{if(1!==e.nodeType)return!1;const t=e;return t.hasAttribute?.("data-wabbit-form-id")||t.querySelector?.("[data-wabbit-form-id]")})){console.log("[Wabbit] Detected dynamically added form container"),L();break}}});"loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{e.observe(document.body,{childList:!0,subtree:!0})}):e.observe(document.body,{childList:!0,subtree:!0})}e.ApiClient=E,e.ChatBubble=m,e.ChatPanel=u,e.ChatWebSocketClient=c,e.ChatWidget=y,e.EmailCaptureModal=M,e.EmailCaptureWidget=$,e.EventEmitter=class{constructor(){this.events=new Map}on(e,t){this.events.has(e)||this.events.set(e,[]),this.events.get(e).push(t)}off(e,t){const n=this.events.get(e);if(n){const e=n.indexOf(t);e>-1&&n.splice(e,1)}}emit(e,...t){const n=this.events.get(e);n&&n.forEach(n=>{try{n(...t)}catch(t){console.error(`[Wabbit] Error in event handler for "${e}":`,t)}})}removeAllListeners(e){e?this.events.delete(e):this.events.clear()}listenerCount(e){return this.events.get(e)?.length||0}},e.FormRenderer=v,e.FormStyles=x,e.FormWidget=S,e.SafeStorage=a,e.Wabbit=l,e.createElement=b,e.default=_,e.destroy=T,e.detectTheme=p,e.escapeHtml=d,e.getChatPageUrl=A,e.getInstance=F,e.init=W,e.mergeConfig=n,e.onDOMReady=h,e.openChatPage=B,e.storage=s,e.validateConfig=t,e.watchTheme=g,Object.defineProperty(e,"__esModule",{value:!0})});
2
2
  //# sourceMappingURL=wabbit-embed.umd.min.js.map