@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.
- package/LICENSE +21 -0
- package/README.md +1 -268
- package/dist/react.es.js +230 -125
- package/dist/react.umd.cjs +4 -4
- package/dist/widget.es.js +144 -47
- package/dist/widget.umd.cjs +4 -4
- package/package.json +1 -1
- package/src/react.jsx +25 -2
- package/src/widget.js +204 -36
package/dist/react.umd.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
(function(
|
|
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
|
-
`,
|
|
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
|
|
256
|
-
`)){if(!
|
|
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=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[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
|
|
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.
|
|
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
|
-
|
|
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`
|
|
288
|
-
* Returns a Promise so callers can await
|
|
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.
|
|
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(() =>
|
|
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
|
-
*
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
316
|
-
|
|
317
|
-
|
|
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
|
|
359
|
-
|
|
360
|
-
const
|
|
361
|
-
|
|
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,
|
|
364
|
-
const
|
|
365
|
-
|
|
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
|
|
450
|
-
throw d.status = t.status, d.rateLimitMessage =
|
|
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 = "",
|
|
552
|
+
let r = "", n = !1;
|
|
456
553
|
try {
|
|
457
554
|
for (; ; ) {
|
|
458
|
-
const { done:
|
|
459
|
-
if (
|
|
460
|
-
const
|
|
461
|
-
for (const g of
|
|
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
|
|
465
|
-
if (
|
|
561
|
+
const f = g.slice(6).trim();
|
|
562
|
+
if (f === "[DONE]") break;
|
|
466
563
|
let h;
|
|
467
564
|
try {
|
|
468
|
-
h = JSON.parse(
|
|
565
|
+
h = JSON.parse(f);
|
|
469
566
|
} catch {
|
|
470
567
|
continue;
|
|
471
568
|
}
|
|
472
|
-
h.type === "content" && h.data && (
|
|
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
|
-
|
|
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
|
|
490
|
-
throw
|
|
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) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[t]);
|
|
524
621
|
}
|
|
525
622
|
}
|
|
526
|
-
function
|
|
527
|
-
const e = new
|
|
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
|
|
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
|
|
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",
|
|
648
|
+
document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", l) : l();
|
|
552
649
|
}
|
|
553
650
|
export {
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
651
|
+
m as CognitionDeskWidget,
|
|
652
|
+
m as default,
|
|
653
|
+
v as init
|
|
557
654
|
};
|
package/dist/widget.umd.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
(function(c,
|
|
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
|
-
`,
|
|
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
|
|
256
|
-
`)){if(!u.startsWith("data: "))continue;const _=u.slice(6).trim();if(_==="[DONE]")break;let
|
|
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=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[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.
|
|
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
|
|
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.
|
|
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
|
|