dunefox-chatbot 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,12 +1,24 @@
1
1
  # dunefox-chatbot
2
2
 
3
- Official SDK for embedding the [Dunefox AI chatbot](https://dunefox.io) widget on any website.
3
+ > **The easiest way to add an [AI chatbot](https://dunefox.io) to any website — HTML, React, Next.js, Vue, Laravel, WordPress, and more.**
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/dunefox-chatbot)](https://www.npmjs.com/package/dunefox-chatbot)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
- [![Website](https://img.shields.io/badge/website-dunefox.io-blue)](https://dunefox.io)
7
+ [![Website](https://img.shields.io/badge/website-dunefox.io-orange)](https://dunefox.io)
8
+ [![Console](https://img.shields.io/badge/dashboard-app.dunefox.io-blue)](https://app.dunefox.io)
8
9
 
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).
10
+ **[Dunefox](https://dunefox.io)** is an AI-powered customer support platform. Deploy an intelligent chatbot on your website in under 2 minutes — no backend required. Manage all conversations, FAQs, and analytics from the [Dunefox Console](https://app.dunefox.io).
11
+
12
+ ---
13
+
14
+ ## ✨ Features
15
+
16
+ - 🤖 **AI-powered responses** — trained on your content, answers customer questions instantly
17
+ - ⚡ **Zero-config embed** — one `<script>` tag, works on any site
18
+ - 🎨 **Fully customisable** — position, icon, colors, and white-label branding
19
+ - 📱 **Mobile-first** — full-screen on mobile, floating panel on desktop
20
+ - 🔒 **Privacy-safe** — GDPR-friendly, no PII collected by default
21
+ - 🌐 **Universal** — HTML, React, Next.js, Vue, Nuxt, Laravel, WordPress, Webflow, Shopify
10
22
 
11
23
  ---
12
24
 
@@ -17,21 +29,26 @@ npm install dunefox-chatbot
17
29
  # or: yarn add dunefox-chatbot | pnpm add dunefox-chatbot
18
30
  ```
19
31
 
32
+ Get your **Tenant ID** from the [Dunefox Console → Settings → Install Widget](https://app.dunefox.io).
33
+
20
34
  ---
21
35
 
22
36
  ## Platform Guides
23
37
 
24
- ### HTML / Laravel / WordPress / Any site
38
+ ### HTML / Laravel / WordPress / Any Site
25
39
 
26
- No npm required. Paste this before `</body>`:
40
+ No npm required. Paste this snippet before `</body>`:
27
41
 
28
42
  ```html
43
+ <!-- AI Chatbot by Dunefox (dunefox.io) -->
29
44
  <script src="https://app.dunefox.io/api/sdk/chatbot.js"></script>
30
45
  <script>
31
46
  DunefoxChat.init({ tenantId: 'YOUR_TENANT_ID' });
32
47
  </script>
33
48
  ```
34
49
 
50
+ Works on **WordPress, Webflow, Squarespace, Shopify, Wix**, and any HTML page.
51
+
35
52
  ---
36
53
 
37
54
  ### Next.js (App Router)
@@ -45,6 +62,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
45
62
  <html lang="en">
46
63
  <body>
47
64
  {children}
65
+ {/* AI chatbot — SSR-safe, mounts only in browser */}
48
66
  <DunefoxChatbot tenantId="YOUR_TENANT_ID" />
49
67
  </body>
50
68
  </html>
@@ -90,6 +108,19 @@ onBeforeUnmount(async () => {
90
108
 
91
109
  ---
92
110
 
111
+ ### WordPress / WooCommerce
112
+
113
+ Add via **WPCode** or your theme's `functions.php`:
114
+
115
+ ```php
116
+ add_action('wp_footer', function() { ?>
117
+ <script src="https://app.dunefox.io/api/sdk/chatbot.js"></script>
118
+ <script>DunefoxChat.init({ tenantId: 'YOUR_TENANT_ID' });</script>
119
+ <?php });
120
+ ```
121
+
122
+ ---
123
+
93
124
  ## API Reference
94
125
 
95
126
  ### `init(options)`
@@ -97,23 +128,32 @@ onBeforeUnmount(async () => {
97
128
  | Option | Type | Default | Description |
98
129
  |--------|------|---------|-------------|
99
130
  | `tenantId` | `string` | **required** | Your [Dunefox](https://dunefox.io) tenant ID |
100
- | `position` | `'bottom-right' \| 'bottom-left'` | `'bottom-right'` | Widget anchor position |
101
- | `defaultOpen` | `boolean` | `false` | Open the panel on load |
131
+ | `position` | `'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left'` | `'bottom-right'` | Widget anchor corner |
132
+ | `defaultOpen` | `boolean` | `false` | Open the chat panel on page load |
133
+ | `hideBranding` | `boolean` | `false` | Remove "Powered by Dunefox" badge (paid plans) |
102
134
  | `baseUrl` | `string` | `https://app.dunefox.io` | Override for staging/self-hosted |
103
- | `iconUrl` | `string` | Dunefox default | Custom toggle button icon |
135
+ | `iconUrl` | `string` | Dunefox default | Custom toggle button icon URL |
104
136
 
105
137
  ### `open()` / `close()`
106
138
 
107
- Programmatically show or hide the chat panel.
139
+ Programmatically show or hide the chat panel:
108
140
 
109
141
  ```js
110
142
  DunefoxChat.open();
111
143
  DunefoxChat.close();
112
144
  ```
113
145
 
146
+ Useful for triggering the chatbot from your own CTA button:
147
+
148
+ ```js
149
+ document.getElementById('my-help-btn').addEventListener('click', () => {
150
+ DunefoxChat.open();
151
+ });
152
+ ```
153
+
114
154
  ### `destroy()`
115
155
 
116
- Completely remove the widget from the page.
156
+ Completely remove the widget from the page:
117
157
 
118
158
  ```js
119
159
  DunefoxChat.destroy();
@@ -121,30 +161,60 @@ DunefoxChat.destroy();
121
161
 
122
162
  ---
123
163
 
124
- ## Building from source
164
+ ## Branding & White Label
165
+
166
+ By default, a small **"⚡ Powered by Dunefox AI"** badge appears near the widget — this links back to [dunefox.io](https://dunefox.io) and helps us grow.
167
+
168
+ To remove it on **paid / white-label plans**:
169
+
170
+ ```js
171
+ DunefoxChat.init({
172
+ tenantId: 'YOUR_TENANT_ID',
173
+ hideBranding: true, // available on Pro and above
174
+ });
175
+ ```
176
+
177
+ [Upgrade your plan →](https://dunefox.io/pricing)
178
+
179
+ ---
180
+
181
+ ## Building from Source
125
182
 
126
183
  ```bash
127
184
  cd packages/chatbot-sdk
128
185
  npm install
129
186
  npm run build
130
- # Outputs: dist/index.js (ESM), dist/index.cjs (CJS), dist/chatbot.umd.js (browser)
187
+ # Outputs: dist/index.js (ESM), dist/index.cjs (CJS), dist/chatbot.umd.js (browser UMD)
131
188
  ```
132
189
 
133
190
  ---
134
191
 
135
192
  ## About Dunefox
136
193
 
137
- [Dunefox](https://dunefox.io) is a modern AI customer support platform for businesses of all sizes. With Dunefox you can:
194
+ **[Dunefox](https://dunefox.io)** is a modern [AI customer support platform](https://dunefox.io) built for businesses of all sizes. With Dunefox you can:
195
+
196
+ - 🤖 Deploy an **[AI chatbot for your website](https://dunefox.io)** in under 2 minutes
197
+ - 💬 Manage all customer conversations from a **unified inbox**
198
+ - ❓ Build and publish **FAQ knowledge bases** your AI can answer from
199
+ - 📊 Track **engagement and resolution rates** with real-time analytics
200
+ - 🔗 Integrate via **webhooks and REST APIs**
201
+ - 🛒 Works with **WooCommerce, Shopify, and any e-commerce platform**
138
202
 
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
203
+ Get started for free at **[dunefox.io](https://dunefox.io)** no credit card required.
143
204
 
144
- Learn more and get started for free at **[dunefox.io](https://dunefox.io)**.
205
+ ---
206
+
207
+ ## Support & Docs
208
+
209
+ - 📖 Documentation: [docs.dunefox.com](https://docs.dunefox.com)
210
+ - 💬 Community & support: [dunefox.io/support](https://dunefox.io/support)
211
+ - 🐛 Issues: [GitHub Issues](https://github.com/SunnyDhakane17/Dunefox-production/issues)
212
+ - 🌐 Website: [dunefox.io](https://dunefox.io)
145
213
 
146
214
  ---
147
215
 
148
216
  ## License
149
217
 
150
218
  MIT © [Dunefox](https://dunefox.io)
219
+
220
+ *Built with ❤️ by the [Dunefox](https://dunefox.io) team — [AI chatbots for every website](https://dunefox.io).*
@@ -1,6 +1,6 @@
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:()=>E,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 $(){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 k(e){if(document.getElementById("dunefox-styles"))return;let t=e.startsWith("top"),i=e.endsWith("left")?"left:16px":"right:16px",p=`
1
+ "use strict";var DunefoxChat=(()=>{var m=Object.defineProperty;var S=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var B=Object.prototype.hasOwnProperty;var C=(e,t)=>{for(var o in t)m(e,o,{get:t[o],enumerable:!0})},D=(e,t,o,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of L(t))!B.call(e,i)&&i!==o&&m(e,i,{get:()=>t[i],enumerable:!(n=S(t,i))||n.enumerable});return e};var O=e=>D(m({},"__esModule",{value:!0}),e);var M={};C(M,{close:()=>w,destroy:()=>E,init:()=>y,open:()=>v});var A="https://app.dunefox.io",U="https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png",g="dunefox_chat_uuid";function k(){var e,t;try{let o=localStorage.getItem(g);return o||(o=(t=(e=crypto==null?void 0:crypto.randomUUID)==null?void 0:e.call(crypto))!=null?t:Math.random().toString(36).slice(2)+Date.now().toString(36),localStorage.setItem(g,o)),o}catch(o){return Math.random().toString(36).slice(2)+Date.now().toString(36)}}function T(e){if(document.getElementById("dunefox-styles"))return;let t=e.startsWith("top"),n=e.endsWith("left")?"left:16px":"right:16px",d=`
2
2
  #dunefox-btn {
3
- position: fixed; ${t?"top:16px":"bottom:16px"}; ${i};
3
+ position: fixed; ${t?"top:16px":"bottom:16px"}; ${n};
4
4
  width: 60px; height: 60px; border-radius: 50%;
5
5
  background: #1a1a1a; border: none; cursor: pointer;
6
6
  display: flex; align-items: center; justify-content: center;
@@ -10,7 +10,7 @@
10
10
  }
11
11
  #dunefox-btn:hover { transform: ${t?"translateY(2px) scale(1.05)":"translateY(-2px) scale(1.05)"}; }
12
12
  #dunefox-frame {
13
- position: fixed; ${t?"top:88px":"bottom:88px"}; ${i};
13
+ position: fixed; ${t?"top:88px":"bottom:88px"}; ${n};
14
14
  width: 350px; height: calc(100vh - 160px); max-height: 600px;
15
15
  border-radius: 16px; border: none; z-index: 2147483645;
16
16
  box-shadow: 0 8px 32px rgba(0,0,0,.12);
@@ -26,6 +26,10 @@
26
26
  body.df-chat-open #dunefox-btn {
27
27
  bottom: unset !important; top: 16px !important;
28
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;
29
33
  }
30
34
  #dunefox-frame {
31
35
  width: 100% !important; height: 100% !important; max-height: none !important;
@@ -35,4 +39,4 @@
35
39
  }
36
40
  #dunefox-frame.df-open { transform: translateY(0) !important; }
37
41
  }
38
- `,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())(()=>{k(i);let l=$(),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 E(){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);})();
42
+ `,c=document.createElement("style");c.id="dunefox-styles",c.textContent=d,document.head.appendChild(c)}function $(e){if(document.getElementById("dunefox-badge"))return;let t=e.endsWith("left"),o=e.startsWith("top"),n=document.createElement("a");n.id="dunefox-badge",n.href="https://dunefox.io?ref=widget",n.target="_blank",n.rel="noopener",n.title="Dunefox \u2014 AI Chatbot for Websites",n.setAttribute("aria-label","Powered by Dunefox AI Chatbot"),n.textContent="Powered by Dunefox AI",n.style.cssText=["position:fixed",o?"top:8px":"bottom:6px",t?"left:80px":"right:80px","z-index:2147483644","font-size:10px",'font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',"font-weight:500","color:rgba(0,0,0,0.38)","text-decoration:none","letter-spacing:0.01em","white-space:nowrap","pointer-events:auto","transition:color 0.2s ease","line-height:1"].join(";"),n.addEventListener("mouseenter",()=>{n.style.color="rgba(235,108,51,0.85)"}),n.addEventListener("mouseleave",()=>{n.style.color="rgba(0,0,0,0.38)"}),document.body.appendChild(n)}var Y='<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>',f=!1;function y(e){let t=typeof e=="string"?{tenantId:e}:e;if(f){console.warn("[DunefoxChat] Already initialised. Call destroy() first.");return}let{tenantId:o,position:n="bottom-right",defaultOpen:i=!1,baseUrl:u=A,iconUrl:x=U,hideBranding:h=!1}=t;if(!o)throw new Error("[DunefoxChat] tenantId is required.");(d=>document.readyState==="loading"?document.addEventListener("DOMContentLoaded",d,{once:!0}):d())(()=>{T(n);let d=k(),c=`${u}/api/${o}?uuid=${d}`,l=document.createElement("iframe");l.id="dunefox-frame",l.title="Dunefox Chat",l.loading="lazy",l.src=c;let a=document.createElement("button");a.id="dunefox-btn",a.setAttribute("aria-label","Open chat"),a.setAttribute("aria-expanded","false");let r=document.createElement("img");r.src=x,r.alt="",r.width=60,r.height=60,r.style.cssText="display:block;border-radius:50%;object-fit:cover;";let p=document.createElement("span");p.innerHTML=Y,p.style.display="none",a.append(r,p);let s=i,b=()=>{l.classList.toggle("df-open",s),document.body.classList.toggle("df-chat-open",s),r.style.display=s?"none":"block",p.style.display=s?"block":"none",a.setAttribute("aria-expanded",String(s))};a.addEventListener("click",()=>{s=!s,b()}),document.body.append(l,a),b(),h||$(n),f=!0})}function v(){let e=document.getElementById("dunefox-frame"),t=document.getElementById("dunefox-btn");!e||!t||(e.classList.add("df-open"),t.setAttribute("aria-expanded","true"))}function w(){let e=document.getElementById("dunefox-frame"),t=document.getElementById("dunefox-btn");!e||!t||(e.classList.remove("df-open"),t.setAttribute("aria-expanded","false"))}function E(){var e,t,o,n;(e=document.getElementById("dunefox-frame"))==null||e.remove(),(t=document.getElementById("dunefox-btn"))==null||t.remove(),(o=document.getElementById("dunefox-styles"))==null||o.remove(),(n=document.getElementById("dunefox-badge"))==null||n.remove(),document.body.classList.remove("df-chat-open"),f=!1}return O(M);})();
package/dist/index.d.mts CHANGED
@@ -13,6 +13,11 @@ interface DunefoxChatOptions {
13
13
  baseUrl?: string;
14
14
  /** Icon image URL override. Falls back to Dunefox CDN asset. */
15
15
  iconUrl?: string;
16
+ /**
17
+ * Hide the "Powered by Dunefox" branding badge.
18
+ * Available on paid / white-label plans.
19
+ */
20
+ hideBranding?: boolean;
16
21
  }
17
22
  /**
18
23
  * Initialise the Dunefox chatbot widget.
package/dist/index.d.ts CHANGED
@@ -13,6 +13,11 @@ interface DunefoxChatOptions {
13
13
  baseUrl?: string;
14
14
  /** Icon image URL override. Falls back to Dunefox CDN asset. */
15
15
  iconUrl?: string;
16
+ /**
17
+ * Hide the "Powered by Dunefox" branding badge.
18
+ * Available on paid / white-label plans.
19
+ */
20
+ hideBranding?: boolean;
16
21
  }
17
22
  /**
18
23
  * Initialise the Dunefox chatbot widget.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
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 E(e){if(document.getElementById("dunefox-styles"))return;let t=e.startsWith("top"),d=e.endsWith("left")?"left:16px":"right:16px",c=`
1
+ 'use strict';var y="https://app.dunefox.io",v="https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png",b="dunefox_chat_uuid";function w(){var e,n;try{let o=localStorage.getItem(b);return o||(o=(n=(e=crypto==null?void 0:crypto.randomUUID)==null?void 0:e.call(crypto))!=null?n:Math.random().toString(36).slice(2)+Date.now().toString(36),localStorage.setItem(b,o)),o}catch(o){return Math.random().toString(36).slice(2)+Date.now().toString(36)}}function E(e){if(document.getElementById("dunefox-styles"))return;let n=e.startsWith("top"),t=e.endsWith("left")?"left:16px":"right:16px",s=`
2
2
  #dunefox-btn {
3
- position: fixed; ${t?"top:16px":"bottom:16px"}; ${d};
3
+ position: fixed; ${n?"top:16px":"bottom:16px"}; ${t};
4
4
  width: 60px; height: 60px; border-radius: 50%;
5
5
  background: #1a1a1a; border: none; cursor: pointer;
6
6
  display: flex; align-items: center; justify-content: center;
@@ -8,14 +8,14 @@
8
8
  box-shadow: 0 4px 20px rgba(0,0,0,.25);
9
9
  transition: transform .25s ease;
10
10
  }
11
- #dunefox-btn:hover { transform: ${t?"translateY(2px) scale(1.05)":"translateY(-2px) scale(1.05)"}; }
11
+ #dunefox-btn:hover { transform: ${n?"translateY(2px) scale(1.05)":"translateY(-2px) scale(1.05)"}; }
12
12
  #dunefox-frame {
13
- position: fixed; ${t?"top:88px":"bottom:88px"}; ${d};
13
+ position: fixed; ${n?"top:88px":"bottom:88px"}; ${t};
14
14
  width: 350px; height: calc(100vh - 160px); max-height: 600px;
15
15
  border-radius: 16px; border: none; z-index: 2147483645;
16
16
  box-shadow: 0 8px 32px rgba(0,0,0,.12);
17
17
  opacity: 0; visibility: hidden;
18
- transform: ${t?"translateY(-24px) scale(.96)":"translateY(24px) scale(.96)"};
18
+ transform: ${n?"translateY(-24px) scale(.96)":"translateY(24px) scale(.96)"};
19
19
  transition: all .4s cubic-bezier(.4,0,.2,1);
20
20
  }
21
21
  #dunefox-frame.df-open {
@@ -26,15 +26,19 @@
26
26
  body.df-chat-open #dunefox-btn {
27
27
  bottom: unset !important; top: 16px !important;
28
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;
29
33
  }
30
34
  #dunefox-frame {
31
35
  width: 100% !important; height: 100% !important; max-height: none !important;
32
36
  position: fixed !important; inset: 0 !important;
33
37
  border-radius: 0 !important;
34
- transform: ${t?"translateY(-100%)":"translateY(100%)"} !important;
38
+ transform: ${n?"translateY(-100%)":"translateY(100%)"} !important;
35
39
  }
36
40
  #dunefox-frame.df-open { transform: translateY(0) !important; }
37
41
  }
38
- `,o=document.createElement("style");o.id="dunefox-styles",o.textContent=c,document.head.appendChild(o);}var w='<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())(()=>{E(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=w,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;}
39
- exports.close=L;exports.destroy=O;exports.init=I;exports.open=S;//# sourceMappingURL=index.js.map
42
+ `,l=document.createElement("style");l.id="dunefox-styles",l.textContent=s,document.head.appendChild(l);}function I(e){if(document.getElementById("dunefox-badge"))return;let n=e.endsWith("left"),o=e.startsWith("top"),t=document.createElement("a");t.id="dunefox-badge",t.href="https://dunefox.io?ref=widget",t.target="_blank",t.rel="noopener",t.title="Dunefox \u2014 AI Chatbot for Websites",t.setAttribute("aria-label","Powered by Dunefox AI Chatbot"),t.textContent="Powered by Dunefox AI",t.style.cssText=["position:fixed",o?"top:8px":"bottom:6px",n?"left:80px":"right:80px","z-index:2147483644","font-size:10px",'font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',"font-weight:500","color:rgba(0,0,0,0.38)","text-decoration:none","letter-spacing:0.01em","white-space:nowrap","pointer-events:auto","transition:color 0.2s ease","line-height:1"].join(";"),t.addEventListener("mouseenter",()=>{t.style.color="rgba(235,108,51,0.85)";}),t.addEventListener("mouseleave",()=>{t.style.color="rgba(0,0,0,0.38)";}),document.body.appendChild(t);}var S='<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 L(e){let n=typeof e=="string"?{tenantId:e}:e;if(p){console.warn("[DunefoxChat] Already initialised. Call destroy() first.");return}let{tenantId:o,position:t="bottom-right",defaultOpen:m=false,baseUrl:f=y,iconUrl:u=v,hideBranding:x=false}=n;if(!o)throw new Error("[DunefoxChat] tenantId is required.");(s=>document.readyState==="loading"?document.addEventListener("DOMContentLoaded",s,{once:true}):s())(()=>{E(t);let s=w(),l=`${f}/api/${o}?uuid=${s}`,d=document.createElement("iframe");d.id="dunefox-frame",d.title="Dunefox Chat",d.loading="lazy",d.src=l;let i=document.createElement("button");i.id="dunefox-btn",i.setAttribute("aria-label","Open chat"),i.setAttribute("aria-expanded","false");let a=document.createElement("img");a.src=u,a.alt="",a.width=60,a.height=60,a.style.cssText="display:block;border-radius:50%;object-fit:cover;";let c=document.createElement("span");c.innerHTML=S,c.style.display="none",i.append(a,c);let r=m,h=()=>{d.classList.toggle("df-open",r),document.body.classList.toggle("df-chat-open",r),a.style.display=r?"none":"block",c.style.display=r?"block":"none",i.setAttribute("aria-expanded",String(r));};i.addEventListener("click",()=>{r=!r,h();}),document.body.append(d,i),h(),x||I(t),p=true;});}function B(){let e=document.getElementById("dunefox-frame"),n=document.getElementById("dunefox-btn");!e||!n||(e.classList.add("df-open"),n.setAttribute("aria-expanded","true"));}function C(){let e=document.getElementById("dunefox-frame"),n=document.getElementById("dunefox-btn");!e||!n||(e.classList.remove("df-open"),n.setAttribute("aria-expanded","false"));}function D(){var e,n,o,t;(e=document.getElementById("dunefox-frame"))==null||e.remove(),(n=document.getElementById("dunefox-btn"))==null||n.remove(),(o=document.getElementById("dunefox-styles"))==null||o.remove(),(t=document.getElementById("dunefox-badge"))==null||t.remove(),document.body.classList.remove("df-chat-open"),p=false;}
43
+ exports.close=C;exports.destroy=D;exports.init=L;exports.open=B;//# sourceMappingURL=index.js.map
40
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 /* 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 }\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"]}
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","injectBadge","isLeft","badge","CLOSE_SVG","_initialised","init","options","opts","tenantId","defaultOpen","baseUrl","iconUrl","hideBranding","cb","uuid","src","iframe","btn","img","closeSpan","open","applyState","close","destroy","_c","_d"],"mappings":"aAsBA,IAAMA,CAAAA,CAAe,yBACfC,CAAAA,CAAe,0DAAA,CACfC,EAAW,mBAAA,CAGjB,SAASC,GAA0B,CA3BnC,IAAAC,EAAAC,CAAAA,CA4BE,GAAI,CACF,IAAIC,CAAAA,CAAK,aAAa,OAAA,CAAQJ,CAAQ,CAAA,CACtC,OAAKI,CAAAA,GACHA,CAAAA,CAAAA,CAAKD,GAAAD,CAAAA,CAAA,MAAA,EAAA,IAAA,CAAA,KAAA,CAAA,CAAA,MAAA,CAAQ,aAAR,IAAA,CAAA,KAAA,CAAA,CAAAA,CAAAA,CAAA,oBAAAC,CAAAA,CACA,IAAA,CAAK,QAAO,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA,CAAI,IAAA,CAAK,KAAI,CAAE,QAAA,CAAS,EAAE,CAAA,CACjE,YAAA,CAAa,OAAA,CAAQH,EAAUI,CAAE,CAAA,CAAA,CAE5BA,CACT,CAAA,MAAQC,CAAAA,CAAA,CAEN,OAAO,IAAA,CAAK,QAAO,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA,CAAI,IAAA,CAAK,KAAI,CAAE,QAAA,CAAS,EAAE,CACrE,CACF,CAGA,SAASC,CAAAA,CAAaC,CAAAA,CAA2E,CAC/F,GAAI,QAAA,CAAS,eAAe,gBAAgB,CAAA,CAAG,OAE/C,IAAMC,CAAAA,CAAQD,EAAS,UAAA,CAAW,KAAK,EAEjCE,CAAAA,CADSF,CAAAA,CAAS,SAAS,MAAM,CAAA,CAChB,WAAA,CAAc,YAAA,CAW/BG,CAAAA,CAAM;AAAA;AAAA,uBAAA,EAVCF,CAAAA,CAAQ,UAAA,CAAa,aAYP,CAAA,EAAA,EAAKC,CAAK,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oCAAA,EANdD,CAAAA,CAAQ,8BAAgC,8BAcb,CAAA;AAAA;AAAA,uBAAA,EAlBnCA,CAAAA,CAAQ,UAAA,CAAa,aAoBP,CAAA,EAAA,EAAKC,CAAK,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAAA,EAlBtBD,CAAAA,CAAQ,+BAAiC,6BAuBjC,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAA,EAnBFA,CAAAA,CAAQ,oBAAsB,kBAuCpB,CAAA;AAAA;AAAA;AAAA;AAAA,EAAA,CAAA,CAK3BG,CAAAA,CAAM,QAAA,CAAS,aAAA,CAAc,OAAO,CAAA,CAC1CA,EAAI,EAAA,CAAK,gBAAA,CACTA,CAAAA,CAAI,WAAA,CAAcD,CAAAA,CAClB,QAAA,CAAS,KAAK,WAAA,CAAYC,CAAG,EAC/B,CAGA,SAASC,CAAAA,CAAYL,EAA2E,CAC9F,GAAI,QAAA,CAAS,cAAA,CAAe,eAAe,CAAA,CAAG,OAE9C,IAAMM,CAAAA,CAASN,CAAAA,CAAS,QAAA,CAAS,MAAM,CAAA,CACjCC,EAAQD,CAAAA,CAAS,UAAA,CAAW,KAAK,CAAA,CAEjCO,CAAAA,CAAQ,QAAA,CAAS,cAAc,GAAG,CAAA,CACxCA,CAAAA,CAAM,EAAA,CAAK,eAAA,CACXA,CAAAA,CAAM,IAAA,CAAO,+BAAA,CACbA,CAAAA,CAAM,MAAA,CAAS,QAAA,CACfA,CAAAA,CAAM,GAAA,CAAM,UAAA,CACZA,EAAM,KAAA,CAAQ,wCAAA,CACdA,CAAAA,CAAM,YAAA,CAAa,YAAA,CAAc,+BAA+B,EAChEA,CAAAA,CAAM,WAAA,CAAc,uBAAA,CAEpBA,CAAAA,CAAM,KAAA,CAAM,OAAA,CAAU,CACpB,gBAAA,CACAN,CAAAA,CAAQ,SAAA,CAAY,YAAA,CACpBK,CAAAA,CAAS,WAAA,CAAc,YAAA,CACvB,oBAAA,CACA,gBAAA,CACA,oEAAA,CACA,iBAAA,CACA,wBAAA,CACA,sBAAA,CACA,uBAAA,CACA,qBACA,qBAAA,CACA,4BAAA,CACA,eACF,CAAA,CAAE,IAAA,CAAK,GAAG,EAEVC,CAAAA,CAAM,gBAAA,CAAiB,YAAA,CAAc,IAAM,CAAEA,CAAAA,CAAM,MAAM,KAAA,CAAQ,wBAAyB,CAAC,CAAA,CAC3FA,CAAAA,CAAM,gBAAA,CAAiB,YAAA,CAAc,IAAM,CAAEA,CAAAA,CAAM,KAAA,CAAM,KAAA,CAAQ,mBAAoB,CAAC,EAEtF,QAAA,CAAS,IAAA,CAAK,WAAA,CAAYA,CAAK,EACjC,CAGA,IAAMC,CAAAA,CAAY,kOAAA,CAGdC,CAAAA,CAAe,KAAA,CAUZ,SAASC,CAAAA,CAAKC,EAA4C,CAE/D,IAAMC,CAAAA,CACJ,OAAOD,CAAAA,EAAY,QAAA,CAAW,CAAE,QAAA,CAAUA,CAAQ,CAAA,CAAIA,CAAAA,CAExD,GAAIF,CAAAA,CAAc,CAChB,QAAQ,IAAA,CAAK,0DAA0D,CAAA,CACvE,MACF,CAEA,GAAM,CACJ,QAAA,CAAAI,CAAAA,CACA,QAAA,CAAAb,CAAAA,CAAW,cAAA,CACX,WAAA,CAAAc,EAAc,KAAA,CACd,OAAA,CAAAC,CAAAA,CAAUxB,CAAAA,CACV,OAAA,CAAAyB,CAAAA,CAAUxB,CAAAA,CACV,YAAA,CAAAyB,CAAAA,CAAe,KACjB,CAAA,CAAIL,CAAAA,CAEJ,GAAI,CAACC,EAAU,MAAM,IAAI,KAAA,CAAM,qCAAqC,CAAA,CAAA,CAGrDK,CAAAA,EACb,SAAS,UAAA,GAAe,SAAA,CACpB,QAAA,CAAS,gBAAA,CAAiB,kBAAA,CAAoBA,CAAAA,CAAI,CAAE,IAAA,CAAM,IAAK,CAAC,CAAA,CAChEA,CAAAA,EAAG,EAEH,IAAM,CACVnB,CAAAA,CAAaC,CAAQ,CAAA,CAErB,IAAMmB,CAAAA,CAAOzB,CAAAA,GACP0B,CAAAA,CAAM,CAAA,EAAGL,CAAO,CAAA,KAAA,EAAQF,CAAQ,CAAA,MAAA,EAASM,CAAI,CAAA,CAAA,CAG7CE,CAAAA,CAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA,CAC9CA,EAAO,EAAA,CAAK,eAAA,CACZA,CAAAA,CAAO,KAAA,CAAQ,cAAA,CACfA,CAAAA,CAAO,OAAA,CAAU,MAAA,CACjBA,CAAAA,CAAO,GAAA,CAAMD,CAAAA,CAGb,IAAME,CAAAA,CAAM,QAAA,CAAS,cAAc,QAAQ,CAAA,CAC3CA,CAAAA,CAAI,EAAA,CAAK,aAAA,CACTA,CAAAA,CAAI,aAAa,YAAA,CAAc,WAAW,CAAA,CAC1CA,CAAAA,CAAI,YAAA,CAAa,eAAA,CAAiB,OAAO,CAAA,CAEzC,IAAMC,CAAAA,CAAM,QAAA,CAAS,aAAA,CAAc,KAAK,EACxCA,CAAAA,CAAI,GAAA,CAAMP,CAAAA,CACVO,CAAAA,CAAI,GAAA,CAAM,EAAA,CACVA,EAAI,KAAA,CAAQ,EAAA,CACZA,CAAAA,CAAI,MAAA,CAAS,EAAA,CACbA,CAAAA,CAAI,MAAM,OAAA,CAAU,mDAAA,CAEpB,IAAMC,CAAAA,CAAY,QAAA,CAAS,aAAA,CAAc,MAAM,CAAA,CAC/CA,CAAAA,CAAU,SAAA,CAAYhB,CAAAA,CACtBgB,CAAAA,CAAU,KAAA,CAAM,OAAA,CAAU,MAAA,CAE1BF,CAAAA,CAAI,MAAA,CAAOC,CAAAA,CAAKC,CAAS,CAAA,CAGzB,IAAIC,EAAOX,CAAAA,CACLY,CAAAA,CAAa,IAAM,CACvBL,CAAAA,CAAO,SAAA,CAAU,OAAO,SAAA,CAAWI,CAAI,CAAA,CAEvC,QAAA,CAAS,IAAA,CAAK,SAAA,CAAU,OAAO,cAAA,CAAgBA,CAAI,CAAA,CACnDF,CAAAA,CAAI,KAAA,CAAM,OAAA,CAAUE,CAAAA,CAAO,MAAA,CAAS,OAAA,CACpCD,CAAAA,CAAU,KAAA,CAAM,OAAA,CAAUC,CAAAA,CAAO,OAAA,CAAU,OAC3CH,CAAAA,CAAI,YAAA,CAAa,eAAA,CAAiB,MAAA,CAAOG,CAAI,CAAC,EAChD,CAAA,CAEAH,CAAAA,CAAI,gBAAA,CAAiB,OAAA,CAAS,IAAM,CAClCG,EAAO,CAACA,CAAAA,CACRC,CAAAA,GACF,CAAC,CAAA,CAED,QAAA,CAAS,IAAA,CAAK,MAAA,CAAOL,CAAAA,CAAQC,CAAG,CAAA,CAChCI,CAAAA,EAAW,CAGNT,GACHZ,CAAAA,CAAYL,CAAQ,CAAA,CAGtBS,CAAAA,CAAe,KACjB,CAAC,EACH,CAKO,SAASgB,CAAAA,EAAa,CAC3B,IAAMJ,CAAAA,CAAS,SAAS,cAAA,CAAe,eAAe,CAAA,CAChDC,CAAAA,CAAM,QAAA,CAAS,cAAA,CAAe,aAAa,CAAA,CAC7C,CAACD,CAAAA,EAAU,CAACC,CAAAA,GAChBD,CAAAA,CAAO,SAAA,CAAU,IAAI,SAAS,CAAA,CAC9BC,CAAAA,CAAI,YAAA,CAAa,eAAA,CAAiB,MAAM,GAC1C,CAKO,SAASK,CAAAA,EAAc,CAC5B,IAAMN,CAAAA,CAAS,SAAS,cAAA,CAAe,eAAe,CAAA,CAChDC,CAAAA,CAAM,QAAA,CAAS,cAAA,CAAe,aAAa,CAAA,CAC7C,CAACD,CAAAA,EAAU,CAACC,CAAAA,GAChBD,CAAAA,CAAO,SAAA,CAAU,OAAO,SAAS,CAAA,CACjCC,CAAAA,CAAI,YAAA,CAAa,eAAA,CAAiB,OAAO,GAC3C,CAKO,SAASM,CAAAA,EAAgB,CAhRhC,IAAAjC,CAAAA,CAAAC,EAAAiC,CAAAA,CAAAC,CAAAA,CAAAA,CAiREnC,CAAAA,CAAA,QAAA,CAAS,cAAA,CAAe,eAAe,CAAA,GAAvC,IAAA,EAAAA,CAAAA,CAA0C,MAAA,EAAA,CAAA,CAC1CC,CAAAA,CAAA,QAAA,CAAS,cAAA,CAAe,aAAa,IAArC,IAAA,EAAAA,CAAAA,CAAwC,MAAA,EAAA,CAAA,CACxCiC,CAAAA,CAAA,QAAA,CAAS,cAAA,CAAe,gBAAgB,CAAA,GAAxC,IAAA,EAAAA,CAAAA,CAA2C,MAAA,EAAA,CAAA,CAC3CC,CAAAA,CAAA,QAAA,CAAS,eAAe,eAAe,CAAA,GAAvC,IAAA,EAAAA,CAAAA,CAA0C,MAAA,EAAA,CAC1C,QAAA,CAAS,IAAA,CAAK,SAAA,CAAU,MAAA,CAAO,cAAc,CAAA,CAC7CrB,CAAAA,CAAe,MACjB","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 * Hide the \"Powered by Dunefox\" branding badge.\r\n * Available on paid / white-label plans.\r\n */\r\n hideBranding?: boolean;\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// ─── Branding badge injector ──────────────────────────────────────────────────\r\nfunction injectBadge(position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'): void {\r\n if (document.getElementById('dunefox-badge')) return;\r\n\r\n const isLeft = position.endsWith('left');\r\n const isTop = position.startsWith('top');\r\n\r\n const badge = document.createElement('a');\r\n badge.id = 'dunefox-badge';\r\n badge.href = 'https://dunefox.io?ref=widget';\r\n badge.target = '_blank';\r\n badge.rel = 'noopener'; // noopener only — NO nofollow → passes link equity\r\n badge.title = 'Dunefox — AI Chatbot for Websites';\r\n badge.setAttribute('aria-label', 'Powered by Dunefox AI Chatbot');\r\n badge.textContent = 'Powered by Dunefox AI';\r\n\r\n badge.style.cssText = [\r\n 'position:fixed',\r\n isTop ? 'top:8px' : 'bottom:6px',\r\n isLeft ? 'left:80px' : 'right:80px',\r\n 'z-index:2147483644',\r\n 'font-size:10px',\r\n 'font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif',\r\n 'font-weight:500',\r\n 'color:rgba(0,0,0,0.38)',\r\n 'text-decoration:none',\r\n 'letter-spacing:0.01em',\r\n 'white-space:nowrap',\r\n 'pointer-events:auto',\r\n 'transition:color 0.2s ease',\r\n 'line-height:1',\r\n ].join(';');\r\n\r\n badge.addEventListener('mouseenter', () => { badge.style.color = 'rgba(235,108,51,0.85)'; });\r\n badge.addEventListener('mouseleave', () => { badge.style.color = 'rgba(0,0,0,0.38)'; });\r\n\r\n document.body.appendChild(badge);\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 hideBranding = false,\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\r\n // Inject SEO backlink badge on free/default plans\r\n if (!hideBranding) {\r\n injectBadge(position);\r\n }\r\n\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.getElementById('dunefox-badge')?.remove();\r\n document.body.classList.remove('df-chat-open');\r\n _initialised = false;\r\n}\r\n"]}
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
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 E(e){if(document.getElementById("dunefox-styles"))return;let t=e.startsWith("top"),d=e.endsWith("left")?"left:16px":"right:16px",c=`
1
+ var y="https://app.dunefox.io",v="https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png",b="dunefox_chat_uuid";function w(){var e,n;try{let o=localStorage.getItem(b);return o||(o=(n=(e=crypto==null?void 0:crypto.randomUUID)==null?void 0:e.call(crypto))!=null?n:Math.random().toString(36).slice(2)+Date.now().toString(36),localStorage.setItem(b,o)),o}catch(o){return Math.random().toString(36).slice(2)+Date.now().toString(36)}}function E(e){if(document.getElementById("dunefox-styles"))return;let n=e.startsWith("top"),t=e.endsWith("left")?"left:16px":"right:16px",s=`
2
2
  #dunefox-btn {
3
- position: fixed; ${t?"top:16px":"bottom:16px"}; ${d};
3
+ position: fixed; ${n?"top:16px":"bottom:16px"}; ${t};
4
4
  width: 60px; height: 60px; border-radius: 50%;
5
5
  background: #1a1a1a; border: none; cursor: pointer;
6
6
  display: flex; align-items: center; justify-content: center;
@@ -8,14 +8,14 @@ var g="https://app.dunefox.io",y="https://dunefoxx.s3.ap-south-1.amazonaws.com/c
8
8
  box-shadow: 0 4px 20px rgba(0,0,0,.25);
9
9
  transition: transform .25s ease;
10
10
  }
11
- #dunefox-btn:hover { transform: ${t?"translateY(2px) scale(1.05)":"translateY(-2px) scale(1.05)"}; }
11
+ #dunefox-btn:hover { transform: ${n?"translateY(2px) scale(1.05)":"translateY(-2px) scale(1.05)"}; }
12
12
  #dunefox-frame {
13
- position: fixed; ${t?"top:88px":"bottom:88px"}; ${d};
13
+ position: fixed; ${n?"top:88px":"bottom:88px"}; ${t};
14
14
  width: 350px; height: calc(100vh - 160px); max-height: 600px;
15
15
  border-radius: 16px; border: none; z-index: 2147483645;
16
16
  box-shadow: 0 8px 32px rgba(0,0,0,.12);
17
17
  opacity: 0; visibility: hidden;
18
- transform: ${t?"translateY(-24px) scale(.96)":"translateY(24px) scale(.96)"};
18
+ transform: ${n?"translateY(-24px) scale(.96)":"translateY(24px) scale(.96)"};
19
19
  transition: all .4s cubic-bezier(.4,0,.2,1);
20
20
  }
21
21
  #dunefox-frame.df-open {
@@ -26,15 +26,19 @@ var g="https://app.dunefox.io",y="https://dunefoxx.s3.ap-south-1.amazonaws.com/c
26
26
  body.df-chat-open #dunefox-btn {
27
27
  bottom: unset !important; top: 16px !important;
28
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;
29
33
  }
30
34
  #dunefox-frame {
31
35
  width: 100% !important; height: 100% !important; max-height: none !important;
32
36
  position: fixed !important; inset: 0 !important;
33
37
  border-radius: 0 !important;
34
- transform: ${t?"translateY(-100%)":"translateY(100%)"} !important;
38
+ transform: ${n?"translateY(-100%)":"translateY(100%)"} !important;
35
39
  }
36
40
  #dunefox-frame.df-open { transform: translateY(0) !important; }
37
41
  }
38
- `,o=document.createElement("style");o.id="dunefox-styles",o.textContent=c,document.head.appendChild(o);}var w='<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())(()=>{E(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=w,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;}
39
- export{L as close,O as destroy,I as init,S as open};//# sourceMappingURL=index.mjs.map
42
+ `,l=document.createElement("style");l.id="dunefox-styles",l.textContent=s,document.head.appendChild(l);}function I(e){if(document.getElementById("dunefox-badge"))return;let n=e.endsWith("left"),o=e.startsWith("top"),t=document.createElement("a");t.id="dunefox-badge",t.href="https://dunefox.io?ref=widget",t.target="_blank",t.rel="noopener",t.title="Dunefox \u2014 AI Chatbot for Websites",t.setAttribute("aria-label","Powered by Dunefox AI Chatbot"),t.textContent="Powered by Dunefox AI",t.style.cssText=["position:fixed",o?"top:8px":"bottom:6px",n?"left:80px":"right:80px","z-index:2147483644","font-size:10px",'font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',"font-weight:500","color:rgba(0,0,0,0.38)","text-decoration:none","letter-spacing:0.01em","white-space:nowrap","pointer-events:auto","transition:color 0.2s ease","line-height:1"].join(";"),t.addEventListener("mouseenter",()=>{t.style.color="rgba(235,108,51,0.85)";}),t.addEventListener("mouseleave",()=>{t.style.color="rgba(0,0,0,0.38)";}),document.body.appendChild(t);}var S='<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 L(e){let n=typeof e=="string"?{tenantId:e}:e;if(p){console.warn("[DunefoxChat] Already initialised. Call destroy() first.");return}let{tenantId:o,position:t="bottom-right",defaultOpen:m=false,baseUrl:f=y,iconUrl:u=v,hideBranding:x=false}=n;if(!o)throw new Error("[DunefoxChat] tenantId is required.");(s=>document.readyState==="loading"?document.addEventListener("DOMContentLoaded",s,{once:true}):s())(()=>{E(t);let s=w(),l=`${f}/api/${o}?uuid=${s}`,d=document.createElement("iframe");d.id="dunefox-frame",d.title="Dunefox Chat",d.loading="lazy",d.src=l;let i=document.createElement("button");i.id="dunefox-btn",i.setAttribute("aria-label","Open chat"),i.setAttribute("aria-expanded","false");let a=document.createElement("img");a.src=u,a.alt="",a.width=60,a.height=60,a.style.cssText="display:block;border-radius:50%;object-fit:cover;";let c=document.createElement("span");c.innerHTML=S,c.style.display="none",i.append(a,c);let r=m,h=()=>{d.classList.toggle("df-open",r),document.body.classList.toggle("df-chat-open",r),a.style.display=r?"none":"block",c.style.display=r?"block":"none",i.setAttribute("aria-expanded",String(r));};i.addEventListener("click",()=>{r=!r,h();}),document.body.append(d,i),h(),x||I(t),p=true;});}function B(){let e=document.getElementById("dunefox-frame"),n=document.getElementById("dunefox-btn");!e||!n||(e.classList.add("df-open"),n.setAttribute("aria-expanded","true"));}function C(){let e=document.getElementById("dunefox-frame"),n=document.getElementById("dunefox-btn");!e||!n||(e.classList.remove("df-open"),n.setAttribute("aria-expanded","false"));}function D(){var e,n,o,t;(e=document.getElementById("dunefox-frame"))==null||e.remove(),(n=document.getElementById("dunefox-btn"))==null||n.remove(),(o=document.getElementById("dunefox-styles"))==null||o.remove(),(t=document.getElementById("dunefox-badge"))==null||t.remove(),document.body.classList.remove("df-chat-open"),p=false;}
43
+ export{C as close,D as destroy,L as init,B as open};//# sourceMappingURL=index.mjs.map
40
44
  //# sourceMappingURL=index.mjs.map
@@ -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 /* 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 }\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"]}
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","injectBadge","isLeft","badge","CLOSE_SVG","_initialised","init","options","opts","tenantId","defaultOpen","baseUrl","iconUrl","hideBranding","cb","uuid","src","iframe","btn","img","closeSpan","open","applyState","close","destroy","_c","_d"],"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 * Hide the \"Powered by Dunefox\" branding badge.\r\n * Available on paid / white-label plans.\r\n */\r\n hideBranding?: boolean;\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// ─── Branding badge injector ──────────────────────────────────────────────────\r\nfunction injectBadge(position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'): void {\r\n if (document.getElementById('dunefox-badge')) return;\r\n\r\n const isLeft = position.endsWith('left');\r\n const isTop = position.startsWith('top');\r\n\r\n const badge = document.createElement('a');\r\n badge.id = 'dunefox-badge';\r\n badge.href = 'https://dunefox.io?ref=widget';\r\n badge.target = '_blank';\r\n badge.rel = 'noopener'; // noopener only — NO nofollow → passes link equity\r\n badge.title = 'Dunefox — AI Chatbot for Websites';\r\n badge.setAttribute('aria-label', 'Powered by Dunefox AI Chatbot');\r\n badge.textContent = 'Powered by Dunefox AI';\r\n\r\n badge.style.cssText = [\r\n 'position:fixed',\r\n isTop ? 'top:8px' : 'bottom:6px',\r\n isLeft ? 'left:80px' : 'right:80px',\r\n 'z-index:2147483644',\r\n 'font-size:10px',\r\n 'font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif',\r\n 'font-weight:500',\r\n 'color:rgba(0,0,0,0.38)',\r\n 'text-decoration:none',\r\n 'letter-spacing:0.01em',\r\n 'white-space:nowrap',\r\n 'pointer-events:auto',\r\n 'transition:color 0.2s ease',\r\n 'line-height:1',\r\n ].join(';');\r\n\r\n badge.addEventListener('mouseenter', () => { badge.style.color = 'rgba(235,108,51,0.85)'; });\r\n badge.addEventListener('mouseleave', () => { badge.style.color = 'rgba(0,0,0,0.38)'; });\r\n\r\n document.body.appendChild(badge);\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 hideBranding = false,\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\r\n // Inject SEO backlink badge on free/default plans\r\n if (!hideBranding) {\r\n injectBadge(position);\r\n }\r\n\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.getElementById('dunefox-badge')?.remove();\r\n document.body.classList.remove('df-chat-open');\r\n _initialised = false;\r\n}\r\n"]}
package/dist/react.d.mts CHANGED
@@ -13,6 +13,11 @@ interface DunefoxChatOptions {
13
13
  baseUrl?: string;
14
14
  /** Icon image URL override. Falls back to Dunefox CDN asset. */
15
15
  iconUrl?: string;
16
+ /**
17
+ * Hide the "Powered by Dunefox" branding badge.
18
+ * Available on paid / white-label plans.
19
+ */
20
+ hideBranding?: boolean;
16
21
  }
17
22
 
18
23
  interface DunefoxChatbotProps extends DunefoxChatOptions {
package/dist/react.d.ts CHANGED
@@ -13,6 +13,11 @@ interface DunefoxChatOptions {
13
13
  baseUrl?: string;
14
14
  /** Icon image URL override. Falls back to Dunefox CDN asset. */
15
15
  iconUrl?: string;
16
+ /**
17
+ * Hide the "Powered by Dunefox" branding badge.
18
+ * Available on paid / white-label plans.
19
+ */
20
+ hideBranding?: boolean;
16
21
  }
17
22
 
18
23
  interface DunefoxChatbotProps extends DunefoxChatOptions {
package/dist/react.js CHANGED
@@ -1,6 +1,6 @@
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",c=`
1
+ 'use strict';var react=require('react');var w="https://app.dunefox.io",E="https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png",b="dunefox_chat_uuid";function I(){var e,n;try{let o=localStorage.getItem(b);return o||(o=(n=(e=crypto==null?void 0:crypto.randomUUID)==null?void 0:e.call(crypto))!=null?n:Math.random().toString(36).slice(2)+Date.now().toString(36),localStorage.setItem(b,o)),o}catch(o){return Math.random().toString(36).slice(2)+Date.now().toString(36)}}function C(e){if(document.getElementById("dunefox-styles"))return;let n=e.startsWith("top"),t=e.endsWith("left")?"left:16px":"right:16px",s=`
2
2
  #dunefox-btn {
3
- position: fixed; ${t?"top:16px":"bottom:16px"}; ${d};
3
+ position: fixed; ${n?"top:16px":"bottom:16px"}; ${t};
4
4
  width: 60px; height: 60px; border-radius: 50%;
5
5
  background: #1a1a1a; border: none; cursor: pointer;
6
6
  display: flex; align-items: center; justify-content: center;
@@ -8,14 +8,14 @@
8
8
  box-shadow: 0 4px 20px rgba(0,0,0,.25);
9
9
  transition: transform .25s ease;
10
10
  }
11
- #dunefox-btn:hover { transform: ${t?"translateY(2px) scale(1.05)":"translateY(-2px) scale(1.05)"}; }
11
+ #dunefox-btn:hover { transform: ${n?"translateY(2px) scale(1.05)":"translateY(-2px) scale(1.05)"}; }
12
12
  #dunefox-frame {
13
- position: fixed; ${t?"top:88px":"bottom:88px"}; ${d};
13
+ position: fixed; ${n?"top:88px":"bottom:88px"}; ${t};
14
14
  width: 350px; height: calc(100vh - 160px); max-height: 600px;
15
15
  border-radius: 16px; border: none; z-index: 2147483645;
16
16
  box-shadow: 0 8px 32px rgba(0,0,0,.12);
17
17
  opacity: 0; visibility: hidden;
18
- transform: ${t?"translateY(-24px) scale(.96)":"translateY(24px) scale(.96)"};
18
+ transform: ${n?"translateY(-24px) scale(.96)":"translateY(24px) scale(.96)"};
19
19
  transition: all .4s cubic-bezier(.4,0,.2,1);
20
20
  }
21
21
  #dunefox-frame.df-open {
@@ -26,15 +26,19 @@
26
26
  body.df-chat-open #dunefox-btn {
27
27
  bottom: unset !important; top: 16px !important;
28
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;
29
33
  }
30
34
  #dunefox-frame {
31
35
  width: 100% !important; height: 100% !important; max-height: none !important;
32
36
  position: fixed !important; inset: 0 !important;
33
37
  border-radius: 0 !important;
34
- transform: ${t?"translateY(-100%)":"translateY(100%)"} !important;
38
+ transform: ${n?"translateY(-100%)":"translateY(100%)"} !important;
35
39
  }
36
40
  #dunefox-frame.df-open { transform: translateY(0) !important; }
37
41
  }
38
- `,o=document.createElement("style");o.id="dunefox-styles",o.textContent=c,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>',p=false;function b(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:u=false,baseUrl:f=v,iconUrl:m=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(),c=`${f}/api/${n}?uuid=${s}`,o=document.createElement("iframe");o.id="dunefox-frame",o.title="Dunefox Chat",o.loading="lazy",o.src=c;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=m,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(),p=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"),p=false;}function U(e){let t=react.useRef(e);return react.useEffect(()=>{if(typeof window!="undefined")return b(t.current),()=>{g();}},[]),null}
39
- exports.DunefoxChatbot=U;//# sourceMappingURL=react.js.map
42
+ `,l=document.createElement("style");l.id="dunefox-styles",l.textContent=s,document.head.appendChild(l);}function D(e){if(document.getElementById("dunefox-badge"))return;let n=e.endsWith("left"),o=e.startsWith("top"),t=document.createElement("a");t.id="dunefox-badge",t.href="https://dunefox.io?ref=widget",t.target="_blank",t.rel="noopener",t.title="Dunefox \u2014 AI Chatbot for Websites",t.setAttribute("aria-label","Powered by Dunefox AI Chatbot"),t.textContent="Powered by Dunefox AI",t.style.cssText=["position:fixed",o?"top:8px":"bottom:6px",n?"left:80px":"right:80px","z-index:2147483644","font-size:10px",'font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',"font-weight:500","color:rgba(0,0,0,0.38)","text-decoration:none","letter-spacing:0.01em","white-space:nowrap","pointer-events:auto","transition:color 0.2s ease","line-height:1"].join(";"),t.addEventListener("mouseenter",()=>{t.style.color="rgba(235,108,51,0.85)";}),t.addEventListener("mouseleave",()=>{t.style.color="rgba(0,0,0,0.38)";}),document.body.appendChild(t);}var S='<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 g(e){let n=typeof e=="string"?{tenantId:e}:e;if(c){console.warn("[DunefoxChat] Already initialised. Call destroy() first.");return}let{tenantId:o,position:t="bottom-right",defaultOpen:f=false,baseUrl:u=w,iconUrl:m=E,hideBranding:x=false}=n;if(!o)throw new Error("[DunefoxChat] tenantId is required.");(s=>document.readyState==="loading"?document.addEventListener("DOMContentLoaded",s,{once:true}):s())(()=>{C(t);let s=I(),l=`${u}/api/${o}?uuid=${s}`,d=document.createElement("iframe");d.id="dunefox-frame",d.title="Dunefox Chat",d.loading="lazy",d.src=l;let i=document.createElement("button");i.id="dunefox-btn",i.setAttribute("aria-label","Open chat"),i.setAttribute("aria-expanded","false");let r=document.createElement("img");r.src=m,r.alt="",r.width=60,r.height=60,r.style.cssText="display:block;border-radius:50%;object-fit:cover;";let p=document.createElement("span");p.innerHTML=S,p.style.display="none",i.append(r,p);let a=f,h=()=>{d.classList.toggle("df-open",a),document.body.classList.toggle("df-chat-open",a),r.style.display=a?"none":"block",p.style.display=a?"block":"none",i.setAttribute("aria-expanded",String(a));};i.addEventListener("click",()=>{a=!a,h();}),document.body.append(d,i),h(),x||D(t),c=true;});}function y(){var e,n,o,t;(e=document.getElementById("dunefox-frame"))==null||e.remove(),(n=document.getElementById("dunefox-btn"))==null||n.remove(),(o=document.getElementById("dunefox-styles"))==null||o.remove(),(t=document.getElementById("dunefox-badge"))==null||t.remove(),document.body.classList.remove("df-chat-open"),c=false;}function k(e){let n=react.useRef(e);return react.useEffect(()=>{if(typeof window!="undefined")return g(n.current),()=>{y();}},[]),null}
43
+ exports.DunefoxChatbot=k;//# sourceMappingURL=react.js.map
40
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 /* 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 }\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"]}
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","injectBadge","isLeft","badge","CLOSE_SVG","_initialised","init","options","opts","tenantId","defaultOpen","baseUrl","iconUrl","hideBranding","cb","uuid","src","iframe","btn","img","closeSpan","open","applyState","destroy","_c","_d","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 * Hide the \"Powered by Dunefox\" branding badge.\r\n * Available on paid / white-label plans.\r\n */\r\n hideBranding?: boolean;\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// ─── Branding badge injector ──────────────────────────────────────────────────\r\nfunction injectBadge(position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'): void {\r\n if (document.getElementById('dunefox-badge')) return;\r\n\r\n const isLeft = position.endsWith('left');\r\n const isTop = position.startsWith('top');\r\n\r\n const badge = document.createElement('a');\r\n badge.id = 'dunefox-badge';\r\n badge.href = 'https://dunefox.io?ref=widget';\r\n badge.target = '_blank';\r\n badge.rel = 'noopener'; // noopener only — NO nofollow → passes link equity\r\n badge.title = 'Dunefox — AI Chatbot for Websites';\r\n badge.setAttribute('aria-label', 'Powered by Dunefox AI Chatbot');\r\n badge.textContent = 'Powered by Dunefox AI';\r\n\r\n badge.style.cssText = [\r\n 'position:fixed',\r\n isTop ? 'top:8px' : 'bottom:6px',\r\n isLeft ? 'left:80px' : 'right:80px',\r\n 'z-index:2147483644',\r\n 'font-size:10px',\r\n 'font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif',\r\n 'font-weight:500',\r\n 'color:rgba(0,0,0,0.38)',\r\n 'text-decoration:none',\r\n 'letter-spacing:0.01em',\r\n 'white-space:nowrap',\r\n 'pointer-events:auto',\r\n 'transition:color 0.2s ease',\r\n 'line-height:1',\r\n ].join(';');\r\n\r\n badge.addEventListener('mouseenter', () => { badge.style.color = 'rgba(235,108,51,0.85)'; });\r\n badge.addEventListener('mouseleave', () => { badge.style.color = 'rgba(0,0,0,0.38)'; });\r\n\r\n document.body.appendChild(badge);\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 hideBranding = false,\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\r\n // Inject SEO backlink badge on free/default plans\r\n if (!hideBranding) {\r\n injectBadge(position);\r\n }\r\n\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.getElementById('dunefox-badge')?.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,6 +1,6 @@
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",c=`
1
+ import {useRef,useEffect}from'react';var w="https://app.dunefox.io",E="https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png",b="dunefox_chat_uuid";function I(){var e,n;try{let o=localStorage.getItem(b);return o||(o=(n=(e=crypto==null?void 0:crypto.randomUUID)==null?void 0:e.call(crypto))!=null?n:Math.random().toString(36).slice(2)+Date.now().toString(36),localStorage.setItem(b,o)),o}catch(o){return Math.random().toString(36).slice(2)+Date.now().toString(36)}}function C(e){if(document.getElementById("dunefox-styles"))return;let n=e.startsWith("top"),t=e.endsWith("left")?"left:16px":"right:16px",s=`
2
2
  #dunefox-btn {
3
- position: fixed; ${t?"top:16px":"bottom:16px"}; ${d};
3
+ position: fixed; ${n?"top:16px":"bottom:16px"}; ${t};
4
4
  width: 60px; height: 60px; border-radius: 50%;
5
5
  background: #1a1a1a; border: none; cursor: pointer;
6
6
  display: flex; align-items: center; justify-content: center;
@@ -8,14 +8,14 @@ import {useRef,useEffect}from'react';var v="https://app.dunefox.io",w="https://d
8
8
  box-shadow: 0 4px 20px rgba(0,0,0,.25);
9
9
  transition: transform .25s ease;
10
10
  }
11
- #dunefox-btn:hover { transform: ${t?"translateY(2px) scale(1.05)":"translateY(-2px) scale(1.05)"}; }
11
+ #dunefox-btn:hover { transform: ${n?"translateY(2px) scale(1.05)":"translateY(-2px) scale(1.05)"}; }
12
12
  #dunefox-frame {
13
- position: fixed; ${t?"top:88px":"bottom:88px"}; ${d};
13
+ position: fixed; ${n?"top:88px":"bottom:88px"}; ${t};
14
14
  width: 350px; height: calc(100vh - 160px); max-height: 600px;
15
15
  border-radius: 16px; border: none; z-index: 2147483645;
16
16
  box-shadow: 0 8px 32px rgba(0,0,0,.12);
17
17
  opacity: 0; visibility: hidden;
18
- transform: ${t?"translateY(-24px) scale(.96)":"translateY(24px) scale(.96)"};
18
+ transform: ${n?"translateY(-24px) scale(.96)":"translateY(24px) scale(.96)"};
19
19
  transition: all .4s cubic-bezier(.4,0,.2,1);
20
20
  }
21
21
  #dunefox-frame.df-open {
@@ -26,15 +26,19 @@ import {useRef,useEffect}from'react';var v="https://app.dunefox.io",w="https://d
26
26
  body.df-chat-open #dunefox-btn {
27
27
  bottom: unset !important; top: 16px !important;
28
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;
29
33
  }
30
34
  #dunefox-frame {
31
35
  width: 100% !important; height: 100% !important; max-height: none !important;
32
36
  position: fixed !important; inset: 0 !important;
33
37
  border-radius: 0 !important;
34
- transform: ${t?"translateY(-100%)":"translateY(100%)"} !important;
38
+ transform: ${n?"translateY(-100%)":"translateY(100%)"} !important;
35
39
  }
36
40
  #dunefox-frame.df-open { transform: translateY(0) !important; }
37
41
  }
38
- `,o=document.createElement("style");o.id="dunefox-styles",o.textContent=c,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>',p=false;function b(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:u=false,baseUrl:f=v,iconUrl:m=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(),c=`${f}/api/${n}?uuid=${s}`,o=document.createElement("iframe");o.id="dunefox-frame",o.title="Dunefox Chat",o.loading="lazy",o.src=c;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=m,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(),p=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"),p=false;}function U(e){let t=useRef(e);return useEffect(()=>{if(typeof window!="undefined")return b(t.current),()=>{g();}},[]),null}
39
- export{U as DunefoxChatbot};//# sourceMappingURL=react.mjs.map
42
+ `,l=document.createElement("style");l.id="dunefox-styles",l.textContent=s,document.head.appendChild(l);}function D(e){if(document.getElementById("dunefox-badge"))return;let n=e.endsWith("left"),o=e.startsWith("top"),t=document.createElement("a");t.id="dunefox-badge",t.href="https://dunefox.io?ref=widget",t.target="_blank",t.rel="noopener",t.title="Dunefox \u2014 AI Chatbot for Websites",t.setAttribute("aria-label","Powered by Dunefox AI Chatbot"),t.textContent="Powered by Dunefox AI",t.style.cssText=["position:fixed",o?"top:8px":"bottom:6px",n?"left:80px":"right:80px","z-index:2147483644","font-size:10px",'font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',"font-weight:500","color:rgba(0,0,0,0.38)","text-decoration:none","letter-spacing:0.01em","white-space:nowrap","pointer-events:auto","transition:color 0.2s ease","line-height:1"].join(";"),t.addEventListener("mouseenter",()=>{t.style.color="rgba(235,108,51,0.85)";}),t.addEventListener("mouseleave",()=>{t.style.color="rgba(0,0,0,0.38)";}),document.body.appendChild(t);}var S='<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 g(e){let n=typeof e=="string"?{tenantId:e}:e;if(c){console.warn("[DunefoxChat] Already initialised. Call destroy() first.");return}let{tenantId:o,position:t="bottom-right",defaultOpen:f=false,baseUrl:u=w,iconUrl:m=E,hideBranding:x=false}=n;if(!o)throw new Error("[DunefoxChat] tenantId is required.");(s=>document.readyState==="loading"?document.addEventListener("DOMContentLoaded",s,{once:true}):s())(()=>{C(t);let s=I(),l=`${u}/api/${o}?uuid=${s}`,d=document.createElement("iframe");d.id="dunefox-frame",d.title="Dunefox Chat",d.loading="lazy",d.src=l;let i=document.createElement("button");i.id="dunefox-btn",i.setAttribute("aria-label","Open chat"),i.setAttribute("aria-expanded","false");let r=document.createElement("img");r.src=m,r.alt="",r.width=60,r.height=60,r.style.cssText="display:block;border-radius:50%;object-fit:cover;";let p=document.createElement("span");p.innerHTML=S,p.style.display="none",i.append(r,p);let a=f,h=()=>{d.classList.toggle("df-open",a),document.body.classList.toggle("df-chat-open",a),r.style.display=a?"none":"block",p.style.display=a?"block":"none",i.setAttribute("aria-expanded",String(a));};i.addEventListener("click",()=>{a=!a,h();}),document.body.append(d,i),h(),x||D(t),c=true;});}function y(){var e,n,o,t;(e=document.getElementById("dunefox-frame"))==null||e.remove(),(n=document.getElementById("dunefox-btn"))==null||n.remove(),(o=document.getElementById("dunefox-styles"))==null||o.remove(),(t=document.getElementById("dunefox-badge"))==null||t.remove(),document.body.classList.remove("df-chat-open"),c=false;}function k(e){let n=useRef(e);return useEffect(()=>{if(typeof window!="undefined")return g(n.current),()=>{y();}},[]),null}
43
+ export{k as DunefoxChatbot};//# sourceMappingURL=react.mjs.map
40
44
  //# sourceMappingURL=react.mjs.map
@@ -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 /* 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 }\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"]}
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","injectBadge","isLeft","badge","CLOSE_SVG","_initialised","init","options","opts","tenantId","defaultOpen","baseUrl","iconUrl","hideBranding","cb","uuid","src","iframe","btn","img","closeSpan","open","applyState","destroy","_c","_d","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 * Hide the \"Powered by Dunefox\" branding badge.\r\n * Available on paid / white-label plans.\r\n */\r\n hideBranding?: boolean;\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// ─── Branding badge injector ──────────────────────────────────────────────────\r\nfunction injectBadge(position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'): void {\r\n if (document.getElementById('dunefox-badge')) return;\r\n\r\n const isLeft = position.endsWith('left');\r\n const isTop = position.startsWith('top');\r\n\r\n const badge = document.createElement('a');\r\n badge.id = 'dunefox-badge';\r\n badge.href = 'https://dunefox.io?ref=widget';\r\n badge.target = '_blank';\r\n badge.rel = 'noopener'; // noopener only — NO nofollow → passes link equity\r\n badge.title = 'Dunefox — AI Chatbot for Websites';\r\n badge.setAttribute('aria-label', 'Powered by Dunefox AI Chatbot');\r\n badge.textContent = 'Powered by Dunefox AI';\r\n\r\n badge.style.cssText = [\r\n 'position:fixed',\r\n isTop ? 'top:8px' : 'bottom:6px',\r\n isLeft ? 'left:80px' : 'right:80px',\r\n 'z-index:2147483644',\r\n 'font-size:10px',\r\n 'font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif',\r\n 'font-weight:500',\r\n 'color:rgba(0,0,0,0.38)',\r\n 'text-decoration:none',\r\n 'letter-spacing:0.01em',\r\n 'white-space:nowrap',\r\n 'pointer-events:auto',\r\n 'transition:color 0.2s ease',\r\n 'line-height:1',\r\n ].join(';');\r\n\r\n badge.addEventListener('mouseenter', () => { badge.style.color = 'rgba(235,108,51,0.85)'; });\r\n badge.addEventListener('mouseleave', () => { badge.style.color = 'rgba(0,0,0,0.38)'; });\r\n\r\n document.body.appendChild(badge);\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 hideBranding = false,\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\r\n // Inject SEO backlink badge on free/default plans\r\n if (!hideBranding) {\r\n injectBadge(position);\r\n }\r\n\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.getElementById('dunefox-badge')?.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dunefox-chatbot",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Official Dunefox chatbot widget — add AI chat to any website in seconds",
5
5
  "keywords": [
6
6
  "dunefox",