@bliptarjs/sdk 0.2.0 → 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,397 +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
- apiUrl: 'https://api.bliptar.com', // optional
56
- debug: false, // optional
57
- userId: 'user-123', // optional
58
- userAttributes: { plan: 'pro' }, // optional
59
- onShow: (form) => console.log('Showing form:', form.formId),
60
- onSubmit: (answers) => console.log('Submitted:', answers),
61
- onDismiss: () => console.log('Form dismissed'),
62
- 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
63
89
  });
64
90
  ```
65
91
 
66
- #### Configuration Options
67
-
68
- | Option | Type | Default | Description |
69
- |--------|------|---------|-------------|
70
- | `apiKey` | `string` | **required** | Your Bliptar API key |
71
- | `apiUrl` | `string` | `https://api.bliptar.com` | API endpoint URL |
72
- | `debug` | `boolean` | `false` | Enable console logging for debugging |
73
- | `userId` | `string` | - | Pre-set user ID for attribution |
74
- | `userAttributes` | `object` | - | Pre-set user attributes for targeting |
75
- | `onShow` | `function` | - | Callback when a form is displayed |
76
- | `onSubmit` | `function` | - | Callback when a response is submitted |
77
- | `onDismiss` | `function` | - | Callback when a form is dismissed |
78
- | `onError` | `function` | - | Callback when an error occurs |
79
-
80
- ---
92
+ ## API Reference
81
93
 
82
94
  ### `init()`
83
95
 
84
- 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.
85
97
 
86
98
  ```javascript
87
- bliptar.init();
99
+ await bliptar.init();
88
100
  ```
89
101
 
90
- > **Note:** Call this once after creating the instance, typically when your app loads.
91
-
92
- ---
93
-
94
102
  ### `identify(userId, attributes?)`
95
103
 
96
- Associates feedback with a specific user. Call this when a user logs in.
104
+ Associates feedback with a user. Call when user logs in.
97
105
 
98
106
  ```javascript
99
- // Basic identification
100
- bliptar.identify('user-123');
101
-
102
- // With attributes for targeting
103
107
  bliptar.identify('user-123', {
104
108
  email: 'user@example.com',
105
109
  plan: 'pro',
106
- company: 'Acme Inc',
107
- signupDate: '2024-01-15',
108
110
  });
109
111
  ```
110
112
 
111
- User attributes can be used in campaign targeting rules (e.g., only show to "pro" plan users).
112
-
113
- ---
114
-
115
113
  ### `track(event, data?)`
116
114
 
117
- Tracks a custom event that may trigger a feedback form.
115
+ Triggers a custom event. Use with "Custom Event" campaign trigger.
118
116
 
119
117
  ```javascript
120
- // Simple event
121
- bliptar.track('checkout_complete');
122
-
123
- // With event data
124
118
  bliptar.track('checkout_complete', {
125
- orderId: 'order-456',
119
+ orderId: 'order-123',
126
120
  total: 99.99,
127
- items: 3,
128
- });
129
-
130
- // Feature usage
131
- bliptar.track('feature_used', {
132
- feature: 'export_pdf',
133
- duration: 5000,
134
121
  });
135
122
  ```
136
123
 
137
- Configure campaigns in the dashboard to trigger forms based on these events.
138
-
139
- ---
140
-
141
- ### `trackClicks(options)`
142
-
143
- Sets up click tracking on specific elements. When a user clicks a matching element, it fires a `button_click` event that can trigger campaigns.
144
-
145
- ```javascript
146
- // Track clicks on a specific button
147
- bliptar.trackClicks({
148
- selector: '#upgrade-button',
149
- name: 'upgrade-cta',
150
- });
151
-
152
- // Track clicks on all pricing buttons
153
- bliptar.trackClicks({
154
- selector: '.pricing-cta',
155
- name: 'pricing-click',
156
- });
157
-
158
- // Track only the first click
159
- bliptar.trackClicks({
160
- selector: '.signup-button',
161
- name: 'signup-click',
162
- once: true,
163
- });
164
- ```
165
-
166
- #### Options
167
-
168
- | Option | Type | Default | Description |
169
- |--------|------|---------|-------------|
170
- | `selector` | `string` | **required** | CSS selector for elements to track |
171
- | `name` | `string` | `selector` | Friendly name for the click target |
172
- | `once` | `boolean` | `false` | Only track the first click per session |
173
-
174
- #### Event Data Sent
175
-
176
- When a click is detected, the following data is sent:
177
-
178
- ```javascript
179
- {
180
- selector: '.pricing-cta',
181
- name: 'pricing-click',
182
- element_text: 'Get Started',
183
- element_id: 'cta-btn',
184
- element_class: 'pricing-cta btn-primary'
185
- }
186
- ```
187
-
188
- ---
189
-
190
- ### `enableExitIntent(options?)`
191
-
192
- Enables exit intent detection. Triggers an `exit_intent` event when a user appears to be leaving the page.
193
-
194
- ```javascript
195
- // Basic usage (triggers once per session)
196
- bliptar.enableExitIntent();
197
-
198
- // Custom configuration
199
- bliptar.enableExitIntent({
200
- threshold: 50, // Pixels from top of viewport
201
- cooldown: 10000, // Ms before can trigger again
202
- once: true, // Only trigger once per session
203
- });
204
- ```
205
-
206
- #### Options
207
-
208
- | Option | Type | Default | Description |
209
- |--------|------|---------|-------------|
210
- | `threshold` | `number` | `50` | Distance in pixels from top of viewport to trigger |
211
- | `cooldown` | `number` | `10000` | Milliseconds before exit intent can trigger again |
212
- | `once` | `boolean` | `true` | Only trigger once per session |
213
-
214
- #### How It Works
215
-
216
- - **Desktop:** Detects when the mouse cursor leaves the viewport from the top (user moving to close tab, click back, or navigate away)
217
- - **Mobile:** Detects when the page visibility changes (user switching apps or tabs)
218
-
219
- ---
220
-
221
124
  ### `showForm(formId)`
222
125
 
223
- Manually displays a specific form, bypassing campaign rules.
126
+ Manually show a specific form, bypassing campaign rules.
224
127
 
225
128
  ```javascript
226
- // Show a form programmatically
227
129
  await bliptar.showForm('form_abc123');
228
-
229
- // Example: Show after completing onboarding
230
- function completeOnboarding() {
231
- // ... onboarding logic
232
- bliptar.showForm('form_onboarding_feedback');
233
- }
234
130
  ```
235
131
 
236
- ---
237
-
238
132
  ### `destroy()`
239
133
 
240
- 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.
241
135
 
242
136
  ```javascript
243
137
  bliptar.destroy();
244
138
  ```
245
139
 
246
- ---
247
-
248
- ## Usage Examples
249
-
250
- ### Basic Setup
251
-
252
- ```javascript
253
- import { createBliptar } from '@bliptarjs/sdk';
254
-
255
- const bliptar = createBliptar({
256
- apiKey: 'blip_live_xxxxxxxx',
257
- });
258
-
259
- bliptar.init();
260
- ```
261
-
262
- ### With User Identification
263
-
264
- ```javascript
265
- import { createBliptar } from '@bliptarjs/sdk';
266
-
267
- const bliptar = createBliptar({
268
- apiKey: 'blip_live_xxxxxxxx',
269
- });
270
-
271
- bliptar.init();
272
-
273
- // When user logs in
274
- function onUserLogin(user) {
275
- bliptar.identify(user.id, {
276
- email: user.email,
277
- plan: user.subscription.plan,
278
- createdAt: user.createdAt,
279
- });
280
- }
281
- ```
282
-
283
- ### E-commerce Checkout Feedback
284
-
285
- ```javascript
286
- import { createBliptar } from '@bliptarjs/sdk';
287
-
288
- const bliptar = createBliptar({
289
- apiKey: 'blip_live_xxxxxxxx',
290
- onSubmit: (answers) => {
291
- // Send to your analytics
292
- analytics.track('feedback_submitted', answers);
293
- },
294
- });
295
-
296
- bliptar.init();
297
-
298
- // Track successful purchase
299
- function onPurchaseComplete(order) {
300
- bliptar.track('purchase_complete', {
301
- orderId: order.id,
302
- total: order.total,
303
- itemCount: order.items.length,
304
- });
305
- }
306
- ```
307
-
308
- ### Exit Intent Survey
309
-
310
- ```javascript
311
- import { createBliptar } from '@bliptarjs/sdk';
312
-
313
- const bliptar = createBliptar({
314
- apiKey: 'blip_live_xxxxxxxx',
315
- });
316
-
317
- bliptar.init();
318
-
319
- // Enable exit intent on pricing page
320
- if (window.location.pathname === '/pricing') {
321
- bliptar.enableExitIntent({
322
- once: true, // Only show once
323
- });
324
- }
325
- ```
326
-
327
- ### Button Click Feedback
140
+ ## Framework Examples
328
141
 
329
- ```javascript
330
- import { createBliptar } from '@bliptarjs/sdk';
331
-
332
- const bliptar = createBliptar({
333
- apiKey: 'blip_live_xxxxxxxx',
334
- });
335
-
336
- bliptar.init();
337
-
338
- // Track clicks on upgrade buttons
339
- bliptar.trackClicks({
340
- selector: '[data-track="upgrade"]',
341
- name: 'upgrade-button',
342
- });
343
-
344
- // Track clicks on help buttons
345
- bliptar.trackClicks({
346
- selector: '.help-button',
347
- name: 'help-click',
348
- });
349
- ```
350
-
351
- ### React Integration
142
+ ### React
352
143
 
353
144
  ```jsx
354
145
  import { useEffect } from 'react';
355
146
  import { createBliptar } from '@bliptarjs/sdk';
356
147
 
357
148
  const bliptar = createBliptar({
358
- apiKey: 'blip_live_xxxxxxxx',
149
+ apiKey: process.env.REACT_APP_BLIPTAR_KEY,
359
150
  });
360
151
 
361
152
  function App() {
362
153
  useEffect(() => {
363
154
  bliptar.init();
364
-
365
- return () => {
366
- bliptar.destroy();
367
- };
155
+ return () => bliptar.destroy();
368
156
  }, []);
369
157
 
370
158
  return <div>Your app</div>;
371
159
  }
372
-
373
- // In a component that needs to identify users
374
- function UserProfile({ user }) {
375
- useEffect(() => {
376
- if (user) {
377
- bliptar.identify(user.id, {
378
- email: user.email,
379
- plan: user.plan,
380
- });
381
- }
382
- }, [user]);
383
-
384
- return <div>Welcome, {user.name}</div>;
385
- }
386
160
  ```
387
161
 
388
- ### Next.js Integration
162
+ ### Next.js
389
163
 
390
164
  ```jsx
391
- // lib/bliptar.js
165
+ // app/providers.js
166
+ 'use client';
167
+ import { useEffect } from 'react';
392
168
  import { createBliptar } from '@bliptarjs/sdk';
393
169
 
394
- export const bliptar = typeof window !== 'undefined'
395
- ? createBliptar({ apiKey: process.env.NEXT_PUBLIC_BLIPTAR_API_KEY })
170
+ const bliptar = typeof window !== 'undefined'
171
+ ? createBliptar({ apiKey: process.env.NEXT_PUBLIC_BLIPTAR_KEY })
396
172
  : null;
397
173
 
398
- // app/layout.js or pages/_app.js
399
- 'use client';
400
-
401
- import { useEffect } from 'react';
402
- import { bliptar } from '@/lib/bliptar';
403
-
404
- export default function RootLayout({ children }) {
174
+ export function BliptarProvider({ children }) {
405
175
  useEffect(() => {
406
176
  bliptar?.init();
407
-
408
- return () => {
409
- bliptar?.destroy();
410
- };
177
+ return () => bliptar?.destroy();
411
178
  }, []);
412
179
 
180
+ return children;
181
+ }
182
+
183
+ // app/layout.js
184
+ import { BliptarProvider } from './providers';
185
+
186
+ export default function RootLayout({ children }) {
413
187
  return (
414
188
  <html>
415
- <body>{children}</body>
189
+ <body>
190
+ <BliptarProvider>{children}</BliptarProvider>
191
+ </body>
416
192
  </html>
417
193
  );
418
194
  }
419
195
  ```
420
196
 
421
- ### Vue Integration
197
+ ### Vue
422
198
 
423
199
  ```vue
424
200
  <script setup>
@@ -426,95 +202,78 @@ import { onMounted, onUnmounted } from 'vue';
426
202
  import { createBliptar } from '@bliptarjs/sdk';
427
203
 
428
204
  const bliptar = createBliptar({
429
- apiKey: 'blip_live_xxxxxxxx',
205
+ apiKey: import.meta.env.VITE_BLIPTAR_KEY,
430
206
  });
431
207
 
432
- onMounted(() => {
433
- bliptar.init();
434
- });
435
-
436
- onUnmounted(() => {
437
- bliptar.destroy();
438
- });
208
+ onMounted(() => bliptar.init());
209
+ onUnmounted(() => bliptar.destroy());
439
210
  </script>
440
211
  ```
441
212
 
442
- ---
213
+ ## User Identification
443
214
 
444
- ## Automatic Tracking
215
+ Track feedback by user:
445
216
 
446
- 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
+ ```
447
225
 
448
- | Data | Description |
449
- |------|-------------|
450
- | `page_view` | Fired on `init()` |
451
- | `scroll_depth` | Percentage scrolled (0-100%) |
452
- | `time_on_page` | Seconds spent on page |
453
- | `device_type` | `desktop`, `mobile`, or `tablet` |
454
- | `page_url` | Current page URL |
455
- | `referrer` | Previous page URL |
226
+ User attributes can be used in campaign targeting rules.
456
227
 
457
- This data is sent with every trigger request and can be used in campaign conditions.
228
+ ## Advanced: Manual Triggers
458
229
 
459
- ---
230
+ For advanced use cases, you can manually set up triggers:
460
231
 
461
- ## Form Types
232
+ ### `trackClicks(options)`
462
233
 
463
- The SDK supports multiple feedback form types:
234
+ Track clicks on specific elements:
464
235
 
465
- | Type | Description |
466
- |------|-------------|
467
- | **Binary** | Thumbs up/down, Yes/No questions |
468
- | **Star Rating** | 1-5 star ratings with optional half stars |
469
- | **Emoji Scale** | 3 or 5 point emoji scales |
470
- | **NPS** | Net Promoter Score (0-10) |
471
- | **Single Choice** | Multiple choice questions |
472
- | **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
+ ```
473
243
 
474
- Forms are configured in the Bliptar dashboard and automatically rendered by the SDK.
244
+ ### `enableExitIntent(options?)`
245
+
246
+ Manually enable exit intent detection:
475
247
 
476
- ---
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
+ ```
477
255
 
478
- ## TypeScript Support
256
+ ## TypeScript
479
257
 
480
- The SDK is written in TypeScript and includes full type definitions.
258
+ Full TypeScript support included:
481
259
 
482
260
  ```typescript
483
- import {
484
- createBliptar,
485
- BliptarInstance,
486
- BliptarConfig,
487
- ClickTrackingOptions,
488
- ExitIntentOptions,
489
- } from '@bliptarjs/sdk';
261
+ import { createBliptar, BliptarConfig } from '@bliptarjs/sdk';
490
262
 
491
263
  const config: BliptarConfig = {
492
- apiKey: 'blip_live_xxxxxxxx',
264
+ apiKey: 'your-api-key',
493
265
  debug: true,
494
- onSubmit: (answers) => {
495
- console.log(answers);
496
- },
497
266
  };
498
267
 
499
- const bliptar: BliptarInstance = createBliptar(config);
268
+ const bliptar = createBliptar(config);
269
+ ```
500
270
 
501
- // Type-safe click tracking
502
- const clickOptions: ClickTrackingOptions = {
503
- selector: '.cta-button',
504
- name: 'main-cta',
505
- once: true,
506
- };
507
- bliptar.trackClicks(clickOptions);
271
+ ## Bundle Size
508
272
 
509
- // Type-safe exit intent
510
- const exitOptions: ExitIntentOptions = {
511
- threshold: 50,
512
- once: true,
513
- };
514
- bliptar.enableExitIntent(exitOptions);
515
- ```
273
+ - **ESM:** ~26KB (minified)
274
+ - **Gzipped:** ~8KB
516
275
 
517
- ---
276
+ Zero dependencies.
518
277
 
519
278
  ## Browser Support
520
279
 
@@ -525,44 +284,22 @@ bliptar.enableExitIntent(exitOptions);
525
284
  | Safari | Latest 2 versions |
526
285
  | Edge | Latest 2 versions |
527
286
 
528
- ---
529
-
530
- ## Bundle Size
531
-
532
- - **ESM:** ~25KB (minified)
533
- - **Gzipped:** ~7KB
534
-
535
- The SDK has zero dependencies.
536
-
537
- ---
538
-
539
287
  ## Troubleshooting
540
288
 
541
289
  ### Forms not showing?
542
290
 
543
- 1. **Check your API key** - Ensure it's valid and for the correct environment
544
- 2. **Check campaign rules** - Verify your campaign is active and targeting rules match
545
- 3. **Enable debug mode** - Set `debug: true` to see console logs
546
- 4. **Check throttling** - Campaigns have frequency limits to avoid annoying users
547
-
548
- ### 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
549
295
 
550
296
  ```javascript
551
297
  const bliptar = createBliptar({
552
- apiKey: 'blip_live_xxxxxxxx',
553
- debug: true, // Enables console logging
298
+ apiKey: 'your-key',
299
+ debug: true, // See what's happening
554
300
  });
555
301
  ```
556
302
 
557
- This will log:
558
- - SDK initialization
559
- - Events being tracked
560
- - Form display triggers
561
- - API responses
562
- - Errors
563
-
564
- ---
565
-
566
303
  ## License
567
304
 
568
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.0",
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",