@cognitiondesk/widget 1.2.1 → 1.2.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,4 +1,4 @@
1
- (function(l,f){typeof exports=="object"&&typeof module<"u"?f(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],f):(l=typeof globalThis<"u"?globalThis:l||self,f(l.CognitionDeskWidgetReact={},l.jsxRuntime,l.React))})(this,function(l,f,g){"use strict";const H="https://mounaji-backendv3.onrender.com",T=`
1
+ (function(h,m){typeof exports=="object"&&typeof module<"u"?m(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],m):(h=typeof globalThis<"u"?globalThis:h||self,m(h.CognitionDeskWidgetReact={},h.jsxRuntime,h.React))})(this,function(h,m,g){"use strict";const H="https://mounaji-backendv3.onrender.com",T=`
2
2
  :host {
3
3
  all: initial;
4
4
  font-family: system-ui, sans-serif;
@@ -244,7 +244,7 @@
244
244
  font-size: 16px;
245
245
  }
246
246
  }
247
- `,v={chat:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>',close:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>',send:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>'};function S(){return"cd_"+Math.random().toString(36).slice(2)+Date.now().toString(36)}let k=class{constructor(t={}){var e,a,s,r,n;if(!t.apiKey)throw new Error("[CognitionDesk] apiKey is required");this._cfg={apiKey:t.apiKey,widgetId:t.widgetId||null,assistantId:t.assistantId||null,backendUrl:t.backendUrl||H,primaryColor:t.primaryColor||"#2563eb",theme:t.theme||"light",botName:t.botName||"AI Assistant",botEmoji:t.botEmoji||"🤖",welcomeMessage:t.welcomeMessage||"Hello! How can I help you today?",placeholder:t.placeholder||"Type a message…",position:t.position||"bottom-right",streaming:t.streaming!==!1,rateLimiting:{enabled:((e=t.rateLimiting)==null?void 0:e.enabled)!==!1,maxMessagesPerSession:((a=t.rateLimiting)==null?void 0:a.maxMessagesPerSession)??0,maxMessagesPerMinute:((s=t.rateLimiting)==null?void 0:s.maxMessagesPerMinute)??0,limitReachedMessage:((r=t.rateLimiting)==null?void 0:r.limitReachedMessage)||"You've reached the message limit for this session.",rateLimitMessage:((n=t.rateLimiting)==null?void 0:n.rateLimitMessage)||"You're sending messages too quickly. Please wait a moment."}},this._sessionId=S(),this._messageCount=0,this._minuteCount=0,this._minuteStart=Date.now(),this._messages=[],this._open=!1,this._loading=!1,this._container=null,this._shadow=null,this._panel=null,this._messagesEl=null,this._textarea=null,this._sendBtn=null,this._viewportHandler=null}mount(t){const e=()=>{const a=t||document.body;this._container=document.createElement("div"),this._container.setAttribute("data-cognitiondesk",""),a.appendChild(this._container),this._shadow=this._container.attachShadow({mode:"open"});const s=document.createElement("style");s.textContent=T,this._shadow.appendChild(s),this._buildDOM(),this._applyTheme(),this._syncViewportMetrics(),this._bindViewportMetrics(),this._bindEvents(),this._cfg.welcomeMessage&&this._appendMessage("assistant",this._cfg.welcomeMessage,!1)};return this._cfg.widgetId?this._fetchWidgetConfig().then(()=>e()).catch(()=>e()):(e(),Promise.resolve(this))}async _fetchWidgetConfig(){var e;const t=`${this._cfg.backendUrl}/platforms/web-widget/public/${this._cfg.widgetId}`;try{const a=await fetch(t);if(!a.ok)return;const{config:s}=await a.json();if(!s)return;s.botName&&!this._userSet("botName")&&(this._cfg.botName=s.botName),s.welcomeMessage&&!this._userSet("welcomeMessage")&&(this._cfg.welcomeMessage=s.welcomeMessage),s.primaryColor&&!this._userSet("primaryColor")&&(this._cfg.primaryColor=s.primaryColor),s.placeholder&&!this._userSet("placeholder")&&(this._cfg.placeholder=s.placeholder),s.assistantId&&!this._cfg.assistantId&&(this._cfg.assistantId=s.assistantId),(e=s.avatar)!=null&&e.value&&!this._userSet("botEmoji")&&(this._cfg.botEmoji=s.avatar.value),s.rateLimiting&&(this._cfg.rateLimiting={...this._cfg.rateLimiting,...s.rateLimiting})}catch{}}_userSet(t){return!1}unmount(){this._unbindViewportMetrics(),this._container&&(this._container.remove(),this._container=null)}open(){var t,e;this._open=!0,this._syncViewportMetrics(),(t=this._panel)==null||t.classList.add("open"),(e=this._textarea)==null||e.focus()}close(){var t;this._open=!1,(t=this._panel)==null||t.classList.remove("open")}toggle(){this._open?this.close():this.open()}clearHistory(){this._messages=[],this._messagesEl&&(this._messagesEl.innerHTML=""),this._cfg.welcomeMessage&&this._appendMessage("assistant",this._cfg.welcomeMessage)}_buildDOM(){const t=document.createElement("div");t.className="cd-root";const e=document.createElement("button");e.className="cd-widget-btn",e.innerHTML=v.chat,e.setAttribute("aria-label","Open chat"),e.style.setProperty("--cd-primary",this._cfg.primaryColor),this._toggleBtn=e;const a=document.createElement("div");a.className="cd-panel",this._panel=a;const s=document.createElement("div");s.className="cd-header",s.innerHTML=`
247
+ `,y={chat:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>',close:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>',send:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>'};function P(){return"cd_"+Math.random().toString(36).slice(2)+Date.now().toString(36)}let M=class{constructor(t={}){var e,i,s,a,n;if(!t.apiKey)throw new Error("[CognitionDesk] apiKey is required");this._overrides=new Set(t.overrideSettings?Object.keys(t.overrideSettings):[]),this._cfg={apiKey:t.apiKey,widgetId:t.widgetId||null,assistantId:t.assistantId||null,backendUrl:t.backendUrl||H,primaryColor:t.primaryColor||"#2563eb",theme:t.theme||"light",botName:t.botName||"AI Assistant",botEmoji:t.botEmoji||"🤖",welcomeMessage:t.welcomeMessage||"Hello! How can I help you today?",placeholder:t.placeholder||"Type a message…",position:t.position||"bottom-right",streaming:t.streaming!==!1,rateLimiting:{enabled:((e=t.rateLimiting)==null?void 0:e.enabled)!==!1,maxMessagesPerSession:((i=t.rateLimiting)==null?void 0:i.maxMessagesPerSession)??0,maxMessagesPerMinute:((s=t.rateLimiting)==null?void 0:s.maxMessagesPerMinute)??0,limitReachedMessage:((a=t.rateLimiting)==null?void 0:a.limitReachedMessage)||"You've reached the message limit for this session.",rateLimitMessage:((n=t.rateLimiting)==null?void 0:n.rateLimitMessage)||"You're sending messages too quickly. Please wait a moment."},overrideSettings:t.overrideSettings||null},t.overrideSettings&&this._applyOverrides(t.overrideSettings),this._sessionId=P(),this._messageCount=0,this._minuteCount=0,this._minuteStart=Date.now(),this._messages=[],this._open=!1,this._loading=!1,this._container=null,this._shadow=null,this._panel=null,this._messagesEl=null,this._textarea=null,this._sendBtn=null,this._viewportHandler=null,this._refreshPending=!1}mount(t){const e=()=>{const i=t||document.body;this._container=document.createElement("div"),this._container.setAttribute("data-cognitiondesk",""),i.appendChild(this._container),this._shadow=this._container.attachShadow({mode:"open"});const s=document.createElement("style");s.textContent=T,this._shadow.appendChild(s),this._buildDOM(),this._applyConfig(),this._syncViewportMetrics(),this._bindViewportMetrics(),this._bindEvents(),this._cfg.welcomeMessage&&this._appendMessage("assistant",this._cfg.welcomeMessage,!1)};return this._cfg.widgetId?this._fetchWidgetConfig().then(i=>{i!==!1&&e()}).catch(()=>e()):(e(),Promise.resolve(this))}updateSettings(t){if(!t||typeof t!="object")return;const e={...this._cfg.overrideSettings||{},...t};this._cfg.overrideSettings=e,this._overrides=new Set(Object.keys(e)),this._applyOverrides(e),this._applyConfig()}async refreshConfig(){if(!this._cfg.widgetId)return;await this._fetchWidgetConfig()===!1?this.unmount():this._applyConfig()}_userSet(t){return this._overrides.has(t)}async _fetchWidgetConfig(){var e;const t=`${this._cfg.backendUrl}/platforms/web-widget/public/${this._cfg.widgetId}`;try{const i=await fetch(t);if(i.status===404||i.status===403)return!1;if(!i.ok)return!0;const{config:s}=await i.json();if(!s)return;const a=(n,o)=>{o!=null&&!this._userSet(n)&&(this._cfg[n]=o)};return a("botName",s.botName),a("welcomeMessage",s.welcomeMessage),a("primaryColor",s.primaryColor),a("secondaryColor",s.secondaryColor),a("placeholder",s.placeholder),a("theme",s.theme),a("position",s.position),a("style",s.style),a("size",s.size),s.assistantId&&!this._cfg.assistantId&&(this._cfg.assistantId=s.assistantId),(e=s.avatar)!=null&&e.value&&!this._userSet("botEmoji")&&(this._cfg.botEmoji=s.avatar.value),s.rateLimiting&&(this._cfg.rateLimiting={...this._cfg.rateLimiting,...s.rateLimiting}),this._cfg.overrideSettings&&this._applyOverrides(this._cfg.overrideSettings),!0}catch{return!0}}_applyOverrides(t){for(const[e,i]of Object.entries(t))e==="rateLimiting"&&typeof i=="object"?this._cfg.rateLimiting={...this._cfg.rateLimiting,...i}:this._cfg[e]=i}_applyConfig(){var s,a,n,o,d,c,l;const t=this._cfg.theme==="dark"||this._cfg.theme==="auto"&&((s=window.matchMedia)==null?void 0:s.call(window,"(prefers-color-scheme: dark)").matches);(a=this._root)==null||a.classList.toggle("cd-dark",t),(n=this._root)==null||n.style.setProperty("--cd-primary",this._cfg.primaryColor),(o=this._panel)==null||o.style.setProperty("--cd-primary",this._cfg.primaryColor),(d=this._toggleBtn)==null||d.style.setProperty("--cd-primary",this._cfg.primaryColor);const e=(c=this._shadow)==null?void 0:c.querySelector(".cd-header-avatar");e&&(e.textContent=this._cfg.botEmoji);const i=(l=this._shadow)==null?void 0:l.querySelector(".cd-header-name");i&&(i.textContent=this._cfg.botName),this._textarea&&(this._textarea.placeholder=this._cfg.placeholder)}_silentRefresh(){!this._cfg.widgetId||this._refreshPending||(this._refreshPending=!0,this._fetchWidgetConfig().then(t=>{t===!1?this.unmount():this._applyConfig()}).catch(()=>{}).finally(()=>{this._refreshPending=!1}))}unmount(){this._unbindViewportMetrics(),this._container&&(this._container.remove(),this._container=null)}open(){var t,e;this._open=!0,this._syncViewportMetrics(),(t=this._panel)==null||t.classList.add("open"),(e=this._textarea)==null||e.focus(),this._silentRefresh()}close(){var t;this._open=!1,(t=this._panel)==null||t.classList.remove("open")}toggle(){this._open?this.close():this.open()}clearHistory(){this._messages=[],this._messagesEl&&(this._messagesEl.innerHTML=""),this._cfg.welcomeMessage&&this._appendMessage("assistant",this._cfg.welcomeMessage)}_buildDOM(){const t=document.createElement("div");t.className="cd-root";const e=document.createElement("button");e.className="cd-widget-btn",e.innerHTML=y.chat,e.setAttribute("aria-label","Open chat"),e.style.setProperty("--cd-primary",this._cfg.primaryColor),this._toggleBtn=e;const i=document.createElement("div");i.className="cd-panel",this._panel=i;const s=document.createElement("div");s.className="cd-header",s.innerHTML=`
248
248
  <div class="cd-header-avatar">${this._cfg.botEmoji}</div>
249
249
  <div class="cd-header-info">
250
250
  <div class="cd-header-name">${this._escHtml(this._cfg.botName)}</div>
@@ -252,5 +252,5 @@
252
252
  <span class="cd-status-dot"></span> Online
253
253
  </div>
254
254
  </div>
255
- `;const r=document.createElement("button");r.className="cd-close-btn",r.innerHTML=v.close,r.setAttribute("aria-label","Close chat"),s.appendChild(r),this._closeBtn=r;const n=document.createElement("div");n.className="cd-messages",n.setAttribute("role","log"),n.setAttribute("aria-live","polite"),this._messagesEl=n;const d=document.createElement("div");d.className="cd-input-area";const o=document.createElement("textarea");o.className="cd-textarea",o.rows=1,o.placeholder=this._cfg.placeholder,this._textarea=o;const c=document.createElement("button");c.className="cd-send-btn",c.innerHTML=v.send,c.setAttribute("aria-label","Send"),this._sendBtn=c,d.appendChild(o),d.appendChild(c);const p=document.createElement("div");p.className="cd-powered",p.innerHTML='Powered by <a href="https://cognitiondesk.com" target="_blank" rel="noopener">CognitionDesk</a>',a.appendChild(s),a.appendChild(n),a.appendChild(d),a.appendChild(p),t.appendChild(e),t.appendChild(a),this._shadow.appendChild(t),this._root=t}_applyTheme(){var e,a,s;(this._cfg.theme==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":this._cfg.theme)==="dark"&&((e=this._root)==null||e.classList.add("cd-dark")),(a=this._root)==null||a.style.setProperty("--cd-primary",this._cfg.primaryColor),(s=this._panel)==null||s.style.setProperty("--cd-primary",this._cfg.primaryColor)}_bindEvents(){this._toggleBtn.addEventListener("click",()=>this.toggle()),this._closeBtn.addEventListener("click",()=>this.close()),this._sendBtn.addEventListener("click",()=>this._sendMessage()),this._textarea.addEventListener("keydown",t=>{t.key==="Enter"&&!t.shiftKey&&(t.preventDefault(),this._sendMessage())}),this._textarea.addEventListener("input",()=>{this._textarea.style.height="auto",this._textarea.style.height=Math.min(this._textarea.scrollHeight,120)+"px"}),this._textarea.addEventListener("focus",()=>{this._syncViewportMetrics()})}_bindViewportMetrics(){this._viewportHandler||(this._viewportHandler=()=>this._syncViewportMetrics(),window.addEventListener("resize",this._viewportHandler,{passive:!0}),window.addEventListener("orientationchange",this._viewportHandler),window.visualViewport&&(window.visualViewport.addEventListener("resize",this._viewportHandler),window.visualViewport.addEventListener("scroll",this._viewportHandler)))}_unbindViewportMetrics(){this._viewportHandler&&(window.removeEventListener("resize",this._viewportHandler),window.removeEventListener("orientationchange",this._viewportHandler),window.visualViewport&&(window.visualViewport.removeEventListener("resize",this._viewportHandler),window.visualViewport.removeEventListener("scroll",this._viewportHandler)),this._viewportHandler=null)}_syncViewportMetrics(){const t=this._root||this._container;if(!t)return;const e=window.visualViewport,a=e?e.height:window.innerHeight;t.style.setProperty("--cd-viewport-height",`${Math.round(a)}px`)}async _sendMessage(){var a;const t=this._textarea.value.trim();if(!t||this._loading)return;const e=this._cfg.rateLimiting;if(e!=null&&e.enabled){const s=Date.now();if(s-this._minuteStart>=6e4&&(this._minuteCount=0,this._minuteStart=s),e.maxMessagesPerSession>0&&this._messageCount>=e.maxMessagesPerSession){this._appendMessage("error",e.limitReachedMessage,!1),this._textarea.disabled=!0,this._sendBtn.disabled=!0;return}if(e.maxMessagesPerMinute>0&&this._minuteCount>=e.maxMessagesPerMinute){this._appendMessage("error",e.rateLimitMessage,!1);return}}this._textarea.value="",this._textarea.style.height="auto",this._loading=!0,this._sendBtn.disabled=!0,this._messages.push({role:"user",content:t}),this._appendMessage("user",t,!1),this._messageCount+=1,this._minuteCount+=1;try{if(this._cfg.streaming)await this._callApiStream();else{const s=this._showTyping(),r=await this._callApi();this._removeTyping(s),this._messages.push({role:"assistant",content:r}),this._appendMessage("assistant",r,!0)}}catch(s){if(s.status===429||(a=s.message)!=null&&a.includes("429")){const r=s.rateLimitMessage||(e==null?void 0:e.rateLimitMessage)||"You're sending messages too quickly. Please wait a moment.";this._appendMessage("error",r,!1)}else this._appendMessage("error","Sorry, something went wrong. Please try again.",!1);console.error("[CognitionDesk]",s)}finally{this._loading=!1,(e==null?void 0:e.maxMessagesPerSession)>0&&this._messageCount>=e.maxMessagesPerSession||(this._sendBtn.disabled=!1),this._textarea.focus()}}_buildRequestBody(){return{messages:this._messages,assistantId:this._cfg.assistantId||void 0,userContext:{sessionId:this._sessionId,assistantId:this._cfg.assistantId||void 0,widgetId:this._cfg.widgetId||void 0,platform:"cognitiondesk-widget"}}}async _callApiStream(){const t=`${this._cfg.backendUrl}/chat-apiKeyAuth/stream`,e=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":this._cfg.apiKey},body:JSON.stringify({...this._buildRequestBody(),stream:!0})});if(!e.ok){const o=await e.json().catch(()=>({})),c=new Error(o.message||`HTTP ${e.status}`);throw c.status=e.status,c.rateLimitMessage=o.message,c}const a=document.createElement("div");a.className="cd-msg assistant",a.innerHTML='<div class="cd-typing"><span></span><span></span><span></span></div>',this._messagesEl.appendChild(a),this._scrollToBottom();const s=e.body.getReader(),r=new TextDecoder;let n="",d=!1;try{for(;;){const{done:o,value:c}=await s.read();if(o)break;const p=r.decode(c,{stream:!0});for(const u of p.split(`
256
- `)){if(!u.startsWith("data: "))continue;const _=u.slice(6).trim();if(_==="[DONE]")break;let m;try{m=JSON.parse(_)}catch{continue}m.type==="content"&&m.data&&(d||(a.innerHTML="",d=!0),n+=m.data,a.innerHTML=this._renderMarkdown(n),this._scrollToBottom())}}}finally{s.releaseLock()}d||(a.innerHTML=this._renderMarkdown("No response")),n&&this._messages.push({role:"assistant",content:n})}async _callApi(){var s,r,n;const t=`${this._cfg.backendUrl}/chat-apiKeyAuth/chat`,e=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":this._cfg.apiKey},body:JSON.stringify(this._buildRequestBody())});if(!e.ok){const d=await e.json().catch(()=>({})),o=new Error(d.message||`HTTP ${e.status}`);throw o.status=e.status,o.rateLimitMessage=d.message,o}const a=await e.json();return a.response||a.message||a.content||((n=(r=(s=a.choices)==null?void 0:s[0])==null?void 0:r.message)==null?void 0:n.content)||"No response"}_appendMessage(t,e,a=!0){const s=document.createElement("div");s.className=`cd-msg ${t}`,a&&t!=="user"?s.innerHTML=this._renderMarkdown(e):s.textContent=e,this._messagesEl.appendChild(s),this._scrollToBottom()}_renderMarkdown(t){if(!t)return"";let e=this._escHtml(t);return e=e.replace(/```[\w]*\n?([\s\S]*?)```/g,(a,s)=>`<pre style="background:#0f172a;color:#e2e8f0;padding:10px;border-radius:6px;font-size:12px;overflow-x:auto;margin:6px 0;white-space:pre-wrap">${s.trim()}</pre>`),e=e.replace(/`([^`]+)`/g,'<code style="background:rgba(0,0,0,.08);padding:1px 5px;border-radius:4px;font-size:.9em">$1</code>'),e=e.replace(/\*\*([^*]+)\*\*/g,"<strong>$1</strong>"),e=e.replace(/__([^_]+)__/g,"<strong>$1</strong>"),e=e.replace(/\*([^*]+)\*/g,"<em>$1</em>"),e=e.replace(/_([^_]+)_/g,"<em>$1</em>"),e=e.replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g,'<a href="$2" target="_blank" rel="noopener" style="color:var(--cd-primary,#2563eb)">$1</a>'),e=e.replace(/\n/g,"<br>"),e}_showTyping(){const t=document.createElement("div");return t.className="cd-msg assistant",t.innerHTML='<div class="cd-typing"><span></span><span></span><span></span></div>',this._messagesEl.appendChild(t),this._scrollToBottom(),t}_removeTyping(t){t==null||t.remove()}_scrollToBottom(){this._messagesEl&&(this._messagesEl.scrollTop=this._messagesEl.scrollHeight)}_escHtml(t){return String(t).replace(/[&<>"']/g,e=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[e])}};const N=g.forwardRef(function(t,e){const{apiKey:a,widgetId:s,assistantId:r,theme:n="light",primaryColor:d="#2563eb",botName:o="AI Assistant",botEmoji:c="🤖",welcomeMessage:p="Hello! How can I help you today?",placeholder:u="Type a message…",defaultOpen:_=!1,streaming:m=!0,backendUrl:M,maxMessagesPerSession:P=0,maxMessagesPerMinute:j=0,limitReachedMessage:C,rateLimitMessage:E,onOpen:b,onClose:x}=t,h=g.useRef(null),L=g.useRef(null);return g.useImperativeHandle(e,()=>({open:()=>{var i;return(i=h.current)==null?void 0:i.open()},close:()=>{var i;return(i=h.current)==null?void 0:i.close()},toggle:()=>{var i;return(i=h.current)==null?void 0:i.toggle()},clearHistory:()=>{var i;return(i=h.current)==null?void 0:i.clearHistory()}}),[]),g.useEffect(()=>{if(!a){console.error("[CognitionDesk] apiKey prop is required");return}const i=new k({apiKey:a,widgetId:s,assistantId:r,theme:n,primaryColor:d,botName:o,botEmoji:c,welcomeMessage:p,placeholder:u,streaming:m,...M?{backendUrl:M}:{},rateLimiting:{enabled:!0,maxMessagesPerSession:P,maxMessagesPerMinute:j,...C?{limitReachedMessage:C}:{},...E?{rateLimitMessage:E}:{}}});if(b||x){const w=i.open.bind(i),B=i.close.bind(i);i.open=(...y)=>{w(...y),b==null||b()},i.close=(...y)=>{B(...y),x==null||x()}}return Promise.resolve(i.mount(L.current)).then(()=>{h.current=i,_&&i.open()}),()=>{i.unmount(),h.current=null}},[a,s,r]),g.useEffect(()=>{var w;const i=h.current;i&&(i._cfg.theme=n,i._cfg.primaryColor=d,(w=i._applyTheme)==null||w.call(i))},[n,d]),f.jsx("div",{ref:L,"data-cognitiondesk-root":""})});l.CognitionDeskWidget=N,l.CognitionDeskWidgetCore=k,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})});
255
+ `;const a=document.createElement("button");a.className="cd-close-btn",a.innerHTML=y.close,a.setAttribute("aria-label","Close chat"),s.appendChild(a),this._closeBtn=a;const n=document.createElement("div");n.className="cd-messages",n.setAttribute("role","log"),n.setAttribute("aria-live","polite"),this._messagesEl=n;const o=document.createElement("div");o.className="cd-input-area";const d=document.createElement("textarea");d.className="cd-textarea",d.rows=1,d.placeholder=this._cfg.placeholder,this._textarea=d;const c=document.createElement("button");c.className="cd-send-btn",c.innerHTML=y.send,c.setAttribute("aria-label","Send"),this._sendBtn=c,o.appendChild(d),o.appendChild(c);const l=document.createElement("div");l.className="cd-powered",l.innerHTML='Powered by <a href="https://cognitiondesk.com" target="_blank" rel="noopener">CognitionDesk</a>',i.appendChild(s),i.appendChild(n),i.appendChild(o),i.appendChild(l),t.appendChild(e),t.appendChild(i),this._shadow.appendChild(t),this._root=t}_applyTheme(){var e,i,s;(this._cfg.theme==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":this._cfg.theme)==="dark"&&((e=this._root)==null||e.classList.add("cd-dark")),(i=this._root)==null||i.style.setProperty("--cd-primary",this._cfg.primaryColor),(s=this._panel)==null||s.style.setProperty("--cd-primary",this._cfg.primaryColor)}_bindEvents(){this._toggleBtn.addEventListener("click",()=>this.toggle()),this._closeBtn.addEventListener("click",()=>this.close()),this._sendBtn.addEventListener("click",()=>this._sendMessage()),this._textarea.addEventListener("keydown",t=>{t.key==="Enter"&&!t.shiftKey&&(t.preventDefault(),this._sendMessage())}),this._textarea.addEventListener("input",()=>{this._textarea.style.height="auto",this._textarea.style.height=Math.min(this._textarea.scrollHeight,120)+"px"}),this._textarea.addEventListener("focus",()=>{this._syncViewportMetrics()})}_bindViewportMetrics(){this._viewportHandler||(this._viewportHandler=()=>this._syncViewportMetrics(),window.addEventListener("resize",this._viewportHandler,{passive:!0}),window.addEventListener("orientationchange",this._viewportHandler),window.visualViewport&&(window.visualViewport.addEventListener("resize",this._viewportHandler),window.visualViewport.addEventListener("scroll",this._viewportHandler)))}_unbindViewportMetrics(){this._viewportHandler&&(window.removeEventListener("resize",this._viewportHandler),window.removeEventListener("orientationchange",this._viewportHandler),window.visualViewport&&(window.visualViewport.removeEventListener("resize",this._viewportHandler),window.visualViewport.removeEventListener("scroll",this._viewportHandler)),this._viewportHandler=null)}_syncViewportMetrics(){const t=this._root||this._container;if(!t)return;const e=window.visualViewport,i=e?e.height:window.innerHeight;t.style.setProperty("--cd-viewport-height",`${Math.round(i)}px`)}async _sendMessage(){var i;const t=this._textarea.value.trim();if(!t||this._loading)return;const e=this._cfg.rateLimiting;if(e!=null&&e.enabled){const s=Date.now();if(s-this._minuteStart>=6e4&&(this._minuteCount=0,this._minuteStart=s),e.maxMessagesPerSession>0&&this._messageCount>=e.maxMessagesPerSession){this._appendMessage("error",e.limitReachedMessage,!1),this._textarea.disabled=!0,this._sendBtn.disabled=!0;return}if(e.maxMessagesPerMinute>0&&this._minuteCount>=e.maxMessagesPerMinute){this._appendMessage("error",e.rateLimitMessage,!1);return}}this._textarea.value="",this._textarea.style.height="auto",this._loading=!0,this._sendBtn.disabled=!0,this._messages.push({role:"user",content:t}),this._appendMessage("user",t,!1),this._messageCount+=1,this._minuteCount+=1;try{if(this._cfg.streaming)await this._callApiStream();else{const s=this._showTyping(),a=await this._callApi();this._removeTyping(s),this._messages.push({role:"assistant",content:a}),this._appendMessage("assistant",a,!0)}}catch(s){if(s.status===429||(i=s.message)!=null&&i.includes("429")){const a=s.rateLimitMessage||(e==null?void 0:e.rateLimitMessage)||"You're sending messages too quickly. Please wait a moment.";this._appendMessage("error",a,!1)}else this._appendMessage("error","Sorry, something went wrong. Please try again.",!1);console.error("[CognitionDesk]",s)}finally{this._loading=!1,(e==null?void 0:e.maxMessagesPerSession)>0&&this._messageCount>=e.maxMessagesPerSession||(this._sendBtn.disabled=!1),this._textarea.focus()}}_buildRequestBody(){return{messages:this._messages,assistantId:this._cfg.assistantId||void 0,userContext:{sessionId:this._sessionId,assistantId:this._cfg.assistantId||void 0,widgetId:this._cfg.widgetId||void 0,platform:"cognitiondesk-widget"}}}async _callApiStream(){const t=`${this._cfg.backendUrl}/chat-apiKeyAuth/stream`,e=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":this._cfg.apiKey},body:JSON.stringify({...this._buildRequestBody(),stream:!0})});if(!e.ok){const d=await e.json().catch(()=>({})),c=new Error(d.message||`HTTP ${e.status}`);throw c.status=e.status,c.rateLimitMessage=d.message,c}const i=document.createElement("div");i.className="cd-msg assistant",i.innerHTML='<div class="cd-typing"><span></span><span></span><span></span></div>',this._messagesEl.appendChild(i),this._scrollToBottom();const s=e.body.getReader(),a=new TextDecoder;let n="",o=!1;try{for(;;){const{done:d,value:c}=await s.read();if(d)break;const l=a.decode(c,{stream:!0});for(const _ of l.split(`
256
+ `)){if(!_.startsWith("data: "))continue;const b=_.slice(6).trim();if(b==="[DONE]")break;let f;try{f=JSON.parse(b)}catch{continue}f.type==="content"&&f.data&&(o||(i.innerHTML="",o=!0),n+=f.data,i.innerHTML=this._renderMarkdown(n),this._scrollToBottom())}}}finally{s.releaseLock()}o||(i.innerHTML=this._renderMarkdown("No response")),n&&this._messages.push({role:"assistant",content:n})}async _callApi(){var s,a,n;const t=`${this._cfg.backendUrl}/chat-apiKeyAuth/chat`,e=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":this._cfg.apiKey},body:JSON.stringify(this._buildRequestBody())});if(!e.ok){const o=await e.json().catch(()=>({})),d=new Error(o.message||`HTTP ${e.status}`);throw d.status=e.status,d.rateLimitMessage=o.message,d}const i=await e.json();return i.response||i.message||i.content||((n=(a=(s=i.choices)==null?void 0:s[0])==null?void 0:a.message)==null?void 0:n.content)||"No response"}_appendMessage(t,e,i=!0){const s=document.createElement("div");s.className=`cd-msg ${t}`,i&&t!=="user"?s.innerHTML=this._renderMarkdown(e):s.textContent=e,this._messagesEl.appendChild(s),this._scrollToBottom()}_renderMarkdown(t){if(!t)return"";let e=this._escHtml(t);return e=e.replace(/```[\w]*\n?([\s\S]*?)```/g,(i,s)=>`<pre style="background:#0f172a;color:#e2e8f0;padding:10px;border-radius:6px;font-size:12px;overflow-x:auto;margin:6px 0;white-space:pre-wrap">${s.trim()}</pre>`),e=e.replace(/`([^`]+)`/g,'<code style="background:rgba(0,0,0,.08);padding:1px 5px;border-radius:4px;font-size:.9em">$1</code>'),e=e.replace(/\*\*([^*]+)\*\*/g,"<strong>$1</strong>"),e=e.replace(/__([^_]+)__/g,"<strong>$1</strong>"),e=e.replace(/\*([^*]+)\*/g,"<em>$1</em>"),e=e.replace(/_([^_]+)_/g,"<em>$1</em>"),e=e.replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g,'<a href="$2" target="_blank" rel="noopener" style="color:var(--cd-primary,#2563eb)">$1</a>'),e=e.replace(/\n/g,"<br>"),e}_showTyping(){const t=document.createElement("div");return t.className="cd-msg assistant",t.innerHTML='<div class="cd-typing"><span></span><span></span><span></span></div>',this._messagesEl.appendChild(t),this._scrollToBottom(),t}_removeTyping(t){t==null||t.remove()}_scrollToBottom(){this._messagesEl&&(this._messagesEl.scrollTop=this._messagesEl.scrollHeight)}_escHtml(t){return String(t).replace(/[&<>"']/g,e=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[e])}};const j=g.forwardRef(function(t,e){const{apiKey:i,widgetId:s,assistantId:a,theme:n="light",primaryColor:o="#2563eb",botName:d="AI Assistant",botEmoji:c="🤖",welcomeMessage:l="Hello! How can I help you today?",placeholder:_="Type a message…",defaultOpen:b=!1,streaming:f=!0,backendUrl:C,maxMessagesPerSession:N=0,maxMessagesPerMinute:z=0,limitReachedMessage:E,rateLimitMessage:L,onOpen:v,onClose:x,overrideSettings:u}=t,p=g.useRef(null),S=g.useRef(null);return g.useImperativeHandle(e,()=>({open:()=>{var r;return(r=p.current)==null?void 0:r.open()},close:()=>{var r;return(r=p.current)==null?void 0:r.close()},toggle:()=>{var r;return(r=p.current)==null?void 0:r.toggle()},clearHistory:()=>{var r;return(r=p.current)==null?void 0:r.clearHistory()}}),[]),g.useEffect(()=>{if(!i){console.error("[CognitionDesk] apiKey prop is required");return}const r=new M({apiKey:i,widgetId:s,assistantId:a,theme:n,primaryColor:o,botName:d,botEmoji:c,welcomeMessage:l,placeholder:_,streaming:f,...C?{backendUrl:C}:{},...u?{overrideSettings:u}:{},rateLimiting:{enabled:!0,maxMessagesPerSession:N,maxMessagesPerMinute:z,...E?{limitReachedMessage:E}:{},...L?{rateLimitMessage:L}:{}}});if(v||x){const w=r.open.bind(r),B=r.close.bind(r);r.open=(...k)=>{w(...k),v==null||v()},r.close=(...k)=>{B(...k),x==null||x()}}return Promise.resolve(r.mount(S.current)).then(()=>{p.current=r,b&&r.open()}),()=>{r.unmount(),p.current=null}},[i,s,a]),g.useEffect(()=>{var w;const r=p.current;r&&(r._cfg.theme=n,r._cfg.primaryColor=o,(w=r._applyConfig)==null||w.call(r))},[n,o]),g.useEffect(()=>{const r=p.current;!r||!u||r.updateSettings(u)},[u]),m.jsx("div",{ref:S,"data-cognitiondesk-root":""})});h.CognitionDeskWidget=j,h.CognitionDeskWidgetCore=M,Object.defineProperty(h,Symbol.toStringTag,{value:"Module"})});
package/dist/widget.es.js CHANGED
@@ -252,76 +252,173 @@ const u = "https://mounaji-backendv3.onrender.com", _ = `
252
252
  function b() {
253
253
  return "cd_" + Math.random().toString(36).slice(2) + Date.now().toString(36);
254
254
  }
255
- class f {
255
+ class m {
256
+ /**
257
+ * @param {Object} config
258
+ * @param {string} config.apiKey - Required. Your CognitionDesk API key.
259
+ * @param {string} [config.widgetId] - Widget ID from the dashboard. Enables remote config.
260
+ * @param {string} [config.assistantId] - Assistant ID (overrides server value if set).
261
+ * @param {string} [config.backendUrl] - Custom backend URL.
262
+ * @param {string} [config.theme] - 'light' | 'dark' | 'auto'
263
+ * @param {string} [config.primaryColor] - Hex color for buttons / header.
264
+ * @param {string} [config.botName] - Display name shown in the header.
265
+ * @param {string} [config.botEmoji] - Emoji shown as avatar.
266
+ * @param {string} [config.welcomeMessage] - First message shown to the user.
267
+ * @param {string} [config.placeholder] - Textarea placeholder text.
268
+ * @param {string} [config.position] - 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
269
+ * @param {boolean} [config.streaming] - Enable streaming responses (default: true).
270
+ * @param {Object} [config.overrideSettings]
271
+ * Explicit code-level overrides. Any key listed here takes precedence over
272
+ * the server-side dashboard config. Use this when you want to manage
273
+ * specific settings from code rather than the CognitionDesk dashboard.
274
+ *
275
+ * Example — let the dashboard control everything except color:
276
+ * overrideSettings: { primaryColor: '#e11d48' }
277
+ *
278
+ * Example — full code control (dashboard settings ignored):
279
+ * overrideSettings: {
280
+ * primaryColor: '#e11d48', botName: 'Aria', welcomeMessage: 'Hi!'
281
+ * }
282
+ *
283
+ * When omitted (default), ALL settings come from the dashboard.
284
+ */
256
285
  constructor(e = {}) {
257
286
  var t, a, s, i, r;
258
287
  if (!e.apiKey) throw new Error("[CognitionDesk] apiKey is required");
259
- this._cfg = {
288
+ this._overrides = new Set(
289
+ e.overrideSettings ? Object.keys(e.overrideSettings) : []
290
+ ), this._cfg = {
260
291
  apiKey: e.apiKey,
261
292
  widgetId: e.widgetId || null,
262
293
  assistantId: e.assistantId || null,
263
294
  backendUrl: e.backendUrl || u,
264
295
  primaryColor: e.primaryColor || "#2563eb",
265
296
  theme: e.theme || "light",
266
- // 'light' | 'dark' | 'auto'
267
297
  botName: e.botName || "AI Assistant",
268
298
  botEmoji: e.botEmoji || "🤖",
269
299
  welcomeMessage: e.welcomeMessage || "Hello! How can I help you today?",
270
300
  placeholder: e.placeholder || "Type a message…",
271
301
  position: e.position || "bottom-right",
272
302
  streaming: e.streaming !== !1,
273
- // default: true
274
- // Rate limiting — can be overridden by inline config or merged from server config
275
303
  rateLimiting: {
276
304
  enabled: ((t = e.rateLimiting) == null ? void 0 : t.enabled) !== !1,
277
305
  maxMessagesPerSession: ((a = e.rateLimiting) == null ? void 0 : a.maxMessagesPerSession) ?? 0,
278
306
  maxMessagesPerMinute: ((s = e.rateLimiting) == null ? void 0 : s.maxMessagesPerMinute) ?? 0,
279
307
  limitReachedMessage: ((i = e.rateLimiting) == null ? void 0 : i.limitReachedMessage) || "You've reached the message limit for this session.",
280
308
  rateLimitMessage: ((r = e.rateLimiting) == null ? void 0 : r.rateLimitMessage) || "You're sending messages too quickly. Please wait a moment."
281
- }
282
- }, this._sessionId = b(), this._messageCount = 0, this._minuteCount = 0, this._minuteStart = Date.now(), this._messages = [], this._open = !1, this._loading = !1, this._container = null, this._shadow = null, this._panel = null, this._messagesEl = null, this._textarea = null, this._sendBtn = null, this._viewportHandler = null;
309
+ },
310
+ // Store overrides for re-application after each config refresh
311
+ overrideSettings: e.overrideSettings || null
312
+ }, e.overrideSettings && this._applyOverrides(e.overrideSettings), this._sessionId = b(), this._messageCount = 0, this._minuteCount = 0, this._minuteStart = Date.now(), this._messages = [], this._open = !1, this._loading = !1, this._container = null, this._shadow = null, this._panel = null, this._messagesEl = null, this._textarea = null, this._sendBtn = null, this._viewportHandler = null, this._refreshPending = !1;
283
313
  }
284
314
  // ── Public API ──────────────────────────────────────────────────────────────
285
315
  /**
286
316
  * Mount the widget.
287
- * If `widgetId` was provided, fetches the server-side config first (async).
288
- * Returns a Promise so callers can await full initialisation.
317
+ * If `widgetId` is set, fetches server config first (async).
318
+ * Returns a Promise so callers can `await widget.mount()`.
289
319
  */
290
320
  mount(e) {
291
321
  const t = () => {
292
322
  const a = e || document.body;
293
323
  this._container = document.createElement("div"), this._container.setAttribute("data-cognitiondesk", ""), a.appendChild(this._container), this._shadow = this._container.attachShadow({ mode: "open" });
294
324
  const s = document.createElement("style");
295
- s.textContent = _, this._shadow.appendChild(s), this._buildDOM(), this._applyTheme(), this._syncViewportMetrics(), this._bindViewportMetrics(), this._bindEvents(), this._cfg.welcomeMessage && this._appendMessage("assistant", this._cfg.welcomeMessage, !1);
325
+ s.textContent = _, this._shadow.appendChild(s), this._buildDOM(), this._applyConfig(), this._syncViewportMetrics(), this._bindViewportMetrics(), this._bindEvents(), this._cfg.welcomeMessage && this._appendMessage("assistant", this._cfg.welcomeMessage, !1);
296
326
  };
297
- return this._cfg.widgetId ? this._fetchWidgetConfig().then(() => t()).catch(() => t()) : (t(), Promise.resolve(this));
327
+ return this._cfg.widgetId ? this._fetchWidgetConfig().then((a) => {
328
+ a !== !1 && t();
329
+ }).catch(() => t()) : (t(), Promise.resolve(this));
330
+ }
331
+ /**
332
+ * Programmatically update settings at runtime.
333
+ * Merges `newSettings` into `overrideSettings` and re-applies to the live DOM.
334
+ * Use this to change colors, bot name, etc. without remounting.
335
+ *
336
+ * widget.updateSettings({ primaryColor: '#dc2626', botName: 'Support Bot' });
337
+ */
338
+ updateSettings(e) {
339
+ if (!e || typeof e != "object") return;
340
+ const t = { ...this._cfg.overrideSettings || {}, ...e };
341
+ this._cfg.overrideSettings = t, this._overrides = new Set(Object.keys(t)), this._applyOverrides(t), this._applyConfig();
342
+ }
343
+ /**
344
+ * Fetch fresh config from the dashboard right now and apply it.
345
+ * Useful after the user changes settings in the CognitionDesk dashboard
346
+ * and wants them reflected immediately without reloading the page.
347
+ */
348
+ async refreshConfig() {
349
+ if (!this._cfg.widgetId) return;
350
+ await this._fetchWidgetConfig() === !1 ? this.unmount() : this._applyConfig();
351
+ }
352
+ // ── Config internals ────────────────────────────────────────────────────────
353
+ /**
354
+ * Returns true if `key` is explicitly controlled by `overrideSettings`.
355
+ * Those keys are immune to server config overwrites.
356
+ */
357
+ _userSet(e) {
358
+ return this._overrides.has(e);
298
359
  }
299
360
  /**
300
361
  * Fetch public widget config from the platform and merge into this._cfg.
301
- * Only fields not already overridden by the constructor are applied.
362
+ * Server values apply to any key NOT present in `overrideSettings`.
363
+ * After merging, overrides are re-applied so they always win.
302
364
  */
303
365
  async _fetchWidgetConfig() {
304
366
  var t;
305
367
  const e = `${this._cfg.backendUrl}/platforms/web-widget/public/${this._cfg.widgetId}`;
306
368
  try {
307
369
  const a = await fetch(e);
308
- if (!a.ok) return;
370
+ if (a.status === 404 || a.status === 403) return !1;
371
+ if (!a.ok) return !0;
309
372
  const { config: s } = await a.json();
310
373
  if (!s) return;
311
- s.botName && !this._userSet("botName") && (this._cfg.botName = s.botName), s.welcomeMessage && !this._userSet("welcomeMessage") && (this._cfg.welcomeMessage = s.welcomeMessage), s.primaryColor && !this._userSet("primaryColor") && (this._cfg.primaryColor = s.primaryColor), s.placeholder && !this._userSet("placeholder") && (this._cfg.placeholder = s.placeholder), s.assistantId && !this._cfg.assistantId && (this._cfg.assistantId = s.assistantId), (t = s.avatar) != null && t.value && !this._userSet("botEmoji") && (this._cfg.botEmoji = s.avatar.value), s.rateLimiting && (this._cfg.rateLimiting = { ...this._cfg.rateLimiting, ...s.rateLimiting });
374
+ const i = (r, n) => {
375
+ n != null && !this._userSet(r) && (this._cfg[r] = n);
376
+ };
377
+ return i("botName", s.botName), i("welcomeMessage", s.welcomeMessage), i("primaryColor", s.primaryColor), i("secondaryColor", s.secondaryColor), i("placeholder", s.placeholder), i("theme", s.theme), i("position", s.position), i("style", s.style), i("size", s.size), s.assistantId && !this._cfg.assistantId && (this._cfg.assistantId = s.assistantId), (t = s.avatar) != null && t.value && !this._userSet("botEmoji") && (this._cfg.botEmoji = s.avatar.value), s.rateLimiting && (this._cfg.rateLimiting = { ...this._cfg.rateLimiting, ...s.rateLimiting }), this._cfg.overrideSettings && this._applyOverrides(this._cfg.overrideSettings), !0;
312
378
  } catch {
379
+ return !0;
313
380
  }
314
381
  }
315
- // eslint-disable-next-line no-unused-vars
316
- _userSet(e) {
317
- return !1;
382
+ /**
383
+ * Write override values into this._cfg (flat fields only; rateLimiting is merged).
384
+ */
385
+ _applyOverrides(e) {
386
+ for (const [t, a] of Object.entries(e))
387
+ t === "rateLimiting" && typeof a == "object" ? this._cfg.rateLimiting = { ...this._cfg.rateLimiting, ...a } : this._cfg[t] = a;
388
+ }
389
+ /**
390
+ * Update live DOM elements to reflect the current this._cfg.
391
+ * Safe to call before or after mounting.
392
+ */
393
+ _applyConfig() {
394
+ var s, i, r, n, o, d, c;
395
+ const e = this._cfg.theme === "dark" || this._cfg.theme === "auto" && ((s = window.matchMedia) == null ? void 0 : s.call(window, "(prefers-color-scheme: dark)").matches);
396
+ (i = this._root) == null || i.classList.toggle("cd-dark", e), (r = this._root) == null || r.style.setProperty("--cd-primary", this._cfg.primaryColor), (n = this._panel) == null || n.style.setProperty("--cd-primary", this._cfg.primaryColor), (o = this._toggleBtn) == null || o.style.setProperty("--cd-primary", this._cfg.primaryColor);
397
+ const t = (d = this._shadow) == null ? void 0 : d.querySelector(".cd-header-avatar");
398
+ t && (t.textContent = this._cfg.botEmoji);
399
+ const a = (c = this._shadow) == null ? void 0 : c.querySelector(".cd-header-name");
400
+ a && (a.textContent = this._cfg.botName), this._textarea && (this._textarea.placeholder = this._cfg.placeholder);
401
+ }
402
+ /**
403
+ * Silently refresh config from server in the background.
404
+ * Called each time the panel opens so dashboard changes
405
+ * are reflected without reloading the page.
406
+ * Uses a debounce flag to avoid concurrent fetches.
407
+ */
408
+ _silentRefresh() {
409
+ !this._cfg.widgetId || this._refreshPending || (this._refreshPending = !0, this._fetchWidgetConfig().then((e) => {
410
+ e === !1 ? this.unmount() : this._applyConfig();
411
+ }).catch(() => {
412
+ }).finally(() => {
413
+ this._refreshPending = !1;
414
+ }));
318
415
  }
319
416
  unmount() {
320
417
  this._unbindViewportMetrics(), this._container && (this._container.remove(), this._container = null);
321
418
  }
322
419
  open() {
323
420
  var e, t;
324
- this._open = !0, this._syncViewportMetrics(), (e = this._panel) == null || e.classList.add("open"), (t = this._textarea) == null || t.focus();
421
+ this._open = !0, this._syncViewportMetrics(), (e = this._panel) == null || e.classList.add("open"), (t = this._textarea) == null || t.focus(), this._silentRefresh();
325
422
  }
326
423
  close() {
327
424
  var e;
@@ -355,14 +452,14 @@ class f {
355
452
  i.className = "cd-close-btn", i.innerHTML = p.close, i.setAttribute("aria-label", "Close chat"), s.appendChild(i), this._closeBtn = i;
356
453
  const r = document.createElement("div");
357
454
  r.className = "cd-messages", r.setAttribute("role", "log"), r.setAttribute("aria-live", "polite"), this._messagesEl = r;
358
- const o = document.createElement("div");
359
- o.className = "cd-input-area";
360
- const n = document.createElement("textarea");
361
- n.className = "cd-textarea", n.rows = 1, n.placeholder = this._cfg.placeholder, this._textarea = n;
455
+ const n = document.createElement("div");
456
+ n.className = "cd-input-area";
457
+ const o = document.createElement("textarea");
458
+ o.className = "cd-textarea", o.rows = 1, o.placeholder = this._cfg.placeholder, this._textarea = o;
362
459
  const d = document.createElement("button");
363
- d.className = "cd-send-btn", d.innerHTML = p.send, d.setAttribute("aria-label", "Send"), this._sendBtn = d, o.appendChild(n), o.appendChild(d);
364
- const l = document.createElement("div");
365
- l.className = "cd-powered", l.innerHTML = 'Powered by <a href="https://cognitiondesk.com" target="_blank" rel="noopener">CognitionDesk</a>', a.appendChild(s), a.appendChild(r), a.appendChild(o), a.appendChild(l), e.appendChild(t), e.appendChild(a), this._shadow.appendChild(e), this._root = e;
460
+ d.className = "cd-send-btn", d.innerHTML = p.send, d.setAttribute("aria-label", "Send"), this._sendBtn = d, n.appendChild(o), n.appendChild(d);
461
+ const c = document.createElement("div");
462
+ c.className = "cd-powered", c.innerHTML = 'Powered by <a href="https://cognitiondesk.com" target="_blank" rel="noopener">CognitionDesk</a>', a.appendChild(s), a.appendChild(r), a.appendChild(n), a.appendChild(c), e.appendChild(t), e.appendChild(a), this._shadow.appendChild(e), this._root = e;
366
463
  }
367
464
  _applyTheme() {
368
465
  var t, a, s;
@@ -446,36 +543,36 @@ class f {
446
543
  body: JSON.stringify({ ...this._buildRequestBody(), stream: !0 })
447
544
  });
448
545
  if (!t.ok) {
449
- const n = await t.json().catch(() => ({})), d = new Error(n.message || `HTTP ${t.status}`);
450
- throw d.status = t.status, d.rateLimitMessage = n.message, d;
546
+ const o = await t.json().catch(() => ({})), d = new Error(o.message || `HTTP ${t.status}`);
547
+ throw d.status = t.status, d.rateLimitMessage = o.message, d;
451
548
  }
452
549
  const a = document.createElement("div");
453
550
  a.className = "cd-msg assistant", a.innerHTML = '<div class="cd-typing"><span></span><span></span><span></span></div>', this._messagesEl.appendChild(a), this._scrollToBottom();
454
551
  const s = t.body.getReader(), i = new TextDecoder();
455
- let r = "", o = !1;
552
+ let r = "", n = !1;
456
553
  try {
457
554
  for (; ; ) {
458
- const { done: n, value: d } = await s.read();
459
- if (n) break;
460
- const l = i.decode(d, { stream: !0 });
461
- for (const g of l.split(`
555
+ const { done: o, value: d } = await s.read();
556
+ if (o) break;
557
+ const c = i.decode(d, { stream: !0 });
558
+ for (const g of c.split(`
462
559
  `)) {
463
560
  if (!g.startsWith("data: ")) continue;
464
- const m = g.slice(6).trim();
465
- if (m === "[DONE]") break;
561
+ const f = g.slice(6).trim();
562
+ if (f === "[DONE]") break;
466
563
  let h;
467
564
  try {
468
- h = JSON.parse(m);
565
+ h = JSON.parse(f);
469
566
  } catch {
470
567
  continue;
471
568
  }
472
- h.type === "content" && h.data && (o || (a.innerHTML = "", o = !0), r += h.data, a.innerHTML = this._renderMarkdown(r), this._scrollToBottom());
569
+ h.type === "content" && h.data && (n || (a.innerHTML = "", n = !0), r += h.data, a.innerHTML = this._renderMarkdown(r), this._scrollToBottom());
473
570
  }
474
571
  }
475
572
  } finally {
476
573
  s.releaseLock();
477
574
  }
478
- o || (a.innerHTML = this._renderMarkdown("No response")), r && this._messages.push({ role: "assistant", content: r });
575
+ n || (a.innerHTML = this._renderMarkdown("No response")), r && this._messages.push({ role: "assistant", content: r });
479
576
  }
480
577
  /** Non-streaming fallback path */
481
578
  async _callApi() {
@@ -486,8 +583,8 @@ class f {
486
583
  body: JSON.stringify(this._buildRequestBody())
487
584
  });
488
585
  if (!t.ok) {
489
- const o = await t.json().catch(() => ({})), n = new Error(o.message || `HTTP ${t.status}`);
490
- throw n.status = t.status, n.rateLimitMessage = o.message, n;
586
+ const n = await t.json().catch(() => ({})), o = new Error(n.message || `HTTP ${t.status}`);
587
+ throw o.status = t.status, o.rateLimitMessage = n.message, o;
491
588
  }
492
589
  const a = await t.json();
493
590
  return a.response || a.message || a.content || ((r = (i = (s = a.choices) == null ? void 0 : s[0]) == null ? void 0 : i.message) == null ? void 0 : r.content) || "No response";
@@ -523,16 +620,16 @@ class f {
523
620
  return String(e).replace(/[&<>"']/g, (t) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[t]);
524
621
  }
525
622
  }
526
- function x(c) {
527
- const e = new f(c);
623
+ function v(l) {
624
+ const e = new m(l);
528
625
  return e.mount(), e;
529
626
  }
530
627
  if (typeof document < "u") {
531
- const c = () => {
628
+ const l = () => {
532
629
  document.querySelectorAll("script[data-api-key]").forEach((e) => {
533
630
  const t = e.getAttribute("data-api-key");
534
631
  if (!t) return;
535
- const a = e.getAttribute("data-widget-id") || void 0, s = e.getAttribute("data-streaming"), i = new f({
632
+ const a = e.getAttribute("data-widget-id") || void 0, s = e.getAttribute("data-streaming"), i = new m({
536
633
  apiKey: t,
537
634
  widgetId: a,
538
635
  assistantId: e.getAttribute("data-assistant-id") || void 0,
@@ -548,10 +645,10 @@ if (typeof document < "u") {
548
645
  Promise.resolve(i.mount());
549
646
  });
550
647
  };
551
- document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", c) : c();
648
+ document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", l) : l();
552
649
  }
553
650
  export {
554
- f as CognitionDeskWidget,
555
- f as default,
556
- x as init
651
+ m as CognitionDeskWidget,
652
+ m as default,
653
+ v as init
557
654
  };
@@ -1,4 +1,4 @@
1
- (function(c,l){typeof exports=="object"&&typeof module<"u"?l(exports):typeof define=="function"&&define.amd?define(["exports"],l):(c=typeof globalThis<"u"?globalThis:c||self,l(c.CognitionDeskWidget={}))})(this,function(c){"use strict";const l="https://mounaji-backendv3.onrender.com",b=`
1
+ (function(c,h){typeof exports=="object"&&typeof module<"u"?h(exports):typeof define=="function"&&define.amd?define(["exports"],h):(c=typeof globalThis<"u"?globalThis:c||self,h(c.CognitionDeskWidget={}))})(this,function(c){"use strict";const h="https://mounaji-backendv3.onrender.com",b=`
2
2
  :host {
3
3
  all: initial;
4
4
  font-family: system-ui, sans-serif;
@@ -244,7 +244,7 @@
244
244
  font-size: 16px;
245
245
  }
246
246
  }
247
- `,f={chat:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>',close:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>',send:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>'};function w(){return"cd_"+Math.random().toString(36).slice(2)+Date.now().toString(36)}class g{constructor(e={}){var t,a,s,i,n;if(!e.apiKey)throw new Error("[CognitionDesk] apiKey is required");this._cfg={apiKey:e.apiKey,widgetId:e.widgetId||null,assistantId:e.assistantId||null,backendUrl:e.backendUrl||l,primaryColor:e.primaryColor||"#2563eb",theme:e.theme||"light",botName:e.botName||"AI Assistant",botEmoji:e.botEmoji||"🤖",welcomeMessage:e.welcomeMessage||"Hello! How can I help you today?",placeholder:e.placeholder||"Type a message…",position:e.position||"bottom-right",streaming:e.streaming!==!1,rateLimiting:{enabled:((t=e.rateLimiting)==null?void 0:t.enabled)!==!1,maxMessagesPerSession:((a=e.rateLimiting)==null?void 0:a.maxMessagesPerSession)??0,maxMessagesPerMinute:((s=e.rateLimiting)==null?void 0:s.maxMessagesPerMinute)??0,limitReachedMessage:((i=e.rateLimiting)==null?void 0:i.limitReachedMessage)||"You've reached the message limit for this session.",rateLimitMessage:((n=e.rateLimiting)==null?void 0:n.rateLimitMessage)||"You're sending messages too quickly. Please wait a moment."}},this._sessionId=w(),this._messageCount=0,this._minuteCount=0,this._minuteStart=Date.now(),this._messages=[],this._open=!1,this._loading=!1,this._container=null,this._shadow=null,this._panel=null,this._messagesEl=null,this._textarea=null,this._sendBtn=null,this._viewportHandler=null}mount(e){const t=()=>{const a=e||document.body;this._container=document.createElement("div"),this._container.setAttribute("data-cognitiondesk",""),a.appendChild(this._container),this._shadow=this._container.attachShadow({mode:"open"});const s=document.createElement("style");s.textContent=b,this._shadow.appendChild(s),this._buildDOM(),this._applyTheme(),this._syncViewportMetrics(),this._bindViewportMetrics(),this._bindEvents(),this._cfg.welcomeMessage&&this._appendMessage("assistant",this._cfg.welcomeMessage,!1)};return this._cfg.widgetId?this._fetchWidgetConfig().then(()=>t()).catch(()=>t()):(t(),Promise.resolve(this))}async _fetchWidgetConfig(){var t;const e=`${this._cfg.backendUrl}/platforms/web-widget/public/${this._cfg.widgetId}`;try{const a=await fetch(e);if(!a.ok)return;const{config:s}=await a.json();if(!s)return;s.botName&&!this._userSet("botName")&&(this._cfg.botName=s.botName),s.welcomeMessage&&!this._userSet("welcomeMessage")&&(this._cfg.welcomeMessage=s.welcomeMessage),s.primaryColor&&!this._userSet("primaryColor")&&(this._cfg.primaryColor=s.primaryColor),s.placeholder&&!this._userSet("placeholder")&&(this._cfg.placeholder=s.placeholder),s.assistantId&&!this._cfg.assistantId&&(this._cfg.assistantId=s.assistantId),(t=s.avatar)!=null&&t.value&&!this._userSet("botEmoji")&&(this._cfg.botEmoji=s.avatar.value),s.rateLimiting&&(this._cfg.rateLimiting={...this._cfg.rateLimiting,...s.rateLimiting})}catch{}}_userSet(e){return!1}unmount(){this._unbindViewportMetrics(),this._container&&(this._container.remove(),this._container=null)}open(){var e,t;this._open=!0,this._syncViewportMetrics(),(e=this._panel)==null||e.classList.add("open"),(t=this._textarea)==null||t.focus()}close(){var e;this._open=!1,(e=this._panel)==null||e.classList.remove("open")}toggle(){this._open?this.close():this.open()}clearHistory(){this._messages=[],this._messagesEl&&(this._messagesEl.innerHTML=""),this._cfg.welcomeMessage&&this._appendMessage("assistant",this._cfg.welcomeMessage)}_buildDOM(){const e=document.createElement("div");e.className="cd-root";const t=document.createElement("button");t.className="cd-widget-btn",t.innerHTML=f.chat,t.setAttribute("aria-label","Open chat"),t.style.setProperty("--cd-primary",this._cfg.primaryColor),this._toggleBtn=t;const a=document.createElement("div");a.className="cd-panel",this._panel=a;const s=document.createElement("div");s.className="cd-header",s.innerHTML=`
247
+ `,m={chat:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>',close:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>',send:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>'};function v(){return"cd_"+Math.random().toString(36).slice(2)+Date.now().toString(36)}class g{constructor(e={}){var t,i,s,a,r;if(!e.apiKey)throw new Error("[CognitionDesk] apiKey is required");this._overrides=new Set(e.overrideSettings?Object.keys(e.overrideSettings):[]),this._cfg={apiKey:e.apiKey,widgetId:e.widgetId||null,assistantId:e.assistantId||null,backendUrl:e.backendUrl||h,primaryColor:e.primaryColor||"#2563eb",theme:e.theme||"light",botName:e.botName||"AI Assistant",botEmoji:e.botEmoji||"🤖",welcomeMessage:e.welcomeMessage||"Hello! How can I help you today?",placeholder:e.placeholder||"Type a message…",position:e.position||"bottom-right",streaming:e.streaming!==!1,rateLimiting:{enabled:((t=e.rateLimiting)==null?void 0:t.enabled)!==!1,maxMessagesPerSession:((i=e.rateLimiting)==null?void 0:i.maxMessagesPerSession)??0,maxMessagesPerMinute:((s=e.rateLimiting)==null?void 0:s.maxMessagesPerMinute)??0,limitReachedMessage:((a=e.rateLimiting)==null?void 0:a.limitReachedMessage)||"You've reached the message limit for this session.",rateLimitMessage:((r=e.rateLimiting)==null?void 0:r.rateLimitMessage)||"You're sending messages too quickly. Please wait a moment."},overrideSettings:e.overrideSettings||null},e.overrideSettings&&this._applyOverrides(e.overrideSettings),this._sessionId=v(),this._messageCount=0,this._minuteCount=0,this._minuteStart=Date.now(),this._messages=[],this._open=!1,this._loading=!1,this._container=null,this._shadow=null,this._panel=null,this._messagesEl=null,this._textarea=null,this._sendBtn=null,this._viewportHandler=null,this._refreshPending=!1}mount(e){const t=()=>{const i=e||document.body;this._container=document.createElement("div"),this._container.setAttribute("data-cognitiondesk",""),i.appendChild(this._container),this._shadow=this._container.attachShadow({mode:"open"});const s=document.createElement("style");s.textContent=b,this._shadow.appendChild(s),this._buildDOM(),this._applyConfig(),this._syncViewportMetrics(),this._bindViewportMetrics(),this._bindEvents(),this._cfg.welcomeMessage&&this._appendMessage("assistant",this._cfg.welcomeMessage,!1)};return this._cfg.widgetId?this._fetchWidgetConfig().then(i=>{i!==!1&&t()}).catch(()=>t()):(t(),Promise.resolve(this))}updateSettings(e){if(!e||typeof e!="object")return;const t={...this._cfg.overrideSettings||{},...e};this._cfg.overrideSettings=t,this._overrides=new Set(Object.keys(t)),this._applyOverrides(t),this._applyConfig()}async refreshConfig(){if(!this._cfg.widgetId)return;await this._fetchWidgetConfig()===!1?this.unmount():this._applyConfig()}_userSet(e){return this._overrides.has(e)}async _fetchWidgetConfig(){var t;const e=`${this._cfg.backendUrl}/platforms/web-widget/public/${this._cfg.widgetId}`;try{const i=await fetch(e);if(i.status===404||i.status===403)return!1;if(!i.ok)return!0;const{config:s}=await i.json();if(!s)return;const a=(r,n)=>{n!=null&&!this._userSet(r)&&(this._cfg[r]=n)};return a("botName",s.botName),a("welcomeMessage",s.welcomeMessage),a("primaryColor",s.primaryColor),a("secondaryColor",s.secondaryColor),a("placeholder",s.placeholder),a("theme",s.theme),a("position",s.position),a("style",s.style),a("size",s.size),s.assistantId&&!this._cfg.assistantId&&(this._cfg.assistantId=s.assistantId),(t=s.avatar)!=null&&t.value&&!this._userSet("botEmoji")&&(this._cfg.botEmoji=s.avatar.value),s.rateLimiting&&(this._cfg.rateLimiting={...this._cfg.rateLimiting,...s.rateLimiting}),this._cfg.overrideSettings&&this._applyOverrides(this._cfg.overrideSettings),!0}catch{return!0}}_applyOverrides(e){for(const[t,i]of Object.entries(e))t==="rateLimiting"&&typeof i=="object"?this._cfg.rateLimiting={...this._cfg.rateLimiting,...i}:this._cfg[t]=i}_applyConfig(){var s,a,r,n,o,d,l;const e=this._cfg.theme==="dark"||this._cfg.theme==="auto"&&((s=window.matchMedia)==null?void 0:s.call(window,"(prefers-color-scheme: dark)").matches);(a=this._root)==null||a.classList.toggle("cd-dark",e),(r=this._root)==null||r.style.setProperty("--cd-primary",this._cfg.primaryColor),(n=this._panel)==null||n.style.setProperty("--cd-primary",this._cfg.primaryColor),(o=this._toggleBtn)==null||o.style.setProperty("--cd-primary",this._cfg.primaryColor);const t=(d=this._shadow)==null?void 0:d.querySelector(".cd-header-avatar");t&&(t.textContent=this._cfg.botEmoji);const i=(l=this._shadow)==null?void 0:l.querySelector(".cd-header-name");i&&(i.textContent=this._cfg.botName),this._textarea&&(this._textarea.placeholder=this._cfg.placeholder)}_silentRefresh(){!this._cfg.widgetId||this._refreshPending||(this._refreshPending=!0,this._fetchWidgetConfig().then(e=>{e===!1?this.unmount():this._applyConfig()}).catch(()=>{}).finally(()=>{this._refreshPending=!1}))}unmount(){this._unbindViewportMetrics(),this._container&&(this._container.remove(),this._container=null)}open(){var e,t;this._open=!0,this._syncViewportMetrics(),(e=this._panel)==null||e.classList.add("open"),(t=this._textarea)==null||t.focus(),this._silentRefresh()}close(){var e;this._open=!1,(e=this._panel)==null||e.classList.remove("open")}toggle(){this._open?this.close():this.open()}clearHistory(){this._messages=[],this._messagesEl&&(this._messagesEl.innerHTML=""),this._cfg.welcomeMessage&&this._appendMessage("assistant",this._cfg.welcomeMessage)}_buildDOM(){const e=document.createElement("div");e.className="cd-root";const t=document.createElement("button");t.className="cd-widget-btn",t.innerHTML=m.chat,t.setAttribute("aria-label","Open chat"),t.style.setProperty("--cd-primary",this._cfg.primaryColor),this._toggleBtn=t;const i=document.createElement("div");i.className="cd-panel",this._panel=i;const s=document.createElement("div");s.className="cd-header",s.innerHTML=`
248
248
  <div class="cd-header-avatar">${this._cfg.botEmoji}</div>
249
249
  <div class="cd-header-info">
250
250
  <div class="cd-header-name">${this._escHtml(this._cfg.botName)}</div>
@@ -252,5 +252,5 @@
252
252
  <span class="cd-status-dot"></span> Online
253
253
  </div>
254
254
  </div>
255
- `;const i=document.createElement("button");i.className="cd-close-btn",i.innerHTML=f.close,i.setAttribute("aria-label","Close chat"),s.appendChild(i),this._closeBtn=i;const n=document.createElement("div");n.className="cd-messages",n.setAttribute("role","log"),n.setAttribute("aria-live","polite"),this._messagesEl=n;const o=document.createElement("div");o.className="cd-input-area";const r=document.createElement("textarea");r.className="cd-textarea",r.rows=1,r.placeholder=this._cfg.placeholder,this._textarea=r;const d=document.createElement("button");d.className="cd-send-btn",d.innerHTML=f.send,d.setAttribute("aria-label","Send"),this._sendBtn=d,o.appendChild(r),o.appendChild(d);const p=document.createElement("div");p.className="cd-powered",p.innerHTML='Powered by <a href="https://cognitiondesk.com" target="_blank" rel="noopener">CognitionDesk</a>',a.appendChild(s),a.appendChild(n),a.appendChild(o),a.appendChild(p),e.appendChild(t),e.appendChild(a),this._shadow.appendChild(e),this._root=e}_applyTheme(){var t,a,s;(this._cfg.theme==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":this._cfg.theme)==="dark"&&((t=this._root)==null||t.classList.add("cd-dark")),(a=this._root)==null||a.style.setProperty("--cd-primary",this._cfg.primaryColor),(s=this._panel)==null||s.style.setProperty("--cd-primary",this._cfg.primaryColor)}_bindEvents(){this._toggleBtn.addEventListener("click",()=>this.toggle()),this._closeBtn.addEventListener("click",()=>this.close()),this._sendBtn.addEventListener("click",()=>this._sendMessage()),this._textarea.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),this._sendMessage())}),this._textarea.addEventListener("input",()=>{this._textarea.style.height="auto",this._textarea.style.height=Math.min(this._textarea.scrollHeight,120)+"px"}),this._textarea.addEventListener("focus",()=>{this._syncViewportMetrics()})}_bindViewportMetrics(){this._viewportHandler||(this._viewportHandler=()=>this._syncViewportMetrics(),window.addEventListener("resize",this._viewportHandler,{passive:!0}),window.addEventListener("orientationchange",this._viewportHandler),window.visualViewport&&(window.visualViewport.addEventListener("resize",this._viewportHandler),window.visualViewport.addEventListener("scroll",this._viewportHandler)))}_unbindViewportMetrics(){this._viewportHandler&&(window.removeEventListener("resize",this._viewportHandler),window.removeEventListener("orientationchange",this._viewportHandler),window.visualViewport&&(window.visualViewport.removeEventListener("resize",this._viewportHandler),window.visualViewport.removeEventListener("scroll",this._viewportHandler)),this._viewportHandler=null)}_syncViewportMetrics(){const e=this._root||this._container;if(!e)return;const t=window.visualViewport,a=t?t.height:window.innerHeight;e.style.setProperty("--cd-viewport-height",`${Math.round(a)}px`)}async _sendMessage(){var a;const e=this._textarea.value.trim();if(!e||this._loading)return;const t=this._cfg.rateLimiting;if(t!=null&&t.enabled){const s=Date.now();if(s-this._minuteStart>=6e4&&(this._minuteCount=0,this._minuteStart=s),t.maxMessagesPerSession>0&&this._messageCount>=t.maxMessagesPerSession){this._appendMessage("error",t.limitReachedMessage,!1),this._textarea.disabled=!0,this._sendBtn.disabled=!0;return}if(t.maxMessagesPerMinute>0&&this._minuteCount>=t.maxMessagesPerMinute){this._appendMessage("error",t.rateLimitMessage,!1);return}}this._textarea.value="",this._textarea.style.height="auto",this._loading=!0,this._sendBtn.disabled=!0,this._messages.push({role:"user",content:e}),this._appendMessage("user",e,!1),this._messageCount+=1,this._minuteCount+=1;try{if(this._cfg.streaming)await this._callApiStream();else{const s=this._showTyping(),i=await this._callApi();this._removeTyping(s),this._messages.push({role:"assistant",content:i}),this._appendMessage("assistant",i,!0)}}catch(s){if(s.status===429||(a=s.message)!=null&&a.includes("429")){const i=s.rateLimitMessage||(t==null?void 0:t.rateLimitMessage)||"You're sending messages too quickly. Please wait a moment.";this._appendMessage("error",i,!1)}else this._appendMessage("error","Sorry, something went wrong. Please try again.",!1);console.error("[CognitionDesk]",s)}finally{this._loading=!1,(t==null?void 0:t.maxMessagesPerSession)>0&&this._messageCount>=t.maxMessagesPerSession||(this._sendBtn.disabled=!1),this._textarea.focus()}}_buildRequestBody(){return{messages:this._messages,assistantId:this._cfg.assistantId||void 0,userContext:{sessionId:this._sessionId,assistantId:this._cfg.assistantId||void 0,widgetId:this._cfg.widgetId||void 0,platform:"cognitiondesk-widget"}}}async _callApiStream(){const e=`${this._cfg.backendUrl}/chat-apiKeyAuth/stream`,t=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":this._cfg.apiKey},body:JSON.stringify({...this._buildRequestBody(),stream:!0})});if(!t.ok){const r=await t.json().catch(()=>({})),d=new Error(r.message||`HTTP ${t.status}`);throw d.status=t.status,d.rateLimitMessage=r.message,d}const a=document.createElement("div");a.className="cd-msg assistant",a.innerHTML='<div class="cd-typing"><span></span><span></span><span></span></div>',this._messagesEl.appendChild(a),this._scrollToBottom();const s=t.body.getReader(),i=new TextDecoder;let n="",o=!1;try{for(;;){const{done:r,value:d}=await s.read();if(r)break;const p=i.decode(d,{stream:!0});for(const u of p.split(`
256
- `)){if(!u.startsWith("data: "))continue;const _=u.slice(6).trim();if(_==="[DONE]")break;let m;try{m=JSON.parse(_)}catch{continue}m.type==="content"&&m.data&&(o||(a.innerHTML="",o=!0),n+=m.data,a.innerHTML=this._renderMarkdown(n),this._scrollToBottom())}}}finally{s.releaseLock()}o||(a.innerHTML=this._renderMarkdown("No response")),n&&this._messages.push({role:"assistant",content:n})}async _callApi(){var s,i,n;const e=`${this._cfg.backendUrl}/chat-apiKeyAuth/chat`,t=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":this._cfg.apiKey},body:JSON.stringify(this._buildRequestBody())});if(!t.ok){const o=await t.json().catch(()=>({})),r=new Error(o.message||`HTTP ${t.status}`);throw r.status=t.status,r.rateLimitMessage=o.message,r}const a=await t.json();return a.response||a.message||a.content||((n=(i=(s=a.choices)==null?void 0:s[0])==null?void 0:i.message)==null?void 0:n.content)||"No response"}_appendMessage(e,t,a=!0){const s=document.createElement("div");s.className=`cd-msg ${e}`,a&&e!=="user"?s.innerHTML=this._renderMarkdown(t):s.textContent=t,this._messagesEl.appendChild(s),this._scrollToBottom()}_renderMarkdown(e){if(!e)return"";let t=this._escHtml(e);return t=t.replace(/```[\w]*\n?([\s\S]*?)```/g,(a,s)=>`<pre style="background:#0f172a;color:#e2e8f0;padding:10px;border-radius:6px;font-size:12px;overflow-x:auto;margin:6px 0;white-space:pre-wrap">${s.trim()}</pre>`),t=t.replace(/`([^`]+)`/g,'<code style="background:rgba(0,0,0,.08);padding:1px 5px;border-radius:4px;font-size:.9em">$1</code>'),t=t.replace(/\*\*([^*]+)\*\*/g,"<strong>$1</strong>"),t=t.replace(/__([^_]+)__/g,"<strong>$1</strong>"),t=t.replace(/\*([^*]+)\*/g,"<em>$1</em>"),t=t.replace(/_([^_]+)_/g,"<em>$1</em>"),t=t.replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g,'<a href="$2" target="_blank" rel="noopener" style="color:var(--cd-primary,#2563eb)">$1</a>'),t=t.replace(/\n/g,"<br>"),t}_showTyping(){const e=document.createElement("div");return e.className="cd-msg assistant",e.innerHTML='<div class="cd-typing"><span></span><span></span><span></span></div>',this._messagesEl.appendChild(e),this._scrollToBottom(),e}_removeTyping(e){e==null||e.remove()}_scrollToBottom(){this._messagesEl&&(this._messagesEl.scrollTop=this._messagesEl.scrollHeight)}_escHtml(e){return String(e).replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[t])}}function x(h){const e=new g(h);return e.mount(),e}if(typeof document<"u"){const h=()=>{document.querySelectorAll("script[data-api-key]").forEach(e=>{const t=e.getAttribute("data-api-key");if(!t)return;const a=e.getAttribute("data-widget-id")||void 0,s=e.getAttribute("data-streaming"),i=new g({apiKey:t,widgetId:a,assistantId:e.getAttribute("data-assistant-id")||void 0,theme:e.getAttribute("data-theme")||"light",primaryColor:e.getAttribute("data-primary-color")||"#2563eb",botName:e.getAttribute("data-bot-name")||"AI Assistant",botEmoji:e.getAttribute("data-bot-emoji")||void 0,welcomeMessage:e.getAttribute("data-welcome-message")||void 0,placeholder:e.getAttribute("data-placeholder")||void 0,backendUrl:e.getAttribute("data-backend-url")||void 0,streaming:s===null?!0:s!=="false"});Promise.resolve(i.mount())})};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",h):h()}c.CognitionDeskWidget=g,c.default=g,c.init=x,Object.defineProperties(c,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
255
+ `;const a=document.createElement("button");a.className="cd-close-btn",a.innerHTML=m.close,a.setAttribute("aria-label","Close chat"),s.appendChild(a),this._closeBtn=a;const r=document.createElement("div");r.className="cd-messages",r.setAttribute("role","log"),r.setAttribute("aria-live","polite"),this._messagesEl=r;const n=document.createElement("div");n.className="cd-input-area";const o=document.createElement("textarea");o.className="cd-textarea",o.rows=1,o.placeholder=this._cfg.placeholder,this._textarea=o;const d=document.createElement("button");d.className="cd-send-btn",d.innerHTML=m.send,d.setAttribute("aria-label","Send"),this._sendBtn=d,n.appendChild(o),n.appendChild(d);const l=document.createElement("div");l.className="cd-powered",l.innerHTML='Powered by <a href="https://cognitiondesk.com" target="_blank" rel="noopener">CognitionDesk</a>',i.appendChild(s),i.appendChild(r),i.appendChild(n),i.appendChild(l),e.appendChild(t),e.appendChild(i),this._shadow.appendChild(e),this._root=e}_applyTheme(){var t,i,s;(this._cfg.theme==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":this._cfg.theme)==="dark"&&((t=this._root)==null||t.classList.add("cd-dark")),(i=this._root)==null||i.style.setProperty("--cd-primary",this._cfg.primaryColor),(s=this._panel)==null||s.style.setProperty("--cd-primary",this._cfg.primaryColor)}_bindEvents(){this._toggleBtn.addEventListener("click",()=>this.toggle()),this._closeBtn.addEventListener("click",()=>this.close()),this._sendBtn.addEventListener("click",()=>this._sendMessage()),this._textarea.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),this._sendMessage())}),this._textarea.addEventListener("input",()=>{this._textarea.style.height="auto",this._textarea.style.height=Math.min(this._textarea.scrollHeight,120)+"px"}),this._textarea.addEventListener("focus",()=>{this._syncViewportMetrics()})}_bindViewportMetrics(){this._viewportHandler||(this._viewportHandler=()=>this._syncViewportMetrics(),window.addEventListener("resize",this._viewportHandler,{passive:!0}),window.addEventListener("orientationchange",this._viewportHandler),window.visualViewport&&(window.visualViewport.addEventListener("resize",this._viewportHandler),window.visualViewport.addEventListener("scroll",this._viewportHandler)))}_unbindViewportMetrics(){this._viewportHandler&&(window.removeEventListener("resize",this._viewportHandler),window.removeEventListener("orientationchange",this._viewportHandler),window.visualViewport&&(window.visualViewport.removeEventListener("resize",this._viewportHandler),window.visualViewport.removeEventListener("scroll",this._viewportHandler)),this._viewportHandler=null)}_syncViewportMetrics(){const e=this._root||this._container;if(!e)return;const t=window.visualViewport,i=t?t.height:window.innerHeight;e.style.setProperty("--cd-viewport-height",`${Math.round(i)}px`)}async _sendMessage(){var i;const e=this._textarea.value.trim();if(!e||this._loading)return;const t=this._cfg.rateLimiting;if(t!=null&&t.enabled){const s=Date.now();if(s-this._minuteStart>=6e4&&(this._minuteCount=0,this._minuteStart=s),t.maxMessagesPerSession>0&&this._messageCount>=t.maxMessagesPerSession){this._appendMessage("error",t.limitReachedMessage,!1),this._textarea.disabled=!0,this._sendBtn.disabled=!0;return}if(t.maxMessagesPerMinute>0&&this._minuteCount>=t.maxMessagesPerMinute){this._appendMessage("error",t.rateLimitMessage,!1);return}}this._textarea.value="",this._textarea.style.height="auto",this._loading=!0,this._sendBtn.disabled=!0,this._messages.push({role:"user",content:e}),this._appendMessage("user",e,!1),this._messageCount+=1,this._minuteCount+=1;try{if(this._cfg.streaming)await this._callApiStream();else{const s=this._showTyping(),a=await this._callApi();this._removeTyping(s),this._messages.push({role:"assistant",content:a}),this._appendMessage("assistant",a,!0)}}catch(s){if(s.status===429||(i=s.message)!=null&&i.includes("429")){const a=s.rateLimitMessage||(t==null?void 0:t.rateLimitMessage)||"You're sending messages too quickly. Please wait a moment.";this._appendMessage("error",a,!1)}else this._appendMessage("error","Sorry, something went wrong. Please try again.",!1);console.error("[CognitionDesk]",s)}finally{this._loading=!1,(t==null?void 0:t.maxMessagesPerSession)>0&&this._messageCount>=t.maxMessagesPerSession||(this._sendBtn.disabled=!1),this._textarea.focus()}}_buildRequestBody(){return{messages:this._messages,assistantId:this._cfg.assistantId||void 0,userContext:{sessionId:this._sessionId,assistantId:this._cfg.assistantId||void 0,widgetId:this._cfg.widgetId||void 0,platform:"cognitiondesk-widget"}}}async _callApiStream(){const e=`${this._cfg.backendUrl}/chat-apiKeyAuth/stream`,t=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":this._cfg.apiKey},body:JSON.stringify({...this._buildRequestBody(),stream:!0})});if(!t.ok){const o=await t.json().catch(()=>({})),d=new Error(o.message||`HTTP ${t.status}`);throw d.status=t.status,d.rateLimitMessage=o.message,d}const i=document.createElement("div");i.className="cd-msg assistant",i.innerHTML='<div class="cd-typing"><span></span><span></span><span></span></div>',this._messagesEl.appendChild(i),this._scrollToBottom();const s=t.body.getReader(),a=new TextDecoder;let r="",n=!1;try{for(;;){const{done:o,value:d}=await s.read();if(o)break;const l=a.decode(d,{stream:!0});for(const u of l.split(`
256
+ `)){if(!u.startsWith("data: "))continue;const _=u.slice(6).trim();if(_==="[DONE]")break;let f;try{f=JSON.parse(_)}catch{continue}f.type==="content"&&f.data&&(n||(i.innerHTML="",n=!0),r+=f.data,i.innerHTML=this._renderMarkdown(r),this._scrollToBottom())}}}finally{s.releaseLock()}n||(i.innerHTML=this._renderMarkdown("No response")),r&&this._messages.push({role:"assistant",content:r})}async _callApi(){var s,a,r;const e=`${this._cfg.backendUrl}/chat-apiKeyAuth/chat`,t=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":this._cfg.apiKey},body:JSON.stringify(this._buildRequestBody())});if(!t.ok){const n=await t.json().catch(()=>({})),o=new Error(n.message||`HTTP ${t.status}`);throw o.status=t.status,o.rateLimitMessage=n.message,o}const i=await t.json();return i.response||i.message||i.content||((r=(a=(s=i.choices)==null?void 0:s[0])==null?void 0:a.message)==null?void 0:r.content)||"No response"}_appendMessage(e,t,i=!0){const s=document.createElement("div");s.className=`cd-msg ${e}`,i&&e!=="user"?s.innerHTML=this._renderMarkdown(t):s.textContent=t,this._messagesEl.appendChild(s),this._scrollToBottom()}_renderMarkdown(e){if(!e)return"";let t=this._escHtml(e);return t=t.replace(/```[\w]*\n?([\s\S]*?)```/g,(i,s)=>`<pre style="background:#0f172a;color:#e2e8f0;padding:10px;border-radius:6px;font-size:12px;overflow-x:auto;margin:6px 0;white-space:pre-wrap">${s.trim()}</pre>`),t=t.replace(/`([^`]+)`/g,'<code style="background:rgba(0,0,0,.08);padding:1px 5px;border-radius:4px;font-size:.9em">$1</code>'),t=t.replace(/\*\*([^*]+)\*\*/g,"<strong>$1</strong>"),t=t.replace(/__([^_]+)__/g,"<strong>$1</strong>"),t=t.replace(/\*([^*]+)\*/g,"<em>$1</em>"),t=t.replace(/_([^_]+)_/g,"<em>$1</em>"),t=t.replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g,'<a href="$2" target="_blank" rel="noopener" style="color:var(--cd-primary,#2563eb)">$1</a>'),t=t.replace(/\n/g,"<br>"),t}_showTyping(){const e=document.createElement("div");return e.className="cd-msg assistant",e.innerHTML='<div class="cd-typing"><span></span><span></span><span></span></div>',this._messagesEl.appendChild(e),this._scrollToBottom(),e}_removeTyping(e){e==null||e.remove()}_scrollToBottom(){this._messagesEl&&(this._messagesEl.scrollTop=this._messagesEl.scrollHeight)}_escHtml(e){return String(e).replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[t])}}function w(p){const e=new g(p);return e.mount(),e}if(typeof document<"u"){const p=()=>{document.querySelectorAll("script[data-api-key]").forEach(e=>{const t=e.getAttribute("data-api-key");if(!t)return;const i=e.getAttribute("data-widget-id")||void 0,s=e.getAttribute("data-streaming"),a=new g({apiKey:t,widgetId:i,assistantId:e.getAttribute("data-assistant-id")||void 0,theme:e.getAttribute("data-theme")||"light",primaryColor:e.getAttribute("data-primary-color")||"#2563eb",botName:e.getAttribute("data-bot-name")||"AI Assistant",botEmoji:e.getAttribute("data-bot-emoji")||void 0,welcomeMessage:e.getAttribute("data-welcome-message")||void 0,placeholder:e.getAttribute("data-placeholder")||void 0,backendUrl:e.getAttribute("data-backend-url")||void 0,streaming:s===null?!0:s!=="false"});Promise.resolve(a.mount())})};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",p):p()}c.CognitionDeskWidget=g,c.default=g,c.init=w,Object.defineProperties(c,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cognitiondesk/widget",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "Embed a CognitionDesk AI chat widget into any website. Authenticate with an API key from your CognitionDesk dashboard.",
5
5
  "keywords": ["chat", "widget", "ai", "cognitiondesk", "chatbot", "react", "streaming", "mounaji"],
6
6
  "license": "MIT",
package/src/react.jsx CHANGED
@@ -23,6 +23,16 @@
23
23
  * onOpen={() => console.log('chat opened')}
24
24
  * onClose={() => console.log('chat closed')}
25
25
  * />
26
+ *
27
+ * // Mixed: load base config from platform, override specific fields in code
28
+ * <CognitionDeskWidget
29
+ * apiKey="YOUR_API_KEY"
30
+ * widgetId="widget_xxx"
31
+ * overrideSettings={{ primaryColor: '#7c3aed', botName: 'Aria' }}
32
+ * />
33
+ * // Fields listed in overrideSettings are controlled by code and will NOT be
34
+ * // overwritten by dashboard changes. All other fields continue to sync from
35
+ * // the platform on every panel open.
26
36
  */
27
37
 
28
38
  import { useEffect, useRef, forwardRef, useImperativeHandle } from 'react';
@@ -49,6 +59,10 @@ export const CognitionDeskWidget = forwardRef(function CognitionDeskWidget(props
49
59
  rateLimitMessage,
50
60
  onOpen,
51
61
  onClose,
62
+ // Fields listed here are owned by code and won't be overwritten by
63
+ // dashboard config refreshes. Pass an object whose keys match widget
64
+ // config fields, e.g. { primaryColor: '#7c3aed', botName: 'Aria' }.
65
+ overrideSettings,
52
66
  } = props;
53
67
 
54
68
  const widgetRef = useRef(null);
@@ -79,7 +93,8 @@ export const CognitionDeskWidget = forwardRef(function CognitionDeskWidget(props
79
93
  welcomeMessage,
80
94
  placeholder,
81
95
  streaming,
82
- ...(backendUrl ? { backendUrl } : {}),
96
+ ...(backendUrl ? { backendUrl } : {}),
97
+ ...(overrideSettings ? { overrideSettings } : {}),
83
98
  rateLimiting: {
84
99
  enabled: true,
85
100
  maxMessagesPerSession,
@@ -116,9 +131,17 @@ export const CognitionDeskWidget = forwardRef(function CognitionDeskWidget(props
116
131
  if (!w) return;
117
132
  w._cfg.theme = theme;
118
133
  w._cfg.primaryColor = primaryColor;
119
- w._applyTheme?.();
134
+ w._applyConfig?.();
120
135
  }, [theme, primaryColor]);
121
136
 
137
+ // Sync overrideSettings changes at runtime — updates which fields are
138
+ // code-controlled and immediately re-applies the overridden values.
139
+ useEffect(() => {
140
+ const w = widgetRef.current;
141
+ if (!w || !overrideSettings) return;
142
+ w.updateSettings(overrideSettings);
143
+ }, [overrideSettings]);
144
+
122
145
  return <div ref={mountRef} data-cognitiondesk-root="" />;
123
146
  });
124
147