@cognitiondesk/widget 1.2.1 → 1.2.2
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 +222 -124
- package/dist/react.umd.cjs +4 -4
- package/dist/widget.es.js +135 -45
- package/dist/widget.umd.cjs +4 -4
- package/package.json +1 -1
- package/src/react.jsx +25 -2
- package/src/widget.js +187 -34
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(()=>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(){this._cfg.widgetId&&(await this._fetchWidgetConfig(),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.ok)return;const{config:s}=await i.json();if(!s)return;const a=(n,o)=>{o!=null&&!this._userSet(n)&&(this._cfg[n]=o)};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)}catch{}}_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(()=>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,53 +252,112 @@ 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
327
|
return this._cfg.widgetId ? this._fetchWidgetConfig().then(() => t()).catch(() => t()) : (t(), Promise.resolve(this));
|
|
298
328
|
}
|
|
329
|
+
/**
|
|
330
|
+
* Programmatically update settings at runtime.
|
|
331
|
+
* Merges `newSettings` into `overrideSettings` and re-applies to the live DOM.
|
|
332
|
+
* Use this to change colors, bot name, etc. without remounting.
|
|
333
|
+
*
|
|
334
|
+
* widget.updateSettings({ primaryColor: '#dc2626', botName: 'Support Bot' });
|
|
335
|
+
*/
|
|
336
|
+
updateSettings(e) {
|
|
337
|
+
if (!e || typeof e != "object") return;
|
|
338
|
+
const t = { ...this._cfg.overrideSettings || {}, ...e };
|
|
339
|
+
this._cfg.overrideSettings = t, this._overrides = new Set(Object.keys(t)), this._applyOverrides(t), this._applyConfig();
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Fetch fresh config from the dashboard right now and apply it.
|
|
343
|
+
* Useful after the user changes settings in the CognitionDesk dashboard
|
|
344
|
+
* and wants them reflected immediately without reloading the page.
|
|
345
|
+
*/
|
|
346
|
+
async refreshConfig() {
|
|
347
|
+
this._cfg.widgetId && (await this._fetchWidgetConfig(), this._applyConfig());
|
|
348
|
+
}
|
|
349
|
+
// ── Config internals ────────────────────────────────────────────────────────
|
|
350
|
+
/**
|
|
351
|
+
* Returns true if `key` is explicitly controlled by `overrideSettings`.
|
|
352
|
+
* Those keys are immune to server config overwrites.
|
|
353
|
+
*/
|
|
354
|
+
_userSet(e) {
|
|
355
|
+
return this._overrides.has(e);
|
|
356
|
+
}
|
|
299
357
|
/**
|
|
300
358
|
* Fetch public widget config from the platform and merge into this._cfg.
|
|
301
|
-
*
|
|
359
|
+
* Server values apply to any key NOT present in `overrideSettings`.
|
|
360
|
+
* After merging, overrides are re-applied so they always win.
|
|
302
361
|
*/
|
|
303
362
|
async _fetchWidgetConfig() {
|
|
304
363
|
var t;
|
|
@@ -308,20 +367,51 @@ class f {
|
|
|
308
367
|
if (!a.ok) return;
|
|
309
368
|
const { config: s } = await a.json();
|
|
310
369
|
if (!s) return;
|
|
311
|
-
|
|
370
|
+
const i = (r, n) => {
|
|
371
|
+
n != null && !this._userSet(r) && (this._cfg[r] = n);
|
|
372
|
+
};
|
|
373
|
+
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);
|
|
312
374
|
} catch {
|
|
313
375
|
}
|
|
314
376
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
377
|
+
/**
|
|
378
|
+
* Write override values into this._cfg (flat fields only; rateLimiting is merged).
|
|
379
|
+
*/
|
|
380
|
+
_applyOverrides(e) {
|
|
381
|
+
for (const [t, a] of Object.entries(e))
|
|
382
|
+
t === "rateLimiting" && typeof a == "object" ? this._cfg.rateLimiting = { ...this._cfg.rateLimiting, ...a } : this._cfg[t] = a;
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Update live DOM elements to reflect the current this._cfg.
|
|
386
|
+
* Safe to call before or after mounting.
|
|
387
|
+
*/
|
|
388
|
+
_applyConfig() {
|
|
389
|
+
var s, i, r, n, o, d, c;
|
|
390
|
+
const e = this._cfg.theme === "dark" || this._cfg.theme === "auto" && ((s = window.matchMedia) == null ? void 0 : s.call(window, "(prefers-color-scheme: dark)").matches);
|
|
391
|
+
(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);
|
|
392
|
+
const t = (d = this._shadow) == null ? void 0 : d.querySelector(".cd-header-avatar");
|
|
393
|
+
t && (t.textContent = this._cfg.botEmoji);
|
|
394
|
+
const a = (c = this._shadow) == null ? void 0 : c.querySelector(".cd-header-name");
|
|
395
|
+
a && (a.textContent = this._cfg.botName), this._textarea && (this._textarea.placeholder = this._cfg.placeholder);
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Silently refresh config from server in the background.
|
|
399
|
+
* Called each time the panel opens so dashboard changes
|
|
400
|
+
* are reflected without reloading the page.
|
|
401
|
+
* Uses a debounce flag to avoid concurrent fetches.
|
|
402
|
+
*/
|
|
403
|
+
_silentRefresh() {
|
|
404
|
+
!this._cfg.widgetId || this._refreshPending || (this._refreshPending = !0, this._fetchWidgetConfig().then(() => this._applyConfig()).catch(() => {
|
|
405
|
+
}).finally(() => {
|
|
406
|
+
this._refreshPending = !1;
|
|
407
|
+
}));
|
|
318
408
|
}
|
|
319
409
|
unmount() {
|
|
320
410
|
this._unbindViewportMetrics(), this._container && (this._container.remove(), this._container = null);
|
|
321
411
|
}
|
|
322
412
|
open() {
|
|
323
413
|
var e, t;
|
|
324
|
-
this._open = !0, this._syncViewportMetrics(), (e = this._panel) == null || e.classList.add("open"), (t = this._textarea) == null || t.focus();
|
|
414
|
+
this._open = !0, this._syncViewportMetrics(), (e = this._panel) == null || e.classList.add("open"), (t = this._textarea) == null || t.focus(), this._silentRefresh();
|
|
325
415
|
}
|
|
326
416
|
close() {
|
|
327
417
|
var e;
|
|
@@ -355,14 +445,14 @@ class f {
|
|
|
355
445
|
i.className = "cd-close-btn", i.innerHTML = p.close, i.setAttribute("aria-label", "Close chat"), s.appendChild(i), this._closeBtn = i;
|
|
356
446
|
const r = document.createElement("div");
|
|
357
447
|
r.className = "cd-messages", r.setAttribute("role", "log"), r.setAttribute("aria-live", "polite"), this._messagesEl = r;
|
|
358
|
-
const
|
|
359
|
-
|
|
360
|
-
const
|
|
361
|
-
|
|
448
|
+
const n = document.createElement("div");
|
|
449
|
+
n.className = "cd-input-area";
|
|
450
|
+
const o = document.createElement("textarea");
|
|
451
|
+
o.className = "cd-textarea", o.rows = 1, o.placeholder = this._cfg.placeholder, this._textarea = o;
|
|
362
452
|
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
|
-
|
|
453
|
+
d.className = "cd-send-btn", d.innerHTML = p.send, d.setAttribute("aria-label", "Send"), this._sendBtn = d, n.appendChild(o), n.appendChild(d);
|
|
454
|
+
const c = document.createElement("div");
|
|
455
|
+
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
456
|
}
|
|
367
457
|
_applyTheme() {
|
|
368
458
|
var t, a, s;
|
|
@@ -446,36 +536,36 @@ class f {
|
|
|
446
536
|
body: JSON.stringify({ ...this._buildRequestBody(), stream: !0 })
|
|
447
537
|
});
|
|
448
538
|
if (!t.ok) {
|
|
449
|
-
const
|
|
450
|
-
throw d.status = t.status, d.rateLimitMessage =
|
|
539
|
+
const o = await t.json().catch(() => ({})), d = new Error(o.message || `HTTP ${t.status}`);
|
|
540
|
+
throw d.status = t.status, d.rateLimitMessage = o.message, d;
|
|
451
541
|
}
|
|
452
542
|
const a = document.createElement("div");
|
|
453
543
|
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
544
|
const s = t.body.getReader(), i = new TextDecoder();
|
|
455
|
-
let r = "",
|
|
545
|
+
let r = "", n = !1;
|
|
456
546
|
try {
|
|
457
547
|
for (; ; ) {
|
|
458
|
-
const { done:
|
|
459
|
-
if (
|
|
460
|
-
const
|
|
461
|
-
for (const g of
|
|
548
|
+
const { done: o, value: d } = await s.read();
|
|
549
|
+
if (o) break;
|
|
550
|
+
const c = i.decode(d, { stream: !0 });
|
|
551
|
+
for (const g of c.split(`
|
|
462
552
|
`)) {
|
|
463
553
|
if (!g.startsWith("data: ")) continue;
|
|
464
|
-
const
|
|
465
|
-
if (
|
|
554
|
+
const f = g.slice(6).trim();
|
|
555
|
+
if (f === "[DONE]") break;
|
|
466
556
|
let h;
|
|
467
557
|
try {
|
|
468
|
-
h = JSON.parse(
|
|
558
|
+
h = JSON.parse(f);
|
|
469
559
|
} catch {
|
|
470
560
|
continue;
|
|
471
561
|
}
|
|
472
|
-
h.type === "content" && h.data && (
|
|
562
|
+
h.type === "content" && h.data && (n || (a.innerHTML = "", n = !0), r += h.data, a.innerHTML = this._renderMarkdown(r), this._scrollToBottom());
|
|
473
563
|
}
|
|
474
564
|
}
|
|
475
565
|
} finally {
|
|
476
566
|
s.releaseLock();
|
|
477
567
|
}
|
|
478
|
-
|
|
568
|
+
n || (a.innerHTML = this._renderMarkdown("No response")), r && this._messages.push({ role: "assistant", content: r });
|
|
479
569
|
}
|
|
480
570
|
/** Non-streaming fallback path */
|
|
481
571
|
async _callApi() {
|
|
@@ -486,8 +576,8 @@ class f {
|
|
|
486
576
|
body: JSON.stringify(this._buildRequestBody())
|
|
487
577
|
});
|
|
488
578
|
if (!t.ok) {
|
|
489
|
-
const
|
|
490
|
-
throw
|
|
579
|
+
const n = await t.json().catch(() => ({})), o = new Error(n.message || `HTTP ${t.status}`);
|
|
580
|
+
throw o.status = t.status, o.rateLimitMessage = n.message, o;
|
|
491
581
|
}
|
|
492
582
|
const a = await t.json();
|
|
493
583
|
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 +613,16 @@ class f {
|
|
|
523
613
|
return String(e).replace(/[&<>"']/g, (t) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[t]);
|
|
524
614
|
}
|
|
525
615
|
}
|
|
526
|
-
function
|
|
527
|
-
const e = new
|
|
616
|
+
function v(l) {
|
|
617
|
+
const e = new m(l);
|
|
528
618
|
return e.mount(), e;
|
|
529
619
|
}
|
|
530
620
|
if (typeof document < "u") {
|
|
531
|
-
const
|
|
621
|
+
const l = () => {
|
|
532
622
|
document.querySelectorAll("script[data-api-key]").forEach((e) => {
|
|
533
623
|
const t = e.getAttribute("data-api-key");
|
|
534
624
|
if (!t) return;
|
|
535
|
-
const a = e.getAttribute("data-widget-id") || void 0, s = e.getAttribute("data-streaming"), i = new
|
|
625
|
+
const a = e.getAttribute("data-widget-id") || void 0, s = e.getAttribute("data-streaming"), i = new m({
|
|
536
626
|
apiKey: t,
|
|
537
627
|
widgetId: a,
|
|
538
628
|
assistantId: e.getAttribute("data-assistant-id") || void 0,
|
|
@@ -548,10 +638,10 @@ if (typeof document < "u") {
|
|
|
548
638
|
Promise.resolve(i.mount());
|
|
549
639
|
});
|
|
550
640
|
};
|
|
551
|
-
document.readyState === "loading" ? document.addEventListener("DOMContentLoaded",
|
|
641
|
+
document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", l) : l();
|
|
552
642
|
}
|
|
553
643
|
export {
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
644
|
+
m as CognitionDeskWidget,
|
|
645
|
+
m as default,
|
|
646
|
+
v as init
|
|
557
647
|
};
|
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(()=>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(){this._cfg.widgetId&&(await this._fetchWidgetConfig(),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.ok)return;const{config:s}=await i.json();if(!s)return;const a=(r,n)=>{n!=null&&!this._userSet(r)&&(this._cfg[r]=n)};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)}catch{}}_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(()=>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.2",
|
|
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
|
|