dunefox-chatbot 1.0.0 → 1.0.1
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 +1 -1
- package/dist/chatbot.umd.global.js +49 -7
- package/dist/index.js +49 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +49 -8
- package/dist/index.mjs.map +1 -1
- package/dist/react.js +51 -10
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +51 -10
- package/dist/react.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
"use strict";var DunefoxChat=(()=>{var
|
|
1
|
+
"use strict";var DunefoxChat=(()=>{var u=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var S=Object.prototype.hasOwnProperty;var C=(e,t)=>{for(var n in t)u(e,n,{get:t[n],enumerable:!0})},O=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of I(t))!S.call(e,r)&&r!==n&&u(e,r,{get:()=>t[r],enumerable:!(o=B(t,r))||o.enumerable});return e};var _=e=>O(u({},"__esModule",{value:!0}),e);var M={};C(M,{close:()=>w,destroy:()=>E,init:()=>y,open:()=>v});var k="https://app.dunefox.io",A="https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png",g="dunefox_chat_uuid";function $(){var e,t;try{let n=localStorage.getItem(g);return n||(n=(t=(e=crypto==null?void 0:crypto.randomUUID)==null?void 0:e.call(crypto))!=null?t:Math.random().toString(36).slice(2)+Date.now().toString(36),localStorage.setItem(g,n)),n}catch(n){return Math.random().toString(36).slice(2)+Date.now().toString(36)}}function D(e){if(document.getElementById("dunefox-styles"))return;let t=e.startsWith("top"),o=e.endsWith("left")?"left:16px":"right:16px",m=`
|
|
2
2
|
#dunefox-btn {
|
|
3
|
-
position: fixed; ${t?"top:16px":"bottom:16px"}; ${
|
|
3
|
+
position: fixed; ${t?"top:16px":"bottom:16px"}; ${o};
|
|
4
4
|
width: 60px; height: 60px; border-radius: 50%;
|
|
5
5
|
background: #1a1a1a; border: none; cursor: pointer;
|
|
6
6
|
display: flex; align-items: center; justify-content: center;
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
}
|
|
11
11
|
#dunefox-btn:hover { transform: ${t?"translateY(2px) scale(1.05)":"translateY(-2px) scale(1.05)"}; }
|
|
12
12
|
#dunefox-frame {
|
|
13
|
-
position: fixed; ${t?"top:88px":"bottom:88px"}; ${
|
|
13
|
+
position: fixed; ${t?"top:88px":"bottom:88px"}; ${o};
|
|
14
14
|
width: 350px; height: calc(100vh - 160px); max-height: 600px;
|
|
15
15
|
border-radius: 16px; border: none; z-index: 2147483645;
|
|
16
16
|
box-shadow: 0 8px 32px rgba(0,0,0,.12);
|
|
@@ -21,12 +21,54 @@
|
|
|
21
21
|
#dunefox-frame.df-open {
|
|
22
22
|
opacity: 1; visibility: visible; transform: translateY(0) scale(1);
|
|
23
23
|
}
|
|
24
|
+
|
|
25
|
+
/* \u2500\u2500 Mobile close bar (hidden on desktop) \u2500\u2500 */
|
|
26
|
+
#dunefox-mobile-bar {
|
|
27
|
+
display: none;
|
|
28
|
+
}
|
|
29
|
+
|
|
24
30
|
@media (max-width: 768px) {
|
|
31
|
+
/* Mobile close bar */
|
|
32
|
+
#dunefox-mobile-bar {
|
|
33
|
+
position: fixed;
|
|
34
|
+
top: 0 !important; left: 0 !important; right: 0 !important;
|
|
35
|
+
height: 48px;
|
|
36
|
+
background: #1a1a1a;
|
|
37
|
+
z-index: 2147483647;
|
|
38
|
+
align-items: center;
|
|
39
|
+
padding: 0 16px;
|
|
40
|
+
gap: 10px;
|
|
41
|
+
border: none;
|
|
42
|
+
width: 100%;
|
|
43
|
+
cursor: pointer;
|
|
44
|
+
box-sizing: border-box;
|
|
45
|
+
}
|
|
46
|
+
#dunefox-mobile-bar.df-open { display: flex; }
|
|
47
|
+
|
|
48
|
+
/* Hide the corner toggle button on mobile when chat is open */
|
|
49
|
+
#dunefox-btn.df-hidden { display: none !important; }
|
|
50
|
+
|
|
51
|
+
/* Full-screen iframe, leaving room for close bar at top */
|
|
25
52
|
#dunefox-frame {
|
|
26
|
-
width: 100vw
|
|
27
|
-
|
|
28
|
-
|
|
53
|
+
width: 100vw !important;
|
|
54
|
+
height: calc(100% - 48px) !important;
|
|
55
|
+
height: calc(100dvh - 48px) !important;
|
|
56
|
+
max-height: none !important;
|
|
57
|
+
top: 48px !important;
|
|
58
|
+
bottom: 0 !important;
|
|
59
|
+
left: 0 !important;
|
|
60
|
+
right: 0 !important;
|
|
61
|
+
border-radius: 0;
|
|
62
|
+
transform: translateY(100%);
|
|
63
|
+
opacity: 1 !important;
|
|
64
|
+
visibility: visible !important;
|
|
65
|
+
box-shadow: none;
|
|
29
66
|
}
|
|
30
67
|
#dunefox-frame.df-open { transform: translateY(0); }
|
|
31
68
|
}
|
|
32
|
-
`,
|
|
69
|
+
`,i=document.createElement("style");i.id="dunefox-styles",i.textContent=m,document.head.appendChild(i)}var U='<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>',H='<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5"/><path d="m12 5-7 7 7 7"/></svg>',x=!1;function y(e){let t=typeof e=="string"?{tenantId:e}:e;if(x){console.warn("[DunefoxChat] Already initialised. Call destroy() first.");return}let{tenantId:n,position:o="bottom-right",defaultOpen:r=!1,baseUrl:h=k,iconUrl:b=A}=t;if(!n)throw new Error("[DunefoxChat] tenantId is required.");(c=>document.readyState==="loading"?document.addEventListener("DOMContentLoaded",c,{once:!0}):c())(()=>{D(o);let c=$(),m=`${h}/api/${n}?uuid=${c}`,i=document.createElement("iframe");i.id="dunefox-frame",i.title="Dunefox Chat",i.loading="lazy",i.src=m;let s=document.createElement("button");s.id="dunefox-btn",s.setAttribute("aria-label","Open chat"),s.setAttribute("aria-expanded","false");let d=document.createElement("img");d.src=b,d.alt="",d.width=60,d.height=60,d.style.cssText="display:block;border-radius:50%;object-fit:cover;";let p=document.createElement("span");p.innerHTML=U,p.style.display="none",s.append(d,p);let l=document.createElement("button");l.id="dunefox-mobile-bar",l.setAttribute("aria-label","Close chat"),l.innerHTML=`
|
|
70
|
+
${H}
|
|
71
|
+
<span style="color:white;font-family:system-ui,-apple-system,sans-serif;font-size:14px;font-weight:600;letter-spacing:.01em;">
|
|
72
|
+
Close chat
|
|
73
|
+
</span>
|
|
74
|
+
`;let a=r,f=()=>{i.classList.toggle("df-open",a),l.classList.toggle("df-open",a),s.classList.toggle("df-hidden",a),d.style.display=a?"none":"block",p.style.display=a?"block":"none",s.setAttribute("aria-expanded",String(a))};s.addEventListener("click",()=>{a=!a,f()}),l.addEventListener("click",()=>{a=!1,f()}),document.body.append(i,l,s),f(),x=!0})}function v(){let e=document.getElementById("dunefox-frame"),t=document.getElementById("dunefox-btn"),n=document.getElementById("dunefox-mobile-bar");!e||!t||(e.classList.add("df-open"),n==null||n.classList.add("df-open"),t.classList.add("df-hidden"),t.setAttribute("aria-expanded","true"))}function w(){let e=document.getElementById("dunefox-frame"),t=document.getElementById("dunefox-btn"),n=document.getElementById("dunefox-mobile-bar");!e||!t||(e.classList.remove("df-open"),n==null||n.classList.remove("df-open"),t.classList.remove("df-hidden"),t.setAttribute("aria-expanded","false"))}function E(){var e,t,n,o;(e=document.getElementById("dunefox-frame"))==null||e.remove(),(t=document.getElementById("dunefox-btn"))==null||t.remove(),(n=document.getElementById("dunefox-mobile-bar"))==null||n.remove(),(o=document.getElementById("dunefox-styles"))==null||o.remove(),x=!1}return _(M);})();
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
'use strict';var
|
|
1
|
+
'use strict';var y="https://app.dunefox.io",v="https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png",b="dunefox_chat_uuid";function w(){var n,t;try{let e=localStorage.getItem(b);return e||(e=(t=(n=crypto==null?void 0:crypto.randomUUID)==null?void 0:n.call(crypto))!=null?t:Math.random().toString(36).slice(2)+Date.now().toString(36),localStorage.setItem(b,e)),e}catch(e){return Math.random().toString(36).slice(2)+Date.now().toString(36)}}function E(n){if(document.getElementById("dunefox-styles"))return;let t=n.startsWith("top"),r=n.endsWith("left")?"left:16px":"right:16px",p=`
|
|
2
2
|
#dunefox-btn {
|
|
3
|
-
position: fixed; ${t?"top:16px":"bottom:16px"}; ${
|
|
3
|
+
position: fixed; ${t?"top:16px":"bottom:16px"}; ${r};
|
|
4
4
|
width: 60px; height: 60px; border-radius: 50%;
|
|
5
5
|
background: #1a1a1a; border: none; cursor: pointer;
|
|
6
6
|
display: flex; align-items: center; justify-content: center;
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
}
|
|
11
11
|
#dunefox-btn:hover { transform: ${t?"translateY(2px) scale(1.05)":"translateY(-2px) scale(1.05)"}; }
|
|
12
12
|
#dunefox-frame {
|
|
13
|
-
position: fixed; ${t?"top:88px":"bottom:88px"}; ${
|
|
13
|
+
position: fixed; ${t?"top:88px":"bottom:88px"}; ${r};
|
|
14
14
|
width: 350px; height: calc(100vh - 160px); max-height: 600px;
|
|
15
15
|
border-radius: 16px; border: none; z-index: 2147483645;
|
|
16
16
|
box-shadow: 0 8px 32px rgba(0,0,0,.12);
|
|
@@ -21,14 +21,55 @@
|
|
|
21
21
|
#dunefox-frame.df-open {
|
|
22
22
|
opacity: 1; visibility: visible; transform: translateY(0) scale(1);
|
|
23
23
|
}
|
|
24
|
+
|
|
25
|
+
/* \u2500\u2500 Mobile close bar (hidden on desktop) \u2500\u2500 */
|
|
26
|
+
#dunefox-mobile-bar {
|
|
27
|
+
display: none;
|
|
28
|
+
}
|
|
29
|
+
|
|
24
30
|
@media (max-width: 768px) {
|
|
31
|
+
/* Mobile close bar */
|
|
32
|
+
#dunefox-mobile-bar {
|
|
33
|
+
position: fixed;
|
|
34
|
+
top: 0 !important; left: 0 !important; right: 0 !important;
|
|
35
|
+
height: 48px;
|
|
36
|
+
background: #1a1a1a;
|
|
37
|
+
z-index: 2147483647;
|
|
38
|
+
align-items: center;
|
|
39
|
+
padding: 0 16px;
|
|
40
|
+
gap: 10px;
|
|
41
|
+
border: none;
|
|
42
|
+
width: 100%;
|
|
43
|
+
cursor: pointer;
|
|
44
|
+
box-sizing: border-box;
|
|
45
|
+
}
|
|
46
|
+
#dunefox-mobile-bar.df-open { display: flex; }
|
|
47
|
+
|
|
48
|
+
/* Hide the corner toggle button on mobile when chat is open */
|
|
49
|
+
#dunefox-btn.df-hidden { display: none !important; }
|
|
50
|
+
|
|
51
|
+
/* Full-screen iframe, leaving room for close bar at top */
|
|
25
52
|
#dunefox-frame {
|
|
26
|
-
width: 100vw
|
|
27
|
-
|
|
28
|
-
|
|
53
|
+
width: 100vw !important;
|
|
54
|
+
height: calc(100% - 48px) !important;
|
|
55
|
+
height: calc(100dvh - 48px) !important;
|
|
56
|
+
max-height: none !important;
|
|
57
|
+
top: 48px !important;
|
|
58
|
+
bottom: 0 !important;
|
|
59
|
+
left: 0 !important;
|
|
60
|
+
right: 0 !important;
|
|
61
|
+
border-radius: 0;
|
|
62
|
+
transform: translateY(100%);
|
|
63
|
+
opacity: 1 !important;
|
|
64
|
+
visibility: visible !important;
|
|
65
|
+
box-shadow: none;
|
|
29
66
|
}
|
|
30
67
|
#dunefox-frame.df-open { transform: translateY(0); }
|
|
31
68
|
}
|
|
32
|
-
`,o=document.createElement("style");o.id="dunefox-styles",o.textContent=
|
|
33
|
-
|
|
69
|
+
`,o=document.createElement("style");o.id="dunefox-styles",o.textContent=p,document.head.appendChild(o);}var L='<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>',B='<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5"/><path d="m12 5-7 7 7 7"/></svg>',f=false;function I(n){let t=typeof n=="string"?{tenantId:n}:n;if(f){console.warn("[DunefoxChat] Already initialised. Call destroy() first.");return}let{tenantId:e,position:r="bottom-right",defaultOpen:u=false,baseUrl:x=y,iconUrl:h=v}=t;if(!e)throw new Error("[DunefoxChat] tenantId is required.");(l=>document.readyState==="loading"?document.addEventListener("DOMContentLoaded",l,{once:true}):l())(()=>{E(r);let l=w(),p=`${x}/api/${e}?uuid=${l}`,o=document.createElement("iframe");o.id="dunefox-frame",o.title="Dunefox Chat",o.loading="lazy",o.src=p;let a=document.createElement("button");a.id="dunefox-btn",a.setAttribute("aria-label","Open chat"),a.setAttribute("aria-expanded","false");let s=document.createElement("img");s.src=h,s.alt="",s.width=60,s.height=60,s.style.cssText="display:block;border-radius:50%;object-fit:cover;";let c=document.createElement("span");c.innerHTML=L,c.style.display="none",a.append(s,c);let d=document.createElement("button");d.id="dunefox-mobile-bar",d.setAttribute("aria-label","Close chat"),d.innerHTML=`
|
|
70
|
+
${B}
|
|
71
|
+
<span style="color:white;font-family:system-ui,-apple-system,sans-serif;font-size:14px;font-weight:600;letter-spacing:.01em;">
|
|
72
|
+
Close chat
|
|
73
|
+
</span>
|
|
74
|
+
`;let i=u,m=()=>{o.classList.toggle("df-open",i),d.classList.toggle("df-open",i),a.classList.toggle("df-hidden",i),s.style.display=i?"none":"block",c.style.display=i?"block":"none",a.setAttribute("aria-expanded",String(i));};a.addEventListener("click",()=>{i=!i,m();}),d.addEventListener("click",()=>{i=false,m();}),document.body.append(o,d,a),m(),f=true;});}function S(){let n=document.getElementById("dunefox-frame"),t=document.getElementById("dunefox-btn"),e=document.getElementById("dunefox-mobile-bar");!n||!t||(n.classList.add("df-open"),e==null||e.classList.add("df-open"),t.classList.add("df-hidden"),t.setAttribute("aria-expanded","true"));}function C(){let n=document.getElementById("dunefox-frame"),t=document.getElementById("dunefox-btn"),e=document.getElementById("dunefox-mobile-bar");!n||!t||(n.classList.remove("df-open"),e==null||e.classList.remove("df-open"),t.classList.remove("df-hidden"),t.setAttribute("aria-expanded","false"));}function O(){var n,t,e,r;(n=document.getElementById("dunefox-frame"))==null||n.remove(),(t=document.getElementById("dunefox-btn"))==null||t.remove(),(e=document.getElementById("dunefox-mobile-bar"))==null||e.remove(),(r=document.getElementById("dunefox-styles"))==null||r.remove(),f=false;}exports.close=C;exports.destroy=O;exports.init=I;exports.open=S;//# sourceMappingURL=index.js.map
|
|
34
75
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core.ts"],"names":["DEFAULT_BASE","DEFAULT_ICON","UUID_KEY","getOrCreateUUID","_a","_b","id","e","injectStyles","position","isTop","hSide","css","tag","CLOSE_SVG","_initialised","init","options","opts","tenantId","defaultOpen","baseUrl","iconUrl","cb","uuid","src","iframe","btn","img","closeSpan","open","applyState","close","destroy","_c"],"mappings":"aAiBA,IAAMA,CAAAA,CAAe,yBACfC,CAAAA,CAAe,0DAAA,CACfC,EAAW,mBAAA,CAGjB,SAASC,GAA0B,CAtBnC,IAAAC,EAAAC,CAAAA,CAuBE,GAAI,CACF,IAAIC,CAAAA,CAAK,aAAa,OAAA,CAAQJ,CAAQ,CAAA,CACtC,OAAKI,CAAAA,GACHA,CAAAA,CAAAA,CAAKD,GAAAD,CAAAA,CAAA,MAAA,EAAA,IAAA,CAAA,KAAA,CAAA,CAAA,MAAA,CAAQ,aAAR,IAAA,CAAA,KAAA,CAAA,CAAAA,CAAAA,CAAA,oBAAAC,CAAAA,CACA,IAAA,CAAK,QAAO,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA,CAAI,IAAA,CAAK,KAAI,CAAE,QAAA,CAAS,EAAE,CAAA,CACjE,YAAA,CAAa,OAAA,CAAQH,EAAUI,CAAE,CAAA,CAAA,CAE5BA,CACT,CAAA,MAAQC,CAAAA,CAAA,CAEN,OAAO,IAAA,CAAK,QAAO,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA,CAAI,IAAA,CAAK,KAAI,CAAE,QAAA,CAAS,EAAE,CACrE,CACF,CAGA,SAASC,CAAAA,CAAaC,CAAAA,CAA2E,CAC/F,GAAI,QAAA,CAAS,eAAe,gBAAgB,CAAA,CAAG,OAE/C,IAAMC,CAAAA,CAAUD,EAAS,UAAA,CAAW,KAAK,EAEnCE,CAAAA,CADUF,CAAAA,CAAS,SAAS,MAAM,CAAA,CACd,WAAA,CAAgB,YAAA,CAWpCG,CAAAA,CAAM;AAAA;AAAA,uBAAA,EAVIF,CAAAA,CAAU,UAAA,CAAgB,aAYf,CAAA,EAAA,EAAKC,CAAK,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oCAAA,EANdD,CAAAA,CAAQ,8BAAgC,8BAcb,CAAA;AAAA;AAAA,uBAAA,EAlBlCA,CAAAA,CAAU,UAAA,CAAgB,aAoBb,CAAA,EAAA,EAAKC,CAAK,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAAA,EAlBtBD,CAAAA,CAAS,+BAAiC,6BAuBlC,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,+CAAA,EAnBDA,CAAAA,CAAQ,oBAAsB,kBA4BO,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAAA,CAAA,CAMvDG,EAAM,QAAA,CAAS,aAAA,CAAc,OAAO,CAAA,CAC1CA,EAAI,EAAA,CAAK,gBAAA,CACTA,CAAAA,CAAI,WAAA,CAAcD,EAClB,QAAA,CAAS,IAAA,CAAK,WAAA,CAAYC,CAAG,EAC/B,CAGA,IAAMC,CAAAA,CAAY,kOAAA,CAGdC,EAAe,KAAA,CAUZ,SAASC,CAAAA,CAAKC,CAAAA,CAA4C,CAE/D,IAAMC,CAAAA,CACJ,OAAOD,CAAAA,EAAY,SAAW,CAAE,QAAA,CAAUA,CAAQ,CAAA,CAAIA,CAAAA,CAExD,GAAIF,CAAAA,CAAc,CAChB,OAAA,CAAQ,IAAA,CAAK,0DAA0D,CAAA,CACvE,MACF,CAEA,GAAM,CACJ,QAAA,CAAAI,CAAAA,CACA,QAAA,CAAAV,CAAAA,CAAW,eACX,WAAA,CAAAW,CAAAA,CAAc,KAAA,CACd,OAAA,CAAAC,EAAUrB,CAAAA,CACV,OAAA,CAAAsB,CAAAA,CAAUrB,CACZ,EAAIiB,CAAAA,CAEJ,GAAI,CAACC,CAAAA,CAAU,MAAM,IAAI,KAAA,CAAM,qCAAqC,CAAA,CAAA,CAGrDI,GACb,QAAA,CAAS,UAAA,GAAe,UACpB,QAAA,CAAS,gBAAA,CAAiB,mBAAoBA,CAAAA,CAAI,CAAE,IAAA,CAAM,IAAK,CAAC,CAAA,CAChEA,CAAAA,EAAG,EAEH,IAAM,CACVf,CAAAA,CAAaC,CAAQ,CAAA,CAErB,IAAMe,EAAOrB,CAAAA,EAAgB,CACvBsB,CAAAA,CAAM,CAAA,EAAGJ,CAAO,CAAA,KAAA,EAAQF,CAAQ,CAAA,MAAA,EAASK,CAAI,GAG7CE,CAAAA,CAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,EAC9CA,CAAAA,CAAO,EAAA,CAAK,eAAA,CACZA,CAAAA,CAAO,MAAQ,cAAA,CACfA,CAAAA,CAAO,QAAU,MAAA,CACjBA,CAAAA,CAAO,IAAMD,CAAAA,CAGb,IAAME,CAAAA,CAAM,QAAA,CAAS,cAAc,QAAQ,CAAA,CAC3CA,CAAAA,CAAI,EAAA,CAAK,cACTA,CAAAA,CAAI,YAAA,CAAa,YAAA,CAAc,WAAW,EAC1CA,CAAAA,CAAI,YAAA,CAAa,gBAAiB,OAAO,CAAA,CAEzC,IAAMC,CAAAA,CAAM,QAAA,CAAS,aAAA,CAAc,KAAK,EACxCA,CAAAA,CAAI,GAAA,CAAMN,CAAAA,CACVM,CAAAA,CAAI,IAAM,EAAA,CACVA,CAAAA,CAAI,KAAA,CAAQ,EAAA,CACZA,EAAI,MAAA,CAAS,EAAA,CACbA,EAAI,KAAA,CAAM,OAAA,CAAU,oDAEpB,IAAMC,CAAAA,CAAY,QAAA,CAAS,aAAA,CAAc,MAAM,CAAA,CAC/CA,CAAAA,CAAU,SAAA,CAAYf,CAAAA,CACtBe,EAAU,KAAA,CAAM,OAAA,CAAU,MAAA,CAE1BF,CAAAA,CAAI,OAAOC,CAAAA,CAAKC,CAAS,CAAA,CAGzB,IAAIC,EAAOV,CAAAA,CACLW,CAAAA,CAAa,IAAM,CACvBL,EAAO,SAAA,CAAU,MAAA,CAAO,SAAA,CAAWI,CAAI,EACvCF,CAAAA,CAAI,KAAA,CAAM,OAAA,CAAUE,CAAAA,CAAO,OAAS,OAAA,CACpCD,CAAAA,CAAU,MAAM,OAAA,CAAUC,CAAAA,CAAO,QAAU,MAAA,CAC3CH,CAAAA,CAAI,YAAA,CAAa,eAAA,CAAiB,OAAOG,CAAI,CAAC,EAChD,CAAA,CAEAH,EAAI,gBAAA,CAAiB,OAAA,CAAS,IAAM,CAClCG,EAAO,CAACA,CAAAA,CACRC,CAAAA,GACF,CAAC,CAAA,CAED,QAAA,CAAS,IAAA,CAAK,MAAA,CAAOL,EAAQC,CAAG,CAAA,CAChCI,CAAAA,EAAW,CACXhB,EAAe,KACjB,CAAC,EACH,CAKO,SAASe,CAAAA,EAAa,CAC3B,IAAMJ,CAAAA,CAAS,QAAA,CAAS,eAAe,eAAe,CAAA,CAChDC,CAAAA,CAAM,QAAA,CAAS,eAAe,aAAa,CAAA,CAC7C,CAACD,CAAAA,EAAU,CAACC,CAAAA,GAChBD,CAAAA,CAAO,SAAA,CAAU,GAAA,CAAI,SAAS,CAAA,CAC9BC,CAAAA,CAAI,YAAA,CAAa,eAAA,CAAiB,MAAM,CAAA,EAC1C,CAKO,SAASK,CAAAA,EAAc,CAC5B,IAAMN,CAAAA,CAAS,QAAA,CAAS,cAAA,CAAe,eAAe,CAAA,CAChDC,CAAAA,CAAM,QAAA,CAAS,cAAA,CAAe,aAAa,CAAA,CAC7C,CAACD,GAAU,CAACC,CAAAA,GAChBD,EAAO,SAAA,CAAU,MAAA,CAAO,SAAS,CAAA,CACjCC,EAAI,YAAA,CAAa,eAAA,CAAiB,OAAO,CAAA,EAC3C,CAKO,SAASM,CAAAA,EAAgB,CAjNhC,IAAA7B,EAAAC,CAAAA,CAAA6B,CAAAA,CAAAA,CAkNE9B,EAAA,QAAA,CAAS,cAAA,CAAe,eAAe,CAAA,GAAvC,IAAA,EAAAA,CAAAA,CAA0C,MAAA,EAAA,CAAA,CAC1CC,EAAA,QAAA,CAAS,cAAA,CAAe,aAAa,CAAA,GAArC,MAAAA,CAAAA,CAAwC,MAAA,EAAA,CAAA,CACxC6B,CAAAA,CAAA,QAAA,CAAS,eAAe,gBAAgB,CAAA,GAAxC,MAAAA,CAAAA,CAA2C,MAAA,EAAA,CAC3CnB,EAAe,MACjB","file":"index.js","sourcesContent":["/** Options accepted by DunefoxChat.init() */\nexport interface DunefoxChatOptions {\n /** Your Dunefox Tenant ID — found in Settings > Install Widget */\n tenantId: string;\n /**\n * Where to anchor the widget. Defaults to \"bottom-right\".\n * Supports: \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\"\n */\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\n /** Open the chat panel on load. Defaults to false. */\n defaultOpen?: boolean;\n /** Override the base URL (for self-hosted / staging). */\n baseUrl?: string;\n /** Icon image URL override. Falls back to Dunefox CDN asset. */\n iconUrl?: string;\n}\n\nconst DEFAULT_BASE = 'https://app.dunefox.io';\nconst DEFAULT_ICON = 'https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png';\nconst UUID_KEY = 'dunefox_chat_uuid';\n\n// ─── UUID generation ──────────────────────────────────────────────────────────\nfunction getOrCreateUUID(): string {\n try {\n let id = localStorage.getItem(UUID_KEY);\n if (!id) {\n id = crypto?.randomUUID?.()\n ?? Math.random().toString(36).slice(2) + Date.now().toString(36);\n localStorage.setItem(UUID_KEY, id);\n }\n return id;\n } catch {\n // Incognito / storage blocked — generate ephemeral ID\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\n }\n}\n\n// ─── CSS injector ─────────────────────────────────────────────────────────────\nfunction injectStyles(position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'): void {\n if (document.getElementById('dunefox-styles')) return;\n\n const isTop = position.startsWith('top');\n const isLeft = position.endsWith('left');\n const hSide = isLeft ? 'left:16px' : 'right:16px';\n const vBtn = isTop ? 'top:16px' : 'bottom:16px';\n // Panel opens away from the button edge\n const vFrame = isTop ? 'top:88px' : 'bottom:88px';\n // Slide direction: panels below slide up, panels above slide down\n const slideOut = isTop ? 'translateY(-24px) scale(.96)' : 'translateY(24px) scale(.96)';\n // Hover nudge direction\n const hoverTranslate = isTop ? 'translateY(2px) scale(1.05)' : 'translateY(-2px) scale(1.05)';\n // Mobile slide direction\n const mobileSlideOut = isTop ? 'translateY(-100%)' : 'translateY(100%)';\n\n const css = `\n #dunefox-btn {\n position: fixed; ${vBtn}; ${hSide};\n width: 60px; height: 60px; border-radius: 50%;\n background: #1a1a1a; border: none; cursor: pointer;\n display: flex; align-items: center; justify-content: center;\n z-index: 2147483646;\n box-shadow: 0 4px 20px rgba(0,0,0,.25);\n transition: transform .25s ease;\n }\n #dunefox-btn:hover { transform: ${hoverTranslate}; }\n #dunefox-frame {\n position: fixed; ${vFrame}; ${hSide};\n width: 350px; height: calc(100vh - 160px); max-height: 600px;\n border-radius: 16px; border: none; z-index: 2147483645;\n box-shadow: 0 8px 32px rgba(0,0,0,.12);\n opacity: 0; visibility: hidden;\n transform: ${slideOut};\n transition: all .4s cubic-bezier(.4,0,.2,1);\n }\n #dunefox-frame.df-open {\n opacity: 1; visibility: visible; transform: translateY(0) scale(1);\n }\n @media (max-width: 768px) {\n #dunefox-frame {\n width: 100vw; height: 100vh; max-height: none;\n inset: 0; border-radius: 0; transform: ${mobileSlideOut};\n bottom: unset; right: unset; left: unset; top: unset;\n }\n #dunefox-frame.df-open { transform: translateY(0); }\n }\n `;\n const tag = document.createElement('style');\n tag.id = 'dunefox-styles';\n tag.textContent = css;\n document.head.appendChild(tag);\n}\n\n// ─── SVG icons ────────────────────────────────────────────────────────────────\nconst CLOSE_SVG = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"26\" height=\"26\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"white\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\"/><path d=\"m6 6 12 12\"/></svg>`;\n\n// ─── Main init ────────────────────────────────────────────────────────────────\nlet _initialised = false;\n\n/**\n * Initialise the Dunefox chatbot widget.\n * Call once on DOMContentLoaded or after your framework has mounted.\n *\n * @example\n * import { init } from 'dunefox-chatbot';\n * init({ tenantId: 'YOUR_TENANT_ID' });\n */\nexport function init(options: DunefoxChatOptions | string): void {\n // Allow shorthand: init('tenantId')\n const opts: DunefoxChatOptions =\n typeof options === 'string' ? { tenantId: options } : options;\n\n if (_initialised) {\n console.warn('[DunefoxChat] Already initialised. Call destroy() first.');\n return;\n }\n\n const {\n tenantId,\n position = 'bottom-right',\n defaultOpen = false,\n baseUrl = DEFAULT_BASE,\n iconUrl = DEFAULT_ICON,\n } = opts;\n\n if (!tenantId) throw new Error('[DunefoxChat] tenantId is required.');\n\n // Wait for DOM if we're running during <head> parse\n const ready = (cb: () => void) =>\n document.readyState === 'loading'\n ? document.addEventListener('DOMContentLoaded', cb, { once: true })\n : cb();\n\n ready(() => {\n injectStyles(position);\n\n const uuid = getOrCreateUUID();\n const src = `${baseUrl}/api/${tenantId}?uuid=${uuid}`;\n\n // ── iframe ──\n const iframe = document.createElement('iframe');\n iframe.id = 'dunefox-frame';\n iframe.title = 'Dunefox Chat';\n iframe.loading = 'lazy';\n iframe.src = src;\n\n // ── toggle button ──\n const btn = document.createElement('button');\n btn.id = 'dunefox-btn';\n btn.setAttribute('aria-label', 'Open chat');\n btn.setAttribute('aria-expanded', 'false');\n\n const img = document.createElement('img');\n img.src = iconUrl;\n img.alt = '';\n img.width = 60;\n img.height = 60;\n img.style.cssText = 'display:block;border-radius:50%;object-fit:cover;';\n\n const closeSpan = document.createElement('span');\n closeSpan.innerHTML = CLOSE_SVG;\n closeSpan.style.display = 'none';\n\n btn.append(img, closeSpan);\n\n // ── state ──\n let open = defaultOpen;\n const applyState = () => {\n iframe.classList.toggle('df-open', open);\n img.style.display = open ? 'none' : 'block';\n closeSpan.style.display = open ? 'block' : 'none';\n btn.setAttribute('aria-expanded', String(open));\n };\n\n btn.addEventListener('click', () => {\n open = !open;\n applyState();\n });\n\n document.body.append(iframe, btn);\n applyState();\n _initialised = true;\n });\n}\n\n/**\n * Programmatically open the chat panel.\n */\nexport function open(): void {\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\n const btn = document.getElementById('dunefox-btn');\n if (!iframe || !btn) return;\n iframe.classList.add('df-open');\n btn.setAttribute('aria-expanded', 'true');\n}\n\n/**\n * Programmatically close the chat panel.\n */\nexport function close(): void {\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\n const btn = document.getElementById('dunefox-btn');\n if (!iframe || !btn) return;\n iframe.classList.remove('df-open');\n btn.setAttribute('aria-expanded', 'false');\n}\n\n/**\n * Remove the widget entirely.\n */\nexport function destroy(): void {\n document.getElementById('dunefox-frame')?.remove();\n document.getElementById('dunefox-btn')?.remove();\n document.getElementById('dunefox-styles')?.remove();\n _initialised = false;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/core.ts"],"names":["DEFAULT_BASE","DEFAULT_ICON","UUID_KEY","getOrCreateUUID","_a","_b","id","injectStyles","position","isTop","hSide","css","tag","CLOSE_SVG","BACK_SVG","_initialised","init","options","opts","tenantId","defaultOpen","baseUrl","iconUrl","cb","uuid","src","iframe","btn","img","closeSpan","mobileBar","open","applyState","bar","close","destroy","_c","_d"],"mappings":"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","file":"index.js","sourcesContent":["/** Options accepted by DunefoxChat.init() */\nexport interface DunefoxChatOptions {\n /** Your Dunefox Tenant ID — found in Settings > Install Widget */\n tenantId: string;\n /**\n * Where to anchor the widget. Defaults to \"bottom-right\".\n * Supports: \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\"\n */\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\n /** Open the chat panel on load. Defaults to false. */\n defaultOpen?: boolean;\n /** Override the base URL (for self-hosted / staging). */\n baseUrl?: string;\n /** Icon image URL override. Falls back to Dunefox CDN asset. */\n iconUrl?: string;\n}\n\nconst DEFAULT_BASE = 'https://app.dunefox.io';\nconst DEFAULT_ICON = 'https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png';\nconst UUID_KEY = 'dunefox_chat_uuid';\nconst CLOSE_BAR_H = 48; // px — mobile top close bar height\n\n// ─── UUID generation ──────────────────────────────────────────────────────────\nfunction getOrCreateUUID(): string {\n try {\n let id = localStorage.getItem(UUID_KEY);\n if (!id) {\n id = crypto?.randomUUID?.()\n ?? Math.random().toString(36).slice(2) + Date.now().toString(36);\n localStorage.setItem(UUID_KEY, id);\n }\n return id;\n } catch {\n // Incognito / storage blocked — generate ephemeral ID\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\n }\n}\n\n// ─── CSS injector ─────────────────────────────────────────────────────────────\nfunction injectStyles(position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'): void {\n if (document.getElementById('dunefox-styles')) return;\n\n const isTop = position.startsWith('top');\n const isLeft = position.endsWith('left');\n const hSide = isLeft ? 'left:16px' : 'right:16px';\n const vBtn = isTop ? 'top:16px' : 'bottom:16px';\n // Panel opens away from the button edge\n const vFrame = isTop ? 'top:88px' : 'bottom:88px';\n // Slide direction: panels below slide up, panels above slide down\n const slideOut = isTop ? 'translateY(-24px) scale(.96)' : 'translateY(24px) scale(.96)';\n // Hover nudge direction\n const hoverTranslate = isTop ? 'translateY(2px) scale(1.05)' : 'translateY(-2px) scale(1.05)';\n // Mobile slide direction (always slides up from bottom)\n const mobileSlideOut = 'translateY(100%)';\n\n const css = `\n #dunefox-btn {\n position: fixed; ${vBtn}; ${hSide};\n width: 60px; height: 60px; border-radius: 50%;\n background: #1a1a1a; border: none; cursor: pointer;\n display: flex; align-items: center; justify-content: center;\n z-index: 2147483646;\n box-shadow: 0 4px 20px rgba(0,0,0,.25);\n transition: transform .25s ease;\n }\n #dunefox-btn:hover { transform: ${hoverTranslate}; }\n #dunefox-frame {\n position: fixed; ${vFrame}; ${hSide};\n width: 350px; height: calc(100vh - 160px); max-height: 600px;\n border-radius: 16px; border: none; z-index: 2147483645;\n box-shadow: 0 8px 32px rgba(0,0,0,.12);\n opacity: 0; visibility: hidden;\n transform: ${slideOut};\n transition: all .4s cubic-bezier(.4,0,.2,1);\n }\n #dunefox-frame.df-open {\n opacity: 1; visibility: visible; transform: translateY(0) scale(1);\n }\n\n /* ── Mobile close bar (hidden on desktop) ── */\n #dunefox-mobile-bar {\n display: none;\n }\n\n @media (max-width: 768px) {\n /* Mobile close bar */\n #dunefox-mobile-bar {\n position: fixed;\n top: 0 !important; left: 0 !important; right: 0 !important;\n height: ${CLOSE_BAR_H}px;\n background: #1a1a1a;\n z-index: 2147483647;\n align-items: center;\n padding: 0 16px;\n gap: 10px;\n border: none;\n width: 100%;\n cursor: pointer;\n box-sizing: border-box;\n }\n #dunefox-mobile-bar.df-open { display: flex; }\n\n /* Hide the corner toggle button on mobile when chat is open */\n #dunefox-btn.df-hidden { display: none !important; }\n\n /* Full-screen iframe, leaving room for close bar at top */\n #dunefox-frame {\n width: 100vw !important;\n height: calc(100% - ${CLOSE_BAR_H}px) !important;\n height: calc(100dvh - ${CLOSE_BAR_H}px) !important;\n max-height: none !important;\n top: ${CLOSE_BAR_H}px !important;\n bottom: 0 !important;\n left: 0 !important;\n right: 0 !important;\n border-radius: 0;\n transform: ${mobileSlideOut};\n opacity: 1 !important;\n visibility: visible !important;\n box-shadow: none;\n }\n #dunefox-frame.df-open { transform: translateY(0); }\n }\n `;\n const tag = document.createElement('style');\n tag.id = 'dunefox-styles';\n tag.textContent = css;\n document.head.appendChild(tag);\n}\n\n// ─── SVG icons ────────────────────────────────────────────────────────────────\nconst CLOSE_SVG = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"22\" height=\"22\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"white\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\"/><path d=\"m6 6 12 12\"/></svg>`;\nconst BACK_SVG = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"white\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M19 12H5\"/><path d=\"m12 5-7 7 7 7\"/></svg>`;\n\n// ─── Main init ────────────────────────────────────────────────────────────────\nlet _initialised = false;\n\n/**\n * Initialise the Dunefox chatbot widget.\n * Call once on DOMContentLoaded or after your framework has mounted.\n *\n * @example\n * import { init } from 'dunefox-chatbot';\n * init({ tenantId: 'YOUR_TENANT_ID' });\n */\nexport function init(options: DunefoxChatOptions | string): void {\n // Allow shorthand: init('tenantId')\n const opts: DunefoxChatOptions =\n typeof options === 'string' ? { tenantId: options } : options;\n\n if (_initialised) {\n console.warn('[DunefoxChat] Already initialised. Call destroy() first.');\n return;\n }\n\n const {\n tenantId,\n position = 'bottom-right',\n defaultOpen = false,\n baseUrl = DEFAULT_BASE,\n iconUrl = DEFAULT_ICON,\n } = opts;\n\n if (!tenantId) throw new Error('[DunefoxChat] tenantId is required.');\n\n // Wait for DOM if we're running during <head> parse\n const ready = (cb: () => void) =>\n document.readyState === 'loading'\n ? document.addEventListener('DOMContentLoaded', cb, { once: true })\n : cb();\n\n ready(() => {\n injectStyles(position);\n\n const uuid = getOrCreateUUID();\n const src = `${baseUrl}/api/${tenantId}?uuid=${uuid}`;\n\n // ── iframe ──\n const iframe = document.createElement('iframe');\n iframe.id = 'dunefox-frame';\n iframe.title = 'Dunefox Chat';\n iframe.loading = 'lazy';\n iframe.src = src;\n\n // ── toggle button ──\n const btn = document.createElement('button');\n btn.id = 'dunefox-btn';\n btn.setAttribute('aria-label', 'Open chat');\n btn.setAttribute('aria-expanded', 'false');\n\n const img = document.createElement('img');\n img.src = iconUrl;\n img.alt = '';\n img.width = 60;\n img.height = 60;\n img.style.cssText = 'display:block;border-radius:50%;object-fit:cover;';\n\n const closeSpan = document.createElement('span');\n closeSpan.innerHTML = CLOSE_SVG;\n closeSpan.style.display = 'none';\n\n btn.append(img, closeSpan);\n\n // ── mobile close bar ──\n // A full-width bar visible only on mobile when the chat is open,\n // so the user always has a clear, tappable way to close the chat.\n const mobileBar = document.createElement('button');\n mobileBar.id = 'dunefox-mobile-bar';\n mobileBar.setAttribute('aria-label', 'Close chat');\n mobileBar.innerHTML = `\n ${BACK_SVG}\n <span style=\"color:white;font-family:system-ui,-apple-system,sans-serif;font-size:14px;font-weight:600;letter-spacing:.01em;\">\n Close chat\n </span>\n `;\n\n // ── state ──\n let open = defaultOpen;\n const applyState = () => {\n iframe.classList.toggle('df-open', open);\n mobileBar.classList.toggle('df-open', open);\n btn.classList.toggle('df-hidden', open); // hides on mobile via CSS\n img.style.display = open ? 'none' : 'block';\n closeSpan.style.display = open ? 'block' : 'none';\n btn.setAttribute('aria-expanded', String(open));\n };\n\n btn.addEventListener('click', () => {\n open = !open;\n applyState();\n });\n\n mobileBar.addEventListener('click', () => {\n open = false;\n applyState();\n });\n\n document.body.append(iframe, mobileBar, btn);\n applyState();\n _initialised = true;\n });\n}\n\n/**\n * Programmatically open the chat panel.\n */\nexport function open(): void {\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\n const btn = document.getElementById('dunefox-btn');\n const bar = document.getElementById('dunefox-mobile-bar');\n if (!iframe || !btn) return;\n iframe.classList.add('df-open');\n bar?.classList.add('df-open');\n btn.classList.add('df-hidden');\n btn.setAttribute('aria-expanded', 'true');\n}\n\n/**\n * Programmatically close the chat panel.\n */\nexport function close(): void {\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\n const btn = document.getElementById('dunefox-btn');\n const bar = document.getElementById('dunefox-mobile-bar');\n if (!iframe || !btn) return;\n iframe.classList.remove('df-open');\n bar?.classList.remove('df-open');\n btn.classList.remove('df-hidden');\n btn.setAttribute('aria-expanded', 'false');\n}\n\n/**\n * Remove the widget entirely.\n */\nexport function destroy(): void {\n document.getElementById('dunefox-frame')?.remove();\n document.getElementById('dunefox-btn')?.remove();\n document.getElementById('dunefox-mobile-bar')?.remove();\n document.getElementById('dunefox-styles')?.remove();\n _initialised = false;\n}\n\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
var
|
|
1
|
+
var y="https://app.dunefox.io",v="https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png",b="dunefox_chat_uuid";function w(){var n,t;try{let e=localStorage.getItem(b);return e||(e=(t=(n=crypto==null?void 0:crypto.randomUUID)==null?void 0:n.call(crypto))!=null?t:Math.random().toString(36).slice(2)+Date.now().toString(36),localStorage.setItem(b,e)),e}catch(e){return Math.random().toString(36).slice(2)+Date.now().toString(36)}}function E(n){if(document.getElementById("dunefox-styles"))return;let t=n.startsWith("top"),r=n.endsWith("left")?"left:16px":"right:16px",p=`
|
|
2
2
|
#dunefox-btn {
|
|
3
|
-
position: fixed; ${t?"top:16px":"bottom:16px"}; ${
|
|
3
|
+
position: fixed; ${t?"top:16px":"bottom:16px"}; ${r};
|
|
4
4
|
width: 60px; height: 60px; border-radius: 50%;
|
|
5
5
|
background: #1a1a1a; border: none; cursor: pointer;
|
|
6
6
|
display: flex; align-items: center; justify-content: center;
|
|
@@ -10,7 +10,7 @@ var b="https://app.dunefox.io",y="https://dunefoxx.s3.ap-south-1.amazonaws.com/c
|
|
|
10
10
|
}
|
|
11
11
|
#dunefox-btn:hover { transform: ${t?"translateY(2px) scale(1.05)":"translateY(-2px) scale(1.05)"}; }
|
|
12
12
|
#dunefox-frame {
|
|
13
|
-
position: fixed; ${t?"top:88px":"bottom:88px"}; ${
|
|
13
|
+
position: fixed; ${t?"top:88px":"bottom:88px"}; ${r};
|
|
14
14
|
width: 350px; height: calc(100vh - 160px); max-height: 600px;
|
|
15
15
|
border-radius: 16px; border: none; z-index: 2147483645;
|
|
16
16
|
box-shadow: 0 8px 32px rgba(0,0,0,.12);
|
|
@@ -21,14 +21,55 @@ var b="https://app.dunefox.io",y="https://dunefoxx.s3.ap-south-1.amazonaws.com/c
|
|
|
21
21
|
#dunefox-frame.df-open {
|
|
22
22
|
opacity: 1; visibility: visible; transform: translateY(0) scale(1);
|
|
23
23
|
}
|
|
24
|
+
|
|
25
|
+
/* \u2500\u2500 Mobile close bar (hidden on desktop) \u2500\u2500 */
|
|
26
|
+
#dunefox-mobile-bar {
|
|
27
|
+
display: none;
|
|
28
|
+
}
|
|
29
|
+
|
|
24
30
|
@media (max-width: 768px) {
|
|
31
|
+
/* Mobile close bar */
|
|
32
|
+
#dunefox-mobile-bar {
|
|
33
|
+
position: fixed;
|
|
34
|
+
top: 0 !important; left: 0 !important; right: 0 !important;
|
|
35
|
+
height: 48px;
|
|
36
|
+
background: #1a1a1a;
|
|
37
|
+
z-index: 2147483647;
|
|
38
|
+
align-items: center;
|
|
39
|
+
padding: 0 16px;
|
|
40
|
+
gap: 10px;
|
|
41
|
+
border: none;
|
|
42
|
+
width: 100%;
|
|
43
|
+
cursor: pointer;
|
|
44
|
+
box-sizing: border-box;
|
|
45
|
+
}
|
|
46
|
+
#dunefox-mobile-bar.df-open { display: flex; }
|
|
47
|
+
|
|
48
|
+
/* Hide the corner toggle button on mobile when chat is open */
|
|
49
|
+
#dunefox-btn.df-hidden { display: none !important; }
|
|
50
|
+
|
|
51
|
+
/* Full-screen iframe, leaving room for close bar at top */
|
|
25
52
|
#dunefox-frame {
|
|
26
|
-
width: 100vw
|
|
27
|
-
|
|
28
|
-
|
|
53
|
+
width: 100vw !important;
|
|
54
|
+
height: calc(100% - 48px) !important;
|
|
55
|
+
height: calc(100dvh - 48px) !important;
|
|
56
|
+
max-height: none !important;
|
|
57
|
+
top: 48px !important;
|
|
58
|
+
bottom: 0 !important;
|
|
59
|
+
left: 0 !important;
|
|
60
|
+
right: 0 !important;
|
|
61
|
+
border-radius: 0;
|
|
62
|
+
transform: translateY(100%);
|
|
63
|
+
opacity: 1 !important;
|
|
64
|
+
visibility: visible !important;
|
|
65
|
+
box-shadow: none;
|
|
29
66
|
}
|
|
30
67
|
#dunefox-frame.df-open { transform: translateY(0); }
|
|
31
68
|
}
|
|
32
|
-
`,o=document.createElement("style");o.id="dunefox-styles",o.textContent=
|
|
33
|
-
|
|
69
|
+
`,o=document.createElement("style");o.id="dunefox-styles",o.textContent=p,document.head.appendChild(o);}var L='<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>',B='<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5"/><path d="m12 5-7 7 7 7"/></svg>',f=false;function I(n){let t=typeof n=="string"?{tenantId:n}:n;if(f){console.warn("[DunefoxChat] Already initialised. Call destroy() first.");return}let{tenantId:e,position:r="bottom-right",defaultOpen:u=false,baseUrl:x=y,iconUrl:h=v}=t;if(!e)throw new Error("[DunefoxChat] tenantId is required.");(l=>document.readyState==="loading"?document.addEventListener("DOMContentLoaded",l,{once:true}):l())(()=>{E(r);let l=w(),p=`${x}/api/${e}?uuid=${l}`,o=document.createElement("iframe");o.id="dunefox-frame",o.title="Dunefox Chat",o.loading="lazy",o.src=p;let a=document.createElement("button");a.id="dunefox-btn",a.setAttribute("aria-label","Open chat"),a.setAttribute("aria-expanded","false");let s=document.createElement("img");s.src=h,s.alt="",s.width=60,s.height=60,s.style.cssText="display:block;border-radius:50%;object-fit:cover;";let c=document.createElement("span");c.innerHTML=L,c.style.display="none",a.append(s,c);let d=document.createElement("button");d.id="dunefox-mobile-bar",d.setAttribute("aria-label","Close chat"),d.innerHTML=`
|
|
70
|
+
${B}
|
|
71
|
+
<span style="color:white;font-family:system-ui,-apple-system,sans-serif;font-size:14px;font-weight:600;letter-spacing:.01em;">
|
|
72
|
+
Close chat
|
|
73
|
+
</span>
|
|
74
|
+
`;let i=u,m=()=>{o.classList.toggle("df-open",i),d.classList.toggle("df-open",i),a.classList.toggle("df-hidden",i),s.style.display=i?"none":"block",c.style.display=i?"block":"none",a.setAttribute("aria-expanded",String(i));};a.addEventListener("click",()=>{i=!i,m();}),d.addEventListener("click",()=>{i=false,m();}),document.body.append(o,d,a),m(),f=true;});}function S(){let n=document.getElementById("dunefox-frame"),t=document.getElementById("dunefox-btn"),e=document.getElementById("dunefox-mobile-bar");!n||!t||(n.classList.add("df-open"),e==null||e.classList.add("df-open"),t.classList.add("df-hidden"),t.setAttribute("aria-expanded","true"));}function C(){let n=document.getElementById("dunefox-frame"),t=document.getElementById("dunefox-btn"),e=document.getElementById("dunefox-mobile-bar");!n||!t||(n.classList.remove("df-open"),e==null||e.classList.remove("df-open"),t.classList.remove("df-hidden"),t.setAttribute("aria-expanded","false"));}function O(){var n,t,e,r;(n=document.getElementById("dunefox-frame"))==null||n.remove(),(t=document.getElementById("dunefox-btn"))==null||t.remove(),(e=document.getElementById("dunefox-mobile-bar"))==null||e.remove(),(r=document.getElementById("dunefox-styles"))==null||r.remove(),f=false;}export{C as close,O as destroy,I as init,S as open};//# sourceMappingURL=index.mjs.map
|
|
34
75
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core.ts"],"names":["DEFAULT_BASE","DEFAULT_ICON","UUID_KEY","getOrCreateUUID","_a","_b","id","e","injectStyles","position","isTop","hSide","css","tag","CLOSE_SVG","_initialised","init","options","opts","tenantId","defaultOpen","baseUrl","iconUrl","cb","uuid","src","iframe","btn","img","closeSpan","open","applyState","close","destroy","_c"],"mappings":"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","file":"index.mjs","sourcesContent":["/** Options accepted by DunefoxChat.init() */\nexport interface DunefoxChatOptions {\n /** Your Dunefox Tenant ID — found in Settings > Install Widget */\n tenantId: string;\n /**\n * Where to anchor the widget. Defaults to \"bottom-right\".\n * Supports: \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\"\n */\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\n /** Open the chat panel on load. Defaults to false. */\n defaultOpen?: boolean;\n /** Override the base URL (for self-hosted / staging). */\n baseUrl?: string;\n /** Icon image URL override. Falls back to Dunefox CDN asset. */\n iconUrl?: string;\n}\n\nconst DEFAULT_BASE = 'https://app.dunefox.io';\nconst DEFAULT_ICON = 'https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png';\nconst UUID_KEY = 'dunefox_chat_uuid';\n\n// ─── UUID generation ──────────────────────────────────────────────────────────\nfunction getOrCreateUUID(): string {\n try {\n let id = localStorage.getItem(UUID_KEY);\n if (!id) {\n id = crypto?.randomUUID?.()\n ?? Math.random().toString(36).slice(2) + Date.now().toString(36);\n localStorage.setItem(UUID_KEY, id);\n }\n return id;\n } catch {\n // Incognito / storage blocked — generate ephemeral ID\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\n }\n}\n\n// ─── CSS injector ─────────────────────────────────────────────────────────────\nfunction injectStyles(position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'): void {\n if (document.getElementById('dunefox-styles')) return;\n\n const isTop = position.startsWith('top');\n const isLeft = position.endsWith('left');\n const hSide = isLeft ? 'left:16px' : 'right:16px';\n const vBtn = isTop ? 'top:16px' : 'bottom:16px';\n // Panel opens away from the button edge\n const vFrame = isTop ? 'top:88px' : 'bottom:88px';\n // Slide direction: panels below slide up, panels above slide down\n const slideOut = isTop ? 'translateY(-24px) scale(.96)' : 'translateY(24px) scale(.96)';\n // Hover nudge direction\n const hoverTranslate = isTop ? 'translateY(2px) scale(1.05)' : 'translateY(-2px) scale(1.05)';\n // Mobile slide direction\n const mobileSlideOut = isTop ? 'translateY(-100%)' : 'translateY(100%)';\n\n const css = `\n #dunefox-btn {\n position: fixed; ${vBtn}; ${hSide};\n width: 60px; height: 60px; border-radius: 50%;\n background: #1a1a1a; border: none; cursor: pointer;\n display: flex; align-items: center; justify-content: center;\n z-index: 2147483646;\n box-shadow: 0 4px 20px rgba(0,0,0,.25);\n transition: transform .25s ease;\n }\n #dunefox-btn:hover { transform: ${hoverTranslate}; }\n #dunefox-frame {\n position: fixed; ${vFrame}; ${hSide};\n width: 350px; height: calc(100vh - 160px); max-height: 600px;\n border-radius: 16px; border: none; z-index: 2147483645;\n box-shadow: 0 8px 32px rgba(0,0,0,.12);\n opacity: 0; visibility: hidden;\n transform: ${slideOut};\n transition: all .4s cubic-bezier(.4,0,.2,1);\n }\n #dunefox-frame.df-open {\n opacity: 1; visibility: visible; transform: translateY(0) scale(1);\n }\n @media (max-width: 768px) {\n #dunefox-frame {\n width: 100vw; height: 100vh; max-height: none;\n inset: 0; border-radius: 0; transform: ${mobileSlideOut};\n bottom: unset; right: unset; left: unset; top: unset;\n }\n #dunefox-frame.df-open { transform: translateY(0); }\n }\n `;\n const tag = document.createElement('style');\n tag.id = 'dunefox-styles';\n tag.textContent = css;\n document.head.appendChild(tag);\n}\n\n// ─── SVG icons ────────────────────────────────────────────────────────────────\nconst CLOSE_SVG = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"26\" height=\"26\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"white\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\"/><path d=\"m6 6 12 12\"/></svg>`;\n\n// ─── Main init ────────────────────────────────────────────────────────────────\nlet _initialised = false;\n\n/**\n * Initialise the Dunefox chatbot widget.\n * Call once on DOMContentLoaded or after your framework has mounted.\n *\n * @example\n * import { init } from 'dunefox-chatbot';\n * init({ tenantId: 'YOUR_TENANT_ID' });\n */\nexport function init(options: DunefoxChatOptions | string): void {\n // Allow shorthand: init('tenantId')\n const opts: DunefoxChatOptions =\n typeof options === 'string' ? { tenantId: options } : options;\n\n if (_initialised) {\n console.warn('[DunefoxChat] Already initialised. Call destroy() first.');\n return;\n }\n\n const {\n tenantId,\n position = 'bottom-right',\n defaultOpen = false,\n baseUrl = DEFAULT_BASE,\n iconUrl = DEFAULT_ICON,\n } = opts;\n\n if (!tenantId) throw new Error('[DunefoxChat] tenantId is required.');\n\n // Wait for DOM if we're running during <head> parse\n const ready = (cb: () => void) =>\n document.readyState === 'loading'\n ? document.addEventListener('DOMContentLoaded', cb, { once: true })\n : cb();\n\n ready(() => {\n injectStyles(position);\n\n const uuid = getOrCreateUUID();\n const src = `${baseUrl}/api/${tenantId}?uuid=${uuid}`;\n\n // ── iframe ──\n const iframe = document.createElement('iframe');\n iframe.id = 'dunefox-frame';\n iframe.title = 'Dunefox Chat';\n iframe.loading = 'lazy';\n iframe.src = src;\n\n // ── toggle button ──\n const btn = document.createElement('button');\n btn.id = 'dunefox-btn';\n btn.setAttribute('aria-label', 'Open chat');\n btn.setAttribute('aria-expanded', 'false');\n\n const img = document.createElement('img');\n img.src = iconUrl;\n img.alt = '';\n img.width = 60;\n img.height = 60;\n img.style.cssText = 'display:block;border-radius:50%;object-fit:cover;';\n\n const closeSpan = document.createElement('span');\n closeSpan.innerHTML = CLOSE_SVG;\n closeSpan.style.display = 'none';\n\n btn.append(img, closeSpan);\n\n // ── state ──\n let open = defaultOpen;\n const applyState = () => {\n iframe.classList.toggle('df-open', open);\n img.style.display = open ? 'none' : 'block';\n closeSpan.style.display = open ? 'block' : 'none';\n btn.setAttribute('aria-expanded', String(open));\n };\n\n btn.addEventListener('click', () => {\n open = !open;\n applyState();\n });\n\n document.body.append(iframe, btn);\n applyState();\n _initialised = true;\n });\n}\n\n/**\n * Programmatically open the chat panel.\n */\nexport function open(): void {\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\n const btn = document.getElementById('dunefox-btn');\n if (!iframe || !btn) return;\n iframe.classList.add('df-open');\n btn.setAttribute('aria-expanded', 'true');\n}\n\n/**\n * Programmatically close the chat panel.\n */\nexport function close(): void {\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\n const btn = document.getElementById('dunefox-btn');\n if (!iframe || !btn) return;\n iframe.classList.remove('df-open');\n btn.setAttribute('aria-expanded', 'false');\n}\n\n/**\n * Remove the widget entirely.\n */\nexport function destroy(): void {\n document.getElementById('dunefox-frame')?.remove();\n document.getElementById('dunefox-btn')?.remove();\n document.getElementById('dunefox-styles')?.remove();\n _initialised = false;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/core.ts"],"names":["DEFAULT_BASE","DEFAULT_ICON","UUID_KEY","getOrCreateUUID","_a","_b","id","injectStyles","position","isTop","hSide","css","tag","CLOSE_SVG","BACK_SVG","_initialised","init","options","opts","tenantId","defaultOpen","baseUrl","iconUrl","cb","uuid","src","iframe","btn","img","closeSpan","mobileBar","open","applyState","bar","close","destroy","_c","_d"],"mappings":"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","file":"index.mjs","sourcesContent":["/** Options accepted by DunefoxChat.init() */\nexport interface DunefoxChatOptions {\n /** Your Dunefox Tenant ID — found in Settings > Install Widget */\n tenantId: string;\n /**\n * Where to anchor the widget. Defaults to \"bottom-right\".\n * Supports: \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\"\n */\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\n /** Open the chat panel on load. Defaults to false. */\n defaultOpen?: boolean;\n /** Override the base URL (for self-hosted / staging). */\n baseUrl?: string;\n /** Icon image URL override. Falls back to Dunefox CDN asset. */\n iconUrl?: string;\n}\n\nconst DEFAULT_BASE = 'https://app.dunefox.io';\nconst DEFAULT_ICON = 'https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png';\nconst UUID_KEY = 'dunefox_chat_uuid';\nconst CLOSE_BAR_H = 48; // px — mobile top close bar height\n\n// ─── UUID generation ──────────────────────────────────────────────────────────\nfunction getOrCreateUUID(): string {\n try {\n let id = localStorage.getItem(UUID_KEY);\n if (!id) {\n id = crypto?.randomUUID?.()\n ?? Math.random().toString(36).slice(2) + Date.now().toString(36);\n localStorage.setItem(UUID_KEY, id);\n }\n return id;\n } catch {\n // Incognito / storage blocked — generate ephemeral ID\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\n }\n}\n\n// ─── CSS injector ─────────────────────────────────────────────────────────────\nfunction injectStyles(position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'): void {\n if (document.getElementById('dunefox-styles')) return;\n\n const isTop = position.startsWith('top');\n const isLeft = position.endsWith('left');\n const hSide = isLeft ? 'left:16px' : 'right:16px';\n const vBtn = isTop ? 'top:16px' : 'bottom:16px';\n // Panel opens away from the button edge\n const vFrame = isTop ? 'top:88px' : 'bottom:88px';\n // Slide direction: panels below slide up, panels above slide down\n const slideOut = isTop ? 'translateY(-24px) scale(.96)' : 'translateY(24px) scale(.96)';\n // Hover nudge direction\n const hoverTranslate = isTop ? 'translateY(2px) scale(1.05)' : 'translateY(-2px) scale(1.05)';\n // Mobile slide direction (always slides up from bottom)\n const mobileSlideOut = 'translateY(100%)';\n\n const css = `\n #dunefox-btn {\n position: fixed; ${vBtn}; ${hSide};\n width: 60px; height: 60px; border-radius: 50%;\n background: #1a1a1a; border: none; cursor: pointer;\n display: flex; align-items: center; justify-content: center;\n z-index: 2147483646;\n box-shadow: 0 4px 20px rgba(0,0,0,.25);\n transition: transform .25s ease;\n }\n #dunefox-btn:hover { transform: ${hoverTranslate}; }\n #dunefox-frame {\n position: fixed; ${vFrame}; ${hSide};\n width: 350px; height: calc(100vh - 160px); max-height: 600px;\n border-radius: 16px; border: none; z-index: 2147483645;\n box-shadow: 0 8px 32px rgba(0,0,0,.12);\n opacity: 0; visibility: hidden;\n transform: ${slideOut};\n transition: all .4s cubic-bezier(.4,0,.2,1);\n }\n #dunefox-frame.df-open {\n opacity: 1; visibility: visible; transform: translateY(0) scale(1);\n }\n\n /* ── Mobile close bar (hidden on desktop) ── */\n #dunefox-mobile-bar {\n display: none;\n }\n\n @media (max-width: 768px) {\n /* Mobile close bar */\n #dunefox-mobile-bar {\n position: fixed;\n top: 0 !important; left: 0 !important; right: 0 !important;\n height: ${CLOSE_BAR_H}px;\n background: #1a1a1a;\n z-index: 2147483647;\n align-items: center;\n padding: 0 16px;\n gap: 10px;\n border: none;\n width: 100%;\n cursor: pointer;\n box-sizing: border-box;\n }\n #dunefox-mobile-bar.df-open { display: flex; }\n\n /* Hide the corner toggle button on mobile when chat is open */\n #dunefox-btn.df-hidden { display: none !important; }\n\n /* Full-screen iframe, leaving room for close bar at top */\n #dunefox-frame {\n width: 100vw !important;\n height: calc(100% - ${CLOSE_BAR_H}px) !important;\n height: calc(100dvh - ${CLOSE_BAR_H}px) !important;\n max-height: none !important;\n top: ${CLOSE_BAR_H}px !important;\n bottom: 0 !important;\n left: 0 !important;\n right: 0 !important;\n border-radius: 0;\n transform: ${mobileSlideOut};\n opacity: 1 !important;\n visibility: visible !important;\n box-shadow: none;\n }\n #dunefox-frame.df-open { transform: translateY(0); }\n }\n `;\n const tag = document.createElement('style');\n tag.id = 'dunefox-styles';\n tag.textContent = css;\n document.head.appendChild(tag);\n}\n\n// ─── SVG icons ────────────────────────────────────────────────────────────────\nconst CLOSE_SVG = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"22\" height=\"22\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"white\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\"/><path d=\"m6 6 12 12\"/></svg>`;\nconst BACK_SVG = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"white\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M19 12H5\"/><path d=\"m12 5-7 7 7 7\"/></svg>`;\n\n// ─── Main init ────────────────────────────────────────────────────────────────\nlet _initialised = false;\n\n/**\n * Initialise the Dunefox chatbot widget.\n * Call once on DOMContentLoaded or after your framework has mounted.\n *\n * @example\n * import { init } from 'dunefox-chatbot';\n * init({ tenantId: 'YOUR_TENANT_ID' });\n */\nexport function init(options: DunefoxChatOptions | string): void {\n // Allow shorthand: init('tenantId')\n const opts: DunefoxChatOptions =\n typeof options === 'string' ? { tenantId: options } : options;\n\n if (_initialised) {\n console.warn('[DunefoxChat] Already initialised. Call destroy() first.');\n return;\n }\n\n const {\n tenantId,\n position = 'bottom-right',\n defaultOpen = false,\n baseUrl = DEFAULT_BASE,\n iconUrl = DEFAULT_ICON,\n } = opts;\n\n if (!tenantId) throw new Error('[DunefoxChat] tenantId is required.');\n\n // Wait for DOM if we're running during <head> parse\n const ready = (cb: () => void) =>\n document.readyState === 'loading'\n ? document.addEventListener('DOMContentLoaded', cb, { once: true })\n : cb();\n\n ready(() => {\n injectStyles(position);\n\n const uuid = getOrCreateUUID();\n const src = `${baseUrl}/api/${tenantId}?uuid=${uuid}`;\n\n // ── iframe ──\n const iframe = document.createElement('iframe');\n iframe.id = 'dunefox-frame';\n iframe.title = 'Dunefox Chat';\n iframe.loading = 'lazy';\n iframe.src = src;\n\n // ── toggle button ──\n const btn = document.createElement('button');\n btn.id = 'dunefox-btn';\n btn.setAttribute('aria-label', 'Open chat');\n btn.setAttribute('aria-expanded', 'false');\n\n const img = document.createElement('img');\n img.src = iconUrl;\n img.alt = '';\n img.width = 60;\n img.height = 60;\n img.style.cssText = 'display:block;border-radius:50%;object-fit:cover;';\n\n const closeSpan = document.createElement('span');\n closeSpan.innerHTML = CLOSE_SVG;\n closeSpan.style.display = 'none';\n\n btn.append(img, closeSpan);\n\n // ── mobile close bar ──\n // A full-width bar visible only on mobile when the chat is open,\n // so the user always has a clear, tappable way to close the chat.\n const mobileBar = document.createElement('button');\n mobileBar.id = 'dunefox-mobile-bar';\n mobileBar.setAttribute('aria-label', 'Close chat');\n mobileBar.innerHTML = `\n ${BACK_SVG}\n <span style=\"color:white;font-family:system-ui,-apple-system,sans-serif;font-size:14px;font-weight:600;letter-spacing:.01em;\">\n Close chat\n </span>\n `;\n\n // ── state ──\n let open = defaultOpen;\n const applyState = () => {\n iframe.classList.toggle('df-open', open);\n mobileBar.classList.toggle('df-open', open);\n btn.classList.toggle('df-hidden', open); // hides on mobile via CSS\n img.style.display = open ? 'none' : 'block';\n closeSpan.style.display = open ? 'block' : 'none';\n btn.setAttribute('aria-expanded', String(open));\n };\n\n btn.addEventListener('click', () => {\n open = !open;\n applyState();\n });\n\n mobileBar.addEventListener('click', () => {\n open = false;\n applyState();\n });\n\n document.body.append(iframe, mobileBar, btn);\n applyState();\n _initialised = true;\n });\n}\n\n/**\n * Programmatically open the chat panel.\n */\nexport function open(): void {\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\n const btn = document.getElementById('dunefox-btn');\n const bar = document.getElementById('dunefox-mobile-bar');\n if (!iframe || !btn) return;\n iframe.classList.add('df-open');\n bar?.classList.add('df-open');\n btn.classList.add('df-hidden');\n btn.setAttribute('aria-expanded', 'true');\n}\n\n/**\n * Programmatically close the chat panel.\n */\nexport function close(): void {\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\n const btn = document.getElementById('dunefox-btn');\n const bar = document.getElementById('dunefox-mobile-bar');\n if (!iframe || !btn) return;\n iframe.classList.remove('df-open');\n bar?.classList.remove('df-open');\n btn.classList.remove('df-hidden');\n btn.setAttribute('aria-expanded', 'false');\n}\n\n/**\n * Remove the widget entirely.\n */\nexport function destroy(): void {\n document.getElementById('dunefox-frame')?.remove();\n document.getElementById('dunefox-btn')?.remove();\n document.getElementById('dunefox-mobile-bar')?.remove();\n document.getElementById('dunefox-styles')?.remove();\n _initialised = false;\n}\n\n"]}
|
package/dist/react.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
'use strict';var react=require('react');var
|
|
1
|
+
'use strict';var react=require('react');var w="https://app.dunefox.io",E="https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png",b="dunefox_chat_uuid";function L(){var t,e;try{let n=localStorage.getItem(b);return n||(n=(e=(t=crypto==null?void 0:crypto.randomUUID)==null?void 0:t.call(crypto))!=null?e:Math.random().toString(36).slice(2)+Date.now().toString(36),localStorage.setItem(b,n)),n}catch(n){return Math.random().toString(36).slice(2)+Date.now().toString(36)}}function C(t){if(document.getElementById("dunefox-styles"))return;let e=t.startsWith("top"),a=t.endsWith("left")?"left:16px":"right:16px",p=`
|
|
2
2
|
#dunefox-btn {
|
|
3
|
-
position: fixed; ${
|
|
3
|
+
position: fixed; ${e?"top:16px":"bottom:16px"}; ${a};
|
|
4
4
|
width: 60px; height: 60px; border-radius: 50%;
|
|
5
5
|
background: #1a1a1a; border: none; cursor: pointer;
|
|
6
6
|
display: flex; align-items: center; justify-content: center;
|
|
@@ -8,27 +8,68 @@
|
|
|
8
8
|
box-shadow: 0 4px 20px rgba(0,0,0,.25);
|
|
9
9
|
transition: transform .25s ease;
|
|
10
10
|
}
|
|
11
|
-
#dunefox-btn:hover { transform: ${
|
|
11
|
+
#dunefox-btn:hover { transform: ${e?"translateY(2px) scale(1.05)":"translateY(-2px) scale(1.05)"}; }
|
|
12
12
|
#dunefox-frame {
|
|
13
|
-
position: fixed; ${
|
|
13
|
+
position: fixed; ${e?"top:88px":"bottom:88px"}; ${a};
|
|
14
14
|
width: 350px; height: calc(100vh - 160px); max-height: 600px;
|
|
15
15
|
border-radius: 16px; border: none; z-index: 2147483645;
|
|
16
16
|
box-shadow: 0 8px 32px rgba(0,0,0,.12);
|
|
17
17
|
opacity: 0; visibility: hidden;
|
|
18
|
-
transform: ${
|
|
18
|
+
transform: ${e?"translateY(-24px) scale(.96)":"translateY(24px) scale(.96)"};
|
|
19
19
|
transition: all .4s cubic-bezier(.4,0,.2,1);
|
|
20
20
|
}
|
|
21
21
|
#dunefox-frame.df-open {
|
|
22
22
|
opacity: 1; visibility: visible; transform: translateY(0) scale(1);
|
|
23
23
|
}
|
|
24
|
+
|
|
25
|
+
/* \u2500\u2500 Mobile close bar (hidden on desktop) \u2500\u2500 */
|
|
26
|
+
#dunefox-mobile-bar {
|
|
27
|
+
display: none;
|
|
28
|
+
}
|
|
29
|
+
|
|
24
30
|
@media (max-width: 768px) {
|
|
31
|
+
/* Mobile close bar */
|
|
32
|
+
#dunefox-mobile-bar {
|
|
33
|
+
position: fixed;
|
|
34
|
+
top: 0 !important; left: 0 !important; right: 0 !important;
|
|
35
|
+
height: 48px;
|
|
36
|
+
background: #1a1a1a;
|
|
37
|
+
z-index: 2147483647;
|
|
38
|
+
align-items: center;
|
|
39
|
+
padding: 0 16px;
|
|
40
|
+
gap: 10px;
|
|
41
|
+
border: none;
|
|
42
|
+
width: 100%;
|
|
43
|
+
cursor: pointer;
|
|
44
|
+
box-sizing: border-box;
|
|
45
|
+
}
|
|
46
|
+
#dunefox-mobile-bar.df-open { display: flex; }
|
|
47
|
+
|
|
48
|
+
/* Hide the corner toggle button on mobile when chat is open */
|
|
49
|
+
#dunefox-btn.df-hidden { display: none !important; }
|
|
50
|
+
|
|
51
|
+
/* Full-screen iframe, leaving room for close bar at top */
|
|
25
52
|
#dunefox-frame {
|
|
26
|
-
width: 100vw
|
|
27
|
-
|
|
28
|
-
|
|
53
|
+
width: 100vw !important;
|
|
54
|
+
height: calc(100% - 48px) !important;
|
|
55
|
+
height: calc(100dvh - 48px) !important;
|
|
56
|
+
max-height: none !important;
|
|
57
|
+
top: 48px !important;
|
|
58
|
+
bottom: 0 !important;
|
|
59
|
+
left: 0 !important;
|
|
60
|
+
right: 0 !important;
|
|
61
|
+
border-radius: 0;
|
|
62
|
+
transform: translateY(100%);
|
|
63
|
+
opacity: 1 !important;
|
|
64
|
+
visibility: visible !important;
|
|
65
|
+
box-shadow: none;
|
|
29
66
|
}
|
|
30
67
|
#dunefox-frame.df-open { transform: translateY(0); }
|
|
31
68
|
}
|
|
32
|
-
`,o=document.createElement("style");o.id="dunefox-styles",o.textContent=
|
|
33
|
-
|
|
69
|
+
`,o=document.createElement("style");o.id="dunefox-styles",o.textContent=p,document.head.appendChild(o);}var B='<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>',I='<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5"/><path d="m12 5-7 7 7 7"/></svg>',f=false;function g(t){let e=typeof t=="string"?{tenantId:t}:t;if(f){console.warn("[DunefoxChat] Already initialised. Call destroy() first.");return}let{tenantId:n,position:a="bottom-right",defaultOpen:u=false,baseUrl:x=w,iconUrl:h=E}=e;if(!n)throw new Error("[DunefoxChat] tenantId is required.");(l=>document.readyState==="loading"?document.addEventListener("DOMContentLoaded",l,{once:true}):l())(()=>{C(a);let l=L(),p=`${x}/api/${n}?uuid=${l}`,o=document.createElement("iframe");o.id="dunefox-frame",o.title="Dunefox Chat",o.loading="lazy",o.src=p;let r=document.createElement("button");r.id="dunefox-btn",r.setAttribute("aria-label","Open chat"),r.setAttribute("aria-expanded","false");let s=document.createElement("img");s.src=h,s.alt="",s.width=60,s.height=60,s.style.cssText="display:block;border-radius:50%;object-fit:cover;";let c=document.createElement("span");c.innerHTML=B,c.style.display="none",r.append(s,c);let d=document.createElement("button");d.id="dunefox-mobile-bar",d.setAttribute("aria-label","Close chat"),d.innerHTML=`
|
|
70
|
+
${I}
|
|
71
|
+
<span style="color:white;font-family:system-ui,-apple-system,sans-serif;font-size:14px;font-weight:600;letter-spacing:.01em;">
|
|
72
|
+
Close chat
|
|
73
|
+
</span>
|
|
74
|
+
`;let i=u,m=()=>{o.classList.toggle("df-open",i),d.classList.toggle("df-open",i),r.classList.toggle("df-hidden",i),s.style.display=i?"none":"block",c.style.display=i?"block":"none",r.setAttribute("aria-expanded",String(i));};r.addEventListener("click",()=>{i=!i,m();}),d.addEventListener("click",()=>{i=false,m();}),document.body.append(o,d,r),m(),f=true;});}function y(){var t,e,n,a;(t=document.getElementById("dunefox-frame"))==null||t.remove(),(e=document.getElementById("dunefox-btn"))==null||e.remove(),(n=document.getElementById("dunefox-mobile-bar"))==null||n.remove(),(a=document.getElementById("dunefox-styles"))==null||a.remove(),f=false;}function A(t){let e=react.useRef(t);return react.useEffect(()=>{if(typeof window!="undefined")return g(e.current),()=>{y();}},[]),null}exports.DunefoxChatbot=A;//# sourceMappingURL=react.js.map
|
|
34
75
|
//# sourceMappingURL=react.js.map
|
package/dist/react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core.ts","../src/react.tsx"],"names":["DEFAULT_BASE","DEFAULT_ICON","UUID_KEY","getOrCreateUUID","_a","_b","id","e","injectStyles","position","isTop","hSide","css","tag","CLOSE_SVG","_initialised","init","options","opts","tenantId","defaultOpen","baseUrl","iconUrl","cb","uuid","src","iframe","btn","img","closeSpan","open","applyState","destroy","_c","DunefoxChatbot","props","optsRef","useRef","useEffect"],"mappings":"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","file":"react.js","sourcesContent":["/** Options accepted by DunefoxChat.init() */\nexport interface DunefoxChatOptions {\n /** Your Dunefox Tenant ID — found in Settings > Install Widget */\n tenantId: string;\n /**\n * Where to anchor the widget. Defaults to \"bottom-right\".\n * Supports: \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\"\n */\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\n /** Open the chat panel on load. Defaults to false. */\n defaultOpen?: boolean;\n /** Override the base URL (for self-hosted / staging). */\n baseUrl?: string;\n /** Icon image URL override. Falls back to Dunefox CDN asset. */\n iconUrl?: string;\n}\n\nconst DEFAULT_BASE = 'https://app.dunefox.io';\nconst DEFAULT_ICON = 'https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png';\nconst UUID_KEY = 'dunefox_chat_uuid';\n\n// ─── UUID generation ──────────────────────────────────────────────────────────\nfunction getOrCreateUUID(): string {\n try {\n let id = localStorage.getItem(UUID_KEY);\n if (!id) {\n id = crypto?.randomUUID?.()\n ?? Math.random().toString(36).slice(2) + Date.now().toString(36);\n localStorage.setItem(UUID_KEY, id);\n }\n return id;\n } catch {\n // Incognito / storage blocked — generate ephemeral ID\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\n }\n}\n\n// ─── CSS injector ─────────────────────────────────────────────────────────────\nfunction injectStyles(position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'): void {\n if (document.getElementById('dunefox-styles')) return;\n\n const isTop = position.startsWith('top');\n const isLeft = position.endsWith('left');\n const hSide = isLeft ? 'left:16px' : 'right:16px';\n const vBtn = isTop ? 'top:16px' : 'bottom:16px';\n // Panel opens away from the button edge\n const vFrame = isTop ? 'top:88px' : 'bottom:88px';\n // Slide direction: panels below slide up, panels above slide down\n const slideOut = isTop ? 'translateY(-24px) scale(.96)' : 'translateY(24px) scale(.96)';\n // Hover nudge direction\n const hoverTranslate = isTop ? 'translateY(2px) scale(1.05)' : 'translateY(-2px) scale(1.05)';\n // Mobile slide direction\n const mobileSlideOut = isTop ? 'translateY(-100%)' : 'translateY(100%)';\n\n const css = `\n #dunefox-btn {\n position: fixed; ${vBtn}; ${hSide};\n width: 60px; height: 60px; border-radius: 50%;\n background: #1a1a1a; border: none; cursor: pointer;\n display: flex; align-items: center; justify-content: center;\n z-index: 2147483646;\n box-shadow: 0 4px 20px rgba(0,0,0,.25);\n transition: transform .25s ease;\n }\n #dunefox-btn:hover { transform: ${hoverTranslate}; }\n #dunefox-frame {\n position: fixed; ${vFrame}; ${hSide};\n width: 350px; height: calc(100vh - 160px); max-height: 600px;\n border-radius: 16px; border: none; z-index: 2147483645;\n box-shadow: 0 8px 32px rgba(0,0,0,.12);\n opacity: 0; visibility: hidden;\n transform: ${slideOut};\n transition: all .4s cubic-bezier(.4,0,.2,1);\n }\n #dunefox-frame.df-open {\n opacity: 1; visibility: visible; transform: translateY(0) scale(1);\n }\n @media (max-width: 768px) {\n #dunefox-frame {\n width: 100vw; height: 100vh; max-height: none;\n inset: 0; border-radius: 0; transform: ${mobileSlideOut};\n bottom: unset; right: unset; left: unset; top: unset;\n }\n #dunefox-frame.df-open { transform: translateY(0); }\n }\n `;\n const tag = document.createElement('style');\n tag.id = 'dunefox-styles';\n tag.textContent = css;\n document.head.appendChild(tag);\n}\n\n// ─── SVG icons ────────────────────────────────────────────────────────────────\nconst CLOSE_SVG = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"26\" height=\"26\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"white\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\"/><path d=\"m6 6 12 12\"/></svg>`;\n\n// ─── Main init ────────────────────────────────────────────────────────────────\nlet _initialised = false;\n\n/**\n * Initialise the Dunefox chatbot widget.\n * Call once on DOMContentLoaded or after your framework has mounted.\n *\n * @example\n * import { init } from 'dunefox-chatbot';\n * init({ tenantId: 'YOUR_TENANT_ID' });\n */\nexport function init(options: DunefoxChatOptions | string): void {\n // Allow shorthand: init('tenantId')\n const opts: DunefoxChatOptions =\n typeof options === 'string' ? { tenantId: options } : options;\n\n if (_initialised) {\n console.warn('[DunefoxChat] Already initialised. Call destroy() first.');\n return;\n }\n\n const {\n tenantId,\n position = 'bottom-right',\n defaultOpen = false,\n baseUrl = DEFAULT_BASE,\n iconUrl = DEFAULT_ICON,\n } = opts;\n\n if (!tenantId) throw new Error('[DunefoxChat] tenantId is required.');\n\n // Wait for DOM if we're running during <head> parse\n const ready = (cb: () => void) =>\n document.readyState === 'loading'\n ? document.addEventListener('DOMContentLoaded', cb, { once: true })\n : cb();\n\n ready(() => {\n injectStyles(position);\n\n const uuid = getOrCreateUUID();\n const src = `${baseUrl}/api/${tenantId}?uuid=${uuid}`;\n\n // ── iframe ──\n const iframe = document.createElement('iframe');\n iframe.id = 'dunefox-frame';\n iframe.title = 'Dunefox Chat';\n iframe.loading = 'lazy';\n iframe.src = src;\n\n // ── toggle button ──\n const btn = document.createElement('button');\n btn.id = 'dunefox-btn';\n btn.setAttribute('aria-label', 'Open chat');\n btn.setAttribute('aria-expanded', 'false');\n\n const img = document.createElement('img');\n img.src = iconUrl;\n img.alt = '';\n img.width = 60;\n img.height = 60;\n img.style.cssText = 'display:block;border-radius:50%;object-fit:cover;';\n\n const closeSpan = document.createElement('span');\n closeSpan.innerHTML = CLOSE_SVG;\n closeSpan.style.display = 'none';\n\n btn.append(img, closeSpan);\n\n // ── state ──\n let open = defaultOpen;\n const applyState = () => {\n iframe.classList.toggle('df-open', open);\n img.style.display = open ? 'none' : 'block';\n closeSpan.style.display = open ? 'block' : 'none';\n btn.setAttribute('aria-expanded', String(open));\n };\n\n btn.addEventListener('click', () => {\n open = !open;\n applyState();\n });\n\n document.body.append(iframe, btn);\n applyState();\n _initialised = true;\n });\n}\n\n/**\n * Programmatically open the chat panel.\n */\nexport function open(): void {\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\n const btn = document.getElementById('dunefox-btn');\n if (!iframe || !btn) return;\n iframe.classList.add('df-open');\n btn.setAttribute('aria-expanded', 'true');\n}\n\n/**\n * Programmatically close the chat panel.\n */\nexport function close(): void {\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\n const btn = document.getElementById('dunefox-btn');\n if (!iframe || !btn) return;\n iframe.classList.remove('df-open');\n btn.setAttribute('aria-expanded', 'false');\n}\n\n/**\n * Remove the widget entirely.\n */\nexport function destroy(): void {\n document.getElementById('dunefox-frame')?.remove();\n document.getElementById('dunefox-btn')?.remove();\n document.getElementById('dunefox-styles')?.remove();\n _initialised = false;\n}\n","import { useEffect, useRef } from 'react';\nimport { init, destroy } from './core';\nimport type { DunefoxChatOptions } from './core';\n\nexport interface DunefoxChatbotProps extends DunefoxChatOptions {}\n\n/**\n * React component wrapper for the Dunefox Chatbot widget.\n *\n * Drop it anywhere in your component tree — typically at the root layout level.\n * SSR-safe: the widget is only mounted in the browser via useEffect.\n *\n * @example\n * // Next.js App Router (app/layout.tsx)\n * import { DunefoxChatbot } from 'dunefox-chatbot/react';\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html><body>\n * {children}\n * <DunefoxChatbot tenantId=\"YOUR_TENANT_ID\" />\n * </body></html>\n * );\n * }\n */\nexport function DunefoxChatbot(props: DunefoxChatbotProps) {\n // Capture props at mount time — we intentionally don't re-init on prop changes\n const optsRef = useRef(props);\n\n useEffect(() => {\n // Prevent running during SSR (window check is a safety net)\n if (typeof window === 'undefined') return;\n\n init(optsRef.current);\n\n return () => {\n destroy();\n };\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n // This component renders nothing to the React tree — the widget is DOM-injected\n return null;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/core.ts","../src/react.tsx"],"names":["DEFAULT_BASE","DEFAULT_ICON","UUID_KEY","getOrCreateUUID","_a","_b","id","e","injectStyles","position","isTop","hSide","css","tag","CLOSE_SVG","BACK_SVG","_initialised","init","options","opts","tenantId","defaultOpen","baseUrl","iconUrl","cb","uuid","src","iframe","btn","img","closeSpan","mobileBar","open","applyState","destroy","_c","_d","DunefoxChatbot","props","optsRef","useRef","useEffect"],"mappings":"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","file":"react.js","sourcesContent":["/** Options accepted by DunefoxChat.init() */\nexport interface DunefoxChatOptions {\n /** Your Dunefox Tenant ID — found in Settings > Install Widget */\n tenantId: string;\n /**\n * Where to anchor the widget. Defaults to \"bottom-right\".\n * Supports: \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\"\n */\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\n /** Open the chat panel on load. Defaults to false. */\n defaultOpen?: boolean;\n /** Override the base URL (for self-hosted / staging). */\n baseUrl?: string;\n /** Icon image URL override. Falls back to Dunefox CDN asset. */\n iconUrl?: string;\n}\n\nconst DEFAULT_BASE = 'https://app.dunefox.io';\nconst DEFAULT_ICON = 'https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png';\nconst UUID_KEY = 'dunefox_chat_uuid';\nconst CLOSE_BAR_H = 48; // px — mobile top close bar height\n\n// ─── UUID generation ──────────────────────────────────────────────────────────\nfunction getOrCreateUUID(): string {\n try {\n let id = localStorage.getItem(UUID_KEY);\n if (!id) {\n id = crypto?.randomUUID?.()\n ?? Math.random().toString(36).slice(2) + Date.now().toString(36);\n localStorage.setItem(UUID_KEY, id);\n }\n return id;\n } catch {\n // Incognito / storage blocked — generate ephemeral ID\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\n }\n}\n\n// ─── CSS injector ─────────────────────────────────────────────────────────────\nfunction injectStyles(position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'): void {\n if (document.getElementById('dunefox-styles')) return;\n\n const isTop = position.startsWith('top');\n const isLeft = position.endsWith('left');\n const hSide = isLeft ? 'left:16px' : 'right:16px';\n const vBtn = isTop ? 'top:16px' : 'bottom:16px';\n // Panel opens away from the button edge\n const vFrame = isTop ? 'top:88px' : 'bottom:88px';\n // Slide direction: panels below slide up, panels above slide down\n const slideOut = isTop ? 'translateY(-24px) scale(.96)' : 'translateY(24px) scale(.96)';\n // Hover nudge direction\n const hoverTranslate = isTop ? 'translateY(2px) scale(1.05)' : 'translateY(-2px) scale(1.05)';\n // Mobile slide direction (always slides up from bottom)\n const mobileSlideOut = 'translateY(100%)';\n\n const css = `\n #dunefox-btn {\n position: fixed; ${vBtn}; ${hSide};\n width: 60px; height: 60px; border-radius: 50%;\n background: #1a1a1a; border: none; cursor: pointer;\n display: flex; align-items: center; justify-content: center;\n z-index: 2147483646;\n box-shadow: 0 4px 20px rgba(0,0,0,.25);\n transition: transform .25s ease;\n }\n #dunefox-btn:hover { transform: ${hoverTranslate}; }\n #dunefox-frame {\n position: fixed; ${vFrame}; ${hSide};\n width: 350px; height: calc(100vh - 160px); max-height: 600px;\n border-radius: 16px; border: none; z-index: 2147483645;\n box-shadow: 0 8px 32px rgba(0,0,0,.12);\n opacity: 0; visibility: hidden;\n transform: ${slideOut};\n transition: all .4s cubic-bezier(.4,0,.2,1);\n }\n #dunefox-frame.df-open {\n opacity: 1; visibility: visible; transform: translateY(0) scale(1);\n }\n\n /* ── Mobile close bar (hidden on desktop) ── */\n #dunefox-mobile-bar {\n display: none;\n }\n\n @media (max-width: 768px) {\n /* Mobile close bar */\n #dunefox-mobile-bar {\n position: fixed;\n top: 0 !important; left: 0 !important; right: 0 !important;\n height: ${CLOSE_BAR_H}px;\n background: #1a1a1a;\n z-index: 2147483647;\n align-items: center;\n padding: 0 16px;\n gap: 10px;\n border: none;\n width: 100%;\n cursor: pointer;\n box-sizing: border-box;\n }\n #dunefox-mobile-bar.df-open { display: flex; }\n\n /* Hide the corner toggle button on mobile when chat is open */\n #dunefox-btn.df-hidden { display: none !important; }\n\n /* Full-screen iframe, leaving room for close bar at top */\n #dunefox-frame {\n width: 100vw !important;\n height: calc(100% - ${CLOSE_BAR_H}px) !important;\n height: calc(100dvh - ${CLOSE_BAR_H}px) !important;\n max-height: none !important;\n top: ${CLOSE_BAR_H}px !important;\n bottom: 0 !important;\n left: 0 !important;\n right: 0 !important;\n border-radius: 0;\n transform: ${mobileSlideOut};\n opacity: 1 !important;\n visibility: visible !important;\n box-shadow: none;\n }\n #dunefox-frame.df-open { transform: translateY(0); }\n }\n `;\n const tag = document.createElement('style');\n tag.id = 'dunefox-styles';\n tag.textContent = css;\n document.head.appendChild(tag);\n}\n\n// ─── SVG icons ────────────────────────────────────────────────────────────────\nconst CLOSE_SVG = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"22\" height=\"22\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"white\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\"/><path d=\"m6 6 12 12\"/></svg>`;\nconst BACK_SVG = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"white\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M19 12H5\"/><path d=\"m12 5-7 7 7 7\"/></svg>`;\n\n// ─── Main init ────────────────────────────────────────────────────────────────\nlet _initialised = false;\n\n/**\n * Initialise the Dunefox chatbot widget.\n * Call once on DOMContentLoaded or after your framework has mounted.\n *\n * @example\n * import { init } from 'dunefox-chatbot';\n * init({ tenantId: 'YOUR_TENANT_ID' });\n */\nexport function init(options: DunefoxChatOptions | string): void {\n // Allow shorthand: init('tenantId')\n const opts: DunefoxChatOptions =\n typeof options === 'string' ? { tenantId: options } : options;\n\n if (_initialised) {\n console.warn('[DunefoxChat] Already initialised. Call destroy() first.');\n return;\n }\n\n const {\n tenantId,\n position = 'bottom-right',\n defaultOpen = false,\n baseUrl = DEFAULT_BASE,\n iconUrl = DEFAULT_ICON,\n } = opts;\n\n if (!tenantId) throw new Error('[DunefoxChat] tenantId is required.');\n\n // Wait for DOM if we're running during <head> parse\n const ready = (cb: () => void) =>\n document.readyState === 'loading'\n ? document.addEventListener('DOMContentLoaded', cb, { once: true })\n : cb();\n\n ready(() => {\n injectStyles(position);\n\n const uuid = getOrCreateUUID();\n const src = `${baseUrl}/api/${tenantId}?uuid=${uuid}`;\n\n // ── iframe ──\n const iframe = document.createElement('iframe');\n iframe.id = 'dunefox-frame';\n iframe.title = 'Dunefox Chat';\n iframe.loading = 'lazy';\n iframe.src = src;\n\n // ── toggle button ──\n const btn = document.createElement('button');\n btn.id = 'dunefox-btn';\n btn.setAttribute('aria-label', 'Open chat');\n btn.setAttribute('aria-expanded', 'false');\n\n const img = document.createElement('img');\n img.src = iconUrl;\n img.alt = '';\n img.width = 60;\n img.height = 60;\n img.style.cssText = 'display:block;border-radius:50%;object-fit:cover;';\n\n const closeSpan = document.createElement('span');\n closeSpan.innerHTML = CLOSE_SVG;\n closeSpan.style.display = 'none';\n\n btn.append(img, closeSpan);\n\n // ── mobile close bar ──\n // A full-width bar visible only on mobile when the chat is open,\n // so the user always has a clear, tappable way to close the chat.\n const mobileBar = document.createElement('button');\n mobileBar.id = 'dunefox-mobile-bar';\n mobileBar.setAttribute('aria-label', 'Close chat');\n mobileBar.innerHTML = `\n ${BACK_SVG}\n <span style=\"color:white;font-family:system-ui,-apple-system,sans-serif;font-size:14px;font-weight:600;letter-spacing:.01em;\">\n Close chat\n </span>\n `;\n\n // ── state ──\n let open = defaultOpen;\n const applyState = () => {\n iframe.classList.toggle('df-open', open);\n mobileBar.classList.toggle('df-open', open);\n btn.classList.toggle('df-hidden', open); // hides on mobile via CSS\n img.style.display = open ? 'none' : 'block';\n closeSpan.style.display = open ? 'block' : 'none';\n btn.setAttribute('aria-expanded', String(open));\n };\n\n btn.addEventListener('click', () => {\n open = !open;\n applyState();\n });\n\n mobileBar.addEventListener('click', () => {\n open = false;\n applyState();\n });\n\n document.body.append(iframe, mobileBar, btn);\n applyState();\n _initialised = true;\n });\n}\n\n/**\n * Programmatically open the chat panel.\n */\nexport function open(): void {\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\n const btn = document.getElementById('dunefox-btn');\n const bar = document.getElementById('dunefox-mobile-bar');\n if (!iframe || !btn) return;\n iframe.classList.add('df-open');\n bar?.classList.add('df-open');\n btn.classList.add('df-hidden');\n btn.setAttribute('aria-expanded', 'true');\n}\n\n/**\n * Programmatically close the chat panel.\n */\nexport function close(): void {\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\n const btn = document.getElementById('dunefox-btn');\n const bar = document.getElementById('dunefox-mobile-bar');\n if (!iframe || !btn) return;\n iframe.classList.remove('df-open');\n bar?.classList.remove('df-open');\n btn.classList.remove('df-hidden');\n btn.setAttribute('aria-expanded', 'false');\n}\n\n/**\n * Remove the widget entirely.\n */\nexport function destroy(): void {\n document.getElementById('dunefox-frame')?.remove();\n document.getElementById('dunefox-btn')?.remove();\n document.getElementById('dunefox-mobile-bar')?.remove();\n document.getElementById('dunefox-styles')?.remove();\n _initialised = false;\n}\n\n","import { useEffect, useRef } from 'react';\nimport { init, destroy } from './core';\nimport type { DunefoxChatOptions } from './core';\n\nexport interface DunefoxChatbotProps extends DunefoxChatOptions {}\n\n/**\n * React component wrapper for the Dunefox Chatbot widget.\n *\n * Drop it anywhere in your component tree — typically at the root layout level.\n * SSR-safe: the widget is only mounted in the browser via useEffect.\n *\n * @example\n * // Next.js App Router (app/layout.tsx)\n * import { DunefoxChatbot } from 'dunefox-chatbot/react';\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html><body>\n * {children}\n * <DunefoxChatbot tenantId=\"YOUR_TENANT_ID\" />\n * </body></html>\n * );\n * }\n */\nexport function DunefoxChatbot(props: DunefoxChatbotProps) {\n // Capture props at mount time — we intentionally don't re-init on prop changes\n const optsRef = useRef(props);\n\n useEffect(() => {\n // Prevent running during SSR (window check is a safety net)\n if (typeof window === 'undefined') return;\n\n init(optsRef.current);\n\n return () => {\n destroy();\n };\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n // This component renders nothing to the React tree — the widget is DOM-injected\n return null;\n}\n"]}
|
package/dist/react.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {useRef,useEffect}from'react';var
|
|
1
|
+
import {useRef,useEffect}from'react';var w="https://app.dunefox.io",E="https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png",b="dunefox_chat_uuid";function L(){var t,e;try{let n=localStorage.getItem(b);return n||(n=(e=(t=crypto==null?void 0:crypto.randomUUID)==null?void 0:t.call(crypto))!=null?e:Math.random().toString(36).slice(2)+Date.now().toString(36),localStorage.setItem(b,n)),n}catch(n){return Math.random().toString(36).slice(2)+Date.now().toString(36)}}function C(t){if(document.getElementById("dunefox-styles"))return;let e=t.startsWith("top"),a=t.endsWith("left")?"left:16px":"right:16px",p=`
|
|
2
2
|
#dunefox-btn {
|
|
3
|
-
position: fixed; ${
|
|
3
|
+
position: fixed; ${e?"top:16px":"bottom:16px"}; ${a};
|
|
4
4
|
width: 60px; height: 60px; border-radius: 50%;
|
|
5
5
|
background: #1a1a1a; border: none; cursor: pointer;
|
|
6
6
|
display: flex; align-items: center; justify-content: center;
|
|
@@ -8,27 +8,68 @@ import {useRef,useEffect}from'react';var v="https://app.dunefox.io",w="https://d
|
|
|
8
8
|
box-shadow: 0 4px 20px rgba(0,0,0,.25);
|
|
9
9
|
transition: transform .25s ease;
|
|
10
10
|
}
|
|
11
|
-
#dunefox-btn:hover { transform: ${
|
|
11
|
+
#dunefox-btn:hover { transform: ${e?"translateY(2px) scale(1.05)":"translateY(-2px) scale(1.05)"}; }
|
|
12
12
|
#dunefox-frame {
|
|
13
|
-
position: fixed; ${
|
|
13
|
+
position: fixed; ${e?"top:88px":"bottom:88px"}; ${a};
|
|
14
14
|
width: 350px; height: calc(100vh - 160px); max-height: 600px;
|
|
15
15
|
border-radius: 16px; border: none; z-index: 2147483645;
|
|
16
16
|
box-shadow: 0 8px 32px rgba(0,0,0,.12);
|
|
17
17
|
opacity: 0; visibility: hidden;
|
|
18
|
-
transform: ${
|
|
18
|
+
transform: ${e?"translateY(-24px) scale(.96)":"translateY(24px) scale(.96)"};
|
|
19
19
|
transition: all .4s cubic-bezier(.4,0,.2,1);
|
|
20
20
|
}
|
|
21
21
|
#dunefox-frame.df-open {
|
|
22
22
|
opacity: 1; visibility: visible; transform: translateY(0) scale(1);
|
|
23
23
|
}
|
|
24
|
+
|
|
25
|
+
/* \u2500\u2500 Mobile close bar (hidden on desktop) \u2500\u2500 */
|
|
26
|
+
#dunefox-mobile-bar {
|
|
27
|
+
display: none;
|
|
28
|
+
}
|
|
29
|
+
|
|
24
30
|
@media (max-width: 768px) {
|
|
31
|
+
/* Mobile close bar */
|
|
32
|
+
#dunefox-mobile-bar {
|
|
33
|
+
position: fixed;
|
|
34
|
+
top: 0 !important; left: 0 !important; right: 0 !important;
|
|
35
|
+
height: 48px;
|
|
36
|
+
background: #1a1a1a;
|
|
37
|
+
z-index: 2147483647;
|
|
38
|
+
align-items: center;
|
|
39
|
+
padding: 0 16px;
|
|
40
|
+
gap: 10px;
|
|
41
|
+
border: none;
|
|
42
|
+
width: 100%;
|
|
43
|
+
cursor: pointer;
|
|
44
|
+
box-sizing: border-box;
|
|
45
|
+
}
|
|
46
|
+
#dunefox-mobile-bar.df-open { display: flex; }
|
|
47
|
+
|
|
48
|
+
/* Hide the corner toggle button on mobile when chat is open */
|
|
49
|
+
#dunefox-btn.df-hidden { display: none !important; }
|
|
50
|
+
|
|
51
|
+
/* Full-screen iframe, leaving room for close bar at top */
|
|
25
52
|
#dunefox-frame {
|
|
26
|
-
width: 100vw
|
|
27
|
-
|
|
28
|
-
|
|
53
|
+
width: 100vw !important;
|
|
54
|
+
height: calc(100% - 48px) !important;
|
|
55
|
+
height: calc(100dvh - 48px) !important;
|
|
56
|
+
max-height: none !important;
|
|
57
|
+
top: 48px !important;
|
|
58
|
+
bottom: 0 !important;
|
|
59
|
+
left: 0 !important;
|
|
60
|
+
right: 0 !important;
|
|
61
|
+
border-radius: 0;
|
|
62
|
+
transform: translateY(100%);
|
|
63
|
+
opacity: 1 !important;
|
|
64
|
+
visibility: visible !important;
|
|
65
|
+
box-shadow: none;
|
|
29
66
|
}
|
|
30
67
|
#dunefox-frame.df-open { transform: translateY(0); }
|
|
31
68
|
}
|
|
32
|
-
`,o=document.createElement("style");o.id="dunefox-styles",o.textContent=
|
|
33
|
-
|
|
69
|
+
`,o=document.createElement("style");o.id="dunefox-styles",o.textContent=p,document.head.appendChild(o);}var B='<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>',I='<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5"/><path d="m12 5-7 7 7 7"/></svg>',f=false;function g(t){let e=typeof t=="string"?{tenantId:t}:t;if(f){console.warn("[DunefoxChat] Already initialised. Call destroy() first.");return}let{tenantId:n,position:a="bottom-right",defaultOpen:u=false,baseUrl:x=w,iconUrl:h=E}=e;if(!n)throw new Error("[DunefoxChat] tenantId is required.");(l=>document.readyState==="loading"?document.addEventListener("DOMContentLoaded",l,{once:true}):l())(()=>{C(a);let l=L(),p=`${x}/api/${n}?uuid=${l}`,o=document.createElement("iframe");o.id="dunefox-frame",o.title="Dunefox Chat",o.loading="lazy",o.src=p;let r=document.createElement("button");r.id="dunefox-btn",r.setAttribute("aria-label","Open chat"),r.setAttribute("aria-expanded","false");let s=document.createElement("img");s.src=h,s.alt="",s.width=60,s.height=60,s.style.cssText="display:block;border-radius:50%;object-fit:cover;";let c=document.createElement("span");c.innerHTML=B,c.style.display="none",r.append(s,c);let d=document.createElement("button");d.id="dunefox-mobile-bar",d.setAttribute("aria-label","Close chat"),d.innerHTML=`
|
|
70
|
+
${I}
|
|
71
|
+
<span style="color:white;font-family:system-ui,-apple-system,sans-serif;font-size:14px;font-weight:600;letter-spacing:.01em;">
|
|
72
|
+
Close chat
|
|
73
|
+
</span>
|
|
74
|
+
`;let i=u,m=()=>{o.classList.toggle("df-open",i),d.classList.toggle("df-open",i),r.classList.toggle("df-hidden",i),s.style.display=i?"none":"block",c.style.display=i?"block":"none",r.setAttribute("aria-expanded",String(i));};r.addEventListener("click",()=>{i=!i,m();}),d.addEventListener("click",()=>{i=false,m();}),document.body.append(o,d,r),m(),f=true;});}function y(){var t,e,n,a;(t=document.getElementById("dunefox-frame"))==null||t.remove(),(e=document.getElementById("dunefox-btn"))==null||e.remove(),(n=document.getElementById("dunefox-mobile-bar"))==null||n.remove(),(a=document.getElementById("dunefox-styles"))==null||a.remove(),f=false;}function A(t){let e=useRef(t);return useEffect(()=>{if(typeof window!="undefined")return g(e.current),()=>{y();}},[]),null}export{A as DunefoxChatbot};//# sourceMappingURL=react.mjs.map
|
|
34
75
|
//# sourceMappingURL=react.mjs.map
|
package/dist/react.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core.ts","../src/react.tsx"],"names":["DEFAULT_BASE","DEFAULT_ICON","UUID_KEY","getOrCreateUUID","_a","_b","id","e","injectStyles","position","isTop","hSide","css","tag","CLOSE_SVG","_initialised","init","options","opts","tenantId","defaultOpen","baseUrl","iconUrl","cb","uuid","src","iframe","btn","img","closeSpan","open","applyState","destroy","_c","DunefoxChatbot","props","optsRef","useRef","useEffect"],"mappings":"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","file":"react.mjs","sourcesContent":["/** Options accepted by DunefoxChat.init() */\nexport interface DunefoxChatOptions {\n /** Your Dunefox Tenant ID — found in Settings > Install Widget */\n tenantId: string;\n /**\n * Where to anchor the widget. Defaults to \"bottom-right\".\n * Supports: \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\"\n */\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\n /** Open the chat panel on load. Defaults to false. */\n defaultOpen?: boolean;\n /** Override the base URL (for self-hosted / staging). */\n baseUrl?: string;\n /** Icon image URL override. Falls back to Dunefox CDN asset. */\n iconUrl?: string;\n}\n\nconst DEFAULT_BASE = 'https://app.dunefox.io';\nconst DEFAULT_ICON = 'https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png';\nconst UUID_KEY = 'dunefox_chat_uuid';\n\n// ─── UUID generation ──────────────────────────────────────────────────────────\nfunction getOrCreateUUID(): string {\n try {\n let id = localStorage.getItem(UUID_KEY);\n if (!id) {\n id = crypto?.randomUUID?.()\n ?? Math.random().toString(36).slice(2) + Date.now().toString(36);\n localStorage.setItem(UUID_KEY, id);\n }\n return id;\n } catch {\n // Incognito / storage blocked — generate ephemeral ID\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\n }\n}\n\n// ─── CSS injector ─────────────────────────────────────────────────────────────\nfunction injectStyles(position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'): void {\n if (document.getElementById('dunefox-styles')) return;\n\n const isTop = position.startsWith('top');\n const isLeft = position.endsWith('left');\n const hSide = isLeft ? 'left:16px' : 'right:16px';\n const vBtn = isTop ? 'top:16px' : 'bottom:16px';\n // Panel opens away from the button edge\n const vFrame = isTop ? 'top:88px' : 'bottom:88px';\n // Slide direction: panels below slide up, panels above slide down\n const slideOut = isTop ? 'translateY(-24px) scale(.96)' : 'translateY(24px) scale(.96)';\n // Hover nudge direction\n const hoverTranslate = isTop ? 'translateY(2px) scale(1.05)' : 'translateY(-2px) scale(1.05)';\n // Mobile slide direction\n const mobileSlideOut = isTop ? 'translateY(-100%)' : 'translateY(100%)';\n\n const css = `\n #dunefox-btn {\n position: fixed; ${vBtn}; ${hSide};\n width: 60px; height: 60px; border-radius: 50%;\n background: #1a1a1a; border: none; cursor: pointer;\n display: flex; align-items: center; justify-content: center;\n z-index: 2147483646;\n box-shadow: 0 4px 20px rgba(0,0,0,.25);\n transition: transform .25s ease;\n }\n #dunefox-btn:hover { transform: ${hoverTranslate}; }\n #dunefox-frame {\n position: fixed; ${vFrame}; ${hSide};\n width: 350px; height: calc(100vh - 160px); max-height: 600px;\n border-radius: 16px; border: none; z-index: 2147483645;\n box-shadow: 0 8px 32px rgba(0,0,0,.12);\n opacity: 0; visibility: hidden;\n transform: ${slideOut};\n transition: all .4s cubic-bezier(.4,0,.2,1);\n }\n #dunefox-frame.df-open {\n opacity: 1; visibility: visible; transform: translateY(0) scale(1);\n }\n @media (max-width: 768px) {\n #dunefox-frame {\n width: 100vw; height: 100vh; max-height: none;\n inset: 0; border-radius: 0; transform: ${mobileSlideOut};\n bottom: unset; right: unset; left: unset; top: unset;\n }\n #dunefox-frame.df-open { transform: translateY(0); }\n }\n `;\n const tag = document.createElement('style');\n tag.id = 'dunefox-styles';\n tag.textContent = css;\n document.head.appendChild(tag);\n}\n\n// ─── SVG icons ────────────────────────────────────────────────────────────────\nconst CLOSE_SVG = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"26\" height=\"26\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"white\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\"/><path d=\"m6 6 12 12\"/></svg>`;\n\n// ─── Main init ────────────────────────────────────────────────────────────────\nlet _initialised = false;\n\n/**\n * Initialise the Dunefox chatbot widget.\n * Call once on DOMContentLoaded or after your framework has mounted.\n *\n * @example\n * import { init } from 'dunefox-chatbot';\n * init({ tenantId: 'YOUR_TENANT_ID' });\n */\nexport function init(options: DunefoxChatOptions | string): void {\n // Allow shorthand: init('tenantId')\n const opts: DunefoxChatOptions =\n typeof options === 'string' ? { tenantId: options } : options;\n\n if (_initialised) {\n console.warn('[DunefoxChat] Already initialised. Call destroy() first.');\n return;\n }\n\n const {\n tenantId,\n position = 'bottom-right',\n defaultOpen = false,\n baseUrl = DEFAULT_BASE,\n iconUrl = DEFAULT_ICON,\n } = opts;\n\n if (!tenantId) throw new Error('[DunefoxChat] tenantId is required.');\n\n // Wait for DOM if we're running during <head> parse\n const ready = (cb: () => void) =>\n document.readyState === 'loading'\n ? document.addEventListener('DOMContentLoaded', cb, { once: true })\n : cb();\n\n ready(() => {\n injectStyles(position);\n\n const uuid = getOrCreateUUID();\n const src = `${baseUrl}/api/${tenantId}?uuid=${uuid}`;\n\n // ── iframe ──\n const iframe = document.createElement('iframe');\n iframe.id = 'dunefox-frame';\n iframe.title = 'Dunefox Chat';\n iframe.loading = 'lazy';\n iframe.src = src;\n\n // ── toggle button ──\n const btn = document.createElement('button');\n btn.id = 'dunefox-btn';\n btn.setAttribute('aria-label', 'Open chat');\n btn.setAttribute('aria-expanded', 'false');\n\n const img = document.createElement('img');\n img.src = iconUrl;\n img.alt = '';\n img.width = 60;\n img.height = 60;\n img.style.cssText = 'display:block;border-radius:50%;object-fit:cover;';\n\n const closeSpan = document.createElement('span');\n closeSpan.innerHTML = CLOSE_SVG;\n closeSpan.style.display = 'none';\n\n btn.append(img, closeSpan);\n\n // ── state ──\n let open = defaultOpen;\n const applyState = () => {\n iframe.classList.toggle('df-open', open);\n img.style.display = open ? 'none' : 'block';\n closeSpan.style.display = open ? 'block' : 'none';\n btn.setAttribute('aria-expanded', String(open));\n };\n\n btn.addEventListener('click', () => {\n open = !open;\n applyState();\n });\n\n document.body.append(iframe, btn);\n applyState();\n _initialised = true;\n });\n}\n\n/**\n * Programmatically open the chat panel.\n */\nexport function open(): void {\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\n const btn = document.getElementById('dunefox-btn');\n if (!iframe || !btn) return;\n iframe.classList.add('df-open');\n btn.setAttribute('aria-expanded', 'true');\n}\n\n/**\n * Programmatically close the chat panel.\n */\nexport function close(): void {\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\n const btn = document.getElementById('dunefox-btn');\n if (!iframe || !btn) return;\n iframe.classList.remove('df-open');\n btn.setAttribute('aria-expanded', 'false');\n}\n\n/**\n * Remove the widget entirely.\n */\nexport function destroy(): void {\n document.getElementById('dunefox-frame')?.remove();\n document.getElementById('dunefox-btn')?.remove();\n document.getElementById('dunefox-styles')?.remove();\n _initialised = false;\n}\n","import { useEffect, useRef } from 'react';\nimport { init, destroy } from './core';\nimport type { DunefoxChatOptions } from './core';\n\nexport interface DunefoxChatbotProps extends DunefoxChatOptions {}\n\n/**\n * React component wrapper for the Dunefox Chatbot widget.\n *\n * Drop it anywhere in your component tree — typically at the root layout level.\n * SSR-safe: the widget is only mounted in the browser via useEffect.\n *\n * @example\n * // Next.js App Router (app/layout.tsx)\n * import { DunefoxChatbot } from 'dunefox-chatbot/react';\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html><body>\n * {children}\n * <DunefoxChatbot tenantId=\"YOUR_TENANT_ID\" />\n * </body></html>\n * );\n * }\n */\nexport function DunefoxChatbot(props: DunefoxChatbotProps) {\n // Capture props at mount time — we intentionally don't re-init on prop changes\n const optsRef = useRef(props);\n\n useEffect(() => {\n // Prevent running during SSR (window check is a safety net)\n if (typeof window === 'undefined') return;\n\n init(optsRef.current);\n\n return () => {\n destroy();\n };\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n // This component renders nothing to the React tree — the widget is DOM-injected\n return null;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/core.ts","../src/react.tsx"],"names":["DEFAULT_BASE","DEFAULT_ICON","UUID_KEY","getOrCreateUUID","_a","_b","id","e","injectStyles","position","isTop","hSide","css","tag","CLOSE_SVG","BACK_SVG","_initialised","init","options","opts","tenantId","defaultOpen","baseUrl","iconUrl","cb","uuid","src","iframe","btn","img","closeSpan","mobileBar","open","applyState","destroy","_c","_d","DunefoxChatbot","props","optsRef","useRef","useEffect"],"mappings":"qCAiBA,IAAMA,CAAAA,CAAe,wBAAA,CACfC,CAAAA,CAAe,0DAAA,CACfC,CAAAA,CAAW,oBAIjB,SAASC,CAAAA,EAA0B,CAvBnC,IAAAC,CAAAA,CAAAC,EAwBE,GAAI,CACF,IAAIC,CAAAA,CAAK,YAAA,CAAa,OAAA,CAAQJ,CAAQ,CAAA,CACtC,OAAKI,IACHA,CAAAA,CAAAA,CAAKD,CAAAA,CAAAA,CAAAD,EAAA,MAAA,EAAA,IAAA,CAAA,KAAA,CAAA,CAAA,MAAA,CAAQ,UAAA,GAAR,IAAA,CAAA,KAAA,CAAA,CAAAA,CAAAA,CAAA,IAAA,CAAA,MAAA,CAAA,GAAA,IAAA,CAAAC,CAAAA,CACA,KAAK,MAAA,EAAO,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA,CAAI,IAAA,CAAK,GAAA,EAAI,CAAE,QAAA,CAAS,EAAE,EACjE,YAAA,CAAa,OAAA,CAAQH,EAAUI,CAAE,CAAA,CAAA,CAE5BA,CACT,CAAA,MAAQC,CAAAA,CAAA,CAEN,OAAO,IAAA,CAAK,MAAA,GAAS,QAAA,CAAS,EAAE,EAAE,KAAA,CAAM,CAAC,EAAI,IAAA,CAAK,GAAA,EAAI,CAAE,QAAA,CAAS,EAAE,CACrE,CACF,CAGA,SAASC,EAAaC,CAAAA,CAA2E,CAC/F,GAAI,QAAA,CAAS,cAAA,CAAe,gBAAgB,CAAA,CAAG,OAE/C,IAAMC,EAAUD,CAAAA,CAAS,UAAA,CAAW,KAAK,CAAA,CAEnCE,CAAAA,CADUF,CAAAA,CAAS,SAAS,MAAM,CAAA,CACd,WAAA,CAAgB,YAAA,CAWpCG,CAAAA,CAAM;AAAA;AAAA,uBAAA,EAVIF,CAAAA,CAAU,UAAA,CAAgB,aAYf,CAAA,EAAA,EAAKC,CAAK,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oCAAA,EANdD,CAAAA,CAAQ,8BAAgC,8BAcb,CAAA;AAAA;AAAA,uBAAA,EAlBlCA,CAAAA,CAAU,UAAA,CAAgB,aAoBb,CAAA,EAAA,EAAKC,CAAK,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAAA,EAlBtBD,CAAAA,CAAS,+BAAiC,6BAuBlC,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAAA,CAAA,CAoDnBG,CAAAA,CAAM,SAAS,aAAA,CAAc,OAAO,EAC1CA,CAAAA,CAAI,EAAA,CAAK,iBACTA,CAAAA,CAAI,WAAA,CAAcD,EAClB,QAAA,CAAS,IAAA,CAAK,YAAYC,CAAG,EAC/B,CAGA,IAAMC,CAAAA,CAAY,oOAAA,CACZC,CAAAA,CAAY,qOAAA,CAGdC,CAAAA,CAAe,MAUZ,SAASC,CAAAA,CAAKC,EAA4C,CAE/D,IAAMC,EACJ,OAAOD,CAAAA,EAAY,SAAW,CAAE,QAAA,CAAUA,CAAQ,CAAA,CAAIA,CAAAA,CAExD,GAAIF,CAAAA,CAAc,CAChB,QAAQ,IAAA,CAAK,0DAA0D,CAAA,CACvE,MACF,CAEA,GAAM,CACJ,QAAA,CAAAI,CAAAA,CACA,SAAAX,CAAAA,CAAW,cAAA,CACX,YAAAY,CAAAA,CAAc,KAAA,CACd,QAAAC,CAAAA,CAAUtB,CAAAA,CACV,QAAAuB,CAAAA,CAAUtB,CACZ,EAAIkB,CAAAA,CAEJ,GAAI,CAACC,CAAAA,CAAU,MAAM,IAAI,KAAA,CAAM,qCAAqC,CAAA,CAAA,CAGrDI,GACb,QAAA,CAAS,UAAA,GAAe,UACpB,QAAA,CAAS,gBAAA,CAAiB,mBAAoBA,CAAAA,CAAI,CAAE,KAAM,IAAK,CAAC,EAChEA,CAAAA,EAAG,EAEH,IAAM,CACVhB,CAAAA,CAAaC,CAAQ,CAAA,CAErB,IAAMgB,CAAAA,CAAOtB,CAAAA,EAAgB,CACvBuB,CAAAA,CAAM,GAAGJ,CAAO,CAAA,KAAA,EAAQF,CAAQ,CAAA,MAAA,EAASK,CAAI,GAG7CE,CAAAA,CAAS,QAAA,CAAS,cAAc,QAAQ,CAAA,CAC9CA,EAAO,EAAA,CAAK,eAAA,CACZA,EAAO,KAAA,CAAQ,cAAA,CACfA,EAAO,OAAA,CAAU,MAAA,CACjBA,CAAAA,CAAO,GAAA,CAAMD,CAAAA,CAGb,IAAME,EAAM,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA,CAC3CA,CAAAA,CAAI,GAAK,aAAA,CACTA,CAAAA,CAAI,aAAa,YAAA,CAAc,WAAW,EAC1CA,CAAAA,CAAI,YAAA,CAAa,gBAAiB,OAAO,CAAA,CAEzC,IAAMC,CAAAA,CAAM,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA,CACxCA,CAAAA,CAAI,IAAMN,CAAAA,CACVM,CAAAA,CAAI,IAAM,EAAA,CACVA,CAAAA,CAAI,MAAQ,EAAA,CACZA,CAAAA,CAAI,OAAS,EAAA,CACbA,CAAAA,CAAI,MAAM,OAAA,CAAU,mDAAA,CAEpB,IAAMC,CAAAA,CAAY,QAAA,CAAS,cAAc,MAAM,CAAA,CAC/CA,CAAAA,CAAU,SAAA,CAAYhB,CAAAA,CACtBgB,CAAAA,CAAU,MAAM,OAAA,CAAU,MAAA,CAE1BF,EAAI,MAAA,CAAOC,CAAAA,CAAKC,CAAS,CAAA,CAKzB,IAAMC,EAAY,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA,CACjDA,CAAAA,CAAU,GAAK,oBAAA,CACfA,CAAAA,CAAU,aAAa,YAAA,CAAc,YAAY,CAAA,CACjDA,CAAAA,CAAU,SAAA,CAAY;AAAA,MAAA,EAClBhB,CAAQ;AAAA;AAAA;AAAA;AAAA,IAAA,CAAA,CAOZ,IAAIiB,CAAAA,CAAOX,CAAAA,CACLY,CAAAA,CAAa,IAAM,CACvBN,CAAAA,CAAO,SAAA,CAAU,MAAA,CAAO,UAAWK,CAAI,CAAA,CACvCD,CAAAA,CAAU,SAAA,CAAU,OAAO,SAAA,CAAWC,CAAI,CAAA,CAC1CJ,CAAAA,CAAI,UAAU,MAAA,CAAO,WAAA,CAAaI,CAAI,CAAA,CACtCH,EAAI,KAAA,CAAM,OAAA,CAAUG,CAAAA,CAAO,MAAA,CAAS,QACpCF,CAAAA,CAAU,KAAA,CAAM,OAAA,CAAUE,CAAAA,CAAO,QAAU,MAAA,CAC3CJ,CAAAA,CAAI,YAAA,CAAa,eAAA,CAAiB,OAAOI,CAAI,CAAC,EAChD,CAAA,CAEAJ,EAAI,gBAAA,CAAiB,OAAA,CAAS,IAAM,CAClCI,EAAO,CAACA,CAAAA,CACRC,CAAAA,GACF,CAAC,CAAA,CAEDF,CAAAA,CAAU,gBAAA,CAAiB,OAAA,CAAS,IAAM,CACxCC,CAAAA,CAAO,KAAA,CACPC,CAAAA,GACF,CAAC,CAAA,CAED,QAAA,CAAS,IAAA,CAAK,OAAON,CAAAA,CAAQI,CAAAA,CAAWH,CAAG,CAAA,CAC3CK,GAAW,CACXjB,CAAAA,CAAe,KACjB,CAAC,EACH,CAiCO,SAASkB,CAAAA,EAAgB,CAlRhC,IAAA9B,CAAAA,CAAAC,CAAAA,CAAA8B,CAAAA,CAAAC,CAAAA,CAAAA,CAmREhC,EAAA,QAAA,CAAS,cAAA,CAAe,eAAe,CAAA,GAAvC,MAAAA,CAAAA,CAA0C,MAAA,EAAA,CAAA,CAC1CC,CAAAA,CAAA,QAAA,CAAS,eAAe,aAAa,CAAA,GAArC,IAAA,EAAAA,CAAAA,CAAwC,UACxC8B,CAAAA,CAAA,QAAA,CAAS,cAAA,CAAe,oBAAoB,IAA5C,IAAA,EAAAA,CAAAA,CAA+C,MAAA,EAAA,CAAA,CAC/CC,CAAAA,CAAA,SAAS,cAAA,CAAe,gBAAgB,CAAA,GAAxC,IAAA,EAAAA,EAA2C,MAAA,EAAA,CAC3CpB,CAAAA,CAAe,MACjB,CC/PO,SAASqB,CAAAA,CAAeC,CAAAA,CAA4B,CAEzD,IAAMC,EAAUC,MAAAA,CAAOF,CAAK,CAAA,CAE5B,OAAAG,UAAU,IAAM,CAEd,GAAI,OAAO,QAAW,WAAA,CAEtB,OAAAxB,CAAAA,CAAKsB,CAAAA,CAAQ,OAAO,CAAA,CAEb,IAAM,CACXL,CAAAA,GACF,CACF,CAAA,CAAG,EAAE,EAGE,IACT","file":"react.mjs","sourcesContent":["/** Options accepted by DunefoxChat.init() */\nexport interface DunefoxChatOptions {\n /** Your Dunefox Tenant ID — found in Settings > Install Widget */\n tenantId: string;\n /**\n * Where to anchor the widget. Defaults to \"bottom-right\".\n * Supports: \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\"\n */\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\n /** Open the chat panel on load. Defaults to false. */\n defaultOpen?: boolean;\n /** Override the base URL (for self-hosted / staging). */\n baseUrl?: string;\n /** Icon image URL override. Falls back to Dunefox CDN asset. */\n iconUrl?: string;\n}\n\nconst DEFAULT_BASE = 'https://app.dunefox.io';\nconst DEFAULT_ICON = 'https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png';\nconst UUID_KEY = 'dunefox_chat_uuid';\nconst CLOSE_BAR_H = 48; // px — mobile top close bar height\n\n// ─── UUID generation ──────────────────────────────────────────────────────────\nfunction getOrCreateUUID(): string {\n try {\n let id = localStorage.getItem(UUID_KEY);\n if (!id) {\n id = crypto?.randomUUID?.()\n ?? Math.random().toString(36).slice(2) + Date.now().toString(36);\n localStorage.setItem(UUID_KEY, id);\n }\n return id;\n } catch {\n // Incognito / storage blocked — generate ephemeral ID\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\n }\n}\n\n// ─── CSS injector ─────────────────────────────────────────────────────────────\nfunction injectStyles(position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'): void {\n if (document.getElementById('dunefox-styles')) return;\n\n const isTop = position.startsWith('top');\n const isLeft = position.endsWith('left');\n const hSide = isLeft ? 'left:16px' : 'right:16px';\n const vBtn = isTop ? 'top:16px' : 'bottom:16px';\n // Panel opens away from the button edge\n const vFrame = isTop ? 'top:88px' : 'bottom:88px';\n // Slide direction: panels below slide up, panels above slide down\n const slideOut = isTop ? 'translateY(-24px) scale(.96)' : 'translateY(24px) scale(.96)';\n // Hover nudge direction\n const hoverTranslate = isTop ? 'translateY(2px) scale(1.05)' : 'translateY(-2px) scale(1.05)';\n // Mobile slide direction (always slides up from bottom)\n const mobileSlideOut = 'translateY(100%)';\n\n const css = `\n #dunefox-btn {\n position: fixed; ${vBtn}; ${hSide};\n width: 60px; height: 60px; border-radius: 50%;\n background: #1a1a1a; border: none; cursor: pointer;\n display: flex; align-items: center; justify-content: center;\n z-index: 2147483646;\n box-shadow: 0 4px 20px rgba(0,0,0,.25);\n transition: transform .25s ease;\n }\n #dunefox-btn:hover { transform: ${hoverTranslate}; }\n #dunefox-frame {\n position: fixed; ${vFrame}; ${hSide};\n width: 350px; height: calc(100vh - 160px); max-height: 600px;\n border-radius: 16px; border: none; z-index: 2147483645;\n box-shadow: 0 8px 32px rgba(0,0,0,.12);\n opacity: 0; visibility: hidden;\n transform: ${slideOut};\n transition: all .4s cubic-bezier(.4,0,.2,1);\n }\n #dunefox-frame.df-open {\n opacity: 1; visibility: visible; transform: translateY(0) scale(1);\n }\n\n /* ── Mobile close bar (hidden on desktop) ── */\n #dunefox-mobile-bar {\n display: none;\n }\n\n @media (max-width: 768px) {\n /* Mobile close bar */\n #dunefox-mobile-bar {\n position: fixed;\n top: 0 !important; left: 0 !important; right: 0 !important;\n height: ${CLOSE_BAR_H}px;\n background: #1a1a1a;\n z-index: 2147483647;\n align-items: center;\n padding: 0 16px;\n gap: 10px;\n border: none;\n width: 100%;\n cursor: pointer;\n box-sizing: border-box;\n }\n #dunefox-mobile-bar.df-open { display: flex; }\n\n /* Hide the corner toggle button on mobile when chat is open */\n #dunefox-btn.df-hidden { display: none !important; }\n\n /* Full-screen iframe, leaving room for close bar at top */\n #dunefox-frame {\n width: 100vw !important;\n height: calc(100% - ${CLOSE_BAR_H}px) !important;\n height: calc(100dvh - ${CLOSE_BAR_H}px) !important;\n max-height: none !important;\n top: ${CLOSE_BAR_H}px !important;\n bottom: 0 !important;\n left: 0 !important;\n right: 0 !important;\n border-radius: 0;\n transform: ${mobileSlideOut};\n opacity: 1 !important;\n visibility: visible !important;\n box-shadow: none;\n }\n #dunefox-frame.df-open { transform: translateY(0); }\n }\n `;\n const tag = document.createElement('style');\n tag.id = 'dunefox-styles';\n tag.textContent = css;\n document.head.appendChild(tag);\n}\n\n// ─── SVG icons ────────────────────────────────────────────────────────────────\nconst CLOSE_SVG = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"22\" height=\"22\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"white\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\"/><path d=\"m6 6 12 12\"/></svg>`;\nconst BACK_SVG = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"white\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M19 12H5\"/><path d=\"m12 5-7 7 7 7\"/></svg>`;\n\n// ─── Main init ────────────────────────────────────────────────────────────────\nlet _initialised = false;\n\n/**\n * Initialise the Dunefox chatbot widget.\n * Call once on DOMContentLoaded or after your framework has mounted.\n *\n * @example\n * import { init } from 'dunefox-chatbot';\n * init({ tenantId: 'YOUR_TENANT_ID' });\n */\nexport function init(options: DunefoxChatOptions | string): void {\n // Allow shorthand: init('tenantId')\n const opts: DunefoxChatOptions =\n typeof options === 'string' ? { tenantId: options } : options;\n\n if (_initialised) {\n console.warn('[DunefoxChat] Already initialised. Call destroy() first.');\n return;\n }\n\n const {\n tenantId,\n position = 'bottom-right',\n defaultOpen = false,\n baseUrl = DEFAULT_BASE,\n iconUrl = DEFAULT_ICON,\n } = opts;\n\n if (!tenantId) throw new Error('[DunefoxChat] tenantId is required.');\n\n // Wait for DOM if we're running during <head> parse\n const ready = (cb: () => void) =>\n document.readyState === 'loading'\n ? document.addEventListener('DOMContentLoaded', cb, { once: true })\n : cb();\n\n ready(() => {\n injectStyles(position);\n\n const uuid = getOrCreateUUID();\n const src = `${baseUrl}/api/${tenantId}?uuid=${uuid}`;\n\n // ── iframe ──\n const iframe = document.createElement('iframe');\n iframe.id = 'dunefox-frame';\n iframe.title = 'Dunefox Chat';\n iframe.loading = 'lazy';\n iframe.src = src;\n\n // ── toggle button ──\n const btn = document.createElement('button');\n btn.id = 'dunefox-btn';\n btn.setAttribute('aria-label', 'Open chat');\n btn.setAttribute('aria-expanded', 'false');\n\n const img = document.createElement('img');\n img.src = iconUrl;\n img.alt = '';\n img.width = 60;\n img.height = 60;\n img.style.cssText = 'display:block;border-radius:50%;object-fit:cover;';\n\n const closeSpan = document.createElement('span');\n closeSpan.innerHTML = CLOSE_SVG;\n closeSpan.style.display = 'none';\n\n btn.append(img, closeSpan);\n\n // ── mobile close bar ──\n // A full-width bar visible only on mobile when the chat is open,\n // so the user always has a clear, tappable way to close the chat.\n const mobileBar = document.createElement('button');\n mobileBar.id = 'dunefox-mobile-bar';\n mobileBar.setAttribute('aria-label', 'Close chat');\n mobileBar.innerHTML = `\n ${BACK_SVG}\n <span style=\"color:white;font-family:system-ui,-apple-system,sans-serif;font-size:14px;font-weight:600;letter-spacing:.01em;\">\n Close chat\n </span>\n `;\n\n // ── state ──\n let open = defaultOpen;\n const applyState = () => {\n iframe.classList.toggle('df-open', open);\n mobileBar.classList.toggle('df-open', open);\n btn.classList.toggle('df-hidden', open); // hides on mobile via CSS\n img.style.display = open ? 'none' : 'block';\n closeSpan.style.display = open ? 'block' : 'none';\n btn.setAttribute('aria-expanded', String(open));\n };\n\n btn.addEventListener('click', () => {\n open = !open;\n applyState();\n });\n\n mobileBar.addEventListener('click', () => {\n open = false;\n applyState();\n });\n\n document.body.append(iframe, mobileBar, btn);\n applyState();\n _initialised = true;\n });\n}\n\n/**\n * Programmatically open the chat panel.\n */\nexport function open(): void {\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\n const btn = document.getElementById('dunefox-btn');\n const bar = document.getElementById('dunefox-mobile-bar');\n if (!iframe || !btn) return;\n iframe.classList.add('df-open');\n bar?.classList.add('df-open');\n btn.classList.add('df-hidden');\n btn.setAttribute('aria-expanded', 'true');\n}\n\n/**\n * Programmatically close the chat panel.\n */\nexport function close(): void {\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\n const btn = document.getElementById('dunefox-btn');\n const bar = document.getElementById('dunefox-mobile-bar');\n if (!iframe || !btn) return;\n iframe.classList.remove('df-open');\n bar?.classList.remove('df-open');\n btn.classList.remove('df-hidden');\n btn.setAttribute('aria-expanded', 'false');\n}\n\n/**\n * Remove the widget entirely.\n */\nexport function destroy(): void {\n document.getElementById('dunefox-frame')?.remove();\n document.getElementById('dunefox-btn')?.remove();\n document.getElementById('dunefox-mobile-bar')?.remove();\n document.getElementById('dunefox-styles')?.remove();\n _initialised = false;\n}\n\n","import { useEffect, useRef } from 'react';\nimport { init, destroy } from './core';\nimport type { DunefoxChatOptions } from './core';\n\nexport interface DunefoxChatbotProps extends DunefoxChatOptions {}\n\n/**\n * React component wrapper for the Dunefox Chatbot widget.\n *\n * Drop it anywhere in your component tree — typically at the root layout level.\n * SSR-safe: the widget is only mounted in the browser via useEffect.\n *\n * @example\n * // Next.js App Router (app/layout.tsx)\n * import { DunefoxChatbot } from 'dunefox-chatbot/react';\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html><body>\n * {children}\n * <DunefoxChatbot tenantId=\"YOUR_TENANT_ID\" />\n * </body></html>\n * );\n * }\n */\nexport function DunefoxChatbot(props: DunefoxChatbotProps) {\n // Capture props at mount time — we intentionally don't re-init on prop changes\n const optsRef = useRef(props);\n\n useEffect(() => {\n // Prevent running during SSR (window check is a safety net)\n if (typeof window === 'undefined') return;\n\n init(optsRef.current);\n\n return () => {\n destroy();\n };\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n // This component renders nothing to the React tree — the widget is DOM-injected\n return null;\n}\n"]}
|