copilot-chat-widget 0.1.4 → 0.1.5

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/README.md CHANGED
@@ -22,6 +22,7 @@ loadCopilotChatWidget({ token: "YOUR_WIDGET_TOKEN" });
22
22
  ```
23
23
 
24
24
  Hàm này se gắn script `chat-widget.min.js` (IIFE) vào trang, đọc `token` từ options (hoặc `window.CopilotChatConfig` nếu bạn set trước).
25
+ - Nếu backend widget không cùng origin với web hiện tại, truyền thêm `baseUrl: "https://your-backend-domain"` để gọi đúng API.
25
26
 
26
27
  ### React/Next.js
27
28
 
@@ -32,7 +33,8 @@ import { loadCopilotChatWidget } from "copilot-chat-widget";
32
33
  export default function Page() {
33
34
  useEffect(() => {
34
35
  loadCopilotChatWidget({
35
- token: process.env.NEXT_PUBLIC_CHAT_WIDGET_TOKEN
36
+ token: process.env.NEXT_PUBLIC_CHAT_WIDGET_TOKEN,
37
+ baseUrl: process.env.NEXT_PUBLIC_WIDGET_BASE_URL // nếu backend khác origin
36
38
  });
37
39
  }, []);
38
40
 
@@ -56,6 +58,7 @@ export default function Page() {
56
58
 
57
59
  - `token` (string, bat buoc): ma token widget.
58
60
  - `autoload` (boolean): dat `false` neu muon tu goi `window.CopilotChat.load()`.
61
+ - `baseUrl` (string): host/backend cung cấp API `/api/chat-widget/config`. Can khi web embed khac origin.
59
62
 
60
63
  Khi script da load, co the goi:
61
64
 
@@ -1,7 +1,7 @@
1
- (function(){"use strict";(()=>{if(typeof window>"u"||typeof document>"u"||window.__copilotWidgetLoaded)return;window.__copilotWidgetLoaded=!0;const m=64,C=720,y=document.currentScript,x=t=>{if(!t?.src)return{};try{const e=new URL(t.src,window.location.href),r={};return e.searchParams.forEach((a,s)=>{r[s]=a}),r}catch(e){return console.warn("[CopilotChat] Failed to parse script query params:",e),{}}},E=t=>{document.readyState==="complete"||document.readyState==="interactive"?setTimeout(t,0):document.addEventListener("DOMContentLoaded",t)},S=t=>{if(!t?.src)return null;try{const e=new URL(t.src,window.location.href);return`${e.protocol}//${e.host}`}catch(e){return console.warn("[CopilotChat] Unable to infer base URL from script source.",e),null}},k=()=>y||Array.from(document.querySelectorAll("script")).reverse().find(r=>r.dataset?.token||r.src&&r.src.includes("chat-widget"))||null,U=({iframeUrl:t,launcherIcon:e})=>{const r=document.querySelector("[data-copilot-widget-root]");r&&r.remove();const a=document.createElement("div");a.setAttribute("data-copilot-widget-root","true");const s=a.attachShadow({mode:"open"});document.body.appendChild(a);const o=document.createElement("button");o.type="button",o.setAttribute("aria-label","Open Copilot chat"),o.innerHTML=`
1
+ (function(){"use strict";(()=>{if(typeof window>"u"||typeof document>"u"||window.__copilotWidgetLoaded)return;window.__copilotWidgetLoaded=!0;const m=64,C=720,y=document.currentScript,x=t=>{if(!t?.src)return{};try{const o=new URL(t.src,window.location.href),n={};return o.searchParams.forEach((a,s)=>{n[s]=a}),n}catch(o){return console.warn("[CopilotChat] Failed to parse script query params:",o),{}}},E=t=>{document.readyState==="complete"||document.readyState==="interactive"?setTimeout(t,0):document.addEventListener("DOMContentLoaded",t)},U=t=>{if(!t?.src)return null;try{const o=new URL(t.src,window.location.href);return`${o.protocol}//${o.host}`}catch(o){return console.warn("[CopilotChat] Unable to infer base URL from script source.",o),null}},k=()=>y||Array.from(document.querySelectorAll("script")).reverse().find(n=>n.dataset?.token||n.src&&n.src.includes("chat-widget"))||null,S=({iframeUrl:t,launcherIcon:o})=>{const n=document.querySelector("[data-copilot-widget-root]");n&&n.remove();const a=document.createElement("div");a.setAttribute("data-copilot-widget-root","true");const s=a.attachShadow({mode:"open"});document.body.appendChild(a);const e=document.createElement("button");e.type="button",e.setAttribute("aria-label","Open Copilot chat"),e.innerHTML=`
2
2
  <img
3
- src="${e}"
3
+ src="${o}"
4
4
  alt="Copilot chat launcher"
5
5
  style="width: 38px; height: 38px; object-fit: contain; border-radius: 50%; pointer-events: none;"
6
6
  />
7
- `,Object.assign(o.style,{position:"fixed",bottom:"24px",right:"24px",width:`${m}px`,height:`${m}px`,borderRadius:"50%",border:"none",background:"linear-gradient(135deg, #0078ff, #00c6ff)",color:"white",cursor:"pointer",zIndex:999998,display:"flex",alignItems:"center",justifyContent:"center",boxShadow:"0 6px 14px rgba(0,0,0,0.25)",transition:"all 0.25s ease"}),o.onmouseover=()=>{o.style.transform="scale(1.12)",o.style.boxShadow="0 10px 25px rgba(0,0,0,0.3)"},o.onmouseout=()=>{o.style.transform="scale(1)",o.style.boxShadow="0 6px 14px rgba(0,0,0,0.25)"};const n=document.createElement("div");n.setAttribute("data-copilot-widget-root","true"),Object.assign(n.style,{display:"none",position:"fixed",bottom:`${m+36}px`,right:"24px",zIndex:"999999",transformOrigin:"bottom right",transform:"scale(0.8) translateY(20px)",opacity:"0",transition:"all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55)"});const i=document.createElement("div"),l=document.createElement("div"),g=document.createElement("div");Object.assign(i.style,{position:"absolute",bottom:"-14px",right:"28px",width:"30px",height:"20px",pointerEvents:"none",display:"none",zIndex:"1"}),Object.assign(l.style,{position:"absolute",bottom:"0",left:"0",right:"0",margin:"0 auto",width:"0",height:"0",borderLeft:"15px solid transparent",borderRight:"15px solid transparent",borderTop:"15px solid rgba(15,23,42,0.1)"}),Object.assign(g.style,{position:"absolute",bottom:"2px",left:"0",right:"0",margin:"0 auto",width:"0",height:"0",borderLeft:"13px solid transparent",borderRight:"13px solid transparent",borderTop:"13px solid white",boxShadow:"0 6px 16px rgba(15,23,42,0.12)",borderRadius:"2px"}),i.appendChild(l),i.appendChild(g);const d=document.createElement("div");Object.assign(d.style,{width:`${C}px`,maxWidth:"calc(100vw - 48px)",height:`${Math.min(C,Math.max(320,window.innerHeight-140))}px`,maxHeight:"calc(100vh - 150px)",borderRadius:"20px",background:"white",border:"1px solid rgba(15,23,42,0.12)",boxShadow:"0 18px 45px rgba(15,23,42,0.16)",overflow:"hidden"}),s.appendChild(d);const c=document.createElement("iframe");c.src=t,c.title="Copilot chat widget",c.allow="clipboard-read; clipboard-write; microphone; camera; display-capture",c.setAttribute("scrolling","no"),Object.assign(c.style,{width:"100%",height:"100%",border:"none",display:"block",background:"transparent",overflow:"hidden"}),s.appendChild(d),d.appendChild(c),n.appendChild(i),n.appendChild(d),document.body.appendChild(o),document.body.appendChild(n);let p=!1;const u=()=>{p&&(p=!1,n.style.opacity="0",n.style.transform="scale(0.8) translateY(20px)",i.style.display="none",setTimeout(()=>{n.style.display="none"},250),o.style.transform="scale(1)")};o.onclick=h=>{h.stopPropagation(),p=!p,p?(n.style.display="block",i.style.display="block",requestAnimationFrame(()=>{n.style.opacity="1",n.style.transform="scale(1) translateY(0)"})):u()},window.addEventListener("message",h=>{h?.data?.type==="CHAT_CLOSED"&&u()}),document.addEventListener("click",h=>{const b=h.target;b&&p&&!n.contains(b)&&b!==o&&u()});const w={close:u,open:()=>{p||o.click()}};return window.CopilotChat=window.CopilotChat||{},window.CopilotChat.close=w.close,window.CopilotChat.open=w.open,window.CopilotChat.controls=w,w},f=t=>{console.error(`[CopilotChat] ${t}`)},L=()=>{const t=k();if(!t)return!1;const e=x(t),r=t.dataset||{},a=r.token||e.token,s=typeof window<"u"&&window.CopilotChatConfig&&window.CopilotChatConfig.token;return(a||s)&&r.autoload!=="false"},v=async(t={})=>{const e=t.scriptEl||k(),r=window.CopilotChatConfig||{},a=e?.dataset||{},s=x(e),o=t.token||r.token||a.token||s.token;if(!o)return f("Missing token (provide via init config, window.CopilotChatConfig.token, or data-token attribute)."),null;const n=S(e)||typeof window<"u"&&window.location?.origin||null;if(!n)return f("Unable to resolve base URL from embedding script or window.location."),null;try{const i=await fetch(`${n}/api/chat-widget/config?token=${encodeURIComponent(o)}`,{credentials:"omit",mode:"cors"});if(!i.ok)throw new Error(`Server responded with ${i.status}`);const l=await i.json();if(!l?.iframeUrl||!l?.launcherIcon)throw new Error("Received incomplete widget configuration from server.");return window.CopilotChat=window.CopilotChat||{},window.CopilotChat.init=(d={})=>{const c={iframeUrl:d.iframeUrl||l.iframeUrl,launcherIcon:d.launcherIcon||l.launcherIcon};return U(c)},window.CopilotChat.init(t)}catch(i){return f(i instanceof Error?i.message:"Unknown error during widget bootstrap."),null}};L()&&E(()=>{v()}),window.CopilotChat=window.CopilotChat||{},window.CopilotChat.load=v})()})();
7
+ `,Object.assign(e.style,{position:"fixed",bottom:"24px",right:"24px",width:`${m}px`,height:`${m}px`,borderRadius:"50%",border:"none",background:"linear-gradient(135deg, #0078ff, #00c6ff)",color:"white",cursor:"pointer",zIndex:999998,display:"flex",alignItems:"center",justifyContent:"center",boxShadow:"0 6px 14px rgba(0,0,0,0.25)",transition:"all 0.25s ease"}),e.onmouseover=()=>{e.style.transform="scale(1.12)",e.style.boxShadow="0 10px 25px rgba(0,0,0,0.3)"},e.onmouseout=()=>{e.style.transform="scale(1)",e.style.boxShadow="0 6px 14px rgba(0,0,0,0.25)"};const r=document.createElement("div");r.setAttribute("data-copilot-widget-root","true"),Object.assign(r.style,{display:"none",position:"fixed",bottom:`${m+36}px`,right:"24px",zIndex:"999999",transformOrigin:"bottom right",transform:"scale(0.8) translateY(20px)",opacity:"0",transition:"all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55)"});const i=document.createElement("div"),c=document.createElement("div"),b=document.createElement("div");Object.assign(i.style,{position:"absolute",bottom:"-14px",right:"28px",width:"30px",height:"20px",pointerEvents:"none",display:"none",zIndex:"1"}),Object.assign(c.style,{position:"absolute",bottom:"0",left:"0",right:"0",margin:"0 auto",width:"0",height:"0",borderLeft:"15px solid transparent",borderRight:"15px solid transparent",borderTop:"15px solid rgba(15,23,42,0.1)"}),Object.assign(b.style,{position:"absolute",bottom:"2px",left:"0",right:"0",margin:"0 auto",width:"0",height:"0",borderLeft:"13px solid transparent",borderRight:"13px solid transparent",borderTop:"13px solid white",boxShadow:"0 6px 16px rgba(15,23,42,0.12)",borderRadius:"2px"}),i.appendChild(c),i.appendChild(b);const d=document.createElement("div");Object.assign(d.style,{width:`${C}px`,maxWidth:"calc(100vw - 48px)",height:`${Math.min(C,Math.max(320,window.innerHeight-140))}px`,maxHeight:"calc(100vh - 150px)",borderRadius:"20px",background:"white",border:"1px solid rgba(15,23,42,0.12)",boxShadow:"0 18px 45px rgba(15,23,42,0.16)",overflow:"hidden"}),s.appendChild(d);const l=document.createElement("iframe");l.src=t,l.title="Copilot chat widget",l.allow="clipboard-read; clipboard-write; microphone; camera; display-capture",l.setAttribute("scrolling","no"),Object.assign(l.style,{width:"100%",height:"100%",border:"none",display:"block",background:"transparent",overflow:"hidden"}),s.appendChild(d),d.appendChild(l),r.appendChild(i),r.appendChild(d),document.body.appendChild(e),document.body.appendChild(r);let p=!1;const u=()=>{p&&(p=!1,r.style.opacity="0",r.style.transform="scale(0.8) translateY(20px)",i.style.display="none",setTimeout(()=>{r.style.display="none"},250),e.style.transform="scale(1)")};e.onclick=h=>{h.stopPropagation(),p=!p,p?(r.style.display="block",i.style.display="block",requestAnimationFrame(()=>{r.style.opacity="1",r.style.transform="scale(1) translateY(0)"})):u()},window.addEventListener("message",h=>{h?.data?.type==="CHAT_CLOSED"&&u()}),document.addEventListener("click",h=>{const g=h.target;g&&p&&!r.contains(g)&&g!==e&&u()});const w={close:u,open:()=>{p||e.click()}};return window.CopilotChat=window.CopilotChat||{},window.CopilotChat.close=w.close,window.CopilotChat.open=w.open,window.CopilotChat.controls=w,w},f=t=>{console.error(`[CopilotChat] ${t}`)},L=()=>{const t=k();if(!t)return!1;const o=x(t),n=t.dataset||{},a=n.token||o.token,s=typeof window<"u"&&window.CopilotChatConfig&&window.CopilotChatConfig.token;return(a||s)&&n.autoload!=="false"},v=async(t={})=>{const o=t.scriptEl||k(),n=window.CopilotChatConfig||{},a=o?.dataset||{},s=x(o),e=t.token||n.token||a.token||s.token;if(!e)return f("Missing token (provide via init config, window.CopilotChatConfig.token, or data-token attribute)."),null;const r=t.baseUrl||n.baseUrl||a.baseUrl||s.baseUrl||U(o)||typeof window<"u"&&window.location?.origin||null;if(!r)return f("Unable to resolve base URL from embedding script or window.location."),null;try{const i=await fetch(`${r}/api/chat-widget/config?token=${encodeURIComponent(e)}`,{credentials:"omit",mode:"cors"});if(!i.ok)throw new Error(`Server responded with ${i.status}`);const c=await i.json();if(!c?.iframeUrl||!c?.launcherIcon)throw new Error("Received incomplete widget configuration from server.");return window.CopilotChat=window.CopilotChat||{},window.CopilotChat.init=(d={})=>{const l={iframeUrl:d.iframeUrl||c.iframeUrl,launcherIcon:d.launcherIcon||c.launcherIcon};return S(l)},window.CopilotChat.init(t)}catch(i){return f(i instanceof Error?i.message:"Unknown error during widget bootstrap."),null}};L()&&E(()=>{v()}),window.CopilotChat=window.CopilotChat||{},window.CopilotChat.load=v})()})();
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});var n=typeof document<"u"?document.currentScript:null;function i(d={}){if(typeof window>"u"||typeof document>"u")return null;const{token:t,autoload:a}=d;window.CopilotChatConfig={...window.CopilotChatConfig,...d};const o=document.getElementById("copilot-chat-widget-loader");if(o)return t&&(o.dataset.token=t),a===!1&&(o.dataset.autoload="false"),o;const e=document.createElement("script");return e.id="copilot-chat-widget-loader",e.src=new URL("./chat-widget.min.js",typeof document>"u"?require("url").pathToFileURL(__filename).href:n&&n.tagName.toUpperCase()==="SCRIPT"&&n.src||new URL("index.cjs",document.baseURI).href).href,e.async=!0,t&&(e.dataset.token=t),a===!1&&(e.dataset.autoload="false"),document.body.appendChild(e),e}exports.default=i;exports.loadCopilotChatWidget=i;
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});var d=typeof document<"u"?document.currentScript:null;function l(n={}){if(typeof window>"u"||typeof document>"u")return null;const{token:o,autoload:i,baseUrl:a}=n;window.CopilotChatConfig={...window.CopilotChatConfig,...n};const t=document.getElementById("copilot-chat-widget-loader");if(t)return o&&(t.dataset.token=o),a&&(t.dataset.baseUrl=a),i===!1&&(t.dataset.autoload="false"),t;const e=document.createElement("script");return e.id="copilot-chat-widget-loader",e.src=new URL("./chat-widget.min.js",typeof document>"u"?require("url").pathToFileURL(__filename).href:d&&d.tagName.toUpperCase()==="SCRIPT"&&d.src||new URL("index.cjs",document.baseURI).href).href,e.async=!0,o&&(e.dataset.token=o),a&&(e.dataset.baseUrl=a),i===!1&&(e.dataset.autoload="false"),document.body.appendChild(e),e}exports.default=l;exports.loadCopilotChatWidget=l;
package/dist/index.mjs CHANGED
@@ -1,16 +1,16 @@
1
- function n(a = {}) {
1
+ function n(d = {}) {
2
2
  if (typeof window > "u" || typeof document > "u")
3
3
  return null;
4
- const { token: e, autoload: d } = a;
4
+ const { token: a, autoload: i, baseUrl: o } = d;
5
5
  window.CopilotChatConfig = {
6
6
  ...window.CopilotChatConfig,
7
- ...a
7
+ ...d
8
8
  };
9
- const o = document.getElementById("copilot-chat-widget-loader");
10
- if (o)
11
- return e && (o.dataset.token = e), d === !1 && (o.dataset.autoload = "false"), o;
9
+ const e = document.getElementById("copilot-chat-widget-loader");
10
+ if (e)
11
+ return a && (e.dataset.token = a), o && (e.dataset.baseUrl = o), i === !1 && (e.dataset.autoload = "false"), e;
12
12
  const t = document.createElement("script");
13
- return t.id = "copilot-chat-widget-loader", t.src = new URL("./chat-widget.min.js", import.meta.url).href, t.async = !0, e && (t.dataset.token = e), d === !1 && (t.dataset.autoload = "false"), document.body.appendChild(t), t;
13
+ return t.id = "copilot-chat-widget-loader", t.src = new URL("./chat-widget.min.js", import.meta.url).href, t.async = !0, a && (t.dataset.token = a), o && (t.dataset.baseUrl = o), i === !1 && (t.dataset.autoload = "false"), document.body.appendChild(t), t;
14
14
  }
15
15
  export {
16
16
  n as default,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "copilot-chat-widget",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Embeddable Copilot chat widget that can be loaded via NPM or a script tag.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
package/src/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export interface CopilotChatOptions {
2
2
  token: string;
3
3
  autoload?: boolean;
4
+ baseUrl?: string;
4
5
  }
5
6
 
6
7
  export function loadCopilotChatWidget(options: CopilotChatOptions): HTMLScriptElement | null;
package/src/index.js CHANGED
@@ -3,7 +3,7 @@ export function loadCopilotChatWidget(options = {}) {
3
3
  return null;
4
4
  }
5
5
 
6
- const { token, autoload } = options;
6
+ const { token, autoload, baseUrl } = options;
7
7
 
8
8
  // persist config for runtime script
9
9
  window.CopilotChatConfig = {
@@ -14,6 +14,7 @@ export function loadCopilotChatWidget(options = {}) {
14
14
  const existing = document.getElementById("copilot-chat-widget-loader");
15
15
  if (existing) {
16
16
  if (token) existing.dataset.token = token;
17
+ if (baseUrl) existing.dataset.baseUrl = baseUrl;
17
18
  if (autoload === false) existing.dataset.autoload = "false";
18
19
  return existing;
19
20
  }
@@ -23,6 +24,7 @@ export function loadCopilotChatWidget(options = {}) {
23
24
  script.src = new URL("./chat-widget.min.js", import.meta.url).href;
24
25
  script.async = true;
25
26
  if (token) script.dataset.token = token;
27
+ if (baseUrl) script.dataset.baseUrl = baseUrl;
26
28
  if (autoload === false) script.dataset.autoload = "false";
27
29
 
28
30
  document.body.appendChild(script);
@@ -291,6 +291,10 @@
291
291
  }
292
292
 
293
293
  const baseUrl =
294
+ config.baseUrl ||
295
+ globalConfig.baseUrl ||
296
+ datasetConfig.baseUrl ||
297
+ queryConfig.baseUrl ||
294
298
  inferBaseUrlFromScript(scriptEl) ||
295
299
  (typeof window !== "undefined" && window.location?.origin) ||
296
300
  null;