@bliptarjs/sdk 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,19 +1,44 @@
1
1
  # @bliptarjs/sdk
2
2
 
3
- Lightweight SDK for collecting in-app user feedback. Designed to be small (~7KB gzipped) and non-intrusive.
3
+ **Bliptar** Micro-feedback that users actually respond to.
4
4
 
5
- ## Installation
5
+ ## The Problem
6
6
 
7
- ```bash
8
- npm install @bliptarjs/sdk
9
- ```
7
+ Users abandon your app, churn from your service, and leave features unused – without ever telling you why. Traditional surveys are too long. Pop-ups are annoying. You're left guessing.
10
8
 
11
- ```bash
12
- yarn add @bliptarjs/sdk
13
- ```
9
+ ## The Solution
10
+
11
+ Bliptar captures real-time user sentiment with **nano feedback forms** – quick 1-2 question surveys that appear at exactly the right moment. After checkout, on exit intent, when they've scrolled 80% of your pricing page, or 30 seconds into using a new feature.
12
+
13
+ **One question. Two seconds. Real insights.**
14
+
15
+ ### Form Types
16
+ - **Thumbs up/down** – "Was this helpful?"
17
+ - **Emoji scales** – "How was your experience?"
18
+ - **Star ratings** – "Rate this feature"
19
+ - **NPS** – "How likely are you to recommend us?"
20
+ - **Single choice** – Quick multiple choice
21
+ - **Text input** – Open-ended feedback
22
+
23
+ ### Smart Triggers
24
+ - **Page view** – When user lands on a page
25
+ - **Scroll depth** – When user scrolls to X%
26
+ - **Time on page** – After X seconds
27
+ - **Exit intent** – When user is about to leave
28
+ - **Custom events** – After checkout, signup, or any action
29
+
30
+ ### Why Bliptar?
31
+ - **Dashboard controlled** – No code changes to update campaigns
32
+ - **Lightweight SDK** – ~8KB gzipped, zero dependencies
33
+ - **Works everywhere** – React, Next.js, Vue, or vanilla JS
34
+ - **Privacy first** – No cookies, GDPR friendly
35
+
36
+ **Stop guessing how users feel. Start knowing.**
37
+
38
+ ## Installation
14
39
 
15
40
  ```bash
16
- pnpm add @bliptarjs/sdk
41
+ npm install @bliptarjs/sdk
17
42
  ```
18
43
 
19
44
  ## Quick Start
@@ -28,395 +53,148 @@ const bliptar = createBliptar({
28
53
  bliptar.init();
29
54
  ```
30
55
 
31
- That's it! The SDK will automatically track page views and display forms based on your campaign rules configured in the Bliptar dashboard.
32
-
33
- ## Core Concepts
34
-
35
- Bliptar uses **campaigns** to determine when to show feedback forms. Campaigns can be triggered by:
36
-
37
- - **Page View** - When a user visits a page
38
- - **Button Click** - When a user clicks a specific element
39
- - **Scroll Depth** - When a user scrolls to a certain percentage
40
- - **Time on Page** - After a user spends time on a page
41
- - **Exit Intent** - When a user is about to leave
42
- - **Custom Events** - Triggered programmatically via SDK
43
-
44
- ## API Reference
56
+ That's it! The SDK automatically:
57
+ - Fetches your active campaigns from the dashboard
58
+ - Sets up the appropriate triggers (page view, scroll depth, time on page, exit intent)
59
+ - Shows feedback forms when conditions are met
60
+ - Submits responses to your Bliptar dashboard
45
61
 
46
- ### `createBliptar(config)`
62
+ ## How It Works
47
63
 
48
- Creates a new Bliptar instance.
64
+ 1. **Create campaigns** in the [Bliptar Dashboard](https://app.bliptar.com)
65
+ 2. **Configure triggers** - when should the form appear?
66
+ - Page View - when user visits a page
67
+ - Scroll Depth - when user scrolls to X%
68
+ - Time on Page - after X seconds
69
+ - Exit Intent - when user is about to leave
70
+ - Custom Event - triggered via `bliptar.track()`
71
+ 3. **Add the SDK** to your app with your API key
72
+ 4. **Done** - forms appear automatically based on your campaign rules
49
73
 
50
- ```typescript
51
- import { createBliptar } from '@bliptarjs/sdk';
74
+ ## Configuration
52
75
 
76
+ ```javascript
53
77
  const bliptar = createBliptar({
54
- apiKey: 'your-api-key',
55
- debug: false, // optional
56
- userId: 'user-123', // optional
57
- userAttributes: { plan: 'pro' }, // optional
58
- onShow: (form) => console.log('Showing form:', form.formId),
59
- onSubmit: (answers) => console.log('Submitted:', answers),
60
- onDismiss: () => console.log('Form dismissed'),
61
- onError: (error) => console.error('Error:', error),
78
+ apiKey: 'your-api-key', // Required
79
+ debug: false, // Enable console logging
80
+ userId: 'user-123', // Pre-set user ID
81
+ userAttributes: { // User attributes for targeting
82
+ plan: 'pro',
83
+ company: 'Acme',
84
+ },
85
+ onShow: (form) => {}, // Callback when form appears
86
+ onSubmit: (answers) => {}, // Callback when form is submitted
87
+ onDismiss: () => {}, // Callback when form is dismissed
88
+ onError: (error) => {}, // Callback on errors
62
89
  });
63
90
  ```
64
91
 
65
- #### Configuration Options
66
-
67
- | Option | Type | Default | Description |
68
- |--------|------|---------|-------------|
69
- | `apiKey` | `string` | **required** | Your Bliptar API key |
70
- | `debug` | `boolean` | `false` | Enable console logging for debugging |
71
- | `userId` | `string` | - | Pre-set user ID for attribution |
72
- | `userAttributes` | `object` | - | Pre-set user attributes for targeting |
73
- | `onShow` | `function` | - | Callback when a form is displayed |
74
- | `onSubmit` | `function` | - | Callback when a response is submitted |
75
- | `onDismiss` | `function` | - | Callback when a form is dismissed |
76
- | `onError` | `function` | - | Callback when an error occurs |
77
-
78
- ---
92
+ ## API Reference
79
93
 
80
94
  ### `init()`
81
95
 
82
- Initializes the SDK. This sets up event listeners and triggers a `page_view` event.
96
+ Initializes the SDK. Fetches campaigns and sets up automatic triggers.
83
97
 
84
98
  ```javascript
85
- bliptar.init();
99
+ await bliptar.init();
86
100
  ```
87
101
 
88
- > **Note:** Call this once after creating the instance, typically when your app loads.
89
-
90
- ---
91
-
92
102
  ### `identify(userId, attributes?)`
93
103
 
94
- Associates feedback with a specific user. Call this when a user logs in.
104
+ Associates feedback with a user. Call when user logs in.
95
105
 
96
106
  ```javascript
97
- // Basic identification
98
- bliptar.identify('user-123');
99
-
100
- // With attributes for targeting
101
107
  bliptar.identify('user-123', {
102
108
  email: 'user@example.com',
103
109
  plan: 'pro',
104
- company: 'Acme Inc',
105
- signupDate: '2024-01-15',
106
110
  });
107
111
  ```
108
112
 
109
- User attributes can be used in campaign targeting rules (e.g., only show to "pro" plan users).
110
-
111
- ---
112
-
113
113
  ### `track(event, data?)`
114
114
 
115
- Tracks a custom event that may trigger a feedback form.
115
+ Triggers a custom event. Use with "Custom Event" campaign trigger.
116
116
 
117
117
  ```javascript
118
- // Simple event
119
- bliptar.track('checkout_complete');
120
-
121
- // With event data
122
118
  bliptar.track('checkout_complete', {
123
- orderId: 'order-456',
119
+ orderId: 'order-123',
124
120
  total: 99.99,
125
- items: 3,
126
- });
127
-
128
- // Feature usage
129
- bliptar.track('feature_used', {
130
- feature: 'export_pdf',
131
- duration: 5000,
132
121
  });
133
122
  ```
134
123
 
135
- Configure campaigns in the dashboard to trigger forms based on these events.
136
-
137
- ---
138
-
139
- ### `trackClicks(options)`
140
-
141
- Sets up click tracking on specific elements. When a user clicks a matching element, it fires a `button_click` event that can trigger campaigns.
142
-
143
- ```javascript
144
- // Track clicks on a specific button
145
- bliptar.trackClicks({
146
- selector: '#upgrade-button',
147
- name: 'upgrade-cta',
148
- });
149
-
150
- // Track clicks on all pricing buttons
151
- bliptar.trackClicks({
152
- selector: '.pricing-cta',
153
- name: 'pricing-click',
154
- });
155
-
156
- // Track only the first click
157
- bliptar.trackClicks({
158
- selector: '.signup-button',
159
- name: 'signup-click',
160
- once: true,
161
- });
162
- ```
163
-
164
- #### Options
165
-
166
- | Option | Type | Default | Description |
167
- |--------|------|---------|-------------|
168
- | `selector` | `string` | **required** | CSS selector for elements to track |
169
- | `name` | `string` | `selector` | Friendly name for the click target |
170
- | `once` | `boolean` | `false` | Only track the first click per session |
171
-
172
- #### Event Data Sent
173
-
174
- When a click is detected, the following data is sent:
175
-
176
- ```javascript
177
- {
178
- selector: '.pricing-cta',
179
- name: 'pricing-click',
180
- element_text: 'Get Started',
181
- element_id: 'cta-btn',
182
- element_class: 'pricing-cta btn-primary'
183
- }
184
- ```
185
-
186
- ---
187
-
188
- ### `enableExitIntent(options?)`
189
-
190
- Enables exit intent detection. Triggers an `exit_intent` event when a user appears to be leaving the page.
191
-
192
- ```javascript
193
- // Basic usage (triggers once per session)
194
- bliptar.enableExitIntent();
195
-
196
- // Custom configuration
197
- bliptar.enableExitIntent({
198
- threshold: 50, // Pixels from top of viewport
199
- cooldown: 10000, // Ms before can trigger again
200
- once: true, // Only trigger once per session
201
- });
202
- ```
203
-
204
- #### Options
205
-
206
- | Option | Type | Default | Description |
207
- |--------|------|---------|-------------|
208
- | `threshold` | `number` | `50` | Distance in pixels from top of viewport to trigger |
209
- | `cooldown` | `number` | `10000` | Milliseconds before exit intent can trigger again |
210
- | `once` | `boolean` | `true` | Only trigger once per session |
211
-
212
- #### How It Works
213
-
214
- - **Desktop:** Detects when the mouse cursor leaves the viewport from the top (user moving to close tab, click back, or navigate away)
215
- - **Mobile:** Detects when the page visibility changes (user switching apps or tabs)
216
-
217
- ---
218
-
219
124
  ### `showForm(formId)`
220
125
 
221
- Manually displays a specific form, bypassing campaign rules.
126
+ Manually show a specific form, bypassing campaign rules.
222
127
 
223
128
  ```javascript
224
- // Show a form programmatically
225
129
  await bliptar.showForm('form_abc123');
226
-
227
- // Example: Show after completing onboarding
228
- function completeOnboarding() {
229
- // ... onboarding logic
230
- bliptar.showForm('form_onboarding_feedback');
231
- }
232
130
  ```
233
131
 
234
- ---
235
-
236
132
  ### `destroy()`
237
133
 
238
- Removes all event listeners and cleans up the SDK. Call this when unmounting your app or when done collecting feedback.
134
+ Cleanup - removes listeners and timers. Call when unmounting.
239
135
 
240
136
  ```javascript
241
137
  bliptar.destroy();
242
138
  ```
243
139
 
244
- ---
245
-
246
- ## Usage Examples
247
-
248
- ### Basic Setup
249
-
250
- ```javascript
251
- import { createBliptar } from '@bliptarjs/sdk';
252
-
253
- const bliptar = createBliptar({
254
- apiKey: 'blip_live_xxxxxxxx',
255
- });
256
-
257
- bliptar.init();
258
- ```
259
-
260
- ### With User Identification
261
-
262
- ```javascript
263
- import { createBliptar } from '@bliptarjs/sdk';
264
-
265
- const bliptar = createBliptar({
266
- apiKey: 'blip_live_xxxxxxxx',
267
- });
268
-
269
- bliptar.init();
270
-
271
- // When user logs in
272
- function onUserLogin(user) {
273
- bliptar.identify(user.id, {
274
- email: user.email,
275
- plan: user.subscription.plan,
276
- createdAt: user.createdAt,
277
- });
278
- }
279
- ```
280
-
281
- ### E-commerce Checkout Feedback
282
-
283
- ```javascript
284
- import { createBliptar } from '@bliptarjs/sdk';
285
-
286
- const bliptar = createBliptar({
287
- apiKey: 'blip_live_xxxxxxxx',
288
- onSubmit: (answers) => {
289
- // Send to your analytics
290
- analytics.track('feedback_submitted', answers);
291
- },
292
- });
293
-
294
- bliptar.init();
295
-
296
- // Track successful purchase
297
- function onPurchaseComplete(order) {
298
- bliptar.track('purchase_complete', {
299
- orderId: order.id,
300
- total: order.total,
301
- itemCount: order.items.length,
302
- });
303
- }
304
- ```
305
-
306
- ### Exit Intent Survey
307
-
308
- ```javascript
309
- import { createBliptar } from '@bliptarjs/sdk';
310
-
311
- const bliptar = createBliptar({
312
- apiKey: 'blip_live_xxxxxxxx',
313
- });
314
-
315
- bliptar.init();
316
-
317
- // Enable exit intent on pricing page
318
- if (window.location.pathname === '/pricing') {
319
- bliptar.enableExitIntent({
320
- once: true, // Only show once
321
- });
322
- }
323
- ```
324
-
325
- ### Button Click Feedback
140
+ ## Framework Examples
326
141
 
327
- ```javascript
328
- import { createBliptar } from '@bliptarjs/sdk';
329
-
330
- const bliptar = createBliptar({
331
- apiKey: 'blip_live_xxxxxxxx',
332
- });
333
-
334
- bliptar.init();
335
-
336
- // Track clicks on upgrade buttons
337
- bliptar.trackClicks({
338
- selector: '[data-track="upgrade"]',
339
- name: 'upgrade-button',
340
- });
341
-
342
- // Track clicks on help buttons
343
- bliptar.trackClicks({
344
- selector: '.help-button',
345
- name: 'help-click',
346
- });
347
- ```
348
-
349
- ### React Integration
142
+ ### React
350
143
 
351
144
  ```jsx
352
145
  import { useEffect } from 'react';
353
146
  import { createBliptar } from '@bliptarjs/sdk';
354
147
 
355
148
  const bliptar = createBliptar({
356
- apiKey: 'blip_live_xxxxxxxx',
149
+ apiKey: process.env.REACT_APP_BLIPTAR_KEY,
357
150
  });
358
151
 
359
152
  function App() {
360
153
  useEffect(() => {
361
154
  bliptar.init();
362
-
363
- return () => {
364
- bliptar.destroy();
365
- };
155
+ return () => bliptar.destroy();
366
156
  }, []);
367
157
 
368
158
  return <div>Your app</div>;
369
159
  }
370
-
371
- // In a component that needs to identify users
372
- function UserProfile({ user }) {
373
- useEffect(() => {
374
- if (user) {
375
- bliptar.identify(user.id, {
376
- email: user.email,
377
- plan: user.plan,
378
- });
379
- }
380
- }, [user]);
381
-
382
- return <div>Welcome, {user.name}</div>;
383
- }
384
160
  ```
385
161
 
386
- ### Next.js Integration
162
+ ### Next.js
387
163
 
388
164
  ```jsx
389
- // lib/bliptar.js
165
+ // app/providers.js
166
+ 'use client';
167
+ import { useEffect } from 'react';
390
168
  import { createBliptar } from '@bliptarjs/sdk';
391
169
 
392
- export const bliptar = typeof window !== 'undefined'
393
- ? createBliptar({ apiKey: process.env.NEXT_PUBLIC_BLIPTAR_API_KEY })
170
+ const bliptar = typeof window !== 'undefined'
171
+ ? createBliptar({ apiKey: process.env.NEXT_PUBLIC_BLIPTAR_KEY })
394
172
  : null;
395
173
 
396
- // app/layout.js or pages/_app.js
397
- 'use client';
398
-
399
- import { useEffect } from 'react';
400
- import { bliptar } from '@/lib/bliptar';
401
-
402
- export default function RootLayout({ children }) {
174
+ export function BliptarProvider({ children }) {
403
175
  useEffect(() => {
404
176
  bliptar?.init();
405
-
406
- return () => {
407
- bliptar?.destroy();
408
- };
177
+ return () => bliptar?.destroy();
409
178
  }, []);
410
179
 
180
+ return children;
181
+ }
182
+
183
+ // app/layout.js
184
+ import { BliptarProvider } from './providers';
185
+
186
+ export default function RootLayout({ children }) {
411
187
  return (
412
188
  <html>
413
- <body>{children}</body>
189
+ <body>
190
+ <BliptarProvider>{children}</BliptarProvider>
191
+ </body>
414
192
  </html>
415
193
  );
416
194
  }
417
195
  ```
418
196
 
419
- ### Vue Integration
197
+ ### Vue
420
198
 
421
199
  ```vue
422
200
  <script setup>
@@ -424,95 +202,78 @@ import { onMounted, onUnmounted } from 'vue';
424
202
  import { createBliptar } from '@bliptarjs/sdk';
425
203
 
426
204
  const bliptar = createBliptar({
427
- apiKey: 'blip_live_xxxxxxxx',
205
+ apiKey: import.meta.env.VITE_BLIPTAR_KEY,
428
206
  });
429
207
 
430
- onMounted(() => {
431
- bliptar.init();
432
- });
433
-
434
- onUnmounted(() => {
435
- bliptar.destroy();
436
- });
208
+ onMounted(() => bliptar.init());
209
+ onUnmounted(() => bliptar.destroy());
437
210
  </script>
438
211
  ```
439
212
 
440
- ---
213
+ ## User Identification
441
214
 
442
- ## Automatic Tracking
215
+ Track feedback by user:
443
216
 
444
- The SDK automatically tracks the following without any additional code:
217
+ ```javascript
218
+ // When user logs in
219
+ bliptar.identify(user.id, {
220
+ email: user.email,
221
+ plan: user.subscription.plan,
222
+ signupDate: user.createdAt,
223
+ });
224
+ ```
445
225
 
446
- | Data | Description |
447
- |------|-------------|
448
- | `page_view` | Fired on `init()` |
449
- | `scroll_depth` | Percentage scrolled (0-100%) |
450
- | `time_on_page` | Seconds spent on page |
451
- | `device_type` | `desktop`, `mobile`, or `tablet` |
452
- | `page_url` | Current page URL |
453
- | `referrer` | Previous page URL |
226
+ User attributes can be used in campaign targeting rules.
454
227
 
455
- This data is sent with every trigger request and can be used in campaign conditions.
228
+ ## Advanced: Manual Triggers
456
229
 
457
- ---
230
+ For advanced use cases, you can manually set up triggers:
458
231
 
459
- ## Form Types
232
+ ### `trackClicks(options)`
460
233
 
461
- The SDK supports multiple feedback form types:
234
+ Track clicks on specific elements:
462
235
 
463
- | Type | Description |
464
- |------|-------------|
465
- | **Binary** | Thumbs up/down, Yes/No questions |
466
- | **Star Rating** | 1-5 star ratings with optional half stars |
467
- | **Emoji Scale** | 3 or 5 point emoji scales |
468
- | **NPS** | Net Promoter Score (0-10) |
469
- | **Single Choice** | Multiple choice questions |
470
- | **Text Input** | Free-form text responses |
236
+ ```javascript
237
+ bliptar.trackClicks({
238
+ selector: '.upgrade-button',
239
+ name: 'upgrade-click',
240
+ once: true, // Only track first click
241
+ });
242
+ ```
471
243
 
472
- Forms are configured in the Bliptar dashboard and automatically rendered by the SDK.
244
+ ### `enableExitIntent(options?)`
245
+
246
+ Manually enable exit intent detection:
473
247
 
474
- ---
248
+ ```javascript
249
+ bliptar.enableExitIntent({
250
+ threshold: 50, // Pixels from top
251
+ cooldown: 10000, // Ms before can trigger again
252
+ once: true, // Only trigger once per session
253
+ });
254
+ ```
475
255
 
476
- ## TypeScript Support
256
+ ## TypeScript
477
257
 
478
- The SDK is written in TypeScript and includes full type definitions.
258
+ Full TypeScript support included:
479
259
 
480
260
  ```typescript
481
- import {
482
- createBliptar,
483
- BliptarInstance,
484
- BliptarConfig,
485
- ClickTrackingOptions,
486
- ExitIntentOptions,
487
- } from '@bliptarjs/sdk';
261
+ import { createBliptar, BliptarConfig } from '@bliptarjs/sdk';
488
262
 
489
263
  const config: BliptarConfig = {
490
- apiKey: 'blip_live_xxxxxxxx',
264
+ apiKey: 'your-api-key',
491
265
  debug: true,
492
- onSubmit: (answers) => {
493
- console.log(answers);
494
- },
495
266
  };
496
267
 
497
- const bliptar: BliptarInstance = createBliptar(config);
268
+ const bliptar = createBliptar(config);
269
+ ```
498
270
 
499
- // Type-safe click tracking
500
- const clickOptions: ClickTrackingOptions = {
501
- selector: '.cta-button',
502
- name: 'main-cta',
503
- once: true,
504
- };
505
- bliptar.trackClicks(clickOptions);
271
+ ## Bundle Size
506
272
 
507
- // Type-safe exit intent
508
- const exitOptions: ExitIntentOptions = {
509
- threshold: 50,
510
- once: true,
511
- };
512
- bliptar.enableExitIntent(exitOptions);
513
- ```
273
+ - **ESM:** ~26KB (minified)
274
+ - **Gzipped:** ~8KB
514
275
 
515
- ---
276
+ Zero dependencies.
516
277
 
517
278
  ## Browser Support
518
279
 
@@ -523,44 +284,22 @@ bliptar.enableExitIntent(exitOptions);
523
284
  | Safari | Latest 2 versions |
524
285
  | Edge | Latest 2 versions |
525
286
 
526
- ---
527
-
528
- ## Bundle Size
529
-
530
- - **ESM:** ~25KB (minified)
531
- - **Gzipped:** ~7KB
532
-
533
- The SDK has zero dependencies.
534
-
535
- ---
536
-
537
287
  ## Troubleshooting
538
288
 
539
289
  ### Forms not showing?
540
290
 
541
- 1. **Check your API key** - Ensure it's valid and for the correct environment
542
- 2. **Check campaign rules** - Verify your campaign is active and targeting rules match
543
- 3. **Enable debug mode** - Set `debug: true` to see console logs
544
- 4. **Check throttling** - Campaigns have frequency limits to avoid annoying users
545
-
546
- ### Debug Mode
291
+ 1. **Check API key** - ensure it's valid
292
+ 2. **Check campaigns** - verify campaign is active in dashboard
293
+ 3. **Enable debug mode** - set `debug: true` to see logs
294
+ 4. **Check throttling** - campaigns have frequency limits
547
295
 
548
296
  ```javascript
549
297
  const bliptar = createBliptar({
550
- apiKey: 'blip_live_xxxxxxxx',
551
- debug: true, // Enables console logging
298
+ apiKey: 'your-key',
299
+ debug: true, // See what's happening
552
300
  });
553
301
  ```
554
302
 
555
- This will log:
556
- - SDK initialization
557
- - Events being tracked
558
- - Form display triggers
559
- - API responses
560
- - Errors
561
-
562
- ---
563
-
564
303
  ## License
565
304
 
566
305
  MIT
package/dist/index.cjs CHANGED
@@ -1,15 +1,15 @@
1
- "use strict";var v=Object.defineProperty;var S=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var P=Object.prototype.hasOwnProperty;var A=(i,e)=>{for(var t in e)v(i,t,{get:e[t],enumerable:!0})},B=(i,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of L(e))!P.call(i,o)&&o!==t&&v(i,o,{get:()=>e[o],enumerable:!(r=S(e,o))||r.enumerable});return i};var j=i=>B(v({},"__esModule",{value:!0}),i);var Y={};A(Y,{Bliptar:()=>b,createBliptar:()=>k});module.exports=j(Y);var u={mode:"light",backgroundColor:"#ffffff",surfaceColor:"#f5f5f5",textPrimary:"#1a1a1a",textSecondary:"#666666",primaryColor:"#3b82f6",primaryHover:"#2563eb",borderColor:"#e0e0e0",shadowColor:"rgba(0, 0, 0, 0.15)",starColor:"#fbbf24"},h={mode:"dark",backgroundColor:"#1a1a1a",surfaceColor:"#2a2a2a",textPrimary:"#ffffff",textSecondary:"#a0a0a0",primaryColor:"#3b82f6",primaryHover:"#60a5fa",borderColor:"#333333",shadowColor:"rgba(0, 0, 0, 0.4)",starColor:"#fbbf24"};function D(i,e){try{let r=window.getComputedStyle(i).getPropertyValue(e);return r&&r!=="rgba(0, 0, 0, 0)"?r:null}catch{return null}}function H(i){try{return getComputedStyle(document.documentElement).getPropertyValue(i).trim()||null}catch{return null}}function C(i){let e=i.match(/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i);if(e)return{r:parseInt(e[1],16),g:parseInt(e[2],16),b:parseInt(e[3],16)};let t=i.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);return t?{r:parseInt(t[1],10),g:parseInt(t[2],10),b:parseInt(t[3],10)}:null}function M(i,e,t){let[r,o,a]=[i,e,t].map(n=>(n=n/255,n<=.03928?n/12.92:Math.pow((n+.055)/1.055,2.4)));return .2126*r+.7152*o+.0722*a}function _(i){let e=C(i);return e?M(e.r,e.g,e.b)<.5:!1}function f(i,e){let t=C(i);if(!t)return i;let r=o=>Math.min(255,Math.max(0,Math.round(o+e/100*255)));return`rgb(${r(t.r)}, ${r(t.g)}, ${r(t.b)})`}function z(i){return _(i)?"#ffffff":"#1a1a1a"}function R(i,e){let t={},r=null;i&&(r=document.querySelector(i)),r||(r=document.body);let o=D(r,"background-color");if(o){let a=_(o);t.mode=a?"dark":"light",t.backgroundColor=o,t.surfaceColor=f(o,a?10:-5),t.textPrimary=z(o),t.textSecondary=f(t.textPrimary,a?-30:30),t.borderColor=f(o,a?20:-15),t.shadowColor=a?"rgba(0, 0, 0, 0.4)":"rgba(0, 0, 0, 0.15)"}if(e){let a={"--primary":"primaryColor","--primary-color":"primaryColor","--accent":"primaryColor","--accent-color":"primaryColor","--brand":"primaryColor","--brand-color":"primaryColor","--color-primary":"primaryColor","--background":"backgroundColor","--bg":"backgroundColor","--bg-color":"backgroundColor","--text":"textPrimary","--text-color":"textPrimary","--foreground":"textPrimary","--border":"borderColor","--border-color":"borderColor"};for(let[n,l]of Object.entries(a)){let p=H(n);p&&(t[l]=p)}}return t.primaryColor&&(t.primaryHover=f(t.primaryColor,t.mode==="dark"?15:-10)),Object.keys(t).length>0?t:null}function w(i){let{theme:e,customTheme:t,adaptiveOptions:r}=i;if(e==="custom"&&t)return{mode:_(t.backgroundColor)?"dark":"light",backgroundColor:t.backgroundColor,surfaceColor:t.surfaceColor,textPrimary:t.textPrimary,textSecondary:t.textSecondary,primaryColor:t.primaryColor,primaryHover:t.primaryHover,borderColor:t.borderColor,shadowColor:t.shadowColor,starColor:"#fbbf24"};if(e==="adaptive"){let o=r||{fallback:"light",autoContrast:!0},a=R(o.sourceElement,o.inheritCssVars);return a?{...a.mode==="dark"?h:u,...a}:o.fallback==="dark"?h:u}return e==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?h:u:e==="dark"?h:u}function $(i){return`
2
- --bliptar-bg: ${i.backgroundColor};
3
- --bliptar-surface: ${i.surfaceColor};
4
- --bliptar-text: ${i.textPrimary};
5
- --bliptar-text-secondary: ${i.textSecondary};
6
- --bliptar-primary: ${i.primaryColor};
7
- --bliptar-primary-hover: ${i.primaryHover};
8
- --bliptar-border: ${i.borderColor};
9
- --bliptar-shadow: ${i.shadowColor};
10
- --bliptar-star: ${i.starColor};
11
- `}var g=class{constructor(e,t){this.container=null;this.currentIndex=0;this.answers={};this.form=e,this.options=t,this.theme=w(e.appearance)}render(){this.createContainer(),this.injectStyles(),this.renderQuestion()}destroy(){if(this.container){let e=this.container.querySelector(".bliptar__card");e?(e.classList.add("bliptar__card--exit"),setTimeout(()=>{this.container?.remove(),this.container=null},200)):(this.container.remove(),this.container=null)}}createContainer(){this.container=document.createElement("div"),this.container.id="bliptar-form",this.container.className=`bliptar bliptar--${this.form.appearance.position}`,document.body.appendChild(this.container)}injectStyles(){let e=document.getElementById("bliptar-styles");e&&e.remove();let t=document.createElement("style");t.id="bliptar-styles",t.textContent=this.getStyles(),document.head.appendChild(t)}getStyles(){let{appearance:e}=this.form,t=this.theme,r=e.animation||"slide-up";return`
12
- :root { ${$(t)} }
1
+ "use strict";var x=Object.defineProperty;var I=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var P=Object.prototype.hasOwnProperty;var D=(o,e)=>{for(var t in e)x(o,t,{get:e[t],enumerable:!0})},A=(o,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of L(e))!P.call(o,i)&&i!==t&&x(o,i,{get:()=>e[i],enumerable:!(r=I(e,i))||r.enumerable});return o};var B=o=>A(x({},"__esModule",{value:!0}),o);var F={};D(F,{Bliptar:()=>h,createBliptar:()=>k});module.exports=B(F);var b={mode:"light",backgroundColor:"#ffffff",surfaceColor:"#f5f5f5",textPrimary:"#1a1a1a",textSecondary:"#666666",primaryColor:"#3b82f6",primaryHover:"#2563eb",borderColor:"#e0e0e0",shadowColor:"rgba(0, 0, 0, 0.15)",starColor:"#fbbf24"},u={mode:"dark",backgroundColor:"#1a1a1a",surfaceColor:"#2a2a2a",textPrimary:"#ffffff",textSecondary:"#a0a0a0",primaryColor:"#3b82f6",primaryHover:"#60a5fa",borderColor:"#333333",shadowColor:"rgba(0, 0, 0, 0.4)",starColor:"#fbbf24"};function j(o,e){try{let r=window.getComputedStyle(o).getPropertyValue(e);return r&&r!=="rgba(0, 0, 0, 0)"?r:null}catch{return null}}function H(o){try{return getComputedStyle(document.documentElement).getPropertyValue(o).trim()||null}catch{return null}}function C(o){let e=o.match(/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i);if(e)return{r:parseInt(e[1],16),g:parseInt(e[2],16),b:parseInt(e[3],16)};let t=o.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);return t?{r:parseInt(t[1],10),g:parseInt(t[2],10),b:parseInt(t[3],10)}:null}function M(o,e,t){let[r,i,a]=[o,e,t].map(n=>(n=n/255,n<=.03928?n/12.92:Math.pow((n+.055)/1.055,2.4)));return .2126*r+.7152*i+.0722*a}function _(o){let e=C(o);return e?M(e.r,e.g,e.b)<.5:!1}function g(o,e){let t=C(o);if(!t)return o;let r=i=>Math.min(255,Math.max(0,Math.round(i+e/100*255)));return`rgb(${r(t.r)}, ${r(t.g)}, ${r(t.b)})`}function z(o){return _(o)?"#ffffff":"#1a1a1a"}function R(o,e){let t={},r=null;o&&(r=document.querySelector(o)),r||(r=document.body);let i=j(r,"background-color");if(i){let a=_(i);t.mode=a?"dark":"light",t.backgroundColor=i,t.surfaceColor=g(i,a?10:-5),t.textPrimary=z(i),t.textSecondary=g(t.textPrimary,a?-30:30),t.borderColor=g(i,a?20:-15),t.shadowColor=a?"rgba(0, 0, 0, 0.4)":"rgba(0, 0, 0, 0.15)"}if(e){let a={"--primary":"primaryColor","--primary-color":"primaryColor","--accent":"primaryColor","--accent-color":"primaryColor","--brand":"primaryColor","--brand-color":"primaryColor","--color-primary":"primaryColor","--background":"backgroundColor","--bg":"backgroundColor","--bg-color":"backgroundColor","--text":"textPrimary","--text-color":"textPrimary","--foreground":"textPrimary","--border":"borderColor","--border-color":"borderColor"};for(let[n,l]of Object.entries(a)){let c=H(n);c&&l!=="mode"&&(t[l]=c)}}return t.primaryColor&&(t.primaryHover=g(t.primaryColor,t.mode==="dark"?15:-10)),Object.keys(t).length>0?t:null}function w(o){let{theme:e,customTheme:t,adaptiveOptions:r}=o;if(e==="custom"&&t)return{mode:_(t.backgroundColor)?"dark":"light",backgroundColor:t.backgroundColor,surfaceColor:t.surfaceColor,textPrimary:t.textPrimary,textSecondary:t.textSecondary,primaryColor:t.primaryColor,primaryHover:t.primaryHover,borderColor:t.borderColor,shadowColor:t.shadowColor,starColor:"#fbbf24"};if(e==="adaptive"){let i=r||{fallback:"light",autoContrast:!0},a=R(i.sourceElement,i.inheritCssVars);return a?{...a.mode==="dark"?u:b,...a}:i.fallback==="dark"?u:b}return e==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?u:b:e==="dark"?u:b}function T(o){return`
2
+ --bliptar-bg: ${o.backgroundColor};
3
+ --bliptar-surface: ${o.surfaceColor};
4
+ --bliptar-text: ${o.textPrimary};
5
+ --bliptar-text-secondary: ${o.textSecondary};
6
+ --bliptar-primary: ${o.primaryColor};
7
+ --bliptar-primary-hover: ${o.primaryHover};
8
+ --bliptar-border: ${o.borderColor};
9
+ --bliptar-shadow: ${o.shadowColor};
10
+ --bliptar-star: ${o.starColor};
11
+ `}var f=class{constructor(e,t){this.container=null;this.currentIndex=0;this.answers={};this.form=e,this.options=t,this.theme=w(e.appearance)}render(){this.createContainer(),this.injectStyles(),this.renderQuestion()}destroy(){if(this.container){let e=this.container.querySelector(".bliptar__card");e?(e.classList.add("bliptar__card--exit"),setTimeout(()=>{this.container?.remove(),this.container=null},200)):(this.container.remove(),this.container=null)}}createContainer(){this.container=document.createElement("div"),this.container.id="bliptar-form",this.container.className=`bliptar bliptar--${this.form.appearance.position}`,document.body.appendChild(this.container)}injectStyles(){let e=document.getElementById("bliptar-styles");e&&e.remove();let t=document.createElement("style");t.id="bliptar-styles",t.textContent=this.getStyles(),document.head.appendChild(t)}getStyles(){let{appearance:e}=this.form,t=this.theme,r=e.animation||"slide-up";return`
12
+ :root { ${T(t)} }
13
13
 
14
14
  .bliptar {
15
15
  position: fixed;
@@ -372,20 +372,20 @@
372
372
  }
373
373
 
374
374
  @media print { .bliptar { display: none !important; } }
375
- `}getAnimationStyles(e){switch(e){case"slide-up":return"animation: bliptar-slide-up 0.35s cubic-bezier(0.16, 1, 0.3, 1);";case"fade":return"animation: bliptar-fade 0.3s ease-out;";case"scale":return"animation: bliptar-scale 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);";case"none":return"";default:return"animation: bliptar-slide-up 0.35s cubic-bezier(0.16, 1, 0.3, 1);"}}renderQuestion(){if(!this.container)return;let{questions:e,appearance:t,behavior:r}=this.form,o=e[this.currentIndex],a=this.currentIndex===e.length-1,n="";if(t.backdrop&&(n+='<div class="bliptar__backdrop"></div>'),n+='<div class="bliptar__card">',r.allowDismiss&&(n+=`<button class="bliptar__close" aria-label="Close">
375
+ `}getAnimationStyles(e){switch(e){case"slide-up":return"animation: bliptar-slide-up 0.35s cubic-bezier(0.16, 1, 0.3, 1);";case"fade":return"animation: bliptar-fade 0.3s ease-out;";case"scale":return"animation: bliptar-scale 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);";case"none":return"";default:return"animation: bliptar-slide-up 0.35s cubic-bezier(0.16, 1, 0.3, 1);"}}renderQuestion(){if(!this.container)return;let{questions:e,appearance:t,behavior:r}=this.form,i=e[this.currentIndex],a=this.currentIndex===e.length-1,n="";if(t.backdrop&&(n+='<div class="bliptar__backdrop"></div>'),n+='<div class="bliptar__card">',r.allowDismiss&&(n+=`<button class="bliptar__close" aria-label="Close">
376
376
  <svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
377
377
  <path d="M1 1l12 12M1 13L13 1"/>
378
378
  </svg>
379
- </button>`),r.showProgress&&e.length>1){n+='<div class="bliptar__progress">';for(let l=0;l<e.length;l++)n+=`<div class="bliptar__progress-bar${l<=this.currentIndex?" bliptar__progress-bar--active":""}"></div>`;n+="</div>"}n+=`<div class="bliptar__question">${this.escapeHtml(o.question)}</div>`,n+=this.renderAnswerOptions(o),(!r.autoAdvance||o.type==="text-input")&&(n+='<div class="bliptar__nav">',n+=this.currentIndex>0?'<button class="bliptar__nav-btn bliptar__nav-btn--back">Back</button>':"<div></div>",n+=`<button class="bliptar__nav-btn bliptar__nav-btn--next">${a?"Submit":"Continue"}</button>`,n+="</div>"),n+="</div>",this.container.innerHTML=n,this.attachEventListeners()}escapeHtml(e){let t=document.createElement("div");return t.textContent=e,t.innerHTML}renderAnswerOptions(e){let t=`q${this.currentIndex}`,r=this.answers[t];switch(e.type){case"binary":return`<div class="bliptar__options">
379
+ </button>`),r.showProgress&&e.length>1){n+='<div class="bliptar__progress">';for(let l=0;l<e.length;l++)n+=`<div class="bliptar__progress-bar${l<=this.currentIndex?" bliptar__progress-bar--active":""}"></div>`;n+="</div>"}n+=`<div class="bliptar__question">${this.escapeHtml(i.question)}</div>`,n+=this.renderAnswerOptions(i),(!r.autoAdvance||i.type==="text-input")&&(n+='<div class="bliptar__nav">',n+=this.currentIndex>0?'<button class="bliptar__nav-btn bliptar__nav-btn--back">Back</button>':"<div></div>",n+=`<button class="bliptar__nav-btn bliptar__nav-btn--next">${a?"Submit":"Continue"}</button>`,n+="</div>"),n+="</div>",this.container.innerHTML=n,this.attachEventListeners()}escapeHtml(e){let t=document.createElement("div");return t.textContent=e,t.innerHTML}renderAnswerOptions(e){let t=`q${this.currentIndex}`,r=this.answers[t];switch(e.type){case"binary":return`<div class="bliptar__options">
380
380
  <button class="bliptar__btn${r===!0?" bliptar__btn--selected":""}" data-value="true">
381
381
  ${e.style==="thumbs"?"\u{1F44D} ":""}${this.escapeHtml(e.positiveLabel)}
382
382
  </button>
383
383
  <button class="bliptar__btn${r===!1?" bliptar__btn--selected":""}" data-value="false">
384
384
  ${e.style==="thumbs"?"\u{1F44E} ":""}${this.escapeHtml(e.negativeLabel)}
385
385
  </button>
386
- </div>`;case"star-rating":let o='<div class="bliptar__stars">';for(let s=1;s<=e.maxStars;s++)o+=`<span class="bliptar__star${r>=s?" bliptar__star--active":""}" data-value="${s}">\u2605</span>`;return o+"</div>";case"emoji-scale":let a=["\u{1F61E}","\u{1F615}","\u{1F610}","\u{1F642}","\u{1F60A}"],n=["\u{1F61E}","\u{1F610}","\u{1F60A}"],l=e.scale===3?n:a,p='<div class="bliptar__emojis">';return l.forEach((s,d)=>{p+=`<span class="bliptar__emoji${r===d+1?" bliptar__emoji--active":""}" data-value="${d+1}" title="${e.labels?.[d]||""}">${s}</span>`}),p+"</div>";case"nps":let c='<div class="bliptar__nps">';for(let s=0;s<=10;s++)c+=`<button class="bliptar__nps-btn${r===s?" bliptar__nps-btn--selected":""}" data-value="${s}">${s}</button>`;return c+=`</div><div class="bliptar__nps-labels"><span>${this.escapeHtml(e.lowLabel)}</span><span>${this.escapeHtml(e.highLabel)}</span></div>`,c;case"single-choice":let m='<div class="bliptar__options">';return e.options.forEach(s=>{m+=`<button class="bliptar__btn${r===s?" bliptar__btn--selected":""}" data-value="${this.escapeHtml(s)}">${this.escapeHtml(s)}</button>`}),m+"</div>";case"text-input":return`<textarea class="bliptar__textarea" placeholder="${this.escapeHtml(e.placeholder)}" maxlength="${e.maxLength}">${r||""}</textarea>`;default:return""}}attachEventListeners(){if(!this.container)return;let e=this.form.questions[this.currentIndex],{behavior:t}=this.form,r=this.container.querySelector(".bliptar__close");r&&r.addEventListener("click",()=>this.options.onDismiss());let o=this.container.querySelector(".bliptar__backdrop");o&&t.allowDismiss&&o.addEventListener("click",()=>this.options.onDismiss());let a=s=>{this.answers[`q${this.currentIndex}`]=s,t.autoAdvance&&e.type!=="text-input"?setTimeout(()=>this.goNext(),t.autoAdvanceDelay||300):this.renderQuestion()};this.container.querySelectorAll(".bliptar__btn, .bliptar__nps-btn").forEach(s=>{s.addEventListener("click",()=>{let d=s.dataset.value;e.type==="binary"?a(d==="true"):e.type==="nps"?a(parseInt(d,10)):a(d)})});let n=this.container.querySelectorAll(".bliptar__star");n.forEach((s,d)=>{s.addEventListener("mouseenter",()=>{n.forEach((x,I)=>x.classList.toggle("bliptar__star--active",I<=d))}),s.addEventListener("click",()=>a(parseInt(s.dataset.value,10)))});let l=this.container.querySelector(".bliptar__stars");l&&l.addEventListener("mouseleave",()=>{let s=this.answers[`q${this.currentIndex}`];n.forEach((d,x)=>d.classList.toggle("bliptar__star--active",s?x<s:!1))}),this.container.querySelectorAll(".bliptar__emoji").forEach(s=>{s.addEventListener("click",()=>a(parseInt(s.dataset.value,10)))});let p=this.container.querySelector(".bliptar__textarea");p&&(p.addEventListener("input",()=>{this.answers[`q${this.currentIndex}`]=p.value}),setTimeout(()=>p.focus(),100));let c=this.container.querySelector(".bliptar__nav-btn--back");c&&c.addEventListener("click",()=>this.goBack());let m=this.container.querySelector(".bliptar__nav-btn--next");m&&m.addEventListener("click",()=>this.goNext())}goBack(){this.currentIndex>0&&(this.currentIndex--,this.renderQuestion())}async goNext(){this.currentIndex===this.form.questions.length-1?await this.submit():(this.currentIndex++,this.renderQuestion())}async submit(){if(this.container){let e=this.container.querySelector(".bliptar__card");e&&(e.innerHTML='<div class="bliptar__loading"><div class="bliptar__spinner"></div></div>')}try{await this.options.onSubmit(this.answers),this.form.behavior.showThankYou?(this.renderThankYou(),setTimeout(()=>this.options.onDismiss(),this.form.behavior.thankYouDuration||2e3)):this.options.onDismiss()}catch{this.options.onDismiss()}}renderThankYou(){if(!this.container)return;let{appearance:e,behavior:t}=this.form,r="";e.backdrop&&(r+='<div class="bliptar__backdrop"></div>'),r+=`<div class="bliptar__card">
386
+ </div>`;case"star-rating":let i='<div class="bliptar__stars">';for(let s=1;s<=e.maxStars;s++)i+=`<span class="bliptar__star${r>=s?" bliptar__star--active":""}" data-value="${s}">\u2605</span>`;return i+"</div>";case"emoji-scale":let a=["\u{1F61E}","\u{1F615}","\u{1F610}","\u{1F642}","\u{1F60A}"],n=["\u{1F61E}","\u{1F610}","\u{1F60A}"],l=e.scale===3?n:a,c='<div class="bliptar__emojis">';return l.forEach((s,d)=>{c+=`<span class="bliptar__emoji${r===d+1?" bliptar__emoji--active":""}" data-value="${d+1}" title="${e.labels?.[d]||""}">${s}</span>`}),c+"</div>";case"nps":let p='<div class="bliptar__nps">';for(let s=0;s<=10;s++)p+=`<button class="bliptar__nps-btn${r===s?" bliptar__nps-btn--selected":""}" data-value="${s}">${s}</button>`;return p+=`</div><div class="bliptar__nps-labels"><span>${this.escapeHtml(e.lowLabel)}</span><span>${this.escapeHtml(e.highLabel)}</span></div>`,p;case"single-choice":let m='<div class="bliptar__options">';return e.options.forEach(s=>{m+=`<button class="bliptar__btn${r===s?" bliptar__btn--selected":""}" data-value="${this.escapeHtml(s)}">${this.escapeHtml(s)}</button>`}),m+"</div>";case"text-input":return`<textarea class="bliptar__textarea" placeholder="${this.escapeHtml(e.placeholder)}" maxlength="${e.maxLength}">${r||""}</textarea>`;default:return""}}attachEventListeners(){if(!this.container)return;let e=this.form.questions[this.currentIndex],{behavior:t}=this.form,r=this.container.querySelector(".bliptar__close");r&&r.addEventListener("click",()=>this.options.onDismiss());let i=this.container.querySelector(".bliptar__backdrop");i&&t.allowDismiss&&i.addEventListener("click",()=>this.options.onDismiss());let a=s=>{this.answers[`q${this.currentIndex}`]=s,t.autoAdvance&&e.type!=="text-input"?setTimeout(()=>this.goNext(),t.autoAdvanceDelay||300):this.renderQuestion()};this.container.querySelectorAll(".bliptar__btn, .bliptar__nps-btn").forEach(s=>{s.addEventListener("click",()=>{let d=s.dataset.value;e.type==="binary"?a(d==="true"):e.type==="nps"?a(parseInt(d,10)):a(d)})});let n=this.container.querySelectorAll(".bliptar__star");n.forEach((s,d)=>{s.addEventListener("mouseenter",()=>{n.forEach((v,E)=>v.classList.toggle("bliptar__star--active",E<=d))}),s.addEventListener("click",()=>a(parseInt(s.dataset.value,10)))});let l=this.container.querySelector(".bliptar__stars");l&&l.addEventListener("mouseleave",()=>{let s=this.answers[`q${this.currentIndex}`];n.forEach((d,v)=>d.classList.toggle("bliptar__star--active",s?v<s:!1))}),this.container.querySelectorAll(".bliptar__emoji").forEach(s=>{s.addEventListener("click",()=>a(parseInt(s.dataset.value,10)))});let c=this.container.querySelector(".bliptar__textarea");c&&(c.addEventListener("input",()=>{this.answers[`q${this.currentIndex}`]=c.value}),setTimeout(()=>c.focus(),100));let p=this.container.querySelector(".bliptar__nav-btn--back");p&&p.addEventListener("click",()=>this.goBack());let m=this.container.querySelector(".bliptar__nav-btn--next");m&&m.addEventListener("click",()=>this.goNext())}goBack(){this.currentIndex>0&&(this.currentIndex--,this.renderQuestion())}async goNext(){this.currentIndex===this.form.questions.length-1?await this.submit():(this.currentIndex++,this.renderQuestion())}async submit(){if(this.container){let e=this.container.querySelector(".bliptar__card");e&&(e.innerHTML='<div class="bliptar__loading"><div class="bliptar__spinner"></div></div>')}try{await this.options.onSubmit(this.answers),this.form.behavior.showThankYou?(this.renderThankYou(),setTimeout(()=>this.options.onDismiss(),this.form.behavior.thankYouDuration||2e3)):this.options.onDismiss()}catch{this.options.onDismiss()}}renderThankYou(){if(!this.container)return;let{appearance:e,behavior:t}=this.form,r="";e.backdrop&&(r+='<div class="bliptar__backdrop"></div>'),r+=`<div class="bliptar__card">
387
387
  <div class="bliptar__thankyou">
388
388
  <div class="bliptar__thankyou-emoji">\u{1F389}</div>
389
389
  <div class="bliptar__thankyou-text">${this.escapeHtml(t.thankYouMessage||"Thank you for your feedback!")}</div>
390
390
  </div>
391
- </div>`,this.container.innerHTML=r}};function T(){let i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",e="";for(let t=0;t<16;t++)e+=i.charAt(Math.floor(Math.random()*i.length));return`sess_${e}`}function y(){let i=navigator.userAgent;return/tablet|ipad|playbook|silk/i.test(i)?"tablet":/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/i.test(i)?"mobile":"desktop"}function E(i,e){let t;return(...r)=>{clearTimeout(t),t=setTimeout(()=>i(...r),e)}}var O="https://api.bliptar.com",b=class{constructor(e){this.renderer=null;this.scrollDepth=0;this.isInitialized=!1;this.eventListeners=[];this.trackedClicks=new Set;this.exitIntentTriggered=!1;this.exitIntentCooldownTimer=null;this.config={apiUrl:O,debug:!1,onShow:()=>{},onSubmit:()=>{},onDismiss:()=>{},onError:()=>{},...e},this.sessionId=this.loadOrCreateSessionId(),this.pageLoadTime=Date.now()}init(){if(this.isInitialized)return;this.isInitialized=!0,this.log("Initializing Bliptar SDK");let e=E(()=>{let t=document.documentElement,r=window.scrollY||t.scrollTop,o=t.scrollHeight-t.clientHeight;this.scrollDepth=o>0?r/o:0},100);this.addEventListener(window,"scroll",e),this.track("page_view")}identify(e,t){this.config.userId=e,this.config.userAttributes={...this.config.userAttributes,...t},this.log("User identified:",e)}async track(e,t){this.log("Tracking event:",e,t);let r=this.getContext();try{let o=await fetch(`${this.config.apiUrl}/api/sdk/trigger`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({session_id:this.sessionId,user_id:this.config.userId,event:e,event_data:t||{},context:r})});if(!o.ok)throw new Error(`API error: ${o.status}`);let a=await o.json();a.show_form&&a.form&&await this.displayForm(a.form,a.campaign_id)}catch(o){this.handleError(o)}}async showForm(e){this.log("Manually showing form:",e);try{let t=await fetch(`${this.config.apiUrl}/api/sdk/form/${e}`,{headers:{Authorization:`Bearer ${this.config.apiKey}`}});if(!t.ok)throw new Error(`Failed to fetch form: ${t.status}`);let r=await t.json();await this.displayForm(r)}catch(t){this.handleError(t)}}trackClicks(e){let{selector:t,name:r,once:o=!1}=e,a=r||t;this.log("Setting up click tracking for:",t);let n=l=>{let c=l.target.closest(t);c&&(o&&this.trackedClicks.has(a)||(this.log("Button click detected:",a),o&&this.trackedClicks.add(a),this.track("button_click",{selector:t,name:a,element_text:c.textContent?.trim().slice(0,100)||"",element_id:c.id||null,element_class:c.className||null})))};this.addEventListener(document,"click",n)}enableExitIntent(e={}){let{threshold:t=50,cooldown:r=1e4,once:o=!0}=e;this.log("Enabling exit intent detection");let a=l=>{l.clientY>t||o&&this.exitIntentTriggered||this.exitIntentCooldownTimer||(this.log("Exit intent detected"),this.exitIntentTriggered=!0,this.track("exit_intent",{trigger:"mouse_leave",mouse_y:l.clientY}),o||(this.exitIntentCooldownTimer=setTimeout(()=>{this.exitIntentCooldownTimer=null},r)))};this.addEventListener(document,"mouseleave",a);let n=()=>{if(document.visibilityState==="hidden"){if(o&&this.exitIntentTriggered)return;this.log("Exit intent detected (visibility change)"),this.exitIntentTriggered=!0,this.track("exit_intent",{trigger:"visibility_change"})}};y()!=="desktop"&&this.addEventListener(document,"visibilitychange",n)}destroy(){this.log("Destroying Bliptar SDK"),this.eventListeners.forEach(({target:e,type:t,handler:r})=>{e.removeEventListener(t,r)}),this.eventListeners=[],this.renderer&&(this.renderer.destroy(),this.renderer=null),this.exitIntentCooldownTimer&&(clearTimeout(this.exitIntentCooldownTimer),this.exitIntentCooldownTimer=null),this.trackedClicks.clear(),this.exitIntentTriggered=!1,this.isInitialized=!1}async displayForm(e,t){this.log("Displaying form:",e.formId),this.config.onShow?.(e),this.renderer=new g(e,{onSubmit:async r=>{await this.submitResponse(e.formId,r,t),this.config.onSubmit?.(r)},onDismiss:()=>{this.config.onDismiss?.(),this.renderer?.destroy(),this.renderer=null}}),this.renderer.render()}async submitResponse(e,t,r){this.log("Submitting response:",e,t);let o=this.getContext(),a=new Date().toISOString();try{await fetch(`${this.config.apiUrl}/api/sdk/submit`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({session_id:this.sessionId,user_id:this.config.userId,form_id:e,campaign_id:r,answers:t,context:o,completed_at:a})})}catch(n){this.handleError(n)}}getContext(){return{pageUrl:window.location.href,referrer:document.referrer,timeOnPage:Math.floor((Date.now()-this.pageLoadTime)/1e3),scrollDepth:Math.round(this.scrollDepth*100)/100,deviceType:y()}}loadOrCreateSessionId(){let e="bliptar_session",t=sessionStorage.getItem(e);return t||(t=T(),sessionStorage.setItem(e,t)),t}addEventListener(e,t,r){e.addEventListener(t,r),this.eventListeners.push({target:e,type:t,handler:r})}log(...e){this.config.debug&&console.log("[Bliptar]",...e)}handleError(e){this.log("Error:",e),this.config.onError?.(e)}};function k(i){return new b(i)}if(typeof window<"u"){let i=document.currentScript;if(i?.dataset.apiKey){let e=k({apiKey:i.dataset.apiKey,debug:i.dataset.debug==="true"});window.bliptar=e,e.init()}}0&&(module.exports={Bliptar,createBliptar});
391
+ </div>`,this.container.innerHTML=r}};function $(){let o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",e="";for(let t=0;t<16;t++)e+=o.charAt(Math.floor(Math.random()*o.length));return`sess_${e}`}function y(){let o=navigator.userAgent;return/tablet|ipad|playbook|silk/i.test(o)?"tablet":/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/i.test(o)?"mobile":"desktop"}function S(o,e){let t;return(...r)=>{clearTimeout(t),t=setTimeout(()=>o(...r),e)}}var O="https://api.bliptar.com",h=class{constructor(e){this.renderer=null;this.scrollDepth=0;this.maxScrollDepth=0;this.isInitialized=!1;this.eventListeners=[];this.trackedClicks=new Set;this.exitIntentTriggered=!1;this.exitIntentCooldownTimer=null;this.timeOnPageTimers=[];this.scrollDepthThresholds=new Set;this.triggeredScrollThresholds=new Set;this.campaigns=[];this.config={apiUrl:O,debug:!1,onShow:()=>{},onSubmit:()=>{},onDismiss:()=>{},onError:()=>{},...e},this.sessionId=this.loadOrCreateSessionId(),this.pageLoadTime=Date.now()}async init(){if(this.isInitialized)return;this.isInitialized=!0,this.log("Initializing Bliptar SDK"),await this.fetchConfigAndSetupTriggers();let e=S(()=>{let t=document.documentElement,r=window.scrollY||t.scrollTop,i=t.scrollHeight-t.clientHeight;this.scrollDepth=i>0?r/i:0;let a=Math.round(this.scrollDepth*100);a>this.maxScrollDepth&&(this.maxScrollDepth=a,this.checkScrollThresholds(a))},100);this.addEventListener(window,"scroll",e),this.track("page_view")}async fetchConfigAndSetupTriggers(){try{let e=await fetch(`${this.config.apiUrl}/api/sdk/config`,{headers:{Authorization:`Bearer ${this.config.apiKey}`}});if(!e.ok){this.log("Failed to fetch config:",e.status);return}let t=await e.json();this.campaigns=t.campaigns||[],this.log("Loaded campaigns:",this.campaigns.length),this.setupTriggersFromCampaigns()}catch(e){this.handleError(e)}}setupTriggersFromCampaigns(){let e=new Set;for(let t of this.campaigns){if(e.add(t.trigger_event),t.trigger_event==="scroll_depth"){let r=t.trigger_conditions?.find(i=>i.field==="scroll_depth_percent");r?.value&&this.scrollDepthThresholds.add(Number(r.value))}if(t.trigger_event==="time_on_page"){let r=t.trigger_conditions?.find(i=>i.field==="time_on_page_seconds");if(r?.value){let i=Number(r.value);this.setupTimeOnPageTrigger(i)}}}this.log("Active trigger types:",Array.from(e)),e.has("exit_intent")&&(this.log("Auto-enabling exit intent detection"),this.enableExitIntent({once:!0})),this.scrollDepthThresholds.size>0&&this.log("Scroll depth thresholds:",Array.from(this.scrollDepthThresholds))}setupTimeOnPageTrigger(e){this.log("Setting up time on page trigger:",e,"seconds");let t=setTimeout(()=>{this.track("time_on_page",{seconds:e,actual_time:Math.floor((Date.now()-this.pageLoadTime)/1e3)})},e*1e3);this.timeOnPageTimers.push(t)}checkScrollThresholds(e){for(let t of this.scrollDepthThresholds)e>=t&&!this.triggeredScrollThresholds.has(t)&&(this.triggeredScrollThresholds.add(t),this.log("Scroll threshold reached:",t+"%"),this.track("scroll_depth",{threshold:t,actual_depth:e}))}identify(e,t){this.config.userId=e,this.config.userAttributes={...this.config.userAttributes,...t},this.log("User identified:",e)}async track(e,t){this.log("Tracking event:",e,t);let r=this.getContext();try{let i=await fetch(`${this.config.apiUrl}/api/sdk/trigger`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({session_id:this.sessionId,user_id:this.config.userId,event:e,event_data:t||{},context:r})});if(!i.ok)throw new Error(`API error: ${i.status}`);let a=await i.json();a.show_form&&a.form&&await this.displayForm(a.form,a.campaign_id)}catch(i){this.handleError(i)}}async showForm(e){this.log("Manually showing form:",e);try{let t=await fetch(`${this.config.apiUrl}/api/sdk/form/${e}`,{headers:{Authorization:`Bearer ${this.config.apiKey}`}});if(!t.ok)throw new Error(`Failed to fetch form: ${t.status}`);let r=await t.json();await this.displayForm(r)}catch(t){this.handleError(t)}}trackClicks(e){let{selector:t,name:r,once:i=!1}=e,a=r||t;this.log("Setting up click tracking for:",t);let n=l=>{let p=l.target.closest(t);p&&(i&&this.trackedClicks.has(a)||(this.log("Button click detected:",a),i&&this.trackedClicks.add(a),this.track("button_click",{selector:t,name:a,element_text:p.textContent?.trim().slice(0,100)||"",element_id:p.id||null,element_class:p.className||null})))};this.addEventListener(document,"click",n)}enableExitIntent(e={}){let{threshold:t=50,cooldown:r=1e4,once:i=!0}=e;this.log("Enabling exit intent detection");let a=l=>{l.clientY>t||i&&this.exitIntentTriggered||this.exitIntentCooldownTimer||(this.log("Exit intent detected"),this.exitIntentTriggered=!0,this.track("exit_intent",{trigger:"mouse_leave",mouse_y:l.clientY}),i||(this.exitIntentCooldownTimer=setTimeout(()=>{this.exitIntentCooldownTimer=null},r)))};this.addEventListener(document,"mouseleave",a);let n=()=>{if(document.visibilityState==="hidden"){if(i&&this.exitIntentTriggered)return;this.log("Exit intent detected (visibility change)"),this.exitIntentTriggered=!0,this.track("exit_intent",{trigger:"visibility_change"})}};y()!=="desktop"&&this.addEventListener(document,"visibilitychange",n)}destroy(){this.log("Destroying Bliptar SDK"),this.eventListeners.forEach(({target:e,type:t,handler:r})=>{e.removeEventListener(t,r)}),this.eventListeners=[],this.renderer&&(this.renderer.destroy(),this.renderer=null),this.exitIntentCooldownTimer&&(clearTimeout(this.exitIntentCooldownTimer),this.exitIntentCooldownTimer=null),this.timeOnPageTimers.forEach(e=>clearTimeout(e)),this.timeOnPageTimers=[],this.trackedClicks.clear(),this.exitIntentTriggered=!1,this.scrollDepthThresholds.clear(),this.triggeredScrollThresholds.clear(),this.campaigns=[],this.maxScrollDepth=0,this.isInitialized=!1}async displayForm(e,t){this.log("Displaying form:",e.formId),this.config.onShow?.(e),this.renderer=new f(e,{onSubmit:async r=>{await this.submitResponse(e.formId,r,t),this.config.onSubmit?.(r)},onDismiss:()=>{this.config.onDismiss?.(),this.renderer?.destroy(),this.renderer=null}}),this.renderer.render()}async submitResponse(e,t,r){this.log("Submitting response:",e,t);let i=this.getContext(),a=new Date().toISOString();try{await fetch(`${this.config.apiUrl}/api/sdk/submit`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({session_id:this.sessionId,user_id:this.config.userId,form_id:e,campaign_id:r,answers:t,context:i,completed_at:a})})}catch(n){this.handleError(n)}}getContext(){return{pageUrl:window.location.href,referrer:document.referrer,timeOnPage:Math.floor((Date.now()-this.pageLoadTime)/1e3),scrollDepth:Math.round(this.scrollDepth*100)/100,deviceType:y()}}loadOrCreateSessionId(){let e="bliptar_session",t=sessionStorage.getItem(e);return t||(t=$(),sessionStorage.setItem(e,t)),t}addEventListener(e,t,r){e.addEventListener(t,r),this.eventListeners.push({target:e,type:t,handler:r})}log(...e){this.config.debug&&console.log("[Bliptar]",...e)}handleError(e){this.log("Error:",e),this.config.onError?.(e)}};function k(o){return new h(o)}if(typeof window<"u"){let o=document.currentScript;if(o?.dataset.apiKey){let e=k({apiKey:o.dataset.apiKey,debug:o.dataset.debug==="true"});window.bliptar=e,e.init()}}0&&(module.exports={Bliptar,createBliptar});
package/dist/index.d.cts CHANGED
@@ -26,7 +26,7 @@ interface ExitIntentOptions {
26
26
  once?: boolean;
27
27
  }
28
28
  interface BliptarInstance {
29
- init: () => void;
29
+ init: () => Promise<void>;
30
30
  identify: (userId: string, attributes?: Record<string, unknown>) => void;
31
31
  track: (event: string, data?: Record<string, unknown>) => void;
32
32
  showForm: (formId: string) => Promise<void>;
@@ -128,13 +128,22 @@ declare class Bliptar implements BliptarInstance {
128
128
  private renderer;
129
129
  private pageLoadTime;
130
130
  private scrollDepth;
131
+ private maxScrollDepth;
131
132
  private isInitialized;
132
133
  private eventListeners;
133
134
  private trackedClicks;
134
135
  private exitIntentTriggered;
135
136
  private exitIntentCooldownTimer;
137
+ private timeOnPageTimers;
138
+ private scrollDepthThresholds;
139
+ private triggeredScrollThresholds;
140
+ private campaigns;
136
141
  constructor(config: BliptarConfig);
137
- init(): void;
142
+ init(): Promise<void>;
143
+ private fetchConfigAndSetupTriggers;
144
+ private setupTriggersFromCampaigns;
145
+ private setupTimeOnPageTrigger;
146
+ private checkScrollThresholds;
138
147
  identify(userId: string, attributes?: Record<string, unknown>): void;
139
148
  track(event: string, data?: Record<string, unknown>): Promise<void>;
140
149
  showForm(formId: string): Promise<void>;
package/dist/index.d.ts CHANGED
@@ -26,7 +26,7 @@ interface ExitIntentOptions {
26
26
  once?: boolean;
27
27
  }
28
28
  interface BliptarInstance {
29
- init: () => void;
29
+ init: () => Promise<void>;
30
30
  identify: (userId: string, attributes?: Record<string, unknown>) => void;
31
31
  track: (event: string, data?: Record<string, unknown>) => void;
32
32
  showForm: (formId: string) => Promise<void>;
@@ -128,13 +128,22 @@ declare class Bliptar implements BliptarInstance {
128
128
  private renderer;
129
129
  private pageLoadTime;
130
130
  private scrollDepth;
131
+ private maxScrollDepth;
131
132
  private isInitialized;
132
133
  private eventListeners;
133
134
  private trackedClicks;
134
135
  private exitIntentTriggered;
135
136
  private exitIntentCooldownTimer;
137
+ private timeOnPageTimers;
138
+ private scrollDepthThresholds;
139
+ private triggeredScrollThresholds;
140
+ private campaigns;
136
141
  constructor(config: BliptarConfig);
137
- init(): void;
142
+ init(): Promise<void>;
143
+ private fetchConfigAndSetupTriggers;
144
+ private setupTriggersFromCampaigns;
145
+ private setupTimeOnPageTrigger;
146
+ private checkScrollThresholds;
138
147
  identify(userId: string, attributes?: Record<string, unknown>): void;
139
148
  track(event: string, data?: Record<string, unknown>): Promise<void>;
140
149
  showForm(formId: string): Promise<void>;
package/dist/index.js CHANGED
@@ -1,14 +1,14 @@
1
- var u={mode:"light",backgroundColor:"#ffffff",surfaceColor:"#f5f5f5",textPrimary:"#1a1a1a",textSecondary:"#666666",primaryColor:"#3b82f6",primaryHover:"#2563eb",borderColor:"#e0e0e0",shadowColor:"rgba(0, 0, 0, 0.15)",starColor:"#fbbf24"},h={mode:"dark",backgroundColor:"#1a1a1a",surfaceColor:"#2a2a2a",textPrimary:"#ffffff",textSecondary:"#a0a0a0",primaryColor:"#3b82f6",primaryHover:"#60a5fa",borderColor:"#333333",shadowColor:"rgba(0, 0, 0, 0.4)",starColor:"#fbbf24"};function I(i,e){try{let r=window.getComputedStyle(i).getPropertyValue(e);return r&&r!=="rgba(0, 0, 0, 0)"?r:null}catch{return null}}function S(i){try{return getComputedStyle(document.documentElement).getPropertyValue(i).trim()||null}catch{return null}}function y(i){let e=i.match(/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i);if(e)return{r:parseInt(e[1],16),g:parseInt(e[2],16),b:parseInt(e[3],16)};let t=i.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);return t?{r:parseInt(t[1],10),g:parseInt(t[2],10),b:parseInt(t[3],10)}:null}function L(i,e,t){let[r,o,a]=[i,e,t].map(n=>(n=n/255,n<=.03928?n/12.92:Math.pow((n+.055)/1.055,2.4)));return .2126*r+.7152*o+.0722*a}function v(i){let e=y(i);return e?L(e.r,e.g,e.b)<.5:!1}function f(i,e){let t=y(i);if(!t)return i;let r=o=>Math.min(255,Math.max(0,Math.round(o+e/100*255)));return`rgb(${r(t.r)}, ${r(t.g)}, ${r(t.b)})`}function P(i){return v(i)?"#ffffff":"#1a1a1a"}function A(i,e){let t={},r=null;i&&(r=document.querySelector(i)),r||(r=document.body);let o=I(r,"background-color");if(o){let a=v(o);t.mode=a?"dark":"light",t.backgroundColor=o,t.surfaceColor=f(o,a?10:-5),t.textPrimary=P(o),t.textSecondary=f(t.textPrimary,a?-30:30),t.borderColor=f(o,a?20:-15),t.shadowColor=a?"rgba(0, 0, 0, 0.4)":"rgba(0, 0, 0, 0.15)"}if(e){let a={"--primary":"primaryColor","--primary-color":"primaryColor","--accent":"primaryColor","--accent-color":"primaryColor","--brand":"primaryColor","--brand-color":"primaryColor","--color-primary":"primaryColor","--background":"backgroundColor","--bg":"backgroundColor","--bg-color":"backgroundColor","--text":"textPrimary","--text-color":"textPrimary","--foreground":"textPrimary","--border":"borderColor","--border-color":"borderColor"};for(let[n,l]of Object.entries(a)){let p=S(n);p&&(t[l]=p)}}return t.primaryColor&&(t.primaryHover=f(t.primaryColor,t.mode==="dark"?15:-10)),Object.keys(t).length>0?t:null}function k(i){let{theme:e,customTheme:t,adaptiveOptions:r}=i;if(e==="custom"&&t)return{mode:v(t.backgroundColor)?"dark":"light",backgroundColor:t.backgroundColor,surfaceColor:t.surfaceColor,textPrimary:t.textPrimary,textSecondary:t.textSecondary,primaryColor:t.primaryColor,primaryHover:t.primaryHover,borderColor:t.borderColor,shadowColor:t.shadowColor,starColor:"#fbbf24"};if(e==="adaptive"){let o=r||{fallback:"light",autoContrast:!0},a=A(o.sourceElement,o.inheritCssVars);return a?{...a.mode==="dark"?h:u,...a}:o.fallback==="dark"?h:u}return e==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?h:u:e==="dark"?h:u}function C(i){return`
2
- --bliptar-bg: ${i.backgroundColor};
3
- --bliptar-surface: ${i.surfaceColor};
4
- --bliptar-text: ${i.textPrimary};
5
- --bliptar-text-secondary: ${i.textSecondary};
6
- --bliptar-primary: ${i.primaryColor};
7
- --bliptar-primary-hover: ${i.primaryHover};
8
- --bliptar-border: ${i.borderColor};
9
- --bliptar-shadow: ${i.shadowColor};
10
- --bliptar-star: ${i.starColor};
11
- `}var g=class{constructor(e,t){this.container=null;this.currentIndex=0;this.answers={};this.form=e,this.options=t,this.theme=k(e.appearance)}render(){this.createContainer(),this.injectStyles(),this.renderQuestion()}destroy(){if(this.container){let e=this.container.querySelector(".bliptar__card");e?(e.classList.add("bliptar__card--exit"),setTimeout(()=>{this.container?.remove(),this.container=null},200)):(this.container.remove(),this.container=null)}}createContainer(){this.container=document.createElement("div"),this.container.id="bliptar-form",this.container.className=`bliptar bliptar--${this.form.appearance.position}`,document.body.appendChild(this.container)}injectStyles(){let e=document.getElementById("bliptar-styles");e&&e.remove();let t=document.createElement("style");t.id="bliptar-styles",t.textContent=this.getStyles(),document.head.appendChild(t)}getStyles(){let{appearance:e}=this.form,t=this.theme,r=e.animation||"slide-up";return`
1
+ var b={mode:"light",backgroundColor:"#ffffff",surfaceColor:"#f5f5f5",textPrimary:"#1a1a1a",textSecondary:"#666666",primaryColor:"#3b82f6",primaryHover:"#2563eb",borderColor:"#e0e0e0",shadowColor:"rgba(0, 0, 0, 0.15)",starColor:"#fbbf24"},u={mode:"dark",backgroundColor:"#1a1a1a",surfaceColor:"#2a2a2a",textPrimary:"#ffffff",textSecondary:"#a0a0a0",primaryColor:"#3b82f6",primaryHover:"#60a5fa",borderColor:"#333333",shadowColor:"rgba(0, 0, 0, 0.4)",starColor:"#fbbf24"};function E(o,e){try{let r=window.getComputedStyle(o).getPropertyValue(e);return r&&r!=="rgba(0, 0, 0, 0)"?r:null}catch{return null}}function I(o){try{return getComputedStyle(document.documentElement).getPropertyValue(o).trim()||null}catch{return null}}function y(o){let e=o.match(/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i);if(e)return{r:parseInt(e[1],16),g:parseInt(e[2],16),b:parseInt(e[3],16)};let t=o.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);return t?{r:parseInt(t[1],10),g:parseInt(t[2],10),b:parseInt(t[3],10)}:null}function L(o,e,t){let[r,i,a]=[o,e,t].map(n=>(n=n/255,n<=.03928?n/12.92:Math.pow((n+.055)/1.055,2.4)));return .2126*r+.7152*i+.0722*a}function x(o){let e=y(o);return e?L(e.r,e.g,e.b)<.5:!1}function g(o,e){let t=y(o);if(!t)return o;let r=i=>Math.min(255,Math.max(0,Math.round(i+e/100*255)));return`rgb(${r(t.r)}, ${r(t.g)}, ${r(t.b)})`}function P(o){return x(o)?"#ffffff":"#1a1a1a"}function D(o,e){let t={},r=null;o&&(r=document.querySelector(o)),r||(r=document.body);let i=E(r,"background-color");if(i){let a=x(i);t.mode=a?"dark":"light",t.backgroundColor=i,t.surfaceColor=g(i,a?10:-5),t.textPrimary=P(i),t.textSecondary=g(t.textPrimary,a?-30:30),t.borderColor=g(i,a?20:-15),t.shadowColor=a?"rgba(0, 0, 0, 0.4)":"rgba(0, 0, 0, 0.15)"}if(e){let a={"--primary":"primaryColor","--primary-color":"primaryColor","--accent":"primaryColor","--accent-color":"primaryColor","--brand":"primaryColor","--brand-color":"primaryColor","--color-primary":"primaryColor","--background":"backgroundColor","--bg":"backgroundColor","--bg-color":"backgroundColor","--text":"textPrimary","--text-color":"textPrimary","--foreground":"textPrimary","--border":"borderColor","--border-color":"borderColor"};for(let[n,l]of Object.entries(a)){let c=I(n);c&&l!=="mode"&&(t[l]=c)}}return t.primaryColor&&(t.primaryHover=g(t.primaryColor,t.mode==="dark"?15:-10)),Object.keys(t).length>0?t:null}function k(o){let{theme:e,customTheme:t,adaptiveOptions:r}=o;if(e==="custom"&&t)return{mode:x(t.backgroundColor)?"dark":"light",backgroundColor:t.backgroundColor,surfaceColor:t.surfaceColor,textPrimary:t.textPrimary,textSecondary:t.textSecondary,primaryColor:t.primaryColor,primaryHover:t.primaryHover,borderColor:t.borderColor,shadowColor:t.shadowColor,starColor:"#fbbf24"};if(e==="adaptive"){let i=r||{fallback:"light",autoContrast:!0},a=D(i.sourceElement,i.inheritCssVars);return a?{...a.mode==="dark"?u:b,...a}:i.fallback==="dark"?u:b}return e==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?u:b:e==="dark"?u:b}function C(o){return`
2
+ --bliptar-bg: ${o.backgroundColor};
3
+ --bliptar-surface: ${o.surfaceColor};
4
+ --bliptar-text: ${o.textPrimary};
5
+ --bliptar-text-secondary: ${o.textSecondary};
6
+ --bliptar-primary: ${o.primaryColor};
7
+ --bliptar-primary-hover: ${o.primaryHover};
8
+ --bliptar-border: ${o.borderColor};
9
+ --bliptar-shadow: ${o.shadowColor};
10
+ --bliptar-star: ${o.starColor};
11
+ `}var f=class{constructor(e,t){this.container=null;this.currentIndex=0;this.answers={};this.form=e,this.options=t,this.theme=k(e.appearance)}render(){this.createContainer(),this.injectStyles(),this.renderQuestion()}destroy(){if(this.container){let e=this.container.querySelector(".bliptar__card");e?(e.classList.add("bliptar__card--exit"),setTimeout(()=>{this.container?.remove(),this.container=null},200)):(this.container.remove(),this.container=null)}}createContainer(){this.container=document.createElement("div"),this.container.id="bliptar-form",this.container.className=`bliptar bliptar--${this.form.appearance.position}`,document.body.appendChild(this.container)}injectStyles(){let e=document.getElementById("bliptar-styles");e&&e.remove();let t=document.createElement("style");t.id="bliptar-styles",t.textContent=this.getStyles(),document.head.appendChild(t)}getStyles(){let{appearance:e}=this.form,t=this.theme,r=e.animation||"slide-up";return`
12
12
  :root { ${C(t)} }
13
13
 
14
14
  .bliptar {
@@ -372,20 +372,20 @@ var u={mode:"light",backgroundColor:"#ffffff",surfaceColor:"#f5f5f5",textPrimary
372
372
  }
373
373
 
374
374
  @media print { .bliptar { display: none !important; } }
375
- `}getAnimationStyles(e){switch(e){case"slide-up":return"animation: bliptar-slide-up 0.35s cubic-bezier(0.16, 1, 0.3, 1);";case"fade":return"animation: bliptar-fade 0.3s ease-out;";case"scale":return"animation: bliptar-scale 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);";case"none":return"";default:return"animation: bliptar-slide-up 0.35s cubic-bezier(0.16, 1, 0.3, 1);"}}renderQuestion(){if(!this.container)return;let{questions:e,appearance:t,behavior:r}=this.form,o=e[this.currentIndex],a=this.currentIndex===e.length-1,n="";if(t.backdrop&&(n+='<div class="bliptar__backdrop"></div>'),n+='<div class="bliptar__card">',r.allowDismiss&&(n+=`<button class="bliptar__close" aria-label="Close">
375
+ `}getAnimationStyles(e){switch(e){case"slide-up":return"animation: bliptar-slide-up 0.35s cubic-bezier(0.16, 1, 0.3, 1);";case"fade":return"animation: bliptar-fade 0.3s ease-out;";case"scale":return"animation: bliptar-scale 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);";case"none":return"";default:return"animation: bliptar-slide-up 0.35s cubic-bezier(0.16, 1, 0.3, 1);"}}renderQuestion(){if(!this.container)return;let{questions:e,appearance:t,behavior:r}=this.form,i=e[this.currentIndex],a=this.currentIndex===e.length-1,n="";if(t.backdrop&&(n+='<div class="bliptar__backdrop"></div>'),n+='<div class="bliptar__card">',r.allowDismiss&&(n+=`<button class="bliptar__close" aria-label="Close">
376
376
  <svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
377
377
  <path d="M1 1l12 12M1 13L13 1"/>
378
378
  </svg>
379
- </button>`),r.showProgress&&e.length>1){n+='<div class="bliptar__progress">';for(let l=0;l<e.length;l++)n+=`<div class="bliptar__progress-bar${l<=this.currentIndex?" bliptar__progress-bar--active":""}"></div>`;n+="</div>"}n+=`<div class="bliptar__question">${this.escapeHtml(o.question)}</div>`,n+=this.renderAnswerOptions(o),(!r.autoAdvance||o.type==="text-input")&&(n+='<div class="bliptar__nav">',n+=this.currentIndex>0?'<button class="bliptar__nav-btn bliptar__nav-btn--back">Back</button>':"<div></div>",n+=`<button class="bliptar__nav-btn bliptar__nav-btn--next">${a?"Submit":"Continue"}</button>`,n+="</div>"),n+="</div>",this.container.innerHTML=n,this.attachEventListeners()}escapeHtml(e){let t=document.createElement("div");return t.textContent=e,t.innerHTML}renderAnswerOptions(e){let t=`q${this.currentIndex}`,r=this.answers[t];switch(e.type){case"binary":return`<div class="bliptar__options">
379
+ </button>`),r.showProgress&&e.length>1){n+='<div class="bliptar__progress">';for(let l=0;l<e.length;l++)n+=`<div class="bliptar__progress-bar${l<=this.currentIndex?" bliptar__progress-bar--active":""}"></div>`;n+="</div>"}n+=`<div class="bliptar__question">${this.escapeHtml(i.question)}</div>`,n+=this.renderAnswerOptions(i),(!r.autoAdvance||i.type==="text-input")&&(n+='<div class="bliptar__nav">',n+=this.currentIndex>0?'<button class="bliptar__nav-btn bliptar__nav-btn--back">Back</button>':"<div></div>",n+=`<button class="bliptar__nav-btn bliptar__nav-btn--next">${a?"Submit":"Continue"}</button>`,n+="</div>"),n+="</div>",this.container.innerHTML=n,this.attachEventListeners()}escapeHtml(e){let t=document.createElement("div");return t.textContent=e,t.innerHTML}renderAnswerOptions(e){let t=`q${this.currentIndex}`,r=this.answers[t];switch(e.type){case"binary":return`<div class="bliptar__options">
380
380
  <button class="bliptar__btn${r===!0?" bliptar__btn--selected":""}" data-value="true">
381
381
  ${e.style==="thumbs"?"\u{1F44D} ":""}${this.escapeHtml(e.positiveLabel)}
382
382
  </button>
383
383
  <button class="bliptar__btn${r===!1?" bliptar__btn--selected":""}" data-value="false">
384
384
  ${e.style==="thumbs"?"\u{1F44E} ":""}${this.escapeHtml(e.negativeLabel)}
385
385
  </button>
386
- </div>`;case"star-rating":let o='<div class="bliptar__stars">';for(let s=1;s<=e.maxStars;s++)o+=`<span class="bliptar__star${r>=s?" bliptar__star--active":""}" data-value="${s}">\u2605</span>`;return o+"</div>";case"emoji-scale":let a=["\u{1F61E}","\u{1F615}","\u{1F610}","\u{1F642}","\u{1F60A}"],n=["\u{1F61E}","\u{1F610}","\u{1F60A}"],l=e.scale===3?n:a,p='<div class="bliptar__emojis">';return l.forEach((s,d)=>{p+=`<span class="bliptar__emoji${r===d+1?" bliptar__emoji--active":""}" data-value="${d+1}" title="${e.labels?.[d]||""}">${s}</span>`}),p+"</div>";case"nps":let c='<div class="bliptar__nps">';for(let s=0;s<=10;s++)c+=`<button class="bliptar__nps-btn${r===s?" bliptar__nps-btn--selected":""}" data-value="${s}">${s}</button>`;return c+=`</div><div class="bliptar__nps-labels"><span>${this.escapeHtml(e.lowLabel)}</span><span>${this.escapeHtml(e.highLabel)}</span></div>`,c;case"single-choice":let b='<div class="bliptar__options">';return e.options.forEach(s=>{b+=`<button class="bliptar__btn${r===s?" bliptar__btn--selected":""}" data-value="${this.escapeHtml(s)}">${this.escapeHtml(s)}</button>`}),b+"</div>";case"text-input":return`<textarea class="bliptar__textarea" placeholder="${this.escapeHtml(e.placeholder)}" maxlength="${e.maxLength}">${r||""}</textarea>`;default:return""}}attachEventListeners(){if(!this.container)return;let e=this.form.questions[this.currentIndex],{behavior:t}=this.form,r=this.container.querySelector(".bliptar__close");r&&r.addEventListener("click",()=>this.options.onDismiss());let o=this.container.querySelector(".bliptar__backdrop");o&&t.allowDismiss&&o.addEventListener("click",()=>this.options.onDismiss());let a=s=>{this.answers[`q${this.currentIndex}`]=s,t.autoAdvance&&e.type!=="text-input"?setTimeout(()=>this.goNext(),t.autoAdvanceDelay||300):this.renderQuestion()};this.container.querySelectorAll(".bliptar__btn, .bliptar__nps-btn").forEach(s=>{s.addEventListener("click",()=>{let d=s.dataset.value;e.type==="binary"?a(d==="true"):e.type==="nps"?a(parseInt(d,10)):a(d)})});let n=this.container.querySelectorAll(".bliptar__star");n.forEach((s,d)=>{s.addEventListener("mouseenter",()=>{n.forEach((x,E)=>x.classList.toggle("bliptar__star--active",E<=d))}),s.addEventListener("click",()=>a(parseInt(s.dataset.value,10)))});let l=this.container.querySelector(".bliptar__stars");l&&l.addEventListener("mouseleave",()=>{let s=this.answers[`q${this.currentIndex}`];n.forEach((d,x)=>d.classList.toggle("bliptar__star--active",s?x<s:!1))}),this.container.querySelectorAll(".bliptar__emoji").forEach(s=>{s.addEventListener("click",()=>a(parseInt(s.dataset.value,10)))});let p=this.container.querySelector(".bliptar__textarea");p&&(p.addEventListener("input",()=>{this.answers[`q${this.currentIndex}`]=p.value}),setTimeout(()=>p.focus(),100));let c=this.container.querySelector(".bliptar__nav-btn--back");c&&c.addEventListener("click",()=>this.goBack());let b=this.container.querySelector(".bliptar__nav-btn--next");b&&b.addEventListener("click",()=>this.goNext())}goBack(){this.currentIndex>0&&(this.currentIndex--,this.renderQuestion())}async goNext(){this.currentIndex===this.form.questions.length-1?await this.submit():(this.currentIndex++,this.renderQuestion())}async submit(){if(this.container){let e=this.container.querySelector(".bliptar__card");e&&(e.innerHTML='<div class="bliptar__loading"><div class="bliptar__spinner"></div></div>')}try{await this.options.onSubmit(this.answers),this.form.behavior.showThankYou?(this.renderThankYou(),setTimeout(()=>this.options.onDismiss(),this.form.behavior.thankYouDuration||2e3)):this.options.onDismiss()}catch{this.options.onDismiss()}}renderThankYou(){if(!this.container)return;let{appearance:e,behavior:t}=this.form,r="";e.backdrop&&(r+='<div class="bliptar__backdrop"></div>'),r+=`<div class="bliptar__card">
386
+ </div>`;case"star-rating":let i='<div class="bliptar__stars">';for(let s=1;s<=e.maxStars;s++)i+=`<span class="bliptar__star${r>=s?" bliptar__star--active":""}" data-value="${s}">\u2605</span>`;return i+"</div>";case"emoji-scale":let a=["\u{1F61E}","\u{1F615}","\u{1F610}","\u{1F642}","\u{1F60A}"],n=["\u{1F61E}","\u{1F610}","\u{1F60A}"],l=e.scale===3?n:a,c='<div class="bliptar__emojis">';return l.forEach((s,d)=>{c+=`<span class="bliptar__emoji${r===d+1?" bliptar__emoji--active":""}" data-value="${d+1}" title="${e.labels?.[d]||""}">${s}</span>`}),c+"</div>";case"nps":let p='<div class="bliptar__nps">';for(let s=0;s<=10;s++)p+=`<button class="bliptar__nps-btn${r===s?" bliptar__nps-btn--selected":""}" data-value="${s}">${s}</button>`;return p+=`</div><div class="bliptar__nps-labels"><span>${this.escapeHtml(e.lowLabel)}</span><span>${this.escapeHtml(e.highLabel)}</span></div>`,p;case"single-choice":let h='<div class="bliptar__options">';return e.options.forEach(s=>{h+=`<button class="bliptar__btn${r===s?" bliptar__btn--selected":""}" data-value="${this.escapeHtml(s)}">${this.escapeHtml(s)}</button>`}),h+"</div>";case"text-input":return`<textarea class="bliptar__textarea" placeholder="${this.escapeHtml(e.placeholder)}" maxlength="${e.maxLength}">${r||""}</textarea>`;default:return""}}attachEventListeners(){if(!this.container)return;let e=this.form.questions[this.currentIndex],{behavior:t}=this.form,r=this.container.querySelector(".bliptar__close");r&&r.addEventListener("click",()=>this.options.onDismiss());let i=this.container.querySelector(".bliptar__backdrop");i&&t.allowDismiss&&i.addEventListener("click",()=>this.options.onDismiss());let a=s=>{this.answers[`q${this.currentIndex}`]=s,t.autoAdvance&&e.type!=="text-input"?setTimeout(()=>this.goNext(),t.autoAdvanceDelay||300):this.renderQuestion()};this.container.querySelectorAll(".bliptar__btn, .bliptar__nps-btn").forEach(s=>{s.addEventListener("click",()=>{let d=s.dataset.value;e.type==="binary"?a(d==="true"):e.type==="nps"?a(parseInt(d,10)):a(d)})});let n=this.container.querySelectorAll(".bliptar__star");n.forEach((s,d)=>{s.addEventListener("mouseenter",()=>{n.forEach((v,S)=>v.classList.toggle("bliptar__star--active",S<=d))}),s.addEventListener("click",()=>a(parseInt(s.dataset.value,10)))});let l=this.container.querySelector(".bliptar__stars");l&&l.addEventListener("mouseleave",()=>{let s=this.answers[`q${this.currentIndex}`];n.forEach((d,v)=>d.classList.toggle("bliptar__star--active",s?v<s:!1))}),this.container.querySelectorAll(".bliptar__emoji").forEach(s=>{s.addEventListener("click",()=>a(parseInt(s.dataset.value,10)))});let c=this.container.querySelector(".bliptar__textarea");c&&(c.addEventListener("input",()=>{this.answers[`q${this.currentIndex}`]=c.value}),setTimeout(()=>c.focus(),100));let p=this.container.querySelector(".bliptar__nav-btn--back");p&&p.addEventListener("click",()=>this.goBack());let h=this.container.querySelector(".bliptar__nav-btn--next");h&&h.addEventListener("click",()=>this.goNext())}goBack(){this.currentIndex>0&&(this.currentIndex--,this.renderQuestion())}async goNext(){this.currentIndex===this.form.questions.length-1?await this.submit():(this.currentIndex++,this.renderQuestion())}async submit(){if(this.container){let e=this.container.querySelector(".bliptar__card");e&&(e.innerHTML='<div class="bliptar__loading"><div class="bliptar__spinner"></div></div>')}try{await this.options.onSubmit(this.answers),this.form.behavior.showThankYou?(this.renderThankYou(),setTimeout(()=>this.options.onDismiss(),this.form.behavior.thankYouDuration||2e3)):this.options.onDismiss()}catch{this.options.onDismiss()}}renderThankYou(){if(!this.container)return;let{appearance:e,behavior:t}=this.form,r="";e.backdrop&&(r+='<div class="bliptar__backdrop"></div>'),r+=`<div class="bliptar__card">
387
387
  <div class="bliptar__thankyou">
388
388
  <div class="bliptar__thankyou-emoji">\u{1F389}</div>
389
389
  <div class="bliptar__thankyou-text">${this.escapeHtml(t.thankYouMessage||"Thank you for your feedback!")}</div>
390
390
  </div>
391
- </div>`,this.container.innerHTML=r}};function w(){let i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",e="";for(let t=0;t<16;t++)e+=i.charAt(Math.floor(Math.random()*i.length));return`sess_${e}`}function _(){let i=navigator.userAgent;return/tablet|ipad|playbook|silk/i.test(i)?"tablet":/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/i.test(i)?"mobile":"desktop"}function $(i,e){let t;return(...r)=>{clearTimeout(t),t=setTimeout(()=>i(...r),e)}}var B="https://api.bliptar.com",m=class{constructor(e){this.renderer=null;this.scrollDepth=0;this.isInitialized=!1;this.eventListeners=[];this.trackedClicks=new Set;this.exitIntentTriggered=!1;this.exitIntentCooldownTimer=null;this.config={apiUrl:B,debug:!1,onShow:()=>{},onSubmit:()=>{},onDismiss:()=>{},onError:()=>{},...e},this.sessionId=this.loadOrCreateSessionId(),this.pageLoadTime=Date.now()}init(){if(this.isInitialized)return;this.isInitialized=!0,this.log("Initializing Bliptar SDK");let e=$(()=>{let t=document.documentElement,r=window.scrollY||t.scrollTop,o=t.scrollHeight-t.clientHeight;this.scrollDepth=o>0?r/o:0},100);this.addEventListener(window,"scroll",e),this.track("page_view")}identify(e,t){this.config.userId=e,this.config.userAttributes={...this.config.userAttributes,...t},this.log("User identified:",e)}async track(e,t){this.log("Tracking event:",e,t);let r=this.getContext();try{let o=await fetch(`${this.config.apiUrl}/api/sdk/trigger`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({session_id:this.sessionId,user_id:this.config.userId,event:e,event_data:t||{},context:r})});if(!o.ok)throw new Error(`API error: ${o.status}`);let a=await o.json();a.show_form&&a.form&&await this.displayForm(a.form,a.campaign_id)}catch(o){this.handleError(o)}}async showForm(e){this.log("Manually showing form:",e);try{let t=await fetch(`${this.config.apiUrl}/api/sdk/form/${e}`,{headers:{Authorization:`Bearer ${this.config.apiKey}`}});if(!t.ok)throw new Error(`Failed to fetch form: ${t.status}`);let r=await t.json();await this.displayForm(r)}catch(t){this.handleError(t)}}trackClicks(e){let{selector:t,name:r,once:o=!1}=e,a=r||t;this.log("Setting up click tracking for:",t);let n=l=>{let c=l.target.closest(t);c&&(o&&this.trackedClicks.has(a)||(this.log("Button click detected:",a),o&&this.trackedClicks.add(a),this.track("button_click",{selector:t,name:a,element_text:c.textContent?.trim().slice(0,100)||"",element_id:c.id||null,element_class:c.className||null})))};this.addEventListener(document,"click",n)}enableExitIntent(e={}){let{threshold:t=50,cooldown:r=1e4,once:o=!0}=e;this.log("Enabling exit intent detection");let a=l=>{l.clientY>t||o&&this.exitIntentTriggered||this.exitIntentCooldownTimer||(this.log("Exit intent detected"),this.exitIntentTriggered=!0,this.track("exit_intent",{trigger:"mouse_leave",mouse_y:l.clientY}),o||(this.exitIntentCooldownTimer=setTimeout(()=>{this.exitIntentCooldownTimer=null},r)))};this.addEventListener(document,"mouseleave",a);let n=()=>{if(document.visibilityState==="hidden"){if(o&&this.exitIntentTriggered)return;this.log("Exit intent detected (visibility change)"),this.exitIntentTriggered=!0,this.track("exit_intent",{trigger:"visibility_change"})}};_()!=="desktop"&&this.addEventListener(document,"visibilitychange",n)}destroy(){this.log("Destroying Bliptar SDK"),this.eventListeners.forEach(({target:e,type:t,handler:r})=>{e.removeEventListener(t,r)}),this.eventListeners=[],this.renderer&&(this.renderer.destroy(),this.renderer=null),this.exitIntentCooldownTimer&&(clearTimeout(this.exitIntentCooldownTimer),this.exitIntentCooldownTimer=null),this.trackedClicks.clear(),this.exitIntentTriggered=!1,this.isInitialized=!1}async displayForm(e,t){this.log("Displaying form:",e.formId),this.config.onShow?.(e),this.renderer=new g(e,{onSubmit:async r=>{await this.submitResponse(e.formId,r,t),this.config.onSubmit?.(r)},onDismiss:()=>{this.config.onDismiss?.(),this.renderer?.destroy(),this.renderer=null}}),this.renderer.render()}async submitResponse(e,t,r){this.log("Submitting response:",e,t);let o=this.getContext(),a=new Date().toISOString();try{await fetch(`${this.config.apiUrl}/api/sdk/submit`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({session_id:this.sessionId,user_id:this.config.userId,form_id:e,campaign_id:r,answers:t,context:o,completed_at:a})})}catch(n){this.handleError(n)}}getContext(){return{pageUrl:window.location.href,referrer:document.referrer,timeOnPage:Math.floor((Date.now()-this.pageLoadTime)/1e3),scrollDepth:Math.round(this.scrollDepth*100)/100,deviceType:_()}}loadOrCreateSessionId(){let e="bliptar_session",t=sessionStorage.getItem(e);return t||(t=w(),sessionStorage.setItem(e,t)),t}addEventListener(e,t,r){e.addEventListener(t,r),this.eventListeners.push({target:e,type:t,handler:r})}log(...e){this.config.debug&&console.log("[Bliptar]",...e)}handleError(e){this.log("Error:",e),this.config.onError?.(e)}};function T(i){return new m(i)}if(typeof window<"u"){let i=document.currentScript;if(i?.dataset.apiKey){let e=T({apiKey:i.dataset.apiKey,debug:i.dataset.debug==="true"});window.bliptar=e,e.init()}}export{m as Bliptar,T as createBliptar};
391
+ </div>`,this.container.innerHTML=r}};function w(){let o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",e="";for(let t=0;t<16;t++)e+=o.charAt(Math.floor(Math.random()*o.length));return`sess_${e}`}function _(){let o=navigator.userAgent;return/tablet|ipad|playbook|silk/i.test(o)?"tablet":/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/i.test(o)?"mobile":"desktop"}function T(o,e){let t;return(...r)=>{clearTimeout(t),t=setTimeout(()=>o(...r),e)}}var A="https://api.bliptar.com",m=class{constructor(e){this.renderer=null;this.scrollDepth=0;this.maxScrollDepth=0;this.isInitialized=!1;this.eventListeners=[];this.trackedClicks=new Set;this.exitIntentTriggered=!1;this.exitIntentCooldownTimer=null;this.timeOnPageTimers=[];this.scrollDepthThresholds=new Set;this.triggeredScrollThresholds=new Set;this.campaigns=[];this.config={apiUrl:A,debug:!1,onShow:()=>{},onSubmit:()=>{},onDismiss:()=>{},onError:()=>{},...e},this.sessionId=this.loadOrCreateSessionId(),this.pageLoadTime=Date.now()}async init(){if(this.isInitialized)return;this.isInitialized=!0,this.log("Initializing Bliptar SDK"),await this.fetchConfigAndSetupTriggers();let e=T(()=>{let t=document.documentElement,r=window.scrollY||t.scrollTop,i=t.scrollHeight-t.clientHeight;this.scrollDepth=i>0?r/i:0;let a=Math.round(this.scrollDepth*100);a>this.maxScrollDepth&&(this.maxScrollDepth=a,this.checkScrollThresholds(a))},100);this.addEventListener(window,"scroll",e),this.track("page_view")}async fetchConfigAndSetupTriggers(){try{let e=await fetch(`${this.config.apiUrl}/api/sdk/config`,{headers:{Authorization:`Bearer ${this.config.apiKey}`}});if(!e.ok){this.log("Failed to fetch config:",e.status);return}let t=await e.json();this.campaigns=t.campaigns||[],this.log("Loaded campaigns:",this.campaigns.length),this.setupTriggersFromCampaigns()}catch(e){this.handleError(e)}}setupTriggersFromCampaigns(){let e=new Set;for(let t of this.campaigns){if(e.add(t.trigger_event),t.trigger_event==="scroll_depth"){let r=t.trigger_conditions?.find(i=>i.field==="scroll_depth_percent");r?.value&&this.scrollDepthThresholds.add(Number(r.value))}if(t.trigger_event==="time_on_page"){let r=t.trigger_conditions?.find(i=>i.field==="time_on_page_seconds");if(r?.value){let i=Number(r.value);this.setupTimeOnPageTrigger(i)}}}this.log("Active trigger types:",Array.from(e)),e.has("exit_intent")&&(this.log("Auto-enabling exit intent detection"),this.enableExitIntent({once:!0})),this.scrollDepthThresholds.size>0&&this.log("Scroll depth thresholds:",Array.from(this.scrollDepthThresholds))}setupTimeOnPageTrigger(e){this.log("Setting up time on page trigger:",e,"seconds");let t=setTimeout(()=>{this.track("time_on_page",{seconds:e,actual_time:Math.floor((Date.now()-this.pageLoadTime)/1e3)})},e*1e3);this.timeOnPageTimers.push(t)}checkScrollThresholds(e){for(let t of this.scrollDepthThresholds)e>=t&&!this.triggeredScrollThresholds.has(t)&&(this.triggeredScrollThresholds.add(t),this.log("Scroll threshold reached:",t+"%"),this.track("scroll_depth",{threshold:t,actual_depth:e}))}identify(e,t){this.config.userId=e,this.config.userAttributes={...this.config.userAttributes,...t},this.log("User identified:",e)}async track(e,t){this.log("Tracking event:",e,t);let r=this.getContext();try{let i=await fetch(`${this.config.apiUrl}/api/sdk/trigger`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({session_id:this.sessionId,user_id:this.config.userId,event:e,event_data:t||{},context:r})});if(!i.ok)throw new Error(`API error: ${i.status}`);let a=await i.json();a.show_form&&a.form&&await this.displayForm(a.form,a.campaign_id)}catch(i){this.handleError(i)}}async showForm(e){this.log("Manually showing form:",e);try{let t=await fetch(`${this.config.apiUrl}/api/sdk/form/${e}`,{headers:{Authorization:`Bearer ${this.config.apiKey}`}});if(!t.ok)throw new Error(`Failed to fetch form: ${t.status}`);let r=await t.json();await this.displayForm(r)}catch(t){this.handleError(t)}}trackClicks(e){let{selector:t,name:r,once:i=!1}=e,a=r||t;this.log("Setting up click tracking for:",t);let n=l=>{let p=l.target.closest(t);p&&(i&&this.trackedClicks.has(a)||(this.log("Button click detected:",a),i&&this.trackedClicks.add(a),this.track("button_click",{selector:t,name:a,element_text:p.textContent?.trim().slice(0,100)||"",element_id:p.id||null,element_class:p.className||null})))};this.addEventListener(document,"click",n)}enableExitIntent(e={}){let{threshold:t=50,cooldown:r=1e4,once:i=!0}=e;this.log("Enabling exit intent detection");let a=l=>{l.clientY>t||i&&this.exitIntentTriggered||this.exitIntentCooldownTimer||(this.log("Exit intent detected"),this.exitIntentTriggered=!0,this.track("exit_intent",{trigger:"mouse_leave",mouse_y:l.clientY}),i||(this.exitIntentCooldownTimer=setTimeout(()=>{this.exitIntentCooldownTimer=null},r)))};this.addEventListener(document,"mouseleave",a);let n=()=>{if(document.visibilityState==="hidden"){if(i&&this.exitIntentTriggered)return;this.log("Exit intent detected (visibility change)"),this.exitIntentTriggered=!0,this.track("exit_intent",{trigger:"visibility_change"})}};_()!=="desktop"&&this.addEventListener(document,"visibilitychange",n)}destroy(){this.log("Destroying Bliptar SDK"),this.eventListeners.forEach(({target:e,type:t,handler:r})=>{e.removeEventListener(t,r)}),this.eventListeners=[],this.renderer&&(this.renderer.destroy(),this.renderer=null),this.exitIntentCooldownTimer&&(clearTimeout(this.exitIntentCooldownTimer),this.exitIntentCooldownTimer=null),this.timeOnPageTimers.forEach(e=>clearTimeout(e)),this.timeOnPageTimers=[],this.trackedClicks.clear(),this.exitIntentTriggered=!1,this.scrollDepthThresholds.clear(),this.triggeredScrollThresholds.clear(),this.campaigns=[],this.maxScrollDepth=0,this.isInitialized=!1}async displayForm(e,t){this.log("Displaying form:",e.formId),this.config.onShow?.(e),this.renderer=new f(e,{onSubmit:async r=>{await this.submitResponse(e.formId,r,t),this.config.onSubmit?.(r)},onDismiss:()=>{this.config.onDismiss?.(),this.renderer?.destroy(),this.renderer=null}}),this.renderer.render()}async submitResponse(e,t,r){this.log("Submitting response:",e,t);let i=this.getContext(),a=new Date().toISOString();try{await fetch(`${this.config.apiUrl}/api/sdk/submit`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({session_id:this.sessionId,user_id:this.config.userId,form_id:e,campaign_id:r,answers:t,context:i,completed_at:a})})}catch(n){this.handleError(n)}}getContext(){return{pageUrl:window.location.href,referrer:document.referrer,timeOnPage:Math.floor((Date.now()-this.pageLoadTime)/1e3),scrollDepth:Math.round(this.scrollDepth*100)/100,deviceType:_()}}loadOrCreateSessionId(){let e="bliptar_session",t=sessionStorage.getItem(e);return t||(t=w(),sessionStorage.setItem(e,t)),t}addEventListener(e,t,r){e.addEventListener(t,r),this.eventListeners.push({target:e,type:t,handler:r})}log(...e){this.config.debug&&console.log("[Bliptar]",...e)}handleError(e){this.log("Error:",e),this.config.onError?.(e)}};function $(o){return new m(o)}if(typeof window<"u"){let o=document.currentScript;if(o?.dataset.apiKey){let e=$({apiKey:o.dataset.apiKey,debug:o.dataset.debug==="true"});window.bliptar=e,e.init()}}export{m as Bliptar,$ as createBliptar};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bliptarjs/sdk",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Bliptar SDK - Lightweight micro-feedback for web apps",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",