dunefox-chatbot 1.0.2 → 1.0.4
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 +20 -3
- package/dist/chatbot.umd.global.js +8 -3
- package/dist/index.js +9 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +9 -4
- package/dist/index.mjs.map +1 -1
- package/dist/react.js +8 -3
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +8 -3
- package/dist/react.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# dunefox-chatbot
|
|
2
2
|
|
|
3
|
-
Official SDK for embedding the [Dunefox](https://dunefox.io)
|
|
3
|
+
Official SDK for embedding the [Dunefox AI chatbot](https://dunefox.io) widget on any website.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/dunefox-chatbot)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://dunefox.io)
|
|
8
|
+
|
|
9
|
+
**[Dunefox](https://dunefox.io)** is an AI-powered customer support platform that lets you build and deploy intelligent chatbots. Get your **Business ID** from the [Dunefox Console → Integrations](https://dunefox.io).
|
|
6
10
|
|
|
7
11
|
---
|
|
8
12
|
|
|
@@ -92,7 +96,7 @@ onBeforeUnmount(async () => {
|
|
|
92
96
|
|
|
93
97
|
| Option | Type | Default | Description |
|
|
94
98
|
|--------|------|---------|-------------|
|
|
95
|
-
| `tenantId` | `string` | **required** | Your Dunefox tenant ID |
|
|
99
|
+
| `tenantId` | `string` | **required** | Your [Dunefox](https://dunefox.io) tenant ID |
|
|
96
100
|
| `position` | `'bottom-right' \| 'bottom-left'` | `'bottom-right'` | Widget anchor position |
|
|
97
101
|
| `defaultOpen` | `boolean` | `false` | Open the panel on load |
|
|
98
102
|
| `baseUrl` | `string` | `https://app.dunefox.io` | Override for staging/self-hosted |
|
|
@@ -128,6 +132,19 @@ npm run build
|
|
|
128
132
|
|
|
129
133
|
---
|
|
130
134
|
|
|
135
|
+
## About Dunefox
|
|
136
|
+
|
|
137
|
+
[Dunefox](https://dunefox.io) is a modern AI customer support platform for businesses of all sizes. With Dunefox you can:
|
|
138
|
+
|
|
139
|
+
- 🤖 Deploy an [AI chatbot](https://dunefox.io) on your website in minutes
|
|
140
|
+
- 💬 Manage conversations from a unified inbox
|
|
141
|
+
- 📊 Gain insights with real-time analytics
|
|
142
|
+
- 🔗 Integrate with your existing tools via webhooks and APIs
|
|
143
|
+
|
|
144
|
+
Learn more and get started for free at **[dunefox.io](https://dunefox.io)**.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
131
148
|
## License
|
|
132
149
|
|
|
133
150
|
MIT © [Dunefox](https://dunefox.io)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var DunefoxChat=(()=>{var m=Object.defineProperty;var I=Object.getOwnPropertyDescriptor;var S=Object.getOwnPropertyNames;var
|
|
1
|
+
"use strict";var DunefoxChat=(()=>{var m=Object.defineProperty;var I=Object.getOwnPropertyDescriptor;var S=Object.getOwnPropertyNames;var L=Object.prototype.hasOwnProperty;var O=(e,t)=>{for(var n in t)m(e,n,{get:t[n],enumerable:!0})},D=(e,t,n,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of S(t))!L.call(e,a)&&a!==n&&m(e,a,{get:()=>t[a],enumerable:!(i=I(t,a))||i.enumerable});return e};var C=e=>D(m({},"__esModule",{value:!0}),e);var T={};O(T,{close:()=>v,destroy:()=>w,init:()=>g,open:()=>y});var B="https://app.dunefox.io",U="https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png",b="dunefox_chat_uuid";function k(){var e,t;try{let n=localStorage.getItem(b);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(b,n)),n}catch(n){return Math.random().toString(36).slice(2)+Date.now().toString(36)}}function $(e){if(document.getElementById("dunefox-styles"))return;let t=e.startsWith("top"),i=e.endsWith("left")?"left:16px":"right:16px",p=`
|
|
2
2
|
#dunefox-btn {
|
|
3
3
|
position: fixed; ${t?"top:16px":"bottom:16px"}; ${i};
|
|
4
4
|
width: 60px; height: 60px; border-radius: 50%;
|
|
@@ -22,9 +22,14 @@
|
|
|
22
22
|
opacity: 1; visibility: visible; transform: translateY(0) scale(1);
|
|
23
23
|
}
|
|
24
24
|
@media (max-width: 768px) {
|
|
25
|
-
|
|
25
|
+
/* Only move button to top-right when chat is open on mobile */
|
|
26
|
+
body.df-chat-open #dunefox-btn {
|
|
26
27
|
bottom: unset !important; top: 16px !important;
|
|
27
28
|
right: 16px !important; left: unset !important;
|
|
29
|
+
/* Strip the circular button style \u2014 show just the X icon */
|
|
30
|
+
background: transparent !important;
|
|
31
|
+
box-shadow: none !important;
|
|
32
|
+
width: 36px !important; height: 36px !important;
|
|
28
33
|
}
|
|
29
34
|
#dunefox-frame {
|
|
30
35
|
width: 100% !important; height: 100% !important; max-height: none !important;
|
|
@@ -34,4 +39,4 @@
|
|
|
34
39
|
}
|
|
35
40
|
#dunefox-frame.df-open { transform: translateY(0) !important; }
|
|
36
41
|
}
|
|
37
|
-
`,o=document.createElement("style");o.id="dunefox-styles",o.textContent=p,document.head.appendChild(o)}var A='<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>',u=!1;function
|
|
42
|
+
`,o=document.createElement("style");o.id="dunefox-styles",o.textContent=p,document.head.appendChild(o)}var A='<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>',u=!1;function g(e){let t=typeof e=="string"?{tenantId:e}:e;if(u){console.warn("[DunefoxChat] Already initialised. Call destroy() first.");return}let{tenantId:n,position:i="bottom-right",defaultOpen:a=!1,baseUrl:f=B,iconUrl:x=U}=t;if(!n)throw new Error("[DunefoxChat] tenantId is required.");(l=>document.readyState==="loading"?document.addEventListener("DOMContentLoaded",l,{once:!0}):l())(()=>{$(i);let l=k(),p=`${f}/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=x,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=A,c.style.display="none",r.append(s,c);let d=a,h=()=>{o.classList.toggle("df-open",d),document.body.classList.toggle("df-chat-open",d),s.style.display=d?"none":"block",c.style.display=d?"block":"none",r.setAttribute("aria-expanded",String(d))};r.addEventListener("click",()=>{d=!d,h()}),document.body.append(o,r),h(),u=!0})}function y(){let e=document.getElementById("dunefox-frame"),t=document.getElementById("dunefox-btn");!e||!t||(e.classList.add("df-open"),t.setAttribute("aria-expanded","true"))}function v(){let e=document.getElementById("dunefox-frame"),t=document.getElementById("dunefox-btn");!e||!t||(e.classList.remove("df-open"),t.setAttribute("aria-expanded","false"))}function w(){var e,t,n;(e=document.getElementById("dunefox-frame"))==null||e.remove(),(t=document.getElementById("dunefox-btn"))==null||t.remove(),(n=document.getElementById("dunefox-styles"))==null||n.remove(),document.body.classList.remove("df-chat-open"),u=!1}return C(T);})();
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
'use strict';var
|
|
1
|
+
'use strict';var g="https://app.dunefox.io",y="https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png",h="dunefox_chat_uuid";function v(){var e,t;try{let n=localStorage.getItem(h);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(h,n)),n}catch(n){return Math.random().toString(36).slice(2)+Date.now().toString(36)}}function w(e){if(document.getElementById("dunefox-styles"))return;let t=e.startsWith("top"),d=e.endsWith("left")?"left:16px":"right:16px",c=`
|
|
2
2
|
#dunefox-btn {
|
|
3
3
|
position: fixed; ${t?"top:16px":"bottom:16px"}; ${d};
|
|
4
4
|
width: 60px; height: 60px; border-radius: 50%;
|
|
@@ -22,9 +22,14 @@
|
|
|
22
22
|
opacity: 1; visibility: visible; transform: translateY(0) scale(1);
|
|
23
23
|
}
|
|
24
24
|
@media (max-width: 768px) {
|
|
25
|
-
|
|
25
|
+
/* Only move button to top-right when chat is open on mobile */
|
|
26
|
+
body.df-chat-open #dunefox-btn {
|
|
26
27
|
bottom: unset !important; top: 16px !important;
|
|
27
28
|
right: 16px !important; left: unset !important;
|
|
29
|
+
/* Strip the circular button style \u2014 show just the X icon */
|
|
30
|
+
background: transparent !important;
|
|
31
|
+
box-shadow: none !important;
|
|
32
|
+
width: 36px !important; height: 36px !important;
|
|
28
33
|
}
|
|
29
34
|
#dunefox-frame {
|
|
30
35
|
width: 100% !important; height: 100% !important; max-height: none !important;
|
|
@@ -34,6 +39,6 @@
|
|
|
34
39
|
}
|
|
35
40
|
#dunefox-frame.df-open { transform: translateY(0) !important; }
|
|
36
41
|
}
|
|
37
|
-
`,o=document.createElement("style");o.id="dunefox-styles",o.textContent=c,document.head.appendChild(o);}var
|
|
38
|
-
exports.close=
|
|
42
|
+
`,o=document.createElement("style");o.id="dunefox-styles",o.textContent=c,document.head.appendChild(o);}var E='<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>',p=false;function I(e){let t=typeof e=="string"?{tenantId:e}:e;if(p){console.warn("[DunefoxChat] Already initialised. Call destroy() first.");return}let{tenantId:n,position:d="bottom-right",defaultOpen:m=false,baseUrl:u=g,iconUrl:f=y}=t;if(!n)throw new Error("[DunefoxChat] tenantId is required.");(s=>document.readyState==="loading"?document.addEventListener("DOMContentLoaded",s,{once:true}):s())(()=>{w(d);let s=v(),c=`${u}/api/${n}?uuid=${s}`,o=document.createElement("iframe");o.id="dunefox-frame",o.title="Dunefox Chat",o.loading="lazy",o.src=c;let a=document.createElement("button");a.id="dunefox-btn",a.setAttribute("aria-label","Open chat"),a.setAttribute("aria-expanded","false");let i=document.createElement("img");i.src=f,i.alt="",i.width=60,i.height=60,i.style.cssText="display:block;border-radius:50%;object-fit:cover;";let l=document.createElement("span");l.innerHTML=E,l.style.display="none",a.append(i,l);let r=m,x=()=>{o.classList.toggle("df-open",r),document.body.classList.toggle("df-chat-open",r),i.style.display=r?"none":"block",l.style.display=r?"block":"none",a.setAttribute("aria-expanded",String(r));};a.addEventListener("click",()=>{r=!r,x();}),document.body.append(o,a),x(),p=true;});}function S(){let e=document.getElementById("dunefox-frame"),t=document.getElementById("dunefox-btn");!e||!t||(e.classList.add("df-open"),t.setAttribute("aria-expanded","true"));}function L(){let e=document.getElementById("dunefox-frame"),t=document.getElementById("dunefox-btn");!e||!t||(e.classList.remove("df-open"),t.setAttribute("aria-expanded","false"));}function O(){var e,t,n;(e=document.getElementById("dunefox-frame"))==null||e.remove(),(t=document.getElementById("dunefox-btn"))==null||t.remove(),(n=document.getElementById("dunefox-styles"))==null||n.remove(),document.body.classList.remove("df-chat-open"),p=false;}
|
|
43
|
+
exports.close=L;exports.destroy=O;exports.init=I;exports.open=S;//# sourceMappingURL=index.js.map
|
|
39
44
|
//# 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":"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","file":"index.js","sourcesContent":["/** Options accepted by DunefoxChat.init() */\r\nexport interface DunefoxChatOptions {\r\n /** Your Dunefox Tenant ID — found in Settings > Install Widget */\r\n tenantId: string;\r\n /**\r\n * Where to anchor the widget. Defaults to \"bottom-right\".\r\n * Supports: \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\"\r\n */\r\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\r\n /** Open the chat panel on load. Defaults to false. */\r\n defaultOpen?: boolean;\r\n /** Override the base URL (for self-hosted / staging). */\r\n baseUrl?: string;\r\n /** Icon image URL override. Falls back to Dunefox CDN asset. */\r\n iconUrl?: string;\r\n}\r\n\r\nconst DEFAULT_BASE = 'https://app.dunefox.io';\r\nconst DEFAULT_ICON = 'https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png';\r\nconst UUID_KEY = 'dunefox_chat_uuid';\r\n\r\n// ─── UUID generation ──────────────────────────────────────────────────────────\r\nfunction getOrCreateUUID(): string {\r\n try {\r\n let id = localStorage.getItem(UUID_KEY);\r\n if (!id) {\r\n id = crypto?.randomUUID?.()\r\n ?? Math.random().toString(36).slice(2) + Date.now().toString(36);\r\n localStorage.setItem(UUID_KEY, id);\r\n }\r\n return id;\r\n } catch {\r\n // Incognito / storage blocked — generate ephemeral ID\r\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\r\n }\r\n}\r\n\r\n// ─── CSS injector ─────────────────────────────────────────────────────────────\r\nfunction injectStyles(position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'): void {\r\n if (document.getElementById('dunefox-styles')) return;\r\n\r\n const isTop = position.startsWith('top');\r\n const isLeft = position.endsWith('left');\r\n const hSide = isLeft ? 'left:16px' : 'right:16px';\r\n const vBtn = isTop ? 'top:16px' : 'bottom:16px';\r\n // Panel opens away from the button edge\r\n const vFrame = isTop ? 'top:88px' : 'bottom:88px';\r\n // Slide direction: panels below slide up, panels above slide down\r\n const slideOut = isTop ? 'translateY(-24px) scale(.96)' : 'translateY(24px) scale(.96)';\r\n // Hover nudge direction\r\n const hoverTranslate = isTop ? 'translateY(2px) scale(1.05)' : 'translateY(-2px) scale(1.05)';\r\n // Mobile slide direction\r\n const mobileSlideOut = isTop ? 'translateY(-100%)' : 'translateY(100%)';\r\n\r\n const css = `\r\n #dunefox-btn {\r\n position: fixed; ${vBtn}; ${hSide};\r\n width: 60px; height: 60px; border-radius: 50%;\r\n background: #1a1a1a; border: none; cursor: pointer;\r\n display: flex; align-items: center; justify-content: center;\r\n z-index: 2147483646;\r\n box-shadow: 0 4px 20px rgba(0,0,0,.25);\r\n transition: transform .25s ease;\r\n }\r\n #dunefox-btn:hover { transform: ${hoverTranslate}; }\r\n #dunefox-frame {\r\n position: fixed; ${vFrame}; ${hSide};\r\n width: 350px; height: calc(100vh - 160px); max-height: 600px;\r\n border-radius: 16px; border: none; z-index: 2147483645;\r\n box-shadow: 0 8px 32px rgba(0,0,0,.12);\r\n opacity: 0; visibility: hidden;\r\n transform: ${slideOut};\r\n transition: all .4s cubic-bezier(.4,0,.2,1);\r\n }\r\n #dunefox-frame.df-open {\r\n opacity: 1; visibility: visible; transform: translateY(0) scale(1);\r\n }\r\n @media (max-width: 768px) {\r\n #dunefox-btn {\r\n bottom: unset !important; top: 16px !important;\r\n right: 16px !important; left: unset !important;\r\n }\r\n #dunefox-frame {\r\n width: 100% !important; height: 100% !important; max-height: none !important;\r\n position: fixed !important; inset: 0 !important;\r\n border-radius: 0 !important;\r\n transform: ${mobileSlideOut} !important;\r\n }\r\n #dunefox-frame.df-open { transform: translateY(0) !important; }\r\n }\r\n `;\r\n const tag = document.createElement('style');\r\n tag.id = 'dunefox-styles';\r\n tag.textContent = css;\r\n document.head.appendChild(tag);\r\n}\r\n\r\n// ─── SVG icons ────────────────────────────────────────────────────────────────\r\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>`;\r\n\r\n// ─── Main init ────────────────────────────────────────────────────────────────\r\nlet _initialised = false;\r\n\r\n/**\r\n * Initialise the Dunefox chatbot widget.\r\n * Call once on DOMContentLoaded or after your framework has mounted.\r\n *\r\n * @example\r\n * import { init } from 'dunefox-chatbot';\r\n * init({ tenantId: 'YOUR_TENANT_ID' });\r\n */\r\nexport function init(options: DunefoxChatOptions | string): void {\r\n // Allow shorthand: init('tenantId')\r\n const opts: DunefoxChatOptions =\r\n typeof options === 'string' ? { tenantId: options } : options;\r\n\r\n if (_initialised) {\r\n console.warn('[DunefoxChat] Already initialised. Call destroy() first.');\r\n return;\r\n }\r\n\r\n const {\r\n tenantId,\r\n position = 'bottom-right',\r\n defaultOpen = false,\r\n baseUrl = DEFAULT_BASE,\r\n iconUrl = DEFAULT_ICON,\r\n } = opts;\r\n\r\n if (!tenantId) throw new Error('[DunefoxChat] tenantId is required.');\r\n\r\n // Wait for DOM if we're running during <head> parse\r\n const ready = (cb: () => void) =>\r\n document.readyState === 'loading'\r\n ? document.addEventListener('DOMContentLoaded', cb, { once: true })\r\n : cb();\r\n\r\n ready(() => {\r\n injectStyles(position);\r\n\r\n const uuid = getOrCreateUUID();\r\n const src = `${baseUrl}/api/${tenantId}?uuid=${uuid}`;\r\n\r\n // ── iframe ──\r\n const iframe = document.createElement('iframe');\r\n iframe.id = 'dunefox-frame';\r\n iframe.title = 'Dunefox Chat';\r\n iframe.loading = 'lazy';\r\n iframe.src = src;\r\n\r\n // ── toggle button ──\r\n const btn = document.createElement('button');\r\n btn.id = 'dunefox-btn';\r\n btn.setAttribute('aria-label', 'Open chat');\r\n btn.setAttribute('aria-expanded', 'false');\r\n\r\n const img = document.createElement('img');\r\n img.src = iconUrl;\r\n img.alt = '';\r\n img.width = 60;\r\n img.height = 60;\r\n img.style.cssText = 'display:block;border-radius:50%;object-fit:cover;';\r\n\r\n const closeSpan = document.createElement('span');\r\n closeSpan.innerHTML = CLOSE_SVG;\r\n closeSpan.style.display = 'none';\r\n\r\n btn.append(img, closeSpan);\r\n\r\n // ── state ──\r\n let open = defaultOpen;\r\n const applyState = () => {\r\n iframe.classList.toggle('df-open', open);\r\n img.style.display = open ? 'none' : 'block';\r\n closeSpan.style.display = open ? 'block' : 'none';\r\n btn.setAttribute('aria-expanded', String(open));\r\n };\r\n\r\n btn.addEventListener('click', () => {\r\n open = !open;\r\n applyState();\r\n });\r\n\r\n document.body.append(iframe, btn);\r\n applyState();\r\n _initialised = true;\r\n });\r\n}\r\n\r\n/**\r\n * Programmatically open the chat panel.\r\n */\r\nexport function open(): void {\r\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\r\n const btn = document.getElementById('dunefox-btn');\r\n if (!iframe || !btn) return;\r\n iframe.classList.add('df-open');\r\n btn.setAttribute('aria-expanded', 'true');\r\n}\r\n\r\n/**\r\n * Programmatically close the chat panel.\r\n */\r\nexport function close(): void {\r\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\r\n const btn = document.getElementById('dunefox-btn');\r\n if (!iframe || !btn) return;\r\n iframe.classList.remove('df-open');\r\n btn.setAttribute('aria-expanded', 'false');\r\n}\r\n\r\n/**\r\n * Remove the widget entirely.\r\n */\r\nexport function destroy(): void {\r\n document.getElementById('dunefox-frame')?.remove();\r\n document.getElementById('dunefox-btn')?.remove();\r\n document.getElementById('dunefox-styles')?.remove();\r\n _initialised = false;\r\n}\r\n"]}
|
|
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.js","sourcesContent":["/** Options accepted by DunefoxChat.init() */\r\nexport interface DunefoxChatOptions {\r\n /** Your Dunefox Tenant ID — found in Settings > Install Widget */\r\n tenantId: string;\r\n /**\r\n * Where to anchor the widget. Defaults to \"bottom-right\".\r\n * Supports: \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\"\r\n */\r\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\r\n /** Open the chat panel on load. Defaults to false. */\r\n defaultOpen?: boolean;\r\n /** Override the base URL (for self-hosted / staging). */\r\n baseUrl?: string;\r\n /** Icon image URL override. Falls back to Dunefox CDN asset. */\r\n iconUrl?: string;\r\n}\r\n\r\nconst DEFAULT_BASE = 'https://app.dunefox.io';\r\nconst DEFAULT_ICON = 'https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png';\r\nconst UUID_KEY = 'dunefox_chat_uuid';\r\n\r\n// ─── UUID generation ──────────────────────────────────────────────────────────\r\nfunction getOrCreateUUID(): string {\r\n try {\r\n let id = localStorage.getItem(UUID_KEY);\r\n if (!id) {\r\n id = crypto?.randomUUID?.()\r\n ?? Math.random().toString(36).slice(2) + Date.now().toString(36);\r\n localStorage.setItem(UUID_KEY, id);\r\n }\r\n return id;\r\n } catch {\r\n // Incognito / storage blocked — generate ephemeral ID\r\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\r\n }\r\n}\r\n\r\n// ─── CSS injector ─────────────────────────────────────────────────────────────\r\nfunction injectStyles(position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'): void {\r\n if (document.getElementById('dunefox-styles')) return;\r\n\r\n const isTop = position.startsWith('top');\r\n const isLeft = position.endsWith('left');\r\n const hSide = isLeft ? 'left:16px' : 'right:16px';\r\n const vBtn = isTop ? 'top:16px' : 'bottom:16px';\r\n // Panel opens away from the button edge\r\n const vFrame = isTop ? 'top:88px' : 'bottom:88px';\r\n // Slide direction: panels below slide up, panels above slide down\r\n const slideOut = isTop ? 'translateY(-24px) scale(.96)' : 'translateY(24px) scale(.96)';\r\n // Hover nudge direction\r\n const hoverTranslate = isTop ? 'translateY(2px) scale(1.05)' : 'translateY(-2px) scale(1.05)';\r\n // Mobile slide direction\r\n const mobileSlideOut = isTop ? 'translateY(-100%)' : 'translateY(100%)';\r\n\r\n const css = `\r\n #dunefox-btn {\r\n position: fixed; ${vBtn}; ${hSide};\r\n width: 60px; height: 60px; border-radius: 50%;\r\n background: #1a1a1a; border: none; cursor: pointer;\r\n display: flex; align-items: center; justify-content: center;\r\n z-index: 2147483646;\r\n box-shadow: 0 4px 20px rgba(0,0,0,.25);\r\n transition: transform .25s ease;\r\n }\r\n #dunefox-btn:hover { transform: ${hoverTranslate}; }\r\n #dunefox-frame {\r\n position: fixed; ${vFrame}; ${hSide};\r\n width: 350px; height: calc(100vh - 160px); max-height: 600px;\r\n border-radius: 16px; border: none; z-index: 2147483645;\r\n box-shadow: 0 8px 32px rgba(0,0,0,.12);\r\n opacity: 0; visibility: hidden;\r\n transform: ${slideOut};\r\n transition: all .4s cubic-bezier(.4,0,.2,1);\r\n }\r\n #dunefox-frame.df-open {\r\n opacity: 1; visibility: visible; transform: translateY(0) scale(1);\r\n }\r\n @media (max-width: 768px) {\r\n /* Only move button to top-right when chat is open on mobile */\r\n body.df-chat-open #dunefox-btn {\r\n bottom: unset !important; top: 16px !important;\r\n right: 16px !important; left: unset !important;\r\n /* Strip the circular button style — show just the X icon */\r\n background: transparent !important;\r\n box-shadow: none !important;\r\n width: 36px !important; height: 36px !important;\r\n }\r\n #dunefox-frame {\r\n width: 100% !important; height: 100% !important; max-height: none !important;\r\n position: fixed !important; inset: 0 !important;\r\n border-radius: 0 !important;\r\n transform: ${mobileSlideOut} !important;\r\n }\r\n #dunefox-frame.df-open { transform: translateY(0) !important; }\r\n }\r\n `;\r\n const tag = document.createElement('style');\r\n tag.id = 'dunefox-styles';\r\n tag.textContent = css;\r\n document.head.appendChild(tag);\r\n}\r\n\r\n// ─── SVG icons ────────────────────────────────────────────────────────────────\r\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>`;\r\n\r\n// ─── Main init ────────────────────────────────────────────────────────────────\r\nlet _initialised = false;\r\n\r\n/**\r\n * Initialise the Dunefox chatbot widget.\r\n * Call once on DOMContentLoaded or after your framework has mounted.\r\n *\r\n * @example\r\n * import { init } from 'dunefox-chatbot';\r\n * init({ tenantId: 'YOUR_TENANT_ID' });\r\n */\r\nexport function init(options: DunefoxChatOptions | string): void {\r\n // Allow shorthand: init('tenantId')\r\n const opts: DunefoxChatOptions =\r\n typeof options === 'string' ? { tenantId: options } : options;\r\n\r\n if (_initialised) {\r\n console.warn('[DunefoxChat] Already initialised. Call destroy() first.');\r\n return;\r\n }\r\n\r\n const {\r\n tenantId,\r\n position = 'bottom-right',\r\n defaultOpen = false,\r\n baseUrl = DEFAULT_BASE,\r\n iconUrl = DEFAULT_ICON,\r\n } = opts;\r\n\r\n if (!tenantId) throw new Error('[DunefoxChat] tenantId is required.');\r\n\r\n // Wait for DOM if we're running during <head> parse\r\n const ready = (cb: () => void) =>\r\n document.readyState === 'loading'\r\n ? document.addEventListener('DOMContentLoaded', cb, { once: true })\r\n : cb();\r\n\r\n ready(() => {\r\n injectStyles(position);\r\n\r\n const uuid = getOrCreateUUID();\r\n const src = `${baseUrl}/api/${tenantId}?uuid=${uuid}`;\r\n\r\n // ── iframe ──\r\n const iframe = document.createElement('iframe');\r\n iframe.id = 'dunefox-frame';\r\n iframe.title = 'Dunefox Chat';\r\n iframe.loading = 'lazy';\r\n iframe.src = src;\r\n\r\n // ── toggle button ──\r\n const btn = document.createElement('button');\r\n btn.id = 'dunefox-btn';\r\n btn.setAttribute('aria-label', 'Open chat');\r\n btn.setAttribute('aria-expanded', 'false');\r\n\r\n const img = document.createElement('img');\r\n img.src = iconUrl;\r\n img.alt = '';\r\n img.width = 60;\r\n img.height = 60;\r\n img.style.cssText = 'display:block;border-radius:50%;object-fit:cover;';\r\n\r\n const closeSpan = document.createElement('span');\r\n closeSpan.innerHTML = CLOSE_SVG;\r\n closeSpan.style.display = 'none';\r\n\r\n btn.append(img, closeSpan);\r\n\r\n // ── state ──\r\n let open = defaultOpen;\r\n const applyState = () => {\r\n iframe.classList.toggle('df-open', open);\r\n // Toggle body class so mobile CSS can move button to top-right only when open\r\n document.body.classList.toggle('df-chat-open', open);\r\n img.style.display = open ? 'none' : 'block';\r\n closeSpan.style.display = open ? 'block' : 'none';\r\n btn.setAttribute('aria-expanded', String(open));\r\n };\r\n\r\n btn.addEventListener('click', () => {\r\n open = !open;\r\n applyState();\r\n });\r\n\r\n document.body.append(iframe, btn);\r\n applyState();\r\n _initialised = true;\r\n });\r\n}\r\n\r\n/**\r\n * Programmatically open the chat panel.\r\n */\r\nexport function open(): void {\r\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\r\n const btn = document.getElementById('dunefox-btn');\r\n if (!iframe || !btn) return;\r\n iframe.classList.add('df-open');\r\n btn.setAttribute('aria-expanded', 'true');\r\n}\r\n\r\n/**\r\n * Programmatically close the chat panel.\r\n */\r\nexport function close(): void {\r\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\r\n const btn = document.getElementById('dunefox-btn');\r\n if (!iframe || !btn) return;\r\n iframe.classList.remove('df-open');\r\n btn.setAttribute('aria-expanded', 'false');\r\n}\r\n\r\n/**\r\n * Remove the widget entirely.\r\n */\r\nexport function destroy(): void {\r\n document.getElementById('dunefox-frame')?.remove();\r\n document.getElementById('dunefox-btn')?.remove();\r\n document.getElementById('dunefox-styles')?.remove();\r\n document.body.classList.remove('df-chat-open');\r\n _initialised = false;\r\n}\r\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var
|
|
1
|
+
var g="https://app.dunefox.io",y="https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png",h="dunefox_chat_uuid";function v(){var e,t;try{let n=localStorage.getItem(h);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(h,n)),n}catch(n){return Math.random().toString(36).slice(2)+Date.now().toString(36)}}function w(e){if(document.getElementById("dunefox-styles"))return;let t=e.startsWith("top"),d=e.endsWith("left")?"left:16px":"right:16px",c=`
|
|
2
2
|
#dunefox-btn {
|
|
3
3
|
position: fixed; ${t?"top:16px":"bottom:16px"}; ${d};
|
|
4
4
|
width: 60px; height: 60px; border-radius: 50%;
|
|
@@ -22,9 +22,14 @@ var b="https://app.dunefox.io",y="https://dunefoxx.s3.ap-south-1.amazonaws.com/c
|
|
|
22
22
|
opacity: 1; visibility: visible; transform: translateY(0) scale(1);
|
|
23
23
|
}
|
|
24
24
|
@media (max-width: 768px) {
|
|
25
|
-
|
|
25
|
+
/* Only move button to top-right when chat is open on mobile */
|
|
26
|
+
body.df-chat-open #dunefox-btn {
|
|
26
27
|
bottom: unset !important; top: 16px !important;
|
|
27
28
|
right: 16px !important; left: unset !important;
|
|
29
|
+
/* Strip the circular button style \u2014 show just the X icon */
|
|
30
|
+
background: transparent !important;
|
|
31
|
+
box-shadow: none !important;
|
|
32
|
+
width: 36px !important; height: 36px !important;
|
|
28
33
|
}
|
|
29
34
|
#dunefox-frame {
|
|
30
35
|
width: 100% !important; height: 100% !important; max-height: none !important;
|
|
@@ -34,6 +39,6 @@ var b="https://app.dunefox.io",y="https://dunefoxx.s3.ap-south-1.amazonaws.com/c
|
|
|
34
39
|
}
|
|
35
40
|
#dunefox-frame.df-open { transform: translateY(0) !important; }
|
|
36
41
|
}
|
|
37
|
-
`,o=document.createElement("style");o.id="dunefox-styles",o.textContent=c,document.head.appendChild(o);}var
|
|
38
|
-
export{
|
|
42
|
+
`,o=document.createElement("style");o.id="dunefox-styles",o.textContent=c,document.head.appendChild(o);}var E='<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>',p=false;function I(e){let t=typeof e=="string"?{tenantId:e}:e;if(p){console.warn("[DunefoxChat] Already initialised. Call destroy() first.");return}let{tenantId:n,position:d="bottom-right",defaultOpen:m=false,baseUrl:u=g,iconUrl:f=y}=t;if(!n)throw new Error("[DunefoxChat] tenantId is required.");(s=>document.readyState==="loading"?document.addEventListener("DOMContentLoaded",s,{once:true}):s())(()=>{w(d);let s=v(),c=`${u}/api/${n}?uuid=${s}`,o=document.createElement("iframe");o.id="dunefox-frame",o.title="Dunefox Chat",o.loading="lazy",o.src=c;let a=document.createElement("button");a.id="dunefox-btn",a.setAttribute("aria-label","Open chat"),a.setAttribute("aria-expanded","false");let i=document.createElement("img");i.src=f,i.alt="",i.width=60,i.height=60,i.style.cssText="display:block;border-radius:50%;object-fit:cover;";let l=document.createElement("span");l.innerHTML=E,l.style.display="none",a.append(i,l);let r=m,x=()=>{o.classList.toggle("df-open",r),document.body.classList.toggle("df-chat-open",r),i.style.display=r?"none":"block",l.style.display=r?"block":"none",a.setAttribute("aria-expanded",String(r));};a.addEventListener("click",()=>{r=!r,x();}),document.body.append(o,a),x(),p=true;});}function S(){let e=document.getElementById("dunefox-frame"),t=document.getElementById("dunefox-btn");!e||!t||(e.classList.add("df-open"),t.setAttribute("aria-expanded","true"));}function L(){let e=document.getElementById("dunefox-frame"),t=document.getElementById("dunefox-btn");!e||!t||(e.classList.remove("df-open"),t.setAttribute("aria-expanded","false"));}function O(){var e,t,n;(e=document.getElementById("dunefox-frame"))==null||e.remove(),(t=document.getElementById("dunefox-btn"))==null||t.remove(),(n=document.getElementById("dunefox-styles"))==null||n.remove(),document.body.classList.remove("df-chat-open"),p=false;}
|
|
43
|
+
export{L as close,O as destroy,I as init,S as open};//# sourceMappingURL=index.mjs.map
|
|
39
44
|
//# 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() */\r\nexport interface DunefoxChatOptions {\r\n /** Your Dunefox Tenant ID — found in Settings > Install Widget */\r\n tenantId: string;\r\n /**\r\n * Where to anchor the widget. Defaults to \"bottom-right\".\r\n * Supports: \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\"\r\n */\r\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\r\n /** Open the chat panel on load. Defaults to false. */\r\n defaultOpen?: boolean;\r\n /** Override the base URL (for self-hosted / staging). */\r\n baseUrl?: string;\r\n /** Icon image URL override. Falls back to Dunefox CDN asset. */\r\n iconUrl?: string;\r\n}\r\n\r\nconst DEFAULT_BASE = 'https://app.dunefox.io';\r\nconst DEFAULT_ICON = 'https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png';\r\nconst UUID_KEY = 'dunefox_chat_uuid';\r\n\r\n// ─── UUID generation ──────────────────────────────────────────────────────────\r\nfunction getOrCreateUUID(): string {\r\n try {\r\n let id = localStorage.getItem(UUID_KEY);\r\n if (!id) {\r\n id = crypto?.randomUUID?.()\r\n ?? Math.random().toString(36).slice(2) + Date.now().toString(36);\r\n localStorage.setItem(UUID_KEY, id);\r\n }\r\n return id;\r\n } catch {\r\n // Incognito / storage blocked — generate ephemeral ID\r\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\r\n }\r\n}\r\n\r\n// ─── CSS injector ─────────────────────────────────────────────────────────────\r\nfunction injectStyles(position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'): void {\r\n if (document.getElementById('dunefox-styles')) return;\r\n\r\n const isTop = position.startsWith('top');\r\n const isLeft = position.endsWith('left');\r\n const hSide = isLeft ? 'left:16px' : 'right:16px';\r\n const vBtn = isTop ? 'top:16px' : 'bottom:16px';\r\n // Panel opens away from the button edge\r\n const vFrame = isTop ? 'top:88px' : 'bottom:88px';\r\n // Slide direction: panels below slide up, panels above slide down\r\n const slideOut = isTop ? 'translateY(-24px) scale(.96)' : 'translateY(24px) scale(.96)';\r\n // Hover nudge direction\r\n const hoverTranslate = isTop ? 'translateY(2px) scale(1.05)' : 'translateY(-2px) scale(1.05)';\r\n // Mobile slide direction\r\n const mobileSlideOut = isTop ? 'translateY(-100%)' : 'translateY(100%)';\r\n\r\n const css = `\r\n #dunefox-btn {\r\n position: fixed; ${vBtn}; ${hSide};\r\n width: 60px; height: 60px; border-radius: 50%;\r\n background: #1a1a1a; border: none; cursor: pointer;\r\n display: flex; align-items: center; justify-content: center;\r\n z-index: 2147483646;\r\n box-shadow: 0 4px 20px rgba(0,0,0,.25);\r\n transition: transform .25s ease;\r\n }\r\n #dunefox-btn:hover { transform: ${hoverTranslate}; }\r\n #dunefox-frame {\r\n position: fixed; ${vFrame}; ${hSide};\r\n width: 350px; height: calc(100vh - 160px); max-height: 600px;\r\n border-radius: 16px; border: none; z-index: 2147483645;\r\n box-shadow: 0 8px 32px rgba(0,0,0,.12);\r\n opacity: 0; visibility: hidden;\r\n transform: ${slideOut};\r\n transition: all .4s cubic-bezier(.4,0,.2,1);\r\n }\r\n #dunefox-frame.df-open {\r\n opacity: 1; visibility: visible; transform: translateY(0) scale(1);\r\n }\r\n @media (max-width: 768px) {\r\n #dunefox-btn {\r\n bottom: unset !important; top: 16px !important;\r\n right: 16px !important; left: unset !important;\r\n }\r\n #dunefox-frame {\r\n width: 100% !important; height: 100% !important; max-height: none !important;\r\n position: fixed !important; inset: 0 !important;\r\n border-radius: 0 !important;\r\n transform: ${mobileSlideOut} !important;\r\n }\r\n #dunefox-frame.df-open { transform: translateY(0) !important; }\r\n }\r\n `;\r\n const tag = document.createElement('style');\r\n tag.id = 'dunefox-styles';\r\n tag.textContent = css;\r\n document.head.appendChild(tag);\r\n}\r\n\r\n// ─── SVG icons ────────────────────────────────────────────────────────────────\r\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>`;\r\n\r\n// ─── Main init ────────────────────────────────────────────────────────────────\r\nlet _initialised = false;\r\n\r\n/**\r\n * Initialise the Dunefox chatbot widget.\r\n * Call once on DOMContentLoaded or after your framework has mounted.\r\n *\r\n * @example\r\n * import { init } from 'dunefox-chatbot';\r\n * init({ tenantId: 'YOUR_TENANT_ID' });\r\n */\r\nexport function init(options: DunefoxChatOptions | string): void {\r\n // Allow shorthand: init('tenantId')\r\n const opts: DunefoxChatOptions =\r\n typeof options === 'string' ? { tenantId: options } : options;\r\n\r\n if (_initialised) {\r\n console.warn('[DunefoxChat] Already initialised. Call destroy() first.');\r\n return;\r\n }\r\n\r\n const {\r\n tenantId,\r\n position = 'bottom-right',\r\n defaultOpen = false,\r\n baseUrl = DEFAULT_BASE,\r\n iconUrl = DEFAULT_ICON,\r\n } = opts;\r\n\r\n if (!tenantId) throw new Error('[DunefoxChat] tenantId is required.');\r\n\r\n // Wait for DOM if we're running during <head> parse\r\n const ready = (cb: () => void) =>\r\n document.readyState === 'loading'\r\n ? document.addEventListener('DOMContentLoaded', cb, { once: true })\r\n : cb();\r\n\r\n ready(() => {\r\n injectStyles(position);\r\n\r\n const uuid = getOrCreateUUID();\r\n const src = `${baseUrl}/api/${tenantId}?uuid=${uuid}`;\r\n\r\n // ── iframe ──\r\n const iframe = document.createElement('iframe');\r\n iframe.id = 'dunefox-frame';\r\n iframe.title = 'Dunefox Chat';\r\n iframe.loading = 'lazy';\r\n iframe.src = src;\r\n\r\n // ── toggle button ──\r\n const btn = document.createElement('button');\r\n btn.id = 'dunefox-btn';\r\n btn.setAttribute('aria-label', 'Open chat');\r\n btn.setAttribute('aria-expanded', 'false');\r\n\r\n const img = document.createElement('img');\r\n img.src = iconUrl;\r\n img.alt = '';\r\n img.width = 60;\r\n img.height = 60;\r\n img.style.cssText = 'display:block;border-radius:50%;object-fit:cover;';\r\n\r\n const closeSpan = document.createElement('span');\r\n closeSpan.innerHTML = CLOSE_SVG;\r\n closeSpan.style.display = 'none';\r\n\r\n btn.append(img, closeSpan);\r\n\r\n // ── state ──\r\n let open = defaultOpen;\r\n const applyState = () => {\r\n iframe.classList.toggle('df-open', open);\r\n img.style.display = open ? 'none' : 'block';\r\n closeSpan.style.display = open ? 'block' : 'none';\r\n btn.setAttribute('aria-expanded', String(open));\r\n };\r\n\r\n btn.addEventListener('click', () => {\r\n open = !open;\r\n applyState();\r\n });\r\n\r\n document.body.append(iframe, btn);\r\n applyState();\r\n _initialised = true;\r\n });\r\n}\r\n\r\n/**\r\n * Programmatically open the chat panel.\r\n */\r\nexport function open(): void {\r\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\r\n const btn = document.getElementById('dunefox-btn');\r\n if (!iframe || !btn) return;\r\n iframe.classList.add('df-open');\r\n btn.setAttribute('aria-expanded', 'true');\r\n}\r\n\r\n/**\r\n * Programmatically close the chat panel.\r\n */\r\nexport function close(): void {\r\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\r\n const btn = document.getElementById('dunefox-btn');\r\n if (!iframe || !btn) return;\r\n iframe.classList.remove('df-open');\r\n btn.setAttribute('aria-expanded', 'false');\r\n}\r\n\r\n/**\r\n * Remove the widget entirely.\r\n */\r\nexport function destroy(): void {\r\n document.getElementById('dunefox-frame')?.remove();\r\n document.getElementById('dunefox-btn')?.remove();\r\n document.getElementById('dunefox-styles')?.remove();\r\n _initialised = false;\r\n}\r\n"]}
|
|
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() */\r\nexport interface DunefoxChatOptions {\r\n /** Your Dunefox Tenant ID — found in Settings > Install Widget */\r\n tenantId: string;\r\n /**\r\n * Where to anchor the widget. Defaults to \"bottom-right\".\r\n * Supports: \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\"\r\n */\r\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\r\n /** Open the chat panel on load. Defaults to false. */\r\n defaultOpen?: boolean;\r\n /** Override the base URL (for self-hosted / staging). */\r\n baseUrl?: string;\r\n /** Icon image URL override. Falls back to Dunefox CDN asset. */\r\n iconUrl?: string;\r\n}\r\n\r\nconst DEFAULT_BASE = 'https://app.dunefox.io';\r\nconst DEFAULT_ICON = 'https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png';\r\nconst UUID_KEY = 'dunefox_chat_uuid';\r\n\r\n// ─── UUID generation ──────────────────────────────────────────────────────────\r\nfunction getOrCreateUUID(): string {\r\n try {\r\n let id = localStorage.getItem(UUID_KEY);\r\n if (!id) {\r\n id = crypto?.randomUUID?.()\r\n ?? Math.random().toString(36).slice(2) + Date.now().toString(36);\r\n localStorage.setItem(UUID_KEY, id);\r\n }\r\n return id;\r\n } catch {\r\n // Incognito / storage blocked — generate ephemeral ID\r\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\r\n }\r\n}\r\n\r\n// ─── CSS injector ─────────────────────────────────────────────────────────────\r\nfunction injectStyles(position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'): void {\r\n if (document.getElementById('dunefox-styles')) return;\r\n\r\n const isTop = position.startsWith('top');\r\n const isLeft = position.endsWith('left');\r\n const hSide = isLeft ? 'left:16px' : 'right:16px';\r\n const vBtn = isTop ? 'top:16px' : 'bottom:16px';\r\n // Panel opens away from the button edge\r\n const vFrame = isTop ? 'top:88px' : 'bottom:88px';\r\n // Slide direction: panels below slide up, panels above slide down\r\n const slideOut = isTop ? 'translateY(-24px) scale(.96)' : 'translateY(24px) scale(.96)';\r\n // Hover nudge direction\r\n const hoverTranslate = isTop ? 'translateY(2px) scale(1.05)' : 'translateY(-2px) scale(1.05)';\r\n // Mobile slide direction\r\n const mobileSlideOut = isTop ? 'translateY(-100%)' : 'translateY(100%)';\r\n\r\n const css = `\r\n #dunefox-btn {\r\n position: fixed; ${vBtn}; ${hSide};\r\n width: 60px; height: 60px; border-radius: 50%;\r\n background: #1a1a1a; border: none; cursor: pointer;\r\n display: flex; align-items: center; justify-content: center;\r\n z-index: 2147483646;\r\n box-shadow: 0 4px 20px rgba(0,0,0,.25);\r\n transition: transform .25s ease;\r\n }\r\n #dunefox-btn:hover { transform: ${hoverTranslate}; }\r\n #dunefox-frame {\r\n position: fixed; ${vFrame}; ${hSide};\r\n width: 350px; height: calc(100vh - 160px); max-height: 600px;\r\n border-radius: 16px; border: none; z-index: 2147483645;\r\n box-shadow: 0 8px 32px rgba(0,0,0,.12);\r\n opacity: 0; visibility: hidden;\r\n transform: ${slideOut};\r\n transition: all .4s cubic-bezier(.4,0,.2,1);\r\n }\r\n #dunefox-frame.df-open {\r\n opacity: 1; visibility: visible; transform: translateY(0) scale(1);\r\n }\r\n @media (max-width: 768px) {\r\n /* Only move button to top-right when chat is open on mobile */\r\n body.df-chat-open #dunefox-btn {\r\n bottom: unset !important; top: 16px !important;\r\n right: 16px !important; left: unset !important;\r\n /* Strip the circular button style — show just the X icon */\r\n background: transparent !important;\r\n box-shadow: none !important;\r\n width: 36px !important; height: 36px !important;\r\n }\r\n #dunefox-frame {\r\n width: 100% !important; height: 100% !important; max-height: none !important;\r\n position: fixed !important; inset: 0 !important;\r\n border-radius: 0 !important;\r\n transform: ${mobileSlideOut} !important;\r\n }\r\n #dunefox-frame.df-open { transform: translateY(0) !important; }\r\n }\r\n `;\r\n const tag = document.createElement('style');\r\n tag.id = 'dunefox-styles';\r\n tag.textContent = css;\r\n document.head.appendChild(tag);\r\n}\r\n\r\n// ─── SVG icons ────────────────────────────────────────────────────────────────\r\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>`;\r\n\r\n// ─── Main init ────────────────────────────────────────────────────────────────\r\nlet _initialised = false;\r\n\r\n/**\r\n * Initialise the Dunefox chatbot widget.\r\n * Call once on DOMContentLoaded or after your framework has mounted.\r\n *\r\n * @example\r\n * import { init } from 'dunefox-chatbot';\r\n * init({ tenantId: 'YOUR_TENANT_ID' });\r\n */\r\nexport function init(options: DunefoxChatOptions | string): void {\r\n // Allow shorthand: init('tenantId')\r\n const opts: DunefoxChatOptions =\r\n typeof options === 'string' ? { tenantId: options } : options;\r\n\r\n if (_initialised) {\r\n console.warn('[DunefoxChat] Already initialised. Call destroy() first.');\r\n return;\r\n }\r\n\r\n const {\r\n tenantId,\r\n position = 'bottom-right',\r\n defaultOpen = false,\r\n baseUrl = DEFAULT_BASE,\r\n iconUrl = DEFAULT_ICON,\r\n } = opts;\r\n\r\n if (!tenantId) throw new Error('[DunefoxChat] tenantId is required.');\r\n\r\n // Wait for DOM if we're running during <head> parse\r\n const ready = (cb: () => void) =>\r\n document.readyState === 'loading'\r\n ? document.addEventListener('DOMContentLoaded', cb, { once: true })\r\n : cb();\r\n\r\n ready(() => {\r\n injectStyles(position);\r\n\r\n const uuid = getOrCreateUUID();\r\n const src = `${baseUrl}/api/${tenantId}?uuid=${uuid}`;\r\n\r\n // ── iframe ──\r\n const iframe = document.createElement('iframe');\r\n iframe.id = 'dunefox-frame';\r\n iframe.title = 'Dunefox Chat';\r\n iframe.loading = 'lazy';\r\n iframe.src = src;\r\n\r\n // ── toggle button ──\r\n const btn = document.createElement('button');\r\n btn.id = 'dunefox-btn';\r\n btn.setAttribute('aria-label', 'Open chat');\r\n btn.setAttribute('aria-expanded', 'false');\r\n\r\n const img = document.createElement('img');\r\n img.src = iconUrl;\r\n img.alt = '';\r\n img.width = 60;\r\n img.height = 60;\r\n img.style.cssText = 'display:block;border-radius:50%;object-fit:cover;';\r\n\r\n const closeSpan = document.createElement('span');\r\n closeSpan.innerHTML = CLOSE_SVG;\r\n closeSpan.style.display = 'none';\r\n\r\n btn.append(img, closeSpan);\r\n\r\n // ── state ──\r\n let open = defaultOpen;\r\n const applyState = () => {\r\n iframe.classList.toggle('df-open', open);\r\n // Toggle body class so mobile CSS can move button to top-right only when open\r\n document.body.classList.toggle('df-chat-open', open);\r\n img.style.display = open ? 'none' : 'block';\r\n closeSpan.style.display = open ? 'block' : 'none';\r\n btn.setAttribute('aria-expanded', String(open));\r\n };\r\n\r\n btn.addEventListener('click', () => {\r\n open = !open;\r\n applyState();\r\n });\r\n\r\n document.body.append(iframe, btn);\r\n applyState();\r\n _initialised = true;\r\n });\r\n}\r\n\r\n/**\r\n * Programmatically open the chat panel.\r\n */\r\nexport function open(): void {\r\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\r\n const btn = document.getElementById('dunefox-btn');\r\n if (!iframe || !btn) return;\r\n iframe.classList.add('df-open');\r\n btn.setAttribute('aria-expanded', 'true');\r\n}\r\n\r\n/**\r\n * Programmatically close the chat panel.\r\n */\r\nexport function close(): void {\r\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\r\n const btn = document.getElementById('dunefox-btn');\r\n if (!iframe || !btn) return;\r\n iframe.classList.remove('df-open');\r\n btn.setAttribute('aria-expanded', 'false');\r\n}\r\n\r\n/**\r\n * Remove the widget entirely.\r\n */\r\nexport function destroy(): void {\r\n document.getElementById('dunefox-frame')?.remove();\r\n document.getElementById('dunefox-btn')?.remove();\r\n document.getElementById('dunefox-styles')?.remove();\r\n document.body.classList.remove('df-chat-open');\r\n _initialised = false;\r\n}\r\n"]}
|
package/dist/react.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
'use strict';var react=require('react');var v="https://app.dunefox.io",
|
|
1
|
+
'use strict';var react=require('react');var v="https://app.dunefox.io",w="https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png",h="dunefox_chat_uuid";function E(){var e,t;try{let n=localStorage.getItem(h);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(h,n)),n}catch(n){return Math.random().toString(36).slice(2)+Date.now().toString(36)}}function I(e){if(document.getElementById("dunefox-styles"))return;let t=e.startsWith("top"),d=e.endsWith("left")?"left:16px":"right:16px",p=`
|
|
2
2
|
#dunefox-btn {
|
|
3
3
|
position: fixed; ${t?"top:16px":"bottom:16px"}; ${d};
|
|
4
4
|
width: 60px; height: 60px; border-radius: 50%;
|
|
@@ -22,9 +22,14 @@
|
|
|
22
22
|
opacity: 1; visibility: visible; transform: translateY(0) scale(1);
|
|
23
23
|
}
|
|
24
24
|
@media (max-width: 768px) {
|
|
25
|
-
|
|
25
|
+
/* Only move button to top-right when chat is open on mobile */
|
|
26
|
+
body.df-chat-open #dunefox-btn {
|
|
26
27
|
bottom: unset !important; top: 16px !important;
|
|
27
28
|
right: 16px !important; left: unset !important;
|
|
29
|
+
/* Strip the circular button style \u2014 show just the X icon */
|
|
30
|
+
background: transparent !important;
|
|
31
|
+
box-shadow: none !important;
|
|
32
|
+
width: 36px !important; height: 36px !important;
|
|
28
33
|
}
|
|
29
34
|
#dunefox-frame {
|
|
30
35
|
width: 100% !important; height: 100% !important; max-height: none !important;
|
|
@@ -34,6 +39,6 @@
|
|
|
34
39
|
}
|
|
35
40
|
#dunefox-frame.df-open { transform: translateY(0) !important; }
|
|
36
41
|
}
|
|
37
|
-
`,o=document.createElement("style");o.id="dunefox-styles",o.textContent=
|
|
42
|
+
`,o=document.createElement("style");o.id="dunefox-styles",o.textContent=p,document.head.appendChild(o);}var D='<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>',c=false;function b(e){let t=typeof e=="string"?{tenantId:e}:e;if(c){console.warn("[DunefoxChat] Already initialised. Call destroy() first.");return}let{tenantId:n,position:d="bottom-right",defaultOpen:u=false,baseUrl:m=v,iconUrl:f=w}=t;if(!n)throw new Error("[DunefoxChat] tenantId is required.");(s=>document.readyState==="loading"?document.addEventListener("DOMContentLoaded",s,{once:true}):s())(()=>{I(d);let s=E(),p=`${m}/api/${n}?uuid=${s}`,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 i=document.createElement("img");i.src=f,i.alt="",i.width=60,i.height=60,i.style.cssText="display:block;border-radius:50%;object-fit:cover;";let l=document.createElement("span");l.innerHTML=D,l.style.display="none",r.append(i,l);let a=u,x=()=>{o.classList.toggle("df-open",a),document.body.classList.toggle("df-chat-open",a),i.style.display=a?"none":"block",l.style.display=a?"block":"none",r.setAttribute("aria-expanded",String(a));};r.addEventListener("click",()=>{a=!a,x();}),document.body.append(o,r),x(),c=true;});}function g(){var e,t,n;(e=document.getElementById("dunefox-frame"))==null||e.remove(),(t=document.getElementById("dunefox-btn"))==null||t.remove(),(n=document.getElementById("dunefox-styles"))==null||n.remove(),document.body.classList.remove("df-chat-open"),c=false;}function U(e){let t=react.useRef(e);return react.useEffect(()=>{if(typeof window!="undefined")return b(t.current),()=>{g();}},[]),null}
|
|
38
43
|
exports.DunefoxChatbot=U;//# sourceMappingURL=react.js.map
|
|
39
44
|
//# 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() */\r\nexport interface DunefoxChatOptions {\r\n /** Your Dunefox Tenant ID — found in Settings > Install Widget */\r\n tenantId: string;\r\n /**\r\n * Where to anchor the widget. Defaults to \"bottom-right\".\r\n * Supports: \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\"\r\n */\r\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\r\n /** Open the chat panel on load. Defaults to false. */\r\n defaultOpen?: boolean;\r\n /** Override the base URL (for self-hosted / staging). */\r\n baseUrl?: string;\r\n /** Icon image URL override. Falls back to Dunefox CDN asset. */\r\n iconUrl?: string;\r\n}\r\n\r\nconst DEFAULT_BASE = 'https://app.dunefox.io';\r\nconst DEFAULT_ICON = 'https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png';\r\nconst UUID_KEY = 'dunefox_chat_uuid';\r\n\r\n// ─── UUID generation ──────────────────────────────────────────────────────────\r\nfunction getOrCreateUUID(): string {\r\n try {\r\n let id = localStorage.getItem(UUID_KEY);\r\n if (!id) {\r\n id = crypto?.randomUUID?.()\r\n ?? Math.random().toString(36).slice(2) + Date.now().toString(36);\r\n localStorage.setItem(UUID_KEY, id);\r\n }\r\n return id;\r\n } catch {\r\n // Incognito / storage blocked — generate ephemeral ID\r\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\r\n }\r\n}\r\n\r\n// ─── CSS injector ─────────────────────────────────────────────────────────────\r\nfunction injectStyles(position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'): void {\r\n if (document.getElementById('dunefox-styles')) return;\r\n\r\n const isTop = position.startsWith('top');\r\n const isLeft = position.endsWith('left');\r\n const hSide = isLeft ? 'left:16px' : 'right:16px';\r\n const vBtn = isTop ? 'top:16px' : 'bottom:16px';\r\n // Panel opens away from the button edge\r\n const vFrame = isTop ? 'top:88px' : 'bottom:88px';\r\n // Slide direction: panels below slide up, panels above slide down\r\n const slideOut = isTop ? 'translateY(-24px) scale(.96)' : 'translateY(24px) scale(.96)';\r\n // Hover nudge direction\r\n const hoverTranslate = isTop ? 'translateY(2px) scale(1.05)' : 'translateY(-2px) scale(1.05)';\r\n // Mobile slide direction\r\n const mobileSlideOut = isTop ? 'translateY(-100%)' : 'translateY(100%)';\r\n\r\n const css = `\r\n #dunefox-btn {\r\n position: fixed; ${vBtn}; ${hSide};\r\n width: 60px; height: 60px; border-radius: 50%;\r\n background: #1a1a1a; border: none; cursor: pointer;\r\n display: flex; align-items: center; justify-content: center;\r\n z-index: 2147483646;\r\n box-shadow: 0 4px 20px rgba(0,0,0,.25);\r\n transition: transform .25s ease;\r\n }\r\n #dunefox-btn:hover { transform: ${hoverTranslate}; }\r\n #dunefox-frame {\r\n position: fixed; ${vFrame}; ${hSide};\r\n width: 350px; height: calc(100vh - 160px); max-height: 600px;\r\n border-radius: 16px; border: none; z-index: 2147483645;\r\n box-shadow: 0 8px 32px rgba(0,0,0,.12);\r\n opacity: 0; visibility: hidden;\r\n transform: ${slideOut};\r\n transition: all .4s cubic-bezier(.4,0,.2,1);\r\n }\r\n #dunefox-frame.df-open {\r\n opacity: 1; visibility: visible; transform: translateY(0) scale(1);\r\n }\r\n @media (max-width: 768px) {\r\n #dunefox-btn {\r\n bottom: unset !important; top: 16px !important;\r\n right: 16px !important; left: unset !important;\r\n }\r\n #dunefox-frame {\r\n width: 100% !important; height: 100% !important; max-height: none !important;\r\n position: fixed !important; inset: 0 !important;\r\n border-radius: 0 !important;\r\n transform: ${mobileSlideOut} !important;\r\n }\r\n #dunefox-frame.df-open { transform: translateY(0) !important; }\r\n }\r\n `;\r\n const tag = document.createElement('style');\r\n tag.id = 'dunefox-styles';\r\n tag.textContent = css;\r\n document.head.appendChild(tag);\r\n}\r\n\r\n// ─── SVG icons ────────────────────────────────────────────────────────────────\r\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>`;\r\n\r\n// ─── Main init ────────────────────────────────────────────────────────────────\r\nlet _initialised = false;\r\n\r\n/**\r\n * Initialise the Dunefox chatbot widget.\r\n * Call once on DOMContentLoaded or after your framework has mounted.\r\n *\r\n * @example\r\n * import { init } from 'dunefox-chatbot';\r\n * init({ tenantId: 'YOUR_TENANT_ID' });\r\n */\r\nexport function init(options: DunefoxChatOptions | string): void {\r\n // Allow shorthand: init('tenantId')\r\n const opts: DunefoxChatOptions =\r\n typeof options === 'string' ? { tenantId: options } : options;\r\n\r\n if (_initialised) {\r\n console.warn('[DunefoxChat] Already initialised. Call destroy() first.');\r\n return;\r\n }\r\n\r\n const {\r\n tenantId,\r\n position = 'bottom-right',\r\n defaultOpen = false,\r\n baseUrl = DEFAULT_BASE,\r\n iconUrl = DEFAULT_ICON,\r\n } = opts;\r\n\r\n if (!tenantId) throw new Error('[DunefoxChat] tenantId is required.');\r\n\r\n // Wait for DOM if we're running during <head> parse\r\n const ready = (cb: () => void) =>\r\n document.readyState === 'loading'\r\n ? document.addEventListener('DOMContentLoaded', cb, { once: true })\r\n : cb();\r\n\r\n ready(() => {\r\n injectStyles(position);\r\n\r\n const uuid = getOrCreateUUID();\r\n const src = `${baseUrl}/api/${tenantId}?uuid=${uuid}`;\r\n\r\n // ── iframe ──\r\n const iframe = document.createElement('iframe');\r\n iframe.id = 'dunefox-frame';\r\n iframe.title = 'Dunefox Chat';\r\n iframe.loading = 'lazy';\r\n iframe.src = src;\r\n\r\n // ── toggle button ──\r\n const btn = document.createElement('button');\r\n btn.id = 'dunefox-btn';\r\n btn.setAttribute('aria-label', 'Open chat');\r\n btn.setAttribute('aria-expanded', 'false');\r\n\r\n const img = document.createElement('img');\r\n img.src = iconUrl;\r\n img.alt = '';\r\n img.width = 60;\r\n img.height = 60;\r\n img.style.cssText = 'display:block;border-radius:50%;object-fit:cover;';\r\n\r\n const closeSpan = document.createElement('span');\r\n closeSpan.innerHTML = CLOSE_SVG;\r\n closeSpan.style.display = 'none';\r\n\r\n btn.append(img, closeSpan);\r\n\r\n // ── state ──\r\n let open = defaultOpen;\r\n const applyState = () => {\r\n iframe.classList.toggle('df-open', open);\r\n img.style.display = open ? 'none' : 'block';\r\n closeSpan.style.display = open ? 'block' : 'none';\r\n btn.setAttribute('aria-expanded', String(open));\r\n };\r\n\r\n btn.addEventListener('click', () => {\r\n open = !open;\r\n applyState();\r\n });\r\n\r\n document.body.append(iframe, btn);\r\n applyState();\r\n _initialised = true;\r\n });\r\n}\r\n\r\n/**\r\n * Programmatically open the chat panel.\r\n */\r\nexport function open(): void {\r\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\r\n const btn = document.getElementById('dunefox-btn');\r\n if (!iframe || !btn) return;\r\n iframe.classList.add('df-open');\r\n btn.setAttribute('aria-expanded', 'true');\r\n}\r\n\r\n/**\r\n * Programmatically close the chat panel.\r\n */\r\nexport function close(): void {\r\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\r\n const btn = document.getElementById('dunefox-btn');\r\n if (!iframe || !btn) return;\r\n iframe.classList.remove('df-open');\r\n btn.setAttribute('aria-expanded', 'false');\r\n}\r\n\r\n/**\r\n * Remove the widget entirely.\r\n */\r\nexport function destroy(): void {\r\n document.getElementById('dunefox-frame')?.remove();\r\n document.getElementById('dunefox-btn')?.remove();\r\n document.getElementById('dunefox-styles')?.remove();\r\n _initialised = false;\r\n}\r\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","_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() */\r\nexport interface DunefoxChatOptions {\r\n /** Your Dunefox Tenant ID — found in Settings > Install Widget */\r\n tenantId: string;\r\n /**\r\n * Where to anchor the widget. Defaults to \"bottom-right\".\r\n * Supports: \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\"\r\n */\r\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\r\n /** Open the chat panel on load. Defaults to false. */\r\n defaultOpen?: boolean;\r\n /** Override the base URL (for self-hosted / staging). */\r\n baseUrl?: string;\r\n /** Icon image URL override. Falls back to Dunefox CDN asset. */\r\n iconUrl?: string;\r\n}\r\n\r\nconst DEFAULT_BASE = 'https://app.dunefox.io';\r\nconst DEFAULT_ICON = 'https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png';\r\nconst UUID_KEY = 'dunefox_chat_uuid';\r\n\r\n// ─── UUID generation ──────────────────────────────────────────────────────────\r\nfunction getOrCreateUUID(): string {\r\n try {\r\n let id = localStorage.getItem(UUID_KEY);\r\n if (!id) {\r\n id = crypto?.randomUUID?.()\r\n ?? Math.random().toString(36).slice(2) + Date.now().toString(36);\r\n localStorage.setItem(UUID_KEY, id);\r\n }\r\n return id;\r\n } catch {\r\n // Incognito / storage blocked — generate ephemeral ID\r\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\r\n }\r\n}\r\n\r\n// ─── CSS injector ─────────────────────────────────────────────────────────────\r\nfunction injectStyles(position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'): void {\r\n if (document.getElementById('dunefox-styles')) return;\r\n\r\n const isTop = position.startsWith('top');\r\n const isLeft = position.endsWith('left');\r\n const hSide = isLeft ? 'left:16px' : 'right:16px';\r\n const vBtn = isTop ? 'top:16px' : 'bottom:16px';\r\n // Panel opens away from the button edge\r\n const vFrame = isTop ? 'top:88px' : 'bottom:88px';\r\n // Slide direction: panels below slide up, panels above slide down\r\n const slideOut = isTop ? 'translateY(-24px) scale(.96)' : 'translateY(24px) scale(.96)';\r\n // Hover nudge direction\r\n const hoverTranslate = isTop ? 'translateY(2px) scale(1.05)' : 'translateY(-2px) scale(1.05)';\r\n // Mobile slide direction\r\n const mobileSlideOut = isTop ? 'translateY(-100%)' : 'translateY(100%)';\r\n\r\n const css = `\r\n #dunefox-btn {\r\n position: fixed; ${vBtn}; ${hSide};\r\n width: 60px; height: 60px; border-radius: 50%;\r\n background: #1a1a1a; border: none; cursor: pointer;\r\n display: flex; align-items: center; justify-content: center;\r\n z-index: 2147483646;\r\n box-shadow: 0 4px 20px rgba(0,0,0,.25);\r\n transition: transform .25s ease;\r\n }\r\n #dunefox-btn:hover { transform: ${hoverTranslate}; }\r\n #dunefox-frame {\r\n position: fixed; ${vFrame}; ${hSide};\r\n width: 350px; height: calc(100vh - 160px); max-height: 600px;\r\n border-radius: 16px; border: none; z-index: 2147483645;\r\n box-shadow: 0 8px 32px rgba(0,0,0,.12);\r\n opacity: 0; visibility: hidden;\r\n transform: ${slideOut};\r\n transition: all .4s cubic-bezier(.4,0,.2,1);\r\n }\r\n #dunefox-frame.df-open {\r\n opacity: 1; visibility: visible; transform: translateY(0) scale(1);\r\n }\r\n @media (max-width: 768px) {\r\n /* Only move button to top-right when chat is open on mobile */\r\n body.df-chat-open #dunefox-btn {\r\n bottom: unset !important; top: 16px !important;\r\n right: 16px !important; left: unset !important;\r\n /* Strip the circular button style — show just the X icon */\r\n background: transparent !important;\r\n box-shadow: none !important;\r\n width: 36px !important; height: 36px !important;\r\n }\r\n #dunefox-frame {\r\n width: 100% !important; height: 100% !important; max-height: none !important;\r\n position: fixed !important; inset: 0 !important;\r\n border-radius: 0 !important;\r\n transform: ${mobileSlideOut} !important;\r\n }\r\n #dunefox-frame.df-open { transform: translateY(0) !important; }\r\n }\r\n `;\r\n const tag = document.createElement('style');\r\n tag.id = 'dunefox-styles';\r\n tag.textContent = css;\r\n document.head.appendChild(tag);\r\n}\r\n\r\n// ─── SVG icons ────────────────────────────────────────────────────────────────\r\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>`;\r\n\r\n// ─── Main init ────────────────────────────────────────────────────────────────\r\nlet _initialised = false;\r\n\r\n/**\r\n * Initialise the Dunefox chatbot widget.\r\n * Call once on DOMContentLoaded or after your framework has mounted.\r\n *\r\n * @example\r\n * import { init } from 'dunefox-chatbot';\r\n * init({ tenantId: 'YOUR_TENANT_ID' });\r\n */\r\nexport function init(options: DunefoxChatOptions | string): void {\r\n // Allow shorthand: init('tenantId')\r\n const opts: DunefoxChatOptions =\r\n typeof options === 'string' ? { tenantId: options } : options;\r\n\r\n if (_initialised) {\r\n console.warn('[DunefoxChat] Already initialised. Call destroy() first.');\r\n return;\r\n }\r\n\r\n const {\r\n tenantId,\r\n position = 'bottom-right',\r\n defaultOpen = false,\r\n baseUrl = DEFAULT_BASE,\r\n iconUrl = DEFAULT_ICON,\r\n } = opts;\r\n\r\n if (!tenantId) throw new Error('[DunefoxChat] tenantId is required.');\r\n\r\n // Wait for DOM if we're running during <head> parse\r\n const ready = (cb: () => void) =>\r\n document.readyState === 'loading'\r\n ? document.addEventListener('DOMContentLoaded', cb, { once: true })\r\n : cb();\r\n\r\n ready(() => {\r\n injectStyles(position);\r\n\r\n const uuid = getOrCreateUUID();\r\n const src = `${baseUrl}/api/${tenantId}?uuid=${uuid}`;\r\n\r\n // ── iframe ──\r\n const iframe = document.createElement('iframe');\r\n iframe.id = 'dunefox-frame';\r\n iframe.title = 'Dunefox Chat';\r\n iframe.loading = 'lazy';\r\n iframe.src = src;\r\n\r\n // ── toggle button ──\r\n const btn = document.createElement('button');\r\n btn.id = 'dunefox-btn';\r\n btn.setAttribute('aria-label', 'Open chat');\r\n btn.setAttribute('aria-expanded', 'false');\r\n\r\n const img = document.createElement('img');\r\n img.src = iconUrl;\r\n img.alt = '';\r\n img.width = 60;\r\n img.height = 60;\r\n img.style.cssText = 'display:block;border-radius:50%;object-fit:cover;';\r\n\r\n const closeSpan = document.createElement('span');\r\n closeSpan.innerHTML = CLOSE_SVG;\r\n closeSpan.style.display = 'none';\r\n\r\n btn.append(img, closeSpan);\r\n\r\n // ── state ──\r\n let open = defaultOpen;\r\n const applyState = () => {\r\n iframe.classList.toggle('df-open', open);\r\n // Toggle body class so mobile CSS can move button to top-right only when open\r\n document.body.classList.toggle('df-chat-open', open);\r\n img.style.display = open ? 'none' : 'block';\r\n closeSpan.style.display = open ? 'block' : 'none';\r\n btn.setAttribute('aria-expanded', String(open));\r\n };\r\n\r\n btn.addEventListener('click', () => {\r\n open = !open;\r\n applyState();\r\n });\r\n\r\n document.body.append(iframe, btn);\r\n applyState();\r\n _initialised = true;\r\n });\r\n}\r\n\r\n/**\r\n * Programmatically open the chat panel.\r\n */\r\nexport function open(): void {\r\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\r\n const btn = document.getElementById('dunefox-btn');\r\n if (!iframe || !btn) return;\r\n iframe.classList.add('df-open');\r\n btn.setAttribute('aria-expanded', 'true');\r\n}\r\n\r\n/**\r\n * Programmatically close the chat panel.\r\n */\r\nexport function close(): void {\r\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\r\n const btn = document.getElementById('dunefox-btn');\r\n if (!iframe || !btn) return;\r\n iframe.classList.remove('df-open');\r\n btn.setAttribute('aria-expanded', 'false');\r\n}\r\n\r\n/**\r\n * Remove the widget entirely.\r\n */\r\nexport function destroy(): void {\r\n document.getElementById('dunefox-frame')?.remove();\r\n document.getElementById('dunefox-btn')?.remove();\r\n document.getElementById('dunefox-styles')?.remove();\r\n document.body.classList.remove('df-chat-open');\r\n _initialised = false;\r\n}\r\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,4 +1,4 @@
|
|
|
1
|
-
import {useRef,useEffect}from'react';var v="https://app.dunefox.io",
|
|
1
|
+
import {useRef,useEffect}from'react';var v="https://app.dunefox.io",w="https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png",h="dunefox_chat_uuid";function E(){var e,t;try{let n=localStorage.getItem(h);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(h,n)),n}catch(n){return Math.random().toString(36).slice(2)+Date.now().toString(36)}}function I(e){if(document.getElementById("dunefox-styles"))return;let t=e.startsWith("top"),d=e.endsWith("left")?"left:16px":"right:16px",p=`
|
|
2
2
|
#dunefox-btn {
|
|
3
3
|
position: fixed; ${t?"top:16px":"bottom:16px"}; ${d};
|
|
4
4
|
width: 60px; height: 60px; border-radius: 50%;
|
|
@@ -22,9 +22,14 @@ import {useRef,useEffect}from'react';var v="https://app.dunefox.io",E="https://d
|
|
|
22
22
|
opacity: 1; visibility: visible; transform: translateY(0) scale(1);
|
|
23
23
|
}
|
|
24
24
|
@media (max-width: 768px) {
|
|
25
|
-
|
|
25
|
+
/* Only move button to top-right when chat is open on mobile */
|
|
26
|
+
body.df-chat-open #dunefox-btn {
|
|
26
27
|
bottom: unset !important; top: 16px !important;
|
|
27
28
|
right: 16px !important; left: unset !important;
|
|
29
|
+
/* Strip the circular button style \u2014 show just the X icon */
|
|
30
|
+
background: transparent !important;
|
|
31
|
+
box-shadow: none !important;
|
|
32
|
+
width: 36px !important; height: 36px !important;
|
|
28
33
|
}
|
|
29
34
|
#dunefox-frame {
|
|
30
35
|
width: 100% !important; height: 100% !important; max-height: none !important;
|
|
@@ -34,6 +39,6 @@ import {useRef,useEffect}from'react';var v="https://app.dunefox.io",E="https://d
|
|
|
34
39
|
}
|
|
35
40
|
#dunefox-frame.df-open { transform: translateY(0) !important; }
|
|
36
41
|
}
|
|
37
|
-
`,o=document.createElement("style");o.id="dunefox-styles",o.textContent=
|
|
42
|
+
`,o=document.createElement("style");o.id="dunefox-styles",o.textContent=p,document.head.appendChild(o);}var D='<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>',c=false;function b(e){let t=typeof e=="string"?{tenantId:e}:e;if(c){console.warn("[DunefoxChat] Already initialised. Call destroy() first.");return}let{tenantId:n,position:d="bottom-right",defaultOpen:u=false,baseUrl:m=v,iconUrl:f=w}=t;if(!n)throw new Error("[DunefoxChat] tenantId is required.");(s=>document.readyState==="loading"?document.addEventListener("DOMContentLoaded",s,{once:true}):s())(()=>{I(d);let s=E(),p=`${m}/api/${n}?uuid=${s}`,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 i=document.createElement("img");i.src=f,i.alt="",i.width=60,i.height=60,i.style.cssText="display:block;border-radius:50%;object-fit:cover;";let l=document.createElement("span");l.innerHTML=D,l.style.display="none",r.append(i,l);let a=u,x=()=>{o.classList.toggle("df-open",a),document.body.classList.toggle("df-chat-open",a),i.style.display=a?"none":"block",l.style.display=a?"block":"none",r.setAttribute("aria-expanded",String(a));};r.addEventListener("click",()=>{a=!a,x();}),document.body.append(o,r),x(),c=true;});}function g(){var e,t,n;(e=document.getElementById("dunefox-frame"))==null||e.remove(),(t=document.getElementById("dunefox-btn"))==null||t.remove(),(n=document.getElementById("dunefox-styles"))==null||n.remove(),document.body.classList.remove("df-chat-open"),c=false;}function U(e){let t=useRef(e);return useEffect(()=>{if(typeof window!="undefined")return b(t.current),()=>{g();}},[]),null}
|
|
38
43
|
export{U as DunefoxChatbot};//# sourceMappingURL=react.mjs.map
|
|
39
44
|
//# 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() */\r\nexport interface DunefoxChatOptions {\r\n /** Your Dunefox Tenant ID — found in Settings > Install Widget */\r\n tenantId: string;\r\n /**\r\n * Where to anchor the widget. Defaults to \"bottom-right\".\r\n * Supports: \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\"\r\n */\r\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\r\n /** Open the chat panel on load. Defaults to false. */\r\n defaultOpen?: boolean;\r\n /** Override the base URL (for self-hosted / staging). */\r\n baseUrl?: string;\r\n /** Icon image URL override. Falls back to Dunefox CDN asset. */\r\n iconUrl?: string;\r\n}\r\n\r\nconst DEFAULT_BASE = 'https://app.dunefox.io';\r\nconst DEFAULT_ICON = 'https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png';\r\nconst UUID_KEY = 'dunefox_chat_uuid';\r\n\r\n// ─── UUID generation ──────────────────────────────────────────────────────────\r\nfunction getOrCreateUUID(): string {\r\n try {\r\n let id = localStorage.getItem(UUID_KEY);\r\n if (!id) {\r\n id = crypto?.randomUUID?.()\r\n ?? Math.random().toString(36).slice(2) + Date.now().toString(36);\r\n localStorage.setItem(UUID_KEY, id);\r\n }\r\n return id;\r\n } catch {\r\n // Incognito / storage blocked — generate ephemeral ID\r\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\r\n }\r\n}\r\n\r\n// ─── CSS injector ─────────────────────────────────────────────────────────────\r\nfunction injectStyles(position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'): void {\r\n if (document.getElementById('dunefox-styles')) return;\r\n\r\n const isTop = position.startsWith('top');\r\n const isLeft = position.endsWith('left');\r\n const hSide = isLeft ? 'left:16px' : 'right:16px';\r\n const vBtn = isTop ? 'top:16px' : 'bottom:16px';\r\n // Panel opens away from the button edge\r\n const vFrame = isTop ? 'top:88px' : 'bottom:88px';\r\n // Slide direction: panels below slide up, panels above slide down\r\n const slideOut = isTop ? 'translateY(-24px) scale(.96)' : 'translateY(24px) scale(.96)';\r\n // Hover nudge direction\r\n const hoverTranslate = isTop ? 'translateY(2px) scale(1.05)' : 'translateY(-2px) scale(1.05)';\r\n // Mobile slide direction\r\n const mobileSlideOut = isTop ? 'translateY(-100%)' : 'translateY(100%)';\r\n\r\n const css = `\r\n #dunefox-btn {\r\n position: fixed; ${vBtn}; ${hSide};\r\n width: 60px; height: 60px; border-radius: 50%;\r\n background: #1a1a1a; border: none; cursor: pointer;\r\n display: flex; align-items: center; justify-content: center;\r\n z-index: 2147483646;\r\n box-shadow: 0 4px 20px rgba(0,0,0,.25);\r\n transition: transform .25s ease;\r\n }\r\n #dunefox-btn:hover { transform: ${hoverTranslate}; }\r\n #dunefox-frame {\r\n position: fixed; ${vFrame}; ${hSide};\r\n width: 350px; height: calc(100vh - 160px); max-height: 600px;\r\n border-radius: 16px; border: none; z-index: 2147483645;\r\n box-shadow: 0 8px 32px rgba(0,0,0,.12);\r\n opacity: 0; visibility: hidden;\r\n transform: ${slideOut};\r\n transition: all .4s cubic-bezier(.4,0,.2,1);\r\n }\r\n #dunefox-frame.df-open {\r\n opacity: 1; visibility: visible; transform: translateY(0) scale(1);\r\n }\r\n @media (max-width: 768px) {\r\n #dunefox-btn {\r\n bottom: unset !important; top: 16px !important;\r\n right: 16px !important; left: unset !important;\r\n }\r\n #dunefox-frame {\r\n width: 100% !important; height: 100% !important; max-height: none !important;\r\n position: fixed !important; inset: 0 !important;\r\n border-radius: 0 !important;\r\n transform: ${mobileSlideOut} !important;\r\n }\r\n #dunefox-frame.df-open { transform: translateY(0) !important; }\r\n }\r\n `;\r\n const tag = document.createElement('style');\r\n tag.id = 'dunefox-styles';\r\n tag.textContent = css;\r\n document.head.appendChild(tag);\r\n}\r\n\r\n// ─── SVG icons ────────────────────────────────────────────────────────────────\r\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>`;\r\n\r\n// ─── Main init ────────────────────────────────────────────────────────────────\r\nlet _initialised = false;\r\n\r\n/**\r\n * Initialise the Dunefox chatbot widget.\r\n * Call once on DOMContentLoaded or after your framework has mounted.\r\n *\r\n * @example\r\n * import { init } from 'dunefox-chatbot';\r\n * init({ tenantId: 'YOUR_TENANT_ID' });\r\n */\r\nexport function init(options: DunefoxChatOptions | string): void {\r\n // Allow shorthand: init('tenantId')\r\n const opts: DunefoxChatOptions =\r\n typeof options === 'string' ? { tenantId: options } : options;\r\n\r\n if (_initialised) {\r\n console.warn('[DunefoxChat] Already initialised. Call destroy() first.');\r\n return;\r\n }\r\n\r\n const {\r\n tenantId,\r\n position = 'bottom-right',\r\n defaultOpen = false,\r\n baseUrl = DEFAULT_BASE,\r\n iconUrl = DEFAULT_ICON,\r\n } = opts;\r\n\r\n if (!tenantId) throw new Error('[DunefoxChat] tenantId is required.');\r\n\r\n // Wait for DOM if we're running during <head> parse\r\n const ready = (cb: () => void) =>\r\n document.readyState === 'loading'\r\n ? document.addEventListener('DOMContentLoaded', cb, { once: true })\r\n : cb();\r\n\r\n ready(() => {\r\n injectStyles(position);\r\n\r\n const uuid = getOrCreateUUID();\r\n const src = `${baseUrl}/api/${tenantId}?uuid=${uuid}`;\r\n\r\n // ── iframe ──\r\n const iframe = document.createElement('iframe');\r\n iframe.id = 'dunefox-frame';\r\n iframe.title = 'Dunefox Chat';\r\n iframe.loading = 'lazy';\r\n iframe.src = src;\r\n\r\n // ── toggle button ──\r\n const btn = document.createElement('button');\r\n btn.id = 'dunefox-btn';\r\n btn.setAttribute('aria-label', 'Open chat');\r\n btn.setAttribute('aria-expanded', 'false');\r\n\r\n const img = document.createElement('img');\r\n img.src = iconUrl;\r\n img.alt = '';\r\n img.width = 60;\r\n img.height = 60;\r\n img.style.cssText = 'display:block;border-radius:50%;object-fit:cover;';\r\n\r\n const closeSpan = document.createElement('span');\r\n closeSpan.innerHTML = CLOSE_SVG;\r\n closeSpan.style.display = 'none';\r\n\r\n btn.append(img, closeSpan);\r\n\r\n // ── state ──\r\n let open = defaultOpen;\r\n const applyState = () => {\r\n iframe.classList.toggle('df-open', open);\r\n img.style.display = open ? 'none' : 'block';\r\n closeSpan.style.display = open ? 'block' : 'none';\r\n btn.setAttribute('aria-expanded', String(open));\r\n };\r\n\r\n btn.addEventListener('click', () => {\r\n open = !open;\r\n applyState();\r\n });\r\n\r\n document.body.append(iframe, btn);\r\n applyState();\r\n _initialised = true;\r\n });\r\n}\r\n\r\n/**\r\n * Programmatically open the chat panel.\r\n */\r\nexport function open(): void {\r\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\r\n const btn = document.getElementById('dunefox-btn');\r\n if (!iframe || !btn) return;\r\n iframe.classList.add('df-open');\r\n btn.setAttribute('aria-expanded', 'true');\r\n}\r\n\r\n/**\r\n * Programmatically close the chat panel.\r\n */\r\nexport function close(): void {\r\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\r\n const btn = document.getElementById('dunefox-btn');\r\n if (!iframe || !btn) return;\r\n iframe.classList.remove('df-open');\r\n btn.setAttribute('aria-expanded', 'false');\r\n}\r\n\r\n/**\r\n * Remove the widget entirely.\r\n */\r\nexport function destroy(): void {\r\n document.getElementById('dunefox-frame')?.remove();\r\n document.getElementById('dunefox-btn')?.remove();\r\n document.getElementById('dunefox-styles')?.remove();\r\n _initialised = false;\r\n}\r\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","_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() */\r\nexport interface DunefoxChatOptions {\r\n /** Your Dunefox Tenant ID — found in Settings > Install Widget */\r\n tenantId: string;\r\n /**\r\n * Where to anchor the widget. Defaults to \"bottom-right\".\r\n * Supports: \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\"\r\n */\r\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\r\n /** Open the chat panel on load. Defaults to false. */\r\n defaultOpen?: boolean;\r\n /** Override the base URL (for self-hosted / staging). */\r\n baseUrl?: string;\r\n /** Icon image URL override. Falls back to Dunefox CDN asset. */\r\n iconUrl?: string;\r\n}\r\n\r\nconst DEFAULT_BASE = 'https://app.dunefox.io';\r\nconst DEFAULT_ICON = 'https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png';\r\nconst UUID_KEY = 'dunefox_chat_uuid';\r\n\r\n// ─── UUID generation ──────────────────────────────────────────────────────────\r\nfunction getOrCreateUUID(): string {\r\n try {\r\n let id = localStorage.getItem(UUID_KEY);\r\n if (!id) {\r\n id = crypto?.randomUUID?.()\r\n ?? Math.random().toString(36).slice(2) + Date.now().toString(36);\r\n localStorage.setItem(UUID_KEY, id);\r\n }\r\n return id;\r\n } catch {\r\n // Incognito / storage blocked — generate ephemeral ID\r\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\r\n }\r\n}\r\n\r\n// ─── CSS injector ─────────────────────────────────────────────────────────────\r\nfunction injectStyles(position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'): void {\r\n if (document.getElementById('dunefox-styles')) return;\r\n\r\n const isTop = position.startsWith('top');\r\n const isLeft = position.endsWith('left');\r\n const hSide = isLeft ? 'left:16px' : 'right:16px';\r\n const vBtn = isTop ? 'top:16px' : 'bottom:16px';\r\n // Panel opens away from the button edge\r\n const vFrame = isTop ? 'top:88px' : 'bottom:88px';\r\n // Slide direction: panels below slide up, panels above slide down\r\n const slideOut = isTop ? 'translateY(-24px) scale(.96)' : 'translateY(24px) scale(.96)';\r\n // Hover nudge direction\r\n const hoverTranslate = isTop ? 'translateY(2px) scale(1.05)' : 'translateY(-2px) scale(1.05)';\r\n // Mobile slide direction\r\n const mobileSlideOut = isTop ? 'translateY(-100%)' : 'translateY(100%)';\r\n\r\n const css = `\r\n #dunefox-btn {\r\n position: fixed; ${vBtn}; ${hSide};\r\n width: 60px; height: 60px; border-radius: 50%;\r\n background: #1a1a1a; border: none; cursor: pointer;\r\n display: flex; align-items: center; justify-content: center;\r\n z-index: 2147483646;\r\n box-shadow: 0 4px 20px rgba(0,0,0,.25);\r\n transition: transform .25s ease;\r\n }\r\n #dunefox-btn:hover { transform: ${hoverTranslate}; }\r\n #dunefox-frame {\r\n position: fixed; ${vFrame}; ${hSide};\r\n width: 350px; height: calc(100vh - 160px); max-height: 600px;\r\n border-radius: 16px; border: none; z-index: 2147483645;\r\n box-shadow: 0 8px 32px rgba(0,0,0,.12);\r\n opacity: 0; visibility: hidden;\r\n transform: ${slideOut};\r\n transition: all .4s cubic-bezier(.4,0,.2,1);\r\n }\r\n #dunefox-frame.df-open {\r\n opacity: 1; visibility: visible; transform: translateY(0) scale(1);\r\n }\r\n @media (max-width: 768px) {\r\n /* Only move button to top-right when chat is open on mobile */\r\n body.df-chat-open #dunefox-btn {\r\n bottom: unset !important; top: 16px !important;\r\n right: 16px !important; left: unset !important;\r\n /* Strip the circular button style — show just the X icon */\r\n background: transparent !important;\r\n box-shadow: none !important;\r\n width: 36px !important; height: 36px !important;\r\n }\r\n #dunefox-frame {\r\n width: 100% !important; height: 100% !important; max-height: none !important;\r\n position: fixed !important; inset: 0 !important;\r\n border-radius: 0 !important;\r\n transform: ${mobileSlideOut} !important;\r\n }\r\n #dunefox-frame.df-open { transform: translateY(0) !important; }\r\n }\r\n `;\r\n const tag = document.createElement('style');\r\n tag.id = 'dunefox-styles';\r\n tag.textContent = css;\r\n document.head.appendChild(tag);\r\n}\r\n\r\n// ─── SVG icons ────────────────────────────────────────────────────────────────\r\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>`;\r\n\r\n// ─── Main init ────────────────────────────────────────────────────────────────\r\nlet _initialised = false;\r\n\r\n/**\r\n * Initialise the Dunefox chatbot widget.\r\n * Call once on DOMContentLoaded or after your framework has mounted.\r\n *\r\n * @example\r\n * import { init } from 'dunefox-chatbot';\r\n * init({ tenantId: 'YOUR_TENANT_ID' });\r\n */\r\nexport function init(options: DunefoxChatOptions | string): void {\r\n // Allow shorthand: init('tenantId')\r\n const opts: DunefoxChatOptions =\r\n typeof options === 'string' ? { tenantId: options } : options;\r\n\r\n if (_initialised) {\r\n console.warn('[DunefoxChat] Already initialised. Call destroy() first.');\r\n return;\r\n }\r\n\r\n const {\r\n tenantId,\r\n position = 'bottom-right',\r\n defaultOpen = false,\r\n baseUrl = DEFAULT_BASE,\r\n iconUrl = DEFAULT_ICON,\r\n } = opts;\r\n\r\n if (!tenantId) throw new Error('[DunefoxChat] tenantId is required.');\r\n\r\n // Wait for DOM if we're running during <head> parse\r\n const ready = (cb: () => void) =>\r\n document.readyState === 'loading'\r\n ? document.addEventListener('DOMContentLoaded', cb, { once: true })\r\n : cb();\r\n\r\n ready(() => {\r\n injectStyles(position);\r\n\r\n const uuid = getOrCreateUUID();\r\n const src = `${baseUrl}/api/${tenantId}?uuid=${uuid}`;\r\n\r\n // ── iframe ──\r\n const iframe = document.createElement('iframe');\r\n iframe.id = 'dunefox-frame';\r\n iframe.title = 'Dunefox Chat';\r\n iframe.loading = 'lazy';\r\n iframe.src = src;\r\n\r\n // ── toggle button ──\r\n const btn = document.createElement('button');\r\n btn.id = 'dunefox-btn';\r\n btn.setAttribute('aria-label', 'Open chat');\r\n btn.setAttribute('aria-expanded', 'false');\r\n\r\n const img = document.createElement('img');\r\n img.src = iconUrl;\r\n img.alt = '';\r\n img.width = 60;\r\n img.height = 60;\r\n img.style.cssText = 'display:block;border-radius:50%;object-fit:cover;';\r\n\r\n const closeSpan = document.createElement('span');\r\n closeSpan.innerHTML = CLOSE_SVG;\r\n closeSpan.style.display = 'none';\r\n\r\n btn.append(img, closeSpan);\r\n\r\n // ── state ──\r\n let open = defaultOpen;\r\n const applyState = () => {\r\n iframe.classList.toggle('df-open', open);\r\n // Toggle body class so mobile CSS can move button to top-right only when open\r\n document.body.classList.toggle('df-chat-open', open);\r\n img.style.display = open ? 'none' : 'block';\r\n closeSpan.style.display = open ? 'block' : 'none';\r\n btn.setAttribute('aria-expanded', String(open));\r\n };\r\n\r\n btn.addEventListener('click', () => {\r\n open = !open;\r\n applyState();\r\n });\r\n\r\n document.body.append(iframe, btn);\r\n applyState();\r\n _initialised = true;\r\n });\r\n}\r\n\r\n/**\r\n * Programmatically open the chat panel.\r\n */\r\nexport function open(): void {\r\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\r\n const btn = document.getElementById('dunefox-btn');\r\n if (!iframe || !btn) return;\r\n iframe.classList.add('df-open');\r\n btn.setAttribute('aria-expanded', 'true');\r\n}\r\n\r\n/**\r\n * Programmatically close the chat panel.\r\n */\r\nexport function close(): void {\r\n const iframe = document.getElementById('dunefox-frame') as HTMLIFrameElement | null;\r\n const btn = document.getElementById('dunefox-btn');\r\n if (!iframe || !btn) return;\r\n iframe.classList.remove('df-open');\r\n btn.setAttribute('aria-expanded', 'false');\r\n}\r\n\r\n/**\r\n * Remove the widget entirely.\r\n */\r\nexport function destroy(): void {\r\n document.getElementById('dunefox-frame')?.remove();\r\n document.getElementById('dunefox-btn')?.remove();\r\n document.getElementById('dunefox-styles')?.remove();\r\n document.body.classList.remove('df-chat-open');\r\n _initialised = false;\r\n}\r\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"]}
|