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 +88 -18
- package/dist/chatbot.umd.global.js +8 -4
- package/dist/index.d.mts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +12 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +12 -8
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.mts +5 -0
- package/dist/react.d.ts +5 -0
- package/dist/react.js +12 -8
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +12 -8
- package/dist/react.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
# dunefox-chatbot
|
|
2
2
|
|
|
3
|
-
|
|
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
|
[](https://www.npmjs.com/package/dunefox-chatbot)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
|
-
[](https://dunefox.io)
|
|
8
|
+
[](https://app.dunefox.io)
|
|
8
9
|
|
|
9
|
-
**[Dunefox](https://dunefox.io)** is an AI-powered customer support platform
|
|
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
|
|
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
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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"}; ${
|
|
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"}; ${
|
|
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
|
-
`,
|
|
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
|
|
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; ${
|
|
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: ${
|
|
11
|
+
#dunefox-btn:hover { transform: ${n?"translateY(2px) scale(1.05)":"translateY(-2px) scale(1.05)"}; }
|
|
12
12
|
#dunefox-frame {
|
|
13
|
-
position: fixed; ${
|
|
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: ${
|
|
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: ${
|
|
38
|
+
transform: ${n?"translateY(-100%)":"translateY(100%)"} !important;
|
|
35
39
|
}
|
|
36
40
|
#dunefox-frame.df-open { transform: translateY(0) !important; }
|
|
37
41
|
}
|
|
38
|
-
`,
|
|
39
|
-
exports.close=
|
|
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":"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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 * 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
|
|
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; ${
|
|
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: ${
|
|
11
|
+
#dunefox-btn:hover { transform: ${n?"translateY(2px) scale(1.05)":"translateY(-2px) scale(1.05)"}; }
|
|
12
12
|
#dunefox-frame {
|
|
13
|
-
position: fixed; ${
|
|
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: ${
|
|
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: ${
|
|
38
|
+
transform: ${n?"translateY(-100%)":"translateY(100%)"} !important;
|
|
35
39
|
}
|
|
36
40
|
#dunefox-frame.df-open { transform: translateY(0) !important; }
|
|
37
41
|
}
|
|
38
|
-
`,
|
|
39
|
-
export{
|
|
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
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core.ts"],"names":["DEFAULT_BASE","DEFAULT_ICON","UUID_KEY","getOrCreateUUID","_a","_b","id","e","injectStyles","position","isTop","hSide","css","tag","CLOSE_SVG","_initialised","init","options","opts","tenantId","defaultOpen","baseUrl","iconUrl","cb","uuid","src","iframe","btn","img","closeSpan","open","applyState","close","destroy","_c"],"mappings":"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","file":"index.mjs","sourcesContent":["/** Options accepted by DunefoxChat.init() */\r\nexport interface DunefoxChatOptions {\r\n /** Your Dunefox Tenant ID — found in Settings > Install Widget */\r\n tenantId: string;\r\n /**\r\n * Where to anchor the widget. Defaults to \"bottom-right\".\r\n * Supports: \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\"\r\n */\r\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\r\n /** Open the chat panel on load. Defaults to false. */\r\n defaultOpen?: boolean;\r\n /** Override the base URL (for self-hosted / staging). */\r\n baseUrl?: string;\r\n /** Icon image URL override. Falls back to Dunefox CDN asset. */\r\n iconUrl?: string;\r\n}\r\n\r\nconst DEFAULT_BASE = 'https://app.dunefox.io';\r\nconst DEFAULT_ICON = 'https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png';\r\nconst UUID_KEY = 'dunefox_chat_uuid';\r\n\r\n// ─── UUID generation ──────────────────────────────────────────────────────────\r\nfunction getOrCreateUUID(): string {\r\n try {\r\n let id = localStorage.getItem(UUID_KEY);\r\n if (!id) {\r\n id = crypto?.randomUUID?.()\r\n ?? Math.random().toString(36).slice(2) + Date.now().toString(36);\r\n localStorage.setItem(UUID_KEY, id);\r\n }\r\n return id;\r\n } catch {\r\n // Incognito / storage blocked — generate ephemeral ID\r\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\r\n }\r\n}\r\n\r\n// ─── CSS injector ─────────────────────────────────────────────────────────────\r\nfunction injectStyles(position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'): void {\r\n if (document.getElementById('dunefox-styles')) return;\r\n\r\n const isTop = position.startsWith('top');\r\n const isLeft = position.endsWith('left');\r\n const hSide = isLeft ? 'left:16px' : 'right:16px';\r\n const vBtn = isTop ? 'top:16px' : 'bottom:16px';\r\n // Panel opens away from the button edge\r\n const vFrame = isTop ? 'top:88px' : 'bottom:88px';\r\n // Slide direction: panels below slide up, panels above slide down\r\n const slideOut = isTop ? 'translateY(-24px) scale(.96)' : 'translateY(24px) scale(.96)';\r\n // Hover nudge direction\r\n const hoverTranslate = isTop ? 'translateY(2px) scale(1.05)' : 'translateY(-2px) scale(1.05)';\r\n // Mobile slide direction\r\n const mobileSlideOut = isTop ? 'translateY(-100%)' : 'translateY(100%)';\r\n\r\n const css = `\r\n #dunefox-btn {\r\n position: fixed; ${vBtn}; ${hSide};\r\n width: 60px; height: 60px; border-radius: 50%;\r\n background: #1a1a1a; border: none; cursor: pointer;\r\n display: flex; align-items: center; justify-content: center;\r\n z-index: 2147483646;\r\n box-shadow: 0 4px 20px rgba(0,0,0,.25);\r\n transition: transform .25s ease;\r\n }\r\n #dunefox-btn:hover { transform: ${hoverTranslate}; }\r\n #dunefox-frame {\r\n position: fixed; ${vFrame}; ${hSide};\r\n width: 350px; height: calc(100vh - 160px); max-height: 600px;\r\n border-radius: 16px; border: none; z-index: 2147483645;\r\n box-shadow: 0 8px 32px rgba(0,0,0,.12);\r\n opacity: 0; visibility: hidden;\r\n transform: ${slideOut};\r\n transition: all .4s cubic-bezier(.4,0,.2,1);\r\n }\r\n #dunefox-frame.df-open {\r\n opacity: 1; visibility: visible; transform: translateY(0) scale(1);\r\n }\r\n @media (max-width: 768px) {\r\n /* 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
|
|
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; ${
|
|
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: ${
|
|
11
|
+
#dunefox-btn:hover { transform: ${n?"translateY(2px) scale(1.05)":"translateY(-2px) scale(1.05)"}; }
|
|
12
12
|
#dunefox-frame {
|
|
13
|
-
position: fixed; ${
|
|
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: ${
|
|
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: ${
|
|
38
|
+
transform: ${n?"translateY(-100%)":"translateY(100%)"} !important;
|
|
35
39
|
}
|
|
36
40
|
#dunefox-frame.df-open { transform: translateY(0) !important; }
|
|
37
41
|
}
|
|
38
|
-
`,
|
|
39
|
-
exports.DunefoxChatbot=
|
|
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
|
|
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; ${
|
|
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: ${
|
|
11
|
+
#dunefox-btn:hover { transform: ${n?"translateY(2px) scale(1.05)":"translateY(-2px) scale(1.05)"}; }
|
|
12
12
|
#dunefox-frame {
|
|
13
|
-
position: fixed; ${
|
|
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: ${
|
|
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: ${
|
|
38
|
+
transform: ${n?"translateY(-100%)":"translateY(100%)"} !important;
|
|
35
39
|
}
|
|
36
40
|
#dunefox-frame.df-open { transform: translateY(0) !important; }
|
|
37
41
|
}
|
|
38
|
-
`,
|
|
39
|
-
export{
|
|
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
|
package/dist/react.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core.ts","../src/react.tsx"],"names":["DEFAULT_BASE","DEFAULT_ICON","UUID_KEY","getOrCreateUUID","_a","_b","id","e","injectStyles","position","isTop","hSide","css","tag","CLOSE_SVG","_initialised","init","options","opts","tenantId","defaultOpen","baseUrl","iconUrl","cb","uuid","src","iframe","btn","img","closeSpan","open","applyState","destroy","_c","DunefoxChatbot","props","optsRef","useRef","useEffect"],"mappings":"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","file":"react.mjs","sourcesContent":["/** Options accepted by DunefoxChat.init() */\r\nexport interface DunefoxChatOptions {\r\n /** Your Dunefox Tenant ID — found in Settings > Install Widget */\r\n tenantId: string;\r\n /**\r\n * Where to anchor the widget. Defaults to \"bottom-right\".\r\n * Supports: \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\"\r\n */\r\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\r\n /** Open the chat panel on load. Defaults to false. */\r\n defaultOpen?: boolean;\r\n /** Override the base URL (for self-hosted / staging). */\r\n baseUrl?: string;\r\n /** Icon image URL override. Falls back to Dunefox CDN asset. */\r\n iconUrl?: string;\r\n}\r\n\r\nconst DEFAULT_BASE = 'https://app.dunefox.io';\r\nconst DEFAULT_ICON = 'https://dunefoxx.s3.ap-south-1.amazonaws.com/chatbot.png';\r\nconst UUID_KEY = 'dunefox_chat_uuid';\r\n\r\n// ─── UUID generation ──────────────────────────────────────────────────────────\r\nfunction getOrCreateUUID(): string {\r\n try {\r\n let id = localStorage.getItem(UUID_KEY);\r\n if (!id) {\r\n id = crypto?.randomUUID?.()\r\n ?? Math.random().toString(36).slice(2) + Date.now().toString(36);\r\n localStorage.setItem(UUID_KEY, id);\r\n }\r\n return id;\r\n } catch {\r\n // Incognito / storage blocked — generate ephemeral ID\r\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\r\n }\r\n}\r\n\r\n// ─── CSS injector ─────────────────────────────────────────────────────────────\r\nfunction injectStyles(position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'): void {\r\n if (document.getElementById('dunefox-styles')) return;\r\n\r\n const isTop = position.startsWith('top');\r\n const isLeft = position.endsWith('left');\r\n const hSide = isLeft ? 'left:16px' : 'right:16px';\r\n const vBtn = isTop ? 'top:16px' : 'bottom:16px';\r\n // Panel opens away from the button edge\r\n const vFrame = isTop ? 'top:88px' : 'bottom:88px';\r\n // Slide direction: panels below slide up, panels above slide down\r\n const slideOut = isTop ? 'translateY(-24px) scale(.96)' : 'translateY(24px) scale(.96)';\r\n // Hover nudge direction\r\n const hoverTranslate = isTop ? 'translateY(2px) scale(1.05)' : 'translateY(-2px) scale(1.05)';\r\n // Mobile slide direction\r\n const mobileSlideOut = isTop ? 'translateY(-100%)' : 'translateY(100%)';\r\n\r\n const css = `\r\n #dunefox-btn {\r\n position: fixed; ${vBtn}; ${hSide};\r\n width: 60px; height: 60px; border-radius: 50%;\r\n background: #1a1a1a; border: none; cursor: pointer;\r\n display: flex; align-items: center; justify-content: center;\r\n z-index: 2147483646;\r\n box-shadow: 0 4px 20px rgba(0,0,0,.25);\r\n transition: transform .25s ease;\r\n }\r\n #dunefox-btn:hover { transform: ${hoverTranslate}; }\r\n #dunefox-frame {\r\n position: fixed; ${vFrame}; ${hSide};\r\n width: 350px; height: calc(100vh - 160px); max-height: 600px;\r\n border-radius: 16px; border: none; z-index: 2147483645;\r\n box-shadow: 0 8px 32px rgba(0,0,0,.12);\r\n opacity: 0; visibility: hidden;\r\n transform: ${slideOut};\r\n transition: all .4s cubic-bezier(.4,0,.2,1);\r\n }\r\n #dunefox-frame.df-open {\r\n opacity: 1; visibility: visible; transform: translateY(0) scale(1);\r\n }\r\n @media (max-width: 768px) {\r\n /* 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"]}
|